summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--RELEASE_NOTES13
-rw-r--r--boost.pri36
-rw-r--r--doc/OpenSCAD-classes.grafflebin5157 -> 5262 bytes
-rw-r--r--doc/OpenSCAD-classes.pdfbin121443 -> 207883 bytes
-rw-r--r--doc/OpenSCAD-compile.grafflebin4883 -> 4873 bytes
-rw-r--r--doc/OpenSCAD-compile.pdfbin137981 -> 150244 bytes
-rw-r--r--doc/OpenSCAD-csg.grafflebin0 -> 2662 bytes
-rw-r--r--doc/OpenSCAD-csg.pdfbin0 -> 58300 bytes
-rw-r--r--doc/OpenSCAD-polygons.grafflebin5035 -> 5047 bytes
-rw-r--r--doc/OpenSCAD-polygons.pdfbin59224 -> 114655 bytes
-rw-r--r--doc/TODO.txt36
-rw-r--r--doc/testing.txt29
-rw-r--r--doc/visitor-changes.txt7
m---------libraries/MCAD0
-rw-r--r--libraries/boxes.scad43
-rw-r--r--libraries/shapes.scad148
-rw-r--r--mjau.gdb2
-rw-r--r--openscad.pro58
-rwxr-xr-xscripts/macosx-build-dependencies.sh4
-rwxr-xr-xscripts/release-common.sh18
-rwxr-xr-xscripts/release-linux.sh6
-rw-r--r--src/CGALEvaluator.cc611
-rw-r--r--src/CGALEvaluator.h55
-rw-r--r--src/CGALRenderer.cc (renamed from src/cgalrenderer.cc)45
-rw-r--r--src/CGALRenderer.h (renamed from src/cgalrenderer.h)4
-rw-r--r--src/CGAL_Nef_polyhedron.cc81
-rw-r--r--src/CGAL_Nef_polyhedron.h34
-rw-r--r--src/CGAL_Nef_polyhedron_DxfData.cc (renamed from src/nef2dxf.cc)22
-rw-r--r--src/CSGTermEvaluator.cc230
-rw-r--r--src/CSGTermEvaluator.h56
-rw-r--r--src/GLView.h2
-rw-r--r--src/MainWindow.h23
-rw-r--r--src/MainWindow.ui2
-rw-r--r--src/OpenCSGRenderer.cc42
-rw-r--r--src/PolySetCGALEvaluator.cc484
-rw-r--r--src/PolySetCGALEvaluator.h29
-rw-r--r--src/PolySetEvaluator.cc15
-rw-r--r--src/PolySetEvaluator.h41
-rw-r--r--src/ThrownTogetherRenderer.cc7
-rw-r--r--src/Tree.cc30
-rw-r--r--src/Tree.h31
-rw-r--r--src/builtin.h8
-rw-r--r--src/cgal.h81
-rw-r--r--src/cgaladv.cc210
-rw-r--r--src/cgaladv_convexhull2.cc9
-rw-r--r--src/cgaladv_minkowski2.cc3
-rw-r--r--src/cgaladv_minkowski3.cc42
-rw-r--r--src/cgaladvnode.h34
-rw-r--r--src/cgalfwd.h45
-rw-r--r--src/cgalutils.cc147
-rw-r--r--src/cgalutils.h9
-rw-r--r--src/color.cc108
-rw-r--r--src/colornode.h20
-rw-r--r--src/context.cc182
-rw-r--r--src/context.h56
-rw-r--r--src/control.cc74
-rw-r--r--src/csgnode.h25
-rw-r--r--src/csgops.cc144
-rw-r--r--src/csgterm.cc105
-rw-r--r--src/csgterm.h31
-rw-r--r--src/dxfdata.cc269
-rw-r--r--src/dxfdata.h31
-rw-r--r--src/dxfdim.cc81
-rw-r--r--src/dxfdim.h6
-rw-r--r--src/dxflinextrude.cc265
-rw-r--r--src/dxflinextrudenode.h30
-rw-r--r--src/dxfrotextrude.cc188
-rw-r--r--src/dxfrotextrudenode.h28
-rw-r--r--src/dxftess-cgal.cc26
-rw-r--r--src/dxftess-glu.cc46
-rw-r--r--src/dxftess.cc20
-rw-r--r--src/dxftess.h4
-rw-r--r--src/export.cc162
-rw-r--r--src/export.h11
-rw-r--r--src/expr.cc225
-rw-r--r--src/expression.h18
-rw-r--r--src/func.cc121
-rw-r--r--src/function.h21
-rw-r--r--src/glview.cc45
-rw-r--r--src/grid.h2
-rw-r--r--src/handle_dep.cc42
-rw-r--r--src/handle_dep.h10
-rw-r--r--src/import.cc169
-rw-r--r--src/importnode.h33
-rw-r--r--src/lexer.l11
-rw-r--r--src/mainwin.cc442
-rw-r--r--src/module.cc166
-rw-r--r--src/module.h53
-rw-r--r--src/myqhash.h16
-rw-r--r--src/namedcolors.cpp155
-rw-r--r--src/node.cc183
-rw-r--r--src/node.h88
-rw-r--r--src/nodecache.h46
-rw-r--r--src/nodedumper.cc103
-rw-r--r--src/nodedumper.h40
-rw-r--r--src/openscad.cc120
-rw-r--r--src/openscad.h7
-rw-r--r--src/parser.y203
-rw-r--r--src/polyset.cc539
-rw-r--r--src/polyset.h52
-rw-r--r--src/primitives.cc286
-rw-r--r--src/printutils.cc8
-rw-r--r--src/printutils.h5
-rw-r--r--src/projection.cc239
-rw-r--r--src/projectionnode.h25
-rw-r--r--src/qhash.cc19
-rw-r--r--src/render-opencsg.cc.org87
-rw-r--r--src/render.cc209
-rw-r--r--src/rendernode.h21
-rw-r--r--src/state.h41
-rw-r--r--src/stl-utils.h19
-rw-r--r--src/surface.cc83
-rw-r--r--src/transform.cc332
-rw-r--r--src/transformnode.h20
-rw-r--r--src/traverser.cc44
-rw-r--r--src/traverser.h26
-rw-r--r--src/value.cc133
-rw-r--r--src/value.h16
-rw-r--r--src/visitor.h55
-rwxr-xr-xtest-code/batch-cgal-tests.sh9
-rwxr-xr-xtest-code/batch-cgal.sh14
-rwxr-xr-xtest-code/batch-dump.sh16
-rw-r--r--test-code/exportdxf.cc (renamed from test-code/dumptest.cc)54
-rw-r--r--test-code/exportdxf.pro (renamed from test-code/dumptest.pro)71
-rw-r--r--testdata/dxf/multiple-layers.dxf2140
-rw-r--r--testdata/scad/bugs/gridbug.scad9
-rw-r--r--testdata/scad/bugs/polygon-illegal-winding.scad (renamed from testdata/scad/polygon-illegal-winding.scad)0
-rw-r--r--testdata/scad/bugs/polygon-touch.scad5
-rw-r--r--testdata/scad/bugs/polyset-reduce-crash.scad (renamed from testdata/scad/polyset-reduce-crash.scad)0
-rw-r--r--testdata/scad/bugs/polyset-reduce-crash.txt (renamed from testdata/scad/polyset-reduce-crash.txt)0
-rw-r--r--testdata/scad/difference-tests.scad4
-rw-r--r--testdata/scad/features/2d-3d.scad3
-rw-r--r--testdata/scad/features/assign-tests.scad6
-rw-r--r--testdata/scad/features/background-modifier.scad4
-rw-r--r--testdata/scad/features/circle-tests.scad10
-rw-r--r--testdata/scad/features/color-tests.scad15
-rw-r--r--testdata/scad/features/cube-tests.scad4
-rw-r--r--testdata/scad/features/cylinder-tests.scad15
-rw-r--r--testdata/scad/features/difference-tests.scad30
-rw-r--r--testdata/scad/features/dim-all.dxf (renamed from testdata/scad/dim-all.dxf)0
-rw-r--r--testdata/scad/features/dim-all.scad (renamed from testdata/scad/dim-all.scad)0
-rw-r--r--testdata/scad/features/disable-modifier.scad4
-rw-r--r--testdata/scad/features/dxf-export.scad12
-rw-r--r--testdata/scad/features/for-nested-tests.scad3
-rw-r--r--testdata/scad/features/for-tests.scad41
-rw-r--r--testdata/scad/features/highlight-modifier.scad4
-rw-r--r--testdata/scad/features/hull2-tests.scad30
-rw-r--r--testdata/scad/features/hull3-tests.scad (renamed from testdata/scad/convex_hull.scad)28
-rw-r--r--testdata/scad/features/import.stl (renamed from testdata/scad/import.stl)0
-rw-r--r--testdata/scad/features/import_dxf-tests.scad9
-rw-r--r--testdata/scad/features/import_stl-tests.scad (renamed from testdata/scad/import_stl-tests.scad)0
-rw-r--r--testdata/scad/features/include test6.scad7
-rw-r--r--testdata/scad/features/include-test5.scad4
-rw-r--r--testdata/scad/features/include-tests.scad (renamed from testdata/scad/include-test.scad)10
-rw-r--r--testdata/scad/features/intersection-tests.scad41
-rw-r--r--testdata/scad/features/intersection_for-tests.scad10
-rw-r--r--testdata/scad/features/linear_extrude-tests.scad13
-rw-r--r--testdata/scad/features/minkowski2-tests.scad31
-rw-r--r--testdata/scad/features/minkowski3-tests.scad35
-rw-r--r--testdata/scad/features/null-polygons.dxf (renamed from testdata/scad/null-polygons.dxf)0
-rw-r--r--testdata/scad/features/null-polygons.scad2
-rw-r--r--testdata/scad/features/polygon-tests.scad17
-rw-r--r--testdata/scad/features/projection-tests.scad (renamed from testdata/scad/projection-tests.scad)7
-rw-r--r--testdata/scad/features/render-tests.scad36
-rw-r--r--testdata/scad/features/root-modifier.scad4
-rw-r--r--testdata/scad/features/rotate_extrude-tests.scad30
-rw-r--r--testdata/scad/features/sphere-tests.scad10
-rw-r--r--testdata/scad/features/square-tests.scad8
-rw-r--r--testdata/scad/features/string-test.scad (renamed from testdata/scad/string-test.scad)0
-rw-r--r--testdata/scad/features/sub1/sub2/sub3/include-test4.scad4
-rw-r--r--testdata/scad/features/sub1/sub2/sub3/sub4/include-test2.scad (renamed from testdata/scad/sub1/sub2/sub3/sub4/include-test2.scad)2
-rw-r--r--testdata/scad/features/sub1/sub2/sub3/sub4/include-test3.scad4
-rw-r--r--testdata/scad/features/surface-tests.scad2
-rw-r--r--testdata/scad/features/surface.dat (renamed from testdata/scad/surface.dat)0
-rw-r--r--testdata/scad/features/testcolornames.scad (renamed from testdata/scad/testcolornames.scad)0
-rw-r--r--testdata/scad/features/transform-tests.scad18
-rw-r--r--testdata/scad/features/union-tests.scad29
-rw-r--r--testdata/scad/include test6.scad4
-rw-r--r--testdata/scad/include-test5.scad4
-rw-r--r--testdata/scad/intersection-tests.scad4
-rw-r--r--testdata/scad/linear_extrude-tests.scad7
-rw-r--r--testdata/scad/minimal/allfunctions.scad24
-rw-r--r--testdata/scad/minimal/allmodules.scad (renamed from testdata/scad/allmodules.scad)2
-rw-r--r--testdata/scad/minimal/assign.scad (renamed from testdata/scad/assign.scad)0
-rw-r--r--testdata/scad/minimal/child.scad (renamed from testdata/scad/child.scad)0
-rw-r--r--testdata/scad/minimal/circle.scad (renamed from testdata/scad/circle.scad)0
-rw-r--r--testdata/scad/minimal/color.scad (renamed from testdata/scad/color.scad)0
-rw-r--r--testdata/scad/minimal/cube.scad (renamed from testdata/scad/cube.scad)0
-rw-r--r--testdata/scad/minimal/cylinder.scad (renamed from testdata/scad/cylinder.scad)0
-rw-r--r--testdata/scad/minimal/difference.scad (renamed from testdata/scad/difference.scad)0
-rw-r--r--testdata/scad/minimal/dxf_linear_extrude.scad (renamed from testdata/scad/dxf_linear_extrude.scad)0
-rw-r--r--testdata/scad/minimal/dxf_rotate_extrude.scad (renamed from testdata/scad/dxf_rotate_extrude.scad)0
-rw-r--r--testdata/scad/minimal/echo.scad (renamed from testdata/scad/echo.scad)0
-rw-r--r--testdata/scad/minimal/for.scad (renamed from testdata/scad/for.scad)0
-rw-r--r--testdata/scad/minimal/glide.scad (renamed from testdata/scad/glide.scad)0
-rw-r--r--testdata/scad/minimal/group.scad (renamed from testdata/scad/group.scad)0
-rw-r--r--testdata/scad/minimal/hull.scad1
-rw-r--r--testdata/scad/minimal/if.scad (renamed from testdata/scad/if.scad)0
-rw-r--r--testdata/scad/minimal/import.scad1
-rw-r--r--testdata/scad/minimal/import_dxf.scad (renamed from testdata/scad/import_dxf.scad)0
-rw-r--r--testdata/scad/minimal/import_off.scad (renamed from testdata/scad/import_off.scad)0
-rw-r--r--testdata/scad/minimal/import_stl.scad (renamed from testdata/scad/import_stl.scad)0
-rw-r--r--testdata/scad/minimal/intersection.scad (renamed from testdata/scad/intersection.scad)0
-rw-r--r--testdata/scad/minimal/intersection_for.scad (renamed from testdata/scad/intersection_for.scad)0
-rw-r--r--testdata/scad/minimal/linear_extrude.scad (renamed from testdata/scad/linear_extrude.scad)0
-rw-r--r--testdata/scad/minimal/minkowski.scad1
-rw-r--r--testdata/scad/minimal/mirror.scad (renamed from testdata/scad/mirror.scad)0
-rw-r--r--testdata/scad/minimal/multmatrix.scad (renamed from testdata/scad/multmatrix.scad)0
-rw-r--r--testdata/scad/minimal/polygon.scad (renamed from testdata/scad/polygon.scad)0
-rw-r--r--testdata/scad/minimal/polyhedron.scad (renamed from testdata/scad/polyhedron.scad)0
-rw-r--r--testdata/scad/minimal/projection.scad (renamed from testdata/scad/projection.scad)0
-rw-r--r--testdata/scad/minimal/render.scad (renamed from testdata/scad/render.scad)0
-rw-r--r--testdata/scad/minimal/rotate.scad (renamed from testdata/scad/rotate.scad)0
-rw-r--r--testdata/scad/minimal/rotate_extrude.scad (renamed from testdata/scad/rotate_extrude.scad)0
-rw-r--r--testdata/scad/minimal/scale.scad (renamed from testdata/scad/scale.scad)0
-rw-r--r--testdata/scad/minimal/sphere.scad (renamed from testdata/scad/sphere.scad)0
-rw-r--r--testdata/scad/minimal/square.scad (renamed from testdata/scad/square.scad)0
-rw-r--r--testdata/scad/minimal/subdiv.scad (renamed from testdata/scad/subdiv.scad)0
-rw-r--r--testdata/scad/minimal/surface.scad (renamed from testdata/scad/surface.scad)0
-rw-r--r--testdata/scad/minimal/transform-insert.dxf (renamed from testdata/scad/transform-insert.dxf)0
-rw-r--r--testdata/scad/minimal/transform-insert.scad (renamed from testdata/scad/transform-insert.scad)0
-rw-r--r--testdata/scad/minimal/translate.scad (renamed from testdata/scad/translate.scad)0
-rw-r--r--testdata/scad/minimal/union.scad (renamed from testdata/scad/union.scad)0
-rw-r--r--testdata/scad/minkowski.scad67
-rw-r--r--testdata/scad/null-polygons.scad2
-rw-r--r--testdata/scad/rotate_extrude-tests.scad2
-rw-r--r--testdata/scad/sphere-tests.scad4
-rw-r--r--testdata/scad/sub1/sub2/sub3/include-test4.scad4
-rw-r--r--testdata/scad/sub1/sub2/sub3/sub4/include-test3.scad4
-rw-r--r--testdata/scad/surface-tests.scad1
-rw-r--r--tests/CMakeLists.txt246
-rw-r--r--tests/CSGTextCache.cc27
-rw-r--r--tests/CSGTextCache.h27
-rw-r--r--tests/CSGTextRenderer.cc185
-rw-r--r--tests/CSGTextRenderer.h40
-rw-r--r--tests/FindGLEW.cmake40
-rw-r--r--tests/OffscreenContext.h14
-rw-r--r--tests/OffscreenContext.mm207
-rw-r--r--tests/OffscreenView.cc239
-rw-r--r--tests/OffscreenView.h39
-rw-r--r--tests/cgalpngtest.cc234
-rw-r--r--tests/cgaltest.cc180
-rwxr-xr-xtests/compare-bitmaps.sh31
-rw-r--r--tests/csgtermtest.cc172
-rw-r--r--tests/csgtexttest.cc158
-rw-r--r--tests/dumptest.cc155
-rw-r--r--tests/opencsgtest.cc251
-rw-r--r--tests/regression/cgalpngtest/2d-3d-expected.pngbin0 -> 7842 bytes
-rw-r--r--tests/regression/cgalpngtest/assign-tests-expected.pngbin0 -> 9420 bytes
-rw-r--r--tests/regression/cgalpngtest/background-modifier-expected.pngbin0 -> 14578 bytes
-rw-r--r--tests/regression/cgalpngtest/circle-tests-expected.pngbin0 -> 8435 bytes
-rw-r--r--tests/regression/cgalpngtest/color-tests-expected.pngbin0 -> 9554 bytes
-rw-r--r--tests/regression/cgalpngtest/cube-tests-expected.pngbin0 -> 8008 bytes
-rw-r--r--tests/regression/cgalpngtest/cylinder-tests-expected.pngbin0 -> 12498 bytes
-rw-r--r--tests/regression/cgalpngtest/difference-tests-expected.pngbin0 -> 11151 bytes
-rw-r--r--tests/regression/cgalpngtest/disable-modifier-expected.pngbin0 -> 7433 bytes
-rw-r--r--tests/regression/cgalpngtest/for-nested-tests-expected.pngbin0 -> 23800 bytes
-rw-r--r--tests/regression/cgalpngtest/for-tests-expected.pngbin0 -> 10995 bytes
-rw-r--r--tests/regression/cgalpngtest/highlight-modifier-expected.pngbin0 -> 13772 bytes
-rw-r--r--tests/regression/cgalpngtest/hull2-tests-expected.pngbin0 -> 7531 bytes
-rw-r--r--tests/regression/cgalpngtest/import_dxf-tests-expected.pngbin0 -> 9919 bytes
-rw-r--r--tests/regression/cgalpngtest/intersection-tests-expected.pngbin0 -> 8295 bytes
-rw-r--r--tests/regression/cgalpngtest/intersection_for-tests-expected.pngbin0 -> 8314 bytes
-rw-r--r--tests/regression/cgalpngtest/linear_extrude-tests-expected.pngbin0 -> 12143 bytes
-rw-r--r--tests/regression/cgalpngtest/minkowski2-tests-expected.pngbin0 -> 7538 bytes
-rw-r--r--tests/regression/cgalpngtest/minkowski3-tests-expected.pngbin0 -> 8602 bytes
-rw-r--r--tests/regression/cgalpngtest/polygon-tests-expected.pngbin0 -> 8240 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-tests-expected.pngbin0 -> 9159 bytes
-rw-r--r--tests/regression/cgalpngtest/render-tests-expected.pngbin0 -> 10002 bytes
-rw-r--r--tests/regression/cgalpngtest/root-modifier-expected.pngbin0 -> 7433 bytes
-rw-r--r--tests/regression/cgalpngtest/rotate_extrude-tests-expected.pngbin0 -> 20273 bytes
-rw-r--r--tests/regression/cgalpngtest/sphere-tests-expected.pngbin0 -> 17379 bytes
-rw-r--r--tests/regression/cgalpngtest/square-tests-expected.pngbin0 -> 7820 bytes
-rw-r--r--tests/regression/cgalpngtest/surface-tests-expected.pngbin0 -> 36682 bytes
-rw-r--r--tests/regression/cgalpngtest/transform-tests-expected.pngbin0 -> 16015 bytes
-rw-r--r--tests/regression/cgalpngtest/union-tests-expected.pngbin0 -> 9818 bytes
-rw-r--r--tests/regression/csgtermtest/allfunctions-expected.txt1
-rw-r--r--tests/regression/csgtermtest/allmodules-expected.txt1
-rw-r--r--tests/regression/csgtermtest/assign-expected.txt1
-rw-r--r--tests/regression/csgtermtest/child-expected.txt1
-rw-r--r--tests/regression/csgtermtest/circle-expected.txt1
-rw-r--r--tests/regression/csgtermtest/color-expected.txt1
-rw-r--r--tests/regression/csgtermtest/cube-expected.txt1
-rw-r--r--tests/regression/csgtermtest/cylinder-expected.txt1
-rw-r--r--tests/regression/csgtermtest/difference-expected.txt1
-rw-r--r--tests/regression/csgtermtest/dxf_linear_extrude-expected.txt1
-rw-r--r--tests/regression/csgtermtest/dxf_rotate_extrude-expected.txt1
-rw-r--r--tests/regression/csgtermtest/echo-expected.txt1
-rw-r--r--tests/regression/csgtermtest/for-expected.txt1
-rw-r--r--tests/regression/csgtermtest/glide-expected.txt1
-rw-r--r--tests/regression/csgtermtest/group-expected.txt1
-rw-r--r--tests/regression/csgtermtest/hull-expected.txt1
-rw-r--r--tests/regression/csgtermtest/if-expected.txt1
-rw-r--r--tests/regression/csgtermtest/import-expected.txt1
-rw-r--r--tests/regression/csgtermtest/import_dxf-expected.txt1
-rw-r--r--tests/regression/csgtermtest/import_off-expected.txt1
-rw-r--r--tests/regression/csgtermtest/import_stl-expected.txt1
-rw-r--r--tests/regression/csgtermtest/intersection-expected.txt1
-rw-r--r--tests/regression/csgtermtest/intersection_for-expected.txt1
-rw-r--r--tests/regression/csgtermtest/linear_extrude-expected.txt1
-rw-r--r--tests/regression/csgtermtest/minkowski-expected.txt1
-rw-r--r--tests/regression/csgtermtest/mirror-expected.txt1
-rw-r--r--tests/regression/csgtermtest/multmatrix-expected.txt1
-rw-r--r--tests/regression/csgtermtest/polygon-expected.txt1
-rw-r--r--tests/regression/csgtermtest/polyhedron-expected.txt1
-rw-r--r--tests/regression/csgtermtest/projection-expected.txt1
-rw-r--r--tests/regression/csgtermtest/render-expected.txt1
-rw-r--r--tests/regression/csgtermtest/rotate-expected.txt1
-rw-r--r--tests/regression/csgtermtest/rotate_extrude-expected.txt1
-rw-r--r--tests/regression/csgtermtest/scale-expected.txt1
-rw-r--r--tests/regression/csgtermtest/sphere-expected.txt1
-rw-r--r--tests/regression/csgtermtest/square-expected.txt1
-rw-r--r--tests/regression/csgtermtest/subdiv-expected.txt1
-rw-r--r--tests/regression/csgtermtest/surface-expected.txt1
-rw-r--r--tests/regression/csgtermtest/transform-insert-expected.txt1
-rw-r--r--tests/regression/csgtermtest/translate-expected.txt1
-rw-r--r--tests/regression/csgtermtest/union-expected.txt1
-rw-r--r--tests/regression/csgtexttest/allfunctions-expected.txt1
-rw-r--r--tests/regression/csgtexttest/allmodules-expected.txt1
-rw-r--r--tests/regression/csgtexttest/assign-expected.txt1
-rw-r--r--tests/regression/csgtexttest/child-expected.txt1
-rw-r--r--tests/regression/csgtexttest/circle-expected.txt1
-rw-r--r--tests/regression/csgtexttest/color-expected.txt1
-rw-r--r--tests/regression/csgtexttest/cube-expected.txt1
-rw-r--r--tests/regression/csgtexttest/cylinder-expected.txt1
-rw-r--r--tests/regression/csgtexttest/difference-expected.txt1
-rw-r--r--tests/regression/csgtexttest/difference-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/dim-all-expected.txt1
-rw-r--r--tests/regression/csgtexttest/dxf_linear_extrude-expected.txt1
-rw-r--r--tests/regression/csgtexttest/dxf_rotate_extrude-expected.txt1
-rw-r--r--tests/regression/csgtexttest/echo-expected.txt1
-rw-r--r--tests/regression/csgtexttest/for-expected.txt1
-rw-r--r--tests/regression/csgtexttest/glide-expected.txt1
-rw-r--r--tests/regression/csgtexttest/group-expected.txt1
-rw-r--r--tests/regression/csgtexttest/hull-expected.txt1
-rw-r--r--tests/regression/csgtexttest/if-expected.txt1
-rw-r--r--tests/regression/csgtexttest/import-expected.txt1
-rw-r--r--tests/regression/csgtexttest/import_dxf-expected.txt1
-rw-r--r--tests/regression/csgtexttest/import_off-expected.txt1
-rw-r--r--tests/regression/csgtexttest/import_stl-expected.txt1
-rw-r--r--tests/regression/csgtexttest/import_stl-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/intersection-expected.txt1
-rw-r--r--tests/regression/csgtexttest/intersection-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/intersection_for-expected.txt1
-rw-r--r--tests/regression/csgtexttest/linear_extrude-expected.txt1
-rw-r--r--tests/regression/csgtexttest/linear_extrude-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/minkowski-expected.txt1
-rw-r--r--tests/regression/csgtexttest/mirror-expected.txt1
-rw-r--r--tests/regression/csgtexttest/multmatrix-expected.txt1
-rw-r--r--tests/regression/csgtexttest/null-polygons-expected.txt1
-rw-r--r--tests/regression/csgtexttest/polygon-expected.txt1
-rw-r--r--tests/regression/csgtexttest/polygon-illegal-winding-expected.txt1
-rw-r--r--tests/regression/csgtexttest/polyhedron-expected.txt1
-rw-r--r--tests/regression/csgtexttest/polyset-reduce-crash-expected.txt1
-rw-r--r--tests/regression/csgtexttest/projection-expected.txt1
-rw-r--r--tests/regression/csgtexttest/projection-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/render-expected.txt1
-rw-r--r--tests/regression/csgtexttest/rotate-expected.txt1
-rw-r--r--tests/regression/csgtexttest/rotate_extrude-expected.txt1
-rw-r--r--tests/regression/csgtexttest/rotate_extrude-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/scale-expected.txt1
-rw-r--r--tests/regression/csgtexttest/sphere-expected.txt1
-rw-r--r--tests/regression/csgtexttest/sphere-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/square-expected.txt1
-rw-r--r--tests/regression/csgtexttest/subdiv-expected.txt1
-rw-r--r--tests/regression/csgtexttest/surface-expected.txt1
-rw-r--r--tests/regression/csgtexttest/surface-tests-expected.txt1
-rw-r--r--tests/regression/csgtexttest/transform-insert-expected.txt1
-rw-r--r--tests/regression/csgtexttest/translate-expected.txt1
-rw-r--r--tests/regression/csgtexttest/union-expected.txt1
-rw-r--r--tests/regression/dumptest/allfunctions-expected.txt1
-rw-r--r--tests/regression/dumptest/allmodules-expected.txt39
-rw-r--r--tests/regression/dumptest/assign-expected.txt3
-rw-r--r--tests/regression/dumptest/child-expected.txt1
-rw-r--r--tests/regression/dumptest/circle-expected.txt3
-rw-r--r--tests/regression/dumptest/color-expected.txt3
-rw-r--r--tests/regression/dumptest/cube-expected.txt3
-rw-r--r--tests/regression/dumptest/cylinder-expected.txt3
-rw-r--r--tests/regression/dumptest/difference-expected.txt3
-rw-r--r--tests/regression/dumptest/difference-tests-expected.txt6
-rw-r--r--tests/regression/dumptest/dim-all-expected.txt10
-rw-r--r--tests/regression/dumptest/dxf-export-expected.txt17
-rw-r--r--tests/regression/dumptest/dxf_linear_extrude-expected.txt3
-rw-r--r--tests/regression/dumptest/dxf_rotate_extrude-expected.txt3
-rw-r--r--tests/regression/dumptest/echo-expected.txt3
-rw-r--r--tests/regression/dumptest/for-expected.txt3
-rw-r--r--tests/regression/dumptest/glide-expected.txt3
-rw-r--r--tests/regression/dumptest/group-expected.txt3
-rw-r--r--tests/regression/dumptest/hull-expected.txt3
-rw-r--r--tests/regression/dumptest/hull-tests-expected.txt46
-rw-r--r--tests/regression/dumptest/if-expected.txt3
-rw-r--r--tests/regression/dumptest/import-expected.txt3
-rw-r--r--tests/regression/dumptest/import_dxf-expected.txt3
-rw-r--r--tests/regression/dumptest/import_off-expected.txt3
-rw-r--r--tests/regression/dumptest/import_stl-expected.txt3
-rw-r--r--tests/regression/dumptest/import_stl-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/include-test-expected.txt20
-rw-r--r--tests/regression/dumptest/intersection-expected.txt3
-rw-r--r--tests/regression/dumptest/intersection-tests-expected.txt6
-rw-r--r--tests/regression/dumptest/intersection_for-expected.txt3
-rw-r--r--tests/regression/dumptest/linear_extrude-expected.txt3
-rw-r--r--tests/regression/dumptest/linear_extrude-tests-expected.txt30
-rw-r--r--tests/regression/dumptest/minkowski-expected.txt3
-rw-r--r--tests/regression/dumptest/minkowski-tests-expected.txt66
-rw-r--r--tests/regression/dumptest/mirror-expected.txt3
-rw-r--r--tests/regression/dumptest/multmatrix-expected.txt3
-rw-r--r--tests/regression/dumptest/null-polygons-expected.txt6
-rw-r--r--tests/regression/dumptest/polygon-expected.txt3
-rw-r--r--tests/regression/dumptest/polygon-illegal-winding-expected.txt3
-rw-r--r--tests/regression/dumptest/polyhedron-expected.txt3
-rw-r--r--tests/regression/dumptest/polyset-reduce-crash-expected.txt17
-rw-r--r--tests/regression/dumptest/projection-expected.txt3
-rw-r--r--tests/regression/dumptest/projection-tests-expected.txt25
-rw-r--r--tests/regression/dumptest/render-expected.txt3
-rw-r--r--tests/regression/dumptest/rotate-expected.txt3
-rw-r--r--tests/regression/dumptest/rotate_extrude-expected.txt3
-rw-r--r--tests/regression/dumptest/rotate_extrude-tests-expected.txt7
-rw-r--r--tests/regression/dumptest/scale-expected.txt3
-rw-r--r--tests/regression/dumptest/sphere-expected.txt3
-rw-r--r--tests/regression/dumptest/sphere-tests-expected.txt12
-rw-r--r--tests/regression/dumptest/square-expected.txt3
-rw-r--r--tests/regression/dumptest/string-test-expected.txt3
-rw-r--r--tests/regression/dumptest/subdiv-expected.txt3
-rw-r--r--tests/regression/dumptest/surface-expected.txt3
-rw-r--r--tests/regression/dumptest/surface-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/transform-insert-expected.txt3
-rw-r--r--tests/regression/dumptest/translate-expected.txt3
-rw-r--r--tests/regression/dumptest/union-expected.txt3
-rw-r--r--tests/regression/opencsgtest/2d-3d-expected.pngbin0 -> 8246 bytes
-rw-r--r--tests/regression/opencsgtest/assign-tests-expected.pngbin0 -> 9990 bytes
-rw-r--r--tests/regression/opencsgtest/background-modifier-expected.pngbin0 -> 17699 bytes
-rw-r--r--tests/regression/opencsgtest/circle-tests-expected.pngbin0 -> 9537 bytes
-rw-r--r--tests/regression/opencsgtest/color-tests-expected.pngbin0 -> 11055 bytes
-rw-r--r--tests/regression/opencsgtest/cube-tests-expected.pngbin0 -> 7965 bytes
-rw-r--r--tests/regression/opencsgtest/cylinder-tests-expected.pngbin0 -> 13165 bytes
-rw-r--r--tests/regression/opencsgtest/difference-tests-expected.pngbin0 -> 12383 bytes
-rw-r--r--tests/regression/opencsgtest/disable-modifier-expected.pngbin0 -> 7440 bytes
-rw-r--r--tests/regression/opencsgtest/for-nested-tests-expected.pngbin0 -> 25755 bytes
-rw-r--r--tests/regression/opencsgtest/for-tests-expected.pngbin0 -> 11584 bytes
-rw-r--r--tests/regression/opencsgtest/highlight-modifier-expected.pngbin0 -> 15502 bytes
-rw-r--r--tests/regression/opencsgtest/hull2-tests-expected.pngbin0 -> 8078 bytes
-rw-r--r--tests/regression/opencsgtest/import_dxf-tests-expected.pngbin0 -> 9289 bytes
-rw-r--r--tests/regression/opencsgtest/intersection-tests-expected.pngbin0 -> 8069 bytes
-rw-r--r--tests/regression/opencsgtest/intersection_for-tests-expected.pngbin0 -> 6642 bytes
-rw-r--r--tests/regression/opencsgtest/linear_extrude-tests-expected.pngbin0 -> 12466 bytes
-rw-r--r--tests/regression/opencsgtest/minkowski2-tests-expected.pngbin0 -> 8177 bytes
-rw-r--r--tests/regression/opencsgtest/minkowski3-tests-expected.pngbin0 -> 8820 bytes
-rw-r--r--tests/regression/opencsgtest/polygon-tests-expected.pngbin0 -> 9057 bytes
-rw-r--r--tests/regression/opencsgtest/projection-tests-expected.pngbin0 -> 9661 bytes
-rw-r--r--tests/regression/opencsgtest/render-tests-expected.pngbin0 -> 10392 bytes
-rw-r--r--tests/regression/opencsgtest/root-modifier-expected.pngbin0 -> 7440 bytes
-rw-r--r--tests/regression/opencsgtest/rotate_extrude-tests-expected.pngbin0 -> 21093 bytes
-rw-r--r--tests/regression/opencsgtest/sphere-tests-expected.pngbin0 -> 18276 bytes
-rw-r--r--tests/regression/opencsgtest/square-tests-expected.pngbin0 -> 8237 bytes
-rw-r--r--tests/regression/opencsgtest/surface-tests-expected.pngbin0 -> 41120 bytes
-rw-r--r--tests/regression/opencsgtest/transform-tests-expected.pngbin0 -> 16911 bytes
-rw-r--r--tests/regression/opencsgtest/union-tests-expected.pngbin0 -> 10539 bytes
-rwxr-xr-xtests/test_cmdline_tool.py163
459 files changed, 11191 insertions, 4067 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4ab4cf0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "libraries/MCAD"]
+ path = libraries/MCAD
+ url = git@github.com:openscad/MCAD.git
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 880ba91..7b9a000 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,12 +1,25 @@
OpenSCAD 20xx.yy
================
+Features:
+o The MCAD library is now bundled with OpenSCAD
+o Added import and export of the OFF file format
+o New import() statement reads the correct file format based on the filename extension
+ (.stl, .dxf and .off is supported)
o The color() statement now supports an alpha parameter, e.g. color(c=[1,0,0], alpha=0.4)
o The color() statement now supports specifying colors as strings, e.g. color("Red")
Bugfixes:
o square() crashed if any of the dimensions were zero
o Flush Caches didn't flush cached USE'd modules
+o STL export should be a bit more robust
+
+Deprecations:
+o dxf_linear_extrude() and dxf_rotate_extrude() are now deprecated.
+ Use linear_extrude() and rotate_extrude() instead.
+o The file, layer, origin and scale parameters to linear_extrude() and rotate_extrude()
+ are now deprecated. Use an import() child instead.
+o import_dxf(), import_stl() and import_off() are now deprecated. Use import() instead.
OpenSCAD 2011.06
================
diff --git a/boost.pri b/boost.pri
index b9f47d0..34a9dd2 100644
--- a/boost.pri
+++ b/boost.pri
@@ -1,18 +1,18 @@
-boost {
- isEmpty(DEPLOYDIR) {
- # Optionally specify location of boost using the
- # BOOSTDIR env. variable
- BOOST_DIR = $$(BOOSTDIR)
- !isEmpty(BOOST_DIR) {
- INCLUDEPATH += $$BOOST_DIR
- message("boost location: $$BOOST_DIR")
- win32:LIBS += -L$$BOOST_DIR/lib
- }
- }
-
- win32 {
- LIBS += -llibboost_thread-vc90-mt-s-1_46_1 -llibboost_program_options-vc90-mt-s-1_46_1
- } else {
- LIBS += -lboost_thread -lboost_program_options
- }
-}
+boost {
+ isEmpty(DEPLOYDIR) {
+ # Optionally specify location of boost using the
+ # BOOSTDIR env. variable
+ BOOST_DIR = $$(BOOSTDIR)
+ !isEmpty(BOOST_DIR) {
+ INCLUDEPATH += $$BOOST_DIR
+ message("boost location: $$BOOST_DIR")
+ win32:LIBS += -L$$BOOST_DIR/lib
+ }
+ }
+
+ win32 {
+ LIBS += -llibboost_thread-vc90-mt-s-1_46_1 -llibboost_program_options-vc90-mt-s-1_46_1
+ } else {
+ LIBS += -lboost_thread -lboost_program_options
+ }
+}
diff --git a/doc/OpenSCAD-classes.graffle b/doc/OpenSCAD-classes.graffle
index ef8c1b8..e067521 100644
--- a/doc/OpenSCAD-classes.graffle
+++ b/doc/OpenSCAD-classes.graffle
Binary files differ
diff --git a/doc/OpenSCAD-classes.pdf b/doc/OpenSCAD-classes.pdf
index 5870db0..04b62a9 100644
--- a/doc/OpenSCAD-classes.pdf
+++ b/doc/OpenSCAD-classes.pdf
Binary files differ
diff --git a/doc/OpenSCAD-compile.graffle b/doc/OpenSCAD-compile.graffle
index 14a2ccc..9a09fc3 100644
--- a/doc/OpenSCAD-compile.graffle
+++ b/doc/OpenSCAD-compile.graffle
Binary files differ
diff --git a/doc/OpenSCAD-compile.pdf b/doc/OpenSCAD-compile.pdf
index 2c9730d..efc7134 100644
--- a/doc/OpenSCAD-compile.pdf
+++ b/doc/OpenSCAD-compile.pdf
Binary files differ
diff --git a/doc/OpenSCAD-csg.graffle b/doc/OpenSCAD-csg.graffle
new file mode 100644
index 0000000..507c694
--- /dev/null
+++ b/doc/OpenSCAD-csg.graffle
Binary files differ
diff --git a/doc/OpenSCAD-csg.pdf b/doc/OpenSCAD-csg.pdf
new file mode 100644
index 0000000..0304cf7
--- /dev/null
+++ b/doc/OpenSCAD-csg.pdf
Binary files differ
diff --git a/doc/OpenSCAD-polygons.graffle b/doc/OpenSCAD-polygons.graffle
index 3e8b5bd..40df7ab 100644
--- a/doc/OpenSCAD-polygons.graffle
+++ b/doc/OpenSCAD-polygons.graffle
Binary files differ
diff --git a/doc/OpenSCAD-polygons.pdf b/doc/OpenSCAD-polygons.pdf
index d2eaa3b..ac18baf 100644
--- a/doc/OpenSCAD-polygons.pdf
+++ b/doc/OpenSCAD-polygons.pdf
Binary files differ
diff --git a/doc/TODO.txt b/doc/TODO.txt
index b40a825..5fe22bb 100644
--- a/doc/TODO.txt
+++ b/doc/TODO.txt
@@ -4,11 +4,9 @@ BUGS
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
-o It's now possible to start a new rendering while one is already running (which call processEvents())
- -> turn off most (or all) interaction while rendering
- -> Lock all or only one MainWindow (MDI)?
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
---------------
@@ -42,7 +40,6 @@ Using STL-imported models is tricky and triggers multiple issues:
CRASH BUGS
----------
o Broken polyhedron() entities are not correctly detected and cause CGAL segfaults
-o linear_extrude(50) square([5,0]);
o union() {
linear_extrude(height=10, twist=90) circle(5);
translate([7,-5,0]) linear_extrude(height=10, twist=180) polygon(points = [[0,0], [10,0], [5,10]]);
@@ -60,7 +57,6 @@ o Preferences
- OpenGL params
- Default language feature settings
- Make the library search path configurable?
-o Export etc.: automatically add missing extension as in SaveAs
o MDI
- Think about how to do MDI the right way
- Ctrl-W should close the current dialog, not the current main window
@@ -82,9 +78,11 @@ o 3D View
- overlay indicator displaying current view mode
- OpenCSG rendering: Coincident surfaces causes z-buffer fighting. Is this somehow
avoidable tuning the depth tests in OpenCSG?
- - Make the 10.000 element OpenCSG klimit configurable (Preferences) ?
+ - Make the 10.000 element OpenCSG limit configurable (Preferences) ?
- Use OpenGL picking to facilitate ray-tracing like features like measuring
thicknesses, distances, slot thicknesses etc.
+ - When specifying a transparency with the color() statement,
+ the object is not sorted and will be rendered wrongly
o Editor wishlist
- More infrastructure for external editor (allow communication from the outside)
- Preferences GUI for the features below
@@ -145,10 +143,6 @@ o Function-Module-Interface
- Pass a module instanciation to a function (e.g. for a volume() function)
- Pass a function to a module instanciation (e.g. for dynamic extrusion paths)
o Language Frontend
- - include statement doesn't support nesting. This can be fixed by
- keeping a nested stack of current input files in the lexer. See
- the "Flex & Bison" O'Reilly book, "Start States and Nested Input
- Files", page 28, for an example.
- Allow local variables and functions everywhere (not only on module level)
- allow any expression to be evaluated as boolean (!0 = true, 0 = false)
- Rethink for vs. intersection_for vs. group. Should for loops
@@ -167,8 +161,6 @@ o Mesh optimization on STL export
- Remove super small triangles (all sides are short)
- Replace super thin triangles (one h is short)
o Misc
- - When specifying a transparency with the color() statement,
- the object is not sorted and will be rendered wrongly
- Go through default values of parameters (e.g. cube() has x,y,z=1 while linear_extrude() has height=100)
- Add support for symbolic names to child() statement
- Add 'lines' object type for non-solid 2d drawings
@@ -182,6 +174,15 @@ o Grammar
- A random(seed) function
- import_*() -> *_import() (consistent prefix vs. postfix)
- linear_extrude()/rotate_extrude(): Cumbersome names? -> (extrude, revolve, lathe, sweep ?)
+o Hollow donut problem
+ When extruding a 2D CSG tree (e.g. a polygon with a hole), the hole
+ information is lost when performing the extrusion. For linear
+ extrusions, this has only a minor visual impact, but for rotate
+ extrusion, the resulting CGAL models will lose the hole. The OpenCSG
+ rendering keeps the hole, but renders slightly incorrect.
+o CGAL issues
+ - CGAL doesn't handle almost planar polygons. Consider splitting into triangles ourselves. See WillamAdams/dodec.scad
+
IDEAS FOR LANGUAGE CHANGES
--------------------------
@@ -204,6 +205,9 @@ o dxflinextrude and dxfrotextrude could share code
o Consider decoupling DXF-specific functionality from the 2D subsystem
o Visitation refactoring
- Make AbstractNode members private/protected?
+o Consider evaluating all referenced files relative to the document path instead
+ of being absolute. This would e.g. make regression testing of dumpcaching easier.
+ This would require us to pass a document contect to all traversal methods though.
BUILD SYSTEM
------------
@@ -216,13 +220,10 @@ o Caching and MDI looks suspicious when the code relies on external resources
which might be loaded from difference locations in different documents
-> we might get a false cache hit
o Are contructs like "child(0)" cached? Could this give false cache hits?
-o Write some cmd-line apps that dump an openscad file to misc. formats
- (dump, stl, dxf)
-o Write a simple test script that collects verified and current STL renderings
- and displays them side-by-side or smth.
-o Write simple driver scripts for comparing output of above command
o Collect "all" available OpenSCAD scripts from the internets and run the integration
tests on them all
+o dumptest tests:
+ - filename are dumped as absolute filenames - this will fail on other systems
o Write a regression test for the hexagonal cylinder orientation issue
INFRASTRUCTURE
@@ -234,6 +235,5 @@ MISC
----
o Streamline the cmd-line interface a bit
- Implicit output file format
-
o Write checklists for typical extension work (add new module, add new function)
-> make sure new test files are added
diff --git a/doc/testing.txt b/doc/testing.txt
new file mode 100644
index 0000000..b64d5a4
--- /dev/null
+++ b/doc/testing.txt
@@ -0,0 +1,29 @@
+Running regression tests:
+-------------------------
+
+Prerequisites: cmake, python
+
+cd tests
+mkdir build
+cd build
+cmake ..
+make
+make test
+
+
+Adding a new regression test:
+------------------------------
+
+1) create a test file at an appropriate location under testdata/
+2) if the test is non-obvious, create a human readable description of the test in the same directory (e.g testdata/scad/mytest.txt)
+3) if a new test app was written, this must be added to tests/CMakeLists.txt
+4) Add the tests to the test apps for which you want them to run (in tests/CMakeLists.txt)
+5) run the test with the environment variable TEST_GENERATE=1, e.g.:
+ $ TEST_GENERATE=1 ctest -R mytest
+ (this will generate a mytest-expected.txt file which is used for regression testing)
+6) manually verify that the output is correct (tests/regression/<testapp>/mytest-expected.<suffix>)
+7) run the test normally and verify that it passes:
+ $ ctest -R mytest
+
+Note that test files which don't have an *-expected.<suffix> file will
+be ignored for the test apps in question.
diff --git a/doc/visitor-changes.txt b/doc/visitor-changes.txt
new file mode 100644
index 0000000..e9df8eb
--- /dev/null
+++ b/doc/visitor-changes.txt
@@ -0,0 +1,7 @@
+Changes in visitor branch:
+o import_dxf(): layername="" imports all layers. Importing a single layer with a zero-length name is no longer supported. FIXME: The same prob. goes for dims
+o cylinder(): the r parameter will now always be used in place of a missing r1 or r2
+o for():
+ - with scalar argument now works, e.g.: for (i=23) echo(i)
+ - empty for loop is not evaluated, e.g. for () echo(i)
+ - for loop with illegal value is not evaluated, e.g. for ([0:true:2])
diff --git a/libraries/MCAD b/libraries/MCAD
new file mode 160000
+Subproject 903569a9a2ae9899805185af487402f864a7fbb
diff --git a/libraries/boxes.scad b/libraries/boxes.scad
deleted file mode 100644
index 86bc099..0000000
--- a/libraries/boxes.scad
+++ /dev/null
@@ -1,43 +0,0 @@
-// Library: boxes.scad
-// Version: 1.0
-// Author: Marius Kintel
-// Copyright: 2010
-// License: BSD
-
-// roundedBox([width, height, depth], float radius, bool sidesonly);
-
-// EXAMPLE USAGE:
-// roundedBox([20, 30, 40], 5, true);
-
-// size is a vector [w, h, d]
-module roundedBox(size, radius, sidesonly)
-{
- rot = [ [0,0,0], [90,0,90], [90,90,0] ];
- if (sidesonly) {
- cube(size - [2*radius,0,0], true);
- cube(size - [0,2*radius,0], true);
- for (x = [radius-size[0]/2, -radius+size[0]/2],
- y = [radius-size[1]/2, -radius+size[1]/2]) {
- translate([x,y,0]) cylinder(r=radius, h=size[2], center=true);
- }
- }
- else {
- cube([size[0], size[1]-radius*2, size[2]-radius*2], center=true);
- cube([size[0]-radius*2, size[1], size[2]-radius*2], center=true);
- cube([size[0]-radius*2, size[1]-radius*2, size[2]], center=true);
-
- for (axis = [0:2]) {
- for (x = [radius-size[axis]/2, -radius+size[axis]/2],
- y = [radius-size[(axis+1)%3]/2, -radius+size[(axis+1)%3]/2]) {
- rotate(rot[axis])
- translate([x,y,0])
- cylinder(h=size[(axis+2)%3]-2*radius, r=radius, center=true);
- }
- }
- for (x = [radius-size[0]/2, -radius+size[0]/2],
- y = [radius-size[1]/2, -radius+size[1]/2],
- z = [radius-size[2]/2, -radius+size[2]/2]) {
- translate([x,y,z]) sphere(radius);
- }
- }
-}
diff --git a/libraries/shapes.scad b/libraries/shapes.scad
deleted file mode 100644
index 6b0e284..0000000
--- a/libraries/shapes.scad
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * OpenSCAD Shapes Library (www.openscad.org)
- * Copyright (C) 2009 Catarina Mota
- *
- * 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.
- *
- * 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
- *
-*/
-
-
-//box(width, height, depth);
-//roundedBox(width, height, depth, factor);
-//cone(height, radius);
-//oval(width, height, depth);
-//tube(height, radius, wall);
-//ovalTube(width, height, depth, wall);
-//hexagon(height, depth);
-//octagon(height, depth);
-//dodecagon(height, depth);
-//hexagram(height, depth);
-//rightTriangle(adjacent, opposite, depth);
-//equiTriangle(side, depth);
-//12ptStar(height, depth);
-
-//----------------------
-
-// size is a vector [w, h, d]
-module box(size) {
- cube(size, true);
-}
-
-// size is a vector [w, h, d]
-module roundedBox(size, radius) {
- cube(size - [2*radius,0,0], true);
- cube(size - [0,2*radius,0], true);
- for (x = [radius-size[0]/2, -radius+size[0]/2],
- y = [radius-size[1]/2, -radius+size[1]/2]) {
- translate([x,y,0]) cylinder(r=radius, h=size[2], center=true);
- }
-}
-
-module cone(height, radius, center = false) {
- cylinder(height, radius, 0, center);
-}
-
-module oval(w,h, height, center = false) {
- scale([1, h/w, 1]) cylinder(h=height, r=w, center=center);
-}
-
-// wall is wall thickness
-module tube(height, radius, wall, center = false) {
- difference() {
- cylinder(h=height, r=radius, center=center);
- cylinder(h=height, r=radius-wall, center=center);
- }
-}
-
-// wall is wall thickness
-module ovalTube(height, rx, ry, wall, center = false) {
- difference() {
- scale([1, ry/rx, 1]) cylinder(h=height, r=rx, center=center);
- scale([(rx-wall)/rx, (ry-wall)/rx, 1]) cylinder(h=height, r=rx, center=center);
- }
-}
-
-// size is the XY plane size, height in Z
-module hexagon(size, height) {
- boxWidth = size/1.75;
- for (r = [-60, 0, 60]) rotate([0,0,r]) cube([boxWidth, size, height], true);
-}
-
-// size is the XY plane size, height in Z
-module octagon(size, height) {
- intersection() {
- cube([size, size, height], true);
- rotate([0,0,45]) cube([size, size, height], true);
- }
-}
-
-// size is the XY plane size, height in Z
-module dodecagon(size, height) {
- intersection() {
- hexagon(size, height);
- rotate([0,0,90]) hexagon(size, height);
- }
-}
-
-// size is the XY plane size, height in Z
-module hexagram(size, height) {
- boxWidth=size/1.75;
- for (v = [[0,1],[0,-1],[1,-1]]) {
- intersection() {
- rotate([0,0,60*v[0]]) cube([size, boxWidth, height], true);
- rotate([0,0,60*v[1]]) cube([size, boxWidth, height], true);
- }
- }
-}
-
-module rightTriangle(adjacent, opposite, height) {
- difference() {
- translate([-adjacent/2,opposite/2,0]) cube([adjacent, opposite, height], true);
- translate([-adjacent,0,0]) {
- rotate([0,0,atan(opposite/adjacent)]) dislocateBox(adjacent*2, opposite, height);
- }
- }
-}
-
-module equiTriangle(side, height) {
- difference() {
- translate([-side/2,side/2,0]) cube([side, side, height], true);
- rotate([0,0,30]) dislocateBox(side*2, side, height);
- translate([-side,0,0]) {
- rotate([0,0,60]) dislocateBox(side*2, side, height);
- }
- }
-}
-
-module 12ptStar(size, height) {
- starNum = 3;
- starAngle = 360/starNum;
- for (s = [1:starNum]) {
- rotate([0, 0, s*starAngle]) cube([size, size, height], true);
- }
-}
-
-//-----------------------
-//MOVES THE ROTATION AXIS OF A BOX FROM ITS CENTER TO THE BOTTOM LEFT CORNER
-//FIXME: Why are the dimensions changed?
-// why not just translate([0,0,-d/2]) cube([w,h,d]);
-module dislocateBox(w,h,d) {
- translate([w/2,h,0]) {
- difference() {
- cube([w, h*2, d+1]);
- translate([-w,0,0]) cube([w, h*2, d+1]);
- }
- }
-}
diff --git a/mjau.gdb b/mjau.gdb
index 7e420bb..7b8e029 100644
--- a/mjau.gdb
+++ b/mjau.gdb
@@ -1 +1 @@
-set environment DYLD_LIBRARY_PATH=/Users/kintel/code/metalab/checkout/OpenSCAD/libraries/install/lib
+set environment DYLD_LIBRARY_PATH=/Users/kintel/code/OpenSCAD/libraries/install/lib
diff --git a/openscad.pro b/openscad.pro
index 583a728..4541eaa 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -119,7 +119,6 @@ FORMS += src/MainWindow.ui \
src/Preferences.ui
HEADERS += src/renderer.h \
- src/cgalrenderer.h \
src/ThrownTogetherRenderer.h \
src/CGAL_renderer.h \
src/OGL_helper.h \
@@ -127,7 +126,6 @@ HEADERS += src/renderer.h \
src/MainWindow.h \
src/Preferences.h \
src/builtin.h \
- src/cgal.h \
src/context.h \
src/csgterm.h \
src/dxfdata.h \
@@ -140,17 +138,37 @@ HEADERS += src/renderer.h \
src/highlighter.h \
src/module.h \
src/node.h \
+ src/csgnode.h \
+ src/dxflinextrudenode.h \
+ src/dxfrotextrudenode.h \
+ src/projectionnode.h \
+ src/cgaladvnode.h \
+ src/importnode.h \
+ src/transformnode.h \
+ src/colornode.h \
+ src/rendernode.h \
src/openscad.h \
+ src/handle_dep.h \
src/polyset.h \
src/printutils.h \
src/value.h \
src/progress.h \
src/editor.h \
- src/mathc99.h
+ src/visitor.h \
+ src/state.h \
+ src/traverser.h \
+ src/nodecache.h \
+ src/nodedumper.h \
+ src/PolySetEvaluator.h \
+ src/CSGTermEvaluator.h \
+ src/myqhash.h \
+ src/Tree.h \
+ src/mathc99.h \
+ src/stl-utils.h
SOURCES += src/openscad.cc \
src/mainwin.cc \
- src/cgalrenderer.cc \
+ src/handle_dep.cc \
src/ThrownTogetherRenderer.cc \
src/glview.cc \
src/export.cc \
@@ -164,12 +182,10 @@ SOURCES += src/openscad.cc \
src/polyset.cc \
src/csgops.cc \
src/transform.cc \
+ src/color.cc \
src/primitives.cc \
src/projection.cc \
src/cgaladv.cc \
- src/cgaladv_convexhull2.cc \
- src/cgaladv_minkowski3.cc \
- src/cgaladv_minkowski2.cc \
src/surface.cc \
src/control.cc \
src/render.cc \
@@ -183,11 +199,35 @@ SOURCES += src/openscad.cc \
src/dxfrotextrude.cc \
src/highlighter.cc \
src/printutils.cc \
- src/nef2dxf.cc \
src/Preferences.cc \
src/progress.cc \
src/editor.cc \
- src/mathc99.cc
+ src/traverser.cc \
+ src/nodedumper.cc \
+ src/CSGTermEvaluator.cc \
+ src/qhash.cc \
+ src/Tree.cc \
+ src/mathc99.cc \
+ src/PolySetEvaluator.cc
+
+cgal {
+HEADERS += src/cgal.h \
+ src/cgalfwd.h \
+ src/cgalutils.h \
+ src/CGALEvaluator.h \
+ src/PolySetCGALEvaluator.h \
+ src/CGALRenderer.h \
+ src/CGAL_Nef_polyhedron.h
+
+SOURCES += src/cgalutils.cc \
+ src/CGALEvaluator.cc \
+ src/PolySetCGALEvaluator.cc \
+ src/CGALRenderer.cc \
+ src/CGAL_Nef_polyhedron.cc \
+ src/CGAL_Nef_polyhedron_DxfData.cc \
+ src/cgaladv_convexhull2.cc \
+ src/cgaladv_minkowski2.cc
+}
macx {
HEADERS += src/AppleEvents.h \
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 6de5331..0ed375f 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -125,7 +125,7 @@ build_cgal()
tar xzf CGAL-$version.tar.gz
cd CGAL-$version
# We build a static lib. Not really necessary, but it's well tested.
- cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DBUILD_SHARED_LIBS=FALSE -DCMAKE_OSX_DEPLOYMENT_TARGET="10.5" -DCMAKE_OSX_ARCHITECTURES="i386;x86_64"
+ cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DBUILD_SHARED_LIBS=FALSE -DCMAKE_OSX_DEPLOYMENT_TARGET="10.5" -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" -DBOOST_ROOT=$DEPLOYDIR
make -j4
make install
}
@@ -135,6 +135,7 @@ build_glew()
version=$1
echo "Building GLEW" $version "..."
cd $BASEDIR/src
+ rm -r glew-*
curl -LO http://downloads.sourceforge.net/project/glew/glew/$version/glew-$version.tgz
tar xzf glew-$version.tgz
cd glew-$version
@@ -152,6 +153,7 @@ build_opencsg()
curl -O http://www.opencsg.org/OpenCSG-$version.tar.gz
tar xzf OpenCSG-$version.tar.gz
cd OpenCSG-$version
+ patch -p1 < $OPENSCADDIR/patches/OpenCSG-$version-FBO.patch
patch -p1 < $OPENSCADDIR/patches/OpenCSG-$version-MacOSX-port.patch
MACOSX_DEPLOY_DIR=$DEPLOYDIR qmake -r CONFIG+="x86 x86_64"
make install
diff --git a/scripts/release-common.sh b/scripts/release-common.sh
index 340266d..9261d7a 100755
--- a/scripts/release-common.sh
+++ b/scripts/release-common.sh
@@ -80,17 +80,29 @@ echo "Creating directory structure..."
case $OS in
MACOSX)
EXAMPLESDIR=OpenSCAD.app/Contents/Resources/examples
+ LIBRARYDIR=OpenSCAD.app/Contents/Resources/libraries
;;
*)
EXAMPLESDIR=openscad-$VERSION/examples/
+ LIBRARYDIR=openscad-$VERSION/libraries/
rm -rf openscad-$VERSION
mkdir openscad-$VERSION
;;
esac
-mkdir -p $EXAMPLESDIR
-cp examples/* $EXAMPLESDIR
-chmod -R 644 $EXAMPLESDIR/*
+if [ -n $EXAMPLESDIR ]; then
+ echo $EXAMPLESDIR
+ mkdir -p $EXAMPLESDIR
+ cp examples/* $EXAMPLESDIR
+ chmod -R 644 $EXAMPLESDIR/*
+fi
+if [ -n $LIBRARYDIR ]; then
+ echo $LIBRARYDIR
+ mkdir -p $LIBRARYDIR
+ cp -R libraries/* $LIBRARYDIR
+ chmod -R u=rwx,go=r,+X $LIBRARYDIR/*
+ rm -rf `find $LIBRARYDIR -name ".git"`
+fi
echo "Creating archive.."
diff --git a/scripts/release-linux.sh b/scripts/release-linux.sh
index 35d177f..7675c07 100755
--- a/scripts/release-linux.sh
+++ b/scripts/release-linux.sh
@@ -87,6 +87,6 @@ chmod 755 -R release/
cp examples/* release/examples/
chmod 644 -R release/examples/*
-cp libraries/* release/libraries/
-chmod 644 -R release/libraries/*
-
+cp -R libraries/* release/libraries/
+chmod -R u=rwx,go=r,+X release/libraries/*
+rm -rf `find release/libraries -name ".git"`
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
new file mode 100644
index 0000000..440637a
--- /dev/null
+++ b/src/CGALEvaluator.cc
@@ -0,0 +1,611 @@
+#include "CGALEvaluator.h"
+#include "visitor.h"
+#include "state.h"
+#include "module.h" // FIXME: Temporarily for ModuleInstantiation
+#include "printutils.h"
+
+#include "csgnode.h"
+#include "cgaladvnode.h"
+#include "transformnode.h"
+#include "polyset.h"
+#include "dxfdata.h"
+#include "dxftess.h"
+
+#include "cgal.h"
+#include "cgalutils.h"
+#include <CGAL/assertions_behaviour.h>
+#include <CGAL/exceptions.h>
+
+#include <string>
+#include <map>
+#include <list>
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+#include <QRegExp>
+
+CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractNode &node)
+{
+ if (!isCached(node)) {
+ Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX);
+ evaluate.execute();
+ assert(isCached(node));
+ }
+ return this->cache[this->tree.getString(node)];
+}
+
+bool CGALEvaluator::isCached(const AbstractNode &node) const
+{
+ return this->cache.contains(this->tree.getString(node));
+}
+
+/*!
+ Modifies target by applying op to target and src:
+ target = target [op] src
+ */
+void CGALEvaluator::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op)
+{
+ if (target.dim != 2 && target.dim != 3) {
+ assert(false && "Dimension of Nef polyhedron must be 2 or 3");
+ }
+ if (src.empty()) return; // Empty polyhedron. This can happen for e.g. square([0,0])
+ if (target.dim != src.dim) return; // If someone tries to e.g. union 2d and 3d objects
+
+ switch (op) {
+ case CGE_UNION:
+ target += src;
+ break;
+ case CGE_INTERSECTION:
+ target *= src;
+ break;
+ case CGE_DIFFERENCE:
+ target -= src;
+ break;
+ case CGE_MINKOWSKI:
+ target.minkowski(src);
+ break;
+ }
+}
+
+/*!
+ FIXME: Let caller insert into the cache since caller might modify the result
+ (e.g. transform)
+*/
+void CGALEvaluator::applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op)
+{
+ CGAL_Nef_polyhedron N;
+ if (this->visitedchildren[node.index()].size() > 0) {
+ for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
+ iter != this->visitedchildren[node.index()].end();
+ iter++) {
+ const AbstractNode *chnode = iter->first;
+ const string &chcacheid = iter->second;
+ // FIXME: Don't use deep access to modinst members
+ if (chnode->modinst->tag_background) continue;
+ assert(isCached(*chnode));
+ if (N.empty()) {
+ N = this->cache[chcacheid].copy();
+ } else {
+ process(N, this->cache[chcacheid], op);
+ }
+ chnode->progress_report();
+ }
+ }
+ const std::string &cacheid = this->tree.getString(node);
+ this->cache.insert(cacheid, N);
+}
+
+extern CGAL_Nef_polyhedron2 *convexhull2(std::list<CGAL_Nef_polyhedron2*> a);
+
+void CGALEvaluator::applyHull(const CgaladvNode &node)
+{
+ if (this->visitedchildren[node.index()].size() > 0) {
+ std::list<CGAL_Nef_polyhedron2*> polys;
+ bool all2d = true;
+ for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
+ iter != this->visitedchildren[node.index()].end();
+ iter++) {
+ const AbstractNode *chnode = iter->first;
+ const string &chcacheid = iter->second;
+ // FIXME: Don't use deep access to modinst members
+ if (chnode->modinst->tag_background) continue;
+ assert(isCached(*chnode));
+ const CGAL_Nef_polyhedron &ch = this->cache[chcacheid];
+ if (ch.dim == 2) {
+ polys.push_back(ch.p2);
+ }
+ else if (ch.dim == 3) {
+ PRINT("WARNING: hull() is not implemented yet for 3D objects!");
+ all2d = false;
+ }
+ chnode->progress_report();
+ }
+
+ if (all2d) {
+ CGAL_Nef_polyhedron N(convexhull2(polys));
+ const std::string &cacheid = this->tree.getString(node);
+ this->cache.insert(cacheid, N);
+ }
+ }
+}
+
+/*
+ Typical visitor behavior:
+ o In prefix: Check if we're cached -> prune
+ o In postfix: Check if we're cached -> don't apply operator to children
+ o In postfix: addToParent()
+ */
+
+Response CGALEvaluator::visit(State &state, const AbstractNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) applyToChildren(node, CGE_UNION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CGALEvaluator::visit(State &state, const AbstractIntersectionNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) applyToChildren(node, CGE_INTERSECTION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CGALEvaluator::visit(State &state, const CsgNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ CGALEvaluator::CsgOp op;
+ switch (node.type) {
+ case CSG_TYPE_UNION:
+ op = CGE_UNION;
+ break;
+ case CSG_TYPE_DIFFERENCE:
+ op = CGE_DIFFERENCE;
+ break;
+ case CSG_TYPE_INTERSECTION:
+ op = CGE_INTERSECTION;
+ break;
+ default:
+ assert(false);
+ }
+ applyToChildren(node, op);
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CGALEvaluator::visit(State &state, const TransformNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ // First union all children
+ applyToChildren(node, CGE_UNION);
+
+ // Then apply transform
+ CGAL_Nef_polyhedron N = this->cache[this->tree.getString(node)];
+ // If there is no geometry under the transform, N will be empty and of dim 0,
+ // just just silently ignore such nodes
+ if (N.dim == 2) {
+ // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2
+ // 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], node.matrix[4], node.matrix[12],
+ node.matrix[1], node.matrix[5], node.matrix[13], node.matrix[15]);
+
+ 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);
+ ps.refcount = 0;
+ delete dd;
+ }
+ else if (N.dim == 3) {
+ CGAL_Aff_transformation t(
+ node.matrix[0], node.matrix[4], node.matrix[ 8], node.matrix[12],
+ node.matrix[1], node.matrix[5], node.matrix[ 9], node.matrix[13],
+ node.matrix[2], node.matrix[6], node.matrix[10], node.matrix[14], node.matrix[15]);
+ N.p3->transform(t);
+ }
+ const std::string &cacheid = this->tree.getString(node);
+ this->cache.insert(cacheid, N);
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ // Apply polyset operation
+ PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_CGAL, &this->psevaluator);
+ CGAL_Nef_polyhedron N;
+ if (ps) {
+ try {
+ N = evaluateCGALMesh(*ps);
+// print_messages_pop();
+ node.progress_report();
+
+ ps->unlink();
+ }
+ catch (...) { // Don't leak the PolySet on ProgressCancelException
+ ps->unlink();
+ throw;
+ }
+ }
+ const std::string &cacheid = this->tree.getString(node);
+ this->cache.insert(cacheid, N);
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CGALEvaluator::visit(State &state, const CgaladvNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ CGALEvaluator::CsgOp op;
+ switch (node.type) {
+ case MINKOWSKI:
+ op = CGE_MINKOWSKI;
+ applyToChildren(node, op);
+ break;
+ case GLIDE:
+ PRINT("WARNING: glide() is not implemented yet!");
+ return PruneTraversal;
+ break;
+ case SUBDIV:
+ PRINT("WARNING: subdiv() is not implemented yet!");
+ return PruneTraversal;
+ break;
+ case HULL:
+ applyHull(node);
+ break;
+ }
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+/*!
+ Adds ourself to out parent's list of traversed children.
+ Call this for _every_ node which affects output during the postfix traversal.
+*/
+void CGALEvaluator::addToParent(const State &state, const AbstractNode &node)
+{
+ assert(state.isPostfix());
+ this->visitedchildren.erase(node.index());
+ if (state.parent()) {
+ this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, this->tree.getString(node)));
+ }
+}
+
+#if 0
+/*!
+ Static function to evaluate CGAL meshes.
+ NB! This is just a support function used for development and debugging
+*/
+CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractPolyNode &node)
+{
+ // FIXME: Lookup Nef polyhedron in cache.
+
+ // print_messages_push();
+
+ PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_CGAL);
+ if (ps) {
+ try {
+ CGAL_Nef_polyhedron N = ps->evaluateCSGMesh();
+ // FIXME: Insert into cache
+ // print_messages_pop();
+ node.progress_report();
+
+ ps->unlink();
+ return N;
+ }
+ catch (...) { // Don't leak the PolySet on ProgressCancelException
+ ps->unlink();
+ throw;
+ }
+ }
+}
+#endif
+
+CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const PolySet &ps)
+{
+ if (ps.empty()) return CGAL_Nef_polyhedron();
+
+ if (ps.is2d)
+ {
+#if 0
+ // This version of the code causes problems in some cases.
+ // Example testcase: import_dxf("testdata/polygon8.dxf");
+ //
+ typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
+ typedef point_list_t::iterator point_list_it;
+ std::list< point_list_t > pdata_point_lists;
+ std::list < std::pair < point_list_it, point_list_it > > pdata;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ pdata_point_lists.push_back(point_list_t());
+ for (int j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j].x;
+ double y = ps.polygons[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ pdata_point_lists.back().push_back(p);
+ }
+ pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
+ pdata_point_lists.back().end()));
+ }
+
+ CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
+ return CGAL_Nef_polyhedron(N);
+#endif
+#if 0
+ // This version of the code works fine but is pretty slow.
+ //
+ CGAL_Nef_polyhedron2 N;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j].x;
+ double y = ps.polygons[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ plist.push_back(p);
+ }
+ N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return CGAL_Nef_polyhedron(N);
+#endif
+#if 1
+ // This version of the code does essentially the same thing as the 2nd
+ // version but merges some triangles before sending them to CGAL. This adds
+ // complexity but speeds up things..
+ //
+ struct PolyReducer
+ {
+ Grid2d<int> grid;
+ QHash< QPair<int,int>, QPair<int,int> > egde_to_poly;
+ QHash< int, CGAL_Nef_polyhedron2::Point > points;
+ QHash< int, QList<int> > polygons;
+ int poly_n;
+
+ void add_edges(int pn)
+ {
+ for (int j = 1; j <= this->polygons[pn].size(); j++) {
+ int a = this->polygons[pn][j-1];
+ int b = this->polygons[pn][j % this->polygons[pn].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0)
+ this->egde_to_poly[QPair<int,int>(a, b)].first = pn;
+ else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0)
+ this->egde_to_poly[QPair<int,int>(a, b)].second = pn;
+ else
+ abort();
+ }
+ }
+
+ void del_poly(int pn)
+ {
+ for (int j = 1; j <= this->polygons[pn].size(); j++) {
+ int a = this->polygons[pn][j-1];
+ int b = this->polygons[pn][j % this->polygons[pn].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn)
+ this->egde_to_poly[QPair<int,int>(a, b)].first = 0;
+ if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn)
+ this->egde_to_poly[QPair<int,int>(a, b)].second = 0;
+ }
+ this->polygons.remove(pn);
+ }
+
+ PolyReducer(const PolySet &ps) : grid(GRID_COARSE), poly_n(1)
+ {
+ int point_n = 1;
+ for (size_t i = 0; i < ps.polygons.size(); i++) {
+ for (size_t j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j][0];
+ double y = ps.polygons[i][j][1];
+ if (this->grid.has(x, y)) {
+ int idx = this->grid.data(x, y);
+ // Filter away two vertices with the same index (due to grid)
+ // This could be done in a more general way, but we'd rather redo the entire
+ // grid concept instead.
+ if (this->polygons[this->poly_n].indexOf(idx) == -1) {
+ this->polygons[this->poly_n].append(this->grid.data(x, y));
+ }
+ } else {
+ this->grid.align(x, y) = point_n;
+ this->polygons[this->poly_n].append(point_n);
+ this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
+ point_n++;
+ }
+ }
+ if (this->polygons[this->poly_n].size() >= 3) {
+ add_edges(this->poly_n);
+ this->poly_n++;
+ }
+ else {
+ this->polygons.remove(this->poly_n);
+ }
+ }
+ }
+
+ int merge(int p1, int p1e, int p2, int p2e)
+ {
+ for (int i = 1; i < this->polygons[p1].size(); i++) {
+ int j = (p1e + i) % this->polygons[p1].size();
+ this->polygons[this->poly_n].append(this->polygons[p1][j]);
+ }
+ for (int i = 1; i < this->polygons[p2].size(); i++) {
+ int j = (p2e + i) % this->polygons[p2].size();
+ this->polygons[this->poly_n].append(this->polygons[p2][j]);
+ }
+ del_poly(p1);
+ del_poly(p2);
+ add_edges(this->poly_n);
+ return this->poly_n++;
+ }
+
+ void reduce()
+ {
+ QList<int> work_queue;
+ QHashIterator< int, QList<int> > it(polygons);
+ while (it.hasNext()) {
+ it.next();
+ work_queue.append(it.key());
+ }
+ while (!work_queue.isEmpty()) {
+ int poly1_n = work_queue.first();
+ work_queue.removeFirst();
+ if (!this->polygons.contains(poly1_n))
+ continue;
+ for (int j = 1; j <= this->polygons[poly1_n].size(); j++) {
+ int a = this->polygons[poly1_n][j-1];
+ int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 &&
+ this->egde_to_poly[QPair<int,int>(a, b)].second != 0) {
+ int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first +
+ this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n;
+ int poly2_edge = -1;
+ for (int k = 1; k <= this->polygons[poly2_n].size(); k++) {
+ int c = this->polygons[poly2_n][k-1];
+ int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
+ if (c > d) { c = c^d; d = c^d; c = c^d; }
+ if (a == c && b == d) {
+ poly2_edge = k-1;
+ continue;
+ }
+ int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first +
+ this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n;
+ if (poly3_n < 0)
+ continue;
+ if (poly3_n == poly1_n)
+ goto next_poly1_edge;
+ }
+ work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge));
+ goto next_poly1;
+ }
+ next_poly1_edge:;
+ }
+ next_poly1:;
+ }
+ }
+
+ CGAL_Nef_polyhedron2 *toNef()
+ {
+ CGAL_Nef_polyhedron2 *N = new CGAL_Nef_polyhedron2;
+
+ QHashIterator< int, QList<int> > it(polygons);
+ while (it.hasNext()) {
+ it.next();
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < it.value().size(); j++) {
+ int p = it.value()[j];
+ plist.push_back(points[p]);
+ }
+ *N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return N;
+ }
+ };
+
+ PolyReducer pr(ps);
+ PRINTF("Number of polygons before reduction: %d\n", pr.polygons.size());
+ pr.reduce();
+ PRINTF("Number of polygons after reduction: %d\n", pr.polygons.size());
+ return CGAL_Nef_polyhedron(pr.toNef());
+#endif
+#if 0
+ // This is another experimental version. I should run faster than the above,
+ // is a lot simpler and has only one known weakness: Degenerate polygons, which
+ // get repaired by GLUTess, might trigger a CGAL crash here. The only
+ // known case for this is triangle-with-duplicate-vertex.dxf
+ // FIXME: If we just did a projection, we need to recreate the border!
+ if (ps.polygons.size() > 0) assert(ps.borders.size() > 0);
+ CGAL_Nef_polyhedron2 N;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.borders.size(); i++) {
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < ps.borders[i].size(); j++) {
+ double x = ps.borders[i][j].x;
+ double y = ps.borders[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ plist.push_back(p);
+ }
+ // FIXME: If a border (path) has a duplicate vertex in dxf,
+ // the CGAL_Nef_polyhedron2 constructor will crash.
+ N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return CGAL_Nef_polyhedron(N);
+
+#endif
+ }
+ else // not (this->is2d)
+ {
+ CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
+ try {
+ CGAL_Polyhedron *P = createPolyhedronFromPolySet(ps);
+ if (P) {
+ CGAL_Nef_polyhedron3 *N = new CGAL_Nef_polyhedron3(*P);
+ return CGAL_Nef_polyhedron(N);
+ }
+ }
+ catch (CGAL::Assertion_exception e) {
+ PRINTF("CGAL error: %s", e.what());
+ CGAL::set_error_behaviour(old_behaviour);
+ return CGAL_Nef_polyhedron();
+ }
+ }
+ return CGAL_Nef_polyhedron();
+}
diff --git a/src/CGALEvaluator.h b/src/CGALEvaluator.h
new file mode 100644
index 0000000..2453c25
--- /dev/null
+++ b/src/CGALEvaluator.h
@@ -0,0 +1,55 @@
+#ifndef CGALEVALUATOR_H_
+#define CGALEVALUATOR_H_
+
+#include "myqhash.h"
+#include "visitor.h"
+#include "Tree.h"
+#include "CGAL_Nef_polyhedron.h"
+#include "PolySetCGALEvaluator.h"
+
+#include <string>
+#include <map>
+#include <list>
+
+using std::string;
+using std::map;
+using std::list;
+using std::pair;
+
+class CGALEvaluator : public Visitor
+{
+public:
+ enum CsgOp {CGE_UNION, CGE_INTERSECTION, CGE_DIFFERENCE, CGE_MINKOWSKI};
+ // FIXME: If a cache is not given, we need to fix this ourselves
+ CGALEvaluator(QHash<string, CGAL_Nef_polyhedron> &cache, const Tree &tree) : cache(cache), tree(tree), psevaluator(*this) {}
+ virtual ~CGALEvaluator() {}
+
+ virtual Response visit(State &state, const AbstractNode &node);
+ virtual Response visit(State &state, const AbstractIntersectionNode &node);
+ virtual Response visit(State &state, const CsgNode &node);
+ virtual Response visit(State &state, const TransformNode &node);
+ virtual Response visit(State &state, const AbstractPolyNode &node);
+ virtual Response visit(State &state, const CgaladvNode &node);
+
+ CGAL_Nef_polyhedron evaluateCGALMesh(const AbstractNode &node);
+ CGAL_Nef_polyhedron evaluateCGALMesh(const PolySet &polyset);
+
+ const Tree &getTree() const { return this->tree; }
+
+private:
+ void addToParent(const State &state, const AbstractNode &node);
+ bool isCached(const AbstractNode &node) const;
+ void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op);
+ void applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op);
+ void applyHull(const CgaladvNode &node);
+
+ string currindent;
+ typedef list<pair<const AbstractNode *, string> > ChildList;
+ map<int, ChildList> visitedchildren;
+
+ QHash<string, CGAL_Nef_polyhedron> &cache;
+ const Tree &tree;
+ PolySetCGALEvaluator psevaluator;
+};
+
+#endif
diff --git a/src/cgalrenderer.cc b/src/CGALRenderer.cc
index 8a9ac21..f8e914b 100644
--- a/src/cgalrenderer.cc
+++ b/src/CGALRenderer.cc
@@ -24,37 +24,40 @@
*
*/
-#include "cgalrenderer.h"
+#include "CGALRenderer.h"
#include "polyset.h"
#include "CGAL_renderer.h"
#include "dxfdata.h"
#include "dxftess.h"
+#include "CGAL_Nef_polyhedron.h"
+#include "cgal.h"
-#include "Preferences.h"
+//#include "Preferences.h"
CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root)
{
if (root.dim == 2) {
- DxfData dd(root);
+ DxfData *dd = root.convertToDxfData();
this->polyhedron = NULL;
this->polyset = new PolySet();
this->polyset->is2d = true;
- dxf_tesselate(this->polyset, &dd, 0, true, false, 0);
+ dxf_tesselate(this->polyset, *dd, 0, true, false, 0);
+ delete dd;
}
else if (root.dim == 3) {
this->polyset = NULL;
this->polyhedron = new Polyhedron();
// FIXME: Make independent of Preferences
- this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR,
- Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(),
- Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(),
- Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue());
- this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR,
- Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(),
- Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(),
- Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue());
+ // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR,
+ // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(),
+ // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(),
+ // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue());
+ // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR,
+ // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(),
+ // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(),
+ // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue());
- CGAL::OGL::Nef3_Converter<CGAL_Nef_polyhedron3>::convert_to_OGLPolyhedron(this->root.p3, this->polyhedron);
+ CGAL::OGL::Nef3_Converter<CGAL_Nef_polyhedron3>::convert_to_OGLPolyhedron(*this->root.p3, this->polyhedron);
this->polyhedron->init();
}
else {
@@ -74,14 +77,15 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const
if (this->root.dim == 2) {
// Draw 2D polygons
glDisable(GL_LIGHTING);
- const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR);
+// FIXME: const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR);
+ const QColor &col = QColor(0x00, 0xbf, 0x99);
glColor3f(col.redF(), col.greenF(), col.blueF());
- for (int i=0; i < this->polyset->polygons.size(); i++) {
+ for (size_t i=0; i < this->polyset->polygons.size(); i++) {
glBegin(GL_POLYGON);
- for (int j=0; j < this->polyset->polygons[i].size(); j++) {
- PolySet::Point p = this->polyset->polygons[i][j];
- glVertex3d(p.x, p.y, -0.1);
+ for (size_t j=0; j < this->polyset->polygons[i].size(); j++) {
+ const Vector3d &p = this->polyset->polygons[i][j];
+ glVertex3d(p[0], p[1], -0.1);
}
glEnd();
}
@@ -90,13 +94,14 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const
typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
typedef Explorer::Point Point;
- Explorer E = this->root.p2.explorer();
+ Explorer E = this->root.p2->explorer();
// Draw 2D edges
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glLineWidth(2);
- const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR);
+// FIXME: const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR);
+ const QColor &col2 = QColor(0xff, 0x00, 0x00);
glColor3f(col2.redF(), col2.greenF(), col2.blueF());
// Extract the boundary, including inner boundaries of the polygons
diff --git a/src/cgalrenderer.h b/src/CGALRenderer.h
index b3c1638..5f854ea 100644
--- a/src/cgalrenderer.h
+++ b/src/CGALRenderer.h
@@ -2,7 +2,7 @@
#define CGALRENDERER_H_
#include "renderer.h"
-#include "cgal.h"
+#include "CGAL_Nef_polyhedron.h"
class CGALRenderer : public Renderer
{
@@ -11,7 +11,7 @@ public:
~CGALRenderer();
void draw(bool showfaces, bool showedges) const;
-private:
+public:
const CGAL_Nef_polyhedron &root;
class Polyhedron *polyhedron;
class PolySet *polyset;
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
new file mode 100644
index 0000000..6dcbdea
--- /dev/null
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -0,0 +1,81 @@
+#include "CGAL_Nef_polyhedron.h"
+#include "cgal.h"
+#include "cgalutils.h"
+#include "polyset.h"
+#include "dxfdata.h"
+#include "dxftess.h"
+#include <CGAL/minkowski_sum_3.h>
+
+CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator+=(const CGAL_Nef_polyhedron &other)
+{
+ if (this->dim == 2) (*this->p2) += (*other.p2);
+ else if (this->dim == 3) (*this->p3) += (*other.p3);
+ return *this;
+}
+
+CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator*=(const CGAL_Nef_polyhedron &other)
+{
+ if (this->dim == 2) (*this->p2) *= (*other.p2);
+ else if (this->dim == 3) (*this->p3) *= (*other.p3);
+ return *this;
+}
+
+CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator-=(const CGAL_Nef_polyhedron &other)
+{
+ if (this->dim == 2) (*this->p2) -= (*other.p2);
+ else if (this->dim == 3) (*this->p3) -= (*other.p3);
+ return *this;
+}
+
+extern CGAL_Nef_polyhedron2 minkowski2(const CGAL_Nef_polyhedron2 &a, const CGAL_Nef_polyhedron2 &b);
+
+CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &other)
+{
+ if (this->dim == 2) (*this->p2) = minkowski2(*this->p2, *other.p2);
+ else if (this->dim == 3) (*this->p3) = CGAL::minkowski_sum_3(*this->p3, *other.p3);
+ return *this;
+}
+
+int CGAL_Nef_polyhedron::weight() const
+{
+ if (this->dim == 2) return this->p2->explorer().number_of_vertices();
+ if (this->dim == 3) return this->p3->number_of_vertices();
+ return 0;
+}
+
+/*!
+ Creates a new PolySet and initializes it with the data from this polyhedron
+
+ This method is not const since convert_to_Polyhedron() wasn't const
+ in earlier versions of CGAL.
+*/
+PolySet *CGAL_Nef_polyhedron::convertToPolyset()
+{
+ assert(!this->empty());
+ PolySet *ps = NULL;
+ if (this->dim == 2) {
+ ps = new PolySet();
+ DxfData *dd = this->convertToDxfData();
+ ps->is2d = true;
+ dxf_tesselate(ps, *dd, 0, true, false, 0);
+ dxf_border_to_ps(ps, *dd);
+ delete dd;
+ }
+ else if (this->dim == 3) {
+ CGAL_Polyhedron P;
+ this->p3->convert_to_Polyhedron(P);
+ ps = createPolySetFromPolyhedron(P);
+ }
+ return ps;
+}
+
+/*!
+ Deep copy
+*/
+CGAL_Nef_polyhedron CGAL_Nef_polyhedron::copy() const
+{
+ CGAL_Nef_polyhedron copy = *this;
+ if (copy.p2) copy.p2 = new CGAL_Nef_polyhedron2(*copy.p2);
+ else if (copy.p3) copy.p3 = new CGAL_Nef_polyhedron3(*copy.p3);
+ return copy;
+}
diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h
new file mode 100644
index 0000000..616ba23
--- /dev/null
+++ b/src/CGAL_Nef_polyhedron.h
@@ -0,0 +1,34 @@
+#ifndef CGAL_NEF_POLYHEDRON_H_
+#define CGAL_NEF_POLYHEDRON_H_
+
+#ifdef ENABLE_CGAL
+
+#include "cgalfwd.h"
+
+class CGAL_Nef_polyhedron
+{
+public:
+ CGAL_Nef_polyhedron() : dim(0), p2(0), p3(0) {}
+ CGAL_Nef_polyhedron(CGAL_Nef_polyhedron2 *p) : dim(2), p2(p), p3(0) {}
+ CGAL_Nef_polyhedron(CGAL_Nef_polyhedron3 *p) : dim(3), p2(0), p3(p) {}
+ ~CGAL_Nef_polyhedron() {}
+
+ bool empty() const { return (dim == 0 || !p2 && !p3); }
+ CGAL_Nef_polyhedron &operator+=(const CGAL_Nef_polyhedron &other);
+ CGAL_Nef_polyhedron &operator*=(const CGAL_Nef_polyhedron &other);
+ CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
+ CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
+ CGAL_Nef_polyhedron copy() const;
+ int weight() const;
+ class PolySet *convertToPolyset();
+ class DxfData *convertToDxfData() const;
+
+ int dim;
+ // FIXME: Define ownership of the CGAL objects, e.g. use reference-counted smart pointers
+ CGAL_Nef_polyhedron2 *p2;
+ CGAL_Nef_polyhedron3 *p3;
+};
+
+#endif /* ENABLE_CGAL */
+
+#endif
diff --git a/src/nef2dxf.cc b/src/CGAL_Nef_polyhedron_DxfData.cc
index 320d6b8..fe58636 100644
--- a/src/nef2dxf.cc
+++ b/src/CGAL_Nef_polyhedron_DxfData.cc
@@ -26,18 +26,21 @@
#include "dxfdata.h"
#include "grid.h"
+#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
#ifdef ENABLE_CGAL
-DxfData::DxfData(const struct CGAL_Nef_polyhedron &N)
+DxfData *CGAL_Nef_polyhedron::convertToDxfData() const
{
+ assert(this->dim == 2);
+ DxfData *dxfdata = new DxfData();
Grid2d<int> grid(GRID_COARSE);
typedef CGAL_Nef_polyhedron2::Explorer Explorer;
typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
- Explorer E = N.p2.explorer();
+ Explorer E = this->p2->explorer();
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit)
{
@@ -51,26 +54,27 @@ DxfData::DxfData(const struct CGAL_Nef_polyhedron &N)
if (grid.has(x, y)) {
this_point = grid.align(x, y);
} else {
- this_point = grid.align(x, y) = points.size();
- points.append(Point(x, y));
+ this_point = grid.align(x, y) = dxfdata->points.size();
+ dxfdata->points.push_back(Vector2d(x, y));
}
if (first_point < 0) {
- paths.append(Path());
+ dxfdata->paths.push_back(DxfData::Path());
first_point = this_point;
}
if (this_point != last_point) {
- paths.last().points.append(&points[this_point]);
+ dxfdata->paths.back().indices.push_back(this_point);
last_point = this_point;
}
}
}
if (first_point >= 0) {
- paths.last().is_closed = 1;
- paths.last().points.append(&points[first_point]);
+ dxfdata->paths.back().is_closed = 1;
+ dxfdata->paths.back().indices.push_back(first_point);
}
}
- fixup_path_direction();
+ dxfdata->fixup_path_direction();
+ return dxfdata;
}
#endif // ENABLE_CGAL
diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc
new file mode 100644
index 0000000..dafa6a6
--- /dev/null
+++ b/src/CSGTermEvaluator.cc
@@ -0,0 +1,230 @@
+#include "CSGTermEvaluator.h"
+#include "visitor.h"
+#include "state.h"
+#include "csgterm.h"
+#include "module.h"
+#include "csgnode.h"
+#include "transformnode.h"
+#include "colornode.h"
+#include "rendernode.h"
+#include "cgaladvnode.h"
+#include "printutils.h"
+#include "PolySetEvaluator.h"
+
+#include <string>
+#include <map>
+#include <list>
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+
+/*!
+ \class CSGTermEvaluator
+
+ A visitor responsible for creating a tree of CSGTerm nodes used for rendering
+ with OpenCSG.
+*/
+
+CSGTerm *CSGTermEvaluator::evaluateCSGTerm(const AbstractNode &node,
+ std::vector<CSGTerm*> &highlights,
+ std::vector<CSGTerm*> &background)
+{
+ Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX);
+ evaluate.execute();
+ highlights = this->highlights;
+ background = this->background;
+ return this->stored_term[node.index()];
+}
+
+void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluator::CsgOp op)
+{
+ CSGTerm *t1 = NULL;
+ for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
+ iter != this->visitedchildren[node.index()].end();
+ iter++) {
+ const AbstractNode *chnode = *iter;
+ CSGTerm *t2 = this->stored_term[chnode->index()];
+ this->stored_term.erase(chnode->index());
+ if (t2 && !t1) {
+ t1 = t2;
+ } else if (t2 && t1) {
+ if (op == CSGT_UNION) {
+ t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
+ } else if (op == CSGT_DIFFERENCE) {
+ t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2);
+ } else if (op == CSGT_INTERSECTION) {
+ t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
+ }
+ }
+ }
+ if (t1 && node.modinst->tag_highlight) {
+ this->highlights.push_back(t1->link());
+ }
+ if (t1 && node.modinst->tag_background) {
+ this->background.push_back(t1);
+ t1 = NULL; // don't propagate background tagged nodes
+ }
+ this->stored_term[node.index()] = t1;
+}
+
+Response CSGTermEvaluator::visit(State &state, const AbstractNode &node)
+{
+ if (state.isPostfix()) {
+ applyToChildren(node, CSGT_UNION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTermEvaluator::visit(State &state, const AbstractIntersectionNode &node)
+{
+ if (state.isPostfix()) {
+ applyToChildren(node, CSGT_INTERSECTION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+static CSGTerm *evaluate_csg_term_from_ps(const State &state,
+ vector<CSGTerm*> &highlights,
+ vector<CSGTerm*> &background,
+ PolySet *ps,
+ const ModuleInstantiation *modinst,
+ const AbstractNode &node)
+{
+ std::stringstream stream;
+ stream << node.name() << node.index();
+ CSGTerm *t = new CSGTerm(ps, state.matrix(), state.color(), stream.str());
+ if (modinst->tag_highlight)
+ highlights.push_back(t->link());
+ if (modinst->tag_background) {
+ background.push_back(t);
+ return NULL;
+ }
+ return t;
+}
+
+Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node)
+{
+ if (state.isPostfix()) {
+ CSGTerm *t1 = NULL;
+ PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_OPENCSG, this->psevaluator);
+ if (ps) {
+ t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
+ ps, node.modinst, node);
+ }
+ this->stored_term[node.index()] = t1;
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTermEvaluator::visit(State &state, const CsgNode &node)
+{
+ if (state.isPostfix()) {
+ CsgOp op;
+ switch (node.type) {
+ case CSG_TYPE_UNION:
+ op = CSGT_UNION;
+ break;
+ case CSG_TYPE_DIFFERENCE:
+ op = CSGT_DIFFERENCE;
+ break;
+ case CSG_TYPE_INTERSECTION:
+ op = CSGT_INTERSECTION;
+ break;
+ default:
+ assert(false);
+ }
+ applyToChildren(node, op);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTermEvaluator::visit(State &state, const TransformNode &node)
+{
+ if (state.isPrefix()) {
+ double m[16];
+
+ for (int i = 0; i < 16; i++)
+ {
+ int c_row = i%4;
+ int m_col = i/4;
+ m[i] = 0;
+ for (int j = 0; j < 4; j++) {
+ m[i] += state.matrix()[c_row + j*4] * node.matrix[m_col*4 + j];
+ }
+ }
+ state.setMatrix(m);
+ }
+ if (state.isPostfix()) {
+ applyToChildren(node, CSGT_UNION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTermEvaluator::visit(State &state, const ColorNode &node)
+{
+ if (state.isPrefix()) {
+ state.setColor(node.color);
+ }
+ if (state.isPostfix()) {
+ applyToChildren(node, CSGT_UNION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+// FIXME: If we've got CGAL support, render this node as a CGAL union into a PolySet
+Response CSGTermEvaluator::visit(State &state, const RenderNode &node)
+{
+ if (state.isPostfix()) {
+ CSGTerm *t1 = NULL;
+ // FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this.
+ PolySet *ps = NULL;
+ if (this->psevaluator) {
+ ps = this->psevaluator->evaluatePolySet(node, AbstractPolyNode::RENDER_OPENCSG);
+ }
+ if (ps) {
+ t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
+ ps, node.modinst, node);
+ }
+ this->stored_term[node.index()] = t1;
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node)
+{
+ if (state.isPostfix()) {
+ CSGTerm *t1 = NULL;
+ // FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this.
+ PolySet *ps = NULL;
+ if (this->psevaluator) {
+ ps = this->psevaluator->evaluatePolySet(node, AbstractPolyNode::RENDER_OPENCSG);
+ }
+ if (ps) {
+ t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
+ ps, node.modinst, node);
+ }
+ this->stored_term[node.index()] = t1;
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+/*!
+ Adds ourself to out parent's list of traversed children.
+ Call this for _every_ node which affects output during the postfix traversal.
+*/
+void CSGTermEvaluator::addToParent(const State &state, const AbstractNode &node)
+{
+ assert(state.isPostfix());
+ this->visitedchildren.erase(node.index());
+ if (state.parent()) {
+ this->visitedchildren[state.parent()->index()].push_back(&node);
+ }
+}
diff --git a/src/CSGTermEvaluator.h b/src/CSGTermEvaluator.h
new file mode 100644
index 0000000..ac22906
--- /dev/null
+++ b/src/CSGTermEvaluator.h
@@ -0,0 +1,56 @@
+#ifndef CSGTERMEVALUATOR_H_
+#define CSGTERMEVALUATOR_H_
+
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include "Tree.h"
+#include "visitor.h"
+#include "node.h"
+
+using std::string;
+using std::map;
+using std::list;
+using std::vector;
+
+class CSGTermEvaluator : public Visitor
+{
+public:
+ CSGTermEvaluator(const Tree &tree, class PolySetEvaluator *psevaluator = NULL)
+ : tree(tree), psevaluator(psevaluator) {
+ }
+ virtual ~CSGTermEvaluator() {}
+
+ virtual Response visit(State &state, const AbstractNode &node);
+ virtual Response visit(State &state, const AbstractIntersectionNode &node);
+ virtual Response visit(State &state, const AbstractPolyNode &node);
+ virtual Response visit(State &state, const CsgNode &node);
+ virtual Response visit(State &state, const TransformNode &node);
+ virtual Response visit(State &state, const ColorNode &node);
+ virtual Response visit(State &state, const RenderNode &node);
+ virtual Response visit(State &state, const CgaladvNode &node);
+
+ class CSGTerm *evaluateCSGTerm(const AbstractNode &node,
+ vector<CSGTerm*> &highlights,
+ vector<CSGTerm*> &background);
+
+private:
+ enum CsgOp {CSGT_UNION, CSGT_INTERSECTION, CSGT_DIFFERENCE, CSGT_MINKOWSKI};
+ void addToParent(const State &state, const AbstractNode &node);
+ void applyToChildren(const AbstractNode &node, CSGTermEvaluator::CsgOp op);
+
+ const AbstractNode *root;
+ typedef list<const AbstractNode *> ChildList;
+ map<int, ChildList> visitedchildren;
+
+public:
+ map<int, class CSGTerm*> stored_term; // The term evaluated from each node index
+
+ vector<CSGTerm*> highlights;
+ vector<CSGTerm*> background;
+ const Tree &tree;
+ class PolySetEvaluator *psevaluator;
+};
+
+#endif
diff --git a/src/GLView.h b/src/GLView.h
index 764b23b..1001754 100644
--- a/src/GLView.h
+++ b/src/GLView.h
@@ -20,6 +20,7 @@ class GLView : public QGLWidget
public:
GLView(QWidget *parent = NULL);
+ GLView(const QGLFormat & format, QWidget *parent = NULL);
void setRenderer(class Renderer* r);
#ifdef ENABLE_OPENCSG
bool hasOpenCSGSupport() { return this->opencsg_support; }
@@ -52,6 +53,7 @@ public:
#endif
private:
+ void init();
Renderer *renderer;
bool showfaces;
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 243a5ad..0745935 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -7,7 +7,9 @@
#include "context.h"
#include "module.h"
#include "polyset.h"
+#include "Tree.h"
#include <QPointer>
+#include <vector>
class MainWindow : public QMainWindow, public Ui::MainWindow
{
@@ -32,6 +34,7 @@ public:
ModuleInstantiation root_inst; // Top level instance
AbstractNode *absolute_root_node; // Result of tree evaluation
AbstractNode *root_node; // Root if the root modifier (!) is used
+ Tree tree;
class CSGTerm *root_raw_term; // Result of CSG term rendering
CSGTerm *root_norm_term; // Normalized CSG products
@@ -45,9 +48,9 @@ public:
#endif
class ThrownTogetherRenderer *thrownTogetherRenderer;
- QVector<CSGTerm*> highlight_terms;
+ std::vector<CSGTerm*> highlight_terms;
CSGChain *highlights_chain;
- QVector<CSGTerm*> background_terms;
+ std::vector<CSGTerm*> background_terms;
CSGChain *background_chain;
QString last_compiled_doc;
@@ -77,6 +80,7 @@ private:
void compileCSG(bool procevents);
bool maybeSave();
bool checkModified();
+ QString dumpCSGTree(AbstractNode *root);
static void consoleOutput(const QString &msg, void *userdata) {
static_cast<MainWindow*>(userdata)->console->append(msg);
}
@@ -157,4 +161,19 @@ public slots:
void autoReloadSet(bool);
};
+class GuiLocker
+{
+public:
+ GuiLocker() {
+ gui_locked++;
+ }
+ ~GuiLocker() {
+ gui_locked--;
+ }
+ static bool isLocked() { return gui_locked > 0; }
+
+private:
+ static unsigned int gui_locked;
+};
+
#endif
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index 6548c8e..1741557 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -38,7 +38,7 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
- <widget class="GLView" name="screen" native="true"/>
+ <widget class="GLView" name="glview" native="true"/>
<widget class="QTextEdit" name="console"/>
</widget>
</item>
diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc
index 69013e9..f07b900 100644
--- a/src/OpenCSGRenderer.cc
+++ b/src/OpenCSGRenderer.cc
@@ -24,9 +24,13 @@
*
*/
+#include <GL/glew.h>
#include "OpenCSGRenderer.h"
#include "polyset.h"
#include "csgterm.h"
+#ifdef ENABLE_OPENCSG
+# include <opencsg.h>
+#endif
class OpenCSGPrim : public OpenCSG::Primitive
{
@@ -53,11 +57,6 @@ OpenCSGRenderer::OpenCSGRenderer(CSGChain *root_chain, CSGChain *highlights_chai
void OpenCSGRenderer::draw(bool showfaces, bool showedges) const
{
- static int glew_initialized = 0;
- if (!glew_initialized) {
- glew_initialized = 1;
- glewInit();
- }
if (this->root_chain) {
GLint *shaderinfo = this->shaderinfo;
if (!shaderinfo[0]) shaderinfo = NULL;
@@ -76,20 +75,17 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
{
std::vector<OpenCSG::Primitive*> primitives;
int j = 0;
- for (int i = 0;; i++)
- {
+ for (int i = 0;; i++) {
bool last = i == chain->polysets.size();
-
- if (last || chain->types[i] == CSGTerm::TYPE_UNION)
- {
+ if (last || chain->types[i] == CSGTerm::TYPE_UNION) {
if (j+1 != i) {
- OpenCSG::render(primitives);
+ OpenCSG::render(primitives);
glDepthFunc(GL_EQUAL);
}
- if (shaderinfo)
- glUseProgram(shaderinfo[0]);
+ if (shaderinfo) glUseProgram(shaderinfo[0]);
for (; j < i; j++) {
double *m = chain->matrices[j];
+ double *c = chain->colors[j];
glPushMatrix();
glMultMatrixd(m);
int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
@@ -97,12 +93,12 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo);
} else if (background) {
chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo);
- } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0) {
+ } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) {
// User-defined color from source
- glColor4d(m[16], m[17], m[18], m[19]);
+ glColor4dv(c);
if (shaderinfo) {
- glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]);
- glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0);
+ glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]);
+ glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
}
chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo);
} else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) {
@@ -112,8 +108,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
}
glPopMatrix();
}
- if (shaderinfo)
- glUseProgram(0);
+ if (shaderinfo) glUseProgram(0);
for (unsigned int k = 0; k < primitives.size(); k++) {
delete primitives[k];
}
@@ -121,18 +116,15 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
primitives.clear();
}
- if (last)
- break;
+ if (last) break;
OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ?
OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity);
prim->p = chain->polysets[i];
prim->m = chain->matrices[i];
prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
- if (highlight)
- prim->csgmode += 20;
- else if (background)
- prim->csgmode += 10;
+ if (highlight) prim->csgmode += 20;
+ else if (background) prim->csgmode += 10;
primitives.push_back(prim);
}
}
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
new file mode 100644
index 0000000..a648587
--- /dev/null
+++ b/src/PolySetCGALEvaluator.cc
@@ -0,0 +1,484 @@
+#include "PolySetCGALEvaluator.h"
+#include "cgal.h"
+#include "polyset.h"
+#include "CGALEvaluator.h"
+#include "projectionnode.h"
+#include "dxflinextrudenode.h"
+#include "dxfrotextrudenode.h"
+#include "cgaladvnode.h"
+#include "rendernode.h"
+#include "dxfdata.h"
+#include "dxftess.h"
+#include "module.h"
+
+#include "printutils.h"
+#include "openscad.h" // get_fragments_from_r()
+#include <boost/foreach.hpp>
+
+PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e)
+{
+ const string &cacheid = this->cgalevaluator.getTree().getString(node);
+ if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
+
+ // Before projecting, union all children
+ CGAL_Nef_polyhedron sum;
+ BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
+ if (v->modinst->tag_background) continue;
+ CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
+ if (N.dim == 3) {
+ if (sum.empty()) sum = N.copy();
+ else sum += N;
+ }
+ }
+ if (sum.empty()) return NULL;
+
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+ ps->is2d = true;
+
+ if (node.cut_mode)
+ {
+ PolySet *cube = new PolySet();
+ double infval = 1e8, eps = 0.1;
+ double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps;
+
+ cube->append_poly(); // top
+ cube->append_vertex(x1, y1, z2);
+ cube->append_vertex(x2, y1, z2);
+ cube->append_vertex(x2, y2, z2);
+ cube->append_vertex(x1, y2, z2);
+
+ cube->append_poly(); // bottom
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x1, y1, z1);
+
+ cube->append_poly(); // side1
+ cube->append_vertex(x1, y1, z1);
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x2, y1, z2);
+ cube->append_vertex(x1, y1, z2);
+
+ cube->append_poly(); // side2
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x2, y2, z2);
+ cube->append_vertex(x2, y1, z2);
+
+ cube->append_poly(); // side3
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x1, y2, z2);
+ cube->append_vertex(x2, y2, z2);
+
+ cube->append_poly(); // side4
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x1, y1, z1);
+ cube->append_vertex(x1, y1, z2);
+ cube->append_vertex(x1, y2, z2);
+ CGAL_Nef_polyhedron Ncube = this->cgalevaluator.evaluateCGALMesh(*cube);
+ cube->unlink();
+
+ // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED);
+ sum *= Ncube;
+ if (!sum.p3->is_simple()) {
+ PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
+ goto cant_project_non_simple_polyhedron;
+ }
+
+ PolySet *ps3 = sum.convertToPolyset();
+ Grid2d<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];
+ double y = ps3->polygons[i][j][1];
+ double z = ps3->polygons[i][j][2];
+ if (z != 0)
+ goto next_ps3_polygon_cut_mode;
+ if (conversion_grid.align(x, y) == i+1)
+ goto next_ps3_polygon_cut_mode;
+ conversion_grid.data(x, y) = i+1;
+ }
+ ps->append_poly();
+ for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j][0];
+ double y = ps3->polygons[i][j][1];
+ conversion_grid.align(x, y);
+ ps->insert_vertex(x, y);
+ }
+ next_ps3_polygon_cut_mode:;
+ }
+ ps3->unlink();
+ }
+ else
+ {
+ if (!sum.p3->is_simple()) {
+ PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
+ goto cant_project_non_simple_polyhedron;
+ }
+
+ PolySet *ps3 = sum.convertToPolyset();
+ CGAL_Nef_polyhedron np;
+ for (size_t i = 0; i < ps3->polygons.size(); i++)
+ {
+ int min_x_p = -1;
+ double min_x_val = 0;
+ for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j][0];
+ if (min_x_p < 0 || x < min_x_val) {
+ min_x_p = j;
+ min_x_val = x;
+ }
+ }
+ int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size();
+ int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size();
+ double ax = ps3->polygons[i][min_x_p1][0] - ps3->polygons[i][min_x_p][0];
+ double ay = ps3->polygons[i][min_x_p1][1] - ps3->polygons[i][min_x_p][1];
+ double at = atan2(ay, ax);
+ double bx = ps3->polygons[i][min_x_p2][0] - ps3->polygons[i][min_x_p][0];
+ double by = ps3->polygons[i][min_x_p2][1] - ps3->polygons[i][min_x_p][1];
+ double bt = atan2(by, bx);
+
+ double eps = 0.000001;
+ if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) ||
+ (fabs(bx) < eps && fabs(by) < eps)) {
+ // this triangle is degenerated in projection
+ continue;
+ }
+
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j][0];
+ double y = ps3->polygons[i][j][1];
+ CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y);
+ if (at > bt)
+ plist.push_front(p);
+ else
+ plist.push_back(p);
+ }
+ // FIXME: Should the CGAL_Nef_polyhedron2 be cached?
+ if (np.empty()) {
+ np.dim = 2;
+ np.p2 = 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);
+ }
+ }
+ DxfData *dxf = np.convertToDxfData();
+ dxf_tesselate(ps, *dxf, 0, true, false, 0);
+ dxf_border_to_ps(ps, *dxf);
+ ps3->unlink();
+ delete dxf;
+ }
+
+cant_project_non_simple_polyhedron:
+
+ this->cache.insert(cacheid, new cache_entry(ps->link()));
+ return ps;
+}
+
+static void add_slice(PolySet *ps, const DxfData &dxf, DxfData::Path &path, double rot1, double rot2, double h1, double h2)
+{
+ for (size_t j = 1; j < path.indices.size(); j++)
+ {
+ int k = j - 1;
+
+ double jx1 = dxf.points[path.indices[j]][0] * cos(rot1*M_PI/180) + dxf.points[path.indices[j]][1] * sin(rot1*M_PI/180);
+ double jy1 = dxf.points[path.indices[j]][0] * -sin(rot1*M_PI/180) + dxf.points[path.indices[j]][1] * cos(rot1*M_PI/180);
+
+ double jx2 = dxf.points[path.indices[j]][0] * cos(rot2*M_PI/180) + dxf.points[path.indices[j]][1] * sin(rot2*M_PI/180);
+ double jy2 = dxf.points[path.indices[j]][0] * -sin(rot2*M_PI/180) + dxf.points[path.indices[j]][1] * cos(rot2*M_PI/180);
+
+ double kx1 = dxf.points[path.indices[k]][0] * cos(rot1*M_PI/180) + dxf.points[path.indices[k]][1] * sin(rot1*M_PI/180);
+ double ky1 = dxf.points[path.indices[k]][0] * -sin(rot1*M_PI/180) + dxf.points[path.indices[k]][1] * cos(rot1*M_PI/180);
+
+ double kx2 = dxf.points[path.indices[k]][0] * cos(rot2*M_PI/180) + dxf.points[path.indices[k]][1] * sin(rot2*M_PI/180);
+ double ky2 = dxf.points[path.indices[k]][0] * -sin(rot2*M_PI/180) + dxf.points[path.indices[k]][1] * cos(rot2*M_PI/180);
+
+ double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2);
+ double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1);
+
+ if (dia1_len_sq > dia2_len_sq)
+ {
+ ps->append_poly();
+ if (path.is_inner) {
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx1, jy1, h1);
+ ps->append_vertex(jx2, jy2, h2);
+ } else {
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx1, jy1, h1);
+ ps->insert_vertex(jx2, jy2, h2);
+ }
+
+ ps->append_poly();
+ if (path.is_inner) {
+ ps->append_vertex(kx2, ky2, h2);
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx2, jy2, h2);
+ } else {
+ ps->insert_vertex(kx2, ky2, h2);
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx2, jy2, h2);
+ }
+ }
+ else
+ {
+ ps->append_poly();
+ if (path.is_inner) {
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx1, jy1, h1);
+ ps->append_vertex(kx2, ky2, h2);
+ } else {
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx1, jy1, h1);
+ ps->insert_vertex(kx2, ky2, h2);
+ }
+
+ ps->append_poly();
+ if (path.is_inner) {
+ ps->append_vertex(jx2, jy2, h2);
+ ps->append_vertex(kx2, ky2, h2);
+ ps->append_vertex(jx1, jy1, h1);
+ } else {
+ ps->insert_vertex(jx2, jy2, h2);
+ ps->insert_vertex(kx2, ky2, h2);
+ ps->insert_vertex(jx1, jy1, h1);
+ }
+ }
+ }
+}
+
+PolySet *PolySetCGALEvaluator::evaluatePolySet(const DxfLinearExtrudeNode &node,
+ AbstractPolyNode::render_mode_e)
+{
+ const string &cacheid = this->cgalevaluator.getTree().getString(node);
+ if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
+
+ DxfData *dxf;
+
+ if (node.filename.empty())
+ {
+ // Before extruding, union all (2D) children nodes
+ // to a single DxfData, then tesselate this into a PolySet
+ CGAL_Nef_polyhedron sum;
+ BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
+ if (v->modinst->tag_background) 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 (sum.empty()) return NULL;
+ dxf = sum.convertToDxfData();;
+ } else {
+ dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale);
+ }
+
+ PolySet *ps = extrudeDxfData(node, *dxf);
+ this->cache.insert(cacheid, new cache_entry(ps->link()));
+ delete dxf;
+ return ps;
+}
+
+PolySet *PolySetCGALEvaluator::extrudeDxfData(const DxfLinearExtrudeNode &node, DxfData &dxf)
+{
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+
+ double h1, h2;
+
+ if (node.center) {
+ h1 = -node.height/2.0;
+ h2 = +node.height/2.0;
+ } else {
+ h1 = 0;
+ h2 = node.height;
+ }
+
+ bool first_open_path = true;
+ for (size_t i = 0; i < dxf.paths.size(); i++)
+ {
+ if (dxf.paths[i].is_closed)
+ continue;
+ if (first_open_path) {
+ PRINTF("WARNING: Open paths in dxf_linear_extrude(file = \"%s\", layer = \"%s\"):",
+ node.filename.c_str(), node.layername.c_str());
+ first_open_path = false;
+ }
+ PRINTF(" %9.5f %10.5f ... %10.5f %10.5f",
+ dxf.points[dxf.paths[i].indices.front()][0] / node.scale + node.origin_x,
+ dxf.points[dxf.paths[i].indices.front()][1] / node.scale + node.origin_y,
+ dxf.points[dxf.paths[i].indices.back()][0] / node.scale + node.origin_x,
+ dxf.points[dxf.paths[i].indices.back()][1] / node.scale + node.origin_y);
+ }
+
+
+ if (node.has_twist)
+ {
+ dxf_tesselate(ps, dxf, 0, false, true, h1);
+ dxf_tesselate(ps, dxf, node.twist, true, true, h2);
+ for (int j = 0; j < node.slices; j++)
+ {
+ double t1 = node.twist*j / node.slices;
+ double t2 = node.twist*(j+1) / node.slices;
+ double g1 = h1 + (h2-h1)*j / node.slices;
+ double g2 = h1 + (h2-h1)*(j+1) / node.slices;
+ for (size_t i = 0; i < dxf.paths.size(); i++)
+ {
+ if (!dxf.paths[i].is_closed)
+ continue;
+ add_slice(ps, dxf, dxf.paths[i], t1, t2, g1, g2);
+ }
+ }
+ }
+ else
+ {
+ dxf_tesselate(ps, dxf, 0, false, true, h1);
+ dxf_tesselate(ps, dxf, 0, true, true, h2);
+ for (size_t i = 0; i < dxf.paths.size(); i++)
+ {
+ if (!dxf.paths[i].is_closed)
+ continue;
+ add_slice(ps, dxf, dxf.paths[i], 0, 0, h1, h2);
+ }
+ }
+
+ return ps;
+}
+
+PolySet *PolySetCGALEvaluator::evaluatePolySet(const DxfRotateExtrudeNode &node,
+ AbstractPolyNode::render_mode_e)
+{
+ const string &cacheid = this->cgalevaluator.getTree().getString(node);
+ if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
+
+ DxfData *dxf;
+
+ if (node.filename.empty())
+ {
+ // Before extruding, union all (2D) children nodes
+ // to a single DxfData, then tesselate this into a PolySet
+ CGAL_Nef_polyhedron sum;
+ BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
+ if (v->modinst->tag_background) 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 (sum.empty()) return NULL;
+ dxf = sum.convertToDxfData();
+ } else {
+ dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale);
+ }
+
+ PolySet *ps = rotateDxfData(node, *dxf);
+ this->cache.insert(cacheid, new cache_entry(ps->link()));
+ delete dxf;
+ return ps;
+}
+
+PolySet *PolySetCGALEvaluator::evaluatePolySet(const CgaladvNode &node, AbstractPolyNode::render_mode_e)
+{
+ CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node);
+ PolySet *ps = NULL;
+ if (!N.empty()) ps = N.convertToPolyset();
+ return ps;
+}
+
+PolySet *PolySetCGALEvaluator::evaluatePolySet(const RenderNode &node, AbstractPolyNode::render_mode_e)
+{
+ CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node);
+ PolySet *ps = NULL;
+ if (!N.empty()) ps = N.convertToPolyset();
+ return ps;
+}
+
+PolySet *PolySetCGALEvaluator::rotateDxfData(const DxfRotateExtrudeNode &node, DxfData &dxf)
+{
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+
+ for (size_t i = 0; i < dxf.paths.size(); i++)
+ {
+ double max_x = 0;
+ for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
+ max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]);
+ }
+
+ int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);
+
+ double ***points;
+ points = new double**[fragments];
+ for (int j=0; j < fragments; j++) {
+ points[j] = new double*[dxf.paths[i].indices.size()];
+ for (size_t k=0; k < dxf.paths[i].indices.size(); k++)
+ points[j][k] = new double[3];
+ }
+
+ for (int j = 0; j < fragments; j++) {
+ double a = (j*2*M_PI) / fragments;
+ 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][2] = dxf.points[dxf.paths[i].indices[k]][1];
+ }
+ }
+
+ for (int j = 0; j < fragments; j++) {
+ int j1 = j + 1 < fragments ? j + 1 : 0;
+ for (size_t k = 0; k < dxf.paths[i].indices.size(); k++) {
+ int k1 = k + 1 < dxf.paths[i].indices.size() ? k + 1 : 0;
+ if (points[j][k][0] != points[j1][k][0] ||
+ points[j][k][1] != points[j1][k][1] ||
+ points[j][k][2] != points[j1][k][2]) {
+ ps->append_poly();
+ ps->append_vertex(points[j ][k ][0],
+ points[j ][k ][1], points[j ][k ][2]);
+ ps->append_vertex(points[j1][k ][0],
+ points[j1][k ][1], points[j1][k ][2]);
+ ps->append_vertex(points[j ][k1][0],
+ points[j ][k1][1], points[j ][k1][2]);
+ }
+ if (points[j][k1][0] != points[j1][k1][0] ||
+ points[j][k1][1] != points[j1][k1][1] ||
+ points[j][k1][2] != points[j1][k1][2]) {
+ ps->append_poly();
+ ps->append_vertex(points[j ][k1][0],
+ points[j ][k1][1], points[j ][k1][2]);
+ ps->append_vertex(points[j1][k ][0],
+ points[j1][k ][1], points[j1][k ][2]);
+ ps->append_vertex(points[j1][k1][0],
+ points[j1][k1][1], points[j1][k1][2]);
+ }
+ }
+ }
+
+ for (int j=0; j < fragments; j++) {
+ for (size_t k=0; k < dxf.paths[i].indices.size(); k++)
+ delete[] points[j][k];
+ delete[] points[j];
+ }
+ delete[] points;
+ }
+
+ return ps;
+}
diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h
new file mode 100644
index 0000000..f5b8665
--- /dev/null
+++ b/src/PolySetCGALEvaluator.h
@@ -0,0 +1,29 @@
+#ifndef POLYSETCGALEVALUATOR_H_
+#define POLYSETCGALEVALUATOR_H_
+
+#include "PolySetEvaluator.h"
+
+/*!
+ This is a PolySet evaluator which uses the CGALEvaluator to support building
+ polysets.
+*/
+class PolySetCGALEvaluator : public PolySetEvaluator
+{
+public:
+ PolySetCGALEvaluator(class CGALEvaluator &CGALEvaluator) :
+ PolySetEvaluator(), cgalevaluator(CGALEvaluator) { }
+ virtual ~PolySetCGALEvaluator() { }
+ virtual PolySet *evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *evaluatePolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *evaluatePolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *evaluatePolySet(const CgaladvNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *evaluatePolySet(const RenderNode &node, AbstractPolyNode::render_mode_e);
+
+protected:
+ PolySet *extrudeDxfData(const DxfLinearExtrudeNode &node, class DxfData &dxf);
+ PolySet *rotateDxfData(const DxfRotateExtrudeNode &node, class DxfData &dxf);
+
+ CGALEvaluator &cgalevaluator;
+};
+
+#endif
diff --git a/src/PolySetEvaluator.cc b/src/PolySetEvaluator.cc
new file mode 100644
index 0000000..56acb1d
--- /dev/null
+++ b/src/PolySetEvaluator.cc
@@ -0,0 +1,15 @@
+#include "PolySetEvaluator.h"
+#include "printutils.h"
+#include "polyset.h"
+
+PolySetEvaluator *PolySetEvaluator::global_evaluator = NULL;
+
+PolySetEvaluator::cache_entry::cache_entry(PolySet *ps) :
+ ps(ps), msg(print_messages_stack.last())
+{
+}
+
+PolySetEvaluator::cache_entry::~cache_entry()
+{
+ ps->unlink();
+}
diff --git a/src/PolySetEvaluator.h b/src/PolySetEvaluator.h
new file mode 100644
index 0000000..70ec7ed
--- /dev/null
+++ b/src/PolySetEvaluator.h
@@ -0,0 +1,41 @@
+#ifndef POLYSETEVALUATOR_H_
+#define POLYSETEVALUATOR_H_
+
+#include "myqhash.h"
+#include "node.h"
+#include <QCache>
+
+class PolySetEvaluator
+{
+public:
+ enum EvaluateMode { EVALUATE_CGAL, EVALUATE_OPENCSG };
+ PolySetEvaluator() : cache(100) {}
+
+ virtual ~PolySetEvaluator() {}
+
+ virtual PolySet *evaluatePolySet(const class ProjectionNode &, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *evaluatePolySet(const class DxfLinearExtrudeNode &, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *evaluatePolySet(const class DxfRotateExtrudeNode &, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *evaluatePolySet(const class CgaladvNode &, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *evaluatePolySet(const class RenderNode &, AbstractPolyNode::render_mode_e) = 0;
+
+ void clearCache() {
+ this->cache.clear();
+ }
+
+protected:
+
+ struct cache_entry {
+ class PolySet *ps;
+ QString msg;
+ cache_entry(PolySet *ps);
+ ~cache_entry();
+ };
+
+ QCache<std::string, cache_entry> cache;
+
+private:
+ static PolySetEvaluator *global_evaluator;
+};
+
+#endif
diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc
index 6e0325e..598d542 100644
--- a/src/ThrownTogetherRenderer.cc
+++ b/src/ThrownTogetherRenderer.cc
@@ -66,6 +66,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
if (polySetVisitMark[QPair<PolySet*,double*>(chain->polysets[i], chain->matrices[i])]++ > 0)
continue;
double *m = chain->matrices[i];
+ double *c = chain->colors[i];
glPushMatrix();
glMultMatrixd(m);
int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
@@ -91,12 +92,12 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
} else {
chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m);
}
- } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0) {
- glColor4d(m[16], m[17], m[18], m[19]);
+ } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) {
+ glColor4dv(c);
chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m);
if (showedges) {
glDisable(GL_LIGHTING);
- glColor4d((m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0);
+ glColor4d((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode));
glEnable(GL_LIGHTING);
}
diff --git a/src/Tree.cc b/src/Tree.cc
new file mode 100644
index 0000000..a451f24
--- /dev/null
+++ b/src/Tree.cc
@@ -0,0 +1,30 @@
+#include "Tree.h"
+#include "nodedumper.h"
+
+#include <assert.h>
+
+/*!
+ Returns the cached string representation of the subtree rootet by \a node.
+ If node is not cached, the cache will be rebuilt.
+*/
+const std::string &Tree::getString(const AbstractNode &node) const
+{
+ assert(this->root_node);
+ if (!this->nodecache.contains(node)) {
+ NodeDumper dumper(this->nodecache, false);
+ Traverser trav(dumper, *this->root_node, Traverser::PRE_AND_POSTFIX);
+ trav.execute();
+ assert(this->nodecache.contains(*this->root_node) &&
+ "NodeDumper failed to create a cache");
+ }
+ return this->nodecache[node];
+}
+
+/*!
+ Sets a new root. Will clear the existing cache.
+ */
+void Tree::setRoot(const AbstractNode *root)
+{
+ this->root_node = root;
+ this->nodecache.clear();
+}
diff --git a/src/Tree.h b/src/Tree.h
new file mode 100644
index 0000000..2c3f0b8
--- /dev/null
+++ b/src/Tree.h
@@ -0,0 +1,31 @@
+#ifndef TREE_H_
+#define TREE_H_
+
+#include "nodecache.h"
+
+using std::string;
+
+/*!
+ For now, just an abstraction of the node tree which keeps a dump
+ cache based on node indices around.
+
+ Note that since node trees don't survive a recompilation, the tree cannot either.
+ */
+class Tree
+{
+public:
+ Tree(const AbstractNode *root = NULL) : root_node(root) {}
+ ~Tree() {}
+
+ void setRoot(const AbstractNode *root);
+ const AbstractNode *root() const { return this->root_node; }
+
+ // FIXME: Really return a reference?
+ const string &getString(const AbstractNode &node) const;
+
+private:
+ const AbstractNode *root_node;
+ mutable NodeCache nodecache;
+};
+
+#endif
diff --git a/src/builtin.h b/src/builtin.h
index d501a19..ae526f2 100644
--- a/src/builtin.h
+++ b/src/builtin.h
@@ -1,18 +1,20 @@
#ifndef BUILTIN_H_
#define BUILTIN_H_
-#include <QHash>
+#include <string>
+#include <boost/unordered_map.hpp>
-extern QHash<QString, class AbstractFunction*> builtin_functions;
+extern boost::unordered_map<std::string, class AbstractFunction*> builtin_functions;
extern void initialize_builtin_functions();
extern void destroy_builtin_functions();
-extern QHash<QString, class AbstractModule*> builtin_modules;
+extern boost::unordered_map<std::string, class AbstractModule*> builtin_modules;
extern void initialize_builtin_modules();
extern void destroy_builtin_modules();
extern void register_builtin_csgops();
extern void register_builtin_transform();
+extern void register_builtin_color();
extern void register_builtin_primitives();
extern void register_builtin_surface();
extern void register_builtin_control();
diff --git a/src/cgal.h b/src/cgal.h
index f9161cc..e0e246c 100644
--- a/src/cgal.h
+++ b/src/cgal.h
@@ -14,87 +14,22 @@
#include <CGAL/Polygon_2.h>
#include <CGAL/Polygon_with_holes_2.h>
-typedef CGAL::Extended_cartesian<CGAL::Gmpq> CGAL_Kernel2;
+typedef CGAL::Gmpq NT;
+typedef CGAL::Extended_cartesian<NT> CGAL_Kernel2;
typedef CGAL::Nef_polyhedron_2<CGAL_Kernel2> CGAL_Nef_polyhedron2;
typedef CGAL_Kernel2::Aff_transformation_2 CGAL_Aff_transformation2;
-typedef CGAL::Cartesian<CGAL::Gmpq> CGAL_Kernel3;
-typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron;
-typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS;
-typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder;
-typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3;
-typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;
-typedef CGAL_Nef_polyhedron3::Vector_3 CGAL_Vector;
-typedef CGAL_Nef_polyhedron3::Plane_3 CGAL_Plane;
-typedef CGAL_Nef_polyhedron3::Point_3 CGAL_Point;
typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2;
typedef CGAL::Polygon_2<CGAL_ExactKernel2> CGAL_Poly2;
typedef CGAL::Polygon_with_holes_2<CGAL_ExactKernel2> CGAL_Poly2h;
-struct CGAL_Nef_polyhedron
-{
- int dim;
- CGAL_Nef_polyhedron2 p2;
- CGAL_Nef_polyhedron3 p3;
-
- CGAL_Nef_polyhedron() {
- dim = 0;
- }
-
- CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron2 &p) {
- dim = 2;
- p2 = p;
- }
-
- CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron3 &p) {
- dim = 3;
- p3 = p;
- }
-
- CGAL_Nef_polyhedron& operator+=(const CGAL_Nef_polyhedron &other) {
- if (other.dim == 2) {
- this->p2 += other.p2;
- this->dim = 2;
- }
- if (other.dim == 3) {
- this->p3 += other.p3;
- this->dim = 3;
- }
- return *this;
- }
-
- CGAL_Nef_polyhedron& operator*=(const CGAL_Nef_polyhedron &other) {
- if (other.dim == 2) {
- this->p2 *= other.p2;
- this->dim = 2;
- }
- if (other.dim == 3) {
- this->p3 *= other.p3;
- this->dim = 3;
- }
- return *this;
- }
-
- CGAL_Nef_polyhedron& operator-=(const CGAL_Nef_polyhedron &other) {
- if (other.dim == 2) {
- this->p2 -= other.p2;
- this->dim = 2;
- }
- if (other.dim == 3) {
- this->p3 -= other.p3;
- this->dim = 3;
- }
- return *this;
- }
+typedef CGAL::Cartesian<NT> CGAL_Kernel3;
+typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3;
+typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;
- int weight() {
- if (dim == 2)
- return p2.explorer().number_of_vertices();
- if (dim == 3)
- return p3.number_of_vertices();
- return 0;
- }
-};
+typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron;
+typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS;
+typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder;
#endif /* ENABLE_CGAL */
diff --git a/src/cgaladv.cc b/src/cgaladv.cc
index d754c1b..8ffd626 100644
--- a/src/cgaladv.cc
+++ b/src/cgaladv.cc
@@ -24,25 +24,15 @@
*
*/
+#include "cgaladvnode.h"
#include "module.h"
-#include "node.h"
#include "context.h"
#include "builtin.h"
#include "printutils.h"
-#include "cgal.h"
-
-#ifdef ENABLE_CGAL
-extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b);
-extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b);
-extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a);
-#endif
-
-enum cgaladv_type_e {
- MINKOWSKI,
- GLIDE,
- SUBDIV,
- HULL
-};
+#include <sstream>
+#include <assert.h>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
class CgaladvModule : public AbstractModule
{
@@ -52,38 +42,21 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class CgaladvNode : public AbstractNode
-{
-public:
- Value path;
- QString subdiv_type;
- int convexity, level;
- cgaladv_type_e type;
- CgaladvNode(const ModuleInstantiation *mi, cgaladv_type_e type) : AbstractNode(mi), type(type) {
- convexity = 1;
- }
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
CgaladvNode *node = new CgaladvNode(inst, type);
- QVector<QString> argnames;
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
if (type == MINKOWSKI)
- argnames = QVector<QString>() << "convexity";
+ argnames += "convexity";
if (type == GLIDE)
- argnames = QVector<QString>() << "path" << "convexity";
+ argnames += "path", "convexity";
if (type == SUBDIV)
- argnames = QVector<QString>() << "type" << "level" << "convexity";
+ argnames += "type", "level", "convexity";
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
@@ -113,11 +86,8 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat
if (node->level <= 1)
node->level = 1;
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
@@ -130,129 +100,47 @@ void register_builtin_cgaladv()
builtin_modules["hull"] = new CgaladvModule(HULL);
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron CgaladvNode::render_cgal_nef_polyhedron() const
+std::string CgaladvNode::name() const
{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
+ switch (this->type) {
+ case MINKOWSKI:
+ return "minkowski";
+ break;
+ case GLIDE:
+ return "glide";
+ break;
+ case SUBDIV:
+ return "subdiv";
+ break;
+ case HULL:
+ return "hull";
+ break;
+ default:
+ assert(false);
}
-
- print_messages_push();
- CGAL_Nef_polyhedron N;
-
- if (type == MINKOWSKI)
- {
- bool first = true;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->render_cgal_nef_polyhedron();
- if (N.dim != 0)
- first = false;
- } else {
- CGAL_Nef_polyhedron tmp = v->render_cgal_nef_polyhedron();
- if (N.dim == 3 && tmp.dim == 3) {
- N.p3 = minkowski3(N.p3, tmp.p3);
- }
- if (N.dim == 2 && tmp.dim == 2) {
- N.p2 = minkowski2(N.p2, tmp.p2);
- }
- }
- v->progress_report();
- }
- }
-
- if (type == GLIDE)
- {
- PRINT("WARNING: glide() is not implemented yet!");
- }
-
- if (type == SUBDIV)
- {
- PRINT("WARNING: subdiv() is not implemented yet!");
- }
-
- if (type == HULL)
- {
- std::list<CGAL_Nef_polyhedron2> polys;
- bool all2d = true;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- N = v->render_cgal_nef_polyhedron();
- if (N.dim == 3) {
- //polys.push_back(tmp.p3);
- PRINT("WARNING: hull() is not implemented yet for 3D objects!");
- all2d=false;
- }
- if (N.dim == 2) {
- polys.push_back(N.p2);
- }
- v->progress_report();
- }
-
- if (all2d)
- N.p2 = convexhull2(polys);
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
}
-CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+std::string CgaladvNode::toString() const
{
- if (type == MINKOWSKI)
- return render_csg_term_from_nef(m, highlights, background, "minkowski", this->convexity);
-
- if (type == GLIDE)
- return render_csg_term_from_nef(m, highlights, background, "glide", this->convexity);
-
- if (type == SUBDIV)
- return render_csg_term_from_nef(m, highlights, background, "subdiv", this->convexity);
-
- if (type == HULL)
- return render_csg_term_from_nef(m, highlights, background, "hull", this->convexity);
-
- return NULL;
-}
-
-#else // ENABLE_CGAL
-
-CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!");
- return NULL;
-}
-
-#endif // ENABLE_CGAL
-
-QString CgaladvNode::dump(QString indent) const
-{
- if (dump_cache.isEmpty()) {
- QString text;
- if (type == MINKOWSKI)
- text.sprintf("minkowski(convexity = %d) {\n", this->convexity);
- if (type == GLIDE) {
- text.sprintf(", convexity = %d) {\n", this->convexity);
- text = QString("glide(path = ") + this->path.dump() + text;
- }
- if (type == SUBDIV)
- text.sprintf("subdiv(level = %d, convexity = %d) {\n", this->level, this->convexity);
- if (type == HULL)
- text.sprintf("hull() {\n");
- foreach (AbstractNode *v, this->children)
- text += v->dump(indent + QString("\t"));
- text += indent + "}\n";
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
+ std::stringstream stream;
+
+ stream << this->name();
+ switch (type) {
+ case MINKOWSKI:
+ stream << "(convexity = " << this->convexity << ")";
+ break;
+ case GLIDE:
+ stream << "(path = " << this->path << ", convexity = " << this->convexity << ")";
+ break;
+ case SUBDIV:
+ stream << "(level = " << this->level << ", convexity = " << this->convexity << ")";
+ break;
+ case HULL:
+ stream << "()";
+ break;
+ default:
+ assert(false);
}
- return dump_cache;
-}
+ return stream.str();
+}
diff --git a/src/cgaladv_convexhull2.cc b/src/cgaladv_convexhull2.cc
index 448dd4b..492df3c 100644
--- a/src/cgaladv_convexhull2.cc
+++ b/src/cgaladv_convexhull2.cc
@@ -29,16 +29,15 @@
#include "cgal.h"
#include <CGAL/convex_hull_2.h>
-extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a);
extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p);
-CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a)
+CGAL_Nef_polyhedron2 *convexhull2(std::list<CGAL_Nef_polyhedron2*> a)
{
std::list<CGAL_Nef_polyhedron2::Point> points;
- std::list<CGAL_Nef_polyhedron2>::iterator i;
+ std::list<CGAL_Nef_polyhedron2*>::iterator i;
for (i=a.begin(); i!=a.end(); i++) {
- CGAL_Poly2 ap=nef2p2(*i);
+ CGAL_Poly2 ap=nef2p2(**i);
for (size_t j=0;j<ap.size();j++) {
double x=to_double(ap[j].x()),y=to_double(ap[j].y());
CGAL_Nef_polyhedron2::Point p=CGAL_Nef_polyhedron2::Point(x,y);
@@ -49,7 +48,7 @@ CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a)
std::list<CGAL_Nef_polyhedron2::Point> result;
CGAL::convex_hull_2(points.begin(),points.end(),std::back_inserter(result));
- return CGAL_Nef_polyhedron2(result.begin(),result.end(),CGAL_Nef_polyhedron2::INCLUDED);
+ return new CGAL_Nef_polyhedron2(result.begin(),result.end(),CGAL_Nef_polyhedron2::INCLUDED);
}
#endif
diff --git a/src/cgaladv_minkowski2.cc b/src/cgaladv_minkowski2.cc
index 2020ab9..f08e7d6 100644
--- a/src/cgaladv_minkowski2.cc
+++ b/src/cgaladv_minkowski2.cc
@@ -34,7 +34,6 @@
#include <CGAL/minkowski_sum_2.h>
-extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b);
extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p);
//-----------------------------------------------------------------------------
@@ -120,7 +119,7 @@ static CGAL_Nef_polyhedron2 p2nef2(CGAL_Poly2 p2) {
return CGAL_Nef_polyhedron2(points.begin(), points.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
-CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b)
+CGAL_Nef_polyhedron2 minkowski2(const CGAL_Nef_polyhedron2 &a, const CGAL_Nef_polyhedron2 &b)
{
CGAL_Poly2 ap = nef2p2(a), bp = nef2p2(b);
diff --git a/src/cgaladv_minkowski3.cc b/src/cgaladv_minkowski3.cc
deleted file mode 100644
index f270de2..0000000
--- a/src/cgaladv_minkowski3.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
- *
- */
-
-#ifdef ENABLE_CGAL
-
-#include "node.h"
-#include "printutils.h"
-#include "cgal.h"
-
-#include <CGAL/minkowski_sum_3.h>
-
-extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b);
-
-CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b)
-{
- return CGAL::minkowski_sum_3(a, b);
-}
-
-#endif
diff --git a/src/cgaladvnode.h b/src/cgaladvnode.h
new file mode 100644
index 0000000..a3f8bea
--- /dev/null
+++ b/src/cgaladvnode.h
@@ -0,0 +1,34 @@
+#ifndef CGALADVNODE_H_
+#define CGALADVNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+#include "value.h"
+
+enum cgaladv_type_e {
+ MINKOWSKI,
+ GLIDE,
+ SUBDIV,
+ HULL
+};
+
+class CgaladvNode : public AbstractNode
+{
+public:
+ CgaladvNode(const ModuleInstantiation *mi, cgaladv_type_e type) : AbstractNode(mi), type(type) {
+ convexity = 1;
+ }
+ virtual ~CgaladvNode() { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const;
+
+ Value path;
+ std::string subdiv_type;
+ int convexity, level;
+ cgaladv_type_e type;
+};
+
+#endif
diff --git a/src/cgalfwd.h b/src/cgalfwd.h
new file mode 100644
index 0000000..3fad608
--- /dev/null
+++ b/src/cgalfwd.h
@@ -0,0 +1,45 @@
+#ifndef CGALFWD_H_
+#define CGALFWD_H_
+
+#ifndef CGAL_FORWARD
+#include "cgal.h"
+#else
+#ifdef ENABLE_CGAL
+
+#include <memory>
+
+namespace CGAL {
+ class Gmpq;
+ template <class T> class Extended_cartesian;
+ class HDS_items;
+ template <class A, typename Items_, typename Mark_> class Nef_polyhedron_2;
+}
+typedef CGAL::Gmpq NT;
+typedef CGAL::Extended_cartesian<NT> CGAL_Kernel2;
+typedef CGAL::Nef_polyhedron_2<CGAL_Kernel2, CGAL::HDS_items, bool> CGAL_Nef_polyhedron2;
+
+namespace CGAL {
+ template <class T> class Cartesian;
+ template<class T> struct Default_items;
+ class SNC_indexed_items;
+ template <typename Kernel_, typename Items_, typename Mark_> class Nef_polyhedron_3;
+}
+typedef CGAL::Cartesian<NT> CGAL_Kernel3;
+typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3, CGAL::SNC_indexed_items, bool> CGAL_Nef_polyhedron3;
+
+namespace CGAL {
+#ifndef CGAL_ALLOCATOR
+# define CGAL_ALLOCATOR(T) std::allocator< T >
+#endif
+ class HalfedgeDS_items_2;
+ template <class Traits_, class HalfedgeDSItems, class Alloc> class HalfedgeDS_default;
+ class Polyhedron_items_3;
+ template <class PolyhedronTraits_3, class PolyhedronItems_3, class T_HDS, class Alloc> class Polyhedron_3;
+}
+typedef CGAL::Polyhedron_3<CGAL_Kernel3, CGAL::Polyhedron_items_3, CGAL::HalfedgeDS_default<CGAL_Kernel3, CGAL::HalfedgeDS_items_2, CGAL_ALLOCATOR(int)>, CGAL_ALLOCATOR(int)> CGAL_Polyhedron;
+
+#endif /* ENABLE_CGAL */
+
+#endif
+
+#endif
diff --git a/src/cgalutils.cc b/src/cgalutils.cc
new file mode 100644
index 0000000..79e9f1f
--- /dev/null
+++ b/src/cgalutils.cc
@@ -0,0 +1,147 @@
+#ifdef ENABLE_CGAL
+
+#include "cgalutils.h"
+#include "polyset.h"
+#include "printutils.h"
+
+#include "cgal.h"
+#include <CGAL/assertions_behaviour.h>
+#include <CGAL/exceptions.h>
+
+PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p)
+{
+ PolySet *ps = new PolySet();
+
+ typedef CGAL_Polyhedron::Vertex Vertex;
+ typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
+ typedef CGAL_Polyhedron::Facet_const_iterator FCI;
+ typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC;
+
+ for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) {
+ HFCC hc = fi->facet_begin();
+ HFCC hc_end = hc;
+ Vertex v1, v2, v3;
+ v1 = *VCI((hc++)->vertex());
+ v3 = *VCI((hc++)->vertex());
+ do {
+ v2 = v3;
+ v3 = *VCI((hc++)->vertex());
+ double x1 = CGAL::to_double(v1.point().x());
+ double y1 = CGAL::to_double(v1.point().y());
+ double z1 = CGAL::to_double(v1.point().z());
+ double x2 = CGAL::to_double(v2.point().x());
+ double y2 = CGAL::to_double(v2.point().y());
+ double z2 = CGAL::to_double(v2.point().z());
+ double x3 = CGAL::to_double(v3.point().x());
+ double y3 = CGAL::to_double(v3.point().y());
+ double z3 = CGAL::to_double(v3.point().z());
+ ps->append_poly();
+ ps->append_vertex(x1, y1, z1);
+ ps->append_vertex(x2, y2, z2);
+ ps->append_vertex(x3, y3, z3);
+ } while (hc != hc_end);
+ }
+ return ps;
+}
+
+#undef GEN_SURFACE_DEBUG
+
+class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS>
+{
+public:
+ typedef CGAL_HDS::Vertex::Point CGALPoint;
+
+ const PolySet &ps;
+ CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { }
+
+ void operator()(CGAL_HDS& hds)
+ {
+ CGAL_Polybuilder B(hds, true);
+
+ std::vector<CGALPoint> vertices;
+ Grid3d<int> vertices_idx(GRID_FINE);
+
+ for (size_t i = 0; i < ps.polygons.size(); i++) {
+ const PolySet::Polygon *poly = &ps.polygons[i];
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+ if (!vertices_idx.has(p[0], p[1], p[2])) {
+ vertices_idx.data(p[0], p[1], p[2]) = vertices.size();
+ vertices.push_back(CGALPoint(p[0], p[1], p[2]));
+ }
+ }
+ }
+
+ B.begin_surface(vertices.size(), ps.polygons.size());
+#ifdef GEN_SURFACE_DEBUG
+ printf("=== CGAL Surface ===\n");
+#endif
+
+ for (size_t i = 0; i < vertices.size(); i++) {
+ const CGALPoint &p = vertices[i];
+ B.add_vertex(p);
+#ifdef GEN_SURFACE_DEBUG
+ printf("%d: %f %f %f\n", i, p[0], p[1], p[2]);
+#endif
+ }
+
+ for (size_t i = 0; i < ps.polygons.size(); i++) {
+ const PolySet::Polygon *poly = &ps.polygons[i];
+ QHash<int,int> fc;
+ bool facet_is_degenerated = false;
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+ int v = vertices_idx.data(p[0], p[1], p[2]);
+ if (fc[v]++ > 0)
+ facet_is_degenerated = true;
+ }
+
+ if (!facet_is_degenerated)
+ B.begin_facet();
+#ifdef GEN_SURFACE_DEBUG
+ printf("F:");
+#endif
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+#ifdef GEN_SURFACE_DEBUG
+ printf(" %d (%f,%f,%f)", vertices_idx.data(p[0], p[1], p[2]), p[0], p[1], p[2]);
+#endif
+ if (!facet_is_degenerated)
+ B.add_vertex_to_facet(vertices_idx.data(p[0], p[1], p[2]));
+ }
+#ifdef GEN_SURFACE_DEBUG
+ if (facet_is_degenerated)
+ printf(" (degenerated)");
+ printf("\n");
+#endif
+ if (!facet_is_degenerated)
+ B.end_facet();
+ }
+
+#ifdef GEN_SURFACE_DEBUG
+ printf("====================\n");
+#endif
+ B.end_surface();
+
+ #undef PointKey
+ }
+};
+
+CGAL_Polyhedron *createPolyhedronFromPolySet(const PolySet &ps)
+{
+ CGAL_Polyhedron *P = NULL;
+ CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
+ try {
+ P = new CGAL_Polyhedron;
+ CGAL_Build_PolySet builder(ps);
+ P->delegate(builder);
+ }
+ catch (CGAL::Assertion_exception e) {
+ PRINTF("CGAL error: %s", e.what());
+ CGAL::set_error_behaviour(old_behaviour);
+ }
+ return P;
+}
+
+#endif /* ENABLE_CGAL */
+
diff --git a/src/cgalutils.h b/src/cgalutils.h
new file mode 100644
index 0000000..a249697
--- /dev/null
+++ b/src/cgalutils.h
@@ -0,0 +1,9 @@
+#ifndef CGALUTILS_H_
+#define CGALUTILS_H_
+
+#include <cgalfwd.h>
+
+class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p);
+CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps);
+
+#endif
diff --git a/src/color.cc b/src/color.cc
new file mode 100644
index 0000000..a65a8e4
--- /dev/null
+++ b/src/color.cc
@@ -0,0 +1,108 @@
+/*
+ * 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 "colornode.h"
+#include "module.h"
+#include "context.h"
+#include "builtin.h"
+#include "printutils.h"
+#include "visitor.h"
+#include <sstream>
+#include <assert.h>
+#include <QColor>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
+
+class ColorModule : public AbstractModule
+{
+public:
+ ColorModule() { }
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+};
+
+using std::string;
+
+AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+{
+ ColorNode *node = new ColorNode(inst);
+
+ node->color[0] = node->color[1] = node->color[2] = -1.0;
+ node->color[3] = 1.0;
+
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
+
+ argnames += "c", "alpha";
+
+ Context c(ctx);
+ c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+
+ Value v = c.lookup_variable("c");
+ if (v.type == Value::VECTOR) {
+ for (size_t i = 0; i < 4; i++)
+ node->color[i] = i < v.vec.size() ? v.vec[i]->num : 1.0;
+ } else if (v.type == Value::STRING) {
+ std::string colorname = v.text;
+ QColor color;
+ color.setNamedColor(QString::fromStdString(colorname));
+ if (color.isValid()) {
+ node->color[0] = color.redF();
+ node->color[1] = color.greenF();
+ node->color[2] = color.blueF();
+ } else {
+ PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see", colorname.c_str());
+ PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");
+ }
+ }
+ Value alpha = c.lookup_variable("alpha");
+ if (alpha.type == Value::NUMBER) {
+ node->color[3] = alpha.num;
+ }
+
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
+
+ return node;
+}
+
+string ColorNode::toString() const
+{
+ std::stringstream stream;
+
+ stream << "color([" << this->color[0] << ", " << this->color[1] << ", " << this->color[2] << ", " << this->color[3] << "])";
+
+ return stream.str();
+}
+
+string ColorNode::name() const
+{
+ return "color";
+}
+
+void register_builtin_color()
+{
+ builtin_modules["color"] = new ColorModule();
+}
diff --git a/src/colornode.h b/src/colornode.h
new file mode 100644
index 0000000..b41e2a9
--- /dev/null
+++ b/src/colornode.h
@@ -0,0 +1,20 @@
+#ifndef COLORNODE_H_
+#define COLORNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+class ColorNode : public AbstractNode
+{
+public:
+ ColorNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const;
+
+ double color[4];
+};
+
+#endif
diff --git a/src/context.cc b/src/context.cc
index bfe9eb6..b4983f6 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -28,19 +28,36 @@
#include "expression.h"
#include "function.h"
#include "module.h"
+#include "builtin.h"
#include "printutils.h"
#include <QFileInfo>
#include <QDir>
+#include <boost/foreach.hpp>
-Context::Context(const Context *parent)
+std::vector<const Context*> Context::ctx_stack;
+
+/*!
+ Initializes this context. Optionally initializes a context for an external library
+*/
+Context::Context(const Context *parent, const Module *library)
+ : parent(parent), inst_p(NULL)
{
- this->parent = parent;
- functions_p = NULL;
- modules_p = NULL;
- usedlibs_p = NULL;
- inst_p = NULL;
+ ctx_stack.push_back(this);
if (parent) document_path = parent->document_path;
- ctx_stack.append(this);
+ if (library) {
+ this->functions_p = &library->functions;
+ this->modules_p = &library->modules;
+ this->usedlibs_p = &library->usedlibs;
+ for (size_t j = 0; j < library->assignments_var.size(); j++) {
+ this->set_variable(library->assignments_var[j],
+ library->assignments_expr[j]->evaluate(this));
+ }
+ }
+ else {
+ functions_p = NULL;
+ modules_p = NULL;
+ usedlibs_p = NULL;
+ }
}
Context::~Context()
@@ -48,16 +65,22 @@ Context::~Context()
ctx_stack.pop_back();
}
-void Context::args(const QVector<QString> &argnames, const QVector<Expression*> &argexpr,
- const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues)
+/*!
+ Initialize context from argument lists (function call/module instantiation)
+ */
+void Context::args(const std::vector<std::string> &argnames,
+ const std::vector<Expression*> &argexpr,
+ const std::vector<std::string> &call_argnames,
+ const std::vector<Value> &call_argvalues)
{
- for (int i=0; i<argnames.size(); i++) {
- set_variable(argnames[i], i < argexpr.size() && argexpr[i] ? argexpr[i]->evaluate(this->parent) : Value());
+ for (size_t i=0; i<argnames.size(); i++) {
+ set_variable(argnames[i], i < argexpr.size() && argexpr[i] ?
+ argexpr[i]->evaluate(this->parent) : Value());
}
- int posarg = 0;
- for (int i=0; i<call_argnames.size(); i++) {
- if (call_argnames[i].isEmpty()) {
+ size_t posarg = 0;
+ for (size_t i=0; i<call_argnames.size(); i++) {
+ if (call_argnames[i].empty()) {
if (posarg < argnames.size())
set_variable(argnames[posarg++], call_argvalues[i]);
} else {
@@ -66,90 +89,107 @@ void Context::args(const QVector<QString> &argnames, const QVector<Expression*>
}
}
-QVector<const Context*> Context::ctx_stack;
+void Context::set_variable(const std::string &name, const Value &value)
+{
+ if (name[0] == '$')
+ this->config_variables[name] = value;
+ else
+ this->variables[name] = value;
+}
-void Context::set_variable(QString name, Value value)
+void Context::set_constant(const std::string &name, const Value &value)
{
- if (name.startsWith("$"))
- config_variables[name] = value;
+ if (this->constants.find(name) != this->constants.end())
+ PRINTF("WARNING: Attempt to modify constant '%s'.",name.c_str());
else
- variables[name] = value;
+ this->constants[name] = value;
}
-Value Context::lookup_variable(QString name, bool silent) const
+Value Context::lookup_variable(const std::string &name, bool silent) const
{
- if (name.startsWith("$")) {
+ if (name[0] == '$') {
for (int i = ctx_stack.size()-1; i >= 0; i--) {
- if (ctx_stack[i]->config_variables.contains(name))
- return ctx_stack[i]->config_variables[name];
+ const ValueMap &confvars = ctx_stack[i]->config_variables;
+ if (confvars.find(name) != confvars.end())
+ return confvars.find(name)->second;
}
return Value();
}
- if (variables.contains(name))
- return variables[name];
- if (parent)
- return parent->lookup_variable(name, silent);
+ if (!this->parent && this->constants.find(name) != this->constants.end())
+ return this->constants.find(name)->second;
+ if (this->variables.find(name) != this->variables.end())
+ return this->variables.find(name)->second;
+ if (this->parent)
+ return this->parent->lookup_variable(name, silent);
if (!silent)
- PRINTA("WARNING: Ignoring unknown variable '%1'.", name);
+ PRINTF("WARNING: Ignoring unknown variable '%s'.", name.c_str());
return Value();
}
-Value Context::evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const
+Value Context::evaluate_function(const std::string &name,
+ const std::vector<std::string> &argnames,
+ const std::vector<Value> &argvalues) const
{
- if (functions_p && functions_p->contains(name))
- return functions_p->value(name)->evaluate(this, argnames, argvalues);
- if (usedlibs_p) {
- QHashIterator<QString, Module*> i(*usedlibs_p);
- while (i.hasNext()) {
- i.next();
- if (i.value()->functions.contains(name)) {
- Module *lib = i.value();
- Context ctx(parent);
- ctx.functions_p = &lib->functions;
- ctx.modules_p = &lib->modules;
- ctx.usedlibs_p = &lib->usedlibs;
- for (int j = 0; j < lib->assignments_var.size(); j++) {
- ctx.set_variable(lib->assignments_var[j], lib->assignments_expr[j]->evaluate(&ctx));
- }
- return i.value()->functions.value(name)->evaluate(&ctx, argnames, argvalues);
+ if (this->functions_p && this->functions_p->find(name) != this->functions_p->end())
+ return this->functions_p->find(name)->second->evaluate(this, argnames, argvalues);
+ if (this->usedlibs_p) {
+ BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
+ if (m.second->functions.find(name) != m.second->functions.end()) {
+ Context ctx(this->parent, m.second);
+ return m.second->functions[name]->evaluate(&ctx, argnames, argvalues);
}
}
}
- if (parent)
- return parent->evaluate_function(name, argnames, argvalues);
- PRINTA("WARNING: Ignoring unkown function '%1'.", name);
+ if (this->parent)
+ return this->parent->evaluate_function(name, argnames, argvalues);
+ PRINTF("WARNING: Ignoring unknown function '%s'.", name.c_str());
return Value();
}
-AbstractNode *Context::evaluate_module(const ModuleInstantiation *inst) const
+AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
{
- if (modules_p && modules_p->contains(inst->modname))
- return modules_p->value(inst->modname)->evaluate(this, inst);
- if (usedlibs_p) {
- QHashIterator<QString, Module*> i(*usedlibs_p);
- while (i.hasNext()) {
- i.next();
- if (i.value()->modules.contains(inst->modname)) {
- Module *lib = i.value();
- Context ctx(parent);
- ctx.functions_p = &lib->functions;
- ctx.modules_p = &lib->modules;
- ctx.usedlibs_p = &lib->usedlibs;
- for (int j = 0; j < lib->assignments_var.size(); j++) {
- ctx.set_variable(lib->assignments_var[j], lib->assignments_expr[j]->evaluate(&ctx));
- }
- return i.value()->modules.value(inst->modname)->evaluate(&ctx, inst);
+ if (this->modules_p && this->modules_p->find(inst.modname) != this->modules_p->end()) {
+ AbstractModule *m = this->modules_p->find(inst.modname)->second;
+ if (m == builtin_modules["dxf_linear_extrude"]) {
+ PRINTF("DEPRECATED: The dxf_linear_extrude() module will be removed in future releases. Use a linear_extrude() instead.");
+ }
+ else if (m == builtin_modules["dxf_rotate_extrude"]) {
+ PRINTF("DEPRECATED: The dxf_rotate_extrude() module will be removed in future releases. Use a rotate_extrude() instead.");
+ }
+ else if (m == builtin_modules["import_stl"]) {
+ PRINTF("DEPRECATED: The import_stl() module will be removed in future releases. Use import() instead.");
+ }
+ else if (m == builtin_modules["import_dxf"]) {
+ PRINTF("DEPRECATED: The import_dxf() module will be removed in future releases. Use import() instead.");
+ }
+ else if (m == builtin_modules["import_off"]) {
+ PRINTF("DEPRECATED: The import_off() module will be removed in future releases. Use import() instead.");
+ }
+ return m->evaluate(this, &inst);
+ }
+ if (this->usedlibs_p) {
+ BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
+ if (m.second->modules.find(inst.modname) != m.second->modules.end()) {
+ Context ctx(this->parent, m.second);
+ return m.second->modules[inst.modname]->evaluate(&ctx, &inst);
}
}
}
- if (parent)
- return parent->evaluate_module(inst);
- PRINTA("WARNING: Ignoring unkown module '%1'.", inst->modname);
+ if (this->parent) return this->parent->evaluate_module(inst);
+ PRINTF("WARNING: Ignoring unknown module '%s'.", inst.modname.c_str());
return NULL;
}
-QString Context::get_absolute_path(const QString &filename) const
+/*!
+ Returns the absolute path to the given filename, unless it's empty.
+ */
+std::string Context::getAbsolutePath(const std::string &filename) const
{
- return QFileInfo(QDir(this->document_path), filename).absoluteFilePath();
+ if (!filename.empty()) {
+ return QFileInfo(QDir(QString::fromStdString(this->document_path)),
+ QString::fromStdString(filename)).absoluteFilePath().toStdString();
+ }
+ else {
+ return filename;
+ }
}
-
diff --git a/src/context.h b/src/context.h
index d5be745..f085e01 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,36 +1,52 @@
#ifndef CONTEXT_H_
#define CONTEXT_H_
-#include <QHash>
-#include <QString>
+#include <string>
+#include <vector>
+#include <boost/unordered_map.hpp>
#include "value.h"
+using boost::unordered_map;
+
class Context
{
public:
- const Context *parent;
- QHash<QString, Value> variables;
- QHash<QString, Value> config_variables;
- const QHash<QString, class AbstractFunction*> *functions_p;
- const QHash<QString, class AbstractModule*> *modules_p;
- const QHash<QString, class Module*> *usedlibs_p;
- const class ModuleInstantiation *inst_p;
- QString document_path;
-
- static QVector<const Context*> ctx_stack;
-
- Context(const Context *parent = NULL);
+ Context(const Context *parent = NULL, const class Module *library = NULL);
~Context();
- void args(const QVector<QString> &argnames, const QVector<class Expression*> &argexpr, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues);
+ void args(const std::vector<std::string> &argnames,
+ const std::vector<class Expression*> &argexpr,
+ const std::vector<std::string> &call_argnames,
+ const std::vector<Value> &call_argvalues);
- void set_variable(QString name, Value value);
- Value lookup_variable(QString name, bool silent = false) const;
+ void set_variable(const std::string &name, const Value &value);
+ void set_constant(const std::string &name, const Value &value);
- QString get_absolute_path(const QString &filename) const;
+ Value lookup_variable(const std::string &name, bool silent = false) const;
+ Value evaluate_function(const std::string &name,
+ const std::vector<std::string> &argnames,
+ const std::vector<Value> &argvalues) const;
+ class AbstractNode *evaluate_module(const class ModuleInstantiation &inst) const;
- Value evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const;
- class AbstractNode *evaluate_module(const ModuleInstantiation *inst) const;
+ void setDocumentPath(const std::string &path) { this->document_path = path; }
+ std::string getAbsolutePath(const std::string &filename) const;
+
+public:
+ const Context *parent;
+ const unordered_map<std::string, class AbstractFunction*> *functions_p;
+ const unordered_map<std::string, class AbstractModule*> *modules_p;
+ typedef unordered_map<std::string, class Module*> ModuleContainer;
+ const ModuleContainer *usedlibs_p;
+ const ModuleInstantiation *inst_p;
+
+ static std::vector<const Context*> ctx_stack;
+
+private:
+ typedef unordered_map<std::string, Value> ValueMap;
+ ValueMap constants;
+ ValueMap variables;
+ ValueMap config_variables;
+ std::string document_path;
};
#endif
diff --git a/src/control.cc b/src/control.cc
index ae1d654..c39b74a 100644
--- a/src/control.cc
+++ b/src/control.cc
@@ -29,6 +29,7 @@
#include "context.h"
#include "builtin.h"
#include "printutils.h"
+#include <sstream>
enum control_type_e {
CHILD,
@@ -47,11 +48,14 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-void for_eval(AbstractNode *node, int l, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues, const QVector<ModuleInstantiation*> arg_children, const Context *arg_context)
+void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l,
+ const std::vector<std::string> &call_argnames,
+ const std::vector<Value> &call_argvalues,
+ const Context *arg_context)
{
if (call_argnames.size() > l) {
- QString it_name = call_argnames[l];
- Value it_values = call_argvalues[l];
+ const std::string &it_name = call_argnames[l];
+ const Value &it_values = call_argvalues[l];
Context c(arg_context);
if (it_values.type == Value::RANGE) {
double range_begin = it_values.range_begin;
@@ -65,25 +69,23 @@ void for_eval(AbstractNode *node, int l, const QVector<QString> &call_argnames,
if (range_step > 0 && (range_begin-range_end)/range_step < 10000) {
for (double i = range_begin; i <= range_end; i += range_step) {
c.set_variable(it_name, Value(i));
- for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c);
+ for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
}
}
}
else if (it_values.type == Value::VECTOR) {
- for (int i = 0; i < it_values.vec.size(); i++) {
+ for (size_t i = 0; i < it_values.vec.size(); i++) {
c.set_variable(it_name, *it_values.vec[i]);
- for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c);
+ for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
}
}
- else {
- for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c);
- }
- } else {
- foreach (ModuleInstantiation *v, arg_children) {
- AbstractNode *n = v->evaluate(arg_context);
- if (n != NULL)
- node->children.append(n);
+ else if (it_values.type != Value::UNDEFINED) {
+ c.set_variable(it_name, it_values);
+ for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
}
+ } else if (l > 0) {
+ std::vector<AbstractNode *> evaluatednodes = inst.evaluateChildren(arg_context);
+ node.children.insert(node.children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
}
@@ -91,7 +93,7 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation
{
if (type == CHILD)
{
- int n = 0;
+ size_t n = 0;
if (inst->argvalues.size() > 0) {
double v;
if (inst->argvalues[0].getnum(v))
@@ -118,52 +120,42 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation
if (type == ECHO)
{
- QString msg = QString("ECHO: ");
- for (int i = 0; i < inst->argnames.size(); i++) {
- if (i > 0)
- msg += QString(", ");
- if (!inst->argnames[i].isEmpty())
- msg += inst->argnames[i] + QString(" = ");
- msg += inst->argvalues[i].dump();
+ std::stringstream msg;
+ msg << "ECHO: ";
+ for (size_t i = 0; i < inst->argnames.size(); i++) {
+ if (i > 0) msg << ", ";
+ if (!inst->argnames[i].empty()) msg << inst->argnames[i] << " = ";
+ msg << inst->argvalues[i];
}
- PRINT(msg);
+ PRINTF("%s", msg.str().c_str());
}
if (type == ASSIGN)
{
Context c(inst->ctx);
- for (int i = 0; i < inst->argnames.size(); i++) {
- if (!inst->argnames[i].isEmpty())
+ for (size_t i = 0; i < inst->argnames.size(); i++) {
+ if (!inst->argnames[i].empty())
c.set_variable(inst->argnames[i], inst->argvalues[i]);
}
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(&c);
- if (n != NULL)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(&c);
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
if (type == FOR || type == INT_FOR)
{
- for_eval(node, 0, inst->argnames, inst->argvalues, inst->children, inst->ctx);
+ for_eval(*node, *inst, 0, inst->argnames, inst->argvalues, inst->ctx);
}
if (type == IF)
{
const IfElseModuleInstantiation *ifelse = dynamic_cast<const IfElseModuleInstantiation*>(inst);
if (ifelse->argvalues.size() > 0 && ifelse->argvalues[0].type == Value::BOOL && ifelse->argvalues[0].b) {
- foreach (ModuleInstantiation *v, ifelse->children) {
- AbstractNode *n = v->evaluate(ifelse->ctx);
- if (n != NULL)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
else {
- foreach (ModuleInstantiation *v, ifelse->else_children) {
- AbstractNode *n = v->evaluate(ifelse->ctx);
- if (n != NULL)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateElseChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
}
diff --git a/src/csgnode.h b/src/csgnode.h
new file mode 100644
index 0000000..2e1d9fb
--- /dev/null
+++ b/src/csgnode.h
@@ -0,0 +1,25 @@
+#ifndef CSGNODE_H_
+#define CSGNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+enum csg_type_e {
+ CSG_TYPE_UNION,
+ CSG_TYPE_DIFFERENCE,
+ CSG_TYPE_INTERSECTION
+};
+
+class CsgNode : public AbstractNode
+{
+public:
+ csg_type_e type;
+ CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const;
+};
+
+#endif
diff --git a/src/csgops.cc b/src/csgops.cc
index 8c295f9..98d68c7 100644
--- a/src/csgops.cc
+++ b/src/csgops.cc
@@ -24,22 +24,14 @@
*
*/
+#include "csgnode.h"
+
#include "module.h"
-#include "node.h"
#include "csgterm.h"
#include "builtin.h"
#include "printutils.h"
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-# include <CGAL/assertions_behaviour.h>
-# include <CGAL/exceptions.h>
-#endif
-
-enum csg_type_e {
- CSG_TYPE_UNION,
- CSG_TYPE_DIFFERENCE,
- CSG_TYPE_INTERSECTION
-};
+#include <sstream>
+#include <assert.h>
class CsgModule : public AbstractModule
{
@@ -49,128 +41,34 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class CsgNode : public AbstractNode
-{
-public:
- csg_type_e type;
- CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { }
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst) const
{
CsgNode *node = new CsgNode(inst, type);
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n != NULL)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron CsgNode::render_cgal_nef_polyhedron() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- bool first = true;
- CGAL_Nef_polyhedron N;
- try {
- foreach (AbstractNode *v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->render_cgal_nef_polyhedron();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- if (type == CSG_TYPE_UNION) {
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- } else if (type == CSG_TYPE_DIFFERENCE) {
- N.p2 -= v->render_cgal_nef_polyhedron().p2;
- } else if (type == CSG_TYPE_INTERSECTION) {
- N.p2 *= v->render_cgal_nef_polyhedron().p2;
- }
- } else if (N.dim == 3) {
- if (type == CSG_TYPE_UNION) {
- N.p3 += v->render_cgal_nef_polyhedron().p3;
- } else if (type == CSG_TYPE_DIFFERENCE) {
- N.p3 -= v->render_cgal_nef_polyhedron().p3;
- } else if (type == CSG_TYPE_INTERSECTION) {
- N.p3 *= v->render_cgal_nef_polyhedron().p3;
- }
- }
- v->progress_report();
- }
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("CGAL error: %s", e.what());
- }
- CGAL::set_error_behaviour(old_behaviour);
-
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
-#endif /* ENABLE_CGAL */
-
-CSGTerm *CsgNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+std::string CsgNode::toString() const
{
- CSGTerm *t1 = NULL;
- foreach (AbstractNode *v, children) {
- CSGTerm *t2 = v->render_csg_term(m, highlights, background);
- if (t2 && !t1) {
- t1 = t2;
- } else if (t2 && t1) {
- if (type == CSG_TYPE_UNION) {
- t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
- } else if (type == CSG_TYPE_DIFFERENCE) {
- t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2);
- } else if (type == CSG_TYPE_INTERSECTION) {
- t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
- }
- }
- }
- if (t1 && modinst->tag_highlight && highlights)
- highlights->append(t1->link());
- if (t1 && modinst->tag_background && background) {
- background->append(t1);
- return NULL;
- }
- return t1;
+ return this->name() + "()";
}
-QString CsgNode::dump(QString indent) const
+std::string CsgNode::name() const
{
- if (dump_cache.isEmpty()) {
- QString text = indent + QString("n%1: ").arg(idx);
- if (type == CSG_TYPE_UNION)
- text += "union() {\n";
- if (type == CSG_TYPE_DIFFERENCE)
- text += "difference() {\n";
- if (type == CSG_TYPE_INTERSECTION)
- text += "intersection() {\n";
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- ((AbstractNode*)this)->dump_cache = text + indent + "}\n";
+ switch (this->type) {
+ case CSG_TYPE_UNION:
+ return "union";
+ break;
+ case CSG_TYPE_DIFFERENCE:
+ return "difference";
+ break;
+ case CSG_TYPE_INTERSECTION:
+ return "intersection";
+ break;
+ default:
+ assert(false);
}
- return dump_cache;
}
void register_builtin_csgops()
diff --git a/src/csgterm.cc b/src/csgterm.cc
index 6dd3bd5..f24a41a 100644
--- a/src/csgterm.cc
+++ b/src/csgterm.cc
@@ -26,16 +26,36 @@
#include "csgterm.h"
#include "polyset.h"
+#include <sstream>
-CSGTerm::CSGTerm(PolySet *polyset, double m[20], QString label)
+/*!
+ \class CSGTerm
+
+ A CSGTerm is either a "primitive" or a CSG operation with two
+ children terms. A primitive in this context is any PolySet, which
+ may or may not have a subtree which is already evaluated (e.g. using
+ the render() module).
+
+ */
+
+/*!
+ \class CSGChain
+
+ A CSGChain is just a vector of primitives, each having a CSG type associated with it.
+ It's created by importing a CSGTerm tree.
+
+ */
+
+
+CSGTerm::CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], const std::string &label)
{
this->type = TYPE_PRIMITIVE;
this->polyset = polyset;
this->label = label;
this->left = NULL;
this->right = NULL;
- for (int i = 0; i < 20; i++)
- this->m[i] = m[i];
+ for (int i = 0; i < 16; i++) this->m[i] = matrix[i];
+ for (int i = 0; i < 4; i++) this->color[i] = color[i];
refcounter = 1;
}
@@ -157,56 +177,83 @@ void CSGTerm::unlink()
}
}
-QString CSGTerm::dump()
+std::string CSGTerm::dump()
{
+ std::stringstream dump;
+
if (type == TYPE_UNION)
- return QString("(%1 + %2)").arg(left->dump(), right->dump());
- if (type == TYPE_INTERSECTION)
- return QString("(%1 * %2)").arg(left->dump(), right->dump());
- if (type == TYPE_DIFFERENCE)
- return QString("(%1 - %2)").arg(left->dump(), right->dump());
- return label;
+ dump << "(" << left->dump() << " + " << right->dump() << ")";
+ else if (type == TYPE_INTERSECTION)
+ dump << "(" << left->dump() << " * " << right->dump() << ")";
+ else if (type == TYPE_DIFFERENCE)
+ dump << "(" << left->dump() << " - " << right->dump() << ")";
+ else
+ dump << this->label;
+
+ return dump.str();
}
CSGChain::CSGChain()
{
}
-void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label)
+void CSGChain::add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, std::string label)
{
- polysets.append(polyset);
- matrices.append(m);
- types.append(type);
- labels.append(label);
+ polysets.push_back(polyset);
+ matrices.push_back(m);
+ colors.push_back(color);
+ types.push_back(type);
+ labels.push_back(label);
}
void CSGChain::import(CSGTerm *term, CSGTerm::type_e type)
{
if (term->type == CSGTerm::TYPE_PRIMITIVE) {
- add(term->polyset, term->m, type, term->label);
+ add(term->polyset, term->m, term->color, type, term->label);
} else {
import(term->left, type);
import(term->right, term->type);
}
}
-QString CSGChain::dump()
+std::string CSGChain::dump()
{
- QString text;
- for (int i = 0; i < types.size(); i++)
+ std::stringstream dump;
+
+ for (size_t i = 0; i < types.size(); i++)
{
if (types[i] == CSGTerm::TYPE_UNION) {
- if (i != 0)
- text += "\n";
- text += "+";
+ if (i != 0) dump << "\n";
+ dump << "+";
}
- if (types[i] == CSGTerm::TYPE_DIFFERENCE)
- text += " -";
- if (types[i] == CSGTerm::TYPE_INTERSECTION)
- text += " *";
- text += labels[i];
+ else if (types[i] == CSGTerm::TYPE_DIFFERENCE)
+ dump << " -";
+ else if (types[i] == CSGTerm::TYPE_INTERSECTION)
+ dump << " *";
+ dump << labels[i];
}
- text += "\n";
- return text;
+ dump << "\n";
+ return dump.str();
}
+BoundingBox CSGChain::getBoundingBox() const
+{
+ BoundingBox bbox;
+ for (size_t i=0;i<polysets.size();i++) {
+ if (types[i] != CSGTerm::TYPE_DIFFERENCE) {
+ BoundingBox psbox = polysets[i]->getBoundingBox();
+ if (!psbox.isNull()) {
+ Eigen::Transform3d t;
+ // Column-major vs. Row-major
+ t.matrix() <<
+ matrices[i][0], matrices[i][4], matrices[i][8], matrices[i][12],
+ matrices[i][1], matrices[i][5], matrices[i][9], matrices[i][13],
+ matrices[i][2], matrices[i][6], matrices[i][10], matrices[i][14],
+ matrices[i][3], matrices[i][7], matrices[i][11], matrices[i][15];
+ bbox.extend(t * psbox.min());
+ bbox.extend(t * psbox.max());
+ }
+ }
+ }
+ return bbox;
+}
diff --git a/src/csgterm.h b/src/csgterm.h
index 35d071d..2a89ef1 100644
--- a/src/csgterm.h
+++ b/src/csgterm.h
@@ -1,8 +1,9 @@
#ifndef CSGTERM_H_
#define CSGTERM_H_
-#include <QString>
-#include <QVector>
+#include <string>
+#include <vector>
+#include "polyset.h"
class CSGTerm
{
@@ -15,14 +16,15 @@ public:
};
type_e type;
- class PolySet *polyset;
- QString label;
+ PolySet *polyset;
+ std::string label;
CSGTerm *left;
CSGTerm *right;
- double m[20];
+ double m[16];
+ double color[4];
int refcounter;
- CSGTerm(PolySet *polyset, double m[20], QString label);
+ CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], const std::string &label);
CSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
CSGTerm *normalize();
@@ -30,22 +32,25 @@ public:
CSGTerm *link();
void unlink();
- QString dump();
+ std::string dump();
};
class CSGChain
{
public:
- QVector<PolySet*> polysets;
- QVector<double*> matrices;
- QVector<CSGTerm::type_e> types;
- QVector<QString> labels;
+ std::vector<PolySet*> polysets;
+ std::vector<double*> matrices;
+ std::vector<double*> colors;
+ std::vector<CSGTerm::type_e> types;
+ std::vector<std::string> labels;
CSGChain();
- void add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label);
+ void add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, std::string label);
void import(CSGTerm *term, CSGTerm::type_e type = CSGTerm::TYPE_UNION);
- QString dump();
+ std::string dump();
+
+ BoundingBox getBoundingBox() const;
};
#endif
diff --git a/src/dxfdata.cc b/src/dxfdata.cc
index 55d00b4..10f3b0c 100644
--- a/src/dxfdata.cc
+++ b/src/dxfdata.cc
@@ -24,24 +24,25 @@
*
*/
+#include "myqhash.h"
#include "dxfdata.h"
#include "grid.h"
#include "printutils.h"
-#include "openscad.h" // handle_dep()
+#include "handle_dep.h"
+#include "openscad.h" // get_fragments_from_r()
#include <QFile>
#include <QTextStream>
-#include <QHash>
-#include <QVector>
#include "mathc99.h"
#include <assert.h>
+#include <boost/unordered_map.hpp>
+#include <boost/foreach.hpp>
+#include <algorithm>
struct Line {
- typedef DxfData::Point Point;
- Point *p[2];
+ int idx[2]; // indices into DxfData::points
bool disabled;
- Line(Point *p1, Point *p2) { p[0] = p1; p[1] = p2; disabled = false; }
- Line() { p[0] = NULL; p[1] = NULL; disabled = false; }
+ Line(int i1 = -1, int i2 = -1) { idx[0] = i1; idx[1] = i2; disabled = false; }
};
DxfData::DxfData()
@@ -49,51 +50,53 @@ DxfData::DxfData()
}
/*!
- Reads a layer from the given file, or all layers if layename.isNull()
+ Reads a layer from the given file, or all layers if layername.empty()
*/
-DxfData::DxfData(double fn, double fs, double fa, QString filename, QString layername, double xorigin, double yorigin, double scale)
+DxfData::DxfData(double fn, double fs, double fa,
+ const std::string &filename, const std::string &layername,
+ double xorigin, double yorigin, double scale)
{
handle_dep(filename); // Register ourselves as a dependency
- QFile f(filename);
+ QFile f(QString::fromStdString(filename));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTF("WARNING: Can't open DXF file `%s'.", filename.toUtf8().data());
+ PRINTF("WARNING: Can't open DXF file `%s'.", filename.c_str());
return;
}
QTextStream stream(&f);
- Grid2d< QVector<int> > grid(GRID_COARSE);
- QList<Line> lines; // Global lines
- QHash< QString, QList<Line> > blockdata; // Lines in blocks
+ Grid2d< std::vector<int> > grid(GRID_COARSE);
+ std::vector<Line> lines; // Global lines
+ boost::unordered_map< std::string, std::vector<Line> > blockdata; // Lines in blocks
bool in_entities_section = false;
bool in_blocks_section = false;
- QString current_block;
+ std::string current_block;
#define ADD_LINE(_x1, _y1, _x2, _y2) do { \
double _p1x = _x1, _p1y = _y1, _p2x = _x2, _p2y = _y2; \
if (!in_entities_section && !in_blocks_section) \
break; \
if (in_entities_section && \
- !(layername.isNull() || layername == layer)) \
+ !(layername.empty() || layername == layer)) \
break; \
grid.align(_p1x, _p1y); \
grid.align(_p2x, _p2y); \
- grid.data(_p1x, _p1y).append(lines.count()); \
- grid.data(_p2x, _p2y).append(lines.count()); \
+ grid.data(_p1x, _p1y).push_back(lines.size()); \
+ grid.data(_p2x, _p2y).push_back(lines.size()); \
if (in_entities_section) \
- lines.append( \
+ lines.push_back( \
Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \
- if (in_blocks_section && !current_block.isNull()) \
- blockdata[current_block].append( \
+ if (in_blocks_section && !current_block.empty()) \
+ blockdata[current_block].push_back( \
Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \
} while (0)
- QString mode, layer, name, iddata;
+ std::string mode, layer, name, iddata;
int dimtype = 0;
double coords[7][2]; // Used by DIMENSION entities
- QVector<double> xverts;
- QVector<double> yverts;
+ std::vector<double> xverts;
+ std::vector<double> yverts;
double radius = 0;
double arc_start_angle = 0, arc_stop_angle = 0;
double ellipse_start_angle = 0, ellipse_stop_angle = 0;
@@ -102,8 +105,8 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
for (int j = 0; j < 2; j++)
coords[i][j] = 0;
- QHash<QString, int> unsupported_entities_list;
-
+ typedef boost::unordered_map<std::string, int> EntityList;
+ EntityList unsupported_entities_list;
//
// Parse DXF file. Will populate this->points, this->dims, lines and blockdata
@@ -117,7 +120,7 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
int id = id_str.toInt(&status);
if (!status) {
- PRINTA("WARNING: Illegal ID `%1' in `%3'.", id_str, filename);
+ PRINTF("WARNING: Illegal ID `%s' in `%s'.", id_str.toUtf8().data(), filename.c_str());
break;
}
@@ -162,16 +165,16 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
}
else if (mode == "CIRCLE") {
int n = get_fragments_from_r(radius, fn, fs, fa);
- Point center(xverts[0], yverts[0]);
+ Vector2d center(xverts[0], yverts[0]);
for (int i = 0; i < n; i++) {
double a1 = (2*M_PI*i)/n;
double a2 = (2*M_PI*(i+1))/n;
- ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y,
- cos(a2)*radius + center.x, sin(a2)*radius + center.y);
+ ADD_LINE(cos(a1)*radius + center[0], sin(a1)*radius + center[1],
+ cos(a2)*radius + center[0], sin(a2)*radius + center[1]);
}
}
else if (mode == "ARC") {
- Point center(xverts[0], yverts[0]);
+ Vector2d center(xverts[0], yverts[0]);
int n = get_fragments_from_r(radius, fn, fs, fa);
while (arc_start_angle > arc_stop_angle)
arc_stop_angle += 360.0;
@@ -181,29 +184,29 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
double a2 = ((arc_stop_angle-arc_start_angle)*(i+1))/n;
a1 = (arc_start_angle + a1) * M_PI / 180.0;
a2 = (arc_start_angle + a2) * M_PI / 180.0;
- ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y,
- cos(a2)*radius + center.x, sin(a2)*radius + center.y);
+ ADD_LINE(cos(a1)*radius + center[0], sin(a1)*radius + center[1],
+ cos(a2)*radius + center[0], sin(a2)*radius + center[1]);
}
}
else if (mode == "ELLIPSE") {
// Commented code is meant as documentation of vector math
while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI;
// Vector2d center(xverts[0], yverts[0]);
- Point center(xverts[0], yverts[0]);
+ Vector2d center(xverts[0], yverts[0]);
// Vector2d ce(xverts[1], yverts[1]);
- Point ce(xverts[1], yverts[1]);
+ Vector2d ce(xverts[1], yverts[1]);
// double r_major = ce.length();
- double r_major = sqrt(ce.x*ce.x + ce.y*ce.y);
+ double r_major = sqrt(ce[0]*ce[0] + ce[1]*ce[1]);
// double rot_angle = ce.angle();
double rot_angle;
{
// double dot = ce.dot(Vector2d(1.0, 0.0));
- double dot = ce.x;
+ double dot = ce[0];
double cosval = dot / r_major;
if (cosval > 1.0) cosval = 1.0;
if (cosval < -1.0) cosval = -1.0;
rot_angle = acos(cosval);
- if (ce.y < 0.0) rot_angle = 2 * M_PI - rot_angle;
+ if (ce[1] < 0.0) rot_angle = 2 * M_PI - rot_angle;
}
// the ratio stored in 'radius; due to the parser code not checking entity type
@@ -212,24 +215,24 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
int n = get_fragments_from_r(r_major, fn, fs, fa);
n = (int)ceil(n * sweep_angle / (2 * M_PI));
// Vector2d p1;
- Point p1;
+ Vector2d p1;
for (int i=0;i<=n;i++) {
double a = (ellipse_start_angle + sweep_angle*i/n);
// Vector2d p2(cos(a)*r_major, sin(a)*r_minor);
- Point p2(cos(a)*r_major, sin(a)*r_minor);
+ Vector2d p2(cos(a)*r_major, sin(a)*r_minor);
// p2.rotate(rot_angle);
- Point p2_rot(cos(rot_angle)*p2.x - sin(rot_angle)*p2.y,
- sin(rot_angle)*p2.x + cos(rot_angle)*p2.y);
+ Vector2d p2_rot(cos(rot_angle)*p2[0] - sin(rot_angle)*p2[1],
+ sin(rot_angle)*p2[0] + cos(rot_angle)*p2[1]);
// p2 += center;
- p2_rot.x += center.x;
- p2_rot.y += center.y;
+ p2_rot[0] += center[0];
+ p2_rot[1] += center[1];
if (i > 0) {
// ADD_LINE(p1[0], p1[1], p2[0], p2[1]);
- ADD_LINE(p1.x, p1.y, p2_rot.x, p2_rot.y);
+ ADD_LINE(p1[0], p1[1], p2_rot[0], p2_rot[1]);
}
// p1 = p2;
- p1.x = p2_rot.x;
- p1.y = p2_rot.y;
+ p1[0] = p2_rot[0];
+ p1[1] = p2_rot[1];
}
}
else if (mode == "INSERT") {
@@ -238,10 +241,10 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
int n = blockdata[iddata].size();
for (int i = 0; i < n; i++) {
double a = arc_start_angle * M_PI / 180.0;
- double lx1 = blockdata[iddata][i].p[0]->x * ellipse_start_angle;
- double ly1 = blockdata[iddata][i].p[0]->y * ellipse_stop_angle;
- double lx2 = blockdata[iddata][i].p[1]->x * ellipse_start_angle;
- double ly2 = blockdata[iddata][i].p[1]->y * ellipse_stop_angle;
+ double lx1 = this->points[blockdata[iddata][i].idx[0]][0] * ellipse_start_angle;
+ double ly1 = this->points[blockdata[iddata][i].idx[0]][1] * ellipse_stop_angle;
+ double lx2 = this->points[blockdata[iddata][i].idx[1]][0] * ellipse_start_angle;
+ double ly2 = this->points[blockdata[iddata][i].idx[1]][1] * ellipse_stop_angle;
double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts[0];
double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts[0];
double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts[0];
@@ -250,32 +253,32 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
}
}
else if (mode == "DIMENSION" &&
- (layername.isNull() || layername == layer)) {
- this->dims.append(Dim());
- this->dims.last().type = dimtype;
+ (layername.empty() || layername == layer)) {
+ this->dims.push_back(Dim());
+ this->dims.back().type = dimtype;
for (int i = 0; i < 7; i++)
for (int j = 0; j < 2; j++)
- this->dims.last().coords[i][j] = coords[i][j];
- this->dims.last().angle = arc_start_angle;
- this->dims.last().length = radius;
- this->dims.last().name = name;
+ this->dims.back().coords[i][j] = coords[i][j];
+ this->dims.back().angle = arc_start_angle;
+ this->dims.back().length = radius;
+ this->dims.back().name = name;
}
else if (mode == "BLOCK") {
current_block = iddata;
}
else if (mode == "ENDBLK") {
- current_block = QString();
+ current_block.erase();
}
else if (mode == "ENDSEC") {
}
else if (in_blocks_section || (in_entities_section &&
- (layername.isNull() || layername == layer))) {
+ (layername.empty() || layername == layer))) {
unsupported_entities_list[mode]++;
}
- mode = data;
- layer = QString();
- name = QString();
- iddata = QString();
+ mode = data.toStdString();
+ layer.erase();
+ name.erase();
+ iddata.erase();
dimtype = 0;
for (int i = 0; i < 7; i++)
for (int j = 0; j < 2; j++)
@@ -289,37 +292,37 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
}
break;
case 1:
- name = data;
+ name = data.toStdString();
break;
case 2:
- iddata = data;
+ iddata = data.toStdString();
break;
case 8:
- layer = data;
+ layer = data.toStdString();
break;
case 10:
if (in_blocks_section)
- xverts.append((data.toDouble()));
+ xverts.push_back((data.toDouble()));
else
- xverts.append((data.toDouble() - xorigin) * scale);
+ xverts.push_back((data.toDouble() - xorigin) * scale);
break;
case 11:
if (in_blocks_section)
- xverts.append((data.toDouble()));
+ xverts.push_back((data.toDouble()));
else
- xverts.append((data.toDouble() - xorigin) * scale);
+ xverts.push_back((data.toDouble() - xorigin) * scale);
break;
case 20:
if (in_blocks_section)
- yverts.append((data.toDouble()));
+ yverts.push_back((data.toDouble()));
else
- yverts.append((data.toDouble() - yorigin) * scale);
+ yverts.push_back((data.toDouble() - yorigin) * scale);
break;
case 21:
if (in_blocks_section)
- yverts.append((data.toDouble()));
+ yverts.push_back((data.toDouble()));
else
- yverts.append((data.toDouble() - yorigin) * scale);
+ yverts.push_back((data.toDouble() - yorigin) * scale);
break;
case 40:
// CIRCLE, ARC: radius
@@ -355,40 +358,40 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
}
}
- QHashIterator<QString, int> i(unsupported_entities_list);
- while (i.hasNext()) {
- i.next();
- if (layername.isNull()) {
- PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in `%3'.",
- i.key(), QString::number(i.value()), filename);
+ BOOST_FOREACH(const EntityList::value_type &i, unsupported_entities_list) {
+ if (layername.empty()) {
+ PRINTF("WARNING: Unsupported DXF Entity `%s' (%x) in `%s'.",
+ i.first.c_str(), i.second, filename.c_str());
} else {
- PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in layer `%3' of `%4'.",
- i.key(), QString::number(i.value()), layername, filename);
+ PRINTF("WARNING: Unsupported DXF Entity `%s' (%x) in layer `%s' of `%s'.",
+ i.first.c_str(), i.second, layername.c_str(), filename.c_str());
}
}
// Extract paths from parsed data
- QHash<int, int> enabled_lines;
- for (int i = 0; i < lines.count(); i++) {
+ typedef boost::unordered_map<int, int> LineMap;
+ LineMap enabled_lines;
+ for (size_t i = 0; i < lines.size(); i++) {
enabled_lines[i] = i;
}
// extract all open paths
- while (enabled_lines.count() > 0)
+ while (enabled_lines.size() > 0)
{
int current_line, current_point;
- foreach (int i, enabled_lines) {
+ BOOST_FOREACH(const LineMap::value_type &l, enabled_lines) {
+ int idx = l.second;
for (int j = 0; j < 2; j++) {
- QVector<int> *lv = &grid.data(lines[i].p[j]->x, lines[i].p[j]->y);
- for (int ki = 0; ki < lv->count(); ki++) {
+ std::vector<int> *lv = &grid.data(this->points[lines[idx].idx[j]][0], this->points[lines[idx].idx[j]][1]);
+ for (size_t ki = 0; ki < lv->size(); ki++) {
int k = lv->at(ki);
- if (k == i || lines[k].disabled)
+ if (k == idx || lines[k].disabled)
continue;
goto next_open_path_j;
}
- current_line = i;
+ current_line = idx;
current_point = j;
goto create_open_path;
next_open_path_j:;
@@ -398,26 +401,26 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
break;
create_open_path:
- this->paths.append(Path());
- Path *this_path = &this->paths.last();
+ this->paths.push_back(Path());
+ Path *this_path = &this->paths.back();
- this_path->points.append(lines[current_line].p[current_point]);
+ this_path->indices.push_back(lines[current_line].idx[current_point]);
while (1) {
- this_path->points.append(lines[current_line].p[!current_point]);
- Point *ref_point = lines[current_line].p[!current_point];
+ this_path->indices.push_back(lines[current_line].idx[!current_point]);
+ const Vector2d &ref_point = this->points[lines[current_line].idx[!current_point]];
lines[current_line].disabled = true;
- enabled_lines.remove(current_line);
- QVector<int> *lv = &grid.data(ref_point->x, ref_point->y);
- for (int ki = 0; ki < lv->count(); ki++) {
+ enabled_lines.erase(current_line);
+ std::vector<int> *lv = &grid.data(ref_point[0], ref_point[1]);
+ for (size_t ki = 0; ki < lv->size(); ki++) {
int k = lv->at(ki);
if (lines[k].disabled)
continue;
- if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) {
+ if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[0]][0], this->points[lines[k].idx[0]][1])) {
current_line = k;
current_point = 0;
goto found_next_line_in_open_path;
}
- if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) {
+ if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[1]][0], this->points[lines[k].idx[1]][1])) {
current_line = k;
current_point = 1;
goto found_next_line_in_open_path;
@@ -429,31 +432,31 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
}
// extract all closed paths
- while (enabled_lines.count() > 0)
+ while (enabled_lines.size() > 0)
{
- int current_line = enabled_lines.begin().value(), current_point = 0;
+ int current_line = enabled_lines.begin()->second, current_point = 0;
- this->paths.append(Path());
- Path *this_path = &this->paths.last();
+ this->paths.push_back(Path());
+ Path *this_path = &this->paths.back();
this_path->is_closed = true;
- this_path->points.append(lines[current_line].p[current_point]);
+ this_path->indices.push_back(lines[current_line].idx[current_point]);
while (1) {
- this_path->points.append(lines[current_line].p[!current_point]);
- Point *ref_point = lines[current_line].p[!current_point];
+ this_path->indices.push_back(lines[current_line].idx[!current_point]);
+ const Vector2d &ref_point = this->points[lines[current_line].idx[!current_point]];
lines[current_line].disabled = true;
- enabled_lines.remove(current_line);
- QVector<int> *lv = &grid.data(ref_point->x, ref_point->y);
- for (int ki = 0; ki < lv->count(); ki++) {
+ enabled_lines.erase(current_line);
+ std::vector<int> *lv = &grid.data(ref_point[0], ref_point[1]);
+ for (size_t ki = 0; ki < lv->size(); ki++) {
int k = lv->at(ki);
if (lines[k].disabled)
continue;
- if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) {
+ if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[0]][0], this->points[lines[k].idx[0]][1])) {
current_line = k;
current_point = 0;
goto found_next_line_in_closed_path;
}
- if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) {
+ if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[1]][0], this->points[lines[k].idx[1]][1])) {
current_line = k;
current_point = 1;
goto found_next_line_in_closed_path;
@@ -468,10 +471,10 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
#if 0
printf("----- DXF Data -----\n");
- for (int i = 0; i < this->paths.count(); i++) {
+ for (int i = 0; i < this->paths.size(); i++) {
printf("Path %d (%s):\n", i, this->paths[i].is_closed ? "closed" : "open");
- for (int j = 0; j < this->paths[i].points.count(); j++)
- printf(" %f %f\n", this->paths[i].points[j]->x, this->paths[i].points[j]->y);
+ for (int j = 0; j < this->paths[i].points.size(); j++)
+ printf(" %f %f\n", (*this->paths[i].points[j])[0], (*this->paths[i].points[j])[1]);
}
printf("--------------------\n");
fflush(stdout);
@@ -484,26 +487,26 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye
*/
void DxfData::fixup_path_direction()
{
- for (int i = 0; i < this->paths.count(); i++) {
+ for (size_t i = 0; i < this->paths.size(); i++) {
if (!this->paths[i].is_closed)
break;
this->paths[i].is_inner = true;
- double min_x = this->paths[i].points[0]->x;
+ double min_x = this->points[this->paths[i].indices[0]][0];
int min_x_point = 0;
- for (int j = 1; j < this->paths[i].points.count(); j++) {
- if (this->paths[i].points[j]->x < min_x) {
- min_x = this->paths[i].points[j]->x;
+ 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];
min_x_point = j;
}
}
// rotate points if the path is in non-standard rotation
int b = min_x_point;
- int a = b == 0 ? this->paths[i].points.count() - 2 : b - 1;
- int c = b == this->paths[i].points.count() - 1 ? 1 : b + 1;
- double ax = this->paths[i].points[a]->x - this->paths[i].points[b]->x;
- double ay = this->paths[i].points[a]->y - this->paths[i].points[b]->y;
- double cx = this->paths[i].points[c]->x - this->paths[i].points[b]->x;
- double cy = this->paths[i].points[c]->y - this->paths[i].points[b]->y;
+ int a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1;
+ int 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];
+ double cy = this->points[this->paths[i].indices[c]][1] - this->points[this->paths[i].indices[b]][1];
#if 0
printf("Rotate check:\n");
printf(" a/b/c indices = %d %d %d\n", a, b, c);
@@ -512,15 +515,17 @@ void DxfData::fixup_path_direction()
#endif
// FIXME: atan2() usually takes y,x. This variant probably makes the path clockwise..
if (atan2(ax, ay) < atan2(cx, cy)) {
- for (int j = 0; j < this->paths[i].points.count()/2; j++)
- this->paths[i].points.swap(j, this->paths[i].points.count()-1-j);
+ std::reverse(this->paths[i].indices.begin(), this->paths[i].indices.end());
}
}
}
-DxfData::Point *DxfData::addPoint(double x, double y)
+/*!
+ Adds a vertex and returns the index into DxfData::points
+ */
+int DxfData::addPoint(double x, double y)
{
- this->points.append(Point(x, y));
- return &this->points.last();
+ this->points.push_back(Vector2d(x, y));
+ return this->points.size()-1;
}
diff --git a/src/dxfdata.h b/src/dxfdata.h
index d59ff5a..bada031 100644
--- a/src/dxfdata.h
+++ b/src/dxfdata.h
@@ -1,19 +1,16 @@
#ifndef DXFDATA_H_
#define DXFDATA_H_
-#include <QList>
-#include <QString>
+#include <vector>
+#include <Eigen/Dense>
+
+using Eigen::Vector2d;
class DxfData
{
public:
- struct Point {
- double x, y;
- Point() : x(0), y(0) { }
- Point(double x, double y) : x(x), y(y) { }
- };
struct Path {
- QList<Point*> points;
+ std::vector<int> indices; // indices into DxfData::points
bool is_closed, is_inner;
Path() : is_closed(false), is_inner(false) { }
};
@@ -22,7 +19,7 @@ public:
double coords[7][2];
double angle;
double length;
- QString name;
+ std::string name;
Dim() {
for (int i = 0; i < 7; i++)
for (int j = 0; j < 2; j++)
@@ -33,19 +30,17 @@ public:
}
};
- QList<Point> points;
- QList<Path> paths;
- QList<Dim> dims;
+ std::vector<Vector2d> points;
+ std::vector<Path> paths;
+ std::vector<Dim> dims;
DxfData();
- DxfData(double fn, double fs, double fa, QString filename, QString layername = QString(), double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0);
-#ifdef ENABLE_CGAL
- DxfData(const struct CGAL_Nef_polyhedron &N);
-#endif
+ DxfData(double fn, double fs, double fa,
+ const std::string &filename, const std::string &layername = "",
+ double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0);
- Point *addPoint(double x, double y);
+ int addPoint(double x, double y);
-private:
void fixup_path_direction();
};
diff --git a/src/dxfdim.cc b/src/dxfdim.cc
index 4c53d86..7b016f1 100644
--- a/src/dxfdim.cc
+++ b/src/dxfdim.cc
@@ -33,25 +33,25 @@
#include "context.h"
#include "mathc99.h"
-#include <QHash>
#include <QDateTime>
#include <QFileInfo>
+#include <sstream>
-QHash<QString,Value> dxf_dim_cache;
-QHash<QString,Value> dxf_cross_cache;
+boost::unordered_map<std::string,Value> dxf_dim_cache;
+boost::unordered_map<std::string,Value> dxf_cross_cache;
-Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args)
+Value builtin_dxf_dim(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args)
{
- QString filename;
- QString layername;
- QString name;
+ std::string filename;
+ std::string layername;
+ std::string name;
double xorigin = 0;
double yorigin = 0;
double scale = 1;
- for (int i = 0; i < argnames.count() && i < args.count(); i++) {
+ for (size_t i = 0; i < argnames.size() && i < args.size(); i++) {
if (argnames[i] == "file")
- filename = ctx->get_absolute_path(args[i].text);
+ filename = ctx->getAbsolutePath(args[i].text);
if (argnames[i] == "layer")
layername = args[i].text;
if (argnames[i] == "origin")
@@ -62,19 +62,21 @@ Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, cons
name = args[i].text;
}
- QFileInfo fileInfo(filename);
+ QFileInfo fileInfo(QString::fromStdString(filename));
- QString key = filename + "|" + layername + "|" + name + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) +
- "|" + QString::number(scale) + "|" + QString::number(fileInfo.lastModified().toTime_t()) + "|" + QString::number(fileInfo.size());
-
- if (dxf_dim_cache.contains(key))
- return dxf_dim_cache[key];
+ std::stringstream keystream;
+ keystream << filename << "|" << layername << "|" << name << "|" << xorigin
+ << "|" << yorigin <<"|" << scale << "|" << fileInfo.lastModified().toTime_t()
+ << "|" << fileInfo.size();
+ std::string key = keystream.str();
+ if (dxf_dim_cache.find(key) != dxf_dim_cache.end())
+ return dxf_dim_cache.find(key)->second;
DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale);
- for (int i = 0; i < dxf.dims.count(); i++)
+ for (size_t i = 0; i < dxf.dims.size(); i++)
{
- if (!name.isNull() && dxf.dims[i].name != name)
+ if (!name.empty() && dxf.dims[i].name != name)
continue;
DxfData::Dim *d = &dxf.dims[i];
@@ -114,26 +116,26 @@ Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, cons
return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]);
}
- PRINTA("WARNING: Dimension `%1' in `%2', layer `%3' has unsupported type!", name, filename, layername);
+ PRINTF("WARNING: Dimension `%s' in `%s', layer `%s' has unsupported type!", name.c_str(), filename.c_str(), layername.c_str());
return Value();
}
- PRINTA("WARNING: Can't find dimension `%1' in `%2', layer `%3'!", name, filename, layername);
+ PRINTF("WARNING: Can't find dimension `%s' in `%s', layer `%s'!", name.c_str(), filename.c_str(), layername.c_str());
return Value();
}
-Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args)
+Value builtin_dxf_cross(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args)
{
- QString filename;
- QString layername;
+ std::string filename;
+ std::string layername;
double xorigin = 0;
double yorigin = 0;
double scale = 1;
- for (int i = 0; i < argnames.count() && i < args.count(); i++) {
+ for (size_t i = 0; i < argnames.size() && i < args.size(); i++) {
if (argnames[i] == "file")
- filename = ctx->get_absolute_path(args[i].text);
+ filename = ctx->getAbsolutePath(args[i].text);
if (argnames[i] == "layer")
layername = args[i].text;
if (argnames[i] == "origin")
@@ -142,25 +144,28 @@ Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, co
args[i].getnum(scale);
}
- QFileInfo fileInfo(filename);
+ QFileInfo fileInfo(QString::fromStdString(filename));
- QString key = filename + "|" + layername + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) +
- "|" + QString::number(scale) + "|" + QString::number(fileInfo.lastModified().toTime_t()) + "|" + QString::number(fileInfo.size());
+ std::stringstream keystream;
+ keystream << filename << "|" << layername << "|" << xorigin << "|" << yorigin
+ << "|" << scale << "|" << fileInfo.lastModified().toTime_t()
+ << "|" << fileInfo.size();
+ std::string key = keystream.str();
- if (dxf_cross_cache.contains(key))
- return dxf_cross_cache[key];
+ if (dxf_cross_cache.find(key) != dxf_cross_cache.end())
+ return dxf_cross_cache.find(key)->second;
DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale);
double coords[4][2];
- for (int i = 0, j = 0; i < dxf.paths.count(); i++) {
- if (dxf.paths[i].points.count() != 2)
+ for (size_t i = 0, j = 0; i < dxf.paths.size(); i++) {
+ if (dxf.paths[i].indices.size() != 2)
continue;
- coords[j][0] = dxf.paths[i].points[0]->x;
- coords[j++][1] = dxf.paths[i].points[0]->y;
- coords[j][0] = dxf.paths[i].points[1]->x;
- coords[j++][1] = dxf.paths[i].points[1]->y;
+ coords[j][0] = dxf.points[dxf.paths[i].indices[0]][0];
+ coords[j++][1] = dxf.points[dxf.paths[i].indices[0]][1];
+ coords[j][0] = dxf.points[dxf.paths[i].indices[1]][0];
+ coords[j++][1] = dxf.points[dxf.paths[i].indices[1]][1];
if (j == 4) {
double x1 = coords[0][0], y1 = coords[0][1];
@@ -176,13 +181,13 @@ Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, co
double y = y1 + ua*(y2 - y1);
Value ret;
ret.type = Value::VECTOR;
- ret.vec.append(new Value(x));
- ret.vec.append(new Value(y));
+ ret.append(new Value(x));
+ ret.append(new Value(y));
return dxf_cross_cache[key] = ret;
}
}
- PRINTA("WARNING: Can't find cross in `%1', layer `%2'!", filename, layername);
+ PRINTF("WARNING: Can't find cross in `%s', layer `%s'!", filename.c_str(), layername.c_str());
return Value();
}
diff --git a/src/dxfdim.h b/src/dxfdim.h
index 9686760..bd42109 100644
--- a/src/dxfdim.h
+++ b/src/dxfdim.h
@@ -1,10 +1,10 @@
#ifndef DXFDIM_H_
#define DXFDIM_H_
-#include <QHash>
+#include <boost/unordered_map.hpp>
#include "value.h"
-extern QHash<QString,Value> dxf_cross_cache;
-extern QHash<QString,Value> dxf_dim_cache;
+extern boost::unordered_map<std::string,Value> dxf_dim_cache;
+extern boost::unordered_map<std::string,Value> dxf_cross_cache;
#endif
diff --git a/src/dxflinextrude.cc b/src/dxflinextrude.cc
index 9661066..fd37ffa 100644
--- a/src/dxflinextrude.cc
+++ b/src/dxflinextrude.cc
@@ -24,8 +24,9 @@
*
*/
+#include "dxflinextrudenode.h"
+
#include "module.h"
-#include "node.h"
#include "context.h"
#include "printutils.h"
#include "builtin.h"
@@ -33,12 +34,14 @@
#include "dxftess.h"
#include "polyset.h"
#include "progress.h"
+#include "visitor.h"
+#include "PolySetEvaluator.h"
#include "openscad.h" // get_fragments_from_r()
-#include <QApplication>
-#include <QTime>
-#include <QProgressDialog>
-#include <QDateTime>
+#include <sstream>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
+
#include <QFileInfo>
class DxfLinearExtrudeModule : public AbstractModule
@@ -48,30 +51,13 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class DxfLinearExtrudeNode : public AbstractPolyNode
-{
-public:
- int convexity, slices;
- double fn, fs, fa, height, twist;
- double origin_x, origin_y, scale;
- bool center, has_twist;
- QString filename, layername;
- DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
- convexity = slices = 0;
- fn = fs = fa = height = twist = 0;
- origin_x = origin_y = scale = 0;
- center = has_twist = false;
- }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
DxfLinearExtrudeNode *node = new DxfLinearExtrudeNode(inst);
- QVector<QString> argnames = QVector<QString>() << "file" << "layer" << "height" << "origin" << "scale" << "center" << "twist" << "slices";
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "file", "layer", "height", "origin", "scale", "center", "twist", "slices";
+ std::vector<Expression*> argexpr;
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
@@ -90,10 +76,10 @@ AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleI
Value twist = c.lookup_variable("twist", true);
Value slices = c.lookup_variable("slices", true);
- if(!file.text.isNull())
- node->filename = c.get_absolute_path(file.text);
- else
- node->filename = file.text;
+ if (!file.text.empty()) {
+ PRINTF("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead.");
+ node->filename = c.getAbsolutePath(file.text);
+ }
node->layername = layer.text;
node->height = height.num;
@@ -124,12 +110,9 @@ AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleI
node->has_twist = true;
}
- if (node->filename.isEmpty()) {
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n)
- node->children.append(n);
- }
+ if (node->filename.empty()) {
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
return node;
@@ -141,203 +124,43 @@ void register_builtin_dxf_linear_extrude()
builtin_modules["linear_extrude"] = new DxfLinearExtrudeModule();
}
-static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2)
-{
- for (int j = 1; j < pt->points.count(); j++)
- {
- int k = j - 1;
-
- double jx1 = pt->points[j]->x * cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180);
- double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180);
-
- double jx2 = pt->points[j]->x * cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180);
- double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180);
-
- double kx1 = pt->points[k]->x * cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180);
- double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180);
-
- double kx2 = pt->points[k]->x * cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180);
- double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180);
-
- double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2);
- double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1);
-
- if (dia1_len_sq > dia2_len_sq)
- {
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx1, jy1, h1);
- ps->append_vertex(jx2, jy2, h2);
- } else {
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx1, jy1, h1);
- ps->insert_vertex(jx2, jy2, h2);
- }
-
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx2, ky2, h2);
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx2, jy2, h2);
- } else {
- ps->insert_vertex(kx2, ky2, h2);
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx2, jy2, h2);
- }
- }
- else
- {
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx1, jy1, h1);
- ps->append_vertex(kx2, ky2, h2);
- } else {
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx1, jy1, h1);
- ps->insert_vertex(kx2, ky2, h2);
- }
-
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(jx2, jy2, h2);
- ps->append_vertex(kx2, ky2, h2);
- ps->append_vertex(jx1, jy1, h1);
- } else {
- ps->insert_vertex(jx2, jy2, h2);
- ps->insert_vertex(kx2, ky2, h2);
- ps->insert_vertex(jx1, jy1, h1);
- }
- }
- }
-}
-
-PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e) const
+PolySet *DxfLinearExtrudeNode::evaluate_polyset(render_mode_e mode,
+ PolySetEvaluator *evaluator) const
{
- QString key = mk_cache_id();
- if (PolySet::ps_cache.contains(key)) {
- PRINT(PolySet::ps_cache[key]->msg);
- return PolySet::ps_cache[key]->ps->link();
+ if (!evaluator) {
+ PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str());
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
+ return ps;
}
print_messages_push();
- DxfData *dxf;
-
- if (filename.isEmpty())
- {
-#ifdef ENABLE_CGAL
- // Before extruding, union all (2D) children nodes
- // to a single DxfData, then tesselate this into a PolySet
- CGAL_Nef_polyhedron N;
- N.dim = 2;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- }
- dxf = new DxfData(N);
-
-#else // ENABLE_CGAL
- PRINT("WARNING: Found linear_extrude() statement without dxf file but compiled without CGAL support!");
- dxf = new DxfData();
-#endif // ENABLE_CGAL
- } else {
- dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale);
- }
+ PolySet *ps = evaluator->evaluatePolySet(*this, mode);
- PolySet *ps = new PolySet();
- ps->convexity = convexity;
-
- double h1, h2;
-
- if (center) {
- h1 = -height/2.0;
- h2 = +height/2.0;
- } else {
- h1 = 0;
- h2 = height;
- }
-
- bool first_open_path = true;
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (dxf->paths[i].is_closed)
- continue;
- if (first_open_path) {
- PRINTF("WARING: Open paths in dxf_liniear_extrude(file = \"%s\", layer = \"%s\"):",
- filename.toAscii().data(), layername.toAscii().data());
- first_open_path = false;
- }
- PRINTF(" %9.5f %10.5f ... %10.5f %10.5f",
- dxf->paths[i].points.first()->x / scale + origin_x,
- dxf->paths[i].points.first()->y / scale + origin_y,
- dxf->paths[i].points.last()->x / scale + origin_x,
- dxf->paths[i].points.last()->y / scale + origin_y);
- }
-
-
- if (has_twist)
- {
- dxf_tesselate(ps, dxf, 0, false, true, h1);
- dxf_tesselate(ps, dxf, twist, true, true, h2);
- for (int j = 0; j < slices; j++)
- {
- double t1 = twist*j / slices;
- double t2 = twist*(j+1) / slices;
- double g1 = h1 + (h2-h1)*j / slices;
- double g2 = h1 + (h2-h1)*(j+1) / slices;
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (!dxf->paths[i].is_closed)
- continue;
- add_slice(ps, &dxf->paths[i], t1, t2, g1, g2);
- }
- }
- }
- else
- {
- dxf_tesselate(ps, dxf, 0, false, true, h1);
- dxf_tesselate(ps, dxf, 0, true, true, h2);
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (!dxf->paths[i].is_closed)
- continue;
- add_slice(ps, &dxf->paths[i], 0, 0, h1, h2);
- }
- }
-
- PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
print_messages_pop();
- delete dxf;
return ps;
}
-QString DxfLinearExtrudeNode::dump(QString indent) const
+std::string DxfLinearExtrudeNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- QFileInfo fileInfo(filename);
- text.sprintf("linear_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", "
- "height = %g, origin = [ %g %g ], scale = %g, center = %s, convexity = %d",
- filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(),
- (int)fileInfo.size(), layername.toAscii().data(), height, origin_x,
- origin_y, scale, center ? "true" : "false", convexity);
- if (has_twist) {
- QString t2;
- t2.sprintf(", twist = %g, slices = %d", twist, slices);
- text += t2;
- }
- QString t3;
- t3.sprintf(", $fn = %g, $fa = %g, $fs = %g) {\n", fn, fa, fs);
- text += t3;
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- text += indent + "}\n";
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
+ std::stringstream stream;
+
+ stream << this->name() << "("
+ "file = \"" << this->filename << "\", "
+ "cache = \"" << QFileInfo(QString::fromStdString(this->filename)) << "\", "
+ "layer = \"" << this->layername << "\", "
+ "height = " << std::dec << this->height << ", "
+ "origin = [ " << this->origin_x << " " << this->origin_y << " ], "
+ "scale = " << this->scale << ", "
+ "center = " << (this->center?"true":"false") << ", "
+ "convexity = " << this->convexity;
+
+ if (this->has_twist) {
+ stream << ", twist = " << this->twist << ", slices = " << this->slices;
}
- return dump_cache;
+ stream << ", $fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")";
+
+ return stream.str();
}
-
diff --git a/src/dxflinextrudenode.h b/src/dxflinextrudenode.h
new file mode 100644
index 0000000..3c3beca
--- /dev/null
+++ b/src/dxflinextrudenode.h
@@ -0,0 +1,30 @@
+#ifndef DXFLINEXTRUDENODE_H_
+#define DXFLINEXTRUDENODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+class DxfLinearExtrudeNode : public AbstractPolyNode
+{
+public:
+ DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
+ convexity = slices = 0;
+ fn = fs = fa = height = twist = 0;
+ origin_x = origin_y = scale = 0;
+ center = has_twist = false;
+ }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const { return "linear_extrude"; }
+
+ int convexity, slices;
+ double fn, fs, fa, height, twist;
+ double origin_x, origin_y, scale;
+ bool center, has_twist;
+ std::string filename, layername;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;
+};
+
+#endif
diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc
index 1be2265..5889cee 100644
--- a/src/dxfrotextrude.cc
+++ b/src/dxfrotextrude.cc
@@ -24,20 +24,22 @@
*
*/
+#include "dxfrotextrudenode.h"
#include "module.h"
-#include "node.h"
#include "context.h"
#include "printutils.h"
#include "builtin.h"
#include "polyset.h"
#include "dxfdata.h"
#include "progress.h"
+#include "visitor.h"
+#include "PolySetEvaluator.h"
#include "openscad.h" // get_fragments_from_r()
-#include <QTime>
-#include <QApplication>
-#include <QProgressDialog>
-#include <QDateTime>
+#include <sstream>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
+
#include <QFileInfo>
class DxfRotateExtrudeModule : public AbstractModule
@@ -47,28 +49,13 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class DxfRotateExtrudeNode : public AbstractPolyNode
-{
-public:
- int convexity;
- double fn, fs, fa;
- double origin_x, origin_y, scale;
- QString filename, layername;
- DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
- convexity = 0;
- fn = fs = fa = 0;
- origin_x = origin_y = scale = 0;
- }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
DxfRotateExtrudeNode *node = new DxfRotateExtrudeNode(inst);
- QVector<QString> argnames = QVector<QString>() << "file" << "layer" << "origin" << "scale";
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "file", "layer", "origin", "scale";
+ std::vector<Expression*> argexpr;
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
@@ -83,10 +70,10 @@ AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleI
Value origin = c.lookup_variable("origin", true);
Value scale = c.lookup_variable("scale", true);
- if(!file.text.isNull())
- node->filename = c.get_absolute_path(file.text);
- else
- node->filename = file.text;
+ if (!file.text.empty()) {
+ PRINTF("DEPRECATED: Support for reading files in rotate_extrude will be removed in future releases. Use a child import() instead.");
+ node->filename = c.getAbsolutePath(file.text);
+ }
node->layername = layer.text;
node->convexity = (int)convexity.num;
@@ -99,12 +86,9 @@ AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleI
if (node->scale <= 0)
node->scale = 1;
- if (node->filename.isEmpty()) {
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n)
- node->children.append(n);
- }
+ if (node->filename.empty()) {
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
return node;
@@ -116,131 +100,37 @@ void register_builtin_dxf_rotate_extrude()
builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule();
}
-PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const
+PolySet *DxfRotateExtrudeNode::evaluate_polyset(render_mode_e mode,
+ PolySetEvaluator *evaluator) const
{
- QString key = mk_cache_id();
- if (PolySet::ps_cache.contains(key)) {
- PRINT(PolySet::ps_cache[key]->msg);
- return PolySet::ps_cache[key]->ps->link();
+ if (!evaluator) {
+ PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str());
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
+ return ps;
}
print_messages_push();
- DxfData *dxf;
-
- if (filename.isEmpty())
- {
-#ifdef ENABLE_CGAL
- CGAL_Nef_polyhedron N;
- N.dim = 2;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- }
- dxf = new DxfData(N);
-
-#else // ENABLE_CGAL
- PRINT("WARNING: Found rotate_extrude() statement without dxf file but compiled without CGAL support!");
- dxf = new DxfData();
-#endif // ENABLE_CGAL
- } else {
- dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale);
- }
-
- PolySet *ps = new PolySet();
- ps->convexity = convexity;
-
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- double max_x = 0;
- for (int j = 0; j < dxf->paths[i].points.count(); j++) {
- max_x = fmax(max_x, dxf->paths[i].points[j]->x);
- }
-
- int fragments = get_fragments_from_r(max_x, fn, fs, fa);
-
- double ***points;
- points = new double**[fragments];
- for (int j=0; j < fragments; j++) {
- points[j] = new double*[dxf->paths[i].points.count()];
- for (int k=0; k < dxf->paths[i].points.count(); k++)
- points[j][k] = new double[3];
- }
-
- for (int j = 0; j < fragments; j++) {
- double a = (j*2*M_PI) / fragments;
- for (int k = 0; k < dxf->paths[i].points.count(); k++) {
- if (dxf->paths[i].points[k]->x == 0) {
- points[j][k][0] = 0;
- points[j][k][1] = 0;
- } else {
- points[j][k][0] = dxf->paths[i].points[k]->x * sin(a);
- points[j][k][1] = dxf->paths[i].points[k]->x * cos(a);
- }
- points[j][k][2] = dxf->paths[i].points[k]->y;
- }
- }
-
- for (int j = 0; j < fragments; j++) {
- int j1 = j + 1 < fragments ? j + 1 : 0;
- for (int k = 0; k < dxf->paths[i].points.count(); k++) {
- int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0;
- if (points[j][k][0] != points[j1][k][0] ||
- points[j][k][1] != points[j1][k][1] ||
- points[j][k][2] != points[j1][k][2]) {
- ps->append_poly();
- ps->append_vertex(points[j ][k ][0],
- points[j ][k ][1], points[j ][k ][2]);
- ps->append_vertex(points[j1][k ][0],
- points[j1][k ][1], points[j1][k ][2]);
- ps->append_vertex(points[j ][k1][0],
- points[j ][k1][1], points[j ][k1][2]);
- }
- if (points[j][k1][0] != points[j1][k1][0] ||
- points[j][k1][1] != points[j1][k1][1] ||
- points[j][k1][2] != points[j1][k1][2]) {
- ps->append_poly();
- ps->append_vertex(points[j ][k1][0],
- points[j ][k1][1], points[j ][k1][2]);
- ps->append_vertex(points[j1][k ][0],
- points[j1][k ][1], points[j1][k ][2]);
- ps->append_vertex(points[j1][k1][0],
- points[j1][k1][1], points[j1][k1][2]);
- }
- }
- }
-
- for (int j=0; j < fragments; j++) {
- for (int k=0; k < dxf->paths[i].points.count(); k++)
- delete[] points[j][k];
- delete[] points[j];
- }
- delete[] points;
- }
- PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
+ PolySet *ps = evaluator->evaluatePolySet(*this, mode);
+
print_messages_pop();
- delete dxf;
return ps;
}
-QString DxfRotateExtrudeNode::dump(QString indent) const
+std::string DxfRotateExtrudeNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- QFileInfo fileInfo(filename);
- text.sprintf("rotate_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", "
- "origin = [ %g %g ], scale = %g, convexity = %d, "
- "$fn = %g, $fa = %g, $fs = %g) {\n",
- filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(),
- (int)fileInfo.size(),layername.toAscii().data(), origin_x, origin_y,
- scale, convexity, fn, fa, fs);
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- text += indent + "}\n";
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
- }
- return dump_cache;
+ std::stringstream stream;
+
+ stream << this->name() << "("
+ "file = \"" << this->filename << "\", "
+ "cache = \"" << QFileInfo(QString::fromStdString(this->filename)) << "\", "
+ "layer = \"" << this->layername << "\", "
+ "origin = [ " << std::dec << this->origin_x << " " << this->origin_y << " ], "
+ "scale = " << this->scale << ", "
+ "convexity = " << this->convexity << ", "
+ "$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")";
+
+ return stream.str();
}
-
diff --git a/src/dxfrotextrudenode.h b/src/dxfrotextrudenode.h
new file mode 100644
index 0000000..797b53a
--- /dev/null
+++ b/src/dxfrotextrudenode.h
@@ -0,0 +1,28 @@
+#ifndef DXFROTEXTRUDENODE_H_
+#define DXFROTEXTRUDENODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+class DxfRotateExtrudeNode : public AbstractPolyNode
+{
+public:
+ DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
+ convexity = 0;
+ fn = fs = fa = 0;
+ origin_x = origin_y = scale = 0;
+ }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const { return "rotate_extrude"; }
+
+ int convexity;
+ double fn, fs, fa;
+ double origin_x, origin_y, scale;
+ std::string filename, layername;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;
+};
+
+#endif
diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc
index adb7e9a..e4a4908 100644
--- a/src/dxftess-cgal.cc
+++ b/src/dxftess-cgal.cc
@@ -93,7 +93,7 @@ void mark_inner_outer(QList<struct triangle> &tri, Grid2d<point_info_t> &point_i
}
}
-void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_triangle_splitting */, double h)
+void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool /* do_triangle_splitting */, double h)
{
CDT cdt;
@@ -106,17 +106,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr
try {
// read path data and copy all relevant infos
- for (int i = 0; i < dxf->paths.count(); i++)
+ for (size_t i = 0; i < dxf.paths.size(); i++)
{
- if (!dxf->paths[i].is_closed)
+ if (!dxf.paths[i].is_closed)
continue;
Vertex_handle first, prev;
struct point_info_t *first_pi = NULL, *prev_pi = NULL;
- for (int j = 1; j < dxf->paths[i].points.count(); j++)
+ for (size_t j = 1; j < dxf.paths[i].indices.size(); j++)
{
- double x = dxf->paths[i].points[j]->x;
- double y = dxf->paths[i].points[j]->y;
+ double x = dxf.points[dxf.paths[i].indices[j]][0];
+ double y = dxf.points[dxf.paths[i].indices[j]][1];
if (point_info.has(x, y)) {
// FIXME: How can the same path set contain the same point twice?
@@ -127,7 +127,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr
}
struct point_info_t *pi = &point_info.align(x, y);
- *pi = point_info_t(x, y, i, j, dxf->paths[i].points.count()-1);
+ *pi = point_info_t(x, y, i, j, dxf.paths[i].indices.size()-1);
Vertex_handle vh = cdt.insert(CDTPoint(x, y));
if (first_pi == NULL) {
@@ -306,17 +306,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr
}
if (path[0] == path[1] && point[0] == 1 && point[1] == 2)
- dxf->paths[path[0]].is_inner = up;
+ dxf.paths[path[0]].is_inner = up;
if (path[0] == path[1] && point[0] == 2 && point[1] == 1)
- dxf->paths[path[0]].is_inner = !up;
+ dxf.paths[path[0]].is_inner = !up;
if (path[1] == path[2] && point[1] == 1 && point[2] == 2)
- dxf->paths[path[1]].is_inner = up;
+ dxf.paths[path[1]].is_inner = up;
if (path[1] == path[2] && point[1] == 2 && point[2] == 1)
- dxf->paths[path[1]].is_inner = !up;
+ dxf.paths[path[1]].is_inner = !up;
if (path[2] == path[0] && point[2] == 1 && point[0] == 2)
- dxf->paths[path[2]].is_inner = up;
+ dxf.paths[path[2]].is_inner = up;
if (path[2] == path[0] && point[2] == 2 && point[0] == 1)
- dxf->paths[path[2]].is_inner = !up;
+ dxf.paths[path[2]].is_inner = !up;
}
}
diff --git a/src/dxftess-glu.cc b/src/dxftess-glu.cc
index 63fa2e5..23d8a45 100644
--- a/src/dxftess-glu.cc
+++ b/src/dxftess-glu.cc
@@ -193,7 +193,7 @@ static bool point_on_line(double *p1, double *p2, double *p3)
rot: CLOCKWISE rotation around positive Z axis
*/
-void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h)
+void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool do_triangle_splitting, double h)
{
GLUtesselator *tobj = gluNewTess();
@@ -227,17 +227,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
Grid3d< QPair<int,int> > point_to_path(GRID_COARSE);
- for (int i = 0; i < dxf->paths.count(); i++) {
- if (!dxf->paths[i].is_closed)
+ for (int i = 0; i < dxf.paths.size(); i++) {
+ if (!dxf.paths[i].is_closed)
continue;
gluTessBeginContour(tobj);
- for (int j = 1; j < dxf->paths[i].points.count(); j++) {
- point_to_path.data(dxf->paths[i].points[j]->x,
- dxf->paths[i].points[j]->y,
- h) = QPair<int,int>(i, j);
+ for (int j = 1; j < dxf.paths[i].indices.size(); j++) {
+ point_to_path.data(dxf.points[dxf.paths[i].indices[j]][0],
+ dxf.points[dxf.paths[i].indices[j]][1],
+ h) = QPair<int,int>(i, j);
vl.append(tess_vdata());
- vl.last().v[0] = dxf->paths[i].points[j]->x;
- vl.last().v[1] = dxf->paths[i].points[j]->y;
+ vl.last().v[0] = dxf.points[dxf.paths[i].indices[j]][0];
+ vl.last().v[1] = dxf.points[dxf.paths[i].indices[j]][1];
vl.last().v[2] = h;
gluTessVertex(tobj, vl.last().v, vl.last().v);
}
@@ -248,7 +248,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
gluDeleteTess(tobj);
#if 0
- for (int i = 0; i < tess_tri.count(); i++) {
+ for (int i = 0; i < tess_tri.size(); i++) {
printf("~~~\n");
printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]);
printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]);
@@ -258,7 +258,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
// GLU tessing sometimes generates degenerated triangles. We must find and remove
// them so we can use the triangle array with CGAL..
- for (int i = 0; i < tess_tri.count(); i++) {
+ for (int i = 0; i < tess_tri.size(); i++) {
if (point_on_line(tess_tri[i].p[0], tess_tri[i].p[1], tess_tri[i].p[2]) ||
point_on_line(tess_tri[i].p[1], tess_tri[i].p[2], tess_tri[i].p[0]) ||
point_on_line(tess_tri[i].p[2], tess_tri[i].p[0], tess_tri[i].p[1])) {
@@ -279,7 +279,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
bool added_triangles = true;
typedef QPair<int,int> QPair_ii;
QHash<int, QPair_ii> tri_by_atan2;
- for (int i = 0; i < tess_tri.count(); i++)
+ for (int i = 0; i < tess_tri.size(); i++)
for (int j = 0; j < 3; j++) {
int ai = (int)round(atan2(fabs(tess_tri[i].p[(j+1)%3][0] - tess_tri[i].p[j][0]),
fabs(tess_tri[i].p[(j+1)%3][1] - tess_tri[i].p[j][1])) / 0.001);
@@ -289,9 +289,9 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
{
added_triangles = false;
#ifdef DEBUG_TRIANGLE_SPLITTING
- printf("*** Triangle splitting (%d) ***\n", tess_tri.count()+1);
+ printf("*** Triangle splitting (%d) ***\n", tess_tri.size()+1);
#endif
- for (int i = 0; i < tess_tri.count(); i++)
+ for (int i = 0; i < tess_tri.size(); i++)
for (int k = 0; k < 3; k++)
{
QHash<QPair_ii, QPair_ii> possible_neigh;
@@ -303,7 +303,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
possible_neigh[jl] = jl;
}
#ifdef DEBUG_TRIANGLE_SPLITTING
- printf("%d/%d: %d\n", i, k, possible_neigh.count());
+ printf("%d/%d: %d\n", i, k, possible_neigh.size());
#endif
foreach (const QPair_ii &jl, possible_neigh) {
int j = jl.first;
@@ -321,7 +321,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
for (int m = 0; m < 2; m++) {
int ai = (int)round(atan2(fabs(tess_tri.last().p[(m+1)%3][0] - tess_tri.last().p[m][0]),
fabs(tess_tri.last().p[(m+1)%3][1] - tess_tri.last().p[m][1])) / 0.001 );
- tri_by_atan2.insertMulti(ai, QPair<int,int>(tess_tri.count()-1, m));
+ tri_by_atan2.insertMulti(ai, QPair<int,int>(tess_tri.size()-1, m));
}
tess_tri[i].p[(k+1)%3] = tess_tri[j].p[l];
for (int m = 0; m < 2; m++) {
@@ -337,7 +337,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
}
#endif
- for (int i = 0; i < tess_tri.count(); i++)
+ for (int i = 0; i < tess_tri.size(); i++)
{
#if 0
printf("---\n");
@@ -370,19 +370,19 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian
int j2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).second;
if (i0 == i1 && j0 == 1 && j1 == 2)
- dxf->paths[i0].is_inner = !up;
+ dxf.paths[i0].is_inner = !up;
if (i0 == i1 && j0 == 2 && j1 == 1)
- dxf->paths[i0].is_inner = up;
+ dxf.paths[i0].is_inner = up;
if (i1 == i2 && j1 == 1 && j2 == 2)
- dxf->paths[i1].is_inner = !up;
+ dxf.paths[i1].is_inner = !up;
if (i1 == i2 && j1 == 2 && j2 == 1)
- dxf->paths[i1].is_inner = up;
+ dxf.paths[i1].is_inner = up;
if (i2 == i0 && j2 == 1 && j0 == 2)
- dxf->paths[i2].is_inner = !up;
+ dxf.paths[i2].is_inner = !up;
if (i2 == i0 && j2 == 2 && j0 == 1)
- dxf->paths[i2].is_inner = up;
+ dxf.paths[i2].is_inner = up;
}
tess_tri.clear();
diff --git a/src/dxftess.cc b/src/dxftess.cc
index 03ed244..d2cb172 100644
--- a/src/dxftess.cc
+++ b/src/dxftess.cc
@@ -37,20 +37,20 @@
without tesselating. Vertex ordering of the resulting polygons
will follow the paths' is_inner flag.
*/
-void dxf_border_to_ps(PolySet *ps, DxfData *dxf)
+void dxf_border_to_ps(PolySet *ps, const DxfData &dxf)
{
- for (int i = 0; i < dxf->paths.count(); i++) {
- const DxfData::Path &pt = dxf->paths[i];
- if (!pt.is_closed)
+ for (size_t i = 0; i < dxf.paths.size(); i++) {
+ const DxfData::Path &path = dxf.paths[i];
+ if (!path.is_closed)
continue;
- ps->borders.append(PolySet::Polygon());
- for (int j = 1; j < pt.points.count(); j++) {
- double x = pt.points[j]->x, y = pt.points[j]->y, z = 0.0;
+ ps->borders.push_back(PolySet::Polygon());
+ for (size_t j = 1; j < path.indices.size(); j++) {
+ double x = dxf.points[path.indices[j]][0], y = dxf.points[path.indices[j]][1], z = 0.0;
ps->grid.align(x, y, z);
- if (pt.is_inner) {
- ps->borders.last().append(PolySet::Point(x, y, z));
+ if (path.is_inner) {
+ ps->borders.back().push_back(Vector3d(x, y, z));
} else {
- ps->borders.last().insert(0, PolySet::Point(x, y, z));
+ ps->borders.back().insert(ps->borders.back().begin(), Vector3d(x, y, z));
}
}
}
diff --git a/src/dxftess.h b/src/dxftess.h
index 19fca7d..d3af36e 100644
--- a/src/dxftess.h
+++ b/src/dxftess.h
@@ -3,7 +3,7 @@
class DxfData;
class PolySet;
-void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h);
-void dxf_border_to_ps(PolySet *ps, DxfData *dxf);
+void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool do_triangle_splitting, double h);
+void dxf_border_to_ps(PolySet *ps, const DxfData &dxf);
#endif
diff --git a/src/export.cc b/src/export.cc
index c207f2b..862e82b 100644
--- a/src/export.cc
+++ b/src/export.cc
@@ -24,6 +24,7 @@
*
*/
+#include "export.h"
#include "printutils.h"
#include "polyset.h"
#include "dxfdata.h"
@@ -34,51 +35,17 @@
#include <fstream>
#ifdef ENABLE_CGAL
+#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
-void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N)
-{
- CGAL_Polyhedron P;
- root_N->p3.convert_to_Polyhedron(P);
-
- typedef CGAL_Polyhedron::Vertex Vertex;
- typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
- typedef CGAL_Polyhedron::Facet_const_iterator FCI;
- typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC;
-
- for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) {
- HFCC hc = fi->facet_begin();
- HFCC hc_end = hc;
- Vertex v1, v2, v3;
- v1 = *VCI((hc++)->vertex());
- v3 = *VCI((hc++)->vertex());
- do {
- v2 = v3;
- v3 = *VCI((hc++)->vertex());
- double x1 = CGAL::to_double(v1.point().x());
- double y1 = CGAL::to_double(v1.point().y());
- double z1 = CGAL::to_double(v1.point().z());
- double x2 = CGAL::to_double(v2.point().x());
- double y2 = CGAL::to_double(v2.point().y());
- double z2 = CGAL::to_double(v2.point().z());
- double x3 = CGAL::to_double(v3.point().x());
- double y3 = CGAL::to_double(v3.point().y());
- double z3 = CGAL::to_double(v3.point().z());
- ps->append_poly();
- ps->append_vertex(x1, y1, z1);
- ps->append_vertex(x2, y2, z2);
- ps->append_vertex(x3, y3, z3);
- } while (hc != hc_end);
- }
-}
-
/*!
- Saves the current 3D CGAL Nef polyhedron as STL to the given absolute filename.
+ Saves the current 3D CGAL Nef polyhedron as STL to the given file.
+ The file must be open.
*/
-void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd)
+void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd)
{
CGAL_Polyhedron P;
- root_N->p3.convert_to_Polyhedron(P);
+ root_N->p3->convert_to_Polyhedron(P);
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
@@ -87,6 +54,8 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
+<<<<<<< HEAD
+=======
std::ofstream output(filename.toUtf8());
if (!output.is_open()) {
PRINTA("Can't open STL file \"%1\" for STL export: %2",
@@ -95,6 +64,7 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *
return;
}
+>>>>>>> master
output << "solid OpenSCAD_Model\n";
int facet_count = 0;
@@ -153,83 +123,81 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *
}
output << "endsolid OpenSCAD_Model\n";
+<<<<<<< HEAD
+=======
output.close();
+>>>>>>> master
setlocale(LC_NUMERIC, ""); // Set default locale
}
-void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*)
+void export_off(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog*)
{
- PRINTF("WARNING: OFF import is not implemented yet.");
+ CGAL_Polyhedron P;
+ root_N->p3->convert_to_Polyhedron(P);
+ output << P;
}
/*!
Saves the current 2D CGAL Nef polyhedron as DXF to the given absolute filename.
*/
-void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *)
+void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *)
{
- FILE *f = fopen(filename.toUtf8().data(), "w");
- if (!f) {
- PRINTA("Can't open DXF file \"%1\" for DXF export: %2",
- filename, QString(strerror(errno)));
- set_output_handler(NULL, NULL);
- return;
- }
-
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
// Some importers (e.g. Inkscape) needs a BLOCKS section to be present
- fprintf(f, " 0\n"
- "SECTION\n"
- " 2\n"
- "BLOCKS\n"
- " 0\n"
- "ENDSEC\n");
-
- fprintf(f, " 0\n"
- "SECTION\n"
- " 2\n"
- "ENTITIES\n");
-
- DxfData dd(*root_N);
- for (int i=0; i<dd.paths.size(); i++)
+ output << " 0\n"
+ << "SECTION\n"
+ << " 2\n"
+ << "BLOCKS\n"
+ << " 0\n"
+ << "ENDSEC\n"
+ << " 0\n"
+ << "SECTION\n"
+ << " 2\n"
+ << "ENTITIES\n";
+
+ DxfData *dd =root_N->convertToDxfData();
+ for (size_t i=0; i<dd->paths.size(); i++)
{
- for (int j=1; j<dd.paths[i].points.size(); j++) {
- DxfData::Point *p1 = dd.paths[i].points[j-1];
- DxfData::Point *p2 = dd.paths[i].points[j];
- double x1 = p1->x;
- double y1 = p1->y;
- double x2 = p2->x;
- double y2 = p2->y;
- fprintf(f, " 0\n");
- fprintf(f, "LINE\n");
+ for (size_t j=1; j<dd->paths[i].indices.size(); j++) {
+ const Vector2d &p1 = dd->points[dd->paths[i].indices[j-1]];
+ const Vector2d &p2 = dd->points[dd->paths[i].indices[j]];
+ double x1 = p1[0];
+ double y1 = p1[1];
+ double x2 = p2[0];
+ double y2 = p2[1];
+ output << " 0\n"
+ << "LINE\n";
// Some importers (e.g. Inkscape) needs a layer to be specified
- fprintf(f, " 8\n");
- fprintf(f, "0\n");
- fprintf(f, " 10\n");
- fprintf(f, "%f\n", x1);
- fprintf(f, " 11\n");
- fprintf(f, "%f\n", x2);
- fprintf(f, " 20\n");
- fprintf(f, "%f\n", y1);
- fprintf(f, " 21\n");
- fprintf(f, "%f\n", y2);
+ output << " 8\n"
+ << "0\n"
+ << " 10\n"
+ << x1 << "\n"
+ << " 11\n"
+ << x2 << "\n"
+ << " 20\n"
+ << y1 << "\n"
+ << " 21\n"
+ << y2 << "\n";
}
}
+
+ output << " 0\n"
+ << "ENDSEC\n";
- fprintf(f, " 0\n");
- fprintf(f, "ENDSEC\n");
// Some importers (e.g. Inkscape) needs an OBJECTS section with a DICTIONARY entry
- fprintf(f, " 0\n"
- "SECTION\n"
- " 2\n"
- "OBJECTS\n"
- " 0\n"
- "DICTIONARY\n"
- " 0\n"
- "ENDSEC\n");
- fprintf(f, " 0\n");
- fprintf(f, "EOF\n");
-
- fclose(f);
+ output << " 0\n"
+ << "SECTION\n"
+ << " 2\n"
+ << "OBJECTS\n"
+ << " 0\n"
+ << "DICTIONARY\n"
+ << " 0\n"
+ << "ENDSEC\n";
+
+ output << " 0\n"
+ <<"EOF\n";
+
+ delete dd;
setlocale(LC_NUMERIC, ""); // Set default locale
}
diff --git a/src/export.h b/src/export.h
index 619d9e0..cba0b23 100644
--- a/src/export.h
+++ b/src/export.h
@@ -2,10 +2,13 @@
#define EXPORT_H_
#ifdef ENABLE_CGAL
-void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N);
-void export_stl(class CGAL_Nef_polyhedron *root_N, QString filename, class QProgressDialog *pd);
-void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd);
-void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd);
+
+#include <iostream>
+
+void cgal_nef3_to_polyset(class PolySet *ps, class CGAL_Nef_polyhedron *root_N);
+void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, class QProgressDialog *pd);
+void export_off(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd);
+void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd);
#endif
#endif
diff --git a/src/expr.cc b/src/expr.cc
index 50950d0..c9eda4e 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -27,74 +27,76 @@
#include "expression.h"
#include "value.h"
#include "context.h"
+#include <assert.h>
+#include <sstream>
+#include <algorithm>
+#include "stl-utils.h"
Expression::Expression()
{
- const_value = NULL;
+ this->const_value = NULL;
}
Expression::~Expression()
{
- for (int i=0; i < children.size(); i++)
- delete children[i];
- if (const_value)
- delete const_value;
+ std::for_each(this->children.begin(), this->children.end(), del_fun<Expression>());
+ delete this->const_value;
}
Value Expression::evaluate(const Context *context) const
{
- if (type == "!")
- return ! children[0]->evaluate(context);
- if (type == "&&")
- return children[0]->evaluate(context) && children[1]->evaluate(context);
- if (type == "||")
- return children[0]->evaluate(context) || children[1]->evaluate(context);
- if (type == "*")
- return children[0]->evaluate(context) * children[1]->evaluate(context);
- if (type == "/")
- return children[0]->evaluate(context) / children[1]->evaluate(context);
- if (type == "%")
- return children[0]->evaluate(context) % children[1]->evaluate(context);
- if (type == "+")
- return children[0]->evaluate(context) + children[1]->evaluate(context);
- if (type == "-")
- return children[0]->evaluate(context) - children[1]->evaluate(context);
- if (type == "<")
- return children[0]->evaluate(context) < children[1]->evaluate(context);
- if (type == "<=")
- return children[0]->evaluate(context) <= children[1]->evaluate(context);
- if (type == "==")
- return children[0]->evaluate(context) == children[1]->evaluate(context);
- if (type == "!=")
- return children[0]->evaluate(context) != children[1]->evaluate(context);
- if (type == ">=")
- return children[0]->evaluate(context) >= children[1]->evaluate(context);
- if (type == ">")
- return children[0]->evaluate(context) > children[1]->evaluate(context);
- if (type == "?:") {
- Value v = children[0]->evaluate(context);
+ if (this->type == "!")
+ return ! this->children[0]->evaluate(context);
+ if (this->type == "&&")
+ return this->children[0]->evaluate(context) && this->children[1]->evaluate(context);
+ if (this->type == "||")
+ return this->children[0]->evaluate(context) || this->children[1]->evaluate(context);
+ if (this->type == "*")
+ return this->children[0]->evaluate(context) * this->children[1]->evaluate(context);
+ if (this->type == "/")
+ return this->children[0]->evaluate(context) / this->children[1]->evaluate(context);
+ if (this->type == "%")
+ return this->children[0]->evaluate(context) % this->children[1]->evaluate(context);
+ if (this->type == "+")
+ return this->children[0]->evaluate(context) + this->children[1]->evaluate(context);
+ if (this->type == "-")
+ return this->children[0]->evaluate(context) - this->children[1]->evaluate(context);
+ if (this->type == "<")
+ return this->children[0]->evaluate(context) < this->children[1]->evaluate(context);
+ if (this->type == "<=")
+ return this->children[0]->evaluate(context) <= this->children[1]->evaluate(context);
+ if (this->type == "==")
+ return this->children[0]->evaluate(context) == this->children[1]->evaluate(context);
+ if (this->type == "!=")
+ return this->children[0]->evaluate(context) != this->children[1]->evaluate(context);
+ if (this->type == ">=")
+ return this->children[0]->evaluate(context) >= this->children[1]->evaluate(context);
+ if (this->type == ">")
+ return this->children[0]->evaluate(context) > this->children[1]->evaluate(context);
+ if (this->type == "?:") {
+ Value v = this->children[0]->evaluate(context);
if (v.type == Value::BOOL)
- return children[v.b ? 1 : 2]->evaluate(context);
+ return this->children[v.b ? 1 : 2]->evaluate(context);
return Value();
}
- if (type == "[]") {
- Value v1 = children[0]->evaluate(context);
- Value v2 = children[1]->evaluate(context);
+ if (this->type == "[]") {
+ Value v1 = this->children[0]->evaluate(context);
+ Value v2 = this->children[1]->evaluate(context);
if (v1.type == Value::VECTOR && v2.type == Value::NUMBER) {
- int i = (int)(v2.num);
- if (i >= 0 && i < v1.vec.size())
+ int i = int(v2.num);
+ if (i >= 0 && i < int(v1.vec.size()))
return *v1.vec[i];
}
return Value();
}
- if (type == "I")
- return children[0]->evaluate(context).inv();
- if (type == "C")
- return *const_value;
- if (type == "R") {
- Value v1 = children[0]->evaluate(context);
- Value v2 = children[1]->evaluate(context);
- Value v3 = children[2]->evaluate(context);
+ if (this->type == "I")
+ return this->children[0]->evaluate(context).inv();
+ if (this->type == "C")
+ return *this->const_value;
+ if (this->type == "R") {
+ Value v1 = this->children[0]->evaluate(context);
+ Value v2 = this->children[1]->evaluate(context);
+ Value v3 = this->children[2]->evaluate(context);
if (v1.type == Value::NUMBER && v2.type == Value::NUMBER && v3.type == Value::NUMBER) {
Value r = Value();
r.type = Value::RANGE;
@@ -105,83 +107,100 @@ Value Expression::evaluate(const Context *context) const
}
return Value();
}
- if (type == "V") {
+ if (this->type == "V") {
Value v;
v.type = Value::VECTOR;
- for (int i = 0; i < children.size(); i++)
- v.vec.append(new Value(children[i]->evaluate(context)));
+ for (size_t i = 0; i < this->children.size(); i++)
+ v.append(new Value(this->children[i]->evaluate(context)));
return v;
}
- if (type == "L")
- return context->lookup_variable(var_name);
- if (type == "N")
+ if (this->type == "L")
+ return context->lookup_variable(this->var_name);
+ if (this->type == "N")
{
- Value v = children[0]->evaluate(context);
+ Value v = this->children[0]->evaluate(context);
- if (v.type == Value::VECTOR && var_name == QString("x"))
+ if (v.type == Value::VECTOR && this->var_name == "x")
return *v.vec[0];
- if (v.type == Value::VECTOR && var_name == QString("y"))
+ if (v.type == Value::VECTOR && this->var_name == "y")
return *v.vec[1];
- if (v.type == Value::VECTOR && var_name == QString("z"))
+ if (v.type == Value::VECTOR && this->var_name == "z")
return *v.vec[2];
- if (v.type == Value::RANGE && var_name == QString("begin"))
+ if (v.type == Value::RANGE && this->var_name == "begin")
return Value(v.range_begin);
- if (v.type == Value::RANGE && var_name == QString("step"))
+ if (v.type == Value::RANGE && this->var_name == "step")
return Value(v.range_step);
- if (v.type == Value::RANGE && var_name == QString("end"))
+ if (v.type == Value::RANGE && this->var_name == "end")
return Value(v.range_end);
return Value();
}
- if (type == "F") {
- QVector<Value> argvalues;
- for (int i=0; i < children.size(); i++)
- argvalues.append(children[i]->evaluate(context));
- return context->evaluate_function(call_funcname, call_argnames, argvalues);
+ if (this->type == "F") {
+ std::vector<Value> argvalues;
+ for (size_t i=0; i < this->children.size(); i++)
+ argvalues.push_back(this->children[i]->evaluate(context));
+ return context->evaluate_function(this->call_funcname, this->call_argnames, argvalues);
}
abort();
}
-QString Expression::dump() const
+std::string Expression::toString() const
{
- if (type == "*" || type == "/" || type == "%" || type == "+" || type == "-" ||
- type == "<" || type == "<=" || type == "==" || type == "!=" || type == ">=" || type == ">")
- return QString("(%1 %2 %3)").arg(children[0]->dump(), QString(type), children[1]->dump());
- if (type == "?:")
- return QString("(%1 ? %2 : %3)").arg(children[0]->dump(), children[1]->dump(), children[2]->dump());
- if (type == "[]")
- return QString("(%1[%2])").arg(children[0]->dump(), children[1]->dump());
- if (type == "I")
- return QString("(-%1)").arg(children[0]->dump());
- if (type == "C")
- return const_value->dump();
- if (type == "R")
- return QString("[%1 : %2 : %3]").arg(children[0]->dump(), children[1]->dump(), children[2]->dump());
- if (type == "V") {
- QString text = QString("[");
- for (int i=0; i < children.size(); i++) {
- if (i > 0)
- text += QString(", ");
- text += children[i]->dump();
+ std::stringstream stream;
+
+ if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" ||
+ this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" ||
+ this->type == "!=" || this->type == ">=" || this->type == ">") {
+ stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")";
+ }
+ else if (this->type == "?:") {
+ stream << "(" << *this->children[0] << " ? " << this->type << " : " << *this->children[1] << ")";
+ }
+ else if (this->type == "[]") {
+ stream << "(" << *this->children[0] << "[" << *this->children[1] << "])";
+ }
+ else if (this->type == "I") {
+ stream << "(-" << *this->children[0] << ")";
+ }
+ else if (this->type == "C") {
+ stream << *this->const_value;
+ }
+ else if (this->type == "R") {
+ stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << this->children[2] << "]";
+ }
+ else if (this->type == "V") {
+ stream << "[";
+ for (size_t i=0; i < this->children.size(); i++) {
+ if (i > 0) stream << ", ";
+ stream << *this->children[i];
}
- return text + QString("]");
- }
- if (type == "L")
- return var_name;
- if (type == "N")
- return QString("(%1.%2)").arg(children[0]->dump(), var_name);
- if (type == "F") {
- QString text = call_funcname + QString("(");
- for (int i=0; i < children.size(); i++) {
- if (i > 0)
- text += QString(", ");
- if (!call_argnames[i].isEmpty())
- text += call_argnames[i] + QString(" = ");
- text += children[i]->dump();
+ stream << "]";
+ }
+ else if (this->type == "L") {
+ stream << this->var_name;
+ }
+ else if (this->type == "N") {
+ stream << "(" << *this->children[0] << "." << this->var_name << ")";
+ }
+ else if (this->type == "F") {
+ stream << this->call_funcname << "(";
+ for (size_t i=0; i < this->children.size(); i++) {
+ if (i > 0) stream << ", ";
+ if (!this->call_argnames[i].empty()) stream << this->call_argnames[i] << " = ";
+ stream << *this->children[i];
}
- return text + QString(")");
+ stream << ")";
}
- abort();
+ else {
+ assert(false && "Illegal expression type");
+ }
+
+ return stream.str();
}
+std::ostream &operator<<(std::ostream &stream, const Expression &expr)
+{
+ stream << expr.toString();
+ return stream;
+}
diff --git a/src/expression.h b/src/expression.h
index dadcea0..acbd6aa 100644
--- a/src/expression.h
+++ b/src/expression.h
@@ -1,19 +1,19 @@
#ifndef EXPRESSION_H_
#define EXPRESSION_H_
-#include <QVector>
-#include <QString>
+#include <string>
+#include <vector>
class Expression
{
public:
- QVector<Expression*> children;
+ std::vector<Expression*> children;
class Value *const_value;
- QString var_name;
+ std::string var_name;
- QString call_funcname;
- QVector<QString> call_argnames;
+ std::string call_funcname;
+ std::vector<std::string> call_argnames;
// Boolean: ! && ||
// Operators: * / % + -
@@ -28,13 +28,15 @@ public:
// Lookup Variable: L
// Lookup member per name: N
// Function call: F
- QString type;
+ std::string type;
Expression();
~Expression();
Value evaluate(const class Context *context) const;
- QString dump() const;
+ std::string toString() const;
};
+std::ostream &operator<<(std::ostream &stream, const Expression &expr);
+
#endif
diff --git a/src/func.cc b/src/func.cc
index 2608960..52f04dd 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -29,31 +29,38 @@
#include "context.h"
#include "dxfdim.h"
#include "builtin.h"
+#include <sstream>
+#include <ctime>
#include "mathc99.h"
-#include <time.h>
+#include <algorithm>
+#include "stl-utils.h"
+#include <boost/foreach.hpp>
AbstractFunction::~AbstractFunction()
{
}
-Value AbstractFunction::evaluate(const Context*, const QVector<QString>&, const QVector<Value>&) const
+Value AbstractFunction::evaluate(const Context*, const std::vector<std::string>&, const std::vector<Value>&) const
{
return Value();
}
-QString AbstractFunction::dump(QString indent, QString name) const
+std::string AbstractFunction::dump(const std::string &indent, const std::string &name) const
{
- return QString("%1abstract function %2();\n").arg(indent, name);
+ std::stringstream dump;
+ dump << indent << "abstract function " << name << "();\n";
+ return dump.str();
}
Function::~Function()
{
- for (int i=0; i < argexpr.size(); i++)
- delete argexpr[i];
+ std::for_each(this->argexpr.begin(), this->argexpr.end(), del_fun<Expression>());
delete expr;
}
-Value Function::evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const
+Value Function::evaluate(const Context *ctx,
+ const std::vector<std::string> &call_argnames,
+ const std::vector<Value> &call_argvalues) const
{
Context c(ctx);
c.args(argnames, argexpr, call_argnames, call_argvalues);
@@ -62,34 +69,36 @@ Value Function::evaluate(const Context *ctx, const QVector<QString> &call_argnam
return Value();
}
-QString Function::dump(QString indent, QString name) const
+std::string Function::dump(const std::string &indent, const std::string &name) const
{
- QString text = QString("%1function %2(").arg(indent, name);
- for (int i=0; i < argnames.size(); i++) {
- if (i > 0)
- text += QString(", ");
- text += argnames[i];
- if (argexpr[i])
- text += QString(" = ") + argexpr[i]->dump();
+ std::stringstream dump;
+ dump << indent << "function " << name << "(";
+ for (size_t i=0; i < argnames.size(); i++) {
+ if (i > 0) dump << ", ";
+ dump << argnames[i];
+ if (argexpr[i]) dump << " = " << *argexpr[i];
}
- text += QString(") = %1;\n").arg(expr->dump());
- return text;
+ dump << ") = " << *expr << ";\n";
+ return dump.str();
}
-QHash<QString, AbstractFunction*> builtin_functions;
+typedef boost::unordered_map<std::string, AbstractFunction*> BuiltinContainer;
+BuiltinContainer builtin_functions;
BuiltinFunction::~BuiltinFunction()
{
}
-Value BuiltinFunction::evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const
+Value BuiltinFunction::evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const
{
return eval_func(ctx, call_argnames, call_argvalues);
}
-QString BuiltinFunction::dump(QString indent, QString name) const
+std::string BuiltinFunction::dump(const std::string &indent, const std::string &name) const
{
- return QString("%1builtin function %2();\n").arg(indent, name);
+ std::stringstream dump;
+ dump << indent << "builtin function " << name << "();\n";
+ return dump.str();
}
static double deg2rad(double x)
@@ -112,14 +121,14 @@ static double rad2deg(double x)
return x;
}
-Value builtin_abs(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_abs(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(fabs(args[0].num));
return Value();
}
-Value builtin_sign(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_sign(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value((args[0].num<0) ? -1.0 : ((args[0].num>0) ? 1.0 : 0.0));
@@ -136,7 +145,7 @@ double frand(double min, double max)
return (min>max) ? frand()*(min-max)+max : frand()*(max-min)+min;
}
-Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_rands(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 3 &&
args[0].type == Value::NUMBER &&
@@ -164,18 +173,18 @@ Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Valu
for (int i=0; i<args[2].num; i++)
{
Value * r = new Value(frand(args[0].num, args[1].num));
- v.vec.append(r);
+ v.vec.push_back(r);
}
return v;
}
-Value builtin_min(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_min(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() >= 1 && args[0].type == Value::NUMBER) {
double val = args[0].num;
- for (int i = 1; i < args.size(); i++)
+ for (size_t i = 1; i < args.size(); i++)
if (args[1].type == Value::NUMBER)
val = fmin(val, args[i].num);
return Value(val);
@@ -183,11 +192,11 @@ Value builtin_min(const Context *, const QVector<QString>&, const QVector<Value>
return Value();
}
-Value builtin_max(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_max(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() >= 1 && args[0].type == Value::NUMBER) {
double val = args[0].num;
- for (int i = 1; i < args.size(); i++)
+ for (size_t i = 1; i < args.size(); i++)
if (args[1].type == Value::NUMBER)
val = fmax(val, args[i].num);
return Value(val);
@@ -195,98 +204,98 @@ Value builtin_max(const Context *, const QVector<QString>&, const QVector<Value>
return Value();
}
-Value builtin_sin(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_sin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(sin(deg2rad(args[0].num)));
return Value();
}
-Value builtin_cos(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_cos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(cos(deg2rad(args[0].num)));
return Value();
}
-Value builtin_asin(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_asin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(rad2deg(asin(args[0].num)));
return Value();
}
-Value builtin_acos(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_acos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(rad2deg(acos(args[0].num)));
return Value();
}
-Value builtin_tan(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_tan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(tan(deg2rad(args[0].num)));
return Value();
}
-Value builtin_atan(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_atan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(rad2deg(atan(args[0].num)));
return Value();
}
-Value builtin_atan2(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_atan2(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER)
return Value(rad2deg(atan2(args[0].num, args[1].num)));
return Value();
}
-Value builtin_pow(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_pow(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER)
return Value(pow(args[0].num, args[1].num));
return Value();
}
-Value builtin_round(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_round(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(round(args[0].num));
return Value();
}
-Value builtin_ceil(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_ceil(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(ceil(args[0].num));
return Value();
}
-Value builtin_floor(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_floor(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(floor(args[0].num));
return Value();
}
-Value builtin_sqrt(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_sqrt(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(sqrt(args[0].num));
return Value();
}
-Value builtin_exp(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_exp(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(exp(args[0].num));
return Value();
}
-Value builtin_log(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_log(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER)
return Value(log(args[1].num) / log(args[0].num));
@@ -295,34 +304,31 @@ Value builtin_log(const Context *, const QVector<QString>&, const QVector<Value>
return Value();
}
-Value builtin_ln(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_ln(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
if (args.size() == 1 && args[0].type == Value::NUMBER)
return Value(log(args[0].num));
return Value();
}
-Value builtin_str(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_str(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
- QString str;
- for (int i = 0; i < args.size(); i++)
- {
- if (args[i].type == Value::STRING)
- str += args[i].text;
- else
- str += args[i].dump();
+ std::stringstream stream;
+
+ for (size_t i = 0; i < args.size(); i++) {
+ stream << args[i];
}
- return Value(str);
+ return Value(stream.str());
}
-Value builtin_lookup(const Context *, const QVector<QString>&, const QVector<Value> &args)
+Value builtin_lookup(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
{
double p, low_p, low_v, high_p, high_v;
if (args.size() < 2 || !args[0].getnum(p) || args[1].vec.size() < 2 || args[1].vec[0]->vec.size() < 2)
return Value();
if (!args[1].vec[0]->getv2(low_p, low_v) || !args[1].vec[0]->getv2(high_p, high_v))
return Value();
- for (int i = 1; i < args[1].vec.size(); i++) {
+ for (size_t i = 1; i < args[1].vec.size(); i++) {
double this_p, this_v;
if (args[1].vec[i]->getv2(this_p, this_v)) {
if (this_p <= p && (this_p > low_p || low_p > p)) {
@@ -372,8 +378,7 @@ void initialize_builtin_functions()
void destroy_builtin_functions()
{
- foreach (AbstractFunction *v, builtin_functions)
- delete v;
+ BOOST_FOREACH(BuiltinContainer::value_type &f, builtin_functions) delete f.second;
+//std::for_each(builtin_functions.begin(), builtin_functions.end(), del_fun<AbstractFunction>());
builtin_functions.clear();
}
-
diff --git a/src/function.h b/src/function.h
index 3bb89c2..623ef7e 100644
--- a/src/function.h
+++ b/src/function.h
@@ -2,42 +2,43 @@
#define FUNCTION_H_
#include "value.h"
-#include <QString>
+#include <string>
+#include <vector>
class AbstractFunction
{
public:
virtual ~AbstractFunction();
- virtual Value evaluate(const class Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const;
- virtual QString dump(QString indent, QString name) const;
+ virtual Value evaluate(const class Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual std::string dump(const std::string &indent, const std::string &name) const;
};
class BuiltinFunction : public AbstractFunction
{
public:
- typedef Value (*eval_func_t)(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args);
+ typedef Value (*eval_func_t)(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args);
eval_func_t eval_func;
BuiltinFunction(eval_func_t f) : eval_func(f) { }
virtual ~BuiltinFunction();
- virtual Value evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const;
- virtual QString dump(QString indent, QString name) const;
+ virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual std::string dump(const std::string &indent, const std::string &name) const;
};
class Function : public AbstractFunction
{
public:
- QVector<QString> argnames;
- QVector<class Expression*> argexpr;
+ std::vector<std::string> argnames;
+ std::vector<class Expression*> argexpr;
Expression *expr;
Function() { }
virtual ~Function();
- virtual Value evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const;
- virtual QString dump(QString indent, QString name) const;
+ virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual std::string dump(const std::string &indent, const std::string &name) const;
};
#endif
diff --git a/src/glview.cc b/src/glview.cc
index 9d82443..896ff0a 100644
--- a/src/glview.cc
+++ b/src/glview.cc
@@ -50,32 +50,42 @@
GLView::GLView(QWidget *parent) : QGLWidget(parent), renderer(NULL)
{
- viewer_distance = 500;
- object_rot_x = 35;
- object_rot_y = 0;
- object_rot_z = 25;
- object_trans_x = 0;
- object_trans_y = 0;
- object_trans_z = 0;
+ init();
+}
- mouse_drag_active = false;
+GLView::GLView(const QGLFormat & format, QWidget *parent) : QGLWidget(format, parent)
+{
+ init();
+}
- orthomode = false;
- showaxes = false;
- showcrosshairs = false;
- showedges = false;
- showfaces = true;
+void GLView::init()
+{
+ this->viewer_distance = 500;
+ this->object_rot_x = 35;
+ this->object_rot_y = 0;
+ this->object_rot_z = 25;
+ this->object_trans_x = 0;
+ this->object_trans_y = 0;
+ this->object_trans_z = 0;
+
+ this->mouse_drag_active = false;
+
+ this->showedges = false;
+ this->showfaces = true;
+ this->orthomode = false;
+ this->showaxes = false;
+ this->showcrosshairs = false;
for (int i = 0; i < 10; i++)
- shaderinfo[i] = 0;
+ this->shaderinfo[i] = 0;
- statusLabel = NULL;
+ this->statusLabel = NULL;
setMouseTracking(true);
#ifdef ENABLE_OPENCSG
- opencsg_support = true;
+ this->opencsg_support = true;
static int sId = 0;
- opencsg_id = sId++;
+ this->opencsg_id = sId++;
#endif
}
@@ -527,4 +537,3 @@ void GLView::mouseReleaseEvent(QMouseEvent*)
mouse_drag_active = false;
releaseMouse();
}
-
diff --git a/src/grid.h b/src/grid.h
index 703e006..74d72f9 100644
--- a/src/grid.h
+++ b/src/grid.h
@@ -1,6 +1,7 @@
#ifndef GRID_H_
#define GRID_H_
+#include <QHash>
#include "mathc99.h"
#ifdef WIN32
typedef __int64 int64_t;
@@ -8,7 +9,6 @@ typedef __int64 int64_t;
#include <stdint.h>
#endif
#include <stdlib.h>
-#include <QHash>
const double GRID_COARSE = 0.001;
const double GRID_FINE = 0.000001;
diff --git a/src/handle_dep.cc b/src/handle_dep.cc
new file mode 100644
index 0000000..2a05b4a
--- /dev/null
+++ b/src/handle_dep.cc
@@ -0,0 +1,42 @@
+#include "handle_dep.h"
+#include "myqhash.h"
+#include <string>
+#include <sstream>
+#include <QString>
+#include <QDir>
+#include <QSet>
+#include <stdlib.h> // for system()
+
+QSet<std::string> dependencies;
+const char *make_command = NULL;
+
+void handle_dep(const std::string &filename)
+{
+ if (filename[0] == '/')
+ dependencies.insert(filename);
+ else {
+ QString dep = QDir::currentPath() + QString("/") + QString::fromStdString(filename);
+ dependencies.insert(dep.toStdString());
+ }
+ if (!QFile(QString::fromStdString(filename)).exists() && make_command) {
+ std::stringstream buf;
+ buf << make_command << " '" << QString::fromStdString(filename).replace("'", "'\\''").toUtf8().data() << "'";
+ system(buf.str().c_str()); // FIXME: Handle error
+ }
+}
+
+bool write_deps(const std::string &filename, const std::string &output_file)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (!fp) {
+ fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", filename.c_str());
+ return false;
+ }
+ fprintf(fp, "%s:", output_file.c_str());
+ QSetIterator<std::string> i(dependencies);
+ while (i.hasNext())
+ fprintf(fp, " \\\n\t%s", i.next().c_str());
+ fprintf(fp, "\n");
+ fclose(fp);
+ return true;
+}
diff --git a/src/handle_dep.h b/src/handle_dep.h
new file mode 100644
index 0000000..1074a64
--- /dev/null
+++ b/src/handle_dep.h
@@ -0,0 +1,10 @@
+#ifndef HANDLE_DEP_H_
+#define HANDLE_DEP_H_
+
+#include <string>
+
+extern const char *make_command;
+void handle_dep(const std::string &filename);
+bool write_deps(const std::string &filename, const std::string &output_file);
+
+#endif
diff --git a/src/import.cc b/src/import.cc
index a924e24..0fdc156 100644
--- a/src/import.cc
+++ b/src/import.cc
@@ -24,63 +24,49 @@
*
*/
+#include "importnode.h"
+
#include "module.h"
-#include "node.h"
#include "polyset.h"
#include "context.h"
#include "builtin.h"
#include "dxfdata.h"
#include "dxftess.h"
#include "printutils.h"
-#include "openscad.h" // handle_dep()
+#include "handle_dep.h" // handle_dep()
+
+#ifdef ENABLE_CGAL
+#include "cgalutils.h"
+#endif
#include <QFile>
+#include <QRegExp>
+#include <QStringList>
#include <sys/types.h>
#include <sys/stat.h>
-
-enum import_type_e {
- TYPE_STL,
- TYPE_OFF,
- TYPE_DXF
-};
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
class ImportModule : public AbstractModule
{
public:
import_type_e type;
- ImportModule(import_type_e type) : type(type) { }
+ ImportModule(import_type_e type = TYPE_UNKNOWN) : type(type) { }
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class ImportNode : public AbstractPolyNode
-{
-public:
- import_type_e type;
- QString filename;
- QString layername;
- int convexity;
- double fn, fs, fa;
- double origin_x, origin_y, scale;
- ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
- ImportNode *node = new ImportNode(inst, type);
-
- QVector<QString> argnames;
- if (type == TYPE_DXF) {
- argnames = QVector<QString>() << "file" << "layer" << "convexity" << "origin" << "scale";
- } else {
- argnames = QVector<QString>() << "file" << "convexity";
- }
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "file", "layer", "convexity", "origin", "scale";
+ std::vector<Expression*> argexpr;
// Map old argnames to new argnames for compatibility
- QVector<QString> inst_argnames = inst->argnames;
- for (int i=0; i<inst_argnames.size(); i++) {
+ std::vector<std::string> inst_argnames = inst->argnames;
+ for (size_t i=0; i<inst_argnames.size(); i++) {
if (inst_argnames[i] == "filename")
inst_argnames[i] = "file";
if (inst_argnames[i] == "layername")
@@ -90,11 +76,23 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati
Context c(ctx);
c.args(argnames, argexpr, inst_argnames, inst->argvalues);
+ Value v = c.lookup_variable("file");
+ std::string filename = c.getAbsolutePath(v.text);
+ import_type_e actualtype = this->type;
+ if (actualtype == TYPE_UNKNOWN) {
+ QFileInfo fi(QString::fromStdString(filename));
+ if (fi.suffix().toLower() == "stl") actualtype = TYPE_STL;
+ else if (fi.suffix().toLower() == "off") actualtype = TYPE_OFF;
+ else if (fi.suffix().toLower() == "dxf") actualtype = TYPE_DXF;
+ }
+
+ ImportNode *node = new ImportNode(inst, actualtype);
+
node->fn = c.lookup_variable("$fn").num;
node->fs = c.lookup_variable("$fs").num;
node->fa = c.lookup_variable("$fa").num;
- node->filename = c.get_absolute_path(c.lookup_variable("file").text);
+ node->filename = filename;
node->layername = c.lookup_variable("layer", true).text;
node->convexity = c.lookup_variable("convexity", true).num;
@@ -113,27 +111,20 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati
return node;
}
-void register_builtin_import()
+PolySet *ImportNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const
{
- builtin_modules["import_stl"] = new ImportModule(TYPE_STL);
- builtin_modules["import_off"] = new ImportModule(TYPE_OFF);
- builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF);
-}
+ PolySet *p = NULL;
-PolySet *ImportNode::render_polyset(render_mode_e) const
-{
- PolySet *p = new PolySet();
- p->convexity = convexity;
-
- if (type == TYPE_STL)
+ if (this->type == TYPE_STL)
{
- handle_dep(filename);
- QFile f(filename);
+ handle_dep(this->filename);
+ QFile f(QString::fromStdString(this->filename));
if (!f.open(QIODevice::ReadOnly)) {
- PRINTF("WARNING: Can't open import file `%s'.", filename.toAscii().data());
+ PRINTF("WARNING: Can't open import file `%s'.", this->filename.c_str());
return p;
}
+ p = new PolySet();
QByteArray data = f.read(5);
if (data.size() == 5 && QString(data) == QString("solid"))
{
@@ -202,44 +193,68 @@ PolySet *ImportNode::render_polyset(render_mode_e) const
}
}
- if (type == TYPE_OFF)
+ else if (this->type == TYPE_OFF)
{
- PRINTF("WARNING: OFF import is not implemented yet.");
+#ifdef ENABLE_CGAL
+ CGAL_Polyhedron poly;
+ std::ifstream file(this->filename.c_str());
+ file >> poly;
+ file.close();
+
+ p = createPolySetFromPolyhedron(poly);
+#else
+ PRINTF("WARNING: OFF import requires CGAL.");
+#endif
}
- if (type == TYPE_DXF)
+ else if (this->type == TYPE_DXF)
{
- DxfData dd(fn, fs, fa, filename, layername, origin_x, origin_y, scale);
+ p = new PolySet();
+ DxfData dd(this->fn, this->fs, this->fa, this->filename, this->layername, this->origin_x, this->origin_y, this->scale);
p->is2d = true;
- dxf_tesselate(p, &dd, 0, true, false, 0);
- dxf_border_to_ps(p, &dd);
+ dxf_tesselate(p, dd, 0, true, false, 0);
+ dxf_border_to_ps(p, dd);
+ }
+ else
+ {
+ PRINTF("ERROR: Unsupported file format while trying to import file '%s'", this->filename.c_str());
}
+ if (p) p->convexity = this->convexity;
return p;
}
-QString ImportNode::dump(QString indent) const
+std::string ImportNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- struct stat st;
- memset(&st, 0, sizeof(struct stat));
- stat(filename.toAscii().data(), &st);
- if (type == TYPE_STL)
- text.sprintf("import_stl(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n",
- filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity);
- if (type == TYPE_OFF)
- text.sprintf("import_off(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n",
- filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity);
- if (type == TYPE_DXF)
- text.sprintf("import_dxf(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", "
- "origin = [ %g %g ], scale = %g, convexity = %d, "
- "$fn = %g, $fa = %g, $fs = %g);\n",
- filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size,
- layername.toAscii().data(), origin_x, origin_y, scale, convexity,
- fn, fa, fs);
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
- }
- return dump_cache;
+ std::stringstream stream;
+
+ QString text;
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(this->filename.c_str(), &st);
+
+ stream << this->name();
+ stream << "(file = \"" << this->filename << "\", "
+ "cache = \"" << std::hex << (int)st.st_mtime << "." << (int)st.st_size << "\", "
+ "layer = \"" << this->layername << "\", "
+ "origin = [ " << std::dec << this->origin_x << " " << this->origin_y << " ], "
+ "scale = " << this->scale << ", "
+ "convexity = " << this->convexity << ", "
+ "$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")";
+
+ return stream.str();
+}
+
+std::string ImportNode::name() const
+{
+ return "import";
+}
+
+void register_builtin_import()
+{
+ builtin_modules["import_stl"] = new ImportModule(TYPE_STL);
+ builtin_modules["import_off"] = new ImportModule(TYPE_OFF);
+ builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF);
+ builtin_modules["import"] = new ImportModule();
}
diff --git a/src/importnode.h b/src/importnode.h
new file mode 100644
index 0000000..49e9f16
--- /dev/null
+++ b/src/importnode.h
@@ -0,0 +1,33 @@
+#ifndef IMPORTNODE_H_
+#define IMPORTNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+enum import_type_e {
+ TYPE_UNKNOWN,
+ TYPE_STL,
+ TYPE_OFF,
+ TYPE_DXF
+};
+
+class ImportNode : public AbstractPolyNode
+{
+public:
+ ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const;
+
+ import_type_e type;
+ std::string filename;
+ std::string layername;
+ int convexity;
+ double fn, fs, fa;
+ double origin_x, origin_y, scale;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;
+};
+
+#endif
diff --git a/src/lexer.l b/src/lexer.l
index f919ee5..4c0ddea 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -26,7 +26,8 @@
%{
-#include "openscad.h"
+#include "handle_dep.h"
+#include "openscad.h" // librarydir
#include "printutils.h"
#include "parser_yacc.h"
#include <QStack>
@@ -88,7 +89,7 @@ DIGIT [0-9]
include[ \t\r\n>]*"<" { BEGIN(include); }
<include>{
-[^\t\r\n>]+"/" { filepath = yytext; }
+[^\t\r\n>]*"/" { filepath = yytext; }
[^\t\r\n>/]+ { filename = yytext; }
">" { BEGIN(INITIAL); includefile(); }
}
@@ -102,7 +103,7 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" {
if (!finfo.exists()) {
finfo = QFileInfo(QDir(librarydir), filename);
}
- handle_dep(finfo.absoluteFilePath());
+ handle_dep(finfo.absoluteFilePath().toStdString());
parserlval.text = strdup(finfo.absoluteFilePath().toLocal8Bit());
return TOK_USE;
}
@@ -116,7 +117,7 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" {
}
PRINTF("DEPRECATED: Support for implicit include will be removed in future releases. Use `include <filename>' instead.");
- handle_dep(finfo.absoluteFilePath());
+ handle_dep(finfo.absoluteFilePath().toStdString());
yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r");
if (!yyin) {
PRINTF("WARNING: Can't open input file `%s'.", filename);
@@ -206,7 +207,7 @@ void includefile()
finfo = QFileInfo(QDir(librarydir), filename);
}
- handle_dep(finfo.absoluteFilePath());
+ handle_dep(finfo.absoluteFilePath().toStdString());
yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r");
if (!yyin) {
PRINTA("WARNING: Can't open input file `%1'.", filename);
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 44c5cff..c937279 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -40,6 +40,7 @@
#include "dxftess.h"
#include "progress.h"
#ifdef ENABLE_OPENCSG
+#include "CSGTermEvaluator.h"
#include "OpenCSGRenderer.h"
#endif
#ifdef USE_PROGRESSWIDGET
@@ -74,19 +75,33 @@
#include "qlanguagefactory.h"
#endif
+#include <fstream>
+
+#include <algorithm>
+#include <boost/foreach.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/lambda/bind.hpp>
+using namespace boost::lambda;
+
#ifdef ENABLE_CGAL
-#include "cgalrenderer.h"
+#include "CGALEvaluator.h"
+#include "PolySetCGALEvaluator.h"
+#include "CGALRenderer.h"
+#include "CGAL_Nef_polyhedron.h"
+#include "cgal.h"
#endif // ENABLE_CGAL
+// Global application state
+unsigned int GuiLocker::gui_locked = 0;
+
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
static char helptitle[] =
- "OpenSCAD "
- QUOTED(OPENSCAD_VERSION)
- " (www.openscad.org)\n";
+ "OpenSCAD " QUOTED(OPENSCAD_VERSION) " (www.openscad.org)\n"
+ "Visitor refactored version\n";
static char copyrighttext[] =
"Copyright (C) 2009-2011 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n"
"\n"
@@ -137,11 +152,13 @@ MainWindow::MainWindow(const QString &filename)
root_ctx.set_variable("$fa", Value(12.0));
root_ctx.set_variable("$t", Value(0.0));
+ root_ctx.set_constant("PI",Value(M_PI));
+
Value zero3;
zero3.type = Value::VECTOR;
- zero3.vec.append(new Value(0.0));
- zero3.vec.append(new Value(0.0));
- zero3.vec.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
root_ctx.set_variable("$vpt", zero3);
root_ctx.set_variable("$vpr", zero3);
@@ -180,8 +197,8 @@ MainWindow::MainWindow(const QString &filename)
editor->setLineWrapping(true); // Not designable
setFont("", 0); // Init default font
- screen->statusLabel = new QLabel(this);
- statusBar()->addWidget(screen->statusLabel);
+ this->glview->statusLabel = new QLabel(this);
+ statusBar()->addWidget(this->glview->statusLabel);
animate_timer = new QTimer(this);
connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal()));
@@ -276,7 +293,7 @@ MainWindow::MainWindow(const QString &filename)
this->viewActionOpenCSG->setVisible(false);
#else
connect(this->viewActionOpenCSG, SIGNAL(triggered()), this, SLOT(viewModeOpenCSG()));
- if (!screen->hasOpenCSGSupport()) {
+ if (!this->glview->hasOpenCSGSupport()) {
this->viewActionOpenCSG->setEnabled(false);
}
#endif
@@ -332,9 +349,9 @@ MainWindow::MainWindow(const QString &filename)
connect(editor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool)));
connect(editor->document(), SIGNAL(modificationChanged(bool)), fileActionSave, SLOT(setEnabled(bool)));
#endif
- connect(screen, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate()));
+ connect(this->glview, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate()));
- connect(Preferences::inst(), SIGNAL(requestRedraw()), this->screen, SLOT(updateGL()));
+ connect(Preferences::inst(), SIGNAL(requestRedraw()), this->glview, SLOT(updateGL()));
connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)),
this, SLOT(setFont(const QString&,uint)));
Preferences::inst()->apply();
@@ -481,7 +498,7 @@ MainWindow::setFileName(const QString &filename)
{
if (filename.isEmpty()) {
this->fileName.clear();
- this->root_ctx.document_path = currentdir;
+ this->root_ctx.setDocumentPath(currentdir.toStdString());
setWindowTitle("OpenSCAD - New Document[*]");
}
else {
@@ -504,7 +521,7 @@ MainWindow::setFileName(const QString &filename)
this->fileName = fileinfo.fileName();
}
- this->root_ctx.document_path = fileinfo.dir().absolutePath();
+ this->root_ctx.setDocumentPath(fileinfo.dir().absolutePath().toStdString());
QDir::setCurrent(fileinfo.dir().absolutePath());
}
@@ -564,7 +581,7 @@ void MainWindow::load()
AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
{
- foreach(AbstractNode *v, n->children) {
+ BOOST_FOREACH (AbstractNode *v, n->children) {
if (v->modinst->tag_root) return v;
if (AbstractNode *vroot = find_root_tag(v)) return vroot;
}
@@ -572,7 +589,7 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
}
/*!
- Parse and evaluate the design -> this->root_node
+ Parse and evaluate the design => this->root_node
*/
void MainWindow::compile(bool procevents)
{
@@ -581,7 +598,7 @@ void MainWindow::compile(bool procevents)
QApplication::processEvents();
// Invalidate renderers before we kill the CSG tree
- screen->setRenderer(NULL);
+ this->glview->setRenderer(NULL);
if (this->opencsgRenderer) {
delete this->opencsgRenderer;
this->opencsgRenderer = NULL;
@@ -592,80 +609,83 @@ void MainWindow::compile(bool procevents)
}
// Remove previous CSG tree
- if (root_module) {
- delete root_module;
- root_module = NULL;
+ if (this->root_module) {
+ delete this->root_module;
+ this->root_module = NULL;
}
- if (absolute_root_node) {
- delete absolute_root_node;
- absolute_root_node = NULL;
+ if (this->absolute_root_node) {
+ delete this->absolute_root_node;
+ this->absolute_root_node = NULL;
}
- if (root_raw_term) {
- root_raw_term->unlink();
- root_raw_term = NULL;
+ if (this->root_raw_term) {
+ this->root_raw_term->unlink();
+ this->root_raw_term = NULL;
}
- if (root_norm_term) {
- root_norm_term->unlink();
- root_norm_term = NULL;
+ if (this->root_norm_term) {
+ this->root_norm_term->unlink();
+ this->root_norm_term = NULL;
}
- if (root_chain) {
- delete root_chain;
- root_chain = NULL;
+ if (this->root_chain) {
+ delete this->root_chain;
+ this->root_chain = NULL;
}
- foreach(CSGTerm *v, highlight_terms) {
- v->unlink();
- }
- highlight_terms.clear();
- if (highlights_chain) {
- delete highlights_chain;
- highlights_chain = NULL;
- }
- foreach(CSGTerm *v, background_terms) {
- v->unlink();
- }
- background_terms.clear();
- if (background_chain) {
- delete background_chain;
- background_chain = NULL;
- }
- root_node = NULL;
+ std::for_each(this->highlight_terms.begin(), this->highlight_terms.end(),
+ bind(&CSGTerm::unlink, _1));
+
+ this->highlight_terms.clear();
+ delete this->highlights_chain;
+ this->highlights_chain = NULL;
+
+ std::for_each(this->background_terms.begin(), this->background_terms.end(),
+ bind(&CSGTerm::unlink, _1));
+ this->background_terms.clear();
+ delete this->background_chain;
+ this->background_chain = NULL;
+
+ this->root_node = NULL;
+ this->tree.setRoot(NULL);
// Initialize special variables
- root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));
+ this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));
Value vpt;
vpt.type = Value::VECTOR;
- vpt.vec.append(new Value(-screen->object_trans_x));
- vpt.vec.append(new Value(-screen->object_trans_y));
- vpt.vec.append(new Value(-screen->object_trans_z));
- root_ctx.set_variable("$vpt", vpt);
+ 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.vec.append(new Value(fmodf(360 - screen->object_rot_x + 90, 360)));
- vpr.vec.append(new Value(fmodf(360 - screen->object_rot_y, 360)));
- vpr.vec.append(new Value(fmodf(360 - screen->object_rot_z, 360)));
+ 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
- last_compiled_doc = editor->toPlainText();
- root_module = parse((last_compiled_doc + "\n" + commandline_commands).toAscii().data(), this->fileName.isEmpty() ? "" : QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false);
+ 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 (highlighter) {
- delete highlighter;
- highlighter = NULL;
+ if (this->highlighter) {
+ delete this->highlighter;
+ this->highlighter = NULL;
}
if (parser_error_pos >= 0) {
- highlighter = new Highlighter(editor->document());
+ this->highlighter = new Highlighter(editor->document());
}
- if (!root_module) {
+ if (!this->root_module) {
if (!animate_panel->isVisible()) {
#ifdef _QCODE_EDIT_
QDocumentCursor cursor = editor->cursor();
@@ -685,19 +705,23 @@ void MainWindow::compile(bool procevents)
QApplication::processEvents();
AbstractNode::resetIndexCounter();
- root_inst = ModuleInstantiation();
- absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
+ this->root_inst = ModuleInstantiation();
+ this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
- if (!absolute_root_node)
+ if (!this->absolute_root_node)
goto fail;
// Do we have an explicit root node (! modifier)?
- if (!(this->root_node = find_root_tag(absolute_root_node))) {
- this->root_node = absolute_root_node;
+ if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
+ this->root_node = this->absolute_root_node;
}
- root_node->dump("");
+ // 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) {
+ if (1) {
PRINT("Compilation finished.");
if (procevents)
QApplication::processEvents();
@@ -707,7 +731,7 @@ fail:
PRINT("ERROR: Compilation failed! (no top level object found)");
} else {
int line = 1;
- QByteArray pb = last_compiled_doc.toAscii();
+ QByteArray pb = this->last_compiled_doc.toAscii();
char *p = pb.data();
for (int i = 0; i < parser_error_pos; i++) {
if (p[i] == '\n')
@@ -735,13 +759,6 @@ void MainWindow::compileCSG(bool procevents)
if (procevents)
QApplication::processEvents();
- double m[20];
-
- for (int i = 0; i < 16; i++)
- m[i] = i % 5 == 0 ? 1.0 : 0.0;
- for (int i = 16; i < 20; i++)
- m[i] = -1;
-
// Main CSG evaluation
QTime t;
t.start();
@@ -762,7 +779,12 @@ void MainWindow::compileCSG(bool procevents)
progress_report_prep(root_node, report_func, pd);
try {
- root_raw_term = root_node->render_csg_term(m, &highlight_terms, &background_terms);
+ // FIXME: put cache somewhere else as it's pretty useless now
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator cgalevaluator(cache, this->tree);
+ PolySetCGALEvaluator psevaluator(cgalevaluator);
+ CSGTermEvaluator csgrenderer(this->tree, &psevaluator);
+ root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
if (!root_raw_term) {
PRINT("ERROR: CSG generation failed! (no top level object found)");
if (procevents)
@@ -801,12 +823,12 @@ void MainWindow::compileCSG(bool procevents)
if (highlight_terms.size() > 0)
{
- PRINTF("Compiling highlights (%d CSG Trees)...", highlight_terms.size());
+ PRINTF("Compiling highlights (%zu CSG Trees)...", highlight_terms.size());
if (procevents)
QApplication::processEvents();
highlights_chain = new CSGChain();
- for (int i = 0; i < highlight_terms.size(); i++) {
+ for (unsigned int i = 0; i < highlight_terms.size(); i++) {
while (1) {
CSGTerm *n = highlight_terms[i]->normalize();
highlight_terms[i]->unlink();
@@ -820,12 +842,12 @@ void MainWindow::compileCSG(bool procevents)
if (background_terms.size() > 0)
{
- PRINTF("Compiling background (%d CSG Trees)...", background_terms.size());
+ PRINTF("Compiling background (%zu CSG Trees)...", background_terms.size());
if (procevents)
QApplication::processEvents();
background_chain = new CSGChain();
- for (int i = 0; i < background_terms.size(); i++) {
+ for (unsigned int i = 0; i < background_terms.size(); i++) {
while (1) {
CSGTerm *n = background_terms[i]->normalize();
background_terms[i]->unlink();
@@ -842,10 +864,11 @@ void MainWindow::compileCSG(bool procevents)
PRINTF("WARNING: OpenCSG rendering has been disabled.");
}
else {
+ PRINTF("Normalized CSG tree has %d elements", root_chain->polysets.size());
this->opencsgRenderer = new OpenCSGRenderer(this->root_chain,
this->highlights_chain,
this->background_chain,
- this->screen->shaderinfo);
+ this->glview->shaderinfo);
}
this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain,
this->highlights_chain,
@@ -876,7 +899,9 @@ void MainWindow::actionOpen()
{
QString new_filename = QFileDialog::getOpenFileName(this, "Open File", "", "OpenSCAD Designs (*.scad)");
#ifdef ENABLE_MDI
- new MainWindow(new_filename);
+ if (!new_filename.isEmpty()) {
+ new MainWindow(new_filename);
+ }
#else
if (!new_filename.isEmpty()) {
if (!maybeSave())
@@ -1027,7 +1052,7 @@ void MainWindow::pasteViewportTranslation()
QTextCursor cursor = editor->textCursor();
#endif
QString txt;
- txt.sprintf("[ %.2f, %.2f, %.2f ]", -screen->object_trans_x, -screen->object_trans_y, -screen->object_trans_z);
+ txt.sprintf("[ %.2f, %.2f, %.2f ]", -this->glview->object_trans_x, -this->glview->object_trans_y, -this->glview->object_trans_z);
cursor.insertText(txt);
}
@@ -1040,7 +1065,7 @@ void MainWindow::pasteViewportRotation()
#endif
QString txt;
txt.sprintf("[ %.2f, %.2f, %.2f ]",
- fmodf(360 - screen->object_rot_x + 90, 360), fmodf(360 - screen->object_rot_y, 360), fmodf(360 - screen->object_rot_z, 360));
+ fmodf(360 - this->glview->object_rot_x + 90, 360), fmodf(360 - this->glview->object_rot_y, 360), fmodf(360 - this->glview->object_rot_z, 360));
cursor.insertText(txt);
}
@@ -1084,6 +1109,9 @@ bool MainWindow::checkModified()
void MainWindow::actionReloadCompile()
{
+ if (GuiLocker::isLocked()) return;
+ GuiLocker lock;
+
if (!checkModified()) return;
console->clear();
@@ -1111,6 +1139,9 @@ void MainWindow::actionReloadCompile()
void MainWindow::actionCompile()
{
+ if (GuiLocker::isLocked()) return;
+ GuiLocker lock;
+
setCurrentOutput();
console->clear();
@@ -1130,7 +1161,7 @@ void MainWindow::actionCompile()
}
if (viewActionAnimate->isChecked() && e_dump->isChecked()) {
- QImage img = screen->grabFrameBuffer();
+ QImage img = this->glview->grabFrameBuffer();
QString filename;
double s = e_fsteps->text().toDouble();
double t = e_tval->text().toDouble();
@@ -1145,15 +1176,19 @@ void MainWindow::actionCompile()
void MainWindow::actionRenderCGAL()
{
+ if (GuiLocker::isLocked()) return;
+ GuiLocker lock;
+
setCurrentOutput();
console->clear();
compile(true);
- if (!root_module || !root_node)
+ if (!this->root_module || !this->root_node) {
return;
+ }
- this->screen->setRenderer(NULL);
+ this->glview->setRenderer(NULL);
delete this->cgalRenderer;
this->cgalRenderer = NULL;
if (this->root_N) {
@@ -1183,9 +1218,12 @@ void MainWindow::actionRenderCGAL()
QApplication::processEvents();
- progress_report_prep(root_node, report_func, pd);
+ progress_report_prep(this->root_node, report_func, pd);
try {
- this->root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron());
+ // FIXME: put cache somewhere else as it's pretty useless now
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator evaluator(cache, this->tree);
+ this->root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->root_node));
}
catch (ProgressCancelException e) {
PRINT("Rendering cancelled.");
@@ -1194,64 +1232,70 @@ void MainWindow::actionRenderCGAL()
if (this->root_N)
{
- PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost());
- PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());
+ // FIXME: Reenable cache cost info
+// PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost());
+// PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());
QApplication::processEvents();
if (this->root_N->dim == 2) {
PRINTF(" Top level object is a 2D object:");
QApplication::processEvents();
- PRINTF(" Empty: %6s", this->root_N->p2.is_empty() ? "yes" : "no");
+ PRINTF(" Empty: %6s", this->root_N->p2->is_empty() ? "yes" : "no");
QApplication::processEvents();
- PRINTF(" Plane: %6s", this->root_N->p2.is_plane() ? "yes" : "no");
+ PRINTF(" Plane: %6s", this->root_N->p2->is_plane() ? "yes" : "no");
QApplication::processEvents();
- PRINTF(" Vertices: %6d", (int)this->root_N->p2.explorer().number_of_vertices());
+ PRINTF(" Vertices: %6d", (int)this->root_N->p2->explorer().number_of_vertices());
QApplication::processEvents();
- PRINTF(" Halfedges: %6d", (int)this->root_N->p2.explorer().number_of_halfedges());
+ PRINTF(" Halfedges: %6d", (int)this->root_N->p2->explorer().number_of_halfedges());
QApplication::processEvents();
- PRINTF(" Edges: %6d", (int)this->root_N->p2.explorer().number_of_edges());
+ PRINTF(" Edges: %6d", (int)this->root_N->p2->explorer().number_of_edges());
QApplication::processEvents();
- PRINTF(" Faces: %6d", (int)this->root_N->p2.explorer().number_of_faces());
+ PRINTF(" Faces: %6d", (int)this->root_N->p2->explorer().number_of_faces());
QApplication::processEvents();
- PRINTF(" FaceCycles: %6d", (int)this->root_N->p2.explorer().number_of_face_cycles());
+ PRINTF(" FaceCycles: %6d", (int)this->root_N->p2->explorer().number_of_face_cycles());
QApplication::processEvents();
- PRINTF(" ConnComp: %6d", (int)this->root_N->p2.explorer().number_of_connected_components());
+ PRINTF(" ConnComp: %6d", (int)this->root_N->p2->explorer().number_of_connected_components());
QApplication::processEvents();
}
if (this->root_N->dim == 3) {
PRINTF(" Top level object is a 3D object:");
- PRINTF(" Simple: %6s", this->root_N->p3.is_simple() ? "yes" : "no");
+ PRINTF(" Simple: %6s", this->root_N->p3->is_simple() ? "yes" : "no");
QApplication::processEvents();
- PRINTF(" Valid: %6s", this->root_N->p3.is_valid() ? "yes" : "no");
+ PRINTF(" Valid: %6s", this->root_N->p3->is_valid() ? "yes" : "no");
QApplication::processEvents();
- PRINTF(" Vertices: %6d", (int)this->root_N->p3.number_of_vertices());
+ PRINTF(" Vertices: %6d", (int)this->root_N->p3->number_of_vertices());
QApplication::processEvents();
- PRINTF(" Halfedges: %6d", (int)this->root_N->p3.number_of_halfedges());
+ PRINTF(" Halfedges: %6d", (int)this->root_N->p3->number_of_halfedges());
QApplication::processEvents();
- PRINTF(" Edges: %6d", (int)this->root_N->p3.number_of_edges());
+ PRINTF(" Edges: %6d", (int)this->root_N->p3->number_of_edges());
QApplication::processEvents();
- PRINTF(" Halffacets: %6d", (int)this->root_N->p3.number_of_halffacets());
+ PRINTF(" Halffacets: %6d", (int)this->root_N->p3->number_of_halffacets());
QApplication::processEvents();
- PRINTF(" Facets: %6d", (int)this->root_N->p3.number_of_facets());
+ PRINTF(" Facets: %6d", (int)this->root_N->p3->number_of_facets());
QApplication::processEvents();
- PRINTF(" Volumes: %6d", (int)this->root_N->p3.number_of_volumes());
+ PRINTF(" Volumes: %6d", (int)this->root_N->p3->number_of_volumes());
QApplication::processEvents();
}
int s = t.elapsed() / 1000;
PRINTF("Total rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60);
- this->cgalRenderer = new CGALRenderer(*this->root_N);
- // Go to CGAL view mode
- if (viewActionCGALGrid->isChecked()) {
- viewModeCGALGrid();
+ if (!this->root_N->empty()) {
+ this->cgalRenderer = new CGALRenderer(*this->root_N);
+ // Go to CGAL view mode
+ if (viewActionCGALGrid->isChecked()) {
+ viewModeCGALGrid();
+ }
+ else {
+ viewModeCGALSurface();
+ }
+
+ PRINT("Rendering finished.");
}
else {
- viewModeCGALSurface();
+ PRINT("WARNING: No top level geometry to render");
}
-
- PRINT("Rendering finished.");
}
#ifdef USE_PROGRESSWIDGET
@@ -1272,7 +1316,7 @@ void MainWindow::actionDisplayAST()
e->setWindowTitle("AST Dump");
e->setReadOnly(true);
if (root_module) {
- e->setPlainText(root_module->dump("", ""));
+ e->setPlainText(QString::fromStdString(root_module->dump("", "")));
} else {
e->setPlainText("No AST to dump. Please try compiling first...");
}
@@ -1289,8 +1333,8 @@ void MainWindow::actionDisplayCSGTree()
e->setTabStopWidth(30);
e->setWindowTitle("CSG Tree Dump");
e->setReadOnly(true);
- if (root_node) {
- e->setPlainText(root_node->dump(""));
+ if (this->root_node) {
+ e->setPlainText(QString::fromStdString(this->tree.getString(*this->root_node)));
} else {
e->setPlainText("No CSG to dump. Please try compiling first...");
}
@@ -1307,7 +1351,7 @@ void MainWindow::actionDisplayCSGProducts()
e->setTabStopWidth(30);
e->setWindowTitle("CSG Products Dump");
e->setReadOnly(true);
- e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? root_raw_term->dump() : "N/A", root_norm_term ? root_norm_term->dump() : "N/A", root_chain ? root_chain->dump() : "N/A", highlights_chain ? highlights_chain->dump() : "N/A", background_chain ? background_chain->dump() : "N/A"));
+ e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? QString::fromStdString(root_raw_term->dump()) : "N/A", root_norm_term ? QString::fromStdString(root_norm_term->dump()) : "N/A", root_chain ? QString::fromStdString(root_chain->dump()) : "N/A", highlights_chain ? QString::fromStdString(highlights_chain->dump()) : "N/A", background_chain ? QString::fromStdString(background_chain->dump()) : "N/A"));
e->show();
e->resize(600, 400);
clearCurrentOutput();
@@ -1319,6 +1363,8 @@ void MainWindow::actionExportSTLorOFF(bool stl_mode)
void MainWindow::actionExportSTLorOFF(bool)
#endif
{
+ if (GuiLocker::isLocked()) return;
+ GuiLocker lock;
#ifdef ENABLE_CGAL
setCurrentOutput();
@@ -1334,14 +1380,16 @@ void MainWindow::actionExportSTLorOFF(bool)
return;
}
- if (!this->root_N->p3.is_simple()) {
+ if (!this->root_N->p3->is_simple()) {
PRINT("Object isn't a valid 2-manifold! Modify your design..");
clearCurrentOutput();
return;
}
+ QString suffix = stl_mode ? ".stl" : ".off";
QString stl_filename = QFileDialog::getSaveFileName(this,
- stl_mode ? "Export STL File" : "Export OFF File", "",
+ stl_mode ? "Export STL File" : "Export OFF File",
+ this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix,
stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)");
if (stl_filename.isEmpty()) {
PRINTF("No filename specified. %s export aborted.", stl_mode ? "STL" : "OFF");
@@ -1351,19 +1399,23 @@ void MainWindow::actionExportSTLorOFF(bool)
QProgressDialog *pd = new QProgressDialog(
stl_mode ? "Exporting object to STL file..." : "Exporting object to OFF file...",
- QString(), 0, this->root_N->p3.number_of_facets() + 1);
+ QString(), 0, this->root_N->p3->number_of_facets() + 1);
pd->setValue(0);
pd->setAutoClose(false);
pd->show();
QApplication::processEvents();
- if (stl_mode)
- export_stl(this->root_N, stl_filename, pd);
- else
- export_off(this->root_N, stl_filename, pd);
-
- PRINTF("%s export finished.", stl_mode ? "STL" : "OFF");
+ std::ofstream fstream(stl_filename.toUtf8());
+ if (!fstream.is_open()) {
+ PRINTA("Can't open file \"%1\" for export", stl_filename);
+ }
+ else {
+ if (stl_mode) export_stl(this->root_N, fstream, pd);
+ else export_off(this->root_N, fstream, pd);
+ fstream.close();
+ PRINTF("%s export finished.", stl_mode ? "STL" : "OFF");
+ }
delete pd;
clearCurrentOutput();
@@ -1397,16 +1449,25 @@ void MainWindow::actionExportDXF()
return;
}
- QString stl_filename = QFileDialog::getSaveFileName(this,
- "Export DXF File", "", "DXF Files (*.dxf)");
- if (stl_filename.isEmpty()) {
+ QString dxf_filename = QFileDialog::getSaveFileName(this,
+ "Export DXF File",
+ this->fileName.isEmpty() ? "Untitled.dxf" : QFileInfo(this->fileName).baseName()+".dxf",
+ "DXF Files (*.dxf)");
+ if (dxf_filename.isEmpty()) {
PRINTF("No filename specified. DXF export aborted.");
clearCurrentOutput();
return;
}
- export_dxf(this->root_N, stl_filename, NULL);
- PRINTF("DXF export finished.");
+ std::ofstream fstream(dxf_filename.toUtf8());
+ if (!fstream.is_open()) {
+ PRINTA("Can't open file \"%s\" for export", dxf_filename);
+ }
+ else {
+ export_dxf(this->root_N, fstream, NULL);
+ fstream.close();
+ PRINTF("DXF export finished.");
+ }
clearCurrentOutput();
#endif /* ENABLE_CGAL */
@@ -1414,7 +1475,7 @@ void MainWindow::actionExportDXF()
void MainWindow::actionExportImage()
{
- QImage img = screen->grabFrameBuffer();
+ QImage img = this->glview->grabFrameBuffer();
setCurrentOutput();
QString img_filename = QFileDialog::getSaveFileName(this,
@@ -1432,9 +1493,12 @@ void MainWindow::actionExportImage()
void MainWindow::actionFlushCaches()
{
- PolySet::ps_cache.clear();
+// FIXME: Polycache -> PolySetEvaluator
+// FIXME: PolySetEvaluator->clearCache();
#ifdef ENABLE_CGAL
- AbstractNode::cgal_nef_cache.clear();
+// FIXME: Flush caches through whatever channels we have
+ // CGALEvaluator::evaluator()->getCache().clear();
+ // this->dumper->clearCache();
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();
@@ -1459,10 +1523,10 @@ void MainWindow::viewModeActionsUncheck()
*/
void MainWindow::viewModeOpenCSG()
{
- if (screen->hasOpenCSGSupport()) {
+ if (this->glview->hasOpenCSGSupport()) {
viewModeActionsUncheck();
viewActionOpenCSG->setChecked(true);
- screen->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer);
+ this->glview->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer);
} else {
viewModeThrownTogether();
}
@@ -1476,17 +1540,17 @@ void MainWindow::viewModeCGALSurface()
{
viewModeActionsUncheck();
viewActionCGALSurfaces->setChecked(true);
- screen->setShowFaces(true);
- screen->setRenderer(this->cgalRenderer);
- screen->updateGL();
+ this->glview->setShowFaces(true);
+ this->glview->setRenderer(this->cgalRenderer);
+ this->glview->updateGL();
}
void MainWindow::viewModeCGALGrid()
{
viewModeActionsUncheck();
viewActionCGALGrid->setChecked(true);
- screen->setShowFaces(false);
- screen->setRenderer(this->cgalRenderer);
+ this->glview->setShowFaces(false);
+ this->glview->setRenderer(this->cgalRenderer);
}
#endif /* ENABLE_CGAL */
@@ -1495,31 +1559,31 @@ void MainWindow::viewModeThrownTogether()
{
viewModeActionsUncheck();
viewActionThrownTogether->setChecked(true);
- screen->setRenderer(this->thrownTogetherRenderer);
+ this->glview->setRenderer(this->thrownTogetherRenderer);
}
void MainWindow::viewModeShowEdges()
{
QSettings settings;
settings.setValue("view/showEdges",viewActionShowEdges->isChecked());
- screen->setShowEdges(viewActionShowEdges->isChecked());
- screen->updateGL();
+ this->glview->setShowEdges(viewActionShowEdges->isChecked());
+ this->glview->updateGL();
}
void MainWindow::viewModeShowAxes()
{
QSettings settings;
settings.setValue("view/showAxes",viewActionShowAxes->isChecked());
- screen->setShowAxes(viewActionShowAxes->isChecked());
- screen->updateGL();
+ this->glview->setShowAxes(viewActionShowAxes->isChecked());
+ this->glview->updateGL();
}
void MainWindow::viewModeShowCrosshairs()
{
QSettings settings;
settings.setValue("view/showCrosshairs",viewActionShowCrosshairs->isChecked());
- screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked());
- screen->updateGL();
+ this->glview->setShowCrosshairs(viewActionShowCrosshairs->isChecked());
+ this->glview->updateGL();
}
void MainWindow::viewModeAnimate()
@@ -1557,66 +1621,66 @@ void MainWindow::animateUpdate()
void MainWindow::viewAngleTop()
{
- screen->object_rot_x = 90;
- screen->object_rot_y = 0;
- screen->object_rot_z = 0;
- screen->updateGL();
+ this->glview->object_rot_x = 90;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 0;
+ this->glview->updateGL();
}
void MainWindow::viewAngleBottom()
{
- screen->object_rot_x = 270;
- screen->object_rot_y = 0;
- screen->object_rot_z = 0;
- screen->updateGL();
+ this->glview->object_rot_x = 270;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 0;
+ this->glview->updateGL();
}
void MainWindow::viewAngleLeft()
{
- screen->object_rot_x = 0;
- screen->object_rot_y = 0;
- screen->object_rot_z = 90;
- screen->updateGL();
+ this->glview->object_rot_x = 0;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 90;
+ this->glview->updateGL();
}
void MainWindow::viewAngleRight()
{
- screen->object_rot_x = 0;
- screen->object_rot_y = 0;
- screen->object_rot_z = 270;
- screen->updateGL();
+ this->glview->object_rot_x = 0;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 270;
+ this->glview->updateGL();
}
void MainWindow::viewAngleFront()
{
- screen->object_rot_x = 0;
- screen->object_rot_y = 0;
- screen->object_rot_z = 0;
- screen->updateGL();
+ this->glview->object_rot_x = 0;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 0;
+ this->glview->updateGL();
}
void MainWindow::viewAngleBack()
{
- screen->object_rot_x = 0;
- screen->object_rot_y = 0;
- screen->object_rot_z = 180;
- screen->updateGL();
+ this->glview->object_rot_x = 0;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 180;
+ this->glview->updateGL();
}
void MainWindow::viewAngleDiagonal()
{
- screen->object_rot_x = 35;
- screen->object_rot_y = 0;
- screen->object_rot_z = 25;
- screen->updateGL();
+ this->glview->object_rot_x = 35;
+ this->glview->object_rot_y = 0;
+ this->glview->object_rot_z = 25;
+ this->glview->updateGL();
}
void MainWindow::viewCenter()
{
- screen->object_trans_x = 0;
- screen->object_trans_y = 0;
- screen->object_trans_z = 0;
- screen->updateGL();
+ this->glview->object_trans_x = 0;
+ this->glview->object_trans_y = 0;
+ this->glview->object_trans_z = 0;
+ this->glview->updateGL();
}
void MainWindow::viewPerspective()
@@ -1625,8 +1689,8 @@ void MainWindow::viewPerspective()
settings.setValue("view/orthogonalProjection",false);
viewActionPerspective->setChecked(true);
viewActionOrthogonal->setChecked(false);
- screen->setOrthoMode(false);
- screen->updateGL();
+ this->glview->setOrthoMode(false);
+ this->glview->updateGL();
}
void MainWindow::viewOrthogonal()
@@ -1635,8 +1699,8 @@ void MainWindow::viewOrthogonal()
settings.setValue("view/orthogonalProjection",true);
viewActionPerspective->setChecked(false);
viewActionOrthogonal->setChecked(true);
- screen->setOrthoMode(true);
- screen->updateGL();
+ this->glview->setOrthoMode(true);
+ this->glview->updateGL();
}
void MainWindow::hideConsole()
diff --git a/src/module.cc b/src/module.cc
index 54b151c..0b17ff7 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -31,6 +31,8 @@
#include "function.h"
#include "builtin.h"
#include "printutils.h"
+#include <boost/foreach.hpp>
+#include <sstream>
AbstractModule::~AbstractModule()
{
@@ -40,91 +42,102 @@ AbstractNode *AbstractModule::evaluate(const Context*, const ModuleInstantiation
{
AbstractNode *node = new AbstractNode(inst);
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n)
- node->children.append(n);
- }
+ node->children = inst->evaluateChildren();
return node;
}
-QString AbstractModule::dump(QString indent, QString name) const
+std::string AbstractModule::dump(const std::string &indent, const std::string &name) const
{
- return QString("%1abstract module %2();\n").arg(indent, name);
+ std::stringstream dump;
+ dump << indent << "abstract module " << name << "();\n";
+ return dump.str();
}
ModuleInstantiation::~ModuleInstantiation()
{
- foreach (Expression *v, argexpr)
- delete v;
- foreach (ModuleInstantiation *v, children)
- delete v;
+ BOOST_FOREACH (Expression *v, argexpr) delete v;
+ BOOST_FOREACH (ModuleInstantiation *v, children) delete v;
}
IfElseModuleInstantiation::~IfElseModuleInstantiation()
{
- foreach (ModuleInstantiation *v, else_children)
- delete v;
+ BOOST_FOREACH (ModuleInstantiation *v, else_children) delete v;
}
-QString ModuleInstantiation::dump(QString indent) const
+std::string ModuleInstantiation::dump(const std::string &indent) const
{
- QString text = indent;
- if (!label.isEmpty())
- text += label + QString(": ");
- text += modname + QString("(");
- for (int i=0; i < argnames.size(); i++) {
- if (i > 0)
- text += QString(", ");
- if (!argnames[i].isEmpty())
- text += argnames[i] + QString(" = ");
- text += argexpr[i]->dump();
+ std::stringstream dump;
+ dump << indent;
+ if (!label.empty()) dump << label <<": ";
+ dump << modname + "(";
+ for (size_t i=0; i < argnames.size(); i++) {
+ if (i > 0) dump << ", ";
+ if (!argnames[i].empty()) dump << argnames[i] << " = ";
+ dump << *argexpr[i];
}
if (children.size() == 0) {
- text += QString(");\n");
+ dump << ");\n";
} else if (children.size() == 1) {
- text += QString(")\n");
- text += children[0]->dump(indent + QString("\t"));
+ dump << ")\n";
+ dump << children[0]->dump(indent + "\t");
} else {
- text += QString(") {\n");
- for (int i = 0; i < children.size(); i++) {
- text += children[i]->dump(indent + QString("\t"));
+ dump << ") {\n";
+ for (size_t i = 0; i < children.size(); i++) {
+ dump << children[i]->dump(indent + "\t");
}
- text += QString("%1}\n").arg(indent);
+ dump << indent << "}\n";
}
- return text;
+ return dump.str();
}
AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const
{
AbstractNode *node = NULL;
if (this->ctx) {
- PRINTA("WARNING: Ignoring recursive module instanciation of '%1'.", modname);
+ PRINTF("WARNING: Ignoring recursive module instantiation of '%s'.", modname.c_str());
} else {
ModuleInstantiation *that = (ModuleInstantiation*)this;
that->argvalues.clear();
- foreach (Expression *v, that->argexpr) {
- that->argvalues.append(v->evaluate(ctx));
+ BOOST_FOREACH (Expression *v, that->argexpr) {
+ that->argvalues.push_back(v->evaluate(ctx));
}
that->ctx = ctx;
- node = ctx->evaluate_module(this);
+ node = ctx->evaluate_module(*this);
that->ctx = NULL;
that->argvalues.clear();
}
return node;
}
+std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *ctx) const
+{
+ if (!ctx) ctx = this->ctx;
+ std::vector<AbstractNode*> childnodes;
+ BOOST_FOREACH (ModuleInstantiation *v, this->children) {
+ AbstractNode *n = v->evaluate(ctx);
+ if (n != NULL) childnodes.push_back(n);
+ }
+ return childnodes;
+}
+
+std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const Context *ctx) const
+{
+ if (!ctx) ctx = this->ctx;
+ std::vector<AbstractNode*> childnodes;
+ BOOST_FOREACH (ModuleInstantiation *v, this->else_children) {
+ AbstractNode *n = v->evaluate(this->ctx);
+ if (n != NULL) childnodes.push_back(n);
+ }
+ return childnodes;
+}
+
Module::~Module()
{
- foreach (Expression *v, assignments_expr)
- delete v;
- foreach (AbstractFunction *v, functions)
- delete v;
- foreach (AbstractModule *v, modules)
- delete v;
- foreach (ModuleInstantiation *v, children)
- delete v;
+ BOOST_FOREACH (Expression *v, assignments_expr) delete v;
+ BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second;
+ BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second;
+ BOOST_FOREACH (ModuleInstantiation *v, children) delete v;
}
AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
@@ -143,62 +156,53 @@ AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *in
else
c.usedlibs_p = NULL;
- for (int i = 0; i < assignments_var.size(); i++) {
+ for (size_t i = 0; i < assignments_var.size(); i++) {
c.set_variable(assignments_var[i], assignments_expr[i]->evaluate(&c));
}
AbstractNode *node = new AbstractNode(inst);
- for (int i = 0; i < children.size(); i++) {
+ for (size_t i = 0; i < children.size(); i++) {
AbstractNode *n = children[i]->evaluate(&c);
if (n != NULL)
- node->children.append(n);
+ node->children.push_back(n);
}
return node;
}
-QString Module::dump(QString indent, QString name) const
+std::string Module::dump(const std::string &indent, const std::string &name) const
{
- QString text, tab;
- if (!name.isEmpty()) {
- text = QString("%1module %2(").arg(indent, name);
- for (int i=0; i < argnames.size(); i++) {
- if (i > 0)
- text += QString(", ");
- text += argnames[i];
- if (argexpr[i])
- text += QString(" = ") + argexpr[i]->dump();
+ std::stringstream dump;
+ std::string tab;
+ if (!name.empty()) {
+ dump << indent << "module " << name << "(";
+ for (size_t i=0; i < argnames.size(); i++) {
+ if (i > 0) dump << ", ";
+ dump << argnames[i];
+ if (argexpr[i]) dump << " = " << *argexpr[i];
}
- text += QString(") {\n");
+ dump << ") {\n";
tab = "\t";
}
- {
- QHashIterator<QString, AbstractFunction*> i(functions);
- while (i.hasNext()) {
- i.next();
- text += i.value()->dump(indent + tab, i.key());
- }
+ BOOST_FOREACH(const FunctionContainer::value_type &f, functions) {
+ dump << f.second->dump(indent + tab, f.first);
}
- {
- QHashIterator<QString, AbstractModule*> i(modules);
- while (i.hasNext()) {
- i.next();
- text += i.value()->dump(indent + tab, i.key());
- }
+ BOOST_FOREACH(const AbstractModuleContainer::value_type &m, modules) {
+ dump << m.second->dump(indent + tab, m.first);
}
- for (int i = 0; i < assignments_var.size(); i++) {
- text += QString("%1%2 = %3;\n").arg(indent + tab, assignments_var[i], assignments_expr[i]->dump());
+ for (size_t i = 0; i < assignments_var.size(); i++) {
+ dump << indent << tab << assignments_var[i] << " = " << *assignments_expr[i] << ";\n";
}
- for (int i = 0; i < children.size(); i++) {
- text += children[i]->dump(indent + tab);
+ for (size_t i = 0; i < children.size(); i++) {
+ dump << children[i]->dump(indent + tab);
}
- if (!name.isEmpty()) {
- text += QString("%1}\n").arg(indent);
+ if (!name.empty()) {
+ dump << indent << "}\n";
}
- return text;
+ return dump.str();
}
-QHash<QString, AbstractModule*> builtin_modules;
+Module::AbstractModuleContainer builtin_modules;
void initialize_builtin_modules()
{
@@ -206,6 +210,7 @@ void initialize_builtin_modules()
register_builtin_csgops();
register_builtin_transform();
+ register_builtin_color();
register_builtin_primitives();
register_builtin_surface();
register_builtin_control();
@@ -219,7 +224,8 @@ void initialize_builtin_modules()
void destroy_builtin_modules()
{
- foreach (AbstractModule *v, builtin_modules)
- delete v;
+ BOOST_FOREACH(Module::AbstractModuleContainer::value_type &m, builtin_modules) {
+ delete m.second;
+ }
builtin_modules.clear();
}
diff --git a/src/module.h b/src/module.h
index b680ea1..c28ab34 100644
--- a/src/module.h
+++ b/src/module.h
@@ -1,20 +1,20 @@
#ifndef MODULE_H_
#define MODULE_H_
-#include <QString>
-#include <QVector>
-#include <QHash>
+#include <string>
+#include <vector>
+#include <boost/unordered_map.hpp>
#include "value.h"
class ModuleInstantiation
{
public:
- QString label;
- QString modname;
- QVector<QString> argnames;
- QVector<class Expression*> argexpr;
- QVector<Value> argvalues;
- QVector<ModuleInstantiation*> children;
+ std::string label;
+ std::string modname;
+ std::vector<std::string> argnames;
+ std::vector<class Expression*> argexpr;
+ std::vector<Value> argvalues;
+ std::vector<ModuleInstantiation*> children;
bool tag_root;
bool tag_highlight;
@@ -24,15 +24,17 @@ public:
ModuleInstantiation() : tag_root(false), tag_highlight(false), tag_background(false), ctx(NULL) { }
virtual ~ModuleInstantiation();
- QString dump(QString indent) const;
+ std::string dump(const std::string &indent) const;
class AbstractNode *evaluate(const Context *ctx) const;
+ std::vector<AbstractNode*> evaluateChildren(const Context *ctx = NULL) const;
};
class IfElseModuleInstantiation : public ModuleInstantiation {
public:
virtual ~IfElseModuleInstantiation();
+ std::vector<AbstractNode*> evaluateElseChildren(const Context *ctx = NULL) const;
- QVector<ModuleInstantiation*> else_children;
+ std::vector<ModuleInstantiation*> else_children;
};
class AbstractModule
@@ -40,37 +42,40 @@ class AbstractModule
public:
virtual ~AbstractModule();
virtual class AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
- virtual QString dump(QString indent, QString name) const;
+ virtual std::string dump(const std::string &indent, const std::string &name) const;
};
class Module : public AbstractModule
{
public:
- QHash< QString, Module*> usedlibs;
+ typedef boost::unordered_map<std::string, class Module*> ModuleContainer;
+ ModuleContainer usedlibs;
struct libs_cache_ent {
Module *mod;
- QString cache_id, msg;
+ std::string cache_id, msg;
};
- static QHash<QString, libs_cache_ent> libs_cache;
- static Module *compile_library(QString filename);
+ static boost::unordered_map<std::string, libs_cache_ent> libs_cache;
+ static Module *compile_library(std::string filename);
- QVector<QString> argnames;
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
- QVector<QString> assignments_var;
- QVector<Expression*> assignments_expr;
+ std::vector<std::string> assignments_var;
+ std::vector<Expression*> assignments_expr;
- QHash<QString, class AbstractFunction*> functions;
- QHash<QString, AbstractModule*> modules;
+ typedef boost::unordered_map<std::string, class AbstractFunction*> FunctionContainer;
+ FunctionContainer functions;
+ typedef boost::unordered_map<std::string, AbstractModule*> AbstractModuleContainer;
+ AbstractModuleContainer modules;
- QVector<ModuleInstantiation*> children;
+ std::vector<ModuleInstantiation*> children;
Module() { }
virtual ~Module();
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
- virtual QString dump(QString indent, QString name) const;
+ virtual std::string dump(const std::string &indent, const std::string &name) const;
};
#endif
diff --git a/src/myqhash.h b/src/myqhash.h
new file mode 100644
index 0000000..9156ec2
--- /dev/null
+++ b/src/myqhash.h
@@ -0,0 +1,16 @@
+#ifndef OPENSCAD_QHASH_H_
+#define OPENSCAD_QHASH_H_
+
+/*!
+ Defines a qHash for std::string.
+
+ Note that this header must be included before Qt headers (at least
+ before qhash.h) to take effect.
+ */
+
+#include <qglobal.h>
+#include <string>
+extern uint qHash(const std::string &);
+#include <QHash>
+
+#endif
diff --git a/src/namedcolors.cpp b/src/namedcolors.cpp
new file mode 100644
index 0000000..3150a40
--- /dev/null
+++ b/src/namedcolors.cpp
@@ -0,0 +1,155 @@
+#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
+
+static const struct RGBData {
+ const char *name;
+ uint value;
+} named_colors[] = {
+ { "aliceblue", rgb(240, 248, 255) },
+ { "antiquewhite", rgb(250, 235, 215) },
+ { "aqua", rgb( 0, 255, 255) },
+ { "aquamarine", rgb(127, 255, 212) },
+ { "azure", rgb(240, 255, 255) },
+ { "beige", rgb(245, 245, 220) },
+ { "bisque", rgb(255, 228, 196) },
+ { "black", rgb( 0, 0, 0) },
+ { "blanchedalmond", rgb(255, 235, 205) },
+ { "blue", rgb( 0, 0, 255) },
+ { "blueviolet", rgb(138, 43, 226) },
+ { "brown", rgb(165, 42, 42) },
+ { "burlywood", rgb(222, 184, 135) },
+ { "cadetblue", rgb( 95, 158, 160) },
+ { "chartreuse", rgb(127, 255, 0) },
+ { "chocolate", rgb(210, 105, 30) },
+ { "coral", rgb(255, 127, 80) },
+ { "cornflowerblue", rgb(100, 149, 237) },
+ { "cornsilk", rgb(255, 248, 220) },
+ { "crimson", rgb(220, 20, 60) },
+ { "cyan", rgb( 0, 255, 255) },
+ { "darkblue", rgb( 0, 0, 139) },
+ { "darkcyan", rgb( 0, 139, 139) },
+ { "darkgoldenrod", rgb(184, 134, 11) },
+ { "darkgray", rgb(169, 169, 169) },
+ { "darkgreen", rgb( 0, 100, 0) },
+ { "darkgrey", rgb(169, 169, 169) },
+ { "darkkhaki", rgb(189, 183, 107) },
+ { "darkmagenta", rgb(139, 0, 139) },
+ { "darkolivegreen", rgb( 85, 107, 47) },
+ { "darkorange", rgb(255, 140, 0) },
+ { "darkorchid", rgb(153, 50, 204) },
+ { "darkred", rgb(139, 0, 0) },
+ { "darksalmon", rgb(233, 150, 122) },
+ { "darkseagreen", rgb(143, 188, 143) },
+ { "darkslateblue", rgb( 72, 61, 139) },
+ { "darkslategray", rgb( 47, 79, 79) },
+ { "darkslategrey", rgb( 47, 79, 79) },
+ { "darkturquoise", rgb( 0, 206, 209) },
+ { "darkviolet", rgb(148, 0, 211) },
+ { "deeppink", rgb(255, 20, 147) },
+ { "deepskyblue", rgb( 0, 191, 255) },
+ { "dimgray", rgb(105, 105, 105) },
+ { "dimgrey", rgb(105, 105, 105) },
+ { "dodgerblue", rgb( 30, 144, 255) },
+ { "firebrick", rgb(178, 34, 34) },
+ { "floralwhite", rgb(255, 250, 240) },
+ { "forestgreen", rgb( 34, 139, 34) },
+ { "fuchsia", rgb(255, 0, 255) },
+ { "gainsboro", rgb(220, 220, 220) },
+ { "ghostwhite", rgb(248, 248, 255) },
+ { "gold", rgb(255, 215, 0) },
+ { "goldenrod", rgb(218, 165, 32) },
+ { "gray", rgb(128, 128, 128) },
+ { "green", rgb( 0, 128, 0) },
+ { "greenyellow", rgb(173, 255, 47) },
+ { "grey", rgb(128, 128, 128) },
+ { "honeydew", rgb(240, 255, 240) },
+ { "hotpink", rgb(255, 105, 180) },
+ { "indianred", rgb(205, 92, 92) },
+ { "indigo", rgb( 75, 0, 130) },
+ { "ivory", rgb(255, 255, 240) },
+ { "khaki", rgb(240, 230, 140) },
+ { "lavender", rgb(230, 230, 250) },
+ { "lavenderblush", rgb(255, 240, 245) },
+ { "lawngreen", rgb(124, 252, 0) },
+ { "lemonchiffon", rgb(255, 250, 205) },
+ { "lightblue", rgb(173, 216, 230) },
+ { "lightcoral", rgb(240, 128, 128) },
+ { "lightcyan", rgb(224, 255, 255) },
+ { "lightgoldenrodyellow", rgb(250, 250, 210) },
+ { "lightgray", rgb(211, 211, 211) },
+ { "lightgreen", rgb(144, 238, 144) },
+ { "lightgrey", rgb(211, 211, 211) },
+ { "lightpink", rgb(255, 182, 193) },
+ { "lightsalmon", rgb(255, 160, 122) },
+ { "lightseagreen", rgb( 32, 178, 170) },
+ { "lightskyblue", rgb(135, 206, 250) },
+ { "lightslategray", rgb(119, 136, 153) },
+ { "lightslategrey", rgb(119, 136, 153) },
+ { "lightsteelblue", rgb(176, 196, 222) },
+ { "lightyellow", rgb(255, 255, 224) },
+ { "lime", rgb( 0, 255, 0) },
+ { "limegreen", rgb( 50, 205, 50) },
+ { "linen", rgb(250, 240, 230) },
+ { "magenta", rgb(255, 0, 255) },
+ { "maroon", rgb(128, 0, 0) },
+ { "mediumaquamarine", rgb(102, 205, 170) },
+ { "mediumblue", rgb( 0, 0, 205) },
+ { "mediumorchid", rgb(186, 85, 211) },
+ { "mediumpurple", rgb(147, 112, 219) },
+ { "mediumseagreen", rgb( 60, 179, 113) },
+ { "mediumslateblue", rgb(123, 104, 238) },
+ { "mediumspringgreen", rgb( 0, 250, 154) },
+ { "mediumturquoise", rgb( 72, 209, 204) },
+ { "mediumvioletred", rgb(199, 21, 133) },
+ { "midnightblue", rgb( 25, 25, 112) },
+ { "mintcream", rgb(245, 255, 250) },
+ { "mistyrose", rgb(255, 228, 225) },
+ { "moccasin", rgb(255, 228, 181) },
+ { "navajowhite", rgb(255, 222, 173) },
+ { "navy", rgb( 0, 0, 128) },
+ { "oldlace", rgb(253, 245, 230) },
+ { "olive", rgb(128, 128, 0) },
+ { "olivedrab", rgb(107, 142, 35) },
+ { "orange", rgb(255, 165, 0) },
+ { "orangered", rgb(255, 69, 0) },
+ { "orchid", rgb(218, 112, 214) },
+ { "palegoldenrod", rgb(238, 232, 170) },
+ { "palegreen", rgb(152, 251, 152) },
+ { "paleturquoise", rgb(175, 238, 238) },
+ { "palevioletred", rgb(219, 112, 147) },
+ { "papayawhip", rgb(255, 239, 213) },
+ { "peachpuff", rgb(255, 218, 185) },
+ { "peru", rgb(205, 133, 63) },
+ { "pink", rgb(255, 192, 203) },
+ { "plum", rgb(221, 160, 221) },
+ { "powderblue", rgb(176, 224, 230) },
+ { "purple", rgb(128, 0, 128) },
+ { "red", rgb(255, 0, 0) },
+ { "rosybrown", rgb(188, 143, 143) },
+ { "royalblue", rgb( 65, 105, 225) },
+ { "saddlebrown", rgb(139, 69, 19) },
+ { "salmon", rgb(250, 128, 114) },
+ { "sandybrown", rgb(244, 164, 96) },
+ { "seagreen", rgb( 46, 139, 87) },
+ { "seashell", rgb(255, 245, 238) },
+ { "sienna", rgb(160, 82, 45) },
+ { "silver", rgb(192, 192, 192) },
+ { "skyblue", rgb(135, 206, 235) },
+ { "slateblue", rgb(106, 90, 205) },
+ { "slategray", rgb(112, 128, 144) },
+ { "slategrey", rgb(112, 128, 144) },
+ { "snow", rgb(255, 250, 250) },
+ { "springgreen", rgb( 0, 255, 127) },
+ { "steelblue", rgb( 70, 130, 180) },
+ { "tan", rgb(210, 180, 140) },
+ { "teal", rgb( 0, 128, 128) },
+ { "thistle", rgb(216, 191, 216) },
+ { "tomato", rgb(255, 99, 71) },
+ { "transparent", 0 },
+ { "turquoise", rgb( 64, 224, 208) },
+ { "violet", rgb(238, 130, 238) },
+ { "wheat", rgb(245, 222, 179) },
+ { "white", rgb(255, 255, 255) },
+ { "whitesmoke", rgb(245, 245, 245) },
+ { "yellow", rgb(255, 255, 0) },
+ { "yellowgreen", rgb(154, 205, 50) }
+};
diff --git a/src/node.cc b/src/node.cc
index 87b3c2b..e2f3fa0 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -30,9 +30,14 @@
#include "csgterm.h"
#include "progress.h"
#include "polyset.h"
-#include <QRegExp>
+#include "visitor.h"
+#include "nodedumper.h"
+#include "stl-utils.h"
-int AbstractNode::idx_counter;
+#include <sstream>
+#include <algorithm>
+
+size_t AbstractNode::idx_counter;
AbstractNode::AbstractNode(const ModuleInstantiation *mi)
{
@@ -42,139 +47,47 @@ AbstractNode::AbstractNode(const ModuleInstantiation *mi)
AbstractNode::~AbstractNode()
{
- foreach (AbstractNode *v, children)
- delete v;
-}
-
-QString AbstractNode::mk_cache_id() const
-{
- QString cache_id = dump("");
- cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
- cache_id.remove(' ');
- cache_id.remove('\t');
- cache_id.remove('\n');
- return cache_id;
-}
-
-#ifdef ENABLE_CGAL
-
-AbstractNode::cgal_nef_cache_entry::cgal_nef_cache_entry(const CGAL_Nef_polyhedron &N) :
- N(N), msg(print_messages_stack.last()) { };
-
-QCache<QString, AbstractNode::cgal_nef_cache_entry> AbstractNode::cgal_nef_cache(100000);
-
-static CGAL_Nef_polyhedron render_cgal_nef_polyhedron_backend(const AbstractNode *that, bool intersect)
-{
- QString cache_id = that->mk_cache_id();
- if (that->cgal_nef_cache.contains(cache_id)) {
- that->progress_report();
- PRINT(that->cgal_nef_cache[cache_id]->msg);
- return that->cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
- foreach (AbstractNode *v, that->children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->render_cgal_nef_polyhedron();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- if (intersect)
- N.p2 *= v->render_cgal_nef_polyhedron().p2;
- else
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- } else {
- if (intersect)
- N.p3 *= v->render_cgal_nef_polyhedron().p3;
- else
- N.p3 += v->render_cgal_nef_polyhedron().p3;
- }
- v->progress_report();
- }
-
- that->cgal_nef_cache.insert(cache_id, new AbstractNode::cgal_nef_cache_entry(N), N.weight());
- that->progress_report();
- print_messages_pop();
-
- return N;
+ std::for_each(this->children.begin(), this->children.end(), del_fun<AbstractNode>());
}
-CGAL_Nef_polyhedron AbstractNode::render_cgal_nef_polyhedron() const
+Response AbstractNode::accept(class State &state, Visitor &visitor) const
{
- return render_cgal_nef_polyhedron_backend(this, false);
+ return visitor.visit(state, *this);
}
-CGAL_Nef_polyhedron AbstractIntersectionNode::render_cgal_nef_polyhedron() const
+Response AbstractIntersectionNode::accept(class State &state, Visitor &visitor) const
{
- return render_cgal_nef_polyhedron_backend(this, true);
+ return visitor.visit(state, *this);
}
-#endif /* ENABLE_CGAL */
-
-static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background)
+Response AbstractPolyNode::accept(class State &state, Visitor &visitor) const
{
- CSGTerm *t1 = NULL;
- foreach(AbstractNode *v, that->children) {
- CSGTerm *t2 = v->render_csg_term(m, highlights, background);
- if (t2 && !t1) {
- t1 = t2;
- } else if (t2 && t1) {
- if (intersect)
- t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
- else
- t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
- }
- }
- if (t1 && that->modinst->tag_highlight && highlights)
- highlights->append(t1->link());
- if (t1 && that->modinst->tag_background && background) {
- background->append(t1);
- return NULL;
- }
- return t1;
+ return visitor.visit(state, *this);
}
-CSGTerm *AbstractNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+std::string AbstractNode::toString() const
{
- return render_csg_term_backend(this, false, m, highlights, background);
+ return this->name() + "()";
}
-CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+std::string AbstractNode::name() const
{
- return render_csg_term_backend(this, true, m, highlights, background);
+ return "group";
}
-QString AbstractNode::dump(QString indent) const
+std::string AbstractIntersectionNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text = indent + QString("n%1: group() {\n").arg(idx);
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- ((AbstractNode*)this)->dump_cache = text + indent + "}\n";
- }
- return dump_cache;
+ return this->name() + "()";
}
-QString AbstractIntersectionNode::dump(QString indent) const
+std::string AbstractIntersectionNode::name() const
{
- if (dump_cache.isEmpty()) {
- QString text = indent + QString("n%1: intersection() {\n").arg(idx);
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- ((AbstractNode*)this)->dump_cache = text + indent + "}\n";
- }
- return dump_cache;
+ return "intersection_for";
}
void AbstractNode::progress_prepare()
{
- foreach (AbstractNode *v, children)
- v->progress_prepare();
+ std::for_each(this->children.begin(), this->children.end(), std::mem_fun(&AbstractNode::progress_prepare));
this->progress_mark = ++progress_report_count;
}
@@ -183,52 +96,8 @@ void AbstractNode::progress_report() const
progress_update(this, this->progress_mark);
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron AbstractPolyNode::render_cgal_nef_polyhedron() const
+std::ostream &operator<<(std::ostream &stream, const AbstractNode &node)
{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- PolySet *ps = render_polyset(RENDER_CGAL);
- try {
- CGAL_Nef_polyhedron N = ps->render_cgal_nef_polyhedron();
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- ps->unlink();
- return N;
- }
- catch (...) { // Don't leak the PolySet on ProgressCancelException
- ps->unlink();
- throw;
- }
+ stream << node.toString();
+ return stream;
}
-
-#endif /* ENABLE_CGAL */
-
-CSGTerm *AbstractPolyNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- PolySet *ps = render_polyset(RENDER_OPENCSG);
- return render_csg_term_from_ps(m, highlights, background, ps, modinst, idx);
-}
-
-CSGTerm *AbstractPolyNode::render_csg_term_from_ps(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, PolySet *ps, const ModuleInstantiation *modinst, int idx)
-{
- CSGTerm *t = new CSGTerm(ps, m, QString("n%1").arg(idx));
- if (modinst->tag_highlight && highlights)
- highlights->append(t->link());
- if (modinst->tag_background && background) {
- background->append(t);
- return NULL;
- }
- return t;
-}
-
diff --git a/src/node.h b/src/node.h
index 2e1d718..a3e1cad 100644
--- a/src/node.h
+++ b/src/node.h
@@ -1,12 +1,10 @@
#ifndef NODE_H_
#define NODE_H_
-#include <QCache>
-#include <QVector>
+#include <vector>
+#include <string>
-#ifdef ENABLE_CGAL
-#include "cgal.h"
-#endif
+#include "traverser.h"
extern int progress_report_count;
extern void (*progress_report_f)(const class AbstractNode*, void*, int);
@@ -15,64 +13,78 @@ extern void *progress_report_vp;
void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp);
void progress_report_fin();
+/*!
+
+ The node tree is the result of evaluation of a module instantiation
+ tree. Both the module tree and the node tree are regenerated from
+ scratch for each compile.
+
+ */
class AbstractNode
{
- static int idx_counter; // Node instantiation index
+ // FIXME: the idx_counter/idx is mostly (only?) for debugging.
+ // We can hash on pointer value or smth. else.
+ // -> remove and
+ // use smth. else to display node identifier in CSG tree output?
+ static size_t idx_counter; // Node instantiation index
public:
+ AbstractNode(const class ModuleInstantiation *mi);
+ virtual ~AbstractNode();
+ virtual Response accept(class State &state, class Visitor &visitor) const;
+ virtual std::string toString() const;
+ /*! The 'OpenSCAD name' of this node, defaults to classname, but can be
+ overloaded to provide specialization for e.g. CSG nodes, primitive nodes etc.
+ Used for human-readable output. */
+ virtual std::string name() const;
+
+ // FIXME: Make return value a reference
+ const std::vector<AbstractNode*> &getChildren() const {
+ return this->children;
+ }
+ size_t index() const { return this->idx; }
+
static void resetIndexCounter() { idx_counter = 1; }
- QVector<AbstractNode*> children;
- const class ModuleInstantiation *modinst;
+ // FIXME: Make protected
+ std::vector<AbstractNode*> children;
+ const ModuleInstantiation *modinst;
+ // progress_mark is a running number used for progress indication
+ // FIXME: Make all progress handling external, put it in the traverser class?
int progress_mark;
void progress_prepare();
void progress_report() const;
- int idx;
- QString dump_cache;
-
- AbstractNode(const ModuleInstantiation *mi);
- virtual ~AbstractNode();
- virtual QString mk_cache_id() const;
-#ifdef ENABLE_CGAL
- struct cgal_nef_cache_entry {
- CGAL_Nef_polyhedron N;
- QString msg;
- cgal_nef_cache_entry(const CGAL_Nef_polyhedron &N);
- };
- static QCache<QString, cgal_nef_cache_entry> cgal_nef_cache;
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
- class CSGTerm *render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const;
-#endif
- virtual class CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
+ int idx; // Node index (unique per tree)
};
class AbstractIntersectionNode : public AbstractNode
{
public:
AbstractIntersectionNode(const ModuleInstantiation *mi) : AbstractNode(mi) { };
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
+ virtual ~AbstractIntersectionNode() { };
+ virtual Response accept(class State &state, class Visitor &visitor) const;
+ virtual std::string toString() const;
+ virtual std::string name() const;
};
class AbstractPolyNode : public AbstractNode
{
public:
+ AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { };
+ virtual ~AbstractPolyNode() { };
+ virtual Response accept(class State &state, class Visitor &visitor) const;
+
enum render_mode_e {
RENDER_CGAL,
RENDER_OPENCSG
};
- AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { };
- virtual class PolySet *render_polyset(render_mode_e mode) const = 0;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- static CSGTerm *render_csg_term_from_ps(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, PolySet *ps, const ModuleInstantiation *modinst, int idx);
+ /*! Should return a PolySet of the given geometry. It's normal to return an
+ empty PolySet if smth. is wrong, but don't return NULL unless we change the calling
+ strategy for this method. */
+ virtual class PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const = 0;
};
+std::ostream &operator<<(std::ostream &stream, const AbstractNode &node);
+
#endif
diff --git a/src/nodecache.h b/src/nodecache.h
new file mode 100644
index 0000000..cc3355e
--- /dev/null
+++ b/src/nodecache.h
@@ -0,0 +1,46 @@
+#ifndef NODECACHE_H_
+#define NODECACHE_H_
+
+#include <vector>
+#include <string>
+#include "node.h"
+
+/*!
+ Caches string values per node based on the node.index().
+ The node index guaranteed to be unique per node tree since the index is reset
+ every time a new tree is generated.
+*/
+class NodeCache
+{
+public:
+ NodeCache() { }
+ virtual ~NodeCache() { }
+
+ bool contains(const AbstractNode &node) const {
+ return !(*this)[node].empty();
+ }
+
+ const std::string & operator[](const AbstractNode &node) const {
+ if (this->cache.size() > node.index()) return this->cache[node.index()];
+ else return this->nullvalue;
+ }
+
+ void insert(const class AbstractNode &node, const std::string & value) {
+ if (this->cache.size() <= node.index()) this->cache.resize(node.index() + 1);
+ this->cache[node.index()] = value;
+ }
+
+ void remove(const class AbstractNode &node) {
+ if (this->cache.size() > node.index()) this->cache[node.index()] = std::string();
+ }
+
+ void clear() {
+ this->cache.clear();
+ }
+
+private:
+ std::vector<std::string> cache;
+ std::string nullvalue;
+};
+
+#endif
diff --git a/src/nodedumper.cc b/src/nodedumper.cc
new file mode 100644
index 0000000..d75c703
--- /dev/null
+++ b/src/nodedumper.cc
@@ -0,0 +1,103 @@
+#include "nodedumper.h"
+#include <string>
+#include <map>
+#include <list>
+#include "visitor.h"
+#include "state.h"
+#include "nodecache.h"
+
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+
+/*!
+ \class NodeDumper
+
+ A visitor responsible for creating a text dump of a node tree. Also
+ contains a cache for fast retrieval of the text representation of
+ any node or subtree.
+*/
+
+bool NodeDumper::isCached(const AbstractNode &node) const
+{
+ return !this->cache[node].empty();
+}
+
+/*!
+ Indent or deindent. Must be called before we output any children.
+*/
+void NodeDumper::handleIndent(const State &state)
+{
+ if (state.isPrefix()) {
+ this->currindent += "\t";
+ }
+ else if (state.isPostfix()) {
+ this->currindent.erase((this->currindent.length() >= 1) ?
+ this->currindent.length() - 1 : 0);
+ }
+}
+
+/*!
+ Dumps the block of children contained in this->visitedchildren,
+ including braces and indentation.
+ All children are assumed to be cached already.
+ */
+string NodeDumper::dumpChildren(const AbstractNode &node)
+{
+ std::stringstream dump;
+ if (!this->visitedchildren[node.index()].empty()) {
+ dump << " {\n";
+
+ for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
+ iter != this->visitedchildren[node.index()].end();
+ iter++) {
+ assert(isCached(**iter));
+ dump << this->cache[**iter] << "\n";
+ }
+
+ dump << this->currindent << "}";
+ }
+ else {
+ dump << ";";
+ }
+ return dump.str();
+}
+
+/*!
+ Called for each node in the tree.
+ Will abort traversal if we're cached
+*/
+Response NodeDumper::visit(State &state, const AbstractNode &node)
+{
+ if (isCached(node)) return PruneTraversal;
+
+ handleIndent(state);
+ if (state.isPostfix()) {
+ std::stringstream dump;
+ dump << this->currindent;
+ if (this->idprefix) dump << "n" << node.index() << ":";
+ dump << node;
+ dump << dumpChildren(node);
+ this->cache.insert(node, dump.str());
+ }
+
+ handleVisitedChildren(state, node);
+ return ContinueTraversal;
+}
+
+/*!
+ Adds this given node to its parent's child list.
+ Should be called for all nodes, including leaf nodes.
+*/
+void NodeDumper::handleVisitedChildren(const State &state, const AbstractNode &node)
+{
+ if (state.isPostfix()) {
+ this->visitedchildren.erase(node.index());
+ if (!state.parent()) {
+ this->root = &node;
+ }
+ else {
+ this->visitedchildren[state.parent()->index()].push_back(&node);
+ }
+ }
+}
diff --git a/src/nodedumper.h b/src/nodedumper.h
new file mode 100644
index 0000000..efaf4fa
--- /dev/null
+++ b/src/nodedumper.h
@@ -0,0 +1,40 @@
+#ifndef NODEDUMPER_H_
+#define NODEDUMPER_H_
+
+#include <string>
+#include <map>
+#include <list>
+#include "visitor.h"
+#include "nodecache.h"
+
+using std::string;
+using std::map;
+using std::list;
+
+class NodeDumper : public Visitor
+{
+public:
+ /*! If idPrefix is true, we will output "n<id>:" in front of each node,
+ which is useful for debugging. */
+ NodeDumper(NodeCache &cache, bool idPrefix = false) :
+ cache(cache), idprefix(idPrefix), root(NULL) { }
+ virtual ~NodeDumper() {}
+
+ virtual Response visit(State &state, const AbstractNode &node);
+
+private:
+ void handleVisitedChildren(const State &state, const AbstractNode &node);
+ bool isCached(const AbstractNode &node) const;
+ void handleIndent(const State &state);
+ string dumpChildren(const AbstractNode &node);
+
+ NodeCache &cache;
+ bool idprefix;
+
+ string currindent;
+ const AbstractNode *root;
+ typedef list<const AbstractNode *> ChildList;
+ map<int, ChildList> visitedchildren;
+};
+
+#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 180dbdf..c4b0d5f 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -24,6 +24,7 @@
*
*/
+#include "myqhash.h"
#include "openscad.h"
#include "MainWindow.h"
#include "node.h"
@@ -32,13 +33,19 @@
#include "value.h"
#include "export.h"
#include "builtin.h"
+#include "nodedumper.h"
+#include "printutils.h"
+#include "handle_dep.h"
#include <string>
#include <vector>
+#include <fstream>
#ifdef ENABLE_CGAL
-#include "cgal.h"
+#include "CGAL_Nef_polyhedron.h"
#include <CGAL/assertions_behaviour.h>
+#include "CGALEvaluator.h"
+#include "PolySetCGALEvaluator.h"
#endif
#include <QApplication>
@@ -46,7 +53,10 @@
#include <QDir>
#include <QSet>
#include <QSettings>
+#include <QTextStream>
#include <boost/program_options.hpp>
+#include <sstream>
+
#ifdef Q_WS_MAC
#include "EventFilter.h"
#include "AppleEvents.h"
@@ -74,9 +84,7 @@ static void version()
exit(1);
}
-QString commandline_commands;
-const char *make_command = NULL;
-QSet<QString> dependencies;
+std::string commandline_commands;
QString currentdir;
QString examplesdir;
QString librarydir;
@@ -84,19 +92,6 @@ QString librarydir;
using std::string;
using std::vector;
-void handle_dep(QString filename)
-{
- if (filename.startsWith("/"))
- dependencies.insert(filename);
- else
- dependencies.insert(QDir::currentPath() + QString("/") + filename);
- if (!QFile(filename).exists() && make_command) {
- char buffer[4096];
- snprintf(buffer, 4096, "%s '%s'", make_command, filename.replace("'", "'\\''").toUtf8().data());
- system(buffer); // FIXME: Handle error
- }
-}
-
int main(int argc, char **argv)
{
int rc = 0;
@@ -194,8 +189,8 @@ int main(int argc, char **argv)
const vector<string> &commands = vm["D"].as<vector<string> >();
for (vector<string>::const_iterator i = commands.begin(); i != commands.end(); i++) {
- commandline_commands.append(i->c_str());
- commandline_commands.append(";\n");
+ commandline_commands += *i;
+ commandline_commands += ";\n";
}
}
@@ -249,6 +244,17 @@ int main(int argc, char **argv)
librarydir = libdir.path();
}
+ // Initialize global visitors
+ NodeCache nodecache;
+ NodeDumper dumper(nodecache);
+ Tree tree;
+#ifdef ENABLE_CGAL
+ // FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator cgalevaluator(cache, tree);
+ PolySetCGALEvaluator psevaluator(cgalevaluator);
+#endif
+
if (stl_output_file || off_output_file || dxf_output_file)
{
if (!filename)
@@ -265,9 +271,9 @@ int main(int argc, char **argv)
Value zero3;
zero3.type = Value::VECTOR;
- zero3.vec.append(new Value(0.0));
- zero3.vec.append(new Value(0.0));
- zero3.vec.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
root_ctx.set_variable("$vpt", zero3);
root_ctx.set_variable("$vpr", zero3);
@@ -283,15 +289,16 @@ int main(int argc, char **argv)
fprintf(stderr, "Can't open input file `%s'!\n", filename);
exit(1);
} else {
- QString text;
+ std::stringstream text;
char buffer[513];
int ret;
while ((ret = fread(buffer, 1, 512, fp)) > 0) {
buffer[ret] = 0;
- text += buffer;
+ text << buffer;
}
fclose(fp);
- root_module = parse((text+commandline_commands).toAscii().data(), fileInfo.absolutePath().toLocal8Bit(), false);
+ text << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
}
QDir::setCurrent(fileInfo.absolutePath());
@@ -299,41 +306,60 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
- CGAL_Nef_polyhedron *root_N;
- root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron());
+ tree.setRoot(root_node);
+ CGAL_Nef_polyhedron root_N = cgalevaluator.evaluateCGALMesh(*tree.root());
QDir::setCurrent(original_path.absolutePath());
if (deps_output_file) {
- fp = fopen(deps_output_file, "wt");
- if (!fp) {
- fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", deps_output_file);
+ if (!write_deps(deps_output_file,
+ stl_output_file ? stl_output_file : off_output_file)) {
exit(1);
}
- fprintf(fp, "%s:", stl_output_file ? stl_output_file : off_output_file);
- QSetIterator<QString> i(dependencies);
- while (i.hasNext())
- fprintf(fp, " \\\n\t%s", i.next().toUtf8().data());
- fprintf(fp, "\n");
- fclose(fp);
}
- if (root_N->dim == 3 && !root_N->p3.is_simple()) {
- fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n");
- exit(1);
+ if (stl_output_file) {
+ if (root_N->dim == 3 && !root_N->p3.is_simple()) {
+ fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n");
+ exit(1);
+ }
+ std::ofstream fstream(stl_output_file);
+ if (!fstream.is_open()) {
+ PRINTF("Can't open file \"%s\" for export", stl_output_file);
+ }
+ else {
+ export_stl(&root_N, fstream, NULL);
+ fstream.close();
+ }
}
- if (stl_output_file)
- export_stl(root_N, stl_output_file, NULL);
-
- if (off_output_file)
- export_off(root_N, off_output_file, NULL);
+ if (off_output_file) {
+ if (root_N->dim == 3 && !root_N->p3.is_simple()) {
+ fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n");
+ exit(1);
+ }
+ std::ofstream fstream(stl_output_file);
+ if (!fstream.is_open()) {
+ PRINTF("Can't open file \"%s\" for export", stl_output_file);
+ }
+ else {
+ export_off(&root_N, fstream, NULL);
+ fstream.close();
+ }
+ }
- if (dxf_output_file)
- export_dxf(root_N, dxf_output_file, NULL);
+ if (dxf_output_file) {
+ std::ofstream fstream(dxf_output_file);
+ if (!fstream.is_open()) {
+ PRINTF("Can't open file \"%s\" for export", dxf_output_file);
+ }
+ else {
+ export_dxf(&root_N, fstream, NULL);
+ fstream.close();
+ }
+ }
delete root_node;
- delete root_N;
#else
fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n");
exit(1);
diff --git a/src/openscad.h b/src/openscad.h
index e022668..9d97bac 100644
--- a/src/openscad.h
+++ b/src/openscad.h
@@ -41,12 +41,11 @@
extern class AbstractModule *parse(const char *text, const char *path, int debug);
extern int get_fragments_from_r(double r, double fn, double fs, double fa);
-#include <QString>
-extern QString commandline_commands;
+#include <string>
+extern std::string commandline_commands;
extern int parser_error_pos;
-extern void handle_dep(QString filename);
-
+#include <QString>
// The CWD when application started. We shouldn't change CWD, but until we stop
// doing this, use currentdir to get the original CWD.
extern QString currentdir;
diff --git a/src/parser.y b/src/parser.y
index aad5ba0..2d78d1f 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -44,6 +44,8 @@
#include "value.h"
#include "function.h"
#include "printutils.h"
+#include <sstream>
+#include <boost/foreach.hpp>
int parser_error_pos = -1;
@@ -54,18 +56,18 @@ int lexerget_lineno(void);
int lexerlex_destroy(void);
int lexerlex(void);
-QVector<Module*> module_stack;
+std::vector<Module*> module_stack;
Module *module;
class ArgContainer {
-public:
- QString argname;
+public:
+ std::string argname;
Expression *argexpr;
};
class ArgsContainer {
public:
- QVector<QString> argnames;
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
};
%}
@@ -144,29 +146,29 @@ statement:
'{' inner_input '}' |
module_instantiation {
if ($1) {
- module->children.append($1);
+ module->children.push_back($1);
} else {
delete $1;
}
} |
TOK_ID '=' expr ';' {
bool add_new_assignment = true;
- for (int i = 0; i < module->assignments_var.size(); i++) {
- if (module->assignments_var[i] != QString($1))
+ for (size_t i = 0; i < module->assignments_var.size(); i++) {
+ if (module->assignments_var[i] != $1)
continue;
delete module->assignments_expr[i];
module->assignments_expr[i] = $3;
add_new_assignment = false;
}
if (add_new_assignment) {
- module->assignments_var.append($1);
- module->assignments_expr.append($3);
+ module->assignments_var.push_back($1);
+ module->assignments_expr.push_back($3);
free($1);
}
} |
TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' {
Module *p = module;
- module_stack.append(module);
+ module_stack.push_back(module);
module = new Module();
p->modules[$2] = module;
module->argnames = $4->argnames;
@@ -174,7 +176,7 @@ statement:
free($2);
delete $4;
} statement {
- module = module_stack.last();
+ module = module_stack.back();
module_stack.pop_back();
} |
TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr {
@@ -192,7 +194,7 @@ children_instantiation:
module_instantiation {
$$ = new ModuleInstantiation();
if ($1) {
- $$->children.append($1);
+ $$->children.push_back($1);
} else {
delete $1;
}
@@ -205,13 +207,13 @@ if_statement:
TOK_IF '(' expr ')' children_instantiation {
$$ = new IfElseModuleInstantiation();
$$->modname = "if";
- $$->argnames.append(QString());
- $$->argexpr.append($3);
+ $$->argnames.push_back("");
+ $$->argexpr.push_back($3);
if ($$) {
$$->children = $5->children;
} else {
- for (int i = 0; i < $5->children.count(); i++)
+ for (size_t i = 0; i < $5->children.size(); i++)
delete $5->children[i];
}
$5->children.clear();
@@ -227,7 +229,7 @@ ifelse_statement:
if ($$) {
$$->else_children = $3->children;
} else {
- for (int i = 0; i < $3->children.count(); i++)
+ for (size_t i = 0; i < $3->children.size(); i++)
delete $3->children[i];
}
$3->children.clear();
@@ -243,7 +245,7 @@ module_instantiation:
if ($$) {
$$->children = $2->children;
} else {
- for (int i = 0; i < $2->children.count(); i++)
+ for (size_t i = 0; i < $2->children.size(); i++)
delete $2->children[i];
}
$2->children.clear();
@@ -261,7 +263,7 @@ module_instantiation_list:
$$ = $1;
if ($$) {
if ($2)
- $$->children.append($2);
+ $$->children.push_back($2);
} else {
delete $2;
}
@@ -270,7 +272,7 @@ module_instantiation_list:
single_module_instantiation:
TOK_ID '(' arguments_call ')' {
$$ = new ModuleInstantiation();
- $$->modname = QString($1);
+ $$->modname = $1;
$$->argnames = $3->argnames;
$$->argexpr = $3->argexpr;
free($1);
@@ -279,7 +281,7 @@ single_module_instantiation:
TOK_ID ':' single_module_instantiation {
$$ = $3;
if ($$)
- $$->label = QString($1);
+ $$->label = $1;
free($1);
} |
'!' single_module_instantiation {
@@ -321,20 +323,20 @@ expr:
TOK_ID {
$$ = new Expression();
$$->type = "L";
- $$->var_name = QString($1);
+ $$->var_name = $1;
free($1);
} |
expr '.' TOK_ID {
$$ = new Expression();
$$->type = "N";
- $$->children.append($1);
- $$->var_name = QString($3);
+ $$->children.push_back($1);
+ $$->var_name = $3;
free($3);
} |
TOK_STRING {
$$ = new Expression();
$$->type = "C";
- $$->const_value = new Value(QString($1));
+ $$->const_value = new Value(std::string($1));
free($1);
} |
TOK_NUMBER {
@@ -348,16 +350,16 @@ expr:
e_one->const_value = new Value(1.0);
$$ = new Expression();
$$->type = "R";
- $$->children.append($2);
- $$->children.append(e_one);
- $$->children.append($4);
+ $$->children.push_back($2);
+ $$->children.push_back(e_one);
+ $$->children.push_back($4);
} |
'[' expr ':' expr ':' expr ']' {
$$ = new Expression();
$$->type = "R";
- $$->children.append($2);
- $$->children.append($4);
- $$->children.append($6);
+ $$->children.push_back($2);
+ $$->children.push_back($4);
+ $$->children.push_back($6);
} |
'[' optional_commas ']' {
$$ = new Expression();
@@ -371,80 +373,80 @@ expr:
expr '*' expr {
$$ = new Expression();
$$->type = "*";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '/' expr {
$$ = new Expression();
$$->type = "/";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '%' expr {
$$ = new Expression();
$$->type = "%";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '+' expr {
$$ = new Expression();
$$->type = "+";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '-' expr {
$$ = new Expression();
$$->type = "-";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '<' expr {
$$ = new Expression();
$$->type = "<";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr LE expr {
$$ = new Expression();
$$->type = "<=";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr EQ expr {
$$ = new Expression();
$$->type = "==";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr NE expr {
$$ = new Expression();
$$->type = "!=";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr GE expr {
$$ = new Expression();
$$->type = ">=";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr '>' expr {
$$ = new Expression();
$$->type = ">";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr AND expr {
$$ = new Expression();
$$->type = "&&";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
expr OR expr {
$$ = new Expression();
$$->type = "||";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
'+' expr {
$$ = $2;
@@ -452,12 +454,12 @@ expr:
'-' expr {
$$ = new Expression();
$$->type = "I";
- $$->children.append($2);
+ $$->children.push_back($2);
} |
'!' expr {
$$ = new Expression();
$$->type = "!";
- $$->children.append($2);
+ $$->children.push_back($2);
} |
'(' expr ')' {
$$ = $2;
@@ -465,20 +467,20 @@ expr:
expr '?' expr ':' expr {
$$ = new Expression();
$$->type = "?:";
- $$->children.append($1);
- $$->children.append($3);
- $$->children.append($5);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
+ $$->children.push_back($5);
} |
expr '[' expr ']' {
$$ = new Expression();
$$->type = "[]";
- $$->children.append($1);
- $$->children.append($3);
+ $$->children.push_back($1);
+ $$->children.push_back($3);
} |
TOK_ID '(' arguments_call ')' {
$$ = new Expression();
$$->type = "F";
- $$->call_funcname = QString($1);
+ $$->call_funcname = $1;
$$->call_argnames = $3->argnames;
$$->children = $3->argexpr;
free($1);
@@ -492,11 +494,11 @@ vector_expr:
expr {
$$ = new Expression();
$$->type = 'V';
- $$->children.append($1);
+ $$->children.push_back($1);
} |
vector_expr ',' optional_commas expr {
$$ = $1;
- $$->children.append($4);
+ $$->children.push_back($4);
} ;
arguments_decl:
@@ -505,27 +507,27 @@ arguments_decl:
} |
argument_decl {
$$ = new ArgsContainer();
- $$->argnames.append($1->argname);
- $$->argexpr.append($1->argexpr);
+ $$->argnames.push_back($1->argname);
+ $$->argexpr.push_back($1->argexpr);
delete $1;
} |
arguments_decl ',' optional_commas argument_decl {
$$ = $1;
- $$->argnames.append($4->argname);
- $$->argexpr.append($4->argexpr);
+ $$->argnames.push_back($4->argname);
+ $$->argexpr.push_back($4->argexpr);
delete $4;
} ;
argument_decl:
TOK_ID {
$$ = new ArgContainer();
- $$->argname = QString($1);
+ $$->argname = $1;
$$->argexpr = NULL;
free($1);
} |
TOK_ID '=' expr {
$$ = new ArgContainer();
- $$->argname = QString($1);
+ $$->argname = $1;
$$->argexpr = $3;
free($1);
} ;
@@ -536,14 +538,14 @@ arguments_call:
} |
argument_call {
$$ = new ArgsContainer();
- $$->argnames.append($1->argname);
- $$->argexpr.append($1->argexpr);
+ $$->argnames.push_back($1->argname);
+ $$->argexpr.push_back($1->argexpr);
delete $1;
} |
arguments_call ',' optional_commas argument_call {
$$ = $1;
- $$->argnames.append($4->argname);
- $$->argexpr.append($4->argexpr);
+ $$->argnames.push_back($4->argname);
+ $$->argexpr.push_back($4->argexpr);
delete $4;
} ;
@@ -554,7 +556,7 @@ argument_call:
} |
TOK_ID '=' expr {
$$ = new ArgContainer();
- $$->argname = QString($1);
+ $$->argname = $1;
$$->argexpr = $3;
free($1);
} ;
@@ -596,13 +598,11 @@ AbstractModule *parse(const char *text, const char *path, int debug)
if (!module)
return NULL;
- QHashIterator<QString, Module*> i(module->usedlibs);
- while (i.hasNext()) {
- i.next();
- module->usedlibs[i.key()] = Module::compile_library(i.key());
- if (!module->usedlibs[i.key()]) {
- PRINTF("WARNING: Failed to compile library `%s'.", i.key().toUtf8().data());
- module->usedlibs.remove(i.key());
+ BOOST_FOREACH(Module::ModuleContainer::value_type &m, module->usedlibs) {
+ module->usedlibs[m.first] = Module::compile_library(m.first);
+ if (!module->usedlibs[m.first]) {
+ PRINTF("WARNING: Failed to compile library `%s'.", m.first.c_str());
+ module->usedlibs.erase(m.first);
}
}
@@ -610,46 +610,47 @@ AbstractModule *parse(const char *text, const char *path, int debug)
return module;
}
-QHash<QString, Module::libs_cache_ent> Module::libs_cache;
+boost::unordered_map<std::string, Module::libs_cache_ent> Module::libs_cache;
-Module *Module::compile_library(QString filename)
+Module *Module::compile_library(std::string filename)
{
struct stat st;
memset(&st, 0, sizeof(struct stat));
- stat(filename.toAscii().data(), &st);
+ stat(filename.c_str(), &st);
- QString cache_id;
- cache_id.sprintf("%x.%x", (int)st.st_mtime, (int)st.st_size);
+ std::stringstream idstream;
+ idstream << std::hex << st.st_mtime << "." << st.st_size;
+ std::string cache_id = idstream.str();
- if (libs_cache.contains(filename) && libs_cache[filename].cache_id == cache_id) {
- PRINT(libs_cache[filename].msg);
- return &(*libs_cache[filename].mod);
+ if (libs_cache.find(filename) != libs_cache.end() && libs_cache[filename].cache_id == cache_id) {
+ PRINTF("%s", libs_cache[filename].msg.c_str());
+ return &(*libs_cache[filename].mod);
}
- QFile f(filename);
+ QFile f(QString::fromStdString(filename));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTF("WARNING: Can't open library file `%s'.", filename.toUtf8().data());
+ PRINTF("WARNING: Can't open library file `%s'.", filename.c_str());
return NULL;
}
QString text = QTextStream(&f).readAll();
print_messages_push();
- PRINTF("Compiling library `%s'.", filename.toUtf8().data());
- libs_cache_ent e = { NULL, cache_id, QString("WARNING: Library `%1' tries to recursively use itself!").arg(filename) };
- if (libs_cache.contains(filename))
+ PRINTF("Compiling library `%s'.", filename.c_str());
+ 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;
- Module *lib_mod = dynamic_cast<Module*>(parse(text.toLocal8Bit(), QFileInfo(filename).absoluteDir().absolutePath().toLocal8Bit(), 0));
+ Module *lib_mod = dynamic_cast<Module*>(parse(text.toLocal8Bit(), QFileInfo(QString::fromStdString(filename)).absoluteDir().absolutePath().toLocal8Bit(), 0));
module = backup_mod;
if (lib_mod) {
libs_cache[filename].mod = lib_mod;
- libs_cache[filename].msg = print_messages_stack.last();
+ libs_cache[filename].msg = print_messages_stack.last().toStdString();
} else {
- libs_cache.remove(filename);
+ libs_cache.erase(filename);
}
print_messages_pop();
diff --git a/src/polyset.cc b/src/polyset.cc
index 9601fef..db37b49 100644
--- a/src/polyset.cc
+++ b/src/polyset.cc
@@ -26,22 +26,15 @@
#include "polyset.h"
#include "printutils.h"
-#include "Preferences.h"
+// FIXME: Reenable/rewrite - don't be dependant on GUI
+// #include "Preferences.h"
#ifdef ENABLE_CGAL
#include <CGAL/assertions_behaviour.h>
#include <CGAL/exceptions.h>
#endif
#include <Eigen/Core>
#include <Eigen/LU>
-
-QCache<QString,PolySet::ps_cache_entry> PolySet::ps_cache(100);
-
-PolySet::ps_cache_entry::ps_cache_entry(PolySet *ps) :
- ps(ps), msg(print_messages_stack.last()) { }
-
-PolySet::ps_cache_entry::~ps_cache_entry() {
- ps->unlink();
-}
+#include <QColor>
PolySet::PolySet() : grid(GRID_FINE)
{
@@ -69,26 +62,26 @@ void PolySet::unlink()
void PolySet::append_poly()
{
- polygons.append(Polygon());
+ polygons.push_back(Polygon());
}
void PolySet::append_vertex(double x, double y, double z)
{
grid.align(x, y, z);
- polygons.last().append(Point(x, y, z));
+ polygons.back().push_back(Vector3d(x, y, z));
}
void PolySet::insert_vertex(double x, double y, double z)
{
grid.align(x, y, z);
- polygons.last().insert(0, Point(x, y, z));
+ polygons.back().insert(polygons.back().begin(), Vector3d(x, y, z));
}
-static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const PolySet::Point *p1, const PolySet::Point *p2, bool e0, bool e1, bool e2, double z, bool mirrored)
+static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector3d &p1, const Vector3d &p2, bool e0, bool e1, bool e2, double z, bool mirrored)
{
- double ax = p1->x - p0->x, bx = p1->x - p2->x;
- double ay = p1->y - p0->y, by = p1->y - p2->y;
- double az = p1->z - p0->z, bz = p1->z - p2->z;
+ double ax = p1[0] - p0[0], bx = p1[0] - p2[0];
+ double ay = p1[1] - p0[1], by = p1[1] - p2[1];
+ double az = p1[2] - p0[2], bz = p1[2] - p2[2];
double nx = ay*bz - az*by;
double ny = az*bx - ax*bz;
double nz = ax*by - ay*bx;
@@ -100,39 +93,39 @@ static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const
double e1f = e1 ? 2.0 : -1.0;
double e2f = e2 ? 2.0 : -1.0;
glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f);
- glVertexAttrib3d(shaderinfo[4], p1->x, p1->y, p1->z + z);
- glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z);
+ glVertexAttrib3d(shaderinfo[4], p1[0], p1[1], p1[2] + z);
+ glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z);
glVertexAttrib3d(shaderinfo[6], 0.0, 1.0, 0.0);
- glVertex3d(p0->x, p0->y, p0->z + z);
+ glVertex3d(p0[0], p0[1], p0[2] + z);
if (!mirrored) {
glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f);
- glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z);
- glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z);
+ glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z);
+ glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z);
glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0);
- glVertex3d(p1->x, p1->y, p1->z + z);
+ glVertex3d(p1[0], p1[1], p1[2] + z);
}
glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f);
- glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z);
- glVertexAttrib3d(shaderinfo[5], p1->x, p1->y, p1->z + z);
+ glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z);
+ glVertexAttrib3d(shaderinfo[5], p1[0], p1[1], p1[2] + z);
glVertexAttrib3d(shaderinfo[6], 1.0, 0.0, 0.0);
- glVertex3d(p2->x, p2->y, p2->z + z);
+ glVertex3d(p2[0], p2[1], p2[2] + z);
if (mirrored) {
glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f);
- glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z);
- glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z);
+ glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z);
+ glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z);
glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0);
- glVertex3d(p1->x, p1->y, p1->z + z);
+ glVertex3d(p1[0], p1[1], p1[2] + z);
}
}
else
#endif
{
- glVertex3d(p0->x, p0->y, p0->z + z);
+ glVertex3d(p0[0], p0[1], p0[2] + z);
if (!mirrored)
- glVertex3d(p1->x, p1->y, p1->z + z);
- glVertex3d(p2->x, p2->y, p2->z + z);
+ glVertex3d(p1[0], p1[1], p1[2] + z);
+ glVertex3d(p2[0], p2[1], p2[2] + z);
if (mirrored)
- glVertex3d(p1->x, p1->y, p1->z + z);
+ glVertex3d(p1[0], p1[1], p1[2] + z);
}
}
@@ -145,7 +138,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m
bool mirrored = m3f.determinant() < 0;
if (colormode == COLORMODE_MATERIAL) {
- const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR);
+// FIXME: Reenable/rewrite - don't be dependant on GUI
+// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR);
+ const QColor &col = QColor(0xf9, 0xd7, 0x2c);
glColor3f(col.redF(), col.greenF(), col.blueF());
#ifdef ENABLE_OPENCSG
if (shaderinfo) {
@@ -155,7 +150,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m
#endif /* ENABLE_OPENCSG */
}
if (colormode == COLORMODE_CUTOUT) {
- const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR);
+// FIXME: Reenable/rewrite - don't be dependant on GUI
+// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR);
+ const QColor &col = QColor(0x9d, 0xcb, 0x51);
glColor3f(col.redF(), col.greenF(), col.blueF());
#ifdef ENABLE_OPENCSG
if (shaderinfo) {
@@ -193,82 +190,82 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m
glBegin(GL_TRIANGLES);
for (double z = -zbase/2; z < zbase; z += zbase)
{
- for (int i = 0; i < polygons.size(); i++) {
+ for (size_t i = 0; i < polygons.size(); i++) {
const Polygon *poly = &polygons[i];
if (poly->size() == 3) {
if (z < 0) {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(2), &poly->at(1), true, true, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(2), poly->at(1), true, true, true, z, mirrored);
} else {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(2), true, true, true, z, mirrored);
}
}
else if (poly->size() == 4) {
if (z < 0) {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(3), &poly->at(1), true, false, true, z, mirrored);
- gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(1), &poly->at(3), true, false, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(3), poly->at(1), true, false, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(2), poly->at(1), poly->at(3), true, false, true, z, mirrored);
} else {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, z, mirrored);
- gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(3), true, false, true, z, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(2), poly->at(3), poly->at(1), true, false, true, z, mirrored);
}
}
else {
- Point center;
- for (int j = 0; j < poly->size(); j++) {
- center.x += poly->at(j).x;
- center.y += poly->at(j).y;
+ Vector3d center = Vector3d::Zero();
+ for (size_t j = 0; j < poly->size(); j++) {
+ center[0] += poly->at(j)[0];
+ center[1] += poly->at(j)[1];
}
- center.x /= poly->size();
- center.y /= poly->size();
- for (int j = 1; j <= poly->size(); j++) {
+ center[0] /= poly->size();
+ center[1] /= poly->size();
+ for (size_t j = 1; j <= poly->size(); j++) {
if (z < 0) {
- gl_draw_triangle(shaderinfo, &center, &poly->at(j % poly->size()), &poly->at(j - 1),
+ gl_draw_triangle(shaderinfo, center, poly->at(j % poly->size()), poly->at(j - 1),
false, true, false, z, mirrored);
} else {
- gl_draw_triangle(shaderinfo, &center, &poly->at(j - 1), &poly->at(j % poly->size()),
+ gl_draw_triangle(shaderinfo, center, poly->at(j - 1), poly->at(j % poly->size()),
false, true, false, z, mirrored);
}
}
}
}
}
- const QVector<Polygon> *borders_p = &borders;
+ const std::vector<Polygon> *borders_p = &borders;
if (borders_p->size() == 0)
borders_p = &polygons;
- for (int i = 0; i < borders_p->size(); i++) {
+ for (size_t i = 0; i < borders_p->size(); i++) {
const Polygon *poly = &borders_p->at(i);
- for (int j = 1; j <= poly->size(); j++) {
- Point p1 = poly->at(j - 1), p2 = poly->at(j - 1);
- Point p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size());
- p1.z -= zbase/2, p2.z += zbase/2;
- p3.z -= zbase/2, p4.z += zbase/2;
- gl_draw_triangle(shaderinfo, &p2, &p1, &p3, true, true, false, 0, mirrored);
- gl_draw_triangle(shaderinfo, &p2, &p3, &p4, false, true, true, 0, mirrored);
+ for (size_t j = 1; j <= poly->size(); j++) {
+ Vector3d p1 = poly->at(j - 1), p2 = poly->at(j - 1);
+ Vector3d p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size());
+ p1[2] -= zbase/2, p2[2] += zbase/2;
+ p3[2] -= zbase/2, p4[2] += zbase/2;
+ gl_draw_triangle(shaderinfo, p2, p1, p3, true, true, false, 0, mirrored);
+ gl_draw_triangle(shaderinfo, p2, p3, p4, false, true, true, 0, mirrored);
}
}
glEnd();
} else {
- for (int i = 0; i < polygons.size(); i++) {
+ for (size_t i = 0; i < polygons.size(); i++) {
const Polygon *poly = &polygons[i];
glBegin(GL_TRIANGLES);
if (poly->size() == 3) {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, 0, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(2), true, true, true, 0, mirrored);
}
else if (poly->size() == 4) {
- gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, 0, mirrored);
- gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, 0, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(3), true, false, true, 0, mirrored);
+ gl_draw_triangle(shaderinfo, poly->at(2), poly->at(3), poly->at(1), true, false, true, 0, mirrored);
}
else {
- Point center;
- for (int j = 0; j < poly->size(); j++) {
- center.x += poly->at(j).x;
- center.y += poly->at(j).y;
- center.z += poly->at(j).z;
+ Vector3d center = Vector3d::Zero();
+ for (size_t j = 0; j < poly->size(); j++) {
+ center[0] += poly->at(j)[0];
+ center[1] += poly->at(j)[1];
+ center[2] += poly->at(j)[2];
}
- center.x /= poly->size();
- center.y /= poly->size();
- center.z /= poly->size();
- for (int j = 1; j <= poly->size(); j++) {
- gl_draw_triangle(shaderinfo, &center, &poly->at(j - 1), &poly->at(j % poly->size()), false, true, false, 0, mirrored);
+ center[0] /= poly->size();
+ center[1] /= poly->size();
+ center[2] /= poly->size();
+ for (size_t j = 1; j <= poly->size(); j++) {
+ gl_draw_triangle(shaderinfo, center, poly->at(j - 1), poly->at(j % poly->size()), false, true, false, 0, mirrored);
}
}
glEnd();
@@ -290,400 +287,48 @@ void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const
double zbase = csgmode;
for (double z = -zbase/2; z < zbase; z += zbase)
{
- for (int i = 0; i < borders.size(); i++) {
+ for (size_t i = 0; i < borders.size(); i++) {
const Polygon *poly = &borders[i];
glBegin(GL_LINE_LOOP);
- for (int j = 0; j < poly->size(); j++) {
- const Point *p = &poly->at(j);
- glVertex3d(p->x, p->y, z);
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+ glVertex3d(p[0], p[1], z);
}
glEnd();
}
}
- for (int i = 0; i < borders.size(); i++) {
+ for (size_t i = 0; i < borders.size(); i++) {
const Polygon *poly = &borders[i];
glBegin(GL_LINES);
- for (int j = 0; j < poly->size(); j++) {
- const Point *p = &poly->at(j);
- glVertex3d(p->x, p->y, -zbase/2);
- glVertex3d(p->x, p->y, +zbase/2);
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+ glVertex3d(p[0], p[1], -zbase/2);
+ glVertex3d(p[0], p[1], +zbase/2);
}
glEnd();
}
} else {
- for (int i = 0; i < polygons.size(); i++) {
+ for (size_t i = 0; i < polygons.size(); i++) {
const Polygon *poly = &polygons[i];
glBegin(GL_LINE_LOOP);
- for (int j = 0; j < poly->size(); j++) {
- const Point *p = &poly->at(j);
- glVertex3d(p->x, p->y, p->z);
+ for (size_t j = 0; j < poly->size(); j++) {
+ const Vector3d &p = poly->at(j);
+ glVertex3d(p[0], p[1], p[2]);
}
glEnd();
}
}
}
-#ifdef ENABLE_CGAL
-
-#undef GEN_SURFACE_DEBUG
-
-class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS>
-{
-public:
- typedef CGAL_HDS::Vertex::Point Point;
-
- const PolySet *ps;
- CGAL_Build_PolySet(const PolySet *ps) : ps(ps) { }
-
- void operator()(CGAL_HDS& hds)
- {
- CGAL_Polybuilder B(hds, true);
-
- QList<PolySet::Point> vertices;
- Grid3d<int> vertices_idx(GRID_FINE);
-
- for (int i = 0; i < ps->polygons.size(); i++) {
- const PolySet::Polygon *poly = &ps->polygons[i];
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
- if (!vertices_idx.has(p->x, p->y, p->z)) {
- vertices_idx.data(p->x, p->y, p->z) = vertices.size();
- vertices.append(*p);
- }
- }
- }
-
- B.begin_surface(vertices.size(), ps->polygons.size());
-#ifdef GEN_SURFACE_DEBUG
- printf("=== CGAL Surface ===\n");
-#endif
-
- for (int i = 0; i < vertices.size(); i++) {
- const PolySet::Point *p = &vertices[i];
- B.add_vertex(Point(p->x, p->y, p->z));
-#ifdef GEN_SURFACE_DEBUG
- printf("%d: %f %f %f\n", i, p->x, p->y, p->z);
-#endif
- }
-
- for (int i = 0; i < ps->polygons.size(); i++) {
- const PolySet::Polygon *poly = &ps->polygons[i];
- QHash<int,int> fc;
- bool facet_is_degenerated = false;
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
- int v = vertices_idx.data(p->x, p->y, p->z);
- if (fc[v]++ > 0)
- facet_is_degenerated = true;
- }
-
- if (!facet_is_degenerated)
- B.begin_facet();
-#ifdef GEN_SURFACE_DEBUG
- printf("F:");
-#endif
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
-#ifdef GEN_SURFACE_DEBUG
- printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z);
-#endif
- if (!facet_is_degenerated)
- B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z));
- }
-#ifdef GEN_SURFACE_DEBUG
- if (facet_is_degenerated)
- printf(" (degenerated)");
- printf("\n");
-#endif
- if (!facet_is_degenerated)
- B.end_facet();
- }
-
-#ifdef GEN_SURFACE_DEBUG
- printf("====================\n");
-#endif
- B.end_surface();
-
- #undef PointKey
- }
-};
-
-CGAL_Nef_polyhedron PolySet::render_cgal_nef_polyhedron() const
+BoundingBox PolySet::getBoundingBox() const
{
- if (this->is2d)
- {
-#if 0
- // This version of the code causes problems in some cases.
- // Example testcase: import_dxf("testdata/polygon8.dxf");
- //
- typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
- typedef point_list_t::iterator point_list_it;
- std::list< point_list_t > pdata_point_lists;
- std::list < std::pair < point_list_it, point_list_it > > pdata;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->polygons.size(); i++) {
- pdata_point_lists.push_back(point_list_t());
- for (int j = 0; j < this->polygons[i].size(); j++) {
- double x = this->polygons[i][j].x;
- double y = this->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- pdata_point_lists.back().push_back(p);
- }
- pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
- pdata_point_lists.back().end()));
- }
-
- CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
- return CGAL_Nef_polyhedron(N);
-#endif
-#if 0
- // This version of the code works fine but is pretty slow.
- //
- CGAL_Nef_polyhedron2 N;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->polygons.size(); i++) {
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < this->polygons[i].size(); j++) {
- double x = this->polygons[i][j].x;
- double y = this->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- plist.push_back(p);
- }
- N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
- }
-
- return CGAL_Nef_polyhedron(N);
-#endif
-#if 1
- // This version of the code does essentially the same thing as the 2nd
- // version but merges some triangles before sending them to CGAL. This adds
- // complexity but speeds up things..
- //
- struct PolyReducer
- {
- Grid2d<int> grid;
- QHash< QPair<int,int>, QPair<int,int> > egde_to_poly;
- QHash< int, CGAL_Nef_polyhedron2::Point > points;
- QHash< int, QList<int> > polygons;
- int poly_n;
-
- void add_edges(int pn)
- {
- for (int j = 1; j <= this->polygons[pn].size(); j++) {
- int a = this->polygons[pn][j-1];
- int b = this->polygons[pn][j % this->polygons[pn].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0)
- this->egde_to_poly[QPair<int,int>(a, b)].first = pn;
- else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0)
- this->egde_to_poly[QPair<int,int>(a, b)].second = pn;
- else
- abort();
- }
- }
-
- void del_poly(int pn)
- {
- for (int j = 1; j <= this->polygons[pn].size(); j++) {
- int a = this->polygons[pn][j-1];
- int b = this->polygons[pn][j % this->polygons[pn].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn)
- this->egde_to_poly[QPair<int,int>(a, b)].first = 0;
- if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn)
- this->egde_to_poly[QPair<int,int>(a, b)].second = 0;
- }
- this->polygons.remove(pn);
- }
-
- PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1)
- {
- int point_n = 1;
- for (int i = 0; i < ps->polygons.size(); i++) {
- for (int j = 0; j < ps->polygons[i].size(); j++) {
- double x = ps->polygons[i][j].x;
- double y = ps->polygons[i][j].y;
- if (this->grid.has(x, y)) {
- int idx = this->grid.data(x, y);
- // Filter away two vertices with the same index (due to grid)
- // This could be done in a more general way, but we'd rather redo the entire
- // grid concept instead.
- if (this->polygons[this->poly_n].indexOf(idx) == -1) {
- this->polygons[this->poly_n].append(this->grid.data(x, y));
- }
- } else {
- this->grid.align(x, y) = point_n;
- this->polygons[this->poly_n].append(point_n);
- this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
- point_n++;
- }
- }
- if (this->polygons[this->poly_n].size() >= 3) {
- add_edges(this->poly_n);
- this->poly_n++;
- }
- else {
- this->polygons.remove(this->poly_n);
- }
- }
- }
-
- int merge(int p1, int p1e, int p2, int p2e)
- {
- for (int i = 1; i < this->polygons[p1].size(); i++) {
- int j = (p1e + i) % this->polygons[p1].size();
- this->polygons[this->poly_n].append(this->polygons[p1][j]);
- }
- for (int i = 1; i < this->polygons[p2].size(); i++) {
- int j = (p2e + i) % this->polygons[p2].size();
- this->polygons[this->poly_n].append(this->polygons[p2][j]);
- }
- del_poly(p1);
- del_poly(p2);
- add_edges(this->poly_n);
- return this->poly_n++;
- }
-
- void reduce()
- {
- QList<int> work_queue;
- QHashIterator< int, QList<int> > it(polygons);
- while (it.hasNext()) {
- it.next();
- work_queue.append(it.key());
- }
- while (!work_queue.isEmpty()) {
- int poly1_n = work_queue.first();
- work_queue.removeFirst();
- if (!this->polygons.contains(poly1_n))
- continue;
- for (int j = 1; j <= this->polygons[poly1_n].size(); j++) {
- int a = this->polygons[poly1_n][j-1];
- int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 &&
- this->egde_to_poly[QPair<int,int>(a, b)].second != 0) {
- int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first +
- this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n;
- int poly2_edge = -1;
- for (int k = 1; k <= this->polygons[poly2_n].size(); k++) {
- int c = this->polygons[poly2_n][k-1];
- int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
- if (c > d) { c = c^d; d = c^d; c = c^d; }
- if (a == c && b == d) {
- poly2_edge = k-1;
- continue;
- }
- int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first +
- this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n;
- if (poly3_n < 0)
- continue;
- if (poly3_n == poly1_n)
- goto next_poly1_edge;
- }
- work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge));
- goto next_poly1;
- }
- next_poly1_edge:;
- }
- next_poly1:;
- }
- }
-
- CGAL_Nef_polyhedron2 toNef()
- {
- CGAL_Nef_polyhedron2 N;
-
- QHashIterator< int, QList<int> > it(polygons);
- while (it.hasNext()) {
- it.next();
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < it.value().size(); j++) {
- int p = it.value()[j];
- plist.push_back(points[p]);
- }
- N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
- }
-
- return N;
- }
- };
-
- PolyReducer pr(this);
- // printf("Number of polygons before reduction: %d\n", pr.polygons.size());
- pr.reduce();
- // printf("Number of polygons after reduction: %d\n", pr.polygons.size());
- return CGAL_Nef_polyhedron(pr.toNef());
-#endif
-#if 0
- // This is another experimental version. I should run faster than the above,
- // is a lot simpler and has only one known weakness: Degenerate polygons, which
- // get repaired by GLUTess, might trigger a CGAL crash here. The only
- // known case for this is triangle-with-duplicate-vertex.dxf
- // FIXME: If we just did a projection, we need to recreate the border!
- if (this->polygons.size() > 0) assert(this->borders.size() > 0);
- CGAL_Nef_polyhedron2 N;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->borders.size(); i++) {
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < this->borders[i].size(); j++) {
- double x = this->borders[i][j].x;
- double y = this->borders[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- plist.push_back(p);
- }
- // FIXME: If a border (path) has a duplicate vertex in dxf,
- // the CGAL_Nef_polyhedron2 constructor will crash.
- N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ BoundingBox bbox;
+ for (size_t i = 0; i < polygons.size(); i++) {
+ const Polygon &poly = polygons[i];
+ for (size_t j = 0; j < poly.size(); j++) {
+ const Vector3d &p = poly[j];
+ bbox.extend(p);
}
-
- return CGAL_Nef_polyhedron(N);
-
-#endif
}
- else // not (this->is2d)
- {
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- try {
- CGAL_Polyhedron P;
- CGAL_Build_PolySet builder(this);
- P.delegate(builder);
-#if 0
- std::cout << P;
-#endif
- CGAL_Nef_polyhedron3 N(P);
- return CGAL_Nef_polyhedron(N);
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("CGAL error: %s", e.what());
- CGAL::set_error_behaviour(old_behaviour);
- return CGAL_Nef_polyhedron();
- }
- CGAL::set_error_behaviour(old_behaviour);
- }
- return CGAL_Nef_polyhedron();
+ return bbox;
}
-
-#endif /* ENABLE_CGAL */
-
diff --git a/src/polyset.h b/src/polyset.h
index 8712ff2..e80d182 100644
--- a/src/polyset.h
+++ b/src/polyset.h
@@ -1,30 +1,21 @@
#ifndef POLYSET_H_
#define POLYSET_H_
-#include <GL/glew.h> // this must be included before the GL headers
-#include <qgl.h>
-
+#include <GL/glew.h>
#include "grid.h"
-#ifdef ENABLE_OPENCSG
-# include <opencsg.h>
-#endif
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
+#include <vector>
+#include <Eigen/Core>
+#include <Eigen/Geometry>
-#include <QCache>
+using Eigen::Vector3d;
+typedef Eigen::AlignedBox<double, 3> BoundingBox;
class PolySet
{
public:
- struct Point {
- double x, y, z;
- Point() : x(0), y(0), z(0) { }
- Point(double x, double y, double z) : x(x), y(y), z(z) { }
- };
- typedef QList<Point> Polygon;
- QVector<Polygon> polygons;
- QVector<Polygon> borders;
+ typedef std::vector<Vector3d> Polygon;
+ std::vector<Polygon> polygons;
+ std::vector<Polygon> borders;
Grid3d<void*> grid;
bool is2d;
@@ -33,16 +24,12 @@ public:
PolySet();
~PolySet();
+ bool empty() const { return polygons.size() == 0; }
void append_poly();
- void append_vertex(double x, double y, double z);
- void insert_vertex(double x, double y, double z);
+ void append_vertex(double x, double y, double z = 0.0);
+ void insert_vertex(double x, double y, double z = 0.0);
- void append_vertex(double x, double y) {
- append_vertex(x, y, 0.0);
- }
- void insert_vertex(double x, double y) {
- insert_vertex(x, y, 0.0);
- }
+ BoundingBox getBoundingBox() const;
enum colormode_e {
COLORMODE_NONE,
@@ -62,22 +49,9 @@ public:
CSGMODE_HIGHLIGHT_DIFFERENCE = 22
};
- struct ps_cache_entry {
- PolySet *ps;
- QString msg;
- ps_cache_entry(PolySet *ps);
- ~ps_cache_entry();
- };
-
- static QCache<QString,ps_cache_entry> ps_cache;
-
void render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo = NULL) const;
void render_edges(colormode_e colormode, csgmode_e csgmode) const;
-#ifdef ENABLE_CGAL
- CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
-
int refcount;
PolySet *link();
void unlink();
diff --git a/src/primitives.cc b/src/primitives.cc
index 45b2da3..50a197d 100644
--- a/src/primitives.cc
+++ b/src/primitives.cc
@@ -33,6 +33,11 @@
#include "builtin.h"
#include "printutils.h"
#include <assert.h>
+#include "visitor.h"
+#include <sstream>
+#include <assert.h>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
#define F_MINIMUM 0.01
@@ -57,47 +62,83 @@ public:
class PrimitiveNode : public AbstractPolyNode
{
public:
+ PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const {
+ switch (this->type) {
+ case CUBE:
+ return "cube";
+ break;
+ case SPHERE:
+ return "sphere";
+ break;
+ case CYLINDER:
+ return "cylinder";
+ break;
+ case POLYHEDRON:
+ return "polyhedron";
+ break;
+ case SQUARE:
+ return "square";
+ break;
+ case CIRCLE:
+ return "circle";
+ break;
+ case POLYGON:
+ return "polygon";
+ break;
+ default:
+ assert(false && "PrimitiveNode::name(): Unknown primitive type");
+ return AbstractPolyNode::name();
+ }
+ }
+
bool center;
double x, y, z, h, r1, r2;
double fn, fs, fa;
primitive_type_e type;
int convexity;
Value points, paths, triangles;
- PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;
};
AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
- PrimitiveNode *node = new PrimitiveNode(inst, type);
+ PrimitiveNode *node = new PrimitiveNode(inst, this->type);
node->center = false;
node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1;
- QVector<QString> argnames;
- QVector<Expression*> argexpr;
-
- if (type == CUBE) {
- argnames = QVector<QString>() << "size" << "center";
- }
- if (type == SPHERE) {
- argnames = QVector<QString>() << "r";
- }
- if (type == CYLINDER) {
- argnames = QVector<QString>() << "h" << "r1" << "r2" << "center";
- }
- if (type == POLYHEDRON) {
- argnames = QVector<QString>() << "points" << "triangles" << "convexity";
- }
- if (type == SQUARE) {
- argnames = QVector<QString>() << "size" << "center";
- }
- if (type == CIRCLE) {
- argnames = QVector<QString>() << "r";
- }
- if (type == POLYGON) {
- argnames = QVector<QString>() << "points" << "paths" << "convexity";
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
+
+ switch (this->type) {
+ case CUBE:
+ argnames += "size", "center";
+ break;
+ case SPHERE:
+ argnames += "r";
+ break;
+ case CYLINDER:
+ argnames += "h", "r1", "r2", "center";
+ break;
+ case POLYHEDRON:
+ argnames += "points", "triangles", "convexity";
+ break;
+ case SQUARE:
+ argnames += "size", "center";
+ break;
+ case CIRCLE:
+ argnames += "r";
+ break;
+ case POLYGON:
+ argnames += "points", "paths", "convexity";
+ break;
+ default:
+ assert(false && "PrimitiveModule::evaluate(): Unknown node type");
}
Context c(ctx);
@@ -141,8 +182,7 @@ AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstanti
Value r, r1, r2;
r1 = c.lookup_variable("r1");
r2 = c.lookup_variable("r2");
- if (r1.type != Value::NUMBER && r2.type != Value::NUMBER)
- r = c.lookup_variable("r", true); // silence warning since r has no default value
+ r = c.lookup_variable("r", true); // silence warning since r has no default value
Value center = c.lookup_variable("center");
if (h.type == Value::NUMBER) {
node->h = h.num;
@@ -233,25 +273,25 @@ static void generate_circle(point2d *circle, double r, int fragments)
}
}
-PolySet *PrimitiveNode::render_polyset(render_mode_e) const
+PolySet *PrimitiveNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const
{
PolySet *p = new PolySet();
- if (type == CUBE && x > 0 && y > 0 && z > 0)
+ if (this->type == CUBE && this->x > 0 && this->y > 0 && this->z > 0)
{
double x1, x2, y1, y2, z1, z2;
- if (center) {
- x1 = -x/2;
- x2 = +x/2;
- y1 = -y/2;
- y2 = +y/2;
- z1 = -z/2;
- z2 = +z/2;
+ if (this->center) {
+ x1 = -this->x/2;
+ x2 = +this->x/2;
+ y1 = -this->y/2;
+ y2 = +this->y/2;
+ z1 = -this->z/2;
+ z2 = +this->z/2;
} else {
x1 = y1 = z1 = 0;
- x2 = x;
- y2 = y;
- z2 = z;
+ x2 = this->x;
+ y2 = this->y;
+ z2 = this->z;
}
p->append_poly(); // top
@@ -291,7 +331,7 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const
p->append_vertex(x1, y2, z2);
}
- if (type == SPHERE && r1 > 0)
+ if (this->type == SPHERE && this->r1 > 0)
{
struct ring_s {
point2d *points;
@@ -358,17 +398,18 @@ sphere_next_r2:
delete[] ring;
}
- if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0))
+ if (this->type == CYLINDER &&
+ this->h > 0 && this->r1 >=0 && this->r2 >= 0 && (this->r1 > 0 || this->r2 > 0))
{
- int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa);
+ int fragments = get_fragments_from_r(fmax(this->r1, this->r2), this->fn, this->fs, this->fa);
double z1, z2;
- if (center) {
- z1 = -h/2;
- z2 = +h/2;
+ if (this->center) {
+ z1 = -this->h/2;
+ z2 = +this->h/2;
} else {
z1 = 0;
- z2 = h;
+ z2 = this->h;
}
point2d *circle1 = new point2d[fragments];
@@ -401,13 +442,13 @@ sphere_next_r2:
}
}
- if (r1 > 0) {
+ if (this->r1 > 0) {
p->append_poly();
for (int i=0; i<fragments; i++)
p->insert_vertex(circle1[i].x, circle1[i].y, z1);
}
- if (r2 > 0) {
+ if (this->r2 > 0) {
p->append_poly();
for (int i=0; i<fragments; i++)
p->append_vertex(circle2[i].x, circle2[i].y, z2);
@@ -417,35 +458,35 @@ sphere_next_r2:
delete[] circle2;
}
- if (type == POLYHEDRON)
+ if (this->type == POLYHEDRON)
{
- p->convexity = convexity;
- for (int i=0; i<triangles.vec.size(); i++)
+ p->convexity = this->convexity;
+ for (size_t i=0; i<this->triangles.vec.size(); i++)
{
p->append_poly();
- for (int j=0; j<triangles.vec[i]->vec.size(); j++) {
- int pt = triangles.vec[i]->vec[j]->num;
- if (pt < points.vec.size()) {
+ for (size_t j=0; j<this->triangles.vec[i]->vec.size(); j++) {
+ size_t pt = this->triangles.vec[i]->vec[j]->num;
+ if (pt < this->points.vec.size()) {
double px, py, pz;
- if (points.vec[pt]->getv3(px, py, pz))
+ if (this->points.vec[pt]->getv3(px, py, pz))
p->insert_vertex(px, py, pz);
}
}
}
}
- if (type == SQUARE && x > 0 && y > 0)
+ if (this->type == SQUARE && x > 0 && y > 0)
{
double x1, x2, y1, y2;
- if (center) {
- x1 = -x/2;
- x2 = +x/2;
- y1 = -y/2;
- y2 = +y/2;
+ if (this->center) {
+ x1 = -this->x/2;
+ x2 = +this->x/2;
+ y1 = -this->y/2;
+ y2 = +this->y/2;
} else {
x1 = y1 = 0;
- x2 = x;
- y2 = y;
+ x2 = this->x;
+ y2 = this->y;
}
p->is2d = true;
@@ -456,96 +497,113 @@ sphere_next_r2:
p->append_vertex(x1, y2);
}
- if (type == CIRCLE)
+ if (this->type == CIRCLE)
{
- int fragments = get_fragments_from_r(r1, fn, fs, fa);
+ int fragments = get_fragments_from_r(this->r1, this->fn, this->fs, this->fa);
p->is2d = true;
p->append_poly();
for (int i=0; i < fragments; i++) {
double phi = (M_PI*2*i) / fragments;
- p->append_vertex(r1*cos(phi), r1*sin(phi));
+ p->append_vertex(this->r1*cos(phi), this->r1*sin(phi));
}
}
- if (type == POLYGON)
+ if (this->type == POLYGON)
{
DxfData dd;
- for (int i=0; i<points.vec.size(); i++) {
+ for (size_t i=0; i<this->points.vec.size(); i++) {
double x,y;
- if (!points.vec[i]->getv2(x, y)) {
+ if (!this->points.vec[i]->getv2(x, y)) {
PRINTF("ERROR: Unable to convert point at index %d to a vec2 of numbers", i);
- // FIXME: Return NULL and make sure this is checked by all callers?
- return p;
+ p->unlink();
+ return NULL;
}
- dd.points.append(DxfData::Point(x, y));
+ dd.points.push_back(Vector2d(x, y));
}
- if (paths.vec.size() == 0)
+ if (this->paths.vec.size() == 0)
{
- dd.paths.append(DxfData::Path());
- for (int i=0; i<points.vec.size(); i++) {
+ dd.paths.push_back(DxfData::Path());
+ for (size_t i=0; i<this->points.vec.size(); i++) {
assert(i < dd.points.size()); // FIXME: Not needed, but this used to be an 'if'
- DxfData::Point *p = &dd.points[i];
- dd.paths.last().points.append(p);
+ dd.paths.back().indices.push_back(i);
}
- if (dd.paths.last().points.size() > 0) {
- dd.paths.last().points.append(dd.paths.last().points.first());
- dd.paths.last().is_closed = true;
+ if (dd.paths.back().indices.size() > 0) {
+ dd.paths.back().indices.push_back(dd.paths.back().indices.front());
+ dd.paths.back().is_closed = true;
}
}
else
{
- for (int i=0; i<paths.vec.size(); i++)
+ for (size_t i=0; i<this->paths.vec.size(); i++)
{
- dd.paths.append(DxfData::Path());
- for (int j=0; j<paths.vec[i]->vec.size(); j++) {
- int idx = paths.vec[i]->vec[j]->num;
+ 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;
if (idx < dd.points.size()) {
- DxfData::Point *p = &dd.points[idx];
- dd.paths.last().points.append(p);
+ dd.paths.back().indices.push_back(idx);
}
}
- if (dd.paths.last().points.isEmpty()) {
- dd.paths.removeLast();
+ if (dd.paths.back().indices.empty()) {
+ dd.paths.pop_back();
} else {
- dd.paths.last().points.append(dd.paths.last().points.first());
- dd.paths.last().is_closed = true;
+ dd.paths.back().indices.push_back(dd.paths.back().indices.front());
+ dd.paths.back().is_closed = true;
}
}
}
p->is2d = true;
p->convexity = convexity;
- dxf_tesselate(p, &dd, 0, true, false, 0);
- dxf_border_to_ps(p, &dd);
+ dxf_tesselate(p, dd, 0, true, false, 0);
+ dxf_border_to_ps(p, dd);
}
return p;
}
-QString PrimitiveNode::dump(QString indent) const
+std::string PrimitiveNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- if (type == CUBE)
- text.sprintf("cube(size = [%g, %g, %g], center = %s);\n", x, y, z, center ? "true" : "false");
- if (type == SPHERE)
- text.sprintf("sphere($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1);
- if (type == CYLINDER)
- text.sprintf("cylinder($fn = %g, $fa = %g, $fs = %g, h = %g, r1 = %g, r2 = %g, center = %s);\n", fn, fa, fs, h, r1, r2, center ? "true" : "false");
- if (type == POLYHEDRON)
- text.sprintf("polyhedron(points = %s, triangles = %s, convexity = %d);\n", points.dump().toAscii().data(), triangles.dump().toAscii().data(), convexity);
- if (type == SQUARE)
- text.sprintf("square(size = [%g, %g], center = %s);\n", x, y, center ? "true" : "false");
- if (type == CIRCLE)
- text.sprintf("circle($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1);
- if (type == POLYGON)
- text.sprintf("polygon(points = %s, paths = %s, convexity = %d);\n", points.dump().toAscii().data(), paths.dump().toAscii().data(), convexity);
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
+ std::stringstream stream;
+
+ stream << this->name();
+
+ switch (this->type) {
+ case CUBE:
+ stream << "(size = [" << this->x << ", " << this->y << ", " << this->z << "], "
+ << "center = " << (center ? "true" : "false") << ")";
+ break;
+ case SPHERE:
+ stream << "($fn = " << this->fn << ", $fa = " << this->fa
+ << ", $fs = " << this->fs << ", r = " << this->r1 << ")";
+ break;
+ case CYLINDER:
+ stream << "($fn = " << this->fn << ", $fa = " << this->fa
+ << ", $fs = " << this->fs << ", h = " << this->h << ", r1 = " << this->r1
+ << ", r2 = " << this->r2 << ", center = " << (center ? "true" : "false") << ")";
+ break;
+ case POLYHEDRON:
+ stream << "(points = " << this->points
+ << ", triangles = " << this->triangles
+ << ", convexity = " << this->convexity << ")";
+ break;
+ case SQUARE:
+ stream << "(size = [" << this->x << ", " << this->y << "], "
+ << "center = " << (center ? "true" : "false") << ")";
+ break;
+ case CIRCLE:
+ stream << "($fn = " << this->fn << ", $fa = " << this->fa
+ << ", $fs = " << this->fs << ", r = " << this->r1 << ")";
+ break;
+ case POLYGON:
+ stream << "(points = " << this->points << ", paths = " << this->paths << ", convexity = " << this->convexity << ")";
+ break;
+ default:
+ assert(false);
}
- return dump_cache;
-}
+ return stream.str();
+}
diff --git a/src/printutils.cc b/src/printutils.cc
index 8830a8c..01fa06b 100644
--- a/src/printutils.cc
+++ b/src/printutils.cc
@@ -18,8 +18,7 @@ void print_messages_push()
void print_messages_pop()
{
- QString msg = print_messages_stack.last();
- print_messages_stack.removeLast();
+ QString msg = print_messages_stack.takeLast();
if (print_messages_stack.size() > 0 && !msg.isNull()) {
if (!print_messages_stack.last().isEmpty())
print_messages_stack.last() += "\n";
@@ -49,3 +48,8 @@ void PRINT_NOCACHE(const QString &msg)
outputhandler(msg, outputhandler_data);
}
}
+
+std::ostream &operator<<(std::ostream &os, const QFileInfo &fi) {
+ os << std::hex << (fi.exists()?fi.lastModified().toTime_t():0) << "." << fi.size();
+ return os;
+}
diff --git a/src/printutils.h b/src/printutils.h
index 7f2e828..0432622 100644
--- a/src/printutils.h
+++ b/src/printutils.h
@@ -3,6 +3,9 @@
#include <QString>
#include <QList>
+#include <iostream>
+#include <QFileInfo>
+#include <QDateTime>
typedef void (OutputHandlerFunc)(const QString &msg, void *userdata);
extern OutputHandlerFunc *outputhandler;
@@ -22,4 +25,6 @@ void PRINT_NOCACHE(const QString &msg);
#define PRINTF_NOCACHE(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT_NOCACHE(_m); } while (0)
#define PRINTA_NOCACHE(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT_NOCACHE(_m); } while (0)
+std::ostream &operator<<(std::ostream &os, const QFileInfo &fi);
+
#endif
diff --git a/src/projection.cc b/src/projection.cc
index d87b366..5a7ea6e 100644
--- a/src/projection.cc
+++ b/src/projection.cc
@@ -24,8 +24,8 @@
*
*/
+#include "projectionnode.h"
#include "module.h"
-#include "node.h"
#include "context.h"
#include "printutils.h"
#include "builtin.h"
@@ -34,6 +34,8 @@
#include "polyset.h"
#include "export.h"
#include "progress.h"
+#include "visitor.h"
+#include "PolySetEvaluator.h"
#ifdef ENABLE_CGAL
# include <CGAL/assertions_behaviour.h>
@@ -41,10 +43,9 @@
#endif
#include <assert.h>
-
-#include <QApplication>
-#include <QTime>
-#include <QProgressDialog>
+#include <sstream>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
class ProjectionModule : public AbstractModule
{
@@ -53,24 +54,13 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class ProjectionNode : public AbstractPolyNode
-{
-public:
- int convexity;
- bool cut_mode;
- ProjectionNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
- cut_mode = false;
- }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
ProjectionNode *node = new ProjectionNode(inst);
- QVector<QString> argnames = QVector<QString>() << "cut";
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "cut";
+ std::vector<Expression*> argexpr;
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
@@ -83,216 +73,41 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant
if (cut.type == Value::BOOL)
node->cut_mode = cut.b;
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
-void register_builtin_projection()
+PolySet *ProjectionNode::evaluate_polyset(render_mode_e mode, PolySetEvaluator *evaluator) const
{
- builtin_modules["projection"] = new ProjectionModule();
-}
-
-#ifdef ENABLE_CGAL
-
-PolySet *ProjectionNode::render_polyset(render_mode_e) const
-{
- QString key = mk_cache_id();
- if (PolySet::ps_cache.contains(key)) {
- PRINT(PolySet::ps_cache[key]->msg);
- return PolySet::ps_cache[key]->ps->link();
- }
-
- print_messages_push();
-
- PolySet *ps = new PolySet();
- ps->convexity = this->convexity;
- ps->is2d = true;
-
- CGAL_Nef_polyhedron N;
- N.dim = 3;
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- try {
- foreach(AbstractNode *v, this->children) {
- if (v->modinst->tag_background)
- continue;
- N.p3 += v->render_cgal_nef_polyhedron().p3;
- }
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("CGAL error: %s", e.what());
- CGAL::set_error_behaviour(old_behaviour);
+ if (!evaluator) {
+ PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str());
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
return ps;
}
- CGAL::set_error_behaviour(old_behaviour);
-
- if (cut_mode)
- {
- PolySet *cube = new PolySet();
- double infval = 1e8, eps = 0.1;
- double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps;
-
- cube->append_poly(); // top
- cube->append_vertex(x1, y1, z2);
- cube->append_vertex(x2, y1, z2);
- cube->append_vertex(x2, y2, z2);
- cube->append_vertex(x1, y2, z2);
-
- cube->append_poly(); // bottom
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x1, y1, z1);
-
- cube->append_poly(); // side1
- cube->append_vertex(x1, y1, z1);
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x2, y1, z2);
- cube->append_vertex(x1, y1, z2);
-
- cube->append_poly(); // side2
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x2, y2, z2);
- cube->append_vertex(x2, y1, z2);
-
- cube->append_poly(); // side3
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x1, y2, z2);
- cube->append_vertex(x2, y2, z2);
-
- cube->append_poly(); // side4
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x1, y1, z1);
- cube->append_vertex(x1, y1, z2);
- cube->append_vertex(x1, y2, z2);
- CGAL_Nef_polyhedron Ncube = cube->render_cgal_nef_polyhedron();
- cube->unlink();
-
- // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED);
- N.p3 *= Ncube.p3;
- if (!N.p3.is_simple()) {
- PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
- PolySet *ps3 = new PolySet();
- cgal_nef3_to_polyset(ps3, &N);
- Grid2d<int> conversion_grid(GRID_COARSE);
- for (int i = 0; i < ps3->polygons.size(); i++) {
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- double z = ps3->polygons[i][j].z;
- if (z != 0)
- goto next_ps3_polygon_cut_mode;
- if (conversion_grid.align(x, y) == i+1)
- goto next_ps3_polygon_cut_mode;
- conversion_grid.data(x, y) = i+1;
- }
- ps->append_poly();
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- conversion_grid.align(x, y);
- ps->insert_vertex(x, y);
- }
- next_ps3_polygon_cut_mode:;
- }
- ps3->unlink();
- }
- else
- {
- if (!N.p3.is_simple()) {
- PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
- PolySet *ps3 = new PolySet();
- cgal_nef3_to_polyset(ps3, &N);
- CGAL_Nef_polyhedron np;
- np.dim = 2;
- for (int i = 0; i < ps3->polygons.size(); i++)
- {
- int min_x_p = -1;
- double min_x_val = 0;
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- if (min_x_p < 0 || x < min_x_val) {
- min_x_p = j;
- min_x_val = x;
- }
- }
- int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size();
- int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size();
- double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x;
- double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y;
- double at = atan2(ay, ax);
- double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x;
- double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y;
- double bt = atan2(by, bx);
+ print_messages_push();
- double eps = 0.000001;
- if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) ||
- (fabs(bx) < eps && fabs(by) < eps)) {
- // this triangle is degenerated in projection
- continue;
- }
+ PolySet *ps = evaluator->evaluatePolySet(*this, mode);
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y);
- if (at > bt)
- plist.push_front(p);
- else
- plist.push_back(p);
- }
- np.p2 += CGAL_Nef_polyhedron2(plist.begin(), plist.end(),
- CGAL_Nef_polyhedron2::INCLUDED);
- }
- DxfData dxf(np);
- dxf_tesselate(ps, &dxf, 0, true, false, 0);
- dxf_border_to_ps(ps, &dxf);
- ps3->unlink();
- }
-
-cant_project_non_simple_polyhedron:
- PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
print_messages_pop();
return ps;
}
-#else // ENABLE_CGAL
-
-PolySet *ProjectionNode::render_polyset(render_mode_e) const
+std::string ProjectionNode::toString() const
{
- PRINT("WARNING: Found projection() statement but compiled without CGAL support!");
- PolySet *ps = new PolySet();
- ps->is2d = true;
- return ps;
-}
+ std::stringstream stream;
-#endif // ENABLE_CGAL
+ stream << "projection(cut = " << (this->cut_mode ? "true" : "false")
+ << ", convexity = " << this->convexity << ")";
-QString ProjectionNode::dump(QString indent) const
-{
- if (dump_cache.isEmpty()) {
- QString text;
- text.sprintf("projection(cut = %s, convexity = %d) {\n",
- this->cut_mode ? "true" : "false", this->convexity);
- foreach (AbstractNode *v, this->children)
- text += v->dump(indent + QString("\t"));
- text += indent + "}\n";
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
- }
- return dump_cache;
+ return stream.str();
}
+void register_builtin_projection()
+{
+ builtin_modules["projection"] = new ProjectionModule();
+}
diff --git a/src/projectionnode.h b/src/projectionnode.h
new file mode 100644
index 0000000..4c29b7b
--- /dev/null
+++ b/src/projectionnode.h
@@ -0,0 +1,25 @@
+#ifndef PROJECTIONNODE_H_
+#define PROJECTIONNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+#include <string>
+
+class ProjectionNode : public AbstractPolyNode
+{
+public:
+ ProjectionNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {
+ cut_mode = false;
+ }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const { return "projection"; }
+
+ int convexity;
+ bool cut_mode;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const;
+};
+
+#endif
diff --git a/src/qhash.cc b/src/qhash.cc
new file mode 100644
index 0000000..cec9adf
--- /dev/null
+++ b/src/qhash.cc
@@ -0,0 +1,19 @@
+#include "myqhash.h"
+
+static uint hash(const uchar *p, int n)
+{
+ uint h = 0;
+ uint g;
+
+ while (n--) {
+ h = (h << 4) + *p++;
+ if ((g = (h & 0xf0000000)) != 0)
+ h ^= g >> 23;
+ h &= ~g;
+ }
+ return h;
+}
+
+uint qHash(const std::string &str) {
+ return hash(reinterpret_cast<const uchar *>(str.c_str()), str.length());
+}
diff --git a/src/render-opencsg.cc.org b/src/render-opencsg.cc.org
new file mode 100644
index 0000000..fe0fc60
--- /dev/null
+++ b/src/render-opencsg.cc.org
@@ -0,0 +1,87 @@
+#include "render-opencsg.h"
+#include "polyset.h"
+#include "csgterm.h"
+#ifdef ENABLE_OPENCSG
+# include <opencsg.h>
+#endif
+
+class OpenCSGPrim : public OpenCSG::Primitive
+{
+public:
+ OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) :
+ OpenCSG::Primitive(operation, convexity) { }
+ PolySet *p;
+ double *m;
+ int csgmode;
+ virtual void render() {
+ glPushMatrix();
+ glMultMatrixd(m);
+ p->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m);
+ glPopMatrix();
+ }
+};
+
+void renderCSGChainviaOpenCSG(CSGChain *chain, GLint *shaderinfo, bool highlight, bool background)
+{
+ std::vector<OpenCSG::Primitive*> primitives;
+ int j = 0;
+ for (int i = 0;; i++)
+ {
+ bool last = i == chain->polysets.size();
+
+ if (last || chain->types[i] == CSGTerm::TYPE_UNION)
+ {
+ if (j+1 != i) {
+ OpenCSG::render(primitives);
+ glDepthFunc(GL_EQUAL);
+ }
+ if (shaderinfo)
+ glUseProgram(shaderinfo[0]);
+ for (; j < i; j++) {
+ double *m = chain->matrices[j];
+ glPushMatrix();
+ glMultMatrixd(m);
+ int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ if (highlight) {
+ chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo);
+ } else if (background) {
+ chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo);
+ } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) {
+ // User-defined color from source
+ glColor4d(m[16], m[17], m[18], m[19]);
+ if (shaderinfo) {
+ glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]);
+ glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0);
+ }
+ chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo);
+ } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) {
+ chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo);
+ } else {
+ chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo);
+ }
+ glPopMatrix();
+ }
+ if (shaderinfo)
+ glUseProgram(0);
+ for (unsigned int k = 0; k < primitives.size(); k++) {
+ delete primitives[k];
+ }
+ glDepthFunc(GL_LEQUAL);
+ primitives.clear();
+ }
+
+ if (last)
+ break;
+
+ OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ?
+ OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity);
+ prim->p = chain->polysets[i];
+ prim->m = chain->matrices[i];
+ prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
+ if (highlight)
+ prim->csgmode += 20;
+ else if (background)
+ prim->csgmode += 10;
+ primitives.push_back(prim);
+ }
+}
diff --git a/src/render.cc b/src/render.cc
index 9fa7ab6..48a8535 100644
--- a/src/render.cc
+++ b/src/render.cc
@@ -24,8 +24,8 @@
*
*/
+#include "rendernode.h"
#include "module.h"
-#include "node.h"
#include "polyset.h"
#include "context.h"
#include "dxfdata.h"
@@ -34,13 +34,11 @@
#include "builtin.h"
#include "printutils.h"
#include "progress.h"
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
+#include "visitor.h"
-#include <QProgressDialog>
-#include <QApplication>
-#include <QTime>
+#include <sstream>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
class RenderModule : public AbstractModule
{
@@ -49,24 +47,13 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class RenderNode : public AbstractNode
-{
-public:
- int convexity;
- RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { }
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
-};
-
AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
RenderNode *node = new RenderNode(inst);
- QVector<QString> argnames = QVector<QString>() << "convexity";
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "convexity";
+ std::vector<Expression*> argexpr;
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
@@ -75,11 +62,8 @@ AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiati
if (v.type == Value::NUMBER)
node->convexity = (int)v.num;
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n != NULL)
- node->children.append(n);
- }
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
@@ -89,176 +73,11 @@ void register_builtin_render()
builtin_modules["render"] = new RenderModule();
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron RenderNode::render_cgal_nef_polyhedron() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
- foreach(AbstractNode * v, children)
- {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->render_cgal_nef_polyhedron();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- } else if (N.dim == 3) {
- N.p3 += v->render_cgal_nef_polyhedron().p3;
- }
- v->progress_report();
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
-CSGTerm *AbstractNode::render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const
-{
- QString key = mk_cache_id();
- if (PolySet::ps_cache.contains(key)) {
- PRINT(PolySet::ps_cache[key]->msg);
- return AbstractPolyNode::render_csg_term_from_ps(m, highlights, background,
- PolySet::ps_cache[key]->ps->link(), modinst, idx);
- }
-
- print_messages_push();
- CGAL_Nef_polyhedron N;
-
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id))
- {
- PRINT(cgal_nef_cache[cache_id]->msg);
- N = cgal_nef_cache[cache_id]->N;
- }
- else
- {
- PRINTF_NOCACHE("Processing uncached %s statement...", statement);
- // PRINTA("Cache ID: %1", cache_id);
- QApplication::processEvents();
-
- QTime t;
- t.start();
-
- N = this->render_cgal_nef_polyhedron();
-
- int s = t.elapsed() / 1000;
- PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60);
- }
-
- PolySet *ps = NULL;
-
- if (N.dim == 2)
- {
- DxfData dd(N);
- ps = new PolySet();
- ps->is2d = true;
- dxf_tesselate(ps, &dd, 0, true, false, 0);
- dxf_border_to_ps(ps, &dd);
- }
-
- if (N.dim == 3)
- {
- if (!N.p3.is_simple()) {
- PRINTF("WARNING: Result of %s() isn't valid 2-manifold! Modify your design..", statement);
- return NULL;
- }
-
- ps = new PolySet();
-
- CGAL_Polyhedron P;
- N.p3.convert_to_Polyhedron(P);
-
- typedef CGAL_Polyhedron::Vertex Vertex;
- typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
- typedef CGAL_Polyhedron::Facet_const_iterator FCI;
- typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC;
-
- for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) {
- HFCC hc = fi->facet_begin();
- HFCC hc_end = hc;
- ps->append_poly();
- do {
- Vertex v = *VCI((hc++)->vertex());
- double x = CGAL::to_double(v.point().x());
- double y = CGAL::to_double(v.point().y());
- double z = CGAL::to_double(v.point().z());
- ps->append_vertex(x, y, z);
- } while (hc != hc_end);
- }
- }
-
- if (ps)
- {
- ps->convexity = convexity;
- PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
-
- CSGTerm *term = new CSGTerm(ps, m, QString("n%1").arg(idx));
- if (modinst->tag_highlight && highlights)
- highlights->append(term->link());
- if (modinst->tag_background && background) {
- background->append(term);
- return NULL;
- }
- return term;
- }
- print_messages_pop();
-
- return NULL;
-}
-
-CSGTerm *RenderNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+std::string RenderNode::toString() const
{
- return render_csg_term_from_nef(m, highlights, background, "render", this->convexity);
-}
+ std::stringstream stream;
-#else
+ stream << this->name() << "(convexity = " << convexity << ")";
-CSGTerm *RenderNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- CSGTerm *t1 = NULL;
- PRINT("WARNING: Found render() statement but compiled without CGAL support!");
- foreach(AbstractNode * v, children) {
- CSGTerm *t2 = v->render_csg_term(m, highlights, background);
- if (t2 && !t1) {
- t1 = t2;
- } else if (t2 && t1) {
- t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
- }
- }
- if (modinst->tag_highlight && highlights)
- highlights->append(t1->link());
- if (t1 && modinst->tag_background && background) {
- background->append(t1);
- return NULL;
- }
- return t1;
+ return stream.str();
}
-
-#endif
-
-QString RenderNode::dump(QString indent) const
-{
- if (dump_cache.isEmpty()) {
- QString text = indent + QString("n%1: ").arg(idx) + QString("render(convexity = %1) {\n").arg(QString::number(convexity));
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- ((AbstractNode*)this)->dump_cache = text + indent + "}\n";
- }
- return dump_cache;
-}
-
diff --git a/src/rendernode.h b/src/rendernode.h
new file mode 100644
index 0000000..9138c29
--- /dev/null
+++ b/src/rendernode.h
@@ -0,0 +1,21 @@
+#ifndef RENDERNODE_H_
+#define RENDERNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+#include <string>
+
+class RenderNode : public AbstractNode
+{
+public:
+ RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const { return "render"; }
+
+ int convexity;
+};
+
+#endif
diff --git a/src/state.h b/src/state.h
new file mode 100644
index 0000000..69aee87
--- /dev/null
+++ b/src/state.h
@@ -0,0 +1,41 @@
+#ifndef STATE_H_
+#define STATE_H_
+
+#include <cstring>
+
+class State
+{
+public:
+ State(const class AbstractNode *parent)
+ : parentnode(parent), isprefix(false), ispostfix(false), numchildren(0) {
+ for (int i=0;i<16;i++) this->m[i] = i % 5 == 0 ? 1.0 : 0.0;
+ for (int i=0;i<4;i++) this->c[i] = -1.0;
+ }
+ virtual ~State() {}
+
+ void setPrefix(bool on) { this->isprefix = on; }
+ void setPostfix(bool on) { this->ispostfix = on; }
+ void setNumChildren(unsigned int numc) { this->numchildren = numc; }
+ void setParent(const AbstractNode *parent) { this->parentnode = parent; }
+ void setMatrix(const double m[16]) { memcpy(this->m, m, 16*sizeof(double)); }
+ void setColor(const double c[4]) { memcpy(this->c, c, 4*sizeof(double)); }
+
+ bool isPrefix() const { return this->isprefix; }
+ bool isPostfix() const { return this->ispostfix; }
+ unsigned int numChildren() const { return this->numchildren; }
+ const AbstractNode *parent() const { return this->parentnode; }
+ const double *matrix() const { return this->m; }
+ const double *color() const { return this->c; }
+
+private:
+ const AbstractNode * parentnode;
+ bool isprefix;
+ bool ispostfix;
+ unsigned int numchildren;
+
+ // Transformation matrix and color. FIXME: Generalize such state variables?
+ double m[16];
+ double c[4];
+};
+
+#endif
diff --git a/src/stl-utils.h b/src/stl-utils.h
new file mode 100644
index 0000000..a48b7d5
--- /dev/null
+++ b/src/stl-utils.h
@@ -0,0 +1,19 @@
+#ifndef STLUTILS_H_
+#define STLUTILS_H_
+
+template<class T>
+struct del_fun_t
+{
+ del_fun_t& operator()(T* p) {
+ delete p;
+ return *this;
+ }
+};
+
+template<class T>
+del_fun_t<T> del_fun()
+{
+ return del_fun_t<T>();
+}
+
+#endif
diff --git a/src/surface.cc b/src/surface.cc
index 92b661f..22598bf 100644
--- a/src/surface.cc
+++ b/src/surface.cc
@@ -31,9 +31,16 @@
#include "builtin.h"
#include "dxftess.h"
#include "printutils.h"
-#include "openscad.h" // handle_dep()
+#include "handle_dep.h" // handle_dep()
+#include "visitor.h"
#include <QFile>
+#include <QRegExp>
+#include <QStringList>
+#include <sstream>
+#include <boost/unordered_map.hpp>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
class SurfaceModule : public AbstractModule
{
@@ -45,12 +52,17 @@ public:
class SurfaceNode : public AbstractPolyNode
{
public:
- QString filename;
+ SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const { return "surface"; }
+
+ std::string filename;
bool center;
int convexity;
- SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { }
- virtual PolySet *render_polyset(render_mode_e mode) const;
- virtual QString dump(QString indent) const;
+ virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;
};
AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
@@ -59,13 +71,14 @@ AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiat
node->center = false;
node->convexity = 1;
- QVector<QString> argnames = QVector<QString>() << "file" << "center" << "convexity";
- QVector<Expression*> argexpr;
+ std::vector<std::string> argnames;
+ argnames += "file", "center", "convexity";
+ std::vector<Expression*> argexpr;
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
- node->filename = c.get_absolute_path(c.lookup_variable("file").text);
+ node->filename = c.getAbsolutePath(c.lookup_variable("file").text);
Value center = c.lookup_variable("center", true);
if (center.type == Value::BOOL) {
@@ -85,18 +98,19 @@ void register_builtin_surface()
builtin_modules["surface"] = new SurfaceModule();
}
-PolySet *SurfaceNode::render_polyset(render_mode_e) const
+PolySet *SurfaceNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const
{
+ PolySet *p = new PolySet();
handle_dep(filename);
- QFile f(filename);
+ QFile f(QString::fromStdString(filename));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTF("WARNING: Can't open DXF file `%s'.", filename.toAscii().data());
- return NULL;
+ PRINTF("WARNING: Can't open DAT file `%s'.", filename.c_str());
+ return p;
}
int lines = 0, columns = 0;
- QHash<QPair<int,int>,double> data;
+ boost::unordered_map<std::pair<int,int>,double> data;
double min_val = 0;
while (!f.atEnd())
@@ -113,13 +127,12 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
if (i >= columns)
columns = i + 1;
double v = fields[i].toDouble();
- data[QPair<int,int>(lines, i)] = v;
+ data[std::make_pair(lines, i)] = v;
min_val = fmin(v-1, min_val);
}
lines++;
}
- PolySet *p = new PolySet();
p->convexity = convexity;
double ox = center ? -columns/2.0 : 0;
@@ -128,10 +141,10 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
for (int i = 1; i < lines; i++)
for (int j = 1; j < columns; j++)
{
- double v1 = data[QPair<int,int>(i-1, j-1)];
- double v2 = data[QPair<int,int>(i-1, j)];
- double v3 = data[QPair<int,int>(i, j-1)];
- double v4 = data[QPair<int,int>(i, j)];
+ double v1 = data[std::make_pair(i-1, j-1)];
+ double v2 = data[std::make_pair(i-1, j)];
+ double v3 = data[std::make_pair(i, j-1)];
+ double v4 = data[std::make_pair(i, j)];
double vx = (v1 + v2 + v3 + v4) / 4;
p->append_poly();
@@ -159,14 +172,14 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
{
p->append_poly();
p->append_vertex(ox + 0, oy + i-1, min_val);
- p->append_vertex(ox + 0, oy + i-1, data[QPair<int,int>(i-1, 0)]);
- p->append_vertex(ox + 0, oy + i, data[QPair<int,int>(i, 0)]);
+ p->append_vertex(ox + 0, oy + i-1, data[std::make_pair(i-1, 0)]);
+ p->append_vertex(ox + 0, oy + i, data[std::make_pair(i, 0)]);
p->append_vertex(ox + 0, oy + i, min_val);
p->append_poly();
p->insert_vertex(ox + columns-1, oy + i-1, min_val);
- p->insert_vertex(ox + columns-1, oy + i-1, data[QPair<int,int>(i-1, columns-1)]);
- p->insert_vertex(ox + columns-1, oy + i, data[QPair<int,int>(i, columns-1)]);
+ p->insert_vertex(ox + columns-1, oy + i-1, data[std::make_pair(i-1, columns-1)]);
+ p->insert_vertex(ox + columns-1, oy + i, data[std::make_pair(i, columns-1)]);
p->insert_vertex(ox + columns-1, oy + i, min_val);
}
@@ -174,14 +187,14 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
{
p->append_poly();
p->insert_vertex(ox + i-1, oy + 0, min_val);
- p->insert_vertex(ox + i-1, oy + 0, data[QPair<int,int>(0, i-1)]);
- p->insert_vertex(ox + i, oy + 0, data[QPair<int,int>(0, i)]);
+ p->insert_vertex(ox + i-1, oy + 0, data[std::make_pair(0, i-1)]);
+ p->insert_vertex(ox + i, oy + 0, data[std::make_pair(0, i)]);
p->insert_vertex(ox + i, oy + 0, min_val);
p->append_poly();
p->append_vertex(ox + i-1, oy + lines-1, min_val);
- p->append_vertex(ox + i-1, oy + lines-1, data[QPair<int,int>(lines-1, i-1)]);
- p->append_vertex(ox + i, oy + lines-1, data[QPair<int,int>(lines-1, i)]);
+ p->append_vertex(ox + i-1, oy + lines-1, data[std::make_pair(lines-1, i-1)]);
+ p->append_vertex(ox + i, oy + lines-1, data[std::make_pair(lines-1, i)]);
p->append_vertex(ox + i, oy + lines-1, min_val);
}
@@ -198,14 +211,12 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
return p;
}
-QString SurfaceNode::dump(QString indent) const
+std::string SurfaceNode::toString() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- text.sprintf("surface(file = \"%s\", center = %s);\n",
- filename.toAscii().data(), center ? "true" : "false");
- ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text;
- }
- return dump_cache;
-}
+ std::stringstream stream;
+ stream << this->name() << "(file = \"" << this->filename
+ << "\", center = " << (this->center ? "true" : "false") << ")";
+
+ return stream.str();
+}
diff --git a/src/transform.cc b/src/transform.cc
index 9df9ade..3308e0c 100644
--- a/src/transform.cc
+++ b/src/transform.cc
@@ -24,8 +24,8 @@
*
*/
+#include "transformnode.h"
#include "module.h"
-#include "node.h"
#include "context.h"
#include "dxfdata.h"
#include "csgterm.h"
@@ -33,14 +33,19 @@
#include "dxftess.h"
#include "builtin.h"
#include "printutils.h"
+#include "visitor.h"
+#include <sstream>
+#include <vector>
+#include <assert.h>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign; // bring 'operator+=()' into scope
enum transform_type_e {
SCALE,
ROTATE,
MIRROR,
TRANSLATE,
- MULTMATRIX,
- COLOR
+ MULTMATRIX
};
class TransformModule : public AbstractModule
@@ -51,69 +56,80 @@ public:
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
-class TransformNode : public AbstractNode
+using std::string;
+using std::vector;
+
+static vector<string> split(const string &str, const string &delim)
{
-public:
- double m[20];
- TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const;
-#endif
- virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
- virtual QString dump(QString indent) const;
-};
+ assert(delim.size() > 0);
+
+ vector<string> strvec;
+ size_t start = 0, end = 0;
+ while (end != string::npos) {
+ end = str.find(delim, start);
+ // If at end, use length=maxLength. Else use length=end-start.
+ strvec.push_back(str.substr(start, (end == string::npos) ? string::npos : end - start));
+ // If at end, use start=maxSize. Else use start=end+delimiter.
+ start = ((end > (string::npos - delim.size())) ? string::npos : end + delim.size());
+ }
+ return strvec;
+}
+
+template <class T> static bool from_string(T &t, const string &s)
+{
+ std::istringstream iss(s);
+ return !(iss >> t).fail();
+}
AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
TransformNode *node = new TransformNode(inst);
for (int i = 0; i < 16; i++)
- node->m[i] = i % 5 == 0 ? 1.0 : 0.0;
- for (int i = 16; i < 19; i++)
- node->m[i] = -1;
- node->m[19] = 1;
-
- QVector<QString> argnames;
- QVector<Expression*> argexpr;
-
- if (type == SCALE) {
- argnames = QVector<QString>() << "v";
- }
- if (type == ROTATE) {
- argnames = QVector<QString>() << "a" << "v";
- }
- if (type == MIRROR) {
- argnames = QVector<QString>() << "v";
- }
- if (type == TRANSLATE) {
- argnames = QVector<QString>() << "v";
- }
- if (type == MULTMATRIX) {
- argnames = QVector<QString>() << "m";
- }
- if (type == COLOR) {
- argnames = QVector<QString>() << "c" << "alpha";
+ node->matrix[i] = i % 5 == 0 ? 1.0 : 0.0;
+
+ std::vector<std::string> argnames;
+ std::vector<Expression*> argexpr;
+
+ switch (this->type) {
+ case SCALE:
+ argnames += "v";
+ break;
+ case ROTATE:
+ argnames += "a", "v";
+ break;
+ case MIRROR:
+ argnames += "v";
+ break;
+ case TRANSLATE:
+ argnames += "v";
+ break;
+ case MULTMATRIX:
+ argnames += "m";
+ break;
+ default:
+ assert(false);
}
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
- if (type == SCALE)
+ if (this->type == SCALE)
{
Value v = c.lookup_variable("v");
- v.getnum(node->m[0]);
- v.getnum(node->m[5]);
- v.getnum(node->m[10]);
- v.getv3(node->m[0], node->m[5], node->m[10]);
- if (node->m[10] <= 0)
- node->m[10] = 1;
+ v.getnum(node->matrix[0]);
+ v.getnum(node->matrix[5]);
+ v.getnum(node->matrix[10]);
+ v.getv3(node->matrix[0], node->matrix[5], node->matrix[10]);
+ if (node->matrix[10] <= 0)
+ node->matrix[10] = 1;
}
- if (type == ROTATE)
+ else if (this->type == ROTATE)
{
Value val_a = c.lookup_variable("a");
if (val_a.type == Value::VECTOR)
{
- for (int i = 0; i < 3 && i < val_a.vec.size(); i++) {
+ for (size_t i = 0; i < 3 && i < val_a.vec.size(); i++) {
double a;
val_a.vec[i]->getnum(a);
double c = cos(a*M_PI/180.0);
@@ -140,10 +156,10 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
{
m[x+y*4] = 0;
for (int i = 0; i < 4; i++)
- m[x+y*4] += node->m[i+y*4] * mr[x+i*4];
+ m[x+y*4] += node->matrix[i+y*4] * mr[x+i*4];
}
for (int i = 0; i < 16; i++)
- node->m[i] = m[i];
+ node->matrix[i] = m[i];
}
}
else
@@ -165,21 +181,21 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
double c = cos(a*M_PI/180.0);
double s = sin(a*M_PI/180.0);
- node->m[ 0] = x*x*(1-c)+c;
- node->m[ 1] = y*x*(1-c)+z*s;
- node->m[ 2] = z*x*(1-c)-y*s;
+ node->matrix[ 0] = x*x*(1-c)+c;
+ node->matrix[ 1] = y*x*(1-c)+z*s;
+ node->matrix[ 2] = z*x*(1-c)-y*s;
- node->m[ 4] = x*y*(1-c)-z*s;
- node->m[ 5] = y*y*(1-c)+c;
- node->m[ 6] = z*y*(1-c)+x*s;
+ node->matrix[ 4] = x*y*(1-c)-z*s;
+ node->matrix[ 5] = y*y*(1-c)+c;
+ node->matrix[ 6] = z*y*(1-c)+x*s;
- node->m[ 8] = x*z*(1-c)+y*s;
- node->m[ 9] = y*z*(1-c)-x*s;
- node->m[10] = z*z*(1-c)+c;
+ node->matrix[ 8] = x*z*(1-c)+y*s;
+ node->matrix[ 9] = y*z*(1-c)-x*s;
+ node->matrix[10] = z*z*(1-c)+c;
}
}
}
- if (type == MIRROR)
+ else if (this->type == MIRROR)
{
Value val_v = c.lookup_variable("v");
double x = 1, y = 0, z = 0;
@@ -193,197 +209,65 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
if (x != 0.0 || y != 0.0 || z != 0.0)
{
- node->m[ 0] = 1-2*x*x;
- node->m[ 1] = -2*y*x;
- node->m[ 2] = -2*z*x;
+ node->matrix[ 0] = 1-2*x*x;
+ node->matrix[ 1] = -2*y*x;
+ node->matrix[ 2] = -2*z*x;
- node->m[ 4] = -2*x*y;
- node->m[ 5] = 1-2*y*y;
- node->m[ 6] = -2*z*y;
+ node->matrix[ 4] = -2*x*y;
+ node->matrix[ 5] = 1-2*y*y;
+ node->matrix[ 6] = -2*z*y;
- node->m[ 8] = -2*x*z;
- node->m[ 9] = -2*y*z;
- node->m[10] = 1-2*z*z;
+ node->matrix[ 8] = -2*x*z;
+ node->matrix[ 9] = -2*y*z;
+ node->matrix[10] = 1-2*z*z;
}
}
- if (type == TRANSLATE)
+ else if (this->type == TRANSLATE)
{
Value v = c.lookup_variable("v");
- v.getv3(node->m[12], node->m[13], node->m[14]);
+ v.getv3(node->matrix[12], node->matrix[13], node->matrix[14]);
}
- if (type == MULTMATRIX)
+ else if (this->type == MULTMATRIX)
{
Value v = c.lookup_variable("m");
if (v.type == Value::VECTOR) {
for (int i = 0; i < 16; i++) {
- int x = i / 4, y = i % 4;
+ size_t x = i / 4, y = i % 4;
if (y < v.vec.size() && v.vec[y]->type == Value::VECTOR && x < v.vec[y]->vec.size())
- v.vec[y]->vec[x]->getnum(node->m[i]);
+ v.vec[y]->vec[x]->getnum(node->matrix[i]);
}
}
}
- if (type == COLOR)
- {
- Value v = c.lookup_variable("c");
- if (v.type == Value::VECTOR) {
- for (int i = 0; i < 4; i++)
- node->m[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0;
- } else if (v.type == Value::STRING) {
- QString colorname = v.text;
- QColor color;
- color.setNamedColor(colorname);
- if (color.isValid()) {
- node->m[16+0] = color.redF();
- node->m[16+1] = color.greenF();
- node->m[16+2] = color.blueF();
- } else {
- PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data());
- PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");
- }
- }
- Value alpha = c.lookup_variable("alpha");
- if (alpha.type == Value::NUMBER) {
- node->m[16+3] = alpha.num;
- }
- }
- foreach (ModuleInstantiation *v, inst->children) {
- AbstractNode *n = v->evaluate(inst->ctx);
- if (n != NULL)
- node->children.append(n);
- }
+ vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const
+string TransformNode::toString() const
{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
-
- foreach (AbstractNode *v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->render_cgal_nef_polyhedron();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- N.p2 += v->render_cgal_nef_polyhedron().p2;
- } else if (N.dim == 3) {
- N.p3 += v->render_cgal_nef_polyhedron().p3;
+ std::stringstream stream;
+
+ stream << "multmatrix([";
+ for (int j=0;j<4;j++) {
+ stream << "[";
+ for (int i=0;i<4;i++) {
+ // FIXME: The 0 test is to avoid a leading minus before a single 0 (cosmetics)
+ stream << ((this->matrix[i*4+j]==0)?0:this->matrix[i*4+j]);
+ if (i != 3) stream << ", ";
}
- v->progress_report();
+ stream << "]";
+ if (j != 3) stream << ", ";
}
+ stream << "])";
- if (N.dim == 2)
- {
- // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2
- // 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(
- m[0], m[4], m[12],
- m[1], m[5], m[13], m[15]);
-
- DxfData dd(N);
- for (int i=0; i < dd.points.size(); i++) {
- CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y);
- p = t.transform(p);
- dd.points[i].x = to_double(p.x());
- dd.points[i].y = to_double(p.y());
- }
-
- PolySet ps;
- ps.is2d = true;
- dxf_tesselate(&ps, &dd, 0, true, false, 0);
-
- N = ps.render_cgal_nef_polyhedron();
- ps.refcount = 0;
- }
- if (N.dim == 3) {
- CGAL_Aff_transformation t(
- m[0], m[4], m[ 8], m[12],
- m[1], m[5], m[ 9], m[13],
- m[2], m[6], m[10], m[14], m[15]);
- N.p3.transform(t);
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
-#endif /* ENABLE_CGAL */
-
-CSGTerm *TransformNode::render_csg_term(double c[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- double x[20];
-
- for (int i = 0; i < 16; i++)
- {
- int c_row = i%4;
- int m_col = i/4;
- x[i] = 0;
- for (int j = 0; j < 4; j++)
- x[i] += c[c_row + j*4] * m[m_col*4 + j];
- }
-
- for (int i = 16; i < 20; i++)
- x[i] = m[i] < 0 ? c[i] : m[i];
-
- CSGTerm *t1 = NULL;
- foreach(AbstractNode *v, children)
- {
- CSGTerm *t2 = v->render_csg_term(x, highlights, background);
- if (t2 && !t1) {
- t1 = t2;
- } else if (t2 && t1) {
- t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
- }
- }
- if (t1 && modinst->tag_highlight && highlights)
- highlights->append(t1->link());
- if (t1 && modinst->tag_background && background) {
- background->append(t1);
- return NULL;
- }
- return t1;
+ return stream.str();
}
-QString TransformNode::dump(QString indent) const
+string TransformNode::name() const
{
- if (dump_cache.isEmpty()) {
- QString text;
- if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0)
- text.sprintf("n%d: color([%g, %g, %g, %g])", idx,
- m[16], m[17], m[18], m[19]);
- else
- text.sprintf("n%d: multmatrix([[%g, %g, %g, %g], [%g, %g, %g, %g], "
- "[%g, %g, %g, %g], [%g, %g, %g, %g]])", idx,
- m[0], m[4], m[ 8], m[12],
- m[1], m[5], m[ 9], m[13],
- m[2], m[6], m[10], m[14],
- m[3], m[7], m[11], m[15]);
- text = indent + text + " {\n";
- foreach (AbstractNode *v, children)
- text += v->dump(indent + QString("\t"));
- ((AbstractNode*)this)->dump_cache = text + indent + "}\n";
- }
- return dump_cache;
+ return "transform";
}
void register_builtin_transform()
@@ -393,6 +277,4 @@ void register_builtin_transform()
builtin_modules["mirror"] = new TransformModule(MIRROR);
builtin_modules["translate"] = new TransformModule(TRANSLATE);
builtin_modules["multmatrix"] = new TransformModule(MULTMATRIX);
- builtin_modules["color"] = new TransformModule(COLOR);
}
-
diff --git a/src/transformnode.h b/src/transformnode.h
new file mode 100644
index 0000000..9d822cb
--- /dev/null
+++ b/src/transformnode.h
@@ -0,0 +1,20 @@
+#ifndef TRANSFORMNODE_H_
+#define TRANSFORMNODE_H_
+
+#include "node.h"
+#include "visitor.h"
+
+class TransformNode : public AbstractNode
+{
+public:
+ TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }
+ virtual Response accept(class State &state, Visitor &visitor) const {
+ return visitor.visit(state, *this);
+ }
+ virtual std::string toString() const;
+ virtual std::string name() const;
+
+ double matrix[16];
+};
+
+#endif
diff --git a/src/traverser.cc b/src/traverser.cc
new file mode 100644
index 0000000..a87449b
--- /dev/null
+++ b/src/traverser.cc
@@ -0,0 +1,44 @@
+#include "traverser.h"
+#include "visitor.h"
+#include "node.h"
+#include "state.h"
+#include <algorithm>
+
+void Traverser::execute()
+{
+ State state(NULL);
+ traverse(this->root, state);
+}
+
+struct TraverseNode
+{
+ Traverser *traverser;
+ const State &state;
+ TraverseNode(Traverser *traverser, const State &state) :
+ traverser(traverser), state(state) {}
+ void operator()(const AbstractNode *node) { traverser->traverse(*node, state); }
+};
+
+void Traverser::traverse(const AbstractNode &node, const State &state)
+{
+ // FIXME: Handle abort
+
+ State newstate = state;
+ newstate.setNumChildren(node.getChildren().size());
+
+ if (traversaltype == PREFIX || traversaltype == PRE_AND_POSTFIX) {
+ newstate.setPrefix(true);
+ newstate.setParent(state.parent());
+ node.accept(newstate, this->visitor);
+ }
+
+ newstate.setParent(&node);
+ std::for_each(node.getChildren().begin(), node.getChildren().end(), TraverseNode(this, newstate));
+
+ if (traversaltype == POSTFIX || traversaltype == PRE_AND_POSTFIX) {
+ newstate.setParent(state.parent());
+ newstate.setPrefix(false);
+ newstate.setPostfix(true);
+ node.accept(newstate, this->visitor);
+ }
+}
diff --git a/src/traverser.h b/src/traverser.h
new file mode 100644
index 0000000..85373cc
--- /dev/null
+++ b/src/traverser.h
@@ -0,0 +1,26 @@
+#ifndef TRAVERSER_H_
+#define TRAVERSER_H_
+
+enum Response {ContinueTraversal, AbortTraversal, PruneTraversal};
+
+class Traverser
+{
+public:
+ enum TraversalType {PREFIX, POSTFIX, PRE_AND_POSTFIX};
+
+ Traverser(class Visitor &visitor, const class AbstractNode &root, TraversalType travtype)
+ : visitor(visitor), root(root), traversaltype(travtype) {
+ }
+ virtual ~Traverser() { }
+
+ void execute();
+ // FIXME: reverse parameters
+ void traverse(const AbstractNode &node, const class State &state);
+private:
+
+ Visitor &visitor;
+ const AbstractNode &root;
+ TraversalType traversaltype;
+};
+
+#endif
diff --git a/src/value.cc b/src/value.cc
index b0a79a4..34566bd 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -26,6 +26,8 @@
#include "value.h"
#include "mathc99.h"
+#include <assert.h>
+#include <sstream>
Value::Value()
{
@@ -34,8 +36,7 @@ Value::Value()
Value::~Value()
{
- for (int i = 0; i < this->vec.size(); i++)
- delete this->vec[i];
+ for (size_t i = 0; i < this->vec.size(); i++) delete this->vec[i];
this->vec.clear();
}
@@ -53,7 +54,7 @@ Value::Value(double v)
this->num = v;
}
-Value::Value(const QString &t)
+Value::Value(const std::string &t)
{
reset_undef();
this->type = STRING;
@@ -71,8 +72,9 @@ Value& Value::operator = (const Value &v)
this->type = v.type;
this->b = v.b;
this->num = v.num;
- for (int i = 0; i < v.vec.size(); i++)
- this->vec.append(new Value(*v.vec[i]));
+ for (size_t i = 0; i < v.vec.size(); i++) {
+ this->vec.push_back(new Value(*v.vec[i]));
+ }
this->range_begin = v.range_begin;
this->range_step = v.range_step;
this->range_end = v.range_end;
@@ -109,8 +111,8 @@ Value Value::operator + (const Value &v) const
if (this->type == VECTOR && v.type == VECTOR) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < this->vec.size() && i < v.vec.size(); i++)
- r.vec.append(new Value(*this->vec[i] + *v.vec[i]));
+ for (size_t i = 0; i < this->vec.size() && i < v.vec.size(); i++)
+ r.vec.push_back(new Value(*this->vec[i] + *v.vec[i]));
return r;
}
if (this->type == NUMBER && v.type == NUMBER) {
@@ -124,8 +126,8 @@ Value Value::operator - (const Value &v) const
if (this->type == VECTOR && v.type == VECTOR) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < this->vec.size() && i < v.vec.size(); i++)
- r.vec.append(new Value(*this->vec[i] - *v.vec[i]));
+ for (size_t i = 0; i < this->vec.size() && i < v.vec.size(); i++)
+ r.vec.push_back(new Value(*this->vec[i] - *v.vec[i]));
return r;
}
if (this->type == NUMBER && v.type == NUMBER) {
@@ -139,15 +141,15 @@ Value Value::operator * (const Value &v) const
if (this->type == VECTOR && v.type == NUMBER) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < this->vec.size(); i++)
- r.vec.append(new Value(*this->vec[i] * v));
+ for (size_t i = 0; i < this->vec.size(); i++)
+ r.vec.push_back(new Value(*this->vec[i] * v));
return r;
}
if (this->type == NUMBER && v.type == VECTOR) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < v.vec.size(); i++)
- r.vec.append(new Value(*this * *v.vec[i]));
+ for (size_t i = 0; i < v.vec.size(); i++)
+ r.vec.push_back(new Value(*this * *v.vec[i]));
return r;
}
if (this->type == NUMBER && v.type == NUMBER) {
@@ -161,15 +163,15 @@ Value Value::operator / (const Value &v) const
if (this->type == VECTOR && v.type == NUMBER) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < this->vec.size(); i++)
- r.vec.append(new Value(*this->vec[i] / v));
+ for (size_t i = 0; i < this->vec.size(); i++)
+ r.vec.push_back(new Value(*this->vec[i] / v));
return r;
}
if (this->type == NUMBER && v.type == VECTOR) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < v.vec.size(); i++)
- r.vec.append(new Value(v / *v.vec[i]));
+ for (size_t i = 0; i < v.vec.size(); i++)
+ r.vec.push_back(new Value(v / *v.vec[i]));
return r;
}
if (this->type == NUMBER && v.type == NUMBER) {
@@ -216,7 +218,7 @@ Value Value::operator == (const Value &v) const
if (this->type == VECTOR && v.type == VECTOR) {
if (this->vec.size() != v.vec.size())
return Value(false);
- for (int i=0; i<this->vec.size(); i++)
+ for (size_t i=0; i<this->vec.size(); i++)
if (!(*this->vec[i] == *v.vec[i]).b)
return Value(false);
return Value(true);
@@ -254,8 +256,8 @@ Value Value::inv() const
if (this->type == VECTOR) {
Value r;
r.type = VECTOR;
- for (int i = 0; i < this->vec.size(); i++)
- r.vec.append(new Value(this->vec[i]->inv()));
+ for (size_t i = 0; i < this->vec.size(); i++)
+ r.vec.push_back(new Value(this->vec[i]->inv()));
return r;
}
if (this->type == NUMBER)
@@ -307,46 +309,71 @@ bool Value::getv3(double &x, double &y, double &z) const
return true;
}
-QString Value::dump() const
-{
- if (this->type == STRING) {
- return QString("\"") + this->text + QString("\"");
- }
- if (this->type == VECTOR) {
- QString text = "[";
- for (int i = 0; i < this->vec.size(); i++) {
- if (i > 0)
- text += ", ";
- text += this->vec[i]->dump();
- }
- return text + "]";
- }
- if (this->type == RANGE) {
- QString text;
- text.sprintf("[ %g : %g : %g ]", this->range_begin, this->range_step, this->range_end);
- return text;
- }
- if (this->type == NUMBER) {
- QString text;
- text.sprintf("%g", this->num);
- return text;
- }
- if (this->type == BOOL) {
- return QString(this->b ? "true" : "false");
- }
- return QString("undef");
-}
-
void Value::reset_undef()
{
this->type = UNDEFINED;
this->b = false;
this->num = 0;
- for (int i = 0; i < this->vec.size(); i++)
- delete this->vec[i];
+ for (size_t i = 0; i < this->vec.size(); i++) delete this->vec[i];
this->vec.clear();
this->range_begin = 0;
this->range_step = 0;
this->range_end = 0;
- this->text = QString();
+ this->text = "";
+}
+
+std::string Value::toString() const
+{
+ std::stringstream stream;
+ stream.precision(16);
+
+ switch (this->type) {
+ case STRING:
+ stream << '"' << this->text << '"';
+ break;
+ case VECTOR:
+ stream << '[';
+ for (size_t i = 0; i < this->vec.size(); i++) {
+ if (i > 0) stream << ", ";
+ stream << *(this->vec[i]);
+ }
+ stream << ']';
+ break;
+ case RANGE:
+ stream << "[ "
+ << this->range_begin
+ << " : "
+ << this->range_step
+ << " : "
+ << this->range_end
+ << " ]";
+ break;
+ case NUMBER:
+ stream << this->num;
+ break;
+ case BOOL:
+ stream << this->b;
+ break;
+ default:
+ stream << "undef";
+ }
+
+ return stream.str();
}
+
+/*!
+ Append a value to this vector.
+ This must be of type VECTOR.
+*/
+void Value::append(Value *val)
+{
+ assert(this->type == VECTOR);
+ this->vec.push_back(val);
+}
+
+std::ostream &operator<<(std::ostream &stream, const Value &value)
+{
+ stream << value.toString();
+ return stream;
+}
+
diff --git a/src/value.h b/src/value.h
index 3491cbb..2003460 100644
--- a/src/value.h
+++ b/src/value.h
@@ -1,8 +1,8 @@
#ifndef VALUE_H_
#define VALUE_H_
-#include <QVector>
-#include <QString>
+#include <vector>
+#include <string>
class Value
{
@@ -20,18 +20,18 @@ public:
bool b;
double num;
- QVector<Value*> vec;
+ std::vector<Value*> vec;
double range_begin;
double range_step;
double range_end;
- QString text;
+ std::string text;
Value();
~Value();
Value(bool v);
Value(double v);
- Value(const QString &t);
+ Value(const std::string &t);
Value(const Value &v);
Value& operator = (const Value &v);
@@ -59,10 +59,14 @@ public:
bool getv2(double &x, double &y) const;
bool getv3(double &x, double &y, double &z) const;
- QString dump() const;
+ std::string toString() const;
+
+ void append(Value *val);
private:
void reset_undef();
};
+std::ostream &operator<<(std::ostream &stream, const Value &value);
+
#endif
diff --git a/src/visitor.h b/src/visitor.h
new file mode 100644
index 0000000..f4846b8
--- /dev/null
+++ b/src/visitor.h
@@ -0,0 +1,55 @@
+#ifndef VISITOR_H_
+#define VISITOR_H_
+
+#include "traverser.h"
+
+class Visitor
+{
+public:
+ Visitor() {}
+ virtual ~Visitor() {}
+
+ virtual Response visit(class State &state, const class AbstractNode &node) = 0;
+ virtual Response visit(class State &state, const class AbstractIntersectionNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class AbstractPolyNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class CgaladvNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class CsgNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class DxfLinearExtrudeNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class DxfRotateExtrudeNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class ImportNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class PrimitiveNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class ProjectionNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class RenderNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class SurfaceNode &node) {
+ return visit(state, (const class AbstractPolyNode &)node);
+ }
+ virtual Response visit(class State &state, const class TransformNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ virtual Response visit(class State &state, const class ColorNode &node) {
+ return visit(state, (const class AbstractNode &)node);
+ }
+ // Add visit() methods for new visitable subtypes of AbstractNode here
+};
+
+#endif
diff --git a/test-code/batch-cgal-tests.sh b/test-code/batch-cgal-tests.sh
deleted file mode 100755
index 12b8fe1..0000000
--- a/test-code/batch-cgal-tests.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-cmd="./cgaltest"
-
-for f in ../testdata/scad/*-tests.scad; do
- echo == `basename $f .scad` ==
- "$cmd" "$f"
-done
-
diff --git a/test-code/batch-cgal.sh b/test-code/batch-cgal.sh
deleted file mode 100755
index 031020d..0000000
--- a/test-code/batch-cgal.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-cmd="./cgaltest"
-
-if [ $# == 0 ]; then
- dir=../testdata/scad
-else
- dir=$1
-fi
-
-for f in $dir/*.scad; do
- echo == `basename $f` ==
- "$cmd" "$f"
-done
diff --git a/test-code/batch-dump.sh b/test-code/batch-dump.sh
deleted file mode 100755
index e8c9cd5..0000000
--- a/test-code/batch-dump.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-cmd="./dumptest"
-
-if [ $# == 0 ]; then
- dir=../testdata/scad
-else
- dir=$1
-fi
-
-echo $dir;
-
-for f in $dir/*.scad; do
- echo `basename $f`
- "$cmd" "$f"
-done
diff --git a/test-code/dumptest.cc b/test-code/exportdxf.cc
index 2cb8f12..97d6f49 100644
--- a/test-code/dumptest.cc
+++ b/test-code/exportdxf.cc
@@ -1,7 +1,6 @@
/*
- * OpenSCAD (www.openscad.org)
- * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
- * Marius Kintel <marius@kintel.net>
+ * OpenSCAD (www.openscad.at)
+ * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at>
*
* 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
@@ -24,6 +23,7 @@
*
*/
+#include "myqhash.h"
#include "openscad.h"
#include "node.h"
#include "module.h"
@@ -31,13 +31,17 @@
#include "value.h"
#include "export.h"
#include "builtin.h"
-//#include "nodedumper.h"
+#include "Tree.h"
+#include "CGALRenderer.h"
+#include "PolySetCGALRenderer.h"
#include <QApplication>
#include <QFile>
#include <QDir>
#include <QSet>
+#include <QTextStream>
#include <getopt.h>
+#include <iostream>
QString commandline_commands;
const char *make_command = NULL;
@@ -46,6 +50,8 @@ QString currentdir;
QString examplesdir;
QString librarydir;
+using std::string;
+
void handle_dep(QString filename)
{
if (filename.startsWith("/"))
@@ -59,6 +65,18 @@ void handle_dep(QString filename)
}
}
+// FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
+QHash<std::string, CGAL_Nef_polyhedron> cache;
+
+void cgalTree(Tree &tree)
+{
+ assert(tree.root());
+
+ CGALRenderer renderer(cache, tree);
+ Traverser render(renderer, *tree.root(), Traverser::PRE_AND_POSTFIX);
+ render.execute();
+}
+
int main(int argc, char **argv)
{
if (argc != 2) {
@@ -144,22 +162,18 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
- // Cache test
- QString dumpstr = root_node->dump("");
- QString dumpstr_cached = root_node->dump("");
- if (dumpstr != dumpstr_cached) rc = 1;
-
- printf(dumpstr.toUtf8());
-
-#if 0
- NodeDumper dumper;
- Traverser trav(dumper, *root_node, Traverser::PRE_AND_POSTFIX);
- trav.execute();
- std::string dumpstdstr = dumper.getDump();
- trav.execute();
- std::string dumpstdstr_cached = dumper.getDump();
- if (dumpstdstr != dumpstdstr_cached) rc = 1;
-#endif
+ Tree tree;
+ tree.setRoot(root_node);
+
+ cgalTree(tree);
+
+ CGAL_Nef_polyhedron N = cache[tree.getString(*root_node)];
+
+ QDir::setCurrent(original_path.absolutePath());
+ QTextStream outstream(stdout);
+ export_dxf(&N, outstream, NULL);
+
+ PolySetRenderer::setRenderer(NULL);
destroy_builtin_functions();
destroy_builtin_modules();
diff --git a/test-code/dumptest.pro b/test-code/exportdxf.pro
index a6729f7..62c2734 100644
--- a/test-code/dumptest.pro
+++ b/test-code/exportdxf.pro
@@ -1,38 +1,30 @@
DEFINES += OPENSCAD_VERSION=test
TEMPLATE = app
-OBJECTS_DIR = objects
-MOC_DIR = objects
-UI_DIR = objects
-RCC_DIR = objects
+OBJECTS_DIR = cgal-objects
+MOC_DIR = cgal-objects
+UI_DIR = cgal-objects
+RCC_DIR = cgal-objects
INCLUDEPATH += ../src
-TARGET = dumptest
macx {
+ macx {
+ DEPLOYDIR = $$(MACOSX_DEPLOY_DIR)
+ !isEmpty(DEPLOYDIR) {
+ INCLUDEPATH += $$DEPLOYDIR/include
+ LIBS += -L$$DEPLOYDIR/lib
+ }
+ }
CONFIG -= app_bundle
LIBS += -framework Carbon
}
CONFIG += qt
QT += opengl
+CONFIG += cgal
-# Optionally specify location of Eigen2 using the
-# EIGEN2DIR env. variable
-EIGEN2_DIR = $$(EIGEN2DIR)
-!isEmpty(EIGEN2_DIR) {
- INCLUDEPATH += $$EIGEN2_DIR
-}
-else {
- macx {
- INCLUDEPATH += /opt/local/include/eigen2
- }
- else {
- INCLUDEPATH += /usr/include/eigen2
- }
-}
-FORMS += ../src/Preferences.ui
-HEADERS += ../src/Preferences.h
-SOURCES += ../src/Preferences.cc
+include(../cgal.pri)
+include(../eigen2.pri)
LEXSOURCES += ../src/lexer.l
YACCSOURCES += ../src/parser.y
@@ -50,13 +42,32 @@ HEADERS += ../src/builtin.h \
../src/grid.h \
../src/module.h \
../src/node.h \
+ ../src/dxflinextrudenode.h \
+ ../src/dxfrotextrudenode.h \
+ ../src/projectionnode.h \
+ ../src/importnode.h \
+ ../src/csgnode.h \
+ ../src/transformnode.h \
+ ../src/rendernode.h \
../src/openscad.h \
../src/polyset.h \
../src/printutils.h \
../src/value.h \
- ../src/progress.h
+ ../src/progress.h \
+ ../src/traverser.h \
+ ../src/csgnode.h \
+ ../src/visitor.h \
+ ../src/nodedumper.h \
+ ../src/CGALRenderer.h \
+ ../src/nodecache.h \
+ ../src/importnode.h \
+ ../src/state.h \
+ ../src/PolySetRenderer.h \
+ ../src/PolySetCGALRenderer.h \
+ ../src/myqhash.h \
+ ../src/Tree.h
-SOURCES += dumptest.cc \
+SOURCES += exportdxf.cc \
../src/export.cc \
../src/value.cc \
../src/expr.cc \
@@ -71,11 +82,14 @@ SOURCES += dumptest.cc \
../src/primitives.cc \
../src/projection.cc \
../src/cgaladv.cc \
+ ../src/cgaladv_minkowski2.cc \
+ ../src/cgaladv_minkowski3.cc \
../src/surface.cc \
../src/control.cc \
../src/render.cc \
../src/import.cc \
../src/dxfdata.cc \
+ ../src/nef2dxf.cc \
../src/dxftess.cc \
../src/dxftess-glu.cc \
../src/dxftess-cgal.cc \
@@ -83,4 +97,11 @@ SOURCES += dumptest.cc \
../src/dxflinextrude.cc \
../src/dxfrotextrude.cc \
../src/printutils.cc \
- ../src/progress.cc
+ ../src/progress.cc \
+ ../src/nodedumper.cc \
+ ../src/CGALRenderer.cc \
+ ../src/traverser.cc \
+ ../src/PolySetRenderer.cc \
+ ../src/PolySetCGALRenderer.cc \
+ ../src/qhash.cc \
+ ../src/Tree.cc
diff --git a/testdata/dxf/multiple-layers.dxf b/testdata/dxf/multiple-layers.dxf
new file mode 100644
index 0000000..88b530f
--- /dev/null
+++ b/testdata/dxf/multiple-layers.dxf
@@ -0,0 +1,2140 @@
+999
+dxflib 2.2.0.0
+ 0
+SECTION
+ 2
+HEADER
+ 9
+$ACADVER
+ 1
+AC1015
+ 9
+$HANDSEED
+ 5
+FFFF
+ 9
+$DIMASZ
+ 40
+2.5
+ 9
+$PLIMMIN
+ 10
+0.0
+ 20
+0.0
+ 9
+$DIMEXE
+ 40
+1.25
+ 9
+$DIMGAP
+ 40
+0.625
+ 9
+$PLIMMAX
+ 10
+210.0
+ 20
+297.0
+ 9
+$INSUNITS
+ 70
+4
+ 9
+$DIMEXO
+ 40
+0.625
+ 9
+$DIMTXT
+ 40
+2.5
+ 9
+$CLAYER
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+TABLES
+ 0
+TABLE
+ 2
+VPORT
+ 5
+8
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+VPORT
+ 5
+30
+100
+AcDbSymbolTableRecord
+100
+AcDbViewportTableRecord
+ 2
+*Active
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 11
+1.0
+ 21
+1.0
+ 12
+286.3055555555554861
+ 22
+148.5
+ 13
+0.0
+ 23
+0.0
+ 14
+10.0
+ 24
+10.0
+ 15
+10.0
+ 25
+10.0
+ 16
+0.0
+ 26
+0.0
+ 36
+1.0
+ 17
+0.0
+ 27
+0.0
+ 37
+0.0
+ 40
+297.0
+ 41
+1.92798353909465
+ 42
+50.0
+ 43
+0.0
+ 44
+0.0
+ 50
+0.0
+ 51
+0.0
+ 71
+0
+ 72
+100
+ 73
+1
+ 74
+3
+ 75
+1
+ 76
+1
+ 77
+0
+ 78
+0
+281
+0
+ 65
+1
+110
+0.0
+120
+0.0
+130
+0.0
+111
+1.0
+121
+0.0
+131
+0.0
+112
+0.0
+122
+1.0
+132
+0.0
+ 79
+0
+146
+0.0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+LTYPE
+ 5
+5
+100
+AcDbSymbolTable
+ 70
+21
+ 0
+LTYPE
+ 5
+14
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+ByBlock
+ 70
+0
+ 3
+
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+15
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+ByLayer
+ 70
+0
+ 3
+
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+16
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CONTINUOUS
+ 70
+0
+ 3
+Solid line
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+31
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOT
+ 70
+0
+ 3
+Dot . . . . . . . . . . . . . . . . . . . . . .
+ 72
+65
+ 73
+2
+ 40
+6.3499999999999996
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+32
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOT2
+ 70
+0
+ 3
+Dot (.5x) .....................................
+ 72
+65
+ 73
+2
+ 40
+3.1749999999999998
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+33
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOTX2
+ 70
+0
+ 3
+Dot (2x) . . . . . . . . . . . . .
+ 72
+65
+ 73
+2
+ 40
+12.6999999999999993
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+34
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHED
+ 70
+0
+ 3
+Dashed __ __ __ __ __ __ __ __ __ __ __ __ __ _
+ 72
+65
+ 73
+2
+ 40
+19.0500000000000007
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+35
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHED2
+ 70
+0
+ 3
+Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ 72
+65
+ 73
+2
+ 40
+9.5250000000000004
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+36
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHEDX2
+ 70
+0
+ 3
+Dashed (2x) ____ ____ ____ ____ ____ ___
+ 72
+65
+ 73
+2
+ 40
+38.1000000000000014
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+37
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOT
+ 70
+0
+ 3
+Dash dot __ . __ . __ . __ . __ . __ . __ . __
+ 72
+65
+ 73
+4
+ 40
+25.3999999999999986
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+38
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOT2
+ 70
+0
+ 3
+Dash dot (.5x) _._._._._._._._._._._._._._._.
+ 72
+65
+ 73
+4
+ 40
+12.6999999999999993
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+39
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOTX2
+ 70
+0
+ 3
+Dash dot (2x) ____ . ____ . ____ . ___
+ 72
+65
+ 73
+4
+ 40
+50.7999999999999972
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+3A
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDE
+ 70
+0
+ 3
+Divide ____ . . ____ . . ____ . . ____ . . ____
+ 72
+65
+ 73
+6
+ 40
+31.75
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+3B
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDE2
+ 70
+0
+ 3
+Divide (.5x) __..__..__..__..__..__..__..__.._
+ 72
+65
+ 73
+6
+ 40
+15.875
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+3C
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDEX2
+ 70
+0
+ 3
+Divide (2x) ________ . . ________ . . _
+ 72
+65
+ 73
+6
+ 40
+63.5
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+3D
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTER
+ 70
+0
+ 3
+Center ____ _ ____ _ ____ _ ____ _ ____ _ ____
+ 72
+65
+ 73
+4
+ 40
+50.7999999999999972
+ 49
+31.75
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+3E
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTER2
+ 70
+0
+ 3
+Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ ___
+ 72
+65
+ 73
+4
+ 40
+28.5749999999999993
+ 49
+19.0500000000000007
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+3.1749999999999998
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+3F
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTERX2
+ 70
+0
+ 3
+Center (2x) ________ __ ________ __ _____
+ 72
+65
+ 73
+4
+ 40
+101.5999999999999943
+ 49
+63.5
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+40
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDER
+ 70
+0
+ 3
+Border __ __ . __ __ . __ __ . __ __ . __ __ .
+ 72
+65
+ 73
+6
+ 40
+44.4500000000000028
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+41
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDER2
+ 70
+0
+ 3
+Border (.5x) __.__.__.__.__.__.__.__.__.__.__.
+ 72
+65
+ 73
+6
+ 40
+22.2250000000000014
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+42
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDERX2
+ 70
+0
+ 3
+Border (2x) ____ ____ . ____ ____ . ___
+ 72
+65
+ 73
+6
+ 40
+88.9000000000000057
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+LAYER
+ 5
+2
+100
+AcDbSymbolTable
+ 70
+3
+ 0
+LAYER
+ 5
+10
+100
+AcDbSymbolTableRecord
+100
+AcDbLayerTableRecord
+ 2
+0
+ 70
+0
+ 62
+7
+420
+0
+ 6
+CONTINUOUS
+370
+25
+390
+F
+ 0
+LAYER
+ 5
+43
+100
+AcDbSymbolTableRecord
+100
+AcDbLayerTableRecord
+ 2
+noname
+ 70
+0
+ 62
+7
+420
+0
+ 6
+CONTINUOUS
+370
+0
+390
+F
+ 0
+LAYER
+ 5
+44
+100
+AcDbSymbolTableRecord
+100
+AcDbLayerTableRecord
+ 2
+Layer with a pretty long name including \ "special" /'\\ characters
+ 70
+0
+ 62
+7
+420
+0
+ 6
+CONTINUOUS
+370
+0
+390
+F
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+STYLE
+ 5
+3
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+STYLE
+ 5
+11
+100
+AcDbSymbolTableRecord
+100
+AcDbTextStyleTableRecord
+ 2
+Standard
+ 70
+0
+ 40
+0.0
+ 41
+0.75
+ 50
+0.0
+ 71
+0
+ 42
+2.5
+ 3
+txt
+ 4
+
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+VIEW
+ 5
+6
+100
+AcDbSymbolTable
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+UCS
+ 5
+7
+100
+AcDbSymbolTable
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+APPID
+ 5
+9
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+APPID
+ 5
+12
+100
+AcDbSymbolTableRecord
+100
+AcDbRegAppTableRecord
+ 2
+ACAD
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+DIMSTYLE
+ 5
+A
+100
+AcDbSymbolTable
+ 70
+1
+100
+AcDbDimStyleTable
+ 71
+0
+ 0
+DIMSTYLE
+105
+27
+100
+AcDbSymbolTableRecord
+100
+AcDbDimStyleTableRecord
+ 2
+Standard
+ 41
+2.5
+ 42
+0.625
+ 43
+3.75
+ 44
+1.25
+ 70
+0
+ 73
+0
+ 74
+0
+ 77
+1
+ 78
+8
+140
+2.5
+141
+2.5
+143
+0.03937007874016
+147
+0.625
+171
+3
+172
+1
+271
+2
+272
+2
+274
+3
+278
+44
+283
+0
+284
+8
+340
+11
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+BLOCK_RECORD
+ 5
+1
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+BLOCK_RECORD
+ 5
+1F
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Model_Space
+340
+22
+ 0
+BLOCK_RECORD
+ 5
+1B
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Paper_Space
+340
+1E
+ 0
+BLOCK_RECORD
+ 5
+23
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Paper_Space0
+340
+26
+ 0
+ENDTAB
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+BLOCKS
+ 0
+BLOCK
+ 5
+20
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Model_Space
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Model_Space
+ 1
+
+ 0
+ENDBLK
+ 5
+21
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+BLOCK
+ 5
+1C
+100
+AcDbEntity
+ 67
+1
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Paper_Space
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Paper_Space
+ 1
+
+ 0
+ENDBLK
+ 5
+1D
+100
+AcDbEntity
+ 67
+1
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+BLOCK
+ 5
+24
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Paper_Space0
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Paper_Space0
+ 1
+
+ 0
+ENDBLK
+ 5
+25
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+ENTITIES
+ 0
+LINE
+ 5
+45
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+-40.0
+ 20
+40.0
+ 30
+0.0
+ 11
+40.0
+ 21
+40.0
+ 31
+0.0
+ 0
+LINE
+ 5
+46
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+40.0
+ 20
+40.0
+ 30
+0.0
+ 11
+40.0
+ 21
+-40.0
+ 31
+0.0
+ 0
+LINE
+ 5
+47
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+40.0
+ 20
+-40.0
+ 30
+0.0
+ 11
+-40.0
+ 21
+-40.0
+ 31
+0.0
+ 0
+LINE
+ 5
+48
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+-40.0
+ 20
+-40.0
+ 30
+0.0
+ 11
+-40.0
+ 21
+40.0
+ 31
+0.0
+ 0
+CIRCLE
+ 5
+49
+100
+AcDbEntity
+100
+AcDbCircle
+ 8
+noname
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 40
+70.7106781186547551
+ 0
+LINE
+ 5
+4A
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+100.0
+ 20
+0.0
+ 30
+0.0
+ 11
+50.0000000000000142
+ 21
+86.6025403784438623
+ 31
+0.0
+ 0
+LINE
+ 5
+4B
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+50.0000000000000142
+ 20
+86.6025403784438623
+ 30
+0.0
+ 11
+-49.9999999999999858
+ 21
+86.6025403784438907
+ 31
+0.0
+ 0
+LINE
+ 5
+4C
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+-49.9999999999999858
+ 20
+86.6025403784438907
+ 30
+0.0
+ 11
+-100.0000000000000142
+ 21
+0.0000000000000122
+ 31
+0.0
+ 0
+LINE
+ 5
+4D
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+-100.0000000000000142
+ 20
+0.0000000000000122
+ 30
+0.0
+ 11
+-50.0000000000000497
+ 21
+-86.6025403784438481
+ 31
+0.0
+ 0
+LINE
+ 5
+4E
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+-50.0000000000000497
+ 20
+-86.6025403784438481
+ 30
+0.0
+ 11
+49.9999999999998579
+ 21
+-86.6025403784439618
+ 31
+0.0
+ 0
+LINE
+ 5
+4F
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+Layer with a pretty long name including \ "special" /'\\ characters
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+49.9999999999998579
+ 20
+-86.6025403784439618
+ 30
+0.0
+ 11
+100.0000000000000142
+ 21
+-0.0000000000002021
+ 31
+0.0
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+OBJECTS
+ 0
+DICTIONARY
+ 5
+C
+100
+AcDbDictionary
+280
+0
+281
+1
+ 3
+ACAD_GROUP
+350
+D
+ 3
+ACAD_LAYOUT
+350
+1A
+ 3
+ACAD_MLINESTYLE
+350
+17
+ 3
+ACAD_PLOTSETTINGS
+350
+19
+ 3
+ACAD_PLOTSTYLENAME
+350
+E
+ 3
+AcDbVariableDictionary
+350
+50
+ 0
+DICTIONARY
+ 5
+D
+100
+AcDbDictionary
+280
+0
+281
+1
+ 0
+ACDBDICTIONARYWDFLT
+ 5
+E
+100
+AcDbDictionary
+281
+1
+ 3
+Normal
+350
+F
+100
+AcDbDictionaryWithDefault
+340
+F
+ 0
+ACDBPLACEHOLDER
+ 5
+F
+ 0
+DICTIONARY
+ 5
+17
+100
+AcDbDictionary
+280
+0
+281
+1
+ 3
+Standard
+350
+18
+ 0
+MLINESTYLE
+ 5
+18
+100
+AcDbMlineStyle
+ 2
+STANDARD
+ 70
+0
+ 3
+
+ 62
+256
+ 51
+90.0
+ 52
+90.0
+ 71
+2
+ 49
+0.5
+ 62
+256
+ 6
+BYLAYER
+ 49
+-0.5
+ 62
+256
+ 6
+BYLAYER
+ 0
+DICTIONARY
+ 5
+19
+100
+AcDbDictionary
+280
+0
+281
+1
+ 0
+DICTIONARY
+ 5
+1A
+100
+AcDbDictionary
+281
+1
+ 3
+Layout1
+350
+1E
+ 3
+Layout2
+350
+26
+ 3
+Model
+350
+22
+ 0
+LAYOUT
+ 5
+1E
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+688
+ 72
+0
+ 73
+0
+ 74
+5
+ 7
+
+ 75
+16
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Layout1
+ 70
+1
+ 71
+1
+ 10
+0.0
+ 20
+0.0
+ 11
+420.0
+ 21
+297.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+100000000000000000000.0
+ 24
+100000000000000000000.0
+ 34
+100000000000000000000.0
+ 15
+-100000000000000000000.0
+ 25
+-100000000000000000000.0
+ 35
+-100000000000000000000.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+1B
+ 0
+LAYOUT
+ 5
+22
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+1712
+ 72
+0
+ 73
+0
+ 74
+0
+ 7
+
+ 75
+0
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Model
+ 70
+1
+ 71
+0
+ 10
+0.0
+ 20
+0.0
+ 11
+12.0
+ 21
+9.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+0.0
+ 24
+0.0
+ 34
+0.0
+ 15
+0.0
+ 25
+0.0
+ 35
+0.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+1F
+ 0
+LAYOUT
+ 5
+26
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+688
+ 72
+0
+ 73
+0
+ 74
+5
+ 7
+
+ 75
+16
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Layout2
+ 70
+1
+ 71
+2
+ 10
+0.0
+ 20
+0.0
+ 11
+12.0
+ 21
+9.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+0.0
+ 24
+0.0
+ 34
+0.0
+ 15
+0.0
+ 25
+0.0
+ 35
+0.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+23
+ 0
+DICTIONARY
+ 5
+50
+100
+AcDbDictionary
+281
+1
+ 3
+DIMASSOC
+350
+52
+ 3
+HIDETEXT
+350
+51
+ 0
+DICTIONARYVAR
+ 5
+51
+100
+DictionaryVariables
+280
+0
+ 1
+2
+ 0
+DICTIONARYVAR
+ 5
+52
+100
+DictionaryVariables
+280
+0
+ 1
+1
+ 0
+ENDSEC
+ 0
+EOF
diff --git a/testdata/scad/bugs/gridbug.scad b/testdata/scad/bugs/gridbug.scad
new file mode 100644
index 0000000..678a22a
--- /dev/null
+++ b/testdata/scad/bugs/gridbug.scad
@@ -0,0 +1,9 @@
+// This crashes OpenSCAD including 2011.06 in PolyReducer due to two vertices of
+// a triangle evaluating to the same index
+linear_extrude(height=2)
+ polygon(points=[[0, 0],
+ [1, 0],
+ [1.0014, 1],
+ [1, 1],
+ [0, 1]],
+ paths=[[0,1,2,3,4]]);
diff --git a/testdata/scad/polygon-illegal-winding.scad b/testdata/scad/bugs/polygon-illegal-winding.scad
index 612154a..612154a 100644
--- a/testdata/scad/polygon-illegal-winding.scad
+++ b/testdata/scad/bugs/polygon-illegal-winding.scad
diff --git a/testdata/scad/bugs/polygon-touch.scad b/testdata/scad/bugs/polygon-touch.scad
new file mode 100644
index 0000000..afa2938
--- /dev/null
+++ b/testdata/scad/bugs/polygon-touch.scad
@@ -0,0 +1,5 @@
+# Somehow the 2D union/tessellation algorithm doesn't support touching polygons
+# Changing translate([-10,-10,0]) to translate([-9.99,-9.99,0]) works
+
+square([10,10]);
+translate([-10,-10,0]) square([10,10]);
diff --git a/testdata/scad/polyset-reduce-crash.scad b/testdata/scad/bugs/polyset-reduce-crash.scad
index ce9fae7..ce9fae7 100644
--- a/testdata/scad/polyset-reduce-crash.scad
+++ b/testdata/scad/bugs/polyset-reduce-crash.scad
diff --git a/testdata/scad/polyset-reduce-crash.txt b/testdata/scad/bugs/polyset-reduce-crash.txt
index bc9e8d5..bc9e8d5 100644
--- a/testdata/scad/polyset-reduce-crash.txt
+++ b/testdata/scad/bugs/polyset-reduce-crash.txt
diff --git a/testdata/scad/difference-tests.scad b/testdata/scad/difference-tests.scad
deleted file mode 100644
index f945246..0000000
--- a/testdata/scad/difference-tests.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-difference() {
- sphere(3);
- cube([3,3,8], center=true);
-}
diff --git a/testdata/scad/features/2d-3d.scad b/testdata/scad/features/2d-3d.scad
new file mode 100644
index 0000000..ca9f966
--- /dev/null
+++ b/testdata/scad/features/2d-3d.scad
@@ -0,0 +1,3 @@
+// Test a mix of toplevel 2D and 3D objects
+cube();
+translate([2,0,0]) square();
diff --git a/testdata/scad/features/assign-tests.scad b/testdata/scad/features/assign-tests.scad
new file mode 100644
index 0000000..b86dabe
--- /dev/null
+++ b/testdata/scad/features/assign-tests.scad
@@ -0,0 +1,6 @@
+for(i=[0:5]) {
+ translate([i*i/2,0,0]) {
+ cube(i);
+ translate([0,-5,0]) assign(f=1.0*i/2) cube(f);
+ }
+}
diff --git a/testdata/scad/features/background-modifier.scad b/testdata/scad/features/background-modifier.scad
new file mode 100644
index 0000000..ec7b28d
--- /dev/null
+++ b/testdata/scad/features/background-modifier.scad
@@ -0,0 +1,4 @@
+difference() {
+ sphere(r=10);
+ %cylinder(h=30, r=6, center=true);
+}
diff --git a/testdata/scad/features/circle-tests.scad b/testdata/scad/features/circle-tests.scad
new file mode 100644
index 0000000..6b54d55
--- /dev/null
+++ b/testdata/scad/features/circle-tests.scad
@@ -0,0 +1,10 @@
+circle();
+translate([0,3,0]) circle(1);
+translate([5,1,0]) circle(r=3);
+translate([0,-1,0]) circle(r=0);
+translate([0,-3,0]) circle(1, $fn=4);
+translate([3,-3,0]) circle(1, $fn=8);
+translate([6,-3,0]) circle(1, $fn=12);
+translate([0,-6,0]) circle(1, $fa=20, $fs=0.3);
+translate([3,-6,0]) circle(1, $fa=30, $fs=0.3);
+translate([6,-6,0]) circle(1, $fa=40, $fs=0.3);
diff --git a/testdata/scad/features/color-tests.scad b/testdata/scad/features/color-tests.scad
new file mode 100644
index 0000000..3ef2540
--- /dev/null
+++ b/testdata/scad/features/color-tests.scad
@@ -0,0 +1,15 @@
+// Empty
+color();
+// No children
+color() { }
+
+module object() cube([10,10,10]);
+
+translate([12,12,0]) object();
+color([1,0,0]) translate([24,12,0]) object();
+translate([0,12,0]) color("Purple") object();
+color([0,0,1,0.5]) object();
+translate([12,0,0]) color([0,0,1],0.5) object();
+translate([24,0,0]) color(c="Green",alpha=0.2) object();
+translate([-12,12,0]) color() object();
+translate([-12,0,0]) color(alpha=0.5) object();
diff --git a/testdata/scad/features/cube-tests.scad b/testdata/scad/features/cube-tests.scad
new file mode 100644
index 0000000..ae2ead1
--- /dev/null
+++ b/testdata/scad/features/cube-tests.scad
@@ -0,0 +1,4 @@
+cube();
+cube([1,1,0]); cube([1,0,1]); cube([0,1,1]); cube([0,0,0]);
+translate([2,0,0]) cube([2,3,1]);
+translate([6,0,0]) cube([2,4,2], center=true);
diff --git a/testdata/scad/features/cylinder-tests.scad b/testdata/scad/features/cylinder-tests.scad
new file mode 100644
index 0000000..54e88cd
--- /dev/null
+++ b/testdata/scad/features/cylinder-tests.scad
@@ -0,0 +1,15 @@
+cylinder();
+translate([1,0,0]) cylinder(r=0);
+translate([2,0,0]) cylinder(r1=0, r2=0);
+translate([0,-11,0]) cylinder(r=5);
+translate([0,11,0]) cylinder(r=5, h=10, center=true);
+
+translate([11,-11,0]) cylinder(h=5, r1=5);
+translate([11,0,0]) cylinder(h=5, r1=5, r2=0);
+translate([11,11,0]) cylinder(h=8, r1=5, r2=5);
+
+translate([22,-11,0]) cylinder(h=5, r=5, r1=0, center=true);
+translate([22,0,0]) cylinder(h=5, r=5, r2=0);
+translate([22,11,0]) cylinder(h=15, r=5, r2=5);
+
+// FIXME: We could test $fs, $fa, $fn as well
diff --git a/testdata/scad/features/difference-tests.scad b/testdata/scad/features/difference-tests.scad
new file mode 100644
index 0000000..4d82676
--- /dev/null
+++ b/testdata/scad/features/difference-tests.scad
@@ -0,0 +1,30 @@
+// Empty
+difference();
+// No children
+difference() { }
+
+difference() {
+ cube([10,10,10], center=true);
+ cylinder(r=4, h=20, center=true);
+}
+
+translate([12,0,0]) difference() {
+ cube([10,10,10], center=true);
+ cylinder(r=4, h=10, center=true);
+}
+
+translate([0,12,0]) difference() {
+ cube([10,10,10], center=true);
+ cylinder(r=4, h=11, center=true);
+ rotate([0,90,0]) cylinder(r=4, h=11, center=true);
+}
+
+translate([12,12,0]) difference() {
+ cube([10,10,10], center=true);
+ translate([0,0,7]) cylinder(r=4, h=4, center=true);
+}
+
+translate([24,0,0]) difference() {
+ cube([10,10,10], center=true);
+ translate([0,0,6.99]) cylinder(r=4, h=4, center=true);
+}
diff --git a/testdata/scad/dim-all.dxf b/testdata/scad/features/dim-all.dxf
index 6ae7610..6ae7610 100644
--- a/testdata/scad/dim-all.dxf
+++ b/testdata/scad/features/dim-all.dxf
diff --git a/testdata/scad/dim-all.scad b/testdata/scad/features/dim-all.scad
index 454ed11..454ed11 100644
--- a/testdata/scad/dim-all.scad
+++ b/testdata/scad/features/dim-all.scad
diff --git a/testdata/scad/features/disable-modifier.scad b/testdata/scad/features/disable-modifier.scad
new file mode 100644
index 0000000..b47e074
--- /dev/null
+++ b/testdata/scad/features/disable-modifier.scad
@@ -0,0 +1,4 @@
+difference() {
+ *sphere(r=10);
+ cylinder(h=30, r=6, center=true);
+}
diff --git a/testdata/scad/features/dxf-export.scad b/testdata/scad/features/dxf-export.scad
new file mode 100644
index 0000000..7f4b8cb
--- /dev/null
+++ b/testdata/scad/features/dxf-export.scad
@@ -0,0 +1,12 @@
+circle(r=5);
+
+translate([15,0,0]) square(size=[10,10], center=true);
+
+translate([30,0,0]) polygon(points=[[-5,-5],[5,-5],[0,5]], paths=[[0,1,2]]);
+
+translate([0,-15,0]) {
+ difference() {
+ circle(r=5);
+ translate([0,-6,0]) square([12,12], center=true);
+ }
+} \ No newline at end of file
diff --git a/testdata/scad/features/for-nested-tests.scad b/testdata/scad/features/for-nested-tests.scad
new file mode 100644
index 0000000..e3b5877
--- /dev/null
+++ b/testdata/scad/features/for-nested-tests.scad
@@ -0,0 +1,3 @@
+for(x=[0:3], y=[0:0.5:1], z=[0,2,3]) {
+ translate(10*[x,y,z]) sphere(r=3);
+}
diff --git a/testdata/scad/features/for-tests.scad b/testdata/scad/features/for-tests.scad
new file mode 100644
index 0000000..fe36789
--- /dev/null
+++ b/testdata/scad/features/for-tests.scad
@@ -0,0 +1,41 @@
+// Empty
+for();
+// No children
+for(i=2) { }
+
+// Null
+translate([-10,0,0]) for() cylinder(r=4);
+
+// Scalar
+translate([10,0,0]) for(i=3) cylinder(r=i);
+
+// Range
+for(r=[1:5]) translate([r*10-30,10,0]) cylinder(r=r);
+
+// Reverse
+for(r=[5:1]) translate([r*10-30,20,0]) cylinder(r=r);
+
+// Step
+for(r=[1:2:6]) translate([r*10-30,30,0]) difference() {cylinder(r=r, center=true); cylinder(r=r/2, h=2, center=true);}
+
+// Fractional step
+for(r=[1.5:0.2:2.5]) translate([r*10-30,30,0]) cube([1, 4*r, 2], center=true);
+
+// Negative range, negative step
+for(r=[5:-1:1]) translate([r*10-60,40,0]) cylinder(r=r);
+
+// Negative range, positive step
+for(r=[5:1:1]) translate([r*10-30,40,0]) cylinder(r=r);
+
+// Zero step
+
+for(r=[1:0:5]) translate([r*10+60,40,0]) cylinder(r=r);
+
+// Negative step
+for(r=[1:-1:5]) translate([r*10-30,50,0]) cylinder(r=r);
+
+// Illegal step value
+for(r=[1:true:5]) translate([r*10-60,50,0]) cylinder(r=r);
+
+// Vector
+for(r=[1,2,5]) translate([r*10-30,0,0]) cylinder(r=r);
diff --git a/testdata/scad/features/highlight-modifier.scad b/testdata/scad/features/highlight-modifier.scad
new file mode 100644
index 0000000..1156a88
--- /dev/null
+++ b/testdata/scad/features/highlight-modifier.scad
@@ -0,0 +1,4 @@
+difference() {
+ sphere(r=10);
+ #cylinder(h=30, r=6, center=true);
+}
diff --git a/testdata/scad/features/hull2-tests.scad b/testdata/scad/features/hull2-tests.scad
new file mode 100644
index 0000000..3bea3c5
--- /dev/null
+++ b/testdata/scad/features/hull2-tests.scad
@@ -0,0 +1,30 @@
+module convex2dSimple() {
+ hull() {
+ translate([15,10]) circle(10);
+ circle(10);
+ }
+}
+
+module concave2dSimple() {
+ hull() {
+ translate([15,10]) square(2);
+ translate([15,0]) square(2);
+ square(2);
+ }
+}
+
+// Works correctly
+module convex2dHole() {
+ hull() {
+ translate([15,10,0]) circle(10);
+ difference() {
+ circle(10);
+ circle(5);
+ }
+ }
+}
+
+
+convex2dHole();
+translate([40,0,0]) convex2dSimple();
+translate([0,-20,0]) concave2dSimple();
diff --git a/testdata/scad/convex_hull.scad b/testdata/scad/features/hull3-tests.scad
index 3114ac5..2bd7d73 100644
--- a/testdata/scad/convex_hull.scad
+++ b/testdata/scad/features/hull3-tests.scad
@@ -1,22 +1,3 @@
-// Works correctly
-module convex2dSimple() {
- hull() {
- translate([15,10]) circle(10);
- circle(10);
- }
-}
-
-// Works correctly
-module convex2dHole() {
- hull() {
- translate([15,10,0]) circle(10);
- difference() {
- circle(10);
- circle(5);
- }
- }
-}
-
// 3d not currently implemented
module convex3dSimple() {
hull() {
@@ -36,8 +17,11 @@ module convex3dHole() {
}
}
-
-convex2dHole();
-translate([40,0,0]) convex2dSimple();
translate([0,40,0]) convex3dHole();
translate([40,40,0]) convex3dSimple();
+
+// Empty
+hull();
+// No children
+hull() { }
+
diff --git a/testdata/scad/import.stl b/testdata/scad/features/import.stl
index c8dc5db..c8dc5db 100644
--- a/testdata/scad/import.stl
+++ b/testdata/scad/features/import.stl
diff --git a/testdata/scad/features/import_dxf-tests.scad b/testdata/scad/features/import_dxf-tests.scad
new file mode 100644
index 0000000..814a46c
--- /dev/null
+++ b/testdata/scad/features/import_dxf-tests.scad
@@ -0,0 +1,9 @@
+import_dxf();
+translate([-210,0,0]) import_dxf(file="../../dxf/polygons.dxf");
+translate([-210,0,0]) import_dxf(file="../../dxf/polygons.dxf", origin=[0,110]);
+translate([-210,0,0]) import_dxf(file="../../dxf/polygons.dxf", origin=[110,110], scale=0.5);
+import_dxf(file="../../dxf/multiple-layers.dxf");
+translate([-200,200,0]) import_dxf(file="../../dxf/multiple-layers.dxf", layer="0");
+translate([0,200,0]) import_dxf(file="../../dxf/multiple-layers.dxf", layer="0");
+translate([200,200,0]) import_dxf(file="../../dxf/multiple-layers.dxf", layer="noname");
+translate([0,200,0]) import_dxf(file="../../dxf/multiple-layers.dxf", layer="Layer with a pretty long name including \\ \"special\" /'\\\\ characters");
diff --git a/testdata/scad/import_stl-tests.scad b/testdata/scad/features/import_stl-tests.scad
index b634d12..b634d12 100644
--- a/testdata/scad/import_stl-tests.scad
+++ b/testdata/scad/features/import_stl-tests.scad
diff --git a/testdata/scad/features/include test6.scad b/testdata/scad/features/include test6.scad
new file mode 100644
index 0000000..0d96b26
--- /dev/null
+++ b/testdata/scad/features/include test6.scad
@@ -0,0 +1,7 @@
+module test6()
+{
+ difference() {
+ cube(center=true);
+ cylinder(r=0.4, h=2, center=true);
+ }
+}
diff --git a/testdata/scad/features/include-test5.scad b/testdata/scad/features/include-test5.scad
new file mode 100644
index 0000000..e4393cb
--- /dev/null
+++ b/testdata/scad/features/include-test5.scad
@@ -0,0 +1,4 @@
+module test5()
+{
+ sphere(r=0.5, $fn=8);
+}
diff --git a/testdata/scad/include-test.scad b/testdata/scad/features/include-tests.scad
index 5db02d7..36c04ca 100644
--- a/testdata/scad/include-test.scad
+++ b/testdata/scad/features/include-tests.scad
@@ -28,13 +28,13 @@ include <>
module test1()
{
test2();
- test3();
- test4();
- test5();
- test6();
+ translate([2,0,0]) test3();
+ translate([2,-2,0]) test4();
+ translate([-2,0,0]) test5();
+ translate([-2,-2,0]) test6();
//Just to give a top level object
- sphere(1);
+ translate([0,-2,0]) sphere(0.7, $fn=16);
}
test1();
diff --git a/testdata/scad/features/intersection-tests.scad b/testdata/scad/features/intersection-tests.scad
new file mode 100644
index 0000000..e53f3c9
--- /dev/null
+++ b/testdata/scad/features/intersection-tests.scad
@@ -0,0 +1,41 @@
+// Empty
+intersection();
+
+// No children
+intersection() { }
+
+intersection() {
+ sphere(r=5);
+ translate([0,0,3]) cube([4,4,6], center=true);
+}
+
+translate([0,12,0]) intersection() {
+ cube([10,10,10], center=true);
+ cylinder(r=4, h=12, center=true);
+}
+
+translate([12,0,0]) intersection() {
+ cube([10,10,10], center=true);
+ cylinder(r=4, h=12, center=true);
+ rotate([0,90,0]) cylinder(r=4, h=12, center=true);
+}
+
+translate([12,12,0]) intersection() {
+ cube([10,10,10], center=true);
+ translate([0,0,7]) cylinder(r=4, h=4, center=true);
+}
+
+translate([24,0,0]) intersection() {
+ cube([10,10,10], center=true);
+ translate([0,0,6.99]) cylinder(r=4, h=4, center=true);
+}
+
+translate([-12,0,0]) intersection() {
+ cube([10,10,10], center=true);
+ translate([0,-10,-10]) cube([10,10,10], center=true);
+}
+
+translate([-12,12,0]) intersection() {
+ cube([10,10,10], center=true);
+ translate([0,-9.99,-9.99]) cube([10,10,10], center=true);
+}
diff --git a/testdata/scad/features/intersection_for-tests.scad b/testdata/scad/features/intersection_for-tests.scad
new file mode 100644
index 0000000..6ab1a89
--- /dev/null
+++ b/testdata/scad/features/intersection_for-tests.scad
@@ -0,0 +1,10 @@
+// Empty
+intersection_for();
+// No children
+intersection_for(i=1) { }
+
+intersection_for(i = [[0, 0, 0],
+ [10, 20, 300],
+ [200, 40, 57],
+ [20, 88, 57]])
+ rotate(i) cube([100, 20, 20], center = true);
diff --git a/testdata/scad/features/linear_extrude-tests.scad b/testdata/scad/features/linear_extrude-tests.scad
new file mode 100644
index 0000000..67de8e6
--- /dev/null
+++ b/testdata/scad/features/linear_extrude-tests.scad
@@ -0,0 +1,13 @@
+// Empty
+rotate_extrude();
+// No children
+rotate_extrude() { }
+// 3D child
+rotate_extrude() { cube(); }
+
+linear_extrude(height=10) square([10,10]);
+translate([19,5,0]) linear_extrude(height=10, center=true) difference() {circle(5); circle(3);}
+translate([31.5,2.5,0]) linear_extrude(height=10, twist=-45) polygon(points = [[-5,-2.5], [5,-2.5], [0,2.5]]);
+
+translate([0,20,0]) linear_extrude(height=20, twist=45, slices=2) square([10,10]);
+translate([19,20,0]) linear_extrude(height=20, twist=45, slices=10) square([10,10]);
diff --git a/testdata/scad/features/minkowski2-tests.scad b/testdata/scad/features/minkowski2-tests.scad
new file mode 100644
index 0000000..08f3d98
--- /dev/null
+++ b/testdata/scad/features/minkowski2-tests.scad
@@ -0,0 +1,31 @@
+module roundedBox2dSimple() {
+ minkowski() {
+ square([10,10]);
+ circle(r=5);
+ }
+}
+
+module roundedBox2dCut() {
+ minkowski() {
+ difference() {
+ square([10,10]);
+ square([5,5]);
+ }
+ circle(r=5);
+ }
+}
+
+// Not quite correct, result does not contain a hole, since the impl currently returns the outer boundary of the polygon_with_holes.
+module roundedBox2dHole() {
+ minkowski() {
+ difference() {
+ square([10,10], center=true);
+ square([8,8], center=true);
+ }
+ circle(r=2);
+ }
+}
+
+translate([-20,5,0]) roundedBox2dHole();
+translate([0,0,0]) roundedBox2dCut();
+translate([25,0,0]) roundedBox2dSimple();
diff --git a/testdata/scad/features/minkowski3-tests.scad b/testdata/scad/features/minkowski3-tests.scad
new file mode 100644
index 0000000..0451b57
--- /dev/null
+++ b/testdata/scad/features/minkowski3-tests.scad
@@ -0,0 +1,35 @@
+module roundedBox3dSimple() {
+ minkowski() {
+ cube([10,10,5]);
+ cylinder(r=5, h=5);
+ }
+}
+
+module roundedBox3dCut() {
+ minkowski() {
+ difference() {
+ cube([10,10,5]);
+ cube([5,5,5]);
+ }
+ cylinder(r=5, h=5);
+ }
+}
+
+module roundedBox3dHole() {
+ minkowski() {
+ difference() {
+ cube([10,10,5], center=true);
+ cube([8,8,10], center=true);
+ }
+ cylinder(r=2);
+ }
+}
+
+translate([-20,30,0]) roundedBox3dHole();
+translate([0,25,0]) roundedBox3dCut();
+translate([25,25,0]) roundedBox3dSimple();
+
+// Empty
+minkowski();
+// No children
+minkowski() { }
diff --git a/testdata/scad/null-polygons.dxf b/testdata/scad/features/null-polygons.dxf
index 390e42e..390e42e 100644
--- a/testdata/scad/null-polygons.dxf
+++ b/testdata/scad/features/null-polygons.dxf
diff --git a/testdata/scad/features/null-polygons.scad b/testdata/scad/features/null-polygons.scad
new file mode 100644
index 0000000..4849c15
--- /dev/null
+++ b/testdata/scad/features/null-polygons.scad
@@ -0,0 +1,2 @@
+linear_extrude() import_dxf("null-polygons.dxf");
+linear_extrude("null-polygons.dxf");
diff --git a/testdata/scad/features/polygon-tests.scad b/testdata/scad/features/polygon-tests.scad
new file mode 100644
index 0000000..ab3a345
--- /dev/null
+++ b/testdata/scad/features/polygon-tests.scad
@@ -0,0 +1,17 @@
+polygon();
+polygon([]);
+polygon([[],[]]);
+polygon([[[]]]);
+translate([2,0,0]) polygon([[0,0], [1,0], [1,1]]);
+translate([0,2,0]) polygon([[0,0]]);
+translate([2,2,0]) polygon([[0,0],[1,1]]);
+translate([2,2,0]) polygon([[0,0],[1,1],[2,2]]);
+translate([0,-2,0]) polygon(points=[[0,0], [1,0], [1,1], [0,1]]);
+translate([0,-4,0]) polygon(points=[[0,0], [1,0], [1,1], [0,1]], paths=[]);
+translate([2,-2,0]) polygon([[0,0], [1,0], [0.8,0.5], [1,1], [0,1]]);
+
+points = [[0,0], [0.5,-0.2], [1,0], [1.2,0.5], [1,1], [0.5,1.2], [0,1], [-0.2,0.5]];
+translate([-2,0,0]) polygon(points);
+translate([-2,-2,0]) polygon(points=points, paths=[[0,1,2,3], [4,5,6,7]]);
+
+// FIXME: convexity
diff --git a/testdata/scad/projection-tests.scad b/testdata/scad/features/projection-tests.scad
index 619aa01..e1b05c7 100644
--- a/testdata/scad/projection-tests.scad
+++ b/testdata/scad/features/projection-tests.scad
@@ -1,3 +1,10 @@
+// Empty
+projection();
+// No children
+projection() { }
+// 2D child
+projection(cut=true) { square(); }
+
linear_extrude(height=20) projection(cut=false) sphere(r=10);
translate([22,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,9]) sphere(r=10);
translate([44,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,7]) sphere(r=10);
diff --git a/testdata/scad/features/render-tests.scad b/testdata/scad/features/render-tests.scad
new file mode 100644
index 0000000..5fcb026
--- /dev/null
+++ b/testdata/scad/features/render-tests.scad
@@ -0,0 +1,36 @@
+module edgeprofile()
+{
+ difference() {
+ cube([20, 20, 150], center = true);
+ translate([-10, -10, 0]) cylinder(h = 80, r = 10, center = true);
+ }
+}
+
+module rendered_edgeprofile()
+{
+ render(convexity = 2) edgeprofile();
+}
+
+module mycube() {
+ difference() {
+ cube(100, center = true);
+ translate([ -50, -50, 0 ]) rotate(180, [0, 0, 1]) edgeprofile();
+ translate([ 50, -50, 0 ]) rotate(270, [0, 0, 1]) edgeprofile();
+ }
+}
+
+module rendered_mycube() {
+ difference() {
+ cube(100, center = true);
+ translate([ -50, -50, 0 ]) rotate(180, [0, 0, 1]) rendered_edgeprofile();
+ translate([ 50, -50, 0 ]) rotate(270, [0, 0, 1]) rendered_edgeprofile();
+ }
+}
+
+// Empty
+render();
+// No children
+render() { }
+
+mycube();
+translate([110,0,0]) rendered_mycube();
diff --git a/testdata/scad/features/root-modifier.scad b/testdata/scad/features/root-modifier.scad
new file mode 100644
index 0000000..6c36433
--- /dev/null
+++ b/testdata/scad/features/root-modifier.scad
@@ -0,0 +1,4 @@
+difference() {
+ sphere(r=10);
+ !cylinder(h=30, r=6, center=true);
+}
diff --git a/testdata/scad/features/rotate_extrude-tests.scad b/testdata/scad/features/rotate_extrude-tests.scad
new file mode 100644
index 0000000..347bc78
--- /dev/null
+++ b/testdata/scad/features/rotate_extrude-tests.scad
@@ -0,0 +1,30 @@
+// Empty
+rotate_extrude();
+// No children
+rotate_extrude() { }
+// 3D child
+rotate_extrude() { cube(); }
+
+// Normal
+rotate_extrude() translate([20,0,0]) circle(r=10);
+
+// Sweep of polygon with hole
+translate([50,-20,0]) {
+ difference() {
+ rotate_extrude() translate([20,0,0]) difference() {
+ circle(r=10); circle(r=8);
+ }
+ translate([-50,0,0]) cube([100,100,100], center=true);
+ }
+}
+
+// Alternative, difference between two solid sweeps
+translate([50,50,0]) {
+ difference() {
+ difference() {
+ rotate_extrude() translate([20,0,0]) circle(r=10);
+ rotate_extrude() translate([20,0,0]) circle(r=8);
+ }
+ translate([-50,0,0]) cube([100,100,100], center=true);
+ }
+}
diff --git a/testdata/scad/features/sphere-tests.scad b/testdata/scad/features/sphere-tests.scad
new file mode 100644
index 0000000..e666c1b
--- /dev/null
+++ b/testdata/scad/features/sphere-tests.scad
@@ -0,0 +1,10 @@
+sphere();
+translate([2,0,0]) sphere(r=0);
+translate([0,11,0]) sphere(5);
+translate([0,-11,0]) sphere(r=5);
+translate([11,-11,0]) sphere(5, $fn=5);
+translate([11,0,0]) sphere(5, $fn=10);
+translate([11,11,0]) sphere(5, $fn=15);
+translate([22,-11, 0]) sphere(5, $fa=20, $fs=0.3);
+translate([22, 0, 0]) sphere(5, $fa=30, $fs=0.3);
+translate([22, 11, 0]) sphere(5, $fa=40, $fs=0.3);
diff --git a/testdata/scad/features/square-tests.scad b/testdata/scad/features/square-tests.scad
new file mode 100644
index 0000000..57ec1c4
--- /dev/null
+++ b/testdata/scad/features/square-tests.scad
@@ -0,0 +1,8 @@
+square();
+translate([2,0,0]) square([1,1], true);
+translate([4,0,0]) square(size=[1,1], center=true);
+translate([6,0,0]) square([0,0], true);
+translate([0,2,0]) square([1,0], true);
+translate([2,2,0]) square([0,1], true);
+translate([4,2,0]) square([1,2], true);
+translate([6,2,0]) square([2,1], true);
diff --git a/testdata/scad/string-test.scad b/testdata/scad/features/string-test.scad
index 5ec4cfb..5ec4cfb 100644
--- a/testdata/scad/string-test.scad
+++ b/testdata/scad/features/string-test.scad
diff --git a/testdata/scad/features/sub1/sub2/sub3/include-test4.scad b/testdata/scad/features/sub1/sub2/sub3/include-test4.scad
new file mode 100644
index 0000000..c13368c
--- /dev/null
+++ b/testdata/scad/features/sub1/sub2/sub3/include-test4.scad
@@ -0,0 +1,4 @@
+module test4()
+{
+ cylinder(r=0.5, $fn=10, center=true);
+}
diff --git a/testdata/scad/sub1/sub2/sub3/sub4/include-test2.scad b/testdata/scad/features/sub1/sub2/sub3/sub4/include-test2.scad
index 9f4c963..140c4ed 100644
--- a/testdata/scad/sub1/sub2/sub3/sub4/include-test2.scad
+++ b/testdata/scad/features/sub1/sub2/sub3/sub4/include-test2.scad
@@ -6,5 +6,5 @@ include <../include-test4.scad>
module test2 ()
{
- echo("included from include-test2.scad");
+ cube(center=true);
}
diff --git a/testdata/scad/features/sub1/sub2/sub3/sub4/include-test3.scad b/testdata/scad/features/sub1/sub2/sub3/sub4/include-test3.scad
new file mode 100644
index 0000000..6e3537e
--- /dev/null
+++ b/testdata/scad/features/sub1/sub2/sub3/sub4/include-test3.scad
@@ -0,0 +1,4 @@
+module test3()
+{
+ cylinder(r1=0.7, r2=0.2, center=true);
+}
diff --git a/testdata/scad/features/surface-tests.scad b/testdata/scad/features/surface-tests.scad
new file mode 100644
index 0000000..5a67293
--- /dev/null
+++ b/testdata/scad/features/surface-tests.scad
@@ -0,0 +1,2 @@
+surface();
+surface("surface.dat", center=true);
diff --git a/testdata/scad/surface.dat b/testdata/scad/features/surface.dat
index 21d10af..21d10af 100644
--- a/testdata/scad/surface.dat
+++ b/testdata/scad/features/surface.dat
diff --git a/testdata/scad/testcolornames.scad b/testdata/scad/features/testcolornames.scad
index b9ad334..b9ad334 100644
--- a/testdata/scad/testcolornames.scad
+++ b/testdata/scad/features/testcolornames.scad
diff --git a/testdata/scad/features/transform-tests.scad b/testdata/scad/features/transform-tests.scad
new file mode 100644
index 0000000..a591695
--- /dev/null
+++ b/testdata/scad/features/transform-tests.scad
@@ -0,0 +1,18 @@
+module mycyl() {
+ cylinder(r1=10, r2=0, h=20);
+}
+
+translate([25,0,0]) scale([1,2,0.5]) mycyl();
+translate([20,-30,0]) scale(0.5) mycyl();
+translate([0,-20,0]) rotate([90,0,0]) mycyl();
+rotate(v=[-1,0,0], a=45) mycyl();
+multmatrix([[1,0,0,-25],
+ [0,1,0,0],
+ [0,0,1,0],
+ [0,0,0,1]]) mycyl();
+multmatrix([[1,0.4,0.1,-25],
+ [0.4,0.8,0,-25],
+ [0.2,0.2,0.5,0],
+ [0,0,0,1]]) mycyl();
+
+//FIXME: mirror() \ No newline at end of file
diff --git a/testdata/scad/features/union-tests.scad b/testdata/scad/features/union-tests.scad
new file mode 100644
index 0000000..718aa8c
--- /dev/null
+++ b/testdata/scad/features/union-tests.scad
@@ -0,0 +1,29 @@
+translate([-12,0,0]) union() {
+ cube([10,10,10]);
+ translate([4,4,8]) cube([2,2,10]);
+}
+
+union() {
+ cube([10,10,10]);
+ translate([0,0,10]) cube([2,2,10]);
+}
+
+translate([12,0,0]) union() {
+ cube([10,10,10]);
+ translate([0,0,11]) cube([2,2,10]);
+}
+
+translate([24,0,0]) union() {
+ cube([10,10,10]);
+ translate([4,4,10]) cube([2,2,10]);
+}
+
+translate([-12,12,0]) union() {
+ cube([10,10,10]);
+ translate([-2,10,10]) cube([2,2,10]);
+}
+
+translate([0,12,0]) union() {
+ cube([10,10,10]);
+ translate([0,10,10]) cube([2,2,10]);
+}
diff --git a/testdata/scad/include test6.scad b/testdata/scad/include test6.scad
deleted file mode 100644
index 7a79456..0000000
--- a/testdata/scad/include test6.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-module test6()
-{
- echo("included from include test6.scad");
-}
diff --git a/testdata/scad/include-test5.scad b/testdata/scad/include-test5.scad
deleted file mode 100644
index 4f6e656..0000000
--- a/testdata/scad/include-test5.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-module test5()
-{
- echo("included from include-test5.scad");
-}
diff --git a/testdata/scad/intersection-tests.scad b/testdata/scad/intersection-tests.scad
deleted file mode 100644
index 4101b03..0000000
--- a/testdata/scad/intersection-tests.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-intersection() {
- sphere(3);
- cube([3,3,8], center=true);
-}
diff --git a/testdata/scad/linear_extrude-tests.scad b/testdata/scad/linear_extrude-tests.scad
deleted file mode 100644
index af050fb..0000000
--- a/testdata/scad/linear_extrude-tests.scad
+++ /dev/null
@@ -1,7 +0,0 @@
-linear_extrude(height=10) square([10,10]);
-translate([19,5,0]) linear_extrude(height=10) circle(5);
-translate([31.5,2.5,0]) linear_extrude(height=10) polygon(points = [[-5,-2.5], [5,-2.5], [0,2.5]]);
-
-translate([0,-12,0]) linear_extrude(height=20, twist=45) square([10,10]);
-translate([19,-7,0]) linear_extrude(height=20, twist=90) circle(5);
-translate([31.5,-9.5,0]) linear_extrude(height=20, twist=180) polygon(points = [[-5,-2.5], [5,-2.5], [0,2.5]]);
diff --git a/testdata/scad/minimal/allfunctions.scad b/testdata/scad/minimal/allfunctions.scad
new file mode 100644
index 0000000..e58bd07
--- /dev/null
+++ b/testdata/scad/minimal/allfunctions.scad
@@ -0,0 +1,24 @@
+a = abs();
+a = sign();
+a = rands();
+a = min();
+a = max();
+a = sin();
+a = cos();
+a = asin();
+a = acos();
+a = tan();
+a = atan();
+a = atan2();
+a = round();
+a = ceil();
+a = floor();
+a = pow();
+a = sqrt();
+a = exp();
+a = log();
+a = ln();
+a = str();
+a = lookup();
+a = dxf_dim();
+a = dxf_cross();
diff --git a/testdata/scad/allmodules.scad b/testdata/scad/minimal/allmodules.scad
index 9d6635c..4395fd7 100644
--- a/testdata/scad/allmodules.scad
+++ b/testdata/scad/minimal/allmodules.scad
@@ -1,6 +1,7 @@
minkowski();
glide();
subdiv();
+hull();
child();
echo();
assign();
@@ -14,6 +15,7 @@ dxf_linear_extrude();
linear_extrude();
dxf_rotate_extrude();
rotate_extrude();
+import();
import_stl();
import_off();
import_dxf();
diff --git a/testdata/scad/assign.scad b/testdata/scad/minimal/assign.scad
index e4dba58..e4dba58 100644
--- a/testdata/scad/assign.scad
+++ b/testdata/scad/minimal/assign.scad
diff --git a/testdata/scad/child.scad b/testdata/scad/minimal/child.scad
index ba69caf..ba69caf 100644
--- a/testdata/scad/child.scad
+++ b/testdata/scad/minimal/child.scad
diff --git a/testdata/scad/circle.scad b/testdata/scad/minimal/circle.scad
index c702f98..c702f98 100644
--- a/testdata/scad/circle.scad
+++ b/testdata/scad/minimal/circle.scad
diff --git a/testdata/scad/color.scad b/testdata/scad/minimal/color.scad
index b0ae89c..b0ae89c 100644
--- a/testdata/scad/color.scad
+++ b/testdata/scad/minimal/color.scad
diff --git a/testdata/scad/cube.scad b/testdata/scad/minimal/cube.scad
index 406bf16..406bf16 100644
--- a/testdata/scad/cube.scad
+++ b/testdata/scad/minimal/cube.scad
diff --git a/testdata/scad/cylinder.scad b/testdata/scad/minimal/cylinder.scad
index 91c2c30..91c2c30 100644
--- a/testdata/scad/cylinder.scad
+++ b/testdata/scad/minimal/cylinder.scad
diff --git a/testdata/scad/difference.scad b/testdata/scad/minimal/difference.scad
index c3fd0fe..c3fd0fe 100644
--- a/testdata/scad/difference.scad
+++ b/testdata/scad/minimal/difference.scad
diff --git a/testdata/scad/dxf_linear_extrude.scad b/testdata/scad/minimal/dxf_linear_extrude.scad
index 06d6f33..06d6f33 100644
--- a/testdata/scad/dxf_linear_extrude.scad
+++ b/testdata/scad/minimal/dxf_linear_extrude.scad
diff --git a/testdata/scad/dxf_rotate_extrude.scad b/testdata/scad/minimal/dxf_rotate_extrude.scad
index dee7f49..dee7f49 100644
--- a/testdata/scad/dxf_rotate_extrude.scad
+++ b/testdata/scad/minimal/dxf_rotate_extrude.scad
diff --git a/testdata/scad/echo.scad b/testdata/scad/minimal/echo.scad
index 34fc70f..34fc70f 100644
--- a/testdata/scad/echo.scad
+++ b/testdata/scad/minimal/echo.scad
diff --git a/testdata/scad/for.scad b/testdata/scad/minimal/for.scad
index 62356c0..62356c0 100644
--- a/testdata/scad/for.scad
+++ b/testdata/scad/minimal/for.scad
diff --git a/testdata/scad/glide.scad b/testdata/scad/minimal/glide.scad
index 9a5f69d..9a5f69d 100644
--- a/testdata/scad/glide.scad
+++ b/testdata/scad/minimal/glide.scad
diff --git a/testdata/scad/group.scad b/testdata/scad/minimal/group.scad
index 0a04719..0a04719 100644
--- a/testdata/scad/group.scad
+++ b/testdata/scad/minimal/group.scad
diff --git a/testdata/scad/minimal/hull.scad b/testdata/scad/minimal/hull.scad
new file mode 100644
index 0000000..096b0b0
--- /dev/null
+++ b/testdata/scad/minimal/hull.scad
@@ -0,0 +1 @@
+hull();
diff --git a/testdata/scad/if.scad b/testdata/scad/minimal/if.scad
index 9e5c706..9e5c706 100644
--- a/testdata/scad/if.scad
+++ b/testdata/scad/minimal/if.scad
diff --git a/testdata/scad/minimal/import.scad b/testdata/scad/minimal/import.scad
new file mode 100644
index 0000000..6f375fe
--- /dev/null
+++ b/testdata/scad/minimal/import.scad
@@ -0,0 +1 @@
+import();
diff --git a/testdata/scad/import_dxf.scad b/testdata/scad/minimal/import_dxf.scad
index b8b8fd0..b8b8fd0 100644
--- a/testdata/scad/import_dxf.scad
+++ b/testdata/scad/minimal/import_dxf.scad
diff --git a/testdata/scad/import_off.scad b/testdata/scad/minimal/import_off.scad
index 353597f..353597f 100644
--- a/testdata/scad/import_off.scad
+++ b/testdata/scad/minimal/import_off.scad
diff --git a/testdata/scad/import_stl.scad b/testdata/scad/minimal/import_stl.scad
index 416ca6a..416ca6a 100644
--- a/testdata/scad/import_stl.scad
+++ b/testdata/scad/minimal/import_stl.scad
diff --git a/testdata/scad/intersection.scad b/testdata/scad/minimal/intersection.scad
index 8340c00..8340c00 100644
--- a/testdata/scad/intersection.scad
+++ b/testdata/scad/minimal/intersection.scad
diff --git a/testdata/scad/intersection_for.scad b/testdata/scad/minimal/intersection_for.scad
index 7b08d61..7b08d61 100644
--- a/testdata/scad/intersection_for.scad
+++ b/testdata/scad/minimal/intersection_for.scad
diff --git a/testdata/scad/linear_extrude.scad b/testdata/scad/minimal/linear_extrude.scad
index a621959..a621959 100644
--- a/testdata/scad/linear_extrude.scad
+++ b/testdata/scad/minimal/linear_extrude.scad
diff --git a/testdata/scad/minimal/minkowski.scad b/testdata/scad/minimal/minkowski.scad
new file mode 100644
index 0000000..26cd972
--- /dev/null
+++ b/testdata/scad/minimal/minkowski.scad
@@ -0,0 +1 @@
+minkowski();
diff --git a/testdata/scad/mirror.scad b/testdata/scad/minimal/mirror.scad
index 8d74882..8d74882 100644
--- a/testdata/scad/mirror.scad
+++ b/testdata/scad/minimal/mirror.scad
diff --git a/testdata/scad/multmatrix.scad b/testdata/scad/minimal/multmatrix.scad
index 7477fa2..7477fa2 100644
--- a/testdata/scad/multmatrix.scad
+++ b/testdata/scad/minimal/multmatrix.scad
diff --git a/testdata/scad/polygon.scad b/testdata/scad/minimal/polygon.scad
index 6a1f288..6a1f288 100644
--- a/testdata/scad/polygon.scad
+++ b/testdata/scad/minimal/polygon.scad
diff --git a/testdata/scad/polyhedron.scad b/testdata/scad/minimal/polyhedron.scad
index 941851f..941851f 100644
--- a/testdata/scad/polyhedron.scad
+++ b/testdata/scad/minimal/polyhedron.scad
diff --git a/testdata/scad/projection.scad b/testdata/scad/minimal/projection.scad
index 43ea0a2..43ea0a2 100644
--- a/testdata/scad/projection.scad
+++ b/testdata/scad/minimal/projection.scad
diff --git a/testdata/scad/render.scad b/testdata/scad/minimal/render.scad
index e035a6f..e035a6f 100644
--- a/testdata/scad/render.scad
+++ b/testdata/scad/minimal/render.scad
diff --git a/testdata/scad/rotate.scad b/testdata/scad/minimal/rotate.scad
index e4acc9c..e4acc9c 100644
--- a/testdata/scad/rotate.scad
+++ b/testdata/scad/minimal/rotate.scad
diff --git a/testdata/scad/rotate_extrude.scad b/testdata/scad/minimal/rotate_extrude.scad
index d11484c..d11484c 100644
--- a/testdata/scad/rotate_extrude.scad
+++ b/testdata/scad/minimal/rotate_extrude.scad
diff --git a/testdata/scad/scale.scad b/testdata/scad/minimal/scale.scad
index 5e9baab..5e9baab 100644
--- a/testdata/scad/scale.scad
+++ b/testdata/scad/minimal/scale.scad
diff --git a/testdata/scad/sphere.scad b/testdata/scad/minimal/sphere.scad
index 8e7ddc1..8e7ddc1 100644
--- a/testdata/scad/sphere.scad
+++ b/testdata/scad/minimal/sphere.scad
diff --git a/testdata/scad/square.scad b/testdata/scad/minimal/square.scad
index 5e44b7d..5e44b7d 100644
--- a/testdata/scad/square.scad
+++ b/testdata/scad/minimal/square.scad
diff --git a/testdata/scad/subdiv.scad b/testdata/scad/minimal/subdiv.scad
index 4c1eb74..4c1eb74 100644
--- a/testdata/scad/subdiv.scad
+++ b/testdata/scad/minimal/subdiv.scad
diff --git a/testdata/scad/surface.scad b/testdata/scad/minimal/surface.scad
index c0b213a..c0b213a 100644
--- a/testdata/scad/surface.scad
+++ b/testdata/scad/minimal/surface.scad
diff --git a/testdata/scad/transform-insert.dxf b/testdata/scad/minimal/transform-insert.dxf
index 40064c2..40064c2 100644
--- a/testdata/scad/transform-insert.dxf
+++ b/testdata/scad/minimal/transform-insert.dxf
diff --git a/testdata/scad/transform-insert.scad b/testdata/scad/minimal/transform-insert.scad
index 1237bb2..1237bb2 100644
--- a/testdata/scad/transform-insert.scad
+++ b/testdata/scad/minimal/transform-insert.scad
diff --git a/testdata/scad/translate.scad b/testdata/scad/minimal/translate.scad
index c7c0b0b..c7c0b0b 100644
--- a/testdata/scad/translate.scad
+++ b/testdata/scad/minimal/translate.scad
diff --git a/testdata/scad/union.scad b/testdata/scad/minimal/union.scad
index e3fa0d5..e3fa0d5 100644
--- a/testdata/scad/union.scad
+++ b/testdata/scad/minimal/union.scad
diff --git a/testdata/scad/minkowski.scad b/testdata/scad/minkowski.scad
deleted file mode 100644
index 6d0dade..0000000
--- a/testdata/scad/minkowski.scad
+++ /dev/null
@@ -1,67 +0,0 @@
-
-// Rounded box using 3d minkowski
-module roundedBox3dSimple() {
- minkowski() {
- cube([10,10,5]);
- cylinder(r=5, h=5);
- }
-}
-
-// Currently segfaults
-module roundedBox3dCut() {
- minkowski() {
- difference() {
- cube([10,10,5]);
- cube([5,5,5]);
- }
- cylinder(r=5, h=5);
- }
-}
-
-// Currently segfaults
-module roundedBox3dHole() {
- minkowski() {
- difference() {
- cube([10,10,5]);
- translate([2,2,-2]) cube([6,6,10]);
- }
- cylinder(r=2);
- }
-}
-
-// Works correctly
-module roundedBox2dSimple() {
- minkowski() {
- square([10,10]);
- circle(r=5);
- }
-}
-
-// Works correctly
-module roundedBox2dCut() {
- minkowski() {
- difference() {
- square([10,10]);
- square([5,5]);
- }
- circle(r=5);
- }
-}
-
-// Not quite correct, result does not contain a hole, since the impl currently returns the outer boundary of the polygon_with_holes.
-module roundedBox2dHole() {
- minkowski() {
- difference() {
- square([10,10]);
- translate([2,2]) square([6,6]);
- }
- circle(r=2);
- }
-}
-
-translate([-25,0,0]) roundedBox2dHole();
-translate([0,0,0]) roundedBox2dCut();
-translate([25,0,0]) roundedBox2dSimple();
-translate([-25,25,0]) roundedBox3dHole();
-translate([0,25,0]) roundedBox3dCut();
-translate([25,25,0]) roundedBox3dSimple();
diff --git a/testdata/scad/null-polygons.scad b/testdata/scad/null-polygons.scad
deleted file mode 100644
index d945325..0000000
--- a/testdata/scad/null-polygons.scad
+++ /dev/null
@@ -1,2 +0,0 @@
-linear_extrude() import_dxf("null-polygons.dxf"); // doen's crash
-linear_extrude("null-polygons.dxf"); // crashes
diff --git a/testdata/scad/rotate_extrude-tests.scad b/testdata/scad/rotate_extrude-tests.scad
deleted file mode 100644
index 7bbcef0..0000000
--- a/testdata/scad/rotate_extrude-tests.scad
+++ /dev/null
@@ -1,2 +0,0 @@
-rotate_extrude() translate([20,0,0]) circle(r=10);
-
diff --git a/testdata/scad/sphere-tests.scad b/testdata/scad/sphere-tests.scad
deleted file mode 100644
index f87aa26..0000000
--- a/testdata/scad/sphere-tests.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-sphere(5);
-translate([0,12,0]) sphere(5, $fa=5, $fs=0.5);
-translate([12,0,0]) sphere(r=6);
-translate([12,12,0]) sphere(r=6, $fn=5);
diff --git a/testdata/scad/sub1/sub2/sub3/include-test4.scad b/testdata/scad/sub1/sub2/sub3/include-test4.scad
deleted file mode 100644
index 1cb7eab..0000000
--- a/testdata/scad/sub1/sub2/sub3/include-test4.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-module test4()
-{
- echo("included from include-test4.scad");
-}
diff --git a/testdata/scad/sub1/sub2/sub3/sub4/include-test3.scad b/testdata/scad/sub1/sub2/sub3/sub4/include-test3.scad
deleted file mode 100644
index 2f67e93..0000000
--- a/testdata/scad/sub1/sub2/sub3/sub4/include-test3.scad
+++ /dev/null
@@ -1,4 +0,0 @@
-module test3()
-{
- echo("included from include-test3.scad");
-}
diff --git a/testdata/scad/surface-tests.scad b/testdata/scad/surface-tests.scad
deleted file mode 100644
index 32072fa..0000000
--- a/testdata/scad/surface-tests.scad
+++ /dev/null
@@ -1 +0,0 @@
-surface("surface.dat");
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..f7fd320
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,246 @@
+cmake_minimum_required(VERSION 2.8)
+project(tests)
+
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
+
+# Build debug build as default
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Debug)
+endif()
+
+#
+# Build test apps
+#
+
+# Mac OS X
+if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ FIND_LIBRARY(COCOA_LIBRARY Cocoa)
+endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+
+# Qt4
+find_package(OpenGL)
+find_package(Qt4 COMPONENTS QtCore QtGui QtOpenGL REQUIRED)
+include(${QT_USE_FILE})
+
+# Eigen2
+if (NOT EIGEN2_INCLUDE_DIR)
+ find_path(EIGEN2_INCLUDE_DIR
+ Eigen/Core
+ PATHS ENV EIGEN2DIR /opt/local/include/eigen2 /usr/include/eigen2)
+ if (NOT EIGEN2_INCLUDE_DIR)
+ message(FATAL_ERROR "Eigen2 not found")
+ else()
+ message(STATUS "Eigen2 found in " ${EIGEN2_INCLUDE_DIR})
+ endif()
+endif()
+include_directories(${EIGEN2_INCLUDE_DIR})
+
+# OpenCSG
+if (NOT $ENV{OPENCSG_DIR} STREQUAL "")
+ set(OPENCSG_DIR "$ENV{OPENCSG_DIR}")
+elseif (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
+ set(OPENCSG_DIR "$ENV{MACOSX_DEPLOY_DIR}")
+endif()
+if (NOT OPENCSG_INCLUDE_DIR)
+ message(STATUS ${OPENCSG_DIR})
+ find_path(OPENCSG_INCLUDE_DIR
+ opencsg.h
+ PATHS ${OPENCSG_DIR}/include)
+ find_library(OPENCSG_LIBRARY
+ opencsg
+ PATHS ${OPENCSG_DIR}/lib)
+ if (NOT OPENCSG_INCLUDE_DIR OR NOT OPENCSG_LIBRARY)
+ message(FATAL_ERROR "OpenCSG not found")
+ else()
+ message(STATUS "OpenCSG found in " ${OPENCSG_LIBRARY})
+ endif()
+endif()
+include_directories(${OPENCSG_INCLUDE_DIR})
+
+if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
+ set(GLEW_DIR "$ENV{MACOSX_DEPLOY_DIR}")
+endif()
+find_package(GLEW REQUIRED)
+include_directories(${GLEW_INCLUDE_PATH})
+
+# Flex/Bison
+find_package(BISON)
+find_package(FLEX)
+# The COMPILE_FLAGS and forced C++ compiler is just to be compatible with qmake
+FLEX_TARGET(OpenSCADlexer ../src/lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp COMPILE_FLAGS "-Plexer")
+BISON_TARGET(OpenSCADparser ../src/parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser_yacc.c COMPILE_FLAGS "-p parser")
+ADD_FLEX_BISON_DEPENDENCY(OpenSCADlexer OpenSCADparser)
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser_yacc.c PROPERTIES LANGUAGE "CXX")
+
+
+# Internal includes
+include_directories(../src)
+
+add_definitions(-DOPENSCAD_VERSION=test)
+
+set(COMMON_SOURCES
+ ../src/handle_dep.cc
+ ../src/qhash.cc
+ ../src/export.cc
+ ../src/value.cc
+ ../src/expr.cc
+ ../src/func.cc
+ ../src/module.cc
+ ../src/node.cc
+ ../src/context.cc
+ ../src/csgterm.cc
+ ../src/polyset.cc
+ ../src/csgops.cc
+ ../src/transform.cc
+ ../src/color.cc
+ ../src/primitives.cc
+ ../src/projection.cc
+ ../src/cgaladv.cc
+ ../src/surface.cc
+ ../src/control.cc
+ ../src/render.cc
+ ../src/import.cc
+ ../src/dxfdata.cc
+ ../src/dxftess.cc
+ ../src/dxfdim.cc
+ ../src/dxflinextrude.cc
+ ../src/dxfrotextrude.cc
+ ../src/printutils.cc
+ ../src/progress.cc
+ ../src/nodedumper.cc
+ ../src/traverser.cc
+ ../src/PolySetEvaluator.cc
+ ../src/Tree.cc
+ ${FLEX_OpenSCADlexer_OUTPUTS}
+ ${BISON_OpenSCADparser_OUTPUTS})
+
+#
+# dumptest
+#
+add_executable(dumptest dumptest.cc ${COMMON_SOURCES})
+target_link_libraries(dumptest ${QT_LIBRARIES} ${OPENGL_LIBRARY})
+
+#
+# csgtexttest
+#
+add_executable(csgtexttest csgtexttest.cc CSGTextRenderer.cc CSGTextCache.cc ../src/qhash.cc ${COMMON_SOURCES})
+target_link_libraries(csgtexttest ${QT_LIBRARIES} ${OPENGL_LIBRARY})
+
+#
+# csgtermtest
+#
+add_executable(csgtermtest csgtermtest.cc ../src/CSGTermEvaluator.cc ${COMMON_SOURCES})
+target_link_libraries(csgtermtest ${QT_LIBRARIES} ${OPENGL_LIBRARY})
+
+if (NOT $ENV{MACOSX_DEPLOY_DIR} STREQUAL "")
+ set(CGAL_DIR "$ENV{MACOSX_DEPLOY_DIR}/lib/CGAL")
+ set(CMAKE_MODULE_PATH "${CGAL_DIR}")
+endif()
+find_package(CGAL REQUIRED)
+include_directories(${CGAL_INCLUDE_DIRS})
+
+#
+# cgaltest
+#
+add_executable(cgaltest cgaltest.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc ../src/CSGTermEvaluator.cc
+ ../src/CGALEvaluator.cc ../src/PolySetCGALEvaluator.cc ../src/qhash.cc
+ ../src/CGAL_Nef_polyhedron_DxfData.cc ../src/cgaladv_minkowski2.cc ../src/cgaladv_convexhull2.cc
+ ${COMMON_SOURCES})
+set_target_properties(cgaltest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
+target_link_libraries(cgaltest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENGL_LIBRARY})
+
+#
+# cgalpngtest
+#
+add_executable(cgalpngtest cgalpngtest.cc OffscreenView.cc OffscreenContext.mm
+ ../src/CGALRenderer.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc
+ ../src/CSGTermEvaluator.cc ../src/CGALEvaluator.cc
+ ../src/PolySetCGALEvaluator.cc ../src/qhash.cc
+ ../src/CGAL_Nef_polyhedron_DxfData.cc ../src/cgaladv_minkowski2.cc ../src/cgaladv_convexhull2.cc
+ ${COMMON_SOURCES})
+set_target_properties(cgalpngtest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
+target_link_libraries(cgalpngtest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY})
+
+#
+# opencsgtest
+#
+add_executable(opencsgtest opencsgtest.cc OffscreenView.cc OffscreenContext.mm
+ ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc
+ ../src/CSGTermEvaluator.cc ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc
+ ../src/CGALEvaluator.cc ../src/PolySetCGALEvaluator.cc ../src/qhash.cc
+ ../src/CGAL_Nef_polyhedron_DxfData.cc ../src/cgaladv_minkowski2.cc ../src/cgaladv_convexhull2.cc
+ ${COMMON_SOURCES})
+set_target_properties(opencsgtest PROPERTIES COMPILE_FLAGS "-DENABLE_OPENCSG -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
+target_link_libraries(opencsgtest ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENCSG_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY})
+
+#
+# This functions adds cmd-line tests given files.
+# Files are sent as the parameters following TESTSUFFIX
+#
+function(add_cmdline_test TESTCMD TESTSUFFIX)
+ get_filename_component(TESTCMD_NAME ${TESTCMD} NAME_WE)
+ foreach (SCADFILE ${ARGN})
+ get_filename_component(TESTNAME ${SCADFILE} NAME_WE)
+ string(REPLACE " " "_" TESTNAME ${TESTNAME}) # Test names cannot include spaces
+ add_test("${TESTCMD_NAME}_${TESTNAME}" ${tests_SOURCE_DIR}/test_cmdline_tool.py -s ${TESTSUFFIX} ${CMAKE_BINARY_DIR}/${TESTCMD} "${SCADFILE}")
+ endforeach()
+endfunction()
+
+enable_testing()
+
+# Find all scad files
+file(GLOB MINIMAL_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/*.scad)
+file(GLOB FEATURES_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/*.scad)
+file(GLOB BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/*.scad)
+
+# Add dumptest tests to CTest
+add_cmdline_test(dumptest txt ${MINIMAL_FILES})
+# Add csgtexttest tests to CTest
+add_cmdline_test(csgtexttest txt ${MINIMAL_FILES})
+# Add csgtermtest tests to CTest
+add_cmdline_test(csgtermtest txt ${MINIMAL_FILES})
+
+# Add cgaltest tests to CTest
+add_cmdline_test(cgaltest stl ${CGALTEST_FILES})
+
+# Add cgalpngtest tests to CTest
+LIST(APPEND CGALPNGTEST_FILES
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/2d-3d.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/circle-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/square-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/polygon-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/cube-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/sphere-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/cylinder-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/union-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/difference-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/intersection-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/linear_extrude-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/rotate_extrude-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/minkowski2-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/minkowski3-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/hull2-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/surface-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/import_dxf-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/transform-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/color-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/background-modifier.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/highlight-modifier.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/root-modifier.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/disable-modifier.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/for-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/for-nested-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/intersection_for-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/render-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/projection-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/assign-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/include-tests.scad)
+#LIST(APPEND CGALPNGTEST_FILES ${CMAKE_SOURCE_DIR}/../examples/example001.scad)
+add_cmdline_test(cgalpngtest png ${CGALPNGTEST_FILES})
+
+# Add opencsg tests to CTest
+LIST(APPEND OPENCSGTEST_FILES ${CGALPNGTEST_FILES})
+add_cmdline_test(opencsgtest png ${OPENCSGTEST_FILES})
+
+# Add dxfexport tests to CTest
+#add_cmdline_test(${CMAKE_SOURCE_DIR}/../test-code/exportdxf dxf ${SCAD_FILES})
diff --git a/tests/CSGTextCache.cc b/tests/CSGTextCache.cc
new file mode 100644
index 0000000..4234c63
--- /dev/null
+++ b/tests/CSGTextCache.cc
@@ -0,0 +1,27 @@
+#include "CSGTextCache.h"
+
+bool CSGTextCache::contains(const AbstractNode &node) const
+{
+ return this->cache.contains(this->tree.getString(node));
+}
+
+// We cannot return a reference since the [] operator returns a temporary value
+string CSGTextCache::operator[](const AbstractNode &node) const
+{
+ return this->cache[this->tree.getString(node)];
+}
+
+void CSGTextCache::insert(const class AbstractNode &node, const string & value)
+{
+ this->cache.insert(this->tree.getString(node), value);
+}
+
+void CSGTextCache::remove(const class AbstractNode &node)
+{
+ this->cache.remove(this->tree.getString(node));
+}
+
+void CSGTextCache::clear()
+{
+ this->cache.clear();
+}
diff --git a/tests/CSGTextCache.h b/tests/CSGTextCache.h
new file mode 100644
index 0000000..57a6972
--- /dev/null
+++ b/tests/CSGTextCache.h
@@ -0,0 +1,27 @@
+#ifndef CSGTEXTCACHE_H_
+#define CSGTEXTCACHE_H_
+
+#include "myqhash.h"
+#include "Tree.h"
+#include <string>
+
+using std::string;
+
+class CSGTextCache
+{
+public:
+ CSGTextCache(const Tree &tree) : tree(tree) {}
+ ~CSGTextCache() {}
+
+ bool contains(const AbstractNode &node) const;
+ string operator[](const AbstractNode &node) const;
+ void insert(const class AbstractNode &node, const string & value);
+ void remove(const class AbstractNode &node);
+ void clear();
+
+private:
+ QHash<string, string> cache;
+ const Tree &tree;
+};
+
+#endif
diff --git a/tests/CSGTextRenderer.cc b/tests/CSGTextRenderer.cc
new file mode 100644
index 0000000..b55c88f
--- /dev/null
+++ b/tests/CSGTextRenderer.cc
@@ -0,0 +1,185 @@
+#include "CSGTextRenderer.h"
+
+#include <string>
+#include <map>
+#include <list>
+#include "visitor.h"
+#include "state.h"
+#include "module.h" // FIXME: Temporarily for ModuleInstantiation
+
+#include "csgnode.h"
+#include "transformnode.h"
+
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+#include <QRegExp>
+
+bool CSGTextRenderer::isCached(const AbstractNode &node)
+{
+ return this->cache.contains(node);
+}
+
+/*!
+ Modifies target by applying op to target and src:
+ target = target [op] src
+ */
+void
+CSGTextRenderer::process(string &target, const string &src, CSGTextRenderer::CsgOp op)
+{
+// if (target.dim != 2 && target.dim != 3) {
+// assert(false && "Dimension of Nef polyhedron must be 2 or 3");
+// }
+
+ switch (op) {
+ case UNION:
+ target += "+" + src;
+ break;
+ case INTERSECTION:
+ target += "*" + src;
+ break;
+ case DIFFERENCE:
+ target += "-" + src;
+ break;
+ case MINKOWSKI:
+ target += "M" + src;
+ break;
+ }
+}
+
+void CSGTextRenderer::applyToChildren(const AbstractNode &node, CSGTextRenderer::CsgOp op)
+{
+ std::stringstream stream;
+ stream << node.name() << node.index();
+ string N = stream.str();
+ if (this->visitedchildren[node.index()].size() > 0) {
+ // FIXME: assert that cache contains nodes in code below
+ bool first = true;
+ for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
+ iter != this->visitedchildren[node.index()].end();
+ iter++) {
+ const AbstractNode *chnode = *iter;
+ assert(this->cache.contains(*chnode));
+ // FIXME: Don't use deep access to modinst members
+ if (chnode->modinst->tag_background) continue;
+ if (first) {
+ N += "(" + this->cache[*chnode];
+// if (N.dim != 0) first = false; // FIXME: when can this happen?
+ first = false;
+ } else {
+ process(N, this->cache[*chnode], op);
+ }
+ chnode->progress_report();
+ }
+ N += ")";
+ }
+ this->cache.insert(node, N);
+}
+
+/*
+ Typical visitor behavior:
+ o In prefix: Check if we're cached -> prune
+ o In postfix: Check if we're cached -> don't apply operator to children
+ o In postfix: addToParent()
+ */
+
+Response CSGTextRenderer::visit(State &state, const AbstractNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) applyToChildren(node, UNION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTextRenderer::visit(State &state, const AbstractIntersectionNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) applyToChildren(node, INTERSECTION);
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTextRenderer::visit(State &state, const CsgNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ CsgOp op;
+ switch (node.type) {
+ case CSG_TYPE_UNION:
+ op = UNION;
+ break;
+ case CSG_TYPE_DIFFERENCE:
+ op = DIFFERENCE;
+ break;
+ case CSG_TYPE_INTERSECTION:
+ op = INTERSECTION;
+ break;
+ }
+ applyToChildren(node, op);
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+Response CSGTextRenderer::visit(State &state, const TransformNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+ // First union all children
+ applyToChildren(node, UNION);
+ // FIXME: Then apply transform
+ }
+ addToParent(state, node);
+ }
+ return ContinueTraversal;
+}
+
+// FIXME: RenderNode: Union over children + some magic
+// FIXME: CgaladvNode: Iterate over children. Special operation
+
+// FIXME: Subtypes of AbstractPolyNode:
+// ProjectionNode
+// DxfLinearExtrudeNode
+// DxfRotateExtrudeNode
+// (SurfaceNode)
+// (PrimitiveNode)
+Response CSGTextRenderer::visit(State &state, const AbstractPolyNode &node)
+{
+ if (state.isPrefix() && isCached(node)) return PruneTraversal;
+ if (state.isPostfix()) {
+ if (!isCached(node)) {
+
+ // FIXME: Manage caching
+ // FIXME: Will generate one single Nef polyhedron (no csg ops necessary)
+
+ string N = node.name();
+ this->cache.insert(node, N);
+
+// std::cout << "Insert: " << N << "\n";
+// std::cout << "Node: " << cacheid.toStdString() << "\n\n";
+ }
+ addToParent(state, node);
+ }
+
+ return ContinueTraversal;
+}
+
+/*!
+ Adds ourself to out parent's list of traversed children.
+ Call this for _every_ node which affects output during the postfix traversal.
+*/
+void CSGTextRenderer::addToParent(const State &state, const AbstractNode &node)
+{
+ assert(state.isPostfix());
+ this->visitedchildren.erase(node.index());
+ if (state.parent()) {
+ this->visitedchildren[state.parent()->index()].push_back(&node);
+ }
+}
diff --git a/tests/CSGTextRenderer.h b/tests/CSGTextRenderer.h
new file mode 100644
index 0000000..79a8a96
--- /dev/null
+++ b/tests/CSGTextRenderer.h
@@ -0,0 +1,40 @@
+#ifndef CSGTEXTRENDERER_H_
+#define CSGTEXTRENDERER_H_
+
+#include "visitor.h"
+#include "CSGTextCache.h"
+
+#include <map>
+#include <list>
+
+using std::string;
+using std::map;
+using std::list;
+
+class CSGTextRenderer : public Visitor
+{
+public:
+ CSGTextRenderer(CSGTextCache &cache) : cache(cache) {}
+ virtual ~CSGTextRenderer() {}
+
+ virtual Response visit(State &state, const AbstractNode &node);
+ virtual Response visit(State &state, const AbstractIntersectionNode &node);
+ virtual Response visit(State &state, const CsgNode &node);
+ virtual Response visit(State &state, const TransformNode &node);
+ virtual Response visit(State &state, const AbstractPolyNode &node);
+
+private:
+ enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI};
+ void addToParent(const State &state, const AbstractNode &node);
+ bool isCached(const AbstractNode &node);
+ void process(string &target, const string &src, CSGTextRenderer::CsgOp op);
+ void applyToChildren(const AbstractNode &node, CSGTextRenderer::CsgOp op);
+
+ string currindent;
+ typedef list<const AbstractNode *> ChildList;
+ map<int, ChildList> visitedchildren;
+
+ CSGTextCache &cache;
+};
+
+#endif
diff --git a/tests/FindGLEW.cmake b/tests/FindGLEW.cmake
new file mode 100644
index 0000000..e058e8c
--- /dev/null
+++ b/tests/FindGLEW.cmake
@@ -0,0 +1,40 @@
+#
+# Try to find GLEW library and include path.
+# Once done this will define
+#
+# GLEW_FOUND
+# GLEW_INCLUDE_PATH
+# GLEW_LIBRARY
+#
+
+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 GLEW glew32 glew32s
+ 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("GLEW_DIR: " ${GLEW_DIR})
+ FIND_PATH( GLEW_INCLUDE_PATH GL/glew.h
+ PATHS ${GLEW_DIR}/include /usr/include /usr/local/include
+ NO_DEFAULT_PATH
+ DOC "The directory where GL/glew.h resides")
+ FIND_LIBRARY( GLEW_LIBRARY
+ NAMES GLEW glew
+ PATHS ${GLEW_DIR}/lib /usr/lib /usr/local/lib
+ NO_DEFAULT_PATH
+ DOC "The GLEW library")
+ENDIF (WIN32)
+
+IF (GLEW_INCLUDE_PATH)
+ SET( GLEW_FOUND 1 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")
+ MESSAGE(STATUS "GLEW found in " ${GLEW_INCLUDE_PATH} " " ${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)
diff --git a/tests/OffscreenContext.h b/tests/OffscreenContext.h
new file mode 100644
index 0000000..0300bcb
--- /dev/null
+++ b/tests/OffscreenContext.h
@@ -0,0 +1,14 @@
+#ifndef OFFSCREENCONTEXT_H_
+#define OFFSCREENCONTEXT_H_
+
+#include <OpenGL/OpenGL.h>
+#include <iostream> // for error output
+
+#define REPORTGLERROR(task) { GLenum tGLErr = glGetError(); if (tGLErr != GL_NO_ERROR) { std::cout << "OpenGL error " << tGLErr << " while " << task << "\n"; } }
+
+struct OffscreenContext *create_offscreen_context(int w, int h);
+void bind_offscreen_context(OffscreenContext *ctx);
+bool teardown_offscreen_context(OffscreenContext *ctx);
+bool save_framebuffer(OffscreenContext *ctx, const char *filename);
+
+#endif
diff --git a/tests/OffscreenContext.mm b/tests/OffscreenContext.mm
new file mode 100644
index 0000000..9a6a5e2
--- /dev/null
+++ b/tests/OffscreenContext.mm
@@ -0,0 +1,207 @@
+#include "OffscreenContext.h"
+
+#import <OpenGL/OpenGL.h>
+#import <OpenGL/glu.h> // for gluCheckExtension
+#import <AppKit/AppKit.h> // for NSOpenGL...
+
+// Simple error reporting macros to help keep the sample code clean
+#define REPORT_ERROR_AND_EXIT(desc) { std::cout << desc << "\n"; return false; }
+#define NULL_ERROR_EXIT(test, desc) { if (!test) REPORT_ERROR_AND_EXIT(desc); }
+
+struct OffscreenContext
+{
+ NSOpenGLContext *openGLContext;
+ NSAutoreleasePool *pool;
+ int width;
+ int height;
+ GLuint fbo;
+};
+
+
+OffscreenContext *create_offscreen_context(int w, int h)
+{
+ OffscreenContext *ctx = new OffscreenContext;
+ ctx->width = w;
+ ctx->height = h;
+
+ ctx->pool = [NSAutoreleasePool new];
+
+ /*
+ * Create an OpenGL context just so that OpenGL calls will work. I'm not
+ using it for actual rendering.
+ */
+
+ NSOpenGLPixelFormatAttribute attributes[] = {
+ NSOpenGLPFAPixelBuffer,
+ NSOpenGLPFANoRecovery,
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFADepthSize, 24,
+ (NSOpenGLPixelFormatAttribute) 0
+ };
+ NSOpenGLPixelFormat* pixFormat = [[[NSOpenGLPixelFormat
+ alloc] initWithAttributes:attributes] autorelease];
+ // Create the OpenGL context to render with (with color and depth buffers)
+ ctx->openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixFormat
+ shareContext:nil];
+ NULL_ERROR_EXIT(ctx->openGLContext, "Unable to create NSOpenGLContext");
+
+ [ctx->openGLContext makeCurrentContext];
+
+ /*
+ * Test if framebuffer objects are supported
+ */
+ const GLubyte* strExt = glGetString(GL_EXTENSIONS);
+ GLboolean fboSupported = gluCheckExtension((const GLubyte*)"GL_EXT_framebuffer_object", strExt);
+ if (!fboSupported)
+ REPORT_ERROR_AND_EXIT("Your system does not support framebuffer extension - unable to render scene");
+ /*
+ * Create an FBO
+ */
+ GLuint renderBuffer = 0;
+ GLuint depthBuffer = 0;
+ // Depth buffer to use for depth testing - optional if you're not using depth testing
+ glGenRenderbuffersEXT(1, &depthBuffer);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
+ REPORTGLERROR("creating depth render buffer");
+
+ // Render buffer to use for imaging
+ glGenRenderbuffersEXT(1, &renderBuffer);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderBuffer);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h);
+ REPORTGLERROR("creating color render buffer");
+ ctx->fbo = 0;
+ glGenFramebuffersEXT(1, &ctx->fbo);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->fbo);
+ REPORTGLERROR("binding framebuffer");
+
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT, renderBuffer);
+ REPORTGLERROR("specifying color render buffer");
+
+ if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
+ GL_FRAMEBUFFER_COMPLETE_EXT)
+ REPORT_ERROR_AND_EXIT("Problem with OpenGL framebuffer after specifying color render buffer.");
+
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depthBuffer);
+ REPORTGLERROR("specifying depth render buffer");
+
+ if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
+ GL_FRAMEBUFFER_COMPLETE_EXT)
+ REPORT_ERROR_AND_EXIT("Problem with OpenGL framebuffer after specifying depth render buffer.");
+
+ return ctx;
+}
+
+bool teardown_offscreen_context(OffscreenContext *ctx)
+{
+ // "un"bind my FBO
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+
+ /*
+ * Cleanup
+ */
+ [ctx->openGLContext clearDrawable];
+ [ctx->openGLContext release];
+
+ [ctx->pool release];
+ return true;
+}
+
+bool save_framebuffer(OffscreenContext *ctx, const char *filename)
+{
+ /*
+ * Extract the resulting rendering as an image
+ */
+
+ int samplesPerPixel = 4; // R, G, B and A
+ int rowBytes = samplesPerPixel * ctx->width;
+ char* bufferData = (char*)malloc(rowBytes * ctx->height);
+ if (!bufferData) {
+ std::cerr << "Unable to allocate buffer for image extraction.";
+ return 1;
+ }
+ glReadPixels(0, 0, ctx->width, ctx->height, GL_BGRA, GL_UNSIGNED_BYTE,
+ bufferData);
+ REPORTGLERROR("reading pixels from framebuffer");
+
+ // Flip it vertically - images read from OpenGL buffers are upside-down
+ char* flippedBuffer = (char*)malloc(rowBytes * ctx->height);
+ if (!flippedBuffer) {
+ std::cout << "Unable to allocate flipped buffer for corrected image.";
+ return 1;
+ }
+ for (int i = 0 ; i < ctx->height ; i++) {
+ bcopy(bufferData + i * rowBytes, flippedBuffer + (ctx->height - i - 1) *
+ rowBytes, rowBytes);
+ }
+
+ /*
+ * Output the image to a file
+ */
+ CGColorSpaceRef colorSpace =
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Little; // XRGB Little Endian
+ int bitsPerComponent = 8;
+ CGContextRef contextRef = CGBitmapContextCreate(flippedBuffer,
+ ctx->width, ctx->height, bitsPerComponent, rowBytes,
+ colorSpace, bitmapInfo);
+ if (!contextRef) {
+ std::cerr << "Unable to create CGContextRef.";
+ return false;
+ }
+
+ CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
+ if (!imageRef) {
+ std::cerr << "Unable to create CGImageRef.";
+ return false;
+ }
+ Boolean isDirectory = false;
+ CFStringRef fname = CFStringCreateWithCString(kCFAllocatorDefault, filename, kCFStringEncodingUTF8);
+ CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+ fname, kCFURLPOSIXPathStyle, isDirectory);
+ if (!fileURL) {
+ std::cerr << "Unable to create file URL ref.";
+ return false;
+ }
+ CGDataConsumerRef dataconsumer = CGDataConsumerCreateWithURL(fileURL);
+
+ CFIndex fileImageIndex = 1;
+ CFMutableDictionaryRef fileDict = NULL;
+ CFStringRef fileUTType = kUTTypePNG;
+ // Create an image destination opaque reference for authoring an image file
+ CGImageDestinationRef imageDest = CGImageDestinationCreateWithDataConsumer(dataconsumer,
+ fileUTType,
+ fileImageIndex,
+ fileDict);
+ if (!imageDest) {
+ std::cerr << "Unable to create CGImageDestinationRef.";
+ return false;
+ }
+ CFIndex capacity = 1;
+ CFMutableDictionaryRef imageProps =
+ CFDictionaryCreateMutable(kCFAllocatorDefault,
+ capacity,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CGImageDestinationAddImage(imageDest, imageRef, imageProps);
+ CGImageDestinationFinalize(imageDest);
+
+ free(flippedBuffer);
+ free(bufferData);
+ CFRelease(imageDest);
+ CFRelease(dataconsumer);
+ CFRelease(fileURL);
+ CFRelease(fname);
+ CFRelease(imageProps);
+ CGColorSpaceRelease(colorSpace);
+ CGImageRelease(imageRef);
+ return true;
+}
+
+void bind_offscreen_context(OffscreenContext *ctx)
+{
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->fbo);
+}
diff --git a/tests/OffscreenView.cc b/tests/OffscreenView.cc
new file mode 100644
index 0000000..447a78c
--- /dev/null
+++ b/tests/OffscreenView.cc
@@ -0,0 +1,239 @@
+#include <GL/glew.h>
+#include "OffscreenView.h"
+#include <opencsg.h>
+#include "Renderer.h"
+#include <math.h>
+
+#define FAR_FAR_AWAY 100000.0
+
+OffscreenView::OffscreenView(size_t width, size_t height)
+ : orthomode(false), showaxes(false), showfaces(true), showedges(false),
+ object_rot(35, 0, 25), camera_eye(0, 0, 0), camera_center(0, 0, 0)
+{
+ for (int i = 0; i < 10; i++) this->shaderinfo[i] = 0;
+ this->ctx = create_offscreen_context(width, height);
+ initializeGL();
+ resizeGL(width, height);
+}
+
+OffscreenView::~OffscreenView()
+{
+ teardown_offscreen_context(this->ctx);
+}
+
+void OffscreenView::setRenderer(Renderer* r)
+{
+ this->renderer = r;
+}
+
+void OffscreenView::initializeGL()
+{
+ glEnable(GL_DEPTH_TEST);
+ glDepthRange(-FAR_FAR_AWAY, +FAR_FAR_AWAY);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat light_position0[] = {-1.0, -1.0, +1.0, 0.0};
+ GLfloat light_position1[] = {+1.0, +1.0, -1.0, 0.0};
+
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
+ glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
+ glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
+ glEnable(GL_LIGHT1);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_NORMALIZE);
+
+ glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+
+#ifdef ENABLE_OPENCSG
+ const char *openscad_disable_gl20_env = getenv("OPENSCAD_DISABLE_GL20");
+ if (openscad_disable_gl20_env && !strcmp(openscad_disable_gl20_env, "0"))
+ openscad_disable_gl20_env = NULL;
+ if (glewIsSupported("GL_VERSION_2_0") && openscad_disable_gl20_env == NULL)
+ {
+ const char *vs_source =
+ "uniform float xscale, yscale;\n"
+ "attribute vec3 pos_b, pos_c;\n"
+ "attribute vec3 trig, mask;\n"
+ "varying vec3 tp, tr;\n"
+ "varying float shading;\n"
+ "void main() {\n"
+ " vec4 p0 = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
+ " vec4 p1 = gl_ModelViewProjectionMatrix * vec4(pos_b, 1.0);\n"
+ " vec4 p2 = gl_ModelViewProjectionMatrix * vec4(pos_c, 1.0);\n"
+ " float a = distance(vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n"
+ " float b = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p1.x/p1.w, yscale*p1.y/p1.w));\n"
+ " float c = distance(vec2(xscale*p0.x/p0.w, yscale*p0.y/p0.w), vec2(xscale*p2.x/p2.w, yscale*p2.y/p2.w));\n"
+ " float s = (a + b + c) / 2.0;\n"
+ " float A = sqrt(s*(s-a)*(s-b)*(s-c));\n"
+ " float ha = 2.0*A/a;\n"
+ " gl_Position = p0;\n"
+ " tp = mask * ha;\n"
+ " tr = trig;\n"
+ " vec3 normal, lightDir;\n"
+ " normal = normalize(gl_NormalMatrix * gl_Normal);\n"
+ " lightDir = normalize(vec3(gl_LightSource[0].position));\n"
+ " shading = abs(dot(normal, lightDir));\n"
+ "}\n";
+
+ const char *fs_source =
+ "uniform vec4 color1, color2;\n"
+ "varying vec3 tp, tr, tmp;\n"
+ "varying float shading;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(color1.r * shading, color1.g * shading, color1.b * shading, color1.a);\n"
+ " if (tp.x < tr.x || tp.y < tr.y || tp.z < tr.z)\n"
+ " gl_FragColor = color2;\n"
+ "}\n";
+
+ GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vs, 1, (const GLchar**)&vs_source, NULL);
+ glCompileShader(vs);
+
+ GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fs, 1, (const GLchar**)&fs_source, NULL);
+ glCompileShader(fs);
+
+ GLuint edgeshader_prog = glCreateProgram();
+ glAttachShader(edgeshader_prog, vs);
+ glAttachShader(edgeshader_prog, fs);
+ glLinkProgram(edgeshader_prog);
+
+ shaderinfo[0] = edgeshader_prog;
+ shaderinfo[1] = glGetUniformLocation(edgeshader_prog, "color1");
+ shaderinfo[2] = glGetUniformLocation(edgeshader_prog, "color2");
+ shaderinfo[3] = glGetAttribLocation(edgeshader_prog, "trig");
+ shaderinfo[4] = glGetAttribLocation(edgeshader_prog, "pos_b");
+ shaderinfo[5] = glGetAttribLocation(edgeshader_prog, "pos_c");
+ shaderinfo[6] = glGetAttribLocation(edgeshader_prog, "mask");
+ shaderinfo[7] = glGetUniformLocation(edgeshader_prog, "xscale");
+ shaderinfo[8] = glGetUniformLocation(edgeshader_prog, "yscale");
+
+ GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ fprintf(stderr, "OpenGL Error: %s\n", gluErrorString(err));
+ }
+
+ GLint status;
+ glGetProgramiv(edgeshader_prog, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE) {
+ int loglen;
+ char logbuffer[1000];
+ glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
+ fprintf(stderr, "OpenGL Program Linker Error:\n%.*s", loglen, logbuffer);
+ } else {
+ int loglen;
+ char logbuffer[1000];
+ glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
+ if (loglen > 0) {
+ fprintf(stderr, "OpenGL Program Link OK:\n%.*s", loglen, logbuffer);
+ }
+ glValidateProgram(edgeshader_prog);
+ glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
+ if (loglen > 0) {
+ fprintf(stderr, "OpenGL Program Validation results:\n%.*s", loglen, logbuffer);
+ }
+ }
+ }
+#endif /* ENABLE_OPENCSG */
+}
+
+void OffscreenView::resizeGL(int w, int h)
+{
+#ifdef ENABLE_OPENCSG
+ shaderinfo[9] = w;
+ shaderinfo[10] = h;
+#endif
+ glViewport(0, 0, w, h);
+ w_h_ratio = sqrt((double)w / (double)h);
+}
+
+void OffscreenView::setupPerspective()
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ double dist = (this->camera_center - this->camera_eye).norm();
+ gluPerspective(45, w_h_ratio, 0.1*dist, 100*dist);
+}
+
+void OffscreenView::setupOrtho(bool offset)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (offset) glTranslated(-0.8, -0.8, 0);
+ double l = (this->camera_center - this->camera_eye).norm() / 10;
+ glOrtho(-w_h_ratio*l, +w_h_ratio*l,
+ -(1/w_h_ratio)*l, +(1/w_h_ratio)*l,
+ -FAR_FAR_AWAY, +FAR_FAR_AWAY);
+}
+
+void OffscreenView::paintGL()
+{
+ glEnable(GL_LIGHTING);
+
+ if (orthomode) setupOrtho();
+ else setupPerspective();
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClearColor(1.0, 1.0, 0.92, 0.0);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ gluLookAt(this->camera_eye[0], this->camera_eye[1], this->camera_eye[2],
+ this->camera_center[0], this->camera_center[1], this->camera_center[2],
+ 0.0, 0.0, 1.0);
+
+ // glRotated(object_rot[0], 1.0, 0.0, 0.0);
+ // glRotated(object_rot[1], 0.0, 1.0, 0.0);
+ // glRotated(object_rot[2], 0.0, 0.0, 1.0);
+
+ // Large gray axis cross inline with the model
+ // FIXME: This is always gray - adjust color to keep contrast with background
+ if (showaxes)
+ {
+ glLineWidth(1);
+ glColor3d(0.5, 0.5, 0.5);
+ glBegin(GL_LINES);
+ double l = 3*(this->camera_center - this->camera_eye).norm();
+ glVertex3d(-l, 0, 0);
+ glVertex3d(+l, 0, 0);
+ glVertex3d(0, -l, 0);
+ glVertex3d(0, +l, 0);
+ glVertex3d(0, 0, -l);
+ glVertex3d(0, 0, +l);
+ glEnd();
+ }
+
+ glDepthFunc(GL_LESS);
+ glCullFace(GL_BACK);
+ glDisable(GL_CULL_FACE);
+
+ glLineWidth(2);
+ glColor3d(1.0, 0.0, 0.0);
+
+ if (this->renderer) {
+#ifdef ENABLE_OPENCSG
+ OpenCSG::setContext(0);
+#endif
+ this->renderer->draw(showfaces, showedges);
+ }
+}
+
+bool OffscreenView::save(const char *filename)
+{
+ return save_framebuffer(this->ctx, filename);
+}
+
+void OffscreenView::setCamera(const Eigen::Vector3d &pos, const Eigen::Vector3d &center)
+{
+ this->camera_eye = pos;
+ this->camera_center = center;
+}
+
diff --git a/tests/OffscreenView.h b/tests/OffscreenView.h
new file mode 100644
index 0000000..587255a
--- /dev/null
+++ b/tests/OffscreenView.h
@@ -0,0 +1,39 @@
+#ifndef OFFSCREENVIEW_H_
+#define OFFSCREENVIEW_H_
+
+#include "OffscreenContext.h"
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+#include <stdint.h>
+
+class OffscreenView
+{
+public:
+ OffscreenView(size_t width, size_t height);
+ ~OffscreenView();
+ void setRenderer(class Renderer* r);
+
+ void setCamera(const Eigen::Vector3d &pos, const Eigen::Vector3d &center);
+ void initializeGL();
+ void resizeGL(int w, int h);
+ void setupPerspective();
+ void setupOrtho(bool offset=false);
+ void paintGL();
+ bool save(const char *filename);
+
+ GLint shaderinfo[11];
+ OffscreenContext *ctx;
+private:
+ Renderer *renderer;
+ double w_h_ratio;
+ Eigen::Vector3d object_rot;
+ Eigen::Vector3d camera_eye;
+ Eigen::Vector3d camera_center;
+
+ bool orthomode;
+ bool showaxes;
+ bool showfaces;
+ bool showedges;
+};
+
+#endif
diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc
new file mode 100644
index 0000000..8e3859b
--- /dev/null
+++ b/tests/cgalpngtest.cc
@@ -0,0 +1,234 @@
+/*
+ * OpenSCAD (www.openscad.at)
+ * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 "myqhash.h"
+#include "openscad.h"
+#include "node.h"
+#include "module.h"
+#include "polyset.h"
+#include "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+#include "CGAL_Nef_polyhedron.h"
+#include "CGALEvaluator.h"
+#include "PolySetCGALEvaluator.h"
+#include "CGALRenderer.h"
+#include "CGAL_renderer.h"
+#include "cgal.h"
+#include "OffscreenView.h"
+#include "handle_dep.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <QTextStream>
+#include <getopt.h>
+#include <iostream>
+#include <assert.h>
+#include <sstream>
+
+std::string commandline_commands;
+QString currentdir;
+QString examplesdir;
+QString librarydir;
+
+using std::string;
+
+// FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
+QHash<std::string, CGAL_Nef_polyhedron> cache;
+
+void cgalTree(Tree &tree)
+{
+ assert(tree.root());
+
+ CGALEvaluator evaluator(cache, tree);
+ Traverser evaluate(evaluator, *tree.root(), Traverser::PRE_AND_POSTFIX);
+ evaluate.execute();
+}
+
+AbstractNode *find_root_tag(AbstractNode *n)
+{
+ foreach(AbstractNode *v, n->children) {
+ if (v->modinst->tag_root) return v;
+ if (AbstractNode *vroot = find_root_tag(v)) return vroot;
+ }
+ return NULL;
+}
+
+struct CsgInfo
+{
+ OffscreenView *glview;
+};
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv, false);
+ QDir original_path = QDir::current();
+
+ currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
+ AbstractNode *root_node;
+ // Do we have an explicit root node (! modifier)?
+ if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node;
+
+ Tree tree(root_node);
+
+ CsgInfo csgInfo;
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator cgalevaluator(cache, tree);
+ PolySetCGALEvaluator psevaluator(cgalevaluator);
+
+ CGAL_Nef_polyhedron N = cgalevaluator.evaluateCGALMesh(*root_node);
+
+ QDir::setCurrent(original_path.absolutePath());
+
+ csgInfo.glview = new OffscreenView(512,512);
+
+ glewInit();
+#ifdef DEBUG
+ cout << "GLEW version " << glewGetString(GLEW_VERSION) << "\n";
+ cout << (const char *)glGetString(GL_RENDERER) << "(" << (const char *)glGetString(GL_VENDOR) << ")\n"
+ << "OpenGL version " << (const char *)glGetString(GL_VERSION) << "\n";
+ cout << "Extensions: " << (const char *)glGetString(GL_EXTENSIONS) << "\n";
+
+
+ if (GLEW_ARB_framebuffer_object) {
+ cout << "ARB_FBO supported\n";
+ }
+ if (GLEW_EXT_framebuffer_object) {
+ cout << "EXT_FBO supported\n";
+ }
+ if (GLEW_EXT_packed_depth_stencil) {
+ cout << "EXT_packed_depth_stencil\n";
+ }
+#endif
+
+ CGALRenderer cgalRenderer(N);
+
+ BoundingBox bbox;
+ if (cgalRenderer.polyhedron) {
+ CGAL::Bbox_3 cgalbbox = cgalRenderer.polyhedron->bbox();
+ bbox = BoundingBox(Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()),
+ Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax()));
+ }
+ else if (cgalRenderer.polyset) {
+ bbox = cgalRenderer.polyset->getBoundingBox();
+ }
+ Vector3d center = (bbox.min() + bbox.max()) / 2;
+ double radius = (bbox.max() - bbox.min()).norm() / 2;
+
+
+ Vector3d cameradir(1, 1, -0.5);
+ Vector3d camerapos = center - radius*2*cameradir;
+ csgInfo.glview->setCamera(camerapos, center);
+
+
+ csgInfo.glview->setRenderer(&cgalRenderer);
+ csgInfo.glview->paintGL();
+ csgInfo.glview->save("/dev/stdout");
+
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return 0;
+}
diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc
new file mode 100644
index 0000000..029fcfc
--- /dev/null
+++ b/tests/cgaltest.cc
@@ -0,0 +1,180 @@
+/*
+ * OpenSCAD (www.openscad.at)
+ * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 "myqhash.h"
+#include "openscad.h"
+#include "handle_dep.h"
+#include "node.h"
+#include "module.h"
+#include "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+#include "CGAL_Nef_polyhedron.h"
+#include "CGALEvaluator.h"
+#include "PolySetCGALEvaluator.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <QTextStream>
+#include <getopt.h>
+#include <iostream>
+#include <assert.h>
+#include <sstream>
+
+std::string commandline_commands;
+QString currentdir;
+QString examplesdir;
+QString librarydir;
+
+using std::string;
+
+// FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
+QHash<std::string, CGAL_Nef_polyhedron> cache;
+
+void cgalTree(Tree &tree)
+{
+ assert(tree.root());
+
+ CGALEvaluator evaluator(cache, tree);
+ Traverser evaluate(evaluator, *tree.root(), Traverser::PRE_AND_POSTFIX);
+ evaluate.execute();
+}
+
+AbstractNode *find_root_tag(AbstractNode *n)
+{
+ foreach(AbstractNode *v, n->children) {
+ if (v->modinst->tag_root) return v;
+ if (AbstractNode *vroot = find_root_tag(v)) return vroot;
+ }
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv, false);
+ QDir original_path = QDir::current();
+
+ currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
+ AbstractNode *root_node;
+ // Do we have an explicit root node (! modifier)?
+ if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node;
+
+ Tree tree(root_node);
+
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator cgalevaluator(cache, tree);
+ PolySetCGALEvaluator psevaluator(cgalevaluator);
+
+ CGAL_Nef_polyhedron N = cgalevaluator.evaluateCGALMesh(*root_node);
+
+ QDir::setCurrent(original_path.absolutePath());
+ if (!N.empty()) {
+ export_stl(&N, std::cout, NULL);
+ }
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return 0;
+}
diff --git a/tests/compare-bitmaps.sh b/tests/compare-bitmaps.sh
new file mode 100755
index 0000000..590d0b5
--- /dev/null
+++ b/tests/compare-bitmaps.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+
+while getopts 'v' c
+do
+ case $c in
+ v) set -x ;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+# FIXME: Sometimes, compare fails when comparing very small images (e.g. 40 x 10 pixels).
+# It's unknown why this happens..
+pixelerror=`compare -fuzz 10% -metric AE $1 $2 null: 2>&1`
+if [ $? -ne 0 ]; then
+ echo "General error: Ouch"
+ exit 1 # Compare failed to read image
+else
+ # Check if $pixelerror contains an integer (it sometimes outputs things like '0 @ 0,0')
+ if [ $pixelerror -eq $pixelerror 2> /dev/null ]; then
+ if [ $pixelerror == 0 ]; then
+ exit 0
+ fi
+ echo "Pixel error: $pixelerror"
+ else
+ echo "Pixel error: Err"
+ fi
+ exit 1
+fi
+exit 0
diff --git a/tests/csgtermtest.cc b/tests/csgtermtest.cc
new file mode 100644
index 0000000..26b809a
--- /dev/null
+++ b/tests/csgtermtest.cc
@@ -0,0 +1,172 @@
+/*
+ * OpenSCAD (www.openscad.at)
+ * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 "myqhash.h"
+#include "CSGTermEvaluator.h"
+#include "CSGTextCache.h"
+#include "openscad.h"
+#include "handle_dep.h"
+#include "node.h"
+#include "module.h"
+#include "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+#include "csgterm.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <getopt.h>
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+
+using std::cout;
+
+std::string commandline_commands;
+QString currentdir;
+QString examplesdir;
+QString librarydir;
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ int rc = 0;
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv, false);
+ QDir original_path = QDir::current();
+
+ currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+ AbstractNode *root_node;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ root_node = root_module->evaluate(&root_ctx, &root_inst);
+
+ Tree tree(root_node);
+
+// cout << tree.getString(*root_node) << "\n";
+
+ vector<CSGTerm*> highlights;
+ vector<CSGTerm*> background;
+ CSGTermEvaluator evaluator(tree);
+ CSGTerm *root_term = evaluator.evaluateCSGTerm(*root_node, highlights, background);
+
+ // cout << "Stored terms: " << evaluator.stored_term.size() << "\n";
+ // for (map<int, class CSGTerm*>::iterator iter = evaluator.stored_term.begin();
+ // iter != evaluator.stored_term.end();
+ // iter++) {
+ // cout << iter->first << ":" << (iter->second ? iter->second->label : "NULL") << "\n";
+ // }
+
+ // if (evaluator.background) cout << "Background terms: " << evaluator.background->size() << "\n";
+ // if (evaluator.highlights) cout << "Highlights terms: " << evaluator.highlights->size() << "\n";
+
+ if (root_term) {
+ cout << root_term->dump() << "\n";
+ }
+ else {
+ cout << "No top-level CSG object\n";
+ }
+
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return rc;
+}
diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc
new file mode 100644
index 0000000..3c4451d
--- /dev/null
+++ b/tests/csgtexttest.cc
@@ -0,0 +1,158 @@
+/*
+ * OpenSCAD (www.openscad.at)
+ * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 "CSGTextRenderer.h"
+#include "CSGTextCache.h"
+#include "openscad.h"
+#include "handle_dep.h"
+#include "node.h"
+#include "module.h"
+#include "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "Tree.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <getopt.h>
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+
+std::string commandline_commands;
+QString currentdir;
+QString examplesdir;
+QString librarydir;
+
+void csgTree(CSGTextCache &cache, const AbstractNode &root)
+{
+ CSGTextRenderer renderer(cache);
+ Traverser render(renderer, root, Traverser::PRE_AND_POSTFIX);
+ render.execute();
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ int rc = 0;
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv, false);
+ QDir original_path = QDir::current();
+
+ currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+ AbstractNode *root_node;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ root_node = root_module->evaluate(&root_ctx, &root_inst);
+
+ Tree tree;
+ tree.setRoot(root_node);
+ CSGTextCache csgcache(tree);
+
+ csgTree(csgcache, *root_node);
+// std::cout << tree.getString(*root_node) << "\n";
+
+ std::cout << csgcache[*root_node] << "\n";
+
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return rc;
+}
diff --git a/tests/dumptest.cc b/tests/dumptest.cc
new file mode 100644
index 0000000..9af7154
--- /dev/null
+++ b/tests/dumptest.cc
@@ -0,0 +1,155 @@
+/*
+ * 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 "openscad.h"
+#include "handle_dep.h"
+#include "node.h"
+#include "module.h"
+#include "context.h"
+#include "value.h"
+#include "export.h"
+#include "builtin.h"
+#include "nodedumper.h"
+#include "Tree.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <getopt.h>
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+
+using std::string;
+
+std::string commandline_commands;
+QString currentdir;
+QString examplesdir;
+QString librarydir;
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ int rc = 0;
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv, false);
+ QDir original_path = QDir::current();
+
+ currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+ AbstractNode *root_node;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ root_node = root_module->evaluate(&root_ctx, &root_inst);
+
+ // Cache test
+ QString teststr("test");
+ Tree tree;
+ tree.setRoot(root_node);
+
+ string dumpstdstr = tree.getString(*root_node);
+ string dumpstdstr_cached = tree.getString(*root_node);
+ if (dumpstdstr != dumpstdstr_cached) rc = 1;
+
+ std::cout << dumpstdstr << "\n";
+
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return rc;
+}
diff --git a/tests/opencsgtest.cc b/tests/opencsgtest.cc
new file mode 100644
index 0000000..72f7488
--- /dev/null
+++ b/tests/opencsgtest.cc
@@ -0,0 +1,251 @@
+#include <GL/glew.h>
+#include "openscad.h"
+#include "handle_dep.h"
+#include "builtin.h"
+#include "context.h"
+#include "node.h"
+#include "module.h"
+#include "polyset.h"
+#include "Tree.h"
+#include "CSGTermEvaluator.h"
+#include "CGALEvaluator.h"
+#include "PolySetCGALEvaluator.h"
+
+#include "OpenCSGRenderer.h"
+#include "ThrownTogetherRenderer.h"
+
+#include "csgterm.h"
+#include "OffscreenView.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+#include <QSet>
+#include <QTimer>
+#include <sstream>
+
+using std::cerr;
+using std::cout;
+
+std::string commandline_commands;
+QString librarydir;
+
+//#define DEBUG
+
+struct CsgInfo
+{
+ CSGTerm *root_norm_term; // Normalized CSG products
+ class CSGChain *root_chain;
+ std::vector<CSGTerm*> highlight_terms;
+ CSGChain *highlights_chain;
+ std::vector<CSGTerm*> background_terms;
+ CSGChain *background_chain;
+ OffscreenView *glview;
+};
+
+AbstractNode *find_root_tag(AbstractNode *n)
+{
+ foreach(AbstractNode *v, n->children) {
+ if (v->modinst->tag_root) return v;
+ if (AbstractNode *vroot = find_root_tag(v)) return vroot;
+ }
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <file.scad>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filename = argv[1];
+
+ initialize_builtin_functions();
+ initialize_builtin_modules();
+
+ QApplication app(argc, argv);
+
+ QDir original_path = QDir::current();
+
+ QString currentdir = QDir::currentPath();
+
+ QDir libdir(QApplication::instance()->applicationDirPath());
+#ifdef Q_WS_MAC
+ libdir.cd("../Resources"); // Libraries can be bundled
+ if (!libdir.exists("libraries")) libdir.cd("../../..");
+#elif defined(Q_OS_UNIX)
+ if (libdir.cd("../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../share/openscad/libraries")) {
+ librarydir = libdir.path();
+ } else
+ if (libdir.cd("../../libraries")) {
+ librarydir = libdir.path();
+ } else
+#endif
+ if (libdir.cd("libraries")) {
+ librarydir = libdir.path();
+ }
+
+ Context root_ctx;
+ root_ctx.functions_p = &builtin_functions;
+ root_ctx.modules_p = &builtin_modules;
+ root_ctx.set_variable("$fn", Value(0.0));
+ root_ctx.set_variable("$fs", Value(1.0));
+ root_ctx.set_variable("$fa", Value(12.0));
+ root_ctx.set_variable("$t", Value(0.0));
+
+ Value zero3;
+ zero3.type = Value::VECTOR;
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ zero3.append(new Value(0.0));
+ root_ctx.set_variable("$vpt", zero3);
+ root_ctx.set_variable("$vpr", zero3);
+
+
+ AbstractModule *root_module;
+ ModuleInstantiation root_inst;
+
+ QFileInfo fileInfo(filename);
+ handle_dep(filename);
+ FILE *fp = fopen(filename, "rt");
+ if (!fp) {
+ 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 << commandline_commands;
+ root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false);
+ if (!root_module) {
+ exit(1);
+ }
+ }
+
+ QDir::setCurrent(fileInfo.absolutePath());
+
+ AbstractNode::resetIndexCounter();
+ AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst);
+ AbstractNode *root_node;
+ // Do we have an explicit root node (! modifier)?
+ if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node;
+
+ Tree tree(root_node);
+
+ CsgInfo csgInfo;
+ QHash<std::string, CGAL_Nef_polyhedron> cache;
+ CGALEvaluator cgalevaluator(cache, tree);
+ PolySetCGALEvaluator psevaluator(cgalevaluator);
+ CSGTermEvaluator evaluator(tree, &psevaluator);
+ CSGTerm *root_raw_term = evaluator.evaluateCSGTerm(*root_node,
+ csgInfo.highlight_terms,
+ csgInfo.background_terms);
+
+ if (!root_raw_term) {
+ cerr << "Error: CSG generation failed! (no top level object found)\n";
+ return 1;
+ }
+
+ // CSG normalization
+ csgInfo.root_norm_term = root_raw_term->link();
+ while (1) {
+ CSGTerm *n = csgInfo.root_norm_term->normalize();
+ csgInfo.root_norm_term->unlink();
+ if (csgInfo.root_norm_term == n)
+ break;
+ csgInfo.root_norm_term = n;
+ }
+
+ assert(csgInfo.root_norm_term);
+
+ csgInfo.root_chain = new CSGChain();
+ csgInfo.root_chain->import(csgInfo.root_norm_term);
+ fprintf(stderr, "Normalized CSG tree has %d elements\n", csgInfo.root_chain->polysets.size());
+
+ if (csgInfo.highlight_terms.size() > 0) {
+ cerr << "Compiling highlights (" << csgInfo.highlight_terms.size() << " CSG Trees)...\n";
+
+ csgInfo.highlights_chain = new CSGChain();
+ for (unsigned int i = 0; i < csgInfo.highlight_terms.size(); i++) {
+ while (1) {
+ CSGTerm *n = csgInfo.highlight_terms[i]->normalize();
+ csgInfo.highlight_terms[i]->unlink();
+ if (csgInfo.highlight_terms[i] == n)
+ break;
+ csgInfo.highlight_terms[i] = n;
+ }
+ csgInfo.highlights_chain->import(csgInfo.highlight_terms[i]);
+ }
+ }
+
+ if (csgInfo.background_terms.size() > 0) {
+ cerr << "Compiling background (" << csgInfo.background_terms.size() << " CSG Trees)...\n";
+
+ csgInfo.background_chain = new CSGChain();
+ for (unsigned int i = 0; i < csgInfo.background_terms.size(); i++) {
+ while (1) {
+ CSGTerm *n = csgInfo.background_terms[i]->normalize();
+ csgInfo.background_terms[i]->unlink();
+ if (csgInfo.background_terms[i] == n)
+ break;
+ csgInfo.background_terms[i] = n;
+ }
+ csgInfo.background_chain->import(csgInfo.background_terms[i]);
+ }
+ }
+
+ QDir::setCurrent(original_path.absolutePath());
+
+ csgInfo.glview = new OffscreenView(512,512);
+ BoundingBox bbox = csgInfo.root_chain->getBoundingBox();
+
+ Vector3d center = (bbox.min() + bbox.max()) / 2;
+ double radius = (bbox.max() - bbox.min()).norm() / 2;
+
+
+ Vector3d cameradir(1, 1, -0.5);
+ Vector3d camerapos = center - radius*1.8*cameradir;
+ csgInfo.glview->setCamera(camerapos, center);
+
+ glewInit();
+#ifdef DEBUG
+ cout << "GLEW version " << glewGetString(GLEW_VERSION) << "\n";
+ cout << (const char *)glGetString(GL_RENDERER) << "(" << (const char *)glGetString(GL_VENDOR) << ")\n"
+ << "OpenGL version " << (const char *)glGetString(GL_VERSION) << "\n";
+ cout << "Extensions: " << (const char *)glGetString(GL_EXTENSIONS) << "\n";
+
+
+ if (GLEW_ARB_framebuffer_object) {
+ cout << "ARB_FBO supported\n";
+ }
+ if (GLEW_EXT_framebuffer_object) {
+ cout << "EXT_FBO supported\n";
+ }
+ if (GLEW_EXT_packed_depth_stencil) {
+ cout << "EXT_packed_depth_stencil\n";
+ }
+#endif
+
+ OpenCSGRenderer opencsgRenderer(csgInfo.root_chain, csgInfo.highlights_chain, csgInfo.background_chain, csgInfo.glview->shaderinfo);
+ ThrownTogetherRenderer thrownTogetherRenderer(csgInfo.root_chain, csgInfo.highlights_chain, csgInfo.background_chain);
+// csgInfo.glview->setRenderer(&thrownTogetherRenderer);
+ csgInfo.glview->setRenderer(&opencsgRenderer);
+
+ csgInfo.glview->paintGL();
+
+ csgInfo.glview->save("/dev/stdout");
+
+ destroy_builtin_functions();
+ destroy_builtin_modules();
+
+ return 0;
+}
diff --git a/tests/regression/cgalpngtest/2d-3d-expected.png b/tests/regression/cgalpngtest/2d-3d-expected.png
new file mode 100644
index 0000000..4d202ac
--- /dev/null
+++ b/tests/regression/cgalpngtest/2d-3d-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/assign-tests-expected.png b/tests/regression/cgalpngtest/assign-tests-expected.png
new file mode 100644
index 0000000..465a94d
--- /dev/null
+++ b/tests/regression/cgalpngtest/assign-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/background-modifier-expected.png b/tests/regression/cgalpngtest/background-modifier-expected.png
new file mode 100644
index 0000000..e003a87
--- /dev/null
+++ b/tests/regression/cgalpngtest/background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/circle-tests-expected.png b/tests/regression/cgalpngtest/circle-tests-expected.png
new file mode 100644
index 0000000..0736af5
--- /dev/null
+++ b/tests/regression/cgalpngtest/circle-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/color-tests-expected.png b/tests/regression/cgalpngtest/color-tests-expected.png
new file mode 100644
index 0000000..207fc5b
--- /dev/null
+++ b/tests/regression/cgalpngtest/color-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/cube-tests-expected.png b/tests/regression/cgalpngtest/cube-tests-expected.png
new file mode 100644
index 0000000..536f220
--- /dev/null
+++ b/tests/regression/cgalpngtest/cube-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/cylinder-tests-expected.png b/tests/regression/cgalpngtest/cylinder-tests-expected.png
new file mode 100644
index 0000000..9d96df2
--- /dev/null
+++ b/tests/regression/cgalpngtest/cylinder-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/difference-tests-expected.png b/tests/regression/cgalpngtest/difference-tests-expected.png
new file mode 100644
index 0000000..e672c48
--- /dev/null
+++ b/tests/regression/cgalpngtest/difference-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/disable-modifier-expected.png b/tests/regression/cgalpngtest/disable-modifier-expected.png
new file mode 100644
index 0000000..550a71d
--- /dev/null
+++ b/tests/regression/cgalpngtest/disable-modifier-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/for-nested-tests-expected.png b/tests/regression/cgalpngtest/for-nested-tests-expected.png
new file mode 100644
index 0000000..e7178c2
--- /dev/null
+++ b/tests/regression/cgalpngtest/for-nested-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/for-tests-expected.png b/tests/regression/cgalpngtest/for-tests-expected.png
new file mode 100644
index 0000000..bf1970a
--- /dev/null
+++ b/tests/regression/cgalpngtest/for-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/highlight-modifier-expected.png b/tests/regression/cgalpngtest/highlight-modifier-expected.png
new file mode 100644
index 0000000..29a4117
--- /dev/null
+++ b/tests/regression/cgalpngtest/highlight-modifier-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/hull2-tests-expected.png b/tests/regression/cgalpngtest/hull2-tests-expected.png
new file mode 100644
index 0000000..256b349
--- /dev/null
+++ b/tests/regression/cgalpngtest/hull2-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/import_dxf-tests-expected.png b/tests/regression/cgalpngtest/import_dxf-tests-expected.png
new file mode 100644
index 0000000..7c8a63e
--- /dev/null
+++ b/tests/regression/cgalpngtest/import_dxf-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/intersection-tests-expected.png b/tests/regression/cgalpngtest/intersection-tests-expected.png
new file mode 100644
index 0000000..6d004b0
--- /dev/null
+++ b/tests/regression/cgalpngtest/intersection-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/intersection_for-tests-expected.png b/tests/regression/cgalpngtest/intersection_for-tests-expected.png
new file mode 100644
index 0000000..dc4c56a
--- /dev/null
+++ b/tests/regression/cgalpngtest/intersection_for-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/linear_extrude-tests-expected.png b/tests/regression/cgalpngtest/linear_extrude-tests-expected.png
new file mode 100644
index 0000000..4ed4361
--- /dev/null
+++ b/tests/regression/cgalpngtest/linear_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/minkowski2-tests-expected.png b/tests/regression/cgalpngtest/minkowski2-tests-expected.png
new file mode 100644
index 0000000..ebdbc74
--- /dev/null
+++ b/tests/regression/cgalpngtest/minkowski2-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/minkowski3-tests-expected.png b/tests/regression/cgalpngtest/minkowski3-tests-expected.png
new file mode 100644
index 0000000..6d74961
--- /dev/null
+++ b/tests/regression/cgalpngtest/minkowski3-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/polygon-tests-expected.png b/tests/regression/cgalpngtest/polygon-tests-expected.png
new file mode 100644
index 0000000..89aed2f
--- /dev/null
+++ b/tests/regression/cgalpngtest/polygon-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png
new file mode 100644
index 0000000..800f7ba
--- /dev/null
+++ b/tests/regression/cgalpngtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/render-tests-expected.png b/tests/regression/cgalpngtest/render-tests-expected.png
new file mode 100644
index 0000000..8087794
--- /dev/null
+++ b/tests/regression/cgalpngtest/render-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/root-modifier-expected.png b/tests/regression/cgalpngtest/root-modifier-expected.png
new file mode 100644
index 0000000..550a71d
--- /dev/null
+++ b/tests/regression/cgalpngtest/root-modifier-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
new file mode 100644
index 0000000..ee60a72
--- /dev/null
+++ b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/sphere-tests-expected.png b/tests/regression/cgalpngtest/sphere-tests-expected.png
new file mode 100644
index 0000000..6ad650f
--- /dev/null
+++ b/tests/regression/cgalpngtest/sphere-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/square-tests-expected.png b/tests/regression/cgalpngtest/square-tests-expected.png
new file mode 100644
index 0000000..8c9bc60
--- /dev/null
+++ b/tests/regression/cgalpngtest/square-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/surface-tests-expected.png b/tests/regression/cgalpngtest/surface-tests-expected.png
new file mode 100644
index 0000000..cc29f66
--- /dev/null
+++ b/tests/regression/cgalpngtest/surface-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/transform-tests-expected.png b/tests/regression/cgalpngtest/transform-tests-expected.png
new file mode 100644
index 0000000..08cfce8
--- /dev/null
+++ b/tests/regression/cgalpngtest/transform-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/union-tests-expected.png b/tests/regression/cgalpngtest/union-tests-expected.png
new file mode 100644
index 0000000..c390f42
--- /dev/null
+++ b/tests/regression/cgalpngtest/union-tests-expected.png
Binary files differ
diff --git a/tests/regression/csgtermtest/allfunctions-expected.txt b/tests/regression/csgtermtest/allfunctions-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/allfunctions-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/allmodules-expected.txt b/tests/regression/csgtermtest/allmodules-expected.txt
new file mode 100644
index 0000000..d7a04fc
--- /dev/null
+++ b/tests/regression/csgtermtest/allmodules-expected.txt
@@ -0,0 +1 @@
+(((((((((((((linear_extrude14 + linear_extrude15) + rotate_extrude16) + rotate_extrude17) + import21) + cube23) + sphere24) + cylinder25) + polyhedron26) + square27) + circle28) + polygon29) + projection30) + surface32)
diff --git a/tests/regression/csgtermtest/assign-expected.txt b/tests/regression/csgtermtest/assign-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/assign-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/child-expected.txt b/tests/regression/csgtermtest/child-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/child-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/circle-expected.txt b/tests/regression/csgtermtest/circle-expected.txt
new file mode 100644
index 0000000..b8c8161
--- /dev/null
+++ b/tests/regression/csgtermtest/circle-expected.txt
@@ -0,0 +1 @@
+circle2
diff --git a/tests/regression/csgtermtest/color-expected.txt b/tests/regression/csgtermtest/color-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/color-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/cube-expected.txt b/tests/regression/csgtermtest/cube-expected.txt
new file mode 100644
index 0000000..3393bc4
--- /dev/null
+++ b/tests/regression/csgtermtest/cube-expected.txt
@@ -0,0 +1 @@
+cube2
diff --git a/tests/regression/csgtermtest/cylinder-expected.txt b/tests/regression/csgtermtest/cylinder-expected.txt
new file mode 100644
index 0000000..f3dd1f9
--- /dev/null
+++ b/tests/regression/csgtermtest/cylinder-expected.txt
@@ -0,0 +1 @@
+cylinder2
diff --git a/tests/regression/csgtermtest/difference-expected.txt b/tests/regression/csgtermtest/difference-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/difference-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/dxf_linear_extrude-expected.txt b/tests/regression/csgtermtest/dxf_linear_extrude-expected.txt
new file mode 100644
index 0000000..bebcc7a
--- /dev/null
+++ b/tests/regression/csgtermtest/dxf_linear_extrude-expected.txt
@@ -0,0 +1 @@
+linear_extrude2
diff --git a/tests/regression/csgtermtest/dxf_rotate_extrude-expected.txt b/tests/regression/csgtermtest/dxf_rotate_extrude-expected.txt
new file mode 100644
index 0000000..318aa70
--- /dev/null
+++ b/tests/regression/csgtermtest/dxf_rotate_extrude-expected.txt
@@ -0,0 +1 @@
+rotate_extrude2
diff --git a/tests/regression/csgtermtest/echo-expected.txt b/tests/regression/csgtermtest/echo-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/echo-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/for-expected.txt b/tests/regression/csgtermtest/for-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/for-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/glide-expected.txt b/tests/regression/csgtermtest/glide-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/glide-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/group-expected.txt b/tests/regression/csgtermtest/group-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/group-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/hull-expected.txt b/tests/regression/csgtermtest/hull-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/hull-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/if-expected.txt b/tests/regression/csgtermtest/if-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/if-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/import-expected.txt b/tests/regression/csgtermtest/import-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/import-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/import_dxf-expected.txt b/tests/regression/csgtermtest/import_dxf-expected.txt
new file mode 100644
index 0000000..847d6c3
--- /dev/null
+++ b/tests/regression/csgtermtest/import_dxf-expected.txt
@@ -0,0 +1 @@
+import2
diff --git a/tests/regression/csgtermtest/import_off-expected.txt b/tests/regression/csgtermtest/import_off-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/import_off-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/import_stl-expected.txt b/tests/regression/csgtermtest/import_stl-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/import_stl-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/intersection-expected.txt b/tests/regression/csgtermtest/intersection-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/intersection-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/intersection_for-expected.txt b/tests/regression/csgtermtest/intersection_for-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/intersection_for-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/linear_extrude-expected.txt b/tests/regression/csgtermtest/linear_extrude-expected.txt
new file mode 100644
index 0000000..bebcc7a
--- /dev/null
+++ b/tests/regression/csgtermtest/linear_extrude-expected.txt
@@ -0,0 +1 @@
+linear_extrude2
diff --git a/tests/regression/csgtermtest/minkowski-expected.txt b/tests/regression/csgtermtest/minkowski-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/minkowski-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/mirror-expected.txt b/tests/regression/csgtermtest/mirror-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/mirror-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/multmatrix-expected.txt b/tests/regression/csgtermtest/multmatrix-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/multmatrix-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/polygon-expected.txt b/tests/regression/csgtermtest/polygon-expected.txt
new file mode 100644
index 0000000..b41dfc6
--- /dev/null
+++ b/tests/regression/csgtermtest/polygon-expected.txt
@@ -0,0 +1 @@
+polygon2
diff --git a/tests/regression/csgtermtest/polyhedron-expected.txt b/tests/regression/csgtermtest/polyhedron-expected.txt
new file mode 100644
index 0000000..5ccc055
--- /dev/null
+++ b/tests/regression/csgtermtest/polyhedron-expected.txt
@@ -0,0 +1 @@
+polyhedron2
diff --git a/tests/regression/csgtermtest/projection-expected.txt b/tests/regression/csgtermtest/projection-expected.txt
new file mode 100644
index 0000000..2a4c47e
--- /dev/null
+++ b/tests/regression/csgtermtest/projection-expected.txt
@@ -0,0 +1 @@
+projection2
diff --git a/tests/regression/csgtermtest/render-expected.txt b/tests/regression/csgtermtest/render-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/render-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/rotate-expected.txt b/tests/regression/csgtermtest/rotate-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/rotate-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/rotate_extrude-expected.txt b/tests/regression/csgtermtest/rotate_extrude-expected.txt
new file mode 100644
index 0000000..318aa70
--- /dev/null
+++ b/tests/regression/csgtermtest/rotate_extrude-expected.txt
@@ -0,0 +1 @@
+rotate_extrude2
diff --git a/tests/regression/csgtermtest/scale-expected.txt b/tests/regression/csgtermtest/scale-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/scale-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/sphere-expected.txt b/tests/regression/csgtermtest/sphere-expected.txt
new file mode 100644
index 0000000..f9b8d73
--- /dev/null
+++ b/tests/regression/csgtermtest/sphere-expected.txt
@@ -0,0 +1 @@
+sphere2
diff --git a/tests/regression/csgtermtest/square-expected.txt b/tests/regression/csgtermtest/square-expected.txt
new file mode 100644
index 0000000..8190c0f
--- /dev/null
+++ b/tests/regression/csgtermtest/square-expected.txt
@@ -0,0 +1 @@
+square2
diff --git a/tests/regression/csgtermtest/subdiv-expected.txt b/tests/regression/csgtermtest/subdiv-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/subdiv-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/surface-expected.txt b/tests/regression/csgtermtest/surface-expected.txt
new file mode 100644
index 0000000..d0e3459
--- /dev/null
+++ b/tests/regression/csgtermtest/surface-expected.txt
@@ -0,0 +1 @@
+surface2
diff --git a/tests/regression/csgtermtest/transform-insert-expected.txt b/tests/regression/csgtermtest/transform-insert-expected.txt
new file mode 100644
index 0000000..847d6c3
--- /dev/null
+++ b/tests/regression/csgtermtest/transform-insert-expected.txt
@@ -0,0 +1 @@
+import2
diff --git a/tests/regression/csgtermtest/translate-expected.txt b/tests/regression/csgtermtest/translate-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/translate-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtermtest/union-expected.txt b/tests/regression/csgtermtest/union-expected.txt
new file mode 100644
index 0000000..a40cf60
--- /dev/null
+++ b/tests/regression/csgtermtest/union-expected.txt
@@ -0,0 +1 @@
+No top-level CSG object
diff --git a/tests/regression/csgtexttest/allfunctions-expected.txt b/tests/regression/csgtexttest/allfunctions-expected.txt
new file mode 100644
index 0000000..331822f
--- /dev/null
+++ b/tests/regression/csgtexttest/allfunctions-expected.txt
@@ -0,0 +1 @@
+group1
diff --git a/tests/regression/csgtexttest/allmodules-expected.txt b/tests/regression/csgtexttest/allmodules-expected.txt
new file mode 100644
index 0000000..34fa5ae
--- /dev/null
+++ b/tests/regression/csgtexttest/allmodules-expected.txt
@@ -0,0 +1 @@
+group1(minkowski2+glide3+subdiv4+hull5+group6+group6+group6+intersection_for9+group6+union11+difference12+intersection13+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group6+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render31+surface+transform33+transform33+transform35+transform33+transform33+color38)
diff --git a/tests/regression/csgtexttest/assign-expected.txt b/tests/regression/csgtexttest/assign-expected.txt
new file mode 100644
index 0000000..a08e59e
--- /dev/null
+++ b/tests/regression/csgtexttest/assign-expected.txt
@@ -0,0 +1 @@
+group1(group2)
diff --git a/tests/regression/csgtexttest/child-expected.txt b/tests/regression/csgtexttest/child-expected.txt
new file mode 100644
index 0000000..331822f
--- /dev/null
+++ b/tests/regression/csgtexttest/child-expected.txt
@@ -0,0 +1 @@
+group1
diff --git a/tests/regression/csgtexttest/circle-expected.txt b/tests/regression/csgtexttest/circle-expected.txt
new file mode 100644
index 0000000..44ca16a
--- /dev/null
+++ b/tests/regression/csgtexttest/circle-expected.txt
@@ -0,0 +1 @@
+group1(circle)
diff --git a/tests/regression/csgtexttest/color-expected.txt b/tests/regression/csgtexttest/color-expected.txt
new file mode 100644
index 0000000..6140174
--- /dev/null
+++ b/tests/regression/csgtexttest/color-expected.txt
@@ -0,0 +1 @@
+group1(color2)
diff --git a/tests/regression/csgtexttest/cube-expected.txt b/tests/regression/csgtexttest/cube-expected.txt
new file mode 100644
index 0000000..07b688d
--- /dev/null
+++ b/tests/regression/csgtexttest/cube-expected.txt
@@ -0,0 +1 @@
+group1(cube)
diff --git a/tests/regression/csgtexttest/cylinder-expected.txt b/tests/regression/csgtexttest/cylinder-expected.txt
new file mode 100644
index 0000000..3f8a41f
--- /dev/null
+++ b/tests/regression/csgtexttest/cylinder-expected.txt
@@ -0,0 +1 @@
+group1(cylinder)
diff --git a/tests/regression/csgtexttest/difference-expected.txt b/tests/regression/csgtexttest/difference-expected.txt
new file mode 100644
index 0000000..551bb16
--- /dev/null
+++ b/tests/regression/csgtexttest/difference-expected.txt
@@ -0,0 +1 @@
+group1(difference2)
diff --git a/tests/regression/csgtexttest/difference-tests-expected.txt b/tests/regression/csgtexttest/difference-tests-expected.txt
new file mode 100644
index 0000000..6bbfe30
--- /dev/null
+++ b/tests/regression/csgtexttest/difference-tests-expected.txt
@@ -0,0 +1 @@
+group1(difference2(sphere-cube))
diff --git a/tests/regression/csgtexttest/dim-all-expected.txt b/tests/regression/csgtexttest/dim-all-expected.txt
new file mode 100644
index 0000000..6c127a8
--- /dev/null
+++ b/tests/regression/csgtexttest/dim-all-expected.txt
@@ -0,0 +1 @@
+group1(group2+group2+group2+group2+group2+group2+group2+group2)
diff --git a/tests/regression/csgtexttest/dxf_linear_extrude-expected.txt b/tests/regression/csgtexttest/dxf_linear_extrude-expected.txt
new file mode 100644
index 0000000..fa1671c
--- /dev/null
+++ b/tests/regression/csgtexttest/dxf_linear_extrude-expected.txt
@@ -0,0 +1 @@
+group1(linear_extrude)
diff --git a/tests/regression/csgtexttest/dxf_rotate_extrude-expected.txt b/tests/regression/csgtexttest/dxf_rotate_extrude-expected.txt
new file mode 100644
index 0000000..452f8e4
--- /dev/null
+++ b/tests/regression/csgtexttest/dxf_rotate_extrude-expected.txt
@@ -0,0 +1 @@
+group1(rotate_extrude)
diff --git a/tests/regression/csgtexttest/echo-expected.txt b/tests/regression/csgtexttest/echo-expected.txt
new file mode 100644
index 0000000..a08e59e
--- /dev/null
+++ b/tests/regression/csgtexttest/echo-expected.txt
@@ -0,0 +1 @@
+group1(group2)
diff --git a/tests/regression/csgtexttest/for-expected.txt b/tests/regression/csgtexttest/for-expected.txt
new file mode 100644
index 0000000..a08e59e
--- /dev/null
+++ b/tests/regression/csgtexttest/for-expected.txt
@@ -0,0 +1 @@
+group1(group2)
diff --git a/tests/regression/csgtexttest/glide-expected.txt b/tests/regression/csgtexttest/glide-expected.txt
new file mode 100644
index 0000000..a792750
--- /dev/null
+++ b/tests/regression/csgtexttest/glide-expected.txt
@@ -0,0 +1 @@
+group1(glide2)
diff --git a/tests/regression/csgtexttest/group-expected.txt b/tests/regression/csgtexttest/group-expected.txt
new file mode 100644
index 0000000..a08e59e
--- /dev/null
+++ b/tests/regression/csgtexttest/group-expected.txt
@@ -0,0 +1 @@
+group1(group2)
diff --git a/tests/regression/csgtexttest/hull-expected.txt b/tests/regression/csgtexttest/hull-expected.txt
new file mode 100644
index 0000000..dc47755
--- /dev/null
+++ b/tests/regression/csgtexttest/hull-expected.txt
@@ -0,0 +1 @@
+group1(hull2)
diff --git a/tests/regression/csgtexttest/if-expected.txt b/tests/regression/csgtexttest/if-expected.txt
new file mode 100644
index 0000000..a08e59e
--- /dev/null
+++ b/tests/regression/csgtexttest/if-expected.txt
@@ -0,0 +1 @@
+group1(group2)
diff --git a/tests/regression/csgtexttest/import-expected.txt b/tests/regression/csgtexttest/import-expected.txt
new file mode 100644
index 0000000..a673ddb
--- /dev/null
+++ b/tests/regression/csgtexttest/import-expected.txt
@@ -0,0 +1 @@
+group1(import)
diff --git a/tests/regression/csgtexttest/import_dxf-expected.txt b/tests/regression/csgtexttest/import_dxf-expected.txt
new file mode 100644
index 0000000..a673ddb
--- /dev/null
+++ b/tests/regression/csgtexttest/import_dxf-expected.txt
@@ -0,0 +1 @@
+group1(import)
diff --git a/tests/regression/csgtexttest/import_off-expected.txt b/tests/regression/csgtexttest/import_off-expected.txt
new file mode 100644
index 0000000..a673ddb
--- /dev/null
+++ b/tests/regression/csgtexttest/import_off-expected.txt
@@ -0,0 +1 @@
+group1(import)
diff --git a/tests/regression/csgtexttest/import_stl-expected.txt b/tests/regression/csgtexttest/import_stl-expected.txt
new file mode 100644
index 0000000..a673ddb
--- /dev/null
+++ b/tests/regression/csgtexttest/import_stl-expected.txt
@@ -0,0 +1 @@
+group1(import)
diff --git a/tests/regression/csgtexttest/import_stl-tests-expected.txt b/tests/regression/csgtexttest/import_stl-tests-expected.txt
new file mode 100644
index 0000000..47c4707
--- /dev/null
+++ b/tests/regression/csgtexttest/import_stl-tests-expected.txt
@@ -0,0 +1 @@
+group1(import_stl)
diff --git a/tests/regression/csgtexttest/intersection-expected.txt b/tests/regression/csgtexttest/intersection-expected.txt
new file mode 100644
index 0000000..0ce0967
--- /dev/null
+++ b/tests/regression/csgtexttest/intersection-expected.txt
@@ -0,0 +1 @@
+group1(intersection2)
diff --git a/tests/regression/csgtexttest/intersection-tests-expected.txt b/tests/regression/csgtexttest/intersection-tests-expected.txt
new file mode 100644
index 0000000..3077e22
--- /dev/null
+++ b/tests/regression/csgtexttest/intersection-tests-expected.txt
@@ -0,0 +1 @@
+group1(intersection2(sphere*cube))
diff --git a/tests/regression/csgtexttest/intersection_for-expected.txt b/tests/regression/csgtexttest/intersection_for-expected.txt
new file mode 100644
index 0000000..b503644
--- /dev/null
+++ b/tests/regression/csgtexttest/intersection_for-expected.txt
@@ -0,0 +1 @@
+group1(intersection_for2)
diff --git a/tests/regression/csgtexttest/linear_extrude-expected.txt b/tests/regression/csgtexttest/linear_extrude-expected.txt
new file mode 100644
index 0000000..fa1671c
--- /dev/null
+++ b/tests/regression/csgtexttest/linear_extrude-expected.txt
@@ -0,0 +1 @@
+group1(linear_extrude)
diff --git a/tests/regression/csgtexttest/linear_extrude-tests-expected.txt b/tests/regression/csgtexttest/linear_extrude-tests-expected.txt
new file mode 100644
index 0000000..f9639c9
--- /dev/null
+++ b/tests/regression/csgtexttest/linear_extrude-tests-expected.txt
@@ -0,0 +1 @@
+group1(linear_extrude+transform4(linear_extrude)+transform7(linear_extrude)+transform10(linear_extrude)+transform13(linear_extrude)+transform16(linear_extrude))
diff --git a/tests/regression/csgtexttest/minkowski-expected.txt b/tests/regression/csgtexttest/minkowski-expected.txt
new file mode 100644
index 0000000..e601bac
--- /dev/null
+++ b/tests/regression/csgtexttest/minkowski-expected.txt
@@ -0,0 +1 @@
+group1(minkowski2)
diff --git a/tests/regression/csgtexttest/mirror-expected.txt b/tests/regression/csgtexttest/mirror-expected.txt
new file mode 100644
index 0000000..80024d2
--- /dev/null
+++ b/tests/regression/csgtexttest/mirror-expected.txt
@@ -0,0 +1 @@
+group1(transform2)
diff --git a/tests/regression/csgtexttest/multmatrix-expected.txt b/tests/regression/csgtexttest/multmatrix-expected.txt
new file mode 100644
index 0000000..80024d2
--- /dev/null
+++ b/tests/regression/csgtexttest/multmatrix-expected.txt
@@ -0,0 +1 @@
+group1(transform2)
diff --git a/tests/regression/csgtexttest/null-polygons-expected.txt b/tests/regression/csgtexttest/null-polygons-expected.txt
new file mode 100644
index 0000000..4afd2e4
--- /dev/null
+++ b/tests/regression/csgtexttest/null-polygons-expected.txt
@@ -0,0 +1 @@
+group1(linear_extrude+linear_extrude)
diff --git a/tests/regression/csgtexttest/polygon-expected.txt b/tests/regression/csgtexttest/polygon-expected.txt
new file mode 100644
index 0000000..e308e30
--- /dev/null
+++ b/tests/regression/csgtexttest/polygon-expected.txt
@@ -0,0 +1 @@
+group1(polygon)
diff --git a/tests/regression/csgtexttest/polygon-illegal-winding-expected.txt b/tests/regression/csgtexttest/polygon-illegal-winding-expected.txt
new file mode 100644
index 0000000..994b16d
--- /dev/null
+++ b/tests/regression/csgtexttest/polygon-illegal-winding-expected.txt
@@ -0,0 +1 @@
+group1(polyhedron)
diff --git a/tests/regression/csgtexttest/polyhedron-expected.txt b/tests/regression/csgtexttest/polyhedron-expected.txt
new file mode 100644
index 0000000..994b16d
--- /dev/null
+++ b/tests/regression/csgtexttest/polyhedron-expected.txt
@@ -0,0 +1 @@
+group1(polyhedron)
diff --git a/tests/regression/csgtexttest/polyset-reduce-crash-expected.txt b/tests/regression/csgtexttest/polyset-reduce-crash-expected.txt
new file mode 100644
index 0000000..93ab028
--- /dev/null
+++ b/tests/regression/csgtexttest/polyset-reduce-crash-expected.txt
@@ -0,0 +1 @@
+group1(transform2(union3(transform4(polygon+polygon+polygon+polygon+polygon+polygon+polygon+polygon)+circle)))
diff --git a/tests/regression/csgtexttest/projection-expected.txt b/tests/regression/csgtexttest/projection-expected.txt
new file mode 100644
index 0000000..38b6abf
--- /dev/null
+++ b/tests/regression/csgtexttest/projection-expected.txt
@@ -0,0 +1 @@
+group1(projection)
diff --git a/tests/regression/csgtexttest/projection-tests-expected.txt b/tests/regression/csgtexttest/projection-tests-expected.txt
new file mode 100644
index 0000000..aa9c079
--- /dev/null
+++ b/tests/regression/csgtexttest/projection-tests-expected.txt
@@ -0,0 +1 @@
+group1(linear_extrude+transform5(linear_extrude)+transform10(linear_extrude))
diff --git a/tests/regression/csgtexttest/render-expected.txt b/tests/regression/csgtexttest/render-expected.txt
new file mode 100644
index 0000000..b53708e
--- /dev/null
+++ b/tests/regression/csgtexttest/render-expected.txt
@@ -0,0 +1 @@
+group1(render2)
diff --git a/tests/regression/csgtexttest/rotate-expected.txt b/tests/regression/csgtexttest/rotate-expected.txt
new file mode 100644
index 0000000..80024d2
--- /dev/null
+++ b/tests/regression/csgtexttest/rotate-expected.txt
@@ -0,0 +1 @@
+group1(transform2)
diff --git a/tests/regression/csgtexttest/rotate_extrude-expected.txt b/tests/regression/csgtexttest/rotate_extrude-expected.txt
new file mode 100644
index 0000000..452f8e4
--- /dev/null
+++ b/tests/regression/csgtexttest/rotate_extrude-expected.txt
@@ -0,0 +1 @@
+group1(rotate_extrude)
diff --git a/tests/regression/csgtexttest/rotate_extrude-tests-expected.txt b/tests/regression/csgtexttest/rotate_extrude-tests-expected.txt
new file mode 100644
index 0000000..452f8e4
--- /dev/null
+++ b/tests/regression/csgtexttest/rotate_extrude-tests-expected.txt
@@ -0,0 +1 @@
+group1(rotate_extrude)
diff --git a/tests/regression/csgtexttest/scale-expected.txt b/tests/regression/csgtexttest/scale-expected.txt
new file mode 100644
index 0000000..80024d2
--- /dev/null
+++ b/tests/regression/csgtexttest/scale-expected.txt
@@ -0,0 +1 @@
+group1(transform2)
diff --git a/tests/regression/csgtexttest/sphere-expected.txt b/tests/regression/csgtexttest/sphere-expected.txt
new file mode 100644
index 0000000..2bf6a72
--- /dev/null
+++ b/tests/regression/csgtexttest/sphere-expected.txt
@@ -0,0 +1 @@
+group1(sphere)
diff --git a/tests/regression/csgtexttest/sphere-tests-expected.txt b/tests/regression/csgtexttest/sphere-tests-expected.txt
new file mode 100644
index 0000000..94323fd
--- /dev/null
+++ b/tests/regression/csgtexttest/sphere-tests-expected.txt
@@ -0,0 +1 @@
+group1(sphere+transform3(sphere)+transform5(sphere)+transform7(sphere))
diff --git a/tests/regression/csgtexttest/square-expected.txt b/tests/regression/csgtexttest/square-expected.txt
new file mode 100644
index 0000000..e235806
--- /dev/null
+++ b/tests/regression/csgtexttest/square-expected.txt
@@ -0,0 +1 @@
+group1(square)
diff --git a/tests/regression/csgtexttest/subdiv-expected.txt b/tests/regression/csgtexttest/subdiv-expected.txt
new file mode 100644
index 0000000..b096229
--- /dev/null
+++ b/tests/regression/csgtexttest/subdiv-expected.txt
@@ -0,0 +1 @@
+group1(subdiv2)
diff --git a/tests/regression/csgtexttest/surface-expected.txt b/tests/regression/csgtexttest/surface-expected.txt
new file mode 100644
index 0000000..7c822d3
--- /dev/null
+++ b/tests/regression/csgtexttest/surface-expected.txt
@@ -0,0 +1 @@
+group1(surface)
diff --git a/tests/regression/csgtexttest/surface-tests-expected.txt b/tests/regression/csgtexttest/surface-tests-expected.txt
new file mode 100644
index 0000000..7c822d3
--- /dev/null
+++ b/tests/regression/csgtexttest/surface-tests-expected.txt
@@ -0,0 +1 @@
+group1(surface)
diff --git a/tests/regression/csgtexttest/transform-insert-expected.txt b/tests/regression/csgtexttest/transform-insert-expected.txt
new file mode 100644
index 0000000..a673ddb
--- /dev/null
+++ b/tests/regression/csgtexttest/transform-insert-expected.txt
@@ -0,0 +1 @@
+group1(import)
diff --git a/tests/regression/csgtexttest/translate-expected.txt b/tests/regression/csgtexttest/translate-expected.txt
new file mode 100644
index 0000000..80024d2
--- /dev/null
+++ b/tests/regression/csgtexttest/translate-expected.txt
@@ -0,0 +1 @@
+group1(transform2)
diff --git a/tests/regression/csgtexttest/union-expected.txt b/tests/regression/csgtexttest/union-expected.txt
new file mode 100644
index 0000000..1884c0f
--- /dev/null
+++ b/tests/regression/csgtexttest/union-expected.txt
@@ -0,0 +1 @@
+group1(union2)
diff --git a/tests/regression/dumptest/allfunctions-expected.txt b/tests/regression/dumptest/allfunctions-expected.txt
new file mode 100644
index 0000000..0a04719
--- /dev/null
+++ b/tests/regression/dumptest/allfunctions-expected.txt
@@ -0,0 +1 @@
+group();
diff --git a/tests/regression/dumptest/allmodules-expected.txt b/tests/regression/dumptest/allmodules-expected.txt
new file mode 100644
index 0000000..e708c43
--- /dev/null
+++ b/tests/regression/dumptest/allmodules-expected.txt
@@ -0,0 +1,39 @@
+group() {
+ minkowski(convexity = 0);
+ glide(path = undef, convexity = 0);
+ subdiv(level = 1, convexity = 0);
+ hull();
+ group();
+ group();
+ group();
+ intersection_for();
+ group();
+ union();
+ difference();
+ intersection();
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ rotate_extrude(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ rotate_extrude(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ group();
+ cube(size = [1, 1, 1], center = false);
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 1);
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 1, r1 = 1, r2 = 1, center = false);
+ polyhedron(points = undef, triangles = undef, convexity = 1);
+ square(size = [1, 1], center = false);
+ circle($fn = 0, $fa = 12, $fs = 1, r = 1);
+ polygon(points = undef, paths = undef, convexity = 1);
+ projection(cut = false, convexity = 0);
+ render(convexity = 1);
+ surface(file = "", center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+ multmatrix([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+ color([-1, -1, -1, 1]);
+}
diff --git a/tests/regression/dumptest/assign-expected.txt b/tests/regression/dumptest/assign-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/assign-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/child-expected.txt b/tests/regression/dumptest/child-expected.txt
new file mode 100644
index 0000000..0a04719
--- /dev/null
+++ b/tests/regression/dumptest/child-expected.txt
@@ -0,0 +1 @@
+group();
diff --git a/tests/regression/dumptest/circle-expected.txt b/tests/regression/dumptest/circle-expected.txt
new file mode 100644
index 0000000..1060d3c
--- /dev/null
+++ b/tests/regression/dumptest/circle-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 1);
+}
diff --git a/tests/regression/dumptest/color-expected.txt b/tests/regression/dumptest/color-expected.txt
new file mode 100644
index 0000000..ef862fc
--- /dev/null
+++ b/tests/regression/dumptest/color-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ color([-1, -1, -1, 1]);
+}
diff --git a/tests/regression/dumptest/cube-expected.txt b/tests/regression/dumptest/cube-expected.txt
new file mode 100644
index 0000000..91dc5fc
--- /dev/null
+++ b/tests/regression/dumptest/cube-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ cube(size = [1, 1, 1], center = false);
+}
diff --git a/tests/regression/dumptest/cylinder-expected.txt b/tests/regression/dumptest/cylinder-expected.txt
new file mode 100644
index 0000000..a26a533
--- /dev/null
+++ b/tests/regression/dumptest/cylinder-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 1, r1 = 1, r2 = 1, center = false);
+}
diff --git a/tests/regression/dumptest/difference-expected.txt b/tests/regression/dumptest/difference-expected.txt
new file mode 100644
index 0000000..09b977f
--- /dev/null
+++ b/tests/regression/dumptest/difference-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ difference();
+}
diff --git a/tests/regression/dumptest/difference-tests-expected.txt b/tests/regression/dumptest/difference-tests-expected.txt
new file mode 100644
index 0000000..bc59e45
--- /dev/null
+++ b/tests/regression/dumptest/difference-tests-expected.txt
@@ -0,0 +1,6 @@
+group() {
+ difference() {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 3);
+ cube(size = [3, 3, 8], center = true);
+ }
+}
diff --git a/tests/regression/dumptest/dim-all-expected.txt b/tests/regression/dumptest/dim-all-expected.txt
new file mode 100644
index 0000000..8d088d9
--- /dev/null
+++ b/tests/regression/dumptest/dim-all-expected.txt
@@ -0,0 +1,10 @@
+group() {
+ group();
+ group();
+ group();
+ group();
+ group();
+ group();
+ group();
+ group();
+}
diff --git a/tests/regression/dumptest/dxf-export-expected.txt b/tests/regression/dumptest/dxf-export-expected.txt
new file mode 100644
index 0000000..692526c
--- /dev/null
+++ b/tests/regression/dumptest/dxf-export-expected.txt
@@ -0,0 +1,17 @@
+group() {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ square(size = [10, 10], center = true);
+ }
+ multmatrix([[1, 0, 0, 30], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[-5, -5], [5, -5], [0, 5]], paths = [[0, 1, 2]], convexity = 1);
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -15], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -6], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ square(size = [12, 12], center = true);
+ }
+ }
+ }
+}
diff --git a/tests/regression/dumptest/dxf_linear_extrude-expected.txt b/tests/regression/dumptest/dxf_linear_extrude-expected.txt
new file mode 100644
index 0000000..fd6535d
--- /dev/null
+++ b/tests/regression/dumptest/dxf_linear_extrude-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/dxf_rotate_extrude-expected.txt b/tests/regression/dumptest/dxf_rotate_extrude-expected.txt
new file mode 100644
index 0000000..2d65d35
--- /dev/null
+++ b/tests/regression/dumptest/dxf_rotate_extrude-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ rotate_extrude(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/echo-expected.txt b/tests/regression/dumptest/echo-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/echo-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/for-expected.txt b/tests/regression/dumptest/for-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/for-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/glide-expected.txt b/tests/regression/dumptest/glide-expected.txt
new file mode 100644
index 0000000..67e40b8
--- /dev/null
+++ b/tests/regression/dumptest/glide-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ glide(path = undef, convexity = 0);
+}
diff --git a/tests/regression/dumptest/group-expected.txt b/tests/regression/dumptest/group-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/group-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/hull-expected.txt b/tests/regression/dumptest/hull-expected.txt
new file mode 100644
index 0000000..6f777b0
--- /dev/null
+++ b/tests/regression/dumptest/hull-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ hull();
+}
diff --git a/tests/regression/dumptest/hull-tests-expected.txt b/tests/regression/dumptest/hull-tests-expected.txt
new file mode 100644
index 0000000..fd204b6
--- /dev/null
+++ b/tests/regression/dumptest/hull-tests-expected.txt
@@ -0,0 +1,46 @@
+group() {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ difference() {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 10);
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 40], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ circle($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 10, r1 = 1, r2 = 1, center = false);
+ }
+ difference() {
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 10, r1 = 1, r2 = 1, center = false);
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 5, r1 = 1, r2 = 1, center = false);
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 40], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ hull() {
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 1, r1 = 10, r2 = 10, center = false);
+ }
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 1, r1 = 10, r2 = 10, center = false);
+ }
+ }
+ }
+}
diff --git a/tests/regression/dumptest/if-expected.txt b/tests/regression/dumptest/if-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/if-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/import-expected.txt b/tests/regression/dumptest/import-expected.txt
new file mode 100644
index 0000000..b424afe
--- /dev/null
+++ b/tests/regression/dumptest/import-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/import_dxf-expected.txt b/tests/regression/dumptest/import_dxf-expected.txt
new file mode 100644
index 0000000..b424afe
--- /dev/null
+++ b/tests/regression/dumptest/import_dxf-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/import_off-expected.txt b/tests/regression/dumptest/import_off-expected.txt
new file mode 100644
index 0000000..b424afe
--- /dev/null
+++ b/tests/regression/dumptest/import_off-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/import_stl-expected.txt b/tests/regression/dumptest/import_stl-expected.txt
new file mode 100644
index 0000000..b424afe
--- /dev/null
+++ b/tests/regression/dumptest/import_stl-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/import_stl-tests-expected.txt b/tests/regression/dumptest/import_stl-tests-expected.txt
new file mode 100644
index 0000000..bc27c6a
--- /dev/null
+++ b/tests/regression/dumptest/import_stl-tests-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import_stl(file = "/Users/kintel/code/metalab/checkout/OpenSCAD/openscad-visitor/testdata/scad/import.stl", cache = "4c34b4bc.2506", convexity = 1);
+}
diff --git a/tests/regression/dumptest/include-test-expected.txt b/tests/regression/dumptest/include-test-expected.txt
new file mode 100644
index 0000000..871e45d
--- /dev/null
+++ b/tests/regression/dumptest/include-test-expected.txt
@@ -0,0 +1,20 @@
+group() {
+ group() {
+ group() {
+ group();
+ }
+ group() {
+ group();
+ }
+ group() {
+ group();
+ }
+ group() {
+ group();
+ }
+ group() {
+ group();
+ }
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 1);
+ }
+}
diff --git a/tests/regression/dumptest/intersection-expected.txt b/tests/regression/dumptest/intersection-expected.txt
new file mode 100644
index 0000000..409e196
--- /dev/null
+++ b/tests/regression/dumptest/intersection-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ intersection();
+}
diff --git a/tests/regression/dumptest/intersection-tests-expected.txt b/tests/regression/dumptest/intersection-tests-expected.txt
new file mode 100644
index 0000000..90349a3
--- /dev/null
+++ b/tests/regression/dumptest/intersection-tests-expected.txt
@@ -0,0 +1,6 @@
+group() {
+ intersection() {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 3);
+ cube(size = [3, 3, 8], center = true);
+ }
+}
diff --git a/tests/regression/dumptest/intersection_for-expected.txt b/tests/regression/dumptest/intersection_for-expected.txt
new file mode 100644
index 0000000..8bb5d2d
--- /dev/null
+++ b/tests/regression/dumptest/intersection_for-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ intersection_for();
+}
diff --git a/tests/regression/dumptest/linear_extrude-expected.txt b/tests/regression/dumptest/linear_extrude-expected.txt
new file mode 100644
index 0000000..fd6535d
--- /dev/null
+++ b/tests/regression/dumptest/linear_extrude-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/linear_extrude-tests-expected.txt b/tests/regression/dumptest/linear_extrude-tests-expected.txt
new file mode 100644
index 0000000..8fe9928
--- /dev/null
+++ b/tests/regression/dumptest/linear_extrude-tests-expected.txt
@@ -0,0 +1,30 @@
+group() {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 10, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ square(size = [10, 10], center = false);
+ }
+ multmatrix([[1, 0, 0, 19], [0, 1, 0, 5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 10, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ }
+ }
+ multmatrix([[1, 0, 0, 31.5], [0, 1, 0, 2.5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 10, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ polygon(points = [[-5, -2.5], [5, -2.5], [0, 2.5]], paths = undef, convexity = 1);
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -12], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, twist = 45, slices = 3, $fn = 0, $fa = 12, $fs = 1) {
+ square(size = [10, 10], center = false);
+ }
+ }
+ multmatrix([[1, 0, 0, 19], [0, 1, 0, -7], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, twist = 90, slices = 7, $fn = 0, $fa = 12, $fs = 1) {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ }
+ }
+ multmatrix([[1, 0, 0, 31.5], [0, 1, 0, -9.5], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, twist = 180, slices = 15, $fn = 0, $fa = 12, $fs = 1) {
+ polygon(points = [[-5, -2.5], [5, -2.5], [0, 2.5]], paths = undef, convexity = 1);
+ }
+ }
+}
diff --git a/tests/regression/dumptest/minkowski-expected.txt b/tests/regression/dumptest/minkowski-expected.txt
new file mode 100644
index 0000000..ecab5f3
--- /dev/null
+++ b/tests/regression/dumptest/minkowski-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ minkowski(convexity = 0);
+}
diff --git a/tests/regression/dumptest/minkowski-tests-expected.txt b/tests/regression/dumptest/minkowski-tests-expected.txt
new file mode 100644
index 0000000..7435769
--- /dev/null
+++ b/tests/regression/dumptest/minkowski-tests-expected.txt
@@ -0,0 +1,66 @@
+group() {
+ multmatrix([[1, 0, 0, -25], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ difference() {
+ square(size = [10, 10], center = false);
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ square(size = [6, 6], center = false);
+ }
+ }
+ circle($fn = 0, $fa = 12, $fs = 1, r = 2);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ difference() {
+ square(size = [10, 10], center = false);
+ square(size = [5, 5], center = false);
+ }
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 25], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ square(size = [10, 10], center = false);
+ circle($fn = 0, $fa = 12, $fs = 1, r = 5);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, -25], [0, 1, 0, 25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ difference() {
+ cube(size = [10, 10, 5], center = false);
+ multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, -2], [0, 0, 0, 1]]) {
+ cube(size = [6, 6, 10], center = false);
+ }
+ }
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 1, r1 = 2, r2 = 2, center = false);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ difference() {
+ cube(size = [10, 10, 5], center = false);
+ cube(size = [5, 5, 5], center = false);
+ }
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 5, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 25], [0, 1, 0, 25], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ minkowski(convexity = 0) {
+ cube(size = [10, 10, 5], center = false);
+ cylinder($fn = 0, $fa = 12, $fs = 1, h = 5, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ }
+}
diff --git a/tests/regression/dumptest/mirror-expected.txt b/tests/regression/dumptest/mirror-expected.txt
new file mode 100644
index 0000000..3ad73cb
--- /dev/null
+++ b/tests/regression/dumptest/mirror-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ multmatrix([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+}
diff --git a/tests/regression/dumptest/multmatrix-expected.txt b/tests/regression/dumptest/multmatrix-expected.txt
new file mode 100644
index 0000000..87e28e2
--- /dev/null
+++ b/tests/regression/dumptest/multmatrix-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+}
diff --git a/tests/regression/dumptest/null-polygons-expected.txt b/tests/regression/dumptest/null-polygons-expected.txt
new file mode 100644
index 0000000..bfceaff
--- /dev/null
+++ b/tests/regression/dumptest/null-polygons-expected.txt
@@ -0,0 +1,6 @@
+group() {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ import_dxf(file = "/Users/kintel/code/metalab/checkout/OpenSCAD/openscad-visitor/testdata/scad/null-polygons.dxf", cache = "4c34b4bc.3124", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+ }
+ linear_extrude(file = "/Users/kintel/code/metalab/checkout/OpenSCAD/openscad-visitor/testdata/scad/null-polygons.dxf", cache = "4c34b4bc.3124", layer = "", height = 100, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/polygon-expected.txt b/tests/regression/dumptest/polygon-expected.txt
new file mode 100644
index 0000000..d04aec9
--- /dev/null
+++ b/tests/regression/dumptest/polygon-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ polygon(points = undef, paths = undef, convexity = 1);
+}
diff --git a/tests/regression/dumptest/polygon-illegal-winding-expected.txt b/tests/regression/dumptest/polygon-illegal-winding-expected.txt
new file mode 100644
index 0000000..3a4c32b
--- /dev/null
+++ b/tests/regression/dumptest/polygon-illegal-winding-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ polyhedron(points = [[0, -10, 60], [0, 10, 60], [0, 10, 0], [0, -10, 0], [60, -10, 60], [60, 10, 60], [10, -10, 50], [10, 10, 50], [10, 10, 30], [10, -10, 30], [30, -10, 50], [30, 10, 50]], triangles = [[0, 2, 3], [0, 1, 2], [0, 4, 5], [0, 5, 1], [5, 4, 2], [2, 4, 3], [6, 8, 9], [6, 7, 8], [6, 10, 11], [6, 11, 7], [10, 8, 11], [10, 9, 8], [0, 3, 9], [9, 0, 6], [10, 6, 0], [0, 4, 10], [3, 9, 10], [3, 10, 4], [1, 7, 11], [1, 11, 5], [1, 7, 8], [1, 8, 2], [2, 8, 11], [2, 11, 5]], convexity = 1);
+}
diff --git a/tests/regression/dumptest/polyhedron-expected.txt b/tests/regression/dumptest/polyhedron-expected.txt
new file mode 100644
index 0000000..c37a529
--- /dev/null
+++ b/tests/regression/dumptest/polyhedron-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ polyhedron(points = undef, triangles = undef, convexity = 1);
+}
diff --git a/tests/regression/dumptest/polyset-reduce-crash-expected.txt b/tests/regression/dumptest/polyset-reduce-crash-expected.txt
new file mode 100644
index 0000000..50253ff
--- /dev/null
+++ b/tests/regression/dumptest/polyset-reduce-crash-expected.txt
@@ -0,0 +1,17 @@
+group() {
+ multmatrix([[0.809017, -0.587785, 0, 0], [0.587785, 0.809017, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ union() {
+ multmatrix([[1, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ polygon(points = [[18.7661, -2.97225], [16.5303, -3.96857], [18.475, -4.43546]], paths = undef, convexity = 1);
+ polygon(points = [[18.7661, -2.97225], [16.7907, -2.65939], [16.5303, -3.96857]], paths = undef, convexity = 1);
+ polygon(points = [[19, 0], [17, 0], [16.7907, -2.65939]], paths = undef, convexity = 1);
+ polygon(points = [[19, 0], [16.7907, -2.65939], [18.7661, -2.97225]], paths = undef, convexity = 1);
+ polygon(points = [[19, 0], [17, 0], [16.7907, 2.65939]], paths = undef, convexity = 1);
+ polygon(points = [[19, 0], [16.7907, 2.65939], [18.7661, 2.97225]], paths = undef, convexity = 1);
+ polygon(points = [[18.7661, 2.97225], [16.7907, 2.65939], [16.5303, 3.96857]], paths = undef, convexity = 1);
+ polygon(points = [[18.7661, 2.97225], [16.5303, 3.96857], [18.475, 4.43546]], paths = undef, convexity = 1);
+ }
+ circle($fn = 0, $fa = 12, $fs = 1, r = 20);
+ }
+ }
+}
diff --git a/tests/regression/dumptest/projection-expected.txt b/tests/regression/dumptest/projection-expected.txt
new file mode 100644
index 0000000..d776c2e
--- /dev/null
+++ b/tests/regression/dumptest/projection-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ projection(cut = false, convexity = 0);
+}
diff --git a/tests/regression/dumptest/projection-tests-expected.txt b/tests/regression/dumptest/projection-tests-expected.txt
new file mode 100644
index 0000000..836c115
--- /dev/null
+++ b/tests/regression/dumptest/projection-tests-expected.txt
@@ -0,0 +1,25 @@
+group() {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ projection(cut = false, convexity = 0) {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ }
+ multmatrix([[1, 0, 0, 22], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ projection(cut = true, convexity = 0) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 9], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "", cache = "0.0", layer = "", height = 20, origin = [ 0 0 ], scale = 1, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ projection(cut = true, convexity = 0) {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 7], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/regression/dumptest/render-expected.txt b/tests/regression/dumptest/render-expected.txt
new file mode 100644
index 0000000..c599c6b
--- /dev/null
+++ b/tests/regression/dumptest/render-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ render(convexity = 1);
+}
diff --git a/tests/regression/dumptest/rotate-expected.txt b/tests/regression/dumptest/rotate-expected.txt
new file mode 100644
index 0000000..87e28e2
--- /dev/null
+++ b/tests/regression/dumptest/rotate-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+}
diff --git a/tests/regression/dumptest/rotate_extrude-expected.txt b/tests/regression/dumptest/rotate_extrude-expected.txt
new file mode 100644
index 0000000..2d65d35
--- /dev/null
+++ b/tests/regression/dumptest/rotate_extrude-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ rotate_extrude(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/rotate_extrude-tests-expected.txt b/tests/regression/dumptest/rotate_extrude-tests-expected.txt
new file mode 100644
index 0000000..068d32c
--- /dev/null
+++ b/tests/regression/dumptest/rotate_extrude-tests-expected.txt
@@ -0,0 +1,7 @@
+group() {
+ rotate_extrude(file = "", cache = "0.0", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1) {
+ multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ circle($fn = 0, $fa = 12, $fs = 1, r = 10);
+ }
+ }
+}
diff --git a/tests/regression/dumptest/scale-expected.txt b/tests/regression/dumptest/scale-expected.txt
new file mode 100644
index 0000000..87e28e2
--- /dev/null
+++ b/tests/regression/dumptest/scale-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+}
diff --git a/tests/regression/dumptest/sphere-expected.txt b/tests/regression/dumptest/sphere-expected.txt
new file mode 100644
index 0000000..bc8bc7b
--- /dev/null
+++ b/tests/regression/dumptest/sphere-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 1);
+}
diff --git a/tests/regression/dumptest/sphere-tests-expected.txt b/tests/regression/dumptest/sphere-tests-expected.txt
new file mode 100644
index 0000000..a672c23
--- /dev/null
+++ b/tests/regression/dumptest/sphere-tests-expected.txt
@@ -0,0 +1,12 @@
+group() {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 5);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 12], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 5, $fs = 0.5, r = 5);
+ }
+ multmatrix([[1, 0, 0, 12], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 0, $fa = 12, $fs = 1, r = 6);
+ }
+ multmatrix([[1, 0, 0, 12], [0, 1, 0, 12], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ sphere($fn = 5, $fa = 12, $fs = 1, r = 6);
+ }
+}
diff --git a/tests/regression/dumptest/square-expected.txt b/tests/regression/dumptest/square-expected.txt
new file mode 100644
index 0000000..a4a24d8
--- /dev/null
+++ b/tests/regression/dumptest/square-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ square(size = [1, 1], center = false);
+}
diff --git a/tests/regression/dumptest/string-test-expected.txt b/tests/regression/dumptest/string-test-expected.txt
new file mode 100644
index 0000000..434cc8f
--- /dev/null
+++ b/tests/regression/dumptest/string-test-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ group();
+}
diff --git a/tests/regression/dumptest/subdiv-expected.txt b/tests/regression/dumptest/subdiv-expected.txt
new file mode 100644
index 0000000..bc40dc2
--- /dev/null
+++ b/tests/regression/dumptest/subdiv-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ subdiv(level = 1, convexity = 0);
+}
diff --git a/tests/regression/dumptest/surface-expected.txt b/tests/regression/dumptest/surface-expected.txt
new file mode 100644
index 0000000..4c9b5b8
--- /dev/null
+++ b/tests/regression/dumptest/surface-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ surface(file = "", center = false);
+}
diff --git a/tests/regression/dumptest/surface-tests-expected.txt b/tests/regression/dumptest/surface-tests-expected.txt
new file mode 100644
index 0000000..9786d85
--- /dev/null
+++ b/tests/regression/dumptest/surface-tests-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ surface(file = "/Users/kintel/code/metalab/checkout/OpenSCAD/openscad-visitor/testdata/scad/surface.dat", center = false);
+}
diff --git a/tests/regression/dumptest/transform-insert-expected.txt b/tests/regression/dumptest/transform-insert-expected.txt
new file mode 100644
index 0000000..758c9e1
--- /dev/null
+++ b/tests/regression/dumptest/transform-insert-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ import_dxf(file = "/Users/kintel/code/OpenSCAD/openscad-visitor/testdata/scad/minimal/transform-insert.dxf", cache = "4e39fca1.4329", layer = "", origin = [ 0 0 ], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 1);
+}
diff --git a/tests/regression/dumptest/translate-expected.txt b/tests/regression/dumptest/translate-expected.txt
new file mode 100644
index 0000000..87e28e2
--- /dev/null
+++ b/tests/regression/dumptest/translate-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]);
+}
diff --git a/tests/regression/dumptest/union-expected.txt b/tests/regression/dumptest/union-expected.txt
new file mode 100644
index 0000000..34f53c2
--- /dev/null
+++ b/tests/regression/dumptest/union-expected.txt
@@ -0,0 +1,3 @@
+group() {
+ union();
+}
diff --git a/tests/regression/opencsgtest/2d-3d-expected.png b/tests/regression/opencsgtest/2d-3d-expected.png
new file mode 100644
index 0000000..47418f0
--- /dev/null
+++ b/tests/regression/opencsgtest/2d-3d-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/assign-tests-expected.png b/tests/regression/opencsgtest/assign-tests-expected.png
new file mode 100644
index 0000000..6fe8e10
--- /dev/null
+++ b/tests/regression/opencsgtest/assign-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/background-modifier-expected.png b/tests/regression/opencsgtest/background-modifier-expected.png
new file mode 100644
index 0000000..24149d0
--- /dev/null
+++ b/tests/regression/opencsgtest/background-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/circle-tests-expected.png b/tests/regression/opencsgtest/circle-tests-expected.png
new file mode 100644
index 0000000..7066eb9
--- /dev/null
+++ b/tests/regression/opencsgtest/circle-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/color-tests-expected.png b/tests/regression/opencsgtest/color-tests-expected.png
new file mode 100644
index 0000000..b2ef8dd
--- /dev/null
+++ b/tests/regression/opencsgtest/color-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/cube-tests-expected.png b/tests/regression/opencsgtest/cube-tests-expected.png
new file mode 100644
index 0000000..8f0a361
--- /dev/null
+++ b/tests/regression/opencsgtest/cube-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/cylinder-tests-expected.png b/tests/regression/opencsgtest/cylinder-tests-expected.png
new file mode 100644
index 0000000..17c10b8
--- /dev/null
+++ b/tests/regression/opencsgtest/cylinder-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/difference-tests-expected.png b/tests/regression/opencsgtest/difference-tests-expected.png
new file mode 100644
index 0000000..d88d98b
--- /dev/null
+++ b/tests/regression/opencsgtest/difference-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/disable-modifier-expected.png b/tests/regression/opencsgtest/disable-modifier-expected.png
new file mode 100644
index 0000000..1e4b471
--- /dev/null
+++ b/tests/regression/opencsgtest/disable-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/for-nested-tests-expected.png b/tests/regression/opencsgtest/for-nested-tests-expected.png
new file mode 100644
index 0000000..c9cdb74
--- /dev/null
+++ b/tests/regression/opencsgtest/for-nested-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/for-tests-expected.png b/tests/regression/opencsgtest/for-tests-expected.png
new file mode 100644
index 0000000..968659d
--- /dev/null
+++ b/tests/regression/opencsgtest/for-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/highlight-modifier-expected.png b/tests/regression/opencsgtest/highlight-modifier-expected.png
new file mode 100644
index 0000000..78d0309
--- /dev/null
+++ b/tests/regression/opencsgtest/highlight-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/hull2-tests-expected.png b/tests/regression/opencsgtest/hull2-tests-expected.png
new file mode 100644
index 0000000..66ee6b2
--- /dev/null
+++ b/tests/regression/opencsgtest/hull2-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/import_dxf-tests-expected.png b/tests/regression/opencsgtest/import_dxf-tests-expected.png
new file mode 100644
index 0000000..6e568c7
--- /dev/null
+++ b/tests/regression/opencsgtest/import_dxf-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/intersection-tests-expected.png b/tests/regression/opencsgtest/intersection-tests-expected.png
new file mode 100644
index 0000000..fc23560
--- /dev/null
+++ b/tests/regression/opencsgtest/intersection-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/intersection_for-tests-expected.png b/tests/regression/opencsgtest/intersection_for-tests-expected.png
new file mode 100644
index 0000000..08c8e06
--- /dev/null
+++ b/tests/regression/opencsgtest/intersection_for-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/linear_extrude-tests-expected.png b/tests/regression/opencsgtest/linear_extrude-tests-expected.png
new file mode 100644
index 0000000..579479e
--- /dev/null
+++ b/tests/regression/opencsgtest/linear_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/minkowski2-tests-expected.png b/tests/regression/opencsgtest/minkowski2-tests-expected.png
new file mode 100644
index 0000000..062f1ed
--- /dev/null
+++ b/tests/regression/opencsgtest/minkowski2-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/minkowski3-tests-expected.png b/tests/regression/opencsgtest/minkowski3-tests-expected.png
new file mode 100644
index 0000000..1f0d572
--- /dev/null
+++ b/tests/regression/opencsgtest/minkowski3-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/polygon-tests-expected.png b/tests/regression/opencsgtest/polygon-tests-expected.png
new file mode 100644
index 0000000..4d88973
--- /dev/null
+++ b/tests/regression/opencsgtest/polygon-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png
new file mode 100644
index 0000000..8239d3d
--- /dev/null
+++ b/tests/regression/opencsgtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/render-tests-expected.png b/tests/regression/opencsgtest/render-tests-expected.png
new file mode 100644
index 0000000..40a1703
--- /dev/null
+++ b/tests/regression/opencsgtest/render-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/root-modifier-expected.png b/tests/regression/opencsgtest/root-modifier-expected.png
new file mode 100644
index 0000000..1e4b471
--- /dev/null
+++ b/tests/regression/opencsgtest/root-modifier-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/rotate_extrude-tests-expected.png b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png
new file mode 100644
index 0000000..96452e1
--- /dev/null
+++ b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/sphere-tests-expected.png b/tests/regression/opencsgtest/sphere-tests-expected.png
new file mode 100644
index 0000000..06161f3
--- /dev/null
+++ b/tests/regression/opencsgtest/sphere-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/square-tests-expected.png b/tests/regression/opencsgtest/square-tests-expected.png
new file mode 100644
index 0000000..4f9db9d
--- /dev/null
+++ b/tests/regression/opencsgtest/square-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/surface-tests-expected.png b/tests/regression/opencsgtest/surface-tests-expected.png
new file mode 100644
index 0000000..2655afb
--- /dev/null
+++ b/tests/regression/opencsgtest/surface-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/transform-tests-expected.png b/tests/regression/opencsgtest/transform-tests-expected.png
new file mode 100644
index 0000000..9c763ba
--- /dev/null
+++ b/tests/regression/opencsgtest/transform-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/union-tests-expected.png b/tests/regression/opencsgtest/union-tests-expected.png
new file mode 100644
index 0000000..caa1467
--- /dev/null
+++ b/tests/regression/opencsgtest/union-tests-expected.png
Binary files differ
diff --git a/tests/test_cmdline_tool.py b/tests/test_cmdline_tool.py
new file mode 100755
index 0000000..b8a0d07
--- /dev/null
+++ b/tests/test_cmdline_tool.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+
+#
+# Regression test driver for cmd-line tools
+#
+# Usage: test_cmdline_tool.py [<options>] <tool> <arguments>
+#
+# If the -g option is given or the TEST_GENERATE environment variable is set to 1,
+# *-expected.<suffix> files will be generated instead of running the tests.
+#
+# Any generated output is written to the file `basename <argument`-actual.<suffix>
+# Any warning or errors are written to stderr.
+#
+# Returns 0 on passed test
+# 1 on error
+# 2 on invalid cmd-line options
+#
+# Author: Marius Kintel <marius@kintel.net>
+#
+
+import sys
+import os
+import glob
+import subprocess
+import re
+import getopt
+
+def initialize_environment():
+ if not options.generate: options.generate = bool(os.getenv("TEST_GENERATE"))
+ return True
+
+def init_expected_filename(testname, cmd):
+ global expecteddir, expectedfilename
+ expecteddir = os.path.join(options.regressiondir, os.path.split(cmd)[1])
+ expectedfilename = os.path.join(expecteddir, testname + "-expected." + options.suffix)
+
+def verify_test(testname, cmd):
+ global expectedfilename
+ if not options.generate:
+ if not os.path.isfile(expectedfilename):
+ print >> sys.stderr, "Error: test '%s' is missing expected output in %s" % (testname, expectedfilename)
+ return False
+ return True
+
+def execute_and_redirect(cmd, params, outfile):
+ proc = subprocess.Popen([cmd] + params, stdout=outfile)
+ retval = proc.wait()
+ return retval
+
+def get_normalized_text(filename):
+ text = open(filename).read()
+ return text.strip("\r\n").replace("\r\n", "\n") + "\n"
+
+def compare_text(expected, actual):
+ return get_normalized_text(expected) == get_normalized_text(actual)
+
+def compare_default(resultfilename):
+ if not compare_text(expectedfilename, resultfilename):
+ execute_and_redirect("diff", [expectedfilename, resultfilename], sys.stderr)
+ return False
+ return True
+
+def compare_png(resultfilename):
+ if execute_and_redirect("diff", [expectedfilename, resultfilename], sys.stderr) != 0:
+ return False
+ return True
+
+def compare_with_expected(resultfilename):
+ if not options.generate:
+ if "compare_" + options.suffix in globals(): return globals()["compare_" + options.suffix](resultfilename)
+ else: return compare_default(resultfilename)
+ return True
+
+def run_test(testname, cmd, args):
+ cmdname = os.path.split(options.cmd)[1]
+
+ outputdir = os.path.join(os.getcwd(), cmdname + "-output")
+ actualfilename = os.path.join(outputdir, testname + "-actual." + options.suffix)
+
+ if options.generate:
+ if not os.path.exists(expecteddir): os.makedirs(expecteddir)
+ outputname = expectedfilename
+ else:
+ if not os.path.exists(outputdir): os.makedirs(outputdir)
+ outputname = actualfilename
+ outfile = open(outputname, "wb")
+ try:
+ proc = subprocess.Popen([cmd] + args, stdout=outfile, stderr=subprocess.PIPE)
+ errtext = proc.communicate()[1]
+ if errtext != None and len(errtext) > 0:
+ print >> sys.stderr, "Error output: " + errtext
+ outfile.close()
+ if proc.returncode != 0:
+ print >> sys.stderr, "Error: %s failed with return code %d" % (cmdname, proc.returncode)
+ return None
+
+ return outputname
+ except OSError, err:
+ print >> sys.stderr, "Error: %s \"%s\"" % (err.strerror, cmd)
+ return None
+
+class Options:
+ def __init__(self):
+ self.__dict__['options'] = {}
+ def __setattr__(self, name, value):
+ self.options[name] = value
+ def __getattr__(self, name):
+ return self.options[name]
+
+def usage():
+ print >> sys.stderr, "Usage: " + sys.argv[0] + " [<options>] <cmdline-tool> <argument>"
+ print >> sys.stderr, "Options:"
+ print >> sys.stderr, " -g, --generate Generate expected output for the given tests"
+ print >> sys.stderr, " -s, --suffix=<suffix> Write -expected and -actual files with the given suffix instead of .txt"
+ print >> sys.stderr, " -t, --test=<name> Specify test name instead of deducting it from the argument"
+
+if __name__ == '__main__':
+ # Handle command-line arguments
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "gs:t:", ["generate", "suffix=", "test="])
+ except getopt.GetoptError, err:
+ usage()
+ sys.exit(2)
+
+ global options
+ options = Options()
+ options.regressiondir = os.path.join(os.path.split(sys.argv[0])[0], "regression")
+ options.generate = False
+ options.suffix = "txt"
+ for o, a in opts:
+ if o in ("-g", "--generate"): options.generate = True
+ elif o in ("-s", "--suffix"):
+ if a[0] == '.': options.suffix = a[1:]
+ else: options.suffix = a
+ elif o in ("-t", "--test"):
+ options.testname = a
+
+ # <cmdline-tool> and <argument>
+ if len(args) < 2:
+ usage()
+ sys.exit(2)
+ options.cmd = args[0]
+
+ # If only one test file, we can usually deduct the test name from the file
+ if len(args) == 2:
+ basename = os.path.splitext(args[1])[0]
+ path, options.testname = os.path.split(basename)
+
+ if not hasattr(options, "testname"):
+ print >> sys.stderr, "Test name cannot be deducted from arguments. Specify test name using the -t option"
+ sys.exit(2)
+
+ # Initialize and verify run-time environment
+ if not initialize_environment(): sys.exit(1)
+
+ init_expected_filename(options.testname, options.cmd)
+
+ # Verify test environment
+ verification = verify_test(options.testname, options.cmd)
+
+ resultfile = run_test(options.testname, options.cmd, args[1:])
+
+ if not verification or not compare_with_expected(resultfile): exit(1)
contact: Jan Huwald // Impressum