diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui
index ac380684e..7e2c2365c 100644
--- a/src/ant/ant/RulerConfigPage4.ui
+++ b/src/ant/ant/RulerConfigPage4.ui
@@ -89,7 +89,7 @@
...
-
+ :/up_16px.png:/up_16px.png
@@ -103,7 +103,7 @@
...
-
+ :/add_16px.png:/add_16px.png
@@ -117,7 +117,7 @@
...
-
+ :/del_16px.png:/del_16px.png
@@ -131,7 +131,7 @@
...
-
+ :/down_16px.png:/down_16px.png
@@ -574,6 +574,16 @@
Ellipse
+
+
+ Angle measurement
+
+
+
+
+ Radius measurement
+
+
@@ -833,6 +843,16 @@
Auto measure (points will be set automatically)
+
+
+ Angle measurement (three mouse clicks)
+
+
+
+
+ Multi-segment (finish with double click)
+
+
@@ -866,7 +886,7 @@
t_snap_cbx
-
+
diff --git a/src/ant/ant/RulerPropertiesPage.ui b/src/ant/ant/RulerPropertiesPage.ui
index 5e4497bbd..28451b680 100644
--- a/src/ant/ant/RulerPropertiesPage.ui
+++ b/src/ant/ant/RulerPropertiesPage.ui
@@ -13,10 +13,7 @@
Form
-
-
- 6
-
+ 9
@@ -29,7 +26,7 @@
9
-
+ QFrame::NoFrame
@@ -53,13 +50,95 @@
6
-
-
+
+
- Second point (x/y)
+ X label format
+
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Label format
+
+
+
+
+
+
+ Y label format
+
+
+
+
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 5
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
@@ -67,173 +146,31 @@
-
-
+
+
- <html>(See <a href="int:/manual/ruler_properties.xml">here</a> for a description of the properties)</html>
+ Outline
-
-
+
+
+
+
+ ... position
-
-
-
- Qt::Vertical
-
-
-
- 20
- 5
-
-
-
-
-
-
-
-
-
-
-
- Diagonal
-
-
-
-
- Horizonal and vertical (in this order)
-
-
-
-
- Diagonal plus horizonal and vertical (triangle)
-
-
-
-
- Vertical and horizonal (in this order)
-
-
-
-
- Diagonal plus vertical and horizontal (triangle)
-
-
-
-
- Box
-
-
-
-
- Ellipse
-
-
-
-
-
-
+
+
- d =
+ ... position
-
-
-
- Qt::Vertical
-
-
-
- 20
- 5
-
-
-
-
-
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 456
- 16
-
-
-
-
-
-
-
-
- Ruler
-
-
-
-
- Arrow at end
-
-
-
-
- Arrow at start
-
-
-
-
- Arrow at both ends
-
-
-
-
- Plain line
-
-
-
-
- Cross at end
-
-
-
-
- Cross at start
-
-
-
-
- Cross at both ends
-
-
-
-
-
-
-
-
- 1
- 0
-
-
-
-
-
-
-
- Delta (x/y)
-
-
-
-
+ QFrame::NoFrame
@@ -378,42 +315,186 @@
-
-
+
+
+
+
+ Diagonal
+
+
+
+
+ Horizonal and vertical (in this order)
+
+
+
+
+ Diagonal plus horizonal and vertical (triangle)
+
+
+
+
+ Vertical and horizonal (in this order)
+
+
+
+
+ Diagonal plus vertical and horizontal (triangle)
+
+
+
+
+ Box
+
+
+
+
+ Ellipse
+
+
+
+
+ Angle measurement
+
+
+
+
+ Radius measurement
+
+
+
+
+
+
+
+
+ Ruler
+
+
+
+
+ Arrow at end
+
+
+
+
+ Arrow at start
+
+
+
+
+ Arrow at both ends
+
+
+
+
+ Plain line
+
+
+
+
+ Cross at end
+
+
+
+
+ Cross at start
+
+
+
+
+ Cross at both ends
+
+
+
+
+
+
+
+ <html>(See <a href="int:/manual/ruler_properties.xml">here</a> for a description of the properties)</html>
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ Sans Serif
+ 12
+ false
+ true
+ false
+ false
+
+
+
+ Ruler Properties
+
+
+
+
+
- Qt::Horizontal
+ Qt::Vertical
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 5
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 5
+
+
+
+
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
-
-
- x =
-
-
-
-
-
-
- Label format
-
-
-
-
-
-
- Length
-
-
-
-
-
-
- Outline
-
-
-
-
+ QFrame::NoFrame
@@ -524,210 +605,7 @@
-
-
-
-
- 1
- 0
-
-
-
- true
-
-
-
-
-
-
-
- 1
- 0
-
-
-
- true
-
-
-
-
-
-
- First point (x/y)
-
-
-
-
-
-
-
- 1
- 0
-
-
-
-
-
-
-
- y =
-
-
-
-
-
-
- QFrame::NoFrame
-
-
- QFrame::Raised
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
- Swap points
-
-
-
-
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 10
- 20
-
-
-
-
-
-
-
- Snap to layout:
-
-
-
-
-
-
- P1
-
-
- false
-
-
-
-
-
-
- P2
-
-
- false
-
-
-
-
-
-
- Both (auto-measure)
-
-
- false
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
-
-
- Qt::Vertical
-
-
-
- 20
- 5
-
-
-
-
-
-
-
- y =
-
-
-
-
-
-
-
-
-
-
- 1
- 0
-
-
-
- true
-
-
-
-
-
-
-
- 1
- 0
-
-
-
-
-
-
-
- ... position
-
-
-
-
-
-
-
+ QFrame::NoFrame
@@ -838,98 +716,429 @@
-
-
+
+ ... position
-
-
-
- x =
-
-
-
-
-
+
+
-
- 1
- 0
-
-
-
-
-
-
-
- Y label format
-
-
-
-
-
-
- X label format
-
-
-
-
-
-
- y =
-
-
-
-
-
-
- x =
-
-
-
-
-
-
-
+ 0
- 0
+ 1
-
-
- Sans Serif
- 12
- 75
- false
- true
- false
- false
-
+
+
+ 0
+ 100
+
-
- Ruler Properties
+
+ 1
+
+
+ Single
+
+
+
+
+
+ First point (x/y)
+
+
+
+
+
+
+ x =
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+ y =
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 558
+ 157
+
+
+
+
+
+
+
+
+ Line
+
+
+
+
+
+ y =
+
+
+
+
+
+
+ First point (x/y)
+
+
+
+
+
+
+ d =
+
+
+
+
+
+
+ y =
+
+
+
+
+
+
+ y =
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+ true
+
+
+
+
+
+
+ Length
+
+
+
+
+
+
+ x =
+
+
+
+
+
+
+ Delta (x/y)
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+ Second point (x/y)
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+ x =
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+ true
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ x =
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+ true
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ 0
+ 30
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+ Swap points
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 5
+ 20
+
+
+
+
+
+
+
+ Snap to layout
+
+
+
+
+
+
+ P1
+
+
+ false
+
+
+
+
+
+
+ P2
+
+
+ false
+
+
+
+
+
+
+ Both (auto-measure)
+
+
+ false
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+ Multi-Segment
+
+
+
+
+
+ false
+
+
+ true
+
+
+
+ x
+
+
+
+
+ y
+
+
+
+
+
+
+
+
+ Text edit
+
+
+
+
+
+ false
+
+
+
+
+
-
-
-
- Qt::Vertical
-
-
-
- 20
- 0
-
-
-
-
@@ -937,13 +1146,6 @@
fmt_y_lestyle_cboutline_cb
- x1
- y1
- x2
- y2
- dx
- dy
- dd
diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc
index 78356a41b..ed5230023 100644
--- a/src/ant/ant/antConfig.cc
+++ b/src/ant/ant/antConfig.cc
@@ -136,6 +136,10 @@ OutlineConverter::to_string (ant::Object::outline_type o)
return "box";
} else if (o == ant::Object::OL_ellipse) {
return "ellipse";
+ } else if (o == ant::Object::OL_radius) {
+ return "radius";
+ } else if (o == ant::Object::OL_angle) {
+ return "angle";
} else {
return "";
}
@@ -159,6 +163,10 @@ OutlineConverter::from_string (const std::string &s, ant::Object::outline_type &
o = ant::Object::OL_box;
} else if (t == "ellipse") {
o = ant::Object::OL_ellipse;
+ } else if (t == "radius") {
+ o = ant::Object::OL_radius;
+ } else if (t == "angle") {
+ o = ant::Object::OL_angle;
} else {
o = ant::Object::OL_diag;
}
@@ -239,6 +247,10 @@ RulerModeConverter::to_string (ant::Template::ruler_mode_type m)
return "single_click";
} else if (m == ant::Template::RulerAutoMetric) {
return "auto_metric";
+ } else if (m == ant::Template::RulerMultiSegment) {
+ return "multi_segment";
+ } else if (m == ant::Template::RulerThreeClicks) {
+ return "angle";
} else {
return "normal";
}
@@ -254,6 +266,10 @@ RulerModeConverter::from_string (const std::string &s, ant::Template::ruler_mode
a = ant::Template::RulerSingleClick;
} else if (t == "auto_metric") {
a = ant::Template::RulerAutoMetric;
+ } else if (t == "multi_segment") {
+ a = ant::Template::RulerMultiSegment;
+ } else if (t == "angle") {
+ a = ant::Template::RulerThreeClicks;
} else {
a = ant::Template::RulerNormal;
}
diff --git a/src/ant/ant/antObject.cc b/src/ant/ant/antObject.cc
index 43bdf736b..41d750642 100644
--- a/src/ant/ant/antObject.cc
+++ b/src/ant/ant/antObject.cc
@@ -32,8 +32,26 @@
namespace ant
{
+static void
+clean_points_impl (ant::Object::point_list &points)
+{
+ auto wp = points.begin ();
+ auto p = points.begin ();
+ while (p != points.end ()) {
+ auto pp = p + 1;
+ while (pp != points.end () && *pp == *p) {
+ ++pp;
+ }
+ *wp++ = *p;
+ p = pp;
+ }
+
+ points.erase (wp, points.end ());
+}
+
+
Object::Object ()
- : m_p1 (), m_p2 (), m_id (-1),
+ : m_id (-1),
m_fmt_x ("$X"), m_fmt_y ("$Y"), m_fmt ("$D"),
m_style (STY_ruler), m_outline (OL_diag),
m_snap (true), m_angle_constraint (lay::AC_Global),
@@ -45,8 +63,8 @@ Object::Object ()
// .. nothing yet ..
}
-Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint)
- : m_p1 (p1), m_p2 (p2), m_id (id),
+Object::Object (const db::DPoint &_p1, const db::DPoint &_p2, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint)
+ : m_id (id),
m_fmt_x (fmt_x), m_fmt_y (fmt_y), m_fmt (fmt),
m_style (style), m_outline (outline),
m_snap (snap), m_angle_constraint (angle_constraint),
@@ -55,11 +73,25 @@ Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const std::s
m_xlabel_xalign (AL_auto), m_xlabel_yalign (AL_auto),
m_ylabel_xalign (AL_auto), m_ylabel_yalign (AL_auto)
{
- // .. nothing else ..
+ p1 (_p1);
+ p2 (_p2);
}
-Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const ant::Template &t)
- : m_p1 (p1), m_p2 (p2), m_id (id),
+Object::Object (const Object::point_list &pts, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint)
+ : m_id (id),
+ m_fmt_x (fmt_x), m_fmt_y (fmt_y), m_fmt (fmt),
+ m_style (style), m_outline (outline),
+ m_snap (snap), m_angle_constraint (angle_constraint),
+ m_main_position (POS_auto),
+ m_main_xalign (AL_auto), m_main_yalign (AL_auto),
+ m_xlabel_xalign (AL_auto), m_xlabel_yalign (AL_auto),
+ m_ylabel_xalign (AL_auto), m_ylabel_yalign (AL_auto)
+{
+ set_points (pts);
+}
+
+Object::Object (const db::DPoint &_p1, const db::DPoint &_p2, int id, const ant::Template &t)
+ : m_id (id),
m_fmt_x (t.fmt_x ()), m_fmt_y (t.fmt_y ()), m_fmt (t.fmt ()),
m_style (t.style ()), m_outline (t.outline ()),
m_snap (t.snap ()), m_angle_constraint (t.angle_constraint ()),
@@ -69,11 +101,26 @@ Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const ant::T
m_xlabel_xalign (t.xlabel_xalign ()), m_xlabel_yalign (t.xlabel_yalign ()),
m_ylabel_xalign (t.ylabel_xalign ()), m_ylabel_yalign (t.ylabel_yalign ())
{
- // .. nothing else ..
+ p1 (_p1);
+ p2 (_p2);
+}
+
+Object::Object (const Object::point_list &pts, int id, const ant::Template &t)
+ : m_points (pts), m_id (id),
+ m_fmt_x (t.fmt_x ()), m_fmt_y (t.fmt_y ()), m_fmt (t.fmt ()),
+ m_style (t.style ()), m_outline (t.outline ()),
+ m_snap (t.snap ()), m_angle_constraint (t.angle_constraint ()),
+ m_category (t.category ()),
+ m_main_position (t.main_position ()),
+ m_main_xalign (t.main_xalign ()), m_main_yalign (t.main_yalign ()),
+ m_xlabel_xalign (t.xlabel_xalign ()), m_xlabel_yalign (t.xlabel_yalign ()),
+ m_ylabel_xalign (t.ylabel_xalign ()), m_ylabel_yalign (t.ylabel_yalign ())
+{
+ clean_points_impl (m_points);
}
Object::Object (const ant::Object &d)
- : m_p1 (d.m_p1), m_p2 (d.m_p2), m_id (d.m_id),
+ : m_points (d.m_points), m_id (d.m_id),
m_fmt_x (d.m_fmt_x), m_fmt_y (d.m_fmt_y), m_fmt (d.m_fmt),
m_style (d.m_style), m_outline (d.m_outline),
m_snap (d.m_snap), m_angle_constraint (d.m_angle_constraint),
@@ -90,8 +137,7 @@ Object &
Object::operator= (const ant::Object &d)
{
if (this != &d) {
- m_p1 = d.m_p1;
- m_p2 = d.m_p2;
+ m_points = d.m_points;
m_id = d.m_id;
m_fmt_x = d.m_fmt_x;
m_fmt_y = d.m_fmt_y;
@@ -119,11 +165,8 @@ Object::operator< (const ant::Object &b) const
if (m_id != b.m_id) {
return m_id < b.m_id;
}
- if (m_p1 != b.m_p1) {
- return m_p1 < b.m_p1;
- }
- if (m_p2 != b.m_p2) {
- return m_p2 < b.m_p2;
+ if (m_points != b.m_points) {
+ return m_points < b.m_points;
}
if (m_fmt_x != b.m_fmt_x) {
return m_fmt_x < b.m_fmt_x;
@@ -187,7 +230,7 @@ Object::equals (const db::DUserObjectBase *d) const
bool
Object::operator== (const ant::Object &d) const
{
- return m_p1 == d.m_p1 && m_p2 == d.m_p2 && m_id == d.m_id &&
+ return m_points == d.m_points && m_id == d.m_id &&
m_fmt_x == d.m_fmt_x && m_fmt_y == d.m_fmt_y && m_fmt == d.m_fmt &&
m_style == d.m_style && m_outline == d.m_outline &&
m_snap == d.m_snap && m_angle_constraint == d.m_angle_constraint &&
@@ -199,6 +242,129 @@ Object::operator== (const ant::Object &d) const
;
}
+void
+Object::clean_points ()
+{
+ auto new_points = m_points;
+ clean_points_impl (new_points);
+ set_points_exact (std::move (new_points));
+}
+
+void
+Object::set_points (const point_list &points)
+{
+ auto new_points = points;
+ clean_points_impl (new_points);
+ set_points_exact (std::move (new_points));
+}
+
+void
+Object::set_points_exact (const point_list &points)
+{
+ if (m_points != points) {
+ m_points = points;
+ property_changed ();
+ }
+}
+
+void
+Object::set_points_exact (point_list &&points)
+{
+ if (m_points != points) {
+ m_points.swap (points);
+ property_changed ();
+ }
+}
+
+db::DPoint
+Object::seg_p1 (size_t seg_index) const
+{
+ if (seg_index == std::numeric_limits::max ()) {
+ return p1 ();
+ } else if (seg_index < m_points.size ()) {
+ return m_points[seg_index];
+ } else if (m_points.empty ()) {
+ return db::DPoint ();
+ } else {
+ return m_points.back ();
+ }
+}
+
+db::DPoint
+Object::seg_p2 (size_t seg_index) const
+{
+ if (seg_index == std::numeric_limits::max ()) {
+ return p2 ();
+ } else if (seg_index + 1 < m_points.size ()) {
+ return m_points[seg_index + 1];
+ } else if (m_points.empty ()) {
+ return db::DPoint ();
+ } else {
+ return m_points.back ();
+ }
+}
+
+void
+Object::seg_p1 (size_t seg_index, const db::DPoint &p)
+{
+ if (seg_index == std::numeric_limits::max ()) {
+ p1 (p);
+ } else if (seg_index < m_points.size ()) {
+ m_points[seg_index] = p;
+ } else if (! m_points.empty ()) {
+ m_points.back () = p;
+ }
+}
+
+void
+Object::seg_p2 (size_t seg_index, const db::DPoint &p)
+{
+ if (seg_index == std::numeric_limits::max ()) {
+ p2 (p);
+ } else if (seg_index + 1 < m_points.size ()) {
+ m_points[seg_index + 1] = p;
+ } else if (! m_points.empty ()) {
+ m_points.back () = p;
+ }
+}
+
+void
+Object::p1 (const db::DPoint &p)
+{
+ if (! p1 ().equal (p)) {
+ if (m_points.size () < 1) {
+ m_points.push_back (p);
+ } else {
+ m_points.front () = p;
+ // makes sure there is only one point if p1 == p2
+ if (m_points.size () == 2 && m_points.back () == m_points.front ()) {
+ m_points.pop_back ();
+ }
+ }
+ property_changed ();
+ }
+}
+
+void
+Object::p2 (const db::DPoint &p)
+{
+ if (! p2 ().equal (p)) {
+ if (m_points.size () < 2) {
+ if (m_points.empty ()) {
+ m_points.push_back (db::DPoint ());
+ }
+ m_points.push_back (p);
+ } else {
+ m_points.back () = p;
+ }
+ // makes sure there is only one point if p1 == p2
+ if (m_points.size () == 2 && m_points.back () == m_points.front ()) {
+ m_points.pop_back ();
+ }
+ property_changed ();
+ }
+}
+
bool
Object::less (const db::DUserObjectBase *d) const
{
@@ -226,7 +392,11 @@ Object::clone () const
db::DBox
Object::box () const
{
- return db::DBox (m_p1, m_p2);
+ db::DBox bx;
+ for (auto d = m_points.begin (); d != m_points.end (); ++d) {
+ bx += *d;
+ }
+ return bx;
}
class AnnotationEval
@@ -245,38 +415,12 @@ private:
db::DFTrans m_trans;
};
-static double
-delta_x (const Object &obj, const db::DFTrans &t)
-{
- double dx = ((t * obj.p2 ()).x () - (t * obj.p1 ()).x ());
-
- // avoid "almost 0" outputs
- if (fabs (dx) < 1e-5 /*micron*/) {
- dx = 0;
- }
-
- return dx;
-}
-
-static double
-delta_y (const Object &obj, const db::DFTrans &t)
-{
- double dy = ((t * obj.p2 ()).y () - (t * obj.p1 ()).y ());
-
- // avoid "almost 0" outputs
- if (fabs (dy) < 1e-5 /*micron*/) {
- dy = 0;
- }
-
- return dy;
-}
-
class AnnotationEvalFunction
: public tl::EvalFunction
{
public:
- AnnotationEvalFunction (char function, const AnnotationEval *eval)
- : m_function (function), mp_eval (eval)
+ AnnotationEvalFunction (char function, const AnnotationEval *eval, size_t index)
+ : m_function (function), mp_eval (eval), m_index (index)
{
// .. nothing yet ..
}
@@ -301,36 +445,82 @@ public:
} else if (m_function == 'Y') {
out = delta_y (obj, trans);
} else if (m_function == 'U') {
- out = (trans * obj.p1 ()).x ();
+ out = (trans * p1 (obj)).x ();
} else if (m_function == 'V') {
- out = (trans * obj.p1 ()).y ();
+ out = (trans * p1 (obj)).y ();
} else if (m_function == 'P') {
- out = (trans * obj.p2 ()).x ();
+ out = (trans * p2 (obj)).x ();
} else if (m_function == 'Q') {
- out = (trans * obj.p2 ()).y ();
+ out = (trans * p2 (obj)).y ();
+ } else if (m_function == 'G') {
+ double r, a1, a2;
+ db::DPoint c;
+ if (obj.compute_angle_parameters (r, c, a1, a2)) {
+ out = tl::Variant ((a2 - a1) * 180.0 / M_PI);
+ } else {
+ out = tl::Variant ();
+ }
} else {
out = tl::Variant ();
}
}
+ db::DPoint p1 (const Object &obj) const
+ {
+ return obj.seg_p1 (m_index);
+ }
+
+ db::DPoint p2 (const Object &obj) const
+ {
+ return obj.seg_p2 (m_index);
+ }
+
+ double
+ delta_x (const Object &obj, const db::DFTrans &t) const
+ {
+ double dx = ((t * p2 (obj)).x () - (t * p1 (obj)).x ());
+
+ // avoid "almost 0" outputs
+ if (fabs (dx) < 1e-5 /*micron*/) {
+ dx = 0;
+ }
+
+ return dx;
+ }
+
+ double
+ delta_y (const Object &obj, const db::DFTrans &t) const
+ {
+ double dy = ((t * p2 (obj)).y () - (t * p1 (obj)).y ());
+
+ // avoid "almost 0" outputs
+ if (fabs (dy) < 1e-5 /*micron*/) {
+ dy = 0;
+ }
+
+ return dy;
+ }
+
private:
char m_function;
const AnnotationEval *mp_eval;
+ size_t m_index;
};
std::string
-Object::formatted (const std::string &fmt, const db::DFTrans &t) const
+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)); // manhattan length
- eval.define_function ("D", new AnnotationEvalFunction('D', &eval)); // euclidian distance
- eval.define_function ("X", new AnnotationEvalFunction('X', &eval)); // x delta
- eval.define_function ("Y", new AnnotationEvalFunction('Y', &eval)); // y delta
- eval.define_function ("U", new AnnotationEvalFunction('U', &eval)); // p1.x
- eval.define_function ("V", new AnnotationEvalFunction('V', &eval)); // p1.y
- eval.define_function ("P", new AnnotationEvalFunction('P', &eval)); // p2.x
- eval.define_function ("Q", new AnnotationEvalFunction('Q', &eval)); // p2.y
- eval.define_function ("A", new AnnotationEvalFunction('A', &eval)); // area mm2
+ 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)
return eval.interpolate (fmt);
}
@@ -343,6 +533,9 @@ Object::class_name () const
void
Object::from_string (const char *s, const char * /*base_dir*/)
{
+ m_points.clear ();
+ point_list new_points;
+
tl::Extractor ex (s);
while (! ex.at_end ()) {
@@ -408,6 +601,14 @@ Object::from_string (const char *s, const char * /*base_dir*/)
p.set_y (q);
p2 (p);
+ } else if (ex.test ("pt=")) {
+
+ double x = 0.0, y = 0.0;
+ ex.read (x);
+ ex.expect (":");
+ ex.read (y);
+ new_points.push_back (db::DPoint (x, y));
+
} else if (ex.test ("position=")) {
std::string s;
@@ -518,6 +719,10 @@ Object::from_string (const char *s, const char * /*base_dir*/)
ex.test (",");
}
+
+ if (! new_points.empty ()) {
+ set_points (new_points);
+ }
}
std::string
@@ -529,18 +734,28 @@ Object::to_string () const
r += tl::to_string (id ());
r += ",";
- r += "x1=";
- r += tl::to_string (p1 ().x ());
- r += ",";
- r += "y1=";
- r += tl::to_string (p1 ().y ());
- r += ",";
- r += "x2=";
- r += tl::to_string (p2 ().x ());
- r += ",";
- r += "y2=";
- r += tl::to_string (p2 ().y ());
- r += ",";
+ if (m_points.size () > 2) {
+ for (auto p = m_points.begin (); p != m_points.end (); ++p) {
+ r += "pt=";
+ r += tl::to_string (p->x ());
+ r += ":";
+ r += tl::to_string (p->y ());
+ r += ",";
+ }
+ } else {
+ r += "x1=";
+ r += tl::to_string (p1 ().x ());
+ r += ",";
+ r += "y1=";
+ r += tl::to_string (p1 ().y ());
+ r += ",";
+ r += "x2=";
+ r += tl::to_string (p2 ().x ());
+ r += ",";
+ r += "y2=";
+ r += tl::to_string (p2 ().y ());
+ r += ",";
+ }
r += "category=";
r += tl::to_word_or_quoted_string (category ());
@@ -602,6 +817,117 @@ Object::to_string () const
return r;
}
+bool
+Object::compute_interpolating_circle (double &radius, db::DPoint ¢er, double &start_angle, double &stop_angle) const
+{
+ if (m_points.size () < 2) {
+ return false;
+ }
+
+ double d = m_points.back ().distance (m_points.front ()) * 0.5;
+ if (d < db::epsilon) {
+ return false;
+ }
+
+ db::DVector n = m_points.back () - m_points.front ();
+ db::DPoint m = m_points.front () + n * 0.5;
+ n = db::DVector (n.y (), -n.x ()) * (0.5 / d);
+
+ double nom = 0.0;
+ double div = 0.0;
+
+ for (size_t i = 1; i + 1 < m_points.size (); ++i) {
+ db::DVector p = m_points [i] - m;
+ double pn = db::sprod (p, n);
+ div += pn * pn;
+ nom += pn * (p.sq_double_length () - d * d);
+ }
+
+ if (div < db::epsilon) {
+ return false;
+ }
+
+ double l = 0.5 * nom / div;
+ radius = sqrt (l * l + d * d);
+ center = m + n * l;
+
+ double a = atan2 (-n.y (), -n.x ());
+ double da = atan2 (d, l);
+
+ if (fabs (l) < db::epsilon) {
+
+ start_angle = 0.0;
+ stop_angle = M_PI * 2.0;
+
+ } else if (l < 0.0) {
+
+ stop_angle = a + da;
+ start_angle = stop_angle + 2.0 * (M_PI - da);
+
+ } else {
+
+ start_angle = a - da;
+ stop_angle = a + da;
+
+ }
+
+ while (stop_angle < start_angle - db::epsilon) {
+ stop_angle += M_PI * 2.0;
+ }
+
+ return true;
+}
+
+bool
+Object::compute_angle_parameters (double &radius, db::DPoint ¢er, double &start_angle, double &stop_angle) const
+{
+ if (m_points.size () < 3) {
+ return false;
+ }
+
+ db::DPoint p1 = m_points.front (), p2 = m_points.back ();
+
+ db::DVector pc;
+ for (size_t i = 1; i + 1 < m_points.size (); ++i) {
+ pc += m_points[i] - db::DPoint ();
+ }
+ center = db::DPoint () + pc * (1.0 / double (m_points.size () - 2));
+
+ db::DVector v1 (p1 - center);
+ if (v1.double_length () < db::epsilon) {
+ return false;
+ }
+
+ db::DVector v2 (p2 - center);
+ if (v2.double_length () < db::epsilon) {
+ return false;
+ }
+
+ radius = std::min (v1.double_length (), v2.double_length ());
+
+ v1 *= 1.0 / v1.double_length ();
+ v2 *= 1.0 / v2.double_length ();
+
+ if (db::vprod_sign (v1, v2) == 0) {
+ return false;
+ }
+
+ start_angle = 0.0;
+ stop_angle = 0.0;
+ start_angle = atan2 (v1.y (), v1.x ());
+ stop_angle = atan2 (v2.y (), v2.x ());
+
+ if (db::vprod_sign (v1, v2) < 0) {
+ std::swap (stop_angle, start_angle);
+ }
+
+ while (stop_angle < start_angle - db::epsilon) {
+ stop_angle += M_PI * 2.0;
+ }
+
+ return true;
+}
+
void
Object::property_changed ()
{
diff --git a/src/ant/ant/antObject.h b/src/ant/ant/antObject.h
index 3fe1422a8..5bcb800ed 100644
--- a/src/ant/ant/antObject.h
+++ b/src/ant/ant/antObject.h
@@ -50,6 +50,7 @@ class ANT_PUBLIC Object
{
public:
typedef db::coord_traits coord_traits;
+ typedef std::vector point_list;
/**
* @brief The ruler style
@@ -76,8 +77,10 @@ public:
* OL_diag_yx: both OL_diag and OL_yx
* OL_box: draw a box defined by start and end point
* OL_ellipse: draws an ellipse with p1 and p2 defining the extension (style is ignored)
+ * OL_angle: an angle measurement ruler (first vs. last segment)
+ * OL_radius: a radius measurement ruler
*/
- enum outline_type { OL_diag = 0, OL_xy = 1, OL_diag_xy = 2, OL_yx = 3, OL_diag_yx = 4, OL_box = 5, OL_ellipse = 6 };
+ enum outline_type { OL_diag = 0, OL_xy = 1, OL_diag_xy = 2, OL_yx = 3, OL_diag_yx = 4, OL_box = 5, OL_ellipse = 6, OL_angle = 7, OL_radius = 8 };
/**
* @brief The position type of the main label
@@ -109,11 +112,21 @@ public:
*/
Object (const db::DPoint &p1, const db::DPoint &p2, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint);
+ /**
+ * @brief Parametrized constructor and a list of points
+ */
+ Object (const point_list &points, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint);
+
/**
* @brief Parametrized constructor from a template
*/
Object (const db::DPoint &p1, const db::DPoint &p2, int id, const ant::Template &d);
+ /**
+ * @brief Parametrized constructor from a template and a list of points
+ */
+ Object (const point_list &points, int id, const ant::Template &d);
+
/**
* @brief Copy constructor
*/
@@ -185,8 +198,9 @@ public:
*/
virtual void transform (const db::DCplxTrans &t)
{
- m_p1 = t * m_p1;
- m_p2 = t * m_p2;
+ for (auto p = m_points.begin (); p != m_points.end (); ++p) {
+ *p = t * *p;
+ }
property_changed ();
}
@@ -195,8 +209,9 @@ public:
*/
virtual void transform (const db::DTrans &t)
{
- m_p1 = t * m_p1;
- m_p2 = t * m_p2;
+ for (auto p = m_points.begin (); p != m_points.end (); ++p) {
+ *p = t * *p;
+ }
property_changed ();
}
@@ -205,8 +220,9 @@ public:
*/
virtual void transform (const db::DFTrans &t)
{
- m_p1 = t * m_p1;
- m_p2 = t * m_p2;
+ for (auto p = m_points.begin (); p != m_points.end (); ++p) {
+ *p = t * *p;
+ }
property_changed ();
}
@@ -224,10 +240,11 @@ public:
/**
* @brief Moves the object by the given distance
*/
- Object &move (const db::DVector &p)
+ Object &move (const db::DVector &d)
{
- m_p1 += p;
- m_p2 += p;
+ for (auto p = m_points.begin (); p != m_points.end (); ++p) {
+ *p += d;
+ }
return *this;
}
@@ -265,42 +282,96 @@ public:
}
/**
- * @brief Gets the first definition point
+ * @brief Gets the ruler's definition points
*/
- const db::DPoint &p1 () const
+ const point_list &points () const
{
- return m_p1;
+ return m_points;
+ }
+
+ /**
+ * @brief Sets the ruler's definition points
+ */
+ void set_points (const point_list &points);
+
+ /**
+ * @brief Sets the ruler's definition points without cleaning
+ */
+ void set_points_exact (const point_list &points);
+
+ /**
+ * @brief Sets the ruler's definition points without cleaning (move semantics)
+ */
+ void set_points_exact (point_list &&points);
+
+ /**
+ * @brief Cleans the point list
+ */
+ void clean_points ();
+
+ /**
+ * @brief Gets the first point of the indicated segment
+ */
+ db::DPoint seg_p1 (size_t seg_index) const;
+
+ /**
+ * @brief Gets the second point of the indicated segment
+ */
+ db::DPoint seg_p2 (size_t seg_index) const;
+
+ /**
+ * @brief Sets the first point of the indicated segment
+ */
+ void seg_p1 (size_t seg_index, const db::DPoint &p);
+
+ /**
+ * @brief Sets the second point of the indicated segment
+ */
+ void seg_p2 (size_t seg_index, const db::DPoint &p);
+
+ /**
+ * @brief Gets the number of segments
+ *
+ * The number of segments is at least 1 for backward compatibility.
+ */
+ size_t segments () const
+ {
+ return m_points.size () < 2 ? 1 : m_points.size () - 1;
+ }
+
+ /**
+ * @brief Gets the first definition point
+ *
+ * This method is provided for backward compatibility. Use the point list accessor for generic point retrieval.
+ */
+ db::DPoint p1 () const
+ {
+ return seg_p1 (0);
}
/**
* @brief Gets the second definition point
+ *
+ * This method is provided for backward compatibility. Use the point list accessor for generic point retrieval.
*/
- const db::DPoint &p2 () const
+ db::DPoint p2 () const
{
- return m_p2;
+ return seg_p2 (segments () - 1);
}
/**
* @brief Sets the first definition point
+ *
+ * This method is provided for backward compatibility. Use the point list accessor for generic point retrieval.
*/
- void p1 (const db::DPoint &p)
- {
- if (!m_p1.equal (p)) {
- m_p1 = p;
- property_changed ();
- }
- }
+ void p1 (const db::DPoint &p);
/**
* @brief Sets the second definition point
+ *
+ * This method is provided for backward compatibility. Use the point list accessor for generic point retrieval.
*/
- void p2 (const db::DPoint &p)
- {
- if (!m_p2.equal (p)) {
- m_p2 = p;
- property_changed ();
- }
- }
+ void p2 (const db::DPoint &p);
/**
* @brief Gets the ID of the annotation object
@@ -619,52 +690,52 @@ public:
/**
* @brief Gets the formatted text for the x label
*/
- std::string text_x () const
+ std::string text_x (size_t index) const
{
- return formatted (m_fmt_x, db::DFTrans ());
+ return formatted (m_fmt_x, db::DFTrans (), index);
}
/**
* @brief Gets the formatted text for the y label
*/
- std::string text_y () const
+ std::string text_y (size_t index) const
{
- return formatted (m_fmt_y, db::DFTrans ());
+ return formatted (m_fmt_y, db::DFTrans (), index);
}
/**
* @brief Gets the formatted text for the main label
*/
- std::string text () const
+ std::string text (size_t index) const
{
- return formatted (m_fmt, db::DFTrans ());
+ return formatted (m_fmt, db::DFTrans (), index);
}
/**
* @brief Gets the formatted text for the x label
* @param t The transformation to apply to the vector before producing the text
*/
- std::string text_x (const db::DFTrans &t) const
+ std::string text_x (size_t index, const db::DFTrans &t) const
{
- return formatted (m_fmt_x, t);
+ return formatted (m_fmt_x, t, index);
}
/**
* @brief Gets the formatted text for the y label
* @param t The transformation to apply to the vector before producing the text
*/
- std::string text_y (const db::DFTrans &t) const
+ std::string text_y (size_t index, const db::DFTrans &t) const
{
- return formatted (m_fmt_y, t);
+ return formatted (m_fmt_y, t, index);
}
/**
* @brief Gets the formatted text for the main label
* @param t The transformation to apply to the vector before producing the text
*/
- std::string text (const db::DFTrans &t) const
+ std::string text (size_t index, const db::DFTrans &t) const
{
- return formatted (m_fmt, t);
+ return formatted (m_fmt, t, index);
}
/**
@@ -688,6 +759,26 @@ public:
*/
virtual std::string to_string () const;
+ /**
+ * @brief Computes the parameters for an angle ruler
+ * @param radius Returns the radius
+ * @param center Returns the center point
+ * @param start_angle Returns the start angle (in radians)
+ * @param stop_angle Returns the stop angle (in radians)
+ * @return True, if the ruler represents an angle measurement
+ */
+ bool compute_angle_parameters (double &radius, db::DPoint ¢er, double &start_angle, double &stop_angle) const;
+
+ /**
+ * @brief Computes the parameters for a radius ruler
+ * @param radius Returns the radius
+ * @param center Returns the center point
+ * @param start_angle Returns the start angle (in radians)
+ * @param stop_angle Returns the stop angle (in radians)
+ * @return True, if the ruler represents an angle measurement
+ */
+ bool compute_interpolating_circle (double &radius, db::DPoint ¢er, double &start_angle, double &stop_angle) const;
+
protected:
/**
* @brief A notification method that is called when a property of the annotation has changed
@@ -695,7 +786,7 @@ protected:
virtual void property_changed ();
private:
- db::DPoint m_p1, m_p2;
+ point_list m_points;
int m_id;
std::string m_fmt_x;
std::string m_fmt_y;
@@ -710,7 +801,7 @@ private:
alignment_type m_xlabel_xalign, m_xlabel_yalign;
alignment_type m_ylabel_xalign, m_ylabel_yalign;
- std::string formatted (const std::string &fmt, const db::DFTrans &trans) const;
+ std::string formatted (const std::string &fmt, const db::DFTrans &trans, size_t index) const;
};
}
diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc
index fda6d5b8a..3545deb40 100644
--- a/src/ant/ant/antPlugin.cc
+++ b/src/ant/ant/antPlugin.cc
@@ -68,12 +68,22 @@ static std::vector make_standard_templates ()
templates.push_back (ant::Template (tl::to_string (tr ("Ruler")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_ruler"));
+ templates.push_back (ant::Template (tl::to_string (tr ("Multi-ruler")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_multi_ruler"));
+ templates.back ().set_mode (ant::Template::RulerMultiSegment);
+
templates.push_back (ant::Template (tl::to_string (tr ("Cross")), "", "", "$U,$V", ant::Object::STY_cross_both, ant::Object::OL_diag, true, lay::AC_Global, "_cross"));
templates.back ().set_mode (ant::Template::RulerSingleClick);
templates.push_back (ant::Template (tl::to_string (tr ("Measure")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure"));
templates.back ().set_mode (ant::Template::RulerAutoMetric);
+ templates.push_back (ant::Template (tl::to_string (tr ("Angle")), "", "", "$(sprintf('%.5g',G))°", ant::Object::STY_line, ant::Object::OL_angle, true, lay::AC_Global, "_angle"));
+ templates.back ().set_mode (ant::Template::RulerThreeClicks);
+
+ templates.push_back (ant::Template (tl::to_string (tr ("Radius")), "", "", "R=$D", ant::Object::STY_arrow_end, ant::Object::OL_radius, true, lay::AC_Global, "_radius"));
+ templates.back ().set_mode (ant::Template::RulerThreeClicks);
+ templates.back ().set_main_position (ant::Object::POS_center);
+
templates.push_back (ant::Template (tl::to_string (tr ("Ellipse")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_ellipse, true, lay::AC_Global, std::string ()));
templates.push_back (ant::Template (tl::to_string (tr ("Box")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_box, true, lay::AC_Global, std::string ()));
@@ -197,14 +207,50 @@ PluginDeclaration::initialized (lay::Dispatcher *root)
// Check if we already have templates (initial setup)
// NOTE: this is not done by using a default value for the configuration item but dynamically.
// This provides a migration path from earlier versions (not having templates) to recent ones.
- bool any_templates = false;
- for (std::vector::iterator i = m_templates.begin (); ! any_templates && i != m_templates.end (); ++i) {
- any_templates = ! i->category ().empty ();
+ std::map cat_names;
+ for (auto i = m_templates.begin (); i != m_templates.end (); ++i) {
+ if (! i->category ().empty ()) {
+ cat_names.insert (std::make_pair (i->category (), i.operator-> ()));
+ }
}
- if (! any_templates) {
+ bool any_missing = false;
+ auto std_templates = make_standard_templates ();
+ for (auto t = std_templates.begin (); ! any_missing && t != std_templates.end (); ++t) {
+ if (! t->category ().empty () && cat_names.find (t->category ()) == cat_names.end ()) {
+ any_missing = true;
+ }
+ }
+
+ if (cat_names.empty ()) {
+
+ // full initial configuration
root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (make_standard_templates ()));
root->config_end ();
+
+ } else if (any_missing) {
+
+ // some standard templates are missing - add them now (migration path for later versions)
+ decltype (m_templates) new_templates;
+ for (auto t = std_templates.begin (); t != std_templates.end (); ++t) {
+ if (! t->category ().empty ()) {
+ auto tt = cat_names.find (t->category ());
+ if (tt != cat_names.end ()) {
+ new_templates.push_back (*tt->second);
+ } else {
+ new_templates.push_back (*t);
+ }
+ }
+ }
+ for (auto i = m_templates.begin (); i != m_templates.end (); ++i) {
+ if (i->category ().empty ()) {
+ new_templates.push_back (*i);
+ }
+ }
+
+ root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (new_templates));
+ root->config_end ();
+
}
}
diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc
index 63bb9406b..d81ed6638 100644
--- a/src/ant/ant/antPropertiesPage.cc
+++ b/src/ant/ant/antPropertiesPage.cc
@@ -30,14 +30,42 @@
namespace ant
{
+// -------------------------------------------------------------------------
+// A ruler that tells us if he was modified
+
+class RulerWithModifiedProperty
+ : public ant::Object
+{
+public:
+ RulerWithModifiedProperty ()
+ : ant::Object (), m_modified (false)
+ {
+ // .. nothing yet ..
+ }
+
+ bool is_modified () const
+ {
+ return m_modified;
+ }
+
+protected:
+ void property_changed ()
+ {
+ m_modified = true;
+ ant::Object::property_changed ();
+ }
+
+ bool m_modified;
+};
+
// -------------------------------------------------------------------------
// PropertiesPage implementation
PropertiesPage::PropertiesPage (ant::Service *rulers, db::Manager *manager, QWidget *parent)
- : lay::PropertiesPage (parent, manager, rulers), mp_rulers (rulers), m_enable_cb_callback (true)
+ : lay::PropertiesPage (parent, manager, rulers), mp_rulers (rulers), m_enable_cb_callback (true), m_in_something_changed (false)
{
mp_rulers->get_selection (m_selection);
- m_pos = m_selection.begin ();
+ m_index = 0;
setupUi (this);
@@ -54,23 +82,27 @@ PropertiesPage::PropertiesPage (ant::Service *rulers, db::Manager *manager, QWid
if (! readonly ()) {
- connect (fmt_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (fmt_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (fmt_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (x1, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (x2, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (y1, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
- connect (y2, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
+ connect (fmt_le, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (fmt_x_le, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (fmt_y_le, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (x0, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (x1, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (x2, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (y0, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (y1, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
+ connect (y2, SIGNAL (editingFinished ()), this, SLOT (something_changed ()));
- connect (style_cb, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (outline_cb, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (main_position, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (main_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (main_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (xlabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (xlabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (ylabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
- connect (ylabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
+ connect (style_cb, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (outline_cb, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (main_position, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (main_xalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (main_yalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (xlabel_xalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (xlabel_yalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (ylabel_xalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+ connect (ylabel_yalign, SIGNAL (activated (int)), this, SLOT (something_changed ()));
+
+ connect (points_edit, SIGNAL (textChanged ()), this, SLOT (something_changed ()));
} else {
@@ -94,18 +126,6 @@ PropertiesPage::~PropertiesPage ()
mp_rulers->restore_highlights ();
}
-void
-PropertiesPage::back ()
-{
- m_pos = m_selection.end ();
-}
-
-void
-PropertiesPage::front ()
-{
- m_pos = m_selection.begin ();
-}
-
void
PropertiesPage::swap_points_clicked ()
{
@@ -173,6 +193,89 @@ PropertiesPage::get_points (db::DPoint &p1, db::DPoint &p2)
p2 = db::DPoint (dx2, dy2);
}
+void
+PropertiesPage::get_point (db::DPoint &p)
+{
+ double dx = 0.0, dy = 0.0;
+ bool has_error = false;
+
+ try {
+ tl::from_string_ext (tl::to_string (x0->text ()), dx);
+ lay::indicate_error (x0, (tl::Exception *) 0);
+ } catch (tl::Exception &ex) {
+ lay::indicate_error (x0, &ex);
+ has_error = true;
+ }
+
+ try {
+ tl::from_string_ext (tl::to_string (y0->text ()), dy);
+ lay::indicate_error (y0, (tl::Exception *) 0);
+ } catch (tl::Exception &ex) {
+ lay::indicate_error (y0, &ex);
+ has_error = true;
+ }
+
+ if (has_error) {
+ throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields")));
+ }
+
+ p = db::DPoint (dx, dy);
+}
+
+void
+PropertiesPage::get_points (ant::Object::point_list &points)
+{
+ std::string coordinates = tl::to_string (points_edit->toPlainText ());
+ points.clear ();
+
+ try {
+
+ tl::Extractor ex (coordinates.c_str ());
+ while (! ex.at_end ()) {
+ double x = 0.0, y = 0.0;
+ ex.read (x);
+ ex.test (",");
+ ex.read (y);
+ ex.test (";");
+ ex.test (",");
+ points.push_back (db::DPoint (x, y));
+ }
+
+ lay::indicate_error (points_edit, (tl::Exception *) 0);
+
+ } catch (tl::Exception &ex) {
+ lay::indicate_error (points_edit, &ex);
+ throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields")));
+ }
+}
+
+void
+PropertiesPage::something_changed ()
+{
+ if (m_in_something_changed) {
+ return;
+ }
+
+ try {
+
+ m_in_something_changed = true;
+
+ RulerWithModifiedProperty obj;
+ obj.ant::Object::operator= (current ());
+ get_object (obj);
+ if (obj.is_modified ()) {
+ update_with (obj);
+ emit edited ();
+ }
+
+ m_in_something_changed = false;
+
+ } catch (...) {
+ m_in_something_changed = false;
+ // ignore exceptions - the edit field will be highlighted anyway
+ }
+}
+
void
PropertiesPage::snap_to_layout_clicked ()
{
@@ -259,32 +362,61 @@ PropertiesPage::snap_to_layout_clicked ()
const ant::Object &
PropertiesPage::current () const
{
- const ant::Object *ruler = dynamic_cast ((*m_pos)->ptr ());
+ const ant::Object *ruler = dynamic_cast (m_selection [m_index]->ptr ());
return *ruler;
}
-bool
-PropertiesPage::at_begin () const
+size_t
+PropertiesPage::count () const
{
- return (m_pos == m_selection.begin ());
+ return m_selection.size ();
}
-bool
-PropertiesPage::at_end () const
+void
+PropertiesPage::select_entries (const std::vector &entries)
{
- return (m_pos == m_selection.end ());
+ tl_assert (entries.size () == 1);
+ m_index = entries.front ();
}
-void
-PropertiesPage::operator-- ()
+std::string
+PropertiesPage::description (size_t entry) const
{
- --m_pos;
+ const ant::Object *obj = dynamic_cast (m_selection [entry]->ptr ());
+ if (! obj) {
+ return std::string ("nil");
+ }
+
+ std::string d = tl::to_string (tr ("Ruler"));
+ if (! obj->category ().empty ()) {
+ std::string cat = obj->category ();
+ // category is "_ruler" for example. Turn in into "Ruler".
+ if (cat.size () >= 2 && cat [0] == '_') {
+ cat = tl::to_upper_case (std::string (cat.begin () + 1, cat.begin () + 2)) + std::string (cat.begin () + 2, cat.end ());
+ }
+ d += "[" + cat + "]";
+ }
+
+ if (obj->points ().size () > 3) {
+ d += tl::sprintf (tl::to_string (tr ("(%d points)")), obj->points ().size ());
+ } else {
+ d += "(";
+ for (auto p = obj->points ().begin (); p != obj->points ().end (); ++p) {
+ if (p != obj->points ().begin ()) {
+ d += ";";
+ }
+ d += p->to_string ();
+ }
+ d += ")";
+ }
+
+ return d;
}
-void
-PropertiesPage::operator++ ()
+std::string
+PropertiesPage::description () const
{
- ++m_pos;
+ return tl::to_string (tr ("Rulers and Annotations"));
}
void
@@ -296,33 +428,80 @@ PropertiesPage::leave ()
void
PropertiesPage::update ()
{
- mp_rulers->highlight (std::distance (m_selection.begin (), m_pos));
+ mp_rulers->highlight (m_index);
+ update_with (current ());
+}
- fmt_le->setText (tl::to_qstring (current ().fmt ()));
- fmt_x_le->setText (tl::to_qstring (current ().fmt_x ()));
- fmt_y_le->setText (tl::to_qstring (current ().fmt_y ()));
- style_cb->setCurrentIndex (current ().style ());
- outline_cb->setCurrentIndex (current ().outline ());
+void
+PropertiesPage::update_with (const ant::Object &obj)
+{
+ fmt_le->setText (tl::to_qstring (obj.fmt ()));
+ fmt_x_le->setText (tl::to_qstring (obj.fmt_x ()));
+ fmt_y_le->setText (tl::to_qstring (obj.fmt_y ()));
+ style_cb->setCurrentIndex (obj.style ());
+ outline_cb->setCurrentIndex (obj.outline ());
- x1->setText (tl::to_qstring (tl::micron_to_string (current ().p1 ().x ())));
+ main_position->setCurrentIndex (obj.main_position ());
+ main_xalign->setCurrentIndex (obj.main_xalign ());
+ main_yalign->setCurrentIndex (obj.main_yalign ());
+ xlabel_xalign->setCurrentIndex (obj.xlabel_xalign ());
+ xlabel_yalign->setCurrentIndex (obj.xlabel_yalign ());
+ ylabel_xalign->setCurrentIndex (obj.ylabel_xalign ());
+ ylabel_yalign->setCurrentIndex (obj.ylabel_yalign ());
+
+ // change tabs if required
+ if (segments_tab->currentIndex () == 1) {
+ if (obj.points ().size () > 2 || obj.points ().size () == 0) {
+ segments_tab->setCurrentIndex (2);
+ }
+ } else if (segments_tab->currentIndex () == 0) {
+ if (obj.points ().size () > 2 || obj.points ().size () == 0) {
+ segments_tab->setCurrentIndex (2);
+ } else if (obj.points ().size () > 1) {
+ segments_tab->setCurrentIndex (1);
+ }
+ }
+ segments_tab->setTabEnabled (0, obj.points ().size () == 1);
+ segments_tab->setTabEnabled (1, obj.points ().size () <= 2 && obj.points ().size () > 0);
+
+ point_list->clear ();
+ for (auto p = obj.points ().begin (); p != obj.points ().end (); ++p) {
+ QTreeWidgetItem *item = new QTreeWidgetItem (point_list);
+ item->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (tl::micron_to_string (p->x ()))));
+ item->setData (1, Qt::DisplayRole, QVariant (tl::to_qstring (tl::micron_to_string (p->y ()))));
+ }
+
+ if (! m_in_something_changed || segments_tab->currentIndex () != 3) {
+
+ std::string text;
+ for (auto p = obj.points ().begin (); p != obj.points ().end (); ++p) {
+ text += tl::micron_to_string (p->x ());
+ text += ", ";
+ text += tl::micron_to_string (p->y ());
+ text += "\n";
+ }
+
+ lay::SignalBlocker blocker (points_edit);
+ points_edit->setPlainText (tl::to_qstring (text));
+
+ }
+
+ x0->setText (tl::to_qstring (tl::micron_to_string (obj.p1 ().x ())));
+ x0->setCursorPosition (0);
+ y0->setText (tl::to_qstring (tl::micron_to_string (obj.p1 ().y ())));
+ y0->setCursorPosition (0);
+
+ x1->setText (tl::to_qstring (tl::micron_to_string (obj.p1 ().x ())));
x1->setCursorPosition (0);
- x2->setText (tl::to_qstring (tl::micron_to_string (current ().p2 ().x ())));
+ x2->setText (tl::to_qstring (tl::micron_to_string (obj.p2 ().x ())));
x2->setCursorPosition (0);
- y1->setText (tl::to_qstring (tl::micron_to_string (current ().p1 ().y ())));
+ y1->setText (tl::to_qstring (tl::micron_to_string (obj.p1 ().y ())));
y1->setCursorPosition (0);
- y2->setText (tl::to_qstring (tl::micron_to_string (current ().p2 ().y ())));
+ y2->setText (tl::to_qstring (tl::micron_to_string (obj.p2 ().y ())));
y2->setCursorPosition (0);
- main_position->setCurrentIndex (current ().main_position ());
- main_xalign->setCurrentIndex (current ().main_xalign ());
- main_yalign->setCurrentIndex (current ().main_yalign ());
- xlabel_xalign->setCurrentIndex (current ().xlabel_xalign ());
- xlabel_yalign->setCurrentIndex (current ().xlabel_yalign ());
- ylabel_xalign->setCurrentIndex (current ().ylabel_xalign ());
- ylabel_yalign->setCurrentIndex (current ().ylabel_yalign ());
-
- double sx = (current ().p2 ().x () - current ().p1 ().x ());
- double sy = (current ().p2 ().y () - current ().p1 ().y ());
+ double sx = (obj.p2 ().x () - obj.p1 ().x ());
+ double sy = (obj.p2 ().y () - obj.p1 ().y ());
dx->setText (tl::to_qstring (tl::micron_to_string (sx)));
dx->setCursorPosition (0);
dy->setText (tl::to_qstring (tl::micron_to_string (sy)));
@@ -340,29 +519,49 @@ PropertiesPage::readonly ()
void
PropertiesPage::apply ()
{
- // only adjust the values if the text has changed
- db::DPoint p1, p2;
- get_points (p1, p2);
+ ant::Object obj;
+ get_object (obj);
+ mp_rulers->change_ruler (m_selection [m_index], obj);
+}
+void PropertiesPage::get_object(ant::Object &obj)
+{
std::string fmt = tl::to_string (fmt_le->text ());
std::string fmt_x = tl::to_string (fmt_x_le->text ());
std::string fmt_y = tl::to_string (fmt_y_le->text ());
Object::style_type style = Object::style_type (style_cb->currentIndex ());
Object::outline_type outline = Object::outline_type (outline_cb->currentIndex ());
- ant::Object ruler (p1, p2, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
+ if (segments_tab->currentIndex () == 0 || segments_tab->currentIndex () == 1) {
- ruler.set_main_position (Object::position_type (main_position->currentIndex ()));
- ruler.set_main_xalign (Object::alignment_type (main_xalign->currentIndex ()));
- ruler.set_main_yalign (Object::alignment_type (main_yalign->currentIndex ()));
- ruler.set_xlabel_xalign (Object::alignment_type (xlabel_xalign->currentIndex ()));
- ruler.set_xlabel_yalign (Object::alignment_type (xlabel_yalign->currentIndex ()));
- ruler.set_ylabel_xalign (Object::alignment_type (ylabel_xalign->currentIndex ()));
- ruler.set_ylabel_yalign (Object::alignment_type (ylabel_yalign->currentIndex ()));
+ db::DPoint p1, p2;
+ if (segments_tab->currentIndex () == 1) {
+ get_points (p1, p2);
+ } else {
+ get_point (p1);
+ p2 = p1;
+ }
- ruler.set_category (current ().category ());
+ obj = ant::Object (p1, p2, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
- mp_rulers->change_ruler (*m_pos, ruler);
+ } else if (segments_tab->currentIndex () == 2 || segments_tab->currentIndex () == 3) {
+
+ ant::Object::point_list points;
+ get_points (points);
+
+ obj = ant::Object (points, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
+
+ }
+
+ obj.set_main_position (Object::position_type (main_position->currentIndex ()));
+ obj.set_main_xalign (Object::alignment_type (main_xalign->currentIndex ()));
+ obj.set_main_yalign (Object::alignment_type (main_yalign->currentIndex ()));
+ obj.set_xlabel_xalign (Object::alignment_type (xlabel_xalign->currentIndex ()));
+ obj.set_xlabel_yalign (Object::alignment_type (xlabel_yalign->currentIndex ()));
+ obj.set_ylabel_xalign (Object::alignment_type (ylabel_xalign->currentIndex ()));
+ obj.set_ylabel_yalign (Object::alignment_type (ylabel_yalign->currentIndex ()));
+
+ obj.set_category (current ().category ());
}
}
diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h
index 8e50f4dc4..b83f5c5d5 100644
--- a/src/ant/ant/antPropertiesPage.h
+++ b/src/ant/ant/antPropertiesPage.h
@@ -43,29 +43,33 @@ public:
PropertiesPage (ant::Service *rulers, db::Manager *manager, QWidget *parent);
~PropertiesPage ();
- virtual void back ();
- virtual void front ();
- virtual bool at_begin () const;
- virtual bool at_end () const;
- virtual void operator-- ();
- virtual void operator++ ();
+ virtual size_t count () const;
+ virtual void select_entries (const std::vector &entries);
+ virtual std::string description (size_t entry) const;
+ virtual std::string description () const;
virtual void update ();
virtual void leave ();
virtual bool readonly ();
- virtual void apply ();
+ virtual void apply ();
private slots:
void swap_points_clicked ();
void snap_to_layout_clicked ();
+ void something_changed ();
private:
std::vector m_selection;
- std::vector ::iterator m_pos;
+ size_t m_index;
ant::Service *mp_rulers;
bool m_enable_cb_callback;
+ bool m_in_something_changed;
const ant::Object ¤t () const;
- void get_points(db::DPoint &p1, db::DPoint &p2);
+ void get_points (db::DPoint &p1, db::DPoint &p2);
+ void get_point (db::DPoint &p);
+ void get_points (ant::Object::point_list &points);
+ void update_with (const ant::Object &obj);
+ void get_object (ant::Object &obj);
};
}
diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc
index b7a4f55a9..3d6fd3412 100644
--- a/src/ant/ant/antService.cc
+++ b/src/ant/ant/antService.cc
@@ -32,6 +32,7 @@
#include "laybasicConfig.h"
#include "layConverters.h"
#include "layLayoutCanvas.h"
+#include "layFixedFont.h"
#if defined(HAVE_QT)
# include "layProperties.h"
#endif
@@ -44,6 +45,10 @@
namespace ant
{
+double angle_ruler_radius_factor = 0.9;
+double ruler_tick_length = 8.0;
+double ruler_arrow_width = 8.0;
+
// -------------------------------------------------------------
// Convert buttons to an angle constraint
@@ -113,6 +118,8 @@ tick_spacings (double d, double min_d, int &minor_ticks, double &ticks)
* @param pos The position where to draw the text
* @param bitmap The bitmap to draw the ruler on
* @param renderer The renderer object
+ * @param first_segment True, if we're drawing the first segment
+ * @param last_segment True, if we're drawing the last segment
*/
void
draw_ruler (const db::DPoint &q1,
@@ -123,10 +130,13 @@ draw_ruler (const db::DPoint &q1,
bool right,
ant::Object::style_type style,
lay::CanvasPlane *bitmap,
- lay::Renderer &renderer)
+ lay::Renderer &renderer,
+ bool first_segment,
+ bool last_segment,
+ bool no_line = false)
{
- double arrow_width = 8 / renderer.resolution ();
- double arrow_length = 12 / renderer.resolution ();
+ double arrow_width = ruler_arrow_width / renderer.resolution ();
+ double arrow_length = 1.5 * arrow_width;
double sel_width = 2 / renderer.resolution ();
if (length_u < 1e-5 /*micron*/ && style != ant::Object::STY_cross_both && style != ant::Object::STY_cross_end && style != ant::Object::STY_cross_start) {
@@ -145,7 +155,7 @@ draw_ruler (const db::DPoint &q1,
} else {
// compute the tick distribution
- double tick_length = (style == ant::Object::STY_ruler ? 8 : 0) / renderer.resolution ();
+ double tick_length = (style == ant::Object::STY_ruler ? ruler_tick_length : 0) / renderer.resolution ();
double ticks = -1.0;
int minor_ticks = -1;
@@ -157,12 +167,14 @@ draw_ruler (const db::DPoint &q1,
// normal and unit vector
double len = q1.double_distance (q2);
- if ((style == ant::Object::STY_arrow_end || style == ant::Object::STY_arrow_start) && len < double (arrow_length) * 1.2) {
- arrow_length = len / 1.2;
- arrow_width = arrow_length * 2.0 / 3.0;
- } else if (style == ant::Object::STY_arrow_both && len < double (arrow_length) * 2.4) {
- arrow_length = len / 2.4;
- arrow_width = arrow_length * 2.0 / 3.0;
+ if (! no_line && len < double (arrow_length) * 2.4) {
+ if ((style == ant::Object::STY_arrow_end || style == ant::Object::STY_arrow_start)) {
+ arrow_length = len / 1.2;
+ arrow_width = arrow_length * 2.0 / 3.0;
+ } else if (style == ant::Object::STY_arrow_both) {
+ arrow_length = len / 2.4;
+ arrow_width = arrow_length * 2.0 / 3.0;
+ }
}
db::DVector qq (q2.y () - q1.y (), q1.x () - q2.x ());
@@ -182,37 +194,53 @@ draw_ruler (const db::DPoint &q1,
qu = db::DVector (1.0, 0.0);
}
- // produce polygon stuff
+ // produce line in selected and unselected mode
- if (sel && style != ant::Object::STY_none) {
+ if (! no_line && style != ant::Object::STY_none) {
+
+ if (sel) {
+
+ db::DVector qw = qq * (sel_width * 0.5);
+
+ db::DVector dq1, dq2;
+ if (! first_segment) {
+ // no start indicator if not first segment
+ } else if (style == ant::Object::STY_arrow_both || style == ant::Object::STY_arrow_start) {
+ dq1 = qu * (arrow_length - 1);
+ } else if (style == ant::Object::STY_cross_both || style == ant::Object::STY_cross_start) {
+ dq1 = qu * (sel_width * 0.5);
+ }
+ if (! last_segment) {
+ // no end indicator if not last segment
+ } else if (style == ant::Object::STY_arrow_both || style == ant::Object::STY_arrow_end) {
+ dq2 = qu * -(arrow_length - 1);
+ } else if (style == ant::Object::STY_cross_both || style == ant::Object::STY_cross_end) {
+ dq2 = qu * -(sel_width * 0.5);
+ }
+
+ db::DPolygon p;
+ db::DPoint points[] = {
+ db::DPoint (q1 + dq1 + qw),
+ db::DPoint (q2 + dq2 + qw),
+ db::DPoint (q2 + dq2 - qw),
+ db::DPoint (q1 + dq1 - qw),
+ };
+ p.assign_hull (points, points + sizeof (points) / sizeof (points [0]));
+ renderer.draw (p, bitmap, bitmap, 0, 0);
+
+ } else {
+
+ renderer.draw (db::DEdge (q1, q2), 0, bitmap, 0, 0);
- db::DVector qw = qq * (sel_width * 0.5);
-
- db::DVector dq1, dq2;
- if (style == ant::Object::STY_arrow_both || style == ant::Object::STY_arrow_start) {
- dq1 = qu * (arrow_length - 1);
- } else if (style == ant::Object::STY_cross_both || style == ant::Object::STY_cross_start) {
- dq1 = qu * (sel_width * 0.5);
- }
- if (style == ant::Object::STY_arrow_both || style == ant::Object::STY_arrow_end) {
- dq2 = qu * -(arrow_length - 1);
- } else if (style == ant::Object::STY_cross_both || style == ant::Object::STY_cross_end) {
- dq2 = qu * -(sel_width * 0.5);
}
- db::DPolygon p;
- db::DPoint points[] = {
- db::DPoint (q1 + dq1 + qw),
- db::DPoint (q2 + dq2 + qw),
- db::DPoint (q2 + dq2 - qw),
- db::DPoint (q1 + dq1 - qw),
- };
- p.assign_hull (points, points + sizeof (points) / sizeof (points [0]));
- renderer.draw (p, bitmap, bitmap, 0, 0);
-
}
- if (style == ant::Object::STY_arrow_end || style == ant::Object::STY_arrow_both) {
+ if (! last_segment) {
+
+ // no end indicator if not last segment
+
+ } else if (style == ant::Object::STY_arrow_end || style == ant::Object::STY_arrow_both) {
db::DPolygon p;
db::DPoint points[] = {
@@ -239,7 +267,11 @@ draw_ruler (const db::DPoint &q1,
}
- if (style == ant::Object::STY_arrow_start || style == ant::Object::STY_arrow_both) {
+ if (! first_segment) {
+
+ // no start indicator if not first segment
+
+ } else if (style == ant::Object::STY_arrow_start || style == ant::Object::STY_arrow_both) {
db::DPolygon p;
db::DPoint points[] = {
@@ -266,12 +298,6 @@ draw_ruler (const db::DPoint &q1,
}
- // produce edge and text stuff
-
- if (! sel && style != ant::Object::STY_none) {
- renderer.draw (db::DEdge (q1, q2), 0, bitmap, 0, 0);
- }
-
// create three tick vectors in tv_text, tv_short and tv_long
double tf = tick_length;
@@ -340,8 +366,8 @@ draw_text (const db::DPoint &q1,
return;
}
- double arrow_width = 8 / renderer.resolution ();
- double arrow_length = 12 / renderer.resolution ();
+ double arrow_width = ruler_arrow_width / renderer.resolution ();
+ double arrow_length = 1.5 * arrow_width;
// Currently, "auto" means p2.
if (pos == ant::Object::POS_auto) {
@@ -360,7 +386,7 @@ draw_text (const db::DPoint &q1,
} else {
// compute the tick distribution
- double tick_length = (style == ant::Object::STY_ruler ? 8 : 0) / renderer.resolution ();
+ double tick_length = (style == ant::Object::STY_ruler ? ruler_tick_length : 0) / renderer.resolution ();
// normal and unit vector
@@ -487,9 +513,12 @@ draw_text (const db::DPoint &q1,
*
* @param q1 The first point in pixel space
* @param q2 The second point in pixel space
+ * @param length_u The "typical dimension" - used to simplify for very small ellipses
* @param sel True to draw ruler in "selected" mode
* @param bitmap The bitmap to draw the ruler on
* @param renderer The renderer object
+ * @param start_angle The starting angle (in radians)
+ * @param stop_angle The stop angle (in radians)
*/
void
draw_ellipse (const db::DPoint &q1,
@@ -497,7 +526,9 @@ draw_ellipse (const db::DPoint &q1,
double length_u,
bool sel,
lay::CanvasPlane *bitmap,
- lay::Renderer &renderer)
+ lay::Renderer &renderer,
+ double start_angle = 0.0,
+ double stop_angle = 2.0 * M_PI)
{
double sel_width = 2 / renderer.resolution ();
@@ -516,51 +547,30 @@ draw_ellipse (const db::DPoint &q1,
} else {
- int npoints = 200;
-
- // produce polygon stuff
+ int npoints = int (floor (200 * abs (stop_angle - start_angle) / (2.0 * M_PI)));
double rx = fabs ((q2 - q1).x () * 0.5);
double ry = fabs ((q2 - q1).y () * 0.5);
db::DPoint c = q1 + (q2 - q1) * 0.5;
- db::DPolygon p;
-
std::vector pts;
- pts.reserve (npoints);
+ pts.reserve (npoints + 1);
- if (sel) {
- rx += sel_width * 0.5;
- ry += sel_width * 0.5;
- }
-
- double da = M_PI * 2.0 / double (npoints);
- for (int i = 0; i < npoints; ++i) {
- double a = da * i;
+ double da = fabs (stop_angle - start_angle) / double (npoints);
+ for (int i = 0; i < npoints + 1; ++i) {
+ double a = da * i + start_angle;
pts.push_back (c + db::DVector (rx * cos (a), ry * sin (a)));
}
- p.assign_hull (pts.begin (), pts.end ());
-
if (sel) {
- pts.clear ();
-
- rx -= sel_width;
- ry -= sel_width;
- for (int i = 0; i < npoints; ++i) {
- double a = da * i;
- pts.push_back (c + db::DVector (rx * cos (a), ry * sin (a)));
- }
-
- p.insert_hole (pts.begin (), pts.end ());
-
+ db::DPath p (pts.begin (), pts.end (), sel_width);
renderer.draw (p, bitmap, bitmap, 0, 0);
} else {
- for (db::DPolygon::polygon_edge_iterator e = p.begin_edge (); ! e.at_end (); ++e) {
- renderer.draw (*e, 0, bitmap, 0, 0);
+ for (size_t i = 0; i + 1 < pts.size (); ++i) {
+ renderer.draw (db::DEdge (pts [i], pts [i + 1]), 0, bitmap, 0, 0);
}
}
@@ -570,21 +580,26 @@ draw_ellipse (const db::DPoint &q1,
}
void
-draw_ruler (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
+draw_ruler_segment (const ant::Object &ruler, size_t index, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
{
+ bool last_segment = (index == ruler.segments () - 1 || index == std::numeric_limits::max ());
+ bool first_segment = (index == 0 || index == std::numeric_limits::max ());
+
+ db::DPoint p1 = ruler.seg_p1 (index), p2 = ruler.seg_p2 (index);
+
// round the starting point, shift both, and round the end point
- std::pair v = lay::snap (trans * ruler.p1 (), trans * ruler.p2 ());
+ std::pair v = lay::snap (trans * p1, trans * p2);
db::DPoint q1 = v.first;
db::DPoint q2 = v.second;
bool xy_swapped = ((trans.rot () % 2) != 0);
- double lu = ruler.p1 ().double_distance (ruler.p2 ());
+ double lu = p1.double_distance (p2);
int min_tick_spc = int (0.5 + 20 / renderer.resolution ()); // min tick spacing in canvas units
double mu = double (min_tick_spc) / trans.ctrans (1.0);
if (ruler.outline () == Object::OL_diag) {
- draw_ruler (q1, q2, lu, mu, sel, q2.x () < q1.x (), ruler.style (), bitmap, renderer);
- draw_text (q1, q2, lu, ruler.text (), q2.x () < q1.x (), ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+ draw_ruler (q1, q2, lu, mu, sel, q2.x () < q1.x (), ruler.style (), bitmap, renderer, first_segment, last_segment);
+ draw_text (q1, q2, lu, ruler.text (index), q2.x () < q1.x (), ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
}
if ((!xy_swapped && (ruler.outline () == Object::OL_xy || ruler.outline () == Object::OL_diag_xy)) ||
@@ -593,13 +608,13 @@ draw_ruler (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay
bool r = (q2.x () > q1.x ()) ^ (q2.y () < q1.y ());
if (ruler.outline () == Object::OL_diag_xy || ruler.outline () == Object::OL_diag_yx) {
- draw_ruler (q1, q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer);
- draw_text (q1, q2, lu, ruler.text (), !r, ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+ draw_ruler (q1, q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer, first_segment, last_segment);
+ draw_text (q1, q2, lu, ruler.text (index), !r, ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
}
- draw_ruler (q1, db::DPoint (q2.x (), q1.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
- draw_ruler (db::DPoint (q2.x (), q1.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
+ draw_ruler (q1, db::DPoint (q2.x (), q1.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer, false, false);
+ draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
+ draw_ruler (db::DPoint (q2.x (), q1.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer, false, false);
+ draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
}
@@ -609,49 +624,265 @@ draw_ruler (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay
bool r = (q2.x () > q1.x ()) ^ (q2.y () > q1.y ());
if (ruler.outline () == Object::OL_diag_xy || ruler.outline () == Object::OL_diag_yx) {
- draw_ruler (q1, q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer);
- draw_text (q1, q2, lu, ruler.text (), !r, ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+ draw_ruler (q1, q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer, first_segment, last_segment);
+ draw_text (q1, q2, lu, ruler.text (index), !r, ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
}
- draw_ruler (q1, db::DPoint (q1.x (), q2.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (q1, db::DPoint (q1.x (), q2.y ()), lu, ruler.text_y (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
- draw_ruler (db::DPoint (q1.x (), q2.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (db::DPoint (q1.x (), q2.y ()), q2, lu, ruler.text_x (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
-
- }
-
- if (ruler.outline () == Object::OL_box) {
-
- bool r = (q2.x () > q1.x ()) ^ (q2.y () < q1.y ());
-
- draw_ruler (q1, db::DPoint (q2.x (), q1.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
- draw_ruler (db::DPoint (q2.x (), q1.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer);
- draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
- draw_ruler (q1, db::DPoint (q1.x (), q2.y ()), lu, mu, sel, !r, ruler.style (), bitmap, renderer);
- draw_ruler (db::DPoint (q1.x (), q2.y ()), q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer);
- draw_text (q1, q2, lu, ruler.text (), !r, ant::Object::STY_none, ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
-
- } else if (ruler.outline () == Object::OL_ellipse) {
-
- bool r = (q2.x () > q1.x ()) ^ (q2.y () < q1.y ());
-
- draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (trans.fp_trans ()), r, ant::Object::STY_none, ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
- draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (trans.fp_trans ()), r, ant::Object::STY_none, ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
- draw_text (q1, q2, lu, ruler.text (), !r, ant::Object::STY_none, ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
-
- draw_ellipse (q1, q2, lu, sel, bitmap, renderer);
+ draw_ruler (q1, db::DPoint (q1.x (), q2.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer, false, false);
+ draw_text (q1, db::DPoint (q1.x (), q2.y ()), lu, ruler.text_y (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
+ draw_ruler (db::DPoint (q1.x (), q2.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer, false, false);
+ draw_text (db::DPoint (q1.x (), q2.y ()), q2, lu, ruler.text_x (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
}
}
-static bool
-is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double &distance)
+void
+draw_ruler_box (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
{
- if (ruler.outline () == ant::Object::OL_ellipse) {
+ db::DPoint p1 = ruler.p1 (), p2 = ruler.p2 ();
+
+ // round the starting point, shift both, and round the end point
+ std::pair v = lay::snap (trans * p1, trans * p2);
+ db::DPoint q1 = v.first;
+ db::DPoint q2 = v.second;
+
+ double lu = p1.double_distance (p2);
+ int min_tick_spc = int (0.5 + 20 / renderer.resolution ()); // min tick spacing in canvas units
+ double mu = double (min_tick_spc) / trans.ctrans (1.0);
+
+ bool r = (q2.x () > q1.x ()) ^ (q2.y () < q1.y ());
+
+ size_t index = std::numeric_limits::max ();
+ draw_ruler (q1, db::DPoint (q2.x (), q1.y ()), lu, mu, sel, r, ruler.style (), bitmap, renderer, true, true);
+ draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
+ draw_ruler (db::DPoint (q2.x (), q1.y ()), q2, lu, mu, sel, r, ruler.style (), bitmap, renderer, true, true);
+ draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (index, trans.fp_trans ()), r, ruler.style (), ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
+ draw_ruler (q1, db::DPoint (q1.x (), q2.y ()), lu, mu, sel, !r, ruler.style (), bitmap, renderer, true, true);
+ draw_ruler (db::DPoint (q1.x (), q2.y ()), q2, lu, mu, sel, !r, ruler.style (), bitmap, renderer, true, true);
+ draw_text (q1, q2, lu, ruler.text (index), !r, ant::Object::STY_none, ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+}
+
+void
+draw_ruler_ellipse (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
+{
+ db::DPoint p1 = ruler.p1 (), p2 = ruler.p2 ();
+
+ // round the starting point, shift both, and round the end point
+ std::pair v = lay::snap (trans * p1, trans * p2);
+ db::DPoint q1 = v.first;
+ db::DPoint q2 = v.second;
+
+ double lu = p1.double_distance (p2);
+
+ bool r = (q2.x () > q1.x ()) ^ (q2.y () < q1.y ());
+
+ size_t index = std::numeric_limits::max ();
+ draw_text (q1, db::DPoint (q2.x (), q1.y ()), lu, ruler.text_x (index, trans.fp_trans ()), r, ant::Object::STY_none, ant::Object::POS_center, ruler.xlabel_xalign (), ruler.xlabel_yalign (), bitmap, renderer);
+ draw_text (db::DPoint (q2.x (), q1.y ()), q2, lu, ruler.text_y (index, trans.fp_trans ()), r, ant::Object::STY_none, ant::Object::POS_center, ruler.ylabel_xalign (), ruler.ylabel_yalign (), bitmap, renderer);
+ draw_text (q1, q2, lu, ruler.text (index), !r, ant::Object::STY_none, ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+
+ draw_ellipse (q1, q2, lu, sel, bitmap, renderer);
+}
+
+void
+draw_ruler_radius (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
+{
+ // draw crosses for the support points
+ for (auto p = ruler.points ().begin (); p != ruler.points ().end (); ++p) {
+ ant::Object supp (*p, *p, 0, std::string (), std::string (), std::string (), ant::Object::STY_cross_start, ant::Object::OL_diag, false, lay::AC_Global);
+ draw_ruler_segment (supp, 0, trans, sel, bitmap, renderer);
+ }
+
+ double radius = 0.0;
+ double start_angle = 0.0, stop_angle = 0.0;
+ db::DPoint center;
+
+ // circle interpolation
+ if (ruler.compute_interpolating_circle (radius, center, start_angle, stop_angle)) {
+
+ // draw circle segment
+ db::DVector rr (radius, radius);
+ std::pair v = lay::snap (trans * (center - rr), trans * (center + rr));
+ draw_ellipse (v.first, v.second, radius * 2.0, sel, bitmap, renderer, start_angle, stop_angle);
+
+ double a = 0.5 * (start_angle + stop_angle);
+ db::DPoint rc = center + db::DVector (cos (a), sin (a)) * radius;
+
+#if 0
+ // draw a center marker
+ ant::Object center_loc (center, center, 0, std::string (), ruler.fmt_x (), ruler.fmt_y (), ant::Object::STY_cross_start, ant::Object::OL_diag, false, lay::AC_Global);
+ draw_ruler_segment (center_loc, 0, trans, sel, bitmap, renderer);
+#endif
+
+ // draw the radius ruler
+ ant::Object radius = ruler;
+ radius.outline (ant::Object::OL_diag);
+ ant::Object::point_list pts;
+ pts.push_back (center);
+ pts.push_back (rc);
+ radius.set_points (pts);
+ draw_ruler_segment (radius, 0, trans, sel, bitmap, renderer);
+
+ }
+}
+
+void
+draw_ruler_angle (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
+{
+ // draw guiding segments in diag/plain line mode
+
+ for (int p = 0; p < 2; ++p) {
+
+ auto p1 = (p == 0 ? ruler.p1 () : ruler.seg_p1 (ruler.segments () - 1));
+ auto p2 = (p == 0 ? ruler.seg_p2 (0) : ruler.p2 ());
+
+ auto v = lay::snap (trans * p1, trans * p2);
+ auto q1 = v.first;
+ auto q2 = v.second;
+
+ double lu = p1.double_distance (p2);
+
+ draw_ruler (q1, q2, lu, 0.0, sel, false, ant::Object::STY_line, bitmap, renderer, true, true);
+
+ }
+
+ double radius = 0.0, start_angle = 0.0, stop_angle = 0.0;
+ db::DPoint center;
+
+ if (! ruler.compute_angle_parameters (radius, center, start_angle, stop_angle)) {
+ return;
+ }
+
+ double circle_radius = angle_ruler_radius_factor * radius;
+
+ // draw decorations at start/end
+
+ for (int p = 0; p < 2; ++p) {
+
+ double a = (p == 0 ? start_angle : stop_angle);
+
+ db::DPoint p1 = center + db::DVector (cos (a), sin (a)) * circle_radius;
+
+ auto v = lay::snap (trans * p1, trans * p1);
+ db::DVector vn = db::DVector (-sin (a), cos (a));
+ auto q1 = v.first + vn * (p == 0 ? 0.0 : -1.0);
+ auto q2 = v.second + vn * (p == 0 ? 1.0 : 0.0);
+
+ double lu = abs (circle_radius * (stop_angle - start_angle));
+
+ draw_ruler (q1, q2, lu, 0.0, sel, false, ruler.style (), bitmap, renderer, p == 0, p != 0, true);
+
+ }
+
+ db::DVector rr (circle_radius, circle_radius);
+ std::pair v = lay::snap (trans * (center - rr), trans * (center + rr));
+ draw_ellipse (v.first, v.second, radius * 2.0, sel, bitmap, renderer, start_angle, stop_angle);
+
+ if (ruler.style () == ant::Object::STY_ruler) {
+
+ // draw ticks if required - minor at 5 degree, major at 10 degree
+
+ double tick_length = ruler_tick_length / renderer.resolution ();
+
+ double da = 5.0 / 180.0 * M_PI;
+ unsigned int major_ticks = 2;
+
+ double n = floor (db::epsilon + std::min (2 * M_PI, stop_angle - start_angle) / da);
+ unsigned int ticks = (unsigned int) std::max (1.0, n);
+
+ for (unsigned int i = 0; i <= ticks; ++i) {
+
+ double l = tick_length * ((i % major_ticks) == 0 ? 1.0 : 0.5);
+
+ double a = start_angle + i * da;
+ db::DVector tv (cos (a), sin (a));
+ db::DPoint p1 = center + tv * circle_radius;
+
+ auto v = lay::snap (trans * p1, trans * p1);
+ auto q1 = v.first;
+ auto q2 = v.second + tv * l;
+
+ renderer.draw (db::DEdge (q1, q2), 0, bitmap, 0, 0);
+
+ }
+
+ }
+
+ {
+ double ta = 0.5 * (stop_angle + start_angle);
+
+ db::DPoint tp = center + db::DVector (cos (ta), sin (ta)) * circle_radius;
+ db::DVector tv = db::DVector (-sin (ta), cos (ta));
+
+ auto v = lay::snap (trans * tp, trans * tp);
+ auto q1 = v.first + tv;
+ auto q2 = v.second - tv;
+
+ double lu = abs (circle_radius * (stop_angle - start_angle));
+
+ draw_text (q1, q2, lu, ruler.text (0), false, ruler.style (), ruler.main_position (), ruler.main_xalign (), ruler.main_yalign (), bitmap, renderer);
+
+ }
+}
+
+void
+draw_ruler (const ant::Object &ruler, const db::DCplxTrans &trans, bool sel, lay::CanvasPlane *bitmap, lay::Renderer &renderer)
+{
+ if (ruler.outline () == Object::OL_box) {
+ draw_ruler_box (ruler, trans, sel, bitmap, renderer);
+ } else if (ruler.outline () == Object::OL_ellipse) {
+ draw_ruler_ellipse (ruler, trans, sel, bitmap, renderer);
+ } else if (ruler.outline () == Object::OL_angle) {
+ draw_ruler_angle (ruler, trans, sel, bitmap, renderer);
+ } else if (ruler.outline () == Object::OL_radius) {
+ draw_ruler_radius (ruler, trans, sel, bitmap, renderer);
+ } else {
+ // other outline styles support segments, so paint them individually
+ for (size_t index = 0; index < ruler.segments (); ++index) {
+ draw_ruler_segment (ruler, index, trans, sel, bitmap, renderer);
+ }
+ }
+}
+
+static bool
+is_selected_by_circle_segment (const ant::Object &ruler, const db::DPoint &pos, double enl, double &distance)
+{
+ double r = 0.0, a1 = 0.0, a2 = 0.0;
+ db::DPoint c;
+
+ bool good;
+ if (ruler.outline () == ant::Object::OL_angle) {
+ good = ruler.compute_angle_parameters (r, c, a1, a2);
+ r *= angle_ruler_radius_factor;
+ } else {
+ good = ruler.compute_interpolating_circle (r, c, a1, a2);
+ }
+ if (good && fabs (pos.distance (c) - r) < enl) {
+
+ double a = atan2 ((pos - c).y (), (pos - c).x ()) - 2 * M_PI;
+ while (a < a1 - db::epsilon) {
+ a += 2 * M_PI;
+ }
+ if (a < a2 + db::epsilon) {
+ distance = std::min (distance, fabs (pos.distance (c) - r));
+ return true;
+ }
+
+ }
+
+ return false;
+}
+
+static bool
+is_selected (const ant::Object &ruler, size_t index, const db::DPoint &pos, double enl, double &distance)
+{
+ ant::Object::outline_type outline = ruler.outline ();
+
+ db::DPoint p1 = ruler.seg_p1 (index), p2 = ruler.seg_p2 (index);
+ db::DBox b (p1, p2);
+
+ if (outline == ant::Object::OL_ellipse) {
// special handling of the (non-degenerated) ellipse case
- db::DBox b (ruler.p1 (), ruler.p2 ());
-
if (b.height () > 1e-6 && b.width () > 1e-6) {
double dx = (pos.x () - b.center ().x ()) / (b.width () * 0.5);
@@ -663,7 +894,7 @@ is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double
db::DPoint ref = b.center () + db::DVector (dx * b.width () * 0.5 / dd, dy * b.height () * 0.5 / dd);
double d = ref.distance (pos);
if (d < enl) {
- distance = d;
+ distance = std::min (distance, d);
return true;
}
}
@@ -674,8 +905,6 @@ is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double
}
- db::DBox b (ruler.p1 (), ruler.p2 ());
-
// enlarge this box by some pixels
b.enlarge (db::DVector (enl, enl));
@@ -686,30 +915,32 @@ is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double
db::DEdge edges[4];
unsigned int nedges = 0;
- if (ruler.outline () == ant::Object::OL_diag ||
- ruler.outline () == ant::Object::OL_diag_xy ||
- ruler.outline () == ant::Object::OL_diag_yx) {
- edges [nedges++] = db::DEdge (ruler.p1 (), ruler.p2 ());
+ if (outline == ant::Object::OL_diag ||
+ outline == ant::Object::OL_angle ||
+ outline == ant::Object::OL_radius ||
+ outline == ant::Object::OL_diag_xy ||
+ outline == ant::Object::OL_diag_yx) {
+ edges [nedges++] = db::DEdge (p1, p2);
}
- if (ruler.outline () == ant::Object::OL_xy ||
- ruler.outline () == ant::Object::OL_diag_xy ||
- ruler.outline () == ant::Object::OL_box ||
- ruler.outline () == ant::Object::OL_ellipse) {
- edges [nedges++] = db::DEdge (ruler.p1 (), db::DPoint (ruler.p2 ().x (), ruler.p1 ().y ()));
- edges [nedges++] = db::DEdge (db::DPoint (ruler.p2 ().x (), ruler.p1 ().y ()), ruler.p2 ());
+ if (outline == ant::Object::OL_xy ||
+ outline == ant::Object::OL_diag_xy ||
+ outline == ant::Object::OL_box ||
+ outline == ant::Object::OL_ellipse) {
+ edges [nedges++] = db::DEdge (p1, db::DPoint (p2.x (), p1.y ()));
+ edges [nedges++] = db::DEdge (db::DPoint (p2.x (), p1.y ()), p2);
}
- if (ruler.outline () == ant::Object::OL_yx ||
- ruler.outline () == ant::Object::OL_diag_yx ||
- ruler.outline () == ant::Object::OL_box ||
- ruler.outline () == ant::Object::OL_ellipse) {
- edges [nedges++] = db::DEdge (ruler.p1 (), db::DPoint (ruler.p1 ().x (), ruler.p2 ().y ()));
- edges [nedges++] = db::DEdge (db::DPoint (ruler.p1 ().x (), ruler.p2 ().y ()), ruler.p2 ());
+ if (outline == ant::Object::OL_yx ||
+ outline == ant::Object::OL_diag_yx ||
+ outline == ant::Object::OL_box ||
+ outline == ant::Object::OL_ellipse) {
+ edges [nedges++] = db::DEdge (p1, db::DPoint (p1.x (), p2.y ()));
+ edges [nedges++] = db::DEdge (db::DPoint (p1.x (), p2.y ()), p2);
}
for (unsigned int i = 0; i < nedges; ++i) {
double d = edges [i].distance_abs (pos);
if (d <= enl) {
- distance = d;
+ distance = std::min (distance, d);
return true;
}
}
@@ -717,10 +948,31 @@ is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double
return false;
}
+static bool
+is_selected (const ant::Object &ruler, const db::DPoint &pos, double enl, double &distance)
+{
+ distance = std::numeric_limits::max ();
+ bool any = false;
+
+ if (ruler.outline () == ant::Object::OL_box || ruler.outline () == ant::Object::OL_ellipse) {
+ return is_selected (ruler, std::numeric_limits::max (), pos, enl, distance);
+ } else if (ruler.outline () == ant::Object::OL_angle || ruler.outline () == ant::Object::OL_radius) {
+ any = is_selected_by_circle_segment (ruler, pos, enl, distance);
+ }
+
+ for (size_t index = 0; index < ruler.segments (); ++index) {
+ // NOTE: we check *all* since distance is updated herein.
+ if (is_selected (ruler, index, pos, enl, distance)) {
+ any = true;
+ }
+ }
+ return any;
+}
+
static bool
is_selected (const ant::Object &ruler, const db::DBox &box, double /*enl*/)
{
- return (box.contains (ruler.p1 ()) && box.contains (ruler.p2 ()));
+ return ruler.box ().inside (box);
}
@@ -802,6 +1054,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
mp_transient_ruler (0),
m_drawing (false), m_current (),
m_move_mode (MoveNone),
+ m_seg_index (0),
m_current_template (0)
{
mp_view->annotations_changed_event.add (this, &Service::annotations_changed);
@@ -1018,29 +1271,28 @@ Service::insert_ruler (const ant::Object &ruler, bool limit_number)
return new_id;
}
-/**
- * @brief Helper function to determine which move mode to choose given a certain search box and ant::Object
- */
static bool
-dragging_what (const ant::Object *robj, const db::DBox &search_dbox, ant::Service::MoveMode &mode, db::DPoint &p1)
+dragging_what_seg (const ant::Object *robj, const db::DBox &search_dbox, ant::Service::MoveMode &mode, db::DPoint &p1, size_t index)
{
+ ant::Object::outline_type outline = robj->outline ();
+
db::DPoint p12, p21;
bool has_p12 = false, has_p21 = false;
- db::DPoint p11 = robj->p1 (), p22 = robj->p2 ();
+ db::DPoint p11 = robj->seg_p1 (index), p22 = robj->seg_p2 (index);
db::DPoint c = p11 + (p22 - p11) * 0.5;
-
- if (robj->outline () == ant::Object::OL_xy || robj->outline () == ant::Object::OL_diag_xy || robj->outline () == ant::Object::OL_box) {
- p12 = db::DPoint (robj->p2 ().x (), robj->p1 ().y ());
+
+ if (outline == ant::Object::OL_xy || outline== ant::Object::OL_diag_xy || outline == ant::Object::OL_box) {
+ p12 = db::DPoint (p22.x (), p11.y ());
has_p12 = true;
}
- if (robj->outline () == ant::Object::OL_yx || robj->outline () == ant::Object::OL_diag_yx || robj->outline () == ant::Object::OL_box) {
- p21 = db::DPoint (robj->p1 ().x (), robj->p2 ().y ());
+ if (outline == ant::Object::OL_yx || outline == ant::Object::OL_diag_yx || outline == ant::Object::OL_box) {
+ p21 = db::DPoint (p11.x (), p22.y ());
has_p21 = true;
}
-
- if (robj->outline () == ant::Object::OL_ellipse) {
+
+ if (outline == ant::Object::OL_ellipse) {
db::DVector d = (p22 - p11) * 0.5;
p12 = c + db::DVector (d.x (), -d.y ());
p21 = c + db::DVector (-d.x (), d.y ());
@@ -1050,7 +1302,7 @@ dragging_what (const ant::Object *robj, const db::DBox &search_dbox, ant::Servic
// HINT: this was implemented returning a std::pair, but
// I was not able to get it to work in gcc 4.1.2 in -O3 mode ...
-
+
if (search_dbox.contains (p11)) {
p1 = p11;
mode = ant::Service::MoveP1;
@@ -1091,18 +1343,28 @@ dragging_what (const ant::Object *robj, const db::DBox &search_dbox, ant::Servic
mode = ant::Service::MoveP2Y;
return true;
}
- if ((robj->outline () == ant::Object::OL_diag || robj->outline () == ant::Object::OL_diag_xy || robj->outline () == ant::Object::OL_diag_yx)
- && db::DEdge (p11, p22).distance_abs (search_dbox.center ()) <= search_dbox.width () * 0.5) {
- p1 = search_dbox.center ();
- mode = ant::Service::MoveRuler;
- return true;
+
+ return false;
+}
+
+/**
+ * @brief Helper function to determine which move mode to choose given a certain search box and ant::Object
+ */
+static bool
+dragging_what (const ant::Object *robj, const db::DBox &search_dbox, ant::Service::MoveMode &mode, db::DPoint &p1, size_t &index)
+{
+ ant::Object::outline_type outline = robj->outline ();
+
+ if (outline == ant::Object::OL_box || outline == ant::Object::OL_ellipse) {
+ index = std::numeric_limits::max ();
+ return dragging_what_seg (robj, search_dbox, mode, p1, index);
}
- if ((robj->outline () == ant::Object::OL_box || robj->outline () == ant::Object::OL_ellipse) && search_dbox.inside (db::DBox (p11, p22))) {
- p1 = search_dbox.center ();
- mode = ant::Service::MoveRuler;
- return true;
+
+ for (index = 0; index < robj->segments (); ++index) {
+ if (dragging_what_seg (robj, search_dbox, mode, p1, index)) {
+ return true;
+ }
}
-
return false;
}
@@ -1129,6 +1391,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
} else if (mode == lay::Editable::Partial) {
m_move_mode = MoveNone;
+ m_seg_index = 0;
// compute search box
double l = catch_distance ();
@@ -1160,7 +1423,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
const ant::Object *robj = dynamic_cast ((*ri).ptr ());
if (robj && (! robj_min || robj == robj_min)) {
- if (dragging_what (robj, search_dbox, m_move_mode, m_p1) && m_move_mode != MoveRuler) {
+ if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index) && m_move_mode != MoveRuler) {
// found anything: make the moved ruler the selection
clear_selection ();
@@ -1217,7 +1480,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
const ant::Object *robj = dynamic_cast ((*r).ptr ());
if (robj && (! robj_min || robj == robj_min)) {
- if (dragging_what (robj, search_dbox, m_move_mode, m_p1)) {
+ if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index)) {
// found anything: make the moved ruler the selection
clear_selection ();
@@ -1284,50 +1547,50 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
if (m_move_mode == MoveP1) {
- m_current.p1 (snap2 (m_p1, p, &m_current, ac).second);
+ m_current.seg_p1 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second);
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2) {
- m_current.p2 (snap2 (m_p1, p, &m_current, ac).second);
+ m_current.seg_p2 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second);
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP12) {
db::DPoint p12 = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p1 (db::DPoint (m_current.p1 ().x(), p12.y ()));
- m_current.p2 (db::DPoint (p12.x (), m_current.p2 ().y ()));
+ m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x(), p12.y ()));
+ m_current.seg_p2 (m_seg_index, db::DPoint (p12.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP21) {
db::DPoint p21 = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p1 (db::DPoint (p21.x (), m_current.p1 ().y ()));
- m_current.p2 (db::DPoint (m_current.p2 ().x(), p21.y ()));
+ m_current.seg_p1 (m_seg_index, db::DPoint (p21.x (), m_current.seg_p1 (m_seg_index).y ()));
+ m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x(), p21.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p1 (db::DPoint (pc.x (), m_current.p1 ().y ()));
+ m_current.seg_p1 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p1 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p2 (db::DPoint (pc.x (), m_current.p2 ().y ()));
+ m_current.seg_p2 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p1 (db::DPoint (m_current.p1 ().x (), pc.y ()));
+ m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
- m_current.p2 (db::DPoint (m_current.p2 ().x (), pc.y ()));
+ m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveRuler) {
@@ -1418,6 +1681,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type)
} else if (m_move_mode != MoveNone) {
// replace the ruler that was moved
+ m_current.clean_points ();
mp_view->annotation_shapes ().replace (m_selected.begin ()->first, db::DUserObject (new ant::Object (m_current)));
annotation_changed_event (m_current.id ());
@@ -1504,11 +1768,52 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
return mouse_click_event (p, buttons, prio);
}
-bool
+void
+Service::finish_drawing ()
+{
+ // create the ruler object
+
+ // begin the transaction
+ if (manager ()) {
+ tl_assert (! manager ()->transacting ());
+ manager ()->transaction (tl::to_string (tr ("Create ruler")));
+ }
+
+ show_message ();
+
+ insert_ruler (ant::Object (m_current.points (), 0, current_template ()), true);
+
+ // stop dragging
+ drag_cancel ();
+ clear_transient_selection ();
+
+ // end the transaction
+ if (manager ()) {
+ manager ()->commit ();
+ }
+}
+
+bool
+Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio)
+{
+ if (m_drawing && prio && (buttons & lay::LeftButton) != 0) {
+
+ // ends the current ruler (specifically in multi-segment mode)
+ finish_drawing ();
+ return true;
+
+ }
+
+ return false;
+}
+
+bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (prio && (buttons & lay::LeftButton) != 0) {
+ const ant::Template &tpl = current_template ();
+
if (! m_drawing) {
// cancel any edit operations so far
@@ -1521,8 +1826,6 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
// and clear surplus rulers
reduce_rulers (m_max_number_of_rulers - 1);
- const ant::Template &tpl = current_template ();
-
// create and start dragging the ruler
if (tpl.mode () == ant::Template::RulerSingleClick) {
@@ -1592,7 +1895,13 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
- m_current = ant::Object (m_p1, m_p1, 0, tpl);
+ // NOTE: generating the ruler this way makes sure we have two points
+ ant::Object::point_list pts;
+ m_current = ant::Object (pts, 0, tpl);
+ pts.push_back (m_p1);
+ pts.push_back (m_p1);
+ m_current.set_points_exact (pts);
+
show_message ();
if (mp_active_ruler) {
@@ -1606,29 +1915,29 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
}
+ } else if (tpl.mode () == ant::Template::RulerMultiSegment || tpl.mode () == ant::Template::RulerThreeClicks) {
+
+ ant::Object::point_list pts = m_current.points ();
+ tl_assert (! pts.empty ());
+
+ 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 {
- // create the ruler object
+ finish_drawing ();
- // begin the transaction
- if (manager ()) {
- tl_assert (! manager ()->transacting ());
- manager ()->transaction (tl::to_string (tr ("Create ruler")));
- }
-
- show_message ();
-
- insert_ruler (ant::Object (m_current.p1 (), m_current.p2 (), 0, current_template ()), true);
-
- // stop dragging
- drag_cancel ();
- clear_transient_selection ();
-
- // end the transaction
- if (manager ()) {
- manager ()->commit ();
- }
-
}
return true;
@@ -1675,7 +1984,14 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
set_cursor (lay::Cursor::cross);
- m_current.p2 (snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second);
+ // NOTE: we use the direct access path so we do not encounter cleanup by the p1 and p2 setters
+ // otherwise we risk manipulating p1 too.
+ ant::Object::point_list pts = m_current.points ();
+ if (! pts.empty ()) {
+ pts.back () = snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second;
+ }
+ m_current.set_points_exact (pts);
+
mp_active_ruler->redraw ();
show_message ();
@@ -2180,17 +2496,23 @@ Service::display_status (bool transient)
if (! transient) {
msg = tl::to_string (tr ("selected: "));
}
- msg += tl::sprintf (tl::to_string (tr ("annotation(d=%s x=%s y=%s)")), ruler->text (), ruler->text_x (), ruler->text_y ());
+ if (ruler->segments () > 1) {
+ msg += tl::sprintf (tl::to_string (tr ("annotation(d=%s x=%s y=%s ...)")), ruler->text (0), ruler->text_x (0), ruler->text_y (0));
+ } else {
+ msg += tl::sprintf (tl::to_string (tr ("annotation(d=%s x=%s y=%s)")), ruler->text (0), ruler->text_x (0), ruler->text_y (0));
+ }
view ()->message (msg);
}
}
#if defined(HAVE_QT)
-lay::PropertiesPage *
-Service::properties_page (db::Manager *manager, QWidget *parent)
+std::vector
+Service::properties_pages (db::Manager *manager, QWidget *parent)
{
- return new PropertiesPage (this, manager, parent);
+ std::vector pages;
+ pages.push_back (new PropertiesPage (this, manager, parent));
+ return pages;
}
#endif
diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h
index 6b4c5be9d..9fb952063 100644
--- a/src/ant/ant/antService.h
+++ b/src/ant/ant/antService.h
@@ -350,7 +350,7 @@ public:
/**
* @brief Create the properties page
*/
- virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent);
+ virtual std::vector properties_pages (db::Manager *manager, QWidget *parent);
#endif
/**
@@ -545,6 +545,8 @@ private:
ant::Object m_original;
// The current move mode
MoveMode m_move_mode;
+ // The currently moving segment
+ size_t m_seg_index;
// The ruler template
std::vector m_ruler_templates;
unsigned int m_current_template;
@@ -566,6 +568,7 @@ private:
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
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 void deactivated ();
/**
@@ -585,6 +588,11 @@ private:
*/
void reduce_rulers (int num);
+ /**
+ * @brief Finishes drawing mode and creates the ruler
+ */
+ void finish_drawing ();
+
/**
* @brief Delete the selected rulers
*
diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h
index 0d8b921f5..ab561a5e8 100644
--- a/src/ant/ant/antTemplate.h
+++ b/src/ant/ant/antTemplate.h
@@ -62,7 +62,17 @@ public:
/**
* @brief The ruler is auto-metric: a single click will place a ruler and the ruler will extend to the next adjacent structures
*/
- RulerAutoMetric = 2
+ RulerAutoMetric = 2,
+
+ /**
+ * @brief The ruler an angle type (two segments, three mouse clicks) for angle and circle radius measurements
+ */
+ RulerThreeClicks = 3,
+
+ /**
+ * @brief The ruler is a multi-segment type
+ */
+ RulerMultiSegment = 4
};
/**
diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc
index a1284d3ff..032190b48 100644
--- a/src/ant/ant/gsiDeclAnt.cc
+++ b/src/ant/ant/gsiDeclAnt.cc
@@ -48,6 +48,8 @@ static int outline_yx () { return int (ant::Object::OL_yx); }
static int outline_diag_yx () { return int (ant::Object::OL_diag_yx); }
static int outline_box () { return int (ant::Object::OL_box); }
static int outline_ellipse () { return int (ant::Object::OL_ellipse); }
+static int outline_angle () { return int (ant::Object::OL_angle); }
+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); }
@@ -236,6 +238,13 @@ static AnnotationRef create_measure_ruler (lay::LayoutViewBase *view, const db::
}
}
+static AnnotationRef *ant_from_s (const std::string &s)
+{
+ std::unique_ptr aref (new AnnotationRef ());
+ aref->from_string (s.c_str ());
+ return aref.release ();
+}
+
static int get_style (const AnnotationRef *obj)
{
return int (obj->style ());
@@ -425,6 +434,16 @@ static int ruler_mode_auto_metric ()
return ant::Template::RulerAutoMetric;
}
+static int ruler_mode_three_clicks ()
+{
+ return ant::Template::RulerThreeClicks;
+}
+
+static int ruler_mode_multi_segment ()
+{
+ return ant::Template::RulerMultiSegment;
+}
+
static void register_annotation_template (const ant::Object &a, const std::string &title, int mode)
{
ant::Template t = ant::Template::from_object (a, title, mode);
@@ -496,68 +515,80 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
) +
gsi::method ("RulerModeSingleClick", &gsi::ruler_mode_single_click,
"@brief Specifies single-click ruler mode for the \\register_template method\n"
- "In single click-mode, a ruler can be placed with a single click and p1 will be == p2."
+ "In single click-mode, a ruler can be placed with a single click and p1 will be == p2.\n"
"\n"
"This constant has been introduced in version 0.25"
) +
gsi::method ("RulerModeAutoMetric", &gsi::ruler_mode_auto_metric,
"@brief Specifies auto-metric ruler mode for the \\register_template method\n"
- "In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the neighborhood."
+ "In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the neighborhood.\n"
"\n"
"This constant has been introduced in version 0.25"
) +
- gsi::method ("StyleRuler|#style_ruler", &gsi::style_ruler,
+ gsi::method ("RulerThreeClicks", &gsi::ruler_mode_three_clicks,
+ "@brief Specifies three-click ruler mode for the \\register_template method\n"
+ "In this ruler mode, two segments are created for angle and circle radius measurements. Three mouse clicks are required.\n"
+ "\n"
+ "This constant has been introduced in version 0.28"
+ ) +
+ gsi::method ("RulerMultiSegment", &gsi::ruler_mode_multi_segment,
+ "@brief Specifies multi-segment mode\n"
+ "In multi-segment mode, multiple segments can be created. The ruler is finished with a double click.\n"
+ "\n"
+ "This constant has been introduced in version 0.28"
+ ) +
+ gsi::method ("StyleRuler", &gsi::style_ruler,
"@brief Gets the ruler style code for use the \\style method\n"
"When this style is specified, the annotation will show a ruler with "
"some ticks at distances indicating a decade of units and a suitable "
"subdivision into minor ticks at intervals of 1, 2 or 5 units."
) +
- gsi::method ("StyleArrowEnd|#style_arrow_end", &gsi::style_arrow_end,
+ gsi::method ("StyleArrowEnd", &gsi::style_arrow_end,
"@brief Gets the end arrow style code for use the \\style method\n"
"When this style is specified, an arrow is drawn pointing from the start to the end point."
) +
- gsi::method ("StyleArrowStart|#style_arrow_start", &gsi::style_arrow_start,
+ gsi::method ("StyleArrowStart", &gsi::style_arrow_start,
"@brief Gets the start arrow style code for use the \\style method\n"
"When this style is specified, an arrow is drawn pointing from the end to the start point."
) +
- gsi::method ("StyleArrowBoth|#style_arrow_both", &gsi::style_arrow_both,
+ gsi::method ("StyleArrowBoth", &gsi::style_arrow_both,
"@brief Gets the both arrow ends style code for use the \\style method\n"
"When this style is specified, a two-headed arrow is drawn."
) +
- gsi::method ("StyleLine|#style_line", &gsi::style_line,
+ gsi::method ("StyleLine", &gsi::style_line,
"@brief Gets the line style code for use with the \\style method\n"
"When this style is specified, a plain line is drawn."
) +
- gsi::method ("StyleCrossStart|#style_cross_start", &gsi::style_cross_start,
+ gsi::method ("StyleCrossStart", &gsi::style_cross_start,
"@brief Gets the line style code for use with the \\style method\n"
"When this style is specified, a cross is drawn at the start point.\n"
"\n"
"This constant has been added in version 0.26."
) +
- gsi::method ("StyleCrossEnd|#style_cross_end", &gsi::style_cross_end,
+ gsi::method ("StyleCrossEnd", &gsi::style_cross_end,
"@brief Gets the line style code for use with the \\style method\n"
"When this style is specified, a cross is drawn at the end point.\n"
"\n"
"This constant has been added in version 0.26."
) +
- gsi::method ("StyleCrossBoth|#style_cross_both", &gsi::style_cross_both,
+ gsi::method ("StyleCrossBoth", &gsi::style_cross_both,
"@brief Gets the line style code for use with the \\style method\n"
"When this style is specified, a cross is drawn at both points.\n"
"\n"
"This constant has been added in version 0.26."
) +
- gsi::method ("OutlineDiag|#outline_diag", &gsi::outline_diag,
+ gsi::method ("OutlineDiag", &gsi::outline_diag,
"@brief Gets the diagonal output code for use with the \\outline method\n"
"When this outline style is specified, a line connecting start and "
"end points in the given style (ruler, arrow or plain line) is drawn."
) +
- gsi::method ("OutlineXY|#outline_xy", &gsi::outline_xy,
+ gsi::method ("OutlineXY", &gsi::outline_xy,
"@brief Gets the xy outline code for use with the \\outline method\n"
"When this outline style is specified, two lines are drawn: one horizontal from left "
"to right and attached to the end of that a line from the bottom to the top. The lines "
"are drawn in the specified style (see \\style method)."
) +
- gsi::method ("OutlineDiagXY|#outline_diag_xy", &gsi::outline_diag_xy,
+ gsi::method ("OutlineDiagXY", &gsi::outline_diag_xy,
"@brief Gets the xy plus diagonal outline code for use with the \\outline method\n"
"@brief outline_xy code used by the \\outline method\n"
"When this outline style is specified, three lines are drawn: one horizontal from left "
@@ -565,53 +596,65 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"is drawn connecting the start and end points directly. The lines "
"are drawn in the specified style (see \\style method)."
) +
- gsi::method ("OutlineYX|#outline_yx", &gsi::outline_yx ,
+ gsi::method ("OutlineYX", &gsi::outline_yx ,
"@brief Gets the yx outline code for use with the \\outline method\n"
"When this outline style is specified, two lines are drawn: one vertical from bottom "
"to top and attached to the end of that a line from the left to the right. The lines "
"are drawn in the specified style (see \\style method)."
) +
- gsi::method ("OutlineDiagYX|#outline_diag_yx", &gsi::outline_diag_yx ,
+ gsi::method ("OutlineDiagYX", &gsi::outline_diag_yx ,
"@brief Gets the yx plus diagonal outline code for use with the \\outline method\n"
"When this outline style is specified, three lines are drawn: one vertical from bottom "
"to top and attached to the end of that a line from the left to the right. Another line "
"is drawn connecting the start and end points directly. The lines "
"are drawn in the specified style (see \\style method)."
) +
- gsi::method ("OutlineBox|#outline_box", &gsi::outline_box,
+ gsi::method ("OutlineBox", &gsi::outline_box,
"@brief Gets the box outline code for use with the \\outline method\n"
"When this outline style is specified, a box is drawn with the corners specified by the "
"start and end point. All box edges are drawn in the style specified with the \\style "
"attribute."
) +
- gsi::method ("OutlineEllipse|#outline_ellipse", &gsi::outline_ellipse,
+ gsi::method ("OutlineEllipse", &gsi::outline_ellipse,
"@brief Gets the ellipse outline code for use with the \\outline method\n"
"When this outline style is specified, an ellipse is drawn with the extensions specified by the "
"start and end point. The contour drawn as a line.\n"
"\n"
"This constant has been introduced in version 0.26."
) +
- gsi::method ("AngleAny|#angle_any", &gsi::angle_any,
+ gsi::method ("OutlineAngle", &gsi::outline_angle,
+ "@brief Gets the angle measurement ruler outline code for use with the \\outline method\n"
+ "When this outline style is specified, the ruler is drawn to indicate the angle between the first and last segment.\n"
+ "\n"
+ "This constant has been introduced in version 0.28."
+ ) +
+ gsi::method ("OutlineRadius", &gsi::outline_radius,
+ "@brief Gets the radius measurement ruler outline code for use with the \\outline method\n"
+ "When this outline style is specified, the ruler is drawn to indicate a radius defined by at least three points of the ruler.\n"
+ "\n"
+ "This constant has been introduced in version 0.28."
+ ) +
+ gsi::method ("AngleAny", &gsi::angle_any,
"@brief Gets the any angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, all angles will be allowed."
) +
- gsi::method ("AngleDiagonal|#angle_diagonal", &gsi::angle_diagonal,
+ gsi::method ("AngleDiagonal", &gsi::angle_diagonal,
"@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 ("AngleOrtho|#angle_ortho", &gsi::angle_ortho,
+ 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."
) +
- gsi::method ("AngleHorizontal|#angle_horizontal", &gsi::angle_horizontal,
+ gsi::method ("AngleHorizontal", &gsi::angle_horizontal,
"@brief Gets the horizontal angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, only horizontal rulers are allowed."
) +
- gsi::method ("AngleVertical|#angle_vertical", &gsi::angle_vertical,
+ gsi::method ("AngleVertical", &gsi::angle_vertical,
"@brief Gets the vertical angle code for use with the \\angle_constraint method\n"
"If this value is specified for the angle constraint, only vertical rulers are allowed."
) +
- gsi::method ("AngleGlobal|#angle_global", &gsi::angle_global,
+ gsi::method ("AngleGlobal", &gsi::angle_global,
"@brief Gets the global angle code for use with the \\angle_constraint method.\n"
"This code will tell the ruler or marker to use the angle constraint defined globally."
) +
@@ -704,27 +747,80 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"\n"
"This method has been introduced in version 0.25."
) +
- gsi::method ("p1", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p1,
+ gsi::method ("points", &AnnotationRef::points,
+ "@brief Gets the points of the ruler\n"
+ "A single-segmented ruler has two points. Rulers with more points "
+ "have more segments correspondingly. Note that the point list may have one point "
+ "only (single-point ruler) or may even be empty.\n"
+ "\n"
+ "Use \\points= to set the segment points. Use \\segments to get the number of "
+ "segments and \\seg_p1 and \\seg_p2 to get the first and second point of one segment.\n"
+ "\n"
+ "Multi-segmented rulers have been introduced in version 0.28"
+ ) +
+ gsi::method ("points=", &AnnotationRef::set_points, gsi::arg ("points"),
+ "@brief Sets the points for a (potentially) multi-segmented ruler\n"
+ "See \\points for a description of multi-segmented rulers. "
+ "The list of points passed to this method is cleaned from duplicates before being "
+ "stored inside the ruler.\n"
+ "\n"
+ "This method has been introduced in version 0.28."
+ ) +
+ gsi::method ("segments", &AnnotationRef::segments,
+ "@brief Gets the number of segments.\n"
+ "This method returns the number of segments the ruler is made up. Even though the "
+ "ruler can be one or even zero points, the number of segments is at least 1.\n"
+ "\n"
+ "This method has been introduced in version 0.28."
+ ) +
+ gsi::method ("seg_p1", &AnnotationRef::seg_p1, gsi::arg ("segment_index"),
+ "@brief Gets the first point of the given segment.\n"
+ "The segment is indicated by the segment index which is a number between 0 and \\segments-1.\n"
+ "\n"
+ "This method has been introduced in version 0.28."
+ ) +
+ gsi::method ("seg_p2", &AnnotationRef::seg_p2, gsi::arg ("segment_index"),
+ "@brief Gets the second point of the given segment.\n"
+ "The segment is indicated by the segment index which is a number between 0 and \\segments-1.\n"
+ "The second point of a segment is also the first point of the following segment if there is one.\n"
+ "\n"
+ "This method has been introduced in version 0.28."
+ ) +
+ gsi::method ("p1", (db::DPoint (AnnotationRef::*) () const) &AnnotationRef::p1,
"@brief Gets the first point of the ruler or marker\n"
"The points of the ruler or marker are always given in micron units in floating-point "
"coordinates.\n"
+ "\n"
+ "This method is provided for backward compatibility. Starting with version 0.28, rulers can "
+ "be multi-segmented. Use \\points or \\seg_p1 to retrieve the points of the ruler segments.\n"
+ "\n"
"@return The first point\n"
) +
- gsi::method ("p2", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p2,
+ gsi::method ("p2", (db::DPoint (AnnotationRef::*) () const) &AnnotationRef::p2,
"@brief Gets the second point of the ruler or marker\n"
"The points of the ruler or marker are always given in micron units in floating-point "
"coordinates.\n"
+ "\n"
+ "This method is provided for backward compatibility. Starting with version 0.28, rulers can "
+ "be multi-segmented. Use \\points or \\seg_p1 to retrieve the points of the ruler segments.\n"
+ "\n"
"@return The second point\n"
) +
gsi::method ("p1=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p1, gsi::arg ("point"),
"@brief Sets the first point of the ruler or marker\n"
"The points of the ruler or marker are always given in micron units in floating-point "
"coordinates.\n"
+ "\n"
+ "This method is provided for backward compatibility. Starting with version 0.28, rulers can "
+ "be multi-segmented. Use \\points= to specify the ruler segments.\n"
) +
gsi::method ("p2=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p2, gsi::arg ("point"),
"@brief Sets the second point of the ruler or marker\n"
"The points of the ruler or marker are always given in micron units in floating-point "
"coordinates.\n"
+ "\n"
+ "This method is provided for backward compatibility. Starting with version 0.28, rulers can "
+ "be multi-segmented. Use \\points= to specify the ruler segments.\n"
) +
gsi::method ("box", &AnnotationRef::box,
"@brief Gets the bounding box of the object (not including text)\n"
@@ -924,14 +1020,17 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"@brief Returns the angle constraint attribute\n"
"See \\angle_constraint= for a more detailed description."
) +
- gsi::method ("text_x", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_x,
- "@brief Returns the formatted text for the x-axis label"
+ gsi::method ("text_x", (std::string (AnnotationRef::*)(size_t index) const) &AnnotationRef::text_x, gsi::arg ("index", 0),
+ "@brief Returns the formatted text for the x-axis label\n"
+ "The index parameter indicates which segment to use (0 is the first one). It has been added in version 0.28.\n"
) +
- gsi::method ("text_y", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_y,
- "@brief Returns the formatted text for the y-axis label"
+ gsi::method ("text_y", (std::string (AnnotationRef::*)(size_t index) const) &AnnotationRef::text_y, gsi::arg ("index", 0),
+ "@brief Returns the formatted text for the y-axis label\n"
+ "The index parameter indicates which segment to use (0 is the first one). It has been added in version 0.28.\n"
) +
- gsi::method ("text", (std::string (AnnotationRef::*)() const) &AnnotationRef::text,
- "@brief Returns the formatted text for the main label"
+ gsi::method ("text", (std::string (AnnotationRef::*)(size_t index) const) &AnnotationRef::text, gsi::arg ("index", 0),
+ "@brief Returns the formatted text for the main label\n"
+ "The index parameter indicates which segment to use (0 is the first one). It has been added in version 0.28.\n"
) +
gsi::method ("id", (int (AnnotationRef::*)() const) &AnnotationRef::id,
"@brief Returns the annotation's ID"
@@ -953,6 +1052,12 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"\n"
"This method was introduced in version 0.19."
) +
+ gsi::constructor ("from_s", &ant_from_s, gsi::arg ("s"),
+ "@brief Creates a ruler from a string representation\n"
+ "This function creates a ruler from the string returned by \\to_s.\n"
+ "\n"
+ "This method was introduced in version 0.28."
+ ) +
gsi::method ("==", &AnnotationRef::operator==, gsi::arg ("other"),
"@brief Equality operator\n"
) +
diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc
index d8c9114d5..2f47d395f 100644
--- a/src/db/db/dbLayoutToNetlist.cc
+++ b/src/db/db/dbLayoutToNetlist.cc
@@ -1366,10 +1366,38 @@ compute_area_and_perimeter_of_net_shapes (const db::hier_clusters
perimeter = ap_collector.perimeter ();
}
+namespace {
+
+class AntennaShapeGenerator
+ : public PolygonSink
+{
+public:
+ AntennaShapeGenerator (db::Layout *layout, db::Shapes &shapes, db::properties_id_type prop_id)
+ : PolygonSink (), mp_layout (layout), mp_shapes (&shapes), m_prop_id (prop_id)
+ { }
+
+ virtual void put (const db::Polygon &polygon)
+ {
+ if (m_prop_id != 0) {
+ mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id));
+ } else {
+ mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
+ }
+ }
+
+private:
+ db::Layout *mp_layout;
+ db::Shapes *mp_shapes;
+ db::properties_id_type m_prop_id;
+};
+
+}
+
static db::Point
-get_merged_shapes_of_net (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Shapes &shapes)
+get_merged_shapes_of_net (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Layout *layout, db::Shapes &shapes, db::properties_id_type prop_id)
{
db::Point ref;
+ bool any_ref = false;
db::EdgeProcessor ep;
// count vertices and reserve space
@@ -1386,7 +1414,10 @@ get_merged_shapes_of_net (const db::hier_clusters &clusters, db::c
db::PolygonRef::polygon_edge_iterator e = pr.begin_edge ();
if (! e.at_end ()) {
// pick one reference point for the label
- ref = (*e).p1 ();
+ if (! any_ref && (*e).p1 () < ref) {
+ ref = (*e).p1 ();
+ any_ref = true;
+ }
ep.insert (pr, ++p);
}
} else {
@@ -1394,7 +1425,7 @@ get_merged_shapes_of_net (const db::hier_clusters &clusters, db::c
}
}
- db::ShapeGenerator sg (shapes);
+ db::AntennaShapeGenerator sg (layout, shapes, prop_id);
db::PolygonGenerator pg (sg, false);
db::SimpleMerge op;
ep.process (pg, op);
@@ -1402,52 +1433,67 @@ get_merged_shapes_of_net (const db::hier_clusters &clusters, db::c
return ref;
}
-static std::string
-create_antenna_msg (double agate, db::Polygon::area_type agate_int, double gate_area_factor, db::Polygon::perimeter_type pgate_int, double gate_perimeter_factor,
- double ametal, db::Polygon::area_type ametal_int, double metal_area_factor, db::Polygon::perimeter_type pmetal_int, double metal_perimeter_factor,
- const std::vector > &diodes,
- const std::vector &adiodes_int,
- double r, double ratio, double dbu)
+static std::vector >
+create_antenna_values (double agate, db::Polygon::area_type agate_int, double gate_area_factor, db::Polygon::perimeter_type pgate_int, double gate_perimeter_factor,
+ double ametal, db::Polygon::area_type ametal_int, double metal_area_factor, db::Polygon::perimeter_type pmetal_int, double metal_perimeter_factor,
+ const std::vector > &diodes,
+ const std::vector &adiodes_int,
+ double r, double ratio, double dbu)
{
- std::string msg;
- msg += tl::sprintf ("agate_eff: %.12g, ", agate);
- if (fabs (gate_area_factor) > 1e-6) {
- msg += tl::sprintf ("agate: %.12g, agate_factor: %.12g, ", agate_int * dbu * dbu, gate_area_factor);
+ std::vector > values;
+
+ if (fabs (gate_area_factor - 1.0) <= db::epsilon && fabs (gate_perimeter_factor) <= db::epsilon) {
+ values.push_back (std::make_pair ("agate", agate));
+ } else {
+ if (fabs (gate_area_factor) > db::epsilon) {
+ values.push_back (std::make_pair ("agate", agate_int * dbu * dbu));
+ values.push_back (std::make_pair ("agate_factor", gate_area_factor));
+ }
+ if (fabs (gate_perimeter_factor) > db::epsilon) {
+ values.push_back (std::make_pair ("pgate", pgate_int * dbu));
+ values.push_back (std::make_pair ("pgate_factor", gate_perimeter_factor));
+ }
+ values.push_back (std::make_pair ("agate_eff", agate));
}
- if (fabs (gate_perimeter_factor) > 1e-6) {
- msg += tl::sprintf ("pgate: %.12g, pgate_factor: %.12g, ", pgate_int * dbu * dbu, gate_perimeter_factor);
- }
- msg += tl::sprintf ("ametal_eff: %.12g, ", ametal);
- if (fabs (metal_area_factor) > 1e-6) {
- msg += tl::sprintf ("ametal: %.12g, ametal_factor: %.12g, ", ametal_int * dbu * dbu, metal_area_factor);
- }
- if (fabs (metal_perimeter_factor) > 1e-6) {
- msg += tl::sprintf ("pmetal: %.12g, pmetal_factor: %.12g, ", pmetal_int * dbu * dbu, metal_perimeter_factor);
+ if (fabs (metal_area_factor - 1.0) <= db::epsilon && fabs (metal_perimeter_factor) <= db::epsilon) {
+ values.push_back (std::make_pair ("ametal", ametal));
+ } else {
+ if (fabs (metal_area_factor) > db::epsilon) {
+ values.push_back (std::make_pair ("ametal", ametal_int * dbu * dbu));
+ values.push_back (std::make_pair ("ametal_factor", metal_area_factor));
+ }
+ if (fabs (metal_perimeter_factor) > db::epsilon) {
+ values.push_back (std::make_pair ("pmetal", pmetal_int * dbu));
+ values.push_back (std::make_pair ("pmetal_factor", metal_perimeter_factor));
+ }
+ values.push_back (std::make_pair ("ametal_eff", ametal));
}
if (! adiodes_int.empty ()) {
- msg += "adiodes: [";
+ std::vector v;
+ v.reserve (adiodes_int.size ());
for (auto d = adiodes_int.begin (); d != adiodes_int.end (); ++d) {
- if (d != adiodes_int.begin ()) {
- msg += ", ";
- }
- msg += tl::sprintf ("%.12g", *d * dbu * dbu);
+ v.push_back (*d * dbu * dbu);
}
- msg += "], ";
+ values.push_back (std::make_pair ("adiodes", tl::Variant (v)));
}
if (! diodes.empty ()) {
- msg += "diode_factors: [";
+ std::vector v;
+ v.reserve (diodes.size ());
for (auto d = diodes.begin (); d != diodes.end (); ++d) {
- if (d != diodes.begin ()) {
- msg += ", ";
- }
- msg += tl::sprintf ("%.12g", d->second);
+ v.push_back (d->second);
}
- msg += "], ";
+ values.push_back (std::make_pair ("diode_factors", tl::Variant (v)));
}
- msg += tl::sprintf ("ratio: %.12g, ", ametal / agate);
- msg += tl::sprintf ("max_ratio_eff: %.12g, ", r);
- msg += tl::sprintf ("max_ratio: %.12g", ratio);
- return msg;
+ values.push_back (std::make_pair ("ratio", ametal / agate));
+ if (ratio > db::epsilon) {
+ if (fabs (r / ratio - 1.0) < db::epsilon) {
+ values.push_back (std::make_pair ("max_ratio", ratio));
+ } else {
+ values.push_back (std::make_pair ("max_ratio_eff", r));
+ values.push_back (std::make_pair ("max_ratio", ratio));
+ }
+ }
+ return values;
}
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes, db::Texts *values)
@@ -1538,25 +1584,50 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
}
if (tl::verbosity () >= 50) {
- tl::info << "cell [" << ly.cell_name (*cid) << "]: " <<
- create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
- ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
- diodes, adiodes_int, r, ratio, dbu);
+ std::vector > antenna_values =
+ create_antenna_values (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
+ ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
+ diodes, adiodes_int, r, ratio, dbu);
+ tl::info << "cell [" << ly.cell_name (*cid) << "]: ";
+ for (auto v = antenna_values.begin (); v != antenna_values.end (); ++v) {
+ tl::info << " " << v->first << ": " << v->second.to_string ();
+ }
}
if (ametal / agate > r + db::epsilon) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
- db::Point ref = get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
+
+ std::vector > antenna_values =
+ create_antenna_values (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
+ ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
+ diodes, adiodes_int, r, ratio, dbu);
+
+ db::properties_id_type prop_id = 0;
+ if (! values) {
+ db::PropertiesRepository::properties_set ps;
+ for (auto v = antenna_values.begin (); v != antenna_values.end (); ++v) {
+ ps.insert (std::make_pair (ly.properties_repository ().prop_name_id (v->first), v->second));
+ }
+ prop_id = ly.properties_repository ().properties_id (ps);
+ }
+
+ db::Point ref = get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), &ly, shapes, prop_id);
if (values) {
- // generate a data string with the details of the antenna computation (intentionally like JSON)
- std::string msg = create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
- ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
- diodes, adiodes_int, r, ratio, dbu);
-
db::Shapes &shapesv = ly.cell (*cid).shapes (dlv.layer ());
+
+ std::string msg;
+ for (auto v = antenna_values.begin (); v != antenna_values.end (); ++v) {
+ if (v != antenna_values.begin ()) {
+ msg += ", ";
+ }
+ msg += v->first;
+ msg += ": ";
+ msg += v->second.to_string ();
+ }
+
shapesv.insert (db::Text (msg, db::Trans (ref - db::Point ())));
}
diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc
index 7c00d4c9d..c3d339043 100644
--- a/src/db/db/gsiDeclDbCell.cc
+++ b/src/db/db/gsiDeclDbCell.cc
@@ -4370,6 +4370,17 @@ Class decl_Instance ("db", "Instance",
// ---------------------------------------------------------------
// db::ParentInstRep binding (to "ParentInstArray")
+static db::DCellInstArray
+dinst (const db::ParentInstRep *parent_inst)
+{
+ const db::Instances *instances = parent_inst->child_inst ().instances ();
+ if (! instances || ! instances->layout ()) {
+ return db::DCellInstArray ();
+ }
+
+ return cell_inst_array_defs::transform_array (parent_inst->inst (), db::CplxTrans (instances->layout ()->dbu ()));
+}
+
Class decl_ParentInstArray ("db", "ParentInstArray",
method ("parent_cell_index", &db::ParentInstRep::parent_cell_index,
"@brief Gets the index of the parent cell\n"
@@ -4381,6 +4392,11 @@ Class decl_ParentInstArray ("db", "ParentInstArray",
) +
method ("inst", &db::ParentInstRep::inst,
"@brief Compute the inverse instance by which the parent is seen from the child\n"
+ ) +
+ method_ext ("dinst", &dinst,
+ "@brief Compute the inverse instance by which the parent is seen from the child in micrometer units\n"
+ "\n"
+ "This convenience method has been introduced in version 0.28."
),
"@brief A parent instance\n"
"\n"
diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc
index 2bf9b60d2..bb5d379eb 100644
--- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc
+++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc
@@ -295,6 +295,30 @@ Class decl_dbNetlistDeviceExtractor ("db", "DeviceEx
"This class has been introduced in version 0.26."
);
+template
+static void
+define_terminal_by_names (GenericDeviceExtractor *extractor, db::Device *device, const std::string &terminal_name, const std::string &layer_name, const Shape &shape)
+{
+ if (! extractor->device_class ()) {
+ throw tl::Exception (tl::to_string (tr ("No device class registered yet")));
+ }
+
+ size_t terminal_id = extractor->device_class ()->terminal_id_for_name (terminal_name);
+
+ size_t layer_id = std::numeric_limits::max ();
+ for (auto l = extractor->begin_layer_definitions (); l != extractor->end_layer_definitions (); ++l) {
+ if (l->name == layer_name) {
+ layer_id = l->index;
+ }
+ }
+
+ if (layer_id == std::numeric_limits::max ()) {
+ throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + layer_name);
+ }
+
+ extractor->define_terminal (device, terminal_id, layer_id, shape);
+}
+
Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor",
gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup,
"@brief Sets up the extractor.\n"
@@ -392,6 +416,27 @@ Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceE
"This version produces a point-like terminal. Note that the point is\n"
"specified in database units.\n"
) +
+ gsi::method_ext ("define_terminal", &define_terminal_by_names,
+ gsi::arg ("device"), gsi::arg ("terminal_name"), gsi::arg ("layer_name"), gsi::arg ("shape"),
+ "@brief Defines a device terminal using names for terminal and layer.\n"
+ "\n"
+ "This convenience version of the ID-based \\define_terminal methods allows using names for terminal and layer.\n"
+ "It has been introduced in version 0.28."
+ ) +
+ gsi::method_ext ("define_terminal", &define_terminal_by_names,
+ gsi::arg ("device"), gsi::arg ("terminal_name"), gsi::arg ("layer_name"), gsi::arg ("shape"),
+ "@brief Defines a device terminal using names for terminal and layer.\n"
+ "\n"
+ "This convenience version of the ID-based \\define_terminal methods allows using names for terminal and layer.\n"
+ "It has been introduced in version 0.28."
+ ) +
+ gsi::method_ext ("define_terminal", &define_terminal_by_names,
+ gsi::arg ("device"), gsi::arg ("terminal_name"), gsi::arg ("layer_name"), gsi::arg ("point"),
+ "@brief Defines a device terminal using names for terminal and layer.\n"
+ "\n"
+ "This convenience version of the ID-based \\define_terminal methods allows using names for terminal and layer.\n"
+ "It has been introduced in version 0.28."
+ ) +
gsi::method ("dbu", &GenericDeviceExtractor::dbu,
"@brief Gets the database unit\n"
) +
diff --git a/src/doc/doc/about/packages.xml b/src/doc/doc/about/packages.xml
index 2b9c3daec..a2b7dad60 100644
--- a/src/doc/doc/about/packages.xml
+++ b/src/doc/doc/about/packages.xml
@@ -209,4 +209,27 @@
subversion equivalent.
+
Installation Hooks
+
+
+ Scripts can register an event through which
+ indicates that packages have been installed or uninstalled.
+
+
+
+ Packages itself can supply special scripts which are executed after a package was installed
+ or before a package is uninstalled:
+
+
+
+
_install.lym: if present, this script is executed after the package is installed.
+
_uninstall.lym: if present, this script is executed before the package is uninstalled.
+
+
+
+ Both scripts need to be stored in the same location as "grain.xml" and have to use
+ "lym" format. This is the generic XML script format KLayout employs as an interpreter-agnostic
+ script representation.
+
A measurement can be performed by clicking on the ruler icon in the
@@ -23,6 +24,32 @@
is attached perpendicular to the edge next to the initial point.
+
+ 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
+ shows the x and y coordinate.
+
+
+
+ The "Multi-Ruler" allows concatenating multiple rulers into a single
+ 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.
+
+
+
+ The "Angle" ruler allows angle measurements. Three clicks are required
+ to define a "V" like arrangement of two rulers. The angle enclosed by the two lines forming the "V"
+ is shown in the ruler.
+
+
+
+ Another special ruler, the "Radius" ruler is also a three-click type.
+ Specify three points to define a circle. The ruler shows the radius
+ of this circle and the circle outline.
+
+
Rulers can be configured in manifold ways. Use "Rulers And Annotations Setup"
in the "Edit" menu to open the ruler configuration dialog.
@@ -52,7 +79,7 @@
Rulers can be moved by selecting "Move" mode with the speedbar buttons
- in the toolbar or "Move" from the "Mode" submenu in the "Edit" menu. Then
+ in the toolbar or "Move" from the "Mode" sub-menu in the "Edit" menu. Then
left-click and drag the ruler or the ruler end point that should be changed.