From 6940d171812565209efe679a5d923417c3f47d4a Mon Sep 17 00:00:00 2001 From: kintel Date: Sat, 30 Jan 2010 04:17:05 +0000 Subject: reorganized file structure layout. more to follow... git-svn-id: http://svn.clifford.at/openscad/trunk@364 b57f626f-c46c-0410-a088-ec61d464b74c diff --git a/CGAL-OGL-Tess-Combine-Fix.patch b/CGAL-OGL-Tess-Combine-Fix.patch deleted file mode 100644 index 2a4ad1a..0000000 --- a/CGAL-OGL-Tess-Combine-Fix.patch +++ /dev/null @@ -1,43 +0,0 @@ ---- CGAL-3.4/include/CGAL/Nef_3/OGL_helper.h -+++ CGAL-3.4/include/CGAL/Nef_3/OGL_helper.h -@@ -243,6 +243,23 @@ - glVertex3dv(pc); - } - -+ inline void CGAL_GLU_TESS_CALLBACK combineCallback(GLdouble coords[3], GLvoid *d[4], GLfloat w[4], GLvoid **dataOut) -+ { -+ static std::list pcache; -+ if (dataOut) { -+ GLdouble *n = new GLdouble[3]; -+ n[0] = coords[0]; -+ n[1] = coords[1]; -+ n[2] = coords[2]; -+ pcache.push_back(n); -+ *dataOut = n; -+ } else { -+ for (std::list::const_iterator i = pcache.begin(); i != pcache.end(); i++) -+ delete[] *i; -+ pcache.clear(); -+ } -+ } -+ - - enum { SNC_AXES}; - enum { SNC_BOUNDARY, SNC_SKELETON }; -@@ -376,6 +393,8 @@ - GLUtesselator* tess_ = gluNewTess(); - gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &vertexCallback); -+ gluTessCallback(tess_, GLenum(GLU_TESS_COMBINE), -+ (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &combineCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_BEGIN), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &beginCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_END), -@@ -410,6 +429,7 @@ - gluTessEndPolygon(tess_); - // CGAL_NEF_TRACEN("End Polygon"); - gluDeleteTess(tess_); -+ combineCallback(NULL, NULL, NULL, NULL); - } - - void construct_axes() const diff --git a/CGAL-Valgrind-Enable-Hack.patch b/CGAL-Valgrind-Enable-Hack.patch deleted file mode 100644 index 90733fc..0000000 --- a/CGAL-Valgrind-Enable-Hack.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- CGAL-3.4/include/CGAL/Interval_nt.h -+++ CGAL-3.4/include/CGAL/Interval_nt.h -@@ -149,11 +149,13 @@ - // The macros CGAL_IA_MUL and CGAL_IA_DIV stop constant propagation only - // on the second argument, so if -fno-rounding-math, the compiler optimizes - // the 2 negations and we get wrong rounding. -+#if 0 - typename Interval_nt<>::Internal_protector P; - CGAL_assertion_msg(-CGAL_IA_MUL(-1.1, 10.1) != CGAL_IA_MUL(1.1, 10.1), - "Wrong rounding: did you forget the -frounding-math option if you use GCC?"); - CGAL_assertion_msg(-CGAL_IA_DIV(-1, 10) != CGAL_IA_DIV(1, 10), - "Wrong rounding: did you forget the -frounding-math option if you use GCC?"); -+#endif - } - }; - diff --git a/CGAL_renderer.h b/CGAL_renderer.h deleted file mode 100644 index 3c36db4..0000000 --- a/CGAL_renderer.h +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright (c) 1997-2002 Max-Planck-Institute Saarbruecken (Germany). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org); you may redistribute it under -// the terms of the Q Public License version 1.0. -// See the file LICENSE.QPL distributed with CGAL. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -// -// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.5-branch/Nef_3/include/CGAL/Nef_3/OGL_helper.h $ -// $Id: OGL_helper.h 44713 2008-08-01 15:38:58Z hachenb $ -// -// -// Author(s) : Peter Hachenberger - -#ifndef CGAL_NEF_OPENGL_HELPER_H -#define CGAL_NEF_OPENGL_HELPER_H - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#define CGAL_GLU_TESS_CALLBACK CALLBACK -#else -#define CGAL_GLU_TESS_CALLBACK -#endif - -#ifdef __APPLE__ -# include -#endif - -#if defined __APPLE__ && !defined MAC_OS_X_VERSION_10_5 -#define CGAL_GLU_TESS_DOTS ... -#else -#define CGAL_GLU_TESS_DOTS -#endif - -using namespace CGAL; -using namespace CGAL::OGL; - -namespace OpenSCAD { - - namespace OGL { - -// ---------------------------------------------------------------------------- -// Drawable double types: -// ---------------------------------------------------------------------------- - - typedef CGAL::Simple_cartesian DKernel; - typedef DKernel::Point_3 Double_point; - typedef DKernel::Vector_3 Double_vector; - typedef DKernel::Segment_3 Double_segment; - typedef DKernel::Aff_transformation_3 Affine_3; - - // DPoint = a double point including a mark - class DPoint : public Double_point { - bool m_; - public: - DPoint() {} - DPoint(const Double_point& p, bool m) : Double_point(p) { m_ = m; } - DPoint(const DPoint& p) : Double_point(p) { m_ = p.m_; } - DPoint& operator=(const DPoint& p) - { Double_point::operator=(p); m_ = p.m_; return *this; } - bool mark() const { return m_; } - }; - - // DSegment = a double segment including a mark - class DSegment : public Double_segment { - bool m_; - public: - DSegment() {} - DSegment(const Double_segment& s, bool m) : Double_segment(s) { m_ = m; } - DSegment(const DSegment& s) : Double_segment(s) { m_ = s.m_; } - DSegment& operator=(const DSegment& s) - { Double_segment::operator=(s); m_ = s.m_; return *this; } - bool mark() const { return m_; } - }; - - // Double_triple = a class that stores a triple of double - // coordinates; we need a pointer to the coordinates in a C array - // for OpenGL - class Double_triple { - typedef double* double_ptr; - typedef const double* const_double_ptr; - double coords_[3]; - public: - Double_triple() - { coords_[0]=coords_[1]=coords_[2]=0.0; } - Double_triple(double x, double y, double z) - { coords_[0]=x; coords_[1]=y; coords_[2]=z; } - Double_triple(const Double_triple& t) - { coords_[0]=t.coords_[0]; - coords_[1]=t.coords_[1]; - coords_[2]=t.coords_[2]; - } - Double_triple& operator=(const Double_triple& t) - { coords_[0]=t.coords_[0]; - coords_[1]=t.coords_[1]; - coords_[2]=t.coords_[2]; - return *this; } - operator double_ptr() const - { return const_cast(*this).coords_; } - double operator[](unsigned i) - { CGAL_assertion(i<3); return coords_[i]; } - }; // Double_triple - - static std::ostream& operator << (std::ostream& os, - const Double_triple& t) - { os << "(" << t[0] << "," << t[1] << "," << t[2] << ")"; - return os; } - - - // DFacet stores the facet cycle vertices in a continuus C array - // of three double components, this is necessary due to the OpenGL - // tesselator input format ! - class DFacet { - typedef std::vector Coord_vector; - typedef std::vector Cycle_vector; - Coord_vector coords_; // stores all vertex coordinates - Cycle_vector fc_ends_; // stores entry points of facet cycles - Double_triple normal_; // stores normal and mark of facet - bool mark_; - - public: - typedef Coord_vector::iterator Coord_iterator; - typedef Coord_vector::const_iterator Coord_const_iterator; - - DFacet() {} - - void push_back_vertex(double x, double y, double z) - { coords_.push_back(Double_triple(x,y,z)); } - - DFacet(const DFacet& f) - { coords_ = f.coords_; - fc_ends_ = f.fc_ends_; - normal_ = f.normal_; - mark_ = f.mark_; - } - - DFacet& operator=(const DFacet& f) - { coords_ = f.coords_; - fc_ends_ = f.fc_ends_; - normal_ = f.normal_; - mark_ = f.mark_; - return *this; - } - - ~DFacet() - { coords_.clear(); fc_ends_.clear(); } - - void push_back_vertex(const Double_point& p) - { push_back_vertex(p.x(),p.y(),p.z()); } - - void set_normal(double x, double y, double z, bool m) - { double l = sqrt(x*x + y*y + z*z); - normal_ = Double_triple(x/l,y/l,z/l); mark_ = m; } - - double dx() const { return normal_[0]; } - double dy() const { return normal_[1]; } - double dz() const { return normal_[2]; } - bool mark() const { return mark_; } - double* normal() const - { return static_cast(normal_); } - - void new_facet_cycle() - { fc_ends_.push_back(coords_.size()); } - - unsigned number_of_facet_cycles() const - { return fc_ends_.size(); } - - Coord_iterator facet_cycle_begin(unsigned i) - { CGAL_assertion(i(vertex)); - GLdouble* pu(static_cast(user)); - // CGAL_NEF_TRACEN("vertexCallback coord "< pcache; - if (dataOut) { - GLdouble *n = new GLdouble[3]; - n[0] = coords[0]; - n[1] = coords[1]; - n[2] = coords[2]; - pcache.push_back(n); - *dataOut = n; - } else { - for (std::list::const_iterator i = pcache.begin(); i != pcache.end(); i++) - delete[] *i; - pcache.clear(); - } - } - - - enum { SNC_AXES}; - enum { SNC_BOUNDARY, SNC_SKELETON }; - - class Polyhedron : public CGAL::OGL::OGL_base_object { - public: - std::list vertices_; - std::list edges_; - std::list halffacets_; - - GLuint object_list_; - bool init_; - - Bbox_3 bbox_; - - int style; - std::vector switches; - - typedef std::list::const_iterator Vertex_iterator; - typedef std::list::const_iterator Edge_iterator; - typedef std::list::const_iterator Halffacet_iterator; - - enum RenderColor { - CGAL_NEF3_MARKED_VERTEX_COLOR, - CGAL_NEF3_MARKED_EDGE_COLOR, - CGAL_NEF3_MARKED_FACET_COLOR, - CGAL_NEF3_UNMARKED_VERTEX_COLOR, - CGAL_NEF3_UNMARKED_EDGE_COLOR, - CGAL_NEF3_UNMARKED_FACET_COLOR, - NUM_COLORS - }; - static unsigned char colors[NUM_COLORS][3]; - public: - Polyhedron() : bbox_(-1,-1,-1,1,1,1), switches(1) { - object_list_ = 0; - init_ = false; - style = SNC_BOUNDARY; - switches[SNC_AXES] = false; - } - - ~Polyhedron() - { if (object_list_) glDeleteLists(object_list_, 4); } - - void push_back(const Double_point& p, bool m) { - vertices_.push_back(DPoint(p,m)); - } - void push_back(const Double_segment& s, bool m) - { edges_.push_back(DSegment(s,m)); } - void push_back(const DFacet& f) - { halffacets_.push_back(f); } - - void toggle(int index) { - switches[index] = !switches[index]; - } - - void set_style(int index) { - style = index; - } - - bool is_initialized() const { return init_; } - - Bbox_3 bbox() const { return bbox_; } - Bbox_3& bbox() { return bbox_; } - - void draw(Vertex_iterator v) const { - // CGAL_NEF_TRACEN("drawing vertex "<<*v); - unsigned char *c = v->mark() ? colors[CGAL_NEF3_UNMARKED_VERTEX_COLOR] : colors[CGAL_NEF3_MARKED_VERTEX_COLOR]; - glPointSize(10); - glColor3ubv(c); - glBegin(GL_POINTS); - glVertex3d(v->x(),v->y(),v->z()); -#ifdef CGAL_NEF_EMPHASIZE_VERTEX - glColor3ub(255,0,0); - glVertex3d(CGAL_NEF_EMPHASIZE_VERTEX); -#endif - glEnd(); - } - - void draw(Edge_iterator e) const { - // CGAL_NEF_TRACEN("drawing edge "<<*e); - Double_point p = e->source(), q = e->target(); - unsigned char *c = e->mark() ? colors[CGAL_NEF3_UNMARKED_EDGE_COLOR] : colors[CGAL_NEF3_MARKED_EDGE_COLOR]; - glLineWidth(5); - glColor3ubv(c); - glBegin(GL_LINE_STRIP); - glVertex3d(p.x(), p.y(), p.z()); - glVertex3d(q.x(), q.y(), q.z()); - glEnd(); - } - - void draw(Halffacet_iterator f) const { - // CGAL_NEF_TRACEN("drawing facet "<<(f->debug(),"")); - GLUtesselator* tess_ = gluNewTess(); - gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &vertexCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_COMBINE), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &combineCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_BEGIN), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &beginCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_END), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &endCallback); - gluTessCallback(tess_, GLenum(GLU_TESS_ERROR), - (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &errorCallback); - gluTessProperty(tess_, GLenum(GLU_TESS_WINDING_RULE), - GLU_TESS_WINDING_POSITIVE); - - DFacet::Coord_const_iterator cit; - unsigned char *c = f->mark() ? colors[CGAL_NEF3_UNMARKED_FACET_COLOR] : colors[CGAL_NEF3_MARKED_FACET_COLOR]; - glColor3ubv(c); - gluTessBeginPolygon(tess_,f->normal()); - // CGAL_NEF_TRACEN(" "); - // CGAL_NEF_TRACEN("Begin Polygon"); - gluTessNormal(tess_,f->dx(),f->dy(),f->dz()); - // forall facet cycles of f: - for(unsigned i = 0; i < f->number_of_facet_cycles(); ++i) { - gluTessBeginContour(tess_); - // CGAL_NEF_TRACEN(" Begin Contour"); - // put all vertices in facet cycle into contour: - for(cit = f->facet_cycle_begin(i); - cit != f->facet_cycle_end(i); ++cit) { - gluTessVertex(tess_, *cit, *cit); - // CGAL_NEF_TRACEN(" add Vertex"); - } - gluTessEndContour(tess_); - // CGAL_NEF_TRACEN(" End Contour"); - } - gluTessEndPolygon(tess_); - // CGAL_NEF_TRACEN("End Polygon"); - gluDeleteTess(tess_); - combineCallback(NULL, NULL, NULL, NULL); - } - - void construct_axes() const - { - glLineWidth(2.0); - // red x-axis - glColor3f(1.0,0.0,0.0); - glBegin(GL_LINES); - glVertex3f(0.0,0.0,0.0); - glVertex3f(5000.0,0.0,0.0); - glEnd(); - // green y-axis - glColor3f(0.0,1.0,0.0); - glBegin(GL_LINES); - glVertex3f(0.0,0.0,0.0); - glVertex3f(0.0,5000.0,0.0); - glEnd(); - // blue z-axis and equator - glColor3f(0.0,0.0,1.0); - glBegin(GL_LINES); - glVertex3f(0.0,0.0,0.0); - glVertex3f(0.0,0.0,5000.0); - glEnd(); - // six coordinate points in pink: - glPointSize(10); - glBegin(GL_POINTS); - glColor3f(1.0,0.0,0.0); - glVertex3d(5,0,0); - glColor3f(0.0,1.0,0.0); - glVertex3d(0,5,0); - glColor3f(0.0,0.0,1.0); - glVertex3d(0,0,5); - glEnd(); - } - - - void fill_display_lists() { - glNewList(object_list_, GL_COMPILE); - Vertex_iterator v; - for(v=vertices_.begin();v!=vertices_.end();++v) - draw(v); - glEndList(); - - glNewList(object_list_+1, GL_COMPILE); - Edge_iterator e; - for(e=edges_.begin();e!=edges_.end();++e) - draw(e); - glEndList(); - - glNewList(object_list_+2, GL_COMPILE); - Halffacet_iterator f; - for(f=halffacets_.begin();f!=halffacets_.end();++f) - draw(f); - glEndList(); - - glNewList(object_list_+3, GL_COMPILE); // axes: - construct_axes(); - glEndList(); - - } - - void init() { - if (init_) return; - init_ = true; - switches[SNC_AXES] = false; - style = SNC_BOUNDARY; - object_list_ = glGenLists(4); - CGAL_assertion(object_list_); - fill_display_lists(); - } - - - void draw() const - { - if (!is_initialized()) const_cast(*this).init(); - double l = (std::max)( (std::max)( bbox().xmax() - bbox().xmin(), - bbox().ymax() - bbox().ymin()), - bbox().zmax() - bbox().zmin()); - if ( l < 1) // make sure that a single point doesn't screw up here - l = 1; - glScaled( 4.0/l, 4.0/l, 4.0/l); - glTranslated( -(bbox().xmax() + bbox().xmin()) / 2.0, - -(bbox().ymax() + bbox().ymin()) / 2.0, - -(bbox().zmax() + bbox().zmin()) / 2.0); - if (style == SNC_BOUNDARY) { - //glEnable(GL_LIGHTING); - glCallList(object_list_+2); // facets - //glDisable(GL_LIGHTING); - } - // move edges and vertices a bit towards the view-point, - // i.e., 1/100th of the unit vector in camera space - // double f = l / 4.0 / 100.0; - // glTranslated( z_vec[0] * f, z_vec[1] * f, z_vec[2] * f); - glCallList(object_list_+1); // edges - glCallList(object_list_); // vertices - if (switches[SNC_AXES]) glCallList(object_list_+3); // axis - } - - void debug(std::ostream& os = std::cerr) const - { - os << "OGL::Polyhedron" << std::endl; - os << "Vertices:" << std::endl; - Vertex_iterator v; - for(v=vertices_.begin();v!=vertices_.end();++v) - os << " "<<*v<<", mark="<mark()<debug(); os << std::endl; - os << std::endl; - } - - }; // Polyhedron - unsigned char Polyhedron::colors[][3] = { - {0xb7, 0xe8, 0x5c}, - {0xab, 0xd8, 0x56}, - {0x9d, 0xcb, 0x51}, - {0xff, 0xf6, 0x7c}, - {0xff, 0xec, 0x5e}, - {0xf9, 0xd7, 0x2c} - }; - - template - class Nef3_Converter { - typedef typename Nef_polyhedron::SNC_structure SNC_structure; - typedef CGAL::SNC_decorator Base; - typedef CGAL::SNC_FM_decorator FM_decorator; - - public: - typedef typename SNC_structure::Vertex_const_iterator Vertex_const_iterator; - typedef typename SNC_structure::Halfedge_const_iterator Halfedge_const_iterator; - typedef typename SNC_structure::Halffacet_const_iterator Halffacet_const_iterator; - typedef typename SNC_structure::Halffacet_cycle_const_iterator Halffacet_cycle_const_iterator; - - typedef typename SNC_structure::Object_const_handle Object_const_handle; - typedef typename SNC_structure::SHalfedge_const_handle SHalfedge_const_handle; - typedef typename SNC_structure::SHalfloop_const_handle SHalfloop_const_handle; - - typedef typename SNC_structure::Vertex_const_handle Vertex_const_handle; - typedef typename SNC_structure::Halfedge_const_handle Halfedge_const_handle; - typedef typename SNC_structure::Halffacet_const_handle Halffacet_const_handle; - - typedef typename SNC_structure::Point_3 Point_3; - typedef typename SNC_structure::Vector_3 Vector_3; - typedef typename SNC_structure::Segment_3 Segment_3; - typedef typename SNC_structure::Plane_3 Plane_3; - typedef typename SNC_structure::Mark Mark; - typedef typename SNC_structure::SHalfedge_around_facet_const_circulator - SHalfedge_around_facet_const_circulator; - - private: - static OGL::Double_point double_point(const Point_3& p) - { return OGL::Double_point(CGAL::to_double(p.x()), - CGAL::to_double(p.y()), - CGAL::to_double(p.z())); } - - static OGL::Double_segment double_segment(const Segment_3& s) - { return OGL::Double_segment(double_point(s.source()), - double_point(s.target())); } - - static void draw(Vertex_const_handle v, const Nef_polyhedron& , - Polyhedron& P) { - Point_3 bp = v->point(); - // CGAL_NEF_TRACEN("vertex " << bp); - P.push_back(double_point(bp), v->mark()); - } - - static void draw(Halfedge_const_handle e, const Nef_polyhedron& , - Polyhedron& P) { - Vertex_const_handle s = e->source(); - Vertex_const_handle t = e->twin()->source(); - Segment_3 seg(s->point(),t->point()); - // CGAL_NEF_TRACEN("edge " << seg); - P.push_back(double_segment(seg), e->mark()); - } - - static void draw(Halffacet_const_handle f, const Nef_polyhedron& , - Polyhedron& P) { - OGL::DFacet g; - Halffacet_cycle_const_iterator fc; // all facet cycles: - CGAL_forall_facet_cycles_of(fc,f) - if ( fc.is_shalfedge() ) { // non-trivial facet cycle - g.new_facet_cycle(); - SHalfedge_const_handle h = fc; - SHalfedge_around_facet_const_circulator hc(h), he(hc); - CGAL_For_all(hc,he){ // all vertex coordinates in facet cycle - Point_3 sp = hc->source()->source()->point(); - // CGAL_NEF_TRACEN(" ");CGAL_NEF_TRACEN("facet" << sp); - g.push_back_vertex(double_point(sp)); - } - } - Vector_3 v = f->plane().orthogonal_vector(); - g.set_normal(CGAL::to_double(v.x()), - CGAL::to_double(v.y()), - CGAL::to_double(v.z()), - f->mark()); - P.push_back(g); - } - - // Returns the bounding box of the finite vertices of the polyhedron. - // Returns $[-1,+1]^3$ as bounding box if no finite vertex exists. - - static Bbox_3 bounded_bbox(const Nef_polyhedron& N) { - bool first_vertex = true; - Bbox_3 bbox( -1.0, -1.0, -1.0, 1.0, 1.0, 1.0); - Vertex_const_iterator vi; - CGAL_forall_vertices(vi, N) { - Point_3 p = vi->point(); - double x = CGAL::to_double(p.hx()); - double y = CGAL::to_double(p.hy()); - double z = CGAL::to_double(p.hz()); - double w = CGAL::to_double(p.hw()); - if (N.is_standard(vi)) { - if(first_vertex) { - bbox = Bbox_3(x/w, y/w, z/w, x/w, y/w, z/w); - first_vertex = false; - } else { - bbox = bbox + Bbox_3(x/w, y/w, z/w, x/w, y/w, z/w); - first_vertex = false; - } - } - } - return bbox; - } - - static void set_R(Bbox_3& bbox, const Nef_polyhedron& N) { - if(N.is_standard_kernel()) return; - double size = abs(bbox.xmin()); - if(size < bbox.xmax()) size = bbox.xmax(); - if(size < bbox.ymin()) size = bbox.ymin(); - if(size < bbox.ymax()) size = bbox.ymax(); - if(size < bbox.zmin()) size = bbox.zmin(); - if(size < bbox.zmax()) size = bbox.zmax(); - N.set_size_of_infimaximal_box(size*50); - // CGAL_NEF_TRACEN("set infi box size to " << size); - Vertex_const_iterator vi; - CGAL_forall_vertices(vi, N) - if(N.is_standard(vi)) - return; - bbox = Bbox_3(bbox.xmin()*10,bbox.ymin()*10,bbox.zmin()*10, - bbox.xmax()*10,bbox.ymax()*10,bbox.zmax()*10); - } - public: - static void setColor(Polyhedron::RenderColor color_index, - unsigned char r, unsigned char g, unsigned char b) { - assert(color_index < Polyhedron::NUM_COLORS); - Polyhedron::colors[color_index][0] = r; - Polyhedron::colors[color_index][1] = g; - Polyhedron::colors[color_index][2] = b; - } - - static void convert_to_OGLPolyhedron(const Nef_polyhedron& N, Polyhedron* P) { - Bbox_3 bbox(bounded_bbox(N)); - set_R(bbox,N); - P->bbox() = bbox; - Vertex_const_iterator v; - CGAL_forall_vertices(v,*N.sncp()) draw(v,N,*P); - Halfedge_const_iterator e; - CGAL_forall_edges(e,*N.sncp()) draw(e,N,*P); - Halffacet_const_iterator f; - CGAL_forall_facets(f,*N.sncp()) draw(f,N,*P); - } - - }; // Nef3_Converter - - } // namespace OGL - -} // namespace OpenSCAD - -#endif // CGAL_NEF_OPENGL_HELPER_H diff --git a/EventFilter.h b/EventFilter.h deleted file mode 100644 index c3942b5..0000000 --- a/EventFilter.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FILTER_H_ -#define FILTER_H_ - -#include -#include -#include "MainWindow.h" - -class EventFilter : public QObject -{ - Q_OBJECT; - -public: - EventFilter(QObject *parent) : QObject(parent) {} -protected: - bool eventFilter(QObject *obj, QEvent *event) { - // Handle Apple event for opening files - if (event->type() == QEvent::FileOpen) { - QFileOpenEvent *foe = static_cast(event); - MainWindow::requestOpenFile(foe->file()); - return true; - } else { - // standard event processing - return QObject::eventFilter(obj, event); - } - } -}; - -#endif diff --git a/GLView.h b/GLView.h deleted file mode 100644 index dd896fc..0000000 --- a/GLView.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef GLVIEW_H_ -#define GLVIEW_H_ - -#ifdef ENABLE_OPENCSG -// this must be included before the GL headers -# include -#endif - -#include -#include - -class GLView : public QGLWidget -{ - Q_OBJECT - Q_PROPERTY(bool showAxes READ showAxes WRITE setShowAxes); - Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs); - Q_PROPERTY(bool orthoMode READ orthoMode WRITE setOrthoMode); - -public: - GLView(QWidget *parent = NULL); - void setRenderFunc(void (*func)(void*), void *userdata); -#ifdef ENABLE_OPENCSG - bool hasOpenCSGSupport() { return this->opencsg_support; } -#endif - // Properties - bool showAxes() const { return this->showaxes; } - void setShowAxes(bool enabled) { this->showaxes = enabled; } - bool showCrosshairs() const { return this->showcrosshairs; } - void setShowCrosshairs(bool enabled) { this->showcrosshairs = enabled; } - bool orthoMode() const { return this->orthomode; } - void setOrthoMode(bool enabled) { this->orthomode = enabled; } - - QLabel *statusLabel; - double object_rot_x; - double object_rot_y; - double object_rot_z; - double object_trans_x; - double object_trans_y; - double object_trans_z; - GLint shaderinfo[11]; - -private: - void (*renderfunc)(void*); - void *renderfunc_vp; - - bool showaxes; - bool showcrosshairs; - bool orthomode; - - double viewer_distance; - - double w_h_ratio; - -#ifdef ENABLE_OPENCSG - bool opencsg_support; -#endif - - bool mouse_drag_active; - int last_mouse_x; - int last_mouse_y; - - void keyPressEvent(QKeyEvent *event); - void wheelEvent(QWheelEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - - void initializeGL(); - void resizeGL(int w, int h); - void paintGL(); - -#ifdef ENABLE_OPENCSG -private slots: - void display_opengl20_warning(); -#endif - -signals: - void doAnimateUpdate(); -}; - -#endif diff --git a/MainWindow.h b/MainWindow.h deleted file mode 100644 index bb1bdc4..0000000 --- a/MainWindow.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef MAINWINDOW_H_ -#define MAINWINDOW_H_ - -#include -#include "ui_MainWindow.h" -#include "openscad.h" - -class MainWindow : public QMainWindow, public Ui::MainWindow -{ - Q_OBJECT - -public: - static QPointer current_win; - static void requestOpenFile(const QString &filename); - - QString fileName; - class Highlighter *highlighter; - - class Preferences *prefs; - - QTimer *animate_timer; - double tval, fps, fsteps; - - Context root_ctx; - AbstractModule *root_module; // Result of parsing - ModuleInstantiation root_inst; // Top level instance - AbstractNode *absolute_root_node; // Result of tree evaluation - AbstractNode *root_node; // Root if the root modifier (!) is used - - CSGTerm *root_raw_term; // Result of CSG term rendering - CSGTerm *root_norm_term; // Normalized CSG products - CSGChain *root_chain; -#ifdef ENABLE_CGAL - CGAL_Nef_polyhedron *root_N; - bool recreate_cgal_ogl_p; - void *cgal_ogl_p; - PolySet *cgal_ogl_ps; -#endif - - QVector highlight_terms; - CSGChain *highlights_chain; - QVector background_terms; - CSGChain *background_chain; - QString last_compiled_doc; - bool enableOpenCSG; - - static const int maxRecentFiles = 10; - QAction *actionRecentFile[maxRecentFiles]; - QString examplesdir; - - MainWindow(const char *filename = 0); - ~MainWindow(); - -protected: - void closeEvent(QCloseEvent *event); - -private slots: - void updatedFps(); - void updateTVal(); - void setFileName(const QString &filename); - void setFont(const QString &family, uint size); - -private: - void openFile(const QString &filename); - void load(); - AbstractNode *find_root_tag(AbstractNode *n); - void compile(bool procevents); - bool maybeSave(); - -private slots: - void actionNew(); - void actionOpen(); - void actionOpenRecent(); - void actionOpenExample(); - void clearRecentFiles(); - void updateRecentFileActions(); - void actionSave(); - void actionSaveAs(); - void actionReload(); - -private slots: - void editIndent(); - void editUnindent(); - void editComment(); - void editUncomment(); - void pasteViewportTranslation(); - void pasteViewportRotation(); - void hideEditor(); - void preferences(); - -private slots: - void actionReloadCompile(); - void actionCompile(); -#ifdef ENABLE_CGAL - void actionRenderCGAL(); -#endif - void actionDisplayAST(); - void actionDisplayCSGTree(); - void actionDisplayCSGProducts(); - void actionExportSTLorOFF(bool stl_mode); - void actionExportSTL(); - void actionExportOFF(); - void actionExportDXF(); - void actionFlushCaches(); - -public: - void viewModeActionsUncheck(); - -public slots: -#ifdef ENABLE_OPENCSG - void viewModeOpenCSG(); -#endif -#ifdef ENABLE_CGAL - void viewModeCGALSurface(); - void viewModeCGALGrid(); -#endif - void viewModeThrownTogether(); - void viewModeShowEdges(); - void viewModeShowAxes(); - void viewModeShowCrosshairs(); - void viewModeAnimate(); - void viewAngleTop(); - void viewAngleBottom(); - void viewAngleLeft(); - void viewAngleRight(); - void viewAngleFront(); - void viewAngleBack(); - void viewAngleDiagonal(); - void viewCenter(); - void viewPerspective(); - void viewOrthogonal(); - void hideConsole(); - void animateUpdateDocChanged(); - void animateUpdate(); - void dragEnterEvent(QDragEnterEvent *event); - void dropEvent(QDropEvent *event); - void helpAbout(); - void helpManual(); - void quit(); -}; - -#endif diff --git a/OpenCSG-1.1.0-Reset-Hack.patch b/OpenCSG-1.1.0-Reset-Hack.patch deleted file mode 100644 index 493a11b..0000000 --- a/OpenCSG-1.1.0-Reset-Hack.patch +++ /dev/null @@ -1,82 +0,0 @@ -diff --git a/include/opencsg.h b/include/opencsg.h -index daecacc..ffde239 100644 ---- a/include/opencsg.h -+++ b/include/opencsg.h -@@ -188,6 +188,9 @@ namespace OpenCSG { - Algorithm = AlgorithmUnused, - DepthComplexityAlgorithm = NoDepthComplexitySampling); - -+ // call this function whenever switching the OpenGL context -+ void reset(); -+ - } // namespace OpenCSG - - #endif // __OpenCSG__opencsg_h__ -diff --git a/src/channelManager.h b/src/channelManager.h -index ecd5a1d..0e1458a 100644 ---- a/src/channelManager.h -+++ b/src/channelManager.h -@@ -79,9 +79,9 @@ namespace OpenCSG { - /// moved into alpha, to allow alpha testing of the channel. - static void setupTexEnv(Channel channel); - -- private: -- - static OpenGL::OffscreenBuffer* gOffscreenBuffer; -+ -+ private: - static int gOffscreenType; - static bool gInUse; - -diff --git a/src/offscreenBuffer.cpp b/src/offscreenBuffer.cpp -index e02dd83..4f978d5 100644 ---- a/src/offscreenBuffer.cpp -+++ b/src/offscreenBuffer.cpp -@@ -22,19 +22,41 @@ - #include "offscreenBuffer.h" - #include "frameBufferObject.h" - #include "pBufferTexture.h" -+#include "channelManager.h" -+ -+static bool reset_f = false; -+static bool reset_p = false; - - namespace OpenCSG { - -+ void reset() -+ { -+ reset_f = true; -+ reset_p = true; -+ OpenCSG::ChannelManager::gOffscreenBuffer = NULL; -+ } -+ - namespace OpenGL { - - OffscreenBuffer* getOffscreenBuffer(bool fbo) { -- static FrameBufferObject* f = new FrameBufferObject; -- static PBufferTexture* p = new PBufferTexture; -- -- if (fbo) -- return f; -- else -+ static FrameBufferObject* f = NULL; -+ static PBufferTexture* p = NULL; -+ -+ if (fbo) { -+ if (reset_f || f == NULL) { -+ delete f; -+ f = new FrameBufferObject; -+ reset_f = false; -+ } -+ return f; -+ } else { -+ if (reset_p || p == NULL) { -+ delete p; -+ p = new PBufferTexture; -+ reset_p = false; -+ } - return p; -+ } - } - - } // namespace OpenGL diff --git a/OpenCSG-1.1.1-MacOSX-port.patch b/OpenCSG-1.1.1-MacOSX-port.patch deleted file mode 100644 index 592cdf9..0000000 --- a/OpenCSG-1.1.1-MacOSX-port.patch +++ /dev/null @@ -1,276 +0,0 @@ -diff -ru OpenCSG-1.1.1/RenderTexture/RenderTexture.h OpenCSG-1.1.1-mac/RenderTexture/RenderTexture.h ---- OpenCSG-1.1.1/RenderTexture/RenderTexture.h 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/RenderTexture/RenderTexture.h 2009-12-09 03:15:26.000000000 +0100 -@@ -294,8 +294,8 @@ - bool _BindDepthBuffer( ) const; - - protected: // data -- int _iWidth; // width of the pbuffer -- int _iHeight; // height of the pbuffer -+ GLint _iWidth; // width of the pbuffer -+ GLint _iHeight; // height of the pbuffer - - bool _bIsTexture; - bool _bIsDepthTexture; -@@ -342,8 +342,8 @@ - - // Texture stuff - GLenum _iTextureTarget; -- unsigned int _iTextureID; -- unsigned int _iDepthTextureID; -+ GLuint _iTextureID; -+ GLuint _iDepthTextureID; - - unsigned short* _pPoorDepthTexture; // [Redge] - -diff -ru OpenCSG-1.1.1/example/example.pro OpenCSG-1.1.1-mac/example/example.pro ---- OpenCSG-1.1.1/example/example.pro 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/example/example.pro 2009-12-09 03:15:26.000000000 +0100 -@@ -2,9 +2,16 @@ - TARGET = opencsgexample - - CONFIG += opengl warn_on release --INCLUDEPATH += ../glew/include ../include -- --LIBS += -L../lib -lopencsg -lglut -L../glew/lib -lGLEW -+INCLUDEPATH += ../include -+LIBS += -L../lib -lopencsg -lGLEW -+macx { -+ INCLUDEPATH += /opt/local/include -+ LIBS += -framework GLUT -L/opt/local/lib -+} -+else { -+ INCLUDEPATH += ../glew/include -+ LIBS += -lglut -L../glew/lib -+} - - HEADERS = displaylistPrimitive.h - SOURCES = displaylistPrimitive.cpp main.cpp -diff -ru OpenCSG-1.1.1/example/main.cpp OpenCSG-1.1.1-mac/example/main.cpp ---- OpenCSG-1.1.1/example/main.cpp 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/example/main.cpp 2009-12-09 03:15:26.000000000 +0100 -@@ -22,7 +22,11 @@ - // - - #include -+#ifdef __APPLE__ -+#include -+#else - #include -+#endif - #include - #include "displaylistPrimitive.h" - #include -diff -ru OpenCSG-1.1.1/opencsg.pro OpenCSG-1.1.1-mac/opencsg.pro ---- OpenCSG-1.1.1/opencsg.pro 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/opencsg.pro 2009-12-09 03:15:26.000000000 +0100 -@@ -1,2 +1,4 @@ - TEMPLATE = subdirs - SUBDIRS = src example -+# On Mac we get glew from MacPorts -+!macx:SUBDIRS += glew -diff -ru OpenCSG-1.1.1/src/channelManager.cpp OpenCSG-1.1.1-mac/src/channelManager.cpp ---- OpenCSG-1.1.1/src/channelManager.cpp 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/channelManager.cpp 2009-12-09 03:15:26.000000000 +0100 -@@ -23,7 +23,7 @@ - #include - #ifdef _WIN32 - #include --#else -+#elif !defined(__APPLE__) - #include - #endif - -@@ -160,9 +160,11 @@ - #ifdef WIN32 - if ( WGLEW_ARB_pbuffer - && WGLEW_ARB_pixel_format --#else -+#elif !defined(__APPLE__) - if ( GLXEW_SGIX_pbuffer - && GLXEW_SGIX_fbconfig -+#else -+ if ( false - #endif - ) { - newOffscreenType = OpenCSG::PBuffer; -diff -ru OpenCSG-1.1.1/src/frameBufferObject.h OpenCSG-1.1.1-mac/src/frameBufferObject.h ---- OpenCSG-1.1.1/src/frameBufferObject.h 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/frameBufferObject.h 2009-12-09 03:15:26.000000000 +0100 -@@ -77,10 +77,10 @@ - - /// Texture stuff - GLenum textureTarget; -- unsigned int textureID; -- unsigned int depthID; -+ GLuint textureID; -+ GLuint depthID; - -- unsigned int framebufferID; -+ GLuint framebufferID; - - bool initialized; - }; -diff -ru OpenCSG-1.1.1/src/occlusionQuery.cpp OpenCSG-1.1.1-mac/src/occlusionQuery.cpp ---- OpenCSG-1.1.1/src/occlusionQuery.cpp 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/occlusionQuery.cpp 2009-12-09 03:15:26.000000000 +0100 -@@ -57,7 +57,7 @@ - } - - unsigned int OcclusionQueryARB::getQueryResult() { -- unsigned int fragmentCount; -+ GLuint fragmentCount; - glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &fragmentCount); - return fragmentCount; - } -@@ -94,7 +94,7 @@ - } - - unsigned int OcclusionQueryNV::getQueryResult() { -- unsigned int fragmentCount; -+ GLuint fragmentCount; - glGetOcclusionQueryuivNV(mQueryObject, GL_PIXEL_COUNT_NV, &fragmentCount); - return fragmentCount; - } -diff -ru OpenCSG-1.1.1/src/openglHelper.cpp OpenCSG-1.1.1-mac/src/openglHelper.cpp ---- OpenCSG-1.1.1/src/openglHelper.cpp 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/openglHelper.cpp 2009-12-09 03:15:26.000000000 +0100 -@@ -29,13 +29,13 @@ - - GLfloat projection[16]; - GLfloat modelview[16]; -- int canvasPos[4]; -+ GLint canvasPos[4]; - -- int stencilBits = 0; -+ GLint stencilBits = 0; - int stencilMax = 0; - int stencilMask = 0; - -- int scissorPos[4]; -+ GLint scissorPos[4]; - - void scissor(const PCArea& area) { - const int dx = area.maxx - area.minx; -diff -ru OpenCSG-1.1.1/src/openglHelper.h OpenCSG-1.1.1-mac/src/openglHelper.h ---- OpenCSG-1.1.1/src/openglHelper.h 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/openglHelper.h 2009-12-09 03:15:26.000000000 +0100 -@@ -36,17 +36,17 @@ - // copy of the projection matrix during CSG computation - extern GLfloat modelview[16]; - // copy of the modelview matrix during CSG computation -- extern int canvasPos[4]; -+ extern GLint canvasPos[4]; - // copy of the viewport size during CSG computation - -- extern int stencilBits; -+ extern GLint stencilBits; - // number of stencil bits in the pbuffer - extern int stencilMax; - // the number where the stencil value would "wrap around" to zero - extern int stencilMask; - // stencilMax - 1 - -- extern int scissorPos[4]; -+ extern GLint scissorPos[4]; - // copy of the scissor settings for CSG computation - - void scissor(const PCArea& area); -diff -ru OpenCSG-1.1.1/src/pBufferTexture.h OpenCSG-1.1.1-mac/src/pBufferTexture.h ---- OpenCSG-1.1.1/src/pBufferTexture.h 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/pBufferTexture.h 2009-12-09 03:15:34.000000000 +0100 -@@ -22,7 +22,7 @@ - - #ifndef __OpenCSG__pbuffer_texture_h__ - #define __OpenCSG__pbuffer_texture_h__ -- -+#ifndef __APPLE__ - #include "opencsgConfig.h" - #include "offscreenBuffer.h" - -@@ -81,5 +81,26 @@ - } // namespace OpenGL - - } // namespace OpenCSG -+#else -+ -+namespace OpenCSG { -+ namespace OpenGL { -+ class PBufferTexture : public OffscreenBuffer { -+ virtual bool Initialize(int , int , bool , bool ) {return false;} -+ virtual bool Reset() {return false;} -+ virtual bool Resize(int, int) {return false;} -+ virtual bool BeginCapture() {return false;} -+ virtual bool EndCapture() {return false;} -+ virtual void Bind() const {} -+ virtual void EnableTextureTarget() const {} -+ virtual void DisableTextureTarget() const {} -+ virtual unsigned int GetTextureTarget() const {return 0;} -+ virtual int GetWidth() const {return 0;} -+ virtual int GetHeight() const {return 0;} -+ virtual bool haveSeparateContext() const {return false;} -+ }; -+ } -+} - -+#endif // __APPLE__ - #endif // __OpenCSG__frame_buffer_object_h__ -Only in OpenCSG-1.1.1-mac/src: pBufferTexture.h~ -diff -ru OpenCSG-1.1.1/src/src.pro OpenCSG-1.1.1-mac/src/src.pro ---- OpenCSG-1.1.1/src/src.pro 2009-07-19 21:05:09.000000000 +0200 -+++ OpenCSG-1.1.1-mac/src/src.pro 2009-12-09 03:15:26.000000000 +0100 -@@ -4,7 +4,15 @@ - DESTDIR = ../lib - - CONFIG += opengl warn_on release --INCLUDEPATH += ../include ../glew/include ../ -+INCLUDEPATH += ../include ../ -+ -+macx { -+ INCLUDEPATH += /opt/local/include -+ LIBS += -L/opt/local/lib -lglew -+} -+else { -+INCLUDEPATH += ../glew/include -+} - - HEADERS = ../include/opencsg.h \ - opencsgConfig.h \ -@@ -16,12 +24,11 @@ - offscreenBuffer.h \ - opencsgRender.h \ - openglHelper.h \ -- pBufferTexture.h \ - primitiveHelper.h \ - scissorMemo.h \ - settings.h \ -- stencilManager.h \ -- ../RenderTexture/RenderTexture.h -+ stencilManager.h -+ - SOURCES = area.cpp \ - batch.cpp \ - channelManager.cpp \ -@@ -30,12 +37,19 @@ - offscreenBuffer.cpp \ - opencsgRender.cpp \ - openglHelper.cpp \ -- pBufferTexture.cpp \ - primitive.cpp \ - primitiveHelper.cpp \ - renderGoldfeather.cpp \ - renderSCS.cpp \ - scissorMemo.cpp \ - settings.cpp \ -- stencilManager.cpp \ -- ../RenderTexture/RenderTexture.cpp -+ stencilManager.cpp -+ -+!macx { -+ HEADERS += ../RenderTexture/RenderTexture.h \ -+ pBufferTexture.h -+ -+ SOURCES += ../RenderTexture/RenderTexture.cpp \ -+ pBufferTexture.cpp -+} -+ diff --git a/OpenCSG-1.2.0-MacOSX-port.patch b/OpenCSG-1.2.0-MacOSX-port.patch deleted file mode 100644 index 3f1bdc4..0000000 --- a/OpenCSG-1.2.0-MacOSX-port.patch +++ /dev/null @@ -1,285 +0,0 @@ -diff -ru OpenCSG-1.2.0/RenderTexture/RenderTexture.h OpenCSG-1.2.0-mac/RenderTexture/RenderTexture.h ---- OpenCSG-1.2.0/RenderTexture/RenderTexture.h 2010-01-02 20:56:12.000000000 +0100 -+++ OpenCSG-1.2.0-mac/RenderTexture/RenderTexture.h 2010-01-24 23:30:24.000000000 +0100 -@@ -294,8 +294,8 @@ - bool _BindDepthBuffer( ) const; - - protected: // data -- int _iWidth; // width of the pbuffer -- int _iHeight; // height of the pbuffer -+ GLint _iWidth; // width of the pbuffer -+ GLint _iHeight; // height of the pbuffer - - bool _bIsTexture; - bool _bIsDepthTexture; -@@ -342,8 +342,8 @@ - - // Texture stuff - GLenum _iTextureTarget; -- unsigned int _iTextureID; -- unsigned int _iDepthTextureID; -+ GLuint _iTextureID; -+ GLuint _iDepthTextureID; - - unsigned short* _pPoorDepthTexture; // [Redge] - -diff -ru OpenCSG-1.2.0/example/example.pro OpenCSG-1.2.0-mac/example/example.pro ---- OpenCSG-1.2.0/example/example.pro 2010-01-02 20:56:12.000000000 +0100 -+++ OpenCSG-1.2.0-mac/example/example.pro 2010-01-24 23:30:24.000000000 +0100 -@@ -2,9 +2,16 @@ - TARGET = opencsgexample - - CONFIG += opengl warn_on release --INCLUDEPATH += ../glew/include ../include -- --LIBS += -L../lib -lopencsg -lglut -L../glew/lib -lGLEW -+INCLUDEPATH += ../include -+LIBS += -L../lib -lopencsg -lGLEW -+macx { -+ INCLUDEPATH += /opt/local/include -+ LIBS += -framework GLUT -L/opt/local/lib -+} -+else { -+ INCLUDEPATH += ../glew/include -+ LIBS += -lglut -L../glew/lib -+} - - HEADERS = displaylistPrimitive.h - SOURCES = displaylistPrimitive.cpp main.cpp -diff -ru OpenCSG-1.2.0/example/main.cpp OpenCSG-1.2.0-mac/example/main.cpp ---- OpenCSG-1.2.0/example/main.cpp 2010-01-02 21:03:19.000000000 +0100 -+++ OpenCSG-1.2.0-mac/example/main.cpp 2010-01-24 23:30:24.000000000 +0100 -@@ -22,7 +22,11 @@ - // - - #include -+#ifdef __APPLE__ -+#include -+#else - #include -+#endif - #include - #include "displaylistPrimitive.h" - #include -diff -ru OpenCSG-1.2.0/opencsg.pro OpenCSG-1.2.0-mac/opencsg.pro ---- OpenCSG-1.2.0/opencsg.pro 2010-01-02 20:56:12.000000000 +0100 -+++ OpenCSG-1.2.0-mac/opencsg.pro 2010-01-24 23:30:24.000000000 +0100 -@@ -1,2 +1,4 @@ - TEMPLATE = subdirs - SUBDIRS = src example -+# On Mac we get glew from MacPorts -+!macx:SUBDIRS += glew -diff -ru OpenCSG-1.2.0/src/channelManager.cpp OpenCSG-1.2.0-mac/src/channelManager.cpp ---- OpenCSG-1.2.0/src/channelManager.cpp 2010-01-02 21:03:04.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/channelManager.cpp 2010-01-24 23:30:24.000000000 +0100 -@@ -23,7 +23,7 @@ - #include - #ifdef _WIN32 - #include --#else -+#elif !defined(__APPLE__) - #include - #endif - -@@ -170,9 +170,11 @@ - #ifdef WIN32 - if ( WGLEW_ARB_pbuffer - && WGLEW_ARB_pixel_format --#else -+#elif !defined(__APPLE__) - if ( GLXEW_SGIX_pbuffer - && GLXEW_SGIX_fbconfig -+#else -+ if ( false - #endif - ) { - newOffscreenType = OpenCSG::PBuffer; -Only in OpenCSG-1.2.0-mac/src: channelManager.cpp.orig -diff -ru OpenCSG-1.2.0/src/frameBufferObject.h OpenCSG-1.2.0-mac/src/frameBufferObject.h ---- OpenCSG-1.2.0/src/frameBufferObject.h 2010-01-02 21:03:01.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/frameBufferObject.h 2010-01-24 23:30:24.000000000 +0100 -@@ -77,10 +77,10 @@ - - /// Texture stuff - GLenum textureTarget; -- unsigned int textureID; -- unsigned int depthID; -+ GLuint textureID; -+ GLuint depthID; - -- unsigned int framebufferID; -+ GLuint framebufferID; - - bool initialized; - }; -Only in OpenCSG-1.2.0-mac/src: frameBufferObject.h.orig -diff -ru OpenCSG-1.2.0/src/occlusionQuery.cpp OpenCSG-1.2.0-mac/src/occlusionQuery.cpp ---- OpenCSG-1.2.0/src/occlusionQuery.cpp 2010-01-02 21:03:04.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/occlusionQuery.cpp 2010-01-24 23:30:24.000000000 +0100 -@@ -57,7 +57,7 @@ - } - - unsigned int OcclusionQueryARB::getQueryResult() { -- unsigned int fragmentCount; -+ GLuint fragmentCount; - glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &fragmentCount); - return fragmentCount; - } -@@ -94,7 +94,7 @@ - } - - unsigned int OcclusionQueryNV::getQueryResult() { -- unsigned int fragmentCount; -+ GLuint fragmentCount; - glGetOcclusionQueryuivNV(mQueryObject, GL_PIXEL_COUNT_NV, &fragmentCount); - return fragmentCount; - } -diff -ru OpenCSG-1.2.0/src/openglHelper.cpp OpenCSG-1.2.0-mac/src/openglHelper.cpp ---- OpenCSG-1.2.0/src/openglHelper.cpp 2010-01-02 21:03:04.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/openglHelper.cpp 2010-01-24 23:30:24.000000000 +0100 -@@ -29,13 +29,13 @@ - - GLfloat projection[16]; - GLfloat modelview[16]; -- int canvasPos[4]; -+ GLint canvasPos[4]; - -- int stencilBits = 0; -+ GLint stencilBits = 0; - int stencilMax = 0; - int stencilMask = 0; - -- int scissorPos[4]; -+ GLint scissorPos[4]; - - void scissor(const PCArea& area) { - const int dx = area.maxx - area.minx; -diff -ru OpenCSG-1.2.0/src/openglHelper.h OpenCSG-1.2.0-mac/src/openglHelper.h ---- OpenCSG-1.2.0/src/openglHelper.h 2010-01-02 21:03:01.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/openglHelper.h 2010-01-24 23:30:24.000000000 +0100 -@@ -36,17 +36,17 @@ - // copy of the projection matrix during CSG computation - extern GLfloat modelview[16]; - // copy of the modelview matrix during CSG computation -- extern int canvasPos[4]; -+ extern GLint canvasPos[4]; - // copy of the viewport size during CSG computation - -- extern int stencilBits; -+ extern GLint stencilBits; - // number of stencil bits in the pbuffer - extern int stencilMax; - // the number where the stencil value would "wrap around" to zero - extern int stencilMask; - // stencilMax - 1 - -- extern int scissorPos[4]; -+ extern GLint scissorPos[4]; - // copy of the scissor settings for CSG computation - - void scissor(const PCArea& area); -diff -ru OpenCSG-1.2.0/src/pBufferTexture.h OpenCSG-1.2.0-mac/src/pBufferTexture.h ---- OpenCSG-1.2.0/src/pBufferTexture.h 2010-01-02 21:03:01.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/pBufferTexture.h 2010-01-24 23:30:24.000000000 +0100 -@@ -22,7 +22,7 @@ - - #ifndef __OpenCSG__pbuffer_texture_h__ - #define __OpenCSG__pbuffer_texture_h__ -- -+#ifndef __APPLE__ - #include "opencsgConfig.h" - #include "offscreenBuffer.h" - -@@ -81,5 +81,26 @@ - } // namespace OpenGL - - } // namespace OpenCSG -+#else -+ -+namespace OpenCSG { -+ namespace OpenGL { -+ class PBufferTexture : public OffscreenBuffer { -+ virtual bool Initialize(int , int , bool , bool ) {return false;} -+ virtual bool Reset() {return false;} -+ virtual bool Resize(int, int) {return false;} -+ virtual bool BeginCapture() {return false;} -+ virtual bool EndCapture() {return false;} -+ virtual void Bind() const {} -+ virtual void EnableTextureTarget() const {} -+ virtual void DisableTextureTarget() const {} -+ virtual unsigned int GetTextureTarget() const {return 0;} -+ virtual int GetWidth() const {return 0;} -+ virtual int GetHeight() const {return 0;} -+ virtual bool haveSeparateContext() const {return false;} -+ }; -+ } -+} - -+#endif // __APPLE__ - #endif // __OpenCSG__frame_buffer_object_h__ -diff -ru OpenCSG-1.2.0/src/src.pro OpenCSG-1.2.0-mac/src/src.pro ---- OpenCSG-1.2.0/src/src.pro 2010-01-02 20:56:12.000000000 +0100 -+++ OpenCSG-1.2.0-mac/src/src.pro 2010-01-24 23:31:57.000000000 +0100 -@@ -1,10 +1,19 @@ - TEMPLATE = lib - TARGET = opencsg - VERSION = 1.2.0 --DESTDIR = ../lib -+DESTDIR = $$(PWD)/lib - - CONFIG += opengl warn_on release --INCLUDEPATH += ../include ../glew/include ../ -+INCLUDEPATH += ../include ../ -+ -+macx { -+ INCLUDEPATH += /opt/local/include -+ LIBS += -L/opt/local/lib -lglew -+ CONFIG += absolute_library_soname -+} -+else { -+INCLUDEPATH += ../glew/include -+} - - HEADERS = ../include/opencsg.h \ - opencsgConfig.h \ -@@ -17,12 +26,11 @@ - offscreenBuffer.h \ - opencsgRender.h \ - openglHelper.h \ -- pBufferTexture.h \ - primitiveHelper.h \ - scissorMemo.h \ - settings.h \ -- stencilManager.h \ -- ../RenderTexture/RenderTexture.h -+ stencilManager.h -+ - SOURCES = area.cpp \ - batch.cpp \ - channelManager.cpp \ -@@ -32,12 +40,21 @@ - offscreenBuffer.cpp \ - opencsgRender.cpp \ - openglHelper.cpp \ -- pBufferTexture.cpp \ - primitive.cpp \ - primitiveHelper.cpp \ - renderGoldfeather.cpp \ - renderSCS.cpp \ - scissorMemo.cpp \ - settings.cpp \ -- stencilManager.cpp \ -- ../RenderTexture/RenderTexture.cpp -+ stencilManager.cpp -+ -+!macx { -+ HEADERS += ../RenderTexture/RenderTexture.h \ -+ pBufferTexture.h -+ -+ SOURCES += ../RenderTexture/RenderTexture.cpp \ -+ pBufferTexture.cpp -+} -+ -+INSTALLS += target -+target.path = $$DESTDIR -Only in OpenCSG-1.2.0-mac/src: src.pro.orig diff --git a/OpenCSG-1.2.0-Reset-Hack.patch b/OpenCSG-1.2.0-Reset-Hack.patch deleted file mode 100644 index 7254ce7..0000000 --- a/OpenCSG-1.2.0-Reset-Hack.patch +++ /dev/null @@ -1,89 +0,0 @@ -diff -ru OpenCSG-1.2.0/include/opencsg.h OpenCSG-1.2.0-reset/include/opencsg.h ---- OpenCSG-1.2.0/include/opencsg.h 2010-01-02 21:04:10.000000000 +0100 -+++ OpenCSG-1.2.0-reset/include/opencsg.h 2010-01-03 00:41:30.000000000 +0100 -@@ -229,6 +229,9 @@ - Algorithm, - DepthComplexityAlgorithm = NoDepthComplexitySampling); - -+ // call this function whenever switching the OpenGL context -+ void reset(); -+ - } // namespace OpenCSG - - #endif // __OpenCSG__opencsg_h__ -Only in OpenCSG-1.2.0-reset/include: opencsg.h~ -diff -ru OpenCSG-1.2.0/src/channelManager.h OpenCSG-1.2.0-reset/src/channelManager.h ---- OpenCSG-1.2.0/src/channelManager.h 2010-01-02 21:03:01.000000000 +0100 -+++ OpenCSG-1.2.0-reset/src/channelManager.h 2010-01-03 00:40:53.000000000 +0100 -@@ -79,9 +79,9 @@ - /// moved into alpha, to allow alpha testing of the channel. - static void setupTexEnv(Channel channel); - -- private: -- - static OpenGL::OffscreenBuffer* gOffscreenBuffer; -+ -+ private: - static int gOffscreenType; - static bool gInUse; - -Only in OpenCSG-1.2.0-reset/src: channelManager.h~ -diff -ru OpenCSG-1.2.0/src/offscreenBuffer.cpp OpenCSG-1.2.0-reset/src/offscreenBuffer.cpp ---- OpenCSG-1.2.0/src/offscreenBuffer.cpp 2010-01-02 21:03:04.000000000 +0100 -+++ OpenCSG-1.2.0-reset/src/offscreenBuffer.cpp 2010-01-03 00:41:28.000000000 +0100 -@@ -24,9 +24,22 @@ - #include "frameBufferObjectExt.h" - #include "pBufferTexture.h" - #include -+#include "channelManager.h" -+ -+static bool reset_fARB = false; -+static bool reset_fEXT = false; -+static bool reset_p = false; - - namespace OpenCSG { - -+ void reset() -+ { -+ reset_fARB = true; -+ reset_fEXT = true; -+ reset_p = true; -+ OpenCSG::ChannelManager::gOffscreenBuffer = NULL; -+ } -+ - namespace OpenGL { - - OffscreenBuffer* getOffscreenBuffer(bool fbo) { -@@ -36,19 +49,28 @@ - - if (fbo) { - if (GLEW_ARB_framebuffer_object) { -- if (!fARB) -+ if (reset_fARB || !fARB) { -+ delete fARB; - fARB = new FrameBufferObject; -+ reset_fARB = false; -+ } - return fARB; - } - else { -- if (!fEXT) -+ if (reset_fEXT || !fEXT) { -+ delete fEXT; - fEXT = new FrameBufferObjectExt; -+ reset_fEXT = false; -+ } - return fEXT; - } - } - else { -- if (!p) -+ if (reset_p || !p) { -+ delete p; - p = new PBufferTexture; -+ reset_p = false; -+ } - return p; - } - } -Only in OpenCSG-1.2.0-reset/src: offscreenBuffer.cpp~ diff --git a/OpenSCAD.icns b/OpenSCAD.icns deleted file mode 100644 index d6bbe9d..0000000 Binary files a/OpenSCAD.icns and /dev/null differ diff --git a/Preferences.cc b/Preferences.cc deleted file mode 100644 index cfc2bdb..0000000 --- a/Preferences.cc +++ /dev/null @@ -1,208 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "Preferences.h" - -#include -#include -#include - -Preferences *Preferences::instance = NULL; - -Preferences::Preferences(QWidget *parent) : QMainWindow(parent) -{ - setupUi(this); - - // Setup default settings - this->defaultmap["3dview/colorscheme"] = this->colorSchemeChooser->currentItem()->text(); - this->defaultmap["editor/fontfamily"] = this->fontChooser->currentText(); - this->defaultmap["editor/fontsize"] = this->fontSize->currentText().toUInt(); - - // Toolbar - QActionGroup *group = new QActionGroup(this); - group->addAction(prefsAction3DView); - group->addAction(prefsActionEditor); - group->addAction(prefsActionAdvanced); - connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); - - prefsAction3DView->setChecked(true); - this->actionTriggered(this->prefsAction3DView); - - // 3D View pane - this->colorschemes["Cornfield"][BACKGROUND_COLOR] = QColor(0xff, 0xff, 0xe5); - this->colorschemes["Cornfield"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][OPENCSG_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][CGAL_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][CGAL_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Cornfield"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); - - this->colorschemes["Metallic"][BACKGROUND_COLOR] = QColor(0xaa, 0xaa, 0xff); - this->colorschemes["Metallic"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][OPENCSG_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][CGAL_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][CGAL_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Metallic"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); - - this->colorschemes["Sunset"][BACKGROUND_COLOR] = QColor(0xaa, 0x44, 0x44); - this->colorschemes["Sunset"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][OPENCSG_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][CGAL_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][CGAL_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Sunset"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); - - // Editor pane - QFontDatabase db; - foreach(int size, db.standardSizes()) { - this->fontSize->addItem(QString::number(size)); - } - - connect(this->colorSchemeChooser, SIGNAL(itemSelectionChanged()), - this, SLOT(colorSchemeChanged())); - connect(this->fontChooser, SIGNAL(activated(const QString &)), - this, SLOT(fontFamilyChanged(const QString &))); - connect(this->fontSize, SIGNAL(editTextChanged(const QString &)), - this, SLOT(fontSizeChanged(const QString &))); - - updateGUI(); -} - -Preferences::~Preferences() -{ - removeDefaultSettings(); -} - -void -Preferences::actionTriggered(QAction *action) -{ - if (action == this->prefsAction3DView) { - this->stackedWidget->setCurrentWidget(this->page3DView); - } - else if (action == this->prefsActionEditor) { - this->stackedWidget->setCurrentWidget(this->pageEditor); - } - else if (action == this->prefsActionAdvanced) { - this->stackedWidget->setCurrentWidget(this->pageAdvanced); - } -} - -void Preferences::colorSchemeChanged() -{ - QSettings settings; - settings.setValue("3dview/colorscheme", this->colorSchemeChooser->currentItem()->text()); - - emit requestRedraw(); -} - -const QColor &Preferences::color(RenderColor idx) -{ - return this->colorschemes[getValue("3dview/colorscheme").toString()][idx]; -} - -void Preferences::fontFamilyChanged(const QString &family) -{ - QSettings settings; - settings.setValue("editor/fontfamily", family); - emit fontChanged(family, getValue("editor/fontsize").toUInt()); -} - -void Preferences::fontSizeChanged(const QString &size) -{ - uint intsize = size.toUInt(); - QSettings settings; - settings.setValue("editor/fontsize", intsize); - emit fontChanged(getValue("editor/fontfamily").toString(), intsize); -} - -void Preferences::keyPressEvent(QKeyEvent *e) -{ -#ifdef Q_WS_MAC - if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { - close(); - } else -#endif - if ((e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_W) || - e->key() == Qt::Key_Escape) { - close(); - } -} - -/*! - Removes settings that are the same as the default settings to avoid - overwriting future changes to default settings. - */ -void Preferences::removeDefaultSettings() -{ - QSettings settings; - for (QSettings::SettingsMap::const_iterator iter = this->defaultmap.begin(); - iter != this->defaultmap.end(); - iter++) { - if (settings.value(iter.key()) == iter.value()) { - settings.remove(iter.key()); - } - } -} - -QVariant Preferences::getValue(const QString &key) const -{ - QSettings settings; - return settings.value(key, this->defaultmap[key]); -} - -void Preferences::updateGUI() -{ - QList found = - this->colorSchemeChooser->findItems(getValue("3dview/colorscheme").toString(), - Qt::MatchExactly); - if (!found.isEmpty()) this->colorSchemeChooser->setCurrentItem(found.first()); - - QString fontfamily = getValue("editor/fontfamily").toString(); - int fidx = this->fontChooser->findText(fontfamily); - if (fidx >= 0) { - this->fontChooser->setCurrentIndex(fidx); - } - - QString fontsize = getValue("editor/fontsize").toString(); - int sidx = this->fontSize->findText(fontsize); - if (sidx >= 0) { - this->fontSize->setCurrentIndex(sidx); - } - else { - this->fontSize->setEditText(fontsize); - } -} - -void Preferences::apply() const -{ - emit fontChanged(getValue("editor/fontfamily").toString(), getValue("editor/fontsize").toUInt()); - emit requestRedraw(); -} - diff --git a/Preferences.h b/Preferences.h deleted file mode 100644 index 39599fd..0000000 --- a/Preferences.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef PREFERENCES_H_ -#define PREFERENCES_H_ - -#include -#include -#include "ui_Preferences.h" - -class Preferences : public QMainWindow, public Ui::Preferences -{ - Q_OBJECT; - -public: - ~Preferences(); - static Preferences *inst() { if (!instance) instance = new Preferences(); return instance; } - - 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 - }; - const QColor &color(RenderColor idx); - QVariant getValue(const QString &key) const; - void apply() const; - -public slots: - void actionTriggered(class QAction *); - void colorSchemeChanged(); - void fontFamilyChanged(const QString &); - void fontSizeChanged(const QString &); - -signals: - void requestRedraw() const; - void fontChanged(const QString &family, uint size) const; - -private: - Preferences(QWidget *parent = NULL); - void keyPressEvent(QKeyEvent *e); - void updateGUI(); - void removeDefaultSettings(); - - QSettings::SettingsMap defaultmap; - QHash > colorschemes; - - static Preferences *instance; -}; - -#endif diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index b61e57b..0000000 --- a/TODO.txt +++ /dev/null @@ -1,122 +0,0 @@ - -BUGS ----- -o Some invalid DXF data gets pass the import checks and breaks the tessing code -o Broken polyhedron() entities are not correctly detected and cause CGAL segfaults -o Tesselation via GLU sometimes produces strange results -o Non-manifold objects make CGAL crash (e.g. two cubes which touch at one edge) -o Export STL: Exports existing CGAL model even though the current model is changed, but not CGAL rendered - -USER INTERFACE --------------- -o Preferences - - Beautify color schemes - - Color schemes read from file - - Color scheme editor - - wireframe width - - pointsize - - OpenGL params - - Default language feature settings - - Auto-view CSG/thrown together on load -o Export etc.: automatically add missing extension as in SaveAs -o MDI - - Think about how to do MDI the right way - - Ctrl-W should close the current dialog, not the current main window - -> implement as for Preferences? - - Menu bar handling: - Mac: share menubar among all top-level windows? -o 3D View - - Improve mouse rotation - - Add modifier key combos to handle pan on 1 mouse button systems - - Show grid - - 4 x split view w/orthogonal cameras? - - Quick highlighting of object under the cursor in the editor - - View All - - overlay indicator displaying current view mode -o Editor - - Autocompletion/hints for builtin (and user-defined) functions/modules - - builtin quick function reference/help - - Drawer/popup with all modules/functions listed which can be inserted into - the editor by clicking or drag&drop -> icons in toolbar? - -> This would be moving in the direction of a traditional CAD GUI - and needs a fair bit of thinking. - - More infrastructur for external editor (or is the reload good enough?) - - Evaluate QCodeEdit (http://qcodeedit.edyuk.org) - - Display some kind of line wrap indicator - - Couple the source code to the AST to allow highlighting selected elements - in the source code in the 3D view -o Misc - - Reload and compile: Ask for confirmation if file is locally edited - (make this configurable in preferences?) - - Save: Ask for confirmation if file has been externaly changed - - Rename OpenCSG and CGAL to smth. not specific to the underlying libraries - (e.g Preview, Render) - -ENGINE ------- -o Primitives - - Springs, spirals (requested by Cathal Garvey) - - (TTF) Text -o 2D Subsystem - - Add generic 3D->2D projection statements - - 2D->3D using clipping plane - - Performance: Is it necessary to union children before extrusion - when compiling? Can this be postponed to CGAL evaluation time? -o Built-in modules - - extrude*: Allow the base 2D primitive to have a Z value -o Advanced Transformations - - Add statement for 2D and 3D minkowski sum - - Add statement for refinement via surface subdivision - - Add statement for intersections in cartesian product of childs -o Function-Module-Interface - - Pass a module instanciation to a function (e.g. for a volume() function) - - Pass a function to a module instanciation (e.g. for dynamic extrusion paths) -o Language Frontend - - Allow local variables and functions everywhere (not only on module level) -o DXF Import - - Support for POLYLINE entity - - Support for SPLINE entity - - Support for LEADER entity - - Support for MTEXT entity ? - - idea: DXF inline - convert from dxf to OpenSCAD syntax -> parametrize dxf content -o Mesh optimization on STL export - - Remove super small triangles (all sides are short) - - Replace super thin triangles (one h is short) -o Misc - - Add symbolic colors to the color() statement - - Go through default values of parameters (e.g. cube() has x,y,z=1 while linear_extrude() has height=100) -o Grammar - - dim->name -> dim->label - - A random(seed) function - - import_*() -> *_import() (consistent prefix vs. postfix) - - linear_extrude()/rotate_extrude(): Cumbersome names? -> (extrude, revolve, lathe, sweep ?) - -CODE ----- - -o Refactor from MainWindow: - - Fix current_win hack - - CSG data structure (compiled model) - - CGAL data structure (compiled model) -o C++-ify - - Use smart pointers where it makes sense (e.g. instead of homegrown refcount, - and to get memory ownership under control) - - Use static_cast instead of C-style casts -o dxflinextrude and dxfrotextrude could share code -o Consider decoupling DXF-specific functionality from the 2D subsystem -o Make the interfaces from OpenSCAD and OpenCSG and CGAL cleaner to facilitate - experimentation with other frameworks (ref. Nef_polyhedron_2() problems) - -INFRASTRUCTURE --------------- -o Think about making external libraries easier available. Probably mostly convenience. -o Use a logging framework to get debugging/info output more under control? - (check log4j, google project) - -MISC ----- -o Collect "all" available OpenSCAD scripts from the internets and batch convert them - to STL to assist with robustness testing. -o Mac OS X: - - universal binary -> fix cgal and opencsg - diff --git a/checklist-macosx.txt b/checklist-macosx.txt deleted file mode 100644 index e3d48eb..0000000 --- a/checklist-macosx.txt +++ /dev/null @@ -1,25 +0,0 @@ -o Build CGAL: - - tar xzf CGAL-3.5.1.tar.gz - cd CGAL-3.5.1 - patch -p1 < ../openscad/CGAL-OGL-Tess-Combine-Fix.patch - cmake -DCMAKE_INSTALL_PREFIX=$PWD/../install -DBUILD_SHARED_LIBS=FALSE - make -j4 - make install - -o Patch OpenCSG - - cd OpenCSG-1.2.0 - patch -p1 < ../openscad/OpenCSG-1.2.0-MacOSX-port.patch - patch -p1 < ../openscad/OpenCSG-1.2.0-Reset-Hack.patch # Only if MDI - -o Build OpenCSG - - qmake -recursive - make - -o Build and Deploy OpenSCAD - -# Update VERSION in release-macosx.sh - cd openscad - ./release-macosx.sh diff --git a/context.cc b/context.cc deleted file mode 100644 index 15a2ea6..0000000 --- a/context.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" -#include "printutils.h" - -Context::Context(const Context *parent) -{ - this->parent = parent; - functions_p = NULL; - modules_p = NULL; - inst_p = NULL; - ctx_stack.append(this); -} - -Context::~Context() -{ - ctx_stack.pop_back(); -} - -void Context::args(const QVector &argnames, const QVector &argexpr, - const QVector &call_argnames, const QVector &call_argvalues) -{ - for (int i=0; ievaluate(this->parent) : Value()); - } - - int posarg = 0; - for (int i=0; i Context::ctx_stack; - -void Context::set_variable(QString name, Value value) -{ - if (name.startsWith("$")) - config_variables[name] = value; - else - variables[name] = value; -} - -Value Context::lookup_variable(QString name, bool silent) const -{ - if (name.startsWith("$")) { - for (int i = ctx_stack.size()-1; i >= 0; i--) { - if (ctx_stack[i]->config_variables.contains(name)) - return ctx_stack[i]->config_variables[name]; - } - return Value(); - } - if (variables.contains(name)) - return variables[name]; - if (parent) - return parent->lookup_variable(name, silent); - if (!silent) - PRINTA("WARNING: Ignoring unkown variable '%1'.", name); - return Value(); -} - -Value Context::evaluate_function(QString name, const QVector &argnames, const QVector &argvalues) const -{ - if (functions_p && functions_p->contains(name)) - return functions_p->value(name)->evaluate(this, argnames, argvalues); - if (parent) - return parent->evaluate_function(name, argnames, argvalues); - PRINTA("WARNING: Ignoring unkown function '%1'.", name); - return Value(); -} - -AbstractNode *Context::evaluate_module(const ModuleInstantiation *inst) const -{ - if (modules_p && modules_p->contains(inst->modname)) - return modules_p->value(inst->modname)->evaluate(this, inst); - if (parent) - return parent->evaluate_module(inst); - PRINTA("WARNING: Ignoring unkown module '%1'.", inst->modname); - return NULL; -} - diff --git a/control.cc b/control.cc deleted file mode 100644 index 19c5255..0000000 --- a/control.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -enum control_type_e { - CHILD, - ECHO, - ASSIGN, - FOR, - INT_FOR, - IF -}; - -class ControlModule : public AbstractModule -{ -public: - control_type_e type; - ControlModule(control_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -void for_eval(AbstractNode *node, int l, const QVector &call_argnames, const QVector &call_argvalues, const QVector arg_children, const Context *arg_context) -{ - if (call_argnames.size() > l) { - QString it_name = call_argnames[l]; - Value it_values = call_argvalues[l]; - Context c(arg_context); - if (it_values.type == Value::RANGE) { - double range_begin = it_values.range_begin; - double range_end = it_values.range_end; - double range_step = it_values.range_step; - if (range_end < range_begin) { - double t = range_begin; - range_begin = range_end; - range_end = t; - } - if (range_step > 0 && (range_begin-range_end)/range_step < 10000) { - for (double i = range_begin; i <= range_end; i += range_step) { - c.set_variable(it_name, Value(i)); - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); - } - } - } - else if (it_values.type == Value::VECTOR) { - for (int i = 0; i < it_values.vec.size(); i++) { - c.set_variable(it_name, *it_values.vec[i]); - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); - } - } - else { - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); - } - } else { - foreach (ModuleInstantiation *v, arg_children) { - AbstractNode *n = v->evaluate(arg_context); - if (n != NULL) - node->children.append(n); - } - } -} - -AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation *inst) const -{ - if (type == CHILD) - { - int n = 0; - if (inst->argvalues.size() > 0) { - double v; - if (inst->argvalues[0].getnum(v)) - n = v; - } - for (int i = Context::ctx_stack.size()-1; i >= 0; i--) { - const Context *c = Context::ctx_stack[i]; - if (c->inst_p) { - if (n < c->inst_p->children.size()) - return c->inst_p->children[n]->evaluate(c->inst_p->ctx); - return NULL; - } - c = c->parent; - } - return NULL; - } - - AbstractNode *node; - - if (type == INT_FOR) - node = new AbstractIntersectionNode(inst); - else - node = new AbstractNode(inst); - - if (type == ECHO) - { - QString msg = QString("ECHO: "); - for (int i = 0; i < inst->argnames.size(); i++) { - if (i > 0) - msg += QString(", "); - if (!inst->argnames[i].isEmpty()) - msg += inst->argnames[i] + QString(" = "); - msg += inst->argvalues[i].dump(); - } - PRINT(msg); - } - - if (type == ASSIGN) - { - Context c(inst->ctx); - for (int i = 0; i < inst->argnames.size(); i++) { - if (!inst->argnames[i].isEmpty()) - c.set_variable(inst->argnames[i], inst->argvalues[i]); - } - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(&c); - if (n != NULL) - node->children.append(n); - } - } - - if (type == FOR || type == INT_FOR) - { - for_eval(node, 0, inst->argnames, inst->argvalues, inst->children, inst->ctx); - } - - if (type == IF) - { - if (inst->argvalues.size() > 0 && inst->argvalues[0].type == Value::BOOL && inst->argvalues[0].b) - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } - } - - return node; -} - -void register_builtin_control() -{ - builtin_modules["child"] = new ControlModule(CHILD); - builtin_modules["echo"] = new ControlModule(ECHO); - builtin_modules["assign"] = new ControlModule(ASSIGN); - builtin_modules["for"] = new ControlModule(FOR); - builtin_modules["intersection_for"] = new ControlModule(INT_FOR); - builtin_modules["if"] = new ControlModule(IF); -} - diff --git a/csgops.cc b/csgops.cc deleted file mode 100644 index be29ede..0000000 --- a/csgops.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -enum csg_type_e { - CSG_TYPE_UNION, - CSG_TYPE_DIFFERENCE, - CSG_TYPE_INTERSECTION -}; - -class CsgModule : public AbstractModule -{ -public: - csg_type_e type; - CsgModule(csg_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class CsgNode : public AbstractNode -{ -public: - csg_type_e type; - CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst) const -{ - CsgNode *node = new CsgNode(inst, type); - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } - return node; -} - -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CsgNode::render_cgal_nef_polyhedron() const -{ - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) { - progress_report(); - PRINT(cgal_nef_cache[cache_id]->msg); - return cgal_nef_cache[cache_id]->N; - } - - print_messages_push(); - - bool first = true; - CGAL_Nef_polyhedron N; - foreach (AbstractNode *v, children) { - if (v->modinst->tag_background) - continue; - if (first) { - N = v->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - if (type == CSG_TYPE_UNION) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (type == CSG_TYPE_DIFFERENCE) { - N.p2 -= v->render_cgal_nef_polyhedron().p2; - } else if (type == CSG_TYPE_INTERSECTION) { - N.p2 *= v->render_cgal_nef_polyhedron().p2; - } - } else if (N.dim == 3) { - if (type == CSG_TYPE_UNION) { - N.p3 += v->render_cgal_nef_polyhedron().p3; - } else if (type == CSG_TYPE_DIFFERENCE) { - N.p3 -= v->render_cgal_nef_polyhedron().p3; - } else if (type == CSG_TYPE_INTERSECTION) { - N.p3 *= v->render_cgal_nef_polyhedron().p3; - } - } - } - - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - print_messages_pop(); - progress_report(); - - return N; -} - -#endif /* ENABLE_CGAL */ - -CSGTerm *CsgNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - CSGTerm *t1 = NULL; - foreach (AbstractNode *v, children) { - CSGTerm *t2 = v->render_csg_term(m, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - if (type == CSG_TYPE_UNION) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } else if (type == CSG_TYPE_DIFFERENCE) { - t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2); - } else if (type == CSG_TYPE_INTERSECTION) { - t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); - } - } - } - if (t1 && modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; -} - -QString CsgNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: ").arg(idx); - if (type == CSG_TYPE_UNION) - text += "union() {\n"; - if (type == CSG_TYPE_DIFFERENCE) - text += "difference() {\n"; - if (type == CSG_TYPE_INTERSECTION) - text += "intersection() {\n"; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - -void register_builtin_csgops() -{ - builtin_modules["union"] = new CsgModule(CSG_TYPE_UNION); - builtin_modules["difference"] = new CsgModule(CSG_TYPE_DIFFERENCE); - builtin_modules["intersection"] = new CsgModule(CSG_TYPE_INTERSECTION); -} - diff --git a/csgterm.cc b/csgterm.cc deleted file mode 100644 index ebad060..0000000 --- a/csgterm.cc +++ /dev/null @@ -1,207 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" - -CSGTerm::CSGTerm(PolySet *polyset, double m[20], QString label) -{ - this->type = TYPE_PRIMITIVE; - this->polyset = polyset; - this->label = label; - this->left = NULL; - this->right = NULL; - for (int i = 0; i < 20; i++) - this->m[i] = m[i]; - refcounter = 1; -} - -CSGTerm::CSGTerm(type_e type, CSGTerm *left, CSGTerm *right) -{ - this->type = type; - this->polyset = NULL; - this->left = left; - this->right = right; - refcounter = 1; -} - -CSGTerm *CSGTerm::normalize() -{ - // This function implements the CSG normalization - // Reference: Florian Kirsch, Juergen Doeller, - // OpenCSG: A Library for Image-Based CSG Rendering, - // University of Potsdam, Hasso-Plattner-Institute, Germany - // http://www.opencsg.org/data/csg_freenix2005_paper.pdf - - if (type == TYPE_PRIMITIVE) - return link(); - - CSGTerm *t1, *t2, *x, *y; - - x = left->normalize(); - y = right->normalize(); - - if (x != left || y != right) { - t1 = new CSGTerm(type, x, y); - } else { - t1 = link(); - x->unlink(); - y->unlink(); - } - - while (1) { - t2 = t1->normalize_tail(); - t1->unlink(); - if (t1 == t2) - break; - t1 = t2; - } - - return t1; -} - -CSGTerm *CSGTerm::normalize_tail() -{ - CSGTerm *x, *y, *z; - - // Part A: The 'x . (y . z)' expressions - - x = left; - y = right->left; - z = right->right; - - // 1. x - (y + z) -> (x - y) - z - if (type == TYPE_DIFFERENCE && right->type == TYPE_UNION) - return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), z->link()); - - // 2. x * (y + z) -> (x * y) + (x * z) - if (type == TYPE_INTERSECTION && right->type == TYPE_UNION) - return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), new CSGTerm(TYPE_INTERSECTION, x->link(), z->link())); - - // 3. x - (y * z) -> (x - y) + (x - z) - if (type == TYPE_DIFFERENCE && right->type == TYPE_INTERSECTION) - return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), new CSGTerm(TYPE_DIFFERENCE, x->link(), z->link())); - - // 4. x * (y * z) -> (x * y) * z - if (type == TYPE_INTERSECTION && right->type == TYPE_INTERSECTION) - return new CSGTerm(TYPE_INTERSECTION, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), z->link()); - - // 5. x - (y - z) -> (x - y) + (x * z) - if (type == TYPE_DIFFERENCE && right->type == TYPE_DIFFERENCE) - return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), new CSGTerm(TYPE_INTERSECTION, x->link(), z->link())); - - // 6. x * (y - z) -> (x * y) - z - if (type == TYPE_INTERSECTION && right->type == TYPE_DIFFERENCE) - return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), z->link()); - - // Part B: The '(x . y) . z' expressions - - x = left->left; - y = left->right; - z = right; - - // 7. (x - y) * z -> (x * z) - y - if (left->type == TYPE_DIFFERENCE && type == TYPE_INTERSECTION) - return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_INTERSECTION, x->link(), z->link()), y->link()); - - // 8. (x + y) - z -> (x - z) + (y - z) - if (left->type == TYPE_UNION && type == TYPE_DIFFERENCE) - return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), z->link()), new CSGTerm(TYPE_DIFFERENCE, y->link(), z->link())); - - // 9. (x + y) * z -> (x * z) + (y * z) - if (left->type == TYPE_UNION && type == TYPE_INTERSECTION) - return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_INTERSECTION, x->link(), z->link()), new CSGTerm(TYPE_INTERSECTION, y->link(), z->link())); - - return link(); -} - -CSGTerm *CSGTerm::link() -{ - refcounter++; - return this; -} - -void CSGTerm::unlink() -{ - if (--refcounter <= 0) { - if (polyset) - polyset->unlink(); - if (left) - left->unlink(); - if (right) - right->unlink(); - delete this; - } -} - -QString CSGTerm::dump() -{ - if (type == TYPE_UNION) - return QString("(%1 + %2)").arg(left->dump(), right->dump()); - if (type == TYPE_INTERSECTION) - return QString("(%1 * %2)").arg(left->dump(), right->dump()); - if (type == TYPE_DIFFERENCE) - return QString("(%1 - %2)").arg(left->dump(), right->dump()); - return label; -} - -CSGChain::CSGChain() -{ -} - -void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label) -{ - polysets.append(polyset); - matrices.append(m); - types.append(type); - labels.append(label); -} - -void CSGChain::import(CSGTerm *term, CSGTerm::type_e type) -{ - if (term->type == CSGTerm::TYPE_PRIMITIVE) { - add(term->polyset, term->m, type, term->label); - } else { - import(term->left, type); - import(term->right, term->type); - } -} - -QString CSGChain::dump() -{ - QString text; - for (int i = 0; i < types.size(); i++) - { - if (types[i] == CSGTerm::TYPE_UNION) { - if (i != 0) - text += "\n"; - text += "+"; - } - if (types[i] == CSGTerm::TYPE_DIFFERENCE) - text += " -"; - if (types[i] == CSGTerm::TYPE_INTERSECTION) - text += " *"; - text += labels[i]; - } - text += "\n"; - return text; -} - diff --git a/doc/TODO.txt b/doc/TODO.txt new file mode 100644 index 0000000..b61e57b --- /dev/null +++ b/doc/TODO.txt @@ -0,0 +1,122 @@ + +BUGS +---- +o Some invalid DXF data gets pass the import checks and breaks the tessing code +o Broken polyhedron() entities are not correctly detected and cause CGAL segfaults +o Tesselation via GLU sometimes produces strange results +o Non-manifold objects make CGAL crash (e.g. two cubes which touch at one edge) +o Export STL: Exports existing CGAL model even though the current model is changed, but not CGAL rendered + +USER INTERFACE +-------------- +o Preferences + - Beautify color schemes + - Color schemes read from file + - Color scheme editor + - wireframe width + - pointsize + - OpenGL params + - Default language feature settings + - Auto-view CSG/thrown together on load +o Export etc.: automatically add missing extension as in SaveAs +o MDI + - Think about how to do MDI the right way + - Ctrl-W should close the current dialog, not the current main window + -> implement as for Preferences? + - Menu bar handling: + Mac: share menubar among all top-level windows? +o 3D View + - Improve mouse rotation + - Add modifier key combos to handle pan on 1 mouse button systems + - Show grid + - 4 x split view w/orthogonal cameras? + - Quick highlighting of object under the cursor in the editor + - View All + - overlay indicator displaying current view mode +o Editor + - Autocompletion/hints for builtin (and user-defined) functions/modules + - builtin quick function reference/help + - Drawer/popup with all modules/functions listed which can be inserted into + the editor by clicking or drag&drop -> icons in toolbar? + -> This would be moving in the direction of a traditional CAD GUI + and needs a fair bit of thinking. + - More infrastructur for external editor (or is the reload good enough?) + - Evaluate QCodeEdit (http://qcodeedit.edyuk.org) + - Display some kind of line wrap indicator + - Couple the source code to the AST to allow highlighting selected elements + in the source code in the 3D view +o Misc + - Reload and compile: Ask for confirmation if file is locally edited + (make this configurable in preferences?) + - Save: Ask for confirmation if file has been externaly changed + - Rename OpenCSG and CGAL to smth. not specific to the underlying libraries + (e.g Preview, Render) + +ENGINE +------ +o Primitives + - Springs, spirals (requested by Cathal Garvey) + - (TTF) Text +o 2D Subsystem + - Add generic 3D->2D projection statements + - 2D->3D using clipping plane + - Performance: Is it necessary to union children before extrusion + when compiling? Can this be postponed to CGAL evaluation time? +o Built-in modules + - extrude*: Allow the base 2D primitive to have a Z value +o Advanced Transformations + - Add statement for 2D and 3D minkowski sum + - Add statement for refinement via surface subdivision + - Add statement for intersections in cartesian product of childs +o Function-Module-Interface + - Pass a module instanciation to a function (e.g. for a volume() function) + - Pass a function to a module instanciation (e.g. for dynamic extrusion paths) +o Language Frontend + - Allow local variables and functions everywhere (not only on module level) +o DXF Import + - Support for POLYLINE entity + - Support for SPLINE entity + - Support for LEADER entity + - Support for MTEXT entity ? + - idea: DXF inline - convert from dxf to OpenSCAD syntax -> parametrize dxf content +o Mesh optimization on STL export + - Remove super small triangles (all sides are short) + - Replace super thin triangles (one h is short) +o Misc + - Add symbolic colors to the color() statement + - Go through default values of parameters (e.g. cube() has x,y,z=1 while linear_extrude() has height=100) +o Grammar + - dim->name -> dim->label + - A random(seed) function + - import_*() -> *_import() (consistent prefix vs. postfix) + - linear_extrude()/rotate_extrude(): Cumbersome names? -> (extrude, revolve, lathe, sweep ?) + +CODE +---- + +o Refactor from MainWindow: + - Fix current_win hack + - CSG data structure (compiled model) + - CGAL data structure (compiled model) +o C++-ify + - Use smart pointers where it makes sense (e.g. instead of homegrown refcount, + and to get memory ownership under control) + - Use static_cast instead of C-style casts +o dxflinextrude and dxfrotextrude could share code +o Consider decoupling DXF-specific functionality from the 2D subsystem +o Make the interfaces from OpenSCAD and OpenCSG and CGAL cleaner to facilitate + experimentation with other frameworks (ref. Nef_polyhedron_2() problems) + +INFRASTRUCTURE +-------------- +o Think about making external libraries easier available. Probably mostly convenience. +o Use a logging framework to get debugging/info output more under control? + (check log4j, google project) + +MISC +---- +o Collect "all" available OpenSCAD scripts from the internets and batch convert them + to STL to assist with robustness testing. +o Mac OS X: + - universal binary -> fix cgal and opencsg + diff --git a/doc/checklist-macosx.txt b/doc/checklist-macosx.txt new file mode 100644 index 0000000..e3d48eb --- /dev/null +++ b/doc/checklist-macosx.txt @@ -0,0 +1,25 @@ +o Build CGAL: + + tar xzf CGAL-3.5.1.tar.gz + cd CGAL-3.5.1 + patch -p1 < ../openscad/CGAL-OGL-Tess-Combine-Fix.patch + cmake -DCMAKE_INSTALL_PREFIX=$PWD/../install -DBUILD_SHARED_LIBS=FALSE + make -j4 + make install + +o Patch OpenCSG + + cd OpenCSG-1.2.0 + patch -p1 < ../openscad/OpenCSG-1.2.0-MacOSX-port.patch + patch -p1 < ../openscad/OpenCSG-1.2.0-Reset-Hack.patch # Only if MDI + +o Build OpenCSG + + qmake -recursive + make + +o Build and Deploy OpenSCAD + +# Update VERSION in release-macosx.sh + cd openscad + ./release-macosx.sh diff --git a/dxfdata.cc b/dxfdata.cc deleted file mode 100644 index 864ee7b..0000000 --- a/dxfdata.cc +++ /dev/null @@ -1,514 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" -#include "printutils.h" - -#include -#include - -struct Line { - typedef DxfData::Point Point; - Point *p[2]; - bool disabled; - Line(Point *p1, Point *p2) { p[0] = p1; p[1] = p2; disabled = false; } - Line() { p[0] = NULL; p[1] = NULL; disabled = false; } -}; - -DxfData::DxfData() -{ -} - -/*! - Reads a layer from the given file, or all layers if layename.isNull() - */ -DxfData::DxfData(double fn, double fs, double fa, QString filename, QString layername, double xorigin, double yorigin, double scale) -{ - handle_dep(filename); // Register ourselves as a dependency - - QFile f(filename); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTF("WARNING: Can't open DXF file `%s'.", filename.toUtf8().data()); - return; - } - QTextStream stream(&f); - - Grid2d< QVector > grid(GRID_COARSE); - QList lines; // Global lines - QHash< QString, QList > blockdata; // Lines in blocks - - bool in_entities_section = false; - bool in_blocks_section = false; - QString current_block; - -#define ADD_LINE(_x1, _y1, _x2, _y2) do { \ - double _p1x = _x1, _p1y = _y1, _p2x = _x2, _p2y = _y2; \ - if (!in_entities_section && !in_blocks_section) \ - break; \ - if (in_entities_section && \ - !(layername.isNull() || layername == layer)) \ - break; \ - grid.align(_p1x, _p1y); \ - grid.align(_p2x, _p2y); \ - grid.data(_p1x, _p1y).append(lines.count()); \ - grid.data(_p2x, _p2y).append(lines.count()); \ - if (in_entities_section) \ - lines.append( \ - Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ - if (in_blocks_section && !current_block.isNull()) \ - blockdata[current_block].append( \ - Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ - } while (0) - - QString mode, layer, name, iddata; - int dimtype = 0; - double coords[7][2]; // Used by DIMENSION entities - QVector xverts; - QVector yverts; - double radius = 0; - double arc_start_angle = 0, arc_stop_angle = 0; - double ellipse_start_angle = 0, ellipse_stop_angle = 0; - - for (int i = 0; i < 7; i++) - for (int j = 0; j < 2; j++) - coords[i][j] = 0; - - QHash unsupported_entities_list; - - - // - // Parse DXF file. Will populate this->points, this->dims, lines and blockdata - // - while (!stream.atEnd()) - { - QString id_str = stream.readLine(); - QString data = stream.readLine(); - - bool status; - int id = id_str.toInt(&status); - - if (!status) { - PRINTA("WARNING: Illegal ID `%1' in `%3'.", id_str, filename); - break; - } - - if (id >= 10 && id <= 16) { - if (in_blocks_section) - coords[id-10][0] = data.toDouble(); - else if (id == 11 || id == 12 || id == 16) - coords[id-10][0] = data.toDouble() * scale; - else - coords[id-10][0] = (data.toDouble() - xorigin) * scale; - } - - if (id >= 20 && id <= 26) { - if (in_blocks_section) - coords[id-20][1] = data.toDouble(); - else if (id == 21 || id == 22 || id == 26) - coords[id-20][1] = data.toDouble() * scale; - else - coords[id-20][1] = (data.toDouble() - yorigin) * scale; - } - - switch (id) - { - case 0: - if (mode == "SECTION") { - in_entities_section = iddata == "ENTITIES"; - in_blocks_section = iddata == "BLOCKS"; - } - else if (mode == "LINE") { - ADD_LINE(xverts[0], yverts[0], xverts[1], yverts[1]); - } - else if (mode == "LWPOLYLINE") { - assert(xverts.size() == yverts.size()); - // polyline flag is stored in 'dimtype' - int numverts = xverts.size(); - for (int i=1;i arc_stop_angle) - arc_stop_angle += 360.0; - n = (int)ceil(n * (arc_stop_angle-arc_start_angle) / 360); - for (int i = 0; i < n; i++) { - double a1 = ((arc_stop_angle-arc_start_angle)*i)/n; - double a2 = ((arc_stop_angle-arc_start_angle)*(i+1))/n; - a1 = (arc_start_angle + a1) * M_PI / 180.0; - a2 = (arc_start_angle + a2) * M_PI / 180.0; - ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y, - cos(a2)*radius + center.x, sin(a2)*radius + center.y); - } - } - else if (mode == "ELLIPSE") { - // Commented code is meant as documentation of vector math - while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI; -// Vector2d center(xverts[0], yverts[0]); - Point center(xverts[0], yverts[0]); -// Vector2d ce(xverts[1], yverts[1]); - Point ce(xverts[1], yverts[1]); -// double r_major = ce.length(); - double r_major = sqrt(ce.x*ce.x + ce.y*ce.y); -// double rot_angle = ce.angle(); - double rot_angle; - { -// double dot = ce.dot(Vector2d(1.0, 0.0)); - double dot = ce.x; - double cosval = dot / r_major; - if (cosval > 1.0) cosval = 1.0; - if (cosval < -1.0) cosval = -1.0; - rot_angle = acos(cosval); - if (ce.y < 0.0) rot_angle = 2 * M_PI - rot_angle; - } - - // the ratio stored in 'radius; due to the parser code not checking entity type - double r_minor = r_major * radius; - double sweep_angle = ellipse_stop_angle-ellipse_start_angle; - int n = get_fragments_from_r(r_major, fn, fs, fa); - n = (int)ceil(n * sweep_angle / (2 * M_PI)); -// Vector2d p1; - Point p1; - for (int i=0;i<=n;i++) { - double a = (ellipse_start_angle + sweep_angle*i/n); -// Vector2d p2(cos(a)*r_major, sin(a)*r_minor); - Point p2(cos(a)*r_major, sin(a)*r_minor); -// p2.rotate(rot_angle); - Point p2_rot(cos(rot_angle)*p2.x - sin(rot_angle)*p2.y, - sin(rot_angle)*p2.x + cos(rot_angle)*p2.y); -// p2 += center; - p2_rot.x += center.x; - p2_rot.y += center.y; - if (i > 0) { -// ADD_LINE(p1[0], p1[1], p2[0], p2[1]); - ADD_LINE(p1.x, p1.y, p2_rot.x, p2_rot.y); - } -// p1 = p2; - p1.x = p2_rot.x; - p1.y = p2_rot.y; - } - } - else if (mode == "INSERT") { - // scale is stored in ellipse_start|stop_angle, rotation in arc_start_angle; - // due to the parser code not checking entity type - int n = blockdata[iddata].size(); - for (int i = 0; i < n; i++) { - double a = arc_start_angle * M_PI / 180.0; - double lx1 = blockdata[iddata][i].p[0]->x * ellipse_start_angle; - double ly1 = blockdata[iddata][i].p[0]->y * ellipse_stop_angle; - double lx2 = blockdata[iddata][i].p[1]->x * ellipse_start_angle; - double ly2 = blockdata[iddata][i].p[1]->y * ellipse_stop_angle; - double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts[0]; - double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts[0]; - double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts[0]; - double py2 = (sin(a)*lx2 + cos(a)*ly2) * scale + yverts[0]; - ADD_LINE(px1, py1, px2, py2); - } - } - else if (mode == "DIMENSION" && - (layername.isNull() || layername == layer)) { - this->dims.append(Dim()); - this->dims.last().type = dimtype; - for (int i = 0; i < 7; i++) - for (int j = 0; j < 2; j++) - this->dims.last().coords[i][j] = coords[i][j]; - this->dims.last().angle = arc_start_angle; - this->dims.last().length = radius; - this->dims.last().name = name; - } - else if (mode == "BLOCK") { - current_block = iddata; - } - else if (mode == "ENDBLK") { - current_block = QString(); - } - else if (mode == "ENDSEC") { - } - else if (in_blocks_section || (in_entities_section && - (layername.isNull() || layername == layer))) { - unsupported_entities_list[mode]++; - } - mode = data; - layer = QString(); - name = QString(); - iddata = QString(); - dimtype = 0; - for (int i = 0; i < 7; i++) - for (int j = 0; j < 2; j++) - coords[i][j] = 0; - xverts.clear(); - yverts.clear(); - radius = arc_start_angle = arc_stop_angle = 0; - ellipse_start_angle = ellipse_stop_angle = 0; - if (mode == "INSERT") { - ellipse_start_angle = ellipse_stop_angle = 1.0; // scale - } - break; - case 1: - name = data; - break; - case 2: - iddata = data; - break; - case 8: - layer = data; - break; - case 10: - if (in_blocks_section) - xverts.append((data.toDouble())); - else - xverts.append((data.toDouble() - xorigin) * scale); - break; - case 11: - if (in_blocks_section) - xverts.append((data.toDouble())); - else - xverts.append((data.toDouble() - xorigin) * scale); - break; - case 20: - if (in_blocks_section) - yverts.append((data.toDouble())); - else - yverts.append((data.toDouble() - yorigin) * scale); - break; - case 21: - if (in_blocks_section) - yverts.append((data.toDouble())); - else - yverts.append((data.toDouble() - yorigin) * scale); - break; - case 40: - // CIRCLE, ARC: radius - // ELLIPSE: minor to major ratio - // DIMENSION (radial, diameter): Leader length - radius = data.toDouble(); - if (!in_blocks_section) radius *= scale; - break; - case 41: - // ELLIPSE: start_angle - // INSERT: X scale - ellipse_start_angle = data.toDouble(); - break; - case 50: - // ARC: start_angle - // INSERT: rot angle - // DIMENSION: linear and rotated: angle - arc_start_angle = data.toDouble(); - break; - case 42: - // ELLIPSE: stop_angle - // INSERT: Y scale - ellipse_stop_angle = data.toDouble(); - break; - case 51: // ARC - arc_stop_angle = data.toDouble(); - break; - case 70: - // LWPOLYLINE: polyline flag - // DIMENSION: dimension type - dimtype = data.toInt(); - break; - } - } - - QHashIterator i(unsupported_entities_list); - while (i.hasNext()) { - i.next(); - if (layername.isNull()) { - PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in `%3'.", - i.key(), QString::number(i.value()), filename); - } else { - PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in layer `%3' of `%4'.", - i.key(), QString::number(i.value()), layername, filename); - } - } - - // Extract paths from parsed data - - QHash enabled_lines; - for (int i = 0; i < lines.count(); i++) { - enabled_lines[i] = i; - } - - // extract all open paths - while (enabled_lines.count() > 0) - { - int current_line, current_point; - - foreach (int i, enabled_lines) { - for (int j = 0; j < 2; j++) { - QVector *lv = &grid.data(lines[i].p[j]->x, lines[i].p[j]->y); - for (int ki = 0; ki < lv->count(); ki++) { - int k = lv->at(ki); - if (k == i || lines[k].disabled) - continue; - goto next_open_path_j; - } - current_line = i; - current_point = j; - goto create_open_path; - next_open_path_j:; - } - } - - break; - - create_open_path: - this->paths.append(Path()); - Path *this_path = &this->paths.last(); - - this_path->points.append(lines[current_line].p[current_point]); - while (1) { - this_path->points.append(lines[current_line].p[!current_point]); - Point *ref_point = lines[current_line].p[!current_point]; - lines[current_line].disabled = true; - enabled_lines.remove(current_line); - QVector *lv = &grid.data(ref_point->x, ref_point->y); - for (int ki = 0; ki < lv->count(); ki++) { - int k = lv->at(ki); - if (lines[k].disabled) - continue; - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { - current_line = k; - current_point = 0; - goto found_next_line_in_open_path; - } - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { - current_line = k; - current_point = 1; - goto found_next_line_in_open_path; - } - } - break; - found_next_line_in_open_path:; - } - } - - // extract all closed paths - while (enabled_lines.count() > 0) - { - int current_line = enabled_lines.begin().value(), current_point = 0; - - this->paths.append(Path()); - Path *this_path = &this->paths.last(); - this_path->is_closed = true; - - this_path->points.append(lines[current_line].p[current_point]); - while (1) { - this_path->points.append(lines[current_line].p[!current_point]); - Point *ref_point = lines[current_line].p[!current_point]; - lines[current_line].disabled = true; - enabled_lines.remove(current_line); - QVector *lv = &grid.data(ref_point->x, ref_point->y); - for (int ki = 0; ki < lv->count(); ki++) { - int k = lv->at(ki); - if (lines[k].disabled) - continue; - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { - current_line = k; - current_point = 0; - goto found_next_line_in_closed_path; - } - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { - current_line = k; - current_point = 1; - goto found_next_line_in_closed_path; - } - } - break; - found_next_line_in_closed_path:; - } - } - - fixup_path_direction(); - -#if 0 - printf("----- DXF Data -----\n"); - for (int i = 0; i < this->paths.count(); i++) { - printf("Path %d (%s):\n", i, this->paths[i].is_closed ? "closed" : "open"); - for (int j = 0; j < this->paths[i].points.count(); j++) - printf(" %f %f\n", this->paths[i].points[j]->x, this->paths[i].points[j]->y); - } - printf("--------------------\n"); - fflush(stdout); -#endif -} - -/*! - Ensures that all paths have the same vertex ordering. - FIXME: CW or CCW? -*/ -void DxfData::fixup_path_direction() -{ - for (int i = 0; i < this->paths.count(); i++) { - if (!this->paths[i].is_closed) - break; - this->paths[i].is_inner = true; - double min_x = this->paths[i].points[0]->x; - int min_x_point = 0; - for (int j = 1; j < this->paths[i].points.count(); j++) { - if (this->paths[i].points[j]->x < min_x) { - min_x = this->paths[i].points[j]->x; - min_x_point = j; - } - } - // rotate points if the path is in non-standard rotation - int b = min_x_point; - int a = b == 0 ? this->paths[i].points.count() - 2 : b - 1; - int c = b == this->paths[i].points.count() - 1 ? 1 : b + 1; - double ax = this->paths[i].points[a]->x - this->paths[i].points[b]->x; - double ay = this->paths[i].points[a]->y - this->paths[i].points[b]->y; - double cx = this->paths[i].points[c]->x - this->paths[i].points[b]->x; - double cy = this->paths[i].points[c]->y - this->paths[i].points[b]->y; -#if 0 - printf("Rotate check:\n"); - printf(" a/b/c indices = %d %d %d\n", a, b, c); - printf(" b->a vector = %f %f (%f)\n", ax, ay, atan2(ax, ay)); - printf(" b->c vector = %f %f (%f)\n", cx, cy, atan2(cx, cy)); -#endif - // FIXME: atan2() usually takes y,x. This variant probably makes the path clockwise.. - if (atan2(ax, ay) < atan2(cx, cy)) { - for (int j = 0; j < this->paths[i].points.count()/2; j++) - this->paths[i].points.swap(j, this->paths[i].points.count()-1-j); - } - } -} - -DxfData::Point *DxfData::addPoint(double x, double y) -{ - this->points.append(Point(x, y)); - return &this->points.last(); -} - diff --git a/dxfdim.cc b/dxfdim.cc deleted file mode 100644 index 359606a..0000000 --- a/dxfdim.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include - -QHash dxf_dim_cache; -QHash dxf_cross_cache; - -Value builtin_dxf_dim(const QVector &argnames, const QVector &args) -{ - QString filename; - QString layername; - QString name; - double xorigin = 0; - double yorigin = 0; - double scale = 1; - - for (int i = 0; i < argnames.count() && i < args.count(); i++) { - if (argnames[i] == "file") - filename = args[i].text; - if (argnames[i] == "layer") - layername = args[i].text; - if (argnames[i] == "origin") - args[i].getv2(xorigin, yorigin); - if (argnames[i] == "scale") - args[i].getnum(scale); - if (argnames[i] == "name") - name = args[i].text; - } - - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - - QString key = filename + "|" + layername + "|" + name + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + - "|" + QString::number(scale) + "|" + QString::number(st.st_mtime) + "|" + QString::number(st.st_size); - - if (dxf_dim_cache.contains(key)) - return dxf_dim_cache[key]; - - DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); - - for (int i = 0; i < dxf.dims.count(); i++) - { - if (!name.isNull() && dxf.dims[i].name != name) - continue; - - DxfData::Dim *d = &dxf.dims[i]; - int type = d->type & 7; - - if (type == 0) { - // Rotated, horizontal or vertical - double x = d->coords[4][0] - d->coords[3][0]; - double y = d->coords[4][1] - d->coords[3][1]; - double angle = d->angle; - double distance_projected_on_line = fabs(x * cos(angle*M_PI/180) + y * sin(angle*M_PI/180)); - return dxf_dim_cache[key] = Value(distance_projected_on_line); - } - else if (type == 1) { - // Aligned - double x = d->coords[4][0] - d->coords[3][0]; - double y = d->coords[4][1] - d->coords[3][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); - } - else if (type == 2) { - // Angular - double a1 = atan2(d->coords[0][0] - d->coords[5][0], d->coords[0][1] - d->coords[5][1]); - double a2 = atan2(d->coords[4][0] - d->coords[3][0], d->coords[4][1] - d->coords[3][1]); - return dxf_dim_cache[key] = Value(fabs(a1 - a2) * 180 / M_PI); - } - else if (type == 3 || type == 4) { - // Diameter or Radius - double x = d->coords[5][0] - d->coords[0][0]; - double y = d->coords[5][1] - d->coords[0][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); - } - else if (type == 5) { - // Angular 3 Point - } - else if (type == 6) { - // Ordinate - return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); - } - - PRINTA("WARNING: Dimension `%1' in `%2', layer `%3' has unsupported type!", name, filename, layername); - return Value(); - } - - PRINTA("WARNING: Can't find dimension `%1' in `%2', layer `%3'!", name, filename, layername); - - return Value(); -} - -Value builtin_dxf_cross(const QVector &argnames, const QVector &args) -{ - QString filename; - QString layername; - double xorigin = 0; - double yorigin = 0; - double scale = 1; - - for (int i = 0; i < argnames.count() && i < args.count(); i++) { - if (argnames[i] == "file") - filename = args[i].text; - if (argnames[i] == "layer") - layername = args[i].text; - if (argnames[i] == "origin") - args[i].getv2(xorigin, yorigin); - if (argnames[i] == "scale") - args[i].getnum(scale); - } - - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - - QString key = filename + "|" + layername + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + - "|" + QString::number(scale) + "|" + QString::number(st.st_mtime) + "|" + QString::number(st.st_size); - - if (dxf_cross_cache.contains(key)) - return dxf_cross_cache[key]; - - DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); - - double coords[4][2]; - - for (int i = 0, j = 0; i < dxf.paths.count(); i++) { - if (dxf.paths[i].points.count() != 2) - continue; - coords[j][0] = dxf.paths[i].points[0]->x; - coords[j++][1] = dxf.paths[i].points[0]->y; - coords[j][0] = dxf.paths[i].points[1]->x; - coords[j++][1] = dxf.paths[i].points[1]->y; - - if (j == 4) { - double x1 = coords[0][0], y1 = coords[0][1]; - double x2 = coords[1][0], y2 = coords[1][1]; - double x3 = coords[2][0], y3 = coords[2][1]; - double x4 = coords[3][0], y4 = coords[3][1]; - double dem = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1); - if (dem == 0) - break; - double ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3)) / dem; - // double ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)) / dem; - double x = x1 + ua*(x2 - x1); - double y = y1 + ua*(y2 - y1); - Value ret; - ret.type = Value::VECTOR; - ret.vec.append(new Value(x)); - ret.vec.append(new Value(y)); - return dxf_cross_cache[key] = ret; - } - } - - PRINTA("WARNING: Can't find cross in `%1', layer `%2'!", filename, layername); - - return Value(); -} - -void initialize_builtin_dxf_dim() -{ - builtin_functions["dxf_dim"] = new BuiltinFunction(&builtin_dxf_dim); - builtin_functions["dxf_cross"] = new BuiltinFunction(&builtin_dxf_cross); -} - diff --git a/dxflinextrude.cc b/dxflinextrude.cc deleted file mode 100644 index f7a1d24..0000000 --- a/dxflinextrude.cc +++ /dev/null @@ -1,359 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include - -#include -#include - -class DxfLinearExtrudeModule : public AbstractModule -{ -public: - DxfLinearExtrudeModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class DxfLinearExtrudeNode : public AbstractPolyNode -{ -public: - int convexity, slices; - double fn, fs, fa, height, twist; - double origin_x, origin_y, scale; - bool center, has_twist; - QString filename, layername; - DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { - convexity = slices = 0; - fn = fs = fa = height = twist = 0; - origin_x = origin_y = scale = 0; - center = has_twist = false; - } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - DxfLinearExtrudeNode *node = new DxfLinearExtrudeNode(inst); - - QVector argnames = QVector() << "file" << "layer" << "height" << "origin" << "scale" << "center" << "twist" << "slices"; - QVector argexpr; - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - node->fn = c.lookup_variable("$fn").num; - node->fs = c.lookup_variable("$fs").num; - node->fa = c.lookup_variable("$fa").num; - - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value height = c.lookup_variable("height", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); - Value center = c.lookup_variable("center", true); - Value twist = c.lookup_variable("twist", true); - Value slices = c.lookup_variable("slices", true); - - node->filename = file.text; - node->layername = layer.text; - node->height = height.num; - node->convexity = (int)convexity.num; - origin.getv2(node->origin_x, node->origin_y); - node->scale = scale.num; - - if (center.type == Value::BOOL) - node->center = center.b; - - if (node->height <= 0) - node->height = 100; - - if (node->convexity <= 0) - node->convexity = 1; - - if (node->scale <= 0) - node->scale = 1; - - if (twist.type == Value::NUMBER) { - node->twist = twist.num; - if (slices.type == Value::NUMBER) { - node->slices = (int)slices.num; - } else { - node->slices = (int)fmax(2, fabs(get_fragments_from_r(node->height, - node->fn, node->fs, node->fa) * node->twist / 360)); - } - node->has_twist = true; - } - - if (node->filename.isEmpty()) { - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } - } - - return node; -} - -void register_builtin_dxf_linear_extrude() -{ - builtin_modules["dxf_linear_extrude"] = new DxfLinearExtrudeModule(); - builtin_modules["linear_extrude"] = new DxfLinearExtrudeModule(); -} - -static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2) -{ - for (int j = 1; j < pt->points.count(); j++) - { - int k = j - 1; - - double jx1 = pt->points[j]->x * cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180); - double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180); - - double jx2 = pt->points[j]->x * cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180); - double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180); - - double kx1 = pt->points[k]->x * cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180); - double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180); - - double kx2 = pt->points[k]->x * cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180); - double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180); - - double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2); - double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1); - - if (dia1_len_sq > dia2_len_sq) - { - ps->append_poly(); - if (pt->is_inner) { - ps->append_vertex(kx1, ky1, h1); - ps->append_vertex(jx1, jy1, h1); - ps->append_vertex(jx2, jy2, h2); - } else { - ps->insert_vertex(kx1, ky1, h1); - ps->insert_vertex(jx1, jy1, h1); - ps->insert_vertex(jx2, jy2, h2); - } - - ps->append_poly(); - if (pt->is_inner) { - ps->append_vertex(kx2, ky2, h2); - ps->append_vertex(kx1, ky1, h1); - ps->append_vertex(jx2, jy2, h2); - } else { - ps->insert_vertex(kx2, ky2, h2); - ps->insert_vertex(kx1, ky1, h1); - ps->insert_vertex(jx2, jy2, h2); - } - } - else - { - ps->append_poly(); - if (pt->is_inner) { - ps->append_vertex(kx1, ky1, h1); - ps->append_vertex(jx1, jy1, h1); - ps->append_vertex(kx2, ky2, h2); - } else { - ps->insert_vertex(kx1, ky1, h1); - ps->insert_vertex(jx1, jy1, h1); - ps->insert_vertex(kx2, ky2, h2); - } - - ps->append_poly(); - if (pt->is_inner) { - ps->append_vertex(jx2, jy2, h2); - ps->append_vertex(kx2, ky2, h2); - ps->append_vertex(jx1, jy1, h1); - } else { - ps->insert_vertex(jx2, jy2, h2); - ps->insert_vertex(kx2, ky2, h2); - ps->insert_vertex(jx1, jy1, h1); - } - } - } -} - -static void report_func(const class AbstractNode*, void *vp, int mark) -{ - QProgressDialog *pd = (QProgressDialog*)vp; - int v = (int)((mark*100.0) / progress_report_count); - pd->setValue(v < 100 ? v : 99); - QString label; - label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); - pd->setLabelText(label); - QApplication::processEvents(); -} - -PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e rm) const -{ - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return PolySet::ps_cache[key]->ps->link(); - } - - print_messages_push(); - DxfData *dxf; - - if (filename.isEmpty()) - { - QTime t; - QProgressDialog *pd = NULL; - - if (rm == RENDER_OPENCSG) - { - PRINT_NOCACHE("Processing uncached linear_extrude outline..."); - QApplication::processEvents(); - - t.start(); - pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); - pd->setValue(0); - pd->setAutoClose(false); - pd->show(); - QApplication::processEvents(); - - progress_report_prep((AbstractNode*)this, report_func, pd); - } - - // Before extruding, union all (2D) children nodes - // to a single DxfData, then tessealte this into a PolySet - CGAL_Nef_polyhedron N; - N.dim = 2; - foreach(AbstractNode * v, children) { - if (v->modinst->tag_background) - continue; - N.p2 += v->render_cgal_nef_polyhedron().p2; - } - dxf = new DxfData(N); - - if (rm == RENDER_OPENCSG) { - progress_report_fin(); - int s = t.elapsed() / 1000; - PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - delete pd; - } - } else { - dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); - } - - PolySet *ps = new PolySet(); - ps->convexity = convexity; - - double h1, h2; - - if (center) { - h1 = -height/2.0; - h2 = +height/2.0; - } else { - h1 = 0; - h2 = height; - } - - bool first_open_path = true; - for (int i = 0; i < dxf->paths.count(); i++) - { - if (dxf->paths[i].is_closed) - continue; - if (first_open_path) { - PRINTF("WARING: Open paths in dxf_liniear_extrude(file = \"%s\", layer = \"%s\"):", - filename.toAscii().data(), layername.toAscii().data()); - first_open_path = false; - } - PRINTF(" %9.5f %10.5f ... %10.5f %10.5f", - dxf->paths[i].points.first()->x / scale + origin_x, - dxf->paths[i].points.first()->y / scale + origin_y, - dxf->paths[i].points.last()->x / scale + origin_x, - dxf->paths[i].points.last()->y / scale + origin_y); - } - - - if (has_twist) - { - dxf_tesselate(ps, dxf, 0, false, true, h1); - dxf_tesselate(ps, dxf, twist, true, true, h2); - for (int j = 0; j < slices; j++) - { - double t1 = twist*j / slices; - double t2 = twist*(j+1) / slices; - double g1 = h1 + (h2-h1)*j / slices; - double g2 = h1 + (h2-h1)*(j+1) / slices; - for (int i = 0; i < dxf->paths.count(); i++) - { - if (!dxf->paths[i].is_closed) - continue; - add_slice(ps, &dxf->paths[i], t1, t2, g1, g2); - } - } - } - else - { - dxf_tesselate(ps, dxf, 0, false, true, h1); - dxf_tesselate(ps, dxf, 0, true, true, h2); - for (int i = 0; i < dxf->paths.count(); i++) - { - if (!dxf->paths[i].is_closed) - continue; - add_slice(ps, &dxf->paths[i], 0, 0, h1, h2); - } - } - - PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); - print_messages_pop(); - delete dxf; - - return ps; -} - -QString DxfLinearExtrudeNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - text.sprintf("linear_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "height = %g, origin = [ %g %g ], scale = %g, center = %s, convexity = %d", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, - layername.toAscii().data(), height, origin_x, origin_y, scale, - center ? "true" : "false", convexity); - if (has_twist) { - QString t2; - t2.sprintf(", twist = %g, slices = %d", twist, slices); - text += t2; - } - QString t3; - t3.sprintf(", $fn = %g, $fa = %g, $fs = %g) {\n", fn, fs, fa); - text += t3; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} - diff --git a/dxfrotextrude.cc b/dxfrotextrude.cc deleted file mode 100644 index 427bcb0..0000000 --- a/dxfrotextrude.cc +++ /dev/null @@ -1,251 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include - -#include -#include - -class DxfRotateExtrudeModule : public AbstractModule -{ -public: - DxfRotateExtrudeModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class DxfRotateExtrudeNode : public AbstractPolyNode -{ -public: - int convexity; - double fn, fs, fa; - double origin_x, origin_y, scale; - QString filename, layername; - DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { - convexity = 0; - fn = fs = fa = 0; - origin_x = origin_y = scale = 0; - } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - DxfRotateExtrudeNode *node = new DxfRotateExtrudeNode(inst); - - QVector argnames = QVector() << "file" << "layer" << "origin" << "scale"; - QVector argexpr; - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - node->fn = c.lookup_variable("$fn").num; - node->fs = c.lookup_variable("$fs").num; - node->fa = c.lookup_variable("$fa").num; - - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); - - node->filename = file.text; - node->layername = layer.text; - node->convexity = (int)convexity.num; - origin.getv2(node->origin_x, node->origin_y); - node->scale = scale.num; - - if (node->convexity <= 0) - node->convexity = 1; - - if (node->scale <= 0) - node->scale = 1; - - if (node->filename.isEmpty()) { - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } - } - - return node; -} - -void register_builtin_dxf_rotate_extrude() -{ - builtin_modules["dxf_rotate_extrude"] = new DxfRotateExtrudeModule(); - builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule(); -} - -static void report_func(const class AbstractNode*, void *vp, int mark) -{ - QProgressDialog *pd = (QProgressDialog*)vp; - int v = (int)((mark*100.0) / progress_report_count); - pd->setValue(v < 100 ? v : 99); - QString label; - label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); - pd->setLabelText(label); - QApplication::processEvents(); -} - -PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e rm) const -{ - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return PolySet::ps_cache[key]->ps->link(); - } - - print_messages_push(); - DxfData *dxf; - - if (filename.isEmpty()) - { - QTime t; - QProgressDialog *pd; - - if (rm == RENDER_OPENCSG) - { - PRINT_NOCACHE("Processing uncached rotate_extrude outline..."); - QApplication::processEvents(); - - t.start(); - pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); - pd->setValue(0); - pd->setAutoClose(false); - pd->show(); - QApplication::processEvents(); - - progress_report_prep((AbstractNode*)this, report_func, pd); - } - - CGAL_Nef_polyhedron N; - N.dim = 2; - foreach(AbstractNode * v, children) { - if (v->modinst->tag_background) - continue; - N.p2 += v->render_cgal_nef_polyhedron().p2; - } - dxf = new DxfData(N); - - if (rm == RENDER_OPENCSG) { - progress_report_fin(); - int s = t.elapsed() / 1000; - PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - delete pd; - } - } else { - dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); - } - - PolySet *ps = new PolySet(); - ps->convexity = convexity; - - for (int i = 0; i < dxf->paths.count(); i++) - { - double max_x = 0; - for (int j = 0; j < dxf->paths[i].points.count(); j++) { - max_x = fmax(max_x, dxf->paths[i].points[j]->x); - } - - int fragments = get_fragments_from_r(max_x, fn, fs, fa); - - double points[fragments][dxf->paths[i].points.count()][3]; - - for (int j = 0; j < fragments; j++) { - double a = (j*2*M_PI) / fragments; - for (int k = 0; k < dxf->paths[i].points.count(); k++) { - if (dxf->paths[i].points[k]->x == 0) { - points[j][k][0] = 0; - points[j][k][1] = 0; - } else { - points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); - points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); - } - points[j][k][2] = dxf->paths[i].points[k]->y; - } - } - - for (int j = 0; j < fragments; j++) { - int j1 = j + 1 < fragments ? j + 1 : 0; - for (int k = 0; k < dxf->paths[i].points.count(); k++) { - int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; - if (points[j][k][0] != points[j1][k][0] || - points[j][k][1] != points[j1][k][1] || - points[j][k][2] != points[j1][k][2]) { - ps->append_poly(); - ps->append_vertex(points[j ][k ][0], - points[j ][k ][1], points[j ][k ][2]); - ps->append_vertex(points[j1][k ][0], - points[j1][k ][1], points[j1][k ][2]); - ps->append_vertex(points[j ][k1][0], - points[j ][k1][1], points[j ][k1][2]); - } - if (points[j][k1][0] != points[j1][k1][0] || - points[j][k1][1] != points[j1][k1][1] || - points[j][k1][2] != points[j1][k1][2]) { - ps->append_poly(); - ps->append_vertex(points[j ][k1][0], - points[j ][k1][1], points[j ][k1][2]); - ps->append_vertex(points[j1][k ][0], - points[j1][k ][1], points[j1][k ][2]); - ps->append_vertex(points[j1][k1][0], - points[j1][k1][1], points[j1][k1][2]); - } - } - } - } - - PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); - print_messages_pop(); - delete dxf; - - return ps; -} - -QString DxfRotateExtrudeNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - text.sprintf("rotate_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "origin = [ %g %g ], scale = %g, convexity = %d, " - "$fn = %g, $fa = %g, $fs = %g) {\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, - layername.toAscii().data(), origin_x, origin_y, scale, convexity, - fn, fs, fa); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} - diff --git a/dxftess-cgal.cc b/dxftess-cgal.cc deleted file mode 100644 index fa879b6..0000000 --- a/dxftess-cgal.cc +++ /dev/null @@ -1,103 +0,0 @@ -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include -#include -#include -#include - -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Triangulation_vertex_base_2 Vb; -typedef CGAL::Delaunay_mesh_face_base_2 Fb; -typedef CGAL::Triangulation_data_structure_2 Tds; -typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; -//typedef CGAL::Delaunay_mesh_criteria_2 Criteria; - -typedef CDT::Vertex_handle Vertex_handle; -typedef CDT::Point CDTPoint; - -#include - -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(); } -}; - -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h) -{ - CDT cdt; - - // - Grid3d< QPair > point_to_path(GRID_FINE); - - for (int i = 0; i < dxf->paths.count(); i++) { - if (!dxf->paths[i].is_closed) - continue; - Vertex_handle first, prev; - for (int j = 1; j < dxf->paths[i].points.count(); j++) { - double x = dxf->paths[i].points[j]->x; - double y = dxf->paths[i].points[j]->y; - point_to_path.data(x, y, h) = QPair(i, j); - Vertex_handle vh = cdt.insert(CDTPoint(x, y)); - if (j == 1) { - first = vh; - } - else { - cdt.insert_constraint(prev, vh); - } - prev = vh; - } - cdt.insert_constraint(prev, first); - } - - std::list list_of_seeds; -// FIXME: Give hints about holes here -// list_of_seeds.push_back(CDTPoint(-1, -1)); -// list_of_seeds.push_back(CDTPoint(20, 50)); - CGAL::refine_Delaunay_mesh_2_without_edge_refinement(cdt, list_of_seeds.begin(), list_of_seeds.end(), - DummyCriteria()); - CDT::Finite_faces_iterator iter = cdt.finite_faces_begin(); - for( ; iter != cdt.finite_faces_end(); ++iter) { - if (!iter->is_in_domain()) continue; - ps->append_poly(); - - int path[3], point[3]; - for (int i=0;i<3;i++) { - int idx = up ? i : (2-i); - double px = iter->vertex(idx)->point()[0]; - double py = iter->vertex(idx)->point()[1]; - ps->append_vertex(px * cos(rot*M_PI/180) + py * sin(rot*M_PI/180), - px * -sin(rot*M_PI/180) + py * cos(rot*M_PI/180), - h); - path[i] = point_to_path.data(px, py, h).first; - point[i] = point_to_path.data(px, py, h).second; - } - - if (path[0] == path[1] && point[0] == 1 && point[1] == 2) - dxf->paths[path[0]].is_inner = up; - if (path[0] == path[1] && point[0] == 2 && point[1] == 1) - dxf->paths[path[0]].is_inner = !up; - if (path[1] == path[2] && point[1] == 1 && point[2] == 2) - dxf->paths[path[1]].is_inner = up; - if (path[1] == path[2] && point[1] == 2 && point[2] == 1) - dxf->paths[path[1]].is_inner = !up; - - if (path[2] == path[0] && point[2] == 1 && point[0] == 2) - dxf->paths[path[2]].is_inner = up; - if (path[2] == path[0] && point[2] == 2 && point[0] == 1) - dxf->paths[path[2]].is_inner = !up; - } -} diff --git a/dxftess-glu.cc b/dxftess-glu.cc deleted file mode 100644 index 9e5f530..0000000 --- a/dxftess-glu.cc +++ /dev/null @@ -1,376 +0,0 @@ -#ifdef WIN32 -# define STDCALL __stdcall -#else -# define STDCALL -#endif - -#undef DEBUG_TRIANGLE_SPLITTING - -struct tess_vdata { - GLdouble v[3]; -}; - -struct tess_triangle { - GLdouble *p[3]; - tess_triangle() { p[0] = NULL; p[1] = NULL; p[2] = NULL; } - tess_triangle(double *p1, double *p2, double *p3) { p[0] = p1; p[1] = p2; p[2] = p3; } -}; - -static GLenum tess_type; -static int tess_count; -static QVector tess_tri; -static GLdouble *tess_p1, *tess_p2; - -static void STDCALL tess_vertex(void *vertex_data) -{ - GLdouble *p = (double*)vertex_data; -#if 0 - printf(" %d: %f %f %f\n", tess_count, p[0], p[1], p[2]); -#endif - if (tess_type == GL_TRIANGLE_FAN) { - if (tess_count == 0) { - tess_p1 = p; - } - if (tess_count == 1) { - tess_p2 = p; - } - if (tess_count > 1) { - tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); - tess_p2 = p; - } - } - if (tess_type == GL_TRIANGLE_STRIP) { - if (tess_count == 0) { - tess_p1 = p; - } - if (tess_count == 1) { - tess_p2 = p; - } - if (tess_count > 1) { - if (tess_count % 2 == 1) { - tess_tri.append(tess_triangle(tess_p2, tess_p1, p)); - } else { - tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); - } - tess_p1 = tess_p2; - tess_p2 = p; - } - } - if (tess_type == GL_TRIANGLES) { - if (tess_count == 0) { - tess_p1 = p; - } - if (tess_count == 1) { - tess_p2 = p; - } - if (tess_count == 2) { - tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); - tess_count = -1; - } - } - tess_count++; -} - -static void STDCALL tess_begin(GLenum type) -{ -#if 0 - if (type == GL_TRIANGLE_FAN) { - printf("GL_TRIANGLE_FAN:\n"); - } - if (type == GL_TRIANGLE_STRIP) { - printf("GL_TRIANGLE_STRIP:\n"); - } - if (type == GL_TRIANGLES) { - printf("GL_TRIANGLES:\n"); - } -#endif - tess_count = 0; - tess_type = type; -} - -static void STDCALL tess_end(void) -{ - /* nothing to be done here */ -} - -static void STDCALL tess_error(GLenum errno) -{ - fprintf(stderr, "GLU tesselation error %s", gluErrorString(errno)); - PRINTF("GLU tesselation error %s", gluErrorString(errno)); -} - -static void STDCALL tess_begin_data() -{ - PRINTF("GLU tesselation BEGIN_DATA\n"); -} - -static void STDCALL tess_edge_flag(GLboolean flag) -{ -// PRINTF("GLU tesselation EDGE_FLAG\n"); -} - -static void STDCALL tess_edge_flag_data(GLboolean flag, void *polygon_data) -{ - PRINTF("GLU tesselation EDGE_FLAG_DATA\n"); -} -static void STDCALL tess_vertex_data(void *vertex_data, void *polygon_data) -{ - PRINTF("GLU tesselation VERTEX_DATA\n"); -} -static void STDCALL tess_end_data(void *polygon_data) -{ - PRINTF("GLU tesselation END_DATA\n"); -} -static void STDCALL tess_combine(GLdouble coords[3], void *vertex_data[4], - GLfloat weight[4], void **outData ) -{ - PRINTF("GLU tesselation COMBINE\n"); -} -static void STDCALL tess_combine_data(GLdouble coords[3], void *vertex_data[4], - GLfloat weight[4], void **outData, - void *polygon_data) -{ - PRINTF("GLU tesselation COMBINE_DATA\n"); -} -static void STDCALL tess_error_data(GLenum errno, void *polygon_data ) -{ - PRINTF("GLU tesselation ERROR_DATA\n"); -} - -static bool point_on_line(double *p1, double *p2, double *p3) -{ - if (fabs(p1[0] - p2[0]) < 0.00001 && fabs(p1[1] - p2[1]) < 0.00001) - return false; - - if (fabs(p3[0] - p2[0]) < 0.00001 && fabs(p3[1] - p2[1]) < 0.00001) - return false; - - double v1[2] = { p2[0] - p1[0], p2[1] - p1[1] }; - double v2[2] = { p3[0] - p1[0], p3[1] - p1[1] }; - - if (sqrt(v1[0]*v1[0] + v1[1]*v1[1]) > sqrt(v2[0]*v2[0] + v2[1]*v2[1])) - return false; - - if (fabs(v1[0]) > fabs(v1[1])) { - // y = x * dy/dx - if (v2[0] == 0 || ((v1[0] > 0) != (v2[0] > 0))) - return false; - double v1_dy_dx = v1[1] / v1[0]; - double v2_dy_dx = v2[1] / v2[0]; - if (fabs(v1_dy_dx - v2_dy_dx) > 1e-15) - return false; - } else { - // x = y * dx/dy - if (v2[1] == 0 || ((v1[1] > 0) != (v2[1] > 0))) - return false; - double v1_dy_dx = v1[0] / v1[1]; - double v2_dy_dx = v2[0] / v2[1]; - if (fabs(v1_dy_dx - v2_dy_dx) > 1e-15) - return false; - } - -#if 0 - printf("Point on line: %f/%f %f/%f %f/%f\n", p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); -#endif - return true; -} - -/*! - up: true if the polygon is facing in the normal direction (i.e. normal = [0,0,1]) - rot: CLOCKWISE rotation around positive Z axis - */ - -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h) -{ - GLUtesselator *tobj = gluNewTess(); - - gluTessCallback(tobj, GLU_TESS_VERTEX, (void(STDCALL *)())&tess_vertex); - gluTessCallback(tobj, GLU_TESS_BEGIN, (void(STDCALL *)())&tess_begin); - gluTessCallback(tobj, GLU_TESS_END, (void(STDCALL *)())&tess_end); - gluTessCallback(tobj, GLU_TESS_ERROR, (void(STDCALL *)())&tess_error); - - gluTessCallback(tobj, GLU_TESS_EDGE_FLAG, (void(STDCALL *)())&tess_edge_flag); -// gluTessCallback(tobj, GLU_TESS_COMBINE, (void(STDCALL *)())&tess_combine); - -/* gluTessCallback(tobj, GLU_TESS_BEGIN_DATA, (void(STDCALL *)())&tess_begin_data); */ -/* gluTessCallback(tobj, GLU_TESS_EDGE_FLAG_DATA, (void(STDCALL *)())&tess_edge_flag_data); */ -/* gluTessCallback(tobj, GLU_TESS_VERTEX_DATA, (void(STDCALL *)())&tess_vertex_data); */ -/* gluTessCallback(tobj, GLU_TESS_END_DATA, (void(STDCALL *)())&tess_end_data); */ -/* gluTessCallback(tobj, GLU_TESS_COMBINE_DATA, (void(STDCALL *)())&tess_combine_data); */ -/* gluTessCallback(tobj, GLU_TESS_ERROR_DATA, (void(STDCALL *)())&tess_error_data); */ - - - tess_tri.clear(); - QList vl; - - gluTessBeginPolygon(tobj, NULL); - - gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); - if (up) { - gluTessNormal(tobj, 0, 0, -1); - } else { - gluTessNormal(tobj, 0, 0, +1); - } - - Grid3d< QPair > point_to_path(GRID_COARSE); - - for (int i = 0; i < dxf->paths.count(); i++) { - if (!dxf->paths[i].is_closed) - continue; - gluTessBeginContour(tobj); - for (int j = 1; j < dxf->paths[i].points.count(); j++) { - point_to_path.data(dxf->paths[i].points[j]->x, - dxf->paths[i].points[j]->y, - h) = QPair(i, j); - vl.append(tess_vdata()); - vl.last().v[0] = dxf->paths[i].points[j]->x; - vl.last().v[1] = dxf->paths[i].points[j]->y; - vl.last().v[2] = h; - gluTessVertex(tobj, vl.last().v, vl.last().v); - } - gluTessEndContour(tobj); - } - - gluTessEndPolygon(tobj); - gluDeleteTess(tobj); - -#if 0 - for (int i = 0; i < tess_tri.count(); i++) { - printf("~~~\n"); - printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]); - printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]); - printf(" %f %f %f\n", tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]); - } -#endif - - // GLU tessing sometimes generates degenerated triangles. We must find and remove - // them so we can use the triangle array with CGAL.. - for (int i = 0; i < tess_tri.count(); i++) { - if (point_on_line(tess_tri[i].p[0], tess_tri[i].p[1], tess_tri[i].p[2]) || - point_on_line(tess_tri[i].p[1], tess_tri[i].p[2], tess_tri[i].p[0]) || - point_on_line(tess_tri[i].p[2], tess_tri[i].p[0], tess_tri[i].p[1])) { - // printf("DEBUG: Removed triangle\n"); - tess_tri.remove(i--); - } - } - - // GLU tessing creates T-junctions. This is ok for GL displaying but creates - // invalid polyhedrons for CGAL. So we split this tirangles up again in order - // to create polyhedrons that are also accepted by CGAL.. - // All triangle edges are sorted by their atan2 and only edges with a simmilar atan2 - // value are compared. This speeds up this code block dramatically (compared to the - // n^2 compares that are neccessary in the trivial implementation). -#if 1 - if (do_triangle_splitting) - { - bool added_triangles = true; - typedef QPair QPair_ii; - QHash tri_by_atan2; - for (int i = 0; i < tess_tri.count(); i++) - for (int j = 0; j < 3; j++) { - int ai = (int)round(atan2(fabs(tess_tri[i].p[(j+1)%3][0] - tess_tri[i].p[j][0]), - fabs(tess_tri[i].p[(j+1)%3][1] - tess_tri[i].p[j][1])) / 0.001); - tri_by_atan2.insertMulti(ai, QPair(i, j)); - } - while (added_triangles) - { - added_triangles = false; -#ifdef DEBUG_TRIANGLE_SPLITTING - printf("*** Triangle splitting (%d) ***\n", tess_tri.count()+1); -#endif - for (int i = 0; i < tess_tri.count(); i++) - for (int k = 0; k < 3; k++) - { - QHash possible_neigh; - int ai = (int)floor(atan2(fabs(tess_tri[i].p[(k+1)%3][0] - tess_tri[i].p[k][0]), - fabs(tess_tri[i].p[(k+1)%3][1] - tess_tri[i].p[k][1])) / 0.001 - 0.5); - for (int j = 0; j < 2; j++) { - foreach (const QPair_ii &jl, tri_by_atan2.values(ai+j)) - if (i != jl.first) - possible_neigh[jl] = jl; - } -#ifdef DEBUG_TRIANGLE_SPLITTING - printf("%d/%d: %d\n", i, k, possible_neigh.count()); -#endif - foreach (const QPair_ii &jl, possible_neigh) { - int j = jl.first; - for (int l = jl.second; l != (jl.second + 2) % 3; l = (l + 1) % 3) - if (point_on_line(tess_tri[i].p[k], tess_tri[j].p[l], tess_tri[i].p[(k+1)%3])) { -#ifdef DEBUG_TRIANGLE_SPLITTING - printf("%% %f %f %f %f %f %f [%d %d]\n", - tess_tri[i].p[k][0], tess_tri[i].p[k][1], - tess_tri[j].p[l][0], tess_tri[j].p[l][1], - tess_tri[i].p[(k+1)%3][0], tess_tri[i].p[(k+1)%3][1], - i, j); -#endif - tess_tri.append(tess_triangle(tess_tri[j].p[l], - tess_tri[i].p[(k+1)%3], tess_tri[i].p[(k+2)%3])); - for (int m = 0; m < 2; m++) { - int ai = (int)round(atan2(fabs(tess_tri.last().p[(m+1)%3][0] - tess_tri.last().p[m][0]), - fabs(tess_tri.last().p[(m+1)%3][1] - tess_tri.last().p[m][1])) / 0.001 ); - tri_by_atan2.insertMulti(ai, QPair(tess_tri.count()-1, m)); - } - tess_tri[i].p[(k+1)%3] = tess_tri[j].p[l]; - for (int m = 0; m < 2; m++) { - int ai = (int)round(atan2(fabs(tess_tri[i].p[(m+1)%3][0] - tess_tri[i].p[m][0]), - fabs(tess_tri[i].p[(m+1)%3][1] - tess_tri[i].p[m][1])) / 0.001 ); - tri_by_atan2.insertMulti(ai, QPair(i, m)); - } - added_triangles = true; - } - } - } - } - } -#endif - - for (int i = 0; i < tess_tri.count(); i++) - { -#if 0 - printf("---\n"); - printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]); - printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]); - printf(" %f %f %f\n", tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]); -#endif - double x, y; - ps->append_poly(); - - x = tess_tri[i].p[0][0] * cos(rot*M_PI/180) + tess_tri[i].p[0][1] * sin(rot*M_PI/180); - y = tess_tri[i].p[0][0] * -sin(rot*M_PI/180) + tess_tri[i].p[0][1] * cos(rot*M_PI/180); - ps->insert_vertex(x, y, tess_tri[i].p[0][2]); - - x = tess_tri[i].p[1][0] * cos(rot*M_PI/180) + tess_tri[i].p[1][1] * sin(rot*M_PI/180); - y = tess_tri[i].p[1][0] * -sin(rot*M_PI/180) + tess_tri[i].p[1][1] * cos(rot*M_PI/180); - ps->insert_vertex(x, y, tess_tri[i].p[1][2]); - - x = tess_tri[i].p[2][0] * cos(rot*M_PI/180) + tess_tri[i].p[2][1] * sin(rot*M_PI/180); - y = tess_tri[i].p[2][0] * -sin(rot*M_PI/180) + tess_tri[i].p[2][1] * cos(rot*M_PI/180); - ps->insert_vertex(x, y, tess_tri[i].p[2][2]); - - int i0 = point_to_path.data(tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]).first; - int j0 = point_to_path.data(tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]).second; - - int i1 = point_to_path.data(tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]).first; - int j1 = point_to_path.data(tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]).second; - - int i2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).first; - int j2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).second; - - if (i0 == i1 && j0 == 1 && j1 == 2) - dxf->paths[i0].is_inner = !up; - if (i0 == i1 && j0 == 2 && j1 == 1) - dxf->paths[i0].is_inner = up; - - if (i1 == i2 && j1 == 1 && j2 == 2) - dxf->paths[i1].is_inner = !up; - if (i1 == i2 && j1 == 2 && j2 == 1) - dxf->paths[i1].is_inner = up; - - if (i2 == i0 && j2 == 1 && j0 == 2) - dxf->paths[i2].is_inner = !up; - if (i2 == i0 && j2 == 2 && j0 == 1) - dxf->paths[i2].is_inner = up; - } - - tess_tri.clear(); -} diff --git a/dxftess.cc b/dxftess.cc deleted file mode 100644 index c0d4c0c..0000000 --- a/dxftess.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#ifdef CGAL_TESSELATE -#include "dxftess-cgal.cc" -#else -#include "dxftess-glu.cc" -#endif // CGAL_TESSELATE - -/*! - Converts all paths in the given DxfData to PolySet::borders polygons - without tesselating. Vertex ordering of the resulting polygons - will follow the paths' is_inner flag. -*/ -void dxf_border_to_ps(PolySet *ps, DxfData *dxf) -{ - for (int i = 0; i < dxf->paths.count(); i++) { - const DxfData::Path &pt = dxf->paths[i]; - if (!pt.is_closed) - continue; - ps->borders.append(PolySet::Polygon()); - for (int j = 1; j < pt.points.count(); j++) { - double x = pt.points[j]->x, y = pt.points[j]->y, z = 0.0; - ps->grid.align(x, y, z); - if (pt.is_inner) { - ps->borders.last().append(PolySet::Point(x, y, z)); - } else { - ps->borders.last().insert(0, PolySet::Point(x, y, z)); - } - } - } -} diff --git a/export.cc b/export.cc deleted file mode 100644 index cdfbef6..0000000 --- a/export.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" -#include "MainWindow.h" - -#include - -#ifdef ENABLE_CGAL - -void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd) -{ - CGAL_Polyhedron P; - root_N->p3.convert_to_Polyhedron(P); - - typedef CGAL_Polyhedron::Vertex Vertex; - typedef CGAL_Polyhedron::Vertex_const_iterator VCI; - typedef CGAL_Polyhedron::Facet_const_iterator FCI; - typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; - - FILE *f = fopen(filename.toUtf8().data(), "w"); - if (!f) { - PRINTA("Can't open STL file \"%1\" for STL export: %2", - filename, QString(strerror(errno))); - MainWindow::current_win = NULL; - return; - } - fprintf(f, "solid\n"); - - int facet_count = 0; - for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - Vertex v1, v2, v3; - v1 = *VCI((hc++)->vertex()); - v3 = *VCI((hc++)->vertex()); - do { - v2 = v3; - v3 = *VCI((hc++)->vertex()); - double x1 = CGAL::to_double(v1.point().x()); - double y1 = CGAL::to_double(v1.point().y()); - double z1 = CGAL::to_double(v1.point().z()); - double x2 = CGAL::to_double(v2.point().x()); - double y2 = CGAL::to_double(v2.point().y()); - double z2 = CGAL::to_double(v2.point().z()); - double x3 = CGAL::to_double(v3.point().x()); - double y3 = CGAL::to_double(v3.point().y()); - double z3 = CGAL::to_double(v3.point().z()); - QString vs1, vs2, vs3; - vs1.sprintf("%f %f %f", x1, y1, z1); - vs2.sprintf("%f %f %f", x2, y2, z2); - vs3.sprintf("%f %f %f", x3, y3, z3); - if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) { - - double nx = (y1-y2)*(z1-z3) - (z1-z2)*(y1-y3); - double ny = (z1-z2)*(x1-x3) - (x1-x2)*(z1-z3); - double nz = (x1-x2)*(y1-y3) - (y1-y2)*(x1-x3); - double n_scale = 1 / sqrt(nx*nx + ny*ny + nz*nz); - fprintf(f, " facet normal %f %f %f\n", - nx * n_scale, ny * n_scale, nz * n_scale); - fprintf(f, " outer loop\n"); - fprintf(f, " vertex %s\n", vs1.toAscii().data()); - fprintf(f, " vertex %s\n", vs2.toAscii().data()); - fprintf(f, " vertex %s\n", vs3.toAscii().data()); - fprintf(f, " endloop\n"); - fprintf(f, " endfacet\n"); - } - } while (hc != hc_end); - if (pd) { - pd->setValue(facet_count++); - QApplication::processEvents(); - } - } - - fprintf(f, "endsolid\n"); - fclose(f); -} - -void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*) -{ - PRINTF("WARNING: OFF import is not implemented yet."); -} - -void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *) -{ - FILE *f = fopen(filename.toUtf8().data(), "w"); - if (!f) { - PRINTA("Can't open DXF file \"%1\" for DXF export: %2", - filename, QString(strerror(errno))); - MainWindow::current_win = NULL; - return; - } - - fprintf(f, " 0\n"); - fprintf(f, "SECTION\n"); - fprintf(f, " 2\n"); - fprintf(f, "ENTITIES\n"); - - DxfData dd(*root_N); - for (int i=0; ix; - double y1 = p1->y; - double x2 = p2->x; - double y2 = p2->y; - fprintf(f, " 0\n"); - fprintf(f, "LINE\n"); - fprintf(f, " 10\n"); - fprintf(f, "%f\n", x1); - fprintf(f, " 11\n"); - fprintf(f, "%f\n", x2); - fprintf(f, " 20\n"); - fprintf(f, "%f\n", y1); - fprintf(f, " 21\n"); - fprintf(f, "%f\n", y2); - } - } - - fprintf(f, " 0\n"); - fprintf(f, "ENDSEC\n"); - fprintf(f, " 0\n"); - fprintf(f, "EOF\n"); - - fclose(f); -} - -#endif - diff --git a/expr.cc b/expr.cc deleted file mode 100644 index e20e977..0000000 --- a/expr.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" - -Expression::Expression() -{ - const_value = NULL; -} - -Expression::~Expression() -{ - for (int i=0; i < children.size(); i++) - delete children[i]; - if (const_value) - delete const_value; -} - -Value Expression::evaluate(const Context *context) const -{ - if (type == "!") - return ! children[0]->evaluate(context); - if (type == "&&") - return children[0]->evaluate(context) && children[1]->evaluate(context); - if (type == "||") - return children[0]->evaluate(context) || children[1]->evaluate(context); - if (type == "*") - return children[0]->evaluate(context) * children[1]->evaluate(context); - if (type == "/") - return children[0]->evaluate(context) / children[1]->evaluate(context); - if (type == "%") - return children[0]->evaluate(context) % children[1]->evaluate(context); - if (type == "+") - return children[0]->evaluate(context) + children[1]->evaluate(context); - if (type == "-") - return children[0]->evaluate(context) - children[1]->evaluate(context); - if (type == "<") - return children[0]->evaluate(context) < children[1]->evaluate(context); - if (type == "<=") - return children[0]->evaluate(context) <= children[1]->evaluate(context); - if (type == "==") - return children[0]->evaluate(context) == children[1]->evaluate(context); - if (type == "!=") - return children[0]->evaluate(context) != children[1]->evaluate(context); - if (type == ">=") - return children[0]->evaluate(context) >= children[1]->evaluate(context); - if (type == ">") - return children[0]->evaluate(context) > children[1]->evaluate(context); - if (type == "?:") { - Value v = children[0]->evaluate(context); - if (v.type == Value::BOOL) - return children[v.b ? 1 : 2]->evaluate(context); - return Value(); - } - if (type == "[]") { - Value v1 = children[0]->evaluate(context); - Value v2 = children[1]->evaluate(context); - if (v1.type == Value::VECTOR && v2.type == Value::NUMBER) { - int i = (int)(v2.num); - if (i < v1.vec.size()) - return *v1.vec[i]; - } - return Value(); - } - if (type == "I") - return children[0]->evaluate(context).inv(); - if (type == "C") - return *const_value; - if (type == "R") { - Value v1 = children[0]->evaluate(context); - Value v2 = children[1]->evaluate(context); - Value v3 = children[2]->evaluate(context); - if (v1.type == Value::NUMBER && v2.type == Value::NUMBER && v3.type == Value::NUMBER) { - Value r = Value(); - r.type = Value::RANGE; - r.range_begin = v1.num; - r.range_step = v2.num; - r.range_end = v3.num; - return r; - } - return Value(); - } - if (type == "V") { - Value v; - v.type = Value::VECTOR; - for (int i = 0; i < children.size(); i++) - v.vec.append(new Value(children[i]->evaluate(context))); - return v; - } - if (type == "L") - return context->lookup_variable(var_name); - if (type == "N") - { - Value v = children[0]->evaluate(context); - - if (v.type == Value::VECTOR && var_name == QString("x")) - return *v.vec[0]; - if (v.type == Value::VECTOR && var_name == QString("y")) - return *v.vec[1]; - if (v.type == Value::VECTOR && var_name == QString("z")) - return *v.vec[2]; - - if (v.type == Value::RANGE && var_name == QString("begin")) - return Value(v.range_begin); - if (v.type == Value::RANGE && var_name == QString("step")) - return Value(v.range_step); - if (v.type == Value::RANGE && var_name == QString("end")) - return Value(v.range_end); - - return Value(); - } - if (type == "F") { - QVector argvalues; - for (int i=0; i < children.size(); i++) - argvalues.append(children[i]->evaluate(context)); - return context->evaluate_function(call_funcname, call_argnames, argvalues); - } - abort(); -} - -QString Expression::dump() const -{ - if (type == "*" || type == "/" || type == "%" || type == "+" || type == "-" || - type == "<" || type == "<=" || type == "==" || type == "!=" || type == ">=" || type == ">") - return QString("(%1 %2 %3)").arg(children[0]->dump(), QString(type), children[1]->dump()); - if (type == "?:") - return QString("(%1 ? %2 : %3)").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); - if (type == "[]") - return QString("(%1[%2])").arg(children[0]->dump(), children[1]->dump()); - if (type == "I") - return QString("(-%1)").arg(children[0]->dump()); - if (type == "C") - return const_value->dump(); - if (type == "R") - return QString("[%1 : %2 : %3]").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); - if (type == "V") { - QString text = QString("["); - for (int i=0; i < children.size(); i++) { - if (i > 0) - text += QString(", "); - text += children[i]->dump(); - } - return text + QString("]"); - } - if (type == "L") - return var_name; - if (type == "N") - return QString("(%1.%2)").arg(children[0]->dump(), var_name); - if (type == "F") { - QString text = call_funcname + QString("("); - for (int i=0; i < children.size(); i++) { - if (i > 0) - text += QString(", "); - if (!call_argnames[i].isEmpty()) - text += call_argnames[i] + QString(" = "); - text += children[i]->dump(); - } - return text + QString(")"); - } - abort(); -} - diff --git a/func.cc b/func.cc deleted file mode 100644 index 1d70d98..0000000 --- a/func.cc +++ /dev/null @@ -1,247 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" - -AbstractFunction::~AbstractFunction() -{ -} - -Value AbstractFunction::evaluate(const Context*, const QVector&, const QVector&) const -{ - return Value(); -} - -QString AbstractFunction::dump(QString indent, QString name) const -{ - return QString("%1abstract function %2();\n").arg(indent, name); -} - -Function::~Function() -{ - for (int i=0; i < argexpr.size(); i++) - delete argexpr[i]; - delete expr; -} - -Value Function::evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const -{ - Context c(ctx); - c.args(argnames, argexpr, call_argnames, call_argvalues); - if (expr) - return expr->evaluate(&c); - return Value(); -} - -QString Function::dump(QString indent, QString name) const -{ - QString text = QString("%1function %2(").arg(indent, name); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - text += argnames[i]; - if (argexpr[i]) - text += QString(" = ") + argexpr[i]->dump(); - } - text += QString(") = %1;\n").arg(expr->dump()); - return text; -} - -QHash builtin_functions; - -BuiltinFunction::~BuiltinFunction() -{ -} - -Value BuiltinFunction::evaluate(const Context*, const QVector &call_argnames, const QVector &call_argvalues) const -{ - return eval_func(call_argnames, call_argvalues); -} - -QString BuiltinFunction::dump(QString indent, QString name) const -{ - return QString("%1builtin function %2();\n").arg(indent, name); -} - -static double deg2rad(double x) -{ - while (x < 0.0) - x += 360.0; - while (x >= 360.0) - x -= 360.0; - x = x * M_PI * 2.0 / 360.0; - return x; -} - -static double rad2deg(double x) -{ - x = x * 360.0 / (M_PI * 2.0); - while (x < 0.0) - x += 360.0; - while (x >= 360.0) - x -= 360.0; - return x; -} - -Value builtin_min(const QVector&, const QVector &args) -{ - if (args.size() >= 1 && args[0].type == Value::NUMBER) { - double val = args[0].num; - for (int i = 1; i < args.size(); i++) - if (args[1].type == Value::NUMBER) - val = fmin(val, args[i].num); - return Value(val); - } - return Value(); -} - -Value builtin_max(const QVector&, const QVector &args) -{ - if (args.size() >= 1 && args[0].type == Value::NUMBER) { - double val = args[0].num; - for (int i = 1; i < args.size(); i++) - if (args[1].type == Value::NUMBER) - val = fmax(val, args[i].num); - return Value(val); - } - return Value(); -} - -Value builtin_sin(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(sin(deg2rad(args[0].num))); - return Value(); -} - -Value builtin_cos(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(cos(deg2rad(args[0].num))); - return Value(); -} - -Value builtin_asin(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(rad2deg(asin(args[0].num))); - return Value(); -} - -Value builtin_acos(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(rad2deg(acos(args[0].num))); - return Value(); -} - -Value builtin_tan(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(tan(deg2rad(args[0].num))); - return Value(); -} - -Value builtin_atan(const QVector&, const QVector &args) -{ - if (args.size() == 1 && args[0].type == Value::NUMBER) - return Value(rad2deg(atan(args[0].num))); - return Value(); -} - -Value builtin_atan2(const QVector&, const QVector &args) -{ - if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) - return Value(rad2deg(atan2(args[0].num, args[1].num))); - return Value(); -} - -Value builtin_pow(const QVector&, const QVector &args) -{ - if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) - return Value(pow(args[0].num, args[1].num)); - return Value(); -} - -Value builtin_str(const QVector&, const QVector &args) -{ - QString str; - for (int i = 0; i < args.size(); i++) - { - if (args[i].type == Value::STRING) - str += args[i].text; - else - str += args[i].dump(); - } - return Value(str); -} - -Value builtin_lookup(const QVector&, const QVector &args) -{ - double p, low_p, low_v, high_p, high_v; - if (args.size() < 2 || !args[0].getnum(p) || args[1].vec.size() < 2 || args[1].vec[0]->vec.size() < 2) - return Value(); - if (!args[1].vec[0]->getv2(low_p, low_v) || !args[1].vec[0]->getv2(high_p, high_v)) - return Value(); - for (int i = 1; i < args[1].vec.size(); i++) { - double this_p, this_v; - if (args[1].vec[i]->getv2(this_p, this_v)) { - if (this_p <= p && (this_p > low_p || low_p > p)) { - low_p = this_p; - low_v = this_v; - } - if (this_p >= p && (this_p < high_p || high_p < p)) { - high_p = this_p; - high_v = this_v; - } - } - } - if (p <= low_p) - return Value(low_v); - if (p >= high_p) - return Value(high_v); - double f = (p-low_p) / (high_p-low_p); - return Value(high_v * f + low_v * (1-f)); -} - -void initialize_builtin_functions() -{ - builtin_functions["min"] = new BuiltinFunction(&builtin_min); - builtin_functions["max"] = new BuiltinFunction(&builtin_max); - builtin_functions["sin"] = new BuiltinFunction(&builtin_sin); - builtin_functions["cos"] = new BuiltinFunction(&builtin_cos); - builtin_functions["asin"] = new BuiltinFunction(&builtin_asin); - builtin_functions["acos"] = new BuiltinFunction(&builtin_acos); - builtin_functions["tan"] = new BuiltinFunction(&builtin_tan); - builtin_functions["atan"] = new BuiltinFunction(&builtin_atan); - builtin_functions["atan2"] = new BuiltinFunction(&builtin_atan2); - builtin_functions["pow"] = new BuiltinFunction(&builtin_pow); - builtin_functions["str"] = new BuiltinFunction(&builtin_str); - builtin_functions["lookup"] = new BuiltinFunction(&builtin_lookup); - initialize_builtin_dxf_dim(); -} - -void destroy_builtin_functions() -{ - foreach (AbstractFunction *v, builtin_functions) - delete v; - builtin_functions.clear(); -} - diff --git a/glview.cc b/glview.cc deleted file mode 100644 index 1068318..0000000 --- a/glview.cc +++ /dev/null @@ -1,491 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" -#include "GLView.h" -#include "Preferences.h" - -#include -#include -#include -#include -#include - -#define FAR_FAR_AWAY 100000.0 - -GLView::GLView(QWidget *parent) : QGLWidget(parent) -{ - viewer_distance = 500; - object_rot_x = 35; - object_rot_y = 0; - object_rot_z = 25; - object_trans_x = 0; - object_trans_y = 0; - object_trans_z = 0; - - mouse_drag_active = false; - last_mouse_x = 0; - last_mouse_y = 0; - - orthomode = false; - showaxes = false; - showcrosshairs = false; - - renderfunc = NULL; - renderfunc_vp = NULL; - - for (int i = 0; i < 10; i++) - shaderinfo[i] = 0; - - statusLabel = NULL; - - setMouseTracking(true); -#ifdef ENABLE_OPENCSG - opencsg_support = true; -#endif -} - -void GLView::setRenderFunc(void (*func)(void*), void *userdata) -{ - this->renderfunc = func; - this->renderfunc_vp = userdata; -} - -void GLView::initializeGL() -{ - glEnable(GL_DEPTH_TEST); - glDepthRange(-FAR_FAR_AWAY, +FAR_FAR_AWAY); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -#ifdef ENABLE_OPENCSG - GLenum err = glewInit(); - if (GLEW_OK != err) { - fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); - } - const char *openscad_disable_gl20_env = getenv("OPENSCAD_DISABLE_GL20"); - if (openscad_disable_gl20_env && !strcmp(openscad_disable_gl20_env, "0")) - openscad_disable_gl20_env = NULL; - if (glewIsSupported("GL_VERSION_2_0") && openscad_disable_gl20_env == NULL) - { - const char *vs_source = - "uniform float xscale, yscale;\n" - "attribute vec3 pos_b, pos_c;\n" - "attribute vec3 trig, mask;\n" - "varying vec3 tp, tr;\n" - "varying float shading;\n" - "void main() {\n" - " vec4 p0 = gl_ModelViewProjectionMatrix * gl_Vertex;\n" - " vec4 p1 = gl_ModelViewProjectionMatrix * vec4(pos_b, 1.0);\n" - " vec4 p2 = gl_ModelViewProjectionMatrix * vec4(pos_c, 1.0);\n" - " float a = distance(vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n" - " float b = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w));\n" - " float c = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n" - " float s = (a + b + c) / 2.0;\n" - " float A = sqrt(s*(s-a)*(s-b)*(s-c));\n" - " float ha = 2.0*A/a;\n" - " gl_Position = p0;\n" - " tp = mask * ha;\n" - " tr = trig;\n" - " vec3 normal, lightDir;\n" - " normal = normalize(gl_NormalMatrix * gl_Normal);\n" - " lightDir = normalize(vec3(gl_LightSource[0].position));\n" - " shading = abs(dot(normal, lightDir));\n" - "}\n"; - - const char *fs_source = - "uniform vec4 color1, color2;\n" - "varying vec3 tp, tr, tmp;\n" - "varying float shading;\n" - "void main() {\n" - " gl_FragColor = vec4(color1.r * shading, color1.g * shading, color1.b * shading, color1.a);\n" - " if (tp.x < tr.x || tp.y < tr.y || tp.z < tr.z)\n" - " gl_FragColor = color2;\n" - "}\n"; - - GLuint vs = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vs, 1, (const GLchar**)&vs_source, NULL); - glCompileShader(vs); - - GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fs, 1, (const GLchar**)&fs_source, NULL); - glCompileShader(fs); - - GLuint edgeshader_prog = glCreateProgram(); - glAttachShader(edgeshader_prog, vs); - glAttachShader(edgeshader_prog, fs); - glLinkProgram(edgeshader_prog); - - shaderinfo[0] = edgeshader_prog; - shaderinfo[1] = glGetUniformLocation(edgeshader_prog, "color1"); - shaderinfo[2] = glGetUniformLocation(edgeshader_prog, "color2"); - shaderinfo[3] = glGetAttribLocation(edgeshader_prog, "trig"); - shaderinfo[4] = glGetAttribLocation(edgeshader_prog, "pos_b"); - shaderinfo[5] = glGetAttribLocation(edgeshader_prog, "pos_c"); - shaderinfo[6] = glGetAttribLocation(edgeshader_prog, "mask"); - shaderinfo[7] = glGetUniformLocation(edgeshader_prog, "xscale"); - shaderinfo[8] = glGetUniformLocation(edgeshader_prog, "yscale"); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - fprintf(stderr, "OpenGL Error: %s\n", gluErrorString(err)); - } - - GLint status; - glGetProgramiv(edgeshader_prog, GL_LINK_STATUS, &status); - if (status == GL_FALSE) { - int loglen; - char logbuffer[1000]; - glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); - fprintf(stderr, "OpenGL Program Linker Error:\n%.*s", loglen, logbuffer); - } else { - int loglen; - char logbuffer[1000]; - glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); - if (loglen > 0) { - fprintf(stderr, "OpenGL Program Link OK:\n%.*s", loglen, logbuffer); - } - glValidateProgram(edgeshader_prog); - glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); - if (loglen > 0) { - fprintf(stderr, "OpenGL Program Validation results:\n%.*s", loglen, logbuffer); - } - } - } else { - opencsg_support = false; - QTimer::singleShot(0, this, SLOT(display_opengl20_warning())); - } -#endif /* ENABLE_OPENCSG */ -} - -#ifdef ENABLE_OPENCSG -void GLView::display_opengl20_warning() -{ - QMessageBox::warning(NULL, "GLEW: GL_VERSION_2_0 is not supported!", - "Warning: No support for OpenGL 2.0 found! OpenCSG View has been disabled.\n\n" - "It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 " - "support. Please check if OpenGL 2.0 drivers are available for your " - "graphics hardware."); -} -#endif - -void GLView::resizeGL(int w, int h) -{ -#ifdef ENABLE_OPENCSG - shaderinfo[9] = w; - shaderinfo[10] = h; -#endif - glViewport(0, 0, w, h); - w_h_ratio = sqrt((double)w / (double)h); -} - -void GLView::paintGL() -{ - const QColor &col = Preferences::inst()->color(Preferences::BACKGROUND_COLOR); - glClearColor(col.redF(), col.greenF(), col.blueF(), 0.0); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (orthomode) - glOrtho(-w_h_ratio*viewer_distance/10, +w_h_ratio*viewer_distance/10, - -(1/w_h_ratio)*viewer_distance/10, +(1/w_h_ratio)*viewer_distance/10, - -FAR_FAR_AWAY, +FAR_FAR_AWAY); - else - glFrustum(-w_h_ratio, +w_h_ratio, -(1/w_h_ratio), +(1/w_h_ratio), +10.0, +FAR_FAR_AWAY); - gluLookAt(0.0, -viewer_distance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; - GLfloat light_position0[] = {-1.0, -1.0, +1.0, 0.0}; - GLfloat light_position1[] = {+1.0, +1.0, -1.0, 0.0}; - - glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); - glLightfv(GL_LIGHT0, GL_POSITION, light_position0); - glEnable(GL_LIGHT0); - glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); - glLightfv(GL_LIGHT1, GL_POSITION, light_position1); - glEnable(GL_LIGHT1); - glEnable(GL_LIGHTING); - glEnable(GL_NORMALIZE); - - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); - - glRotated(object_rot_x, 1.0, 0.0, 0.0); - glRotated(object_rot_y, 0.0, 1.0, 0.0); - glRotated(object_rot_z, 0.0, 0.0, 1.0); - - if (showcrosshairs) - { - glLineWidth(3); - const QColor &col = Preferences::inst()->color(Preferences::CROSSHAIR_COLOR); - glColor3f(col.redF(), col.greenF(), col.blueF()); - glBegin(GL_LINES); - for (double xf = -1; xf <= +1; xf += 2) - for (double yf = -1; yf <= +1; yf += 2) { - double vd = viewer_distance/20; - glVertex3d(-xf*vd, -yf*vd, -vd); - glVertex3d(+xf*vd, +yf*vd, +vd); - } - glEnd(); - } - - glTranslated(object_trans_x, object_trans_y, object_trans_z); - - // Large gray axis cross inline with the model - if (showaxes) - { - glLineWidth(1); - glColor3d(0.5, 0.5, 0.5); - glBegin(GL_LINES); - glVertex3d(-viewer_distance/10, 0, 0); - glVertex3d(+viewer_distance/10, 0, 0); - glVertex3d(0, -viewer_distance/10, 0); - glVertex3d(0, +viewer_distance/10, 0); - glVertex3d(0, 0, -viewer_distance/10); - glVertex3d(0, 0, +viewer_distance/10); - glEnd(); - } - - glDepthFunc(GL_LESS); - glCullFace(GL_BACK); - glDisable(GL_CULL_FACE); - - glLineWidth(2); - glColor3d(1.0, 0.0, 0.0); - - if (renderfunc) - renderfunc(renderfunc_vp); - - // Small axis cross in the lower left corner - // FIXME: Adjust color to keep contrast with background - if (showaxes) - { - glDepthFunc(GL_ALWAYS); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glTranslated(-0.8, -0.8, 0); - glOrtho(-w_h_ratio*1000/10, +w_h_ratio*1000/10, - -(1/w_h_ratio)*1000/10, +(1/w_h_ratio)*1000/10, - -FAR_FAR_AWAY, +FAR_FAR_AWAY); - gluLookAt(0.0, -1000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRotated(object_rot_x, 1.0, 0.0, 0.0); - glRotated(object_rot_y, 0.0, 1.0, 0.0); - glRotated(object_rot_z, 0.0, 0.0, 1.0); - - glLineWidth(1); - glBegin(GL_LINES); - glColor3d(1.0, 0.0, 0.0); - glVertex3d(0, 0, 0); glVertex3d(10, 0, 0); - glColor3d(0.0, 1.0, 0.0); - glVertex3d(0, 0, 0); glVertex3d(0, 10, 0); - glColor3d(0.0, 0.0, 1.0); - glVertex3d(0, 0, 0); glVertex3d(0, 0, 10); - glEnd(); - - GLdouble mat_model[16]; - glGetDoublev(GL_MODELVIEW_MATRIX, mat_model); - - GLdouble mat_proj[16]; - glGetDoublev(GL_PROJECTION_MATRIX, mat_proj); - - GLint viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - - GLdouble xlabel_x, xlabel_y, xlabel_z; - gluProject(12, 0, 0, mat_model, mat_proj, viewport, &xlabel_x, &xlabel_y, &xlabel_z); - xlabel_x = round(xlabel_x); xlabel_y = round(xlabel_y); - - GLdouble ylabel_x, ylabel_y, ylabel_z; - gluProject(0, 12, 0, mat_model, mat_proj, viewport, &ylabel_x, &ylabel_y, &ylabel_z); - ylabel_x = round(ylabel_x); ylabel_y = round(ylabel_y); - - GLdouble zlabel_x, zlabel_y, zlabel_z; - gluProject(0, 0, 12, mat_model, mat_proj, viewport, &zlabel_x, &zlabel_y, &zlabel_z); - zlabel_x = round(zlabel_x); zlabel_y = round(zlabel_y); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glTranslated(-1, -1, 0); - glScaled(2.0/viewport[2], 2.0/viewport[3], 1); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // FIXME: Adjust color to keep contrast with background - glColor3d(0.0, 0.0, 0.0); - glBegin(GL_LINES); - // X Label - glVertex3d(xlabel_x-3, xlabel_y-3, 0); glVertex3d(xlabel_x+3, xlabel_y+3, 0); - glVertex3d(xlabel_x-3, xlabel_y+3, 0); glVertex3d(xlabel_x+3, xlabel_y-3, 0); - // Y Label - glVertex3d(ylabel_x-3, ylabel_y-3, 0); glVertex3d(ylabel_x+3, ylabel_y+3, 0); - glVertex3d(ylabel_x-3, ylabel_y+3, 0); glVertex3d(ylabel_x, ylabel_y, 0); - // Z Label - glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y-3, 0); - glVertex3d(zlabel_x-3, zlabel_y+3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); - glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); - glEnd(); - } - - if (statusLabel) { - QString msg; - msg.sprintf("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f", - -object_trans_x, -object_trans_y, -object_trans_z, - fmodf(360 - object_rot_x + 90, 360), fmodf(360 - object_rot_y, 360), fmodf(360 - object_rot_z, 360), viewer_distance); - statusLabel->setText(msg); - } -} - -void GLView::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Plus) { - viewer_distance *= 0.9; - updateGL(); - return; - } - if (event->key() == Qt::Key_Minus) { - viewer_distance /= 0.9; - updateGL(); - return; - } -} - -void GLView::wheelEvent(QWheelEvent *event) -{ - viewer_distance *= pow(0.9, event->delta() / 120.0); - updateGL(); -} - -void GLView::mousePressEvent(QMouseEvent *event) -{ - mouse_drag_active = true; - last_mouse_x = event->globalX(); - last_mouse_y = event->globalY(); - grabMouse(); - setFocus(); -} - -static void mat_id(double *trg) -{ - for (int i = 0; i < 16; i++) - trg[i] = i%5 == 0; -} - -static void mat_mul(double *trg, const double *m1, const double *m2) -{ - double m[16]; - for (int x = 0; x < 4; x++) - for (int y = 0; y < 4; y++) - { - m[x+y*4] = 0; - for (int i = 0; i < 4; i++) - m[x+y*4] += m1[i+y*4] * m2[x+i*4]; - } - for (int i = 0; i < 16; i++) - trg[i] = m[i]; -} - -static void mat_rot(double *trg, double angle, double x, double y, double z) -{ - double s = sin(M_PI*angle/180), c = cos(M_PI*angle/180); - double cc = 1 - c; - double m[16] = { - x*x*cc+c, x*y*cc-z*s, x*z*cc+y*s, 0, - y*x*cc+z*s, y*y*cc+c, y*z*cc-x*s, 0, - x*z*cc-y*s, y*z*cc+x*s, z*z*cc+c, 0, - 0, 0, 0, 1 - }; - for (int i = 0; i < 16; i++) - trg[i] = m[i]; -} - -void GLView::mouseMoveEvent(QMouseEvent *event) -{ - int this_mouse_x = event->globalX(); - int this_mouse_y = event->globalY(); - if (mouse_drag_active) { - if ((event->buttons() & Qt::LeftButton) != 0) { - object_rot_x += (this_mouse_y-last_mouse_y) * 0.7; - if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) - object_rot_y += (this_mouse_x-last_mouse_x) * 0.7; - else - object_rot_z += (this_mouse_x-last_mouse_x) * 0.7; - while (object_rot_x < 0) - object_rot_x += 360; - while (object_rot_x >= 360) - object_rot_x -= 360; - while (object_rot_y < 0) - object_rot_y += 360; - while (object_rot_y >= 360) - object_rot_y -= 360; - while (object_rot_z < 0) - object_rot_z += 360; - while (object_rot_z >= 360) - object_rot_z -= 360; - } else { - double mx = +(this_mouse_x-last_mouse_x) * viewer_distance/1000; - double my = -(this_mouse_y-last_mouse_y) * viewer_distance/1000; - double rx[16], ry[16], rz[16], tm[16]; - mat_rot(rx, -object_rot_x, 1.0, 0.0, 0.0); - mat_rot(ry, -object_rot_y, 0.0, 1.0, 0.0); - mat_rot(rz, -object_rot_z, 0.0, 0.0, 1.0); - mat_id(tm); - mat_mul(tm, rx, tm); - mat_mul(tm, ry, tm); - mat_mul(tm, rz, tm); - double vec[16] = { - 0, 0, 0, mx, - 0, 0, 0, 0, - 0, 0, 0, my, - 0, 0, 0, 1 - }; - if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) { - vec[3] = 0; - vec[7] = my; - vec[11] = 0; - } - mat_mul(tm, tm, vec); - object_trans_x += tm[3]; - object_trans_y += tm[7]; - object_trans_z += tm[11]; - } - updateGL(); - emit doAnimateUpdate(); - } - last_mouse_x = this_mouse_x; - last_mouse_y = this_mouse_y; -} - -void GLView::mouseReleaseEvent(QMouseEvent*) -{ - mouse_drag_active = false; - releaseMouse(); -} - diff --git a/highlighter.cc b/highlighter.cc deleted file mode 100644 index 5e15867..0000000 --- a/highlighter.cc +++ /dev/null @@ -1,46 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" - -Highlighter::Highlighter(QTextDocument *parent) - : QSyntaxHighlighter(parent) -{ -} - -void Highlighter::highlightBlock(const QString &text) -{ - int n = previousBlockState(); - if (n < 0) - n = 0; - int k = n + text.size() + 1; - setCurrentBlockState(k); - if (parser_error_pos >= n && parser_error_pos < k) { - QTextCharFormat style; - style.setBackground(Qt::red); - setFormat(0, text.size(), style); -#if 0 - style.setBackground(Qt::black); - style.setForeground(Qt::white); - setFormat(parser_error_pos - n, 1, style); -#endif - } -} - diff --git a/icon-alpha.png b/icon-alpha.png deleted file mode 100644 index 67b6e63..0000000 Binary files a/icon-alpha.png and /dev/null differ diff --git a/icon.png b/icon.png deleted file mode 100644 index 7912038..0000000 Binary files a/icon.png and /dev/null differ diff --git a/icons/OpenSCAD.icns b/icons/OpenSCAD.icns new file mode 100644 index 0000000..d6bbe9d Binary files /dev/null and b/icons/OpenSCAD.icns differ diff --git a/icons/icon-alpha.png b/icons/icon-alpha.png new file mode 100644 index 0000000..67b6e63 Binary files /dev/null and b/icons/icon-alpha.png differ diff --git a/icons/icon.png b/icons/icon.png new file mode 100644 index 0000000..7912038 Binary files /dev/null and b/icons/icon.png differ diff --git a/icons/mask.png b/icons/mask.png new file mode 100644 index 0000000..eea1027 Binary files /dev/null and b/icons/mask.png differ diff --git a/icons/openscad.ico b/icons/openscad.ico new file mode 100644 index 0000000..a210b49 Binary files /dev/null and b/icons/openscad.ico differ diff --git a/icons/prefs3DView.png b/icons/prefs3DView.png new file mode 100644 index 0000000..c6b3d56 Binary files /dev/null and b/icons/prefs3DView.png differ diff --git a/icons/prefsAdvanced.png b/icons/prefsAdvanced.png new file mode 100644 index 0000000..333dc2a Binary files /dev/null and b/icons/prefsAdvanced.png differ diff --git a/icons/prefsEditor.png b/icons/prefsEditor.png new file mode 100644 index 0000000..e65d9f7 Binary files /dev/null and b/icons/prefsEditor.png differ diff --git a/import.cc b/import.cc deleted file mode 100644 index c9b1a66..0000000 --- a/import.cc +++ /dev/null @@ -1,223 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include - -enum import_type_e { - TYPE_STL, - TYPE_OFF, - TYPE_DXF -}; - -class ImportModule : public AbstractModule -{ -public: - import_type_e type; - ImportModule(import_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class ImportNode : public AbstractPolyNode -{ -public: - import_type_e type; - QString filename; - QString layername; - int convexity; - double fn, fs, fa; - double origin_x, origin_y, scale; - ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - ImportNode *node = new ImportNode(inst, type); - - QVector argnames; - if (type == TYPE_DXF) { - argnames = QVector() << "file" << "layer" << "convexity" << "origin" << "scale"; - } else { - argnames = QVector() << "file" << "convexity"; - } - QVector argexpr; - - // Map old argnames to new argnames for compatibility - QVector inst_argnames = inst->argnames; - for (int i=0; iargvalues); - - node->fn = c.lookup_variable("$fn").num; - node->fs = c.lookup_variable("$fs").num; - node->fa = c.lookup_variable("$fa").num; - - node->filename = c.lookup_variable("file").text; - node->layername = c.lookup_variable("layer", true).text; - node->convexity = c.lookup_variable("convexity", true).num; - - if (node->convexity <= 0) - node->convexity = 1; - - Value origin = c.lookup_variable("origin", true); - node->origin_x = node->origin_y = 0; - origin.getv2(node->origin_x, node->origin_y); - - node->scale = c.lookup_variable("scale", true).num; - - if (node->scale <= 0) - node->scale = 1; - - return node; -} - -void register_builtin_import() -{ - builtin_modules["import_stl"] = new ImportModule(TYPE_STL); - builtin_modules["import_off"] = new ImportModule(TYPE_OFF); - builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF); -} - -PolySet *ImportNode::render_polyset(render_mode_e) const -{ - PolySet *p = new PolySet(); - p->convexity = convexity; - - if (type == TYPE_STL) - { - handle_dep(filename); - QFile f(filename); - if (!f.open(QIODevice::ReadOnly)) { - PRINTF("WARNING: Can't open import file `%s'.", filename.toAscii().data()); - return p; - } - - QByteArray data = f.read(5); - if (data.size() == 5 && QString(data) == QString("solid")) - { - int i = 0; - double vdata[3][3]; - QRegExp splitre = QRegExp("\\s*(vertex)?\\s+"); - f.readLine(); - while (!f.atEnd()) - { - QString line = QString(f.readLine()).remove("\n").remove("\r"); - if (line.contains("solid") || line.contains("facet") || line.contains("endloop")) - continue; - if (line.contains("outer loop")) { - i = 0; - continue; - } - if (line.contains("vertex")) { - QStringList tokens = line.split(splitre); - bool ok[3] = { false, false, false }; - if (tokens.size() == 4) { - vdata[i][0] = tokens[1].toDouble(&ok[0]); - vdata[i][1] = tokens[2].toDouble(&ok[1]); - vdata[i][2] = tokens[3].toDouble(&ok[2]); - } - if (!ok[0] || !ok[1] || !ok[2]) { - PRINTF("WARNING: Can't parse vertex line `%s'.", line.toAscii().data()); - i = 10; - } else if (++i == 3) { - p->append_poly(); - p->append_vertex(vdata[0][0], vdata[0][1], vdata[0][2]); - p->append_vertex(vdata[1][0], vdata[1][1], vdata[1][2]); - p->append_vertex(vdata[2][0], vdata[2][1], vdata[2][2]); - } - } - } - } - else - { - f.read(80-5+4); - while (1) { - struct { - float i, j, k; - float x1, y1, z1; - float x2, y2, z2; - float x3, y3, z3; - unsigned short acount; - } __attribute__ ((packed)) data; - if (f.read((char*)&data, sizeof(data)) != sizeof(data)) - break; - p->append_poly(); - p->append_vertex(data.x1, data.y1, data.z1); - p->append_vertex(data.x2, data.y2, data.z2); - p->append_vertex(data.x3, data.y3, data.z3); - } - } - } - - if (type == TYPE_OFF) - { - PRINTF("WARNING: OFF import is not implemented yet."); - } - - if (type == TYPE_DXF) - { - DxfData dd(fn, fs, fa, filename, layername, origin_x, origin_y, scale); - p->is2d = true; - dxf_tesselate(p, &dd, 0, true, false, 0); - dxf_border_to_ps(p, &dd); - } - - return p; -} - -QString ImportNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - if (type == TYPE_STL) - text.sprintf("import_stl(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); - if (type == TYPE_OFF) - text.sprintf("import_off(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); - if (type == TYPE_DXF) - text.sprintf("import_dxf(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "origin = [ %g %g ], scale = %g, convexity = %d, " - "$fn = %g, $fa = %g, $fs = %g);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, - layername.toAscii().data(), origin_x, origin_y, scale, convexity, - fn, fs, fa); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} - diff --git a/mainwin.cc b/mainwin.cc deleted file mode 100644 index 7abb4ed..0000000 --- a/mainwin.cc +++ /dev/null @@ -1,1800 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "MainWindow.h" -#include "Preferences.h" -#include "printutils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//for chdir -#include - -#ifdef ENABLE_CGAL - -#if 1 -#include "CGAL_renderer.h" -using OpenSCAD::OGL::Polyhedron; -using OpenSCAD::OGL::SNC_BOUNDARY; -using OpenSCAD::OGL::SNC_SKELETON; -using OpenSCAD::OGL::Nef3_Converter; -#else -// a little hackish: we need access to default-private members of -// CGAL::OGL::Nef3_Converter so we can implement our own draw function -// that does not scale the model. so we define 'class' to 'struct' -// for this header.. -// -// theoretically there could be two problems: -// 1.) defining language keyword with the pre processor is illegal afair -// 2.) the compiler could use a different memory layout or name mangling for structs -// -// both does not seam to be the case with todays compilers... -// -#define class struct -#include -#undef class -using CGAL::OGL::Polyhedron; -using CGAL::OGL::SNC_BOUNDARY; -using CGAL::OGL::SNC_SKELETON; -using CGAL::OGL::Nef3_Converter; -#endif -#endif // ENABLE_CGAL - -#define QUOTE(x__) # x__ -#define QUOTED(x__) QUOTE(x__) - -static char helptitle[] = - "OpenSCAD " - QUOTED(OPENSCAD_VERSION) - " (www.openscad.org)\n"; -static char copyrighttext[] = - "Copyright (C) 2009 Clifford Wolf \n" - "\n" - "This program is free software; you can redistribute it and/or modify" - "it under the terms of the GNU General Public License as published by" - "the Free Software Foundation; either version 2 of the License, or" - "(at your option) any later version."; - -QPointer MainWindow::current_win = NULL; - -MainWindow::MainWindow(const char *filename) -{ - setupUi(this); - - root_ctx.functions_p = &builtin_functions; - root_ctx.modules_p = &builtin_modules; - root_ctx.set_variable("$fn", Value(0.0)); - root_ctx.set_variable("$fs", Value(1.0)); - root_ctx.set_variable("$fa", Value(12.0)); - root_ctx.set_variable("$t", Value(0.0)); - - Value zero3; - zero3.type = Value::VECTOR; - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - root_ctx.set_variable("$vpt", zero3); - root_ctx.set_variable("$vpr", zero3); - - root_module = NULL; - absolute_root_node = NULL; - root_raw_term = NULL; - root_norm_term = NULL; - root_chain = NULL; -#ifdef ENABLE_CGAL - this->root_N = NULL; - this->recreate_cgal_ogl_p = false; - cgal_ogl_p = NULL; - cgal_ogl_ps = NULL; -#endif - - highlights_chain = NULL; - background_chain = NULL; - root_node = NULL; - enableOpenCSG = false; - - tval = 0; - fps = 0; - fsteps = 1; - - highlighter = NULL; - - editor->setWordWrapMode(QTextOption::WrapAnywhere); // Not designable - setFont("", 0); // Init default font - - screen->statusLabel = new QLabel(this); - statusBar()->addWidget(screen->statusLabel); - - animate_timer = new QTimer(this); - connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); - - connect(e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile())); - connect(e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps())); - - animate_panel->hide(); - - // File menu - connect(this->fileActionNew, SIGNAL(triggered()), this, SLOT(actionNew())); - connect(this->fileActionOpen, SIGNAL(triggered()), this, SLOT(actionOpen())); - connect(this->fileActionSave, SIGNAL(triggered()), this, SLOT(actionSave())); - connect(this->fileActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); - connect(this->fileActionReload, SIGNAL(triggered()), this, SLOT(actionReload())); - connect(this->fileActionQuit, SIGNAL(triggered()), this, SLOT(quit())); -#ifndef __APPLE__ - this->fileActionSave->setShortcut(QKeySequence(Qt::Key_F2)); - this->fileActionReload->setShortcut(QKeySequence(Qt::Key_F3)); -#endif - // Open Recent - for (int i = 0;iactionRecentFile[i] = new QAction(this); - this->actionRecentFile[i]->setVisible(false); - this->menuOpenRecent->addAction(this->actionRecentFile[i]); - connect(this->actionRecentFile[i], SIGNAL(triggered()), - this, SLOT(actionOpenRecent())); - } - this->menuOpenRecent->addSeparator(); - this->menuOpenRecent->addAction(this->fileActionClearRecent); - connect(this->fileActionClearRecent, SIGNAL(triggered()), - this, SLOT(clearRecentFiles())); - - QDir examplesdir(QApplication::instance()->applicationDirPath()); -#ifdef Q_WS_MAC - examplesdir.cd("../Resources"); // Examples can be bundled - if (!examplesdir.exists("examples")) examplesdir.cd("../../.."); -#endif - if (examplesdir.cd("examples")) { - this->examplesdir = examplesdir.path(); - - QStringList examples = examplesdir.entryList(QStringList("*.scad"), - QDir::Files | QDir::Readable, QDir::Name); - foreach (const QString &ex, examples) { - this->menuExamples->addAction(ex, this, SLOT(actionOpenExample())); - } - } - - // Edit menu - connect(this->editActionUndo, SIGNAL(triggered()), editor, SLOT(undo())); - connect(this->editActionRedo, SIGNAL(triggered()), editor, SLOT(redo())); - connect(this->editActionCut, SIGNAL(triggered()), editor, SLOT(cut())); - connect(this->editActionCopy, SIGNAL(triggered()), editor, SLOT(copy())); - connect(this->editActionPaste, SIGNAL(triggered()), editor, SLOT(paste())); - connect(this->editActionIndent, SIGNAL(triggered()), this, SLOT(editIndent())); - connect(this->editActionUnindent, SIGNAL(triggered()), this, SLOT(editUnindent())); - connect(this->editActionComment, SIGNAL(triggered()), this, SLOT(editComment())); - connect(this->editActionUncomment, SIGNAL(triggered()), this, SLOT(editUncomment())); - connect(this->editActionPasteVPT, SIGNAL(triggered()), this, SLOT(pasteViewportTranslation())); - connect(this->editActionPasteVPR, SIGNAL(triggered()), this, SLOT(pasteViewportRotation())); - connect(this->editActionZoomIn, SIGNAL(triggered()), editor, SLOT(zoomIn())); - connect(this->editActionZoomOut, SIGNAL(triggered()), editor, SLOT(zoomOut())); - connect(this->editActionHide, SIGNAL(triggered()), this, SLOT(hideEditor())); - connect(this->editActionPreferences, SIGNAL(triggered()), this, SLOT(preferences())); - - // Design menu - connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadCompile())); - connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionCompile())); -#ifdef ENABLE_CGAL - connect(this->designActionCompileAndRender, SIGNAL(triggered()), this, SLOT(actionRenderCGAL())); -#else - this->designActionCompileAndRender->setVisible(false); -#endif - connect(this->designActionDisplayAST, SIGNAL(triggered()), this, SLOT(actionDisplayAST())); - connect(this->designActionDisplayCSGTree, SIGNAL(triggered()), this, SLOT(actionDisplayCSGTree())); - connect(this->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts())); - connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL())); - connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF())); - connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF())); - connect(this->designActionFlushCaches, SIGNAL(triggered()), this, SLOT(actionFlushCaches())); - - // View menu -#ifndef ENABLE_OPENCSG - this->viewActionOpenCSG->setVisible(false); -#else - connect(this->viewActionOpenCSG, SIGNAL(triggered()), this, SLOT(viewModeOpenCSG())); - if (!screen->hasOpenCSGSupport()) { - this->viewActionOpenCSG->setEnabled(false); - } -#endif - -#ifdef ENABLE_CGAL - connect(this->viewActionCGALSurfaces, SIGNAL(triggered()), this, SLOT(viewModeCGALSurface())); - connect(this->viewActionCGALGrid, SIGNAL(triggered()), this, SLOT(viewModeCGALGrid())); -#else - this->viewActionCGALSurfaces->setVisible(false); - this->viewActionCGALGrid->setVisible(false); -#endif - connect(this->viewActionThrownTogether, SIGNAL(triggered()), this, SLOT(viewModeThrownTogether())); - connect(this->viewActionShowEdges, SIGNAL(triggered()), this, SLOT(viewModeShowEdges())); - connect(this->viewActionShowAxes, SIGNAL(triggered()), this, SLOT(viewModeShowAxes())); - connect(this->viewActionShowCrosshairs, SIGNAL(triggered()), this, SLOT(viewModeShowCrosshairs())); - connect(this->viewActionAnimate, SIGNAL(triggered()), this, SLOT(viewModeAnimate())); - connect(this->viewActionTop, SIGNAL(triggered()), this, SLOT(viewAngleTop())); - connect(this->viewActionBottom, SIGNAL(triggered()), this, SLOT(viewAngleBottom())); - connect(this->viewActionLeft, SIGNAL(triggered()), this, SLOT(viewAngleLeft())); - connect(this->viewActionRight, SIGNAL(triggered()), this, SLOT(viewAngleRight())); - connect(this->viewActionFront, SIGNAL(triggered()), this, SLOT(viewAngleFront())); - connect(this->viewActionBack, SIGNAL(triggered()), this, SLOT(viewAngleBack())); - connect(this->viewActionDiagonal, SIGNAL(triggered()), this, SLOT(viewAngleDiagonal())); - connect(this->viewActionCenter, SIGNAL(triggered()), this, SLOT(viewCenter())); - connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective())); - connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal())); - connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole())); - -// #ifdef ENABLE_CGAL -// viewActionCGALSurface = menu->addAction("CGAL Surfaces", this, SLOT(viewModeCGALSurface()), QKeySequence(Qt::Key_F10)); -// viewActionCGALGrid = menu->addAction("CGAL Grid Only", this, SLOT(viewModeCGALGrid()), QKeySequence(Qt::Key_F11)); -// #endif - - // Help menu - connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout())); - connect(this->helpActionManual, SIGNAL(triggered()), this, SLOT(helpManual())); - - - console->setReadOnly(true); - current_win = this; - - PRINT(helptitle); - PRINT(copyrighttext); - PRINT(""); - - if (filename) { - openFile(filename); - } else { - setFileName(""); - } - - connect(editor->document(), SIGNAL(contentsChanged()), this, SLOT(animateUpdateDocChanged())); - connect(editor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool))); - connect(editor->document(), SIGNAL(modificationChanged(bool)), fileActionSave, SLOT(setEnabled(bool))); - connect(screen, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate())); - - connect(Preferences::inst(), SIGNAL(requestRedraw()), this->screen, SLOT(updateGL())); - connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)), - this, SLOT(setFont(const QString&,uint))); - Preferences::inst()->apply(); - - - // display this window and check for OpenGL 2.0 (OpenCSG) support - viewModeThrownTogether(); - show(); - - // make sure it looks nice.. - resize(800, 600); - splitter1->setSizes(QList() << 400 << 400); - splitter2->setSizes(QList() << 400 << 200); - -#ifdef ENABLE_OPENCSG - viewModeOpenCSG(); -#else - viewModeThrownTogether(); -#endif - viewPerspective(); - - setAcceptDrops(true); - current_win = NULL; -} - -MainWindow::~MainWindow() -{ - if (root_module) - delete root_module; - if (root_node) - delete root_node; -#ifdef ENABLE_CGAL - if (this->root_N) - delete this->root_N; - if (cgal_ogl_p) { - Polyhedron *p = (Polyhedron*)cgal_ogl_p; - delete p; - } - if (cgal_ogl_ps) - cgal_ogl_ps->unlink(); -#endif -} - -/*! - Requests to open a file from an external event, e.g. by double-clicking a filename. - */ -#ifdef ENABLE_MDI -void MainWindow::requestOpenFile(const QString &filename) -{ - new MainWindow(filename.toUtf8()); -} -#else -void MainWindow::requestOpenFile(const QString &) -{ -} -#endif - -void -MainWindow::openFile(const QString &new_filename) -{ -#ifdef ENABLE_MDI - if (!editor->toPlainText().isEmpty()) { - new MainWindow(new_filename.toUtf8()); - current_win = NULL; - return; - } -#endif - setFileName(new_filename); - - load(); -} - -void -MainWindow::setFileName(const QString &filename) -{ - if (filename.isEmpty()) { - this->fileName.clear(); - setWindowTitle("OpenSCAD - New Document[*]"); - } - else { - QFileInfo fileinfo(filename); - QString infoFileName = fileinfo.canonicalFilePath(); - setWindowTitle("OpenSCAD - " + fileinfo.fileName() + "[*]"); - - // Check that the canonical file path exists - only update recent files - // if it does. Should prevent empty list items on initial open etc. - if (!infoFileName.isEmpty()) { - this->fileName = infoFileName; - QSettings settings; // already set up properly via main.cpp - QStringList files = settings.value("recentFileList").toStringList(); - files.removeAll(this->fileName); - files.prepend(this->fileName); - while (files.size() > maxRecentFiles) - files.removeLast(); - settings.setValue("recentFileList", files); - } else { - this->fileName = fileinfo.fileName(); - } - - QDir::setCurrent(fileinfo.dir().absolutePath()); - } - - foreach(QWidget *widget, QApplication::topLevelWidgets()) { - MainWindow *mainWin = qobject_cast(widget); - if (mainWin) { - mainWin->updateRecentFileActions(); - } - } -} - -void MainWindow::updatedFps() -{ - bool fps_ok; - double fps = e_fps->text().toDouble(&fps_ok); - animate_timer->stop(); - if (fps_ok && fps > 0) { - animate_timer->setSingleShot(false); - animate_timer->setInterval(int(1000 / e_fps->text().toDouble())); - animate_timer->start(); - } -} - -void MainWindow::updateTVal() -{ - bool fps_ok; - double fps = e_fps->text().toDouble(&fps_ok); - if (fps_ok) { - if (fps <= 0) { - actionCompile(); - } else { - double s = e_fsteps->text().toDouble(); - double t = e_tval->text().toDouble() + 1/s; - QString txt; - txt.sprintf("%.5f", t >= 1.0 ? 0.0 : t); - e_tval->setText(txt); - } - } -} - -void MainWindow::load() -{ - current_win = this; - if (!this->fileName.isEmpty()) { - QFile file(this->fileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTA("Failed to open file: %1 (%2)", this->fileName, file.errorString()); - } - else { - QString text = QTextStream(&file).readAll(); - PRINTA("Loaded design `%1'.", this->fileName); - editor->setPlainText(text); - } - } - current_win = this; -} - -AbstractNode *MainWindow::find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - -void MainWindow::compile(bool procevents) -{ - PRINT("Parsing design (AST generation)..."); - if (procevents) - QApplication::processEvents(); - - - // Remove previous CSG tree - if (root_module) { - delete root_module; - root_module = NULL; - } - - if (absolute_root_node) { - delete absolute_root_node; - absolute_root_node = NULL; - } - - if (root_raw_term) { - root_raw_term->unlink(); - root_raw_term = NULL; - } - - if (root_norm_term) { - root_norm_term->unlink(); - root_norm_term = NULL; - } - - if (root_chain) { - delete root_chain; - root_chain = NULL; - } - - foreach(CSGTerm *v, highlight_terms) { - v->unlink(); - } - highlight_terms.clear(); - if (highlights_chain) { - delete highlights_chain; - highlights_chain = NULL; - } - foreach(CSGTerm *v, background_terms) { - v->unlink(); - } - background_terms.clear(); - if (background_chain) { - delete background_chain; - background_chain = NULL; - } - root_node = NULL; - enableOpenCSG = false; - - // Initialize special variables - root_ctx.set_variable("$t", Value(e_tval->text().toDouble())); - - Value vpt; - vpt.type = Value::VECTOR; - vpt.vec.append(new Value(-screen->object_trans_x)); - vpt.vec.append(new Value(-screen->object_trans_y)); - vpt.vec.append(new Value(-screen->object_trans_z)); - root_ctx.set_variable("$vpt", vpt); - - Value vpr; - vpr.type = Value::VECTOR; - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_x + 90, 360))); - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_y, 360))); - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_z, 360))); - root_ctx.set_variable("$vpr", vpr); - - // Parse - last_compiled_doc = editor->toPlainText(); - root_module = parse((last_compiled_doc + "\n" + commandline_commands).toAscii().data(), false); - - // Error highlighting - if (highlighter) { - delete highlighter; - highlighter = NULL; - } - if (parser_error_pos >= 0) { - highlighter = new Highlighter(editor->document()); - } - - if (!root_module) { - if (!animate_panel->isVisible()) { - QTextCursor cursor = editor->textCursor(); - cursor.setPosition(parser_error_pos); - editor->setTextCursor(cursor); - } - goto fail; - } - - // Evaluate CSG tree - PRINT("Compiling design (CSG Tree generation)..."); - if (procevents) - QApplication::processEvents(); - - AbstractNode::idx_counter = 1; - root_inst = ModuleInstantiation(); - absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); - - if (!absolute_root_node) - goto fail; - - // Do we have an explicit root node (! modifier)? - if (!(this->root_node = find_root_tag(absolute_root_node))) { - this->root_node = absolute_root_node; - } - root_node->dump(""); - - PRINT("Compiling design (CSG Products generation)..."); - if (procevents) - QApplication::processEvents(); - - double m[20]; - - for (int i = 0; i < 16; i++) - m[i] = i % 5 == 0 ? 1.0 : 0.0; - for (int i = 16; i < 20; i++) - m[i] = -1; - - // Main CSG evaluation - root_raw_term = root_node->render_csg_term(m, &highlight_terms, &background_terms); - - if (!root_raw_term) - goto fail; - - PRINT("Compiling design (CSG Products normalization)..."); - if (procevents) - QApplication::processEvents(); - - root_norm_term = root_raw_term->link(); - - // CSG normalization - while (1) { - CSGTerm *n = root_norm_term->normalize(); - root_norm_term->unlink(); - if (root_norm_term == n) - break; - root_norm_term = n; - } - - if (!root_norm_term) - goto fail; - - root_chain = new CSGChain(); - root_chain->import(root_norm_term); - - if (root_chain->polysets.size() > 1000) { - PRINTF("WARNING: Normalized tree has %d elements!", root_chain->polysets.size()); - PRINTF("WARNING: OpenCSG rendering has been disabled."); - } else { - enableOpenCSG = true; - } - - if (highlight_terms.size() > 0) - { - PRINTF("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); - if (procevents) - QApplication::processEvents(); - - highlights_chain = new CSGChain(); - for (int i = 0; i < highlight_terms.size(); i++) { - while (1) { - CSGTerm *n = highlight_terms[i]->normalize(); - highlight_terms[i]->unlink(); - if (highlight_terms[i] == n) - break; - highlight_terms[i] = n; - } - highlights_chain->import(highlight_terms[i]); - } - } - - if (background_terms.size() > 0) - { - PRINTF("Compiling background (%d CSG Trees)...", background_terms.size()); - if (procevents) - QApplication::processEvents(); - - background_chain = new CSGChain(); - for (int i = 0; i < background_terms.size(); i++) { - while (1) { - CSGTerm *n = background_terms[i]->normalize(); - background_terms[i]->unlink(); - if (background_terms[i] == n) - break; - background_terms[i] = n; - } - background_chain->import(background_terms[i]); - } - } - - if (1) { - PRINT("Compilation finished."); - if (procevents) - QApplication::processEvents(); - } else { -fail: - if (parser_error_pos < 0) { - PRINT("ERROR: Compilation failed! (no top level object found)"); - } else { - int line = 1; - QByteArray pb = last_compiled_doc.toAscii(); - char *p = pb.data(); - for (int i = 0; i < parser_error_pos; i++) { - if (p[i] == '\n') - line++; - if (p[i] == 0) { - line = -1; - break; - } - } - PRINTF("ERROR: Compilation failed! (parser error in line %d)", line); - } - if (procevents) - QApplication::processEvents(); - } -} - -void MainWindow::actionNew() -{ -#ifdef ENABLE_MDI - new MainWindow; -#else - setFileName(""); - editor->setPlainText(""); -#endif -} - -void MainWindow::actionOpen() -{ - current_win = this; - QString new_filename = QFileDialog::getOpenFileName(this, "Open File", "", "OpenSCAD Designs (*.scad)"); - if (!new_filename.isEmpty()) openFile(new_filename); - current_win = NULL; -} - -void MainWindow::actionOpenRecent() -{ - QAction *action = qobject_cast(sender()); - if (action) { - openFile(action->data().toString()); - } -} - -void MainWindow::clearRecentFiles() -{ - QSettings settings; // already set up properly via main.cpp - QStringList files; - settings.setValue("recentFileList", files); - - updateRecentFileActions(); -} - -void MainWindow::updateRecentFileActions() -{ - QSettings settings; // set up project and program properly in main.cpp - QStringList files = settings.value("recentFileList").toStringList(); - - int originalNumRecentFiles = files.size(); - - // Remove any duplicate or empty entries from the list -#if (QT_VERSION >= QT_VERSION_CHECK(4, 5, 0)) - files.removeDuplicates(); -#endif - files.removeAll(QString()); - // Now remove any entries which do not exist - for(int i = files.size()-1; i >= 0; --i) { - QFileInfo fileInfo(files[i]); - if (!QFile(fileInfo.absoluteFilePath()).exists()) - files.removeAt(i); - } - - int numRecentFiles = qMin(files.size(), - static_cast(maxRecentFiles)); - - for (int i = 0; i < numRecentFiles; ++i) { - this->actionRecentFile[i]->setText(QFileInfo(files[i]).fileName()); - this->actionRecentFile[i]->setData(files[i]); - this->actionRecentFile[i]->setVisible(true); - } - for (int j = numRecentFiles; j < maxRecentFiles; ++j) - this->actionRecentFile[j]->setVisible(false); - - // If we had to prune the list, then save the cleaned list - if (originalNumRecentFiles != numRecentFiles) - settings.setValue("recentFileList", files); -} - -void MainWindow::actionOpenExample() -{ - QAction *action = qobject_cast(sender()); - if (action) { - openFile(this->examplesdir + QDir::separator() + action->text()); - } -} - -void MainWindow::actionSave() -{ - if (this->fileName.isEmpty()) { - actionSaveAs(); - } - else { - current_win = this; - QFile file(this->fileName); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { - PRINTA("Failed to open file for writing: %1 (%2)", this->fileName, file.errorString()); - } - else { - QTextStream(&file) << this->editor->toPlainText(); - PRINTA("Saved design `%1'.", this->fileName); - this->editor->document()->setModified(false); - } - current_win = NULL; - } -} - -void MainWindow::actionSaveAs() -{ - QString new_filename = QFileDialog::getSaveFileName(this, "Save File", - this->fileName.isEmpty()?"Untitled.scad":this->fileName, - "OpenSCAD Designs (*.scad)"); - if (!new_filename.isEmpty()) { - if (QFileInfo(new_filename).suffix().isEmpty()) { - new_filename.append(".scad"); - - // Manual overwrite check since Qt doesn't do it, when using the - // defaultSuffix property - QFileInfo info(new_filename); - if (info.exists()) { - if (QMessageBox::warning(this, windowTitle(), - tr("%1 already exists.\nDo you want to replace it?").arg(info.fileName()), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - } - setFileName(new_filename); - actionSave(); - } -} - -void MainWindow::actionReload() -{ - load(); -} - -void MainWindow::editIndent() -{ - QTextCursor cursor = editor->textCursor(); - int p1 = cursor.selectionStart(); - QString txt = cursor.selectedText(); - - txt.replace(QString(QChar(8233)), QString(QChar(8233)) + QString("\t")); - if (txt.endsWith(QString(QChar(8233)) + QString("\t"))) - txt.chop(1); - txt = QString("\t") + txt; - - cursor.insertText(txt); - int p2 = cursor.position(); - cursor.setPosition(p1, QTextCursor::MoveAnchor); - cursor.setPosition(p2, QTextCursor::KeepAnchor); - editor->setTextCursor(cursor); -} - -void MainWindow::editUnindent() -{ - QTextCursor cursor = editor->textCursor(); - int p1 = cursor.selectionStart(); - QString txt = cursor.selectedText(); - - txt.replace(QString(QChar(8233)) + QString("\t"), QString(QChar(8233))); - if (txt.startsWith(QString("\t"))) - txt.remove(0, 1); - - cursor.insertText(txt); - int p2 = cursor.position(); - cursor.setPosition(p1, QTextCursor::MoveAnchor); - cursor.setPosition(p2, QTextCursor::KeepAnchor); - editor->setTextCursor(cursor); -} - -void MainWindow::editComment() -{ - QTextCursor cursor = editor->textCursor(); - int p1 = cursor.selectionStart(); - QString txt = cursor.selectedText(); - - txt.replace(QString(QChar(8233)), QString(QChar(8233)) + QString("//")); - if (txt.endsWith(QString(QChar(8233)) + QString("//"))) - txt.chop(2); - txt = QString("//") + txt; - - cursor.insertText(txt); - int p2 = cursor.position(); - cursor.setPosition(p1, QTextCursor::MoveAnchor); - cursor.setPosition(p2, QTextCursor::KeepAnchor); - editor->setTextCursor(cursor); -} - -void MainWindow::editUncomment() -{ - QTextCursor cursor = editor->textCursor(); - int p1 = cursor.selectionStart(); - QString txt = cursor.selectedText(); - - txt.replace(QString(QChar(8233)) + QString("//"), QString(QChar(8233))); - if (txt.startsWith(QString("//"))) - txt.remove(0, 2); - - cursor.insertText(txt); - int p2 = cursor.position(); - cursor.setPosition(p1, QTextCursor::MoveAnchor); - cursor.setPosition(p2, QTextCursor::KeepAnchor); - editor->setTextCursor(cursor); -} - -void MainWindow::hideEditor() -{ - if (editActionHide->isChecked()) { - editor->hide(); - } else { - editor->show(); - } -} - -void MainWindow::pasteViewportTranslation() -{ - QTextCursor cursor = editor->textCursor(); - QString txt; - txt.sprintf("[ %.2f, %.2f, %.2f ]", -screen->object_trans_x, -screen->object_trans_y, -screen->object_trans_z); - cursor.insertText(txt); -} - -void MainWindow::pasteViewportRotation() -{ - QTextCursor cursor = editor->textCursor(); - QString txt; - txt.sprintf("[ %.2f, %.2f, %.2f ]", - fmodf(360 - screen->object_rot_x + 90, 360), fmodf(360 - screen->object_rot_y, 360), fmodf(360 - screen->object_rot_z, 360)); - cursor.insertText(txt); -} - -void MainWindow::actionReloadCompile() -{ - console->clear(); - - load(); - - current_win = this; - compile(true); - -#ifdef ENABLE_OPENCSG - if (!(viewActionOpenCSG->isVisible() && viewActionOpenCSG->isChecked()) && - !viewActionThrownTogether->isChecked()) { - viewModeOpenCSG(); - } - else -#endif - { - screen->updateGL(); - } - current_win = NULL; -} - -void MainWindow::actionCompile() -{ - current_win = this; - console->clear(); - - compile(!viewActionAnimate->isChecked()); - - // Go to non-CGAL view mode - if (!viewActionOpenCSG->isChecked() && !viewActionThrownTogether->isChecked()) { - viewModeOpenCSG(); - } - else { - screen->updateGL(); - } - - if (viewActionAnimate->isChecked() && e_dump->isChecked()) { - QImage img = screen->grabFrameBuffer(); - QString filename; - double s = e_fsteps->text().toDouble(); - double t = e_tval->text().toDouble(); - filename.sprintf("frame%05d.png", int(round(s*t))); - img.save(filename, "PNG"); - } - - current_win = NULL; -} - -#ifdef ENABLE_CGAL - -static void report_func(const class AbstractNode*, void *vp, int mark) -{ - QProgressDialog *pd = (QProgressDialog*)vp; - int v = (int)((mark*100.0) / progress_report_count); - pd->setValue(v < 100 ? v : 99); - QString label; - label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); - pd->setLabelText(label); - QApplication::processEvents(); -} - -void MainWindow::actionRenderCGAL() -{ - current_win = this; - console->clear(); - - compile(true); - - if (!root_module || !root_node) - return; - - if (this->root_N) { - delete this->root_N; - this->root_N = NULL; - this->recreate_cgal_ogl_p = true; - } - - PRINT("Rendering Polygon Mesh using CGAL..."); - QApplication::processEvents(); - - QTime t; - t.start(); - - QProgressDialog *pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); - pd->setValue(0); - pd->setAutoClose(false); - pd->show(); - QApplication::processEvents(); - - progress_report_prep(root_node, report_func, pd); - this->root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); - progress_report_fin(); - - PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost()); - PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size()); - QApplication::processEvents(); - - if (this->root_N->dim == 2) { - PRINTF(" Top level object is a 2D object:"); - QApplication::processEvents(); - PRINTF(" Empty: %6s", this->root_N->p2.is_empty() ? "yes" : "no"); - QApplication::processEvents(); - PRINTF(" Plane: %6s", this->root_N->p2.is_plane() ? "yes" : "no"); - QApplication::processEvents(); - PRINTF(" Vertices: %6d", (int)this->root_N->p2.explorer().number_of_vertices()); - QApplication::processEvents(); - PRINTF(" Halfedges: %6d", (int)this->root_N->p2.explorer().number_of_halfedges()); - QApplication::processEvents(); - PRINTF(" Edges: %6d", (int)this->root_N->p2.explorer().number_of_edges()); - QApplication::processEvents(); - PRINTF(" Faces: %6d", (int)this->root_N->p2.explorer().number_of_faces()); - QApplication::processEvents(); - PRINTF(" FaceCycles: %6d", (int)this->root_N->p2.explorer().number_of_face_cycles()); - QApplication::processEvents(); - PRINTF(" ConnComp: %6d", (int)this->root_N->p2.explorer().number_of_connected_components()); - QApplication::processEvents(); - } - - if (this->root_N->dim == 3) { - PRINTF(" Top level object is a 3D object:"); - PRINTF(" Simple: %6s", this->root_N->p3.is_simple() ? "yes" : "no"); - QApplication::processEvents(); - PRINTF(" Valid: %6s", this->root_N->p3.is_valid() ? "yes" : "no"); - QApplication::processEvents(); - PRINTF(" Vertices: %6d", (int)this->root_N->p3.number_of_vertices()); - QApplication::processEvents(); - PRINTF(" Halfedges: %6d", (int)this->root_N->p3.number_of_halfedges()); - QApplication::processEvents(); - PRINTF(" Edges: %6d", (int)this->root_N->p3.number_of_edges()); - QApplication::processEvents(); - PRINTF(" Halffacets: %6d", (int)this->root_N->p3.number_of_halffacets()); - QApplication::processEvents(); - PRINTF(" Facets: %6d", (int)this->root_N->p3.number_of_facets()); - QApplication::processEvents(); - PRINTF(" Volumes: %6d", (int)this->root_N->p3.number_of_volumes()); - QApplication::processEvents(); - } - - int s = t.elapsed() / 1000; - PRINTF("Total rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - - if (!viewActionCGALSurfaces->isChecked() && !viewActionCGALGrid->isChecked()) { - viewModeCGALSurface(); - } else { - screen->updateGL(); - } - - PRINT("Rendering finished."); - - delete pd; - current_win = NULL; -} - -#endif /* ENABLE_CGAL */ - -void MainWindow::actionDisplayAST() -{ - current_win = this; - QTextEdit *e = new QTextEdit(this); - e->setWindowFlags(Qt::Window); - e->setTabStopWidth(30); - e->setWindowTitle("AST Dump"); - e->setReadOnly(true); - if (root_module) { - e->setPlainText(root_module->dump("", "")); - } else { - e->setPlainText("No AST to dump. Please try compiling first..."); - } - e->show(); - e->resize(600, 400); - current_win = NULL; -} - -void MainWindow::actionDisplayCSGTree() -{ - current_win = this; - QTextEdit *e = new QTextEdit(this); - e->setWindowFlags(Qt::Window); - e->setTabStopWidth(30); - e->setWindowTitle("CSG Tree Dump"); - e->setReadOnly(true); - if (root_node) { - e->setPlainText(root_node->dump("")); - } else { - e->setPlainText("No CSG to dump. Please try compiling first..."); - } - e->show(); - e->resize(600, 400); - current_win = NULL; -} - -void MainWindow::actionDisplayCSGProducts() -{ - current_win = this; - QTextEdit *e = new QTextEdit(this); - e->setWindowFlags(Qt::Window); - e->setTabStopWidth(30); - e->setWindowTitle("CSG Products Dump"); - e->setReadOnly(true); - e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? root_raw_term->dump() : "N/A", root_norm_term ? root_norm_term->dump() : "N/A", root_chain ? root_chain->dump() : "N/A", highlights_chain ? highlights_chain->dump() : "N/A", background_chain ? background_chain->dump() : "N/A")); - e->show(); - e->resize(600, 400); - current_win = NULL; -} - -#ifdef ENABLE_CGAL -void MainWindow::actionExportSTLorOFF(bool stl_mode) -#else -void MainWindow::actionExportSTLorOFF(bool) -#endif -{ -#ifdef ENABLE_CGAL - current_win = this; - - if (!this->root_N) { - PRINT("Nothing to export! Try building first (press F6)."); - current_win = NULL; - return; - } - - if (this->root_N->dim != 3) { - PRINT("Current top level object is not a 3D object."); - current_win = NULL; - return; - } - - if (!this->root_N->p3.is_simple()) { - PRINT("Object isn't a valid 2-manifold! Modify your design.."); - current_win = NULL; - return; - } - - QString stl_filename = QFileDialog::getSaveFileName(this, - stl_mode ? "Export STL File" : "Export OFF File", "", - stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)"); - if (stl_filename.isEmpty()) { - PRINTF("No filename specified. %s export aborted.", stl_mode ? "STL" : "OFF"); - current_win = NULL; - return; - } - - QProgressDialog *pd = new QProgressDialog( - stl_mode ? "Exporting object to STL file..." : "Exporting object to OFF file...", - QString(), 0, this->root_N->p3.number_of_facets() + 1); - pd->setValue(0); - pd->setAutoClose(false); - pd->show(); - QApplication::processEvents(); - - if (stl_mode) - export_stl(this->root_N, stl_filename, pd); - else - export_off(this->root_N, stl_filename, pd); - - PRINTF("%s export finished.", stl_mode ? "STL" : "OFF"); - - delete pd; - - current_win = NULL; -#endif /* ENABLE_CGAL */ -} - -void MainWindow::actionExportSTL() -{ - actionExportSTLorOFF(true); -} - -void MainWindow::actionExportOFF() -{ - actionExportSTLorOFF(false); -} - -void MainWindow::actionExportDXF() -{ -#ifdef ENABLE_CGAL - current_win = this; - - if (!this->root_N) { - PRINT("Nothing to export! Try building first (press F6)."); - current_win = NULL; - return; - } - - if (this->root_N->dim != 2) { - PRINT("Current top level object is not a 2D object."); - current_win = NULL; - return; - } - - QString stl_filename = QFileDialog::getSaveFileName(this, - "Export DXF File", "", "DXF Files (*.dxf)"); - if (stl_filename.isEmpty()) { - PRINTF("No filename specified. DXF export aborted."); - current_win = NULL; - return; - } - - export_dxf(this->root_N, stl_filename, NULL); - PRINTF("DXF export finished."); - - current_win = NULL; -#endif /* ENABLE_CGAL */ -} - -void MainWindow::actionFlushCaches() -{ - PolySet::ps_cache.clear(); - AbstractNode::cgal_nef_cache.clear(); - dxf_dim_cache.clear(); - dxf_cross_cache.clear(); -} - -void MainWindow::viewModeActionsUncheck() -{ - viewActionOpenCSG->setChecked(false); -#ifdef ENABLE_CGAL - viewActionCGALSurfaces->setChecked(false); - viewActionCGALGrid->setChecked(false); -#endif - viewActionThrownTogether->setChecked(false); -} - -#ifdef ENABLE_OPENCSG - -class OpenCSGPrim : public OpenCSG::Primitive -{ -public: - OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) : - OpenCSG::Primitive(operation, convexity) { } - PolySet *p; - double *m; - int csgmode; - virtual void render() { - glPushMatrix(); - glMultMatrixd(m); - p->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); - glPopMatrix(); - } -}; - -static void renderCSGChainviaOpenCSG(CSGChain *chain, GLint *shaderinfo, bool highlight, bool background) -{ - std::vector primitives; - int j = 0; - for (int i = 0;; i++) - { - bool last = i == chain->polysets.size(); - - if (last || chain->types[i] == CSGTerm::TYPE_UNION) - { - if (j+1 != i) { - OpenCSG::render(primitives); - glDepthFunc(GL_EQUAL); - } - if (shaderinfo) - glUseProgram(shaderinfo[0]); - for (; j < i; j++) { - double *m = chain->matrices[j]; - glPushMatrix(); - glMultMatrixd(m); - int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); - } else if (background) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); - } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { - // User-defined color from source - glColor4d(m[16], m[17], m[18], m[19]); - if (shaderinfo) { - glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]); - glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); - } - chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); - } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo); - } else { - chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo); - } - glPopMatrix(); - } - if (shaderinfo) - glUseProgram(0); - for (unsigned int k = 0; k < primitives.size(); k++) { - delete primitives[k]; - } - glDepthFunc(GL_LEQUAL); - primitives.clear(); - } - - if (last) - break; - - OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? - OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); - prim->p = chain->polysets[i]; - prim->m = chain->matrices[i]; - prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) - prim->csgmode += 20; - else if (background) - prim->csgmode += 10; - primitives.push_back(prim); - } -} - -static void renderGLThrownTogether(void *vp); - -static void renderGLviaOpenCSG(void *vp) -{ - MainWindow *m = (MainWindow*)vp; - if (!m->enableOpenCSG) { - renderGLThrownTogether(vp); - return; - } - static int glew_initialized = 0; - if (!glew_initialized) { - glew_initialized = 1; - glewInit(); - } - if (m->root_chain) { - GLint *shaderinfo = m->screen->shaderinfo; - if (!shaderinfo[0]) - shaderinfo = NULL; - renderCSGChainviaOpenCSG(m->root_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, false); - if (m->background_chain) { - renderCSGChainviaOpenCSG(m->background_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, true); - } - if (m->highlights_chain) { - renderCSGChainviaOpenCSG(m->highlights_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, true, false); - } - } -#ifdef ENABLE_MDI - OpenCSG::reset(); -#endif -} - -/*! - Go to the OpenCSG view mode. - Falls back to thrown together mode if OpenCSG is not available -*/ -void MainWindow::viewModeOpenCSG() -{ - if (screen->hasOpenCSGSupport()) { - viewModeActionsUncheck(); - viewActionOpenCSG->setChecked(true); - screen->setRenderFunc(renderGLviaOpenCSG, this); - screen->updateGL(); - } else { - viewModeThrownTogether(); - } -} - -#endif /* ENABLE_OPENCSG */ - -#ifdef ENABLE_CGAL - -static void renderGLviaCGAL(void *vp) -{ - MainWindow *m = (MainWindow*)vp; - if (m->recreate_cgal_ogl_p) { - m->recreate_cgal_ogl_p = false; - Polyhedron *p = (Polyhedron*)m->cgal_ogl_p; - delete p; - m->cgal_ogl_p = NULL; - if (m->cgal_ogl_ps) - m->cgal_ogl_ps->unlink(); - m->cgal_ogl_ps = NULL; - } - if (!m->root_N) return; - if (m->root_N->dim == 2) - { - if (m->cgal_ogl_ps == NULL) { - DxfData dd(*m->root_N); - m->cgal_ogl_ps = new PolySet(); - m->cgal_ogl_ps->is2d = true; - dxf_tesselate(m->cgal_ogl_ps, &dd, 0, true, false, 0); - } - - // Draw 2D polygons - glDisable(GL_LIGHTING); - const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR); - glColor3f(col.redF(), col.greenF(), col.blueF()); - - for (int i=0; i < m->cgal_ogl_ps->polygons.size(); i++) { - glBegin(GL_POLYGON); - for (int j=0; j < m->cgal_ogl_ps->polygons[i].size(); j++) { - PolySet::Point p = m->cgal_ogl_ps->polygons[i][j]; - glVertex3d(p.x, p.y, -0.1); - } - glEnd(); - } - - typedef CGAL_Nef_polyhedron2::Explorer Explorer; - typedef Explorer::Face_const_iterator fci_t; - typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; - typedef Explorer::Point Point; - Explorer E = m->root_N->p2.explorer(); - - // Draw 2D edges - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glLineWidth(2); - const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR); - glColor3f(col2.redF(), col2.greenF(), col2.blueF()); - - // Extract the boundary, including inner boundaries of the polygons - for (fci_t fit = E.faces_begin(), fend = E.faces_end(); fit != fend; ++fit) - { - bool fset = false; - double fx = 0.0, fy = 0.0; - heafcc_t fcirc(E.halfedge(fit)), fend(fcirc); - CGAL_For_all(fcirc, fend) { - if(E.is_standard(E.target(fcirc))) { - Point p = E.point(E.target(fcirc)); - double x = to_double(p.x()), y = to_double(p.y()); - if (!fset) { - glBegin(GL_LINE_STRIP); - fx = x, fy = y; - fset = true; - } - glVertex3d(x, y, -0.1); - } - } - if (fset) { - glVertex3d(fx, fy, -0.1); - glEnd(); - } - } - - glEnable(GL_DEPTH_TEST); - } - else if (m->root_N->dim == 3) - { - Polyhedron *p = (Polyhedron*)m->cgal_ogl_p; - if (!p) { - Nef3_Converter::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()); - Nef3_Converter::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()); - m->cgal_ogl_p = p = new Polyhedron(); - Nef3_Converter::convert_to_OGLPolyhedron(m->root_N->p3, p); - p->init(); - } - if (m->viewActionCGALSurfaces->isChecked()) - p->set_style(SNC_BOUNDARY); - if (m->viewActionCGALGrid->isChecked()) - p->set_style(SNC_SKELETON); -#if 0 - p->draw(); -#else - if (p->style == SNC_BOUNDARY) { - glCallList(p->object_list_+2); - if (m->viewActionShowEdges->isChecked()) { - glDisable(GL_LIGHTING); - glCallList(p->object_list_+1); - glCallList(p->object_list_); - } - } else { - glDisable(GL_LIGHTING); - glCallList(p->object_list_+1); - glCallList(p->object_list_); - } -#endif - } -} - -void MainWindow::viewModeCGALSurface() -{ - viewModeActionsUncheck(); - viewActionCGALSurfaces->setChecked(true); - screen->setRenderFunc(renderGLviaCGAL, this); - screen->updateGL(); -} - -void MainWindow::viewModeCGALGrid() -{ - viewModeActionsUncheck(); - viewActionCGALGrid->setChecked(true); - screen->setRenderFunc(renderGLviaCGAL, this); - screen->updateGL(); -} - -#endif /* ENABLE_CGAL */ - -static void renderGLThrownTogetherChain(MainWindow *m, CSGChain *chain, bool highlight, bool background, bool fberror) -{ - glDepthFunc(GL_LEQUAL); - QHash,int> polySetVisitMark; - bool showEdges = m->viewActionShowEdges->isChecked(); - for (int i = 0; i < chain->polysets.size(); i++) { - if (polySetVisitMark[QPair(chain->polysets[i], chain->matrices[i])]++ > 0) - continue; - double *m = chain->matrices[i]; - glPushMatrix(); - glMultMatrixd(m); - int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m); - if (showEdges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20)); - glEnable(GL_LIGHTING); - } - } else if (background) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m); - if (showEdges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10)); - glEnable(GL_LIGHTING); - } - } else if (fberror) { - if (highlight) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 20), m); - } else if (background) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 10), m); - } else { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); - } - } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { - glColor4d(m[16], m[17], m[18], m[19]); - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); - if (showEdges) { - glDisable(GL_LIGHTING); - glColor4d((m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); - chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); - } - } else if (chain->types[i] == CSGTerm::TYPE_DIFFERENCE) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m); - if (showEdges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); - } - } else { - chain->polysets[i]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m); - if (showEdges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); - } - } - glPopMatrix(); - } -} - -static void renderGLThrownTogether(void *vp) -{ - MainWindow *m = (MainWindow*)vp; - if (m->root_chain) { - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - renderGLThrownTogetherChain(m, m->root_chain, false, false, false); - glCullFace(GL_FRONT); - glColor3ub(255, 0, 255); - renderGLThrownTogetherChain(m, m->root_chain, false, false, true); - glDisable(GL_CULL_FACE); - } - if (m->background_chain) - renderGLThrownTogetherChain(m, m->background_chain, false, true, false); - if (m->highlights_chain) - renderGLThrownTogetherChain(m, m->highlights_chain, true, false, false); -} - -void MainWindow::viewModeThrownTogether() -{ - viewModeActionsUncheck(); - viewActionThrownTogether->setChecked(true); - screen->setRenderFunc(renderGLThrownTogether, this); - screen->updateGL(); -} - -void MainWindow::viewModeShowEdges() -{ - screen->updateGL(); -} - -void MainWindow::viewModeShowAxes() -{ - screen->setShowAxes(viewActionShowAxes->isChecked()); - screen->updateGL(); -} - -void MainWindow::viewModeShowCrosshairs() -{ - screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); - screen->updateGL(); -} - -void MainWindow::viewModeAnimate() -{ - if (viewActionAnimate->isChecked()) { - animate_panel->show(); - actionCompile(); - updatedFps(); - } else { - animate_panel->hide(); - animate_timer->stop(); - } -} - -void MainWindow::animateUpdateDocChanged() -{ - QString current_doc = editor->toPlainText(); - if (current_doc != last_compiled_doc) - animateUpdate(); -} - -void MainWindow::animateUpdate() -{ - if (animate_panel->isVisible()) { - bool fps_ok; - double fps = e_fps->text().toDouble(&fps_ok); - if (fps_ok && fps <= 0 && !animate_timer->isActive()) { - animate_timer->stop(); - animate_timer->setSingleShot(true); - animate_timer->setInterval(50); - animate_timer->start(); - } - } -} - -void MainWindow::viewAngleTop() -{ - screen->object_rot_x = 90; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); -} - -void MainWindow::viewAngleBottom() -{ - screen->object_rot_x = 270; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); -} - -void MainWindow::viewAngleLeft() -{ - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 90; - screen->updateGL(); -} - -void MainWindow::viewAngleRight() -{ - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 270; - screen->updateGL(); -} - -void MainWindow::viewAngleFront() -{ - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); -} - -void MainWindow::viewAngleBack() -{ - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 180; - screen->updateGL(); -} - -void MainWindow::viewAngleDiagonal() -{ - screen->object_rot_x = 35; - screen->object_rot_y = 0; - screen->object_rot_z = 25; - screen->updateGL(); -} - -void MainWindow::viewCenter() -{ - screen->object_trans_x = 0; - screen->object_trans_y = 0; - screen->object_trans_z = 0; - screen->updateGL(); -} - -void MainWindow::viewPerspective() -{ - viewActionPerspective->setChecked(true); - viewActionOrthogonal->setChecked(false); - screen->setOrthoMode(false); - screen->updateGL(); -} - -void MainWindow::viewOrthogonal() -{ - viewActionPerspective->setChecked(false); - viewActionOrthogonal->setChecked(true); - screen->setOrthoMode(true); - screen->updateGL(); -} - -void MainWindow::hideConsole() -{ - if (viewActionHide->isChecked()) { - console->hide(); - } else { - console->show(); - } -} - -void MainWindow::dragEnterEvent(QDragEnterEvent *event) -{ - if (event->mimeData()->hasUrls()) - event->acceptProposedAction(); -} - -void MainWindow::dropEvent(QDropEvent *event) -{ - current_win = this; - const QList urls = event->mimeData()->urls(); - for (int i = 0; i < urls.size(); i++) { - if (urls[i].scheme() != "file") - continue; - openFile(urls[i].path()); - } - current_win = NULL; -} - -void -MainWindow::helpAbout() -{ - qApp->setWindowIcon(QApplication::windowIcon()); - QMessageBox::information(this, "About OpenSCAD", QString(helptitle) + QString(copyrighttext)); -} - -void -MainWindow::helpManual() -{ - QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual")); -} - -/*! - FIXME: In SDI mode, this should be called also on New and Open - In MDI mode; also call on both reload functions? - */ -bool -MainWindow::maybeSave() -{ - if (editor->document()->isModified()) { - QMessageBox::StandardButton ret; - ret = QMessageBox::warning(this, "Application", - "The document has been modified.\n" - "Do you want to save your changes?", - QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - if (ret == QMessageBox::Save) { - actionSave(); - return true; // FIXME: Should return false on error - } - else if (ret == QMessageBox::Cancel) { - return false; - } - } - return true; -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - if (maybeSave()) { - event->accept(); - } else { - event->ignore(); - } -} - -void -MainWindow::preferences() -{ - Preferences::inst()->show(); - Preferences::inst()->activateWindow(); - Preferences::inst()->raise(); -} - -void MainWindow::setFont(const QString &family, uint size) -{ - QFont font(editor->font()); - if (!family.isEmpty()) font.setFamily(family); - if (size > 0) font.setPointSize(size); - font.setStyleHint(QFont::TypeWriter); - editor->setFont(font); -} - -void MainWindow::quit() -{ - QCloseEvent ev; - QApplication::sendEvent(QApplication::instance(), &ev); - if (ev.isAccepted()) QApplication::instance()->quit(); -} diff --git a/mask.png b/mask.png deleted file mode 100644 index eea1027..0000000 Binary files a/mask.png and /dev/null differ diff --git a/module.cc b/module.cc deleted file mode 100644 index a319e1e..0000000 --- a/module.cc +++ /dev/null @@ -1,374 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -AbstractModule::~AbstractModule() -{ -} - -AbstractNode *AbstractModule::evaluate(const Context*, const ModuleInstantiation *inst) const -{ - AbstractNode *node = new AbstractNode(inst); - - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } - - return node; -} - -QString AbstractModule::dump(QString indent, QString name) const -{ - return QString("%1abstract module %2();\n").arg(indent, name); -} - -ModuleInstantiation::~ModuleInstantiation() -{ - foreach (Expression *v, argexpr) - delete v; - foreach (ModuleInstantiation *v, children) - delete v; -} - -QString ModuleInstantiation::dump(QString indent) const -{ - QString text = indent; - if (!label.isEmpty()) - text += label + QString(": "); - text += modname + QString("("); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - if (!argnames[i].isEmpty()) - text += argnames[i] + QString(" = "); - text += argexpr[i]->dump(); - } - if (children.size() == 0) { - text += QString(");\n"); - } else if (children.size() == 1) { - text += QString(")\n"); - text += children[0]->dump(indent + QString("\t")); - } else { - text += QString(") {\n"); - for (int i = 0; i < children.size(); i++) { - text += children[i]->dump(indent + QString("\t")); - } - text += QString("%1}\n").arg(indent); - } - return text; -} - -AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const -{ - AbstractNode *node = NULL; - if (this->ctx) { - PRINTA("WARNING: Ignoring recursive module instanciation of '%1'.", modname); - } else { - ModuleInstantiation *that = (ModuleInstantiation*)this; - that->argvalues.clear(); - foreach (Expression *v, that->argexpr) { - that->argvalues.append(v->evaluate(ctx)); - } - that->ctx = ctx; - node = ctx->evaluate_module(this); - that->ctx = NULL; - that->argvalues.clear(); - } - return node; -} - -Module::~Module() -{ - foreach (Expression *v, assignments_expr) - delete v; - foreach (AbstractFunction *v, functions) - delete v; - foreach (AbstractModule *v, modules) - delete v; - foreach (ModuleInstantiation *v, children) - delete v; -} - -AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - c.inst_p = inst; - c.set_variable("$children", Value(double(inst->children.size()))); - - c.functions_p = &functions; - c.modules_p = &modules; - - for (int i = 0; i < assignments_var.size(); i++) { - c.set_variable(assignments_var[i], assignments_expr[i]->evaluate(&c)); - } - - AbstractNode *node = new AbstractNode(inst); - for (int i = 0; i < children.size(); i++) { - AbstractNode *n = children[i]->evaluate(&c); - if (n != NULL) - node->children.append(n); - } - - return node; -} - -QString Module::dump(QString indent, QString name) const -{ - QString text, tab; - if (!name.isEmpty()) { - text = QString("%1module %2(").arg(indent, name); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - text += argnames[i]; - if (argexpr[i]) - text += QString(" = ") + argexpr[i]->dump(); - } - text += QString(") {\n"); - tab = "\t"; - } - { - QHashIterator i(functions); - while (i.hasNext()) { - i.next(); - text += i.value()->dump(indent + tab, i.key()); - } - } - { - QHashIterator i(modules); - while (i.hasNext()) { - i.next(); - text += i.value()->dump(indent + tab, i.key()); - } - } - for (int i = 0; i < assignments_var.size(); i++) { - text += QString("%1%2 = %3;\n").arg(indent + tab, assignments_var[i], assignments_expr[i]->dump()); - } - for (int i = 0; i < children.size(); i++) { - text += children[i]->dump(indent + tab); - } - if (!name.isEmpty()) { - text += QString("%1}\n").arg(indent); - } - return text; -} - -QHash builtin_modules; - -void initialize_builtin_modules() -{ - builtin_modules["group"] = new AbstractModule(); - - register_builtin_csgops(); - register_builtin_transform(); - register_builtin_primitives(); - register_builtin_surface(); - register_builtin_control(); - register_builtin_render(); - register_builtin_import(); - register_builtin_dxf_linear_extrude(); - register_builtin_dxf_rotate_extrude(); -} - -void destroy_builtin_modules() -{ - foreach (AbstractModule *v, builtin_modules) - delete v; - builtin_modules.clear(); -} - -int AbstractNode::idx_counter; - -AbstractNode::AbstractNode(const ModuleInstantiation *mi) -{ - modinst = mi; - idx = idx_counter++; -} - -AbstractNode::~AbstractNode() -{ - foreach (AbstractNode *v, children) - delete v; -} - -QString AbstractNode::mk_cache_id() const -{ - QString cache_id = dump(""); - cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:")); - cache_id.remove(' '); - cache_id.remove('\t'); - cache_id.remove('\n'); - return cache_id; -} - -#ifdef ENABLE_CGAL - -AbstractNode::cgal_nef_cache_entry::cgal_nef_cache_entry(CGAL_Nef_polyhedron N) : - N(N), msg(print_messages_stack.last()) { }; - -QCache AbstractNode::cgal_nef_cache(100000); - -static CGAL_Nef_polyhedron render_cgal_nef_polyhedron_backend(const AbstractNode *that, bool intersect) -{ - QString cache_id = that->mk_cache_id(); - if (that->cgal_nef_cache.contains(cache_id)) { - that->progress_report(); - PRINT(that->cgal_nef_cache[cache_id]->msg); - return that->cgal_nef_cache[cache_id]->N; - } - - print_messages_push(); - - bool first = true; - CGAL_Nef_polyhedron N; - foreach (AbstractNode *v, that->children) { - if (v->modinst->tag_background) - continue; - if (first) { - N = v->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - if (intersect) - N.p2 *= v->render_cgal_nef_polyhedron().p2; - else - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else { - if (intersect) - N.p3 *= v->render_cgal_nef_polyhedron().p3; - else - N.p3 += v->render_cgal_nef_polyhedron().p3; - } - } - - that->cgal_nef_cache.insert(cache_id, new AbstractNode::cgal_nef_cache_entry(N), N.weight()); - that->progress_report(); - print_messages_pop(); - - return N; -} - -CGAL_Nef_polyhedron AbstractNode::render_cgal_nef_polyhedron() const -{ - return render_cgal_nef_polyhedron_backend(this, false); -} - -CGAL_Nef_polyhedron AbstractIntersectionNode::render_cgal_nef_polyhedron() const -{ - return render_cgal_nef_polyhedron_backend(this, true); -} - -#endif /* ENABLE_CGAL */ - -static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector *highlights, QVector *background) -{ - CSGTerm *t1 = NULL; - foreach(AbstractNode *v, that->children) { - CSGTerm *t2 = v->render_csg_term(m, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - if (intersect) - t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); - else - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } - } - if (t1 && that->modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && that->modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; -} - -CSGTerm *AbstractNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - return render_csg_term_backend(this, false, m, highlights, background); -} - -CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - return render_csg_term_backend(this, true, m, highlights, background); -} - -QString AbstractNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: group() {\n").arg(idx); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - -QString AbstractIntersectionNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: intersection() {\n").arg(idx); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - -int progress_report_count; -void (*progress_report_f)(const class AbstractNode*, void*, int); -void *progress_report_vp; - -void AbstractNode::progress_prepare() -{ - foreach (AbstractNode *v, children) - v->progress_prepare(); - progress_mark = ++progress_report_count; -} - -void AbstractNode::progress_report() const -{ - if (progress_report_f) - progress_report_f(this, progress_report_vp, progress_mark); -} - -void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp) -{ - progress_report_count = 0; - progress_report_f = f; - progress_report_vp = vp; - root->progress_prepare(); -} - -void progress_report_fin() -{ - progress_report_count = 0; - progress_report_f = NULL; - progress_report_vp = NULL; -} - diff --git a/nef2dxf.cc b/nef2dxf.cc deleted file mode 100644 index 9c69e84..0000000 --- a/nef2dxf.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" - -DxfData::DxfData(const struct CGAL_Nef_polyhedron &N) -{ - Grid2d grid(GRID_COARSE); - - typedef CGAL_Nef_polyhedron2::Explorer Explorer; - typedef Explorer::Face_const_iterator fci_t; - typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; - Explorer E = N.p2.explorer(); - - for (fci_t fit = E.faces_begin(), fend = E.faces_end(); fit != fend; ++fit) - { - heafcc_t fcirc(E.halfedge(fit)), fend(fcirc); - int first_point = -1, last_point = -1; - CGAL_For_all(fcirc, fend) { - if (E.is_standard(E.target(fcirc))) { - Explorer::Point ep = E.point(E.target(fcirc)); - double x = to_double(ep.x()), y = to_double(ep.y()); - int this_point = -1; - if (grid.has(x, y)) { - this_point = grid.align(x, y); - } else { - this_point = grid.align(x, y) = points.size(); - points.append(Point(x, y)); - } - if (first_point < 0) { - paths.append(Path()); - first_point = this_point; - } - if (this_point != last_point) { - paths.last().points.append(&points[this_point]); - last_point = this_point; - } - } - } - if (first_point >= 0) { - paths.last().is_closed = 1; - paths.last().points.append(&points[first_point]); - } - } - - fixup_path_direction(); -} - diff --git a/openscad.cc b/openscad.cc deleted file mode 100644 index 8c521b6..0000000 --- a/openscad.cc +++ /dev/null @@ -1,257 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "MainWindow.h" - -#include -#include -#include -#include -#include -#include -#ifdef Q_WS_MAC -#include "EventFilter.h" -#endif - -static void help(const char *progname) -{ - fprintf(stderr, "Usage: %s [ { -s stl_file | -o off_file | -x dxf_file } [ -d deps_file ] ]\\\n" - "%*s[ -m make_command ] [ -D var=val [..] ] filename\n", - progname, int(strlen(progname))+8, ""); - exit(1); -} - -QString commandline_commands; -const char *make_command = NULL; -QSet dependencies; - -void handle_dep(QString filename) -{ - if (filename.startsWith("/")) - dependencies.insert(filename); - else - dependencies.insert(QDir::currentPath() + QString("/") + filename); - if (!QFile(filename).exists() && make_command) { - char buffer[4096]; - snprintf(buffer, 4096, "%s '%s'", make_command, filename.replace("'", "'\\''").toUtf8().data()); - system(buffer); - } -} - -int main(int argc, char **argv) -{ - int rc = 0; - - initialize_builtin_functions(); - initialize_builtin_modules(); - -#ifdef Q_WS_X11 - // see : - // On X11, the window system is initialized if GUIenabled is true. If GUIenabled - // is false, the application does not connect to the X server. On Windows and - // Macintosh, currently the window system is always initialized, regardless of the - // value of GUIenabled. This may change in future versions of Qt. - bool useGUI = getenv("DISPLAY") != 0; -#else - bool useGUI = true; -#endif - QApplication app(argc, argv, useGUI); -#ifdef Q_WS_MAC - app.setLibraryPaths(QStringList(app.applicationDirPath() + "/../PlugIns")); - app.installEventFilter(new EventFilter(&app)); -#endif - - // set up groups for QSettings - QCoreApplication::setOrganizationName("OpenSCAD"); - QCoreApplication::setOrganizationDomain("openscad.org"); - QCoreApplication::setApplicationName("OpenSCAD"); - - - const char *filename = NULL; - const char *stl_output_file = NULL; - const char *off_output_file = NULL; - const char *dxf_output_file = NULL; - const char *deps_output_file = NULL; - - int opt; - - while ((opt = getopt(argc, argv, "s:o:x:d:m:D:")) != -1) - { - switch (opt) - { - case 's': - if (stl_output_file || off_output_file || dxf_output_file) - help(argv[0]); - stl_output_file = optarg; - break; - case 'o': - if (stl_output_file || off_output_file || dxf_output_file) - help(argv[0]); - off_output_file = optarg; - break; - case 'x': - if (stl_output_file || off_output_file || dxf_output_file) - help(argv[0]); - dxf_output_file = optarg; - break; - case 'd': - if (deps_output_file) - help(argv[0]); - deps_output_file = optarg; - break; - case 'm': - if (make_command) - help(argv[0]); - make_command = optarg; - break; - case 'D': - commandline_commands += QString(optarg) + QString(";\n"); - break; - default: - help(argv[0]); - } - } - - if (optind < argc) - filename = argv[optind++]; - -#ifndef ENABLE_MDI - if (optind != argc) - help(argv[0]); -#endif - - if (stl_output_file || off_output_file || dxf_output_file) - { - if (!filename) - help(argv[0]); - -#ifdef ENABLE_CGAL - Context root_ctx; - root_ctx.functions_p = &builtin_functions; - root_ctx.modules_p = &builtin_modules; - root_ctx.set_variable("$fn", Value(0.0)); - root_ctx.set_variable("$fs", Value(1.0)); - root_ctx.set_variable("$fa", Value(12.0)); - root_ctx.set_variable("$t", Value(0.0)); - - Value zero3; - zero3.type = Value::VECTOR; - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - root_ctx.set_variable("$vpt", zero3); - root_ctx.set_variable("$vpr", zero3); - - AbstractModule *root_module; - ModuleInstantiation root_inst; - AbstractNode *root_node; - - handle_dep(filename); - FILE *fp = fopen(filename, "rt"); - if (!fp) { - fprintf(stderr, "Can't open input file `%s'!\n", filename); - exit(1); - } else { - QString text; - char buffer[513]; - int ret; - while ((ret = fread(buffer, 1, 512, fp)) > 0) { - buffer[ret] = 0; - text += buffer; - } - fclose(fp); - root_module = parse((text+commandline_commands).toAscii().data(), false); - } - - QString original_path = QDir::currentPath(); - QFileInfo fileInfo(filename); - QDir::setCurrent(fileInfo.dir().absolutePath()); - - AbstractNode::idx_counter = 1; - root_node = root_module->evaluate(&root_ctx, &root_inst); - - CGAL_Nef_polyhedron *root_N; - root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); - - QDir::setCurrent(original_path); - - if (deps_output_file) { - fp = fopen(deps_output_file, "wt"); - if (!fp) { - fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", deps_output_file); - exit(1); - } - fprintf(fp, "%s:", stl_output_file ? stl_output_file : off_output_file); - QSetIterator i(dependencies); - while (i.hasNext()) - fprintf(fp, " \\\n\t%s", i.next().toUtf8().data()); - fprintf(fp, "\n"); - fclose(fp); - } - - if (stl_output_file) - export_stl(root_N, stl_output_file, NULL); - - if (off_output_file) - export_off(root_N, off_output_file, NULL); - - if (dxf_output_file) - export_dxf(root_N, dxf_output_file, NULL); - - delete root_node; - delete root_N; -#else - fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); - exit(1); -#endif - } - else if (useGUI) - { - // turn on anti-aliasing - QGLFormat f; - f.setSampleBuffers(true); - f.setSamples(4); - QGLFormat::setDefaultFormat(f); -#ifdef ENABLE_MDI - new MainWindow(filename); - while (optind < argc) - new MainWindow(argv[optind++]); - app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); -#else - MainWindow *m = new MainWindow(filename); - app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit())); -#endif - rc = app.exec(); - } - else - { - fprintf(stderr, "Requested GUI mode but can't open display!\n"); - exit(1); - } - - destroy_builtin_functions(); - destroy_builtin_modules(); - - return rc; -} - diff --git a/openscad.h b/openscad.h deleted file mode 100644 index 03ea1cb..0000000 --- a/openscad.h +++ /dev/null @@ -1,738 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef OPENSCAD_H -#define OPENSCAD_H - -#ifdef ENABLE_OPENCSG -// this must be included before the GL headers -# include -#endif - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -// for win32 and maybe others.. -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -class Value; -class Expression; - -class AbstractFunction; -class BuiltinFunction; -class Function; - -class AbstractModule; -class ModuleInstantiation; -class Module; - -class Context; -class PolySet; -class CSGTerm; -class CSGChain; -class AbstractNode; -class AbstractIntersectionNode; -class AbstractPolyNode; -struct CGAL_Nef_polyhedron; - -const double GRID_COARSE = 0.001; -const double GRID_FINE = 0.000001; - -template -class Grid2d -{ -public: - double res; - QHash, T> db; - - Grid2d(double resolution) { - res = resolution; - } - /*! - Aligns x,y to the grid or to existing point if one close enough exists. - Returns the value stored if a point already existing or an uninitialized new value - if not. - */ - T &align(double &x, double &y) { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - if (!db.contains(QPair(ix, iy))) { - int dist = 10; - for (int64_t jx = ix - 1; jx <= ix + 1; jx++) { - for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { - if (!db.contains(QPair(jx, jy))) - continue; - if (abs(ix-jx) + abs(iy-jy) < dist) { - dist = abs(ix-jx) + abs(iy-jy); - ix = jx; - iy = jy; - } - } - } - } - x = ix * res, y = iy * res; - return db[QPair(ix, iy)]; - } - bool has(double x, double y) const { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - if (db.contains(QPair(ix, iy))) - return true; - for (int64_t jx = ix - 1; jx <= ix + 1; jx++) - for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { - if (db.contains(QPair(jx, jy))) - return true; - } - return false; - } - bool eq(double x1, double y1, double x2, double y2) { - align(x1, y1); - align(x2, y2); - if (fabs(x1 - x2) < res && fabs(y1 - y2) < res) - return true; - return false; - } - T &data(double x, double y) { - return align(x, y); - } - T &operator()(double x, double y) { - return align(x, y); - } -}; - -template -class Grid3d -{ -public: - double res; - QHash,int64_t>, T> db; - - Grid3d(double resolution) { - res = resolution; - } - T &align(double &x, double &y, double &z) { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - int64_t iz = (int64_t)round(z / res); - if (!db.contains(QPair,int64_t>(QPair(ix, iy), iz))) { - int dist = 10; - for (int64_t jx = ix - 1; jx <= ix + 1; jx++) { - for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { - for (int64_t jz = iz - 1; jz <= iz + 1; jz++) { - if (!db.contains(QPair,int64_t>(QPair(jx, jy), jz))) - continue; - if (abs(ix-jx) + abs(iy-jy) + abs(iz-jz) < dist) { - dist = abs(ix-jx) + abs(iy-jy) + abs(iz-jz); - ix = jx; - iy = jy; - iz = jz; - } - } - } - } - } - x = ix * res, y = iy * res, z = iz * res; - return db[QPair,int64_t>(QPair(ix, iy), iz)]; - } - bool has(double x, double y, double z) { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - int64_t iz = (int64_t)round(z / res); - if (db.contains(QPair,int64_t>(QPair(ix, iy), iz))) - return true; - for (int64_t jx = ix - 1; jx <= ix + 1; jx++) - for (int64_t jy = iy - 1; jy <= iy + 1; jy++) - for (int64_t jz = iz - 1; jz <= iz + 1; jz++) { - if (db.contains(QPair,int64_t>(QPair(jx, jy), jz))) - return true; - } - return false; - - } - bool eq(double x1, double y1, double z1, double x2, double y2, double z2) { - align(x1, y1, z1); - align(x2, y2, z2); - if (fabs(x1 - x2) < res && fabs(y1 - y2) < res && fabs(z1 - z2) < res) - return true; - return false; - } - T &data(double x, double y, double z) { - return align(x, y, z); - } - T &operator()(double x, double y, double z) { - return align(x, y, z); - } -}; - -class Value -{ -public: - enum type_e { - UNDEFINED, - BOOL, - NUMBER, - RANGE, - VECTOR, - STRING - }; - - enum type_e type; - - bool b; - double num; - QVector vec; - double range_begin; - double range_step; - double range_end; - QString text; - - Value(); - ~Value(); - - Value(bool v); - Value(double v); - Value(const QString &t); - - Value(const Value &v); - Value& operator = (const Value &v); - - Value operator ! () const; - Value operator && (const Value &v) const; - Value operator || (const Value &v) const; - - Value operator + (const Value &v) const; - Value operator - (const Value &v) const; - Value operator * (const Value &v) const; - Value operator / (const Value &v) const; - Value operator % (const Value &v) const; - - Value operator < (const Value &v) const; - Value operator <= (const Value &v) const; - Value operator == (const Value &v) const; - Value operator != (const Value &v) const; - Value operator >= (const Value &v) const; - Value operator > (const Value &v) const; - - Value inv() const; - - bool getnum(double &v) const; - bool getv2(double &x, double &y) const; - bool getv3(double &x, double &y, double &z) const; - - QString dump() const; - -private: - void reset_undef(); -}; - -class Expression -{ -public: - QVector children; - - Value *const_value; - QString var_name; - - QString call_funcname; - QVector call_argnames; - - // Boolean: ! && || - // Operators: * / % + - - // Relations: < <= == != >= > - // Vector element: [] - // Condition operator: ?: - // Invert (prefix '-'): I - // Constant value: C - // Create Range: R - // Create Vector: V - // Create Matrix: M - // Lookup Variable: L - // Lookup member per name: N - // Function call: F - QString type; - - Expression(); - ~Expression(); - - Value evaluate(const Context *context) const; - QString dump() const; -}; - -class AbstractFunction -{ -public: - virtual ~AbstractFunction(); - virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; -}; - -class BuiltinFunction : public AbstractFunction -{ -public: - typedef Value (*eval_func_t)(const QVector &argnames, const QVector &args); - eval_func_t eval_func; - - BuiltinFunction(eval_func_t f) : eval_func(f) { } - virtual ~BuiltinFunction(); - - virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; -}; - -class Function : public AbstractFunction -{ -public: - QVector argnames; - QVector argexpr; - - Expression *expr; - - Function() { } - virtual ~Function(); - - virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; -}; - -extern QHash builtin_functions; -extern void initialize_builtin_functions(); -extern void initialize_builtin_dxf_dim(); -extern void destroy_builtin_functions(); - -class AbstractModule -{ -public: - virtual ~AbstractModule(); - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; - virtual QString dump(QString indent, QString name) const; -}; - -class ModuleInstantiation -{ -public: - QString label; - QString modname; - QVector argnames; - QVector argexpr; - QVector argvalues; - QVector children; - - bool tag_root; - bool tag_highlight; - bool tag_background; - const Context *ctx; - - ModuleInstantiation() : tag_root(false), tag_highlight(false), tag_background(false), ctx(NULL) { } - ~ModuleInstantiation(); - - QString dump(QString indent) const; - AbstractNode *evaluate(const Context *ctx) const; -}; - -class Module : public AbstractModule -{ -public: - QVector argnames; - QVector argexpr; - - QVector assignments_var; - QVector assignments_expr; - - QHash functions; - QHash modules; - - QVector children; - - Module() { } - virtual ~Module(); - - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; - virtual QString dump(QString indent, QString name) const; -}; - -extern QHash builtin_modules; -extern void initialize_builtin_modules(); -extern void destroy_builtin_modules(); - -extern void register_builtin_csgops(); -extern void register_builtin_transform(); -extern void register_builtin_primitives(); -extern void register_builtin_surface(); -extern void register_builtin_control(); -extern void register_builtin_render(); -extern void register_builtin_import(); -extern void register_builtin_dxf_linear_extrude(); -extern void register_builtin_dxf_rotate_extrude(); - -class Context -{ -public: - const Context *parent; - QHash variables; - QHash config_variables; - const QHash *functions_p; - const QHash *modules_p; - const ModuleInstantiation *inst_p; - - static QVector ctx_stack; - - Context(const Context *parent = NULL); - ~Context(); - - void args(const QVector &argnames, const QVector &argexpr, const QVector &call_argnames, const QVector &call_argvalues); - - void set_variable(QString name, Value value); - Value lookup_variable(QString name, bool silent = false) const; - - Value evaluate_function(QString name, const QVector &argnames, const QVector &argvalues) const; - AbstractNode *evaluate_module(const ModuleInstantiation *inst) const; -}; - -class DxfData -{ -public: - struct Point { - double x, y; - Point() : x(0), y(0) { } - Point(double x, double y) : x(x), y(y) { } - }; - struct Path { - QList points; - bool is_closed, is_inner; - Path() : is_closed(false), is_inner(false) { } - }; - struct Dim { - unsigned int type; - double coords[7][2]; - double angle; - double length; - QString name; - Dim() { - for (int i = 0; i < 7; i++) - for (int j = 0; j < 2; j++) - coords[i][j] = 0; - type = 0; - angle = 0; - length = 0; - } - }; - - QList points; - QList paths; - QList dims; - - DxfData(); - DxfData(double fn, double fs, double fa, QString filename, QString layername = QString(), double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0); - DxfData(const struct CGAL_Nef_polyhedron &N); - - Point *addPoint(double x, double y); - -private: - void fixup_path_direction(); -}; - -// The CGAL template magic slows down the compilation process by a factor of 5. -// So we only include the declaration of AbstractNode where it is needed... -#ifdef INCLUDE_ABSTRACT_NODE_DETAILS - -#ifdef ENABLE_CGAL - -#include -#include -#include -#include -#include -#include -#include - -typedef CGAL::Extended_cartesian CGAL_Kernel2; -typedef CGAL::Nef_polyhedron_2 CGAL_Nef_polyhedron2; -typedef CGAL_Kernel2::Aff_transformation_2 CGAL_Aff_transformation2; - -typedef CGAL::Cartesian CGAL_Kernel3; -typedef CGAL::Polyhedron_3 CGAL_Polyhedron; -typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS; -typedef CGAL::Polyhedron_incremental_builder_3 CGAL_Polybuilder; -typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3; -typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; -typedef CGAL_Nef_polyhedron3::Vector_3 CGAL_Vector; -typedef CGAL_Nef_polyhedron3::Plane_3 CGAL_Plane; -typedef CGAL_Nef_polyhedron3::Point_3 CGAL_Point; - -struct CGAL_Nef_polyhedron -{ - int dim; - CGAL_Nef_polyhedron2 p2; - CGAL_Nef_polyhedron3 p3; - - CGAL_Nef_polyhedron() { - dim = 0; - } - - CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron2 &p) { - dim = 2; - p2 = p; - } - - CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron3 &p) { - dim = 3; - p3 = p; - } - - int weight() { - if (dim == 2) - return p2.explorer().number_of_vertices(); - if (dim == 3) - return p3.number_of_vertices(); - return 0; - } -}; - -#endif /* ENABLE_CGAL */ - -#ifdef ENABLE_OPENCSG -# include -#endif - -class PolySet -{ -public: - struct Point { - double x, y, z; - Point() : x(0), y(0), z(0) { } - Point(double x, double y, double z) : x(x), y(y), z(z) { } - }; - typedef QList Polygon; - QVector polygons; - QVector borders; - Grid3d grid; - - bool is2d; - int convexity; - - PolySet(); - ~PolySet(); - - void append_poly(); - void append_vertex(double x, double y, double z); - void insert_vertex(double x, double y, double z); - - void append_vertex(double x, double y) { - append_vertex(x, y, 0.0); - } - void insert_vertex(double x, double y) { - insert_vertex(x, y, 0.0); - } - - enum colormode_e { - COLORMODE_NONE, - COLORMODE_MATERIAL, - COLORMODE_CUTOUT, - COLORMODE_HIGHLIGHT, - COLORMODE_BACKGROUND - }; - - enum csgmode_e { - CSGMODE_NONE, - CSGMODE_NORMAL = 1, - CSGMODE_DIFFERENCE = 2, - CSGMODE_BACKGROUND = 11, - CSGMODE_BACKGROUND_DIFFERENCE = 12, - CSGMODE_HIGHLIGHT = 21, - CSGMODE_HIGHLIGHT_DIFFERENCE = 22 - }; - - struct ps_cache_entry { - PolySet *ps; - QString msg; - ps_cache_entry(PolySet *ps); - ~ps_cache_entry(); - }; - - static QCache ps_cache; - - void render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo = NULL) const; - void render_edges(colormode_e colormode, csgmode_e csgmode) const; - -#ifdef ENABLE_CGAL - CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - - int refcount; - PolySet *link(); - void unlink(); -}; - -class CSGTerm -{ -public: - enum type_e { - TYPE_PRIMITIVE, - TYPE_UNION, - TYPE_INTERSECTION, - TYPE_DIFFERENCE - }; - - type_e type; - PolySet *polyset; - QString label; - CSGTerm *left; - CSGTerm *right; - double m[20]; - int refcounter; - - CSGTerm(PolySet *polyset, double m[20], QString label); - CSGTerm(type_e type, CSGTerm *left, CSGTerm *right); - - CSGTerm *normalize(); - CSGTerm *normalize_tail(); - - CSGTerm *link(); - void unlink(); - QString dump(); -}; - -class CSGChain -{ -public: - QVector polysets; - QVector matrices; - QVector types; - QVector labels; - - CSGChain(); - - void add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label); - void import(CSGTerm *term, CSGTerm::type_e type = CSGTerm::TYPE_UNION); - QString dump(); -}; - -class AbstractNode -{ -public: - QVector children; - const ModuleInstantiation *modinst; - - int progress_mark; - void progress_prepare(); - void progress_report() const; - - int idx; - static int idx_counter; - QString dump_cache; - - AbstractNode(const ModuleInstantiation *mi); - virtual ~AbstractNode(); - virtual QString mk_cache_id() const; -#ifdef ENABLE_CGAL - struct cgal_nef_cache_entry { - CGAL_Nef_polyhedron N; - QString msg; - cgal_nef_cache_entry(CGAL_Nef_polyhedron N); - }; - static QCache cgal_nef_cache; - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - virtual QString dump(QString indent) const; -}; - -class AbstractIntersectionNode : public AbstractNode -{ -public: - AbstractIntersectionNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - virtual QString dump(QString indent) const; -}; - -class AbstractPolyNode : public AbstractNode -{ -public: - enum render_mode_e { - RENDER_CGAL, - RENDER_OPENCSG - }; - AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; - virtual PolySet *render_polyset(render_mode_e mode) const; -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - static CSGTerm *render_csg_term_from_ps(double m[20], QVector *highlights, QVector *background, PolySet *ps, const ModuleInstantiation *modinst, int idx); -}; - -extern QHash dxf_dim_cache; -extern QHash dxf_cross_cache; - -extern int progress_report_count; -extern void (*progress_report_f)(const class AbstractNode*, void*, int); -extern void *progress_report_vp; - -void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp); -void progress_report_fin(); - -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h); -void dxf_border_to_ps(PolySet *ps, DxfData *dxf); - -#endif /* INCLUDE_ABSTRACT_NODE_DETAILS */ - -class Highlighter : public QSyntaxHighlighter -{ -public: - Highlighter(QTextDocument *parent); - void highlightBlock(const QString &text); -}; - -extern AbstractModule *parse(const char *text, int debug); -extern int get_fragments_from_r(double r, double fn, double fs, double fa); - -extern QString commandline_commands; -extern int parser_error_pos; - -#ifdef ENABLE_CGAL -void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); -void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); -void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); -#endif -extern void handle_dep(QString filename); - -#endif - diff --git a/openscad.ico b/openscad.ico deleted file mode 100644 index a210b49..0000000 Binary files a/openscad.ico and /dev/null differ diff --git a/patches/CGAL-OGL-Tess-Combine-Fix.patch b/patches/CGAL-OGL-Tess-Combine-Fix.patch new file mode 100644 index 0000000..2a4ad1a --- /dev/null +++ b/patches/CGAL-OGL-Tess-Combine-Fix.patch @@ -0,0 +1,43 @@ +--- CGAL-3.4/include/CGAL/Nef_3/OGL_helper.h ++++ CGAL-3.4/include/CGAL/Nef_3/OGL_helper.h +@@ -243,6 +243,23 @@ + glVertex3dv(pc); + } + ++ inline void CGAL_GLU_TESS_CALLBACK combineCallback(GLdouble coords[3], GLvoid *d[4], GLfloat w[4], GLvoid **dataOut) ++ { ++ static std::list pcache; ++ if (dataOut) { ++ GLdouble *n = new GLdouble[3]; ++ n[0] = coords[0]; ++ n[1] = coords[1]; ++ n[2] = coords[2]; ++ pcache.push_back(n); ++ *dataOut = n; ++ } else { ++ for (std::list::const_iterator i = pcache.begin(); i != pcache.end(); i++) ++ delete[] *i; ++ pcache.clear(); ++ } ++ } ++ + + enum { SNC_AXES}; + enum { SNC_BOUNDARY, SNC_SKELETON }; +@@ -376,6 +393,8 @@ + GLUtesselator* tess_ = gluNewTess(); + gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &vertexCallback); ++ gluTessCallback(tess_, GLenum(GLU_TESS_COMBINE), ++ (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &combineCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_BEGIN), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &beginCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_END), +@@ -410,6 +429,7 @@ + gluTessEndPolygon(tess_); + // CGAL_NEF_TRACEN("End Polygon"); + gluDeleteTess(tess_); ++ combineCallback(NULL, NULL, NULL, NULL); + } + + void construct_axes() const diff --git a/patches/CGAL-Valgrind-Enable-Hack.patch b/patches/CGAL-Valgrind-Enable-Hack.patch new file mode 100644 index 0000000..90733fc --- /dev/null +++ b/patches/CGAL-Valgrind-Enable-Hack.patch @@ -0,0 +1,16 @@ +--- CGAL-3.4/include/CGAL/Interval_nt.h ++++ CGAL-3.4/include/CGAL/Interval_nt.h +@@ -149,11 +149,13 @@ + // The macros CGAL_IA_MUL and CGAL_IA_DIV stop constant propagation only + // on the second argument, so if -fno-rounding-math, the compiler optimizes + // the 2 negations and we get wrong rounding. ++#if 0 + typename Interval_nt<>::Internal_protector P; + CGAL_assertion_msg(-CGAL_IA_MUL(-1.1, 10.1) != CGAL_IA_MUL(1.1, 10.1), + "Wrong rounding: did you forget the -frounding-math option if you use GCC?"); + CGAL_assertion_msg(-CGAL_IA_DIV(-1, 10) != CGAL_IA_DIV(1, 10), + "Wrong rounding: did you forget the -frounding-math option if you use GCC?"); ++#endif + } + }; + diff --git a/patches/OpenCSG-1.1.0-Reset-Hack.patch b/patches/OpenCSG-1.1.0-Reset-Hack.patch new file mode 100644 index 0000000..493a11b --- /dev/null +++ b/patches/OpenCSG-1.1.0-Reset-Hack.patch @@ -0,0 +1,82 @@ +diff --git a/include/opencsg.h b/include/opencsg.h +index daecacc..ffde239 100644 +--- a/include/opencsg.h ++++ b/include/opencsg.h +@@ -188,6 +188,9 @@ namespace OpenCSG { + Algorithm = AlgorithmUnused, + DepthComplexityAlgorithm = NoDepthComplexitySampling); + ++ // call this function whenever switching the OpenGL context ++ void reset(); ++ + } // namespace OpenCSG + + #endif // __OpenCSG__opencsg_h__ +diff --git a/src/channelManager.h b/src/channelManager.h +index ecd5a1d..0e1458a 100644 +--- a/src/channelManager.h ++++ b/src/channelManager.h +@@ -79,9 +79,9 @@ namespace OpenCSG { + /// moved into alpha, to allow alpha testing of the channel. + static void setupTexEnv(Channel channel); + +- private: +- + static OpenGL::OffscreenBuffer* gOffscreenBuffer; ++ ++ private: + static int gOffscreenType; + static bool gInUse; + +diff --git a/src/offscreenBuffer.cpp b/src/offscreenBuffer.cpp +index e02dd83..4f978d5 100644 +--- a/src/offscreenBuffer.cpp ++++ b/src/offscreenBuffer.cpp +@@ -22,19 +22,41 @@ + #include "offscreenBuffer.h" + #include "frameBufferObject.h" + #include "pBufferTexture.h" ++#include "channelManager.h" ++ ++static bool reset_f = false; ++static bool reset_p = false; + + namespace OpenCSG { + ++ void reset() ++ { ++ reset_f = true; ++ reset_p = true; ++ OpenCSG::ChannelManager::gOffscreenBuffer = NULL; ++ } ++ + namespace OpenGL { + + OffscreenBuffer* getOffscreenBuffer(bool fbo) { +- static FrameBufferObject* f = new FrameBufferObject; +- static PBufferTexture* p = new PBufferTexture; +- +- if (fbo) +- return f; +- else ++ static FrameBufferObject* f = NULL; ++ static PBufferTexture* p = NULL; ++ ++ if (fbo) { ++ if (reset_f || f == NULL) { ++ delete f; ++ f = new FrameBufferObject; ++ reset_f = false; ++ } ++ return f; ++ } else { ++ if (reset_p || p == NULL) { ++ delete p; ++ p = new PBufferTexture; ++ reset_p = false; ++ } + return p; ++ } + } + + } // namespace OpenGL diff --git a/patches/OpenCSG-1.1.1-MacOSX-port.patch b/patches/OpenCSG-1.1.1-MacOSX-port.patch new file mode 100644 index 0000000..592cdf9 --- /dev/null +++ b/patches/OpenCSG-1.1.1-MacOSX-port.patch @@ -0,0 +1,276 @@ +diff -ru OpenCSG-1.1.1/RenderTexture/RenderTexture.h OpenCSG-1.1.1-mac/RenderTexture/RenderTexture.h +--- OpenCSG-1.1.1/RenderTexture/RenderTexture.h 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/RenderTexture/RenderTexture.h 2009-12-09 03:15:26.000000000 +0100 +@@ -294,8 +294,8 @@ + bool _BindDepthBuffer( ) const; + + protected: // data +- int _iWidth; // width of the pbuffer +- int _iHeight; // height of the pbuffer ++ GLint _iWidth; // width of the pbuffer ++ GLint _iHeight; // height of the pbuffer + + bool _bIsTexture; + bool _bIsDepthTexture; +@@ -342,8 +342,8 @@ + + // Texture stuff + GLenum _iTextureTarget; +- unsigned int _iTextureID; +- unsigned int _iDepthTextureID; ++ GLuint _iTextureID; ++ GLuint _iDepthTextureID; + + unsigned short* _pPoorDepthTexture; // [Redge] + +diff -ru OpenCSG-1.1.1/example/example.pro OpenCSG-1.1.1-mac/example/example.pro +--- OpenCSG-1.1.1/example/example.pro 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/example/example.pro 2009-12-09 03:15:26.000000000 +0100 +@@ -2,9 +2,16 @@ + TARGET = opencsgexample + + CONFIG += opengl warn_on release +-INCLUDEPATH += ../glew/include ../include +- +-LIBS += -L../lib -lopencsg -lglut -L../glew/lib -lGLEW ++INCLUDEPATH += ../include ++LIBS += -L../lib -lopencsg -lGLEW ++macx { ++ INCLUDEPATH += /opt/local/include ++ LIBS += -framework GLUT -L/opt/local/lib ++} ++else { ++ INCLUDEPATH += ../glew/include ++ LIBS += -lglut -L../glew/lib ++} + + HEADERS = displaylistPrimitive.h + SOURCES = displaylistPrimitive.cpp main.cpp +diff -ru OpenCSG-1.1.1/example/main.cpp OpenCSG-1.1.1-mac/example/main.cpp +--- OpenCSG-1.1.1/example/main.cpp 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/example/main.cpp 2009-12-09 03:15:26.000000000 +0100 +@@ -22,7 +22,11 @@ + // + + #include ++#ifdef __APPLE__ ++#include ++#else + #include ++#endif + #include + #include "displaylistPrimitive.h" + #include +diff -ru OpenCSG-1.1.1/opencsg.pro OpenCSG-1.1.1-mac/opencsg.pro +--- OpenCSG-1.1.1/opencsg.pro 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/opencsg.pro 2009-12-09 03:15:26.000000000 +0100 +@@ -1,2 +1,4 @@ + TEMPLATE = subdirs + SUBDIRS = src example ++# On Mac we get glew from MacPorts ++!macx:SUBDIRS += glew +diff -ru OpenCSG-1.1.1/src/channelManager.cpp OpenCSG-1.1.1-mac/src/channelManager.cpp +--- OpenCSG-1.1.1/src/channelManager.cpp 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/channelManager.cpp 2009-12-09 03:15:26.000000000 +0100 +@@ -23,7 +23,7 @@ + #include + #ifdef _WIN32 + #include +-#else ++#elif !defined(__APPLE__) + #include + #endif + +@@ -160,9 +160,11 @@ + #ifdef WIN32 + if ( WGLEW_ARB_pbuffer + && WGLEW_ARB_pixel_format +-#else ++#elif !defined(__APPLE__) + if ( GLXEW_SGIX_pbuffer + && GLXEW_SGIX_fbconfig ++#else ++ if ( false + #endif + ) { + newOffscreenType = OpenCSG::PBuffer; +diff -ru OpenCSG-1.1.1/src/frameBufferObject.h OpenCSG-1.1.1-mac/src/frameBufferObject.h +--- OpenCSG-1.1.1/src/frameBufferObject.h 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/frameBufferObject.h 2009-12-09 03:15:26.000000000 +0100 +@@ -77,10 +77,10 @@ + + /// Texture stuff + GLenum textureTarget; +- unsigned int textureID; +- unsigned int depthID; ++ GLuint textureID; ++ GLuint depthID; + +- unsigned int framebufferID; ++ GLuint framebufferID; + + bool initialized; + }; +diff -ru OpenCSG-1.1.1/src/occlusionQuery.cpp OpenCSG-1.1.1-mac/src/occlusionQuery.cpp +--- OpenCSG-1.1.1/src/occlusionQuery.cpp 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/occlusionQuery.cpp 2009-12-09 03:15:26.000000000 +0100 +@@ -57,7 +57,7 @@ + } + + unsigned int OcclusionQueryARB::getQueryResult() { +- unsigned int fragmentCount; ++ GLuint fragmentCount; + glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &fragmentCount); + return fragmentCount; + } +@@ -94,7 +94,7 @@ + } + + unsigned int OcclusionQueryNV::getQueryResult() { +- unsigned int fragmentCount; ++ GLuint fragmentCount; + glGetOcclusionQueryuivNV(mQueryObject, GL_PIXEL_COUNT_NV, &fragmentCount); + return fragmentCount; + } +diff -ru OpenCSG-1.1.1/src/openglHelper.cpp OpenCSG-1.1.1-mac/src/openglHelper.cpp +--- OpenCSG-1.1.1/src/openglHelper.cpp 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/openglHelper.cpp 2009-12-09 03:15:26.000000000 +0100 +@@ -29,13 +29,13 @@ + + GLfloat projection[16]; + GLfloat modelview[16]; +- int canvasPos[4]; ++ GLint canvasPos[4]; + +- int stencilBits = 0; ++ GLint stencilBits = 0; + int stencilMax = 0; + int stencilMask = 0; + +- int scissorPos[4]; ++ GLint scissorPos[4]; + + void scissor(const PCArea& area) { + const int dx = area.maxx - area.minx; +diff -ru OpenCSG-1.1.1/src/openglHelper.h OpenCSG-1.1.1-mac/src/openglHelper.h +--- OpenCSG-1.1.1/src/openglHelper.h 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/openglHelper.h 2009-12-09 03:15:26.000000000 +0100 +@@ -36,17 +36,17 @@ + // copy of the projection matrix during CSG computation + extern GLfloat modelview[16]; + // copy of the modelview matrix during CSG computation +- extern int canvasPos[4]; ++ extern GLint canvasPos[4]; + // copy of the viewport size during CSG computation + +- extern int stencilBits; ++ extern GLint stencilBits; + // number of stencil bits in the pbuffer + extern int stencilMax; + // the number where the stencil value would "wrap around" to zero + extern int stencilMask; + // stencilMax - 1 + +- extern int scissorPos[4]; ++ extern GLint scissorPos[4]; + // copy of the scissor settings for CSG computation + + void scissor(const PCArea& area); +diff -ru OpenCSG-1.1.1/src/pBufferTexture.h OpenCSG-1.1.1-mac/src/pBufferTexture.h +--- OpenCSG-1.1.1/src/pBufferTexture.h 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/pBufferTexture.h 2009-12-09 03:15:34.000000000 +0100 +@@ -22,7 +22,7 @@ + + #ifndef __OpenCSG__pbuffer_texture_h__ + #define __OpenCSG__pbuffer_texture_h__ +- ++#ifndef __APPLE__ + #include "opencsgConfig.h" + #include "offscreenBuffer.h" + +@@ -81,5 +81,26 @@ + } // namespace OpenGL + + } // namespace OpenCSG ++#else ++ ++namespace OpenCSG { ++ namespace OpenGL { ++ class PBufferTexture : public OffscreenBuffer { ++ virtual bool Initialize(int , int , bool , bool ) {return false;} ++ virtual bool Reset() {return false;} ++ virtual bool Resize(int, int) {return false;} ++ virtual bool BeginCapture() {return false;} ++ virtual bool EndCapture() {return false;} ++ virtual void Bind() const {} ++ virtual void EnableTextureTarget() const {} ++ virtual void DisableTextureTarget() const {} ++ virtual unsigned int GetTextureTarget() const {return 0;} ++ virtual int GetWidth() const {return 0;} ++ virtual int GetHeight() const {return 0;} ++ virtual bool haveSeparateContext() const {return false;} ++ }; ++ } ++} + ++#endif // __APPLE__ + #endif // __OpenCSG__frame_buffer_object_h__ +Only in OpenCSG-1.1.1-mac/src: pBufferTexture.h~ +diff -ru OpenCSG-1.1.1/src/src.pro OpenCSG-1.1.1-mac/src/src.pro +--- OpenCSG-1.1.1/src/src.pro 2009-07-19 21:05:09.000000000 +0200 ++++ OpenCSG-1.1.1-mac/src/src.pro 2009-12-09 03:15:26.000000000 +0100 +@@ -4,7 +4,15 @@ + DESTDIR = ../lib + + CONFIG += opengl warn_on release +-INCLUDEPATH += ../include ../glew/include ../ ++INCLUDEPATH += ../include ../ ++ ++macx { ++ INCLUDEPATH += /opt/local/include ++ LIBS += -L/opt/local/lib -lglew ++} ++else { ++INCLUDEPATH += ../glew/include ++} + + HEADERS = ../include/opencsg.h \ + opencsgConfig.h \ +@@ -16,12 +24,11 @@ + offscreenBuffer.h \ + opencsgRender.h \ + openglHelper.h \ +- pBufferTexture.h \ + primitiveHelper.h \ + scissorMemo.h \ + settings.h \ +- stencilManager.h \ +- ../RenderTexture/RenderTexture.h ++ stencilManager.h ++ + SOURCES = area.cpp \ + batch.cpp \ + channelManager.cpp \ +@@ -30,12 +37,19 @@ + offscreenBuffer.cpp \ + opencsgRender.cpp \ + openglHelper.cpp \ +- pBufferTexture.cpp \ + primitive.cpp \ + primitiveHelper.cpp \ + renderGoldfeather.cpp \ + renderSCS.cpp \ + scissorMemo.cpp \ + settings.cpp \ +- stencilManager.cpp \ +- ../RenderTexture/RenderTexture.cpp ++ stencilManager.cpp ++ ++!macx { ++ HEADERS += ../RenderTexture/RenderTexture.h \ ++ pBufferTexture.h ++ ++ SOURCES += ../RenderTexture/RenderTexture.cpp \ ++ pBufferTexture.cpp ++} ++ diff --git a/patches/OpenCSG-1.2.0-MacOSX-port.patch b/patches/OpenCSG-1.2.0-MacOSX-port.patch new file mode 100644 index 0000000..3f1bdc4 --- /dev/null +++ b/patches/OpenCSG-1.2.0-MacOSX-port.patch @@ -0,0 +1,285 @@ +diff -ru OpenCSG-1.2.0/RenderTexture/RenderTexture.h OpenCSG-1.2.0-mac/RenderTexture/RenderTexture.h +--- OpenCSG-1.2.0/RenderTexture/RenderTexture.h 2010-01-02 20:56:12.000000000 +0100 ++++ OpenCSG-1.2.0-mac/RenderTexture/RenderTexture.h 2010-01-24 23:30:24.000000000 +0100 +@@ -294,8 +294,8 @@ + bool _BindDepthBuffer( ) const; + + protected: // data +- int _iWidth; // width of the pbuffer +- int _iHeight; // height of the pbuffer ++ GLint _iWidth; // width of the pbuffer ++ GLint _iHeight; // height of the pbuffer + + bool _bIsTexture; + bool _bIsDepthTexture; +@@ -342,8 +342,8 @@ + + // Texture stuff + GLenum _iTextureTarget; +- unsigned int _iTextureID; +- unsigned int _iDepthTextureID; ++ GLuint _iTextureID; ++ GLuint _iDepthTextureID; + + unsigned short* _pPoorDepthTexture; // [Redge] + +diff -ru OpenCSG-1.2.0/example/example.pro OpenCSG-1.2.0-mac/example/example.pro +--- OpenCSG-1.2.0/example/example.pro 2010-01-02 20:56:12.000000000 +0100 ++++ OpenCSG-1.2.0-mac/example/example.pro 2010-01-24 23:30:24.000000000 +0100 +@@ -2,9 +2,16 @@ + TARGET = opencsgexample + + CONFIG += opengl warn_on release +-INCLUDEPATH += ../glew/include ../include +- +-LIBS += -L../lib -lopencsg -lglut -L../glew/lib -lGLEW ++INCLUDEPATH += ../include ++LIBS += -L../lib -lopencsg -lGLEW ++macx { ++ INCLUDEPATH += /opt/local/include ++ LIBS += -framework GLUT -L/opt/local/lib ++} ++else { ++ INCLUDEPATH += ../glew/include ++ LIBS += -lglut -L../glew/lib ++} + + HEADERS = displaylistPrimitive.h + SOURCES = displaylistPrimitive.cpp main.cpp +diff -ru OpenCSG-1.2.0/example/main.cpp OpenCSG-1.2.0-mac/example/main.cpp +--- OpenCSG-1.2.0/example/main.cpp 2010-01-02 21:03:19.000000000 +0100 ++++ OpenCSG-1.2.0-mac/example/main.cpp 2010-01-24 23:30:24.000000000 +0100 +@@ -22,7 +22,11 @@ + // + + #include ++#ifdef __APPLE__ ++#include ++#else + #include ++#endif + #include + #include "displaylistPrimitive.h" + #include +diff -ru OpenCSG-1.2.0/opencsg.pro OpenCSG-1.2.0-mac/opencsg.pro +--- OpenCSG-1.2.0/opencsg.pro 2010-01-02 20:56:12.000000000 +0100 ++++ OpenCSG-1.2.0-mac/opencsg.pro 2010-01-24 23:30:24.000000000 +0100 +@@ -1,2 +1,4 @@ + TEMPLATE = subdirs + SUBDIRS = src example ++# On Mac we get glew from MacPorts ++!macx:SUBDIRS += glew +diff -ru OpenCSG-1.2.0/src/channelManager.cpp OpenCSG-1.2.0-mac/src/channelManager.cpp +--- OpenCSG-1.2.0/src/channelManager.cpp 2010-01-02 21:03:04.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/channelManager.cpp 2010-01-24 23:30:24.000000000 +0100 +@@ -23,7 +23,7 @@ + #include + #ifdef _WIN32 + #include +-#else ++#elif !defined(__APPLE__) + #include + #endif + +@@ -170,9 +170,11 @@ + #ifdef WIN32 + if ( WGLEW_ARB_pbuffer + && WGLEW_ARB_pixel_format +-#else ++#elif !defined(__APPLE__) + if ( GLXEW_SGIX_pbuffer + && GLXEW_SGIX_fbconfig ++#else ++ if ( false + #endif + ) { + newOffscreenType = OpenCSG::PBuffer; +Only in OpenCSG-1.2.0-mac/src: channelManager.cpp.orig +diff -ru OpenCSG-1.2.0/src/frameBufferObject.h OpenCSG-1.2.0-mac/src/frameBufferObject.h +--- OpenCSG-1.2.0/src/frameBufferObject.h 2010-01-02 21:03:01.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/frameBufferObject.h 2010-01-24 23:30:24.000000000 +0100 +@@ -77,10 +77,10 @@ + + /// Texture stuff + GLenum textureTarget; +- unsigned int textureID; +- unsigned int depthID; ++ GLuint textureID; ++ GLuint depthID; + +- unsigned int framebufferID; ++ GLuint framebufferID; + + bool initialized; + }; +Only in OpenCSG-1.2.0-mac/src: frameBufferObject.h.orig +diff -ru OpenCSG-1.2.0/src/occlusionQuery.cpp OpenCSG-1.2.0-mac/src/occlusionQuery.cpp +--- OpenCSG-1.2.0/src/occlusionQuery.cpp 2010-01-02 21:03:04.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/occlusionQuery.cpp 2010-01-24 23:30:24.000000000 +0100 +@@ -57,7 +57,7 @@ + } + + unsigned int OcclusionQueryARB::getQueryResult() { +- unsigned int fragmentCount; ++ GLuint fragmentCount; + glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &fragmentCount); + return fragmentCount; + } +@@ -94,7 +94,7 @@ + } + + unsigned int OcclusionQueryNV::getQueryResult() { +- unsigned int fragmentCount; ++ GLuint fragmentCount; + glGetOcclusionQueryuivNV(mQueryObject, GL_PIXEL_COUNT_NV, &fragmentCount); + return fragmentCount; + } +diff -ru OpenCSG-1.2.0/src/openglHelper.cpp OpenCSG-1.2.0-mac/src/openglHelper.cpp +--- OpenCSG-1.2.0/src/openglHelper.cpp 2010-01-02 21:03:04.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/openglHelper.cpp 2010-01-24 23:30:24.000000000 +0100 +@@ -29,13 +29,13 @@ + + GLfloat projection[16]; + GLfloat modelview[16]; +- int canvasPos[4]; ++ GLint canvasPos[4]; + +- int stencilBits = 0; ++ GLint stencilBits = 0; + int stencilMax = 0; + int stencilMask = 0; + +- int scissorPos[4]; ++ GLint scissorPos[4]; + + void scissor(const PCArea& area) { + const int dx = area.maxx - area.minx; +diff -ru OpenCSG-1.2.0/src/openglHelper.h OpenCSG-1.2.0-mac/src/openglHelper.h +--- OpenCSG-1.2.0/src/openglHelper.h 2010-01-02 21:03:01.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/openglHelper.h 2010-01-24 23:30:24.000000000 +0100 +@@ -36,17 +36,17 @@ + // copy of the projection matrix during CSG computation + extern GLfloat modelview[16]; + // copy of the modelview matrix during CSG computation +- extern int canvasPos[4]; ++ extern GLint canvasPos[4]; + // copy of the viewport size during CSG computation + +- extern int stencilBits; ++ extern GLint stencilBits; + // number of stencil bits in the pbuffer + extern int stencilMax; + // the number where the stencil value would "wrap around" to zero + extern int stencilMask; + // stencilMax - 1 + +- extern int scissorPos[4]; ++ extern GLint scissorPos[4]; + // copy of the scissor settings for CSG computation + + void scissor(const PCArea& area); +diff -ru OpenCSG-1.2.0/src/pBufferTexture.h OpenCSG-1.2.0-mac/src/pBufferTexture.h +--- OpenCSG-1.2.0/src/pBufferTexture.h 2010-01-02 21:03:01.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/pBufferTexture.h 2010-01-24 23:30:24.000000000 +0100 +@@ -22,7 +22,7 @@ + + #ifndef __OpenCSG__pbuffer_texture_h__ + #define __OpenCSG__pbuffer_texture_h__ +- ++#ifndef __APPLE__ + #include "opencsgConfig.h" + #include "offscreenBuffer.h" + +@@ -81,5 +81,26 @@ + } // namespace OpenGL + + } // namespace OpenCSG ++#else ++ ++namespace OpenCSG { ++ namespace OpenGL { ++ class PBufferTexture : public OffscreenBuffer { ++ virtual bool Initialize(int , int , bool , bool ) {return false;} ++ virtual bool Reset() {return false;} ++ virtual bool Resize(int, int) {return false;} ++ virtual bool BeginCapture() {return false;} ++ virtual bool EndCapture() {return false;} ++ virtual void Bind() const {} ++ virtual void EnableTextureTarget() const {} ++ virtual void DisableTextureTarget() const {} ++ virtual unsigned int GetTextureTarget() const {return 0;} ++ virtual int GetWidth() const {return 0;} ++ virtual int GetHeight() const {return 0;} ++ virtual bool haveSeparateContext() const {return false;} ++ }; ++ } ++} + ++#endif // __APPLE__ + #endif // __OpenCSG__frame_buffer_object_h__ +diff -ru OpenCSG-1.2.0/src/src.pro OpenCSG-1.2.0-mac/src/src.pro +--- OpenCSG-1.2.0/src/src.pro 2010-01-02 20:56:12.000000000 +0100 ++++ OpenCSG-1.2.0-mac/src/src.pro 2010-01-24 23:31:57.000000000 +0100 +@@ -1,10 +1,19 @@ + TEMPLATE = lib + TARGET = opencsg + VERSION = 1.2.0 +-DESTDIR = ../lib ++DESTDIR = $$(PWD)/lib + + CONFIG += opengl warn_on release +-INCLUDEPATH += ../include ../glew/include ../ ++INCLUDEPATH += ../include ../ ++ ++macx { ++ INCLUDEPATH += /opt/local/include ++ LIBS += -L/opt/local/lib -lglew ++ CONFIG += absolute_library_soname ++} ++else { ++INCLUDEPATH += ../glew/include ++} + + HEADERS = ../include/opencsg.h \ + opencsgConfig.h \ +@@ -17,12 +26,11 @@ + offscreenBuffer.h \ + opencsgRender.h \ + openglHelper.h \ +- pBufferTexture.h \ + primitiveHelper.h \ + scissorMemo.h \ + settings.h \ +- stencilManager.h \ +- ../RenderTexture/RenderTexture.h ++ stencilManager.h ++ + SOURCES = area.cpp \ + batch.cpp \ + channelManager.cpp \ +@@ -32,12 +40,21 @@ + offscreenBuffer.cpp \ + opencsgRender.cpp \ + openglHelper.cpp \ +- pBufferTexture.cpp \ + primitive.cpp \ + primitiveHelper.cpp \ + renderGoldfeather.cpp \ + renderSCS.cpp \ + scissorMemo.cpp \ + settings.cpp \ +- stencilManager.cpp \ +- ../RenderTexture/RenderTexture.cpp ++ stencilManager.cpp ++ ++!macx { ++ HEADERS += ../RenderTexture/RenderTexture.h \ ++ pBufferTexture.h ++ ++ SOURCES += ../RenderTexture/RenderTexture.cpp \ ++ pBufferTexture.cpp ++} ++ ++INSTALLS += target ++target.path = $$DESTDIR +Only in OpenCSG-1.2.0-mac/src: src.pro.orig diff --git a/patches/OpenCSG-1.2.0-Reset-Hack.patch b/patches/OpenCSG-1.2.0-Reset-Hack.patch new file mode 100644 index 0000000..7254ce7 --- /dev/null +++ b/patches/OpenCSG-1.2.0-Reset-Hack.patch @@ -0,0 +1,89 @@ +diff -ru OpenCSG-1.2.0/include/opencsg.h OpenCSG-1.2.0-reset/include/opencsg.h +--- OpenCSG-1.2.0/include/opencsg.h 2010-01-02 21:04:10.000000000 +0100 ++++ OpenCSG-1.2.0-reset/include/opencsg.h 2010-01-03 00:41:30.000000000 +0100 +@@ -229,6 +229,9 @@ + Algorithm, + DepthComplexityAlgorithm = NoDepthComplexitySampling); + ++ // call this function whenever switching the OpenGL context ++ void reset(); ++ + } // namespace OpenCSG + + #endif // __OpenCSG__opencsg_h__ +Only in OpenCSG-1.2.0-reset/include: opencsg.h~ +diff -ru OpenCSG-1.2.0/src/channelManager.h OpenCSG-1.2.0-reset/src/channelManager.h +--- OpenCSG-1.2.0/src/channelManager.h 2010-01-02 21:03:01.000000000 +0100 ++++ OpenCSG-1.2.0-reset/src/channelManager.h 2010-01-03 00:40:53.000000000 +0100 +@@ -79,9 +79,9 @@ + /// moved into alpha, to allow alpha testing of the channel. + static void setupTexEnv(Channel channel); + +- private: +- + static OpenGL::OffscreenBuffer* gOffscreenBuffer; ++ ++ private: + static int gOffscreenType; + static bool gInUse; + +Only in OpenCSG-1.2.0-reset/src: channelManager.h~ +diff -ru OpenCSG-1.2.0/src/offscreenBuffer.cpp OpenCSG-1.2.0-reset/src/offscreenBuffer.cpp +--- OpenCSG-1.2.0/src/offscreenBuffer.cpp 2010-01-02 21:03:04.000000000 +0100 ++++ OpenCSG-1.2.0-reset/src/offscreenBuffer.cpp 2010-01-03 00:41:28.000000000 +0100 +@@ -24,9 +24,22 @@ + #include "frameBufferObjectExt.h" + #include "pBufferTexture.h" + #include ++#include "channelManager.h" ++ ++static bool reset_fARB = false; ++static bool reset_fEXT = false; ++static bool reset_p = false; + + namespace OpenCSG { + ++ void reset() ++ { ++ reset_fARB = true; ++ reset_fEXT = true; ++ reset_p = true; ++ OpenCSG::ChannelManager::gOffscreenBuffer = NULL; ++ } ++ + namespace OpenGL { + + OffscreenBuffer* getOffscreenBuffer(bool fbo) { +@@ -36,19 +49,28 @@ + + if (fbo) { + if (GLEW_ARB_framebuffer_object) { +- if (!fARB) ++ if (reset_fARB || !fARB) { ++ delete fARB; + fARB = new FrameBufferObject; ++ reset_fARB = false; ++ } + return fARB; + } + else { +- if (!fEXT) ++ if (reset_fEXT || !fEXT) { ++ delete fEXT; + fEXT = new FrameBufferObjectExt; ++ reset_fEXT = false; ++ } + return fEXT; + } + } + else { +- if (!p) ++ if (reset_p || !p) { ++ delete p; + p = new PBufferTexture; ++ reset_p = false; ++ } + return p; + } + } +Only in OpenCSG-1.2.0-reset/src: offscreenBuffer.cpp~ diff --git a/polyset.cc b/polyset.cc deleted file mode 100644 index 5f22fde..0000000 --- a/polyset.cc +++ /dev/null @@ -1,706 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" -#include "Preferences.h" - -QCache PolySet::ps_cache(100); - -PolySet::ps_cache_entry::ps_cache_entry(PolySet *ps) : - ps(ps), msg(print_messages_stack.last()) { } - -PolySet::ps_cache_entry::~ps_cache_entry() { - ps->unlink(); -} - -PolySet::PolySet() : grid(GRID_FINE) -{ - is2d = false; - convexity = 1; - refcount = 1; -} - -PolySet::~PolySet() -{ - assert(refcount == 0); -} - -PolySet* PolySet::link() -{ - refcount++; - return this; -} - -void PolySet::unlink() -{ - if (--refcount == 0) - delete this; -} - -void PolySet::append_poly() -{ - polygons.append(Polygon()); -} - -void PolySet::append_vertex(double x, double y, double z) -{ - grid.align(x, y, z); - polygons.last().append(Point(x, y, z)); -} - -void PolySet::insert_vertex(double x, double y, double z) -{ - grid.align(x, y, z); - polygons.last().insert(0, Point(x, y, z)); -} - -static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const PolySet::Point *p1, const PolySet::Point *p2, bool e0, bool e1, bool e2, double z, bool mirrored) -{ - double ax = p1->x - p0->x, bx = p1->x - p2->x; - double ay = p1->y - p0->y, by = p1->y - p2->y; - double az = p1->z - p0->z, bz = p1->z - p2->z; - double nx = ay*bz - az*by; - double ny = az*bx - ax*bz; - double nz = ax*by - ay*bx; - double nl = sqrt(nx*nx + ny*ny + nz*nz); - glNormal3d(nx / nl, ny / nl, nz / nl); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - double e0f = e0 ? 2.0 : -1.0; - double e1f = e1 ? 2.0 : -1.0; - double e2f = e2 ? 2.0 : -1.0; - glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p1->x, p1->y, p1->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); - glVertexAttrib3d(shaderinfo[6], 0.0, 1.0, 0.0); - glVertex3d(p0->x, p0->y, p0->z + z); - if (!mirrored) { - glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); - glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); - glVertex3d(p1->x, p1->y, p1->z + z); - } - glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p1->x, p1->y, p1->z + z); - glVertexAttrib3d(shaderinfo[6], 1.0, 0.0, 0.0); - glVertex3d(p2->x, p2->y, p2->z + z); - if (mirrored) { - glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); - glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); - glVertex3d(p1->x, p1->y, p1->z + z); - } - } - else -#endif - { - glVertex3d(p0->x, p0->y, p0->z + z); - if (!mirrored) - glVertex3d(p1->x, p1->y, p1->z + z); - glVertex3d(p2->x, p2->y, p2->z + z); - if (mirrored) - glVertex3d(p1->x, p1->y, p1->z + z); - } -} - -void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo) const -{ - double m_scale_rotate_det = - m[0]*m[5]*m[10] + m[4]*m[9]*m[2] + m[8]*m[1]*m[6] - - (m[8]*m[5]*m[2] + m[4]*m[1]*m[10] + m[0]*m[9]*m[6]); - bool mirrored = m_scale_rotate_det < 0; - - if (colormode == COLORMODE_MATERIAL) { - const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); - glColor3f(col.redF(), col.greenF(), col.blueF()); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], col.redF(), col.greenF(), col.blueF(), 1.0f); - glUniform4f(shaderinfo[2], 255 / 255.0, 236 / 255.0, 94 / 255.0, 1.0); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_CUTOUT) { - const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR); - glColor3f(col.redF(), col.greenF(), col.blueF()); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 157 / 255.0, 203 / 255.0, 81 / 255.0, 1.0); - glUniform4f(shaderinfo[2], 171 / 255.0, 216 / 255.0, 86 / 255.0, 1.0); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_HIGHLIGHT) { - glColor4ub(255, 157, 81, 128); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 255 / 255.0, 157 / 255.0, 81 / 255.0, 0.5); - glUniform4f(shaderinfo[2], 255 / 255.0, 171 / 255.0, 86 / 255.0, 0.5); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_BACKGROUND) { - glColor4ub(180, 180, 180, 128); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 180 / 255.0, 180 / 255.0, 180 / 255.0, 0.5); - glUniform4f(shaderinfo[2], 150 / 255.0, 150 / 255.0, 150 / 255.0, 0.5); - } -#endif /* ENABLE_OPENCSG */ - } -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform1f(shaderinfo[7], shaderinfo[9]); - glUniform1f(shaderinfo[8], shaderinfo[10]); - } -#endif /* ENABLE_OPENCSG */ - if (this->is2d) { - double zbase = csgmode; - glBegin(GL_TRIANGLES); - for (double z = -zbase/2; z < zbase; z += zbase) - { - for (int i = 0; i < polygons.size(); i++) { - const Polygon *poly = &polygons[i]; - if (poly->size() == 3) { - if (z < 0) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(2), &poly->at(1), true, true, true, z, mirrored); - } else { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, z, mirrored); - } - } - else if (poly->size() == 4) { - if (z < 0) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); - } else { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); - } - } - else { - Point center; - for (int j = 0; j < poly->size(); j++) { - center.x += poly->at(j).x; - center.y += poly->at(j).y; - } - center.x /= poly->size(); - center.y /= poly->size(); - for (int j = 1; j <= poly->size(); j++) { - if (z < 0) { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j % poly->size()), &poly->at(j - 1), - false, true, false, z, mirrored); - } else { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), - false, true, false, z, mirrored); - } - } - } - } - } - const QVector *borders_p = &borders; - if (borders_p->size() == 0) - borders_p = &polygons; - for (int i = 0; i < borders_p->size(); i++) { - const Polygon *poly = &borders_p->at(i); - for (int j = 1; j <= poly->size(); j++) { - Point p1 = poly->at(j - 1), p2 = poly->at(j - 1); - Point p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size()); - p1.z -= zbase/2, p2.z += zbase/2; - p3.z -= zbase/2, p4.z += zbase/2; - gl_draw_triangle(shaderinfo, &p2, &p1, &p3, true, true, false, 0, mirrored); - gl_draw_triangle(shaderinfo, &p2, &p3, &p4, false, true, true, 0, mirrored); - } - } - glEnd(); - } else { - for (int i = 0; i < polygons.size(); i++) { - const Polygon *poly = &polygons[i]; - glBegin(GL_TRIANGLES); - if (poly->size() == 3) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, 0, mirrored); - } - else if (poly->size() == 4) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, 0, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, 0, mirrored); - } - else { - Point center; - for (int j = 0; j < poly->size(); j++) { - center.x += poly->at(j).x; - center.y += poly->at(j).y; - center.z += poly->at(j).z; - } - center.x /= poly->size(); - center.y /= poly->size(); - center.z /= poly->size(); - for (int j = 1; j <= poly->size(); j++) { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), false, true, false, 0, mirrored); - } - } - glEnd(); - } - } -} - -void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const -{ - if (colormode == COLORMODE_MATERIAL) - glColor3ub(255, 236, 94); - if (colormode == COLORMODE_CUTOUT) - glColor3ub(171, 216, 86); - if (colormode == COLORMODE_HIGHLIGHT) - glColor4ub(255, 171, 86, 128); - if (colormode == COLORMODE_BACKGROUND) - glColor4ub(150, 150, 150, 128); - if (this->is2d) { - double zbase = csgmode; - for (double z = -zbase/2; z < zbase; z += zbase) - { - for (int i = 0; i < borders.size(); i++) { - const Polygon *poly = &borders[i]; - glBegin(GL_LINE_LOOP); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, z); - } - glEnd(); - } - } - for (int i = 0; i < borders.size(); i++) { - const Polygon *poly = &borders[i]; - glBegin(GL_LINES); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, -zbase/2); - glVertex3d(p->x, p->y, +zbase/2); - } - glEnd(); - } - } else { - for (int i = 0; i < polygons.size(); i++) { - const Polygon *poly = &polygons[i]; - glBegin(GL_LINE_LOOP); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, p->z); - } - glEnd(); - } - } -} - -#ifdef ENABLE_CGAL - -#undef GEN_SURFACE_DEBUG - -class CGAL_Build_PolySet : public CGAL::Modifier_base -{ -public: - typedef CGAL_HDS::Vertex::Point Point; - - const PolySet *ps; - CGAL_Build_PolySet(const PolySet *ps) : ps(ps) { } - - void operator()(CGAL_HDS& hds) - { - CGAL_Polybuilder B(hds, true); - - QList vertices; - Grid3d vertices_idx(GRID_FINE); - - for (int i = 0; i < ps->polygons.size(); i++) { - const PolySet::Polygon *poly = &ps->polygons[i]; - for (int j = 0; j < poly->size(); j++) { - const PolySet::Point *p = &poly->at(j); - if (!vertices_idx.has(p->x, p->y, p->z)) { - vertices_idx.data(p->x, p->y, p->z) = vertices.size(); - vertices.append(*p); - } - } - } - - B.begin_surface(vertices.size(), ps->polygons.size()); -#ifdef GEN_SURFACE_DEBUG - printf("=== CGAL Surface ===\n"); -#endif - - for (int i = 0; i < vertices.size(); i++) { - const PolySet::Point *p = &vertices[i]; - B.add_vertex(Point(p->x, p->y, p->z)); -#ifdef GEN_SURFACE_DEBUG - printf("%d: %f %f %f\n", i, p->x, p->y, p->z); -#endif - } - - for (int i = 0; i < ps->polygons.size(); i++) { - const PolySet::Polygon *poly = &ps->polygons[i]; - QHash fc; - bool facet_is_degenerated = false; - for (int j = 0; j < poly->size(); j++) { - const PolySet::Point *p = &poly->at(j); - int v = vertices_idx.data(p->x, p->y, p->z); - if (fc[v]++ > 0) - facet_is_degenerated = true; - } - - if (!facet_is_degenerated) - B.begin_facet(); -#ifdef GEN_SURFACE_DEBUG - printf("F:"); -#endif - for (int j = 0; j < poly->size(); j++) { - const PolySet::Point *p = &poly->at(j); -#ifdef GEN_SURFACE_DEBUG - printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z); -#endif - if (!facet_is_degenerated) - B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z)); - } -#ifdef GEN_SURFACE_DEBUG - if (facet_is_degenerated) - printf(" (degenerated)"); - printf("\n"); -#endif - if (!facet_is_degenerated) - B.end_facet(); - } - -#ifdef GEN_SURFACE_DEBUG - printf("====================\n"); -#endif - B.end_surface(); - - #undef PointKey - } -}; - -CGAL_Nef_polyhedron PolySet::render_cgal_nef_polyhedron() const -{ - if (this->is2d) - { -#if 0 - // This version of the code causes problems in some cases. - // Example testcase: import_dxf("testdata/polygon8.dxf"); - // - typedef std::list point_list_t; - typedef point_list_t::iterator point_list_it; - std::list< point_list_t > pdata_point_lists; - std::list < std::pair < point_list_it, point_list_it > > pdata; - Grid2d grid(GRID_COARSE); - - for (int i = 0; i < this->polygons.size(); i++) { - pdata_point_lists.push_back(point_list_t()); - for (int j = 0; j < this->polygons[i].size(); j++) { - double x = this->polygons[i][j].x; - double y = this->polygons[i][j].y; - CGAL_Nef_polyhedron2::Point p; - if (grid.has(x, y)) { - p = grid.data(x, y); - } else { - p = CGAL_Nef_polyhedron2::Point(x, y); - grid.data(x, y) = p; - } - pdata_point_lists.back().push_back(p); - } - pdata.push_back(std::make_pair(pdata_point_lists.back().begin(), - pdata_point_lists.back().end())); - } - - CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS); - return CGAL_Nef_polyhedron(N); -#endif -#if 0 - // This version of the code works fine but is pretty slow. - // - CGAL_Nef_polyhedron2 N; - Grid2d grid(GRID_COARSE); - - for (int i = 0; i < this->polygons.size(); i++) { - std::list plist; - for (int j = 0; j < this->polygons[i].size(); j++) { - double x = this->polygons[i][j].x; - double y = this->polygons[i][j].y; - CGAL_Nef_polyhedron2::Point p; - if (grid.has(x, y)) { - p = grid.data(x, y); - } else { - p = CGAL_Nef_polyhedron2::Point(x, y); - grid.data(x, y) = p; - } - plist.push_back(p); - } - N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); - } - - return CGAL_Nef_polyhedron(N); -#endif -#if 1 - // This version of the code does essentially the same thing as the 2nd - // version but merges some triangles before sending them to CGAL. This adds - // complexity but speeds up things.. - // - struct PolyReducer - { - Grid2d grid; - QHash< QPair, QPair > egde_to_poly; - QHash< int, CGAL_Nef_polyhedron2::Point > points; - QHash< int, QList > polygons; - int poly_n; - - void add_edges(int pn) - { - for (int j = 1; j <= this->polygons[pn].size(); j++) { - int a = this->polygons[pn][j-1]; - int b = this->polygons[pn][j % this->polygons[pn].size()]; - if (a > b) { a = a^b; b = a^b; a = a^b; } - if (this->egde_to_poly[QPair(a, b)].first == 0) - this->egde_to_poly[QPair(a, b)].first = pn; - else if (this->egde_to_poly[QPair(a, b)].second == 0) - this->egde_to_poly[QPair(a, b)].second = pn; - else - abort(); - } - } - - void del_poly(int pn) - { - for (int j = 1; j <= this->polygons[pn].size(); j++) { - int a = this->polygons[pn][j-1]; - int b = this->polygons[pn][j % this->polygons[pn].size()]; - if (a > b) { a = a^b; b = a^b; a = a^b; } - if (this->egde_to_poly[QPair(a, b)].first == pn) - this->egde_to_poly[QPair(a, b)].first = 0; - if (this->egde_to_poly[QPair(a, b)].second == pn) - this->egde_to_poly[QPair(a, b)].second = 0; - } - this->polygons.remove(pn); - } - - PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1) - { - int point_n = 1; - for (int i = 0; i < ps->polygons.size(); i++) { - for (int j = 0; j < ps->polygons[i].size(); j++) { - double x = ps->polygons[i][j].x; - double y = ps->polygons[i][j].y; - if (this->grid.has(x, y)) { - this->polygons[this->poly_n].append(this->grid.data(x, y)); - } else { - this->grid.align(x, y) = point_n; - this->polygons[this->poly_n].append(point_n); - this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y); - point_n++; - } - } - add_edges(this->poly_n); - this->poly_n++; - } - } - - int merge(int p1, int p1e, int p2, int p2e) - { - for (int i = 1; i < this->polygons[p1].size(); i++) { - int j = (p1e + i) % this->polygons[p1].size(); - this->polygons[this->poly_n].append(this->polygons[p1][j]); - } - for (int i = 1; i < this->polygons[p2].size(); i++) { - int j = (p2e + i) % this->polygons[p2].size(); - this->polygons[this->poly_n].append(this->polygons[p2][j]); - } - del_poly(p1); - del_poly(p2); - add_edges(this->poly_n); - return this->poly_n++; - } - - void reduce() - { - QList work_queue; - QHashIterator< int, QList > it(polygons); - while (it.hasNext()) { - it.next(); - work_queue.append(it.key()); - } - while (!work_queue.isEmpty()) { - int poly1_n = work_queue.first(); - work_queue.removeFirst(); - if (!this->polygons.contains(poly1_n)) - continue; - for (int j = 1; j <= this->polygons[poly1_n].size(); j++) { - int a = this->polygons[poly1_n][j-1]; - int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()]; - if (a > b) { a = a^b; b = a^b; a = a^b; } - if (this->egde_to_poly[QPair(a, b)].first != 0 && - this->egde_to_poly[QPair(a, b)].second != 0) { - int poly2_n = this->egde_to_poly[QPair(a, b)].first + - this->egde_to_poly[QPair(a, b)].second - poly1_n; - int poly2_edge = -1; - for (int k = 1; k <= this->polygons[poly2_n].size(); k++) { - int c = this->polygons[poly2_n][k-1]; - int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()]; - if (c > d) { c = c^d; d = c^d; c = c^d; } - if (a == c && b == d) { - poly2_edge = k-1; - continue; - } - int poly3_n = this->egde_to_poly[QPair(c, d)].first + - this->egde_to_poly[QPair(c, d)].second - poly2_n; - if (poly3_n < 0) - continue; - if (poly3_n == poly1_n) - goto next_poly1_edge; - } - work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge)); - goto next_poly1; - } - next_poly1_edge:; - } - next_poly1:; - } - } - - CGAL_Nef_polyhedron2 toNef() - { - CGAL_Nef_polyhedron2 N; - - QHashIterator< int, QList > it(polygons); - while (it.hasNext()) { - it.next(); - std::list plist; - for (int j = 0; j < it.value().size(); j++) { - int p = it.value()[j]; - plist.push_back(points[p]); - } - N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); - } - - return N; - } - }; - - PolyReducer pr(this); - // printf("Number of polygons before reduction: %d\n", pr.polygons.size()); - pr.reduce(); - // printf("Number of polygons after reduction: %d\n", pr.polygons.size()); - return CGAL_Nef_polyhedron(pr.toNef()); -#endif -#if 0 - // This is another experimental version. I should run faster than the above, - // is a lot simpler and has only one known weakness: Degenerate polygons, which - // get repaired by GLUTess, might trigger a CGAL crash here. The only - // known case for this is triangle-with-duplicate-vertex.dxf - if (this->polygons.size() > 0) assert(this->borders.size() > 0); - CGAL_Nef_polyhedron2 N; - Grid2d grid(GRID_COARSE); - - for (int i = 0; i < this->borders.size(); i++) { - std::list plist; - for (int j = 0; j < this->borders[i].size(); j++) { - double x = this->borders[i][j].x; - double y = this->borders[i][j].y; - CGAL_Nef_polyhedron2::Point p; - if (grid.has(x, y)) { - p = grid.data(x, y); - } else { - p = CGAL_Nef_polyhedron2::Point(x, y); - grid.data(x, y) = p; - } - plist.push_back(p); - } - // FIXME: If a border (path) has a duplicate vertex in dxf, - // the CGAL_Nef_polyhedron2 constructor will crash. - N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); - } - - return CGAL_Nef_polyhedron(N); - -#endif - } - else - { - CGAL_Polyhedron P; - CGAL_Build_PolySet builder(this); - P.delegate(builder); -#if 0 - std::cout << P; -#endif - CGAL_Nef_polyhedron3 N(P); - return CGAL_Nef_polyhedron(N); - } - return CGAL_Nef_polyhedron(); -} - -#endif /* ENABLE_CGAL */ - -PolySet *AbstractPolyNode::render_polyset(render_mode_e) const -{ - return NULL; -} - -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron AbstractPolyNode::render_cgal_nef_polyhedron() const -{ - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) { - progress_report(); - PRINT(cgal_nef_cache[cache_id]->msg); - return cgal_nef_cache[cache_id]->N; - } - - print_messages_push(); - - PolySet *ps = render_polyset(RENDER_CGAL); - CGAL_Nef_polyhedron N = ps->render_cgal_nef_polyhedron(); - - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - print_messages_pop(); - progress_report(); - - ps->unlink(); - return N; -} - -#endif /* ENABLE_CGAL */ - -CSGTerm *AbstractPolyNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - PolySet *ps = render_polyset(RENDER_OPENCSG); - return render_csg_term_from_ps(m, highlights, background, ps, modinst, idx); -} - -CSGTerm *AbstractPolyNode::render_csg_term_from_ps(double m[20], QVector *highlights, QVector *background, PolySet *ps, const ModuleInstantiation *modinst, int idx) -{ - CSGTerm *t = new CSGTerm(ps, m, QString("n%1").arg(idx)); - if (modinst->tag_highlight && highlights) - highlights->append(t->link()); - if (modinst->tag_background && background) { - background->append(t); - return NULL; - } - return t; -} - diff --git a/prefs3DView.png b/prefs3DView.png deleted file mode 100644 index c6b3d56..0000000 Binary files a/prefs3DView.png and /dev/null differ diff --git a/prefsAdvanced.png b/prefsAdvanced.png deleted file mode 100644 index 333dc2a..0000000 Binary files a/prefsAdvanced.png and /dev/null differ diff --git a/prefsEditor.png b/prefsEditor.png deleted file mode 100644 index e65d9f7..0000000 Binary files a/prefsEditor.png and /dev/null differ diff --git a/primitives.cc b/primitives.cc deleted file mode 100644 index 2f50578..0000000 --- a/primitives.cc +++ /dev/null @@ -1,529 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" - -enum primitive_type_e { - CUBE, - SPHERE, - CYLINDER, - POLYHEDRON, - SQUARE, - CIRCLE, - POLYGON -}; - -class PrimitiveModule : public AbstractModule -{ -public: - primitive_type_e type; - PrimitiveModule(primitive_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class PrimitiveNode : public AbstractPolyNode -{ -public: - bool center; - double x, y, z, h, r1, r2; - double fn, fs, fa; - primitive_type_e type; - int convexity; - Value points, paths, triangles; - PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - PrimitiveNode *node = new PrimitiveNode(inst, type); - - node->center = false; - node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1; - - QVector argnames; - QVector argexpr; - - if (type == CUBE) { - argnames = QVector() << "size" << "center"; - } - if (type == SPHERE) { - argnames = QVector() << "r"; - } - if (type == CYLINDER) { - argnames = QVector() << "h" << "r1" << "r2" << "center"; - } - if (type == POLYHEDRON) { - argnames = QVector() << "points" << "triangles" << "convexity"; - } - if (type == SQUARE) { - argnames = QVector() << "size" << "center"; - } - if (type == CIRCLE) { - argnames = QVector() << "r"; - } - if (type == POLYGON) { - argnames = QVector() << "points" << "paths" << "convexity"; - } - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - node->fn = c.lookup_variable("$fn").num; - node->fs = c.lookup_variable("$fs").num; - node->fa = c.lookup_variable("$fa").num; - - if (type == CUBE) { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getnum(node->x); - size.getnum(node->y); - size.getnum(node->z); - size.getv3(node->x, node->y, node->z); - if (center.type == Value::BOOL) { - node->center = center.b; - } - } - - if (type == SPHERE) { - Value r = c.lookup_variable("r"); - if (r.type == Value::NUMBER) { - node->r1 = r.num; - } - } - - if (type == CYLINDER) { - Value h = c.lookup_variable("h"); - Value r, r1, r2; - r1 = c.lookup_variable("r1"); - r2 = c.lookup_variable("r2"); - if (r1.type != Value::NUMBER && r1.type != Value::NUMBER) - r = c.lookup_variable("r"); - Value center = c.lookup_variable("center"); - if (h.type == Value::NUMBER) { - node->h = h.num; - } - if (r.type == Value::NUMBER) { - node->r1 = r.num; - node->r2 = r.num; - } - if (r1.type == Value::NUMBER) { - node->r1 = r1.num; - } - if (r2.type == Value::NUMBER) { - node->r2 = r2.num; - } - if (center.type == Value::BOOL) { - node->center = center.b; - } - } - - if (type == POLYHEDRON) { - node->points = c.lookup_variable("points"); - node->triangles = c.lookup_variable("triangles"); - } - - if (type == SQUARE) { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getnum(node->x); - size.getnum(node->y); - size.getv2(node->x, node->y); - if (center.type == Value::BOOL) { - node->center = center.b; - } - } - - if (type == CIRCLE) { - Value r = c.lookup_variable("r"); - if (r.type == Value::NUMBER) { - node->r1 = r.num; - } - } - - if (type == POLYGON) { - node->points = c.lookup_variable("points"); - node->paths = c.lookup_variable("paths"); - } - - node->convexity = c.lookup_variable("convexity", true).num; - if (node->convexity < 1) - node->convexity = 1; - - return node; -} - -void register_builtin_primitives() -{ - builtin_modules["cube"] = new PrimitiveModule(CUBE); - builtin_modules["sphere"] = new PrimitiveModule(SPHERE); - builtin_modules["cylinder"] = new PrimitiveModule(CYLINDER); - builtin_modules["polyhedron"] = new PrimitiveModule(POLYHEDRON); - builtin_modules["square"] = new PrimitiveModule(SQUARE); - builtin_modules["circle"] = new PrimitiveModule(CIRCLE); - builtin_modules["polygon"] = new PrimitiveModule(POLYGON); -} - -/*! - Returns the number of subdivision of a whole circle, given radius and - the three special variables $fn, $fs and $fa -*/ -int get_fragments_from_r(double r, double fn, double fs, double fa) -{ - if (fn > 0.0) - return (int)fn; - return (int)ceil(fmax(fmin(360.0 / fa, r*M_PI / fs), 5)); -} - -PolySet *PrimitiveNode::render_polyset(render_mode_e) const -{ - PolySet *p = new PolySet(); - - if (type == CUBE && x > 0 && y > 0 && z > 0) - { - double x1, x2, y1, y2, z1, z2; - if (center) { - x1 = -x/2; - x2 = +x/2; - y1 = -y/2; - y2 = +y/2; - z1 = -z/2; - z2 = +z/2; - } else { - x1 = y1 = z1 = 0; - x2 = x; - y2 = y; - z2 = z; - } - - p->append_poly(); // top - p->append_vertex(x1, y1, z2); - p->append_vertex(x2, y1, z2); - p->append_vertex(x2, y2, z2); - p->append_vertex(x1, y2, z2); - - p->append_poly(); // bottom - p->append_vertex(x1, y2, z1); - p->append_vertex(x2, y2, z1); - p->append_vertex(x2, y1, z1); - p->append_vertex(x1, y1, z1); - - p->append_poly(); // side1 - p->append_vertex(x1, y1, z1); - p->append_vertex(x2, y1, z1); - p->append_vertex(x2, y1, z2); - p->append_vertex(x1, y1, z2); - - p->append_poly(); // side2 - p->append_vertex(x2, y1, z1); - p->append_vertex(x2, y2, z1); - p->append_vertex(x2, y2, z2); - p->append_vertex(x2, y1, z2); - - p->append_poly(); // side3 - p->append_vertex(x2, y2, z1); - p->append_vertex(x1, y2, z1); - p->append_vertex(x1, y2, z2); - p->append_vertex(x2, y2, z2); - - p->append_poly(); // side4 - p->append_vertex(x1, y2, z1); - p->append_vertex(x1, y1, z1); - p->append_vertex(x1, y1, z2); - p->append_vertex(x1, y2, z2); - } - - if (type == SPHERE && r1 > 0) - { - struct point2d { - double x, y; - }; - - struct ring_s { - int fragments; - point2d *points; - double r, z; - }; - - int rings = get_fragments_from_r(r1, fn, fs, fa); - ring_s ring[rings]; - - for (int i = 0; i < rings; i++) { - double phi = (M_PI * (i + 0.5)) / rings; - ring[i].r = r1 * sin(phi); - ring[i].z = r1 * cos(phi); - ring[i].fragments = get_fragments_from_r(ring[i].r, fn, fs, fa); - ring[i].points = new point2d[ring[i].fragments]; - for (int j = 0; j < ring[i].fragments; j++) { - phi = (M_PI*2*j) / ring[i].fragments; - ring[i].points[j].x = ring[i].r * cos(phi); - ring[i].points[j].y = ring[i].r * sin(phi); - } - } - - p->append_poly(); - for (int i = 0; i < ring[0].fragments; i++) - p->append_vertex(ring[0].points[i].x, ring[0].points[i].y, ring[0].z); - - for (int i = 0; i < rings-1; i++) { - ring_s *r1 = &ring[i]; - ring_s *r2 = &ring[i+1]; - int r1i = 0, r2i = 0; - while (r1i < r1->fragments || r2i < r2->fragments) - { - if (r1i >= r1->fragments) - goto sphere_next_r2; - if (r2i >= r2->fragments) - goto sphere_next_r1; - if ((double)r1i / r1->fragments < - (double)r2i / r2->fragments) - { -sphere_next_r1: - p->append_poly(); - int r1j = (r1i+1) % r1->fragments; - p->insert_vertex(r1->points[r1i].x, r1->points[r1i].y, r1->z); - p->insert_vertex(r1->points[r1j].x, r1->points[r1j].y, r1->z); - p->insert_vertex(r2->points[r2i % r2->fragments].x, r2->points[r2i % r2->fragments].y, r2->z); - r1i++; - } else { -sphere_next_r2: - p->append_poly(); - int r2j = (r2i+1) % r2->fragments; - p->append_vertex(r2->points[r2i].x, r2->points[r2i].y, r2->z); - p->append_vertex(r2->points[r2j].x, r2->points[r2j].y, r2->z); - p->append_vertex(r1->points[r1i % r1->fragments].x, r1->points[r1i % r1->fragments].y, r1->z); - r2i++; - } - } - } - - p->append_poly(); - for (int i = 0; i < ring[rings-1].fragments; i++) - p->insert_vertex(ring[rings-1].points[i].x, ring[rings-1].points[i].y, ring[rings-1].z); - } - - if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0)) - { - int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa); - - double z1, z2; - if (center) { - z1 = -h/2; - z2 = +h/2; - } else { - z1 = 0; - z2 = h; - } - - struct point2d { - double x, y; - }; - - point2d circle1[fragments]; - point2d circle2[fragments]; - - for (int i=0; i 0) { - circle1[i].x = r1*cos(phi); - circle1[i].y = r1*sin(phi); - } else { - circle1[i].x = 0; - circle1[i].y = 0; - } - if (r2 > 0) { - circle2[i].x = r2*cos(phi); - circle2[i].y = r2*sin(phi); - } else { - circle2[i].x = 0; - circle2[i].y = 0; - } - } - - for (int i=0; i 0) { - p->append_poly(); - p->insert_vertex(circle1[i].x, circle1[i].y, z1); - p->insert_vertex(circle2[i].x, circle2[i].y, z2); - p->insert_vertex(circle1[j].x, circle1[j].y, z1); - } - if (r2 > 0) { - p->append_poly(); - p->insert_vertex(circle2[i].x, circle2[i].y, z2); - p->insert_vertex(circle2[j].x, circle2[j].y, z2); - p->insert_vertex(circle1[j].x, circle1[j].y, z1); - } - } - - if (r1 > 0) { - p->append_poly(); - for (int i=0; iinsert_vertex(circle1[i].x, circle1[i].y, z1); - } - - if (r2 > 0) { - p->append_poly(); - for (int i=0; iappend_vertex(circle2[i].x, circle2[i].y, z2); - } - } - - if (type == POLYHEDRON) - { - p->convexity = convexity; - for (int i=0; iappend_poly(); - for (int j=0; jvec.size(); j++) { - int pt = triangles.vec[i]->vec[j]->num; - if (pt < points.vec.size()) { - double px, py, pz; - if (points.vec[pt]->getv3(px, py, pz)) - p->insert_vertex(px, py, pz); - } - } - } - } - - if (type == SQUARE) - { - double x1, x2, y1, y2; - if (center) { - x1 = -x/2; - x2 = +x/2; - y1 = -y/2; - y2 = +y/2; - } else { - x1 = y1 = 0; - x2 = x; - y2 = y; - } - - p->is2d = true; - p->append_poly(); - p->append_vertex(x1, y1); - p->append_vertex(x2, y1); - p->append_vertex(x2, y2); - p->append_vertex(x1, y2); - } - - if (type == CIRCLE) - { - int fragments = get_fragments_from_r(r1, fn, fs, fa); - - struct point2d { - double x, y; - }; - - point2d circle[fragments]; - - for (int i=0; iis2d = true; - p->append_poly(); - for (int i=0; iappend_vertex(circle[i].x, circle[i].y); - } - - if (type == POLYGON) - { - DxfData dd; - - for (int i=0; ivec[0]->num; - double y = points.vec[i]->vec[1]->num; - dd.points.append(DxfData::Point(x, y)); - } - - if (paths.vec.size() == 0) - { - dd.paths.append(DxfData::Path()); - for (int i=0; i 0) { - dd.paths.last().points.append(dd.paths.last().points.first()); - dd.paths.last().is_closed = true; - } - } - else - { - for (int i=0; ivec.size(); j++) { - int idx = paths.vec[i]->vec[j]->num; - if (idx < dd.points.size()) { - DxfData::Point *p = &dd.points[idx]; - dd.paths.last().points.append(p); - } - } - if (dd.paths.last().points.isEmpty()) { - dd.paths.removeLast(); - } else { - dd.paths.last().points.append(dd.paths.last().points.first()); - dd.paths.last().is_closed = true; - } - } - } - - p->is2d = true; - p->convexity = convexity; - dxf_tesselate(p, &dd, 0, true, false, 0); - dxf_border_to_ps(p, &dd); - } - - return p; -} - -QString PrimitiveNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - if (type == CUBE) - text.sprintf("cube(size = [%g, %g, %g], center = %s);\n", x, y, z, center ? "true" : "false"); - if (type == SPHERE) - text.sprintf("sphere($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); - if (type == CYLINDER) - text.sprintf("cylinder($fn = %g, $fa = %g, $fs = %g, h = %g, r1 = %g, r2 = %g, center = %s);\n", fn, fa, fs, h, r1, r2, center ? "true" : "false"); - if (type == POLYHEDRON) - text.sprintf("polyhedron(points = %s, triangles = %s, convexity = %d);\n", points.dump().toAscii().data(), triangles.dump().toAscii().data(), convexity); - if (type == SQUARE) - text.sprintf("square(size = [%g, %g], center = %s);\n", x, y, center ? "true" : "false"); - if (type == CIRCLE) - text.sprintf("circle($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); - if (type == POLYGON) - text.sprintf("polygon(points = %s, paths = %s, convexity = %d);\n", points.dump().toAscii().data(), paths.dump().toAscii().data(), convexity); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} - diff --git a/printutils.cc b/printutils.cc deleted file mode 100644 index a75cf14..0000000 --- a/printutils.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "printutils.h" -#include "MainWindow.h" - -QList print_messages_stack; - -void print_messages_push() -{ - print_messages_stack.append(QString()); -} - -void print_messages_pop() -{ - QString msg = print_messages_stack.last(); - print_messages_stack.removeLast(); - if (print_messages_stack.size() > 0 && !msg.isNull()) { - if (!print_messages_stack.last().isEmpty()) - print_messages_stack.last() += "\n"; - print_messages_stack.last() += msg; - } -} - -void PRINT(const QString &msg) -{ - if (msg.isNull()) - return; - if (print_messages_stack.size() > 0) { - if (!print_messages_stack.last().isEmpty()) - print_messages_stack.last() += "\n"; - print_messages_stack.last() += msg; - } - PRINT_NOCACHE(msg); -} - -void PRINT_NOCACHE(const QString &msg) -{ - if (msg.isNull()) - return; - if (MainWindow::current_win.isNull()) { - fprintf(stderr, "%s\n", msg.toAscii().data()); - } else { - MainWindow::current_win->console->append(msg); - } -} - diff --git a/printutils.h b/printutils.h deleted file mode 100644 index 18cef93..0000000 --- a/printutils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PRINTUTILS_H_ -#define PRINTUTILS_H_ - -#include -#include - -extern QList print_messages_stack; -void print_messages_push(); -void print_messages_pop(); - -void PRINT(const QString &msg); -#define PRINTF(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT(_m); } while (0) -#define PRINTA(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT(_m); } while (0) - -void PRINT_NOCACHE(const QString &msg); -#define PRINTF_NOCACHE(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) -#define PRINTA_NOCACHE(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) - -#endif diff --git a/publish-macosx.sh b/publish-macosx.sh deleted file mode 100755 index b616d64..0000000 --- a/publish-macosx.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -VERSION=`date "+%Y.%m.%d"` -#VERSION=2010.02 - -export OPENCSGDIR=$PWD/../OpenCSG-1.2.0 - -./release-common.sh -v $VERSION -cp OpenSCAD-$VERSION.dmg ~/Documents/Dropbox/Public -ln -sf OpenSCAD-$VERSION.dmg ~/Documents/Dropbox/Public/OpenSCAD-latest.dmg - -echo "Upload in progress..." diff --git a/qtcolorbutton/README.txt b/qtcolorbutton/README.txt deleted file mode 100644 index 4b55f67..0000000 --- a/qtcolorbutton/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -The QtColorButton class is copied from the gradient editor in Qt's shared tool classes: -http://qt.gitorious.org/qt/qt/trees/master/tools/shared/qtgradienteditor diff --git a/qtcolorbutton/qtcolorbutton.cpp b/qtcolorbutton/qtcolorbutton.cpp deleted file mode 100644 index 21b9848..0000000 --- a/qtcolorbutton/qtcolorbutton.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtcolorbutton.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QtColorButtonPrivate -{ - QtColorButton *q_ptr; - Q_DECLARE_PUBLIC(QtColorButton) -public: - QColor m_color; -#ifndef QT_NO_DRAGANDDROP - QColor m_dragColor; - QPoint m_dragStart; - bool m_dragging; -#endif - bool m_backgroundCheckered; - - void slotEditColor(); - QColor shownColor() const; - QPixmap generatePixmap() const; -}; - -void QtColorButtonPrivate::slotEditColor() -{ - bool ok; - const QRgb rgba = QColorDialog::getRgba(m_color.rgba(), &ok, q_ptr); - if (!ok) - return; - const QColor c = QColor::fromRgba(rgba); - if (c == q_ptr->color()) - return; - q_ptr->setColor(c); - emit q_ptr->colorChanged(m_color); -} - -QColor QtColorButtonPrivate::shownColor() const -{ -#ifndef QT_NO_DRAGANDDROP - if (m_dragging) - return m_dragColor; -#endif - return m_color; -} - -QPixmap QtColorButtonPrivate::generatePixmap() const -{ - QPixmap pix(24, 24); - - int pixSize = 20; - QBrush br(shownColor()); - - QPixmap pm(2 * pixSize, 2 * pixSize); - QPainter pmp(&pm); - pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); - pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); - pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); - pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); - pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); - br = QBrush(pm); - - QPainter p(&pix); - int corr = 1; - QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); - p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); - p.fillRect(r, br); - - p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, - r.width() / 2, r.height() / 2, - QColor(shownColor().rgb())); - p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); - - return pix; -} - -/////////////// - -QtColorButton::QtColorButton(QWidget *parent) - : QToolButton(parent) -{ - d_ptr = new QtColorButtonPrivate; - d_ptr->q_ptr = this; - d_ptr->m_dragging = false; - d_ptr->m_backgroundCheckered = true; - - setAcceptDrops(true); - - connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); - setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); -} - -QtColorButton::~QtColorButton() -{ - delete d_ptr; -} - -void QtColorButton::setColor(const QColor &color) -{ - if (d_ptr->m_color == color) - return; - d_ptr->m_color = color; - update(); -} - -QColor QtColorButton::color() const -{ - return d_ptr->m_color; -} - -void QtColorButton::setBackgroundCheckered(bool checkered) -{ - if (d_ptr->m_backgroundCheckered == checkered) - return; - d_ptr->m_backgroundCheckered = checkered; - update(); -} - -bool QtColorButton::isBackgroundCheckered() const -{ - return d_ptr->m_backgroundCheckered; -} - -void QtColorButton::paintEvent(QPaintEvent *event) -{ - QToolButton::paintEvent(event); - if (!isEnabled()) - return; - - const int pixSize = 10; - QBrush br(d_ptr->shownColor()); - if (d_ptr->m_backgroundCheckered) { - QPixmap pm(2 * pixSize, 2 * pixSize); - QPainter pmp(&pm); - pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); - pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); - pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); - pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); - pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); - br = QBrush(pm); - } - - QPainter p(this); - const int corr = 4; - QRect r = rect().adjusted(corr, corr, -corr, -corr); - p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); - p.fillRect(r, br); - - //const int adjX = qRound(r.width() / 4.0); - //const int adjY = qRound(r.height() / 4.0); - //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY), - // QColor(d_ptr->shownColor().rgb())); - /* - p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0), - QColor(d_ptr->shownColor().rgb())); - p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4), - QColor(d_ptr->shownColor().rgb())); - */ - /* - const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha()))); - p.setPen(frameColor0); - p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1)); - */ - - const QColor frameColor1(0, 0, 0, 26); - p.setPen(frameColor1); - p.drawRect(r.adjusted(1, 1, -2, -2)); - const QColor frameColor2(0, 0, 0, 51); - p.setPen(frameColor2); - p.drawRect(r.adjusted(0, 0, -1, -1)); -} - -void QtColorButton::mousePressEvent(QMouseEvent *event) -{ -#ifndef QT_NO_DRAGANDDROP - if (event->button() == Qt::LeftButton) - d_ptr->m_dragStart = event->pos(); -#endif - QToolButton::mousePressEvent(event); -} - -void QtColorButton::mouseMoveEvent(QMouseEvent *event) -{ -#ifndef QT_NO_DRAGANDDROP - if (event->buttons() & Qt::LeftButton && - (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { - QMimeData *mime = new QMimeData; - mime->setColorData(color()); - QDrag *drg = new QDrag(this); - drg->setMimeData(mime); - drg->setPixmap(d_ptr->generatePixmap()); - setDown(false); - event->accept(); - drg->start(); - return; - } -#endif - QToolButton::mouseMoveEvent(event); -} - -#ifndef QT_NO_DRAGANDDROP -void QtColorButton::dragEnterEvent(QDragEnterEvent *event) -{ - const QMimeData *mime = event->mimeData(); - if (!mime->hasColor()) - return; - - event->accept(); - d_ptr->m_dragColor = qvariant_cast(mime->colorData()); - d_ptr->m_dragging = true; - update(); -} - -void QtColorButton::dragLeaveEvent(QDragLeaveEvent *event) -{ - event->accept(); - d_ptr->m_dragging = false; - update(); -} - -void QtColorButton::dropEvent(QDropEvent *event) -{ - event->accept(); - d_ptr->m_dragging = false; - if (d_ptr->m_dragColor == color()) - return; - setColor(d_ptr->m_dragColor); - emit colorChanged(color()); -} -#endif - -QT_END_NAMESPACE - -#include "moc_qtcolorbutton.cpp" diff --git a/qtcolorbutton/qtcolorbutton.h b/qtcolorbutton/qtcolorbutton.h deleted file mode 100644 index 26bdde0..0000000 --- a/qtcolorbutton/qtcolorbutton.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTCOLORBUTTON_H -#define QTCOLORBUTTON_H - -#include - -QT_BEGIN_NAMESPACE - -class QtColorButton : public QToolButton -{ - Q_OBJECT - Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) -public: - QtColorButton(QWidget *parent = 0); - ~QtColorButton(); - - bool isBackgroundCheckered() const; - void setBackgroundCheckered(bool checkered); - - QColor color() const; - -public slots: - - void setColor(const QColor &color); - -signals: - void colorChanged(const QColor &color); -protected: - void paintEvent(QPaintEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); -#ifndef QT_NO_DRAGANDDROP - void dragEnterEvent(QDragEnterEvent *event); - void dragLeaveEvent(QDragLeaveEvent *event); - void dropEvent(QDropEvent *event); -#endif -private: - class QtColorButtonPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtColorButton) - Q_DISABLE_COPY(QtColorButton) - Q_PRIVATE_SLOT(d_func(), void slotEditColor()) -}; - -QT_END_NAMESPACE - -#endif diff --git a/qtcolorbutton/qtcolorbutton.pri b/qtcolorbutton/qtcolorbutton.pri deleted file mode 100644 index 0e41068..0000000 --- a/qtcolorbutton/qtcolorbutton.pri +++ /dev/null @@ -1,4 +0,0 @@ -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -SOURCES += $$PWD/qtcolorbutton.cpp -HEADERS += $$PWD/qtcolorbutton.h diff --git a/release-common.sh b/release-common.sh deleted file mode 100755 index 9624035..0000000 --- a/release-common.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/sh -# -# This script creates a binary release of OpenSCAD. -# This should work under Mac OS X and Windows (msys). Linux support pending. -# The script will create a file called openscad-.zip -# in the current directory. -# -# Usage: release-common.sh [-v ] -# -v Version string (e.g. -v 2010.01) -# -# If no version string is given, todays date will be used (YYYY-MM-DD) -# If no make target is given, release will be used on Windows, none one Mac OS X -# - -printUsage() -{ - echo "Usage: $0 -v - echo - echo " Example: $0 -v 2010.01 -} - -if [[ $OSTYPE =~ "darwin" ]]; then - OS=MACOSX -elif [[ $OSTYPE == "msys" ]]; then - OS=WIN -fi - -echo "Detected OS: $OS" - -while getopts 'v:' c -do - case $c in - v) VERSION=$OPTARG;; - esac -done - -if test -z "$VERSION"; then - VERSION=`date "+%Y.%m.%d"` -fi - -echo "Building openscad-$VERSION $CONFIGURATION..." - -case $OS in - MACOSX) - CONFIG=mdi - TARGET= - ;; - WIN) - unset CONFIG - export QTDIR=/c/devmingw/qt2009.03 - export QTMAKESPEC=win32-g++ - export PATH=$PATH:/c/devmingw/qt2009.03/bin:/c/devmingw/qt2009.03/qt/bin - ZIP="/c/Program Files/7-Zip/7z.exe" - ZIPARGS="a -tzip" - TARGET=release - ;; -esac - -qmake VERSION=$VERSION CONFIG+=$CONFIG CONFIG-=debug openscad.pro -make -s clean -case $OS in - MACOSX) - rm -rf OpenSCAD.app - ;; - WIN) - #if the following files are missing their tried removal stops the build process on msys - touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp - ;; -esac - -make -j2 $TARGET - -echo "Creating directory structure..." -rm -rf openscad-$VERSION -mkdir openscad-$VERSION -EXAMPLESDIR=openscad-$VERSION/examples/ - -case $OS in - MACOSX) - OPENCSGDIR=`cd "$OPENCSGDIR" && pwd` - mkdir OpenSCAD.app/Contents/Frameworks - cp $OPENCSGDIR/lib/libopencsg.dylib OpenSCAD.app/Contents/Frameworks - cp /opt/local/lib/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks - cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL OpenSCAD.app/Contents/Frameworks - cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui OpenSCAD.app/Contents/Frameworks - cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore OpenSCAD.app/Contents/Frameworks - install_name_tool -change $OPENCSGDIR/lib/libopencsg.1.dylib @executable_path/../Frameworks/libopencsg.dylib OpenSCAD.app/Contents/MacOS/openscad - install_name_tool -change QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL OpenSCAD.app/Contents/MacOS/openscad - install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/MacOS/openscad - install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/MacOS/openscad - install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/QtOpenGL - install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtOpenGL - install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtGui - install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/MacOS/openscad - install_name_tool -id libopencsg.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib - install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib - install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/libopencsg.dylib - install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/libopencsg.dylib - install_name_tool -id libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libGLEW.1.5.1.dylib - mv OpenSCAD.app openscad-$VERSION - EXAMPLESDIR=openscad-$VERSION/OpenSCAD.app/Contents/Resources/examples - ;; - WIN) - #package - cp win32deps/* openscad-$VERSION - cp $TARGET/openscad.exe openscad-$VERSION - ;; -esac - -mkdir -p $EXAMPLESDIR -cp examples/* $EXAMPLESDIR -chmod -R 644 $EXAMPLESDIR/* - -echo "Creating archive.." -case $OS in - MACOSX) - hdiutil create -quiet -ov -srcfolder openscad-$VERSION/OpenSCAD.app OpenSCAD-$VERSION.dmg - hdiutil internet-enable -yes -quiet OpenSCAD-$VERSION.dmg - echo "Binary created: OpenSCAD-$VERSION.dmg" - ;; - *) - rm -f openscad-$VERSION.zip - "$ZIP" $ZIPARGS openscad-$VERSION.zip openscad-$VERSION - echo "Binary created: openscad-$VERSION.zip" - ;; -esac - -rm -rf openscad-$VERSION - diff --git a/release-linux.sh b/release-linux.sh deleted file mode 100755 index 41a7aa3..0000000 --- a/release-linux.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -# WARNING: This script might only work with the authors setup... - -VERSION=2010.01 - -set -ex - -# svnclean - -qmake-qt4 VERSION=$VERSION -make - -rm -rf release -mkdir -p release/{bin,lib/openscad,examples} - -cat > release/bin/openscad << "EOT" -#!/bin/bash - -cd "$( dirname "$( type -p $0 )" )" -libdir=$PWD/../lib/openscad/ -cd "$OLDPWD" - -export LD_LIBRARY_PATH="$libdir${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" -exec $libdir/openscad "$@" -EOT - -cp openscad release/lib/openscad/ -gcc -o chrpath_linux chrpath_linux.c -./chrpath_linux -d release/lib/openscad/openscad - -ldd openscad | sed -re 's,.* => ,,; s,[\t ].*,,;' -e '/Qt|boost/ { p; d; };' \ - -e '/lib(audio|CGAL|GLEW|opencsg|png)\.so/ { p; d; };' \ - -e 'd;' | xargs cp -vt release/lib/openscad/ -strip release/lib/openscad/* - -cat > release/install.sh << "EOT" -#!/bin/bash - -# change to the install source directory -cd "$( dirname "$( type -p $0 )" )" - -if ! [ -f bin/openscad -a -d lib/openscad -a -d examples ]; then - echo "Error: Can't change to install source directory!" >&2 - exit 1 -fi - -echo "This will install openscad. Please enter the install prefix" -echo "or press Ctrl-C to abort the install process:" -read -p "[/usr/local]: " prefix - -if [ "$prefix" = "" ]; then - prefix="/usr/local" -fi - -if [ ! -d "$prefix" ]; then - echo; echo "Install prefix \`$prefix' does not exist. Press ENTER to continue" - echo "or press Ctrl-C to abort the install process:" - read -p "press enter to continue> " -fi - -mkdir -p "$prefix"/{bin,lib/openscad} - -if ! [ -w "$prefix"/bin/ -a -w "$prefix"/lib/ ]; then - echo "You does not seam to have write permissions for prefix \`$prefix'!" >&2 - echo "Maybe you should have run this install script using \`sudo'?" >&2 - exit 1 -fi - -echo "Copying application wrappers..." -cp -rv bin/. "$prefix"/bin/ - -echo "Copying application and libraries..." -cp -rv lib/. "$prefix"/lib/ - -echo "Installation finished. Have a nice day." -EOT - -chmod 755 -R release/ - -cp examples/* release/examples/ -chmod 644 -R release/examples/* - diff --git a/release-macosx.sh b/release-macosx.sh deleted file mode 100755 index 8ac5dc0..0000000 --- a/release-macosx.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -# -# This script creates a binary release of OpenSCAD for Mac OS X. -# The script will create a file called openscad-.zip -# in the current directory. -# -# Usage: makedmg.sh [-v ] -# -v Version string (e.g. -v 2010.01) -# -# If no version string is given, todays date will be used (YYYY-MM-DD) -# -printUsage() -{ - echo "Usage: $0 -v " - echo - echo " Example: $0 -v 2010.01" -} - -while getopts 'v:' c -do - case $c in - v) VERSION=$OPTARG;; - esac -done - -if test -z "$VERSION"; then - VERSION=`date "+%Y.%m.%d"` -fi - -echo "Building openscad-$VERSION..." -export OPENCSGDIR=$PWD/../OpenCSG-1.2.0 -qmake VERSION=$VERSION CONFIG+=mdi openscad.pro -make clean -make -j2 -echo "Preparing executable.." -mkdir OpenSCAD.app/Contents/Frameworks -cp $OPENCSGDIR/lib/libopencsg.dylib OpenSCAD.app/Contents/Frameworks -cp /opt/local/lib/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks -cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL OpenSCAD.app/Contents/Frameworks -cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui OpenSCAD.app/Contents/Frameworks -cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore OpenSCAD.app/Contents/Frameworks -install_name_tool -change libopencsg.1.dylib @executable_path/../Frameworks/libopencsg.dylib OpenSCAD.app/Contents/MacOS/openscad -install_name_tool -change QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL OpenSCAD.app/Contents/MacOS/openscad -install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/MacOS/openscad -install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/MacOS/openscad -install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/QtOpenGL -install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtOpenGL -install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtGui -install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/MacOS/openscad -install_name_tool -id libopencsg.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib -install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib -install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/libopencsg.dylib -install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/libopencsg.dylib -install_name_tool -id libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libGLEW.1.5.1.dylib - -echo "Creating directory structure.." -rm -rf openscad-$VERSION -mkdir -p openscad-$VERSION/examples -cp examples/* openscad-$VERSION/examples/ -chmod -R 644 openscad-$VERSION/examples/* -mv OpenSCAD.app openscad-$VERSION - -echo "Creating archive.." -zip -qr openscad-$VERSION.zip openscad-$VERSION -echo "Mac OS X binary created: openscad-$VERSION.zip" diff --git a/release-win32.sh b/release-win32.sh deleted file mode 100644 index 540eab9..0000000 --- a/release-win32.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh -# -# This script creates a binary release of OpenSCAD for Mac OS X. -# The script will create a file called openscad-.zip -# in the current directory. -# -# Usage: release-win32.sh [-v ] -# -v Version string (e.g. -v 2010.01) -# -# If no version string is given, todays date will be used (YYYY-MM-DD) -# - -#used for windows -ZIP="/c/Program Files/7-Zip/7z.exe" -ZIPARGS="a -tzip" - -printUsage() -{ - echo "Usage: $0 -v -t " - echo - echo " Example: $0 -v 2010.01 -t release" -} - -OS=OSX -if test "`uname -o`" == "Msys"; then - OS=WIN -fi - -echo "detected OS= $OS" - -while getopts 'v:' c -do - case $c in - v) VERSION=$OPTARG;; - b) TARGET=$OPTARG;; - esac -done - -if test -z "$VERSION"; then - VERSION=`date "+%Y.%m.%d"` -fi - -if test -z "$TARGET"; then - TARGET=release -fi - -echo "Building openscad-$VERSION $CONFIGURATION..." - -case $OS in - OSX) - CONFIG = mdi;; - WIN) - unset CONFIG - export QTDIR=/c/devmingw/qt2009.03 - export QTMAKESPEC=win32-g++ - export PATH=$PATH:/c/devmingw/qt2009.03/bin:/c/devmingw/qt2009.03/qt/bin - ;; -esac - -qmake VERSION=$VERSION CONFIG+=$CONFIG -make clean -if test $OS == WIN; then - #if the following files are missing their tried removal stops the build process on msys - touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp -fi - -make -j2 $TARGET - -echo "Preparing executable..." - -echo "Creating directory structure..." -rm -rf openscad-$VERSION -rm -f openscad-$VERSION.zip -mkdir -p openscad-$VERSION/examples -cp examples/* openscad-$VERSION/examples/ - -case $OS in - OSX) ;; - WIN) - #package - cp win32deps/* openscad-$VERSION - cp $TARGET/openscad.exe openscad-$VERSION - ;; -esac - -echo "Creating directory structure..." -case $OS in - OSX) ;; - WIN) - "$ZIP" $ZIPARGS openscad-$VERSION.zip openscad-$VERSION - ;; -esac - -rm -rf openscad-$VERSION - -echo "binary created: openscad-$VERSION.zip" diff --git a/render.cc b/render.cc deleted file mode 100644 index 7c82d92..0000000 --- a/render.cc +++ /dev/null @@ -1,264 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#include -#include -#include - -class RenderModule : public AbstractModule -{ -public: - RenderModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class RenderNode : public AbstractNode -{ -public: - int convexity; - RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - RenderNode *node = new RenderNode(inst); - - QVector argnames = QVector() << "convexity"; - QVector argexpr; - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - Value v = c.lookup_variable("convexity"); - if (v.type == Value::NUMBER) - node->convexity = (int)v.num; - - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } - - return node; -} - -void register_builtin_render() -{ - builtin_modules["render"] = new RenderModule(); -} - -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron RenderNode::render_cgal_nef_polyhedron() const -{ - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) { - progress_report(); - PRINT(cgal_nef_cache[cache_id]->msg); - return cgal_nef_cache[cache_id]->N; - } - - print_messages_push(); - - bool first = true; - CGAL_Nef_polyhedron N; - foreach(AbstractNode * v, children) - { - if (v->modinst->tag_background) - continue; - if (first) { - N = v->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (N.dim == 3) { - N.p3 += v->render_cgal_nef_polyhedron().p3; - } - } - - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - print_messages_pop(); - progress_report(); - - return N; -} - -static void report_func(const class AbstractNode*, void *vp, int mark) -{ - QProgressDialog *pd = (QProgressDialog*)vp; - int v = (int)((mark*100.0) / progress_report_count); - pd->setValue(v < 100 ? v : 99); - QString label; - label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); - pd->setLabelText(label); - QApplication::processEvents(); -} - -CSGTerm *RenderNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return AbstractPolyNode::render_csg_term_from_ps(m, highlights, background, - PolySet::ps_cache[key]->ps->link(), modinst, idx); - } - - print_messages_push(); - CGAL_Nef_polyhedron N; - - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) - { - PRINT(cgal_nef_cache[cache_id]->msg); - N = cgal_nef_cache[cache_id]->N; - } - else - { - PRINT_NOCACHE("Processing uncached render statement..."); - // PRINTA("Cache ID: %1", cache_id); - QApplication::processEvents(); - - QTime t; - t.start(); - - QProgressDialog *pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); - pd->setValue(0); - pd->setAutoClose(false); - pd->show(); - QApplication::processEvents(); - - progress_report_prep((AbstractNode*)this, report_func, pd); - N = this->render_cgal_nef_polyhedron(); - progress_report_fin(); - - int s = t.elapsed() / 1000; - PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - - delete pd; - } - - PolySet *ps = NULL; - - if (N.dim == 2) - { - DxfData dd(N); - ps = new PolySet(); - ps->is2d = true; - dxf_tesselate(ps, &dd, 0, true, false, 0); - dxf_border_to_ps(ps, &dd); - } - - if (N.dim == 3) - { - if (!N.p3.is_simple()) { - PRINTF("WARNING: Result of render() isn't valid 2-manifold! Modify your design.."); - return NULL; - } - - ps = new PolySet(); - - CGAL_Polyhedron P; - N.p3.convert_to_Polyhedron(P); - - typedef CGAL_Polyhedron::Vertex Vertex; - typedef CGAL_Polyhedron::Vertex_const_iterator VCI; - typedef CGAL_Polyhedron::Facet_const_iterator FCI; - typedef CGAL_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 v = *VCI((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); - } - } - - if (ps) - { - ps->convexity = convexity; - PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); - - CSGTerm *term = new CSGTerm(ps, m, QString("n%1").arg(idx)); - if (modinst->tag_highlight && highlights) - highlights->append(term->link()); - if (modinst->tag_background && background) { - background->append(term); - return NULL; - } - return term; - } - print_messages_pop(); - - return NULL; -} - -#else - -CSGTerm *RenderNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const -{ - CSGTerm *t1 = NULL; - PRINT("WARNING: Found render() statement but compiled without CGAL support!"); - foreach(AbstractNode * v, children) { - CSGTerm *t2 = v->render_csg_term(m, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } - } - if (modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; -} - -#endif - -QString RenderNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: ").arg(idx) + QString("render(convexity = %1) {\n").arg(QString::number(convexity)); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - diff --git a/scripts/publish-macosx.sh b/scripts/publish-macosx.sh new file mode 100755 index 0000000..b616d64 --- /dev/null +++ b/scripts/publish-macosx.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +VERSION=`date "+%Y.%m.%d"` +#VERSION=2010.02 + +export OPENCSGDIR=$PWD/../OpenCSG-1.2.0 + +./release-common.sh -v $VERSION +cp OpenSCAD-$VERSION.dmg ~/Documents/Dropbox/Public +ln -sf OpenSCAD-$VERSION.dmg ~/Documents/Dropbox/Public/OpenSCAD-latest.dmg + +echo "Upload in progress..." diff --git a/scripts/release-common.sh b/scripts/release-common.sh new file mode 100755 index 0000000..9624035 --- /dev/null +++ b/scripts/release-common.sh @@ -0,0 +1,129 @@ +#!/bin/sh +# +# This script creates a binary release of OpenSCAD. +# This should work under Mac OS X and Windows (msys). Linux support pending. +# The script will create a file called openscad-.zip +# in the current directory. +# +# Usage: release-common.sh [-v ] +# -v Version string (e.g. -v 2010.01) +# +# If no version string is given, todays date will be used (YYYY-MM-DD) +# If no make target is given, release will be used on Windows, none one Mac OS X +# + +printUsage() +{ + echo "Usage: $0 -v + echo + echo " Example: $0 -v 2010.01 +} + +if [[ $OSTYPE =~ "darwin" ]]; then + OS=MACOSX +elif [[ $OSTYPE == "msys" ]]; then + OS=WIN +fi + +echo "Detected OS: $OS" + +while getopts 'v:' c +do + case $c in + v) VERSION=$OPTARG;; + esac +done + +if test -z "$VERSION"; then + VERSION=`date "+%Y.%m.%d"` +fi + +echo "Building openscad-$VERSION $CONFIGURATION..." + +case $OS in + MACOSX) + CONFIG=mdi + TARGET= + ;; + WIN) + unset CONFIG + export QTDIR=/c/devmingw/qt2009.03 + export QTMAKESPEC=win32-g++ + export PATH=$PATH:/c/devmingw/qt2009.03/bin:/c/devmingw/qt2009.03/qt/bin + ZIP="/c/Program Files/7-Zip/7z.exe" + ZIPARGS="a -tzip" + TARGET=release + ;; +esac + +qmake VERSION=$VERSION CONFIG+=$CONFIG CONFIG-=debug openscad.pro +make -s clean +case $OS in + MACOSX) + rm -rf OpenSCAD.app + ;; + WIN) + #if the following files are missing their tried removal stops the build process on msys + touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp + ;; +esac + +make -j2 $TARGET + +echo "Creating directory structure..." +rm -rf openscad-$VERSION +mkdir openscad-$VERSION +EXAMPLESDIR=openscad-$VERSION/examples/ + +case $OS in + MACOSX) + OPENCSGDIR=`cd "$OPENCSGDIR" && pwd` + mkdir OpenSCAD.app/Contents/Frameworks + cp $OPENCSGDIR/lib/libopencsg.dylib OpenSCAD.app/Contents/Frameworks + cp /opt/local/lib/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks + cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL OpenSCAD.app/Contents/Frameworks + cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui OpenSCAD.app/Contents/Frameworks + cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore OpenSCAD.app/Contents/Frameworks + install_name_tool -change $OPENCSGDIR/lib/libopencsg.1.dylib @executable_path/../Frameworks/libopencsg.dylib OpenSCAD.app/Contents/MacOS/openscad + install_name_tool -change QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL OpenSCAD.app/Contents/MacOS/openscad + install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/MacOS/openscad + install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/MacOS/openscad + install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/QtOpenGL + install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtOpenGL + install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtGui + install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/MacOS/openscad + install_name_tool -id libopencsg.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib + install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib + install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/libopencsg.dylib + install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/libopencsg.dylib + install_name_tool -id libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libGLEW.1.5.1.dylib + mv OpenSCAD.app openscad-$VERSION + EXAMPLESDIR=openscad-$VERSION/OpenSCAD.app/Contents/Resources/examples + ;; + WIN) + #package + cp win32deps/* openscad-$VERSION + cp $TARGET/openscad.exe openscad-$VERSION + ;; +esac + +mkdir -p $EXAMPLESDIR +cp examples/* $EXAMPLESDIR +chmod -R 644 $EXAMPLESDIR/* + +echo "Creating archive.." +case $OS in + MACOSX) + hdiutil create -quiet -ov -srcfolder openscad-$VERSION/OpenSCAD.app OpenSCAD-$VERSION.dmg + hdiutil internet-enable -yes -quiet OpenSCAD-$VERSION.dmg + echo "Binary created: OpenSCAD-$VERSION.dmg" + ;; + *) + rm -f openscad-$VERSION.zip + "$ZIP" $ZIPARGS openscad-$VERSION.zip openscad-$VERSION + echo "Binary created: openscad-$VERSION.zip" + ;; +esac + +rm -rf openscad-$VERSION + diff --git a/scripts/release-linux.sh b/scripts/release-linux.sh new file mode 100755 index 0000000..41a7aa3 --- /dev/null +++ b/scripts/release-linux.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# WARNING: This script might only work with the authors setup... + +VERSION=2010.01 + +set -ex + +# svnclean + +qmake-qt4 VERSION=$VERSION +make + +rm -rf release +mkdir -p release/{bin,lib/openscad,examples} + +cat > release/bin/openscad << "EOT" +#!/bin/bash + +cd "$( dirname "$( type -p $0 )" )" +libdir=$PWD/../lib/openscad/ +cd "$OLDPWD" + +export LD_LIBRARY_PATH="$libdir${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" +exec $libdir/openscad "$@" +EOT + +cp openscad release/lib/openscad/ +gcc -o chrpath_linux chrpath_linux.c +./chrpath_linux -d release/lib/openscad/openscad + +ldd openscad | sed -re 's,.* => ,,; s,[\t ].*,,;' -e '/Qt|boost/ { p; d; };' \ + -e '/lib(audio|CGAL|GLEW|opencsg|png)\.so/ { p; d; };' \ + -e 'd;' | xargs cp -vt release/lib/openscad/ +strip release/lib/openscad/* + +cat > release/install.sh << "EOT" +#!/bin/bash + +# change to the install source directory +cd "$( dirname "$( type -p $0 )" )" + +if ! [ -f bin/openscad -a -d lib/openscad -a -d examples ]; then + echo "Error: Can't change to install source directory!" >&2 + exit 1 +fi + +echo "This will install openscad. Please enter the install prefix" +echo "or press Ctrl-C to abort the install process:" +read -p "[/usr/local]: " prefix + +if [ "$prefix" = "" ]; then + prefix="/usr/local" +fi + +if [ ! -d "$prefix" ]; then + echo; echo "Install prefix \`$prefix' does not exist. Press ENTER to continue" + echo "or press Ctrl-C to abort the install process:" + read -p "press enter to continue> " +fi + +mkdir -p "$prefix"/{bin,lib/openscad} + +if ! [ -w "$prefix"/bin/ -a -w "$prefix"/lib/ ]; then + echo "You does not seam to have write permissions for prefix \`$prefix'!" >&2 + echo "Maybe you should have run this install script using \`sudo'?" >&2 + exit 1 +fi + +echo "Copying application wrappers..." +cp -rv bin/. "$prefix"/bin/ + +echo "Copying application and libraries..." +cp -rv lib/. "$prefix"/lib/ + +echo "Installation finished. Have a nice day." +EOT + +chmod 755 -R release/ + +cp examples/* release/examples/ +chmod 644 -R release/examples/* + diff --git a/scripts/release-macosx.sh b/scripts/release-macosx.sh new file mode 100755 index 0000000..8ac5dc0 --- /dev/null +++ b/scripts/release-macosx.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# +# This script creates a binary release of OpenSCAD for Mac OS X. +# The script will create a file called openscad-.zip +# in the current directory. +# +# Usage: makedmg.sh [-v ] +# -v Version string (e.g. -v 2010.01) +# +# If no version string is given, todays date will be used (YYYY-MM-DD) +# +printUsage() +{ + echo "Usage: $0 -v " + echo + echo " Example: $0 -v 2010.01" +} + +while getopts 'v:' c +do + case $c in + v) VERSION=$OPTARG;; + esac +done + +if test -z "$VERSION"; then + VERSION=`date "+%Y.%m.%d"` +fi + +echo "Building openscad-$VERSION..." +export OPENCSGDIR=$PWD/../OpenCSG-1.2.0 +qmake VERSION=$VERSION CONFIG+=mdi openscad.pro +make clean +make -j2 +echo "Preparing executable.." +mkdir OpenSCAD.app/Contents/Frameworks +cp $OPENCSGDIR/lib/libopencsg.dylib OpenSCAD.app/Contents/Frameworks +cp /opt/local/lib/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks +cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL OpenSCAD.app/Contents/Frameworks +cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui OpenSCAD.app/Contents/Frameworks +cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore OpenSCAD.app/Contents/Frameworks +install_name_tool -change libopencsg.1.dylib @executable_path/../Frameworks/libopencsg.dylib OpenSCAD.app/Contents/MacOS/openscad +install_name_tool -change QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL OpenSCAD.app/Contents/MacOS/openscad +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/MacOS/openscad +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/MacOS/openscad +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/QtOpenGL +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtOpenGL +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtGui +install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/MacOS/openscad +install_name_tool -id libopencsg.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib +install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/libopencsg.dylib +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/libopencsg.dylib +install_name_tool -id libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libGLEW.1.5.1.dylib + +echo "Creating directory structure.." +rm -rf openscad-$VERSION +mkdir -p openscad-$VERSION/examples +cp examples/* openscad-$VERSION/examples/ +chmod -R 644 openscad-$VERSION/examples/* +mv OpenSCAD.app openscad-$VERSION + +echo "Creating archive.." +zip -qr openscad-$VERSION.zip openscad-$VERSION +echo "Mac OS X binary created: openscad-$VERSION.zip" diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh new file mode 100644 index 0000000..540eab9 --- /dev/null +++ b/scripts/release-win32.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# +# This script creates a binary release of OpenSCAD for Mac OS X. +# The script will create a file called openscad-.zip +# in the current directory. +# +# Usage: release-win32.sh [-v ] +# -v Version string (e.g. -v 2010.01) +# +# If no version string is given, todays date will be used (YYYY-MM-DD) +# + +#used for windows +ZIP="/c/Program Files/7-Zip/7z.exe" +ZIPARGS="a -tzip" + +printUsage() +{ + echo "Usage: $0 -v -t " + echo + echo " Example: $0 -v 2010.01 -t release" +} + +OS=OSX +if test "`uname -o`" == "Msys"; then + OS=WIN +fi + +echo "detected OS= $OS" + +while getopts 'v:' c +do + case $c in + v) VERSION=$OPTARG;; + b) TARGET=$OPTARG;; + esac +done + +if test -z "$VERSION"; then + VERSION=`date "+%Y.%m.%d"` +fi + +if test -z "$TARGET"; then + TARGET=release +fi + +echo "Building openscad-$VERSION $CONFIGURATION..." + +case $OS in + OSX) + CONFIG = mdi;; + WIN) + unset CONFIG + export QTDIR=/c/devmingw/qt2009.03 + export QTMAKESPEC=win32-g++ + export PATH=$PATH:/c/devmingw/qt2009.03/bin:/c/devmingw/qt2009.03/qt/bin + ;; +esac + +qmake VERSION=$VERSION CONFIG+=$CONFIG +make clean +if test $OS == WIN; then + #if the following files are missing their tried removal stops the build process on msys + touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp +fi + +make -j2 $TARGET + +echo "Preparing executable..." + +echo "Creating directory structure..." +rm -rf openscad-$VERSION +rm -f openscad-$VERSION.zip +mkdir -p openscad-$VERSION/examples +cp examples/* openscad-$VERSION/examples/ + +case $OS in + OSX) ;; + WIN) + #package + cp win32deps/* openscad-$VERSION + cp $TARGET/openscad.exe openscad-$VERSION + ;; +esac + +echo "Creating directory structure..." +case $OS in + OSX) ;; + WIN) + "$ZIP" $ZIPARGS openscad-$VERSION.zip openscad-$VERSION + ;; +esac + +rm -rf openscad-$VERSION + +echo "binary created: openscad-$VERSION.zip" diff --git a/src/CGAL_renderer.h b/src/CGAL_renderer.h new file mode 100644 index 0000000..3c36db4 --- /dev/null +++ b/src/CGAL_renderer.h @@ -0,0 +1,660 @@ +// Copyright (c) 1997-2002 Max-Planck-Institute Saarbruecken (Germany). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you may redistribute it under +// the terms of the Q Public License version 1.0. +// See the file LICENSE.QPL distributed with CGAL. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.5-branch/Nef_3/include/CGAL/Nef_3/OGL_helper.h $ +// $Id: OGL_helper.h 44713 2008-08-01 15:38:58Z hachenb $ +// +// +// Author(s) : Peter Hachenberger + +#ifndef CGAL_NEF_OPENGL_HELPER_H +#define CGAL_NEF_OPENGL_HELPER_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define CGAL_GLU_TESS_CALLBACK CALLBACK +#else +#define CGAL_GLU_TESS_CALLBACK +#endif + +#ifdef __APPLE__ +# include +#endif + +#if defined __APPLE__ && !defined MAC_OS_X_VERSION_10_5 +#define CGAL_GLU_TESS_DOTS ... +#else +#define CGAL_GLU_TESS_DOTS +#endif + +using namespace CGAL; +using namespace CGAL::OGL; + +namespace OpenSCAD { + + namespace OGL { + +// ---------------------------------------------------------------------------- +// Drawable double types: +// ---------------------------------------------------------------------------- + + typedef CGAL::Simple_cartesian DKernel; + typedef DKernel::Point_3 Double_point; + typedef DKernel::Vector_3 Double_vector; + typedef DKernel::Segment_3 Double_segment; + typedef DKernel::Aff_transformation_3 Affine_3; + + // DPoint = a double point including a mark + class DPoint : public Double_point { + bool m_; + public: + DPoint() {} + DPoint(const Double_point& p, bool m) : Double_point(p) { m_ = m; } + DPoint(const DPoint& p) : Double_point(p) { m_ = p.m_; } + DPoint& operator=(const DPoint& p) + { Double_point::operator=(p); m_ = p.m_; return *this; } + bool mark() const { return m_; } + }; + + // DSegment = a double segment including a mark + class DSegment : public Double_segment { + bool m_; + public: + DSegment() {} + DSegment(const Double_segment& s, bool m) : Double_segment(s) { m_ = m; } + DSegment(const DSegment& s) : Double_segment(s) { m_ = s.m_; } + DSegment& operator=(const DSegment& s) + { Double_segment::operator=(s); m_ = s.m_; return *this; } + bool mark() const { return m_; } + }; + + // Double_triple = a class that stores a triple of double + // coordinates; we need a pointer to the coordinates in a C array + // for OpenGL + class Double_triple { + typedef double* double_ptr; + typedef const double* const_double_ptr; + double coords_[3]; + public: + Double_triple() + { coords_[0]=coords_[1]=coords_[2]=0.0; } + Double_triple(double x, double y, double z) + { coords_[0]=x; coords_[1]=y; coords_[2]=z; } + Double_triple(const Double_triple& t) + { coords_[0]=t.coords_[0]; + coords_[1]=t.coords_[1]; + coords_[2]=t.coords_[2]; + } + Double_triple& operator=(const Double_triple& t) + { coords_[0]=t.coords_[0]; + coords_[1]=t.coords_[1]; + coords_[2]=t.coords_[2]; + return *this; } + operator double_ptr() const + { return const_cast(*this).coords_; } + double operator[](unsigned i) + { CGAL_assertion(i<3); return coords_[i]; } + }; // Double_triple + + static std::ostream& operator << (std::ostream& os, + const Double_triple& t) + { os << "(" << t[0] << "," << t[1] << "," << t[2] << ")"; + return os; } + + + // DFacet stores the facet cycle vertices in a continuus C array + // of three double components, this is necessary due to the OpenGL + // tesselator input format ! + class DFacet { + typedef std::vector Coord_vector; + typedef std::vector Cycle_vector; + Coord_vector coords_; // stores all vertex coordinates + Cycle_vector fc_ends_; // stores entry points of facet cycles + Double_triple normal_; // stores normal and mark of facet + bool mark_; + + public: + typedef Coord_vector::iterator Coord_iterator; + typedef Coord_vector::const_iterator Coord_const_iterator; + + DFacet() {} + + void push_back_vertex(double x, double y, double z) + { coords_.push_back(Double_triple(x,y,z)); } + + DFacet(const DFacet& f) + { coords_ = f.coords_; + fc_ends_ = f.fc_ends_; + normal_ = f.normal_; + mark_ = f.mark_; + } + + DFacet& operator=(const DFacet& f) + { coords_ = f.coords_; + fc_ends_ = f.fc_ends_; + normal_ = f.normal_; + mark_ = f.mark_; + return *this; + } + + ~DFacet() + { coords_.clear(); fc_ends_.clear(); } + + void push_back_vertex(const Double_point& p) + { push_back_vertex(p.x(),p.y(),p.z()); } + + void set_normal(double x, double y, double z, bool m) + { double l = sqrt(x*x + y*y + z*z); + normal_ = Double_triple(x/l,y/l,z/l); mark_ = m; } + + double dx() const { return normal_[0]; } + double dy() const { return normal_[1]; } + double dz() const { return normal_[2]; } + bool mark() const { return mark_; } + double* normal() const + { return static_cast(normal_); } + + void new_facet_cycle() + { fc_ends_.push_back(coords_.size()); } + + unsigned number_of_facet_cycles() const + { return fc_ends_.size(); } + + Coord_iterator facet_cycle_begin(unsigned i) + { CGAL_assertion(i(vertex)); + GLdouble* pu(static_cast(user)); + // CGAL_NEF_TRACEN("vertexCallback coord "< pcache; + if (dataOut) { + GLdouble *n = new GLdouble[3]; + n[0] = coords[0]; + n[1] = coords[1]; + n[2] = coords[2]; + pcache.push_back(n); + *dataOut = n; + } else { + for (std::list::const_iterator i = pcache.begin(); i != pcache.end(); i++) + delete[] *i; + pcache.clear(); + } + } + + + enum { SNC_AXES}; + enum { SNC_BOUNDARY, SNC_SKELETON }; + + class Polyhedron : public CGAL::OGL::OGL_base_object { + public: + std::list vertices_; + std::list edges_; + std::list halffacets_; + + GLuint object_list_; + bool init_; + + Bbox_3 bbox_; + + int style; + std::vector switches; + + typedef std::list::const_iterator Vertex_iterator; + typedef std::list::const_iterator Edge_iterator; + typedef std::list::const_iterator Halffacet_iterator; + + enum RenderColor { + CGAL_NEF3_MARKED_VERTEX_COLOR, + CGAL_NEF3_MARKED_EDGE_COLOR, + CGAL_NEF3_MARKED_FACET_COLOR, + CGAL_NEF3_UNMARKED_VERTEX_COLOR, + CGAL_NEF3_UNMARKED_EDGE_COLOR, + CGAL_NEF3_UNMARKED_FACET_COLOR, + NUM_COLORS + }; + static unsigned char colors[NUM_COLORS][3]; + public: + Polyhedron() : bbox_(-1,-1,-1,1,1,1), switches(1) { + object_list_ = 0; + init_ = false; + style = SNC_BOUNDARY; + switches[SNC_AXES] = false; + } + + ~Polyhedron() + { if (object_list_) glDeleteLists(object_list_, 4); } + + void push_back(const Double_point& p, bool m) { + vertices_.push_back(DPoint(p,m)); + } + void push_back(const Double_segment& s, bool m) + { edges_.push_back(DSegment(s,m)); } + void push_back(const DFacet& f) + { halffacets_.push_back(f); } + + void toggle(int index) { + switches[index] = !switches[index]; + } + + void set_style(int index) { + style = index; + } + + bool is_initialized() const { return init_; } + + Bbox_3 bbox() const { return bbox_; } + Bbox_3& bbox() { return bbox_; } + + void draw(Vertex_iterator v) const { + // CGAL_NEF_TRACEN("drawing vertex "<<*v); + unsigned char *c = v->mark() ? colors[CGAL_NEF3_UNMARKED_VERTEX_COLOR] : colors[CGAL_NEF3_MARKED_VERTEX_COLOR]; + glPointSize(10); + glColor3ubv(c); + glBegin(GL_POINTS); + glVertex3d(v->x(),v->y(),v->z()); +#ifdef CGAL_NEF_EMPHASIZE_VERTEX + glColor3ub(255,0,0); + glVertex3d(CGAL_NEF_EMPHASIZE_VERTEX); +#endif + glEnd(); + } + + void draw(Edge_iterator e) const { + // CGAL_NEF_TRACEN("drawing edge "<<*e); + Double_point p = e->source(), q = e->target(); + unsigned char *c = e->mark() ? colors[CGAL_NEF3_UNMARKED_EDGE_COLOR] : colors[CGAL_NEF3_MARKED_EDGE_COLOR]; + glLineWidth(5); + glColor3ubv(c); + glBegin(GL_LINE_STRIP); + glVertex3d(p.x(), p.y(), p.z()); + glVertex3d(q.x(), q.y(), q.z()); + glEnd(); + } + + void draw(Halffacet_iterator f) const { + // CGAL_NEF_TRACEN("drawing facet "<<(f->debug(),"")); + GLUtesselator* tess_ = gluNewTess(); + gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &vertexCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_COMBINE), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &combineCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_BEGIN), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &beginCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_END), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &endCallback); + gluTessCallback(tess_, GLenum(GLU_TESS_ERROR), + (GLvoid (CGAL_GLU_TESS_CALLBACK *)(CGAL_GLU_TESS_DOTS)) &errorCallback); + gluTessProperty(tess_, GLenum(GLU_TESS_WINDING_RULE), + GLU_TESS_WINDING_POSITIVE); + + DFacet::Coord_const_iterator cit; + unsigned char *c = f->mark() ? colors[CGAL_NEF3_UNMARKED_FACET_COLOR] : colors[CGAL_NEF3_MARKED_FACET_COLOR]; + glColor3ubv(c); + gluTessBeginPolygon(tess_,f->normal()); + // CGAL_NEF_TRACEN(" "); + // CGAL_NEF_TRACEN("Begin Polygon"); + gluTessNormal(tess_,f->dx(),f->dy(),f->dz()); + // forall facet cycles of f: + for(unsigned i = 0; i < f->number_of_facet_cycles(); ++i) { + gluTessBeginContour(tess_); + // CGAL_NEF_TRACEN(" Begin Contour"); + // put all vertices in facet cycle into contour: + for(cit = f->facet_cycle_begin(i); + cit != f->facet_cycle_end(i); ++cit) { + gluTessVertex(tess_, *cit, *cit); + // CGAL_NEF_TRACEN(" add Vertex"); + } + gluTessEndContour(tess_); + // CGAL_NEF_TRACEN(" End Contour"); + } + gluTessEndPolygon(tess_); + // CGAL_NEF_TRACEN("End Polygon"); + gluDeleteTess(tess_); + combineCallback(NULL, NULL, NULL, NULL); + } + + void construct_axes() const + { + glLineWidth(2.0); + // red x-axis + glColor3f(1.0,0.0,0.0); + glBegin(GL_LINES); + glVertex3f(0.0,0.0,0.0); + glVertex3f(5000.0,0.0,0.0); + glEnd(); + // green y-axis + glColor3f(0.0,1.0,0.0); + glBegin(GL_LINES); + glVertex3f(0.0,0.0,0.0); + glVertex3f(0.0,5000.0,0.0); + glEnd(); + // blue z-axis and equator + glColor3f(0.0,0.0,1.0); + glBegin(GL_LINES); + glVertex3f(0.0,0.0,0.0); + glVertex3f(0.0,0.0,5000.0); + glEnd(); + // six coordinate points in pink: + glPointSize(10); + glBegin(GL_POINTS); + glColor3f(1.0,0.0,0.0); + glVertex3d(5,0,0); + glColor3f(0.0,1.0,0.0); + glVertex3d(0,5,0); + glColor3f(0.0,0.0,1.0); + glVertex3d(0,0,5); + glEnd(); + } + + + void fill_display_lists() { + glNewList(object_list_, GL_COMPILE); + Vertex_iterator v; + for(v=vertices_.begin();v!=vertices_.end();++v) + draw(v); + glEndList(); + + glNewList(object_list_+1, GL_COMPILE); + Edge_iterator e; + for(e=edges_.begin();e!=edges_.end();++e) + draw(e); + glEndList(); + + glNewList(object_list_+2, GL_COMPILE); + Halffacet_iterator f; + for(f=halffacets_.begin();f!=halffacets_.end();++f) + draw(f); + glEndList(); + + glNewList(object_list_+3, GL_COMPILE); // axes: + construct_axes(); + glEndList(); + + } + + void init() { + if (init_) return; + init_ = true; + switches[SNC_AXES] = false; + style = SNC_BOUNDARY; + object_list_ = glGenLists(4); + CGAL_assertion(object_list_); + fill_display_lists(); + } + + + void draw() const + { + if (!is_initialized()) const_cast(*this).init(); + double l = (std::max)( (std::max)( bbox().xmax() - bbox().xmin(), + bbox().ymax() - bbox().ymin()), + bbox().zmax() - bbox().zmin()); + if ( l < 1) // make sure that a single point doesn't screw up here + l = 1; + glScaled( 4.0/l, 4.0/l, 4.0/l); + glTranslated( -(bbox().xmax() + bbox().xmin()) / 2.0, + -(bbox().ymax() + bbox().ymin()) / 2.0, + -(bbox().zmax() + bbox().zmin()) / 2.0); + if (style == SNC_BOUNDARY) { + //glEnable(GL_LIGHTING); + glCallList(object_list_+2); // facets + //glDisable(GL_LIGHTING); + } + // move edges and vertices a bit towards the view-point, + // i.e., 1/100th of the unit vector in camera space + // double f = l / 4.0 / 100.0; + // glTranslated( z_vec[0] * f, z_vec[1] * f, z_vec[2] * f); + glCallList(object_list_+1); // edges + glCallList(object_list_); // vertices + if (switches[SNC_AXES]) glCallList(object_list_+3); // axis + } + + void debug(std::ostream& os = std::cerr) const + { + os << "OGL::Polyhedron" << std::endl; + os << "Vertices:" << std::endl; + Vertex_iterator v; + for(v=vertices_.begin();v!=vertices_.end();++v) + os << " "<<*v<<", mark="<mark()<debug(); os << std::endl; + os << std::endl; + } + + }; // Polyhedron + unsigned char Polyhedron::colors[][3] = { + {0xb7, 0xe8, 0x5c}, + {0xab, 0xd8, 0x56}, + {0x9d, 0xcb, 0x51}, + {0xff, 0xf6, 0x7c}, + {0xff, 0xec, 0x5e}, + {0xf9, 0xd7, 0x2c} + }; + + template + class Nef3_Converter { + typedef typename Nef_polyhedron::SNC_structure SNC_structure; + typedef CGAL::SNC_decorator Base; + typedef CGAL::SNC_FM_decorator FM_decorator; + + public: + typedef typename SNC_structure::Vertex_const_iterator Vertex_const_iterator; + typedef typename SNC_structure::Halfedge_const_iterator Halfedge_const_iterator; + typedef typename SNC_structure::Halffacet_const_iterator Halffacet_const_iterator; + typedef typename SNC_structure::Halffacet_cycle_const_iterator Halffacet_cycle_const_iterator; + + typedef typename SNC_structure::Object_const_handle Object_const_handle; + typedef typename SNC_structure::SHalfedge_const_handle SHalfedge_const_handle; + typedef typename SNC_structure::SHalfloop_const_handle SHalfloop_const_handle; + + typedef typename SNC_structure::Vertex_const_handle Vertex_const_handle; + typedef typename SNC_structure::Halfedge_const_handle Halfedge_const_handle; + typedef typename SNC_structure::Halffacet_const_handle Halffacet_const_handle; + + typedef typename SNC_structure::Point_3 Point_3; + typedef typename SNC_structure::Vector_3 Vector_3; + typedef typename SNC_structure::Segment_3 Segment_3; + typedef typename SNC_structure::Plane_3 Plane_3; + typedef typename SNC_structure::Mark Mark; + typedef typename SNC_structure::SHalfedge_around_facet_const_circulator + SHalfedge_around_facet_const_circulator; + + private: + static OGL::Double_point double_point(const Point_3& p) + { return OGL::Double_point(CGAL::to_double(p.x()), + CGAL::to_double(p.y()), + CGAL::to_double(p.z())); } + + static OGL::Double_segment double_segment(const Segment_3& s) + { return OGL::Double_segment(double_point(s.source()), + double_point(s.target())); } + + static void draw(Vertex_const_handle v, const Nef_polyhedron& , + Polyhedron& P) { + Point_3 bp = v->point(); + // CGAL_NEF_TRACEN("vertex " << bp); + P.push_back(double_point(bp), v->mark()); + } + + static void draw(Halfedge_const_handle e, const Nef_polyhedron& , + Polyhedron& P) { + Vertex_const_handle s = e->source(); + Vertex_const_handle t = e->twin()->source(); + Segment_3 seg(s->point(),t->point()); + // CGAL_NEF_TRACEN("edge " << seg); + P.push_back(double_segment(seg), e->mark()); + } + + static void draw(Halffacet_const_handle f, const Nef_polyhedron& , + Polyhedron& P) { + OGL::DFacet g; + Halffacet_cycle_const_iterator fc; // all facet cycles: + CGAL_forall_facet_cycles_of(fc,f) + if ( fc.is_shalfedge() ) { // non-trivial facet cycle + g.new_facet_cycle(); + SHalfedge_const_handle h = fc; + SHalfedge_around_facet_const_circulator hc(h), he(hc); + CGAL_For_all(hc,he){ // all vertex coordinates in facet cycle + Point_3 sp = hc->source()->source()->point(); + // CGAL_NEF_TRACEN(" ");CGAL_NEF_TRACEN("facet" << sp); + g.push_back_vertex(double_point(sp)); + } + } + Vector_3 v = f->plane().orthogonal_vector(); + g.set_normal(CGAL::to_double(v.x()), + CGAL::to_double(v.y()), + CGAL::to_double(v.z()), + f->mark()); + P.push_back(g); + } + + // Returns the bounding box of the finite vertices of the polyhedron. + // Returns $[-1,+1]^3$ as bounding box if no finite vertex exists. + + static Bbox_3 bounded_bbox(const Nef_polyhedron& N) { + bool first_vertex = true; + Bbox_3 bbox( -1.0, -1.0, -1.0, 1.0, 1.0, 1.0); + Vertex_const_iterator vi; + CGAL_forall_vertices(vi, N) { + Point_3 p = vi->point(); + double x = CGAL::to_double(p.hx()); + double y = CGAL::to_double(p.hy()); + double z = CGAL::to_double(p.hz()); + double w = CGAL::to_double(p.hw()); + if (N.is_standard(vi)) { + if(first_vertex) { + bbox = Bbox_3(x/w, y/w, z/w, x/w, y/w, z/w); + first_vertex = false; + } else { + bbox = bbox + Bbox_3(x/w, y/w, z/w, x/w, y/w, z/w); + first_vertex = false; + } + } + } + return bbox; + } + + static void set_R(Bbox_3& bbox, const Nef_polyhedron& N) { + if(N.is_standard_kernel()) return; + double size = abs(bbox.xmin()); + if(size < bbox.xmax()) size = bbox.xmax(); + if(size < bbox.ymin()) size = bbox.ymin(); + if(size < bbox.ymax()) size = bbox.ymax(); + if(size < bbox.zmin()) size = bbox.zmin(); + if(size < bbox.zmax()) size = bbox.zmax(); + N.set_size_of_infimaximal_box(size*50); + // CGAL_NEF_TRACEN("set infi box size to " << size); + Vertex_const_iterator vi; + CGAL_forall_vertices(vi, N) + if(N.is_standard(vi)) + return; + bbox = Bbox_3(bbox.xmin()*10,bbox.ymin()*10,bbox.zmin()*10, + bbox.xmax()*10,bbox.ymax()*10,bbox.zmax()*10); + } + public: + static void setColor(Polyhedron::RenderColor color_index, + unsigned char r, unsigned char g, unsigned char b) { + assert(color_index < Polyhedron::NUM_COLORS); + Polyhedron::colors[color_index][0] = r; + Polyhedron::colors[color_index][1] = g; + Polyhedron::colors[color_index][2] = b; + } + + static void convert_to_OGLPolyhedron(const Nef_polyhedron& N, Polyhedron* P) { + Bbox_3 bbox(bounded_bbox(N)); + set_R(bbox,N); + P->bbox() = bbox; + Vertex_const_iterator v; + CGAL_forall_vertices(v,*N.sncp()) draw(v,N,*P); + Halfedge_const_iterator e; + CGAL_forall_edges(e,*N.sncp()) draw(e,N,*P); + Halffacet_const_iterator f; + CGAL_forall_facets(f,*N.sncp()) draw(f,N,*P); + } + + }; // Nef3_Converter + + } // namespace OGL + +} // namespace OpenSCAD + +#endif // CGAL_NEF_OPENGL_HELPER_H diff --git a/src/EventFilter.h b/src/EventFilter.h new file mode 100644 index 0000000..c3942b5 --- /dev/null +++ b/src/EventFilter.h @@ -0,0 +1,28 @@ +#ifndef FILTER_H_ +#define FILTER_H_ + +#include +#include +#include "MainWindow.h" + +class EventFilter : public QObject +{ + Q_OBJECT; + +public: + EventFilter(QObject *parent) : QObject(parent) {} +protected: + bool eventFilter(QObject *obj, QEvent *event) { + // Handle Apple event for opening files + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent *foe = static_cast(event); + MainWindow::requestOpenFile(foe->file()); + return true; + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } + } +}; + +#endif diff --git a/src/GLView.h b/src/GLView.h new file mode 100644 index 0000000..dd896fc --- /dev/null +++ b/src/GLView.h @@ -0,0 +1,81 @@ +#ifndef GLVIEW_H_ +#define GLVIEW_H_ + +#ifdef ENABLE_OPENCSG +// this must be included before the GL headers +# include +#endif + +#include +#include + +class GLView : public QGLWidget +{ + Q_OBJECT + Q_PROPERTY(bool showAxes READ showAxes WRITE setShowAxes); + Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs); + Q_PROPERTY(bool orthoMode READ orthoMode WRITE setOrthoMode); + +public: + GLView(QWidget *parent = NULL); + void setRenderFunc(void (*func)(void*), void *userdata); +#ifdef ENABLE_OPENCSG + bool hasOpenCSGSupport() { return this->opencsg_support; } +#endif + // Properties + bool showAxes() const { return this->showaxes; } + void setShowAxes(bool enabled) { this->showaxes = enabled; } + bool showCrosshairs() const { return this->showcrosshairs; } + void setShowCrosshairs(bool enabled) { this->showcrosshairs = enabled; } + bool orthoMode() const { return this->orthomode; } + void setOrthoMode(bool enabled) { this->orthomode = enabled; } + + QLabel *statusLabel; + double object_rot_x; + double object_rot_y; + double object_rot_z; + double object_trans_x; + double object_trans_y; + double object_trans_z; + GLint shaderinfo[11]; + +private: + void (*renderfunc)(void*); + void *renderfunc_vp; + + bool showaxes; + bool showcrosshairs; + bool orthomode; + + double viewer_distance; + + double w_h_ratio; + +#ifdef ENABLE_OPENCSG + bool opencsg_support; +#endif + + bool mouse_drag_active; + int last_mouse_x; + int last_mouse_y; + + void keyPressEvent(QKeyEvent *event); + void wheelEvent(QWheelEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + +#ifdef ENABLE_OPENCSG +private slots: + void display_opengl20_warning(); +#endif + +signals: + void doAnimateUpdate(); +}; + +#endif diff --git a/src/MainWindow.h b/src/MainWindow.h new file mode 100644 index 0000000..bb1bdc4 --- /dev/null +++ b/src/MainWindow.h @@ -0,0 +1,142 @@ +#ifndef MAINWINDOW_H_ +#define MAINWINDOW_H_ + +#include +#include "ui_MainWindow.h" +#include "openscad.h" + +class MainWindow : public QMainWindow, public Ui::MainWindow +{ + Q_OBJECT + +public: + static QPointer current_win; + static void requestOpenFile(const QString &filename); + + QString fileName; + class Highlighter *highlighter; + + class Preferences *prefs; + + QTimer *animate_timer; + double tval, fps, fsteps; + + Context root_ctx; + AbstractModule *root_module; // Result of parsing + ModuleInstantiation root_inst; // Top level instance + AbstractNode *absolute_root_node; // Result of tree evaluation + AbstractNode *root_node; // Root if the root modifier (!) is used + + CSGTerm *root_raw_term; // Result of CSG term rendering + CSGTerm *root_norm_term; // Normalized CSG products + CSGChain *root_chain; +#ifdef ENABLE_CGAL + CGAL_Nef_polyhedron *root_N; + bool recreate_cgal_ogl_p; + void *cgal_ogl_p; + PolySet *cgal_ogl_ps; +#endif + + QVector highlight_terms; + CSGChain *highlights_chain; + QVector background_terms; + CSGChain *background_chain; + QString last_compiled_doc; + bool enableOpenCSG; + + static const int maxRecentFiles = 10; + QAction *actionRecentFile[maxRecentFiles]; + QString examplesdir; + + MainWindow(const char *filename = 0); + ~MainWindow(); + +protected: + void closeEvent(QCloseEvent *event); + +private slots: + void updatedFps(); + void updateTVal(); + void setFileName(const QString &filename); + void setFont(const QString &family, uint size); + +private: + void openFile(const QString &filename); + void load(); + AbstractNode *find_root_tag(AbstractNode *n); + void compile(bool procevents); + bool maybeSave(); + +private slots: + void actionNew(); + void actionOpen(); + void actionOpenRecent(); + void actionOpenExample(); + void clearRecentFiles(); + void updateRecentFileActions(); + void actionSave(); + void actionSaveAs(); + void actionReload(); + +private slots: + void editIndent(); + void editUnindent(); + void editComment(); + void editUncomment(); + void pasteViewportTranslation(); + void pasteViewportRotation(); + void hideEditor(); + void preferences(); + +private slots: + void actionReloadCompile(); + void actionCompile(); +#ifdef ENABLE_CGAL + void actionRenderCGAL(); +#endif + void actionDisplayAST(); + void actionDisplayCSGTree(); + void actionDisplayCSGProducts(); + void actionExportSTLorOFF(bool stl_mode); + void actionExportSTL(); + void actionExportOFF(); + void actionExportDXF(); + void actionFlushCaches(); + +public: + void viewModeActionsUncheck(); + +public slots: +#ifdef ENABLE_OPENCSG + void viewModeOpenCSG(); +#endif +#ifdef ENABLE_CGAL + void viewModeCGALSurface(); + void viewModeCGALGrid(); +#endif + void viewModeThrownTogether(); + void viewModeShowEdges(); + void viewModeShowAxes(); + void viewModeShowCrosshairs(); + void viewModeAnimate(); + void viewAngleTop(); + void viewAngleBottom(); + void viewAngleLeft(); + void viewAngleRight(); + void viewAngleFront(); + void viewAngleBack(); + void viewAngleDiagonal(); + void viewCenter(); + void viewPerspective(); + void viewOrthogonal(); + void hideConsole(); + void animateUpdateDocChanged(); + void animateUpdate(); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + void helpAbout(); + void helpManual(); + void quit(); +}; + +#endif diff --git a/src/Preferences.cc b/src/Preferences.cc new file mode 100644 index 0000000..cfc2bdb --- /dev/null +++ b/src/Preferences.cc @@ -0,0 +1,208 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "Preferences.h" + +#include +#include +#include + +Preferences *Preferences::instance = NULL; + +Preferences::Preferences(QWidget *parent) : QMainWindow(parent) +{ + setupUi(this); + + // Setup default settings + this->defaultmap["3dview/colorscheme"] = this->colorSchemeChooser->currentItem()->text(); + this->defaultmap["editor/fontfamily"] = this->fontChooser->currentText(); + this->defaultmap["editor/fontsize"] = this->fontSize->currentText().toUInt(); + + // Toolbar + QActionGroup *group = new QActionGroup(this); + group->addAction(prefsAction3DView); + group->addAction(prefsActionEditor); + group->addAction(prefsActionAdvanced); + connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); + + prefsAction3DView->setChecked(true); + this->actionTriggered(this->prefsAction3DView); + + // 3D View pane + this->colorschemes["Cornfield"][BACKGROUND_COLOR] = QColor(0xff, 0xff, 0xe5); + this->colorschemes["Cornfield"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colorschemes["Cornfield"][OPENCSG_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colorschemes["Cornfield"][CGAL_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colorschemes["Cornfield"][CGAL_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colorschemes["Cornfield"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Cornfield"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + + this->colorschemes["Metallic"][BACKGROUND_COLOR] = QColor(0xaa, 0xaa, 0xff); + this->colorschemes["Metallic"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); + this->colorschemes["Metallic"][OPENCSG_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); + this->colorschemes["Metallic"][CGAL_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); + this->colorschemes["Metallic"][CGAL_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); + this->colorschemes["Metallic"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Metallic"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + + this->colorschemes["Sunset"][BACKGROUND_COLOR] = QColor(0xaa, 0x44, 0x44); + this->colorschemes["Sunset"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); + this->colorschemes["Sunset"][OPENCSG_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); + this->colorschemes["Sunset"][CGAL_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); + this->colorschemes["Sunset"][CGAL_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); + this->colorschemes["Sunset"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Sunset"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + + // Editor pane + QFontDatabase db; + foreach(int size, db.standardSizes()) { + this->fontSize->addItem(QString::number(size)); + } + + connect(this->colorSchemeChooser, SIGNAL(itemSelectionChanged()), + this, SLOT(colorSchemeChanged())); + connect(this->fontChooser, SIGNAL(activated(const QString &)), + this, SLOT(fontFamilyChanged(const QString &))); + connect(this->fontSize, SIGNAL(editTextChanged(const QString &)), + this, SLOT(fontSizeChanged(const QString &))); + + updateGUI(); +} + +Preferences::~Preferences() +{ + removeDefaultSettings(); +} + +void +Preferences::actionTriggered(QAction *action) +{ + if (action == this->prefsAction3DView) { + this->stackedWidget->setCurrentWidget(this->page3DView); + } + else if (action == this->prefsActionEditor) { + this->stackedWidget->setCurrentWidget(this->pageEditor); + } + else if (action == this->prefsActionAdvanced) { + this->stackedWidget->setCurrentWidget(this->pageAdvanced); + } +} + +void Preferences::colorSchemeChanged() +{ + QSettings settings; + settings.setValue("3dview/colorscheme", this->colorSchemeChooser->currentItem()->text()); + + emit requestRedraw(); +} + +const QColor &Preferences::color(RenderColor idx) +{ + return this->colorschemes[getValue("3dview/colorscheme").toString()][idx]; +} + +void Preferences::fontFamilyChanged(const QString &family) +{ + QSettings settings; + settings.setValue("editor/fontfamily", family); + emit fontChanged(family, getValue("editor/fontsize").toUInt()); +} + +void Preferences::fontSizeChanged(const QString &size) +{ + uint intsize = size.toUInt(); + QSettings settings; + settings.setValue("editor/fontsize", intsize); + emit fontChanged(getValue("editor/fontfamily").toString(), intsize); +} + +void Preferences::keyPressEvent(QKeyEvent *e) +{ +#ifdef Q_WS_MAC + if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { + close(); + } else +#endif + if ((e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_W) || + e->key() == Qt::Key_Escape) { + close(); + } +} + +/*! + Removes settings that are the same as the default settings to avoid + overwriting future changes to default settings. + */ +void Preferences::removeDefaultSettings() +{ + QSettings settings; + for (QSettings::SettingsMap::const_iterator iter = this->defaultmap.begin(); + iter != this->defaultmap.end(); + iter++) { + if (settings.value(iter.key()) == iter.value()) { + settings.remove(iter.key()); + } + } +} + +QVariant Preferences::getValue(const QString &key) const +{ + QSettings settings; + return settings.value(key, this->defaultmap[key]); +} + +void Preferences::updateGUI() +{ + QList found = + this->colorSchemeChooser->findItems(getValue("3dview/colorscheme").toString(), + Qt::MatchExactly); + if (!found.isEmpty()) this->colorSchemeChooser->setCurrentItem(found.first()); + + QString fontfamily = getValue("editor/fontfamily").toString(); + int fidx = this->fontChooser->findText(fontfamily); + if (fidx >= 0) { + this->fontChooser->setCurrentIndex(fidx); + } + + QString fontsize = getValue("editor/fontsize").toString(); + int sidx = this->fontSize->findText(fontsize); + if (sidx >= 0) { + this->fontSize->setCurrentIndex(sidx); + } + else { + this->fontSize->setEditText(fontsize); + } +} + +void Preferences::apply() const +{ + emit fontChanged(getValue("editor/fontfamily").toString(), getValue("editor/fontsize").toUInt()); + emit requestRedraw(); +} + diff --git a/src/Preferences.h b/src/Preferences.h new file mode 100644 index 0000000..39599fd --- /dev/null +++ b/src/Preferences.h @@ -0,0 +1,54 @@ +#ifndef PREFERENCES_H_ +#define PREFERENCES_H_ + +#include +#include +#include "ui_Preferences.h" + +class Preferences : public QMainWindow, public Ui::Preferences +{ + Q_OBJECT; + +public: + ~Preferences(); + static Preferences *inst() { if (!instance) instance = new Preferences(); return instance; } + + 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 + }; + const QColor &color(RenderColor idx); + QVariant getValue(const QString &key) const; + void apply() const; + +public slots: + void actionTriggered(class QAction *); + void colorSchemeChanged(); + void fontFamilyChanged(const QString &); + void fontSizeChanged(const QString &); + +signals: + void requestRedraw() const; + void fontChanged(const QString &family, uint size) const; + +private: + Preferences(QWidget *parent = NULL); + void keyPressEvent(QKeyEvent *e); + void updateGUI(); + void removeDefaultSettings(); + + QSettings::SettingsMap defaultmap; + QHash > colorschemes; + + static Preferences *instance; +}; + +#endif diff --git a/src/context.cc b/src/context.cc new file mode 100644 index 0000000..15a2ea6 --- /dev/null +++ b/src/context.cc @@ -0,0 +1,103 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" +#include "printutils.h" + +Context::Context(const Context *parent) +{ + this->parent = parent; + functions_p = NULL; + modules_p = NULL; + inst_p = NULL; + ctx_stack.append(this); +} + +Context::~Context() +{ + ctx_stack.pop_back(); +} + +void Context::args(const QVector &argnames, const QVector &argexpr, + const QVector &call_argnames, const QVector &call_argvalues) +{ + for (int i=0; ievaluate(this->parent) : Value()); + } + + int posarg = 0; + for (int i=0; i Context::ctx_stack; + +void Context::set_variable(QString name, Value value) +{ + if (name.startsWith("$")) + config_variables[name] = value; + else + variables[name] = value; +} + +Value Context::lookup_variable(QString name, bool silent) const +{ + if (name.startsWith("$")) { + for (int i = ctx_stack.size()-1; i >= 0; i--) { + if (ctx_stack[i]->config_variables.contains(name)) + return ctx_stack[i]->config_variables[name]; + } + return Value(); + } + if (variables.contains(name)) + return variables[name]; + if (parent) + return parent->lookup_variable(name, silent); + if (!silent) + PRINTA("WARNING: Ignoring unkown variable '%1'.", name); + return Value(); +} + +Value Context::evaluate_function(QString name, const QVector &argnames, const QVector &argvalues) const +{ + if (functions_p && functions_p->contains(name)) + return functions_p->value(name)->evaluate(this, argnames, argvalues); + if (parent) + return parent->evaluate_function(name, argnames, argvalues); + PRINTA("WARNING: Ignoring unkown function '%1'.", name); + return Value(); +} + +AbstractNode *Context::evaluate_module(const ModuleInstantiation *inst) const +{ + if (modules_p && modules_p->contains(inst->modname)) + return modules_p->value(inst->modname)->evaluate(this, inst); + if (parent) + return parent->evaluate_module(inst); + PRINTA("WARNING: Ignoring unkown module '%1'.", inst->modname); + return NULL; +} + diff --git a/src/control.cc b/src/control.cc new file mode 100644 index 0000000..19c5255 --- /dev/null +++ b/src/control.cc @@ -0,0 +1,166 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +enum control_type_e { + CHILD, + ECHO, + ASSIGN, + FOR, + INT_FOR, + IF +}; + +class ControlModule : public AbstractModule +{ +public: + control_type_e type; + ControlModule(control_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +void for_eval(AbstractNode *node, int l, const QVector &call_argnames, const QVector &call_argvalues, const QVector arg_children, const Context *arg_context) +{ + if (call_argnames.size() > l) { + QString it_name = call_argnames[l]; + Value it_values = call_argvalues[l]; + Context c(arg_context); + if (it_values.type == Value::RANGE) { + double range_begin = it_values.range_begin; + double range_end = it_values.range_end; + double range_step = it_values.range_step; + if (range_end < range_begin) { + double t = range_begin; + range_begin = range_end; + range_end = t; + } + if (range_step > 0 && (range_begin-range_end)/range_step < 10000) { + for (double i = range_begin; i <= range_end; i += range_step) { + c.set_variable(it_name, Value(i)); + for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); + } + } + } + else if (it_values.type == Value::VECTOR) { + for (int i = 0; i < it_values.vec.size(); i++) { + c.set_variable(it_name, *it_values.vec[i]); + for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); + } + } + else { + for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); + } + } else { + foreach (ModuleInstantiation *v, arg_children) { + AbstractNode *n = v->evaluate(arg_context); + if (n != NULL) + node->children.append(n); + } + } +} + +AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation *inst) const +{ + if (type == CHILD) + { + int n = 0; + if (inst->argvalues.size() > 0) { + double v; + if (inst->argvalues[0].getnum(v)) + n = v; + } + for (int i = Context::ctx_stack.size()-1; i >= 0; i--) { + const Context *c = Context::ctx_stack[i]; + if (c->inst_p) { + if (n < c->inst_p->children.size()) + return c->inst_p->children[n]->evaluate(c->inst_p->ctx); + return NULL; + } + c = c->parent; + } + return NULL; + } + + AbstractNode *node; + + if (type == INT_FOR) + node = new AbstractIntersectionNode(inst); + else + node = new AbstractNode(inst); + + if (type == ECHO) + { + QString msg = QString("ECHO: "); + for (int i = 0; i < inst->argnames.size(); i++) { + if (i > 0) + msg += QString(", "); + if (!inst->argnames[i].isEmpty()) + msg += inst->argnames[i] + QString(" = "); + msg += inst->argvalues[i].dump(); + } + PRINT(msg); + } + + if (type == ASSIGN) + { + Context c(inst->ctx); + for (int i = 0; i < inst->argnames.size(); i++) { + if (!inst->argnames[i].isEmpty()) + c.set_variable(inst->argnames[i], inst->argvalues[i]); + } + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(&c); + if (n != NULL) + node->children.append(n); + } + } + + if (type == FOR || type == INT_FOR) + { + for_eval(node, 0, inst->argnames, inst->argvalues, inst->children, inst->ctx); + } + + if (type == IF) + { + if (inst->argvalues.size() > 0 && inst->argvalues[0].type == Value::BOOL && inst->argvalues[0].b) + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n != NULL) + node->children.append(n); + } + } + + return node; +} + +void register_builtin_control() +{ + builtin_modules["child"] = new ControlModule(CHILD); + builtin_modules["echo"] = new ControlModule(ECHO); + builtin_modules["assign"] = new ControlModule(ASSIGN); + builtin_modules["for"] = new ControlModule(FOR); + builtin_modules["intersection_for"] = new ControlModule(INT_FOR); + builtin_modules["if"] = new ControlModule(IF); +} + diff --git a/src/csgops.cc b/src/csgops.cc new file mode 100644 index 0000000..be29ede --- /dev/null +++ b/src/csgops.cc @@ -0,0 +1,162 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +enum csg_type_e { + CSG_TYPE_UNION, + CSG_TYPE_DIFFERENCE, + CSG_TYPE_INTERSECTION +}; + +class CsgModule : public AbstractModule +{ +public: + csg_type_e type; + CsgModule(csg_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class CsgNode : public AbstractNode +{ +public: + csg_type_e type; + CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { } +#ifdef ENABLE_CGAL + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst) const +{ + CsgNode *node = new CsgNode(inst, type); + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n != NULL) + node->children.append(n); + } + return node; +} + +#ifdef ENABLE_CGAL + +CGAL_Nef_polyhedron CsgNode::render_cgal_nef_polyhedron() const +{ + QString cache_id = mk_cache_id(); + if (cgal_nef_cache.contains(cache_id)) { + progress_report(); + PRINT(cgal_nef_cache[cache_id]->msg); + return cgal_nef_cache[cache_id]->N; + } + + print_messages_push(); + + bool first = true; + CGAL_Nef_polyhedron N; + foreach (AbstractNode *v, children) { + if (v->modinst->tag_background) + continue; + if (first) { + N = v->render_cgal_nef_polyhedron(); + if (N.dim != 0) + first = false; + } else if (N.dim == 2) { + if (type == CSG_TYPE_UNION) { + N.p2 += v->render_cgal_nef_polyhedron().p2; + } else if (type == CSG_TYPE_DIFFERENCE) { + N.p2 -= v->render_cgal_nef_polyhedron().p2; + } else if (type == CSG_TYPE_INTERSECTION) { + N.p2 *= v->render_cgal_nef_polyhedron().p2; + } + } else if (N.dim == 3) { + if (type == CSG_TYPE_UNION) { + N.p3 += v->render_cgal_nef_polyhedron().p3; + } else if (type == CSG_TYPE_DIFFERENCE) { + N.p3 -= v->render_cgal_nef_polyhedron().p3; + } else if (type == CSG_TYPE_INTERSECTION) { + N.p3 *= v->render_cgal_nef_polyhedron().p3; + } + } + } + + cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); + print_messages_pop(); + progress_report(); + + return N; +} + +#endif /* ENABLE_CGAL */ + +CSGTerm *CsgNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + CSGTerm *t1 = NULL; + foreach (AbstractNode *v, children) { + CSGTerm *t2 = v->render_csg_term(m, highlights, background); + if (t2 && !t1) { + t1 = t2; + } else if (t2 && t1) { + if (type == CSG_TYPE_UNION) { + t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); + } else if (type == CSG_TYPE_DIFFERENCE) { + t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2); + } else if (type == CSG_TYPE_INTERSECTION) { + t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); + } + } + } + if (t1 && modinst->tag_highlight && highlights) + highlights->append(t1->link()); + if (t1 && modinst->tag_background && background) { + background->append(t1); + return NULL; + } + return t1; +} + +QString CsgNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text = indent + QString("n%1: ").arg(idx); + if (type == CSG_TYPE_UNION) + text += "union() {\n"; + if (type == CSG_TYPE_DIFFERENCE) + text += "difference() {\n"; + if (type == CSG_TYPE_INTERSECTION) + text += "intersection() {\n"; + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + } + return dump_cache; +} + +void register_builtin_csgops() +{ + builtin_modules["union"] = new CsgModule(CSG_TYPE_UNION); + builtin_modules["difference"] = new CsgModule(CSG_TYPE_DIFFERENCE); + builtin_modules["intersection"] = new CsgModule(CSG_TYPE_INTERSECTION); +} + diff --git a/src/csgterm.cc b/src/csgterm.cc new file mode 100644 index 0000000..ebad060 --- /dev/null +++ b/src/csgterm.cc @@ -0,0 +1,207 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" + +CSGTerm::CSGTerm(PolySet *polyset, double m[20], QString label) +{ + this->type = TYPE_PRIMITIVE; + this->polyset = polyset; + this->label = label; + this->left = NULL; + this->right = NULL; + for (int i = 0; i < 20; i++) + this->m[i] = m[i]; + refcounter = 1; +} + +CSGTerm::CSGTerm(type_e type, CSGTerm *left, CSGTerm *right) +{ + this->type = type; + this->polyset = NULL; + this->left = left; + this->right = right; + refcounter = 1; +} + +CSGTerm *CSGTerm::normalize() +{ + // This function implements the CSG normalization + // Reference: Florian Kirsch, Juergen Doeller, + // OpenCSG: A Library for Image-Based CSG Rendering, + // University of Potsdam, Hasso-Plattner-Institute, Germany + // http://www.opencsg.org/data/csg_freenix2005_paper.pdf + + if (type == TYPE_PRIMITIVE) + return link(); + + CSGTerm *t1, *t2, *x, *y; + + x = left->normalize(); + y = right->normalize(); + + if (x != left || y != right) { + t1 = new CSGTerm(type, x, y); + } else { + t1 = link(); + x->unlink(); + y->unlink(); + } + + while (1) { + t2 = t1->normalize_tail(); + t1->unlink(); + if (t1 == t2) + break; + t1 = t2; + } + + return t1; +} + +CSGTerm *CSGTerm::normalize_tail() +{ + CSGTerm *x, *y, *z; + + // Part A: The 'x . (y . z)' expressions + + x = left; + y = right->left; + z = right->right; + + // 1. x - (y + z) -> (x - y) - z + if (type == TYPE_DIFFERENCE && right->type == TYPE_UNION) + return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), z->link()); + + // 2. x * (y + z) -> (x * y) + (x * z) + if (type == TYPE_INTERSECTION && right->type == TYPE_UNION) + return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), new CSGTerm(TYPE_INTERSECTION, x->link(), z->link())); + + // 3. x - (y * z) -> (x - y) + (x - z) + if (type == TYPE_DIFFERENCE && right->type == TYPE_INTERSECTION) + return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), new CSGTerm(TYPE_DIFFERENCE, x->link(), z->link())); + + // 4. x * (y * z) -> (x * y) * z + if (type == TYPE_INTERSECTION && right->type == TYPE_INTERSECTION) + return new CSGTerm(TYPE_INTERSECTION, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), z->link()); + + // 5. x - (y - z) -> (x - y) + (x * z) + if (type == TYPE_DIFFERENCE && right->type == TYPE_DIFFERENCE) + return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), y->link()), new CSGTerm(TYPE_INTERSECTION, x->link(), z->link())); + + // 6. x * (y - z) -> (x * y) - z + if (type == TYPE_INTERSECTION && right->type == TYPE_DIFFERENCE) + return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_INTERSECTION, x->link(), y->link()), z->link()); + + // Part B: The '(x . y) . z' expressions + + x = left->left; + y = left->right; + z = right; + + // 7. (x - y) * z -> (x * z) - y + if (left->type == TYPE_DIFFERENCE && type == TYPE_INTERSECTION) + return new CSGTerm(TYPE_DIFFERENCE, new CSGTerm(TYPE_INTERSECTION, x->link(), z->link()), y->link()); + + // 8. (x + y) - z -> (x - z) + (y - z) + if (left->type == TYPE_UNION && type == TYPE_DIFFERENCE) + return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_DIFFERENCE, x->link(), z->link()), new CSGTerm(TYPE_DIFFERENCE, y->link(), z->link())); + + // 9. (x + y) * z -> (x * z) + (y * z) + if (left->type == TYPE_UNION && type == TYPE_INTERSECTION) + return new CSGTerm(TYPE_UNION, new CSGTerm(TYPE_INTERSECTION, x->link(), z->link()), new CSGTerm(TYPE_INTERSECTION, y->link(), z->link())); + + return link(); +} + +CSGTerm *CSGTerm::link() +{ + refcounter++; + return this; +} + +void CSGTerm::unlink() +{ + if (--refcounter <= 0) { + if (polyset) + polyset->unlink(); + if (left) + left->unlink(); + if (right) + right->unlink(); + delete this; + } +} + +QString CSGTerm::dump() +{ + if (type == TYPE_UNION) + return QString("(%1 + %2)").arg(left->dump(), right->dump()); + if (type == TYPE_INTERSECTION) + return QString("(%1 * %2)").arg(left->dump(), right->dump()); + if (type == TYPE_DIFFERENCE) + return QString("(%1 - %2)").arg(left->dump(), right->dump()); + return label; +} + +CSGChain::CSGChain() +{ +} + +void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label) +{ + polysets.append(polyset); + matrices.append(m); + types.append(type); + labels.append(label); +} + +void CSGChain::import(CSGTerm *term, CSGTerm::type_e type) +{ + if (term->type == CSGTerm::TYPE_PRIMITIVE) { + add(term->polyset, term->m, type, term->label); + } else { + import(term->left, type); + import(term->right, term->type); + } +} + +QString CSGChain::dump() +{ + QString text; + for (int i = 0; i < types.size(); i++) + { + if (types[i] == CSGTerm::TYPE_UNION) { + if (i != 0) + text += "\n"; + text += "+"; + } + if (types[i] == CSGTerm::TYPE_DIFFERENCE) + text += " -"; + if (types[i] == CSGTerm::TYPE_INTERSECTION) + text += " *"; + text += labels[i]; + } + text += "\n"; + return text; +} + diff --git a/src/dxfdata.cc b/src/dxfdata.cc new file mode 100644 index 0000000..864ee7b --- /dev/null +++ b/src/dxfdata.cc @@ -0,0 +1,514 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" +#include "printutils.h" + +#include +#include + +struct Line { + typedef DxfData::Point Point; + Point *p[2]; + bool disabled; + Line(Point *p1, Point *p2) { p[0] = p1; p[1] = p2; disabled = false; } + Line() { p[0] = NULL; p[1] = NULL; disabled = false; } +}; + +DxfData::DxfData() +{ +} + +/*! + Reads a layer from the given file, or all layers if layename.isNull() + */ +DxfData::DxfData(double fn, double fs, double fa, QString filename, QString layername, double xorigin, double yorigin, double scale) +{ + handle_dep(filename); // Register ourselves as a dependency + + QFile f(filename); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + PRINTF("WARNING: Can't open DXF file `%s'.", filename.toUtf8().data()); + return; + } + QTextStream stream(&f); + + Grid2d< QVector > grid(GRID_COARSE); + QList lines; // Global lines + QHash< QString, QList > blockdata; // Lines in blocks + + bool in_entities_section = false; + bool in_blocks_section = false; + QString current_block; + +#define ADD_LINE(_x1, _y1, _x2, _y2) do { \ + double _p1x = _x1, _p1y = _y1, _p2x = _x2, _p2y = _y2; \ + if (!in_entities_section && !in_blocks_section) \ + break; \ + if (in_entities_section && \ + !(layername.isNull() || layername == layer)) \ + break; \ + grid.align(_p1x, _p1y); \ + grid.align(_p2x, _p2y); \ + grid.data(_p1x, _p1y).append(lines.count()); \ + grid.data(_p2x, _p2y).append(lines.count()); \ + if (in_entities_section) \ + lines.append( \ + Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ + if (in_blocks_section && !current_block.isNull()) \ + blockdata[current_block].append( \ + Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ + } while (0) + + QString mode, layer, name, iddata; + int dimtype = 0; + double coords[7][2]; // Used by DIMENSION entities + QVector xverts; + QVector yverts; + double radius = 0; + double arc_start_angle = 0, arc_stop_angle = 0; + double ellipse_start_angle = 0, ellipse_stop_angle = 0; + + for (int i = 0; i < 7; i++) + for (int j = 0; j < 2; j++) + coords[i][j] = 0; + + QHash unsupported_entities_list; + + + // + // Parse DXF file. Will populate this->points, this->dims, lines and blockdata + // + while (!stream.atEnd()) + { + QString id_str = stream.readLine(); + QString data = stream.readLine(); + + bool status; + int id = id_str.toInt(&status); + + if (!status) { + PRINTA("WARNING: Illegal ID `%1' in `%3'.", id_str, filename); + break; + } + + if (id >= 10 && id <= 16) { + if (in_blocks_section) + coords[id-10][0] = data.toDouble(); + else if (id == 11 || id == 12 || id == 16) + coords[id-10][0] = data.toDouble() * scale; + else + coords[id-10][0] = (data.toDouble() - xorigin) * scale; + } + + if (id >= 20 && id <= 26) { + if (in_blocks_section) + coords[id-20][1] = data.toDouble(); + else if (id == 21 || id == 22 || id == 26) + coords[id-20][1] = data.toDouble() * scale; + else + coords[id-20][1] = (data.toDouble() - yorigin) * scale; + } + + switch (id) + { + case 0: + if (mode == "SECTION") { + in_entities_section = iddata == "ENTITIES"; + in_blocks_section = iddata == "BLOCKS"; + } + else if (mode == "LINE") { + ADD_LINE(xverts[0], yverts[0], xverts[1], yverts[1]); + } + else if (mode == "LWPOLYLINE") { + assert(xverts.size() == yverts.size()); + // polyline flag is stored in 'dimtype' + int numverts = xverts.size(); + for (int i=1;i arc_stop_angle) + arc_stop_angle += 360.0; + n = (int)ceil(n * (arc_stop_angle-arc_start_angle) / 360); + for (int i = 0; i < n; i++) { + double a1 = ((arc_stop_angle-arc_start_angle)*i)/n; + double a2 = ((arc_stop_angle-arc_start_angle)*(i+1))/n; + a1 = (arc_start_angle + a1) * M_PI / 180.0; + a2 = (arc_start_angle + a2) * M_PI / 180.0; + ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y, + cos(a2)*radius + center.x, sin(a2)*radius + center.y); + } + } + else if (mode == "ELLIPSE") { + // Commented code is meant as documentation of vector math + while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI; +// Vector2d center(xverts[0], yverts[0]); + Point center(xverts[0], yverts[0]); +// Vector2d ce(xverts[1], yverts[1]); + Point ce(xverts[1], yverts[1]); +// double r_major = ce.length(); + double r_major = sqrt(ce.x*ce.x + ce.y*ce.y); +// double rot_angle = ce.angle(); + double rot_angle; + { +// double dot = ce.dot(Vector2d(1.0, 0.0)); + double dot = ce.x; + double cosval = dot / r_major; + if (cosval > 1.0) cosval = 1.0; + if (cosval < -1.0) cosval = -1.0; + rot_angle = acos(cosval); + if (ce.y < 0.0) rot_angle = 2 * M_PI - rot_angle; + } + + // the ratio stored in 'radius; due to the parser code not checking entity type + double r_minor = r_major * radius; + double sweep_angle = ellipse_stop_angle-ellipse_start_angle; + int n = get_fragments_from_r(r_major, fn, fs, fa); + n = (int)ceil(n * sweep_angle / (2 * M_PI)); +// Vector2d p1; + Point p1; + for (int i=0;i<=n;i++) { + double a = (ellipse_start_angle + sweep_angle*i/n); +// Vector2d p2(cos(a)*r_major, sin(a)*r_minor); + Point p2(cos(a)*r_major, sin(a)*r_minor); +// p2.rotate(rot_angle); + Point p2_rot(cos(rot_angle)*p2.x - sin(rot_angle)*p2.y, + sin(rot_angle)*p2.x + cos(rot_angle)*p2.y); +// p2 += center; + p2_rot.x += center.x; + p2_rot.y += center.y; + if (i > 0) { +// ADD_LINE(p1[0], p1[1], p2[0], p2[1]); + ADD_LINE(p1.x, p1.y, p2_rot.x, p2_rot.y); + } +// p1 = p2; + p1.x = p2_rot.x; + p1.y = p2_rot.y; + } + } + else if (mode == "INSERT") { + // scale is stored in ellipse_start|stop_angle, rotation in arc_start_angle; + // due to the parser code not checking entity type + int n = blockdata[iddata].size(); + for (int i = 0; i < n; i++) { + double a = arc_start_angle * M_PI / 180.0; + double lx1 = blockdata[iddata][i].p[0]->x * ellipse_start_angle; + double ly1 = blockdata[iddata][i].p[0]->y * ellipse_stop_angle; + double lx2 = blockdata[iddata][i].p[1]->x * ellipse_start_angle; + double ly2 = blockdata[iddata][i].p[1]->y * ellipse_stop_angle; + double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts[0]; + double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts[0]; + double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts[0]; + double py2 = (sin(a)*lx2 + cos(a)*ly2) * scale + yverts[0]; + ADD_LINE(px1, py1, px2, py2); + } + } + else if (mode == "DIMENSION" && + (layername.isNull() || layername == layer)) { + this->dims.append(Dim()); + this->dims.last().type = dimtype; + for (int i = 0; i < 7; i++) + for (int j = 0; j < 2; j++) + this->dims.last().coords[i][j] = coords[i][j]; + this->dims.last().angle = arc_start_angle; + this->dims.last().length = radius; + this->dims.last().name = name; + } + else if (mode == "BLOCK") { + current_block = iddata; + } + else if (mode == "ENDBLK") { + current_block = QString(); + } + else if (mode == "ENDSEC") { + } + else if (in_blocks_section || (in_entities_section && + (layername.isNull() || layername == layer))) { + unsupported_entities_list[mode]++; + } + mode = data; + layer = QString(); + name = QString(); + iddata = QString(); + dimtype = 0; + for (int i = 0; i < 7; i++) + for (int j = 0; j < 2; j++) + coords[i][j] = 0; + xverts.clear(); + yverts.clear(); + radius = arc_start_angle = arc_stop_angle = 0; + ellipse_start_angle = ellipse_stop_angle = 0; + if (mode == "INSERT") { + ellipse_start_angle = ellipse_stop_angle = 1.0; // scale + } + break; + case 1: + name = data; + break; + case 2: + iddata = data; + break; + case 8: + layer = data; + break; + case 10: + if (in_blocks_section) + xverts.append((data.toDouble())); + else + xverts.append((data.toDouble() - xorigin) * scale); + break; + case 11: + if (in_blocks_section) + xverts.append((data.toDouble())); + else + xverts.append((data.toDouble() - xorigin) * scale); + break; + case 20: + if (in_blocks_section) + yverts.append((data.toDouble())); + else + yverts.append((data.toDouble() - yorigin) * scale); + break; + case 21: + if (in_blocks_section) + yverts.append((data.toDouble())); + else + yverts.append((data.toDouble() - yorigin) * scale); + break; + case 40: + // CIRCLE, ARC: radius + // ELLIPSE: minor to major ratio + // DIMENSION (radial, diameter): Leader length + radius = data.toDouble(); + if (!in_blocks_section) radius *= scale; + break; + case 41: + // ELLIPSE: start_angle + // INSERT: X scale + ellipse_start_angle = data.toDouble(); + break; + case 50: + // ARC: start_angle + // INSERT: rot angle + // DIMENSION: linear and rotated: angle + arc_start_angle = data.toDouble(); + break; + case 42: + // ELLIPSE: stop_angle + // INSERT: Y scale + ellipse_stop_angle = data.toDouble(); + break; + case 51: // ARC + arc_stop_angle = data.toDouble(); + break; + case 70: + // LWPOLYLINE: polyline flag + // DIMENSION: dimension type + dimtype = data.toInt(); + break; + } + } + + QHashIterator i(unsupported_entities_list); + while (i.hasNext()) { + i.next(); + if (layername.isNull()) { + PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in `%3'.", + i.key(), QString::number(i.value()), filename); + } else { + PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in layer `%3' of `%4'.", + i.key(), QString::number(i.value()), layername, filename); + } + } + + // Extract paths from parsed data + + QHash enabled_lines; + for (int i = 0; i < lines.count(); i++) { + enabled_lines[i] = i; + } + + // extract all open paths + while (enabled_lines.count() > 0) + { + int current_line, current_point; + + foreach (int i, enabled_lines) { + for (int j = 0; j < 2; j++) { + QVector *lv = &grid.data(lines[i].p[j]->x, lines[i].p[j]->y); + for (int ki = 0; ki < lv->count(); ki++) { + int k = lv->at(ki); + if (k == i || lines[k].disabled) + continue; + goto next_open_path_j; + } + current_line = i; + current_point = j; + goto create_open_path; + next_open_path_j:; + } + } + + break; + + create_open_path: + this->paths.append(Path()); + Path *this_path = &this->paths.last(); + + this_path->points.append(lines[current_line].p[current_point]); + while (1) { + this_path->points.append(lines[current_line].p[!current_point]); + Point *ref_point = lines[current_line].p[!current_point]; + lines[current_line].disabled = true; + enabled_lines.remove(current_line); + QVector *lv = &grid.data(ref_point->x, ref_point->y); + for (int ki = 0; ki < lv->count(); ki++) { + int k = lv->at(ki); + if (lines[k].disabled) + continue; + if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { + current_line = k; + current_point = 0; + goto found_next_line_in_open_path; + } + if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { + current_line = k; + current_point = 1; + goto found_next_line_in_open_path; + } + } + break; + found_next_line_in_open_path:; + } + } + + // extract all closed paths + while (enabled_lines.count() > 0) + { + int current_line = enabled_lines.begin().value(), current_point = 0; + + this->paths.append(Path()); + Path *this_path = &this->paths.last(); + this_path->is_closed = true; + + this_path->points.append(lines[current_line].p[current_point]); + while (1) { + this_path->points.append(lines[current_line].p[!current_point]); + Point *ref_point = lines[current_line].p[!current_point]; + lines[current_line].disabled = true; + enabled_lines.remove(current_line); + QVector *lv = &grid.data(ref_point->x, ref_point->y); + for (int ki = 0; ki < lv->count(); ki++) { + int k = lv->at(ki); + if (lines[k].disabled) + continue; + if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { + current_line = k; + current_point = 0; + goto found_next_line_in_closed_path; + } + if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { + current_line = k; + current_point = 1; + goto found_next_line_in_closed_path; + } + } + break; + found_next_line_in_closed_path:; + } + } + + fixup_path_direction(); + +#if 0 + printf("----- DXF Data -----\n"); + for (int i = 0; i < this->paths.count(); i++) { + printf("Path %d (%s):\n", i, this->paths[i].is_closed ? "closed" : "open"); + for (int j = 0; j < this->paths[i].points.count(); j++) + printf(" %f %f\n", this->paths[i].points[j]->x, this->paths[i].points[j]->y); + } + printf("--------------------\n"); + fflush(stdout); +#endif +} + +/*! + Ensures that all paths have the same vertex ordering. + FIXME: CW or CCW? +*/ +void DxfData::fixup_path_direction() +{ + for (int i = 0; i < this->paths.count(); i++) { + if (!this->paths[i].is_closed) + break; + this->paths[i].is_inner = true; + double min_x = this->paths[i].points[0]->x; + int min_x_point = 0; + for (int j = 1; j < this->paths[i].points.count(); j++) { + if (this->paths[i].points[j]->x < min_x) { + min_x = this->paths[i].points[j]->x; + min_x_point = j; + } + } + // rotate points if the path is in non-standard rotation + int b = min_x_point; + int a = b == 0 ? this->paths[i].points.count() - 2 : b - 1; + int c = b == this->paths[i].points.count() - 1 ? 1 : b + 1; + double ax = this->paths[i].points[a]->x - this->paths[i].points[b]->x; + double ay = this->paths[i].points[a]->y - this->paths[i].points[b]->y; + double cx = this->paths[i].points[c]->x - this->paths[i].points[b]->x; + double cy = this->paths[i].points[c]->y - this->paths[i].points[b]->y; +#if 0 + printf("Rotate check:\n"); + printf(" a/b/c indices = %d %d %d\n", a, b, c); + printf(" b->a vector = %f %f (%f)\n", ax, ay, atan2(ax, ay)); + printf(" b->c vector = %f %f (%f)\n", cx, cy, atan2(cx, cy)); +#endif + // FIXME: atan2() usually takes y,x. This variant probably makes the path clockwise.. + if (atan2(ax, ay) < atan2(cx, cy)) { + for (int j = 0; j < this->paths[i].points.count()/2; j++) + this->paths[i].points.swap(j, this->paths[i].points.count()-1-j); + } + } +} + +DxfData::Point *DxfData::addPoint(double x, double y) +{ + this->points.append(Point(x, y)); + return &this->points.last(); +} + diff --git a/src/dxfdim.cc b/src/dxfdim.cc new file mode 100644 index 0000000..359606a --- /dev/null +++ b/src/dxfdim.cc @@ -0,0 +1,187 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include + +QHash dxf_dim_cache; +QHash dxf_cross_cache; + +Value builtin_dxf_dim(const QVector &argnames, const QVector &args) +{ + QString filename; + QString layername; + QString name; + double xorigin = 0; + double yorigin = 0; + double scale = 1; + + for (int i = 0; i < argnames.count() && i < args.count(); i++) { + if (argnames[i] == "file") + filename = args[i].text; + if (argnames[i] == "layer") + layername = args[i].text; + if (argnames[i] == "origin") + args[i].getv2(xorigin, yorigin); + if (argnames[i] == "scale") + args[i].getnum(scale); + if (argnames[i] == "name") + name = args[i].text; + } + + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(filename.toAscii().data(), &st); + + QString key = filename + "|" + layername + "|" + name + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + + "|" + QString::number(scale) + "|" + QString::number(st.st_mtime) + "|" + QString::number(st.st_size); + + if (dxf_dim_cache.contains(key)) + return dxf_dim_cache[key]; + + DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); + + for (int i = 0; i < dxf.dims.count(); i++) + { + if (!name.isNull() && dxf.dims[i].name != name) + continue; + + DxfData::Dim *d = &dxf.dims[i]; + int type = d->type & 7; + + if (type == 0) { + // Rotated, horizontal or vertical + double x = d->coords[4][0] - d->coords[3][0]; + double y = d->coords[4][1] - d->coords[3][1]; + double angle = d->angle; + double distance_projected_on_line = fabs(x * cos(angle*M_PI/180) + y * sin(angle*M_PI/180)); + return dxf_dim_cache[key] = Value(distance_projected_on_line); + } + else if (type == 1) { + // Aligned + double x = d->coords[4][0] - d->coords[3][0]; + double y = d->coords[4][1] - d->coords[3][1]; + return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + } + else if (type == 2) { + // Angular + double a1 = atan2(d->coords[0][0] - d->coords[5][0], d->coords[0][1] - d->coords[5][1]); + double a2 = atan2(d->coords[4][0] - d->coords[3][0], d->coords[4][1] - d->coords[3][1]); + return dxf_dim_cache[key] = Value(fabs(a1 - a2) * 180 / M_PI); + } + else if (type == 3 || type == 4) { + // Diameter or Radius + double x = d->coords[5][0] - d->coords[0][0]; + double y = d->coords[5][1] - d->coords[0][1]; + return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + } + else if (type == 5) { + // Angular 3 Point + } + else if (type == 6) { + // Ordinate + return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); + } + + PRINTA("WARNING: Dimension `%1' in `%2', layer `%3' has unsupported type!", name, filename, layername); + return Value(); + } + + PRINTA("WARNING: Can't find dimension `%1' in `%2', layer `%3'!", name, filename, layername); + + return Value(); +} + +Value builtin_dxf_cross(const QVector &argnames, const QVector &args) +{ + QString filename; + QString layername; + double xorigin = 0; + double yorigin = 0; + double scale = 1; + + for (int i = 0; i < argnames.count() && i < args.count(); i++) { + if (argnames[i] == "file") + filename = args[i].text; + if (argnames[i] == "layer") + layername = args[i].text; + if (argnames[i] == "origin") + args[i].getv2(xorigin, yorigin); + if (argnames[i] == "scale") + args[i].getnum(scale); + } + + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(filename.toAscii().data(), &st); + + QString key = filename + "|" + layername + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + + "|" + QString::number(scale) + "|" + QString::number(st.st_mtime) + "|" + QString::number(st.st_size); + + if (dxf_cross_cache.contains(key)) + return dxf_cross_cache[key]; + + DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); + + double coords[4][2]; + + for (int i = 0, j = 0; i < dxf.paths.count(); i++) { + if (dxf.paths[i].points.count() != 2) + continue; + coords[j][0] = dxf.paths[i].points[0]->x; + coords[j++][1] = dxf.paths[i].points[0]->y; + coords[j][0] = dxf.paths[i].points[1]->x; + coords[j++][1] = dxf.paths[i].points[1]->y; + + if (j == 4) { + double x1 = coords[0][0], y1 = coords[0][1]; + double x2 = coords[1][0], y2 = coords[1][1]; + double x3 = coords[2][0], y3 = coords[2][1]; + double x4 = coords[3][0], y4 = coords[3][1]; + double dem = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1); + if (dem == 0) + break; + double ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3)) / dem; + // double ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)) / dem; + double x = x1 + ua*(x2 - x1); + double y = y1 + ua*(y2 - y1); + Value ret; + ret.type = Value::VECTOR; + ret.vec.append(new Value(x)); + ret.vec.append(new Value(y)); + return dxf_cross_cache[key] = ret; + } + } + + PRINTA("WARNING: Can't find cross in `%1', layer `%2'!", filename, layername); + + return Value(); +} + +void initialize_builtin_dxf_dim() +{ + builtin_functions["dxf_dim"] = new BuiltinFunction(&builtin_dxf_dim); + builtin_functions["dxf_cross"] = new BuiltinFunction(&builtin_dxf_cross); +} + diff --git a/src/dxflinextrude.cc b/src/dxflinextrude.cc new file mode 100644 index 0000000..f7a1d24 --- /dev/null +++ b/src/dxflinextrude.cc @@ -0,0 +1,359 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include + +#include +#include + +class DxfLinearExtrudeModule : public AbstractModule +{ +public: + DxfLinearExtrudeModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class DxfLinearExtrudeNode : public AbstractPolyNode +{ +public: + int convexity, slices; + double fn, fs, fa, height, twist; + double origin_x, origin_y, scale; + bool center, has_twist; + QString filename, layername; + DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + convexity = slices = 0; + fn = fs = fa = height = twist = 0; + origin_x = origin_y = scale = 0; + center = has_twist = false; + } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + DxfLinearExtrudeNode *node = new DxfLinearExtrudeNode(inst); + + QVector argnames = QVector() << "file" << "layer" << "height" << "origin" << "scale" << "center" << "twist" << "slices"; + QVector argexpr; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + node->fn = c.lookup_variable("$fn").num; + node->fs = c.lookup_variable("$fs").num; + node->fa = c.lookup_variable("$fa").num; + + Value file = c.lookup_variable("file"); + Value layer = c.lookup_variable("layer", true); + Value height = c.lookup_variable("height", true); + Value convexity = c.lookup_variable("convexity", true); + Value origin = c.lookup_variable("origin", true); + Value scale = c.lookup_variable("scale", true); + Value center = c.lookup_variable("center", true); + Value twist = c.lookup_variable("twist", true); + Value slices = c.lookup_variable("slices", true); + + node->filename = file.text; + node->layername = layer.text; + node->height = height.num; + node->convexity = (int)convexity.num; + origin.getv2(node->origin_x, node->origin_y); + node->scale = scale.num; + + if (center.type == Value::BOOL) + node->center = center.b; + + if (node->height <= 0) + node->height = 100; + + if (node->convexity <= 0) + node->convexity = 1; + + if (node->scale <= 0) + node->scale = 1; + + if (twist.type == Value::NUMBER) { + node->twist = twist.num; + if (slices.type == Value::NUMBER) { + node->slices = (int)slices.num; + } else { + node->slices = (int)fmax(2, fabs(get_fragments_from_r(node->height, + node->fn, node->fs, node->fa) * node->twist / 360)); + } + node->has_twist = true; + } + + if (node->filename.isEmpty()) { + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n) + node->children.append(n); + } + } + + return node; +} + +void register_builtin_dxf_linear_extrude() +{ + builtin_modules["dxf_linear_extrude"] = new DxfLinearExtrudeModule(); + builtin_modules["linear_extrude"] = new DxfLinearExtrudeModule(); +} + +static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2) +{ + for (int j = 1; j < pt->points.count(); j++) + { + int k = j - 1; + + double jx1 = pt->points[j]->x * cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180); + double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180); + + double jx2 = pt->points[j]->x * cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180); + double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180); + + double kx1 = pt->points[k]->x * cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180); + double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180); + + double kx2 = pt->points[k]->x * cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180); + double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180); + + double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2); + double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1); + + if (dia1_len_sq > dia2_len_sq) + { + ps->append_poly(); + if (pt->is_inner) { + ps->append_vertex(kx1, ky1, h1); + ps->append_vertex(jx1, jy1, h1); + ps->append_vertex(jx2, jy2, h2); + } else { + ps->insert_vertex(kx1, ky1, h1); + ps->insert_vertex(jx1, jy1, h1); + ps->insert_vertex(jx2, jy2, h2); + } + + ps->append_poly(); + if (pt->is_inner) { + ps->append_vertex(kx2, ky2, h2); + ps->append_vertex(kx1, ky1, h1); + ps->append_vertex(jx2, jy2, h2); + } else { + ps->insert_vertex(kx2, ky2, h2); + ps->insert_vertex(kx1, ky1, h1); + ps->insert_vertex(jx2, jy2, h2); + } + } + else + { + ps->append_poly(); + if (pt->is_inner) { + ps->append_vertex(kx1, ky1, h1); + ps->append_vertex(jx1, jy1, h1); + ps->append_vertex(kx2, ky2, h2); + } else { + ps->insert_vertex(kx1, ky1, h1); + ps->insert_vertex(jx1, jy1, h1); + ps->insert_vertex(kx2, ky2, h2); + } + + ps->append_poly(); + if (pt->is_inner) { + ps->append_vertex(jx2, jy2, h2); + ps->append_vertex(kx2, ky2, h2); + ps->append_vertex(jx1, jy1, h1); + } else { + ps->insert_vertex(jx2, jy2, h2); + ps->insert_vertex(kx2, ky2, h2); + ps->insert_vertex(jx1, jy1, h1); + } + } + } +} + +static void report_func(const class AbstractNode*, void *vp, int mark) +{ + QProgressDialog *pd = (QProgressDialog*)vp; + int v = (int)((mark*100.0) / progress_report_count); + pd->setValue(v < 100 ? v : 99); + QString label; + label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); + pd->setLabelText(label); + QApplication::processEvents(); +} + +PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e rm) const +{ + QString key = mk_cache_id(); + if (PolySet::ps_cache.contains(key)) { + PRINT(PolySet::ps_cache[key]->msg); + return PolySet::ps_cache[key]->ps->link(); + } + + print_messages_push(); + DxfData *dxf; + + if (filename.isEmpty()) + { + QTime t; + QProgressDialog *pd = NULL; + + if (rm == RENDER_OPENCSG) + { + PRINT_NOCACHE("Processing uncached linear_extrude outline..."); + QApplication::processEvents(); + + t.start(); + pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + progress_report_prep((AbstractNode*)this, report_func, pd); + } + + // Before extruding, union all (2D) children nodes + // to a single DxfData, then tessealte this into a PolySet + CGAL_Nef_polyhedron N; + N.dim = 2; + foreach(AbstractNode * v, children) { + if (v->modinst->tag_background) + continue; + N.p2 += v->render_cgal_nef_polyhedron().p2; + } + dxf = new DxfData(N); + + if (rm == RENDER_OPENCSG) { + progress_report_fin(); + int s = t.elapsed() / 1000; + PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); + delete pd; + } + } else { + dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); + } + + PolySet *ps = new PolySet(); + ps->convexity = convexity; + + double h1, h2; + + if (center) { + h1 = -height/2.0; + h2 = +height/2.0; + } else { + h1 = 0; + h2 = height; + } + + bool first_open_path = true; + for (int i = 0; i < dxf->paths.count(); i++) + { + if (dxf->paths[i].is_closed) + continue; + if (first_open_path) { + PRINTF("WARING: Open paths in dxf_liniear_extrude(file = \"%s\", layer = \"%s\"):", + filename.toAscii().data(), layername.toAscii().data()); + first_open_path = false; + } + PRINTF(" %9.5f %10.5f ... %10.5f %10.5f", + dxf->paths[i].points.first()->x / scale + origin_x, + dxf->paths[i].points.first()->y / scale + origin_y, + dxf->paths[i].points.last()->x / scale + origin_x, + dxf->paths[i].points.last()->y / scale + origin_y); + } + + + if (has_twist) + { + dxf_tesselate(ps, dxf, 0, false, true, h1); + dxf_tesselate(ps, dxf, twist, true, true, h2); + for (int j = 0; j < slices; j++) + { + double t1 = twist*j / slices; + double t2 = twist*(j+1) / slices; + double g1 = h1 + (h2-h1)*j / slices; + double g2 = h1 + (h2-h1)*(j+1) / slices; + for (int i = 0; i < dxf->paths.count(); i++) + { + if (!dxf->paths[i].is_closed) + continue; + add_slice(ps, &dxf->paths[i], t1, t2, g1, g2); + } + } + } + else + { + dxf_tesselate(ps, dxf, 0, false, true, h1); + dxf_tesselate(ps, dxf, 0, true, true, h2); + for (int i = 0; i < dxf->paths.count(); i++) + { + if (!dxf->paths[i].is_closed) + continue; + add_slice(ps, &dxf->paths[i], 0, 0, h1, h2); + } + } + + PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); + print_messages_pop(); + delete dxf; + + return ps; +} + +QString DxfLinearExtrudeNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(filename.toAscii().data(), &st); + text.sprintf("linear_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " + "height = %g, origin = [ %g %g ], scale = %g, center = %s, convexity = %d", + filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, + layername.toAscii().data(), height, origin_x, origin_y, scale, + center ? "true" : "false", convexity); + if (has_twist) { + QString t2; + t2.sprintf(", twist = %g, slices = %d", twist, slices); + text += t2; + } + QString t3; + t3.sprintf(", $fn = %g, $fa = %g, $fs = %g) {\n", fn, fs, fa); + text += t3; + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + text += indent + "}\n"; + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc new file mode 100644 index 0000000..427bcb0 --- /dev/null +++ b/src/dxfrotextrude.cc @@ -0,0 +1,251 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include + +#include +#include + +class DxfRotateExtrudeModule : public AbstractModule +{ +public: + DxfRotateExtrudeModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class DxfRotateExtrudeNode : public AbstractPolyNode +{ +public: + int convexity; + double fn, fs, fa; + double origin_x, origin_y, scale; + QString filename, layername; + DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + convexity = 0; + fn = fs = fa = 0; + origin_x = origin_y = scale = 0; + } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + DxfRotateExtrudeNode *node = new DxfRotateExtrudeNode(inst); + + QVector argnames = QVector() << "file" << "layer" << "origin" << "scale"; + QVector argexpr; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + node->fn = c.lookup_variable("$fn").num; + node->fs = c.lookup_variable("$fs").num; + node->fa = c.lookup_variable("$fa").num; + + Value file = c.lookup_variable("file"); + Value layer = c.lookup_variable("layer", true); + Value convexity = c.lookup_variable("convexity", true); + Value origin = c.lookup_variable("origin", true); + Value scale = c.lookup_variable("scale", true); + + node->filename = file.text; + node->layername = layer.text; + node->convexity = (int)convexity.num; + origin.getv2(node->origin_x, node->origin_y); + node->scale = scale.num; + + if (node->convexity <= 0) + node->convexity = 1; + + if (node->scale <= 0) + node->scale = 1; + + if (node->filename.isEmpty()) { + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n) + node->children.append(n); + } + } + + return node; +} + +void register_builtin_dxf_rotate_extrude() +{ + builtin_modules["dxf_rotate_extrude"] = new DxfRotateExtrudeModule(); + builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule(); +} + +static void report_func(const class AbstractNode*, void *vp, int mark) +{ + QProgressDialog *pd = (QProgressDialog*)vp; + int v = (int)((mark*100.0) / progress_report_count); + pd->setValue(v < 100 ? v : 99); + QString label; + label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); + pd->setLabelText(label); + QApplication::processEvents(); +} + +PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e rm) const +{ + QString key = mk_cache_id(); + if (PolySet::ps_cache.contains(key)) { + PRINT(PolySet::ps_cache[key]->msg); + return PolySet::ps_cache[key]->ps->link(); + } + + print_messages_push(); + DxfData *dxf; + + if (filename.isEmpty()) + { + QTime t; + QProgressDialog *pd; + + if (rm == RENDER_OPENCSG) + { + PRINT_NOCACHE("Processing uncached rotate_extrude outline..."); + QApplication::processEvents(); + + t.start(); + pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + progress_report_prep((AbstractNode*)this, report_func, pd); + } + + CGAL_Nef_polyhedron N; + N.dim = 2; + foreach(AbstractNode * v, children) { + if (v->modinst->tag_background) + continue; + N.p2 += v->render_cgal_nef_polyhedron().p2; + } + dxf = new DxfData(N); + + if (rm == RENDER_OPENCSG) { + progress_report_fin(); + int s = t.elapsed() / 1000; + PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); + delete pd; + } + } else { + dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); + } + + PolySet *ps = new PolySet(); + ps->convexity = convexity; + + for (int i = 0; i < dxf->paths.count(); i++) + { + double max_x = 0; + for (int j = 0; j < dxf->paths[i].points.count(); j++) { + max_x = fmax(max_x, dxf->paths[i].points[j]->x); + } + + int fragments = get_fragments_from_r(max_x, fn, fs, fa); + + double points[fragments][dxf->paths[i].points.count()][3]; + + for (int j = 0; j < fragments; j++) { + double a = (j*2*M_PI) / fragments; + for (int k = 0; k < dxf->paths[i].points.count(); k++) { + if (dxf->paths[i].points[k]->x == 0) { + points[j][k][0] = 0; + points[j][k][1] = 0; + } else { + points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); + points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); + } + points[j][k][2] = dxf->paths[i].points[k]->y; + } + } + + for (int j = 0; j < fragments; j++) { + int j1 = j + 1 < fragments ? j + 1 : 0; + for (int k = 0; k < dxf->paths[i].points.count(); k++) { + int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; + if (points[j][k][0] != points[j1][k][0] || + points[j][k][1] != points[j1][k][1] || + points[j][k][2] != points[j1][k][2]) { + ps->append_poly(); + ps->append_vertex(points[j ][k ][0], + points[j ][k ][1], points[j ][k ][2]); + ps->append_vertex(points[j1][k ][0], + points[j1][k ][1], points[j1][k ][2]); + ps->append_vertex(points[j ][k1][0], + points[j ][k1][1], points[j ][k1][2]); + } + if (points[j][k1][0] != points[j1][k1][0] || + points[j][k1][1] != points[j1][k1][1] || + points[j][k1][2] != points[j1][k1][2]) { + ps->append_poly(); + ps->append_vertex(points[j ][k1][0], + points[j ][k1][1], points[j ][k1][2]); + ps->append_vertex(points[j1][k ][0], + points[j1][k ][1], points[j1][k ][2]); + ps->append_vertex(points[j1][k1][0], + points[j1][k1][1], points[j1][k1][2]); + } + } + } + } + + PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); + print_messages_pop(); + delete dxf; + + return ps; +} + +QString DxfRotateExtrudeNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(filename.toAscii().data(), &st); + text.sprintf("rotate_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " + "origin = [ %g %g ], scale = %g, convexity = %d, " + "$fn = %g, $fa = %g, $fs = %g) {\n", + filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, + layername.toAscii().data(), origin_x, origin_y, scale, convexity, + fn, fs, fa); + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + text += indent + "}\n"; + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc new file mode 100644 index 0000000..fa879b6 --- /dev/null +++ b/src/dxftess-cgal.cc @@ -0,0 +1,103 @@ +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Triangulation_vertex_base_2 Vb; +typedef CGAL::Delaunay_mesh_face_base_2 Fb; +typedef CGAL::Triangulation_data_structure_2 Tds; +typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; +//typedef CGAL::Delaunay_mesh_criteria_2 Criteria; + +typedef CDT::Vertex_handle Vertex_handle; +typedef CDT::Point CDTPoint; + +#include + +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(); } +}; + +void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h) +{ + CDT cdt; + + // + Grid3d< QPair > point_to_path(GRID_FINE); + + for (int i = 0; i < dxf->paths.count(); i++) { + if (!dxf->paths[i].is_closed) + continue; + Vertex_handle first, prev; + for (int j = 1; j < dxf->paths[i].points.count(); j++) { + double x = dxf->paths[i].points[j]->x; + double y = dxf->paths[i].points[j]->y; + point_to_path.data(x, y, h) = QPair(i, j); + Vertex_handle vh = cdt.insert(CDTPoint(x, y)); + if (j == 1) { + first = vh; + } + else { + cdt.insert_constraint(prev, vh); + } + prev = vh; + } + cdt.insert_constraint(prev, first); + } + + std::list list_of_seeds; +// FIXME: Give hints about holes here +// list_of_seeds.push_back(CDTPoint(-1, -1)); +// list_of_seeds.push_back(CDTPoint(20, 50)); + CGAL::refine_Delaunay_mesh_2_without_edge_refinement(cdt, list_of_seeds.begin(), list_of_seeds.end(), + DummyCriteria()); + CDT::Finite_faces_iterator iter = cdt.finite_faces_begin(); + for( ; iter != cdt.finite_faces_end(); ++iter) { + if (!iter->is_in_domain()) continue; + ps->append_poly(); + + int path[3], point[3]; + for (int i=0;i<3;i++) { + int idx = up ? i : (2-i); + double px = iter->vertex(idx)->point()[0]; + double py = iter->vertex(idx)->point()[1]; + ps->append_vertex(px * cos(rot*M_PI/180) + py * sin(rot*M_PI/180), + px * -sin(rot*M_PI/180) + py * cos(rot*M_PI/180), + h); + path[i] = point_to_path.data(px, py, h).first; + point[i] = point_to_path.data(px, py, h).second; + } + + if (path[0] == path[1] && point[0] == 1 && point[1] == 2) + dxf->paths[path[0]].is_inner = up; + if (path[0] == path[1] && point[0] == 2 && point[1] == 1) + dxf->paths[path[0]].is_inner = !up; + if (path[1] == path[2] && point[1] == 1 && point[2] == 2) + dxf->paths[path[1]].is_inner = up; + if (path[1] == path[2] && point[1] == 2 && point[2] == 1) + dxf->paths[path[1]].is_inner = !up; + + if (path[2] == path[0] && point[2] == 1 && point[0] == 2) + dxf->paths[path[2]].is_inner = up; + if (path[2] == path[0] && point[2] == 2 && point[0] == 1) + dxf->paths[path[2]].is_inner = !up; + } +} diff --git a/src/dxftess-glu.cc b/src/dxftess-glu.cc new file mode 100644 index 0000000..9e5f530 --- /dev/null +++ b/src/dxftess-glu.cc @@ -0,0 +1,376 @@ +#ifdef WIN32 +# define STDCALL __stdcall +#else +# define STDCALL +#endif + +#undef DEBUG_TRIANGLE_SPLITTING + +struct tess_vdata { + GLdouble v[3]; +}; + +struct tess_triangle { + GLdouble *p[3]; + tess_triangle() { p[0] = NULL; p[1] = NULL; p[2] = NULL; } + tess_triangle(double *p1, double *p2, double *p3) { p[0] = p1; p[1] = p2; p[2] = p3; } +}; + +static GLenum tess_type; +static int tess_count; +static QVector tess_tri; +static GLdouble *tess_p1, *tess_p2; + +static void STDCALL tess_vertex(void *vertex_data) +{ + GLdouble *p = (double*)vertex_data; +#if 0 + printf(" %d: %f %f %f\n", tess_count, p[0], p[1], p[2]); +#endif + if (tess_type == GL_TRIANGLE_FAN) { + if (tess_count == 0) { + tess_p1 = p; + } + if (tess_count == 1) { + tess_p2 = p; + } + if (tess_count > 1) { + tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); + tess_p2 = p; + } + } + if (tess_type == GL_TRIANGLE_STRIP) { + if (tess_count == 0) { + tess_p1 = p; + } + if (tess_count == 1) { + tess_p2 = p; + } + if (tess_count > 1) { + if (tess_count % 2 == 1) { + tess_tri.append(tess_triangle(tess_p2, tess_p1, p)); + } else { + tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); + } + tess_p1 = tess_p2; + tess_p2 = p; + } + } + if (tess_type == GL_TRIANGLES) { + if (tess_count == 0) { + tess_p1 = p; + } + if (tess_count == 1) { + tess_p2 = p; + } + if (tess_count == 2) { + tess_tri.append(tess_triangle(tess_p1, tess_p2, p)); + tess_count = -1; + } + } + tess_count++; +} + +static void STDCALL tess_begin(GLenum type) +{ +#if 0 + if (type == GL_TRIANGLE_FAN) { + printf("GL_TRIANGLE_FAN:\n"); + } + if (type == GL_TRIANGLE_STRIP) { + printf("GL_TRIANGLE_STRIP:\n"); + } + if (type == GL_TRIANGLES) { + printf("GL_TRIANGLES:\n"); + } +#endif + tess_count = 0; + tess_type = type; +} + +static void STDCALL tess_end(void) +{ + /* nothing to be done here */ +} + +static void STDCALL tess_error(GLenum errno) +{ + fprintf(stderr, "GLU tesselation error %s", gluErrorString(errno)); + PRINTF("GLU tesselation error %s", gluErrorString(errno)); +} + +static void STDCALL tess_begin_data() +{ + PRINTF("GLU tesselation BEGIN_DATA\n"); +} + +static void STDCALL tess_edge_flag(GLboolean flag) +{ +// PRINTF("GLU tesselation EDGE_FLAG\n"); +} + +static void STDCALL tess_edge_flag_data(GLboolean flag, void *polygon_data) +{ + PRINTF("GLU tesselation EDGE_FLAG_DATA\n"); +} +static void STDCALL tess_vertex_data(void *vertex_data, void *polygon_data) +{ + PRINTF("GLU tesselation VERTEX_DATA\n"); +} +static void STDCALL tess_end_data(void *polygon_data) +{ + PRINTF("GLU tesselation END_DATA\n"); +} +static void STDCALL tess_combine(GLdouble coords[3], void *vertex_data[4], + GLfloat weight[4], void **outData ) +{ + PRINTF("GLU tesselation COMBINE\n"); +} +static void STDCALL tess_combine_data(GLdouble coords[3], void *vertex_data[4], + GLfloat weight[4], void **outData, + void *polygon_data) +{ + PRINTF("GLU tesselation COMBINE_DATA\n"); +} +static void STDCALL tess_error_data(GLenum errno, void *polygon_data ) +{ + PRINTF("GLU tesselation ERROR_DATA\n"); +} + +static bool point_on_line(double *p1, double *p2, double *p3) +{ + if (fabs(p1[0] - p2[0]) < 0.00001 && fabs(p1[1] - p2[1]) < 0.00001) + return false; + + if (fabs(p3[0] - p2[0]) < 0.00001 && fabs(p3[1] - p2[1]) < 0.00001) + return false; + + double v1[2] = { p2[0] - p1[0], p2[1] - p1[1] }; + double v2[2] = { p3[0] - p1[0], p3[1] - p1[1] }; + + if (sqrt(v1[0]*v1[0] + v1[1]*v1[1]) > sqrt(v2[0]*v2[0] + v2[1]*v2[1])) + return false; + + if (fabs(v1[0]) > fabs(v1[1])) { + // y = x * dy/dx + if (v2[0] == 0 || ((v1[0] > 0) != (v2[0] > 0))) + return false; + double v1_dy_dx = v1[1] / v1[0]; + double v2_dy_dx = v2[1] / v2[0]; + if (fabs(v1_dy_dx - v2_dy_dx) > 1e-15) + return false; + } else { + // x = y * dx/dy + if (v2[1] == 0 || ((v1[1] > 0) != (v2[1] > 0))) + return false; + double v1_dy_dx = v1[0] / v1[1]; + double v2_dy_dx = v2[0] / v2[1]; + if (fabs(v1_dy_dx - v2_dy_dx) > 1e-15) + return false; + } + +#if 0 + printf("Point on line: %f/%f %f/%f %f/%f\n", p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); +#endif + return true; +} + +/*! + up: true if the polygon is facing in the normal direction (i.e. normal = [0,0,1]) + rot: CLOCKWISE rotation around positive Z axis + */ + +void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h) +{ + GLUtesselator *tobj = gluNewTess(); + + gluTessCallback(tobj, GLU_TESS_VERTEX, (void(STDCALL *)())&tess_vertex); + gluTessCallback(tobj, GLU_TESS_BEGIN, (void(STDCALL *)())&tess_begin); + gluTessCallback(tobj, GLU_TESS_END, (void(STDCALL *)())&tess_end); + gluTessCallback(tobj, GLU_TESS_ERROR, (void(STDCALL *)())&tess_error); + + gluTessCallback(tobj, GLU_TESS_EDGE_FLAG, (void(STDCALL *)())&tess_edge_flag); +// gluTessCallback(tobj, GLU_TESS_COMBINE, (void(STDCALL *)())&tess_combine); + +/* gluTessCallback(tobj, GLU_TESS_BEGIN_DATA, (void(STDCALL *)())&tess_begin_data); */ +/* gluTessCallback(tobj, GLU_TESS_EDGE_FLAG_DATA, (void(STDCALL *)())&tess_edge_flag_data); */ +/* gluTessCallback(tobj, GLU_TESS_VERTEX_DATA, (void(STDCALL *)())&tess_vertex_data); */ +/* gluTessCallback(tobj, GLU_TESS_END_DATA, (void(STDCALL *)())&tess_end_data); */ +/* gluTessCallback(tobj, GLU_TESS_COMBINE_DATA, (void(STDCALL *)())&tess_combine_data); */ +/* gluTessCallback(tobj, GLU_TESS_ERROR_DATA, (void(STDCALL *)())&tess_error_data); */ + + + tess_tri.clear(); + QList vl; + + gluTessBeginPolygon(tobj, NULL); + + gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); + if (up) { + gluTessNormal(tobj, 0, 0, -1); + } else { + gluTessNormal(tobj, 0, 0, +1); + } + + Grid3d< QPair > point_to_path(GRID_COARSE); + + for (int i = 0; i < dxf->paths.count(); i++) { + if (!dxf->paths[i].is_closed) + continue; + gluTessBeginContour(tobj); + for (int j = 1; j < dxf->paths[i].points.count(); j++) { + point_to_path.data(dxf->paths[i].points[j]->x, + dxf->paths[i].points[j]->y, + h) = QPair(i, j); + vl.append(tess_vdata()); + vl.last().v[0] = dxf->paths[i].points[j]->x; + vl.last().v[1] = dxf->paths[i].points[j]->y; + vl.last().v[2] = h; + gluTessVertex(tobj, vl.last().v, vl.last().v); + } + gluTessEndContour(tobj); + } + + gluTessEndPolygon(tobj); + gluDeleteTess(tobj); + +#if 0 + for (int i = 0; i < tess_tri.count(); i++) { + printf("~~~\n"); + printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]); + printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]); + printf(" %f %f %f\n", tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]); + } +#endif + + // GLU tessing sometimes generates degenerated triangles. We must find and remove + // them so we can use the triangle array with CGAL.. + for (int i = 0; i < tess_tri.count(); i++) { + if (point_on_line(tess_tri[i].p[0], tess_tri[i].p[1], tess_tri[i].p[2]) || + point_on_line(tess_tri[i].p[1], tess_tri[i].p[2], tess_tri[i].p[0]) || + point_on_line(tess_tri[i].p[2], tess_tri[i].p[0], tess_tri[i].p[1])) { + // printf("DEBUG: Removed triangle\n"); + tess_tri.remove(i--); + } + } + + // GLU tessing creates T-junctions. This is ok for GL displaying but creates + // invalid polyhedrons for CGAL. So we split this tirangles up again in order + // to create polyhedrons that are also accepted by CGAL.. + // All triangle edges are sorted by their atan2 and only edges with a simmilar atan2 + // value are compared. This speeds up this code block dramatically (compared to the + // n^2 compares that are neccessary in the trivial implementation). +#if 1 + if (do_triangle_splitting) + { + bool added_triangles = true; + typedef QPair QPair_ii; + QHash tri_by_atan2; + for (int i = 0; i < tess_tri.count(); i++) + for (int j = 0; j < 3; j++) { + int ai = (int)round(atan2(fabs(tess_tri[i].p[(j+1)%3][0] - tess_tri[i].p[j][0]), + fabs(tess_tri[i].p[(j+1)%3][1] - tess_tri[i].p[j][1])) / 0.001); + tri_by_atan2.insertMulti(ai, QPair(i, j)); + } + while (added_triangles) + { + added_triangles = false; +#ifdef DEBUG_TRIANGLE_SPLITTING + printf("*** Triangle splitting (%d) ***\n", tess_tri.count()+1); +#endif + for (int i = 0; i < tess_tri.count(); i++) + for (int k = 0; k < 3; k++) + { + QHash possible_neigh; + int ai = (int)floor(atan2(fabs(tess_tri[i].p[(k+1)%3][0] - tess_tri[i].p[k][0]), + fabs(tess_tri[i].p[(k+1)%3][1] - tess_tri[i].p[k][1])) / 0.001 - 0.5); + for (int j = 0; j < 2; j++) { + foreach (const QPair_ii &jl, tri_by_atan2.values(ai+j)) + if (i != jl.first) + possible_neigh[jl] = jl; + } +#ifdef DEBUG_TRIANGLE_SPLITTING + printf("%d/%d: %d\n", i, k, possible_neigh.count()); +#endif + foreach (const QPair_ii &jl, possible_neigh) { + int j = jl.first; + for (int l = jl.second; l != (jl.second + 2) % 3; l = (l + 1) % 3) + if (point_on_line(tess_tri[i].p[k], tess_tri[j].p[l], tess_tri[i].p[(k+1)%3])) { +#ifdef DEBUG_TRIANGLE_SPLITTING + printf("%% %f %f %f %f %f %f [%d %d]\n", + tess_tri[i].p[k][0], tess_tri[i].p[k][1], + tess_tri[j].p[l][0], tess_tri[j].p[l][1], + tess_tri[i].p[(k+1)%3][0], tess_tri[i].p[(k+1)%3][1], + i, j); +#endif + tess_tri.append(tess_triangle(tess_tri[j].p[l], + tess_tri[i].p[(k+1)%3], tess_tri[i].p[(k+2)%3])); + for (int m = 0; m < 2; m++) { + int ai = (int)round(atan2(fabs(tess_tri.last().p[(m+1)%3][0] - tess_tri.last().p[m][0]), + fabs(tess_tri.last().p[(m+1)%3][1] - tess_tri.last().p[m][1])) / 0.001 ); + tri_by_atan2.insertMulti(ai, QPair(tess_tri.count()-1, m)); + } + tess_tri[i].p[(k+1)%3] = tess_tri[j].p[l]; + for (int m = 0; m < 2; m++) { + int ai = (int)round(atan2(fabs(tess_tri[i].p[(m+1)%3][0] - tess_tri[i].p[m][0]), + fabs(tess_tri[i].p[(m+1)%3][1] - tess_tri[i].p[m][1])) / 0.001 ); + tri_by_atan2.insertMulti(ai, QPair(i, m)); + } + added_triangles = true; + } + } + } + } + } +#endif + + for (int i = 0; i < tess_tri.count(); i++) + { +#if 0 + printf("---\n"); + printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]); + printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]); + printf(" %f %f %f\n", tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]); +#endif + double x, y; + ps->append_poly(); + + x = tess_tri[i].p[0][0] * cos(rot*M_PI/180) + tess_tri[i].p[0][1] * sin(rot*M_PI/180); + y = tess_tri[i].p[0][0] * -sin(rot*M_PI/180) + tess_tri[i].p[0][1] * cos(rot*M_PI/180); + ps->insert_vertex(x, y, tess_tri[i].p[0][2]); + + x = tess_tri[i].p[1][0] * cos(rot*M_PI/180) + tess_tri[i].p[1][1] * sin(rot*M_PI/180); + y = tess_tri[i].p[1][0] * -sin(rot*M_PI/180) + tess_tri[i].p[1][1] * cos(rot*M_PI/180); + ps->insert_vertex(x, y, tess_tri[i].p[1][2]); + + x = tess_tri[i].p[2][0] * cos(rot*M_PI/180) + tess_tri[i].p[2][1] * sin(rot*M_PI/180); + y = tess_tri[i].p[2][0] * -sin(rot*M_PI/180) + tess_tri[i].p[2][1] * cos(rot*M_PI/180); + ps->insert_vertex(x, y, tess_tri[i].p[2][2]); + + int i0 = point_to_path.data(tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]).first; + int j0 = point_to_path.data(tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]).second; + + int i1 = point_to_path.data(tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]).first; + int j1 = point_to_path.data(tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]).second; + + int i2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).first; + int j2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).second; + + if (i0 == i1 && j0 == 1 && j1 == 2) + dxf->paths[i0].is_inner = !up; + if (i0 == i1 && j0 == 2 && j1 == 1) + dxf->paths[i0].is_inner = up; + + if (i1 == i2 && j1 == 1 && j2 == 2) + dxf->paths[i1].is_inner = !up; + if (i1 == i2 && j1 == 2 && j2 == 1) + dxf->paths[i1].is_inner = up; + + if (i2 == i0 && j2 == 1 && j0 == 2) + dxf->paths[i2].is_inner = !up; + if (i2 == i0 && j2 == 2 && j0 == 1) + dxf->paths[i2].is_inner = up; + } + + tess_tri.clear(); +} diff --git a/src/dxftess.cc b/src/dxftess.cc new file mode 100644 index 0000000..c0d4c0c --- /dev/null +++ b/src/dxftess.cc @@ -0,0 +1,54 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#ifdef CGAL_TESSELATE +#include "dxftess-cgal.cc" +#else +#include "dxftess-glu.cc" +#endif // CGAL_TESSELATE + +/*! + Converts all paths in the given DxfData to PolySet::borders polygons + without tesselating. Vertex ordering of the resulting polygons + will follow the paths' is_inner flag. +*/ +void dxf_border_to_ps(PolySet *ps, DxfData *dxf) +{ + for (int i = 0; i < dxf->paths.count(); i++) { + const DxfData::Path &pt = dxf->paths[i]; + if (!pt.is_closed) + continue; + ps->borders.append(PolySet::Polygon()); + for (int j = 1; j < pt.points.count(); j++) { + double x = pt.points[j]->x, y = pt.points[j]->y, z = 0.0; + ps->grid.align(x, y, z); + if (pt.is_inner) { + ps->borders.last().append(PolySet::Point(x, y, z)); + } else { + ps->borders.last().insert(0, PolySet::Point(x, y, z)); + } + } + } +} diff --git a/src/export.cc b/src/export.cc new file mode 100644 index 0000000..cdfbef6 --- /dev/null +++ b/src/export.cc @@ -0,0 +1,151 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" +#include "MainWindow.h" + +#include + +#ifdef ENABLE_CGAL + +void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd) +{ + CGAL_Polyhedron P; + root_N->p3.convert_to_Polyhedron(P); + + typedef CGAL_Polyhedron::Vertex Vertex; + typedef CGAL_Polyhedron::Vertex_const_iterator VCI; + typedef CGAL_Polyhedron::Facet_const_iterator FCI; + typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; + + FILE *f = fopen(filename.toUtf8().data(), "w"); + if (!f) { + PRINTA("Can't open STL file \"%1\" for STL export: %2", + filename, QString(strerror(errno))); + MainWindow::current_win = NULL; + return; + } + fprintf(f, "solid\n"); + + int facet_count = 0; + for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + Vertex v1, v2, v3; + v1 = *VCI((hc++)->vertex()); + v3 = *VCI((hc++)->vertex()); + do { + v2 = v3; + v3 = *VCI((hc++)->vertex()); + double x1 = CGAL::to_double(v1.point().x()); + double y1 = CGAL::to_double(v1.point().y()); + double z1 = CGAL::to_double(v1.point().z()); + double x2 = CGAL::to_double(v2.point().x()); + double y2 = CGAL::to_double(v2.point().y()); + double z2 = CGAL::to_double(v2.point().z()); + double x3 = CGAL::to_double(v3.point().x()); + double y3 = CGAL::to_double(v3.point().y()); + double z3 = CGAL::to_double(v3.point().z()); + QString vs1, vs2, vs3; + vs1.sprintf("%f %f %f", x1, y1, z1); + vs2.sprintf("%f %f %f", x2, y2, z2); + vs3.sprintf("%f %f %f", x3, y3, z3); + if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) { + + double nx = (y1-y2)*(z1-z3) - (z1-z2)*(y1-y3); + double ny = (z1-z2)*(x1-x3) - (x1-x2)*(z1-z3); + double nz = (x1-x2)*(y1-y3) - (y1-y2)*(x1-x3); + double n_scale = 1 / sqrt(nx*nx + ny*ny + nz*nz); + fprintf(f, " facet normal %f %f %f\n", + nx * n_scale, ny * n_scale, nz * n_scale); + fprintf(f, " outer loop\n"); + fprintf(f, " vertex %s\n", vs1.toAscii().data()); + fprintf(f, " vertex %s\n", vs2.toAscii().data()); + fprintf(f, " vertex %s\n", vs3.toAscii().data()); + fprintf(f, " endloop\n"); + fprintf(f, " endfacet\n"); + } + } while (hc != hc_end); + if (pd) { + pd->setValue(facet_count++); + QApplication::processEvents(); + } + } + + fprintf(f, "endsolid\n"); + fclose(f); +} + +void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*) +{ + PRINTF("WARNING: OFF import is not implemented yet."); +} + +void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *) +{ + FILE *f = fopen(filename.toUtf8().data(), "w"); + if (!f) { + PRINTA("Can't open DXF file \"%1\" for DXF export: %2", + filename, QString(strerror(errno))); + MainWindow::current_win = NULL; + return; + } + + fprintf(f, " 0\n"); + fprintf(f, "SECTION\n"); + fprintf(f, " 2\n"); + fprintf(f, "ENTITIES\n"); + + DxfData dd(*root_N); + for (int i=0; ix; + double y1 = p1->y; + double x2 = p2->x; + double y2 = p2->y; + fprintf(f, " 0\n"); + fprintf(f, "LINE\n"); + fprintf(f, " 10\n"); + fprintf(f, "%f\n", x1); + fprintf(f, " 11\n"); + fprintf(f, "%f\n", x2); + fprintf(f, " 20\n"); + fprintf(f, "%f\n", y1); + fprintf(f, " 21\n"); + fprintf(f, "%f\n", y2); + } + } + + fprintf(f, " 0\n"); + fprintf(f, "ENDSEC\n"); + fprintf(f, " 0\n"); + fprintf(f, "EOF\n"); + + fclose(f); +} + +#endif + diff --git a/src/expr.cc b/src/expr.cc new file mode 100644 index 0000000..e20e977 --- /dev/null +++ b/src/expr.cc @@ -0,0 +1,179 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" + +Expression::Expression() +{ + const_value = NULL; +} + +Expression::~Expression() +{ + for (int i=0; i < children.size(); i++) + delete children[i]; + if (const_value) + delete const_value; +} + +Value Expression::evaluate(const Context *context) const +{ + if (type == "!") + return ! children[0]->evaluate(context); + if (type == "&&") + return children[0]->evaluate(context) && children[1]->evaluate(context); + if (type == "||") + return children[0]->evaluate(context) || children[1]->evaluate(context); + if (type == "*") + return children[0]->evaluate(context) * children[1]->evaluate(context); + if (type == "/") + return children[0]->evaluate(context) / children[1]->evaluate(context); + if (type == "%") + return children[0]->evaluate(context) % children[1]->evaluate(context); + if (type == "+") + return children[0]->evaluate(context) + children[1]->evaluate(context); + if (type == "-") + return children[0]->evaluate(context) - children[1]->evaluate(context); + if (type == "<") + return children[0]->evaluate(context) < children[1]->evaluate(context); + if (type == "<=") + return children[0]->evaluate(context) <= children[1]->evaluate(context); + if (type == "==") + return children[0]->evaluate(context) == children[1]->evaluate(context); + if (type == "!=") + return children[0]->evaluate(context) != children[1]->evaluate(context); + if (type == ">=") + return children[0]->evaluate(context) >= children[1]->evaluate(context); + if (type == ">") + return children[0]->evaluate(context) > children[1]->evaluate(context); + if (type == "?:") { + Value v = children[0]->evaluate(context); + if (v.type == Value::BOOL) + return children[v.b ? 1 : 2]->evaluate(context); + return Value(); + } + if (type == "[]") { + Value v1 = children[0]->evaluate(context); + Value v2 = children[1]->evaluate(context); + if (v1.type == Value::VECTOR && v2.type == Value::NUMBER) { + int i = (int)(v2.num); + if (i < v1.vec.size()) + return *v1.vec[i]; + } + return Value(); + } + if (type == "I") + return children[0]->evaluate(context).inv(); + if (type == "C") + return *const_value; + if (type == "R") { + Value v1 = children[0]->evaluate(context); + Value v2 = children[1]->evaluate(context); + Value v3 = children[2]->evaluate(context); + if (v1.type == Value::NUMBER && v2.type == Value::NUMBER && v3.type == Value::NUMBER) { + Value r = Value(); + r.type = Value::RANGE; + r.range_begin = v1.num; + r.range_step = v2.num; + r.range_end = v3.num; + return r; + } + return Value(); + } + if (type == "V") { + Value v; + v.type = Value::VECTOR; + for (int i = 0; i < children.size(); i++) + v.vec.append(new Value(children[i]->evaluate(context))); + return v; + } + if (type == "L") + return context->lookup_variable(var_name); + if (type == "N") + { + Value v = children[0]->evaluate(context); + + if (v.type == Value::VECTOR && var_name == QString("x")) + return *v.vec[0]; + if (v.type == Value::VECTOR && var_name == QString("y")) + return *v.vec[1]; + if (v.type == Value::VECTOR && var_name == QString("z")) + return *v.vec[2]; + + if (v.type == Value::RANGE && var_name == QString("begin")) + return Value(v.range_begin); + if (v.type == Value::RANGE && var_name == QString("step")) + return Value(v.range_step); + if (v.type == Value::RANGE && var_name == QString("end")) + return Value(v.range_end); + + return Value(); + } + if (type == "F") { + QVector argvalues; + for (int i=0; i < children.size(); i++) + argvalues.append(children[i]->evaluate(context)); + return context->evaluate_function(call_funcname, call_argnames, argvalues); + } + abort(); +} + +QString Expression::dump() const +{ + if (type == "*" || type == "/" || type == "%" || type == "+" || type == "-" || + type == "<" || type == "<=" || type == "==" || type == "!=" || type == ">=" || type == ">") + return QString("(%1 %2 %3)").arg(children[0]->dump(), QString(type), children[1]->dump()); + if (type == "?:") + return QString("(%1 ? %2 : %3)").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); + if (type == "[]") + return QString("(%1[%2])").arg(children[0]->dump(), children[1]->dump()); + if (type == "I") + return QString("(-%1)").arg(children[0]->dump()); + if (type == "C") + return const_value->dump(); + if (type == "R") + return QString("[%1 : %2 : %3]").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); + if (type == "V") { + QString text = QString("["); + for (int i=0; i < children.size(); i++) { + if (i > 0) + text += QString(", "); + text += children[i]->dump(); + } + return text + QString("]"); + } + if (type == "L") + return var_name; + if (type == "N") + return QString("(%1.%2)").arg(children[0]->dump(), var_name); + if (type == "F") { + QString text = call_funcname + QString("("); + for (int i=0; i < children.size(); i++) { + if (i > 0) + text += QString(", "); + if (!call_argnames[i].isEmpty()) + text += call_argnames[i] + QString(" = "); + text += children[i]->dump(); + } + return text + QString(")"); + } + abort(); +} + diff --git a/src/func.cc b/src/func.cc new file mode 100644 index 0000000..1d70d98 --- /dev/null +++ b/src/func.cc @@ -0,0 +1,247 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" + +AbstractFunction::~AbstractFunction() +{ +} + +Value AbstractFunction::evaluate(const Context*, const QVector&, const QVector&) const +{ + return Value(); +} + +QString AbstractFunction::dump(QString indent, QString name) const +{ + return QString("%1abstract function %2();\n").arg(indent, name); +} + +Function::~Function() +{ + for (int i=0; i < argexpr.size(); i++) + delete argexpr[i]; + delete expr; +} + +Value Function::evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const +{ + Context c(ctx); + c.args(argnames, argexpr, call_argnames, call_argvalues); + if (expr) + return expr->evaluate(&c); + return Value(); +} + +QString Function::dump(QString indent, QString name) const +{ + QString text = QString("%1function %2(").arg(indent, name); + for (int i=0; i < argnames.size(); i++) { + if (i > 0) + text += QString(", "); + text += argnames[i]; + if (argexpr[i]) + text += QString(" = ") + argexpr[i]->dump(); + } + text += QString(") = %1;\n").arg(expr->dump()); + return text; +} + +QHash builtin_functions; + +BuiltinFunction::~BuiltinFunction() +{ +} + +Value BuiltinFunction::evaluate(const Context*, const QVector &call_argnames, const QVector &call_argvalues) const +{ + return eval_func(call_argnames, call_argvalues); +} + +QString BuiltinFunction::dump(QString indent, QString name) const +{ + return QString("%1builtin function %2();\n").arg(indent, name); +} + +static double deg2rad(double x) +{ + while (x < 0.0) + x += 360.0; + while (x >= 360.0) + x -= 360.0; + x = x * M_PI * 2.0 / 360.0; + return x; +} + +static double rad2deg(double x) +{ + x = x * 360.0 / (M_PI * 2.0); + while (x < 0.0) + x += 360.0; + while (x >= 360.0) + x -= 360.0; + return x; +} + +Value builtin_min(const QVector&, const QVector &args) +{ + if (args.size() >= 1 && args[0].type == Value::NUMBER) { + double val = args[0].num; + for (int i = 1; i < args.size(); i++) + if (args[1].type == Value::NUMBER) + val = fmin(val, args[i].num); + return Value(val); + } + return Value(); +} + +Value builtin_max(const QVector&, const QVector &args) +{ + if (args.size() >= 1 && args[0].type == Value::NUMBER) { + double val = args[0].num; + for (int i = 1; i < args.size(); i++) + if (args[1].type == Value::NUMBER) + val = fmax(val, args[i].num); + return Value(val); + } + return Value(); +} + +Value builtin_sin(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(sin(deg2rad(args[0].num))); + return Value(); +} + +Value builtin_cos(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(cos(deg2rad(args[0].num))); + return Value(); +} + +Value builtin_asin(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(rad2deg(asin(args[0].num))); + return Value(); +} + +Value builtin_acos(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(rad2deg(acos(args[0].num))); + return Value(); +} + +Value builtin_tan(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(tan(deg2rad(args[0].num))); + return Value(); +} + +Value builtin_atan(const QVector&, const QVector &args) +{ + if (args.size() == 1 && args[0].type == Value::NUMBER) + return Value(rad2deg(atan(args[0].num))); + return Value(); +} + +Value builtin_atan2(const QVector&, const QVector &args) +{ + if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) + return Value(rad2deg(atan2(args[0].num, args[1].num))); + return Value(); +} + +Value builtin_pow(const QVector&, const QVector &args) +{ + if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) + return Value(pow(args[0].num, args[1].num)); + return Value(); +} + +Value builtin_str(const QVector&, const QVector &args) +{ + QString str; + for (int i = 0; i < args.size(); i++) + { + if (args[i].type == Value::STRING) + str += args[i].text; + else + str += args[i].dump(); + } + return Value(str); +} + +Value builtin_lookup(const QVector&, const QVector &args) +{ + double p, low_p, low_v, high_p, high_v; + if (args.size() < 2 || !args[0].getnum(p) || args[1].vec.size() < 2 || args[1].vec[0]->vec.size() < 2) + return Value(); + if (!args[1].vec[0]->getv2(low_p, low_v) || !args[1].vec[0]->getv2(high_p, high_v)) + return Value(); + for (int i = 1; i < args[1].vec.size(); i++) { + double this_p, this_v; + if (args[1].vec[i]->getv2(this_p, this_v)) { + if (this_p <= p && (this_p > low_p || low_p > p)) { + low_p = this_p; + low_v = this_v; + } + if (this_p >= p && (this_p < high_p || high_p < p)) { + high_p = this_p; + high_v = this_v; + } + } + } + if (p <= low_p) + return Value(low_v); + if (p >= high_p) + return Value(high_v); + double f = (p-low_p) / (high_p-low_p); + return Value(high_v * f + low_v * (1-f)); +} + +void initialize_builtin_functions() +{ + builtin_functions["min"] = new BuiltinFunction(&builtin_min); + builtin_functions["max"] = new BuiltinFunction(&builtin_max); + builtin_functions["sin"] = new BuiltinFunction(&builtin_sin); + builtin_functions["cos"] = new BuiltinFunction(&builtin_cos); + builtin_functions["asin"] = new BuiltinFunction(&builtin_asin); + builtin_functions["acos"] = new BuiltinFunction(&builtin_acos); + builtin_functions["tan"] = new BuiltinFunction(&builtin_tan); + builtin_functions["atan"] = new BuiltinFunction(&builtin_atan); + builtin_functions["atan2"] = new BuiltinFunction(&builtin_atan2); + builtin_functions["pow"] = new BuiltinFunction(&builtin_pow); + builtin_functions["str"] = new BuiltinFunction(&builtin_str); + builtin_functions["lookup"] = new BuiltinFunction(&builtin_lookup); + initialize_builtin_dxf_dim(); +} + +void destroy_builtin_functions() +{ + foreach (AbstractFunction *v, builtin_functions) + delete v; + builtin_functions.clear(); +} + diff --git a/src/glview.cc b/src/glview.cc new file mode 100644 index 0000000..1068318 --- /dev/null +++ b/src/glview.cc @@ -0,0 +1,491 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" +#include "GLView.h" +#include "Preferences.h" + +#include +#include +#include +#include +#include + +#define FAR_FAR_AWAY 100000.0 + +GLView::GLView(QWidget *parent) : QGLWidget(parent) +{ + viewer_distance = 500; + object_rot_x = 35; + object_rot_y = 0; + object_rot_z = 25; + object_trans_x = 0; + object_trans_y = 0; + object_trans_z = 0; + + mouse_drag_active = false; + last_mouse_x = 0; + last_mouse_y = 0; + + orthomode = false; + showaxes = false; + showcrosshairs = false; + + renderfunc = NULL; + renderfunc_vp = NULL; + + for (int i = 0; i < 10; i++) + shaderinfo[i] = 0; + + statusLabel = NULL; + + setMouseTracking(true); +#ifdef ENABLE_OPENCSG + opencsg_support = true; +#endif +} + +void GLView::setRenderFunc(void (*func)(void*), void *userdata) +{ + this->renderfunc = func; + this->renderfunc_vp = userdata; +} + +void GLView::initializeGL() +{ + glEnable(GL_DEPTH_TEST); + glDepthRange(-FAR_FAR_AWAY, +FAR_FAR_AWAY); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +#ifdef ENABLE_OPENCSG + GLenum err = glewInit(); + if (GLEW_OK != err) { + fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); + } + const char *openscad_disable_gl20_env = getenv("OPENSCAD_DISABLE_GL20"); + if (openscad_disable_gl20_env && !strcmp(openscad_disable_gl20_env, "0")) + openscad_disable_gl20_env = NULL; + if (glewIsSupported("GL_VERSION_2_0") && openscad_disable_gl20_env == NULL) + { + const char *vs_source = + "uniform float xscale, yscale;\n" + "attribute vec3 pos_b, pos_c;\n" + "attribute vec3 trig, mask;\n" + "varying vec3 tp, tr;\n" + "varying float shading;\n" + "void main() {\n" + " vec4 p0 = gl_ModelViewProjectionMatrix * gl_Vertex;\n" + " vec4 p1 = gl_ModelViewProjectionMatrix * vec4(pos_b, 1.0);\n" + " vec4 p2 = gl_ModelViewProjectionMatrix * vec4(pos_c, 1.0);\n" + " float a = distance(vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n" + " float b = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w));\n" + " float c = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n" + " float s = (a + b + c) / 2.0;\n" + " float A = sqrt(s*(s-a)*(s-b)*(s-c));\n" + " float ha = 2.0*A/a;\n" + " gl_Position = p0;\n" + " tp = mask * ha;\n" + " tr = trig;\n" + " vec3 normal, lightDir;\n" + " normal = normalize(gl_NormalMatrix * gl_Normal);\n" + " lightDir = normalize(vec3(gl_LightSource[0].position));\n" + " shading = abs(dot(normal, lightDir));\n" + "}\n"; + + const char *fs_source = + "uniform vec4 color1, color2;\n" + "varying vec3 tp, tr, tmp;\n" + "varying float shading;\n" + "void main() {\n" + " gl_FragColor = vec4(color1.r * shading, color1.g * shading, color1.b * shading, color1.a);\n" + " if (tp.x < tr.x || tp.y < tr.y || tp.z < tr.z)\n" + " gl_FragColor = color2;\n" + "}\n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, (const GLchar**)&vs_source, NULL); + glCompileShader(vs); + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, (const GLchar**)&fs_source, NULL); + glCompileShader(fs); + + GLuint edgeshader_prog = glCreateProgram(); + glAttachShader(edgeshader_prog, vs); + glAttachShader(edgeshader_prog, fs); + glLinkProgram(edgeshader_prog); + + shaderinfo[0] = edgeshader_prog; + shaderinfo[1] = glGetUniformLocation(edgeshader_prog, "color1"); + shaderinfo[2] = glGetUniformLocation(edgeshader_prog, "color2"); + shaderinfo[3] = glGetAttribLocation(edgeshader_prog, "trig"); + shaderinfo[4] = glGetAttribLocation(edgeshader_prog, "pos_b"); + shaderinfo[5] = glGetAttribLocation(edgeshader_prog, "pos_c"); + shaderinfo[6] = glGetAttribLocation(edgeshader_prog, "mask"); + shaderinfo[7] = glGetUniformLocation(edgeshader_prog, "xscale"); + shaderinfo[8] = glGetUniformLocation(edgeshader_prog, "yscale"); + + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + fprintf(stderr, "OpenGL Error: %s\n", gluErrorString(err)); + } + + GLint status; + glGetProgramiv(edgeshader_prog, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + int loglen; + char logbuffer[1000]; + glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); + fprintf(stderr, "OpenGL Program Linker Error:\n%.*s", loglen, logbuffer); + } else { + int loglen; + char logbuffer[1000]; + glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); + if (loglen > 0) { + fprintf(stderr, "OpenGL Program Link OK:\n%.*s", loglen, logbuffer); + } + glValidateProgram(edgeshader_prog); + glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer); + if (loglen > 0) { + fprintf(stderr, "OpenGL Program Validation results:\n%.*s", loglen, logbuffer); + } + } + } else { + opencsg_support = false; + QTimer::singleShot(0, this, SLOT(display_opengl20_warning())); + } +#endif /* ENABLE_OPENCSG */ +} + +#ifdef ENABLE_OPENCSG +void GLView::display_opengl20_warning() +{ + QMessageBox::warning(NULL, "GLEW: GL_VERSION_2_0 is not supported!", + "Warning: No support for OpenGL 2.0 found! OpenCSG View has been disabled.\n\n" + "It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 " + "support. Please check if OpenGL 2.0 drivers are available for your " + "graphics hardware."); +} +#endif + +void GLView::resizeGL(int w, int h) +{ +#ifdef ENABLE_OPENCSG + shaderinfo[9] = w; + shaderinfo[10] = h; +#endif + glViewport(0, 0, w, h); + w_h_ratio = sqrt((double)w / (double)h); +} + +void GLView::paintGL() +{ + const QColor &col = Preferences::inst()->color(Preferences::BACKGROUND_COLOR); + glClearColor(col.redF(), col.greenF(), col.blueF(), 0.0); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (orthomode) + glOrtho(-w_h_ratio*viewer_distance/10, +w_h_ratio*viewer_distance/10, + -(1/w_h_ratio)*viewer_distance/10, +(1/w_h_ratio)*viewer_distance/10, + -FAR_FAR_AWAY, +FAR_FAR_AWAY); + else + glFrustum(-w_h_ratio, +w_h_ratio, -(1/w_h_ratio), +(1/w_h_ratio), +10.0, +FAR_FAR_AWAY); + gluLookAt(0.0, -viewer_distance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; + GLfloat light_position0[] = {-1.0, -1.0, +1.0, 0.0}; + GLfloat light_position1[] = {+1.0, +1.0, -1.0, 0.0}; + + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, light_position0); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT1, GL_POSITION, light_position1); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHTING); + glEnable(GL_NORMALIZE); + + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + + glRotated(object_rot_x, 1.0, 0.0, 0.0); + glRotated(object_rot_y, 0.0, 1.0, 0.0); + glRotated(object_rot_z, 0.0, 0.0, 1.0); + + if (showcrosshairs) + { + glLineWidth(3); + const QColor &col = Preferences::inst()->color(Preferences::CROSSHAIR_COLOR); + glColor3f(col.redF(), col.greenF(), col.blueF()); + glBegin(GL_LINES); + for (double xf = -1; xf <= +1; xf += 2) + for (double yf = -1; yf <= +1; yf += 2) { + double vd = viewer_distance/20; + glVertex3d(-xf*vd, -yf*vd, -vd); + glVertex3d(+xf*vd, +yf*vd, +vd); + } + glEnd(); + } + + glTranslated(object_trans_x, object_trans_y, object_trans_z); + + // Large gray axis cross inline with the model + if (showaxes) + { + glLineWidth(1); + glColor3d(0.5, 0.5, 0.5); + glBegin(GL_LINES); + glVertex3d(-viewer_distance/10, 0, 0); + glVertex3d(+viewer_distance/10, 0, 0); + glVertex3d(0, -viewer_distance/10, 0); + glVertex3d(0, +viewer_distance/10, 0); + glVertex3d(0, 0, -viewer_distance/10); + glVertex3d(0, 0, +viewer_distance/10); + glEnd(); + } + + glDepthFunc(GL_LESS); + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + + glLineWidth(2); + glColor3d(1.0, 0.0, 0.0); + + if (renderfunc) + renderfunc(renderfunc_vp); + + // Small axis cross in the lower left corner + // FIXME: Adjust color to keep contrast with background + if (showaxes) + { + glDepthFunc(GL_ALWAYS); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glTranslated(-0.8, -0.8, 0); + glOrtho(-w_h_ratio*1000/10, +w_h_ratio*1000/10, + -(1/w_h_ratio)*1000/10, +(1/w_h_ratio)*1000/10, + -FAR_FAR_AWAY, +FAR_FAR_AWAY); + gluLookAt(0.0, -1000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotated(object_rot_x, 1.0, 0.0, 0.0); + glRotated(object_rot_y, 0.0, 1.0, 0.0); + glRotated(object_rot_z, 0.0, 0.0, 1.0); + + glLineWidth(1); + glBegin(GL_LINES); + glColor3d(1.0, 0.0, 0.0); + glVertex3d(0, 0, 0); glVertex3d(10, 0, 0); + glColor3d(0.0, 1.0, 0.0); + glVertex3d(0, 0, 0); glVertex3d(0, 10, 0); + glColor3d(0.0, 0.0, 1.0); + glVertex3d(0, 0, 0); glVertex3d(0, 0, 10); + glEnd(); + + GLdouble mat_model[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, mat_model); + + GLdouble mat_proj[16]; + glGetDoublev(GL_PROJECTION_MATRIX, mat_proj); + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + GLdouble xlabel_x, xlabel_y, xlabel_z; + gluProject(12, 0, 0, mat_model, mat_proj, viewport, &xlabel_x, &xlabel_y, &xlabel_z); + xlabel_x = round(xlabel_x); xlabel_y = round(xlabel_y); + + GLdouble ylabel_x, ylabel_y, ylabel_z; + gluProject(0, 12, 0, mat_model, mat_proj, viewport, &ylabel_x, &ylabel_y, &ylabel_z); + ylabel_x = round(ylabel_x); ylabel_y = round(ylabel_y); + + GLdouble zlabel_x, zlabel_y, zlabel_z; + gluProject(0, 0, 12, mat_model, mat_proj, viewport, &zlabel_x, &zlabel_y, &zlabel_z); + zlabel_x = round(zlabel_x); zlabel_y = round(zlabel_y); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glTranslated(-1, -1, 0); + glScaled(2.0/viewport[2], 2.0/viewport[3], 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // FIXME: Adjust color to keep contrast with background + glColor3d(0.0, 0.0, 0.0); + glBegin(GL_LINES); + // X Label + glVertex3d(xlabel_x-3, xlabel_y-3, 0); glVertex3d(xlabel_x+3, xlabel_y+3, 0); + glVertex3d(xlabel_x-3, xlabel_y+3, 0); glVertex3d(xlabel_x+3, xlabel_y-3, 0); + // Y Label + glVertex3d(ylabel_x-3, ylabel_y-3, 0); glVertex3d(ylabel_x+3, ylabel_y+3, 0); + glVertex3d(ylabel_x-3, ylabel_y+3, 0); glVertex3d(ylabel_x, ylabel_y, 0); + // Z Label + glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y-3, 0); + glVertex3d(zlabel_x-3, zlabel_y+3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); + glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); + glEnd(); + } + + if (statusLabel) { + QString msg; + msg.sprintf("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f", + -object_trans_x, -object_trans_y, -object_trans_z, + fmodf(360 - object_rot_x + 90, 360), fmodf(360 - object_rot_y, 360), fmodf(360 - object_rot_z, 360), viewer_distance); + statusLabel->setText(msg); + } +} + +void GLView::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Plus) { + viewer_distance *= 0.9; + updateGL(); + return; + } + if (event->key() == Qt::Key_Minus) { + viewer_distance /= 0.9; + updateGL(); + return; + } +} + +void GLView::wheelEvent(QWheelEvent *event) +{ + viewer_distance *= pow(0.9, event->delta() / 120.0); + updateGL(); +} + +void GLView::mousePressEvent(QMouseEvent *event) +{ + mouse_drag_active = true; + last_mouse_x = event->globalX(); + last_mouse_y = event->globalY(); + grabMouse(); + setFocus(); +} + +static void mat_id(double *trg) +{ + for (int i = 0; i < 16; i++) + trg[i] = i%5 == 0; +} + +static void mat_mul(double *trg, const double *m1, const double *m2) +{ + double m[16]; + for (int x = 0; x < 4; x++) + for (int y = 0; y < 4; y++) + { + m[x+y*4] = 0; + for (int i = 0; i < 4; i++) + m[x+y*4] += m1[i+y*4] * m2[x+i*4]; + } + for (int i = 0; i < 16; i++) + trg[i] = m[i]; +} + +static void mat_rot(double *trg, double angle, double x, double y, double z) +{ + double s = sin(M_PI*angle/180), c = cos(M_PI*angle/180); + double cc = 1 - c; + double m[16] = { + x*x*cc+c, x*y*cc-z*s, x*z*cc+y*s, 0, + y*x*cc+z*s, y*y*cc+c, y*z*cc-x*s, 0, + x*z*cc-y*s, y*z*cc+x*s, z*z*cc+c, 0, + 0, 0, 0, 1 + }; + for (int i = 0; i < 16; i++) + trg[i] = m[i]; +} + +void GLView::mouseMoveEvent(QMouseEvent *event) +{ + int this_mouse_x = event->globalX(); + int this_mouse_y = event->globalY(); + if (mouse_drag_active) { + if ((event->buttons() & Qt::LeftButton) != 0) { + object_rot_x += (this_mouse_y-last_mouse_y) * 0.7; + if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) + object_rot_y += (this_mouse_x-last_mouse_x) * 0.7; + else + object_rot_z += (this_mouse_x-last_mouse_x) * 0.7; + while (object_rot_x < 0) + object_rot_x += 360; + while (object_rot_x >= 360) + object_rot_x -= 360; + while (object_rot_y < 0) + object_rot_y += 360; + while (object_rot_y >= 360) + object_rot_y -= 360; + while (object_rot_z < 0) + object_rot_z += 360; + while (object_rot_z >= 360) + object_rot_z -= 360; + } else { + double mx = +(this_mouse_x-last_mouse_x) * viewer_distance/1000; + double my = -(this_mouse_y-last_mouse_y) * viewer_distance/1000; + double rx[16], ry[16], rz[16], tm[16]; + mat_rot(rx, -object_rot_x, 1.0, 0.0, 0.0); + mat_rot(ry, -object_rot_y, 0.0, 1.0, 0.0); + mat_rot(rz, -object_rot_z, 0.0, 0.0, 1.0); + mat_id(tm); + mat_mul(tm, rx, tm); + mat_mul(tm, ry, tm); + mat_mul(tm, rz, tm); + double vec[16] = { + 0, 0, 0, mx, + 0, 0, 0, 0, + 0, 0, 0, my, + 0, 0, 0, 1 + }; + if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) { + vec[3] = 0; + vec[7] = my; + vec[11] = 0; + } + mat_mul(tm, tm, vec); + object_trans_x += tm[3]; + object_trans_y += tm[7]; + object_trans_z += tm[11]; + } + updateGL(); + emit doAnimateUpdate(); + } + last_mouse_x = this_mouse_x; + last_mouse_y = this_mouse_y; +} + +void GLView::mouseReleaseEvent(QMouseEvent*) +{ + mouse_drag_active = false; + releaseMouse(); +} + diff --git a/src/highlighter.cc b/src/highlighter.cc new file mode 100644 index 0000000..5e15867 --- /dev/null +++ b/src/highlighter.cc @@ -0,0 +1,46 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" + +Highlighter::Highlighter(QTextDocument *parent) + : QSyntaxHighlighter(parent) +{ +} + +void Highlighter::highlightBlock(const QString &text) +{ + int n = previousBlockState(); + if (n < 0) + n = 0; + int k = n + text.size() + 1; + setCurrentBlockState(k); + if (parser_error_pos >= n && parser_error_pos < k) { + QTextCharFormat style; + style.setBackground(Qt::red); + setFormat(0, text.size(), style); +#if 0 + style.setBackground(Qt::black); + style.setForeground(Qt::white); + setFormat(parser_error_pos - n, 1, style); +#endif + } +} + diff --git a/src/import.cc b/src/import.cc new file mode 100644 index 0000000..c9b1a66 --- /dev/null +++ b/src/import.cc @@ -0,0 +1,223 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include + +enum import_type_e { + TYPE_STL, + TYPE_OFF, + TYPE_DXF +}; + +class ImportModule : public AbstractModule +{ +public: + import_type_e type; + ImportModule(import_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class ImportNode : public AbstractPolyNode +{ +public: + import_type_e type; + QString filename; + QString layername; + int convexity; + double fn, fs, fa; + double origin_x, origin_y, scale; + ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + ImportNode *node = new ImportNode(inst, type); + + QVector argnames; + if (type == TYPE_DXF) { + argnames = QVector() << "file" << "layer" << "convexity" << "origin" << "scale"; + } else { + argnames = QVector() << "file" << "convexity"; + } + QVector argexpr; + + // Map old argnames to new argnames for compatibility + QVector inst_argnames = inst->argnames; + for (int i=0; iargvalues); + + node->fn = c.lookup_variable("$fn").num; + node->fs = c.lookup_variable("$fs").num; + node->fa = c.lookup_variable("$fa").num; + + node->filename = c.lookup_variable("file").text; + node->layername = c.lookup_variable("layer", true).text; + node->convexity = c.lookup_variable("convexity", true).num; + + if (node->convexity <= 0) + node->convexity = 1; + + Value origin = c.lookup_variable("origin", true); + node->origin_x = node->origin_y = 0; + origin.getv2(node->origin_x, node->origin_y); + + node->scale = c.lookup_variable("scale", true).num; + + if (node->scale <= 0) + node->scale = 1; + + return node; +} + +void register_builtin_import() +{ + builtin_modules["import_stl"] = new ImportModule(TYPE_STL); + builtin_modules["import_off"] = new ImportModule(TYPE_OFF); + builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF); +} + +PolySet *ImportNode::render_polyset(render_mode_e) const +{ + PolySet *p = new PolySet(); + p->convexity = convexity; + + if (type == TYPE_STL) + { + handle_dep(filename); + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) { + PRINTF("WARNING: Can't open import file `%s'.", filename.toAscii().data()); + return p; + } + + QByteArray data = f.read(5); + if (data.size() == 5 && QString(data) == QString("solid")) + { + int i = 0; + double vdata[3][3]; + QRegExp splitre = QRegExp("\\s*(vertex)?\\s+"); + f.readLine(); + while (!f.atEnd()) + { + QString line = QString(f.readLine()).remove("\n").remove("\r"); + if (line.contains("solid") || line.contains("facet") || line.contains("endloop")) + continue; + if (line.contains("outer loop")) { + i = 0; + continue; + } + if (line.contains("vertex")) { + QStringList tokens = line.split(splitre); + bool ok[3] = { false, false, false }; + if (tokens.size() == 4) { + vdata[i][0] = tokens[1].toDouble(&ok[0]); + vdata[i][1] = tokens[2].toDouble(&ok[1]); + vdata[i][2] = tokens[3].toDouble(&ok[2]); + } + if (!ok[0] || !ok[1] || !ok[2]) { + PRINTF("WARNING: Can't parse vertex line `%s'.", line.toAscii().data()); + i = 10; + } else if (++i == 3) { + p->append_poly(); + p->append_vertex(vdata[0][0], vdata[0][1], vdata[0][2]); + p->append_vertex(vdata[1][0], vdata[1][1], vdata[1][2]); + p->append_vertex(vdata[2][0], vdata[2][1], vdata[2][2]); + } + } + } + } + else + { + f.read(80-5+4); + while (1) { + struct { + float i, j, k; + float x1, y1, z1; + float x2, y2, z2; + float x3, y3, z3; + unsigned short acount; + } __attribute__ ((packed)) data; + if (f.read((char*)&data, sizeof(data)) != sizeof(data)) + break; + p->append_poly(); + p->append_vertex(data.x1, data.y1, data.z1); + p->append_vertex(data.x2, data.y2, data.z2); + p->append_vertex(data.x3, data.y3, data.z3); + } + } + } + + if (type == TYPE_OFF) + { + PRINTF("WARNING: OFF import is not implemented yet."); + } + + if (type == TYPE_DXF) + { + DxfData dd(fn, fs, fa, filename, layername, origin_x, origin_y, scale); + p->is2d = true; + dxf_tesselate(p, &dd, 0, true, false, 0); + dxf_border_to_ps(p, &dd); + } + + return p; +} + +QString ImportNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(filename.toAscii().data(), &st); + if (type == TYPE_STL) + text.sprintf("import_stl(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", + filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); + if (type == TYPE_OFF) + text.sprintf("import_off(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", + filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); + if (type == TYPE_DXF) + text.sprintf("import_dxf(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " + "origin = [ %g %g ], scale = %g, convexity = %d, " + "$fn = %g, $fa = %g, $fs = %g);\n", + filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, + layername.toAscii().data(), origin_x, origin_y, scale, convexity, + fn, fs, fa); + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + diff --git a/src/mainwin.cc b/src/mainwin.cc new file mode 100644 index 0000000..7abb4ed --- /dev/null +++ b/src/mainwin.cc @@ -0,0 +1,1800 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "MainWindow.h" +#include "Preferences.h" +#include "printutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//for chdir +#include + +#ifdef ENABLE_CGAL + +#if 1 +#include "CGAL_renderer.h" +using OpenSCAD::OGL::Polyhedron; +using OpenSCAD::OGL::SNC_BOUNDARY; +using OpenSCAD::OGL::SNC_SKELETON; +using OpenSCAD::OGL::Nef3_Converter; +#else +// a little hackish: we need access to default-private members of +// CGAL::OGL::Nef3_Converter so we can implement our own draw function +// that does not scale the model. so we define 'class' to 'struct' +// for this header.. +// +// theoretically there could be two problems: +// 1.) defining language keyword with the pre processor is illegal afair +// 2.) the compiler could use a different memory layout or name mangling for structs +// +// both does not seam to be the case with todays compilers... +// +#define class struct +#include +#undef class +using CGAL::OGL::Polyhedron; +using CGAL::OGL::SNC_BOUNDARY; +using CGAL::OGL::SNC_SKELETON; +using CGAL::OGL::Nef3_Converter; +#endif +#endif // ENABLE_CGAL + +#define QUOTE(x__) # x__ +#define QUOTED(x__) QUOTE(x__) + +static char helptitle[] = + "OpenSCAD " + QUOTED(OPENSCAD_VERSION) + " (www.openscad.org)\n"; +static char copyrighttext[] = + "Copyright (C) 2009 Clifford Wolf \n" + "\n" + "This program is free software; you can redistribute it and/or modify" + "it under the terms of the GNU General Public License as published by" + "the Free Software Foundation; either version 2 of the License, or" + "(at your option) any later version."; + +QPointer MainWindow::current_win = NULL; + +MainWindow::MainWindow(const char *filename) +{ + setupUi(this); + + root_ctx.functions_p = &builtin_functions; + root_ctx.modules_p = &builtin_modules; + root_ctx.set_variable("$fn", Value(0.0)); + root_ctx.set_variable("$fs", Value(1.0)); + root_ctx.set_variable("$fa", Value(12.0)); + root_ctx.set_variable("$t", Value(0.0)); + + Value zero3; + zero3.type = Value::VECTOR; + zero3.vec.append(new Value(0.0)); + zero3.vec.append(new Value(0.0)); + zero3.vec.append(new Value(0.0)); + root_ctx.set_variable("$vpt", zero3); + root_ctx.set_variable("$vpr", zero3); + + root_module = NULL; + absolute_root_node = NULL; + root_raw_term = NULL; + root_norm_term = NULL; + root_chain = NULL; +#ifdef ENABLE_CGAL + this->root_N = NULL; + this->recreate_cgal_ogl_p = false; + cgal_ogl_p = NULL; + cgal_ogl_ps = NULL; +#endif + + highlights_chain = NULL; + background_chain = NULL; + root_node = NULL; + enableOpenCSG = false; + + tval = 0; + fps = 0; + fsteps = 1; + + highlighter = NULL; + + editor->setWordWrapMode(QTextOption::WrapAnywhere); // Not designable + setFont("", 0); // Init default font + + screen->statusLabel = new QLabel(this); + statusBar()->addWidget(screen->statusLabel); + + animate_timer = new QTimer(this); + connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); + + connect(e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile())); + connect(e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps())); + + animate_panel->hide(); + + // File menu + connect(this->fileActionNew, SIGNAL(triggered()), this, SLOT(actionNew())); + connect(this->fileActionOpen, SIGNAL(triggered()), this, SLOT(actionOpen())); + connect(this->fileActionSave, SIGNAL(triggered()), this, SLOT(actionSave())); + connect(this->fileActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); + connect(this->fileActionReload, SIGNAL(triggered()), this, SLOT(actionReload())); + connect(this->fileActionQuit, SIGNAL(triggered()), this, SLOT(quit())); +#ifndef __APPLE__ + this->fileActionSave->setShortcut(QKeySequence(Qt::Key_F2)); + this->fileActionReload->setShortcut(QKeySequence(Qt::Key_F3)); +#endif + // Open Recent + for (int i = 0;iactionRecentFile[i] = new QAction(this); + this->actionRecentFile[i]->setVisible(false); + this->menuOpenRecent->addAction(this->actionRecentFile[i]); + connect(this->actionRecentFile[i], SIGNAL(triggered()), + this, SLOT(actionOpenRecent())); + } + this->menuOpenRecent->addSeparator(); + this->menuOpenRecent->addAction(this->fileActionClearRecent); + connect(this->fileActionClearRecent, SIGNAL(triggered()), + this, SLOT(clearRecentFiles())); + + QDir examplesdir(QApplication::instance()->applicationDirPath()); +#ifdef Q_WS_MAC + examplesdir.cd("../Resources"); // Examples can be bundled + if (!examplesdir.exists("examples")) examplesdir.cd("../../.."); +#endif + if (examplesdir.cd("examples")) { + this->examplesdir = examplesdir.path(); + + QStringList examples = examplesdir.entryList(QStringList("*.scad"), + QDir::Files | QDir::Readable, QDir::Name); + foreach (const QString &ex, examples) { + this->menuExamples->addAction(ex, this, SLOT(actionOpenExample())); + } + } + + // Edit menu + connect(this->editActionUndo, SIGNAL(triggered()), editor, SLOT(undo())); + connect(this->editActionRedo, SIGNAL(triggered()), editor, SLOT(redo())); + connect(this->editActionCut, SIGNAL(triggered()), editor, SLOT(cut())); + connect(this->editActionCopy, SIGNAL(triggered()), editor, SLOT(copy())); + connect(this->editActionPaste, SIGNAL(triggered()), editor, SLOT(paste())); + connect(this->editActionIndent, SIGNAL(triggered()), this, SLOT(editIndent())); + connect(this->editActionUnindent, SIGNAL(triggered()), this, SLOT(editUnindent())); + connect(this->editActionComment, SIGNAL(triggered()), this, SLOT(editComment())); + connect(this->editActionUncomment, SIGNAL(triggered()), this, SLOT(editUncomment())); + connect(this->editActionPasteVPT, SIGNAL(triggered()), this, SLOT(pasteViewportTranslation())); + connect(this->editActionPasteVPR, SIGNAL(triggered()), this, SLOT(pasteViewportRotation())); + connect(this->editActionZoomIn, SIGNAL(triggered()), editor, SLOT(zoomIn())); + connect(this->editActionZoomOut, SIGNAL(triggered()), editor, SLOT(zoomOut())); + connect(this->editActionHide, SIGNAL(triggered()), this, SLOT(hideEditor())); + connect(this->editActionPreferences, SIGNAL(triggered()), this, SLOT(preferences())); + + // Design menu + connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadCompile())); + connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionCompile())); +#ifdef ENABLE_CGAL + connect(this->designActionCompileAndRender, SIGNAL(triggered()), this, SLOT(actionRenderCGAL())); +#else + this->designActionCompileAndRender->setVisible(false); +#endif + connect(this->designActionDisplayAST, SIGNAL(triggered()), this, SLOT(actionDisplayAST())); + connect(this->designActionDisplayCSGTree, SIGNAL(triggered()), this, SLOT(actionDisplayCSGTree())); + connect(this->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts())); + connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL())); + connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF())); + connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF())); + connect(this->designActionFlushCaches, SIGNAL(triggered()), this, SLOT(actionFlushCaches())); + + // View menu +#ifndef ENABLE_OPENCSG + this->viewActionOpenCSG->setVisible(false); +#else + connect(this->viewActionOpenCSG, SIGNAL(triggered()), this, SLOT(viewModeOpenCSG())); + if (!screen->hasOpenCSGSupport()) { + this->viewActionOpenCSG->setEnabled(false); + } +#endif + +#ifdef ENABLE_CGAL + connect(this->viewActionCGALSurfaces, SIGNAL(triggered()), this, SLOT(viewModeCGALSurface())); + connect(this->viewActionCGALGrid, SIGNAL(triggered()), this, SLOT(viewModeCGALGrid())); +#else + this->viewActionCGALSurfaces->setVisible(false); + this->viewActionCGALGrid->setVisible(false); +#endif + connect(this->viewActionThrownTogether, SIGNAL(triggered()), this, SLOT(viewModeThrownTogether())); + connect(this->viewActionShowEdges, SIGNAL(triggered()), this, SLOT(viewModeShowEdges())); + connect(this->viewActionShowAxes, SIGNAL(triggered()), this, SLOT(viewModeShowAxes())); + connect(this->viewActionShowCrosshairs, SIGNAL(triggered()), this, SLOT(viewModeShowCrosshairs())); + connect(this->viewActionAnimate, SIGNAL(triggered()), this, SLOT(viewModeAnimate())); + connect(this->viewActionTop, SIGNAL(triggered()), this, SLOT(viewAngleTop())); + connect(this->viewActionBottom, SIGNAL(triggered()), this, SLOT(viewAngleBottom())); + connect(this->viewActionLeft, SIGNAL(triggered()), this, SLOT(viewAngleLeft())); + connect(this->viewActionRight, SIGNAL(triggered()), this, SLOT(viewAngleRight())); + connect(this->viewActionFront, SIGNAL(triggered()), this, SLOT(viewAngleFront())); + connect(this->viewActionBack, SIGNAL(triggered()), this, SLOT(viewAngleBack())); + connect(this->viewActionDiagonal, SIGNAL(triggered()), this, SLOT(viewAngleDiagonal())); + connect(this->viewActionCenter, SIGNAL(triggered()), this, SLOT(viewCenter())); + connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective())); + connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal())); + connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole())); + +// #ifdef ENABLE_CGAL +// viewActionCGALSurface = menu->addAction("CGAL Surfaces", this, SLOT(viewModeCGALSurface()), QKeySequence(Qt::Key_F10)); +// viewActionCGALGrid = menu->addAction("CGAL Grid Only", this, SLOT(viewModeCGALGrid()), QKeySequence(Qt::Key_F11)); +// #endif + + // Help menu + connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout())); + connect(this->helpActionManual, SIGNAL(triggered()), this, SLOT(helpManual())); + + + console->setReadOnly(true); + current_win = this; + + PRINT(helptitle); + PRINT(copyrighttext); + PRINT(""); + + if (filename) { + openFile(filename); + } else { + setFileName(""); + } + + connect(editor->document(), SIGNAL(contentsChanged()), this, SLOT(animateUpdateDocChanged())); + connect(editor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool))); + connect(editor->document(), SIGNAL(modificationChanged(bool)), fileActionSave, SLOT(setEnabled(bool))); + connect(screen, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate())); + + connect(Preferences::inst(), SIGNAL(requestRedraw()), this->screen, SLOT(updateGL())); + connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)), + this, SLOT(setFont(const QString&,uint))); + Preferences::inst()->apply(); + + + // display this window and check for OpenGL 2.0 (OpenCSG) support + viewModeThrownTogether(); + show(); + + // make sure it looks nice.. + resize(800, 600); + splitter1->setSizes(QList() << 400 << 400); + splitter2->setSizes(QList() << 400 << 200); + +#ifdef ENABLE_OPENCSG + viewModeOpenCSG(); +#else + viewModeThrownTogether(); +#endif + viewPerspective(); + + setAcceptDrops(true); + current_win = NULL; +} + +MainWindow::~MainWindow() +{ + if (root_module) + delete root_module; + if (root_node) + delete root_node; +#ifdef ENABLE_CGAL + if (this->root_N) + delete this->root_N; + if (cgal_ogl_p) { + Polyhedron *p = (Polyhedron*)cgal_ogl_p; + delete p; + } + if (cgal_ogl_ps) + cgal_ogl_ps->unlink(); +#endif +} + +/*! + Requests to open a file from an external event, e.g. by double-clicking a filename. + */ +#ifdef ENABLE_MDI +void MainWindow::requestOpenFile(const QString &filename) +{ + new MainWindow(filename.toUtf8()); +} +#else +void MainWindow::requestOpenFile(const QString &) +{ +} +#endif + +void +MainWindow::openFile(const QString &new_filename) +{ +#ifdef ENABLE_MDI + if (!editor->toPlainText().isEmpty()) { + new MainWindow(new_filename.toUtf8()); + current_win = NULL; + return; + } +#endif + setFileName(new_filename); + + load(); +} + +void +MainWindow::setFileName(const QString &filename) +{ + if (filename.isEmpty()) { + this->fileName.clear(); + setWindowTitle("OpenSCAD - New Document[*]"); + } + else { + QFileInfo fileinfo(filename); + QString infoFileName = fileinfo.canonicalFilePath(); + setWindowTitle("OpenSCAD - " + fileinfo.fileName() + "[*]"); + + // Check that the canonical file path exists - only update recent files + // if it does. Should prevent empty list items on initial open etc. + if (!infoFileName.isEmpty()) { + this->fileName = infoFileName; + QSettings settings; // already set up properly via main.cpp + QStringList files = settings.value("recentFileList").toStringList(); + files.removeAll(this->fileName); + files.prepend(this->fileName); + while (files.size() > maxRecentFiles) + files.removeLast(); + settings.setValue("recentFileList", files); + } else { + this->fileName = fileinfo.fileName(); + } + + QDir::setCurrent(fileinfo.dir().absolutePath()); + } + + foreach(QWidget *widget, QApplication::topLevelWidgets()) { + MainWindow *mainWin = qobject_cast(widget); + if (mainWin) { + mainWin->updateRecentFileActions(); + } + } +} + +void MainWindow::updatedFps() +{ + bool fps_ok; + double fps = e_fps->text().toDouble(&fps_ok); + animate_timer->stop(); + if (fps_ok && fps > 0) { + animate_timer->setSingleShot(false); + animate_timer->setInterval(int(1000 / e_fps->text().toDouble())); + animate_timer->start(); + } +} + +void MainWindow::updateTVal() +{ + bool fps_ok; + double fps = e_fps->text().toDouble(&fps_ok); + if (fps_ok) { + if (fps <= 0) { + actionCompile(); + } else { + double s = e_fsteps->text().toDouble(); + double t = e_tval->text().toDouble() + 1/s; + QString txt; + txt.sprintf("%.5f", t >= 1.0 ? 0.0 : t); + e_tval->setText(txt); + } + } +} + +void MainWindow::load() +{ + current_win = this; + if (!this->fileName.isEmpty()) { + QFile file(this->fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + PRINTA("Failed to open file: %1 (%2)", this->fileName, file.errorString()); + } + else { + QString text = QTextStream(&file).readAll(); + PRINTA("Loaded design `%1'.", this->fileName); + editor->setPlainText(text); + } + } + current_win = this; +} + +AbstractNode *MainWindow::find_root_tag(AbstractNode *n) +{ + foreach(AbstractNode *v, n->children) { + if (v->modinst->tag_root) return v; + if (AbstractNode *vroot = find_root_tag(v)) return vroot; + } + return NULL; +} + +void MainWindow::compile(bool procevents) +{ + PRINT("Parsing design (AST generation)..."); + if (procevents) + QApplication::processEvents(); + + + // Remove previous CSG tree + if (root_module) { + delete root_module; + root_module = NULL; + } + + if (absolute_root_node) { + delete absolute_root_node; + absolute_root_node = NULL; + } + + if (root_raw_term) { + root_raw_term->unlink(); + root_raw_term = NULL; + } + + if (root_norm_term) { + root_norm_term->unlink(); + root_norm_term = NULL; + } + + if (root_chain) { + delete root_chain; + root_chain = NULL; + } + + foreach(CSGTerm *v, highlight_terms) { + v->unlink(); + } + highlight_terms.clear(); + if (highlights_chain) { + delete highlights_chain; + highlights_chain = NULL; + } + foreach(CSGTerm *v, background_terms) { + v->unlink(); + } + background_terms.clear(); + if (background_chain) { + delete background_chain; + background_chain = NULL; + } + root_node = NULL; + enableOpenCSG = false; + + // Initialize special variables + root_ctx.set_variable("$t", Value(e_tval->text().toDouble())); + + Value vpt; + vpt.type = Value::VECTOR; + vpt.vec.append(new Value(-screen->object_trans_x)); + vpt.vec.append(new Value(-screen->object_trans_y)); + vpt.vec.append(new Value(-screen->object_trans_z)); + root_ctx.set_variable("$vpt", vpt); + + Value vpr; + vpr.type = Value::VECTOR; + vpr.vec.append(new Value(fmodf(360 - screen->object_rot_x + 90, 360))); + vpr.vec.append(new Value(fmodf(360 - screen->object_rot_y, 360))); + vpr.vec.append(new Value(fmodf(360 - screen->object_rot_z, 360))); + root_ctx.set_variable("$vpr", vpr); + + // Parse + last_compiled_doc = editor->toPlainText(); + root_module = parse((last_compiled_doc + "\n" + commandline_commands).toAscii().data(), false); + + // Error highlighting + if (highlighter) { + delete highlighter; + highlighter = NULL; + } + if (parser_error_pos >= 0) { + highlighter = new Highlighter(editor->document()); + } + + if (!root_module) { + if (!animate_panel->isVisible()) { + QTextCursor cursor = editor->textCursor(); + cursor.setPosition(parser_error_pos); + editor->setTextCursor(cursor); + } + goto fail; + } + + // Evaluate CSG tree + PRINT("Compiling design (CSG Tree generation)..."); + if (procevents) + QApplication::processEvents(); + + AbstractNode::idx_counter = 1; + root_inst = ModuleInstantiation(); + absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + + if (!absolute_root_node) + goto fail; + + // Do we have an explicit root node (! modifier)? + if (!(this->root_node = find_root_tag(absolute_root_node))) { + this->root_node = absolute_root_node; + } + root_node->dump(""); + + PRINT("Compiling design (CSG Products generation)..."); + if (procevents) + QApplication::processEvents(); + + double m[20]; + + for (int i = 0; i < 16; i++) + m[i] = i % 5 == 0 ? 1.0 : 0.0; + for (int i = 16; i < 20; i++) + m[i] = -1; + + // Main CSG evaluation + root_raw_term = root_node->render_csg_term(m, &highlight_terms, &background_terms); + + if (!root_raw_term) + goto fail; + + PRINT("Compiling design (CSG Products normalization)..."); + if (procevents) + QApplication::processEvents(); + + root_norm_term = root_raw_term->link(); + + // CSG normalization + while (1) { + CSGTerm *n = root_norm_term->normalize(); + root_norm_term->unlink(); + if (root_norm_term == n) + break; + root_norm_term = n; + } + + if (!root_norm_term) + goto fail; + + root_chain = new CSGChain(); + root_chain->import(root_norm_term); + + if (root_chain->polysets.size() > 1000) { + PRINTF("WARNING: Normalized tree has %d elements!", root_chain->polysets.size()); + PRINTF("WARNING: OpenCSG rendering has been disabled."); + } else { + enableOpenCSG = true; + } + + if (highlight_terms.size() > 0) + { + PRINTF("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); + if (procevents) + QApplication::processEvents(); + + highlights_chain = new CSGChain(); + for (int i = 0; i < highlight_terms.size(); i++) { + while (1) { + CSGTerm *n = highlight_terms[i]->normalize(); + highlight_terms[i]->unlink(); + if (highlight_terms[i] == n) + break; + highlight_terms[i] = n; + } + highlights_chain->import(highlight_terms[i]); + } + } + + if (background_terms.size() > 0) + { + PRINTF("Compiling background (%d CSG Trees)...", background_terms.size()); + if (procevents) + QApplication::processEvents(); + + background_chain = new CSGChain(); + for (int i = 0; i < background_terms.size(); i++) { + while (1) { + CSGTerm *n = background_terms[i]->normalize(); + background_terms[i]->unlink(); + if (background_terms[i] == n) + break; + background_terms[i] = n; + } + background_chain->import(background_terms[i]); + } + } + + if (1) { + PRINT("Compilation finished."); + if (procevents) + QApplication::processEvents(); + } else { +fail: + if (parser_error_pos < 0) { + PRINT("ERROR: Compilation failed! (no top level object found)"); + } else { + int line = 1; + QByteArray pb = last_compiled_doc.toAscii(); + char *p = pb.data(); + for (int i = 0; i < parser_error_pos; i++) { + if (p[i] == '\n') + line++; + if (p[i] == 0) { + line = -1; + break; + } + } + PRINTF("ERROR: Compilation failed! (parser error in line %d)", line); + } + if (procevents) + QApplication::processEvents(); + } +} + +void MainWindow::actionNew() +{ +#ifdef ENABLE_MDI + new MainWindow; +#else + setFileName(""); + editor->setPlainText(""); +#endif +} + +void MainWindow::actionOpen() +{ + current_win = this; + QString new_filename = QFileDialog::getOpenFileName(this, "Open File", "", "OpenSCAD Designs (*.scad)"); + if (!new_filename.isEmpty()) openFile(new_filename); + current_win = NULL; +} + +void MainWindow::actionOpenRecent() +{ + QAction *action = qobject_cast(sender()); + if (action) { + openFile(action->data().toString()); + } +} + +void MainWindow::clearRecentFiles() +{ + QSettings settings; // already set up properly via main.cpp + QStringList files; + settings.setValue("recentFileList", files); + + updateRecentFileActions(); +} + +void MainWindow::updateRecentFileActions() +{ + QSettings settings; // set up project and program properly in main.cpp + QStringList files = settings.value("recentFileList").toStringList(); + + int originalNumRecentFiles = files.size(); + + // Remove any duplicate or empty entries from the list +#if (QT_VERSION >= QT_VERSION_CHECK(4, 5, 0)) + files.removeDuplicates(); +#endif + files.removeAll(QString()); + // Now remove any entries which do not exist + for(int i = files.size()-1; i >= 0; --i) { + QFileInfo fileInfo(files[i]); + if (!QFile(fileInfo.absoluteFilePath()).exists()) + files.removeAt(i); + } + + int numRecentFiles = qMin(files.size(), + static_cast(maxRecentFiles)); + + for (int i = 0; i < numRecentFiles; ++i) { + this->actionRecentFile[i]->setText(QFileInfo(files[i]).fileName()); + this->actionRecentFile[i]->setData(files[i]); + this->actionRecentFile[i]->setVisible(true); + } + for (int j = numRecentFiles; j < maxRecentFiles; ++j) + this->actionRecentFile[j]->setVisible(false); + + // If we had to prune the list, then save the cleaned list + if (originalNumRecentFiles != numRecentFiles) + settings.setValue("recentFileList", files); +} + +void MainWindow::actionOpenExample() +{ + QAction *action = qobject_cast(sender()); + if (action) { + openFile(this->examplesdir + QDir::separator() + action->text()); + } +} + +void MainWindow::actionSave() +{ + if (this->fileName.isEmpty()) { + actionSaveAs(); + } + else { + current_win = this; + QFile file(this->fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + PRINTA("Failed to open file for writing: %1 (%2)", this->fileName, file.errorString()); + } + else { + QTextStream(&file) << this->editor->toPlainText(); + PRINTA("Saved design `%1'.", this->fileName); + this->editor->document()->setModified(false); + } + current_win = NULL; + } +} + +void MainWindow::actionSaveAs() +{ + QString new_filename = QFileDialog::getSaveFileName(this, "Save File", + this->fileName.isEmpty()?"Untitled.scad":this->fileName, + "OpenSCAD Designs (*.scad)"); + if (!new_filename.isEmpty()) { + if (QFileInfo(new_filename).suffix().isEmpty()) { + new_filename.append(".scad"); + + // Manual overwrite check since Qt doesn't do it, when using the + // defaultSuffix property + QFileInfo info(new_filename); + if (info.exists()) { + if (QMessageBox::warning(this, windowTitle(), + tr("%1 already exists.\nDo you want to replace it?").arg(info.fileName()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + } + setFileName(new_filename); + actionSave(); + } +} + +void MainWindow::actionReload() +{ + load(); +} + +void MainWindow::editIndent() +{ + QTextCursor cursor = editor->textCursor(); + int p1 = cursor.selectionStart(); + QString txt = cursor.selectedText(); + + txt.replace(QString(QChar(8233)), QString(QChar(8233)) + QString("\t")); + if (txt.endsWith(QString(QChar(8233)) + QString("\t"))) + txt.chop(1); + txt = QString("\t") + txt; + + cursor.insertText(txt); + int p2 = cursor.position(); + cursor.setPosition(p1, QTextCursor::MoveAnchor); + cursor.setPosition(p2, QTextCursor::KeepAnchor); + editor->setTextCursor(cursor); +} + +void MainWindow::editUnindent() +{ + QTextCursor cursor = editor->textCursor(); + int p1 = cursor.selectionStart(); + QString txt = cursor.selectedText(); + + txt.replace(QString(QChar(8233)) + QString("\t"), QString(QChar(8233))); + if (txt.startsWith(QString("\t"))) + txt.remove(0, 1); + + cursor.insertText(txt); + int p2 = cursor.position(); + cursor.setPosition(p1, QTextCursor::MoveAnchor); + cursor.setPosition(p2, QTextCursor::KeepAnchor); + editor->setTextCursor(cursor); +} + +void MainWindow::editComment() +{ + QTextCursor cursor = editor->textCursor(); + int p1 = cursor.selectionStart(); + QString txt = cursor.selectedText(); + + txt.replace(QString(QChar(8233)), QString(QChar(8233)) + QString("//")); + if (txt.endsWith(QString(QChar(8233)) + QString("//"))) + txt.chop(2); + txt = QString("//") + txt; + + cursor.insertText(txt); + int p2 = cursor.position(); + cursor.setPosition(p1, QTextCursor::MoveAnchor); + cursor.setPosition(p2, QTextCursor::KeepAnchor); + editor->setTextCursor(cursor); +} + +void MainWindow::editUncomment() +{ + QTextCursor cursor = editor->textCursor(); + int p1 = cursor.selectionStart(); + QString txt = cursor.selectedText(); + + txt.replace(QString(QChar(8233)) + QString("//"), QString(QChar(8233))); + if (txt.startsWith(QString("//"))) + txt.remove(0, 2); + + cursor.insertText(txt); + int p2 = cursor.position(); + cursor.setPosition(p1, QTextCursor::MoveAnchor); + cursor.setPosition(p2, QTextCursor::KeepAnchor); + editor->setTextCursor(cursor); +} + +void MainWindow::hideEditor() +{ + if (editActionHide->isChecked()) { + editor->hide(); + } else { + editor->show(); + } +} + +void MainWindow::pasteViewportTranslation() +{ + QTextCursor cursor = editor->textCursor(); + QString txt; + txt.sprintf("[ %.2f, %.2f, %.2f ]", -screen->object_trans_x, -screen->object_trans_y, -screen->object_trans_z); + cursor.insertText(txt); +} + +void MainWindow::pasteViewportRotation() +{ + QTextCursor cursor = editor->textCursor(); + QString txt; + txt.sprintf("[ %.2f, %.2f, %.2f ]", + fmodf(360 - screen->object_rot_x + 90, 360), fmodf(360 - screen->object_rot_y, 360), fmodf(360 - screen->object_rot_z, 360)); + cursor.insertText(txt); +} + +void MainWindow::actionReloadCompile() +{ + console->clear(); + + load(); + + current_win = this; + compile(true); + +#ifdef ENABLE_OPENCSG + if (!(viewActionOpenCSG->isVisible() && viewActionOpenCSG->isChecked()) && + !viewActionThrownTogether->isChecked()) { + viewModeOpenCSG(); + } + else +#endif + { + screen->updateGL(); + } + current_win = NULL; +} + +void MainWindow::actionCompile() +{ + current_win = this; + console->clear(); + + compile(!viewActionAnimate->isChecked()); + + // Go to non-CGAL view mode + if (!viewActionOpenCSG->isChecked() && !viewActionThrownTogether->isChecked()) { + viewModeOpenCSG(); + } + else { + screen->updateGL(); + } + + if (viewActionAnimate->isChecked() && e_dump->isChecked()) { + QImage img = screen->grabFrameBuffer(); + QString filename; + double s = e_fsteps->text().toDouble(); + double t = e_tval->text().toDouble(); + filename.sprintf("frame%05d.png", int(round(s*t))); + img.save(filename, "PNG"); + } + + current_win = NULL; +} + +#ifdef ENABLE_CGAL + +static void report_func(const class AbstractNode*, void *vp, int mark) +{ + QProgressDialog *pd = (QProgressDialog*)vp; + int v = (int)((mark*100.0) / progress_report_count); + pd->setValue(v < 100 ? v : 99); + QString label; + label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); + pd->setLabelText(label); + QApplication::processEvents(); +} + +void MainWindow::actionRenderCGAL() +{ + current_win = this; + console->clear(); + + compile(true); + + if (!root_module || !root_node) + return; + + if (this->root_N) { + delete this->root_N; + this->root_N = NULL; + this->recreate_cgal_ogl_p = true; + } + + PRINT("Rendering Polygon Mesh using CGAL..."); + QApplication::processEvents(); + + QTime t; + t.start(); + + QProgressDialog *pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + progress_report_prep(root_node, report_func, pd); + this->root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); + progress_report_fin(); + + PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost()); + PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size()); + QApplication::processEvents(); + + if (this->root_N->dim == 2) { + PRINTF(" Top level object is a 2D object:"); + QApplication::processEvents(); + PRINTF(" Empty: %6s", this->root_N->p2.is_empty() ? "yes" : "no"); + QApplication::processEvents(); + PRINTF(" Plane: %6s", this->root_N->p2.is_plane() ? "yes" : "no"); + QApplication::processEvents(); + PRINTF(" Vertices: %6d", (int)this->root_N->p2.explorer().number_of_vertices()); + QApplication::processEvents(); + PRINTF(" Halfedges: %6d", (int)this->root_N->p2.explorer().number_of_halfedges()); + QApplication::processEvents(); + PRINTF(" Edges: %6d", (int)this->root_N->p2.explorer().number_of_edges()); + QApplication::processEvents(); + PRINTF(" Faces: %6d", (int)this->root_N->p2.explorer().number_of_faces()); + QApplication::processEvents(); + PRINTF(" FaceCycles: %6d", (int)this->root_N->p2.explorer().number_of_face_cycles()); + QApplication::processEvents(); + PRINTF(" ConnComp: %6d", (int)this->root_N->p2.explorer().number_of_connected_components()); + QApplication::processEvents(); + } + + if (this->root_N->dim == 3) { + PRINTF(" Top level object is a 3D object:"); + PRINTF(" Simple: %6s", this->root_N->p3.is_simple() ? "yes" : "no"); + QApplication::processEvents(); + PRINTF(" Valid: %6s", this->root_N->p3.is_valid() ? "yes" : "no"); + QApplication::processEvents(); + PRINTF(" Vertices: %6d", (int)this->root_N->p3.number_of_vertices()); + QApplication::processEvents(); + PRINTF(" Halfedges: %6d", (int)this->root_N->p3.number_of_halfedges()); + QApplication::processEvents(); + PRINTF(" Edges: %6d", (int)this->root_N->p3.number_of_edges()); + QApplication::processEvents(); + PRINTF(" Halffacets: %6d", (int)this->root_N->p3.number_of_halffacets()); + QApplication::processEvents(); + PRINTF(" Facets: %6d", (int)this->root_N->p3.number_of_facets()); + QApplication::processEvents(); + PRINTF(" Volumes: %6d", (int)this->root_N->p3.number_of_volumes()); + QApplication::processEvents(); + } + + int s = t.elapsed() / 1000; + PRINTF("Total rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); + + if (!viewActionCGALSurfaces->isChecked() && !viewActionCGALGrid->isChecked()) { + viewModeCGALSurface(); + } else { + screen->updateGL(); + } + + PRINT("Rendering finished."); + + delete pd; + current_win = NULL; +} + +#endif /* ENABLE_CGAL */ + +void MainWindow::actionDisplayAST() +{ + current_win = this; + QTextEdit *e = new QTextEdit(this); + e->setWindowFlags(Qt::Window); + e->setTabStopWidth(30); + e->setWindowTitle("AST Dump"); + e->setReadOnly(true); + if (root_module) { + e->setPlainText(root_module->dump("", "")); + } else { + e->setPlainText("No AST to dump. Please try compiling first..."); + } + e->show(); + e->resize(600, 400); + current_win = NULL; +} + +void MainWindow::actionDisplayCSGTree() +{ + current_win = this; + QTextEdit *e = new QTextEdit(this); + e->setWindowFlags(Qt::Window); + e->setTabStopWidth(30); + e->setWindowTitle("CSG Tree Dump"); + e->setReadOnly(true); + if (root_node) { + e->setPlainText(root_node->dump("")); + } else { + e->setPlainText("No CSG to dump. Please try compiling first..."); + } + e->show(); + e->resize(600, 400); + current_win = NULL; +} + +void MainWindow::actionDisplayCSGProducts() +{ + current_win = this; + QTextEdit *e = new QTextEdit(this); + e->setWindowFlags(Qt::Window); + e->setTabStopWidth(30); + e->setWindowTitle("CSG Products Dump"); + e->setReadOnly(true); + e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? root_raw_term->dump() : "N/A", root_norm_term ? root_norm_term->dump() : "N/A", root_chain ? root_chain->dump() : "N/A", highlights_chain ? highlights_chain->dump() : "N/A", background_chain ? background_chain->dump() : "N/A")); + e->show(); + e->resize(600, 400); + current_win = NULL; +} + +#ifdef ENABLE_CGAL +void MainWindow::actionExportSTLorOFF(bool stl_mode) +#else +void MainWindow::actionExportSTLorOFF(bool) +#endif +{ +#ifdef ENABLE_CGAL + current_win = this; + + if (!this->root_N) { + PRINT("Nothing to export! Try building first (press F6)."); + current_win = NULL; + return; + } + + if (this->root_N->dim != 3) { + PRINT("Current top level object is not a 3D object."); + current_win = NULL; + return; + } + + if (!this->root_N->p3.is_simple()) { + PRINT("Object isn't a valid 2-manifold! Modify your design.."); + current_win = NULL; + return; + } + + QString stl_filename = QFileDialog::getSaveFileName(this, + stl_mode ? "Export STL File" : "Export OFF File", "", + stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)"); + if (stl_filename.isEmpty()) { + PRINTF("No filename specified. %s export aborted.", stl_mode ? "STL" : "OFF"); + current_win = NULL; + return; + } + + QProgressDialog *pd = new QProgressDialog( + stl_mode ? "Exporting object to STL file..." : "Exporting object to OFF file...", + QString(), 0, this->root_N->p3.number_of_facets() + 1); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + if (stl_mode) + export_stl(this->root_N, stl_filename, pd); + else + export_off(this->root_N, stl_filename, pd); + + PRINTF("%s export finished.", stl_mode ? "STL" : "OFF"); + + delete pd; + + current_win = NULL; +#endif /* ENABLE_CGAL */ +} + +void MainWindow::actionExportSTL() +{ + actionExportSTLorOFF(true); +} + +void MainWindow::actionExportOFF() +{ + actionExportSTLorOFF(false); +} + +void MainWindow::actionExportDXF() +{ +#ifdef ENABLE_CGAL + current_win = this; + + if (!this->root_N) { + PRINT("Nothing to export! Try building first (press F6)."); + current_win = NULL; + return; + } + + if (this->root_N->dim != 2) { + PRINT("Current top level object is not a 2D object."); + current_win = NULL; + return; + } + + QString stl_filename = QFileDialog::getSaveFileName(this, + "Export DXF File", "", "DXF Files (*.dxf)"); + if (stl_filename.isEmpty()) { + PRINTF("No filename specified. DXF export aborted."); + current_win = NULL; + return; + } + + export_dxf(this->root_N, stl_filename, NULL); + PRINTF("DXF export finished."); + + current_win = NULL; +#endif /* ENABLE_CGAL */ +} + +void MainWindow::actionFlushCaches() +{ + PolySet::ps_cache.clear(); + AbstractNode::cgal_nef_cache.clear(); + dxf_dim_cache.clear(); + dxf_cross_cache.clear(); +} + +void MainWindow::viewModeActionsUncheck() +{ + viewActionOpenCSG->setChecked(false); +#ifdef ENABLE_CGAL + viewActionCGALSurfaces->setChecked(false); + viewActionCGALGrid->setChecked(false); +#endif + viewActionThrownTogether->setChecked(false); +} + +#ifdef ENABLE_OPENCSG + +class OpenCSGPrim : public OpenCSG::Primitive +{ +public: + OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) : + OpenCSG::Primitive(operation, convexity) { } + PolySet *p; + double *m; + int csgmode; + virtual void render() { + glPushMatrix(); + glMultMatrixd(m); + p->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + glPopMatrix(); + } +}; + +static void renderCSGChainviaOpenCSG(CSGChain *chain, GLint *shaderinfo, bool highlight, bool background) +{ + std::vector primitives; + int j = 0; + for (int i = 0;; i++) + { + bool last = i == chain->polysets.size(); + + if (last || chain->types[i] == CSGTerm::TYPE_UNION) + { + if (j+1 != i) { + OpenCSG::render(primitives); + glDepthFunc(GL_EQUAL); + } + if (shaderinfo) + glUseProgram(shaderinfo[0]); + for (; j < i; j++) { + double *m = chain->matrices[j]; + glPushMatrix(); + glMultMatrixd(m); + int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + if (highlight) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); + } else if (background) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); + } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { + // User-defined color from source + glColor4d(m[16], m[17], m[18], m[19]); + if (shaderinfo) { + glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]); + glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); + } + chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); + } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo); + } else { + chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo); + } + glPopMatrix(); + } + if (shaderinfo) + glUseProgram(0); + for (unsigned int k = 0; k < primitives.size(); k++) { + delete primitives[k]; + } + glDepthFunc(GL_LEQUAL); + primitives.clear(); + } + + if (last) + break; + + OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? + OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); + prim->p = chain->polysets[i]; + prim->m = chain->matrices[i]; + prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + if (highlight) + prim->csgmode += 20; + else if (background) + prim->csgmode += 10; + primitives.push_back(prim); + } +} + +static void renderGLThrownTogether(void *vp); + +static void renderGLviaOpenCSG(void *vp) +{ + MainWindow *m = (MainWindow*)vp; + if (!m->enableOpenCSG) { + renderGLThrownTogether(vp); + return; + } + static int glew_initialized = 0; + if (!glew_initialized) { + glew_initialized = 1; + glewInit(); + } + if (m->root_chain) { + GLint *shaderinfo = m->screen->shaderinfo; + if (!shaderinfo[0]) + shaderinfo = NULL; + renderCSGChainviaOpenCSG(m->root_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, false); + if (m->background_chain) { + renderCSGChainviaOpenCSG(m->background_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, true); + } + if (m->highlights_chain) { + renderCSGChainviaOpenCSG(m->highlights_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, true, false); + } + } +#ifdef ENABLE_MDI + OpenCSG::reset(); +#endif +} + +/*! + Go to the OpenCSG view mode. + Falls back to thrown together mode if OpenCSG is not available +*/ +void MainWindow::viewModeOpenCSG() +{ + if (screen->hasOpenCSGSupport()) { + viewModeActionsUncheck(); + viewActionOpenCSG->setChecked(true); + screen->setRenderFunc(renderGLviaOpenCSG, this); + screen->updateGL(); + } else { + viewModeThrownTogether(); + } +} + +#endif /* ENABLE_OPENCSG */ + +#ifdef ENABLE_CGAL + +static void renderGLviaCGAL(void *vp) +{ + MainWindow *m = (MainWindow*)vp; + if (m->recreate_cgal_ogl_p) { + m->recreate_cgal_ogl_p = false; + Polyhedron *p = (Polyhedron*)m->cgal_ogl_p; + delete p; + m->cgal_ogl_p = NULL; + if (m->cgal_ogl_ps) + m->cgal_ogl_ps->unlink(); + m->cgal_ogl_ps = NULL; + } + if (!m->root_N) return; + if (m->root_N->dim == 2) + { + if (m->cgal_ogl_ps == NULL) { + DxfData dd(*m->root_N); + m->cgal_ogl_ps = new PolySet(); + m->cgal_ogl_ps->is2d = true; + dxf_tesselate(m->cgal_ogl_ps, &dd, 0, true, false, 0); + } + + // Draw 2D polygons + glDisable(GL_LIGHTING); + const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR); + glColor3f(col.redF(), col.greenF(), col.blueF()); + + for (int i=0; i < m->cgal_ogl_ps->polygons.size(); i++) { + glBegin(GL_POLYGON); + for (int j=0; j < m->cgal_ogl_ps->polygons[i].size(); j++) { + PolySet::Point p = m->cgal_ogl_ps->polygons[i][j]; + glVertex3d(p.x, p.y, -0.1); + } + glEnd(); + } + + typedef CGAL_Nef_polyhedron2::Explorer Explorer; + typedef Explorer::Face_const_iterator fci_t; + typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; + typedef Explorer::Point Point; + Explorer E = m->root_N->p2.explorer(); + + // Draw 2D edges + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glLineWidth(2); + const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR); + glColor3f(col2.redF(), col2.greenF(), col2.blueF()); + + // Extract the boundary, including inner boundaries of the polygons + for (fci_t fit = E.faces_begin(), fend = E.faces_end(); fit != fend; ++fit) + { + bool fset = false; + double fx = 0.0, fy = 0.0; + heafcc_t fcirc(E.halfedge(fit)), fend(fcirc); + CGAL_For_all(fcirc, fend) { + if(E.is_standard(E.target(fcirc))) { + Point p = E.point(E.target(fcirc)); + double x = to_double(p.x()), y = to_double(p.y()); + if (!fset) { + glBegin(GL_LINE_STRIP); + fx = x, fy = y; + fset = true; + } + glVertex3d(x, y, -0.1); + } + } + if (fset) { + glVertex3d(fx, fy, -0.1); + glEnd(); + } + } + + glEnable(GL_DEPTH_TEST); + } + else if (m->root_N->dim == 3) + { + Polyhedron *p = (Polyhedron*)m->cgal_ogl_p; + if (!p) { + Nef3_Converter::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()); + Nef3_Converter::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()); + m->cgal_ogl_p = p = new Polyhedron(); + Nef3_Converter::convert_to_OGLPolyhedron(m->root_N->p3, p); + p->init(); + } + if (m->viewActionCGALSurfaces->isChecked()) + p->set_style(SNC_BOUNDARY); + if (m->viewActionCGALGrid->isChecked()) + p->set_style(SNC_SKELETON); +#if 0 + p->draw(); +#else + if (p->style == SNC_BOUNDARY) { + glCallList(p->object_list_+2); + if (m->viewActionShowEdges->isChecked()) { + glDisable(GL_LIGHTING); + glCallList(p->object_list_+1); + glCallList(p->object_list_); + } + } else { + glDisable(GL_LIGHTING); + glCallList(p->object_list_+1); + glCallList(p->object_list_); + } +#endif + } +} + +void MainWindow::viewModeCGALSurface() +{ + viewModeActionsUncheck(); + viewActionCGALSurfaces->setChecked(true); + screen->setRenderFunc(renderGLviaCGAL, this); + screen->updateGL(); +} + +void MainWindow::viewModeCGALGrid() +{ + viewModeActionsUncheck(); + viewActionCGALGrid->setChecked(true); + screen->setRenderFunc(renderGLviaCGAL, this); + screen->updateGL(); +} + +#endif /* ENABLE_CGAL */ + +static void renderGLThrownTogetherChain(MainWindow *m, CSGChain *chain, bool highlight, bool background, bool fberror) +{ + glDepthFunc(GL_LEQUAL); + QHash,int> polySetVisitMark; + bool showEdges = m->viewActionShowEdges->isChecked(); + for (int i = 0; i < chain->polysets.size(); i++) { + if (polySetVisitMark[QPair(chain->polysets[i], chain->matrices[i])]++ > 0) + continue; + double *m = chain->matrices[i]; + glPushMatrix(); + glMultMatrixd(m); + int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + if (highlight) { + chain->polysets[i]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m); + if (showEdges) { + glDisable(GL_LIGHTING); + chain->polysets[i]->render_edges(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20)); + glEnable(GL_LIGHTING); + } + } else if (background) { + chain->polysets[i]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m); + if (showEdges) { + glDisable(GL_LIGHTING); + chain->polysets[i]->render_edges(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10)); + glEnable(GL_LIGHTING); + } + } else if (fberror) { + if (highlight) { + chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 20), m); + } else if (background) { + chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 10), m); + } else { + chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + } + } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { + glColor4d(m[16], m[17], m[18], m[19]); + chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + if (showEdges) { + glDisable(GL_LIGHTING); + glColor4d((m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); + chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode)); + glEnable(GL_LIGHTING); + } + } else if (chain->types[i] == CSGTerm::TYPE_DIFFERENCE) { + chain->polysets[i]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m); + if (showEdges) { + glDisable(GL_LIGHTING); + chain->polysets[i]->render_edges(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode)); + glEnable(GL_LIGHTING); + } + } else { + chain->polysets[i]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m); + if (showEdges) { + glDisable(GL_LIGHTING); + chain->polysets[i]->render_edges(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode)); + glEnable(GL_LIGHTING); + } + } + glPopMatrix(); + } +} + +static void renderGLThrownTogether(void *vp) +{ + MainWindow *m = (MainWindow*)vp; + if (m->root_chain) { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + renderGLThrownTogetherChain(m, m->root_chain, false, false, false); + glCullFace(GL_FRONT); + glColor3ub(255, 0, 255); + renderGLThrownTogetherChain(m, m->root_chain, false, false, true); + glDisable(GL_CULL_FACE); + } + if (m->background_chain) + renderGLThrownTogetherChain(m, m->background_chain, false, true, false); + if (m->highlights_chain) + renderGLThrownTogetherChain(m, m->highlights_chain, true, false, false); +} + +void MainWindow::viewModeThrownTogether() +{ + viewModeActionsUncheck(); + viewActionThrownTogether->setChecked(true); + screen->setRenderFunc(renderGLThrownTogether, this); + screen->updateGL(); +} + +void MainWindow::viewModeShowEdges() +{ + screen->updateGL(); +} + +void MainWindow::viewModeShowAxes() +{ + screen->setShowAxes(viewActionShowAxes->isChecked()); + screen->updateGL(); +} + +void MainWindow::viewModeShowCrosshairs() +{ + screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); + screen->updateGL(); +} + +void MainWindow::viewModeAnimate() +{ + if (viewActionAnimate->isChecked()) { + animate_panel->show(); + actionCompile(); + updatedFps(); + } else { + animate_panel->hide(); + animate_timer->stop(); + } +} + +void MainWindow::animateUpdateDocChanged() +{ + QString current_doc = editor->toPlainText(); + if (current_doc != last_compiled_doc) + animateUpdate(); +} + +void MainWindow::animateUpdate() +{ + if (animate_panel->isVisible()) { + bool fps_ok; + double fps = e_fps->text().toDouble(&fps_ok); + if (fps_ok && fps <= 0 && !animate_timer->isActive()) { + animate_timer->stop(); + animate_timer->setSingleShot(true); + animate_timer->setInterval(50); + animate_timer->start(); + } + } +} + +void MainWindow::viewAngleTop() +{ + screen->object_rot_x = 90; + screen->object_rot_y = 0; + screen->object_rot_z = 0; + screen->updateGL(); +} + +void MainWindow::viewAngleBottom() +{ + screen->object_rot_x = 270; + screen->object_rot_y = 0; + screen->object_rot_z = 0; + screen->updateGL(); +} + +void MainWindow::viewAngleLeft() +{ + screen->object_rot_x = 0; + screen->object_rot_y = 0; + screen->object_rot_z = 90; + screen->updateGL(); +} + +void MainWindow::viewAngleRight() +{ + screen->object_rot_x = 0; + screen->object_rot_y = 0; + screen->object_rot_z = 270; + screen->updateGL(); +} + +void MainWindow::viewAngleFront() +{ + screen->object_rot_x = 0; + screen->object_rot_y = 0; + screen->object_rot_z = 0; + screen->updateGL(); +} + +void MainWindow::viewAngleBack() +{ + screen->object_rot_x = 0; + screen->object_rot_y = 0; + screen->object_rot_z = 180; + screen->updateGL(); +} + +void MainWindow::viewAngleDiagonal() +{ + screen->object_rot_x = 35; + screen->object_rot_y = 0; + screen->object_rot_z = 25; + screen->updateGL(); +} + +void MainWindow::viewCenter() +{ + screen->object_trans_x = 0; + screen->object_trans_y = 0; + screen->object_trans_z = 0; + screen->updateGL(); +} + +void MainWindow::viewPerspective() +{ + viewActionPerspective->setChecked(true); + viewActionOrthogonal->setChecked(false); + screen->setOrthoMode(false); + screen->updateGL(); +} + +void MainWindow::viewOrthogonal() +{ + viewActionPerspective->setChecked(false); + viewActionOrthogonal->setChecked(true); + screen->setOrthoMode(true); + screen->updateGL(); +} + +void MainWindow::hideConsole() +{ + if (viewActionHide->isChecked()) { + console->hide(); + } else { + console->show(); + } +} + +void MainWindow::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent *event) +{ + current_win = this; + const QList urls = event->mimeData()->urls(); + for (int i = 0; i < urls.size(); i++) { + if (urls[i].scheme() != "file") + continue; + openFile(urls[i].path()); + } + current_win = NULL; +} + +void +MainWindow::helpAbout() +{ + qApp->setWindowIcon(QApplication::windowIcon()); + QMessageBox::information(this, "About OpenSCAD", QString(helptitle) + QString(copyrighttext)); +} + +void +MainWindow::helpManual() +{ + QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual")); +} + +/*! + FIXME: In SDI mode, this should be called also on New and Open + In MDI mode; also call on both reload functions? + */ +bool +MainWindow::maybeSave() +{ + if (editor->document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, "Application", + "The document has been modified.\n" + "Do you want to save your changes?", + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + if (ret == QMessageBox::Save) { + actionSave(); + return true; // FIXME: Should return false on error + } + else if (ret == QMessageBox::Cancel) { + return false; + } + } + return true; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void +MainWindow::preferences() +{ + Preferences::inst()->show(); + Preferences::inst()->activateWindow(); + Preferences::inst()->raise(); +} + +void MainWindow::setFont(const QString &family, uint size) +{ + QFont font(editor->font()); + if (!family.isEmpty()) font.setFamily(family); + if (size > 0) font.setPointSize(size); + font.setStyleHint(QFont::TypeWriter); + editor->setFont(font); +} + +void MainWindow::quit() +{ + QCloseEvent ev; + QApplication::sendEvent(QApplication::instance(), &ev); + if (ev.isAccepted()) QApplication::instance()->quit(); +} diff --git a/src/module.cc b/src/module.cc new file mode 100644 index 0000000..a319e1e --- /dev/null +++ b/src/module.cc @@ -0,0 +1,374 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +AbstractModule::~AbstractModule() +{ +} + +AbstractNode *AbstractModule::evaluate(const Context*, const ModuleInstantiation *inst) const +{ + AbstractNode *node = new AbstractNode(inst); + + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n) + node->children.append(n); + } + + return node; +} + +QString AbstractModule::dump(QString indent, QString name) const +{ + return QString("%1abstract module %2();\n").arg(indent, name); +} + +ModuleInstantiation::~ModuleInstantiation() +{ + foreach (Expression *v, argexpr) + delete v; + foreach (ModuleInstantiation *v, children) + delete v; +} + +QString ModuleInstantiation::dump(QString indent) const +{ + QString text = indent; + if (!label.isEmpty()) + text += label + QString(": "); + text += modname + QString("("); + for (int i=0; i < argnames.size(); i++) { + if (i > 0) + text += QString(", "); + if (!argnames[i].isEmpty()) + text += argnames[i] + QString(" = "); + text += argexpr[i]->dump(); + } + if (children.size() == 0) { + text += QString(");\n"); + } else if (children.size() == 1) { + text += QString(")\n"); + text += children[0]->dump(indent + QString("\t")); + } else { + text += QString(") {\n"); + for (int i = 0; i < children.size(); i++) { + text += children[i]->dump(indent + QString("\t")); + } + text += QString("%1}\n").arg(indent); + } + return text; +} + +AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const +{ + AbstractNode *node = NULL; + if (this->ctx) { + PRINTA("WARNING: Ignoring recursive module instanciation of '%1'.", modname); + } else { + ModuleInstantiation *that = (ModuleInstantiation*)this; + that->argvalues.clear(); + foreach (Expression *v, that->argexpr) { + that->argvalues.append(v->evaluate(ctx)); + } + that->ctx = ctx; + node = ctx->evaluate_module(this); + that->ctx = NULL; + that->argvalues.clear(); + } + return node; +} + +Module::~Module() +{ + foreach (Expression *v, assignments_expr) + delete v; + foreach (AbstractFunction *v, functions) + delete v; + foreach (AbstractModule *v, modules) + delete v; + foreach (ModuleInstantiation *v, children) + delete v; +} + +AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + c.inst_p = inst; + c.set_variable("$children", Value(double(inst->children.size()))); + + c.functions_p = &functions; + c.modules_p = &modules; + + for (int i = 0; i < assignments_var.size(); i++) { + c.set_variable(assignments_var[i], assignments_expr[i]->evaluate(&c)); + } + + AbstractNode *node = new AbstractNode(inst); + for (int i = 0; i < children.size(); i++) { + AbstractNode *n = children[i]->evaluate(&c); + if (n != NULL) + node->children.append(n); + } + + return node; +} + +QString Module::dump(QString indent, QString name) const +{ + QString text, tab; + if (!name.isEmpty()) { + text = QString("%1module %2(").arg(indent, name); + for (int i=0; i < argnames.size(); i++) { + if (i > 0) + text += QString(", "); + text += argnames[i]; + if (argexpr[i]) + text += QString(" = ") + argexpr[i]->dump(); + } + text += QString(") {\n"); + tab = "\t"; + } + { + QHashIterator i(functions); + while (i.hasNext()) { + i.next(); + text += i.value()->dump(indent + tab, i.key()); + } + } + { + QHashIterator i(modules); + while (i.hasNext()) { + i.next(); + text += i.value()->dump(indent + tab, i.key()); + } + } + for (int i = 0; i < assignments_var.size(); i++) { + text += QString("%1%2 = %3;\n").arg(indent + tab, assignments_var[i], assignments_expr[i]->dump()); + } + for (int i = 0; i < children.size(); i++) { + text += children[i]->dump(indent + tab); + } + if (!name.isEmpty()) { + text += QString("%1}\n").arg(indent); + } + return text; +} + +QHash builtin_modules; + +void initialize_builtin_modules() +{ + builtin_modules["group"] = new AbstractModule(); + + register_builtin_csgops(); + register_builtin_transform(); + register_builtin_primitives(); + register_builtin_surface(); + register_builtin_control(); + register_builtin_render(); + register_builtin_import(); + register_builtin_dxf_linear_extrude(); + register_builtin_dxf_rotate_extrude(); +} + +void destroy_builtin_modules() +{ + foreach (AbstractModule *v, builtin_modules) + delete v; + builtin_modules.clear(); +} + +int AbstractNode::idx_counter; + +AbstractNode::AbstractNode(const ModuleInstantiation *mi) +{ + modinst = mi; + idx = idx_counter++; +} + +AbstractNode::~AbstractNode() +{ + foreach (AbstractNode *v, children) + delete v; +} + +QString AbstractNode::mk_cache_id() const +{ + QString cache_id = dump(""); + cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:")); + cache_id.remove(' '); + cache_id.remove('\t'); + cache_id.remove('\n'); + return cache_id; +} + +#ifdef ENABLE_CGAL + +AbstractNode::cgal_nef_cache_entry::cgal_nef_cache_entry(CGAL_Nef_polyhedron N) : + N(N), msg(print_messages_stack.last()) { }; + +QCache AbstractNode::cgal_nef_cache(100000); + +static CGAL_Nef_polyhedron render_cgal_nef_polyhedron_backend(const AbstractNode *that, bool intersect) +{ + QString cache_id = that->mk_cache_id(); + if (that->cgal_nef_cache.contains(cache_id)) { + that->progress_report(); + PRINT(that->cgal_nef_cache[cache_id]->msg); + return that->cgal_nef_cache[cache_id]->N; + } + + print_messages_push(); + + bool first = true; + CGAL_Nef_polyhedron N; + foreach (AbstractNode *v, that->children) { + if (v->modinst->tag_background) + continue; + if (first) { + N = v->render_cgal_nef_polyhedron(); + if (N.dim != 0) + first = false; + } else if (N.dim == 2) { + if (intersect) + N.p2 *= v->render_cgal_nef_polyhedron().p2; + else + N.p2 += v->render_cgal_nef_polyhedron().p2; + } else { + if (intersect) + N.p3 *= v->render_cgal_nef_polyhedron().p3; + else + N.p3 += v->render_cgal_nef_polyhedron().p3; + } + } + + that->cgal_nef_cache.insert(cache_id, new AbstractNode::cgal_nef_cache_entry(N), N.weight()); + that->progress_report(); + print_messages_pop(); + + return N; +} + +CGAL_Nef_polyhedron AbstractNode::render_cgal_nef_polyhedron() const +{ + return render_cgal_nef_polyhedron_backend(this, false); +} + +CGAL_Nef_polyhedron AbstractIntersectionNode::render_cgal_nef_polyhedron() const +{ + return render_cgal_nef_polyhedron_backend(this, true); +} + +#endif /* ENABLE_CGAL */ + +static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector *highlights, QVector *background) +{ + CSGTerm *t1 = NULL; + foreach(AbstractNode *v, that->children) { + CSGTerm *t2 = v->render_csg_term(m, highlights, background); + if (t2 && !t1) { + t1 = t2; + } else if (t2 && t1) { + if (intersect) + t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); + else + t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); + } + } + if (t1 && that->modinst->tag_highlight && highlights) + highlights->append(t1->link()); + if (t1 && that->modinst->tag_background && background) { + background->append(t1); + return NULL; + } + return t1; +} + +CSGTerm *AbstractNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + return render_csg_term_backend(this, false, m, highlights, background); +} + +CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + return render_csg_term_backend(this, true, m, highlights, background); +} + +QString AbstractNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text = indent + QString("n%1: group() {\n").arg(idx); + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + } + return dump_cache; +} + +QString AbstractIntersectionNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text = indent + QString("n%1: intersection() {\n").arg(idx); + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + } + return dump_cache; +} + +int progress_report_count; +void (*progress_report_f)(const class AbstractNode*, void*, int); +void *progress_report_vp; + +void AbstractNode::progress_prepare() +{ + foreach (AbstractNode *v, children) + v->progress_prepare(); + progress_mark = ++progress_report_count; +} + +void AbstractNode::progress_report() const +{ + if (progress_report_f) + progress_report_f(this, progress_report_vp, progress_mark); +} + +void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp) +{ + progress_report_count = 0; + progress_report_f = f; + progress_report_vp = vp; + root->progress_prepare(); +} + +void progress_report_fin() +{ + progress_report_count = 0; + progress_report_f = NULL; + progress_report_vp = NULL; +} + diff --git a/src/nef2dxf.cc b/src/nef2dxf.cc new file mode 100644 index 0000000..9c69e84 --- /dev/null +++ b/src/nef2dxf.cc @@ -0,0 +1,67 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" + +DxfData::DxfData(const struct CGAL_Nef_polyhedron &N) +{ + Grid2d grid(GRID_COARSE); + + typedef CGAL_Nef_polyhedron2::Explorer Explorer; + typedef Explorer::Face_const_iterator fci_t; + typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; + Explorer E = N.p2.explorer(); + + for (fci_t fit = E.faces_begin(), fend = E.faces_end(); fit != fend; ++fit) + { + heafcc_t fcirc(E.halfedge(fit)), fend(fcirc); + int first_point = -1, last_point = -1; + CGAL_For_all(fcirc, fend) { + if (E.is_standard(E.target(fcirc))) { + Explorer::Point ep = E.point(E.target(fcirc)); + double x = to_double(ep.x()), y = to_double(ep.y()); + int this_point = -1; + if (grid.has(x, y)) { + this_point = grid.align(x, y); + } else { + this_point = grid.align(x, y) = points.size(); + points.append(Point(x, y)); + } + if (first_point < 0) { + paths.append(Path()); + first_point = this_point; + } + if (this_point != last_point) { + paths.last().points.append(&points[this_point]); + last_point = this_point; + } + } + } + if (first_point >= 0) { + paths.last().is_closed = 1; + paths.last().points.append(&points[first_point]); + } + } + + fixup_path_direction(); +} + diff --git a/src/openscad.cc b/src/openscad.cc new file mode 100644 index 0000000..8c521b6 --- /dev/null +++ b/src/openscad.cc @@ -0,0 +1,257 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "MainWindow.h" + +#include +#include +#include +#include +#include +#include +#ifdef Q_WS_MAC +#include "EventFilter.h" +#endif + +static void help(const char *progname) +{ + fprintf(stderr, "Usage: %s [ { -s stl_file | -o off_file | -x dxf_file } [ -d deps_file ] ]\\\n" + "%*s[ -m make_command ] [ -D var=val [..] ] filename\n", + progname, int(strlen(progname))+8, ""); + exit(1); +} + +QString commandline_commands; +const char *make_command = NULL; +QSet dependencies; + +void handle_dep(QString filename) +{ + if (filename.startsWith("/")) + dependencies.insert(filename); + else + dependencies.insert(QDir::currentPath() + QString("/") + filename); + if (!QFile(filename).exists() && make_command) { + char buffer[4096]; + snprintf(buffer, 4096, "%s '%s'", make_command, filename.replace("'", "'\\''").toUtf8().data()); + system(buffer); + } +} + +int main(int argc, char **argv) +{ + int rc = 0; + + initialize_builtin_functions(); + initialize_builtin_modules(); + +#ifdef Q_WS_X11 + // see : + // On X11, the window system is initialized if GUIenabled is true. If GUIenabled + // is false, the application does not connect to the X server. On Windows and + // Macintosh, currently the window system is always initialized, regardless of the + // value of GUIenabled. This may change in future versions of Qt. + bool useGUI = getenv("DISPLAY") != 0; +#else + bool useGUI = true; +#endif + QApplication app(argc, argv, useGUI); +#ifdef Q_WS_MAC + app.setLibraryPaths(QStringList(app.applicationDirPath() + "/../PlugIns")); + app.installEventFilter(new EventFilter(&app)); +#endif + + // set up groups for QSettings + QCoreApplication::setOrganizationName("OpenSCAD"); + QCoreApplication::setOrganizationDomain("openscad.org"); + QCoreApplication::setApplicationName("OpenSCAD"); + + + const char *filename = NULL; + const char *stl_output_file = NULL; + const char *off_output_file = NULL; + const char *dxf_output_file = NULL; + const char *deps_output_file = NULL; + + int opt; + + while ((opt = getopt(argc, argv, "s:o:x:d:m:D:")) != -1) + { + switch (opt) + { + case 's': + if (stl_output_file || off_output_file || dxf_output_file) + help(argv[0]); + stl_output_file = optarg; + break; + case 'o': + if (stl_output_file || off_output_file || dxf_output_file) + help(argv[0]); + off_output_file = optarg; + break; + case 'x': + if (stl_output_file || off_output_file || dxf_output_file) + help(argv[0]); + dxf_output_file = optarg; + break; + case 'd': + if (deps_output_file) + help(argv[0]); + deps_output_file = optarg; + break; + case 'm': + if (make_command) + help(argv[0]); + make_command = optarg; + break; + case 'D': + commandline_commands += QString(optarg) + QString(";\n"); + break; + default: + help(argv[0]); + } + } + + if (optind < argc) + filename = argv[optind++]; + +#ifndef ENABLE_MDI + if (optind != argc) + help(argv[0]); +#endif + + if (stl_output_file || off_output_file || dxf_output_file) + { + if (!filename) + help(argv[0]); + +#ifdef ENABLE_CGAL + Context root_ctx; + root_ctx.functions_p = &builtin_functions; + root_ctx.modules_p = &builtin_modules; + root_ctx.set_variable("$fn", Value(0.0)); + root_ctx.set_variable("$fs", Value(1.0)); + root_ctx.set_variable("$fa", Value(12.0)); + root_ctx.set_variable("$t", Value(0.0)); + + Value zero3; + zero3.type = Value::VECTOR; + zero3.vec.append(new Value(0.0)); + zero3.vec.append(new Value(0.0)); + zero3.vec.append(new Value(0.0)); + root_ctx.set_variable("$vpt", zero3); + root_ctx.set_variable("$vpr", zero3); + + AbstractModule *root_module; + ModuleInstantiation root_inst; + AbstractNode *root_node; + + handle_dep(filename); + FILE *fp = fopen(filename, "rt"); + if (!fp) { + fprintf(stderr, "Can't open input file `%s'!\n", filename); + exit(1); + } else { + QString text; + char buffer[513]; + int ret; + while ((ret = fread(buffer, 1, 512, fp)) > 0) { + buffer[ret] = 0; + text += buffer; + } + fclose(fp); + root_module = parse((text+commandline_commands).toAscii().data(), false); + } + + QString original_path = QDir::currentPath(); + QFileInfo fileInfo(filename); + QDir::setCurrent(fileInfo.dir().absolutePath()); + + AbstractNode::idx_counter = 1; + root_node = root_module->evaluate(&root_ctx, &root_inst); + + CGAL_Nef_polyhedron *root_N; + root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); + + QDir::setCurrent(original_path); + + if (deps_output_file) { + fp = fopen(deps_output_file, "wt"); + if (!fp) { + fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", deps_output_file); + exit(1); + } + fprintf(fp, "%s:", stl_output_file ? stl_output_file : off_output_file); + QSetIterator i(dependencies); + while (i.hasNext()) + fprintf(fp, " \\\n\t%s", i.next().toUtf8().data()); + fprintf(fp, "\n"); + fclose(fp); + } + + if (stl_output_file) + export_stl(root_N, stl_output_file, NULL); + + if (off_output_file) + export_off(root_N, off_output_file, NULL); + + if (dxf_output_file) + export_dxf(root_N, dxf_output_file, NULL); + + delete root_node; + delete root_N; +#else + fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); + exit(1); +#endif + } + else if (useGUI) + { + // turn on anti-aliasing + QGLFormat f; + f.setSampleBuffers(true); + f.setSamples(4); + QGLFormat::setDefaultFormat(f); +#ifdef ENABLE_MDI + new MainWindow(filename); + while (optind < argc) + new MainWindow(argv[optind++]); + app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); +#else + MainWindow *m = new MainWindow(filename); + app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit())); +#endif + rc = app.exec(); + } + else + { + fprintf(stderr, "Requested GUI mode but can't open display!\n"); + exit(1); + } + + destroy_builtin_functions(); + destroy_builtin_modules(); + + return rc; +} + diff --git a/src/openscad.h b/src/openscad.h new file mode 100644 index 0000000..03ea1cb --- /dev/null +++ b/src/openscad.h @@ -0,0 +1,738 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef OPENSCAD_H +#define OPENSCAD_H + +#ifdef ENABLE_OPENCSG +// this must be included before the GL headers +# include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +// for win32 and maybe others.. +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +class Value; +class Expression; + +class AbstractFunction; +class BuiltinFunction; +class Function; + +class AbstractModule; +class ModuleInstantiation; +class Module; + +class Context; +class PolySet; +class CSGTerm; +class CSGChain; +class AbstractNode; +class AbstractIntersectionNode; +class AbstractPolyNode; +struct CGAL_Nef_polyhedron; + +const double GRID_COARSE = 0.001; +const double GRID_FINE = 0.000001; + +template +class Grid2d +{ +public: + double res; + QHash, T> db; + + Grid2d(double resolution) { + res = resolution; + } + /*! + Aligns x,y to the grid or to existing point if one close enough exists. + Returns the value stored if a point already existing or an uninitialized new value + if not. + */ + T &align(double &x, double &y) { + int64_t ix = (int64_t)round(x / res); + int64_t iy = (int64_t)round(y / res); + if (!db.contains(QPair(ix, iy))) { + int dist = 10; + for (int64_t jx = ix - 1; jx <= ix + 1; jx++) { + for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { + if (!db.contains(QPair(jx, jy))) + continue; + if (abs(ix-jx) + abs(iy-jy) < dist) { + dist = abs(ix-jx) + abs(iy-jy); + ix = jx; + iy = jy; + } + } + } + } + x = ix * res, y = iy * res; + return db[QPair(ix, iy)]; + } + bool has(double x, double y) const { + int64_t ix = (int64_t)round(x / res); + int64_t iy = (int64_t)round(y / res); + if (db.contains(QPair(ix, iy))) + return true; + for (int64_t jx = ix - 1; jx <= ix + 1; jx++) + for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { + if (db.contains(QPair(jx, jy))) + return true; + } + return false; + } + bool eq(double x1, double y1, double x2, double y2) { + align(x1, y1); + align(x2, y2); + if (fabs(x1 - x2) < res && fabs(y1 - y2) < res) + return true; + return false; + } + T &data(double x, double y) { + return align(x, y); + } + T &operator()(double x, double y) { + return align(x, y); + } +}; + +template +class Grid3d +{ +public: + double res; + QHash,int64_t>, T> db; + + Grid3d(double resolution) { + res = resolution; + } + T &align(double &x, double &y, double &z) { + int64_t ix = (int64_t)round(x / res); + int64_t iy = (int64_t)round(y / res); + int64_t iz = (int64_t)round(z / res); + if (!db.contains(QPair,int64_t>(QPair(ix, iy), iz))) { + int dist = 10; + for (int64_t jx = ix - 1; jx <= ix + 1; jx++) { + for (int64_t jy = iy - 1; jy <= iy + 1; jy++) { + for (int64_t jz = iz - 1; jz <= iz + 1; jz++) { + if (!db.contains(QPair,int64_t>(QPair(jx, jy), jz))) + continue; + if (abs(ix-jx) + abs(iy-jy) + abs(iz-jz) < dist) { + dist = abs(ix-jx) + abs(iy-jy) + abs(iz-jz); + ix = jx; + iy = jy; + iz = jz; + } + } + } + } + } + x = ix * res, y = iy * res, z = iz * res; + return db[QPair,int64_t>(QPair(ix, iy), iz)]; + } + bool has(double x, double y, double z) { + int64_t ix = (int64_t)round(x / res); + int64_t iy = (int64_t)round(y / res); + int64_t iz = (int64_t)round(z / res); + if (db.contains(QPair,int64_t>(QPair(ix, iy), iz))) + return true; + for (int64_t jx = ix - 1; jx <= ix + 1; jx++) + for (int64_t jy = iy - 1; jy <= iy + 1; jy++) + for (int64_t jz = iz - 1; jz <= iz + 1; jz++) { + if (db.contains(QPair,int64_t>(QPair(jx, jy), jz))) + return true; + } + return false; + + } + bool eq(double x1, double y1, double z1, double x2, double y2, double z2) { + align(x1, y1, z1); + align(x2, y2, z2); + if (fabs(x1 - x2) < res && fabs(y1 - y2) < res && fabs(z1 - z2) < res) + return true; + return false; + } + T &data(double x, double y, double z) { + return align(x, y, z); + } + T &operator()(double x, double y, double z) { + return align(x, y, z); + } +}; + +class Value +{ +public: + enum type_e { + UNDEFINED, + BOOL, + NUMBER, + RANGE, + VECTOR, + STRING + }; + + enum type_e type; + + bool b; + double num; + QVector vec; + double range_begin; + double range_step; + double range_end; + QString text; + + Value(); + ~Value(); + + Value(bool v); + Value(double v); + Value(const QString &t); + + Value(const Value &v); + Value& operator = (const Value &v); + + Value operator ! () const; + Value operator && (const Value &v) const; + Value operator || (const Value &v) const; + + Value operator + (const Value &v) const; + Value operator - (const Value &v) const; + Value operator * (const Value &v) const; + Value operator / (const Value &v) const; + Value operator % (const Value &v) const; + + Value operator < (const Value &v) const; + Value operator <= (const Value &v) const; + Value operator == (const Value &v) const; + Value operator != (const Value &v) const; + Value operator >= (const Value &v) const; + Value operator > (const Value &v) const; + + Value inv() const; + + bool getnum(double &v) const; + bool getv2(double &x, double &y) const; + bool getv3(double &x, double &y, double &z) const; + + QString dump() const; + +private: + void reset_undef(); +}; + +class Expression +{ +public: + QVector children; + + Value *const_value; + QString var_name; + + QString call_funcname; + QVector call_argnames; + + // Boolean: ! && || + // Operators: * / % + - + // Relations: < <= == != >= > + // Vector element: [] + // Condition operator: ?: + // Invert (prefix '-'): I + // Constant value: C + // Create Range: R + // Create Vector: V + // Create Matrix: M + // Lookup Variable: L + // Lookup member per name: N + // Function call: F + QString type; + + Expression(); + ~Expression(); + + Value evaluate(const Context *context) const; + QString dump() const; +}; + +class AbstractFunction +{ +public: + virtual ~AbstractFunction(); + virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; + virtual QString dump(QString indent, QString name) const; +}; + +class BuiltinFunction : public AbstractFunction +{ +public: + typedef Value (*eval_func_t)(const QVector &argnames, const QVector &args); + eval_func_t eval_func; + + BuiltinFunction(eval_func_t f) : eval_func(f) { } + virtual ~BuiltinFunction(); + + virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; + virtual QString dump(QString indent, QString name) const; +}; + +class Function : public AbstractFunction +{ +public: + QVector argnames; + QVector argexpr; + + Expression *expr; + + Function() { } + virtual ~Function(); + + virtual Value evaluate(const Context *ctx, const QVector &call_argnames, const QVector &call_argvalues) const; + virtual QString dump(QString indent, QString name) const; +}; + +extern QHash builtin_functions; +extern void initialize_builtin_functions(); +extern void initialize_builtin_dxf_dim(); +extern void destroy_builtin_functions(); + +class AbstractModule +{ +public: + virtual ~AbstractModule(); + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; + virtual QString dump(QString indent, QString name) const; +}; + +class ModuleInstantiation +{ +public: + QString label; + QString modname; + QVector argnames; + QVector argexpr; + QVector argvalues; + QVector children; + + bool tag_root; + bool tag_highlight; + bool tag_background; + const Context *ctx; + + ModuleInstantiation() : tag_root(false), tag_highlight(false), tag_background(false), ctx(NULL) { } + ~ModuleInstantiation(); + + QString dump(QString indent) const; + AbstractNode *evaluate(const Context *ctx) const; +}; + +class Module : public AbstractModule +{ +public: + QVector argnames; + QVector argexpr; + + QVector assignments_var; + QVector assignments_expr; + + QHash functions; + QHash modules; + + QVector children; + + Module() { } + virtual ~Module(); + + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; + virtual QString dump(QString indent, QString name) const; +}; + +extern QHash builtin_modules; +extern void initialize_builtin_modules(); +extern void destroy_builtin_modules(); + +extern void register_builtin_csgops(); +extern void register_builtin_transform(); +extern void register_builtin_primitives(); +extern void register_builtin_surface(); +extern void register_builtin_control(); +extern void register_builtin_render(); +extern void register_builtin_import(); +extern void register_builtin_dxf_linear_extrude(); +extern void register_builtin_dxf_rotate_extrude(); + +class Context +{ +public: + const Context *parent; + QHash variables; + QHash config_variables; + const QHash *functions_p; + const QHash *modules_p; + const ModuleInstantiation *inst_p; + + static QVector ctx_stack; + + Context(const Context *parent = NULL); + ~Context(); + + void args(const QVector &argnames, const QVector &argexpr, const QVector &call_argnames, const QVector &call_argvalues); + + void set_variable(QString name, Value value); + Value lookup_variable(QString name, bool silent = false) const; + + Value evaluate_function(QString name, const QVector &argnames, const QVector &argvalues) const; + AbstractNode *evaluate_module(const ModuleInstantiation *inst) const; +}; + +class DxfData +{ +public: + struct Point { + double x, y; + Point() : x(0), y(0) { } + Point(double x, double y) : x(x), y(y) { } + }; + struct Path { + QList points; + bool is_closed, is_inner; + Path() : is_closed(false), is_inner(false) { } + }; + struct Dim { + unsigned int type; + double coords[7][2]; + double angle; + double length; + QString name; + Dim() { + for (int i = 0; i < 7; i++) + for (int j = 0; j < 2; j++) + coords[i][j] = 0; + type = 0; + angle = 0; + length = 0; + } + }; + + QList points; + QList paths; + QList dims; + + DxfData(); + DxfData(double fn, double fs, double fa, QString filename, QString layername = QString(), double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0); + DxfData(const struct CGAL_Nef_polyhedron &N); + + Point *addPoint(double x, double y); + +private: + void fixup_path_direction(); +}; + +// The CGAL template magic slows down the compilation process by a factor of 5. +// So we only include the declaration of AbstractNode where it is needed... +#ifdef INCLUDE_ABSTRACT_NODE_DETAILS + +#ifdef ENABLE_CGAL + +#include +#include +#include +#include +#include +#include +#include + +typedef CGAL::Extended_cartesian CGAL_Kernel2; +typedef CGAL::Nef_polyhedron_2 CGAL_Nef_polyhedron2; +typedef CGAL_Kernel2::Aff_transformation_2 CGAL_Aff_transformation2; + +typedef CGAL::Cartesian CGAL_Kernel3; +typedef CGAL::Polyhedron_3 CGAL_Polyhedron; +typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS; +typedef CGAL::Polyhedron_incremental_builder_3 CGAL_Polybuilder; +typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3; +typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; +typedef CGAL_Nef_polyhedron3::Vector_3 CGAL_Vector; +typedef CGAL_Nef_polyhedron3::Plane_3 CGAL_Plane; +typedef CGAL_Nef_polyhedron3::Point_3 CGAL_Point; + +struct CGAL_Nef_polyhedron +{ + int dim; + CGAL_Nef_polyhedron2 p2; + CGAL_Nef_polyhedron3 p3; + + CGAL_Nef_polyhedron() { + dim = 0; + } + + CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron2 &p) { + dim = 2; + p2 = p; + } + + CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron3 &p) { + dim = 3; + p3 = p; + } + + int weight() { + if (dim == 2) + return p2.explorer().number_of_vertices(); + if (dim == 3) + return p3.number_of_vertices(); + return 0; + } +}; + +#endif /* ENABLE_CGAL */ + +#ifdef ENABLE_OPENCSG +# include +#endif + +class PolySet +{ +public: + struct Point { + double x, y, z; + Point() : x(0), y(0), z(0) { } + Point(double x, double y, double z) : x(x), y(y), z(z) { } + }; + typedef QList Polygon; + QVector polygons; + QVector borders; + Grid3d grid; + + bool is2d; + int convexity; + + PolySet(); + ~PolySet(); + + void append_poly(); + void append_vertex(double x, double y, double z); + void insert_vertex(double x, double y, double z); + + void append_vertex(double x, double y) { + append_vertex(x, y, 0.0); + } + void insert_vertex(double x, double y) { + insert_vertex(x, y, 0.0); + } + + enum colormode_e { + COLORMODE_NONE, + COLORMODE_MATERIAL, + COLORMODE_CUTOUT, + COLORMODE_HIGHLIGHT, + COLORMODE_BACKGROUND + }; + + enum csgmode_e { + CSGMODE_NONE, + CSGMODE_NORMAL = 1, + CSGMODE_DIFFERENCE = 2, + CSGMODE_BACKGROUND = 11, + CSGMODE_BACKGROUND_DIFFERENCE = 12, + CSGMODE_HIGHLIGHT = 21, + CSGMODE_HIGHLIGHT_DIFFERENCE = 22 + }; + + struct ps_cache_entry { + PolySet *ps; + QString msg; + ps_cache_entry(PolySet *ps); + ~ps_cache_entry(); + }; + + static QCache ps_cache; + + void render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo = NULL) const; + void render_edges(colormode_e colormode, csgmode_e csgmode) const; + +#ifdef ENABLE_CGAL + CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + + int refcount; + PolySet *link(); + void unlink(); +}; + +class CSGTerm +{ +public: + enum type_e { + TYPE_PRIMITIVE, + TYPE_UNION, + TYPE_INTERSECTION, + TYPE_DIFFERENCE + }; + + type_e type; + PolySet *polyset; + QString label; + CSGTerm *left; + CSGTerm *right; + double m[20]; + int refcounter; + + CSGTerm(PolySet *polyset, double m[20], QString label); + CSGTerm(type_e type, CSGTerm *left, CSGTerm *right); + + CSGTerm *normalize(); + CSGTerm *normalize_tail(); + + CSGTerm *link(); + void unlink(); + QString dump(); +}; + +class CSGChain +{ +public: + QVector polysets; + QVector matrices; + QVector types; + QVector labels; + + CSGChain(); + + void add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label); + void import(CSGTerm *term, CSGTerm::type_e type = CSGTerm::TYPE_UNION); + QString dump(); +}; + +class AbstractNode +{ +public: + QVector children; + const ModuleInstantiation *modinst; + + int progress_mark; + void progress_prepare(); + void progress_report() const; + + int idx; + static int idx_counter; + QString dump_cache; + + AbstractNode(const ModuleInstantiation *mi); + virtual ~AbstractNode(); + virtual QString mk_cache_id() const; +#ifdef ENABLE_CGAL + struct cgal_nef_cache_entry { + CGAL_Nef_polyhedron N; + QString msg; + cgal_nef_cache_entry(CGAL_Nef_polyhedron N); + }; + static QCache cgal_nef_cache; + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + virtual QString dump(QString indent) const; +}; + +class AbstractIntersectionNode : public AbstractNode +{ +public: + AbstractIntersectionNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; +#ifdef ENABLE_CGAL + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + virtual QString dump(QString indent) const; +}; + +class AbstractPolyNode : public AbstractNode +{ +public: + enum render_mode_e { + RENDER_CGAL, + RENDER_OPENCSG + }; + AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; + virtual PolySet *render_polyset(render_mode_e mode) const; +#ifdef ENABLE_CGAL + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + static CSGTerm *render_csg_term_from_ps(double m[20], QVector *highlights, QVector *background, PolySet *ps, const ModuleInstantiation *modinst, int idx); +}; + +extern QHash dxf_dim_cache; +extern QHash dxf_cross_cache; + +extern int progress_report_count; +extern void (*progress_report_f)(const class AbstractNode*, void*, int); +extern void *progress_report_vp; + +void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp); +void progress_report_fin(); + +void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h); +void dxf_border_to_ps(PolySet *ps, DxfData *dxf); + +#endif /* INCLUDE_ABSTRACT_NODE_DETAILS */ + +class Highlighter : public QSyntaxHighlighter +{ +public: + Highlighter(QTextDocument *parent); + void highlightBlock(const QString &text); +}; + +extern AbstractModule *parse(const char *text, int debug); +extern int get_fragments_from_r(double r, double fn, double fs, double fa); + +extern QString commandline_commands; +extern int parser_error_pos; + +#ifdef ENABLE_CGAL +void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); +void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); +void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); +#endif +extern void handle_dep(QString filename); + +#endif + diff --git a/src/polyset.cc b/src/polyset.cc new file mode 100644 index 0000000..5f22fde --- /dev/null +++ b/src/polyset.cc @@ -0,0 +1,706 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" +#include "Preferences.h" + +QCache PolySet::ps_cache(100); + +PolySet::ps_cache_entry::ps_cache_entry(PolySet *ps) : + ps(ps), msg(print_messages_stack.last()) { } + +PolySet::ps_cache_entry::~ps_cache_entry() { + ps->unlink(); +} + +PolySet::PolySet() : grid(GRID_FINE) +{ + is2d = false; + convexity = 1; + refcount = 1; +} + +PolySet::~PolySet() +{ + assert(refcount == 0); +} + +PolySet* PolySet::link() +{ + refcount++; + return this; +} + +void PolySet::unlink() +{ + if (--refcount == 0) + delete this; +} + +void PolySet::append_poly() +{ + polygons.append(Polygon()); +} + +void PolySet::append_vertex(double x, double y, double z) +{ + grid.align(x, y, z); + polygons.last().append(Point(x, y, z)); +} + +void PolySet::insert_vertex(double x, double y, double z) +{ + grid.align(x, y, z); + polygons.last().insert(0, Point(x, y, z)); +} + +static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const PolySet::Point *p1, const PolySet::Point *p2, bool e0, bool e1, bool e2, double z, bool mirrored) +{ + double ax = p1->x - p0->x, bx = p1->x - p2->x; + double ay = p1->y - p0->y, by = p1->y - p2->y; + double az = p1->z - p0->z, bz = p1->z - p2->z; + double nx = ay*bz - az*by; + double ny = az*bx - ax*bz; + double nz = ax*by - ay*bx; + double nl = sqrt(nx*nx + ny*ny + nz*nz); + glNormal3d(nx / nl, ny / nl, nz / nl); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + double e0f = e0 ? 2.0 : -1.0; + double e1f = e1 ? 2.0 : -1.0; + double e2f = e2 ? 2.0 : -1.0; + glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); + glVertexAttrib3d(shaderinfo[4], p1->x, p1->y, p1->z + z); + glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[6], 0.0, 1.0, 0.0); + glVertex3d(p0->x, p0->y, p0->z + z); + if (!mirrored) { + glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); + glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); + glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); + glVertex3d(p1->x, p1->y, p1->z + z); + } + glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); + glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); + glVertexAttrib3d(shaderinfo[5], p1->x, p1->y, p1->z + z); + glVertexAttrib3d(shaderinfo[6], 1.0, 0.0, 0.0); + glVertex3d(p2->x, p2->y, p2->z + z); + if (mirrored) { + glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); + glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); + glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); + glVertex3d(p1->x, p1->y, p1->z + z); + } + } + else +#endif + { + glVertex3d(p0->x, p0->y, p0->z + z); + if (!mirrored) + glVertex3d(p1->x, p1->y, p1->z + z); + glVertex3d(p2->x, p2->y, p2->z + z); + if (mirrored) + glVertex3d(p1->x, p1->y, p1->z + z); + } +} + +void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo) const +{ + double m_scale_rotate_det = + m[0]*m[5]*m[10] + m[4]*m[9]*m[2] + m[8]*m[1]*m[6] - + (m[8]*m[5]*m[2] + m[4]*m[1]*m[10] + m[0]*m[9]*m[6]); + bool mirrored = m_scale_rotate_det < 0; + + if (colormode == COLORMODE_MATERIAL) { + const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); + glColor3f(col.redF(), col.greenF(), col.blueF()); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform4f(shaderinfo[1], col.redF(), col.greenF(), col.blueF(), 1.0f); + glUniform4f(shaderinfo[2], 255 / 255.0, 236 / 255.0, 94 / 255.0, 1.0); + } +#endif /* ENABLE_OPENCSG */ + } + if (colormode == COLORMODE_CUTOUT) { + const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR); + glColor3f(col.redF(), col.greenF(), col.blueF()); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform4f(shaderinfo[1], 157 / 255.0, 203 / 255.0, 81 / 255.0, 1.0); + glUniform4f(shaderinfo[2], 171 / 255.0, 216 / 255.0, 86 / 255.0, 1.0); + } +#endif /* ENABLE_OPENCSG */ + } + if (colormode == COLORMODE_HIGHLIGHT) { + glColor4ub(255, 157, 81, 128); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform4f(shaderinfo[1], 255 / 255.0, 157 / 255.0, 81 / 255.0, 0.5); + glUniform4f(shaderinfo[2], 255 / 255.0, 171 / 255.0, 86 / 255.0, 0.5); + } +#endif /* ENABLE_OPENCSG */ + } + if (colormode == COLORMODE_BACKGROUND) { + glColor4ub(180, 180, 180, 128); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform4f(shaderinfo[1], 180 / 255.0, 180 / 255.0, 180 / 255.0, 0.5); + glUniform4f(shaderinfo[2], 150 / 255.0, 150 / 255.0, 150 / 255.0, 0.5); + } +#endif /* ENABLE_OPENCSG */ + } +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform1f(shaderinfo[7], shaderinfo[9]); + glUniform1f(shaderinfo[8], shaderinfo[10]); + } +#endif /* ENABLE_OPENCSG */ + if (this->is2d) { + double zbase = csgmode; + glBegin(GL_TRIANGLES); + for (double z = -zbase/2; z < zbase; z += zbase) + { + for (int i = 0; i < polygons.size(); i++) { + const Polygon *poly = &polygons[i]; + if (poly->size() == 3) { + if (z < 0) { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(2), &poly->at(1), true, true, true, z, mirrored); + } else { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, z, mirrored); + } + } + else if (poly->size() == 4) { + if (z < 0) { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); + } else { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); + } + } + else { + Point center; + for (int j = 0; j < poly->size(); j++) { + center.x += poly->at(j).x; + center.y += poly->at(j).y; + } + center.x /= poly->size(); + center.y /= poly->size(); + for (int j = 1; j <= poly->size(); j++) { + if (z < 0) { + gl_draw_triangle(shaderinfo, ¢er, &poly->at(j % poly->size()), &poly->at(j - 1), + false, true, false, z, mirrored); + } else { + gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), + false, true, false, z, mirrored); + } + } + } + } + } + const QVector *borders_p = &borders; + if (borders_p->size() == 0) + borders_p = &polygons; + for (int i = 0; i < borders_p->size(); i++) { + const Polygon *poly = &borders_p->at(i); + for (int j = 1; j <= poly->size(); j++) { + Point p1 = poly->at(j - 1), p2 = poly->at(j - 1); + Point p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size()); + p1.z -= zbase/2, p2.z += zbase/2; + p3.z -= zbase/2, p4.z += zbase/2; + gl_draw_triangle(shaderinfo, &p2, &p1, &p3, true, true, false, 0, mirrored); + gl_draw_triangle(shaderinfo, &p2, &p3, &p4, false, true, true, 0, mirrored); + } + } + glEnd(); + } else { + for (int i = 0; i < polygons.size(); i++) { + const Polygon *poly = &polygons[i]; + glBegin(GL_TRIANGLES); + if (poly->size() == 3) { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, 0, mirrored); + } + else if (poly->size() == 4) { + gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, 0, mirrored); + gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, 0, mirrored); + } + else { + Point center; + for (int j = 0; j < poly->size(); j++) { + center.x += poly->at(j).x; + center.y += poly->at(j).y; + center.z += poly->at(j).z; + } + center.x /= poly->size(); + center.y /= poly->size(); + center.z /= poly->size(); + for (int j = 1; j <= poly->size(); j++) { + gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), false, true, false, 0, mirrored); + } + } + glEnd(); + } + } +} + +void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const +{ + if (colormode == COLORMODE_MATERIAL) + glColor3ub(255, 236, 94); + if (colormode == COLORMODE_CUTOUT) + glColor3ub(171, 216, 86); + if (colormode == COLORMODE_HIGHLIGHT) + glColor4ub(255, 171, 86, 128); + if (colormode == COLORMODE_BACKGROUND) + glColor4ub(150, 150, 150, 128); + if (this->is2d) { + double zbase = csgmode; + for (double z = -zbase/2; z < zbase; z += zbase) + { + for (int i = 0; i < borders.size(); i++) { + const Polygon *poly = &borders[i]; + glBegin(GL_LINE_LOOP); + for (int j = 0; j < poly->size(); j++) { + const Point *p = &poly->at(j); + glVertex3d(p->x, p->y, z); + } + glEnd(); + } + } + for (int i = 0; i < borders.size(); i++) { + const Polygon *poly = &borders[i]; + glBegin(GL_LINES); + for (int j = 0; j < poly->size(); j++) { + const Point *p = &poly->at(j); + glVertex3d(p->x, p->y, -zbase/2); + glVertex3d(p->x, p->y, +zbase/2); + } + glEnd(); + } + } else { + for (int i = 0; i < polygons.size(); i++) { + const Polygon *poly = &polygons[i]; + glBegin(GL_LINE_LOOP); + for (int j = 0; j < poly->size(); j++) { + const Point *p = &poly->at(j); + glVertex3d(p->x, p->y, p->z); + } + glEnd(); + } + } +} + +#ifdef ENABLE_CGAL + +#undef GEN_SURFACE_DEBUG + +class CGAL_Build_PolySet : public CGAL::Modifier_base +{ +public: + typedef CGAL_HDS::Vertex::Point Point; + + const PolySet *ps; + CGAL_Build_PolySet(const PolySet *ps) : ps(ps) { } + + void operator()(CGAL_HDS& hds) + { + CGAL_Polybuilder B(hds, true); + + QList vertices; + Grid3d vertices_idx(GRID_FINE); + + for (int i = 0; i < ps->polygons.size(); i++) { + const PolySet::Polygon *poly = &ps->polygons[i]; + for (int j = 0; j < poly->size(); j++) { + const PolySet::Point *p = &poly->at(j); + if (!vertices_idx.has(p->x, p->y, p->z)) { + vertices_idx.data(p->x, p->y, p->z) = vertices.size(); + vertices.append(*p); + } + } + } + + B.begin_surface(vertices.size(), ps->polygons.size()); +#ifdef GEN_SURFACE_DEBUG + printf("=== CGAL Surface ===\n"); +#endif + + for (int i = 0; i < vertices.size(); i++) { + const PolySet::Point *p = &vertices[i]; + B.add_vertex(Point(p->x, p->y, p->z)); +#ifdef GEN_SURFACE_DEBUG + printf("%d: %f %f %f\n", i, p->x, p->y, p->z); +#endif + } + + for (int i = 0; i < ps->polygons.size(); i++) { + const PolySet::Polygon *poly = &ps->polygons[i]; + QHash fc; + bool facet_is_degenerated = false; + for (int j = 0; j < poly->size(); j++) { + const PolySet::Point *p = &poly->at(j); + int v = vertices_idx.data(p->x, p->y, p->z); + if (fc[v]++ > 0) + facet_is_degenerated = true; + } + + if (!facet_is_degenerated) + B.begin_facet(); +#ifdef GEN_SURFACE_DEBUG + printf("F:"); +#endif + for (int j = 0; j < poly->size(); j++) { + const PolySet::Point *p = &poly->at(j); +#ifdef GEN_SURFACE_DEBUG + printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z); +#endif + if (!facet_is_degenerated) + B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z)); + } +#ifdef GEN_SURFACE_DEBUG + if (facet_is_degenerated) + printf(" (degenerated)"); + printf("\n"); +#endif + if (!facet_is_degenerated) + B.end_facet(); + } + +#ifdef GEN_SURFACE_DEBUG + printf("====================\n"); +#endif + B.end_surface(); + + #undef PointKey + } +}; + +CGAL_Nef_polyhedron PolySet::render_cgal_nef_polyhedron() const +{ + if (this->is2d) + { +#if 0 + // This version of the code causes problems in some cases. + // Example testcase: import_dxf("testdata/polygon8.dxf"); + // + typedef std::list point_list_t; + typedef point_list_t::iterator point_list_it; + std::list< point_list_t > pdata_point_lists; + std::list < std::pair < point_list_it, point_list_it > > pdata; + Grid2d grid(GRID_COARSE); + + for (int i = 0; i < this->polygons.size(); i++) { + pdata_point_lists.push_back(point_list_t()); + for (int j = 0; j < this->polygons[i].size(); j++) { + double x = this->polygons[i][j].x; + double y = this->polygons[i][j].y; + CGAL_Nef_polyhedron2::Point p; + if (grid.has(x, y)) { + p = grid.data(x, y); + } else { + p = CGAL_Nef_polyhedron2::Point(x, y); + grid.data(x, y) = p; + } + pdata_point_lists.back().push_back(p); + } + pdata.push_back(std::make_pair(pdata_point_lists.back().begin(), + pdata_point_lists.back().end())); + } + + CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS); + return CGAL_Nef_polyhedron(N); +#endif +#if 0 + // This version of the code works fine but is pretty slow. + // + CGAL_Nef_polyhedron2 N; + Grid2d grid(GRID_COARSE); + + for (int i = 0; i < this->polygons.size(); i++) { + std::list plist; + for (int j = 0; j < this->polygons[i].size(); j++) { + double x = this->polygons[i][j].x; + double y = this->polygons[i][j].y; + CGAL_Nef_polyhedron2::Point p; + if (grid.has(x, y)) { + p = grid.data(x, y); + } else { + p = CGAL_Nef_polyhedron2::Point(x, y); + grid.data(x, y) = p; + } + plist.push_back(p); + } + N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); + } + + return CGAL_Nef_polyhedron(N); +#endif +#if 1 + // This version of the code does essentially the same thing as the 2nd + // version but merges some triangles before sending them to CGAL. This adds + // complexity but speeds up things.. + // + struct PolyReducer + { + Grid2d grid; + QHash< QPair, QPair > egde_to_poly; + QHash< int, CGAL_Nef_polyhedron2::Point > points; + QHash< int, QList > polygons; + int poly_n; + + void add_edges(int pn) + { + for (int j = 1; j <= this->polygons[pn].size(); j++) { + int a = this->polygons[pn][j-1]; + int b = this->polygons[pn][j % this->polygons[pn].size()]; + if (a > b) { a = a^b; b = a^b; a = a^b; } + if (this->egde_to_poly[QPair(a, b)].first == 0) + this->egde_to_poly[QPair(a, b)].first = pn; + else if (this->egde_to_poly[QPair(a, b)].second == 0) + this->egde_to_poly[QPair(a, b)].second = pn; + else + abort(); + } + } + + void del_poly(int pn) + { + for (int j = 1; j <= this->polygons[pn].size(); j++) { + int a = this->polygons[pn][j-1]; + int b = this->polygons[pn][j % this->polygons[pn].size()]; + if (a > b) { a = a^b; b = a^b; a = a^b; } + if (this->egde_to_poly[QPair(a, b)].first == pn) + this->egde_to_poly[QPair(a, b)].first = 0; + if (this->egde_to_poly[QPair(a, b)].second == pn) + this->egde_to_poly[QPair(a, b)].second = 0; + } + this->polygons.remove(pn); + } + + PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1) + { + int point_n = 1; + for (int i = 0; i < ps->polygons.size(); i++) { + for (int j = 0; j < ps->polygons[i].size(); j++) { + double x = ps->polygons[i][j].x; + double y = ps->polygons[i][j].y; + if (this->grid.has(x, y)) { + this->polygons[this->poly_n].append(this->grid.data(x, y)); + } else { + this->grid.align(x, y) = point_n; + this->polygons[this->poly_n].append(point_n); + this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y); + point_n++; + } + } + add_edges(this->poly_n); + this->poly_n++; + } + } + + int merge(int p1, int p1e, int p2, int p2e) + { + for (int i = 1; i < this->polygons[p1].size(); i++) { + int j = (p1e + i) % this->polygons[p1].size(); + this->polygons[this->poly_n].append(this->polygons[p1][j]); + } + for (int i = 1; i < this->polygons[p2].size(); i++) { + int j = (p2e + i) % this->polygons[p2].size(); + this->polygons[this->poly_n].append(this->polygons[p2][j]); + } + del_poly(p1); + del_poly(p2); + add_edges(this->poly_n); + return this->poly_n++; + } + + void reduce() + { + QList work_queue; + QHashIterator< int, QList > it(polygons); + while (it.hasNext()) { + it.next(); + work_queue.append(it.key()); + } + while (!work_queue.isEmpty()) { + int poly1_n = work_queue.first(); + work_queue.removeFirst(); + if (!this->polygons.contains(poly1_n)) + continue; + for (int j = 1; j <= this->polygons[poly1_n].size(); j++) { + int a = this->polygons[poly1_n][j-1]; + int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()]; + if (a > b) { a = a^b; b = a^b; a = a^b; } + if (this->egde_to_poly[QPair(a, b)].first != 0 && + this->egde_to_poly[QPair(a, b)].second != 0) { + int poly2_n = this->egde_to_poly[QPair(a, b)].first + + this->egde_to_poly[QPair(a, b)].second - poly1_n; + int poly2_edge = -1; + for (int k = 1; k <= this->polygons[poly2_n].size(); k++) { + int c = this->polygons[poly2_n][k-1]; + int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()]; + if (c > d) { c = c^d; d = c^d; c = c^d; } + if (a == c && b == d) { + poly2_edge = k-1; + continue; + } + int poly3_n = this->egde_to_poly[QPair(c, d)].first + + this->egde_to_poly[QPair(c, d)].second - poly2_n; + if (poly3_n < 0) + continue; + if (poly3_n == poly1_n) + goto next_poly1_edge; + } + work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge)); + goto next_poly1; + } + next_poly1_edge:; + } + next_poly1:; + } + } + + CGAL_Nef_polyhedron2 toNef() + { + CGAL_Nef_polyhedron2 N; + + QHashIterator< int, QList > it(polygons); + while (it.hasNext()) { + it.next(); + std::list plist; + for (int j = 0; j < it.value().size(); j++) { + int p = it.value()[j]; + plist.push_back(points[p]); + } + N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); + } + + return N; + } + }; + + PolyReducer pr(this); + // printf("Number of polygons before reduction: %d\n", pr.polygons.size()); + pr.reduce(); + // printf("Number of polygons after reduction: %d\n", pr.polygons.size()); + return CGAL_Nef_polyhedron(pr.toNef()); +#endif +#if 0 + // This is another experimental version. I should run faster than the above, + // is a lot simpler and has only one known weakness: Degenerate polygons, which + // get repaired by GLUTess, might trigger a CGAL crash here. The only + // known case for this is triangle-with-duplicate-vertex.dxf + if (this->polygons.size() > 0) assert(this->borders.size() > 0); + CGAL_Nef_polyhedron2 N; + Grid2d grid(GRID_COARSE); + + for (int i = 0; i < this->borders.size(); i++) { + std::list plist; + for (int j = 0; j < this->borders[i].size(); j++) { + double x = this->borders[i][j].x; + double y = this->borders[i][j].y; + CGAL_Nef_polyhedron2::Point p; + if (grid.has(x, y)) { + p = grid.data(x, y); + } else { + p = CGAL_Nef_polyhedron2::Point(x, y); + grid.data(x, y) = p; + } + plist.push_back(p); + } + // FIXME: If a border (path) has a duplicate vertex in dxf, + // the CGAL_Nef_polyhedron2 constructor will crash. + N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); + } + + return CGAL_Nef_polyhedron(N); + +#endif + } + else + { + CGAL_Polyhedron P; + CGAL_Build_PolySet builder(this); + P.delegate(builder); +#if 0 + std::cout << P; +#endif + CGAL_Nef_polyhedron3 N(P); + return CGAL_Nef_polyhedron(N); + } + return CGAL_Nef_polyhedron(); +} + +#endif /* ENABLE_CGAL */ + +PolySet *AbstractPolyNode::render_polyset(render_mode_e) const +{ + return NULL; +} + +#ifdef ENABLE_CGAL + +CGAL_Nef_polyhedron AbstractPolyNode::render_cgal_nef_polyhedron() const +{ + QString cache_id = mk_cache_id(); + if (cgal_nef_cache.contains(cache_id)) { + progress_report(); + PRINT(cgal_nef_cache[cache_id]->msg); + return cgal_nef_cache[cache_id]->N; + } + + print_messages_push(); + + PolySet *ps = render_polyset(RENDER_CGAL); + CGAL_Nef_polyhedron N = ps->render_cgal_nef_polyhedron(); + + cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); + print_messages_pop(); + progress_report(); + + ps->unlink(); + return N; +} + +#endif /* ENABLE_CGAL */ + +CSGTerm *AbstractPolyNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + PolySet *ps = render_polyset(RENDER_OPENCSG); + return render_csg_term_from_ps(m, highlights, background, ps, modinst, idx); +} + +CSGTerm *AbstractPolyNode::render_csg_term_from_ps(double m[20], QVector *highlights, QVector *background, PolySet *ps, const ModuleInstantiation *modinst, int idx) +{ + CSGTerm *t = new CSGTerm(ps, m, QString("n%1").arg(idx)); + if (modinst->tag_highlight && highlights) + highlights->append(t->link()); + if (modinst->tag_background && background) { + background->append(t); + return NULL; + } + return t; +} + diff --git a/src/primitives.cc b/src/primitives.cc new file mode 100644 index 0000000..2f50578 --- /dev/null +++ b/src/primitives.cc @@ -0,0 +1,529 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" + +enum primitive_type_e { + CUBE, + SPHERE, + CYLINDER, + POLYHEDRON, + SQUARE, + CIRCLE, + POLYGON +}; + +class PrimitiveModule : public AbstractModule +{ +public: + primitive_type_e type; + PrimitiveModule(primitive_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class PrimitiveNode : public AbstractPolyNode +{ +public: + bool center; + double x, y, z, h, r1, r2; + double fn, fs, fa; + primitive_type_e type; + int convexity; + Value points, paths, triangles; + PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + PrimitiveNode *node = new PrimitiveNode(inst, type); + + node->center = false; + node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1; + + QVector argnames; + QVector argexpr; + + if (type == CUBE) { + argnames = QVector() << "size" << "center"; + } + if (type == SPHERE) { + argnames = QVector() << "r"; + } + if (type == CYLINDER) { + argnames = QVector() << "h" << "r1" << "r2" << "center"; + } + if (type == POLYHEDRON) { + argnames = QVector() << "points" << "triangles" << "convexity"; + } + if (type == SQUARE) { + argnames = QVector() << "size" << "center"; + } + if (type == CIRCLE) { + argnames = QVector() << "r"; + } + if (type == POLYGON) { + argnames = QVector() << "points" << "paths" << "convexity"; + } + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + node->fn = c.lookup_variable("$fn").num; + node->fs = c.lookup_variable("$fs").num; + node->fa = c.lookup_variable("$fa").num; + + if (type == CUBE) { + Value size = c.lookup_variable("size"); + Value center = c.lookup_variable("center"); + size.getnum(node->x); + size.getnum(node->y); + size.getnum(node->z); + size.getv3(node->x, node->y, node->z); + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == SPHERE) { + Value r = c.lookup_variable("r"); + if (r.type == Value::NUMBER) { + node->r1 = r.num; + } + } + + if (type == CYLINDER) { + Value h = c.lookup_variable("h"); + Value r, r1, r2; + r1 = c.lookup_variable("r1"); + r2 = c.lookup_variable("r2"); + if (r1.type != Value::NUMBER && r1.type != Value::NUMBER) + r = c.lookup_variable("r"); + Value center = c.lookup_variable("center"); + if (h.type == Value::NUMBER) { + node->h = h.num; + } + if (r.type == Value::NUMBER) { + node->r1 = r.num; + node->r2 = r.num; + } + if (r1.type == Value::NUMBER) { + node->r1 = r1.num; + } + if (r2.type == Value::NUMBER) { + node->r2 = r2.num; + } + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == POLYHEDRON) { + node->points = c.lookup_variable("points"); + node->triangles = c.lookup_variable("triangles"); + } + + if (type == SQUARE) { + Value size = c.lookup_variable("size"); + Value center = c.lookup_variable("center"); + size.getnum(node->x); + size.getnum(node->y); + size.getv2(node->x, node->y); + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == CIRCLE) { + Value r = c.lookup_variable("r"); + if (r.type == Value::NUMBER) { + node->r1 = r.num; + } + } + + if (type == POLYGON) { + node->points = c.lookup_variable("points"); + node->paths = c.lookup_variable("paths"); + } + + node->convexity = c.lookup_variable("convexity", true).num; + if (node->convexity < 1) + node->convexity = 1; + + return node; +} + +void register_builtin_primitives() +{ + builtin_modules["cube"] = new PrimitiveModule(CUBE); + builtin_modules["sphere"] = new PrimitiveModule(SPHERE); + builtin_modules["cylinder"] = new PrimitiveModule(CYLINDER); + builtin_modules["polyhedron"] = new PrimitiveModule(POLYHEDRON); + builtin_modules["square"] = new PrimitiveModule(SQUARE); + builtin_modules["circle"] = new PrimitiveModule(CIRCLE); + builtin_modules["polygon"] = new PrimitiveModule(POLYGON); +} + +/*! + Returns the number of subdivision of a whole circle, given radius and + the three special variables $fn, $fs and $fa +*/ +int get_fragments_from_r(double r, double fn, double fs, double fa) +{ + if (fn > 0.0) + return (int)fn; + return (int)ceil(fmax(fmin(360.0 / fa, r*M_PI / fs), 5)); +} + +PolySet *PrimitiveNode::render_polyset(render_mode_e) const +{ + PolySet *p = new PolySet(); + + if (type == CUBE && x > 0 && y > 0 && z > 0) + { + double x1, x2, y1, y2, z1, z2; + if (center) { + x1 = -x/2; + x2 = +x/2; + y1 = -y/2; + y2 = +y/2; + z1 = -z/2; + z2 = +z/2; + } else { + x1 = y1 = z1 = 0; + x2 = x; + y2 = y; + z2 = z; + } + + p->append_poly(); // top + p->append_vertex(x1, y1, z2); + p->append_vertex(x2, y1, z2); + p->append_vertex(x2, y2, z2); + p->append_vertex(x1, y2, z2); + + p->append_poly(); // bottom + p->append_vertex(x1, y2, z1); + p->append_vertex(x2, y2, z1); + p->append_vertex(x2, y1, z1); + p->append_vertex(x1, y1, z1); + + p->append_poly(); // side1 + p->append_vertex(x1, y1, z1); + p->append_vertex(x2, y1, z1); + p->append_vertex(x2, y1, z2); + p->append_vertex(x1, y1, z2); + + p->append_poly(); // side2 + p->append_vertex(x2, y1, z1); + p->append_vertex(x2, y2, z1); + p->append_vertex(x2, y2, z2); + p->append_vertex(x2, y1, z2); + + p->append_poly(); // side3 + p->append_vertex(x2, y2, z1); + p->append_vertex(x1, y2, z1); + p->append_vertex(x1, y2, z2); + p->append_vertex(x2, y2, z2); + + p->append_poly(); // side4 + p->append_vertex(x1, y2, z1); + p->append_vertex(x1, y1, z1); + p->append_vertex(x1, y1, z2); + p->append_vertex(x1, y2, z2); + } + + if (type == SPHERE && r1 > 0) + { + struct point2d { + double x, y; + }; + + struct ring_s { + int fragments; + point2d *points; + double r, z; + }; + + int rings = get_fragments_from_r(r1, fn, fs, fa); + ring_s ring[rings]; + + for (int i = 0; i < rings; i++) { + double phi = (M_PI * (i + 0.5)) / rings; + ring[i].r = r1 * sin(phi); + ring[i].z = r1 * cos(phi); + ring[i].fragments = get_fragments_from_r(ring[i].r, fn, fs, fa); + ring[i].points = new point2d[ring[i].fragments]; + for (int j = 0; j < ring[i].fragments; j++) { + phi = (M_PI*2*j) / ring[i].fragments; + ring[i].points[j].x = ring[i].r * cos(phi); + ring[i].points[j].y = ring[i].r * sin(phi); + } + } + + p->append_poly(); + for (int i = 0; i < ring[0].fragments; i++) + p->append_vertex(ring[0].points[i].x, ring[0].points[i].y, ring[0].z); + + for (int i = 0; i < rings-1; i++) { + ring_s *r1 = &ring[i]; + ring_s *r2 = &ring[i+1]; + int r1i = 0, r2i = 0; + while (r1i < r1->fragments || r2i < r2->fragments) + { + if (r1i >= r1->fragments) + goto sphere_next_r2; + if (r2i >= r2->fragments) + goto sphere_next_r1; + if ((double)r1i / r1->fragments < + (double)r2i / r2->fragments) + { +sphere_next_r1: + p->append_poly(); + int r1j = (r1i+1) % r1->fragments; + p->insert_vertex(r1->points[r1i].x, r1->points[r1i].y, r1->z); + p->insert_vertex(r1->points[r1j].x, r1->points[r1j].y, r1->z); + p->insert_vertex(r2->points[r2i % r2->fragments].x, r2->points[r2i % r2->fragments].y, r2->z); + r1i++; + } else { +sphere_next_r2: + p->append_poly(); + int r2j = (r2i+1) % r2->fragments; + p->append_vertex(r2->points[r2i].x, r2->points[r2i].y, r2->z); + p->append_vertex(r2->points[r2j].x, r2->points[r2j].y, r2->z); + p->append_vertex(r1->points[r1i % r1->fragments].x, r1->points[r1i % r1->fragments].y, r1->z); + r2i++; + } + } + } + + p->append_poly(); + for (int i = 0; i < ring[rings-1].fragments; i++) + p->insert_vertex(ring[rings-1].points[i].x, ring[rings-1].points[i].y, ring[rings-1].z); + } + + if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0)) + { + int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa); + + double z1, z2; + if (center) { + z1 = -h/2; + z2 = +h/2; + } else { + z1 = 0; + z2 = h; + } + + struct point2d { + double x, y; + }; + + point2d circle1[fragments]; + point2d circle2[fragments]; + + for (int i=0; i 0) { + circle1[i].x = r1*cos(phi); + circle1[i].y = r1*sin(phi); + } else { + circle1[i].x = 0; + circle1[i].y = 0; + } + if (r2 > 0) { + circle2[i].x = r2*cos(phi); + circle2[i].y = r2*sin(phi); + } else { + circle2[i].x = 0; + circle2[i].y = 0; + } + } + + for (int i=0; i 0) { + p->append_poly(); + p->insert_vertex(circle1[i].x, circle1[i].y, z1); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } + if (r2 > 0) { + p->append_poly(); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle2[j].x, circle2[j].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } + } + + if (r1 > 0) { + p->append_poly(); + for (int i=0; iinsert_vertex(circle1[i].x, circle1[i].y, z1); + } + + if (r2 > 0) { + p->append_poly(); + for (int i=0; iappend_vertex(circle2[i].x, circle2[i].y, z2); + } + } + + if (type == POLYHEDRON) + { + p->convexity = convexity; + for (int i=0; iappend_poly(); + for (int j=0; jvec.size(); j++) { + int pt = triangles.vec[i]->vec[j]->num; + if (pt < points.vec.size()) { + double px, py, pz; + if (points.vec[pt]->getv3(px, py, pz)) + p->insert_vertex(px, py, pz); + } + } + } + } + + if (type == SQUARE) + { + double x1, x2, y1, y2; + if (center) { + x1 = -x/2; + x2 = +x/2; + y1 = -y/2; + y2 = +y/2; + } else { + x1 = y1 = 0; + x2 = x; + y2 = y; + } + + p->is2d = true; + p->append_poly(); + p->append_vertex(x1, y1); + p->append_vertex(x2, y1); + p->append_vertex(x2, y2); + p->append_vertex(x1, y2); + } + + if (type == CIRCLE) + { + int fragments = get_fragments_from_r(r1, fn, fs, fa); + + struct point2d { + double x, y; + }; + + point2d circle[fragments]; + + for (int i=0; iis2d = true; + p->append_poly(); + for (int i=0; iappend_vertex(circle[i].x, circle[i].y); + } + + if (type == POLYGON) + { + DxfData dd; + + for (int i=0; ivec[0]->num; + double y = points.vec[i]->vec[1]->num; + dd.points.append(DxfData::Point(x, y)); + } + + if (paths.vec.size() == 0) + { + dd.paths.append(DxfData::Path()); + for (int i=0; i 0) { + dd.paths.last().points.append(dd.paths.last().points.first()); + dd.paths.last().is_closed = true; + } + } + else + { + for (int i=0; ivec.size(); j++) { + int idx = paths.vec[i]->vec[j]->num; + if (idx < dd.points.size()) { + DxfData::Point *p = &dd.points[idx]; + dd.paths.last().points.append(p); + } + } + if (dd.paths.last().points.isEmpty()) { + dd.paths.removeLast(); + } else { + dd.paths.last().points.append(dd.paths.last().points.first()); + dd.paths.last().is_closed = true; + } + } + } + + p->is2d = true; + p->convexity = convexity; + dxf_tesselate(p, &dd, 0, true, false, 0); + dxf_border_to_ps(p, &dd); + } + + return p; +} + +QString PrimitiveNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + if (type == CUBE) + text.sprintf("cube(size = [%g, %g, %g], center = %s);\n", x, y, z, center ? "true" : "false"); + if (type == SPHERE) + text.sprintf("sphere($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); + if (type == CYLINDER) + text.sprintf("cylinder($fn = %g, $fa = %g, $fs = %g, h = %g, r1 = %g, r2 = %g, center = %s);\n", fn, fa, fs, h, r1, r2, center ? "true" : "false"); + if (type == POLYHEDRON) + text.sprintf("polyhedron(points = %s, triangles = %s, convexity = %d);\n", points.dump().toAscii().data(), triangles.dump().toAscii().data(), convexity); + if (type == SQUARE) + text.sprintf("square(size = [%g, %g], center = %s);\n", x, y, center ? "true" : "false"); + if (type == CIRCLE) + text.sprintf("circle($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); + if (type == POLYGON) + text.sprintf("polygon(points = %s, paths = %s, convexity = %d);\n", points.dump().toAscii().data(), paths.dump().toAscii().data(), convexity); + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + diff --git a/src/printutils.cc b/src/printutils.cc new file mode 100644 index 0000000..a75cf14 --- /dev/null +++ b/src/printutils.cc @@ -0,0 +1,44 @@ +#include "printutils.h" +#include "MainWindow.h" + +QList print_messages_stack; + +void print_messages_push() +{ + print_messages_stack.append(QString()); +} + +void print_messages_pop() +{ + QString msg = print_messages_stack.last(); + print_messages_stack.removeLast(); + if (print_messages_stack.size() > 0 && !msg.isNull()) { + if (!print_messages_stack.last().isEmpty()) + print_messages_stack.last() += "\n"; + print_messages_stack.last() += msg; + } +} + +void PRINT(const QString &msg) +{ + if (msg.isNull()) + return; + if (print_messages_stack.size() > 0) { + if (!print_messages_stack.last().isEmpty()) + print_messages_stack.last() += "\n"; + print_messages_stack.last() += msg; + } + PRINT_NOCACHE(msg); +} + +void PRINT_NOCACHE(const QString &msg) +{ + if (msg.isNull()) + return; + if (MainWindow::current_win.isNull()) { + fprintf(stderr, "%s\n", msg.toAscii().data()); + } else { + MainWindow::current_win->console->append(msg); + } +} + diff --git a/src/printutils.h b/src/printutils.h new file mode 100644 index 0000000..18cef93 --- /dev/null +++ b/src/printutils.h @@ -0,0 +1,19 @@ +#ifndef PRINTUTILS_H_ +#define PRINTUTILS_H_ + +#include +#include + +extern QList print_messages_stack; +void print_messages_push(); +void print_messages_pop(); + +void PRINT(const QString &msg); +#define PRINTF(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT(_m); } while (0) +#define PRINTA(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT(_m); } while (0) + +void PRINT_NOCACHE(const QString &msg); +#define PRINTF_NOCACHE(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) +#define PRINTA_NOCACHE(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) + +#endif diff --git a/src/qtcolorbutton/README.txt b/src/qtcolorbutton/README.txt new file mode 100644 index 0000000..4b55f67 --- /dev/null +++ b/src/qtcolorbutton/README.txt @@ -0,0 +1,2 @@ +The QtColorButton class is copied from the gradient editor in Qt's shared tool classes: +http://qt.gitorious.org/qt/qt/trees/master/tools/shared/qtgradienteditor diff --git a/src/qtcolorbutton/qtcolorbutton.cpp b/src/qtcolorbutton/qtcolorbutton.cpp new file mode 100644 index 0000000..21b9848 --- /dev/null +++ b/src/qtcolorbutton/qtcolorbutton.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtcolorbutton.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QtColorButtonPrivate +{ + QtColorButton *q_ptr; + Q_DECLARE_PUBLIC(QtColorButton) +public: + QColor m_color; +#ifndef QT_NO_DRAGANDDROP + QColor m_dragColor; + QPoint m_dragStart; + bool m_dragging; +#endif + bool m_backgroundCheckered; + + void slotEditColor(); + QColor shownColor() const; + QPixmap generatePixmap() const; +}; + +void QtColorButtonPrivate::slotEditColor() +{ + bool ok; + const QRgb rgba = QColorDialog::getRgba(m_color.rgba(), &ok, q_ptr); + if (!ok) + return; + const QColor c = QColor::fromRgba(rgba); + if (c == q_ptr->color()) + return; + q_ptr->setColor(c); + emit q_ptr->colorChanged(m_color); +} + +QColor QtColorButtonPrivate::shownColor() const +{ +#ifndef QT_NO_DRAGANDDROP + if (m_dragging) + return m_dragColor; +#endif + return m_color; +} + +QPixmap QtColorButtonPrivate::generatePixmap() const +{ + QPixmap pix(24, 24); + + int pixSize = 20; + QBrush br(shownColor()); + + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); + br = QBrush(pm); + + QPainter p(&pix); + int corr = 1; + QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, + r.width() / 2, r.height() / 2, + QColor(shownColor().rgb())); + p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + + return pix; +} + +/////////////// + +QtColorButton::QtColorButton(QWidget *parent) + : QToolButton(parent) +{ + d_ptr = new QtColorButtonPrivate; + d_ptr->q_ptr = this; + d_ptr->m_dragging = false; + d_ptr->m_backgroundCheckered = true; + + setAcceptDrops(true); + + connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +QtColorButton::~QtColorButton() +{ + delete d_ptr; +} + +void QtColorButton::setColor(const QColor &color) +{ + if (d_ptr->m_color == color) + return; + d_ptr->m_color = color; + update(); +} + +QColor QtColorButton::color() const +{ + return d_ptr->m_color; +} + +void QtColorButton::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool QtColorButton::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtColorButton::paintEvent(QPaintEvent *event) +{ + QToolButton::paintEvent(event); + if (!isEnabled()) + return; + + const int pixSize = 10; + QBrush br(d_ptr->shownColor()); + if (d_ptr->m_backgroundCheckered) { + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); + br = QBrush(pm); + } + + QPainter p(this); + const int corr = 4; + QRect r = rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + //const int adjX = qRound(r.width() / 4.0); + //const int adjY = qRound(r.height() / 4.0); + //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY), + // QColor(d_ptr->shownColor().rgb())); + /* + p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0), + QColor(d_ptr->shownColor().rgb())); + p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4), + QColor(d_ptr->shownColor().rgb())); + */ + /* + const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha()))); + p.setPen(frameColor0); + p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1)); + */ + + const QColor frameColor1(0, 0, 0, 26); + p.setPen(frameColor1); + p.drawRect(r.adjusted(1, 1, -2, -2)); + const QColor frameColor2(0, 0, 0, 51); + p.setPen(frameColor2); + p.drawRect(r.adjusted(0, 0, -1, -1)); +} + +void QtColorButton::mousePressEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->button() == Qt::LeftButton) + d_ptr->m_dragStart = event->pos(); +#endif + QToolButton::mousePressEvent(event); +} + +void QtColorButton::mouseMoveEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->buttons() & Qt::LeftButton && + (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { + QMimeData *mime = new QMimeData; + mime->setColorData(color()); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(d_ptr->generatePixmap()); + setDown(false); + event->accept(); + drg->start(); + return; + } +#endif + QToolButton::mouseMoveEvent(event); +} + +#ifndef QT_NO_DRAGANDDROP +void QtColorButton::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + if (!mime->hasColor()) + return; + + event->accept(); + d_ptr->m_dragColor = qvariant_cast(mime->colorData()); + d_ptr->m_dragging = true; + update(); +} + +void QtColorButton::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + update(); +} + +void QtColorButton::dropEvent(QDropEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + if (d_ptr->m_dragColor == color()) + return; + setColor(d_ptr->m_dragColor); + emit colorChanged(color()); +} +#endif + +QT_END_NAMESPACE + +#include "moc_qtcolorbutton.cpp" diff --git a/src/qtcolorbutton/qtcolorbutton.h b/src/qtcolorbutton/qtcolorbutton.h new file mode 100644 index 0000000..26bdde0 --- /dev/null +++ b/src/qtcolorbutton/qtcolorbutton.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCOLORBUTTON_H +#define QTCOLORBUTTON_H + +#include + +QT_BEGIN_NAMESPACE + +class QtColorButton : public QToolButton +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) +public: + QtColorButton(QWidget *parent = 0); + ~QtColorButton(); + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + QColor color() const; + +public slots: + + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); +#endif +private: + class QtColorButtonPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtColorButton) + Q_DISABLE_COPY(QtColorButton) + Q_PRIVATE_SLOT(d_func(), void slotEditColor()) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtcolorbutton/qtcolorbutton.pri b/src/qtcolorbutton/qtcolorbutton.pri new file mode 100644 index 0000000..0e41068 --- /dev/null +++ b/src/qtcolorbutton/qtcolorbutton.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/qtcolorbutton.cpp +HEADERS += $$PWD/qtcolorbutton.h diff --git a/src/render.cc b/src/render.cc new file mode 100644 index 0000000..7c82d92 --- /dev/null +++ b/src/render.cc @@ -0,0 +1,264 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#include +#include +#include + +class RenderModule : public AbstractModule +{ +public: + RenderModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class RenderNode : public AbstractNode +{ +public: + int convexity; + RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { } +#ifdef ENABLE_CGAL + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + RenderNode *node = new RenderNode(inst); + + QVector argnames = QVector() << "convexity"; + QVector argexpr; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + Value v = c.lookup_variable("convexity"); + if (v.type == Value::NUMBER) + node->convexity = (int)v.num; + + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n != NULL) + node->children.append(n); + } + + return node; +} + +void register_builtin_render() +{ + builtin_modules["render"] = new RenderModule(); +} + +#ifdef ENABLE_CGAL + +CGAL_Nef_polyhedron RenderNode::render_cgal_nef_polyhedron() const +{ + QString cache_id = mk_cache_id(); + if (cgal_nef_cache.contains(cache_id)) { + progress_report(); + PRINT(cgal_nef_cache[cache_id]->msg); + return cgal_nef_cache[cache_id]->N; + } + + print_messages_push(); + + bool first = true; + CGAL_Nef_polyhedron N; + foreach(AbstractNode * v, children) + { + if (v->modinst->tag_background) + continue; + if (first) { + N = v->render_cgal_nef_polyhedron(); + if (N.dim != 0) + first = false; + } else if (N.dim == 2) { + N.p2 += v->render_cgal_nef_polyhedron().p2; + } else if (N.dim == 3) { + N.p3 += v->render_cgal_nef_polyhedron().p3; + } + } + + cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); + print_messages_pop(); + progress_report(); + + return N; +} + +static void report_func(const class AbstractNode*, void *vp, int mark) +{ + QProgressDialog *pd = (QProgressDialog*)vp; + int v = (int)((mark*100.0) / progress_report_count); + pd->setValue(v < 100 ? v : 99); + QString label; + label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); + pd->setLabelText(label); + QApplication::processEvents(); +} + +CSGTerm *RenderNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + QString key = mk_cache_id(); + if (PolySet::ps_cache.contains(key)) { + PRINT(PolySet::ps_cache[key]->msg); + return AbstractPolyNode::render_csg_term_from_ps(m, highlights, background, + PolySet::ps_cache[key]->ps->link(), modinst, idx); + } + + print_messages_push(); + CGAL_Nef_polyhedron N; + + QString cache_id = mk_cache_id(); + if (cgal_nef_cache.contains(cache_id)) + { + PRINT(cgal_nef_cache[cache_id]->msg); + N = cgal_nef_cache[cache_id]->N; + } + else + { + PRINT_NOCACHE("Processing uncached render statement..."); + // PRINTA("Cache ID: %1", cache_id); + QApplication::processEvents(); + + QTime t; + t.start(); + + QProgressDialog *pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + progress_report_prep((AbstractNode*)this, report_func, pd); + N = this->render_cgal_nef_polyhedron(); + progress_report_fin(); + + int s = t.elapsed() / 1000; + PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); + + delete pd; + } + + PolySet *ps = NULL; + + if (N.dim == 2) + { + DxfData dd(N); + ps = new PolySet(); + ps->is2d = true; + dxf_tesselate(ps, &dd, 0, true, false, 0); + dxf_border_to_ps(ps, &dd); + } + + if (N.dim == 3) + { + if (!N.p3.is_simple()) { + PRINTF("WARNING: Result of render() isn't valid 2-manifold! Modify your design.."); + return NULL; + } + + ps = new PolySet(); + + CGAL_Polyhedron P; + N.p3.convert_to_Polyhedron(P); + + typedef CGAL_Polyhedron::Vertex Vertex; + typedef CGAL_Polyhedron::Vertex_const_iterator VCI; + typedef CGAL_Polyhedron::Facet_const_iterator FCI; + typedef CGAL_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 v = *VCI((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); + } + } + + if (ps) + { + ps->convexity = convexity; + PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); + + CSGTerm *term = new CSGTerm(ps, m, QString("n%1").arg(idx)); + if (modinst->tag_highlight && highlights) + highlights->append(term->link()); + if (modinst->tag_background && background) { + background->append(term); + return NULL; + } + return term; + } + print_messages_pop(); + + return NULL; +} + +#else + +CSGTerm *RenderNode::render_csg_term(double m[20], QVector *highlights, QVector *background) const +{ + CSGTerm *t1 = NULL; + PRINT("WARNING: Found render() statement but compiled without CGAL support!"); + foreach(AbstractNode * v, children) { + CSGTerm *t2 = v->render_csg_term(m, highlights, background); + if (t2 && !t1) { + t1 = t2; + } else if (t2 && t1) { + t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); + } + } + if (modinst->tag_highlight && highlights) + highlights->append(t1->link()); + if (t1 && modinst->tag_background && background) { + background->append(t1); + return NULL; + } + return t1; +} + +#endif + +QString RenderNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text = indent + QString("n%1: ").arg(idx) + QString("render(convexity = %1) {\n").arg(QString::number(convexity)); + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + } + return dump_cache; +} + diff --git a/src/surface.cc b/src/surface.cc new file mode 100644 index 0000000..4dc3d98 --- /dev/null +++ b/src/surface.cc @@ -0,0 +1,201 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +#include + +class SurfaceModule : public AbstractModule +{ +public: + SurfaceModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class SurfaceNode : public AbstractPolyNode +{ +public: + QString filename; + bool center; + int convexity; + SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + SurfaceNode *node = new SurfaceNode(inst); + node->center = false; + node->convexity = 1; + + QVector argnames = QVector() << "file" << "center" << "convexity"; + QVector argexpr; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + node->filename = c.lookup_variable("file").text; + + Value center = c.lookup_variable("center", true); + if (center.type == Value::BOOL) { + node->center = center.b; + } + + Value convexity = c.lookup_variable("convexity", true); + if (convexity.type == Value::NUMBER) { + node->convexity = (int)convexity.num; + } + + return node; +} + +void register_builtin_surface() +{ + builtin_modules["surface"] = new SurfaceModule(); +} + +PolySet *SurfaceNode::render_polyset(render_mode_e) const +{ + handle_dep(filename); + QFile f(filename); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + PRINTF("WARNING: Can't open DXF file `%s'.", filename.toAscii().data()); + return NULL; + } + + int lines = 0, columns = 0; + QHash,double> data; + double min_val = 0; + + while (!f.atEnd()) + { + QString line = QString(f.readLine()).remove("\n").remove("\r"); + line.replace(QRegExp("^[ \t]+"), ""); + line.replace(QRegExp("[ \t]+$"), ""); + + if (line.startsWith("#")) + continue; + + QStringList fields = line.split(QRegExp("[ \t]+")); + for (int i = 0; i < fields.count(); i++) { + if (i >= columns) + columns = i + 1; + double v = fields[i].toDouble(); + data[QPair(lines, i)] = v; + min_val = fmin(v-1, min_val); + } + lines++; + } + + PolySet *p = new PolySet(); + p->convexity = convexity; + + double ox = center ? -columns/2.0 : 0; + double oy = center ? -lines/2.0 : 0; + + for (int i = 1; i < lines; i++) + for (int j = 1; j < columns; j++) + { + double v1 = data[QPair(i-1, j-1)]; + double v2 = data[QPair(i-1, j)]; + double v3 = data[QPair(i, j-1)]; + double v4 = data[QPair(i, j)]; + double vx = (v1 + v2 + v3 + v4) / 4; + + p->append_poly(); + p->append_vertex(ox + j-1, oy + i-1, v1); + p->append_vertex(ox + j, oy + i-1, v2); + p->append_vertex(ox + j-0.5, oy + i-0.5, vx); + + p->append_poly(); + p->append_vertex(ox + j, oy + i-1, v2); + p->append_vertex(ox + j, oy + i, v4); + p->append_vertex(ox + j-0.5, oy + i-0.5, vx); + + p->append_poly(); + p->append_vertex(ox + j, oy + i, v4); + p->append_vertex(ox + j-1, oy + i, v3); + p->append_vertex(ox + j-0.5, oy + i-0.5, vx); + + p->append_poly(); + p->append_vertex(ox + j-1, oy + i, v3); + p->append_vertex(ox + j-1, oy + i-1, v1); + p->append_vertex(ox + j-0.5, oy + i-0.5, vx); + } + + for (int i = 1; i < lines; i++) + { + p->append_poly(); + p->append_vertex(ox + 0, oy + i-1, min_val); + p->append_vertex(ox + 0, oy + i-1, data[QPair(i-1, 0)]); + p->append_vertex(ox + 0, oy + i, data[QPair(i, 0)]); + p->append_vertex(ox + 0, oy + i, min_val); + + p->append_poly(); + p->insert_vertex(ox + columns-1, oy + i-1, min_val); + p->insert_vertex(ox + columns-1, oy + i-1, data[QPair(i-1, columns-1)]); + p->insert_vertex(ox + columns-1, oy + i, data[QPair(i, columns-1)]); + p->insert_vertex(ox + columns-1, oy + i, min_val); + } + + for (int i = 1; i < columns; i++) + { + p->append_poly(); + p->insert_vertex(ox + i-1, oy + 0, min_val); + p->insert_vertex(ox + i-1, oy + 0, data[QPair(0, i-1)]); + p->insert_vertex(ox + i, oy + 0, data[QPair(0, i)]); + p->insert_vertex(ox + i, oy + 0, min_val); + + p->append_poly(); + p->append_vertex(ox + i-1, oy + lines-1, min_val); + p->append_vertex(ox + i-1, oy + lines-1, data[QPair(lines-1, i-1)]); + p->append_vertex(ox + i, oy + lines-1, data[QPair(lines-1, i)]); + p->append_vertex(ox + i, oy + lines-1, min_val); + } + + p->append_poly(); + for (int i = 0; i < columns-1; i++) + p->insert_vertex(ox + i, oy + 0, min_val); + for (int i = 0; i < lines-1; i++) + p->insert_vertex(ox + columns-1, oy + i, min_val); + for (int i = columns-1; i > 0; i--) + p->insert_vertex(ox + i, oy + lines-1, min_val); + for (int i = lines-1; i > 0; i--) + p->insert_vertex(ox + 0, oy + i, min_val); + + return p; +} + +QString SurfaceNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + text.sprintf("surface(file = \"%s\", center = %s);\n", + filename.toAscii().data(), center ? "true" : "false"); + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + diff --git a/src/transform.cc b/src/transform.cc new file mode 100644 index 0000000..ecbfcc2 --- /dev/null +++ b/src/transform.cc @@ -0,0 +1,369 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" +#include "printutils.h" + +enum transform_type_e { + SCALE, + ROTATE, + MIRROR, + TRANSLATE, + MULTMATRIX, + COLOR +}; + +class TransformModule : public AbstractModule +{ +public: + transform_type_e type; + TransformModule(transform_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class TransformNode : public AbstractNode +{ +public: + double m[20]; + TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } +#ifdef ENABLE_CGAL + virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; +#endif + virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + TransformNode *node = new TransformNode(inst); + + for (int i = 0; i < 16; i++) + node->m[i] = i % 5 == 0 ? 1.0 : 0.0; + for (int i = 16; i < 20; i++) + node->m[i] = -1; + + QVector argnames; + QVector argexpr; + + if (type == SCALE) { + argnames = QVector() << "v"; + } + if (type == ROTATE) { + argnames = QVector() << "a" << "v"; + } + if (type == MIRROR) { + argnames = QVector() << "v"; + } + if (type == TRANSLATE) { + argnames = QVector() << "v"; + } + if (type == MULTMATRIX) { + argnames = QVector() << "m"; + } + if (type == COLOR) { + argnames = QVector() << "c"; + } + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + if (type == SCALE) + { + Value v = c.lookup_variable("v"); + v.getnum(node->m[0]); + v.getnum(node->m[5]); + v.getnum(node->m[10]); + v.getv3(node->m[0], node->m[5], node->m[10]); + if (node->m[10] <= 0) + node->m[10] = 1; + } + if (type == ROTATE) + { + Value val_a = c.lookup_variable("a"); + if (val_a.type == Value::VECTOR) + { + for (int i = 0; i < 3 && i < val_a.vec.size(); i++) { + double a; + val_a.vec[i]->getnum(a); + double c = cos(a*M_PI/180.0); + double s = sin(a*M_PI/180.0); + double x = i == 0, y = i == 1, z = i == 2; + double mr[16] = { + x*x*(1-c)+c, + y*x*(1-c)+z*s, + z*x*(1-c)-y*s, + 0, + x*y*(1-c)-z*s, + y*y*(1-c)+c, + z*y*(1-c)+x*s, + 0, + x*z*(1-c)+y*s, + y*z*(1-c)-x*s, + z*z*(1-c)+c, + 0, + 0, 0, 0, 1 + }; + double m[16]; + for (int x = 0; x < 4; x++) + for (int y = 0; y < 4; y++) + { + m[x+y*4] = 0; + for (int i = 0; i < 4; i++) + m[x+y*4] += node->m[i+y*4] * mr[x+i*4]; + } + for (int i = 0; i < 16; i++) + node->m[i] = m[i]; + } + } + else + { + Value val_v = c.lookup_variable("v"); + double a = 0, x = 0, y = 0, z = 1; + + val_a.getnum(a); + + if (val_v.getv3(x, y, z)) { + if (x != 0.0 || y != 0.0 || z != 0.0) { + double sn = 1.0 / sqrt(x*x + y*y + z*z); + x *= sn, y *= sn, z *= sn; + } + } + + if (x != 0.0 || y != 0.0 || z != 0.0) + { + double c = cos(a*M_PI/180.0); + double s = sin(a*M_PI/180.0); + + node->m[ 0] = x*x*(1-c)+c; + node->m[ 1] = y*x*(1-c)+z*s; + node->m[ 2] = z*x*(1-c)-y*s; + + node->m[ 4] = x*y*(1-c)-z*s; + node->m[ 5] = y*y*(1-c)+c; + node->m[ 6] = z*y*(1-c)+x*s; + + node->m[ 8] = x*z*(1-c)+y*s; + node->m[ 9] = y*z*(1-c)-x*s; + node->m[10] = z*z*(1-c)+c; + } + } + } + if (type == MIRROR) + { + Value val_v = c.lookup_variable("v"); + double x = 1, y = 0, z = 0; + + if (val_v.getv3(x, y, z)) { + if (x != 0.0 || y != 0.0 || z != 0.0) { + double sn = 1.0 / sqrt(x*x + y*y + z*z); + x *= sn, y *= sn, z *= sn; + } + } + + if (x != 0.0 || y != 0.0 || z != 0.0) + { + node->m[ 0] = 1-2*x*x; + node->m[ 1] = -2*y*x; + node->m[ 2] = -2*z*x; + + node->m[ 4] = -2*x*y; + node->m[ 5] = 1-2*y*y; + node->m[ 6] = -2*z*y; + + node->m[ 8] = -2*x*z; + node->m[ 9] = -2*y*z; + node->m[10] = 1-2*z*z; + } + } + if (type == TRANSLATE) + { + Value v = c.lookup_variable("v"); + v.getv3(node->m[12], node->m[13], node->m[14]); + } + if (type == MULTMATRIX) + { + Value v = c.lookup_variable("m"); + if (v.type == Value::VECTOR) { + for (int i = 0; i < 16; i++) { + int x = i / 4, y = i % 4; + if (y < v.vec.size() && v.vec[y]->type == Value::VECTOR && x < v.vec[y]->vec.size()) + v.vec[y]->vec[x]->getnum(node->m[i]); + } + } + } + if (type == COLOR) + { + Value v = c.lookup_variable("c"); + if (v.type == Value::VECTOR) { + for (int i = 0; i < 4; i++) + node->m[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0; + } + } + + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n != NULL) + node->children.append(n); + } + + return node; +} + +#ifdef ENABLE_CGAL + +CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const +{ + QString cache_id = mk_cache_id(); + if (cgal_nef_cache.contains(cache_id)) { + progress_report(); + PRINT(cgal_nef_cache[cache_id]->msg); + return cgal_nef_cache[cache_id]->N; + } + + print_messages_push(); + + bool first = true; + CGAL_Nef_polyhedron N; + + foreach (AbstractNode *v, children) { + if (v->modinst->tag_background) + continue; + if (first) { + N = v->render_cgal_nef_polyhedron(); + if (N.dim != 0) + first = false; + } else if (N.dim == 2) { + N.p2 += v->render_cgal_nef_polyhedron().p2; + } else if (N.dim == 3) { + N.p3 += v->render_cgal_nef_polyhedron().p3; + } + } + + if (N.dim == 2) + { + // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2 + // objects. So we convert in to our internal 2d data format, transform it, + // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack! + + CGAL_Aff_transformation2 t( + m[0], m[4], m[12], + m[1], m[5], m[13], m[15]); + + DxfData dd(N); + for (int i=0; i < dd.points.size(); i++) { + CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y); + p = t.transform(p); + dd.points[i].x = to_double(p.x()); + dd.points[i].y = to_double(p.y()); + } + + PolySet ps; + ps.is2d = true; + dxf_tesselate(&ps, &dd, 0, true, false, 0); + + N = ps.render_cgal_nef_polyhedron(); + ps.refcount = 0; + } + if (N.dim == 3) { + CGAL_Aff_transformation t( + m[0], m[4], m[ 8], m[12], + m[1], m[5], m[ 9], m[13], + m[2], m[6], m[10], m[14], m[15]); + N.p3.transform(t); + } + + cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); + print_messages_pop(); + progress_report(); + + return N; +} + +#endif /* ENABLE_CGAL */ + +CSGTerm *TransformNode::render_csg_term(double c[20], QVector *highlights, QVector *background) const +{ + double x[20]; + + for (int i = 0; i < 16; i++) + { + int c_row = i%4; + int m_col = i/4; + x[i] = 0; + for (int j = 0; j < 4; j++) + x[i] += c[c_row + j*4] * m[m_col*4 + j]; + } + + for (int i = 16; i < 20; i++) + x[i] = m[i] < 0 ? c[i] : m[i]; + + CSGTerm *t1 = NULL; + foreach(AbstractNode *v, children) + { + CSGTerm *t2 = v->render_csg_term(x, highlights, background); + if (t2 && !t1) { + t1 = t2; + } else if (t2 && t1) { + t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); + } + } + if (t1 && modinst->tag_highlight && highlights) + highlights->append(t1->link()); + if (t1 && modinst->tag_background && background) { + background->append(t1); + return NULL; + } + return t1; +} + +QString TransformNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) + text.sprintf("n%d: color([%g, %g, %g, %g])", idx, + m[16], m[17], m[18], m[19]); + else + text.sprintf("n%d: multmatrix([[%g, %g, %g, %g], [%g, %g, %g, %g], " + "[%g, %g, %g, %g], [%g, %g, %g, %g]])", idx, + m[0], m[4], m[ 8], m[12], + m[1], m[5], m[ 9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15]); + text = indent + text + " {\n"; + foreach (AbstractNode *v, children) + text += v->dump(indent + QString("\t")); + ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + } + return dump_cache; +} + +void register_builtin_transform() +{ + builtin_modules["scale"] = new TransformModule(SCALE); + builtin_modules["rotate"] = new TransformModule(ROTATE); + builtin_modules["mirror"] = new TransformModule(MIRROR); + builtin_modules["translate"] = new TransformModule(TRANSLATE); + builtin_modules["multmatrix"] = new TransformModule(MULTMATRIX); + builtin_modules["color"] = new TransformModule(COLOR); +} + diff --git a/src/value.cc b/src/value.cc new file mode 100644 index 0000000..a02a27f --- /dev/null +++ b/src/value.cc @@ -0,0 +1,346 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad.h" + +Value::Value() +{ + reset_undef(); +} + +Value::~Value() +{ + for (int i = 0; i < vec.size(); i++) + delete vec[i]; + vec.clear(); +} + +Value::Value(bool v) +{ + reset_undef(); + type = BOOL; + b = v; +} + +Value::Value(double v) +{ + reset_undef(); + type = NUMBER; + num = v; +} + +Value::Value(const QString &t) +{ + reset_undef(); + type = STRING; + text = t; +} + +Value::Value(const Value &v) +{ + *this = v; +} + +Value& Value::operator = (const Value &v) +{ + reset_undef(); + type = v.type; + b = v.b; + num = v.num; + for (int i = 0; i < v.vec.size(); i++) + vec.append(new Value(*v.vec[i])); + range_begin = v.range_begin; + range_step = v.range_step; + range_end = v.range_end; + text = v.text; + return *this; +} + +Value Value::operator ! () const +{ + if (type == BOOL) { + return Value(!b); + } + return Value(); +} + +Value Value::operator && (const Value &v) const +{ + if (type == BOOL && v.type == BOOL) { + return Value(b && v.b); + } + return Value(); +} + +Value Value::operator || (const Value &v) const +{ + if (type == BOOL && v.type == BOOL) { + return Value(b || v.b); + } + return Value(); +} + +Value Value::operator + (const Value &v) const +{ + if (type == VECTOR && v.type == VECTOR) { + Value r; + r.type = VECTOR; + for (int i = 0; i < vec.size() && i < v.vec.size(); i++) + r.vec.append(new Value(*vec[i] + *v.vec[i])); + return r; + } + if (type == NUMBER && v.type == NUMBER) { + return Value(num + v.num); + } + return Value(); +} + +Value Value::operator - (const Value &v) const +{ + if (type == VECTOR && v.type == VECTOR) { + Value r; + r.type = VECTOR; + for (int i = 0; i < vec.size() && i < v.vec.size(); i++) + r.vec.append(new Value(*vec[i] - *v.vec[i])); + return r; + } + if (type == NUMBER && v.type == NUMBER) { + return Value(num - v.num); + } + return Value(); +} + +Value Value::operator * (const Value &v) const +{ + if (type == VECTOR && v.type == NUMBER) { + Value r; + r.type = VECTOR; + for (int i = 0; i < vec.size(); i++) + r.vec.append(new Value(*vec[i] * v)); + return r; + } + if (type == NUMBER && v.type == VECTOR) { + Value r; + r.type = VECTOR; + for (int i = 0; i < v.vec.size(); i++) + r.vec.append(new Value(v * *v.vec[i])); + return r; + } + if (type == NUMBER && v.type == NUMBER) { + return Value(num * v.num); + } + return Value(); +} + +Value Value::operator / (const Value &v) const +{ + if (type == VECTOR && v.type == NUMBER) { + Value r; + r.type = VECTOR; + for (int i = 0; i < vec.size(); i++) + r.vec.append(new Value(*vec[i] / v)); + return r; + } + if (type == NUMBER && v.type == VECTOR) { + Value r; + r.type = VECTOR; + for (int i = 0; i < v.vec.size(); i++) + r.vec.append(new Value(v / *v.vec[i])); + return r; + } + if (type == NUMBER && v.type == NUMBER) { + return Value(num / v.num); + } + return Value(); +} + +Value Value::operator % (const Value &v) const +{ + if (type == NUMBER && v.type == NUMBER) { + return Value(fmod(num, v.num)); + } + return Value(); +} + +Value Value::operator < (const Value &v) const +{ + if (type == NUMBER && v.type == NUMBER) { + return Value(num < v.num); + } + return Value(); +} + +Value Value::operator <= (const Value &v) const +{ + if (type == NUMBER && v.type == NUMBER) { + return Value(num <= v.num); + } + return Value(); +} + +Value Value::operator == (const Value &v) const +{ + if (type == BOOL && v.type == BOOL) { + return Value(b == v.b); + } + if (type == NUMBER && v.type == NUMBER) { + return Value(num == v.num); + } + if (type == RANGE && v.type == RANGE) { + return Value(range_begin == v.range_begin && range_step == v.range_step && range_end == v.range_end); + } + if (type == VECTOR && v.type == VECTOR) { + if (vec.size() != v.vec.size()) + return Value(false); + for (int i=0; i= (const Value &v) const +{ + if (type == NUMBER && v.type == NUMBER) { + return Value(num >= v.num); + } + return Value(); +} + +Value Value::operator > (const Value &v) const +{ + if (type == NUMBER && v.type == NUMBER) { + return Value(num > v.num); + } + return Value(); +} + +Value Value::inv() const +{ + if (type == VECTOR) { + Value r; + r.type = VECTOR; + for (int i = 0; i < vec.size(); i++) + r.vec.append(new Value(vec[i]->inv())); + return r; + } + if (type == NUMBER) + return Value(-num); + return Value(); +} + +bool Value::getnum(double &v) const +{ + if (type != NUMBER) + return false; + v = num; + return true; +} + +bool Value::getv2(double &x, double &y) const +{ + if (type != VECTOR || vec.size() != 2) + return false; + if (vec[0]->type != NUMBER) + return false; + if (vec[1]->type != NUMBER) + return false; + x = vec[0]->num; + y = vec[1]->num; + return true; +} + +bool Value::getv3(double &x, double &y, double &z) const +{ + if (type == VECTOR && vec.size() == 2) { + if (getv2(x, y)) { + z = 0; + return true; + } + return false; + } + if (type != VECTOR || vec.size() != 3) + return false; + if (vec[0]->type != NUMBER) + return false; + if (vec[1]->type != NUMBER) + return false; + if (vec[2]->type != NUMBER) + return false; + x = vec[0]->num; + y = vec[1]->num; + z = vec[2]->num; + return true; +} + +QString Value::dump() const +{ + if (type == STRING) { + return QString("\"") + text + QString("\""); + } + if (type == VECTOR) { + QString text = "["; + for (int i = 0; i < vec.size(); i++) { + if (i > 0) + text += ", "; + text += vec[i]->dump(); + } + return text + "]"; + } + if (type == RANGE) { + QString text; + text.sprintf("[ %g : %g : %g ]", range_begin, range_step, range_end); + return text; + } + if (type == NUMBER) { + QString text; + text.sprintf("%g", num); + return text; + } + if (type == BOOL) { + return QString(b ? "true" : "false"); + } + return QString("undef"); +} + +void Value::reset_undef() +{ + type = UNDEFINED; + b = false; + num = 0; + for (int i = 0; i < vec.size(); i++) + delete vec[i]; + vec.clear(); + range_begin = 0; + range_step = 0; + range_end = 0; + text = QString(); +} + diff --git a/surface.cc b/surface.cc deleted file mode 100644 index 4dc3d98..0000000 --- a/surface.cc +++ /dev/null @@ -1,201 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -#include - -class SurfaceModule : public AbstractModule -{ -public: - SurfaceModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class SurfaceNode : public AbstractPolyNode -{ -public: - QString filename; - bool center; - int convexity; - SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - SurfaceNode *node = new SurfaceNode(inst); - node->center = false; - node->convexity = 1; - - QVector argnames = QVector() << "file" << "center" << "convexity"; - QVector argexpr; - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - node->filename = c.lookup_variable("file").text; - - Value center = c.lookup_variable("center", true); - if (center.type == Value::BOOL) { - node->center = center.b; - } - - Value convexity = c.lookup_variable("convexity", true); - if (convexity.type == Value::NUMBER) { - node->convexity = (int)convexity.num; - } - - return node; -} - -void register_builtin_surface() -{ - builtin_modules["surface"] = new SurfaceModule(); -} - -PolySet *SurfaceNode::render_polyset(render_mode_e) const -{ - handle_dep(filename); - QFile f(filename); - - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTF("WARNING: Can't open DXF file `%s'.", filename.toAscii().data()); - return NULL; - } - - int lines = 0, columns = 0; - QHash,double> data; - double min_val = 0; - - while (!f.atEnd()) - { - QString line = QString(f.readLine()).remove("\n").remove("\r"); - line.replace(QRegExp("^[ \t]+"), ""); - line.replace(QRegExp("[ \t]+$"), ""); - - if (line.startsWith("#")) - continue; - - QStringList fields = line.split(QRegExp("[ \t]+")); - for (int i = 0; i < fields.count(); i++) { - if (i >= columns) - columns = i + 1; - double v = fields[i].toDouble(); - data[QPair(lines, i)] = v; - min_val = fmin(v-1, min_val); - } - lines++; - } - - PolySet *p = new PolySet(); - p->convexity = convexity; - - double ox = center ? -columns/2.0 : 0; - double oy = center ? -lines/2.0 : 0; - - for (int i = 1; i < lines; i++) - for (int j = 1; j < columns; j++) - { - double v1 = data[QPair(i-1, j-1)]; - double v2 = data[QPair(i-1, j)]; - double v3 = data[QPair(i, j-1)]; - double v4 = data[QPair(i, j)]; - double vx = (v1 + v2 + v3 + v4) / 4; - - p->append_poly(); - p->append_vertex(ox + j-1, oy + i-1, v1); - p->append_vertex(ox + j, oy + i-1, v2); - p->append_vertex(ox + j-0.5, oy + i-0.5, vx); - - p->append_poly(); - p->append_vertex(ox + j, oy + i-1, v2); - p->append_vertex(ox + j, oy + i, v4); - p->append_vertex(ox + j-0.5, oy + i-0.5, vx); - - p->append_poly(); - p->append_vertex(ox + j, oy + i, v4); - p->append_vertex(ox + j-1, oy + i, v3); - p->append_vertex(ox + j-0.5, oy + i-0.5, vx); - - p->append_poly(); - p->append_vertex(ox + j-1, oy + i, v3); - p->append_vertex(ox + j-1, oy + i-1, v1); - p->append_vertex(ox + j-0.5, oy + i-0.5, vx); - } - - for (int i = 1; i < lines; i++) - { - p->append_poly(); - p->append_vertex(ox + 0, oy + i-1, min_val); - p->append_vertex(ox + 0, oy + i-1, data[QPair(i-1, 0)]); - p->append_vertex(ox + 0, oy + i, data[QPair(i, 0)]); - p->append_vertex(ox + 0, oy + i, min_val); - - p->append_poly(); - p->insert_vertex(ox + columns-1, oy + i-1, min_val); - p->insert_vertex(ox + columns-1, oy + i-1, data[QPair(i-1, columns-1)]); - p->insert_vertex(ox + columns-1, oy + i, data[QPair(i, columns-1)]); - p->insert_vertex(ox + columns-1, oy + i, min_val); - } - - for (int i = 1; i < columns; i++) - { - p->append_poly(); - p->insert_vertex(ox + i-1, oy + 0, min_val); - p->insert_vertex(ox + i-1, oy + 0, data[QPair(0, i-1)]); - p->insert_vertex(ox + i, oy + 0, data[QPair(0, i)]); - p->insert_vertex(ox + i, oy + 0, min_val); - - p->append_poly(); - p->append_vertex(ox + i-1, oy + lines-1, min_val); - p->append_vertex(ox + i-1, oy + lines-1, data[QPair(lines-1, i-1)]); - p->append_vertex(ox + i, oy + lines-1, data[QPair(lines-1, i)]); - p->append_vertex(ox + i, oy + lines-1, min_val); - } - - p->append_poly(); - for (int i = 0; i < columns-1; i++) - p->insert_vertex(ox + i, oy + 0, min_val); - for (int i = 0; i < lines-1; i++) - p->insert_vertex(ox + columns-1, oy + i, min_val); - for (int i = columns-1; i > 0; i--) - p->insert_vertex(ox + i, oy + lines-1, min_val); - for (int i = lines-1; i > 0; i--) - p->insert_vertex(ox + 0, oy + i, min_val); - - return p; -} - -QString SurfaceNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - text.sprintf("surface(file = \"%s\", center = %s);\n", - filename.toAscii().data(), center ? "true" : "false"); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} - diff --git a/transform.cc b/transform.cc deleted file mode 100644 index ecbfcc2..0000000 --- a/transform.cc +++ /dev/null @@ -1,369 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define INCLUDE_ABSTRACT_NODE_DETAILS - -#include "openscad.h" -#include "printutils.h" - -enum transform_type_e { - SCALE, - ROTATE, - MIRROR, - TRANSLATE, - MULTMATRIX, - COLOR -}; - -class TransformModule : public AbstractModule -{ -public: - transform_type_e type; - TransformModule(transform_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -}; - -class TransformNode : public AbstractNode -{ -public: - double m[20]; - TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector *highlights, QVector *background) const; - virtual QString dump(QString indent) const; -}; - -AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const -{ - TransformNode *node = new TransformNode(inst); - - for (int i = 0; i < 16; i++) - node->m[i] = i % 5 == 0 ? 1.0 : 0.0; - for (int i = 16; i < 20; i++) - node->m[i] = -1; - - QVector argnames; - QVector argexpr; - - if (type == SCALE) { - argnames = QVector() << "v"; - } - if (type == ROTATE) { - argnames = QVector() << "a" << "v"; - } - if (type == MIRROR) { - argnames = QVector() << "v"; - } - if (type == TRANSLATE) { - argnames = QVector() << "v"; - } - if (type == MULTMATRIX) { - argnames = QVector() << "m"; - } - if (type == COLOR) { - argnames = QVector() << "c"; - } - - Context c(ctx); - c.args(argnames, argexpr, inst->argnames, inst->argvalues); - - if (type == SCALE) - { - Value v = c.lookup_variable("v"); - v.getnum(node->m[0]); - v.getnum(node->m[5]); - v.getnum(node->m[10]); - v.getv3(node->m[0], node->m[5], node->m[10]); - if (node->m[10] <= 0) - node->m[10] = 1; - } - if (type == ROTATE) - { - Value val_a = c.lookup_variable("a"); - if (val_a.type == Value::VECTOR) - { - for (int i = 0; i < 3 && i < val_a.vec.size(); i++) { - double a; - val_a.vec[i]->getnum(a); - double c = cos(a*M_PI/180.0); - double s = sin(a*M_PI/180.0); - double x = i == 0, y = i == 1, z = i == 2; - double mr[16] = { - x*x*(1-c)+c, - y*x*(1-c)+z*s, - z*x*(1-c)-y*s, - 0, - x*y*(1-c)-z*s, - y*y*(1-c)+c, - z*y*(1-c)+x*s, - 0, - x*z*(1-c)+y*s, - y*z*(1-c)-x*s, - z*z*(1-c)+c, - 0, - 0, 0, 0, 1 - }; - double m[16]; - for (int x = 0; x < 4; x++) - for (int y = 0; y < 4; y++) - { - m[x+y*4] = 0; - for (int i = 0; i < 4; i++) - m[x+y*4] += node->m[i+y*4] * mr[x+i*4]; - } - for (int i = 0; i < 16; i++) - node->m[i] = m[i]; - } - } - else - { - Value val_v = c.lookup_variable("v"); - double a = 0, x = 0, y = 0, z = 1; - - val_a.getnum(a); - - if (val_v.getv3(x, y, z)) { - if (x != 0.0 || y != 0.0 || z != 0.0) { - double sn = 1.0 / sqrt(x*x + y*y + z*z); - x *= sn, y *= sn, z *= sn; - } - } - - if (x != 0.0 || y != 0.0 || z != 0.0) - { - double c = cos(a*M_PI/180.0); - double s = sin(a*M_PI/180.0); - - node->m[ 0] = x*x*(1-c)+c; - node->m[ 1] = y*x*(1-c)+z*s; - node->m[ 2] = z*x*(1-c)-y*s; - - node->m[ 4] = x*y*(1-c)-z*s; - node->m[ 5] = y*y*(1-c)+c; - node->m[ 6] = z*y*(1-c)+x*s; - - node->m[ 8] = x*z*(1-c)+y*s; - node->m[ 9] = y*z*(1-c)-x*s; - node->m[10] = z*z*(1-c)+c; - } - } - } - if (type == MIRROR) - { - Value val_v = c.lookup_variable("v"); - double x = 1, y = 0, z = 0; - - if (val_v.getv3(x, y, z)) { - if (x != 0.0 || y != 0.0 || z != 0.0) { - double sn = 1.0 / sqrt(x*x + y*y + z*z); - x *= sn, y *= sn, z *= sn; - } - } - - if (x != 0.0 || y != 0.0 || z != 0.0) - { - node->m[ 0] = 1-2*x*x; - node->m[ 1] = -2*y*x; - node->m[ 2] = -2*z*x; - - node->m[ 4] = -2*x*y; - node->m[ 5] = 1-2*y*y; - node->m[ 6] = -2*z*y; - - node->m[ 8] = -2*x*z; - node->m[ 9] = -2*y*z; - node->m[10] = 1-2*z*z; - } - } - if (type == TRANSLATE) - { - Value v = c.lookup_variable("v"); - v.getv3(node->m[12], node->m[13], node->m[14]); - } - if (type == MULTMATRIX) - { - Value v = c.lookup_variable("m"); - if (v.type == Value::VECTOR) { - for (int i = 0; i < 16; i++) { - int x = i / 4, y = i % 4; - if (y < v.vec.size() && v.vec[y]->type == Value::VECTOR && x < v.vec[y]->vec.size()) - v.vec[y]->vec[x]->getnum(node->m[i]); - } - } - } - if (type == COLOR) - { - Value v = c.lookup_variable("c"); - if (v.type == Value::VECTOR) { - for (int i = 0; i < 4; i++) - node->m[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0; - } - } - - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } - - return node; -} - -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const -{ - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) { - progress_report(); - PRINT(cgal_nef_cache[cache_id]->msg); - return cgal_nef_cache[cache_id]->N; - } - - print_messages_push(); - - bool first = true; - CGAL_Nef_polyhedron N; - - foreach (AbstractNode *v, children) { - if (v->modinst->tag_background) - continue; - if (first) { - N = v->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (N.dim == 3) { - N.p3 += v->render_cgal_nef_polyhedron().p3; - } - } - - if (N.dim == 2) - { - // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2 - // objects. So we convert in to our internal 2d data format, transform it, - // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack! - - CGAL_Aff_transformation2 t( - m[0], m[4], m[12], - m[1], m[5], m[13], m[15]); - - DxfData dd(N); - for (int i=0; i < dd.points.size(); i++) { - CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y); - p = t.transform(p); - dd.points[i].x = to_double(p.x()); - dd.points[i].y = to_double(p.y()); - } - - PolySet ps; - ps.is2d = true; - dxf_tesselate(&ps, &dd, 0, true, false, 0); - - N = ps.render_cgal_nef_polyhedron(); - ps.refcount = 0; - } - if (N.dim == 3) { - CGAL_Aff_transformation t( - m[0], m[4], m[ 8], m[12], - m[1], m[5], m[ 9], m[13], - m[2], m[6], m[10], m[14], m[15]); - N.p3.transform(t); - } - - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - print_messages_pop(); - progress_report(); - - return N; -} - -#endif /* ENABLE_CGAL */ - -CSGTerm *TransformNode::render_csg_term(double c[20], QVector *highlights, QVector *background) const -{ - double x[20]; - - for (int i = 0; i < 16; i++) - { - int c_row = i%4; - int m_col = i/4; - x[i] = 0; - for (int j = 0; j < 4; j++) - x[i] += c[c_row + j*4] * m[m_col*4 + j]; - } - - for (int i = 16; i < 20; i++) - x[i] = m[i] < 0 ? c[i] : m[i]; - - CSGTerm *t1 = NULL; - foreach(AbstractNode *v, children) - { - CSGTerm *t2 = v->render_csg_term(x, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } - } - if (t1 && modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; -} - -QString TransformNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) - text.sprintf("n%d: color([%g, %g, %g, %g])", idx, - m[16], m[17], m[18], m[19]); - else - text.sprintf("n%d: multmatrix([[%g, %g, %g, %g], [%g, %g, %g, %g], " - "[%g, %g, %g, %g], [%g, %g, %g, %g]])", idx, - m[0], m[4], m[ 8], m[12], - m[1], m[5], m[ 9], m[13], - m[2], m[6], m[10], m[14], - m[3], m[7], m[11], m[15]); - text = indent + text + " {\n"; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - -void register_builtin_transform() -{ - builtin_modules["scale"] = new TransformModule(SCALE); - builtin_modules["rotate"] = new TransformModule(ROTATE); - builtin_modules["mirror"] = new TransformModule(MIRROR); - builtin_modules["translate"] = new TransformModule(TRANSLATE); - builtin_modules["multmatrix"] = new TransformModule(MULTMATRIX); - builtin_modules["color"] = new TransformModule(COLOR); -} - diff --git a/value.cc b/value.cc deleted file mode 100644 index a02a27f..0000000 --- a/value.cc +++ /dev/null @@ -1,346 +0,0 @@ -/* - * OpenSCAD (www.openscad.at) - * Copyright (C) 2009 Clifford Wolf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "openscad.h" - -Value::Value() -{ - reset_undef(); -} - -Value::~Value() -{ - for (int i = 0; i < vec.size(); i++) - delete vec[i]; - vec.clear(); -} - -Value::Value(bool v) -{ - reset_undef(); - type = BOOL; - b = v; -} - -Value::Value(double v) -{ - reset_undef(); - type = NUMBER; - num = v; -} - -Value::Value(const QString &t) -{ - reset_undef(); - type = STRING; - text = t; -} - -Value::Value(const Value &v) -{ - *this = v; -} - -Value& Value::operator = (const Value &v) -{ - reset_undef(); - type = v.type; - b = v.b; - num = v.num; - for (int i = 0; i < v.vec.size(); i++) - vec.append(new Value(*v.vec[i])); - range_begin = v.range_begin; - range_step = v.range_step; - range_end = v.range_end; - text = v.text; - return *this; -} - -Value Value::operator ! () const -{ - if (type == BOOL) { - return Value(!b); - } - return Value(); -} - -Value Value::operator && (const Value &v) const -{ - if (type == BOOL && v.type == BOOL) { - return Value(b && v.b); - } - return Value(); -} - -Value Value::operator || (const Value &v) const -{ - if (type == BOOL && v.type == BOOL) { - return Value(b || v.b); - } - return Value(); -} - -Value Value::operator + (const Value &v) const -{ - if (type == VECTOR && v.type == VECTOR) { - Value r; - r.type = VECTOR; - for (int i = 0; i < vec.size() && i < v.vec.size(); i++) - r.vec.append(new Value(*vec[i] + *v.vec[i])); - return r; - } - if (type == NUMBER && v.type == NUMBER) { - return Value(num + v.num); - } - return Value(); -} - -Value Value::operator - (const Value &v) const -{ - if (type == VECTOR && v.type == VECTOR) { - Value r; - r.type = VECTOR; - for (int i = 0; i < vec.size() && i < v.vec.size(); i++) - r.vec.append(new Value(*vec[i] - *v.vec[i])); - return r; - } - if (type == NUMBER && v.type == NUMBER) { - return Value(num - v.num); - } - return Value(); -} - -Value Value::operator * (const Value &v) const -{ - if (type == VECTOR && v.type == NUMBER) { - Value r; - r.type = VECTOR; - for (int i = 0; i < vec.size(); i++) - r.vec.append(new Value(*vec[i] * v)); - return r; - } - if (type == NUMBER && v.type == VECTOR) { - Value r; - r.type = VECTOR; - for (int i = 0; i < v.vec.size(); i++) - r.vec.append(new Value(v * *v.vec[i])); - return r; - } - if (type == NUMBER && v.type == NUMBER) { - return Value(num * v.num); - } - return Value(); -} - -Value Value::operator / (const Value &v) const -{ - if (type == VECTOR && v.type == NUMBER) { - Value r; - r.type = VECTOR; - for (int i = 0; i < vec.size(); i++) - r.vec.append(new Value(*vec[i] / v)); - return r; - } - if (type == NUMBER && v.type == VECTOR) { - Value r; - r.type = VECTOR; - for (int i = 0; i < v.vec.size(); i++) - r.vec.append(new Value(v / *v.vec[i])); - return r; - } - if (type == NUMBER && v.type == NUMBER) { - return Value(num / v.num); - } - return Value(); -} - -Value Value::operator % (const Value &v) const -{ - if (type == NUMBER && v.type == NUMBER) { - return Value(fmod(num, v.num)); - } - return Value(); -} - -Value Value::operator < (const Value &v) const -{ - if (type == NUMBER && v.type == NUMBER) { - return Value(num < v.num); - } - return Value(); -} - -Value Value::operator <= (const Value &v) const -{ - if (type == NUMBER && v.type == NUMBER) { - return Value(num <= v.num); - } - return Value(); -} - -Value Value::operator == (const Value &v) const -{ - if (type == BOOL && v.type == BOOL) { - return Value(b == v.b); - } - if (type == NUMBER && v.type == NUMBER) { - return Value(num == v.num); - } - if (type == RANGE && v.type == RANGE) { - return Value(range_begin == v.range_begin && range_step == v.range_step && range_end == v.range_end); - } - if (type == VECTOR && v.type == VECTOR) { - if (vec.size() != v.vec.size()) - return Value(false); - for (int i=0; i= (const Value &v) const -{ - if (type == NUMBER && v.type == NUMBER) { - return Value(num >= v.num); - } - return Value(); -} - -Value Value::operator > (const Value &v) const -{ - if (type == NUMBER && v.type == NUMBER) { - return Value(num > v.num); - } - return Value(); -} - -Value Value::inv() const -{ - if (type == VECTOR) { - Value r; - r.type = VECTOR; - for (int i = 0; i < vec.size(); i++) - r.vec.append(new Value(vec[i]->inv())); - return r; - } - if (type == NUMBER) - return Value(-num); - return Value(); -} - -bool Value::getnum(double &v) const -{ - if (type != NUMBER) - return false; - v = num; - return true; -} - -bool Value::getv2(double &x, double &y) const -{ - if (type != VECTOR || vec.size() != 2) - return false; - if (vec[0]->type != NUMBER) - return false; - if (vec[1]->type != NUMBER) - return false; - x = vec[0]->num; - y = vec[1]->num; - return true; -} - -bool Value::getv3(double &x, double &y, double &z) const -{ - if (type == VECTOR && vec.size() == 2) { - if (getv2(x, y)) { - z = 0; - return true; - } - return false; - } - if (type != VECTOR || vec.size() != 3) - return false; - if (vec[0]->type != NUMBER) - return false; - if (vec[1]->type != NUMBER) - return false; - if (vec[2]->type != NUMBER) - return false; - x = vec[0]->num; - y = vec[1]->num; - z = vec[2]->num; - return true; -} - -QString Value::dump() const -{ - if (type == STRING) { - return QString("\"") + text + QString("\""); - } - if (type == VECTOR) { - QString text = "["; - for (int i = 0; i < vec.size(); i++) { - if (i > 0) - text += ", "; - text += vec[i]->dump(); - } - return text + "]"; - } - if (type == RANGE) { - QString text; - text.sprintf("[ %g : %g : %g ]", range_begin, range_step, range_end); - return text; - } - if (type == NUMBER) { - QString text; - text.sprintf("%g", num); - return text; - } - if (type == BOOL) { - return QString(b ? "true" : "false"); - } - return QString("undef"); -} - -void Value::reset_undef() -{ - type = UNDEFINED; - b = false; - num = 0; - for (int i = 0; i < vec.size(); i++) - delete vec[i]; - vec.clear(); - range_begin = 0; - range_step = 0; - range_end = 0; - text = QString(); -} - -- cgit v0.10.1