summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDon Bright <hugh.m.bright@gmail.com>2012-05-28 16:48:46 (GMT)
committerDon Bright <hugh.m.bright@gmail.com>2012-05-28 16:48:46 (GMT)
commitdd2002a81673b3875ce8c4e8a61cb10278c4eb03 (patch)
tree7aaadf1c9b12cd37a7a913d3e76256f6406fa939 /src
parent4381762f5aa2e6a56258618e585e1510ead88684 (diff)
parent67eb2ebe90447e966dc1e08b91c43d937c521583 (diff)
Tidy up code. Generate proper test png images. Merge branch 'master' into caliston1.
Conflicts: src/PolySetCGALEvaluator.cc
Diffstat (limited to 'src')
-rw-r--r--src/CGALEvaluator.cc63
-rw-r--r--src/CGAL_Nef_polyhedron.cc2
-rw-r--r--src/MainWindow.h16
-rw-r--r--src/MainWindow.ui6
-rw-r--r--src/ModuleCache.cc106
-rw-r--r--src/ModuleCache.h23
-rw-r--r--src/OpenCSGWarningDialog.cc2
-rw-r--r--src/PolySetCGALEvaluator.cc199
-rw-r--r--src/PolySetCGALEvaluator.h2
-rw-r--r--src/context.cc1
-rw-r--r--src/csgtermnormalizer.cc5
-rw-r--r--src/dxfdata.cc8
-rw-r--r--src/dxftess-cgal.cc2
-rw-r--r--src/expr.cc4
-rw-r--r--src/func.cc140
-rw-r--r--src/glview.cc27
-rw-r--r--src/lexer.l19
-rw-r--r--src/mainwin.cc354
-rw-r--r--src/module.cc37
-rw-r--r--src/module.h13
-rw-r--r--src/openscad.cc45
-rw-r--r--src/openscad.h2
-rw-r--r--src/parser.y120
-rw-r--r--src/primitives.cc2
-rw-r--r--src/system-gl.h1
-rw-r--r--src/transform.cc9
-rw-r--r--src/value.cc67
-rw-r--r--src/value.h2
28 files changed, 830 insertions, 447 deletions
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index 15fa746..46f4cfa 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -43,11 +43,8 @@ CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractNode &node)
Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX);
evaluate.execute();
return this->root;
- assert(this->visitedchildren.empty());
- }
- else {
- return CGALCache::instance()->get(this->tree.getIdString(node));
}
+ return CGALCache::instance()->get(this->tree.getIdString(node));
}
bool CGALEvaluator::isCached(const AbstractNode &node) const
@@ -252,31 +249,45 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node)
// 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(
- node.matrix(0,0), node.matrix(0,1), node.matrix(0,3),
- node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3));
-
- DxfData *dd = N.convertToDxfData();
- for (size_t i=0; i < dd->points.size(); i++) {
- CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]);
- p = t.transform(p);
- dd->points[i][0] = to_double(p.x());
- dd->points[i][1] = to_double(p.y());
+ Eigen::Matrix2f testmat;
+ testmat << node.matrix(0,0), node.matrix(0,1), node.matrix(1,0), node.matrix(1,1);
+ if (testmat.determinant() == 0) {
+ PRINT("Warning: Scaling a 2D object with 0 - removing object");
+ N.p2.reset();
+ }
+ else {
+ CGAL_Aff_transformation2 t(
+ node.matrix(0,0), node.matrix(0,1), node.matrix(0,3),
+ node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3));
+
+ DxfData *dd = N.convertToDxfData();
+ for (size_t i=0; i < dd->points.size(); i++) {
+ CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]);
+ p = t.transform(p);
+ dd->points[i][0] = to_double(p.x());
+ dd->points[i][1] = to_double(p.y());
+ }
+
+ PolySet ps;
+ ps.is2d = true;
+ dxf_tesselate(&ps, *dd, 0, true, false, 0);
+
+ N = evaluateCGALMesh(ps);
+ delete dd;
}
-
- PolySet ps;
- ps.is2d = true;
- dxf_tesselate(&ps, *dd, 0, true, false, 0);
-
- N = evaluateCGALMesh(ps);
- delete dd;
}
else if (N.dim == 3) {
- CGAL_Aff_transformation t(
- node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3),
- node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3),
- node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3));
- N.p3->transform(t);
+ if (node.matrix.matrix().determinant() == 0) {
+ PRINT("Warning: Scaling a 3D object with 0 - removing object");
+ N.p3.reset();
+ }
+ else {
+ CGAL_Aff_transformation t(
+ node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3),
+ node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3),
+ node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3));
+ N.p3->transform(t);
+ }
}
}
else {
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 54c02e7..046ed12 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -62,6 +62,8 @@ CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &o
int CGAL_Nef_polyhedron::weight() const
{
+ if (this->empty()) return 0;
+
size_t memsize = sizeof(CGAL_Nef_polyhedron);
if (this->dim == 2) {
memsize += sizeof(CGAL_Nef_polyhedron2) +
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 5546290..a4835c2 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -27,10 +27,10 @@ public:
double tval, fps, fsteps;
QTimer *autoReloadTimer;
- QString autoReloadInfo;
+ std::string autoReloadId;
Context root_ctx;
- AbstractModule *root_module; // Result of parsing
+ Module *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
@@ -73,12 +73,16 @@ private slots:
private:
void openFile(const QString &filename);
- void load();
+ void refreshDocument();
AbstractNode *find_root_tag(AbstractNode *n);
- void compile(bool procevents);
+ void updateTemporalVariables();
+ bool fileChangedOnDisk();
+ bool includesChanged();
+ bool compileTopLevelDocument(bool reload);
+ bool compile(bool reload, bool procevents);
void compileCSG(bool procevents);
bool maybeSave();
- bool checkModified();
+ bool checkEditorModified();
QString dumpCSGTree(AbstractNode *root);
static void consoleOutput(const std::string &msg, void *userdata);
void loadViewSettings();
@@ -157,7 +161,7 @@ public slots:
void helpAbout();
void helpHomepage();
void helpManual();
- void helpOpenGL();
+ void helpLibrary();
void quit();
void actionReloadCompile();
void checkAutoReload();
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index f89e0d4..13bb226 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -222,7 +222,7 @@
<addaction name="helpActionAbout"/>
<addaction name="helpActionHomepage"/>
<addaction name="helpActionManual"/>
- <addaction name="helpActionOpenGLInfo"/>
+ <addaction name="helpActionLibraryInfo"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
@@ -671,9 +671,9 @@
<string>Export as CSG...</string>
</property>
</action>
- <action name="helpActionOpenGLInfo">
+ <action name="helpActionLibraryInfo">
<property name="text">
- <string>OpenGL info</string>
+ <string>Library info</string>
</property>
</action>
</widget>
diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc
new file mode 100644
index 0000000..70b1fb9
--- /dev/null
+++ b/src/ModuleCache.cc
@@ -0,0 +1,106 @@
+#include "ModuleCache.h"
+#include "module.h"
+#include "printutils.h"
+#include "openscad.h"
+
+#include "boosty.h"
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+
+#include <stdio.h>
+#include <fstream>
+#include <sstream>
+#include <time.h>
+#include <sys/stat.h>
+
+/*!
+ FIXME: Implement an LRU scheme to avoid having an ever-growing module cache
+*/
+
+ModuleCache *ModuleCache::inst = NULL;
+
+static bool is_modified(const std::string &filename, const time_t &mtime)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ return (st.st_mtime > mtime);
+}
+
+Module *ModuleCache::evaluate(const std::string &filename)
+{
+ Module *lib_mod = NULL;
+
+ // Create cache ID
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+
+ std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ // Lookup in cache
+ if (this->entries.find(filename) != this->entries.end() &&
+ this->entries[filename].cache_id == cache_id) {
+#ifdef DEBUG
+// Causes too much debug output
+// PRINTB("Using cached library: %s (%s)", filename % cache_id);
+#endif
+ lib_mod = &(*this->entries[filename].module);
+
+ BOOST_FOREACH(const Module::IncludeContainer::value_type &item, lib_mod->includes) {
+ if (is_modified(item.first, item.second)) {
+ lib_mod = NULL;
+ break;
+ }
+ }
+ }
+
+ // If cache lookup failed (non-existing or old timestamp), compile module
+ if (!lib_mod) {
+#ifdef DEBUG
+ if (this->entries.find(filename) != this->entries.end()) {
+ PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
+ }
+ else {
+ PRINTB("Compiling library '%s'.", filename);
+ }
+#endif
+
+ std::ifstream ifs(filename.c_str());
+ if (!ifs.is_open()) {
+ PRINTB("WARNING: Can't open library file '%s'\n", filename);
+ return NULL;
+ }
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+
+ print_messages_push();
+
+ cache_entry e = { NULL, cache_id };
+ if (this->entries.find(filename) != this->entries.end()) {
+ delete this->entries[filename].module;
+ }
+ this->entries[filename] = e;
+
+ std::string pathname = boosty::stringy(fs::path(filename).parent_path());
+ lib_mod = dynamic_cast<Module*>(parse(text.c_str(), pathname.c_str(), 0));
+
+ if (lib_mod) {
+ this->entries[filename].module = lib_mod;
+ } else {
+ this->entries.erase(filename);
+ }
+
+ print_messages_pop();
+ }
+
+ if (lib_mod) lib_mod->handleDependencies();
+
+ return lib_mod;
+}
+
+void ModuleCache::clear()
+{
+ this->entries.clear();
+}
+
diff --git a/src/ModuleCache.h b/src/ModuleCache.h
new file mode 100644
index 0000000..1e6373d
--- /dev/null
+++ b/src/ModuleCache.h
@@ -0,0 +1,23 @@
+#include <string>
+#include <boost/unordered_map.hpp>
+
+class ModuleCache
+{
+public:
+ static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
+ class Module *evaluate(const std::string &filename);
+ size_t size() { return this->entries.size(); }
+ void clear();
+
+private:
+ ModuleCache() {}
+ ~ModuleCache() {}
+
+ static ModuleCache *inst;
+
+ struct cache_entry {
+ class Module *module;
+ std::string cache_id;
+ };
+ boost::unordered_map<std::string, cache_entry> entries;
+};
diff --git a/src/OpenCSGWarningDialog.cc b/src/OpenCSGWarningDialog.cc
index fdaaa50..5648576 100644
--- a/src/OpenCSGWarningDialog.cc
+++ b/src/OpenCSGWarningDialog.cc
@@ -1,7 +1,7 @@
#include "OpenCSGWarningDialog.h"
#include "Preferences.h"
-OpenCSGWarningDialog::OpenCSGWarningDialog(QWidget *parent)
+OpenCSGWarningDialog::OpenCSGWarningDialog(QWidget*)
{
setupUi(this);
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index 9e30bc5..3b272d2 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -16,19 +16,45 @@
#include "openscad.h" // get_fragments_from_r()
#include <boost/foreach.hpp>
-// This object 'visits' the Nef Polyhedron 3d, extracting 2d information
-// from it for the projection( cut = true ) command.
-// http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+/*
+
+This Visitor is used in the 'cut' process. Essentially, one or more of
+our 3d nef polyhedrons have been 'cut' by the xy-plane, forming a number
+of polygons that are essentially 'flat'. This Visitor object, when
+called repeatedly, collects those flat polygons together and forms a new
+2d nef polyhedron out of them. It keeps track of the 'holes' so that
+in the final resulting polygon, they are preserved properly.
+
+The output polygon is stored in the output_nefpoly2d variable.
+
+For more information on 3d + 2d nef polyhedrons, facets, halffacets,
+facet cycles, etc, please see these websites:
+
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron_3-Traits---Halffacet.html
+
+The halffacet iteration 'circulator' code is based on OGL_helper.h
+
+Why do we throw out all 'down' half-facets? Imagine a triangle in 3d
+space - it has one 'half face' for one 'side' and another 'half face' for the
+other 'side'. If we iterated over both half-faces we would get the same vertex
+coordinates twice. Instead, we only need one side, in our case, we
+chose the 'up' side.
+*/
class NefShellVisitor_for_cut {
public:
std::stringstream out;
CGAL_Nef_polyhedron2::Boundary boundary;
- shared_ptr<CGAL_Nef_polyhedron2> tmpnef;
- shared_ptr<CGAL_Nef_polyhedron2> nefpoly2d;
- NefShellVisitor_for_cut()
+ shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d;
+ shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d;
+ CGAL::Direction_3<CGAL_Kernel3> up;
+ bool debug;
+ NefShellVisitor_for_cut(bool debug=false)
{
- nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
+ output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
boundary = CGAL_Nef_polyhedron2::INCLUDED;
+ up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1);
+ this->debug = debug;
}
std::string dump()
{
@@ -40,59 +66,38 @@ public:
void visit( CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::SFace_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) {
- // This method is fed each 'half-facet' of the Nef_polyhedron3 that's been intersected
- // with the flat x-y plane. I.e. it's fed a bunch of flat 3d polygons with z==0 at all vertexes.
- // It is fed both the 'up' pointing half-facets and the 'down' pointing half-facets.
- // We only need one set of vertexes, so we skip all of the 'down' facets.
- //
- // The vertexes are recorded in lists called 'contours'. Those are 'joined' or 'intersected'
- // with the main result polyhedron, 'nefpoly2d', depending on whether it's a "hole" contour
- // or a "body" contour.
- CGAL::Direction_3<CGAL_Kernel3> up(0,0,1);
- CGAL::Plane_3<CGAL_Kernel3> plane = hfacet->plane();
- // out << " direction == up? " << ( plane.orthogonal_direction() == up ) << "\n";
- if ( plane.orthogonal_direction() != up ) {
- // out << "direction == down. skipping";
+ if ( hfacet->plane().orthogonal_direction() != this->up ) {
+ if (debug) out << "down facing half-facet. skipping\n";
return;
}
int numcontours = 0;
- CGAL_Nef_polyhedron2::Point point;
- CGAL_Nef_polyhedron3::Vertex_const_handle vertex;
- CGAL_Nef_polyhedron3::Halfedge_const_handle halfedge;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;
- CGAL_Nef_polyhedron3::SHalfedge_const_handle first_halfedge, j;
- for ( i = hfacet->facet_cycles_begin(); i != hfacet->facet_cycles_end(); ++i ) {
- j = CGAL_Nef_polyhedron3::SHalfedge_const_handle( i );
- first_halfedge = j;
+ CGAL_forall_facet_cycles_of( i, hfacet ) {
+ CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);
std::list<CGAL_Nef_polyhedron2::Point> contour;
- do {
- // j->source() is a CGAL_Nef_polyhedron3::Nef_polyhedron_S2::SVertex,
- // but SVertex is the same thing as CGAL_Nef_polyhedron3::Halfedge
- // and Halfedge can give us an actual point.
- halfedge = CGAL_Nef_polyhedron3::Halfedge_const_handle( j->source() );
- vertex = CGAL_Nef_polyhedron3::Vertex_const_handle( halfedge->source() );
- point = CGAL_Nef_polyhedron2::Point( vertex->point().x(), vertex->point().y() );
- contour.push_back( point );
- //out << " add xyz " << x << " "<< y << " " <<z << endl;
- j = j->next();
- } while ( j != first_halfedge );
- tmpnef.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
+ CGAL_For_all( c1, c2 ) {
+ CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->source()->point();
+ CGAL_Nef_polyhedron2::Point point2d( point3d.x(), point3d.y() );
+ contour.push_back( point2d );
+ }
+ tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
if ( numcontours == 0 ) {
- //out << " contour is a body. joining. " << contour.size() << " points.\n" ;
- *nefpoly2d += *tmpnef;
+ if (debug) out << " contour is a body. make union(). " << contour.size() << " points.\n" ;
+ *output_nefpoly2d += *tmpnef2d;
} else {
- //out << " contour is a hole. intersecting. " << contour.size() << " points.\n";
- *nefpoly2d *= *tmpnef;
+ if (debug) out << " contour is a hole. make intersection(). " << contour.size() << " points.\n";
+ *output_nefpoly2d *= *tmpnef2d;
}
numcontours++;
- } // next facet cycle
+ } // next facet cycle (i.e. next contour)
} // visit()
};
PolySetCGALEvaluator::PolySetCGALEvaluator(CGALEvaluator &cgalevaluator)
: PolySetEvaluator(cgalevaluator.getTree()), cgalevaluator(cgalevaluator)
{
+ this->debug = false;
}
PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
@@ -108,19 +113,24 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
}
}
if (sum.empty()) return NULL;
+ if (!sum.p3->is_simple()) {
+ if (!node.cut_mode) {
+ PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
+ return new PolySet();
+ }
+ }
- PolySet *ps = new PolySet();
- PolySet *ps3 = NULL;
- DxfData *dxf = NULL;
- CGAL_Nef_polyhedron np;
- ps->convexity = node.convexity;
- ps->is2d = true;
+ CGAL_Nef_polyhedron nef_poly;
if (node.cut_mode)
{
- CGAL_Nef_polyhedron3::Plane_3 plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
- *sum.p3 = sum.p3->intersection( plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
+ // intersect 'sum' with the x-y plane
+ CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
+ *sum.p3 = sum.p3->intersection( xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
+ // Visit each polygon in sum.p3 and union/intersect into a 2d polygon (with holes)
+ // For info on Volumes, Shells, Facets, and the 'visitor' pattern, please see
+ // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
NefShellVisitor_for_cut shell_visitor;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
@@ -131,20 +141,11 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
sum.p3->visit_shell_objects( sface_handle , shell_visitor );
}
}
- // std::cout << "shell visitor\n" << shell_visitor.dump() << "\n";
+ if (debug) std::cout << "shell visitor\n" << shell_visitor.dump() << "\nshell visitor end\n";
- /*if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }*/
-
- np.p2 = shell_visitor.nefpoly2d;
- // std::cout << np.dump_p2() << "\n";
- np.dim = 2;
-
- ps3 = np.convertToPolyset();
- // std::cout << "----------\n" << ps3->dump() << "\n";
- if (!ps3) return NULL;
+ nef_poly.p2 = shell_visitor.output_nefpoly2d;
+ nef_poly.dim = 2;
+ if (debug) std::cout << "--\n" << nef_poly.dump_p2() << "\n";
// Extract polygons in the XY plane, ignoring all other polygons
// FIXME: If the polyhedron is really thin, there might be unwanted polygons
@@ -152,7 +153,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
// and cause a crash in CGALEvaluator::PolyReducer. The right solution is to
// filter these polygons here. kintel 20120203.
/*
- Grid2d<int> conversion_grid(GRID_COARSE);
+ Grid2d<unsigned int> conversion_grid(GRID_COARSE);
for (size_t i = 0; i < ps3->polygons.size(); i++) {
for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
double x = ps3->polygons[i][j][0];
@@ -178,12 +179,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
// In projection mode all the triangles are projected manually into the XY plane
else
{
- if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
- ps3 = sum.convertToPolyset();
+ PolySet *ps3 = sum.convertToPolyset();
if (!ps3) return NULL;
for (size_t i = 0; i < ps3->polygons.size(); i++)
{
@@ -223,22 +219,22 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
plist.push_back(p);
}
// FIXME: Should the CGAL_Nef_polyhedron2 be cached?
- if (np.empty()) {
- np.dim = 2;
- np.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
+ if (nef_poly.empty()) {
+ nef_poly.dim = 2;
+ nef_poly.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
}
else {
- (*np.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ (*nef_poly.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
}
+ delete ps3;
}
- delete ps3;
- dxf = np.convertToDxfData();
- dxf_tesselate(ps, *dxf, 0, true, false, 0);
- dxf_border_to_ps(ps, *dxf);
- delete dxf;
-cant_project_non_simple_polyhedron:
+ PolySet *ps = nef_poly.convertToPolyset();
+ assert( ps != NULL );
+ ps->convexity = node.convexity;
+ if (debug) std::cout << "--\n" << ps->dump() << "\n";
+
return ps;
}
@@ -324,12 +320,14 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const LinearExtrudeNode &node)
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
- if (N.dim != 2) {
- PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
- }
- else {
- if (sum.empty()) sum = N.copy();
- else sum += N;
+ if (!N.empty()) {
+ if (N.dim != 2) {
+ PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
+ }
+ else {
+ if (sum.empty()) sum = N.copy();
+ else sum += N;
+ }
}
}
@@ -422,12 +420,14 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const RotateExtrudeNode &node)
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
- if (N.dim != 2) {
- PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!");
- }
- else {
- if (sum.empty()) sum = N.copy();
- else sum += N;
+ if (!N.empty()) {
+ if (N.dim != 2) {
+ PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!");
+ }
+ else {
+ if (sum.empty()) sum = N.copy();
+ else sum += N;
+ }
}
}
@@ -493,15 +493,10 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
}
for (int j = 0; j < fragments; j++) {
- double a = (j*2*M_PI) / fragments;
+ double a = (j*2*M_PI) / fragments - M_PI/2; // start on the X axis
for (size_t k = 0; k < dxf.paths[i].indices.size(); k++) {
- if (dxf.points[dxf.paths[i].indices[k]][0] == 0) {
- points[j][k][0] = 0;
- points[j][k][1] = 0;
- } else {
- points[j][k][0] = dxf.points[dxf.paths[i].indices[k]][0] * sin(a);
- points[j][k][1] = dxf.points[dxf.paths[i].indices[k]][0] * cos(a);
- }
+ points[j][k][0] = dxf.points[dxf.paths[i].indices[k]][0] * sin(a);
+ points[j][k][1] = dxf.points[dxf.paths[i].indices[k]][0] * cos(a);
points[j][k][2] = dxf.points[dxf.paths[i].indices[k]][1];
}
}
diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h
index dddcfc5..00e79f1 100644
--- a/src/PolySetCGALEvaluator.h
+++ b/src/PolySetCGALEvaluator.h
@@ -17,7 +17,7 @@ public:
virtual PolySet *evaluatePolySet(const RotateExtrudeNode &node);
virtual PolySet *evaluatePolySet(const CgaladvNode &node);
virtual PolySet *evaluatePolySet(const RenderNode &node);
-
+ bool debug;
protected:
PolySet *extrudeDxfData(const LinearExtrudeNode &node, class DxfData &dxf);
PolySet *rotateDxfData(const RotateExtrudeNode &node, class DxfData &dxf);
diff --git a/src/context.cc b/src/context.cc
index b9e685c..f96a45b 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -162,6 +162,7 @@ AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
}
if (this->usedlibs_p) {
BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
+ assert(m.second);
if (m.second->modules.find(inst.name()) != m.second->modules.end()) {
Context ctx(this->parent, m.second);
return m.second->modules[inst.name()]->evaluate(&ctx, &inst);
diff --git a/src/csgtermnormalizer.cc b/src/csgtermnormalizer.cc
index 0e7a759..6600758 100644
--- a/src/csgtermnormalizer.cc
+++ b/src/csgtermnormalizer.cc
@@ -44,9 +44,10 @@ shared_ptr<CSGTerm> CSGTermNormalizer::normalizePass(shared_ptr<CSGTerm> term)
do {
while (term && normalize_tail(term)) { }
if (!term || term->type == CSGTerm::TYPE_PRIMITIVE) return term;
- term->left = normalizePass(term->left);
+ if (term->left) term->left = normalizePass(term->left);
} while (term->type != CSGTerm::TYPE_UNION &&
- (term->right->type != CSGTerm::TYPE_PRIMITIVE || term->left->type == CSGTerm::TYPE_UNION));
+ ((term->right && term->right->type != CSGTerm::TYPE_PRIMITIVE) ||
+ (term->left && term->left->type == CSGTerm::TYPE_UNION)));
term->right = normalizePass(term->right);
// FIXME: Do we need to take into account any transformation of item here?
diff --git a/src/dxfdata.cc b/src/dxfdata.cc
index 20fcc71..00b246f 100644
--- a/src/dxfdata.cc
+++ b/src/dxfdata.cc
@@ -519,7 +519,7 @@ void DxfData::fixup_path_direction()
break;
this->paths[i].is_inner = true;
double min_x = this->points[this->paths[i].indices[0]][0];
- int min_x_point = 0;
+ size_t min_x_point = 0;
for (size_t j = 1; j < this->paths[i].indices.size(); j++) {
if (this->points[this->paths[i].indices[j]][0] < min_x) {
min_x = this->points[this->paths[i].indices[j]][0];
@@ -527,9 +527,9 @@ void DxfData::fixup_path_direction()
}
}
// rotate points if the path is in non-standard rotation
- int b = min_x_point;
- int a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1;
- int c = b == this->paths[i].indices.size() - 1 ? 1 : b + 1;
+ size_t b = min_x_point;
+ size_t a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1;
+ size_t c = b == this->paths[i].indices.size() - 1 ? 1 : b + 1;
double ax = this->points[this->paths[i].indices[a]][0] - this->points[this->paths[i].indices[b]][0];
double ay = this->points[this->paths[i].indices[a]][1] - this->points[this->paths[i].indices[b]][1];
double cx = this->points[this->paths[i].indices[c]][0] - this->points[this->paths[i].indices[b]][0];
diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc
index 15859bd..f221e3a 100644
--- a/src/dxftess-cgal.cc
+++ b/src/dxftess-cgal.cc
@@ -131,7 +131,7 @@ void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool /* do_tr
// ..maybe it would be better to assert here. But this would
// break compatibility with the glu tesselator that handled such
// cases just fine.
- PRINT( "WARNING: Duplicate vertex during Tessellation. Render may be incorrect." );
+ PRINT( "WARNING: Duplicate vertex found during Tessellation. Render may be incorrect." );
continue;
}
diff --git a/src/expr.cc b/src/expr.cc
index 66a0d11..671553c 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -86,8 +86,8 @@ Value Expression::evaluate(const Context *context) const
return *v1.vec[i];
}
if (v1.type == Value::STRING && v2.type == Value::NUMBER) {
- int i = int(v2.num);
- if (i >= 0 && i < v1.text.size())
+ unsigned int i = int(v2.num);
+ if (i < v1.text.size())
return Value(v1.text.substr(i, 1));
}
return Value();
diff --git a/src/func.cc b/src/func.cc
index 6686cb9..0c9b450 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -33,6 +33,7 @@
#include "mathc99.h"
#include <algorithm>
#include "stl-utils.h"
+#include "printutils.h"
AbstractFunction::~AbstractFunction()
{
@@ -345,6 +346,144 @@ Value builtin_lookup(const Context *, const std::vector<std::string>&, const std
return Value(high_v * f + low_v * (1-f));
}
+/*
+ Pattern:
+
+ "search" "(" ( match_value | list_of_match_values ) "," vector_of_vectors
+ ("," num_returns_per_match
+ ("," index_col_num )? )?
+ ")";
+ match_value : ( Value::NUMBER | Value::STRING );
+ list_of_values : "[" match_value ("," match_value)* "]";
+ vector_of_vectors : "[" ("[" Value ("," Value)* "]")+ "]";
+ num_returns_per_match : int;
+ index_col_num : int;
+
+ Examples:
+ Index values return as list:
+ search("a","abcdabcd");
+ - returns [0,4]
+ search("a","abcdabcd",1);
+ - returns [0]
+ search("e","abcdabcd",1);
+ - returns []
+ search("a",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]);
+ - returns [0,4]
+
+ Search on different column; return Index values:
+ search(3,[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",3] ], 0, 1);
+ - returns [0,8]
+
+ Search on list of values:
+ Return all matches per search vector element:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 0);
+ - returns [[0,4],[1,5],[2,6]]
+
+ Return first match per search vector element; special case return vector:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 1);
+ - returns [0,1,2]
+
+ Return first two matches per search vector element; vector of vectors:
+ search("abce",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 2);
+ - returns [[0,4],[1,5],[2,6],[8]]
+
+*/
+Value builtin_search(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+{
+ if (args.size() < 2) return Value();
+
+ const Value &findThis = args[0];
+ const Value &searchTable = args[1];
+ unsigned int num_returns_per_match = (args.size() > 2) ? args[2].num : 1;
+ unsigned int index_col_num = (args.size() > 3) ? args[3].num : 0;
+
+ Value returnVector;
+ returnVector.type = Value::VECTOR;
+
+ if (findThis.type == Value::NUMBER) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ if (searchTable.vec[j]->vec[index_col_num]->type == Value::NUMBER &&
+ findThis.num == searchTable.vec[j]->vec[index_col_num]->num) {
+ returnVector.append(new Value(double(j)));
+ matchCount++;
+ if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ } else if (findThis.type == Value::STRING) {
+ unsigned int searchTableSize;
+ if (searchTable.type == Value::STRING) searchTableSize = searchTable.text.size();
+ else searchTableSize = searchTable.vec.size();
+ for (size_t i = 0; i < findThis.text.size(); i++) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTableSize; j++) {
+ if ((searchTable.type == Value::VECTOR &&
+ findThis.text[i] == searchTable.vec[j]->vec[index_col_num]->text[0]) ||
+ (searchTable.type == Value::STRING &&
+ findThis.text[i] == searchTable.text[j])) {
+ Value *resultValue = new Value(double(j));
+ matchCount++;
+ if (num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ if (matchCount == 0) PRINTB(" search term not found: \"%s\"", findThis.text[i]);
+ if (num_returns_per_match == 0 || num_returns_per_match > 1) {
+ returnVector.append(resultVector);
+ }
+ }
+ } else if (findThis.type == Value::VECTOR) {
+ for (size_t i = 0; i < findThis.vec.size(); i++) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ if ((findThis.vec[i]->type == Value::NUMBER &&
+ searchTable.vec[j]->vec[index_col_num]->type == Value::NUMBER &&
+ findThis.vec[i]->num == searchTable.vec[j]->vec[index_col_num]->num) ||
+ (findThis.vec[i]->type == Value::STRING &&
+ searchTable.vec[j]->vec[index_col_num]->type == Value::STRING &&
+ findThis.vec[i]->text == searchTable.vec[j]->vec[index_col_num]->text)) {
+ Value *resultValue = new Value(double(j));
+ matchCount++;
+ if (num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ if (num_returns_per_match == 1 && matchCount == 0) {
+ if (findThis.vec[i]->type == Value::NUMBER) {
+ PRINTB(" search term not found: %s",findThis.vec[i]->num);
+ }
+ else if (findThis.vec[i]->type == Value::STRING) {
+ PRINTB(" search term not found: \"%s\"",findThis.vec[i]->text);
+ }
+ returnVector.append(resultVector);
+ }
+ if (num_returns_per_match == 0 || num_returns_per_match > 1) {
+ returnVector.append(resultVector);
+ }
+ }
+ } else {
+ PRINTB(" search: none performed on input %s", findThis);
+ return Value();
+ }
+ return returnVector;
+}
+
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
@@ -397,6 +536,7 @@ void register_builtin_functions()
Builtins::init("ln", new BuiltinFunction(&builtin_ln));
Builtins::init("str", new BuiltinFunction(&builtin_str));
Builtins::init("lookup", new BuiltinFunction(&builtin_lookup));
+ Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version));
Builtins::init("version_num", new BuiltinFunction(&builtin_version_num));
}
diff --git a/src/glview.cc b/src/glview.cc
index 0f9ec5b..aa2e746 100644
--- a/src/glview.cc
+++ b/src/glview.cc
@@ -393,8 +393,6 @@ void GLView::paintGL()
glRotated(object_rot_y, 0.0, 1.0, 0.0);
glRotated(object_rot_z, 0.0, 0.0, 1.0);
- glTranslated(object_trans_x, object_trans_y, object_trans_z);
-
// FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them
// to change color based on view orientation.
if (showcrosshairs)
@@ -412,6 +410,8 @@ void GLView::paintGL()
glEnd();
}
+ glTranslated(object_trans_x, object_trans_y, object_trans_z);
+
// Large gray axis cross inline with the model
// FIXME: This is always gray - adjust color to keep contrast with background
if (showaxes)
@@ -589,14 +589,24 @@ void GLView::mouseMoveEvent(QMouseEvent *event)
normalizeAngle(object_rot_y);
normalizeAngle(object_rot_z);
} else {
- // Right button pans
- // Shift-right zooms
+ // Right button pans in the xz plane
+ // Middle button pans in the xy plane
+ // Shift-right and Shift-middle zooms
if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) {
viewer_distance += (GLdouble)dy;
} else {
double mx = +(dx) * viewer_distance/1000;
- double my = -(dy) * viewer_distance/1000;
+ double mz = -(dy) * viewer_distance/1000;
+
+ double my = 0;
+ if (event->buttons() & Qt::MiddleButton) {
+ my = mz;
+ mz = 0;
+ // actually lock the x-position
+ // (turns out to be easier to use than xy panning)
+ mx = 0;
+ }
Matrix3d aax, aay, aaz, tm3;
aax = Eigen::AngleAxisd(-(object_rot_x/180) * M_PI, Vector3d::UnitX());
@@ -612,15 +622,10 @@ void GLView::mouseMoveEvent(QMouseEvent *event)
Matrix4d vec;
vec <<
0, 0, 0, mx,
- 0, 0, 0, 0,
0, 0, 0, my,
+ 0, 0, 0, mz,
0, 0, 0, 1
;
- if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) {
- vec(0,3) = 0;
- vec(1,3) = my;
- vec(2,3) = 0;
- }
tm = tm * vec;
object_trans_x += tm(0,3);
object_trans_y += tm(1,3);
diff --git a/src/lexer.l b/src/lexer.l
index 5644ded..188046f 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -30,6 +30,7 @@
#include "printutils.h"
#include "parsersettings.h"
#include "parser_yacc.h"
+#include "module.h"
#include <assert.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
@@ -52,7 +53,8 @@ int lexerget_lineno(void);
static void yyunput(int, char*) __attribute__((unused));
#endif
extern const char *parser_input_buffer;
-extern const char *parser_source_path;
+extern std::string parser_source_path;
+extern Module *currmodule;
#define YY_INPUT(buf,result,max_size) { \
if (yyin && yyin != stdin) { \
@@ -119,9 +121,12 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
usepath = boosty::absolute(fs::path(get_librarydir()) / filename);
}
}
- handle_dep(usepath.string());
- parserlval.text = strdup(usepath.string().c_str());
- return TOK_USE;
+ /* Only accept regular files which exists */
+ if (usepath.has_parent_path() && fs::exists(usepath)) {
+ handle_dep(usepath.string());
+ parserlval.text = strdup(usepath.string().c_str());
+ return TOK_USE;
+ }
}
}
@@ -215,8 +220,10 @@ void includefile()
filepath.clear();
path_stack.push_back(finfo.parent_path());
- handle_dep(boosty::absolute(finfo).string());
- yyin = fopen(boosty::absolute(finfo).string().c_str(), "r");
+ std::string fullname = boosty::absolute(finfo).string();
+ handle_dep(fullname);
+ currmodule->registerInclude(fullname);
+ yyin = fopen(fullname.c_str(), "r");
if (!yyin) {
PRINTB("WARNING: Can't open input file '%s'.", filename);
path_stack.pop_back();
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 2fd75c9..087cb30 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -25,6 +25,7 @@
*/
#include "PolySetCache.h"
+#include "ModuleCache.h"
#include "MainWindow.h"
#include "openscad.h" // examplesdir
#include "parsersettings.h"
@@ -77,10 +78,10 @@
#include <fstream>
#include <algorithm>
+#include <boost/version.hpp>
#include <boost/foreach.hpp>
-#include <boost/lambda/lambda.hpp>
-#include <boost/lambda/bind.hpp>
-using namespace boost::lambda;
+#include <boost/version.hpp>
+#include <sys/stat.h>
#ifdef ENABLE_CGAL
@@ -94,6 +95,10 @@ using namespace boost::lambda;
#endif // ENABLE_CGAL
+#ifndef OPENCSG_VERSION_STRING
+#define OPENCSG_VERSION_STRING "unknown, <1.3.2"
+#endif
+
// Global application state
unsigned int GuiLocker::gui_locked = 0;
@@ -199,8 +204,8 @@ MainWindow::MainWindow(const QString &filename)
autoReloadTimer->setSingleShot(false);
connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload()));
- connect(e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
- connect(e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
+ connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
+ connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
animate_panel->hide();
@@ -323,7 +328,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout()));
connect(this->helpActionHomepage, SIGNAL(triggered()), this, SLOT(helpHomepage()));
connect(this->helpActionManual, SIGNAL(triggered()), this, SLOT(helpManual()));
- connect(this->helpActionOpenGLInfo, SIGNAL(triggered()), this, SLOT(helpOpenGL()));
+ connect(this->helpActionLibraryInfo, SIGNAL(triggered()), this, SLOT(helpLibrary()));
setCurrentOutput();
@@ -488,7 +493,7 @@ MainWindow::openFile(const QString &new_filename)
#endif
setFileName(new_filename);
- load();
+ refreshDocument();
updateRecentFiles();
}
@@ -544,11 +549,11 @@ void MainWindow::updateRecentFiles()
void MainWindow::updatedFps()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->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->setInterval(int(1000 / this->e_fps->text().toDouble()));
animate_timer->start();
}
}
@@ -556,27 +561,28 @@ void MainWindow::updatedFps()
void MainWindow::updateTVal()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->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;
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble() + 1/s;
QString txt;
txt.sprintf("%.5f", t >= 1.0 ? 0.0 : t);
- e_tval->setText(txt);
+ this->e_tval->setText(txt);
}
}
}
-void MainWindow::load()
+void MainWindow::refreshDocument()
{
setCurrentOutput();
if (!this->fileName.isEmpty()) {
QFile file(this->fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTB("Failed to open file: %s (%s)", this->fileName.toStdString() % file.errorString().toStdString());
+ PRINTB("Failed to open file %s: %s",
+ this->fileName.toStdString() % file.errorString().toStdString());
}
else {
QString text = QTextStream(&file).readAll();
@@ -598,42 +604,30 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
/*!
Parse and evaluate the design => this->root_node
+
+ Returns true if something was compiled, false if nothing was changed
+ and the root_node was left untouched.
*/
-void MainWindow::compile(bool procevents)
+bool MainWindow::compile(bool reload, bool procevents)
{
- PRINT("Parsing design (AST generation)...");
- if (procevents)
- QApplication::processEvents();
+ if (!compileTopLevelDocument(reload)) return false;
// Invalidate renderers before we kill the CSG tree
this->glview->setRenderer(NULL);
- if (this->opencsgRenderer) {
- delete this->opencsgRenderer;
- this->opencsgRenderer = NULL;
- }
- if (this->thrownTogetherRenderer) {
- delete this->thrownTogetherRenderer;
- this->thrownTogetherRenderer = NULL;
- }
+ delete this->opencsgRenderer;
+ this->opencsgRenderer = NULL;
+ delete this->thrownTogetherRenderer;
+ this->thrownTogetherRenderer = NULL;
// Remove previous CSG tree
- if (this->root_module) {
- delete this->root_module;
- this->root_module = NULL;
- }
-
- if (this->absolute_root_node) {
- delete this->absolute_root_node;
- this->absolute_root_node = NULL;
- }
+ delete this->absolute_root_node;
+ this->absolute_root_node = NULL;
this->root_raw_term.reset();
this->root_norm_term.reset();
- if (this->root_chain) {
- delete this->root_chain;
- this->root_chain = NULL;
- }
+ delete this->root_chain;
+ this->root_chain = NULL;
this->highlight_terms.clear();
delete this->highlights_chain;
@@ -646,96 +640,43 @@ void MainWindow::compile(bool procevents)
this->root_node = NULL;
this->tree.setRoot(NULL);
- // Initialize special variables
- this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));
-
- Value vpt;
- vpt.type = Value::VECTOR;
- vpt.append(new Value(-this->glview->object_trans_x));
- vpt.append(new Value(-this->glview->object_trans_y));
- vpt.append(new Value(-this->glview->object_trans_z));
- this->root_ctx.set_variable("$vpt", vpt);
-
- Value vpr;
- vpr.type = Value::VECTOR;
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
- root_ctx.set_variable("$vpr", vpr);
-
- // Parse
- this->last_compiled_doc = editor->toPlainText();
- this->root_module = parse((this->last_compiled_doc + "\n" +
- QString::fromStdString(commandline_commands)).toAscii().data(),
- this->fileName.isEmpty() ?
- "" :
- QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
- false);
-
- // Error highlighting
- if (this->highlighter) {
- delete this->highlighter;
- this->highlighter = NULL;
- }
- if (parser_error_pos >= 0) {
- this->highlighter = new Highlighter(editor->document());
- }
-
- if (!this->root_module) {
- if (!animate_panel->isVisible()) {
-#ifdef _QCODE_EDIT_
- QDocumentCursor cursor = editor->cursor();
- cursor.setPosition(parser_error_pos);
-#else
- QTextCursor cursor = editor->textCursor();
- cursor.setPosition(parser_error_pos);
- editor->setTextCursor(cursor);
-#endif
+ if (this->root_module) {
+ // Evaluate CSG tree
+ PRINT("Compiling design (CSG Tree generation)...");
+ if (procevents) QApplication::processEvents();
+
+ AbstractNode::resetIndexCounter();
+ this->root_inst = ModuleInstantiation();
+ this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
+
+ if (this->absolute_root_node) {
+ // Do we have an explicit root node (! modifier)?
+ if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
+ this->root_node = this->absolute_root_node;
+ }
+ // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
+ this->tree.setRoot(this->root_node);
+ // Dump the tree (to initialize caches).
+ // FIXME: We shouldn't really need to do this explicitly..
+ this->tree.getString(*this->root_node);
}
- goto fail;
}
- // Evaluate CSG tree
- PRINT("Compiling design (CSG Tree generation)...");
- if (procevents)
- QApplication::processEvents();
-
- AbstractNode::resetIndexCounter();
- this->root_inst = ModuleInstantiation();
- this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
-
- if (!this->absolute_root_node)
- goto fail;
-
- // Do we have an explicit root node (! modifier)?
- if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
- this->root_node = this->absolute_root_node;
- }
- // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
- this->tree.setRoot(this->root_node);
- // Dump the tree (to initialize caches).
- // FIXME: We shouldn't really need to do this explicitly..
- this->tree.getString(*this->root_node);
-
- if (1) {
- PRINT("Compilation finished.");
- if (procevents)
- QApplication::processEvents();
- } else {
-fail:
+ if (!this->root_node) {
if (parser_error_pos < 0) {
PRINT("ERROR: Compilation failed! (no top level object found)");
} else {
PRINT("ERROR: Compilation failed!");
}
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
}
+
+ return true;
}
/*!
Generates CSG tree for OpenCSG evaluation.
- Assumes that the design has been parsed and evaluated
+ Assumes that the design has been parsed and evaluated (this->root_node is set)
*/
void MainWindow::compileCSG(bool procevents)
{
@@ -991,7 +932,7 @@ void MainWindow::actionSaveAs()
void MainWindow::actionReload()
{
- if (checkModified()) load();
+ if (checkEditorModified()) refreshDocument();
}
void MainWindow::hideEditor()
@@ -1031,16 +972,127 @@ void MainWindow::pasteViewportRotation()
cursor.insertText(txt);
}
-void MainWindow::checkAutoReload()
+void MainWindow::updateTemporalVariables()
+{
+ this->root_ctx.set_variable("$t", Value(this->e_tval->text().toDouble()));
+
+ Value vpt;
+ vpt.type = Value::VECTOR;
+ vpt.append(new Value(-this->glview->object_trans_x));
+ vpt.append(new Value(-this->glview->object_trans_y));
+ vpt.append(new Value(-this->glview->object_trans_z));
+ this->root_ctx.set_variable("$vpt", vpt);
+
+ Value vpr;
+ vpr.type = Value::VECTOR;
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
+ root_ctx.set_variable("$vpr", vpr);
+}
+
+bool MainWindow::fileChangedOnDisk()
{
if (!this->fileName.isEmpty()) {
- QString new_stinfo;
- QFileInfo finfo(this->fileName);
- new_stinfo = QString::number(finfo.size()) + QString::number(finfo.lastModified().toTime_t());
- if (new_stinfo != autoReloadInfo)
- actionReloadCompile();
- autoReloadInfo = new_stinfo;
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(this->fileName.toLocal8Bit(), &st);
+ std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ if (newid != this->autoReloadId) {
+ this->autoReloadId = newid;
+ return true;
+ }
+ }
+ return false;
+}
+
+// FIXME: The following two methods are duplicated in ModuleCache.cc - refactor
+static bool is_modified(const std::string &filename, const time_t &mtime)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ return (st.st_mtime > mtime);
+}
+
+bool MainWindow::includesChanged()
+{
+ if (this->root_module) {
+ BOOST_FOREACH(const Module::IncludeContainer::value_type &item, this->root_module->includes) {
+ if (is_modified(item.first, item.second)) return true;
+ }
}
+ return false;
+}
+
+/*!
+ If reload is true, does a timestamp check on the document and tries to reload it.
+ Otherwise, just reparses the current document and any dependencies, updates the
+ GUI accordingly and populates this->root_module.
+
+ Returns true if anything was compiled.
+*/
+bool MainWindow::compileTopLevelDocument(bool reload)
+{
+ bool shouldcompiletoplevel = !reload;
+
+ if ((reload && fileChangedOnDisk() && checkEditorModified()) ||
+ includesChanged()) {
+ shouldcompiletoplevel = true;
+ refreshDocument();
+ }
+
+ if (shouldcompiletoplevel) {
+ console->clear();
+
+ updateTemporalVariables();
+
+ this->last_compiled_doc = editor->toPlainText();
+ std::string fulltext =
+ this->last_compiled_doc.toStdString() + "\n" + commandline_commands;
+
+ delete this->root_module;
+ this->root_module = NULL;
+
+ this->root_module = parse(fulltext.c_str(),
+ this->fileName.isEmpty() ?
+ "" :
+ QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
+ false);
+
+ // Error highlighting
+ delete this->highlighter;
+ this->highlighter = NULL;
+
+ if (!this->root_module) {
+ this->highlighter = new Highlighter(editor->document());
+
+ if (!animate_panel->isVisible()) {
+#ifdef _QCODE_EDIT_
+ QDocumentCursor cursor = editor->cursor();
+ cursor.setPosition(parser_error_pos);
+#else
+ QTextCursor cursor = editor->textCursor();
+ cursor.setPosition(parser_error_pos);
+ editor->setTextCursor(cursor);
+#endif
+ }
+ }
+ }
+
+ bool changed = shouldcompiletoplevel;
+ if (this->root_module) {
+ changed |= this->root_module->handleDependencies();
+ if (changed) PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
+ }
+
+ return changed;
+}
+
+void MainWindow::checkAutoReload()
+{
+ if (!this->fileName.isEmpty()) actionReloadCompile();
}
void MainWindow::autoReloadSet(bool on)
@@ -1048,14 +1100,14 @@ void MainWindow::autoReloadSet(bool on)
QSettings settings;
settings.setValue("design/autoReload",designActionAutoReload->isChecked());
if (on) {
- autoReloadInfo = QString();
+ autoReloadId = "";
autoReloadTimer->start(200);
} else {
autoReloadTimer->stop();
}
}
-bool MainWindow::checkModified()
+bool MainWindow::checkEditorModified()
{
if (editor->isContentModified()) {
QMessageBox::StandardButton ret;
@@ -1075,15 +1127,11 @@ void MainWindow::actionReloadCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
- if (!checkModified()) return;
-
- console->clear();
-
- load();
-
setCurrentOutput();
- compile(true);
+
+ // PRINT("Parsing design (AST generation)...");
+ // QApplication::processEvents();
+ if (!compile(true, true)) return;
if (this->root_node) compileCSG(true);
// Go to non-CGAL view mode
@@ -1105,11 +1153,12 @@ void MainWindow::actionCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
setCurrentOutput();
console->clear();
- compile(!viewActionAnimate->isChecked());
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, !viewActionAnimate->isChecked());
if (this->root_node) compileCSG(!viewActionAnimate->isChecked());
// Go to non-CGAL view mode
@@ -1127,8 +1176,8 @@ void MainWindow::actionCompile()
if (viewActionAnimate->isChecked() && e_dump->isChecked()) {
QImage img = this->glview->grabFrameBuffer();
QString filename;
- double s = e_fsteps->text().toDouble();
- double t = e_tval->text().toDouble();
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble();
filename.sprintf("frame%05d.png", int(round(s*t)));
img.save(filename, "PNG");
}
@@ -1146,7 +1195,9 @@ void MainWindow::actionRenderCGAL()
setCurrentOutput();
console->clear();
- compile(true);
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, true);
if (!this->root_module || !this->root_node) {
return;
@@ -1449,7 +1500,7 @@ void MainWindow::actionFlushCaches()
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();
- Module::clear_library_cache();
+ ModuleCache::instance()->clear();
}
void MainWindow::viewModeActionsUncheck()
@@ -1556,7 +1607,7 @@ void MainWindow::animateUpdate()
{
if (animate_panel->isVisible()) {
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok && fps <= 0 && !animate_timer->isActive()) {
animate_timer->stop();
animate_timer->setSingleShot(true);
@@ -1699,15 +1750,28 @@ MainWindow::helpManual()
QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual"));
}
-void MainWindow::helpOpenGL()
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+void MainWindow::helpLibrary()
{
+ QString libinfo;
+ libinfo.sprintf("Boost version: %s\n"
+ "Eigen version: %d.%d.%d\n"
+ "CGAL version: %s\n"
+ "OpenCSG version: %s\n\n",
+ BOOST_LIB_VERSION,
+ EIGEN_WORLD_VERSION, EIGEN_MAJOR_VERSION, EIGEN_MINOR_VERSION,
+ TOSTRING(CGAL_VERSION),
+ OPENCSG_VERSION_STRING);
+
if (!this->openglbox) {
this->openglbox = new QMessageBox(QMessageBox::Information,
- "OpenGL Info", "Detailed OpenGL Info",
+ "OpenGL Info", "Detailed Library Info",
QMessageBox::Ok, this);
}
- this->openglbox->setDetailedText(this->glview->getRendererInfo());
+
+ this->openglbox->setDetailedText(libinfo + this->glview->getRendererInfo());
this->openglbox->show();
}
diff --git a/src/module.cc b/src/module.cc
index 6641ff7..fc849ff 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -25,13 +25,16 @@
*/
#include "module.h"
+#include "ModuleCache.h"
#include "node.h"
#include "context.h"
#include "expression.h"
#include "function.h"
#include "printutils.h"
+
#include <boost/foreach.hpp>
#include <sstream>
+#include <sys/stat.h>
AbstractModule::~AbstractModule()
{
@@ -201,7 +204,37 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
return dump.str();
}
-void Module::clear_library_cache()
+void Module::registerInclude(const std::string &filename)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ this->includes[filename] = st.st_mtime;
+}
+
+/*!
+ Check if any dependencies have been modified and recompile them.
+ Returns true if anything was recompiled.
+*/
+bool Module::handleDependencies()
{
- Module::libs_cache.clear();
+ bool changed = false;
+ // Iterating manually since we want to modify the container while iterating
+ Module::ModuleContainer::iterator iter = this->usedlibs.begin();
+ while (iter != this->usedlibs.end()) {
+ Module::ModuleContainer::iterator curr = iter++;
+ Module *oldmodule = curr->second;
+ curr->second = ModuleCache::instance()->evaluate(curr->first);
+ if (curr->second != oldmodule) {
+ changed = true;
+#ifdef DEBUG
+ PRINTB_NOCACHE(" %s: %p", curr->first % curr->second);
+#endif
+ }
+ if (!curr->second) {
+ PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", curr->first);
+ this->usedlibs.erase(curr);
+ }
+ }
+ return changed;
}
diff --git a/src/module.h b/src/module.h
index 6c6529b..cd25287 100644
--- a/src/module.h
+++ b/src/module.h
@@ -3,6 +3,7 @@
#include <string>
#include <vector>
+#include <list>
#include <boost/unordered_map.hpp>
#include "value.h"
@@ -65,11 +66,12 @@ public:
void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); }
- static Module *compile_library(const std::string &filename);
- static void clear_library_cache();
-
typedef boost::unordered_map<std::string, class Module*> ModuleContainer;
ModuleContainer usedlibs;
+ void registerInclude(const std::string &filename);
+ typedef boost::unordered_map<std::string, time_t> IncludeContainer;
+ IncludeContainer includes;
+ bool handleDependencies();
std::vector<std::string> assignments_var;
std::vector<Expression*> assignments_expr;
@@ -87,11 +89,6 @@ public:
protected:
private:
- struct libs_cache_ent {
- Module *mod;
- std::string cache_id, msg;
- };
- static boost::unordered_map<std::string, libs_cache_ent> libs_cache;
};
#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 980e2af..7fe054f 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -149,9 +149,14 @@ int main(int argc, char **argv)
all_options.add(desc).add(hidden);
po::variables_map vm;
- po::store(po::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
-// po::notify(vm);
-
+ try {
+ po::store(po::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
+ }
+ catch(std::exception &e) { // Catches e.g. unknown options
+ fprintf(stderr, "%s\n", e.what());
+ help(argv[0]);
+ }
+
if (vm.count("help")) help(argv[0]);
if (vm.count("version")) version();
@@ -255,34 +260,28 @@ int main(int argc, char **argv)
Context root_ctx;
register_builtin(root_ctx);
- AbstractModule *root_module;
+ Module *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);
+
+ std::ifstream ifs(filename);
+ if (!ifs.is_open()) {
+ fprintf(stderr, "Can't open input file '%s'!\n", filename);
exit(1);
- } else {
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
- text << "\n" << commandline_commands;
- fs::path abspath = boosty::absolute( filename );
- std::string fname = boosty::stringy( abspath );
- root_module = parse(text.str().c_str(), fname.c_str(), false);
- if (!root_module) exit(1);
}
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+ text += "\n" + commandline_commands;
+ fs::path abspath = boosty::absolute(filename);
+ std::string parentpath = boosty::stringy(abspath.parent_path());
+ root_module = parse(text.c_str(), parentpath.c_str(), false);
+ if (!root_module) exit(1);
+ root_module->handleDependencies();
- fs::path fpath = boosty::absolute( fs::path(filename) );
+ fs::path fpath = boosty::absolute(fs::path(filename));
fs::path fparent = fpath.parent_path();
- fs::current_path( fparent );
+ fs::current_path(fparent);
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/src/openscad.h b/src/openscad.h
index dab14cd..8b49ba2 100644
--- a/src/openscad.h
+++ b/src/openscad.h
@@ -27,7 +27,7 @@
#ifndef OPENSCAD_H
#define OPENSCAD_H
-extern class AbstractModule *parse(const char *text, const char *path, int debug);
+extern class Module *parse(const char *text, const char *path, int debug);
extern int get_fragments_from_r(double r, double fn, double fs, double fa);
#include <string>
diff --git a/src/parser.y b/src/parser.y
index 15a754b..195e7a8 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -43,7 +43,7 @@
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
#include "boosty.h"
int parser_error_pos = -1;
@@ -56,7 +56,7 @@ int lexerlex_destroy(void);
int lexerlex(void);
std::vector<Module*> module_stack;
-Module *module;
+Module *currmodule;
class ArgContainer {
public:
@@ -133,7 +133,7 @@ public:
input:
/* empty */ |
- TOK_USE { module->usedlibs[$1] = NULL; } input |
+ TOK_USE { currmodule->usedlibs[$1] = NULL; } input |
statement input ;
inner_input:
@@ -145,37 +145,37 @@ statement:
'{' inner_input '}' |
module_instantiation {
if ($1) {
- module->addChild($1);
+ currmodule->addChild($1);
} else {
delete $1;
}
} |
TOK_ID '=' expr ';' {
bool add_new_assignment = true;
- for (size_t i = 0; i < module->assignments_var.size(); i++) {
- if (module->assignments_var[i] != $1)
+ for (size_t i = 0; i < currmodule->assignments_var.size(); i++) {
+ if (currmodule->assignments_var[i] != $1)
continue;
- delete module->assignments_expr[i];
- module->assignments_expr[i] = $3;
+ delete currmodule->assignments_expr[i];
+ currmodule->assignments_expr[i] = $3;
add_new_assignment = false;
}
if (add_new_assignment) {
- module->assignments_var.push_back($1);
- module->assignments_expr.push_back($3);
+ currmodule->assignments_var.push_back($1);
+ currmodule->assignments_expr.push_back($3);
free($1);
}
} |
TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' {
- Module *p = module;
- module_stack.push_back(module);
- module = new Module();
- p->modules[$2] = module;
- module->argnames = $4->argnames;
- module->argexpr = $4->argexpr;
+ Module *p = currmodule;
+ module_stack.push_back(currmodule);
+ currmodule = new Module();
+ p->modules[$2] = currmodule;
+ currmodule->argnames = $4->argnames;
+ currmodule->argexpr = $4->argexpr;
free($2);
delete $4;
} statement {
- module = module_stack.back();
+ currmodule = module_stack.back();
module_stack.pop_back();
} |
TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr {
@@ -183,7 +183,7 @@ statement:
func->argnames = $4->argnames;
func->argexpr = $4->argexpr;
func->expr = $8;
- module->functions[$2] = func;
+ currmodule->functions[$2] = func;
free($2);
delete $4;
} ';' ;
@@ -560,101 +560,33 @@ void yyerror (char const *s)
{
// FIXME: We leak memory on parser errors...
PRINTB("Parser error in line %d: %s\n", lexerget_lineno() % s);
- module = NULL;
+ currmodule = NULL;
}
extern void lexerdestroy();
extern FILE *lexerin;
extern const char *parser_input_buffer;
const char *parser_input_buffer;
-const char *parser_source_path;
+std::string parser_source_path;
-AbstractModule *parse(const char *text, const char *path, int debug)
+Module *parse(const char *text, const char *path, int debug)
{
lexerin = NULL;
parser_error_pos = -1;
parser_input_buffer = text;
- parser_source_path = path;
+ parser_source_path = std::string(path);
module_stack.clear();
- module = new Module();
+ Module *rootmodule = currmodule = new Module();
+ // PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule);
parserdebug = debug;
parserparse();
lexerdestroy();
lexerlex_destroy();
- if (!module)
- return NULL;
-
- // Iterating manually since we want to modify the container while iterating
- Module::ModuleContainer::iterator iter = module->usedlibs.begin();
- while (iter != module->usedlibs.end()) {
- Module::ModuleContainer::iterator curr = iter++;
- curr->second = Module::compile_library(curr->first);
- if (!curr->second) {
- PRINTB("WARNING: Failed to compile library '%s'.", curr->first);
- module->usedlibs.erase(curr);
- }
- }
+ if (!rootmodule) return NULL;
parser_error_pos = -1;
- return module;
-}
-
-boost::unordered_map<std::string, Module::libs_cache_ent> Module::libs_cache;
-
-Module *Module::compile_library(const std::string &filename)
-{
- struct stat st;
- memset(&st, 0, sizeof(struct stat));
- stat(filename.c_str(), &st);
-
- std::stringstream idstream;
- idstream << std::hex << st.st_mtime << "." << st.st_size;
- std::string cache_id = idstream.str();
-
- if (libs_cache.find(filename) != libs_cache.end() && libs_cache[filename].cache_id == cache_id) {
- PRINTB("%s", libs_cache[filename].msg);
- return &(*libs_cache[filename].mod);
- }
-
- FILE *fp = fopen(filename.c_str(), "rt");
- if (!fp) {
- fprintf(stderr, "WARNING: Can't open library file '%s'\n", filename.c_str());
- return NULL;
- }
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
-
- print_messages_push();
-
- PRINTB("Compiling library '%s'.", filename);
- libs_cache_ent e = { NULL, cache_id, std::string("WARNING: Library `") + filename + "' tries to recursively use itself!" };
- if (libs_cache.find(filename) != libs_cache.end())
- delete libs_cache[filename].mod;
- libs_cache[filename] = e;
-
- Module *backup_mod = module;
- std::string pathname = boosty::stringy( fs::path(filename).parent_path() );
- Module *lib_mod = dynamic_cast<Module*>(parse(text.str().c_str(), pathname.c_str(), 0));
- module = backup_mod;
-
- if (lib_mod) {
- libs_cache[filename].mod = lib_mod;
- libs_cache[filename].msg = print_messages_stack.back();
- } else {
- libs_cache.erase(filename);
- }
-
- print_messages_pop();
-
- return lib_mod;
+ return rootmodule;
}
-
diff --git a/src/primitives.cc b/src/primitives.cc
index feaa1a4..ce52550 100644
--- a/src/primitives.cc
+++ b/src/primitives.cc
@@ -530,7 +530,7 @@ sphere_next_r2:
{
dd.paths.push_back(DxfData::Path());
for (size_t j=0; j<this->paths.vec[i]->vec.size(); j++) {
- int idx = this->paths.vec[i]->vec[j]->num;
+ unsigned int idx = this->paths.vec[i]->vec[j]->num;
if (idx < dd.points.size()) {
dd.paths.back().indices.push_back(idx);
}
diff --git a/src/system-gl.h b/src/system-gl.h
index 0377b72..d7de3c6 100644
--- a/src/system-gl.h
+++ b/src/system-gl.h
@@ -7,6 +7,7 @@
#include <OpenGL/OpenGL.h>
#else
#include <GL/gl.h>
+ #include <GL/glu.h>
#ifdef _WIN32
#include <windows.h> // For the CALLBACK macro
#endif
diff --git a/src/transform.cc b/src/transform.cc
index f5038b1..c2ac194 100644
--- a/src/transform.cc
+++ b/src/transform.cc
@@ -87,11 +87,10 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
{
Vector3d scalevec(1,1,1);
Value v = c.lookup_variable("v");
- v.getnum(scalevec[0]);
- v.getnum(scalevec[1]);
- v.getnum(scalevec[2]);
- v.getv3(scalevec[0], scalevec[1], scalevec[2]);
- if (scalevec[2] == 0) scalevec[2] = 1;
+ if (!v.getv3(scalevec[0], scalevec[1], scalevec[2], 1.0)) {
+ double num;
+ if (v.getnum(num)) scalevec.setConstant(num);
+ }
node->matrix.scale(scalevec);
}
else if (this->type == ROTATE)
diff --git a/src/value.cc b/src/value.cc
index 47fac1e..93c4d5e 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -30,6 +30,7 @@
#include <sstream>
#include <QDir>
#include <boost/foreach.hpp>
+#include "printutils.h"
Value::Value()
{
@@ -157,6 +158,64 @@ Value Value::operator * (const Value &v) const
if (this->type == NUMBER && v.type == NUMBER) {
return Value(this->num * v.num);
}
+ if (this->type == VECTOR && v.type == VECTOR && this->vec.size() == v.vec.size() ) {
+ if ( this->vec[0]->type == NUMBER && v.vec[0]->type == NUMBER ) {
+ // Vector dot product.
+ double r=0.0;
+ for (size_t i=0; i <this->vec.size(); i++) {
+ if ( this->vec[i]->type != NUMBER || v.vec[i]->type != NUMBER ) return Value();
+ r = r + (this->vec[i]->num * v.vec[i]->num);
+ }
+ return Value(r);
+ } else if ( this->vec[0]->type == VECTOR && v.vec[0]->type == NUMBER ) {
+ // Matrix * Vector
+ Value r;
+ r.type = VECTOR;
+ for ( size_t i=0; i < this->vec.size(); i++) {
+ double r_e=0.0;
+ if ( this->vec[i]->vec.size() != v.vec.size() ) return Value();
+ for ( size_t j=0; j < this->vec[i]->vec.size(); j++) {
+ if ( this->vec[i]->vec[j]->type != NUMBER || v.vec[i]->type != NUMBER ) return Value();
+ r_e = r_e + (this->vec[i]->vec[j]->num * v.vec[j]->num);
+ }
+ r.vec.push_back(new Value(r_e));
+ }
+ return r;
+ } else if (this->vec[0]->type == NUMBER && v.vec[0]->type == VECTOR ) {
+ // Vector * Matrix
+ Value r;
+ r.type = VECTOR;
+ for ( size_t i=0; i < v.vec[0]->vec.size(); i++) {
+ double r_e=0.0;
+ for ( size_t j=0; j < v.vec.size(); j++) {
+ if ( v.vec[j]->vec.size() != v.vec[0]->vec.size() ) return Value();
+ if ( this->vec[j]->type != NUMBER || v.vec[j]->vec[i]->type != NUMBER ) return Value();
+ r_e = r_e + (this->vec[j]->num * v.vec[j]->vec[i]->num);
+ }
+ r.vec.push_back(new Value(r_e));
+ }
+ return r;
+ }
+ }
+ if (this->type == VECTOR && v.type == VECTOR && this->vec[0]->type == VECTOR && v.vec[0]->type == VECTOR && this->vec[0]->vec.size() == v.vec.size() ) {
+ // Matrix * Matrix
+ Value rrow;
+ rrow.type = VECTOR;
+ for ( size_t i=0; i < this->vec.size(); i++ ) {
+ Value * rcol=new Value();
+ rcol->type = VECTOR;
+ for ( size_t j=0; j < this->vec.size(); j++ ) {
+ double r_e=0.0;
+ for ( size_t k=0; k < v.vec.size(); k++ ) {
+ r_e = r_e + (this->vec[i]->vec[k]->num * v.vec[k]->vec[j]->num);
+ }
+ // PRINTB(" r_e = %s",r_e);
+ rcol->vec.push_back(new Value(r_e));
+ }
+ rrow.vec.push_back(rcol);
+ }
+ return rrow;
+ }
return Value();
}
@@ -300,11 +359,11 @@ bool Value::getv2(double &x, double &y) const
return true;
}
-bool Value::getv3(double &x, double &y, double &z) const
+bool Value::getv3(double &x, double &y, double &z, double defaultval) const
{
if (this->type == VECTOR && this->vec.size() == 2) {
if (getv2(x, y)) {
- z = 0;
+ z = defaultval;
return true;
}
return false;
@@ -367,6 +426,10 @@ std::string Value::toString() const
// Quick and dirty hack to work around floating point rounding differences
// across platforms for testing purposes.
{
+ if (this->num != this->num) { // Fix for avoiding nan vs. -nan across platforms
+ stream << "nan";
+ break;
+ }
std::stringstream tmp;
tmp.precision(12);
tmp.setf(std::ios_base::fixed);
diff --git a/src/value.h b/src/value.h
index a2cfbdf..4a67fbc 100644
--- a/src/value.h
+++ b/src/value.h
@@ -73,7 +73,7 @@ public:
bool getnum(double &v) const;
bool getv2(double &x, double &y) const;
- bool getv3(double &x, double &y, double &z) const;
+ bool getv3(double &x, double &y, double &z, double defaultval = 0.0) const;
std::string toString() const;
contact: Jan Huwald // Impressum