diff options
author | Don Bright <hugh.m.bright@gmail.com> | 2012-05-28 16:48:46 (GMT) |
---|---|---|
committer | Don Bright <hugh.m.bright@gmail.com> | 2012-05-28 16:48:46 (GMT) |
commit | dd2002a81673b3875ce8c4e8a61cb10278c4eb03 (patch) | |
tree | 7aaadf1c9b12cd37a7a913d3e76256f6406fa939 | |
parent | 4381762f5aa2e6a56258618e585e1510ead88684 (diff) | |
parent | 67eb2ebe90447e966dc1e08b91c43d937c521583 (diff) |
Tidy up code. Generate proper test png images. Merge branch 'master' into caliston1.
Conflicts:
src/PolySetCGALEvaluator.cc
80 files changed, 2635 insertions, 728 deletions
@@ -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 @@ -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 Binary files differnew file mode 100644 index 0000000..c528b90 --- /dev/null +++ b/tests/regression/cgalpngtest/example023-expected.png diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png Binary files differindex 800f7ba..31890b9 100644 --- a/tests/regression/cgalpngtest/projection-tests-expected.png +++ b/tests/regression/cgalpngtest/projection-tests-expected.png diff --git a/tests/regression/cgalpngtest/scale2D-tests-expected.png b/tests/regression/cgalpngtest/scale2D-tests-expected.png Binary files differnew file mode 100644 index 0000000..c23b7a0 --- /dev/null +++ b/tests/regression/cgalpngtest/scale2D-tests-expected.png diff --git a/tests/regression/cgalpngtest/scale3D-tests-expected.png b/tests/regression/cgalpngtest/scale3D-tests-expected.png Binary files differnew file mode 100644 index 0000000..cbd8227 --- /dev/null +++ b/tests/regression/cgalpngtest/scale3D-tests-expected.png diff --git a/tests/regression/cgalpngtest/text-search-test-expected.png b/tests/regression/cgalpngtest/text-search-test-expected.png Binary files differnew file mode 100644 index 0000000..76e7087 --- /dev/null +++ b/tests/regression/cgalpngtest/text-search-test-expected.png 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 Binary files differnew file mode 100644 index 0000000..a097d99 --- /dev/null +++ b/tests/regression/opencsgtest/example023-expected.png diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png Binary files differindex 8239d3d..98e12e2 100644 --- a/tests/regression/opencsgtest/projection-tests-expected.png +++ b/tests/regression/opencsgtest/projection-tests-expected.png diff --git a/tests/regression/opencsgtest/scale2D-tests-expected.png b/tests/regression/opencsgtest/scale2D-tests-expected.png Binary files differnew file mode 100644 index 0000000..515265b --- /dev/null +++ b/tests/regression/opencsgtest/scale2D-tests-expected.png diff --git a/tests/regression/opencsgtest/scale3D-tests-expected.png b/tests/regression/opencsgtest/scale3D-tests-expected.png Binary files differnew file mode 100644 index 0000000..7401b99 --- /dev/null +++ b/tests/regression/opencsgtest/scale3D-tests-expected.png diff --git a/tests/regression/opencsgtest/text-search-test-expected.png b/tests/regression/opencsgtest/text-search-test-expected.png Binary files differnew file mode 100644 index 0000000..eadd61e --- /dev/null +++ b/tests/regression/opencsgtest/text-search-test-expected.png diff --git a/tests/regression/throwntogethertest/example023-expected.png b/tests/regression/throwntogethertest/example023-expected.png Binary files differnew file mode 100644 index 0000000..a097d99 --- /dev/null +++ b/tests/regression/throwntogethertest/example023-expected.png diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png Binary files differindex 7bcb888..08c2998 100644 --- a/tests/regression/throwntogethertest/projection-tests-expected.png +++ b/tests/regression/throwntogethertest/projection-tests-expected.png diff --git a/tests/regression/throwntogethertest/scale2D-tests-expected.png b/tests/regression/throwntogethertest/scale2D-tests-expected.png Binary files differnew file mode 100644 index 0000000..515265b --- /dev/null +++ b/tests/regression/throwntogethertest/scale2D-tests-expected.png diff --git a/tests/regression/throwntogethertest/scale3D-tests-expected.png b/tests/regression/throwntogethertest/scale3D-tests-expected.png Binary files differnew file mode 100644 index 0000000..7401b99 --- /dev/null +++ b/tests/regression/throwntogethertest/scale3D-tests-expected.png diff --git a/tests/regression/throwntogethertest/text-search-test-expected.png b/tests/regression/throwntogethertest/text-search-test-expected.png Binary files differnew file mode 100644 index 0000000..eadd61e --- /dev/null +++ b/tests/regression/throwntogethertest/text-search-test-expected.png 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 |