From b97413234adb91ad34219aa95080eb2b272b3042 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Feb 2021 19:14:20 +0100 Subject: [PATCH 1/6] trying to provide a log along with progress --- src/lay/lay/layProgressWidget.cc | 7 ++++++- src/lay/lay/layProgressWidget.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index 703c5ed41..60c61830c 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -136,7 +137,7 @@ ProgressBarWidget::resizeEvent (QResizeEvent *) ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full_width) : QFrame (parent), - mp_widget (0), mp_pr (pr) + mp_widget (0), mp_pr (pr), m_log_file (6, true) { QVBoxLayout *top_layout = new QVBoxLayout (this); top_layout->addStretch (1); @@ -144,6 +145,10 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full QFrame *bar_frame = new QFrame (this); top_layout->addWidget (bar_frame); + QListView *log_list = new QListView (this); + log_list->setModel (&m_log_file); + top_layout->addWidget (log_list); + top_layout->addStretch (1); // this does not allow the label to control the overall size, so a long string does not hurt: diff --git a/src/lay/lay/layProgressWidget.h b/src/lay/lay/layProgressWidget.h index 4e712be4f..c47ea9a8d 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -31,6 +31,7 @@ #include #include "layProgress.h" +#include "layLogViewerDialog.h" class QToolButton; class QLabel; @@ -73,6 +74,7 @@ private: QGridLayout *mp_layout; QToolButton *mp_cancel_button; ProgressReporter *mp_pr; + lay::LogFile m_log_file; }; } From 620776fe5158d31fc3993c48eba8a677295b68ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Feb 2021 19:15:47 +0100 Subject: [PATCH 2/6] Provide better log output for region --- src/db/db/dbAsIfFlatRegion.cc | 14 ++++++++++++++ src/db/db/dbDeepRegion.cc | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+) 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) { From 94e6f0f7a6d84475f318fd6f651b8b3b00f25f5c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Feb 2021 23:41:53 +0100 Subject: [PATCH 3/6] Logging progress for DRC, introducing 'abstract progress' concept --- src/buddies/src/bd/bdInit.cc | 26 +--- src/drc/drc/built-in-macros/_drc_engine.rb | 16 +- .../drc/built-in-macros/drc_interpreters.lym | 5 + src/gsi/gsi/gsiDeclTl.cc | 40 ++--- src/lay/lay/layLogViewerDialog.cc | 22 +++ src/lay/lay/layLogViewerDialog.h | 12 ++ src/lay/lay/layProgress.cc | 66 +++++---- src/lay/lay/layProgress.h | 7 - src/lay/lay/layProgressWidget.cc | 91 ++++++++++-- src/lay/lay/layProgressWidget.h | 10 ++ src/lay/lay/laySaltDownloadManager.cc | 2 - src/lay/lay/layTextProgress.cc | 4 + src/lym/lym/lymMacro.cc | 3 + src/tl/tl/tlProgress.cc | 81 +++++++++- src/tl/tl/tlProgress.h | 140 ++++++++++++++++-- src/tl/tl/tlThreadedWorkers.cc | 12 -- 16 files changed, 418 insertions(+), 119 deletions(-) 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/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_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/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 60c61830c..3c4cfe670 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -60,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); @@ -135,20 +135,49 @@ 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), m_log_file (6, true) + 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); - QListView *log_list = new QListView (this); - log_list->setModel (&m_log_file); - top_layout->addWidget (log_list); - top_layout->addStretch (1); // this does not allow the label to control the overall size, so a long string does not hurt: @@ -162,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); @@ -176,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); @@ -196,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 * @@ -238,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 c47ea9a8d..0600f7e5d 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -37,6 +37,8 @@ class QToolButton; class QLabel; class QToolButton; class QGridLayout; +class QListView; +class QFrame; namespace tl { @@ -60,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; @@ -75,6 +79,12 @@ private: 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 .. From a9fa5d73f9dd7d25bc7006da0d6f98cc2e5e2d34 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Feb 2021 20:59:17 +0100 Subject: [PATCH 4/6] Introducing 'with_holes' and 'without_holes' in DRC and RBA::Region. --- src/db/db/dbRegionUtils.cc | 28 +++++++++++ src/db/db/dbRegionUtils.h | 45 ++++++++++++++++++ src/db/db/gsiDeclDbRegion.cc | 36 ++++++++++++++ src/db/unit_tests/dbRegionTests.cc | 21 +++++++++ src/drc/drc/built-in-macros/_drc_layer.rb | 57 ++++++++++++++++++++++- src/drc/unit_tests/drcSimpleTests.cc | 11 +++++ testdata/ruby/dbRegionTest.rb | 21 +++++++++ 7 files changed, 218 insertions(+), 1 deletion(-) 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/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_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/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/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") From 1c8eca50bf874f4c739a73a6266880d1aa5b84d9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Feb 2021 20:59:48 +0100 Subject: [PATCH 5/6] Added test case files. --- testdata/drc/drcSimpleTests_29.drc | 25 +++++++++++++++++++++++++ testdata/drc/drcSimpleTests_29.gds | Bin 0 -> 554 bytes testdata/drc/drcSimpleTests_au29.gds | Bin 0 -> 2170 bytes testdata/drc/drcSimpleTests_au29d.gds | Bin 0 -> 2170 bytes 4 files changed, 25 insertions(+) create mode 100644 testdata/drc/drcSimpleTests_29.drc create mode 100644 testdata/drc/drcSimpleTests_29.gds create mode 100644 testdata/drc/drcSimpleTests_au29.gds create mode 100644 testdata/drc/drcSimpleTests_au29d.gds 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 0000000000000000000000000000000000000000..25e1867e1ccb9a41d3ed44fdf504766abdceca6b GIT binary patch literal 554 zcmaLTKQ9D99LDkQ&F#%@h|QfWiE||viOW$CK|+Yazo--t^NP?fH9k zXRz?}czR5y@lVhDtBXrWGVo)L8a{VGuUUum2&UJD_IuF22Geqxv?BeP2W{L#aOxH| z^<~p?8SRf0>VH76InXTQrIyQT-&N-y&7u1W!TED&bxSRm(f-`KIxi5M{LH)c7&magX#8MkOdkaZn zEk1%TU@Ja?_m~`Z5k}A=#a7>PXU@*OXATSkIJbvt%H`hS5Tb-8%JFZ6F4*2!1xo4c z(#lnH>*e8deyuxmf49GjYW5dh5L}#{1DOyAMjTQI@(w_~kOp!)z{En2jXF@d1Vq)1 znW9HOd20(d09oz;%l2ncb)#25R5JY=Ko+{z%V_UNBT$KNctdOolSB3@Q)u P@B2Of%X^Tsr#FKybIAbH literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au29d.gds b/testdata/drc/drcSimpleTests_au29d.gds new file mode 100644 index 0000000000000000000000000000000000000000..3c2e167c2b7178b0e7da8070ab477a0182b69ddd GIT binary patch literal 2170 zcmds&y-EW?6opSF$!1Xui9{hWr3Aqc8zrcqC>H)c7&magX#8MkOdkaZn zEk1%TU@Ja?_m~`Z5k}A=#a7>PXU@*OXATSkIJbvt%H`hS5Tb-8%JFZ6F4*2!1xo4c z(#lnH>*e8deyuxmf49GjYW5dh5L}#{1DOyAMjTQI@(w_~kOp!)z{En2jXF@d1Vq)1 znW9HOd20(d09oz;%l2ncb)#25R5JY=Ko+{z%V_UNBT$KNctdOolSB3@Q)u P@B2Of%X^Tsr#FKybIAbH literal 0 HcmV?d00001 From b3685c67225eb89caae28d8a5b469b7be72ceb52 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Feb 2021 21:29:41 +0100 Subject: [PATCH 6/6] Implemented with_holes for generic DRC too --- src/db/db/gsiDeclDbCompoundOperation.cc | 11 ++++++ .../drc/built-in-macros/_drc_complex_ops.rb | 32 +++++++++++++++--- .../built-in-macros/_drc_cop_integration.rb | 13 +++++++ src/drc/unit_tests/drcGenericTests.cc | 10 ++++++ testdata/drc/drcGenericTests_18.drc | 25 ++++++++++++++ testdata/drc/drcGenericTests_18.gds | Bin 0 -> 554 bytes testdata/drc/drcGenericTests_au18.gds | Bin 0 -> 1962 bytes testdata/drc/drcGenericTests_au18d.gds | Bin 0 -> 1962 bytes 8 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 testdata/drc/drcGenericTests_18.drc create mode 100644 testdata/drc/drcGenericTests_18.gds create mode 100644 testdata/drc/drcGenericTests_au18.gds create mode 100644 testdata/drc/drcGenericTests_au18d.gds 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/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/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/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 0000000000000000000000000000000000000000..25e1867e1ccb9a41d3ed44fdf504766abdceca6b GIT binary patch literal 554 zcmaLTKQ9D99LDkQ&F#%@h|QfWiE||viOW$CK|+Yazo--t^NP?fH9k zXRz?}czR5y@lVhDtBXrWGVo)L8a{VGuUUum2&UJD_IuF22Geqxv?BeP2W{L#aOxH| z^<~p?8SRf0>VH76InXTQrIyQT-&N-y&7u1W!TED&bxSRm(f-`KIxi5M{LB$5awDJ2Mo*eIfcqFDHcm4!%3jX#YRK7$}wh^01m_7;-D zT6_dwz*c+&&zKCm2v^V|MXE2Hy|d?>xkDBKoZCe$<-&J3L@1+*T=F*}7i_Js0Ht(x zarts~^X1`lZnZOYf48@TTJ{%R5S*W$0htI0h8g zrs&a+-`ay4fE>F5mh8=<>PD}AsAT##fGl)umXVmM8Q4YU*#?UBn5rAS z`iWQ5JObqChwZ)*Q+1?Ngw2ke4;<}MLy}AeAjzPZ=9zg=E|e4YW-gQy=AY+CB$5awDJ2Mo*eIfcqFDHcm4!%3jX#YRK7$}wh^01m_7;-D zT6_dwz*c+&&zKCm2v^V|MXE2Hy|d?>xkDBKoZCe$<-&J3L@1+*T=F*}7i_Js0Ht(x zarts~^X1`lZnZOYf48@TTJ{%R5S*W$0htI0h8g zrs&a+-`ay4fE>F5mh8=<>PD}AsAT##fGl)umXVmM8Q4YU*#?UBn5rAS z`iWQ5JObqChwZ)*Q+1?Ngw2ke4;<}MLy}AeAjzPZ=9zg=E|e4YW-gQy=AY+C