diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui
index 7e2c2365c..87e5dc3cc 100644
--- a/src/ant/ant/RulerConfigPage4.ui
+++ b/src/ant/ant/RulerConfigPage4.ui
@@ -6,8 +6,8 @@
0
0
- 668
- 410
+ 745
+ 438
@@ -845,7 +845,12 @@
-
- Angle measurement (three mouse clicks)
+ Auto measure along edge (points will be set automatically)
+
+
+ -
+
+ Angle or radius measurement (three mouse clicks)
-
diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc
index 36576b00a..5ed7c7d0b 100644
--- a/src/ant/ant/antConfig.cc
+++ b/src/ant/ant/antConfig.cc
@@ -247,6 +247,8 @@ 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::RulerAutoMetricEdge) {
+ return "auto_metric_edge";
} else if (m == ant::Template::RulerMultiSegment) {
return "multi_segment";
} else if (m == ant::Template::RulerThreeClicks) {
@@ -266,6 +268,8 @@ 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 == "auto_metric_edge") {
+ a = ant::Template::RulerAutoMetricEdge;
} else if (t == "multi_segment") {
a = ant::Template::RulerMultiSegment;
} else if (t == "angle") {
diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc
index 86a14d11a..06680c2a8 100644
--- a/src/ant/ant/antPlugin.cc
+++ b/src/ant/ant/antPlugin.cc
@@ -60,6 +60,9 @@ static std::vector make_standard_templates ()
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 ("Measure edge")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure_edge"));
+ templates.back ().set_mode (ant::Template::RulerAutoMetricEdge);
+
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);
diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc
index f7506bea6..11b0d2fb7 100644
--- a/src/ant/ant/antService.cc
+++ b/src/ant/ant/antService.cc
@@ -1056,8 +1056,18 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
m_drawing (false), m_current (),
m_move_mode (MoveNone),
m_seg_index (0),
- m_current_template (0)
-{
+ m_current_template (0),
+ m_hover (false),
+ m_hover_wait (false),
+ m_hover_buttons (0),
+ m_mouse_in_window (false)
+{
+#if defined(HAVE_QT)
+ m_timer.setInterval (100 /*hover time*/);
+ m_timer.setSingleShot (true);
+ connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
+#endif
+
mp_view->annotations_changed_event.add (this, &Service::annotations_changed);
}
@@ -1809,9 +1819,36 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button
return false;
}
+lay::TwoPointSnapToObjectResult
+Service::auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl)
+{
+ // for auto-metric we need some cutline constraint - any or global won't do.
+ if (ac == lay::AC_Global) {
+ ac = tpl.angle_constraint ();
+ }
+ if (ac == lay::AC_Global) {
+ ac = m_snap_mode;
+ }
+ if (ac == lay::AC_Global) {
+ ac = lay::AC_Diagonal;
+ }
+
+ db::DVector g;
+ if (m_grid_snap) {
+ g = db::DVector (m_grid, m_grid);
+ }
+
+ double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
+ snap_range *= 0.5;
+
+ return lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
+}
+
bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
+ hover_reset ();
+
if (prio && (buttons & lay::LeftButton) != 0) {
const ant::Template &tpl = current_template ();
@@ -1852,27 +1889,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
} else if (tpl.mode () == ant::Template::RulerAutoMetric) {
- // for auto-metric we need some cutline constraint - any or global won't do.
- lay::angle_constraint_type ac = ac_from_buttons (buttons);
- if (ac == lay::AC_Global) {
- ac = tpl.angle_constraint ();
- }
- if (ac == lay::AC_Global) {
- ac = m_snap_mode;
- }
- if (ac == lay::AC_Global) {
- ac = lay::AC_Diagonal;
- }
-
- db::DVector g;
- if (m_grid_snap) {
- g = db::DVector (m_grid, m_grid);
- }
-
- double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
- snap_range *= 0.5;
-
- lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
+ lay::TwoPointSnapToObjectResult ee = auto_measure (p, ac_from_buttons (buttons), tpl);
if (ee.any) {
// begin the transaction
@@ -1893,6 +1910,29 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
}
+ } else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) {
+
+ lay::PointSnapToObjectResult snap_details = snap1_details (p, true);
+ if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) {
+
+ // begin the transaction
+ if (manager ()) {
+ tl_assert (! manager ()->transacting ());
+ manager ()->transaction (tl::to_string (tr ("Create ruler")));
+ }
+
+ m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl);
+ show_message ();
+
+ insert_ruler (m_current, true);
+
+ // end the transaction
+ if (manager ()) {
+ manager ()->commit ();
+ }
+
+ }
+
} else {
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
@@ -1968,21 +2008,33 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type
bool
Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
- if (prio) {
+ if (! prio) {
+ return false;
+ }
- lay::PointSnapToObjectResult snap_details;
- if (m_drawing) {
- snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
- } else {
- const ant::Template &tpl = current_template ();
- snap_details = snap1_details (p, m_obj_snap && tpl.snap ());
- }
+ if (! m_drawing && m_mouse_in_window && view ()->transient_selection_mode ()) {
- mouse_cursor_from_snap_details (snap_details);
+ // Restart hover timer
+ m_hover_wait = true;
+#if defined(HAVE_QT)
+ m_timer.start ();
+#endif
+ m_hover_point = p;
+ m_hover_buttons = buttons;
}
- if (m_drawing && prio) {
+ lay::PointSnapToObjectResult snap_details;
+ if (m_drawing) {
+ snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
+ } else {
+ const ant::Template &tpl = current_template ();
+ snap_details = snap1_details (p, m_obj_snap && tpl.snap () && (tpl.mode () != ant::Template::RulerAutoMetricEdge || ! view ()->transient_selection_mode ()));
+ }
+
+ mouse_cursor_from_snap_details (snap_details);
+
+ if (m_drawing) {
set_cursor (lay::Cursor::cross);
@@ -2284,6 +2336,84 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo
}
}
+bool
+Service::enter_event (bool /*prio*/)
+{
+ m_mouse_in_window = true;
+ return false;
+}
+
+bool
+Service::leave_event (bool)
+{
+ m_mouse_in_window = false;
+ hover_reset ();
+ return false;
+}
+
+void
+Service::hover_reset ()
+{
+ if (m_hover_wait) {
+#if defined(HAVE_QT)
+ m_timer.stop ();
+#endif
+ m_hover_wait = false;
+ }
+ if (m_hover) {
+ // as we use the transient selection for the hover ruler, we have to remove it here
+ clear_transient_selection ();
+ m_hover = false;
+ }
+}
+
+#if defined(HAVE_QT)
+void
+Service::timeout ()
+{
+ m_hover_wait = false;
+ m_hover = true;
+
+ // as we use the transient selection for the hover ruler, we have to remove it here
+ clear_transient_selection ();
+
+ // transiently create an auto-metric ruler if requested
+
+ ant::Object *ruler = 0;
+
+ const ant::Template &tpl = current_template ();
+ if (tpl.mode () == ant::Template::RulerAutoMetric) {
+
+ lay::TwoPointSnapToObjectResult ee = auto_measure (m_hover_point, ac_from_buttons (m_hover_buttons), tpl);
+ if (ee.any) {
+ m_current = ant::Object (ee.first, ee.second, 0, tpl);
+ ruler = &m_current;
+ }
+
+ } else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) {
+
+ lay::PointSnapToObjectResult snap_details = snap1_details (m_hover_point, true);
+ if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) {
+ m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl);
+ ruler = &m_current;
+ }
+
+ }
+
+ if (ruler) {
+
+ // HINT: there is no special style for "transient selection on rulers"
+ mp_transient_ruler = new ant::View (this, ruler, true /*not selected*/);
+
+ if (! editables ()->has_selection ()) {
+ display_status (true);
+ }
+
+ }
+
+}
+#endif
+
bool
Service::transient_select (const db::DPoint &pos)
{
diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h
index eaf664726..fb3ed67fe 100644
--- a/src/ant/ant/antService.h
+++ b/src/ant/ant/antService.h
@@ -39,6 +39,11 @@
#include
-out = in.drc(if_all(area > 0.5, rectangle))
+out = in.drc(if_all(area > 0.5, rectangles))
The condition expressions may be of any type (edges, edge pairs and polygons).
diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
index 40b93cbfe..8bf358bf0 100644
--- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb
+++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
@@ -555,7 +555,7 @@ module DRC
# whose area is larger than 0.5 square micrometers:
#
# @code
- # out = in.drc(if_all(area > 0.5, rectangle))
+ # out = in.drc(if_all(area > 0.5, rectangles))
# @/code
#
# The condition expressions may be of any type (edges, edge pairs and polygons).
diff --git a/src/laybasic/laybasic/layMouseTracker.cc b/src/laybasic/laybasic/layMouseTracker.cc
index 4de45d0f3..3c531e29b 100644
--- a/src/laybasic/laybasic/layMouseTracker.cc
+++ b/src/laybasic/laybasic/layMouseTracker.cc
@@ -105,15 +105,22 @@ MouseTracker::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, b
double max_coord = 1e30; // big enough I guess
- mp_markers.push_back (new lay::DMarker (mp_view));
- mp_markers.back ()->set_line_style (m_cursor_line_style);
- mp_markers.back ()->set_color (m_cursor_color);
- mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord)));
+ for (int i = 0; i < 2; ++i) {
- mp_markers.push_back (new lay::DMarker (mp_view));
- mp_markers.back ()->set_line_style (m_cursor_line_style);
- mp_markers.back ()->set_color (m_cursor_color);
- mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ())));
+ mp_markers.push_back (new lay::DMarker (mp_view));
+ mp_markers.back ()->set_line_style (m_cursor_line_style);
+ mp_markers.back ()->set_line_width (1);
+ mp_markers.back ()->set_halo (false);
+ mp_markers.back ()->set_dither_pattern (1);
+ mp_markers.back ()->set_color (m_cursor_color.is_valid () ? m_cursor_color : mp_view->canvas ()->foreground_color ());
+
+ if (i == 0) {
+ mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord)));
+ } else {
+ mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ())));
+ }
+
+ }
}
diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc
index ab6c4d6d6..4bc22e7d0 100644
--- a/src/laybasic/laybasic/layMove.cc
+++ b/src/laybasic/laybasic/layMove.cc
@@ -305,10 +305,7 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_
if (mp_editables->begin_move (p, ac_from_buttons (buttons))) {
- lay::SelectionService *selector = mp_view->selection_service ();
- if (selector) {
- selector->hover_reset ();
- }
+ ui ()->hover_reset ();
mp_view->clear_transient_selection ();
diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h
index a47789158..6ad7600a9 100644
--- a/src/laybasic/laybasic/laySelector.h
+++ b/src/laybasic/laybasic/laySelector.h
@@ -69,14 +69,7 @@ public:
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio);
-
- /**
- * @brief Reset the hover timer for the transient selection
- *
- * This method may be used by other services (in particular Move) to avoid the transient to
- * be triggered from a move operation.
- */
- void hover_reset ();
+ virtual void hover_reset ();
#if defined (HAVE_QT)
public slots:
diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc
index 0927fd261..d794fe87f 100644
--- a/src/laybasic/laybasic/layViewObject.cc
+++ b/src/laybasic/laybasic/layViewObject.cc
@@ -1051,7 +1051,15 @@ ViewObjectUI::drag_cancel ()
}
}
-namespace
+void
+ViewObjectUI::hover_reset ()
+{
+ for (service_iterator svc = begin_services (); svc != end_services (); ++svc) {
+ (*svc)->hover_reset ();
+ }
+}
+
+namespace
{
struct z_order_compare_f
{
diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h
index 76f9b5175..8ba1bebb9 100644
--- a/src/laybasic/laybasic/layViewObject.h
+++ b/src/laybasic/laybasic/layViewObject.h
@@ -147,6 +147,17 @@ public:
virtual bool drop_event (const db::DPoint & /*p*/, const DragDropDataBase * /*data*/) { return false; }
#endif
+ /**
+ * @brief Hover reset request
+ *
+ * This event is issued for services providing some "hover" mode - i.e. capture
+ * mouse move events and start a timer on them.
+ *
+ * The implementation of this event should cancel this timer and
+ * not raise a hover condition.
+ */
+ virtual void hover_reset () { }
+
/**
* @brief Mouse press event handler
*
@@ -605,6 +616,11 @@ public:
*/
void drag_cancel ();
+ /**
+ * @brief Calls hover_reset on all services
+ */
+ void hover_reset ();
+
/**
* @brief CanvasPlane rendering
*
diff --git a/src/layui/layui/LayoutViewConfigPage2d.ui b/src/layui/layui/LayoutViewConfigPage2d.ui
index ad779f091..f70f43881 100644
--- a/src/layui/layui/LayoutViewConfigPage2d.ui
+++ b/src/layui/layui/LayoutViewConfigPage2d.ui
@@ -55,9 +55,6 @@
-
-
- The color in which the rulers are drawn
-
@@ -151,9 +148,6 @@
-
-
- The color in which the rulers are drawn
-
diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc
index cdc7a4e1b..792818c65 100644
--- a/src/layui/layui/layWidgets.cc
+++ b/src/layui/layui/layWidgets.cc
@@ -972,17 +972,31 @@ SimpleColorButton::set_color_internal (QColor c)
m_color = c;
QFontMetrics fm (font (), this);
- QRect rt (fm.boundingRect (QObject::tr ("Auto"))); // dummy text to be compliant with the other color button
- QPixmap pxmp (rt.width () + 24, rt.height ());
+ QRect rt (fm.boundingRect (QObject::tr ("XXXXXXX")));
+
+#if QT_VERSION >= 0x050000
+ double dpr = devicePixelRatio ();
+#else
+ double dpr = 1.0;
+#endif
+
+ QPixmap pxmp (rt.width () * dpr, rt.height () * dpr);
+#if QT_VERSION >= 0x050000
+ pxmp.setDevicePixelRatio (dpr);
+#endif
QPainter pxpainter (&pxmp);
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
- pxpainter.setPen (QPen (text_color));
pxpainter.setBrush (QBrush (c.isValid () ? c : QColor (128, 128, 128)));
- QRect r (0, 0, pxmp.width () - 1, pxmp.height () - 1);
+ QPen frame_pen (text_color);
+ frame_pen.setWidthF (1.0);
+ frame_pen.setJoinStyle (Qt::MiterJoin);
+ pxpainter.setPen (frame_pen);
+ int dpri = int (dpr);
+ QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0);
pxpainter.drawRect (r);
- setIconSize (pxmp.size ());
+ setIconSize (QSize (rt.width (), rt.height ()));
setIcon (QIcon (pxmp));
}
@@ -1216,22 +1230,25 @@ ColorButton::set_color_internal (QColor c)
#if QT_VERSION >= 0x50000
pixmap.setDevicePixelRatio (dpr);
#endif
- pixmap.fill (QColor (0, 0, 0, 0));
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
QPainter pxpainter (&pixmap);
- pxpainter.setPen (QPen (text_color));
+ QPen frame_pen (text_color);
+ frame_pen.setWidthF (1.0);
+ frame_pen.setJoinStyle (Qt::MiterJoin);
+ pxpainter.setPen (frame_pen);
+
+ int dpri = int (dpr);
+ QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0);
if (! m_color.isValid ()) {
pxpainter.setFont (font ());
- 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));
- QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ());
pxpainter.drawRect (r);
}
diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
index b87a413c1..f9e4bfefc 100644
--- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
+++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
@@ -602,3 +602,21 @@ TEST(Bug_1474)
EXPECT_EQ (ex.msg (), "Cell named ADDHX2 with ID 4 was already given name SEDFFTRX2 (position=763169, cell=)");
}
}
+
+TEST(DuplicateCellname)
+{
+ db::Manager m (false);
+ db::Layout layout (&m);
+
+ try {
+ tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
+ db::OASISReader reader (file);
+ reader.read (layout);
+ EXPECT_EQ (false, true);
+ } catch (tl::CancelException &ex) {
+ // Seen when private test data is not installed
+ throw;
+ } catch (tl::Exception &ex) {
+ EXPECT_EQ (ex.msg (), "Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)");
+ }
+}
diff --git a/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz b/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz
index c9dd21723..907e64016 100644
Binary files a/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz and b/testdata/lefdef/foreigncell/au_ignore_foreign.oas.gz differ
diff --git a/testdata/oasis/duplicate_cellname.oas b/testdata/oasis/duplicate_cellname.oas
new file mode 100644
index 000000000..0ce5df921
Binary files /dev/null and b/testdata/oasis/duplicate_cellname.oas differ
diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb
index e0abb515b..d08f161fa 100644
--- a/testdata/ruby/dbRegionTest.rb
+++ b/testdata/ruby/dbRegionTest.rb
@@ -1226,6 +1226,36 @@ class DBRegion_TestClass < TestBase
end
+ # rasterize
+ def test_rasterize
+
+ r = RBA::Region::new()
+ r.insert(RBA::Polygon::new([[0, 0], [100, 100], [150, 0]]))
+ r.insert(RBA::Polygon::new(RBA::Box::new([0, 200], [100, 300])))
+
+ pd = RBA::Vector::new(50, 50)
+ ps = RBA::Vector::new(25, 25)
+
+ sum = 0
+ 2.times do |ix|
+ 2.times do |iy|
+ am = r.rasterize(RBA::Point::new(-50 + ix * ps.x, -20 + iy * ps.y), pd, ps, 7, 7)
+ sum += am.collect { |r| r.inject(:+) }.inject(:+)
+ end
+ end
+
+ assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y))
+
+ tot = 0.0
+ pd = RBA::Vector::new(50, 50)
+
+ am = r.rasterize(RBA::Point::new(-50, -20), pd, 7, 7)
+ sum = am.collect { |r| r.inject(:+) }.inject(:+)
+
+ assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y))
+
+ end
+
end
load("test_epilogue.rb")