/* 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 static db::DEdge compute_shifted (const db::edge &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 inline db::DVector dpx (const db::DVector &p, double d); template <> inline db::DVector dpx (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::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::rounded (d * M_SQRT1_2)); } else { return p * d; } } template <> inline db::DVector dpx (const db::DVector &p, double d) { // No need to round in the double case return p * d; } template static void compute_normals (const db::vector &d, C dx, C dy, int nsign, db::DVector &ed, db::DVector &nd) { if (db::coord_traits::equal (dx, dy)) { // Simplified handling for the isotropic case double f = d.double_length (); if (f < db::coord_traits::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 (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::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 void polygon_contour::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 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 > pts (new_points); tl_assert (*pp != *p); // no coincident points allowed db::vector 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 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 (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 (nd); *pts++ = *pp; } else if (vpsign == 0 && db::sprod_sign (nd, nnd) > 0) { // colinear edges: simply shift the point *pts++ = *pp + vector (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 (nd); *pts++ = *pp; *pts++ = *pp + vector (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 (nd); *pts++ = *pp + vector (nnd); } else if (l1 < l1max + db::epsilon && l2 < l2max + db::epsilon) { // well-formed corner *pts++ = *pp + vector (nd + ed * l1); } else { // cut-off corner: produce two points connecting the edges *pts++ = *pp + vector (nd + ed * std::min (l1max, l1)); *pts++ = *pp + vector (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 (), 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 new_points; new_points.reserve (npts); // create a vector where we remember what edge is obsolete since it became inverted std::vector 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 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 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 ip = lie_s.intersect_point (cie_s); // last output point db::point lop; // If the lines intersect, we have a well-formed inner or outer corner if (ip.first) { lop = point::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::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 ((*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 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 o (lop, point::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 o (lop, point::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::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 and simple_polygon template class polygon_contour; template class polygon_contour; } 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 DB_PUBLIC bool _test_extractor_impl (tl::Extractor &ex, db::polygon &p) { typedef db::point point_type; std::vector 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 DB_PUBLIC bool _test_extractor_impl (tl::Extractor &ex, db::simple_polygon &p) { typedef db::point point_type; std::vector 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); } }