diff --git a/src/buddies/src/bd/bdInit.cc b/src/buddies/src/bd/bdInit.cc index 261ab0c1a..0d3160ccc 100644 --- a/src/buddies/src/bd/bdInit.cc +++ b/src/buddies/src/bd/bdInit.cc @@ -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 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::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; diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 8881e454f..9547a71cf 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -403,6 +403,8 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse, db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > 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 proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > others; others.push_back (other.begin ()); @@ -564,6 +568,8 @@ AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, boo db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > 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 proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > others; others.push_back (other.begin_merged ()); @@ -752,6 +760,8 @@ AsIfFlatRegion::pull_generic (const Texts &other) const db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > others; others.push_back (other.begin ()); @@ -807,6 +817,8 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > 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 proc; proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); std::vector > others; std::vector foreign; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index ee3fb5892..bc49410e9 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -601,6 +601,8 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&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 proc (const_cast (&deep_layer ().layout ()), const_cast (&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 (®ion->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 proc (const_cast (&polygons.layout ()), const_cast (&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 proc (const_cast (&polygons.layout ()), const_cast (&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 proc (const_cast (&polygons.layout ()), const_cast (&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 proc (const_cast (&polygons.layout ()), const_cast (&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 proc (const_cast (&polygons.layout ()), const_cast (&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 proc (const_cast (&polygons.layout ()), const_cast (&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) { diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index a1eacf9f7..0d2ed0de1 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -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 diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index 28ce19f67..89392bd0d 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -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 * diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 90ad2447b..05ff115a0 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -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::area_type amin, db::coord_traits::area_type amax) { check_non_null (input, "input"); @@ -672,6 +678,11 @@ Class 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::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::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 " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index a52ca5717..91ee1dfec 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -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 (), max.is_nil () ? std::numeric_limits ::max () : max.to (), 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 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 " diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index db2d9d296..c8b2334fa 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -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; diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index 1c8419bbe..db5607c5b 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 0b17df957..2a7566ff9 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 2d449e49f..8cb066c0b 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -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) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 0f1363f61..b5fa7a3df 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/drc_interpreters.lym b/src/drc/drc/built-in-macros/drc_interpreters.lym index 6ada00b60..f1affc3d7 100644 --- a/src/drc/drc/built-in-macros/drc_interpreters.lym +++ b/src/drc/drc/built-in-macros/drc_interpreters.lym @@ -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 diff --git a/src/drc/unit_tests/drcGenericTests.cc b/src/drc/unit_tests/drcGenericTests.cc index a175c6b7b..ea4de5476 100644 --- a/src/drc/unit_tests/drcGenericTests.cc +++ b/src/drc/unit_tests/drcGenericTests.cc @@ -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); +} diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 9f0d45e6a..b3215743f 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -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); +} + diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index ef05a59e3..ccb800826 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -201,25 +201,6 @@ Class decl_Timer ("tl", "Timer", // ---------------------------------------------------------------- // Progress reporter objects -namespace tl { - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - -} - namespace gsi { @@ -247,6 +228,27 @@ Class 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 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); diff --git a/src/lay/lay/layLogViewerDialog.cc b/src/lay/lay/layLogViewerDialog.cc index 5493d7aae..e0ca83912 100644 --- a/src/lay/lay/layLogViewerDialog.cc +++ b/src/lay/lay/layLogViewerDialog.cc @@ -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 (); } diff --git a/src/lay/lay/layLogViewerDialog.h b/src/lay/lay/layLogViewerDialog.h index 69caeb327..c9da714a3 100644 --- a/src/lay/lay/layLogViewerDialog.h +++ b/src/lay/lay/layLogViewerDialog.h @@ -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 (); diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index 28c13a600..36624f20d 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -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::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 ()); } } diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index 570a0506d..7bb7023c0 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -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 mp_objects; tl::Clock m_start_time; lay::ProgressBar *mp_pb; bool m_pw_visible; diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index 703c5ed41..3c4cfe670 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -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; diff --git a/src/lay/lay/layProgressWidget.h b/src/lay/lay/layProgressWidget.h index 4e712be4f..0600f7e5d 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -31,11 +31,14 @@ #include #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); }; } diff --git a/src/lay/lay/laySaltDownloadManager.cc b/src/lay/lay/laySaltDownloadManager.cc index e2f5d07a9..bc3ed4f4e 100644 --- a/src/lay/lay/laySaltDownloadManager.cc +++ b/src/lay/lay/laySaltDownloadManager.cc @@ -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) diff --git a/src/lay/lay/layTextProgress.cc b/src/lay/lay/layTextProgress.cc index 829b84896..6d67fc2b8 100644 --- a/src/lay/lay/layTextProgress.cc +++ b/src/lay/lay/layTextProgress.cc @@ -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 << " .."; diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 17026ae56..c8d20fee9 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -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) { diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index 4015e0107..88d7e4ee0 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -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::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { + k->signal_break (); + } +} + +tl::Progress * +ProgressAdaptor::first () +{ + for (tl::list::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) diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index d8bf73917..e0001cd28 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -31,12 +31,57 @@ #include "tlTimer.h" #include "tlList.h" +#include + class QWidget; namespace tl { class Progress; +class RelativeProgress; +class AbstractProgress; +class AbsoluteProgress; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + 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 mp_valid_objects; +}; /** * @brief The receivers for progress reports @@ -48,20 +93,45 @@ class Progress; class TL_PUBLIC ProgressAdaptor { -public: +public: + typedef tl::list::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 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 diff --git a/src/tl/tl/tlThreadedWorkers.cc b/src/tl/tl/tlThreadedWorkers.cc index 7cd63cf76..48ddbabd0 100644 --- a/src/tl/tl/tlThreadedWorkers.cc +++ b/src/tl/tl/tlThreadedWorkers.cc @@ -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 .. diff --git a/testdata/drc/drcGenericTests_18.drc b/testdata/drc/drcGenericTests_18.drc new file mode 100644 index 000000000..f03aaab29 --- /dev/null +++ b/testdata/drc/drcGenericTests_18.drc @@ -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) + diff --git a/testdata/drc/drcGenericTests_18.gds b/testdata/drc/drcGenericTests_18.gds new file mode 100644 index 000000000..25e1867e1 Binary files /dev/null and b/testdata/drc/drcGenericTests_18.gds differ diff --git a/testdata/drc/drcGenericTests_au18.gds b/testdata/drc/drcGenericTests_au18.gds new file mode 100644 index 000000000..f656a7608 Binary files /dev/null and b/testdata/drc/drcGenericTests_au18.gds differ diff --git a/testdata/drc/drcGenericTests_au18d.gds b/testdata/drc/drcGenericTests_au18d.gds new file mode 100644 index 000000000..f656a7608 Binary files /dev/null and b/testdata/drc/drcGenericTests_au18d.gds differ diff --git a/testdata/drc/drcSimpleTests_29.drc b/testdata/drc/drcSimpleTests_29.drc new file mode 100644 index 000000000..f46052830 --- /dev/null +++ b/testdata/drc/drcSimpleTests_29.drc @@ -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) + diff --git a/testdata/drc/drcSimpleTests_29.gds b/testdata/drc/drcSimpleTests_29.gds new file mode 100644 index 000000000..25e1867e1 Binary files /dev/null and b/testdata/drc/drcSimpleTests_29.gds differ diff --git a/testdata/drc/drcSimpleTests_au29.gds b/testdata/drc/drcSimpleTests_au29.gds new file mode 100644 index 000000000..3c2e167c2 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au29.gds differ diff --git a/testdata/drc/drcSimpleTests_au29d.gds b/testdata/drc/drcSimpleTests_au29d.gds new file mode 100644 index 000000000..3c2e167c2 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au29d.gds differ diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index b5d273646..27cd9b257 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -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")