From 4809c0609186189c777156fa018331295eacd0c2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2024 22:10:24 +0200 Subject: [PATCH] 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"