Merge pull request #727 from KLayout/drc-enhancements

Drc enhancements
This commit is contained in:
Matthias Köfferlein 2021-02-25 21:27:06 +01:00 committed by GitHub
commit c28451ed88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 784 additions and 121 deletions

View File

@ -71,13 +71,10 @@ public:
ProgressAdaptor (int verbosity);
virtual ~ProgressAdaptor ();
virtual void register_object (tl::Progress *progress);
virtual void unregister_object (tl::Progress *progress);
virtual void trigger (tl::Progress *progress);
virtual void yield (tl::Progress *progress);
private:
std::list<tl::Progress *> mp_objects;
int m_verbosity;
std::string m_progress_text, m_progress_value;
};
@ -93,36 +90,19 @@ ProgressAdaptor::~ProgressAdaptor ()
// .. nothing yet ..
}
void
ProgressAdaptor::register_object (tl::Progress *progress)
{
mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible.
}
void
ProgressAdaptor::unregister_object (tl::Progress *progress)
{
for (std::list<tl::Progress *>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
if (*k == progress) {
mp_objects.erase (k);
return;
}
}
}
void
ProgressAdaptor::trigger (tl::Progress *progress)
{
if (! mp_objects.empty () && mp_objects.front () == progress && tl::verbosity () >= m_verbosity) {
if (progress && first () == progress && tl::verbosity () >= m_verbosity) {
std::string text = mp_objects.front ()->desc ();
std::string text = progress->desc ();
if (m_progress_text != text) {
tl::info << text << " ..";
m_progress_text = text;
}
std::string value = mp_objects.front ()->formatted_value ();
std::string value = progress->formatted_value ();
if (m_progress_value != value) {
tl::info << ".. " << value;
m_progress_value = value;

View File

@ -403,6 +403,8 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse,
db::local_processor<db::Polygon, db::Edge, db::Polygon> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Edge> > others;
others.push_back (counting ? other.begin_merged () : other.begin ());
@ -479,6 +481,8 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, bool inverse,
db::local_processor<db::Polygon, db::Text, db::Polygon> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Text> > others;
others.push_back (other.begin ());
@ -564,6 +568,8 @@ AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, boo
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Polygon> > others;
others.push_back ((mode < 0 || counting) ? other.begin_merged () : other.begin ());
@ -702,6 +708,8 @@ AsIfFlatRegion::pull_generic (const Edges &other) const
db::local_processor<db::Polygon, db::Edge, db::Edge> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Edge> > others;
others.push_back (other.begin_merged ());
@ -752,6 +760,8 @@ AsIfFlatRegion::pull_generic (const Texts &other) const
db::local_processor<db::Polygon, db::Text, db::Text> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Text> > others;
others.push_back (other.begin ());
@ -807,6 +817,8 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Polygon> > others;
others.push_back (other.begin_merged ());
@ -1170,6 +1182,8 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
std::vector<generic_shape_iterator<db::Polygon> > others;
std::vector<bool> foreign;

View File

@ -601,6 +601,8 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_threads (deep_layer ().store ()->threads ());
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
@ -620,6 +622,8 @@ DeepRegion::and_and_not_with (const DeepRegion *other) const
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
proc.set_base_verbosity (base_verbosity ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_threads (deep_layer ().store ()->threads ());
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
@ -1359,6 +1363,8 @@ Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &no
const_cast<db::Cell *> (&region->deep_layer ().initial_cell ()),
region->deep_layer ().breakout_cells ());
proc.set_description (region->progress_desc ());
proc.set_report_progress (region->report_progress ());
proc.set_base_verbosity (region->base_verbosity ());
proc.set_threads (region->deep_layer ().store ()->threads ());
@ -1464,6 +1470,8 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
deep_layer ().breakout_cells (),
other_deep ? other_deep->deep_layer ().breakout_cells () : 0);
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
@ -1536,6 +1544,8 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to
db::InteractingLocalOperation op (mode, touching, inverse, min_count, max_count, true);
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
if (split_after) {
@ -1575,6 +1585,8 @@ DeepRegion::selected_interacting_generic (const Edges &other, bool inverse, size
db::InteractingWithEdgeLocalOperation op (inverse, min_count, max_count, true);
db::local_processor<db::PolygonRef, db::Edge, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
if (split_after) {
@ -1614,6 +1626,8 @@ DeepRegion::pull_generic (const Region &other, int mode, bool touching) const
db::PullLocalOperation op (mode, touching);
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
if (split_after) {
@ -1650,6 +1664,8 @@ DeepRegion::pull_generic (const Edges &other) const
db::PullWithEdgeLocalOperation op;
db::local_processor<db::PolygonRef, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_edges.layout (), &other_edges.initial_cell (), polygons.breakout_cells (), other_edges.breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_edges.layer (), dl_out.layer ());
@ -1679,6 +1695,8 @@ DeepRegion::pull_generic (const Texts &other) const
db::PullWithTextLocalOperation op;
db::local_processor<db::PolygonRef, db::TextRef, db::TextRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_texts.layout (), &other_texts.initial_cell (), polygons.breakout_cells (), other_texts.breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_texts.layer (), dl_out.layer ());
@ -1709,6 +1727,8 @@ DeepRegion::selected_interacting_generic (const Texts &other, bool inverse, size
db::InteractingWithTextLocalOperation op (inverse, min_count, max_count);
db::local_processor<db::PolygonRef, db::TextRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
if (split_after) {

View File

@ -614,6 +614,34 @@ RectilinearFilter::vars () const
return 0;
}
// -------------------------------------------------------------------------------------
// HoleCountFilter implementation
HoleCountFilter::HoleCountFilter (size_t min_count, size_t max_count, bool inverse)
: m_min_count (min_count), m_max_count (max_count), m_inverse (inverse)
{
// .. nothing yet ..
}
bool
HoleCountFilter::selected (const db::Polygon &poly) const
{
bool ok = poly.holes () < m_max_count && poly.holes () >= m_min_count;
return ok != m_inverse;
}
bool
HoleCountFilter::selected (const db::PolygonRef &poly) const
{
bool ok = poly.obj ().holes () < m_max_count && poly.obj ().holes () >= m_min_count;
return ok != m_inverse;
}
const TransformationReducer *HoleCountFilter::vars () const
{
return 0;
}
// -------------------------------------------------------------------------------------
// RectilinearFilter implementation

View File

@ -286,6 +286,51 @@ private:
bool m_inverse;
};
/**
* @brief Filters by number of holes
*
* This filter will select all polygons with a hole count between min_holes and max_holes (exclusively)
*/
struct DB_PUBLIC HoleCountFilter
: public AllMustMatchFilter
{
/**
* @brief Constructor
* @param inverse If set to true, only polygons not matching this criterion will be filtered
*/
HoleCountFilter (size_t min_count, size_t max_count, bool inverse);
/**
* @brief Returns true if the polygon is a rectangle
*/
virtual bool selected (const db::Polygon &poly) const;
/**
* @brief Returns true if the polygon is a rectangle
*/
virtual bool selected (const db::PolygonRef &poly) const;
/**
* @brief This filter does not need variants
*/
virtual const TransformationReducer *vars () const;
/**
* @brief This filter prefers producing variants
*/
virtual bool wants_variants () const { return true; }
/**
* @brief This filter wants merged input
*/
virtual bool requires_raw_input () const { return false; }
private:
size_t m_min_count, m_max_count;
bool m_inverse;
};
/**
* @brief A bounding box filter for use with Region::filter or Region::filtered
*

View File

@ -466,6 +466,12 @@ static db::CompoundRegionOperationNode *new_perimeter_sum_filter (db::CompoundRe
return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true, true /*sum of set*/);
}
static db::CompoundRegionOperationNode *new_hole_count_filter (db::CompoundRegionOperationNode *input, bool inverse, size_t hmin, size_t hmax)
{
check_non_null (input, "input");
return new db::CompoundRegionFilterOperationNode (new db::HoleCountFilter (hmin, hmax, inverse), input, true);
}
static db::CompoundRegionOperationNode *new_area_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::area_type amin, db::coord_traits<db::Coord>::area_type amax)
{
check_non_null (input, "input");
@ -672,6 +678,11 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
"@brief Creates a node filtering the input by area sum.\n"
"Like \\new_area_filter, but applies to the sum of all shapes in the current set.\n"
) +
gsi::constructor ("new_hole_count_filter", &new_hole_count_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("hmin", 0), gsi::arg ("hmax", std::numeric_limits<size_t>::max (), "max"),
"@brief Creates a node filtering the input by number of holes per polygon.\n"
"This node renders the input if the hole count is between hmin and hmax (exclusively). If 'inverse' is set to true, the "
"input shape is returned if the hole count is less than hmin (exclusively) or larger than hmax (inclusively)."
) +
gsi::constructor ("new_bbox_filter", &new_bbox_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::area_type>::max (), "max"),
"@brief Creates a node filtering the input by bounding box parameters.\n"
"This node renders the input if the specified bounding box parameter of the input shape is between pmin and pmax (exclusively). If 'inverse' is set to true, the "

View File

@ -308,6 +308,18 @@ static db::Region with_area2 (const db::Region *r, const tl::Variant &min, const
return r->filtered (f);
}
static db::Region with_holes1 (const db::Region *r, size_t n, bool inverse)
{
db::HoleCountFilter f (n, n + 1, inverse);
return r->filtered (f);
}
static db::Region with_holes2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::HoleCountFilter f (min.is_nil () ? size_t (0) : min.to<size_t> (), max.is_nil () ? std::numeric_limits <size_t>::max () : max.to<size_t> (), inverse);
return r->filtered (f);
}
static db::Region with_bbox_width1 (const db::Region *r, db::Region::distance_type bbox_width, bool inverse)
{
db::RegionBBoxFilter f (bbox_width, bbox_width + 1, inverse, db::RegionBBoxFilter::BoxWidth);
@ -925,6 +937,30 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
) +
method_ext ("with_holes", with_holes1, gsi::arg ("nholes"), gsi::arg ("inverse"),
"@brief Filters the polygons by their number of holes\n"
"Filters the polygons of the region by number of holes. If \"inverse\" is false, only "
"polygons which have the given number of holes are returned. If \"inverse\" is true, "
"polygons not having the given of holes are returned.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
"\n"
"This method has been introduced in version 0.27.\n"
) +
method_ext ("with_holes", with_holes2, gsi::arg ("min_bholes"), gsi::arg ("max_nholes"), gsi::arg ("inverse"),
"@brief Filter the polygons by their number of holes\n"
"Filters the polygons of the region by number of holes. If \"inverse\" is false, only "
"polygons which have a hole count larger or equal to \"min_nholes\" and less than \"max_nholes\" are "
"returned. If \"inverse\" is true, "
"polygons having a hole count less than \"min_nholes\" or larger or equal than \"max_nholes\" are "
"returned.\n"
"\n"
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
"\n"
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
"\n"
"This method has been introduced in version 0.27.\n"
) +
method_ext ("with_bbox_width", with_bbox_width1, gsi::arg ("width"), gsi::arg ("inverse"),
"@brief Filter the polygons by bounding box width\n"
"Filters the polygons of the region by the width of their bounding box. If \"inverse\" is false, only "

View File

@ -1973,6 +1973,27 @@ TEST(35c_interact_with_count_text)
EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)");
}
TEST(40_with_holes)
{
db::Region r;
r.insert (db::Box (db::Point (0, 0), db::Point (100, 200)));
db::Region rr;
rr.insert (db::Box (db::Point (10, 10), db::Point (20, 20)));
rr.insert (db::Box (db::Point (30, 30), db::Point (40, 40)));
r.set_merged_semantics (true);
r.set_min_coherence (false);
r -= rr;
EXPECT_EQ (rr.filtered (db::HoleCountFilter (0, 1, false)).to_string (), "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)");
EXPECT_EQ (r.filtered (db::HoleCountFilter (2, 3, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
EXPECT_EQ (r.filtered (db::HoleCountFilter (1, 2, false)).to_string (), "");
EXPECT_EQ (r.filtered (db::HoleCountFilter (1, 3, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
EXPECT_EQ (r.filtered (db::HoleCountFilter (0, 2, false)).to_string (), "");
EXPECT_EQ (r.filtered (db::HoleCountFilter (2, 5, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
EXPECT_EQ (r.filtered (db::HoleCountFilter (3, 5, true)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
}
TEST(100_Processors)
{
db::Region r;

View File

@ -71,6 +71,7 @@ module DRC
# @li \global#space @/li
# @li \global#squares @/li
# @li \global#width @/li
# @li \global#with_holes @/li
# @/ul
#
# The following documentation will list the methods available for DRC expression objects.
@ -434,7 +435,7 @@ CODE
# result. Without "if_any" three corners are returned for each triangle.
def count
DRCOpNodeCountFilter::new(@engine, self)
DRCOpNodeCountFilter::new(@engine, self, :new_count_filter, "count")
end
# %DRC%
@ -1003,6 +1004,24 @@ CODE
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
end
# %DRC%
# @name with_holes
# @brief Selects all input polygons with the specified number of holes
# @synopsis expression.with_holes (in condition)
#
# This operation can be used as a plain function in which case it acts on primary
# shapes or can be used as method on another DRC expression.
# The following example selects all polygons with more than 2 holes:
#
# @code
# out = in.drc(with_holes > 2)
# out = in.drc(primary.with_holes > 2) # equivalent
# @/code
def with_holes
return DRCOpNodeCountFilter::new(@engine, self, :new_hole_count_filter, "with_holes")
end
# %DRC%
# @name merged
# @brief Returns the merged input polygons, optionally selecting multi-overlap
@ -1552,16 +1571,19 @@ class DRCOpNodeCountFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
attr_accessor :method
attr_accessor :name
def initialize(engine, input)
def initialize(engine, input, method, name)
super(engine)
self.input = input
self.inverse = false
self.description = "count"
self.description = name
self.method = method
end
def _description_for_dump
self.inverse ? "count" : "not_count"
self.inverse ? name : "not_" + name
end
def do_create_node(cache)
@ -1570,7 +1592,7 @@ class DRCOpNodeCountFilter < DRCOpNodeWithCompare
if self.lt || self.le
args << (self.lt ? @engine._make_numeric_value(self.lt) : @engine._make_numeric_value(self.le) + 1)
end
RBA::CompoundRegionOperationNode::new_count_filter(*args)
RBA::CompoundRegionOperationNode::send(self.method, *args)
end
def inverted

View File

@ -953,6 +953,19 @@ CODE
CODE
end
# %DRC%
# @name with_holes
# @brief Selects all input polygons according to their number of holes in DRC expressions
# @synopsis with_holes (in condition)
#
# "with_holes" represents a polygon selector for
# \DRC# expressions selecting polygons of the primary by their number of holes
# (see \Layer#drc and \DRC#with_holes for more details).
def with_holes
primary.with_holes
end
# %DRC%
# @name enclosing
# @brief Performs an enclosing check

View File

@ -498,6 +498,20 @@ module DRC
end
end
# %DRC%
# @name warn
# @brief Prints a warning
# @synopsis warn(message)
# Similar to \log, but the message is printed formatted as a warning
def warn(arg)
if @log_file
@log_file.puts("WARNING: " + arg)
else
RBA::Logger::warn(arg)
end
end
# %DRC%
# @name log_file
# @brief Specify the log file where to send to log to
@ -1840,7 +1854,7 @@ CODE
def run_timed(desc, obj)
info(desc)
log(desc)
# enable progress
if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts)

View File

@ -488,7 +488,7 @@ CODE
#
# This method is available for polygon layers only.
%w(bbox_height bbox_max bbox_min bbox_width perimeter).each do |f|
%w(bbox_height bbox_max bbox_min bbox_width perimeter holes).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
@ -517,6 +517,61 @@ CODE
end
end
# %DRC%
# @name with_holes
# @brief Selects all polygons with the specified number of holes
# @synopsis layer.with_holes(count)
# @synopsis layer.with_holes(min_count, max_count)
# @synopsis layer.with_holes(min_count .. max_count)
#
# This method is available for polygon layers. It will select all polygons from the input layer
# which have the specified number of holes.
# %DRC%
# @name without_holes
# @brief Selects all polygons with the specified number of holes
# @synopsis layer.without_holes(count)
# @synopsis layer.without_holes(min_count, max_count)
# @synopsis layer.without_holes(min_count .. max_count)
#
# This method is available for polygon layers. It will select all polygons from the input layer
# which do not have the specified number of holes.
%w(holes).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
requires_region
if args.size == 1
a = args[0]
if a.is_a?(Range)
min = @engine._make_numeric_value_with_nil(a.begin)
max = @engine._make_numeric_value_with_nil(a.end)
max && (max += 1)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, min, max, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._make_value(a), #{inv.inspect}))
end
elsif args.size == 2
min = @engine._make_numeric_value_with_nil(args[0])
max = @engine._make_numeric_value_with_nil(args[1])
max && (max += 1)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, min, max, #{inv.inspect}))
else
raise("Invalid number of arguments (1 or 2 expected)")
end
end
end
CODE
end
end
# %DRC%
# @name with_bbox_aspect_ratio
# @brief Selects polygons by the aspect ratio of their bounding box

View File

@ -25,6 +25,8 @@ module DRC
drc._rdb_index = rdb_index
drc._generator = generator
drc_progress = RBA::AbstractProgress::new("DRC: " + macro.path)
begin
# Set a debugger scope so that our errors end up with the debugger set to the DRC's line
@ -46,6 +48,9 @@ module DRC
# cleans up and creates layout and report views
drc._finish
# unlocks the UI
drc_progress._destroy
end
timer.stop

View File

@ -238,3 +238,13 @@ TEST(17d)
{
run_test (_this, "17", true);
}
TEST(18)
{
run_test (_this, "18", false);
}
TEST(18d)
{
run_test (_this, "18", true);
}

View File

@ -1152,3 +1152,14 @@ TEST(28_inputFragmentation)
{
run_test (_this, "28", true);
}
TEST(29_holes)
{
run_test (_this, "29", false);
}
TEST(29d_holes)
{
run_test (_this, "29", true);
}

View File

@ -201,25 +201,6 @@ Class<tl::Timer> decl_Timer ("tl", "Timer",
// ----------------------------------------------------------------
// Progress reporter objects
namespace tl {
template <> struct type_traits<tl::Progress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template <> struct type_traits<tl::RelativeProgress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template <> struct type_traits<tl::AbsoluteProgress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
namespace gsi
{
@ -247,6 +228,27 @@ Class<tl::Progress> decl_Progress ("tl", "Progress",
"This class has been introduced in version 0.23.\n"
);
static tl::AbstractProgress *abstract_progress (const std::string &desc)
{
return new tl::AbstractProgress (desc);
}
Class<tl::AbstractProgress> decl_AbstractProgress (decl_Progress, "tl", "AbstractProgress",
gsi::constructor ("new", &abstract_progress, gsi::arg ("desc"),
"@brief Creates an abstract progress reporter with the given description\n"
),
"@brief The abstract progress reporter\n"
"\n"
"The abstract progress reporter acts as a 'bracket' for a sequence of operations which are connected "
"logically. For example, a DRC script consists of multiple operations. An abstract progress reportert "
"is instantiated during the run time of the DRC script. This way, the application leaves the UI open while "
"the DRC executes and log messages can be collected.\n"
"\n"
"The abstract progress does not have a value.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static tl::RelativeProgress *rel_progress_2 (const std::string &desc, size_t max)
{
return new tl::RelativeProgress (desc, max);

View File

@ -200,11 +200,33 @@ LogFile::timeout ()
}
}
void
LogFile::set_max_entries (size_t n)
{
QMutexLocker locker (&m_lock);
m_max_entries = n;
while (m_messages.size () > m_max_entries) {
m_messages.pop_front ();
}
}
size_t
LogFile::max_entries () const
{
return m_max_entries;
}
void
LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continued)
{
QMutexLocker locker (&m_lock);
if (m_max_entries == 0) {
return;
}
if (m_messages.size () >= m_max_entries) {
m_messages.pop_front ();
}

View File

@ -192,6 +192,18 @@ public slots:
return m_log_receiver;
}
/**
* @brief Sets the maximum number of entries to show
*
* Setting this value to 0 basically disables the log collection
*/
void set_max_entries (size_t n);
/**
* @brief Gets the maximum number of entries to show
*/
size_t max_entries () const;
private slots:
void timeout ();

View File

@ -86,14 +86,13 @@ ProgressReporter::set_progress_bar (lay::ProgressBar *pb)
void
ProgressReporter::register_object (tl::Progress *progress)
{
if (mp_objects.empty ()) {
if (begin () == end ()) {
// to avoid recursions of any kind, disallow any user interaction except
// cancelling the operation
QApplication::instance ()->installEventFilter (this);
}
mp_objects.push_back (*progress); // this keeps the outmost one visible. push_front would make the latest one visible.
// mp_objects.push_front (progress);
tl::ProgressAdaptor::register_object (progress);
if (m_start_time == tl::Clock () && ! m_pw_visible) {
m_start_time = tl::Clock::current ();
@ -104,33 +103,47 @@ ProgressReporter::register_object (tl::Progress *progress)
set_visible (true);
}
update_and_yield ();
if (progress->is_abstract ()) {
if (mp_pb) {
mp_pb->update_progress (progress);
}
process_events ();
} else {
update_and_yield ();
}
}
void
ProgressReporter::unregister_object (tl::Progress *progress)
{
progress->unlink ();
tl::ProgressAdaptor::unregister_object (progress);
// close or refresh window
if (mp_objects.empty ()) {
if (begin () == end ()) {
if (m_pw_visible) {
set_visible (false);
}
m_start_time = tl::Clock ();
}
update_and_yield ();
if (mp_pb) {
mp_pb->update_progress (0);
}
process_events ();
if (mp_objects.empty ()) {
QApplication::instance ()->removeEventFilter (this);
} else {
update_and_yield ();
}
}
void
ProgressReporter::trigger (tl::Progress * /*progress*/)
{
if (! mp_objects.empty ()) {
if (begin () != end ()) {
// make dialog visible after some time has passed
if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) {
set_visible (true);
@ -152,27 +165,22 @@ ProgressReporter::yield (tl::Progress * /*progress*/)
}
}
void
ProgressReporter::signal_break ()
{
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
k->signal_break ();
}
}
void
ProgressReporter::update_and_yield ()
{
if (m_pw_visible && ! mp_objects.empty ()) {
if (mp_pb) {
mp_pb->update_progress (mp_objects.first ());
QWidget *w = mp_pb->progress_get_widget ();
if (w) {
mp_objects.first ()->render_progress (w);
}
}
process_events (); // Qt4 seems to need this
if (! m_pw_visible) {
return;
}
if (mp_pb && first ()) {
mp_pb->update_progress (first ());
QWidget *w = mp_pb->progress_get_widget ();
if (w) {
first ()->render_progress (w);
}
}
process_events (); // Qt4 seems to need this
}
void
@ -202,8 +210,8 @@ ProgressReporter::set_visible (bool vis)
if (mp_pb) {
if (!vis) {
mp_pb->progress_remove_widget ();
} else if (mp_pb->progress_wants_widget () && mp_objects.first ()) {
mp_pb->progress_add_widget (mp_objects.first ()->progress_widget ());
} else if (mp_pb->progress_wants_widget () && first ()) {
mp_pb->progress_add_widget (first ()->progress_widget ());
}
}

View File

@ -68,16 +68,9 @@ public:
virtual void yield (tl::Progress *progress);
virtual bool eventFilter (QObject *dest, QEvent *event);
void signal_break ();
void set_progress_bar (lay::ProgressBar *pb);
bool is_busy () const
{
return !mp_objects.empty ();
}
private:
tl::list<tl::Progress> mp_objects;
tl::Clock m_start_time;
lay::ProgressBar *mp_pb;
bool m_pw_visible;

View File

@ -26,6 +26,7 @@
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
#include <QListView>
#include <math.h>
@ -59,7 +60,7 @@ private:
ProgressBarWidget::ProgressBarWidget (QWidget *parent, const char *name)
: QWidget (parent),
m_value (0.0), m_width (64), m_length (0), m_fw (1), m_bw (0)
m_value (0.0), m_width (200), m_length (0), m_fw (1), m_bw (0)
{
setObjectName (QString::fromUtf8 (name));
setMinimumSize (64, 10);
@ -134,13 +135,46 @@ ProgressBarWidget::resizeEvent (QResizeEvent *)
// --------------------------------------------------------------------
ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full_width)
ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool fw)
: QFrame (parent),
mp_widget (0), mp_pr (pr)
mp_widget (0), mp_pr (pr), m_log_file (0, true), m_log_visible (false)
{
QVBoxLayout *top_layout = new QVBoxLayout (this);
top_layout->addStretch (1);
mp_log_frame = new QFrame (this);
mp_log_frame->setFrameShape (QFrame::NoFrame);
mp_log_frame->hide ();
top_layout->addWidget (mp_log_frame);
QVBoxLayout *log_layout = new QVBoxLayout (mp_log_frame);
QListView *log_view = new QListView (this);
log_view->setModel (&m_log_file);
log_view->setUniformItemSizes (true);
log_layout->addWidget (log_view);
QFrame *attn_frame = new QFrame (this);
attn_frame->setFrameShape (QFrame::NoFrame);
attn_frame->hide ();
log_layout->addWidget (attn_frame);
QHBoxLayout *attn_layout = new QHBoxLayout (attn_frame);
attn_layout->setContentsMargins (0, 0, 0, 0);
QLabel *attn_label1 = new QLabel (attn_frame);
attn_label1->setPixmap (QPixmap (QString::fromUtf8 (":/warn_16.png")));
attn_layout->addWidget (attn_label1);
QLabel *attn_label2 = new QLabel (attn_frame);
attn_label2->setText (tr ("There are errors or warnings"));
attn_layout->addWidget (attn_label2);
attn_layout->addStretch (1);
connect (&m_log_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ()));
connect (&m_log_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool)));
QFrame *bar_frame = new QFrame (this);
top_layout->addWidget (bar_frame);
@ -157,12 +191,11 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
int col = 0;
if (! full_width) {
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
layout->setColumnStretch (col++, 1);
}
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
m_left_col = col++;
mp_label = new QLabel (bar_frame);
layout->setColumnStretch(col, 2);
layout->addWidget (mp_label, 0, col++, 1, 1);
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1);
@ -171,7 +204,6 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
progress_bar_frame->setFrameStyle (QFrame::Box | QFrame::Plain);
progress_bar_frame->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
layout->addWidget (progress_bar_frame, 0, col, 1, 1);
layout->setColumnStretch(col++, 2);
QGridLayout *pbf_layout = new QGridLayout (progress_bar_frame);
progress_bar_frame->setLayout (pbf_layout);
@ -191,16 +223,41 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
mp_cancel_button->setText (QObject::tr ("Cancel"));
layout->addWidget (mp_cancel_button, 0, col++, 1, 1);
if (! full_width) {
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
layout->setColumnStretch (col++, 1);
}
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
m_right_col = col++;
layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, col);
m_widget_col = col;
connect (mp_cancel_button, SIGNAL (clicked ()), this, SLOT (signal_break ()));
set_full_width (fw);
}
void
ProgressWidget::set_log_visible (bool f)
{
if (f != m_log_visible) {
m_log_visible = f;
mp_log_frame->setVisible (f);
set_full_width (m_full_width);
}
}
void
ProgressWidget::set_full_width (bool fw)
{
m_full_width = fw;
bool f = (fw || m_log_visible);
mp_layout->setColumnStretch (m_left_col, f ? 0 : 1);
mp_layout->setColumnStretch (m_right_col, f ? 0 : 1);
}
bool
ProgressWidget::full_width () const
{
return m_full_width;
}
QWidget *
@ -233,6 +290,13 @@ ProgressWidget::remove_widget ()
void
ProgressWidget::set_progress (tl::Progress *progress)
{
if (! progress || progress->is_abstract ()) {
m_log_file.clear ();
m_log_file.set_max_entries (progress ? 1000 : 0);
set_log_visible (progress != 0);
return;
}
bool can_cancel = false;
std::string text;

View File

@ -31,11 +31,14 @@
#include <QHBoxLayout>
#include "layProgress.h"
#include "layLogViewerDialog.h"
class QToolButton;
class QLabel;
class QToolButton;
class QGridLayout;
class QListView;
class QFrame;
namespace tl
{
@ -59,6 +62,8 @@ public:
void add_widget (QWidget *widget);
void remove_widget ();
QWidget *get_widget () const;
void set_full_width (bool fw);
bool full_width () const;
QSize sizeHint () const;
@ -73,6 +78,13 @@ private:
QGridLayout *mp_layout;
QToolButton *mp_cancel_button;
ProgressReporter *mp_pr;
lay::LogFile m_log_file;
QFrame *mp_log_frame;
bool m_full_width;
int m_left_col, m_right_col;
bool m_log_visible;
void set_log_visible (bool f);
};
}

View File

@ -409,8 +409,6 @@ namespace
mp_dialog->mark_fetching (m_name);
}
virtual void register_object (tl::Progress * /*progress*/) { }
virtual void unregister_object (tl::Progress * /*progress*/) { }
virtual void yield (tl::Progress * /*progress*/) { }
virtual void trigger (tl::Progress *progress)

View File

@ -35,6 +35,10 @@ TextProgress::TextProgress (int verbosity)
void TextProgress::update_progress (tl::Progress *progress)
{
if (! progress || progress->is_abstract ()) {
return;
}
std::string text = progress->desc ();
if (m_progress_text != text && tl::verbosity () >= m_verbosity) {
tl::info << text << " ..";

View File

@ -34,6 +34,7 @@
#include "tlXMLParser.h"
#include "tlGlobPattern.h"
#include "tlInclude.h"
#include "tlProgress.h"
#include "rba.h"
#include "pya.h"
@ -1022,6 +1023,8 @@ int Macro::run () const
try {
tl::ProgressGarbageCollector progress_gc;
gsi::Interpreter *ip = script_interpreter (interpreter ());
if (ip) {

View File

@ -46,6 +46,18 @@ ProgressAdaptor::~ProgressAdaptor ()
tl::Progress::register_adaptor (0);
}
void
ProgressAdaptor::register_object (Progress *progress)
{
mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible.
}
void
ProgressAdaptor::unregister_object (Progress *progress)
{
progress->unlink ();
}
void
ProgressAdaptor::prev (ProgressAdaptor *pa)
{
@ -58,6 +70,59 @@ ProgressAdaptor::prev ()
return mp_prev;
}
void
ProgressAdaptor::signal_break ()
{
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
k->signal_break ();
}
}
tl::Progress *
ProgressAdaptor::first ()
{
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
if (! k->is_abstract ()) {
return k.operator-> ();
}
}
return 0;
}
// ---------------------------------------------------------------------------------------------
// ProgressGarbageCollector implementation
ProgressGarbageCollector::ProgressGarbageCollector ()
{
tl::ProgressAdaptor *a = tl::Progress::adaptor ();
if (a) {
for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ++p) {
mp_valid_objects.insert (p.operator-> ());
}
}
}
ProgressGarbageCollector::~ProgressGarbageCollector ()
{
tl::ProgressAdaptor *a = tl::Progress::adaptor ();
if (a) {
for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ) {
tl::ProgressAdaptor::iterator pn = p;
++pn;
if (mp_valid_objects.find (p.operator-> ()) == mp_valid_objects.end ()) {
a->unregister_object (p.operator-> ());
}
p = pn;
}
}
}
// ---------------------------------------------------------------------------------------------
// Progress implementation
@ -187,7 +252,21 @@ bool Progress::test(bool force_yield)
}
// ---------------------------------------------------------------------------------------------
// Progress implementation
// AbstractProgress implementation
AbstractProgress::AbstractProgress (const std::string &desc)
: tl::Progress (desc)
{
initialize ();
}
AbstractProgress::~AbstractProgress ()
{
shutdown ();
}
// ---------------------------------------------------------------------------------------------
// RelativeProgress implementation
RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, size_t yield_interval)
: Progress (desc, yield_interval)

View File

@ -31,12 +31,57 @@
#include "tlTimer.h"
#include "tlList.h"
#include <set>
class QWidget;
namespace tl
{
class Progress;
class RelativeProgress;
class AbstractProgress;
class AbsoluteProgress;
template <> struct type_traits<tl::Progress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template <> struct type_traits<tl::RelativeProgress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template <> struct type_traits<tl::AbstractProgress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template <> struct type_traits<tl::AbsoluteProgress> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
/**
* @brief A helper class to clean up pending progress objects
*
* Pending progress objects may be created in scripts. If scripts are aborted
* (e.g. in the debugger), progress objects may stay behing a block the application.
* To prevent this, this object keeps track of progress objects created between
* it's constructor and destructor and cleans up the objects created but not
* destroyed.
*/
class TL_PUBLIC ProgressGarbageCollector
{
public:
ProgressGarbageCollector ();
~ProgressGarbageCollector ();
private:
std::set<tl::Progress *> mp_valid_objects;
};
/**
* @brief The receivers for progress reports
@ -48,20 +93,45 @@ class Progress;
class TL_PUBLIC ProgressAdaptor
{
public:
public:
typedef tl::list<tl::Progress>::iterator iterator;
ProgressAdaptor ();
virtual ~ProgressAdaptor ();
virtual void register_object (Progress *progress) = 0;
virtual void unregister_object (Progress *progress) = 0;
virtual void register_object (Progress *progress);
virtual void unregister_object (Progress *progress);
virtual void trigger (Progress *progress) = 0;
virtual void yield (Progress *progress) = 0;
void prev (ProgressAdaptor *pa);
ProgressAdaptor *prev ();
bool is_busy () const
{
return !mp_objects.empty ();
}
tl::Progress *first ();
void signal_break ();
protected:
iterator begin ()
{
return mp_objects.begin ();
}
iterator end ()
{
return mp_objects.end ();
}
private:
friend class ProgressGarbageCollector;
ProgressAdaptor *mp_prev;
tl::list<tl::Progress> mp_objects;
};
/**
@ -77,8 +147,6 @@ public:
BreakException () : tl::Exception ("Operation cancelled") { }
};
class Progress;
/**
* @brief A "progress" reporter class
*
@ -138,6 +206,15 @@ public:
*/
virtual double value () const = 0;
/**
* @brief Returns true if the progress is an abstract one
*
* Abstract progress objcts don't have a value but mark a section begin executed as a top level progress.
* Technically they will open a channel for the UI - e.g. leaving a progress dialog open while the
* operation is running.
*/
virtual bool is_abstract () const = 0;
/**
* @brief Creates a widget that renders the progress graphically
*
@ -217,6 +294,7 @@ protected:
private:
friend class ProgressAdaptor;
friend class ProgressGarbageCollector;
std::string m_desc;
std::string m_title;
@ -231,6 +309,43 @@ private:
static void register_adaptor (tl::ProgressAdaptor *pa);
};
/**
* @brief The abstract progress
*
* An abstract progress object can be used as a top-level progress object to mark a section
* in an operation flow. This will provide a hint for the UI to leave the progress dialog open
* for example.
*/
class TL_PUBLIC AbstractProgress
: public Progress
{
public:
/**
* @brief Constructor
*/
AbstractProgress (const std::string &desc);
/**
* @brief Destructor
*/
~AbstractProgress ();
/**
* @brief Delivers the current progress as a string (empty for the abstract progress)
*/
std::string formatted_value () const { return std::string (); }
/**
* @brief Delivers the relative progress (0 for the abstract progress)
*/
double value () const { return 0.0; }
/**
* @brief Indicates this progress reporter is abstract
*/
bool is_abstract() const { return true; }
};
/**
* @brief A relative progress value
*
@ -253,9 +368,6 @@ public:
*/
RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000);
/**
* @brief Destructor
*/
~RelativeProgress ();
/**
@ -271,6 +383,11 @@ public:
*/
double value () const;
/**
* @brief Indicates this progress reporter isn't abstract
*/
bool is_abstract() const { return false; }
/**
* @brief Set the format of the output.
*
@ -345,7 +462,12 @@ public:
*/
double value () const;
/**
/**
* @brief Indicates this progress reporter isn't abstract
*/
bool is_abstract() const { return false; }
/**
* @brief Set the format of the output.
*
* This is a sprintf format string with the value being

View File

@ -512,8 +512,6 @@ class TL_PUBLIC WorkerProgressAdaptor : public tl::ProgressAdaptor
public:
WorkerProgressAdaptor (Worker *worker);
virtual void register_object (Progress *progress);
virtual void unregister_object (Progress *progress);
virtual void trigger (Progress *progress);
virtual void yield (Progress *progress);
@ -527,16 +525,6 @@ WorkerProgressAdaptor::WorkerProgressAdaptor (Worker *worker)
// .. nothing yet ..
}
void WorkerProgressAdaptor::register_object (Progress * /*progress*/)
{
// .. nothing yet ..
}
void WorkerProgressAdaptor::unregister_object (Progress * /*progress*/)
{
// .. nothing yet ..
}
void WorkerProgressAdaptor::trigger (Progress * /*progress*/)
{
// .. nothing yet ..

25
testdata/drc/drcGenericTests_18.drc vendored Normal file
View File

@ -0,0 +1,25 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
h = l1 - l2
h.drc(with_holes == 0).output(100, 0)
h.drc(with_holes != 0).output(101, 0)
h.drc(with_holes == 3).output(102, 0)
h.drc(1 <= with_holes < 3).output(103, 0)
h.drc(1 <= primary.with_holes <= 1).output(104, 0)
h.drc(with_holes >= 2).output(105, 0)
h.drc(with_holes >= 0).output(106, 0)

BIN
testdata/drc/drcGenericTests_18.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcGenericTests_au18.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcGenericTests_au18d.gds vendored Normal file

Binary file not shown.

25
testdata/drc/drcSimpleTests_29.drc vendored Normal file
View File

@ -0,0 +1,25 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
h = l1 - l2
h.with_holes(0).output(100, 0)
h.without_holes(0).output(101, 0)
h.with_holes(3).output(102, 0)
h.with_holes(1..3).output(103, 0)
h.with_holes(1..1).output(104, 0)
h.with_holes(2, nil).output(105, 0)
h.with_holes(0, nil).output(106, 0)

BIN
testdata/drc/drcSimpleTests_29.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au29.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au29d.gds vendored Normal file

Binary file not shown.

View File

@ -1049,6 +1049,27 @@ class DBRegion_TestClass < TestBase
end
# Some filters
def test_holesfilter
r = RBA::Region::new
r.insert(RBA::Box::new(RBA::Point::new(0, 0), RBA::Point::new(100, 200)))
rr = RBA::Region::new
rr.insert(RBA::Box::new(RBA::Point::new(10, 10), RBA::Point::new(20, 20)))
rr.insert(RBA::Box::new(RBA::Point::new(30, 30), RBA::Point::new(40, 40)))
r -= rr
assert_equal(r.with_holes(0, false).to_s, "")
assert_equal(r.with_holes(0, true).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
assert_equal(rr.with_holes(0, false).to_s, "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)")
assert_equal(rr.with_holes(0, true).to_s, "")
assert_equal(rr.with_holes(2, false).to_s, "")
assert_equal(r.with_holes(1, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
assert_equal(r.with_holes(2, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
assert_equal(r.with_holes(1, 2, false).to_s, "")
end
end
load("test_epilogue.rb")