diff --git a/src/ant/ant/RulerConfigPage3.ui b/src/ant/ant/RulerConfigPage3.ui index e9d90e434..4e7fed45d 100644 --- a/src/ant/ant/RulerConfigPage3.ui +++ b/src/ant/ant/RulerConfigPage3.ui @@ -7,7 +7,7 @@ 0 0 665 - 103 + 108 @@ -56,6 +56,13 @@ 0 + + + + Diagonal + + + @@ -77,17 +84,17 @@ - - + + - Diagonal + Vertical only - + - Vertical only + Diagonal only @@ -102,7 +109,6 @@ ruler_ortho_rb ruler_diag_rb ruler_hor_rb - ruler_vert_rb diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui index 87e5dc3cc..1f5ec4a29 100644 --- a/src/ant/ant/RulerConfigPage4.ui +++ b/src/ant/ant/RulerConfigPage4.ui @@ -644,6 +644,11 @@ Orthogonal + + + Diagonal only + + Horizontal only diff --git a/src/ant/ant/ant.pro b/src/ant/ant/ant.pro index c6052dab5..1d1286c73 100644 --- a/src/ant/ant/ant.pro +++ b/src/ant/ant/ant.pro @@ -31,6 +31,7 @@ SOURCES = \ HEADERS += \ antConfig.h \ + antEditorOptionsPages.h \ antObject.h \ antPlugin.h \ antService.h \ @@ -40,6 +41,7 @@ HEADERS += \ SOURCES += \ antConfig.cc \ + antEditorOptionsPages.cc \ antObject.cc \ antPlugin.cc \ antService.cc \ diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc index bec7c21f4..b2f4c239b 100644 --- a/src/ant/ant/antConfig.cc +++ b/src/ant/ant/antConfig.cc @@ -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) { diff --git a/src/ant/ant/antConfig.h b/src/ant/ant/antConfig.h index cb1bb5e10..4c603ac37 100644 --- a/src/ant/ant/antConfig.h +++ b/src/ant/ant/antConfig.h @@ -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); diff --git a/src/ant/ant/antConfigPage.cc b/src/ant/ant/antConfigPage.cc index 1dcd1cc69..6647035be 100644 --- a/src/ant/ant/antConfigPage.cc +++ b/src/ant/ant/antConfigPage.cc @@ -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 ()); } // ------------------------------------------------------------ diff --git a/src/ant/ant/antEditorOptionsPages.cc b/src/ant/ant/antEditorOptionsPages.cc new file mode 100644 index 000000000..56dea4a17 --- /dev/null +++ b/src/ant/ant/antEditorOptionsPages.cc @@ -0,0 +1,155 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#if defined(HAVE_QT) + +#include "antService.h" +#include "antEditorOptionsPages.h" + +#include "layWidgets.h" +#include "layDispatcher.h" +#include "tlInternational.h" + +#include + +namespace ant +{ + +// ------------------------------------------------------------------ +// Annotations Toolbox widget + +ToolkitWidget::ToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) +{ + mp_layout = new QHBoxLayout (this); + + mp_x_le = new lay::DecoratedLineEdit (this); + mp_x_le->set_label ("dx:"); + mp_layout->addWidget (mp_x_le); + + mp_y_le = new lay::DecoratedLineEdit (this); + mp_y_le->set_label ("dy:"); + mp_layout->addWidget (mp_y_le); + + mp_d_le = new lay::DecoratedLineEdit (this); + mp_d_le->set_label ("d:"); + mp_layout->addWidget (mp_d_le); + + mp_layout->addStretch (1); + + hide (); + + set_toolbox_widget (true); + set_transparent (true); +} + +ToolkitWidget::~ToolkitWidget () +{ + // .. nothing yet .. +} + +std::string +ToolkitWidget::title () const +{ + return "Box Options"; +} + +const char * +ToolkitWidget::name () const +{ + return ant::Service::editor_options_name (); +} + +void +ToolkitWidget::deactivated () +{ + hide (); +} + +void +ToolkitWidget::commit (lay::Dispatcher *dispatcher) +{ + try { + + if (mp_d_le->hasFocus ()) { + + double d = 0.0; + + tl::from_string (tl::to_string (mp_d_le->text ()), d); + + dispatcher->call_function (ant::Service::d_function_name (), tl::to_string (d)); + + } else { + + double dx = 0.0, dy = 0.0; + + tl::from_string (tl::to_string (mp_x_le->text ()), dx); + tl::from_string (tl::to_string (mp_y_le->text ()), dy); + + dispatcher->call_function (ant::Service::xy_function_name (), db::DVector (dx, dy).to_string ()); + + } + + } catch (...) { + } +} + +void +ToolkitWidget::configure (const std::string &name, const std::string &value) +{ + if (name == ant::Service::xy_configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { + + try { + + db::DVector mv; + tl::from_string (value, mv); + + mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ()))); + mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ()))); + + } catch (...) { + } + + } else if (name == ant::Service::d_configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { + + try { + + double d; + tl::from_string (value, d); + + mp_d_le->setText (tl::to_qstring (tl::micron_to_string (d))); + + } catch (...) { + } + + } +} + +// ------------------------------------------------------------------ +// Registrations + +// toolkit widgets +static tl::RegisteredClass s_tookit_widget_factory (new lay::EditorOptionsPageFactory ("ant::Plugin"), 0); + +} + +#endif diff --git a/src/ant/ant/antEditorOptionsPages.h b/src/ant/ant/antEditorOptionsPages.h new file mode 100644 index 000000000..470a69686 --- /dev/null +++ b/src/ant/ant/antEditorOptionsPages.h @@ -0,0 +1,68 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#if defined(HAVE_QT) + +#ifndef HDR_antEditorOptionsPages +#define HDR_antEditorOptionsPages + +#include "layEditorOptionsPageWidget.h" + +class QHBoxLayout; + +namespace lay +{ + class DecoratedLineEdit; +} + +namespace ant +{ + +/** + * @brief The toolbox widget for annotations + */ +class ToolkitWidget + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + ToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~ToolkitWidget (); + + virtual std::string title () const; + virtual const char *name () const; + virtual int order () const { return 0; } + virtual void configure (const std::string &name, const std::string &value); + virtual void commit (lay::Dispatcher *root); + virtual void deactivated (); + +private: + QHBoxLayout *mp_layout; + lay::DecoratedLineEdit *mp_x_le, *mp_y_le, *mp_d_le; +}; + +} + +#endif + +#endif diff --git a/src/ant/ant/antObject.cc b/src/ant/ant/antObject.cc index 9ce86eed3..aa3dbb693 100644 --- a/src/ant/ant/antObject.cc +++ b/src/ant/ant/antObject.cc @@ -24,6 +24,7 @@ #include "antObject.h" #include "antTemplate.h" #include "antConfig.h" +#include "layConverters.h" #include "tlString.h" #include "tlExpression.h" @@ -425,7 +426,24 @@ class AnnotationEvalFunction : public tl::EvalFunction { public: - AnnotationEvalFunction (char function, const AnnotationEval *eval, size_t index) + enum FunctionType { + ManhattanLength, // L + ManhattanLengthIncremental, // LL + EuclidianDistance, // D + EuclidianDistanceIncremental, // DD + XDelta, // X + XDeltaIncremental, // XX + YDelta, // Y + YDeltaIncremental, // YY + P1X, // U + P1Y, // V + P2X, // P + P2Y, // Q + Area, // A + Angle // G + }; + + AnnotationEvalFunction (FunctionType function, const AnnotationEval *eval, size_t index) : m_function (function), mp_eval (eval), m_index (index) { // .. nothing yet .. @@ -440,25 +458,53 @@ class AnnotationEvalFunction const Object &obj = mp_eval->obj (); const db::DFTrans &trans = mp_eval->trans (); - if (m_function == 'L') { - out = fabs (delta_x (obj, trans)) + fabs (delta_y (obj, trans)); - } else if (m_function == 'D') { - out = sqrt (delta_x (obj, trans) * delta_x (obj, trans) + delta_y (obj, trans) * delta_y (obj, trans)); - } else if (m_function == 'A') { - out = delta_x (obj, trans) * delta_y (obj, trans) * 1e-6; - } else if (m_function == 'X') { - out = delta_x (obj, trans); - } else if (m_function == 'Y') { - out = delta_y (obj, trans); - } else if (m_function == 'U') { - out = (trans * p1 (obj)).x (); - } else if (m_function == 'V') { - out = (trans * p1 (obj)).y (); - } else if (m_function == 'P') { - out = (trans * p2 (obj)).x (); - } else if (m_function == 'Q') { - out = (trans * p2 (obj)).y (); - } else if (m_function == 'G') { + if (m_function == ManhattanLength) { + out = fabs (delta_x (obj, trans, m_index)) + fabs (delta_y (obj, trans, m_index)); + } else if (m_function == ManhattanLengthIncremental) { + double res = 0.0; + for (size_t index = 0; index <= m_index; ++index) { + res += fabs (delta_x (obj, trans, index)) + fabs (delta_y (obj, trans, index)); + } + out = res; + } else if (m_function == EuclidianDistance) { + auto dx = delta_x (obj, trans, m_index); + auto dy = delta_y (obj, trans, m_index); + out = sqrt (dx * dx + dy * dy); + } else if (m_function == EuclidianDistanceIncremental) { + double res = 0.0; + for (size_t index = 0; index <= m_index; ++index) { + auto dx = delta_x (obj, trans, index); + auto dy = delta_y (obj, trans, index); + res += sqrt (dx * dx + dy * dy); + } + out = res; + } else if (m_function == Area) { + out = delta_x (obj, trans, m_index) * delta_y (obj, trans, m_index) * 1e-6; + } else if (m_function == XDelta) { + out = delta_x (obj, trans, m_index); + } else if (m_function == XDeltaIncremental) { + double res = 0.0; + for (size_t index = 0; index <= m_index; ++index) { + res += delta_x (obj, trans, index); + } + out = res; + } else if (m_function == YDelta) { + out = delta_y (obj, trans, m_index); + } else if (m_function == YDeltaIncremental) { + double res = 0.0; + for (size_t index = 0; index <= m_index; ++index) { + res += delta_y (obj, trans, index); + } + out = res; + } else if (m_function == P1X) { + out = (trans * p1 (obj, m_index)).x (); + } else if (m_function == P1Y) { + out = (trans * p1 (obj, m_index)).y (); + } else if (m_function == P2X) { + out = (trans * p2 (obj, m_index)).x (); + } else if (m_function == P2Y) { + out = (trans * p2 (obj, m_index)).y (); + } else if (m_function == Angle) { double r, a1, a2; db::DPoint c; if (obj.compute_angle_parameters (r, c, a1, a2)) { @@ -471,20 +517,20 @@ class AnnotationEvalFunction } } - db::DPoint p1 (const Object &obj) const + db::DPoint p1 (const Object &obj, size_t index) const { - return obj.seg_p1 (m_index); + return obj.seg_p1 (index); } - db::DPoint p2 (const Object &obj) const + db::DPoint p2 (const Object &obj, size_t index) const { - return obj.seg_p2 (m_index); + return obj.seg_p2 (index); } double - delta_x (const Object &obj, const db::DFTrans &t) const + delta_x (const Object &obj, const db::DFTrans &t, size_t index) const { - double dx = ((t * p2 (obj)).x () - (t * p1 (obj)).x ()); + double dx = ((t * p2 (obj, index)).x () - (t * p1 (obj, index)).x ()); // avoid "almost 0" outputs if (fabs (dx) < 1e-5 /*micron*/) { @@ -495,9 +541,9 @@ class AnnotationEvalFunction } double - delta_y (const Object &obj, const db::DFTrans &t) const + delta_y (const Object &obj, const db::DFTrans &t, size_t index) const { - double dy = ((t * p2 (obj)).y () - (t * p1 (obj)).y ()); + double dy = ((t * p2 (obj, index)).y () - (t * p1 (obj, index)).y ()); // avoid "almost 0" outputs if (fabs (dy) < 1e-5 /*micron*/) { @@ -508,7 +554,7 @@ class AnnotationEvalFunction } private: - char m_function; + FunctionType m_function; const AnnotationEval *mp_eval; size_t m_index; }; @@ -517,16 +563,20 @@ std::string Object::formatted (const std::string &fmt, const db::DFTrans &t, size_t index) const { AnnotationEval eval (*this, t); - eval.define_function ("L", new AnnotationEvalFunction('L', &eval, index)); // manhattan length - eval.define_function ("D", new AnnotationEvalFunction('D', &eval, index)); // euclidian distance - eval.define_function ("X", new AnnotationEvalFunction('X', &eval, index)); // x delta - eval.define_function ("Y", new AnnotationEvalFunction('Y', &eval, index)); // y delta - eval.define_function ("U", new AnnotationEvalFunction('U', &eval, index)); // p1.x - eval.define_function ("V", new AnnotationEvalFunction('V', &eval, index)); // p1.y - eval.define_function ("P", new AnnotationEvalFunction('P', &eval, index)); // p2.x - eval.define_function ("Q", new AnnotationEvalFunction('Q', &eval, index)); // p2.y - eval.define_function ("A", new AnnotationEvalFunction('A', &eval, index)); // area mm2 - eval.define_function ("G", new AnnotationEvalFunction('G', &eval, index)); // angle (if applicable) + eval.define_function ("L", new AnnotationEvalFunction (AnnotationEvalFunction::ManhattanLength, &eval, index)); // manhattan length + eval.define_function ("LL", new AnnotationEvalFunction (AnnotationEvalFunction::ManhattanLengthIncremental, &eval, index)); // manhattan length + eval.define_function ("D", new AnnotationEvalFunction (AnnotationEvalFunction::EuclidianDistance, &eval, index)); // euclidian distance + eval.define_function ("DD", new AnnotationEvalFunction (AnnotationEvalFunction::EuclidianDistanceIncremental, &eval, index)); // euclidian distance (incremental, for multi-rulers) + eval.define_function ("X", new AnnotationEvalFunction (AnnotationEvalFunction::XDelta, &eval, index)); // x delta + eval.define_function ("XX", new AnnotationEvalFunction (AnnotationEvalFunction::XDeltaIncremental, &eval, index)); // x delta (incremental, for multi-rulers) + eval.define_function ("Y", new AnnotationEvalFunction (AnnotationEvalFunction::YDelta, &eval, index)); // y delta + eval.define_function ("YY", new AnnotationEvalFunction (AnnotationEvalFunction::YDeltaIncremental, &eval, index)); // y delta (incremental, for multi-rulers) + eval.define_function ("U", new AnnotationEvalFunction (AnnotationEvalFunction::P1X, &eval, index)); // p1.x + eval.define_function ("V", new AnnotationEvalFunction (AnnotationEvalFunction::P1Y, &eval, index)); // p1.y + eval.define_function ("P", new AnnotationEvalFunction (AnnotationEvalFunction::P2X, &eval, index)); // p2.x + eval.define_function ("Q", new AnnotationEvalFunction (AnnotationEvalFunction::P2Y, &eval, index)); // p2.y + eval.define_function ("A", new AnnotationEvalFunction (AnnotationEvalFunction::Area, &eval, index)); // area mm2 + eval.define_function ("G", new AnnotationEvalFunction (AnnotationEvalFunction::Angle, &eval, index)); // angle (if applicable) return eval.interpolate (fmt); } @@ -713,7 +763,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 +867,7 @@ Object::to_string () const r += ","; r += "angle_constraint="; - ant::ACConverter acc; + lay::ACConverter acc; r += acc.to_string (angle_constraint ()); return r; diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index ab21a915c..aaed7f8d6 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -102,7 +102,7 @@ PluginDeclaration::get_options (std::vector < std::pair (cfg_ruler_snap_range, "8")); options.push_back (std::pair (cfg_ruler_color, lay::ColorConverter ().to_string (tl::Color ()))); options.push_back (std::pair (cfg_ruler_halo, "true")); - options.push_back (std::pair (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any))); + options.push_back (std::pair (cfg_ruler_snap_mode, lay::ACConverter ().to_string (lay::AC_Any))); options.push_back (std::pair (cfg_ruler_obj_snap, tl::to_string (true))); options.push_back (std::pair (cfg_ruler_grid_snap, tl::to_string (false))); options.push_back (std::pair (cfg_ruler_templates, std::string ())); diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 6f6c0e556..90f070655 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -33,6 +33,7 @@ #include "layConverters.h" #include "layLayoutCanvas.h" #include "layFixedFont.h" +#include "layEditorOptionsPage.h" #if defined(HAVE_QT) # include "layProperties.h" #endif @@ -1041,6 +1042,12 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) // ------------------------------------------------------------- // ant::Service implementation +const char *Service::editor_options_name () { return "ant-toolkit-widget-name"; } +const char *Service::xy_configure_name () { return "ant-toolkit-widget-xy-value"; } +const char *Service::d_configure_name () { return "ant-toolkit-widget-d-value"; } +const char *Service::xy_function_name () { return "ant-toolkit-widget-xy-commit"; } +const char *Service::d_function_name () { return "ant-toolkit-widget-d-commit"; } + Service::Service (db::Manager *manager, lay::LayoutViewBase *view) : lay::EditorServiceBase (view), lay::Drawing (1/*number of planes*/, view->drawings ()), @@ -1056,6 +1063,9 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_drawing (false), m_current (), m_move_mode (MoveNone), m_seg_index (0), + m_length_confined (false), + m_length (0.0), + m_centered (false), m_current_template (0), m_hover (false), m_hover_wait (false), @@ -1134,7 +1144,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) { @@ -1169,6 +1179,21 @@ Service::config_finalize () { } +void +Service::show_toolbox (bool visible) +{ + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + tb->set_visible (visible); + } +} + +lay::EditorOptionsPage * +Service::toolbox_widget () +{ + return mp_view->editor_options_pages () ? mp_view->editor_options_pages ()->page_with_name (editor_options_name ()) : 0; +} + void Service::annotations_changed () { @@ -1244,6 +1269,7 @@ void Service::drag_cancel () { if (m_drawing) { + show_toolbox (false); ui ()->ungrab_mouse (this); m_drawing = false; } @@ -1598,72 +1624,84 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; clear_mouse_cursors (); + if (m_move_mode == MoveSelected) { + + db::DVector dp = p - m_p1; + 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); + + propose_move_transformation (m_trans, 1); + + snap_rulers (ac_eff); + + for (std::vector::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) { + (*r)->transform_by (db::DCplxTrans (m_trans)); + } + + show_message (); + + } else if (m_move_mode != MoveNone) { + + db::DPoint ps = snap2_visual (m_p1, p, &m_current, ac); + m_trans = db::DTrans (ps - m_p1); + + apply_partial_move (ps); + + propose_move_transformation (db::DTrans (ps - m_p1), 1); + + // display current move distance + std::string pos = std::string ("dx: ") + tl::micron_to_string (ps.x () - m_p1.x ()) + " " + + "dy: " + tl::micron_to_string (ps.y () - m_p1.y ()); + view ()->message (pos); + + } +} + +void +Service::apply_partial_move (db::DPoint &ps) +{ if (m_move_mode == MoveP1) { - - m_current.seg_p1 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac)); - m_rulers [0]->redraw (); + + m_current.seg_p1 (m_seg_index, ps); } else if (m_move_mode == MoveP2) { - - m_current.seg_p2 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac)); - m_rulers [0]->redraw (); + + m_current.seg_p2 (m_seg_index, ps); } else if (m_move_mode == MoveP12) { - - 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 (); + + m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x(), ps.y ())); + m_current.seg_p2 (m_seg_index, db::DPoint (ps.x (), m_current.seg_p2 (m_seg_index).y ())); } else if (m_move_mode == MoveP21) { - - 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 (); + + m_current.seg_p1 (m_seg_index, db::DPoint (ps.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(), ps.y ())); } else if (m_move_mode == MoveP1X) { - - 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_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 (); + ps.set_y (m_p1.y ()); + m_current.seg_p1 (m_seg_index, db::DPoint (ps.x (), m_current.seg_p1 (m_seg_index).y ())); - } else if (m_move_mode == MoveP1Y) { - - 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 == MoveP2X) { - } else if (m_move_mode == MoveP2Y) { - - 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 (); + ps.set_y (m_p1.y ()); + m_current.seg_p2 (m_seg_index, db::DPoint (ps.x (), m_current.seg_p2 (m_seg_index).y ())); - } else if (m_move_mode == MoveSelected) { + } else if (m_move_mode == MoveP1Y) { - db::DVector dp = p - m_p1; - dp = lay::snap_angle (dp, ac_eff); + ps.set_x (m_p1.x ()); + m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x (), ps.y ())); - m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1); + } else if (m_move_mode == MoveP2Y) { - snap_rulers (ac_eff); - - for (std::vector::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) { - (*r)->transform_by (db::DCplxTrans (m_trans)); - } + ps.set_x (m_p1.x ()); + m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x (), ps.y ())); } - if (m_move_mode != MoveSelected) { - show_message (); - } + m_rulers [0]->redraw (); } void @@ -1676,6 +1714,13 @@ Service::show_message () view ()->message (pos); } +void +Service::end_move (const db::DVector &v) +{ + m_trans = db::DTrans (v) * db::DTrans (m_trans.fp_trans ()); + end_move (db::DPoint (), lay::AC_Any); +} + void Service::end_move (const db::DPoint &, lay::angle_constraint_type) { @@ -1705,6 +1750,9 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) } else if (m_move_mode != MoveNone) { + db::DPoint ps = m_trans * m_p1; + apply_partial_move (ps); + // replace the ruler that was moved m_current.clean_points (); mp_view->annotation_shapes ().replace (*m_selected.begin (), db::DUserObject (new ant::Object (m_current))); @@ -1830,9 +1878,23 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button finish_drawing (); return true; + } else { + return false; } +} - return false; +bool +Service::key_event (unsigned int key, unsigned int buttons) +{ + if (m_drawing && buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) { + + // ends the current ruler (specifically in multi-segment mode) + finish_drawing (); + return true; + + } else { + return false; + } } lay::TwoPointSnapToObjectResult @@ -1873,6 +1935,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio // cancel any edit operations so far m_move_mode = MoveNone; + m_length_confined = false; // reset selection clear_selection (); @@ -1969,6 +2032,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio mp_active_ruler->thaw (); m_drawing = true; + show_toolbox (true); ui ()->grab_mouse (this, false); } @@ -1989,6 +2053,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio pts.push_back (m_p1); m_current.set_points_exact (pts); + m_length_confined = false; } @@ -2021,6 +2086,116 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type } } +void +Service::function (const std::string &name, const std::string &value) +{ + if (name == xy_function_name ()) { + + try { + + db::DVector s; + tl::from_string (value, s); + + if (m_drawing) { + + ant::Object::point_list pts = m_current.points (); + if (pts.size () >= 2) { + + db::DVector d = pts.back () - pts [pts.size () - 2]; + + // Adjust the direction so positive coordinates are in the current drag direction + s = db::DVector (s.x () * (d.x () < 0 ? -1.0 : 1.0), s.y () * (d.y () < 0 ? -1.0 : 1.0)); + + pts.back () = pts [pts.size () - 2] + s; + m_current.set_points_exact (pts); + + } + + const ant::Template &tpl = current_template (); + + if (tpl.mode () == ant::Template::RulerMultiSegment || tpl.mode () == ant::Template::RulerThreeClicks) { + + if (tpl.mode () == ant::Template::RulerThreeClicks && pts.size () == 3) { + + finish_drawing (); + + } else { + + // add a new point + m_p1 = pts.back (); + + pts.push_back (m_p1); + m_current.set_points_exact (pts); + + } + + } else { + + finish_drawing (); + + } + + } + + } catch (...) { + } + + } else if (name == d_function_name ()) { + + try { + + double s = 0.0; + tl::from_string (value, s); + + if (m_drawing) { + + m_length_confined = true; + m_length = s; + + ant::Object::point_list pts = m_current.points (); + confine_length (pts); + m_current.set_points_exact (pts); + + } + + } catch (...) { + } + + } +} + +void +Service::confine_length (ant::Object::point_list &pts) +{ + if (m_length_confined && pts.size () >= 2) { + + const ant::Template &tpl = current_template (); + bool is_box_style = tpl.mode () == ant::Template::RulerNormal && (tpl.outline () == ant::Object::OL_box || tpl.outline () == ant::Object::OL_ellipse); + + db::DPoint p1 = m_centered ? m_p1 : pts [pts.size () - 2]; + db::DVector s = pts.back () - p1; + if (is_box_style) { + db::DVector snew = s; + double l = m_centered ? m_length * 0.5 : m_length; + if (fabs (s.x ()) < fabs (s.y ()) + db::epsilon) { + snew.set_y (l * (s.y () < 0 ? -1.0 : 1.0)); + } + if (fabs (s.y ()) < fabs (s.x ()) + db::epsilon) { + snew.set_x (l * (s.x () < 0 ? -1.0 : 1.0)); + } + s = snew; + } else { + double l = s.double_length (); + if (l > db::epsilon) { + s *= m_length / l; + } + } + + pts.back () = p1 + s; + + } +} + bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { @@ -2040,11 +2215,26 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) } + const ant::Template &tpl = current_template (); + + // for normal rulers with box or ellipse rendering we use a different button scheme: + // Shift will keep the center, Ctrl will confine the box to a square/ellipse to a circle + bool is_box_style = tpl.mode () == ant::Template::RulerNormal && (tpl.outline () == ant::Object::OL_box || tpl.outline () == ant::Object::OL_ellipse); + bool snap_square = is_box_style && (buttons & lay::ControlButton) != 0; + m_centered = is_box_style && (buttons & lay::ShiftButton) != 0; + lay::PointSnapToObjectResult snap_details; if (m_drawing) { - snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)); + lay::angle_constraint_type ac; + if (snap_square) { + ac = lay::AC_DiagonalOnly; + } else if (is_box_style) { + ac = lay::AC_Any; + } else { + ac = ac_from_buttons (buttons); + } + snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac); } else { - const ant::Template &tpl = current_template (); snap_details = snap1_details (p, m_obj_snap && tpl.snap () && (tpl.mode () != ant::Template::RulerAutoMetricEdge || ! view ()->transient_selection_mode ())); } @@ -2058,10 +2248,36 @@ 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 () = snap_details.snapped_point; + + confine_length (pts); + + if (is_box_style) { + if (m_centered) { + pts.front () = m_p1 - (pts.back () - m_p1); + } else { + pts.front () = m_p1; + } + } + } + m_current.set_points_exact (pts); + db::DVector delta; + if (pts.size () >= 2) { + delta = pts.back () - pts[pts.size () - 2]; + delta = db::DVector (fabs (delta.x ()), fabs (delta.y ())); + } + + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + double d = is_box_style ? std::min (fabs (delta.x ()), fabs (delta.y ())) : delta.length (); + tb->configure (xy_configure_name (), delta.to_string ()); + tb->configure (d_configure_name (), tl::to_string (d)); + } + mp_active_ruler->redraw (); show_message (); diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index 13086eeb4..2bd5c574d 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -197,6 +197,13 @@ Q_OBJECT public: typedef lay::AnnotationShapes::iterator obj_iterator; + // for communicating with the toolbox widget + static const char *editor_options_name (); + static const char *xy_configure_name (); + static const char *d_configure_name (); + static const char *xy_function_name (); + static const char *d_function_name (); + /** * The current move mode: * MoveNone - not moving @@ -347,6 +354,11 @@ Q_OBJECT */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Return the bbox of the selection (reimplementation of lay::Editable interface) */ @@ -498,10 +510,15 @@ Q_OBJECT } /** - * @brief Implement the menu response function + * @brief Implements the menu response function */ void menu_activated (const std::string &symbol); + /** + * @brief Implements the toolbox widget response function + */ + void function (const std::string &name, const std::string &value); + /** * @brief Return the annotation iterator that delivers the annotations (and only these) */ @@ -583,6 +600,11 @@ public slots: MoveMode m_move_mode; // The currently moving segment size_t m_seg_index; + // When set to true, the length is confined to the value given by m_length + bool m_length_confined; + double m_length; + // When set to true, the last point was established in centered fashion + bool m_centered; // The ruler template std::vector m_ruler_templates; unsigned int m_current_template; @@ -603,10 +625,15 @@ public slots: 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); + void confine_length (ant::Object::point_list &pts); const ant::Template ¤t_template () const; + void show_toolbox (bool visible); + lay::EditorOptionsPage *toolbox_widget (); + void show_message (); + void apply_partial_move (db::DPoint &ps); /** * @brief A handler for the shape container's changed event @@ -617,6 +644,7 @@ public slots: virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool mouse_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 bool key_event (unsigned int key, unsigned int buttons); virtual void deactivated (); void snap_rulers (lay::angle_constraint_type ac); diff --git a/src/ant/ant/antTemplate.cc b/src/ant/ant/antTemplate.cc index 58994d619..2c5c7d537 100644 --- a/src/ant/ant/antTemplate.cc +++ b/src/ant/ant/antTemplate.cc @@ -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