diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 83209ee29..a9489321e 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -129,6 +129,7 @@ SOURCES = \ gsiDeclDbVector.cc \ gsiDeclDbLayoutDiff.cc \ gsiDeclDbGlyphs.cc \ + dbVariableWidthPath.cc HEADERS = \ dbArray.h \ @@ -228,7 +229,8 @@ HEADERS = \ contrib/dbGDS2TextWriter.h \ dbCommonReader.h \ dbGlyphs.h \ - dbCommon.h + dbCommon.h \ + dbVariableWidthPath.h RESOURCES = \ dbResources.qrc diff --git a/src/db/db/dbDXFReader.cc b/src/db/db/dbDXFReader.cc index 86cb7a393..9fe1dd928 100644 --- a/src/db/db/dbDXFReader.cc +++ b/src/db/db/dbDXFReader.cc @@ -30,6 +30,7 @@ #include "dbRecursiveShapeIterator.h" #include "dbEdgeProcessor.h" #include "dbEdgesToContours.h" +#include "dbVariableWidthPath.h" #include "tlException.h" #include "tlString.h" @@ -1574,17 +1575,15 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } else if (entity_code == "LWPOLYLINE" || entity_code == "POLYLINE") { std::vector points; + std::vector > widths; std::string layer; int flags = 0; - double width = 0.0; - bool width_set = false; + double width1 = 0.0, width2 = 0.0; unsigned int got_width = 0; - double common_width = 0.0; + double common_width1 = 0.0, common_width2 = 0.0; unsigned int common_width_set = 0; double ex = 0.0, ey = 0.0, ez = 1.0; - size_t points_with_width = 0; - size_t points_with_one_width = 0; size_t tot_points = 0; double b = 0.0; @@ -1610,20 +1609,23 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } if (xy_flags == 3) { + + size_t seg_start = std::max (size_t (1), points.size ()) - 1; add_bulge_segment (points, db::DPoint (x, y), b); ++tot_points; b = 0.0; xy_flags = 0; + + if (got_width == 3) { + widths.push_back (std::make_pair (seg_start, width1)); + widths.push_back (std::make_pair (points.size () - 1, width2)); + got_width = 0; + } + + got_width = 0; + } - if (got_width == 3) { - ++points_with_width; - } else if (got_width > 0) { - ++points_with_one_width; - } - - got_width = 0; - } else if (g == 210) { ex = read_double (); } else if (g == 220) { @@ -1632,7 +1634,7 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector ez = read_double (); } else if (g == 43) { - common_width = read_double (); + common_width1 = common_width2 = read_double (); common_width_set = 3; } else if (g == 42) { @@ -1641,18 +1643,10 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector if (g == 41) { got_width |= 2; + width2 = read_double (); } else { got_width |= 1; - } - - if (!width_set) { - width = read_double (); - width_set = true; - } else { - double w = read_double (); - if (fabs (w - width) > 1e-6) { - warn ("Non-uniform width encountered on LWPOLYLINE"); - } + width1 = read_double (); } } else { @@ -1677,18 +1671,11 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector ez = read_double (); } else if (g == 40 || g == 41) { - if (common_width_set == 0) { - common_width = read_double (); - } else { - double w = read_double (); - if (fabs (w - common_width) > 1e-6) { - warn ("Different start and end width encountered on POLYLINE"); - } - } - if (g == 40) { + common_width1 = read_double (); common_width_set |= 1; } else { + common_width2 = read_double (); common_width_set |= 2; } @@ -1703,12 +1690,6 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector const std::string &e = read_string (true); if (e == "VERTEX") { - if (got_width == 3) { - ++points_with_width; - } else if (got_width > 0) { - ++points_with_one_width; - } - got_width = 0; double x = 0.0, y = 0.0; @@ -1725,18 +1706,10 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector if (g == 41) { got_width |= 2; + width2 = read_double (); } else { got_width |= 1; - } - - if (!width_set) { - width = read_double (); - width_set = true; - } else { - double w = read_double (); - if (fabs (w - width) > 1e-6) { - warn ("Non-uniform width encountered on POLYLINE"); - } + width1 = read_double (); } } else { @@ -1744,10 +1717,17 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } } + size_t seg_start = std::max (size_t (1), points.size ()) - 1; add_bulge_segment (points, db::DPoint (x, y), b); ++tot_points; b = bnew; + if (got_width == 3) { + widths.push_back (std::make_pair (seg_start, width1)); + widths.push_back (std::make_pair (points.size () - 1, width2)); + got_width = 0; + } + } else if (e == "SEQEND") { while ((g = read_group_code ()) != 0) { skip_value (g); @@ -1763,10 +1743,19 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector } - if (got_width == 3) { - ++points_with_width; - } else if (got_width > 0) { - ++points_with_one_width; + // Adds the common width if given + if (common_width_set > 0 && ! points.empty ()) { + if (widths.empty ()) { + widths.insert (widths.begin (), std::make_pair (size_t (0), common_width1)); + widths.push_back (std::make_pair (points.size () - 1, common_width2)); + } else { + if (widths.front ().first != 0) { + widths.insert (widths.begin (), std::make_pair (size_t (0), common_width1)); + } + if (widths.back ().first != points.size () - 1) { + widths.push_back (std::make_pair (points.size () - 1, common_width2)); + } + } } // Create a closing arc if a bulge was specified on the last point and the polygon is @@ -1775,24 +1764,21 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector // Hint: needs a copy, since points may be altered by add_bulge_segment db::DPoint p0 (points [0]); add_bulge_segment (points, p0, b); + if (! widths.empty ()) { + widths.push_back (std::make_pair (points.size () - 1, widths.front ().second)); + } } - // Let the per-vertex width override the common width - // TODO: this scheme just covers the case if all or no vertex has width - // specifications. - if (! width_set || fabs (width) < 1e-6) { - width = common_width; - } - - // issue a warning if there are vertices without a width specification - if (points_with_width > 0 && points_with_width < tot_points) { - warn ("Mixed width specification encountered on LWPOLYLINE or POLYLINE - uniform width supported only"); - } - if (points_with_one_width > 0) { - warn ("Single width specification encountered on LWPOLYLINE or POLYLINE vertex - full and uniform specification supported only"); - } - if (common_width_set > 0 && common_width_set < 3) { - warn ("Either both start and end width must be specified on POLYLINE or none of them"); + // check whether there is a common width and create a path if there is one + bool width_set = false; + double width = 0.0; + for (std::vector >::const_iterator w = widths.begin (); w != widths.end (); ++w) { + if (! width_set) { + width = w->second; + width_set = true; + } else if (width > -1e-6 && fabs (width - w->second) > 1e-6) { + width = -1.0; + } } std::pair ll = open_layer (layout, layer); @@ -1803,7 +1789,12 @@ DXFReader::read_entities (db::Layout &layout, db::Cell &cell, const db::DVector // create the polygon or path for width = 0: in mode 2, the polygon is always created, in modes 3 and 4 // the polygon is split into edges and joined with other edges later (this will resolve holes formed by // other polygons) - if (width < 1e-6 && (flags & 1) != 0 && m_polyline_mode == 2) { + if (width < -1e-6) { + + db::DVariableWidthPath vp (points.begin (), points.end (), widths.begin (), widths.end (), tt); + cell.shapes(ll.second).insert (safe_from_double (vp.to_poly ())); + + } else if (width < 1e-6 && (flags & 1) != 0 && m_polyline_mode == 2) { db::DPolygon p; p.assign_hull (points.begin (), points.end (), tt); diff --git a/src/db/db/dbVariableWidthPath.cc b/src/db/db/dbVariableWidthPath.cc new file mode 100644 index 000000000..d094a7c50 --- /dev/null +++ b/src/db/db/dbVariableWidthPath.cc @@ -0,0 +1,319 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 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 "dbVariableWidthPath.h" + +namespace db +{ + +template +void +variable_width_path::init () +{ + // compress the points + + typename std::vector >::iterator pw = m_points.begin (); + typename std::vector >::const_iterator pr = m_points.begin (); + + typename std::vector >::iterator ow = m_org_widths.begin (); + + while (pr != m_points.end ()) { + + size_t ir = pr - m_points.begin (); + *pw = *pr++; + while (pr != m_points.end () && *pw == *pr) { + ++pr; + } + size_t irr = pr - m_points.begin (); + + size_t iw = pw - m_points.begin (); + ++pw; + + while (ow != m_org_widths.end () && ow->first < irr && ow->first >= ir) { + ow->first = iw; + ++ow; + } + if (ow != m_org_widths.end ()) { + tl_assert (ow->first >= irr); + } + + } + + m_points.erase (pw, m_points.end ()); + + // create a per-point width specification + + width_type w = 0; + size_t i = 0; + bool last_set = false; + + for (typename std::vector >::const_iterator j = m_org_widths.begin (); j != m_org_widths.end (); ++j) { + + width_type w0 = w; + w = j->second; + + tl_assert (j->first < m_points.size ()); + + if (j->first == i) { + + if (last_set) { + m_widths.back ().second = j->second; + } else { + m_widths.push_back (std::make_pair (w0, j->second)); + } + + } else { + + tl_assert (j->first > i); + tl_assert (j->first < m_points.size ()); + + // interpolation: first determine the whole length from last point to next + // and then interpolate each segment + + double ll = 0; + for (size_t ii = i; ii < j->first; ++ii) { + ll += (m_points [ii + 1] - m_points [ii]).double_length (); + } + + double l = 0; + for (size_t ii = i; ii <= j->first; ++ii) { + if (! last_set) { + width_type ww = db::coord_traits::rounded (w0 + (w - w0) * (l / ll)); + m_widths.push_back (std::make_pair (ww, ww)); + } + last_set = false; + if (ii < j->first) { + l += (m_points [ii + 1] - m_points [ii]).double_length (); + } + } + + i = j->first; + + } + + last_set = true; + + } + + // fill up the remaining widths (should not happen if the last width_spec is for + // the last point) + while (m_points.size () > m_widths.size ()) { + if (! last_set) { + m_widths.push_back (std::make_pair (w, w)); + } + last_set = false; + } +} + +template +static +void create_shifted_points (C /*c*/, bool forward, Iter from, Iter to, WIter wfrom, WIter wto, Inserter pts) +{ + // for safety reasons + if (from == to) { + return; + } + + WIter w = wfrom; + WIter ww = w; + ++ww; + + Iter p = from; + Iter pp = p; + ++pp; + + bool first = true; + + while (pp != to) { + + Iter ppp = pp; + ++ppp; + + WIter www = ww; + ++www; + + // Compute the unit vector of the line and it's normal (times width) + + db::DVector ed (*pp - *p); + ed *= 1.0 / ed.double_length (); + + if (first) { + + first = false; + + db::DVector nd (-ed.y (), ed.x ()); + nd *= (forward ? w->second : w->first) * 0.5; + + *pts++ = (*p + vector (nd)); + + } + + if (ppp == to) { + + db::DVector nd (-ed.y (), ed.x ()); + nd *= (forward ? ww->first : ww->second) * 0.5; + + *pts++ = (*pp + vector (nd)); + + } else if (fabs (double (ww->first) - double (ww->second)) > db::epsilon) { + + // switching widths -> create a direct connection + + db::DVector eed (*ppp - *pp); + eed *= 1.0 / eed.double_length (); + + db::DVector nd (-ed.y (), ed.x ()); + nd *= (forward ? ww->first : ww->second) * 0.5; + db::DVector nnd (-eed.y (), eed.x ()); + nnd *= (forward ? ww->second : ww->first) * 0.5; + + *pts++ = *pp + vector (nd); + *pts++ = *pp + vector (nnd); + + } else { + + tl_assert (www != wto); + + double wi = ww->first; + + db::DVector eed (*ppp - *pp); + eed *= 1.0 / eed.double_length (); + + // Points in between are determined from taking two + // edges being shifted perpendicular from the orginal + // and being slightly extended. The intersection point + // of both gives the new vertex. If there is no intersection, + // the edges are simply connected. + + db::DVector nd1 (-ed.y (), ed.x ()); + nd1 *= (forward ? w->second : w->first) * 0.5; + db::DVector nd2 (-ed.y (), ed.x ()); + nd2 *= wi * 0.5; + + db::DVector nnd1 (-eed.y (), eed.x ()); + nnd1 *= wi * 0.5; + db::DVector nnd2 (-eed.y (), eed.x ()); + nnd2 *= (forward ? www->first : www->second) * 0.5; + + bool is_folded = false; + + double du = db::vprod (ed, eed); + if (fabs (du) > db::epsilon) { + + double u1 = db::vprod (nnd1 - nd2, eed) / du; + double u2 = db::vprod (nd2 - nnd1, ed) / du; + + is_folded = ((u1 < -db::epsilon) != (u2 < -db::epsilon)); + + } + + if (is_folded) { + + // No well-formed intersection (reflecting/back-folded segments) -> + // create a direct (inner) connection + *pts++ = *pp + vector (nd2); + *pts++ = *pp + vector (nnd1); + + } else { + + db::DVector g = (db::DPoint (*pp) + nd2) - (db::DPoint (*p) + nd1); + double gl = g.double_length (); + g *= 1.0 / gl; + db::DVector gg = (db::DPoint (*ppp) + nnd2) - (db::DPoint (*pp) + nnd1); + double ggl = gg.double_length (); + gg *= 1.0 / ggl; + + double l1max = wi; + double l2max = wi; + + double l1min = -gl - wi; + double l2min = -ggl - wi; + + double dv = db::vprod (g, gg); + if (fabs (dv) > db::epsilon) { + + double l1 = db::vprod (nnd1 - nd2, gg) / dv; + double l2 = db::vprod (nd2 - nnd1, g) / dv; + + if (l1 < l1min - db::epsilon || l2 < l2min - db::epsilon) { + + // Segments are too short - the won't intersect: In this case we create a loop of three + // points which define the area in self-overlapping way but confined to the path within + // the limits of it's width. + // HINT: the execution of this code is a pretty strong evidence for the existance to loops + // in the contour delivered. A proof however is missing .. + *pts++ = *pp + vector (nd2); + *pts++ = *pp; + *pts++ = *pp + vector (nnd1); + + } else if (l1 < l1max + db::epsilon && l2 < l2max + db::epsilon) { + + // well-formed corner + *pts++ = *pp + vector (nd2 + g * l1); + + } else { + + // cut-off corner: produce two points connecting the edges + *pts++ = *pp + vector (nd2 + g * std::min (l1max, l1)); + *pts++ = *pp + vector (nnd1 - gg * std::min (l2max, l2)); + + } + + } else if (db::sprod (g, gg) < -db::epsilon) { + + // reflecting segment + *pts++ = *pp + vector (nd2 + g * wi); + *pts++ = *pp + vector (nnd1 - gg * wi); + + } + + } + + } + + p = pp; + pp = ppp; + w = ww; + ww = www; + + } +} + +template +typename variable_width_path::simple_polygon_type variable_width_path::to_poly () const +{ + std::vector > pts; + pts.reserve (m_points.size () * 2); // minimum number of points required + + C c = 0; + create_shifted_points (c, true, m_points.begin (), m_points.end (), m_widths.begin (), m_widths.end (), std::back_inserter (pts)); + create_shifted_points (c, false, m_points.rbegin (), m_points.rend (), m_widths.rbegin (), m_widths.rend (), std::back_inserter (pts)); + + simple_polygon_type poly; + poly.assign_hull (pts.begin (), pts.end ()); + return poly; +} + +template class variable_width_path; +template class variable_width_path; + +} diff --git a/src/db/db/dbVariableWidthPath.h b/src/db/db/dbVariableWidthPath.h new file mode 100644 index 000000000..cdd5d7c32 --- /dev/null +++ b/src/db/db/dbVariableWidthPath.h @@ -0,0 +1,123 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 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_dbVariableWidthPath +#define HDR_dbVariableWidthPath + +#include "dbPoint.h" +#include "dbPolygon.h" + +#include +#include + +namespace db +{ + +/** + * @brief A class representing a variable width path + * + * A variable-width path is a path which has a non-constant width over it's + * length. A width can be assigned to certain points and will be interpolated + * for other points. Interpolation is performed along the length of the + * path's spine. + * + * The initial and final width must be specified. A point can be assigned two + * widths: an incoming and an outgoing width. If one width is specified, the + * incoming and outgoing widths are the same. + */ +template +class DB_PUBLIC variable_width_path +{ +public: + typedef db::point point_type; + typedef db::simple_polygon simple_polygon_type; + typedef C width_type; + typedef std::pair width_spec_type; + + /** + * @brief Constructor from a set of points and width specifications + * + * I is an iterator delivering point_type objects. + * + * J is an iterator delivering width_spec_type objects. + * + * The width specification is a list of point index and width. The + * list must be sorted ascending by index. One index can be present + * twice. In this case, the first specification will be the incoming + * width, the second one will be the outgoing width. + * + * The first element of the width specification needs to be + * the initial width (0, w1) and the last element needs to be + * the final width (n-1, w2) where n is the number of points. + */ + template + variable_width_path (I b, I e, J bs, J es) + : m_points (b, e), m_org_widths (bs, es) + { + init (); + } + + /** + * @brief Constructor with a transformation + */ + template + variable_width_path (I b, I e, J bs, J es, const T &trans) + { + for (I i = b; i != e; ++i) { + m_points.push_back (trans.trans (*i)); + } + for (J j = bs; j != es; ++j) { + m_org_widths.push_back (std::make_pair (j->first, trans.ctrans (j->second))); + } + + init (); + } + + /** + * @brief Turns the variable-width path into a polygon + */ + simple_polygon_type to_poly () const; + +private: + void init (); + + std::vector m_points; + std::vector > m_widths; + std::vector > m_org_widths; +}; + +/** + * @brief The integer-type variable-width path + */ +typedef variable_width_path VariableWidthPath; + +/** + * @brief The float-type variable-width path + */ +typedef variable_width_path DVariableWidthPath; + +} + +#endif + + diff --git a/src/db/unit_tests/dbDXFReader.cc b/src/db/unit_tests/dbDXFReader.cc index a9a6d5d01..49d560e1e 100644 --- a/src/db/unit_tests/dbDXFReader.cc +++ b/src/db/unit_tests/dbDXFReader.cc @@ -290,3 +290,8 @@ TEST(29d) run_test (_this, "t29.dxf.gz", "t29d_au.gds.gz", 0, 0.001, 1, 4, 1000, 0.001); } +TEST(30) +{ + run_test (_this, "t30.dxf.gz", "t30d_au.gds.gz", 0, 0.001, 1000, 4, 1000, 0.001); +} + diff --git a/src/db/unit_tests/dbVariableWidthPath.cc b/src/db/unit_tests/dbVariableWidthPath.cc new file mode 100644 index 000000000..b835342d2 --- /dev/null +++ b/src/db/unit_tests/dbVariableWidthPath.cc @@ -0,0 +1,115 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 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 "dbVariableWidthPath.h" +#include "tlUnitTest.h" + +TEST(EmptyVP) +{ + db::Point pts[] = { }; + std::pair widths[] = { }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "()"); +} + +TEST(VP1Point) +{ + db::Point pts[] = { db::Point (0, 0) }; + std::pair widths[] = { }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "()"); +} + +TEST(VP2Point) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (200, 0) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (1), 50) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(0,-50;0,50;200,25;200,-25)"); +} + +TEST(VP3Point_Interpolate) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (200, 0) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (2), 50) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(0,-50;0,50;200,25;200,-25)"); +} + +TEST(VP3Point_Step) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (200, 0) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (1), 100), std::make_pair (size_t (1), 50), std::make_pair (size_t (2), 50) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(0,-50;0,50;100,50;100,25;200,25;200,-25;100,-25;100,-50)"); +} + +TEST(VP3Point_Step2) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (100, 0), db::Point (200, 0) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (1), 100), std::make_pair (size_t (2), 50), std::make_pair (size_t (3), 50) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(0,-50;0,50;100,50;100,25;200,25;200,-25;100,-25;100,-50)"); +} + +TEST(VP3Point90_Step) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (100, -100) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (1), 100), std::make_pair (size_t (1), 50), std::make_pair (size_t (2), 50) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(75,-100;75,0;100,-50;0,-50;0,50;100,50;125,0;125,-100)"); +} + +TEST(VP3Point90) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (100, -100) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (2), 0) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(100,-100;82,-29;0,-50;0,50;129,18)"); +} + +TEST(VP3Point90_ConstWidth) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (100, -100) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (2), 100) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(50,-100;50,-50;0,-50;0,50;150,50;150,-100)"); +} + +TEST(VP3Point135_ConstWidth) +{ + db::Point pts[] = { db::Point (0, 0), db::Point (100, 0), db::Point (0, -100) }; + std::pair widths[] = { std::make_pair (size_t (0), 100), std::make_pair (size_t (2), 100) }; + + db::VariableWidthPath vp (&pts[0], &pts[sizeof (pts) / sizeof (pts[0])], &widths[0], &widths[sizeof (widths) / sizeof (widths[0])]); + EXPECT_EQ (vp.to_poly ().to_string (), "(35,-135;-35,-65;-21,-50;0,-50;0,50;200,50;206,35)"); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 48df8e9a6..5f88cde6f 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -58,6 +58,7 @@ SOURCES = \ dbTrans.cc \ dbVector.cc \ dbWriterTools.cc \ + dbVariableWidthPath.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC