From 064ee8f98a097fb30ea2ab9e7dbad7f15f116ebb Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 1 Nov 2013 18:57:36 -0400 Subject: [PATCH] Basic linear_extrude now works --- src/CGALEvaluator.cc | 20 ------ src/CGALEvaluator.h | 1 - src/GeometryEvaluator.cc | 131 +++++++++++++++++++++++++++++++++++++++ src/GeometryEvaluator.h | 1 + src/polyset.cc | 6 ++ src/polyset.h | 2 + 6 files changed, 140 insertions(+), 21 deletions(-) diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 96c1a3c1..a91550a7 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -349,26 +349,6 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node) return ContinueTraversal; } -/*! - Leaf nodes can create their own geometry, so let them do that -*/ -Response CGALEvaluator::visit(State &state, const LeafNode &node) -{ - if (state.isPrefix()) { - CGAL_Nef_polyhedron N; - if (!isCached(node)) { - shared_ptr geom(node.createGeometry()); - if (geom) N = createNefPolyhedronFromGeometry(*geom); - node.progress_report(); - } - else { - N = CGALCache::instance()->get(this->tree.getIdString(node)); - } - addToParent(state, node, N); - } - return PruneTraversal; -} - /*! Handles non-leaf PolyNodes; extrudes, projection */ diff --git a/src/CGALEvaluator.h b/src/CGALEvaluator.h index f41603cf..458d2a0d 100644 --- a/src/CGALEvaluator.h +++ b/src/CGALEvaluator.h @@ -20,7 +20,6 @@ public: virtual Response visit(State &state, const AbstractIntersectionNode &node); virtual Response visit(State &state, const CsgNode &node); virtual Response visit(State &state, const TransformNode &node); - virtual Response visit(State &state, const LeafNode &node); virtual Response visit(State &state, const AbstractPolyNode &node); virtual Response visit(State &state, const CgaladvNode &node); diff --git a/src/GeometryEvaluator.cc b/src/GeometryEvaluator.cc index 99abea8c..5736e248 100644 --- a/src/GeometryEvaluator.cc +++ b/src/GeometryEvaluator.cc @@ -6,6 +6,7 @@ #include "module.h" #include "state.h" #include "transformnode.h" +#include "linearextrudenode.h" #include "clipper-utils.h" #include "CGALEvaluator.h" #include "CGALCache.h" @@ -62,6 +63,7 @@ shared_ptr GeometryEvaluator::evaluateGeometry(const AbstractNod } /*! + */ Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op) { @@ -97,6 +99,11 @@ Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADO clipper.Execute(ClipperLib::ctUnion, result); if (result.size() == 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. + // FIXME: We might want to introduce a flag in Polygon2d to signify this return ClipperUtils::toPolygon2d(result); } @@ -150,6 +157,8 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node) */ Response GeometryEvaluator::visit(State &state, const LeafNode &node) { + // FIXME: We should run the result of 2D geometry to Clipper to ensure + // correct winding order if (state.isPrefix()) { shared_ptr geom; if (!isCached(node)) geom.reset(node.createGeometry()); @@ -191,6 +200,128 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node) return ContinueTraversal; } +static Vector2d transform(const Vector2d &v, double rot, const Vector2d &scale) +{ + return Vector2d(scale[0] * (v[0] * cos(rot*M_PI/180) + v[1] * sin(rot*M_PI/180)), + scale[1] * (v[0] * -sin(rot*M_PI/180) + v[1] * cos(rot*M_PI/180))); +} + +static void transform_PolySet(PolySet &ps, + double height, double rot, const Vector2d &scale) +{ + BOOST_FOREACH(PolySet::Polygon &p, ps.polygons) { + BOOST_FOREACH(Vector3d &v, p) { + v = Vector3d(scale[0] * (v[0] * cos(rot*M_PI/180) + v[1] * sin(rot*M_PI/180)), + scale[1] * (v[0] * -sin(rot*M_PI/180) + v[1] * cos(rot*M_PI/180)), + height); + } + } +} + +static void add_slice(PolySet *ps, const Polygon2d &poly, + double rot1, double rot2, + double h1, double h2, + const Vector2d &scale1, + const Vector2d &scale2) +{ + // FIXME: If scale2 == 0 we need to handle tessellation separately + bool splitfirst = sin(rot2 - rot1) >= 0.0; + BOOST_FOREACH(const Outline2d &o, poly.outlines()) { + Vector2d prev1 = transform(o[0], rot1, scale1); + Vector2d prev2 = transform(o[0], rot2, scale2); + for (size_t i=1;i<=o.size();i++) { + Vector2d curr1 = transform(o[i % o.size()], rot1, scale1); + Vector2d curr2 = transform(o[i % o.size()], rot2, scale2); + ps->append_poly(); + + if (splitfirst) { + ps->insert_vertex(prev1[0], prev1[1], h1); + ps->insert_vertex(curr1[0], curr1[1], h1); + ps->insert_vertex(curr2[0], curr2[1], h2); + if (scale2[0] > 0 || scale2[1] > 0) { + ps->append_poly(); + ps->insert_vertex(curr2[0], curr2[1], h2); + ps->insert_vertex(prev1[0], prev1[1], h1); + ps->insert_vertex(prev2[0], prev2[1], h2); + } + } + else { + ps->insert_vertex(prev1[0], prev1[1], h1); + ps->insert_vertex(curr1[0], curr1[1], h1); + ps->insert_vertex(prev2[0], prev2[1], h2); + if (scale2[0] > 0 || scale2[1] > 0) { + ps->append_poly(); + ps->insert_vertex(prev2[0], prev2[1], h2); + ps->insert_vertex(curr1[0], curr1[1], h1); + ps->insert_vertex(curr2[0], curr2[1], h2); + } + } + prev1 = curr1; + prev2 = curr2; + } + } +} + +/*! + Input to extrude should be clean. This means non-intersecting, correct winding order + etc., the input coming from a library like Clipper. +*/ +static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d &poly) +{ + PolySet *ps = new PolySet(); + ps->convexity = node.convexity; + if (node.height <= 0) return ps; + + double h1, h2; + + if (node.center) { + h1 = -node.height/2.0; + h2 = +node.height/2.0; + } else { + h1 = 0; + h2 = node.height; + } + + PolySet *ps_bottom = poly.tessellate(); // bottom + ps->append(*ps_bottom); + delete ps_bottom; + if (node.scale_x > 0 || node.scale_y > 0) { + PolySet *ps_top = poly.tessellate(); // top + transform_PolySet(*ps_top, h2, node.twist, Vector2d(node.scale_x, node.scale_y)); + ps->append(*ps_top); + delete ps_top; + } + size_t slices = node.has_twist ? node.slices : 1; + + for (int j = 0; j < slices; j++) { + double rot1 = node.twist*j / slices; + double rot2 = node.twist*(j+1) / slices; + double height1 = h1 + (h2-h1)*j / slices; + double height2 = h1 + (h2-h1)*(j+1) / slices; + Vector2d scale1(1 - (1-node.scale_x)*j / slices, + 1 - (1-node.scale_y)*j / slices); + Vector2d scale2(1 - (1-node.scale_x)*(j+1) / slices, + 1 - (1-node.scale_y)*(j+1) / slices); + add_slice(ps, poly, rot1, rot2, height1, height2, scale1, scale2); + } + + return ps; +} + +Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + shared_ptr geom(applyToChildren(node, CGE_UNION)); + shared_ptr polygons = dynamic_pointer_cast(geom); + assert(polygons); + Geometry *extruded = extrudePolygon(node, *polygons); + assert(extruded); + addToParent(state, node, shared_ptr(extruded)); + } + return ContinueTraversal; +} + /*! Handles non-leaf PolyNodes; extrusions, projection */ diff --git a/src/GeometryEvaluator.h b/src/GeometryEvaluator.h index d25494f1..b77ccbd1 100644 --- a/src/GeometryEvaluator.h +++ b/src/GeometryEvaluator.h @@ -20,6 +20,7 @@ public: virtual Response visit(State &state, const AbstractNode &node); virtual Response visit(State &state, const AbstractPolyNode &node); + virtual Response visit(State &state, const LinearExtrudeNode &node); virtual Response visit(State &state, const LeafNode &node); virtual Response visit(State &state, const TransformNode &node); diff --git a/src/polyset.cc b/src/polyset.cc index 65641cf2..608977f8 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -312,3 +312,9 @@ size_t PolySet::memsize() const mem += sizeof(PolySet); return mem; } + +void PolySet::append(const PolySet &ps) +{ + this->polygons.insert(this->polygons.end(), ps.polygons.begin(), ps.polygons.end()); +} + diff --git a/src/polyset.h b/src/polyset.h index 6c2cc3fc..eca12270 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -33,6 +33,8 @@ public: void append_poly(); void append_vertex(double x, double y, double z = 0.0); void insert_vertex(double x, double y, double z = 0.0); + void append(const PolySet &ps); + void render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const; void render_edges(Renderer::csgmode_e csgmode) const; };