diff --git a/doc/OpenSCAD-polygons.graffle b/doc/OpenSCAD-polygons.graffle index 63985dcb..cda8bc76 100644 Binary files a/doc/OpenSCAD-polygons.graffle and b/doc/OpenSCAD-polygons.graffle differ diff --git a/openscad.pro b/openscad.pro index 194e47a6..50d7aca6 100644 --- a/openscad.pro +++ b/openscad.pro @@ -221,8 +221,9 @@ HEADERS += src/typedefs.h \ src/parsersettings.h \ src/renderer.h \ src/rendersettings.h \ + src/colormap.h \ src/ThrownTogetherRenderer.h \ - src/CGAL_renderer.h \ + src/CGAL_OGL_Polyhedron.h \ src/OGL_helper.h \ src/QGLView.h \ src/GLView.h \ @@ -379,6 +380,7 @@ src/FontCache.cc \ src/export_png.cc \ src/import.cc \ src/renderer.cc \ + src/colormap.cc \ src/ThrownTogetherRenderer.cc \ src/CSGTermEvaluator.cc \ src/svg.cc \ @@ -428,6 +430,7 @@ HEADERS += src/cgal.h \ src/Polygon2d-CGAL.h SOURCES += src/cgalutils.cc \ + src/cgalutils-tess.cc \ src/CGALCache.cc \ src/CGALRenderer.cc \ src/CGAL_Nef_polyhedron.cc \ diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index c8ef5225..fad0026b 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -96,10 +96,10 @@ fontconfig_sysver() { fcpath=$1/include/fontconfig/fontconfig.h if [ ! -e $fcpath ]; then return; fi - fcmajor=`grep "define *FC_MAJOR *[0-9.]*" $fcpath | awk '{print $3}'` - fcminor=`grep "define *FC_MINOR *[0-9.]*" $fcpath | awk '{print $3}'` - fcrevison=`grep "define *FC_REVISION *[0-9.]*" $fcpath | awk '{print $3}'` - fontconfig_sysver="${fcmajor}.${fcminor}.${fcrevision}" + fcmajor=`grep "define *FC_MAJOR.*[0-9.]*" $fcpath | awk '{print $3}'` + fcminor=`grep "define *FC_MINOR.*[0-9.]*" $fcpath | awk '{print $3}'` + fcrevison=`grep "define *FC_REVISION.*[0-9.]*" $fcpath | awk '{print $3}'` + fontconfig_sysver_result="${fcmajor}.${fcminor}.${fcrevision}" } freetype2_sysver() diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc index c59df6bc..c9b816c5 100644 --- a/src/CGALRenderer.cc +++ b/src/CGALRenderer.cc @@ -32,9 +32,10 @@ // dxfdata.h must come first for Eigen SIMD alignment issues #include "dxfdata.h" #include "polyset.h" +#include "printutils.h" #include "CGALRenderer.h" -#include "CGAL_renderer.h" +#include "CGAL_OGL_Polyhedron.h" #include "CGAL_Nef_polyhedron.h" #include "cgal.h" @@ -51,19 +52,7 @@ CGALRenderer::CGALRenderer(shared_ptr geom) else if (shared_ptr new_N = dynamic_pointer_cast(geom)) { assert(new_N->getDimension() == 3); if (!new_N->isEmpty()) { - this->polyhedron.reset(new Polyhedron()); - // FIXME: Make independent of Preferences - // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR, - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(), - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(), - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue()); - // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR, - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(), - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(), - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue()); - - CGAL::OGL::Nef3_Converter::convert_to_OGLPolyhedron(*new_N->p3, this->polyhedron.get()); - this->polyhedron->init(); + this->N = new_N; } } } @@ -72,9 +61,37 @@ CGALRenderer::~CGALRenderer() { } +shared_ptr CGALRenderer::getPolyhedron() const +{ + if (this->N && !this->polyhedron) buildPolyhedron(); + return this->polyhedron; +} + +void CGALRenderer::buildPolyhedron() const +{ + PRINTD("buildPolyhedron"); + this->polyhedron.reset(new CGAL_OGL_Polyhedron(*this->colorscheme)); + CGAL::OGL::Nef3_Converter::convert_to_OGLPolyhedron(*this->N->p3, this->polyhedron.get()); + // CGAL_NEF3_MARKED_FACET_COLOR <- CGAL_FACE_BACK_COLOR + // CGAL_NEF3_UNMARKED_FACET_COLOR <- CGAL_FACE_FRONT_COLOR + this->polyhedron->init(); + PRINTD("buildPolyhedron() end"); +} + +// Overridden from Renderer +void CGALRenderer::setColorScheme(const ColorScheme &cs) +{ + PRINTD("setColorScheme"); + Renderer::setColorScheme(cs); + this->polyhedron.reset(); // Mark as dirty + PRINTD("setColorScheme done"); +} + void CGALRenderer::draw(bool showfaces, bool showedges) const { + PRINTD("draw()"); if (this->polyset) { + PRINTD("draw() polyset"); if (this->polyset->getDimension() == 2) { // Draw 2D polygons glDisable(GL_LIGHTING); @@ -106,27 +123,33 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const this->polyset->render_surface(CSGMODE_NORMAL, Transform3d::Identity(), NULL); } } - else if (this->polyhedron) { - if (showfaces) this->polyhedron->set_style(SNC_BOUNDARY); - else this->polyhedron->set_style(SNC_SKELETON); - - this->polyhedron->draw(showfaces && showedges); - } + else { + shared_ptr polyhedron = getPolyhedron(); + if (polyhedron) { + PRINTD("draw() polyhedron"); + if (showfaces) polyhedron->set_style(SNC_BOUNDARY); + else polyhedron->set_style(SNC_SKELETON); + polyhedron->draw(showfaces && showedges); + } + } + PRINTD("draw() end"); } BoundingBox CGALRenderer::getBoundingBox() const { BoundingBox bbox; - if (this->polyhedron) { - CGAL::Bbox_3 cgalbbox = this->polyhedron->bbox(); - bbox = BoundingBox( - Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), - Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax()) ); - } - else if (this->polyset) { + if (this->polyset) { bbox = this->polyset->getBoundingBox(); } - + else { + shared_ptr polyhedron = getPolyhedron(); + if (polyhedron) { + CGAL::Bbox_3 cgalbbox = polyhedron->bbox(); + bbox = BoundingBox( + Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), + Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax())); + } + } return bbox; } diff --git a/src/CGALRenderer.h b/src/CGALRenderer.h index 5a5b3ac6..d3fae77a 100644 --- a/src/CGALRenderer.h +++ b/src/CGALRenderer.h @@ -1,16 +1,22 @@ #pragma once #include "renderer.h" +#include "CGAL_Nef_polyhedron.h" class CGALRenderer : public Renderer { public: CGALRenderer(shared_ptr geom); - virtual ~CGALRenderer(); + ~CGALRenderer(); virtual void draw(bool showfaces, bool showedges) const; + virtual void setColorScheme(const ColorScheme &cs); virtual BoundingBox getBoundingBox() const; -public: - shared_ptr polyhedron; +private: + shared_ptr getPolyhedron() const; + void buildPolyhedron() const; + + mutable shared_ptr polyhedron; + shared_ptr N; shared_ptr polyset; }; diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc index a974f5fd..dfe16536 100644 --- a/src/CGAL_Nef_polyhedron.cc +++ b/src/CGAL_Nef_polyhedron.cc @@ -53,6 +53,8 @@ size_t CGAL_Nef_polyhedron::memsize() const Note: Can return NULL if an error occurred */ +// FIXME: Deprecated by CGALUtils::createPolySetFromNefPolyhedron3 +#if 0 PolySet *CGAL_Nef_polyhedron::convertToPolyset() const { if (this->isEmpty()) return new PolySet(3); @@ -74,7 +76,7 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset() const err = true; errmsg = std::string(e.what()); } - if (!err) err = createPolySetFromPolyhedron(P, *ps); + if (!err) err = CGALUtils::createPolySetFromPolyhedron(P, *ps); if (err) { PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed."); if (errmsg!="") PRINTB("ERROR: %s",errmsg); @@ -83,6 +85,7 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset() const CGAL::set_error_behaviour(old_behaviour); return ps; } +#endif void CGAL_Nef_polyhedron::resize(Vector3d newsize, const Eigen::Matrix &autosize) diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h index c1346710..f3585469 100644 --- a/src/CGAL_Nef_polyhedron.h +++ b/src/CGAL_Nef_polyhedron.h @@ -27,7 +27,8 @@ public: CGAL_Nef_polyhedron &operator*=(const CGAL_Nef_polyhedron &other); CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other); CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other); - class PolySet *convertToPolyset() const; +// FIXME: Deprecated by CGALUtils::createPolySetFromNefPolyhedron3 +// class PolySet *convertToPolyset() const; void transform( const Transform3d &matrix ); void resize(Vector3d newsize, const Eigen::Matrix &autosize); diff --git a/src/CGAL_renderer.h b/src/CGAL_OGL_Polyhedron.h similarity index 62% rename from src/CGAL_renderer.h rename to src/CGAL_OGL_Polyhedron.h index c66337e3..ef056258 100644 --- a/src/CGAL_renderer.h +++ b/src/CGAL_OGL_Polyhedron.h @@ -27,19 +27,16 @@ #pragma once #ifndef NULLGL -#include "OGL_helper.h" -#undef CGAL_NEF3_MARKED_VERTEX_COLOR -#undef CGAL_NEF3_MARKED_EDGE_COLOR -#undef CGAL_NEF3_MARKED_FACET_COLOR -#undef CGAL_NEF3_UNMARKED_VERTEX_COLOR -#undef CGAL_NEF3_UNMARKED_EDGE_COLOR -#undef CGAL_NEF3_UNMARKED_FACET_COLOR +#include "colormap.h" +#include "rendersettings.h" +#include "OGL_helper.h" +#include "printutils.h" using CGAL::OGL::SNC_BOUNDARY; using CGAL::OGL::SNC_SKELETON; -class Polyhedron : public CGAL::OGL::Polyhedron +class CGAL_OGL_Polyhedron : public CGAL::OGL::Polyhedron { public: @@ -53,16 +50,18 @@ public: NUM_COLORS }; - Polyhedron() { + CGAL_OGL_Polyhedron(const ColorScheme &cs) { + PRINTD("CGAL_OGL_Polyhedron()"); + // Set default colors. setColor(CGAL_NEF3_MARKED_VERTEX_COLOR,0xb7,0xe8,0x5c); - setColor(CGAL_NEF3_MARKED_EDGE_COLOR,0xab,0xd8,0x56); - setColor(CGAL_NEF3_MARKED_FACET_COLOR,0x9d,0xcb,0x51); setColor(CGAL_NEF3_UNMARKED_VERTEX_COLOR,0xff,0xf6,0x7c); - setColor(CGAL_NEF3_UNMARKED_EDGE_COLOR,0xff,0xec,0x5e); - setColor(CGAL_NEF3_UNMARKED_FACET_COLOR,0xf9,0xd7,0x2c); + // Face and Edge colors are taken from default colorscheme + setColorScheme(cs); + PRINTD("CGAL_OGL_Polyhedron() end"); } void draw(bool showedges) const { + PRINTD("draw()"); if(this->style == SNC_BOUNDARY) { glCallList(this->object_list_+2); if(showedges) { @@ -75,31 +74,55 @@ public: glCallList(this->object_list_+1); glCallList(this->object_list_); } + PRINTD("draw() end"); } + + // overrides function in OGL_helper.h CGAL::Color getVertexColor(Vertex_iterator v) const { + PRINTD("getVertexColor"); CGAL::Color c = v->mark() ? colors[CGAL_NEF3_UNMARKED_VERTEX_COLOR] : colors[CGAL_NEF3_MARKED_VERTEX_COLOR]; return c; } + // overrides function in OGL_helper.h CGAL::Color getEdgeColor(Edge_iterator e) const { + PRINTD("getEdgeColor"); CGAL::Color c = e->mark() ? colors[CGAL_NEF3_UNMARKED_EDGE_COLOR] : colors[CGAL_NEF3_MARKED_EDGE_COLOR]; return c; } - CGAL::Color getFacetColor(Halffacet_iterator f) const { + // overrides function in OGL_helper.h + CGAL::Color getFacetColor(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("getFacetColor"); CGAL::Color c = f->mark() ? colors[CGAL_NEF3_UNMARKED_FACET_COLOR] : colors[CGAL_NEF3_MARKED_FACET_COLOR]; return c; } - void setColor(Polyhedron::RenderColor color_index, + void setColor(CGAL_OGL_Polyhedron::RenderColor color_index, const Color4f &c) { + PRINTDB("setColor %i %f %f %f",color_index%c[0]%c[1]%c[2]); + this->colors[color_index] = CGAL::Color(c[0]*255,c[1]*255,c[2]*255); + } + + void setColor(CGAL_OGL_Polyhedron::RenderColor color_index, unsigned char r, unsigned char g, unsigned char b) { - assert(color_index < Polyhedron::NUM_COLORS); + PRINTDB("setColor %i %i %i %i",color_index%r%g%b); this->colors[color_index] = CGAL::Color(r,g,b); } + + // set this->colors based on the given colorscheme. vertex colors + // are not set here as colorscheme doesnt yet hold vertex colors. + void setColorScheme(const ColorScheme &cs) { + PRINTD("setColorScheme"); + setColor(CGAL_NEF3_MARKED_FACET_COLOR, ColorMap::getColor(cs, CGAL_FACE_BACK_COLOR)); + setColor(CGAL_NEF3_UNMARKED_FACET_COLOR, ColorMap::getColor(cs, CGAL_FACE_FRONT_COLOR)); + setColor(CGAL_NEF3_MARKED_EDGE_COLOR, ColorMap::getColor(cs, CGAL_EDGE_BACK_COLOR)); + setColor(CGAL_NEF3_UNMARKED_EDGE_COLOR, ColorMap::getColor(cs, CGAL_EDGE_FRONT_COLOR)); + } + private: CGAL::Color colors[NUM_COLORS]; -}; // Polyhedron +}; // CGAL_OGL_Polyhedron @@ -108,10 +131,10 @@ private: #include -class Polyhedron +class CGAL_OGL_Polyhedron { public: - Polyhedron() {} + CGAL_OGL_Polyhedron() {} void draw(bool showedges) const {} CGAL::Bbox_3 bbox() const { return CGAL::Bbox_3(-1,-1,-1,1,1,1); } }; diff --git a/src/Camera.cc b/src/Camera.cc index 7f4d3c12..29ecc013 100644 --- a/src/Camera.cc +++ b/src/Camera.cc @@ -5,6 +5,7 @@ Camera::Camera(enum CameraType camtype) : type(camtype), projection(Camera::PERSPECTIVE), fov(45), height(60), viewall(false) { + PRINTD("Camera()"); if (this->type == Camera::GIMBAL) { object_trans << 0,0,0; object_rot << 35,0,25; @@ -92,6 +93,10 @@ void Camera::viewAll(const BoundingBox &bbox, float scalefactor) } break; } + PRINTDB("modified center x y z %f %f %f",center.x() % center.y() % center.z()); + PRINTDB("modified eye x y z %f %f %f",eye.x() % eye.y() % eye.z()); + PRINTDB("modified obj trans x y z %f %f %f",object_trans.x() % object_trans.y() % object_trans.z()); + PRINTDB("modified obj rot x y z %f %f %f",object_rot.x() % object_rot.y() % object_rot.z()); } void Camera::zoom(int delta) diff --git a/src/GLView.cc b/src/GLView.cc index 60589a18..a75e41fc 100644 --- a/src/GLView.cc +++ b/src/GLView.cc @@ -1,8 +1,10 @@ #include "GLView.h" #include "stdio.h" +#include "colormap.h" #include "rendersettings.h" #include "mathc99.h" +#include "printutils.h" #include "renderer.h" #ifdef _WIN32 @@ -22,6 +24,7 @@ GLView::GLView() showaxes = false; showcrosshairs = false; renderer = NULL; + colorscheme = &ColorMap::inst()->defaultColorScheme(); cam = Camera(); far_far_away = RenderSettings::inst()->far_gl_clip_limit; #ifdef ENABLE_OPENCSG @@ -39,6 +42,32 @@ void GLView::setRenderer(Renderer* r) renderer = r; } +/* update the color schemes of the Renderer attached to this GLView +to match the colorscheme of this GLView.*/ +void GLView::updateColorScheme() +{ + if (this->renderer) this->renderer->setColorScheme(*this->colorscheme); +} + +/* change this GLView's colorscheme to the one given, and update the +Renderer attached to this GLView as well. */ +void GLView::setColorScheme(const ColorScheme &cs) +{ + this->colorscheme = &cs; + this->updateColorScheme(); +} + +void GLView::setColorScheme(const std::string &cs) +{ + const ColorScheme *colorscheme = ColorMap::inst()->findColorScheme(cs); + if (colorscheme) { + setColorScheme(*colorscheme); + } + else { + PRINTB("WARNING: GLView: unknown colorscheme %s", cs); + } +} + void GLView::resizeGL(int w, int h) { #ifdef ENABLE_OPENCSG @@ -125,7 +154,7 @@ void GLView::paintGL() setupCamera(); - Color4f bgcol = RenderSettings::inst()->color(RenderSettings::BACKGROUND_COLOR); + Color4f bgcol = ColorMap::getColor(*this->colorscheme, BACKGROUND_COLOR); glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -440,7 +469,7 @@ void GLView::showCrosshairs() // FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them // to change color based on view orientation. glLineWidth(3); - Color4f col = RenderSettings::inst()->color(RenderSettings::CROSSHAIR_COLOR); + 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) diff --git a/src/GLView.h b/src/GLView.h index 83559aff..f3f98ae2 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -26,6 +26,7 @@ Some actions (showCrossHairs) only work properly on Gimbal Camera. #include "system-gl.h" #include #include "Camera.h" +#include "colormap.h" class GLView { @@ -45,11 +46,16 @@ public: void showAxes(); void showSmallaxes(); + void setColorScheme(const ColorScheme &cs); + void setColorScheme(const std::string &cs); + void updateColorScheme(); + virtual bool save(const char *filename) = 0; virtual std::string getRendererInfo() const = 0; virtual float getDPI() { return 1.0f; } Renderer *renderer; + const ColorScheme *colorscheme; Camera cam; double far_far_away; size_t width; diff --git a/src/GeometryEvaluator.cc b/src/GeometryEvaluator.cc index 73848bcf..1ba6f169 100644 --- a/src/GeometryEvaluator.cc +++ b/src/GeometryEvaluator.cc @@ -60,7 +60,14 @@ shared_ptr GeometryEvaluator::evaluateGeometry(const AbstractNod if (!allownef) { if (shared_ptr N = dynamic_pointer_cast(this->root)) { - this->root.reset(N->convertToPolyset()); + PolySet *ps = new PolySet(3); + ps->setConvexity(N->getConvexity()); + this->root.reset(ps); + bool err = CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *ps); + if (err) { + PRINT("ERROR: Nef->PolySet failed"); + } + smartCacheInsert(node, this->root); } } @@ -873,7 +880,7 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) if (chN) chPS.reset(chN->convertToPolyset()); if (chPS) ps2d = PolysetUtils::flatten(*chPS); if (ps2d) { - CGAL_Nef_polyhedron *N2d = createNefPolyhedronFromGeometry(*ps2d); + CGAL_Nef_polyhedron *N2d = CGALUtils::createNefPolyhedronFromGeometry(*ps2d); poly = N2d->convertToPolygon2d(); } #endif @@ -887,7 +894,14 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) if (!chPS) { shared_ptr chN = dynamic_pointer_cast(chgeom); if (chN) { - chPS.reset(chN->convertToPolyset()); + PolySet *ps = new PolySet(3); + bool err = CGALUtils::createPolySetFromNefPolyhedron3(*chN->p3, *ps); + if (err) { + PRINT("ERROR: Nef->PolySet failed"); + } + else { + chPS.reset(ps); + } } } if (chPS) poly = PolysetUtils::project(*chPS); @@ -915,7 +929,7 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) if (newgeom) { shared_ptr Nptr = dynamic_pointer_cast(newgeom); if (!Nptr) { - Nptr.reset(createNefPolyhedronFromGeometry(*newgeom)); + Nptr.reset(CGALUtils::createNefPolyhedronFromGeometry(*newgeom)); } if (!Nptr->isEmpty()) { Polygon2d *poly = CGALUtils::project(*Nptr, node.cut_mode); @@ -948,7 +962,17 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node) if (!isSmartCached(node)) { switch (node.type) { case MINKOWSKI: { - geom = applyToChildren(node, OPENSCAD_MINKOWSKI).constptr(); + ResultObject res = applyToChildren(node, OPENSCAD_MINKOWSKI); + geom = res.constptr(); + // If we added convexity, we need to pass it on + if (geom && geom->getConvexity() != node.convexity) { + shared_ptr editablegeom; + // If we got a const object, make a copy + if (res.isConst()) editablegeom.reset(geom->copy()); + else editablegeom = res.ptr(); + geom = editablegeom; + editablegeom->setConvexity(node.convexity); + } break; } case HULL: { diff --git a/src/MainWindow.h b/src/MainWindow.h index a40755c1..a5dbf33c 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -82,6 +82,8 @@ private slots: void updateMdiMode(bool mdi); void updateUndockMode(bool undockMode); void setFileName(const QString &filename); + void setFont(const QString &family, uint size); + void setColorScheme(const QString &cs); void showProgress(); void openCSGSettingsChanged(); diff --git a/src/OGL_helper.h b/src/OGL_helper.h index 1c7ba2c1..488ee133 100644 --- a/src/OGL_helper.h +++ b/src/OGL_helper.h @@ -17,6 +17,8 @@ // // Author(s) : Peter Hachenberger +// Modified for OpenSCAD + #pragma once #include @@ -25,16 +27,18 @@ #include "system-gl.h" #include -#define CGAL_NEF3_MARKED_VERTEX_COLOR 183,232,92 -#define CGAL_NEF3_MARKED_EDGE_COLOR 171,216,86 -#define CGAL_NEF3_MARKED_FACET_COLOR 157,203,81 -#define CGAL_NEF3_MARKED_BACK_FACET_COLOR 157,103,181 - -#define CGAL_NEF3_UNMARKED_VERTEX_COLOR 255,246,124 -#define CGAL_NEF3_UNMARKED_EDGE_COLOR 255,236,94 -#define CGAL_NEF3_UNMARKED_FACET_COLOR 249,215,44 -#define CGAL_NEF3_UNMARKED_BACK_FACET_COLOR 249,115,144 +// Overridden in CGAL_renderer +/* +#define CGAL_NEF3_OGL_MARKED_VERTEX_COLOR 183,232,92 +#define CGAL_NEF3_OGL_MARKED_EDGE_COLOR 171,216,86 +#define CGAL_NEF3_OGL_MARKED_FACET_COLOR 157,203,81 +#define CGAL_NEF3_OGL_MARKED_BACK_FACET_COLOR 157,103,181 +#define CGAL_NEF3_OGL_UNMARKED_VERTEX_COLOR 255,246,124 +#define CGAL_NEF3_OGL_UNMARKED_EDGE_COLOR 255,236,94 +#define CGAL_NEF3_OGL_UNMARKED_FACET_COLOR 249,215,44 +#define CGAL_NEF3_OGL_UNMARKED_BACK_FACET_COLOR 249,115,144 +*/ const bool cull_backfaces = false; const bool color_backfaces = false; @@ -362,18 +366,24 @@ namespace OGL { Bbox_3 bbox() const { return bbox_; } Bbox_3& bbox() { return bbox_; } + // Overridden in CGAL_renderer virtual CGAL::Color getVertexColor(Vertex_iterator v) const { - CGAL::Color cf(CGAL_NEF3_MARKED_VERTEX_COLOR), - ct(CGAL_NEF3_UNMARKED_VERTEX_COLOR); // more blue-ish - CGAL::Color c = v->mark() ? ct : cf; + PRINTD("getVertexColor()"); + (void)v; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_VERTEX_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_VERTEX_COLOR); // more blue-ish +// CGAL::Color c = v->mark() ? ct : cf; + CGAL::Color c(0,0,200); return c; } void draw(Vertex_iterator v) const { + PRINTD("draw( Vertex_iterator )"); // CGAL_NEF_TRACEN("drawing vertex "<<*v); CGAL::Color c = getVertexColor(v); glPointSize(10); + //glPointSize(1); glColor3ub(c.red(), c.green(), c.blue()); glBegin(GL_POINTS); glVertex3d(v->x(),v->y(),v->z()); @@ -384,19 +394,26 @@ namespace OGL { glEnd(); } + // Overridden in CGAL_renderer virtual CGAL::Color getEdgeColor(Edge_iterator e) const { - CGAL::Color cf(CGAL_NEF3_MARKED_EDGE_COLOR), - ct(CGAL_NEF3_UNMARKED_EDGE_COLOR); // more blue-ish - CGAL::Color c = e->mark() ? ct : cf; + PRINTD("getEdgeColor)"); + (void)e; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_EDGE_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_EDGE_COLOR); // more blue-ish +// CGAL::Color c = e->mark() ? ct : cf; + // Overridden in CGAL_renderer + CGAL::Color c(200,0,0); return c; } void draw(Edge_iterator e) const { + PRINTD("draw(Edge_iterator)"); // CGAL_NEF_TRACEN("drawing edge "<<*e); Double_point p = e->source(), q = e->target(); CGAL::Color c = getEdgeColor(e); glLineWidth(5); + //glLineWidth(1); glColor3ub(c.red(),c.green(),c.blue()); glBegin(GL_LINE_STRIP); glVertex3d(p.x(), p.y(), p.z()); @@ -404,18 +421,32 @@ namespace OGL { glEnd(); } + + // Overridden in CGAL_renderer virtual CGAL::Color getFacetColor(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("getFacetColor"); +/* + (void)f; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_FACET_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_FACET_COLOR); // more blue-ish +// CGAL::Color c = (f->mark() ? ct : cf); +*/ + CGAL::Color c(0,200,0); + return c; +/* if (is_back_facing) return !f->mark() - ? CGAL::Color(CGAL_NEF3_MARKED_BACK_FACET_COLOR) - : CGAL::Color(CGAL_NEF3_UNMARKED_BACK_FACET_COLOR); + ? CGAL::Color(CGAL_NEF3_OGL_MARKED_BACK_FACET_COLOR) + : CGAL::Color(CGAL_NEF3_OGL_UNMARKED_BACK_FACET_COLOR); else return !f->mark() - ? CGAL::Color(CGAL_NEF3_MARKED_FACET_COLOR) - : CGAL::Color(CGAL_NEF3_UNMARKED_FACET_COLOR); + ? CGAL::Color(CGAL_NEF3_OGL_MARKED_FACET_COLOR) + : CGAL::Color(CGAL_NEF3_OGL_UNMARKED_FACET_COLOR); +*/ } void draw(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("draw(Halffacet_iterator)"); // CGAL_NEF_TRACEN("drawing facet "<<(f->debug(),"")); GLUtesselator* tess_ = gluNewTess(); gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), @@ -458,6 +489,7 @@ namespace OGL { void construct_axes() const { + PRINTD("construct_axes"); glLineWidth(2.0); // red x-axis glColor3f(1.0,0.0,0.0); @@ -491,6 +523,7 @@ namespace OGL { void fill_display_lists() { + PRINTD("fill_display_lists"); glNewList(object_list_, GL_COMPILE); Vertex_iterator v; for(v=vertices_.begin();v!=vertices_.end();++v) @@ -528,6 +561,7 @@ namespace OGL { } void init() { + PRINTD("init()"); if (init_) return; init_ = true; switches[SNC_AXES] = false; @@ -535,11 +569,13 @@ namespace OGL { object_list_ = glGenLists(4); CGAL_assertion(object_list_); fill_display_lists(); + PRINTD("init() end"); } void draw() const { + PRINTD("draw()"); if (!is_initialized()) const_cast(*this).init(); double l = (std::max)( (std::max)( bbox().xmax() - bbox().xmin(), bbox().ymax() - bbox().ymin()), @@ -562,6 +598,7 @@ namespace OGL { glCallList(object_list_+1); // edges glCallList(object_list_); // vertices if (switches[SNC_AXES]) glCallList(object_list_+3); // axis + PRINTD("draw() end"); } void debug(std::ostream& os = std::cerr) const diff --git a/src/Polygon2d.h b/src/Polygon2d.h index 1a8bb78e..4477a8f0 100644 --- a/src/Polygon2d.h +++ b/src/Polygon2d.h @@ -4,6 +4,10 @@ #include "linalg.h" #include +/*! + A single contour. + positive is (optionally) used to distinguish between polygon contours and hold contours. +*/ struct Outline2d { Outline2d() : positive(true) {} std::vector vertices; diff --git a/src/Preferences.cc b/src/Preferences.cc index 7d311aa0..5a47d880 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -37,6 +37,8 @@ #ifdef ENABLE_CGAL #include "CGALCache.h" #endif +#include "colormap.h" +#include "rendersettings.h" Preferences *Preferences::instance = NULL; @@ -91,7 +93,6 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->fontSize->setEditText( QString("%1").arg( savedsize ) ); // Setup default settings - this->defaultmap["3dview/colorscheme"] = this->colorSchemeChooser->currentItem()->text(); this->defaultmap["advanced/opencsg_show_warning"] = true; this->defaultmap["advanced/enable_opencsg_opengl1x"] = true; this->defaultmap["advanced/polysetCacheSize"] = uint(GeometryCache::instance()->maxSize()); @@ -124,38 +125,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->actionTriggered(this->prefsAction3DView); // 3D View pane - this->colorschemes["Cornfield"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xff, 0xff, 0xe5); - this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); - - this->colorschemes["Metallic"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xaa, 0xaa, 0xff); - this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); - - this->colorschemes["Sunset"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xaa, 0x44, 0x44); - this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); + this->defaultmap["3dview/colorscheme"] = "Cornfield"; // Advanced pane QValidator *validator = new QIntValidator(this); @@ -167,8 +137,6 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) setupFeaturesPage(); updateGUI(); - - RenderSettings::inst()->setColors(this->colorschemes[getValue("3dview/colorscheme").toString()]); } Preferences::~Preferences() @@ -279,10 +247,7 @@ void Preferences::on_colorSchemeChooser_itemSelectionChanged() QString scheme = this->colorSchemeChooser->currentItem()->text(); QSettings settings; settings.setValue("3dview/colorscheme", scheme); - - RenderSettings::inst()->setColors(this->colorschemes[scheme]); - - emit requestRedraw(); + emit colorSchemeChanged( scheme ); } void Preferences::on_fontChooser_activated(const QString &family) diff --git a/src/Preferences.h b/src/Preferences.h index 35a06faf..be951ade 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -3,9 +3,7 @@ #include #include #include "ui_Preferences.h" -#include "rendersettings.h" -#include "linalg.h" -#include +#include "colormap.h" class Preferences : public QMainWindow, public Ui::Preferences { @@ -43,6 +41,7 @@ signals: void updateMdiMode(bool mdi) const; void updateUndockMode(bool mdi) const; void fontChanged(const QString &family, uint size) const; + void colorSchemeChanged(const QString &scheme) const; void openCSGSettingsChanged() const; void syntaxHighlightChanged(const QString &s); void editorTypeChanged(const QString &type); @@ -56,7 +55,6 @@ private: void addPrefPage(QActionGroup *group, QAction *action, QWidget *widget); QSettings::SettingsMap defaultmap; - QHash > colorschemes; QHash prefPages; static Preferences *instance; diff --git a/src/Preferences.ui b/src/Preferences.ui index 5ec72dc3..f880fb57 100644 --- a/src/Preferences.ui +++ b/src/Preferences.ui @@ -49,29 +49,55 @@ - - - false - - - 0 - - - - Cornfield - - - - - Metallic - - - - - Sunset - - - + + + + 0 + 0 + + + + false + + + 0 + + + + Cornfield + + + + + Metallic + + + + + Sunset + + + + + Starnight + + + + + BeforeDawn + + + + + Nature + + + + + DeepOcean + + + @@ -158,6 +184,9 @@ QSizePolicy::Expanding + + QSizePolicy::Expanding + 30 @@ -429,7 +458,45 @@ - + + + + 75 + true + + + + Color syntax highlighting + + + + + + + + 0 + 0 + + + + + For Light Background + + + + + For Dark Background + + + + + Off + + + + + + Qt::Horizontal diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index d95e7afe..4b114041 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -27,6 +27,7 @@ #include "ThrownTogetherRenderer.h" #include "polyset.h" #include "csgterm.h" +#include "printutils.h" #include "system-gl.h" @@ -43,6 +44,7 @@ ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain, void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const { + PRINTD("Thrown draw"); if (this->root_chain) { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -62,6 +64,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, bool background, bool showedges, bool fberror) const { + PRINTD("Thrown renderCSGChain"); glDepthFunc(GL_LEQUAL); boost::unordered_map,int> geomVisitMark; BOOST_FOREACH(const CSGChainObject &obj, chain->objects) { diff --git a/src/cgalutils-tess.cc b/src/cgalutils-tess.cc new file mode 100644 index 00000000..c8188908 --- /dev/null +++ b/src/cgalutils-tess.cc @@ -0,0 +1,433 @@ +/* + +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 +#include + +typedef CGAL_Kernel3 Kernel; +//typedef CGAL::Triangulation_vertex_base_2 Vb; +typedef CGAL::Triangulation_vertex_base_2 Vb; +//typedef CGAL::Constrained_triangulation_face_base_2 Fb; +typedef CGAL::Delaunay_mesh_face_base_2 Fb; +typedef CGAL::Triangulation_data_structure_2 TDS; +typedef CGAL::Exact_intersections_tag ITAG; +typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; +//typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; + +typedef CDT::Vertex_handle Vertex_handle; +typedef CDT::Point CDTPoint; + +typedef CGAL::Ray_2 CGAL_Ray_2; +typedef CGAL::Line_3 CGAL_Line_3; +typedef CGAL::Point_2 CGAL_Point_2; +typedef CGAL::Vector_2 CGAL_Vector_2; +typedef CGAL::Segment_2 CGAL_Segment_2; +typedef CGAL::Direction_2 CGAL_Direction_2; +typedef CGAL::Direction_3 CGAL_Direction_3; +typedef CGAL::Plane_3 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 . 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(&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 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 &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(&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 tessellate3DFaceWithHoles(std::vector &polygons, + std::vector &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 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 > polygons2d; + for (size_t i=0;i vhandles; + std::vector polygon2d; + for (size_t j=0;j list_of_seeds; + for (size_t i=1;i &pgon = polygons2d[i]; + for (size_t j=0;j::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() ); + + 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; + } +}; diff --git a/src/cgalutils.cc b/src/cgalutils.cc index e7265b9b..fb5fcbbb 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -26,10 +26,335 @@ namespace /* anonymous */ { 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; + } + } + + class CGAL_Build_PolySet : public CGAL::Modifier_base + { + public: + typedef CGAL_Polybuilder::Point_3 CGALPoint; + + const PolySet &ps; + CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } + +/* + Using Grid here is important for performance reasons. See following model. + If we don't grid the geometry before converting to a Nef Polyhedron, the quads + in the cylinders to tessellated into triangles since floating point + incertainty causes the faces to not be 100% planar. The incertainty is exaggerated + by the transform. This wasn't a problem earlier since we used Nef for everything, + but optimizations since then has made us keep it in floating point space longer. + + minkowski() { + cube([200, 50, 7], center = true); + rotate([90,0,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); + rotate([0,90,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); + } +*/ +#if 1 // Use Grid + void operator()(CGAL_HDS& hds) { + CGAL_Polybuilder B(hds, true); + + std::vector vertices; + Grid3d grid(GRID_FINE); + std::vector indices(3); + + BOOST_FOREACH(const PolySet::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 + grid.align(v[0], v[1], v[2]) = vertices.size(); + vertices.push_back(CGALPoint(v[0], v[1], v[2])); + } + } + } + +#ifdef GEN_SURFACE_DEBUG + printf("polyhedron(faces=["); + int pidx = 0; +#endif + B.begin_surface(vertices.size(), ps.polygons.size()); + BOOST_FOREACH(const CGALPoint &p, vertices) { + B.add_vertex(p); + } + BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { +#ifdef GEN_SURFACE_DEBUG + if (pidx++ > 0) printf(","); +#endif + indices.clear(); + BOOST_FOREACH(const Vector3d &v, p) { + indices.push_back(grid.data(v[0], v[1], v[2])); + } + + // We perform this test since there is a bug in CGAL's + // Polyhedron_incremental_builder_3::test_facet() which + // fails to detect duplicate indices + bool err = false; + for (std::size_t i = 0; i < indices.size(); ++i) { + // check if vertex indices[i] is already in the sequence [0..i-1] + for (std::size_t k = 0; k < i && !err; ++k) { + if (indices[k] == indices[i]) { + err = true; + break; + } + } + } + if (!err && B.test_facet(indices.begin(), indices.end())) { + B.add_facet(indices.begin(), indices.end()); + } +#ifdef GEN_SURFACE_DEBUG + printf("["); + int fidx = 0; + BOOST_FOREACH(size_t i, indices) { + if (fidx++ > 0) printf(","); + printf("%ld", i); + } + printf("]"); +#endif + } + B.end_surface(); +#ifdef GEN_SURFACE_DEBUG + printf("],\n"); +#endif +#ifdef GEN_SURFACE_DEBUG + printf("points=["); + for (int i=0;i 0) printf(","); + const CGALPoint &p = vertices[i]; + printf("[%g,%g,%g]", CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z())); + } + printf("]);\n"); +#endif + } +#else // Don't use Grid + void operator()(CGAL_HDS& hds) + { + CGAL_Polybuilder B(hds, true); + typedef boost::tuple BuilderVertex; + Reindexer vertices; + std::vector indices(3); + + // Estimating same # of vertices as polygons (very rough) + B.begin_surface(ps.polygons.size(), ps.polygons.size()); + int pidx = 0; +#ifdef GEN_SURFACE_DEBUG + printf("polyhedron(faces=["); +#endif + BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { +#ifdef GEN_SURFACE_DEBUG + if (pidx++ > 0) printf(","); +#endif + indices.clear(); + BOOST_REVERSE_FOREACH(const Vector3d &v, p) { + size_t s = vertices.size(); + size_t idx = vertices.lookup(v); + // If we added a vertex, also add it to the CGAL builder + if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2])); + indices.push_back(idx); + } + // We perform this test since there is a bug in CGAL's + // Polyhedron_incremental_builder_3::test_facet() which + // fails to detect duplicate indices + bool err = false; + for (std::size_t i = 0; i < indices.size(); ++i) { + // check if vertex indices[i] is already in the sequence [0..i-1] + for (std::size_t k = 0; k < i && !err; ++k) { + if (indices[k] == indices[i]) { + err = true; + break; + } + } + } + if (!err && B.test_facet(indices.begin(), indices.end())) { + B.add_facet(indices.begin(), indices.end()); +#ifdef GEN_SURFACE_DEBUG + printf("["); + int fidx = 0; + BOOST_FOREACH(size_t i, indices) { + if (fidx++ > 0) printf(","); + printf("%ld", i); + } + printf("]"); +#endif + } + } + B.end_surface(); +#ifdef GEN_SURFACE_DEBUG + printf("],\n"); + + printf("points=["); + for (int vidx=0;vidx 0) printf(","); + const Vector3d &v = vertices.getArray()[vidx]; + printf("[%g,%g,%g]", v[0], v[1], v[2]); + } + printf("]);\n"); +#endif + } +#endif + }; + + // This code is from CGAL/demo/Polyhedron/Scene_nef_polyhedron_item.cpp + // quick hacks to convert polyhedra from exact to inexact and vice-versa + template + struct Copy_polyhedron_to + : public CGAL::Modifier_base + { + Copy_polyhedron_to(const Polyhedron_input& in_poly) + : in_poly(in_poly) {} + + void operator()(typename Polyhedron_output::HalfedgeDS& out_hds) + { + typedef typename Polyhedron_output::HalfedgeDS Output_HDS; + + CGAL::Polyhedron_incremental_builder_3 builder(out_hds); + + typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator; + typedef typename Polyhedron_input::Facet_const_iterator Facet_const_iterator; + typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC; + + builder.begin_surface(in_poly.size_of_vertices(), + in_poly.size_of_facets(), + in_poly.size_of_halfedges()); + + for(Vertex_const_iterator + 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())); + builder.add_vertex(p); + } + + typedef CGAL::Inverse_index Index; + Index index( in_poly.vertices_begin(), in_poly.vertices_end()); + + for(Facet_const_iterator + fi = in_poly.facets_begin(), end = in_poly.facets_end(); + fi != end; ++fi) + { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + // 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); + builder.end_facet(); + } + builder.end_surface(); + } // end operator()(..) + private: + const Polyhedron_input& in_poly; + }; // end Copy_polyhedron_to<> + + template + void copy_to(const Poly_A& poly_a, Poly_B& poly_b) + { + Copy_polyhedron_to modifier(poly_a); + poly_b.delegate(modifier); + } + +} + +static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) +{ + if (ps.isEmpty()) return new CGAL_Nef_polyhedron(); + assert(ps.getDimension() == 3); + + if (ps.is_convex()) { + typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + // Collect point cloud + std::set points; + for (int i = 0; i < ps.polygons.size(); i++) { + for (int j = 0; j < ps.polygons[i].size(); j++) { + points.insert(vector_convert(ps.polygons[i][j])); + } + } + + if (points.size() <= 3) return new CGAL_Nef_polyhedron();; + + // Apply hull + CGAL::Polyhedron_3 r; + CGAL::convex_hull_3(points.begin(), points.end(), r); + CGAL::Polyhedron_3 r_exact; + copy_to(r,r_exact); + return new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(r_exact)); + } + + CGAL_Nef_polyhedron3 *N = NULL; + bool plane_error = false; + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + 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) 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) { + PRINT("PolySet has nonplanar faces. Attempting alternate construction"); + plane_error=true; + } + } else { + PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); + } + } + if (plane_error) try { + PolySet ps2(3); + CGAL_Polyhedron P; + PolysetUtils::tessellate_faces(ps, ps2); + bool err = CGALUtils::createPolyhedronFromPolySet(ps2,P); + if (!err) N = new CGAL_Nef_polyhedron3(P); + } + catch (const CGAL::Assertion_exception &e) { + PRINTB("Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); + } + CGAL::set_error_behaviour(old_behaviour); + return new CGAL_Nef_polyhedron(N); +} + +static CGAL_Nef_polyhedron *createNefPolyhedronFromPolygon2d(const Polygon2d &polygon) +{ + shared_ptr ps(polygon.tessellate()); + return createNefPolyhedronFromPolySet(*ps); } namespace CGALUtils { + bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p) + { + bool err = false; + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + CGAL_Build_PolySet builder(ps); + p.delegate(builder); + } + catch (const CGAL::Assertion_exception &e) { + PRINTB("CGAL error in CGALUtils::createPolyhedronFromPolySet: %s", e.what()); + err = true; + } + CGAL::set_error_behaviour(old_behaviour); + return err; + } + bool applyHull(const Geometry::ChildList &children, PolySet &result) { typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -128,11 +453,12 @@ namespace CGALUtils { const CGAL_Nef_polyhedron * nef = dynamic_cast(operands[i]); - if (ps) createPolyhedronFromPolySet(*ps, poly); + if (ps) CGALUtils::createPolyhedronFromPolySet(*ps, poly); else if (nef && nef->p3->is_simple()) nefworkaround::convert_to_Polyhedron(*nef->p3, poly); else throw 0; - if (ps && ps->is_convex() || !ps && is_weakly_convex(poly)) { + if ((ps && ps->is_convex()) || + (!ps && is_weakly_convex(poly))) { PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef") ); P[i].push_back(poly); } else { @@ -509,9 +835,9 @@ namespace CGALUtils { newN.p3.reset(new CGAL_Nef_polyhedron3(N.p3->intersection(xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY))); } catch (const CGAL::Failure_exception &e) { - PRINTB("CGALUtils::project during plane intersection: %s", e.what()); + PRINTDB("CGALUtils::project during plane intersection: %s", e.what()); try { - PRINT("Trying alternative intersection using very large thin box: "); + PRINTD("Trying alternative intersection using very large thin box: "); std::vector pts; // dont use z of 0. there are bugs in CGAL. double inf = 1e8; @@ -562,10 +888,13 @@ namespace CGALUtils { } // In projection mode all the triangles are projected manually into the XY plane else { - PolySet *ps3 = N.convertToPolyset(); - if (!ps3) return poly; - poly = PolysetUtils::project(*ps3); - delete ps3; + PolySet ps(3); + bool err = CGALUtils::createPolySetFromNefPolyhedron3(*N.p3, ps); + if (err) { + PRINT("ERROR: Nef->PolySet failed"); + return poly; + } + poly = PolysetUtils::project(ps); } return poly; } @@ -619,23 +948,26 @@ namespace CGALUtils { std::vector facet_planes; facet_planes.reserve(ps.polygons.size()); for (int i = 0; i < ps.polygons.size(); i++) { + Plane plane; size_t N = ps.polygons[i].size(); - assert(N > 0); - std::vector v(N); - for (int j = 0; j < N; j++) { - v[j] = vector_convert(ps.polygons[i][j]); - Edge edge(ps.polygons[i][j],ps.polygons[i][(j+1)%N]); - if (edge_to_facet_map.count(edge)) return false; // edge already exists: nonmanifold - edge_to_facet_map[edge] = i; + if (N >= 3) { + std::vector v(N); + for (int j = 0; j < N; j++) { + v[j] = vector_convert(ps.polygons[i][j]); + Edge edge(ps.polygons[i][j],ps.polygons[i][(j+1)%N]); + if (edge_to_facet_map.count(edge)) return false; // edge already exists: nonmanifold + edge_to_facet_map[edge] = i; + } + Vector normal; + CGAL::normal_vector_newell_3(v.begin(), v.end(), normal); + plane = Plane(v[0], normal); } - Vector normal; - CGAL::normal_vector_newell_3(v.begin(), v.end(), normal); - - facet_planes.push_back(Plane(v[0], normal)); + facet_planes.push_back(plane); } for (int i = 0; i < ps.polygons.size(); i++) { size_t N = ps.polygons[i].size(); + if (N < 3) continue; for (int j = 0; j < N; j++) { Edge other_edge(ps.polygons[i][(j+1)%N], ps.polygons[i][j]); if (edge_to_facet_map.count(other_edge) == 0) return false;// @@ -681,472 +1013,35 @@ namespace CGALUtils { // Make sure that we were able to reach all polygons during our visit return explored_facets.size() == ps.polygons.size(); } -}; -template -bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps) -{ - bool err = false; - typedef typename Polyhedron::Vertex Vertex; - typedef typename Polyhedron::Vertex_const_iterator VCI; - typedef typename Polyhedron::Facet_const_iterator FCI; - typedef typename Polyhedron::Halfedge_around_facet_const_circulator HFCC; - - for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - ps.append_poly(); - do { - Vertex const& v = *((hc++)->vertex()); - double x = CGAL::to_double(v.point().x()); - double y = CGAL::to_double(v.point().y()); - double z = CGAL::to_double(v.point().z()); - ps.append_vertex(x, y, z); - } while (hc != hc_end); - } - return err; -} - -template bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps); -template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); -template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); -template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 > &p, PolySet &ps); - - -/////// Tessellation begin - -typedef CGAL::Plane_3 CGAL_Plane_3; - - -/* - -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 -#include - -typedef CGAL_Kernel3 Kernel; -//typedef CGAL::Triangulation_vertex_base_2 Vb; -typedef CGAL::Triangulation_vertex_base_2 Vb; -//typedef CGAL::Constrained_triangulation_face_base_2 Fb; -typedef CGAL::Delaunay_mesh_face_base_2 Fb; -typedef CGAL::Triangulation_data_structure_2 TDS; -typedef CGAL::Exact_intersections_tag ITAG; -typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; -//typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; - -typedef CDT::Vertex_handle Vertex_handle; -typedef CDT::Point CDTPoint; - -typedef CGAL::Ray_2 CGAL_Ray_2; -typedef CGAL::Line_3 CGAL_Line_3; -typedef CGAL::Point_2 CGAL_Point_2; -typedef CGAL::Vector_2 CGAL_Vector_2; -typedef CGAL::Segment_2 CGAL_Segment_2; -typedef std::vector CGAL_Polygon_3; -typedef CGAL::Direction_2 CGAL_Direction_2; -typedef CGAL::Direction_3 CGAL_Direction_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 . 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(&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 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 &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(&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; -} - -/* 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 tessellate_3d_face_with_holes( std::vector &polygons, std::vector &triangles, CGAL_Plane_3 &plane ) -{ - if (polygons.size()==1 && polygons[0].size()==3) { - PRINT("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 vertmap; - - PRINT("finding good projection"); - projection_t goodproj = find_good_projection( plane ); - - PRINTB("plane %s",plane ); - PRINTB("proj: %i %i",goodproj.plane % goodproj.flip); - PRINT("Inserting points and edges into Constrained Delaunay Triangulation"); - std::vector< std::vector > polygons2d; - for (size_t i=0;i vhandles; - std::vector polygon2d; - for (size_t j=0;j list_of_seeds; - for (size_t i=1;i &pgon = polygons2d[i]; - for (size_t j=0;j::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() ); - PRINTB("seed %f,%f",x%y); - } - PRINT("seeding done"); - - PRINT( "meshing" ); - CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt, - list_of_seeds.begin(), list_of_seeds.end(), - DummyCriteria() ); - - PRINT("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++ ) + template + bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps) { - 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 ); - } - } + bool err = false; + typedef typename Polyhedron::Vertex Vertex; + typedef typename Polyhedron::Vertex_const_iterator VCI; + typedef typename Polyhedron::Facet_const_iterator FCI; + typedef typename Polyhedron::Halfedge_around_facet_const_circulator HFCC; + + for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + ps.append_poly(); + do { + Vertex const& v = *((hc++)->vertex()); + double x = CGAL::to_double(v.point().x()); + double y = CGAL::to_double(v.point().y()); + double z = CGAL::to_double(v.point().z()); + ps.append_vertex(x, y, z); + } while (hc != hc_end); + } + return err; + } - PRINTDB("built %i triangles",triangles.size()); - return err; -} -/////// Tessellation end + template bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 > &p, PolySet &ps); /* Create a PolySet from a Nef Polyhedron 3. return false on success, @@ -1155,243 +1050,70 @@ bool tessellate_3d_face_with_holes( std::vector &polygons, std:: formats) do not allow for holes in their faces. The function documents the method used to deal with this */ -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 plane( hfaceti->plane() ); - std::vector polygons; - // the 0-mark-volume is the 'empty' volume of space. skip it. - if (hfaceti->incident_volume()->mark() == 0) 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 ); - } - polygons.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*/ - std::vector triangles; - bool err = tessellate_3d_face_with_holes( polygons, triangles, plane ); - if (!err) for (size_t i=0;i -{ -public: - typedef CGAL_Polybuilder::Point_3 CGALPoint; - - const PolySet &ps; - CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } - -/* - Using Grid here is important for performance reasons. See following model. - If we don't grid the geometry before converting to a Nef Polyhedron, the quads - in the cylinders to tessellated into triangles since floating point - incertainty causes the faces to not be 100% planar. The incertainty is exaggerated - by the transform. This wasn't a problem earlier since we used Nef for everything, - but optimizations since then has made us keep it in floating point space longer. - - minkowski() { - cube([200, 50, 7], center = true); - rotate([90,0,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); - rotate([0,90,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); - } - */ -#if 1 // Use Grid - void operator()(CGAL_HDS& hds) { - CGAL_Polybuilder B(hds, true); - - std::vector vertices; - Grid3d grid(GRID_FINE); - std::vector indices(3); - - BOOST_FOREACH(const PolySet::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 - grid.align(v[0], v[1], v[2]) = vertices.size(); - vertices.push_back(CGALPoint(v[0], v[1], v[2])); - } - } - } - -#ifdef GEN_SURFACE_DEBUG - printf("polyhedron(faces=["); - int pidx = 0; -#endif - B.begin_surface(vertices.size(), ps.polygons.size()); - BOOST_FOREACH(const CGALPoint &p, vertices) { - B.add_vertex(p); - } - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { -#ifdef GEN_SURFACE_DEBUG - if (pidx++ > 0) printf(","); -#endif - indices.clear(); - BOOST_FOREACH(const Vector3d &v, p) { - indices.push_back(grid.data(v[0], v[1], v[2])); - } - - // We perform this test since there is a bug in CGAL's - // Polyhedron_incremental_builder_3::test_facet() which - // fails to detect duplicate indices - bool err = false; - for (std::size_t i = 0; i < indices.size(); ++i) { - // check if vertex indices[i] is already in the sequence [0..i-1] - for (std::size_t k = 0; k < i && !err; ++k) { - if (indices[k] == indices[i]) { - err = true; - break; - } - } - } - if (!err && B.test_facet(indices.begin(), indices.end())) { - B.add_facet(indices.begin(), indices.end()); - } -#ifdef GEN_SURFACE_DEBUG - printf("["); - int fidx = 0; - BOOST_FOREACH(size_t i, indices) { - if (fidx++ > 0) printf(","); - printf("%ld", i); - } - printf("]"); -#endif - } - B.end_surface(); -#ifdef GEN_SURFACE_DEBUG - printf("],\n"); -#endif -#ifdef GEN_SURFACE_DEBUG - printf("points=["); - for (int i=0;i 0) printf(","); - const CGALPoint &p = vertices[i]; - printf("[%g,%g,%g]", CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z())); - } - printf("]);\n"); -#endif - } -#else // Don't use Grid - void operator()(CGAL_HDS& hds) + bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps) { - CGAL_Polybuilder B(hds, true); - typedef boost::tuple BuilderVertex; - Reindexer vertices; - std::vector indices(3); - - // Estimating same # of vertices as polygons (very rough) - B.begin_surface(ps.polygons.size(), ps.polygons.size()); - int pidx = 0; -#ifdef GEN_SURFACE_DEBUG - printf("polyhedron(faces=["); -#endif - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { -#ifdef GEN_SURFACE_DEBUG - if (pidx++ > 0) printf(","); -#endif - indices.clear(); - BOOST_REVERSE_FOREACH(const Vector3d &v, p) { - size_t s = vertices.size(); - size_t idx = vertices.lookup(v); - // If we added a vertex, also add it to the CGAL builder - if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2])); - indices.push_back(idx); + bool err = false; + CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti; + CGAL_forall_halffacets( hfaceti, N ) { + CGAL::Plane_3 plane( hfaceti->plane() ); + std::vector polygons; + // 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 ); + } + polygons.push_back( polygon ); } - // We perform this test since there is a bug in CGAL's - // Polyhedron_incremental_builder_3::test_facet() which - // fails to detect duplicate indices - bool err = false; - for (std::size_t i = 0; i < indices.size(); ++i) { - // check if vertex indices[i] is already in the sequence [0..i-1] - for (std::size_t k = 0; k < i && !err; ++k) { - if (indices[k] == indices[i]) { - err = true; - break; + + /* 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*/ + std::vector triangles; + bool err = CGALUtils::tessellate3DFaceWithHoles(polygons, triangles, plane); + if (!err) { + for (size_t i=0;i=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); } } } - if (!err && B.test_facet(indices.begin(), indices.end())) { - B.add_facet(indices.begin(), indices.end()); -#ifdef GEN_SURFACE_DEBUG - printf("["); - int fidx = 0; - BOOST_FOREACH(size_t i, indices) { - if (fidx++ > 0) printf(","); - printf("%ld", i); - } - printf("]"); -#endif - } } - B.end_surface(); -#ifdef GEN_SURFACE_DEBUG - printf("],\n"); + return err; + } - printf("points=["); - for (int vidx=0;vidx 0) printf(","); - const Vector3d &v = vertices.getArray()[vidx]; - printf("[%g,%g,%g]", v[0], v[1], v[2]); + CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom) + { + const PolySet *ps = dynamic_cast(&geom); + if (ps) { + return createNefPolyhedronFromPolySet(*ps); } - printf("]);\n"); -#endif + else { + const Polygon2d *poly2d = dynamic_cast(&geom); + if (poly2d) return createNefPolyhedronFromPolygon2d(*poly2d); + } + assert(false && "createNefPolyhedronFromGeometry(): Unsupported geometry type"); + return NULL; } -#endif -}; +}; // namespace CGALUtils -bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p) -{ - bool err = false; - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { - CGAL_Build_PolySet builder(ps); - p.delegate(builder); - } - catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGALUtils::createPolyhedronFromPolySet: %s", e.what()); - err = true; - } - CGAL::set_error_behaviour(old_behaviour); - return err; -} void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) { @@ -1446,154 +1168,6 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) PRINTD(" "); } -namespace /* anonymous */ { - // This code is from CGAL/demo/Polyhedron/Scene_nef_polyhedron_item.cpp - // quick hacks to convert polyhedra from exact to inexact and vice-versa - template - struct Copy_polyhedron_to - : public CGAL::Modifier_base - { - Copy_polyhedron_to(const Polyhedron_input& in_poly) - : in_poly(in_poly) {} - - void operator()(typename Polyhedron_output::HalfedgeDS& out_hds) - { - typedef typename Polyhedron_output::HalfedgeDS Output_HDS; - - CGAL::Polyhedron_incremental_builder_3 builder(out_hds); - - typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator; - typedef typename Polyhedron_input::Facet_const_iterator Facet_const_iterator; - typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC; - - builder.begin_surface(in_poly.size_of_vertices(), - in_poly.size_of_facets(), - in_poly.size_of_halfedges()); - - for(Vertex_const_iterator - 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())); - builder.add_vertex(p); - } - - typedef CGAL::Inverse_index Index; - Index index( in_poly.vertices_begin(), in_poly.vertices_end()); - - for(Facet_const_iterator - fi = in_poly.facets_begin(), end = in_poly.facets_end(); - fi != end; ++fi) - { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - // 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); - builder.end_facet(); - } - builder.end_surface(); - } // end operator()(..) - private: - const Polyhedron_input& in_poly; - }; // end Copy_polyhedron_to<> - - template - void copy_to(const Poly_A& poly_a, Poly_B& poly_b) - { - Copy_polyhedron_to modifier(poly_a); - poly_b.delegate(modifier); - } -} - -static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) -{ - if (ps.isEmpty()) return new CGAL_Nef_polyhedron(); - assert(ps.getDimension() == 3); - - if (ps.is_convex()) { - typedef CGAL::Exact_predicates_inexact_constructions_kernel K; - // Collect point cloud - std::set points; - for (int i = 0; i < ps.polygons.size(); i++) { - for (int j = 0; j < ps.polygons[i].size(); j++) { - points.insert(vector_convert(ps.polygons[i][j])); - } - } - - if (points.size() <= 3) return new CGAL_Nef_polyhedron();; - - // Apply hull - CGAL::Polyhedron_3 r; - CGAL::convex_hull_3(points.begin(), points.end(), r); - CGAL::Polyhedron_3 r_exact; - copy_to(r,r_exact); - return new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(r_exact)); - } - - CGAL_Nef_polyhedron3 *N = NULL; - bool plane_error = false; - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { - CGAL_Polyhedron P; - bool err = createPolyhedronFromPolySet(ps, P); - // if (!err) { - // PRINTB("Polyhedron is closed: %d", P.is_closed()); - // PRINTB("Polyhedron is valid: %d", P.is_valid(true, 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) { - PRINT("PolySet has nonplanar faces. Attempting alternate construction"); - plane_error=true; - } - } else { - PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); - } - } - if (plane_error) try { - PolySet ps2(3); - CGAL_Polyhedron P; - PolysetUtils::tessellate_faces(ps, ps2); - bool err = createPolyhedronFromPolySet(ps2,P); - if (!err) N = new CGAL_Nef_polyhedron3(P); - } - catch (const CGAL::Assertion_exception &e) { - PRINTB("Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); - } - CGAL::set_error_behaviour(old_behaviour); - return new CGAL_Nef_polyhedron(N); -} - -static CGAL_Nef_polyhedron *createNefPolyhedronFromPolygon2d(const Polygon2d &polygon) -{ - shared_ptr ps(polygon.tessellate()); - return createNefPolyhedronFromPolySet(*ps); -} - -CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom) -{ - const PolySet *ps = dynamic_cast(&geom); - if (ps) { - return createNefPolyhedronFromPolySet(*ps); - } - else { - const Polygon2d *poly2d = dynamic_cast(&geom); - if (poly2d) return createNefPolyhedronFromPolygon2d(*poly2d); - } - assert(false && "createNefPolyhedronFromGeometry(): Unsupported geometry type"); - return NULL; -} #endif /* ENABLE_CGAL */ diff --git a/src/cgalutils.h b/src/cgalutils.h index e4514c3a..f660f5aa 100644 --- a/src/cgalutils.h +++ b/src/cgalutils.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "cgal.h" #include "polyset.h" #include "CGAL_Nef_polyhedron.h" #include "enums.h" @@ -13,12 +13,17 @@ namespace CGALUtils { CGAL_Iso_cuboid_3 boundingBox(const CGAL_Nef_polyhedron3 &N); bool is_approximately_convex(const PolySet &ps); Geometry const* applyMinkowski(const Geometry::ChildList &children); -}; -CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const class Geometry &geom); -template bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps); -bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps); -bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p); + CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const class Geometry &geom); + template bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps); + bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps); + bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p); + + typedef std::vector CGAL_Polygon_3; + bool tessellate3DFaceWithHoles(std::vector &polygons, + std::vector &triangles, + CGAL::Plane_3 &plane); +}; #include "svg.h" #include "printutils.h" diff --git a/src/color.cc b/src/color.cc index b91fed15..390a11d4 100644 --- a/src/color.cc +++ b/src/color.cc @@ -35,18 +35,18 @@ #include #include using namespace boost::assign; // bring 'operator+=()' into scope +#include "colormap.h" class ColorModule : public AbstractModule { public: - ColorModule() { } + ColorModule() : webcolors(ColorMap::inst()->webColors()) { } virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; private: - static boost::unordered_map colormap; + const boost::unordered_map &webcolors; }; -#include "colormap.h" AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { ColorNode *node = new ColorNode(inst); @@ -72,8 +72,8 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia std::string colorname = v.toString(); boost::algorithm::to_lower(colorname); Color4f color; - if (colormap.find(colorname) != colormap.end()) { - node->color = colormap[colorname]; + if (webcolors.find(colorname) != webcolors.end()) { + node->color = webcolors.at(colorname); } else { PRINTB_NOCACHE("WARNING: Color name \"%s\" unknown. Please see", colorname); PRINT_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); diff --git a/src/colormap.cc b/src/colormap.cc new file mode 100644 index 00000000..0ec7c460 --- /dev/null +++ b/src/colormap.cc @@ -0,0 +1,318 @@ +#include "colormap.h" +#include +#include "printutils.h" +using namespace boost::assign; // bring map_list_of() into scope + +ColorMap *ColorMap::inst(bool erase) +{ + static ColorMap *instance = new ColorMap; + if (erase) { + delete instance; + instance = NULL; + } + return instance; +} + +ColorMap::ColorMap() { + webcolors = map_list_of + ("aliceblue", Color4f(240, 248, 255)) + ("antiquewhite", Color4f(250, 235, 215)) + ("aqua", Color4f(0, 255, 255)) + ("aquamarine", Color4f(127, 255, 212)) + ("azure", Color4f(240, 255, 255)) + ("beige", Color4f(245, 245, 220)) + ("bisque", Color4f(255, 228, 196)) + ("black", Color4f(0, 0, 0)) + ("blanchedalmond", Color4f(255, 235, 205)) + ("blue", Color4f(0, 0, 255)) + ("blueviolet", Color4f(138, 43, 226)) + ("brown", Color4f(165, 42, 42)) + ("burlywood", Color4f(222, 184, 135)) + ("cadetblue", Color4f(95, 158, 160)) + ("chartreuse", Color4f(127, 255, 0)) + ("chocolate", Color4f(210, 105, 30)) + ("coral", Color4f(255, 127, 80)) + ("cornflowerblue", Color4f(100, 149, 237)) + ("cornsilk", Color4f(255, 248, 220)) + ("crimson", Color4f(220, 20, 60)) + ("cyan", Color4f(0, 255, 255)) + ("darkblue", Color4f(0, 0, 139)) + ("darkcyan", Color4f(0, 139, 139)) + ("darkgoldenrod", Color4f(184, 134, 11)) + ("darkgray", Color4f(169, 169, 169)) + ("darkgreen", Color4f(0, 100, 0)) + ("darkgrey", Color4f(169, 169, 169)) + ("darkkhaki", Color4f(189, 183, 107)) + ("darkmagenta", Color4f(139, 0, 139)) + ("darkolivegreen", Color4f(85, 107, 47)) + ("darkorange", Color4f(255, 140, 0)) + ("darkorchid", Color4f(153, 50, 204)) + ("darkred", Color4f(139, 0, 0)) + ("darksalmon", Color4f(233, 150, 122)) + ("darkseagreen", Color4f(143, 188, 143)) + ("darkslateblue", Color4f(72, 61, 139)) + ("darkslategray", Color4f(47, 79, 79)) + ("darkslategrey", Color4f(47, 79, 79)) + ("darkturquoise", Color4f(0, 206, 209)) + ("darkviolet", Color4f(148, 0, 211)) + ("deeppink", Color4f(255, 20, 147)) + ("deepskyblue", Color4f(0, 191, 255)) + ("dimgray", Color4f(105, 105, 105)) + ("dimgrey", Color4f(105, 105, 105)) + ("dodgerblue", Color4f(30, 144, 255)) + ("firebrick", Color4f(178, 34, 34)) + ("floralwhite", Color4f(255, 250, 240)) + ("forestgreen", Color4f(34, 139, 34)) + ("fuchsia", Color4f(255, 0, 255)) + ("gainsboro", Color4f(220, 220, 220)) + ("ghostwhite", Color4f(248, 248, 255)) + ("gold", Color4f(255, 215, 0)) + ("goldenrod", Color4f(218, 165, 32)) + ("gray", Color4f(128, 128, 128)) + ("green", Color4f(0, 128, 0)) + ("greenyellow", Color4f(173, 255, 47)) + ("grey", Color4f(128, 128, 128)) + ("honeydew", Color4f(240, 255, 240)) + ("hotpink", Color4f(255, 105, 180)) + ("indianred", Color4f(205, 92, 92)) + ("indigo", Color4f(75, 0, 130)) + ("ivory", Color4f(255, 255, 240)) + ("khaki", Color4f(240, 230, 140)) + ("lavender", Color4f(230, 230, 250)) + ("lavenderblush", Color4f(255, 240, 245)) + ("lawngreen", Color4f(124, 252, 0)) + ("lemonchiffon", Color4f(255, 250, 205)) + ("lightblue", Color4f(173, 216, 230)) + ("lightcoral", Color4f(240, 128, 128)) + ("lightcyan", Color4f(224, 255, 255)) + ("lightgoldenrodyellow", Color4f(250, 250, 210)) + ("lightgray", Color4f(211, 211, 211)) + ("lightgreen", Color4f(144, 238, 144)) + ("lightgrey", Color4f(211, 211, 211)) + ("lightpink", Color4f(255, 182, 193)) + ("lightsalmon", Color4f(255, 160, 122)) + ("lightseagreen", Color4f(32, 178, 170)) + ("lightskyblue", Color4f(135, 206, 250)) + ("lightslategray", Color4f(119, 136, 153)) + ("lightslategrey", Color4f(119, 136, 153)) + ("lightsteelblue", Color4f(176, 196, 222)) + ("lightyellow", Color4f(255, 255, 224)) + ("lime", Color4f(0, 255, 0)) + ("limegreen", Color4f(50, 205, 50)) + ("linen", Color4f(250, 240, 230)) + ("magenta", Color4f(255, 0, 255)) + ("maroon", Color4f(128, 0, 0)) + ("mediumaquamarine", Color4f(102, 205, 170)) + ("mediumblue", Color4f(0, 0, 205)) + ("mediumorchid", Color4f(186, 85, 211)) + ("mediumpurple", Color4f(147, 112, 219)) + ("mediumseagreen", Color4f(60, 179, 113)) + ("mediumslateblue", Color4f(123, 104, 238)) + ("mediumspringgreen", Color4f(0, 250, 154)) + ("mediumturquoise", Color4f(72, 209, 204)) + ("mediumvioletred", Color4f(199, 21, 133)) + ("midnightblue", Color4f(25, 25, 112)) + ("mintcream", Color4f(245, 255, 250)) + ("mistyrose", Color4f(255, 228, 225)) + ("moccasin", Color4f(255, 228, 181)) + ("navajowhite", Color4f(255, 222, 173)) + ("navy", Color4f(0, 0, 128)) + ("oldlace", Color4f(253, 245, 230)) + ("olive", Color4f(128, 128, 0)) + ("olivedrab", Color4f(107, 142, 35)) + ("orange", Color4f(255, 165, 0)) + ("orangered", Color4f(255, 69, 0)) + ("orchid", Color4f(218, 112, 214)) + ("palegoldenrod", Color4f(238, 232, 170)) + ("palegreen", Color4f(152, 251, 152)) + ("paleturquoise", Color4f(175, 238, 238)) + ("palevioletred", Color4f(219, 112, 147)) + ("papayawhip", Color4f(255, 239, 213)) + ("peachpuff", Color4f(255, 218, 185)) + ("peru", Color4f(205, 133, 63)) + ("pink", Color4f(255, 192, 203)) + ("plum", Color4f(221, 160, 221)) + ("powderblue", Color4f(176, 224, 230)) + ("purple", Color4f(128, 0, 128)) + ("red", Color4f(255, 0, 0)) + ("rosybrown", Color4f(188, 143, 143)) + ("royalblue", Color4f(65, 105, 225)) + ("saddlebrown", Color4f(139, 69, 19)) + ("salmon", Color4f(250, 128, 114)) + ("sandybrown", Color4f(244, 164, 96)) + ("seagreen", Color4f(46, 139, 87)) + ("seashell", Color4f(255, 245, 238)) + ("sienna", Color4f(160, 82, 45)) + ("silver", Color4f(192, 192, 192)) + ("skyblue", Color4f(135, 206, 235)) + ("slateblue", Color4f(106, 90, 205)) + ("slategray", Color4f(112, 128, 144)) + ("slategrey", Color4f(112, 128, 144)) + ("snow", Color4f(255, 250, 250)) + ("springgreen", Color4f(0, 255, 127)) + ("steelblue", Color4f(70, 130, 180)) + ("tan", Color4f(210, 180, 140)) + ("teal", Color4f(0, 128, 128)) + ("thistle", Color4f(216, 191, 216)) + ("tomato", Color4f(255, 99, 71)) + ("transparent", Color4f(0, 0, 0, 0)) + ("turquoise", Color4f(64, 224, 208)) + ("violet", Color4f(238, 130, 238)) + ("wheat", Color4f(245, 222, 179)) + ("white", Color4f(255, 255, 255)) + ("whitesmoke", Color4f(245, 245, 245)) + ("yellow", Color4f(255, 255, 0)) + ("yellowgreen", Color4f(154, 205, 50)); + + ColorScheme cornfield = map_list_of + (BACKGROUND_COLOR, Color4f(0xff, 0xff, 0xe5)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0x9d, 0xcb, 0x51)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (CGAL_FACE_BACK_COLOR, Color4f(0x9d, 0xcb, 0x51)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0xec, 0x5e)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xab, 0xd8, 0x56)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0x80, 0x00, 0x00)); + + ColorScheme metallic = map_list_of + (BACKGROUND_COLOR, Color4f(0xaa, 0xaa, 0xff)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xdd, 0xdd, 0xff)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0xdd, 0x22, 0xdd)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xdd, 0xdd, 0xff)) + (CGAL_FACE_BACK_COLOR, Color4f(0xdd, 0x22, 0xdd)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0x80, 0x00, 0x00)); + + ColorScheme sunset = map_list_of + (BACKGROUND_COLOR, Color4f(0xaa, 0x44, 0x44)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xff, 0xaa, 0xaa)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0x88, 0x22, 0x33)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xff, 0xaa, 0xaa)) + (CGAL_FACE_BACK_COLOR, Color4f(0x88, 0x22, 0x33)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0x80, 0x00, 0x00)); + + ColorScheme starnight = map_list_of + (BACKGROUND_COLOR, webcolors["black"]) + (OPENCSG_FACE_FRONT_COLOR, webcolors["lightyellow"]) + (OPENCSG_FACE_BACK_COLOR, webcolors["cyan"]) + (CGAL_FACE_FRONT_COLOR, webcolors["lightyellow"]) + (CGAL_FACE_BACK_COLOR, webcolors["cyan"]) + (CGAL_FACE_2D_COLOR, webcolors["mediumpurple"]) + (CGAL_EDGE_FRONT_COLOR, Color4f(0x00, 0x00, 0xff)) + (CGAL_EDGE_BACK_COLOR, Color4f(0x00, 0x00, 0xff)) + (CGAL_EDGE_2D_COLOR, webcolors["magenta"]) + (CROSSHAIR_COLOR, Color4f(0xf0, 0xf0, 0xf0)); + + ColorScheme beforedawn = map_list_of + (BACKGROUND_COLOR, Color4f(0x33, 0x33, 0x33)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xcc, 0xcc, 0xcc)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0x55, 0x63, 0xdd)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xcc, 0xcc, 0xcc)) + (CGAL_FACE_BACK_COLOR, Color4f(0x55, 0x63, 0xdd)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0xf0, 0xf0, 0xf0)); + + ColorScheme nature = map_list_of + (BACKGROUND_COLOR, Color4f(0xfa, 0xfa, 0xfa)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0x16, 0xa0, 0x85)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0xdb, 0xf4, 0xda)) + (CGAL_FACE_FRONT_COLOR, Color4f(0x16, 0xa0, 0x85)) + (CGAL_FACE_BACK_COLOR, Color4f(0xdb, 0xf4, 0xda)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0x11, 0x11, 0x11)); + + ColorScheme deepocean = map_list_of + (BACKGROUND_COLOR, Color4f(0x33, 0x33, 0x33)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xee, 0xee, 0xee)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0x0b, 0xab, 0xc8)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xee, 0xee, 0xee)) + (CGAL_FACE_BACK_COLOR, Color4f(0x0b, 0xab, 0xc8)) + (CGAL_FACE_2D_COLOR, webcolors["mediumpurple"]) + (CGAL_EDGE_FRONT_COLOR, Color4f(0x00, 0x00, 0xff)) + (CGAL_EDGE_BACK_COLOR, Color4f(0x00, 0x00, 0xff)) + (CGAL_EDGE_2D_COLOR, webcolors["magenta"]) + (CROSSHAIR_COLOR, Color4f(0xf0, 0xf0, 0xf0)); + + // Monotone - no difference between 'back face' and 'front face' + ColorScheme monotone = map_list_of + (BACKGROUND_COLOR, Color4f(0xff, 0xff, 0xe5)) + (OPENCSG_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (OPENCSG_FACE_BACK_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (CGAL_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (CGAL_FACE_BACK_COLOR, Color4f(0xf9, 0xd7, 0x2c)) + (CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)) + (CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_BACK_COLOR, Color4f(0xff, 0x00, 0x00)) + (CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)) + (CROSSHAIR_COLOR, Color4f(0x80, 0x00, 0x00)); + + colorschemes = map_list_of + ("Cornfield", cornfield) + ("Metallic", metallic) + ("Sunset", sunset) + ("Starnight", starnight) + ("BeforeDawn", beforedawn) + ("Nature", nature) + ("DeepOcean", deepocean) + ("Monotone", monotone); // Hidden, not in GUI + +} + +const ColorScheme &ColorMap::defaultColorScheme() const +{ + return colorschemes.at("Cornfield"); +} + +const ColorScheme *ColorMap::findColorScheme(const std::string &name) const +{ + if (colorschemes.find(name) != colorschemes.end()) return &colorschemes.at(name); + return NULL; +} + +std::list ColorMap::colorSchemeNames() const +{ + std::list names; + for (boost::unordered_map::const_iterator iter=colorschemes.begin(); iter!=colorschemes.end(); iter++) { + names.push_back(iter->first); + } + return names; +} + +Color4f ColorMap::getColor(const ColorScheme &cs, const RenderColor rc) +{ + if (cs.count(rc)) return cs.at(rc); + if (ColorMap::inst()->defaultColorScheme().count(rc)) return ColorMap::inst()->defaultColorScheme().at(rc); + return Color4f(0, 0, 0, 127); +} + +/* + void printcolorscheme(const ColorScheme &cs) + { + for (ColorScheme::const_iterator j=cs.begin();j!=cs.end();j++) + PRINTB("%i %s",j->first % j->second.transpose()); + } + + void printcolorschemes() + { + for (boost::unordered_map::const_iterator i=colorschemes.begin();i!=colorschemes.end();i++) { + PRINTB("-- %s --", i->first); + for (ColorScheme::const_iterator j=i->second.begin();j!=i->second.end();j++) + PRINTB("%i %s",j->first % j->second.transpose()); + } + } +*/ diff --git a/src/colormap.h b/src/colormap.h index 7c0652ca..9f305efd 100644 --- a/src/colormap.h +++ b/src/colormap.h @@ -1,149 +1,43 @@ -boost::unordered_map ColorModule::colormap = map_list_of - ("aliceblue", Color4f(240, 248, 255)) - ("antiquewhite", Color4f(250, 235, 215)) - ("aqua", Color4f(0, 255, 255)) - ("aquamarine", Color4f(127, 255, 212)) - ("azure", Color4f(240, 255, 255)) - ("beige", Color4f(245, 245, 220)) - ("bisque", Color4f(255, 228, 196)) - ("black", Color4f(0, 0, 0)) - ("blanchedalmond", Color4f(255, 235, 205)) - ("blue", Color4f(0, 0, 255)) - ("blueviolet", Color4f(138, 43, 226)) - ("brown", Color4f(165, 42, 42)) - ("burlywood", Color4f(222, 184, 135)) - ("cadetblue", Color4f(95, 158, 160)) - ("chartreuse", Color4f(127, 255, 0)) - ("chocolate", Color4f(210, 105, 30)) - ("coral", Color4f(255, 127, 80)) - ("cornflowerblue", Color4f(100, 149, 237)) - ("cornsilk", Color4f(255, 248, 220)) - ("crimson", Color4f(220, 20, 60)) - ("cyan", Color4f(0, 255, 255)) - ("darkblue", Color4f(0, 0, 139)) - ("darkcyan", Color4f(0, 139, 139)) - ("darkgoldenrod", Color4f(184, 134, 11)) - ("darkgray", Color4f(169, 169, 169)) - ("darkgreen", Color4f(0, 100, 0)) - ("darkgrey", Color4f(169, 169, 169)) - ("darkkhaki", Color4f(189, 183, 107)) - ("darkmagenta", Color4f(139, 0, 139)) - ("darkolivegreen", Color4f(85, 107, 47)) - ("darkorange", Color4f(255, 140, 0)) - ("darkorchid", Color4f(153, 50, 204)) - ("darkred", Color4f(139, 0, 0)) - ("darksalmon", Color4f(233, 150, 122)) - ("darkseagreen", Color4f(143, 188, 143)) - ("darkslateblue", Color4f(72, 61, 139)) - ("darkslategray", Color4f(47, 79, 79)) - ("darkslategrey", Color4f(47, 79, 79)) - ("darkturquoise", Color4f(0, 206, 209)) - ("darkviolet", Color4f(148, 0, 211)) - ("deeppink", Color4f(255, 20, 147)) - ("deepskyblue", Color4f(0, 191, 255)) - ("dimgray", Color4f(105, 105, 105)) - ("dimgrey", Color4f(105, 105, 105)) - ("dodgerblue", Color4f(30, 144, 255)) - ("firebrick", Color4f(178, 34, 34)) - ("floralwhite", Color4f(255, 250, 240)) - ("forestgreen", Color4f(34, 139, 34)) - ("fuchsia", Color4f(255, 0, 255)) - ("gainsboro", Color4f(220, 220, 220)) - ("ghostwhite", Color4f(248, 248, 255)) - ("gold", Color4f(255, 215, 0)) - ("goldenrod", Color4f(218, 165, 32)) - ("gray", Color4f(128, 128, 128)) - ("green", Color4f(0, 128, 0)) - ("greenyellow", Color4f(173, 255, 47)) - ("grey", Color4f(128, 128, 128)) - ("honeydew", Color4f(240, 255, 240)) - ("hotpink", Color4f(255, 105, 180)) - ("indianred", Color4f(205, 92, 92)) - ("indigo", Color4f(75, 0, 130)) - ("ivory", Color4f(255, 255, 240)) - ("khaki", Color4f(240, 230, 140)) - ("lavender", Color4f(230, 230, 250)) - ("lavenderblush", Color4f(255, 240, 245)) - ("lawngreen", Color4f(124, 252, 0)) - ("lemonchiffon", Color4f(255, 250, 205)) - ("lightblue", Color4f(173, 216, 230)) - ("lightcoral", Color4f(240, 128, 128)) - ("lightcyan", Color4f(224, 255, 255)) - ("lightgoldenrodyellow", Color4f(250, 250, 210)) - ("lightgray", Color4f(211, 211, 211)) - ("lightgreen", Color4f(144, 238, 144)) - ("lightgrey", Color4f(211, 211, 211)) - ("lightpink", Color4f(255, 182, 193)) - ("lightsalmon", Color4f(255, 160, 122)) - ("lightseagreen", Color4f(32, 178, 170)) - ("lightskyblue", Color4f(135, 206, 250)) - ("lightslategray", Color4f(119, 136, 153)) - ("lightslategrey", Color4f(119, 136, 153)) - ("lightsteelblue", Color4f(176, 196, 222)) - ("lightyellow", Color4f(255, 255, 224)) - ("lime", Color4f(0, 255, 0)) - ("limegreen", Color4f(50, 205, 50)) - ("linen", Color4f(250, 240, 230)) - ("magenta", Color4f(255, 0, 255)) - ("maroon", Color4f(128, 0, 0)) - ("mediumaquamarine", Color4f(102, 205, 170)) - ("mediumblue", Color4f(0, 0, 205)) - ("mediumorchid", Color4f(186, 85, 211)) - ("mediumpurple", Color4f(147, 112, 219)) - ("mediumseagreen", Color4f(60, 179, 113)) - ("mediumslateblue", Color4f(123, 104, 238)) - ("mediumspringgreen", Color4f(0, 250, 154)) - ("mediumturquoise", Color4f(72, 209, 204)) - ("mediumvioletred", Color4f(199, 21, 133)) - ("midnightblue", Color4f(25, 25, 112)) - ("mintcream", Color4f(245, 255, 250)) - ("mistyrose", Color4f(255, 228, 225)) - ("moccasin", Color4f(255, 228, 181)) - ("navajowhite", Color4f(255, 222, 173)) - ("navy", Color4f(0, 0, 128)) - ("oldlace", Color4f(253, 245, 230)) - ("olive", Color4f(128, 128, 0)) - ("olivedrab", Color4f(107, 142, 35)) - ("orange", Color4f(255, 165, 0)) - ("orangered", Color4f(255, 69, 0)) - ("orchid", Color4f(218, 112, 214)) - ("palegoldenrod", Color4f(238, 232, 170)) - ("palegreen", Color4f(152, 251, 152)) - ("paleturquoise", Color4f(175, 238, 238)) - ("palevioletred", Color4f(219, 112, 147)) - ("papayawhip", Color4f(255, 239, 213)) - ("peachpuff", Color4f(255, 218, 185)) - ("peru", Color4f(205, 133, 63)) - ("pink", Color4f(255, 192, 203)) - ("plum", Color4f(221, 160, 221)) - ("powderblue", Color4f(176, 224, 230)) - ("purple", Color4f(128, 0, 128)) - ("red", Color4f(255, 0, 0)) - ("rosybrown", Color4f(188, 143, 143)) - ("royalblue", Color4f(65, 105, 225)) - ("saddlebrown", Color4f(139, 69, 19)) - ("salmon", Color4f(250, 128, 114)) - ("sandybrown", Color4f(244, 164, 96)) - ("seagreen", Color4f(46, 139, 87)) - ("seashell", Color4f(255, 245, 238)) - ("sienna", Color4f(160, 82, 45)) - ("silver", Color4f(192, 192, 192)) - ("skyblue", Color4f(135, 206, 235)) - ("slateblue", Color4f(106, 90, 205)) - ("slategray", Color4f(112, 128, 144)) - ("slategrey", Color4f(112, 128, 144)) - ("snow", Color4f(255, 250, 250)) - ("springgreen", Color4f(0, 255, 127)) - ("steelblue", Color4f(70, 130, 180)) - ("tan", Color4f(210, 180, 140)) - ("teal", Color4f(0, 128, 128)) - ("thistle", Color4f(216, 191, 216)) - ("tomato", Color4f(255, 99, 71)) - ("transparent", Color4f(0, 0, 0, 0)) - ("turquoise", Color4f(64, 224, 208)) - ("violet", Color4f(238, 130, 238)) - ("wheat", Color4f(245, 222, 179)) - ("white", Color4f(255, 255, 255)) - ("whitesmoke", Color4f(245, 245, 245)) - ("yellow", Color4f(255, 255, 0)) - ("yellowgreen", Color4f(154, 205, 50)); +#pragma once + +#include +#include +#include +#include "linalg.h" +#include + +enum RenderColor { + BACKGROUND_COLOR, + OPENCSG_FACE_FRONT_COLOR, + OPENCSG_FACE_BACK_COLOR, + CGAL_FACE_FRONT_COLOR, + CGAL_FACE_2D_COLOR, + CGAL_FACE_BACK_COLOR, + CGAL_EDGE_FRONT_COLOR, + CGAL_EDGE_BACK_COLOR, + CGAL_EDGE_2D_COLOR, + CROSSHAIR_COLOR +}; + +typedef std::map ColorScheme; + +class ColorMap +{ +public: + static ColorMap *inst(bool erase = false); + + const ColorScheme &defaultColorScheme() const; + + const boost::unordered_map &webColors() const { return webcolors; } + const ColorScheme *findColorScheme(const std::string &name) const; + std::list colorSchemeNames() const; + + static Color4f getColor(const ColorScheme &cs, const RenderColor rc); + +private: + ColorMap(); + ~ColorMap() {} + + boost::unordered_map webcolors; + boost::unordered_map colorschemes; +}; diff --git a/src/export.cc b/src/export.cc index dcdb6676..f059f24d 100644 --- a/src/export.cc +++ b/src/export.cc @@ -134,14 +134,33 @@ 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) { - output << " facet normal 0 0 0\n"; - output << " outer loop\n"; assert(p.size() == 3); // STL only allows triangles - BOOST_FOREACH(const Vector3d &v, p) { - output << "vertex " << v[0] << " " << v[1] << " " << v[2] << "\n"; + std::stringstream stream; + stream << p[0][0] << " " << p[0][1] << " " << p[0][2]; + std::string vs1 = stream.str(); + stream.str(""); + stream << p[1][0] << " " << p[1][1] << " " << p[1][2]; + std::string vs2 = stream.str(); + stream.str(""); + stream << p[2][0] << " " << p[2][1] << " " << p[2][2]; + std::string vs3 = stream.str(); + if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) { + // The above condition ensures that there are 3 distinct vertices, but + // they may be collinear. If they are, the unit normal is meaningless + // so the default value of "1 0 0" can be used. If the vertices are not + // collinear then the unit normal must be calculated from the + // components. + Vector3d normal = (p[1] - p[0]).cross(p[2] - p[0]); + normal.normalize(); + output << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "\n"; + output << " outer loop\n"; + + BOOST_FOREACH(const Vector3d &v, p) { + output << " vertex " << v[0] << " " << v[1] << " " << v[2] << "\n"; + } + output << " endloop\n"; + output << " endfacet\n"; } - output << " endloop\n"; - output << " endfacet\n"; } output << "endsolid OpenSCAD_Model\n"; setlocale(LC_NUMERIC, ""); // Set default locale @@ -230,10 +249,10 @@ void export_stl(const CGAL_Nef_polyhedron *root_N, std::ostream &output) PRINT("Object isn't a valid 2-manifold! Modify your design.\n"); } - bool usePolySet = false; + bool usePolySet = true; if (usePolySet) { PolySet ps(3); - bool err = createPolySetFromNefPolyhedron3(*(root_N->p3), ps); + bool err = CGALUtils::createPolySetFromNefPolyhedron3(*(root_N->p3), ps); if (err) { PRINT("ERROR: Nef->PolySet failed"); } else { export_stl(ps, output); @@ -264,7 +283,7 @@ void export_stl(const CGAL_Nef_polyhedron *root_N, std::ostream &output) void export_off(const class PolySet &ps, std::ostream &output) { // FIXME: Implement this without creating a Nef polyhedron - CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(ps); + CGAL_Nef_polyhedron *N = CGALUtils::createNefPolyhedronFromGeometry(ps); export_off(N, output); delete N; } @@ -290,7 +309,7 @@ void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output) void export_amf(const class PolySet &ps, std::ostream &output) { // FIXME: Implement this without creating a Nef polyhedron - CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(ps); + CGAL_Nef_polyhedron *N = CGALUtils::createNefPolyhedronFromGeometry(ps); export_amf(N, output); delete N; } diff --git a/src/export_png.cc b/src/export_png.cc index befdda4e..d9c4b5a4 100644 --- a/src/export_png.cc +++ b/src/export_png.cc @@ -8,7 +8,6 @@ #ifdef ENABLE_CGAL #include "CGALRenderer.h" -#include "CGAL_renderer.h" #include "cgal.h" #include "cgalutils.h" #include "CGAL_Nef_polyhedron.h" @@ -38,6 +37,7 @@ void export_png(const Geometry *root_geom, Camera &cam, std::ostream &output) glview->setCamera(cam); glview->setRenderer(&cgalRenderer); + glview->setColorScheme(RenderSettings::inst()->colorscheme); glview->paintGL(); glview->save(output); } @@ -85,6 +85,7 @@ void export_png_preview_common(Tree &tree, Camera &cam, std::ostream &output, Pr OpenCSG::setContext(0); OpenCSG::setOption(OpenCSG::OffscreenSetting, OpenCSG::FrameBufferObject); #endif + csgInfo.glview->setColorScheme(RenderSettings::inst()->colorscheme); csgInfo.glview->paintGL(); csgInfo.glview->save(output); } diff --git a/src/func.cc b/src/func.cc index e481cc1e..f26f44c5 100644 --- a/src/func.cc +++ b/src/func.cc @@ -527,6 +527,17 @@ Value builtin_str(const Context *, const EvalContext *evalctx) return Value(stream.str()); } +Value builtin_chr(const Context *, const EvalContext *evalctx) +{ + std::stringstream stream; + + for (size_t i = 0; i < evalctx->numArgs(); i++) { + const Value v = evalctx->getArgValue(i); + stream << v.chrString(); + } + return Value(stream.str()); +} + Value builtin_concat(const Context *, const EvalContext *evalctx) { Value::VectorType result; @@ -928,6 +939,7 @@ void register_builtin_functions() Builtins::init("log", new BuiltinFunction(&builtin_log)); Builtins::init("ln", new BuiltinFunction(&builtin_ln)); Builtins::init("str", new BuiltinFunction(&builtin_str)); + Builtins::init("chr", new BuiltinFunction(&builtin_chr)); Builtins::init("concat", new BuiltinFunction(&builtin_concat)); Builtins::init("lookup", new BuiltinFunction(&builtin_lookup)); Builtins::init("search", new BuiltinFunction(&builtin_search)); diff --git a/src/import.cc b/src/import.cc index a6b61663..3841dc02 100644 --- a/src/import.cc +++ b/src/import.cc @@ -287,7 +287,7 @@ Geometry *ImportNode::createGeometry() const else { file >> poly; file.close(); - bool err = createPolySetFromPolyhedron(poly, *p); + bool err = CGALUtils::createPolySetFromPolyhedron(poly, *p); } #else PRINT("WARNING: OFF import requires CGAL."); diff --git a/src/linalg.h b/src/linalg.h index d43a28a5..69fe316f 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d) using Eigen::Vector2d; diff --git a/src/mainwin.cc b/src/mainwin.cc index 1af23c5f..7130175e 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -27,6 +27,7 @@ #include "ModuleCache.h" #include "MainWindow.h" #include "parsersettings.h" +#include "rendersettings.h" #include "Preferences.h" #include "printutils.h" #include "node.h" @@ -430,8 +431,13 @@ MainWindow::MainWindow(const QString &filename) this, SLOT(openCSGSettingsChanged())); connect(Preferences::inst(), SIGNAL(syntaxHighlightChanged(const QString&)), editor, SLOT(setHighlightScheme(const QString&))); + connect(Preferences::inst(), SIGNAL(colorSchemeChanged(const QString&)), + this, SLOT(setColorScheme(const QString&))); Preferences::inst()->apply(); + QString cs = Preferences::inst()->getValue("3dview/colorscheme").toString(); + this->setColorScheme(cs); + //find and replace panel connect(this->findTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFindType(int))); connect(this->findInputField, SIGNAL(textChanged(QString)), this, SLOT(findString(QString))); @@ -1811,6 +1817,12 @@ void MainWindow::actionExport(export_type_e, QString, QString) return; } + if (this->root_geom->isEmpty()) { + PRINT("Current top level object is empty."); + clearCurrentOutput(); + return; + } + const CGAL_Nef_polyhedron *N = dynamic_cast(this->root_geom.get()); if (N && !N->p3->is_simple()) { PRINT("Object isn't a valid 2-manifold! Modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export"); @@ -1938,7 +1950,7 @@ void MainWindow::actionExportCSG() return; } - std::ofstream fstream(csg_filename.toUtf8()); + std::ofstream fstream(csg_filename.toLocal8Bit()); if (!fstream.is_open()) { PRINTB("Can't open file \"%s\" for export", csg_filename.toLocal8Bit().constData()); } @@ -2000,6 +2012,7 @@ void MainWindow::viewModePreview() viewModeActionsUncheck(); viewActionPreview->setChecked(true); this->qglview->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } else { viewModeThrownTogether(); @@ -2016,6 +2029,7 @@ void MainWindow::viewModeSurface() viewActionSurfaces->setChecked(true); this->qglview->setShowFaces(true); this->qglview->setRenderer(this->cgalRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2025,6 +2039,7 @@ void MainWindow::viewModeWireframe() viewActionWireframe->setChecked(true); this->qglview->setShowFaces(false); this->qglview->setRenderer(this->cgalRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2035,6 +2050,7 @@ void MainWindow::viewModeThrownTogether() viewModeActionsUncheck(); viewActionThrownTogether->setChecked(true); this->qglview->setRenderer(this->thrownTogetherRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2373,6 +2389,23 @@ MainWindow::preferences() Preferences::inst()->raise(); } +void MainWindow::setColorScheme(const QString &scheme) +{ + RenderSettings::inst()->colorscheme = scheme.toStdString(); + this->qglview->setColorScheme(scheme.toStdString()); + this->qglview->updateGL(); +} + +void MainWindow::setFont(const QString &family, uint size) +{ + QFont font; + if (!family.isEmpty()) font.setFamily(family); + else font.setFixedPitch(true); + if (size > 0) font.setPointSize(size); + font.setStyleHint(QFont::TypeWriter); + editor->setFont(font); +} + void MainWindow::quit() { QCloseEvent ev; diff --git a/src/openscad.cc b/src/openscad.cc index 9c0fc6b6..c3d92c85 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -81,6 +81,7 @@ std::string currentdir; using std::string; using std::vector; using boost::lexical_cast; +using boost::bad_lexical_cast; using boost::is_any_of; class Echostream : public std::ofstream @@ -114,6 +115,7 @@ static void help(const char *progname) "%2%[ --viewall ] \\\n" "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" "%2%[ --render | --preview[=throwntogether] ] \\\n" + "%2%[ --colorscheme=[Cornfield|Sunset|Metallic|Starnight|BeforeDawn|Nature|DeepOcean] ] \\\n" "%2%[ --csglimit=num ]" #ifdef ENABLE_EXPERIMENTAL " [ --enable= ]" @@ -152,7 +154,7 @@ static void info() exit(0); } -Camera get_camera( po::variables_map vm ) +Camera get_camera(po::variables_map vm) { Camera camera; @@ -161,12 +163,16 @@ Camera get_camera( po::variables_map vm ) vector cam_parameters; split(strs, vm["camera"].as(), is_any_of(",")); if ( strs.size()==6 || strs.size()==7 ) { - BOOST_FOREACH(string &s, strs) - cam_parameters.push_back(lexical_cast(s)); - camera.setup( cam_parameters ); + try { + BOOST_FOREACH(string &s, strs) cam_parameters.push_back(lexical_cast(s)); + camera.setup(cam_parameters); + } + catch (bad_lexical_cast &) { + PRINT("Camera setup requires numbers as parameters"); + } } else { - PRINT("Camera setup requires either 7 numbers for Gimbal Camera\n"); - PRINT("or 6 numbers for Vector Camera\n"); + PRINT("Camera setup requires either 7 numbers for Gimbal Camera"); + PRINT("or 6 numbers for Vector Camera"); exit(1); } } @@ -201,11 +207,16 @@ Camera get_camera( po::variables_map vm ) vector strs; split(strs, vm["imgsize"].as(), is_any_of(",")); if ( strs.size() != 2 ) { - PRINT("Need 2 numbers for imgsize\n"); + PRINT("Need 2 numbers for imgsize"); exit(1); } else { - w = lexical_cast( strs[0] ); - h = lexical_cast( strs[1] ); + try { + w = lexical_cast(strs[0]); + h = lexical_cast(strs[1]); + } + catch (bad_lexical_cast &) { + PRINT("Need 2 numbers for imgsize"); + } } } camera.pixel_width = w; @@ -228,6 +239,10 @@ static bool checkAndExport(shared_ptr root_geom, unsigned nd, PRINTB("Current top level object is not a %dD object.", nd); return false; } + if (root_geom->isEmpty()) { + PRINT("Current top level object is empty."); + return false; + } exportFileByName(root_geom.get(), format, filename, filename); return true; } @@ -623,6 +638,7 @@ int main(int argc, char **argv) ("viewall", "adjust camera to fit object") ("imgsize", po::value(), "=width,height for exporting png") ("projection", po::value(), "(o)rtho or (p)erspective when exporting png") + ("colorscheme", po::value(), "colorscheme") ("debug", po::value(), "special debug info") ("o,o", po::value(), "out-file") ("s,s", po::value(), "stl-file") @@ -723,6 +739,19 @@ int main(int argc, char **argv) } #endif + if (vm.count("colorscheme")) { + std::string colorscheme = vm["colorscheme"].as(); + if (ColorMap::inst()->findColorScheme(colorscheme)) { + RenderSettings::inst()->colorscheme = colorscheme; + } else { + PRINT("Unknown color scheme. Valid schemes:"); + BOOST_FOREACH (const std::string &name, ColorMap::inst()->colorSchemeNames()) { + PRINT(name); + } + exit(1); + } + } + currentdir = boosty::stringy(fs::current_path()); Camera camera = get_camera(vm); diff --git a/src/polyset.cc b/src/polyset.cc index 0d811e4a..222bfcb2 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -239,6 +239,7 @@ static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const { + PRINTD("Polyset render"); bool mirrored = m.matrix().determinant() < 0; #ifdef ENABLE_OPENCSG if (shaderinfo) { diff --git a/src/primitives.cc b/src/primitives.cc index 73b7137e..33e59109 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -243,7 +243,7 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta node->faces = c.lookup_variable("faces"); if (node->faces.type() == Value::UNDEFINED) { // backwards compatible - node->faces = c.lookup_variable("triangles"); + node->faces = c.lookup_variable("triangles", true); if (node->faces.type() != Value::UNDEFINED) { printDeprecation("DEPRECATED: polyhedron(triangles=[]) will be removed in future releases. Use polyhedron(faces=[]) instead."); } diff --git a/src/renderer.cc b/src/renderer.cc index 2f64999b..3b7a2a8a 100644 --- a/src/renderer.cc +++ b/src/renderer.cc @@ -3,47 +3,46 @@ #include "Geometry.h" #include "polyset.h" #include "Polygon2d.h" +#include "colormap.h" +#include "printutils.h" bool Renderer::getColor(Renderer::ColorMode colormode, Color4f &col) const { - switch (colormode) { - case COLORMODE_NONE: - return false; - break; - case COLORMODE_MATERIAL: - col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); - break; - case COLORMODE_CUTOUT: - col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_BACK_COLOR); - break; - case COLORMODE_HIGHLIGHT: - col.setRgb(255, 81, 81, 128); - break; - case COLORMODE_BACKGROUND: - col.setRgb(180, 180, 180, 128); - break; - case COLORMODE_MATERIAL_EDGES: - col.setRgb(255, 236, 94); - break; - case COLORMODE_CUTOUT_EDGES: - col.setRgb(171, 216, 86); - break; - case COLORMODE_HIGHLIGHT_EDGES: - col.setRgb(255, 171, 86, 128); - break; - case COLORMODE_BACKGROUND_EDGES: - col.setRgb(150, 150, 150, 128); - break; - default: - return false; - break; + if (colormode==COLORMODE_NONE) return false; + if (colormap.count(colormode) > 0) { + col = colormap.at(colormode); + return true; } - return true; + return false; +} + +Renderer::Renderer() : colorscheme(NULL) +{ + PRINTD("Renderer() start"); + // Setup default colors + // The main colors, MATERIAL and CUTOUT, come from this object's + // colorscheme. Colorschemes don't currently hold information + // for Highlight/Background colors + // but it wouldn't be too hard to make them do so. + + // MATERIAL is set by this object's colorscheme + // CUTOUT is set by this object's colorscheme + colormap[COLORMODE_HIGHLIGHT] = Color4f(255, 81, 81, 128); + colormap[COLORMODE_BACKGROUND] = Color4f(180, 180, 180, 128); + // MATERIAL_EDGES is set by this object's colorscheme + // CUTOUT_EDGES is set by this object's colorscheme + colormap[COLORMODE_HIGHLIGHT_EDGES] = Color4f(255, 171, 86, 128); + colormap[COLORMODE_BACKGROUND_EDGES] = Color4f(150, 150, 150, 128); + + setColorScheme(ColorMap::inst()->defaultColorScheme()); + PRINTD("Renderer() end"); } void Renderer::setColor(const float color[4], GLint *shaderinfo) const { - Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); + PRINTD("setColor a"); + Color4f col; + getColor(COLORMODE_MATERIAL,col); float c[4] = {color[0], color[1], color[2], color[3]}; if (c[0] < 0) c[0] = col[0]; if (c[1] < 0) c[1] = col[1]; @@ -60,6 +59,7 @@ void Renderer::setColor(const float color[4], GLint *shaderinfo) const void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shaderinfo) const { + PRINTD("setColor b"); Color4f basecol; if (getColor(colormode, basecol)) { if (colormode == COLORMODE_BACKGROUND) { @@ -80,10 +80,25 @@ void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shader void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const { + PRINTD("setColor c"); float c[4] = {-1,-1,-1,-1}; setColor(colormode, c, shaderinfo); } +/* fill this->colormap with matching entries from the colorscheme. note +this does not change Highlight or Background colors as they are not +represented in the colorscheme (yet). Also edgecolors are currently the +same for CGAL & OpenCSG */ +void Renderer::setColorScheme(const ColorScheme &cs) { + PRINTD("setColorScheme"); + colormap[COLORMODE_MATERIAL] = ColorMap::getColor(cs, OPENCSG_FACE_FRONT_COLOR); + colormap[COLORMODE_CUTOUT] = ColorMap::getColor(cs, OPENCSG_FACE_BACK_COLOR); + colormap[COLORMODE_MATERIAL_EDGES] = ColorMap::getColor(cs, CGAL_EDGE_FRONT_COLOR); + colormap[COLORMODE_CUTOUT_EDGES] = ColorMap::getColor(cs, CGAL_EDGE_BACK_COLOR); + colormap[COLORMODE_EMPTY_SPACE] = ColorMap::getColor(cs, BACKGROUND_COLOR); + this->colorscheme = &cs; +} + void Renderer::render_surface(shared_ptr geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) { shared_ptr ps; diff --git a/src/renderer.h b/src/renderer.h index 992ef347..d8498607 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,6 +3,7 @@ #include "system-gl.h" #include "linalg.h" #include "memory.h" +#include "colormap.h" #ifdef _MSC_VER // NULL #include @@ -11,6 +12,7 @@ class Renderer { public: + Renderer(); virtual ~Renderer() {} virtual void draw(bool showfaces, bool showedges) const = 0; virtual BoundingBox getBoundingBox() const = 0; @@ -35,14 +37,20 @@ public: COLORMODE_MATERIAL_EDGES, COLORMODE_CUTOUT_EDGES, COLORMODE_HIGHLIGHT_EDGES, - COLORMODE_BACKGROUND_EDGES + COLORMODE_BACKGROUND_EDGES, + COLORMODE_EMPTY_SPACE }; virtual bool getColor(ColorMode colormode, Color4f &col) const; virtual void setColor(const float color[4], GLint *shaderinfo = NULL) const; virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const; virtual void setColor(ColorMode colormode, const float color[4], GLint *shaderinfo = NULL) const; + virtual void setColorScheme(const ColorScheme &cs); static void render_surface(shared_ptr geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL); static void render_edges(shared_ptr geom, csgmode_e csgmode); + +protected: + std::map colormap; + const ColorScheme *colorscheme; }; diff --git a/src/rendersettings.cc b/src/rendersettings.cc index ba68dc5d..b3eb86b2 100644 --- a/src/rendersettings.cc +++ b/src/rendersettings.cc @@ -1,4 +1,6 @@ #include "rendersettings.h" +#include "colormap.h" +#include "printutils.h" RenderSettings *RenderSettings::inst(bool erase) { @@ -16,24 +18,5 @@ RenderSettings::RenderSettings() far_gl_clip_limit = 100000.0; img_width = 512; img_height = 512; - this->colors[BACKGROUND_COLOR] = Color4f(0xff, 0xff, 0xe5); - this->colors[OPENCSG_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colors[OPENCSG_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colors[CGAL_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colors[CGAL_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colors[CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colors[CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); -} - -Color4f RenderSettings::color(RenderColor idx) -{ - return this->colors[idx]; -} - -void RenderSettings::setColors(const std::map &colors) -{ - this->colors = colors; + colorscheme = "Cornfield"; } diff --git a/src/rendersettings.h b/src/rendersettings.h index e0be8174..09be9ded 100644 --- a/src/rendersettings.h +++ b/src/rendersettings.h @@ -2,33 +2,17 @@ #include #include "linalg.h" +#include "colormap.h" class RenderSettings { public: static RenderSettings *inst(bool erase = false); - enum RenderColor { - BACKGROUND_COLOR, - OPENCSG_FACE_FRONT_COLOR, - OPENCSG_FACE_BACK_COLOR, - CGAL_FACE_FRONT_COLOR, - CGAL_FACE_2D_COLOR, - CGAL_FACE_BACK_COLOR, - CGAL_EDGE_FRONT_COLOR, - CGAL_EDGE_BACK_COLOR, - CGAL_EDGE_2D_COLOR, - CROSSHAIR_COLOR - }; - - void setColors(const std::map &colors); - Color4f color(RenderColor idx); - unsigned int openCSGTermLimit, img_width, img_height; double far_gl_clip_limit; + std::string colorscheme; private: RenderSettings(); ~RenderSettings() {} - - std::map colors; }; diff --git a/src/typedefs.h b/src/typedefs.h index a7516ca6..56cd5e8a 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -2,13 +2,14 @@ #include #include +#include #include class Assignment : public std::pair > { public: - Assignment(std::string name) : pair(name, boost::shared_ptr()) {} - Assignment(std::string name, boost::shared_ptr expr) : pair(name, expr) {} + Assignment(std::string name) { first = name; second = boost::shared_ptr(); } + Assignment(std::string name, boost::shared_ptr expr) { first = name; second = expr; } }; typedef std::vector AssignmentList; diff --git a/src/value.cc b/src/value.cc index f3f6189e..56c591be 100644 --- a/src/value.cc +++ b/src/value.cc @@ -248,6 +248,58 @@ std::string Value::toString() const return boost::apply_visitor(tostring_visitor(), this->value); } +class chr_visitor : public boost::static_visitor { +public: + template std::string operator()(const S &) const + { + return ""; + } + + std::string operator()(const double &v) const + { + char buf[8]; + memset(buf, 0, 8); + if (v > 0) { + const gunichar c = v; + if (g_unichar_validate(c) && (c != 0)) { + g_unichar_to_utf8(c, buf); + } + } + return std::string(buf); + } + + std::string operator()(const Value::VectorType &v) const + { + std::stringstream stream; + for (size_t i = 0; i < v.size(); i++) { + stream << v[i].chrString(); + } + return stream.str(); + } + + std::string operator()(const Value::RangeType &v) const + { + const boost::uint32_t steps = v.nbsteps(); + if (steps >= 10000) { + PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); + return ""; + } + + std::stringstream stream; + Value::RangeType range = v; + for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { + const Value value(*it); + stream << value.chrString(); + } + return stream.str(); + } +}; + +std::string Value::chrString() const +{ + return boost::apply_visitor(chr_visitor(), this->value); +} + const Value::VectorType &Value::toVector() const { static VectorType empty; diff --git a/src/value.h b/src/value.h index d3204d31..6c59ef2c 100644 --- a/src/value.h +++ b/src/value.h @@ -87,6 +87,7 @@ public: /// return number of steps, max uint32_t value if step is 0 boost::uint32_t nbsteps() const; + friend class chr_visitor; friend class tostring_visitor; friend class bracket_visitor; }; @@ -121,6 +122,7 @@ public: bool getDouble(double &v) const; bool toBool() const; std::string toString() const; + std::string chrString() const; const VectorType &toVector() const; bool getVec2(double &x, double &y) const; bool getVec3(double &x, double &y, double &z, double defaultval = 0.0) const; diff --git a/testdata/scad/features/circle-tests.scad b/testdata/scad/2D/features/circle-tests.scad similarity index 100% rename from testdata/scad/features/circle-tests.scad rename to testdata/scad/2D/features/circle-tests.scad diff --git a/testdata/scad/features/control-hull-dimension.scad b/testdata/scad/2D/features/control-hull-dimension.scad similarity index 100% rename from testdata/scad/features/control-hull-dimension.scad rename to testdata/scad/2D/features/control-hull-dimension.scad diff --git a/testdata/scad/features/difference-2d-tests.scad b/testdata/scad/2D/features/difference-2d-tests.scad similarity index 100% rename from testdata/scad/features/difference-2d-tests.scad rename to testdata/scad/2D/features/difference-2d-tests.scad diff --git a/testdata/scad/features/hull2-tests.scad b/testdata/scad/2D/features/hull2-tests.scad similarity index 100% rename from testdata/scad/features/hull2-tests.scad rename to testdata/scad/2D/features/hull2-tests.scad diff --git a/testdata/scad/features/intersection2-tests.scad b/testdata/scad/2D/features/intersection2-tests.scad similarity index 100% rename from testdata/scad/features/intersection2-tests.scad rename to testdata/scad/2D/features/intersection2-tests.scad diff --git a/testdata/scad/bugs/issue612.scad b/testdata/scad/2D/features/minkowski2-crack.scad similarity index 100% rename from testdata/scad/bugs/issue612.scad rename to testdata/scad/2D/features/minkowski2-crack.scad diff --git a/testdata/scad/features/minkowski2-hole-tests.scad b/testdata/scad/2D/features/minkowski2-hole-tests.scad similarity index 100% rename from testdata/scad/features/minkowski2-hole-tests.scad rename to testdata/scad/2D/features/minkowski2-hole-tests.scad diff --git a/testdata/scad/features/minkowski2-tests.scad b/testdata/scad/2D/features/minkowski2-tests.scad similarity index 100% rename from testdata/scad/features/minkowski2-tests.scad rename to testdata/scad/2D/features/minkowski2-tests.scad diff --git a/testdata/scad/features/offset-tests.scad b/testdata/scad/2D/features/offset-tests.scad similarity index 100% rename from testdata/scad/features/offset-tests.scad rename to testdata/scad/2D/features/offset-tests.scad diff --git a/testdata/scad/features/polygon-tests.scad b/testdata/scad/2D/features/polygon-tests.scad similarity index 100% rename from testdata/scad/features/polygon-tests.scad rename to testdata/scad/2D/features/polygon-tests.scad diff --git a/testdata/scad/bugs/issue585.scad b/testdata/scad/2D/features/polygons-touch.scad similarity index 100% rename from testdata/scad/bugs/issue585.scad rename to testdata/scad/2D/features/polygons-touch.scad diff --git a/testdata/scad/features/projection-cut-tests.scad b/testdata/scad/2D/features/projection-cut-tests.scad similarity index 100% rename from testdata/scad/features/projection-cut-tests.scad rename to testdata/scad/2D/features/projection-cut-tests.scad diff --git a/testdata/scad/features/projection-tests.scad b/testdata/scad/2D/features/projection-tests.scad similarity index 100% rename from testdata/scad/features/projection-tests.scad rename to testdata/scad/2D/features/projection-tests.scad diff --git a/testdata/scad/features/render-2d-tests.scad b/testdata/scad/2D/features/render-2d-tests.scad similarity index 100% rename from testdata/scad/features/render-2d-tests.scad rename to testdata/scad/2D/features/render-2d-tests.scad diff --git a/testdata/scad/features/resize-2d-tests.scad b/testdata/scad/2D/features/resize-2d-tests.scad similarity index 100% rename from testdata/scad/features/resize-2d-tests.scad rename to testdata/scad/2D/features/resize-2d-tests.scad diff --git a/testdata/scad/features/scale2D-tests.scad b/testdata/scad/2D/features/scale2D-tests.scad similarity index 100% rename from testdata/scad/features/scale2D-tests.scad rename to testdata/scad/2D/features/scale2D-tests.scad diff --git a/testdata/scad/features/square-tests.scad b/testdata/scad/2D/features/square-tests.scad similarity index 100% rename from testdata/scad/features/square-tests.scad rename to testdata/scad/2D/features/square-tests.scad diff --git a/testdata/scad/3D/deprecated/import_stl-test.scad b/testdata/scad/3D/deprecated/import_stl-test.scad new file mode 100644 index 00000000..b634d12e --- /dev/null +++ b/testdata/scad/3D/deprecated/import_stl-test.scad @@ -0,0 +1 @@ +import_stl("import.stl"); diff --git a/testdata/scad/features/rotate_extrude_dxf-tests.scad b/testdata/scad/3D/deprecated/rotate_extrude_dxf-tests.scad similarity index 63% rename from testdata/scad/features/rotate_extrude_dxf-tests.scad rename to testdata/scad/3D/deprecated/rotate_extrude_dxf-tests.scad index aacde690..c2d7b427 100644 --- a/testdata/scad/features/rotate_extrude_dxf-tests.scad +++ b/testdata/scad/3D/deprecated/rotate_extrude_dxf-tests.scad @@ -1,4 +1,4 @@ // These are tests which are not yet possible to express with the // non-deprecated functionality -rotate_extrude(file = "../../dxf/open-polyline.dxf"); +rotate_extrude(file = "../../../dxf/open-polyline.dxf"); diff --git a/testdata/scad/features/2d-3d.scad b/testdata/scad/3D/features/2d-3d.scad similarity index 100% rename from testdata/scad/features/2d-3d.scad rename to testdata/scad/3D/features/2d-3d.scad diff --git a/testdata/scad/features/assign-tests.scad b/testdata/scad/3D/features/assign-tests.scad similarity index 100% rename from testdata/scad/features/assign-tests.scad rename to testdata/scad/3D/features/assign-tests.scad diff --git a/testdata/scad/features/background-modifier.scad b/testdata/scad/3D/features/background-modifier.scad similarity index 100% rename from testdata/scad/features/background-modifier.scad rename to testdata/scad/3D/features/background-modifier.scad diff --git a/testdata/scad/features/camera-tests.scad b/testdata/scad/3D/features/camera-tests.scad similarity index 100% rename from testdata/scad/features/camera-tests.scad rename to testdata/scad/3D/features/camera-tests.scad diff --git a/testdata/scad/features/child-background.scad b/testdata/scad/3D/features/child-background.scad similarity index 100% rename from testdata/scad/features/child-background.scad rename to testdata/scad/3D/features/child-background.scad diff --git a/testdata/scad/features/child-child-test.scad b/testdata/scad/3D/features/child-child-test.scad similarity index 100% rename from testdata/scad/features/child-child-test.scad rename to testdata/scad/3D/features/child-child-test.scad diff --git a/testdata/scad/features/child-tests.scad b/testdata/scad/3D/features/child-tests.scad similarity index 100% rename from testdata/scad/features/child-tests.scad rename to testdata/scad/3D/features/child-tests.scad diff --git a/testdata/scad/features/color-tests.scad b/testdata/scad/3D/features/color-tests.scad similarity index 100% rename from testdata/scad/features/color-tests.scad rename to testdata/scad/3D/features/color-tests.scad diff --git a/testdata/scad/features/cube-tests.scad b/testdata/scad/3D/features/cube-tests.scad similarity index 100% rename from testdata/scad/features/cube-tests.scad rename to testdata/scad/3D/features/cube-tests.scad diff --git a/testdata/scad/features/cylinder-diameter-tests.scad b/testdata/scad/3D/features/cylinder-diameter-tests.scad similarity index 100% rename from testdata/scad/features/cylinder-diameter-tests.scad rename to testdata/scad/3D/features/cylinder-diameter-tests.scad diff --git a/testdata/scad/features/cylinder-tests.scad b/testdata/scad/3D/features/cylinder-tests.scad similarity index 100% rename from testdata/scad/features/cylinder-tests.scad rename to testdata/scad/3D/features/cylinder-tests.scad diff --git a/testdata/scad/features/difference-tests.scad b/testdata/scad/3D/features/difference-tests.scad similarity index 100% rename from testdata/scad/features/difference-tests.scad rename to testdata/scad/3D/features/difference-tests.scad diff --git a/testdata/scad/features/disable-modifier.scad b/testdata/scad/3D/features/disable-modifier.scad similarity index 100% rename from testdata/scad/features/disable-modifier.scad rename to testdata/scad/3D/features/disable-modifier.scad diff --git a/testdata/scad/features/for-nested-tests.scad b/testdata/scad/3D/features/for-nested-tests.scad similarity index 100% rename from testdata/scad/features/for-nested-tests.scad rename to testdata/scad/3D/features/for-nested-tests.scad diff --git a/testdata/scad/features/for-tests.scad b/testdata/scad/3D/features/for-tests.scad similarity index 100% rename from testdata/scad/features/for-tests.scad rename to testdata/scad/3D/features/for-tests.scad diff --git a/testdata/scad/features/highlight-and-background-modifier.scad b/testdata/scad/3D/features/highlight-and-background-modifier.scad similarity index 100% rename from testdata/scad/features/highlight-and-background-modifier.scad rename to testdata/scad/3D/features/highlight-and-background-modifier.scad diff --git a/testdata/scad/features/highlight-modifier.scad b/testdata/scad/3D/features/highlight-modifier.scad similarity index 100% rename from testdata/scad/features/highlight-modifier.scad rename to testdata/scad/3D/features/highlight-modifier.scad diff --git a/testdata/scad/features/hull3-tests.scad b/testdata/scad/3D/features/hull3-tests.scad similarity index 100% rename from testdata/scad/features/hull3-tests.scad rename to testdata/scad/3D/features/hull3-tests.scad diff --git a/testdata/scad/features/ifelse-tests.scad b/testdata/scad/3D/features/ifelse-tests.scad similarity index 100% rename from testdata/scad/features/ifelse-tests.scad rename to testdata/scad/3D/features/ifelse-tests.scad diff --git a/testdata/scad/features/import-empty-tests.scad b/testdata/scad/3D/features/import-empty-tests.scad similarity index 100% rename from testdata/scad/features/import-empty-tests.scad rename to testdata/scad/3D/features/import-empty-tests.scad diff --git a/testdata/scad/features/import.stl b/testdata/scad/3D/features/import.stl similarity index 100% rename from testdata/scad/features/import.stl rename to testdata/scad/3D/features/import.stl diff --git a/testdata/scad/features/import_bin.stl b/testdata/scad/3D/features/import_bin.stl similarity index 100% rename from testdata/scad/features/import_bin.stl rename to testdata/scad/3D/features/import_bin.stl diff --git a/testdata/scad/features/import_bin_solid.stl b/testdata/scad/3D/features/import_bin_solid.stl similarity index 100% rename from testdata/scad/features/import_bin_solid.stl rename to testdata/scad/3D/features/import_bin_solid.stl diff --git a/testdata/scad/features/intersection-tests.scad b/testdata/scad/3D/features/intersection-tests.scad similarity index 100% rename from testdata/scad/features/intersection-tests.scad rename to testdata/scad/3D/features/intersection-tests.scad diff --git a/testdata/scad/features/intersection_for-tests.scad b/testdata/scad/3D/features/intersection_for-tests.scad similarity index 100% rename from testdata/scad/features/intersection_for-tests.scad rename to testdata/scad/3D/features/intersection_for-tests.scad diff --git a/testdata/scad/features/linear_extrude-scale-zero-tests.scad b/testdata/scad/3D/features/linear_extrude-scale-zero-tests.scad similarity index 100% rename from testdata/scad/features/linear_extrude-scale-zero-tests.scad rename to testdata/scad/3D/features/linear_extrude-scale-zero-tests.scad diff --git a/testdata/scad/features/linear_extrude-tests.scad b/testdata/scad/3D/features/linear_extrude-tests.scad similarity index 100% rename from testdata/scad/features/linear_extrude-tests.scad rename to testdata/scad/3D/features/linear_extrude-tests.scad diff --git a/testdata/scad/features/minkowski3-erosion.scad b/testdata/scad/3D/features/minkowski3-erosion.scad similarity index 91% rename from testdata/scad/features/minkowski3-erosion.scad rename to testdata/scad/3D/features/minkowski3-erosion.scad index e49e93c5..52cce8db 100644 --- a/testdata/scad/features/minkowski3-erosion.scad +++ b/testdata/scad/3D/features/minkowski3-erosion.scad @@ -1,7 +1,7 @@ module erode(r) { difference() { children(); - minkowski() { + minkowski(convexity=3) { difference() { cube(25, center=true); children(); diff --git a/testdata/scad/features/minkowski3-tests.scad b/testdata/scad/3D/features/minkowski3-tests.scad similarity index 100% rename from testdata/scad/features/minkowski3-tests.scad rename to testdata/scad/3D/features/minkowski3-tests.scad diff --git a/testdata/scad/features/module-recursion.scad b/testdata/scad/3D/features/module-recursion.scad similarity index 100% rename from testdata/scad/features/module-recursion.scad rename to testdata/scad/3D/features/module-recursion.scad diff --git a/testdata/scad/features/modulevariables.scad b/testdata/scad/3D/features/modulevariables.scad similarity index 100% rename from testdata/scad/features/modulevariables.scad rename to testdata/scad/3D/features/modulevariables.scad diff --git a/testdata/scad/features/polyhedron-nonplanar-tests.scad b/testdata/scad/3D/features/polyhedron-nonplanar-tests.scad similarity index 100% rename from testdata/scad/features/polyhedron-nonplanar-tests.scad rename to testdata/scad/3D/features/polyhedron-nonplanar-tests.scad diff --git a/testdata/scad/features/polyhedron-tests.scad b/testdata/scad/3D/features/polyhedron-tests.scad similarity index 100% rename from testdata/scad/features/polyhedron-tests.scad rename to testdata/scad/3D/features/polyhedron-tests.scad diff --git a/testdata/scad/features/primitive-inf-tests.scad b/testdata/scad/3D/features/primitive-inf-tests.scad similarity index 100% rename from testdata/scad/features/primitive-inf-tests.scad rename to testdata/scad/3D/features/primitive-inf-tests.scad diff --git a/testdata/scad/features/projection-extrude-tests.scad b/testdata/scad/3D/features/projection-extrude-tests.scad similarity index 100% rename from testdata/scad/features/projection-extrude-tests.scad rename to testdata/scad/3D/features/projection-extrude-tests.scad diff --git a/testdata/scad/features/render-tests.scad b/testdata/scad/3D/features/render-tests.scad similarity index 100% rename from testdata/scad/features/render-tests.scad rename to testdata/scad/3D/features/render-tests.scad diff --git a/testdata/scad/features/resize-tests.scad b/testdata/scad/3D/features/resize-tests.scad similarity index 100% rename from testdata/scad/features/resize-tests.scad rename to testdata/scad/3D/features/resize-tests.scad diff --git a/testdata/scad/features/root-modifier.scad b/testdata/scad/3D/features/root-modifier.scad similarity index 100% rename from testdata/scad/features/root-modifier.scad rename to testdata/scad/3D/features/root-modifier.scad diff --git a/testdata/scad/features/rotate_extrude-tests.scad b/testdata/scad/3D/features/rotate_extrude-tests.scad similarity index 100% rename from testdata/scad/features/rotate_extrude-tests.scad rename to testdata/scad/3D/features/rotate_extrude-tests.scad diff --git a/testdata/scad/features/scale3D-tests.scad b/testdata/scad/3D/features/scale3D-tests.scad similarity index 100% rename from testdata/scad/features/scale3D-tests.scad rename to testdata/scad/3D/features/scale3D-tests.scad diff --git a/testdata/scad/features/sphere-tests.scad b/testdata/scad/3D/features/sphere-tests.scad similarity index 100% rename from testdata/scad/features/sphere-tests.scad rename to testdata/scad/3D/features/sphere-tests.scad diff --git a/testdata/scad/3D/features/surface-png-image-tests.scad b/testdata/scad/3D/features/surface-png-image-tests.scad new file mode 100644 index 00000000..dce2cbb3 --- /dev/null +++ b/testdata/scad/3D/features/surface-png-image-tests.scad @@ -0,0 +1 @@ +surface("../../../image/black-white-gradient.png"); diff --git a/testdata/scad/3D/features/surface-png-image2-tests.scad b/testdata/scad/3D/features/surface-png-image2-tests.scad new file mode 100644 index 00000000..d6f4ed44 --- /dev/null +++ b/testdata/scad/3D/features/surface-png-image2-tests.scad @@ -0,0 +1,2 @@ +scale([1, 1, .2]) + surface("../../../image/smiley.png", invert=false); diff --git a/testdata/scad/3D/features/surface-png-image3-tests.scad b/testdata/scad/3D/features/surface-png-image3-tests.scad new file mode 100644 index 00000000..a8088912 --- /dev/null +++ b/testdata/scad/3D/features/surface-png-image3-tests.scad @@ -0,0 +1,2 @@ +scale([1, 1, .2]) + surface("../../../image/smiley.png", invert=true); diff --git a/testdata/scad/features/surface-simple.dat b/testdata/scad/3D/features/surface-simple.dat similarity index 100% rename from testdata/scad/features/surface-simple.dat rename to testdata/scad/3D/features/surface-simple.dat diff --git a/testdata/scad/features/surface-simple.scad b/testdata/scad/3D/features/surface-simple.scad similarity index 100% rename from testdata/scad/features/surface-simple.scad rename to testdata/scad/3D/features/surface-simple.scad diff --git a/testdata/scad/features/surface-simple2.dat b/testdata/scad/3D/features/surface-simple2.dat similarity index 100% rename from testdata/scad/features/surface-simple2.dat rename to testdata/scad/3D/features/surface-simple2.dat diff --git a/testdata/scad/features/surface-tests.scad b/testdata/scad/3D/features/surface-tests.scad similarity index 100% rename from testdata/scad/features/surface-tests.scad rename to testdata/scad/3D/features/surface-tests.scad diff --git a/testdata/scad/features/surface.dat b/testdata/scad/3D/features/surface.dat similarity index 100% rename from testdata/scad/features/surface.dat rename to testdata/scad/3D/features/surface.dat diff --git a/testdata/scad/features/testcolornames.scad b/testdata/scad/3D/features/testcolornames.scad similarity index 100% rename from testdata/scad/features/testcolornames.scad rename to testdata/scad/3D/features/testcolornames.scad diff --git a/testdata/scad/features/transform-tests.scad b/testdata/scad/3D/features/transform-tests.scad similarity index 100% rename from testdata/scad/features/transform-tests.scad rename to testdata/scad/3D/features/transform-tests.scad diff --git a/testdata/scad/features/union-coincident-test.scad b/testdata/scad/3D/features/union-coincident-test.scad similarity index 100% rename from testdata/scad/features/union-coincident-test.scad rename to testdata/scad/3D/features/union-coincident-test.scad diff --git a/testdata/scad/features/union-tests.scad b/testdata/scad/3D/features/union-tests.scad similarity index 100% rename from testdata/scad/features/union-tests.scad rename to testdata/scad/3D/features/union-tests.scad diff --git a/testdata/scad/bugs/issue911.scad b/testdata/scad/bugs/issue911.scad new file mode 100644 index 00000000..9918f308 --- /dev/null +++ b/testdata/scad/bugs/issue911.scad @@ -0,0 +1,13 @@ +// Doing a minkowski sum on an object with +// an internal cavity causes rendering artifacts +// in preview mode (similar to convexity issues) +difference() { + minkowski(convexity=2) { + difference() { + cube(19, center = true); + cube(11, center = true); + } + cube(1, center=true); + } + translate([0, 0, 15]) cube(30, center=true); +} diff --git a/testdata/scad/bugs/issue913.scad b/testdata/scad/bugs/issue913.scad new file mode 100644 index 00000000..4972ab7d --- /dev/null +++ b/testdata/scad/bugs/issue913.scad @@ -0,0 +1,15 @@ +module erode(r) { + difference() { + children(); + minkowski(convexity=2) { + difference() { + cube(14, center=true); + children(); + } + cube(2*r, center=true); + } + } +} + +erode(2) cube(10, center=true); + diff --git a/testdata/scad/features/surface-png-image-tests.scad b/testdata/scad/features/surface-png-image-tests.scad deleted file mode 100644 index f5ca3ccb..00000000 --- a/testdata/scad/features/surface-png-image-tests.scad +++ /dev/null @@ -1 +0,0 @@ -surface("../../image/black-white-gradient.png"); diff --git a/testdata/scad/features/surface-png-image2-tests.scad b/testdata/scad/features/surface-png-image2-tests.scad deleted file mode 100644 index 7fb304b4..00000000 --- a/testdata/scad/features/surface-png-image2-tests.scad +++ /dev/null @@ -1,2 +0,0 @@ -scale([1, 1, .2]) - surface("../../image/smiley.png", invert=false); diff --git a/testdata/scad/features/surface-png-image3-tests.scad b/testdata/scad/features/surface-png-image3-tests.scad deleted file mode 100644 index 65f4ea7a..00000000 --- a/testdata/scad/features/surface-png-image3-tests.scad +++ /dev/null @@ -1,2 +0,0 @@ -scale([1, 1, .2]) - surface("../../image/smiley.png", invert=true); diff --git a/testdata/scad/bugs/issue541.scad b/testdata/scad/misc/bad-stl-pcbvicebar.scad similarity index 86% rename from testdata/scad/bugs/issue541.scad rename to testdata/scad/misc/bad-stl-pcbvicebar.scad index 5af62ba8..51815263 100644 --- a/testdata/scad/bugs/issue541.scad +++ b/testdata/scad/misc/bad-stl-pcbvicebar.scad @@ -6,4 +6,4 @@ ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. This has been fixed, but keep this test for future reference */ -render() import("issue541.stl"); +render() import("bad-stl-pcbvicebar.stl"); diff --git a/testdata/scad/bugs/issue541.stl b/testdata/scad/misc/bad-stl-pcbvicebar.stl similarity index 100% rename from testdata/scad/bugs/issue541.stl rename to testdata/scad/misc/bad-stl-pcbvicebar.stl diff --git a/testdata/scad/bugs/issue578.scad b/testdata/scad/misc/bad-stl-tardis.scad similarity index 93% rename from testdata/scad/bugs/issue578.scad rename to testdata/scad/misc/bad-stl-tardis.scad index a92e4f9c..43261f51 100644 --- a/testdata/scad/bugs/issue578.scad +++ b/testdata/scad/misc/bad-stl-tardis.scad @@ -12,4 +12,4 @@ Refer to the bug-reporting instructions at http://www.cgal.org/bug_report.html This has been fixed, but keep this test for future reference */ -render() import("issue578.stl"); +render() import("bad-stl-tardis.stl"); diff --git a/testdata/scad/bugs/issue578.stl b/testdata/scad/misc/bad-stl-tardis.stl similarity index 100% rename from testdata/scad/bugs/issue578.stl rename to testdata/scad/misc/bad-stl-tardis.stl diff --git a/testdata/scad/bugs/issue578b.scad b/testdata/scad/misc/bad-stl-wing.scad similarity index 91% rename from testdata/scad/bugs/issue578b.scad rename to testdata/scad/misc/bad-stl-wing.scad index 6951a3c2..bf8e4a58 100644 --- a/testdata/scad/bugs/issue578b.scad +++ b/testdata/scad/misc/bad-stl-wing.scad @@ -10,4 +10,4 @@ Refer to the bug-reporting instructions at http://www.cgal.org/bug_report.html This has been fixed, but keep this test for future reference */ -render() import("issue578b.stl"); +render() import("bad-stl-wing.stl"); diff --git a/testdata/scad/bugs/issue578b.stl b/testdata/scad/misc/bad-stl-wing.stl similarity index 100% rename from testdata/scad/bugs/issue578b.stl rename to testdata/scad/misc/bad-stl-wing.stl diff --git a/testdata/scad/misc/chr-tests.scad b/testdata/scad/misc/chr-tests.scad new file mode 100644 index 00000000..d8a6f983 --- /dev/null +++ b/testdata/scad/misc/chr-tests.scad @@ -0,0 +1,24 @@ +function f(x) = x; +s = "string"; +smiley = 9786; + +// empty, inf, -inf, nan +echo(chr(), chr(1/0), chr(-1/0), chr(0/0)); + +// invalid values +echo(chr(0), chr(-0), chr(0.5), chr(-0.9), chr(-100), chr(2097152), chr(1e10), chr(-2e10)); + +// valid values +echo(chr(f(33)), chr(49), chr(65), chr(97), chr(smiley), chr(128512)); + +// multiple values +echo(chr(90, 89, 88), chr(65, "test", false, -5, 0, 66, [67:69], [70, 71, 72, 73], smiley)); + +// ranges +echo(chr([48:57]), chr([70 : 2 : 80])); + +// vectors +echo(chr([65, 66, 67, 97, 98, 99]), chr([49, "test", true, -1, 50])); + +// other (not supported) types +echo(chr(undef), chr(true), chr("test"), chr(s)); diff --git a/testdata/scad/misc/empty-union.scad b/testdata/scad/misc/empty-union.scad new file mode 100644 index 00000000..f16a9d46 --- /dev/null +++ b/testdata/scad/misc/empty-union.scad @@ -0,0 +1,4 @@ +union() { + polyhedron(); + polyhedron(); +} diff --git a/testdata/scad/bugs/issue495b.scad b/testdata/scad/misc/internal-cavity-polyhedron.scad similarity index 100% rename from testdata/scad/bugs/issue495b.scad rename to testdata/scad/misc/internal-cavity-polyhedron.scad diff --git a/testdata/scad/bugs/issue495.scad b/testdata/scad/misc/internal-cavity.scad similarity index 100% rename from testdata/scad/bugs/issue495.scad rename to testdata/scad/misc/internal-cavity.scad diff --git a/testdata/scad/dxf/null-polygons.scad b/testdata/scad/misc/null-polygons.scad similarity index 100% rename from testdata/scad/dxf/null-polygons.scad rename to testdata/scad/misc/null-polygons.scad diff --git a/testdata/scad/bugs/issue112.scad b/testdata/scad/misc/rotate_extrude-hole.scad similarity index 100% rename from testdata/scad/bugs/issue112.scad rename to testdata/scad/misc/rotate_extrude-hole.scad diff --git a/testdata/scad/templates/import_dxf-tests-template.scad b/testdata/scad/templates/import_dxf-tests-template.scad index 91eca7a9..1a8b0df5 100644 --- a/testdata/scad/templates/import_dxf-tests-template.scad +++ b/testdata/scad/templates/import_dxf-tests-template.scad @@ -1,10 +1,10 @@ import(); -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); -import(file="../../dxf/multiple-layers.dxf"); -translate([-200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="0"); -translate([0,200,0]) import(filename="../../dxf/multiple-layers.dxf", layername="0"); -translate([200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="noname"); -translate([0,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="Layer with a pretty long name including \\ \"special\" /'\\\\ characters"); +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); +import(file="../../../dxf/multiple-layers.dxf"); +translate([-200,200,0]) import(file="../../../dxf/multiple-layers.dxf", layer="0"); +translate([0,200,0]) import(filename="../../../dxf/multiple-layers.dxf", layername="0"); +translate([200,200,0]) import(file="../../../dxf/multiple-layers.dxf", layer="noname"); +translate([0,200,0]) import(file="../../../dxf/multiple-layers.dxf", layer="Layer with a pretty long name including \\ \"special\" /'\\\\ characters"); translate([200,0,0]) import(file="@CMAKE_SOURCE_DIR@/../testdata/dxf/polygons.dxf"); diff --git a/testdata/scad/templates/import_stl-tests-template.scad b/testdata/scad/templates/import_stl-tests-template.scad index 2910fe5f..36cc9a14 100644 --- a/testdata/scad/templates/import_stl-tests-template.scad +++ b/testdata/scad/templates/import_stl-tests-template.scad @@ -1,9 +1,8 @@ -import_stl("import.stl"); translate([2,0,0]) import("import.stl"); translate([4,0,0]) import("import_bin.stl"); // Test binary STLs which happen to start with the string "solid" translate([0,4,0]) import("import_bin_solid.stl"); -translate([0,2,0]) import("@CMAKE_SOURCE_DIR@/../testdata/scad/features/import.stl"); +translate([0,2,0]) import("@CMAKE_SOURCE_DIR@/../testdata/scad/3D/features/import.stl"); translate([2,2,0]) { difference() { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 24a4e3ac..f2bf6e90 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -553,15 +553,6 @@ else() endif() endif() -if (DIFFPNG) - # How to set cflags to optimize the executable regardless of CMAKE_BUILD_TYPE? - set(IMAGE_COMPARE_EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/diffpng) - set(COMPARATOR "diffpng") - add_executable(diffpng diffpng.cpp ../src/lodepng.cpp) - message(STATUS "Image comparison program: diffpng") -endif() - - # Internal includes include_directories(../src) @@ -622,6 +613,7 @@ set(CORE_SOURCES ../src/parsersettings.cc ../src/mathc99.cc ../src/linalg.cc + ../src/colormap.cc ../src/Camera.cc ../src/handle_dep.cc ../src/value.cc @@ -681,6 +673,7 @@ set(CGAL_SOURCES ../src/CSGTermEvaluator.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc + ../src/cgalutils-tess.cc ../src/CGALCache.cc ../src/CGAL_Nef_polyhedron_DxfData.cc ../src/Polygon2d-CGAL.cc @@ -873,6 +866,18 @@ function(get_test_fullname TESTCMD FILENAME FULLNAME) set(${FULLNAME} ${${FULLNAME}} PARENT_SCOPE) endfunction() +# +# Check if a test file is a 2D test +# +function(is_2d FULLNAME RESULT) + list(FIND ALL_2D_FILES ${FULLNAME} IDX) + if (${IDX} GREATER -1) + set(${RESULT} 1 PARENT_SCOPE) + else() + set(${RESULT} PARENT_SCOPE) + endif() +endfunction() + # # This functions adds cmd-line tests given files. # @@ -905,16 +910,24 @@ function(add_cmdline_test TESTCMD_BASENAME) if (${DISABLED} EQUAL -1) + set(EXPERIMENTAL_OPTION "") + string(REGEX MATCH "csgtexttest" match_result1 ${TEST_FULLNAME}) + string(REGEX MATCH "cgalstlsanity" match_result2 ${TEST_FULLNAME}) + string(REGEX MATCH "dumptest" match_result3 ${TEST_FULLNAME}) + if( "${match_result1}" STREQUAL "" ) + if( "${match_result2}" STREQUAL "" ) + if( "${match_result3}" STREQUAL "" ) + set(EXPERIMENTAL_OPTION "--enable=text") + endif() + endif() + endif() + # 2D tests should be viewed from the top, not an angle. set(CAMERA_OPTION "") - foreach(test2d IN LISTS TESTS_2D) - string(REGEX MATCH "${test2d}" match_result ${TEST_FULLNAME}) - if (NOT "${match_result}" STREQUAL "") - #message(STATUS "2d test found: ${TEST_FULLNAME}, ${match_result}") - set(CAMERA_OPTION "--camera=0,0,100,0,0,0" "--viewall" "--autocenter" "--projection=ortho") - break() - endif() - endforeach() + is_2d(${SCADFILE} IS2D) + if (IS2D) + set(CAMERA_OPTION "--camera=0,0,100,0,0,0" "--viewall" "--autocenter" "--projection=ortho") + endif() # Handle configurations unset(FOUNDCONFIGS) @@ -933,7 +946,53 @@ function(add_cmdline_test TESTCMD_BASENAME) set(FILENAME_OPTION -f ${FILE_BASENAME}) endif() - add_test(NAME ${TEST_FULLNAME} ${CONFARG} ${CONFVAL} COMMAND ${PYTHON_EXECUTABLE} ${tests_SOURCE_DIR}/test_cmdline_tool.py --comparator=${COMPARATOR} -c ${IMAGE_COMPARE_EXECUTABLE} -s ${TESTCMD_SUFFIX} ${EXTRA_OPTIONS} ${TESTNAME_OPTION} ${FILENAME_OPTION} ${TESTCMD_EXE} ${TESTCMD_SCRIPT} "${SCADFILE}" ${CAMERA_OPTION} ${TESTCMD_ARGS}) + add_test(NAME ${TEST_FULLNAME} ${CONFARG} ${CONFVAL} COMMAND ${PYTHON_EXECUTABLE} ${tests_SOURCE_DIR}/test_cmdline_tool.py --comparator=${COMPARATOR} -c ${IMAGE_COMPARE_EXECUTABLE} -s ${TESTCMD_SUFFIX} ${EXTRA_OPTIONS} ${TESTNAME_OPTION} ${FILENAME_OPTION} ${TESTCMD_EXE} ${TESTCMD_SCRIPT} "${SCADFILE}" ${CAMERA_OPTION} ${EXPERIMENTAL_OPTION} ${TESTCMD_ARGS}) + set_property(TEST ${TEST_FULLNAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + endif() + endforeach() +endfunction() + +# +# Usage add_failing_test(testbasename RETVAL +# [EXE ] [SCRIPT