mirror of https://github.com/KLayout/klayout.git
Variable path widths for DXF reader
This commit is contained in:
parent
59dd9896e0
commit
2fd33a289a
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<db::DPoint> points;
|
||||
std::vector<std::pair<size_t, double> > 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<std::pair<size_t, double> >::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 <bool, unsigned int> 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);
|
||||
|
|
|
|||
|
|
@ -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 <class C>
|
||||
void
|
||||
variable_width_path<C>::init ()
|
||||
{
|
||||
// compress the points
|
||||
|
||||
typename std::vector<db::point<C> >::iterator pw = m_points.begin ();
|
||||
typename std::vector<db::point<C> >::const_iterator pr = m_points.begin ();
|
||||
|
||||
typename std::vector<std::pair<size_t, C> >::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<std::pair<size_t, C> >::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<C>::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 <class C, class Iter, class WIter, class Inserter>
|
||||
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<C> (nd));
|
||||
|
||||
}
|
||||
|
||||
if (ppp == to) {
|
||||
|
||||
db::DVector nd (-ed.y (), ed.x ());
|
||||
nd *= (forward ? ww->first : ww->second) * 0.5;
|
||||
|
||||
*pts++ = (*pp + vector<C> (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<C> (nd);
|
||||
*pts++ = *pp + vector<C> (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<C> (nd2);
|
||||
*pts++ = *pp + vector<C> (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<C> (nd2);
|
||||
*pts++ = *pp;
|
||||
*pts++ = *pp + vector<C> (nnd1);
|
||||
|
||||
} else if (l1 < l1max + db::epsilon && l2 < l2max + db::epsilon) {
|
||||
|
||||
// well-formed corner
|
||||
*pts++ = *pp + vector<C> (nd2 + g * l1);
|
||||
|
||||
} else {
|
||||
|
||||
// cut-off corner: produce two points connecting the edges
|
||||
*pts++ = *pp + vector<C> (nd2 + g * std::min (l1max, l1));
|
||||
*pts++ = *pp + vector<C> (nnd1 - gg * std::min (l2max, l2));
|
||||
|
||||
}
|
||||
|
||||
} else if (db::sprod (g, gg) < -db::epsilon) {
|
||||
|
||||
// reflecting segment
|
||||
*pts++ = *pp + vector<C> (nd2 + g * wi);
|
||||
*pts++ = *pp + vector<C> (nnd1 - gg * wi);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p = pp;
|
||||
pp = ppp;
|
||||
w = ww;
|
||||
ww = www;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class C>
|
||||
typename variable_width_path<C>::simple_polygon_type variable_width_path<C>::to_poly () const
|
||||
{
|
||||
std::vector<point<C> > 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<Coord>;
|
||||
template class variable_width_path<DCoord>;
|
||||
|
||||
}
|
||||
|
|
@ -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 <vector>
|
||||
#include <algorithm>
|
||||
|
||||
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 C>
|
||||
class DB_PUBLIC variable_width_path
|
||||
{
|
||||
public:
|
||||
typedef db::point<C> point_type;
|
||||
typedef db::simple_polygon<C> simple_polygon_type;
|
||||
typedef C width_type;
|
||||
typedef std::pair<size_t, width_type> 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 <class I, class J>
|
||||
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 <class I, class J, class T>
|
||||
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<point_type> m_points;
|
||||
std::vector<std::pair<width_type, width_type> > m_widths;
|
||||
std::vector<std::pair<size_t, C> > m_org_widths;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The integer-type variable-width path
|
||||
*/
|
||||
typedef variable_width_path<db::Coord> VariableWidthPath;
|
||||
|
||||
/**
|
||||
* @brief The float-type variable-width path
|
||||
*/
|
||||
typedef variable_width_path<db::DCoord> DVariableWidthPath;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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<size_t, db::Coord> 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)");
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue