Merge remote-tracking branch 'origin/master' into translation2

master
Marius Kintel 2014-12-01 02:34:28 -05:00
commit 5743064d56
85 changed files with 1845 additions and 1281 deletions

View File

@ -454,6 +454,7 @@ HEADERS += src/cgal.h \
SOURCES += src/cgalutils.cc \
src/cgalutils-tess.cc \
src/cgalutils-tess-old.cc \
src/CGALCache.cc \
src/CGALRenderer.cc \
src/CGAL_Nef_polyhedron.cc \

View File

@ -6,6 +6,7 @@
* min() and max() can now take a wvector argument
* concat()
* 2D minkowski and holes
* chr()
**Program Features:**
* Added --viewall cmd-line parameter
@ -14,6 +15,12 @@
* Qt5, retina
* SVG import/export
* AMF import/export
* Color schemes for viewer and editor can be user-edited
* Improved editor
* Splash screen
* Docking of GUI components
* Toolbar
* More robust STL export
**Bugfixes/improvements:**
* Internal cavity fix

View File

@ -1,4 +1,6 @@
#include <Carbon/Carbon.h>
#include <AppleEvents.h>
#include <MacTypes.h>
#include <CoreServices/CoreServices.h>
#include <QApplication>
#include "MainWindow.h"

View File

@ -113,7 +113,6 @@ void GLView::setupCamera()
glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0);
glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z() );
break;
case Camera::VECTOR: {
switch (this->cam.projection) {
@ -150,18 +149,24 @@ void GLView::setupCamera()
void GLView::paintGL()
{
glEnable(GL_LIGHTING);
setupCamera();
glDisable(GL_LIGHTING);
Color4f bgcol = ColorMap::getColor(*this->colorscheme, BACKGROUND_COLOR);
Color4f bgcontrast = ColorMap::getContrastColor(bgcol);
glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Only for GIMBAL cam
if (showcrosshairs) GLView::showCrosshairs();
if (showaxes) GLView::showAxes();
setupCamera();
if (this->cam.type) {
// Only for GIMBAL cam
// The crosshair should be fixed at the center of the viewport...
if (showcrosshairs) GLView::showCrosshairs();
glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z());
// ...the axis lines need to follow the object translation.
if (showaxes) GLView::showAxes(bgcontrast);
}
glEnable(GL_LIGHTING);
glDepthFunc(GL_LESS);
glCullFace(GL_BACK);
glDisable(GL_CULL_FACE);
@ -174,10 +179,11 @@ void GLView::paintGL()
OpenCSG::setContext(this->opencsg_id);
#endif
this->renderer->draw(showfaces, showedges);
}
}
// Only for GIMBAL
if (showaxes) GLView::showSmallaxes();
glDisable(GL_LIGHTING);
if (showaxes) GLView::showSmallaxes(bgcontrast);
}
#ifdef ENABLE_OPENCSG
@ -348,6 +354,7 @@ void GLView::initializeGL()
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
glEnable(GL_LINE_STIPPLE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
// The following line is reported to fix issue #71
@ -358,7 +365,7 @@ void GLView::initializeGL()
#endif
}
void GLView::showSmallaxes()
void GLView::showSmallaxes(const Color4f &col)
{
// Fixme - this doesnt work in Vector Camera mode
@ -423,14 +430,9 @@ void GLView::showSmallaxes()
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// FIXME: This was an attempt to keep contrast with background, but is suboptimal
// (e.g. nearly invisible against a gray background).
// int r,g,b;
// r=g=b=0;
// bgcol.getRgb(&r, &g, &b);
// glColor3f((255.0f-r)/255.0f, (255.0f-g)/255.0f, (255.0f-b)/255.0f);
float d = 3*dpi;
glColor3f(0.0f, 0.0f, 0.0f);
glColor3f(col[0], col[1], col[2]);
float d = 3*dpi;
glBegin(GL_LINES);
// X Label
glVertex3d(xlabel_x-d, xlabel_y-d, 0); glVertex3d(xlabel_x+d, xlabel_y+d, 0);
@ -447,36 +449,47 @@ void GLView::showSmallaxes()
glEnd();
}
void GLView::showAxes()
void GLView::showAxes(const Color4f &col)
{
double l = cam.projection == Camera::PERSPECTIVE ? cam.viewer_distance : cam.height;
// FIXME: doesn't work under Vector Camera
// Large gray axis cross inline with the model
// FIXME: This is always gray - adjust color to keep contrast with background
glLineWidth(this->getDPI());
glColor3d(0.5, 0.5, 0.5);
glColor3f(col[0], col[1], col[2]);
glBegin(GL_LINES);
double l = cam.projection == Camera::PERSPECTIVE ? cam.viewer_distance : cam.height;
glVertex3d(-l, 0, 0);
glVertex3d(0, 0, 0);
glVertex3d(+l, 0, 0);
glVertex3d(0, -l, 0);
glVertex3d(0, 0, 0);
glVertex3d(0, +l, 0);
glVertex3d(0, 0, -l);
glVertex3d(0, 0, 0);
glVertex3d(0, 0, +l);
glEnd();
glPushAttrib(GL_LINE_BIT);
glLineStipple(3, 0xAAAA);
glBegin(GL_LINES);
glVertex3d(0, 0, 0);
glVertex3d(-l, 0, 0);
glVertex3d(0, 0, 0);
glVertex3d(0, -l, 0);
glVertex3d(0, 0, 0);
glVertex3d(0, 0, -l);
glEnd();
glPopAttrib();
}
void GLView::showCrosshairs()
{
// FIXME: this might not work with Vector camera
// FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them
// to change color based on view orientation.
glLineWidth(3);
glLineWidth(this->getDPI());
Color4f col = ColorMap::getColor(*this->colorscheme, CROSSHAIR_COLOR);
glColor3f(col[0], col[1], col[2]);
glBegin(GL_LINES);
for (double xf = -1; xf <= +1; xf += 2)
for (double yf = -1; yf <= +1; yf += 2) {
double vd = cam.viewer_distance/20;
double vd = cam.viewer_distance/8;
glVertex3d(-xf*vd, -yf*vd, -vd);
glVertex3d(+xf*vd, +yf*vd, +vd);
}

View File

@ -42,10 +42,6 @@ public:
void setCamera(const Camera &cam);
void setupCamera();
void showCrosshairs();
void showAxes();
void showSmallaxes();
void setColorScheme(const ColorScheme &cs);
void setColorScheme(const std::string &cs);
void updateColorScheme();
@ -76,4 +72,8 @@ public:
bool opencsg_support;
int opencsg_id;
#endif
private:
void showCrosshairs();
void showAxes(const Color4f &col);
void showSmallaxes(const Color4f &col);
};

View File

@ -88,8 +88,12 @@ GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren(const Abstrac
}
}
}
if (dim == 2) return ResultObject(applyToChildren2D(node, op));
else if (dim == 3) return applyToChildren3D(node, op);
if (dim == 2) {
Polygon2d *p2d = applyToChildren2D(node, op);
assert(p2d);
return ResultObject(p2d);
}
else if (dim == 3) return applyToChildren3D(node, op);
return ResultObject();
}
@ -125,10 +129,16 @@ GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren3D(const Abstr
}
/*!
Apply 2D hull.
May return an empty geometry but will not return NULL.
*/
Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node)
{
std::vector<const Polygon2d *> children = collectChildren2D(node);
Polygon2d *geometry = NULL;
Polygon2d *geometry = new Polygon2d();
typedef CGAL::Point_2<CGAL::Cartesian<double> > CGALPoint2;
// Collect point cloud
@ -150,7 +160,6 @@ Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node)
BOOST_FOREACH(const CGALPoint2 &p, result) {
outline.vertices.push_back(Vector2d(p[0], p[1]));
}
geometry = new Polygon2d();
geometry->addOutline(outline);
}
return geometry;
@ -271,12 +280,12 @@ Geometry::ChildList GeometryEvaluator::collectChildren3D(const AbstractNode &nod
smartCacheInsert(*chnode, chgeom);
if (chgeom) {
if (chgeom->isEmpty() || chgeom->getDimension() == 3) {
children.push_back(item);
}
else {
if (chgeom->getDimension() == 2) {
PRINT("WARNING: Ignoring 2D child object for 3D operation");
}
else if (chgeom->isEmpty() || chgeom->getDimension() == 3) {
children.push_back(item);
}
}
}
return children;
@ -443,6 +452,7 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node)
shared_ptr<const Geometry> geom;
if (!isSmartCached(node)) {
const Geometry *geometry = node.createGeometry();
assert(geometry);
if (const Polygon2d *polygon = dynamic_cast<const Polygon2d*>(geometry)) {
if (!polygon->isSanitized()) {
Polygon2d *p = ClipperUtils::sanitize(*polygon);
@ -573,7 +583,7 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node)
static void translate_PolySet(PolySet &ps, const Vector3d &translation)
{
BOOST_FOREACH(PolySet::Polygon &p, ps.polygons) {
BOOST_FOREACH(Polygon &p, ps.polygons) {
BOOST_FOREACH(Vector3d &v, p) {
v += translation;
}
@ -651,7 +661,7 @@ static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d &
PolySet *ps_bottom = poly.tessellate(); // bottom
// Flip vertex ordering for bottom polygon
BOOST_FOREACH(PolySet::Polygon &p, ps_bottom->polygons) {
BOOST_FOREACH(Polygon &p, ps_bottom->polygons) {
std::reverse(p.begin(), p.end());
}
translate_PolySet(*ps_bottom, Vector3d(0,0,h1));

View File

@ -11,7 +11,8 @@
We can store sanitized vs. unsanitized polygons. Sanitized polygons
will have opposite winding order for holes and is guaranteed to not
have intersecting geometry. Sanitization is typically done by ClipperUtils.
have intersecting geometry. Sanitization is typically done by ClipperUtils, but
if you create geometry which you know is sanitized, the flag can be set manually.
*/
size_t Polygon2d::memsize() const

View File

@ -77,11 +77,12 @@ void Builtins::initialize()
register_builtin_dxf_rotate_extrude();
register_builtin_text();
this->deprecations["dxf_linear_extrude"] = "linear_extrude";
this->deprecations["dxf_rotate_extrude"] = "rotate_extrude";
this->deprecations["import_stl"] = "import";
this->deprecations["import_dxf"] = "import";
this->deprecations["import_off"] = "import";
this->deprecations["dxf_linear_extrude"] = "linear_extrude()";
this->deprecations["dxf_rotate_extrude"] = "rotate_extrude()";
this->deprecations["import_stl"] = "import()";
this->deprecations["import_dxf"] = "import()";
this->deprecations["import_off"] = "import()";
this->deprecations["assign"] = "a regular assignment";
}
std::string Builtins::isDeprecated(const std::string &name)

View File

@ -62,6 +62,7 @@ typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder;
typedef CGAL::Point_3<CGAL_Kernel3> CGAL_Point_3;
typedef CGAL::Iso_cuboid_3<CGAL_Kernel3> CGAL_Iso_cuboid_3;
typedef std::vector<CGAL_Point_3> CGAL_Polygon_3;
// CGAL_Nef_polyhedron2 uses CGAL_Kernel2, but Iso_rectangle_2 needs to match
// CGAL_Nef_polyhedron2::Explorer::Point which is different than

View File

@ -39,10 +39,10 @@ class CgaladvModule : public AbstractModule
public:
cgaladv_type_e type;
CgaladvModule(cgaladv_type_e type) : type(type) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
CgaladvNode *node = new CgaladvNode(inst, type);
@ -62,6 +62,7 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
Value convexity, path, subdiv_type, level;

553
src/cgalutils-tess-old.cc Normal file
View File

@ -0,0 +1,553 @@
/*
This is our custom tessellator of Nef Polyhedron faces. The problem with
Nef faces is that sometimes the 'default' tessellator of Nef Polyhedron
doesnt work. This is particularly true with situations where the polygon
face is not, actually, 'simple', according to CGAL itself. This can
occur on a bad quality STL import but also for other reasons. The
resulting Nef face will appear to the average human eye as an ordinary,
simple polygon... but in reality it has multiple edges that are
slightly-out-of-alignment and sometimes they backtrack on themselves.
When the triangulator is fed a polygon with self-intersecting edges,
it's default behavior is to throw an exception. The other terminology
for this is to say that the 'constraints' in the triangulation are
'intersecting'. The 'constraints' represent the edges of the polygon.
The 'triangulation' is the covering of all the polygon points with
triangles.
How do we allow interseting constraints during triangulation? We use an
'Itag' for the triangulation, per the CGAL docs. This allows the
triangulator to run without throwing an exception when it encounters
self-intersecting polygon edges. The trick here is that when it finds
an intersection, it actually creates a new point.
The triangulator creates new points in 2d, but they aren't matched to
any 3d points on our 3d polygon plane. (The plane of the Nef face). How
to fix this problem? We actually 'project back up' or 'lift' into the 3d
plane from the 2d point. This is handled in the 'deproject()' function.
There is also the issue of the Simplicity of Nef Polyhedron face
polygons. They are often not simple. The intersecting-constraints
Triangulation can triangulate non-simple polygons, but of course it's
result is also non-simple. This means that CGAL functions like
orientation_2() and bounded_side() simply will not work on the resulting
polygons because they all require input polygons to pass the
'is_simple2()' test. We have to use alternatives in order to create our
triangles.
There is also the question of which underlying number type to use. Some
of the CGAL functions simply dont guarantee good results with a type
like double. Although much the math here is somewhat simple, like
line-line intersection, and involves only simple algebra, the
approximations required when using floating-point types can cause the
answers to be wrong. For example questions like 'is a point inside a
triangle' do not have good answers under floating-point systems where a
line may have a slope that is not expressible exactly as a floating
point number. There are ways to deal with floating point inaccuracy but
it is much, much simpler to use Rational numbers, although potentially
much slower in many cases.
*/
#include "cgalutils.h"
#include <CGAL/Delaunay_mesher_no_edge_refinement_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
typedef CGAL_Kernel3 Kernel;
//typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_intersections_tag ITAG;
typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT;
//typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT;
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Point CDTPoint;
typedef CGAL::Ray_2<Kernel> CGAL_Ray_2;
typedef CGAL::Line_3<Kernel> CGAL_Line_3;
typedef CGAL::Point_2<Kernel> CGAL_Point_2;
typedef CGAL::Vector_2<Kernel> CGAL_Vector_2;
typedef CGAL::Segment_2<Kernel> CGAL_Segment_2;
typedef CGAL::Direction_2<Kernel> CGAL_Direction_2;
typedef CGAL::Direction_3<Kernel> CGAL_Direction_3;
typedef CGAL::Plane_3<Kernel> CGAL_Plane_3;
/* The idea of 'projection' is how we make 3d points appear as though
they were 2d points to the tessellation algorithm. We take the 3-d plane
on which the polygon lies, and then 'project' or 'cast its shadow' onto
one of three standard planes, the xyplane, the yzplane, or the xzplane,
depending on which projection will prevent the polygon looking like a
flat line. (imagine, the triangle 0,0,1 0,1,1 0,1,0 ... if viewed from
the 'top' it looks line a flat line. so we want to view it from the
side). Thus we create a sequence of x,y points to feed to the algorithm,
but those points might actually be x,z pairs or y,z pairs... it is an
illusion we present to the triangulation algorithm by way of 'projection'.
We get a resulting sequence of triangles with x,y coordinates, which we
then 'deproject' back to x,z or y,z, in 3d space as needed. For example
the square 0,0,0 0,0,1 0,1,1 0,1,0 becomes '0,0 0,1 1,1 1,0', is then
split into two triangles, 0,0 1,0 1,1 and 0,0 1,1 0,1. those two triangles
then are projected back to 3d as 0,0,0 0,1,0 0,1,1 and 0,0 0,1,1 0,0,1.
There is an additional trick we do with projection related to Polygon
orientation and the orientation of our output triangles, and thus, which
way they are facing in space (aka their 'normals' or 'oriented side').
The basic issues is this: every 3d flat polygon can be thought of as
having two sides. In Computer Graphics the convention is that the
'outside' or 'oriented side' or 'normal' is determined by looking at the
triangle in terms of the 'ordering' or 'winding' of the points. If the
points come in a 'clockwise' order, you must be looking at the triangle
from 'inside'. If the points come in a 'counterclockwise' order, you
must be looking at the triangle from the outside. For example, the
triangle 0,0,0 1,0,0 0,1,0, when viewed from the 'top', has points in a
counterclockwise order, so the 'up' side is the 'normal' or 'outside'.
if you look at that same triangle from the 'bottom' side, the points
will appear to be 'clockwise', so the 'down' side is the 'inside', and is the
opposite of the 'normal' side.
How do we keep track of all that when doing a triangulation? We could
check each triangle as it was generated, and fix it's orientation before
we feed it back to our output list. That is done by, for example, checking
the orientation of the input polygon and then forcing the triangle to
match that orientation during output. This is what CGAL's Nef Polyhedron
does, you can read it inside /usr/include/CGAL/Nef_polyhedron_3.h.
Or.... we could actually add an additional 'projection' to the incoming
polygon points so that our triangulation algorithm is guaranteed to
create triangles with the proper orientation in the first place. How?
First, we assume that the triangulation algorithm will always produce
'counterclockwise' triangles in our plain old x-y plane.
The method is based on the following curious fact: That is, if you take
the points of a polygon, and flip the x,y coordinate of each point,
making y:=x and x:=y, then you essentially get a 'mirror image' of the
original polygon... but the orientation will be flipped. Given a
clockwise polygon, the 'flip' will result in a 'counterclockwise'
polygon mirror-image and vice versa.
Now, there is a second curious fact that helps us here. In 3d, we are
using the plane equation of ax+by+cz+d=0, where a,b,c determine its
direction. If you notice, there are actually mutiple sets of numbers
a:b:c that will describe the exact same plane. For example the 'ground'
plane, called the XYplane, where z is everywhere 0, has the equation
0x+0y+1z+0=0, simplifying to a solution for x,y,z of z=0 and x,y = any
numbers in your number system. However you can also express this as
0x+0y+-1z=0. The x,y,z solution is the same: z is everywhere 0, x and y
are any number, even though a,b,c are different. We can say that the
plane is 'oriented' differently, if we wish.
But how can we link that concept to the points on the polygon? Well, if
you generate a plane using the standard plane-equation generation
formula, given three points M,N,P, then you will get a plane equation
with <a:b:c:d>. However if you feed the points in the reverse order,
P,N,M, so that they are now oriented in the opposite order, you will get
a plane equation with the signs flipped. <-a:-b:-c:-d> This means you
can essentially consider that a plane has an 'orientation' based on it's
equation, by looking at the signs of a,b,c relative to some other
quantity.
This means that you can 'flip' the projection of the input polygon
points so that the projection will match the orientation of the input
plane, thus guaranteeing that the output triangles will be oriented in
the same direction as the input polygon was. In other words, even though
we technically 'lose information' when we project from 3d->2d, we can
actually keep the concept of 'orientation' through the whole
triangulation process, and not have to recalculate the proper
orientation during output.
For example take two side-squares of a cube and the plane equations
formed by feeding the points in counterclockwise, as if looking in from
outside the cube:
0,0,0 0,1,0 0,1,1 0,0,1 <-1:0:0:0>
1,0,0 1,1,0 1,1,1 1,0,1 <1:0:0:1>
They are both projected onto the YZ plane. They look the same:
0,0 1,0 1,1 0,1
0,0 1,0 1,1 0,1
But the second square plane has opposite orientation, so we flip the x
and y for each point:
0,0 1,0 1,1 0,1
0,0 0,1 1,1 1,0
Only now do we feed these two 2-d squares to the tessellation algorithm.
The result is 4 triangles. When de-projected back to 3d, they will have
the appropriate winding that will match that of the original 3d faces.
And the first two triangles will have opposite orientation from the last two.
*/
typedef enum { XYPLANE, YZPLANE, XZPLANE, NONE } plane_t;
struct projection_t {
plane_t plane;
bool flip;
};
CGAL_Point_2 get_projected_point( CGAL_Point_3 &p3, projection_t projection ) {
NT3 x,y;
if (projection.plane == XYPLANE) { x = p3.x(); y = p3.y(); }
else if (projection.plane == XZPLANE) { x = p3.x(); y = p3.z(); }
else if (projection.plane == YZPLANE) { x = p3.y(); y = p3.z(); }
else if (projection.plane == NONE) { x = 0; y = 0; }
if (projection.flip) return CGAL_Point_2( y,x );
return CGAL_Point_2( x,y );
}
/* given 2d point, 3d plane, and 3d->2d projection, 'deproject' from
2d back onto a point on the 3d plane. true on failure, false on success */
bool deproject( CGAL_Point_2 &p2, projection_t &projection, CGAL_Plane_3 &plane, CGAL_Point_3 &p3 )
{
NT3 x,y;
CGAL_Line_3 l;
CGAL_Point_3 p;
CGAL_Point_2 pf( p2.x(), p2.y() );
if (projection.flip) pf = CGAL_Point_2( p2.y(), p2.x() );
if (projection.plane == XYPLANE) {
p = CGAL_Point_3( pf.x(), pf.y(), 0 );
l = CGAL_Line_3( p, CGAL_Direction_3(0,0,1) );
} else if (projection.plane == XZPLANE) {
p = CGAL_Point_3( pf.x(), 0, pf.y() );
l = CGAL_Line_3( p, CGAL_Direction_3(0,1,0) );
} else if (projection.plane == YZPLANE) {
p = CGAL_Point_3( 0, pf.x(), pf.y() );
l = CGAL_Line_3( p, CGAL_Direction_3(1,0,0) );
}
CGAL::Object obj = CGAL::intersection( l, plane );
const CGAL_Point_3 *point_test = CGAL::object_cast<CGAL_Point_3>(&obj);
if (point_test) {
p3 = *point_test;
return false;
}
PRINT("ERROR: deproject failure");
return true;
}
/* this simple criteria guarantees CGALs triangulation algorithm will
terminate (i.e. not lock up and freeze the program) */
template <class T> class DummyCriteria {
public:
typedef double Quality;
class Is_bad {
public:
CGAL::Mesh_2::Face_badness operator()(const Quality) const {
return CGAL::Mesh_2::NOT_BAD;
}
CGAL::Mesh_2::Face_badness operator()(const typename T::Face_handle&, Quality&q) const {
q = 1;
return CGAL::Mesh_2::NOT_BAD;
}
};
Is_bad is_bad_object() const { return Is_bad(); }
};
NT3 sign( const NT3 &n )
{
if (n>0) return NT3(1);
if (n<0) return NT3(-1);
return NT3(0);
}
/* wedge, also related to 'determinant', 'signed parallelogram area',
'side', 'turn', 'winding', '2d portion of cross-product', etc etc. this
function can tell you whether v1 is 'counterclockwise' or 'clockwise'
from v2, based on the sign of the result. when the input Vectors are
formed from three points, A-B and B-C, it can tell you if the path along
the points A->B->C is turning left or right.*/
NT3 wedge( CGAL_Vector_2 &v1, CGAL_Vector_2 &v2 ) {
return v1.x()*v2.y()-v2.x()*v1.y();
}
/* given a point and a possibly non-simple polygon, determine if the
point is inside the polygon or not, using the given winding rule. note
that even_odd is not implemented. */
typedef enum { NONZERO_WINDING, EVEN_ODD } winding_rule_t;
bool inside(CGAL_Point_2 &p1,std::vector<CGAL_Point_2> &pgon, winding_rule_t winding_rule)
{
NT3 winding_sum = NT3(0);
CGAL_Point_2 p2;
CGAL_Ray_2 eastray(p1,CGAL_Direction_2(1,0));
for (size_t i=0;i<pgon.size();i++) {
CGAL_Point_2 tail = pgon[i];
CGAL_Point_2 head = pgon[(i+1)%pgon.size()];
CGAL_Segment_2 seg( tail, head );
CGAL::Object obj = intersection( eastray, seg );
const CGAL_Point_2 *point_test = CGAL::object_cast<CGAL_Point_2>(&obj);
if (point_test) {
p2 = *point_test;
CGAL_Vector_2 v1( p1, p2 );
CGAL_Vector_2 v2( p2, head );
NT3 this_winding = wedge( v1, v2 );
winding_sum += sign(this_winding);
} else {
continue;
}
}
if (winding_sum != NT3(0) && winding_rule == NONZERO_WINDING ) return true;
return false;
}
projection_t find_good_projection( CGAL_Plane_3 &plane )
{
projection_t goodproj;
goodproj.plane = NONE;
goodproj.flip = false;
NT3 qxy = plane.a()*plane.a()+plane.b()*plane.b();
NT3 qyz = plane.b()*plane.b()+plane.c()*plane.c();
NT3 qxz = plane.a()*plane.a()+plane.c()*plane.c();
NT3 min = std::min(qxy,std::min(qyz,qxz));
if (min==qxy) {
goodproj.plane = XYPLANE;
if (sign(plane.c())>0) goodproj.flip = true;
} else if (min==qyz) {
goodproj.plane = YZPLANE;
if (sign(plane.a())>0) goodproj.flip = true;
} else if (min==qxz) {
goodproj.plane = XZPLANE;
if (sign(plane.b())<0) goodproj.flip = true;
} else PRINT("ERROR: failed to find projection");
return goodproj;
}
namespace CGALUtils {
/* given a single near-planar 3d polygon with holes, tessellate into a
sequence of polygons without holes. as of writing, this means conversion
into a sequence of 3d triangles. the given plane should be the same plane
holding the polygon and it's holes. */
bool tessellate3DFaceWithHolesNew(std::vector<CGAL_Polygon_3> &polygons,
Polygons &triangles,
CGAL_Plane_3 &plane)
{
if (polygons.size()==1 && polygons[0].size()==3) {
PRINTD("input polygon has 3 points. shortcut tessellation.");
Polygon t;
t.push_back(Vector3d(CGAL::to_double(polygons[0][0].x()), CGAL::to_double(polygons[0][0].y()), CGAL::to_double(polygons[0][0].z())));
t.push_back(Vector3d(CGAL::to_double(polygons[0][1].x()), CGAL::to_double(polygons[0][1].y()), CGAL::to_double(polygons[0][1].z())));
t.push_back(Vector3d(CGAL::to_double(polygons[0][2].x()), CGAL::to_double(polygons[0][2].y()), CGAL::to_double(polygons[0][2].z())));
triangles.push_back( t );
return false;
}
bool err = false;
CDT cdt;
std::map<CDTPoint,CGAL_Point_3> vertmap;
PRINTD("finding good projection");
projection_t goodproj = find_good_projection( plane );
PRINTDB("plane %s",plane );
PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip);
PRINTD("Inserting points and edges into Constrained Delaunay Triangulation");
std::vector< std::vector<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
}
std::list<CDTPoint>::iterator li = list_of_seeds.begin();
for (;li!=list_of_seeds.end();li++) {
//PRINTB("seed %s",*li);
double x = CGAL::to_double( li->x() );
double y = CGAL::to_double( li->y() );
PRINTDB("seed %f,%f",x%y);
}
PRINTD("seeding done");
PRINTD( "meshing" );
CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt,
list_of_seeds.begin(), list_of_seeds.end(),
DummyCriteria<CDT>() );
PRINTD("meshing done");
// this fails because it calls is_simple and is_simple fails on many
// Nef Polyhedron faces
//CGAL::Orientation original_orientation =
// CGAL::orientation_2( orienpgon.begin(), orienpgon.end() );
CDT::Finite_faces_iterator fit;
for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ )
{
if(fit->is_in_domain()) {
CDTPoint p1 = cdt.triangle( fit )[0];
CDTPoint p2 = cdt.triangle( fit )[1];
CDTPoint p3 = cdt.triangle( fit )[2];
CGAL_Point_3 cp1,cp2,cp3;
if (vertmap.count(p1)) cp1 = vertmap[p1];
else err = deproject( p1, goodproj, plane, cp1 );
if (vertmap.count(p2)) cp2 = vertmap[p2];
else err = deproject( p2, goodproj, plane, cp2 );
if (vertmap.count(p3)) cp3 = vertmap[p3];
else err = deproject( p3, goodproj, plane, cp3 );
if (err) PRINT("WARNING: 2d->3d deprojection failure");
Polygon tri;
tri.push_back(Vector3d(CGAL::to_double(cp1.x()), CGAL::to_double(cp1.y()), CGAL::to_double(cp1.z())));
tri.push_back(Vector3d(CGAL::to_double(cp2.x()), CGAL::to_double(cp2.y()), CGAL::to_double(cp2.z())));
tri.push_back(Vector3d(CGAL::to_double(cp3.x()), CGAL::to_double(cp3.y()), CGAL::to_double(cp3.z())));
triangles.push_back( tri );
}
}
PRINTDB("built %i triangles",triangles.size());
return err;
}
/* given a single near-planar 3d polygon with holes, tessellate into a
sequence of polygons without holes. as of writing, this means conversion
into a sequence of 3d triangles. the given plane should be the same plane
holding the polygon and it's holes. */
bool tessellate3DFaceWithHoles(std::vector<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &triangles,
CGAL_Plane_3 &plane)
{
if (polygons.size()==1 && polygons[0].size()==3) {
PRINTD("input polygon has 3 points. shortcut tessellation.");
CGAL_Polygon_3 t;
t.push_back(polygons[0][2]);
t.push_back(polygons[0][1]);
t.push_back(polygons[0][0]);
triangles.push_back( t );
return false;
}
bool err = false;
CDT cdt;
std::map<CDTPoint,CGAL_Point_3> vertmap;
PRINTD("finding good projection");
projection_t goodproj = find_good_projection( plane );
PRINTDB("plane %s",plane );
PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip);
PRINTD("Inserting points and edges into Constrained Delaunay Triangulation");
std::vector< std::vector<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
}
std::list<CDTPoint>::iterator li = list_of_seeds.begin();
for (;li!=list_of_seeds.end();li++) {
//PRINTB("seed %s",*li);
double x = CGAL::to_double( li->x() );
double y = CGAL::to_double( li->y() );
PRINTDB("seed %f,%f",x%y);
}
PRINTD("seeding done");
PRINTD( "meshing" );
CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt,
list_of_seeds.begin(), list_of_seeds.end(),
DummyCriteria<CDT>() );
PRINTD("meshing done");
// this fails because it calls is_simple and is_simple fails on many
// Nef Polyhedron faces
//CGAL::Orientation original_orientation =
// CGAL::orientation_2( orienpgon.begin(), orienpgon.end() );
CDT::Finite_faces_iterator fit;
for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ )
{
if(fit->is_in_domain()) {
CDTPoint p1 = cdt.triangle( fit )[0];
CDTPoint p2 = cdt.triangle( fit )[1];
CDTPoint p3 = cdt.triangle( fit )[2];
CGAL_Point_3 cp1,cp2,cp3;
CGAL_Polygon_3 pgon;
if (vertmap.count(p1)) cp1 = vertmap[p1];
else err = deproject( p1, goodproj, plane, cp1 );
if (vertmap.count(p2)) cp2 = vertmap[p2];
else err = deproject( p2, goodproj, plane, cp2 );
if (vertmap.count(p3)) cp3 = vertmap[p3];
else err = deproject( p3, goodproj, plane, cp3 );
if (err) PRINT("WARNING: 2d->3d deprojection failure");
pgon.push_back( cp1 );
pgon.push_back( cp2 );
pgon.push_back( cp3 );
triangles.push_back( pgon );
}
}
PRINTDB("built %i triangles",triangles.size());
return err;
}
};

View File

@ -1,433 +1,198 @@
/*
This is our custom tessellator of Nef Polyhedron faces. The problem with
Nef faces is that sometimes the 'default' tessellator of Nef Polyhedron
doesnt work. This is particularly true with situations where the polygon
face is not, actually, 'simple', according to CGAL itself. This can
occur on a bad quality STL import but also for other reasons. The
resulting Nef face will appear to the average human eye as an ordinary,
simple polygon... but in reality it has multiple edges that are
slightly-out-of-alignment and sometimes they backtrack on themselves.
When the triangulator is fed a polygon with self-intersecting edges,
it's default behavior is to throw an exception. The other terminology
for this is to say that the 'constraints' in the triangulation are
'intersecting'. The 'constraints' represent the edges of the polygon.
The 'triangulation' is the covering of all the polygon points with
triangles.
How do we allow interseting constraints during triangulation? We use an
'Itag' for the triangulation, per the CGAL docs. This allows the
triangulator to run without throwing an exception when it encounters
self-intersecting polygon edges. The trick here is that when it finds
an intersection, it actually creates a new point.
The triangulator creates new points in 2d, but they aren't matched to
any 3d points on our 3d polygon plane. (The plane of the Nef face). How
to fix this problem? We actually 'project back up' or 'lift' into the 3d
plane from the 2d point. This is handled in the 'deproject()' function.
There is also the issue of the Simplicity of Nef Polyhedron face
polygons. They are often not simple. The intersecting-constraints
Triangulation can triangulate non-simple polygons, but of course it's
result is also non-simple. This means that CGAL functions like
orientation_2() and bounded_side() simply will not work on the resulting
polygons because they all require input polygons to pass the
'is_simple2()' test. We have to use alternatives in order to create our
triangles.
There is also the question of which underlying number type to use. Some
of the CGAL functions simply dont guarantee good results with a type
like double. Although much the math here is somewhat simple, like
line-line intersection, and involves only simple algebra, the
approximations required when using floating-point types can cause the
answers to be wrong. For example questions like 'is a point inside a
triangle' do not have good answers under floating-point systems where a
line may have a slope that is not expressible exactly as a floating
point number. There are ways to deal with floating point inaccuracy but
it is much, much simpler to use Rational numbers, although potentially
much slower in many cases.
*/
#include "cgalutils.h"
#include <CGAL/Delaunay_mesher_no_edge_refinement_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
//#include "cgal.h"
//#include "tess.h"
typedef CGAL_Kernel3 Kernel;
//typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_intersections_tag ITAG;
typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT;
//typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT;
#ifdef NDEBUG
#define PREV_NDEBUG NDEBUG
#undef NDEBUG
#endif
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_2_filtered_projection_traits_3.h>
#include <CGAL/Triangulation_face_base_with_info_2.h>
#ifdef PREV_NDEBUG
#define NDEBUG PREV_NDEBUG
#endif
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Point CDTPoint;
#include <boost/foreach.hpp>
typedef CGAL::Ray_2<Kernel> CGAL_Ray_2;
typedef CGAL::Line_3<Kernel> CGAL_Line_3;
typedef CGAL::Point_2<Kernel> CGAL_Point_2;
typedef CGAL::Vector_2<Kernel> CGAL_Vector_2;
typedef CGAL::Segment_2<Kernel> CGAL_Segment_2;
typedef CGAL::Direction_2<Kernel> CGAL_Direction_2;
typedef CGAL::Direction_3<Kernel> CGAL_Direction_3;
typedef CGAL::Plane_3<Kernel> CGAL_Plane_3;
/* The idea of 'projection' is how we make 3d points appear as though
they were 2d points to the tessellation algorithm. We take the 3-d plane
on which the polygon lies, and then 'project' or 'cast its shadow' onto
one of three standard planes, the xyplane, the yzplane, or the xzplane,
depending on which projection will prevent the polygon looking like a
flat line. (imagine, the triangle 0,0,1 0,1,1 0,1,0 ... if viewed from
the 'top' it looks line a flat line. so we want to view it from the
side). Thus we create a sequence of x,y points to feed to the algorithm,
but those points might actually be x,z pairs or y,z pairs... it is an
illusion we present to the triangulation algorithm by way of 'projection'.
We get a resulting sequence of triangles with x,y coordinates, which we
then 'deproject' back to x,z or y,z, in 3d space as needed. For example
the square 0,0,0 0,0,1 0,1,1 0,1,0 becomes '0,0 0,1 1,1 1,0', is then
split into two triangles, 0,0 1,0 1,1 and 0,0 1,1 0,1. those two triangles
then are projected back to 3d as 0,0,0 0,1,0 0,1,1 and 0,0 0,1,1 0,0,1.
There is an additional trick we do with projection related to Polygon
orientation and the orientation of our output triangles, and thus, which
way they are facing in space (aka their 'normals' or 'oriented side').
The basic issues is this: every 3d flat polygon can be thought of as
having two sides. In Computer Graphics the convention is that the
'outside' or 'oriented side' or 'normal' is determined by looking at the
triangle in terms of the 'ordering' or 'winding' of the points. If the
points come in a 'clockwise' order, you must be looking at the triangle
from 'inside'. If the points come in a 'counterclockwise' order, you
must be looking at the triangle from the outside. For example, the
triangle 0,0,0 1,0,0 0,1,0, when viewed from the 'top', has points in a
counterclockwise order, so the 'up' side is the 'normal' or 'outside'.
if you look at that same triangle from the 'bottom' side, the points
will appear to be 'clockwise', so the 'down' side is the 'inside', and is the
opposite of the 'normal' side.
How do we keep track of all that when doing a triangulation? We could
check each triangle as it was generated, and fix it's orientation before
we feed it back to our output list. That is done by, for example, checking
the orientation of the input polygon and then forcing the triangle to
match that orientation during output. This is what CGAL's Nef Polyhedron
does, you can read it inside /usr/include/CGAL/Nef_polyhedron_3.h.
Or.... we could actually add an additional 'projection' to the incoming
polygon points so that our triangulation algorithm is guaranteed to
create triangles with the proper orientation in the first place. How?
First, we assume that the triangulation algorithm will always produce
'counterclockwise' triangles in our plain old x-y plane.
The method is based on the following curious fact: That is, if you take
the points of a polygon, and flip the x,y coordinate of each point,
making y:=x and x:=y, then you essentially get a 'mirror image' of the
original polygon... but the orientation will be flipped. Given a
clockwise polygon, the 'flip' will result in a 'counterclockwise'
polygon mirror-image and vice versa.
Now, there is a second curious fact that helps us here. In 3d, we are
using the plane equation of ax+by+cz+d=0, where a,b,c determine its
direction. If you notice, there are actually mutiple sets of numbers
a:b:c that will describe the exact same plane. For example the 'ground'
plane, called the XYplane, where z is everywhere 0, has the equation
0x+0y+1z+0=0, simplifying to a solution for x,y,z of z=0 and x,y = any
numbers in your number system. However you can also express this as
0x+0y+-1z=0. The x,y,z solution is the same: z is everywhere 0, x and y
are any number, even though a,b,c are different. We can say that the
plane is 'oriented' differently, if we wish.
But how can we link that concept to the points on the polygon? Well, if
you generate a plane using the standard plane-equation generation
formula, given three points M,N,P, then you will get a plane equation
with <a:b:c:d>. However if you feed the points in the reverse order,
P,N,M, so that they are now oriented in the opposite order, you will get
a plane equation with the signs flipped. <-a:-b:-c:-d> This means you
can essentially consider that a plane has an 'orientation' based on it's
equation, by looking at the signs of a,b,c relative to some other
quantity.
This means that you can 'flip' the projection of the input polygon
points so that the projection will match the orientation of the input
plane, thus guaranteeing that the output triangles will be oriented in
the same direction as the input polygon was. In other words, even though
we technically 'lose information' when we project from 3d->2d, we can
actually keep the concept of 'orientation' through the whole
triangulation process, and not have to recalculate the proper
orientation during output.
For example take two side-squares of a cube and the plane equations
formed by feeding the points in counterclockwise, as if looking in from
outside the cube:
0,0,0 0,1,0 0,1,1 0,0,1 <-1:0:0:0>
1,0,0 1,1,0 1,1,1 1,0,1 <1:0:0:1>
They are both projected onto the YZ plane. They look the same:
0,0 1,0 1,1 0,1
0,0 1,0 1,1 0,1
But the second square plane has opposite orientation, so we flip the x
and y for each point:
0,0 1,0 1,1 0,1
0,0 0,1 1,1 1,0
Only now do we feed these two 2-d squares to the tessellation algorithm.
The result is 4 triangles. When de-projected back to 3d, they will have
the appropriate winding that will match that of the original 3d faces.
And the first two triangles will have opposite orientation from the last two.
*/
typedef enum { XYPLANE, YZPLANE, XZPLANE, NONE } plane_t;
struct projection_t {
plane_t plane;
bool flip;
struct FaceInfo {
int nesting_level;
bool in_domain() { return nesting_level%2 == 1; }
};
CGAL_Point_2 get_projected_point( CGAL_Point_3 &p3, projection_t projection ) {
NT3 x,y;
if (projection.plane == XYPLANE) { x = p3.x(); y = p3.y(); }
else if (projection.plane == XZPLANE) { x = p3.x(); y = p3.z(); }
else if (projection.plane == YZPLANE) { x = p3.y(); y = p3.z(); }
else if (projection.plane == NONE) { x = 0; y = 0; }
if (projection.flip) return CGAL_Point_2( y,x );
return CGAL_Point_2( x,y );
}
typedef CGAL::Triangulation_2_filtered_projection_traits_3<K> Projection;
typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo, K> Fbb;
typedef CGAL::Triangulation_data_structure_2<
CGAL::Triangulation_vertex_base_2<Projection>,
CGAL::Constrained_triangulation_face_base_2<Projection, Fbb> > Tds;
typedef CGAL::Constrained_Delaunay_triangulation_2<
Projection, Tds, CGAL::Exact_predicates_tag> CDT;
/* given 2d point, 3d plane, and 3d->2d projection, 'deproject' from
2d back onto a point on the 3d plane. true on failure, false on success */
bool deproject( CGAL_Point_2 &p2, projection_t &projection, CGAL_Plane_3 &plane, CGAL_Point_3 &p3 )
static void mark_domains(CDT &ct,
CDT::Face_handle start,
int index,
std::list<CDT::Edge>& border)
{
NT3 x,y;
CGAL_Line_3 l;
CGAL_Point_3 p;
CGAL_Point_2 pf( p2.x(), p2.y() );
if (projection.flip) pf = CGAL_Point_2( p2.y(), p2.x() );
if (projection.plane == XYPLANE) {
p = CGAL_Point_3( pf.x(), pf.y(), 0 );
l = CGAL_Line_3( p, CGAL_Direction_3(0,0,1) );
} else if (projection.plane == XZPLANE) {
p = CGAL_Point_3( pf.x(), 0, pf.y() );
l = CGAL_Line_3( p, CGAL_Direction_3(0,1,0) );
} else if (projection.plane == YZPLANE) {
p = CGAL_Point_3( 0, pf.x(), pf.y() );
l = CGAL_Line_3( p, CGAL_Direction_3(1,0,0) );
}
CGAL::Object obj = CGAL::intersection( l, plane );
const CGAL_Point_3 *point_test = CGAL::object_cast<CGAL_Point_3>(&obj);
if (point_test) {
p3 = *point_test;
return false;
}
PRINT("ERROR: deproject failure");
return true;
if (start->info().nesting_level != -1) return;
std::list<CDT::Face_handle> queue;
queue.push_back(start);
while (!queue.empty()) {
CDT::Face_handle fh = queue.front();
queue.pop_front();
if (fh->info().nesting_level == -1) {
fh->info().nesting_level = index;
for (int i = 0; i < 3; i++) {
CDT::Edge e(fh,i);
CDT::Face_handle n = fh->neighbor(i);
if (n->info().nesting_level == -1) {
if (ct.is_constrained(e)) border.push_back(e);
else queue.push_back(n);
}
}
}
}
}
/* this simple criteria guarantees CGALs triangulation algorithm will
terminate (i.e. not lock up and freeze the program) */
template <class T> class DummyCriteria {
public:
typedef double Quality;
class Is_bad {
public:
CGAL::Mesh_2::Face_badness operator()(const Quality) const {
return CGAL::Mesh_2::NOT_BAD;
}
CGAL::Mesh_2::Face_badness operator()(const typename T::Face_handle&, Quality&q) const {
q = 1;
return CGAL::Mesh_2::NOT_BAD;
}
};
Is_bad is_bad_object() const { return Is_bad(); }
};
NT3 sign( const NT3 &n )
//explore set of facets connected with non constrained edges,
//and attribute to each such set a nesting level.
//We start from facets incident to the infinite vertex, with a nesting
//level of 0. Then we recursively consider the non-explored facets incident
//to constrained edges bounding the former set and increase the nesting level by 1.
//Facets in the domain are those with an odd nesting level.
static void mark_domains(CDT& cdt)
{
if (n>0) return NT3(1);
if (n<0) return NT3(-1);
return NT3(0);
}
/* wedge, also related to 'determinant', 'signed parallelogram area',
'side', 'turn', 'winding', '2d portion of cross-product', etc etc. this
function can tell you whether v1 is 'counterclockwise' or 'clockwise'
from v2, based on the sign of the result. when the input Vectors are
formed from three points, A-B and B-C, it can tell you if the path along
the points A->B->C is turning left or right.*/
NT3 wedge( CGAL_Vector_2 &v1, CGAL_Vector_2 &v2 ) {
return v1.x()*v2.y()-v2.x()*v1.y();
}
/* given a point and a possibly non-simple polygon, determine if the
point is inside the polygon or not, using the given winding rule. note
that even_odd is not implemented. */
typedef enum { NONZERO_WINDING, EVEN_ODD } winding_rule_t;
bool inside(CGAL_Point_2 &p1,std::vector<CGAL_Point_2> &pgon, winding_rule_t winding_rule)
{
NT3 winding_sum = NT3(0);
CGAL_Point_2 p2;
CGAL_Ray_2 eastray(p1,CGAL_Direction_2(1,0));
for (size_t i=0;i<pgon.size();i++) {
CGAL_Point_2 tail = pgon[i];
CGAL_Point_2 head = pgon[(i+1)%pgon.size()];
CGAL_Segment_2 seg( tail, head );
CGAL::Object obj = intersection( eastray, seg );
const CGAL_Point_2 *point_test = CGAL::object_cast<CGAL_Point_2>(&obj);
if (point_test) {
p2 = *point_test;
CGAL_Vector_2 v1( p1, p2 );
CGAL_Vector_2 v2( p2, head );
NT3 this_winding = wedge( v1, v2 );
winding_sum += sign(this_winding);
} else {
continue;
}
}
if (winding_sum != NT3(0) && winding_rule == NONZERO_WINDING ) return true;
return false;
}
projection_t find_good_projection( CGAL_Plane_3 &plane )
{
projection_t goodproj;
goodproj.plane = NONE;
goodproj.flip = false;
NT3 qxy = plane.a()*plane.a()+plane.b()*plane.b();
NT3 qyz = plane.b()*plane.b()+plane.c()*plane.c();
NT3 qxz = plane.a()*plane.a()+plane.c()*plane.c();
NT3 min = std::min(qxy,std::min(qyz,qxz));
if (min==qxy) {
goodproj.plane = XYPLANE;
if (sign(plane.c())>0) goodproj.flip = true;
} else if (min==qyz) {
goodproj.plane = YZPLANE;
if (sign(plane.a())>0) goodproj.flip = true;
} else if (min==qxz) {
goodproj.plane = XZPLANE;
if (sign(plane.b())<0) goodproj.flip = true;
} else PRINT("ERROR: failed to find projection");
return goodproj;
for(CDT::All_faces_iterator it = cdt.all_faces_begin(); it != cdt.all_faces_end(); ++it) {
it->info().nesting_level = -1;
}
std::list<CDT::Edge> border;
mark_domains(cdt, cdt.infinite_face(), 0, border);
while (!border.empty()) {
CDT::Edge e = border.front();
border.pop_front();
CDT::Face_handle n = e.first->neighbor(e.second);
if (n->info().nesting_level == -1) {
mark_domains(cdt, n, e.first->info().nesting_level+1, border);
}
}
}
namespace CGALUtils {
/* given a single near-planar 3d polygon with holes, tessellate into a
sequence of polygons without holes. as of writing, this means conversion
into a sequence of 3d triangles. the given plane should be the same plane
holding the polygon and it's holes. */
bool tessellate3DFaceWithHoles(std::vector<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &triangles,
CGAL_Plane_3 &plane)
/*!
polygons define a polygon, potentially with holes. Each contour
should be added as a separate PolygonK instance.
The tessellator will handle almost planar polygons.
If the normal is given, we will assume this as the normal vector of the polygon.
Otherwise, we will try to calculate it using Newell's method.
The resulting triangles is added to the given triangles vector.
*/
bool tessellatePolygonWithHoles(const PolyholeK &polygons,
Polygons &triangles,
const K::Vector_3 *normal)
{
if (polygons.size()==1 && polygons[0].size()==3) {
// No polygon. FIXME: Will this ever happen or can we assert here?
if (polygons.empty()) return false;
// No hole
if (polygons.size() == 1) return tessellatePolygon(polygons.front(), triangles, normal);
K::Vector_3 normalvec;
if (normal) {
normalvec = *normal;
}
else {
// Calculate best guess at face normal using Newell's method
CGAL::normal_vector_newell_3(polygons.front().begin(), polygons.front().end(), normalvec);
}
// Pass the normal vector to the (undocumented)
// CGAL::Triangulation_2_filtered_projection_traits_3. This
// trait deals with projection from 3D to 2D using the normal
// vector as a hint, and allows for near-planar polygons to be passed to
// the Constrained Delaunay Triangulator.
Projection actualProjection(normalvec);
CDT cdt(actualProjection);
BOOST_FOREACH(const PolygonK &poly, polygons) {
for (size_t i=0;i<poly.size(); i++) {
cdt.insert_constraint(poly[i], poly[(i+1)%poly.size()]);
}
}
//Mark facets that are inside the domain bounded by the polygon
mark_domains(cdt);
// Iterate over the resulting faces
for (CDT::Finite_faces_iterator fit = cdt.finite_faces_begin();
fit != cdt.finite_faces_end(); fit++) {
if (fit->info().in_domain()) {
Polygon tri;
for (int i=0;i<3;i++) {
Vertex3K v = cdt.triangle(fit)[i];
tri.push_back(Vector3d(v.x(), v.y(), v.z()));
}
triangles.push_back(tri);
}
}
return false;
}
bool tessellatePolygon(const PolygonK &polygon,
Polygons &triangles,
const K::Vector_3 *normal)
{
if (polygon.size() == 3) {
PRINTD("input polygon has 3 points. shortcut tessellation.");
CGAL_Polygon_3 t;
t.push_back(polygons[0][2]);
t.push_back(polygons[0][1]);
t.push_back(polygons[0][0]);
triangles.push_back( t );
Polygon t;
t.push_back(Vector3d(polygon[0].x(), polygon[0].y(), polygon[0].z()));
t.push_back(Vector3d(polygon[1].x(), polygon[1].y(), polygon[1].z()));
t.push_back(Vector3d(polygon[2].x(), polygon[2].y(), polygon[2].z()));
triangles.push_back(t);
return false;
}
bool err = false;
CDT cdt;
std::map<CDTPoint,CGAL_Point_3> vertmap;
PRINTD("finding good projection");
projection_t goodproj = find_good_projection( plane );
PRINTDB("plane %s",plane );
PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip);
PRINTD("Inserting points and edges into Constrained Delaunay Triangulation");
std::vector< std::vector<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
K::Vector_3 normalvec;
if (normal) {
normalvec = *normal;
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
else {
// Calculate best guess at face normal using Newell's method
CGAL::normal_vector_newell_3(polygon.begin(), polygon.end(), normalvec);
}
std::list<CDTPoint>::iterator li = list_of_seeds.begin();
for (;li!=list_of_seeds.end();li++) {
//PRINTB("seed %s",*li);
double x = CGAL::to_double( li->x() );
double y = CGAL::to_double( li->y() );
PRINTDB("seed %f,%f",x%y);
// Pass the normal vector to the (undocumented)
// CGAL::Triangulation_2_filtered_projection_traits_3. This
// trait deals with projection from 3D to 2D using the normal
// vector as a hint, and allows for near-planar polygons to be passed to
// the Constrained Delaunay Triangulator.
Projection actualProjection(normalvec);
CDT cdt(actualProjection);
for (size_t i=0;i<polygon.size(); i++) {
cdt.insert_constraint(polygon[i], polygon[(i+1)%polygon.size()]);
}
PRINTD("seeding done");
PRINTD( "meshing" );
CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt,
list_of_seeds.begin(), list_of_seeds.end(),
DummyCriteria<CDT>() );
PRINTD("meshing done");
// this fails because it calls is_simple and is_simple fails on many
// Nef Polyhedron faces
//CGAL::Orientation original_orientation =
// CGAL::orientation_2( orienpgon.begin(), orienpgon.end() );
//Mark facets that are inside the domain bounded by the polygon
mark_domains(cdt);
// Iterate over the resulting faces
CDT::Finite_faces_iterator fit;
for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ )
{
if(fit->is_in_domain()) {
CDTPoint p1 = cdt.triangle( fit )[0];
CDTPoint p2 = cdt.triangle( fit )[1];
CDTPoint p3 = cdt.triangle( fit )[2];
CGAL_Point_3 cp1,cp2,cp3;
CGAL_Polygon_3 pgon;
if (vertmap.count(p1)) cp1 = vertmap[p1];
else err = deproject( p1, goodproj, plane, cp1 );
if (vertmap.count(p2)) cp2 = vertmap[p2];
else err = deproject( p2, goodproj, plane, cp2 );
if (vertmap.count(p3)) cp3 = vertmap[p3];
else err = deproject( p3, goodproj, plane, cp3 );
if (err) PRINT("WARNING: 2d->3d deprojection failure");
pgon.push_back( cp1 );
pgon.push_back( cp2 );
pgon.push_back( cp3 );
triangles.push_back( pgon );
for (fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++) {
if (fit->info().in_domain()) {
Polygon tri;
for (int i=0;i<3;i++) {
K::Point_3 v = cdt.triangle(fit)[i];
tri.push_back(Vector3d(v.x(), v.y(), v.z()));
}
triangles.push_back(tri);
}
}
PRINTDB("built %i triangles",triangles.size());
return err;
return false;
}
};
}; // namespace CGALUtils

View File

@ -21,21 +21,21 @@
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
namespace Eigen {
size_t hash_value(Vector3d const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
}
namespace /* anonymous */ {
template<typename Result, typename V>
Result vector_convert(V const& v) {
return Result(CGAL::to_double(v[0]),CGAL::to_double(v[1]),CGAL::to_double(v[2]));
}
#undef GEN_SURFACE_DEBUG
namespace Eigen {
size_t hash_value(Vector3d const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
}
#define GEN_SURFACE_DEBUG
class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS>
{
@ -67,7 +67,7 @@ namespace /* anonymous */ {
Grid3d<int> grid(GRID_FINE);
std::vector<size_t> indices(3);
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
BOOST_FOREACH(const Polygon &p, ps.polygons) {
BOOST_REVERSE_FOREACH(Vector3d v, p) {
if (!grid.has(v[0], v[1], v[2])) {
// align v to the grid; the CGALPoint will receive the aligned vertex
@ -85,7 +85,7 @@ namespace /* anonymous */ {
BOOST_FOREACH(const CGALPoint &p, vertices) {
B.add_vertex(p);
}
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
BOOST_FOREACH(const Polygon &p, ps.polygons) {
#ifdef GEN_SURFACE_DEBUG
if (pidx++ > 0) printf(",");
#endif
@ -138,7 +138,6 @@ namespace /* anonymous */ {
void operator()(CGAL_HDS& hds)
{
CGAL_Polybuilder B(hds, true);
typedef boost::tuple<double, double, double> BuilderVertex;
Reindexer<Vector3d> vertices;
std::vector<size_t> indices(3);
@ -148,7 +147,7 @@ namespace /* anonymous */ {
#ifdef GEN_SURFACE_DEBUG
printf("polyhedron(faces=[");
#endif
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
BOOST_FOREACH(const Polygon &p, ps.polygons) {
#ifdef GEN_SURFACE_DEBUG
if (pidx++ > 0) printf(",");
#endif
@ -230,14 +229,14 @@ namespace /* anonymous */ {
vi = in_poly.vertices_begin(), end = in_poly.vertices_end();
vi != end ; ++vi)
{
typename Polyhedron_output::Point_3 p(::CGAL::to_double( vi->point().x()),
::CGAL::to_double( vi->point().y()),
::CGAL::to_double( vi->point().z()));
typename Polyhedron_output::Point_3 p(::CGAL::to_double(vi->point().x()),
::CGAL::to_double(vi->point().y()),
::CGAL::to_double(vi->point().z()));
builder.add_vertex(p);
}
typedef CGAL::Inverse_index<Vertex_const_iterator> Index;
Index index( in_poly.vertices_begin(), in_poly.vertices_end());
Index index(in_poly.vertices_begin(), in_poly.vertices_end());
for(Facet_const_iterator
fi = in_poly.facets_begin(), end = in_poly.facets_end();
@ -245,13 +244,13 @@ namespace /* anonymous */ {
{
HFCC hc = fi->facet_begin();
HFCC hc_end = hc;
// std::size_t n = circulator_size( hc);
// CGAL_assertion( n >= 3);
// std::size_t n = circulator_size(hc);
// CGAL_assertion(n >= 3);
builder.begin_facet ();
do {
builder.add_vertex_to_facet(index[hc->vertex()]);
++hc;
} while( hc != hc_end);
} while(hc != hc_end);
builder.end_facet();
}
builder.end_surface();
@ -300,19 +299,18 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps)
try {
CGAL_Polyhedron P;
bool err = CGALUtils::createPolyhedronFromPolySet(ps, P);
// if (!err) {
// PRINTB("Polyhedron is closed: %d", P.is_closed());
// PRINTB("Polyhedron is valid: %d", P.is_valid(false, 0));
// }
if (!err) {
PRINTDB("Polyhedron is closed: %d", P.is_closed());
PRINTDB("Polyhedron is valid: %d", P.is_valid(false, 0));
}
if (!err) N = new CGAL_Nef_polyhedron3(P);
}
catch (const CGAL::Assertion_exception &e) {
if (std::string(e.what()).find("Plane_constructor")!=std::string::npos) {
if (std::string(e.what()).find("has_on")!=std::string::npos) {
if (std::string(e.what()).find("Plane_constructor")!=std::string::npos &&
std::string(e.what()).find("has_on")!=std::string::npos) {
PRINT("PolySet has nonplanar faces. Attempting alternate construction");
plane_error=true;
}
} else {
PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what());
}
@ -373,7 +371,7 @@ namespace CGALUtils {
} else {
const PolySet *ps = dynamic_cast<const PolySet *>(chgeom.get());
if (ps) {
BOOST_FOREACH(const PolySet::Polygon &p, ps->polygons) {
BOOST_FOREACH(const Polygon &p, ps->polygons) {
BOOST_FOREACH(const Vector3d &v, p) {
points.insert(K::Point_3(v[0], v[1], v[2]));
}
@ -461,7 +459,7 @@ namespace CGALUtils {
if ((ps && ps->is_convex()) ||
(!ps && is_weakly_convex(poly))) {
PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef") );
PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef"));
P[i].push_back(poly);
} else {
CGAL_Nef_polyhedron3 decomposed_nef;
@ -480,7 +478,7 @@ namespace CGALUtils {
// the first volume is the outer volume, which ignored in the decomposition
CGAL_Nef_polyhedron3::Volume_const_iterator ci = ++decomposed_nef.volumes_begin();
for( ; ci != decomposed_nef.volumes_end(); ++ci) {
for(; ci != decomposed_nef.volumes_end(); ++ci) {
if(ci->mark()) {
CGAL_Polyhedron poly;
decomposed_nef.convert_inner_shell_to_polyhedron(ci->shells_begin(), poly);
@ -844,13 +842,13 @@ namespace CGALUtils {
// dont use z of 0. there are bugs in CGAL.
double inf = 1e8;
double eps = 0.001;
CGAL_Point_3 minpt( -inf, -inf, -eps );
CGAL_Point_3 maxpt( inf, inf, eps );
CGAL_Iso_cuboid_3 bigcuboid( minpt, maxpt );
for ( int i=0;i<8;i++ ) pts.push_back( bigcuboid.vertex(i) );
CGAL_Point_3 minpt(-inf, -inf, -eps);
CGAL_Point_3 maxpt( inf, inf, eps);
CGAL_Iso_cuboid_3 bigcuboid(minpt, maxpt);
for (int i=0;i<8;i++) pts.push_back(bigcuboid.vertex(i));
CGAL_Polyhedron bigbox;
CGAL::convex_hull_3(pts.begin(), pts.end(), bigbox);
CGAL_Nef_polyhedron3 nef_bigbox( bigbox );
CGAL_Nef_polyhedron3 nef_bigbox(bigbox);
newN.p3.reset(new CGAL_Nef_polyhedron3(nef_bigbox.intersection(*N.p3)));
}
catch (const CGAL::Failure_exception &e) {
@ -864,18 +862,18 @@ namespace CGALUtils {
return poly;
}
PRINTDB("%s",OpenSCAD::svg_header( 480, 100000 ));
PRINTDB("%s",OpenSCAD::svg_header(480, 100000));
try {
ZRemover zremover;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
CGAL_Nef_polyhedron3::SFace_const_handle sface_handle;
for ( i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i ) {
for (i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i) {
PRINTDB("<!-- volume. mark: %s -->",i->mark());
for ( j = i->shells_begin(); j != i->shells_end(); ++j ) {
for (j = i->shells_begin(); j != i->shells_end(); ++j) {
PRINTDB("<!-- shell. (vol mark was: %i)", i->mark());;
sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j );
newN.p3->visit_shell_objects( sface_handle , zremover );
sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle(j);
newN.p3->visit_shell_objects(sface_handle , zremover);
PRINTD("<!-- shell. end. -->");
}
PRINTD("<!-- volume end. -->");
@ -909,7 +907,7 @@ namespace CGALUtils {
// can be optimized by rewriting bounding_box to accept vertices
CGAL_forall_vertices(vi, N)
points.push_back(vi->point());
if (points.size()) result = CGAL::bounding_box( points.begin(), points.end() );
if (points.size()) result = CGAL::bounding_box(points.begin(), points.end());
return result;
}
@ -1052,25 +1050,26 @@ namespace CGALUtils {
formats) do not allow for holes in their faces. The function documents
the method used to deal with this
*/
#if 0
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti;
CGAL_forall_halffacets( hfaceti, N ) {
CGAL::Plane_3<CGAL_Kernel3> plane( hfaceti->plane() );
std::vector<CGAL_Polygon_3> polygons;
CGAL_forall_halffacets(hfaceti, N) {
CGAL::Plane_3<CGAL_Kernel3> plane(hfaceti->plane());
std::vector<CGAL_Polygon_3> polyholes;
// the 0-mark-volume is the 'empty' volume of space. skip it.
if (hfaceti->incident_volume()->mark()) continue;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei;
CGAL_forall_facet_cycles_of( cyclei, hfaceti ) {
CGAL_forall_facet_cycles_of(cyclei, hfaceti) {
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei);
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1);
CGAL_Polygon_3 polygon;
CGAL_For_all( c1, c2 ) {
CGAL_For_all(c1, c2) {
CGAL_Point_3 p = c1->source()->center_vertex()->point();
polygon.push_back( p );
polygon.push_back(p);
}
polygons.push_back( polygon );
polyholes.push_back(polygon);
}
/* at this stage, we have a sequence of polygons. the first
@ -1079,28 +1078,125 @@ namespace CGALUtils {
options here to get rid of the holes. we choose to go ahead
and let the tessellater deal with the holes, and then
just output the resulting 3d triangles*/
CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<CGAL_Polygon_3> triangles;
bool err = CGALUtils::tessellate3DFaceWithHoles(polygons, triangles, plane);
bool err = CGALUtils::tessellate3DFaceWithHoles(polyholes, triangles, plane);
if (!err) {
for (size_t i=0;i<triangles.size();i++) {
if (triangles[i].size()!=3) {
BOOST_FOREACH(const CGAL_Polygon_3 &p, triangles) {
if (p.size() != 3) {
PRINT("WARNING: triangle doesn't have 3 points. skipping");
continue;
}
ps.append_poly();
for (int j=2;j>=0;j--) {
double x1,y1,z1;
x1 = CGAL::to_double(triangles[i][j].x());
y1 = CGAL::to_double(triangles[i][j].y());
z1 = CGAL::to_double(triangles[i][j].z());
ps.append_vertex(x1,y1,z1);
}
ps.append_vertex(CGAL::to_double(p[2].x()), CGAL::to_double(p[2].y()), CGAL::to_double(p[2].z()));
ps.append_vertex(CGAL::to_double(p[1].x()), CGAL::to_double(p[1].y()), CGAL::to_double(p[1].z()));
ps.append_vertex(CGAL::to_double(p[0].x()), CGAL::to_double(p[0].y()), CGAL::to_double(p[0].z()));
}
}
}
return err;
}
#endif
#if 0
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti;
CGAL_forall_halffacets(hfaceti, N) {
CGAL::Plane_3<CGAL_Kernel3> plane(hfaceti->plane());
std::vector<CGAL_Polygon_3> polyholes;
// the 0-mark-volume is the 'empty' volume of space. skip it.
if (hfaceti->incident_volume()->mark()) continue;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei;
CGAL_forall_facet_cycles_of(cyclei, hfaceti) {
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei);
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1);
CGAL_Polygon_3 polygon;
CGAL_For_all(c1, c2) {
CGAL_Point_3 p = c1->source()->center_vertex()->point();
polygon.push_back(p);
}
polyholes.push_back(polygon);
}
/* at this stage, we have a sequence of polygons. the first
is the "outside edge' or 'body' or 'border', and the rest of the
polygons are 'holes' within the first. there are several
options here to get rid of the holes. we choose to go ahead
and let the tessellater deal with the holes, and then
just output the resulting 3d triangles*/
CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<Polygon> triangles;
bool err = CGALUtils::tessellate3DFaceWithHolesNew(polyholes, triangles, plane);
if (!err) {
BOOST_FOREACH(const Polygon &p, triangles) {
if (p.size() != 3) {
PRINT("WARNING: triangle doesn't have 3 points. skipping");
continue;
}
ps.append_poly();
ps.append_vertex(p[0].x(), p[0].y(), p[0].z());
ps.append_vertex(p[1].x(), p[1].y(), p[1].z());
ps.append_vertex(p[2].x(), p[2].y(), p[2].z());
}
}
}
return err;
}
#endif
#if 1
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti;
CGAL_forall_halffacets(hfaceti, N) {
CGAL::Plane_3<CGAL_Kernel3> plane(hfaceti->plane());
PolyholeK polyholes;
// the 0-mark-volume is the 'empty' volume of space. skip it.
if (hfaceti->incident_volume()->mark()) continue;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei;
CGAL_forall_facet_cycles_of(cyclei, hfaceti) {
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei);
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1);
PolygonK polygon;
CGAL_For_all(c1, c2) {
CGAL_Point_3 p = c1->source()->center_vertex()->point();
polygon.push_back(Vertex3K(CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z())));
}
polyholes.push_back(polygon);
}
/* at this stage, we have a sequence of polygons. the first
is the "outside edge' or 'body' or 'border', and the rest of the
polygons are 'holes' within the first. there are several
options here to get rid of the holes. we choose to go ahead
and let the tessellater deal with the holes, and then
just output the resulting 3d triangles*/
CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<Polygon> triangles;
bool err = CGALUtils::tessellatePolygonWithHoles(polyholes, triangles, &normal);
if (!err) {
BOOST_FOREACH(const Polygon &p, triangles) {
if (p.size() != 3) {
PRINT("WARNING: triangle doesn't have 3 points. skipping");
continue;
}
ps.append_poly();
ps.append_vertex(p[0].x(), p[0].y(), p[0].z());
ps.append_vertex(p[1].x(), p[1].y(), p[1].z());
ps.append_vertex(p[2].x(), p[2].y(), p[2].z());
}
}
}
return err;
}
#endif
CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom)
{
const PolySet *ps = dynamic_cast<const PolySet*>(&geom);
@ -1117,10 +1213,10 @@ namespace CGALUtils {
}; // namespace CGALUtils
void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )
void ZRemover::visit(CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet)
{
PRINTDB(" <!-- ZRemover Halffacet visit. Mark: %i --> ",hfacet->mark());
if ( hfacet->plane().orthogonal_direction() != this->up ) {
if (hfacet->plane().orthogonal_direction() != this->up) {
PRINTD(" <!-- ZRemover down-facing half-facet. skipping -->");
PRINTD(" <!-- ZRemover Halffacet visit end-->");
return;
@ -1130,36 +1226,36 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci;
int contour_counter = 0;
CGAL_forall_facet_cycles_of( fci, hfacet ) {
if ( fci.is_shalfedge() ) {
CGAL_forall_facet_cycles_of(fci, hfacet) {
if (fci.is_shalfedge()) {
PRINTD(" <!-- ZRemover Halffacet cycle begin -->");
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1);
std::vector<CGAL_Nef_polyhedron2::Explorer::Point> contour;
CGAL_For_all( c1, cend ) {
CGAL_For_all(c1, cend) {
CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point();
CGAL_Nef_polyhedron2::Explorer::Point point2d(CGAL::to_double(point3d.x()),
CGAL::to_double(point3d.y()));
contour.push_back( point2d );
contour.push_back(point2d);
}
if (contour.size()==0) continue;
if (OpenSCAD::debug!="")
PRINTDB(" <!-- is_simple_2: %i -->", CGAL::is_simple_2( contour.begin(), contour.end() ) );
PRINTDB(" <!-- is_simple_2: %i -->", CGAL::is_simple_2(contour.begin(), contour.end()));
tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
tmpnef2d.reset(new CGAL_Nef_polyhedron2(contour.begin(), contour.end(), boundary));
if ( contour_counter == 0 ) {
PRINTDB(" <!-- contour is a body. make union(). %i points -->", contour.size() );
if (contour_counter == 0) {
PRINTDB(" <!-- contour is a body. make union(). %i points -->", contour.size());
*(output_nefpoly2d) += *(tmpnef2d);
} else {
PRINTDB(" <!-- contour is a hole. make intersection(). %i points -->", contour.size() );
PRINTDB(" <!-- contour is a hole. make intersection(). %i points -->", contour.size());
*(output_nefpoly2d) *= *(tmpnef2d);
}
/*log << "\n<!-- ======== output tmp nef: ==== -->\n"
<< OpenSCAD::dump_svg( *tmpnef2d ) << "\n"
<< OpenSCAD::dump_svg(*tmpnef2d) << "\n"
<< "\n<!-- ======== output accumulator: ==== -->\n"
<< OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n";*/
<< OpenSCAD::dump_svg(*output_nefpoly2d) << "\n";*/
contour_counter++;
} else {

View File

@ -5,6 +5,12 @@
#include "CGAL_Nef_polyhedron.h"
#include "enums.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
typedef CGAL::Epick K;
typedef CGAL::Point_3<K> Vertex3K;
typedef std::vector<Vertex3K> PolygonK;
typedef std::vector<PolygonK> PolyholeK;
namespace CGALUtils {
bool applyHull(const Geometry::ChildList &children, PolySet &P);
void applyOperator(const Geometry::ChildList &children, CGAL_Nef_polyhedron &dest, OpenSCADOperator op);
@ -19,8 +25,16 @@ namespace CGALUtils {
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps);
bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p);
typedef std::vector<CGAL_Point_3> CGAL_Polygon_3;
bool tessellate3DFaceWithHoles(std::vector<CGAL_Polygon_3> &polygons,
bool tessellatePolygon(const PolygonK &polygon,
Polygons &triangles,
const K::Vector_3 *normal = NULL);
bool tessellatePolygonWithHoles(const PolyholeK &polygons,
Polygons &triangles,
const K::Vector_3 *normal = NULL);
bool tessellate3DFaceWithHolesNew(std::vector<CGAL_Polygon_3> &polygons,
Polygons &triangles,
CGAL::Plane_3<CGAL_Kernel3> &plane);
bool tessellate3DFaceWithHoles(std::vector<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &triangles,
CGAL::Plane_3<CGAL_Kernel3> &plane);
};

View File

@ -81,12 +81,17 @@ namespace ClipperUtils {
return result;
}
/*!
Apply the clipper operator to the given paths.
May return an empty Polygon2d, but will not return NULL.
*/
Polygon2d *apply(const std::vector<ClipperLib::Paths> &pathsvector,
ClipperLib::ClipType clipType)
{
ClipperLib::Clipper clipper;
if (clipType == ClipperLib::ctIntersection && pathsvector.size() > 2) {
if (clipType == ClipperLib::ctIntersection && pathsvector.size() >= 2) {
// intersection operations must be split into a sequence of binary operations
ClipperLib::Paths source = pathsvector[0];
ClipperLib::PolyTree result;
@ -109,13 +114,17 @@ namespace ClipperUtils {
}
ClipperLib::PolyTree sumresult;
clipper.Execute(clipType, sumresult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
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);
}
/*!
Apply the clipper operator to the given polygons.
May return an empty Polygon2d, but will not return NULL.
*/
Polygon2d *apply(const std::vector<const Polygon2d*> &polygons,
ClipperLib::ClipType clipType)
{
@ -125,7 +134,9 @@ namespace ClipperUtils {
if (!polygon->isSanitized()) ClipperLib::PolyTreeToPaths(sanitize(polypaths), polypaths);
pathsvector.push_back(polypaths);
}
return apply(pathsvector, clipType);
Polygon2d *res = apply(pathsvector, clipType);
assert(res);
return res;
}

View File

@ -42,7 +42,7 @@ class ColorModule : public AbstractModule
public:
ColorModule();
virtual ~ColorModule();
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
private:
boost::unordered_map<std::string, Color4f> webcolors;
@ -198,14 +198,15 @@ ColorModule::ColorModule()
("white", Color4f(255, 255, 255))
("whitesmoke", Color4f(245, 245, 245))
("yellow", Color4f(255, 255, 0))
("yellowgreen", Color4f(154, 205, 50));
("yellowgreen", Color4f(154, 205, 50))
.convert_to_container<boost::unordered_map<std::string, Color4f> >();
}
ColorModule::~ColorModule()
{
}
AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
ColorNode *node = new ColorNode(inst);
@ -218,6 +219,7 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
Value v = c.lookup_variable("c");
if (v.type() == Value::VECTOR) {

View File

@ -5,6 +5,29 @@
static const char *DEFAULT_COLOR_SCHEME_NAME = "Cornfield";
// See http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
static void rgbtohsv(float r, float g, float b, float &h, float &s, float &v)
{
float K = 0.f;
if (g < b)
{
std::swap(g, b);
K = -1.f;
}
if (r < g)
{
std::swap(r, g);
K = -2.f / 6.f - K;
}
float chroma = r - std::min(g, b);
h = fabs(K + (g - b) / (6.f * chroma + 1e-20f));
s = chroma / (r + 1e-20f);
v = r;
}
RenderColorScheme::RenderColorScheme() : _path("")
{
_name = DEFAULT_COLOR_SCHEME_NAME;
@ -196,6 +219,42 @@ Color4f ColorMap::getColor(const ColorScheme &cs, const RenderColor rc)
return Color4f(0, 0, 0, 127);
}
Color4f ColorMap::getColorHSV(const Color4f &col)
{
float h, s, v;
rgbtohsv(col[0], col[1], col[2], h, s, v);
return Color4f(h, s, v, col[3]);
}
/**
* Calculate contrast color. Based on the article
* http://gamedev.stackexchange.com/questions/38536/given-a-rgb-color-x-how-to-find-the-most-contrasting-color-y
*
* @param col the input color
* @return a color with high contrast to the input color
*/
Color4f ColorMap::getContrastColor(const Color4f &col)
{
Color4f hsv = ColorMap::getColorHSV(col);
float Y = 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2];
float S = hsv[1];
if (S < 0.5) {
// low saturation, choose between black / white based on luminance Y
float val = Y > 0.5 ? 0.0f : 1.0f;
return Color4f(val, val, val, 1.0f);
} else {
float H = 360 * hsv[0];
if ((H < 60) || (H > 300)) {
return Color4f(0.0f, 1.0f, 1.0f, 1.0f); // red -> cyan
} else if (H < 180) {
return Color4f(1.0f, 0.0f, 1.0f, 1.0f); // green -> magenta
} else {
return Color4f(1.0f, 1.0f, 0.0f, 1.0f); // blue -> yellow
}
}
}
void ColorMap::enumerateColorSchemesInPath(colorscheme_set_t &result_set, const fs::path basePath)
{
const fs::path color_schemes = basePath / "color-schemes" / "render";

View File

@ -79,6 +79,8 @@ public:
std::list<std::string> colorSchemeNames(bool guiOnly = false) const;
static Color4f getColor(const ColorScheme &cs, const RenderColor rc);
static Color4f getContrastColor(const Color4f &col);
static Color4f getColorHSV(const Color4f &col);
private:
ColorMap();

View File

@ -152,7 +152,7 @@ Value Context::evaluate_function(const std::string &name, const EvalContext *eva
return Value();
}
AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const
AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const
{
if (this->parent) return this->parent->instantiate_module(inst, evalctx);
PRINTB("WARNING: Ignoring unknown module '%s'.", inst.name());

View File

@ -15,7 +15,7 @@ public:
const Context *getParent() const { return this->parent; }
virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const;
virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const;
virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, EvalContext *evalctx) const;
void setVariables(const AssignmentList &args,
const class EvalContext *evalctx = NULL);

View File

@ -29,6 +29,7 @@
#include "node.h"
#include "evalcontext.h"
#include "modcontext.h"
#include "expression.h"
#include "builtin.h"
#include "printutils.h"
#include <sstream>
@ -55,7 +56,7 @@ public: // methods
: type(type)
{ }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
static void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l,
const Context *ctx, const EvalContext *evalctx);
@ -99,7 +100,14 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst
for_eval(node, inst, l+1, &c, evalctx);
}
} else if (l > 0) {
std::vector<AbstractNode *> instantiatednodes = inst.instantiateChildren(ctx);
// At this point, the for loop variables have been set and we can initialize
// the local scope (as they may depend on the for loop variables
Context c(ctx);
BOOST_FOREACH(const Assignment &ass, inst.scope.assignments) {
c.set_variable(ass.first, ass.second->evaluate(&c));
}
std::vector<AbstractNode *> instantiatednodes = inst.instantiateChildren(&c);
node.children.insert(node.children.end(), instantiatednodes.begin(), instantiatednodes.end());
}
}
@ -155,12 +163,12 @@ AbstractNode* ControlModule::getChild(const Value& value, const EvalContext* mod
return modulectx->getChild(n)->evaluate(modulectx);
}
AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
AbstractNode *node = NULL;
if (type == CHILD)
{
switch (this->type) {
case CHILD: {
printDeprecation("DEPRECATED: child() will be removed in future releases. Use children() instead.");
int n = 0;
if (evalctx->numArgs() > 0) {
@ -192,9 +200,9 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
}
return node;
}
break;
if (type == CHILDREN)
{
case CHILDREN: {
const EvalContext *modulectx = getLastModuleCtx(evalctx);
if (modulectx==NULL) {
return NULL;
@ -251,14 +259,10 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
}
return NULL;
}
break;
if (type == INT_FOR)
node = new AbstractIntersectionNode(inst);
else
case ECHO: {
node = new AbstractNode(inst);
if (type == ECHO)
{
std::stringstream msg;
msg << "ECHO: ";
for (size_t i = 0; i < inst->arguments.size(); i++) {
@ -273,36 +277,50 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
}
PRINTB("%s", msg.str());
}
break;
if (type == ASSIGN)
{
case ASSIGN: {
node = new AbstractNode(inst);
// We create a new context to avoid parameters from influencing each other
// -> parallel evaluation. This is to be backwards compatible.
Context c(evalctx);
for (size_t i = 0; i < evalctx->numArgs(); i++) {
if (!evalctx->getArgName(i).empty())
c.set_variable(evalctx->getArgName(i), evalctx->getArgValue(i));
}
// Let any local variables override the parameters
inst->scope.apply(c);
std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(&c);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
}
break;
if (type == FOR || type == INT_FOR)
{
case FOR:
node = new AbstractNode(inst);
for_eval(*node, *inst, 0, evalctx, evalctx);
}
break;
if (type == IF)
{
case INT_FOR:
node = new AbstractIntersectionNode(inst);
for_eval(*node, *inst, 0, evalctx, evalctx);
break;
case IF: {
node = new AbstractNode(inst);
const IfElseModuleInstantiation *ifelse = dynamic_cast<const IfElseModuleInstantiation*>(inst);
if (evalctx->numArgs() > 0 && evalctx->getArgValue(0).toBool()) {
inst->scope.apply(*evalctx);
std::vector<AbstractNode *> instantiatednodes = ifelse->instantiateChildren(evalctx);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
}
else {
ifelse->else_scope.apply(*evalctx);
std::vector<AbstractNode *> instantiatednodes = ifelse->instantiateElseChildren(evalctx);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
}
}
break;
}
return node;
}

View File

@ -38,11 +38,12 @@ class CsgModule : public AbstractModule
public:
OpenSCADOperator type;
CsgModule(OpenSCADOperator type) : type(type) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *CsgModule::instantiate(const Context*, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *CsgModule::instantiate(const Context*, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
inst->scope.apply(*evalctx);
CsgNode *node = new CsgNode(inst, type);
std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());

View File

@ -588,6 +588,9 @@ std::string DxfData::dump() const
return out.str();
}
/*
May return an empty polygon, but will not return NULL
*/
Polygon2d *DxfData::toPolygon2d() const
{
Polygon2d *poly = new Polygon2d();
@ -602,9 +605,5 @@ Polygon2d *DxfData::toPolygon2d() const
}
poly->addOutline(outline);
}
if (poly->outlines().size() == 0) {
delete poly;
poly = NULL;
}
return poly;
}

View File

@ -8,6 +8,12 @@
#include <boost/foreach.hpp>
EvalContext::EvalContext(const Context *parent,
const AssignmentList &args, const class LocalScope *const scope)
: Context(parent), eval_arguments(args), scope(scope)
{
}
const std::string &EvalContext::getArgName(size_t i) const
{
assert(i < this->eval_arguments.size());

View File

@ -12,8 +12,7 @@ public:
typedef std::vector<class ModuleInstantiation *> InstanceList;
EvalContext(const Context *parent,
const AssignmentList &args, const class LocalScope *const scope = NULL)
: Context(parent), eval_arguments(args), scope(scope) {}
const AssignmentList &args, const class LocalScope *const scope = NULL);
virtual ~EvalContext() {}
size_t numArgs() const { return this->eval_arguments.size(); }
@ -29,6 +28,5 @@ public:
private:
const AssignmentList &eval_arguments;
std::vector<std::pair<std::string, Value> > eval_values;
const LocalScope *const scope;
};

View File

@ -133,7 +133,7 @@ void export_stl(const PolySet &ps, std::ostream &output)
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
output << "solid OpenSCAD_Model\n";
BOOST_FOREACH(const PolySet::Polygon &p, triangulated.polygons) {
BOOST_FOREACH(const Polygon &p, triangulated.polygons) {
assert(p.size() == 3); // STL only allows triangles
std::stringstream stream;
stream << p[0][0] << " " << p[0][1] << " " << p[0][2];

View File

@ -375,7 +375,7 @@ std::string Expression::toString() const
stream << "for(" << c->call_arguments << ") ";
c = c->children[0];
} else if (c->call_funcname == "if") {
stream << "if(" << c->children[0] << ") ";
stream << "if(" << *c->children[0] << ") ";
c = c->children[1];
} else if (c->call_funcname == "let") {
stream << "let(" << c->call_arguments << ") ";

View File

@ -61,10 +61,10 @@ class ImportModule : public AbstractModule
public:
import_type_e type;
ImportModule(import_type_e type = TYPE_UNKNOWN) : type(type) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
AssignmentList args;
args += Assignment("file"), Assignment("layer"), Assignment("convexity"), Assignment("origin"), Assignment("scale");

View File

@ -17,6 +17,9 @@ using Eigen::Matrix4d;
#define Transform3d Eigen::Affine3d
#define Transform2d Eigen::Affine2d
typedef std::vector<Vector3d> Polygon;
typedef std::vector<Polygon> Polygons;
bool matrix_contains_infinity( const Transform3d &m );
bool matrix_contains_nan( const Transform3d &m );

View File

@ -46,10 +46,10 @@ class LinearExtrudeModule : public AbstractModule
{
public:
LinearExtrudeModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
LinearExtrudeNode *node = new LinearExtrudeNode(inst);
@ -58,6 +58,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();

View File

@ -43,32 +43,27 @@ std::string LocalScope::dump(const std::string &indent) const
}
// FIXME: Two parameters here is a hack. Rather have separate types of scopes, or check the type of the first parameter. Note const vs. non-const
std::vector<AbstractNode*> LocalScope::instantiateChildren(const Context *evalctx, FileContext *filectx) const
std::vector<AbstractNode*> LocalScope::instantiateChildren(const Context *evalctx) const
{
Context *c = filectx;
if (!c) {
c = new Context(evalctx);
// FIXME: If we make c a ModuleContext, child() doesn't work anymore
// c->functions_p = &this->functions;
// c->modules_p = &this->modules;
// Uncommenting the following would allow assignments in local scopes,
// but would cause duplicate evaluation of module scopes
// BOOST_FOREACH (const Assignment &ass, this->assignments) {
// c->set_variable(ass.first, ass.second->evaluate(c));
// }
}
std::vector<AbstractNode*> childnodes;
BOOST_FOREACH (ModuleInstantiation *modinst, this->children) {
AbstractNode *node = modinst->evaluate(c);
AbstractNode *node = modinst->evaluate(evalctx);
if (node) childnodes.push_back(node);
}
if (c != filectx) delete c;
return childnodes;
}
/*!
When instantiating a module which can take a scope as parameter (i.e. non-leaf nodes),
use this method to apply the local scope definitions to the evaluation context.
This will enable variables defined in local blocks.
NB! for loops are special as the local block may depend on variables evaluated by the
for loop parameters. The for loop code will handle this specially.
*/
void LocalScope::apply(Context &ctx) const
{
BOOST_FOREACH(const Assignment &ass, this->assignments) {
ctx.set_variable(ass.first, ass.second->evaluate(&ctx));
}
}

View File

@ -11,8 +11,9 @@ public:
size_t numElements() const { return assignments.size() + children.size(); }
std::string dump(const std::string &indent) const;
std::vector<class AbstractNode*> instantiateChildren(const class Context *evalctx, class FileContext *filectx = NULL) const;
std::vector<class AbstractNode*> instantiateChildren(const class Context *evalctx) const;
void addChild(ModuleInstantiation *ch);
void apply(Context &ctx) const;
AssignmentList assignments;
ModuleInstantiationList children;

View File

@ -71,6 +71,7 @@ void ModuleContext::initializeModule(const class Module &module)
BOOST_FOREACH(const Assignment &ass, module.scope.assignments) {
this->set_variable(ass.first, ass.second->evaluate(this));
}
// Experimental code. See issue #399
// evaluateAssignments(module.scope.assignments);
}
@ -115,7 +116,7 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co
}
std::string replacement = Builtins::instance()->isDeprecated(name);
if (!replacement.empty()) {
PRINT_DEPRECATION("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement);
PRINT_DEPRECATION("DEPRECATED: The %s() module will be removed in future releases. Use %s instead.", name % replacement);
}
return m;
}
@ -130,7 +131,7 @@ Value ModuleContext::evaluate_function(const std::string &name, const EvalContex
return Context::evaluate_function(name, evalctx);
}
AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const
AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const
{
const AbstractModule *foundm = this->findLocalModule(inst.name());
if (foundm) return foundm->instantiate(this, &inst, evalctx);
@ -207,7 +208,7 @@ Value FileContext::evaluate_function(const std::string &name, const EvalContext
return ModuleContext::evaluate_function(name, evalctx);
}
AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const
AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const
{
const AbstractModule *foundm = this->findLocalModule(inst.name());
if (foundm) return foundm->instantiate(this, &inst, evalctx);

View File

@ -21,7 +21,7 @@ public:
virtual Value evaluate_function(const std::string &name,
const EvalContext *evalctx) const;
virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst,
const EvalContext *evalctx) const;
EvalContext *evalctx) const;
const AbstractModule *findLocalModule(const std::string &name) const;
const AbstractFunction *findLocalFunction(const std::string &name) const;
@ -47,7 +47,7 @@ public:
virtual ~FileContext() {}
virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst,
const EvalContext *evalctx) const;
EvalContext *evalctx) const;
private:
const FileModule::ModuleContainer &usedlibs;

View File

@ -46,7 +46,7 @@ AbstractModule::~AbstractModule()
{
}
AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
(void)ctx; // avoid unusued parameter warning
@ -185,7 +185,7 @@ private:
const ModuleInstantiation &inst;
};
AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
ModRecursionGuard g(*inst);
if (g.recursion_detected()) {
@ -193,6 +193,10 @@ AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation
return NULL;
}
// At this point we know that nobody will modify the dependencies of the local scope
// passed to this instance, so we can populate the context
inst->scope.apply(*evalctx);
ModuleContext c(ctx, evalctx);
// set $children first since we might have variables depending on it
c.set_variable("$children", Value(double(inst->scope.children.size())));
@ -342,7 +346,7 @@ bool FileModule::handleDependencies()
return somethingchanged;
}
AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
assert(evalctx == NULL);
FileContext c(*this, ctx);
@ -353,7 +357,7 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat
#endif
AbstractNode *node = new AbstractNode(inst);
std::vector<AbstractNode *> instantiatednodes = this->scope.instantiateChildren(ctx, &c);
std::vector<AbstractNode *> instantiatednodes = this->scope.instantiateChildren(&c);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
return node;

View File

@ -68,7 +68,7 @@ public:
virtual ~AbstractModule();
virtual bool is_experimental() const { return feature != NULL; }
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }
virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const;
virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, class EvalContext *evalctx = NULL) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
virtual double lookup_double_variable_with_default(Context &c, std::string variable, double def) const;
virtual std::string lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const;
@ -81,7 +81,7 @@ public:
Module(const Feature& feature) : AbstractModule(feature) { }
virtual ~Module();
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
static const std::string& stack_element(int n) { return module_stack[n]; };
static int stack_size() { return module_stack.size(); };
@ -108,7 +108,7 @@ public:
void registerInclude(const std::string &localpath, const std::string &fullpath);
bool includesChanged() const;
bool handleDependencies();
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL) const;
bool hasIncludes() const { return !this->includes.empty(); }
bool usesLibraries() const { return !this->usedlibs.empty(); }
bool isHandlingDependencies() const { return this->is_handling_dependencies; }

View File

@ -46,10 +46,10 @@ class OffsetModule : public AbstractModule
{
public:
OffsetModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
OffsetNode *node = new OffsetNode(inst);
@ -58,6 +58,7 @@ AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstanti
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();

View File

@ -270,6 +270,7 @@ if_statement:
child_statements:
/* empty */
| child_statements child_statement
| child_statements assignment
;
child_statement:
@ -281,13 +282,6 @@ child_statement:
}
;
/*
FIXME: This allows for variable declaration in child blocks, not activated yet
|
assignment ;
*/
// "for" is a valid module identifier
module_id:
TOK_ID { $$ = $1; }

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Version : 6.2.1 *
* Date : 31 October 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
@ -34,7 +34,7 @@
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.1.3"
#define CLIPPER_VERSION "6.2.0"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
@ -46,9 +46,8 @@
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <vector>
#include <set>
@ -57,6 +56,7 @@
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
namespace ClipperLib {
@ -69,11 +69,16 @@ enum PolyType { ptSubject, ptClip };
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
typedef unsigned int cUInt;
typedef int cInt;
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
#else
typedef signed long long cInt;
typedef unsigned long long cUInt;
typedef signed long long cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class
typedef unsigned long long ulong64;
#endif
struct IntPoint {
@ -117,15 +122,12 @@ struct DoublePoint
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
@ -134,6 +136,7 @@ class PolyNode
{
public:
PolyNode();
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
@ -168,11 +171,6 @@ bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
@ -183,8 +181,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
@ -202,7 +199,7 @@ enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinima;
struct LocalMinimum;
struct Scanbeam;
struct OutPt;
struct OutRec;
@ -213,7 +210,6 @@ typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
@ -236,12 +232,14 @@ protected:
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
@ -268,7 +266,7 @@ public:
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(TZFillCallback zFillFunc);
void ZFillFunction(ZFillCallback zFillFunc);
#endif
protected:
void Reset();
@ -279,7 +277,8 @@ private:
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
@ -289,7 +288,7 @@ private:
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
TZFillCallback m_ZFill; //custom callback
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
@ -308,21 +307,19 @@ private:
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
@ -344,7 +341,7 @@ private:
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e);
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
};
//------------------------------------------------------------------------------

View File

@ -2,25 +2,7 @@
#include "polyset.h"
#include "Polygon2d.h"
#include "printutils.h"
#include "cgal.h"
#ifdef NDEBUG
#define PREV_NDEBUG NDEBUG
#undef NDEBUG
#endif
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_2_filtered_projection_traits_3.h>
#ifdef PREV_NDEBUG
#define NDEBUG PREV_NDEBUG
#endif
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_2_filtered_projection_traits_3<K> Projection;
typedef CGAL::Triangulation_data_structure_2 <
CGAL::Triangulation_vertex_base_2<Projection>,
CGAL::Constrained_triangulation_face_base_2<Projection> > Tds;
typedef CGAL::Constrained_Delaunay_triangulation_2<Projection, Tds, CGAL::Exact_predicates_tag> CDT;
#include "cgalutils.h"
#include <boost/foreach.hpp>
@ -32,7 +14,7 @@ namespace PolysetUtils {
Polygon2d *project(const PolySet &ps) {
Polygon2d *poly = new Polygon2d;
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
BOOST_FOREACH(const Polygon &p, ps.polygons) {
Outline2d outline;
BOOST_FOREACH(const Vector3d &v, p) {
outline.vertices.push_back(Vector2d(v[0], v[1]));
@ -60,56 +42,33 @@ namespace PolysetUtils {
faces. As of writing, our only tessellation method is triangulation
using CGAL's Constrained Delaunay algorithm. This code assumes the input
polyset has simple polygon faces with no holes.
The tessellation will be robust wrt. degenerate and self-intersecting
The tessellation will be robust wrt. degenerate and self-intersecting
*/
void tessellate_faces(const PolySet &inps, PolySet &outps) {
int degeneratePolygons = 0;
for (size_t i = 0; i < inps.polygons.size(); i++) {
const PolySet::Polygon pgon = inps.polygons[i];
const Polygon pgon = inps.polygons[i];
if (pgon.size() < 3) {
degeneratePolygons++;
continue;
}
std::vector<PolySet::Polygon> triangles;
std::vector<Polygon> triangles;
if (pgon.size() == 3) {
triangles.push_back(pgon);
}
else {
// Build a data structure that CGAL accepts
std::vector<K::Point_3> cgalpoints;
PolygonK cgalpoints;
BOOST_FOREACH(const Vector3d &v, pgon) {
cgalpoints.push_back(K::Point_3(v[0], v[1], v[2]));
cgalpoints.push_back(Vertex3K(v[0], v[1], v[2]));
}
// Calculate best guess at face normal using Newell's method
K::Vector_3 normal;
CGAL::normal_vector_newell_3(cgalpoints.begin(), cgalpoints.end(), normal);
// Pass the normal vector to the (undocumented)
// CGAL::Triangulation_2_filtered_projection_traits_3. This
// trait deals with projection from 3D to 2D using the normal
// vector as a hint, and allows for near-planar polygons to be passed to
// the Constrained Delaunay Triangulator.
Projection actualProjection(normal);
CDT cdt(actualProjection);
for (size_t i=0;i<cgalpoints.size(); i++) {
cdt.insert_constraint(cgalpoints[i], cgalpoints[(i+1)%cgalpoints.size()]);
}
// Iterate over the resulting faces
CDT::Finite_faces_iterator fit;
for (fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++) {
PolySet::Polygon pgon;
for (int i=0;i<3;i++) {
K::Point_3 v = cdt.triangle(fit)[i];
pgon.push_back(Vector3d(v.x(), v.y(), v.z()));
}
triangles.push_back(pgon);
}
bool err = CGALUtils::tessellatePolygon(cgalpoints, triangles);
}
// ..and pass to the output polyhedron
for (size_t j=0;j<triangles.size();j++) {
PolySet::Polygon t = triangles[j];
Polygon t = triangles[j];
outps.append_poly();
outps.append_vertex(t[0].x(),t[0].y(),t[0].z());
outps.append_vertex(t[1].x(),t[1].y(),t[1].z());

View File

@ -309,7 +309,7 @@ void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m,
else {
// If we don't have borders, use the polygons as borders.
// FIXME: When is this used?
const std::vector<Polygon> *borders_p = &polygons;
const Polygons *borders_p = &polygons;
for (size_t i = 0; i < borders_p->size(); i++) {
const Polygon *poly = &borders_p->at(i);
for (size_t j = 1; j <= poly->size(); j++) {

View File

@ -14,8 +14,7 @@ BOOST_TRIBOOL_THIRD_STATE(unknown)
class PolySet : public Geometry
{
public:
typedef std::vector<Vector3d> Polygon;
std::vector<Polygon> polygons;
Polygons polygons;
PolySet(unsigned int dim, boost::tribool convex = unknown);
PolySet(const Polygon2d &origin);

View File

@ -61,7 +61,7 @@ class PrimitiveModule : public AbstractModule
public:
primitive_type_e type;
PrimitiveModule(primitive_type_e type) : type(type) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
private:
Value lookup_radius(const Context &ctx, const std::string &radius_var, const std::string &diameter_var) const;
};
@ -141,7 +141,7 @@ Value PrimitiveModule::lookup_radius(const Context &ctx, const std::string &diam
}
}
AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
PrimitiveNode *node = new PrimitiveNode(inst, this->type);
@ -516,8 +516,7 @@ Geometry *PrimitiveNode::createGeometry() const
if (!this->points.toVector()[pt].getVec3(px, py, pz) ||
isinf(px) || isinf(py) || isinf(pz)) {
PRINTB("ERROR: Unable to convert point at index %d to a vec3 of numbers", j);
delete p;
return NULL;
return p;
}
p->insert_vertex(px, py, pz);
}
@ -544,8 +543,8 @@ Geometry *PrimitiveNode::createGeometry() const
o.vertices[2] = v2;
o.vertices[3] = Vector2d(v1[0], v2[1]);
p->addOutline(o);
p->setSanitized(true);
}
p->setSanitized(true);
}
break;
case CIRCLE: {
@ -561,8 +560,8 @@ Geometry *PrimitiveNode::createGeometry() const
o.vertices[i] = Vector2d(this->r1*cos(phi), this->r1*sin(phi));
}
p->addOutline(o);
p->setSanitized(true);
}
p->setSanitized(true);
}
break;
case POLYGON: {
@ -574,12 +573,10 @@ Geometry *PrimitiveNode::createGeometry() const
const Value::VectorType &vec = this->points.toVector();
for (unsigned int i=0;i<vec.size();i++) {
const Value &val = vec[i];
if (!val.getVec2(x, y) ||
isinf(x) || isinf(y)) {
if (!val.getVec2(x, y) || isinf(x) || isinf(y)) {
PRINTB("ERROR: Unable to convert point %s at index %d to a vec2 of numbers",
val.toString() % i);
delete p;
return NULL;
return p;
}
outline.vertices.push_back(Vector2d(x, y));
}
@ -601,11 +598,7 @@ Geometry *PrimitiveNode::createGeometry() const
}
}
if (p->outlines().size() == 0) {
delete p;
g = NULL;
}
else {
if (p->outlines().size() > 0) {
p->setConvexity(convexity);
}
}

View File

@ -41,10 +41,10 @@ class ProjectionModule : public AbstractModule
{
public:
ProjectionModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
ProjectionNode *node = new ProjectionNode(inst);
@ -53,6 +53,7 @@ AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInst
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
Value convexity = c.lookup_variable("convexity", true);
Value cut = c.lookup_variable("cut", true);

View File

@ -38,10 +38,10 @@ class RenderModule : public AbstractModule
{
public:
RenderModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
RenderNode *node = new RenderNode(inst);
@ -50,6 +50,7 @@ AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstanti
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
Value v = c.lookup_variable("convexity");
if (v.type() == Value::NUMBER)

View File

@ -44,10 +44,10 @@ class RotateExtrudeModule : public AbstractModule
{
public:
RotateExtrudeModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
RotateExtrudeNode *node = new RotateExtrudeNode(inst);
@ -56,6 +56,7 @@ AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleI
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();

View File

@ -52,7 +52,7 @@ class SurfaceModule : public AbstractModule
{
public:
SurfaceModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
typedef boost::unordered_map<std::pair<int,int>,double> img_data_t;
@ -80,7 +80,7 @@ private:
img_data_t read_png_or_dat(std::string filename) const;
};
AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
SurfaceNode *node = new SurfaceNode(inst);
node->center = false;

View File

@ -41,10 +41,10 @@ class TextModule : public AbstractModule
{
public:
TextModule() : AbstractModule(Feature::ExperimentalTextModule) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
TextNode *node = new TextNode(inst);

View File

@ -50,10 +50,10 @@ class TransformModule : public AbstractModule
public:
transform_type_e type;
TransformModule(transform_type_e type) : type(type) { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const;
};
AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const
{
TransformNode *node = new TransformNode(inst);
@ -83,6 +83,7 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta
Context c(ctx);
c.setVariables(args, evalctx);
inst->scope.apply(*evalctx);
if (this->type == SCALE)
{

View File

@ -0,0 +1,6 @@
translate() square(0);
translate() {
square(0);
circle(0);
}

View File

@ -41,3 +41,7 @@ offset(delta = 5) shape1(1, -1);
// Bug with fragment calculateion with delta < 1 due to abs() instead of std::abs()
translate([-50,-50]) scale([25,25,1])
offset(delta = 0.9, join_type="round") square(.1);
// Malformed offsets
offset();
offset() square(0);

View File

@ -26,4 +26,10 @@ translate([0,0,0])
polygon(points = [[0,1], [0,0], [1,0], [1,1], [0.8,0.8], [0.8,0.2], [0.2,0.2], [0.2,0.8]],
paths = [[7,6,5,4,3,2,1,0],[7,0,3,4] ]);
// More malformed polygons
polyhedron(points = undef, paths = [[1, 2, 3]]);
polyhedron(points = [[0,0,0],[1,1,1]], paths = undef);
polyhedron(points=[0], paths = [[0]]);
// FIXME: convexity

View File

@ -1,3 +1,9 @@
// Test a mix of toplevel 2D and 3D objects
cube();
translate([2,0,0]) square();
// Test mixing of empty 2D and 3D objects
union() {
cube(0);
circle(0);
}

View File

@ -46,3 +46,6 @@ translate([0,2,0]) difference() {
// dont crash (issue #703)
polyhedron(points = undef, triangles = [[1, 2, 3]]);
polyhedron(points = [[0,0,0],[1,1,1]], triangles = undef);
// More malformed polyhedrons
polyhedron();
polyhedron(points=[0], faces = [[0]]);

View File

@ -1,2 +1,4 @@
surface();
surface("notfound.dat");
surface("notfound.png");
surface("surface.dat", center=true);

View File

@ -0,0 +1,2 @@
text();
text("");

View File

@ -28,3 +28,5 @@ y = !i;
z = (j);
aa = k ? l : m;
bb = n[o];
cc = let(a=1) a;
dd = [for (a=[0,1]) let(b=a) if (true) b];

View File

@ -16,13 +16,18 @@ o = floor();
p = pow();
q = sqrt();
r = exp();
ra = len();
s = log();
t = ln();
u = str();
ua = chr();
ub = concat();
v = lookup();
w = dxf_dim();
x = dxf_cross();
va = search();
y = version();
z = version_num();
aa = len();
bb = search();
za = norm();
zb = cross();
zc = parent_module();
w = dxf_dim();
x = dxf_cross();

View File

@ -37,3 +37,5 @@ mirror();
translate();
multmatrix();
color();
offset();
text();

View File

@ -0,0 +1,85 @@
echo("union scope");
a = 4;
union() {
a = 5;
echo("local a (5):", a);
}
echo("global a (4):", a);
echo("module scope:");
module mymodule(b=6) {
b = 7;
echo("local b (7)", b);
}
mymodule();
mymodule(8);
echo("module children scope:");
module mymodule2(b2=6) {
b2 = 2;
children(0);
}
mymodule2(b2=7) {
b2 = 3;
echo("b2 (3)", b2);
}
echo("for loop (c = 0,1,25):");
for (i=[0:2]) {
c = (i > 1) ? i + 23 : i;
echo("c", c);
}
echo("if scope:");
if (true) {
d = 8;
echo("d (8)", d);
}
echo("else scope:");
if (false) {
} else {
d = 9;
echo("d (9)", d);
}
echo("anonymous inner scope (scope ignored):");
union() {
e = 2;
echo("outer e (3)", e);
{
e = 3;
echo("inner e (3)", e);
}
}
echo("anonymous scope (scope ignored):");
f=1;
echo("outer f (2)", f);
{
f=2;
echo("inner f (2)", f);
}
echo("anonymous scope reassign:");
{
g=1;
echo("g (2)", g);
g=2;
}
echo("anonymous reassign using outer (scope ignored)", h);
h=5;
{
h=h*2; // Not allowed
echo("h (undef)", h);
}
echo("override variable in assign scope:");
assign(i=9) {
i=10;
echo("i (10)", i);
}

View File

@ -2,16 +2,13 @@
//Helper function that pretty prints our search test
//Expected result is checked against execution of a search() invocation and OK/FAIL is indicated
module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef)
{
if(undef != search_up_to_num_matches)
{
assign( test_res = search(search_to_find, search_to_search, search_up_to_num_matches) )
module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef) {
if (undef != search_up_to_num_matches) {
test_res = search(search_to_find, search_to_search, search_up_to_num_matches);
echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ", ", search_up_to_num_matches, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
}
else
{
assign( test_res = search(search_to_find, search_to_search) )
else {
test_res = search(search_to_find, search_to_search);
echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
}
}

View File

@ -1,4 +1,5 @@
import();
import("notfound.dxf");
translate([-210,0,0]) import(file="../../../dxf/polygons.dxf");
translate([-210,0,0]) import(file="../../../dxf/polygons.dxf", origin=[0,110]);
translate([-210,0,0]) import(file="../../../dxf/polygons.dxf", origin=[110,110], scale=0.5);

View File

@ -674,6 +674,7 @@ set(CGAL_SOURCES
../src/CGAL_Nef_polyhedron.cc
../src/cgalutils.cc
../src/cgalutils-tess.cc
../src/cgalutils-tess-old.cc
../src/CGALCache.cc
../src/CGAL_Nef_polyhedron_DxfData.cc
../src/Polygon2d-CGAL.cc
@ -1046,6 +1047,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/scope-assignment-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/expression-shortcircuit-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/parent_module-tests.scad
@ -1118,8 +1120,10 @@ list(APPEND EXPORT3D_TEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/3D/features
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/rotate_extrude-hole.scad)
disable_tests(
# This doesn't output anything
# These don't output anything
dxfpngtest_text-empty-tests
dxfpngtest_nothing-decimal-comma-separated
dxfpngtest_nullspace-2d
# Not useful
throwntogethertest_internal-cavity
@ -1346,6 +1350,8 @@ set_test_config(Bugs offpngtest_polyhedron-tests
offpngtest_bad-stl-pcbvicebar
offpngtest_bad-stl-tardis
offpngtest_bad-stl-wing)
# No issue - this was introduced when fixing #1033
set_test_config(Bugs stlpngtest_bad-stl-wing)
add_cmdline_test(monotonepngtest EXE ${OPENSCAD_BINPATH} ARGS --colorscheme=Monotone --enable=text --render -o SUFFIX png FILES ${EXPORT3D_TEST_FILES})
add_cmdline_test(stlpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=STL --render=cgal EXPECTEDDIR monotonepngtest SUFFIX png FILES ${EXPORT3D_TEST_FILES})

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1 +1 @@
group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40)
group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40+offset)

View File

@ -3,4 +3,8 @@ group() {
multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
square(size = [1, 1], center = false);
}
union() {
cube(size = [0, 0, 0], center = false);
circle($fn = 0, $fa = 12, $fs = 2, r = 0);
}
}

View File

@ -39,4 +39,5 @@ group() {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
color([-1, -1, -1, 1]);
offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2);
}

View File

@ -1,5 +1,6 @@
group() {
import(file = "", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
import(file = "notfound.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
multmatrix([[1, 0, 0, -210], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "../../../dxf/polygons.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
}

View File

@ -0,0 +1,9 @@
group() {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
square(size = [0, 0], center = false);
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
square(size = [0, 0], center = false);
circle($fn = 0, $fa = 12, $fs = 2, r = 0);
}
}

View File

@ -137,4 +137,8 @@ group() {
}
}
}
offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2);
offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) {
square(size = [0, 0], center = false);
}
}

View File

@ -39,4 +39,7 @@ group() {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polygon(points = [[0, 1], [0, 0], [1, 0], [1, 1], [0.8, 0.8], [0.8, 0.2], [0.2, 0.2], [0.2, 0.8]], paths = [[7, 6, 5, 4, 3, 2, 1, 0], [7, 0, 3, 4]], convexity = 1);
}
polyhedron(points = undef, faces = undef, convexity = 1);
polyhedron(points = [[0, 0, 0], [1, 1, 1]], faces = undef, convexity = 1);
polyhedron(points = [0], faces = undef, convexity = 1);
}

View File

@ -32,4 +32,6 @@ group() {
}
polyhedron(points = undef, faces = [[1, 2, 3]], convexity = 1);
polyhedron(points = [[0, 0, 0], [1, 1, 1]], faces = undef, convexity = 1);
polyhedron(points = undef, faces = undef, convexity = 1);
polyhedron(points = [0], faces = [[0]], convexity = 1);
}

View File

@ -1,4 +1,6 @@
group() {
surface(file = "", center = false, invert = false);
surface(file = "notfound.dat", center = false, invert = false);
surface(file = "notfound.png", center = false, invert = false);
surface(file = "surface.dat", center = true, invert = false);
}

View File

@ -0,0 +1,4 @@
group() {
text(text = "", size = 10, spacing = 1, font = "", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 0, $fa = 12, $fs = 2);
text(text = "", size = 10, spacing = 1, font = "", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 0, $fa = 12, $fs = 2);
}

View File

@ -0,0 +1,30 @@
WARNING: Ignoring unknown variable 'h'.
ECHO: "union scope"
ECHO: "local a (5):", 5
ECHO: "global a (4):", 4
ECHO: "module scope:"
ECHO: "local b (7)", 7
ECHO: "local b (7)", 7
ECHO: "module children scope:"
ECHO: "b2 (3)", 3
ECHO: "for loop (c = 0,1,25):"
ECHO: "c", 0
ECHO: "c", 1
ECHO: "c", 25
ECHO: "if scope:"
ECHO: "d (8)", 8
ECHO: "else scope:"
ECHO: "d (9)", 9
ECHO: "anonymous inner scope (scope ignored):"
ECHO: "outer e (3)", 3
ECHO: "inner e (3)", 3
ECHO: "anonymous scope (scope ignored):"
ECHO: "outer f (2)", 2
ECHO: "inner f (2)", 2
ECHO: "anonymous scope reassign:"
ECHO: "g (2)", 2
ECHO: "anonymous reassign using outer (scope ignored)", undef
ECHO: "h (undef)", undef
ECHO: "override variable in assign scope:"
DEPRECATED: The assign() module will be removed in future releases. Use a regular assignment instead.
ECHO: "i (10)", 10

View File

@ -18,6 +18,7 @@ ECHO: "user-defined special variables as parameter"
ECHO: 7
ECHO: 7
ECHO: "assign only visible in children's scope"
DEPRECATED: The assign() module will be removed in future releases. Use a regular assignment instead.
WARNING: Ignoring unknown variable 'c'.
ECHO: undef
ECHO: 5

View File

@ -28,4 +28,6 @@ y = !i;
z = j;
aa = (k ? l : m);
bb = n[o];
cc = let(a = 1) a;
dd = [for(a = [0, 1]) let(b = a) if(true) b];

View File

@ -16,14 +16,19 @@ o = floor();
p = pow();
q = sqrt();
r = exp();
ra = len();
s = log();
t = ln();
u = str();
ua = chr();
ub = concat();
v = lookup();
w = dxf_dim();
x = dxf_cross();
va = search();
y = version();
z = version_num();
aa = len();
bb = search();
za = norm();
zb = cross();
zc = parent_module();
w = dxf_dim();
x = dxf_cross();

View File

@ -38,4 +38,6 @@ mirror();
translate();
multmatrix();
color();
offset();
text();

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB