mirror of https://github.com/KLayout/klayout.git
540 lines
14 KiB
C++
540 lines
14 KiB
C++
|
|
/*
|
|
|
|
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 "dbPolygon.h"
|
|
|
|
namespace db
|
|
{
|
|
|
|
template <class C>
|
|
static
|
|
db::DEdge compute_shifted (const db::edge<C> &e, C dx, C dy, double ext, int nsign)
|
|
{
|
|
tl_assert (! e.is_degenerate ()); // no coincident points allowed
|
|
|
|
// Compute the unit vector of the line and it's normal (times width)
|
|
db::DVector ec (e.d ());
|
|
ec *= 1.0 / ec.double_length ();
|
|
db::DVector nc (-ec.y (), ec.x ());
|
|
|
|
ec *= sqrt (ec.x () * ec.x () * dx * dx + ec.y () * ec.y () * dy * dy) * ext;
|
|
nc *= sqrt (nc.x () * nc.x () * dx * dx + nc.y () * nc.y () * dy * dy) * nsign;
|
|
|
|
// We create two test lines for the adjacent edges that extend somewhat further (i.e by half
|
|
// the width). These test lines define the limits of the area where the segments are responsible
|
|
return db::DEdge (db::DPoint (e.p1 ()) + nc - ec, db::DPoint (e.p2 ()) + nc + ec);
|
|
}
|
|
|
|
/**
|
|
* @brief Smart multiplication of a vector with a distance
|
|
* This function tries to keep the length of the vector on grid if it's
|
|
* a 45 degree or horizontal/vertical one.
|
|
*/
|
|
template <class C>
|
|
inline db::DVector dpx (const db::DVector &p, double d);
|
|
|
|
template <>
|
|
inline db::DVector dpx<Coord> (const db::DVector &p, double d)
|
|
{
|
|
// Note: "up" mode is used for computing the extension. Some bigger value
|
|
// is helpful in avoiding the case of too short extensions causing
|
|
// a missing intersection even in non-acute angle case
|
|
if (fabs (p.x ()) < db::epsilon || fabs (p.y ()) < db::epsilon) {
|
|
return p * double (coord_traits<Coord>::rounded (d));
|
|
} else if (fabs (fabs (p.x ()) - fabs (p.y ())) < db::epsilon) {
|
|
// 45 degree case: try to round d such that if p is on the grid it will be later
|
|
return p * (M_SQRT2 * coord_traits<Coord>::rounded (d * M_SQRT1_2));
|
|
} else {
|
|
return p * d;
|
|
}
|
|
}
|
|
|
|
template <>
|
|
inline db::DVector dpx<DCoord> (const db::DVector &p, double d)
|
|
{
|
|
// No need to round in the double case
|
|
return p * d;
|
|
}
|
|
|
|
template <class C>
|
|
static void
|
|
compute_normals (const db::vector<C> &d, C dx, C dy, int nsign, db::DVector &ed, db::DVector &nd)
|
|
{
|
|
if (db::coord_traits<C>::equal (dx, dy)) {
|
|
|
|
// Simplified handling for the isotropic case
|
|
double f = d.double_length ();
|
|
|
|
if (f < db::coord_traits<DCoord>::prec_distance ()) {
|
|
|
|
// TODO: this should never happen (we assert before that d is not 0)
|
|
ed = db::DVector ();
|
|
nd = db::DVector ();
|
|
|
|
} else {
|
|
|
|
ed = db::DVector (d) * (1.0 / f);
|
|
nd = db::DVector (-ed.y (), ed.x ());
|
|
|
|
// dpx is a smart multiplication trying to preserve 45 degree edges on grid
|
|
nd = dpx<C> (nd, fabs (double (dx)) * nsign);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
double f = sqrt(double (dx) * double (dx) * double (d.y()) * double (d.y()) + double (dy) * double (dy) * double (d.x()) * double (d.x()));
|
|
if (f < db::coord_traits<DCoord>::prec_area ()) {
|
|
|
|
if (dx == 0) {
|
|
ed = db::DVector (0.0, 1.0);
|
|
} else if (dy == 0) {
|
|
ed = db::DVector (1.0, 0.0);
|
|
} else {
|
|
ed = db::DVector ();
|
|
}
|
|
|
|
nd = db::DVector();
|
|
|
|
} else {
|
|
|
|
ed = db::DVector (d) * (double (dx) * double (dy) / f);
|
|
|
|
nd = db::DVector (double (-d.y ()) * double (dx) * double (dx), double (d.x ()) * double (dy) * double (dy));
|
|
nd *= nsign / f;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
template <class C>
|
|
void polygon_contour<C>::size (C dx, C dy, unsigned int mode)
|
|
{
|
|
if (dx == 0 && dy == 0) {
|
|
return;
|
|
}
|
|
if (size () < 2) {
|
|
return;
|
|
}
|
|
|
|
double ext = 100.0;
|
|
if (mode == 0) {
|
|
ext = 0.0;
|
|
} else if (mode == 1) {
|
|
ext = sqrt(2.0) - 1.0;
|
|
} else if (mode == 2) {
|
|
ext = 1.0;
|
|
} else if (mode == 3) {
|
|
ext = sqrt(2.0) + 1.0;
|
|
} else if (mode == 4) {
|
|
ext = 10.0;
|
|
}
|
|
|
|
bool outside = (dx + dy) > 0;
|
|
int nsign = outside ? 1 : -1;
|
|
dx *= nsign;
|
|
dy *= nsign;
|
|
|
|
#if 1
|
|
// New algorithm: trying to preserve 45 degree angles
|
|
|
|
std::vector<point_type> new_points;
|
|
new_points.reserve (size () * 2);
|
|
|
|
simple_iterator p0 (this, 0);
|
|
simple_iterator pn (this, size ());
|
|
|
|
simple_iterator p = p0;
|
|
|
|
simple_iterator pp = p;
|
|
++pp;
|
|
|
|
std::back_insert_iterator<std::vector<point_type> > pts (new_points);
|
|
|
|
tl_assert (*pp != *p); // no coincident points allowed
|
|
|
|
db::vector<C> d (*pp - *p);
|
|
db::DVector ed, nd;
|
|
compute_normals (d, dx, dy, nsign, ed, nd);
|
|
|
|
do {
|
|
|
|
simple_iterator ppp = pp;
|
|
++ppp;
|
|
if (ppp == pn) {
|
|
ppp = p0;
|
|
}
|
|
|
|
tl_assert (*ppp != *pp); // no coincident points allowed
|
|
|
|
db::vector<C> dd (*ppp - *pp);
|
|
db::DVector eed, nnd;
|
|
compute_normals (dd, dx, dy, nsign, eed, nnd);
|
|
|
|
int vpsign = db::vprod_sign (eed, ed) * nsign;
|
|
|
|
if (vpsign <= 0) {
|
|
|
|
if (nd.double_length () < db::epsilon) {
|
|
|
|
// no shift implied by second edge: simply shift the point in the
|
|
// direction implied by the second edge and connect to the vertex
|
|
*pts++ = *pp;
|
|
*pts++ = *pp + vector<C> (nnd);
|
|
|
|
} else if (nnd.double_length () < db::epsilon) {
|
|
|
|
// no shift implied by second edge: simply shift the point in the
|
|
// direction implied by the first edge and connect to the vertex
|
|
*pts++ = *pp + vector<C> (nd);
|
|
*pts++ = *pp;
|
|
|
|
} else if (vpsign == 0 && db::sprod_sign (nd, nnd) > 0) {
|
|
|
|
// colinear edges: simply shift the point
|
|
*pts++ = *pp + vector<C> (nd);
|
|
|
|
} else {
|
|
|
|
// inner corner -> create a loop of three points which define the area
|
|
// in self-overlapping way but confined to the resulting contour
|
|
*pts++ = *pp + vector<C> (nd);
|
|
*pts++ = *pp;
|
|
*pts++ = *pp + vector<C> (nnd);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
double l1max = ext * nd.double_length () / ed.double_length ();
|
|
double l2max = ext * nnd.double_length () / eed.double_length ();
|
|
|
|
double dv = db::vprod (ed, eed);
|
|
|
|
double l1 = db::vprod (nnd - nd, eed) / dv;
|
|
double l2 = db::vprod (nd - nnd, ed) / dv;
|
|
|
|
if ((l1 < -db::epsilon) != (l2 < -db::epsilon)) {
|
|
|
|
// No well-formed intersection (reflecting edge) ->
|
|
// create a direct connection
|
|
*pts++ = *pp + vector<C> (nd);
|
|
*pts++ = *pp + vector<C> (nnd);
|
|
|
|
} else if (l1 < l1max + db::epsilon && l2 < l2max + db::epsilon) {
|
|
|
|
// well-formed corner
|
|
*pts++ = *pp + vector<C> (nd + ed * l1);
|
|
|
|
} else {
|
|
|
|
// cut-off corner: produce two points connecting the edges
|
|
*pts++ = *pp + vector<C> (nd + ed * std::min (l1max, l1));
|
|
*pts++ = *pp + vector<C> (nnd - eed * std::min (l2max, l2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = pp;
|
|
pp = ppp;
|
|
|
|
ed = eed;
|
|
nd = nnd;
|
|
|
|
d = dd;
|
|
|
|
} while (p != p0);
|
|
|
|
// assign the results
|
|
assign (new_points.begin (), new_points.end (), db::unit_trans<C> (), is_hole (), true /*compress*/, false /*don't normalize*/);
|
|
|
|
#else
|
|
|
|
size_t npts = size ();
|
|
|
|
if (npts < 2) {
|
|
return;
|
|
}
|
|
|
|
// create a vector for the output points
|
|
std::vector<point_type> new_points;
|
|
new_points.reserve (npts);
|
|
|
|
// create a vector where we remember what edge is obsolete since it became inverted
|
|
std::vector<short> inverted;
|
|
inverted.resize (npts, 0);
|
|
size_t nvalid = npts;
|
|
size_t nvalid_last = 0;
|
|
|
|
while (nvalid >= 2 && nvalid_last != nvalid) {
|
|
|
|
nvalid_last = nvalid;
|
|
|
|
new_points.clear ();
|
|
|
|
// find the first and second valid edge:
|
|
// lie = last input edge, cie = current input edges
|
|
|
|
unsigned int i = 0;
|
|
|
|
while (i < npts && inverted[i]) { ++i; }
|
|
|
|
tl_assert (i != npts);
|
|
db::edge<C> lie ((*this)[i], (*this)[(i + 1) % npts]);
|
|
db::DEdge lie_s (compute_shifted (lie, dx, dy, ext, nsign));
|
|
unsigned int lie_index = i;
|
|
|
|
do { ++i; } while (i < npts && inverted[i]);
|
|
|
|
tl_assert (i != npts);
|
|
db::edge<C> cie ((*this)[i], (*this)[(i + 1) % npts]);
|
|
db::DEdge cie_s (compute_shifted (cie, dx, dy, ext, nsign));
|
|
unsigned int cie_index = i;
|
|
|
|
// Do an intersection test on these lines
|
|
std::pair <bool, db::DPoint> ip = lie_s.intersect_point (cie_s);
|
|
|
|
// last output point
|
|
db::point<C> lop;
|
|
|
|
// If the lines intersect, we have a well-formed inner or outer corner
|
|
if (ip.first) {
|
|
lop = point<C>::from_double (ip.second);
|
|
} else {
|
|
// If the test lines to not cross, we have the case of an acute angle bend.
|
|
// This is a normal outer bend: we insert both points to define the contour in a
|
|
// confined, cut-off fashion.
|
|
lop = point<C>::from_double (cie_s.p1 ());
|
|
}
|
|
|
|
// start with the next edge
|
|
i = (i + 1) % npts;
|
|
unsigned int ii;
|
|
|
|
unsigned int llie_index;
|
|
|
|
for (unsigned int j = 0; j < npts; ++j, i = ii) {
|
|
|
|
ii = ((i + 1) >= npts ? 0 : (i + 1));
|
|
|
|
// ignore inverted edges now.
|
|
if (inverted[i]) {
|
|
continue;
|
|
}
|
|
|
|
llie_index = lie_index;
|
|
|
|
lie = cie;
|
|
lie_s = cie_s;
|
|
lie_index = cie_index;
|
|
|
|
cie = db::edge<C> ((*this)[i], (*this)[ii]);
|
|
cie_s = compute_shifted (cie, dx, dy, ext, nsign);
|
|
cie_index = i;
|
|
|
|
// Do an intersection test on these lines
|
|
std::pair <bool, db::DPoint> ip = lie_s.intersect_point (cie_s);
|
|
|
|
// If the lines intersect, we have a well-formed inner or outer corner
|
|
if (ip.first && ! lie_s.parallel (cie_s)) {
|
|
|
|
// compute next output edge (corresponding to last input edge)
|
|
db::edge<C> o (lop, point<C>::from_double (ip.second));
|
|
|
|
int s = sprod_sign (o, lie);
|
|
if (s > 0) {
|
|
// No inversion: output new point
|
|
if (new_points.empty () || new_points.back () != lop) {
|
|
new_points.push_back (lop);
|
|
}
|
|
new_points.push_back (o.p2 ());
|
|
} else if (s < 0) {
|
|
// mark this edge as inverted
|
|
inverted[lie_index] = 1;
|
|
--nvalid;
|
|
}
|
|
|
|
lop = o.p2 ();
|
|
|
|
} else {
|
|
|
|
// compute next output edge (corresponding to last input edge)
|
|
db::edge<C> o (lop, point<C>::from_double (lie_s.p2 ()));
|
|
|
|
int s = sprod_sign (o, lie);
|
|
if (s > 0) {
|
|
// No inversion: output new point
|
|
if (! new_points.empty () && new_points.back () != lop) {
|
|
new_points.push_back (lop);
|
|
}
|
|
} else if (s < 0) {
|
|
// mark this edge as inverted
|
|
inverted[lie_index] = 1;
|
|
--nvalid;
|
|
}
|
|
|
|
new_points.push_back (o.p2 ());
|
|
|
|
lop = point<C>::from_double (cie_s.p1 ());
|
|
new_points.push_back (lop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// assign the results
|
|
assign (new_points.begin (), new_points.end (), db::unit_trans (), is_hole (), true /*compress*/, false /*don't normalize*/);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
// explicit instantiations for polygon<T> and simple_polygon<T>
|
|
template class polygon_contour<db::Coord>;
|
|
template class polygon_contour<db::DCoord>;
|
|
|
|
}
|
|
|
|
namespace tl
|
|
{
|
|
|
|
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Polygon &p)
|
|
{
|
|
if (! test_extractor_impl (ex, p)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a polygon specification")));
|
|
}
|
|
}
|
|
|
|
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::DPolygon &p)
|
|
{
|
|
if (! test_extractor_impl (ex, p)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a polygon specification")));
|
|
}
|
|
}
|
|
|
|
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::SimplePolygon &p)
|
|
{
|
|
if (! test_extractor_impl (ex, p)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a polygon specification")));
|
|
}
|
|
}
|
|
|
|
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::DSimplePolygon &p)
|
|
{
|
|
if (! test_extractor_impl (ex, p)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a polygon specification")));
|
|
}
|
|
}
|
|
|
|
|
|
template<class C> DB_PUBLIC bool _test_extractor_impl (tl::Extractor &ex, db::polygon<C> &p)
|
|
{
|
|
typedef db::point<C> point_type;
|
|
std::vector <point_type> points;
|
|
|
|
if (ex.test ("(")) {
|
|
|
|
point_type pt;
|
|
while (ex.try_read (pt)) {
|
|
points.push_back (pt);
|
|
ex.test (";");
|
|
}
|
|
|
|
p.assign_hull (points.begin (), points.end (), false, false);
|
|
|
|
while (ex.test ("/")) {
|
|
|
|
points.clear ();
|
|
|
|
point_type pt;
|
|
while (ex.try_read (pt)) {
|
|
points.push_back (pt);
|
|
ex.test (";");
|
|
}
|
|
|
|
p.insert_hole (points.begin (), points.end (), false, false);
|
|
|
|
}
|
|
|
|
ex.expect (")");
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Polygon &p)
|
|
{
|
|
return _test_extractor_impl (ex, p);
|
|
}
|
|
|
|
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::DPolygon &p)
|
|
{
|
|
return _test_extractor_impl (ex, p);
|
|
}
|
|
|
|
template<class C> DB_PUBLIC bool _test_extractor_impl (tl::Extractor &ex, db::simple_polygon<C> &p)
|
|
{
|
|
typedef db::point<C> point_type;
|
|
std::vector <point_type> points;
|
|
|
|
if (ex.test ("(")) {
|
|
|
|
point_type pt;
|
|
while (ex.try_read (pt)) {
|
|
points.push_back (pt);
|
|
ex.test (";");
|
|
}
|
|
|
|
p.assign_hull (points.begin (), points.end (), false, false);
|
|
|
|
ex.expect (")");
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::SimplePolygon &p)
|
|
{
|
|
return _test_extractor_impl (ex, p);
|
|
}
|
|
|
|
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::DSimplePolygon &p)
|
|
{
|
|
return _test_extractor_impl (ex, p);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|