diff --git a/README.md b/README.md index 3323a07c..6b538d59 100644 --- a/README.md +++ b/README.md @@ -86,16 +86,16 @@ libraries from aptitude. If you're using Mac, or an older Linux/BSD, there are build scripts that download and compile the libraries from source. Follow the instructions for the platform you're compiling on below. -* [Qt4 (4.4 - 5.2)](http://www.qt.nokia.com/) -* [CGAL (3.6 - 4.1)](http://www.cgal.org/) +* [Qt4 (4.4 - 5.3)](http://www.qt.nokia.com/) +* [CGAL (3.6 - 4.4)](http://www.cgal.org/) * [GMP (5.x)](http://www.gmplib.org/) * [MPFR (3.x)](http://www.mpfr.org/) * [cmake (2.8, required by CGAL and the test framework)](http://www.cmake.org/) * [boost (1.35 - 1.55)](http://www.boost.org/) -* [OpenCSG (1.3.2)](http://www.opencsg.org/) +* [OpenCSG (1.3.2 ->)](http://www.opencsg.org/) * [GLEW (1.5.4 ->)](http://glew.sourceforge.net/) -* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/) -* [glib2 (2.2.0)](https://developer.gnome.org/glib/) +* [Eigen (3.x)](http://eigen.tuxfamily.org/) +* [glib2 (2.x)](https://developer.gnome.org/glib/) * [fontconfig (2.10)](http://fontconfig.org/) * [freetype2 (2.4)](http://freetype.org/) * [harfbuzz (0.9.19)](http://harfbuzz.org/) diff --git a/openscad.pro b/openscad.pro index abc133ce..200a13a5 100644 --- a/openscad.pro +++ b/openscad.pro @@ -317,6 +317,7 @@ SOURCES += src/version_check.cc \ src/ProgressWidget.cc \ src/mathc99.cc \ src/linalg.cc \ + src/Camera.cc \ src/handle_dep.cc \ src/value.cc \ src/expr.cc \ diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh index 4117f58c..65d5d3e0 100755 --- a/scripts/travis-ci.sh +++ b/scripts/travis-ci.sh @@ -13,7 +13,7 @@ if [[ $? != 0 ]]; then fi # Exclude tests known the cause issues on Travis # opencsgtest_rotate_extrude-tests - Fails on Ubuntu 12.04 using Gallium 0.4 drivers -ctest -j8 -E opencsgtest_rotate_extrude-tests +ctest -j8 -E "opencsgtest_rotate_extrude-tests|opencsgtest_render-tests" if [[ $? != 0 ]]; then echo "Test failure" exit 1 diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc index f072e309..c59df6bc 100644 --- a/src/CGALRenderer.cc +++ b/src/CGALRenderer.cc @@ -113,3 +113,20 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const this->polyhedron->draw(showfaces && showedges); } } + +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) { + bbox = this->polyset->getBoundingBox(); + } + + return bbox; +} diff --git a/src/CGALRenderer.h b/src/CGALRenderer.h index 57a0e4ab..5a5b3ac6 100644 --- a/src/CGALRenderer.h +++ b/src/CGALRenderer.h @@ -6,8 +6,9 @@ class CGALRenderer : public Renderer { public: CGALRenderer(shared_ptr geom); - ~CGALRenderer(); - void draw(bool showfaces, bool showedges) const; + virtual ~CGALRenderer(); + virtual void draw(bool showfaces, bool showedges) const; + virtual BoundingBox getBoundingBox() const; public: shared_ptr polyhedron; diff --git a/src/Camera.cc b/src/Camera.cc new file mode 100644 index 00000000..a774fda9 --- /dev/null +++ b/src/Camera.cc @@ -0,0 +1,80 @@ +#include "Camera.h" +#include "rendersettings.h" + +Camera::Camera(enum CameraType camtype) : + type(camtype), projection(Camera::PERSPECTIVE), fov(45), height(60), viewall(false) +{ + if (this->type == Camera::GIMBAL) { + object_trans << 0,0,0; + object_rot << 35,0,25; + viewer_distance = 500; + } else if (this->type == Camera::VECTOR) { + center << 0,0,0; + Eigen::Vector3d cameradir(1, 1, -0.5); + eye = center - 500 * cameradir; + } + pixel_width = RenderSettings::inst()->img_width; + pixel_height = RenderSettings::inst()->img_height; +} + +void Camera::setup(std::vector params) +{ + if (params.size() == 7) { + type = Camera::GIMBAL; + object_trans << params[0], params[1], params[2]; + object_rot << params[3], params[4], params[5]; + viewer_distance = params[6]; + } else if (params.size() == 6) { + type = Camera::VECTOR; + eye << params[0], params[1], params[2]; + center << params[3], params[4], params[5]; + } else { + assert("Gimbal cam needs 7 numbers, Vector camera needs 6"); + } +} + +void Camera::gimbalDefaultTranslate() +{ // match the GUI viewport numbers (historical reasons) + object_trans.x() *= -1; + object_trans.y() *= -1; + object_trans.z() *= -1; + object_rot.x() = fmodf(360 - object_rot.x() + 90, 360); + object_rot.y() = fmodf(360 - object_rot.y(), 360); + object_rot.z() = fmodf(360 - object_rot.z(), 360); +} + +/*! + Moves camera so that the given bbox is fully visible. + FIXME: The scalefactor is a temporary hack to be compatible with + earlier ways of showing the whole scene. +*/ +void Camera::viewAll(const BoundingBox &bbox, float scalefactor) +{ + if (this->type == Camera::NONE) { + this->type = Camera::VECTOR; + this->center = bbox.center(); + this->eye = this->center - Vector3d(1,1,-0.5); + } + + switch (this->projection) { + case Camera::ORTHOGONAL: + this->height = bbox.diagonal().norm(); + break; + case Camera::PERSPECTIVE: { + double radius = bbox.diagonal().norm()/2; + switch (this->type) { + case Camera::GIMBAL: + this->viewer_distance = radius / tan(this->fov*M_PI/360); + break; + case Camera::VECTOR: { + Vector3d cameradir = (this->center - this->eye).normalized(); + this->eye = this->center - radius*scalefactor*cameradir; + break; + } + default: + assert(false && "Camera type not specified"); + } + } + break; + } +} diff --git a/src/Camera.h b/src/Camera.h index 87be4e3a..dad092aa 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -16,61 +16,19 @@ projection, Perspective and Orthogonal. */ +#include "linalg.h" #include #include -#include "rendersettings.h" class Camera { public: enum CameraType { NONE, GIMBAL, VECTOR } type; enum ProjectionType { ORTHOGONAL, PERSPECTIVE } projection; - Camera() { - type = Camera::NONE; - projection = Camera::PERSPECTIVE; - } - Camera( enum CameraType e ) - { - type = e; - if ( e == Camera::GIMBAL ) { - object_trans << 0,0,0; - object_rot << 35,0,25; - viewer_distance = 500; - } else if ( e == Camera::VECTOR ) { - center << 0,0,0; - Eigen::Vector3d cameradir(1, 1, -0.5); - eye = center - 500 * cameradir; - } - pixel_width = RenderSettings::inst()->img_width; - pixel_height = RenderSettings::inst()->img_height; - projection = Camera::PERSPECTIVE; - } - - void setup( std::vector params ) - { - if ( params.size() == 7 ) { - type = Camera::GIMBAL; - object_trans << params[0], params[1], params[2]; - object_rot << params[3], params[4], params[5]; - viewer_distance = params[6]; - } else if ( params.size() == 6 ) { - type = Camera::VECTOR; - eye << params[0], params[1], params[2]; - center << params[3], params[4], params[5]; - } else { - assert( "Gimbal cam needs 7 numbers, Vector camera needs 6" ); - } - } - - void gimbalDefaultTranslate() - { // match the GUI viewport numbers (historical reasons) - object_trans.x() *= -1; - object_trans.y() *= -1; - object_trans.z() *= -1; - object_rot.x() = fmodf(360 - object_rot.x() + 90, 360 ); - object_rot.y() = fmodf(360 - object_rot.y(), 360); - object_rot.z() = fmodf(360 - object_rot.z(), 360); - } + Camera(enum CameraType camtype = NONE); + void setup(std::vector params); + void gimbalDefaultTranslate(); + void viewAll(const BoundingBox &bbox, float scalefactor = 1.0f); // Vectorcam Eigen::Vector3d eye; @@ -82,6 +40,14 @@ public: Eigen::Vector3d object_rot; double viewer_distance; + // Perspective settings + double fov; // Field of view + + // Orthographic settings + double height; // world-space height of viewport + + bool viewall; + unsigned int pixel_width; unsigned int pixel_height; }; diff --git a/src/GLView.cc b/src/GLView.cc index db9b3465..81a2f29a 100644 --- a/src/GLView.cc +++ b/src/GLView.cc @@ -3,6 +3,7 @@ #include "stdio.h" #include "rendersettings.h" #include "mathc99.h" +#include "renderer.h" #ifdef _WIN32 #include @@ -47,60 +48,107 @@ void GLView::resizeGL(int w, int h) cam.pixel_width = w; cam.pixel_height = h; glViewport(0, 0, w, h); - w_h_ratio = sqrt((double)w / (double)h); + aspectratio = 1.0*w/h; } -void GLView::setupGimbalCamPerspective() -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(-w_h_ratio, +w_h_ratio, -(1/w_h_ratio), +(1/w_h_ratio), +10.0, +far_far_away); - gluLookAt(0.0, -cam.viewer_distance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); -} - -void GLView::setupGimbalCamOrtho(double distance, bool offset) -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (offset) glTranslated(-0.8, -0.8, 0); - double l = distance/10; - glOrtho(-w_h_ratio*l, +w_h_ratio*l, - -(1/w_h_ratio)*l, +(1/w_h_ratio)*l, - -far_far_away, +far_far_away); - gluLookAt(0.0, -cam.viewer_distance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); -} - -void GLView::setupVectorCamPerspective() -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - double dist = (cam.center - cam.eye).norm(); - gluPerspective(45, pow(w_h_ratio,2), 0.1*dist, 100*dist); -} - -void GLView::setupVectorCamOrtho(bool offset) -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (offset) glTranslated(-0.8, -0.8, 0); - double l = (cam.center - cam.eye).norm() / 10; - glOrtho(-pow(w_h_ratio,2)*l, +pow(w_h_ratio,2)*l, - -(1/pow(w_h_ratio,2))*l, +(1/pow(w_h_ratio,2))*l, - -far_far_away, +far_far_away); -} - -void GLView::setCamera( Camera &cam ) +void GLView::setCamera(const Camera &cam) { this->cam = cam; } +void GLView::setupCamera() +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + switch (this->cam.type) { + case Camera::GIMBAL: + switch (this->cam.projection) { + case Camera::PERSPECTIVE: { + double dist = cam.viewer_distance; + gluPerspective(cam.fov, aspectratio, 0.1*dist, 100*dist); + break; + } + case Camera::ORTHOGONAL: { + glOrtho(-cam.height/2*aspectratio, cam.height*aspectratio/2, + -cam.height/2, cam.height/2, + -far_far_away, +far_far_away); + break; + } + } + gluLookAt(0.0, -cam.viewer_distance, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0); + glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0); + glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0); + glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z() ); + break; + case Camera::VECTOR: { + switch (this->cam.projection) { + case Camera::PERSPECTIVE: { + double dist = (cam.center - cam.eye).norm(); + gluPerspective(cam.fov, aspectratio, 0.1*dist, 100*dist); + break; + } + case Camera::ORTHOGONAL: { + glOrtho(-cam.height/2*aspectratio, cam.height*aspectratio/2, + -cam.height/2, cam.height/2, + -far_far_away, +far_far_away); + break; + } + } + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + Vector3d dir(cam.eye - cam.center); + Vector3d up(0.0,0.0,1.0); + if (dir.cross(up).norm() < 0.001) { // View direction is ~parallel with up vector + up << 0.0,1.0,0.0; + } + + gluLookAt(cam.eye[0], cam.eye[1], cam.eye[2], + cam.center[0], cam.center[1], cam.center[2], + up[0], up[1], up[2]); + break; + } + default: + break; + } +} + void GLView::paintGL() { - if (cam.type == Camera::GIMBAL) gimbalCamPaintGL(); - else if (cam.type == Camera::VECTOR) vectorCamPaintGL(); - else if (cam.type == Camera::NONE) { - fprintf(stderr,"paintGL with null camera\n"); - } + glEnable(GL_LIGHTING); + + setupCamera(); + + Color4f bgcol = RenderSettings::inst()->color(RenderSettings::BACKGROUND_COLOR); + glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Only for GIMBAL cam + if (showcrosshairs) GLView::showCrosshairs(); + if (showaxes) GLView::showAxes(); + + glDepthFunc(GL_LESS); + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + glLineWidth(2); + glColor3d(1.0, 0.0, 0.0); + + if (this->renderer) { +#if defined(ENABLE_OPENCSG) + // FIXME: This belongs in the OpenCSG renderer, but it doesn't know about this ID yet + OpenCSG::setContext(this->opencsg_id); +#endif + this->renderer->draw(showfaces, showedges); + } + + // Only for GIMBAL + if (showaxes) GLView::showSmallaxes(); } #ifdef ENABLE_OPENCSG @@ -279,86 +327,6 @@ void GLView::initializeGL() #endif } -void GLView::vectorCamPaintGL() -{ - glEnable(GL_LIGHTING); - - if (cam.projection==Camera::ORTHOGONAL) setupVectorCamOrtho(); - else setupVectorCamPerspective(); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glClearColor(1.0f, 1.0f, 0.92f, 1.0f); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - gluLookAt(cam.eye[0], cam.eye[1], cam.eye[2], - cam.center[0], cam.center[1], cam.center[2], - 0.0, 0.0, 1.0); - - // fixme - showcrosshairs doesnt work with vector camera - // if (showcrosshairs) GLView::showCrosshairs(); - - if (showaxes) GLView::showAxes(); - - glDepthFunc(GL_LESS); - glCullFace(GL_BACK); - glDisable(GL_CULL_FACE); - - glLineWidth(2); - glColor3d(1.0, 0.0, 0.0); - //FIXME showSmallAxes wont work with vector camera - //if (showaxes) GLView::showSmallaxes(); - - if (this->renderer) { - this->renderer->draw(showfaces, showedges); - } -} - -void GLView::gimbalCamPaintGL() -{ - glEnable(GL_LIGHTING); - - if (cam.projection == Camera::ORTHOGONAL) - GLView::setupGimbalCamOrtho(cam.viewer_distance); - else - GLView::setupGimbalCamPerspective(); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - Color4f bgcol = RenderSettings::inst()->color(RenderSettings::BACKGROUND_COLOR); - glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0); - glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0); - glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0); - - if (showcrosshairs) GLView::showCrosshairs(); - - glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z() ); - - if (showaxes) GLView::showAxes(); - - glDepthFunc(GL_LESS); - glCullFace(GL_BACK); - glDisable(GL_CULL_FACE); - glLineWidth(2); - glColor3d(1.0, 0.0, 0.0); - - if (this->renderer) { -#if defined(ENABLE_OPENCSG) - // FIXME: This belongs in the OpenCSG renderer, but it doesn't know about this ID yet - OpenCSG::setContext(this->opencsg_id); -#endif - this->renderer->draw(showfaces, showedges); - } - // Small axis cross in the lower left corner - if (showaxes) GLView::showSmallaxes(); -} - void GLView::showSmallaxes() { // Fixme - this doesnt work in Vector Camera mode @@ -367,8 +335,18 @@ void GLView::showSmallaxes() // Small axis cross in the lower left corner glDepthFunc(GL_ALWAYS); - GLView::setupGimbalCamOrtho(1000,true); - + // Set up an orthographic projection of the axis cross in the corner + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glTranslatef(-0.8f, -0.8f, 0.0f); + double scale = 90; + glOrtho(-scale*dpi*aspectratio,scale*dpi*aspectratio, + -scale*dpi,scale*dpi, + -scale*dpi,scale*dpi); + gluLookAt(0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0); @@ -436,10 +414,6 @@ void GLView::showSmallaxes() // FIXME - depends on gimbal camera 'viewer distance'.. how to fix this // for VectorCamera? glEnd(); - - //Restore perspective for next paint - if(cam.projection==Camera::PERSPECTIVE) - GLView::setupGimbalCamPerspective(); } void GLView::showAxes() @@ -447,11 +421,10 @@ void GLView::showAxes() // FIXME: doesn't work under Vector Camera // Large gray axis cross inline with the model // FIXME: This is always gray - adjust color to keep contrast with background - float dpi = this->getDPI(); - glLineWidth(1*dpi); + glLineWidth(this->getDPI()); glColor3d(0.5, 0.5, 0.5); glBegin(GL_LINES); - double l = cam.viewer_distance*dpi/10; + double l = cam.viewer_distance; glVertex3d(-l, 0, 0); glVertex3d(+l, 0, 0); glVertex3d(0, -l, 0); diff --git a/src/GLView.h b/src/GLView.h index 3fba6f67..83559aff 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -25,28 +25,21 @@ Some actions (showCrossHairs) only work properly on Gimbal Camera. #endif #include "system-gl.h" #include -#include "renderer.h" #include "Camera.h" class GLView { public: GLView(); - void setRenderer(Renderer* r); + void setRenderer(class Renderer* r); + Renderer *getRenderer() const { return this->renderer; } void initializeGL(); void resizeGL(int w, int h); virtual void paintGL(); - void setCamera( Camera &cam ); - - void setupGimbalCamPerspective(); - void setupGimbalCamOrtho(double distance, bool offset=false); - void gimbalCamPaintGL(); - - void setupVectorCamPerspective(); - void setupVectorCamOrtho(bool offset=false); - void vectorCamPaintGL(); + void setCamera(const Camera &cam); + void setupCamera(); void showCrosshairs(); void showAxes(); @@ -61,7 +54,7 @@ public: double far_far_away; size_t width; size_t height; - double w_h_ratio; + double aspectratio; bool orthomode; bool showaxes; bool showfaces; diff --git a/src/MainWindow.h b/src/MainWindow.h index dc791390..af347b1f 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -86,6 +86,7 @@ private: void openFile(const QString &filename); void handleFileDrop(const QString &filename); void refreshDocument(); + void updateCamera(); void updateTemporalVariables(); bool fileChangedOnDisk(); void compileTopLevelDocument(); @@ -202,6 +203,7 @@ public slots: void viewPerspective(); void viewOrthogonal(); void viewResetView(); + void viewAll(); void hideConsole(); void animateUpdateDocChanged(); void animateUpdate(); @@ -224,6 +226,7 @@ private: char const * afterCompileSlot; bool procevents; + bool isClosing; class QTemporaryFile *tempFile; class ProgressWidget *progresswidget; class CGALWorker *cgalworker; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index cdcf6fb7..31d6d49a 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -120,7 +120,7 @@ 0 0 936 - 34 + 22 @@ -234,6 +234,7 @@ + @@ -961,6 +962,11 @@ Ctrl+[ + + + View All + + diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc index c56dad13..0c1aa4e0 100644 --- a/src/OpenCSGRenderer.cc +++ b/src/OpenCSGRenderer.cc @@ -147,3 +147,10 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, } std::for_each(primitives.begin(), primitives.end(), del_fun()); } + +BoundingBox OpenCSGRenderer::getBoundingBox() const +{ + BoundingBox bbox; + if (this->root_chain) bbox = this->root_chain->getBoundingBox(); + return bbox; +} diff --git a/src/OpenCSGRenderer.h b/src/OpenCSGRenderer.h index cca7161d..421a43e6 100644 --- a/src/OpenCSGRenderer.h +++ b/src/OpenCSGRenderer.h @@ -8,7 +8,8 @@ class OpenCSGRenderer : public Renderer public: OpenCSGRenderer(class CSGChain *root_chain, CSGChain *highlights_chain, CSGChain *background_chain, GLint *shaderinfo); - void draw(bool showfaces, bool showedges) const; + virtual void draw(bool showfaces, bool showedges) const; + virtual BoundingBox getBoundingBox() const; private: void renderCSGChain(class CSGChain *chain, GLint *shaderinfo, bool highlight, bool background) const; diff --git a/src/QGLView.cc b/src/QGLView.cc index 27659f90..376c1ff0 100644 --- a/src/QGLView.cc +++ b/src/QGLView.cc @@ -85,7 +85,16 @@ void QGLView::resetView() { cam.object_rot << 35, 0, -25; cam.object_trans << 0, 0, 0; - cam.viewer_distance = 500; + cam.viewer_distance = 140; +} + +void QGLView::viewAll() +{ + if (Renderer *r = this->getRenderer()) { + BoundingBox bbox = r->getBoundingBox(); + cam.object_trans = -bbox.center(); + cam.viewAll(r->getBoundingBox()); + } } void QGLView::initializeGL() @@ -147,17 +156,16 @@ void QGLView::display_opencsg_warning_dialog() void QGLView::resizeGL(int w, int h) { GLView::resizeGL(w,h); - GLView::setupGimbalCamPerspective(); } void QGLView::paintGL() { - GLView::gimbalCamPaintGL(); + GLView::paintGL(); if (statusLabel) { QString msg; - Camera nc( cam ); + Camera nc(cam); nc.gimbalDefaultTranslate(); msg.sprintf("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f", nc.object_trans.x(), nc.object_trans.y(), nc.object_trans.z(), diff --git a/src/QGLView.h b/src/QGLView.h index e06f5a79..25d6a90d 100644 --- a/src/QGLView.h +++ b/src/QGLView.h @@ -44,6 +44,7 @@ public: #endif bool save(const char *filename); void resetView(); + void viewAll(); public slots: void ZoomIn(void); diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index 84425883..d95e7afe 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -123,3 +123,10 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, glPopMatrix(); } } + +BoundingBox ThrownTogetherRenderer::getBoundingBox() const +{ + BoundingBox bbox; + if (this->root_chain) bbox = this->root_chain->getBoundingBox(); + return bbox; +} diff --git a/src/ThrownTogetherRenderer.h b/src/ThrownTogetherRenderer.h index 7933f5b6..5db60fc8 100644 --- a/src/ThrownTogetherRenderer.h +++ b/src/ThrownTogetherRenderer.h @@ -7,7 +7,8 @@ class ThrownTogetherRenderer : public Renderer public: ThrownTogetherRenderer(class CSGChain *root_chain, CSGChain *highlights_chain, CSGChain *background_chain); - void draw(bool showfaces, bool showedges) const; + virtual void draw(bool showfaces, bool showedges) const; + virtual BoundingBox getBoundingBox() const; private: void renderCSGChain(CSGChain *chain, bool highlight, bool background, bool showedges, bool fberror) const; diff --git a/src/builtin.cc b/src/builtin.cc index fc43b5e4..c20f9f03 100644 --- a/src/builtin.cc +++ b/src/builtin.cc @@ -106,6 +106,7 @@ Builtins::Builtins() Value zero3val(zero3); this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr(new Expression(zero3val)))); this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr(new Expression(zero3val)))); + this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr(new Expression(500)))); } Builtins::~Builtins() diff --git a/src/export_png.cc b/src/export_png.cc index ea060f9d..a0b8aff6 100644 --- a/src/export_png.cc +++ b/src/export_png.cc @@ -13,11 +13,17 @@ #include "cgalutils.h" #include "CGAL_Nef_polyhedron.h" +static void setupCamera(Camera &cam, const BoundingBox &bbox, float scalefactor) +{ + if (cam.type == Camera::NONE) cam.viewall = true; + if (cam.viewall) cam.viewAll(bbox, scalefactor); +} + void export_png(const Geometry *root_geom, Camera &cam, std::ostream &output) { OffscreenView *glview; try { - glview = new OffscreenView( cam.pixel_width, cam.pixel_height ); + glview = new OffscreenView(cam.pixel_width, cam.pixel_height); } catch (int error) { fprintf(stderr,"Can't create OpenGL OffscreenView. Code: %i.\n", error); return; @@ -25,32 +31,16 @@ void export_png(const Geometry *root_geom, Camera &cam, std::ostream &output) shared_ptr ptr(root_geom); CGALRenderer cgalRenderer(ptr); - BoundingBox bbox; - if (cgalRenderer.polyhedron) { - CGAL::Bbox_3 cgalbbox = cgalRenderer.polyhedron->bbox(); - bbox = BoundingBox( - Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), - Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax()) ); - } - else if (cgalRenderer.polyset) { - bbox = cgalRenderer.polyset->getBoundingBox(); - } + BoundingBox bbox = cgalRenderer.getBoundingBox(); + setupCamera(cam, bbox, 3); - if (cam.type == Camera::NONE) { - cam.type = Camera::VECTOR; - cam.center = getBoundingCenter(bbox); - double radius = getBoundingRadius(bbox); - Vector3d cameradir(1, 1, -0.5); - cam.eye = cam.center - radius*2*cameradir; - } - - glview->setCamera( cam ); + glview->setCamera(cam); glview->setRenderer(&cgalRenderer); glview->paintGL(); glview->save(output); } -enum Previewer { OPENCSG, THROWN } previewer; +enum Previewer { OPENCSG, THROWNTOGETHER } previewer; #ifdef ENABLE_OPENCSG #include "OpenCSGRenderer.h" @@ -58,16 +48,16 @@ enum Previewer { OPENCSG, THROWN } previewer; #endif #include "ThrownTogetherRenderer.h" -void export_png_preview_common( Tree &tree, Camera &cam, std::ostream &output, Previewer previewer = OPENCSG ) +void export_png_preview_common(Tree &tree, Camera &cam, std::ostream &output, Previewer previewer = OPENCSG) { CsgInfo csgInfo = CsgInfo(); - if ( !csgInfo.compile_chains( tree ) ) { + if (!csgInfo.compile_chains(tree)) { fprintf(stderr,"Couldn't initialize CSG chains\n"); return; } try { - csgInfo.glview = new OffscreenView( cam.pixel_width, cam.pixel_height ); + csgInfo.glview = new OffscreenView(cam.pixel_width, cam.pixel_height); } catch (int error) { fprintf(stderr,"Can't create OpenGL OffscreenView. Code: %i.\n", error); return; @@ -76,39 +66,30 @@ void export_png_preview_common( Tree &tree, Camera &cam, std::ostream &output, P #ifdef ENABLE_OPENCSG OpenCSGRenderer openCSGRenderer(csgInfo.root_chain, csgInfo.highlights_chain, csgInfo.background_chain, csgInfo.glview->shaderinfo); #endif - ThrownTogetherRenderer thrownTogetherRenderer( csgInfo.root_chain, csgInfo.highlights_chain, csgInfo.background_chain ); + ThrownTogetherRenderer thrownTogetherRenderer(csgInfo.root_chain, csgInfo.highlights_chain, csgInfo.background_chain); - if (cam.type == Camera::NONE) { - cam.type = Camera::VECTOR; - double radius = 1.0; - if (csgInfo.root_chain) { - BoundingBox bbox = csgInfo.root_chain->getBoundingBox(); - cam.center = (bbox.min() + bbox.max()) / 2; - radius = (bbox.max() - bbox.min()).norm() / 2; - } - Vector3d cameradir(1, 1, -0.5); - cam.eye = cam.center - radius*1.8*cameradir; - } - - csgInfo.glview->setCamera( cam ); #ifdef ENABLE_OPENCSG - if ( previewer == OPENCSG ) - csgInfo.glview->setRenderer( &openCSGRenderer ); + if (previewer == OPENCSG) + csgInfo.glview->setRenderer(&openCSGRenderer); else #endif - csgInfo.glview->setRenderer( &thrownTogetherRenderer ); + csgInfo.glview->setRenderer(&thrownTogetherRenderer); #ifdef ENABLE_OPENCSG - OpenCSG::setContext( 0 ); - OpenCSG::setOption( OpenCSG::OffscreenSetting, OpenCSG::FrameBufferObject ); + BoundingBox bbox = csgInfo.glview->getRenderer()->getBoundingBox(); + setupCamera(cam, bbox, 2.7); + + csgInfo.glview->setCamera(cam); + OpenCSG::setContext(0); + OpenCSG::setOption(OpenCSG::OffscreenSetting, OpenCSG::FrameBufferObject); #endif csgInfo.glview->paintGL(); - csgInfo.glview->save( output ); + csgInfo.glview->save(output); } void export_png_with_opencsg(Tree &tree, Camera &cam, std::ostream &output) { #ifdef ENABLE_OPENCSG - export_png_preview_common( tree, cam, output, OPENCSG ); + export_png_preview_common(tree, cam, output, OPENCSG); #else fprintf(stderr,"This openscad was built without OpenCSG support\n"); #endif @@ -116,8 +97,7 @@ void export_png_with_opencsg(Tree &tree, Camera &cam, std::ostream &output) void export_png_with_throwntogether(Tree &tree, Camera &cam, std::ostream &output) { - export_png_preview_common( tree, cam, output, THROWN ); + export_png_preview_common(tree, cam, output, THROWNTOGETHER); } - #endif // ENABLE_CGAL diff --git a/src/highlighter.cc b/src/highlighter.cc index 955911d5..67c343e2 100644 --- a/src/highlighter.cc +++ b/src/highlighter.cc @@ -225,7 +225,7 @@ Highlighter::Highlighter(QTextDocument *parent) tokentypes["prim3d"] << "cube" << "cylinder" << "sphere" << "polyhedron"; tokentypes["prim2d"] << "square" << "polygon" << "circle"; tokentypes["import"] << "include" << "use" << "import_stl" << "import" << "import_dxf" << "dxf_dim" << "dxf_cross" << "surface"; - tokentypes["special"] << "$children" << "child" << "children" << "$fn" << "$fa" << "$fs" << "$t" << "$vpt" << "$vpr"; + tokentypes["special"] << "$children" << "child" << "children" << "$fn" << "$fa" << "$fs" << "$t" << "$vpt" << "$vpr" << "$vpd"; tokentypes["extrude"] << "linear_extrude" << "rotate_extrude"; tokentypes["bracket"] << "[" << "]" << "(" << ")"; tokentypes["curlies"] << "{" << "}"; diff --git a/src/linalg.cc b/src/linalg.cc index 274a70a5..4d85a710 100644 --- a/src/linalg.cc +++ b/src/linalg.cc @@ -46,18 +46,3 @@ bool matrix_contains_nan( const Transform3d &m ) } return false; } - -double getBoundingRadius(BoundingBox bbox) -{ - // FIXME: For eigen3, we can use bbox.diagonal().norm()/2; - double radius = (bbox.max() - bbox.min()).norm() / 2; - return radius; // 0; -} - -Vector3d getBoundingCenter(BoundingBox bbox) -{ - // FIXME: For eigen3, we can use bbox.center(); - Vector3d center = (bbox.min() + bbox.max()) / 2; - return center; // Vector3d(0,0,0); -} - diff --git a/src/linalg.h b/src/linalg.h index 8cf19392..d43a28a5 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -21,9 +21,6 @@ bool matrix_contains_infinity( const Transform3d &m ); bool matrix_contains_nan( const Transform3d &m ); BoundingBox operator*(const Transform3d &m, const BoundingBox &box); -Vector3d getBoundingCenter(BoundingBox bbox); -double getBoundingRadius(BoundingBox bbox); - class Color4f : public Eigen::Vector4f { diff --git a/src/mainwin.cc b/src/mainwin.cc index d2b2a722..3f8fe223 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -36,6 +36,7 @@ #include "highlighter.h" #include "export.h" #include "builtin.h" +#include "expression.h" #include "progress.h" #include "dxfdim.h" #include "AboutDialog.h" @@ -204,6 +205,7 @@ MainWindow::MainWindow(const QString &filename) tval = 0; fps = 0; fsteps = 1; + isClosing = false; const QString importStatement = "import(\"%1\");\n"; const QString surfaceStatement = "surface(\"%1\");\n"; @@ -370,6 +372,7 @@ MainWindow::MainWindow(const QString &filename) connect(this->viewActionDiagonal, SIGNAL(triggered()), this, SLOT(viewAngleDiagonal())); connect(this->viewActionCenter, SIGNAL(triggered()), this, SLOT(viewCenter())); connect(this->viewActionResetView, SIGNAL(triggered()), this, SLOT(viewResetView())); + connect(this->viewActionViewAll, SIGNAL(triggered()), this, SLOT(viewAll())); connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective())); connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal())); connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole())); @@ -485,14 +488,10 @@ MainWindow::loadViewSettings(){ } else { viewPerspective(); } - if (settings.value("view/hideConsole").toBool()) { - viewActionHide->setChecked(true); - hideConsole(); - } - if (settings.value("view/hideEditor").toBool()) { - editActionHide->setChecked(true); - hideEditor(); - } + viewActionHide->setChecked(settings.value("view/hideConsole").toBool()); + hideConsole(); + editActionHide->setChecked(settings.value("view/hideEditor").toBool()); + hideEditor(); updateMdiMode(settings.value("advanced/mdi").toBool()); updateUndockMode(settings.value("advanced/undockableWindows").toBool()); } @@ -1366,6 +1365,68 @@ void MainWindow::updateTemporalVariables() vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.z(), 360))); top_ctx.set_variable("$vpr", Value(vpr)); + + top_ctx.set_variable("$vpd", Value(qglview->cam.viewer_distance)); +} + + +/*! + * Update the viewport camera by evaluating the special variables. If they + * are assigned on top-level, the values are used to change the camera + * rotation, translation and distance. + */ +void MainWindow::updateCamera() +{ + if (!root_module) + return; + + bool camera_set = false; + double tx = qglview->cam.object_trans.x(); + double ty = qglview->cam.object_trans.y(); + double tz = qglview->cam.object_trans.z(); + double rx = qglview->cam.object_rot.x(); + double ry = qglview->cam.object_rot.y(); + double rz = qglview->cam.object_rot.z(); + double d = qglview->cam.viewer_distance; + BOOST_FOREACH(const Assignment &a, root_module->scope.assignments) { + double x, y, z; + if ("$vpr" == a.first) { + const Value vpr = a.second.get()->evaluate(&top_ctx); + if (vpr.getVec3(x, y, z)) { + rx = x; + ry = y; + rz = z; + camera_set = true; + } + } else if ("$vpt" == a.first) { + const Value vpt = a.second.get()->evaluate(&top_ctx); + if (vpt.getVec3(x, y, z)) { + tx = x; + ty = y; + tz = z; + camera_set = true; + } + } else if ("$vpd" == a.first) { + const Value vpd = a.second.get()->evaluate(&top_ctx); + if (vpd.type() == Value::NUMBER) { + d = vpd.toDouble(); + camera_set = true; + } + } + } + + if (camera_set) { + std::vector params; + params.push_back(tx); + params.push_back(ty); + params.push_back(tz); + params.push_back(rx); + params.push_back(ry); + params.push_back(rz); + params.push_back(d); + qglview->cam.setup(params); + qglview->updateGL(); + } } /*! @@ -1413,6 +1474,7 @@ void MainWindow::compileTopLevelDocument() QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false); + updateCamera(); } void MainWindow::checkAutoReload() @@ -2080,8 +2142,17 @@ void MainWindow::viewResetView() this->qglview->updateGL(); } +void MainWindow::viewAll() +{ + this->qglview->viewAll(); + this->qglview->updateGL(); +} + void MainWindow::on_editorDock_visibilityChanged(bool visible) { + if (isClosing) { + return; + } QSettings settings; settings.setValue("view/hideEditor", !visible); editActionHide->setChecked(!visible); @@ -2090,6 +2161,9 @@ void MainWindow::on_editorDock_visibilityChanged(bool visible) void MainWindow::on_consoleDock_visibilityChanged(bool visible) { + if (isClosing) { + return; + } QSettings settings; settings.setValue("view/hideConsole", !visible); viewActionHide->setChecked(!visible); @@ -2257,6 +2331,7 @@ void MainWindow::closeEvent(QCloseEvent *event) delete this->tempFile; this->tempFile = NULL; } + isClosing = true; event->accept(); } else { event->ignore(); diff --git a/src/openscad.cc b/src/openscad.cc index e7551d7f..cd0bea08 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -109,6 +109,7 @@ static void help(const char *progname) "%2%[ --version ] [ --info ] \\\n" "%2%[ --camera=translatex,y,z,rotx,y,z,dist | \\\n" "%2% --camera=eyex,y,z,centerx,y,z ] \\\n" + "%2%[ --viewall ] \\\n" "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" "%2%[ --render | --preview[=throwntogether] ] \\\n" "%2%[ --csglimit=num ]" @@ -172,6 +173,10 @@ Camera get_camera( po::variables_map vm ) camera.gimbalDefaultTranslate(); } + if (vm.count("viewall")) { + camera.viewall = true; + } + if (vm.count("projection")) { string proj = vm["projection"].as(); if (proj=="o" || proj=="ortho" || proj=="orthogonal") @@ -430,7 +435,6 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c } else { export_png_with_opencsg(tree, camera, fstream); } - PRINTB("Camera eye: %f %f %f\n", camera.eye[0] % camera.eye[1] % camera.eye[2]); fstream.close(); } } @@ -609,6 +613,7 @@ int main(int argc, char **argv) ("preview", po::value(), "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview") ("csglimit", po::value(), "if exporting a png image, stop rendering at the given number of CSG elements") ("camera", po::value(), "parameters for camera when exporting png") + ("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") ("debug", po::value(), "special debug info") @@ -713,7 +718,7 @@ int main(int argc, char **argv) currentdir = boosty::stringy(fs::current_path()); - Camera camera = get_camera( vm ); + Camera camera = get_camera(vm); // Initialize global visitors NodeCache nodecache; diff --git a/src/polyclipping/clipper.cpp b/src/polyclipping/clipper.cpp index 162df7d9..9d6d029b 100755 --- a/src/polyclipping/clipper.cpp +++ b/src/polyclipping/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.2 * -* Date : 15 December 2013 * +* Version : 6.1.3a * +* Date : 22 January 2014 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2013 * +* Copyright : Angus Johnson 2010-2014 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -491,7 +491,52 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp) } //------------------------------------------------------------------------------ -int PointInPolygon (const IntPoint& pt, OutPt* op) +int PointInPolygon (const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf @@ -534,7 +579,7 @@ int PointInPolygon (const IntPoint& pt, OutPt* op) } //------------------------------------------------------------------------------ -bool Poly2ContainsPoly1(OutPt* OutPt1, OutPt* OutPt2) +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { OutPt* op = OutPt1; do @@ -1126,11 +1171,9 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) catch(...) { delete [] edges; - return false; //almost certainly a vertex has exceeded range + throw; //range test fails } - TEdge *eStart = &edges[0]; - if (!Closed) eStart->Prev->OutIdx = Skip; //2. Remove duplicate vertices, and (when closed) collinear edges ... TEdge *E = eStart, *eLoopStop = eStart; @@ -1171,7 +1214,11 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; } - if (!Closed) m_HasOpenPaths = true; + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } //3. Do second stage of edge initialization ... E = eStart; @@ -1431,21 +1478,12 @@ void Clipper::ZFillFunction(TZFillCallback zFillFunc) //------------------------------------------------------------------------------ #endif -void Clipper::Clear() -{ - if (m_edges.empty()) return; //avoids problems with ClipperBase destructor - DisposeAllOutRecs(); - ClipperBase::Clear(); -} -//------------------------------------------------------------------------------ - void Clipper::Reset() { ClipperBase::Reset(); m_Scanbeam.clear(); m_ActiveEdges = 0; m_SortedEdges = 0; - DisposeAllOutRecs(); LocalMinima* lm = m_MinimaList; while (lm) { @@ -1469,6 +1507,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, m_UsingPolyTree = false; bool succeeded = ExecuteInternal(); if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); m_ExecuteLocked = false; return succeeded; } @@ -1485,6 +1524,7 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, m_UsingPolyTree = true; bool succeeded = ExecuteInternal(); if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); m_ExecuteLocked = false; return succeeded; } @@ -2674,12 +2714,13 @@ void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) //First, match up overlapping horizontal edges (eg when one polygon's //intermediate horz edge overlaps an intermediate horz edge of another, or //when one polygon sits on top of another) ... - for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - { - Join* j = m_GhostJoins[i]; - if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) - AddJoin(j->OutPt1, outPt, j->OffPt); - } + //for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + //{ + // Join* j = m_GhostJoins[i]; + // if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) + // AddJoin(j->OutPt1, outPt, j->OffPt); + //} + //Also, since horizontal edges at the top of one SB are often removed from //the AEL before we process the horizontal edges at the bottom of the next, //we need to create 'ghost' Join records of 'contrubuting' horizontals that @@ -4255,6 +4296,15 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) { Clipper c; @@ -4428,8 +4478,8 @@ void Minkowski(const Path& poly, const Path& path, Paths quads; quads.reserve((pathCnt + delta) * (polyCnt + 1)); - for (size_t i = 0; i <= pathCnt - 2 + delta; ++i) - for (size_t j = 0; j <= polyCnt - 1; ++j) + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) { Path quad; quad.reserve(4); @@ -4447,15 +4497,30 @@ void Minkowski(const Path& poly, const Path& path, } //------------------------------------------------------------------------------ -void MinkowskiSum(const Path& poly, const Path& path, Paths& solution, bool isClosed) +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) { - Minkowski(poly, path, solution, true, isClosed); + Minkowski(pattern, path, solution, true, pathIsClosed); } //------------------------------------------------------------------------------ -void MinkowskiDiff(const Path& poly, const Path& path, Paths& solution, bool isClosed) +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, + PolyFillType pathFillType, bool pathIsClosed) { - Minkowski(poly, path, solution, false, isClosed); + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + } + if (pathIsClosed) c.AddPaths(paths, ptClip, true); + c.Execute(ctUnion, solution, pathFillType, pathFillType); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); } //------------------------------------------------------------------------------ diff --git a/src/polyclipping/clipper.hpp b/src/polyclipping/clipper.hpp index cacdb8b8..84870141 100755 --- a/src/polyclipping/clipper.hpp +++ b/src/polyclipping/clipper.hpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.2 * -* Date : 15 December 2013 * +* Version : 6.1.3a * +* Date : 22 January 2014 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2013 * +* Copyright : Angus Johnson 2010-2014 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.1.2" +#define CLIPPER_VERSION "6.1.3" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -166,6 +166,7 @@ private: bool Orientation(const Path &poly); double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); #ifdef use_deprecated void OffsetPaths(const Paths &in_polys, Paths &out_polys, @@ -181,8 +182,10 @@ void CleanPolygon(Path& poly, double distance = 1.415); void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); void CleanPolygons(Paths& polys, double distance = 1.415); -void MinkowskiSum(const Path& poly, const Path& path, Paths& solution, bool isClosed); -void MinkowskiDiff(const Path& poly, const Path& path, Paths& solution, bool isClosed); +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, + Paths& solution, PolyFillType pathFillType, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); @@ -259,7 +262,6 @@ public: PolyTree &polytree, PolyFillType subjFillType = pftEvenOdd, PolyFillType clipFillType = pftEvenOdd); - void Clear(); bool ReverseSolution() {return m_ReverseOutput;}; void ReverseSolution(bool value) {m_ReverseOutput = value;}; bool StrictlySimple() {return m_StrictSimple;}; diff --git a/src/renderer.h b/src/renderer.h index 35b1845d..992ef347 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -13,7 +13,8 @@ class Renderer public: virtual ~Renderer() {} virtual void draw(bool showfaces, bool showedges) const = 0; - + virtual BoundingBox getBoundingBox() const = 0; + #define CSGMODE_DIFFERENCE_FLAG 0x10 enum csgmode_e { CSGMODE_NONE = 0x00, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3d571327..b563b255 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -138,7 +138,6 @@ if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-extensions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") endif() @@ -153,27 +152,34 @@ function(inclusion user_set_path found_paths) # their set_paths to be a priority. if (DEBUG_OSCD) - message(STATUS "inclusion ${user_set_path} ${found_paths}") - message(STATUS "inclusion ${${user_set_path}} ${${found_paths}}") + message(STATUS "inclusion:") + message(STATUS " ${user_set_path}: ${${user_set_path}}") + message(STATUS " ${found_paths}: ${${found_paths}}") endif() - set( inclusion_match 0 ) - foreach( found_path ${${found_paths}} ) - if (${found_path} MATCHES ${${user_set_path}}.*) - set( inclusion_match 1 ) + set(inclusion_match 0) + if (${user_set_path}) + foreach(found_path ${${found_paths}}) + string(FIND ${found_path} ${${user_set_path}} INDEX) + if (DEFINED INDEX) + if (${INDEX} EQUAL 0) + set(inclusion_match 1) + endif() + endif() + endforeach() + if (inclusion_match) + include_directories(BEFORE ${${found_paths}}) + if (DEBUG_OSCD) + message(STATUS "inclusion prepend ${${found_paths}} for ${user_set_path}") + endif() + set(inclusion_match 0) endif() - endforeach() - if (user_set_path AND inclusion_match) - include_directories(BEFORE ${${found_paths}}) - if (DEBUG_OSCD) - message(STATUS "inclusion prepend ${${found_paths}} for ${user_set_path}") - endif() - else() + endif() + if (NOT inclusion_match) include_directories(AFTER ${${found_paths}}) if (DEBUG_OSCD) message(STATUS "inclusion append ${${found_paths}} for ${user_set_path}") endif() endif() - set( inclusion_match 0 ) endfunction() # Boost @@ -208,14 +214,26 @@ if (NOT $ENV{BOOSTDIR} STREQUAL "") endif() find_package( Boost 1.35.0 COMPONENTS thread program_options filesystem system regex REQUIRED) -message(STATUS "Boost includes found: " ${Boost_INCLUDE_DIRS}) +message(STATUS "Boost ${Boost_VERSION} includes found: " ${Boost_INCLUDE_DIRS}) message(STATUS "Boost libraries found:") foreach(boostlib ${Boost_LIBRARIES}) message(STATUS " " ${boostlib}) endforeach() inclusion(BOOST_ROOT Boost_INCLUDE_DIRS) -add_definitions(-DBOOST_FILESYSTEM_VERSION=3) # Use V3 for boost 1.44-1.45 +if (${Boost_VERSION} LESS 104600) + add_definitions(-DBOOST_FILESYSTEM_VERSION=3) # Use V3 for boost 1.44-1.45 +endif() + +# On Mac, we need to link against the correct C++ library. We choose the same one +# as Boost uses. +if(APPLE) + execute_process(COMMAND grep -q __112basic_string ${Boost_LIBRARIES} + RESULT_VARIABLE BOOST_USE_STDLIBCPP) + if (NOT BOOST_USE_STDLIBCPP) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + endif() +endif() # Mac OS X if(APPLE) @@ -570,6 +588,7 @@ set(CORE_SOURCES ../src/parsersettings.cc ../src/mathc99.cc ../src/linalg.cc + ../src/Camera.cc ../src/handle_dep.cc ../src/value.cc ../src/calc.cc @@ -1095,7 +1114,7 @@ add_cmdline_test(dumptest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX csg FILES ${DUM add_cmdline_test(dumptest-examples EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX csg FILES ${EXAMPLE_FILES}) add_cmdline_test(cgalpngtest EXE ${OPENSCAD_BINPATH} ARGS --render -o SUFFIX png FILES ${CGALPNGTEST_FILES}) add_cmdline_test(opencsgtest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX png FILES ${OPENCSGTEST_FILES}) -add_cmdline_test(csgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT csg-import-test.py ARGS ${OPENSCAD_BINPATH} EXPECTEDDIR cgalpngtest SUFFIX png FILES ${CGALPNGTEST_FILES}) +add_cmdline_test(csgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=csg EXPECTEDDIR cgalpngtest SUFFIX png FILES ${CGALPNGTEST_FILES}) add_cmdline_test(throwntogethertest EXE ${OPENSCAD_BINPATH} ARGS --preview=throwntogether -o SUFFIX png FILES ${THROWNTOGETHERTEST_FILES}) # FIXME: We don't actually need to compare the output of cgalstlsanitytest # with anything. It's self-contained and returns != 0 on error @@ -1148,36 +1167,64 @@ add_cmdline_test(openscad-imgstretch2 EXE ${OPENSCAD_BINPATH} ARGS --imgsize 100,500 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective gimbal cam add_cmdline_test(openscad-camdist EXE ${OPENSCAD_BINPATH} - ARGS --imgsize=500,500 --camera=0,0,0,90,0,90,300 -o + ARGS --imgsize=500,500 --camera=0,0,0,90,0,90,100 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective gimbal cam add_cmdline_test(openscad-camrot EXE ${OPENSCAD_BINPATH} - ARGS --imgsize=500,500 --camera=0,0,0,440,337.5,315,300 -o + ARGS --imgsize=500,500 --camera=0,0,0,440,337.5,315,100 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective gimbal cam add_cmdline_test(openscad-camtrans EXE ${OPENSCAD_BINPATH} - ARGS --imgsize=500,500 --camera=100,-20,-10,90,0,90,300 -o + ARGS --imgsize=500,500 --camera=100,-20,-10,90,0,90,100 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective gimbal cam, viewall +add_cmdline_test(openscad-camtrans-viewall EXE ${OPENSCAD_BINPATH} + ARGS --imgsize=500,500 --camera=100,-20,-10,90,0,90,3000 --viewall -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Orthographic gimbal cam add_cmdline_test(openscad-camortho EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=100,-20,-10,90,0,90,300 --projection=o -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Orthographic gimbal cam viewall +add_cmdline_test(openscad-camortho-viewall EXE ${OPENSCAD_BINPATH} + ARGS --imgsize=500,500 --camera=100,-20,-10,90,0,90,3000 --viewall --projection=o -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective vector cam add_cmdline_test(openscad-cameye EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=60,40,30,0,0,0 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective vector cam add_cmdline_test(openscad-cameye2 EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=160,140,130,0,0,0 -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective vector cam +add_cmdline_test(openscad-camcenter EXE ${OPENSCAD_BINPATH} + ARGS --imgsize=500,500 --camera=60,40,30,20,10,30 -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Perspective vector cam viewall +add_cmdline_test(openscad-camcenter-viewall EXE ${OPENSCAD_BINPATH} + ARGS --imgsize=500,500 --camera=60,40,30,20,10,30 --viewall -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) +# Orthographic vector cam add_cmdline_test(openscad-cameyeortho EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=160,140,130,0,0,0 --projection=o -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) -add_cmdline_test(openscad-camcenter EXE ${OPENSCAD_BINPATH} - ARGS --imgsize=500,500 --camera=60,40,30,20,10,30 -o +# Orthographic vector cam viewall +add_cmdline_test(openscad-cameyeortho-viewall EXE ${OPENSCAD_BINPATH} + ARGS --imgsize=500,500 --camera=16,14,13,0,0,0 --viewall --projection=o -o SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) @@ -1187,3 +1234,5 @@ message("Available test configurations: ${TEST_CONFIGS}") #foreach(CONF ${TEST_CONFIGS}) # message("${CONF}: ${${CONF}_TEST_CONFIG}") #endforeach() + +message("CPPFLAGS: ${CMAKE_CXX_FLAGS}") \ No newline at end of file diff --git a/tests/csg-import-test.py b/tests/csg-import-test.py deleted file mode 100755 index 2473e263..00000000 --- a/tests/csg-import-test.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -import sys, os, re, subprocess - -scad = sys.argv[1] -bin = sys.argv[2] -png = sys.argv[3] -csg = re.sub(r"\.scad$", ".csg", scad) - -print(bin, scad, csg, png); - -subprocess.call([bin, scad, '-o', csg]) -subprocess.call([bin, csg, '--render', '-o', png]) -os.remove(csg); diff --git a/tests/export_import_pngtest.py b/tests/export_import_pngtest.py new file mode 100644 index 00000000..57f4006d --- /dev/null +++ b/tests/export_import_pngtest.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +# Export-import test +# +# +# Usage: