Implemented editor hooks for partial mode too. Integrated modification events into edit protocol

This commit is contained in:
Matthias Koefferlein 2024-03-31 22:10:24 +02:00
parent 1c284b0939
commit 4809c06091
5 changed files with 282 additions and 185 deletions

View File

@ -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 () { }

View File

@ -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<db::DCplxTrans> *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<const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (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<const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (m_editor_hooks, &edt::EditorHooks::transformed, *r, applied, gt);
}
}
// make the Partial Edit Service issue "modify" events
std::vector<edt::PartialService *> partial_services = view ()->get_plugins<edt::PartialService> ();
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);
}

View File

@ -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<lay::CellViewRef &> (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<edt::EditorHooks> &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 <db::Shape, std::vector<partial_objects::iterator> > 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<db::DCplxTrans> *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<partial_objects::iterator> ())).first->second.push_back (r);
}
}
}
db::Shapes tmp_shapes;
for (std::map <db::Shape, std::vector<partial_objects::iterator> >::iterator sps = sel_per_shape.begin (); sps != sel_per_shape.end (); ++sps) {
db::Shape shape = tmp_shapes.insert (sps->first);
for (std::vector<partial_objects::iterator>::const_iterator rr = sps->second.begin (); rr != sps->second.end (); ++rr) {
std::map <EdgeWithIndex, db::Edge> new_edges;
std::map <PointWithIndex, db::Point> new_points;
shape = modify_shape (tv, shape, (*rr)->first, (*rr)->second, move_trans, new_edges, new_points);
}
for (std::vector<partial_objects::iterator>::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<db::DCplxTrans> *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<const lay::ObjectInstPath &, const db::Shape &, const db::CplxTrans &> (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::pair <db::cell_index_type, unsigned int>, std::vector <partial_objects::const_iterator> > 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 <partial_objects::const_iterator> ())).first->second.push_back (r);
}
}
for (std::map <std::pair <db::cell_index_type, unsigned int>, std::vector <partial_objects::const_iterator> >::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<db::DCplxTrans> *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<const lay::ObjectInstPath &, const db::ICplxTrans &, const db::CplxTrans &> (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 <EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &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<db::DCplxTrans> *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 <PointWithIndex, db::Point>::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 <PointWithIndex, db::Point>::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<db::DCplxTrans> *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 <EdgeWithIndex, db::Edge> new_edges;
std::map <PointWithIndex, db::Point> 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 <PointWithIndex, db::Point>::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 <PointWithIndex, db::Point>::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<db::Layout *>::const_iterator l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) {
(*l)->cleanup ();

View File

@ -34,6 +34,7 @@
#include "tlDeferredExecution.h"
#include "edtUtils.h"
#include "edtConfig.h"
#include "edtEditorHooks.h"
#if defined(HAVE_QT)
# include <QObject>
@ -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<edt::EditorHooks> &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<edt::EditorHooks> m_editor_hooks;
// Deferred method to update the selection
tl::DeferredMethod<edt::PartialService> 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<EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points);
void open_editor_hooks ();
void close_editor_hooks (bool commit);
};
}

View File

@ -147,60 +147,6 @@ public:
}
}
virtual void begin_modify (lay::LayoutViewBase *view)
{
if (f_begin_modify.can_issue ()) {
f_begin_modify.issue<edt::EditorHooks, lay::LayoutViewBase *> (&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> (&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, const lay::ObjectInstPath &, const db::CplxTrans &> (&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> (&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> (&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> (&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, const lay::ObjectInstPath &, const db::Shape &, const db::CplxTrans &> (&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<EditorHooksImpl> 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<EditorHooksImpl> 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"