Implementing 'diagonal only' for rulers

This commit is contained in:
Matthias Koefferlein 2026-01-14 02:37:26 +01:00
parent cca73a8ebb
commit 6666b2b68c
14 changed files with 134 additions and 147 deletions

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>665</width>
<height>103</height>
<height>108</height>
</rect>
</property>
<property name="windowTitle">
@ -56,6 +56,13 @@
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="QRadioButton" name="ruler_diag_rb">
<property name="text">
<string>Diagonal</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="ruler_hor_rb">
<property name="text">
@ -77,17 +84,17 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="ruler_diag_rb">
<item row="0" column="3">
<widget class="QRadioButton" name="ruler_vert_rb">
<property name="text">
<string>Diagonal</string>
<string>Vertical only</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QRadioButton" name="ruler_vert_rb">
<widget class="QRadioButton" name="ruler_diag_only_rb">
<property name="text">
<string>Vertical only</string>
<string>Diagonal only</string>
</property>
</widget>
</item>
@ -102,7 +109,6 @@
<tabstop>ruler_ortho_rb</tabstop>
<tabstop>ruler_diag_rb</tabstop>
<tabstop>ruler_hor_rb</tabstop>
<tabstop>ruler_vert_rb</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@ -644,6 +644,11 @@
<string>Orthogonal</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal only</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontal only</string>

View File

@ -29,47 +29,6 @@ namespace ant
// ------------------------------------------------------------
// Helper functions to get and set the configuration
std::string
ACConverter::to_string (const lay::angle_constraint_type &m)
{
if (m == lay::AC_Any) {
return "any";
} else if (m == lay::AC_Diagonal) {
return "diagonal";
} else if (m == lay::AC_Ortho) {
return "ortho";
} else if (m == lay::AC_Horizontal) {
return "horizontal";
} else if (m == lay::AC_Vertical) {
return "vertical";
} else if (m == lay::AC_Global) {
return "global";
} else {
return "";
}
}
void
ACConverter::from_string (const std::string &tt, lay::angle_constraint_type &m)
{
std::string t (tl::trim (tt));
if (t == "any") {
m = lay::AC_Any;
} else if (t == "diagonal") {
m = lay::AC_Diagonal;
} else if (t == "ortho") {
m = lay::AC_Ortho;
} else if (t == "horizontal") {
m = lay::AC_Horizontal;
} else if (t == "vertical") {
m = lay::AC_Vertical;
} else if (t == "global") {
m = lay::AC_Global;
} else {
m = lay::AC_Any;
}
}
std::string
StyleConverter::to_string (ant::Object::style_type s)
{

View File

@ -51,12 +51,6 @@ extern ANT_PUBLIC const std::string cfg_current_ruler_template;
// ------------------------------------------------------------
// Helper functions to get and set the configuration
struct ACConverter
{
std::string to_string (const lay::angle_constraint_type &m);
void from_string (const std::string &s, lay::angle_constraint_type &m);
};
struct StyleConverter
{
std::string to_string (ant::Object::style_type s);

View File

@ -159,10 +159,11 @@ ConfigPage3::setup (lay::Dispatcher *root)
{
// snap mode
lay::angle_constraint_type rm = lay::AC_Any;
root->config_get (cfg_ruler_snap_mode, rm, ACConverter ());
root->config_get (cfg_ruler_snap_mode, rm, lay::ACConverter ());
mp_ui->ruler_any_angle_rb->setChecked (rm == lay::AC_Any);
mp_ui->ruler_ortho_rb->setChecked (rm == lay::AC_Ortho);
mp_ui->ruler_diag_rb->setChecked (rm == lay::AC_Diagonal);
mp_ui->ruler_diag_only_rb->setChecked (rm == lay::AC_DiagonalOnly);
mp_ui->ruler_hor_rb->setChecked (rm == lay::AC_Horizontal);
mp_ui->ruler_vert_rb->setChecked (rm == lay::AC_Vertical);
}
@ -180,13 +181,16 @@ ConfigPage3::commit (lay::Dispatcher *root)
if (mp_ui->ruler_diag_rb->isChecked ()) {
rm = lay::AC_Diagonal;
}
if (mp_ui->ruler_diag_only_rb->isChecked ()) {
rm = lay::AC_DiagonalOnly;
}
if (mp_ui->ruler_hor_rb->isChecked ()) {
rm = lay::AC_Horizontal;
}
if (mp_ui->ruler_vert_rb->isChecked ()) {
rm = lay::AC_Vertical;
}
root->config_set (cfg_ruler_snap_mode, rm, ACConverter ());
root->config_set (cfg_ruler_snap_mode, rm, lay::ACConverter ());
}
// ------------------------------------------------------------

View File

@ -24,6 +24,7 @@
#include "antObject.h"
#include "antTemplate.h"
#include "antConfig.h"
#include "layConverters.h"
#include "tlString.h"
#include "tlExpression.h"
@ -713,7 +714,7 @@ Object::from_string (const char *s, const char * /*base_dir*/)
std::string s;
ex.read_word (s);
ant::ACConverter sc;
lay::ACConverter sc;
lay::angle_constraint_type sm;
sc.from_string (s, sm);
angle_constraint (sm);
@ -817,7 +818,7 @@ Object::to_string () const
r += ",";
r += "angle_constraint=";
ant::ACConverter acc;
lay::ACConverter acc;
r += acc.to_string (angle_constraint ());
return r;

View File

@ -102,7 +102,7 @@ PluginDeclaration::get_options (std::vector < std::pair<std::string, std::string
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_range, "8"));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_color, lay::ColorConverter ().to_string (tl::Color ())));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_halo, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_mode, lay::ACConverter ().to_string (lay::AC_Any)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_obj_snap, tl::to_string (true)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_grid_snap, tl::to_string (false)));
options.push_back (std::pair<std::string, std::string> (cfg_ruler_templates, std::string ()));

View File

@ -1134,7 +1134,7 @@ Service::configure (const std::string &name, const std::string &value)
} else if (name == cfg_ruler_snap_mode) {
lay::angle_constraint_type sm = lay::AC_Any;
ACConverter ().from_string (value, sm);
lay::ACConverter ().from_string (value, sm);
m_snap_mode = sm;
} else if (name == cfg_ruler_templates) {

View File

@ -23,6 +23,7 @@
#include "antTemplate.h"
#include "antConfig.h"
#include "layConverters.h"
#include "tlInternational.h"
#include "tlException.h"
#include "tlLog.h"
@ -263,7 +264,7 @@ Template::from_string (const std::string &s)
} else if (key == "angle_constraint") {
ant::ACConverter sc;
lay::ACConverter sc;
lay::angle_constraint_type sm;
sc.from_string (s, sm);
r.back ().angle_constraint (sm);
@ -373,7 +374,7 @@ Template::to_string (const std::vector<Template> &v)
r += ",";
r += "angle_constraint=";
ant::ACConverter acc;
lay::ACConverter acc;
r += acc.to_string (t->angle_constraint ());
}

View File

@ -53,6 +53,7 @@ static int outline_radius () { return int (ant::Object::OL_radius); }
static int angle_any () { return int (lay::AC_Any); }
static int angle_diagonal () { return int (lay::AC_Diagonal); }
static int angle_diagonal_only () { return int (lay::AC_DiagonalOnly); }
static int angle_ortho () { return int (lay::AC_Ortho); }
static int angle_horizontal () { return int (lay::AC_Horizontal); }
static int angle_vertical () { return int (lay::AC_Vertical); }
@ -653,6 +654,11 @@ gsi::Class<AnnotationRef> decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"@brief Gets the diagonal angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, only multiples of 45 degree are allowed."
) +
gsi::method ("AngleDiagonalOnly", &gsi::angle_diagonal_only,
"@brief Gets the diagonal angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, only 45 degree or 135 degree are allowed.\n"
"This constant has been introduced in version 0.30.6."
) +
gsi::method ("AngleOrtho", &gsi::angle_ortho,
"@brief Gets the ortho angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, only multiples of 90 degree are allowed."

View File

@ -1406,15 +1406,6 @@ PartialService::snap (const db::DVector &v_org) const
}
}
const int sr_pixels = 8; // TODO: make variable
lay::PointSnapToObjectResult
PartialService::snap2 (const db::DPoint &p) const
{
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
return lay::obj_snap (m_snap_to_objects ? view () : 0, m_start, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, move_ac (), snap_range);
}
void
PartialService::transform (const db::DCplxTrans &tr)
{
@ -1790,6 +1781,78 @@ PartialService::wheel_event (int /*delta*/, bool /*horizontal*/, const db::DPoin
return false;
}
const int sr_pixels = 8; // TODO: make variable
void
PartialService::move_impl (const db::DPoint &p)
{
// drag the vertex or edge/segment
if (is_single_point_selection () || is_single_edge_selection ()) {
lay::PointSnapToObjectResult snap_details;
// for a single selected point or edge, m_start is the original position and we snap the target -
// thus, we can bring the point on grid or to an object's edge or vertex
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
snap_details = lay::obj_snap (m_snap_to_objects ? view () : 0, m_start, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, lay::AC_Any, snap_range);;
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
m_current = m_start + snap_move (p - m_start);
} else {
auto snapped_to_object = snap_details.snapped_point;
m_current = snapped_to_object;
if (snap_details.object_snap != lay::PointSnapToObjectResult::ObjectVertex) {
// snap to grid on longer side of reference edge and to object on shorter
auto snapped_to_object_and_grid = m_start + snap_move (snapped_to_object - m_start);
if (std::abs (snap_details.object_ref.dx ()) > std::abs (snap_details.object_ref.dy ())) {
m_current.set_x (snapped_to_object_and_grid.x ());
// project to edge, so we always hit it
auto cp = snap_details.object_ref.cut_point (db::DEdge (m_current, m_current + db::DVector (0, 1.0)));
if (cp.first) {
m_current.set_y (cp.second.y ());
}
} else if (std::abs (snap_details.object_ref.dy ()) > std::abs (snap_details.object_ref.dx ())) {
m_current.set_y (snapped_to_object_and_grid.y ());
// project to edge, so we always hit it
auto cp = snap_details.object_ref.cut_point (db::DEdge (m_current, m_current + db::DVector (1.0, 0)));
if (cp.first) {
m_current.set_x (cp.second.x ());
}
}
}
mouse_cursor_from_snap_details (snap_details);
}
if (is_single_edge_selection ()) {
// in case of edge movement, project the move vector to the edge normal -
// that is cosmetic, so we don't imply a lateral shift
auto e = single_selected_edge ().d ();
if (e.double_length () > db::epsilon) {
db::DVector n = db::DVector (e.y (), -e.x ()) * (1.0 / e.double_length ());
m_current = m_start + n * db::sprod (m_current - m_start, n);
}
}
} else {
// snap movement to angle and grid without object
m_current = m_start + snap_move (p - m_start);
}
selection_to_view ();
}
bool
PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
@ -1801,57 +1864,7 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
m_alt_ac = lay::ac_from_buttons (buttons);
// drag the vertex or edge/segment
if (is_single_point_selection () || is_single_edge_selection ()) {
lay::PointSnapToObjectResult snap_details;
// for a single selected point or edge, m_start is the original position and we snap the target -
// thus, we can bring the point on grid or to an object's edge or vertex
snap_details = snap2 (p);
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
m_current = m_start + snap_move (p - m_start);
} else {
auto snapped_to_object = snap_details.snapped_point;
m_current = snapped_to_object;
if (snap_details.object_snap != lay::PointSnapToObjectResult::ObjectVertex) {
// snap to grid on longer side of reference edge and to object on shorter
auto snapped_to_object_and_grid = m_start + snap_move (snapped_to_object - m_start);
if (std::abs (snap_details.object_ref.dx ()) > std::abs (snap_details.object_ref.dy ())) {
m_current.set_x (snapped_to_object_and_grid.x ());
// project to edge, so we always hit it
auto cp = snap_details.object_ref.cut_point (db::DEdge (m_current, m_current + db::DVector (0, 1.0)));
if (cp.first) {
m_current.set_y (cp.second.y ());
}
} else if (std::abs (snap_details.object_ref.dy ()) > std::abs (snap_details.object_ref.dx ())) {
m_current.set_y (snapped_to_object_and_grid.y ());
// project to edge, so we always hit it
auto cp = snap_details.object_ref.cut_point (db::DEdge (m_current, m_current + db::DVector (1.0, 0)));
if (cp.first) {
m_current.set_x (cp.second.x ());
}
}
}
mouse_cursor_from_snap_details (snap_details);
}
} else {
// snap movement to angle and grid without object
m_current = m_start + snap_move (p - m_start);
clear_mouse_cursors ();
}
selection_to_view ();
move_impl (p);
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits);
issue_editor_hook_calls (m_editor_hooks);
@ -2457,34 +2470,12 @@ PartialService::move (const db::DPoint &p, lay::angle_constraint_type ac)
m_alt_ac = ac;
set_cursor (lay::Cursor::size_all);
clear_mouse_cursors ();
// drag the vertex or edge/segment
if (is_single_point_selection () || is_single_edge_selection ()) {
lay::PointSnapToObjectResult snap_details;
// for a single selected point or edge, m_start is the original position and we snap the target -
// thus, we can bring the point on grid or to an object's edge or vertex
snap_details = snap2 (p);
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
m_current = m_start + snap_move (p - m_start);
} else {
m_current = snap_details.snapped_point;
mouse_cursor_from_snap_details (snap_details);
}
} else {
// snap movement to angle and grid without object
m_current = m_start + snap_move (p - m_start);
clear_mouse_cursors ();
}
move_impl (p);
propose_move_transformation (db::DTrans (m_current - m_start), 0);
selection_to_view ();
m_alt_ac = lay::AC_Global;
}

View File

@ -386,10 +386,10 @@ private:
db::DPoint snap (const db::DPoint &p) const;
db::DVector snap (const db::DVector &p) const;
lay::PointSnapToObjectResult snap2 (const db::DPoint &p) const;
void update_vector_snapped_point (const db::DPoint &pt, db::DVector &vr, bool &result_set) const;
db::DVector snap_marker_to_grid (const db::DVector &v, bool &snapped) const;
db::DVector snap_move(const db::DVector &p) const;
void move_impl (const db::DPoint &p);
void enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_objects::const_iterator sel, const std::map <PointWithIndex, db::Point> &new_points, const std::map <EdgeWithIndex, db::Edge> &new_edges, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool transient);
void enter_vertices (size_t &nmarker, partial_objects::const_iterator sel, const std::map <PointWithIndex, db::Point> &new_points, const std::map <EdgeWithIndex, db::Edge> &new_edges, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool transient);

View File

@ -1092,6 +1092,10 @@ gsi::Enum<lay::angle_constraint_type> decl_AngleConstraintType ("lay", "AngleCon
gsi::enum_const ("AC_Diagonal", lay::AC_Diagonal,
"@brief Specifies to use multiples of 45 degree.\n"
) +
gsi::enum_const ("AC_DiagonalOnly", lay::AC_DiagonalOnly,
"@brief Specifies to use 45 degree or 135 degree only.\n"
"This variant has been introduced in version 0.30.6."
) +
gsi::enum_const ("AC_Ortho", lay::AC_Ortho,
"@brief Specifies to use multiples of 90 degree.\n"
) +

View File

@ -87,8 +87,16 @@ ACConverter::to_string (const lay::angle_constraint_type &m)
return "any";
} else if (m == lay::AC_Diagonal) {
return "diagonal";
} else if (m == lay::AC_DiagonalOnly) {
return "diagonal_only";
} else if (m == lay::AC_Ortho) {
return "ortho";
} else if (m == lay::AC_Horizontal) {
return "horizontal";
} else if (m == lay::AC_Vertical) {
return "vertical";
} else if (m == lay::AC_Global) {
return "global";
} else {
return "";
}
@ -102,8 +110,16 @@ ACConverter::from_string (const std::string &tt, lay::angle_constraint_type &m)
m = lay::AC_Any;
} else if (t == "diagonal") {
m = lay::AC_Diagonal;
} else if (t == "diagonal_only") {
m = lay::AC_DiagonalOnly;
} else if (t == "ortho") {
m = lay::AC_Ortho;
} else if (t == "horizontal") {
m = lay::AC_Horizontal;
} else if (t == "vertical") {
m = lay::AC_Vertical;
} else if (t == "global") {
m = lay::AC_Global;
} else {
m = lay::AC_Any;
}