summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Kintel <marius@kintel.net>2013-06-18 05:46:48 (GMT)
committerMarius Kintel <marius@kintel.net>2013-06-18 05:46:48 (GMT)
commit6d91540e4cc3f9fe0caaea63ac64518a5626d28b (patch)
treeca7034453a1f55124e4b7378aaab22985aff6a21
parent95947a877b8e88521a7f00348d56c89e9b7c2a79 (diff)
parent6c7d386a3338039416ced323bf1aa75edbb43d19 (diff)
Merge branch 'master' into epec-kernel
-rw-r--r--RELEASE_NOTES51
-rw-r--r--bison.pri4
-rw-r--r--boost.pri4
-rw-r--r--cgal.pri4
-rw-r--r--common.pri2
-rw-r--r--contrib/scad-mode.el6
-rw-r--r--doc/TODO.txt2
-rw-r--r--doc/release-checklist.txt32
-rw-r--r--examples/example017.scad2
m---------libraries/MCAD0
-rw-r--r--mingw-cross-env.pri3
-rw-r--r--openscad.pro19
-rwxr-xr-xscripts/builder.sh36
-rwxr-xr-xscripts/check-dependencies.sh23
-rwxr-xr-xscripts/git-archive-all.py571
-rw-r--r--scripts/installer.nsi2
-rwxr-xr-xscripts/macosx-build-dependencies.sh16
-rwxr-xr-xscripts/publish-macosx.sh13
-rwxr-xr-xscripts/release-common.sh32
-rw-r--r--scripts/setenv-mingw-xbuild.sh1
-rw-r--r--scripts/setenv-unibuild.sh4
-rwxr-xr-xscripts/uni-build-dependencies.sh23
-rwxr-xr-xscripts/uni-get-dependencies.sh12
-rw-r--r--src/AboutDialog.html16
-rw-r--r--src/CGALEvaluator.cc2
-rw-r--r--src/CSGTermEvaluator.cc4
-rw-r--r--src/CocoaUtils.h3
-rw-r--r--src/CocoaUtils.mm5
-rw-r--r--src/CsgInfo.h2
-rw-r--r--src/MainWindow.h2
-rw-r--r--src/MainWindow.ui11
-rw-r--r--src/ModuleCache.cc66
-rw-r--r--src/ModuleCache.h1
-rw-r--r--src/OffscreenContextWGL.cc33
-rw-r--r--src/OpenCSGRenderer.cc63
-rw-r--r--src/PlatformUtils-mac.mm7
-rw-r--r--src/PlatformUtils-posix.cc10
-rw-r--r--src/PlatformUtils-win.cc70
-rw-r--r--src/PlatformUtils.cc40
-rw-r--r--src/PlatformUtils.h14
-rw-r--r--src/PolySetCGALEvaluator.cc1
-rw-r--r--src/ProgressWidget.cc2
-rw-r--r--src/ProgressWidget.ui3
-rw-r--r--src/ThrownTogetherRenderer.cc82
-rw-r--r--src/cgaladv.cc1
-rw-r--r--src/cgalworker.cc1
-rw-r--r--src/control.cc2
-rw-r--r--src/csgops.cc1
-rw-r--r--src/csgterm.cc57
-rw-r--r--src/csgterm.h38
-rw-r--r--src/dxfdim.cc11
-rw-r--r--src/expr.cc18
-rw-r--r--src/func.cc2
-rw-r--r--src/highlighter.cc16
-rw-r--r--src/import.cc2
-rw-r--r--src/lexer.l87
-rw-r--r--src/linearextrude.cc6
-rw-r--r--src/localscope.cc8
-rw-r--r--src/mainwin.cc125
-rw-r--r--src/modcontext.cc71
-rw-r--r--src/modcontext.h3
-rw-r--r--src/module.cc92
-rw-r--r--src/module.h27
-rw-r--r--src/openscad.cc1
-rw-r--r--src/parser.y8
-rw-r--r--src/parsersettings.cc90
-rw-r--r--src/parsersettings.h6
-rw-r--r--src/primitives.cc5
-rw-r--r--src/renderer.cc73
-rw-r--r--src/renderer.h3
-rw-r--r--src/rotateextrude.cc1
-rw-r--r--src/system-gl.cc3
-rw-r--r--testdata/modulecache-tests/README.txt61
-rwxr-xr-xtestdata/modulecache-tests/cascade.sh12
-rwxr-xr-xtestdata/modulecache-tests/cascade2.sh12
-rw-r--r--testdata/modulecache-tests/includemissing.scad2
-rw-r--r--testdata/modulecache-tests/includemissingsub.scad2
-rw-r--r--testdata/modulecache-tests/usemissing.scad2
-rw-r--r--testdata/scad/dxf/arc.scad1
-rw-r--r--testdata/scad/dxf/circle.scad1
-rw-r--r--testdata/scad/features/background-modifier.scad5
-rw-r--r--testdata/scad/features/circle-tests.scad1
-rw-r--r--testdata/scad/features/cylinder-tests.scad4
-rw-r--r--testdata/scad/features/highlight-and-background-modifier.scad31
-rw-r--r--testdata/scad/features/highlight-modifier.scad5
-rw-r--r--testdata/scad/features/ifelse-tests.scad10
-rw-r--r--testdata/scad/features/linear_extrude-tests.scad3
-rw-r--r--testdata/scad/features/rotate_extrude-tests.scad4
-rw-r--r--testdata/scad/features/sphere-tests.scad1
-rw-r--r--testdata/scad/minimal/allexpressions.scad30
-rw-r--r--testdata/scad/minimal/allfunctions.scad53
-rw-r--r--testdata/scad/minimal/allmodules.scad3
-rw-r--r--testdata/scad/misc/value-reassignment-tests.scad9
-rw-r--r--testdata/scad/misc/value-reassignment-tests2.scad16
-rw-r--r--testdata/scad/misc/variable-scope-tests.scad13
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/CMakeLists.txt20
-rw-r--r--tests/moduledumptest.cc118
-rw-r--r--tests/regression/cgalpngtest/arc-expected.pngbin6782 -> 6641 bytes
-rw-r--r--tests/regression/cgalpngtest/circle-expected.pngbin6979 -> 6282 bytes
-rw-r--r--tests/regression/cgalpngtest/circle-tests-expected.pngbin8435 -> 7373 bytes
-rw-r--r--tests/regression/cgalpngtest/cylinder-tests-expected.pngbin11017 -> 11431 bytes
-rw-r--r--tests/regression/cgalpngtest/example024-expected.pngbin0 -> 5797 bytes
-rw-r--r--tests/regression/cgalpngtest/highlight-modifier-expected.pngbin12436 -> 12437 bytes
-rw-r--r--tests/regression/cgalpngtest/ifelse-tests-expected.pngbin9264 -> 8135 bytes
-rw-r--r--tests/regression/cgalpngtest/rotate_extrude-tests-expected.pngbin20273 -> 17684 bytes
-rw-r--r--tests/regression/cgalpngtest/sphere-tests-expected.pngbin16818 -> 16561 bytes
-rw-r--r--tests/regression/csgtermtest/allexpressions-expected.txt1
-rw-r--r--tests/regression/csgtermtest/allmodules-expected.txt2
-rw-r--r--tests/regression/csgtexttest/allexpressions-expected.txt1
-rw-r--r--tests/regression/csgtexttest/allmodules-expected.txt2
-rw-r--r--tests/regression/dumptest/allexpressions-expected.txt1
-rw-r--r--tests/regression/dumptest/allmodules-expected.txt5
-rw-r--r--tests/regression/dumptest/background-modifier-expected.txt12
-rw-r--r--tests/regression/dumptest/circle-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/cylinder-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/example024-expected.txt1869
-rw-r--r--tests/regression/dumptest/highlight-and-background-modifier-expected.txt55
-rw-r--r--tests/regression/dumptest/highlight-modifier-expected.txt12
-rw-r--r--tests/regression/dumptest/ifelse-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/linear_extrude-tests-expected.txt5
-rw-r--r--tests/regression/dumptest/rotate_extrude-tests-expected.txt7
-rw-r--r--tests/regression/dumptest/sphere-tests-expected.txt3
-rw-r--r--tests/regression/echotest/value-reassignment-tests-expected.txt3
-rw-r--r--tests/regression/echotest/value-reassignment-tests2-expected.txt1
-rw-r--r--tests/regression/echotest/variable-scope-tests-expected.txt2
-rw-r--r--tests/regression/moduledumptest/allexpressions-expected.txt31
-rw-r--r--tests/regression/moduledumptest/allfunctions-expected.txt29
-rw-r--r--tests/regression/moduledumptest/allmodules-expected.txt41
-rw-r--r--tests/regression/opencsgtest/arc-expected.pngbin7166 -> 6711 bytes
-rw-r--r--tests/regression/opencsgtest/background-modifier-expected.pngbin17797 -> 17763 bytes
-rw-r--r--tests/regression/opencsgtest/circle-expected.pngbin7115 -> 6464 bytes
-rw-r--r--tests/regression/opencsgtest/circle-tests-expected.pngbin8880 -> 8418 bytes
-rw-r--r--tests/regression/opencsgtest/cylinder-tests-expected.pngbin11612 -> 12080 bytes
-rw-r--r--tests/regression/opencsgtest/difference-tests-expected.pngbin11900 -> 11897 bytes
-rw-r--r--tests/regression/opencsgtest/example024-expected.pngbin0 -> 5031 bytes
-rw-r--r--tests/regression/opencsgtest/highlight-and-background-modifier-expected.pngbin22188 -> 25034 bytes
-rw-r--r--tests/regression/opencsgtest/highlight-modifier-expected.pngbin14172 -> 16507 bytes
-rw-r--r--tests/regression/opencsgtest/ifelse-tests-expected.pngbin9650 -> 8465 bytes
-rw-r--r--tests/regression/opencsgtest/rotate_extrude-tests-expected.pngbin21093 -> 18320 bytes
-rw-r--r--tests/regression/opencsgtest/sphere-tests-expected.pngbin18173 -> 17564 bytes
-rw-r--r--tests/regression/throwntogethertest/arc-expected.pngbin2758 -> 6711 bytes
-rw-r--r--tests/regression/throwntogethertest/background-modifier-expected.pngbin17797 -> 17772 bytes
-rw-r--r--tests/regression/throwntogethertest/child-background-expected.pngbin0 -> 13893 bytes
-rw-r--r--tests/regression/throwntogethertest/circle-expected.pngbin2837 -> 6464 bytes
-rw-r--r--tests/regression/throwntogethertest/circle-tests-expected.pngbin8880 -> 8418 bytes
-rw-r--r--tests/regression/throwntogethertest/cylinder-tests-expected.pngbin11612 -> 12080 bytes
-rw-r--r--tests/regression/throwntogethertest/difference-tests-expected.pngbin11762 -> 11748 bytes
-rw-r--r--tests/regression/throwntogethertest/example024-expected.pngbin0 -> 7158 bytes
-rw-r--r--tests/regression/throwntogethertest/highlight-and-background-modifier-expected.pngbin0 -> 28740 bytes
-rw-r--r--tests/regression/throwntogethertest/highlight-modifier-expected.pngbin13335 -> 17975 bytes
-rw-r--r--tests/regression/throwntogethertest/ifelse-tests-expected.pngbin4999 -> 8474 bytes
-rw-r--r--tests/regression/throwntogethertest/rotate_extrude-tests-expected.pngbin6429 -> 8198 bytes
-rw-r--r--tests/regression/throwntogethertest/sphere-tests-expected.pngbin18173 -> 17564 bytes
-rwxr-xr-xtests/virtualfb.sh6
-rw-r--r--win.pri (renamed from win32.pri)0
156 files changed, 3916 insertions, 660 deletions
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 56b92d9..455515c 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,31 +1,54 @@
-OpenSCAD 2013.XX
+OpenSCAD 2013.06
================
-Features:
-o Recursive modules and functions is now supported (including cascading child() operations)
+Language Features:
+o linear_extrude now takes a scale parameter:
+ linear_extrude(height=a, slices=b, twist=c, scale=[x,y])
+o Recursive use of modules is now supported (including cascading child() operations):
+ https://github.com/openscad/openscad/blob/master/examples/example024.scad
o Parameter list values can now depend on earlier values, e.g. for (i=[0:2], j=[0:i]) ..
-o Console output is now enabled on Windows through the openscad.com executable
+o value assignments in parameters can now depend on already declared parameters
+o Added resize() module:
+ http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#resize
+
+Program Features:
o Added basic syntax highlighting in the editor
+o There is now a built-in library path in user-space:
+ http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Library_Locations
+o Commandline output to PNG, with various camera and rendering settings.
+ Run openscad -h to see usage info or see the OpenSCAD wiki user manual.
+o Attempting to open dxf, off or stl files in the GUI will now create an import statement.
+o The preview operator (%) will now preserve any manually set color
+o The highlight operator (#) will now color the object in transparent red
o Mac: Added document icon
o Mac: Added auto-update check
-o Commandline output to PNG, with various camera and rendering settings
-o resize() command introduced
-o Regression test now creates single monolithic .html file for easier uploading
-o value reassignment is now less strict
-o value assignments in parameters can now depend on already declared parameters
-o Attempting to open dxf, off or stl files in the GUI will now create an import statement.
+o Windows: Better cmd-line support using the openscad.com executable
Bugfixes:
o Importing files is now always relative to the importing script, also for libraries
-o OpenCSG rendering sometimes crashed when rendering large models
o We didn't always print a warning when CSG normalization created too many elements
o Binary STLs can now be read on big endian architectures
o Some binary STLs couldn't be read
o Fixed some issues related to ARM builds
-o Changed multmatrix floating-point output to improve dumptest portability
-o Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine
o CGAL triangulation more lenient- enables partial rendering of 'bad' DXF data
-o Fixes problem where local changes are overwritten on automatic reload when included files has changed.
+o The Automatic Reload feature is now more robust
+o If a file couldn't be saved it no longer fails silently
+o Fixed a number of crashes related to CGAL and OpenCSG rendering or complex models
+o The lookup() function had bad boundary condition behavior
+o The surface() module failed when the .dat file lacked a trailing newline
+o The hull() module could crash if any of the children were empty objects
+o Some problems using unicode filenames have been fixed
+
+Misc:
+o Build scripts have been further improved
+o Regression test now creates single monolithic .html file for easier uploading
+o Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine
+o The backend is finally independent of Qt
+o Windows: We now have a 64-bit version
+
+Known Bugs:
+o Linux: command-line png rendering on Gallium is flaky.
+ Workaround: use CGAL --render or hardware rendering.
OpenSCAD 2013.01
================
diff --git a/bison.pri b/bison.pri
index d2972d6..f28c6e0 100644
--- a/bison.pri
+++ b/bison.pri
@@ -3,7 +3,7 @@
bison.input = BISONSOURCES
bison.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp
bison.commands = bison -d -p ${QMAKE_FILE_BASE} -o ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp ${QMAKE_FILE_IN}
- bison.commands += && mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h
+ bison.commands += && if [ -e ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ]; then mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h ; fi
bison.CONFIG += target_predeps
bison.variable_out = GENERATED_SOURCES
silent:bison.commands = @echo Bison ${QMAKE_FILE_IN} && $$bison.commands
@@ -11,7 +11,7 @@
bison_header.input = BISONSOURCES
bison_header.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h
bison_header.commands = bison -d -p ${QMAKE_FILE_BASE} -o ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp ${QMAKE_FILE_IN}
- bison_header.commands += && mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h
+ bison_header.commands += && if [ -e ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ]; then mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h ; fi
bison_header.CONFIG += target_predeps no_link
silent:bison_header.commands = @echo Bison ${QMAKE_FILE_IN} && $$bison.commands
QMAKE_EXTRA_COMPILERS += bison_header
diff --git a/boost.pri b/boost.pri
index f3619a0..2084c89 100644
--- a/boost.pri
+++ b/boost.pri
@@ -6,7 +6,7 @@ boost {
!isEmpty(BOOST_DIR) {
QMAKE_INCDIR += $$BOOST_DIR
message("boost location: $$BOOST_DIR")
- win32: QMAKE_LIBDIR += -L$$BOOST_DIR/lib
+ win*: QMAKE_LIBDIR += -L$$BOOST_DIR/lib
}
CONFIG(mingw-cross-env) {
@@ -16,7 +16,7 @@ boost {
BOOST_LINK_FLAGS = -lboost_thread_win32-mt -lboost_program_options-mt -lboost_filesystem-mt -lboost_system-mt -lboost_regex-mt -lboost_chrono-mt
}
- isEmpty(BOOST_LINK_FLAGS):win32 {
+ isEmpty(BOOST_LINK_FLAGS):win* {
BOOST_LINK_FLAGS = -llibboost_thread-vc90-mt-s-1_46_1 -llibboost_program_options-vc90-mt-s-1_46_1 -llibboost_filesystem-vc90-mt-s-1_46_1 -llibboost_system-vc90-mt-s-1_46_1 -llibboost_regex-vc90-mt-s-1_46_1
}
diff --git a/cgal.pri b/cgal.pri
index ae532f0..241332a 100644
--- a/cgal.pri
+++ b/cgal.pri
@@ -6,7 +6,7 @@ cgal {
CGAL_DIR = $$(CGALDIR)
!isEmpty(CGAL_DIR) {
QMAKE_INCDIR += $$CGAL_DIR/include
- win32: QMAKE_INCDIR += $$CGAL_DIR/auxiliary/gmp/include
+ win*: QMAKE_INCDIR += $$CGAL_DIR/auxiliary/gmp/include
QMAKE_LIBDIR += $$CGAL_DIR/lib
message("CGAL location: $$CGAL_DIR")
}
@@ -15,7 +15,7 @@ cgal {
LIBS += -lgmp -lmpfr -lCGAL
QMAKE_CXXFLAGS += -frounding-math
} else {
- win32 {
+ win* {
*-g++* {
QMAKE_CXXFLAGS += -frounding-math
}
diff --git a/common.pri b/common.pri
index 71aa510..7153ded 100644
--- a/common.pri
+++ b/common.pri
@@ -3,7 +3,7 @@ MOC_DIR = objects
UI_DIR = objects
RCC_DIR = objects
-include(win32.pri)
+include(win.pri)
include(flex.pri)
include(bison.pri)
include(cgal.pri)
diff --git a/contrib/scad-mode.el b/contrib/scad-mode.el
index 5961b08..68a47e6 100644
--- a/contrib/scad-mode.el
+++ b/contrib/scad-mode.el
@@ -66,7 +66,7 @@
"round" "ceil" "floor"
"pow" "sqrt" "exp" "log" "ln"
"str"
- "lookup" "version" "version_num"
+ "lookup" "version" "version_num" "len" "search"
"dxf_dim" "dxf_cross" ;;dxfdim.cc
)
"SCAD functions."
@@ -74,7 +74,7 @@
:group 'scad-font-lock)
(defcustom scad-modules
- '("child" "echo" "assign" "for" "intersection_for" "if" ;;control.cc
+ '("child" "echo" "assign" "for" "intersection_for" "if" "else" ;;control.cc
"cube" "sphere" "cylinder" "polyhedron" "square" "circle" "polygon" ;;primitives.cc
"scale" "rotate" "translate" "mirror" "multmatrix" ;;transform.cc
"union" "difference" "intersection" ;;csgops.cc
@@ -86,7 +86,7 @@
"import_stl" "import_off" "import_dxf" "import" ;;import.cc
"group" ;;builtin.cc
"projection" ;;projection.cc
- "minkowski" "glide" "subdiv" "hull" ;;cgaladv.cc
+ "minkowski" "glide" "subdiv" "hull" "resize" ;;cgaladv.cc
)
"SCAD modules."
:type 'list
diff --git a/doc/TODO.txt b/doc/TODO.txt
index d05df2c..e56dbd7 100644
--- a/doc/TODO.txt
+++ b/doc/TODO.txt
@@ -227,7 +227,7 @@ DOCUMENTATION
-------------
o Auto-generate API documentation instead of, in addition to or combined with, the wikibooks docs.
o Write checklists for typical extension work (add new module, add new function)
- -> make sure new test files are added
+ -> syntax highlighter, test files, examples, documentation (external editor modes)
o Clarify include/use better in the wikibook docs (e.g. that use'd modules have to be self-contained)
TESTING
diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt
index f5bb759..d239278 100644
--- a/doc/release-checklist.txt
+++ b/doc/release-checklist.txt
@@ -1,6 +1,19 @@
OpenSCAD Release Checklist
--------------------------
+o Pre-release preparations
+ - Merge MCAD
+ o In MCAD clone:
+ $ git fetch upstream
+ $ git merge upstream/master
+ $ git push
+ o In OpenSCAD:
+ $ cd libraries/MCAD
+ $ git pull
+ $ cd ../..
+ $ git commit -m "Updated MCAD"
+ $ git push
+
(See bottom of this file for how to build release binaries)
o Set VERSION and VERSIONDATE environment variable
@@ -31,19 +44,24 @@ o git push --tags
o Upload Source package
$ ./scripts/googlecode_upload.py -s 'Source Code' -p openscad -l Featured,Type-Source openscad-$VERSION.src.tar.gz
+ $ scp openscad-$VERSION.src.tar.gz openscad@files.openscad.org:www
o Remove VERSION environment variable
$ unset VERSION
+o Write release email/blog entry
o Update web page
-o Write email to mailing list
+ - news.html
+ - inc/src_release_links.js
o Update external resources:
- http://en.wikipedia.org/wiki/OpenSCAD
+o Write to mailing list
+o Tweet
o Notify package managers
- Ubuntu: https://launchpad.net/~chrysn
- Fedora: Miro Hrončok <miro@hroncok.cz> or <mhroncok@redhat.com>
- - MacPorts:
-
+ - OpenSUSE: Pavol Rusnak <prusnak@opensuse.org>
+ - MacPorts: Frank Schima <macports2000@gmail.com>
Build and Upload Release Binaries
---------------------------------
@@ -66,10 +84,6 @@ Linux:
$ ./scripts/googlecode_upload.py -s 'Linux Binaries' -p openscad openscad-$VERSION.x86-ARCH.tar.gz -l Featured,OpSys-Linux,Type-Archive openscad-$VERSION.x86-ARCH.tar.gz
o Update web page with download links
-Windows mingw cross-build: FIXME 32 vs. 64 bit
+Windows mingw cross-build:
- $ ./scripts/publish-mingw-x.sh -> mingw32/Openscad-$VERSION.zip
- -> mingw32/Openscad-$VERSION-Installer.exe
- $ ./scripts/googlecode_upload.py -s 'Windows Binaries' -p openscad OpenSCAD-$VERSION.zip -l Featured,OpSys-Windows,Type-Archive OpenSCAD-$VERSION.zip
- $ ./scripts/googlecode_upload.py -s 'Windows Installer' -p openscad OpenSCAD-$VERSION-Installer.exe -l Featured,OpSys-Windows,Type-Installer OpenSCAD-$VERSION-Installer.exe
- o Update web page with download links
+FIXME: Adapt scripts/builder.sh to build release binaries
diff --git a/examples/example017.scad b/examples/example017.scad
index 9013d4e..4279546 100644
--- a/examples/example017.scad
+++ b/examples/example017.scad
@@ -1,6 +1,6 @@
// To render the DXF file from the command line:
-// openscad -x example017.dxf -D'mode="parts"' example017.scad
+// openscad -o example017.dxf -D'mode="parts"' example017.scad
// mode = "parts";
// mode = "exploded";
diff --git a/libraries/MCAD b/libraries/MCAD
-Subproject 9af89906fa87e2c7fa21f914f00f3fe3879c69f
+Subproject 9a958fd11b0a6b5f8becd37c4f8a42f585abfcd
diff --git a/mingw-cross-env.pri b/mingw-cross-env.pri
index e696b56..07a0fc1 100644
--- a/mingw-cross-env.pri
+++ b/mingw-cross-env.pri
@@ -13,6 +13,9 @@ CONFIG(mingw-cross-env) {
LIBS += mingw-cross-env/lib/libgmp.a
LIBS += mingw-cross-env/lib/libCGAL.a
QMAKE_CXXFLAGS += -fpermissive
+ WINSTACKSIZE = 8388608 # 8MB # github issue 116
+ QMAKE_CXXFLAGS += -Wl,--stack,$$WINSTACKSIZE
+ LIBS += -Wl,--stack,$$WINSTACKSIZE
QMAKE_DEL_FILE = rm -f
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs #eigen3
}
diff --git a/openscad.pro b/openscad.pro
index 542bcc3..fd9f494 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -31,7 +31,7 @@ isEmpty(QT_VERSION) {
include(version.pri)
# for debugging link problems (use nmake -f Makefile.Release > log.txt)
-win32 {
+win* {
# QMAKE_LFLAGS += -VERBOSE
}
debug: DEFINES += DEBUG
@@ -88,7 +88,7 @@ else {
TARGET = openscad
}
-win32 {
+win* {
RC_FILE = openscad_win32.rc
}
@@ -107,6 +107,7 @@ netbsd* {
QMAKE_LFLAGS += -L/usr/X11R7/lib
QMAKE_LFLAGS += -Wl,-R/usr/X11R7/lib
QMAKE_LFLAGS += -Wl,-R/usr/pkg/lib
+ !clang: { QMAKE_CXXFLAGS += -std=c++0x }
!isEmpty(OPENSCAD_LIBDIR) {
QMAKE_CFLAGS = -I$$OPENSCAD_LIBDIR/include $$QMAKE_CFLAGS
QMAKE_CXXFLAGS = -I$$OPENSCAD_LIBDIR/include $$QMAKE_CXXFLAGS
@@ -171,7 +172,7 @@ CONFIG(mingw-cross-env) {
include(mingw-cross-env.pri)
}
-win32 {
+win* {
FLEXSOURCES = src/lexer.l
BISONSOURCES = src/parser.y
} else {
@@ -302,6 +303,7 @@ SOURCES += src/version_check.cc \
src/parsersettings.cc \
src/stl-utils.cc \
src/boost-utils.cc \
+ src/PlatformUtils.cc \
\
src/nodedumper.cc \
src/traverser.cc \
@@ -347,7 +349,7 @@ macx {
SOURCES += src/imageutils-macosx.cc
OBJECTIVE_SOURCES += src/OffscreenContextCGL.mm
}
-win32* {
+win* {
SOURCES += src/imageutils-lodepng.cc
SOURCES += src/OffscreenContextWGL.cc
}
@@ -384,7 +386,14 @@ macx {
src/EventFilter.h \
src/CocoaUtils.h
SOURCES += src/AppleEvents.cc
- OBJECTIVE_SOURCES += src/CocoaUtils.mm
+ OBJECTIVE_SOURCES += src/CocoaUtils.mm \
+ src/PlatformUtils-mac.mm
+}
+unix:!macx {
+ SOURCES += src/PlatformUtils-posix.cc
+}
+win* {
+ SOURCES += src/PlatformUtils-win.cc
}
isEmpty(PREFIX):PREFIX = /usr/local
diff --git a/scripts/builder.sh b/scripts/builder.sh
index 6a143e3..b00919f 100755
--- a/scripts/builder.sh
+++ b/scripts/builder.sh
@@ -11,6 +11,9 @@
# todo - make linux work
#
# todo - detect failure and stop
+#
+# todo - generalize to build release binaries as well
+#
init_variables()
{
@@ -94,8 +97,11 @@ upload_win_generic()
if [ $DRYRUN ]; then
echo dry run, not uploading to googlecode
echo cmd - python ./scripts/googlecode_upload.py -s '"'$summary'"' $opts
+ echo dry run, not uploading to files.openscad.org
+ echo scp -v $filename openscad@files.openscad.org:www/
else
python ./scripts/googlecode_upload.py -s "$summary" $opts
+ scp -v $filename openscad@files.openscad.org:www/
fi
}
@@ -173,22 +179,23 @@ update_win_www_download_links()
cd inc
echo `pwd`
BASEURL='https://openscad.googlecode.com/files/'
+ # BASEURL='http://files.openscad.org'
DATECODE=`date +"%Y.%m.%d"`
rm win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
- echo "snapinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
-
- echo "snapinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js
- echo "snapinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js
- echo "snapinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
- echo "snapinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
- echo "snapinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
- echo "snapinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
+
+ echo "fileinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js
+ echo "fileinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js
+ echo "fileinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
+ echo "fileinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
echo 'modified win_snapshot_links.js'
PAGER=cat git diff
@@ -200,6 +207,9 @@ update_win_www_download_links()
fi
}
+# FIXME: We might be running this locally and not need an ssh agent.
+# Before checking $SSH_AUTH_SOCK, try 'ssh -T git@github.com' to verify that we
+# can access github over ssh
check_ssh_agent()
{
if [ $DRYRUN ]; then echo 'skipping ssh, dry run'; return; fi
diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh
index ef8c904..5fddb13 100755
--- a/scripts/check-dependencies.sh
+++ b/scripts/check-dependencies.sh
@@ -87,11 +87,16 @@ mpfr_sysver()
gmp_sysver()
{
# on some systems you have VERSION in gmp-$arch.h not gmp.h. use gmp*.h
- if [ ! -e $1/include ]; then return; fi
- gmppaths=`ls $1/include | grep ^gmp`
+ if [ -e $1/include/multiarch-x86_64-linux ]; then
+ subdir=include/multiarch-x86_64-linux
+ else
+ subdir=include
+ fi
+ if [ ! -e $1/$subdir ]; then return; fi
+ gmppaths=`ls $1/$subdir | grep ^gmp`
if [ ! "$gmppaths" ]; then return; fi
for gmpfile in $gmppaths; do
- gmppath=$1/include/$gmpfile
+ gmppath=$1/$subdir/$gmpfile
if [ "`grep __GNU_MP_VERSION $gmppath`" ]; then
gmpmaj=`grep "define *__GNU_MP_VERSION *[0-9]*" $gmppath | awk '{print $3}'`
gmpmin=`grep "define *__GNU_MP_VERSION_MINOR *[0-9]*" $gmppath | awk '{print $3}'`
@@ -155,8 +160,15 @@ flex_sysver()
bison_sysver()
{
+ # bison (GNU Bison) 2.7.12-4996
if [ ! -x $1/bin/bison ]; then return ; fi
- bison_sysver_result=`$1/bin/bison --version | grep bison | sed s/"[^0-9.]"/" "/g`
+ bison_sver=`$1/bin/bison --version | grep bison`
+ debug bison_sver1: $bison_sver
+ bison_sver=`echo $bison_sver | awk -F ")" ' { print $2 } '`
+ debug bison_sver2: $bison_sver
+ bison_sver=`echo $bison_sver | awk -F "-" ' { print $1 } '`
+ debug bison_sver3: $bison_sver
+ bison_sysver_result=$bison_sver
}
gcc_sysver()
@@ -425,7 +437,7 @@ find_installed_version()
debug $depname"_sysver" $syspath
eval $depname"_sysver" $syspath
fsv_tmp=`eval echo "$"$depname"_sysver_result"`
- if [ $fsv_tmp ]; then break; fi
+ if [ $fsv_tmp ]; then break; fi
fi
done
fi
@@ -511,6 +523,7 @@ main()
deps="qt4 cgal gmp mpfr boost opencsg glew eigen gcc bison flex make"
#deps="$deps curl git" # not technically necessary for build
#deps="$deps python cmake imagemagick" # only needed for tests
+ #deps="cgal"
pretty_print title
for depname in $deps; do
debug "processing $dep"
diff --git a/scripts/git-archive-all.py b/scripts/git-archive-all.py
index ccfb08a..0088e1a 100755
--- a/scripts/git-archive-all.py
+++ b/scripts/git-archive-all.py
@@ -1,171 +1,464 @@
#! /usr/bin/env python
+# coding=utf-8
+
+from __future__ import print_function
+from __future__ import unicode_literals
+
+__version__ = "1.7"
import sys
-from os import path, chdir
-from subprocess import Popen, PIPE
-from sys import argv, stdout
-from fnmatch import fnmatch
+from os import path, extsep
+from subprocess import Popen, PIPE, CalledProcessError
class GitArchiver(object):
"""
GitArchiver
-
+
Scan a git repository and export all tracked files, and submodules.
Checks for .gitattributes files in each directory and uses 'export-ignore'
pattern entries for ignore files in the archive.
-
- Automatically detects output format extension: zip, tar, bz2, or gz
+
+ Automatically detects output format extension: zip, tar, bz2, or gz.
"""
-
- def __init__(self, prefix='', verbose=False, exclude=True, extra=[]):
- self.prefix = prefix
- self.verbose = verbose
- self.exclude = exclude
- self.extra = extra
-
- self._excludes = []
+ def __init__(self, prefix='', verbose=False, exclude=True, force_sub=False, extra=None, main_repo_abspath=None):
+ """
+ @type prefix: string
+ @param prefix: Prefix used to prepend all paths in the resulting archive.
+
+ @type verbose: bool
+ @param verbose: Determines verbosity of the output (stdout).
+
+ @type exclude: bool
+ @param exclude: Determines whether archiver should follow rules specified in .gitattributes files.
+ Defaults to True.
+
+ @type force_sub: bool
+ @param force_sub: Determines whether submodules are initialized and updated before archiving.
+ Defaults to False
- def create(self, output_file):
+ @type extra: list
+ @param extra: List of extra paths to include in the resulting archive.
+
+ @type main_repo_abspath: string
+ @param main_repo_abspath: Absolute path to the main repository (or one of subdirectories).
+ If None, current cwd is used.
+ If given path is path to a subdirectory (but not a submodule directory!)
+ it will be replaced with abspath to toplevel directory of the repository.
"""
- create(str output_file) -> None
-
- Creates the archive, written to the given output_file
- Filetype may be one of: gz, zip, bz2, tar
+ if extra is None:
+ extra = []
+
+ if main_repo_abspath is None:
+ main_repo_abspath = path.abspath('')
+ elif not path.isabs(main_repo_abspath):
+ raise ValueError("You MUST pass absolute path to the main git repository.")
+
+ # Raises an exception if there is no repo under main_repo_abspath.
+ try:
+ self.run_shell("[ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1", main_repo_abspath)
+ except Exception as e:
+ raise ValueError("Not a git repository (or any of the parent directories).".format(path=main_repo_abspath))
+
+ # Detect toplevel directory of the repo.
+ main_repo_abspath = path.abspath(self.read_git_shell('git rev-parse --show-toplevel', main_repo_abspath).rstrip())
+
+ self.prefix = prefix
+ self.verbose = verbose
+ self.exclude = exclude
+ self.extra = extra
+ self.force_sub = force_sub
+ self.main_repo_abspath = main_repo_abspath
+
+ def create(self, output_path, dry_run=False, output_format=None):
"""
- #
- # determine the format
- #
- _, _, format = output_file.rpartition(".")
+ Creates the archive, written to the given output_file_path
+
+ Type of the archive is determined either by extension of output_file_path or by the format argument.
+ Supported formats are: gz, zip, bz2, tar, tgz
+
+ @type output_path: string
+ @param output_path: Output file path.
- if format.lower() == 'zip':
+ @type dry_run: bool
+ @param dry_run: Determines whether create should do nothing but print what it would archive.
+
+ @type output_format: string
+ @param output_format: Determines format of the output archive.
+ If None, format is determined from extension of output_file_path.
+ """
+ if output_format is None:
+ file_name, file_ext = path.splitext(output_path)
+ output_format = file_ext[len(extsep):].lower()
+
+ if output_format == 'zip':
from zipfile import ZipFile, ZIP_DEFLATED
- output_archive = ZipFile(path.abspath(output_file), 'w')
- add = lambda name, arcname: output_archive.write(name, self.prefix + arcname, ZIP_DEFLATED)
-
- elif format.lower() in ['tar', 'bz2', 'gz']:
+
+ if not dry_run:
+ archive = ZipFile(path.abspath(output_path), 'w')
+ add = lambda file_path, file_name: archive.write(file_path, path.join(self.prefix, file_name), ZIP_DEFLATED)
+ elif output_format in ['tar', 'bz2', 'gz', 'tgz']:
import tarfile
- t_mode = ('w:%s' % format) if format != 'tar' else 'w'
-
- output_archive = tarfile.open(path.abspath(output_file), t_mode)
- add = lambda name, arcname: output_archive.add(name, self.prefix + arcname)
+
+ if output_format == 'tar':
+ t_mode = 'w'
+ elif output_format == 'tgz':
+ t_mode = 'w:gz'
+ else:
+ t_mode = 'w:{f}'.format(f=output_format)
+
+ if not dry_run:
+ archive = tarfile.open(path.abspath(output_path), t_mode)
+ add = lambda file_path, file_name: archive.add(file_path, path.join(self.prefix, file_name))
+ else:
+ raise RuntimeError("Unknown format: {f}".format(f=output_format))
+
+ for file_path in self.extra:
+ if not dry_run:
+ if self.verbose:
+ print("Compressing {f} => {a}...".format(f=file_path,
+ a=path.join(self.prefix, file_path)))
+ add(file_path, file_path)
+ else:
+ print("{f} => {a}".format(f=file_path,
+ a=path.join(self.prefix, file_path)))
+
+ for file_path in self.list_files():
+ if not dry_run:
+ if self.verbose:
+ print("Compressing {f} => {a}...".format(f=path.join(self.main_repo_abspath, file_path),
+ a=path.join(self.prefix, file_path)))
+ add(path.join(self.main_repo_abspath, file_path), file_path)
+ else:
+ print("{f} => {a}".format(f=path.join(self.main_repo_abspath, file_path),
+ a=path.join(self.prefix, file_path)))
+
+ if not dry_run:
+ archive.close()
+
+ def get_path_components(self, repo_abspath, abspath):
+ """
+ Splits given abspath into components until repo_abspath is reached.
+
+ E.g. if repo_abspath is '/Documents/Hobby/ParaView/' and abspath is
+ '/Documents/Hobby/ParaView/Catalyst/Editions/Base/', function will return:
+ ['.', 'Catalyst', 'Editions', 'Base']
+
+ First element is always '.' (concrete symbol depends on OS).
+
+ @type repo_abspath: string
+ @param repo_abspath: Absolute path to the git repository.
+
+ @type abspath: string
+ @param abspath: Absolute path to within repo_abspath.
+
+ @rtype: list
+ @return: List of path components.
+ """
+ components = []
+
+ while not path.samefile(abspath, repo_abspath):
+ abspath, tail = path.split(abspath)
+
+ if len(tail):
+ components.insert(0, tail)
+
+ components.insert(0, path.relpath(repo_abspath, repo_abspath))
+ return components
+
+ def get_exclude_patterns(self, repo_abspath, repo_file_paths):
+ """
+ Returns exclude patterns for a given repo. It looks for .gitattributes files in repo_file_paths.
+
+ Resulting dictionary will contain exclude patterns per path (relative to the repo_abspath).
+ E.g. {('.', 'Catalyst', 'Editions', 'Base'), ['Foo*', '*Bar']}
+
+ @type repo_abspath: string
+ @param repo_abspath: Absolute path to the git repository.
+
+ @type repo_file_paths: list
+ @param repo_file_paths: List of paths relative to the repo_abspath that are under git control.
+
+ @rtype: dict
+ @return: Dictionary representing exclude patterns.
+ Keys are tuples of strings. Values are lists of strings.
+ Returns None if self.exclude is not set.
+ """
+ if not self.exclude:
+ return None
+
+ def read_attributes(attributes_abspath):
+ patterns = []
+ if path.isfile(attributes_abspath):
+ attributes = open(attributes_abspath, 'r').readlines()
+ patterns = []
+ for line in attributes:
+ tokens = line.strip().split()
+ if "export-ignore" in tokens[1:]:
+ patterns.append(tokens[0])
+ return patterns
+
+ exclude_patterns = {(): []}
+
+ # There may be no gitattributes.
+ try:
+ global_attributes_abspath = self.read_shell("git config --get core.attributesfile", repo_abspath).rstrip()
+ exclude_patterns[()] = read_attributes(global_attributes_abspath)
+ except:
+ # And valid to not have them.
+ pass
+
+ for attributes_abspath in [path.join(repo_abspath, f) for f in repo_file_paths if f.endswith(".gitattributes")]:
+ # Each .gitattributes affects only files within its directory.
+ key = tuple(self.get_path_components(repo_abspath, path.dirname(attributes_abspath)))
+ exclude_patterns[key] = read_attributes(attributes_abspath)
+
+ local_attributes_abspath = path.join(repo_abspath, ".git", "info", "attributes")
+ key = tuple(self.get_path_components(repo_abspath, repo_abspath))
+
+ if key in exclude_patterns:
+ exclude_patterns[key].extend(read_attributes(local_attributes_abspath))
else:
- raise RuntimeError("Unknown format: '%s'" % format)
-
- #
- # compress
- #
-
- # extra files first (we may change folder later)
- for name in self.extra:
- if self.verbose:
- toPath = '=> %s%s' % (self.prefix, name) if self.prefix else ""
- print 'Compressing %s %s ...' % (name, toPath)
- add(name, name)
-
- self._excludes = []
-
- for name, arcname in self.listFiles(path.abspath('')):
- if self.verbose:
- toPath = '=> %s%s' % (self.prefix, arcname) if self.prefix else ""
- print 'Compressing %s %s ...' % (arcname, toPath)
- add(name, arcname)
-
- output_archive.close()
-
-
- def listFiles(self, git_repositary_path, baselevel=''):
- """
- listFiles(str git_repository_path, str baselevel='') -> iterator
-
- An iterator method that yields a tuple(filepath, fullpath)
+ exclude_patterns[key] = read_attributes(local_attributes_abspath)
+
+ return exclude_patterns
+
+ def is_file_excluded(self, repo_abspath, repo_file_path, exclude_patterns):
+ """
+ Checks whether file at a given path is excluded.
+
+ @type repo_abspath: string
+ @param repo_abspath: Absolute path to the git repository.
+
+ @type repo_file_path: string
+ @param repo_file_path: Path to a file within repo_abspath.
+
+ @type exclude_patterns: dict
+ @param exclude_patterns: Exclude patterns with format specified for get_exclude_patterns.
+
+ @rtype: bool
+ @return: True if file should be excluded. Otherwise False.
+ """
+ if exclude_patterns is None or not len(exclude_patterns):
+ return False
+
+ from fnmatch import fnmatch
+
+ file_name = path.basename(repo_file_path)
+ components = self.get_path_components(repo_abspath, path.join(repo_abspath, path.dirname(repo_file_path)))
+
+ is_excluded = False
+ # We should check all patterns specified in intermediate directories to the given file.
+ # At the end we should also check for the global patterns (key '()' or empty tuple).
+ while not is_excluded:
+ key = tuple(components)
+ if key in exclude_patterns:
+ patterns = exclude_patterns[key]
+ for p in patterns:
+ if fnmatch(file_name, p) or fnmatch(repo_file_path, p):
+ if self.verbose:
+ print("Exclude pattern matched {pattern}: {path}".format(pattern=p, path=repo_file_path))
+ is_excluded = True
+
+ if not len(components):
+ break
+
+ components.pop()
+
+ return is_excluded
+
+ def list_files(self, repo_path=''):
+ """
+ An iterator method that yields a file path relative to main_repo_abspath
for each file that should be included in the archive.
Skips those that match the exclusion patterns found in
any discovered .gitattributes files along the way.
-
- Recurses into submodules as well.
- """
- for filepath in self.runShell('git ls-files --cached --full-name --no-empty-directory'):
- fullpath = path.join(baselevel, filepath)
- filename = path.basename(filepath)
-
- if self.exclude and filename == '.gitattributes':
- self._excludes = []
- fh = open(filepath, 'r')
- for line in fh:
- if not line: break
- tokens = line.strip().split()
- if 'export-ignore' in tokens[1:]:
- self._excludes.append(tokens[0])
- fh.close()
-
- if not filename.startswith('.git') and not path.isdir(filepath):
-
- # check the patterns first
- ignore = False
- for pattern in self._excludes:
- if fnmatch(fullpath, pattern) or fnmatch(filename, pattern):
- if self.verbose: print 'Exclude pattern matched (%s): %s' % (pattern, fullpath)
- ignore = True
- break
- if ignore:
- continue
-
- # baselevel is needed to tell the arhiver where it have to extract file
- yield filepath, fullpath
-
- # get paths for every submodule
- for submodule in self.runShell("git submodule --quiet foreach 'pwd'"):
- chdir(submodule)
- # in order to get output path we need to exclude repository path from the submodule path
- submodule = submodule[len(git_repositary_path)+1:]
- # recursion allows us to process repositories with more than one level of submodules
- for git_file in self.listFiles(git_repositary_path, submodule):
- yield git_file
-
-
-
+
+ Recurs into submodules as well.
+
+ @type repo_path: string
+ @param repo_path: Path to the git submodule repository within the main git repository.
+
+ @rtype: iterator
+ @return: Iterator to traverse files under git control relative to main_repo_abspath.
+ """
+ repo_abspath = path.join(self.main_repo_abspath, repo_path)
+ repo_file_paths = self.read_git_shell("git ls-files --cached --full-name --no-empty-directory", repo_abspath).splitlines()
+ exclude_patterns = self.get_exclude_patterns(repo_abspath, repo_file_paths)
+
+ for repo_file_path in repo_file_paths:
+ # Git puts path in quotes if file path has unicode characters.
+ repo_file_path = repo_file_path.strip('"') # file path relative to current repo
+ file_name = path.basename(repo_file_path)
+
+ # Only list symlinks and files that don't start with git.
+ if file_name.startswith(".git") or (not path.islink(repo_file_path) and path.isdir(repo_file_path)):
+ continue
+
+ main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo
+
+ if self.is_file_excluded(repo_abspath, repo_file_path, exclude_patterns):
+ continue
+
+ # Yield both repo_file_path and main_repo_file_path to preserve structure of the repo.
+ yield main_repo_file_path
+
+ if self.force_sub:
+ self.run_shell("git submodule init", repo_abspath)
+ self.run_shell("git submodule update", repo_abspath)
+
+ # List files of every submodule.
+ for submodule_path in self.read_shell("git submodule --quiet foreach 'pwd'", repo_abspath).splitlines():
+ # In order to get output path we need to exclude repository path from submodule_path.
+ submodule_path = path.relpath(submodule_path, self.main_repo_abspath)
+ for file_path in self.list_files(submodule_path):
+ yield file_path
+
@staticmethod
- def runShell(cmd):
- return Popen(cmd, shell=True, stdout=PIPE).stdout.read().splitlines()
-
-
-
-if __name__ == "__main__":
+ def run_shell(cmd, cwd=None):
+ """
+ Runs shell command.
+
+ @type cmd: string
+ @param cmd: Command to be executed.
+
+ @type cwd: string
+ @param cwd: Working directory.
+
+ @rtype: int
+ @return: Return code of the command.
+
+ @raise CalledProcessError: Raises exception if return code of the command is non-zero.
+ """
+ p = Popen(cmd, shell=True, cwd=cwd)
+ p.wait()
+
+ if p.returncode:
+ raise CalledProcessError(returncode=p.returncode, cmd=cmd)
+
+ return p.returncode
+
+ @staticmethod
+ def read_shell(cmd, cwd=None, encoding='utf-8'):
+ """
+ Runs shell command and reads output.
+
+ @type cmd: string
+ @param cmd: Command to be executed.
+
+ @type cwd: string
+ @param cwd: Working directory.
+
+ @type encoding: string
+ @param encoding: Encoding used to decode bytes returned by Popen into string.
+
+ @rtype: string
+ @return: Output of the command.
+
+ @raise CalledProcessError: Raises exception if return code of the command is non-zero.
+ """
+ p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
+ output, _ = p.communicate()
+ output = output.decode(encoding)
+
+ if p.returncode:
+ raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output)
+
+ return output
+
+ @staticmethod
+ def read_git_shell(cmd, cwd=None):
+ """
+ Runs git shell command, reads output and decodes it into unicode string
+
+ @type cmd: string
+ @param cmd: Command to be executed.
+
+ @type cwd: string
+ @param cwd: Working directory.
+
+ @rtype: string
+ @return: Output of the command.
+
+ @raise CalledProcessError: Raises exception if return code of the command is non-zero.
+ """
+ p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
+ output, _ = p.communicate()
+ output = output.decode('unicode_escape').encode('raw_unicode_escape').decode('utf-8')
+
+ if p.returncode:
+ raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output)
+
+ return output
+
+
+if __name__ == '__main__':
from optparse import OptionParser
- parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] OUTPUT_FILE", version="%prog 1.0")
-
- parser.add_option('--prefix', type='string', dest='prefix',
- default='', help="prepend PREFIX to each filename in the archive")
-
- parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='enable verbose mode')
-
- parser.add_option('--no-exclude', action='store_false', dest='exclude',
- default=True, help="Dont read .gitattributes files for patterns containing export-ignore attrib")
-
- parser.add_option('--extra', action='append', dest='extra', default=[],
+ parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] [--force-submodules] [--dry-run] OUTPUT_FILE",
+ version="%prog {version}".format(version=__version__))
+
+ parser.add_option('--prefix',
+ type='string',
+ dest='prefix',
+ default='',
+ help="Prepend PREFIX to each filename in the archive. OUTPUT_FILE name is used by default to avoid tarbomb.")
+
+ parser.add_option('-v', '--verbose',
+ action='store_true',
+ dest='verbose',
+ help='Enable verbose mode.')
+
+ parser.add_option('--no-exclude',
+ action='store_false',
+ dest='exclude',
+ default=True,
+ help="Don't read .gitattributes files for patterns containing export-ignore attrib.")
+
+ parser.add_option('--force-submodules',
+ action='store_true',
+ dest='force_sub',
+ help="Force a git submodule init && git submodule update at each level before iterating submodules.")
+
+ parser.add_option('--extra',
+ action='append',
+ dest='extra',
+ default=[],
help="Any additional files to include in the archive.")
+ parser.add_option('--dry-run',
+ action='store_true',
+ dest='dry_run',
+ help="Don't actually archive anything, just show what would be done.")
options, args = parser.parse_args()
-
+
if len(args) != 1:
- parser.error('You must specify exactly one output file')
-
- outFile = args[0]
-
- if path.isdir(outFile):
- parser.error('You cannot use directory as output')
-
- archiver = GitArchiver(options.prefix,
- options.verbose,
- options.exclude,
- options.extra)
-
+ parser.error("You must specify exactly one output file")
+
+ output_file_path = args[0]
+
+ if path.isdir(output_file_path):
+ parser.error("You cannot use directory as output")
+
+ # avoid tarbomb
+ if options.prefix:
+ options.prefix = path.join(options.prefix, '')
+ else:
+ import re
+
+ output_name = path.basename(output_file_path)
+ output_name = re.sub('(\.zip|\.tar|\.tgz|\.gz|\.bz2|\.tar\.gz|\.tar\.bz2)$', '', output_name) or "Archive"
+ options.prefix = path.join(output_name, '')
+
try:
- archiver.create(outFile)
- except Exception, e:
- parser.exit(2, "%s\n" % e)
-
+ archiver = GitArchiver(options.prefix,
+ options.verbose,
+ options.exclude,
+ options.force_sub,
+ options.extra)
+ archiver.create(output_file_path, options.dry_run)
+ except Exception as e:
+ parser.exit(2, "{exception}\n".format(exception=e))
+
sys.exit(0)
diff --git a/scripts/installer.nsi b/scripts/installer.nsi
index 2cbd6d3..fea6563 100644
--- a/scripts/installer.nsi
+++ b/scripts/installer.nsi
@@ -17,6 +17,7 @@ CreateShortCut $SMPROGRAMS\OpenSCAD.lnk $INSTDIR\openscad.exe
WriteUninstaller $INSTDIR\Uninstall.exe
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "DisplayName" "OpenSCAD (remove only)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "UninstallString" "$INSTDIR\Uninstall.exe"
+WriteRegStr HKCR ".scad" "PerceivedType" "text"
SectionEnd
Section "Uninstall"
${unregisterExtension} ".scad" "OpenSCAD_File"
@@ -30,5 +31,6 @@ Delete $INSTDIR\libraries\boxes.scad
Delete $INSTDIR\libraries\shapes.scad
RMDir $INSTDIR\libraries
Delete $INSTDIR\openscad.exe
+Delete $INSTDIR\openscad.com
RMDir $INSTDIR
SectionEnd
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 088d8b4..03f8598 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -238,8 +238,9 @@ build_cgal()
cd $BASEDIR/src
rm -rf CGAL-$version
if [ ! -f CGAL-$version.tar.gz ]; then
- # 4.1
- curl -O https://gforge.inria.fr/frs/download.php/31641/CGAL-$version.tar.gz
+ # 4.2
+ curl -O https://gforge.inria.fr/frs/download.php/32359/CGAL-$version.tar.gz
+ # 4.1 curl -O https://gforge.inria.fr/frs/download.php/31641/CGAL-$version.tar.gz
# 4.1-beta1 curl -O https://gforge.inria.fr/frs/download.php/31348/CGAL-$version.tar.gz
# 4.0.2 curl -O https://gforge.inria.fr/frs/download.php/31175/CGAL-$version.tar.gz
# 4.0 curl -O https://gforge.inria.fr/frs/download.php/30387/CGAL-$version.tar.gz
@@ -306,7 +307,9 @@ build_eigen()
EIGENDIR="none"
if [ $version = "2.0.17" ]; then EIGENDIR=eigen-eigen-b23437e61a07; fi
- if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4; fi
+ if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4;
+ elif [ $version = "3.1.3" ]; then EIGENDIR=eigen-eigen-2249f9c22fe8; fi
+
if [ $EIGENDIR = "none" ]; then
echo Unknown eigen version. Please edit script.
exit 1
@@ -435,12 +438,13 @@ fi
echo "Using basedir:" $BASEDIR
mkdir -p $SRCDIR $DEPLOYDIR
build_qt 4.8.4
-build_eigen 3.1.2
-build_gmp 5.1.1
+# NB! For eigen, also update the path in the function
+build_eigen 3.1.3
+build_gmp 5.1.2
build_mpfr 3.1.2
build_boost 1.53.0
# NB! For CGAL, also update the actual download URL in the function
-build_cgal 4.1
+build_cgal 4.2
build_glew 1.9.0
build_opencsg 1.3.2
if $OPTION_DEPLOY; then
diff --git a/scripts/publish-macosx.sh b/scripts/publish-macosx.sh
index a3b0090..3617570 100755
--- a/scripts/publish-macosx.sh
+++ b/scripts/publish-macosx.sh
@@ -26,9 +26,9 @@ update_www_download_links()
if [ -f $webdir/$incfile ]; then
cd $webdir
- echo "snapinfo['MAC_SNAPSHOT_URL'] = '$BASEURL$packagefile'" > $incfile
- echo "snapinfo['MAC_SNAPSHOT_NAME'] = 'OpenSCAD $version'" >> $incfile
- echo "snapinfo['MAC_SNAPSHOT_SIZE'] = '$filesize'" >> $incfile
+ echo "fileinfo['MAC_SNAPSHOT_URL'] = '$BASEURL$packagefile'" > $incfile
+ echo "fileinfo['MAC_SNAPSHOT_NAME'] = 'OpenSCAD $version'" >> $incfile
+ echo "fileinfo['MAC_SNAPSHOT_SIZE'] = '$filesize'" >> $incfile
echo 'modified mac_snapshot_links.js'
git --no-pager diff
@@ -67,7 +67,7 @@ if [[ $? != 0 ]]; then
exit 1
fi
-SIGNATURE=$(openssl dgst -sha1 -binary < OpenSCAD-$VERSION.dmg | openssl dgst -dss1 -sign dsa_priv.pem | openssl enc -base64)
+SIGNATURE=$(openssl dgst -sha1 -binary < OpenSCAD-$VERSION.dmg | openssl dgst -dss1 -sign $HOME/.ssh/openscad-appcast.pem | openssl enc -base64)
if [[ $VERSION == $VERSIONDATE ]]; then
APPCASTFILE=appcast-snapshots.xml
@@ -90,5 +90,10 @@ if [[ $? != 0 ]]; then
exit 1
fi
+scp OpenSCAD-$VERSION.dmg openscad@files.openscad.org:www
+if [[ $? != 0 ]]; then
+ exit 1
+fi
+
# Update snapshot filename on web page
update_www_download_links version=$VERSION packagefile=OpenSCAD-$VERSION.dmg filesize=$FILESIZE
diff --git a/scripts/release-common.sh b/scripts/release-common.sh
index 7d36907..8a1ed7c 100755
--- a/scripts/release-common.sh
+++ b/scripts/release-common.sh
@@ -52,13 +52,13 @@ elif [[ $OSTYPE == "linux-gnu" ]]; then
fi
if [ "`echo $* | grep mingw32`" ]; then
- OS=LINXWIN
+ OS=UNIX_CROSS_WIN
ARCH=32
echo Mingw-cross build using ARCH=32
fi
if [ "`echo $* | grep mingw64`" ]; then
- OS=LINXWIN
+ OS=UNIX_CROSS_WIN
ARCH=64
echo Mingw-cross build using ARCH=64
fi
@@ -90,7 +90,7 @@ fi
echo "Checking pre-requisites..."
case $OS in
- LINXWIN)
+ UNIX_CROSS_WIN)
MAKENSIS=
if [ "`command -v makensis`" ]; then
MAKENSIS=makensis
@@ -120,10 +120,9 @@ echo "Building openscad-$VERSION ($VERSIONDATE) $CONFIGURATION..."
if [ ! $NUMCPU ]; then
echo "note: you can 'export NUMCPU=x' for multi-core compiles (x=number)";
- NUMCPU=2
-else
- echo "NUMCPU: " $NUMCPU
+ NUMCPU=1
fi
+echo "NUMCPU: " $NUMCPU
CONFIG=deploy
case $OS in
@@ -138,7 +137,7 @@ case $OS in
ZIPARGS="a -tzip"
TARGET=release
;;
- LINXWIN)
+ UNIX_CROSS_WIN)
. ./scripts/setenv-mingw-xbuild.sh $ARCH
TARGET=release
ZIP="zip"
@@ -148,7 +147,7 @@ esac
case $OS in
- LINXWIN)
+ UNIX_CROSS_WIN)
cd $DEPLOYDIR && qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+=$CONFIG CONFIG+=mingw-cross-env CONFIG-=debug ../openscad.pro
cd $OPENSCADDIR
;;
@@ -158,7 +157,7 @@ case $OS in
esac
case $OS in
- LINXWIN)
+ UNIX_CROSS_WIN)
cd $DEPLOYDIR
make clean ## comment out for test-run
cd $OPENSCADDIR
@@ -176,10 +175,17 @@ case $OS in
#if the following files are missing their tried removal stops the build process on msys
touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp
;;
+ UNIX_CROSS_WIN)
+ # kludge to enable paralell make
+ touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.h
+ touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.cpp
+ touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.hpp
+ touch -t 200012121010 $OPENSCADDIR/src/lexer_lex.cpp
+ ;;
esac
case $OS in
- LINXWIN)
+ UNIX_CROSS_WIN)
# make main openscad.exe
cd $DEPLOYDIR
make $TARGET -j$NUMCPU ## comment 4 test
@@ -214,12 +220,12 @@ case $OS in
EXAMPLESDIR=OpenSCAD.app/Contents/Resources/examples
LIBRARYDIR=OpenSCAD.app/Contents/Resources/libraries
;;
- LINXWIN)
+ UNIX_CROSS_WIN)
EXAMPLESDIR=$DEPLOYDIR/openscad-$VERSION/examples/
LIBRARYDIR=$DEPLOYDIR/openscad-$VERSION/libraries/
rm -rf $DEPLOYDIR/openscad-$VERSION
mkdir $DEPLOYDIR/openscad-$VERSION
- ;;
+ ;;
*)
EXAMPLESDIR=openscad-$VERSION/examples/
LIBRARYDIR=openscad-$VERSION/libraries/
@@ -267,7 +273,7 @@ case $OS in
rm -rf openscad-$VERSION
echo "Binary created: openscad-$VERSION.zip"
;;
- LINXWIN)
+ UNIX_CROSS_WIN)
BINFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH.zip
INSTFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH-Installer.exe
diff --git a/scripts/setenv-mingw-xbuild.sh b/scripts/setenv-mingw-xbuild.sh
index d3a014c..a88b752 100644
--- a/scripts/setenv-mingw-xbuild.sh
+++ b/scripts/setenv-mingw-xbuild.sh
@@ -6,6 +6,7 @@
#
# source ./scripts/setenv-mingw-xbuild.sh # 32 bit build
# source ./scripts/setenv-mingw-xbuild.sh 64 # 64 bit build
+# source ./scripts/setenv-mingw-xbuild.sh clean # Clean up exported variables
#
# Prerequisites:
#
diff --git a/scripts/setenv-unibuild.sh b/scripts/setenv-unibuild.sh
index 980fa7b..cb0b0a0 100644
--- a/scripts/setenv-unibuild.sh
+++ b/scripts/setenv-unibuild.sh
@@ -54,7 +54,9 @@ setenv_netbsd()
QMAKESPEC=netbsd-g++
QTDIR=/usr/pkg/qt4
PATH=/usr/pkg/qt4/bin:$PATH
- LD_LIBRARY_PATH=/usr/pkg/qt4/lib:/usr/X11R7/lib:$LD_LIBRARY_PATH
+ LD_LIBRARY_PATH=/usr/pkg/qt4/lib:$LD_LIBRARY_PATH
+ LD_LIBRARY_PATH=/usr/X11R7/lib:$LD_LIBRARY_PATH
+ LD_LIBRARY_PATH=/usr/pkg/lib:$LD_LIBRARY_PATH
export QMAKESPEC
export QTDIR
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index 60dbb74..6596c8a 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -290,11 +290,26 @@ build_cgal()
ver3_7="curl --insecure -O https://gforge.inria.fr/frs/download.php/27641/CGAL-3.7.tar.gz"
vernull="echo already downloaded..skipping"
download_cmd=ver`echo $version | sed s/"\."/"_"/`
- if [ -e CGAL-$version.tar.gz ]; then download_cmd=vernull; fi
- if [ -e CGAL-$version.tar.bz2 ]; then download_cmd=vernull; fi
+
+ if [ -e CGAL-$version.tar.gz ]; then
+ download_cmd=vernull;
+ fi
+ if [ -e CGAL-$version.tar.bz2 ]; then
+ download_cmd=vernull;
+ fi
+
`eval echo "$"$download_cmd`
- if [ -e CGAL-$version.tar.gz ]; then tar xf CGAL-$version.tar.gz; fi
- if [ -e CGAL-$version.tar.bz2 ]; then tar xf CGAL-$version.tar.bz2; fi
+
+ zipper=gzip
+ suffix=gz
+ if [ -e CGAL-$version.tar.bz2 ]; then
+ zipper=bzip2
+ suffix=bz2
+ fi
+
+ $zipper -f -d CGAL-$version.tar.$suffix;
+ tar xf CGAL-$version.tar
+
cd CGAL-$version
# older cmakes have buggy FindBoost that can result in
diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh
index e2fdaa7..31337c8 100755
--- a/scripts/uni-get-dependencies.sh
+++ b/scripts/uni-get-dependencies.sh
@@ -7,8 +7,9 @@
get_fedora_deps()
{
sudo yum install qt-devel bison flex eigen2-devel python-paramiko \
- boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc pkgconfig \
- git libXmu-devel curl imagemagick
+ boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
+ opencsg-devel git libXmu-devel curl imagemagick ImageMagick make \
+ xorg-x11-server-Xvfb
}
get_qomo_deps()
@@ -34,7 +35,8 @@ get_freebsd_deps()
get_netbsd_deps()
{
sudo pkgin install bison boost cmake git bash eigen flex gmake gmp mpfr \
- qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl imagemagick
+ qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl \
+ imagemagick ImageMagick
}
get_opensuse_deps()
@@ -48,7 +50,7 @@ get_mageia_deps()
sudo urpmi ctags
sudo urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \
libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \
- cmake imagemagick python curl git
+ cmake imagemagick python curl git x11-server-xvfb
}
get_debian_deps()
@@ -74,6 +76,8 @@ if [ -e /etc/issue ]; then
get_debian_deps
elif [ "`grep -i debian /etc/issue`" ]; then
get_debian_deps
+ elif [ "`grep -i mint /etc/issue`" ]; then
+ get_debian_deps
elif [ "`grep -i suse /etc/issue`" ]; then
get_opensuse_deps
elif [ "`grep -i fedora /etc/issue`" ]; then
diff --git a/src/AboutDialog.html b/src/AboutDialog.html
index 005f61f..b5a5d7c 100644
--- a/src/AboutDialog.html
+++ b/src/AboutDialog.html
@@ -7,7 +7,8 @@
make to do your testing. -->
<head>
- <meta name="qrichtext" content="1" />
+ <meta charset="UTF-8"/>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
@@ -74,18 +75,23 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
<b>OpenSCAD Github Project members (public)</b>
</p>
-<lu>
+<ul>
<li><a href="https://github.com/kintel">Marius Kintel </a>
<li><a href="http://clifford.at">Clifford Wolf</a>
<li><a href="http://www.github.com/GilesBathgate">Giles Bathgate</a>
<li><a href="https://github.com/brad">Brad Pitcher</a>
<li><a href="https://github.com/donbright">Don Bright</a>
-</lu>
+</ul>
<p>
-<b><a href="http://www.debian.org">Debian</a> maintainer:</b>
- <a href="http://christian.amsuess.com/">chrysn</a>
+<b>Package Maintainers</b>
</p>
+<ul>
+<li><a href="http://www.debian.org">Debian</a> maintainer:</b>
+ <a href="http://christian.amsuess.com/">chrysn</a></lu>
+<li><a href="http://fedoraproject.org/">Fedora</a> maintainer:</b>
+ <a href="http://hroncok.cz/">Miro Hrončok</a></lu>
+</ul>
<p>
<b>Patches</b>
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index 8417636..8e1b5eb 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -280,7 +280,7 @@ Response CGALEvaluator::visit(State &state, const CsgNode &node)
if (state.isPostfix()) {
CGAL_Nef_polyhedron N;
if (!isCached(node)) {
- CGALEvaluator::CsgOp op;
+ CGALEvaluator::CsgOp op = CGE_UNION;
switch (node.type) {
case CSG_TYPE_UNION:
op = CGE_UNION;
diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc
index 4624d4c..a6b654c 100644
--- a/src/CSGTermEvaluator.cc
+++ b/src/CSGTermEvaluator.cc
@@ -57,6 +57,7 @@ void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluato
}
}
if (t1 && node.modinst->isHighlight()) {
+ t1->flag = CSGTerm::FLAG_HIGHLIGHT;
this->highlights.push_back(t1);
}
if (t1 && node.modinst->isBackground()) {
@@ -95,6 +96,7 @@ static shared_ptr<CSGTerm> evaluate_csg_term_from_ps(const State &state,
stream << node.name() << node.index();
shared_ptr<CSGTerm> t(new CSGTerm(ps, state.matrix(), state.color(), stream.str()));
if (modinst->isHighlight()) {
+ t->flag = CSGTerm::FLAG_HIGHLIGHT;
highlights.push_back(t);
}
if (modinst->isBackground()) {
@@ -124,7 +126,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node)
Response CSGTermEvaluator::visit(State &state, const CsgNode &node)
{
if (state.isPostfix()) {
- CsgOp op;
+ CsgOp op = CSGT_UNION;
switch (node.type) {
case CSG_TYPE_UNION:
op = CSGT_UNION;
diff --git a/src/CocoaUtils.h b/src/CocoaUtils.h
index ad5518b..8543d84 100644
--- a/src/CocoaUtils.h
+++ b/src/CocoaUtils.h
@@ -1,13 +1,10 @@
#ifndef COCOAUTILS_H_
#define COCOAUTILS_H_
-#include <string>
-
class CocoaUtils
{
public:
static void endApplication();
- static std::string documentsPath();
};
#endif
diff --git a/src/CocoaUtils.mm b/src/CocoaUtils.mm
index 295ceb9..b72583c 100644
--- a/src/CocoaUtils.mm
+++ b/src/CocoaUtils.mm
@@ -1,6 +1,5 @@
#include "CocoaUtils.h"
#import <Foundation/Foundation.h>
-#include <stdio.h>
void CocoaUtils::endApplication()
{
@@ -9,7 +8,3 @@ void CocoaUtils::endApplication()
object:nil];
}
-std::string CocoaUtils::documentsPath()
-{
- return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]);
-}
diff --git a/src/CsgInfo.h b/src/CsgInfo.h
index fe953b5..774325b 100644
--- a/src/CsgInfo.h
+++ b/src/CsgInfo.h
@@ -57,7 +57,7 @@ public:
if (this->root_norm_term) {
this->root_chain = new CSGChain();
this->root_chain->import(this->root_norm_term);
- PRINTB("Normalized CSG tree has %d elements", int(this->root_chain->polysets.size()));
+ PRINTB("Normalized CSG tree has %d elements", int(this->root_chain->objects.size()));
}
else {
this->root_chain = NULL;
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 378705e..1dcffeb 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -76,7 +76,6 @@ private:
void refreshDocument();
void updateTemporalVariables();
bool fileChangedOnDisk();
- bool includesChanged();
bool compileTopLevelDocument(bool reload);
bool compile(bool reload, bool procevents);
void compileCSG(bool procevents);
@@ -101,6 +100,7 @@ private slots:
void actionSave();
void actionSaveAs();
void actionReload();
+ void actionShowLibraryFolder();
private slots:
void pasteViewportTranslation();
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index 8e995cd..68bc064 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -137,6 +137,7 @@
<addaction name="fileActionOpen"/>
<addaction name="menuOpenRecent"/>
<addaction name="menuExamples"/>
+ <addaction name="fileShowLibraryFolder"/>
<addaction name="separator"/>
<addaction name="fileActionClose"/>
<addaction name="fileActionSave"/>
@@ -176,14 +177,17 @@
<addaction name="designActionReloadAndCompile"/>
<addaction name="designActionCompile"/>
<addaction name="designActionCompileAndRender"/>
+ <addaction name="separator"/>
<addaction name="designActionDisplayAST"/>
<addaction name="designActionDisplayCSGTree"/>
<addaction name="designActionDisplayCSGProducts"/>
+ <addaction name="separator"/>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportCSG"/>
<addaction name="designActionExportImage"/>
+ <addaction name="separator"/>
<addaction name="designActionFlushCaches"/>
</widget>
<widget class="QMenu" name="menu_View">
@@ -621,7 +625,7 @@
</action>
<action name="helpActionManual">
<property name="text">
- <string>OpenSCAD Manual</string>
+ <string>Documentation</string>
</property>
</action>
<action name="fileActionClearRecent">
@@ -688,6 +692,11 @@
<string>Check for Update..</string>
</property>
</action>
+ <action name="fileShowLibraryFolder">
+ <property name="text">
+ <string>Show Library Folder...</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc
index 4944495..99c0b20 100644
--- a/src/ModuleCache.cc
+++ b/src/ModuleCache.cc
@@ -14,50 +14,58 @@
#include <time.h>
#include <sys/stat.h>
+//#include "parsersettings.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);
-}
+/*!
+ Reevaluate the given file and recompile if necessary.
+ Returns NULL on any error (e.g. compile error or file not found)
+ If the given filename is relative, it means that the module hasn't been
+ previously located.
+*/
FileModule *ModuleCache::evaluate(const std::string &filename)
{
- FileModule *lib_mod = NULL;
+ FileModule *lib_mod = (this->entries.find(filename) != this->entries.end()) ?
+ &(*this->entries[filename].module) : NULL;
+
+ // Don't try to recursively evaluate - if the file changes
+ // during evaluation, that would be really bad.
+ if (lib_mod && lib_mod->isHandlingDependencies()) return lib_mod;
+
+ bool shouldCompile = true;
- // Create cache ID
+ // 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);
+ // 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
- 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);
+ // Lookup in cache
+ if (lib_mod) {
+ if (this->entries[filename].cache_id == cache_id) {
+ shouldCompile = false;
- BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, lib_mod->includes) {
- if (is_modified(item.first, item.second)) {
+ if (lib_mod->includesChanged()) {
lib_mod = NULL;
- break;
+ shouldCompile = true;
}
}
}
+ else {
+ shouldCompile = valid;
+ }
- // If cache lookup failed (non-existing or old timestamp), compile module
- if (!lib_mod) {
+ // If cache lookup failed (non-existing or old timestamp), compile module
+ if (shouldCompile) {
#ifdef DEBUG
if (this->entries.find(filename) != this->entries.end()) {
PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
@@ -91,7 +99,7 @@ FileModule *ModuleCache::evaluate(const std::string &filename)
if (lib_mod) {
// We defer deletion so we can ensure that the new module won't
- // have the same address as the old
+ // have the same address as the old
delete oldmodule;
this->entries[filename].module = lib_mod;
} else {
@@ -101,7 +109,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename)
print_messages_pop();
}
- if (lib_mod) lib_mod->handleDependencies();
+ if (lib_mod) {
+ lib_mod->handleDependencies();
+ }
return lib_mod;
}
@@ -111,3 +121,9 @@ void ModuleCache::clear()
this->entries.clear();
}
+FileModule *ModuleCache::lookup(const std::string &filename)
+{
+ return (this->entries.find(filename) != this->entries.end()) ?
+ &(*this->entries[filename].module) : NULL;
+}
+
diff --git a/src/ModuleCache.h b/src/ModuleCache.h
index b8ded38..7795ab7 100644
--- a/src/ModuleCache.h
+++ b/src/ModuleCache.h
@@ -9,6 +9,7 @@ class ModuleCache
public:
static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
class FileModule *evaluate(const std::string &filename);
+ class FileModule *lookup(const std::string &filename);
size_t size() { return this->entries.size(); }
void clear();
diff --git a/src/OffscreenContextWGL.cc b/src/OffscreenContextWGL.cc
index 0ccd68d..6e657fe 100644
--- a/src/OffscreenContextWGL.cc
+++ b/src/OffscreenContextWGL.cc
@@ -108,15 +108,33 @@ bool create_wgl_dummy_context(OffscreenContext &ctx)
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hInstance = inst;
- wc.lpszClassName = (LPCWSTR)"OpenSCAD";
- RegisterClass( &wc );
+ wc.lpszClassName = L"OpenSCAD";
+ ATOM class_atom = RegisterClassW( &wc );
- HWND window = CreateWindow( (LPCWSTR)"OpenSCAD", (LPCWSTR)"OpenSCAD",
- WS_CAPTION | WS_POPUPWINDOW, //| WS_VISIBLE,
- 0, 0, ctx.width, ctx.height, NULL, NULL, inst, NULL );
+ if ( class_atom == 0 ) {
+ cerr << "MS GDI - RegisterClass failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
+ return false;
+ }
+
+ LPCTSTR lpClassName = L"OpenSCAD";
+ LPCTSTR lpWindowName = L"OpenSCAD";
+ DWORD dwStyle = WS_CAPTION | WS_POPUPWINDOW; // | WS_VISIBLE
+ int x = 0;
+ int y = 0;
+ int nWidth = ctx.width;
+ int nHeight = ctx.height;
+ HWND hWndParent = NULL;
+ HMENU hMenu = NULL;
+ HINSTANCE hInstance = inst;
+ LPVOID lpParam = NULL;
+
+ HWND window = CreateWindowW( lpClassName, lpWindowName, dwStyle, x, y,
+ nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam );
if ( window==NULL ) {
cerr << "MS GDI - CreateWindow failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
return false;
}
@@ -127,6 +145,7 @@ bool create_wgl_dummy_context(OffscreenContext &ctx)
HDC dev_context = GetDC( window );
if ( dev_context == NULL ) {
cerr << "MS GDI - GetDC failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
return false;
}
@@ -145,18 +164,21 @@ bool create_wgl_dummy_context(OffscreenContext &ctx)
chosenformat = ChoosePixelFormat( dev_context, &pixformat );
if (chosenformat==0) {
cerr << "MS GDI - ChoosePixelFormat failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
return false;
}
bool spfok = SetPixelFormat( dev_context, chosenformat, &pixformat );
if (!spfok) {
cerr << "MS GDI - SetPixelFormat failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
return false;
}
HGLRC gl_render_context = wglCreateContext( dev_context );
if ( gl_render_context == NULL ) {
cerr << "MS WGL - wglCreateContext failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
ReleaseDC( ctx.window, ctx.dev_context );
return false;
}
@@ -164,6 +186,7 @@ bool create_wgl_dummy_context(OffscreenContext &ctx)
bool mcok = wglMakeCurrent( dev_context, gl_render_context );
if (!mcok) {
cerr << "MS WGL - wglMakeCurrent failed\n";
+ cerr << "last-error code: " << GetLastError() << "\n";
return false;
}
diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc
index eb66687..b4acf82 100644
--- a/src/OpenCSGRenderer.cc
+++ b/src/OpenCSGRenderer.cc
@@ -77,35 +77,50 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
std::vector<OpenCSG::Primitive*> primitives;
size_t j = 0;
for (size_t i = 0;; i++) {
- bool last = i == chain->polysets.size();
- if (last || chain->types[i] == CSGTerm::TYPE_UNION) {
+ const CSGChainObject &i_obj = chain->objects[i];
+ bool last = i == chain->objects.size();
+ if (last || i_obj.type == CSGTerm::TYPE_UNION) {
if (j+1 != i) {
OpenCSG::render(primitives);
glDepthFunc(GL_EQUAL);
}
if (shaderinfo) glUseProgram(shaderinfo[0]);
for (; j < i; j++) {
- const Transform3d &m = chain->matrices[j];
- const Color4f &c = chain->colors[j];
+ const CSGChainObject &j_obj = chain->objects[j];
+ const Color4f &c = j_obj.color;
glPushMatrix();
- glMultMatrixd(m.data());
- PolySet::csgmode_e csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
- if (highlight) {
- setColor(COLORMODE_HIGHLIGHT, shaderinfo);
- csgmode = PolySet::csgmode_e(csgmode + 20);
- }
- else if (background) {
- setColor(COLORMODE_BACKGROUND, shaderinfo);
+ glMultMatrixd(j_obj.matrix.data());
+ PolySet::csgmode_e csgmode = j_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ ColorMode colormode = COLORMODE_NONE;
+ if (background) {
+ if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ }
+ else {
+ colormode = COLORMODE_BACKGROUND;
+ }
csgmode = PolySet::csgmode_e(csgmode + 10);
- } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) {
- // User-defined color or alpha from source
- setColor(c.data(), shaderinfo);
- } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) {
- setColor(COLORMODE_CUTOUT, shaderinfo);
+ } else if (j_obj.type == CSGTerm::TYPE_DIFFERENCE) {
+ if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ csgmode = PolySet::csgmode_e(csgmode + 20);
+ }
+ else {
+ colormode = COLORMODE_CUTOUT;
+ }
} else {
- setColor(COLORMODE_MATERIAL, shaderinfo);
+ if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ csgmode = PolySet::csgmode_e(csgmode + 20);
+ }
+ else {
+ colormode = COLORMODE_MATERIAL;
+ }
}
- chain->polysets[j]->render_surface(csgmode, m, shaderinfo);
+
+ setColor(colormode, c.data(), shaderinfo);
+
+ j_obj.polyset->render_surface(csgmode, j_obj.matrix, shaderinfo);
glPopMatrix();
}
if (shaderinfo) glUseProgram(0);
@@ -118,11 +133,11 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
if (last) break;
- OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ?
- OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity);
- prim->ps = chain->polysets[i];
- prim->m = chain->matrices[i];
- prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ OpenCSGPrim *prim = new OpenCSGPrim(i_obj.type == CSGTerm::TYPE_DIFFERENCE ?
+ OpenCSG::Subtraction : OpenCSG::Intersection, i_obj.polyset->convexity);
+ prim->ps = i_obj.polyset;
+ prim->m = i_obj.matrix;
+ prim->csgmode = i_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
if (highlight) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 20);
else if (background) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 10);
primitives.push_back(prim);
diff --git a/src/PlatformUtils-mac.mm b/src/PlatformUtils-mac.mm
new file mode 100644
index 0000000..1e2ba43
--- /dev/null
+++ b/src/PlatformUtils-mac.mm
@@ -0,0 +1,7 @@
+#include "PlatformUtils.h"
+#import <Foundation/Foundation.h>
+
+std::string PlatformUtils::documentsPath()
+{
+ return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]);
+}
diff --git a/src/PlatformUtils-posix.cc b/src/PlatformUtils-posix.cc
new file mode 100644
index 0000000..d7b7b6d
--- /dev/null
+++ b/src/PlatformUtils-posix.cc
@@ -0,0 +1,10 @@
+#include "PlatformUtils.h"
+#include "boosty.h"
+
+std::string PlatformUtils::documentsPath()
+{
+ fs::path docpath(getenv("HOME"));
+ docpath = docpath / ".local" / "share";
+
+ return boosty::stringy(docpath);
+}
diff --git a/src/PlatformUtils-win.cc b/src/PlatformUtils-win.cc
new file mode 100644
index 0000000..a58a346
--- /dev/null
+++ b/src/PlatformUtils-win.cc
@@ -0,0 +1,70 @@
+#include "PlatformUtils.h"
+#include "printutils.h"
+#include <windows.h>
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0501 // SHGFP_TYPE_CURRENT
+#endif
+#include <shlobj.h>
+
+// convert from windows api w_char strings (usually utf16) to utf8 std::string
+std::string winapi_wstr_to_utf8( std::wstring wstr )
+{
+ UINT CodePage = CP_UTF8;
+ DWORD dwFlags = 0;
+ LPCWSTR lpWideCharStr = &wstr[0];
+ int cchWideChar = (int)wstr.size();
+ LPSTR lpMultiByteStr = NULL;
+ int cbMultiByte = 0;
+ LPCSTR lpDefaultChar = NULL;
+ LPBOOL lpUsedDefaultChar = NULL;
+
+ int numbytes = WideCharToMultiByte( CodePage, dwFlags, lpWideCharStr,
+ cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar );
+
+ //PRINTB("utf16 to utf8 conversion: numbytes %i",numbytes);
+
+ std::string utf8_str(numbytes,0);
+ lpMultiByteStr = &utf8_str[0];
+ cbMultiByte = numbytes;
+
+ int result = WideCharToMultiByte( CodePage, dwFlags, lpWideCharStr,
+ cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar );
+
+ if (result != numbytes) {
+ PRINT("ERROR: error converting w_char str to utf8 string");
+ PRINTB("ERROR: error code %i",GetLastError());
+ }
+
+ return utf8_str;
+}
+
+
+// retrieve the path to 'My Documents' for the current user under windows
+// In XP this is 'c:\documents and settings\username\my documents'
+// In Vista, 7, 8+ this is 'c:\users\username\documents'
+// This code may have problems with unusual dir types in Vista because
+// Mingw does not provide access to the updated SHGetKnownFolderPath
+std::string PlatformUtils::documentsPath()
+{
+ std::string retval;
+ std::wstring path(MAX_PATH,0);
+
+ HWND hwndOwner = 0;
+ int nFolder = CSIDL_PERSONAL;
+ HANDLE hToken = NULL;
+ DWORD dwFlags = SHGFP_TYPE_CURRENT;
+ LPTSTR pszPath = &path[0];
+
+ int result = SHGetFolderPathW( hwndOwner, nFolder, hToken, dwFlags, pszPath );
+
+ if (result == S_OK) {
+ path = std::wstring( path.c_str() ); // stip extra NULLs
+ //std::wcerr << "wchar path:" << "\n";
+ retval = winapi_wstr_to_utf8( path );
+ //PRINTB("Path found: %s",retval);
+ } else {
+ PRINT("ERROR: Could not find My Documents location");
+ retval = "";
+ }
+ return retval;
+}
diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc
new file mode 100644
index 0000000..5dd007d
--- /dev/null
+++ b/src/PlatformUtils.cc
@@ -0,0 +1,40 @@
+#include "PlatformUtils.h"
+#include "boosty.h"
+
+bool PlatformUtils::createLibraryPath()
+{
+ std::string path = PlatformUtils::libraryPath();
+ bool OK = false;
+ try {
+ if (!fs::exists(fs::path(path))) {
+ //PRINTB("Creating library folder %s", path );
+ OK = fs::create_directories( path );
+ }
+ if (!OK) {
+ PRINTB("ERROR: Cannot create %s", path );
+ }
+ } catch (const fs::filesystem_error& ex) {
+ PRINTB("ERROR: %s",ex.what());
+ }
+ return OK;
+}
+
+std::string PlatformUtils::libraryPath()
+{
+ fs::path path;
+ try {
+ std::string pathstr = PlatformUtils::documentsPath();
+ if (pathstr=="") return "";
+ path = boosty::canonical(fs::path( pathstr ));
+ //PRINTB("path size %i",boosty::stringy(path).size());
+ //PRINTB("lib path found: [%s]", path );
+ if (path.empty()) return "";
+ path /= "OpenSCAD";
+ path /= "libraries";
+ //PRINTB("Appended path %s", path );
+ //PRINTB("Exists: %i", fs::exists(path) );
+ } catch (const fs::filesystem_error& ex) {
+ PRINTB("ERROR: %s",ex.what());
+ }
+ return boosty::stringy( path );
+}
diff --git a/src/PlatformUtils.h b/src/PlatformUtils.h
new file mode 100644
index 0000000..089b3ca
--- /dev/null
+++ b/src/PlatformUtils.h
@@ -0,0 +1,14 @@
+#ifndef PLATFORMUTILS_H_
+#define PLATFORMUTILS_H_
+
+#include <string>
+
+namespace PlatformUtils {
+
+ std::string documentsPath();
+ std::string libraryPath();
+ bool createLibraryPath();
+
+}
+
+#endif
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index f0c274f..bc9206f 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -324,6 +324,7 @@ PolySet *PolySetCGALEvaluator::extrudeDxfData(const LinearExtrudeNode &node, Dxf
{
PolySet *ps = new PolySet();
ps->convexity = node.convexity;
+ if (node.height <= 0) return ps;
double h1, h2;
diff --git a/src/ProgressWidget.cc b/src/ProgressWidget.cc
index ce66405..7aa6a4a 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 895586c..24aefdb 100644
--- a/src/ProgressWidget.ui
+++ b/src/ProgressWidget.ui
@@ -22,6 +22,9 @@
</property>
<item>
<widget class="QProgressBar" name="progressBar">
+ <property name="maximum">
+ <number>1000</number>
+ </property>
<property name="value">
<number>24</number>
</property>
diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc
index 146d2e1..6be30dc 100644
--- a/src/ThrownTogetherRenderer.cc
+++ b/src/ThrownTogetherRenderer.cc
@@ -31,6 +31,7 @@
#include "system-gl.h"
#include <boost/unordered_map.hpp>
+#include <boost/foreach.hpp>
ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain,
CSGChain *highlights_chain,
@@ -52,9 +53,9 @@ void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const
glDisable(GL_CULL_FACE);
}
if (this->background_chain)
- renderCSGChain(this->background_chain, false, true, showedges, false);
+ renderCSGChain(this->background_chain, false, true, showedges, false);
if (this->highlights_chain)
- renderCSGChain(this->highlights_chain, true, false, showedges, false);
+ renderCSGChain(this->highlights_chain, true, false, showedges, false);
}
void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
@@ -62,58 +63,63 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
bool fberror) const
{
glDepthFunc(GL_LEQUAL);
- boost::unordered_map<std::pair<PolySet*,Transform3d*>,int> polySetVisitMark;
- for (size_t i = 0; i < chain->polysets.size(); i++) {
- if (polySetVisitMark[std::make_pair(chain->polysets[i].get(), &chain->matrices[i])]++ > 0)
+ boost::unordered_map<std::pair<PolySet*,const Transform3d*>,int> polySetVisitMark;
+ BOOST_FOREACH(const CSGChainObject &obj, chain->objects) {
+ if (polySetVisitMark[std::make_pair(obj.polyset.get(), &obj.matrix)]++ > 0)
continue;
- const Transform3d &m = chain->matrices[i];
- const Color4f &c = chain->colors[i];
+ const Transform3d &m = obj.matrix;
+ const Color4f &c = obj.color;
glPushMatrix();
glMultMatrixd(m.data());
- PolySet::csgmode_e csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ PolySet::csgmode_e csgmode = obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ ColorMode colormode = COLORMODE_NONE;
+ ColorMode edge_colormode = COLORMODE_NONE;
+
if (highlight) {
csgmode = PolySet::csgmode_e(csgmode + 20);
- setColor(COLORMODE_HIGHLIGHT);
- chain->polysets[i]->render_surface(csgmode, m);
- if (showedges) {
- setColor(COLORMODE_HIGHLIGHT_EDGES);
- chain->polysets[i]->render_edges(csgmode);
- }
+ colormode = COLORMODE_HIGHLIGHT;
+ edge_colormode = COLORMODE_HIGHLIGHT_EDGES;
} else if (background) {
- csgmode = PolySet::csgmode_e(csgmode + 10);
- setColor(COLORMODE_BACKGROUND);
- chain->polysets[i]->render_surface(csgmode, m);
- if (showedges) {
- setColor(COLORMODE_BACKGROUND_EDGES);
- chain->polysets[i]->render_edges(csgmode);
+ if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ }
+ else {
+ colormode = COLORMODE_BACKGROUND;
}
+ csgmode = PolySet::csgmode_e(csgmode + 10);
+ edge_colormode = COLORMODE_BACKGROUND_EDGES;
} else if (fberror) {
if (highlight) csgmode = PolySet::csgmode_e(csgmode + 20);
else if (background) csgmode = PolySet::csgmode_e(csgmode + 10);
else csgmode = PolySet::csgmode_e(csgmode);
- chain->polysets[i]->render_surface(csgmode, m);
- } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) {
- setColor(c.data());
- chain->polysets[i]->render_surface(csgmode, m);
- if (showedges) {
- glColor4f((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
- chain->polysets[i]->render_edges(csgmode);
+ } else if (obj.type == CSGTerm::TYPE_DIFFERENCE) {
+ if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ csgmode = PolySet::csgmode_e(csgmode + 20);
}
- } else if (chain->types[i] == CSGTerm::TYPE_DIFFERENCE) {
- setColor(COLORMODE_CUTOUT);
- chain->polysets[i]->render_surface(csgmode, m);
- if (showedges) {
- setColor(COLORMODE_CUTOUT_EDGES);
- chain->polysets[i]->render_edges(csgmode);
+ else {
+ colormode = COLORMODE_CUTOUT;
}
+ edge_colormode = COLORMODE_CUTOUT_EDGES;
} else {
- setColor(COLORMODE_MATERIAL);
- chain->polysets[i]->render_surface(csgmode, m);
- if (showedges) {
- setColor(COLORMODE_MATERIAL_EDGES);
- chain->polysets[i]->render_edges(csgmode);
+ if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
+ colormode = COLORMODE_HIGHLIGHT;
+ csgmode = PolySet::csgmode_e(csgmode + 20);
+ }
+ else {
+ colormode = COLORMODE_MATERIAL;
}
+ edge_colormode = COLORMODE_MATERIAL_EDGES;
}
+
+ setColor(colormode, c.data());
+ obj.polyset->render_surface(csgmode, m);
+ if (showedges) {
+ // FIXME? glColor4f((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
+ setColor(edge_colormode);
+ obj.polyset->render_edges(csgmode);
+ }
+
glPopMatrix();
}
}
diff --git a/src/cgaladv.cc b/src/cgaladv.cc
index 70590f7..ee3d657 100644
--- a/src/cgaladv.cc
+++ b/src/cgaladv.cc
@@ -142,6 +142,7 @@ std::string CgaladvNode::name() const
default:
assert(false);
}
+ return "internal_error";
}
std::string CgaladvNode::toString() const
diff --git a/src/cgalworker.cc b/src/cgalworker.cc
index 96fead9..f011262 100644
--- a/src/cgalworker.cc
+++ b/src/cgalworker.cc
@@ -9,6 +9,7 @@
CGALWorker::CGALWorker()
{
this->thread = new QThread();
+ if (this->thread->stackSize() < 1024*1024) this->thread->setStackSize(1024*1024);
connect(this->thread, SIGNAL(started()), this, SLOT(work()));
moveToThread(this->thread);
}
diff --git a/src/control.cc b/src/control.cc
index 7786e36..c5ad09b 100644
--- a/src/control.cc
+++ b/src/control.cc
@@ -114,7 +114,7 @@ AbstractNode *ControlModule::instantiate(const Context *ctx, const ModuleInstant
// assert(filectx->evalctx);
if (filectx->evalctx) {
- if (n < filectx->evalctx->numChildren()) {
+ if (n < (int)filectx->evalctx->numChildren()) {
node = filectx->evalctx->getChild(n)->evaluate(filectx->evalctx);
}
else {
diff --git a/src/csgops.cc b/src/csgops.cc
index 92b97e7..8ac1d4f 100644
--- a/src/csgops.cc
+++ b/src/csgops.cc
@@ -69,6 +69,7 @@ std::string CsgNode::name() const
default:
assert(false);
}
+ return "internal_error";
}
void register_builtin_csgops()
diff --git a/src/csgterm.cc b/src/csgterm.cc
index aed97b2..a0379e7 100644
--- a/src/csgterm.cc
+++ b/src/csgterm.cc
@@ -28,6 +28,7 @@
#include "polyset.h"
#include "linalg.h"
#include <sstream>
+#include <boost/foreach.hpp>
/*!
\class CSGTerm
@@ -103,19 +104,19 @@ shared_ptr<CSGTerm> CSGTerm::createCSGTerm(type_e type, CSGTerm *left, CSGTerm *
}
CSGTerm::CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label)
- : type(TYPE_PRIMITIVE), polyset(polyset), label(label), m(matrix), color(color)
+ : type(TYPE_PRIMITIVE), polyset(polyset), label(label), flag(CSGTerm::FLAG_NONE), m(matrix), color(color)
{
initBoundingBox();
}
CSGTerm::CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right)
- : type(type), left(left), right(right), m(Transform3d::Identity())
+ : type(type), left(left), right(right), flag(CSGTerm::FLAG_NONE), m(Transform3d::Identity())
{
initBoundingBox();
}
CSGTerm::CSGTerm(type_e type, CSGTerm *left, CSGTerm *right)
- : type(type), left(left), right(right), m(Transform3d::Identity())
+ : type(type), left(left), right(right), flag(CSGTerm::FLAG_NONE), m(Transform3d::Identity())
{
initBoundingBox();
}
@@ -181,26 +182,15 @@ std::string CSGTerm::dump()
return dump.str();
}
-CSGChain::CSGChain()
-{
-}
-
-void CSGChain::add(const shared_ptr<PolySet> &polyset, const Transform3d &m, const Color4f &color, CSGTerm::type_e type, std::string label)
-{
- polysets.push_back(polyset);
- matrices.push_back(m);
- colors.push_back(color);
- types.push_back(type);
- labels.push_back(label);
-}
-
-void CSGChain::import(shared_ptr<CSGTerm> term, CSGTerm::type_e type)
+void CSGChain::import(shared_ptr<CSGTerm> term, CSGTerm::type_e type, CSGTerm::Flag flag)
{
+ CSGTerm::Flag newflag = (CSGTerm::Flag)(term->flag | flag);
if (term->type == CSGTerm::TYPE_PRIMITIVE) {
- add(term->polyset, term->m, term->color, type, term->label);
+ this->objects.push_back(CSGChainObject(term->polyset, term->m, term->color, type, term->label, newflag));
} else {
- import(term->left, type);
- import(term->right, term->type);
+ assert(term->left && term->right);
+ import(term->left, type, newflag);
+ import(term->right, term->type, newflag);
}
}
@@ -208,21 +198,20 @@ std::string CSGChain::dump(bool full)
{
std::stringstream dump;
- for (size_t i = 0; i < types.size(); i++)
- {
- if (types[i] == CSGTerm::TYPE_UNION) {
- if (i != 0) dump << "\n";
+ BOOST_FOREACH(const CSGChainObject &obj, this->objects) {
+ if (obj.type == CSGTerm::TYPE_UNION) {
+ if (&obj != &this->objects.front()) dump << "\n";
dump << "+";
}
- else if (types[i] == CSGTerm::TYPE_DIFFERENCE)
+ else if (obj.type == CSGTerm::TYPE_DIFFERENCE)
dump << " -";
- else if (types[i] == CSGTerm::TYPE_INTERSECTION)
+ else if (obj.type == CSGTerm::TYPE_INTERSECTION)
dump << " *";
- dump << labels[i];
+ dump << obj.label;
if (full) {
- dump << " polyset: \n" << polysets[i]->dump() << "\n";
- dump << " matrix: \n" << matrices[i].matrix() << "\n";
- dump << " color: \n" << colors[i] << "\n";
+ dump << " polyset: \n" << obj.polyset->dump() << "\n";
+ dump << " matrix: \n" << obj.matrix.matrix() << "\n";
+ dump << " color: \n" << obj.color << "\n";
}
}
dump << "\n";
@@ -232,11 +221,11 @@ std::string CSGChain::dump(bool full)
BoundingBox CSGChain::getBoundingBox() const
{
BoundingBox bbox;
- for (size_t i=0;i<polysets.size();i++) {
- if (types[i] != CSGTerm::TYPE_DIFFERENCE) {
- BoundingBox psbox = polysets[i]->getBoundingBox();
+ BOOST_FOREACH(const CSGChainObject &obj, this->objects) {
+ if (obj.type != CSGTerm::TYPE_DIFFERENCE) {
+ BoundingBox psbox = obj.polyset->getBoundingBox();
if (!psbox.isNull()) {
- bbox.extend(matrices[i] * psbox);
+ bbox.extend(obj.matrix * psbox);
}
}
}
diff --git a/src/csgterm.h b/src/csgterm.h
index 566ebc3..94878e5 100644
--- a/src/csgterm.h
+++ b/src/csgterm.h
@@ -18,6 +18,12 @@ public:
TYPE_DIFFERENCE
};
+ enum Flag {
+ FLAG_NONE = 0x00,
+ FLAG_BACKGROUND = 0x01,
+ FLAG_HIGHLIGHT = 0x03
+ };
+
static shared_ptr<CSGTerm> createCSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right);
static shared_ptr<CSGTerm> createCSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
@@ -27,6 +33,7 @@ public:
shared_ptr<CSGTerm> left;
shared_ptr<CSGTerm> right;
BoundingBox bbox;
+ Flag flag;
CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label);
~CSGTerm();
@@ -46,19 +53,34 @@ private:
friend class CSGChain;
};
+class CSGChainObject
+{
+public:
+ CSGChainObject(shared_ptr<PolySet> polyset,
+ const Transform3d &matrix,
+ const Color4f &color,
+ CSGTerm::type_e type,
+ const std::string &label,
+ CSGTerm::Flag flag = CSGTerm::FLAG_NONE)
+ : polyset(polyset), matrix(matrix), color(color), type(type), label(label), flag(flag) {}
+
+ shared_ptr<PolySet> polyset;
+ Transform3d matrix;
+ Color4f color;
+ CSGTerm::type_e type;
+ std::string label;
+ CSGTerm::Flag flag;
+};
+
class CSGChain
{
public:
- std::vector<shared_ptr<PolySet> > polysets;
- std::vector<Transform3d> matrices;
- std::vector<Color4f> colors;
- std::vector<CSGTerm::type_e> types;
- std::vector<std::string> labels;
+ std::vector<CSGChainObject> objects;
- CSGChain();
+ CSGChain() {};
- void add(const shared_ptr<PolySet> &polyset, const Transform3d &m, const Color4f &color, CSGTerm::type_e type, std::string label);
- void import(shared_ptr<CSGTerm> term, CSGTerm::type_e type = CSGTerm::TYPE_UNION);
+ void import(shared_ptr<CSGTerm> term, CSGTerm::type_e type = CSGTerm::TYPE_UNION,
+ CSGTerm::Flag flag = CSGTerm::FLAG_NONE);
std::string dump(bool full = false);
BoundingBox getBoundingBox() const;
diff --git a/src/dxfdim.cc b/src/dxfdim.cc
index 66842d2..a241b87 100644
--- a/src/dxfdim.cc
+++ b/src/dxfdim.cc
@@ -160,9 +160,16 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx)
}
std::stringstream keystream;
+ fs::path filepath(filename);
+ uintmax_t filesize = -1;
+ time_t lastwritetime = -1;
+ if (fs::exists(filepath) && fs::is_regular_file(filepath)) {
+ filesize = fs::file_size(filepath);
+ lastwritetime = fs::last_write_time(filepath);
+ }
keystream << filename << "|" << layername << "|" << xorigin << "|" << yorigin
- << "|" << scale << "|" << fs::last_write_time(filename)
- << "|" << fs::file_size(filename);
+ << "|" << scale << "|" << lastwritetime
+ << "|" << filesize;
std::string key = keystream.str();
if (dxf_cross_cache.find(key) != dxf_cross_cache.end())
diff --git a/src/expr.cc b/src/expr.cc
index 4355400..2e069f0 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;
};
@@ -172,23 +172,27 @@ std::string Expression::toString() const
if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" ||
this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" ||
- this->type == "!=" || this->type == ">=" || this->type == ">") {
+ this->type == "!=" || this->type == ">=" || this->type == ">" ||
+ this->type == "&&" || this->type == "||") {
stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")";
}
else if (this->type == "?:") {
- stream << "(" << *this->children[0] << " ? " << this->type << " : " << *this->children[1] << ")";
+ stream << "(" << *this->children[0] << " ? " << *this->children[1] << " : " << *this->children[2] << ")";
}
else if (this->type == "[]") {
- stream << "(" << *this->children[0] << "[" << *this->children[1] << "])";
+ stream << *this->children[0] << "[" << *this->children[1] << "]";
}
else if (this->type == "I") {
- stream << "(-" << *this->children[0] << ")";
+ stream << "-" << *this->children[0];
+ }
+ else if (this->type == "!") {
+ stream << "!" << *this->children[0];
}
else if (this->type == "C") {
stream << this->const_value;
}
else if (this->type == "R") {
- stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << this->children[2] << "]";
+ stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << *this->children[2] << "]";
}
else if (this->type == "V") {
stream << "[";
@@ -202,7 +206,7 @@ std::string Expression::toString() const
stream << this->var_name;
}
else if (this->type == "N") {
- stream << "(" << *this->children[0] << "." << this->var_name << ")";
+ stream << *this->children[0] << "." << this->var_name;
}
else if (this->type == "F") {
stream << this->call_funcname << "(";
diff --git a/src/func.cc b/src/func.cc
index 18884b8..eaaae74 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -64,6 +64,7 @@ AbstractFunction::~AbstractFunction()
Value AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const
{
+ (void)evalctx; // unusued parameter
return Value();
}
@@ -500,6 +501,7 @@ Value builtin_search(const Context *, const EvalContext *evalctx)
Value builtin_version(const Context *, const EvalContext *evalctx)
{
+ (void)evalctx; // unusued parameter
Value::VectorType val;
val.push_back(Value(double(OPENSCAD_YEAR)));
val.push_back(Value(double(OPENSCAD_MONTH)));
diff --git a/src/highlighter.cc b/src/highlighter.cc
index bf80bb4..4b4aa30 100644
--- a/src/highlighter.cc
+++ b/src/highlighter.cc
@@ -132,11 +132,14 @@ Highlighter::Highlighter(QTextDocument *parent)
tokentypes["operator"] << "=" << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";";
typeformats["operator"].setForeground(Qt::blue);
- tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign";
+ tokentypes["math"] << "abs" << "sign" << "acos" << "asin" << "atan" << "atan2" << "sin" << "cos" << "floor" << "round" << "ceil" << "ln" << "log" << "lookup" << "min" << "max" << "pow" << "sqrt" << "exp" << "rands";
+ typeformats["math"].setForeground(Qt::green);
+
+ tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign" << "echo"<< "search" << "str";
typeformats["keyword"].setForeground(QColor("Green"));
typeformats["keyword"].setToolTip("Keyword");
- tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize";
+ tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize" << "mirror" << "minkowski";
typeformats["transform"].setForeground(QColor("Indigo"));
tokentypes["csgop"] << "union" << "intersection" << "difference" << "render";
@@ -148,7 +151,7 @@ Highlighter::Highlighter(QTextDocument *parent)
tokentypes["prim2d"] << "square" << "polygon" << "circle";
typeformats["prim2d"].setForeground(QColor("MidnightBlue"));
- tokentypes["import"] << "include" << "use" << "import_stl" << "import" << "import_dxf" << "dxf_dim" << "dxf_cross";
+ tokentypes["import"] << "include" << "use" << "import_stl" << "import" << "import_dxf" << "dxf_dim" << "dxf_cross" << "surface";
typeformats["import"].setForeground(Qt::darkYellow);
tokentypes["special"] << "$children" << "child" << "$fn" << "$fa" << "$fs" << "$t" << "$vpt" << "$vpr";
@@ -285,6 +288,7 @@ void Highlighter::highlightBlock(const QString &text)
// Quoting and Comments.
state_e state = (state_e) previousBlockState();
+ int quote_esc_state = 0;
for (int n = 0; n < text.size(); ++n){
if (state == NORMAL){
if (text[n] == '"'){
@@ -301,7 +305,11 @@ void Highlighter::highlightBlock(const QString &text)
}
} else if (state == QUOTE){
setFormat(n,1,quoteFormat);
- if (text[n] == '"' && n-1 >=0 && text[n-1] != '\\')
+ if (quote_esc_state > 0)
+ quote_esc_state = 0;
+ else if (text[n] == '\\')
+ quote_esc_state = 1;
+ else if (text[n] == '"')
state = NORMAL;
} else if (state == COMMENT){
setFormat(n,1,commentFormat);
diff --git a/src/import.cc b/src/import.cc
index 2180684..b5d67d2 100644
--- a/src/import.cc
+++ b/src/import.cc
@@ -204,7 +204,7 @@ PolySet *ImportNode::evaluate_polyset(class PolySetEvaluator *) const
boost::regex ex_vertices("\\s*vertex\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)");
bool binary = false;
- int file_size = f.tellg();
+ std::streampos file_size = f.tellg();
f.seekg(80);
if (!f.eof()) {
uint32_t facenum = 0;
diff --git a/src/lexer.l b/src/lexer.l
index 6dfe9bc..0b8048f 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -100,7 +100,7 @@ E [Ee][+-]?{D}+
%%
-include[ \t\r\n>]*"<" { BEGIN(cond_include); }
+include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = "";}
<cond_include>{
[^\t\r\n>]*"/" { filepath = yytext; }
[^\t\r\n>/]+ { filename = yytext; }
@@ -113,29 +113,16 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
[^\t\r\n>]+ { filename = yytext; }
">" {
BEGIN(INITIAL);
- fs::path usepath;
- if (boosty::is_absolute(fs::path(filename))) {
- usepath = filename;
- }
- else {
- usepath = sourcepath() / filename;
- if (!fs::exists(usepath)) {
- usepath = locate_file(filename);
- }
- }
- /* Only accept regular files which exists */
- if (usepath.has_parent_path() && fs::exists(usepath) && !fs::is_directory(usepath)) {
- handle_dep(usepath.string());
- parserlval.text = strdup(usepath.string().c_str());
- return TOK_USE;
- } else {
- PRINTB("WARNING: Can't open 'use' file '%s'.", filename);
- if ( filename.size() == 0 )
- PRINT("WARNING: 'use' filename is blank");
- else if ( fs::is_directory( usepath ) )
- PRINTB("WARNING: 'use' file points to a directory: %s",filename);
- }
- }
+ fs::path fullpath = find_valid_path(sourcepath(), fs::path(filename), &openfilenames);
+ if (fullpath.empty()) {
+ PRINTB("WARNING: Can't open library '%s'.", filename);
+ parserlval.text = strdup(filename.c_str());
+ } else {
+ handle_dep(fullpath.string());
+ parserlval.text = strdup(fullpath.string().c_str());
+ }
+ return TOK_USE;
+ }
}
<<EOF>> {
@@ -206,55 +193,37 @@ fs::path sourcepath()
Rules for include <path/file>
1) include <sourcepath/path/file>
2) include <librarydir/path/file>
+
+ Globals used: filepath, sourcepath, filename
*/
void includefile()
{
- if (filename.empty()) return;
-
- fs::path dirinfo = sourcepath();
- if (!filepath.empty()) {
- if (boosty::is_absolute(fs::path(filepath))) {
- dirinfo = filepath;
- }
- else {
- dirinfo /= filepath;
- }
- }
-
- fs::path finfo = dirinfo / filename;
- if (!exists(finfo)) {
- finfo = locate_file((fs::path(filepath) / filename).string());
- }
-
- if (!exists(finfo) || finfo.empty()) {
- // deal with some unusual situations with is_absolute() and Wine
- fs::path fnp( fs::path(filepath) / filename );
- if (fs::exists( fnp ) && !fs::is_directory( fnp )) {
- finfo = fnp;
- }
- }
-
- if (finfo.empty()) {
- PRINTB("WARNING: Can't find 'include' file '%s'.", filename);
+ fs::path localpath = fs::path(filepath) / filename;
+ fs::path fullpath = find_valid_path(sourcepath(), localpath, &openfilenames);
+ if (!fullpath.empty()) {
+ rootmodule->registerInclude(boosty::stringy(localpath), boosty::stringy(fullpath));
}
+ else {
+ rootmodule->registerInclude(boosty::stringy(localpath), boosty::stringy(localpath));
+ PRINTB("WARNING: Can't open include file '%s'.", boosty::stringy(localpath));
+ if (path_stack.size() > 0) path_stack.pop_back();
+ return;
+ };
- std::string fullname = boosty::absolute(finfo).string();
- // Detect circular includes
- BOOST_FOREACH(std::string &s, openfilenames) {
- if (s == fullname) return;
- }
+ std::string fullname = boosty::stringy(fullpath);
filepath.clear();
- path_stack.push_back(finfo.parent_path());
+ path_stack.push_back(fullpath.parent_path());
handle_dep(fullname);
- rootmodule->registerInclude(fullname);
+
yyin = fopen(fullname.c_str(), "r");
if (!yyin) {
- PRINTB("WARNING: Can't open 'include' file '%s'.", filename);
+ PRINTB("WARNING: Can't open include file '%s'.", boosty::stringy(localpath));
path_stack.pop_back();
return;
}
+
openfiles.push_back(yyin);
openfilenames.push_back(fullname);
filename.clear();
diff --git a/src/linearextrude.cc b/src/linearextrude.cc
index 9a7365b..c5d4529 100644
--- a/src/linearextrude.cc
+++ b/src/linearextrude.cc
@@ -88,7 +88,8 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI
}
node->layername = layer.isUndefined() ? "" : layer.toString();
- node->height = height.toDouble();
+ node->height = 100;
+ height.getDouble(node->height);
node->convexity = (int)convexity.toDouble();
origin.getVec2(node->origin_x, node->origin_y);
node->scale_x = node->scale_y = 1;
@@ -99,8 +100,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI
if (center.type() == Value::BOOL)
node->center = center.toBool();
- if (node->height <= 0)
- node->height = 100;
+ if (node->height <= 0) node->height = 0;
if (node->convexity <= 0)
node->convexity = 1;
diff --git a/src/localscope.cc b/src/localscope.cc
index eecff91..dd40563 100644
--- a/src/localscope.cc
+++ b/src/localscope.cc
@@ -55,9 +55,11 @@ std::vector<AbstractNode*> LocalScope::instantiateChildren(const Context *evalct
// c->functions_p = &this->functions;
// c->modules_p = &this->modules;
- BOOST_FOREACH (const Assignment &ass, this->assignments) {
- c->set_variable(ass.first, ass.second->evaluate(c));
- }
+ // Uncommenting the following would allow assignments in local scopes,
+ // but would cause duplicate evaluation of module scopes
+ // BOOST_FOREACH (const Assignment &ass, this->assignments) {
+ // c->set_variable(ass.first, ass.second->evaluate(c));
+ // }
}
std::vector<AbstractNode*> childnodes;
diff --git a/src/mainwin.cc b/src/mainwin.cc
index da3501d..95f7242 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -53,6 +53,7 @@
#ifdef Q_OS_MAC
#include "CocoaUtils.h"
#endif
+#include "PlatformUtils.h"
#include <QMenu>
#include <QTime>
@@ -104,6 +105,8 @@
#define OPENCSG_VERSION_STRING "unknown, <1.3.2"
#endif
+#include "boosty.h"
+
extern QString examplesdir;
// Global application state
@@ -119,7 +122,7 @@ static char helptitle[] =
#endif
"\nhttp://www.openscad.org\n\n";
static char copyrighttext[] =
- "Copyright (C) 2009-2013 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n"
+ "Copyright (C) 2009-2013 The OpenSCAD Developers\n"
"\n"
"This program is free software; you can redistribute it and/or modify "
"it under the terms of the GNU General Public License as published by "
@@ -227,6 +230,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->fileActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs()));
connect(this->fileActionReload, SIGNAL(triggered()), this, SLOT(actionReload()));
connect(this->fileActionQuit, SIGNAL(triggered()), this, SLOT(quit()));
+ connect(this->fileShowLibraryFolder, SIGNAL(triggered()), this, SLOT(actionShowLibraryFolder()));
#ifndef __APPLE__
QList<QKeySequence> shortcuts = this->fileActionSave->shortcuts();
shortcuts.push_back(QKeySequence(Qt::Key_F2));
@@ -458,12 +462,11 @@ void MainWindow::showProgress()
void MainWindow::report_func(const class AbstractNode*, void *vp, int mark)
{
MainWindow *thisp = static_cast<MainWindow*>(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;
+ if (permille > thisp->progresswidget->value()) {
QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection,
- Q_ARG(int, percent));
+ Q_ARG(int, permille));
QApplication::processEvents();
}
@@ -489,12 +492,12 @@ void
MainWindow::openFile(const QString &new_filename)
{
QString actual_filename = new_filename;
+ QFileInfo fi(new_filename);
+ if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) {
+ actual_filename = QString();
+ }
#ifdef ENABLE_MDI
if (!editor->toPlainText().isEmpty()) {
- QFileInfo fi(new_filename);
- if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) {
- actual_filename = QString();
- }
new MainWindow(actual_filename);
clearCurrentOutput();
return;
@@ -502,6 +505,7 @@ MainWindow::openFile(const QString &new_filename)
#endif
setFileName(actual_filename);
+ fileChangedOnDisk(); // force cached autoReloadId to update
refreshDocument();
updateRecentFiles();
if (actual_filename.isEmpty()) {
@@ -651,7 +655,11 @@ bool MainWindow::compile(bool reload, bool procevents)
if (procevents) QApplication::processEvents();
AbstractNode::resetIndexCounter();
- this->root_inst = ModuleInstantiation("group");
+
+ // split these two lines - gcc 4.7 bug
+ ModuleInstantiation mi = ModuleInstantiation( "group" );
+ this->root_inst = mi;
+
this->absolute_root_node = this->root_module->instantiate(&top_ctx, &this->root_inst, NULL);
if (this->absolute_root_node) {
@@ -687,8 +695,7 @@ void MainWindow::compileCSG(bool procevents)
{
assert(this->root_node);
PRINT("Compiling design (CSG Products generation)...");
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
// Main CSG evaluation
QTime t;
@@ -706,16 +713,16 @@ void MainWindow::compileCSG(bool procevents)
PolySetEvaluator psevaluator(this->tree);
#endif
CSGTermEvaluator csgrenderer(this->tree, &psevaluator);
+ if (procevents) QApplication::processEvents();
this->root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
if (!root_raw_term) {
PRINT("ERROR: CSG generation failed! (no top level object found)");
- if (procevents)
- QApplication::processEvents();
}
PolySetCache::instance()->print();
#ifdef ENABLE_CGAL
CGALCache::instance()->print();
#endif
+ if (procevents) QApplication::processEvents();
}
catch (const ProgressCancelException &e) {
PRINT("CSG generation cancelled.");
@@ -727,8 +734,7 @@ void MainWindow::compileCSG(bool procevents)
if (root_raw_term) {
PRINT("Compiling design (CSG Products normalization)...");
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
size_t normalizelimit = 2 * Preferences::inst()->getValue("advanced/openCSGLimit").toUInt();
CSGTermNormalizer normalizer(normalizelimit);
@@ -740,15 +746,13 @@ void MainWindow::compileCSG(bool procevents)
else {
this->root_chain = NULL;
PRINT("WARNING: CSG normalization resulted in an empty tree");
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
}
if (highlight_terms.size() > 0)
{
PRINTB("Compiling highlights (%d CSG Trees)...", highlight_terms.size());
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
highlights_chain = new CSGChain();
for (unsigned int i = 0; i < highlight_terms.size(); i++) {
@@ -760,8 +764,7 @@ void MainWindow::compileCSG(bool procevents)
if (background_terms.size() > 0)
{
PRINTB("Compiling background (%d CSG Trees)...", background_terms.size());
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
background_chain = new CSGChain();
for (unsigned int i = 0; i < background_terms.size(); i++) {
@@ -771,14 +774,14 @@ void MainWindow::compileCSG(bool procevents)
}
if (this->root_chain &&
- (this->root_chain->polysets.size() >
+ (this->root_chain->objects.size() >
Preferences::inst()->getValue("advanced/openCSGLimit").toUInt())) {
- PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->polysets.size());
+ PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->objects.size());
PRINT("WARNING: OpenCSG rendering has been disabled.");
}
else {
PRINTB("Normalized CSG tree has %d elements",
- (this->root_chain ? this->root_chain->polysets.size() : 0));
+ (this->root_chain ? this->root_chain->objects.size() : 0));
this->opencsgRenderer = new OpenCSGRenderer(this->root_chain,
this->highlights_chain,
this->background_chain,
@@ -790,8 +793,7 @@ void MainWindow::compileCSG(bool procevents)
PRINT("CSG generation finished.");
int s = t.elapsed() / 1000;
PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60));
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
}
}
@@ -913,6 +915,8 @@ void MainWindow::actionSave()
QFile file(this->fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
PRINTB("Failed to open file for writing: %s (%s)", this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData());
+ QMessageBox::warning(this, windowTitle(), tr("Failed to open file for writing:\n %1 (%2)")
+ .arg(this->fileName).arg(file.errorString()));
}
else {
QTextStream writer(&file);
@@ -951,9 +955,26 @@ void MainWindow::actionSaveAs()
}
}
+void MainWindow::actionShowLibraryFolder()
+{
+ std::string path = PlatformUtils::libraryPath();
+ if (!fs::exists(path)) {
+ PRINTB("WARNING: Library path %s doesnt exist. Creating", path);
+ if (!PlatformUtils::createLibraryPath()) {
+ PRINTB("ERROR: Cannot create library path: %s",path);
+ }
+ }
+ QString url = QString::fromStdString( path );
+ //PRINTB("Opening file browser for %s", url.toStdString() );
+ QDesktopServices::openUrl(QUrl::fromLocalFile( url ));
+}
+
void MainWindow::actionReload()
{
- if (checkEditorModified()) refreshDocument();
+ if (checkEditorModified()) {
+ fileChangedOnDisk(); // force cached autoReloadId to update
+ refreshDocument();
+ }
}
void MainWindow::hideEditor()
@@ -1007,7 +1028,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) {
@@ -1018,25 +1042,6 @@ bool MainWindow::fileChangedOnDisk()
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 FileModule::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
@@ -1048,11 +1053,22 @@ bool MainWindow::compileTopLevelDocument(bool reload)
{
bool shouldcompiletoplevel = !reload;
- if (includesChanged()) shouldcompiletoplevel = true;
-
- if (reload && fileChangedOnDisk() && checkEditorModified()) {
+ if (this->root_module && this->root_module->includesChanged()) {
shouldcompiletoplevel = true;
- refreshDocument();
+ }
+
+ if (reload) {
+ // Refresh file 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 if (!editor->isContentModified()) {
+ QString current_doc = editor->toPlainText();
+ if (current_doc != last_compiled_doc) shouldcompiletoplevel = true;
+ }
}
if (shouldcompiletoplevel) {
@@ -1105,7 +1121,6 @@ void MainWindow::autoReloadSet(bool on)
QSettings settings;
settings.setValue("design/autoReload",designActionAutoReload->isChecked());
if (on) {
- autoReloadId = "";
autoReloadTimer->start(200);
} else {
autoReloadTimer->stop();
@@ -1746,7 +1761,7 @@ MainWindow::helpHomepage()
void
MainWindow::helpManual()
{
- QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual"));
+ QDesktopServices::openUrl(QUrl("http://www.openscad.org/documentation.html"));
}
#define STRINGIFY(x) #x
diff --git a/src/modcontext.cc b/src/modcontext.cc
index 3879811..5b48009 100644
--- a/src/modcontext.cc
+++ b/src/modcontext.cc
@@ -4,6 +4,7 @@
#include "function.h"
#include "printutils.h"
#include "builtin.h"
+#include "ModuleCache.h"
#include <boost/foreach.hpp>
@@ -16,6 +17,51 @@ ModuleContext::~ModuleContext()
{
}
+// Experimental code. See issue #399
+#if 0
+void ModuleContext::evaluateAssignments(const AssignmentList &assignments)
+{
+ // First, assign all simple variables
+ std::list<std::string> undefined_vars;
+ BOOST_FOREACH(const Assignment &ass, assignments) {
+ Value tmpval = ass.second->evaluate(this);
+ if (tmpval.isUndefined()) undefined_vars.push_back(ass.first);
+ else this->set_variable(ass.first, tmpval);
+ }
+
+ // Variables which couldn't be evaluated in the first pass is attempted again,
+ // to allow for initialization out of order
+
+ boost::unordered_map<std::string, Expression *> tmpass;
+ BOOST_FOREACH (const Assignment &ass, assignments) {
+ tmpass[ass.first] = ass.second;
+ }
+
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ std::list<std::string>::iterator iter = undefined_vars.begin();
+ while (iter != undefined_vars.end()) {
+ std::list<std::string>::iterator curr = iter++;
+ boost::unordered_map<std::string, Expression *>::iterator found = tmpass.find(*curr);
+ if (found != tmpass.end()) {
+ const Expression *expr = found->second;
+ Value tmpval = expr->evaluate(this);
+ // FIXME: it's not enough to check for undefined;
+ // we need to check for any undefined variable in the subexpression
+ // For now, ignore this and revisit the validity and order of variable
+ // assignments later
+ if (!tmpval.isUndefined()) {
+ changed = true;
+ this->set_variable(*curr, tmpval);
+ undefined_vars.erase(curr);
+ }
+ }
+ }
+ }
+}
+#endif
+
void ModuleContext::initializeModule(const class Module &module)
{
this->setVariables(module.definition_arguments, evalctx);
@@ -25,6 +71,8 @@ void ModuleContext::initializeModule(const class Module &module)
BOOST_FOREACH(const Assignment &ass, module.scope.assignments) {
this->set_variable(ass.first, ass.second->evaluate(this));
}
+// Experimental code. See issue #399
+// evaluateAssignments(module.scope.assignments);
}
/*!
@@ -125,15 +173,18 @@ Value FileContext::evaluate_function(const std::string &name, const EvalContext
if (foundf) return foundf->evaluate(this, evalctx);
BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) {
- if (m.second->scope.functions.find(name) != m.second->scope.functions.end()) {
- FileContext ctx(*m.second, this->parent);
- ctx.initializeModule(*m.second);
+ // usedmod is NULL if the library wasn't be compiled (error or file-not-found)
+ FileModule *usedmod = ModuleCache::instance()->lookup(m);
+ if (usedmod &&
+ usedmod->scope.functions.find(name) != usedmod->scope.functions.end()) {
+ FileContext ctx(*usedmod, this->parent);
+ ctx.initializeModule(*usedmod);
// FIXME: Set document path
#if 0 && DEBUG
PRINTB("New lib Context for %s func:", name);
ctx.dump(NULL, NULL);
#endif
- return m.second->scope.functions[name]->evaluate(&ctx, evalctx);
+ return usedmod->scope.functions[name]->evaluate(&ctx, evalctx);
}
}
@@ -146,16 +197,18 @@ AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, c
if (foundm) return foundm->instantiate(this, &inst, evalctx);
BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) {
- assert(m.second);
- if (m.second->scope.modules.find(inst.name()) != m.second->scope.modules.end()) {
- FileContext ctx(*m.second, this->parent);
- ctx.initializeModule(*m.second);
+ FileModule *usedmod = ModuleCache::instance()->lookup(m);
+ // usedmod is NULL if the library wasn't be compiled (error or file-not-found)
+ if (usedmod &&
+ usedmod->scope.modules.find(inst.name()) != usedmod->scope.modules.end()) {
+ FileContext ctx(*usedmod, this->parent);
+ ctx.initializeModule(*usedmod);
// FIXME: Set document path
#if 0 && DEBUG
PRINT("New file Context:");
ctx.dump(NULL, &inst);
#endif
- return m.second->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx);
+ return usedmod->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx);
}
}
diff --git a/src/modcontext.h b/src/modcontext.h
index 0b3e48c..3a05a0c 100644
--- a/src/modcontext.h
+++ b/src/modcontext.h
@@ -36,6 +36,9 @@ public:
#ifdef DEBUG
virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
#endif
+private:
+// Experimental code. See issue #399
+// void evaluateAssignments(const AssignmentList &assignments);
};
class FileContext : public ModuleContext
diff --git a/src/module.cc b/src/module.cc
index e853457..8fb8506 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -32,6 +32,7 @@
#include "expression.h"
#include "function.h"
#include "printutils.h"
+#include "parsersettings.h"
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
@@ -46,6 +47,8 @@ AbstractModule::~AbstractModule()
AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
+ (void)ctx; // avoid unusued parameter warning
+
AbstractNode *node = new AbstractNode(inst);
node->children = inst->instantiateChildren(evalctx);
@@ -99,16 +102,35 @@ std::string ModuleInstantiation::dump(const std::string &indent) const
if (scope.numElements() == 0) {
dump << ");\n";
} else if (scope.numElements() == 1) {
- dump << ")\n";
- dump << scope.dump(indent + "\t");
+ dump << ") ";
+ dump << scope.dump("");
} else {
dump << ") {\n";
- scope.dump(indent + "\t");
+ dump << scope.dump(indent + "\t");
dump << indent << "}\n";
}
return dump.str();
}
+std::string IfElseModuleInstantiation::dump(const std::string &indent) const
+{
+ std::stringstream dump;
+ dump << ModuleInstantiation::dump(indent);
+ dump << indent;
+ if (else_scope.numElements() > 0) {
+ dump << indent << "else ";
+ if (else_scope.numElements() == 1) {
+ dump << else_scope.dump("");
+ }
+ else {
+ dump << "{\n";
+ dump << else_scope.dump(indent + "\t");
+ dump << indent << "}\n";
+ }
+ }
+ return dump.str();
+}
+
AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const
{
EvalContext c(ctx, this->arguments, &this->scope);
@@ -144,7 +166,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;
};
@@ -194,12 +216,36 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
return dump.str();
}
-void FileModule::registerInclude(const std::string &filename)
+void FileModule::registerInclude(const std::string &localpath,
+ const std::string &fullpath)
{
struct stat st;
memset(&st, 0, sizeof(struct stat));
- stat(filename.c_str(), &st);
- this->includes[filename] = st.st_mtime;
+ bool valid = stat(fullpath.c_str(), &st) == 0;
+ IncludeFile inc = {fullpath, valid, st.st_mtime};
+ 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;
}
/*!
@@ -212,21 +258,39 @@ bool FileModule::handleDependencies()
this->is_handling_dependencies = true;
bool changed = false;
+
+ // 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
FileModule::ModuleContainer::iterator iter = this->usedlibs.begin();
while (iter != this->usedlibs.end()) {
FileModule::ModuleContainer::iterator curr = iter++;
- FileModule *oldmodule = curr->second;
- curr->second = ModuleCache::instance()->evaluate(curr->first);
- if (curr->second != oldmodule) {
+
+ bool wasmissing = false;
+ // Get an absolute filename for the module
+ std::string filename = *curr;
+ if (!boosty::is_absolute(filename)) {
+ wasmissing = true;
+ fs::path fullpath = find_valid_path(this->path, filename);
+ if (!fullpath.empty()) filename = boosty::stringy(fullpath);
+ }
+
+ FileModule *oldmodule = ModuleCache::instance()->lookup(filename);
+ 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", curr->first % curr->second);
+ PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule);
#endif
}
- if (!curr->second) {
- PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", curr->first);
- this->usedlibs.erase(curr);
+ if (!newmodule) {
+ // Only print warning if we're not part of an automatic reload
+ if (!oldmodule && !wasmissing) {
+ PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
+ }
}
}
diff --git a/src/module.h b/src/module.h
index 9f46d37..6027fe6 100644
--- a/src/module.h
+++ b/src/module.h
@@ -5,6 +5,10 @@
#include <vector>
#include <list>
#include <boost/unordered_map.hpp>
+#include <boost/unordered_set.hpp>
+#include <time.h>
+#include <sys/stat.h>
+
#include "value.h"
#include "typedefs.h"
#include "localscope.h"
@@ -16,7 +20,7 @@ public:
: tag_root(false), tag_highlight(false), tag_background(false), recursioncount(0), modname(name) { }
virtual ~ModuleInstantiation();
- std::string dump(const std::string &indent) const;
+ virtual std::string dump(const std::string &indent) const;
class AbstractNode *evaluate(const class Context *ctx) const;
std::vector<AbstractNode*> instantiateChildren(const Context *evalctx) const;
@@ -48,6 +52,7 @@ public:
IfElseModuleInstantiation() : ModuleInstantiation("if") { }
virtual ~IfElseModuleInstantiation();
std::vector<AbstractNode*> instantiateElseChildren(const Context *evalctx) const;
+ virtual std::string dump(const std::string &indent) const;
LocalScope else_scope;
};
@@ -84,16 +89,26 @@ public:
void setModulePath(const std::string &path) { this->path = path; }
const std::string &modulePath() const { return this->path; }
- void registerInclude(const std::string &filename);
+ 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(); }
+ bool isHandlingDependencies() const { return this->is_handling_dependencies; }
- typedef boost::unordered_map<std::string, class FileModule*> ModuleContainer;
+ typedef boost::unordered_set<std::string> ModuleContainer;
ModuleContainer usedlibs;
- typedef boost::unordered_map<std::string, time_t> IncludeContainer;
- IncludeContainer includes;
-
private:
+ struct IncludeFile {
+ std::string filename;
+ bool valid;
+ time_t mtime;
+ };
+
+ bool include_modified(const IncludeFile &inc) const;
+ typedef boost::unordered_map<std::string, struct IncludeFile> IncludeContainer;
+ IncludeContainer includes;
bool is_handling_dependencies;
std::string path;
};
diff --git a/src/openscad.cc b/src/openscad.cc
index 7c54762..bcde5e2 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -330,7 +330,6 @@ int main(int argc, char **argv)
// Top context - this context only holds builtins
ModuleContext top_ctx;
top_ctx.registerBuiltin();
- PRINT("Root Context:");
#if 0 && DEBUG
top_ctx.dump(NULL, NULL);
#endif
diff --git a/src/parser.y b/src/parser.y
index 2b07f14..5b3d019 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -127,7 +127,7 @@
input:
/* empty */ |
-TOK_USE { rootmodule->usedlibs[$1] = NULL; } input |
+TOK_USE { rootmodule->usedlibs.insert($1); } input |
statement input ;
inner_input:
@@ -136,15 +136,17 @@ statement inner_input ;
assignment:
TOK_ID '=' expr ';' {
+ bool found = false;
for (AssignmentList::iterator iter = scope_stack.top()->assignments.begin();
iter != scope_stack.top()->assignments.end();
iter++) {
if (iter->first == $1) {
- scope_stack.top()->assignments.erase(iter);
+ iter->second = $3;
+ found = true;
break;
}
}
- scope_stack.top()->assignments.push_back(Assignment($1, $3));
+ if (!found) scope_stack.top()->assignments.push_back(Assignment($1, $3));
} ;
statement:
diff --git a/src/parsersettings.cc b/src/parsersettings.cc
index 8d82744..ab93b78 100644
--- a/src/parsersettings.cc
+++ b/src/parsersettings.cc
@@ -3,9 +3,7 @@
#include <boost/foreach.hpp>
#include "boosty.h"
#include <boost/algorithm/string.hpp>
-#ifdef __APPLE__
-#include "CocoaUtils.h"
-#endif
+#include "PlatformUtils.h"
namespace fs = boost::filesystem;
@@ -20,13 +18,74 @@ void add_librarydir(const std::string &libdir)
Searces for the given file in library paths and returns the full path if found.
Returns an empty path if file cannot be found or filename is a directory.
*/
-std::string locate_file(const std::string &filename)
+fs::path search_libs(const fs::path &localpath)
{
BOOST_FOREACH(const std::string &dir, librarypath) {
- fs::path usepath = fs::path(dir) / filename;
- if (fs::exists(usepath) && !fs::is_directory(usepath)) return usepath.string();
+ fs::path usepath = fs::path(dir) / localpath;
+ if (fs::exists(usepath) && !fs::is_directory(usepath)) {
+ return usepath.string();
+ }
+ }
+ return fs::path();
+}
+
+// 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<std::string> *openfilenames)
+{
+ if (p.empty()) {
+// PRINTB("WARNING: File path is blank: %s",p);
+ return false;
+ }
+ if (!p.has_parent_path()) {
+// PRINTB("WARNING: No parent path: %s",p);
+ return false;
+ }
+ if (!fs::exists(p)) {
+// PRINTB("WARNING: File not found: %s",p);
+ return false;
+ }
+ if (fs::is_directory(p)) {
+// PRINTB("WARNING: %s invalid - points to a directory",p);
+ return false;
+ }
+ std::string fullname = boosty::stringy(p);
+ // Detect circular includes
+ if (openfilenames) {
+ BOOST_FOREACH(const std::string &s, *openfilenames) {
+ if (s == fullname) {
+// PRINTB("WARNING: circular include file %s", fullname);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ Check if the given filename is valid.
+
+ If the given filename is absolute, do a simple check.
+ If not, search the applicable paths for a valid file.
+
+ Returns the absolute path to a valid file, or an empty path if no
+ valid files could be found.
+*/
+fs::path find_valid_path(const fs::path &sourcepath,
+ const fs::path &localpath,
+ const std::vector<std::string> *openfilenames)
+{
+ if (boosty::is_absolute(localpath)) {
+ if (check_valid(localpath, openfilenames)) return boosty::absolute(localpath);
+ }
+ else {
+ fs::path fpath = sourcepath / localpath;
+ if (check_valid(fpath, openfilenames)) return fpath;
+ fpath = search_libs(localpath);
+ if (!fpath.empty() && check_valid(fpath, openfilenames)) return fpath;
}
- return std::string();
+ return fs::path();
}
void parser_init(const std::string &applicationpath)
@@ -44,12 +103,15 @@ void parser_init(const std::string &applicationpath)
}
}
- // FIXME: Add ~/.openscad/libraries
-#if defined(__APPLE__) && !defined(OPENSCAD_TESTING)
- fs::path docdir(CocoaUtils::documentsPath());
+ // This is the built-in user-writable library path
+#ifndef OPENSCAD_TESTING
+ // This will resolve to ~/Documents on Mac, "My Documents" on Windows and
+ // ~/.local/share on Linux
+ fs::path docdir(PlatformUtils::documentsPath());
add_librarydir(boosty::stringy(docdir / "OpenSCAD" / "libraries"));
#endif
+ // This is the built-in read-only library path
std::string librarydir;
fs::path libdir(applicationpath);
fs::path tmpdir;
@@ -58,15 +120,15 @@ void parser_init(const std::string &applicationpath)
if (!is_directory(libdir / "libraries")) libdir /= "../../..";
#elif !defined(WIN32)
if (is_directory(tmpdir = libdir / "../share/openscad/libraries")) {
- librarydir = boosty::stringy( tmpdir );
+ librarydir = boosty::stringy(tmpdir);
} else if (is_directory(tmpdir = libdir / "../../share/openscad/libraries")) {
- librarydir = boosty::stringy( tmpdir );
+ librarydir = boosty::stringy(tmpdir);
} else if (is_directory(tmpdir = libdir / "../../libraries")) {
- librarydir = boosty::stringy( tmpdir );
+ librarydir = boosty::stringy(tmpdir);
} else
#endif
if (is_directory(tmpdir = libdir / "libraries")) {
- librarydir = boosty::stringy( tmpdir );
+ librarydir = boosty::stringy(tmpdir);
}
if (!librarydir.empty()) add_librarydir(librarydir);
}
diff --git a/src/parsersettings.h b/src/parsersettings.h
index 007aa9c..52172b6 100644
--- a/src/parsersettings.h
+++ b/src/parsersettings.h
@@ -2,11 +2,15 @@
#define PARSERSETTINGS_H_
#include <string>
+#include "boosty.h"
extern int parser_error_pos;
void parser_init(const std::string &applicationpath);
void add_librarydir(const std::string &libdir);
-std::string locate_file(const std::string &filename);
+fs::path search_libs(const fs::path &localpath);
+fs::path find_valid_path(const fs::path &sourcepath,
+ const fs::path &localpath,
+ const std::vector<std::string> *openfilenames = NULL);
#endif
diff --git a/src/primitives.cc b/src/primitives.cc
index 9b755ef..89c1573 100644
--- a/src/primitives.cc
+++ b/src/primitives.cc
@@ -242,9 +242,8 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta
*/
int get_fragments_from_r(double r, double fn, double fs, double fa)
{
- if (r < GRID_FINE) return 0;
- if (fn > 0.0)
- return (int)fn;
+ if (r < GRID_FINE) return 3;
+ if (fn > 0.0) return (int)(fn >= 3 ? fn : 3);
return (int)ceil(fmax(fmin(360.0 / fa, r*2*M_PI / fs), 5));
}
diff --git a/src/renderer.cc b/src/renderer.cc
index 985b460..7c4f8d7 100644
--- a/src/renderer.cc
+++ b/src/renderer.cc
@@ -1,28 +1,11 @@
#include "renderer.h"
#include "rendersettings.h"
-#include "linalg.h"
-void Renderer::setColor(const float color[4], GLint *shaderinfo) const
-{
- Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR);
- float c[4] = {color[0], color[1], color[2], color[3]};
- if (c[0] < 0) c[0] = col[0];
- if (c[1] < 0) c[1] = col[1];
- if (c[2] < 0) c[2] = col[2];
- if (c[3] < 0) c[3] = col[3];
- glColor4fv(c);
- if (shaderinfo) {
- glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]);
- glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
- }
-}
-
-void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const
+bool Renderer::getColor(Renderer::ColorMode colormode, Color4f &col) const
{
- Color4f col;
switch (colormode) {
case COLORMODE_NONE:
- return;
+ return false;
break;
case COLORMODE_MATERIAL:
col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR);
@@ -31,7 +14,7 @@ void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const
col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_BACK_COLOR);
break;
case COLORMODE_HIGHLIGHT:
- col.setRgb(255, 157, 81, 128);
+ col.setRgb(255, 81, 81, 128);
break;
case COLORMODE_BACKGROUND:
col.setRgb(180, 180, 180, 128);
@@ -49,19 +32,51 @@ void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const
col.setRgb(150, 150, 150, 128);
break;
default:
- return;
+ return false;
break;
}
- float rgba[4];
- rgba[0] = col[0];
- rgba[1] = col[1];
- rgba[2] = col[2];
- rgba[3] = col[3];
- glColor4fv(rgba);
+ return true;
+}
+
+void Renderer::setColor(const float color[4], GLint *shaderinfo) const
+{
+ Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR);
+ float c[4] = {color[0], color[1], color[2], color[3]};
+ if (c[0] < 0) c[0] = col[0];
+ if (c[1] < 0) c[1] = col[1];
+ if (c[2] < 0) c[2] = col[2];
+ if (c[3] < 0) c[3] = col[3];
+ glColor4fv(c);
#ifdef ENABLE_OPENCSG
if (shaderinfo) {
- glUniform4f(shaderinfo[1], col[0], col[1], col[2], 1.0f);
- glUniform4f(shaderinfo[2], (col[0]+1)/2, (col[1]+1)/2, (col[2]+1)/2, 1.0f);
+ glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]);
+ glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
}
#endif
}
+
+void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shaderinfo) const
+{
+ Color4f basecol;
+ if (getColor(colormode, basecol)) {
+ if (colormode == COLORMODE_BACKGROUND) {
+ basecol = Color4f(color[0] >= 0 ? color[0] : basecol[0],
+ color[1] >= 0 ? color[1] : basecol[1],
+ color[2] >= 0 ? color[2] : basecol[2],
+ color[3] >= 0 ? color[3] : basecol[3]);
+ }
+ else if (colormode != COLORMODE_HIGHLIGHT) {
+ basecol = Color4f(color[0] >= 0 ? color[0] : basecol[0],
+ color[1] >= 0 ? color[1] : basecol[1],
+ color[2] >= 0 ? color[2] : basecol[2],
+ color[3] >= 0 ? color[3] : basecol[3]);
+ }
+ setColor(basecol.data(), shaderinfo);
+ }
+}
+
+void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const
+{
+ float c[4] = {-1,-1,-1,-1};
+ setColor(colormode, c, shaderinfo);
+}
diff --git a/src/renderer.h b/src/renderer.h
index 2bc482d..f70b4e1 100644
--- a/src/renderer.h
+++ b/src/renderer.h
@@ -2,6 +2,7 @@
#define RENDERER_H_
#include "system-gl.h"
+#include "linalg.h"
#ifdef _MSC_VER // NULL
#include <cstdlib>
@@ -25,8 +26,10 @@ public:
COLORMODE_BACKGROUND_EDGES
};
+ virtual bool getColor(ColorMode colormode, Color4f &col) const;
virtual void setColor(const float color[4], GLint *shaderinfo = NULL) const;
virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const;
+ virtual void setColor(ColorMode colormode, const float color[4], GLint *shaderinfo = NULL) const;
};
#endif // RENDERER_H
diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc
index e073a69..a79b92e 100644
--- a/src/rotateextrude.cc
+++ b/src/rotateextrude.cc
@@ -33,7 +33,6 @@
#include "polyset.h"
#include "visitor.h"
#include "PolySetEvaluator.h"
-#include "openscad.h" // get_fragments_from_r()
#include <sstream>
#include <boost/assign/std/vector.hpp>
diff --git a/src/system-gl.cc b/src/system-gl.cc
index 0c436e5..098dcb2 100644
--- a/src/system-gl.cc
+++ b/src/system-gl.cc
@@ -46,7 +46,8 @@ string glew_extensions_dump()
sort( extensions.begin(), extensions.end() );
stringstream out;
out << "GL Extensions:";
- for ( int i=0;i<extensions.size();i++ ) out << extensions[i] << "\n";
+ for ( unsigned int i=0;i<extensions.size();i++ )
+ out << extensions[i] << "\n";
return out.str();
}
diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt
index 277cff8..214acc5 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
------
@@ -72,10 +65,62 @@ o Compile (F5) - Verify that you get a circular disc
o Edit radius.scad: Change RADIUS
o Compile (F5) - Verify that the disc changed size
-Test9: Circular include
+Test10: Circular include
------
o Open circularincludemain.scad
o Compile (F5)
o Verify that OpenSCAD won't hang or crash
+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 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 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
+------
+o rm subdir/missingsub.scad
+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 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 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 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 ./cascade.sh
+o Open cascadetest.scad
+o Turn on Automatic Reload and Compile
+o Verify that the 4 objects render correctly
+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
new file mode 100755
index 0000000..3193d06
--- /dev/null
+++ b/testdata/modulecache-tests/cascade.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+rm -f cascade*.scad
+echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> 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
+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/cascade2.sh b/testdata/modulecache-tests/cascade2.sh
new file mode 100755
index 0000000..4969e6f
--- /dev/null
+++ b/testdata/modulecache-tests/cascade2.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+rm -f cascade-*.scad
+echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> 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
+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
diff --git a/testdata/modulecache-tests/includemissing.scad b/testdata/modulecache-tests/includemissing.scad
new file mode 100644
index 0000000..d8a165f
--- /dev/null
+++ b/testdata/modulecache-tests/includemissing.scad
@@ -0,0 +1,2 @@
+include <missing.scad>
+missing();
diff --git a/testdata/modulecache-tests/includemissingsub.scad b/testdata/modulecache-tests/includemissingsub.scad
new file mode 100644
index 0000000..50acffe
--- /dev/null
+++ b/testdata/modulecache-tests/includemissingsub.scad
@@ -0,0 +1,2 @@
+include <subdir/missingsub.scad>
+missingsub();
diff --git a/testdata/modulecache-tests/usemissing.scad b/testdata/modulecache-tests/usemissing.scad
new file mode 100644
index 0000000..9f80b8b
--- /dev/null
+++ b/testdata/modulecache-tests/usemissing.scad
@@ -0,0 +1,2 @@
+use <missing.scad>
+missing();
diff --git a/testdata/scad/dxf/arc.scad b/testdata/scad/dxf/arc.scad
index fff70a3..f53b7bd 100644
--- a/testdata/scad/dxf/arc.scad
+++ b/testdata/scad/dxf/arc.scad
@@ -1 +1,2 @@
import("../../dxf/arc.dxf");
+translate([110,0]) import("../../dxf/arc.dxf", $fn=0.1);
diff --git a/testdata/scad/dxf/circle.scad b/testdata/scad/dxf/circle.scad
index 8b5d132..edb9f77 100644
--- a/testdata/scad/dxf/circle.scad
+++ b/testdata/scad/dxf/circle.scad
@@ -1 +1,2 @@
import("../../dxf/circle.dxf");
+translate([210,0,0]) import("../../dxf/circle.dxf", $fn=0.1);
diff --git a/testdata/scad/features/background-modifier.scad b/testdata/scad/features/background-modifier.scad
index 5430472..d67270e 100644
--- a/testdata/scad/features/background-modifier.scad
+++ b/testdata/scad/features/background-modifier.scad
@@ -3,3 +3,8 @@ difference() {
%cylinder(h=30, r=6, center=true);
}
%if (true) cube([25,6,3], center=true);
+
+%translate([0,-9,0]) difference() {
+ color("green") cube([10,4,10], center=true);
+ color("red") translate([0,-2,0]) sphere(3);
+}
diff --git a/testdata/scad/features/circle-tests.scad b/testdata/scad/features/circle-tests.scad
index 6b54d55..90cd9f6 100644
--- a/testdata/scad/features/circle-tests.scad
+++ b/testdata/scad/features/circle-tests.scad
@@ -8,3 +8,4 @@ translate([6,-3,0]) circle(1, $fn=12);
translate([0,-6,0]) circle(1, $fa=20, $fs=0.3);
translate([3,-6,0]) circle(1, $fa=30, $fs=0.3);
translate([6,-6,0]) circle(1, $fa=40, $fs=0.3);
+translate([3,-9,0]) circle(1, $fn=0.1);
diff --git a/testdata/scad/features/cylinder-tests.scad b/testdata/scad/features/cylinder-tests.scad
index 71f43a6..75b06f2 100644
--- a/testdata/scad/features/cylinder-tests.scad
+++ b/testdata/scad/features/cylinder-tests.scad
@@ -16,5 +16,5 @@ translate([22,11,0]) cylinder(h=15, r=5, r2=5);
// tend to "abuse" this for captured nut slots
translate([-10,0,0]) cylinder(h=2, r=3, $fn=6);
-
-// FIXME: We could test $fs, $fa, $fn as well
+// Test that we clamp number of sections to a minimum of 3
+translate([-10, -10, 0]) cylinder(r=3.5356, h=7.0711, $fn=0.1, center=true);
diff --git a/testdata/scad/features/highlight-and-background-modifier.scad b/testdata/scad/features/highlight-and-background-modifier.scad
index 5dca703..d97408b 100644
--- a/testdata/scad/features/highlight-and-background-modifier.scad
+++ b/testdata/scad/features/highlight-and-background-modifier.scad
@@ -1,10 +1,31 @@
difference() {
sphere(r=10);
%#cylinder(h=30, r=6, center=true);
- %#if (true) cube([6,25,3], center=true);
+ %#if (true) cube([25,6,3], center=true);
}
-translate([13,0,0]) difference() {
- sphere(r=10);
- #%cylinder(h=30, r=6, center=true);
- #%if (true) cube([6,25,3], center=true);
+%#translate([0,-9,0]) difference() {
+ color("green") cube([10,4,10], center=true);
+ color("red") translate([0,-2,0]) sphere(3);
+}
+%translate([13,0,0]){
+ difference() {
+ sphere(r=10);
+ cylinder(h=30, r=6, center=true);
+ if (true) cube([25,6,3], center=true);
+ }
+ #translate([0,-9,0]) difference() {
+ color("green") cube([10,4,10], center=true);
+ color("red") translate([0,-2,0]) sphere(3);
+ }
+}
+#translate([26,0,0]){
+ difference() {
+ sphere(r=10);
+ cylinder(h=30, r=6, center=true);
+ if (true) cube([25,6,3], center=true);
+ }
+ %translate([0,-9,0]) difference() {
+ color("green") cube([10,4,10], center=true);
+ color("red") translate([0,-2,0]) sphere(3);
+ }
}
diff --git a/testdata/scad/features/highlight-modifier.scad b/testdata/scad/features/highlight-modifier.scad
index f228d08..2141f58 100644
--- a/testdata/scad/features/highlight-modifier.scad
+++ b/testdata/scad/features/highlight-modifier.scad
@@ -3,3 +3,8 @@ difference() {
#cylinder(h=30, r=6, center=true);
}
#if (true) cube([25,6,3], center=true);
+
+#translate([0,-9,0]) difference() {
+ color("green") cube([10,4,10], center=true);
+ color("red") translate([0,-2,0]) sphere(3);
+}
diff --git a/testdata/scad/features/ifelse-tests.scad b/testdata/scad/features/ifelse-tests.scad
index d8c777b..3c72d55 100644
--- a/testdata/scad/features/ifelse-tests.scad
+++ b/testdata/scad/features/ifelse-tests.scad
@@ -1,5 +1,11 @@
-if (true) cube(2, true);
-else cylinder(r=1,h=2);
+if (true) {
+ cube(2, true);
+ translate([-3,0,0]) cube(2, true);
+}
+else {
+ cylinder(r=1,h=2);
+ translate([-3,0,0]) cylinder(r=1,h=2);
+}
translate([3,0,0])
if (false) cylinder(r=1,h=2);
diff --git a/testdata/scad/features/linear_extrude-tests.scad b/testdata/scad/features/linear_extrude-tests.scad
index 528eea2..680bf53 100644
--- a/testdata/scad/features/linear_extrude-tests.scad
+++ b/testdata/scad/features/linear_extrude-tests.scad
@@ -22,3 +22,6 @@ translate([-15,20,0]) linear_extrude(height=20, scale=[4,5,6]) square(10);
translate([-10,5,0]) linear_extrude(height=15, scale=-2) square(10, center=true);
// scale given as undefined
translate([-15,-15,0]) linear_extrude(height=10, scale=var_undef) square(10);
+
+// height is negative
+translate([0,-25,0]) linear_extrude(-1) square(10, center=true);
diff --git a/testdata/scad/features/rotate_extrude-tests.scad b/testdata/scad/features/rotate_extrude-tests.scad
index 347bc78..010b7d2 100644
--- a/testdata/scad/features/rotate_extrude-tests.scad
+++ b/testdata/scad/features/rotate_extrude-tests.scad
@@ -28,3 +28,7 @@ translate([50,50,0]) {
translate([-50,0,0]) cube([100,100,100], center=true);
}
}
+
+// Minimal $fn
+translate([0,-60,0]) rotate_extrude($fn=1) translate([20,0,0]) circle(r=10,$fn=1);
+
diff --git a/testdata/scad/features/sphere-tests.scad b/testdata/scad/features/sphere-tests.scad
index e666c1b..cc80738 100644
--- a/testdata/scad/features/sphere-tests.scad
+++ b/testdata/scad/features/sphere-tests.scad
@@ -8,3 +8,4 @@ translate([11,11,0]) sphere(5, $fn=15);
translate([22,-11, 0]) sphere(5, $fa=20, $fs=0.3);
translate([22, 0, 0]) sphere(5, $fa=30, $fs=0.3);
translate([22, 11, 0]) sphere(5, $fa=40, $fs=0.3);
+translate([11, 22, 0]) sphere(5, $fn=0.1);
diff --git a/testdata/scad/minimal/allexpressions.scad b/testdata/scad/minimal/allexpressions.scad
new file mode 100644
index 0000000..f618c4b
--- /dev/null
+++ b/testdata/scad/minimal/allexpressions.scad
@@ -0,0 +1,30 @@
+a = true;
+b = false;
+c = undef;
+d = a;
+e = $fn;
+f1 = [1,,];
+f2 = [1,2,3];
+g = f2.x + f2.y + f2.z;
+h1 = [2:5];
+h2 = [1:2:10];
+i = h2.begin - h2.step - h2.end;
+j = "test";
+k = 1.23e-2;
+l = a * b;
+m = a / b;
+n = a % b;
+o = c < d;
+p = c <= d;
+q = c == d;
+r = c != d;
+s = c >= d;
+t = c > d;
+u = e && g;
+v = e || g;
+w = +i;
+x = -i;
+y = !i;
+z = (j);
+aa = k ? l : m;
+bb = n[o];
diff --git a/testdata/scad/minimal/allfunctions.scad b/testdata/scad/minimal/allfunctions.scad
index 2aebe54..b97f121 100644
--- a/testdata/scad/minimal/allfunctions.scad
+++ b/testdata/scad/minimal/allfunctions.scad
@@ -1,27 +1,28 @@
a = abs();
-a = sign();
-a = rands();
-a = min();
-a = max();
-a = sin();
-a = cos();
-a = asin();
-a = acos();
-a = tan();
-a = atan();
-a = atan2();
-a = round();
-a = ceil();
-a = floor();
-a = pow();
-a = sqrt();
-a = exp();
-a = log();
-a = ln();
-a = str();
-a = lookup();
-a = dxf_dim();
-a = dxf_cross();
-a = version();
-a = version_num();
-a = len();
+b = sign();
+c = rands();
+d = min();
+e = max();
+f = sin();
+g = cos();
+h = asin();
+i = acos();
+j = tan();
+k = atan();
+l = atan2();
+m = round();
+n = ceil();
+o = floor();
+p = pow();
+q = sqrt();
+r = exp();
+s = log();
+t = ln();
+u = str();
+v = lookup();
+w = dxf_dim();
+x = dxf_cross();
+y = version();
+z = version_num();
+aa = len();
+bb = search();
diff --git a/testdata/scad/minimal/allmodules.scad b/testdata/scad/minimal/allmodules.scad
index 4395fd7..2e38d8f 100644
--- a/testdata/scad/minimal/allmodules.scad
+++ b/testdata/scad/minimal/allmodules.scad
@@ -2,12 +2,13 @@ minkowski();
glide();
subdiv();
hull();
+resize();
child();
echo();
assign();
for();
intersection_for();
-if(true) { }
+if(false) { cube(); } else { sphere(); }
union();
difference();
intersection();
diff --git a/testdata/scad/misc/value-reassignment-tests.scad b/testdata/scad/misc/value-reassignment-tests.scad
index 475f78f..26afa03 100644
--- a/testdata/scad/misc/value-reassignment-tests.scad
+++ b/testdata/scad/misc/value-reassignment-tests.scad
@@ -1,4 +1,9 @@
+// Test reassignment which depends on a previously assigned variable,
+// as this could be messed up if order of assignment evaluation
+// changes
+
myval = 2;
i = 2;
-myval = i * 2;
-echo(myval);
+myval = i * 2; // This is not (yet) allowed as it will be evaluates in place of the first assignment
+echo(myval, i); // Should output undef, 2
+
diff --git a/testdata/scad/misc/value-reassignment-tests2.scad b/testdata/scad/misc/value-reassignment-tests2.scad
new file mode 100644
index 0000000..29a2fb7
--- /dev/null
+++ b/testdata/scad/misc/value-reassignment-tests2.scad
@@ -0,0 +1,16 @@
+// Test reassignment where another variable has used the previous
+// value before the reassignment. This could get messed up if order of
+// assignment evaluation changes
+
+myval = 2;
+i = myval;
+myval = 3;
+echo(myval, i); // Should output 3, 3
+
+// NB! This feels wrong, but it's a simulation of what happens
+// when overriding a variable on the cmd-line: openscad -Dmyval=3 myfile.scad
+// Since the intention is to override a top-level variable, the evaluation of the
+// new expression must be done in the same place as the old.
+// This is currently solved by appending the text given to the -D parameter to the end
+// of the main file.
+
diff --git a/testdata/scad/misc/variable-scope-tests.scad b/testdata/scad/misc/variable-scope-tests.scad
index 8426fbb..104d1a4 100644
--- a/testdata/scad/misc/variable-scope-tests.scad
+++ b/testdata/scad/misc/variable-scope-tests.scad
@@ -49,5 +49,16 @@ echo("undeclared variable can still be passed and used");
module undeclared_var() {
echo(d);
}
-
undeclared_var(d=6);
+
+echo("attempt to assign from a not-yet-defined variable which also exists globally");
+
+globalval = 1;
+// Test that b = a turns into b = 1, heeding the order of the assignments
+// See issue #399 for more discussion
+module global_lookup() {
+ b = globalval; // Should be assigned 1 since the local one isn't yet defined
+ globalval = 5; // Overrides the value for the local scope only
+ echo(globalval,b); // Should output 5, 1
+}
+global_lookup();
diff --git a/tests/.gitignore b/tests/.gitignore
index 2f5abd7..5033bd2 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -21,5 +21,8 @@ out.png
/throwntogethertest
/cgalstlsanitytest
/cgalcachetest
+/modulecachetest
+/moduledumptest
+/test_pretty_print
/sysinfo.txt
/CTestCustom.cmake \ No newline at end of file
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 39dc341..7509ed1 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -333,6 +333,9 @@ endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
include_directories( /usr/pkg/include /usr/X11R7/include )
set(FLEX_EXECUTABLE /usr/pkg/bin/flex)
+ if(NOT ${CMAKE_CXX_COMPILER} MATCHES ".*clang.*")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ endif()
endif()
find_package(FLEX REQUIRED)
@@ -501,8 +504,8 @@ set(OFFSCREEN_SOURCES
../src/system-gl.cc)
add_library(tests-core STATIC ${CORE_SOURCES})
-target_link_libraries(tests-core ${OPENGL_LIBRARY})
-set(TESTS-CORE-LIBRARIES ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
+target_link_libraries(tests-core ${OPENGL_LIBRARIES})
+set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${Boost_LIBRARIES})
add_library(tests-common STATIC ${COMMON_SOURCES})
target_link_libraries(tests-common tests-core)
@@ -531,6 +534,12 @@ add_executable(dumptest dumptest.cc)
target_link_libraries(dumptest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
#
+# moduledumptest
+#
+add_executable(moduledumptest moduledumptest.cc)
+target_link_libraries(moduledumptest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
+
+#
# modulecachetest
#
add_executable(modulecachetest modulecachetest.cc)
@@ -770,6 +779,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad)
@@ -834,8 +844,6 @@ disable_tests(cgalpngtest_child-background
openscad-cgalpng_child-background
openscad-cgalpng_highlight-and-background-modifier
openscad-cgalpng_testcolornames
- throwntogethertest_child-background
- throwntogethertest_highlight-and-background-modifier
throwntogethertest_testcolornames)
# Test config handling
@@ -922,6 +930,10 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake ${TMP})
add_cmdline_test(echotest SUFFIX txt FILES ${ECHO_FILES})
add_cmdline_test(dumptest SUFFIX txt FILES ${DUMPTEST_FILES})
+add_cmdline_test(moduledumptest SUFFIX txt FILES
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allmodules.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allfunctions.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allexpressions.scad)
add_cmdline_test(csgtexttest SUFFIX txt FILES ${MINIMAL_FILES})
add_cmdline_test(csgtermtest SUFFIX txt FILES ${MINIMAL_FILES})
add_cmdline_test(cgalpngtest SUFFIX png FILES ${CGALPNGTEST_FILES})
diff --git a/tests/moduledumptest.cc b/tests/moduledumptest.cc
new file mode 100644
index 0000000..5925d03
--- /dev/null
+++ b/tests/moduledumptest.cc
@@ -0,0 +1,118 @@
+/*
+ * OpenSCAD (www.openscad.org)
+ * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
+ * Marius Kintel <marius@kintel.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * As a special exception, you have permission to link this program
+ * with the CGAL library and distribute executables, as long as you
+ * follow the requirements of the GNU GPL in regard to all of the
+ * software in the executable aside from CGAL.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "tests-common.h"
+#include "openscad.h"
+#include "parsersettings.h"
+#include "node.h"
+#include "module.h"
+#include "modcontext.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+
+#ifndef _MSC_VER
+#include <getopt.h>
+#endif
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
+#include "boosty.h"
+
+std::string commandline_commands;
+std::string currentdir;
+
+using std::string;
+
+string dumptree(const Tree &tree, const AbstractNode &node)
+{
+ std::stringstream str;
+ const std::vector<AbstractNode*> &children = node.getChildren();
+ for (std::vector<AbstractNode*>::const_iterator iter = children.begin(); iter != children.end(); iter++) {
+ str << tree.getString(**iter) << "\n";
+ }
+ return str.str();
+}
+
+int main(int argc, char **argv)
+{
+#ifdef _MSC_VER
+ _set_output_format(_TWO_DIGIT_EXPONENT);
+#endif
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s <file.scad> <output.txt>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+ const char *outfilename = argv[2];
+ int rc = 0;
+
+ Builtins::instance()->initialize();
+
+ fs::path original_path = fs::current_path();
+
+ currentdir = boosty::stringy(fs::current_path());
+
+ parser_init(boosty::stringy(fs::path(argv[0]).branch_path()));
+ add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries"));
+
+ ModuleContext top_ctx;
+ top_ctx.registerBuiltin();
+
+ FileModule *root_module;
+ ModuleInstantiation root_inst("group");
+
+ root_module = parsefile(filename);
+ if (!root_module) {
+ exit(1);
+ }
+
+ string dumpstdstr = root_module->dump("", "");
+
+ fs::current_path(original_path);
+ std::ofstream outfile;
+ outfile.open(outfilename);
+ if (!outfile.is_open()) {
+ fprintf(stderr, "Error: Unable to open output file %s\n", outfilename);
+ exit(1);
+ }
+ std::cout << "Opened " << outfilename << "\n";
+ outfile << dumpstdstr << "\n";
+ outfile.close();
+ if (outfile.fail()) fprintf(stderr, "Failed to close file\n");
+
+ delete root_module;
+
+ Builtins::instance(true);
+
+ return rc;
+}
diff --git a/tests/regression/cgalpngtest/arc-expected.png b/tests/regression/cgalpngtest/arc-expected.png
index 2f555c4..23a67c7 100644
--- a/tests/regression/cgalpngtest/arc-expected.png
+++ b/tests/regression/cgalpngtest/arc-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/circle-expected.png b/tests/regression/cgalpngtest/circle-expected.png
index aacf12d..89a7fd0 100644
--- a/tests/regression/cgalpngtest/circle-expected.png
+++ b/tests/regression/cgalpngtest/circle-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/circle-tests-expected.png b/tests/regression/cgalpngtest/circle-tests-expected.png
index 0736af5..d640042 100644
--- a/tests/regression/cgalpngtest/circle-tests-expected.png
+++ b/tests/regression/cgalpngtest/circle-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/cylinder-tests-expected.png b/tests/regression/cgalpngtest/cylinder-tests-expected.png
index 843d70f..5d63548 100644
--- a/tests/regression/cgalpngtest/cylinder-tests-expected.png
+++ b/tests/regression/cgalpngtest/cylinder-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/example024-expected.png b/tests/regression/cgalpngtest/example024-expected.png
new file mode 100644
index 0000000..8e69808
--- /dev/null
+++ b/tests/regression/cgalpngtest/example024-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/highlight-modifier-expected.png b/tests/regression/cgalpngtest/highlight-modifier-expected.png
index e220aa1..2fc7678 100644
--- a/tests/regression/cgalpngtest/highlight-modifier-expected.png
+++ b/tests/regression/cgalpngtest/highlight-modifier-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/ifelse-tests-expected.png b/tests/regression/cgalpngtest/ifelse-tests-expected.png
index fcda7bc..6680498 100644
--- a/tests/regression/cgalpngtest/ifelse-tests-expected.png
+++ b/tests/regression/cgalpngtest/ifelse-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
index ee60a72..1488c85 100644
--- a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
+++ b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/sphere-tests-expected.png b/tests/regression/cgalpngtest/sphere-tests-expected.png
index f2a11f3..781d8a9 100644
--- a/tests/regression/cgalpngtest/sphere-tests-expected.png
+++ b/tests/regression/cgalpngtest/sphere-tests-expected.png
Binary files differ
diff --git a/tests/regression/csgtermtest/allexpressions-expected.txt b/tests/regression/csgtermtest/allexpressions-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/allexpressions-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/allmodules-expected.txt b/tests/regression/csgtermtest/allmodules-expected.txt
index f544c01..b802c81 100644
--- a/tests/regression/csgtermtest/allmodules-expected.txt
+++ b/tests/regression/csgtermtest/allmodules-expected.txt
@@ -1 +1 @@
-((((((cube23 + sphere24) + cylinder25) + polyhedron26) + square27) + circle28) + polygon29)
+(((((((sphere12 + cube25) + sphere26) + cylinder27) + polyhedron28) + square29) + circle30) + polygon31)
diff --git a/tests/regression/csgtexttest/allexpressions-expected.txt b/tests/regression/csgtexttest/allexpressions-expected.txt
new file mode 100644
index 0000000..331822f
--- /dev/null
+++ b/tests/regression/csgtexttest/allexpressions-expected.txt
@@ -0,0 +1 @@
+group1
diff --git a/tests/regression/csgtexttest/allmodules-expected.txt b/tests/regression/csgtexttest/allmodules-expected.txt
index 49171ba..95e0029 100644
--- a/tests/regression/csgtexttest/allmodules-expected.txt
+++ b/tests/regression/csgtexttest/allmodules-expected.txt
@@ -1 +1 @@
-group1(minkowski2+glide3+subdiv4+hull5+group6+group6+group6+intersection9+group6+union11+difference12+intersection9+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group6+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render31+surface+transform33+transform33+transform35+transform33+transform33+color38)
+group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40)
diff --git a/tests/regression/dumptest/allexpressions-expected.txt b/tests/regression/dumptest/allexpressions-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tests/regression/dumptest/allexpressions-expected.txt
@@ -0,0 +1 @@
+
diff --git a/tests/regression/dumptest/allmodules-expected.txt b/tests/regression/dumptest/allmodules-expected.txt
index e6bd498..74bf191 100644
--- a/tests/regression/dumptest/allmodules-expected.txt
+++ b/tests/regression/dumptest/allmodules-expected.txt
@@ -2,11 +2,14 @@
glide(path = undef, convexity = 0);
subdiv(level = 1, convexity = 0);
hull();
+ resize(newsize = [0,0,0], auto = [0,0,0]);
group();
group();
group();
intersection();
- group();
+ group() {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 1);
+ }
union();
difference();
intersection();
diff --git a/tests/regression/dumptest/background-modifier-expected.txt b/tests/regression/dumptest/background-modifier-expected.txt
index ed769b3..5861bef 100644
--- a/tests/regression/dumptest/background-modifier-expected.txt
+++ b/tests/regression/dumptest/background-modifier-expected.txt
@@ -5,4 +5,16 @@
%group() {
cube(size = [25, 6, 3], center = true);
}
+ %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ color([0, 0.501961, 0, 1]) {
+ cube(size = [10, 4, 10], center = true);
+ }
+ color([1, 0, 0, 1]) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 3);
+ }
+ }
+ }
+ }
diff --git a/tests/regression/dumptest/circle-tests-expected.txt b/tests/regression/dumptest/circle-tests-expected.txt
index 910b375..28359d8 100644
--- a/tests/regression/dumptest/circle-tests-expected.txt
+++ b/tests/regression/dumptest/circle-tests-expected.txt
@@ -26,4 +26,7 @@
multmatrix([[1, 0, 0, 6], [0, 1, 0, -6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
circle($fn = 0, $fa = 40, $fs = 0.3, r = 1);
}
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 0.1, $fa = 12, $fs = 2, r = 1);
+ }
diff --git a/tests/regression/dumptest/cylinder-tests-expected.txt b/tests/regression/dumptest/cylinder-tests-expected.txt
index b1e8b6e..c47ffe1 100644
--- a/tests/regression/dumptest/cylinder-tests-expected.txt
+++ b/tests/regression/dumptest/cylinder-tests-expected.txt
@@ -32,4 +32,7 @@
multmatrix([[1, 0, 0, -10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 6, $fa = 12, $fs = 2, h = 2, r1 = 3, r2 = 3, center = false);
}
+ multmatrix([[1, 0, 0, -10], [0, 1, 0, -10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0.1, $fa = 12, $fs = 2, h = 7.0711, r1 = 3.5356, r2 = 3.5356, center = true);
+ }
diff --git a/tests/regression/dumptest/example024-expected.txt b/tests/regression/dumptest/example024-expected.txt
new file mode 100644
index 0000000..e7d6e8b
--- /dev/null
+++ b/tests/regression/dumptest/example024-expected.txt
@@ -0,0 +1,1869 @@
+ difference() {
+ multmatrix([[0.81649658092, 0.40824829046, 0.40824829046, 0], [0, 0.70710678118, -0.70710678118, 0], [-0.57735026919, 0.57735026919, 0.57735026919, 0], [0, 0, 0, 1]]) {
+ group() {
+ difference() {
+ cube(size = [100, 100, 100], center = true);
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 33.3333, 33.3333], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[0, -1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 33.3333, 33.3333], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 33.3333, 33.3333], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 11.1111, 11.1111], center = true);
+ group() {
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group();
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) {
+ group() {
+ cube(size = [110, 3.7037, 3.7037], center = true);
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -100], [0, 0, 0, 1]]) {
+ cube(size = [200, 200, 200], center = true);
+ }
+ }
+
diff --git a/tests/regression/dumptest/highlight-and-background-modifier-expected.txt b/tests/regression/dumptest/highlight-and-background-modifier-expected.txt
index 20c82cc..eb8931c 100644
--- a/tests/regression/dumptest/highlight-and-background-modifier-expected.txt
+++ b/tests/regression/dumptest/highlight-and-background-modifier-expected.txt
@@ -2,15 +2,60 @@
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
%cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true);
%group() {
- cube(size = [6, 25, 3], center = true);
+ cube(size = [25, 6, 3], center = true);
}
}
- multmatrix([[1, 0, 0, 13], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ color([0, 0.501961, 0, 1]) {
+ cube(size = [10, 4, 10], center = true);
+ }
+ color([1, 0, 0, 1]) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 3);
+ }
+ }
+ }
+ }
+ %multmatrix([[1, 0, 0, 13], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true);
+ group() {
+ cube(size = [25, 6, 3], center = true);
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ color([0, 0.501961, 0, 1]) {
+ cube(size = [10, 4, 10], center = true);
+ }
+ color([1, 0, 0, 1]) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 3);
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 26], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
difference() {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
- %cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true);
- %group() {
- cube(size = [6, 25, 3], center = true);
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true);
+ group() {
+ cube(size = [25, 6, 3], center = true);
+ }
+ }
+ %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ color([0, 0.501961, 0, 1]) {
+ cube(size = [10, 4, 10], center = true);
+ }
+ color([1, 0, 0, 1]) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 3);
+ }
+ }
}
}
}
diff --git a/tests/regression/dumptest/highlight-modifier-expected.txt b/tests/regression/dumptest/highlight-modifier-expected.txt
index c6204bd..c0a29ad 100644
--- a/tests/regression/dumptest/highlight-modifier-expected.txt
+++ b/tests/regression/dumptest/highlight-modifier-expected.txt
@@ -5,4 +5,16 @@
group() {
cube(size = [25, 6, 3], center = true);
}
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ color([0, 0.501961, 0, 1]) {
+ cube(size = [10, 4, 10], center = true);
+ }
+ color([1, 0, 0, 1]) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 3);
+ }
+ }
+ }
+ }
diff --git a/tests/regression/dumptest/ifelse-tests-expected.txt b/tests/regression/dumptest/ifelse-tests-expected.txt
index 1b60002..e2d3fce 100644
--- a/tests/regression/dumptest/ifelse-tests-expected.txt
+++ b/tests/regression/dumptest/ifelse-tests-expected.txt
@@ -1,5 +1,8 @@
group() {
cube(size = [2, 2, 2], center = true);
+ multmatrix([[1, 0, 0, -3], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cube(size = [2, 2, 2], center = true);
+ }
}
multmatrix([[1, 0, 0, 3], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
group() {
diff --git a/tests/regression/dumptest/linear_extrude-tests-expected.txt b/tests/regression/dumptest/linear_extrude-tests-expected.txt
index c867388..face5e2 100644
--- a/tests/regression/dumptest/linear_extrude-tests-expected.txt
+++ b/tests/regression/dumptest/linear_extrude-tests-expected.txt
@@ -54,4 +54,9 @@
square(size = [10, 10], center = false);
}
}
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(height = 0, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
+ square(size = [10, 10], center = true);
+ }
+ }
diff --git a/tests/regression/dumptest/rotate_extrude-tests-expected.txt b/tests/regression/dumptest/rotate_extrude-tests-expected.txt
index 42faff2..023d78d 100644
--- a/tests/regression/dumptest/rotate_extrude-tests-expected.txt
+++ b/tests/regression/dumptest/rotate_extrude-tests-expected.txt
@@ -42,4 +42,11 @@
}
}
}
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -60], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ rotate_extrude(convexity = 1, $fn = 1, $fa = 12, $fs = 2) {
+ multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 1, $fa = 12, $fs = 2, r = 10);
+ }
+ }
+ }
diff --git a/tests/regression/dumptest/sphere-tests-expected.txt b/tests/regression/dumptest/sphere-tests-expected.txt
index 84f8c23..461e946 100644
--- a/tests/regression/dumptest/sphere-tests-expected.txt
+++ b/tests/regression/dumptest/sphere-tests-expected.txt
@@ -26,4 +26,7 @@
multmatrix([[1, 0, 0, 22], [0, 1, 0, 11], [0, 0, 1, 0], [0, 0, 0, 1]]) {
sphere($fn = 0, $fa = 40, $fs = 0.3, r = 5);
}
+ multmatrix([[1, 0, 0, 11], [0, 1, 0, 22], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0.1, $fa = 12, $fs = 2, r = 5);
+ }
diff --git a/tests/regression/echotest/value-reassignment-tests-expected.txt b/tests/regression/echotest/value-reassignment-tests-expected.txt
index 05a6741..344f7ab 100644
--- a/tests/regression/echotest/value-reassignment-tests-expected.txt
+++ b/tests/regression/echotest/value-reassignment-tests-expected.txt
@@ -1 +1,2 @@
-ECHO: 4
+WARNING: Ignoring unknown variable 'i'.
+ECHO: undef, 2
diff --git a/tests/regression/echotest/value-reassignment-tests2-expected.txt b/tests/regression/echotest/value-reassignment-tests2-expected.txt
new file mode 100644
index 0000000..efb1be7
--- /dev/null
+++ b/tests/regression/echotest/value-reassignment-tests2-expected.txt
@@ -0,0 +1 @@
+ECHO: 3, 3
diff --git a/tests/regression/echotest/variable-scope-tests-expected.txt b/tests/regression/echotest/variable-scope-tests-expected.txt
index 92db05d..5994778 100644
--- a/tests/regression/echotest/variable-scope-tests-expected.txt
+++ b/tests/regression/echotest/variable-scope-tests-expected.txt
@@ -14,3 +14,5 @@ ECHO: undef
ECHO: 5
ECHO: "undeclared variable can still be passed and used"
ECHO: 6
+ECHO: "attempt to assign from a not-yet-defined variable which also exists globally"
+ECHO: 5, 1
diff --git a/tests/regression/moduledumptest/allexpressions-expected.txt b/tests/regression/moduledumptest/allexpressions-expected.txt
new file mode 100644
index 0000000..6d9de40
--- /dev/null
+++ b/tests/regression/moduledumptest/allexpressions-expected.txt
@@ -0,0 +1,31 @@
+a = true;
+b = false;
+c = undef;
+d = a;
+e = $fn;
+f1 = [1];
+f2 = [1, 2, 3];
+g = ((f2.x + f2.y) + f2.z);
+h1 = [2 : 1 : 5];
+h2 = [1 : 2 : 10];
+i = ((h2.begin - h2.step) - h2.end);
+j = "test";
+k = 0.0123;
+l = (a * b);
+m = (a / b);
+n = (a % b);
+o = (c < d);
+p = (c <= d);
+q = (c == d);
+r = (c != d);
+s = (c >= d);
+t = (c > d);
+u = (e && g);
+v = (e || g);
+w = i;
+x = -i;
+y = !i;
+z = j;
+aa = (k ? l : m);
+bb = n[o];
+
diff --git a/tests/regression/moduledumptest/allfunctions-expected.txt b/tests/regression/moduledumptest/allfunctions-expected.txt
new file mode 100644
index 0000000..a8a0fea
--- /dev/null
+++ b/tests/regression/moduledumptest/allfunctions-expected.txt
@@ -0,0 +1,29 @@
+a = abs();
+b = sign();
+c = rands();
+d = min();
+e = max();
+f = sin();
+g = cos();
+h = asin();
+i = acos();
+j = tan();
+k = atan();
+l = atan2();
+m = round();
+n = ceil();
+o = floor();
+p = pow();
+q = sqrt();
+r = exp();
+s = log();
+t = ln();
+u = str();
+v = lookup();
+w = dxf_dim();
+x = dxf_cross();
+y = version();
+z = version_num();
+aa = len();
+bb = search();
+
diff --git a/tests/regression/moduledumptest/allmodules-expected.txt b/tests/regression/moduledumptest/allmodules-expected.txt
new file mode 100644
index 0000000..0e3fc32
--- /dev/null
+++ b/tests/regression/moduledumptest/allmodules-expected.txt
@@ -0,0 +1,41 @@
+minkowski();
+glide();
+subdiv();
+hull();
+resize();
+child();
+echo();
+assign();
+for();
+intersection_for();
+if(false) cube();
+else sphere();
+union();
+difference();
+intersection();
+dxf_linear_extrude();
+linear_extrude();
+dxf_rotate_extrude();
+rotate_extrude();
+import();
+import_stl();
+import_off();
+import_dxf();
+group();
+cube();
+sphere();
+cylinder();
+polyhedron();
+square();
+circle();
+polygon();
+projection();
+render();
+surface();
+scale();
+rotate();
+mirror();
+translate();
+multmatrix();
+color();
+
diff --git a/tests/regression/opencsgtest/arc-expected.png b/tests/regression/opencsgtest/arc-expected.png
index a930c0e..7361853 100644
--- a/tests/regression/opencsgtest/arc-expected.png
+++ b/tests/regression/opencsgtest/arc-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/background-modifier-expected.png b/tests/regression/opencsgtest/background-modifier-expected.png
index 2505331..9cf7db0 100644
--- a/tests/regression/opencsgtest/background-modifier-expected.png
+++ b/tests/regression/opencsgtest/background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/circle-expected.png b/tests/regression/opencsgtest/circle-expected.png
index 7bc63b1..14fce98 100644
--- a/tests/regression/opencsgtest/circle-expected.png
+++ b/tests/regression/opencsgtest/circle-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/circle-tests-expected.png b/tests/regression/opencsgtest/circle-tests-expected.png
index 2e5b314..06f7d9c 100644
--- a/tests/regression/opencsgtest/circle-tests-expected.png
+++ b/tests/regression/opencsgtest/circle-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/cylinder-tests-expected.png b/tests/regression/opencsgtest/cylinder-tests-expected.png
index 4c7ab79..d0c607c 100644
--- a/tests/regression/opencsgtest/cylinder-tests-expected.png
+++ b/tests/regression/opencsgtest/cylinder-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/difference-tests-expected.png b/tests/regression/opencsgtest/difference-tests-expected.png
index a6d863a..8db2742 100644
--- a/tests/regression/opencsgtest/difference-tests-expected.png
+++ b/tests/regression/opencsgtest/difference-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/example024-expected.png b/tests/regression/opencsgtest/example024-expected.png
new file mode 100644
index 0000000..dfbe847
--- /dev/null
+++ b/tests/regression/opencsgtest/example024-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png b/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png
index 8febe76..c1c7313 100644
--- a/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png
+++ b/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/highlight-modifier-expected.png b/tests/regression/opencsgtest/highlight-modifier-expected.png
index af01e5b..1021c22 100644
--- a/tests/regression/opencsgtest/highlight-modifier-expected.png
+++ b/tests/regression/opencsgtest/highlight-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/ifelse-tests-expected.png b/tests/regression/opencsgtest/ifelse-tests-expected.png
index 6dae4df..84e18e3 100644
--- a/tests/regression/opencsgtest/ifelse-tests-expected.png
+++ b/tests/regression/opencsgtest/ifelse-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/rotate_extrude-tests-expected.png b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png
index 96452e1..861f6ab 100644
--- a/tests/regression/opencsgtest/rotate_extrude-tests-expected.png
+++ b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/sphere-tests-expected.png b/tests/regression/opencsgtest/sphere-tests-expected.png
index d11e3bf..d1b4845 100644
--- a/tests/regression/opencsgtest/sphere-tests-expected.png
+++ b/tests/regression/opencsgtest/sphere-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/arc-expected.png b/tests/regression/throwntogethertest/arc-expected.png
index 8783806..7361853 100644
--- a/tests/regression/throwntogethertest/arc-expected.png
+++ b/tests/regression/throwntogethertest/arc-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/background-modifier-expected.png b/tests/regression/throwntogethertest/background-modifier-expected.png
index 2505331..499b92f 100644
--- a/tests/regression/throwntogethertest/background-modifier-expected.png
+++ b/tests/regression/throwntogethertest/background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/child-background-expected.png b/tests/regression/throwntogethertest/child-background-expected.png
new file mode 100644
index 0000000..7540ee6
--- /dev/null
+++ b/tests/regression/throwntogethertest/child-background-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/circle-expected.png b/tests/regression/throwntogethertest/circle-expected.png
index c001955..14fce98 100644
--- a/tests/regression/throwntogethertest/circle-expected.png
+++ b/tests/regression/throwntogethertest/circle-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/circle-tests-expected.png b/tests/regression/throwntogethertest/circle-tests-expected.png
index 2e5b314..06f7d9c 100644
--- a/tests/regression/throwntogethertest/circle-tests-expected.png
+++ b/tests/regression/throwntogethertest/circle-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/cylinder-tests-expected.png b/tests/regression/throwntogethertest/cylinder-tests-expected.png
index 4c7ab79..d0c607c 100644
--- a/tests/regression/throwntogethertest/cylinder-tests-expected.png
+++ b/tests/regression/throwntogethertest/cylinder-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/difference-tests-expected.png b/tests/regression/throwntogethertest/difference-tests-expected.png
index 0a27c90..7a61f42 100644
--- a/tests/regression/throwntogethertest/difference-tests-expected.png
+++ b/tests/regression/throwntogethertest/difference-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/example024-expected.png b/tests/regression/throwntogethertest/example024-expected.png
new file mode 100644
index 0000000..ffa2360
--- /dev/null
+++ b/tests/regression/throwntogethertest/example024-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png b/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png
new file mode 100644
index 0000000..e95f763
--- /dev/null
+++ b/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/highlight-modifier-expected.png b/tests/regression/throwntogethertest/highlight-modifier-expected.png
index 7973d82..e8d4e62 100644
--- a/tests/regression/throwntogethertest/highlight-modifier-expected.png
+++ b/tests/regression/throwntogethertest/highlight-modifier-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/ifelse-tests-expected.png b/tests/regression/throwntogethertest/ifelse-tests-expected.png
index 7f6374c..5cb1c4c 100644
--- a/tests/regression/throwntogethertest/ifelse-tests-expected.png
+++ b/tests/regression/throwntogethertest/ifelse-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png b/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png
index 94b0d9c..8956be2 100644
--- a/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png
+++ b/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/sphere-tests-expected.png b/tests/regression/throwntogethertest/sphere-tests-expected.png
index d11e3bf..d1b4845 100644
--- a/tests/regression/throwntogethertest/sphere-tests-expected.png
+++ b/tests/regression/throwntogethertest/sphere-tests-expected.png
Binary files differ
diff --git a/tests/virtualfb.sh b/tests/virtualfb.sh
index 186b389..c5c9668 100755
--- a/tests/virtualfb.sh
+++ b/tests/virtualfb.sh
@@ -24,7 +24,11 @@ start()
fi
VFB_DISPLAY=`echo | awk 'BEGIN{srand();} {printf ":%.0f", rand()*1000+100};'`
- $VFB_BINARY $VFB_DISPLAY -screen 0 800x600x24 &> ./virtualfb.log &
+ if [ $debug ]; then
+ echo debug VFB_DISPLAY $VFB_DISPLAY
+ echo debug VFB_BINARY $VFB_BINARY
+ fi
+ $VFB_BINARY $VFB_DISPLAY -screen 0 800x600x24 > ./virtualfb1.log 2> ./virtualfb2.log &
# on some systems $! gives us VFB_BINARY's PID, on others we have to subtract 1
VFB_PID_MINUS0=$!
VFB_PID_MINUS1=$(($VFB_PID_MINUS0 - 1))
diff --git a/win32.pri b/win.pri
index bb41b09..bb41b09 100644
--- a/win32.pri
+++ b/win.pri
contact: Jan Huwald // Impressum