Use CLipperLib's PolyTree to identify negative contours

527olive
Marius Kintel 2013-12-26 23:05:11 -05:00
parent 1c8221004a
commit 5a4ff8e39a
14 changed files with 151 additions and 104 deletions

View File

@ -63,6 +63,7 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset() const
if (this->dim == 3) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
ps = new PolySet();
ps->setConvexity(this->convexity);
bool err = true;
std::string errmsg("");
CGAL_Polyhedron P;

View File

@ -157,7 +157,7 @@ Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node)
std::list<CGAL_Nef_polyhedron2::Point> points;
BOOST_FOREACH(const Polygon2d *p, children) {
BOOST_FOREACH(const Outline2d &o, p->outlines()) {
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
points.push_back(CGAL_Nef_polyhedron2::Point(v[0], v[1]));
}
}
@ -170,7 +170,7 @@ Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node)
// Construct Polygon2d
Outline2d outline;
BOOST_FOREACH(const CGAL_Nef_polyhedron2::Point &p, result) {
outline.push_back(Vector2d(CGAL::to_double(p[0]), CGAL::to_double(p[1])));
outline.vertices.push_back(Vector2d(CGAL::to_double(p[0]), CGAL::to_double(p[1])));
}
geometry = new Polygon2d();
geometry->addOutline(outline);
@ -251,14 +251,14 @@ Polygon2d *GeometryEvaluator::applyMinkowski2D(const AbstractNode &node)
// The results may contain holes due to ClipperLib failing to maintain
// solidity of minkowski results:
// https://sourceforge.net/p/polyclipping/discussion/1148419/thread/8488d4e8/
ClipperLib::Clipper clipper;
ClipperLib::Paths paths;
BOOST_FOREACH(ClipperLib::Path &p, result) {
if (ClipperLib::Orientation(p)) std::reverse(p.begin(), p.end());
clipper.AddPath(p, ClipperLib::ptSubject, true);
paths.push_back(p);
}
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
return ClipperUtils::toPolygon2d(result);
std::vector<ClipperLib::Paths> pathsvector;
pathsvector.push_back(paths);
return ClipperUtils::apply(pathsvector, ClipperLib::ctUnion);
}
return NULL;
}
@ -416,28 +416,42 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
return ContinueTraversal;
}
/*
FIXME: Where do we handle nodes which should be sent to CGAL?
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const Geometry> geom;
if (!isCached(node)) {
CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
CGALCache::instance()->insert(this->tree.getIdString(node), N);
PolySet *ps = NULL;
if (!N.isNull()) ps = N.convertToPolyset();
geom.reset(ps);
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
}
return ContinueTraversal;
/*!
RenderNodes just pass on convexity
*/
Response GeometryEvaluator::visit(State &state, const RenderNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const class Geometry> geom;
if (!isCached(node)) {
ResultObject res = applyToChildren(node, OPENSCAD_UNION);
geom = res.constptr();
if (shared_ptr<const PolySet> ps = dynamic_pointer_cast<const PolySet>(geom)) {
// If we got a const object, make a copy
shared_ptr<PolySet> newps;
if (res.isConst()) newps.reset(new PolySet(*ps));
else newps = dynamic_pointer_cast<PolySet>(res.ptr());
newps->setConvexity(node.convexity);
geom = newps;
}
else if (shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom)) {
// If we got a const object, make a copy
shared_ptr<CGAL_Nef_polyhedron> newN;
if (res.isConst()) newN.reset(new CGAL_Nef_polyhedron(*N));
else newN = dynamic_pointer_cast<CGAL_Nef_polyhedron>(res.ptr());
newN->setConvexity(node.convexity);
geom = newN;
}
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
}
return ContinueTraversal;
}
/*!
Leaf nodes can create their own geometry, so let them do that
@ -579,14 +593,15 @@ static void add_slice(PolySet *ps, const Polygon2d &poly,
// FIXME: If scale2 == 0 we need to handle tessellation separately
bool splitfirst = sin(rot1 - rot2) >= 0.0;
BOOST_FOREACH(const Outline2d &o, poly.outlines()) {
Vector2d prev1 = trans1 * o[0];
Vector2d prev2 = trans2 * o[0];
for (size_t i=1;i<=o.size();i++) {
Vector2d curr1 = trans1 * o[i % o.size()];
Vector2d curr2 = trans2 * o[i % o.size()];
Vector2d prev1 = trans1 * o.vertices[0];
Vector2d prev2 = trans2 * o.vertices[0];
for (size_t i=1;i<=o.vertices.size();i++) {
Vector2d curr1 = trans1 * o.vertices[i % o.vertices.size()];
Vector2d curr2 = trans2 * o.vertices[i % o.vertices.size()];
ps->append_poly();
if (splitfirst) {
// Make sure to split negative outlines correctly
if (splitfirst xor !o.positive) {
ps->insert_vertex(prev1[0], prev1[1], h1);
ps->insert_vertex(curr2[0], curr2[1], h2);
ps->insert_vertex(curr1[0], curr1[1], h1);
@ -617,6 +632,13 @@ static void add_slice(PolySet *ps, const Polygon2d &poly,
/*!
Input to extrude should be clean. This means non-intersecting, correct winding order
etc., the input coming from a library like Clipper.
We need to split quads in the same way (for e.g. thin shells). To do
this, we need to know which contours are negative vs. positive:
o Flag per contour (when sanitized)?
o Hierarchy of contours?
FIXME: This is probably also important for rotate_extrude()
*/
static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d &poly)
{
@ -712,10 +734,10 @@ Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node)
static void fill_ring(std::vector<Vector3d> &ring, const Outline2d &o, double a)
{
for (int i=0;i<o.size();i++) {
ring[i][0] = o[i][0] * sin(a);
ring[i][1] = o[i][0] * cos(a);
ring[i][2] = o[i][1];
for (int i=0;i<o.vertices.size();i++) {
ring[i][0] = o.vertices[i][0] * sin(a);
ring[i][1] = o.vertices[i][0] * cos(a);
ring[i][2] = o.vertices[i][1];
}
}
@ -731,7 +753,7 @@ static Geometry *rotatePolygon(const RotateExtrudeNode &node, const Polygon2d &p
BOOST_FOREACH(const Outline2d &o, poly.outlines()) {
double min_x = 0;
double max_x = 0;
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
min_x = fmin(min_x, v[0]);
max_x = fmax(max_x, v[0]);
@ -744,23 +766,23 @@ static Geometry *rotatePolygon(const RotateExtrudeNode &node, const Polygon2d &p
int fragments = get_fragments_from_r(max_x - min_x, node.fn, node.fs, node.fa);
std::vector<Vector3d> rings[2];
rings[0].reserve(o.size());
rings[1].reserve(o.size());
rings[0].resize(o.vertices.size());
rings[1].resize(o.vertices.size());
fill_ring(rings[0], o, -M_PI/2); // first ring
for (int j = 0; j < fragments; j++) {
double a = ((j+1)%fragments*2*M_PI) / fragments - M_PI/2; // start on the X axis
fill_ring(rings[(j+1)%2], o, a);
for (size_t i=0;i<o.size();i++) {
for (size_t i=0;i<o.vertices.size();i++) {
ps->append_poly();
ps->insert_vertex(rings[j%2][i]);
ps->insert_vertex(rings[(j+1)%2][(i+1)%o.size()]);
ps->insert_vertex(rings[j%2][(i+1)%o.size()]);
ps->insert_vertex(rings[(j+1)%2][(i+1)%o.vertices.size()]);
ps->insert_vertex(rings[j%2][(i+1)%o.vertices.size()]);
ps->append_poly();
ps->insert_vertex(rings[j%2][i]);
ps->insert_vertex(rings[(j+1)%2][i]);
ps->insert_vertex(rings[(j+1)%2][(i+1)%o.size()]);
ps->insert_vertex(rings[(j+1)%2][(i+1)%o.vertices.size()]);
}
}
}
@ -884,11 +906,11 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
sumclipper.AddPaths(result, ClipperLib::ptSubject, true);
}
}
ClipperLib::Paths sumresult;
ClipperLib::PolyTree sumresult;
// This is key - without StrictlySimple, we tend to get self-intersecting results
sumclipper.StrictlySimple(true);
sumclipper.Execute(ClipperLib::ctUnion, sumresult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
if (sumresult.size() > 0) geom.reset(ClipperUtils::toPolygon2d(sumresult));
if (sumresult.Total() > 0) geom.reset(ClipperUtils::toPolygon2d(sumresult));
}
else {
shared_ptr<const Geometry> newgeom = applyToChildren3D(node, OPENSCAD_UNION).constptr();

View File

@ -30,6 +30,7 @@ public:
virtual Response visit(State &state, const CsgNode &node);
virtual Response visit(State &state, const CgaladvNode &node);
virtual Response visit(State &state, const ProjectionNode &node);
virtual Response visit(State &state, const RenderNode &node);
const Tree &getTree() const { return this->tree; }

View File

@ -113,8 +113,8 @@ PolySet *Polygon2d::tessellate() const
// Adds all vertices, and add all contours as constraints.
BOOST_FOREACH(const Outline2d &outline, this->outlines()) {
// Start with last point
Polygon2DCGAL::CDT::Vertex_handle prev = cdt.insert(Polygon2DCGAL::Point(outline[outline.size()-1][0], outline[outline.size()-1][1]));
BOOST_FOREACH(const Vector2d &v, outline) {
Polygon2DCGAL::CDT::Vertex_handle prev = cdt.insert(Polygon2DCGAL::Point(outline.vertices[outline.vertices.size()-1][0], outline.vertices[outline.vertices.size()-1][1]));
BOOST_FOREACH(const Vector2d &v, outline.vertices) {
Polygon2DCGAL::CDT::Vertex_handle curr = cdt.insert(Polygon2DCGAL::Point(v[0], v[1]));
if (prev != curr) { // Ignore duplicate vertices
cdt.insert_constraint(prev, curr);

View File

@ -17,7 +17,7 @@ size_t Polygon2d::memsize() const
{
size_t mem = 0;
BOOST_FOREACH(const Outline2d &o, this->outlines()) {
mem += o.size() * sizeof(Vector2d) + sizeof(Outline2d);
mem += o.vertices.size() * sizeof(Vector2d) + sizeof(Outline2d);
}
mem += sizeof(Polygon2d);
return mem;
@ -27,7 +27,7 @@ BoundingBox Polygon2d::getBoundingBox() const
{
BoundingBox bbox;
BOOST_FOREACH(const Outline2d &o, this->outlines()) {
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
bbox.extend(Vector3d(v[0], v[1], 0));
}
}
@ -39,7 +39,7 @@ std::string Polygon2d::dump() const
std::stringstream out;
BOOST_FOREACH(const Outline2d &o, this->theoutlines) {
out << "contour:\n";
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
out << " " << v.transpose();
}
out << "\n";
@ -49,8 +49,8 @@ std::string Polygon2d::dump() const
void Polygon2d::transform(const Transform2d &mat)
{
BOOST_FOREACH(Outline2d &outline, this->theoutlines) {
BOOST_FOREACH(Vector2d &v, outline) {
BOOST_FOREACH(Outline2d &o, this->theoutlines) {
BOOST_FOREACH(Vector2d &v, o.vertices) {
v = mat * v;
}
}

View File

@ -5,7 +5,11 @@
#include "linalg.h"
#include <vector>
typedef std::vector<Vector2d> Outline2d;
struct Outline2d {
Outline2d() : positive(true) {}
std::vector<Vector2d> vertices;
bool positive;
};
class Polygon2d : public Geometry
{

View File

@ -126,11 +126,11 @@ namespace CGALUtils {
CGAL_For_all(fcirc, fend) {
if (E.is_standard(E.target(fcirc))) {
Explorer::Point ep = E.point(E.target(fcirc));
outline.push_back(Vector2d(to_double(ep.x()),
outline.vertices.push_back(Vector2d(to_double(ep.x()),
to_double(ep.y())));
}
}
if (outline.size() > 0) poly->addOutline(outline);
if (outline.vertices.size() > 0) poly->addOutline(outline);
}
return poly;
}

View File

@ -5,7 +5,7 @@ namespace ClipperUtils {
ClipperLib::Path fromOutline2d(const Outline2d &outline, bool keep_orientation) {
ClipperLib::Path p;
BOOST_FOREACH(const Vector2d &v, outline) {
BOOST_FOREACH(const Vector2d &v, outline.vertices) {
p.push_back(ClipperLib::IntPoint(v[0]*CLIPPER_SCALE, v[1]*CLIPPER_SCALE));
}
// Make sure all polygons point up, since we project also
@ -27,33 +27,38 @@ namespace ClipperUtils {
return toPolygon2d(sanitize(ClipperUtils::fromPolygon2d(poly)));
}
ClipperLib::Paths sanitize(const ClipperLib::Paths &paths) {
return ClipperUtils::process(paths,
ClipperLib::ctUnion,
ClipperLib::pftEvenOdd);
ClipperLib::PolyTree sanitize(const ClipperLib::Paths &paths) {
ClipperLib::PolyTree result;
ClipperLib::Clipper clipper;
clipper.AddPaths(paths, ClipperLib::ptSubject, true);
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftEvenOdd);
return result;
}
Polygon2d *toPolygon2d(const ClipperLib::Paths &poly) {
Polygon2d *toPolygon2d(const ClipperLib::PolyTree &poly) {
Polygon2d *result = new Polygon2d;
BOOST_FOREACH(const ClipperLib::Path &p, poly) {
const ClipperLib::PolyNode *node = poly.GetFirst();
while (node) {
Outline2d outline;
outline.positive = !node->IsHole();
const Vector2d *lastv = NULL;
BOOST_FOREACH(const ClipperLib::IntPoint &ip, p) {
BOOST_FOREACH(const ClipperLib::IntPoint &ip, node->Contour) {
Vector2d v(1.0*ip.X/CLIPPER_SCALE, 1.0*ip.Y/CLIPPER_SCALE);
// Ignore too close vertices. This is to be nice to subsequent processes.
if (lastv && (v-*lastv).squaredNorm() < 0.001) continue;
outline.push_back(v);
lastv = &outline.back();
outline.vertices.push_back(v);
lastv = &outline.vertices.back();
}
result->addOutline(outline);
node = node->GetNext();
}
result->setSanitized(true);
return result;
}
ClipperLib::Paths process(const ClipperLib::Paths &polygons,
ClipperLib::ClipType cliptype,
ClipperLib::PolyFillType polytype)
ClipperLib::ClipType cliptype,
ClipperLib::PolyFillType polytype)
{
ClipperLib::Paths result;
ClipperLib::Clipper clipper;
@ -62,24 +67,34 @@ namespace ClipperUtils {
return result;
}
Polygon2d *apply(std::vector<const Polygon2d*> polygons,
Polygon2d *apply(const std::vector<ClipperLib::Paths> &pathsvector,
ClipperLib::ClipType clipType)
{
ClipperLib::Clipper clipper;
bool first = true;
BOOST_FOREACH(const Polygon2d *polygon, polygons) {
ClipperLib::Paths paths = fromPolygon2d(*polygon);
if (!polygon->isSanitized()) paths = sanitize(paths);
BOOST_FOREACH(const ClipperLib::Paths &paths, pathsvector) {
clipper.AddPaths(paths, first ? ClipperLib::ptSubject : ClipperLib::ptClip, true);
if (first) first = false;
}
ClipperLib::Paths sumresult;
ClipperLib::PolyTree sumresult;
clipper.Execute(clipType, sumresult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
if (sumresult.size() == 0) return NULL;
if (sumresult.Total() == 0) return NULL;
// The returned result will have outlines ordered according to whether
// they're positive or negative: Positive outlines counter-clockwise and
// negative outlines clockwise.
return ClipperUtils::toPolygon2d(sumresult);
}
Polygon2d *apply(const std::vector<const Polygon2d*> &polygons,
ClipperLib::ClipType clipType)
{
std::vector<ClipperLib::Paths> pathsvector;
BOOST_FOREACH(const Polygon2d *polygon, polygons) {
ClipperLib::Paths polypaths = fromPolygon2d(*polygon);
if (!polygon->isSanitized()) ClipperLib::PolyTreeToPaths(sanitize(polypaths), polypaths);
pathsvector.push_back(polypaths);
}
return apply(pathsvector, clipType);
}
};

View File

@ -10,14 +10,14 @@ namespace ClipperUtils {
ClipperLib::Path fromOutline2d(const Outline2d &poly, bool keep_orientation);
ClipperLib::Paths fromPolygon2d(const Polygon2d &poly);
ClipperLib::Paths sanitize(const ClipperLib::Paths &paths);
ClipperLib::PolyTree sanitize(const ClipperLib::Paths &paths);
Polygon2d *sanitize(const Polygon2d &poly);
Polygon2d *toPolygon2d(const ClipperLib::Path &poly);
Polygon2d *toPolygon2d(const ClipperLib::Paths &poly);
Polygon2d *toPolygon2d(const ClipperLib::PolyTree &poly);
ClipperLib::Paths process(const ClipperLib::Paths &polygons,
ClipperLib::ClipType, ClipperLib::PolyFillType);
Polygon2d *apply(std::vector<const Polygon2d*> polygons, ClipperLib::ClipType);
Polygon2d *apply(const std::vector<const Polygon2d*> &polygons, ClipperLib::ClipType);
Polygon2d *apply(const std::vector<ClipperLib::Paths> &pathsvector, ClipperLib::ClipType);
};

View File

@ -592,7 +592,7 @@ Polygon2d *DxfData::toPolygon2d() const
if (!path.is_closed) continue; // We don't support open paths for now
Outline2d outline;
for (size_t j = 1; j < path.indices.size(); j++) {
outline.push_back(Vector2d(this->points[path.indices[path.indices.size()-j]]));
outline.vertices.push_back(Vector2d(this->points[path.indices[path.indices.size()-j]]));
}
poly->addOutline(outline);
}

View File

@ -221,9 +221,9 @@ void export_dxf(const Polygon2d &poly, std::ostream &output)
<< "ENTITIES\n";
BOOST_FOREACH(const Outline2d &o, poly.outlines()) {
for (int i=0;i<o.size();i++) {
const Vector2d &p1 = o[i];
const Vector2d &p2 = o[(i+1)%o.size()];
for (int i=0;i<o.vertices.size();i++) {
const Vector2d &p1 = o.vertices[i];
const Vector2d &p2 = o.vertices[(i+1)%o.vertices.size()];
double x1 = p1[0];
double y1 = p1[1];
double x2 = p2[0];

View File

@ -58,7 +58,7 @@ namespace PolysetUtils {
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
Outline2d outline;
BOOST_FOREACH(const Vector3d &v, p) {
outline.push_back(Vector2d(v[0], v[1]));
outline.vertices.push_back(Vector2d(v[0], v[1]));
}
poly->addOutline(outline);
}

View File

@ -214,11 +214,11 @@ void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m,
// Render sides
if (polygon.outlines().size() > 0) {
BOOST_FOREACH(const Outline2d &o, polygon.outlines()) {
for (size_t j = 1; j <= o.size(); j++) {
Vector3d p1(o[j-1][0], o[j-1][1], -zbase/2);
Vector3d p2(o[j-1][0], o[j-1][1], zbase/2);
Vector3d p3(o[j % o.size()][0], o[j % o.size()][1], -zbase/2);
Vector3d p4(o[j % o.size()][0], o[j % o.size()][1], zbase/2);
for (size_t j = 1; j <= o.vertices.size(); j++) {
Vector3d p1(o.vertices[j-1][0], o.vertices[j-1][1], -zbase/2);
Vector3d p2(o.vertices[j-1][0], o.vertices[j-1][1], zbase/2);
Vector3d p3(o.vertices[j % o.vertices.size()][0], o.vertices[j % o.vertices.size()][1], -zbase/2);
Vector3d p4(o.vertices[j % o.vertices.size()][0], o.vertices[j % o.vertices.size()][1], zbase/2);
gl_draw_triangle(shaderinfo, p2, p1, p3, true, true, false, 0, mirrored);
gl_draw_triangle(shaderinfo, p2, p3, p4, false, true, true, 0, mirrored);
}
@ -285,7 +285,7 @@ void PolySet::render_edges(Renderer::csgmode_e csgmode) const
// Render only outlines
BOOST_FOREACH(const Outline2d &o, polygon.outlines()) {
glBegin(GL_LINE_LOOP);
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
glVertex3d(v[0], v[1], -0.1);
}
glEnd();
@ -299,14 +299,14 @@ void PolySet::render_edges(Renderer::csgmode_e csgmode) const
// Render top+bottom outlines
for (double z = -zbase/2; z < zbase; z += zbase) {
glBegin(GL_LINE_LOOP);
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
glVertex3d(v[0], v[1], z);
}
glEnd();
}
// Render sides
glBegin(GL_LINES);
BOOST_FOREACH(const Vector2d &v, o) {
BOOST_FOREACH(const Vector2d &v, o.vertices) {
glVertex3d(v[0], v[1], -zbase/2);
glVertex3d(v[0], v[1], +zbase/2);
}

View File

@ -515,12 +515,14 @@ sphere_next_r2:
v2 -= Vector2d(this->x/2, this->y/2);
}
Outline2d o(4);
o[0] = v1;
o[1] = Vector2d(v2[0], v1[1]);
o[2] = v2;
o[3] = Vector2d(v1[0], v2[1]);
Outline2d o;
o.vertices.resize(4);
o.vertices[0] = v1;
o.vertices[1] = Vector2d(v2[0], v1[1]);
o.vertices[2] = v2;
o.vertices[3] = Vector2d(v1[0], v2[1]);
p->addOutline(o);
p->setSanitized(true);
}
if (this->type == CIRCLE && this->r1 > 0)
@ -529,12 +531,14 @@ sphere_next_r2:
g = p;
int fragments = Calc::get_fragments_from_r(this->r1, this->fn, this->fs, this->fa);
Outline2d o(fragments);
Outline2d o;
o.vertices.resize(fragments);
for (int i=0; i < fragments; i++) {
double phi = (M_PI*2*i) / fragments;
o[i] = Vector2d(this->r1*cos(phi), this->r1*sin(phi));
o.vertices[i] = Vector2d(this->r1*cos(phi), this->r1*sin(phi));
}
p->addOutline(o);
p->setSanitized(true);
}
if (this->type == POLYGON)
@ -542,7 +546,7 @@ sphere_next_r2:
Polygon2d *p = new Polygon2d();
g = p;
std::vector<Vector2d> vertices;
Outline2d outline;
double x,y;
const Value::VectorType &vec = this->points.toVector();
for (int i=0;i<vec.size();i++) {
@ -553,19 +557,19 @@ sphere_next_r2:
delete p;
return NULL;
}
vertices.push_back(Vector2d(x, y));
outline.vertices.push_back(Vector2d(x, y));
}
if (this->paths.toVector().size() == 0 && vertices.size() > 2) {
p->addOutline(vertices);
if (this->paths.toVector().size() == 0 && outline.vertices.size() > 2) {
p->addOutline(outline);
}
else {
BOOST_FOREACH(const Value &polygon, this->paths.toVector()) {
Outline2d curroutline;
BOOST_FOREACH(const Value &index, polygon.toVector()) {
unsigned int idx = index.toDouble();
if (idx < vertices.size()) {
curroutline.push_back(vertices[idx]);
if (idx < outline.vertices.size()) {
curroutline.vertices.push_back(outline.vertices[idx]);
}
// FIXME: Warning on out of bounds?
}