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 ());
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
clear_selection ();
@ -1516,28 +1516,70 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
}
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 ()) {
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 ();
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) {
if (m_move_mode == MoveSelected) {
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) {
(*r)->transform_by (db::DCplxTrans (m_trans));
}
@ -1553,90 +1595,66 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
return;
}
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
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 ();
} 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 ();
} 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_p2 (m_seg_index, db::DPoint (p12.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} 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_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x(), p21.y ()));
m_rulers [0]->redraw ();
} 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_rulers [0]->redraw ();
} 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_rulers [0]->redraw ();
} 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_rulers [0]->redraw ();
} 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_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) {
db::DVector dp = p - m_p1;
// round the drag distance to grid if required: this is the least we can do in this case
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);
dp = lay::snap_angle (dp, ac_eff);
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) {
(*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) {
show_message ();
}
}
void
@ -1703,6 +1720,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type)
// termine the operation
m_move_mode = MoveNone;
clear_mouse_cursors ();
}
void
@ -2040,7 +2058,7 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
// otherwise we risk manipulating p1 too.
ant::Object::point_list pts = m_current.points ();
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);
@ -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);
}
std::pair <bool, db::DPoint>
Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
db::DPoint
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);
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)
* MoveP1Y - dragging P1.y (if box-like)
* MoveP2Y - dragging P2.y (if box-like)
* MoveRuler - dragging a whole ruler (one)
* 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);
@ -601,7 +600,7 @@ private:
std::pair<bool, db::DPoint> snap1 (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::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 void deactivated ();
void snap_rulers (lay::angle_constraint_type ac);
/**
* @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"
"\n"
"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, "
"make sure you call \\clear_mouse_cursors before to remove existing cursors.\n"
"provide a visualization of the edges or vertexes that the point is snapping to. \\clear_mouse_cursors will "
"be called before.\n"
"\n"
"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"
"\n"
"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, "
"make sure you call \\clear_mouse_cursors before to remove existing cursors.\n"
"provide a visualization of the edges or vertexes that the point is snapping to. \\clear_mouse_cursors will "
"be called before.\n"
"\n"
"This method has been added in version 0.30.4."
) +