diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b421e2327..8c38ef2d2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -20,11 +20,11 @@ jobs:
max-parallel: 12
matrix:
include:
- - os: "macos-13" # intel runner
+ - os: "macos-15-intel" # intel runner
cibuild: "*macosx*"
cibw_arch: "macos_x86_64"
macos-arch: "x86_64"
- - os: "macos-14" # M1 runner
+ - os: "macos-latest" # M1 runner
cibuild: "*macosx*"
cibw_arch: "macos_arm64"
macos-arch: "arm64"
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 @@
00665
- 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_rbruler_diag_rbruler_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 @@ public:
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 @@ public:
}
}
- 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 @@ public:
}
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 @@ public:
}
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..73454b6b8 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 ()));
@@ -138,6 +138,15 @@ PluginDeclaration::create_plugin (db::Manager *manager, lay::Dispatcher *, lay::
return new ant::Service (manager, view);
}
+std::vector
+PluginDeclaration::additional_editor_options_pages () const
+{
+ std::vector names;
+ // TODO: provide in a central place instead of borrowing from the edt module
+ names.push_back ("GenericEditorOptions");
+ return names;
+}
+
bool
PluginDeclaration::menu_activated (const std::string &symbol) const
{
diff --git a/src/ant/ant/antPlugin.h b/src/ant/ant/antPlugin.h
index 2e2812506..ce632f4de 100644
--- a/src/ant/ant/antPlugin.h
+++ b/src/ant/ant/antPlugin.h
@@ -54,6 +54,8 @@ public:
virtual void uninitialize (lay::Dispatcher *);
virtual bool menu_activated (const std::string &symbol) const;
+ virtual std::vector additional_editor_options_pages () const;
+
void register_annotation_template (const ant::Template &t, lay::Plugin *plugin = 0);
void unregister_annotation_template (const std::string &category, lay::Plugin *plugin = 0);
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 == MoveP1) {
-
- 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_visual (m_p1, p, &m_current, ac));
- m_rulers [0]->redraw ();
-
- } 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 ();
-
- } 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 ();
-
- } 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 ();
-
- } 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 == 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 ();
-
- } else if (m_move_mode == MoveSelected) {
+ 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, ps);
+
+ } else if (m_move_mode == MoveP2) {
+
+ m_current.seg_p2 (m_seg_index, ps);
+
+ } else if (m_move_mode == MoveP12) {
+
+ 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) {
+
+ 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) {
+
+ 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 == MoveP2X) {
+
+ 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 == MoveP1Y) {
+
+ 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 ()));
+
+ } else if (m_move_mode == MoveP2Y) {
+
+ 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 @@ public:
*/
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 @@ public:
}
/**
- * @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 @@ private:
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 @@ private:
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 @@ private:
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 &v)
r += ",";
r += "angle_constraint=";
- ant::ACConverter acc;
+ lay::ACConverter acc;
r += acc.to_string (t->angle_constraint ());
}
diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc
index e5087c2e6..80a5eea04 100644
--- a/src/ant/ant/gsiDeclAnt.cc
+++ b/src/ant/ant/gsiDeclAnt.cc
@@ -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 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."
diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc
index 1cab8c0c3..a52acfa09 100644
--- a/src/buddies/src/bd/bdReaderOptions.cc
+++ b/src/buddies/src/bd/bdReaderOptions.cc
@@ -475,7 +475,7 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd)
"For example, a datatype specification of \"6,1:61,2:62\" will use datatype 6 for via geometry without a mask assignment, "
"datatype 61 for via geometry assigned to MASK 1 and datatype 62 for via geometry assigned to MASK 2.\n"
"\n"
- "An alternative way to provide a layer mapping is through a map file (see '--" + m_long_prefix + "lefdef-map-file')."
+ "An alternative way to provide a layer mapping is through a map file (see '--" + m_long_prefix + "lefdef-map')."
)
<< tl::arg (group +
"#--" + m_long_prefix + "lefdef-via-geometry-datatype", &m_lefdef_via_geometry_datatype, "Specifies the via geometry layer datatype in pattern-based mode",
diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc
index 3c83fe30c..d36373cee 100644
--- a/src/db/db/gsiDeclDbEdgePairs.cc
+++ b/src/db/db/gsiDeclDbEdgePairs.cc
@@ -31,6 +31,7 @@
#include "dbEdgesUtils.h"
#include "dbEdgePairFilters.h"
#include "dbPropertiesFilter.h"
+#include "dbRegionProcessors.h"
#include "gsiDeclDbContainerHelpers.h"
#include "gsiDeclDbMeasureHelpers.h"
@@ -531,6 +532,71 @@ static db::Region extents0 (const db::EdgePairs *r)
return extents2 (r, 0, 0);
}
+namespace {
+
+// a combined processor that implements db::RelativeExtents on the edge bounding boxes
+
+class EdgePairsRelativeExtents
+ : virtual public db::EdgePairToPolygonProcessorBase,
+ virtual public db::RelativeExtents
+{
+public:
+ EdgePairsRelativeExtents (double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy)
+ : db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy)
+ {
+ // .. nothing yet ..
+ }
+
+ // not needed, but mutes
+ void process (const db::PolygonWithProperties &poly, std::vector &result) const
+ {
+ db::RelativeExtents::process (poly, result);
+ }
+
+ void process (const db::EdgePairWithProperties &ep, std::vector &result) const
+ {
+ db::RelativeExtents::process (db::Polygon (ep.bbox ()), result);
+ }
+};
+
+class EdgePairsRelativeExtentsAsEdges
+ : virtual public db::EdgePairToEdgeProcessorBase,
+ virtual public db::RelativeExtentsAsEdges
+{
+public:
+ EdgePairsRelativeExtentsAsEdges (double fx1, double fy1, double fx2, double fy2)
+ : db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2)
+ {
+ // .. nothing yet ..
+ }
+
+ void process (const db::PolygonWithProperties &poly, std::vector &result) const
+ {
+ db::RelativeExtentsAsEdges::process (poly, result);
+ }
+
+ void process (const db::EdgePairWithProperties &ep, std::vector &result) const
+ {
+ db::RelativeExtentsAsEdges::process (db::Polygon (ep.bbox ()), result);
+ }
+};
+
+}
+
+static db::Region extent_refs (const db::EdgePairs *r, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy)
+{
+ db::Region result;
+ r->processed (result, EdgePairsRelativeExtents (fx1, fy1, fx2, fy2, dx, dy));
+ return result;
+}
+
+static db::Edges extent_refs_edges (const db::EdgePairs *r, double fx1, double fy1, double fx2, double fy2)
+{
+ db::Edges result;
+ r->processed (result, EdgePairsRelativeExtentsAsEdges (fx1, fy1, fx2, fy2));
+ return result;
+}
+
static db::Edges edges (const db::EdgePairs *ep)
{
db::Edges e;
@@ -1247,7 +1313,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"The boxes will not be merged, so it is possible to determine overlaps "
"of these boxes for example.\n"
) +
- method_ext ("filter", &filter, gsi::arg ("filter"),
+ method_ext ("extent_refs", &extent_refs,
+ "@hide\n"
+ "This method is provided for DRC implementation.\n"
+ ) +
+ method_ext ("extent_refs_edges", &extent_refs_edges,
+ "@hide\n"
+ "This method is provided for DRC implementation.\n"
+ ) +
+ method_ext ("filter", &filter, gsi::arg ("filter"),
"@brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection)\n"
"See \\EdgePairFilter for a description of this feature.\n"
"\n"
diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc
index 109101272..21d42a803 100644
--- a/src/db/db/gsiDeclDbEdges.cc
+++ b/src/db/db/gsiDeclDbEdges.cc
@@ -32,6 +32,7 @@
#include "dbOriginalLayerRegion.h"
#include "dbLayoutUtils.h"
#include "dbPropertiesFilter.h"
+#include "dbRegionProcessors.h"
#include "gsiDeclDbContainerHelpers.h"
#include "gsiDeclDbMeasureHelpers.h"
@@ -941,6 +942,69 @@ static std::vector split_interacting_with_region (const db::Edges *r,
return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count));
}
+namespace {
+
+// a combined processor that implements db::RelativeExtents on the edge bounding boxes
+
+class EdgesRelativeExtents
+ : virtual public db::EdgeToPolygonProcessorBase,
+ virtual public db::RelativeExtents
+{
+public:
+ EdgesRelativeExtents (double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy)
+ : db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy)
+ {
+ // .. nothing yet ..
+ }
+
+ // not needed, but mutes
+ void process (const db::PolygonWithProperties &poly, std::vector &result) const
+ {
+ db::RelativeExtents::process (poly, result);
+ }
+
+ void process (const db::EdgeWithProperties &edge, std::vector &result) const
+ {
+ db::RelativeExtents::process (db::Polygon (edge.bbox ()), result);
+ }
+};
+
+class EdgesRelativeExtentsAsEdges
+ : virtual public db::EdgeProcessorBase,
+ virtual public db::RelativeExtentsAsEdges
+{
+public:
+ EdgesRelativeExtentsAsEdges (double fx1, double fy1, double fx2, double fy2)
+ : db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2)
+ {
+ // .. nothing yet ..
+ }
+
+ void process (const db::PolygonWithProperties &poly, std::vector &result) const
+ {
+ db::RelativeExtentsAsEdges::process (poly, result);
+ }
+
+ void process (const db::EdgeWithProperties &edge, std::vector &result) const
+ {
+ db::RelativeExtentsAsEdges::process (db::Polygon (edge.bbox ()), result);
+ }
+};
+
+}
+
+static db::Region extent_refs (const db::Edges *r, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy)
+{
+ db::Region result;
+ r->processed (result, EdgesRelativeExtents (fx1, fy1, fx2, fy2, dx, dy));
+ return result;
+}
+
+static db::Edges extent_refs_edges (const db::Edges *r, double fx1, double fy1, double fx2, double fy2)
+{
+ return r->processed (EdgesRelativeExtentsAsEdges (fx1, fy1, fx2, fy2));
+}
+
static tl::Variant nth (const db::Edges *edges, size_t n)
{
const db::Edge *e = edges->nth (n);
@@ -2372,6 +2436,14 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges",
"The boxes will not be merged, so it is possible to determine overlaps "
"of these boxes for example.\n"
) +
+ method_ext ("extent_refs", &extent_refs,
+ "@hide\n"
+ "This method is provided for DRC implementation.\n"
+ ) +
+ method_ext ("extent_refs_edges", &extent_refs_edges,
+ "@hide\n"
+ "This method is provided for DRC implementation.\n"
+ ) +
method_ext ("extended_in", &extended_in, gsi::arg ("e"),
"@brief Returns a region with shapes representing the edges with the given width\n"
"@param e The extension width\n"
diff --git a/src/doc/doc/about/about_libraries.xml b/src/doc/doc/about/about_libraries.xml
index 86debab30..70ee02d31 100644
--- a/src/doc/doc/about/about_libraries.xml
+++ b/src/doc/doc/about/about_libraries.xml
@@ -4,8 +4,8 @@
About Libraries
- Library
- Libraries
+
+
Starting with version 0.22, KLayout offers a library concept.
diff --git a/src/doc/doc/about/about_pcells.xml b/src/doc/doc/about/about_pcells.xml
index 8453f32d8..292d757cb 100644
--- a/src/doc/doc/about/about_pcells.xml
+++ b/src/doc/doc/about/about_pcells.xml
@@ -4,6 +4,7 @@
About PCells
+
Starting with version 0.22, KLayout offers parametrized cells (PCells).
diff --git a/src/doc/doc/about/basic_lib.xml b/src/doc/doc/about/basic_lib.xml
index 3fa5c1715..5689306cc 100644
--- a/src/doc/doc/about/basic_lib.xml
+++ b/src/doc/doc/about/basic_lib.xml
@@ -4,6 +4,7 @@
About The Basic Library
+
diff --git a/src/doc/doc/about/connectivity.xml b/src/doc/doc/about/connectivity.xml
index 1118c2211..e2f8f51c6 100644
--- a/src/doc/doc/about/connectivity.xml
+++ b/src/doc/doc/about/connectivity.xml
@@ -4,6 +4,7 @@
Connectivity
+
Use the connectivity page to specify the conductor layers and their connections.
diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml
index 203941a1d..3974cf751 100644
--- a/src/doc/doc/about/drc_ref.xml
+++ b/src/doc/doc/about/drc_ref.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml
index a4ad2a239..78e0e1744 100644
--- a/src/doc/doc/about/drc_ref_drc.xml
+++ b/src/doc/doc/about/drc_ref_drc.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml
index 735374f88..da588bf12 100644
--- a/src/doc/doc/about/drc_ref_global.xml
+++ b/src/doc/doc/about/drc_ref_global.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml
index df0a8c7cc..0e485e947 100644
--- a/src/doc/doc/about/drc_ref_layer.xml
+++ b/src/doc/doc/about/drc_ref_layer.xml
@@ -1,7 +1,7 @@
-
+
@@ -1182,20 +1182,19 @@ The formal specifiers for lines are:
:right or :r : the right line
-Dots are represented by small (2x2 DBU) boxes or point-like
+The following additional option controls the output format:
+
+
+
as_boxes : with this option, boxes (rectangular polygons) will be produced on output
+
as_dots or as_edges : with this option, edges will be produced on output
+
+
+Dots on are represented by small (2x2 DBU) boxes or point-like
edges with edge output. Lines are represented by narrow or
flat (2 DBU) boxes or edges for edge output. Edges will follow
the orientation convention for the corresponding edges - i.e.
"inside" of the bounding box is on the right side of the edge.
-The following additional option controls the output format:
-
-
-
as_boxes : with this option, small boxes will be produced as markers
-
as_dots or as_edges : with this option, point-like edges will be produced for dots
-and edges will be produced for line-like selections
Beside a ruby and Python programming API, KLayout provides support for simple expressions in some places.
diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml
index 58168f012..842f21820 100644
--- a/src/doc/doc/about/lvs_ref.xml
+++ b/src/doc/doc/about/lvs_ref.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml
index 494ef2f6e..d1121526c 100644
--- a/src/doc/doc/about/lvs_ref_global.xml
+++ b/src/doc/doc/about/lvs_ref_global.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml
index 2e1dbd89e..be3e6e538 100644
--- a/src/doc/doc/about/lvs_ref_netter.xml
+++ b/src/doc/doc/about/lvs_ref_netter.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/manual/create_box.xml b/src/doc/doc/manual/create_box.xml
index e7de4aed5..b2516cbcc 100644
--- a/src/doc/doc/manual/create_box.xml
+++ b/src/doc/doc/manual/create_box.xml
@@ -14,7 +14,15 @@
the second point. Press the ESC key to cancel the operation.
-
Hint: A box, once created, will remain a box. For example, it is not possible to delete one vertex
+
+ While you drag the box, two edit boxes are shown at the top of the layout view.
+ Press the Tab key to enter these edit boxes.
+ Use the Tab and Shift+Tab keys to navigate between the boxes.
+ You can specify numerical values for the box width and height here. Pressing the Enter key will apply these
+ dimensions. Pressing the Escape key will leave the edit fields.
+
+
+
Note: A box, once created, will remain a box. For example, it is not possible to delete one vertex
of it, thus forming a triangle. This is only possible for polygons.
diff --git a/src/doc/doc/manual/create_path.xml b/src/doc/doc/manual/create_path.xml
index e6763c061..d7c48e5ac 100644
--- a/src/doc/doc/manual/create_path.xml
+++ b/src/doc/doc/manual/create_path.xml
@@ -18,8 +18,24 @@
To actually draw a path,
choose a layer from the layer panel in which to create a new path.
Left click at the first vertex, move the mouse to the second vertex, click to place this one and continue
- to the last vertex. Double-click at the last vertex to finish the path. Press the ESC key to cancel the operation.
- Use the backspace key to remove the current segment and go back to the previous segment.
+ to the last vertex. Double-click at the last vertex or press the Enter key to finish the path.
+ Press the Escape key to cancel the operation.
+ Use the Backspace key to remove the current segment and go back to the previous segment.
+
+
+
+ To temporarily constrain the segment direction, press the Shift or Ctrl key or both while dragging
+ the segment. Shift will apply Manhattan
+ constraints (vertical and horizontal only), Ctrl will allow diagonal directions in addition and
+ pressing Shift+Ctrl will allow all directions.
+
+
+
+ While you drag a path segment, two edit boxes are shown at the top of the layout view.
+ Press the Tab key to enter these edit boxes.
+ Use the Tab and Shift+Tab keys to navigate between the boxes.
+ You can specify a numerical values for the segment vector here. Pressing the Enter key will apply these
+ relative coordinates and enter a new segment. Pressing the Escape key will leave the edit fields.
diff --git a/src/doc/doc/manual/create_polygon.xml b/src/doc/doc/manual/create_polygon.xml
index 1d1d42f33..6e00a85e5 100644
--- a/src/doc/doc/manual/create_polygon.xml
+++ b/src/doc/doc/manual/create_polygon.xml
@@ -14,11 +14,28 @@
with a left mouse button click. Move to the next vertex. Depending on the connection mode, the edges
created are confined to certain directions. See
for a detailed description of the modes. Use the "editor options" dialog (F3 shortcut) to change the mode,
- even during editing.
+ even during editing.
- Double-click at the final point to finish the polygon. Press the ESC key to cancel the operation.
+ To temporarily constrain the segment direction, press the Shift or Ctrl key or both while dragging
+ the segment. Shift will apply Manhattan
+ constraints (vertical and horizontal only), Ctrl will allow diagonal directions in addition and
+ pressing Shift+Ctrl will allow all directions.
+
+
+
+ Double-click at the final point or press the Enter key to finish the polygon.
+ Press the Escape key to cancel the operation. Use the Backspace key to delete the
+ last segment.
+
+
+
+ While you drag a polygon segment, two edit boxes are shown at the top of the layout view.
+ Press the Tab key to enter these edit boxes.
+ Use the Tab and Shift+Tab keys to navigate between the boxes.
+ You can specify a numerical values for the segment vector here. Pressing the Enter key will apply these
+ relative coordinates and enter a new segment. Pressing the Escape key will leave the edit fields.
A measurement can be performed by clicking on the ruler icon in the
toolbar and selecting "Ruler" from the drop-down options.
- Left-click on a point in the layout and then left-click again to
- specify the second point. A ruler will be shown that indicates
- the distance measured.
+ Left-click on a point in the layout and then left-click again
+ or press the Enter key to set the second point.
+ A ruler will be shown that indicates the distance measured.
- A more convenient way is provided with the single-click measurement
+ While you move the endpoint, you can hold the Shift or Ctrl key
+ or both. With only the Shift key pressed, the ruler's direction
+ will be limited to horizontal or vertical only. With only the Ctrl
+ key pressed, the direction is limited to horizontal, vertical or
+ diagonal. With Ctrl and Shift pressed together, no limitation
+ of direction applies.
+
+
+
+ While the ruler is dragged, the current drag distance is indicated
+ in three edit fields at the top of the layout view. By pressing the
+ Tab key, the input focus changes to these edit fields. You can
+ specify a numerical value for the distances here. Use Tab and
+ Shift+Tab to jump between the fields. Press the Escape key to
+ leave the edit fields. Pressing the Enter key while
+ the cursor is on the "dx" or "dy" field will
+ accept the values and apply them to the ruler.
+ If you specify a value in the last field labelled "d" and press
+ Enter, the ruler will enter "fixed length" mode - in that mode you
+ can still define the ruler's direction with the mouse, but the
+ length is fixed to the given value.
+
+
+
+ A convenient way to measure a distance is the single-click measurement
ruler. Select "Measure" from the drop-down options of the ruler symbol.
In this mode, a single click will set a ruler to the specified
position. This feature will look for edges in the vicinity of the
@@ -24,6 +48,11 @@
is attached perpendicular to the edge next to the initial point.
+
+ The "Measure Edge" ruler type is also a single-click measurement
+ ruler, but measures the length of an edge at the click position.
+
+
You can mark a position with a single click by selecting the "Cross"
ruler type. Clicking at a location will place such a ruler. The ruler
@@ -35,7 +64,12 @@
object. Click and the first point to start such a ruler. Then click
on more points to add new segments to the ruler. Each segment is shown
as an individual ruler with tick marks and a length. Finish the sequence
- with a double-click.
+ with a double-click or by pressing the Enter key.
+ Note that the lengths indicated by the ruler labels are the lengths
+ of the individual segments. To get an incremental length (the sum
+ of all segment length), change the ruler's label format from "$D" to "$DD" ("DD"
+ is variable giving the incremental length).
+ See the description of ruler templates below, about how to make this change permanent.
diff --git a/src/doc/doc/manual/move_layer_sel.xml b/src/doc/doc/manual/move_layer_sel.xml
index 997fd5954..ece22ce01 100644
--- a/src/doc/doc/manual/move_layer_sel.xml
+++ b/src/doc/doc/manual/move_layer_sel.xml
@@ -12,10 +12,16 @@
All selected shapes are moved to the layer that is the current one (marked with a rectangle) in the layer list.
The shapes will not be moved across the hierarchy but just inside their cell.
+
All layers (source and target) must be located in the same layout. To move shapes to a
different layout, use copy & paste.
+
+ You can also change the layer in the shape properties. This will effectively
+ move shapes to other layers too.
+
While moving, the whole selection can be rotated by 90 degree counterclockwise with a right mouse
button click.
- The ESC key will cancel the operation.
+ The Escape key will cancel the operation.
For movements, the movement direction constraint apply.
See for details about the modes
- available. For example, in manhattan mode, only horizontal and vertical movements are allowed.
+ available. For example, in Manhattan mode, only horizontal and vertical movements are allowed.
The global movement constraint can be overridden by pressing Shift (orthogonal), Ctrl (diagonal) or
both Shift and Ctrl (any angle) while moving the mouse.
- If a move distance and direction is known numerically, "Move By" from the "Edit/Selection" menu can be used.
+ While you move the selection, two edit boxes are shown at the top of the layout view.
+ Press the Tab key to enter these edit boxes.
+ Use the Tab and Shift+Tab keys to navigate between the boxes.
+ You can specify a numerical shift value here. Pressing the Enter key will apply these
+ shifts. Pressing the Escape key will leave the edit fields.
+
+
+
+ To apply a specific shift, you can also use "Move By" from the "Edit/Selection" menu.
A dialog will open that allows specification of the horizontal and vertical move distance in micrometers.
Positive values move to the top or right and negative ones to the bottom or left.
This dialog also applies to partial mode, so that edges or parts of a layout can be
diff --git a/src/doc/doc/manual/partial.xml b/src/doc/doc/manual/partial.xml
index db0bed8d9..adda0fdc0 100644
--- a/src/doc/doc/manual/partial.xml
+++ b/src/doc/doc/manual/partial.xml
@@ -35,7 +35,9 @@
Simply clicking at an item immediately enters "move" mode. In this mode, you can position the element at the desired
target location and place it there by left-clicking at the position. Press "ESC" to cancel the operation.
When a complex selection is made, move mode is entered by clicking at one of the selected items (the edges
- or vertices, not the shape to which they belong).
+ or vertices, not the shape to which they belong). While you move, two edit boxes are shown at the top of
+ the layout view. Press the Tab key to enter these edit fields. You can specify explicit move distances there.
+ Press the Enter key to apply them or the Escape key to leave these edit fields.
$X: The value of the X variable (the horizontal distance, see below for a complete list of variables).
-
$(sprintf('%.2f',X)): The value of the 'X' variable formatted as two digit fixed precision value.
-
$(abs(X)+abs(Y)): The manhattan distance of the ruler.
+
$(sprintf('%.2f',X)): The value of the 'X' variable formatted as two digit fixed precision value.
+ The "sprintf" function follows the conventions of the same standard C function.
+
$(abs(X)+abs(Y)): The Manhattan distance of the ruler.
$min(X,Y): The minimum of X and Y.
+
$(X) ($Y): the value of the X variable, followed by the value of the Y variables in brackets.
+ This will give a string like "2.5 (-0.5)". Note
+ that you cannot simply write "$X ($Y)" because the expression evaluation reads that as an attempt to call a
+ function named "X".
@@ -68,7 +73,7 @@
D: The length of the ruler in micron units.
-
L: The manhattan length of the ruler in micron units.
+
L: The Manhattan length of the ruler in micron units.
U: The x-position of the ruler's first point in micron units.
V: The y-position of the ruler's first point in micron units.
P: The x-position of the ruler's second point in micron units.
@@ -79,5 +84,17 @@
G: The angle enclosed by the first and last segment of the ruler (used for angle measurement rulers).
+
+ For multi-rulers additional variables are provided for "incremental" values.
+ These are the sums of the respective values up to the given part:
+
+
+
+
DD: The sum of all lengths up to the labelled segment.
+
LL: The sum of all Manhattan up to the labelled segment.
+
XX: The horizontal distance between first and current point.
+
YY: The vertical distance between first and current point.
" << std::endl;
+ }
+
+ // Inserts an index
+
os << "" << std::endl;
+ // Produce class doc body
+
+ if (class_doc.hidden && class_doc.alias.empty ()) {
+ os << "
"
+ << tl::to_string (QObject::tr ("Note"))
+ << ": "
+ << tl::to_string (QObject::tr (
+ "This class is an internal class provided for technical reasons - i.e. "
+ "as a placeholder class for argument binding or as an abstract interface. "
+ "You should not instantiate objects of this class directly. "
+ "Instead, use the subclasses listed above. "
+ "Also see there for more documentation and actual incarnations of this class."
+ ))
+ << "
" << std::endl;
+ }
+
os << replace_references (class_doc.doc_html (), cls_obj) << std::endl;
// collect the methods of the class and their hidden base classes
@@ -1116,6 +1179,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
os << "" << std::endl;
return os.str ();
}
+
+ // Produce methods brief descriptions
int n = 0;
int row = 0;
@@ -1190,6 +1255,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
os << "
" << std::endl;
}
+ // Produce static methods brief descriptions
+
any = false;
n = 0;
@@ -1230,6 +1297,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
os << "" << std::endl;
}
+ // Produce protected methods brief descriptions
+
any = false;
n = 0;
@@ -1272,6 +1341,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
os << "" << std::endl;
}
+ // Produce deprecated methods brief descriptions
+
any = false;
n = 0;
@@ -1323,6 +1394,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
os << "" << std::endl;
}
+ // Produce method details
+
n = 0;
os << "