From 067d52a2695e161cc897edcb324a4cc1183dabae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 18 Apr 2018 23:27:44 +0200 Subject: [PATCH] FEATURE: DXF accuracy and "keep layer names" feature * "keep layer name" will not try to modify layer names into GDS layer/datatypes. The feature is available in the DXF reader options and for the buddy scripts as "--keep-layer-names" It applies to CIF too. * "contour accuracy": when merging lines into contours, this accuracy is applied to find neighbors. By default this value is 0. If set to a value >0, points with distances (square metrics) less than this value will be connected. The buddy script option is --dxf-contour-accuracy=value. --- src/db/db/db.pro | 3 +- src/db/db/dbDXFReader.cc | 10 +- src/db/db/dbEdgesToContours.cc | 293 +++++++++++++++++++++++++ src/db/db/dbEdgesToContours.h | 227 ++----------------- src/db/unit_tests/dbDXFReader.cc | 25 ++- src/db/unit_tests/dbEdgesToContours.cc | 171 ++++++++++++--- 6 files changed, 478 insertions(+), 251 deletions(-) create mode 100644 src/db/db/dbEdgesToContours.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 02b98fc64..fde996345 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -130,7 +130,8 @@ SOURCES = \ gsiDeclDbLayoutDiff.cc \ gsiDeclDbGlyphs.cc \ dbVariableWidthPath.cc \ - dbNamedLayerReader.cc + dbNamedLayerReader.cc \ + dbEdgesToContours.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbDXFReader.cc b/src/db/db/dbDXFReader.cc index a4510cf87..9f642ebf4 100644 --- a/src/db/db/dbDXFReader.cc +++ b/src/db/db/dbDXFReader.cc @@ -2743,6 +2743,8 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector db::EdgesToContours e2c; + db::Coord accuracy = db::coord_traits::rounded (m_contour_accuracy * m_unit / m_dbu); + for (std::map >::iterator ce = collected_edges.begin (); ce != collected_edges.end (); ++ce) { std::vector &edges = ce->second; @@ -2750,20 +2752,18 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector std::vector cc_edges; - e2c.fill (edges.begin (), edges.end (), true /*unordered*/, &progress); + e2c.fill (edges.begin (), edges.end (), true /*unordered*/, accuracy, &progress); for (size_t c = 0; c < e2c.contours (); ++c) { - if (e2c.contour (c).back () == e2c.contour (c).front () || m_polyline_mode == 4 /*auto-close*/) { + if (e2c.contour_closed (c) || m_polyline_mode == 4 /*auto-close*/) { // closed contour: store for later merging for (std::vector::const_iterator cc = e2c.contour (c).begin (); cc + 1 != e2c.contour (c).end (); ++cc) { cc_edges.push_back (db::Edge (cc[0], cc[1])); } - if (e2c.contour (c).back () != e2c.contour (c).front ()) { - cc_edges.push_back (db::Edge (e2c.contour (c).back (), e2c.contour (c).front ())); - } + cc_edges.push_back (db::Edge (e2c.contour (c).back (), e2c.contour (c).front ())); } else { diff --git a/src/db/db/dbEdgesToContours.cc b/src/db/db/dbEdgesToContours.cc new file mode 100644 index 000000000..4a1df24e4 --- /dev/null +++ b/src/db/db/dbEdgesToContours.cc @@ -0,0 +1,293 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbEdgesToContours.h" +#include "dbBoxTree.h" +#include "dbBoxConvert.h" +#include "tlProgress.h" + +#include +#include +#include + +namespace +{ + +template +class EdgeRef +{ +public: + typedef typename std::iterator_traits::value_type value_type; + typedef typename value_type::coord_type coord_type; + typedef db::point point_type; + + EdgeRef (Iter i) + : iter (i), swapped (0), connected (false), delivered (false), seen (false), next (0) + { } + + Iter iter; + short swapped; + bool connected : 1; + bool delivered : 1; + bool seen : 1; + EdgeRef *next; + + point_type a () const + { + return swapped > 0 ? iter->p2 () : iter->p1 (); + } + + point_type b () const + { + return swapped > 0 ? iter->p1 () : iter->p2 (); + } +}; + +template +struct EdgeRefToBox +{ +public: + typedef typename std::iterator_traits::value_type value_type; + typedef typename value_type::coord_type coord_type; + typedef db::box box_type; + typedef db::simple_bbox_tag complexity; + + EdgeRefToBox (coord_type d) + : distance (d) + { } + + box_type operator() (EdgeRef *er) const + { + db::point p = swapped ? er->iter->p2 () : er->iter->p1 (); + db::vector d (distance, distance); + return box_type (p - d, p + d); + } + + coord_type distance; +}; + +} + +namespace db +{ + +// ----------------------------------------------------------------------------- +// EdgesToContours implementation + +EdgesToContours::EdgesToContours () +{ + // .. nothing yet .. +} + +const std::vector & +EdgesToContours::contour (size_t i) const +{ + static std::vector empty; + if (i < m_contours.size ()) { + return m_contours [i]; + } else { + return empty; + } +} + +template static +EdgeRef *search_follower (const db::point &p, const EdgeRef *e, C distance, const db::box_tree, EdgeRef *, EdgeRefToBox > &t1, const db::box_tree, EdgeRef *, EdgeRefToBox > &t2) +{ + typedef db::box box_type; + + double vp_min = 0.0; + EdgeRef *cand = 0; + bool fwd = true; + + // try in forward tree + + typename db::box_tree *, EdgeRefToBox >::touching_iterator f = t1.begin_touching (box_type (p, p), EdgeRefToBox (distance)); + while (! f.at_end ()) { + if (*f != e && ! (*f)->connected && (*f)->swapped != 1) { + double vp = db::vprod ((*f)->iter->d (), e->iter->d ()) * (1.0 / (*f)->iter->d ().double_length ()); + if (! cand || vp < vp_min) { + vp_min = vp; + cand = *f; + } + } + ++f; + } + + if (! t2.empty ()) { + typename db::box_tree *, EdgeRefToBox >::touching_iterator f = t2.begin_touching (box_type (p, p), EdgeRefToBox (distance)); + while (! f.at_end ()) { + if (*f != e && ! (*f)->connected && (*f)->swapped != -1) { + double vp = db::vprod ((*f)->iter->d (), e->iter->d ()) * (1.0 / (*f)->iter->d ().double_length ()); + if (! cand || vp < vp_min) { + vp_min = vp; + cand = *f; + fwd = false; + } + } + ++f; + } + } + + if (cand) { + cand->swapped = fwd ? -1 : 1; + cand->connected = true; + } + + return cand; +} + +template +void +EdgesToContours::fill (Iter from, Iter to, bool no, typename std::iterator_traits::value_type::coord_type distance, tl::RelativeProgress *progress) +{ + typedef typename std::iterator_traits::value_type value_type; + typedef typename value_type::coord_type coord_type; + typedef db::box box_type; + typedef db::point point_type; + typedef db::box_tree *, EdgeRefToBox > box_tree; + typedef db::box_tree *, EdgeRefToBox > box_tree_rev; + + std::vector > erefs; + erefs.reserve (to - from); + + for (Iter i = from; i < to; ++i) { + erefs.push_back (EdgeRef (i)); + } + + // prepare two box trees: one forward (with p1 being the key) + // and a backward one with p2 being the key + + box_tree bt; + box_tree_rev btr; + + bt.reserve (erefs.size ()); + for (typename std::vector >::iterator e = erefs.begin (); e != erefs.end (); ++e) { + bt.insert (e.operator-> ()); + } + bt.sort (EdgeRefToBox (distance)); + + if (no) { + btr.reserve (erefs.size ()); + for (typename std::vector >::iterator e = erefs.begin (); e != erefs.end (); ++e) { + btr.insert (e.operator-> ()); + } + btr.sort (EdgeRefToBox (distance)); + } + + // Build the edge dependency graph (e->next being the following one) + + for (typename std::vector >::iterator e = erefs.begin (); e != erefs.end (); ++e) { + + EdgeRef *ee = e.operator-> (); + while (ee && !ee->seen) { + + if (progress) { + ++*progress; + } + + ee->seen = true; + + EdgeRef *f1 = 0, *f2 = 0; + if (ee->swapped != 1) { + f1 = search_follower (ee->iter->p2 (), ee, distance, bt, btr); + } + if (! f1 && ee->swapped != -1 && no) { + f2 = search_follower (ee->iter->p1 (), ee, distance, bt, btr); + } + + if (f1) { + ee->swapped = -1; + ee->next = f1; + } else if (f2) { + ee->swapped = 1; + ee->next = f2; + } + + ee = ee->next; + + } + + } + + // Delivery + + m_contours.clear (); + m_contours_closed.clear (); + + // Extract the open contours + + for (typename std::vector >::iterator e = erefs.begin (); e != erefs.end (); ++e) { + + if (progress) { + ++*progress; + } + + if (! e->delivered && ! e->connected) { + + m_contours.push_back (std::vector ()); + m_contours_closed.push_back (false); + + m_contours.back ().push_back (e->a ()); + EdgeRef *ee = e.operator-> (); + while (ee) { + tl_assert (! ee->delivered); + m_contours.back ().push_back (ee->b ()); + ee->delivered = true; + ee = ee->next; + } + + } + + } + + // Extract the closed contours + + for (typename std::vector >::iterator e = erefs.begin (); e != erefs.end (); ++e) { + + if (progress) { + ++*progress; + } + + if (! e->delivered) { + + m_contours.push_back (std::vector ()); + m_contours_closed.push_back (true); + + EdgeRef *ee = e.operator-> (); + while (ee && ! ee->delivered) { + m_contours.back ().push_back (ee->b ()); + ee->delivered = true; + ee = ee->next; + } + + } + + } +} + +// explicit instantiation +template DB_PUBLIC void EdgesToContours::fill (std::vector::iterator, std::vector::iterator, bool, db::Coord, tl::RelativeProgress *); +template DB_PUBLIC void EdgesToContours::fill (db::Edge *, db::Edge *, bool, db::Coord, tl::RelativeProgress *); + +} // namespace db + diff --git a/src/db/db/dbEdgesToContours.h b/src/db/db/dbEdgesToContours.h index e89ba358f..b22cf4c44 100644 --- a/src/db/db/dbEdgesToContours.h +++ b/src/db/db/dbEdgesToContours.h @@ -27,121 +27,15 @@ #include "dbPoint.h" #include "dbEdge.h" #include "dbVector.h" -#include "tlProgress.h" -#include -#include -#include +#include + +namespace tl { + class RelativeProgress; +} namespace db { -class DB_PUBLIC ECRef -{ -public: - template - ECRef (Iter from, Iter e, bool swap) - { - m_index = ((long) std::distance (from, e)) + 1; - if (swap) { - m_index = -m_index; - } - } - - ECRef () - : m_index (0) - { - // .. nothing yet .. - } - - ECRef (long i) - : m_index (i) - { - // .. nothing yet .. - } - - bool is_null () const - { - return m_index == 0; - } - - bool operator!= (ECRef b) - { - return m_index != b.m_index; - } - - bool operator== (ECRef b) - { - return m_index == b.m_index; - } - - template - const Point &a (Iter from) const - { - if (m_index > 0) { - return from [m_index - 1].p1 (); - } else { - return from [-m_index - 1].p2 (); - } - } - - template - const Point &b (Iter from) const - { - if (m_index > 0) { - return from [m_index - 1].p2 (); - } else { - return from [-m_index - 1].p1 (); - } - } - - ECRef reverse () const - { - return ECRef (-m_index); - } - - size_t index () const - { - return (size_t)((m_index < 0 ? -m_index : m_index) - 1); - } - - template - Vector d (Iter from) const - { - if (m_index > 0) { - return from [m_index - 1].d (); - } else { - return -(from [-m_index - 1].d ()); - } - } - - static ECRef invalid () - { - return ECRef (std::numeric_limits ::min ()); - } - -private: - long m_index; -}; - -template -class ECLess -{ -public: - ECLess (Iter from) - : m_from (from) - { - // .. nothing yet .. - } - - bool operator() (ECRef p, ECRef q) const - { - return p.a (m_from) < q.a (m_from); - } - -private: - Iter m_from; -}; - /** * @brief A facility to create contours from edges * @@ -152,122 +46,29 @@ private: * The use of this objects is to first fill it with edges and then deliver the contours * collected in the fill step. */ -class EdgesToContours +class DB_PUBLIC EdgesToContours { public: - EdgesToContours () - { - // .. nothing yet .. - } + EdgesToContours (); size_t contours () const { return m_contours.size (); } - const std::vector &contour (size_t i) const + const std::vector &contour (size_t i) const; + + bool contour_closed (size_t i) const { - return m_contours [i]; + return m_contours_closed [i]; } template - void fill (Iter from, Iter to, bool no = false, tl::RelativeProgress *progress = 0) - { - m_contours.clear (); - - std::vector ptmap; - ptmap.reserve ((size_t)(no ? 2 : 1) * std::distance (from, to)); - - for (Iter e = from; e != to; ++e) { - ptmap.push_back (ECRef (from, e, false)); - if (no) { - ptmap.push_back (ECRef (from, e, true)); - } - } - - std::sort (ptmap.begin (), ptmap.end (), ECLess (from)); - - std::vector cmap; - cmap.resize (std::distance (from, to), ECRef::invalid ()); - - for (std::vector ::iterator s0 = cmap.begin (); s0 != cmap.end (); ++s0) { - - if (*s0 != ECRef::invalid ()) { - continue; - } - - m_contours.push_back (std::vector ()); - - std::vector ::iterator s = s0; - - ECRef fr (from, from + std::distance (cmap.begin (), s0), false); - while (! fr.is_null ()) { - - std::vector ::iterator s_old = s; - *s_old = ECRef (); - double vp_min = 0.0; - ECRef fr_old = fr; - fr = ECRef (); - - std::vector ::const_iterator f = std::lower_bound (ptmap.begin (), ptmap.end (), fr_old.reverse (), ECLess (from)); - while (f != ptmap.end () && f->a (from) == fr_old.b (from)) { - - if (progress) { - ++*progress; - } - - std::vector ::iterator s_new = cmap.begin () + f->index (); - if (*s_new == ECRef::invalid ()) { - - double vp = db::vprod (f->d (from), fr_old.d (from)) * (1.0 / f->d (from).double_length ()); - if (fr.is_null () || vp < vp_min) { - vp_min = vp; - fr = *f; - s = s_new; - *s_old = *f; - } - - } - - ++f; - - } - - if (progress) { - ++*progress; - } - - } - - // create the contour - - size_t n = 2; - s = s0; - while (! s->is_null ()) { - s = cmap.begin () + s->index (); - ++n; - } - - m_contours.back ().reserve (n); - - const db::Edge e0 (from [std::distance (cmap.begin (), s0)]); - m_contours.back ().push_back (e0.p1 ()); - m_contours.back ().push_back (e0.p2 ()); - - s = s0; - - while (! s->is_null ()) { - m_contours.back ().push_back (s->b (from)); - s = cmap.begin () + s->index (); - ++n; - } - - } - - } + void fill (Iter from, Iter to, bool no = false, typename std::iterator_traits::value_type::coord_type distance = 0, tl::RelativeProgress *progress = 0); private: - std::vector > m_contours; + std::vector > m_contours; + std::vector m_contours_closed; }; } // namespace db diff --git a/src/db/unit_tests/dbDXFReader.cc b/src/db/unit_tests/dbDXFReader.cc index 5aa0ef8a2..92ee77582 100644 --- a/src/db/unit_tests/dbDXFReader.cc +++ b/src/db/unit_tests/dbDXFReader.cc @@ -169,11 +169,6 @@ TEST(12) run_test (_this, "t12.dxf.gz", "t12_au.gds.gz"); } -TEST(13) -{ - run_test (_this, "t13.dxf.gz", "t13_au.gds.gz"); -} - TEST(14) { db::DXFReaderOptions opt; @@ -446,3 +441,23 @@ TEST(30) run_test (_this, "t30.dxf.gz", "t30d_au.gds.gz", opt); } +// accuracy +TEST(31) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1000; + + opt.contour_accuracy = 0; + run_test (_this, "t31.dxf.gz", "t31a_au.gds.gz", opt); + + opt.contour_accuracy = 0.005; + run_test (_this, "t31.dxf.gz", "t31b_au.gds.gz", opt); + + opt.contour_accuracy = 0.01; + run_test (_this, "t31.dxf.gz", "t31c_au.gds.gz", opt); + + opt.contour_accuracy = 0.02; + run_test (_this, "t31.dxf.gz", "t31d_au.gds.gz", opt); +} + diff --git a/src/db/unit_tests/dbEdgesToContours.cc b/src/db/unit_tests/dbEdgesToContours.cc index 85a1f3b7f..0d27a812e 100644 --- a/src/db/unit_tests/dbEdgesToContours.cc +++ b/src/db/unit_tests/dbEdgesToContours.cc @@ -49,24 +49,29 @@ TEST(1) e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "0,0;100,0;100,100;0,100;0,0"); + EXPECT_EQ (c2s (e2c.contour (0)), "100,0;100,100;0,100;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); edges [0].swap_points (); e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); EXPECT_EQ (e2c.contours (), size_t (2)); EXPECT_EQ (c2s (e2c.contour (0)), "100,0;0,0"); + EXPECT_EQ (e2c.contour_closed (0), false); EXPECT_EQ (c2s (e2c.contour (1)), "100,0;100,100;0,100;0,0"); + EXPECT_EQ (e2c.contour_closed (1), false); e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), true); EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "100,0;0,0;0,100;100,100;100,0"); + EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;100,100;100,0"); + EXPECT_EQ (e2c.contour_closed (0), true); edges [2].swap_points (); e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), true); EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "100,0;0,0;0,100;100,100;100,0"); + EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;100,100;100,0"); + EXPECT_EQ (e2c.contour_closed (0), true); } TEST(2) @@ -84,17 +89,17 @@ TEST(2) db::EdgesToContours e2c; e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), true); - EXPECT_EQ (e2c.contours (), size_t (2)); - EXPECT_EQ (c2s (e2c.contour (0)), "-100,-100;100,-100;0,0;-100,-100"); - EXPECT_EQ (c2s (e2c.contour (1)), "200,-50;0,0;-200,-50;0,100;200,-50"); + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "100,-100;0,0;200,-50;0,100;-200,-50;0,0;-100,-100"); + EXPECT_EQ (e2c.contour_closed (0), true); std::swap (edges [0], edges [3]); e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), true); - EXPECT_EQ (e2c.contours (), size_t (2)); - EXPECT_EQ (c2s (e2c.contour (0)), "200,-50;0,100;-200,-50;0,0;200,-50"); - EXPECT_EQ (c2s (e2c.contour (1)), "100,-100;0,0;-100,-100;100,-100"); + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;-200,-50;0,0;100,-100;-100,-100;0,0;200,-50"); + EXPECT_EQ (e2c.contour_closed (0), true); } @@ -114,16 +119,20 @@ TEST(3) e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); EXPECT_EQ (e2c.contours (), size_t (2)); - EXPECT_EQ (c2s (e2c.contour (0)), "-100,-100;100,-100;0,0;-100,-100"); - EXPECT_EQ (c2s (e2c.contour (1)), "0,0;200,-50;0,100;-200,-50;0,0"); + EXPECT_EQ (c2s (e2c.contour (0)), "100,-100;0,0;-100,-100"); + EXPECT_EQ (e2c.contour_closed (0), true); + EXPECT_EQ (c2s (e2c.contour (1)), "200,-50;0,100;-200,-50;0,0"); + EXPECT_EQ (e2c.contour_closed (1), true); std::swap (edges [0], edges [3]); e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); EXPECT_EQ (e2c.contours (), size_t (2)); - EXPECT_EQ (c2s (e2c.contour (0)), "200,-50;0,100;-200,-50;0,0;200,-50"); - EXPECT_EQ (c2s (e2c.contour (1)), "100,-100;0,0;-100,-100;100,-100"); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;-200,-50;0,0;200,-50"); + EXPECT_EQ (e2c.contour_closed (1), true); + EXPECT_EQ (c2s (e2c.contour (1)), "0,0;-100,-100;100,-100"); + EXPECT_EQ (e2c.contour_closed (0), true); } @@ -146,7 +155,8 @@ TEST(4) e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;-100,100;-100,200;200,200;200,100;0,100;0,200;100,200;100,0;0,0"); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;-100,100;-100,200;200,200;200,100;0,100;0,200;100,200;100,0;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); } TEST(5) @@ -163,11 +173,27 @@ TEST(5) db::Edge (db::Point (0, 200), db::Point (100, 200)), }; - db::EdgesToContours e2c; - e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); - EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;-100,100;-100,200;200,200;200,100;0,100;0,200;100,200;100,0"); + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;-100,100;-100,200;200,200;200,100;0,100;0,200;100,200;100,0"); + EXPECT_EQ (e2c.contour_closed (0), false); + } + + for (size_t i = 0 ; i < sizeof (edges) / sizeof (edges[0]); ++i) { + edges[i].set_p1 (edges[i].p1 () + db::Vector (1, 1)); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false, 10); + + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "1,1;0,100;-100,100;-100,200;200,200;200,100;0,100;0,200;100,200;100,0"); + EXPECT_EQ (e2c.contour_closed (0), false); + } } TEST(6) @@ -183,12 +209,31 @@ TEST(6) db::Edge (db::Point (1000, 100), db::Point (1000, 0)) }; - db::EdgesToContours e2c; - e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); - EXPECT_EQ (e2c.contours (), size_t (2)); - EXPECT_EQ (c2s (e2c.contour (0)), "0,0;100,0;100,100;0,100;0,0"); - EXPECT_EQ (c2s (e2c.contour (1)), "1000,0;1100,0;1100,100;1000,100;1000,0"); + EXPECT_EQ (e2c.contours (), size_t (2)); + EXPECT_EQ (c2s (e2c.contour (0)), "100,0;100,100;0,100;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); + EXPECT_EQ (c2s (e2c.contour (1)), "1100,0;1100,100;1000,100;1000,0"); + EXPECT_EQ (e2c.contour_closed (1), true); + } + + for (size_t i = 0 ; i < sizeof (edges) / sizeof (edges[0]); ++i) { + edges[i].set_p1 (edges[i].p1 () + db::Vector (1, 1)); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false, 10); + + EXPECT_EQ (e2c.contours (), size_t (2)); + EXPECT_EQ (c2s (e2c.contour (0)), "100,0;100,100;0,100;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); + EXPECT_EQ (c2s (e2c.contour (1)), "1100,0;1100,100;1000,100;1000,0"); + EXPECT_EQ (e2c.contour_closed (1), true); + } } TEST(7) @@ -208,12 +253,84 @@ TEST(7) db::Edge (db::Point (100, 0), db::Point (0, 0)) }; - db::EdgesToContours e2c; - e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); - EXPECT_EQ (e2c.contours (), size_t (1)); - EXPECT_EQ (c2s (e2c.contour (0)), "0,0;0,100;200,100;400,100;400,0;300,0;300,100;200,100;200,0;200,100;100,100;100,0;0,0"); + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;200,100;400,100;400,0;300,0;300,100;200,100;200,0;200,100;100,100;100,0;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false, 10); + + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;200,100;400,100;400,0;300,0;300,100;200,100;200,0;200,100;100,100;100,0;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); + } + + for (size_t i = 0 ; i < sizeof (edges) / sizeof (edges[0]); ++i) { + edges[i].set_p1 (edges[i].p1 () + db::Vector (1, 1)); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false, 10); + + EXPECT_EQ (e2c.contours (), size_t (1)); + EXPECT_EQ (c2s (e2c.contour (0)), "0,100;200,100;400,100;400,0;300,0;300,100;200,100;200,0;200,100;100,100;100,0;0,0"); + EXPECT_EQ (e2c.contour_closed (0), true); + } + + for (size_t i = 0 ; i < sizeof (edges) / sizeof (edges[0]); ++i) { + edges[i].set_p1 (edges[i].p1 () + db::Vector (10, 10)); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false, 10); + + EXPECT_EQ (e2c.contours (), size_t (12)); + EXPECT_EQ (c2s (e2c.contour (0)), "11,11;0,100"); + EXPECT_EQ (e2c.contour_closed (0), false); + } } +TEST(8) +{ + db::Edge edges[] = { + db::Edge (db::Point (100, 100), db::Point (200, 100)), + db::Edge (db::Point (100, 100), db::Point (100, 200)), + db::Edge (db::Point (0, 0), db::Point (0, 1000)), + db::Edge (db::Point (0, 1000), db::Point (1000, 1000)), + db::Edge (db::Point (1000, 1000), db::Point (1000, 0)), + db::Edge (db::Point (1000, 0), db::Point (0, 0)) + }; + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), false); + + EXPECT_EQ (e2c.contours (), size_t (3)); + EXPECT_EQ (c2s (e2c.contour (0)), "100,100;200,100"); + EXPECT_EQ (e2c.contour_closed (0), false); + EXPECT_EQ (c2s (e2c.contour (1)), "100,100;100,200"); + EXPECT_EQ (e2c.contour_closed (1), false); + EXPECT_EQ (c2s (e2c.contour (2)), "0,1000;1000,1000;1000,0;0,0"); + EXPECT_EQ (e2c.contour_closed (2), true); + } + + { + db::EdgesToContours e2c; + e2c.fill (&edges[0], &edges[0] + (sizeof (edges) / sizeof (edges [0])), true); + + EXPECT_EQ (e2c.contours (), size_t (2)); + EXPECT_EQ (c2s (e2c.contour (0)), "200,100;100,100;100,200"); + EXPECT_EQ (e2c.contour_closed (0), false); + EXPECT_EQ (c2s (e2c.contour (1)), "0,1000;1000,1000;1000,0;0,0"); + EXPECT_EQ (e2c.contour_closed (1), true); + } +}