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_le style_cb outline_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. +

+ diff --git a/src/doc/doc/manual/measure.xml b/src/doc/doc/manual/measure.xml index 836557ac3..06df213fc 100644 --- a/src/doc/doc/manual/measure.xml +++ b/src/doc/doc/manual/measure.xml @@ -5,6 +5,7 @@ Doing Measurements +

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.

diff --git a/src/doc/doc/manual/ruler_properties.xml b/src/doc/doc/manual/ruler_properties.xml index d889bd883..a1ffed576 100644 --- a/src/doc/doc/manual/ruler_properties.xml +++ b/src/doc/doc/manual/ruler_properties.xml @@ -76,6 +76,7 @@
  • X: The horizontal extension of the ruler in micron units.
  • Y: The vertical extension of the ruler in micron units.
  • A: The area enclosed by the ruler (if it was a box) in square millimeters.
  • +
  • G: The angle enclosed by the first and last segment of the ruler (used for angle measurement rulers).
  • diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index cde690911..ce5c0d14e 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -61,7 +61,6 @@ InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *mana for (edt::Service::obj_iterator s = service->selection ().begin (); s != service->selection ().end (); ++s) { m_selection_ptrs.push_back (s); } - m_index = 0; m_prop_id = 0; mp_service->clear_highlights (); @@ -156,6 +155,10 @@ get_cell_or_pcell_ids_by_name (const db::Layout *layout, const std::string &name void InstPropertiesPage::browse_cell () { + if (m_indexes.empty ()) { + return; + } + BEGIN_PROTECTED // find the layout the cell has to be looked up: that is either the layout of the current instance or @@ -166,7 +169,7 @@ BEGIN_PROTECTED lib = lib_cbx->current_library (); layout = &lib->layout (); } else { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); layout = &cv->layout (); } @@ -207,8 +210,12 @@ END_PROTECTED void InstPropertiesPage::show_props () { + if (m_indexes.empty ()) { + return; + } + lay::UserPropertiesForm props_form (this); - if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + if (props_form.show (mp_service->view (), m_selection_ptrs [m_indexes.front ()]->cv_index (), m_prop_id)) { emit edited (); } } @@ -226,43 +233,67 @@ InstPropertiesPage::display_mode_changed (bool) update (); } -void -InstPropertiesPage::back () +size_t +InstPropertiesPage::count () const { - m_index = (unsigned int) m_selection_ptrs.size (); + return m_selection_ptrs.size (); } -void -InstPropertiesPage::front () +void +InstPropertiesPage::select_entries (const std::vector &entries) { - m_index = 0; + m_indexes = entries; } -bool -InstPropertiesPage::at_begin () const +std::string +InstPropertiesPage::description (size_t entry) const { - return (m_index == 0); + std::string d; + + edt::Service::obj_iterator pos = m_selection_ptrs [entry]; + if (! pos->is_cell_inst ()) { + return d; + } + + const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); + double dbu = cv->layout ().dbu (); + + db::Layout *def_layout = &cv->layout (); + db::cell_index_type def_cell_index = pos->back ().inst_ptr.cell_index (); + std::pair dl = def_layout->defining_library (def_cell_index); + if (dl.first) { + def_layout = &dl.first->layout (); + def_cell_index = dl.second; + } + + std::pair pci = def_layout->is_pcell_instance (def_cell_index); + if (pci.first && def_layout->pcell_declaration (pci.second)) { + d += def_layout->pcell_header (pci.second)->get_name (); + } else { + d += def_layout->cell_name (def_cell_index); + } + + db::ICplxTrans t (pos->back ().inst_ptr.complex_trans ()); + db::DCplxTrans dt = db::CplxTrans (dbu) * t * db::CplxTrans (dbu).inverted (); + + db::Vector rowv, columnv; + unsigned long rows, columns; + if (pos->back ().inst_ptr.is_regular_array (rowv, columnv, rows, columns)) { + d += tl::sprintf ("(%s; array %dx%d)", dt.to_string (true), rows, columns); + } else { + d += tl::sprintf ("(%s)", dt.to_string (true)); + } + + return d; } -bool -InstPropertiesPage::at_end () const +std::string +InstPropertiesPage::description () const { - return (m_index == m_selection_ptrs.size ()); + return tl::to_string (tr ("Instances")); } -void -InstPropertiesPage::operator-- () -{ - --m_index; -} - -void -InstPropertiesPage::operator++ () -{ - ++m_index; -} - -void +void InstPropertiesPage::leave () { mp_service->clear_highlights (); @@ -271,10 +302,14 @@ InstPropertiesPage::leave () void InstPropertiesPage::update () { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + if (m_indexes.empty ()) { + return; + } + + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; tl_assert (pos->is_cell_inst ()); - mp_service->highlight (m_index); + mp_service->highlight (m_indexes); m_enable_cb_callback = false; dbu_cb->setChecked (mp_service->view ()->dbu_coordinates ()); @@ -382,7 +417,11 @@ InstPropertiesPage::update () void InstPropertiesPage::show_cell () { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + if (m_indexes.empty ()) { + return; + } + + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; lay::CellView::unspecific_cell_path_type path (mp_service->view ()->cellview (pos->cv_index ()).combined_unspecific_path ()); for (lay::ObjectInstPath::iterator p = pos->begin (); p != pos->end (); ++p) { @@ -395,8 +434,12 @@ InstPropertiesPage::show_cell () void InstPropertiesPage::show_inst () { + if (m_indexes.empty ()) { + return; + } + InstantiationForm inst_form (this); - inst_form.show (mp_service->view (), *m_selection_ptrs [m_index]); + inst_form.show (mp_service->view (), *m_selection_ptrs [m_indexes.front ()]); } bool @@ -408,12 +451,14 @@ InstPropertiesPage::readonly () ChangeApplicator * InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & /*inst*/, double dbu) { + tl_assert (! m_indexes.empty ()); + bool has_error = false; bool has_pcell_error = false; std::unique_ptr appl (new CombinedChangeApplicator ()); - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); bool du = dbu_cb->isChecked (); @@ -707,13 +752,17 @@ InstPropertiesPage::recompute_selection_ptrs (const std::vectorview ()->layer_snapshot (); - unsigned int cv_index = m_selection_ptrs [m_index]->cv_index (); + unsigned int cv_index = m_selection_ptrs [m_indexes.front ()]->cv_index (); std::unique_ptr applicator; { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; tl_assert (pos->is_cell_inst ()); const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); @@ -741,7 +790,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) // But it avoids issues with duplicate selections of the same instance which may happen when // an instance is selected multiple times through different hierarchy branches. - db::Instance current = m_selection_ptrs [m_index]->back ().inst_ptr; + db::Instance current = m_selection_ptrs [m_indexes.front ()]->back ().inst_ptr; std::vector new_sel; new_sel.reserve (m_selection_ptrs.size ()); @@ -755,9 +804,10 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) try { - for (std::vector::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) { + for (auto ii = m_indexes.begin (); ii != m_indexes.end (); ++ii) { - edt::Service::obj_iterator pos = *p; + size_t index = *ii; + edt::Service::obj_iterator pos = m_selection_ptrs [*ii]; // only update objects from the same layout - this is not practical limitation but saves a lot of effort for // managing different property id's etc. @@ -790,8 +840,6 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) if (new_inst != pos->back ().inst_ptr) { - size_t index = p - m_selection_ptrs.begin (); - // change selection to new instance new_sel[index].back ().inst_ptr = new_inst; @@ -844,6 +892,10 @@ InstPropertiesPage::apply_to_all (bool relative) void InstPropertiesPage::update_pcell_parameters () { + if (m_indexes.empty ()) { + return; + } + db::Layout *layout; // find the layout the cell has to be looked up: that is either the layout of the current instance or @@ -854,7 +906,7 @@ InstPropertiesPage::update_pcell_parameters () } else { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); layout = &cv->layout (); @@ -878,7 +930,7 @@ InstPropertiesPage::update_pcell_parameters () std::vector parameters; - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); db::Cell &cell = cv->layout ().cell (pos->cell_index ()); std::pair pci = cell.is_pcell_instance (pos->back ().inst_ptr); diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index 07a193a76..3e124161d 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -47,12 +47,10 @@ public: InstPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); ~InstPropertiesPage (); - 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 leave (); private: @@ -61,7 +59,7 @@ private: protected: std::vector m_selection_ptrs; - unsigned int m_index; + std::vector m_indexes; edt::Service *mp_service; bool m_enable_cb_callback; db::properties_id_type m_prop_id; diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index f2a91af17..0f97e0166 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -30,6 +30,7 @@ #include "laySelector.h" #include "layFinder.h" #include "layLayerProperties.h" +#include "laybasicConfig.h" #include "tlProgress.h" #include "edtPlugin.h" #include "edtMainService.h" diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 7f01f3158..4377281e2 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -43,15 +43,14 @@ namespace edt // ------------------------------------------------------------------------- // ShapePropertiesPage implementation -ShapePropertiesPage::ShapePropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) +ShapePropertiesPage::ShapePropertiesPage (const std::string &description, edt::Service *service, db::Manager *manager, QWidget *parent) : lay::PropertiesPage (parent, manager, service), - mp_service (service), m_enable_cb_callback (true) + m_description (description), mp_service (service), m_enable_cb_callback (true) { m_selection_ptrs.reserve (service->selection ().size ()); for (edt::Service::obj_iterator s = service->selection ().begin (); s != service->selection ().end (); ++s) { m_selection_ptrs.push_back (s); } - m_index = 0; m_prop_id = 0; mp_service->clear_highlights (); } @@ -73,43 +72,78 @@ ShapePropertiesPage::setup () m_enable_cb_callback = true; } -void -ShapePropertiesPage::back () +size_t +ShapePropertiesPage::count () const { - m_index = (unsigned int) m_selection_ptrs.size (); + return m_selection_ptrs.size (); } -void -ShapePropertiesPage::front () +void +ShapePropertiesPage::select_entries (const std::vector &entries) { - m_index = 0; + m_indexes = entries; } -bool -ShapePropertiesPage::at_begin () const +lay::LayoutViewBase * +ShapePropertiesPage::view () const { - return (m_index == 0); + return mp_service->view (); } -bool -ShapePropertiesPage::at_end () const +const db::Shape & +ShapePropertiesPage::shape (size_t entry) const { - return (m_index == m_selection_ptrs.size ()); + return m_selection_ptrs [entry]->shape (); } -void -ShapePropertiesPage::operator-- () +double +ShapePropertiesPage::dbu (size_t entry) const { - --m_index; + unsigned int cv_index = m_selection_ptrs [entry]->cv_index (); + return view ()->cellview (cv_index)->layout ().dbu (); } -void -ShapePropertiesPage::operator++ () +std::string +ShapePropertiesPage::description (size_t entry) const { - ++m_index; + unsigned int cv_index = m_selection_ptrs [entry]->cv_index (); + unsigned int layer = m_selection_ptrs [entry]->layer (); + + if (view ()->cellview (cv_index).is_valid ()) { + const db::LayerProperties &lp = view ()->cellview (cv_index)->layout ().get_properties (layer); + if (view ()->cellviews () > 1) { + return lp.to_string () + "@" + tl::to_string (cv_index + 1); + } else { + return lp.to_string (); + } + } + + return std::string (); } -void +QIcon +ShapePropertiesPage::icon (size_t entry, int w, int h) const +{ + int cv_index = m_selection_ptrs [entry]->cv_index (); + int layer = m_selection_ptrs [entry]->layer (); + + auto *view = mp_service->view (); + for (auto lp = view->begin_layers (view->current_layer_list ()); ! lp.at_end (); ++lp) { + const lay::LayerPropertiesNode *ln = lp.operator-> (); + if (ln->cellview_index () == cv_index && ln->layer_index () == layer) { + return QIcon (QPixmap::fromImage (view->icon_for_layer (lp, w, h).to_image ())); + } + } + + return QIcon (); +} +std::string +ShapePropertiesPage::description () const +{ + return m_description; +} + +void ShapePropertiesPage::leave () { mp_service->clear_highlights (); @@ -130,8 +164,8 @@ ShapePropertiesPage::abs_trans () const db::ICplxTrans ShapePropertiesPage::trans () const { - if (abs_trans ()) { - return m_selection_ptrs[m_index]->trans (); + if (abs_trans () && ! m_indexes.empty ()) { + return m_selection_ptrs[m_indexes.front ()]->trans (); } else { return db::ICplxTrans (); } @@ -154,7 +188,7 @@ END_PROTECTED void ShapePropertiesPage::update () { - mp_service->highlight (m_index); + mp_service->highlight (m_indexes); update_shape (); } @@ -179,12 +213,16 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector applicator; - unsigned int cv_index = m_selection_ptrs [m_index]->cv_index (); + unsigned int cv_index = m_selection_ptrs [m_indexes.front ()]->cv_index (); { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; tl_assert (! pos->is_cell_inst ()); const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); @@ -213,7 +251,7 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative) // But it avoids issues with duplicate selections of the same shape which may happen when // a shape is selected multiple times through different hierarchy branches. - db::Shape current = m_selection_ptrs [m_index]->shape (); + db::Shape current = m_selection_ptrs [m_indexes.front ()]->shape (); std::vector new_sel; new_sel.reserve (m_selection_ptrs.size ()); @@ -227,11 +265,10 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative) try { - for (std::vector::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) { + for (auto i = m_indexes.begin (); i != m_indexes.end (); ++i) { - size_t index = p - m_selection_ptrs.begin (); - - edt::Service::obj_iterator pos = *p; + size_t index = *i; + edt::Service::obj_iterator pos = m_selection_ptrs [*i]; // only update objects from the same layout - this is not practical limitation but saves a lot of effort for // managing different property id's etc. @@ -329,7 +366,11 @@ ShapePropertiesPage::apply_to_all (bool relative) void ShapePropertiesPage::update_shape () { - edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; + if (m_indexes.empty ()) { + return; + } + + edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()]; const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); double dbu = cv->layout ().dbu (); @@ -361,15 +402,23 @@ ShapePropertiesPage::update_shape () void ShapePropertiesPage::show_inst () { + if (m_indexes.empty ()) { + return; + } + InstantiationForm inst_form (this); - inst_form.show (mp_service->view (), *m_selection_ptrs [m_index]); + inst_form.show (mp_service->view (), *m_selection_ptrs [m_indexes.front ()]); } void ShapePropertiesPage::show_props () { + if (m_indexes.empty ()) { + return; + } + lay::UserPropertiesForm props_form (this); - if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + if (props_form.show (mp_service->view (), m_selection_ptrs [m_indexes.front ()]->cv_index (), m_prop_id)) { emit edited (); } } @@ -384,7 +433,7 @@ ShapePropertiesPage::readonly () // PolygonPropertiesPage implementation PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) + : ShapePropertiesPage (tl::to_string (tr ("Polygons")), service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); @@ -405,7 +454,34 @@ PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager } } -void +static size_t count_polygon_points (const db::Shape &sh) +{ + size_t n = 0; + for (auto pt = sh.begin_hull (); pt != sh.end_hull (); ++pt) { + ++n; + } + return n; +} + +std::string +PolygonPropertiesPage::description (size_t entry) const +{ + const db::Shape &sh = shape (entry); + + size_t npts = count_polygon_points (sh); + if (sh.holes () == 0 && npts > 4) { + return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Polygon(%d points)")), npts); + } else if (sh.holes () > 0) { + return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Polygon(%d points, %d holes)")), npts, sh.holes ()); + } else { + db::Polygon poly; + sh.polygon (poly); + db::CplxTrans dbu_trans (dbu (entry)); + return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Polygon%s")), (dbu_trans * poly).to_string ()); + } +} + +void PolygonPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -536,7 +612,7 @@ PolygonPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Sha static bool s_coordinateMode = true; BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent), + : ShapePropertiesPage (tl::to_string (tr ("Boxes")), service, manager, parent), m_recursion_sentinel (false), m_tab_index (0), m_dbu (1.0), m_lr_swapped (false), m_tb_swapped (false) { setupUi (this); @@ -573,6 +649,14 @@ BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manage connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); } +std::string +BoxPropertiesPage::description (size_t entry) const +{ + const db::Shape &sh = shape (entry); + db::CplxTrans dbu_trans (dbu (entry)); + return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Box%s")), (dbu_trans * sh.box ()).to_string ()); +} + void BoxPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { @@ -773,7 +857,7 @@ BoxPropertiesPage::changed () // TextPropertiesPage implementation TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (tl::to_string (tr ("Texts")), service, manager, parent) { setupUi (this); setup (); @@ -804,7 +888,17 @@ TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *mana } } -void +std::string +TextPropertiesPage::description (size_t entry) const +{ + const db::Shape &sh = shape (entry); + db::Text text; + sh.text (text); + db::CplxTrans dbu_trans (dbu (entry)); + return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Text%s")), (dbu_trans * text).to_string ()); +} + +void TextPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -907,7 +1001,7 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape // PathPropertiesPage implementation PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) + : ShapePropertiesPage (tl::to_string (tr ("Paths")), service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); @@ -928,6 +1022,34 @@ PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *mana round_cb->setEnabled (false); } +static size_t count_path_points (const db::Shape &sh) +{ + size_t n = 0; + for (auto pt = sh.begin_point (); pt != sh.end_point (); ++pt) { + ++n; + } + return n; +} + +static std::string path_description (const db::Shape &sh, double dbu) +{ + size_t npts = count_path_points (sh); + if (npts > 4) { + return tl::sprintf (tl::to_string (tr ("Path(%d points, w=%.12g)")), npts, sh.path_width () * dbu); + } else { + db::CplxTrans dbu_trans (dbu); + db::Path path; + sh.path (path); + return tl::sprintf (tl::to_string (tr ("Path%s")), (dbu_trans * path).to_string ()); + } +} + +std::string +PathPropertiesPage::description (size_t entry) const +{ + return ShapePropertiesPage::description (entry) + " - " + path_description (shape (entry), dbu (entry)); +} + void PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { @@ -974,7 +1096,7 @@ PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape // EditablePathPropertiesPage implementation EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) + : ShapePropertiesPage (tl::to_string (tr ("Paths")), service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); @@ -1026,6 +1148,12 @@ EditablePathPropertiesPage::text_changed () m_in_text_changed = false; } +std::string +EditablePathPropertiesPage::description (size_t entry) const +{ + return ShapePropertiesPage::description (entry) + " - " + path_description (shape (entry), dbu (entry)); +} + void EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 0172cd1c5..834da0fb2 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -46,15 +46,14 @@ class ShapePropertiesPage Q_OBJECT public: - ShapePropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + ShapePropertiesPage (const std::string &description, edt::Service *service, db::Manager *manager, QWidget *parent); ~ShapePropertiesPage (); - 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 QIcon icon (size_t entry, int w, int h) const; + virtual std::string description () const; virtual void leave (); protected: @@ -69,8 +68,9 @@ private: void recompute_selection_ptrs (const std::vector &new_sel); protected: + std::string m_description; std::vector m_selection_ptrs; - unsigned int m_index; + std::vector m_indexes; edt::Service *mp_service; bool m_enable_cb_callback; db::properties_id_type m_prop_id; @@ -83,6 +83,9 @@ protected: bool abs_trans () const; db::ICplxTrans trans () const; void setup (); + lay::LayoutViewBase *view () const; + const db::Shape &shape (size_t entry) const; + double dbu (size_t entry) const; public slots: void show_inst (); @@ -101,6 +104,7 @@ Q_OBJECT public: PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + virtual std::string description (size_t entry) const; virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname); virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu); @@ -124,6 +128,7 @@ Q_OBJECT public: BoxPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + virtual std::string description (size_t entry) const; virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname); virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu); @@ -153,6 +158,7 @@ Q_OBJECT public: TextPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + virtual std::string description (size_t entry) const; virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname); virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu); @@ -170,6 +176,7 @@ Q_OBJECT public: PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + virtual std::string description (size_t entry) const; virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname); virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu); @@ -190,6 +197,7 @@ Q_OBJECT public: EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent); + virtual std::string description (size_t entry) const; virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname); virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu); diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index e49d0248b..ae5a4dcb7 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -77,6 +77,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter m_hier_copy_mode (-1), m_indicate_secondary_selection (false), m_seq (0), + m_highlights_selected (false), dm_selection_to_view (this, &edt::Service::do_selection_to_view) { mp_view->geom_changed_event.add (this, &edt::Service::selection_to_view); @@ -97,6 +98,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_hier_copy_mode (-1), m_indicate_secondary_selection (false), m_seq (0), + m_highlights_selected (false), dm_selection_to_view (this, &edt::Service::do_selection_to_view) { mp_view->geom_changed_event.add (this, &edt::Service::selection_to_view); @@ -271,28 +273,36 @@ Service::configure (const std::string &name, const std::string &value) void Service::clear_highlights () { - for (std::vector::iterator r = m_markers.begin (); r != m_markers.end (); ++r) { - (*r)->visible (false); - } + m_highlights_selected = true; + m_selected_highlights.clear (); + apply_highlights (); } void Service::restore_highlights () { - for (std::vector::iterator r = m_markers.begin (); r != m_markers.end (); ++r) { - (*r)->visible (true); - } + m_highlights_selected = false; + m_selected_highlights.clear (); + apply_highlights (); } -void -Service::highlight (unsigned int n) +void +Service::highlight (const std::vector &n) +{ + m_highlights_selected = true; + m_selected_highlights = std::set (n.begin (), n.end ()); + apply_highlights (); +} + +void +Service::apply_highlights () { for (std::vector::iterator r = m_markers.begin (); r != m_markers.end (); ++r) { - (*r)->visible (n-- == 0); + (*r)->visible (! m_highlights_selected || m_selected_highlights.find (r - m_markers.begin ()) != m_selected_highlights.end ()); } } -void +void Service::cut () { if (has_selection () && view ()->is_editable ()) { @@ -1597,6 +1607,8 @@ Service::do_selection_to_view () } } + + apply_highlights (); } void diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 81f3ea400..26c56b150 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -105,10 +105,10 @@ public: */ void restore_highlights (); - /** - * @brief Highlight a certain object + /** + * @brief Highlights a group of objects */ - void highlight (unsigned int n); + void highlight (const std::vector &n); /** * @brief "delete" operation @@ -616,6 +616,10 @@ private: bool m_indicate_secondary_selection; unsigned long m_seq; + // selective highlights + bool m_highlights_selected; + std::set m_selected_highlights; + // Deferred method to update the selection tl::DeferredMethod dm_selection_to_view; @@ -646,6 +650,11 @@ private: */ void display_status (bool transient); + /** + * @brief Apply highlight selection + */ + void apply_highlights (); + private: void copy_selected (unsigned int inst_mode); }; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 60cd2ee29..a88ce7d88 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -381,10 +381,12 @@ PolygonService::PolygonService (db::Manager *manager, lay::LayoutViewBase *view) } #if defined(HAVE_QT) -lay::PropertiesPage * -PolygonService::properties_page (db::Manager *manager, QWidget *parent) +std::vector +PolygonService::properties_pages (db::Manager *manager, QWidget *parent) { - return new edt::PolygonPropertiesPage (this, manager, parent); + std::vector pages; + pages.push_back (new edt::PolygonPropertiesPage (this, manager, parent)); + return pages; } #endif @@ -712,10 +714,12 @@ BoxService::BoxService (db::Manager *manager, lay::LayoutViewBase *view) } #if defined(HAVE_QT) -lay::PropertiesPage * -BoxService::properties_page (db::Manager *manager, QWidget *parent) +std::vector +BoxService::properties_pages (db::Manager *manager, QWidget *parent) { - return new edt::BoxPropertiesPage (this, manager, parent); + std::vector pages; + pages.push_back (new edt::BoxPropertiesPage (this, manager, parent)); + return pages; } #endif @@ -812,10 +816,12 @@ TextService::~TextService () } #if defined(HAVE_QT) -lay::PropertiesPage * -TextService::properties_page (db::Manager *manager, QWidget *parent) +std::vector +TextService::properties_pages (db::Manager *manager, QWidget *parent) { - return new edt::TextPropertiesPage (this, manager, parent); + std::vector pages; + pages.push_back (new edt::TextPropertiesPage (this, manager, parent)); + return pages; } #endif @@ -1004,14 +1010,16 @@ PathService::~PathService () } #if defined(HAVE_QT) -lay::PropertiesPage * -PathService::properties_page (db::Manager *manager, QWidget *parent) +std::vector +PathService::properties_pages (db::Manager *manager, QWidget *parent) { + std::vector pages; if (view ()->is_editable ()) { - return new edt::EditablePathPropertiesPage (this, manager, parent); + pages.push_back (new edt::EditablePathPropertiesPage (this, manager, parent)); } else { - return new edt::PathPropertiesPage (this, manager, parent); + pages.push_back (new edt::PathPropertiesPage (this, manager, parent)); } + return pages; } #endif @@ -1246,10 +1254,12 @@ InstService::InstService (db::Manager *manager, lay::LayoutViewBase *view) } #if defined(HAVE_QT) -lay::PropertiesPage * -InstService::properties_page (db::Manager *manager, QWidget *parent) +std::vector +InstService::properties_pages (db::Manager *manager, QWidget *parent) { - return new edt::InstPropertiesPage (this, manager, parent); + std::vector pages; + pages.push_back (new edt::InstPropertiesPage (this, manager, parent)); + return pages; } #endif diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 1de7c70a6..40a5ed584 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -90,7 +90,7 @@ public: PolygonService (db::Manager *manager, lay::LayoutViewBase *view); #if defined(HAVE_QT) - virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); + virtual std::vector properties_pages (db::Manager *manager, QWidget *parent); #endif virtual void do_delete (); virtual void do_begin_edit (const db::DPoint &p); @@ -123,7 +123,7 @@ public: BoxService (db::Manager *manager, lay::LayoutViewBase *view); #if defined(HAVE_QT) - virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); + virtual std::vector properties_pages (db::Manager *manager, QWidget *parent); #endif virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); @@ -151,7 +151,7 @@ public: ~TextService (); #if defined(HAVE_QT) - virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); + virtual std::vector properties_pages (db::Manager *manager, QWidget *parent); #endif virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans); @@ -185,7 +185,7 @@ public: ~PathService (); #if defined(HAVE_QT) - virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); + virtual std::vector properties_pages (db::Manager *manager, QWidget *parent); #endif virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); @@ -223,7 +223,7 @@ public: InstService (db::Manager *manager, lay::LayoutViewBase *view); #if defined(HAVE_QT) - virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); + virtual std::vector properties_pages (db::Manager *manager, QWidget *parent); #endif virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move_inactive (const db::DPoint &p); diff --git a/src/icons/icons.qrc b/src/icons/icons.qrc index e082993eb..7067e958d 100644 --- a/src/icons/icons.qrc +++ b/src/icons/icons.qrc @@ -52,6 +52,8 @@ images/clear_edit_16px@2x.png images/clearbreakpoints_16px.png images/clearbreakpoints_16px@2x.png + images/clone_16px.png + images/clone_16px@2x.png images/cm_add_24px.png images/cm_add_24px@2x.png images/cm_diff_24px.png @@ -115,6 +117,7 @@ images/left_16px.png images/left_16px@2x.png images/logo.png + images/logo@2x.png images/lt_31px.png images/lt_31px@2x.png images/m0_24px.png diff --git a/src/icons/images/clone_16px.png b/src/icons/images/clone_16px.png new file mode 100644 index 000000000..9cc80dafc Binary files /dev/null and b/src/icons/images/clone_16px.png differ diff --git a/src/icons/images/clone_16px@2x.png b/src/icons/images/clone_16px@2x.png new file mode 100644 index 000000000..ac31d6e31 Binary files /dev/null and b/src/icons/images/clone_16px@2x.png differ diff --git a/src/icons/images/logo.png b/src/icons/images/logo.png index 4451152f6..2095cab0b 100644 Binary files a/src/icons/images/logo.png and b/src/icons/images/logo.png differ diff --git a/src/icons/images/logo@2x.png b/src/icons/images/logo@2x.png new file mode 100755 index 000000000..f5c0f7d2d Binary files /dev/null and b/src/icons/images/logo@2x.png differ diff --git a/src/icons/svg/clone_16px.svg b/src/icons/svg/clone_16px.svg new file mode 100644 index 000000000..c08c3d904 --- /dev/null +++ b/src/icons/svg/clone_16px.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index 7107a0a90..f7d966cfa 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -44,7 +44,7 @@ PropertiesPage::PropertiesPage (img::Service *service, db::Manager *manager, QWi : lay::PropertiesPage (parent, manager, service), mp_service (service), mp_direct_image (0) { mp_service->get_selection (m_selection); - m_pos = m_selection.begin (); + m_index = 0; mp_service->clear_highlights (); @@ -107,10 +107,12 @@ PropertiesPage::init () colors->set_color (std::make_pair (QColor (), QColor ())); colors->setEnabled (false); + value_le->setEnabled (false); connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ())); connect (colors, SIGNAL (color_changed (std::pair)), false_color_control, SLOT (set_current_color (std::pair))); connect (false_color_control, SIGNAL (selection_changed (std::pair)), colors, SLOT (set_color (std::pair))); + connect (false_color_control, SIGNAL (selection_changed (std::pair)), this, SLOT (color_mapping_changed ())); connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ())); connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int))); @@ -154,44 +156,40 @@ PropertiesPage::invalidate () } } -void -PropertiesPage::back () +size_t +PropertiesPage::count () const { - m_pos = m_selection.end (); + return m_selection.size (); +} + +void +PropertiesPage::select_entries (const std::vector &entries) +{ + tl_assert (entries.size () == 1); + m_index = entries.front (); invalidate (); } -void -PropertiesPage::front () +std::string +PropertiesPage::description (size_t entry) const { - m_pos = m_selection.begin (); - invalidate (); + const img::Object *obj = dynamic_cast (m_selection [entry]->ptr ()); + if (! obj) { + return std::string ("nil"); + } + + std::string d = tl::to_string (tr ("Image")); + if (! obj->filename ().empty ()) { + d += "[" + tl::filename (obj->filename ()) + "]"; + } + d += tl::sprintf ("(%dx%d)", obj->width (), obj->height ()); + return d; } -bool -PropertiesPage::at_begin () const +std::string +PropertiesPage::description () const { - return (m_pos == m_selection.begin ()); -} - -bool -PropertiesPage::at_end () const -{ - return (m_pos == m_selection.end ()); -} - -void -PropertiesPage::operator-- () -{ - --m_pos; - invalidate (); -} - -void -PropertiesPage::operator++ () -{ - ++m_pos; - invalidate (); + return tl::to_string (tr ("Images")); } void @@ -378,11 +376,11 @@ PropertiesPage::update () if (mp_service) { - mp_service->highlight (std::distance (m_selection.begin (), m_pos)); + mp_service->highlight (m_index); // create a local copy in which we can apply modifications if (! mp_direct_image) { - const img::Object *image = dynamic_cast ((*m_pos)->ptr ()); + const img::Object *image = dynamic_cast (m_selection [m_index]->ptr ()); mp_direct_image = new img::Object (*image); } @@ -908,7 +906,7 @@ PropertiesPage::apply () mp_direct_image->set_data_mapping (dm); if (mp_service) { - mp_service->change_image (*m_pos, *mp_direct_image); + mp_service->change_image (m_selection [m_index], *mp_direct_image); } } diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index 797bedd86..1c864d86e 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -50,12 +50,10 @@ public: PropertiesPage (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 (); @@ -91,7 +89,7 @@ private slots: private: std::vector m_selection; - std::vector ::iterator m_pos; + size_t m_index; img::Service *mp_service; img::Object *mp_direct_image; bool m_no_signals; diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 6c9e2ec78..39448cb5d 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -1377,10 +1377,12 @@ Service::display_status (bool transient) } #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 img::PropertiesPage (this, manager, parent); + std::vector pages; + pages.push_back (new img::PropertiesPage (this, manager, parent)); + return pages; } #endif diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 9379c9b49..929110152 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -373,7 +373,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 /** diff --git a/src/img/img/imgWidgets.cc b/src/img/img/imgWidgets.cc index 59e662d2b..3aa277bb8 100644 --- a/src/img/img/imgWidgets.cc +++ b/src/img/img/imgWidgets.cc @@ -63,8 +63,8 @@ TwoColorWidget::TwoColorWidget (QWidget *parent) mp_lock->setIconSize (QSize (16, 16)); QIcon icon; - icon.addFile (":/locked_16.png", QSize (), QIcon::Normal, QIcon::On); - icon.addFile (":/unlocked_16.png", QSize (), QIcon::Normal, QIcon::Off); + icon.addFile (":/locked_16px.png", QSize (), QIcon::Normal, QIcon::On); + icon.addFile (":/unlocked_16px.png", QSize (), QIcon::Normal, QIcon::Off); mp_lock->setIcon (icon); connect (mp_left, SIGNAL (color_changed (QColor)), this, SLOT (lcolor_changed (QColor))); diff --git a/src/lay/lay/HelpAboutDialog.ui b/src/lay/lay/HelpAboutDialog.ui index 69753802a..07b329094 100644 --- a/src/lay/lay/HelpAboutDialog.ui +++ b/src/lay/lay/HelpAboutDialog.ui @@ -137,7 +137,7 @@ - :/logo@2x.png + :/logo.png
    diff --git a/src/lay/lay/SearchReplaceDialog.ui b/src/lay/lay/SearchReplaceDialog.ui index 9c24eb188..0c9f821a6 100644 --- a/src/lay/lay/SearchReplaceDialog.ui +++ b/src/lay/lay/SearchReplaceDialog.ui @@ -171,7 +171,7 @@ p, li { white-space: pre-wrap; } - :/run.png:/run.png + :/run_16px.png:/run_16px.png true @@ -280,7 +280,7 @@ p, li { white-space: pre-wrap; } - :/run.png:/run.png + :/run_16px.png:/run_16px.png true @@ -454,7 +454,7 @@ p, li { white-space: pre-wrap; } - :/run.png:/run.png + :/run_16px.png:/run_16px.png true @@ -642,7 +642,7 @@ p, li { white-space: pre-wrap; } - :/run.png:/run.png + :/run_16px.png:/run_16px.png true diff --git a/src/lay/lay/TechSetupDialog.ui b/src/lay/lay/TechSetupDialog.ui index 71b39a402..999b2810b 100644 --- a/src/lay/lay/TechSetupDialog.ui +++ b/src/lay/lay/TechSetupDialog.ui @@ -108,6 +108,9 @@ + + Add technology + ... @@ -116,12 +119,15 @@ :/add_16px.png:/add_16px.png - true + false + + Delete technology + ... @@ -130,33 +136,37 @@ :/clear_16px.png:/clear_16px.png - true + false - + + + Rename technology + + + Rename + + + + :/rename_16px@2x.png:/rename_16px@2x.png + + + + + Qt::Horizontal - - QSizePolicy::Expanding - - 141 + 40 20 - - - - Rename - - - diff --git a/src/lay/lay/gsiDeclLayApplication.cc b/src/lay/lay/gsiDeclLayApplication.cc index fbf23517b..f59e77244 100644 --- a/src/lay/lay/gsiDeclLayApplication.cc +++ b/src/lay/lay/gsiDeclLayApplication.cc @@ -24,6 +24,7 @@ #include "layMainWindow.h" #include "laySignalHandler.h" #include "gsiDecl.h" +#include "gsiSignals.h" #include "tlArch.h" #if defined(HAVE_QTBINDINGS) @@ -243,6 +244,14 @@ static gsi::Methods application_methods () "\n" "There is exactly one instance of the application. This instance can be obtained with this " "method." + ) + + event ("on_salt_changed", &C::salt_changed_event, + "@brief This event is triggered when the package status changes.\n" + "\n" + "Register to this event if you are interested in package changes - i.e. installation or removal of packages or " + "package updates.\n" + "\n" + "This event has been introduced in version 0.28." ) ; } diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index 5aee10b73..707416e5a 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -672,6 +672,8 @@ ApplicationBase::init_app () } } + sc->salt_changed_event.add (this, &ApplicationBase::salt_changed); + } if (tc) { @@ -852,6 +854,14 @@ ApplicationBase::add_macro_category (const std::string &name, const std::string } } +void +ApplicationBase::salt_changed () +{ +BEGIN_PROTECTED_SILENT + salt_changed_event (); +END_PROTECTED_SILENT +} + ApplicationBase::~ApplicationBase () { tl::set_ui_exception_handlers (0, 0, 0); diff --git a/src/lay/lay/layApplication.h b/src/lay/lay/layApplication.h index 6b42e1318..79cf507fb 100644 --- a/src/lay/lay/layApplication.h +++ b/src/lay/lay/layApplication.h @@ -26,6 +26,7 @@ #include "layCommon.h" #include "layBusy.h" +#include "tlEvents.h" #include #include @@ -76,7 +77,7 @@ class LayoutView; * and one for the GUI version (derived from QApplication). */ class LAY_PUBLIC ApplicationBase - : public gsi::ObjectBase + : public gsi::ObjectBase, public tl::Object { public: /** @@ -313,6 +314,11 @@ public: */ void init_app (); + /** + * @brief An event indicating that the package collection has changed + */ + tl::Event salt_changed_event; + /** * @brief Gets the QApplication object * This method will return non-null only if a GUI-enabled application is present. @@ -376,6 +382,8 @@ private: // in order to maintain a valid MainWindow reference for ruby scripts and Ruby's GC all the time. gsi::Interpreter *mp_ruby_interpreter; gsi::Interpreter *mp_python_interpreter; + + void salt_changed (); }; /** diff --git a/src/lay/lay/laySalt.cc b/src/lay/lay/laySalt.cc index 6c47c949b..45313074d 100644 --- a/src/lay/lay/laySalt.cc +++ b/src/lay/lay/laySalt.cc @@ -26,6 +26,7 @@ #include "tlLog.h" #include "tlInternational.h" #include "tlWebDAV.h" +#include "lymMacro.h" #include #include @@ -281,6 +282,21 @@ Salt::remove_grain (const SaltGrain &grain) QString name = tl::to_qstring (grain.name ()); tl::info << QObject::tr ("Removing package '%1' ..").arg (name); + + // Execute "_uninstall.lym" if it exists + try { + QFile uninstall_lym (QDir (tl::to_qstring (grain.path ())).absoluteFilePath (tl::to_qstring ("_uninstall.lym"))); + if (uninstall_lym.exists ()) { + lym::Macro uninstall; + uninstall.load_from (tl::to_string (uninstall_lym.fileName ())); + uninstall.set_file_path (tl::to_string (uninstall_lym.fileName ())); + uninstall.run (); + } + } catch (tl::Exception &ex) { + // Errors in the uninstallation script are only logged, but do not prevent uninstallation + tl::error << ex.msg (); + } + bool res = remove_from_collection (m_root, grain.name ()); if (res) { tl::info << QObject::tr ("Package '%1' removed.").arg (name); @@ -493,10 +509,25 @@ Salt::create_grain (const SaltGrain &templ, SaltGrain &target, double timeout, t if (res) { - tl::info << QObject::tr ("Package '%1' installed").arg (tl::to_qstring (target.name ())); target.set_installed_time (QDateTime::currentDateTime ()); target.save (); + // Execute "_install.lym" if it exists + try { + QFile install_lym (QDir (tl::to_qstring (target.path ())).absoluteFilePath (tl::to_qstring ("_install.lym"))); + if (install_lym.exists ()) { + lym::Macro install; + install.load_from (tl::to_string (install_lym.fileName ())); + install.set_file_path (tl::to_string (install_lym.fileName ())); + install.run (); + } + } catch (tl::Exception &ex) { + // Errors in the installation script are only logged, but do not prevent installation + tl::error << ex.msg (); + } + + tl::info << QObject::tr ("Package '%1' installed").arg (tl::to_qstring (target.name ())); + // NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure refresh (); diff --git a/src/lay/lay/laySaltController.cc b/src/lay/lay/laySaltController.cc index bfc5af1b2..17967733e 100644 --- a/src/lay/lay/laySaltController.cc +++ b/src/lay/lay/laySaltController.cc @@ -58,13 +58,13 @@ SaltController::initialized (lay::Dispatcher * /*root*/) connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); } - connect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ())); + connect (&m_salt, SIGNAL (collections_changed ()), this, SLOT (emit_salt_changed ())); } void SaltController::uninitialize (lay::Dispatcher * /*root*/) { - disconnect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ())); + disconnect (&m_salt, SIGNAL (collections_changed ()), this, SLOT (emit_salt_changed ())); if (m_file_watcher) { disconnect (m_file_watcher, SIGNAL (fileChanged (const QString &)), this, SLOT (file_watcher_triggered ())); @@ -168,7 +168,7 @@ void SaltController::sync_files () { tl::log << tl::to_string (tr ("Detected file system change in packages - updating")); - emit salt_changed (); + emit_salt_changed (); } bool @@ -242,6 +242,13 @@ SaltController::file_watcher_triggered () dm_sync_files (); } +void +SaltController::emit_salt_changed () +{ + salt_changed_event (); + emit salt_changed (); +} + void SaltController::set_salt_mine_url (const std::string &url) { diff --git a/src/lay/lay/laySaltController.h b/src/lay/lay/laySaltController.h index f660039a6..e2a9fa0fb 100644 --- a/src/lay/lay/laySaltController.h +++ b/src/lay/lay/laySaltController.h @@ -29,6 +29,7 @@ #include "laySalt.h" #include "tlFileSystemWatcher.h" #include "tlDeferredExecution.h" +#include "tlEvents.h" #include #include @@ -171,6 +172,11 @@ public: return m_salt; } + /** + * @brief Event-style version of "salt_changed" + */ + tl::Event salt_changed_event; + /** * @brief Gets the singleton instance for this object */ @@ -182,6 +188,11 @@ private slots: */ void file_watcher_triggered (); + /** + * @brief Emits a salt_changed event + signal + */ + void emit_salt_changed (); + signals: /** * @brief This signal is emitted if the salt changed diff --git a/src/lay/lay/layViewWidgetStack.cc b/src/lay/lay/layViewWidgetStack.cc index e9901c7eb..b5bcf9743 100644 --- a/src/lay/lay/layViewWidgetStack.cc +++ b/src/lay/lay/layViewWidgetStack.cc @@ -35,7 +35,13 @@ ViewWidgetStack::ViewWidgetStack (QWidget *parent, const char *name) mp_bglabel = new QLabel (this); mp_bglabel->setAutoFillBackground (true); - mp_bglabel->setText (QObject::tr ("

    Use File/Open to open a layout

    ")); + std::string logo = "logo.png"; +#if QT_VERSION >= 0x50000 + if (devicePixelRatio () >= 2.0) { + logo = "logo@2x.png"; + } +#endif + mp_bglabel->setText (QObject::tr ("

    Use File/Open to open a layout

    ").arg (tl::to_qstring (logo))); mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); mp_bglabel->show (); } diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index dc10d5ba7..db6c1c5d5 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -1603,6 +1603,17 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutVi "Returns an array of \\LayerPropertiesIterator objects pointing to the currently selected layers. " "If no layer view is selected currently, an empty array is returned.\n" ) + + gsi::method ("icon_for_layer", &lay::LayoutViewBase::icon_for_layer, gsi::arg ("iter"), gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("dpr"), gsi::arg ("di_off", 0), gsi::arg ("no_state", false), + "@brief Creates an icon pixmap for the given layer.\n" + "\n" + "The icon will have size w times h pixels multiplied by the device pixel ratio (dpr). The dpr is " + "The number of physical pixels per logical pixels on high-DPI displays.\n" + "\n" + "'di_off' will shift the dither pattern by the given number of (physical) pixels. " + "If 'no_state' is true, the icon will not reflect visibility or validity states but rather the display style.\n" + "\n" + "This method has been introduced in version 0.28." + ) + gsi::event ("on_active_cellview_changed", static_cast (&lay::LayoutViewBase::active_cellview_changed_event), "@brief An event indicating that the active cellview has changed\n" "\n" @@ -1631,6 +1642,14 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutVi "Before version 0.25 this event was based on the observer pattern obsolete now. The corresponding methods " "(add_cellview_observer/remove_cellview_observer) have been removed in 0.25.\n" ) + + gsi::event ("on_apply_technology", static_cast (lay::LayoutViewBase::*)> (&lay::LayoutViewBase::apply_technology_event), gsi::arg ("cellview_index"), + "@brief An event indicating that a cellview has requested a new technology\n" + "\n" + "If the technology of a cellview is changed, this event is triggered.\n" + "The integer parameter of this event will indicate the cellview that has changed.\n" + "\n" + "This event has been introduced in version 0.28.\n" + ) + gsi::event ("on_file_open", static_cast (&lay::LayoutViewBase::file_open_event), "@brief An event indicating that a file was opened\n" "\n" diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index 17e062233..405a8fac1 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -311,15 +311,22 @@ render_scanline_cross (const uint32_t *dp, unsigned int ds, const lay::Bitmap *p return; } - if (pixels > 15) { - pixels = 15; + // NOTE: hardcoded bar/width ratio for crosses. + unsigned int lw = std::max (std::min ((unsigned int) 6, pixels / 9), (unsigned int) 1); + + const int max_pixels = 31; + if (pixels > max_pixels) { + pixels = max_pixels; } const uint32_t *dm = dp; unsigned int px1 = (pixels - 1) / 2; unsigned int px2 = (pixels - 1) - px1; - const uint32_t *ps[16]; + unsigned int spx1 = (lw - 1) / 2; + unsigned int spx2 = (lw - 1) - spx1; + + const uint32_t *ps[max_pixels + 1]; for (unsigned int p = 0; p < pixels; ++p) { if (y + p < px1) { ps[p] = pbitmap->scanline (0); @@ -330,56 +337,94 @@ render_scanline_cross (const uint32_t *dp, unsigned int ds, const lay::Bitmap *p } } - uint32_t d, dd = 0, dn; - dn = *(ps[px1]++); + uint32_t *dpp = data; + for (unsigned int i = (w + lay::wordlen - 1) / lay::wordlen; i > 0; --i) { + *dpp++ = 0; + } - unsigned int x = w; - while (true) { + for (unsigned int o = 0; o < pixels; ++o) { - d = dn; + dpp = data; - dn = 0; - if (x > lay::wordlen) { - dn = *(ps[px1]++); - } - - uint32_t d0 = d; - if (d0 != 0) { - for (unsigned int p = 1; p <= px1; ++p) { - d |= (d0 >> p); - } - for (unsigned int p = 1; p <= px2; ++p) { - d |= (d0 << p); - } - } - if (dn != 0) { - for (unsigned int p = 1; p <= px1; ++p) { - d |= (dn << (32 - p)); - } - } - if (dd != 0) { - for (unsigned int p = 1; p <= px2; ++p) { - d |= (dd >> (32 - p)); - } - } - for (unsigned int p = 0; p < px1; ++p) { - d |= *(ps[p]++); - } - for (unsigned int p = px1 + 1; p < pixels; ++p) { - d |= *(ps[p]++); - } - - dd = d0; - - *data++ = d & *dm++; - if (dm == dp + ds) { - dm = dp; - } - - if (x > lay::wordlen) { - x -= lay::wordlen; + unsigned int bpx1 = 0, bpx2 = 0; + if (o >= px1 - spx1 && o <= px1 + spx2) { + bpx1 = px1; + bpx2 = px2; } else { - break; + bpx1 = spx1; + bpx2 = spx2; + } + + if (bpx1 > 0 || bpx2 > 0) { + + uint32_t d, dd = 0, dn; + dn = *(ps[o]++); + + unsigned int x = w; + while (true) { + + d = dn; + + dn = 0; + if (x > lay::wordlen) { + dn = *(ps[o]++); + } + + uint32_t d0 = d; + if (d0 != 0) { + for (unsigned int p = 1; p <= bpx1; ++p) { + d |= (d0 >> p); + } + for (unsigned int p = 1; p <= bpx2; ++p) { + d |= (d0 << p); + } + } + if (dn != 0) { + for (unsigned int p = 1; p <= bpx1; ++p) { + d |= (dn << (32 - p)); + } + } + if (dd != 0) { + for (unsigned int p = 1; p <= bpx2; ++p) { + d |= (dd >> (32 - p)); + } + } + + dd = d0; + + *dpp++ |= d & *dm++; + if (dm == dp + ds) { + dm = dp; + } + + if (x > lay::wordlen) { + x -= lay::wordlen; + } else { + break; + } + + } + + } else { + + unsigned int x = w; + while (true) { + + uint32_t d = *(ps[o]++); + + *dpp++ |= d & *dm++; + if (dm == dp + ds) { + dm = dp; + } + + if (x > lay::wordlen) { + x -= lay::wordlen; + } else { + break; + } + + } + } } diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index 64b117efb..faeb9101a 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -368,9 +368,9 @@ public: * by the caller. The return value is 0 if the Editable object does * not support a properties page. */ - virtual lay::PropertiesPage *properties_page (db::Manager * /*manager*/, QWidget * /*parent*/) + virtual std::vector properties_pages (db::Manager * /*manager*/, QWidget * /*parent*/) { - return 0; + return std::vector (); } #endif diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index da500822b..1eca0e107 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -160,7 +160,7 @@ protected: ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, solid_style, 0, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *arrow_plane = canvas.plane (ops); - ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 1, 0, lay::ViewOp::Rect, lw, 0); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 0, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *edge_plane = canvas.plane (ops); lay::Renderer &r = canvas.renderer (); diff --git a/src/laybasic/laybasic/layLayoutCanvas.cc b/src/laybasic/laybasic/layLayoutCanvas.cc index 0a65906bf..06eb5664f 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.cc +++ b/src/laybasic/laybasic/layLayoutCanvas.cc @@ -131,137 +131,6 @@ std::string ImageCacheEntry::to_string () const // ---------------------------------------------------------------------------- -static void -blowup (const tl::PixelBuffer &src, tl::PixelBuffer &dest, unsigned int os) -{ - unsigned int ymax = src.height (); - unsigned int xmax = src.width (); - - for (unsigned int y = 0; y < ymax; ++y) { - for (unsigned int i = 0; i < os; ++i) { - const uint32_t *psrc = (const uint32_t *) src.scan_line (y); - uint32_t *pdest = (uint32_t *) dest.scan_line (y * os + i); - for (unsigned int x = 0; x < xmax; ++x) { - for (unsigned int j = 0; j < os; ++j) { - *pdest++ = *psrc; - } - ++psrc; - } - } - } -} - -static void -subsample (const tl::PixelBuffer &src, tl::PixelBuffer &dest, unsigned int os, double g) -{ - // TODO: this is probably not compatible with the endianess of SPARC .. - - // LUT's for combining the RGB channels - - // forward transformation table - unsigned short lut1[256]; - for (unsigned int i = 0; i < 256; ++i) { - double f = (65536 / (os * os)) - 1; - lut1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + pow (i / 255.0, g) * f))); - } - - // backward transformation table - unsigned char lut2[65536]; - for (unsigned int i = 0; i < 65536; ++i) { - double f = os * os * ((65536 / (os * os)) - 1); - lut2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + pow (i / f, 1.0 / g) * 255.0))); - } - - // LUT's for alpha channel - - // forward transformation table - unsigned short luta1[256]; - for (unsigned int i = 0; i < 256; ++i) { - double f = (65536 / (os * os)) - 1; - luta1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + (i / 255.0) * f))); - } - - // backward transformation table - unsigned char luta2[65536]; - for (unsigned int i = 0; i < 65536; ++i) { - double f = os * os * ((65536 / (os * os)) - 1); - luta2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + (i / f) * 255.0))); - } - - unsigned int ymax = dest.height (); - unsigned int xmax = dest.width (); - - unsigned short *buffer = new unsigned short[xmax * 4]; - - for (unsigned int y = 0; y < ymax; ++y) { - - { - - const unsigned char *psrc = (const unsigned char *) src.scan_line (y * os); - unsigned short *pdest = buffer; - - for (unsigned int x = 0; x < xmax; ++x) { - - pdest[0] = lut1[psrc[0]]; - pdest[1] = lut1[psrc[1]]; - pdest[2] = lut1[psrc[2]]; - pdest[3] = luta1[psrc[3]]; - psrc += 4; - - for (unsigned int j = os; j > 1; j--) { - pdest[0] += lut1[psrc[0]]; - pdest[1] += lut1[psrc[1]]; - pdest[2] += lut1[psrc[2]]; - pdest[3] += luta1[psrc[3]]; - psrc += 4; - } - - pdest += 4; - - } - - } - - for (unsigned int i = 1; i < os; ++i) { - - const unsigned char *psrc = (const unsigned char *) src.scan_line (y * os + i); - unsigned short *pdest = buffer; - - for (unsigned int x = 0; x < xmax; ++x) { - - for (unsigned int j = os; j > 0; j--) { - pdest[0] += lut1[psrc[0]]; - pdest[1] += lut1[psrc[1]]; - pdest[2] += lut1[psrc[2]]; - pdest[3] += luta1[psrc[3]]; - psrc += 4; - } - - pdest += 4; - - } - - } - - { - - unsigned char *pdest = (unsigned char *) dest.scan_line (y); - const unsigned short *psrc = buffer; - - for (unsigned int x = 0; x < xmax; ++x) { - *pdest++ = lut2[*psrc++]; - *pdest++ = lut2[*psrc++]; - *pdest++ = lut2[*psrc++]; - *pdest++ = luta2[*psrc++]; - } - - } - - } - - delete[] buffer; -} - void invert (unsigned char *data, unsigned int width, unsigned int height) { @@ -281,6 +150,7 @@ LayoutCanvas::LayoutCanvas (lay::LayoutViewBase *view) mp_image_fg (0), m_background (0), m_foreground (0), m_active (0), m_oversampling (1), + m_hrm (false), m_need_redraw (false), m_redraw_clearing (false), m_redraw_force_update (true), @@ -328,19 +198,15 @@ LayoutCanvas::~LayoutCanvas () clear_fg_bitmaps (); } -#if defined(HAVE_QT) && QT_VERSION >= 0x050000 double -LayoutCanvas::dpr () +LayoutCanvas::resolution () const { - return widget () ? widget ()->devicePixelRatio () : 1.0; + if (m_hrm) { + return 1.0 / m_oversampling; + } else { + return 1.0 / (m_oversampling * dpr ()); + } } -#else -double -LayoutCanvas::dpr () -{ - return 1.0; -} -#endif #if defined(HAVE_QT) void @@ -407,6 +273,26 @@ LayoutCanvas::set_oversampling (unsigned int os) } } +void +LayoutCanvas::set_highres_mode (bool hrm) +{ + if (hrm != m_hrm) { + m_image_cache.clear (); + m_hrm = hrm; + do_redraw_all (); + } +} + +double +LayoutCanvas::dpr () const +{ +#if defined(HAVE_QT) && QT_VERSION >= 0x50000 + return widget () ? widget ()->devicePixelRatio () : 1.0; +#else + return 1.0; +#endif +} + void LayoutCanvas::set_colors (tl::Color background, tl::Color foreground, tl::Color active) { @@ -477,7 +363,7 @@ LayoutCanvas::prepare_drawing () { if (m_need_redraw) { - BitmapViewObjectCanvas::set_size (m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling * dpr ())); + BitmapViewObjectCanvas::set_size (m_viewport_l.width (), m_viewport_l.height (), resolution ()); if (! mp_image || (unsigned int) mp_image->width () != m_viewport_l.width () || @@ -513,7 +399,7 @@ LayoutCanvas::prepare_drawing () ++c; } - mp_redraw_thread->commit (m_layers, m_viewport_l, 1.0 / double (m_oversampling * dpr ())); + mp_redraw_thread->commit (m_layers, m_viewport_l, resolution ()); if (tl::verbosity () >= 20) { tl::info << "Restored image from cache"; @@ -563,7 +449,7 @@ LayoutCanvas::prepare_drawing () } if (m_redraw_clearing) { - mp_redraw_thread->start (mp_view->synchronous () ? 0 : mp_view->drawing_workers (), m_layers, m_viewport_l, 1.0 / double (m_oversampling * dpr ()), m_redraw_force_update); + mp_redraw_thread->start (mp_view->synchronous () ? 0 : mp_view->drawing_workers (), m_layers, m_viewport_l, resolution (), m_redraw_force_update); } else { mp_redraw_thread->restart (m_need_redraw_layer); } @@ -634,7 +520,7 @@ LayoutCanvas::paint_event () } // render the main bitmaps - to_image (scaled_view_ops (m_oversampling * dpr ()), dither_pattern (), line_styles (), m_oversampling * dpr (), background_color (), foreground_color (), active_color (), this, *mp_image, m_viewport_l.width (), m_viewport_l.height ()); + to_image (scaled_view_ops (1.0 / resolution ()), dither_pattern (), line_styles (), 1.0 / resolution (), background_color (), foreground_color (), active_color (), this, *mp_image, m_viewport_l.width (), m_viewport_l.height ()); if (mp_image_fg) { delete mp_image_fg; @@ -664,7 +550,7 @@ LayoutCanvas::paint_event () if (fg_bitmaps () > 0) { tl::PixelBuffer full_image (*mp_image); - bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), m_oversampling * dpr (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex); + bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), 1.0 / resolution (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex); // render the foreground parts .. if (m_oversampling == 1) { @@ -672,7 +558,7 @@ LayoutCanvas::paint_event () } else { tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ()); subsampled_image.set_transparent (mp_image->transparent ()); - subsample (full_image, subsampled_image, m_oversampling, m_gamma); + full_image.subsample (subsampled_image, m_oversampling, m_gamma); *mp_image_fg = subsampled_image; } @@ -684,7 +570,7 @@ LayoutCanvas::paint_event () tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ()); subsampled_image.set_transparent (mp_image->transparent ()); - subsample (*mp_image, subsampled_image, m_oversampling, m_gamma); + mp_image->subsample (subsampled_image, m_oversampling, m_gamma); *mp_image_fg = subsampled_image; } @@ -711,7 +597,7 @@ LayoutCanvas::paint_event () full_image.set_transparent (true); full_image.fill (0); - bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), m_oversampling * dpr (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex); + bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), 1.0 / resolution (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex); // render the foreground parts .. if (m_oversampling == 1) { @@ -723,7 +609,7 @@ LayoutCanvas::paint_event () } else { tl::PixelBuffer subsampled_image (m_viewport.width (), m_viewport.height ()); subsampled_image.set_transparent (true); - subsample (full_image, subsampled_image, m_oversampling, m_gamma); + full_image.subsample (subsampled_image, m_oversampling, m_gamma); QImage img = subsampled_image.to_image (); #if QT_VERSION >= 0x050000 img.setDevicePixelRatio (dpr ()); @@ -799,9 +685,9 @@ public: { if (mp_image_l) { unsigned int os = mp_image_l->width () / width; - blowup (*mp_image, *mp_image_l, os); + mp_image->blowup (*mp_image_l, os); bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, 1.0 / resolution (), mp_image_l, mp_image_l->width (), mp_image_l->height (), false, 0); - subsample (*mp_image_l, *mp_image, os, m_gamma); + mp_image_l->subsample (*mp_image, os, m_gamma); } else { bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, 1.0 / resolution (), mp_image, width, height, false, 0); } @@ -812,7 +698,7 @@ public: { if (mp_image_l && mp_image->width () > 0) { unsigned int os = mp_image_l->width () / mp_image->width (); - subsample (*mp_image_l, *mp_image, os, m_gamma); + mp_image_l->subsample (*mp_image, os, m_gamma); } } @@ -986,13 +872,13 @@ LayoutCanvas::screenshot () tl::PixelBuffer img (m_viewport.width (), m_viewport.height ()); img.fill (m_background); - DetachedViewObjectCanvas vo_canvas (background_color (), foreground_color (), active_color (), m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling * dpr ()), &img); + DetachedViewObjectCanvas vo_canvas (background_color (), foreground_color (), active_color (), m_viewport_l.width (), m_viewport_l.height (), resolution (), &img); // and paint the background objects. It uses "img" to paint on. do_render_bg (m_viewport_l, vo_canvas); // paint the layout bitmaps - to_image (scaled_view_ops (m_oversampling * dpr ()), dither_pattern (), line_styles (), m_oversampling * dpr (), background_color (), foreground_color (), active_color (), this, *vo_canvas.bg_image (), m_viewport_l.width (), m_viewport_l.height ()); + to_image (scaled_view_ops (1.0 / resolution ()), dither_pattern (), line_styles (), 1.0 / resolution (), background_color (), foreground_color (), active_color (), this, *vo_canvas.bg_image (), m_viewport_l.width (), m_viewport_l.height ()); // subsample current image to provide the background for the foreground objects vo_canvas.make_background (); diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index ae4f58da2..001cf0f33 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -256,6 +256,32 @@ public: */ void set_oversampling (unsigned int os); + /** + * @brief Gets the oversampling factor + */ + unsigned int oversampling () const + { + return m_oversampling; + } + + /** + * @brief Set high resolution mode (utilize full DPI on high-DPI displays) + */ + void set_highres_mode (bool hrm); + + /** + * @brief Gets the high resolution mode flag + */ + bool highres_mode () const + { + return m_hrm; + } + + /** + * @brief Gets the default device pixel ratio for this canvas + */ + double dpr () const; + /** * @brief Sets the depth of the image cache */ @@ -380,6 +406,7 @@ private: lay::LineStyles m_line_styles; std::map > m_scaled_view_ops; unsigned int m_oversampling; + bool m_hrm; double m_gamma; bool m_need_redraw; @@ -417,7 +444,7 @@ private: void do_redraw_all (bool force_redraw = true); void prepare_drawing (); - double dpr (); + virtual double resolution () const; const std::vector &scaled_view_ops (unsigned int lw); }; diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 57c00aa38..b2e3a805d 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -36,6 +36,7 @@ #include "tlExceptions.h" #include "tlDeferredExecution.h" #include "layLayoutViewBase.h" +#include "layBitmapsToImage.h" #include "layViewOp.h" #include "layViewObject.h" #include "layConverters.h" @@ -780,6 +781,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value) mp_canvas->set_oversampling (os); return true; + } else if (name == cfg_highres_mode) { + + bool hrm = false; + tl::from_string (value, hrm); + mp_canvas->set_highres_mode (hrm); + return true; + } else if (name == cfg_image_cache_size) { int sz = 0; @@ -1520,6 +1528,185 @@ LayoutViewBase::set_selected_layers (const std::vector view_ops; + view_ops.push_back (view_op); + + std::vector pbitmaps; + pbitmaps.push_back (&bitmap); + + lay::bitmaps_to_image (view_ops, pbitmaps, dither_pattern, line_styles, dpr, pimage, width, height, false, 0); +} + +tl::PixelBuffer +LayoutViewBase::icon_for_layer (const LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr, unsigned int di_off, bool no_state) +{ + if (dpr < 0.0) { + dpr = canvas ()->dpr (); + } + + int oversampling = canvas () ? canvas ()->oversampling () : 1; + double gamma = 2.0; + + bool hrm = canvas () ? canvas ()->highres_mode () : false; + double dpr_drawing = oversampling * (hrm ? 1.0 : dpr); + + h = std::max ((unsigned int) 16, h) * oversampling * dpr + 0.5; + w = std::max ((unsigned int) 16, w) * oversampling * dpr + 0.5; + + tl::color_t def_color = 0x808080; + tl::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; + tl::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; + + tl::PixelBuffer image (w, h); + image.set_transparent (true); + image.fill (background_color ().rgb ()); + + // upper scanline is a dummy one + tl::color_t *sl0 = (uint32_t *) image.scan_line (0); + for (size_t i = 0; i < w; ++i) { + *sl0++ = 0; + } + + lay::Bitmap fill (w, h, 1.0); + lay::Bitmap frame (w, h, 1.0); + lay::Bitmap text (w, h, 1.0); + lay::Bitmap vertex (w, h, 1.0); + + unsigned int wp = w - 1; + + if (! no_state && ! iter->visible (true)) { + + wp = w / 4; + + // Show the arrow if it is invisible also locally. + if (! iter->visible (false)) { + + unsigned int aw = h / 4; + unsigned int ap = w / 2 - 1; + for (unsigned int i = 0; i <= aw; ++i) { + text.fill (h / 2 - 1 - i, ap, ap + aw - i + 1); + text.fill (h / 2 - 1 + i, ap, ap + aw - i + 1); + } + + } + + } + + if (! no_state && no_stipples ()) { + // Show a partial stipple pattern only for "no stipple" mode + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - w / 4, w); + } + } else { + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - wp, w); + } + } + + int lw = iter->width (true); + if (lw < 0) { + // default line width is 0 for parents and 1 for leafs + lw = iter->has_children () ? 0 : 1; + } + lw = lw * dpr_drawing + 0.5; + + int p0 = lw / 2; + p0 = std::max (0, std::min (int (w / 4 - 1), p0)); + + int p1 = (lw - 1) / 2; + p1 = std::max (0, std::min (int (w / 4 - 1), p1)); + + int p0x = p0, p1x = p1; + unsigned int ddx = 0; + unsigned int ddy = h - 2 - p1 - p0; + if (iter->xfill (true)) { + ddx = wp - p0 - p1 - 1; + } + unsigned int d = ddx / 2; + + frame.fill (p0, w - 1 - (wp - p1), w); + frame.fill (h - 2 - p1, w - 1 - (wp - p1), w); + + for (unsigned int i = p0; i < h - 2; ++i) { + + frame.fill (i, w - 1 - p0, w - p0); + frame.fill (i, w - 1 - (wp - p1), w - (wp - p1)); + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + + while (d < ddx) { + d += ddy; + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + ++p0x; + ++p1x; + } + + if (d >= ddx) { + d -= ddx; + } + + } + + if (! no_state && ! iter->valid (true)) { + + unsigned int bp = w - 1 - ((w * 7) / 8 - 1); + unsigned int be = bp + h / 2; + unsigned int bw = h / 4 - 1; + unsigned int by = h / 2 - 1; + + for (unsigned int i = 0; i < bw + 2; ++i) { + fill.clear (by - i, bp - 1, be); + fill.clear (by + i, bp - 1, be); + } + + for (unsigned int i = 0; i < bw; ++i) { + text.fill (by - i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i - 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i, bp + bw + i, bp + bw + i + 2); + text.fill (by - i - 1, bp + bw + i, bp + bw + i + 2); + text.fill (by + i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i + 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i, bp + bw + i, bp + bw + i + 2); + text.fill (by + i + 1, bp + bw + i, bp + bw + i + 2); + } + + } + + vertex.fill (h / 2 - 1, w - 1 - wp / 2, w - wp / 2); + + lay::ViewOp::Mode mode = lay::ViewOp::Copy; + + // create fill + single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, dither_pattern (), line_styles (), dpr_drawing, w, h); + // create frame + if (lw == 0) { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, dither_pattern (), line_styles (), dpr_drawing, w, h); + } else { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, dither_pattern (), line_styles (), dpr_drawing, w, h); + } + // create text + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, dither_pattern (), line_styles (), dpr_drawing, w, h); + // create vertex + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? int (9 * dpr_drawing + 0.5) : 0), vertex, &image, dither_pattern (), line_styles (), dpr_drawing, w, h); + + if (oversampling > 1) { + tl::PixelBuffer subsampled (image.width () / oversampling, image.height () / oversampling); + image.subsample (subsampled, oversampling, gamma); + return subsampled; + } else { + return image; + } +} + void LayoutViewBase::merge_dither_pattern (lay::LayerPropertiesList &props) { @@ -2314,6 +2501,8 @@ LayoutViewBase::signal_apply_technology (lay::LayoutHandle *layout_handle) } + apply_technology_event (int (i)); + } } diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 5837c042d..a45056f80 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -581,6 +581,18 @@ public: */ virtual std::vector selected_layers () const; + /** + * @brief Gets a pixmap representing the given layer + * + * @param iter indicates the layer + * @param w The width in logical pixels of the generated pixmap (will be multiplied by dpr) + * @param h The height in logical pixels of the generated pixmap (will be multiplied by dpr) + * @param dpr The device pixel ratio (number of image pixes per logical pixel) - negative values mean auto-detect + * @param di_off The dither pattern offset (used for animation) + * @param no_state If true, the state will not be indicated + */ + tl::PixelBuffer icon_for_layer (const lay::LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr = -1.0, unsigned int di_off = 0, bool no_state = false); + /** * @brief Sets the layers that are selected in the layer browser */ @@ -677,6 +689,14 @@ public: */ tl::event cellview_changed_event; + /** + * @brief A event signalling that one cellview has requested a new technology + * + * This event is triggered if a cellview has requested a new technology. + * The argument is the index of the cellview that received the new technology. + */ + tl::event apply_technology_event; + /** * @brief An event signalling that a file has been loaded. * diff --git a/src/laybasic/laybasic/layLayoutViewConfig.cc b/src/laybasic/laybasic/layLayoutViewConfig.cc index 3d9f5878b..9ed60be0d 100644 --- a/src/laybasic/laybasic/layLayoutViewConfig.cc +++ b/src/laybasic/laybasic/layLayoutViewConfig.cc @@ -107,6 +107,7 @@ public: options.push_back (std::pair (cfg_drop_small_cells_value, "10")); options.push_back (std::pair (cfg_array_border_instances, "false")); options.push_back (std::pair (cfg_bitmap_oversampling, "1")); + options.push_back (std::pair (cfg_highres_mode, "false")); options.push_back (std::pair (cfg_image_cache_size, "1")); options.push_back (std::pair (cfg_default_font_size, "0")); options.push_back (std::pair (cfg_color_palette, lay::ColorPalette ().to_string ())); diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index 07c6ffe98..081a171b3 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -29,6 +29,7 @@ #include "layEditable.h" #include +#include namespace db { @@ -69,80 +70,54 @@ public: virtual ~PropertiesPage (); /** - * @brief Move the current pointer to the end of the selected objects list - * - * Must be implemented by the Editable function and must set the - * selected object pointer to past-the-end of the list. at_end () must - * return true after this. + * @brief Gets the number of entries represented by this page */ - virtual void back () = 0; + virtual size_t count () const = 0; /** - * @brief A helper function for the dialog that additionally reports - * if there are any elements in the selection + * @brief Selects the entries with the given indexes + * + * If multiple indexes are selected, the properties page shows + * all items with different values as "leave as is". Items with + * same value are shown with the given value. */ - bool back_checked () + virtual void select_entries (const std::vector &entries) = 0; + + /** + * @brief Convenience function to select a specific entry + */ + void select_entry (size_t entry) { - back (); - return ! at_begin (); + std::vector entries; + entries.push_back (entry); + select_entries (entries); } /** - * @brief Move the current pointer to the beginning of the selected objects list - * - * Must be implemented by the Editable function and must set the - * selected object pointer to the beginning of the list. at_begin () must - * return true after this. - * This method must return true, if there are any elements in the selection - * - i.e. operator++ () will be successful afterwards. + * @brief Gets a description text for the nth entry */ - virtual void front () = 0; + virtual std::string description (size_t entry) const = 0; /** - * @brief A helper function for the dialog that additionally reports - * if there are any elements in the selection + * @brief Gets the icon for the nth entry */ - bool front_checked () + virtual QIcon icon (size_t /*entry*/, int /*w*/, int /*h*/) const { - front (); - return ! at_end (); + return QIcon (); } /** - * @brief Tell if the current object references the first one - * - * Must be implemented by the Editable function and must return - * true if the current object is the first one - i.e. if an - * operator-- would render the status invalid. - * If no object is referenced, this method and at_end () must return true. + * @brief Gets a description text for the whole group */ - virtual bool at_begin () const = 0; + virtual std::string description () const = 0; /** - * @brief Tell if the current object references past the last one - * - * If the current object pointer is past the end of the selected - * object space, + * @brief Gets the icon associated with the whole group */ - virtual bool at_end () const = 0; - - /** - * @brief Step one element back - * - * This method is supposed to move the current pointer one position - * back. If at_begin () was true before, the result may be unpredictable. - * The dialog will call update () to update the display accordingly. - */ - virtual void operator-- () = 0; - - /** - * @brief Advance one element - * - * This method is supposed to move the current pointer one position - * forward. If at_end () was true before, the result may be unpredictable. - * The dialog will call update () to update the display accordingly. - */ - virtual void operator++ () = 0; + virtual QIcon icon (int /*w*/, int /*h*/) const + { + return QIcon (); + } /** * @brief Update the display @@ -176,7 +151,7 @@ public: /** * @brief Apply any changes to the current object * - * Apply any changes to the current object. If nothing was + * Apply any changes to the current objects. If nothing was * changed, the object may be left untouched. * The dialog will start a transaction on the manager object. */ diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 34e7207c6..f14c36cdd 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -966,7 +966,7 @@ public: /** * @brief Gets the QWidget representing this canvas visually in Qt */ - QWidget *widget () + QWidget *widget () const { return mp_widget; } diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 0b84bf38f..ff816a0d8 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -120,6 +120,7 @@ static const std::string cfg_reader_options_show_always ("reader-options-show-al static const std::string cfg_tip_window_hidden ("tip-window-hidden"); static const std::string cfg_bitmap_oversampling ("bitmap-oversampling"); +static const std::string cfg_highres_mode ("highres-mode"); static const std::string cfg_image_cache_size ("image-cache-size"); static const std::string cfg_default_font_size ("default-font-size"); diff --git a/src/layui/layui/LayoutViewConfigPage7.ui b/src/layui/layui/LayoutViewConfigPage7.ui index ae9b834a9..c5f922f10 100644 --- a/src/layui/layui/LayoutViewConfigPage7.ui +++ b/src/layui/layui/LayoutViewConfigPage7.ui @@ -7,7 +7,7 @@ 0 0 791 - 385 + 403 @@ -32,7 +32,7 @@ - Oversampling + Display Quality @@ -53,7 +53,14 @@ - Use oversampling: + Oversampling mode + + + + + + + High resolution mode utilizes the full pixel density on high-DPI displays. Features may look small but rich in details. @@ -99,6 +106,13 @@ + + + + High resolution mode enabled + + + diff --git a/src/layui/layui/MarkerBrowserPage.ui b/src/layui/layui/MarkerBrowserPage.ui index 1eafe265d..07bbf0344 100644 --- a/src/layui/layui/MarkerBrowserPage.ui +++ b/src/layui/layui/MarkerBrowserPage.ui @@ -63,7 +63,7 @@ - :/run.png:/run.png + :/run_16px.png:/run_16px.png F5 diff --git a/src/layui/layui/NetlistBrowserPage.ui b/src/layui/layui/NetlistBrowserPage.ui index 0f887e8a8..88687f10e 100644 --- a/src/layui/layui/NetlistBrowserPage.ui +++ b/src/layui/layui/NetlistBrowserPage.ui @@ -135,7 +135,7 @@ - :/run.png:/run.png + :/run_16px.png:/run_16px.png F5 diff --git a/src/layui/layui/PropertiesDialog.ui b/src/layui/layui/PropertiesDialog.ui index 112840254..10f1b72b1 100644 --- a/src/layui/layui/PropertiesDialog.ui +++ b/src/layui/layui/PropertiesDialog.ui @@ -6,43 +6,59 @@ 0 0 - 601 - 391 + 828 + 567 Object Properties - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - + - - - - 1 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised + + + Qt::Horizontal + + + + 0 + 0 + + + + QAbstractItemView::ExtendedSelection + + + + 16 + 16 + + + + true + + + false + + + false + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + diff --git a/src/layui/layui/layLayerControlPanel.cc b/src/layui/layui/layLayerControlPanel.cc index 5b1ff584c..1ff0370c8 100644 --- a/src/layui/layui/layLayerControlPanel.cc +++ b/src/layui/layui/layLayerControlPanel.cc @@ -208,6 +208,8 @@ LayerControlPanel::LayerControlPanel (lay::LayoutViewBase *view, db::Manager *ma m_hidden_flags_need_update (true), m_in_update (false), m_phase (0), + m_oversampling (1), + m_hrm (false), m_do_update_content_dm (this, &LayerControlPanel::do_update_content), m_no_stipples (false) { @@ -1712,6 +1714,24 @@ LayerControlPanel::set_phase (int phase) } } +void +LayerControlPanel::set_highres_mode (bool hrm) +{ + if (m_hrm != hrm) { + m_hrm = hrm; + m_do_update_content_dm (); + } +} + +void +LayerControlPanel::set_oversampling (int os) +{ + if (m_oversampling != os) { + m_oversampling = os; + m_do_update_content_dm (); + } +} + static void set_hidden_flags_rec (LayerTreeModel *model, QTreeView *tree_view, const QModelIndex &parent) { diff --git a/src/layui/layui/layLayerControlPanel.h b/src/layui/layui/layLayerControlPanel.h index 93aad12b6..87920ff57 100644 --- a/src/layui/layui/layLayerControlPanel.h +++ b/src/layui/layui/layLayerControlPanel.h @@ -208,6 +208,16 @@ public: */ void set_phase (int phase); + /** + * @brief Sets highres mode + */ + void set_highres_mode (bool hrm); + + /** + * @brief Sets oversampling mode + */ + void set_oversampling (int os); + /** * @brief Tell, if the model has been updated already (true) or if it is still under construction (false) */ @@ -347,6 +357,8 @@ private: bool m_in_update; std::vector m_new_sel; int m_phase; + int m_oversampling; + bool m_hrm; tl::DeferredMethod m_do_update_content_dm; std::set m_expanded; bool m_no_stipples; diff --git a/src/layui/layui/layLayerTreeModel.cc b/src/layui/layui/layLayerTreeModel.cc index fe3f1a97f..61aadcfec 100644 --- a/src/layui/layui/layLayerTreeModel.cc +++ b/src/layui/layui/layLayerTreeModel.cc @@ -24,7 +24,6 @@ #include "layLayerTreeModel.h" #include "layLayoutViewBase.h" -#include "layBitmapsToImage.h" #include "dbLayoutUtils.h" #include "tlLog.h" #include "tlTimer.h" @@ -182,7 +181,8 @@ EmptyWithinViewCache::determine_empty_layers (const db::Layout *layout, unsigned LayerTreeModel::LayerTreeModel (QWidget *parent, lay::LayoutViewBase *view) : QAbstractItemModel (parent), mp_parent (parent), mp_view (view), m_filter_mode (false), m_id_start (0), m_id_end (0), - m_phase ((unsigned int) -1), m_test_shapes_in_view (false), m_hide_empty_layers (false) + m_phase ((unsigned int) -1), + m_test_shapes_in_view (false), m_hide_empty_layers (false) { // .. nothing yet .. } @@ -620,170 +620,11 @@ LayerTreeModel::empty_within_view_predicate (const QModelIndex &index) const } } -/** - * @brief A helper function to create an image from a single bitmap - */ -static void -single_bitmap_to_image (const lay::ViewOp &view_op, lay::Bitmap &bitmap, - tl::PixelBuffer *pimage, const lay::DitherPattern &dither_pattern, const lay::LineStyles &line_styles, - double dpr, unsigned int width, unsigned int height) -{ - std::vector view_ops; - view_ops.push_back (view_op); - - std::vector pbitmaps; - pbitmaps.push_back (&bitmap); - - lay::bitmaps_to_image (view_ops, pbitmaps, dither_pattern, line_styles, dpr, pimage, width, height, false, 0); -} - -LAYUI_PUBLIC QIcon LayerTreeModel::icon_for_layer (const lay::LayerPropertiesConstIterator &iter, lay::LayoutViewBase *view, unsigned int w, unsigned int h, double dpr, unsigned int di_off, bool no_state) { - h = std::max ((unsigned int) 16, h) * dpr + 0.5; - w = std::max ((unsigned int) 16, w) * dpr + 0.5; - - tl::color_t def_color = 0x808080; - tl::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; - tl::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; - - tl::PixelBuffer image (w, h); - image.set_transparent (true); - image.fill (view->background_color ().rgb ()); - - // upper scanline is a dummy one - uint32_t *sl0 = (uint32_t *) image.scan_line (0); - uint32_t transparent = QColor (Qt::transparent).rgba (); - for (size_t i = 0; i < w; ++i) { - *sl0++ = transparent; - } - - // TODO: adjust the resolution according to the oversampling mode - lay::Bitmap fill (w, h, 1.0); - lay::Bitmap frame (w, h, 1.0); - lay::Bitmap text (w, h, 1.0); - lay::Bitmap vertex (w, h, 1.0); - - unsigned int wp = w - 1; - - if (! no_state && ! iter->visible (true)) { - - wp = w / 4; - - // Show the arrow if it is invisible also locally. - if (! iter->visible (false)) { - - unsigned int aw = h / 4; - unsigned int ap = w / 2 - 1; - for (unsigned int i = 0; i <= aw; ++i) { - text.fill (h / 2 - 1 - i, ap, ap + aw - i + 1); - text.fill (h / 2 - 1 + i, ap, ap + aw - i + 1); - } - - } - - } - - if (! no_state && view->no_stipples ()) { - // Show a partial stipple pattern only for "no stipple" mode - for (unsigned int i = 1; i < h - 2; ++i) { - fill.fill (i, w - 1 - w / 4, w); - } - } else { - for (unsigned int i = 1; i < h - 2; ++i) { - fill.fill (i, w - 1 - wp, w); - } - } - - int lw = iter->width (true); - if (lw < 0) { - // default line width is 0 for parents and 1 for leafs - lw = iter->has_children () ? 0 : 1; - } - lw = lw * dpr + 0.5; - - int p0 = lw / 2; - p0 = std::max (0, std::min (int (w / 4 - 1), p0)); - - int p1 = (lw - 1) / 2; - p1 = std::max (0, std::min (int (w / 4 - 1), p1)); - - int p0x = p0, p1x = p1; - unsigned int ddx = 0; - unsigned int ddy = h - 2 - p1 - p0; - if (iter->xfill (true)) { - ddx = wp - p0 - p1 - 1; - } - unsigned int d = ddx / 2; - - frame.fill (p0, w - 1 - (wp - p1), w); - frame.fill (h - 2 - p1, w - 1 - (wp - p1), w); - - for (unsigned int i = p0; i < h - 2; ++i) { - - frame.fill (i, w - 1 - p0, w - p0); - frame.fill (i, w - 1 - (wp - p1), w - (wp - p1)); - frame.fill (i, w - 1 - p0x, w - p0x); - frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); - - while (d < ddx) { - d += ddy; - frame.fill (i, w - 1 - p0x, w - p0x); - frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); - ++p0x; - ++p1x; - } - - if (d >= ddx) { - d -= ddx; - } - - } - - if (! no_state && ! iter->valid (true)) { - - unsigned int bp = w - 1 - ((w * 7) / 8 - 1); - unsigned int be = bp + h / 2; - unsigned int bw = h / 4 - 1; - unsigned int by = h / 2 - 1; - - for (unsigned int i = 0; i < bw + 2; ++i) { - fill.clear (by - i, bp - 1, be); - fill.clear (by + i, bp - 1, be); - } - - for (unsigned int i = 0; i < bw; ++i) { - text.fill (by - i, bp + bw - i - 1, bp + bw - i + 1); - text.fill (by - i - 1, bp + bw - i - 1, bp + bw - i + 1); - text.fill (by - i, bp + bw + i, bp + bw + i + 2); - text.fill (by - i - 1, bp + bw + i, bp + bw + i + 2); - text.fill (by + i, bp + bw - i - 1, bp + bw - i + 1); - text.fill (by + i + 1, bp + bw - i - 1, bp + bw - i + 1); - text.fill (by + i, bp + bw + i, bp + bw + i + 2); - text.fill (by + i + 1, bp + bw + i, bp + bw + i + 2); - } - - } - - vertex.fill (h / 2 - 1, w - 1 - wp / 2, w - wp / 2); - - lay::ViewOp::Mode mode = lay::ViewOp::Copy; - - // create fill - single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, view->dither_pattern (), view->line_styles (), dpr, w, h); - // create frame - if (lw == 0) { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, view->dither_pattern (), view->line_styles (), dpr, w, h); - } else { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, view->dither_pattern (), view->line_styles (), dpr, w, h); - } - // create text - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, view->dither_pattern (), view->line_styles (), dpr, w, h); - // create vertex - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, view->dither_pattern (), view->line_styles (), dpr, w, h); - - QPixmap pixmap = QPixmap::fromImage (image.to_image ()); + QImage img = view->icon_for_layer (iter, w, h, dpr, di_off, no_state).to_image_copy (); + QPixmap pixmap = QPixmap::fromImage (std::move (img)); #if QT_VERSION >= 0x050000 pixmap.setDevicePixelRatio (dpr); #endif diff --git a/src/layui/layui/layLayerTreeModel.h b/src/layui/layui/layLayerTreeModel.h index 902166bdd..fbd548814 100644 --- a/src/layui/layui/layLayerTreeModel.h +++ b/src/layui/layui/layLayerTreeModel.h @@ -123,17 +123,17 @@ public: QModelIndex index (lay::LayerPropertiesConstIterator iter, int column) const; /** - * @brief Convert a QModelIndex to an iterator + * @brief Converts a QModelIndex to an iterator */ lay::LayerPropertiesConstIterator iterator (const QModelIndex &index) const; /** - * @brief Get a flag indicating that an entry is hidden + * @brief Gets a flag indicating that an entry is hidden */ bool is_hidden (const QModelIndex &index) const; /** - * @brief Set the animation phase + * @brief Sets the animation phase */ void set_phase (unsigned int ph); diff --git a/src/layui/layui/layLayoutViewConfigPages.cc b/src/layui/layui/layLayoutViewConfigPages.cc index ab8406b8e..609c3df59 100644 --- a/src/layui/layui/layLayoutViewConfigPages.cc +++ b/src/layui/layui/layLayoutViewConfigPages.cc @@ -888,10 +888,10 @@ LayoutViewConfigPage4::update () #endif QPainter painter (&img); - painter.setPen (QPen (palette ().color (QPalette::Active, QPalette::Text), 1.0 / dpr)); - painter.setBrush (QBrush (color)); - QRectF r (0, 0, w - painter.pen ().widthF (), h - painter.pen ().widthF ()); - painter.drawRect (r); + QRectF r (0.0, 0.0, w, h); + painter.fillRect (r, QBrush (palette ().color (QPalette::Active, QPalette::ButtonText))); + r = QRectF (1.0, 1.0, w - 2.0, h - 2.0); + painter.fillRect (r, QBrush (color)); painter.setFont (font ()); painter.setPen (QPen (text_color)); painter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, text); @@ -1476,6 +1476,10 @@ LayoutViewConfigPage7::setup (lay::Dispatcher *root) root->config_get (cfg_bitmap_oversampling, oversampling); mp_ui->oversampling->setCurrentIndex (oversampling - 1); + bool highres_mode = false; + root->config_get (cfg_highres_mode, highres_mode); + mp_ui->highres_mode->setChecked (highres_mode); + int default_font_size = 0; root->config_get (cfg_default_font_size, default_font_size); mp_ui->default_font_size->setCurrentIndex (default_font_size); @@ -1499,6 +1503,7 @@ void LayoutViewConfigPage7::commit (lay::Dispatcher *root) { root->config_set (cfg_bitmap_oversampling, mp_ui->oversampling->currentIndex () + 1); + root->config_set (cfg_highres_mode, mp_ui->highres_mode->isChecked ()); root->config_set (cfg_default_font_size, mp_ui->default_font_size->currentIndex ()); root->config_set (cfg_global_trans, db::DCplxTrans (db::DFTrans (mp_ui->global_trans->currentIndex ())).to_string ()); root->config_set (cfg_initial_hier_depth, mp_ui->def_depth->value ()); diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index 37e00cda9..84b867137 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -32,91 +32,222 @@ #include "ui_PropertiesDialog.h" #include +#include +#include namespace lay { +#if QT_VERSION >= 0x50000 +typedef qint64 tree_id_type; +#else +typedef qint32 tree_id_type; +#endif + +// ---------------------------------------------------------------------------------------------------------- +// PropertiesTreeModel definition and implementation + +class PropertiesTreeModel + : public QAbstractItemModel +{ +public: + PropertiesTreeModel (PropertiesDialog *dialog, int icon_width, int icon_height) + : QAbstractItemModel (dialog), mp_dialog (dialog), m_icon_width (icon_width), m_icon_height (icon_height) + { } + + int columnCount (const QModelIndex &) const + { + return 1; + } + + QVariant data (const QModelIndex &index, int role) const + { + if (role == Qt::DisplayRole) { + if (tree_id_type (index.internalId ()) < tree_id_type (mp_dialog->properties_pages ().size ())) { + return tl::to_qstring (mp_dialog->properties_pages () [index.internalId ()]->description (index.row ())); + } else if (index.row () < int (mp_dialog->properties_pages ().size ())) { + return tl::to_qstring (mp_dialog->properties_pages () [index.row ()]->description ()); + } + } else if (role == Qt::DecorationRole) { + QIcon icon; + if (tree_id_type (index.internalId ()) < tree_id_type (mp_dialog->properties_pages ().size ())) { + icon = mp_dialog->properties_pages () [index.internalId ()]->icon (index.row (), m_icon_width, m_icon_height); + } else if (index.row () < int (mp_dialog->properties_pages ().size ())) { + icon = mp_dialog->properties_pages () [index.row ()]->icon (m_icon_width, m_icon_height); + } + if (! icon.isNull ()) { + return QVariant (icon); + } + } + return QVariant (); + } + + Qt::ItemFlags flags (const QModelIndex &index) const + { + Qt::ItemFlags f = QAbstractItemModel::flags (index); + if (tree_id_type (index.internalId ()) >= tree_id_type (mp_dialog->properties_pages ().size ()) && ! mp_dialog->properties_pages () [index.row ()]->can_apply_to_all ()) { + f &= ~Qt::ItemIsSelectable; + } + return f; + } + + bool hasChildren (const QModelIndex &parent) const + { + return (! parent.isValid () || tree_id_type (parent.internalId ()) >= tree_id_type (mp_dialog->properties_pages ().size ())); + } + + QModelIndex index (int row, int column, const QModelIndex &parent) const + { + if (! parent.isValid ()) { + return createIndex (row, column, tree_id_type (mp_dialog->properties_pages ().size ())); + } else { + return createIndex (row, column, tree_id_type (parent.row ())); + } + } + + QModelIndex parent (const QModelIndex &child) const + { + if (tree_id_type (child.internalId ()) < tree_id_type (mp_dialog->properties_pages ().size ())) { + return createIndex (int (child.internalId ()), child.column (), tree_id_type (mp_dialog->properties_pages ().size ())); + } else { + return QModelIndex (); + } + } + + int rowCount (const QModelIndex &parent) const + { + if (! hasChildren (parent)) { + return 0; + } else if (parent.isValid ()) { + return int (mp_dialog->properties_pages () [parent.row ()]->count ()); + } else { + return int (mp_dialog->properties_pages ().size ()); + } + } + + int page_index (const QModelIndex &index) + { + return int (index.internalId ()); + } + + int object_index (const QModelIndex &index) + { + return int (index.row ()); + } + + QModelIndex index_for (int page_index, int object_index) + { + if (page_index < 0) { + return QModelIndex (); + } else { + return createIndex (object_index, 0, tree_id_type (page_index)); + } + } + + QModelIndex index_for (int page_index) + { + if (page_index < 0) { + return QModelIndex (); + } else { + return createIndex (page_index, 0, tree_id_type (mp_dialog->properties_pages ().size ())); + } + } + +private: + PropertiesDialog *mp_dialog; + int m_icon_width, m_icon_height; +}; + +// ---------------------------------------------------------------------------------------------------------- +// PropertiesDialog + PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, lay::Editables *editables) : QDialog (0 /*parent*/), - mp_manager (manager), mp_editables (editables), m_index (-1), m_auto_applied (false), m_transaction_id (0) + mp_manager (manager), + mp_editables (editables), + m_index (0), m_prev_index (-1), + m_auto_applied (false), + m_transaction_id (0), + m_signals_enabled (true) { mp_ui = new Ui::PropertiesDialog (); setObjectName (QString::fromUtf8 ("properties_dialog")); mp_ui->setupUi (this); + mp_tree_model = 0; mp_editables->enable_edits (false); mp_stack = new QStackedLayout; for (lay::Editables::iterator e = mp_editables->begin (); e != mp_editables->end (); ++e) { - mp_properties_pages.push_back (e->properties_page (mp_manager, mp_ui->content_frame)); - if (mp_properties_pages.back ()) { - mp_stack->addWidget (mp_properties_pages.back ()); - connect (mp_properties_pages.back (), SIGNAL (edited ()), this, SLOT (apply ())); + auto pp = e->properties_pages (mp_manager, mp_ui->content_frame); + for (auto p = pp.begin (); p != pp.end (); ++p) { + if ((*p)->count () == 0) { + delete *p; + } else { + mp_properties_pages.push_back (*p); + } } } + for (size_t i = 0; i < mp_properties_pages.size (); ++i) { + mp_stack->addWidget (mp_properties_pages [i]); + connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (apply ())); + } // Necessary to maintain the page order for UI regression testing of 0.18 vs. 0.19 (because tl::Collection has changed to order) .. std::reverse (mp_properties_pages.begin (), mp_properties_pages.end ()); // Add a label as a dummy - QLabel *dummy = new QLabel (QObject::tr ("No object with properties to display"), mp_ui->content_frame); - dummy->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter); - mp_stack->addWidget (dummy); + mp_none = new QLabel (QObject::tr ("No object with properties to display"), mp_ui->content_frame); + mp_none->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter); + mp_stack->addWidget (mp_none); mp_ui->content_frame->setLayout (mp_stack); - // disable the apply button for first .. - mp_ui->apply_to_all_cbx->setEnabled (false); - mp_ui->relative_cbx->setEnabled (false); - mp_ui->ok_button->setEnabled (false); - - // as a proposal, the start button can be enabled in most cases - mp_ui->prev_button->setEnabled (true); - // count the total number of objects m_objects = mp_editables->selection_size (); m_current_object = 0; + // look for next usable editable + if (m_index >= int (mp_properties_pages.size ())) { + m_index = -1; + } else { + m_object_indexes.push_back (0); + } + update_title (); - // look for next usable editable - while (m_index < int (mp_properties_pages.size ()) && - (m_index < 0 || mp_properties_pages [m_index] == 0 || mp_properties_pages [m_index]->at_end ())) { - ++m_index; - } - - mp_ui->prev_button->setEnabled (false); - // if at end disable the "Next" button and return (this may only happen at the first call) - if (m_index >= int (mp_properties_pages.size ())) { - - mp_ui->next_button->setEnabled (false); - mp_stack->setCurrentWidget (dummy); - mp_ui->apply_to_all_cbx->setEnabled (false); - mp_ui->apply_to_all_cbx->setChecked (false); - mp_ui->relative_cbx->setEnabled (false); - mp_ui->relative_cbx->setChecked (false); - mp_ui->ok_button->setEnabled (false); - - } else { - - mp_ui->next_button->setEnabled (any_next ()); - mp_properties_pages [m_index]->update (); - mp_stack->setCurrentWidget (mp_properties_pages [m_index]); - mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); - mp_ui->apply_to_all_cbx->setChecked (false); - mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ()); - mp_ui->relative_cbx->setChecked (true); - mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); + mp_tree_model = new PropertiesTreeModel (this, mp_ui->tree->iconSize ().width (), mp_ui->tree->iconSize ().height ()); + mp_ui->tree->setModel (mp_tree_model); +#if QT_VERSION >= 0x50000 + mp_ui->tree->header()->setSectionResizeMode (QHeaderView::ResizeToContents); +#else + mp_ui->tree->header()->setResizeMode (QHeaderView::ResizeToContents); +#endif + mp_ui->tree->expandAll (); + if (mp_properties_pages.empty ()) { + mp_ui->tree->hide (); } + m_signals_enabled = false; + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, 0)); + m_signals_enabled = true; + + update_controls (); + + mp_ui->apply_to_all_cbx->setChecked (false); + mp_ui->relative_cbx->setChecked (true); + connect (mp_ui->ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ())); connect (mp_ui->cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ())); connect (mp_ui->prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ())); connect (mp_ui->next_button, SIGNAL (clicked ()), this, SLOT (next_pressed ())); + connect (mp_ui->apply_to_all_cbx, SIGNAL (clicked ()), this, SLOT (apply_to_all_pressed ())); + connect (mp_ui->tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &, const QModelIndex &))); + connect (mp_ui->tree->selectionModel (), SIGNAL (selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT (selection_changed ())); } PropertiesDialog::~PropertiesDialog () @@ -138,11 +269,147 @@ PropertiesDialog::disconnect () mp_properties_pages.clear (); } +void +PropertiesDialog::apply_to_all_pressed () +{ + m_signals_enabled = false; + if (mp_ui->apply_to_all_cbx->isChecked ()) { + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index)); + } else if (! m_object_indexes.empty ()) { + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, int (m_object_indexes.front ()))); + } + m_signals_enabled = true; +} + +void +PropertiesDialog::selection_changed () +{ + current_index_changed (mp_ui->tree->currentIndex (), QModelIndex ()); +} + +void +PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelIndex & /*previous*/) +{ + if (! m_signals_enabled) { + return; + } + + m_object_indexes.clear (); + + if (! index.isValid ()) { + + m_index = -1; + + } else { + + if (mp_tree_model->parent (index).isValid ()) { + + m_index = mp_tree_model->page_index (index); + + if (mp_properties_pages [m_index]->can_apply_to_all ()) { + + m_object_indexes.push_back (size_t (mp_tree_model->object_index (index))); + + auto selection = mp_ui->tree->selectionModel ()->selectedIndexes (); + for (auto i = selection.begin (); i != selection.end (); ++i) { + if (mp_tree_model->parent (*i).isValid () && mp_tree_model->page_index (*i) == m_index) { + int oi = mp_tree_model->object_index (*i); + if (oi != int (m_object_indexes.front ())) { + m_object_indexes.push_back (size_t (oi)); + } + } + } + + } else { + + m_object_indexes.push_back (size_t (mp_tree_model->object_index (index))); + + } + + } else { + + m_index = index.row (); + mp_ui->apply_to_all_cbx->setChecked (mp_properties_pages [m_index]->can_apply_to_all ()); + + if (mp_properties_pages [m_index]->can_apply_to_all ()) { + + for (size_t oi = 0; oi < mp_properties_pages [m_index]->count (); ++oi) { + m_object_indexes.push_back (oi); + } + + } else { + + m_object_indexes.push_back (size_t (mp_tree_model->object_index (index))); + + } + + } + + } + + if (! m_object_indexes.empty ()) { + m_current_object = 0; + for (int i = 0; i < m_index; ++i) { + m_current_object += mp_properties_pages [i]->count (); + } + m_current_object += int (m_object_indexes.front ()); + } else { + m_current_object = -1; + } + + update_title (); + update_controls (); +} + +void +PropertiesDialog::update_controls () +{ + if (m_prev_index >= 0 && m_index != m_prev_index) { + if (m_prev_index >= 0 && m_prev_index < int (mp_properties_pages.size ())) { + mp_properties_pages [m_prev_index]->leave (); + } + } + m_prev_index = m_index; + + mp_ui->apply_to_all_cbx->setChecked (m_object_indexes.size () > 1); + + if (m_index < 0) { + + mp_stack->setCurrentWidget (mp_none); + + mp_ui->prev_button->setEnabled (false); + mp_ui->next_button->setEnabled (false); + mp_ui->apply_to_all_cbx->setEnabled (false); + mp_ui->relative_cbx->setEnabled (false); + mp_ui->ok_button->setEnabled (false); + mp_ui->tree->setEnabled (false); + + } else { + + mp_stack->setCurrentWidget (mp_properties_pages [m_index]); + + mp_ui->prev_button->setEnabled (any_prev ()); + mp_ui->next_button->setEnabled (any_next ()); + mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ()); + mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); + mp_ui->tree->setEnabled (true); + + mp_properties_pages [m_index]->select_entries (m_object_indexes); + mp_properties_pages [m_index]->update (); + + } +} + void PropertiesDialog::next_pressed () { BEGIN_PROTECTED + if (m_object_indexes.empty ()) { + return; + } + if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); @@ -152,32 +419,32 @@ BEGIN_PROTECTED } // advance the current entry - ++(*mp_properties_pages [m_index]); + int object_index = int (m_object_indexes.front ()); + ++object_index; // look for next usable editable if at end - if (mp_properties_pages [m_index]->at_end ()) { - mp_properties_pages [m_index]->leave (); + if (object_index >= int (mp_properties_pages [m_index]->count ())) { + ++m_index; - while (m_index < int (mp_properties_pages.size ()) && - (mp_properties_pages [m_index] == 0 || ! mp_properties_pages [m_index]->front_checked ())) { - ++m_index; - } + object_index = 0; + // because we checked that there are any further elements, this should not happen: if (m_index >= int (mp_properties_pages.size ())) { return; } - mp_stack->setCurrentWidget (mp_properties_pages [m_index]); + } + m_object_indexes.clear (); + m_object_indexes.push_back (object_index); + ++m_current_object; update_title (); - mp_ui->prev_button->setEnabled (true); - mp_ui->next_button->setEnabled (any_next ()); - mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); - mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ()); - mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - mp_properties_pages [m_index]->update (); + update_controls (); + m_signals_enabled = false; + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, object_index)); + m_signals_enabled = true; END_PROTECTED } @@ -187,6 +454,10 @@ PropertiesDialog::prev_pressed () { BEGIN_PROTECTED + if (m_object_indexes.empty ()) { + return; + } + if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); @@ -195,35 +466,35 @@ BEGIN_PROTECTED } } - if (mp_properties_pages [m_index]->at_begin ()) { + // advance the current entry + int object_index = int (m_object_indexes.front ()); + if (object_index == 0) { // look for last usable editable if at end - mp_properties_pages [m_index]->leave (); --m_index; - while (m_index >= 0 && - (mp_properties_pages [m_index] == 0 || ! mp_properties_pages [m_index]->back_checked ())) { - --m_index; - } + // because we checked that there are any further elements, this should not happen: if (m_index < 0) { return; } - mp_stack->setCurrentWidget (mp_properties_pages [m_index]); - + + object_index = mp_properties_pages [m_index]->count (); + } // decrement the current entry - --(*mp_properties_pages [m_index]); + --object_index; + + m_object_indexes.clear (); + m_object_indexes.push_back (object_index); --m_current_object; update_title (); - mp_ui->next_button->setEnabled (true); - mp_ui->prev_button->setEnabled (any_prev ()); - mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); - mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ()); - mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - mp_properties_pages [m_index]->update (); + update_controls (); + m_signals_enabled = false; + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, object_index)); + m_signals_enabled = true; END_PROTECTED } @@ -231,40 +502,39 @@ END_PROTECTED void PropertiesDialog::update_title () { - setWindowTitle (tl::to_qstring (tl::to_string (QObject::tr ("Object Properties - ")) + tl::to_string (m_current_object + 1) + tl::to_string (QObject::tr (" of ")) + tl::to_string (m_objects))); + if (m_index < 0) { + setWindowTitle (QObject::tr ("Object Properties")); + } else { + setWindowTitle (tl::to_qstring (tl::to_string (QObject::tr ("Object Properties - ")) + tl::to_string (m_current_object + 1) + tl::to_string (QObject::tr (" of ")) + tl::to_string (m_objects))); + } } bool PropertiesDialog::any_next () const { - // test-advance + if (m_object_indexes.empty ()) { + return false; + } + int index = m_index; - ++(*mp_properties_pages [index]); - if (mp_properties_pages [index]->at_end ()) { + if (m_object_indexes.front () + 1 >= mp_properties_pages [index]->count ()) { ++index; - while (index < int (mp_properties_pages.size ()) && - (mp_properties_pages [index] == 0 || ! mp_properties_pages [index]->front_checked ())) { - ++index; - } } // return true, if not at end - bool ret = (index < int (mp_properties_pages.size ())); - --(*mp_properties_pages [m_index]); - return ret; + return (index < int (mp_properties_pages.size ())); } bool PropertiesDialog::any_prev () const { - // test-decrement + if (m_object_indexes.empty ()) { + return false; + } + int index = m_index; - if (mp_properties_pages [index]->at_begin ()) { + if (m_object_indexes.front () == 0) { --index; - while (index >= 0 && - (mp_properties_pages [index] == 0 || ! mp_properties_pages [index]->back_checked ())) { - --index; - } } // return true, if not at the beginning diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index c59f3975b..2f69f6591 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -54,6 +54,7 @@ class Editable; class Editables; class PropertiesPage; class MainWindow; +class PropertiesTreeModel; /** * @brief The properties dialog @@ -80,20 +81,28 @@ public: ~PropertiesDialog (); private: + friend class PropertiesTreeModel; + std::vector mp_properties_pages; db::Manager *mp_manager; lay::Editables *mp_editables; - int m_index; + int m_index, m_prev_index; + std::vector m_object_indexes; QStackedLayout *mp_stack; + QLabel *mp_none; lay::MainWindow *mp_mw; size_t m_objects, m_current_object; bool m_auto_applied; db::Manager::transaction_id_t m_transaction_id; + PropertiesTreeModel *mp_tree_model; + bool m_signals_enabled; + const std::vector &properties_pages () { return mp_properties_pages; } void disconnect (); bool any_prev () const; bool any_next () const; void update_title (); + void update_controls (); public slots: void apply (); @@ -101,6 +110,9 @@ public slots: void prev_pressed (); void cancel_pressed (); void ok_pressed (); + void apply_to_all_pressed (); + void current_index_changed (const QModelIndex &index, const QModelIndex &previous); + void selection_changed (); protected: void reject (); diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index df3289568..e79b6ebcf 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -860,6 +860,40 @@ const char *color_icon = "xxxxE44ExxD33Dxxxx" "xxxxxeexxxxddxxxxx"; +const char *color_icon_2x = + "xxxxxxxxxxaaaaxxxxxxxxbbbbxxxxxxxxxx" + "xxxxxxxxxA0000AxxxxxxB1111Bxxxxxxxxx" + "xxxxxxxxA000000AxxxxB111111Bxxxxxxxx" + "xxxxxxxA00000000AxxB11111111Bxxxxxxx" + "xxxxxxa0000000000ab1111111111bxxxxxx" + "xxxxxxa0000000000ab1111111111bxxxxxx" + "xxxxxxa0000000000ab1111111111bxxxxxx" + "xxxxxxa0000000000ab1111111111bxxxxxx" + "xxxxxxxA00000000AxxB11111111Bxxxxxxx" + "xxxxxxxxA000000AxxxxB111111Bxxxxxxxx" + "xxxxffffxA0000AxxxxxxB1111Bxccccxxxx" + "xxxF5555FxaaaaxxxxxxxxbbbbxC2222Cxxx" + "xxF555555FxxxxxxxxxxxxxxxxC222222Cxx" + "xF55555555FxxxxxxxxxxxxxxC22222222Cx" + "f5555555555fxxxxxxxxxxxxc2222222222c" + "f5555555555fxxxxxxxxxxxxc2222222222c" + "f5555555555fxxxxxxxxxxxxc2222222222c" + "f5555555555fxxxxxxxxxxxxc2222222222c" + "xF55555555FxxxxxxxxxxxxxxC22222222Cx" + "xxF555555FxxxxxxxxxxxxxxxxC222222Cxx" + "xxxF5555FxeeeexxxxxxxxddddxC2222Cxxx" + "xxxxffffxE4444ExxxxxxD3333Dxccccxxxx" + "xxxxxxxxE444444ExxxxD333333Dxxxxxxxx" + "xxxxxxxE44444444ExxD33333333Dxxxxxxx" + "xxxxxxe4444444444ed3333333333dxxxxxx" + "xxxxxxe4444444444ed3333333333dxxxxxx" + "xxxxxxe4444444444ed3333333333dxxxxxx" + "xxxxxxe4444444444ed3333333333dxxxxxx" + "xxxxxxxE44444444ExxD33333333Dxxxxxxx" + "xxxxxxxxE444444ExxxxD333333Dxxxxxxxx" + "xxxxxxxxxE4444ExxxxxxD3333Dxxxxxxxxx" + "xxxxxxxxxxeeeexxxxxxxxddddxxxxxxxxxx"; + void ColorButton::build_color_menu (QMenu *menu, QObject *receiver, const char *browse_slot, const char *selected_slot) { @@ -898,10 +932,18 @@ ColorButton::build_color_menu (QMenu *menu, QObject *receiver, const char *brows codes.insert (std::make_pair ('A' + j, c)); } - QImage icon (18, 16, QImage::Format_ARGB32); - const char *cp = color_icon; - for (int y = 0; y < 16; ++y) { - for (int x = 0; x < 18; ++x) { + int dpr = 1; +#if QT_VERSION >= 0x50000 + dpr = (menu->devicePixelRatio () >= 2.0) ? 2 : 1; +#endif + + QImage icon (18 * dpr, 16 * dpr, QImage::Format_ARGB32); +#if QT_VERSION >= 0x50000 + icon.setDevicePixelRatio (menu->devicePixelRatio ()); +#endif + const char *cp = dpr > 1 ? color_icon_2x : color_icon; + for (int y = 0; y < icon.height (); ++y) { + for (int x = 0; x < icon.width (); ++x) { icon.setPixel (x, y, codes [*cp].rgba ()); ++cp; } @@ -915,6 +957,9 @@ ColorButton::build_color_menu (QMenu *menu, QObject *receiver, const char *brows std::string name = tl::sprintf ("#%d", i + 1); QPixmap icon (16, 16); +#if QT_VERSION >= 0x50000 + icon.setDevicePixelRatio (menu->devicePixelRatio ()); +#endif icon.fill (color); submenu->addAction (QIcon (icon), tl::to_qstring (name), receiver, selected_slot)->setData (QVariant (color)); @@ -949,7 +994,16 @@ ColorButton::set_color_internal (QColor c) QPushButton::setIconSize (QSize (rt.width (), rt.height ())); - QPixmap pixmap (rt.width (), rt.height ()); +#if QT_VERSION >= 0x050000 + double dpr = devicePixelRatio (); +#else + double dpr = 1.0; +#endif + + QPixmap pixmap (rt.width () * dpr, rt.height () * dpr); +#if QT_VERSION >= 0x50000 + pixmap.setDevicePixelRatio (dpr); +#endif pixmap.fill (QColor (0, 0, 0, 0)); QColor text_color = palette ().color (QPalette::Active, QPalette::Text); @@ -959,13 +1013,13 @@ ColorButton::set_color_internal (QColor c) if (! m_color.isValid ()) { pxpainter.setFont (font ()); - QRect r (0, 0, pixmap.width () - 1, pixmap.height () - 1); + QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ()); pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QObject::tr ("Auto")); } else { pxpainter.setBrush (QBrush (c)); - QRect r (0, 0, pixmap.width () - 1, pixmap.height () - 1); + QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ()); pxpainter.drawRect (r); } diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 39863b20a..965a60d77 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -25,6 +25,7 @@ #include "rdbMarkerBrowserDialog.h" #include "rdb.h" #include "rdbReader.h" +#include "rdbUtils.h" #include "tlProgress.h" #include "layLayoutViewBase.h" #include "tlExceptions.h" @@ -790,11 +791,7 @@ MarkerBrowserDialog::scan_layer () for (db::ShapeIterator shape = cell.shapes ((*l)->layer_index ()).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { - std::unique_ptr value (rdb::ValueBase::create_from_shape (*shape, db::CplxTrans (layout.dbu ()))); - if (value.get ()) { - rdb::Item *item = rdb->create_item (rdb_cell->id (), cat->id ()); - item->values ().add (value.release ()); - } + rdb::create_item_from_shape (rdb.get (), rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); ++progress; @@ -869,11 +866,7 @@ MarkerBrowserDialog::scan_layer_flat () db::RecursiveShapeIterator shape (layout, *cv.cell (), (*l)->layer_index ()); while (! shape.at_end ()) { - std::unique_ptr value (rdb::ValueBase::create_from_shape (*shape, db::CplxTrans (layout.dbu ()) * shape.trans ())); - if (value.get ()) { - rdb::Item *item = rdb->create_item (rdb_top_cell->id (), cat->id ()); - item->values ().add (value.release ()); - } + rdb::create_item_from_shape (rdb.get (), rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); ++progress; ++shape; diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index e25e42ebb..1d084063a 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -584,10 +584,15 @@ LayoutView::show_properties () } // re-create a new properties dialog + QByteArray geom; if (mp_properties_dialog) { + geom = mp_properties_dialog->saveGeometry (); delete mp_properties_dialog.data (); } mp_properties_dialog = new lay::PropertiesDialog (widget (), manager (), this); + if (! geom.isEmpty ()) { + mp_properties_dialog->restoreGeometry (geom); + } // if launched from a dialog, do not use "show" as this blocks user interaction if (QApplication::activeModalWidget ()) { @@ -731,6 +736,24 @@ LayoutView::update_menu (lay::LayoutView *view, lay::AbstractMenu &menu) bool LayoutView::configure (const std::string &name, const std::string &value) { + if (name == cfg_bitmap_oversampling) { + + int os = 1; + tl::from_string (value, os); + if (mp_control_panel) { + mp_control_panel->set_oversampling (os); + } + + } else if (name == cfg_highres_mode) { + + bool hrm = false; + tl::from_string (value, hrm); + if (mp_control_panel) { + mp_control_panel->set_highres_mode (hrm); + } + + } + if (LayoutViewBase::configure (name, value)) { return true; } diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc index 2da4268f5..8aa4697b9 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc @@ -182,9 +182,9 @@ NetTracerLayerExpressionInfo::compile (const std::string &s) } NetTracerLayerExpression * -NetTracerLayerExpressionInfo::get_expr (const db::LayerProperties &lp, const db::Layout &layout, const NetTracerTechnologyComponent &tech, const std::set &used_symbols) const +NetTracerLayerExpressionInfo::get_expr (const db::LayerProperties &lp, const db::Layout &layout, const NetTracerConnectivity &tech, const std::set &used_symbols) const { - for (NetTracerTechnologyComponent::const_symbol_iterator s = tech.begin_symbols (); s != tech.end_symbols (); ++s) { + for (NetTracerConnectivity::const_symbol_iterator s = tech.begin_symbols (); s != tech.end_symbols (); ++s) { if (s->symbol ().log_equal (lp)) { std::set us = used_symbols; if (! us.insert (s->symbol ().to_string ()).second) { @@ -204,14 +204,14 @@ NetTracerLayerExpressionInfo::get_expr (const db::LayerProperties &lp, const db: } NetTracerLayerExpression * -NetTracerLayerExpressionInfo::get (const db::Layout &layout, const NetTracerTechnologyComponent &tech) const +NetTracerLayerExpressionInfo::get (const db::Layout &layout, const NetTracerConnectivity &tech) const { std::set us; return get (layout, tech, us); } NetTracerLayerExpression * -NetTracerLayerExpressionInfo::get (const db::Layout &layout, const NetTracerTechnologyComponent &tech, const std::set &used_symbols) const +NetTracerLayerExpressionInfo::get (const db::Layout &layout, const NetTracerConnectivity &tech, const std::set &used_symbols) const { NetTracerLayerExpression *e = 0; @@ -252,7 +252,7 @@ NetTracerConnectionInfo::NetTracerConnectionInfo (const NetTracerLayerExpression // .. nothing yet .. } -static int get_layer_id (const NetTracerLayerExpressionInfo &e, const db::Layout &layout, const NetTracerTechnologyComponent &tech, NetTracerData *data) +static int get_layer_id (const NetTracerLayerExpressionInfo &e, const db::Layout &layout, const NetTracerConnectivity &tech, NetTracerData *data) { std::unique_ptr expr_in (NetTracerLayerExpressionInfo::compile (e.to_string ()).get (layout, tech)); int l = expr_in->alias_for (); @@ -266,7 +266,7 @@ static int get_layer_id (const NetTracerLayerExpressionInfo &e, const db::Layout } NetTracerConnection -NetTracerConnectionInfo::get (const db::Layout &layout, const NetTracerTechnologyComponent &tech, NetTracerData &data) const +NetTracerConnectionInfo::get (const db::Layout &layout, const NetTracerConnectivity &tech, NetTracerData &data) const { int la = get_layer_id (m_la, layout, tech, &data); int lb = get_layer_id (m_lb, layout, tech, &data); @@ -490,30 +490,40 @@ NetTracerNet::define_layer (unsigned int l, const db::LayerProperties &lp, const NetTracerTechnologyComponent::NetTracerTechnologyComponent () : db::TechnologyComponent (net_tracer_component_name (), tl::to_string (tr ("Connectivity"))) +{ + // .. nothing yet .. +} + +// ----------------------------------------------------------------------------------- +// NetTracerConnectivity implementation + +NetTracerConnectivity::NetTracerConnectivity () { // .. nothing yet .. } -NetTracerTechnologyComponent::NetTracerTechnologyComponent (const NetTracerTechnologyComponent &d) - : db::TechnologyComponent (net_tracer_component_name (), tl::to_string (tr ("Connectivity"))) +NetTracerConnectivity::NetTracerConnectivity (const NetTracerConnectivity &d) { - m_connections = d.m_connections; - m_symbols = d.m_symbols; + operator= (d); } -NetTracerTechnologyComponent &NetTracerTechnologyComponent::operator= (const NetTracerTechnologyComponent &d) +NetTracerConnectivity &NetTracerConnectivity::operator= (const NetTracerConnectivity &d) { - m_connections = d.m_connections; - m_symbols = d.m_symbols; + if (this != &d) { + m_connections = d.m_connections; + m_symbols = d.m_symbols; + m_name = d.m_name; + m_description = d.m_description; + } return *this; } NetTracerData -NetTracerTechnologyComponent::get_tracer_data (const db::Layout &layout) const +NetTracerConnectivity::get_tracer_data (const db::Layout &layout) const { // test run on the expressions to verify their syntax int n = 1; - for (NetTracerTechnologyComponent::const_iterator c = begin (); c != end (); ++c, ++n) { + for (NetTracerConnectivity::const_iterator c = begin (); c != end (); ++c, ++n) { if (c->layer_a ().to_string ().empty ()) { throw tl::Exception (tl::to_string (tr ("Missing first layer specification on connectivity specification #%d")), n); } @@ -523,7 +533,7 @@ NetTracerTechnologyComponent::get_tracer_data (const db::Layout &layout) const } n = 1; - for (NetTracerTechnologyComponent::const_symbol_iterator s = begin_symbols (); s != end_symbols (); ++s, ++n) { + for (NetTracerConnectivity::const_symbol_iterator s = begin_symbols (); s != end_symbols (); ++s, ++n) { if (s->symbol ().to_string ().empty ()) { throw tl::Exception (tl::to_string (tr ("Missing symbol name on symbol specification #%d")), n); } @@ -540,12 +550,12 @@ NetTracerTechnologyComponent::get_tracer_data (const db::Layout &layout) const NetTracerData data; // register a logical layer for each original one as alias and one for each expression with a new ID - for (db::NetTracerTechnologyComponent::const_symbol_iterator s = begin_symbols (); s != end_symbols (); ++s) { + for (db::NetTracerConnectivity::const_symbol_iterator s = begin_symbols (); s != end_symbols (); ++s) { db::NetTracerLayerExpression *expr = db::NetTracerLayerExpressionInfo::compile (s->expression ()).get (layout, *this); data.register_logical_layer (expr, s->symbol ().to_string ().c_str ()); } - for (db::NetTracerTechnologyComponent::const_iterator c = begin (); c != end (); ++c) { + for (db::NetTracerConnectivity::const_iterator c = begin (); c != end (); ++c) { data.add_connection (c->get (layout, *this, data)); } diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h index b0f760256..451d2cf3f 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h @@ -32,7 +32,7 @@ namespace db { -class NetTracerTechnologyComponent; +class NetTracerConnectivity; DB_PLUGIN_PUBLIC std::string net_tracer_component_name (); @@ -57,7 +57,7 @@ public: return m_expression; } - NetTracerLayerExpression *get (const db::Layout &layout, const NetTracerTechnologyComponent &tech) const; + NetTracerLayerExpression *get (const db::Layout &layout, const NetTracerConnectivity &tech) const; private: std::string m_expression; @@ -70,8 +70,8 @@ private: static NetTracerLayerExpressionInfo parse_mult (tl::Extractor &ex); static NetTracerLayerExpressionInfo parse_atomic (tl::Extractor &ex); - NetTracerLayerExpression *get (const db::Layout &layout, const NetTracerTechnologyComponent &tech, const std::set &used_symbols) const; - NetTracerLayerExpression *get_expr (const db::LayerProperties &lp, const db::Layout &layout, const NetTracerTechnologyComponent &tech, const std::set &used_symbols) const; + NetTracerLayerExpression *get (const db::Layout &layout, const NetTracerConnectivity &tech, const std::set &used_symbols) const; + NetTracerLayerExpression *get_expr (const db::LayerProperties &lp, const db::Layout &layout, const NetTracerConnectivity &tech, const std::set &used_symbols) const; }; class DB_PLUGIN_PUBLIC NetTracerConnectionInfo @@ -81,7 +81,7 @@ public: NetTracerConnectionInfo (const NetTracerLayerExpressionInfo &la, const NetTracerLayerExpressionInfo &lb); NetTracerConnectionInfo (const NetTracerLayerExpressionInfo &la, const NetTracerLayerExpressionInfo &via, const NetTracerLayerExpressionInfo &lb); - NetTracerConnection get (const db::Layout &layout, const NetTracerTechnologyComponent &tech, NetTracerData &data) const; + NetTracerConnection get (const db::Layout &layout, const NetTracerConnectivity &tech, NetTracerData &data) const; std::string to_string () const; void parse (tl::Extractor &ex); @@ -357,8 +357,7 @@ private: void define_layer (unsigned int l, const db::LayerProperties &lp, const db::LayerProperties &lp_representative); }; -class DB_PLUGIN_PUBLIC NetTracerTechnologyComponent - : public db::TechnologyComponent +class DB_PLUGIN_PUBLIC NetTracerConnectivity { public: typedef std::vector::const_iterator const_iterator; @@ -366,9 +365,29 @@ public: typedef std::vector::const_iterator const_symbol_iterator; typedef std::vector::iterator symbol_iterator; - NetTracerTechnologyComponent (); - NetTracerTechnologyComponent (const NetTracerTechnologyComponent &d); - NetTracerTechnologyComponent &operator= (const NetTracerTechnologyComponent &d); + NetTracerConnectivity (); + NetTracerConnectivity (const NetTracerConnectivity &d); + NetTracerConnectivity &operator= (const NetTracerConnectivity &d); + + const std::string &name () const + { + return m_name; + } + + void set_name (const std::string &n) + { + m_name = n; + } + + const std::string &description () const + { + return m_description; + } + + void set_description (const std::string &d) + { + m_description = d; + } const_iterator begin () const { @@ -416,6 +435,16 @@ public: m_symbols.clear (); } + void clear_connections () + { + m_connections.clear (); + } + + void clear_symbols () + { + m_symbols.clear (); + } + void erase (iterator p) { m_connections.erase (p); @@ -458,14 +487,73 @@ public: NetTracerData get_tracer_data (const db::Layout &layout) const; - db::TechnologyComponent *clone () const +private: + std::vector m_connections; + std::vector m_symbols; + std::string m_name, m_description; +}; + +class DB_PLUGIN_PUBLIC NetTracerTechnologyComponent + : public db::TechnologyComponent +{ +public: + typedef std::vector::const_iterator const_iterator; + typedef std::vector::iterator iterator; + + NetTracerTechnologyComponent (); + + size_t size () const + { + return m_connectivity.size (); + } + + void push_back (const db::NetTracerConnectivity &c) + { + m_connectivity.push_back (c); + } + + void clear () + { + m_connectivity.clear (); + } + + void erase (iterator i) + { + m_connectivity.erase (i); + } + + void insert (iterator i, const db::NetTracerConnectivity &c) + { + m_connectivity.insert (i, c); + } + + const_iterator begin () const + { + return m_connectivity.begin (); + } + + const_iterator end () const + { + return m_connectivity.end (); + } + + iterator begin () + { + return m_connectivity.begin (); + } + + iterator end () + { + return m_connectivity.end (); + } + + db::NetTracerTechnologyComponent *clone () const { return new NetTracerTechnologyComponent (*this); } private: - std::vector m_connections; - std::vector m_symbols; + std::vector m_connectivity; }; } diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc index d0793d4be..e9b442f7b 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerPlugin.cc @@ -65,6 +65,101 @@ namespace tl }; } +namespace +{ + +static const db::NetTracerConnectivity * +get_default (const db::NetTracerTechnologyComponent &tc) +{ + for (auto d = tc.begin (); d != tc.end (); ++d) { + if (d->name ().empty ()) { + return d.operator-> (); + } + } + + if (tc.begin () != tc.end ()) { + return tc.begin ().operator-> (); + } else { + return 0; + } +} + +template +struct FallbackXMLWriteAdaptor +{ + FallbackXMLWriteAdaptor (void (db::NetTracerConnectivity::*member) (const Value &), void (db::NetTracerConnectivity::*clear) ()) + : mp_member (member), mp_clear (clear), mp_stack (0) + { + // .. nothing yet .. + } + + void operator () (db::NetTracerTechnologyComponent &owner, tl::XMLReaderState &reader) const + { + if (! mp_stack) { + mp_stack = const_cast (get_default (owner)); + if (! mp_stack) { + owner.push_back (db::NetTracerConnectivity ()); + mp_stack = (owner.end () - 1).operator-> (); + } + (mp_stack->*mp_clear) (); + } + + tl::XMLObjTag tag; + (mp_stack->*mp_member) (*reader.back (tag)); + } + +private: + void (db::NetTracerConnectivity::*mp_member) (const Value &); + void (db::NetTracerConnectivity::*mp_clear) (); + mutable db::NetTracerConnectivity *mp_stack; +}; + +template +struct FallbackXMLReadAdaptor +{ + typedef tl::pass_by_ref_tag tag; + + FallbackXMLReadAdaptor (Iter (db::NetTracerConnectivity::*begin) () const, Iter (db::NetTracerConnectivity::*end) () const) + : mp_begin (begin), mp_end (end) + { + // .. nothing yet .. + } + + Value operator () () const + { + return *m_iter; + } + + bool at_end () const + { + return m_iter == m_end; + } + + void start (const db::NetTracerTechnologyComponent &parent) + { + const db::NetTracerConnectivity *tn = get_default (parent); + if (! tn) { + m_iter = Iter (); + m_end = Iter (); + } else { + m_iter = (tn->*mp_begin) (); + m_end = (tn->*mp_end) (); + } + } + + void next () + { + ++m_iter; + } + +private: + Iter (db::NetTracerConnectivity::*mp_begin) () const; + Iter (db::NetTracerConnectivity::*mp_end) () const; + Iter m_iter, m_end; +}; + +} + namespace db { @@ -86,8 +181,20 @@ public: virtual tl::XMLElementBase *xml_element () const { return new db::TechnologyComponentXMLElement (net_tracer_component_name (), - tl::make_member ((NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::begin, (NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::end, &NetTracerTechnologyComponent::add, "connection") + - tl::make_member ((NetTracerTechnologyComponent::const_symbol_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::begin_symbols, (NetTracerTechnologyComponent::const_symbol_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::end_symbols, &NetTracerTechnologyComponent::add_symbol, "symbols") + // 0.28 definitions + tl::make_element ((NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::begin, (NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::end, (void (NetTracerTechnologyComponent::*) (const NetTracerConnectivity &)) &NetTracerTechnologyComponent::push_back, "stack", + tl::make_member (&NetTracerConnectivity::name, &NetTracerConnectivity::set_name, "name") + + tl::make_member (&NetTracerConnectivity::description, &NetTracerConnectivity::set_description, "description") + + tl::make_member ((NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin, (NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end, &NetTracerConnectivity::add, "connection") + + tl::make_member ((NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin_symbols, (NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end_symbols, &NetTracerConnectivity::add_symbol, "symbols") + ) + + // Fallback readers for migrating pre-0.28 setups to 0.28 and backward compatibility + tl::XMLMember, FallbackXMLWriteAdaptor , tl::XMLStdConverter > ( + FallbackXMLReadAdaptor (&NetTracerConnectivity::begin, &NetTracerConnectivity::end), + FallbackXMLWriteAdaptor (&NetTracerConnectivity::add, &NetTracerConnectivity::clear_connections), "connection") + + tl::XMLMember, FallbackXMLWriteAdaptor , tl::XMLStdConverter > ( + FallbackXMLReadAdaptor (&NetTracerConnectivity::begin_symbols, &NetTracerConnectivity::end_symbols), + FallbackXMLWriteAdaptor (&NetTracerConnectivity::add_symbol, &NetTracerConnectivity::clear_symbols), "symbols") ); } }; diff --git a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc index ff1f6b5a0..5f24ca7d3 100644 --- a/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/gsiDeclDbNetTracer.cc @@ -32,14 +32,14 @@ namespace gsi // ----------------------------------------------------------------------------------- // GSI binding -static void def_connection2 (db::NetTracerTechnologyComponent *tech, const std::string &la, const std::string &lb) +static void def_connection2 (db::NetTracerConnectivity *tech, const std::string &la, const std::string &lb) { db::NetTracerLayerExpressionInfo la_info = db::NetTracerLayerExpressionInfo::compile (la); db::NetTracerLayerExpressionInfo lb_info = db::NetTracerLayerExpressionInfo::compile (lb); tech->add (db::NetTracerConnectionInfo (la_info, lb_info)); } -static void def_connection3 (db::NetTracerTechnologyComponent *tech, const std::string &la, const std::string &via, const std::string &lb) +static void def_connection3 (db::NetTracerConnectivity *tech, const std::string &la, const std::string &via, const std::string &lb) { db::NetTracerLayerExpressionInfo la_info = db::NetTracerLayerExpressionInfo::compile (la); db::NetTracerLayerExpressionInfo via_info = db::NetTracerLayerExpressionInfo::compile (via); @@ -47,14 +47,26 @@ static void def_connection3 (db::NetTracerTechnologyComponent *tech, const std:: tech->add (db::NetTracerConnectionInfo (la_info, via_info, lb_info)); } -static void def_symbol (db::NetTracerTechnologyComponent *tech, const std::string &name, const std::string &expr) +static void def_symbol (db::NetTracerConnectivity *tech, const std::string &name, const std::string &expr) { tech->add_symbol (db::NetTracerSymbolInfo (db::LayerProperties (name), expr)); } -gsi::Class &decl_dbTechnologyComponent (); - -gsi::Class decl_NetTracerTechnology (decl_dbTechnologyComponent (), "db", "NetTracerTechnology", +gsi::Class decl_NetTracerConnectivity ("db", "NetTracerConnectivity", + gsi::method ("name", &db::NetTracerConnectivity::name, + "@brief Gets the name of the connectivty definition\n" + "The name is an optional string defining the formal name for this definition.\n" + ) + + gsi::method ("name=", &db::NetTracerConnectivity::set_name, gsi::arg ("n"), + "@brief Sets the name of the connectivty definition\n" + ) + + gsi::method ("description", &db::NetTracerConnectivity::description, + "@brief Gets the description text of the connectivty definition\n" + "The description is an optional string giving a human-readable description for this definition." + ) + + gsi::method ("description=", &db::NetTracerConnectivity::set_description, gsi::arg ("d"), + "@brief Sets the description of the connectivty definition\n" + ) + gsi::method_ext ("connection", &def_connection2, gsi::arg("a"), gsi::arg("b"), "@brief Defines a connection between two materials\n" "See the class description for details about this method." @@ -68,7 +80,7 @@ gsi::Class decl_NetTracerTechnology (decl_dbTe "Defines a sub-expression to be used in further symbols or material expressions. " "For the detailed notation of the expression see the description of the net tracer feature." ), - "@brief A technology description for the net tracer\n" + "@brief A connectivity description for the net tracer\n" "\n" "This object represents the technology description for the net tracer (represented by the \\NetTracer class).\n" "A technology description basically consists of connection declarations.\n" @@ -83,16 +95,17 @@ gsi::Class decl_NetTracerTechnology (decl_dbTe "\n" "For details about the expressions see the description of the net tracer feature.\n" "\n" - "This class has been introduced in version 0.25.\n" + "This class has been introduced in version 0.28 and replaces the 'NetTracerTechnology' class which " + "has been generalized.\n" ); -static void trace1 (db::NetTracer *net_tracer, const db::NetTracerTechnologyComponent &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer) +static void trace1 (db::NetTracer *net_tracer, const db::NetTracerConnectivity &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer) { db::NetTracerData tracer_data = tech.get_tracer_data (layout); net_tracer->trace (layout, cell, start_point, start_layer, tracer_data); } -static void trace2 (db::NetTracer *net_tracer, const db::NetTracerTechnologyComponent &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer, const db::Point &stop_point, unsigned int stop_layer) +static void trace2 (db::NetTracer *net_tracer, const db::NetTracerConnectivity &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer, const db::Point &stop_point, unsigned int stop_layer) { db::NetTracerData tracer_data = tech.get_tracer_data (layout); net_tracer->trace (layout, cell, start_point, start_layer, stop_point, stop_layer, tracer_data); @@ -106,7 +119,31 @@ static db::NetTracerData get_tracer_data_from_tech (const std::string &tech_name const db::NetTracerTechnologyComponent *tech_component = dynamic_cast (tech->component_by_name (db::net_tracer_component_name ())); tl_assert (tech_component != 0); - return tech_component->get_tracer_data (layout); + if (tech_component->size () < 1) { + throw tl::Exception (tl::to_string (tr ("No connectivity setup exists for technology '%s'")), tech_name); + } + if (tech_component->size () > 1) { + throw tl::Exception (tl::to_string (tr ("Multiple connectivity setups exist for technology '%s' - specify a name")), tech_name); + } + + return tech_component->begin ()->get_tracer_data (layout); +} + +static db::NetTracerData get_tracer_data_from_tech (const std::string &tech_name, const std::string &name, const db::Layout &layout) +{ + const db::Technology *tech = db::Technologies::instance ()->technology_by_name (tech_name); + tl_assert (tech != 0); + + const db::NetTracerTechnologyComponent *tech_component = dynamic_cast (tech->component_by_name (db::net_tracer_component_name ())); + tl_assert (tech_component != 0); + + for (auto t = tech_component->begin (); t != tech_component->end (); ++t) { + if (t->name () == name) { + return t->get_tracer_data (layout); + } + } + + throw tl::Exception (tl::to_string (tr ("No connectivity setup exists with name '%s' for technology '%s'")), name, tech_name); } static void trace1_tn (db::NetTracer *net_tracer, const std::string &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer) @@ -115,12 +152,24 @@ static void trace1_tn (db::NetTracer *net_tracer, const std::string &tech, const net_tracer->trace (layout, cell, start_point, start_layer, tracer_data); } +static void trace1_tn2 (db::NetTracer *net_tracer, const std::string &tech, const std::string &name, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer) +{ + db::NetTracerData tracer_data = get_tracer_data_from_tech (tech, name, layout); + net_tracer->trace (layout, cell, start_point, start_layer, tracer_data); +} + static void trace2_tn (db::NetTracer *net_tracer, const std::string &tech, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer, const db::Point &stop_point, unsigned int stop_layer) { db::NetTracerData tracer_data = get_tracer_data_from_tech (tech, layout); net_tracer->trace (layout, cell, start_point, start_layer, stop_point, stop_layer, tracer_data); } +static void trace2_tn2 (db::NetTracer *net_tracer, const std::string &tech, const std::string &name, const db::Layout &layout, const db::Cell &cell, const db::Point &start_point, unsigned int start_layer, const db::Point &stop_point, unsigned int stop_layer) +{ + db::NetTracerData tracer_data = get_tracer_data_from_tech (tech, name, layout); + net_tracer->trace (layout, cell, start_point, start_layer, stop_point, stop_layer, tracer_data); +} + gsi::Class decl_NetElement ("db", "NetElement", gsi::method ("trans", &db::NetTracerShape::trans, "@brief Gets the transformation to apply for rendering the shape in the original top cell\n" @@ -178,7 +227,7 @@ gsi::Class decl_NetTracer ("db", "NetTracer", "A path extraction version is provided as well which will extract one (the presumably shortest) path between two " "points.\n" "\n" - "@param tech The technology definition\n" + "@param tech The connectivity definition\n" "@param layout The layout on which to run the extraction\n" "@param cell The cell on which to run the extraction (child cells will be included)\n" "@param start_point The start point from which to start extraction of the net\n" @@ -194,7 +243,7 @@ gsi::Class decl_NetTracer ("db", "NetTracer", "\n" "This version runs a path extraction and will deliver elements forming one path leading from the start to the end point.\n" "\n" - "@param tech The technology definition\n" + "@param tech The connectivity definition\n" "@param layout The layout on which to run the extraction\n" "@param cell The cell on which to run the extraction (child cells will be included)\n" "@param start_point The start point from which to start extraction of the net\n" @@ -205,13 +254,31 @@ gsi::Class decl_NetTracer ("db", "NetTracer", gsi::method_ext ("trace", &trace1_tn, gsi::arg ("tech"), gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("start_point"), gsi::arg ("start_layer"), "@brief Runs a net extraction taking a predefined technology\n" "This method behaves identical as the version with a technology object, except that it will look for a technology " - "with the given name to obtain the extraction setup." + "with the given name to obtain the extraction setup.\n" + "The technology is looked up by technology name. A version of this method exists where it is possible " + "to specify the name of the particular connectivity to use in case there are multiple definitions available." + ) + + gsi::method_ext ("trace", &trace1_tn2, gsi::arg ("tech"), gsi::arg ("connectivity_name"), gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("start_point"), gsi::arg ("start_layer"), + "@brief Runs a net extraction taking a predefined technology\n" + "This method behaves identical as the version with a technology object, except that it will look for a technology " + "with the given name to obtain the extraction setup. " + "This version allows specifying the name of the connecvitiy setup.\n" + "\n" + "This method variant has been introduced in version 0.28." ) + gsi::method_ext ("trace", &trace2_tn, gsi::arg ("tech"), gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("start_point"), gsi::arg ("start_layer"), gsi::arg ("stop_point"), gsi::arg ("stop_layer"), "@brief Runs a path extraction taking a predefined technology\n" "This method behaves identical as the version with a technology object, except that it will look for a technology " "with the given name to obtain the extraction setup." ) + + gsi::method_ext ("trace", &trace2_tn2, gsi::arg ("tech"), gsi::arg ("connectivity_name"), gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("start_point"), gsi::arg ("start_layer"), gsi::arg ("stop_point"), gsi::arg ("stop_layer"), + "@brief Runs a path extraction taking a predefined technology\n" + "This method behaves identical as the version with a technology object, except that it will look for a technology " + "with the given name to obtain the extraction setup." + "This version allows specifying the name of the connecvitiy setup.\n" + "\n" + "This method variant has been introduced in version 0.28." + ) + gsi::iterator ("each_element", &db::NetTracer::begin, &db::NetTracer::end, "@brief Iterates over the elements found during extraction\n" "The elements are available only after the extraction has been performed." @@ -267,7 +334,7 @@ gsi::Class decl_NetTracer ("db", "NetTracer", "\n" "tracer = RBA::NetTracer::new\n" "\n" - "tech = RBA::NetTracerTechnology::new\n" + "tech = RBA::NetTracerConnectivity::new\n" "tech.connection(\"1/0\", \"2/0\", \"3/0\")\n" "\n" "tracer.trace(tech, ly, ly.top_cell, RBA::Point::new(7000, 1500), ly.find_layer(1, 0))\n" @@ -277,7 +344,7 @@ gsi::Class decl_NetTracer ("db", "NetTracer", "end\n" "@/code\n" "\n" - "This class has been introduced in version 0.25." + "This class has been introduced in version 0.25. With version 0.28, the \\NetTracerConnectivity class replaces the 'NetTracerTechnology' class." ); } diff --git a/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui b/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui new file mode 100644 index 000000000..62af9ff85 --- /dev/null +++ b/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui @@ -0,0 +1,343 @@ + + + NetTracerConnectivityEditor + + + + 0 + 0 + 572 + 449 + + + + Form + + + + 6 + + + 9 + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + <html>Connectivity (<a href="int:/about/connectivity.xml">See here for details</a>)</html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 6 + + + + + ... + + + + :/down_16px.png:/down_16px.png + + + + + + + ... + + + + :/del_16px.png:/del_16px.png + + + Del + + + + + + + QAbstractItemView::AllEditTriggers + + + true + + + QAbstractItemView::SelectRows + + + 3 + + + + + + + + + + ... + + + + :/add_16px.png:/add_16px.png + + + Return + + + + + + + Qt::Vertical + + + + 20 + 131 + + + + + + + + ... + + + + :/up_16px.png:/up_16px.png + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + <html>Computed and symbolic layers (<a href="int:/about/symbolic_layers.xml">See here for details</a>)</html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 6 + + + + + ... + + + + :/down_16px.png:/down_16px.png + + + + + + + ... + + + + :/del_16px.png:/del_16px.png + + + Del + + + + + + + QAbstractItemView::AllEditTriggers + + + true + + + QAbstractItemView::SelectRows + + + 2 + + + + + + + + + ... + + + + :/add_16px.png:/add_16px.png + + + Return + + + + + + + Qt::Vertical + + + + 20 + 131 + + + + + + + + ... + + + + :/up_16px.png:/up_16px.png + + + + + + + + + + + + + + + + + diff --git a/src/plugins/tools/net_tracer/lay_plugin/NetTracerDialog.ui b/src/plugins/tools/net_tracer/lay_plugin/NetTracerDialog.ui index 823a74505..8a2669a57 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/NetTracerDialog.ui +++ b/src/plugins/tools/net_tracer/lay_plugin/NetTracerDialog.ui @@ -29,24 +29,14 @@ 6 - + Qt::Horizontal - - - - Configure - - - false - - - - + @@ -67,7 +57,7 @@ Select one or multiple nets and choose "Export" to export the selected - + @@ -196,60 +186,7 @@ Select one or multiple nets and choose "Export" to export the selected - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - Trace depth: - - - - - - - Close - - - - - - - - 1 - 0 - - - - - - - - - - - Layer Stack - - - false - - - - + @@ -332,7 +269,6 @@ Select one or multiple nets and choose "Export" to export the selected 12 - 75 true @@ -477,7 +413,23 @@ p, li { white-space: pre-wrap; } - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + @@ -488,7 +440,6 @@ p, li { white-space: pre-wrap; } 12 - 75 false true @@ -498,14 +449,54 @@ p, li { white-space: pre-wrap; } - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + Layer Stack + + + false + + + + + + + Configure + + + false + + + + + + + Close + + + + shapes - + @@ -521,6 +512,26 @@ p, li { white-space: pre-wrap; } + + + + Trace depth: + + + + + + + + 1 + 0 + + + + + + + diff --git a/src/plugins/tools/net_tracer/lay_plugin/NetTracerTechComponentEditor.ui b/src/plugins/tools/net_tracer/lay_plugin/NetTracerTechComponentEditor.ui index 188969569..15e3a6e16 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/NetTracerTechComponentEditor.ui +++ b/src/plugins/tools/net_tracer/lay_plugin/NetTracerTechComponentEditor.ui @@ -1,7 +1,8 @@ - + + NetTracerTechComponentEditor - - + + 0 0 @@ -9,324 +10,265 @@ 449 - + Form - - - 9 - - - 6 - + - - - Qt::Vertical + + + Qt::Horizontal - - + + + + 0 + 0 + + + QFrame::NoFrame - + QFrame::Raised - - + + 0 - - 6 + + 0 - - - - QFrame::NoFrame + + 0 + + + 0 + + + + + Move selected stacks up - - QFrame::Raised + + ... + + + + :/down_16px.png:/down_16px.png - - - 0 - - - 6 - - - - - <html>Connectivity (<a href="int:/about/connectivity.xml">See here for details</a>)</html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + + Add new stack + + + ... + + + + :/add_16px.png:/add_16px.png + + + Return + + + + + + QFrame::NoFrame - + QFrame::Raised - - - 0 + + + + + + + 0 + 1 + + + + Qt::ActionsContextMenu + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + + Name - - 6 + + + + Description - - - - ... - - - :/down_16px.png - - - - - - - ... - - - :/del_16px.png - - - Del - - - - - - - QAbstractItemView::AllEditTriggers - - - true - - - QAbstractItemView::SelectRows - - - 3 - - - - - - - - - - ... - - - :/add_16px.png - - - Return - - - - - - - Qt::Vertical - - - - 20 - 131 - - - - - - - - ... - - - :/up_16px.png - - - - + + + + + + + Remove selected stacks + + + ... + + + + :/del_16px.png:/del_16px.png + + + Del + + + + + + + Move selected stacks down + + + ... + + + + :/up_16px.png:/up_16px.png + + + + + + + + true + + + + Double-click to edit text + + + + + + + Qt::Horizontal + + + + 157 + 20 + + + + + + + + Technology Stacks + + + + + + + Clone current stack + + + ... + + + + :/clone_16px.png:/clone_16px.png + - - + + + + 1 + 0 + + + QFrame::NoFrame - + QFrame::Raised - - + + 0 - - 6 + + 0 + + + 0 + + + 0 - - - QFrame::NoFrame + + + Connectivity - - QFrame::Raised - - - - 0 - - - 6 - - - - - <html>Computed and symbolic layers (<a href="int:/about/symbolic_layers.xml">See here for details</a>)</html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - QFrame::NoFrame + + + + 0 + 1 + - - QFrame::Raised - - - - 0 - - - 6 - - - - - ... - - - :/down_16px.png - - - - - - - ... - - - :/del_16px.png - - - Del - - - - - - - QAbstractItemView::AllEditTriggers - - - true - - - QAbstractItemView::SelectRows - - - 2 - - - - - - - - - ... - - - :/add_16px.png - - - Return - - - - - - - Qt::Vertical - - - - 20 - 131 - - - - - - - - ... - - - :/up_16px.png - - - - + + + + Qt::Vertical + + + + 20 + 0 + + + + - + + + lay::NetTracerConnectivityEditor + QWidget +
    layNetTracerConnectivityEditor.h
    + 1 +
    +
    + + +
    diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.cc similarity index 92% rename from src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc rename to src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.cc index 752e9dabb..5458327ec 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.cc @@ -21,7 +21,7 @@ */ -#include "layNetTracerIO.h" +#include "layNetTracerConnectivityEditor.h" #include "layNetTracerConfig.h" #include "layConfigurationDialog.h" @@ -56,7 +56,7 @@ class NetTracerConnectivityColumnDelegate : public QItemDelegate { public: - NetTracerConnectivityColumnDelegate (QWidget *parent, db::NetTracerTechnologyComponent *data) + NetTracerConnectivityColumnDelegate (QWidget *parent, db::NetTracerConnectivity *data) : QItemDelegate (parent), mp_data (data) { // .. nothing yet .. @@ -148,7 +148,7 @@ public: } private: - db::NetTracerTechnologyComponent *mp_data; + db::NetTracerConnectivity *mp_data; }; // ----------------------------------------------------------------------------------------- @@ -158,7 +158,7 @@ class NetTracerConnectivitySymbolColumnDelegate : public QItemDelegate { public: - NetTracerConnectivitySymbolColumnDelegate (QWidget *parent, db::NetTracerTechnologyComponent *data) + NetTracerConnectivitySymbolColumnDelegate (QWidget *parent, db::NetTracerConnectivity *data) : QItemDelegate (parent), mp_data (data) { // .. nothing yet .. @@ -262,16 +262,16 @@ public: } private: - db::NetTracerTechnologyComponent *mp_data; + db::NetTracerConnectivity *mp_data; }; // ----------------------------------------------------------------------------------- // NetTracerTechComponentEditor implementation -NetTracerTechComponentEditor::NetTracerTechComponentEditor (QWidget *parent) - : TechnologyComponentEditor (parent) +NetTracerConnectivityEditor::NetTracerConnectivityEditor (QWidget *parent) + : QWidget (parent) { - Ui::NetTracerTechComponentEditor::setupUi (this); + Ui::NetTracerConnectivityEditor::setupUi (this); connect (add_conductor_pb, SIGNAL (clicked ()), this, SLOT (add_clicked ())); connect (del_conductor_pb, SIGNAL (clicked ()), this, SLOT (del_clicked ())); @@ -293,26 +293,20 @@ NetTracerTechComponentEditor::NetTracerTechComponentEditor (QWidget *parent) symbol_table->verticalHeader ()->hide (); } -void -NetTracerTechComponentEditor::commit () +void +NetTracerConnectivityEditor::get_connectivity (db::NetTracerConnectivity &data) { - db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ()); - if (! data) { - return; - } - - *data = m_data; + std::string name = data.name (); + std::string description = data.description (); + data = m_data; + data.set_name (name); + data.set_description (description); } void -NetTracerTechComponentEditor::setup () +NetTracerConnectivityEditor::set_connectivity (const db::NetTracerConnectivity &data) { - db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ()); - if (! data) { - return; - } - - m_data = *data; + m_data = data; for (int c = 0; c < 3; ++c) { if (connectivity_table->itemDelegateForColumn (c) != 0) { @@ -332,7 +326,7 @@ NetTracerTechComponentEditor::setup () } void -NetTracerTechComponentEditor::add_clicked () +NetTracerConnectivityEditor::add_clicked () { // removes focus from the tree view - commits the data add_conductor_pb->setFocus (); @@ -351,7 +345,7 @@ NetTracerTechComponentEditor::add_clicked () } void -NetTracerTechComponentEditor::del_clicked () +NetTracerConnectivityEditor::del_clicked () { // removes focus from the tree view - commits the data del_conductor_pb->setFocus (); @@ -374,7 +368,7 @@ NetTracerTechComponentEditor::del_clicked () } void -NetTracerTechComponentEditor::move_up_clicked () +NetTracerConnectivityEditor::move_up_clicked () { // removes focus from the tree view - commits the data move_conductor_up_pb->setFocus (); @@ -391,7 +385,7 @@ NetTracerTechComponentEditor::move_up_clicked () connectivity_table->setCurrentIndex (QModelIndex ()); int n = 0; - for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { + for (db::NetTracerConnectivity::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { if (selected_rows.find (n + 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { std::swap (m_data.begin () [n + 1], m_data.begin () [n]); selected_rows.erase (n + 1); @@ -415,7 +409,7 @@ NetTracerTechComponentEditor::move_up_clicked () } void -NetTracerTechComponentEditor::move_down_clicked () +NetTracerConnectivityEditor::move_down_clicked () { // removes focus from the tree view - commits the data move_conductor_down_pb->setFocus (); @@ -432,7 +426,7 @@ NetTracerTechComponentEditor::move_down_clicked () connectivity_table->setCurrentIndex (QModelIndex ()); int n = int (m_data.size ()); - for (db::NetTracerTechnologyComponent::iterator l = m_data.end (); l != m_data.begin (); ) { + for (db::NetTracerConnectivity::iterator l = m_data.end (); l != m_data.begin (); ) { --l; --n; if (selected_rows.find (n - 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { @@ -458,7 +452,7 @@ NetTracerTechComponentEditor::move_down_clicked () } void -NetTracerTechComponentEditor::symbol_add_clicked () +NetTracerConnectivityEditor::symbol_add_clicked () { // removes focus from the tree view - commits the data add_symbol_pb->setFocus (); @@ -477,7 +471,7 @@ NetTracerTechComponentEditor::symbol_add_clicked () } void -NetTracerTechComponentEditor::symbol_del_clicked () +NetTracerConnectivityEditor::symbol_del_clicked () { // removes focus from the tree view - commits the data del_symbol_pb->setFocus (); @@ -500,7 +494,7 @@ NetTracerTechComponentEditor::symbol_del_clicked () } void -NetTracerTechComponentEditor::symbol_move_up_clicked () +NetTracerConnectivityEditor::symbol_move_up_clicked () { // removes focus from the tree view - commits the data move_symbol_up_pb->setFocus (); @@ -517,7 +511,7 @@ NetTracerTechComponentEditor::symbol_move_up_clicked () symbol_table->setCurrentIndex (QModelIndex ()); int n = 0; - for (db::NetTracerTechnologyComponent::symbol_iterator l = m_data.begin_symbols (); l != m_data.end_symbols (); ++l, ++n) { + for (db::NetTracerConnectivity::symbol_iterator l = m_data.begin_symbols (); l != m_data.end_symbols (); ++l, ++n) { if (selected_rows.find (n + 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { std::swap (m_data.begin_symbols () [n + 1], m_data.begin_symbols () [n]); selected_rows.erase (n + 1); @@ -541,7 +535,7 @@ NetTracerTechComponentEditor::symbol_move_up_clicked () } void -NetTracerTechComponentEditor::symbol_move_down_clicked () +NetTracerConnectivityEditor::symbol_move_down_clicked () { // removes focus from the tree view - commits the data move_symbol_down_pb->setFocus (); @@ -558,7 +552,7 @@ NetTracerTechComponentEditor::symbol_move_down_clicked () symbol_table->setCurrentIndex (QModelIndex ()); int n = int (m_data.symbols ()); - for (db::NetTracerTechnologyComponent::symbol_iterator l = m_data.end_symbols (); l != m_data.begin_symbols (); ) { + for (db::NetTracerConnectivity::symbol_iterator l = m_data.end_symbols (); l != m_data.begin_symbols (); ) { --l; --n; if (selected_rows.find (n - 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { @@ -584,7 +578,7 @@ NetTracerTechComponentEditor::symbol_move_down_clicked () } void -NetTracerTechComponentEditor::update () +NetTracerConnectivityEditor::update () { QStringList labels; int n; @@ -600,7 +594,7 @@ NetTracerTechComponentEditor::update () connectivity_table->setHorizontalHeaderLabels (labels); n = 0; - for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { + for (db::NetTracerConnectivity::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { for (int c = 0; c < 3; ++c) { @@ -652,7 +646,7 @@ NetTracerTechComponentEditor::update () symbol_table->setHorizontalHeaderLabels (labels); n = 0; - for (db::NetTracerTechnologyComponent::symbol_iterator l = m_data.begin_symbols (); l != m_data.end_symbols (); ++l, ++n) { + for (db::NetTracerConnectivity::symbol_iterator l = m_data.begin_symbols (); l != m_data.end_symbols (); ++l, ++n) { for (int c = 0; c < 2; ++c) { diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.h new file mode 100644 index 000000000..beed78303 --- /dev/null +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerConnectivityEditor.h @@ -0,0 +1,82 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_layNetTracerConnectivityEditor +#define HDR_layNetTracerConnectivityEditor + +#include "ui_NetTracerConnectivityEditor.h" + +#include "dbNetTracer.h" +#include "dbNetTracerIO.h" +#include "dbTechnology.h" + +#include "layNetTracerConfig.h" +#include "layBrowser.h" +#include "layPlugin.h" +#include "layViewObject.h" +#include "layMarker.h" +#include "layTechnology.h" + +#include "tlObject.h" + +namespace db +{ + class NetTracerConnectivity; +} + +namespace lay +{ + +class NetTracerConnectivityEditor + : public QWidget, + public Ui::NetTracerConnectivityEditor +{ +Q_OBJECT + +public: + NetTracerConnectivityEditor (QWidget *parent); + + void set_connectivity (const db::NetTracerConnectivity &data); + void get_connectivity (db::NetTracerConnectivity &); + +public slots: + void add_clicked (); + void del_clicked (); + void move_up_clicked (); + void move_down_clicked (); + void symbol_add_clicked (); + void symbol_del_clicked (); + void symbol_move_up_clicked (); + void symbol_move_down_clicked (); + +private: + db::NetTracerConnectivity m_data; + + void update (); +}; + +} + +#endif + diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index d33a14748..654212b30 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -92,7 +92,9 @@ NetTracerDialog::NetTracerDialog (lay::Dispatcher *root, LayoutViewBase *view) view->layer_list_changed_event.add (this, &NetTracerDialog::layer_list_changed); + attach_events (); update_info (); + update_list_of_stacks (); } NetTracerDialog::~NetTracerDialog () @@ -101,6 +103,69 @@ NetTracerDialog::~NetTracerDialog () clear_nets (); } +void +NetTracerDialog::attach_events () +{ + detach_from_all_events (); + + mp_view->layer_list_changed_event.add (this, &NetTracerDialog::layer_list_changed); + + db::Technologies::instance ()->technology_changed_event.add (this, &NetTracerDialog::update_list_of_stacks_with_technology); + db::Technologies::instance ()->technologies_changed_event.add (this, &NetTracerDialog::update_list_of_stacks); + + mp_view->cellviews_changed_event.add (this, &NetTracerDialog::update_list_of_stacks); + mp_view->apply_technology_event.add (this, &NetTracerDialog::update_list_of_stacks_with_cellview); +} + +void +NetTracerDialog::update_list_of_stacks_with_technology (db::Technology *) +{ + update_list_of_stacks (); +} + +void +NetTracerDialog::update_list_of_stacks_with_cellview (int) +{ + update_list_of_stacks (); +} + +void +NetTracerDialog::update_list_of_stacks () +{ + QString current_name = stack_selector->currentText (); + + std::set names; + for (unsigned int cvi = 0; cvi < mp_view->cellviews (); ++cvi) { + const db::Technology *tech = mp_view->cellview (cvi)->technology (); + if (tech) { + const db::NetTracerTechnologyComponent *tech_component = dynamic_cast (tech->component_by_name (db::net_tracer_component_name ())); + if (tech_component) { + for (auto d = tech_component->begin (); d != tech_component->end (); ++d) { + names.insert (tl::to_qstring (d->name ())); + } + } + } + } + + stack_selector->clear (); + + int current_index = 0; + int i = 0; + for (auto n = names.begin (); n != names.end (); ++n, ++i) { + if (n->isEmpty ()) { + stack_selector->addItem (tr ("(default)"), QVariant (*n)); + } else { + stack_selector->addItem (*n, QVariant (*n)); + } + if (*n == current_name) { + current_index = i; + } + } + + stack_selector->setVisible (stack_selector->count () >= 2); + stack_selector->setCurrentIndex (current_index); +} + void NetTracerDialog::clear_nets () { @@ -290,14 +355,27 @@ NetTracerDialog::get_net_tracer_setup (const lay::CellView &cv, db::NetTracerDat if (! tech) { return false; } + const db::NetTracerTechnologyComponent *tech_component = dynamic_cast (tech->component_by_name (db::net_tracer_component_name ())); if (! tech_component) { return false; } - // Set up the net tracer environment - data = tech_component->get_tracer_data (cv->layout ()); + std::string stack_name = tl::to_string (stack_selector->itemData (stack_selector->currentIndex ()).toString ()); + const db::NetTracerConnectivity *connectivity = 0; + for (auto d = tech_component->begin (); d != tech_component->end () && ! connectivity; ++d) { + if (d->name () == stack_name) { + connectivity = d.operator-> (); + } + } + + if (! connectivity) { + return false; + } + + // Set up the net tracer environment + data = connectivity->get_tracer_data (cv->layout ()); return true; } @@ -545,6 +623,7 @@ NetTracerDialog::configure (const std::string &name, const std::string &value) update_highlights (); adjust_view (); update_info (); + update_list_of_stacks (); } return taken; @@ -1243,6 +1322,7 @@ BEGIN_PROTECTED lay::TechComponentSetupDialog dialog (this, &tech, db::net_tracer_component_name ()); if (dialog.exec ()) { *db::Technologies::instance ()->technology_by_name (tech.name ()) = tech; + update_list_of_stacks (); } END_PROTECTED diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index b777d32e7..f439f4af6 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -112,6 +112,11 @@ private: void commit (); size_t get_trace_depth (); + void attach_events (); + void update_list_of_stacks_with_technology (db::Technology *); + void update_list_of_stacks_with_cellview (int); + void update_list_of_stacks (); + void update_highlights (); void adjust_view (); void clear_markers (); diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc index 7cc918cd9..d4aa6d75a 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerPlugin.cc @@ -22,7 +22,7 @@ #include "dbNetTracerIO.h" -#include "layNetTracerIO.h" +#include "layNetTracerTechComponentEditor.h" #include "layNetTracerDialog.h" #include "layNetTracerConfig.h" diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc new file mode 100644 index 000000000..22f69020f --- /dev/null +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc @@ -0,0 +1,424 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layNetTracerTechComponentEditor.h" +#include "layNetTracerConfig.h" + +#include "layConfigurationDialog.h" +#include "laybasicConfig.h" +#include "layConverters.h" +#include "layFinder.h" +#include "layLayoutView.h" +#include "layTechSetupDialog.h" +#include "layFileDialog.h" +#include "layQtTools.h" +#include "tlExceptions.h" +#include "tlXMLWriter.h" +#include "tlUtils.h" +#include "gsiDecl.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace lay +{ + +// ----------------------------------------------------------------------------------------- +// NetTracerTechComponentColumnDelegate definition and implementation + +class NetTracerTechComponentColumnDelegate + : public QItemDelegate +{ +public: + NetTracerTechComponentColumnDelegate (QWidget *parent, db::NetTracerTechnologyComponent *data) + : QItemDelegate (parent), mp_data (data) + { + // .. nothing yet .. + } + + QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const + { + return new QLineEdit (parent); + } + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const + { + editor->setGeometry(option.rect); + } + + void setEditorData (QWidget *widget, const QModelIndex &index) const + { + QLineEdit *editor = dynamic_cast (widget); + if (editor) { + int n = index.model ()->data (index, Qt::UserRole).toInt (); + if (mp_data->size () > size_t (n)) { + if (index.column () == 0) { + std::string name = mp_data->begin () [n].name (); + editor->setText (tl::to_qstring (name)); + editor->setPlaceholderText (tr ("(default)")); + } else if (index.column () == 1) { + editor->setText (tl::to_qstring (mp_data->begin () [n].description ())); + } + } + } + } + + void setModelData (QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const + { + QLineEdit *editor = dynamic_cast (widget); + if (editor) { + + int n = model->data (index, Qt::UserRole).toInt (); + if (mp_data->size () > size_t (n)) { + + std::string text = tl::to_string (editor->text ()); + if (index.column () == 0 && text.empty ()) { + model->setData (index, QVariant (tr ("(default)")), Qt::DisplayRole); + } else { + model->setData (index, QVariant (tl::to_qstring (text)), Qt::DisplayRole); + } + + if (index.column () == 0) { + mp_data->begin () [n].set_name (text); + } else if (index.column () == 1) { + mp_data->begin () [n].set_description (text); + } + + } + + } + } + + QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QWidget *editor = createEditor (0, option, index); + QSize size = editor->sizeHint (); + delete editor; + return size - QSize (2, 2); + } + +private: + db::NetTracerTechnologyComponent *mp_data; +}; + +// ----------------------------------------------------------------------------------- +// NetTracerTechComponentEditor implementation + +NetTracerTechComponentEditor::NetTracerTechComponentEditor (QWidget *parent) + : TechnologyComponentEditor (parent) +{ + Ui::NetTracerTechComponentEditor::setupUi (this); + + QAction *action; + action = new QAction (QObject::tr ("Add Stack"), this); + connect (action, SIGNAL (triggered ()), this, SLOT (add_clicked ())); + stack_tree->addAction (action); + action = new QAction (QObject::tr ("Delete Selected Stacks"), this); + connect (action, SIGNAL (triggered ()), this, SLOT (delete_clicked ())); + stack_tree->addAction (action); + action = new QAction (QObject::tr ("Duplicate Stack"), this); + connect (action, SIGNAL (triggered ()), this, SLOT (clone_clicked ())); + stack_tree->addAction (action); + + connect (add_pb, SIGNAL (clicked ()), this, SLOT (add_clicked ())); + connect (del_pb, SIGNAL (clicked ()), this, SLOT (del_clicked ())); + connect (clone_pb, SIGNAL (clicked ()), this, SLOT (clone_clicked ())); + connect (move_up_pb, SIGNAL (clicked ()), this, SLOT (move_up_clicked ())); + connect (move_down_pb, SIGNAL (clicked ()), this, SLOT (move_down_clicked ())); + + stack_tree->header ()->setHighlightSections (false); + stack_tree->header ()->setStretchLastSection (true); + + connect (stack_tree, SIGNAL (currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT (current_item_changed(QTreeWidgetItem *, QTreeWidgetItem *))); +} + +void +NetTracerTechComponentEditor::commit () +{ + db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + commit_current (); + *data = m_data; +} + +void +NetTracerTechComponentEditor::setup () +{ + db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + m_data = *data; + + if (m_data.size () == 0) { + m_data.push_back (db::NetTracerConnectivity ()); + } + + stack_tree->setItemDelegateForColumn (0, new NetTracerTechComponentColumnDelegate (stack_tree, &m_data)); + stack_tree->setItemDelegateForColumn (1, new NetTracerTechComponentColumnDelegate (stack_tree, &m_data)); + + update (); + + if (stack_tree->topLevelItemCount () > 0) { + stack_tree->setCurrentItem (stack_tree->topLevelItem (0)); + } + current_item_changed (stack_tree->currentItem (), 0); +} + +void +NetTracerTechComponentEditor::current_item_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + commit_current (previous); + + int row = current ? stack_tree->indexOfTopLevelItem (current) : -1; + if (row < 0 || row >= int (m_data.size ())) { + connectivity_editor_widget->set_connectivity (db::NetTracerConnectivity ()); + connectivity_editor_widget->hide (); + } else { + connectivity_editor_widget->set_connectivity (m_data.begin ()[row]); + connectivity_editor_widget->show (); + } +} + +void +NetTracerTechComponentEditor::commit_current () +{ + commit_current (stack_tree->currentItem ()); +} + +void +NetTracerTechComponentEditor::commit_current (QTreeWidgetItem *current) +{ + int row = current ? stack_tree->indexOfTopLevelItem (current) : -1; + if (row >= 0 && row < int (m_data.size ())) { + connectivity_editor_widget->get_connectivity (m_data.begin () [row]); + } +} + +static std::string +new_name (const db::NetTracerTechnologyComponent &data) +{ + for (int i = 1; ; ++i) { + std::string n = "STACK" + tl::to_string (i); + bool found = false; + for (auto d = data.begin (); d != data.end () && ! found; ++d) { + found = (d->name () == n); + } + if (! found) { + return n; + } + } + + return std::string (); +} + +void +NetTracerTechComponentEditor::clone_clicked () +{ + // removes focus from the tree view - commits the data + add_pb->setFocus (); + commit_current (); + + int row = stack_tree->currentItem () ? stack_tree->indexOfTopLevelItem (stack_tree->currentItem ()) : -1; + if (row < 0) { + m_data.push_back (db::NetTracerConnectivity ()); + row = int (m_data.size () - 1); + } else { + row += 1; + m_data.insert (m_data.begin () + row, db::NetTracerConnectivity ()); + m_data.begin ()[row] = m_data.begin ()[row - 1]; + } + + m_data.begin ()[row].set_name (new_name (m_data)); + + update (); + stack_tree->setCurrentItem (stack_tree->topLevelItem (row)); +} + +void +NetTracerTechComponentEditor::add_clicked () +{ + // removes focus from the tree view - commits the data + add_pb->setFocus (); + commit_current (); + + int row = stack_tree->currentItem () ? stack_tree->indexOfTopLevelItem (stack_tree->currentItem ()) : -1; + if (row < 0) { + m_data.push_back (db::NetTracerConnectivity ()); + row = int (m_data.size () - 1); + } else { + row += 1; + m_data.insert (m_data.begin () + row, db::NetTracerConnectivity ()); + } + + m_data.begin ()[row].set_name (new_name (m_data)); + + update (); + stack_tree->setCurrentItem (stack_tree->topLevelItem (row)); +} + +void +NetTracerTechComponentEditor::del_clicked () +{ + // removes focus from the tree view - commits the data + del_pb->setFocus (); + commit_current (); + + std::set selected_rows; + QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes (); + for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) { + selected_rows.insert (i->row ()); + } + + int offset = 0; + for (std::set::const_iterator r = selected_rows.begin (); r != selected_rows.end (); ++r) { + m_data.erase (m_data.begin () + (*r - offset)); + ++offset; + } + + update (); + stack_tree->setCurrentItem (0); +} + +void +NetTracerTechComponentEditor::move_up_clicked () +{ + // removes focus from the tree view - commits the data + move_up_pb->setFocus (); + commit_current (); + + std::set selected_rows; + QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes (); + for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) { + selected_rows.insert (i->row ()); + } + + QTreeWidgetItem *current = stack_tree->currentItem (); + int n_current = current ? current->data (0, Qt::UserRole).toInt () : -1; + + stack_tree->setCurrentIndex (QModelIndex ()); + + int n = 0; + for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { + if (selected_rows.find (n + 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { + std::swap (m_data.begin () [n + 1], m_data.begin () [n]); + selected_rows.erase (n + 1); + selected_rows.insert (n); + if (n_current == n + 1) { + n_current = n; + } + } + } + + update (); + + // select the new items + for (std::set ::const_iterator s = selected_rows.begin (); s != selected_rows.end (); ++s) { + stack_tree->topLevelItem (*s)->setSelected (true); + } + if (n_current >= 0) { + stack_tree->setCurrentItem (stack_tree->topLevelItem (n_current), 0, QItemSelectionModel::Current); + } +} + +void +NetTracerTechComponentEditor::move_down_clicked () +{ + // removes focus from the tree view - commits the data + move_down_pb->setFocus (); + commit_current (); + + std::set selected_rows; + QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes (); + for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) { + selected_rows.insert (i->row ()); + } + + QTreeWidgetItem *current = stack_tree->currentItem (); + int n_current = current ? current->data (0, Qt::UserRole).toInt () : -1; + + stack_tree->setCurrentIndex (QModelIndex ()); + + int n = int (m_data.size ()); + for (db::NetTracerTechnologyComponent::iterator l = m_data.end (); l != m_data.begin (); ) { + --l; + --n; + if (selected_rows.find (n - 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) { + std::swap (m_data.begin () [n - 1], m_data.begin () [n]); + selected_rows.erase (n - 1); + selected_rows.insert (n); + if (n_current == n - 1) { + n_current = n; + } + } + } + + update (); + + // select the new items + for (std::set ::const_iterator s = selected_rows.begin (); s != selected_rows.end (); ++s) { + stack_tree->topLevelItem (*s)->setSelected (true); + } + if (n_current >= 0) { + stack_tree->setCurrentItem (stack_tree->topLevelItem (n_current), 0, QItemSelectionModel::Current); + } +} + +void +NetTracerTechComponentEditor::update () +{ + stack_tree->clear (); + stack_tree->clearSelection (); + + int n = 0; + for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) { + + QTreeWidgetItem *item = new QTreeWidgetItem (stack_tree); + item->setFlags (item->flags () | Qt::ItemIsEditable); + + std::string name = l->name (); + if (name.empty ()) { + item->setData (0, Qt::DisplayRole, QVariant (tr ("(default)"))); + } else { + item->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (name))); + } + item->setData (0, Qt::UserRole, QVariant (n)); + + item->setData (1, Qt::DisplayRole, QVariant (tl::to_qstring (l->description ()))); + item->setData (1, Qt::UserRole, QVariant (n)); + + } +} + +} + diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h similarity index 85% rename from src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h rename to src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h index fcb48e989..df7a60cc7 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerIO.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h @@ -22,8 +22,8 @@ -#ifndef HDR_layNetTracerIO -#define HDR_layNetTracerIO +#ifndef HDR_layNetTracerTechComponentEditor +#define HDR_layNetTracerTechComponentEditor #include "ui_NetTracerTechComponentEditor.h" @@ -48,8 +48,6 @@ namespace db namespace lay { -class FileDialog; - class NetTracerTechComponentEditor : public lay::TechnologyComponentEditor, public Ui::NetTracerTechComponentEditor @@ -64,18 +62,18 @@ public: public slots: void add_clicked (); + void clone_clicked (); void del_clicked (); void move_up_clicked (); void move_down_clicked (); - void symbol_add_clicked (); - void symbol_del_clicked (); - void symbol_move_up_clicked (); - void symbol_move_down_clicked (); + void current_item_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous); private: db::NetTracerTechnologyComponent m_data; void update (); + void commit_current (QTreeWidgetItem *current); + void commit_current (); }; } diff --git a/src/plugins/tools/net_tracer/lay_plugin/lay_plugin.pro b/src/plugins/tools/net_tracer/lay_plugin/lay_plugin.pro index 7f05e1cc4..1216f559a 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/net_tracer/lay_plugin/lay_plugin.pro @@ -14,17 +14,20 @@ LIBS += -L$$DESTDIR/../db_plugins -lnet_tracer HEADERS = \ layNetTracerConfig.h \ + layNetTracerConnectivityEditor.h \ layNetTracerDialog.h \ - layNetTracerIO.h \ + layNetTracerTechComponentEditor.h SOURCES = \ layNetTracerConfig.cc \ + layNetTracerConnectivityEditor.cc \ layNetTracerDialog.cc \ layNetTracerPlugin.cc \ - layNetTracerIO.cc \ + layNetTracerTechComponentEditor.cc FORMS = \ NetTracerConfigPage.ui \ + NetTracerConnectivityEditor.ui \ NetTracerDialog.ui \ NetTracerTechComponentEditor.ui \ diff --git a/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc b/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc index 52375e61f..e0fa275a5 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc @@ -76,21 +76,21 @@ static db::NetTracerShape find_shape (const db::Layout &layout, const db::Cell & } #endif -static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerConnectivity &tc, unsigned int l_start, const db::Point &p_start) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, tracer_data); return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } -static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerConnectivity &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, p_stop, l_stop, tracer_data); return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } -void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerTechnologyComponent &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const std::string &file_au, const char *net_name = 0, size_t depth = 0) +void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerConnectivity &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const std::string &file_au, const char *net_name = 0, size_t depth = 0) { db::Manager m (false); @@ -130,7 +130,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer db::compare_layouts (_this, layout_net, fn, db::WriteOAS); } -void run_test2 (tl::TestBase *_this, const std::string &file, const db::NetTracerTechnologyComponent &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const db::LayerProperties &lp_stop, const db::Point &p_stop, const std::string &file_au, const char *net_name = 0) +void run_test2 (tl::TestBase *_this, const std::string &file, const db::NetTracerConnectivity &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const db::LayerProperties &lp_stop, const db::Point &p_stop, const std::string &file_au, const char *net_name = 0) { db::Manager m (false); @@ -169,7 +169,7 @@ TEST(1) std::string file = "t1.oas.gz"; std::string file_au = "t1_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -180,7 +180,7 @@ TEST(1b) std::string file = "t1.oas.gz"; std::string file_au = "t1b_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); // point is off net ... @@ -192,7 +192,7 @@ TEST(1c) std::string file = "t1.oas.gz"; std::string file_au = "t1_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add_symbol (symbol ("a", "1/0")); tc.add_symbol (symbol ("c", "cc")); tc.add_symbol (symbol ("cc", "3/0")); @@ -206,7 +206,7 @@ TEST(1d) std::string file = "t1.oas.gz"; std::string file_au = "t1d_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "10/0", "11/0")); // some layers are non-existing @@ -218,7 +218,7 @@ TEST(2) std::string file = "t2.oas.gz"; std::string file_au = "t2_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); run_test2 (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), db::LayerProperties (3, 0), db::Point (4000, -20000), file_au, "THE_NAME"); @@ -229,7 +229,7 @@ TEST(3) std::string file = "t3.oas.gz"; std::string file_au = "t3_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); std::string msg; @@ -246,7 +246,7 @@ TEST(4) std::string file = "t4.oas.gz"; std::string file_au = "t4_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, ""); @@ -257,7 +257,7 @@ TEST(4b) std::string file = "t4.oas.gz"; std::string file_au = "t4b_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -268,7 +268,7 @@ TEST(5) std::string file = "t5.oas.gz"; std::string file_au = "t5_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0*10/0", "2/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -279,7 +279,7 @@ TEST(5b) std::string file = "t5.oas.gz"; std::string file_au = "t5b_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0*10/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -290,7 +290,7 @@ TEST(5c) std::string file = "t5.oas.gz"; std::string file_au = "t5c_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0-11/0", "3/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, ""); @@ -301,7 +301,7 @@ TEST(5d) std::string file = "t5.oas.gz"; std::string file_au = "t5d_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0-12/0", "2/0", "3/0-12/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -312,7 +312,7 @@ TEST(5e) std::string file = "t5.oas.gz"; std::string file_au = "t5e_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0-12/0", "2/0", "3/0-12/0")); run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (7000, 1500), file_au, "THE_NAME"); @@ -323,7 +323,7 @@ TEST(5f) std::string file = "t5.oas.gz"; std::string file_au = "t5f_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add_symbol (symbol ("x", "3-14")); tc.add (connection ("10-13", "x")); tc.add (connection ("x", "2", "1+13")); @@ -336,7 +336,7 @@ TEST(6) std::string file = "t6.oas.gz"; std::string file_au = "t6_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1-10", "2", "3")); tc.add (connection ("3", "4", "5")); @@ -348,7 +348,7 @@ TEST(6b) std::string file = "t6.oas.gz"; std::string file_au = "t6b_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1-10", "2", "3")); tc.add (connection ("3", "4", "5")); @@ -360,7 +360,7 @@ TEST(7) std::string file = "t7.oas.gz"; std::string file_au = "t7_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("15", "14", "2-7")); tc.add (connection ("15", "14", "7")); @@ -373,7 +373,7 @@ TEST(8) std::string file = "t8.oas.gz"; std::string file_au = "t8_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("15", "14", "7")); run_test (_this, file, tc, db::LayerProperties (15, 0), db::Point (4000, 10000), file_au, ""); @@ -384,7 +384,7 @@ TEST(9) std::string file = "t9.oas.gz"; std::string file_au = "t9_net.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add_symbol (symbol ("a", "8-12")); tc.add_symbol (symbol ("b", "a+7")); tc.add_symbol (symbol ("c", "15*26")); diff --git a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc index 53b674871..2ed796f0c 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc @@ -50,7 +50,7 @@ static db::NetTracerSymbolInfo symbol (const std::string &s, const std::string & return db::NetTracerSymbolInfo (s, e); } -void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerTechnologyComponent &tc, const std::string &file_au) +void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerConnectivity &tc, const std::string &file_au) { db::Manager m (false); @@ -93,7 +93,7 @@ TEST(1) std::string file = "t1.oas.gz"; std::string file_au = "t1_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); run_test (_this, file, tc, file_au); @@ -104,7 +104,7 @@ TEST(1c) std::string file = "t1.oas.gz"; std::string file_au = "t1_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add_symbol (symbol ("a", "1/0")); tc.add_symbol (symbol ("c", "cc")); tc.add_symbol (symbol ("cc", "3/0")); @@ -118,7 +118,7 @@ TEST(1d) std::string file = "t1.oas.gz"; std::string file_au = "t1d_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "10/0", "11/0")); // some layers are non-existing @@ -130,7 +130,7 @@ TEST(4) std::string file = "t4.oas.gz"; std::string file_au = "t4_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0", "3/0")); run_test (_this, file, tc, file_au); @@ -141,7 +141,7 @@ TEST(4b) std::string file = "t4.oas.gz"; std::string file_au = "t4b_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "3/0")); run_test (_this, file, tc, file_au); @@ -152,7 +152,7 @@ TEST(5) std::string file = "t5.oas.gz"; std::string file_au = "t5_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0*10/0", "2/0", "3/0")); run_test (_this, file, tc, file_au); @@ -163,7 +163,7 @@ TEST(5b) std::string file = "t5.oas.gz"; std::string file_au = "t5b_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0*10/0", "3/0")); run_test (_this, file, tc, file_au); @@ -174,7 +174,7 @@ TEST(5c) std::string file = "t5.oas.gz"; std::string file_au = "t5c_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0", "2/0-11/0", "3/0")); run_test (_this, file, tc, file_au); @@ -185,7 +185,7 @@ TEST(5d) std::string file = "t5.oas.gz"; std::string file_au = "t5d_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1/0-12/0", "2/0", "3/0-12/0")); run_test (_this, file, tc, file_au); @@ -196,7 +196,7 @@ TEST(5f) std::string file = "t5.oas.gz"; std::string file_au = "t5f_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add_symbol (symbol ("x", "3-14")); tc.add (connection ("10-13", "x")); tc.add (connection ("x", "2", "1+13")); @@ -209,7 +209,7 @@ TEST(6) std::string file = "t6.oas.gz"; std::string file_au = "t6_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("1-10", "2", "3")); tc.add (connection ("3", "4", "5")); @@ -221,7 +221,7 @@ TEST(7) std::string file = "t7.oas.gz"; std::string file_au = "t7_all_nets.oas.gz"; - db::NetTracerTechnologyComponent tc; + db::NetTracerConnectivity tc; tc.add (connection ("15", "14", "2-7")); tc.add (connection ("15", "14", "7")); diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 5ac851c82..35bc1cd49 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -71,8 +71,8 @@ ... - - :/run.png:/run.png + + :/run_16px.png:/run_16px.png true @@ -260,7 +260,7 @@ ... - + :/fit_left_24px.png:/fit_left_24px.png @@ -280,7 +280,7 @@ ... - + :/fit_front_24px.png:/fit_front_24px.png @@ -300,7 +300,7 @@ ... - + :/fit_right_24px.png:/fit_right_24px.png @@ -320,7 +320,7 @@ ... - + :/fit_back_24px.png:/fit_back_24px.png @@ -340,7 +340,7 @@ ... - + :/fit_top_24px.png:/fit_top_24px.png @@ -360,7 +360,7 @@ ... - + :/fit_bottom_24px.png:/fit_bottom_24px.png @@ -628,7 +628,7 @@ See here for more information: <a href="int:/about/25d_view.xml">
    - + diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index e42c3d02b..920adbe74 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -248,42 +248,38 @@ ItemRefUnwrappingIterator category_items_end (const rdb::Category *cat) return cat->database ()->items_by_category (cat->id ()).second; } -static void scan_layer1 (rdb::Category *cat, const db::Layout &layout, unsigned int layer) +static void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell, int levels, bool with_properties) { - rdb::scan_layer (cat, layout, layer); + rdb::scan_layer (cat, layout, layer, from_cell, levels, with_properties); } -static void scan_layer2 (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell) +static void scan_shapes (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat, bool with_properties) { - rdb::scan_layer (cat, layout, layer, from_cell); + rdb::scan_layer (cat, iter, flat, with_properties); } -static void scan_layer3 (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell, int levels) -{ - rdb::scan_layer (cat, layout, layer, from_cell, levels); -} - -static void scan_shapes (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat) -{ - rdb::scan_layer (cat, iter, flat); -} - -static void scan_region (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Region ®ion, bool flat) +static void scan_region (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Region ®ion, bool flat, bool with_properties) { std::pair it = region.begin_iter (); - rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat, with_properties); } -static void scan_edges (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Edges &edges, bool flat) +static void scan_edges (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Edges &edges, bool flat, bool with_properties) { std::pair it = edges.begin_iter (); - rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat, with_properties); } -static void scan_edge_pairs (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::EdgePairs &edge_pairs, bool flat) +static void scan_edge_pairs (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::EdgePairs &edge_pairs, bool flat, bool with_properties) { std::pair it = edge_pairs.begin_iter (); - rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat, with_properties); +} + +static void scan_texts (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Texts &texts, bool flat, bool with_properties) +{ + std::pair it = texts.begin_iter (); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat, with_properties); } Class decl_RdbCategory ("rdb", "RdbCategory", @@ -303,7 +299,7 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "\n" "This method has been introduced in version 0.23." ) + - gsi::method_ext ("scan_shapes", &scan_shapes, gsi::arg ("iter"), gsi::arg ("flat", false), + gsi::method_ext ("scan_shapes", &scan_shapes, gsi::arg ("iter"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Scans the polygon or edge shapes from the shape iterator into the category\n" "Creates RDB items for each polygon or edge shape read from the iterator and puts them into this category.\n" "A similar, but lower-level method is \\ReportDatabase#create_items with a \\RecursiveShapeIterator argument.\n" @@ -311,9 +307,11 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "if the \\flat argument is false. In this case, the hierarchy the recursive shape iterator traverses is " "copied into the report database using sample references.\n" "\n" - "This method has been introduced in version 0.23. The flat mode argument has been added in version 0.26.\n" + "If 'with_properties' is true, user properties will be turned into tagged values as well.\n" + "\n" + "This method has been introduced in version 0.23. The flat mode argument has been added in version 0.26. The 'with_properties' argument has been added in version 0.28.\n" ) + - gsi::method_ext ("scan_collection", &scan_region, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("region"), gsi::arg ("flat", false), + gsi::method_ext ("scan_collection", &scan_region, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("region"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Turns the given region into a hierarchical or flat report database\n" "The exact behavior depends on the nature of the region. If the region is a hierarchical (original or deep) region " "and the 'flat' argument is false, this method will produce a hierarchical report database in the given category. " @@ -325,44 +323,38 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "\n" "The transformation argument needs to supply the dbu-to-micron transformation.\n" "\n" - "This method has been introduced in version 0.26.\n" + "If 'with_properties' is true, user properties will be turned into tagged values as well.\n" + "\n" + "This method has been introduced in version 0.26. The 'with_properties' argument has been added in version 0.28.\n" ) + - gsi::method_ext ("scan_collection", &scan_edges, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edges"), gsi::arg ("flat", false), + gsi::method_ext ("scan_collection", &scan_edges, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edges"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Turns the given edge collection into a hierarchical or flat report database\n" "This a another flavour of \\scan_collection accepting an edge collection.\n" "\n" - "This method has been introduced in version 0.26.\n" + "This method has been introduced in version 0.26. The 'with_properties' argument has been added in version 0.28.\n" ) + - gsi::method_ext ("scan_collection", &scan_edge_pairs, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), gsi::arg ("flat", false), + gsi::method_ext ("scan_collection", &scan_edge_pairs, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Turns the given edge pair collection into a hierarchical or flat report database\n" "This a another flavour of \\scan_collection accepting an edge pair collection.\n" "\n" - "This method has been introduced in version 0.26.\n" + "This method has been introduced in version 0.26. The 'with_properties' argument has been added in version 0.28.\n" ) + - gsi::method_ext ("scan_layer", &scan_layer1, gsi::arg ("layout"), gsi::arg ("layer"), - "@brief Scans a layer from a layout into this category\n" - "Creates RDB items for each polygon or edge shape read from the each cell in the layout on the given layer and puts them into this category.\n" - "New cells will be generated for every cell encountered in the layout.\n" - "Other settings like database unit, description, top cell etc. are not made in the RDB.\n" + gsi::method_ext ("scan_collection", &scan_texts, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("texts"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), + "@brief Turns the given edge pair collection into a hierarchical or flat report database\n" + "This a another flavour of \\scan_collection accepting a text collection.\n" "\n" - "This method has been introduced in version 0.23.\n" + "This method has been introduced in version 0.28.\n" ) + - gsi::method_ext ("scan_layer", &scan_layer2, gsi::arg ("layout"), gsi::arg ("layer"), gsi::arg ("cell"), - "@brief Scans a layer from a layout into this category, starting with a given cell\n" - "Creates RDB items for each polygon or edge shape read from the cell and it's children in the layout on the given layer and puts them into this category.\n" - "New cells will be generated when required.\n" - "Other settings like database unit, description, top cell etc. are not made in the RDB.\n" - "\n" - "This method has been introduced in version 0.23.\n" - ) + - gsi::method_ext ("scan_layer", &scan_layer3, gsi::arg ("layout"), gsi::arg ("layer"), gsi::arg ("cell"), gsi::arg ("levels"), + gsi::method_ext ("scan_layer", &scan_layer, gsi::arg ("layout"), gsi::arg ("layer"), gsi::arg ("cell", (const db::Cell *) 0, "nil"), gsi::arg ("levels", -1), gsi::arg ("with_properties", true), "@brief Scans a layer from a layout into this category, starting with a given cell and a depth specification\n" "Creates RDB items for each polygon or edge shape read from the cell and it's children in the layout on the given layer and puts them into this category.\n" "New cells will be generated when required.\n" "\"levels\" is the number of hierarchy levels to take the child cells from. 0 means to use only \"cell\" and don't descend, -1 means \"all levels\".\n" "Other settings like database unit, description, top cell etc. are not made in the RDB.\n" "\n" - "This method has been introduced in version 0.23.\n" + "If 'with_properties' is true, user properties will be turned into tagged values as well.\n" + "\n" + "This method has been introduced in version 0.23. The 'with_properties' argument has been added in version 0.28.\n" ) + gsi::method ("name", &rdb::Category::name, "@brief Gets the category name\n" @@ -1209,7 +1201,7 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This convenience method has been added in version 0.25.\n" ) + - gsi::method_ext ("create_items", &rdb::create_items_from_iterator, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("iter"), + gsi::method_ext ("create_items", &rdb::create_items_from_iterator, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("iter"), gsi::arg ("with_properties", true), "@brief Creates new items from a shape iterator\n" "This method takes the shapes from the given iterator and produces items from them.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " @@ -1217,41 +1209,44 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "A similar method, which is intended for production of polygon or edge error layers and also provides hierarchical database " "construction is \\RdbCategory#scan_shapes.\n" "\n" - "This method has been introduced in version 0.25.3.\n" + "This method has been introduced in version 0.25.3. The 'with_properties' argument has been added in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param iter The iterator (a \\RecursiveShapeIterator object) from which to take the items\n" + "@param with_properties If true, user properties will be turned into tagged values as well\n" ) + - gsi::method_ext ("create_item", &rdb::create_item_from_shape, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shape"), + gsi::method_ext ("create_item", &rdb::create_item_from_shape, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shape"), gsi::arg ("with_properties", true), "@brief Creates a new item from a single shape\n" "This method produces an item from the given shape.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " "converts them to a corresponding item. The transformation argument can be used to " "supply the transformation that applies the database unit for example.\n" "\n" - "This method has been introduced in version 0.25.3.\n" + "This method has been introduced in version 0.25.3. The 'with_properties' argument has been added in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param shape The shape to take the geometrical object from\n" "@param trans The transformation to apply\n" + "@param with_properties If true, user properties will be turned into tagged values as well\n" ) + - gsi::method_ext ("create_items", &rdb::create_items_from_shapes, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shapes"), + gsi::method_ext ("create_items", &rdb::create_items_from_shapes, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shapes"), gsi::arg ("with_properties", true), "@brief Creates new items from a shape container\n" "This method takes the shapes from the given container and produces items from them.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " "converts them to corresponding items. The transformation argument can be used to " "supply the transformation that applies the database unit for example.\n" "\n" - "This method has been introduced in version 0.25.3.\n" + "This method has been introduced in version 0.25.3. The 'with_properties' argument has been added in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param shapes The shape container from which to take the items\n" "@param trans The transformation to apply\n" + "@param with_properties If true, user properties will be turned into tagged values as well\n" ) + - gsi::method_ext ("create_items", &rdb::create_items_from_region, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("region"), + gsi::method_ext ("#create_items", &rdb::create_items_from_region, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("region"), "@brief Creates new polygon items for the given cell/category combination\n" "For each polygon in the region a single item will be created. The value of the item will be this " "polygon.\n" @@ -1262,14 +1257,14 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\\RdbCategory#scan_collection is a similar method which also supports construction of " "hierarchical databases from deep regions.\n" "\n" - "This method has been introduced in version 0.23.\n" + "This method has been introduced in version 0.23. It has been deprecated in favor of \\RdbCategory#scan_collection in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param region The region (a \\Region object) containing the polygons for which to create items\n" ) + - gsi::method_ext ("create_items", &rdb::create_items_from_edges, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edges"), + gsi::method_ext ("#create_items", &rdb::create_items_from_edges, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edges"), "@brief Creates new edge items for the given cell/category combination\n" "For each edge a single item will be created. The value of the item will be this " "edge.\n" @@ -1280,14 +1275,14 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\\RdbCategory#scan_collection is a similar method which also supports construction of " "hierarchical databases from deep edge collections.\n" "\n" - "This method has been introduced in version 0.23.\n" + "This method has been introduced in version 0.23. It has been deprecated in favor of \\RdbCategory#scan_collection in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param edges The list of edges (an \\Edges object) for which the items are created\n" ) + - gsi::method_ext ("create_items", &rdb::create_items_from_edge_pairs, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), + gsi::method_ext ("#create_items", &rdb::create_items_from_edge_pairs, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), "@brief Creates new edge pair items for the given cell/category combination\n" "For each edge pair a single item will be created. The value of the item will be this " "edge pair.\n" @@ -1298,7 +1293,7 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\\RdbCategory#scan_collection is a similar method which also supports construction of " "hierarchical databases from deep edge pair collections.\n" "\n" - "This method has been introduced in version 0.23.\n" + "This method has been introduced in version 0.23. It has been deprecated in favor of \\RdbCategory#scan_collection in version 0.28.\n" "\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 26eaafc70..9bf139de9 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -860,9 +860,11 @@ public: * @brief Add a value in a generic way */ template - void add_value (const V &v) + ValueBase *add_value (const V &v, id_type tag_id = 0) { - values ().add (new Value (v)); + ValueBase *value = new Value (v); + values ().add (value, tag_id); + return value; } /** diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 15c1e1bc8..c2c281fd3 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -34,7 +34,7 @@ namespace rdb { void -scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from, int levels) +scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from, int levels, bool with_properties) { rdb::Database *rdb = cat->database (); if (! rdb) { @@ -78,7 +78,7 @@ scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, co } - create_items_from_shapes (rdb, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), cell.shapes (layer)); + create_items_from_shapes (rdb, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), cell.shapes (layer), with_properties); } @@ -92,8 +92,8 @@ class CreateItemsRecursiveReceiver : public db::RecursiveShapeReceiver { public: - CreateItemsRecursiveReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell) - : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell) + CreateItemsRecursiveReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell, bool with_properties) + : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell), m_with_properties (with_properties) { // just in case the iterator is non-hierarchical if (cell) { @@ -159,7 +159,7 @@ public: virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { tl_assert (! m_cell_stack.empty ()); - create_item_from_shape (mp_rdb, m_cell_stack.back ()->id (), mp_cat->id (), m_trans, shape); + create_item_from_shape (mp_rdb, m_cell_stack.back ()->id (), mp_cat->id (), m_trans, shape, m_with_properties); } public: @@ -169,6 +169,7 @@ public: std::map m_id_to_cell; db::CplxTrans m_trans; rdb::Cell *mp_rdb_cell; + bool m_with_properties; const rdb::Cell *cell_for_id (const db::Layout *layout, db::cell_index_type ci) { @@ -187,8 +188,8 @@ class CreateItemsFlatReceiver : public db::RecursiveShapeReceiver { public: - CreateItemsFlatReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell) - : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell) + CreateItemsFlatReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell, bool with_properties) + : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell), m_with_properties (with_properties) { // .. nothing yet .. } @@ -211,7 +212,7 @@ public: virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { - create_item_from_shape (mp_rdb, mp_rdb_cell->id (), mp_cat->id (), m_trans * trans, shape); + create_item_from_shape (mp_rdb, mp_rdb_cell->id (), mp_cat->id (), m_trans * trans, shape, m_with_properties); } public: @@ -219,23 +220,24 @@ public: rdb::Database *mp_rdb; db::CplxTrans m_trans; const rdb::Cell *mp_rdb_cell; + bool m_with_properties; }; } void -scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat) +scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat, bool with_properties) { if (! iter.top_cell () || ! iter.layout ()) { return; } db::CplxTrans trans (iter.layout ()->dbu ()); - scan_layer (cat, 0, trans, iter, flat); + scan_layer (cat, 0, trans, iter, flat, with_properties); } void -scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat) +scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat, bool with_properties) { if (! cat->database ()) { return; @@ -243,9 +245,9 @@ scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, con std::unique_ptr rec; if (flat) { - rec.reset (new CreateItemsFlatReceiver (cat, trans, cell)); + rec.reset (new CreateItemsFlatReceiver (cat, trans, cell, with_properties)); } else { - rec.reset (new CreateItemsRecursiveReceiver (cat, trans, cell)); + rec.reset (new CreateItemsRecursiveReceiver (cat, trans, cell, with_properties)); } db::RecursiveShapeIterator (iter).push (rec.get ()); @@ -253,37 +255,47 @@ scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, con // ------------------------------------------------------------------------------------------------------------ -void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter) +void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter, bool with_properties) { tl_assert (iter.layout ()); double dbu = iter.layout ()->dbu (); for (db::RecursiveShapeIterator i = iter; !i.at_end (); ++i) { - std::unique_ptr value (rdb::ValueBase::create_from_shape (*i, db::CplxTrans (dbu) * i.trans ())); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); - } + create_item_from_shape (db, cell_id, cat_id, db::CplxTrans (dbu) * i.trans (), *i, with_properties); } } -void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes) +void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes, bool with_properties) { for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); !s.at_end (); ++s) { - std::unique_ptr value (rdb::ValueBase::create_from_shape (*s, trans)); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); - } + create_item_from_shape (db, cell_id, cat_id, trans, *s, with_properties); } } -void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape) +void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape, bool with_properties) { std::unique_ptr value (rdb::ValueBase::create_from_shape (shape, trans)); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); + if (! value.get ()) { + return; + } + + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (value.release ()); + + // translate properties + if (with_properties && shape.has_prop_id () && shape.shapes () && shape.shapes ()->cell ()) { + + const db::Layout *layout = shape.shapes ()->cell ()->layout (); + if (layout) { + + auto ps = layout->properties_repository ().properties (shape.prop_id ()); + for (auto i = ps.begin (); i != ps.end (); ++i) { + id_type tag_id = db->tags ().tag (layout->properties_repository ().prop_name (i->first).to_string (), true /*user tag*/).id (); + add_item_value (item, i->second, trans, tag_id); + } + + } + } } @@ -317,50 +329,101 @@ void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb: } } -void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu) +ValueBase *add_item_value (rdb::Item *item, const tl::Variant &v, const db::CplxTrans &trans, rdb::id_type tag_id) { - if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); - } else if (dbu > 0 && v.is_user ()) { - db::DPoint p = db::CplxTrans (dbu) * v.to_user (); - item->add_value (db::DEdge (p, p)); - } else if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); - } else if (dbu > 0 && v.is_user ()) { + if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); + } else if (v.is_user ()) { + db::DPoint p = trans * v.to_user (); + return item->add_value (db::DEdge (p, p), tag_id); + } else if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); + } else if (v.is_user ()) { db::DPolygon p; - db::DSimplePolygon sp = db::CplxTrans (dbu) * v.to_user (); + db::DSimplePolygon sp = trans * v.to_user (); p.assign_hull (sp.begin_hull (), sp.end_hull ()); - item->add_value (p); - } else if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); - } else if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); - } else if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); - } else if (dbu > 0 && v.is_user ()) { - item->add_value (db::CplxTrans (dbu) * v.to_user ()); + return item->add_value (p, tag_id); + } else if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (trans * v.to_user (), tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); } else if (v.is_user ()) { db::DPoint p = v.to_user (); - item->add_value (db::DEdge (p, p)); + return item->add_value (db::DEdge (p, p), tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); } else if (v.is_user ()) { db::DPolygon p; db::DSimplePolygon sp = v.to_user (); p.assign_hull (sp.begin_hull (), sp.end_hull ()); - item->add_value (p); + return item->add_value (p, tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); } else if (v.is_user ()) { - item->add_value (v.to_user ()); + return item->add_value (v.to_user (), tag_id); + } else if (v.is_double () || v.is_long () || v.is_ulong () || v.is_longlong () || v.is_ulonglong ()) { + return item->add_value (v.to_double (), tag_id); } else { - item->add_value (std::string (v.to_string ())); + return item->add_value (std::string (v.to_string ()), tag_id); + } +} + +ValueBase *add_item_value(rdb::Item *item, const tl::Variant &v, double dbu, rdb::id_type tag_id) +{ + if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (dbu > 0 && v.is_user ()) { + db::DPoint p = db::CplxTrans (dbu) * v.to_user (); + return item->add_value (db::DEdge (p, p), tag_id); + } else if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (dbu > 0 && v.is_user ()) { + db::DPolygon p; + db::DSimplePolygon sp = db::CplxTrans (dbu) * v.to_user (); + p.assign_hull (sp.begin_hull (), sp.end_hull ()); + return item->add_value (p, tag_id); + } else if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (dbu > 0 && v.is_user ()) { + return item->add_value (db::CplxTrans (dbu) * v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_user ()) { + db::DPoint p = v.to_user (); + return item->add_value (db::DEdge (p, p), tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_user ()) { + db::DPolygon p; + db::DSimplePolygon sp = v.to_user (); + p.assign_hull (sp.begin_hull (), sp.end_hull ()); + return item->add_value (p, tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_user ()) { + return item->add_value (v.to_user (), tag_id); + } else if (v.is_double () || v.is_long () || v.is_ulong () || v.is_longlong () || v.is_ulonglong ()) { + return item->add_value (v.to_double (), tag_id); + } else { + return item->add_value (std::string (v.to_string ()), tag_id); } } diff --git a/src/rdb/rdb/rdbUtils.h b/src/rdb/rdb/rdbUtils.h index 32530e2f4..99135cbac 100644 --- a/src/rdb/rdb/rdbUtils.h +++ b/src/rdb/rdb/rdbUtils.h @@ -51,45 +51,63 @@ namespace rdb * * If "from" is 0, all cells will be scanned. Levels are the number of hierarchy levels scanned if * "from" is given. -1 means "all levels". + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell = 0, int levels = -1); +RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell = 0, int levels = -1, bool with_properties = true); /** * @brief Scans a recursive shape iterator into a RDB category + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat = false); +RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat = false, bool with_properties = true); /** * @brief Scans a recursive shape iterator into a RDB category * * This version allows supplying a cell and a transformation. With this information, the function can also handle * pseudo-iterators which don't deliver the information from a layout from from a plain shape collection. + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat = false); +RDB_PUBLIC void scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat = false, bool with_properties = true); /** * @brief Creates RDB items from a recursive shape iterator * * This function will produce items from the flattened shape iterator. The items will be stored under * the given cell. + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter); +RDB_PUBLIC void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter, bool with_properties = true); /** * @brief Creates RDB items from a shape collection * * An arbitrary transformation can be applied to translate the shapes before turning them to items. * This transformation is useful for providing the DBU-to-micron conversion. + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes); +RDB_PUBLIC void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes, bool with_properties = true); /** * @brief Creates RDB items from a single shape * * An arbitrary transformation can be applied to translate the shapes before turning them to items. * This transformation is useful for providing the DBU-to-micron conversion. + * + * If "with_properties" is true, user properties are translated into values with tags corresponding + * to the property names. */ -RDB_PUBLIC void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape); +RDB_PUBLIC void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape, bool with_properties = true); /** * @brief Creates RDB items from a region @@ -146,7 +164,14 @@ RDB_PUBLIC_TEMPLATE void create_items_from_sequence (rdb::Database *db, rdb::id_ * * Unknown types are converted to strings. */ -RDB_PUBLIC void add_item_value (rdb::Item *item, const tl::Variant &v, double dbu = 0.0); +RDB_PUBLIC ValueBase *add_item_value (rdb::Item *item, const tl::Variant &v, double dbu = 0.0, rdb::id_type tag_id = 0); + +/** + * @brief Creates a value from a tl::Variant + * + * This version takes a db::CplxTrans for converting integer-unit geometry objects to micron-unit ones. + */ +RDB_PUBLIC ValueBase *add_item_value(rdb::Item *item, const tl::Variant &v, const db::CplxTrans &trans, rdb::id_type tag_id = 0); } diff --git a/src/tl/tl/tlPixelBuffer.cc b/src/tl/tl/tlPixelBuffer.cc index 0c8566ad8..4accba4df 100644 --- a/src/tl/tl/tlPixelBuffer.cc +++ b/src/tl/tl/tlPixelBuffer.cc @@ -29,6 +29,7 @@ #endif #include +#include namespace tl { @@ -358,6 +359,141 @@ PixelBuffer::diff (const PixelBuffer &other) const return res; } +void +PixelBuffer::blowup (tl::PixelBuffer &dest, unsigned int os) +{ + tl_assert (dest.width () == width () * os); + tl_assert (dest.height () == height () * os); + + unsigned int ymax = height (); + unsigned int xmax = width (); + + for (unsigned int y = 0; y < ymax; ++y) { + for (unsigned int i = 0; i < os; ++i) { + const uint32_t *psrc = (const uint32_t *) scan_line (y); + uint32_t *pdest = (uint32_t *) dest.scan_line (y * os + i); + for (unsigned int x = 0; x < xmax; ++x) { + for (unsigned int j = 0; j < os; ++j) { + *pdest++ = *psrc; + } + ++psrc; + } + } + } +} + +void +PixelBuffer::subsample (tl::PixelBuffer &dest, unsigned int os, double g) +{ + // TODO: this is probably not compatible with the endianess of SPARC .. + + // LUT's for combining the RGB channels + + // forward transformation table + unsigned short lut1[256]; + for (unsigned int i = 0; i < 256; ++i) { + double f = (65536 / (os * os)) - 1; + lut1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + pow (i / 255.0, g) * f))); + } + + // backward transformation table + unsigned char lut2[65536]; + for (unsigned int i = 0; i < 65536; ++i) { + double f = os * os * ((65536 / (os * os)) - 1); + lut2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + pow (i / f, 1.0 / g) * 255.0))); + } + + // LUT's for alpha channel + + // forward transformation table + unsigned short luta1[256]; + for (unsigned int i = 0; i < 256; ++i) { + double f = (65536 / (os * os)) - 1; + luta1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + (i / 255.0) * f))); + } + + // backward transformation table + unsigned char luta2[65536]; + for (unsigned int i = 0; i < 65536; ++i) { + double f = os * os * ((65536 / (os * os)) - 1); + luta2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + (i / f) * 255.0))); + } + + unsigned int ymax = dest.height (); + unsigned int xmax = dest.width (); + + unsigned short *buffer = new unsigned short[xmax * 4]; + + for (unsigned int y = 0; y < ymax; ++y) { + + { + + const unsigned char *psrc = (const unsigned char *) scan_line (y * os); + unsigned short *pdest = buffer; + + for (unsigned int x = 0; x < xmax; ++x) { + + pdest[0] = lut1[psrc[0]]; + pdest[1] = lut1[psrc[1]]; + pdest[2] = lut1[psrc[2]]; + pdest[3] = luta1[psrc[3]]; + psrc += 4; + + for (unsigned int j = os; j > 1; j--) { + pdest[0] += lut1[psrc[0]]; + pdest[1] += lut1[psrc[1]]; + pdest[2] += lut1[psrc[2]]; + pdest[3] += luta1[psrc[3]]; + psrc += 4; + } + + pdest += 4; + + } + + } + + for (unsigned int i = 1; i < os; ++i) { + + const unsigned char *psrc = (const unsigned char *) scan_line (y * os + i); + unsigned short *pdest = buffer; + + for (unsigned int x = 0; x < xmax; ++x) { + + for (unsigned int j = os; j > 0; j--) { + pdest[0] += lut1[psrc[0]]; + pdest[1] += lut1[psrc[1]]; + pdest[2] += lut1[psrc[2]]; + pdest[3] += luta1[psrc[3]]; + psrc += 4; + } + + pdest += 4; + + } + + } + + { + + unsigned char *pdest = (unsigned char *) dest.scan_line (y); + const unsigned short *psrc = buffer; + + for (unsigned int x = 0; x < xmax; ++x) { + *pdest++ = lut2[*psrc++]; + *pdest++ = lut2[*psrc++]; + *pdest++ = lut2[*psrc++]; + *pdest++ = luta2[*psrc++]; + } + + } + + } + + delete[] buffer; +} + + #if defined(HAVE_PNG) PixelBuffer diff --git a/src/tl/tl/tlPixelBuffer.h b/src/tl/tl/tlPixelBuffer.h index 62b6206f3..0f48d91fb 100644 --- a/src/tl/tl/tlPixelBuffer.h +++ b/src/tl/tl/tlPixelBuffer.h @@ -257,6 +257,28 @@ public: */ PixelBuffer diff (const PixelBuffer &other) const; + /** + * @brief Subsamples the image and puts the subsampled image into the destination image + * + * @param dest Where the subsampled image goes to + * @param os The subsampling factor + * @param g The gamma value for color interpolation + * + * The dimension of the destination image must be set to the corresponding fraction of + * self's dimension. + */ + void subsample (tl::PixelBuffer &dest, unsigned int os, double g); + + /** + * @brief Scales the image into the given destination image + * + * @param dest Where the scaled image goes to + * @param os The scaling factor + * + * The destination images dimension must have been set of self's dimension times os. + */ + void blowup (tl::PixelBuffer &dest, unsigned int os); + /** * @brief Gets the texts * diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 178020c61..bb2a14dad 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -256,7 +256,7 @@ std::string to_lower_case (const std::string &s) std::string to_local (const std::string &s) { - std::unique_ptr buffer (new char [MB_CUR_MAX]); // MB_CUR_MAX isn't a constant + std::unique_ptr buffer (new char [MB_CUR_MAX]); // MB_CUR_MAX isn't a constant std::string ls; std::wstring ws = to_wstring (s); diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds index cf85326dd..e5cca0f9e 100644 Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/drc/drcSimpleTests_au6.gds b/testdata/drc/drcSimpleTests_au6.gds index a1bc32508..08fada80b 100644 Binary files a/testdata/drc/drcSimpleTests_au6.gds and b/testdata/drc/drcSimpleTests_au6.gds differ diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds index 582724aac..cb37bf096 100644 Binary files a/testdata/drc/drcSimpleTests_au7.gds and b/testdata/drc/drcSimpleTests_au7.gds differ diff --git a/testdata/lvs/res_combine3.lvs b/testdata/lvs/res_combine3.lvs index e0ebec331..c609c0824 100644 --- a/testdata/lvs/res_combine3.lvs +++ b/testdata/lvs/res_combine3.lvs @@ -67,8 +67,8 @@ class ResistorExtractor < RBA::GenericDeviceExtractor device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l) device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w) device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w) - define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, 0, terminals[0]); - define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, 0, terminals[1]); + define_terminal(device, "A", "C", terminals[0]); + define_terminal(device, "B", "C", terminals[1]); end end end diff --git a/testdata/ruby/antTest.rb b/testdata/ruby/antTest.rb index 357cebe98..34677cd68 100644 --- a/testdata/ruby/antTest.rb +++ b/testdata/ruby/antTest.rb @@ -37,6 +37,19 @@ def annot_obj( p1, p2, fmt, fmt_x, fmt_y, style, outline, snap, ac ) return a end +def annot_obj2( pts, fmt, fmt_x, fmt_y, style, outline, snap, ac ) + a = RBA::Annotation::new + a.points = pts + a.fmt = fmt + a.fmt_x = fmt_x + a.fmt_y = fmt_y + a.style = style + a.outline = outline + a.snap = snap + a.angle_constraint = ac + return a +end + class RBA::Annotation def style_to_s(s) if s == StyleRuler @@ -72,6 +85,12 @@ class RBA::Annotation "diag_yx" elsif s == OutlineBox "diag_box" + elsif s == OutlineEllipse + "ellipse" + elsif s == OutlineAngle + "angle" + elsif s == OutlineRadius + "radius" else "" end @@ -119,7 +138,7 @@ class RBA::Annotation "" end end - def to_s + def to_s_debug s = "p1=" + p1.to_s + ", p2=" + p2.to_s + ", fmt=" + fmt + ", fmt_x=" + fmt_x + ", fmt_y=" + fmt_y + ", style=" + self.style_to_s(style) + ", outline=" + self.outline_to_s(outline) + ", snap=" + snap?.to_s + ", ac=" + self.ac_to_s(angle_constraint) if category != "" @@ -160,13 +179,52 @@ class Ant_TestClass < TestBase end a = RBA::Annotation::new - assert_equal( a.to_s, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) + + a.outline = RBA::Annotation::OutlineXY + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=xy, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineDiagXY + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag_xy, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineYX + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=yx, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineDiagYX + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag_yx, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineBox + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag_box, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineEllipse + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=ellipse, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineAngle + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=angle, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineRadius + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=radius, snap=true, ac=global" ) + a.outline = RBA::Annotation::OutlineDiag + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) + + a.style = RBA::Annotation::StyleArrowEnd + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=arrow_end, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleArrowStart + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=arrow_start, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleArrowBoth + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=arrow_both, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleLine + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=line, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleCrossEnd + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=cross_end, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleCrossStart + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=cross_start, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleCrossBoth + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=cross_both, outline=diag, snap=true, ac=global" ) + a.style = RBA::Annotation::StyleRuler + assert_equal( a.to_s_debug, "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + + a = annot_obj2( [ RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ) ], "a", "b", "c", 1, 2, false, 3 ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) b = a.dup - assert_equal( b.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( b.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) a = RBA::Annotation::new assert_equal( a == b, false ) @@ -177,10 +235,10 @@ class Ant_TestClass < TestBase assert_equal( a != b, false ) c = a.transformed( RBA::DTrans::new( RBA::DPoint::new( 10.0, 20.0 ) ) ) - assert_equal( c.to_s, "p1=11,22, p2=13,24, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( c.to_s_debug, "p1=11,22, p2=13,24, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) c = a.transformed_cplx( RBA::DCplxTrans::new( RBA::DTrans::new( RBA::DPoint::new( 11.0, 22.0 ) ) ) ) - assert_equal( c.to_s, "p1=12,24, p2=14,26, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( c.to_s_debug, "p1=12,24, p2=14,26, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) a.fmt = "X=$X,Y=$Y,D=$D,A=$A,P=$P,Q=$Q,U=$U,V=$V" a.fmt_x = "$(X*1000)+$(Y*1e3)" @@ -189,199 +247,199 @@ class Ant_TestClass < TestBase assert_equal( a.text_y, "c" ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.category = "abc" - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, category=abc" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, category=abc" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_position = RBA::Annotation::PositionP1 - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=p1" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=p1" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_position = RBA::Annotation::PositionP2 - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=p2" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=p2" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_position = RBA::Annotation::PositionCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_position=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_xalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_xalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_xalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_xalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_yalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_yalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.main_yalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, main_yalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_xalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_xalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_xalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_xalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_yalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_yalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.xlabel_yalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, xlabel_yalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_xalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_xalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_xalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_xalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_yalign = RBA::Annotation::AlignCenter - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=center" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=center" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_yalign = RBA::Annotation::AlignLeft - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=down" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=down" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) a = annot_obj( RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ), "a", "b", "c", 1, 2, false, 3 ) - assert_equal( a.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) aa = a.dup aa.ylabel_yalign = RBA::Annotation::AlignRight - assert_equal( aa.to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=up" ) + assert_equal( aa.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal, ylabel_yalign=up" ) assert_equal( a == aa, false ) a = aa.dup assert_equal( a == aa, true ) @@ -421,10 +479,10 @@ class Ant_TestClass < TestBase assert_equal( a.is_valid?, true ) assert_equal( lv.annotation( -1 ).is_valid?, false ) assert_equal( lv.annotation( a.id ).is_valid?, true ) - assert_equal( lv.annotation( a.id ).to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) + assert_equal( lv.annotation( a.id ).to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) id = nil @@ -445,19 +503,19 @@ class Ant_TestClass < TestBase assert_equal( asc, 1 ) arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=u, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) lv.erase_annotation(id) arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) lv.clear_annotations arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "" ) mw.close_all @@ -483,25 +541,25 @@ class Ant_TestClass < TestBase lv.insert_annotation( a ) arr = [] - lv.each_annotation { |x| arr.push( x.to_s ) } + lv.each_annotation { |x| arr.push( x.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) a.fmt = "u" arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=u, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) a.delete arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global" ) a0.delete arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "" ) a0 = RBA::Annotation::new @@ -511,19 +569,19 @@ class Ant_TestClass < TestBase lv.insert_annotation( a ) arr = [] - lv.each_annotation { |x| arr.push( x.to_s ) } + lv.each_annotation { |x| arr.push( x.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=$D, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) lv.each_annotation { |x| x.fmt = "q" } arr = [] - lv.each_annotation { |x| arr.push( x.to_s ) } + lv.each_annotation { |x| arr.push( x.to_s_debug ) } assert_equal( arr.join( ";" ), "p1=0,0, p2=0,0, fmt=q, fmt_x=$X, fmt_y=$Y, style=ruler, outline=diag, snap=true, ac=global;p1=1,2, p2=3,4, fmt=q, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal" ) lv.each_annotation { |x| x.delete } arr = [] - lv.each_annotation { |a| arr.push( a.to_s ) } + lv.each_annotation { |a| arr.push( a.to_s_debug ) } assert_equal( arr.join( ";" ), "" ) mw.close_all @@ -551,14 +609,14 @@ class Ant_TestClass < TestBase arr = [] lv.each_annotation_selected { |a| arr.push(a) } assert_equal(arr.size, 1) - assert_equal(arr[0].to_s, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") + assert_equal(arr[0].to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") arr[0].p1 = RBA::DPoint::new(11, 12) arr = [] lv.each_annotation_selected { |a| arr.push(a) } assert_equal(arr.size, 1) - assert_equal(arr[0].to_s, "p1=11,12, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") + assert_equal(arr[0].to_s_debug, "p1=11,12, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") mw.close_all @@ -601,6 +659,53 @@ class Ant_TestClass < TestBase end + # multi-segment rulers + def test_6 + + a = annot_obj2( [ RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 3.0, 4.0 ) ], "a", "b", "c", 1, 2, false, 3) + assert_equal(a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") + assert_equal(a.to_s, "id=-1,x1=1,y1=2,x2=3,y2=4,category='',fmt=a,fmt_x=b,fmt_y=c,position=auto,xalign=auto,yalign=auto,xlabel_xalign=auto,xlabel_yalign=auto,ylabel_xalign=auto,ylabel_yalign=auto,style=arrow_end,outline=diag_xy,snap=false,angle_constraint=horizontal") + aa = RBA::Annotation::from_s(a.to_s) + assert_equal(aa.to_s, a. to_s) + + a = annot_obj2( [ RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 1.5, 2.5 ), RBA::DPoint::new( 3.0, 4.0 ) ], "a", "b", "c", 1, 2, false, 3) + assert_equal(a.to_s_debug, "p1=1,2, p2=3,4, fmt=a, fmt_x=b, fmt_y=c, style=arrow_end, outline=diag_xy, snap=false, ac=horizontal") + assert_equal(a.to_s, "id=-1,pt=1:2,pt=1.5:2.5,pt=3:4,category='',fmt=a,fmt_x=b,fmt_y=c,position=auto,xalign=auto,yalign=auto,xlabel_xalign=auto,xlabel_yalign=auto,ylabel_xalign=auto,ylabel_yalign=auto,style=arrow_end,outline=diag_xy,snap=false,angle_constraint=horizontal") + assert_equal(a.segments, 2) + assert_equal(a.seg_p1(0).to_s, "1,2") + assert_equal(a.seg_p2(0).to_s, "1.5,2.5") + assert_equal(a.seg_p1(1).to_s, "1.5,2.5") + assert_equal(a.seg_p2(1).to_s, "3,4") + a.fmt_x = "$X" + a.fmt_y = "$(Y*2)" + a.fmt = "$D" + assert_equal(a.text_x(0), "0.5") + assert_equal(a.text_x(1), "1.5") + assert_equal(a.text_y(0), "1") + assert_equal(a.text_y(1), "3") + assert_equal(a.text(0), "0.70710678") + assert_equal(a.text(1), "2.1213203") + aa = RBA::Annotation::from_s(a.to_s) + assert_equal(aa.to_s, a. to_s) + + assert_equal(a.points.collect { |p| p.to_s }.join(";"), "1,2;1.5,2.5;3,4") + + a.p1 = RBA::DPoint::new(1.1, 2.1) + assert_equal(a.points.collect { |p| p.to_s }.join(";"), "1.1,2.1;1.5,2.5;3,4") + + a.p2 = RBA::DPoint::new(3.1, 4.1) + assert_equal(a.points.collect { |p| p.to_s }.join(";"), "1.1,2.1;1.5,2.5;3.1,4.1") + + a.points = [ RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 2.0, 3.0 ) ] + assert_equal(a.points.collect { |p| p.to_s }.join(";"), "1,2;2,3") + assert_equal(a.segments, 1) + + a.points = [ RBA::DPoint::new( 1.0, 2.0 ), RBA::DPoint::new( 1.0, 2.0 ) ] + assert_equal(a.points.collect { |p| p.to_s }.join(";"), "1,2") + assert_equal(a.segments, 1) + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index cc079830f..6b679975f 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -599,6 +599,14 @@ class DBLayoutTests2_TestClass < TestBase c1.each_parent_inst { |i| arr.push( i ) } assert_equal( arr.size, 1 ) + strarr = [] + c1.each_parent_inst { |i| strarr.push( i.inst.to_s ) } + assert_equal( strarr.join(";"), "#1 m45 *0.666666667 33,-67 [0,-67*10;-67,0*20]" ) + + strarr = [] + c1.each_parent_inst { |i| strarr.push( i.dinst.to_s ) } + assert_equal( strarr.join(";"), "#1 m45 *0.666666667 0.033,-0.067 [0,-0.067*10;-0.067,0*20]" ) + assert_equal( arr[0].parent_cell_index, c2.cell_index ) assert_equal( arr[0].child_inst.cell_index, c1.cell_index ) assert_equal( arr[0].inst.cell_index, c2.cell_index ) diff --git a/testdata/ruby/extNetTracer.rb b/testdata/ruby/extNetTracer.rb index fd21fa5e0..b5a0744a0 100644 --- a/testdata/ruby/extNetTracer.rb +++ b/testdata/ruby/extNetTracer.rb @@ -32,7 +32,7 @@ class EXT_TestClass < TestBase tracer = RBA::NetTracer::new - tech = RBA::NetTracerTechnology::new + tech = RBA::NetTracerConnectivity::new tech.connection("1/0", "2/0", "3/0") tracer.trace(tech, ly, ly.top_cell, RBA::Point::new(7000, 1500), ly.find_layer(1, 0)) @@ -77,7 +77,7 @@ class EXT_TestClass < TestBase tracer = RBA::NetTracer::new - tech = RBA::NetTracerTechnology::new + tech = RBA::NetTracerConnectivity::new tech.connection("1/0", "2/0", "3/0") tracer.trace(tech, ly, ly.top_cell, RBA::Point::new(7000, 1500), ly.find_layer(1, 0), RBA::Point::new(6000, -6000), ly.find_layer(3, 0)) @@ -122,7 +122,7 @@ class EXT_TestClass < TestBase tracer = RBA::NetTracer::new - tech = RBA::NetTracerTechnology::new + tech = RBA::NetTracerConnectivity::new tech.connection("1/0", "3/0") tracer.trace(tech, ly, ly.top_cell, RBA::Point::new(7000, 1500), ly.find_layer(1, 0)) diff --git a/testdata/ruby/layTechnologies.rb b/testdata/ruby/layTechnologies.rb index c19e8916d..3bbe22ecb 100644 --- a/testdata/ruby/layTechnologies.rb +++ b/testdata/ruby/layTechnologies.rb @@ -117,7 +117,7 @@ END assert_equal(tech.component_names.size > 0, true) assert_equal(tech.component_names.find("connectivity") != nil, true) - assert_equal(tech.component("connectivity").class.to_s, "RBA::NetTracerTechnology") + assert_equal(tech.component("connectivity").class.to_s, "RBA::TechnologyComponent") end diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index 07d06874b..eba883a62 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -731,14 +731,28 @@ class RDB_TestClass < TestBase c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(10, 20))) c2.insert(RBA::CellInstArray::new(c3.cell_index, RBA::Trans::new(11, 21))) l1 = ly.insert_layer(RBA::LayerInfo::new(1, 0)) - c0.shapes(l1).insert(RBA::Box::new(0, 1, 2, 3)) - c1.shapes(l1).insert(RBA::Box::new(0, 1, 20, 30)) + prop_id = ly.properties_id([[ "a", 17 ]]) + c0.shapes(l1).insert(RBA::Box::new(0, 1, 2, 3), prop_id) + prop_id = ly.properties_id([[ "a", 21 ]]) + c1.shapes(l1).insert(RBA::Box::new(0, 1, 20, 30), prop_id) c2.shapes(l1).insert(RBA::Box::new(0, 1, 21, 31)) c3.shapes(l1).insert(RBA::Box::new(0, 1, 22, 32)) rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") cat.scan_layer(ly, l1) + assert_equal(rdb.tag_name(1), "a") # from property + assert_equal(cat.num_items, 4) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "c0[];c1[];c2[];c3[]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c0[polygon: (0,0.001;0,0.003;0.002,0.003;0.002,0.001)/float: 17];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + cat.scan_layer(ly, l1, nil, -1, false) assert_equal(cat.num_items, 4) cn = [] rdb.each_cell { |c| cn << c.to_s_test } @@ -756,7 +770,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -767,7 +781,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)]") + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -778,7 +792,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -789,7 +803,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)]") + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -800,6 +814,17 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + cat.scan_shapes(c1.begin_shapes_rec(l1), false, false) # hierarchical scan + assert_equal(cat.num_items, 3) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") rdb = RBA::ReportDatabase.new("neu") @@ -811,7 +836,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "c1[]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001),polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21,polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -823,7 +848,7 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "TOP[];c1[TOP->r0 *1 0,0];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "TOP[];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + assert_equal(cn.join(";"), "TOP[];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") @@ -835,6 +860,18 @@ class RDB_TestClass < TestBase assert_equal(cn.join(";"), "TOP[]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "TOP[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21,polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + r = RBA::Region::new(c1.begin_shapes_rec(l1)) + cat.scan_collection(rdb.create_cell("TOP"), RBA::CplxTrans::new(0.001), r, true, false) # flat scan + assert_equal(cat.num_items, 3) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "TOP[]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } assert_equal(cn.join(";"), "TOP[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001),polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") rdb = RBA::ReportDatabase.new("neu")