/*
* 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 .
*/
#include
#include
#include
#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 */