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 #include +#if defined (HAVE_QT) +# include +# include +#endif + namespace ant { class LayoutViewBase; @@ -177,12 +182,19 @@ private: // ------------------------------------------------------------- -class ANT_PUBLIC Service - : public lay::EditorServiceBase, +class ANT_PUBLIC Service : +#if defined (HAVE_QT) + public QObject, +#endif + public lay::EditorServiceBase, public lay::Drawing, public db::Object { -public: +#if defined (HAVE_QT) +Q_OBJECT +#endif + +public: typedef lay::AnnotationShapes::iterator obj_iterator; /** @@ -341,6 +353,21 @@ public: */ virtual db::DBox selection_bbox (); + /** + * @brief Implementation of the editables API + */ + virtual bool enter_event (bool); + + /** + * @brief Implementation of the editables API + */ + virtual bool leave_event (bool); + + /** + * @brief Implementation of the editables API + */ + virtual void hover_reset (); + /** * @brief Transform the selection (reimplementation of lay::Editable interface) */ @@ -506,6 +533,11 @@ public: */ tl::Event annotation_selection_changed_event; +#if defined (HAVE_QT) +public slots: + void timeout (); +#endif + private: // Ruler display and snapping configuration tl::Color m_color; @@ -551,10 +583,22 @@ private: std::vector m_ruler_templates; unsigned int m_current_template; + // Hover detector + bool m_hover; + bool m_hover_wait; + db::DPoint m_hover_point; + unsigned int m_hover_buttons; +#if defined (HAVE_QT) + QTimer m_timer; +#endif + + bool m_mouse_in_window; + std::pair snap1 (const db::DPoint &p, bool obj_snap); lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap); std::pair snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); + lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl); const ant::Template ¤t_template () const; diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h index f589cc91e..6310ce2c6 100644 --- a/src/ant/ant/antTemplate.h +++ b/src/ant/ant/antTemplate.h @@ -64,15 +64,20 @@ public: */ RulerAutoMetric = 2, + /** + * @brief The ruler is auto-metric along an edge: a single click will place a ruler and the ruler will extend to the edge below + */ + RulerAutoMetricEdge = 3, + /** * @brief The ruler an angle type (two segments, three mouse clicks) for angle and circle radius measurements */ - RulerThreeClicks = 3, + RulerThreeClicks = 4, /** * @brief The ruler is a multi-segment type */ - RulerMultiSegment = 4 + RulerMultiSegment = 5 }; /** diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc index d7e709318..6961636e3 100644 --- a/src/ant/ant/gsiDeclAnt.cc +++ b/src/ant/ant/gsiDeclAnt.cc @@ -434,6 +434,11 @@ static int ruler_mode_auto_metric () return ant::Template::RulerAutoMetric; } +static int ruler_mode_auto_metric_edge () +{ + return ant::Template::RulerAutoMetricEdge; +} + static int ruler_mode_three_clicks () { return ant::Template::RulerThreeClicks; @@ -525,6 +530,12 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat "\n" "This constant has been introduced in version 0.25" ) + + gsi::method ("RulerModeAutoMetricEdge", &gsi::ruler_mode_auto_metric_edge, + "@brief Specifies edge-sensitive 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 edge it is placed on.\n" + "\n" + "This constant has been introduced in version 0.29" + ) + 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" diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index e40b88694..dc87204c9 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -154,6 +154,10 @@ CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first)); } + if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) { + common_reader_error (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld")), cn, id, iname->second.first)); + } + if (iid != m_id_map.end () && iname != m_name_map.end ()) { if (iname->second.second != iid->second.second) { diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index a59b03903..4ccb7dd6b 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -1566,23 +1566,26 @@ compute_rounded (const db::DPolygon &polygon, double rinner, double router, unsi } // ------------------------------------------------------------------------- -// Implementation of AreaMap +// Implementation of area_map -AreaMap::AreaMap () +template +area_map::area_map () : m_nx (0), m_ny (0) { mp_av = 0; } -AreaMap::AreaMap (const AreaMap &other) +template +area_map::area_map (const area_map &other) : m_nx (0), m_ny (0) { mp_av = 0; operator= (other); } -AreaMap & -AreaMap::operator= (const AreaMap &other) +template +area_map & +area_map::operator= (const area_map &other) { if (this != &other) { // TODO: this could be copy on write @@ -1594,21 +1597,24 @@ AreaMap::operator= (const AreaMap &other) return *this; } -AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) +template +area_map::area_map (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny) : m_p0 (p0), m_d (d), m_p (d), m_nx (nx), m_ny (ny) { mp_av = new area_type [nx * ny]; clear (); } -AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) +template +area_map::area_map (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny) : m_p0 (p0), m_d (d), m_p (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())), m_nx (nx), m_ny (ny) { mp_av = new area_type [nx * ny]; clear (); } -AreaMap::~AreaMap () +template +area_map::~area_map () { if (mp_av) { delete[] mp_av; @@ -1616,18 +1622,20 @@ AreaMap::~AreaMap () mp_av = 0; } +template void -AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) +area_map::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny) { reinitialize (p0, d, d, nx, ny); } +template void -AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) +area_map::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny) { m_p0 = p0; m_d = d; - m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); + m_p = vector_type (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); if (nx != m_nx || ny != m_ny) { @@ -1645,8 +1653,9 @@ AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vecto clear (); } +template void -AreaMap::clear () +area_map::clear () { if (mp_av) { area_type *a = mp_av; @@ -1656,8 +1665,9 @@ AreaMap::clear () } } +template void -AreaMap::swap (AreaMap &other) +area_map::swap (area_map &other) { std::swap (m_p0, other.m_p0); std::swap (m_d, other.m_d); @@ -1667,8 +1677,9 @@ AreaMap::swap (AreaMap &other) std::swap (mp_av, other.mp_av); } -AreaMap::area_type -AreaMap::total_area () const +template +typename area_map::area_type +area_map::total_area () const { area_type asum = 0; if (mp_av) { @@ -1680,16 +1691,21 @@ AreaMap::total_area () const return asum; } -db::Box -AreaMap::bbox () const +template +typename area_map::box_type +area_map::bbox () const { if (m_nx == 0 || m_ny == 0) { - return db::Box (); + return box_type (); } else { - return db::Box (m_p0, m_p0 + db::Vector (db::Coord (m_nx - 1) * m_d.x () + m_p.x (), db::Coord (m_ny - 1) * m_d.y () + m_p.y ())); + return box_type (m_p0, m_p0 + vector_type (C (m_nx - 1) * m_d.x () + m_p.x (), C (m_ny - 1) * m_d.y () + m_p.y ())); } } +// explicit instantiations +template class area_map; +template class area_map; + // ------------------------------------------------------------------------- // Implementation of rasterize @@ -1707,29 +1723,69 @@ static bool edge_is_partially_left_of (const db::Edge &e, const db::Edge &e_orig } } -bool -rasterize (const db::Polygon &polygon, db::AreaMap &am) +static bool edge_is_partially_left_of (const db::DEdge &e, const db::DEdge &e_original, db::DCoord x) { - typedef db::AreaMap::area_type area_type; - db::Box box = am.bbox (); - db::Box pbox = polygon.box (); + DCoord xmin = db::edge_xmin (e); + if (db::coord_traits::less (xmin, x)) { + return true; + } else if (db::coord_traits::equal (xmin, x) && ! db::coord_traits::equal (e_original.dx (), 0)) { + // the skew edge is cut partially rendering a straight vertical line (due to rounding) + // which we will count as "left of" + return true; + } else { + return false; + } +} + +static size_t npixels_floor (db::Coord d, db::Coord p) +{ + return size_t (std::max (db::Coord (0), d / p)); +} + +static size_t npixels_ceil (db::Coord d, db::Coord p) +{ + return size_t (std::max (db::Coord (0), (d + p - 1) / p)); +} + +static size_t npixels_floor (db::DCoord d, db::DCoord p) +{ + return size_t (std::max (db::DCoord (0), floor (d / p + db::epsilon))); +} + +static size_t npixels_ceil (db::DCoord d, db::DCoord p) +{ + return size_t (std::max (db::DCoord (0), ceil (d / p - db::epsilon))); +} + + +template +static +bool +rasterize_impl (const db::polygon &polygon, db::area_map &am) +{ + typedef typename db::area_map::area_type area_type; + typedef db::box box_type; + typedef db::edge edge_type; + + box_type box = am.bbox (); + box_type pbox = polygon.box (); // check if the polygon overlaps the rasterization area. Otherwise, we simply do nothing. if (! pbox.overlaps (box)) { return false; } - db::Coord ymin = box.bottom (), ymax = box.top (); - db::Coord dy = am.d ().y (), dx = am.d ().x (); - db::Coord py = am.p ().y (), px = am.p ().x (); - db::Coord y0 = am.p0 ().y (), x0 = am.p0 ().x (); + C ymin = box.bottom (), ymax = box.top (); + C dy = am.d ().y (), dx = am.d ().x (); + C py = am.p ().y (), px = am.p ().x (); + C y0 = am.p0 ().y (), x0 = am.p0 ().x (); size_t ny = am.ny (), nx = am.nx (); - size_t iy0 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.bottom () - am.p0 ().y ()) / am.d ().y ()))); - size_t iy1 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.top () - am.p0 ().y () + am.d ().y () - 1) / am.d ().y ()))); + size_t iy0 = std::min (ny, npixels_floor (pbox.bottom () - am.p0 ().y (), am.d ().y ())); + size_t iy1 = std::min (ny, npixels_ceil (pbox.top () - am.p0 ().y (), am.d ().y ())); - size_t ix0 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.left () - am.p0 ().x ()) / am.d ().x ()))); - size_t ix1 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.right () - am.p0 ().x () + am.d ().x () - 1) / am.d ().x ()))); + size_t ix0 = std::min (nx, npixels_floor (pbox.left () - am.p0 ().x (), am.d ().x ())); + size_t ix1 = std::min (nx, npixels_ceil (pbox.right () - am.p0 ().x (), am.d ().x ())); // no scanning required (i.e. degenerated polygon) -> do nothing if (iy0 == iy1 || ix0 == ix1) { @@ -1738,26 +1794,26 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) // collect edges size_t n = 0; - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + for (typename db::polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) { ++n; } } - std::vector edges; + std::vector edges; edges.reserve (n); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + for (typename db::polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) { edges.push_back (*e); } } // sort edges - std::sort (edges.begin (), edges.end (), db::edge_ymin_compare ()); + std::sort (edges.begin (), edges.end (), db::edge_ymin_compare ()); - std::vector ::iterator c = edges.begin (); + typename std::vector ::iterator c = edges.begin (); - db::Coord y = y0 + dy * db::Coord (iy0); + C y = y0 + dy * C (iy0); while (c != edges.end () && db::edge_ymax (*c) <= y) { ++c; @@ -1767,36 +1823,36 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) return false; } - std::vector ::iterator f = c; + typename std::vector ::iterator f = c; for (size_t iy = iy0; iy < iy1; ++iy) { - db::Coord yy = y + py; + C yy = y + py; while (f != edges.end () && db::edge_ymin (*f) < yy) { ++f; } - std::sort (c, f, db::edge_xmin_compare ()); + std::sort (c, f, db::edge_xmin_compare ()); - db::Coord x = x0 + dx * db::Coord (ix0); - db::Coord xl = pbox.left (); + C x = x0 + dx * C (ix0); + C xl = pbox.left (); area_type a = 0; - std::vector ::iterator cc = c; + typename std::vector ::iterator cc = c; while (cc != edges.end () && cc != f && db::edge_xmax (*cc) <= x) { - db::Coord y1 = std::max (y, std::min (yy, cc->p1 ().y ())); - db::Coord y2 = std::max (y, std::min (yy, cc->p2 ().y ())); + C y1 = std::max (y, std::min (yy, cc->p1 ().y ())); + C y2 = std::max (y, std::min (yy, cc->p2 ().y ())); a += area_type (px) * area_type (y2 - y1); ++cc; } - std::vector ::iterator ff = cc; + typename std::vector ::iterator ff = cc; for (size_t ix = ix0; ix < ix1; ++ix) { - db::Coord xx = x + px; - db::Coord xxx = x + dx; + C xx = x + px; + C xxx = x + dx; // TODO: edge_xmin_at_interval(y, yy) and edge_xmax.. would be more efficient in the // all-angle case. However, it is crucial that the edge clipping produces @@ -1807,7 +1863,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ++ff; } - std::vector ::iterator fff = ff; + typename std::vector ::iterator fff = ff; if (xx < xxx) { while (fff != f && db::edge_xmin (*fff) < xxx) { @@ -1818,11 +1874,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) if (xl < x) { // consider all edges or parts of those left of the first cell - db::Box left (xl, y, x, yy); + box_type left (xl, y, x, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (left); + std::pair ec = e->clipped (left); if (ec.first && edge_is_partially_left_of (ec.second, *e, x)) { a += area_type (ec.second.dy ()) * area_type (px); } @@ -1835,11 +1891,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) if (dx == py) { - db::Box cell (x, y, xx, yy); + box_type cell (x, y, xx, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (cell); + std::pair ec = e->clipped (cell); if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) { aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; a += area_type (ec.second.dy ()) * area_type (px); @@ -1849,22 +1905,22 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) } else { - db::Box cell (x, y, xx, yy); + box_type cell (x, y, xx, yy); - for (std::vector ::iterator e = cc; e != ff; ++e) { + for (typename std::vector ::iterator e = cc; e != ff; ++e) { - std::pair ec = e->clipped (cell); + std::pair ec = e->clipped (cell); if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) { aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; } } - db::Box wide_cell (x, y, x + dx, yy); + box_type wide_cell (x, y, x + dx, yy); - for (std::vector ::iterator e = cc; e != fff; ++e) { + for (typename std::vector ::iterator e = cc; e != fff; ++e) { - std::pair wide_ec = e->clipped (wide_cell); + std::pair wide_ec = e->clipped (wide_cell); if (wide_ec.first && edge_is_partially_left_of (wide_ec.second, *e, x + dx)) { a += area_type (wide_ec.second.dy ()) * area_type (px); } @@ -1880,7 +1936,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ff = fff; - for (std::vector ::iterator ccx = cc; ccx != ff; ++ccx) { + for (typename std::vector ::iterator ccx = cc; ccx != ff; ++ccx) { if (db::edge_xmax (*ccx) <= x) { std::swap (*ccx, *cc); ++cc; @@ -1898,7 +1954,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) y = yy; - for (std::vector ::iterator cx = c; cx != f; ++cx) { + for (typename std::vector ::iterator cx = c; cx != f; ++cx) { if (db::edge_ymax (*cx) <= y) { std::swap (*cx, *c); ++c; @@ -1910,6 +1966,18 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) return true; } +bool +rasterize (const db::Polygon &polygon, db::AreaMap &am) +{ + return rasterize_impl (polygon, am); +} + +bool +rasterize (const db::DPolygon &polygon, db::DAreaMap &am) +{ + return rasterize_impl (polygon, am); +} + // ------------------------------------------------------------------------- // Implementation of Minkowski sum diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index e931aa54e..87497812f 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -493,55 +493,59 @@ bool DB_PUBLIC is_non_orientable_polygon (const db::Polygon &poly, std::vector +class DB_PUBLIC area_map { public: - typedef db::coord_traits::area_type area_type; + typedef typename db::coord_traits::area_type area_type; + typedef db::point point_type; + typedef db::vector vector_type; + typedef db::box box_type; /** * @brief Constructor */ - AreaMap (); + area_map (); /** * @brief Copy constructor */ - AreaMap (const AreaMap &); + area_map (const area_map &); /** * @brief Constructor */ - AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + area_map (const point_type &p0, const vector_type &d, size_t nx, size_t ny); /** * @brief Constructor with pixel size */ - AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + area_map (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny); /** * @brief Destructor */ - ~AreaMap (); + ~area_map (); /** * @brief Assignment */ - AreaMap &operator= (const AreaMap &); + area_map &operator= (const area_map &); /** * @brief Reinitialize */ - void reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + void reinitialize (const point_type &p0, const vector_type &d, size_t nx, size_t ny); /** * @brief Reinitialize with pixel size */ - void reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + void reinitialize (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny); /** * @brief Swap of two maps */ - void swap (AreaMap &other); + void swap (area_map &other); /** * @brief Get the area of one pixel @@ -578,7 +582,7 @@ public: /** * @brief The origin */ - const db::Point &p0 () const + const point_type &p0 () const { return m_p0; } @@ -586,7 +590,7 @@ public: /** * @brief Move the origin */ - void move (const db::Vector &d) + void move (const vector_type &d) { m_p0 += d; } @@ -594,7 +598,7 @@ public: /** * @brief The per-pixel displacement vector (pixel size) */ - const db::Vector &d () const + const vector_type &d () const { return m_d; } @@ -602,7 +606,7 @@ public: /** * @brief The pixel size (must be less than d) */ - const db::Vector &p () const + const vector_type &p () const { return m_p; } @@ -610,7 +614,7 @@ public: /** * @brief Compute the bounding box of the area map */ - db::Box bbox () const; + box_type bbox () const; /** * @brief Compute the total area @@ -632,12 +636,15 @@ public: private: area_type *mp_av; - db::Point m_p0; - db::Vector m_d; - db::Vector m_p; + point_type m_p0; + vector_type m_d; + vector_type m_p; size_t m_nx, m_ny; }; +typedef area_map AreaMap; +typedef area_map DAreaMap; + /** * @brief Rasterize the polygon into the given area map * @@ -648,6 +655,16 @@ private: */ bool DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am); +/** + * @brief Rasterize the polygon into the given area map (double version) + * + * This will decompose the polygon and produce per-pixel area values for the given + * polygon. The area contributions will be added to the given area map. + * + * Returns a value indicating whether the map will be non-empty. + */ +bool DB_PUBLIC rasterize (const db::DPolygon &polygon, db::DAreaMap &am); + /** * @brief Minkowski sum of an edge and a polygon */ diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 33d13a5de..7bac67ea8 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -788,6 +788,40 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static std::vector > +rasterize2 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_distance, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) +{ + db::DAreaMap am (db::DPoint (origin), db::DVector (pixel_distance), db::DVector (pixel_size), nx, ny); + + auto pi = region->begin (); + pi = pi.confined (db::Box (am.bbox ()), false /*not overlapping*/); + + while (! pi.at_end ()) { + db::DPolygon dp (*pi); + db::rasterize (dp, am); + ++pi; + } + + std::vector > result; + result.reserve (ny); + for (unsigned int y = 0; y < ny; ++y) { + result.push_back (std::vector ()); + std::vector &row = result.back (); + row.reserve (nx); + for (unsigned int x = 0; x < nx; ++x) { + row.push_back (am.get (x, y)); + } + } + + return result; +} + +static std::vector > +rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) +{ + return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny); +} + static db::Point default_origin; // provided by gsiDeclDbPolygon.cc: @@ -3095,6 +3129,36 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@brief Converts the region to a string\n" "This version allows specification of the maximum number of polygons contained in the string." ) + + method_ext ("rasterize", &rasterize1, gsi::arg ("origin"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"), + "@brief A grayscale rasterizer delivering the area covered per pixel\n" + "@param origin The lower-left corner of the lowest-left pixel\n" + "@param pixel_size The dimension of each pixel (the x component gives the width, the y component the height)\n" + "@param nx The number of pixels in horizontal direction\n" + "@param ny The number of pixels in vertical direction\n" + "The method will create a grayscale, high-resolution density map of a rectangular region.\n" + "The scan region is defined by the origin, the pixel size and the number of pixels in horizontal (nx) and\n" + "vertical (ny) direction. The resulting array will contain the area covered by polygons from the region\n" + "in square database units.\n" + "\n" + "For non-overlapping polygons, the maximum density value is px*py. Overlapping polygons are counted multiple\n" + "times, so the actual values may be larger. If you want overlaps removed, you have to\n" + "merge the region before. Merge semantics does not apply for the 'rasterize' method.\n" + "\n" + "The resulting area values are precise within the limits of double-precision floating point arithmetics.\n" + "\n" + "A second version exists that allows specifying an active pixel size which is smaller than the\n" + "pixel distance hence allowing pixels samples that do not cover the full area, but leave gaps between the pixels.\n" + "\n" + "This method has been added in version 0.29.\n" + ) + + method_ext ("rasterize", &rasterize2, gsi::arg ("origin"), gsi::arg ("pixel_distance"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"), + "@brief A version of 'rasterize' that allows a pixel step distance which is larger than the pixel size\n" + "This version behaves like the first variant of 'rasterize', but the pixel distance (pixel-to-pixel step raster)\n" + "can be specified separately from the pixel size. Currently, the pixel size must be equal or smaller than the\n" + "pixel distance - i.e. the pixels must not overlap.\n" + "\n" + "This method has been added in version 0.29.\n" + ) + method ("enable_progress", &db::Region::enable_progress, gsi::arg ("label"), "@brief Enable progress reporting\n" "After calling this method, the region will report the progress through a progress bar while " diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index f18b0f0e7..670651628 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -828,7 +828,7 @@ The following example selects all shapes which are rectangles and whose area is larger than 0.5 square micrometers:

-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")