From c43b70b783f54d23213157722e42fe45af88fdec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Mar 2024 21:47:37 +0100 Subject: [PATCH 01/17] Shape#hash and Shape#< allow using Shape objects as hash keys --- Changelog | 1 + src/db/db/dbShape.cc | 19 +++++++++++ src/db/db/dbShape.h | 20 +++++++++++ src/db/db/gsiDeclDbShape.cc | 15 +++++++++ src/pya/unit_tests/pyaTests.cc | 1 + testdata/python/dbShapesTest.py | 59 +++++++++++++++++++++++++++++++++ testdata/ruby/dbShapesTest.rb | 29 ++++++++++++++++ 7 files changed, 144 insertions(+) create mode 100644 testdata/python/dbShapesTest.py diff --git a/Changelog b/Changelog index 323f705d4..57b377c30 100644 --- a/Changelog +++ b/Changelog @@ -43,6 +43,7 @@ using them to select edges. * Enhancement: New RBA/pya Features - Main window title: MainWindow#title (property) + - MainWindow#synchronous (getter added) - LayoutView#is_dirty? - Triangulation: Region#delaunay - Quality rasterizer: Region#rasterize diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index e5c1d7820..648221066 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -24,6 +24,7 @@ #include "dbShape.h" #include "dbBoxConvert.h" #include "dbPolygonTools.h" +#include "dbHash.h" #include "tlCpp.h" namespace db @@ -862,6 +863,24 @@ Shape::box_type Shape::rectangle () const return box_type (); } +size_t +Shape::hash_value () const +{ + size_t h = size_t (m_type); + h = std::hcombine (h, std::hfunc (m_trans)); + + if (m_stable) { + // Use the bytes of the iterator binary pattern (see operator<) + for (unsigned int i = 0; i < sizeof (tl::reuse_vector::const_iterator); ++i) { + h = std::hcombine (h, size_t (m_generic.iter[i])); + } + } else { + h = std::hcombine (h, size_t (m_generic.any)); + } + + return h; +} + std::string Shape::to_string () const { diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index dd003b47e..469351376 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2769,6 +2769,11 @@ public: return m_trans < d.m_trans; } + /** + * @brief Hash value + */ + size_t hash_value () const; + /** * @brief Convert to a string */ @@ -2837,6 +2842,21 @@ public: }; } // namespace db + +namespace std +{ + + // provide a template specialization for std::hash + template <> + struct hash + { + size_t operator() (const db::Shape &s) const + { + return s.hash_value (); + } + }; + +} // namespace std #endif diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index 68f6ff4fb..6d03cf26c 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -2131,6 +2131,21 @@ Class decl_Shape ("db", "Shape", "Equality of shapes is not specified by the identity of the objects but by the\n" "identity of the pointers - both shapes must refer to the same object.\n" ) + + gsi::method ("<", &db::Shape::operator<, gsi::arg ("other"), + "@brief Less operator\n" + "\n" + "The less operator implementation is based on pointers and not strictly reproducible." + "However, it is good enough so Shape objects can serve as keys in hashes (see also \\hash).\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method ("hash", &db::Shape::hash_value, + "@brief Hash function\n" + "\n" + "The hash function enables Shape objects as keys in hashes.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("to_s", &db::Shape::to_string, "@brief Create a string showing the contents of the reference\n" "\n" diff --git a/src/pya/unit_tests/pyaTests.cc b/src/pya/unit_tests/pyaTests.cc index d14c393cd..7fb7d0366 100644 --- a/src/pya/unit_tests/pyaTests.cc +++ b/src/pya/unit_tests/pyaTests.cc @@ -99,6 +99,7 @@ void run_pythontest (tl::TestBase *_this, const std::string &fn) PYTHONTEST (kwargs, "kwargs.py") PYTHONTEST (dbLayoutTest, "dbLayoutTest.py") PYTHONTEST (dbRegionTest, "dbRegionTest.py") +PYTHONTEST (dbShapesTest, "dbShapesTest.py") PYTHONTEST (dbReaders, "dbReaders.py") PYTHONTEST (dbPCellsTest, "dbPCells.py") PYTHONTEST (dbPolygonTest, "dbPolygonTest.py") diff --git a/testdata/python/dbShapesTest.py b/testdata/python/dbShapesTest.py new file mode 100644 index 000000000..f5a2482e8 --- /dev/null +++ b/testdata/python/dbShapesTest.py @@ -0,0 +1,59 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2024 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +import pya +import unittest +import sys +import os + +class DBShapesTest(unittest.TestCase): + + # Shape objects as hashes + def test_12(self): + + s = pya.Shapes() + s1 = s.insert(pya.Box(1, 2, 3, 4)) + s2 = s.insert(pya.Polygon(pya.Box(1, 2, 3, 4))) + s3 = s.insert(pya.SimplePolygon(pya.Box(1, 2, 3, 4))) + + self.assertEqual(s1.hash != s2.hash, True) # let's hope so ... + self.assertEqual(s1.hash != s3.hash, True) + self.assertEqual(s2.hash != s3.hash, True) + + self.assertEqual(s1 < s2 or s2 < s1, True) + self.assertEqual(s1 < s3 or s3 < s1, True) + self.assertEqual(s2 < s3 or s3 < s2, True) + + h = {} + h[s1] = 1 + h[s2] = 2 + h[s3] = 3 + + self.assertEqual(len(h), 3) + + self.assertEqual(h[s1], 1) + self.assertEqual(h[s2], 2) + self.assertEqual(h[s3], 3) + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(DBShapesTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index bca7672cf..61b8c9243 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -1627,6 +1627,35 @@ class DBShapes_TestClass < TestBase end + # Shape objects as hashes + def test_12 + + s = RBA::Shapes::new + s1 = s.insert(RBA::Box::new(1, 2, 3, 4)) + s2 = s.insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4))) + s3 = s.insert(RBA::SimplePolygon::new(RBA::Box::new(1, 2, 3, 4))) + + assert_equal(s1.hash != s2.hash, true) # let's hope so ... + assert_equal(s1.hash != s3.hash, true) + assert_equal(s2.hash != s3.hash, true) + + assert_equal(s1 < s2 || s2 < s1, true) + assert_equal(s1 < s3 || s3 < s1, true) + assert_equal(s2 < s3 || s3 < s2, true) + + h = {} + h[s1] = 1 + h[s2] = 2 + h[s3] = 3 + + assert_equal(h.size, 3) + + assert_equal(h[s1], 1) + assert_equal(h[s2], 2) + assert_equal(h[s3], 3) + + end + end load("test_epilogue.rb") From b40a7f9575703ca660d30f59f83a2b19c2c16089 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Mar 2024 22:59:12 +0100 Subject: [PATCH 02/17] WIP --- src/edt/edt/edt.pro | 2 + src/edt/edt/edtEditorHooks.cc | 133 +++++++++++++++++++++++++++ src/edt/edt/edtEditorHooks.h | 167 ++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 src/edt/edt/edtEditorHooks.cc create mode 100644 src/edt/edt/edtEditorHooks.h diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 477e14ff4..97ce17c08 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -36,6 +36,7 @@ DEFINES += MAKE_EDT_LIBRARY HEADERS = \ edtDialogs.h \ + edtEditorHooks.h \ edtEditorOptionsPages.h \ edtInstPropertiesPage.h \ edtPCellParametersPage.h \ @@ -45,6 +46,7 @@ HEADERS = \ SOURCES = \ edtDialogs.cc \ + edtEditorHooks.cc \ edtEditorOptionsPages.cc \ edtInstPropertiesPage.cc \ edtPCellParametersPage.cc \ diff --git a/src/edt/edt/edtEditorHooks.cc b/src/edt/edt/edtEditorHooks.cc new file mode 100644 index 000000000..b369c5950 --- /dev/null +++ b/src/edt/edt/edtEditorHooks.cc @@ -0,0 +1,133 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "edtEditorHooks.h" +#include "tlObjectCollection.h" + +namespace edt +{ + +// --------------------------------------------------------------- +// EditorHooksManager definition and implementation + +class EditorHooksManager +{ +public: + EditorHooksManager () { } + + static EditorHooksManager *instance () + { + static EditorHooksManager *sp_instance = 0; + if (! sp_instance) { + sp_instance = new EditorHooksManager (); + } + return sp_instance; + } + + void register_editor_hook (EditorHooks *hook) + { + m_hooks.push_back (hook); + } + + std::vector get_editor_hooks (const std::string &for_technology) + { + std::vector res; + for (auto h = m_hooks.begin (); h != m_hooks.end (); ++h) { + if (! h->for_technologies () || h->is_for_technology (for_technology)) { + res.push_back (h.operator-> ()); + } + } + + return res; + } + +private: + tl::shared_collection m_hooks; +}; + +// --------------------------------------------------------------- +// EditorHooks implementation + +EditorHooks::EditorHooks () +{ + // .. nothing yet .. +} + +EditorHooks::~EditorHooks () +{ + // .. nothing yet .. +} + +bool +EditorHooks::is_for_technology (const std::string &name) const +{ + return m_technologies.find (name) != m_technologies.end (); +} + +bool +EditorHooks::for_technologies () const +{ + return ! m_technologies.empty (); +} + +void +EditorHooks::set_technology (const std::string &t) +{ + m_technologies.clear (); + if (! t.empty ()) { + m_technologies.insert (t); + } +} + +void +EditorHooks::clear_technologies () +{ + m_technologies.clear (); +} + +void +EditorHooks::add_technology (const std::string &tech) +{ + m_technologies.insert (tech); +} + +void +EditorHooks::register_editor_hook (EditorHooks *hook) +{ + if (EditorHooksManager::instance ()) { + hook->keep (); + EditorHooksManager::instance ()->register_editor_hook (hook); + } +} + +std::vector +EditorHooks::get_editor_hooks (const std::string &for_technology) +{ + if (EditorHooksManager::instance ()) { + return EditorHooksManager::instance ()->get_editor_hooks (for_technology); + } else { + return std::vector (); + } +} + +} diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h new file mode 100644 index 000000000..4f93986cc --- /dev/null +++ b/src/edt/edt/edtEditorHooks.h @@ -0,0 +1,167 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_edtEditorHooks +#define HDR_edtEditorHooks + +#include "edtCommon.h" +#include "dbTrans.h" +#include "gsiObject.h" + +#include +#include + +namespace lay +{ + class LayoutView; + class ObjectInstPath; +} + +namespace edt +{ + +/** + * @ brief The editor hooks handler object + * + * Editor hooks are a way to hook into the editor feature - for example + * to implement dynamic DRC or display hints. + * + * The protocols are: + * + * 1. Object Creation + * + * begin_create { begin_new_objects { create } end_new_objects } [ commit_create ] end_create + * + * 2. Modification (i.e. partial edit) + * + * begin_modify { begin_modifications { modified } end_modifications } [ commit_modify ] end_modify + * + * 3. Interactive edit (move, transform, interactive clone) + * + * begin_edit { begin_edits { transformed } end_edits } [ commit_edit ] end_edit + * + * Notation: { ... } means the sequence can be repeated, [ ... ] means the call is optional. + */ + +class EDT_PUBLIC EditorHooks + : public gsi::ObjectBase, public tl::Object +{ +public: + /** + * @brief Constructor + */ + EditorHooks (); + + /** + * @brief Destructor + */ + virtual ~EditorHooks (); + + // creation protocol + virtual void begin_create (lay::LayoutView * /*view*/) { } + virtual void begin_new_objects () { } + virtual void create (const lay::ObjectInstPath & /*object*/, double /*dbu*/) { } + virtual void end_new_objects () { } + virtual void commit_create () { } + virtual void end_create () { } + + // modification protocol + virtual void begin_modify (lay::LayoutView * /*view*/) { } + virtual void begin_modifications () { } + virtual void modified (const lay::ObjectInstPath & /*object*/, double /*dbu*/) { } + virtual void end_modifications () { } + virtual void commit_modfiy () { } + virtual void end_modify () { } + + // editing protocol + virtual void begin_edit (lay::LayoutView * /*view*/) { } + virtual void begin_edits () { } + virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::DCplxTrans & /*trans*/, double /*dbu*/) { } + virtual void end_edits () { } + virtual void commit_edit () { } + virtual void end_edit () { } + + /** + * @brief Gets the technology name this hook is associated with + * + * If this attribute is non-empty, the hook is selected only when the given technology is + * used for the layout. + */ + const std::set &get_technologies () const + { + return m_technologies; + } + + /** + * @brief Gets a value indicating whether this hook is associated with the given technology + */ + bool is_for_technology (const std::string &name) const; + + /** + * @brief Gets a value indicating whether the hook is associated with any technology + */ + bool for_technologies () const; + + /** + * @brief Sets the technology name this hook is associated with + * + * This will reset the list of technologies to this one. + * If the given technology string is empty, the list of technologies will be cleared. + */ + void set_technology (const std::string &t); + + /** + * @brief Clears the list of technologies this hook is associated with + */ + void clear_technologies (); + + /** + * @brief Additionally associate the hook with the given technology + */ + void add_technology (const std::string &tech); + + /** + * @brief Registers the editor hook + */ + static void register_editor_hook (EditorHooks *hook); + + /** + * @brief Gets the editor hooks for a given technology + * + * The order of the hooks is determined by the registration order. + */ + static std::vector get_editor_hooks (const std::string &for_technology); + +private: + std::set m_technologies; + + // no copying. + EditorHooks &operator= (const EditorHooks &); + EditorHooks (const EditorHooks &); +}; + +} + +#endif + From 876215f6c75c6405b38ca18468166e8ba6ea6ceb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 00:12:24 +0100 Subject: [PATCH 03/17] WIP --- src/edt/edt/edt.pro | 3 +- src/edt/edt/edtEditorHooks.h | 2 +- src/edt/edt/gsiDeclEdtEditorHooks.cc | 355 +++++++++++++++++++++++++++ 3 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 src/edt/edt/gsiDeclEdtEditorHooks.cc diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 97ce17c08..300f3faa8 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -52,7 +52,8 @@ SOURCES = \ edtPCellParametersPage.cc \ edtPropertiesPages.cc \ edtPropertiesPageUtils.cc \ - edtRecentConfigurationPage.cc + edtRecentConfigurationPage.cc \ + gsiDeclEdtEditorHooks.cc # Enabled without Qt: diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index 4f93986cc..a442dbf00 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -91,7 +91,7 @@ public: virtual void begin_modifications () { } virtual void modified (const lay::ObjectInstPath & /*object*/, double /*dbu*/) { } virtual void end_modifications () { } - virtual void commit_modfiy () { } + virtual void commit_modify () { } virtual void end_modify () { } // editing protocol diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc new file mode 100644 index 000000000..50ac6ab80 --- /dev/null +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -0,0 +1,355 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "edtEditorHooks.h" +#include "layObjectInstPath.h" +#include "layLayoutView.h" + +namespace gsi +{ +gsi::Class decl_EditorHooksBase ("lay", "EditorHooksBase", + gsi::method ("begin_create", &edt::EditorHooks::begin_create, gsi::arg ("view"), + "@brief Creation protocol - begin session\n" + "This method is called to initiate an object creation session. The session is ended with " + "\\end_create. Between these calls, new objects are announced with \\begin_new_objects, " + "\\create and \\end_new_objects calls. These calls are repeated to indicate changes in the objects " + "created.\n" + "\n" + "\\commit_create is called once before \\end_create to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::method ("begin_new_objects", &edt::EditorHooks::begin_new_objects, + "@brief Creation protocol - begin new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::method ("create", &edt::EditorHooks::create, gsi::arg ("object"), gsi::arg ("dbu"), + "@brief Creation protocol - indicate a new object\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::method ("end_new_objects", &edt::EditorHooks::end_new_objects, + "@brief Creation protocol - finish list of new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::method ("commit_create", &edt::EditorHooks::commit_create, + "@brief Creation protocol - commit new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::method ("end_create", &edt::EditorHooks::end_create, + "@brief Creation protocol - finish session\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::method ("begin_modify", &edt::EditorHooks::begin_modify, gsi::arg ("view"), + "@brief Modification protocol - begin session\n" + "This method is called to initiate an object modification session. The session is ended with " + "\\end_modify. Between these calls, modified objects are announced with \\begin_modifications, " + "\\modified and \\end_modifications calls. These calls are repeated to indicate changes in the objects " + "modified while moving the mouse for example.\n" + "\n" + "\\commit_modify is called once before \\end_modify to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::method ("begin_modifications", &edt::EditorHooks::begin_modifications, + "@brief Modification protocol - begin modifications\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::method ("modified", &edt::EditorHooks::modified, gsi::arg ("object"), gsi::arg ("dbu"), + "@brief Modification protocol - indicate a modified object\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::method ("end_modifications", &edt::EditorHooks::end_modifications, + "@brief Modification protocol - finish list of modifications\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::method ("commit_modify", &edt::EditorHooks::commit_modify, + "@brief Modification protocol - commit new objects\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::method ("end_modify", &edt::EditorHooks::end_modify, + "@brief Modification protocol - finish session\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::method ("begin_edit", &edt::EditorHooks::begin_edit, gsi::arg ("view"), + "@brief Editing protocol - begin session\n" + "This method is called to initiate an object editing session. The session is ended with " + "\\end_edit. Between these calls, edits are announced with \\begin_edits, " + "\\transformed and \\end_edits calls. These calls are repeated to indicate changes in the objects " + "modified while moving the mouse for example.\n" + "\n" + "\\commit_edit is called once before \\end_edit to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::method ("begin_edits", &edt::EditorHooks::begin_edits, + "@brief Editing protocol - begin edits\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("transformed", &edt::EditorHooks::transformed, gsi::arg ("object"), gsi::arg ("trans"), gsi::arg ("dbu"), + "@brief Editing protocol - indicate an object transformation\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("end_edits", &edt::EditorHooks::end_edits, + "@brief Editing protocol - finish list of edits\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("commit_edit", &edt::EditorHooks::commit_edit, + "@brief Editing protocol - commit new objects\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("end_edit", &edt::EditorHooks::end_edit, + "@brief Editing protocol - finish session\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("technology=", &edt::EditorHooks::set_technology, gsi::arg ("technology"), + "@brief sets the name of the technology the hooks are associated with\n" + "This will clear all technology associations and associate the hooks with that technology only.\n" + ) + + gsi::method ("clear_technologies", &edt::EditorHooks::clear_technologies, + "@brief Clears the list of technologies the hooks are associated with.\n" + "See also \\add_technology.\n" + ) + + gsi::method ("add_technology", &edt::EditorHooks::add_technology, gsi::arg ("tech"), + "@brief Additionally associates the hooks with the given technology.\n" + "See also \\clear_technologies.\n" + ) + + gsi::method ("is_for_technology", &edt::EditorHooks::is_for_technology, gsi::arg ("tech"), + "@brief Returns a value indicating whether the hooks are associated with the given technology.\n" + ) + + gsi::method ("for_technologies", &edt::EditorHooks::for_technologies, + "@brief Returns a value indicating whether the hooks are associated with any technology.\n" + "The method is equivalent to checking whether the \\technologies list is empty.\n" + ) + + gsi::method ("technologies", &edt::EditorHooks::get_technologies, + "@brief Gets the list of technologies these hooks are associated with.\n" + ), + "@brief The base class for editor hooks\n" + "Editor hooks allow implementing technology-specific callbacks into the editor " + "for example to implement visual feedback about DRC rules.\n" + "\n" + "This class provides the basic interface. To implement callbacks, use the \\EditorHooks class." + "\n" + "The EditorHooksBase class has been introduced in version 0.29." +); + +class EditorHooksImpl + : public edt::EditorHooks +{ +public: + EditorHooksImpl () { } + + virtual void begin_create (lay::LayoutView *view) + { + if (f_begin_create.can_issue ()) { + f_begin_create.issue (&edt::EditorHooks::begin_create, view); + } else { + edt::EditorHooks::begin_create (view); + } + } + + virtual void begin_new_objects () + { + if (f_begin_new_objects.can_issue ()) { + f_begin_new_objects.issue (&edt::EditorHooks::begin_new_objects); + } else { + edt::EditorHooks::begin_new_objects (); + } + } + + virtual void create (const lay::ObjectInstPath &object, double dbu) + { + if (f_create.can_issue ()) { + f_create.issue (&edt::EditorHooks::create, object, dbu); + } else { + edt::EditorHooks::create (object, dbu); + } + } + + virtual void end_new_objects () + { + if (f_end_new_objects.can_issue ()) { + f_end_new_objects.issue (&edt::EditorHooks::end_new_objects); + } else { + edt::EditorHooks::end_new_objects (); + } + } + + virtual void commit_create () + { + if (f_commit_create.can_issue ()) { + f_commit_create.issue (&edt::EditorHooks::commit_create); + } else { + edt::EditorHooks::commit_create (); + } + } + + virtual void end_create () + { + if (f_end_create.can_issue ()) { + f_end_create.issue (&edt::EditorHooks::end_create); + } else { + edt::EditorHooks::end_create (); + } + } + + virtual void begin_modify (lay::LayoutView *view) + { + if (f_begin_modify.can_issue ()) { + f_begin_modify.issue (&edt::EditorHooks::begin_modify, view); + } else { + edt::EditorHooks::begin_modify (view); + } + } + + virtual void begin_modifications () + { + if (f_begin_modifications.can_issue ()) { + f_begin_modifications.issue (&edt::EditorHooks::begin_modifications); + } else { + edt::EditorHooks::begin_modifications (); + } + } + + virtual void modified (const lay::ObjectInstPath &object, double dbu) + { + if (f_modified.can_issue ()) { + f_modified.issue (&edt::EditorHooks::modified, object, dbu); + } else { + edt::EditorHooks::modified (object, dbu); + } + } + + virtual void end_modifications () + { + if (f_end_modifications.can_issue ()) { + f_end_modifications.issue (&edt::EditorHooks::end_modifications); + } else { + edt::EditorHooks::end_modifications (); + } + } + + virtual void commit_modify () + { + if (f_commit_modify.can_issue ()) { + f_commit_modify.issue (&edt::EditorHooks::commit_modify); + } else { + edt::EditorHooks::commit_modify (); + } + } + + virtual void end_modify () + { + if (f_end_modify.can_issue ()) { + f_end_modify.issue (&edt::EditorHooks::end_modify); + } else { + edt::EditorHooks::end_modify (); + } + } + + virtual void begin_edit (lay::LayoutView *view) + { + if (f_begin_edit.can_issue ()) { + f_begin_edit.issue (&edt::EditorHooks::begin_edit, view); + } else { + edt::EditorHooks::begin_edit (view); + } + } + + virtual void begin_edits () + { + if (f_begin_edits.can_issue ()) { + f_begin_edits.issue (&edt::EditorHooks::begin_edits); + } else { + edt::EditorHooks::begin_edits (); + } + } + + virtual void transformed (const lay::ObjectInstPath &object, const db::DCplxTrans &trans, double dbu) + { + if (f_transformed.can_issue ()) { + f_transformed.issue (&edt::EditorHooks::transformed, object, trans, dbu); + } else { + edt::EditorHooks::transformed (object, trans, dbu); + } + } + + virtual void end_edits () + { + if (f_end_edits.can_issue ()) { + f_end_edits.issue (&edt::EditorHooks::end_edits); + } else { + edt::EditorHooks::end_edits (); + } + } + + virtual void commit_edit () + { + if (f_commit_edit.can_issue ()) { + f_commit_edit.issue (&edt::EditorHooks::commit_edit); + } else { + edt::EditorHooks::commit_edit (); + } + } + + virtual void end_edit () + { + if (f_end_edit.can_issue ()) { + f_end_edit.issue (&edt::EditorHooks::end_edit); + } else { + edt::EditorHooks::end_edit (); + } + } + + gsi::Callback f_begin_create; + gsi::Callback f_begin_new_objects; + gsi::Callback f_create; + gsi::Callback f_end_new_objects; + gsi::Callback f_commit_create; + gsi::Callback f_end_create; + + gsi::Callback f_begin_modify; + gsi::Callback f_begin_modifications; + gsi::Callback f_modified; + gsi::Callback f_end_modifications; + gsi::Callback f_commit_modify; + gsi::Callback f_end_modify; + + gsi::Callback f_begin_edit; + gsi::Callback f_begin_edits; + gsi::Callback f_transformed; + gsi::Callback f_end_edits; + gsi::Callback f_commit_edit; + gsi::Callback f_end_edit; +}; + +gsi::Class decl_EditorHooks ("lay", "EditorHooks", + callback ("begin_create", &EditorHooksImpl::begin_create, &EditorHooksImpl::f_begin_create, + "@brief Creation protocol - begin session\n" + "See \\EditorHooksBase for a description of the protocol" + ), + "@brief An implementation base class for editor hooks\n" + // @@@ + "\n" + "The EditorHooks class has been introduced in version 0.29." +); + +} + From 80d1aad90a6c539663a8c684781ff0d1acf5e12f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 00:21:16 +0100 Subject: [PATCH 04/17] WIP --- src/edt/edt/edtEditorHooks.cc | 19 ++++++++++++++++--- src/edt/edt/gsiDeclEdtEditorHooks.cc | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/edt/edt/edtEditorHooks.cc b/src/edt/edt/edtEditorHooks.cc index b369c5950..f9bacc137 100644 --- a/src/edt/edt/edtEditorHooks.cc +++ b/src/edt/edt/edtEditorHooks.cc @@ -23,6 +23,7 @@ #include "edtEditorHooks.h" #include "tlObjectCollection.h" +#include "tlStaticObjects.h" namespace edt { @@ -30,16 +31,28 @@ namespace edt // --------------------------------------------------------------- // EditorHooksManager definition and implementation +static EditorHooksManager *sp_instance = 0; +static bool sp_instance_initialized = false; + class EditorHooksManager { public: - EditorHooksManager () { } + EditorHooksManager () + { + // .. nothing yet .. + } + + ~EditorHooksManager () + { + sp_instance = 0; + } static EditorHooksManager *instance () { - static EditorHooksManager *sp_instance = 0; - if (! sp_instance) { + if (! sp_instance && ! sp_instance_initialized) { sp_instance = new EditorHooksManager (); + sp_instance_initialized = true; + tl::StaticObjects::reg (&sp_instance); } return sp_instance; } diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 50ac6ab80..5350014c2 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -140,6 +140,11 @@ gsi::Class decl_EditorHooksBase ("lay", "EditorHooksBase", ) + gsi::method ("technologies", &edt::EditorHooks::get_technologies, "@brief Gets the list of technologies these hooks are associated with.\n" + ) + + gsi::method ("register", &edt::EditorHooks::register_editor_hook, gsi::arg ("hooks"), + "@brief Registers the hooks in the system.\n" + "The hooks will not be active before they are registered in the system. Registration will " + "also transfer object ownership to the system." ), "@brief The base class for editor hooks\n" "Editor hooks allow implementing technology-specific callbacks into the editor " From d1216b5891dd8771d2db118523a111c02bffd83a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 00:21:41 +0100 Subject: [PATCH 05/17] WIP --- src/edt/edt/edtEditorHooks.cc | 2 + src/edt/edt/edtEditorHooks.h | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/edt/edt/edtEditorHooks.cc b/src/edt/edt/edtEditorHooks.cc index f9bacc137..5641ddd20 100644 --- a/src/edt/edt/edtEditorHooks.cc +++ b/src/edt/edt/edtEditorHooks.cc @@ -31,6 +31,8 @@ namespace edt // --------------------------------------------------------------- // EditorHooksManager definition and implementation +class EditorHooksManager; + static EditorHooksManager *sp_instance = 0; static bool sp_instance_initialized = false; diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index a442dbf00..5a557a4b3 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -28,6 +28,7 @@ #include "edtCommon.h" #include "dbTrans.h" #include "gsiObject.h" +#include "tlExceptions.h" #include #include @@ -161,6 +162,87 @@ private: EditorHooks (const EditorHooks &); }; +/** + * @brief A helper function to call editor hooks in the right sequence and with error handling + */ + +inline +void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) ()) +{ + for (auto h = hooks.begin (); h != hooks.end (); ++h) { +BEGIN_PROTECTED + try { + ((*h)->*meth) (); + } catch (tl::CancelException &) { + return; + } +END_PROTECTED + } +} + +/** + * @brief A helper function to call editor hooks in the right sequence and with error handling + * + * This version provides one argument + */ + +template +inline +void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1), A1 a1) +{ + for (auto h = hooks.begin (); h != hooks.end (); ++h) { +BEGIN_PROTECTED + try { + ((*h)->*meth) (a1); + } catch (tl::CancelException &) { + return; + } +END_PROTECTED + } +} + +/** + * @brief A helper function to call editor hooks in the right sequence and with error handling + * + * This version provides two arguments + */ + +template +inline +void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2) +{ + for (auto h = hooks.begin (); h != hooks.end (); ++h) { +BEGIN_PROTECTED + try { + ((*h)->*meth) (a1, a2); + } catch (tl::CancelException &) { + return; + } +END_PROTECTED + } +} + +/** + * @brief A helper function to call editor hooks in the right sequence and with error handling + * + * This version provides three arguments + */ + +template +inline +void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2, A3 a3) +{ + for (auto h = hooks.begin (); h != hooks.end (); ++h) { +BEGIN_PROTECTED + try { + ((*h)->*meth) (a1, a2, a3); + } catch (tl::CancelException &) { + return; + } +END_PROTECTED + } +} + } #endif From 1868dcc1048a2413ef88df7ee7c4ace4251d3c17 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 00:58:09 +0100 Subject: [PATCH 06/17] Copy ctor, swap and move for tl::shared_collection and tl::weak_collection --- src/edt/edt/edtEditorHooks.cc | 9 ++-- src/edt/edt/edtEditorHooks.h | 27 ++++++++---- src/tl/tl/tlObjectCollection.h | 69 +++++++++++++++++++++++++----- src/tl/unit_tests/tlObjectTests.cc | 42 ++++++++++++++++++ 4 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/edt/edt/edtEditorHooks.cc b/src/edt/edt/edtEditorHooks.cc index 5641ddd20..eaca9b718 100644 --- a/src/edt/edt/edtEditorHooks.cc +++ b/src/edt/edt/edtEditorHooks.cc @@ -64,9 +64,10 @@ public: m_hooks.push_back (hook); } - std::vector get_editor_hooks (const std::string &for_technology) + tl::weak_collection + get_editor_hooks (const std::string &for_technology) { - std::vector res; + tl::weak_collection res; for (auto h = m_hooks.begin (); h != m_hooks.end (); ++h) { if (! h->for_technologies () || h->is_for_technology (for_technology)) { res.push_back (h.operator-> ()); @@ -135,13 +136,13 @@ EditorHooks::register_editor_hook (EditorHooks *hook) } } -std::vector +tl::weak_collection EditorHooks::get_editor_hooks (const std::string &for_technology) { if (EditorHooksManager::instance ()) { return EditorHooksManager::instance ()->get_editor_hooks (for_technology); } else { - return std::vector (); + return tl::weak_collection (); } } diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index 5a557a4b3..076fa193b 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -29,6 +29,7 @@ #include "dbTrans.h" #include "gsiObject.h" #include "tlExceptions.h" +#include "tlObjectCollection.h" #include #include @@ -152,7 +153,7 @@ public: * * The order of the hooks is determined by the registration order. */ - static std::vector get_editor_hooks (const std::string &for_technology); + static tl::weak_collection get_editor_hooks (const std::string &for_technology); private: std::set m_technologies; @@ -167,12 +168,14 @@ private: */ inline -void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) ()) +void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) ()) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { BEGIN_PROTECTED try { - ((*h)->*meth) (); + if (h.operator-> ()) { + (const_cast (h.operator-> ())->*meth) (); + } } catch (tl::CancelException &) { return; } @@ -188,12 +191,14 @@ END_PROTECTED template inline -void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1), A1 a1) +void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1), A1 a1) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { BEGIN_PROTECTED try { - ((*h)->*meth) (a1); + if (h.operator-> ()) { + (const_cast (h.operator-> ())->*meth) (a1); + } } catch (tl::CancelException &) { return; } @@ -209,12 +214,14 @@ END_PROTECTED template inline -void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2) +void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { BEGIN_PROTECTED try { - ((*h)->*meth) (a1, a2); + if (h.operator-> ()) { + (const_cast (h.operator-> ())->*meth) (a1, a2); + } } catch (tl::CancelException &) { return; } @@ -230,12 +237,14 @@ END_PROTECTED template inline -void call_editor_hooks (const std::vector &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2, A3 a3) +void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2, A3 a3) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { BEGIN_PROTECTED try { - ((*h)->*meth) (a1, a2, a3); + if (h.operator-> ()) { + (const_cast (h.operator-> ())->*meth) (a1, a2, a3); + } } catch (tl::CancelException &) { return; } diff --git a/src/tl/tl/tlObjectCollection.h b/src/tl/tl/tlObjectCollection.h index 878e09dab..c2a4ff820 100644 --- a/src/tl/tl/tlObjectCollection.h +++ b/src/tl/tl/tlObjectCollection.h @@ -176,38 +176,36 @@ public: : public weak_or_shared_ptr { public: - holder_type (weak_or_shared_collection *collection) - : weak_or_shared_ptr (), next (0), prev (0), mp_collection (collection) + holder_type (weak_or_shared_collection *_collection) + : weak_or_shared_ptr (), next (0), prev (0), collection (_collection) { // .. nothing yet .. } - holder_type (weak_or_shared_collection *collection, T *t) - : weak_or_shared_ptr (t), next (0), prev (0), mp_collection (collection) + holder_type (weak_or_shared_collection *_collection, T *t) + : weak_or_shared_ptr (t), next (0), prev (0), collection (_collection) { // .. nothing yet .. } - holder_type (weak_or_shared_collection *collection, const weak_or_shared_ptr &d) - : weak_or_shared_ptr (d), next (0), prev (0), mp_collection (collection) + holder_type (weak_or_shared_collection *_collection, const weak_or_shared_ptr &d) + : weak_or_shared_ptr (d), next (0), prev (0), collection (_collection) { // .. nothing yet .. } holder_type *next, *prev; + weak_or_shared_collection *collection; protected: virtual void reset_object () { weak_or_shared_ptr::reset_object (); - if (mp_collection) { + if (collection) { // Caution: this will probably delete "this"! - mp_collection->remove_element (this); + collection->remove_element (this); } } - - private: - weak_or_shared_collection *mp_collection; }; typedef weak_or_shared_collection_iterator iterator; @@ -224,6 +222,24 @@ public: { } + /** + * @brief The copy constructor + */ + weak_or_shared_collection (const weak_or_shared_collection &other) + : mp_first (0), mp_last (0), m_size (0) + { + operator= (other); + } + + /** + * @brief The move constructor + */ + weak_or_shared_collection (weak_or_shared_collection &&other) + : mp_first (0), mp_last (0), m_size (0) + { + swap (other); + } + /** * @brief Destructor */ @@ -234,6 +250,37 @@ public: } } + /** + * @brief Assignment + */ + weak_or_shared_collection &operator= (const weak_or_shared_collection &other) + { + if (this != &other) { + clear (); + for (auto i = other.begin (); i != other.end (); ++i) { + push_back (const_cast (i.operator-> ())); + } + } + return *this; + } + + /** + * @brief Swap + */ + void swap (weak_or_shared_collection &other) + { + std::swap (mp_first, other.mp_first); + std::swap (mp_last, other.mp_last); + std::swap (m_size, other.m_size); + + for (holder_type *h = mp_first; h; h = h->next) { + h->collection = this; + } + for (holder_type *h = other.mp_first; h; h = h->next) { + h->collection = &other; + } + } + /** * @brief Returns a value indicating whether the collection is empty */ diff --git a/src/tl/unit_tests/tlObjectTests.cc b/src/tl/unit_tests/tlObjectTests.cc index d4b5653d3..b5cfaabd5 100644 --- a/src/tl/unit_tests/tlObjectTests.cc +++ b/src/tl/unit_tests/tlObjectTests.cc @@ -516,6 +516,48 @@ TEST(24) EXPECT_EQ (MyClass::instances (), 0); } +TEST(25) +{ + MyClass::reset_instance_counter (); + MyClass *o1 = new MyClass (1); + MyClass *o2 = new MyClass (2); + EXPECT_EQ (MyClass::instances (), 2); + + tl::shared_collection sc1, sc2; + sc1.push_back (o1); + sc1.push_back (o2); + EXPECT_EQ (sc1.size (), size_t (2)); + EXPECT_EQ (sc2.size (), size_t (0)); + EXPECT_EQ (sc1.front () == o1, true); + + sc1.swap (sc2); + EXPECT_EQ (sc1.size (), size_t (0)); + EXPECT_EQ (sc2.size (), size_t (2)); + EXPECT_EQ (sc2.front () == o1, true); + EXPECT_EQ (MyClass::instances (), 2); + + sc1 = sc2; + EXPECT_EQ (sc1.size (), size_t (2)); + EXPECT_EQ (sc2.size (), size_t (2)); + EXPECT_EQ (sc1.front () == o1, true); + EXPECT_EQ (sc2.front () == o1, true); + EXPECT_EQ (MyClass::instances (), 2); + + delete o1; + EXPECT_EQ (sc1.size (), size_t (1)); + EXPECT_EQ (sc2.size (), size_t (1)); + EXPECT_EQ (sc1.front () == o2, true); + EXPECT_EQ (sc2.front () == o2, true); + EXPECT_EQ (MyClass::instances (), 1); + + sc1.clear (); + sc2.clear (); + EXPECT_EQ (sc1.size (), size_t (0)); + EXPECT_EQ (sc2.size (), size_t (0)); + + EXPECT_EQ (MyClass::instances (), 0); +} + TEST(30) { MyClass::reset_instance_counter (); From 7e8ce51da2cbb67f0ba56bd4de21ce20c2302d75 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 16:48:01 +0100 Subject: [PATCH 07/17] post-increment operators must not use the int argument (it is zero) --- src/tl/tl/tlObjectCollection.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tl/tl/tlObjectCollection.h b/src/tl/tl/tlObjectCollection.h index c2a4ff820..362868125 100644 --- a/src/tl/tl/tlObjectCollection.h +++ b/src/tl/tl/tlObjectCollection.h @@ -116,12 +116,10 @@ public: /** * @brief Post-decrement */ - weak_or_shared_collection_iterator operator-- (int n) + weak_or_shared_collection_iterator operator-- (int) { weak_or_shared_collection_iterator ret = *this; - while (n-- > 0) { - operator-- (); - } + operator-- (); return ret; } @@ -138,12 +136,10 @@ public: /** * @brief Post-increment */ - weak_or_shared_collection_iterator operator++ (int n) + weak_or_shared_collection_iterator operator++ (int) { weak_or_shared_collection_iterator ret = *this; - while (n-- > 0) { - operator++ (); - } + operator++ (); return ret; } From b4d1a4a95482dd702ac263da740a1981a08c7d2e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 19:11:15 +0100 Subject: [PATCH 08/17] First draft --- src/edt/edt/edtEditorHooks.cc | 24 ++- src/edt/edt/edtEditorHooks.h | 62 ++++-- src/edt/edt/edtServiceImpl.cc | 228 ++++++++++++++++++-- src/edt/edt/edtServiceImpl.h | 23 +- src/edt/edt/gsiDeclEdtEditorHooks.cc | 303 ++++++++++++++------------- 5 files changed, 459 insertions(+), 181 deletions(-) diff --git a/src/edt/edt/edtEditorHooks.cc b/src/edt/edt/edtEditorHooks.cc index eaca9b718..49b866769 100644 --- a/src/edt/edt/edtEditorHooks.cc +++ b/src/edt/edt/edtEditorHooks.cc @@ -59,9 +59,21 @@ public: return sp_instance; } - void register_editor_hook (EditorHooks *hook) + void register_editor_hooks (EditorHooks *hooks, const std::string &name) { - m_hooks.push_back (hook); + // needed, so we do not loose the object in case we erase it: + tl::shared_ptr tmp (hooks); + + // remove other hooks with the same name or with an identical address + for (auto h = m_hooks.begin (); h != m_hooks.end (); ) { + auto hh = h++; + if (hh.operator-> () && (hh.operator-> () == hooks || hh->name () == name)) { + m_hooks.erase (hh); + } + } + + hooks->set_name (name); + m_hooks.push_back (hooks); } tl::weak_collection @@ -69,7 +81,7 @@ public: { tl::weak_collection res; for (auto h = m_hooks.begin (); h != m_hooks.end (); ++h) { - if (! h->for_technologies () || h->is_for_technology (for_technology)) { + if (h.operator-> () && (! h->for_technologies () || h->is_for_technology (for_technology))) { res.push_back (h.operator-> ()); } } @@ -128,11 +140,11 @@ EditorHooks::add_technology (const std::string &tech) } void -EditorHooks::register_editor_hook (EditorHooks *hook) +EditorHooks::register_editor_hooks (EditorHooks *hooks, const std::string &name) { if (EditorHooksManager::instance ()) { - hook->keep (); - EditorHooksManager::instance ()->register_editor_hook (hook); + hooks->keep (); + EditorHooksManager::instance ()->register_editor_hooks (hooks, name); } } diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index 076fa193b..893562147 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -29,6 +29,7 @@ #include "dbTrans.h" #include "gsiObject.h" #include "tlExceptions.h" +#include "tlLog.h" #include "tlObjectCollection.h" #include @@ -36,7 +37,7 @@ namespace lay { - class LayoutView; + class LayoutViewBase; class ObjectInstPath; } @@ -72,6 +73,10 @@ class EDT_PUBLIC EditorHooks public: /** * @brief Constructor + * + * The name is arbitrary, but should be unique, as hooks with the + * same name replace each other. This is a debugging aid for GSI as we can + * re-register hooks while we keep them in the system. */ EditorHooks (); @@ -81,29 +86,45 @@ public: virtual ~EditorHooks (); // creation protocol - virtual void begin_create (lay::LayoutView * /*view*/) { } + virtual void begin_create (lay::LayoutViewBase * /*view*/) { } virtual void begin_new_objects () { } - virtual void create (const lay::ObjectInstPath & /*object*/, double /*dbu*/) { } + virtual void create (const lay::ObjectInstPath & /*object*/, const db::CplxTrans & /*view_trans*/) { } virtual void end_new_objects () { } virtual void commit_create () { } virtual void end_create () { } // modification protocol - virtual void begin_modify (lay::LayoutView * /*view*/) { } + virtual void begin_modify (lay::LayoutViewBase * /*view*/) { } virtual void begin_modifications () { } - virtual void modified (const lay::ObjectInstPath & /*object*/, double /*dbu*/) { } + virtual void modified (const lay::ObjectInstPath & /*object*/, const db::CplxTrans & /*view_trans*/) { } virtual void end_modifications () { } virtual void commit_modify () { } virtual void end_modify () { } // editing protocol - virtual void begin_edit (lay::LayoutView * /*view*/) { } + virtual void begin_edit (lay::LayoutViewBase * /*view*/) { } virtual void begin_edits () { } - virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::DCplxTrans & /*trans*/, double /*dbu*/) { } + virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::ICplxTrans & /*applied*/, const db::CplxTrans & /*view_trans*/) { } virtual void end_edits () { } virtual void commit_edit () { } virtual void end_edit () { } + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &name) + { + m_name = name; + } + /** * @brief Gets the technology name this hook is associated with * @@ -146,7 +167,7 @@ public: /** * @brief Registers the editor hook */ - static void register_editor_hook (EditorHooks *hook); + static void register_editor_hooks (EditorHooks *hooks, const std::string &name); /** * @brief Gets the editor hooks for a given technology @@ -157,6 +178,7 @@ public: private: std::set m_technologies; + std::string m_name; // no copying. EditorHooks &operator= (const EditorHooks &); @@ -171,15 +193,17 @@ inline void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) ()) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { -BEGIN_PROTECTED try { if (h.operator-> ()) { (const_cast (h.operator-> ())->*meth) (); } } catch (tl::CancelException &) { return; + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (std::exception &ex) { + tl::error << ex.what (); } -END_PROTECTED } } @@ -194,15 +218,17 @@ inline void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1), A1 a1) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { -BEGIN_PROTECTED try { if (h.operator-> ()) { (const_cast (h.operator-> ())->*meth) (a1); } } catch (tl::CancelException &) { return; + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (std::exception &ex) { + tl::error << ex.what (); } -END_PROTECTED } } @@ -217,15 +243,17 @@ inline void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { -BEGIN_PROTECTED try { if (h.operator-> ()) { (const_cast (h.operator-> ())->*meth) (a1, a2); } } catch (tl::CancelException &) { return; + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (std::exception &ex) { + tl::error << ex.what (); } -END_PROTECTED } } @@ -240,15 +268,17 @@ inline void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2, A3 a3) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { -BEGIN_PROTECTED try { if (h.operator-> ()) { (const_cast (h.operator-> ())->*meth) (a1, a2, a3); } } catch (tl::CancelException &) { return; + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (std::exception &ex) { + tl::error << ex.what (); } -END_PROTECTED } } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 2fec150e4..03e11c1ec 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -131,7 +131,7 @@ ShapeEditService::get_edit_layer () m_cv_index = (unsigned int) cv_index; m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted (); mp_layout = &(cv->layout ()); - mp_cell = &(mp_layout->cell (cv.cell_index ())); + mp_cell = cv.cell (); if (mp_cell->is_proxy ()) { throw tl::Exception (tl::to_string (tr ("Cannot put a shape into a PCell or library cell"))); @@ -157,7 +157,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl return; } - if (cv->layout ().cell (cv.cell_index ()).is_proxy ()) { + if (cv.cell ()->is_proxy ()) { return; } @@ -198,7 +198,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl m_cv_index = (unsigned int) cv_index; m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted (); mp_layout = &(cv->layout ()); - mp_cell = &(mp_layout->cell (cv.cell_index ())); + mp_cell = cv.cell (); current_layer_changed (); } @@ -400,6 +400,62 @@ ShapeEditService::deliver_shape (const db::Point &point) } } +void +ShapeEditService::open_editor_hooks () +{ + std::string technology; + if (mp_layout && mp_layout->technology ()) { + technology = mp_layout->technology ()->name (); + } + + // NOTE: this is a kind of hack - as we want to present the new shape in some + // natural habitat (aka Shapes), we create an artificial Shapes container for holding the + // temporary object. + m_tmp_shapes.reset (new db::Shapes (0, mp_cell, true)); + + m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create, view ()); +} + +void +ShapeEditService::close_editor_hooks (bool with_commit) +{ + if (with_commit) { + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_create); + } + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create); + + m_editor_hooks.clear (); + m_tmp_shapes.reset (0); +} + +template +void +ShapeEditService::deliver_shape_to_hooks (const Shape &shape) +{ + if (! mp_cell || ! m_tmp_shapes.get ()) { + return; + } + + m_tmp_shapes->clear (); + db::Shape s = m_tmp_shapes->insert (shape); + + lay::ObjectInstPath path; + path.set_cv_index (m_cv_index); + path.set_layer (m_layer); + path.set_topcell (mp_cell->cell_index ()); + path.set_shape (s); + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create, path, trans ().inverted ()); +} + +// explicit instantiations +template void ShapeEditService::deliver_shape_to_hooks (const db::Polygon &); +template void ShapeEditService::deliver_shape_to_hooks (const db::Path &); +template void ShapeEditService::deliver_shape_to_hooks (const db::Box &); +template void ShapeEditService::deliver_shape_to_hooks (const db::Point &); +template void ShapeEditService::deliver_shape_to_hooks (const db::Text &); + // ----------------------------------------------------------------------------- // PolygonService implementation @@ -433,6 +489,8 @@ PolygonService::do_begin_edit (const db::DPoint &p) m_points.push_back (pp); m_closure_set = false; + open_editor_hooks (); + update_marker (); } @@ -503,26 +561,30 @@ PolygonService::do_mouse_click (const db::DPoint &p) void PolygonService::do_finish_edit () { - deliver_shape (get_polygon ()); + deliver_shape (get_polygon (false)); commit_recent (view ()); + close_editor_hooks (true); } db::Polygon -PolygonService::get_polygon () const +PolygonService::get_polygon (bool all_points) const { db::Polygon poly; - if (m_points.size () < 4) { + if (m_points.size () + (m_closure_set ? 1 : 0) < (all_points ? 3 : 4)) { throw tl::Exception (tl::to_string (tr ("A polygon must have at least 3 points"))); } std::vector points_dbu; - points_dbu.reserve (m_points.size ()); + points_dbu.reserve (m_points.size () + 1); // one point is reserved for the current one for (std::vector::const_iterator p = m_points.begin (); p + 1 != m_points.end (); ++p) { points_dbu.push_back (trans () * *p); } + if (all_points) { + points_dbu.push_back (trans () * m_points.back ()); + } if (m_closure_set) { points_dbu.push_back (trans () * m_closure); } @@ -539,7 +601,7 @@ PolygonService::get_polygon () const void PolygonService::do_cancel_edit () { - // .. nothing yet .. + close_editor_hooks (false); } bool @@ -732,6 +794,17 @@ PolygonService::update_marker () std::string (" l: ") + tl::micron_to_string (m_points.back ().distance (m_points.end () [-2]))); } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + try { + deliver_shape_to_hooks (get_polygon (true)); + } catch (...) { + // ignore exceptions + } + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + } } // ----------------------------------------------------------------------------- @@ -761,6 +834,8 @@ BoxService::do_begin_edit (const db::DPoint &p) db::DPoint pp = snap2 (p); m_p1 = m_p2 = pp; + open_editor_hooks (); + set_edit_marker (new lay::Marker (view (), cv_index ())); update_marker (); } @@ -785,6 +860,17 @@ BoxService::update_marker () tl::micron_to_string (m_p2.y () - m_p1.y ())); } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + try { + deliver_shape_to_hooks (get_box ()); + } catch (...) { + // ignore exceptions + } + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + } } void @@ -816,12 +902,13 @@ BoxService::do_finish_edit () { deliver_shape (get_box ()); commit_recent (view ()); + close_editor_hooks (true); } void BoxService::do_cancel_edit () { - // .. nothing yet .. + close_editor_hooks (false); } bool @@ -857,6 +944,8 @@ PointService::do_begin_edit (const db::DPoint &p) db::DPoint pp = snap2 (p); m_p = pp; + open_editor_hooks (); + set_edit_marker (new lay::Marker (view (), cv_index ())); update_marker (); } @@ -882,6 +971,17 @@ PointService::update_marker () tl::micron_to_string (m_p.y ())); } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + try { + deliver_shape_to_hooks (get_point ()); + } catch (...) { + // ignore exceptions + } + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + } } void @@ -913,12 +1013,13 @@ PointService::do_finish_edit () { deliver_shape (get_point ()); commit_recent (view ()); + close_editor_hooks (true); } void PointService::do_cancel_edit () { - // .. nothing yet .. + close_editor_hooks (false); } bool @@ -959,6 +1060,8 @@ TextService::do_begin_edit (const db::DPoint &p) m_text.trans (db::DTrans (m_rot, snap2 (p) - db::DPoint ())); + open_editor_hooks (); + lay::DMarker *marker = new lay::DMarker (view ()); marker->set_vertex_shape (lay::ViewOp::Cross); marker->set_vertex_size (9 /*cross vertex size*/); @@ -985,6 +1088,17 @@ TextService::update_marker () view ()->message (pos); } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + try { + deliver_shape_to_hooks (get_text ()); + } catch (...) { + // ignore exceptions + } + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + } } bool @@ -1065,12 +1179,14 @@ TextService::do_finish_edit () } #endif + + close_editor_hooks (true); } void TextService::do_cancel_edit () { - // .. nothing yet .. + close_editor_hooks (false); } bool @@ -1166,6 +1282,8 @@ PathService::do_begin_edit (const db::DPoint &p) m_points.push_back (pp); m_points.push_back (pp); + open_editor_hooks (); + set_edit_marker (new lay::Marker (view (), cv_index ())); update_marker (); } @@ -1250,6 +1368,8 @@ PathService::do_finish_edit () deliver_shape (get_path ()); commit_recent (view ()); + + close_editor_hooks (true); } void @@ -1271,6 +1391,17 @@ PathService::update_marker () } } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + try { + deliver_shape_to_hooks (get_path ()); + } catch (...) { + // ignore exceptions + } + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + } } db::Path @@ -1305,7 +1436,7 @@ PathService::get_path () const void PathService::do_cancel_edit () { - // .. nothing yet .. + close_editor_hooks (false); } bool @@ -1582,6 +1713,8 @@ InstService::do_begin_edit (const db::DPoint &p) m_trans = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * tv [0] * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans (); } + open_editor_hooks (); + update_marker (); } @@ -1756,7 +1889,7 @@ InstService::do_finish_edit () cv->layout ().cell (inst.object ().cell_index ()).collect_called_cells (called); called.insert (inst.object ().cell_index ()); - cv->layout ().cell (cv.cell_index ()).collect_caller_cells (callers); + cv.cell ()->collect_caller_cells (callers); callers.insert (cv.cell_index ()); std::vector intersection; @@ -1769,7 +1902,7 @@ InstService::do_finish_edit () manager ()->transaction (tl::to_string (tr ("Create instance")), m_reference_transaction_id); } m_reference_transaction_id = 0; - db::Instance i = cv->layout ().cell (cv.cell_index ()).insert (inst); + db::Instance i = cv.cell ()->insert (inst); cv->layout ().cleanup (); if (manager ()) { manager ()->commit (); @@ -1792,10 +1925,12 @@ InstService::do_finish_edit () m_has_valid_cell = false; m_in_drag_drop = false; + close_editor_hooks (true); } catch (...) { m_has_valid_cell = false; m_in_drag_drop = false; + close_editor_hooks (false); throw; } } @@ -1819,6 +1954,8 @@ InstService::do_cancel_edit () if (cv.is_valid ()) { cv->layout ().cleanup (); } + + close_editor_hooks (false); } void @@ -2128,6 +2265,41 @@ InstService::update_marker () } else { set_edit_marker (0); } + + // call hooks with new shape + if (! editor_hooks ().empty ()) { + + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + + try { + + const lay::CellView &cv = view ()->cellview (m_cv_index); + + db::CellInstArray inst; + if (cv.is_valid () && get_inst (inst)) { + + // Note: we create a temporary instance collection and a temporary + // ObjectInstPath object there, so + db::Instances instances (cv.cell ()); + + lay::ObjectInstPath path; + path.set_cv_index (m_cv_index); + path.set_topcell (cv.cell_index ()); + path.add_path (db::InstElement (instances.insert (inst))); + + db::CplxTrans view_trans = db::CplxTrans (cv->layout ().dbu ()) * m_trans; + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create, path, view_trans); + + } + + } catch (...) { + // ignore exceptions + } + + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + + } } bool @@ -2159,6 +2331,34 @@ InstService::get_inst (db::CellInstArray &inst) return false; } +void +InstService::open_editor_hooks () +{ + const lay::CellView &cv = view ()->cellview (m_cv_index); + if (! cv.is_valid ()) { + return; + } + + std::string technology; + if (cv->layout ().technology ()) { + technology = cv->layout ().technology ()->name (); + } + + m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create, view ()); +} + +void +InstService::close_editor_hooks (bool with_commit) +{ + if (with_commit) { + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_create); + } + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create); + + m_editor_hooks.clear (); +} + } // namespace edt diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index d799aa8ee..747597ec4 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -26,6 +26,7 @@ #include "edtService.h" #include "edtConfig.h" +#include "edtEditorHooks.h" #include @@ -68,6 +69,16 @@ protected: void deliver_shape (const db::Path &path); void deliver_shape (const db::Box &box); void deliver_shape (const db::Point &point); + void open_editor_hooks (); + template + void deliver_shape_to_hooks (const Shape &shape); + void close_editor_hooks (bool with_commit); + + const tl::weak_collection &editor_hooks () + { + return m_editor_hooks; + } + virtual void current_layer_changed () { } private: @@ -77,6 +88,8 @@ private: db::Cell *mp_cell; db::Layout *mp_layout; combine_mode_type m_combine_mode; + tl::weak_collection m_editor_hooks; + std::unique_ptr m_tmp_shapes; void update_edit_layer (const lay::LayerPropertiesConstIterator &iter); }; @@ -109,7 +122,7 @@ private: db::DPoint m_last; void update_marker (); - db::Polygon get_polygon () const; + db::Polygon get_polygon (bool all_points) const; void add_closure (); void set_last_point (const db::DPoint &p); }; @@ -298,6 +311,7 @@ private: const db::PCellDeclaration *mp_pcell_decl; int m_cv_index; db::ICplxTrans m_trans; + tl::weak_collection m_editor_hooks; void update_marker (); bool get_inst (db::CellInstArray &inst); @@ -305,6 +319,13 @@ private: tl::Variant get_default_layer_for_pcell (); void sync_to_config (); void switch_cell_or_pcell (bool switch_parameters); + void open_editor_hooks (); + void close_editor_hooks (bool with_commit); + + const tl::weak_collection &editor_hooks () + { + return m_editor_hooks; + } }; } diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 5350014c2..19508939e 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -28,143 +28,21 @@ namespace gsi { -gsi::Class decl_EditorHooksBase ("lay", "EditorHooksBase", - gsi::method ("begin_create", &edt::EditorHooks::begin_create, gsi::arg ("view"), - "@brief Creation protocol - begin session\n" - "This method is called to initiate an object creation session. The session is ended with " - "\\end_create. Between these calls, new objects are announced with \\begin_new_objects, " - "\\create and \\end_new_objects calls. These calls are repeated to indicate changes in the objects " - "created.\n" - "\n" - "\\commit_create is called once before \\end_create to indicate that the last set of " - "objects are committed to the database." - ) + - gsi::method ("begin_new_objects", &edt::EditorHooks::begin_new_objects, - "@brief Creation protocol - begin new objects\n" - "See \\begin_create for a description of the protocol." - ) + - gsi::method ("create", &edt::EditorHooks::create, gsi::arg ("object"), gsi::arg ("dbu"), - "@brief Creation protocol - indicate a new object\n" - "See \\begin_create for a description of the protocol." - ) + - gsi::method ("end_new_objects", &edt::EditorHooks::end_new_objects, - "@brief Creation protocol - finish list of new objects\n" - "See \\begin_create for a description of the protocol." - ) + - gsi::method ("commit_create", &edt::EditorHooks::commit_create, - "@brief Creation protocol - commit new objects\n" - "See \\begin_create for a description of the protocol." - ) + - gsi::method ("end_create", &edt::EditorHooks::end_create, - "@brief Creation protocol - finish session\n" - "See \\begin_create for a description of the protocol." - ) + - gsi::method ("begin_modify", &edt::EditorHooks::begin_modify, gsi::arg ("view"), - "@brief Modification protocol - begin session\n" - "This method is called to initiate an object modification session. The session is ended with " - "\\end_modify. Between these calls, modified objects are announced with \\begin_modifications, " - "\\modified and \\end_modifications calls. These calls are repeated to indicate changes in the objects " - "modified while moving the mouse for example.\n" - "\n" - "\\commit_modify is called once before \\end_modify to indicate that the last set of " - "objects are committed to the database." - ) + - gsi::method ("begin_modifications", &edt::EditorHooks::begin_modifications, - "@brief Modification protocol - begin modifications\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::method ("modified", &edt::EditorHooks::modified, gsi::arg ("object"), gsi::arg ("dbu"), - "@brief Modification protocol - indicate a modified object\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::method ("end_modifications", &edt::EditorHooks::end_modifications, - "@brief Modification protocol - finish list of modifications\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::method ("commit_modify", &edt::EditorHooks::commit_modify, - "@brief Modification protocol - commit new objects\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::method ("end_modify", &edt::EditorHooks::end_modify, - "@brief Modification protocol - finish session\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::method ("begin_edit", &edt::EditorHooks::begin_edit, gsi::arg ("view"), - "@brief Editing protocol - begin session\n" - "This method is called to initiate an object editing session. The session is ended with " - "\\end_edit. Between these calls, edits are announced with \\begin_edits, " - "\\transformed and \\end_edits calls. These calls are repeated to indicate changes in the objects " - "modified while moving the mouse for example.\n" - "\n" - "\\commit_edit is called once before \\end_edit to indicate that the last set of " - "objects are committed to the database." - ) + - gsi::method ("begin_edits", &edt::EditorHooks::begin_edits, - "@brief Editing protocol - begin edits\n" - "See \\begin_edit for a description of the protocol." - ) + - gsi::method ("transformed", &edt::EditorHooks::transformed, gsi::arg ("object"), gsi::arg ("trans"), gsi::arg ("dbu"), - "@brief Editing protocol - indicate an object transformation\n" - "See \\begin_edit for a description of the protocol." - ) + - gsi::method ("end_edits", &edt::EditorHooks::end_edits, - "@brief Editing protocol - finish list of edits\n" - "See \\begin_edit for a description of the protocol." - ) + - gsi::method ("commit_edit", &edt::EditorHooks::commit_edit, - "@brief Editing protocol - commit new objects\n" - "See \\begin_edit for a description of the protocol." - ) + - gsi::method ("end_edit", &edt::EditorHooks::end_edit, - "@brief Editing protocol - finish session\n" - "See \\begin_edit for a description of the protocol." - ) + - gsi::method ("technology=", &edt::EditorHooks::set_technology, gsi::arg ("technology"), - "@brief sets the name of the technology the hooks are associated with\n" - "This will clear all technology associations and associate the hooks with that technology only.\n" - ) + - gsi::method ("clear_technologies", &edt::EditorHooks::clear_technologies, - "@brief Clears the list of technologies the hooks are associated with.\n" - "See also \\add_technology.\n" - ) + - gsi::method ("add_technology", &edt::EditorHooks::add_technology, gsi::arg ("tech"), - "@brief Additionally associates the hooks with the given technology.\n" - "See also \\clear_technologies.\n" - ) + - gsi::method ("is_for_technology", &edt::EditorHooks::is_for_technology, gsi::arg ("tech"), - "@brief Returns a value indicating whether the hooks are associated with the given technology.\n" - ) + - gsi::method ("for_technologies", &edt::EditorHooks::for_technologies, - "@brief Returns a value indicating whether the hooks are associated with any technology.\n" - "The method is equivalent to checking whether the \\technologies list is empty.\n" - ) + - gsi::method ("technologies", &edt::EditorHooks::get_technologies, - "@brief Gets the list of technologies these hooks are associated with.\n" - ) + - gsi::method ("register", &edt::EditorHooks::register_editor_hook, gsi::arg ("hooks"), - "@brief Registers the hooks in the system.\n" - "The hooks will not be active before they are registered in the system. Registration will " - "also transfer object ownership to the system." - ), - "@brief The base class for editor hooks\n" - "Editor hooks allow implementing technology-specific callbacks into the editor " - "for example to implement visual feedback about DRC rules.\n" - "\n" - "This class provides the basic interface. To implement callbacks, use the \\EditorHooks class." - "\n" - "The EditorHooksBase class has been introduced in version 0.29." -); class EditorHooksImpl : public edt::EditorHooks { public: - EditorHooksImpl () { } + EditorHooksImpl () + : edt::EditorHooks () + { + // .. nothing yet .. + } - virtual void begin_create (lay::LayoutView *view) + virtual void begin_create (lay::LayoutViewBase *view) { if (f_begin_create.can_issue ()) { - f_begin_create.issue (&edt::EditorHooks::begin_create, view); + f_begin_create.issue (&edt::EditorHooks::begin_create, view); } else { edt::EditorHooks::begin_create (view); } @@ -179,12 +57,12 @@ public: } } - virtual void create (const lay::ObjectInstPath &object, double dbu) + virtual void create (const lay::ObjectInstPath &object, const db::CplxTrans &view_trans) { if (f_create.can_issue ()) { - f_create.issue (&edt::EditorHooks::create, object, dbu); + f_create.issue (&edt::EditorHooks::create, object, view_trans); } else { - edt::EditorHooks::create (object, dbu); + edt::EditorHooks::create (object, view_trans); } } @@ -215,10 +93,10 @@ public: } } - virtual void begin_modify (lay::LayoutView *view) + virtual void begin_modify (lay::LayoutViewBase *view) { if (f_begin_modify.can_issue ()) { - f_begin_modify.issue (&edt::EditorHooks::begin_modify, view); + f_begin_modify.issue (&edt::EditorHooks::begin_modify, view); } else { edt::EditorHooks::begin_modify (view); } @@ -233,12 +111,12 @@ public: } } - virtual void modified (const lay::ObjectInstPath &object, double dbu) + virtual void modified (const lay::ObjectInstPath &object, const db::CplxTrans &view_trans) { if (f_modified.can_issue ()) { - f_modified.issue (&edt::EditorHooks::modified, object, dbu); + f_modified.issue (&edt::EditorHooks::modified, object, view_trans); } else { - edt::EditorHooks::modified (object, dbu); + edt::EditorHooks::modified (object, view_trans); } } @@ -269,10 +147,10 @@ public: } } - virtual void begin_edit (lay::LayoutView *view) + virtual void begin_edit (lay::LayoutViewBase *view) { if (f_begin_edit.can_issue ()) { - f_begin_edit.issue (&edt::EditorHooks::begin_edit, view); + f_begin_edit.issue (&edt::EditorHooks::begin_edit, view); } else { edt::EditorHooks::begin_edit (view); } @@ -287,12 +165,12 @@ public: } } - virtual void transformed (const lay::ObjectInstPath &object, const db::DCplxTrans &trans, double dbu) + virtual void transformed (const lay::ObjectInstPath &object, const db::ICplxTrans &applied_trans, const db::CplxTrans &view_trans) { if (f_transformed.can_issue ()) { - f_transformed.issue (&edt::EditorHooks::transformed, object, trans, dbu); + f_transformed.issue (&edt::EditorHooks::transformed, object, applied_trans, view_trans); } else { - edt::EditorHooks::transformed (object, trans, dbu); + edt::EditorHooks::transformed (object, applied_trans, view_trans); } } @@ -345,12 +223,149 @@ public: gsi::Callback f_end_edit; }; +static void register_editor_hooks (EditorHooksImpl *hooks, const std::string &name) +{ + edt::EditorHooks::register_editor_hooks (hooks, name); +} + gsi::Class decl_EditorHooks ("lay", "EditorHooks", - callback ("begin_create", &EditorHooksImpl::begin_create, &EditorHooksImpl::f_begin_create, + gsi::callback ("begin_create", &EditorHooksImpl::begin_create, &EditorHooksImpl::f_begin_create, "@brief Creation protocol - begin session\n" - "See \\EditorHooksBase for a description of the protocol" + "This method is called to initiate an object creation session. The session is ended with " + "\\end_create. Between these calls, new objects are announced with \\begin_new_objects, " + "\\create and \\end_new_objects calls. These calls are repeated to indicate changes in the objects " + "created.\n" + "\n" + "\\commit_create is called once before \\end_create to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::callback ("begin_new_objects", &EditorHooksImpl::begin_new_objects, &EditorHooksImpl::f_begin_new_objects, + "@brief Creation protocol - begin new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::callback ("create", &EditorHooksImpl::create, &EditorHooksImpl::f_create, gsi::arg ("object"), gsi::arg ("view_trans"), + "@brief Creation protocol - indicate a new object\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::callback ("end_new_objects", &EditorHooksImpl::end_new_objects, &EditorHooksImpl::f_end_new_objects, + "@brief Creation protocol - finish list of new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::callback ("commit_create", &EditorHooksImpl::commit_create, &EditorHooksImpl::f_commit_create, + "@brief Creation protocol - commit new objects\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::callback ("end_create", &EditorHooksImpl::end_create, &EditorHooksImpl::f_end_create, + "@brief Creation protocol - finish session\n" + "See \\begin_create for a description of the protocol." + ) + + gsi::callback ("begin_modify", &EditorHooksImpl::begin_modify, &EditorHooksImpl::f_begin_modify, gsi::arg ("view"), + "@brief Modification protocol - begin session\n" + "This method is called to initiate an object modification session. The session is ended with " + "\\end_modify. Between these calls, modified objects are announced with \\begin_modifications, " + "\\modified and \\end_modifications calls. These calls are repeated to indicate changes in the objects " + "modified while moving the mouse for example.\n" + "\n" + "\\commit_modify is called once before \\end_modify to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::callback ("begin_modifications", &EditorHooksImpl::begin_modifications, &EditorHooksImpl::f_begin_modifications, + "@brief Modification protocol - begin modifications\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::callback ("modified", &EditorHooksImpl::modified, &EditorHooksImpl::f_modified, gsi::arg ("object"), gsi::arg ("view_trans"), + "@brief Modification protocol - indicate a modified object\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::callback ("end_modifications", &EditorHooksImpl::end_modifications, &EditorHooksImpl::f_end_modifications, + "@brief Modification protocol - finish list of modifications\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::callback ("commit_modify", &EditorHooksImpl::commit_modify, &EditorHooksImpl::f_commit_modify, + "@brief Modification protocol - commit new objects\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::callback ("end_modify", &EditorHooksImpl::end_modify, &EditorHooksImpl::f_end_modify, + "@brief Modification protocol - finish session\n" + "See \\begin_modify for a description of the protocol." + ) + + gsi::callback ("begin_edit", &EditorHooksImpl::begin_edit, &EditorHooksImpl::f_begin_edit, gsi::arg ("view"), + "@brief Editing protocol - begin session\n" + "This method is called to initiate an object editing session. The session is ended with " + "\\end_edit. Between these calls, edits are announced with \\begin_edits, " + "\\transformed and \\end_edits calls. These calls are repeated to indicate changes in the objects " + "modified while moving the mouse for example.\n" + "\n" + "\\commit_edit is called once before \\end_edit to indicate that the last set of " + "objects are committed to the database." + ) + + gsi::callback ("begin_edits", &EditorHooksImpl::begin_edits, &EditorHooksImpl::f_begin_edits, + "@brief Editing protocol - begin edits\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::callback ("transformed", &EditorHooksImpl::transformed, &EditorHooksImpl::f_transformed, gsi::arg ("object"), gsi::arg ("applied_trans"), gsi::arg ("view_trans"), + "@brief Editing protocol - indicate an object transformation\n" + "See \\begin_edit for a description of the protocol.\n" + "\n" + "@param object A path to the modified object\n" + "@param applied_trans The DBU-space of the transformation applied to the object\n" + "@param view_trans The combined transformation of DBU space to view space\n" + ) + + gsi::callback ("end_edits", &EditorHooksImpl::end_edits, &EditorHooksImpl::f_end_edits, + "@brief Editing protocol - finish list of edits\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::callback ("commit_edit", &EditorHooksImpl::commit_edit, &EditorHooksImpl::f_commit_edit, + "@brief Editing protocol - commit new objects\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::callback ("end_edit", &EditorHooksImpl::end_edit, &EditorHooksImpl::f_end_edit, + "@brief Editing protocol - finish session\n" + "See \\begin_edit for a description of the protocol." + ) + + gsi::method ("technology=", &EditorHooksImpl::set_technology, gsi::arg ("technology"), + "@brief sets the name of the technology the hooks are associated with\n" + "This will clear all technology associations and associate the hooks with that technology only.\n" + ) + + gsi::method ("clear_technologies", &EditorHooksImpl::clear_technologies, + "@brief Clears the list of technologies the hooks are associated with.\n" + "See also \\add_technology.\n" + ) + + gsi::method ("add_technology", &EditorHooksImpl::add_technology, gsi::arg ("tech"), + "@brief Additionally associates the hooks with the given technology.\n" + "See also \\clear_technologies.\n" + ) + + gsi::method ("is_for_technology", &EditorHooksImpl::is_for_technology, gsi::arg ("tech"), + "@brief Returns a value indicating whether the hooks are associated with the given technology.\n" + ) + + gsi::method ("for_technologies", &EditorHooksImpl::for_technologies, + "@brief Returns a value indicating whether the hooks are associated with any technology.\n" + "The method is equivalent to checking whether the \\technologies list is empty.\n" + ) + + gsi::method ("technologies", &EditorHooksImpl::get_technologies, + "@brief Gets the list of technologies these hooks are associated with.\n" + ) + + gsi::method ("name", &EditorHooksImpl::name, + "@brief Gets the name of the hooks object.\n" + "This is the name, the object was registered under in the system." + ) + + gsi::method_ext ("register", ®ister_editor_hooks, gsi::arg ("name"), + "@brief Registers the hooks in the system.\n" + "The hooks will not be active before they are registered in the system. Registration will " + "also transfer object ownership to the system.\n" + "\n" + "The name is arbitary, but should be unique. Upon registration, this hooks object will " + "replace others with the same name already registered in the system. This will simplify " + "debugging as you can re-run the same code, without accumulating hooks.\n" ), "@brief An implementation base class for editor hooks\n" + "\n" + "Editor hooks allow implementing technology-specific callbacks into the editor " + "for example to implement visual feedback about DRC rules.\n" + "\n" + "This class provides the basic interface. To implement callbacks, use the \\EditorHooks class. " + "You should not need to instantiate this class.\n" + "\n" // @@@ "\n" "The EditorHooks class has been introduced in version 0.29." From f087a579aff197037e8bebe23292f3b1bed748ac Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 20:53:43 +0100 Subject: [PATCH 09/17] Refactoring of the editor hooks API --- src/edt/edt/edtEditorHooks.h | 35 +++-- src/edt/edt/edtServiceImpl.cc | 75 ++++------- src/edt/edt/edtServiceImpl.h | 1 - src/edt/edt/gsiDeclEdtEditorHooks.cc | 191 ++++++++++++++++++++------- 4 files changed, 195 insertions(+), 107 deletions(-) diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index 893562147..f17ed2989 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -37,10 +37,18 @@ namespace lay { + class CellViewRef; class LayoutViewBase; + class LayerProperties; class ObjectInstPath; } +namespace db +{ + class Instance; + class Shape; +} + namespace edt { @@ -54,7 +62,8 @@ namespace edt * * 1. Object Creation * - * begin_create { begin_new_objects { create } end_new_objects } [ commit_create ] end_create + * begin_create_shapes { begin_new_shapes { create_shape } end_new_shapes } [ commit_shapes ] end_create_shapes + * begin_create_instances { begin_new_instances { create_instance } end_new_instances } [ commit_instances ] end_create_instances * * 2. Modification (i.e. partial edit) * @@ -85,13 +94,21 @@ public: */ virtual ~EditorHooks (); - // creation protocol - virtual void begin_create (lay::LayoutViewBase * /*view*/) { } - virtual void begin_new_objects () { } - virtual void create (const lay::ObjectInstPath & /*object*/, const db::CplxTrans & /*view_trans*/) { } - virtual void end_new_objects () { } - virtual void commit_create () { } - virtual void end_create () { } + // shape creation protocol + virtual void begin_create_shapes (lay::CellViewRef & /*cv*/, const lay::LayerProperties & /*layer*/) { } + virtual void begin_new_shapes () { } + virtual void create_shape (const db::Shape & /*shape*/, const db::CplxTrans & /*view_trans*/) { } + virtual void end_new_shapes () { } + virtual void commit_shapes () { } + virtual void end_create_shapes () { } + + // instance creation protocol + virtual void begin_create_instances (lay::CellViewRef & /*cv*/) { } + virtual void begin_new_instances () { } + virtual void create_instance (const db::Instance & /*instance*/, const db::CplxTrans & /*view_trans*/) { } + virtual void end_new_instances () { } + virtual void commit_instances () { } + virtual void end_create_instances () { } // modification protocol virtual void begin_modify (lay::LayoutViewBase * /*view*/) { } @@ -265,7 +282,7 @@ void call_editor_hooks (const tl::weak_collection &hooks, void (Edi template inline -void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2), A1 a1, A2 a2, A3 a3) +void call_editor_hooks (const tl::weak_collection &hooks, void (EditorHooks::*meth) (A1, A2, A3), A1 a1, A2 a2, A3 a3) { for (auto h = hooks.begin (); h != hooks.end (); ++h) { try { diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 03e11c1ec..ee6ed623f 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -408,45 +408,30 @@ ShapeEditService::open_editor_hooks () technology = mp_layout->technology ()->name (); } - // NOTE: this is a kind of hack - as we want to present the new shape in some - // natural habitat (aka Shapes), we create an artificial Shapes container for holding the - // temporary object. - m_tmp_shapes.reset (new db::Shapes (0, mp_cell, true)); - m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create, view ()); + + lay::CellViewRef cv_ref (view ()->cellview_ref (m_cv_index)); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create_shapes, cv_ref, *view ()->current_layer ()); } void ShapeEditService::close_editor_hooks (bool with_commit) { if (with_commit) { - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_create); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_shapes); } - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create_shapes); m_editor_hooks.clear (); - m_tmp_shapes.reset (0); } template void ShapeEditService::deliver_shape_to_hooks (const Shape &shape) { - if (! mp_cell || ! m_tmp_shapes.get ()) { - return; - } - - m_tmp_shapes->clear (); - db::Shape s = m_tmp_shapes->insert (shape); - - lay::ObjectInstPath path; - path.set_cv_index (m_cv_index); - path.set_layer (m_layer); - path.set_topcell (mp_cell->cell_index ()); - path.set_shape (s); - - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create, path, trans ().inverted ()); + db::Shapes tmp (true); + db::Shape s = tmp.insert (shape); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create_shape, s, trans ().inverted ()); } // explicit instantiations @@ -797,13 +782,13 @@ PolygonService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes); try { deliver_shape_to_hooks (get_polygon (true)); } catch (...) { // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes); } } @@ -863,13 +848,13 @@ BoxService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes); try { deliver_shape_to_hooks (get_box ()); } catch (...) { // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes); } } @@ -974,13 +959,13 @@ PointService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes); try { deliver_shape_to_hooks (get_point ()); } catch (...) { // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes); } } @@ -1091,13 +1076,13 @@ TextService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes); try { deliver_shape_to_hooks (get_text ()); } catch (...) { // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes); } } @@ -1394,13 +1379,13 @@ PathService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_shapes); try { deliver_shape_to_hooks (get_path ()); } catch (...) { // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_shapes); } } @@ -2269,7 +2254,7 @@ InstService::update_marker () // call hooks with new shape if (! editor_hooks ().empty ()) { - call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::begin_new_instances); try { @@ -2278,18 +2263,12 @@ InstService::update_marker () db::CellInstArray inst; if (cv.is_valid () && get_inst (inst)) { - // Note: we create a temporary instance collection and a temporary - // ObjectInstPath object there, so + // Note: the instance collection is temporary db::Instances instances (cv.cell ()); - - lay::ObjectInstPath path; - path.set_cv_index (m_cv_index); - path.set_topcell (cv.cell_index ()); - path.add_path (db::InstElement (instances.insert (inst))); + db::Instance i = instances.insert (inst); db::CplxTrans view_trans = db::CplxTrans (cv->layout ().dbu ()) * m_trans; - - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create, path, view_trans); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::create_instance, i, view_trans); } @@ -2297,7 +2276,7 @@ InstService::update_marker () // ignore exceptions } - call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_objects); + call_editor_hooks (editor_hooks (), &edt::EditorHooks::end_new_instances); } } @@ -2345,16 +2324,18 @@ InstService::open_editor_hooks () } m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create, view ()); + + lay::CellViewRef cv_ref (view ()->cellview_ref (m_cv_index)); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_create_instances, cv_ref); } void InstService::close_editor_hooks (bool with_commit) { if (with_commit) { - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_create); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_instances); } - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_create_instances); m_editor_hooks.clear (); } diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 747597ec4..80e4bc515 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -89,7 +89,6 @@ private: db::Layout *mp_layout; combine_mode_type m_combine_mode; tl::weak_collection m_editor_hooks; - std::unique_ptr m_tmp_shapes; void update_edit_layer (const lay::LayerPropertiesConstIterator &iter); }; diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 19508939e..47af39b22 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -39,57 +39,111 @@ public: // .. nothing yet .. } - virtual void begin_create (lay::LayoutViewBase *view) + virtual void begin_create_shapes (lay::CellViewRef &cv, const lay::LayerProperties &layer) { - if (f_begin_create.can_issue ()) { - f_begin_create.issue (&edt::EditorHooks::begin_create, view); + if (f_begin_create_shapes.can_issue ()) { + f_begin_create_shapes.issue (&edt::EditorHooks::begin_create_shapes, cv, layer); } else { - edt::EditorHooks::begin_create (view); + edt::EditorHooks::begin_create_shapes (cv, layer); } } - virtual void begin_new_objects () + virtual void begin_new_shapes () { - if (f_begin_new_objects.can_issue ()) { - f_begin_new_objects.issue (&edt::EditorHooks::begin_new_objects); + if (f_begin_new_shapes.can_issue ()) { + f_begin_new_shapes.issue (&edt::EditorHooks::begin_new_shapes); } else { - edt::EditorHooks::begin_new_objects (); + edt::EditorHooks::begin_new_shapes (); } } - virtual void create (const lay::ObjectInstPath &object, const db::CplxTrans &view_trans) + virtual void create_shape (const db::Shape &shape, const db::CplxTrans &view_trans) { - if (f_create.can_issue ()) { - f_create.issue (&edt::EditorHooks::create, object, view_trans); + if (f_create_shape.can_issue ()) { + f_create_shape.issue (&edt::EditorHooks::create_shape, shape, view_trans); } else { - edt::EditorHooks::create (object, view_trans); + edt::EditorHooks::create_shape (shape, view_trans); } } - virtual void end_new_objects () + virtual void end_new_shapes () { - if (f_end_new_objects.can_issue ()) { - f_end_new_objects.issue (&edt::EditorHooks::end_new_objects); + if (f_end_new_shapes.can_issue ()) { + f_end_new_shapes.issue (&edt::EditorHooks::end_new_shapes); } else { - edt::EditorHooks::end_new_objects (); + edt::EditorHooks::end_new_shapes (); } } - virtual void commit_create () + virtual void commit_shapes () { - if (f_commit_create.can_issue ()) { - f_commit_create.issue (&edt::EditorHooks::commit_create); + if (f_commit_shapes.can_issue ()) { + f_commit_shapes.issue (&edt::EditorHooks::commit_shapes); } else { - edt::EditorHooks::commit_create (); + edt::EditorHooks::commit_shapes (); } } - virtual void end_create () + virtual void end_create_shapes () { - if (f_end_create.can_issue ()) { - f_end_create.issue (&edt::EditorHooks::end_create); + if (f_end_create_shapes.can_issue ()) { + f_end_create_shapes.issue (&edt::EditorHooks::end_create_shapes); } else { - edt::EditorHooks::end_create (); + edt::EditorHooks::end_create_shapes (); + } + } + + virtual void begin_create_instances (lay::CellViewRef &cv) + { + if (f_begin_create_instances.can_issue ()) { + f_begin_create_instances.issue (&edt::EditorHooks::begin_create_instances, cv); + } else { + edt::EditorHooks::begin_create_instances (cv); + } + } + + virtual void begin_new_instances () + { + if (f_begin_new_instances.can_issue ()) { + f_begin_new_instances.issue (&edt::EditorHooks::begin_new_instances); + } else { + edt::EditorHooks::begin_new_instances (); + } + } + + virtual void create_instance (const db::Instance &object, const db::CplxTrans &view_trans) + { + if (f_create_instance.can_issue ()) { + f_create_instance.issue (&edt::EditorHooks::create_instance, object, view_trans); + } else { + edt::EditorHooks::create_instance (object, view_trans); + } + } + + virtual void end_new_instances () + { + if (f_end_new_instances.can_issue ()) { + f_end_new_instances.issue (&edt::EditorHooks::end_new_instances); + } else { + edt::EditorHooks::end_new_instances (); + } + } + + virtual void commit_instances () + { + if (f_commit_instances.can_issue ()) { + f_commit_instances.issue (&edt::EditorHooks::commit_instances); + } else { + edt::EditorHooks::commit_instances (); + } + } + + virtual void end_create_instances () + { + if (f_end_create_instances.can_issue ()) { + f_end_create_instances.issue (&edt::EditorHooks::end_create_instances); + } else { + edt::EditorHooks::end_create_instances (); } } @@ -201,12 +255,19 @@ public: } } - gsi::Callback f_begin_create; - gsi::Callback f_begin_new_objects; - gsi::Callback f_create; - gsi::Callback f_end_new_objects; - gsi::Callback f_commit_create; - gsi::Callback f_end_create; + gsi::Callback f_begin_create_shapes; + gsi::Callback f_begin_new_shapes; + gsi::Callback f_create_shape; + gsi::Callback f_end_new_shapes; + gsi::Callback f_commit_shapes; + gsi::Callback f_end_create_shapes; + + gsi::Callback f_begin_create_instances; + gsi::Callback f_begin_new_instances; + gsi::Callback f_create_instance; + gsi::Callback f_end_new_instances; + gsi::Callback f_commit_instances; + gsi::Callback f_end_create_instances; gsi::Callback f_begin_modify; gsi::Callback f_begin_modifications; @@ -229,34 +290,64 @@ static void register_editor_hooks (EditorHooksImpl *hooks, const std::string &na } gsi::Class decl_EditorHooks ("lay", "EditorHooks", - gsi::callback ("begin_create", &EditorHooksImpl::begin_create, &EditorHooksImpl::f_begin_create, - "@brief Creation protocol - begin session\n" - "This method is called to initiate an object creation session. The session is ended with " - "\\end_create. Between these calls, new objects are announced with \\begin_new_objects, " - "\\create and \\end_new_objects calls. These calls are repeated to indicate changes in the objects " + gsi::callback ("begin_create_shapes", &EditorHooksImpl::begin_create_shapes, &EditorHooksImpl::f_begin_create_shapes, gsi::arg ("cellview"), gsi::arg ("layer"), + "@brief Shape creation protocol - begin session\n" + "This method is called to initiate a shape creation session. The session is ended with " + "\\end_create_shapes. Between these calls, new objects are announced with \\begin_new_shapes, " + "\\create_shape and \\end_new_shapes calls. These calls are repeated to indicate changes in the objects " "created.\n" "\n" - "\\commit_create is called once before \\end_create to indicate that the last set of " - "objects are committed to the database." + "\\commit_shapes is called once before \\end_create_shapes to indicate that the last set of " + "objects is committed to the database." ) + - gsi::callback ("begin_new_objects", &EditorHooksImpl::begin_new_objects, &EditorHooksImpl::f_begin_new_objects, - "@brief Creation protocol - begin new objects\n" + gsi::callback ("begin_new_shapes", &EditorHooksImpl::begin_new_shapes, &EditorHooksImpl::f_begin_new_shapes, + "@brief Shape creation protocol - begin new shapes\n" + "See \\begin_create_shapes for a description of the protocol." + ) + + gsi::callback ("create_shape", &EditorHooksImpl::create_shape, &EditorHooksImpl::f_create_shape, gsi::arg ("shape"), gsi::arg ("view_trans"), + "@brief Shape creation protocol - indicate a new object\n" + "See \\begin_create_shapes for a description of the protocol." + ) + + gsi::callback ("end_new_shapes", &EditorHooksImpl::end_new_shapes, &EditorHooksImpl::f_end_new_shapes, + "@brief Shape creation protocol - finish list of new shapes\n" + "See \\begin_create_shapes for a description of the protocol." + ) + + gsi::callback ("commit_shapes", &EditorHooksImpl::commit_shapes, &EditorHooksImpl::f_commit_shapes, + "@brief Shape creation protocol - commit new objects\n" + "See \\begin_create_shapes for a description of the protocol." + ) + + gsi::callback ("end_create_shapes", &EditorHooksImpl::end_create_shapes, &EditorHooksImpl::f_end_create_shapes, + "@brief Shape creation protocol - finish session\n" "See \\begin_create for a description of the protocol." ) + - gsi::callback ("create", &EditorHooksImpl::create, &EditorHooksImpl::f_create, gsi::arg ("object"), gsi::arg ("view_trans"), - "@brief Creation protocol - indicate a new object\n" - "See \\begin_create for a description of the protocol." + gsi::callback ("begin_create_instances", &EditorHooksImpl::begin_create_instances, &EditorHooksImpl::f_begin_create_instances, gsi::arg ("cellview"), + "@brief Instance creation protocol - begin session\n" + "This method is called to initiate an instance creation session. The session is ended with " + "\\end_create_instances. Between these calls, new objects are announced with \\begin_new_instances, " + "\\create_instance and \\end_new_instances calls. These calls are repeated to indicate changes in the objects " + "created.\n" + "\n" + "\\commit_instances is called once before \\end_create_instances to indicate that the last set of " + "objects is committed to the database." ) + - gsi::callback ("end_new_objects", &EditorHooksImpl::end_new_objects, &EditorHooksImpl::f_end_new_objects, - "@brief Creation protocol - finish list of new objects\n" - "See \\begin_create for a description of the protocol." + gsi::callback ("begin_new_instances", &EditorHooksImpl::begin_new_instances, &EditorHooksImpl::f_begin_new_instances, + "@brief Instance creation protocol - begin new instances\n" + "See \\begin_create_instances for a description of the protocol." ) + - gsi::callback ("commit_create", &EditorHooksImpl::commit_create, &EditorHooksImpl::f_commit_create, - "@brief Creation protocol - commit new objects\n" - "See \\begin_create for a description of the protocol." + gsi::callback ("create_instance", &EditorHooksImpl::create_instance, &EditorHooksImpl::f_create_instance, gsi::arg ("instance"), gsi::arg ("view_trans"), + "@brief Instance creation protocol - indicate a new object\n" + "See \\begin_create_instances for a description of the protocol." ) + - gsi::callback ("end_create", &EditorHooksImpl::end_create, &EditorHooksImpl::f_end_create, - "@brief Creation protocol - finish session\n" + gsi::callback ("end_new_instances", &EditorHooksImpl::end_new_instances, &EditorHooksImpl::f_end_new_instances, + "@brief Instance creation protocol - finish list of new instances\n" + "See \\begin_create_instances for a description of the protocol." + ) + + gsi::callback ("commit_instances", &EditorHooksImpl::commit_instances, &EditorHooksImpl::f_commit_instances, + "@brief Instance creation protocol - commit new objects\n" + "See \\begin_create_instances for a description of the protocol." + ) + + gsi::callback ("end_create_instances", &EditorHooksImpl::end_create_instances, &EditorHooksImpl::f_end_create_instances, + "@brief Instance creation protocol - finish session\n" "See \\begin_create for a description of the protocol." ) + gsi::callback ("begin_modify", &EditorHooksImpl::begin_modify, &EditorHooksImpl::f_begin_modify, gsi::arg ("view"), From 6c8ff49bc79f3f4b4940d4d520b4a34a8eb8b6a5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 20:56:24 +0100 Subject: [PATCH 10/17] Added a test for post-increment iterator of tl::Object collections --- src/tl/unit_tests/tlObjectTests.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tl/unit_tests/tlObjectTests.cc b/src/tl/unit_tests/tlObjectTests.cc index b5cfaabd5..ae8493723 100644 --- a/src/tl/unit_tests/tlObjectTests.cc +++ b/src/tl/unit_tests/tlObjectTests.cc @@ -399,6 +399,9 @@ TEST(22) EXPECT_EQ (sc.empty (), false); EXPECT_EQ (sc.front ()->attr (), 1); EXPECT_EQ ((++sc.begin ())->attr (), 1); + auto i = sc.begin (); + i++; + EXPECT_EQ (i->attr (), 1); sc.pop_back (); EXPECT_EQ (int (sc.size ()), 1); @@ -418,6 +421,9 @@ TEST(22) EXPECT_EQ (sc.empty (), false); EXPECT_EQ (sc.front ()->attr (), 2); EXPECT_EQ ((++sc.begin ())->attr (), 2); + i = sc.begin (); + i++; + EXPECT_EQ (i->attr (), 2); } EXPECT_EQ (MyClass::instances (), 0); From 03d77bbff6df9cca4a809faacda0072c98a62ec2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 22:01:39 +0100 Subject: [PATCH 11/17] Region#drc_hull for generating DRC space forbidding region visualizations --- src/db/db/dbRegionProcessors.cc | 121 ++++++++++++++++++++++++++++++++ src/db/db/dbRegionProcessors.h | 19 +++++ src/db/db/gsiDeclDbRegion.cc | 16 +++++ testdata/ruby/dbRegionTest.rb | 12 ++++ 4 files changed, 168 insertions(+) diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 63fcdea3b..655af7c7c 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -346,4 +346,125 @@ TriangulationProcessor::process (const db::Polygon &poly, std::vector &points, const db::Edge &e, db::Coord dist, size_t n_circle) +{ + db::Vector d (e.d ()); + db::Vector n (-d.y (), d.x ()); + + if (d.x () == 0 && d.y () == 0) { + return; + } + + double da = M_PI * 2.0 / n_circle; + double f = dist / n.double_length (); + double f2 = f / cos (0.5 * da); + + points.push_back (e.p1 ()); + points.push_back (e.p1 () + db::Vector (d * -f)); + + for (size_t i = 0; i < n_circle / 4; ++i) { + double a = (i + 0.5) * da; + points.push_back (e.p1 () + db::Vector (d * (-f2 * cos (a)) + n * (f2 * sin (a)))); + } + + points.push_back (e.p1 () + db::Vector (n * f)); + points.push_back (e.p2 () + db::Vector (n * f)); + + for (size_t i = 0; i < n_circle / 4; ++i) { + double a = (i + 0.5) * da; + points.push_back (e.p2 () + db::Vector (d * (f2 * sin (a)) + n * (f2 * cos (a)))); + } + + points.push_back (e.p2 () + db::Vector (d * f)); +} + +static void create_edge_segment_square (std::vector &points, const db::Edge &e, db::Coord dist) +{ + db::Vector d (e.d ()); + db::Vector n (-d.y (), d.x ()); + + if (d.x () == 0 && d.y () == 0) { + return; + } + + double f = dist / n.double_length (); + + points.push_back (e.p1 ()); + points.push_back (e.p1 () + db::Vector (d * -f)); + points.push_back (e.p1 () + db::Vector (d * -f + n * f)); + points.push_back (e.p2 () + db::Vector (d * f + n * f)); + points.push_back (e.p2 () + db::Vector (d * f)); +} + +static void create_edge_segment_projection (std::vector &points, const db::Edge &e, db::Coord dist) +{ + db::Vector d (e.d ()); + db::Vector n (-d.y (), d.x ()); + + if (d.x () == 0 && d.y () == 0) { + return; + } + + double f = dist / n.double_length (); + + points.push_back (e.p1 ()); + points.push_back (e.p1 () + db::Vector (n * f)); + points.push_back (e.p2 () + db::Vector (n * f)); +} + +static void create_edge_segment (std::vector &points, db::metrics_type metrics, const db::Edge &e, db::Coord d, size_t n_circle) +{ + if (metrics == db::Euclidian) { + create_edge_segment_euclidian (points, e, d, n_circle); + } else if (metrics == db::Square) { + create_edge_segment_square (points, e, d); + } else if (metrics == db::Projection) { + create_edge_segment_projection (points, e, d); + } +} + +void +DRCHullProcessor::process (const db::Polygon &poly, std::vector &result) const +{ + result.push_back (db::Polygon ()); + + for (unsigned int i = 0; i < poly.holes () + 1; ++i) { + + auto c = poly.contour (i); + if (c.size () < 2) { + continue; + } + + std::vector points; + + for (auto p = c.begin (); p != c.end (); ++p) { + + auto pp = p; + ++pp; + if (pp == c.end ()) { + pp = c.begin (); + } + + create_edge_segment (points, m_metrics, db::Edge (*p, *pp), m_d, m_n_circle); + + } + + if (i == 0) { + result.back ().assign_hull (points.begin (), points.end ()); + } else { + result.back ().insert_hole (points.begin (), points.end ()); + } + + } +} + } diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index 08553ca25..de017aa7c 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -29,6 +29,7 @@ #include "dbPolygonTools.h" #include "dbEdgesUtils.h" #include "dbTriangles.h" +#include "dbEdgePairRelations.h" namespace db { @@ -464,6 +465,24 @@ private: db::MagnificationAndOrientationReducer m_vars; }; +/** + * @brief Computes DRC hulls for DRC space visualization + */ +class DB_PUBLIC_TEMPLATE DRCHullProcessor + : public db::PolygonProcessorBase +{ +public: + DRCHullProcessor (db::Coord d, db::metrics_type metrics, size_t n_circle = 64); + + void process (const db::Polygon &poly, std::vector &result) const; + +private: + db::Coord m_d; + db::metrics_type m_metrics; + size_t m_n_circle; +}; + + } #endif diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index c5d6c6570..f6380c72f 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -436,6 +436,11 @@ static db::Region refined_delaunay (const db::Region *r, double max_area, double return res; } +static db::Region drc_hull (const db::Region *r, db::metrics_type metrics, db::Coord space, size_t n_circle) +{ + return r->processed (db::DRCHullProcessor (space, metrics, n_circle)); +} + static db::Region minkowski_sum_pe (const db::Region *r, const db::Edge &e) { return r->processed (db::minkowski_sum_computation (e)); @@ -2782,6 +2787,17 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method." "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("drc_hull", &drc_hull, gsi::arg ("metrics"), gsi::arg ("space"), gsi::arg ("n_circle", size_t (64)), + "@brief Computes a visualization of the forbidden region for a DRC space check\n" + "\n" + "@param metrics The metrics to apply\n" + "@param space The space value to apply\n" + "@param n_circle The full-circle number of points for the Euclidian space visualization\n" + "\n" + "@return The new polygons representing the forbidden region.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("move", &move_p, gsi::arg ("v"), "@brief Moves the region\n" "\n" diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 8c59d587c..eb1d52f09 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1452,6 +1452,18 @@ class DBRegion_TestClass < TestBase end + # DRC hull + def test_drc_hull + + r = RBA::Region::new() + r.insert(RBA::Polygon::new([[0, 0], [1000, 1000], [1500, 0]])) + + assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;917,1200;953,1200;985,1216;1033,1200;1083,1200;1107,1176;1142,1164;1179,1089;1716,15;1700,-33;1700,-83;1675,-108;1664,-142;1619,-164;1583,-200)") + assert_equal(r.drc_hull(RBA::Region::Square, 200).merged.to_s, "(-200,-200;-200,-82;-283,0;1000,1283;1039,1243;1089,1268;1768,-89;1700,-123;1700,-200)") + assert_equal(r.drc_hull(RBA::Region::Projection, 200).merged.to_s, "(0,-200;0,0;-141,141;859,1141;1000,1000;1179,1089;1679,89;1500,0;1500,-200)") + + end + end load("test_epilogue.rb") From eb1ac4903eee2e043e27e46776db8947c0642ff2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2024 22:34:26 +0100 Subject: [PATCH 12/17] Refinement of solution for DRC hull generator, still not perfect. --- src/db/db/dbRegionProcessors.cc | 26 +++++++++++++++++++------- src/edt/edt/edtServiceImpl.cc | 10 +++++----- src/edt/edt/edtServiceImpl.h | 2 +- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 655af7c7c..bf9b1ccf2 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -435,17 +435,18 @@ static void create_edge_segment (std::vector &points, db::metrics_typ void DRCHullProcessor::process (const db::Polygon &poly, std::vector &result) const { - result.push_back (db::Polygon ()); + db::EdgeProcessor ep; + std::vector points; for (unsigned int i = 0; i < poly.holes () + 1; ++i) { + points.clear (); + auto c = poly.contour (i); if (c.size () < 2) { continue; } - std::vector points; - for (auto p = c.begin (); p != c.end (); ++p) { auto pp = p; @@ -458,13 +459,24 @@ DRCHullProcessor::process (const db::Polygon &poly, std::vector &re } - if (i == 0) { - result.back ().assign_hull (points.begin (), points.end ()); - } else { - result.back ().insert_hole (points.begin (), points.end ()); + for (auto p = points.begin (); p != points.end (); ++p) { + + auto pp = p; + ++pp; + if (pp == points.end ()) { + pp = points.begin (); + } + + ep.insert (db::Edge (*p, *pp)); + } } + + db::SimpleMerge op; + db::PolygonContainer psink (result); + db::PolygonGenerator pg (psink, false); + ep.process (pg, op); } } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index ee6ed623f..dc3d2228c 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -552,11 +552,11 @@ PolygonService::do_finish_edit () } db::Polygon -PolygonService::get_polygon (bool all_points) const +PolygonService::get_polygon (bool editing) const { db::Polygon poly; - if (m_points.size () + (m_closure_set ? 1 : 0) < (all_points ? 3 : 4)) { + if (! editing && m_points.size () + (m_closure_set ? 1 : 0) < 4) { throw tl::Exception (tl::to_string (tr ("A polygon must have at least 3 points"))); } @@ -567,16 +567,16 @@ PolygonService::get_polygon (bool all_points) const for (std::vector::const_iterator p = m_points.begin (); p + 1 != m_points.end (); ++p) { points_dbu.push_back (trans () * *p); } - if (all_points) { + if (editing) { points_dbu.push_back (trans () * m_points.back ()); } if (m_closure_set) { points_dbu.push_back (trans () * m_closure); } - poly.assign_hull (points_dbu.begin (), points_dbu.end (), true, true /*remove reflected*/); + poly.assign_hull (points_dbu.begin (), points_dbu.end (), !editing /*compress*/, !editing /*remove reflected*/); - if (poly.hull ().size () < 3) { + if (! editing && poly.hull ().size () < 3) { throw tl::Exception (tl::to_string (tr ("A polygon must have at least 3 effective points"))); } diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 80e4bc515..e2c31ae5d 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -121,7 +121,7 @@ private: db::DPoint m_last; void update_marker (); - db::Polygon get_polygon (bool all_points) const; + db::Polygon get_polygon (bool editing) const; void add_closure (); void set_last_point (const db::DPoint &p); }; From 3836874b8f39df512d3c07886dd160226feedf93 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2024 10:30:04 +0200 Subject: [PATCH 13/17] Enhanced Euclidian visualization --- src/db/db/dbRegionProcessors.cc | 67 +++++++++++++++++++++------------ testdata/ruby/dbRegionTest.rb | 9 ++++- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index bf9b1ccf2..40f515b1e 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -355,36 +355,52 @@ DRCHullProcessor::DRCHullProcessor (db::Coord d, db::metrics_type metrics, size_ // .. nothing yet .. } -static void create_edge_segment_euclidian (std::vector &points, const db::Edge &e, db::Coord dist, size_t n_circle) +static void create_edge_segment_euclidian (std::vector &points, const db::Edge &e, const db::Edge &ee, db::Coord dist, size_t n_circle) { db::Vector d (e.d ()); db::Vector n (-d.y (), d.x ()); - if (d.x () == 0 && d.y () == 0) { + db::Vector dd (ee.d ()); + db::Vector nn (-dd.y (), dd.x ()); + + if ((d.x () == 0 && d.y () == 0) || (dd.x () == 0 && dd.y () == 0)) { + // should not happen return; } - double da = M_PI * 2.0 / n_circle; double f = dist / n.double_length (); - double f2 = f / cos (0.5 * da); - - points.push_back (e.p1 ()); - points.push_back (e.p1 () + db::Vector (d * -f)); - - for (size_t i = 0; i < n_circle / 4; ++i) { - double a = (i + 0.5) * da; - points.push_back (e.p1 () + db::Vector (d * (-f2 * cos (a)) + n * (f2 * sin (a)))); - } + double ff = dist / nn.double_length (); points.push_back (e.p1 () + db::Vector (n * f)); points.push_back (e.p2 () + db::Vector (n * f)); - for (size_t i = 0; i < n_circle / 4; ++i) { - double a = (i + 0.5) * da; - points.push_back (e.p2 () + db::Vector (d * (f2 * sin (a)) + n * (f2 * cos (a)))); - } + if (db::vprod_sign (nn, n) < 0) { - points.push_back (e.p2 () + db::Vector (d * f)); + // concave corner + points.push_back (e.p2 ()); + points.push_back (e.p2 () + db::Vector (nn * ff)); + + } else { + + double amax; + if (db::vprod_sign (nn, n) == 0) { + amax = db::sprod_sign (nn, n) < 0 ? M_PI : 0.0; + } else { + amax = atan2 (db::vprod (nn, n), db::sprod (nn, n)); + } + + double da = M_PI * 2.0 / n_circle; + double f2 = f / cos (0.5 * da); + + int na = int (floor (amax / da + db::epsilon)); + double a0 = 0.5 * (amax - da * (na - 1)); + + for (int i = 0; i < na; ++i) { + double a = i * da + a0; + points.push_back (e.p2 () + db::Vector (d * (f2 * sin (a)) + n * (f2 * cos (a)))); + } + + } } static void create_edge_segment_square (std::vector &points, const db::Edge &e, db::Coord dist) @@ -421,10 +437,10 @@ static void create_edge_segment_projection (std::vector &points, cons points.push_back (e.p2 () + db::Vector (n * f)); } -static void create_edge_segment (std::vector &points, db::metrics_type metrics, const db::Edge &e, db::Coord d, size_t n_circle) +static void create_edge_segment (std::vector &points, db::metrics_type metrics, const db::Edge &e, const db::Edge &ee, db::Coord d, size_t n_circle) { if (metrics == db::Euclidian) { - create_edge_segment_euclidian (points, e, d, n_circle); + create_edge_segment_euclidian (points, e, ee, d, n_circle); } else if (metrics == db::Square) { create_edge_segment_square (points, e, d); } else if (metrics == db::Projection) { @@ -450,20 +466,23 @@ DRCHullProcessor::process (const db::Polygon &poly, std::vector &re for (auto p = c.begin (); p != c.end (); ++p) { auto pp = p; - ++pp; - if (pp == c.end ()) { + if (++pp == c.end ()) { pp = c.begin (); } - create_edge_segment (points, m_metrics, db::Edge (*p, *pp), m_d, m_n_circle); + auto ppp = pp; + if (++ppp == c.end ()) { + ppp = c.begin (); + } + + create_edge_segment (points, m_metrics, db::Edge (*p, *pp), db::Edge (*pp, *ppp), m_d, m_n_circle); } for (auto p = points.begin (); p != points.end (); ++p) { auto pp = p; - ++pp; - if (pp == points.end ()) { + if (++ pp == points.end ()) { pp = points.begin (); } diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index eb1d52f09..cd4546c7c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1458,10 +1458,17 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new() r.insert(RBA::Polygon::new([[0, 0], [1000, 1000], [1500, 0]])) - assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;917,1200;953,1200;985,1216;1033,1200;1083,1200;1107,1176;1142,1164;1179,1089;1716,15;1700,-33;1700,-83;1675,-108;1664,-142;1619,-164;1583,-200)") + assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;950,1211;1114,1184;1179,1089;1679,89;1714,-35;1627,-176;1500,-200)") assert_equal(r.drc_hull(RBA::Region::Square, 200).merged.to_s, "(-200,-200;-200,-82;-283,0;1000,1283;1039,1243;1089,1268;1768,-89;1700,-123;1700,-200)") assert_equal(r.drc_hull(RBA::Region::Projection, 200).merged.to_s, "(0,-200;0,0;-141,141;859,1141;1000,1000;1179,1089;1679,89;1500,0;1500,-200)") + r = RBA::Region::new() + r.merged_semantics = false + r.insert(RBA::Polygon::new([[0, 0], [1000, 1000]], true)) + assert_equal(r.drc_hull(RBA::Region::Euclidian, 200, 8).merged.to_s, "(-83,-200;-200,-83;-200,83;-141,141;859,1141;917,1200;1083,1200;1200,1083;1200,917;1141,859;141,-141;83,-200)") + assert_equal(r.drc_hull(RBA::Region::Square, 200, 8).merged.to_s, "(0,-283;-141,-141;-283,0;1000,1283;1141,1141;1283,1000)") + assert_equal(r.drc_hull(RBA::Region::Projection, 200, 8).merged.to_s, "(141,-141;-141,141;859,1141;1141,859)") + end end From 1c284b09398fd1745f2948f34ad4637d46132c03 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2024 19:26:22 +0200 Subject: [PATCH 14/17] Editor hooks for move operations --- src/edt/edt/edt.pro | 2 + src/edt/edt/edtEditorHooks.h | 2 +- src/edt/edt/edtMoveTrackerService.cc | 161 +++++++++++++++++++++++++++ src/edt/edt/edtMoveTrackerService.h | 97 ++++++++++++++++ src/edt/edt/edtPlugin.cc | 22 ++++ src/edt/edt/edtService.h | 16 +++ src/edt/edt/gsiDeclEdtEditorHooks.cc | 8 +- 7 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 src/edt/edt/edtMoveTrackerService.cc create mode 100644 src/edt/edt/edtMoveTrackerService.h diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 300f3faa8..fe31cbc66 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -39,6 +39,7 @@ HEADERS = \ edtEditorHooks.h \ edtEditorOptionsPages.h \ edtInstPropertiesPage.h \ + edtMoveTrackerService.h \ edtPCellParametersPage.h \ edtPropertiesPages.h \ edtPropertiesPageUtils.h \ @@ -49,6 +50,7 @@ SOURCES = \ edtEditorHooks.cc \ edtEditorOptionsPages.cc \ edtInstPropertiesPage.cc \ + edtMoveTrackerService.cc \ edtPCellParametersPage.cc \ edtPropertiesPages.cc \ edtPropertiesPageUtils.cc \ diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index f17ed2989..d355dc51f 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -119,7 +119,7 @@ public: virtual void end_modify () { } // editing protocol - virtual void begin_edit (lay::LayoutViewBase * /*view*/) { } + virtual void begin_edit (lay::CellViewRef & /*cv*/) { } virtual void begin_edits () { } virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::ICplxTrans & /*applied*/, const db::CplxTrans & /*view_trans*/) { } virtual void end_edits () { } diff --git a/src/edt/edt/edtMoveTrackerService.cc b/src/edt/edt/edtMoveTrackerService.cc new file mode 100644 index 000000000..1c8f10b59 --- /dev/null +++ b/src/edt/edt/edtMoveTrackerService.cc @@ -0,0 +1,161 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layLayoutViewBase.h" +#include "edtMoveTrackerService.h" +#include "edtService.h" + +namespace edt +{ + +// ------------------------------------------------------------- + +MoveTrackerService::MoveTrackerService (lay::LayoutViewBase *view) + : lay::EditorServiceBase (view), + mp_view (view) +{ + // .. nothing yet .. +} + +MoveTrackerService::~MoveTrackerService () +{ + // .. nothing yet .. +} + +bool +MoveTrackerService::begin_move (lay::Editable::MoveMode mode, const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/) +{ + if (view ()->is_editable () && mode == lay::Editable::Selected) { + open_editor_hooks (); + } + return false; +} + +void +MoveTrackerService::issue_edit_events () +{ + if (m_editor_hooks.empty ()) { + return; + } + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits); + + // build the transformation variants cache + TransformationVariants tv (view ()); + + std::vector services = view ()->get_plugins (); + std::vector sel; + + for (auto s = services.begin (); s != services.end (); ++s) { + + edt::Service *svc = *s; + + sel.clear (); + svc->get_selection (sel); + + for (auto r = sel.begin (); r != sel.end (); ++r) { + + const lay::CellView &cv = view ()->cellview (r->cv_index ()); + + // compute the transformation into context cell's micron space + double dbu = cv->layout ().dbu (); + db::CplxTrans gt = db::CplxTrans (dbu) * cv.context_trans () * r->trans (); + + // compute the move transformation in local object space + db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (svc->move_trans ()) * gt; + + db::DCplxTrans tvt; + + // get one representative global transformation + const std::vector *tv_list = 0; + if (r->is_cell_inst ()) { + tv_list = tv.per_cv (r->cv_index ()); + } else { + tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ()); + } + if (tv_list && ! tv_list->empty ()) { + tvt = tv_list->front (); + } + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::transformed, *r, applied, tvt * gt); + + } + + } + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edits); +} + +void +MoveTrackerService::move (const db::DPoint & /*pu*/, lay::angle_constraint_type /*ac*/) +{ + // we don't interpret this event, but use it to request status from the editor services + issue_edit_events (); +} + +void +MoveTrackerService::move_transform (const db::DPoint & /*pu*/, db::DFTrans /*tr*/, lay::angle_constraint_type /*ac*/) +{ + // we don't interpret this event, but use it to request status from the editor services + issue_edit_events (); +} + +void +MoveTrackerService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/) +{ + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_edit); + move_cancel (); // formally this functionality fits here +} + +void +MoveTrackerService::edit_cancel () +{ + move_cancel (); +} + +void +MoveTrackerService::move_cancel () +{ + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edit); + m_editor_hooks.clear (); +} + +void +MoveTrackerService::open_editor_hooks () +{ + lay::CellViewRef cv_ref (view ()->cellview_ref (view ()->active_cellview_index ())); + if (! cv_ref.is_valid ()) { + return; + } + + std::string technology; + if (cv_ref->layout ().technology ()) { + technology = cv_ref->layout ().technology ()->name (); + } + + m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edit, (lay::CellViewRef &) cv_ref); +} + +} // namespace edt + + diff --git a/src/edt/edt/edtMoveTrackerService.h b/src/edt/edt/edtMoveTrackerService.h new file mode 100644 index 000000000..2981bf48f --- /dev/null +++ b/src/edt/edt/edtMoveTrackerService.h @@ -0,0 +1,97 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_edtMoveTrackerService +#define HDR_edtMoveTrackerService + +#include "edtCommon.h" + +#include "layEditorServiceBase.h" +#include "edtEditorHooks.h" + +namespace edt { + +/** + * @brief A service tracking move commands a forwarding them to the editor hooks + */ + +class EDT_PUBLIC MoveTrackerService + : public lay::EditorServiceBase +{ +public: + /** + * @brief The constructor + */ + MoveTrackerService (lay::LayoutViewBase *view); + + /** + * @brief The destructor + */ + ~MoveTrackerService (); + + /** + * @brief Begin a "move" operation + */ + virtual bool begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac); + + /** + * @brief Continue a "move" operation + */ + virtual void move (const db::DPoint &p, lay::angle_constraint_type ac); + + /** + * @brief Transform during a move operation + */ + virtual void move_transform (const db::DPoint &p, db::DFTrans tr, lay::angle_constraint_type ac); + + /** + * @brief Terminate a "move" operation + */ + virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + + /** + * @brief Access to the view object + */ + lay::LayoutViewBase *view () const + { + tl_assert (mp_view != 0); + return mp_view; + } + + /** + * @brief Cancel any edit operations (such as move) + */ + virtual void edit_cancel (); + +private: + lay::LayoutViewBase *mp_view; + tl::weak_collection m_editor_hooks; + + void move_cancel (); + void open_editor_hooks (); + void issue_edit_events (); +}; + +} + +#endif + diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index c0a12ee26..fe0e239c9 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -34,6 +34,7 @@ #include "edtServiceImpl.h" #include "edtMainService.h" #include "edtPartialService.h" +#include "edtMoveTrackerService.h" #if defined(HAVE_QT) # include "edtEditorOptionsPages.h" # include "edtRecentConfigurationPage.h" @@ -565,5 +566,26 @@ static tl::RegisteredClass config_decl30 ( "edt::PartialService" ); +class MoveTrackerPluginDeclaration + : public lay::PluginDeclaration +{ +public: + MoveTrackerPluginDeclaration () + { + // .. nothing yet .. + } + + virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*root*/, lay::LayoutViewBase *view) const + { + return new edt::MoveTrackerService (view); + } +}; + +static tl::RegisteredClass config_decl40 ( + new MoveTrackerPluginDeclaration (), + 4100, + "edt::MoveTrackerService" +); + } diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 1f8f87999..0890d9bc7 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -381,6 +381,22 @@ public: */ std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const; + /** + * @brief Gets a value indicating whether a move operation is ongoing + */ + bool is_moving () const + { + return m_moving; + } + + /** + * @brief Gets the current move transformation (in DBU space on context cell level) + */ + const db::DTrans &move_trans () const + { + return m_move_trans; + } + protected: /** * @brief Update m_markers to reflect the selection diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 47af39b22..cd04df757 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -201,12 +201,12 @@ public: } } - virtual void begin_edit (lay::LayoutViewBase *view) + virtual void begin_edit (lay::CellViewRef &cv_ref) { if (f_begin_edit.can_issue ()) { - f_begin_edit.issue (&edt::EditorHooks::begin_edit, view); + f_begin_edit.issue (&edt::EditorHooks::begin_edit, cv_ref); } else { - edt::EditorHooks::begin_edit (view); + edt::EditorHooks::begin_edit (cv_ref); } } @@ -380,7 +380,7 @@ gsi::Class decl_EditorHooks ("lay", "EditorHooks", "@brief Modification protocol - finish session\n" "See \\begin_modify for a description of the protocol." ) + - gsi::callback ("begin_edit", &EditorHooksImpl::begin_edit, &EditorHooksImpl::f_begin_edit, gsi::arg ("view"), + gsi::callback ("begin_edit", &EditorHooksImpl::begin_edit, &EditorHooksImpl::f_begin_edit, gsi::arg ("cellview"), "@brief Editing protocol - begin session\n" "This method is called to initiate an object editing session. The session is ended with " "\\end_edit. Between these calls, edits are announced with \\begin_edits, " From 4809c0609186189c777156fa018331295eacd0c2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2024 22:10:24 +0200 Subject: [PATCH 15/17] Implemented editor hooks for partial mode too. Integrated modification events into edit protocol --- src/edt/edt/edtEditorHooks.h | 17 +- src/edt/edt/edtMoveTrackerService.cc | 21 +- src/edt/edt/edtPartialService.cc | 301 ++++++++++++++++++++------- src/edt/edt/edtPartialService.h | 12 ++ src/edt/edt/gsiDeclEdtEditorHooks.cc | 116 +++-------- 5 files changed, 282 insertions(+), 185 deletions(-) diff --git a/src/edt/edt/edtEditorHooks.h b/src/edt/edt/edtEditorHooks.h index d355dc51f..6762219ac 100644 --- a/src/edt/edt/edtEditorHooks.h +++ b/src/edt/edt/edtEditorHooks.h @@ -65,13 +65,9 @@ namespace edt * begin_create_shapes { begin_new_shapes { create_shape } end_new_shapes } [ commit_shapes ] end_create_shapes * begin_create_instances { begin_new_instances { create_instance } end_new_instances } [ commit_instances ] end_create_instances * - * 2. Modification (i.e. partial edit) + * 2. Interactive edit (move, transform, interactive clone) * - * begin_modify { begin_modifications { modified } end_modifications } [ commit_modify ] end_modify - * - * 3. Interactive edit (move, transform, interactive clone) - * - * begin_edit { begin_edits { transformed } end_edits } [ commit_edit ] end_edit + * begin_edit { begin_edits { transformed | modified } end_edits } [ commit_edit ] end_edit * * Notation: { ... } means the sequence can be repeated, [ ... ] means the call is optional. */ @@ -110,18 +106,11 @@ public: virtual void commit_instances () { } virtual void end_create_instances () { } - // modification protocol - virtual void begin_modify (lay::LayoutViewBase * /*view*/) { } - virtual void begin_modifications () { } - virtual void modified (const lay::ObjectInstPath & /*object*/, const db::CplxTrans & /*view_trans*/) { } - virtual void end_modifications () { } - virtual void commit_modify () { } - virtual void end_modify () { } - // editing protocol virtual void begin_edit (lay::CellViewRef & /*cv*/) { } virtual void begin_edits () { } virtual void transformed (const lay::ObjectInstPath & /*object*/, const db::ICplxTrans & /*applied*/, const db::CplxTrans & /*view_trans*/) { } + virtual void modified (const lay::ObjectInstPath & /*object*/, const db::Shape & /*shape*/, const db::CplxTrans & /*view_trans*/) { } virtual void end_edits () { } virtual void commit_edit () { } virtual void end_edit () { } diff --git a/src/edt/edt/edtMoveTrackerService.cc b/src/edt/edt/edtMoveTrackerService.cc index 1c8f10b59..14a42e4ed 100644 --- a/src/edt/edt/edtMoveTrackerService.cc +++ b/src/edt/edt/edtMoveTrackerService.cc @@ -23,6 +23,7 @@ #include "layLayoutViewBase.h" #include "edtMoveTrackerService.h" #include "edtService.h" +#include "edtPartialService.h" namespace edt { @@ -80,11 +81,6 @@ MoveTrackerService::issue_edit_events () double dbu = cv->layout ().dbu (); db::CplxTrans gt = db::CplxTrans (dbu) * cv.context_trans () * r->trans (); - // compute the move transformation in local object space - db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (svc->move_trans ()) * gt; - - db::DCplxTrans tvt; - // get one representative global transformation const std::vector *tv_list = 0; if (r->is_cell_inst ()) { @@ -93,15 +89,26 @@ MoveTrackerService::issue_edit_events () tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ()); } if (tv_list && ! tv_list->empty ()) { - tvt = tv_list->front (); + gt = tv_list->front () * gt; } - call_editor_hooks (m_editor_hooks, &edt::EditorHooks::transformed, *r, applied, tvt * gt); + // compute the move transformation in local object space + db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (svc->move_trans ()) * gt; + + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::transformed, *r, applied, gt); } } + // make the Partial Edit Service issue "modify" events + + std::vector partial_services = view ()->get_plugins (); + + for (auto s = partial_services.begin (); s != partial_services.end (); ++s) { + (*s)->issue_editor_hook_calls (m_editor_hooks); + } + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edits); } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index c614d7334..b1861d969 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1416,7 +1416,216 @@ PartialService::transform (const db::DCplxTrans &tr) selection_to_view (); } -void +void +PartialService::open_editor_hooks () +{ + lay::CellViewRef cv_ref (view ()->cellview_ref (view ()->active_cellview_index ())); + if (! cv_ref.is_valid ()) { + return; + } + + std::string technology; + if (cv_ref->layout ().technology ()) { + technology = cv_ref->layout ().technology ()->name (); + } + + m_editor_hooks = edt::EditorHooks::get_editor_hooks (technology); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edit, (lay::CellViewRef &) cv_ref); +} + +void +PartialService::close_editor_hooks (bool commit) +{ + if (commit) { + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_edit); + } + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edit); + + m_editor_hooks.clear (); +} + +void +PartialService::issue_editor_hook_calls (const tl::weak_collection &hooks) +{ + if (hooks.empty ()) { + return; + } + + // NOTE: needs to be called during move operations + db::DTrans move_trans = db::DTrans (m_current - m_start); + + // build the transformation variants cache + TransformationVariants tv (view ()); + + // Issue editor hook calls for the shape modification events + + // since a shape reference may become invalid while moving it and + // because it creates ambiguities, we treat each shape separately: + // collect the valid selected items in a selection-per-shape map. + std::map > sel_per_shape; + + for (partial_objects::iterator r = m_selection.begin (); r != m_selection.end (); ++r) { + if (! r->first.is_cell_inst ()) { + const std::vector *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ()); + if (tv_list && ! tv_list->empty ()) { + sel_per_shape.insert (std::make_pair (r->first.shape (), std::vector ())).first->second.push_back (r); + } + } + } + + db::Shapes tmp_shapes; + + for (std::map >::iterator sps = sel_per_shape.begin (); sps != sel_per_shape.end (); ++sps) { + + db::Shape shape = tmp_shapes.insert (sps->first); + for (std::vector::const_iterator rr = sps->second.begin (); rr != sps->second.end (); ++rr) { + + std::map new_edges; + std::map new_points; + + shape = modify_shape (tv, shape, (*rr)->first, (*rr)->second, move_trans, new_edges, new_points); + + } + + for (std::vector::const_iterator rr = sps->second.begin (); rr != sps->second.end (); ++rr) { + + const lay::ObjectInstPath &sel = (*rr)->first; + + const lay::CellView &cv = view ()->cellview (sel.cv_index ()); + + // compute the transformation into context cell's micron space + double dbu = cv->layout ().dbu (); + db::CplxTrans gt = db::CplxTrans (dbu) * cv.context_trans () * sel.trans (); + + // get one representative global transformation + const std::vector *tv_list = tv.per_cv_and_layer (sel.cv_index (), sel.layer ()); + if (tv_list && ! tv_list->empty ()) { + gt = tv_list->front () * gt; + } + + call_editor_hooks (hooks, &edt::EditorHooks::modified, (*rr)->first, shape, gt); + + } + + } + + // Issue editor hook calls for the instance transformation events + + // sort the selected objects (the instances) by the cell they are in + // The key is a pair: cell_index, cv_index + std::map , std::vector > insts_by_cell; + for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) { + if (r->first.is_cell_inst ()) { + insts_by_cell.insert (std::make_pair (std::make_pair (r->first.cell_index (), r->first.cv_index ()), std::vector ())).first->second.push_back (r); + } + } + + for (std::map , std::vector >::const_iterator ibc = insts_by_cell.begin (); ibc != insts_by_cell.end (); ++ibc) { + + const lay::CellView &cv = view ()->cellview (ibc->first.second); + if (cv.is_valid ()) { + + const std::vector *tv_list = tv.per_cv (ibc->first.second); + db::DCplxTrans tvt; + if (tv_list && ! tv_list->empty ()) { + tvt = tv_list->front (); + } + + for (auto inst = ibc->second.begin (); inst != ibc->second.end (); ++inst) { + + db::CplxTrans gt = tvt * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans () * (*inst)->first.trans (); + db::ICplxTrans applied = gt.inverted () * db::DCplxTrans (move_trans) * gt; + + call_editor_hooks (hooks, &edt::EditorHooks::transformed, (*inst)->first, applied, gt); + + } + + } + + } +} + +db::Shape +PartialService::modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set &sel, const db::DTrans &move_trans, std::map &new_edges, std::map &new_points) +{ + tl_assert (shape_in.shapes () != 0); + db::Shape shape = shape_in; + db::Shapes &shapes = *shape_in.shapes (); + + const lay::CellView &cv = view ()->cellview (path.cv_index ()); + + // use only the first one of the explicit transformations + // TODO: clarify how this can be implemented in a more generic form or leave it thus. + + db::ICplxTrans gt (cv.context_trans () * path.trans ()); + const std::vector *tv_list = tv.per_cv_and_layer (path.cv_index (), path.layer ()); + db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt; + db::Vector move_vector = db::Vector ((tt.inverted () * db::DCplxTrans (move_trans) * tt).disp ()); + + create_shift_sets (shape, sel, new_points, new_edges, move_vector); + + // modify the shapes and insert + + if (shape.is_polygon ()) { + + db::Polygon poly; + shape.polygon (poly); + + // warning: poly is modified: + modify_polygon (poly, new_points, new_edges, true /*compress*/); + + shape = shapes.replace (shape, poly); + + } else if (shape.is_path ()) { + + db::Path path; + shape.path (path); + + // warning: path is modified: + modify_path (path, new_points, new_edges, true /*compress*/); + + shape = shapes.replace (shape, path); + + } else if (shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + + // warning: poly is modified: + modify_polygon (poly, new_points, new_edges, true /*compress*/); + + shape = shapes.replace (shape, poly.box ()); + + } else if (shape.is_text ()) { + + db::Text t; + shape.text (t); + + db::Point tp (shape.text_trans () * db::Point ()); + std::map ::const_iterator np = new_points.find (PointWithIndex (tp, 0, 0)); + + if (np != new_points.end ()) { + t.transform (db::Trans (np->second - tp)); + shape = shapes.replace (shape, t); + } + + } else if (shape.is_point ()) { + + db::Point p; + shape.point (p); + + std::map ::const_iterator np = new_points.find (PointWithIndex (p, 0, 0)); + + if (np != new_points.end ()) { + shape = shapes.replace (shape, np->second); + } + + } + + return shape; +} + +void PartialService::transform_selection (const db::DTrans &move_trans) { // build the transformation variants cache @@ -1444,79 +1653,9 @@ PartialService::transform_selection (const db::DTrans &move_trans) partial_objects::iterator r = *rr; - const lay::CellView &cv = view ()->cellview (r->first.cv_index ()); - - // use only the first one of the explicit transformations - // TODO: clarify how this can be implemented in a more generic form or leave it thus. - - db::ICplxTrans gt (cv.context_trans () * r->first.trans ()); - const std::vector *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ()); - db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt; - db::Vector move_vector = db::Vector ((tt.inverted () * db::DCplxTrans (move_trans) * tt).disp ()); - std::map new_edges; std::map new_points; - create_shift_sets (shape, r->second, new_points, new_edges, move_vector); - - // modify the shapes and insert - - db::Shapes &shapes = cv->layout ().cell (r->first.cell_index ()).shapes (r->first.layer ()); - - if (shape.is_polygon ()) { - - db::Polygon poly; - shape.polygon (poly); - - // warning: poly is modified: - modify_polygon (poly, new_points, new_edges, true /*compress*/); - - shape = shapes.replace (shape, poly); - - } else if (shape.is_path ()) { - - db::Path path; - shape.path (path); - - // warning: path is modified: - modify_path (path, new_points, new_edges, true /*compress*/); - - shape = shapes.replace (shape, path); - - } else if (shape.is_box ()) { - - db::Polygon poly; - shape.polygon (poly); - - // warning: poly is modified: - modify_polygon (poly, new_points, new_edges, true /*compress*/); - - shape = shapes.replace (shape, poly.box ()); - - } else if (shape.is_text ()) { - - db::Text t; - shape.text (t); - - db::Point tp (shape.text_trans () * db::Point ()); - std::map ::const_iterator np = new_points.find (PointWithIndex (tp, 0, 0)); - - if (np != new_points.end ()) { - t.transform (db::Trans (np->second - tp)); - shape = shapes.replace (shape, t); - } - - } else if (shape.is_point ()) { - - db::Point p; - shape.point (p); - - std::map ::const_iterator np = new_points.find (PointWithIndex (p, 0, 0)); - - if (np != new_points.end ()) { - shape = shapes.replace (shape, np->second); - } - - } + shape = modify_shape (tv, shape, r->first, r->second, move_trans, new_edges, new_points); // transform the selection @@ -1625,6 +1764,8 @@ PartialService::edit_cancel () ui ()->ungrab_mouse (this); + close_editor_hooks (false); + selection_to_view (); } @@ -1671,6 +1812,10 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo selection_to_view (); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits); + issue_editor_hook_calls (m_editor_hooks); + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::end_edits); + m_alt_ac = lay::AC_Global; } else if (prio) { @@ -1796,6 +1941,8 @@ PartialService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bo ui ()->grab_mouse (this, true); + open_editor_hooks (); + } m_alt_ac = lay::AC_Global; @@ -1858,6 +2005,8 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo m_dragging = false; selection_to_view (); + close_editor_hooks (true); + m_alt_ac = lay::AC_Global; return true; @@ -1991,6 +2140,8 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo m_current = m_start = p; } + open_editor_hooks (); + } selection_to_view (); @@ -2023,6 +2174,8 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt m_alt_ac = ac_from_buttons (buttons); + close_editor_hooks (false); + // stop dragging ui ()->ungrab_mouse (this); m_dragging = false; @@ -2353,6 +2506,8 @@ PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type a clear_mouse_cursors (); + close_editor_hooks (false); + m_alt_ac = lay::AC_Global; } @@ -2530,6 +2685,8 @@ PartialService::del () m_dragging = false; selection_to_view (); + close_editor_hooks (false); + // clean up the layouts that need to do so. for (std::set::const_iterator l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) { (*l)->cleanup (); diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 645267a30..d39fe7332 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -34,6 +34,7 @@ #include "tlDeferredExecution.h" #include "edtUtils.h" #include "edtConfig.h" +#include "edtEditorHooks.h" #if defined(HAVE_QT) # include @@ -316,6 +317,11 @@ public: */ virtual void edit_cancel (); + /** + * @brief Issues editor hook calls ("modified") for the current selection and the given move transformation + */ + void issue_editor_hook_calls (const tl::weak_collection &hooks); + #if defined(HAVE_QT) public slots: void timeout (); @@ -361,6 +367,8 @@ private: bool m_hover_wait; db::DPoint m_hover_point; + tl::weak_collection m_editor_hooks; + // Deferred method to update the selection tl::DeferredMethod dm_selection_to_view; @@ -392,6 +400,10 @@ private: db::DEdge single_selected_edge () const; bool handle_guiding_shape_changes (); void transform_selection (const db::DTrans &move_trans); + db::Shape modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set &sel, const db::DTrans &move_trans, std::map &new_edges, std::map &new_points); + + void open_editor_hooks (); + void close_editor_hooks (bool commit); }; } diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index cd04df757..0373bdca9 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -147,60 +147,6 @@ public: } } - virtual void begin_modify (lay::LayoutViewBase *view) - { - if (f_begin_modify.can_issue ()) { - f_begin_modify.issue (&edt::EditorHooks::begin_modify, view); - } else { - edt::EditorHooks::begin_modify (view); - } - } - - virtual void begin_modifications () - { - if (f_begin_modifications.can_issue ()) { - f_begin_modifications.issue (&edt::EditorHooks::begin_modifications); - } else { - edt::EditorHooks::begin_modifications (); - } - } - - virtual void modified (const lay::ObjectInstPath &object, const db::CplxTrans &view_trans) - { - if (f_modified.can_issue ()) { - f_modified.issue (&edt::EditorHooks::modified, object, view_trans); - } else { - edt::EditorHooks::modified (object, view_trans); - } - } - - virtual void end_modifications () - { - if (f_end_modifications.can_issue ()) { - f_end_modifications.issue (&edt::EditorHooks::end_modifications); - } else { - edt::EditorHooks::end_modifications (); - } - } - - virtual void commit_modify () - { - if (f_commit_modify.can_issue ()) { - f_commit_modify.issue (&edt::EditorHooks::commit_modify); - } else { - edt::EditorHooks::commit_modify (); - } - } - - virtual void end_modify () - { - if (f_end_modify.can_issue ()) { - f_end_modify.issue (&edt::EditorHooks::end_modify); - } else { - edt::EditorHooks::end_modify (); - } - } - virtual void begin_edit (lay::CellViewRef &cv_ref) { if (f_begin_edit.can_issue ()) { @@ -228,6 +174,15 @@ public: } } + virtual void modified (const lay::ObjectInstPath &object, const db::Shape &shape, const db::CplxTrans &view_trans) + { + if (f_modified.can_issue ()) { + f_modified.issue (&edt::EditorHooks::modified, object, shape, view_trans); + } else { + edt::EditorHooks::modified (object, shape, view_trans); + } + } + virtual void end_edits () { if (f_end_edits.can_issue ()) { @@ -269,16 +224,10 @@ public: gsi::Callback f_commit_instances; gsi::Callback f_end_create_instances; - gsi::Callback f_begin_modify; - gsi::Callback f_begin_modifications; - gsi::Callback f_modified; - gsi::Callback f_end_modifications; - gsi::Callback f_commit_modify; - gsi::Callback f_end_modify; - gsi::Callback f_begin_edit; gsi::Callback f_begin_edits; gsi::Callback f_transformed; + gsi::Callback f_modified; gsi::Callback f_end_edits; gsi::Callback f_commit_edit; gsi::Callback f_end_edit; @@ -350,41 +299,11 @@ gsi::Class decl_EditorHooks ("lay", "EditorHooks", "@brief Instance creation protocol - finish session\n" "See \\begin_create for a description of the protocol." ) + - gsi::callback ("begin_modify", &EditorHooksImpl::begin_modify, &EditorHooksImpl::f_begin_modify, gsi::arg ("view"), - "@brief Modification protocol - begin session\n" - "This method is called to initiate an object modification session. The session is ended with " - "\\end_modify. Between these calls, modified objects are announced with \\begin_modifications, " - "\\modified and \\end_modifications calls. These calls are repeated to indicate changes in the objects " - "modified while moving the mouse for example.\n" - "\n" - "\\commit_modify is called once before \\end_modify to indicate that the last set of " - "objects are committed to the database." - ) + - gsi::callback ("begin_modifications", &EditorHooksImpl::begin_modifications, &EditorHooksImpl::f_begin_modifications, - "@brief Modification protocol - begin modifications\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::callback ("modified", &EditorHooksImpl::modified, &EditorHooksImpl::f_modified, gsi::arg ("object"), gsi::arg ("view_trans"), - "@brief Modification protocol - indicate a modified object\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::callback ("end_modifications", &EditorHooksImpl::end_modifications, &EditorHooksImpl::f_end_modifications, - "@brief Modification protocol - finish list of modifications\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::callback ("commit_modify", &EditorHooksImpl::commit_modify, &EditorHooksImpl::f_commit_modify, - "@brief Modification protocol - commit new objects\n" - "See \\begin_modify for a description of the protocol." - ) + - gsi::callback ("end_modify", &EditorHooksImpl::end_modify, &EditorHooksImpl::f_end_modify, - "@brief Modification protocol - finish session\n" - "See \\begin_modify for a description of the protocol." - ) + gsi::callback ("begin_edit", &EditorHooksImpl::begin_edit, &EditorHooksImpl::f_begin_edit, gsi::arg ("cellview"), "@brief Editing protocol - begin session\n" "This method is called to initiate an object editing session. The session is ended with " "\\end_edit. Between these calls, edits are announced with \\begin_edits, " - "\\transformed and \\end_edits calls. These calls are repeated to indicate changes in the objects " + "\\transformed or \\modified and \\end_edits calls. These calls are repeated to indicate changes in the objects " "modified while moving the mouse for example.\n" "\n" "\\commit_edit is called once before \\end_edit to indicate that the last set of " @@ -401,6 +320,19 @@ gsi::Class decl_EditorHooks ("lay", "EditorHooks", "@param object A path to the modified object\n" "@param applied_trans The DBU-space of the transformation applied to the object\n" "@param view_trans The combined transformation of DBU space to view space\n" + "\n" + "Note that 'object' is the original, unmodified objects to which 'applied_trans' will be applied upon commit." + ) + + gsi::callback ("modified", &EditorHooksImpl::modified, &EditorHooksImpl::f_modified, gsi::arg ("object"), gsi::arg ("shape"), gsi::arg ("view_trans"), + "@brief Modification protocol - indicate a modified object\n" + "See \\begin_edit for a description of the protocol." + "\n" + "@param object A path to the modified object\n" + "@param shape The new, modified shape\n" + "@param view_trans The combined transformation of DBU space to view space\n" + "\n" + "Note that 'object' is the original, unmodified objects while 'shape' is the modified shape. This shape object is a synthetic reference " + "and does not exist in the database yet.\n" ) + gsi::callback ("end_edits", &EditorHooksImpl::end_edits, &EditorHooksImpl::f_end_edits, "@brief Editing protocol - finish list of edits\n" From 2dc9df3f40937f81bf91a8ee2044d5f0c0eb4b65 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2024 22:23:23 +0200 Subject: [PATCH 16/17] Updated documentation with example --- src/edt/edt/gsiDeclEdtEditorHooks.cc | 107 ++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 0373bdca9..589f7f20a 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -389,7 +389,112 @@ gsi::Class decl_EditorHooks ("lay", "EditorHooks", "This class provides the basic interface. To implement callbacks, use the \\EditorHooks class. " "You should not need to instantiate this class.\n" "\n" - // @@@ + "The following is an excample for editor hooks that add DRC space indicators for polygon-alike shapes:\n" + "\n" + "@code\n" + "class MyEditorHooks(pya.EditorHooks):\n" + "\n" + " def __init__(self):\n" + " \n" + " self.register(\"editor_hooks_demo\")\n" + " \n" + " self.markers = []\n" + " self.view = None\n" + " self.layout = None\n" + " self.space = 0.0\n" + " \n" + " self.spaces = {\n" + " pya.LayerInfo(1, 0): ( 0.2, pya.Region.Euclidian ),\n" + " pya.LayerInfo(2, 0): ( 0.5, pya.Region.Projection )\n" + " }\n" + " \n" + " self.instance_space = 0.4\n" + "\n" + " # Utilities\n" + " \n" + " def set_space_from_layer(self, layer_index):\n" + " # pick the space value or None\n" + " lp = self.layout.get_info(layer_index)\n" + " if lp in self.spaces:\n" + " (s, m) = self.spaces[lp]\n" + " self.space = s / self.layout.dbu\n" + " self.metrics = m\n" + " else:\n" + " self.space = None\n" + " \n" + " def add_marker_from_shape(self, shape, trans):\n" + " if self.space is None:\n" + " return\n" + " p = shape.polygon\n" + " if p is not None and p.num_points() < 100:\n" + " r = pya.Region()\n" + " # maintain 2-point polygons for the first edge drawn\n" + " r.merged_semantics = (p.num_points() != 2)\n" + " r.insert(p)\n" + " r = r.drc_hull(self.metrics, self.space)\n" + " for pp in r.each():\n" + " m = pya.Marker(self.view)\n" + " m.line_style = 2\n" + " m.vertex_size = 0\n" + " m.set_polygon(trans * pp)\n" + " self.markers.append(m)\n" + " \n" + " # Shape creation protocol\n" + " \n" + " def begin_create_shapes(self, cv, layer):\n" + " # setup session\n" + " self.view = cv.view()\n" + " self.layout = cv.layout()\n" + " self.markers = []\n" + " # pick the space value\n" + " self.set_space_from_layer(layer.layer_index())\n" + "\n" + " def begin_new_shapes(self):\n" + " # create new markers\n" + " self.markers = []\n" + "\n" + " def create_shape(self, shape, trans):\n" + " # create a marker with space halo\n" + " self.add_marker_from_shape(shape, trans)\n" + " \n" + " def end_create_shapes(self):\n" + " # cleanup\n" + " self.markers = []\n" + " self.view = None\n" + " self.layout = None\n" + " \n" + " # Modification protocol\n" + " \n" + " def begin_edit(self, cv):\n" + " # setup session\n" + " self.view = cv.view()\n" + " self.layout = cv.layout()\n" + " self.markers = []\n" + "\n" + " def begin_edits(self):\n" + " # create new markers\n" + " self.markers = []\n" + "\n" + " def transformed(self, path, applied, trans):\n" + " # transformation of a shape or instance\n" + " if path.shape is not None:\n" + " self.set_space_from_layer(path.layer)\n" + " self.add_marker_from_shape(path.shape, trans * applied)\n" + "\n" + " def modified(self, path, shape, trans):\n" + " # modification of a shape\n" + " self.set_space_from_layer(path.layer)\n" + " self.add_marker_from_shape(shape, trans)\n" + "\n" + " def end_edit(self):\n" + " # cleanup\n" + " self.markers = []\n" + " self.view = None\n" + " self.layout = None\n" + "\n" + "# instantiation of the hooks object\n" + "MyEditorHooks()\n" + "@/code\n" "\n" "The EditorHooks class has been introduced in version 0.29." ); From 7f16c7b59796190a60adfd493ff11fdc1c35aad8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 1 Apr 2024 20:21:55 +0200 Subject: [PATCH 17/17] Fixed doc --- src/db/db/gsiDeclDbRegion.cc | 2 +- src/db/db/gsiDeclDbShape.cc | 4 +- src/edt/edt/gsiDeclEdtEditorHooks.cc | 191 ++++++++++++++++----------- 3 files changed, 115 insertions(+), 82 deletions(-) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f6380c72f..013c43154 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2796,7 +2796,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "@return The new polygons representing the forbidden region.\n" "\n" - "This method has been introduced in version 0.29.\n" + "This method has been introduced in version 0.29.1.\n" ) + method_ext ("move", &move_p, gsi::arg ("v"), "@brief Moves the region\n" diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index 6d03cf26c..708a52f5e 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -2137,14 +2137,14 @@ Class decl_Shape ("db", "Shape", "The less operator implementation is based on pointers and not strictly reproducible." "However, it is good enough so Shape objects can serve as keys in hashes (see also \\hash).\n" "\n" - "This method has been introduced in version 0.29." + "This method has been introduced in version 0.29.1." ) + gsi::method ("hash", &db::Shape::hash_value, "@brief Hash function\n" "\n" "The hash function enables Shape objects as keys in hashes.\n" "\n" - "This method has been introduced in version 0.29." + "This method has been introduced in version 0.29.1." ) + gsi::method ("to_s", &db::Shape::to_string, "@brief Create a string showing the contents of the reference\n" diff --git a/src/edt/edt/gsiDeclEdtEditorHooks.cc b/src/edt/edt/gsiDeclEdtEditorHooks.cc index 589f7f20a..009d19f7c 100644 --- a/src/edt/edt/gsiDeclEdtEditorHooks.cc +++ b/src/edt/edt/gsiDeclEdtEditorHooks.cc @@ -389,114 +389,147 @@ gsi::Class decl_EditorHooks ("lay", "EditorHooks", "This class provides the basic interface. To implement callbacks, use the \\EditorHooks class. " "You should not need to instantiate this class.\n" "\n" - "The following is an excample for editor hooks that add DRC space indicators for polygon-alike shapes:\n" + "The following is an excample for editor hooks that add DRC space indicators for polygon-alike shapes,\n" + "It implements the shape creation protocol to capture new shapes and the modification protocol to " + "capture shape manipulations. It displays a halo following hard-coded DRC rules to indicate the " + "forbidden zones around the shapes:\n" "\n" "@code\n" - "class MyEditorHooks(pya.EditorHooks):\n" + "class MyEditorHooks < RBA::EditorHooks\n" "\n" - " def __init__(self):\n" + " def initialize()\n" " \n" - " self.register(\"editor_hooks_demo\")\n" - " \n" - " self.markers = []\n" - " self.view = None\n" - " self.layout = None\n" - " self.space = 0.0\n" - " \n" - " self.spaces = {\n" - " pya.LayerInfo(1, 0): ( 0.2, pya.Region.Euclidian ),\n" - " pya.LayerInfo(2, 0): ( 0.5, pya.Region.Projection )\n" + " register(\"editor_hooks_demo\")\n" + "\n" + " cleanup \n" + "\n" + " # some demo values \n" + " @spaces = {\n" + " RBA::LayerInfo::new(1, 0) => [ 0.2, RBA::Region::Euclidian ],\n" + " RBA::LayerInfo::new(2, 0) => [ 0.5, RBA::Region::Projection ]\n" " }\n" " \n" - " self.instance_space = 0.4\n" + " end\n" "\n" " # Utilities\n" " \n" - " def set_space_from_layer(self, layer_index):\n" - " # pick the space value or None\n" - " lp = self.layout.get_info(layer_index)\n" - " if lp in self.spaces:\n" - " (s, m) = self.spaces[lp]\n" - " self.space = s / self.layout.dbu\n" - " self.metrics = m\n" - " else:\n" - " self.space = None\n" + " # pick the space value from layer or set to nil\n" + " def set_space_from_layer(layer_index)\n" + " lp = @layout.get_info(layer_index)\n" + " if @spaces[lp]\n" + " (s, m) = @spaces[lp]\n" + " @space = s / @layout.dbu\n" + " @metrics = m\n" + " else\n" + " @space = nil\n" + " end\n" + " end\n" " \n" - " def add_marker_from_shape(self, shape, trans):\n" - " if self.space is None:\n" - " return\n" + " def add_marker_from_shape(shape, trans)\n" + " \n" + " if !@space\n" + " return # no space value set\n" + " end\n" + " \n" " p = shape.polygon\n" - " if p is not None and p.num_points() < 100:\n" - " r = pya.Region()\n" - " # maintain 2-point polygons for the first edge drawn\n" - " r.merged_semantics = (p.num_points() != 2)\n" - " r.insert(p)\n" - " r = r.drc_hull(self.metrics, self.space)\n" - " for pp in r.each():\n" - " m = pya.Marker(self.view)\n" - " m.line_style = 2\n" - " m.vertex_size = 0\n" - " m.set_polygon(trans * pp)\n" - " self.markers.append(m)\n" + " if !p\n" + " return # not a polygon-like object\n" + " end\n" + " \n" + " r = RBA::Region::new\n" + " # maintain 2-point polygons for the first edge drawn\n" + " r.merged_semantics = (p.num_points != 2)\n" + " r.insert(p)\n" + " \n" + " # compute DRC hull and prepare markers\n" + " r.drc_hull(@metrics, @space).each do |pp|\n" + " m = RBA::Marker::new(@view)\n" + " m.line_style = 2\n" + " m.vertex_size = 0\n" + " m.set_polygon(trans * pp)\n" + " @markers.append(m)\n" + " end\n" + " \n" + " end\n" + " \n" + " # setup session\n" + " def start(cv)\n" + " cleanup\n" + " @view = cv.view\n" + " @layout = cv.layout\n" + " end\n" + " \n" + " # end session\n" + " def cleanup\n" + " @space = nil\n" + " @view = nil\n" + " @layout = nil\n" + " clear_markers\n" + " end\n" + " \n" + " def clear_markers\n" + " @markers && @markers.each do |m|\n" + " # this is how a marker gets removed in Ruby:\n" + " m._destroy\n" + " end\n" + " @markers = []\n" + " end\n" " \n" " # Shape creation protocol\n" " \n" - " def begin_create_shapes(self, cv, layer):\n" - " # setup session\n" - " self.view = cv.view()\n" - " self.layout = cv.layout()\n" - " self.markers = []\n" - " # pick the space value\n" - " self.set_space_from_layer(layer.layer_index())\n" + " def begin_create_shapes(cv, layer)\n" + " start(cv)\n" + " set_space_from_layer(layer.layer_index)\n" + " end\n" "\n" - " def begin_new_shapes(self):\n" - " # create new markers\n" - " self.markers = []\n" + " def begin_new_shapes\n" + " clear_markers\n" + " end\n" "\n" - " def create_shape(self, shape, trans):\n" - " # create a marker with space halo\n" - " self.add_marker_from_shape(shape, trans)\n" + " def create_shape(shape, trans)\n" + " add_marker_from_shape(shape, trans)\n" + " end\n" " \n" - " def end_create_shapes(self):\n" - " # cleanup\n" - " self.markers = []\n" - " self.view = None\n" - " self.layout = None\n" + " def end_create_shapes\n" + " cleanup\n" + " end\n" " \n" " # Modification protocol\n" " \n" - " def begin_edit(self, cv):\n" - " # setup session\n" - " self.view = cv.view()\n" - " self.layout = cv.layout()\n" - " self.markers = []\n" + " def begin_edit(cv)\n" + " start(cv)\n" + " end\n" "\n" - " def begin_edits(self):\n" + " def begin_edits\n" " # create new markers\n" - " self.markers = []\n" + " clear_markers\n" + " end\n" "\n" - " def transformed(self, path, applied, trans):\n" - " # transformation of a shape or instance\n" - " if path.shape is not None:\n" - " self.set_space_from_layer(path.layer)\n" - " self.add_marker_from_shape(path.shape, trans * applied)\n" + " # transformation of a shape or instance\n" + " def transformed(path, applied, trans)\n" + " if path.shape\n" + " set_space_from_layer(path.layer)\n" + " add_marker_from_shape(path.shape, trans * applied)\n" + " end\n" + " end\n" "\n" - " def modified(self, path, shape, trans):\n" - " # modification of a shape\n" - " self.set_space_from_layer(path.layer)\n" - " self.add_marker_from_shape(shape, trans)\n" + " # modification of a shape\n" + " def modified(path, shape, trans)\n" + " set_space_from_layer(path.layer)\n" + " add_marker_from_shape(shape, trans)\n" + " end\n" "\n" - " def end_edit(self):\n" - " # cleanup\n" - " self.markers = []\n" - " self.view = None\n" - " self.layout = None\n" + " def end_edit\n" + " cleanup\n" + " end\n" + " \n" + "end\n" "\n" "# instantiation of the hooks object\n" - "MyEditorHooks()\n" + "MyEditorHooks::new\n" "@/code\n" "\n" - "The EditorHooks class has been introduced in version 0.29." + "The EditorHooks class has been introduced in version 0.29.1." ); }