summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDon Bright <hugh.m.bright@gmail.com>2012-05-28 16:48:46 (GMT)
committerDon Bright <hugh.m.bright@gmail.com>2012-05-28 16:48:46 (GMT)
commitdd2002a81673b3875ce8c4e8a61cb10278c4eb03 (patch)
tree7aaadf1c9b12cd37a7a913d3e76256f6406fa939
parent4381762f5aa2e6a56258618e585e1510ead88684 (diff)
parent67eb2ebe90447e966dc1e08b91c43d937c521583 (diff)
Tidy up code. Generate proper test png images. Merge branch 'master' into caliston1.
Conflicts: src/PolySetCGALEvaluator.cc
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules2
-rw-r--r--README88
-rw-r--r--README.md139
-rw-r--r--RELEASE_NOTES9
-rw-r--r--contrib/OpenSCAD.xml2
-rw-r--r--doc/TODO.txt19
-rw-r--r--doc/testing.txt14
-rw-r--r--examples/example023.scad20
m---------libraries/MCAD0
-rw-r--r--openscad.pro2
-rw-r--r--openscad.pro.user19
-rwxr-xr-xscripts/macosx-build-dependencies.sh181
-rwxr-xr-xscripts/release-macosx.sh69
-rw-r--r--src/CGALEvaluator.cc63
-rw-r--r--src/CGAL_Nef_polyhedron.cc2
-rw-r--r--src/MainWindow.h16
-rw-r--r--src/MainWindow.ui6
-rw-r--r--src/ModuleCache.cc106
-rw-r--r--src/ModuleCache.h23
-rw-r--r--src/OpenCSGWarningDialog.cc2
-rw-r--r--src/PolySetCGALEvaluator.cc199
-rw-r--r--src/PolySetCGALEvaluator.h2
-rw-r--r--src/context.cc1
-rw-r--r--src/csgtermnormalizer.cc5
-rw-r--r--src/dxfdata.cc8
-rw-r--r--src/dxftess-cgal.cc2
-rw-r--r--src/expr.cc4
-rw-r--r--src/func.cc140
-rw-r--r--src/glview.cc27
-rw-r--r--src/lexer.l19
-rw-r--r--src/mainwin.cc354
-rw-r--r--src/module.cc37
-rw-r--r--src/module.h13
-rw-r--r--src/openscad.cc45
-rw-r--r--src/openscad.h2
-rw-r--r--src/parser.y120
-rw-r--r--src/primitives.cc2
-rw-r--r--src/system-gl.h1
-rw-r--r--src/transform.cc9
-rw-r--r--src/value.cc67
-rw-r--r--src/value.h2
-rw-r--r--testdata/scad/bugs/issue95-normalization-crash.scad128
-rw-r--r--testdata/scad/features/text-search-test.scad29
-rw-r--r--testdata/scad/misc/search-tests.scad63
-rw-r--r--testdata/scad/misc/vector-values.scad40
-rw-r--r--tests/CMakeLists.txt44
-rw-r--r--tests/FindGLEW.cmake94
-rw-r--r--tests/cgalcachetest.cc4
-rw-r--r--tests/cgalpngtest.cc4
-rw-r--r--tests/cgalstlsanitytest.cc4
-rw-r--r--tests/cgaltest.cc4
-rw-r--r--tests/csgtermtest.cc4
-rw-r--r--tests/csgtestcore.cc4
-rw-r--r--tests/csgtexttest.cc4
-rw-r--r--tests/dumptest.cc4
-rw-r--r--tests/echotest.cc4
-rw-r--r--tests/modulecachetest.cc123
-rw-r--r--tests/regression/cgalpngtest/example023-expected.pngbin0 -> 6984 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-tests-expected.pngbin9159 -> 5599 bytes
-rw-r--r--tests/regression/cgalpngtest/scale2D-tests-expected.pngbin0 -> 7215 bytes
-rw-r--r--tests/regression/cgalpngtest/scale3D-tests-expected.pngbin0 -> 6642 bytes
-rw-r--r--tests/regression/cgalpngtest/text-search-test-expected.pngbin0 -> 3623 bytes
-rw-r--r--tests/regression/dumptest/scale2D-tests-expected.txt34
-rw-r--r--tests/regression/dumptest/scale3D-tests-expected.txt43
-rw-r--r--tests/regression/dumptest/text-search-test-expected.txt848
-rw-r--r--tests/regression/echotest/search-tests-expected.txt20
-rw-r--r--tests/regression/echotest/vector-values-expected.txt10
-rw-r--r--tests/regression/opencsgtest/example023-expected.pngbin0 -> 7402 bytes
-rw-r--r--tests/regression/opencsgtest/projection-tests-expected.pngbin9661 -> 6207 bytes
-rw-r--r--tests/regression/opencsgtest/scale2D-tests-expected.pngbin0 -> 7716 bytes
-rw-r--r--tests/regression/opencsgtest/scale3D-tests-expected.pngbin0 -> 7278 bytes
-rw-r--r--tests/regression/opencsgtest/text-search-test-expected.pngbin0 -> 4981 bytes
-rw-r--r--tests/regression/throwntogethertest/example023-expected.pngbin0 -> 7402 bytes
-rw-r--r--tests/regression/throwntogethertest/projection-tests-expected.pngbin5062 -> 5642 bytes
-rw-r--r--tests/regression/throwntogethertest/scale2D-tests-expected.pngbin0 -> 7716 bytes
-rw-r--r--tests/regression/throwntogethertest/scale3D-tests-expected.pngbin0 -> 7278 bytes
-rw-r--r--tests/regression/throwntogethertest/text-search-test-expected.pngbin0 -> 4981 bytes
-rw-r--r--tests/tests-common.cc7
-rw-r--r--tests/tests-common.h2
80 files changed, 2635 insertions, 728 deletions
diff --git a/.gitignore b/.gitignore
index a1db306..50dace1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ parser_yacc.h
/tmp
/OpenSCAD.app
*/#*#
+/openscad
diff --git a/.gitmodules b/.gitmodules
index 4ab4cf0..6556377 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "libraries/MCAD"]
path = libraries/MCAD
- url = git@github.com:openscad/MCAD.git
+ url = ../MCAD.git
diff --git a/README b/README
deleted file mode 100644
index 8c000fa..0000000
--- a/README
+++ /dev/null
@@ -1,88 +0,0 @@
-
-WHAT IS IT?
-===========
-
-OpenSCAD is a software for creating solid 3D CAD objects. It is free software
-and available for Linux/UNIX, MS Windows and Mac OS X.
-
-Unlike most free software for creating 3D models (such as the famous
-application Blender) it does not focus on the artistic aspects of 3D modelling
-but instead on the CAD aspects. Thus it might be the application you are
-looking for when you are planning to create 3D models of machine parts but
-pretty sure is not what you are looking for when you are more interested in
-creating computer-animated movies.
-
-OpenSCAD is not an interactive modeller. Instead it is something like a
-3D-compiler that reads in a script file that describes the object and renders
-the 3D model from this script file (see examples below). This gives you (the
-designer) full control over the modelling process and enables you to easily
-change any step in the modelling process or make designs that are defined by
-configurable parameters.
-
-OpenSCAD provides two main modelling techniques: First there is constructive
-solid geometry (aka CSG) and second there is extrusion of 2D outlines. As data
-exchange format format for this 2D outlines Autocad DXF files are used. In
-addition to 2D paths for extrusion it is also possible to read design parametes
-from DXF files. Besides DXF files OpenSCAD can read and create 3D models in the
-STL and OFF file formats.
-
-
-PREREQUISITES
-=============
-
-To build OpenSCAD, you need some libraries and tools. The version
-numbers in brackets specify the versions which have been used for
-development. Other versions may or may not work as well..
-
-* Qt4 (4.4 - 4.7):
- http://www.qt.nokia.com/
-
-* CGAL (3.6 - 3.9):
- http://www.cgal.org/
-
-* GMP (5.0.x):
- http://www.gmplib.org/
-
-* MPFR (3.x):
- http://www.mpfr.org/
-
-* boost (1.35 - 1.47)
- http://www.boost.org/
-
-* cmake (2.6 - 2.8, required by CGAL and the test framework)
- http://www.cmake.org/
-
-* OpenCSG (1.3.2):
- http://www.opencsg.org/
-
-* GLEW (1.5 ->)
- http://glew.sourceforge.net/
-
-* Eigen2 (2.0.13->)
- http://eigen.tuxfamily.org/
-
-* GCC C++ Compiler (4.2 ->):
- http://gcc.gnu.org/
-
-* Bison (2.4):
- http://www.gnu.org/software/bison/
-
-* Flex (2.5.35):
- http://flex.sourceforge.net/
-
-
-BUILDING OPENSCAD
-================
-
-First, run 'qmake' from Qt4 to generate a Makefile. On some systems you need to
-run 'qmake4', 'qmake-qt4' or something alike to run the qt4 version of the tool.
-
-Then run make. Finally you might run 'make install' as root or simply copy the
-'openscad' binary (OpenSCAD.app on Mac OS X) to the bin directory of your choice.
-
-
-DOCUMENTATION
-=============
-
-Have a look at the OpenSCAD Homepage (http://openscad.org/) for documentation.
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..13fba4d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,139 @@
+# What is OpenSCAD?
+
+OpenSCAD is a software for creating solid 3D CAD objects. It is free software
+and available for Linux/UNIX, MS Windows and Mac OS X.
+
+Unlike most free software for creating 3D models (such as the famous
+application Blender) it does not focus on the artistic aspects of 3D modeling
+but instead on the CAD aspects. Thus it might be the application you are
+looking for when you are planning to create 3D models of machine parts but
+pretty sure is not what you are looking for when you are more interested in
+creating computer-animated movies.
+
+OpenSCAD is not an interactive modeler. Instead it is something like a
+3D-compiler that reads in a script file that describes the object and renders
+the 3D model from this script file (see examples below). This gives you (the
+designer) full control over the modeling process and enables you to easily
+change any step in the modeling process or make designs that are defined by
+configurable parameters.
+
+OpenSCAD provides two main modeling techniques: First there is constructive
+solid geometry (aka CSG) and second there is extrusion of 2D outlines. As data
+exchange format format for this 2D outlines Autocad DXF files are used. In
+addition to 2D paths for extrusion it is also possible to read design parameters
+from DXF files. Besides DXF files OpenSCAD can read and create 3D models in the
+STL and OFF file formats.
+
+# Getting started
+
+You can download the latest binaries of OpenSCAD at
+<http://www.openscad.org>. Install binaries as you would any other
+software.
+
+When you open OpenSCAD, you'll see three frames within the window. The
+left frame is where you'll write code to model 3D objects. The right
+frame is where you'll see the 3D rendering of your model.
+
+Let's make a tree! Type the following code into the left frame:
+
+ cylinder(h = 30, r = 8);
+
+Then render the 3D model by hitting F5. Now you can see a cylinder for
+the trunk in our tree. Now let's add the bushy/leafy part of the tree
+represented by a sphere. To do so, we will union a cylinder and a
+sphere.
+
+ union() {
+ cylinder(h = 30, r = 8);
+ sphere(20);
+ }
+
+But, it's not quite right! The bushy/leafy are around the base of the
+tree. We need to move the sphere up the z-axis.
+
+ union() {
+ cylinder(h = 30, r = 8);
+ translate([0, 0, 40]) sphere(20);
+ }
+
+And that's it! You made your first 3D model! There are other primitive
+shapes that you can combine with other set operations (union,
+intersection, difference) and transformations (rotate, scale,
+translate) to make complex models! Check out all the other language
+features in the [OpenSCAD
+Manual](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual).
+
+# Documentation
+
+Have a look at the OpenSCAD Homepage (http://openscad.org/) for documentation.
+
+## Building OpenSCAD
+
+To build OpenSCAD from source, follow the instructions for the
+platform applicable to you below.
+
+### Prerequisites
+
+To build OpenSCAD, you need some libraries and tools. The version
+numbers in brackets specify the versions which have been used for
+development. Other versions may or may not work as well.
+
+If you're using Ubuntu, you can install these libraries from
+aptitude. If you're using Mac, there is a build script that compiles
+the libraries from source. Follow the instructions for the platform
+you're compiling on below.
+
+* [Qt4 (4.4 - 4.7)](http://www.qt.nokia.com/)
+* [CGAL (3.6 - 3.9)](http://www.cgal.org/)
+ * [GMP (5.0.x)](http://www.gmplib.org/)
+ * [cmake (2.6 - 2.8, required by CGAL and the test framework)](http://www.cmake.org/)
+ * [MPFR (3.x)](http://www.mpfr.org/)
+ * [boost (1.35 - 1.47)](http://www.boost.org/)
+* [OpenCSG (1.3.2)](http://www.opencsg.org/)
+* [GLEW (1.6 ->)](http://glew.sourceforge.net/)
+* [Eigen2 (2.0.13->)](http://eigen.tuxfamily.org/)
+* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
+* [Bison (2.4)](http://www.gnu.org/software/bison/)
+* [Flex (2.5.35)](http://flex.sourceforge.net/)
+
+### Building for Mac OS X
+
+First, make sure that you have XCode installed to get GCC. Then after
+you've cloned this git repository, run the script that sets up the
+environment variables.
+
+ source setenv_mjau.sh
+
+Then run the script to compile all the prerequisite libraries above:
+
+ ./scripts/macosx-build-dependencies.sh
+
+We currently don't use [MacPorts](http://www.macports.org) or
+[brew](http://mxcl.github.com/homebrew/) to install the prerequisite
+libraries because CGAL doesn't exist on brew and opencsg doesn't exist
+on ports. And more importantly, there are some patches to GMP in the
+compilation process.
+
+After that, follow the Compilation instructions below.
+
+### Building for Ubuntu
+
+If you have done this and want to contribute, fork the repo and
+contribute docs on how to build for windows!
+
+### Building for Windows
+
+If you have done this and want to contribute, fork the repo and
+contribute docs on how to build for windows!
+
+### Compilation
+
+First, run 'qmake' from Qt4 to generate a Makefile. On some systems you need to
+run 'qmake4', 'qmake-qt4' or something alike to run the qt4 version of the tool.
+
+Then run make. Finally you might run 'make install' as root or simply copy the
+'openscad' binary (OpenSCAD.app on Mac OS X) to the bin directory of your choice.
+
+If you had problems compiling from source, raise a new issue in the
+[issue tracker on the github page](https://github.com/openscad/openscad/issues).
+
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 8d66ad9..32f3788 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -5,13 +5,20 @@ Features:
o Snappier GUI while performing CGAL computations (computations running in separate thread)
o The size of the misc. caches can now be adjusted from Preferences
o The limit for when to disable OpenCSG can now be adjusted from Preferences
+o Added Dot product operator: vec * vec
+o Added Matrix multiplication operator: vec * mat, mat * mat
+o Added search() function
+o Dependencies are now tracked - any changes in uses/included files will be detected and cause a recompile
Bugfixes:
o use'ing an non-existing file sometimes crashed under Windows
o Better font handling: Ensure a monospace font is chosen as default
o Division by zero caused hang in some cases (e.g. sin(1/0))
o Larger minkowski operations sometimes caused a crash after a CGAL assert was thrown
-o Fixed crashes in shared_ptr.hpp (or similar places) due to a cache management bug
+o Fixed crashes in shared_ptr.hpp (or similar places) due bugs in cache management and CSG normalization
+o scale() with a scale factor of zero could cause a crash
+o Fixed a number of issues related to use/include
+o Providing an unknown parameter on the cmd-line caused a crash
Deprecations:
o The old include syntax "<filename.scad>" without the include keyword is no
diff --git a/contrib/OpenSCAD.xml b/contrib/OpenSCAD.xml
index 7c6cf76..e632887 100644
--- a/contrib/OpenSCAD.xml
+++ b/contrib/OpenSCAD.xml
@@ -1,3 +1,5 @@
+<!-- (C) 2011 tjhowse tjhowse@gmail.com, published under the terms of the GNU GPL version 2 or later -->
+
<NotepadPlus>
<UserLang name="OpenSCAD" ext="scad">
<Settings>
diff --git a/doc/TODO.txt b/doc/TODO.txt
index 60078e2..7f8378d 100644
--- a/doc/TODO.txt
+++ b/doc/TODO.txt
@@ -1,6 +1,6 @@
-BUGS
-----
+BUGS (FIXME: Verify and move these to github)
+---------------------------------------------
o Some invalid DXF data gets pass the import checks and breaks the tessing code
o Tesselation via GLU sometimes produces strange results
o Export STL: Exports existing CGAL model even though the current model is changed, but not CGAL rendered
@@ -8,8 +8,8 @@ o Look into the polygon winding and rotate_extrude() problem reported by Britton
o CGAL Aff_transformation_3 doesn't support non-affine transformations (non-aff-matrix.scad)
o 2D union of polygons with a touching vertex doesn't work. see testdata/scad/bugs/polygon-touch.scad
-STL Import BUGS
----------------
+STL Import BUGS (FIXME: Verify and move these to github)
+--------------------------------------------------------
Using STL-imported models is tricky and triggers multiple issues:
(these all fail with the usual "Illegal polygonal object" error)
@@ -73,6 +73,7 @@ o 3D View
- 2D objects are rendered at z = -0.1 - why?
- Rewrite to use VBOs or smth. - avoid inline calculations
- Rewrite to a higher-level library (e.g. OSG)?
+ - Make navigation (e.g. zoom) available through menu entries (requested by disabled user)
o Editor wishlist
- More infrastructure for external editor (allow communication from the outside)
- Preferences GUI for the features below
@@ -93,12 +94,14 @@ o Editor wishlist
- Auto-indent on enter and on tab
o Error reporting/debugging
- Provide better error messages when polygon ordering causes CGAL errors:
+ o Detect common error: self-intersecting polyhedron()
o Supply syntax highlighting of the exact polygon indices which are
reported to be wrong
o Provide some interaction for debug walk-through?
- Provide visual highlighting of geometry corresponding to code
-> could aid debugging a lot
- Optionally output console log to a file
+ - Common error: detect and report (and ignore?) duplicate line segments in DXF
o Computation
- Multi-threaded computation (mostly important for CGAL)
- Progress: Call progresswidget more often to improve feedback
@@ -117,7 +120,6 @@ OpenCSG-related
---------------
o OpenCSG rendering: Coincident surfaces causes z-buffer fighting. Is this somehow
avoidable tuning the depth tests in OpenCSG?
-o Make the 10.000 element OpenCSG limit configurable (Preferences) ?
o When specifying a transparency with the color() statement,
the object is not sorted and will be rendered wrongly
o Bug: Using the background operator (%) on the only object in a scene triggers a
@@ -256,8 +258,13 @@ o Caching
- Test that caching is actually performed (speedup + same results)
- Test the modifier characters correctly influence the cache (also when
added/removed)
+ - Test the individual caches
+ - PolySetCache
+ - CGALCache
+ - nodecache
+ - ModuleCache
o other tests
- export
- cmd-line tests
- leaf nodes having children, e.g. cube() cylinder();
- - dependency tracking
+ - dependency tracking (use and include)
diff --git a/doc/testing.txt b/doc/testing.txt
index 542de5d..6990c2f 100644
--- a/doc/testing.txt
+++ b/doc/testing.txt
@@ -51,6 +51,20 @@ Adding a new regression test:
7) run the test normally and verify that it passes:
$ ctest -R mytest
+Adding a new example:
+---------------------
+
+This is almost the same as adding a new regression test:
+1) Create the example under examples/
+2) run the test with the environment variable TEST_GENERATE=1, e.g.:
+ $ TEST_GENERATE=1 ctest -C Examples -R exampleNNN
+ (this will generate a exampleNNN-expected.txt file which is used for regression testing)
+3) manually verify that the output is correct (tests/regression/<testapp>/exampleNNN.<suffix>)
+4) run the test normally and verify that it passes:
+ $ ctest -C Examples -R exampleNNN
+
+
+
Troubleshooting:
------------------------------
diff --git a/examples/example023.scad b/examples/example023.scad
new file mode 100644
index 0000000..1332899
--- /dev/null
+++ b/examples/example023.scad
@@ -0,0 +1,20 @@
+// Example combining MCAD/fonts.scad with search() function.
+
+use <MCAD/fonts.scad>
+
+thisFont=8bit_polyfont();
+x_shift=thisFont[0][0];
+y_shift=thisFont[0][1];
+
+hours=["one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];
+
+module clock_hour_words(word_offset=20.0,word_height=2.0) {
+ for(i=[0:(len(hours)-1)]) assign( hourHandAngle=(i+1)*360/len(hours), theseIndicies=search(hours[i],thisFont[2],1,1) ) {
+ rotate(90-hourHandAngle) translate([word_offset,0])
+ for( j=[0:(len(theseIndicies)-1)] ) translate([j*x_shift,-y_shift/2]) {
+ linear_extrude(height=word_height) polygon(points=thisFont[2][theseIndicies[j]][6][0],paths=thisFont[2][theseIndicies[j]][6][1]);
+ }
+ }
+}
+
+clock_hour_words(word_offset=16.0,word_height=5.0);
diff --git a/libraries/MCAD b/libraries/MCAD
-Subproject cde0d5514a7441812fb8a88f2bd6f2af48ded69
+Subproject fa265644af9b720557414125fd5ba26981b9757
diff --git a/openscad.pro b/openscad.pro
index 100785b..453ab77 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -188,6 +188,7 @@ HEADERS += src/parsersettings.h \
src/traverser.h \
src/nodecache.h \
src/nodedumper.h \
+ src/ModuleCache.h \
src/PolySetCache.h \
src/PolySetEvaluator.h \
src/CSGTermEvaluator.h \
@@ -230,6 +231,7 @@ SOURCES += src/mathc99.cc \
src/nodedumper.cc \
src/traverser.cc \
src/PolySetEvaluator.cc \
+ src/ModuleCache.cc \
src/PolySetCache.cc \
src/Tree.cc \
\
diff --git a/openscad.pro.user b/openscad.pro.user
index 4bce49a..087d6e6 100644
--- a/openscad.pro.user
+++ b/openscad.pro.user
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
-<!-- Written by Qt Creator 2.4.1, 2012-02-06T09:01:56. -->
+<!-- Written by Qt Creator 2.4.1, 2012-02-15T08:41:23. -->
<qtcreator>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
@@ -103,7 +103,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
- <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">/Users/clothbot/Projects/github/clothbot/openscad-build-desktop</value>
+ <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">$$PWD/../openscad-build-desktop</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId">-1</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
@@ -124,7 +124,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
- <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">-j4</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
@@ -150,14 +150,17 @@
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges">
<value type="QString">OPENSCAD_LIBRARIES=$$PWD/../libraries/install</value>
+ <value type="QString">CCACHE_BASEDIR=$$PWD/..</value>
+ <value type="QString">PATH=/opt/local/libexec/ccache:/usr/bin:$QTDIR/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/local/bin</value>
+ <value type="QString">EIGEN2DIR=$$PWD/../libraries/install/include/eigen2</value>
</valuelist>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 4.7.4 for GCC (Qt SDK) Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
- <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">/Users/clothbot/Projects/github/clothbot/openscad-build-desktop</value>
+ <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">$$PWD</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId">3</value>
- <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+ <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="ProjectExplorer.BuildCOnfiguration.ToolChain">INVALID</value>
@@ -205,7 +208,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
- <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">/Users/clothbot/Projects/github/clothbot/openscad-build-desktop</value>
+ <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">$$PWD/../openscad-build-desktop</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId">-1</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
@@ -255,7 +258,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
- <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">/Users/clothbot/Projects/github/clothbot/openscad-build-desktop</value>
+ <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">$$PWD/../openscad-build-desktop</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId">-1</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
@@ -307,7 +310,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 4.7.4 for GCC (Qt SDK)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
- <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">/Users/clothbot/Projects/github/clothbot/openscad-release-desktop</value>
+ <value type="QString" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory">$$PWD/../openscad-release-desktop</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId">3</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 5dabede..405f7aa 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -1,15 +1,16 @@
#!/bin/sh -e
#
# This script builds all library dependencies of OpenSCAD for Mac OS X.
-# The libraries will be build in 32- and 64-bit mode and backwards compatible with
-# 10.5 "Leopard".
+# The libraries will be build in 64-bit (and optionally 32-bit mode) mode
+# and backwards compatible with 10.5 "Leopard".
#
-# Usage:
-# - Edit the BASEDIR variable. This is where libraries will be built and installed
-# - Edit the OPENSCADDIR variable. This is where patches are fetched from
+# This script must be run from the OpenSCAD source root directory
+#
+# Usage: macosx-build-dependencies.sh [-6]
+# -6 Build only 64-bit binaries
#
# Prerequisites:
-# - MacPorts: curl eigen
+# - MacPorts: curl
# - Qt4
#
# FIXME:
@@ -21,38 +22,63 @@ BASEDIR=$PWD/../libraries
OPENSCADDIR=$PWD
SRCDIR=$BASEDIR/src
DEPLOYDIR=$BASEDIR/install
+MAC_OSX_VERSION_MIN=10.5
+OPTION_32BIT=true
+
+printUsage()
+{
+ echo "Usage: $0 [-6]"
+ echo
+ echo " -6 Build only 64-bit binaries"
+}
# Hack warning: gmplib is built separately in 32-bit and 64-bit mode
-# and then merged afterwards. gmplib's header files are dependant on
+# and then merged afterwards. gmplib's header files are dependent on
# the CPU architecture on which configure was run and will be patched accordingly.
build_gmp()
{
version=$1
echo "Building gmp" $version "..."
cd $BASEDIR/src
- rm -rf gmp*
- curl -O ftp://ftp.gmplib.org/pub/gmp-$version/gmp-$version.tar.bz2
+ rm -rf gmp-$version
+ if [ ! -f gmp-$version.tar.bz2 ]; then
+ curl -O ftp://ftp.gmplib.org/pub/gmp-$version/gmp-$version.tar.bz2
+ fi
tar xjf gmp-$version.tar.bz2
cd gmp-$version
- # 32-bit version
- mkdir build-i386
- cd build-i386
- ../configure --prefix=$DEPLOYDIR/i386 "CFLAGS=-mmacosx-version-min=10.5 -arch i386" LDFLAGS="-mmacosx-version-min=10.5 -arch i386" ABI=32 --enable-cxx
- make install
- cd ..
+ if $OPTION_32BIT; then
+ mkdir build-i386
+ cd build-i386
+ ../configure --prefix=$DEPLOYDIR/i386 "CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch i386" LDFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch i386" ABI=32 --enable-cxx
+ make install
+ cd ..
+ fi
+
# 64-bit version
mkdir build-x86_64
cd build-x86_64
- ../configure --prefix=$DEPLOYDIR/x86_64 "CFLAGS=-mmacosx-version-min=10.5 -arch x86_64" LDFLAGS="-mmacosx-version-min=10.5 -arch x86_64" ABI=64 --enable-cxx
+ ../configure --prefix=$DEPLOYDIR/x86_64 "CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64" LDFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64" ABI=64 --enable-cxx
make install
# merge
cd $DEPLOYDIR
mkdir -p lib
- lipo -create i386/lib/libgmp.dylib x86_64/lib/libgmp.dylib -output lib/libgmp.dylib
+ if $OPTION_32BIT; then
+ lipo -create i386/lib/libgmp.dylib x86_64/lib/libgmp.dylib -output lib/libgmp.dylib
+ lipo -create i386/lib/libgmpxx.dylib x86_64/lib/libgmpxx.dylib -output lib/libgmpxx.dylib
+ else
+ cp x86_64/lib/libgmp.dylib lib/libgmp.dylib
+ cp x86_64/lib/libgmpxx.dylib lib/libgmpxx.dylib
+ fi
install_name_tool -id $DEPLOYDIR/lib/libgmp.dylib lib/libgmp.dylib
- cp lib/libgmp.dylib i386/lib/
- cp lib/libgmp.dylib x86_64/lib/
+ install_name_tool -id $DEPLOYDIR/lib/libgmpxx.dylib lib/libgmpxx.dylib
+ install_name_tool -change $DEPLOYDIR/x86_64/lib/libgmp.10.dylib $DEPLOYDIR/lib/libgmp.dylib lib/libgmpxx.dylib
+ if $OPTION_32BIT; then
+ cp lib/libgmp.dylib i386/lib/
+ cp lib/libgmp.dylib x86_64/lib/
+ cp lib/libgmpxx.dylib i386/lib/
+ cp lib/libgmpxx.dylib x86_64/lib/
+ fi
mkdir -p include
cp x86_64/include/gmp.h include/
cp x86_64/include/gmpxx.h include/
@@ -101,27 +127,35 @@ build_mpfr()
version=$1
echo "Building mpfr" $version "..."
cd $BASEDIR/src
- rm -rf mpfr*
- curl -O http://www.mpfr.org/mpfr-current/mpfr-$version.tar.bz2
+ rm -rf mpfr-$version
+ if [ ! -f mpfr-$version.tar.bz2 ]; then
+ curl -O http://www.mpfr.org/mpfr-current/mpfr-$version.tar.bz2
+ fi
tar xjf mpfr-$version.tar.bz2
cd mpfr-$version
-
- # 32-bit version
- mkdir build-i386
- cd build-i386
- ../configure --prefix=$DEPLOYDIR/i386 --with-gmp=$DEPLOYDIR/i386 CFLAGS="-mmacosx-version-min=10.5 -arch i386" LDFLAGS="-mmacosx-version-min=10.5 -arch i386"
- make install
- cd ..
+ curl -O http://www.mpfr.org/mpfr-current/allpatches
+ patch -N -Z -p1 < allpatches
+ if $OPTION_32BIT; then
+ mkdir build-i386
+ cd build-i386
+ ../configure --prefix=$DEPLOYDIR/i386 --with-gmp=$DEPLOYDIR/i386 CFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch i386" LDFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch i386"
+ make install
+ cd ..
+ fi
# 64-bit version
mkdir build-x86_64
cd build-x86_64
- ../configure --prefix=$DEPLOYDIR/x86_64 --with-gmp=$DEPLOYDIR/x86_64 CFLAGS="-mmacosx-version-min=10.5 -arch x86_64" LDFLAGS="-mmacosx-version-min=10.5 -arch x86_64"
+ ../configure --prefix=$DEPLOYDIR/x86_64 --with-gmp=$DEPLOYDIR/x86_64 CFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64" LDFLAGS="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64"
make install
# merge
cd $DEPLOYDIR
- lipo -create i386/lib/libmpfr.dylib x86_64/lib/libmpfr.dylib -output lib/libmpfr.dylib
+ if $OPTION_32BIT; then
+ lipo -create i386/lib/libmpfr.dylib x86_64/lib/libmpfr.dylib -output lib/libmpfr.dylib
+ else
+ cp x86_64/lib/libmpfr.dylib lib/libmpfr.dylib
+ fi
install_name_tool -id $DEPLOYDIR/lib/libmpfr.dylib lib/libmpfr.dylib
mkdir -p include
cp x86_64/include/mpfr.h include/
@@ -135,13 +169,18 @@ build_boost()
bversion=`echo $version | tr "." "_"`
echo "Building boost" $version "..."
cd $BASEDIR/src
- rm -rf boost*
- curl -LO http://downloads.sourceforge.net/project/boost/boost/$version/boost_$bversion.tar.bz2
+ rm -rf boost_$bversion
+ if [ ! -f boost_$bversion.tar.bz2 ]; then
+ curl -LO http://downloads.sourceforge.net/project/boost/boost/$version/boost_$bversion.tar.bz2
+ fi
tar xjf boost_$bversion.tar.bz2
cd boost_$bversion
# We only need the thread and program_options libraries
./bootstrap.sh --prefix=$DEPLOYDIR --with-libraries=thread,program_options,filesystem,system,regex
- ./bjam cflags="-mmacosx-version-min=10.5 -arch i386 -arch x86_64" linkflags="-mmacosx-version-min=10.5 -arch i386 -arch x86_64"
+ if $OPTION_32BIT; then
+ BOOST_EXTRA_FLAGS="-arch i386"
+ fi
+ ./bjam cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS"
./bjam install
install_name_tool -id $DEPLOYDIR/lib/libboost_thread.dylib $DEPLOYDIR/lib/libboost_thread.dylib
install_name_tool -id $DEPLOYDIR/lib/libboost_program_options.dylib $DEPLOYDIR/lib/libboost_program_options.dylib
@@ -158,16 +197,26 @@ build_cgal()
version=$1
echo "Building CGAL" $version "..."
cd $BASEDIR/src
- rm -rf CGAL*
- curl -O https://gforge.inria.fr/frs/download.php/29125/CGAL-$version.tar.gz
-# curl -O https://gforge.inria.fr/frs/download.php/28500/CGAL-$version.tar.gz
-# curl -O https://gforge.inria.fr/frs/download.php/27641/CGAL-$version.tar.gz
+ rm -rf CGAL-$version
+ if [ ! -f CGAL-$version.tar.gz ]; then
+ #4.0
+ curl -O https://gforge.inria.fr/frs/download.php/30387/CGAL-$version.tar.gz
+ # 3.9 curl -O https://gforge.inria.fr/frs/download.php/29125/CGAL-$version.tar.gz
+ # 3.8 curl -O https://gforge.inria.fr/frs/download.php/28500/CGAL-$version.tar.gz
+ # 3.7 curl -O https://gforge.inria.fr/frs/download.php/27641/CGAL-$version.tar.gz
+ fi
tar xzf CGAL-$version.tar.gz
cd CGAL-$version
+ if $OPTION_32BIT; then
+ CGAL_EXTRA_FLAGS=";i386"
+ fi
# We build a static lib. Not really necessary, but it's well tested.
- cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DGMP_INCLUDE_DIR=$DEPLOYDIR/include -DGMP_LIBRARIES=$DEPLOYDIR/lib/libgmp.dylib -DGMPXX_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_LIBRARIES=$DEPLOYDIR/lib/libmpfr.dylib -DWITH_CGAL_Qt3=OFF -DWITH_CGAL_Qt4=OFF -DWITH_CGAL_ImageIO=OFF -DBUILD_SHARED_LIBS=FALSE -DCMAKE_OSX_DEPLOYMENT_TARGET="10.5" -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" -DBOOST_ROOT=$DEPLOYDIR
+ cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DGMP_INCLUDE_DIR=$DEPLOYDIR/include -DGMP_LIBRARIES=$DEPLOYDIR/lib/libgmp.dylib -DGMPXX_LIBRARIES=$DEPLOYDIR/lib/libgmpxx.dylib -DGMPXX_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_LIBRARIES=$DEPLOYDIR/lib/libmpfr.dylib -DWITH_CGAL_Qt3=OFF -DWITH_CGAL_Qt4=OFF -DWITH_CGAL_ImageIO=OFF -DBUILD_SHARED_LIBS=TRUE -DCMAKE_OSX_DEPLOYMENT_TARGET="$MAC_OSX_VERSION_MIN" -DCMAKE_OSX_ARCHITECTURES="x86_64$CGAL_EXTRA_FLAGS" -DBOOST_ROOT=$DEPLOYDIR -DCMAKE_BUILD_TYPE=Debug
make -j4
make install
+ install_name_tool -id $DEPLOYDIR/lib/libCGAL.dylib $DEPLOYDIR/lib/libCGAL.dylib
+ install_name_tool -id $DEPLOYDIR/lib/libCGAL_Core.dylib $DEPLOYDIR/lib/libCGAL_Core.dylib
+ install_name_tool -change $PWD/lib/libCGAL.9.dylib $DEPLOYDIR/lib/libCGAL.dylib $DEPLOYDIR/lib/libCGAL_Core.dylib
}
build_glew()
@@ -175,14 +224,19 @@ build_glew()
version=$1
echo "Building GLEW" $version "..."
cd $BASEDIR/src
- rm -rf glew-*
- curl -LO http://downloads.sourceforge.net/project/glew/glew/$version/glew-$version.tgz
+ rm -rf glew-$version
+ if [ ! -f glew-$version.tgz ]; then
+ curl -LO http://downloads.sourceforge.net/project/glew/glew/$version/glew-$version.tgz
+ fi
tar xzf glew-$version.tgz
cd glew-$version
mkdir -p $DEPLOYDIR/lib/pkgconfig
# To avoid running strip on a fat archive as this is not supported by strip
sed -i bak -e "s/\$(STRIP) -x lib\/\$(LIB.STATIC)//" Makefile
- make GLEW_DEST=$DEPLOYDIR CFLAGS.EXTRA="-no-cpp-precomp -dynamic -fno-common -mmacosx-version-min=10.5 -arch i386 -arch x86_64" LDFLAGS.EXTRA="-mmacosx-version-min=10.5 -arch i386 -arch x86_64" install
+ if $OPTION_32BIT; then
+ GLEW_EXTRA_FLAGS="-arch i386"
+ fi
+ make GLEW_DEST=$DEPLOYDIR CFLAGS.EXTRA="-no-cpp-precomp -dynamic -fno-common -mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" LDFLAGS.EXTRA="-mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" install
}
build_opencsg()
@@ -191,11 +245,40 @@ build_opencsg()
echo "Building OpenCSG" $version "..."
cd $BASEDIR/src
rm -rf OpenCSG-$version
- curl -O http://www.opencsg.org/OpenCSG-$version.tar.gz
+ if [ ! -f OpenCSG-$version.tar.gz ]; then
+ curl -O http://www.opencsg.org/OpenCSG-$version.tar.gz
+ fi
tar xzf OpenCSG-$version.tar.gz
cd OpenCSG-$version
patch -p1 < $OPENSCADDIR/patches/OpenCSG-$version-MacOSX-port.patch
- OPENSCAD_LIBRARIES=$DEPLOYDIR qmake -r CONFIG+="x86 x86_64"
+ if $OPTION_32BIT; then
+ OPENCSG_EXTRA_FLAGS="x86"
+ fi
+ OPENSCAD_LIBRARIES=$DEPLOYDIR qmake -r CONFIG+="x86_64 $OPENCSG_EXTRA_FLAGS"
+ make install
+}
+
+build_eigen()
+{
+ version=$1
+ echo "Building eigen" $version "..."
+ cd $BASEDIR/src
+ rm -rf eigen-$version
+ ## Directory name for v2.0.17
+ rm -rf eigen-eigen-b23437e61a07
+ if [ ! -f eigen-$version.tar.bz2 ]; then
+ curl -LO http://bitbucket.org/eigen/eigen/get/$version.tar.bz2
+ mv $version.tar.bz2 eigen-$version.tar.bz2
+ fi
+ tar xjf eigen-$version.tar.bz2
+ ## File name for v2.0.17
+ ln -s eigen-eigen-b23437e61a07 eigen-$version
+ cd eigen-$version
+ if $OPTION_32BIT; then
+ EIGEN_EXTRA_FLAGS=";i386"
+ fi
+ cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DEIGEN_BUILD_LIB=ON -DBUILD_SHARED_LIBS=FALSE -DCMAKE_OSX_DEPLOYMENT_TARGET="$MAC_OSX_VERSION_MIN" -DCMAKE_OSX_ARCHITECTURES="x86_64$EIGEN_EXTRA_FLAGS"
+ make -j4
make install
}
@@ -204,12 +287,20 @@ if [ ! -f $OPENSCADDIR/openscad.pro ]; then
exit 0
fi
+while getopts '6' c
+do
+ case $c in
+ 6) OPTION_32BIT=false
+ esac
+done
+
echo "Using basedir:" $BASEDIR
mkdir -p $SRCDIR $DEPLOYDIR
-build_gmp 5.0.3
+build_eigen 2.0.17
+build_gmp 5.0.5
build_mpfr 3.1.0
build_boost 1.47.0
# NB! For CGAL, also update the actual download URL in the function
-build_cgal 3.9
+build_cgal 4.0
build_glew 1.7.0
build_opencsg 1.3.2
diff --git a/scripts/release-macosx.sh b/scripts/release-macosx.sh
deleted file mode 100755
index 18174d6..0000000
--- a/scripts/release-macosx.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-##
-## Deprecated! Use release-common.sh instead
-##
-
-#!/bin/sh
-#
-# This script creates a binary release of OpenSCAD for Mac OS X.
-# The script will create a file called openscad-<versionstring>.zip
-# in the current directory.
-#
-# Usage: makedmg.sh [-v <versionstring>]
-# -v Version string (e.g. -v 2010.01)
-#
-# If no version string is given, todays date will be used (YYYY-MM-DD)
-#
-printUsage()
-{
- echo "Usage: $0 -v <versionstring>"
- echo
- echo " Example: $0 -v 2010.01"
-}
-
-while getopts 'v:' c
-do
- case $c in
- v) VERSION=$OPTARG;;
- esac
-done
-
-if test -z "$VERSION"; then
- VERSION=`date "+%Y.%m.%d"`
-fi
-
-echo "Building openscad-$VERSION..."
-export OPENCSGDIR=$PWD/../OpenCSG-1.2.0
-qmake VERSION=$VERSION CONFIG+=mdi openscad.pro
-make clean
-make -j2
-echo "Preparing executable.."
-mkdir OpenSCAD.app/Contents/Frameworks
-cp $OPENCSGDIR/lib/libopencsg.dylib OpenSCAD.app/Contents/Frameworks
-cp /opt/local/lib/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks
-cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL OpenSCAD.app/Contents/Frameworks
-cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui OpenSCAD.app/Contents/Frameworks
-cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore OpenSCAD.app/Contents/Frameworks
-install_name_tool -change libopencsg.1.dylib @executable_path/../Frameworks/libopencsg.dylib OpenSCAD.app/Contents/MacOS/openscad
-install_name_tool -change QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL OpenSCAD.app/Contents/MacOS/openscad
-install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/MacOS/openscad
-install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/MacOS/openscad
-install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/QtOpenGL
-install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtOpenGL
-install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/QtGui
-install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/MacOS/openscad
-install_name_tool -id libopencsg.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib
-install_name_tool -change /opt/local/lib/libGLEW.1.5.1.dylib @executable_path/../Frameworks/libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libopencsg.dylib
-install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui OpenSCAD.app/Contents/Frameworks/libopencsg.dylib
-install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore OpenSCAD.app/Contents/Frameworks/libopencsg.dylib
-install_name_tool -id libGLEW.1.5.1.dylib OpenSCAD.app/Contents/Frameworks/libGLEW.1.5.1.dylib
-
-echo "Creating directory structure.."
-rm -rf openscad-$VERSION
-mkdir -p openscad-$VERSION/examples
-cp examples/* openscad-$VERSION/examples/
-chmod -R 644 openscad-$VERSION/examples/*
-mv OpenSCAD.app openscad-$VERSION
-
-echo "Creating archive.."
-zip -qr openscad-$VERSION.zip openscad-$VERSION
-echo "Mac OS X binary created: openscad-$VERSION.zip"
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index 15fa746..46f4cfa 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -43,11 +43,8 @@ CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractNode &node)
Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX);
evaluate.execute();
return this->root;
- assert(this->visitedchildren.empty());
- }
- else {
- return CGALCache::instance()->get(this->tree.getIdString(node));
}
+ return CGALCache::instance()->get(this->tree.getIdString(node));
}
bool CGALEvaluator::isCached(const AbstractNode &node) const
@@ -252,31 +249,45 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node)
// objects. So we convert in to our internal 2d data format, transform it,
// tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack!
- CGAL_Aff_transformation2 t(
- node.matrix(0,0), node.matrix(0,1), node.matrix(0,3),
- node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3));
-
- DxfData *dd = N.convertToDxfData();
- for (size_t i=0; i < dd->points.size(); i++) {
- CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]);
- p = t.transform(p);
- dd->points[i][0] = to_double(p.x());
- dd->points[i][1] = to_double(p.y());
+ Eigen::Matrix2f testmat;
+ testmat << node.matrix(0,0), node.matrix(0,1), node.matrix(1,0), node.matrix(1,1);
+ if (testmat.determinant() == 0) {
+ PRINT("Warning: Scaling a 2D object with 0 - removing object");
+ N.p2.reset();
+ }
+ else {
+ CGAL_Aff_transformation2 t(
+ node.matrix(0,0), node.matrix(0,1), node.matrix(0,3),
+ node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3));
+
+ DxfData *dd = N.convertToDxfData();
+ for (size_t i=0; i < dd->points.size(); i++) {
+ CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]);
+ p = t.transform(p);
+ dd->points[i][0] = to_double(p.x());
+ dd->points[i][1] = to_double(p.y());
+ }
+
+ PolySet ps;
+ ps.is2d = true;
+ dxf_tesselate(&ps, *dd, 0, true, false, 0);
+
+ N = evaluateCGALMesh(ps);
+ delete dd;
}
-
- PolySet ps;
- ps.is2d = true;
- dxf_tesselate(&ps, *dd, 0, true, false, 0);
-
- N = evaluateCGALMesh(ps);
- delete dd;
}
else if (N.dim == 3) {
- CGAL_Aff_transformation t(
- node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3),
- node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3),
- node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3));
- N.p3->transform(t);
+ if (node.matrix.matrix().determinant() == 0) {
+ PRINT("Warning: Scaling a 3D object with 0 - removing object");
+ N.p3.reset();
+ }
+ else {
+ CGAL_Aff_transformation t(
+ node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3),
+ node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3),
+ node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3));
+ N.p3->transform(t);
+ }
}
}
else {
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 54c02e7..046ed12 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -62,6 +62,8 @@ CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &o
int CGAL_Nef_polyhedron::weight() const
{
+ if (this->empty()) return 0;
+
size_t memsize = sizeof(CGAL_Nef_polyhedron);
if (this->dim == 2) {
memsize += sizeof(CGAL_Nef_polyhedron2) +
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 5546290..a4835c2 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -27,10 +27,10 @@ public:
double tval, fps, fsteps;
QTimer *autoReloadTimer;
- QString autoReloadInfo;
+ std::string autoReloadId;
Context root_ctx;
- AbstractModule *root_module; // Result of parsing
+ Module *root_module; // Result of parsing
ModuleInstantiation root_inst; // Top level instance
AbstractNode *absolute_root_node; // Result of tree evaluation
AbstractNode *root_node; // Root if the root modifier (!) is used
@@ -73,12 +73,16 @@ private slots:
private:
void openFile(const QString &filename);
- void load();
+ void refreshDocument();
AbstractNode *find_root_tag(AbstractNode *n);
- void compile(bool procevents);
+ void updateTemporalVariables();
+ bool fileChangedOnDisk();
+ bool includesChanged();
+ bool compileTopLevelDocument(bool reload);
+ bool compile(bool reload, bool procevents);
void compileCSG(bool procevents);
bool maybeSave();
- bool checkModified();
+ bool checkEditorModified();
QString dumpCSGTree(AbstractNode *root);
static void consoleOutput(const std::string &msg, void *userdata);
void loadViewSettings();
@@ -157,7 +161,7 @@ public slots:
void helpAbout();
void helpHomepage();
void helpManual();
- void helpOpenGL();
+ void helpLibrary();
void quit();
void actionReloadCompile();
void checkAutoReload();
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index f89e0d4..13bb226 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -222,7 +222,7 @@
<addaction name="helpActionAbout"/>
<addaction name="helpActionHomepage"/>
<addaction name="helpActionManual"/>
- <addaction name="helpActionOpenGLInfo"/>
+ <addaction name="helpActionLibraryInfo"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
@@ -671,9 +671,9 @@
<string>Export as CSG...</string>
</property>
</action>
- <action name="helpActionOpenGLInfo">
+ <action name="helpActionLibraryInfo">
<property name="text">
- <string>OpenGL info</string>
+ <string>Library info</string>
</property>
</action>
</widget>
diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc
new file mode 100644
index 0000000..70b1fb9
--- /dev/null
+++ b/src/ModuleCache.cc
@@ -0,0 +1,106 @@
+#include "ModuleCache.h"
+#include "module.h"
+#include "printutils.h"
+#include "openscad.h"
+
+#include "boosty.h"
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+
+#include <stdio.h>
+#include <fstream>
+#include <sstream>
+#include <time.h>
+#include <sys/stat.h>
+
+/*!
+ FIXME: Implement an LRU scheme to avoid having an ever-growing module cache
+*/
+
+ModuleCache *ModuleCache::inst = NULL;
+
+static bool is_modified(const std::string &filename, const time_t &mtime)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ return (st.st_mtime > mtime);
+}
+
+Module *ModuleCache::evaluate(const std::string &filename)
+{
+ Module *lib_mod = NULL;
+
+ // Create cache ID
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+
+ std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ // Lookup in cache
+ if (this->entries.find(filename) != this->entries.end() &&
+ this->entries[filename].cache_id == cache_id) {
+#ifdef DEBUG
+// Causes too much debug output
+// PRINTB("Using cached library: %s (%s)", filename % cache_id);
+#endif
+ lib_mod = &(*this->entries[filename].module);
+
+ BOOST_FOREACH(const Module::IncludeContainer::value_type &item, lib_mod->includes) {
+ if (is_modified(item.first, item.second)) {
+ lib_mod = NULL;
+ break;
+ }
+ }
+ }
+
+ // If cache lookup failed (non-existing or old timestamp), compile module
+ if (!lib_mod) {
+#ifdef DEBUG
+ if (this->entries.find(filename) != this->entries.end()) {
+ PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
+ }
+ else {
+ PRINTB("Compiling library '%s'.", filename);
+ }
+#endif
+
+ std::ifstream ifs(filename.c_str());
+ if (!ifs.is_open()) {
+ PRINTB("WARNING: Can't open library file '%s'\n", filename);
+ return NULL;
+ }
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+
+ print_messages_push();
+
+ cache_entry e = { NULL, cache_id };
+ if (this->entries.find(filename) != this->entries.end()) {
+ delete this->entries[filename].module;
+ }
+ this->entries[filename] = e;
+
+ std::string pathname = boosty::stringy(fs::path(filename).parent_path());
+ lib_mod = dynamic_cast<Module*>(parse(text.c_str(), pathname.c_str(), 0));
+
+ if (lib_mod) {
+ this->entries[filename].module = lib_mod;
+ } else {
+ this->entries.erase(filename);
+ }
+
+ print_messages_pop();
+ }
+
+ if (lib_mod) lib_mod->handleDependencies();
+
+ return lib_mod;
+}
+
+void ModuleCache::clear()
+{
+ this->entries.clear();
+}
+
diff --git a/src/ModuleCache.h b/src/ModuleCache.h
new file mode 100644
index 0000000..1e6373d
--- /dev/null
+++ b/src/ModuleCache.h
@@ -0,0 +1,23 @@
+#include <string>
+#include <boost/unordered_map.hpp>
+
+class ModuleCache
+{
+public:
+ static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
+ class Module *evaluate(const std::string &filename);
+ size_t size() { return this->entries.size(); }
+ void clear();
+
+private:
+ ModuleCache() {}
+ ~ModuleCache() {}
+
+ static ModuleCache *inst;
+
+ struct cache_entry {
+ class Module *module;
+ std::string cache_id;
+ };
+ boost::unordered_map<std::string, cache_entry> entries;
+};
diff --git a/src/OpenCSGWarningDialog.cc b/src/OpenCSGWarningDialog.cc
index fdaaa50..5648576 100644
--- a/src/OpenCSGWarningDialog.cc
+++ b/src/OpenCSGWarningDialog.cc
@@ -1,7 +1,7 @@
#include "OpenCSGWarningDialog.h"
#include "Preferences.h"
-OpenCSGWarningDialog::OpenCSGWarningDialog(QWidget *parent)
+OpenCSGWarningDialog::OpenCSGWarningDialog(QWidget*)
{
setupUi(this);
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index 9e30bc5..3b272d2 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -16,19 +16,45 @@
#include "openscad.h" // get_fragments_from_r()
#include <boost/foreach.hpp>
-// This object 'visits' the Nef Polyhedron 3d, extracting 2d information
-// from it for the projection( cut = true ) command.
-// http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+/*
+
+This Visitor is used in the 'cut' process. Essentially, one or more of
+our 3d nef polyhedrons have been 'cut' by the xy-plane, forming a number
+of polygons that are essentially 'flat'. This Visitor object, when
+called repeatedly, collects those flat polygons together and forms a new
+2d nef polyhedron out of them. It keeps track of the 'holes' so that
+in the final resulting polygon, they are preserved properly.
+
+The output polygon is stored in the output_nefpoly2d variable.
+
+For more information on 3d + 2d nef polyhedrons, facets, halffacets,
+facet cycles, etc, please see these websites:
+
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron_3-Traits---Halffacet.html
+
+The halffacet iteration 'circulator' code is based on OGL_helper.h
+
+Why do we throw out all 'down' half-facets? Imagine a triangle in 3d
+space - it has one 'half face' for one 'side' and another 'half face' for the
+other 'side'. If we iterated over both half-faces we would get the same vertex
+coordinates twice. Instead, we only need one side, in our case, we
+chose the 'up' side.
+*/
class NefShellVisitor_for_cut {
public:
std::stringstream out;
CGAL_Nef_polyhedron2::Boundary boundary;
- shared_ptr<CGAL_Nef_polyhedron2> tmpnef;
- shared_ptr<CGAL_Nef_polyhedron2> nefpoly2d;
- NefShellVisitor_for_cut()
+ shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d;
+ shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d;
+ CGAL::Direction_3<CGAL_Kernel3> up;
+ bool debug;
+ NefShellVisitor_for_cut(bool debug=false)
{
- nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
+ output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
boundary = CGAL_Nef_polyhedron2::INCLUDED;
+ up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1);
+ this->debug = debug;
}
std::string dump()
{
@@ -40,59 +66,38 @@ public:
void visit( CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::SFace_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) {
- // This method is fed each 'half-facet' of the Nef_polyhedron3 that's been intersected
- // with the flat x-y plane. I.e. it's fed a bunch of flat 3d polygons with z==0 at all vertexes.
- // It is fed both the 'up' pointing half-facets and the 'down' pointing half-facets.
- // We only need one set of vertexes, so we skip all of the 'down' facets.
- //
- // The vertexes are recorded in lists called 'contours'. Those are 'joined' or 'intersected'
- // with the main result polyhedron, 'nefpoly2d', depending on whether it's a "hole" contour
- // or a "body" contour.
- CGAL::Direction_3<CGAL_Kernel3> up(0,0,1);
- CGAL::Plane_3<CGAL_Kernel3> plane = hfacet->plane();
- // out << " direction == up? " << ( plane.orthogonal_direction() == up ) << "\n";
- if ( plane.orthogonal_direction() != up ) {
- // out << "direction == down. skipping";
+ if ( hfacet->plane().orthogonal_direction() != this->up ) {
+ if (debug) out << "down facing half-facet. skipping\n";
return;
}
int numcontours = 0;
- CGAL_Nef_polyhedron2::Point point;
- CGAL_Nef_polyhedron3::Vertex_const_handle vertex;
- CGAL_Nef_polyhedron3::Halfedge_const_handle halfedge;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;
- CGAL_Nef_polyhedron3::SHalfedge_const_handle first_halfedge, j;
- for ( i = hfacet->facet_cycles_begin(); i != hfacet->facet_cycles_end(); ++i ) {
- j = CGAL_Nef_polyhedron3::SHalfedge_const_handle( i );
- first_halfedge = j;
+ CGAL_forall_facet_cycles_of( i, hfacet ) {
+ CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);
std::list<CGAL_Nef_polyhedron2::Point> contour;
- do {
- // j->source() is a CGAL_Nef_polyhedron3::Nef_polyhedron_S2::SVertex,
- // but SVertex is the same thing as CGAL_Nef_polyhedron3::Halfedge
- // and Halfedge can give us an actual point.
- halfedge = CGAL_Nef_polyhedron3::Halfedge_const_handle( j->source() );
- vertex = CGAL_Nef_polyhedron3::Vertex_const_handle( halfedge->source() );
- point = CGAL_Nef_polyhedron2::Point( vertex->point().x(), vertex->point().y() );
- contour.push_back( point );
- //out << " add xyz " << x << " "<< y << " " <<z << endl;
- j = j->next();
- } while ( j != first_halfedge );
- tmpnef.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
+ CGAL_For_all( c1, c2 ) {
+ CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->source()->point();
+ CGAL_Nef_polyhedron2::Point point2d( point3d.x(), point3d.y() );
+ contour.push_back( point2d );
+ }
+ tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
if ( numcontours == 0 ) {
- //out << " contour is a body. joining. " << contour.size() << " points.\n" ;
- *nefpoly2d += *tmpnef;
+ if (debug) out << " contour is a body. make union(). " << contour.size() << " points.\n" ;
+ *output_nefpoly2d += *tmpnef2d;
} else {
- //out << " contour is a hole. intersecting. " << contour.size() << " points.\n";
- *nefpoly2d *= *tmpnef;
+ if (debug) out << " contour is a hole. make intersection(). " << contour.size() << " points.\n";
+ *output_nefpoly2d *= *tmpnef2d;
}
numcontours++;
- } // next facet cycle
+ } // next facet cycle (i.e. next contour)
} // visit()
};
PolySetCGALEvaluator::PolySetCGALEvaluator(CGALEvaluator &cgalevaluator)
: PolySetEvaluator(cgalevaluator.getTree()), cgalevaluator(cgalevaluator)
{
+ this->debug = false;
}
PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
@@ -108,19 +113,24 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
}
}
if (sum.empty()) return NULL;
+ if (!sum.p3->is_simple()) {
+ if (!node.cut_mode) {
+ PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
+ return new PolySet();
+ }
+ }
- PolySet *ps = new PolySet();
- PolySet *ps3 = NULL;
- DxfData *dxf = NULL;
- CGAL_Nef_polyhedron np;
- ps->convexity = node.convexity;
- ps->is2d = true;
+ CGAL_Nef_polyhedron nef_poly;
if (node.cut_mode)
{
- CGAL_Nef_polyhedron3::Plane_3 plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
- *sum.p3 = sum.p3->intersection( plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
+ // intersect 'sum' with the x-y plane
+ CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
+ *sum.p3 = sum.p3->intersection( xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
+ // Visit each polygon in sum.p3 and union/intersect into a 2d polygon (with holes)
+ // For info on Volumes, Shells, Facets, and the 'visitor' pattern, please see
+ // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
NefShellVisitor_for_cut shell_visitor;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
@@ -131,20 +141,11 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
sum.p3->visit_shell_objects( sface_handle , shell_visitor );
}
}
- // std::cout << "shell visitor\n" << shell_visitor.dump() << "\n";
+ if (debug) std::cout << "shell visitor\n" << shell_visitor.dump() << "\nshell visitor end\n";
- /*if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }*/
-
- np.p2 = shell_visitor.nefpoly2d;
- // std::cout << np.dump_p2() << "\n";
- np.dim = 2;
-
- ps3 = np.convertToPolyset();
- // std::cout << "----------\n" << ps3->dump() << "\n";
- if (!ps3) return NULL;
+ nef_poly.p2 = shell_visitor.output_nefpoly2d;
+ nef_poly.dim = 2;
+ if (debug) std::cout << "--\n" << nef_poly.dump_p2() << "\n";
// Extract polygons in the XY plane, ignoring all other polygons
// FIXME: If the polyhedron is really thin, there might be unwanted polygons
@@ -152,7 +153,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
// and cause a crash in CGALEvaluator::PolyReducer. The right solution is to
// filter these polygons here. kintel 20120203.
/*
- Grid2d<int> conversion_grid(GRID_COARSE);
+ Grid2d<unsigned int> conversion_grid(GRID_COARSE);
for (size_t i = 0; i < ps3->polygons.size(); i++) {
for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
double x = ps3->polygons[i][j][0];
@@ -178,12 +179,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
// In projection mode all the triangles are projected manually into the XY plane
else
{
- if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
- ps3 = sum.convertToPolyset();
+ PolySet *ps3 = sum.convertToPolyset();
if (!ps3) return NULL;
for (size_t i = 0; i < ps3->polygons.size(); i++)
{
@@ -223,22 +219,22 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
plist.push_back(p);
}
// FIXME: Should the CGAL_Nef_polyhedron2 be cached?
- if (np.empty()) {
- np.dim = 2;
- np.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
+ if (nef_poly.empty()) {
+ nef_poly.dim = 2;
+ nef_poly.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
}
else {
- (*np.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ (*nef_poly.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
}
+ delete ps3;
}
- delete ps3;
- dxf = np.convertToDxfData();
- dxf_tesselate(ps, *dxf, 0, true, false, 0);
- dxf_border_to_ps(ps, *dxf);
- delete dxf;
-cant_project_non_simple_polyhedron:
+ PolySet *ps = nef_poly.convertToPolyset();
+ assert( ps != NULL );
+ ps->convexity = node.convexity;
+ if (debug) std::cout << "--\n" << ps->dump() << "\n";
+
return ps;
}
@@ -324,12 +320,14 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const LinearExtrudeNode &node)
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
- if (N.dim != 2) {
- PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
- }
- else {
- if (sum.empty()) sum = N.copy();
- else sum += N;
+ if (!N.empty()) {
+ if (N.dim != 2) {
+ PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
+ }
+ else {
+ if (sum.empty()) sum = N.copy();
+ else sum += N;
+ }
}
}
@@ -422,12 +420,14 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const RotateExtrudeNode &node)
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
- if (N.dim != 2) {
- PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!");
- }
- else {
- if (sum.empty()) sum = N.copy();
- else sum += N;
+ if (!N.empty()) {
+ if (N.dim != 2) {
+ PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!");
+ }
+ else {
+ if (sum.empty()) sum = N.copy();
+ else sum += N;
+ }
}
}
@@ -493,15 +493,10 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
}
for (int j = 0; j < fragments; j++) {
- double a = (j*2*M_PI) / fragments;
+ double a = (j*2*M_PI) / fragments - M_PI/2; // start on the X axis
for (size_t k = 0; k < dxf.paths[i].indices.size(); k++) {
- if (dxf.points[dxf.paths[i].indices[k]][0] == 0) {
- points[j][k][0] = 0;
- points[j][k][1] = 0;
- } else {
- points[j][k][0] = dxf.points[dxf.paths[i].indices[k]][0] * sin(a);
- points[j][k][1] = dxf.points[dxf.paths[i].indices[k]][0] * cos(a);
- }
+ points[j][k][0] = dxf.points[dxf.paths[i].indices[k]][0] * sin(a);
+ points[j][k][1] = dxf.points[dxf.paths[i].indices[k]][0] * cos(a);
points[j][k][2] = dxf.points[dxf.paths[i].indices[k]][1];
}
}
diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h
index dddcfc5..00e79f1 100644
--- a/src/PolySetCGALEvaluator.h
+++ b/src/PolySetCGALEvaluator.h
@@ -17,7 +17,7 @@ public:
virtual PolySet *evaluatePolySet(const RotateExtrudeNode &node);
virtual PolySet *evaluatePolySet(const CgaladvNode &node);
virtual PolySet *evaluatePolySet(const RenderNode &node);
-
+ bool debug;
protected:
PolySet *extrudeDxfData(const LinearExtrudeNode &node, class DxfData &dxf);
PolySet *rotateDxfData(const RotateExtrudeNode &node, class DxfData &dxf);
diff --git a/src/context.cc b/src/context.cc
index b9e685c..f96a45b 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -162,6 +162,7 @@ AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
}
if (this->usedlibs_p) {
BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
+ assert(m.second);
if (m.second->modules.find(inst.name()) != m.second->modules.end()) {
Context ctx(this->parent, m.second);
return m.second->modules[inst.name()]->evaluate(&ctx, &inst);
diff --git a/src/csgtermnormalizer.cc b/src/csgtermnormalizer.cc
index 0e7a759..6600758 100644
--- a/src/csgtermnormalizer.cc
+++ b/src/csgtermnormalizer.cc
@@ -44,9 +44,10 @@ shared_ptr<CSGTerm> CSGTermNormalizer::normalizePass(shared_ptr<CSGTerm> term)
do {
while (term && normalize_tail(term)) { }
if (!term || term->type == CSGTerm::TYPE_PRIMITIVE) return term;
- term->left = normalizePass(term->left);
+ if (term->left) term->left = normalizePass(term->left);
} while (term->type != CSGTerm::TYPE_UNION &&
- (term->right->type != CSGTerm::TYPE_PRIMITIVE || term->left->type == CSGTerm::TYPE_UNION));
+ ((term->right && term->right->type != CSGTerm::TYPE_PRIMITIVE) ||
+ (term->left && term->left->type == CSGTerm::TYPE_UNION)));
term->right = normalizePass(term->right);
// FIXME: Do we need to take into account any transformation of item here?
diff --git a/src/dxfdata.cc b/src/dxfdata.cc
index 20fcc71..00b246f 100644
--- a/src/dxfdata.cc
+++ b/src/dxfdata.cc
@@ -519,7 +519,7 @@ void DxfData::fixup_path_direction()
break;
this->paths[i].is_inner = true;
double min_x = this->points[this->paths[i].indices[0]][0];
- int min_x_point = 0;
+ size_t min_x_point = 0;
for (size_t j = 1; j < this->paths[i].indices.size(); j++) {
if (this->points[this->paths[i].indices[j]][0] < min_x) {
min_x = this->points[this->paths[i].indices[j]][0];
@@ -527,9 +527,9 @@ void DxfData::fixup_path_direction()
}
}
// rotate points if the path is in non-standard rotation
- int b = min_x_point;
- int a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1;
- int c = b == this->paths[i].indices.size() - 1 ? 1 : b + 1;
+ size_t b = min_x_point;
+ size_t a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1;
+ size_t c = b == this->paths[i].indices.size() - 1 ? 1 : b + 1;
double ax = this->points[this->paths[i].indices[a]][0] - this->points[this->paths[i].indices[b]][0];
double ay = this->points[this->paths[i].indices[a]][1] - this->points[this->paths[i].indices[b]][1];
double cx = this->points[this->paths[i].indices[c]][0] - this->points[this->paths[i].indices[b]][0];
diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc
index 15859bd..f221e3a 100644
--- a/src/dxftess-cgal.cc
+++ b/src/dxftess-cgal.cc
@@ -131,7 +131,7 @@ void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool /* do_tr
// ..maybe it would be better to assert here. But this would
// break compatibility with the glu tesselator that handled such
// cases just fine.
- PRINT( "WARNING: Duplicate vertex during Tessellation. Render may be incorrect." );
+ PRINT( "WARNING: Duplicate vertex found during Tessellation. Render may be incorrect." );
continue;
}
diff --git a/src/expr.cc b/src/expr.cc
index 66a0d11..671553c 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -86,8 +86,8 @@ Value Expression::evaluate(const Context *context) const
return *v1.vec[i];
}
if (v1.type == Value::STRING && v2.type == Value::NUMBER) {
- int i = int(v2.num);
- if (i >= 0 && i < v1.text.size())
+ unsigned int i = int(v2.num);
+ if (i < v1.text.size())
return Value(v1.text.substr(i, 1));
}
return Value();
diff --git a/src/func.cc b/src/func.cc
index 6686cb9..0c9b450 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -33,6 +33,7 @@
#include "mathc99.h"
#include <algorithm>
#include "stl-utils.h"
+#include "printutils.h"
AbstractFunction::~AbstractFunction()
{
@@ -345,6 +346,144 @@ Value builtin_lookup(const Context *, const std::vector<std::string>&, const std
return Value(high_v * f + low_v * (1-f));
}
+/*
+ Pattern:
+
+ "search" "(" ( match_value | list_of_match_values ) "," vector_of_vectors
+ ("," num_returns_per_match
+ ("," index_col_num )? )?
+ ")";
+ match_value : ( Value::NUMBER | Value::STRING );
+ list_of_values : "[" match_value ("," match_value)* "]";
+ vector_of_vectors : "[" ("[" Value ("," Value)* "]")+ "]";
+ num_returns_per_match : int;
+ index_col_num : int;
+
+ Examples:
+ Index values return as list:
+ search("a","abcdabcd");
+ - returns [0,4]
+ search("a","abcdabcd",1);
+ - returns [0]
+ search("e","abcdabcd",1);
+ - returns []
+ search("a",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]);
+ - returns [0,4]
+
+ Search on different column; return Index values:
+ search(3,[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",3] ], 0, 1);
+ - returns [0,8]
+
+ Search on list of values:
+ Return all matches per search vector element:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 0);
+ - returns [[0,4],[1,5],[2,6]]
+
+ Return first match per search vector element; special case return vector:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 1);
+ - returns [0,1,2]
+
+ Return first two matches per search vector element; vector of vectors:
+ search("abce",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 2);
+ - returns [[0,4],[1,5],[2,6],[8]]
+
+*/
+Value builtin_search(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+{
+ if (args.size() < 2) return Value();
+
+ const Value &findThis = args[0];
+ const Value &searchTable = args[1];
+ unsigned int num_returns_per_match = (args.size() > 2) ? args[2].num : 1;
+ unsigned int index_col_num = (args.size() > 3) ? args[3].num : 0;
+
+ Value returnVector;
+ returnVector.type = Value::VECTOR;
+
+ if (findThis.type == Value::NUMBER) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ if (searchTable.vec[j]->vec[index_col_num]->type == Value::NUMBER &&
+ findThis.num == searchTable.vec[j]->vec[index_col_num]->num) {
+ returnVector.append(new Value(double(j)));
+ matchCount++;
+ if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ } else if (findThis.type == Value::STRING) {
+ unsigned int searchTableSize;
+ if (searchTable.type == Value::STRING) searchTableSize = searchTable.text.size();
+ else searchTableSize = searchTable.vec.size();
+ for (size_t i = 0; i < findThis.text.size(); i++) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTableSize; j++) {
+ if ((searchTable.type == Value::VECTOR &&
+ findThis.text[i] == searchTable.vec[j]->vec[index_col_num]->text[0]) ||
+ (searchTable.type == Value::STRING &&
+ findThis.text[i] == searchTable.text[j])) {
+ Value *resultValue = new Value(double(j));
+ matchCount++;
+ if (num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ if (matchCount == 0) PRINTB(" search term not found: \"%s\"", findThis.text[i]);
+ if (num_returns_per_match == 0 || num_returns_per_match > 1) {
+ returnVector.append(resultVector);
+ }
+ }
+ } else if (findThis.type == Value::VECTOR) {
+ for (size_t i = 0; i < findThis.vec.size(); i++) {
+ unsigned int matchCount = 0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ if ((findThis.vec[i]->type == Value::NUMBER &&
+ searchTable.vec[j]->vec[index_col_num]->type == Value::NUMBER &&
+ findThis.vec[i]->num == searchTable.vec[j]->vec[index_col_num]->num) ||
+ (findThis.vec[i]->type == Value::STRING &&
+ searchTable.vec[j]->vec[index_col_num]->type == Value::STRING &&
+ findThis.vec[i]->text == searchTable.vec[j]->vec[index_col_num]->text)) {
+ Value *resultValue = new Value(double(j));
+ matchCount++;
+ if (num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
+ }
+ }
+ if (num_returns_per_match == 1 && matchCount == 0) {
+ if (findThis.vec[i]->type == Value::NUMBER) {
+ PRINTB(" search term not found: %s",findThis.vec[i]->num);
+ }
+ else if (findThis.vec[i]->type == Value::STRING) {
+ PRINTB(" search term not found: \"%s\"",findThis.vec[i]->text);
+ }
+ returnVector.append(resultVector);
+ }
+ if (num_returns_per_match == 0 || num_returns_per_match > 1) {
+ returnVector.append(resultVector);
+ }
+ }
+ } else {
+ PRINTB(" search: none performed on input %s", findThis);
+ return Value();
+ }
+ return returnVector;
+}
+
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
@@ -397,6 +536,7 @@ void register_builtin_functions()
Builtins::init("ln", new BuiltinFunction(&builtin_ln));
Builtins::init("str", new BuiltinFunction(&builtin_str));
Builtins::init("lookup", new BuiltinFunction(&builtin_lookup));
+ Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version));
Builtins::init("version_num", new BuiltinFunction(&builtin_version_num));
}
diff --git a/src/glview.cc b/src/glview.cc
index 0f9ec5b..aa2e746 100644
--- a/src/glview.cc
+++ b/src/glview.cc
@@ -393,8 +393,6 @@ void GLView::paintGL()
glRotated(object_rot_y, 0.0, 1.0, 0.0);
glRotated(object_rot_z, 0.0, 0.0, 1.0);
- glTranslated(object_trans_x, object_trans_y, object_trans_z);
-
// FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them
// to change color based on view orientation.
if (showcrosshairs)
@@ -412,6 +410,8 @@ void GLView::paintGL()
glEnd();
}
+ glTranslated(object_trans_x, object_trans_y, object_trans_z);
+
// Large gray axis cross inline with the model
// FIXME: This is always gray - adjust color to keep contrast with background
if (showaxes)
@@ -589,14 +589,24 @@ void GLView::mouseMoveEvent(QMouseEvent *event)
normalizeAngle(object_rot_y);
normalizeAngle(object_rot_z);
} else {
- // Right button pans
- // Shift-right zooms
+ // Right button pans in the xz plane
+ // Middle button pans in the xy plane
+ // Shift-right and Shift-middle zooms
if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) {
viewer_distance += (GLdouble)dy;
} else {
double mx = +(dx) * viewer_distance/1000;
- double my = -(dy) * viewer_distance/1000;
+ double mz = -(dy) * viewer_distance/1000;
+
+ double my = 0;
+ if (event->buttons() & Qt::MiddleButton) {
+ my = mz;
+ mz = 0;
+ // actually lock the x-position
+ // (turns out to be easier to use than xy panning)
+ mx = 0;
+ }
Matrix3d aax, aay, aaz, tm3;
aax = Eigen::AngleAxisd(-(object_rot_x/180) * M_PI, Vector3d::UnitX());
@@ -612,15 +622,10 @@ void GLView::mouseMoveEvent(QMouseEvent *event)
Matrix4d vec;
vec <<
0, 0, 0, mx,
- 0, 0, 0, 0,
0, 0, 0, my,
+ 0, 0, 0, mz,
0, 0, 0, 1
;
- if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) {
- vec(0,3) = 0;
- vec(1,3) = my;
- vec(2,3) = 0;
- }
tm = tm * vec;
object_trans_x += tm(0,3);
object_trans_y += tm(1,3);
diff --git a/src/lexer.l b/src/lexer.l
index 5644ded..188046f 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -30,6 +30,7 @@
#include "printutils.h"
#include "parsersettings.h"
#include "parser_yacc.h"
+#include "module.h"
#include <assert.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
@@ -52,7 +53,8 @@ int lexerget_lineno(void);
static void yyunput(int, char*) __attribute__((unused));
#endif
extern const char *parser_input_buffer;
-extern const char *parser_source_path;
+extern std::string parser_source_path;
+extern Module *currmodule;
#define YY_INPUT(buf,result,max_size) { \
if (yyin && yyin != stdin) { \
@@ -119,9 +121,12 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
usepath = boosty::absolute(fs::path(get_librarydir()) / filename);
}
}
- handle_dep(usepath.string());
- parserlval.text = strdup(usepath.string().c_str());
- return TOK_USE;
+ /* Only accept regular files which exists */
+ if (usepath.has_parent_path() && fs::exists(usepath)) {
+ handle_dep(usepath.string());
+ parserlval.text = strdup(usepath.string().c_str());
+ return TOK_USE;
+ }
}
}
@@ -215,8 +220,10 @@ void includefile()
filepath.clear();
path_stack.push_back(finfo.parent_path());
- handle_dep(boosty::absolute(finfo).string());
- yyin = fopen(boosty::absolute(finfo).string().c_str(), "r");
+ std::string fullname = boosty::absolute(finfo).string();
+ handle_dep(fullname);
+ currmodule->registerInclude(fullname);
+ yyin = fopen(fullname.c_str(), "r");
if (!yyin) {
PRINTB("WARNING: Can't open input file '%s'.", filename);
path_stack.pop_back();
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 2fd75c9..087cb30 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -25,6 +25,7 @@
*/
#include "PolySetCache.h"
+#include "ModuleCache.h"
#include "MainWindow.h"
#include "openscad.h" // examplesdir
#include "parsersettings.h"
@@ -77,10 +78,10 @@
#include <fstream>
#include <algorithm>
+#include <boost/version.hpp>
#include <boost/foreach.hpp>
-#include <boost/lambda/lambda.hpp>
-#include <boost/lambda/bind.hpp>
-using namespace boost::lambda;
+#include <boost/version.hpp>
+#include <sys/stat.h>
#ifdef ENABLE_CGAL
@@ -94,6 +95,10 @@ using namespace boost::lambda;
#endif // ENABLE_CGAL
+#ifndef OPENCSG_VERSION_STRING
+#define OPENCSG_VERSION_STRING "unknown, <1.3.2"
+#endif
+
// Global application state
unsigned int GuiLocker::gui_locked = 0;
@@ -199,8 +204,8 @@ MainWindow::MainWindow(const QString &filename)
autoReloadTimer->setSingleShot(false);
connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload()));
- connect(e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
- connect(e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
+ connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
+ connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
animate_panel->hide();
@@ -323,7 +328,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout()));
connect(this->helpActionHomepage, SIGNAL(triggered()), this, SLOT(helpHomepage()));
connect(this->helpActionManual, SIGNAL(triggered()), this, SLOT(helpManual()));
- connect(this->helpActionOpenGLInfo, SIGNAL(triggered()), this, SLOT(helpOpenGL()));
+ connect(this->helpActionLibraryInfo, SIGNAL(triggered()), this, SLOT(helpLibrary()));
setCurrentOutput();
@@ -488,7 +493,7 @@ MainWindow::openFile(const QString &new_filename)
#endif
setFileName(new_filename);
- load();
+ refreshDocument();
updateRecentFiles();
}
@@ -544,11 +549,11 @@ void MainWindow::updateRecentFiles()
void MainWindow::updatedFps()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
animate_timer->stop();
if (fps_ok && fps > 0) {
animate_timer->setSingleShot(false);
- animate_timer->setInterval(int(1000 / e_fps->text().toDouble()));
+ animate_timer->setInterval(int(1000 / this->e_fps->text().toDouble()));
animate_timer->start();
}
}
@@ -556,27 +561,28 @@ void MainWindow::updatedFps()
void MainWindow::updateTVal()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok) {
if (fps <= 0) {
actionCompile();
} else {
- double s = e_fsteps->text().toDouble();
- double t = e_tval->text().toDouble() + 1/s;
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble() + 1/s;
QString txt;
txt.sprintf("%.5f", t >= 1.0 ? 0.0 : t);
- e_tval->setText(txt);
+ this->e_tval->setText(txt);
}
}
}
-void MainWindow::load()
+void MainWindow::refreshDocument()
{
setCurrentOutput();
if (!this->fileName.isEmpty()) {
QFile file(this->fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTB("Failed to open file: %s (%s)", this->fileName.toStdString() % file.errorString().toStdString());
+ PRINTB("Failed to open file %s: %s",
+ this->fileName.toStdString() % file.errorString().toStdString());
}
else {
QString text = QTextStream(&file).readAll();
@@ -598,42 +604,30 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
/*!
Parse and evaluate the design => this->root_node
+
+ Returns true if something was compiled, false if nothing was changed
+ and the root_node was left untouched.
*/
-void MainWindow::compile(bool procevents)
+bool MainWindow::compile(bool reload, bool procevents)
{
- PRINT("Parsing design (AST generation)...");
- if (procevents)
- QApplication::processEvents();
+ if (!compileTopLevelDocument(reload)) return false;
// Invalidate renderers before we kill the CSG tree
this->glview->setRenderer(NULL);
- if (this->opencsgRenderer) {
- delete this->opencsgRenderer;
- this->opencsgRenderer = NULL;
- }
- if (this->thrownTogetherRenderer) {
- delete this->thrownTogetherRenderer;
- this->thrownTogetherRenderer = NULL;
- }
+ delete this->opencsgRenderer;
+ this->opencsgRenderer = NULL;
+ delete this->thrownTogetherRenderer;
+ this->thrownTogetherRenderer = NULL;
// Remove previous CSG tree
- if (this->root_module) {
- delete this->root_module;
- this->root_module = NULL;
- }
-
- if (this->absolute_root_node) {
- delete this->absolute_root_node;
- this->absolute_root_node = NULL;
- }
+ delete this->absolute_root_node;
+ this->absolute_root_node = NULL;
this->root_raw_term.reset();
this->root_norm_term.reset();
- if (this->root_chain) {
- delete this->root_chain;
- this->root_chain = NULL;
- }
+ delete this->root_chain;
+ this->root_chain = NULL;
this->highlight_terms.clear();
delete this->highlights_chain;
@@ -646,96 +640,43 @@ void MainWindow::compile(bool procevents)
this->root_node = NULL;
this->tree.setRoot(NULL);
- // Initialize special variables
- this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));
-
- Value vpt;
- vpt.type = Value::VECTOR;
- vpt.append(new Value(-this->glview->object_trans_x));
- vpt.append(new Value(-this->glview->object_trans_y));
- vpt.append(new Value(-this->glview->object_trans_z));
- this->root_ctx.set_variable("$vpt", vpt);
-
- Value vpr;
- vpr.type = Value::VECTOR;
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
- root_ctx.set_variable("$vpr", vpr);
-
- // Parse
- this->last_compiled_doc = editor->toPlainText();
- this->root_module = parse((this->last_compiled_doc + "\n" +
- QString::fromStdString(commandline_commands)).toAscii().data(),
- this->fileName.isEmpty() ?
- "" :
- QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
- false);
-
- // Error highlighting
- if (this->highlighter) {
- delete this->highlighter;
- this->highlighter = NULL;
- }
- if (parser_error_pos >= 0) {
- this->highlighter = new Highlighter(editor->document());
- }
-
- if (!this->root_module) {
- if (!animate_panel->isVisible()) {
-#ifdef _QCODE_EDIT_
- QDocumentCursor cursor = editor->cursor();
- cursor.setPosition(parser_error_pos);
-#else
- QTextCursor cursor = editor->textCursor();
- cursor.setPosition(parser_error_pos);
- editor->setTextCursor(cursor);
-#endif
+ if (this->root_module) {
+ // Evaluate CSG tree
+ PRINT("Compiling design (CSG Tree generation)...");
+ if (procevents) QApplication::processEvents();
+
+ AbstractNode::resetIndexCounter();
+ this->root_inst = ModuleInstantiation();
+ this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
+
+ if (this->absolute_root_node) {
+ // Do we have an explicit root node (! modifier)?
+ if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
+ this->root_node = this->absolute_root_node;
+ }
+ // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
+ this->tree.setRoot(this->root_node);
+ // Dump the tree (to initialize caches).
+ // FIXME: We shouldn't really need to do this explicitly..
+ this->tree.getString(*this->root_node);
}
- goto fail;
}
- // Evaluate CSG tree
- PRINT("Compiling design (CSG Tree generation)...");
- if (procevents)
- QApplication::processEvents();
-
- AbstractNode::resetIndexCounter();
- this->root_inst = ModuleInstantiation();
- this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
-
- if (!this->absolute_root_node)
- goto fail;
-
- // Do we have an explicit root node (! modifier)?
- if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
- this->root_node = this->absolute_root_node;
- }
- // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
- this->tree.setRoot(this->root_node);
- // Dump the tree (to initialize caches).
- // FIXME: We shouldn't really need to do this explicitly..
- this->tree.getString(*this->root_node);
-
- if (1) {
- PRINT("Compilation finished.");
- if (procevents)
- QApplication::processEvents();
- } else {
-fail:
+ if (!this->root_node) {
if (parser_error_pos < 0) {
PRINT("ERROR: Compilation failed! (no top level object found)");
} else {
PRINT("ERROR: Compilation failed!");
}
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
}
+
+ return true;
}
/*!
Generates CSG tree for OpenCSG evaluation.
- Assumes that the design has been parsed and evaluated
+ Assumes that the design has been parsed and evaluated (this->root_node is set)
*/
void MainWindow::compileCSG(bool procevents)
{
@@ -991,7 +932,7 @@ void MainWindow::actionSaveAs()
void MainWindow::actionReload()
{
- if (checkModified()) load();
+ if (checkEditorModified()) refreshDocument();
}
void MainWindow::hideEditor()
@@ -1031,16 +972,127 @@ void MainWindow::pasteViewportRotation()
cursor.insertText(txt);
}
-void MainWindow::checkAutoReload()
+void MainWindow::updateTemporalVariables()
+{
+ this->root_ctx.set_variable("$t", Value(this->e_tval->text().toDouble()));
+
+ Value vpt;
+ vpt.type = Value::VECTOR;
+ vpt.append(new Value(-this->glview->object_trans_x));
+ vpt.append(new Value(-this->glview->object_trans_y));
+ vpt.append(new Value(-this->glview->object_trans_z));
+ this->root_ctx.set_variable("$vpt", vpt);
+
+ Value vpr;
+ vpr.type = Value::VECTOR;
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
+ root_ctx.set_variable("$vpr", vpr);
+}
+
+bool MainWindow::fileChangedOnDisk()
{
if (!this->fileName.isEmpty()) {
- QString new_stinfo;
- QFileInfo finfo(this->fileName);
- new_stinfo = QString::number(finfo.size()) + QString::number(finfo.lastModified().toTime_t());
- if (new_stinfo != autoReloadInfo)
- actionReloadCompile();
- autoReloadInfo = new_stinfo;
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(this->fileName.toLocal8Bit(), &st);
+ std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ if (newid != this->autoReloadId) {
+ this->autoReloadId = newid;
+ return true;
+ }
+ }
+ return false;
+}
+
+// FIXME: The following two methods are duplicated in ModuleCache.cc - refactor
+static bool is_modified(const std::string &filename, const time_t &mtime)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ return (st.st_mtime > mtime);
+}
+
+bool MainWindow::includesChanged()
+{
+ if (this->root_module) {
+ BOOST_FOREACH(const Module::IncludeContainer::value_type &item, this->root_module->includes) {
+ if (is_modified(item.first, item.second)) return true;
+ }
}
+ return false;
+}
+
+/*!
+ If reload is true, does a timestamp check on the document and tries to reload it.
+ Otherwise, just reparses the current document and any dependencies, updates the
+ GUI accordingly and populates this->root_module.
+
+ Returns true if anything was compiled.
+*/
+bool MainWindow::compileTopLevelDocument(bool reload)
+{
+ bool shouldcompiletoplevel = !reload;
+
+ if ((reload && fileChangedOnDisk() && checkEditorModified()) ||
+ includesChanged()) {
+ shouldcompiletoplevel = true;
+ refreshDocument();
+ }
+
+ if (shouldcompiletoplevel) {
+ console->clear();
+
+ updateTemporalVariables();
+
+ this->last_compiled_doc = editor->toPlainText();
+ std::string fulltext =
+ this->last_compiled_doc.toStdString() + "\n" + commandline_commands;
+
+ delete this->root_module;
+ this->root_module = NULL;
+
+ this->root_module = parse(fulltext.c_str(),
+ this->fileName.isEmpty() ?
+ "" :
+ QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
+ false);
+
+ // Error highlighting
+ delete this->highlighter;
+ this->highlighter = NULL;
+
+ if (!this->root_module) {
+ this->highlighter = new Highlighter(editor->document());
+
+ if (!animate_panel->isVisible()) {
+#ifdef _QCODE_EDIT_
+ QDocumentCursor cursor = editor->cursor();
+ cursor.setPosition(parser_error_pos);
+#else
+ QTextCursor cursor = editor->textCursor();
+ cursor.setPosition(parser_error_pos);
+ editor->setTextCursor(cursor);
+#endif
+ }
+ }
+ }
+
+ bool changed = shouldcompiletoplevel;
+ if (this->root_module) {
+ changed |= this->root_module->handleDependencies();
+ if (changed) PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
+ }
+
+ return changed;
+}
+
+void MainWindow::checkAutoReload()
+{
+ if (!this->fileName.isEmpty()) actionReloadCompile();
}
void MainWindow::autoReloadSet(bool on)
@@ -1048,14 +1100,14 @@ void MainWindow::autoReloadSet(bool on)
QSettings settings;
settings.setValue("design/autoReload",designActionAutoReload->isChecked());
if (on) {
- autoReloadInfo = QString();
+ autoReloadId = "";
autoReloadTimer->start(200);
} else {
autoReloadTimer->stop();
}
}
-bool MainWindow::checkModified()
+bool MainWindow::checkEditorModified()
{
if (editor->isContentModified()) {
QMessageBox::StandardButton ret;
@@ -1075,15 +1127,11 @@ void MainWindow::actionReloadCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
- if (!checkModified()) return;
-
- console->clear();
-
- load();
-
setCurrentOutput();
- compile(true);
+
+ // PRINT("Parsing design (AST generation)...");
+ // QApplication::processEvents();
+ if (!compile(true, true)) return;
if (this->root_node) compileCSG(true);
// Go to non-CGAL view mode
@@ -1105,11 +1153,12 @@ void MainWindow::actionCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
setCurrentOutput();
console->clear();
- compile(!viewActionAnimate->isChecked());
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, !viewActionAnimate->isChecked());
if (this->root_node) compileCSG(!viewActionAnimate->isChecked());
// Go to non-CGAL view mode
@@ -1127,8 +1176,8 @@ void MainWindow::actionCompile()
if (viewActionAnimate->isChecked() && e_dump->isChecked()) {
QImage img = this->glview->grabFrameBuffer();
QString filename;
- double s = e_fsteps->text().toDouble();
- double t = e_tval->text().toDouble();
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble();
filename.sprintf("frame%05d.png", int(round(s*t)));
img.save(filename, "PNG");
}
@@ -1146,7 +1195,9 @@ void MainWindow::actionRenderCGAL()
setCurrentOutput();
console->clear();
- compile(true);
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, true);
if (!this->root_module || !this->root_node) {
return;
@@ -1449,7 +1500,7 @@ void MainWindow::actionFlushCaches()
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();
- Module::clear_library_cache();
+ ModuleCache::instance()->clear();
}
void MainWindow::viewModeActionsUncheck()
@@ -1556,7 +1607,7 @@ void MainWindow::animateUpdate()
{
if (animate_panel->isVisible()) {
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok && fps <= 0 && !animate_timer->isActive()) {
animate_timer->stop();
animate_timer->setSingleShot(true);
@@ -1699,15 +1750,28 @@ MainWindow::helpManual()
QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual"));
}
-void MainWindow::helpOpenGL()
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+void MainWindow::helpLibrary()
{
+ QString libinfo;
+ libinfo.sprintf("Boost version: %s\n"
+ "Eigen version: %d.%d.%d\n"
+ "CGAL version: %s\n"
+ "OpenCSG version: %s\n\n",
+ BOOST_LIB_VERSION,
+ EIGEN_WORLD_VERSION, EIGEN_MAJOR_VERSION, EIGEN_MINOR_VERSION,
+ TOSTRING(CGAL_VERSION),
+ OPENCSG_VERSION_STRING);
+
if (!this->openglbox) {
this->openglbox = new QMessageBox(QMessageBox::Information,
- "OpenGL Info", "Detailed OpenGL Info",
+ "OpenGL Info", "Detailed Library Info",
QMessageBox::Ok, this);
}
- this->openglbox->setDetailedText(this->glview->getRendererInfo());
+
+ this->openglbox->setDetailedText(libinfo + this->glview->getRendererInfo());
this->openglbox->show();
}
diff --git a/src/module.cc b/src/module.cc
index 6641ff7..fc849ff 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -25,13 +25,16 @@
*/
#include "module.h"
+#include "ModuleCache.h"
#include "node.h"
#include "context.h"
#include "expression.h"
#include "function.h"
#include "printutils.h"
+
#include <boost/foreach.hpp>
#include <sstream>
+#include <sys/stat.h>
AbstractModule::~AbstractModule()
{
@@ -201,7 +204,37 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
return dump.str();
}
-void Module::clear_library_cache()
+void Module::registerInclude(const std::string &filename)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ this->includes[filename] = st.st_mtime;
+}
+
+/*!
+ Check if any dependencies have been modified and recompile them.
+ Returns true if anything was recompiled.
+*/
+bool Module::handleDependencies()
{
- Module::libs_cache.clear();
+ bool changed = false;
+ // Iterating manually since we want to modify the container while iterating
+ Module::ModuleContainer::iterator iter = this->usedlibs.begin();
+ while (iter != this->usedlibs.end()) {
+ Module::ModuleContainer::iterator curr = iter++;
+ Module *oldmodule = curr->second;
+ curr->second = ModuleCache::instance()->evaluate(curr->first);
+ if (curr->second != oldmodule) {
+ changed = true;
+#ifdef DEBUG
+ PRINTB_NOCACHE(" %s: %p", curr->first % curr->second);
+#endif
+ }
+ if (!curr->second) {
+ PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", curr->first);
+ this->usedlibs.erase(curr);
+ }
+ }
+ return changed;
}
diff --git a/src/module.h b/src/module.h
index 6c6529b..cd25287 100644
--- a/src/module.h
+++ b/src/module.h
@@ -3,6 +3,7 @@
#include <string>
#include <vector>
+#include <list>
#include <boost/unordered_map.hpp>
#include "value.h"
@@ -65,11 +66,12 @@ public:
void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); }
- static Module *compile_library(const std::string &filename);
- static void clear_library_cache();
-
typedef boost::unordered_map<std::string, class Module*> ModuleContainer;
ModuleContainer usedlibs;
+ void registerInclude(const std::string &filename);
+ typedef boost::unordered_map<std::string, time_t> IncludeContainer;
+ IncludeContainer includes;
+ bool handleDependencies();
std::vector<std::string> assignments_var;
std::vector<Expression*> assignments_expr;
@@ -87,11 +89,6 @@ public:
protected:
private:
- struct libs_cache_ent {
- Module *mod;
- std::string cache_id, msg;
- };
- static boost::unordered_map<std::string, libs_cache_ent> libs_cache;
};
#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 980e2af..7fe054f 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -149,9 +149,14 @@ int main(int argc, char **argv)
all_options.add(desc).add(hidden);
po::variables_map vm;
- po::store(po::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
-// po::notify(vm);
-
+ try {
+ po::store(po::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
+ }
+ catch(std::exception &e) { // Catches e.g. unknown options
+ fprintf(stderr, "%s\n", e.what());
+ help(argv[0]);
+ }
+
if (vm.count("help")) help(argv[0]);
if (vm.count("version")) version();
@@ -255,34 +260,28 @@ int main(int argc, char **argv)
Context root_ctx;
register_builtin(root_ctx);
- AbstractModule *root_module;
+ Module *root_module;
ModuleInstantiation root_inst;
AbstractNode *root_node;
handle_dep(filename);
- FILE *fp = fopen(filename, "rt");
- if (!fp) {
- fprintf(stderr, "Can't open input file `%s'!\n", filename);
+
+ std::ifstream ifs(filename);
+ if (!ifs.is_open()) {
+ fprintf(stderr, "Can't open input file '%s'!\n", filename);
exit(1);
- } else {
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
- text << "\n" << commandline_commands;
- fs::path abspath = boosty::absolute( filename );
- std::string fname = boosty::stringy( abspath );
- root_module = parse(text.str().c_str(), fname.c_str(), false);
- if (!root_module) exit(1);
}
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+ text += "\n" + commandline_commands;
+ fs::path abspath = boosty::absolute(filename);
+ std::string parentpath = boosty::stringy(abspath.parent_path());
+ root_module = parse(text.c_str(), parentpath.c_str(), false);
+ if (!root_module) exit(1);
+ root_module->handleDependencies();
- fs::path fpath = boosty::absolute( fs::path(filename) );
+ fs::path fpath = boosty::absolute(fs::path(filename));
fs::path fparent = fpath.parent_path();
- fs::current_path( fparent );
+ fs::current_path(fparent);
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/src/openscad.h b/src/openscad.h
index dab14cd..8b49ba2 100644
--- a/src/openscad.h
+++ b/src/openscad.h
@@ -27,7 +27,7 @@
#ifndef OPENSCAD_H
#define OPENSCAD_H
-extern class AbstractModule *parse(const char *text, const char *path, int debug);
+extern class Module *parse(const char *text, const char *path, int debug);
extern int get_fragments_from_r(double r, double fn, double fs, double fa);
#include <string>
diff --git a/src/parser.y b/src/parser.y
index 15a754b..195e7a8 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -43,7 +43,7 @@
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
#include "boosty.h"
int parser_error_pos = -1;
@@ -56,7 +56,7 @@ int lexerlex_destroy(void);
int lexerlex(void);
std::vector<Module*> module_stack;
-Module *module;
+Module *currmodule;
class ArgContainer {
public:
@@ -133,7 +133,7 @@ public:
input:
/* empty */ |
- TOK_USE { module->usedlibs[$1] = NULL; } input |
+ TOK_USE { currmodule->usedlibs[$1] = NULL; } input |
statement input ;
inner_input:
@@ -145,37 +145,37 @@ statement:
'{' inner_input '}' |
module_instantiation {
if ($1) {
- module->addChild($1);
+ currmodule->addChild($1);
} else {
delete $1;
}
} |
TOK_ID '=' expr ';' {
bool add_new_assignment = true;
- for (size_t i = 0; i < module->assignments_var.size(); i++) {
- if (module->assignments_var[i] != $1)
+ for (size_t i = 0; i < currmodule->assignments_var.size(); i++) {
+ if (currmodule->assignments_var[i] != $1)
continue;
- delete module->assignments_expr[i];
- module->assignments_expr[i] = $3;
+ delete currmodule->assignments_expr[i];
+ currmodule->assignments_expr[i] = $3;
add_new_assignment = false;
}
if (add_new_assignment) {
- module->assignments_var.push_back($1);
- module->assignments_expr.push_back($3);
+ currmodule->assignments_var.push_back($1);
+ currmodule->assignments_expr.push_back($3);
free($1);
}
} |
TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' {
- Module *p = module;
- module_stack.push_back(module);
- module = new Module();
- p->modules[$2] = module;
- module->argnames = $4->argnames;
- module->argexpr = $4->argexpr;
+ Module *p = currmodule;
+ module_stack.push_back(currmodule);
+ currmodule = new Module();
+ p->modules[$2] = currmodule;
+ currmodule->argnames = $4->argnames;
+ currmodule->argexpr = $4->argexpr;
free($2);
delete $4;
} statement {
- module = module_stack.back();
+ currmodule = module_stack.back();
module_stack.pop_back();
} |
TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr {
@@ -183,7 +183,7 @@ statement:
func->argnames = $4->argnames;
func->argexpr = $4->argexpr;
func->expr = $8;
- module->functions[$2] = func;
+ currmodule->functions[$2] = func;
free($2);
delete $4;
} ';' ;
@@ -560,101 +560,33 @@ void yyerror (char const *s)
{
// FIXME: We leak memory on parser errors...
PRINTB("Parser error in line %d: %s\n", lexerget_lineno() % s);
- module = NULL;
+ currmodule = NULL;
}
extern void lexerdestroy();
extern FILE *lexerin;
extern const char *parser_input_buffer;
const char *parser_input_buffer;
-const char *parser_source_path;
+std::string parser_source_path;
-AbstractModule *parse(const char *text, const char *path, int debug)
+Module *parse(const char *text, const char *path, int debug)
{
lexerin = NULL;
parser_error_pos = -1;
parser_input_buffer = text;
- parser_source_path = path;
+ parser_source_path = std::string(path);
module_stack.clear();
- module = new Module();
+ Module *rootmodule = currmodule = new Module();
+ // PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule);
parserdebug = debug;
parserparse();
lexerdestroy();
lexerlex_destroy();
- if (!module)
- return NULL;
-
- // Iterating manually since we want to modify the container while iterating
- Module::ModuleContainer::iterator iter = module->usedlibs.begin();
- while (iter != module->usedlibs.end()) {
- Module::ModuleContainer::iterator curr = iter++;
- curr->second = Module::compile_library(curr->first);
- if (!curr->second) {
- PRINTB("WARNING: Failed to compile library '%s'.", curr->first);
- module->usedlibs.erase(curr);
- }
- }
+ if (!rootmodule) return NULL;
parser_error_pos = -1;
- return module;
-}
-
-boost::unordered_map<std::string, Module::libs_cache_ent> Module::libs_cache;
-
-Module *Module::compile_library(const std::string &filename)
-{
- struct stat st;
- memset(&st, 0, sizeof(struct stat));
- stat(filename.c_str(), &st);
-
- std::stringstream idstream;
- idstream << std::hex << st.st_mtime << "." << st.st_size;
- std::string cache_id = idstream.str();
-
- if (libs_cache.find(filename) != libs_cache.end() && libs_cache[filename].cache_id == cache_id) {
- PRINTB("%s", libs_cache[filename].msg);
- return &(*libs_cache[filename].mod);
- }
-
- FILE *fp = fopen(filename.c_str(), "rt");
- if (!fp) {
- fprintf(stderr, "WARNING: Can't open library file '%s'\n", filename.c_str());
- return NULL;
- }
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
-
- print_messages_push();
-
- PRINTB("Compiling library '%s'.", filename);
- libs_cache_ent e = { NULL, cache_id, std::string("WARNING: Library `") + filename + "' tries to recursively use itself!" };
- if (libs_cache.find(filename) != libs_cache.end())
- delete libs_cache[filename].mod;
- libs_cache[filename] = e;
-
- Module *backup_mod = module;
- std::string pathname = boosty::stringy( fs::path(filename).parent_path() );
- Module *lib_mod = dynamic_cast<Module*>(parse(text.str().c_str(), pathname.c_str(), 0));
- module = backup_mod;
-
- if (lib_mod) {
- libs_cache[filename].mod = lib_mod;
- libs_cache[filename].msg = print_messages_stack.back();
- } else {
- libs_cache.erase(filename);
- }
-
- print_messages_pop();
-
- return lib_mod;
+ return rootmodule;
}
-
diff --git a/src/primitives.cc b/src/primitives.cc
index feaa1a4..ce52550 100644
--- a/src/primitives.cc
+++ b/src/primitives.cc
@@ -530,7 +530,7 @@ sphere_next_r2:
{
dd.paths.push_back(DxfData::Path());
for (size_t j=0; j<this->paths.vec[i]->vec.size(); j++) {
- int idx = this->paths.vec[i]->vec[j]->num;
+ unsigned int idx = this->paths.vec[i]->vec[j]->num;
if (idx < dd.points.size()) {
dd.paths.back().indices.push_back(idx);
}
diff --git a/src/system-gl.h b/src/system-gl.h
index 0377b72..d7de3c6 100644
--- a/src/system-gl.h
+++ b/src/system-gl.h
@@ -7,6 +7,7 @@
#include <OpenGL/OpenGL.h>
#else
#include <GL/gl.h>
+ #include <GL/glu.h>
#ifdef _WIN32
#include <windows.h> // For the CALLBACK macro
#endif
diff --git a/src/transform.cc b/src/transform.cc
index f5038b1..c2ac194 100644
--- a/src/transform.cc
+++ b/src/transform.cc
@@ -87,11 +87,10 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
{
Vector3d scalevec(1,1,1);
Value v = c.lookup_variable("v");
- v.getnum(scalevec[0]);
- v.getnum(scalevec[1]);
- v.getnum(scalevec[2]);
- v.getv3(scalevec[0], scalevec[1], scalevec[2]);
- if (scalevec[2] == 0) scalevec[2] = 1;
+ if (!v.getv3(scalevec[0], scalevec[1], scalevec[2], 1.0)) {
+ double num;
+ if (v.getnum(num)) scalevec.setConstant(num);
+ }
node->matrix.scale(scalevec);
}
else if (this->type == ROTATE)
diff --git a/src/value.cc b/src/value.cc
index 47fac1e..93c4d5e 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -30,6 +30,7 @@
#include <sstream>
#include <QDir>
#include <boost/foreach.hpp>
+#include "printutils.h"
Value::Value()
{
@@ -157,6 +158,64 @@ Value Value::operator * (const Value &v) const
if (this->type == NUMBER && v.type == NUMBER) {
return Value(this->num * v.num);
}
+ if (this->type == VECTOR && v.type == VECTOR && this->vec.size() == v.vec.size() ) {
+ if ( this->vec[0]->type == NUMBER && v.vec[0]->type == NUMBER ) {
+ // Vector dot product.
+ double r=0.0;
+ for (size_t i=0; i <this->vec.size(); i++) {
+ if ( this->vec[i]->type != NUMBER || v.vec[i]->type != NUMBER ) return Value();
+ r = r + (this->vec[i]->num * v.vec[i]->num);
+ }
+ return Value(r);
+ } else if ( this->vec[0]->type == VECTOR && v.vec[0]->type == NUMBER ) {
+ // Matrix * Vector
+ Value r;
+ r.type = VECTOR;
+ for ( size_t i=0; i < this->vec.size(); i++) {
+ double r_e=0.0;
+ if ( this->vec[i]->vec.size() != v.vec.size() ) return Value();
+ for ( size_t j=0; j < this->vec[i]->vec.size(); j++) {
+ if ( this->vec[i]->vec[j]->type != NUMBER || v.vec[i]->type != NUMBER ) return Value();
+ r_e = r_e + (this->vec[i]->vec[j]->num * v.vec[j]->num);
+ }
+ r.vec.push_back(new Value(r_e));
+ }
+ return r;
+ } else if (this->vec[0]->type == NUMBER && v.vec[0]->type == VECTOR ) {
+ // Vector * Matrix
+ Value r;
+ r.type = VECTOR;
+ for ( size_t i=0; i < v.vec[0]->vec.size(); i++) {
+ double r_e=0.0;
+ for ( size_t j=0; j < v.vec.size(); j++) {
+ if ( v.vec[j]->vec.size() != v.vec[0]->vec.size() ) return Value();
+ if ( this->vec[j]->type != NUMBER || v.vec[j]->vec[i]->type != NUMBER ) return Value();
+ r_e = r_e + (this->vec[j]->num * v.vec[j]->vec[i]->num);
+ }
+ r.vec.push_back(new Value(r_e));
+ }
+ return r;
+ }
+ }
+ if (this->type == VECTOR && v.type == VECTOR && this->vec[0]->type == VECTOR && v.vec[0]->type == VECTOR && this->vec[0]->vec.size() == v.vec.size() ) {
+ // Matrix * Matrix
+ Value rrow;
+ rrow.type = VECTOR;
+ for ( size_t i=0; i < this->vec.size(); i++ ) {
+ Value * rcol=new Value();
+ rcol->type = VECTOR;
+ for ( size_t j=0; j < this->vec.size(); j++ ) {
+ double r_e=0.0;
+ for ( size_t k=0; k < v.vec.size(); k++ ) {
+ r_e = r_e + (this->vec[i]->vec[k]->num * v.vec[k]->vec[j]->num);
+ }
+ // PRINTB(" r_e = %s",r_e);
+ rcol->vec.push_back(new Value(r_e));
+ }
+ rrow.vec.push_back(rcol);
+ }
+ return rrow;
+ }
return Value();
}
@@ -300,11 +359,11 @@ bool Value::getv2(double &x, double &y) const
return true;
}
-bool Value::getv3(double &x, double &y, double &z) const
+bool Value::getv3(double &x, double &y, double &z, double defaultval) const
{
if (this->type == VECTOR && this->vec.size() == 2) {
if (getv2(x, y)) {
- z = 0;
+ z = defaultval;
return true;
}
return false;
@@ -367,6 +426,10 @@ std::string Value::toString() const
// Quick and dirty hack to work around floating point rounding differences
// across platforms for testing purposes.
{
+ if (this->num != this->num) { // Fix for avoiding nan vs. -nan across platforms
+ stream << "nan";
+ break;
+ }
std::stringstream tmp;
tmp.precision(12);
tmp.setf(std::ios_base::fixed);
diff --git a/src/value.h b/src/value.h
index a2cfbdf..4a67fbc 100644
--- a/src/value.h
+++ b/src/value.h
@@ -73,7 +73,7 @@ public:
bool getnum(double &v) const;
bool getv2(double &x, double &y) const;
- bool getv3(double &x, double &y, double &z) const;
+ bool getv3(double &x, double &y, double &z, double defaultval = 0.0) const;
std::string toString() const;
diff --git a/testdata/scad/bugs/issue95-normalization-crash.scad b/testdata/scad/bugs/issue95-normalization-crash.scad
new file mode 100644
index 0000000..10a3f66
--- /dev/null
+++ b/testdata/scad/bugs/issue95-normalization-crash.scad
@@ -0,0 +1,128 @@
+//
+// Reported by Triffid Hunter.
+// Causes a crash in CCGTermNormalizer::normalizePass()
+//
+
+pi = 3.141592653589;
+sl = 0.5;
+
+w = 400;
+
+pulley_diam = 28;
+bearing_diam = 22; //include size of bearing guides
+
+module crx(size=[1, 1, 1]) {
+ linear_extrude(height=size[2])
+ square([size[0], size[1]], center=true);
+}
+
+module cyl(r=1, h=1, center=false) {
+ cylinder(r=r, h=h, center=center, $fn=r * 2 * pi / sl);
+}
+
+module lm8uu() {
+ cyl(r=15 /2, h=24);
+}
+
+module nema17() {
+ translate([0, 0, -48]) crx([42, 42, 48]);
+ translate([0, 0, -1]) {
+ cyl(r=2.5, h=26);
+ cyl(r=22.5 / 2, h=3.1);
+ }
+ translate([0, 0, 2.1]) {
+ difference() {
+ cyl(r=pulley_diam / 2, h=11.5);
+ translate([0, 0, (11.5 - 8) / 2]) rotate_extrude()
+ translate([21.5 / 2, 0])
+ square(8);
+ }
+ }
+ cyl(r=17.5 / 2, h=19.5);
+ for (i=[0:3]) {
+ rotate([0, 0, i * 90])
+ translate([31 / 2, 31 / 2, -1]) {
+ cyl(r=1.5, h=11);
+ translate([0, 0, 5.5])
+ cyl(r=4, h=30);
+ }
+ }
+}
+
+module bearing608() {
+ cyl(r=bearing_diam / 2, h = 7);
+}
+
+module rods() {
+ for (i=[0:1]) {
+ translate([25, 0, i * 70])
+ rotate([0, -90, 0])
+ cyl(r=4, h=420);
+ }
+
+ translate([0, 10, -40]) {
+ cyl(r=3, h=150);
+ translate([0, 0, 34]) cylinder(r=10 / cos(180 / 6) / 2, h=6, $fn=6);
+ }
+
+ translate([30, 10, -40]) {
+ cyl(r=4, h=150);
+ }
+}
+
+module lm8uu_holder() {
+ render()
+ difference() {
+ union() {
+ translate([0, 0, -13]) difference() {
+ hull() {
+ translate([-9, -9, -1]) cube([18, 1, 28]);
+ translate([0, 1, -1]) cyl(r=18 / 2, h = 28);
+ }
+ translate([-10, 5, -1]) cube([20, 10, 28]);
+ translate([-10, -3, 11 - 5]) cube([20, 20, 4]);
+ translate([-10, -3, 11 + 5]) cube([20, 20, 4]);
+ }
+ }
+ translate([0, 0, -11]) {
+ hull() {
+ #lm8uu();
+ translate([0, 10, 0]) cyl(r=6, h=24);
+ }
+ translate([0, 0, -3]) hull() {
+ cyl(r=5, h=30);
+ translate([0, 10, 0]) cyl(r=5, h=30);
+ }
+ }
+ }
+}
+
+module x_end_motor() {
+ difference() {
+ union() {
+ translate([-10, -18, -19]) #cube([50, 2, 90]);
+ translate([30, 10, 60]) lm8uu_holder();
+ translate([30, 10, -5]) lm8uu_holder();
+ }
+ #rods();
+
+ translate([15.5, -19, 14]) {
+ rotate([90, 0, 0]) cyl(r=30 / 2, h= 50, center=true);
+ rotate([-90, 45, 0])
+ #nema17();
+ translate([-w, 4, 0])
+ rotate([-90, 0, 0])
+ bearing608();
+ translate([0, 5, 0]) hull() {
+ #translate([0, 0, 24 / 2 - 1.5]) cube([1, 5, 1.5]);
+ #translate([-w, 0, bearing_diam / 2 ]) cube([1, 5, 1.5]);
+ }
+ translate([0, 5, 0]) hull() {
+ #translate([0, 0, 24 / -2]) cube([1, 5, 1.5]);
+ #translate([-w, 0, bearing_diam / -2 - 1.5]) cube([1, 5, 1.5]);
+ }
+ }
+ }
+}
+
+x_end_motor(); \ No newline at end of file
diff --git a/testdata/scad/features/text-search-test.scad b/testdata/scad/features/text-search-test.scad
new file mode 100644
index 0000000..8b6047f
--- /dev/null
+++ b/testdata/scad/features/text-search-test.scad
@@ -0,0 +1,29 @@
+// fonts test
+
+use <MCAD/fonts.scad>
+
+thisFont=8bit_polyfont();
+thisText="OpenSCAD Rocks!";
+// Find one letter matches from 2nd column (index 1)
+theseIndicies=search(thisText,thisFont[2],1,1);
+// Letter spacing, x direction.
+x_shift=thisFont[0][0];
+y_shift=thisFont[0][1];
+echo(theseIndicies);
+// Simple polygon usage.
+for(i=[0:len(theseIndicies)-1]) translate([i*x_shift-len(theseIndicies)*x_shift/2,0]) {
+ polygon(points=thisFont[2][theseIndicies[i]][6][0],paths=thisFont[2][theseIndicies[i]][6][1]);
+}
+
+theseIndicies2=search("ABC",thisFont[2],1,1);
+// outline_2d() example
+for(i=[0:len(theseIndicies2)-1]) translate([i*x_shift-len(theseIndicies2)*x_shift,-y_shift]) {
+ outline_2d(outline=true,points=thisFont[2][theseIndicies2[i]][6][0],paths=thisFont[2][theseIndicies2[i]][6][1],width=0.25);
+}
+
+theseIndicies3=search("123",thisFont[2],1,1);
+// bold_2d() outline_2d(false) example
+for(i=[0:len(theseIndicies3)-1]) translate([i*x_shift,-2*y_shift]) {
+ bold_2d(bold=true,width=0.25,resolution=8)
+ outline_2d(false,thisFont[2][theseIndicies3[i]][6][0],thisFont[2][theseIndicies3[i]][6][1]);
+} \ No newline at end of file
diff --git a/testdata/scad/misc/search-tests.scad b/testdata/scad/misc/search-tests.scad
new file mode 100644
index 0000000..fb85109
--- /dev/null
+++ b/testdata/scad/misc/search-tests.scad
@@ -0,0 +1,63 @@
+// string searches
+
+simpleSearch1=search("a","abcdabcd");
+echo(str("Characters in string (\"a\"): ",simpleSearch1));
+
+simpleSearch2=search("adeq","abcdeabcd",0);
+echo(str("Characters in string (\"adeq\"): ",simpleSearch2));
+
+sTable1=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ];
+s1= search("abe",sTable1);
+echo(str("Default string search (\"abe\"): ",s1));
+
+sTable2=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9],["a",10],["a",11] ];
+s2= search("abe",sTable2,0);
+echo(str("Return all matches for string search (\"abe\"): ",s2));
+
+sTable3=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9],["a",10],["a",11] ];
+s3= search("abe",sTable3,2);
+echo(str("Return up to 2 matches for string search (\"abe\"): ",s3));
+
+sTable4=[ [1,"a",[20]],[2,"b",21],[3,"c",22],[4,"d",23],[5,"a",24],[6,"b",25],[7,"c",26],[8,"d",27],[9,"e",28],[10,"a",29],[11,"a",30] ];
+s4= search("aebe",sTable4,2,1);
+echo(str("Return up to 2 matches for string search; alternate columns (\"aebe\"): ",s4));
+
+// s5= search("abe",sTable4,2,1,3); // bounds checking needs fixing.
+// echo(str("Return up to 2 matches for string search; alternate columns: ",s4));
+
+
+// number searches
+nTable1=[ [1,"a"],[3,"b"],[2,"c"],[4,"d"],[1,"a"],[7,"b"],[2,"c"],[8,"d"],[9,"e"],[10,"a"],[1,"a"] ];
+n1 = search(7,nTable1);
+echo(str("Default number search (7): ",n1));
+n2 = search(1,nTable1,0);
+echo(str("Return all matches for number search (1): ",n2));
+n3 = search(1,nTable1,2);
+echo(str("Return up to 2 matches for number search (1): ",n3));
+
+// list searches
+lTable1=[ [1,"a"],[3,"b"],[2,"c"],[4,"d"],[1,"a"],[7,"b"],[2,"c"],[8,"d"],[9,"e"],[10,"a"],[1,"a"] ];
+lSearch1=[1,3,1000];
+l1=search(lSearch1,lTable1);
+echo(str("Default list number search (",lSearch1,"): ",l1));
+
+lTable2=[ ["cat",1],["b",2],["c",3],["dog",4],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ];
+lSearch2=["b","zzz","a","c","apple","dog"];
+l2=search(lSearch2,lTable2);
+echo(str("Default list string search (",lSearch2,"): ",l2));
+
+lTable3=[ ["cat",1],["b",2],["c",3],[4,"dog"],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ];
+lSearch3=["b",4,"zzz","c","apple",500,"a",""];
+l3=search(lSearch3,lTable3);
+echo(str("Default list mixed search (",lSearch3,"): ",l3));
+
+l4=search(lSearch3,lTable3,0);
+echo(str("Return all matches for mixed search (",lSearch3,"): ",l4));
+
+lSearch5=[1,"zz","dog",500,11];
+l5=search(lSearch5,lTable3,0,1);
+echo(str("Return all matches for mixed search; alternate columns (",lSearch5,"): ",l5));
+
+
+// for completeness
+cube(1.0);
diff --git a/testdata/scad/misc/vector-values.scad b/testdata/scad/misc/vector-values.scad
new file mode 100644
index 0000000..1872b39
--- /dev/null
+++ b/testdata/scad/misc/vector-values.scad
@@ -0,0 +1,40 @@
+// Value vector tests.
+
+a1=[0,1,2];
+b1=[3,4,5];
+c1=a1*b1;
+echo(str("Testing vector dot product: ",c1));
+
+d1=[1,0];
+echo(str(" Bounds check: ",a1*d1));
+
+m2=[[0,1],[1,0]];
+v2=[2,3];
+p2=m2*v2;
+echo(str("Testing matrix * vector: ",p2));
+
+d2=[0,0,1];
+echo(str(" Bounds check: ",m2*d2));
+
+m3=[[1,-1,1],[1,0,-1]];
+v3=[1,1];
+p3=v3*m3;
+echo(str("Testing vector * matrix: ",p3));
+
+echo(str(" Bounds check: ",m3*v3));
+
+ma4=[ [1,0],[0,1] ];
+mb4=[ [1,0],[0,1] ];
+echo(str("Testing id matrix * id matrix: ",ma4*mb4));
+
+ma5=[ [1, 0, 1]
+ ,[0, 1,-1] ];
+mb5=[ [1,0]
+ ,[0,1]
+ ,[1,1] ];
+echo(str("Testing asymmetric matrix * matrix: ",ma5*mb5));
+echo(str("Testing alternate asymmetric matrix * matrix: ",mb5*ma5));
+
+echo(str(" Bounds check: ",ma5*ma4));
+
+cube(1.0);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b81ce8c..12b8543 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -229,10 +229,27 @@ if (NOT $ENV{GLEWDIR} STREQUAL "")
elseif (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
set(GLEW_DIR "$ENV{OPENSCAD_LIBRARIES}")
endif()
+if (NOT GLEW_INCLUDE_DIR)
+ message(STATUS "OPENCSG_DIR: " ${OPENCSG_DIR})
+ find_path(GLEW_INCLUDE_DIR
+ GL/glew.h
+ HINTS ${GLEW_DIR}/include
+ NO_DEFAULT_PATH)
+ find_library(GLEW_LIBRARY
+ NAMES GLEW glew
+ HINTS ${GLEW_DIR}/lib
+ NO_DEFAULT_PATH)
+ if (NOT GLEW_LIBRARY)
+ find_package(GLEW REQUIRED)
+ if (NOT GLEW_LIBRARY)
+ message(FATAL_ERROR "GLEW not found")
+ endif()
+ endif()
+ message(STATUS "GLEW include: " ${GLEW_INCLUDE_DIR})
+ message(STATUS "GLEW library: " ${GLEW_LIBRARY})
+endif()
-find_package(GLEW REQUIRED)
-
-inclusion( GLEW_DIR GLEW_INCLUDE_PATH )
+inclusion(GLEW_DIR GLEW_INCLUDE_DIR)
# Flex/Bison
find_package(BISON REQUIRED)
@@ -309,6 +326,7 @@ set(CORE_SOURCES
../src/expr.cc
../src/func.cc
../src/module.cc
+ ../src/ModuleCache.cc
../src/node.cc
../src/context.cc
../src/csgterm.cc
@@ -400,19 +418,25 @@ target_link_libraries(echotest tests-nocgal tests-core ${QT_LIBRARIES} ${OPENGL_
# dumptest
#
add_executable(dumptest dumptest.cc)
-target_link_libraries(dumptest tests-common tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
+target_link_libraries(dumptest tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
+
+#
+# modulecachetest
+#
+add_executable(modulecachetest modulecachetest.cc)
+target_link_libraries(modulecachetest tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
#
# csgtexttest
#
add_executable(csgtexttest csgtexttest.cc CSGTextRenderer.cc CSGTextCache.cc)
-target_link_libraries(csgtexttest tests-common tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
+target_link_libraries(csgtexttest tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
#
# csgtermtest
#
add_executable(csgtermtest csgtermtest.cc ../src/CSGTermEvaluator.cc)
-target_link_libraries(csgtermtest tests-common tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
+target_link_libraries(csgtermtest tests-nocgal ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})
#
# cgaltest
@@ -608,7 +632,9 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/builtin-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/dim-all.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-test.scad
- ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad)
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad)
list(APPEND DUMPTEST_FILES ${MINIMAL_FILES} ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
@@ -631,6 +657,7 @@ disable_tests(dumptest_transform-tests
dumptest_render-tests
dumptest_difference-tests
dumptest_intersection-tests
+ dumptest_text-search-test
dumptest_example001
dumptest_example005
dumptest_example006
@@ -642,7 +669,8 @@ disable_tests(dumptest_transform-tests
dumptest_example017
dumptest_example020
dumptest_example021
- dumptest_example022)
+ dumptest_example022
+ dumptest_example023)
# FIXME: This test illustrates a weakness in child() combined with modifiers.
# Reenable it when this is improved
diff --git a/tests/FindGLEW.cmake b/tests/FindGLEW.cmake
index 1b0cac4..1d94ca2 100644
--- a/tests/FindGLEW.cmake
+++ b/tests/FindGLEW.cmake
@@ -3,55 +3,63 @@
# Once done this will define
#
# GLEW_FOUND
-# GLEW_INCLUDE_PATH
+# GLEW_INCLUDE_DIR
# GLEW_LIBRARY
#
# a few lines of this file are based on the LGPL code found at
# http://openlibraries.org/browser/trunk/FindGLEW.cmake?rev=1383
+include(FindPkgConfig)
-IF (WIN32 AND MSVC)
- IF (WIN32_STATIC_BUILD) # passed from caller
- SET(GLEW_LIB_SEARCH_NAME glew32s.lib) # static, non-debug (Release)
- ELSE ()
- SET(GLEW_LIB_SEARCH_NAME glew32.lib) # other. untested with OpenSCAD
- ENDIF()
-ELSE () # GCC
- SET(GLEW_LIB_SEARCH_NAME "libglew32s.a")
-ENDIF ()
+if (PKG_CONFIG_FOUND)
+ message("Doing pkg config glew check...")
+ pkg_check_modules(GLEW glew>=1.6)
+endif()
-IF (WIN32)
- FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
- $ENV{PROGRAMFILES}/GLEW/include
- ${PROJECT_SOURCE_DIR}/src/nvgl/glew/include
- DOC "The directory where GL/glew.h resides")
- FIND_LIBRARY( GLEW_LIBRARY
- NAMES ${GLEW_LIB_SEARCH_NAME}
- PATHS
- $ENV{PROGRAMFILES}/GLEW/lib
- ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin
- ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib
- DOC "The GLEW library")
-ELSE (WIN32)
- message(STATUS "GLEW_DIR: " ${GLEW_DIR})
- FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
- HINTS ${GLEW_DIR}/include
- PATHS /usr/include /usr/local/include /usr/pkg/include
- NO_DEFAULT_PATH
- DOC "The directory where GL/glew.h resides")
- FIND_LIBRARY( GLEW_LIBRARY
- NAMES GLEW glew
- HINTS ${GLEW_DIR}/lib
- PATHS /usr/lib /usr/local/lib /usr/pkg/lib
- NO_DEFAULT_PATH
- DOC "The GLEW library")
-ENDIF (WIN32)
+if (GLEW_LIBRARIES)
+ set(GLEW_INCLUDE_DIR "${GLEW_INCLUDE_DIRS}")
+ set(GLEW_LIBRARY ${GLEW_LDFLAGS})
+else()
+ if (WIN32 AND MSVC)
+ if (WIN32_STATIC_BUILD) # passed from caller
+ set(GLEW_LIB_SEARCH_NAME glew32s.lib) # static, non-debug (Release)
+ else ()
+ set(GLEW_LIB_SEARCH_NAME glew32.lib) # other. untested with OpenSCAD
+ endif()
+ else () # GCC
+ set(GLEW_LIB_SEARCH_NAME "libglew32s.a")
+ endif ()
-IF (GLEW_INCLUDE_PATH)
- SET( GLEW_FOUND 1 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
- MESSAGE(STATUS "GLEW include found in " ${GLEW_INCLUDE_PATH} )
- MESSAGE(STATUS "GLEW library found in " ${GLEW_LIBRARY} )
-ELSE (GLEW_INCLUDE_PATH)
- SET( GLEW_FOUND 0 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
-ENDIF (GLEW_INCLUDE_PATH)
+ if (WIN32)
+ find_path(GLEW_INCLUDE_DIR GL/glew.h
+ $ENV{PROGRAMFILES}/GLEW/include
+ ${PROJECT_SOURCE_DIR}/src/nvgl/glew/include
+ DOC "The directory where GL/glew.h resides")
+ find_library(GLEW_LIBRARY
+ NAMES ${GLEW_LIB_SEARCH_NAME}
+ PATHS
+ $ENV{PROGRAMFILES}/GLEW/lib
+ ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin
+ ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib
+ DOC "The GLEW library")
+ else (WIN32)
+ find_path(GLEW_INCLUDE_DIR GL/glew.h
+ PATHS /usr/include /usr/local/include /usr/pkg/include
+ NO_DEFAULT_PATH
+ DOC "The directory where GL/glew.h resides")
+ find_library(GLEW_LIBRARY
+ NAMES GLEW glew
+ PATHS /usr/lib /usr/local/lib /usr/pkg/lib
+ NO_DEFAULT_PATH
+ DOC "The GLEW library")
+ endif (WIN32)
+
+ if (GLEW_INCLUDE_DIR)
+ set(GLEW_FOUND 1 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
+ message(STATUS "GLEW include found in " ${GLEW_INCLUDE_DIR} )
+ message(STATUS "GLEW library found in " ${GLEW_LIBRARY} )
+ else()
+ set(GLEW_FOUND 0 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
+ endif()
+endif()
diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc
index 5675ef5..18e9efa 100644
--- a/tests/cgalcachetest.cc
+++ b/tests/cgalcachetest.cc
@@ -152,7 +152,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc
index 650c6d7..7c9684a 100644
--- a/tests/cgalpngtest.cc
+++ b/tests/cgalpngtest.cc
@@ -125,7 +125,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc
index 0bf7ac2..d0d0077 100644
--- a/tests/cgalstlsanitytest.cc
+++ b/tests/cgalstlsanitytest.cc
@@ -111,7 +111,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc
index 076983f..98617a3 100644
--- a/tests/cgaltest.cc
+++ b/tests/cgaltest.cc
@@ -104,7 +104,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/csgtermtest.cc b/tests/csgtermtest.cc
index 89a9bdc..1bd2468 100644
--- a/tests/csgtermtest.cc
+++ b/tests/csgtermtest.cc
@@ -91,7 +91,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc
index b9f0d27..cb96940 100644
--- a/tests/csgtestcore.cc
+++ b/tests/csgtestcore.cc
@@ -276,7 +276,9 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type)
}
if (!sysinfo_dump) {
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
}
AbstractNode::resetIndexCounter();
diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc
index 67e3416..6e18f20 100644
--- a/tests/csgtexttest.cc
+++ b/tests/csgtexttest.cc
@@ -95,7 +95,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/dumptest.cc b/tests/dumptest.cc
index dbf74ab..f923a64 100644
--- a/tests/dumptest.cc
+++ b/tests/dumptest.cc
@@ -101,7 +101,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/echotest.cc b/tests/echotest.cc
index b08e81a..d731ee3 100644
--- a/tests/echotest.cc
+++ b/tests/echotest.cc
@@ -103,7 +103,9 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(fs::path(filename).parent_path());
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/tests/modulecachetest.cc b/tests/modulecachetest.cc
new file mode 100644
index 0000000..2ef7a6c
--- /dev/null
+++ b/tests/modulecachetest.cc
@@ -0,0 +1,123 @@
+/*
+ * 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 "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+
+#include <QCoreApplication>
+#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;
+QString examplesdir;
+
+using std::string;
+
+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();
+
+ QCoreApplication app(argc, argv);
+ fs::path original_path = fs::current_path();
+
+ currentdir = boosty::stringy( fs::current_path() );
+
+ parser_init(QCoreApplication::instance()->applicationDirPath().toStdString());
+ set_librarydir(boosty::stringy(fs::path(QCoreApplication::instance()->applicationDirPath().toStdString()) / "../libraries"));
+
+ Context root_ctx;
+ register_builtin(root_ctx);
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+ AbstractNode *root_node;
+
+ root_module = parsefile(filename);
+ if (!root_module) {
+ fprintf(stderr, "Error: Unable to parse input file\n");
+ exit(1);
+ }
+
+ if (fs::path(filename).has_parent_path()) {
+ fs::current_path(fs::path(filename).parent_path());
+ }
+
+ AbstractNode::resetIndexCounter();
+ root_node = root_module->evaluate(&root_ctx, &root_inst);
+
+ delete root_node;
+ delete root_module;
+
+ fs::current_path(original_path);
+
+ fprintf(stderr, "Second parse\n");
+ root_module = parsefile(filename);
+ if (!root_module) {
+ fprintf(stderr, "Error: Unable to parse second time\n");
+ exit(1);
+ }
+
+ AbstractNode::resetIndexCounter();
+ root_node = root_module->evaluate(&root_ctx, &root_inst);
+
+ delete root_node;
+ delete root_module;
+
+ Builtins::instance(true);
+
+ return rc;
+}
diff --git a/tests/regression/cgalpngtest/example023-expected.png b/tests/regression/cgalpngtest/example023-expected.png
new file mode 100644
index 0000000..c528b90
--- /dev/null
+++ b/tests/regression/cgalpngtest/example023-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png
index 800f7ba..31890b9 100644
--- a/tests/regression/cgalpngtest/projection-tests-expected.png
+++ b/tests/regression/cgalpngtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/scale2D-tests-expected.png b/tests/regression/cgalpngtest/scale2D-tests-expected.png
new file mode 100644
index 0000000..c23b7a0
--- /dev/null
+++ b/tests/regression/cgalpngtest/scale2D-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/scale3D-tests-expected.png b/tests/regression/cgalpngtest/scale3D-tests-expected.png
new file mode 100644
index 0000000..cbd8227
--- /dev/null
+++ b/tests/regression/cgalpngtest/scale3D-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/text-search-test-expected.png b/tests/regression/cgalpngtest/text-search-test-expected.png
new file mode 100644
index 0000000..76e7087
--- /dev/null
+++ b/tests/regression/cgalpngtest/text-search-test-expected.png
Binary files differ
diff --git a/tests/regression/dumptest/scale2D-tests-expected.txt b/tests/regression/dumptest/scale2D-tests-expected.txt
new file mode 100644
index 0000000..aa1eca2
--- /dev/null
+++ b/tests/regression/dumptest/scale2D-tests-expected.txt
@@ -0,0 +1,34 @@
+ multmatrix([[2, 0, 0, 0], [0, 1.33333, 0, 0], [0, 0, 2, 0], [0, 0, 0, 1]]) {
+ group() {
+ square(size = [2, 3], center = true);
+ }
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[2, 0, 0, 0], [0, 1.33333, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ square(size = [2, 3], center = true);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 1]]) {
+ group() {
+ square(size = [2, 3], center = true);
+ }
+ }
+ }
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ multmatrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ square(size = [2, 3], center = true);
+ }
+ }
+ }
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ multmatrix([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ square(size = [2, 3], center = true);
+ }
+ }
+ }
+
diff --git a/tests/regression/dumptest/scale3D-tests-expected.txt b/tests/regression/dumptest/scale3D-tests-expected.txt
new file mode 100644
index 0000000..c06b2fe
--- /dev/null
+++ b/tests/regression/dumptest/scale3D-tests-expected.txt
@@ -0,0 +1,43 @@
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[1, 0, 0, 0], [0, 2, 0, 0], [0, 0, 3, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[2, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ multmatrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ multmatrix([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]) {
+ group() {
+ cylinder($fn = 8, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = true);
+ }
+ }
+ }
+
diff --git a/tests/regression/dumptest/text-search-test-expected.txt b/tests/regression/dumptest/text-search-test-expected.txt
new file mode 100644
index 0000000..f0c8aad
--- /dev/null
+++ b/tests/regression/dumptest/text-search-test-expected.txt
@@ -0,0 +1,848 @@
+ group();
+ group() {
+ multmatrix([[1, 0, 0, -60], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 2], [6, 2], [6, 1], [3, 2], [3, 6], [5, 6], [5, 2]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -52], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 0], [1, 6], [6, 6], [6, 5], [7, 5], [7, 3], [6, 3], [6, 2], [3, 2], [3, 0], [3, 3], [3, 5], [5, 5], [5, 3]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 5], [2, 5], [2, 6], [6, 6], [6, 5], [7, 5], [7, 3], [3, 3], [3, 2], [6, 2], [6, 1], [3, 4], [3, 5], [5, 5], [5, 4]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [14, 15, 16, 17]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -36], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 6], [6, 6], [6, 5], [7, 5], [7, 1], [5, 1], [5, 5], [3, 5], [3, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -28], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [5, 2], [5, 4], [2, 4], [2, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [3, 6], [3, 5], [6, 5], [6, 4], [7, 4], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [5, 5], [5, 6], [3, 6], [3, 2], [5, 2], [5, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -12], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 6], [6, 6], [6, 5], [7, 5], [7, 1], [5, 1], [5, 2], [3, 2], [3, 1], [3, 3], [3, 5], [5, 5], [5, 3]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, -4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 7], [5, 7], [5, 6], [6, 6], [6, 5], [7, 5], [7, 3], [6, 3], [6, 2], [5, 2], [5, 1], [3, 2], [3, 6], [4, 6], [4, 5], [5, 5], [5, 3], [4, 3], [4, 2]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = undef, paths = undef, convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 12], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 7], [6, 7], [6, 6], [7, 6], [7, 4], [6, 4], [6, 2], [7, 2], [7, 1], [5, 1], [5, 2], [4, 2], [4, 3], [3, 3], [3, 1], [3, 4], [3, 6], [5, 6], [5, 4]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 5], [2, 5], [2, 6], [6, 6], [6, 5], [7, 5], [7, 2], [6, 2], [6, 1], [3, 2], [3, 5], [5, 5], [5, 2]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 28], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 5], [2, 5], [2, 6], [6, 6], [6, 5], [3, 5], [3, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 36], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 7], [3, 7], [3, 4], [4, 4], [4, 5], [6, 5], [6, 4], [5, 4], [5, 3], [6, 3], [6, 2], [7, 2], [7, 1], [5, 1], [5, 2], [4, 2], [4, 3], [3, 3], [3, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[1, 1], [1, 2], [5, 2], [5, 3], [2, 3], [2, 4], [1, 4], [1, 5], [2, 5], [2, 6], [7, 6], [7, 5], [3, 5], [3, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 52], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[3, 1], [3, 2], [5, 2], [5, 1], [3, 3], [3, 7], [5, 7], [5, 3]], paths = [[0, 1, 2, 3], [4, 5, 6, 7]], convexity = 1);
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -24], [0, 1, 0, -8], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, -16], [0, 1, 0, -8], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, -8], [0, 1, 0, -8], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 3], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 7], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ hull() {
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 8, $fa = 12, $fs = 2, r = 0.125);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -16], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0.25], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 1.53081e-17], [0, 1, 0, 0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.25], [0, 1, 0, 3.06162e-17], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -4.59243e-17], [0, 1, 0, -0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [3, 2], [3, 5], [2, 5], [2, 6], [3, 6], [3, 7], [5, 7], [5, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], convexity = 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 8], [0, 1, 0, -16], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0.25], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 1.53081e-17], [0, 1, 0, 0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.25], [0, 1, 0, 3.06162e-17], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -4.59243e-17], [0, 1, 0, -0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[1, 1], [1, 2], [2, 2], [2, 3], [3, 3], [3, 4], [4, 4], [4, 5], [5, 5], [5, 6], [3, 6], [3, 5], [1, 5], [1, 6], [2, 6], [2, 7], [6, 7], [6, 6], [7, 6], [7, 5], [6, 5], [6, 4], [5, 4], [5, 3], [4, 3], [4, 2], [3, 2], [7, 2], [7, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]], convexity = 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 16], [0, 1, 0, -16], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ group() {
+ union() {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ group() {
+ group() {
+ multmatrix([[1, 0, 0, 0.25], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 1.53081e-17], [0, 1, 0, 0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, 0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.25], [0, 1, 0, 3.06162e-17], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, -4.59243e-17], [0, 1, 0, -0.25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ group() {
+ multmatrix([[1, 0, 0, 0.176777], [0, 1, 0, -0.176777], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ polygon(points = [[2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 2], [5, 2], [5, 3], [4, 3], [4, 4], [3, 4], [3, 5], [4, 5], [4, 6], [1, 6], [1, 7], [7, 7], [7, 6], [6, 6], [6, 5], [5, 5], [5, 4], [6, 4], [6, 3], [7, 3], [7, 2], [6, 2], [6, 1]], paths = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]], convexity = 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
diff --git a/tests/regression/echotest/search-tests-expected.txt b/tests/regression/echotest/search-tests-expected.txt
new file mode 100644
index 0000000..64df0b6
--- /dev/null
+++ b/tests/regression/echotest/search-tests-expected.txt
@@ -0,0 +1,20 @@
+ search term not found: "q"
+ search term not found: 1000
+ search term not found: "zzz"
+ search term not found: "zzz"
+ search term not found: 500
+ search term not found: ""
+ECHO: "Characters in string (\"a\"): [0]"
+ECHO: "Characters in string (\"adeq\"): [[0, 5], [3, 8], [4], []]"
+ECHO: "Default string search (\"abe\"): [0, 1, 8]"
+ECHO: "Return all matches for string search (\"abe\"): [[0, 4, 9, 10], [1, 5], [8]]"
+ECHO: "Return up to 2 matches for string search (\"abe\"): [[0, 4], [1, 5], [8]]"
+ECHO: "Return up to 2 matches for string search; alternate columns (\"aebe\"): [[0, 4], [8], [1, 5], [8]]"
+ECHO: "Default number search (7): [5]"
+ECHO: "Return all matches for number search (1): [0, 4, 10]"
+ECHO: "Return up to 2 matches for number search (1): [0, 4]"
+ECHO: "Default list number search ([1, 3, 1000]): [0, 1, []]"
+ECHO: "Default list string search ([\"b\", \"zzz\", \"a\", \"c\", \"apple\", \"dog\"]): [1, [], 4, 2, 9, 3]"
+ECHO: "Default list mixed search ([\"b\", 4, \"zzz\", \"c\", \"apple\", 500, \"a\", \"\"]): [1, 3, [], 2, 9, [], 4, []]"
+ECHO: "Return all matches for mixed search ([\"b\", 4, \"zzz\", \"c\", \"apple\", 500, \"a\", \"\"]): [[1, 5], [3], [], [2, 6], [9], [], [4, 10], []]"
+ECHO: "Return all matches for mixed search; alternate columns ([1, \"zz\", \"dog\", 500, 11]): [[0], [], [3], [], [10]]"
diff --git a/tests/regression/echotest/vector-values-expected.txt b/tests/regression/echotest/vector-values-expected.txt
new file mode 100644
index 0000000..7654892
--- /dev/null
+++ b/tests/regression/echotest/vector-values-expected.txt
@@ -0,0 +1,10 @@
+ECHO: "Testing vector dot product: 14"
+ECHO: " Bounds check: undef"
+ECHO: "Testing matrix * vector: [3, 2]"
+ECHO: " Bounds check: undef"
+ECHO: "Testing vector * matrix: [2, -1, 0]"
+ECHO: " Bounds check: undef"
+ECHO: "Testing id matrix * id matrix: [[1, 0], [0, 1]]"
+ECHO: "Testing asymmetric matrix * matrix: [[2, 1], [-1, 0]]"
+ECHO: "Testing alternate asymmetric matrix * matrix: [[1, 0, 1], [0, 1, -1], [1, 1, 0]]"
+ECHO: " Bounds check: undef"
diff --git a/tests/regression/opencsgtest/example023-expected.png b/tests/regression/opencsgtest/example023-expected.png
new file mode 100644
index 0000000..a097d99
--- /dev/null
+++ b/tests/regression/opencsgtest/example023-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png
index 8239d3d..98e12e2 100644
--- a/tests/regression/opencsgtest/projection-tests-expected.png
+++ b/tests/regression/opencsgtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/scale2D-tests-expected.png b/tests/regression/opencsgtest/scale2D-tests-expected.png
new file mode 100644
index 0000000..515265b
--- /dev/null
+++ b/tests/regression/opencsgtest/scale2D-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/scale3D-tests-expected.png b/tests/regression/opencsgtest/scale3D-tests-expected.png
new file mode 100644
index 0000000..7401b99
--- /dev/null
+++ b/tests/regression/opencsgtest/scale3D-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/text-search-test-expected.png b/tests/regression/opencsgtest/text-search-test-expected.png
new file mode 100644
index 0000000..eadd61e
--- /dev/null
+++ b/tests/regression/opencsgtest/text-search-test-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/example023-expected.png b/tests/regression/throwntogethertest/example023-expected.png
new file mode 100644
index 0000000..a097d99
--- /dev/null
+++ b/tests/regression/throwntogethertest/example023-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png
index 7bcb888..08c2998 100644
--- a/tests/regression/throwntogethertest/projection-tests-expected.png
+++ b/tests/regression/throwntogethertest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/scale2D-tests-expected.png b/tests/regression/throwntogethertest/scale2D-tests-expected.png
new file mode 100644
index 0000000..515265b
--- /dev/null
+++ b/tests/regression/throwntogethertest/scale2D-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/scale3D-tests-expected.png b/tests/regression/throwntogethertest/scale3D-tests-expected.png
new file mode 100644
index 0000000..7401b99
--- /dev/null
+++ b/tests/regression/throwntogethertest/scale3D-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/text-search-test-expected.png b/tests/regression/throwntogethertest/text-search-test-expected.png
new file mode 100644
index 0000000..eadd61e
--- /dev/null
+++ b/tests/regression/throwntogethertest/text-search-test-expected.png
Binary files differ
diff --git a/tests/tests-common.cc b/tests/tests-common.cc
index 1694a74..5b0cc3b 100644
--- a/tests/tests-common.cc
+++ b/tests/tests-common.cc
@@ -7,9 +7,9 @@
#include <QFileInfo>
#include <sstream>
-AbstractModule *parsefile(const char *filename)
+Module *parsefile(const char *filename)
{
- AbstractModule *root_module = NULL;
+ Module *root_module = NULL;
QFileInfo fileInfo(filename);
handle_dep(filename);
@@ -27,6 +27,9 @@ AbstractModule *parsefile(const char *filename)
fclose(fp);
text << "\n" << commandline_commands;
root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (root_module) {
+ root_module->handleDependencies();
+ }
}
return root_module;
}
diff --git a/tests/tests-common.h b/tests/tests-common.h
index 92ebc78..0047562 100644
--- a/tests/tests-common.h
+++ b/tests/tests-common.h
@@ -1,6 +1,6 @@
#ifndef TESTS_COMMON_H_
#define TESTS_COMMON_H_
-class AbstractModule *parsefile(const char *filename);
+class Module *parsefile(const char *filename);
#endif
contact: Jan Huwald // Impressum