Improving ruler snapping (after move_transform, snap to objects, visual snap details hint ...)

This commit is contained in:
Matthias Koefferlein 2025-08-30 23:20:23 +02:00
parent 53c173d01e
commit 43454962d4
3 changed files with 88 additions and 64 deletions

View File

@ -1431,7 +1431,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
const ant::Object *robj = dynamic_cast <const ant::Object *> ((*ri).ptr ()); const ant::Object *robj = dynamic_cast <const ant::Object *> ((*ri).ptr ());
if (robj && (! robj_min || robj == robj_min)) { if (robj && (! robj_min || robj == robj_min)) {
if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index) && m_move_mode != MoveRuler) { if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index)) {
// found anything: make the moved ruler the selection // found anything: make the moved ruler the selection
clear_selection (); clear_selection ();
@ -1516,28 +1516,70 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
} }
void void
Service::move_transform (const db::DPoint &p, db::DFTrans tr, lay::angle_constraint_type /*ac*/) Service::snap_rulers (lay::angle_constraint_type ac)
{
if (m_rulers.empty ()) {
return;
}
lay::PointSnapToObjectResult min_snp;
double min_dist = -1.0;
db::DVector min_delta;
for (auto r = m_rulers.begin (); r != m_rulers.end (); ++r) {
const ant::Object *ruler = (*r)->ruler ();
db::DPoint p1 = m_trans * ruler->p1 ();
db::DPoint p2 = m_trans * ruler->p2 ();
auto tr = db::DTrans ((m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1);
db::DPoint org1 = tr * ruler->p1 ();
db::DPoint org2 = tr * ruler->p2 ();
auto snp = snap2_details (org1, p1, ruler, ac);
double dist = p1.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p1;
}
snp = snap2_details (org2, p2, ruler, ac);
dist = p2.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p2;
}
}
if (min_snp.object_snap != lay::PointSnapToObjectResult::NoObject) {
mouse_cursor_from_snap_details (min_snp);
}
m_trans = db::DTrans (min_delta) * m_trans;
}
void
Service::move_transform (const db::DPoint & /*p*/, db::DFTrans tr, lay::angle_constraint_type ac)
{ {
if (m_rulers.empty () || m_selected.empty ()) { if (m_rulers.empty () || m_selected.empty ()) {
return; return;
} }
if (m_move_mode == MoveRuler) { auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
db::DVector dp = p - db::DPoint (); if (m_move_mode == MoveSelected) {
m_original.transform (db::DTrans (m_p1 - db::DPoint ()) * db::DTrans (tr) * db::DTrans (db::DPoint () - m_p1));
m_current.transform (db::DTrans (dp) * db::DTrans (tr) * db::DTrans (-dp));
// display current rulers' parameters
show_message ();
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveSelected) {
m_trans *= db::DTrans (m_p1 - db::DPoint ()) * db::DTrans (tr) * db::DTrans (db::DPoint () - m_p1); m_trans *= db::DTrans (m_p1 - db::DPoint ()) * db::DTrans (tr) * db::DTrans (db::DPoint () - m_p1);
snap_rulers (ac_eff);
for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) { for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) {
(*r)->transform_by (db::DCplxTrans (m_trans)); (*r)->transform_by (db::DCplxTrans (m_trans));
} }
@ -1553,90 +1595,66 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
return; return;
} }
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
if (m_move_mode == MoveP1) { if (m_move_mode == MoveP1) {
m_current.seg_p1 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second); m_current.seg_p1 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2) { } else if (m_move_mode == MoveP2) {
m_current.seg_p2 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second); m_current.seg_p2 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP12) { } else if (m_move_mode == MoveP12) {
db::DPoint p12 = snap2 (m_p1, p, &m_current, ac).second; db::DPoint p12 = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x(), p12.y ())); m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x(), p12.y ()));
m_current.seg_p2 (m_seg_index, db::DPoint (p12.x (), m_current.seg_p2 (m_seg_index).y ())); m_current.seg_p2 (m_seg_index, db::DPoint (p12.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP21) { } else if (m_move_mode == MoveP21) {
db::DPoint p21 = snap2 (m_p1, p, &m_current, ac).second; db::DPoint p21 = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (p21.x (), m_current.seg_p1 (m_seg_index).y ())); m_current.seg_p1 (m_seg_index, db::DPoint (p21.x (), m_current.seg_p1 (m_seg_index).y ()));
m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x(), p21.y ())); m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x(), p21.y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1X) { } else if (m_move_mode == MoveP1X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second; db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p1 (m_seg_index).y ())); m_current.seg_p1 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p1 (m_seg_index).y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2X) { } else if (m_move_mode == MoveP2X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second; db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p2 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p2 (m_seg_index).y ())); m_current.seg_p2 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1Y) { } else if (m_move_mode == MoveP1Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second; db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x (), pc.y ())); m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2Y) { } else if (m_move_mode == MoveP2Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second; db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x (), pc.y ())); m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw (); m_rulers [0]->redraw ();
} else if (m_move_mode == MoveRuler) {
// try two ways of snapping
db::DVector dp = lay::snap_angle (p - m_p1, ac == lay::AC_Global ? m_snap_mode : ac);
db::DPoint p1 = m_original.p1 () + dp;
db::DPoint p2 = m_original.p2 () + dp;
std::pair<bool, db::DPoint> r1 = snap1 (p1, m_obj_snap && m_original.snap ());
db::DPoint q1 = r1.second;
std::pair<bool, db::DPoint> r2 = snap1 (p2, m_obj_snap && m_original.snap ());
db::DPoint q2 = r2.second;
if ((!r2.first && r1.first) || ((r1.first || (!r1.first && !r2.first)) && q1.distance (p1) < q2.distance (p2))) {
q2 = q1 + (m_original.p2 () - m_original.p1 ());
} else {
q1 = q2 + (m_original.p1 () - m_original.p2 ());
}
m_current.p1 (q1);
m_current.p2 (q2);
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveSelected) { } else if (m_move_mode == MoveSelected) {
db::DVector dp = p - m_p1; db::DVector dp = p - m_p1;
// round the drag distance to grid if required: this is the least we can do in this case dp = lay::snap_angle (dp, ac_eff);
if (m_grid_snap) {
dp = db::DVector (lay::snap (dp.x (), m_grid), lay::snap (dp.y (), m_grid));
}
dp = lay::snap_angle (dp, ac == lay::AC_Global ? m_snap_mode : ac);
m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1); m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1);
snap_rulers (ac_eff);
for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) { for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) {
(*r)->transform_by (db::DCplxTrans (m_trans)); (*r)->transform_by (db::DCplxTrans (m_trans));
} }
@ -1646,7 +1664,6 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
if (m_move_mode != MoveSelected) { if (m_move_mode != MoveSelected) {
show_message (); show_message ();
} }
} }
void void
@ -1703,6 +1720,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type)
// termine the operation // termine the operation
m_move_mode = MoveNone; m_move_mode = MoveNone;
clear_mouse_cursors ();
} }
void void
@ -2040,7 +2058,7 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
// otherwise we risk manipulating p1 too. // otherwise we risk manipulating p1 too.
ant::Object::point_list pts = m_current.points (); ant::Object::point_list pts = m_current.points ();
if (! pts.empty ()) { if (! pts.empty ()) {
pts.back () = snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second; pts.back () = snap_details.snapped_point;
} }
m_current.set_points_exact (pts); m_current.set_points_exact (pts);
@ -2095,11 +2113,16 @@ Service::snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::O
return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range);
} }
std::pair <bool, db::DPoint> db::DPoint
Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) Service::snap2_visual (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
{ {
lay::PointSnapToObjectResult res = snap2_details (p1, p2, obj, ac); lay::PointSnapToObjectResult res = snap2_details (p1, p2, obj, ac);
return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point);
if (res.object_snap != lay::PointSnapToObjectResult::NoObject) {
mouse_cursor_from_snap_details (res);
}
return res.snapped_point;
} }

View File

@ -208,10 +208,9 @@ public:
* MoveP2X - dragging P2.x (if box-like) * MoveP2X - dragging P2.x (if box-like)
* MoveP1Y - dragging P1.y (if box-like) * MoveP1Y - dragging P1.y (if box-like)
* MoveP2Y - dragging P2.y (if box-like) * MoveP2Y - dragging P2.y (if box-like)
* MoveRuler - dragging a whole ruler (one)
* MoveSelection - dragging a whole ruler (many) * MoveSelection - dragging a whole ruler (many)
*/ */
enum MoveMode { MoveNone, MoveP1, MoveP2, MoveP12, MoveP21, MoveP1X, MoveP2X, MoveP1Y, MoveP2Y, MoveRuler, MoveSelected }; enum MoveMode { MoveNone, MoveP1, MoveP2, MoveP12, MoveP21, MoveP1X, MoveP2X, MoveP1Y, MoveP2Y, MoveSelected };
Service (db::Manager *manager, lay::LayoutViewBase *view); Service (db::Manager *manager, lay::LayoutViewBase *view);
@ -601,7 +600,7 @@ private:
std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap); std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap);
lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap); lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap);
std::pair<bool, db::DPoint> snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); db::DPoint snap2_visual (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl); lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl);
@ -620,6 +619,8 @@ private:
virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual void deactivated (); virtual void deactivated ();
void snap_rulers (lay::angle_constraint_type ac);
/** /**
* @brief Select a certain ruler * @brief Select a certain ruler
* *

View File

@ -1584,8 +1584,8 @@ Class<gsi::PluginBase> decl_Plugin ("lay", "Plugin",
"angle constraint. Only grid constraints and snapping to objects is supported.\n" "angle constraint. Only grid constraints and snapping to objects is supported.\n"
"\n" "\n"
"If \"visualize\" is true, the function will generate calls to \\add_mouse_cursor or \\add_edge_marker to " "If \"visualize\" is true, the function will generate calls to \\add_mouse_cursor or \\add_edge_marker to "
"provide a visualization of the edges or vertexes that the point is snapping to. If you use this feature, " "provide a visualization of the edges or vertexes that the point is snapping to. \\clear_mouse_cursors will "
"make sure you call \\clear_mouse_cursors before to remove existing cursors.\n" "be called before.\n"
"\n" "\n"
"This method has been added in version 0.30.4." "This method has been added in version 0.30.4."
) + ) +
@ -1609,8 +1609,8 @@ Class<gsi::PluginBase> decl_Plugin ("lay", "Plugin",
"will snap to another object. The behavior is given by the respective configuration.\n" "will snap to another object. The behavior is given by the respective configuration.\n"
"\n" "\n"
"If \"visualize\" is true, the function will generate calls to \\add_mouse_cursor or \\add_edge_marker to " "If \"visualize\" is true, the function will generate calls to \\add_mouse_cursor or \\add_edge_marker to "
"provide a visualization of the edges or vertexes that the point is snapping to. If you use this feature, " "provide a visualization of the edges or vertexes that the point is snapping to. \\clear_mouse_cursors will "
"make sure you call \\clear_mouse_cursors before to remove existing cursors.\n" "be called before.\n"
"\n" "\n"
"This method has been added in version 0.30.4." "This method has been added in version 0.30.4."
) + ) +