diff options
Diffstat (limited to 'hyp2mat/lib/polygon.cc')
-rw-r--r-- | hyp2mat/lib/polygon.cc | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/hyp2mat/lib/polygon.cc b/hyp2mat/lib/polygon.cc new file mode 100644 index 0000000..5d3d9d1 --- /dev/null +++ b/hyp2mat/lib/polygon.cc @@ -0,0 +1,335 @@ +/* + * hyp2mat - convert hyperlynx files to matlab scripts + * Copyright 2012 Koen De Vleeschauwer. + * + * This file is part of hyp2mat. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <cmath> +#include <iostream> +#include <algorithm> + +#include "polygon.h" + +using namespace Hyp2Mat; + +double Polygon::_grid = 10E-6; /* 10 micron */ +double Polygon::_arc_precision = 0.0; + +Polygon::Polygon() +{ + _filltype = ClipperLib::pftNonZero; // Use edge orientation to determine whether or not polygon edge is a hole. +} + +void Polygon::SetGrid(double new_grid) +{ + _grid = new_grid; +} + +void Polygon::SetArcPrecision(double new_arc_precision) +{ + _arc_precision = new_arc_precision; +} + +/* + * Align coordinates to grid + */ + +double Polygon::AlignToGrid(double x) +{ + return floor(x / _grid + 0.5) * _grid; +} + +/* + * Return true if polygon is empty + */ + +bool Polygon::IsEmpty() +{ + return _subject.empty(); +} + +/* + * Add an outer edge to the polygon + */ + +void Polygon::AddEdge(FloatPolygon edge) +{ + _AddEdge(edge, true); +} + +/* + * Add an inner edge to the polygon + */ + +void Polygon::AddHole(FloatPolygon hole) +{ + _AddEdge(hole, false); +} + +/* + * Add an edge - positive or negative - to the polygon + */ + +void Polygon::_AddEdge(FloatPolygon edge, bool orientation) +{ + + /* trivial case */ + if (edge.empty()) return; + + /* convert edge to Clipper polygon */ + ClipperLib::Polygon poly = _convert(edge); + + /* orient edge clockwise */ + if (!Orientation(poly) == orientation) ClipperLib::ReversePolygon(poly); + + /* Add to polygon */ + _subject.push_back(poly); + + return; +} + +/* + * convert from Hyp2Mat floating point point to Clipper long64 point + */ + +ClipperLib::IntPoint Polygon::_convert(FloatPoint p) +{ + return ClipperLib::IntPoint(floor(p.x / _grid + 0.5), floor(p.y / _grid + 0.5)); +} + +/* + * convert from Hyp2Mat floating point polygon to Clipper long64 polygon + */ + +ClipperLib::Polygon Polygon::_convert(FloatPolygon edge) +{ + ClipperLib::Polygon poly; + for (FloatPolygon::iterator i = edge.begin(); i != edge.end(); ++i) + poly.push_back(_convert(*i)); + + /* remove repeated vertices */ + poly = _unique(poly); + + return poly; +} + +/* + * Remove repeated vertices from a polygon + */ + +ClipperLib::Polygon Polygon::_unique(ClipperLib::Polygon p) +{ + ClipperLib::Polygon q = p; + q.clear(); + + if (p.empty()) return q; + + /* initial condition: add first vertex */ + q.push_back(p.front()); + + /* create list of non-repeating vertices */ + for (ClipperLib::Polygon::iterator i = p.begin(); i != p.end(); ++i) + if ((i->X != q.back().X) || (i->Y != q.back().Y)) q.push_back(*i); + + /* Remove last vertex if first and last vertex same */ + while ((q.size() > 1) && ((q.front().X == q.back().X) && (q.front().Y == q.back().Y))) + q.pop_back(); + + return q; +} + +/* + * Expand the polygon by a distance + */ + +void Polygon::Offset(double delta) +{ + ClipperLib::Polygons result; + double d = delta / _grid; + double limit = _arc_precision / _grid; + + if (delta == 0.0) return; + + if (_arc_precision == 0) + /* square corners */ + OffsetPolygons(_subject, result, d, ClipperLib::jtSquare); + else + /* round corners */ + OffsetPolygons(_subject, result, d, ClipperLib::jtRound, limit); + + _subject = result; + + return; +} + +/* + * Remove self-intersections + */ + +void Polygon::Simplify() +{ + ClipperLib::Polygons result; + ClipperLib::SimplifyPolygons(_subject, result, _filltype); + _subject = result; + +} + +/* + * Basic polygon operations + */ + +void Polygon::Intersection(Polygon clip) +{ + _Execute(ClipperLib::ctIntersection, clip); +} + +void Polygon::Union(Polygon clip) +{ + _Execute(ClipperLib::ctUnion, clip); +} + +void Polygon::Difference(Polygon clip) +{ + _Execute(ClipperLib::ctDifference, clip); +} + +void Polygon::Xor(Polygon clip) +{ + _Execute(ClipperLib::ctXor, clip); +} + +void Polygon::_Execute(ClipperLib::ClipType op, Polygon clip) +{ + ClipperLib::Polygons result; + ClipperLib::Clipper clipper; + + clipper.AddPolygons(_subject, ClipperLib::ptSubject); + clipper.AddPolygons(clip._subject, ClipperLib::ptClip); + + clipper.Execute(op, result, _filltype, _filltype); + _subject = result; + + return; +} + +/* + * Convert from Clipper long64 polygon to Hyp2Mat floating point polygon + */ + +FloatPolygons Polygon::Result() +{ + ClipperLib::Clipper clipper; + ClipperLib::PolyTree polytree; + + /* convert to PolyTree */ + clipper.AddPolygons(_subject, ClipperLib::ptSubject); + clipper.Execute(ClipperLib::ctUnion, polytree, _filltype, _filltype); + + /* recursively descend polytree, converting nodes */ + FloatPolygons result; + for (int i = 0; i < polytree.ChildCount(); ++i) + _AddToPolygonList(*polytree.Childs[i], result, 0); + + return result; +} + +void Polygon::_AddToPolygonList(ClipperLib::PolyNode& polynode, FloatPolygons& polygons, int level) +{ + FloatPoly outer_edge; + + outer_edge.poly = _convert(polynode.Contour); + outer_edge.is_hole = !ClipperLib::Orientation(polynode.Contour); + outer_edge.nesting_level = level; + /* make outer edges of polygon clockwise; holes counterclockwise. XXX is this superfluous? */ + if (outer_edge.is_hole == IsClockwise(outer_edge.poly)) Reverse(outer_edge.poly); + + polygons.push_back(outer_edge); + + for (int i = 0; i < polynode.ChildCount(); ++i) { + FloatPoly inner_edge; + inner_edge.poly = _convert(polynode.Childs[i]->Contour); + inner_edge.is_hole = !ClipperLib::Orientation(polynode.Childs[i]->Contour); + inner_edge.nesting_level = level + 1; + /* make outer edges of polygon clockwise; holes counterclockwise. XXX is this superfluous? */ + if (inner_edge.is_hole == IsClockwise(inner_edge.poly)) Reverse(inner_edge.poly); + polygons.push_back(inner_edge); + } + + /* Add outer polygons nested within holes */ + for (int i = 0; i < polynode.ChildCount(); ++i) + for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) + _AddToPolygonList(*polynode.Childs[i]->Childs[j], polygons, level + 2); + + return; +} + + /* + * Convert edge back from ClipperLib to Hyp2Mat + */ + +FloatPolygon Polygon::_convert (ClipperLib::Polygon clip_poly) +{ + FloatPolygon edge; + + for (ClipperLib::Polygon::iterator i = clip_poly.begin(); i != clip_poly.end(); ++i) + edge.push_back(_convert(*i)); + + return edge; +} + + /* + * Convert point back from ClipperLib to Hyp2Mat + */ + +FloatPoint Polygon::_convert (ClipperLib::IntPoint p) +{ + return FloatPoint(p.X * _grid, p.Y * _grid); +} + +/* + * Calculate if a polygon is clockwise or not. + */ + +bool Hyp2Mat::IsClockwise(FloatPolygon& poly) { + + /* calculate area of polygon */ + double area = 0; + for (FloatPolygon::iterator i = poly.begin(); i != poly.end(); ++i) { + /* i points to current element; j points to next element */ + FloatPolygon::iterator j = i; + ++j; + /* polygon is closed */ + if (j == poly.end()) j = poly.begin(); + area += i->x * j->y - j->x * i->y; + } + + /* positive area is clockwise. Arbitrarily choose clockwise if area 0 */ + return area >= 0; + +} + +/* + * Reverse order of vertices. + */ + +void Hyp2Mat::Reverse(FloatPolygon& poly) +{ + std::reverse(poly.begin(), poly.end()); +} + + +/* not truncated */ |