From 32469374cfe2985f936f017a204d4e9d7dcd3e2f Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 22 Jan 2013 17:26:03 -0500 Subject: [PATCH 01/20] Try the CGAL EPEC kernel for 3D operations --- src/PolySetCGALEvaluator.cc | 3 ++- src/cgal.h | 5 +++-- src/svg.cc | 40 ++++++++++++++++++------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index 224e657c..be8ecc03 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -86,7 +86,8 @@ public: std::vector contour; CGAL_For_all( c1, cend ) { CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); - CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() ); + CGAL_Nef_polyhedron2::Explorer::Point point2d(CGAL::to_double(point3d.x()), + CGAL::to_double(point3d.y())); contour.push_back( point2d ); } diff --git a/src/cgal.h b/src/cgal.h index a7300c63..366dbfff 100644 --- a/src/cgal.h +++ b/src/cgal.h @@ -48,7 +48,8 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2; typedef CGAL::Polygon_2 CGAL_Poly2; typedef CGAL::Polygon_with_holes_2 CGAL_Poly2h; -typedef CGAL::Cartesian CGAL_Kernel3; + //typedef CGAL::Cartesian CGAL_Kernel3; +typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3; typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3; typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; @@ -63,7 +64,7 @@ typedef CGAL::Iso_cuboid_3 CGAL_Iso_cuboid_3; // CGAL_Nef_polyhedron2::Explorer::Point which is different than // CGAL_Kernel2::Point. Hence the suffix 'e' typedef CGAL_Nef_polyhedron2::Explorer::Point CGAL_Point_2e; -typedef CGAL::Iso_rectangle_2< CGAL::Simple_cartesian > CGAL_Iso_rectangle_2e; +typedef CGAL::Iso_rectangle_2 > CGAL_Iso_rectangle_2e; #ifdef PREV_NDEBUG diff --git a/src/svg.cc b/src/svg.cc index e5130b0b..a5d5ac23 100644 --- a/src/svg.cc +++ b/src/svg.cc @@ -49,29 +49,29 @@ std::string svg_axes() return out.str(); } -CGAL_Point_2e project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox ) +CGAL_Nef_polyhedron2::Explorer::Point project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox ) { - NT screenw(svg_px_width); - NT screenh(svg_px_height); - NT screenxc = screenw / 2; - NT screenyc = screenh / 2; - NT bboxx = ( bbox.xmax() - bbox.xmin() ); - NT bboxy = ( bbox.ymax() - bbox.ymin() ); - NT bboxz = ( bbox.zmax() - bbox.zmin() ); - NT largest_dim = CGAL::max( CGAL::max( bboxx, bboxy ), bboxz ); - NT bboxxc = bboxx / 2 + bbox.xmin(); - NT bboxyc = bboxy / 2 + bbox.ymin(); - NT bboxzc = bboxz / 2 + bbox.zmin(); - NT xinbox = ( p.x() - bboxxc ) / largest_dim; - NT yinbox = ( p.y() - bboxyc ) / largest_dim; - NT zinbox = ( p.z() - bboxzc ) / largest_dim; + CGAL_Kernel3::FT screenw(svg_px_width); + CGAL_Kernel3::FT screenh(svg_px_height); + CGAL_Kernel3::FT screenxc = screenw / 2; + CGAL_Kernel3::FT screenyc = screenh / 2; + CGAL_Kernel3::FT bboxx = ( bbox.xmax() - bbox.xmin() ); + CGAL_Kernel3::FT bboxy = ( bbox.ymax() - bbox.ymin() ); + CGAL_Kernel3::FT bboxz = ( bbox.zmax() - bbox.zmin() ); + CGAL_Kernel3::FT largest_dim = CGAL::max( CGAL::max( bboxx, bboxy ), bboxz ); + CGAL_Kernel3::FT bboxxc = bboxx / 2 + bbox.xmin(); + CGAL_Kernel3::FT bboxyc = bboxy / 2 + bbox.ymin(); + CGAL_Kernel3::FT bboxzc = bboxz / 2 + bbox.zmin(); + CGAL_Kernel3::FT xinbox = ( p.x() - bboxxc ) / largest_dim; + CGAL_Kernel3::FT yinbox = ( p.y() - bboxyc ) / largest_dim; + CGAL_Kernel3::FT zinbox = ( p.z() - bboxzc ) / largest_dim; // do simple fake paralell projection - NT tx = screenxc + xinbox * screenw / 1.618 + yinbox * screenh / 3.2; - NT ty = screenyc - zinbox * screenh / 1.618 - yinbox * screenh / 3.2; - return CGAL_Point_2e( tx, ty ); + CGAL_Kernel3::FT tx = screenxc + xinbox * screenw / 1.618 + yinbox * screenh / 3.2; + CGAL_Kernel3::FT ty = screenyc - zinbox * screenh / 1.618 - yinbox * screenh / 3.2; + return CGAL_Point_2e(CGAL::to_double(tx), CGAL::to_double(ty)); } -CGAL_Point_2e project_svg_2to2( CGAL_Point_2e p, CGAL_Iso_rectangle_2e bbox ) +CGAL_Point_2e project_svg_2to2(const CGAL_Point_2e &p, const CGAL_Iso_rectangle_2e &bbox) { double screenw = svg_px_width; double screenh = svg_px_height; @@ -99,7 +99,7 @@ std::string dump_cgal_nef_polyhedron2_face_svg( std::stringstream out; CGAL_For_all(c1, c2) { if ( explorer.is_standard( explorer.target(c1) ) ) { - CGAL_Point_2e source = explorer.point( explorer.source( c1 ) ); +CGAL_Nef_polyhedron2::Explorer::Point source = explorer.point( explorer.source( c1 ) ); CGAL_Point_2e target = explorer.point( explorer.target( c1 ) ); CGAL_Point_2e tp1 = project_svg_2to2( source, bbox ); CGAL_Point_2e tp2 = project_svg_2to2( target, bbox ); From 95947a877b8e88521a7f00348d56c89e9b7c2a79 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 24 May 2013 13:58:52 -0400 Subject: [PATCH 02/20] Ported recent changes to the EPEC kernel --- src/CGALEvaluator.cc | 13 +++++++------ src/CGAL_Nef_polyhedron.h | 2 +- src/cgal.h | 7 ++++--- src/cgalutils.cc | 3 ++- src/cgalutils.h | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index d0140fa0..84176366 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -202,30 +202,31 @@ CGAL_Nef_polyhedron CGALEvaluator::applyResize(const CgaladvNode &node) if ( N.dim == 2 ) { CGAL_Iso_rectangle_2e bbox = bounding_box( *N.p2 ); CGAL_Point_2e min2(bbox.min()), max2(bbox.max()); - CGAL_Point_3 min3(min2.x(),min2.y(),0), max3(max2.x(),max2.y(),0); + CGAL_Point_3 min3(CGAL::to_double(min2.x()), CGAL::to_double(min2.y()), 0), + max3(CGAL::to_double(max2.x()), CGAL::to_double(max2.y()), 0); bb = CGAL_Iso_cuboid_3( min3, max3 ); } else { bb = bounding_box( *N.p3 ); } - std::vector scale, bbox_size; - for (int i=0;i<3;i++) scale.push_back( NT(1) ); + std::vector scale, bbox_size; + for (int i=0;i<3;i++) scale.push_back( NT3(1) ); bbox_size.push_back( bb.xmax()-bb.xmin() ); bbox_size.push_back( bb.ymax()-bb.ymin() ); bbox_size.push_back( bb.zmax()-bb.zmin() ); for (int i=0;i<3;i++) { if (node.newsize[i]) { - if (bbox_size[i]==NT(0)) { + if (bbox_size[i]==NT3(0)) { PRINT("WARNING: Cannot resize in direction normal to flat object"); return N; } else { - scale[i] = NT(node.newsize[i]) / bbox_size[i]; + scale[i] = NT3(node.newsize[i]) / bbox_size[i]; } } } - NT autoscale = std::max( scale[0], std::max( scale[1], scale[2] )); + NT3 autoscale = std::max( scale[0], std::max( scale[1], scale[2] )); for (int i=0;i<3;i++) { if (node.autosize[i]) scale[i] = autoscale; } diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h index cfab9933..7f59861e 100644 --- a/src/CGAL_Nef_polyhedron.h +++ b/src/CGAL_Nef_polyhedron.h @@ -1,7 +1,7 @@ #ifndef CGAL_NEF_POLYHEDRON_H_ #define CGAL_NEF_POLYHEDRON_H_ -#include "cgalfwd.h" +#include "cgal.h" #include "memory.h" #include #include "linalg.h" diff --git a/src/cgal.h b/src/cgal.h index 366dbfff..45228be1 100644 --- a/src/cgal.h +++ b/src/cgal.h @@ -39,8 +39,8 @@ using boost::uintmax_t; #include #include -typedef CGAL::Gmpq NT; -typedef CGAL::Extended_cartesian CGAL_Kernel2; +typedef CGAL::Gmpq NT2; +typedef CGAL::Extended_cartesian CGAL_Kernel2; typedef CGAL::Nef_polyhedron_2 CGAL_Nef_polyhedron2; typedef CGAL_Kernel2::Aff_transformation_2 CGAL_Aff_transformation2; @@ -50,6 +50,7 @@ typedef CGAL::Polygon_with_holes_2 CGAL_Poly2h; //typedef CGAL::Cartesian CGAL_Kernel3; typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3; +typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3; typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3; typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; @@ -64,7 +65,7 @@ typedef CGAL::Iso_cuboid_3 CGAL_Iso_cuboid_3; // CGAL_Nef_polyhedron2::Explorer::Point which is different than // CGAL_Kernel2::Point. Hence the suffix 'e' typedef CGAL_Nef_polyhedron2::Explorer::Point CGAL_Point_2e; -typedef CGAL::Iso_rectangle_2 > CGAL_Iso_rectangle_2e; +typedef CGAL::Iso_rectangle_2 > CGAL_Iso_rectangle_2e; #ifdef PREV_NDEBUG diff --git a/src/cgalutils.cc b/src/cgalutils.cc index 8b4c4764..bb46f1cd 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -193,7 +193,8 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) std::vector contour; CGAL_For_all( c1, cend ) { CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); - CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() ); + CGAL_Nef_polyhedron2::Explorer::Point point2d(CGAL::to_double(point3d.x()), + CGAL::to_double(point3d.y())); contour.push_back( point2d ); } if (contour.size()==0) continue; diff --git a/src/cgalutils.h b/src/cgalutils.h index 6ea7711a..d25fd4ca 100644 --- a/src/cgalutils.h +++ b/src/cgalutils.h @@ -1,7 +1,7 @@ #ifndef CGALUTILS_H_ #define CGALUTILS_H_ -#include +#include class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p); CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps); CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N ); From 4bc18dc6802906738eb9f0d75ea063f271a0c9b7 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Mon, 27 May 2013 18:28:29 -0400 Subject: [PATCH 03/20] Don't auto-reload missing files, reduce warning output from periodically called functions --- src/ModuleCache.cc | 29 ++++++++++++++++------------- src/module.cc | 4 +++- src/parsersettings.cc | 13 +++++++------ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 505015ef..0e443e8c 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -30,34 +30,37 @@ ModuleCache *ModuleCache::inst = NULL; */ FileModule *ModuleCache::evaluate(const std::string &filename) { + bool shouldCompile = true; FileModule *lib_mod = NULL; // Create cache ID struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); + bool valid = (stat(filename.c_str(), &st) == 0); 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 + if (this->entries.find(filename) != this->entries.end()) { lib_mod = &(*this->entries[filename].module); - - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) { - if (lib_mod->include_modified(include.second)) { - lib_mod = NULL; - break; + if (this->entries[filename].cache_id == cache_id) { + shouldCompile = false; + + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) { + if (lib_mod->include_modified(include.second)) { + lib_mod = NULL; + shouldCompile = true; + break; + } } } } + else { + shouldCompile = valid; + } // If cache lookup failed (non-existing or old timestamp), compile module - if (!lib_mod) { + if (shouldCompile) { #ifdef DEBUG if (this->entries.find(filename) != this->entries.end()) { PRINTB("Recompiling cached library: %s (%s)", filename % cache_id); diff --git a/src/module.cc b/src/module.cc index 425403b3..080a3dd2 100644 --- a/src/module.cc +++ b/src/module.cc @@ -228,9 +228,11 @@ bool FileModule::handleDependencies() FileModule::ModuleContainer::iterator curr = iter++; FileModule *oldmodule = curr->second; + bool wasmissing = false; // Get an absolute filename for the module std::string filename = curr->first; if (!boosty::is_absolute(filename)) { + wasmissing = true; fs::path fullpath = find_valid_path(this->path, filename); if (!fullpath.empty()) filename = boosty::stringy(fullpath); } @@ -242,7 +244,7 @@ bool FileModule::handleDependencies() PRINTB_NOCACHE(" %s: %p", filename % curr->second); #endif } - if (!curr->second) { + if (!curr->second && !wasmissing) { PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); } } diff --git a/src/parsersettings.cc b/src/parsersettings.cc index 8db33a80..ab93b789 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -30,23 +30,24 @@ fs::path search_libs(const fs::path &localpath) } // files must be 'ordinary' - they must exist and be non-directories +// FIXME: We cannot print any output here since these function is called periodically +// from "Automatic reload and compile" static bool check_valid(const fs::path &p, const std::vector *openfilenames) { if (p.empty()) { - PRINTB("WARNING: File path is blank: %s",p); +// PRINTB("WARNING: File path is blank: %s",p); return false; } if (!p.has_parent_path()) { - PRINTB("WARNING: No parent path: %s",p); +// PRINTB("WARNING: No parent path: %s",p); return false; } if (!fs::exists(p)) { - PRINTB("WARNING: File not found: %s",p); - // searched === +// PRINTB("WARNING: File not found: %s",p); return false; } if (fs::is_directory(p)) { - PRINTB("WARNING: %s invalid - points to a directory",p); +// PRINTB("WARNING: %s invalid - points to a directory",p); return false; } std::string fullname = boosty::stringy(p); @@ -54,7 +55,7 @@ static bool check_valid(const fs::path &p, const std::vector *openf if (openfilenames) { BOOST_FOREACH(const std::string &s, *openfilenames) { if (s == fullname) { - PRINTB("WARNING: circular include file %s", fullname); +// PRINTB("WARNING: circular include file %s", fullname); return false; } } From eade7ede546f2415dfe6a8ff36a77136f883d03b Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 01:24:30 -0400 Subject: [PATCH 04/20] typo --- testdata/modulecache-tests/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index ce341ca0..b1682002 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -84,7 +84,7 @@ Test11: Missing include file appears o rm missing.scad o Open includemissing.scad o Compile (F5) -o Verify that you get: WARNING: Can't open 'use' file 'missing.scad'. +o Verify that you get: WARNING: Can't open include file 'missing.scad'. o echo "module missing() { sphere(10); }" > missing.scad o rm missing.scad o Reload and Compile (F4) - verify that the sphere is gone From 33e873d8dae644b28dd3d6cbcf04450d6675fafd Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 01:25:03 -0400 Subject: [PATCH 05/20] Changed compile GUI logic to properly handler automatic reload on cascading changes --- src/AppleEvents.cc | 2 +- src/MainWindow.h | 20 +++- src/lexer.l | 2 +- src/mainwin.cc | 237 +++++++++++++++++++++++++++--------------- src/parsersettings.cc | 14 +-- 5 files changed, 179 insertions(+), 96 deletions(-) diff --git a/src/AppleEvents.cc b/src/AppleEvents.cc index 1fb4f999..3102792c 100644 --- a/src/AppleEvents.cc +++ b/src/AppleEvents.cc @@ -15,7 +15,7 @@ OSErr eventHandler(const AppleEvent *, AppleEvent *, SRefCon ) if (mainwin) break; } if (mainwin) { - mainwin->actionReloadCompile(); + mainwin->actionReloadRenderCSG(); } return noErr; } diff --git a/src/MainWindow.h b/src/MainWindow.h index bd32bdd5..fc64137d 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -28,6 +28,7 @@ public: QTimer *autoReloadTimer; std::string autoReloadId; + QTimer *waitAfterReloadTimer; ModuleContext top_ctx; FileModule *root_module; // Result of parsing @@ -77,8 +78,8 @@ private: void updateTemporalVariables(); bool fileChangedOnDisk(); bool includesChanged(); - bool compileTopLevelDocument(bool reload); - bool compile(bool reload, bool procevents); + void compileTopLevelDocument(); + void compile(bool reload, bool forcedone = false); void compileCSG(bool procevents); bool maybeSave(); bool checkEditorModified(); @@ -103,6 +104,10 @@ private slots: void actionReload(); void actionShowLibraryFolder(); + void instantiateRoot(); + void compileDone(bool didchange); + void compileEnded(); + private slots: void pasteViewportTranslation(); void pasteViewportRotation(); @@ -110,10 +115,13 @@ private slots: void preferences(); private slots: - void actionCompile(); + void actionRenderCSG(); + void csgRender(); + void csgReloadRender(); #ifdef ENABLE_CGAL void actionRenderCGAL(); void actionRenderCGALDone(class CGAL_Nef_polyhedron *); + void cgalRender(); #endif void actionDisplayAST(); void actionDisplayCSGTree(); @@ -132,6 +140,7 @@ public: void clearCurrentOutput(); public slots: + void actionReloadRenderCSG(); #ifdef ENABLE_OPENCSG void viewModeOpenCSG(); #endif @@ -164,13 +173,16 @@ public slots: void helpManual(); void helpLibrary(); void quit(); - void actionReloadCompile(); void checkAutoReload(); + void waitAfterReload(); void autoReloadSet(bool); private: static void report_func(const class AbstractNode*, void *vp, int mark); + char const * afterCompileSlot; + bool procevents; + class ProgressWidget *progresswidget; class CGALWorker *cgalworker; QMutex consolemutex; diff --git a/src/lexer.l b/src/lexer.l index 0b8048fa..3dd3b6bf 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -100,7 +100,7 @@ E [Ee][+-]?{D}+ %% -include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = "";} +include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = ""; } { [^\t\r\n>]*"/" { filepath = yytext; } [^\t\r\n>/]+ { filename = yytext; } diff --git a/src/mainwin.cc b/src/mainwin.cc index ac752699..52b432c2 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -205,10 +205,16 @@ MainWindow::MainWindow(const QString &filename) connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); autoReloadTimer = new QTimer(this); - autoReloadTimer->setSingleShot(false); + autoReloadTimer->setSingleShot(true); + autoReloadTimer->setInterval(200); connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload())); - connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile())); + waitAfterReloadTimer = new QTimer(this); + waitAfterReloadTimer->setSingleShot(true); + waitAfterReloadTimer->setInterval(200); + connect(waitAfterReloadTimer, SIGNAL(timeout()), this, SLOT(waitAfterReload())); + + connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionRenderCSG())); connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps())); animate_panel->hide(); @@ -288,8 +294,8 @@ MainWindow::MainWindow(const QString &filename) // Design menu connect(this->designActionAutoReload, SIGNAL(toggled(bool)), this, SLOT(autoReloadSet(bool))); - connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadCompile())); - connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionCompile())); + connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadRenderCSG())); + connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionRenderCSG())); #ifdef ENABLE_CGAL connect(this->designActionCompileAndRender, SIGNAL(triggered()), this, SLOT(actionRenderCGAL())); #else @@ -580,7 +586,7 @@ void MainWindow::updateTVal() double fps = this->e_fps->text().toDouble(&fps_ok); if (fps_ok) { if (fps <= 0) { - actionCompile(); + actionRenderCSG(); } else { double s = this->e_fsteps->text().toDouble(); double t = this->e_tval->text().toDouble() + 1/s; @@ -612,14 +618,88 @@ void MainWindow::refreshDocument() } /*! - 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. + compiles the design. Calls compileDone() if anything was compiled */ -bool MainWindow::compile(bool reload, bool procevents) +void MainWindow::compile(bool reload, bool forcedone) { - if (!compileTopLevelDocument(reload)) return false; + bool shouldcompiletoplevel = false; + bool didcompile = false; + + // Reload checks the timestamp of the toplevel file and refreshes if necessary, + if (reload) { + if (fileChangedOnDisk() && checkEditorModified()) { + shouldcompiletoplevel = true; + refreshDocument(); + } + } + else { + shouldcompiletoplevel = true; + } + + if (!shouldcompiletoplevel && includesChanged()) { + shouldcompiletoplevel = true; + } + + if (shouldcompiletoplevel) { + compileTopLevelDocument(); + didcompile = true; + } + + if (this->root_module) { + if (this->root_module->handleDependencies()) { + PRINTB("Module cache size: %d modules", ModuleCache::instance()->size()); + didcompile = true; + } + } + + // If we're auto-reloading, listen for a cascade of changes by starting a timer + // if something changed _and_ there are any external dependencies + if (reload && didcompile && this->root_module) { + if (this->root_module->includes.size() > 0 || + this->root_module->usedlibs.size() > 0) { + this->waitAfterReloadTimer->start(); + return; + } + } + compileDone(didcompile | forcedone); +} + +void MainWindow::waitAfterReload() +{ + if (this->root_module->handleDependencies()) { + this->waitAfterReloadTimer->start(); + return; + } + else { + compile(true, true); // In case file itself or top-level includes changed during dependency updates + } +} + +void MainWindow::compileDone(bool didchange) +{ + const char *callslot; + if (didchange) { + instantiateRoot(); + callslot = afterCompileSlot; + } + else { + callslot = "compileEnded"; + } + + this->procevents = false; + QMetaObject::invokeMethod(this, callslot); +} + +void MainWindow::compileEnded() +{ + clearCurrentOutput(); + GuiLocker::unlock(); + if (designActionAutoReload->isChecked()) autoReloadTimer->start(); +} + +void MainWindow::instantiateRoot() +{ + // Go on and instantiate root_node, then call the continuation slot // Invalidate renderers before we kill the CSG tree this->qglview->setRenderer(NULL); @@ -652,7 +732,7 @@ bool MainWindow::compile(bool reload, bool procevents) if (this->root_module) { // Evaluate CSG tree PRINT("Compiling design (CSG Tree generation)..."); - if (procevents) QApplication::processEvents(); + if (this->procevents) QApplication::processEvents(); AbstractNode::resetIndexCounter(); this->root_inst = ModuleInstantiation("group"); @@ -677,10 +757,8 @@ bool MainWindow::compile(bool reload, bool procevents) } else { PRINT("ERROR: Compilation failed!"); } - if (procevents) QApplication::processEvents(); + if (this->procevents) QApplication::processEvents(); } - - return true; } /*! @@ -1049,66 +1127,44 @@ bool MainWindow::includesChanged() } /*! - 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) +void MainWindow::compileTopLevelDocument() { - bool shouldcompiletoplevel = !reload; - - if (includesChanged()) shouldcompiletoplevel = true; - - if (reload && fileChangedOnDisk() && checkEditorModified()) { - shouldcompiletoplevel = true; - refreshDocument(); - } + console->clear(); - if (shouldcompiletoplevel) { - console->clear(); - - updateTemporalVariables(); - - this->last_compiled_doc = editor->toPlainText(); - std::string fulltext = - std::string(this->last_compiled_doc.toLocal8Bit().constData()) + - "\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); - - if (!animate_panel->isVisible()) { - highlighter->unhighlightLastError(); - if (!this->root_module) { - QTextCursor cursor = editor->textCursor(); - cursor.setPosition(parser_error_pos); - editor->setTextCursor(cursor); - highlighter->highlightError( parser_error_pos ); - } + updateTemporalVariables(); + + this->last_compiled_doc = editor->toPlainText(); + std::string fulltext = + std::string(this->last_compiled_doc.toLocal8Bit().constData()) + + "\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); + + if (!animate_panel->isVisible()) { + highlighter->unhighlightLastError(); + if (!this->root_module) { + QTextCursor cursor = editor->textCursor(); + cursor.setPosition(parser_error_pos); + editor->setTextCursor(cursor); + highlighter->highlightError( parser_error_pos ); } - } - - 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(); + if (!this->fileName.isEmpty()) { + actionReloadRenderCSG(); + } } void MainWindow::autoReloadSet(bool on) @@ -1117,7 +1173,7 @@ void MainWindow::autoReloadSet(bool on) settings.setValue("design/autoReload",designActionAutoReload->isChecked()); if (on) { autoReloadId = ""; - autoReloadTimer->start(200); + autoReloadTimer->start(); } else { autoReloadTimer->stop(); } @@ -1139,15 +1195,22 @@ bool MainWindow::checkEditorModified() return true; } -void MainWindow::actionReloadCompile() +void MainWindow::actionReloadRenderCSG() { if (GuiLocker::isLocked()) return; - GuiLocker lock; + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); // PRINT("Parsing design (AST generation)..."); // QApplication::processEvents(); - if (!compile(true, true)) return; + this->afterCompileSlot = "csgReloadRender"; + this->procevents = true; + compile(true); +} + +void MainWindow::csgReloadRender() +{ if (this->root_node) compileCSG(true); // Go to non-CGAL view mode @@ -1161,20 +1224,26 @@ void MainWindow::actionReloadCompile() viewModeThrownTogether(); #endif } - - clearCurrentOutput(); + compileEnded(); } -void MainWindow::actionCompile() +void MainWindow::actionRenderCSG() { if (GuiLocker::isLocked()) return; - GuiLocker lock; + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); - compile(false, !viewActionAnimate->isChecked()); + this->afterCompileSlot = "csgRender"; + this->procevents = !viewActionAnimate->isChecked(); + compile(false); +} + +void MainWindow::csgRender() +{ if (this->root_node) compileCSG(!viewActionAnimate->isChecked()); // Go to non-CGAL view mode @@ -1198,7 +1267,7 @@ void MainWindow::actionCompile() img.save(filename, "PNG"); } - clearCurrentOutput(); + compileEnded(); } #ifdef ENABLE_CGAL @@ -1206,15 +1275,20 @@ void MainWindow::actionCompile() void MainWindow::actionRenderCGAL() { if (GuiLocker::isLocked()) return; - GuiLocker lock; - + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); - compile(false, true); + this->afterCompileSlot = "cgalRender"; + this->procevents = true; + compile(false); +} +void MainWindow::cgalRender() +{ if (!this->root_module || !this->root_node) { return; } @@ -1234,7 +1308,6 @@ void MainWindow::actionRenderCGAL() progress_report_prep(this->root_node, report_func, this); - GuiLocker::lock(); // Will be unlocked in actionRenderCGALDone() this->cgalworker->start(this->tree); } @@ -1297,9 +1370,7 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N) this->statusBar()->removeWidget(this->progresswidget); delete this->progresswidget; this->progresswidget = NULL; - clearCurrentOutput(); - - GuiLocker::unlock(); + compileEnded(); } #endif /* ENABLE_CGAL */ @@ -1612,7 +1683,7 @@ void MainWindow::viewModeAnimate() { if (viewActionAnimate->isChecked()) { animate_panel->show(); - actionCompile(); + actionRenderCSG(); updatedFps(); } else { animate_panel->hide(); diff --git a/src/parsersettings.cc b/src/parsersettings.cc index ab93b789..04c20edc 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -30,24 +30,24 @@ fs::path search_libs(const fs::path &localpath) } // files must be 'ordinary' - they must exist and be non-directories -// FIXME: We cannot print any output here since these function is called periodically -// from "Automatic reload and compile" +// FIXME: Don't print anything from this function as it's called repeatedly +// for the automatic reload function (FileModule::include_modified()) static bool check_valid(const fs::path &p, const std::vector *openfilenames) { if (p.empty()) { -// PRINTB("WARNING: File path is blank: %s",p); + //PRINTB("WARNING: File path is blank: %s",p); return false; } if (!p.has_parent_path()) { -// PRINTB("WARNING: No parent path: %s",p); + //PRINTB("WARNING: No parent path: %s",p); return false; } if (!fs::exists(p)) { -// PRINTB("WARNING: File not found: %s",p); + //PRINTB("WARNING: File not found: %s",p); return false; } if (fs::is_directory(p)) { -// PRINTB("WARNING: %s invalid - points to a directory",p); + //PRINTB("WARNING: %s invalid - points to a directory",p); return false; } std::string fullname = boosty::stringy(p); @@ -55,7 +55,7 @@ static bool check_valid(const fs::path &p, const std::vector *openf if (openfilenames) { BOOST_FOREACH(const std::string &s, *openfilenames) { if (s == fullname) { -// PRINTB("WARNING: circular include file %s", fullname); + //PRINTB("WARNING: circular include file %s", fullname); return false; } } From 72de958320c89df13e940afdd56a102d2ec223ac Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 10:20:07 -0400 Subject: [PATCH 06/20] Added testcase for cascading changes --- testdata/modulecache-tests/README.txt | 10 ++++++++++ testdata/modulecache-tests/cascade.sh | 11 +++++++++++ testdata/modulecache-tests/cascadetest.scad | 9 +++++++++ 3 files changed, 30 insertions(+) create mode 100755 testdata/modulecache-tests/cascade.sh create mode 100644 testdata/modulecache-tests/cascadetest.scad diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index b1682002..da0c8437 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -108,3 +108,13 @@ o Verify that you get: WARNING: Can't open 'use' file 'missing.scad'. o echo "module missing() { sphere(10); }" > missing.scad o rm missing.scad o Compile (F5) - verify that the sphere is gone + +Test14: Automatic reload of cascading changes +------- + +o rm cascade-*.scad +o Open cascadetest.scad +o Compile (F5) +o ./cascade.sh +o Verify that everything reloads at once without flickering + diff --git a/testdata/modulecache-tests/cascade.sh b/testdata/modulecache-tests/cascade.sh new file mode 100755 index 00000000..6f15c7fc --- /dev/null +++ b/testdata/modulecache-tests/cascade.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rm cascade-*.scad +sleep 0.05 +echo "module A() { sphere(5); }" > cascade-A.scad +sleep 0.05 +echo "module B() { cube([8,8,8], center=true); }" > cascade-B.scad +sleep 0.05 +echo "module C() { cylinder(h=8, r=5, center=true); }" > cascade-C.scad +sleep 0.05 +echo "module D() { cylinder(h=10, r1=5, r2=0, center=true); }" > cascade-D.scad diff --git a/testdata/modulecache-tests/cascadetest.scad b/testdata/modulecache-tests/cascadetest.scad new file mode 100644 index 00000000..5cce652d --- /dev/null +++ b/testdata/modulecache-tests/cascadetest.scad @@ -0,0 +1,9 @@ +include +include +use +use + +A(); +translate([11,0,0]) B(); +translate([22,0,0]) C(); +translate([33,0,0]) D(); From c0fa353e063c2075f6e1920f483bfa7142c4345e Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 10:23:36 -0400 Subject: [PATCH 07/20] Added automatic reload and compile note --- testdata/modulecache-tests/README.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index da0c8437..a46e3d31 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -114,7 +114,7 @@ Test14: Automatic reload of cascading changes o rm cascade-*.scad o Open cascadetest.scad -o Compile (F5) +o Turn on Automatic Reload and Compile +o Verify that the 4 objects render correctly o ./cascade.sh o Verify that everything reloads at once without flickering - From 5722dd9e411ed52a0b840afc47b468c3ed1970c1 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 16:29:53 -0400 Subject: [PATCH 08/20] Minor refactoring, no longer automatically reloads if a dependency disappears --- src/MainWindow.h | 1 - src/ModuleCache.cc | 12 ++--- src/mainwin.cc | 21 ++------- src/module.cc | 62 +++++++++++++++++--------- src/module.h | 23 ++++++---- testdata/modulecache-tests/README.txt | 30 +++++++------ testdata/modulecache-tests/cascade2.sh | 11 +++++ 7 files changed, 94 insertions(+), 66 deletions(-) create mode 100755 testdata/modulecache-tests/cascade2.sh diff --git a/src/MainWindow.h b/src/MainWindow.h index fc64137d..79e20809 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -77,7 +77,6 @@ private: void refreshDocument(); void updateTemporalVariables(); bool fileChangedOnDisk(); - bool includesChanged(); void compileTopLevelDocument(); void compile(bool reload, bool forcedone = false); void compileCSG(bool procevents); diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 0e443e8c..5ebd5499 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -38,6 +38,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename) memset(&st, 0, sizeof(struct stat)); bool valid = (stat(filename.c_str(), &st) == 0); + // If file isn't there, just return and let the cache retain the old module + if (!valid) return NULL; + std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); // Lookup in cache @@ -46,12 +49,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename) if (this->entries[filename].cache_id == cache_id) { shouldCompile = false; - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) { - if (lib_mod->include_modified(include.second)) { - lib_mod = NULL; - shouldCompile = true; - break; - } + if (lib_mod->includesChanged()) { + lib_mod = NULL; + shouldCompile = true; } } } diff --git a/src/mainwin.cc b/src/mainwin.cc index 52b432c2..9652d6ca 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -636,11 +636,12 @@ void MainWindow::compile(bool reload, bool forcedone) shouldcompiletoplevel = true; } - if (!shouldcompiletoplevel && includesChanged()) { + if (!shouldcompiletoplevel && this->root_module && this->root_module->includesChanged()) { shouldcompiletoplevel = true; } if (shouldcompiletoplevel) { + console->clear(); compileTopLevelDocument(); didcompile = true; } @@ -655,8 +656,8 @@ void MainWindow::compile(bool reload, bool forcedone) // If we're auto-reloading, listen for a cascade of changes by starting a timer // if something changed _and_ there are any external dependencies if (reload && didcompile && this->root_module) { - if (this->root_module->includes.size() > 0 || - this->root_module->usedlibs.size() > 0) { + if (this->root_module->hasIncludes() || + this->root_module->usesLibraries()) { this->waitAfterReloadTimer->start(); return; } @@ -1116,23 +1117,11 @@ bool MainWindow::fileChangedOnDisk() return false; } -bool MainWindow::includesChanged() -{ - if (this->root_module) { - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->root_module->includes) { - if (this->root_module->include_modified(item.second)) return true; - } - } - return false; -} - /*! Returns true if anything was compiled. */ void MainWindow::compileTopLevelDocument() { - console->clear(); - updateTemporalVariables(); this->last_compiled_doc = editor->toPlainText(); @@ -1233,7 +1222,6 @@ void MainWindow::actionRenderCSG() GuiLocker::lock(); autoReloadTimer->stop(); setCurrentOutput(); - console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); @@ -1278,7 +1266,6 @@ void MainWindow::actionRenderCGAL() GuiLocker::lock(); autoReloadTimer->stop(); setCurrentOutput(); - console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); diff --git a/src/module.cc b/src/module.cc index 080a3dd2..6cd8322a 100644 --- a/src/module.cc +++ b/src/module.cc @@ -207,6 +207,28 @@ void FileModule::registerInclude(const std::string &localpath, this->includes[localpath] = inc; } +bool FileModule::includesChanged() const +{ + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->includes) { + if (include_modified(item.second)) return true; + } + return false; +} + +bool FileModule::include_modified(const IncludeFile &inc) const +{ + struct stat st; + memset(&st, 0, sizeof(struct stat)); + + fs::path fullpath = find_valid_path(this->path, inc.filename); + bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false; + + if (valid && !inc.valid) return true; // Detect appearance of file but not removal + if (valid && st.st_mtime > inc.mtime) return true; + + return false; +} + /*! Check if any dependencies have been modified and recompile them. Returns true if anything was recompiled. @@ -217,12 +239,13 @@ bool FileModule::handleDependencies() this->is_handling_dependencies = true; bool changed = false; - // Iterating manually since we want to modify the container while iterating - // If a lib in usedlibs was previously missing, we need to relocate it // by searching the applicable paths. We can identify a previously missing module // as it will have a relative path. + + // Iterating manually since we want to modify the container while iterating + std::vector > modified_modules; FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { FileModule::ModuleContainer::iterator curr = iter++; @@ -237,16 +260,27 @@ bool FileModule::handleDependencies() if (!fullpath.empty()) filename = boosty::stringy(fullpath); } - curr->second = ModuleCache::instance()->evaluate(filename); - if (curr->second != oldmodule) { + FileModule *newmodule = ModuleCache::instance()->evaluate(filename); + // Detect appearance but not removal of files + if (newmodule && oldmodule != newmodule) { changed = true; #ifdef DEBUG - PRINTB_NOCACHE(" %s: %p", filename % curr->second); + PRINTB_NOCACHE(" %s: %p", filename % newmodule); #endif } - if (!curr->second && !wasmissing) { - PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); + if (newmodule) { + modified_modules.push_back(std::make_pair(filename, newmodule)); + this->usedlibs.erase(curr); } + else { + // Only print warning if we're not part of an automatic reload + if (!oldmodule && !wasmissing) { + PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); + } + } + } + BOOST_FOREACH(const FileModule::ModuleContainer::value_type &mod, modified_modules) { + this->usedlibs[mod.first] = mod.second; } this->is_handling_dependencies = false; @@ -269,17 +303,3 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat return node; } - -bool FileModule::include_modified(IncludeFile inc) -{ - struct stat st; - memset(&st, 0, sizeof(struct stat)); - - fs::path fullpath = find_valid_path(this->path, inc.filename); - bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false; - - if (valid != inc.valid) return true; - if (valid && st.st_mtime > inc.mtime) return true; - - return false; -} diff --git a/src/module.h b/src/module.h index 5dfb8c46..ad2a46ce 100644 --- a/src/module.h +++ b/src/module.h @@ -77,12 +77,6 @@ public: LocalScope scope; }; -struct IncludeFile { - std::string filename; - bool valid; - time_t mtime; -}; - // FIXME: A FileModule doesn't have definition arguments, so we shouldn't really // inherit from a Module class FileModule : public Module @@ -94,15 +88,28 @@ public: void setModulePath(const std::string &path) { this->path = path; } const std::string &modulePath() const { return this->path; } void registerInclude(const std::string &localpath, const std::string &fullpath); + bool includesChanged() const; bool handleDependencies(); virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; + bool hasIncludes() const { return !this->includes.empty(); } + bool usesLibraries() const { return !this->usedlibs.empty(); } + + typedef boost::unordered_map ModuleContainer; ModuleContainer usedlibs; +private: + struct IncludeFile { + std::string filename; + bool valid; + time_t mtime; + }; + + bool include_modified(const IncludeFile &inc) const; + typedef boost::unordered_map IncludeContainer; IncludeContainer includes; - bool include_modified(struct IncludeFile inc); -private: + bool is_handling_dependencies; std::string path; }; diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index a46e3d31..c38822f9 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -26,13 +26,6 @@ o Open use-mcad.scad o Compile (F5) o Check that you get a rounded box -Test4: USE Non-existing file ------- - -o Open usenonexsistingfile.scad -o Compile (F5) -o Verify that you get: WARNING: Can't open 'use' file 'nofile.scad'. - Test5: Overload USEd module ------ @@ -86,8 +79,11 @@ o Open includemissing.scad o Compile (F5) o Verify that you get: WARNING: Can't open include file 'missing.scad'. o echo "module missing() { sphere(10); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere appeared o rm missing.scad -o Reload and Compile (F4) - verify that the sphere is gone +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missing() { sphere(20); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere increased in size Test12: Missing include file in subpath appears ------ @@ -96,25 +92,33 @@ o Open includemissingsub.scad o Compile (F5) o Verify that you get: WARNING: Can't open include file 'subdir/missingsub.scad'. o echo "module missingsub() { sphere(10); }" > subdir/missingsub.scad +o Reload and Compile (F4) - verify that the sphere appeared o rm subdir/missingsub.scad -o Reload and Compile (F4) - verify that the sphere is gone +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missingsub() { sphere(20); }" > subdir/missingsub.scad +o Reload and Compile (F4) - verify that the sphere increased in size Test13: Missing library file appears ------- o rm missing.scad o Open usemissing.scad o Compile (F5) -o Verify that you get: WARNING: Can't open 'use' file 'missing.scad'. +o Verify that you get: WARNING: Can't open library file 'missing.scad'. o echo "module missing() { sphere(10); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere appeared o rm missing.scad -o Compile (F5) - verify that the sphere is gone +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missing() { sphere(20); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere increased in size Test14: Automatic reload of cascading changes ------- -o rm cascade-*.scad +o ./cascade.sh o Open cascadetest.scad o Turn on Automatic Reload and Compile o Verify that the 4 objects render correctly -o ./cascade.sh +o rm cascade-*.scad +o Verify that no rerendering was triggered (the 4 objects are still there) +o ./cascade2.sh o Verify that everything reloads at once without flickering diff --git a/testdata/modulecache-tests/cascade2.sh b/testdata/modulecache-tests/cascade2.sh new file mode 100755 index 00000000..aa17c6c8 --- /dev/null +++ b/testdata/modulecache-tests/cascade2.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rm cascade-*.scad +sleep 0.1 +echo "module A() { sphere(6); }" > cascade-A.scad +sleep 0.1 +echo "module B() { cube([10,10,10], center=true); }" > cascade-B.scad +sleep 0.1 +echo "module C() { cylinder(h=10, r=6, center=true); }" > cascade-C.scad +sleep 0.1 +echo "module D() { cylinder(h=12, r1=6, r2=0, center=true); }" > cascade-D.scad From 1255b19c2e2a75b1cb52fd82584ff768ed9c2385 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 20:58:44 -0400 Subject: [PATCH 09/20] bugfix: Auto-reload failed in some circumstances --- src/mainwin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mainwin.cc b/src/mainwin.cc index 9652d6ca..788f8249 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -205,7 +205,7 @@ MainWindow::MainWindow(const QString &filename) connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); autoReloadTimer = new QTimer(this); - autoReloadTimer->setSingleShot(true); + autoReloadTimer->setSingleShot(false); autoReloadTimer->setInterval(200); connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload())); From 0041f3012d8a401c9e2750ed08fe4e5ce80eb4a9 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 30 May 2013 16:50:05 -0400 Subject: [PATCH 10/20] Increase recursion limit to 1000 --- src/expr.cc | 2 +- src/module.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expr.cc b/src/expr.cc index 43554004..1381c248 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -69,7 +69,7 @@ public: expr.recursioncount++; } ~FuncRecursionGuard() { expr.recursioncount--; } - bool recursion_detected() const { return (expr.recursioncount > 100); } + bool recursion_detected() const { return (expr.recursioncount > 1000); } private: const Expression &expr; }; diff --git a/src/module.cc b/src/module.cc index 6cd8322a..b9b23d8d 100644 --- a/src/module.cc +++ b/src/module.cc @@ -147,7 +147,7 @@ public: ~ModRecursionGuard() { inst.recursioncount--; } - bool recursion_detected() const { return (inst.recursioncount > 100); } + bool recursion_detected() const { return (inst.recursioncount > 1000); } private: const ModuleInstantiation &inst; }; From 06237a067fb124be1e8ead8fe54d1098a083734f Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 30 May 2013 16:51:00 -0400 Subject: [PATCH 11/20] Related to #181, also ignore removal of the main file when auto-reloading --- src/mainwin.cc | 5 ++++- testdata/modulecache-tests/README.txt | 4 +++- testdata/modulecache-tests/cascade.sh | 3 ++- testdata/modulecache-tests/cascade2.sh | 1 + testdata/modulecache-tests/cascadetest.scad | 10 +--------- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/mainwin.cc b/src/mainwin.cc index 788f8249..eedd3a52 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -1106,7 +1106,10 @@ bool MainWindow::fileChangedOnDisk() if (!this->fileName.isEmpty()) { struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(this->fileName.toLocal8Bit(), &st); + bool valid = (stat(this->fileName.toLocal8Bit(), &st) == 0); + // If file isn't there, just return and use current editor text + if (!valid) return false; + std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size); if (newid != this->autoReloadId) { diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index c38822f9..214acc56 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -118,7 +118,9 @@ o ./cascade.sh o Open cascadetest.scad o Turn on Automatic Reload and Compile o Verify that the 4 objects render correctly -o rm cascade-*.scad +o rm cascadetest.scad +o Verify that no rerendering was triggered (the 4 objects are still there) +o rm cascade*.scad o Verify that no rerendering was triggered (the 4 objects are still there) o ./cascade2.sh o Verify that everything reloads at once without flickering diff --git a/testdata/modulecache-tests/cascade.sh b/testdata/modulecache-tests/cascade.sh index 6f15c7fc..5dd0ef7b 100755 --- a/testdata/modulecache-tests/cascade.sh +++ b/testdata/modulecache-tests/cascade.sh @@ -1,6 +1,7 @@ #!/bin/bash -rm cascade-*.scad +rm cascade*.scad +echo "include include use use A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad sleep 0.05 echo "module A() { sphere(5); }" > cascade-A.scad sleep 0.05 diff --git a/testdata/modulecache-tests/cascade2.sh b/testdata/modulecache-tests/cascade2.sh index aa17c6c8..50f98e07 100755 --- a/testdata/modulecache-tests/cascade2.sh +++ b/testdata/modulecache-tests/cascade2.sh @@ -1,6 +1,7 @@ #!/bin/bash rm cascade-*.scad +echo "include include use use A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad sleep 0.1 echo "module A() { sphere(6); }" > cascade-A.scad sleep 0.1 diff --git a/testdata/modulecache-tests/cascadetest.scad b/testdata/modulecache-tests/cascadetest.scad index 5cce652d..33a089e4 100644 --- a/testdata/modulecache-tests/cascadetest.scad +++ b/testdata/modulecache-tests/cascadetest.scad @@ -1,9 +1 @@ -include -include -use -use - -A(); -translate([11,0,0]) B(); -translate([22,0,0]) C(); -translate([33,0,0]) D(); +include include use use A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D(); From 5d3ae2a77e97223984b74f312460d9d2f60edbe6 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 30 May 2013 16:51:32 -0400 Subject: [PATCH 12/20] cascadetest.scad is now autogenerated --- testdata/modulecache-tests/cascadetest.scad | 1 - 1 file changed, 1 deletion(-) delete mode 100644 testdata/modulecache-tests/cascadetest.scad diff --git a/testdata/modulecache-tests/cascadetest.scad b/testdata/modulecache-tests/cascadetest.scad deleted file mode 100644 index 33a089e4..00000000 --- a/testdata/modulecache-tests/cascadetest.scad +++ /dev/null @@ -1 +0,0 @@ -include include use use A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D(); From 3e024822e6820bf29cdf555cfac180880d6fc962 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 4 Jun 2013 21:15:01 -0400 Subject: [PATCH 13/20] Fixed a bug where a file was loaded twice when auto reload was on --- src/mainwin.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/mainwin.cc b/src/mainwin.cc index eedd3a52..7cebd7ef 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -511,7 +511,8 @@ MainWindow::openFile(const QString &new_filename) } #endif setFileName(actual_filename); - + + fileChangedOnDisk(); // force cached autoReloadId to update refreshDocument(); updateRecentFiles(); if (actual_filename.isEmpty()) { @@ -627,10 +628,17 @@ void MainWindow::compile(bool reload, bool forcedone) // Reload checks the timestamp of the toplevel file and refreshes if necessary, if (reload) { + // Refresh files if it has changed on disk if (fileChangedOnDisk() && checkEditorModified()) { shouldcompiletoplevel = true; refreshDocument(); } + // If the file hasn't changed, we might still need to compile it + // if we haven't yet compiled the current text. + else { + QString current_doc = editor->toPlainText(); + if (current_doc != last_compiled_doc) shouldcompiletoplevel = true; + } } else { shouldcompiletoplevel = true; @@ -1052,7 +1060,10 @@ void MainWindow::actionShowLibraryFolder() void MainWindow::actionReload() { - if (checkEditorModified()) refreshDocument(); + if (checkEditorModified()) { + fileChangedOnDisk(); // force cached autoReloadId to update + refreshDocument(); + } } void MainWindow::hideEditor() From c719dde9719b7063247f6d6e149e66e75fbcf40a Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 4 Jun 2013 21:52:03 -0400 Subject: [PATCH 14/20] More frequent progress updates, progress updates also for CSG evaluation --- src/CSGTermEvaluator.cc | 3 +++ src/ProgressWidget.ui | 3 +++ src/mainwin.cc | 10 +++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc index a6b654c2..71cf1497 100644 --- a/src/CSGTermEvaluator.cc +++ b/src/CSGTermEvaluator.cc @@ -115,6 +115,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node) if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, ps, node.modinst, node); + node.progress_report(); } } this->stored_term[node.index()] = t1; @@ -178,6 +179,7 @@ Response CSGTermEvaluator::visit(State &state, const RenderNode &node) shared_ptr ps; if (this->psevaluator) { ps = this->psevaluator->getPolySet(node, true); + node.progress_report(); } if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, @@ -201,6 +203,7 @@ Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node) if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, ps, node.modinst, node); + node.progress_report(); } this->stored_term[node.index()] = t1; addToParent(state, node); diff --git a/src/ProgressWidget.ui b/src/ProgressWidget.ui index 895586cb..9563b0ff 100644 --- a/src/ProgressWidget.ui +++ b/src/ProgressWidget.ui @@ -22,6 +22,9 @@ + + 988 + 24 diff --git a/src/mainwin.cc b/src/mainwin.cc index febb3253..597a094f 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -468,12 +468,12 @@ void MainWindow::showProgress() void MainWindow::report_func(const class AbstractNode*, void *vp, int mark) { MainWindow *thisp = static_cast(vp); - int v = (int)((mark*100.0) / progress_report_count); - int percent = v < 100 ? v : 99; - - if (percent > thisp->progresswidget->value()) { + int v = (int)((mark*1000.0) / progress_report_count); + int permille = v < 1000 ? v : 999; + printf("Progress: %d\n", permille); + if (permille > thisp->progresswidget->value()) { QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection, - Q_ARG(int, percent)); + Q_ARG(int, permille)); QApplication::processEvents(); } From a126fa826c29b88a9edb3452e407de7f2e5a6c03 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 4 Jun 2013 23:45:00 -0400 Subject: [PATCH 15/20] bugfix: the previous progress bar fix wasn't fixed properly --- src/ProgressWidget.cc | 2 +- src/ProgressWidget.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProgressWidget.cc b/src/ProgressWidget.cc index ce66405f..7aa6a4ac 100644 --- a/src/ProgressWidget.cc +++ b/src/ProgressWidget.cc @@ -5,7 +5,7 @@ ProgressWidget::ProgressWidget(QWidget *parent) :QWidget(parent) { setupUi(this); - setRange(0, 100); + setRange(0, 1000); setValue(0); this->wascanceled = false; this->starttime.start(); diff --git a/src/ProgressWidget.ui b/src/ProgressWidget.ui index 9563b0ff..24aefdb2 100644 --- a/src/ProgressWidget.ui +++ b/src/ProgressWidget.ui @@ -23,7 +23,7 @@ - 988 + 1000 24 From 169e08adce2b049cdb11aaba8a713548236e3d2d Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 18 Jun 2013 01:58:15 -0400 Subject: [PATCH 16/20] Default to clang for Mac --- scripts/macosx-build-dependencies.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index 03f8598f..9cc06621 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -8,6 +8,7 @@ # # Usage: macosx-build-dependencies.sh [-6lcd] # -6 Build only 64-bit binaries +# -c Force use of LLVM compiler # -l Force use of LLVM compiler # -c Force use of clang compiler # -d Build for deployment (if not specified, e.g. Sparkle won't be built) @@ -17,6 +18,7 @@ # # FIXME: # o Verbose option +# o Force rebuild vs. only rebuild changes # BASEDIR=$PWD/../libraries @@ -392,7 +394,7 @@ elif $OPTION_GCC; then elif $OPTION_CLANG; then USING_CLANG=true elif (( $OSX_VERSION >= 7 )); then - USING_GCC=true + USING_CLANG=true fi if $USING_LLVM; then From f452c986e1639455c518daaa9213af2bb4b672cb Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 18 Jun 2013 01:58:26 -0400 Subject: [PATCH 17/20] Default to clang for Mac --- setenv_mjau.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setenv_mjau.sh b/setenv_mjau.sh index 7976c609..51eec8ed 100644 --- a/setenv_mjau.sh +++ b/setenv_mjau.sh @@ -1,7 +1,7 @@ export OPENSCAD_LIBRARIES=$PWD/../libraries/install export DYLD_LIBRARY_PATH=$OPENSCAD_LIBRARIES/lib export DYLD_FRAMEWORK_PATH=$OPENSCAD_LIBRARIES/lib -export QMAKESPEC=macx-g++ +#export QMAKESPEC=macx-g++ #export OPENCSGDIR=$PWD/../OpenCSG-1.3.0 #export CGALDIR=$PWD/../install/CGAL-3.6 From 9b7c83bbda30c0896067895e8bb640a9c3dcb039 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 18 Jun 2013 02:17:25 -0400 Subject: [PATCH 18/20] missed one conflict --- src/module.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/module.cc b/src/module.cc index 9c0272dd..35b8edf1 100644 --- a/src/module.cc +++ b/src/module.cc @@ -264,10 +264,6 @@ bool FileModule::handleDependencies() // as it will have a relative path. // Iterating manually since we want to modify the container while iterating -<<<<<<< HEAD -======= - std::vector > modified_modules; ->>>>>>> origin/issue181 FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { FileModule::ModuleContainer::iterator curr = iter++; From b75826d2987d58252f1bf7edf673ec3c9024bf95 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Wed, 19 Jun 2013 03:48:16 -0400 Subject: [PATCH 19/20] Leftover from previous merge --- src/module.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/module.cc b/src/module.cc index 35b8edf1..8fb8506b 100644 --- a/src/module.cc +++ b/src/module.cc @@ -293,9 +293,6 @@ bool FileModule::handleDependencies() } } } - BOOST_FOREACH(const FileModule::ModuleContainer::value_type &mod, modified_modules) { - this->usedlibs[mod.first] = mod.second; - } this->is_handling_dependencies = false; return changed; From c75d19555256c5daceb9aea2c3e45010a7a75e88 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Wed, 19 Jun 2013 03:48:50 -0400 Subject: [PATCH 20/20] clang fixes --- scripts/macosx-build-dependencies.sh | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index 9cc06621..de739b81 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -25,8 +25,8 @@ BASEDIR=$PWD/../libraries OPENSCADDIR=$PWD SRCDIR=$BASEDIR/src DEPLOYDIR=$BASEDIR/install -MAC_OSX_VERSION_MIN=10.5 -OPTION_32BIT=true +MAC_OSX_VERSION_MIN=10.6 +OPTION_32BIT=false OPTION_LLVM=false OPTION_CLANG=false OPTION_GCC=false @@ -55,14 +55,15 @@ build_qt() fi tar xzf qt-everywhere-opensource-src-$version.tar.gz cd qt-everywhere-opensource-src-$version - if $OPTION_CLANG; then + if $USING_CLANG; then # FIX for clang sed -i "" -e "s/::TabletProximityRec/TabletProximityRec/g" src/gui/kernel/qt_cocoa_helpers_mac_p.h + PLATFORM="-platform unsupported/macx-clang" fi if $OPTION_32BIT; then QT_32BIT="-arch x86" fi - ./configure -prefix $DEPLOYDIR -release $QT_32BIT -arch x86_64 -opensource -confirm-license -fast -no-qt3support -no-svg -no-phonon -no-audio-backend -no-multimedia -no-javascript-jit -no-script -no-scripttools -no-declarative -no-xmlpatterns -nomake demos -nomake examples -nomake docs -nomake translations -no-webkit + ./configure -prefix $DEPLOYDIR -release $QT_32BIT -arch x86_64 -opensource -confirm-license $PLATFORM -fast -no-qt3support -no-svg -no-phonon -no-audio-backend -no-multimedia -no-javascript-jit -no-script -no-scripttools -no-declarative -no-xmlpatterns -nomake demos -nomake examples -nomake docs -nomake translations -no-webkit make -j6 install } @@ -213,10 +214,10 @@ build_boost() if $OPTION_32BIT; then BOOST_EXTRA_FLAGS="-arch i386" fi - if $OPTION_LLVM; then + if $USING_LLVM; then BOOST_TOOLSET="toolset=darwin-llvm" echo "using darwin : llvm : llvm-g++ ;" >> tools/build/v2/user-config.jam - elif $OPTION_CLANG; then + elif $USING_CLANG; then BOOST_TOOLSET="toolset=clang" echo "using clang ;" >> tools/build/v2/user-config.jam fi @@ -379,7 +380,7 @@ OSX_VERSION=`sw_vers -productVersion | cut -d. -f2` if (( $OSX_VERSION >= 8 )); then echo "Detected Mountain Lion (10.8) or later" elif (( $OSX_VERSION >= 7 )); then - echo "Detected Lion (10.7) or later" + echo "Detected Lion (10.7)" else echo "Detected Snow Leopard (10.6) or earlier" fi @@ -417,16 +418,11 @@ elif $USING_CLANG; then export QMAKESPEC=unsupported/macx-clang fi -if (( $OSX_VERSION >= 8 )); then - echo "Setting build target to 10.6 or later" - MAC_OSX_VERSION_MIN=10.6 -else - echo "Setting build target to 10.5 or later" - MAC_OSX_VERSION_MIN=10.5 -fi +echo "Building for $MAC_OSX_VERSION_MIN or later" if $OPTION_DEPLOY; then echo "Building deployment version of libraries" + OPTION_32BIT=true else OPTION_32BIT=false fi