Merge branch 'master' into text-module

Conflicts:
	scripts/macosx-build-homebrew.sh
	scripts/mingw-x-build-dependencies.sh
	scripts/uni-build-dependencies.sh
	scripts/uni-get-dependencies.sh
	src/GeometryEvaluator.h
	src/MainWindow.ui
	src/clipper-utils.h
	src/mainwin.cc
	tests/CMakeLists.txt
master
Marius Kintel 2014-04-26 22:08:31 -04:00
commit 2cba2a1b55
276 changed files with 4427 additions and 1264 deletions

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ testdata/scad/features/import_dxf-tests.scad
testdata/scad/features/import_stl-tests.scad testdata/scad/features/import_stl-tests.scad
testdata/scad/misc/include-tests.scad testdata/scad/misc/include-tests.scad
testdata/scad/misc/use-tests.scad testdata/scad/misc/use-tests.scad
/mingw32
/mingw64

View File

@ -8,10 +8,57 @@ before_install:
- sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick - sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
- sudo apt-get install -qq libopencsg-dev - sudo apt-get install -qq libopencsg-dev
env: OPENSCAD_UPLOAD_TESTS=yes
branches: branches:
only: only:
- master - master
before_script:
- echo -e "Host files.openscad.org\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- echo -n $openscad_rsa_{00..40} >> ~/.ssh/openscad_rsa_base64
- base64 --decode --ignore-garbage ~/.ssh/openscad_rsa_base64 > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
script: ./scripts/travis-ci.sh script: ./scripts/travis-ci.sh
env:
global:
- OPENSCAD_UPLOAD_TESTS=yes
- secure: "mBWdnkXe8BL1SqYRBnOOpXn60xtHGfpeVOh+HrK8xZPflLyJLdgAz3jsrgPaaMibfuygOEfn86jm8dFZPDJSeGh+gGbcjbtOywvYtr0d/KJPk2ccCavlGEBsTSdfWVBMH2hqZ+OXrSn4amx12fmcF/rnpRZu0jHxRWrL4SbouXM="
- secure: "gYPUvQekTxYW4bNIDvL6MFdPihd0HFGIq2saEbcOVyGYuZTdyBIzoh3tLIdG6jJNLWb/Nn2iWEsbsSsSIVk9UowW2qmuKVmzOyezKiu7wO2bdk08zeNtZLdFU4f8NsPz/ODXGQBCbjrBr8csnczQ9DKcxQeG2Lr9z1PjpJE43zY="
- secure: "N4tu1zJPq4tDrfArXawiQZ/oAb3jZ1xEonxH2WWMnUXj6CGMSrRbkUD7h7Yx5WcIgxkDE87Q+mMgKDNDhDQuK4RiTngwokXKSCHYn0odo10o1SWiNE42czLpbCTWJeM/5m+nyPhE1Hn8StTwiwqiA2YkBUICh/z+s6cmqV5JaEU="
- secure: "hfkGLUH90vkYIIQlBjAlYRxl/KJE/d5Z8cGnXGN77+RuvmGN2vMspSTXdvIe7KXesLs3ly96iA/mx3LLzW4YQu1qlwaAtffXiX2GBUFgRAHQV4Ty6PJzwRjzTxqZTh2jE+lZukB/6oh0ZlF4lpFYRFIMwLQMP64gGN0aqdQwc0I="
- secure: "UBH3ATAhykSH4su70oG6vmnOfV62ghNDLO/Y5dZwZvZVkKdERdddtpP8tPmJrkeqZp6nZs2DPA1ufgifaShZgv5eo5U1pdg4zFRA8UuOlK51QWdPYdk4/kTbifqeKhiQlNpvMXGnRPfk0a2HdAiaAHk0FKIPBRPAgwJlE03zKf4="
- secure: "YAMu566rDdaAHflMlvBdwkb7F4DNoDa6nJ1/d0bbaPDDk2onxzt5Nos9P3LMMya9XDo/+/8XZWUhdU2t8fwKfKiAanaIPiDYFaS7gZDohu7TI41rqJnnHFF1KNtG624PbABIgKBu8AEHsF1op7EG31SD8ZFyYTBdFOlgVruNWEA="
- secure: "cLVNa1aAtGyO7nHumphZRcNmMnFp+Ts4Bqh6WlclZ3RljEp5rKqHP3knHVgE6T0jC6zMTdNDwKHkyW2jlETRa0l7SGblZI1iuAATnNMwH14FPJgfHP2g30dYu714J6ik6UWUyFNex6o5ZlrVnTnrTuQpTQmF/Ehb4xBcFh780S8="
- secure: "VXa5G1ow1YND0Ap7jTH6oewSlgE3bQz0ZRKPz/bMEQhmImitAW24LG+kLi1RWfvtwJdHBOExlwh/yGpe508WXLF5F/Hv9LKTeAFixUSpFXt1Llsx1/LuApy7i4bq9nmx30Of5GiFvUARTr0wa1a3BbH5E0Sj963vVLwy+FZDiS8="
- secure: "K9pzYAsFc9rvFVz12Y80uFpruH7R5tJpurtGxQJpgSRCr4kRlJO+ODt4/Voq7lzFCiMuhbBiE0G4f8SzylyyiyJaZshAxqRBteEdDEMEnx1ND4FbqDnB9apWnWRoY1AbqBx1ckEy3jCvw1Tn9bRvvvcnOI6qXFxLpTJMP3eY6R4="
- secure: "Oa/blQENesTANzJbqzdQPQ1Hg99DaUVCekms1xkymLBUCSzqPL44KVdz3cxVGYBl/Om7mBwENEQ9BOUI6vGq+CZ7aaTYrYfEUokgvhGUgku0iLjxpHrIH04TH4XwpDXRg/iLwdQZO3d+xq7Q4XTBLOEyHVdzn5uDGP+cNoW9NbM="
- secure: "L3vKi+0gahSm8DF2GGp98FPhuWtUlqRRpCXjAUsSVNCPtru01tI4i6Kls7yqq2L+31FtkyWqBIlm1S1OZw8uiPyis3eUDqMRgASs4tOx/q2Xj2TEK8p+TKK+d3A8mct/y0e806GwdH5JIIppGSPOknPtxT92jZMrv0PnGnPAJSQ="
- secure: "fZzEs4GUtK494s63as9K72kmw0taicfouk2NDym58wHqS88UeRVjvrAkwoIGLJS6rCrh/4NkKhxPkd6JNVRVHy3KH8j2FYoIwtLStEC7YZfT25gi8EiN+O6MrTVre1WA9RT52v5gQ+a2v2YPUU6fBJAy6Xgy372PzoQrXF4iYOA="
- secure: "fNSoCKmJQUJJn4rFxsX0YXHIEtF3JCIixBkM8sCmuzOxjUPFLEjD/5D4x/t+FJfGwb1PR4194L3Tys7AK4IrpIDDXBO8vF3PYK1/evoPLPgj4IkPOReoX2zDUhxsvnEVlF3pGMK6erB6QZoDFCYD6OvqeQUxDDDuEvG8UEvNUgc="
- secure: "apJEmAnp5Ome0UJk+UEsdlVNBOX4bXE9FjBp6yYVrSeAEASFZTZbnIQXP9N8XwFc+TBa5aBlV9s7GZaieVW1/uNZg9njGUsbxPLtqpyHqJTDBgw3edcs7V12jGl+quRJoYVOtbyMQyr5VwCPzV6MmaLOoEtJM75yoSUoerkyNiI="
- secure: "AnXGOWlfhnxEuwqfnir411x/Z2Kvd/R37F8Jcv60+3dugCTLsOL/9i7rY5mwwm2ZFx4uwbCDXEC+UlW/vUhLhvdh8sCYocpghxY+WQye1cib+voqkP6Tgi1AtZL2CxBTJ2Kvyr+kNGTl0TvQC/uv3qI/D+ePijn89KFt9Z2NQEk="
- secure: "KPpkgyXr1mpbNOtxn0NAH2iS0NSy/LC6/DBoR9PBmK80LmHqhQkcB+OAwHy5q4RvRpeNIm2zg49OvV7RgzXQD3NXIy7Eik3uM6IzrcHXlDO6zPye/EAMYt/C0tJylR6ESDfp1+pEI0VyFz6pjFFs7FAX5m9UinaEJns7vucb8SA="
- secure: "Hf1TNzMMPw7msFCttHoF0V2wOFQERbGwYsZSMUac8njBXDjbsP+S/iYNkqoqJIfBjknXbn1U0RDfiDwvs/71lhV0FEoF2zPbOWitO7+ZeqpNcP2oMqnPBLCuCldKWxbsjKLwE5Qgd9KflPh49ZlnHe/cZZdzeDCIpAmHk+Ey/4U="
- secure: "cG7qndC1YAHa05QSLD8CzAZJOyJsHj4+I8OKvv+nfKFO/pQVZzKfF84v0ZVxpqdFNRRUwdH4n7zYCNAaMSoeSQmk++jyf+w8cu+x1MZ9HcnfqduuyirZR5iehmlIy1v/c1CsxAQeovDMulnFJOXGf7q7tUqB3rw/WlZZHn6Fpfc="
- secure: "NHym8MhLToBE5dlldCKTBn8NuLQg9jhjdB/9Gnn0h5j0G3x3O9yx3XbBtVSKNdvtqfGog6NG6XT2UhCYGtv59lTcWVR5tPulYkJV1vqeHfY21QeDYIESr/JOZaAIZ0RQaT/jWnoIghQNe38VMozWdwZ3ulKQ2HnzFagTbftfF5A="
- secure: "mClAXQOm9gc7UXuXpyqfNWp7JJU4n6n3pd792VK8K8ioXKVSySZLbJOFul5KY8ZLePCvdliZ0yfKqyBR7XqbzPFwH/0R3pppaI+T/0mZRi5SqcJ9KnJdf7jkTLsQI73wU25YC/T6ZHodEfANYTaP06Ex7QnY/djWsTvAH3Ga2dA="
- secure: "BdYq/BztHLp4TGExfjAxRW9Qtnpi7aizY6fyQNqOBorF6aYKNLOaaN8i7bURL7I5B+WT2vR/NFDv8Ehz096HFjnDZ8vVFn4tQQgGlxf+BcYrBOeA+AIxA9XR/yp9v6ljONasD7PQ8uDJoyUy9FOimet6T5GAOTy8LO8SRQDvxoY="
- secure: "iHb3IwbyrCKW884oVvckz7BR8F9YhLQXkEUvufc/5oKtsn/zMflHQ1+OV04q4j7B5hdu9/ur8T0q80YysJL8btHT0vnR7kx/uDx/S8tqXlMMb0Tr1JUGLFIHBZWRPUCayXTcBk06BrYRA1wB8cLQnLxqI4OfgMAN+rOv60CGR8k="
- secure: "XzK4KXCtcu5B1Flyiq9jl7hxnsUXXTYcmSq7bmIM4g8dsRz6EdDWrSBJUIstKCLckmoeQcfyCyXYjy2dvGgv7FX+Hu1nNGCZGy/rOIaciiinHf9mQZVTTwUZv1gGPZo2jdue15NvvCLwJt/N6SSZYor8fRyPyuVnK8/PBj4jcao="
- secure: "YFEbESe4qlFKXsgYgST3Z9NRMyNEj2oVYEzgcB+dWsVnfDufhtjUhQ+AGw+vsbYg2g2TIQzyVSWMG+mQo41SX4bLFH0Rwgial4ZnpWP7NHrkCgjIeeZRP/f81KIdGnCyabatOkJPQQ5qS0cZVRpQaXq8Zsxk+aDmYc2kZwWA3xc="
- secure: "Tv1OUgeQDBPwnL5GsEBiKjr1HdMrvsedk5I6ckbpBa6S1dKCzirOJRRHr9ZP4/TiZYEBd84nQZ5fKTIrCxU18CQzrs/gsS2hHyN+Nr8cpWpa6IovKOpMKxCYryLJFpX69cihcOVzp1xQq+a7sZq/yhZJmKaUBFlZchyPtJ1ysmg="
- secure: "iYz1GPvgcYpue6CKaW/O8iEolwC0UFztsZWzJZyrpZGrsw0fZbqpcn9ACEc+LQKYmvTo3hdBj9ewZB4+Bj4VMVIjgTlYsP0rYESsPd7njgpll+WfslRd5WNJLioUiPeb29yC4woiGa+/YHy2ACJI+ix7F6NyHk+2ri0MCOa4BnQ="
- secure: "Xwco3s7JM7hsAuDrAKuaYBjMccNTKWCXsoE48kQsKGlBM/iQ849nGTmImIKvdh0tZG5LDXgc04rbldhnpvMfWmty0hrQZuwjPaHBqHQ7zWXeNl3/CpAIGTBsefO7hTRoT2+xw1I6f4n8a5U7FICaGmWkR/MvykPOJyR+ms/hOQM="
- secure: "lNW2JDlel5MC9kwRu9lvNPWdlkbhAmkLo/YovVsrKyXPZfETWZO6IU2dDfKQvetIB2zQQnplbozdyiRBMywBnAk27FqLRAjbBqlKTC1jPlU4W29SBCMf+kJlVcQHvh0feeJChBZrVb2GQIAiHEkloDFX03E7RhOPAE5GqzvYow0="
- secure: "ZCfE5sS+70M4MVDOxVMlYcge7LewYbHIm0DE7QFfkwJ9sobaVr+uQMWe+9bA3DhxhdvQQ9p6lGE36ST+WYWopO7SHW6Ly+WvH1kM1nOZfY4w5R+BlAJ9QPcudnPUuFMOBXM0Wm8XccPuld2D5Jg7SalmS1F6mdArXsuL55CXxco="
- secure: "G4MDFYa3BbzqUGMfrABOjEjIa/pXHR8/jgOIQxY3FK2/0bYr//2YU/bw1gJBPQc8Ef243LzhTKvfFhntTecSfH2pS0Ezm2HWwtaF+lBg/A5bYCJX5Tek6HAOv1+aajTRZAjjj4X28T0NCqJI6u1WvbHwYFmW8wsfZtYZKB8JFuo="
- secure: "Py01+IBxGWKG7lyV10bZPYnqvLxNNBE+L6i1hvvvQmx078LfKtVr3tdDSZJVjf3H+gMXW0qnEhBYWJlzKPO5zCMhX314s43yxW3a5N+QZ74ucCO0Yvc4JXA8y7+g4lgsihJmzLSGQZziT+xnn0Q4iNpB8G0kXcl7zVT/ZsLFooQ="
- secure: "lkkLCzuHhR1Z3zzV/8me/PbzMConA8kdxUQ6mQb4VZE8h3wsRDh9P8yKciluV/ZVgqZtLG8VbVnLHgVzzu2zDTqf1t/Gsb5u8Rq5w/sTZyqMpB9qpRjHmq+uPds+ScvAplyoPH4oskXS8xQ8G1ykJwPBbRETyZzAU2/JoOgWiOs="
- secure: "KQlczojVt3jfMtsnl1GFiKsCxkPOaIr95MxopGYqqOVxzRDeNvtJcA/xQU1ouibJlx9B/EHZx0K+/DIk6dyr+irAVzgPGdT5pRxmjSREONPDaGEj9f4D6GWS3AtUJW2AqTMdtI3+d6o7oaYFGGXTCeW+qFMcsxEpn3eT1HMJs1A="
- secure: "XeqXuqqHPAW6/5WukY8y3n1PmaP7ASuNvMTd/Xv3sXzcci11ZKWNEOQDxZeRUwuEot4v2WG+m4HuvIqGWbz6mi8+4OaWUynLRitlGWj7JBu1igN9awmOw+HOcM32v2UcxUdsUSB5AdEOs7hafjodbshxrWQbo05ruk6Au4+jrd4="
- secure: "Ej8RRGUT95llMPDUtbx/lF7D9bKGxTCGdKZbTKFdIPjUzTJgE0pm89vIxKvq7y//oMCposX5DjcVolF+dS2Ya2031MTUto3mUwFq+sx3t2knWt812mQ6p2Ow/l6/uOlr2FHxUJKOxPeWqLRB6NYd9YnUiC3OJNlERr8nyHkCx4c="
- secure: "JsOTX5JsRAY3zyKa/5yYkd48Nejt9CAEraXVpN1NDW25jJmsOynyW5d2WYazJ1H0x1oR6Fhc4gstNxaj1MNh/gVM9O3UM2tyFnSxDY6fZnEGWaf17ln4dqi3KHXWU7h2Gdg1ah19NS0nWytooW6VcEXmk+cYSheTqyfc5gK7Sdo="
- secure: "mMc9MyopIjvzpDg3eN6owSmr2PI58JiPnlLhMNvOWW9axUzup5tthfqM+tvR/AqdLQSSMUC9JXwwQK0d543Q4YyoQ0jwVY2RT56VdEywxD5+yWRfXD+ANlJhdQWmlPVc3KsavKYmfQPBLbwe0nyhtQTWGeAgKTYvYT+k1/PD4rg="
- secure: "X7KDSiSOR3XcePJgTXNzwE1wU285yFsxB7crMLskD08wU8xdRqS8NL+1++/Lju6pypOkospI2AYH1JAJ7JK3Sx5QYM4MxgRJcrMHiTMirN3cm3KzkWUuv3iEZNJ7q6ANi5oFfHh3k0D4JhCEnA1ICTDPdq+r9+mOvgkrly8V0Dw="

View File

@ -1,4 +1,4 @@
![Travis CI](https://api.travis-ci.org/openscad/openscad.png) [![Travis CI](https://api.travis-ci.org/openscad/openscad.png)](https://travis-ci.org/openscad/openscad)
# What is OpenSCAD? # What is OpenSCAD?
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=openscad&url=http://openscad.org&title=OpenSCAD&language=&tags=github&category=software) [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=openscad&url=http://openscad.org&title=OpenSCAD&language=&tags=github&category=software)
@ -89,9 +89,9 @@ Follow the instructions for the platform you're compiling on below.
* [Qt4 (4.4 - 4.8)](http://www.qt.nokia.com/) * [Qt4 (4.4 - 4.8)](http://www.qt.nokia.com/)
* [CGAL (3.6 - 4.1)](http://www.cgal.org/) * [CGAL (3.6 - 4.1)](http://www.cgal.org/)
* [GMP (5.x)](http://www.gmplib.org/) * [GMP (5.x)](http://www.gmplib.org/)
* [cmake (2.8, required by CGAL and the test framework)](http://www.cmake.org/)
* [MPFR (3.x)](http://www.mpfr.org/) * [MPFR (3.x)](http://www.mpfr.org/)
* [boost (1.35 - 1.53)](http://www.boost.org/) * [cmake (2.8, required by CGAL and the test framework)](http://www.cmake.org/)
* [boost (1.35 - 1.55)](http://www.boost.org/)
* [OpenCSG (1.3.2)](http://www.opencsg.org/) * [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/) * [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/) * [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/)
@ -153,7 +153,7 @@ the dependency packages listed above using your system's package
manager. A convenience script is provided that can help with this manager. A convenience script is provided that can help with this
process on some systems: process on some systems:
./scripts/uni-get-dependencies.sh sudo ./scripts/uni-get-dependencies.sh
After installing dependencies, check their versions. You can run this After installing dependencies, check their versions. You can run this
script to help you: script to help you:
@ -177,17 +177,9 @@ Then run the script to compile all the prerequisite libraries above:
./scripts/uni-build-dependencies.sh ./scripts/uni-build-dependencies.sh
This may take an hour or more, depending on your network and system. It Note that huge dependencies like gcc, qt, or glib2 are not included
is recommended to have at least 1 gigabyte of free disk space. As a here, only the smaller ones (boost, CGAL, opencsg, etc). After the
special timesaver if you are only missing CGAL and OpenCSG, you can do build, again check dependencies.
this instead:
./scripts/uni-build-dependencies.sh opencsg
./scripts/uni-build-dependencies.sh cgal
Note that huge dependencies like gcc or qt are not included here, only
the smaller ones (boost, CGAL, opencsg, etc). After the build, again
check dependencies.
./scripts/check-dependencies.sh ./scripts/check-dependencies.sh

View File

@ -1,247 +1,240 @@
OpenSCAD YYYY.MM
================
Language Features: # OpenSCAD 2014.03
o Added diameter argument: circle(d), cylinder(d, d1, d2) and sphere(d)
o Added parent_module() and $parent_modules
o Added children() as a replacement for child()
o FIXME: Unicode support
o FIXME: Ranges with negative steps
o FIXME: Experimental concat()
Program Features: **Language Features:**
o Added --info parameter to the cmd-line for system/library info * Added diameter argument: circle(d), cylinder(d, d1, d2) and sphere(d)
o Added --enable parameter to enable experimental features * Added parent_module() and $parent_modules
o Added Reset View in GUI * Added children() as a replacement for child()
o Added Feature tab in Preferences * Unicode strings (using UTF-8) are now correctly handled
* Ranges can have a negative step value
* Added norm() and cross() functions
Bugfixes: **Program Features:**
o polyhedron() is now much more robust handling almost planar polygons * Cmd-line: --info parameter prints system/library info
o Automatic reloads of large designs are more robust * Cmd-line: --csglimit parameter to change CSG rendering limit
o Boolean logic in if() statements are now correctly short-circuited * Cmd-line: Better handling of cmd-line arguments under Windows
o rands() with zero range caused an infinite loop * GUI: Added Reset View
o resize(, auto=true) didn't work when shrinking objects * GUI: Added Search&Replace in editor
o The $children variable sometimes misbehaved due to dynamic scoping * GUI: Syntax highlighting now has a dark background theme
o The --camera cmd-line option behaved differently then the corresponding GUI function * GUI: We now create a backup file before rendering to allow for recovery if OpenSCAD crashes/freezes
o PNG export now doesn't leak transparency settings into the target image * GUI: Accessibility features enabled (e.g. screenreading)
o Improved performance of OpenCSG (F5) compilation in some cases
o Some editor misbehaviors were fixed
o Stability fixes of CGAL-related crashes
Deprecations: **Bugfixes/improvements:**
o child() is no longer supported. Use children() instead. * Reading empty STL files sometimes caused a crash
o polyhedron(triangles=[...]): Use polyhedron(faces=[...]) instead. * OPENSCADPATH now uses semicolon as path separator under Windows
* polyhedron() is now much more robust handling almost planar polygons
* Automatic reloads of large designs are more robust
* Boolean logic in if() statements are now correctly short-circuited
* rands() with zero range caused an infinite loop
* resize(, auto=true) didn't work when shrinking objects
* The $children variable sometimes misbehaved due to dynamic scoping
* The --camera cmd-line option behaved differently then the corresponding GUI function
* PNG export now doesn't leak transparency settings into the target image
* Improved performance of 3D hull() operations
* Some editor misbehaviors were fixed
* Stability fixes of CGAL-related crashes
* Windows cmd-line can now handle spaces in filenames
* Default CSG rendering limit is now 100K elements
* Fixed a crash reading DXF files using comma as decimal separator
* Fixed a crash running the cmd-line without a HOME env. variable
* Intersecting something with nothing now correctly results in an empty object
Misc: **Deprecations:**
o We now use CGAL's EPEC kernel * child() is no longer supported. Use children() instead.
o Additional output formats: .ast, .term, null (these are most useful for testing) * polyhedron(triangles=[...]): Use polyhedron(faces=[...]) instead.
o Test framework now shares more code with the GUI app
o Test report can now be automatically uploaded to dinkypage.com
o Better compatibility with BSD systems
OpenSCAD 2013.06 **Misc:**
================ * Test framework now shares more code with the GUI app
* Test report can now be automatically uploaded to dinkypage.com
* Better compatibility with BSD systems
* Qt5 support
Language Features: # OpenSCAD 2013.06
o linear_extrude now takes a scale parameter:
**Language Features:**
* linear_extrude now takes a scale parameter:
linear_extrude(height=a, slices=b, twist=c, scale=[x,y]) linear_extrude(height=a, slices=b, twist=c, scale=[x,y])
o Recursive use of modules is now supported (including cascading child() operations): * Recursive use of modules is now supported (including cascading child() operations):
https://github.com/openscad/openscad/blob/master/examples/example024.scad https://github.com/openscad/openscad/blob/master/examples/example024.scad
o Parameter list values can now depend on earlier values, e.g. for (i=[0:2], j=[0:i]) .. * Parameter list values can now depend on earlier values, e.g. for (i=[0:2], j=[0:i]) ..
o value assignments in parameters can now depend on already declared parameters * value assignments in parameters can now depend on already declared parameters
o Added resize() module: * Added resize() module:
http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#resize http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#resize
Program Features: **Program Features:**
o Added basic syntax highlighting in the editor * Added basic syntax highlighting in the editor
o There is now a built-in library path in user-space: * There is now a built-in library path in user-space:
http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Library_Locations http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Library_Locations
o Commandline output to PNG, with various camera and rendering settings. * Commandline output to PNG, with various camera and rendering settings.
Run openscad -h to see usage info or see the OpenSCAD wiki user manual. Run openscad -h to see usage info or see the OpenSCAD wiki user manual.
o Attempting to open dxf, off or stl files in the GUI will now create an import statement. * Attempting to open dxf, off or stl files in the GUI will now create an import statement.
o The preview operator (%) will now preserve any manually set color * The preview operator (%) will now preserve any manually set color
o The highlight operator (#) will now color the object in transparent red * The highlight operator (#) will now color the object in transparent red
o Mac: Added document icon * Mac: Added document icon
o Mac: Added auto-update check * Mac: Added auto-update check
o Windows: Better cmd-line support using the openscad.com executable * Windows: Better cmd-line support using the openscad.com executable
Bugfixes: **Bugfixes:**
o Importing files is now always relative to the importing script, also for libraries * Importing files is now always relative to the importing script, also for libraries
o We didn't always print a warning when CSG normalization created too many elements * We didn't always print a warning when CSG normalization created too many elements
o Binary STLs can now be read on big endian architectures * Binary STLs can now be read on big endian architectures
o Some binary STLs couldn't be read * Some binary STLs couldn't be read
o Fixed some issues related to ARM builds * Fixed some issues related to ARM builds
o CGAL triangulation more lenient- enables partial rendering of 'bad' DXF data * CGAL triangulation more lenient- enables partial rendering of 'bad' DXF data
o The Automatic Reload feature is now more robust * The Automatic Reload feature is now more robust
o If a file couldn't be saved it no longer fails silently * If a file couldn't be saved it no longer fails silently
o Fixed a number of crashes related to CGAL and OpenCSG rendering or complex models * Fixed a number of crashes related to CGAL and OpenCSG rendering or complex models
o The lookup() function had bad boundary condition behavior * The lookup() function had bad boundary condition behavior
o The surface() module failed when the .dat file lacked a trailing newline * The surface() module failed when the .dat file lacked a trailing newline
o The hull() module could crash if any of the children were empty objects * The hull() module could crash if any of the children were empty objects
o Some problems using unicode filenames have been fixed * Some problems using unicode filenames have been fixed
Misc: **Misc:**
o Build scripts have been further improved * Build scripts have been further improved
o Regression test now creates single monolithic .html file for easier uploading * Regression test now creates single monolithic .html file for easier uploading
o Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine * Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine
o The backend is finally independent of Qt * The backend is finally independent of Qt
o Windows: We now have a 64-bit version * Windows: We now have a 64-bit version
Known Bugs: **Known Bugs:**
o Linux: command-line png rendering on Gallium is flaky. * Linux: command-line png rendering on Gallium is flaky.
Workaround: use CGAL --render or hardware rendering. Workaround: use CGAL --render or hardware rendering.
# OpenSCAD 2013.01
OpenSCAD 2013.01 **Features:**
================ * Snappier GUI while performing CGAL computations (computations running in separate thread)
* The size of the misc. caches can now be adjusted from Preferences
* The limit for when to disable OpenCSG can now be adjusted from Preferences
* Added Dot product operator: vec * vec
* Added Matrix multiplication operator: vec * mat, mat * mat
* Added search() function
* Dependencies are now tracked - any changes in uses/included files will be detected and cause a recompile
* The OPENSCADPATH environment variable is now implemented will have precedence when searching for libraries
* .csg files can now be opened from the GUI
* linear_extrude() will now assume that the first parameter means 'height' if it's a number
Features: **Bugfixes:**
o Snappier GUI while performing CGAL computations (computations running in separate thread) * use'ing an non-existing file sometimes crashed under Windows
o The size of the misc. caches can now be adjusted from Preferences * Better font handling: Ensure a monospace font is chosen as default
o The limit for when to disable OpenCSG can now be adjusted from Preferences * Division by zero caused hang in some cases (e.g. sin(1/0))
o Added Dot product operator: vec * vec * Larger minkowski operations sometimes caused a crash after a CGAL assert was thrown
o Added Matrix multiplication operator: vec * mat, mat * mat * Fixed crashes in shared_ptr.hpp (or similar places) due bugs in cache management and CSG normalization
o Added search() function * scale() with a scale factor of zero could cause a crash
o Dependencies are now tracked - any changes in uses/included files will be detected and cause a recompile * Fixed a number of issues related to use/include
o The OPENSCADPATH environment variable is now implemented will have precedence when searching for libraries * Providing an unknown parameter on the cmd-line caused a crash
o .csg files can now be opened from the GUI * cmd-line overrides using -D now also work for USEd modules
o linear_extrude() will now assume that the first parameter means 'height' if it's a number * Modifier characters can now be used in front of if statements
* rotate() with a vector argument with less that 3 elements used uninitialized variables, ending up being non-deterministic.
* .csg files will now have relative filenames whenever possible
* Don't just ignore geometric nodes having zero volume/area - when doing difference/intersection, they tend to turn negative objects into positive ones.
* Always use utf-8 file encoding, also under Windows
* A lot of build script fixes
* Some other crash bugs fixes
Bugfixes: **Deprecations:**
o use'ing an non-existing file sometimes crashed under Windows * The old include syntax "<filename.scad>" without the include keyword is no
o Better font handling: Ensure a monospace font is chosen as default
o Division by zero caused hang in some cases (e.g. sin(1/0))
o Larger minkowski operations sometimes caused a crash after a CGAL assert was thrown
o Fixed crashes in shared_ptr.hpp (or similar places) due bugs in cache management and CSG normalization
o scale() with a scale factor of zero could cause a crash
o Fixed a number of issues related to use/include
o Providing an unknown parameter on the cmd-line caused a crash
o cmd-line overrides using -D now also work for USEd modules
o Modifier characters can now be used in front of if statements
o rotate() with a vector argument with less that 3 elements used uninitialized variables, ending up being non-deterministic.
o .csg files will now have relative filenames whenever possible
o Don't just ignore geometric nodes having zero volume/area - when doing difference/intersection, they tend to turn negative objects into positive ones.
o Always use utf-8 file encoding, also under Windows
o A lot of build script fixes
o Some other crash bugs fixes
Deprecations:
o The old include syntax "<filename.scad>" without the include keyword is no
longer supported and will cause a syntax error. longer supported and will cause a syntax error.
OpenSCAD 2011.12 # OpenSCAD 2011.12
================
Features: **Features:**
o The MCAD library is now bundled with OpenSCAD * The MCAD library is now bundled with OpenSCAD
o Added len() function. Takes one vector or string parameter and returns its length. * Added len() function. Takes one vector or string parameter and returns its length.
o The index operator [] now works on strings * The index operator [] now works on strings
o The version() function will return the OpenSCAD version as a vector, e.g. [2011, 09] * The version() function will return the OpenSCAD version as a vector, e.g. [2011, 09]
o The version_num() function will return the OpenSCAD version as a number, e.g. 20110923 * The version_num() function will return the OpenSCAD version as a number, e.g. 20110923
o hull() Now supports 3D objects * hull() Now supports 3D objects
o hull() with 2D object can now use for loops and boolean operations as children * hull() with 2D object can now use for loops and boolean operations as children
o New import() statement reads the correct file format based on the filename extension * New import() statement reads the correct file format based on the filename extension
(.stl, .dxf and .off is supported) (.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) * 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") * The color() statement now supports specifying colors as strings, e.g. color("Red")
o The color() statement now overrides colors specified further down in the tree * The color() statement now overrides colors specified further down in the tree
o if()/else() and the ternary operator can now take any value type as parameter. false, 0, empty string and empty vector or illegal value type will evaluate as false, everything else as true. * if()/else() and the ternary operator can now take any value type as parameter. false, 0, empty string and empty vector or illegal value type will evaluate as false, everything else as true.
o Strings can now be lexographically compared using the <, <=, >, >= operators * Strings can now be lexographically compared using the <, <=, >, >= operators
o Added PI constant. * Added PI constant.
o Number literals in scientific notation are now accepted by the parser * Number literals in scientific notation are now accepted by the parser
o Added import and export of the OFF file format * Added import and export of the OFF file format
o Now uses standard shortcuts for save, reload and quit on Linux and Windows. F2/F3 will still work but is deprecated. * Now uses standard shortcuts for save, reload and quit on Linux and Windows. F2/F3 will still work but is deprecated.
Bugfixes: **Bugfixes:**
o Complex CSG models sometimes took extremely long time to normalize before OpenCSG preview * Complex CSG models sometimes took extremely long time to normalize before OpenCSG preview
o square() crashed if any of the dimensions were zero * square() crashed if any of the dimensions were zero
o Flush Caches didn't flush cached USE'd modules * Flush Caches didn't flush cached USE'd modules
o STL export should be a bit more robust * STL export should be a bit more robust
o Dropping a file into the editor under Windows didn't work (double C:/C:/ problem) * Dropping a file into the editor under Windows didn't work (double C:/C:/ problem)
o On some platforms it was possible to insertion rich text in the editor, causing confusion. * On some platforms it was possible to insertion rich text in the editor, causing confusion.
o Less crashes due to CGAL assertions * Less crashes due to CGAL assertions
o OpenCSG should now work on systems with OpenGL 1.x, given that the right extensions are available * OpenCSG should now work on systems with OpenGL 1.x, given that the right extensions are available
o include now searches librarydir * include now searches librarydir
o The $fs parameter yielded only half the number of segments it should have * The $fs parameter yielded only half the number of segments it should have
o surface(center=true) is now correctly centered in the XY plane * surface(center=true) is now correctly centered in the XY plane
Deprecations: **Deprecations:**
o dxf_linear_extrude() and dxf_rotate_extrude() are now deprecated. * dxf_linear_extrude() and dxf_rotate_extrude() are now deprecated.
Use linear_extrude() and rotate_extrude() instead. Use linear_extrude() and rotate_extrude() instead.
o The file, layer, origin and scale parameters to linear_extrude() and rotate_extrude() * The file, layer, origin and scale parameters to linear_extrude() and rotate_extrude()
are now deprecated. Use an import() child instead. are now deprecated. Use an import() child instead.
o import_dxf(), import_stl() and import_off() are now deprecated. Use import() instead. * import_dxf(), import_stl() and import_off() are now deprecated. Use import() instead.
o When exporting geometry from the cmd-line, use the universal -o option. It will export to the correct file format based on the given suffix (dxf, stl, off). The -x and -s parameters are still working but deprecated. * When exporting geometry from the cmd-line, use the universal -o option. It will export to the correct file format based on the given suffix (dxf, stl, off). The -x and -s parameters are still working but deprecated.
o F2 and F3 for Save and Reload is now deprecated * F2 and F3 for Save and Reload is now deprecated
# OpenSCAD 2011.06
OpenSCAD 2011.06 * Added "Export as Image" menu.
================
o Added "Export as Image" menu. **Bugfixes:**
* Cylinder tesselation broke existing models which are using cylinders
Bugfixes:
o Cylinder tesselation broke existing models which are using cylinders
for e.g. captured nut slots and are dependent on the orientation not for e.g. captured nut slots and are dependent on the orientation not
changing. changing.
o DXF output couldn't be imported into e.g. AutoCAD and Solidworks after updating * DXF output couldn't be imported into e.g. AutoCAD and Solidworks after updating
to using the AutoCAD 2000 (AC1015) format. Reverted to the old entity-only output, to using the AutoCAD 2000 (AC1015) format. Reverted to the old entity-only output,
causing LWPOLYLINES to not exported allowed anymore. causing LWPOLYLINES to not exported allowed anymore.
# OpenSCAD 2011.04
* Added hull() for convex hulls (2D object only)
* minkowski() now supports 2D objects
* Added functions: rands(), sign()
* Now supports escaping of the following characters in strings: \n, \t, \r, \\, \"
* Support nested includes
* Improved parsing of numbers
* DXF: output LWPOLYLINE instead of just LINE entities
* Bugfixes: More robust DXF export, setting $fs/$fa to 0 caused a crash
* Some bugs fixed, maybe some new bugs added
# OpenSCAD 2010.05
* Added functions and statements
* Added abs() function
* Added exp(x), log(b, x), log(x) and ln(x) functions
* Added minkowski() statement for 3d minkowski sums
* Added 'include <filename>' and 'use <filename>' statements
* Old implicit '<filename>' include statement is now obsolete
* Some bugs fixed, maybe some new bugs added
# OpenSCAD 2010.02
OpenSCAD 2011.04 * Added functions and statements
================ * Added sqrt() function
* Added round(), ceil() and floor() functions
o Added hull() for convex hulls (2D object only) * Added lookup() function for linear interpolation in value list
o minkowski() now supports 2D objects * Added projection(cut = true/false) statement
o Added functions: rands(), sign() * Added child() statement for accessing child nodes of module instances
o Now supports escaping of the following characters in strings: \n, \t, \r, \\, \" * Added mirror() statement
o Support nested includes * Improved DXF import code (more entities and some bugs fixed)
o Improved parsing of numbers * Added feature for dumping animation as PNG files
o DXF: output LWPOLYLINE instead of just LINE entities * Added a preferences dialog
o Bugfixes: More robust DXF export, setting $fs/$fa to 0 caused a crash * Now using CGAL's delaunay tesselator
o Some bugs fixed, maybe some new bugs added * Now using eigen2 for linear algebra
* Reorganisation of the source tree
OpenSCAD 2010.05 * Some bugs fixed, maybe some new bugs added
================ # OpenSCAD 2010.01
o Added functions and statements
- Added abs() function
- Added exp(x), log(b, x), log(x) and ln(x) functions
- Added minkowski() statement for 3d minkowski sums
o Added 'include <filename>' and 'use <filename>' statements
- Old implicit '<filename>' include statement is now obsolete
o Some bugs fixed, maybe some new bugs added
OpenSCAD 2010.02
================
o Added functions and statements
- Added sqrt() function
- Added round(), ceil() and floor() functions
- Added lookup() function for linear interpolation in value list
- Added projection(cut = true/false) statement
- Added child() statement for accessing child nodes of module instances
- Added mirror() statement
o Improved DXF import code (more entities and some bugs fixed)
o Added feature for dumping animation as PNG files
o Added a preferences dialog
o Now using CGAL's delaunay tesselator
o Now using eigen2 for linear algebra
o Reorganisation of the source tree
o Some bugs fixed, maybe some new bugs added
OpenSCAD 2010.01
================
o Added functions and statements
- Added intersection_for()
- Added str function
- Added min and max function
- Added color() statement
o Added 2D Subsystem
- New primitives: circle(), square() and polygon()
- 2D->3D path: linear_extrude() and rotate_extrude()
- Import of DXF to 2d subsystem: import_dxf()
- Export of 2D data as DXF files
o Some bugs fixed, maybe some new bugs added
* Added functions and statements
* Added intersection_for()
* Added str function
* Added min and max function
* Added color() statement
* Added 2D Subsystem
* New primitives: circle(), square() and polygon()
* 2D->3D path: linear_extrude() and rotate_extrude()
* Import of DXF to 2d subsystem: import_dxf()
* Export of 2D data as DXF files
* Some bugs fixed, maybe some new bugs added

View File

@ -8,6 +8,7 @@
<title>OpenSCAD @VERSION@</title> <title>OpenSCAD @VERSION@</title>
<pubDate>@VERSIONDATE@</pubDate> <pubDate>@VERSIONDATE@</pubDate>
<sparkle:releaseNotesLink>https://raw.github.com/openscad/openscad/master/RELEASE_NOTES</sparkle:releaseNotesLink> <sparkle:releaseNotesLink>https://raw.github.com/openscad/openscad/master/RELEASE_NOTES</sparkle:releaseNotesLink>
<sparkle:minimumSystemVersion>10.7.0</sparkle:minimumSystemVersion>
<enclosure url="http://files.openscad.org/OpenSCAD-@VERSION@.dmg" <enclosure url="http://files.openscad.org/OpenSCAD-@VERSION@.dmg"
sparkle:version="@VERSIONDATE@" sparkle:version="@VERSIONDATE@"
sparkle:shortVersionString="@VERSION@" sparkle:shortVersionString="@VERSION@"

View File

@ -8,6 +8,7 @@
<title>OpenSCAD @VERSION@</title> <title>OpenSCAD @VERSION@</title>
<pubDate>@VERSIONDATE@</pubDate> <pubDate>@VERSIONDATE@</pubDate>
<sparkle:releaseNotesLink>https://raw.github.com/openscad/openscad/openscad-@VERSION@/RELEASE_NOTES</sparkle:releaseNotesLink> <sparkle:releaseNotesLink>https://raw.github.com/openscad/openscad/openscad-@VERSION@/RELEASE_NOTES</sparkle:releaseNotesLink>
<sparkle:minimumSystemVersion>10.7.0</sparkle:minimumSystemVersion>
<enclosure url="http://files.openscad.org/OpenSCAD-@VERSION@.dmg" <enclosure url="http://files.openscad.org/OpenSCAD-@VERSION@.dmg"
sparkle:version="@VERSIONDATE@" sparkle:version="@VERSIONDATE@"
sparkle:shortVersionString="@VERSION@" sparkle:shortVersionString="@VERSION@"

View File

@ -9,6 +9,8 @@ boost {
win*: QMAKE_LIBDIR += -L$$BOOST_DIR/lib win*: QMAKE_LIBDIR += -L$$BOOST_DIR/lib
} }
# See https://svn.boost.org/trac/boost/ticket/6219
macx: DEFINES += __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0
CONFIG(mingw-cross-env) { CONFIG(mingw-cross-env) {
DEFINES += BOOST_STATIC DEFINES += BOOST_STATIC
DEFINES += BOOST_THREAD_USE_LIB DEFINES += BOOST_THREAD_USE_LIB

View File

@ -1,4 +1,4 @@
.TH OPENSCAD 1 "2013-06-xx" .TH OPENSCAD 1 "2014.03"
.\" Please adjust this date whenever revising the manpage. .\" Please adjust this date whenever revising the manpage.
.SH NAME .SH NAME
openscad \- script file based graphical CAD environment openscad \- script file based graphical CAD environment
@ -19,9 +19,9 @@ the OpenSCAD user manual at http://en.wikibooks.org/wiki/OpenSCAD_User_Manual.
.TP .TP
\fB-o\fP \fIoutputfile\fP \fB-o\fP \fIoutputfile\fP
Export the given file to \fIoutputfile\fP in STL, OFF, DXF, or PNG format, Export the given file to \fIoutputfile\fP in STL, OFF, DXF, or PNG
depending on file extension of \fIoutputfile\fP (which has to be lower case). format, depending on file extension of \fIoutputfile\fP. If this
If this option is given, the GUI will not be started. option is given, the GUI will not be started.
Additional formats, which are mainly used for debugging and testing (but can Additional formats, which are mainly used for debugging and testing (but can
also be used in automation), are AST (the input file as parsed and serialized also be used in automation), are AST (the input file as parsed and serialized
@ -46,12 +46,18 @@ read it again.
This option can be used to assign constant values to OpenSCAD variables. The This option can be used to assign constant values to OpenSCAD variables. The
variable's value is an expression, so if this mechanism is used to assign variable's value is an expression, so if this mechanism is used to assign
strings, care has to be taken that the shell does not consume quotation marks. strings, care has to be taken that the shell does not consume quotation marks.
More than one \fB-D\fP options can be given. More than one \fB-D\fP option can be given.
.TP .TP
.B \-\-render .B \-\-render
If exporting an image, use a full CGAL render. (Default is an OpenCSG compile) If exporting an image, use a full CGAL render. (Default is an OpenCSG compile)
.TP .TP
.B \-\-camera=rotx,roty,rotz,transx,transy,transz,distance .B \-\-preview[=throwntogether]
If exporting an image, use an OpenCSG previce (optionally in throwntogether mode for quicker rendering).
.TP
.B \-\-csglimit=limit
If exporting an image as an OpenCSG preview, stop rendering after encountering \fIlimit\fP elements to avoid runaway resource usage.
.TP
.B \-\-camera=transx,transy,transz,rotx,roty,rotz,distance
If exporting an image, use a Gimbal camera with the given parameters. If exporting an image, use a Gimbal camera with the given parameters.
Rot is rotation around the x, y, and z axis, trans is the distance to Rot is rotation around the x, y, and z axis, trans is the distance to
move the object in the x, y, and z directions, and distance is the move the object in the x, y, and z directions, and distance is the
@ -66,7 +72,7 @@ not currently supported.
.B \-\-imgsize=width,height .B \-\-imgsize=width,height
If exporting an image, specify the pixel width and height If exporting an image, specify the pixel width and height
.TP .TP
.B \-\-projection=[o|p] .B \-\-projection=[o|ortho|p|perspective]
If exporting an image, specify whether to use orthographic or perspective If exporting an image, specify whether to use orthographic or perspective
projection projection
.TP .TP

View File

@ -11,7 +11,7 @@ o Pre-release preparations
$ cd libraries/MCAD $ cd libraries/MCAD
$ git pull $ git pull
$ cd ../.. $ cd ../..
$ git commit -m "Updated MCAD" $ git commit -m "Updated MCAD" libraries/MCAD
$ git push $ git push
(See bottom of this file for how to build release binaries) (See bottom of this file for how to build release binaries)
@ -29,7 +29,11 @@ o Set VERSION and VERSIONDATE environment variable
scripts/publish-macosx.sh scripts/publish-macosx.sh
scripts/publish-mingw-x.sh scripts/publish-mingw-x.sh
o Update RELEASE_NOTES o Update manpage: doc/openscad.1
o Update releases/$VERSION.md
o scripts/makereleasenotes.sh
o Update version number in doc/openscad.1
o Update copyright year in AboutDialog.html and mainwin.cc o Update copyright year in AboutDialog.html and mainwin.cc
o Tag release o Tag release
@ -40,15 +44,18 @@ o build source package
o Sanity check; build a binary or two and manually run some tests o Sanity check; build a binary or two and manually run some tests
o git push --tags o git push --tags master
o Upload Source package o Upload Source package
$ ./scripts/googlecode_upload.py -s 'Source Code' -p openscad -l Featured,Type-Source openscad-$VERSION.src.tar.gz
$ scp openscad-$VERSION.src.tar.gz openscad@files.openscad.org:www $ scp openscad-$VERSION.src.tar.gz openscad@files.openscad.org:www
o Remove VERSION environment variable o Remove VERSION environment variable
$ unset VERSION $ unset VERSION
o Build binaries for all platforms and wait for upload
o Announce:
o ./scripts/github-release.sh $VERSION
o Write release email/blog entry o Write release email/blog entry
o Update web page o Update web page
- news.html - news.html
@ -56,12 +63,13 @@ o Update web page
o Update external resources: o Update external resources:
- http://en.wikipedia.org/wiki/OpenSCAD - http://en.wikipedia.org/wiki/OpenSCAD
o Write to mailing list o Write to mailing list
o Tweet o Tweet as OpenSCAD
o Notify package managers o Notify package managers
- Ubuntu: https://launchpad.net/~chrysn - Ubuntu: https://launchpad.net/~chrysn
- Fedora: Miro Hrončok <miro@hroncok.cz> or <mhroncok@redhat.com> - Fedora: Miro Hrončok <miro@hroncok.cz> or <mhroncok@redhat.com>
- OpenSUSE: Pavol Rusnak <prusnak@opensuse.org> - OpenSUSE: Pavol Rusnak <prusnak@opensuse.org>
- MacPorts: Frank Schima <macports2000@gmail.com> - MacPorts: Frank Schima <macports2000@gmail.com>
- Arch Linux: Kyle Keen <keenerd@gmail.com>
Build and Upload Release Binaries Build and Upload Release Binaries
--------------------------------- ---------------------------------

View File

@ -1,9 +1,20 @@
Running regression tests: Running regression tests:
------------------------- -------------------------
Prerequisites: cmake, python, ImageMagick 6.5.9.3 or newer 0) Prerequisites
First, install MCAD. Install the prerequisite helper programs on your system:
cmake, python2 (not 3), ImageMagick 6.5.9.3 or newer, diff
There are binary installer packages of these tools available for Mac,
Win, Linux, BSD, and other systems. (except maybe diff for Win)
Next, get a working qmake GUI build of the main openscad binary working.
For Windows(TM) this means get a cross-build working from within linux.
See README.md for how to do this.
Then, install MCAD under openscad/libraries.
$ cd openscad $ cd openscad
$ git submodule update --init $ git submodule update --init
@ -11,30 +22,29 @@ $ git submodule update --init
A) Building test environment A) Building test environment
Linux, Mac: Linux, Mac:
$ cd tests
$ cmake .
$ make
Windows + MSVC: $ cd tests
$ cmake .
$ make
The MSVC build hasn't been tested in years. See the README for pointers. Windows(TM):
First, gett the main GUI to build. Then, to build the tests:
From the QT command prompt: Cross-build from within linux:
> cd tests 64-bit:
> cmake . -DCMAKE_BUILD_TYPE=Release $ source ./scripts/setenv-mingw-xbuild.sh 64
> sed -i s/\/MD/\/MT/ CMakeCache.txt $ ./scripts/release-common.sh mingw64 tests
> cmake . $ # result is .zip file under ./mingw64/
> nmake -f Makefile
Cross compiling Linux->Win32 and testing under Wine: 32-bit:
$ source ./scripts/setenv-mingw-xbuild.sh 32
Experimental. Please see openscad/tests/CMingw-cross-env.cmake for instructions $ ./scripts/release-common.sh mingw32 tests
on attempting to get it to work. $ # result is .zip file under ./mingw32/
B) Running tests B) Running tests
Linux, Mac:
$ ctest Runs tests enabled by default $ ctest Runs tests enabled by default
$ ctest -R <regex> Runs only matching tests, e.g. ctest -R dxf $ ctest -R <regex> Runs only matching tests, e.g. ctest -R dxf
$ ctest -C <configs> Adds extended tests belonging to configs. $ ctest -C <configs> Adds extended tests belonging to configs.
@ -45,6 +55,22 @@ $ ctest -C <configs> Adds extended tests belonging to configs.
Bugs - test known bugs (tests will fail) Bugs - test known bugs (tests will fail)
All - test everything All - test everything
Win:
Unzip the OpenSCAD-Tests-YYYY.MM.DD file onto a Windows(TM) machine.
There will be a script called OpenSCAD-Test-Console.py in the parent folder.
Double-click it, and it will open a console, from which you can type the ctest
commands listed above.
C) Automatically upload test results (experimental)
It's possible to automatically upload tests results to an external
server. This is good for CI, as well as being able to easily report
bugs.
To enable this feature, add '-DOPENSCAD_UPLOAD_TESTS=1' to the cmake
cmd-line, e.g.: cmake -DOPENSCAD_UPLOAD_TESTS=1 .
Adding a new test: Adding a new test:
------------------ ------------------
@ -120,7 +146,7 @@ Imagemagick may have crashed while comparing the expected images to the
test-run generated (actual) images. You can try using the alternate test-run generated (actual) images. You can try using the alternate
ImageMagick comparison method by by erasing CMakeCache, and re-running ImageMagick comparison method by by erasing CMakeCache, and re-running
cmake with -DCOMPARATOR=ncc. This will enable the Normalized Cross cmake with -DCOMPARATOR=ncc. This will enable the Normalized Cross
Comparison method. Comparison method which is less accurate but won't usually crash.
4. Testing images fails with 'morphology not found" for ImageMagick in the log 4. Testing images fails with 'morphology not found" for ImageMagick in the log
@ -132,7 +158,7 @@ cmake. The comparison will be of lowered reliability.
"terminate called after throwing an instance of 'std::runtime_error' "terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid" what(): locale::facet::_S_create_c_locale name not valid"
Is a boost/libstdc++ bug. Fix like so: Is a boost/libstdc++ bug. Fix like so before running:
$ export LC_MESSAGES= $ export LC_MESSAGES=
@ -147,21 +173,43 @@ Is a boost/libstdc++ bug. Fix like so:
useful for debugging and outputting 3d-formats like STL on systems without GL. useful for debugging and outputting 3d-formats like STL on systems without GL.
This option may break in the future and require tweaking to get working again. This option may break in the future and require tweaking to get working again.
7. Other issues 7. Proprietary GL driver issues
The OpenSCAD User Manual has a section on buildling. Please check there There are sporadic reports of problems running on remote machines with
for updates: proprietary GL drivers. Try doing a web search for your exact error
message to see solutions and workarounds that others have found.
8. Windows + MSVC:
The MSVC build was last tested circa 2012. The last time it worked,
these were the necessary commands to run.
> Start the 'QT command prompt'
> cd \where\you\installed\openscad
> cd tests
> cmake . -DCMAKE_BUILD_TYPE=Release
> sed -i s/\/MD/\/MT/ CMakeCache.txt
> cmake .
> nmake -f Makefile
9. Other issues
The OpenSCAD User Manual Wiki has a section on buildling. Please check
there for possible updates and workarounds:
http://en.wikibooks.org/wiki/OpenSCAD_User_Manual http://en.wikibooks.org/wiki/OpenSCAD_User_Manual
Please report build errors (after double checking the instructions) in
the github issue tracker
http://github.com/openscad/openscad/issues
Migration away from dedicated regression tests: 10. Migration away from dedicated regression tests:
-----------------------------------------------
In 2013 the test programs underwent a major change. These notes are leftover. In 2013 the test programs underwent a major change. The following notes
are leftover.
This test still needs an intermediate script that mangles away timestamps and "This test still needs an intermediate script that mangles away timestamps and
near-zero floating point numbers: near-zero floating point numbers:
* cgalstlsanitytest * cgalstlsanitytest
@ -174,4 +222,4 @@ These look like tests, but are not actually in use:
* modulecachetest * modulecachetest
* cgalcachetest * cgalcachetest
"

View File

@ -43,14 +43,14 @@ EIGEN_DIR = $$(EIGENDIR)
} }
isEmpty(EIGEN_INCLUDEPATH) { isEmpty(EIGEN_INCLUDEPATH) {
linux*|hurd*|unix: EIGEN_INCLUDEPATH = /usr/include/eigen3
freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen3 freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen3
netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen3 netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen3
linux*|hurd*|unix: EIGEN_INCLUDEPATH = /usr/include/eigen3
macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen3 macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen3
!exists($$EIGEN_INCLUDEPATH) { !exists($$EIGEN_INCLUDEPATH) {
linux*|hurd*|unix*: EIGEN_INCLUDEPATH = /usr/include/eigen2
freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen2 freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen2
netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen2 netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen2
linux*|hurd*|unix*: EIGEN_INCLUDEPATH = /usr/include/eigen2
macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen2 macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen2
} }
} }

View File

@ -2,7 +2,7 @@
module step(len, mod) module step(len, mod)
{ {
for (i = [0:$children-1]) for (i = [0:$children-1])
translate([ len*(i - ($children-1)/2), 0, 0 ]) child((i+mod) % $children); translate([ len*(i - ($children-1)/2), 0, 0 ]) children((i+mod) % $children);
} }
for (i = [1:4]) for (i = [1:4])

31
examples/example025.scad Normal file
View File

@ -0,0 +1,31 @@
// Example for offset() usage
// (c) 2014 Torsten Paul
// CC-BY-SA 4.0
$fn = 40;
foot_height = 20;
module outline(wall = 1) {
difference() {
offset(wall / 2) children();
offset(-wall / 2) children();
}
}
// offsetting with a positive value and join_type = "round"
// allows to create rounded corners easily
linear_extrude(height = foot_height, scale = 0.5) {
offset(10, join_type = "round") {
square(50, center = true);
}
}
translate([0, 0, foot_height]) {
linear_extrude(height = 20) {
outline(wall = 2) circle(15);
}
}
%cylinder(r = 14, h = 100);
%translate([0, 0, 100]) sphere(r = 30);

View File

@ -19,6 +19,14 @@ GLIB2_DIR = $$(GLIB2DIR)
} }
} }
!exists($$GLIB2_INCLUDEPATH/glib.h) {
!exists($$GLIB2_INCLUDEPATH_2/glib.h) {
GLIB2_INCLUDEPATH =
GLIB2_INCLUDEPATH_2 =
GLIB2_LIBPATH =
}
}
isEmpty(GLIB2_INCLUDEPATH) { isEmpty(GLIB2_INCLUDEPATH) {
GLIB2_CFLAGS = $$system("pkg-config --cflags glib-2.0") GLIB2_CFLAGS = $$system("pkg-config --cflags glib-2.0")
} else { } else {
@ -32,6 +40,27 @@ isEmpty(GLIB2_LIBPATH) {
GLIB2_LIBS = -L$$GLIB2_LIBPATH -lglib-2.0 GLIB2_LIBS = -L$$GLIB2_LIBPATH -lglib-2.0
} }
CONFIG(mingw-cross-env) {
#message("mingw")
isEmpty(GLIB2_INCLUDEPATH) {
MXE_TARGET_DIR=$$(MXETARGETDIR)
#message($$MXE_TARGET_DIR)
contains( MXE_TARGET_DIR, .*x86_64-w64-mingw32 ) {
GLIB2_CFLAGS = $$system("x86_64-w64-mingw32-pkg-config --cflags glib-2.0")
GLIB2_LIBS = $$system("x86_64-w64-mingw32-pkg-config --libs glib-2.0")
}
contains( MXE_TARGET_DIR, .*i686-w64-mingw32 ) {
GLIB2_CFLAGS = $$system("i686-w64-mingw32-pkg-config --cflags glib-2.0")
GLIB2_LIBS = $$system("i686-w64-mingw32-pkg-config --libs glib-2.0")
}
contains( MXE_TARGET_DIR, .*i686-pc-mingw32 ) {
GLIB2_CFLAGS = $$system("i686-pc-mingw32-pkg-config --cflags glib-2.0")
GLIB2_LIBS = $$system("i686-pc-mingw32-pkg-config --libs glib-2.0")
}
}
}
QMAKE_CXXFLAGS += $$GLIB2_CFLAGS QMAKE_CXXFLAGS += $$GLIB2_CFLAGS
LIBS += $$GLIB2_LIBS LIBS += $$GLIB2_LIBS
} }

View File

@ -4,4 +4,5 @@ Version=1.0
Name=OpenSCAD Name=OpenSCAD
Icon=openscad Icon=openscad
Exec=openscad %f Exec=openscad %f
MimeType=application/x-openscad;
Categories=Graphics;3DGraphics;Engineering;Development; Categories=Graphics;3DGraphics;Engineering;Development;

8
icons/openscad.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-openscad">
<comment>OpenSCAD Model</comment>
<glob pattern="*.scad"/>
<icon name="openscad"/>
</mime-type>
</mime-info>

@ -1 +1 @@
Subproject commit 9a958fd11b0a6b5f8becd37c4f8a42f585abfcd8 Subproject commit 85794e4b4f2294a1b445a4d928866bedd5cc64ec

View File

@ -13,6 +13,10 @@
# #
# http://en.wikibooks.org/wiki/OpenSCAD_User_Manual # http://en.wikibooks.org/wiki/OpenSCAD_User_Manual
!experimental {
message("If you're building a development binary, consider adding CONFIG+=experimental")
}
isEmpty(QT_VERSION) { isEmpty(QT_VERSION) {
error("Please use qmake for Qt 4 (probably qmake-qt4)") error("Please use qmake for Qt 4 (probably qmake-qt4)")
} }
@ -45,6 +49,7 @@ DEPENDPATH += src
# Used when manually installing 3rd party libraries # Used when manually installing 3rd party libraries
OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES) OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES)
!isEmpty(OPENSCAD_LIBDIR) { !isEmpty(OPENSCAD_LIBDIR) {
INCLUDEPATH += $$OPENSCAD_LIBDIR/include
QMAKE_INCDIR_QT = $$OPENSCAD_LIBDIR/include $$QMAKE_INCDIR_QT QMAKE_INCDIR_QT = $$OPENSCAD_LIBDIR/include $$QMAKE_INCDIR_QT
QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib $$QMAKE_LIBDIR QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib $$QMAKE_LIBDIR
} }
@ -74,14 +79,6 @@ macx {
APP_RESOURCES.files = OpenSCAD.sdef dsa_pub.pem icons/SCAD.icns APP_RESOURCES.files = OpenSCAD.sdef dsa_pub.pem icons/SCAD.icns
QMAKE_BUNDLE_DATA += APP_RESOURCES QMAKE_BUNDLE_DATA += APP_RESOURCES
LIBS += -framework Cocoa -framework ApplicationServices LIBS += -framework Cocoa -framework ApplicationServices
# FIXME: Somehow, setting the deployment target to a lower version causes a
# seldom crash in debug mode (e.g. the minkowski2-test):
# frame #4: 0x00007fff8b7d5be5 libc++.1.dylib`std::runtime_error::~runtime_error() + 55
# frame #5: 0x0000000100150df5 OpenSCAD`CGAL::Uncertain_conversion_exception::~Uncertain_conversion_exception(this=0x0000000105044488) + 21 at Uncertain.h:78
# The reason for the crash appears to be linking with libgcc_s,
# but it's unclear what's really going on
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.6
} }
else { else {
TARGET = openscad TARGET = openscad
@ -89,6 +86,7 @@ else {
win* { win* {
RC_FILE = openscad_win32.rc RC_FILE = openscad_win32.rc
QTPLUGIN += qtaccessiblewidgets
} }
CONFIG += qt CONFIG += qt
@ -129,6 +127,7 @@ netbsd* {
# See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs. # See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs.
*g++* { *g++* {
QMAKE_CXXFLAGS *= -fno-strict-aliasing QMAKE_CXXFLAGS *= -fno-strict-aliasing
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs # ignored before 4.8
} }
*clang* { *clang* {
@ -162,6 +161,11 @@ CONFIG += fontconfig
#Uncomment the following line to enable QCodeEdit #Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit #CONFIG += qcodeedit
# Make experimental features available
experimental {
DEFINES += ENABLE_EXPERIMENTAL
}
mdi { mdi {
DEFINES += ENABLE_MDI DEFINES += ENABLE_MDI
} }
@ -227,6 +231,7 @@ HEADERS += src/typedefs.h \
src/feature.h \ src/feature.h \
src/node.h \ src/node.h \
src/csgnode.h \ src/csgnode.h \
src/offsetnode.h \
src/linearextrudenode.h \ src/linearextrudenode.h \
src/rotateextrudenode.h \ src/rotateextrudenode.h \
src/projectionnode.h \ src/projectionnode.h \
@ -315,6 +320,7 @@ SOURCES += src/version_check.cc \
src/text.cc \ src/text.cc \
src/dxfdata.cc \ src/dxfdata.cc \
src/dxfdim.cc \ src/dxfdim.cc \
src/offset.cc \
src/linearextrude.cc \ src/linearextrude.cc \
src/rotateextrude.cc \ src/rotateextrude.cc \
src/printutils.cc \ src/printutils.cc \
@ -438,6 +444,10 @@ applications.path = $$PREFIX/share/applications
applications.files = icons/openscad.desktop applications.files = icons/openscad.desktop
INSTALLS += applications INSTALLS += applications
mimexml.path = $$PREFIX/share/mime/packages
mimexml.files = icons/openscad.xml
INSTALLS += mimexml
appdata.path = $$PREFIX/share/appdata appdata.path = $$PREFIX/share/appdata
appdata.files = openscad.appdata.xml appdata.files = openscad.appdata.xml
INSTALLS += appdata INSTALLS += appdata

11
releases/2010.01.md Normal file
View File

@ -0,0 +1,11 @@
* Added functions and statements
* Added intersection_for()
* Added str function
* Added min and max function
* Added color() statement
* Added 2D Subsystem
* New primitives: circle(), square() and polygon()
* 2D->3D path: linear_extrude() and rotate_extrude()
* Import of DXF to 2d subsystem: import_dxf()
* Export of 2D data as DXF files
* Some bugs fixed, maybe some new bugs added

14
releases/2010.02.md Normal file
View File

@ -0,0 +1,14 @@
* Added functions and statements
* Added sqrt() function
* Added round(), ceil() and floor() functions
* Added lookup() function for linear interpolation in value list
* Added projection(cut = true/false) statement
* Added child() statement for accessing child nodes of module instances
* Added mirror() statement
* Improved DXF import code (more entities and some bugs fixed)
* Added feature for dumping animation as PNG files
* Added a preferences dialog
* Now using CGAL's delaunay tesselator
* Now using eigen2 for linear algebra
* Reorganisation of the source tree
* Some bugs fixed, maybe some new bugs added

7
releases/2010.05.md Normal file
View File

@ -0,0 +1,7 @@
* Added functions and statements
* Added abs() function
* Added exp(x), log(b, x), log(x) and ln(x) functions
* Added minkowski() statement for 3d minkowski sums
* Added 'include <filename>' and 'use <filename>' statements
* Old implicit '<filename>' include statement is now obsolete
* Some bugs fixed, maybe some new bugs added

9
releases/2011.04.md Normal file
View File

@ -0,0 +1,9 @@
* Added hull() for convex hulls (2D object only)
* minkowski() now supports 2D objects
* Added functions: rands(), sign()
* Now supports escaping of the following characters in strings: \n, \t, \r, \\, \"
* Support nested includes
* Improved parsing of numbers
* DXF: output LWPOLYLINE instead of just LINE entities
* Bugfixes: More robust DXF export, setting $fs/$fa to 0 caused a crash
* Some bugs fixed, maybe some new bugs added

9
releases/2011.06.md Normal file
View File

@ -0,0 +1,9 @@
* Added "Export as Image" menu.
**Bugfixes:**
* Cylinder tesselation broke existing models which are using cylinders
for e.g. captured nut slots and are dependent on the orientation not
changing.
* DXF output couldn't be imported into e.g. AutoCAD and Solidworks after updating
to using the AutoCAD 2000 (AC1015) format. Reverted to the old entity-only output,
causing LWPOLYLINES to not exported allowed anymore.

41
releases/2011.12.md Normal file
View File

@ -0,0 +1,41 @@
**Features:**
* The MCAD library is now bundled with OpenSCAD
* Added len() function. Takes one vector or string parameter and returns its length.
* The index operator [] now works on strings
* The version() function will return the OpenSCAD version as a vector, e.g. [2011, 09]
* The version_num() function will return the OpenSCAD version as a number, e.g. 20110923
* hull() Now supports 3D objects
* hull() with 2D object can now use for loops and boolean operations as children
* New import() statement reads the correct file format based on the filename extension
(.stl, .dxf and .off is supported)
* The color() statement now supports an alpha parameter, e.g. color(c=[1,0,0], alpha=0.4)
* The color() statement now supports specifying colors as strings, e.g. color("Red")
* The color() statement now overrides colors specified further down in the tree
* if()/else() and the ternary operator can now take any value type as parameter. false, 0, empty string and empty vector or illegal value type will evaluate as false, everything else as true.
* Strings can now be lexographically compared using the <, <=, >, >= operators
* Added PI constant.
* Number literals in scientific notation are now accepted by the parser
* Added import and export of the OFF file format
* Now uses standard shortcuts for save, reload and quit on Linux and Windows. F2/F3 will still work but is deprecated.
**Bugfixes:**
* Complex CSG models sometimes took extremely long time to normalize before OpenCSG preview
* square() crashed if any of the dimensions were zero
* Flush Caches didn't flush cached USE'd modules
* STL export should be a bit more robust
* Dropping a file into the editor under Windows didn't work (double C:/C:/ problem)
* On some platforms it was possible to insertion rich text in the editor, causing confusion.
* Less crashes due to CGAL assertions
* OpenCSG should now work on systems with OpenGL 1.x, given that the right extensions are available
* include now searches librarydir
* The $fs parameter yielded only half the number of segments it should have
* surface(center=true) is now correctly centered in the XY plane
**Deprecations:**
* dxf_linear_extrude() and dxf_rotate_extrude() are now deprecated.
Use linear_extrude() and rotate_extrude() instead.
* The file, layer, origin and scale parameters to linear_extrude() and rotate_extrude()
are now deprecated. Use an import() child instead.
* import_dxf(), import_stl() and import_off() are now deprecated. Use import() instead.
* When exporting geometry from the cmd-line, use the universal -o option. It will export to the correct file format based on the given suffix (dxf, stl, off). The -x and -s parameters are still working but deprecated.
* F2 and F3 for Save and Reload is now deprecated

34
releases/2013.01.md Normal file
View File

@ -0,0 +1,34 @@
**Features:**
* Snappier GUI while performing CGAL computations (computations running in separate thread)
* The size of the misc. caches can now be adjusted from Preferences
* The limit for when to disable OpenCSG can now be adjusted from Preferences
* Added Dot product operator: vec * vec
* Added Matrix multiplication operator: vec * mat, mat * mat
* Added search() function
* Dependencies are now tracked - any changes in uses/included files will be detected and cause a recompile
* The OPENSCADPATH environment variable is now implemented will have precedence when searching for libraries
* .csg files can now be opened from the GUI
* linear_extrude() will now assume that the first parameter means 'height' if it's a number
**Bugfixes:**
* use'ing an non-existing file sometimes crashed under Windows
* Better font handling: Ensure a monospace font is chosen as default
* Division by zero caused hang in some cases (e.g. sin(1/0))
* Larger minkowski operations sometimes caused a crash after a CGAL assert was thrown
* Fixed crashes in shared_ptr.hpp (or similar places) due bugs in cache management and CSG normalization
* scale() with a scale factor of zero could cause a crash
* Fixed a number of issues related to use/include
* Providing an unknown parameter on the cmd-line caused a crash
* cmd-line overrides using -D now also work for USEd modules
* Modifier characters can now be used in front of if statements
* rotate() with a vector argument with less that 3 elements used uninitialized variables, ending up being non-deterministic.
* .csg files will now have relative filenames whenever possible
* Don't just ignore geometric nodes having zero volume/area - when doing difference/intersection, they tend to turn negative objects into positive ones.
* Always use utf-8 file encoding, also under Windows
* A lot of build script fixes
* Some other crash bugs fixes
**Deprecations:**
* The old include syntax "<filename.scad>" without the include keyword is no
longer supported and will cause a syntax error.

48
releases/2013.06.md Normal file
View File

@ -0,0 +1,48 @@
**Language Features:**
* linear_extrude now takes a scale parameter:
linear_extrude(height=a, slices=b, twist=c, scale=[x,y])
* Recursive use of modules is now supported (including cascading child() operations):
https://github.com/openscad/openscad/blob/master/examples/example024.scad
* Parameter list values can now depend on earlier values, e.g. for (i=[0:2], j=[0:i]) ..
* value assignments in parameters can now depend on already declared parameters
* Added resize() module:
http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#resize
**Program Features:**
* Added basic syntax highlighting in the editor
* There is now a built-in library path in user-space:
http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Library_Locations
* Commandline output to PNG, with various camera and rendering settings.
Run openscad -h to see usage info or see the OpenSCAD wiki user manual.
* Attempting to open dxf, off or stl files in the GUI will now create an import statement.
* The preview operator (%) will now preserve any manually set color
* The highlight operator (#) will now color the object in transparent red
* Mac: Added document icon
* Mac: Added auto-update check
* Windows: Better cmd-line support using the openscad.com executable
**Bugfixes:**
* Importing files is now always relative to the importing script, also for libraries
* We didn't always print a warning when CSG normalization created too many elements
* Binary STLs can now be read on big endian architectures
* Some binary STLs couldn't be read
* Fixed some issues related to ARM builds
* CGAL triangulation more lenient- enables partial rendering of 'bad' DXF data
* The Automatic Reload feature is now more robust
* If a file couldn't be saved it no longer fails silently
* Fixed a number of crashes related to CGAL and OpenCSG rendering or complex models
* The lookup() function had bad boundary condition behavior
* The surface() module failed when the .dat file lacked a trailing newline
* The hull() module could crash if any of the children were empty objects
* Some problems using unicode filenames have been fixed
**Misc:**
* Build scripts have been further improved
* Regression test now creates single monolithic .html file for easier uploading
* Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine
* The backend is finally independent of Qt
* Windows: We now have a 64-bit version
**Known Bugs:**
* Linux: command-line png rendering on Gallium is flaky.
Workaround: use CGAL --render or hardware rendering.

48
releases/2014.03.md Normal file
View File

@ -0,0 +1,48 @@
**Language Features:**
* Added diameter argument: circle(d), cylinder(d, d1, d2) and sphere(d)
* Added parent_module() and $parent_modules
* Added children() as a replacement for child()
* Unicode strings (using UTF-8) are now correctly handled
* Ranges can have a negative step value
* Added norm() and cross() functions
**Program Features:**
* Cmd-line: --info parameter prints system/library info
* Cmd-line: --csglimit parameter to change CSG rendering limit
* Cmd-line: Better handling of cmd-line arguments under Windows
* GUI: Added Reset View
* GUI: Added Search&Replace in editor
* GUI: Syntax highlighting now has a dark background theme
* GUI: We now create a backup file before rendering to allow for recovery if OpenSCAD crashes/freezes
* GUI: Accessibility features enabled (e.g. screenreading)
**Bugfixes/improvements:**
* Reading empty STL files sometimes caused a crash
* OPENSCADPATH now uses semicolon as path separator under Windows
* polyhedron() is now much more robust handling almost planar polygons
* Automatic reloads of large designs are more robust
* Boolean logic in if() statements are now correctly short-circuited
* rands() with zero range caused an infinite loop
* resize(, auto=true) didn't work when shrinking objects
* The $children variable sometimes misbehaved due to dynamic scoping
* The --camera cmd-line option behaved differently then the corresponding GUI function
* PNG export now doesn't leak transparency settings into the target image
* Improved performance of 3D hull() operations
* Some editor misbehaviors were fixed
* Stability fixes of CGAL-related crashes
* Windows cmd-line can now handle spaces in filenames
* Default CSG rendering limit is now 100K elements
* Fixed a crash reading DXF files using comma as decimal separator
* Fixed a crash running the cmd-line without a HOME env. variable
* Intersecting something with nothing now correctly results in an empty object
**Deprecations:**
* child() is no longer supported. Use children() instead.
* polyhedron(triangles=[...]): Use polyhedron(faces=[...]) instead.
**Misc:**
* Test framework now shares more code with the GUI app
* Test report can now be automatically uploaded to dinkypage.com
* Better compatibility with BSD systems
* Qt5 support

View File

@ -47,50 +47,126 @@
init_variables() init_variables()
{ {
BRANCH_TO_BUILD=unstable
#BRANCH_TO_BUILD=master
STARTPATH=$PWD STARTPATH=$PWD
export STARTPATH # kilobit (not kilobyte!) per second for scp upload
RATELIMIT=420
DOBUILD=1 DOBUILD=1
DOUPLOAD=1 DOUPLOAD=1
DRYRUN= DRYRUN=
DOSNAPSHOT=1
DOLOOP=
#solar day
LOOPSLEEP=86400
DATECODE=`date +"%Y.%m.%d"`
if [ "`echo $* | grep loop`" ]; then
echo "----------------------------"
echo "loop mode activated! woopee!"
echo "----------------------------"
DOLOOP=1
fi
if [ "`echo $* | grep release`" ]; then
echo "this script cannot yet build releases, only snapshots"
DOSNAPSHOT=
exit 1
fi
if [ "`echo $* | grep uploadonly`" ]; then if [ "`echo $* | grep uploadonly`" ]; then
DOUPLOAD=1 DOUPLOAD=1
DOBUILD= DOBUILD=
DATECODE=`date +"%Y.%m.%d"`
fi fi
if [ "`echo $* | grep buildonly`" ]; then if [ "`echo $* | grep buildonly`" ]; then
DOUPLOAD= DOUPLOAD=
DOBUILD=1 DOBUILD=1
DATECODE=`date +"%Y.%m.%d"`
fi fi
if [ "`echo $* | grep dry`" ]; then if [ "`echo $* | grep dry`" ]; then
DRYRUN=1 DRYRUN=1
fi fi
export STARTPATH
export BRANCH_TO_BUILD
export DOBUILD export DOBUILD
export DOUPLOAD export DOUPLOAD
export DRYRUN export DRYRUN
export DATECODE export DATECODE
export DOSNAPSHOT
export DOLOOP
export LOOPSLEEP
export RATELIMIT
export DATECODE
} }
check_starting_path() check_starting_path()
{ {
cd $STARTPATH
if [ -e openscad.pro ]; then if [ -e openscad.pro ]; then
echo 'please start from a clean directory outside of openscad' echo 'please start from a clean directory outside of openscad'
exit exit
fi fi
} }
check_nsis()
{
# 64 bit mingw-cross build MXE cannot build nsis.... for now, we can
# just ask the user to install their system's nsis package.
# (it might be possible to d/l & build nsis here, or use pre-existing
# 32-bit-mxe nsis)
if [ ! "`command -v makensis`" ]; then
echo the makensis command was not found.
echo please install nsis for your system. for example
echo on debian, sudo apt-get install nsis
exit 1
else
echo makensis found.
fi
}
get_openscad_source_code() get_openscad_source_code()
{ {
git clone http://github.com/openscad/openscad.git if [ -d openscad ]; then
if [ "`echo $? | grep 0`" ]; then cd openscad
if [ $? -ne 0 ]; then
echo cd to 'openscad' directory failed
exit 1
fi
git checkout $BRANCH_TO_BUILD
if [ $? -ne 0 ]; then
echo git checkout $BRANCH_TO_BUILD failed
exit 1
fi
git fetch -a
if [ $? -ne 0 ]; then
echo git fetch -a openscad source code failed
exit 1
fi
git pull origin $BRANCH_TO_BUILD
if [ $? -ne 0 ]; then
echo git pull origin $BRANCH_TO_BUILD failed
exit 1
fi
git submodule update # MCAD
return
else
git clone http://github.com/openscad/openscad.git
fi
if [ $? -eq 0 ]; then
echo clone of source code is ok echo clone of source code is ok
else else
echo clone of openscad source code failed. exiting if [ $DOUPLOAD ]; then
exit 1 if [ ! $DOBUILD ]; then
echo upload only - skipping openscad git clone
fi
else
echo clone of openscad source code failed. exiting
exit 1
fi
fi fi
cd openscad cd openscad
git checkout $BRANCH_TO_BUILD
if [ $? -ne 0 ]; then
echo git checkout $BRANCH_TO_BUILD failed
exit 1
fi
git submodule update --init # MCAD git submodule update --init # MCAD
#git checkout branch ##debugging
} }
build_win32() build_win32()
@ -98,15 +174,18 @@ build_win32()
. ./scripts/setenv-mingw-xbuild.sh clean . ./scripts/setenv-mingw-xbuild.sh clean
. ./scripts/setenv-mingw-xbuild.sh . ./scripts/setenv-mingw-xbuild.sh
./scripts/mingw-x-build-dependencies.sh ./scripts/mingw-x-build-dependencies.sh
./scripts/release-common.sh mingw32 if [ $DOSNAPSHOT ] ; then
if [ "`echo $? | grep 0`" ]; then ./scripts/release-common.sh snapshot mingw32 tests
else
echo "this script cant yet build releases, only snapshots"
exit 1
fi
if [ $? -eq 0 ]; then
echo build of win32 stage over echo build of win32 stage over
else else
echo build of win32 failed. exiting echo build of win32 failed. exiting
exit 1 exit 1
fi fi
DATECODE=`date +"%Y.%m.%d"`
export DATECODE
} }
build_win64() build_win64()
@ -114,24 +193,30 @@ build_win64()
. ./scripts/setenv-mingw-xbuild.sh clean . ./scripts/setenv-mingw-xbuild.sh clean
. ./scripts/setenv-mingw-xbuild.sh 64 . ./scripts/setenv-mingw-xbuild.sh 64
./scripts/mingw-x-build-dependencies.sh 64 ./scripts/mingw-x-build-dependencies.sh 64
./scripts/release-common.sh mingw64 if [ $DOSNAPSHOT ] ; then
if [ "`echo $? | grep 0`" ]; then ./scripts/release-common.sh snapshot mingw64 tests
else
echo "this script cant yet build releases, only snapshots"
exit 1
fi
if [ $? -eq 0 ]; then
echo build of win64 stage over echo build of win64 stage over
else else
echo build of win64 failed. exiting echo build of win64 failed. exiting
exit 1 exit 1
fi fi
DATECODE=`date +"%Y.%m.%d"`
export DATECODE
} }
build_lin32() build_lin32()
{ {
. ./scripts/setenv-unibuild.sh . ./scripts/setenv-unibuild.sh
./scripts/uni-build-dependencies.sh ./scripts/uni-build-dependencies.sh
./scripts/release-common.sh if [ $DOSNAPSHOT ] ; then
DATECODE=`date +"%Y.%m.%d"` ./scripts/release-common.sh snapshot
export DATECODE else
echo "this script cant yet build releases, only snapshots"
exit 1
fi
} }
upload_win_common() upload_win_common()
@ -143,16 +228,21 @@ upload_win_common()
echo 'file "'$filename'" found' echo 'file "'$filename'" found'
else else
echo 'file "'$filename'" not found' echo 'file "'$filename'" not found'
exit 1
fi fi
opts= opts=
opts="$opts -p openscad" opts="$opts -p openscad"
opts="$opts -u $username" opts="$opts -u $username"
opts="$opts $filename" opts="$opts $filename"
remotepath=www/
if [ $DOSNAPSHOT ]; then
remotepath=www/snapshots/
fi
if [ $DRYRUN ]; then if [ $DRYRUN ]; then
echo dry run, not uploading to files.openscad.org echo dry run, not uploading to files.openscad.org
echo scp -v $filename openscad@files.openscad.org:www/ echo scp -v -l $RATELIMIT $filename openscad@files.openscad.org:$remotepath
else else
scp -v $filename openscad@files.openscad.org:www/ scp -v -l $RATELIMIT $filename openscad@files.openscad.org:$remotepath
fi fi
} }
@ -160,39 +250,52 @@ upload_win32()
{ {
SUMMARY1="Windows x86-32 Snapshot Installer" SUMMARY1="Windows x86-32 Snapshot Installer"
SUMMARY2="Windows x86-32 Snapshot Zipfile" SUMMARY2="Windows x86-32 Snapshot Zipfile"
DATECODE=`date +"%Y.%m.%d"` SUMMARY3="Windows x86-32 Snapshot Tests"
BASEDIR=./mingw32/ BASEDIR=./mingw32/
WIN32_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-32-Installer.exe WIN32_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-32-Installer.exe
WIN32_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-32.zip WIN32_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-32.zip
WIN32_PACKAGEFILE3=OpenSCAD-Tests-$DATECODE-x86-32.zip
upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE1 upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE1
upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE2 upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE2
upload_win_common "$SUMMARY3" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE3
export WIN32_PACKAGEFILE1 export WIN32_PACKAGEFILE1
export WIN32_PACKAGEFILE2 export WIN32_PACKAGEFILE2
export WIN32_PACKAGEFILE3
WIN32_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE1 | awk ' {print $1} ';` WIN32_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE1 | awk ' {print $1} ';`
WIN32_PACKAGEFILE2_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE2 | awk ' {print $1} ';` WIN32_PACKAGEFILE2_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE2 | awk ' {print $1} ';`
WIN32_PACKAGEFILE3_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE3 | awk ' {print $1} ';`
WIN32_PACKAGEFILE1_SIZE=`echo "$WIN32_PACKAGEFILE1_SIZE""B"` WIN32_PACKAGEFILE1_SIZE=`echo "$WIN32_PACKAGEFILE1_SIZE""B"`
WIN32_PACKAGEFILE2_SIZE=`echo "$WIN32_PACKAGEFILE2_SIZE""B"` WIN32_PACKAGEFILE2_SIZE=`echo "$WIN32_PACKAGEFILE2_SIZE""B"`
WIN32_PACKAGEFILE3_SIZE=`echo "$WIN32_PACKAGEFILE3_SIZE""B"`
export WIN32_PACKAGEFILE1_SIZE export WIN32_PACKAGEFILE1_SIZE
export WIN32_PACKAGEFILE2_SIZE export WIN32_PACKAGEFILE2_SIZE
export WIN32_PACKAGEFILE3_SIZE
} }
upload_win64() upload_win64()
{ {
SUMMARY1="Windows x86-64 Snapshot Zipfile" SUMMARY1="Windows x86-64 Snapshot Zipfile"
SUMMARY2="Windows x86-64 Snapshot Installer" SUMMARY2="Windows x86-64 Snapshot Installer"
SUMMARY3="Windows x86-64 Snapshot Tests"
BASEDIR=./mingw64/ BASEDIR=./mingw64/
WIN64_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-64-Installer.exe WIN64_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-64-Installer.exe
WIN64_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-64.zip WIN64_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-64.zip
WIN64_PACKAGEFILE3=OpenSCAD-Tests-$DATECODE-x86-64.zip
upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE1 upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE1
upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE2 upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE2
upload_win_common "$SUMMARY3" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE3
export WIN64_PACKAGEFILE1 export WIN64_PACKAGEFILE1
export WIN64_PACKAGEFILE2 export WIN64_PACKAGEFILE2
export WIN64_PACKAGEFILE3
WIN64_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE1 | awk ' {print $1} ';` WIN64_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE1 | awk ' {print $1} ';`
WIN64_PACKAGEFILE2_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE2 | awk ' {print $1} ';` WIN64_PACKAGEFILE2_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE2 | awk ' {print $1} ';`
WIN64_PACKAGEFILE3_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE3 | awk ' {print $1} ';`
WIN64_PACKAGEFILE1_SIZE=`echo "$WIN64_PACKAGEFILE1_SIZE""B"` WIN64_PACKAGEFILE1_SIZE=`echo "$WIN64_PACKAGEFILE1_SIZE""B"`
WIN64_PACKAGEFILE2_SIZE=`echo "$WIN64_PACKAGEFILE2_SIZE""B"` WIN64_PACKAGEFILE2_SIZE=`echo "$WIN64_PACKAGEFILE2_SIZE""B"`
WIN64_PACKAGEFILE3_SIZE=`echo "$WIN64_PACKAGEFILE3_SIZE""B"`
export WIN64_PACKAGEFILE1_SIZE export WIN64_PACKAGEFILE1_SIZE
export WIN64_PACKAGEFILE2_SIZE export WIN64_PACKAGEFILE2_SIZE
export WIN64_PACKAGEFILE3_SIZE
} }
read_username_from_user() read_username_from_user()
@ -234,29 +337,38 @@ read_password_from_user()
update_win_www_download_links() update_win_www_download_links()
{ {
cd $STARTPATH cd $STARTPATH
rm -rf ./openscad.github.com
git clone git@github.com:openscad/openscad.github.com.git git clone git@github.com:openscad/openscad.github.com.git
cd openscad.github.com cd openscad.github.com
cd inc cd inc
echo `pwd` echo `pwd`
# BASEURL='https://openscad.googlecode.com/files/' # BASEURL='https://openscad.googlecode.com/files/'
BASEURL='http://files.openscad.org/' BASEURL='http://files.openscad.org/'
DATECODE=`date +"%Y.%m.%d"` if [ $DOSNAPSHOT ]; then
BASEURL='http://files.openscad.org/snapshots/'
fi
mv win_snapshot_links.js win_snapshot_links.js.backup mv win_snapshot_links.js win_snapshot_links.js.backup
rm win_snapshot_links.js rm win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT3_URL'] = '$BASEURL$WIN64_PACKAGEFILE3'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT3_NAME'] = 'OpenSCAD Tests $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js echo "fileinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT3_SIZE'] = '$WIN64_PACKAGEFILE3_SIZE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT3_URL'] = '$BASEURL$WIN32_PACKAGEFILE3'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT3_NAME'] = 'OpenSCAD Tests $DATECODE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js echo "fileinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js
echo "fileinfo['WIN32_SNAPSHOT3_SIZE'] = '$WIN32_PACKAGEFILE3_SIZE'" >> win_snapshot_links.js
echo 'modified win_snapshot_links.js' echo 'modified win_snapshot_links.js'
PAGER=cat git diff PAGER=cat git diff
@ -266,6 +378,7 @@ update_win_www_download_links()
else else
echo dry run, not updating www links echo dry run, not updating www links
fi fi
cd $STARTPATH
} }
# FIXME: We might be running this locally and not need an ssh agent. # FIXME: We might be running this locally and not need an ssh agent.
@ -274,30 +387,55 @@ update_win_www_download_links()
check_ssh_agent() check_ssh_agent()
{ {
if [ $DRYRUN ]; then echo 'skipping ssh, dry run'; return; fi if [ $DRYRUN ]; then echo 'skipping ssh, dry run'; return; fi
if [ $SSH_AUTH_SKIP ]; then
return
fi
if [ ! $SSH_AUTH_SOCK ]; then if [ ! $SSH_AUTH_SOCK ]; then
echo 'please start an ssh-agent for github.com/openscad/openscad.github.com uploads' echo 'please start an ssh-agent for github.com/openscad/openscad.github.com uploads'
echo 'for example:' echo 'for example:'
echo echo
echo ' ssh-agent > .tmp && source .tmp && ssh-add' echo ' ssh-agent > .tmp && source .tmp && ssh-add'
echo echo
echo 'to force a run anyway, set SSH_AUTH_SKIP environment variable to 1'
exit 1
fi fi
} }
init_variables $* main()
if [ $DOUPLOAD ]; then {
check_ssh_agent init_variables $*
fi if [ $DOUPLOAD ]; then
check_starting_path check_ssh_agent
read_username_from_user fi
read_password_from_user check_starting_path
get_openscad_source_code check_nsis
if [ $DOBUILD ]; then read_username_from_user
build_win32 read_password_from_user
build_win64 get_openscad_source_code
fi if [ $DOBUILD ]; then
if [ $DOUPLOAD ]; then build_win32
upload_win32 build_win64
upload_win64 fi
update_win_www_download_links if [ $DOUPLOAD ]; then
fi upload_win32
upload_win64
update_win_www_download_links
fi
}
if [ $DOLOOP ]; then
while [ 1 ]; do
init_variables $*
main $*
echo ---------------------------------------------------
echo main loop finished. repeating in $LOOPSLEEP seconds
echo ---------------------------------------------------
sleep $LOOPSLEEP
#if [ "`uname | grep -i linux`" ]; then
# rtcwake -m mem -s 86400
#fi
done
else
main $*
fi

View File

@ -37,13 +37,9 @@ debug()
eigen_sysver() eigen_sysver()
{ {
debug eigen debug eigen
eigpath= eigpath=$1/include/eigen3/Eigen/src/Core/util/Macros.h
eig3path=$1/include/eigen3/Eigen/src/Core/util/Macros.h debug $eigpath
eig2path=$1/include/eigen2/Eigen/src/Core/util/Macros.h if [ ! -e $eigpath ]; then return; fi
if [ -e $eig3path ]; then eigpath=$eig3path; fi
if [ -e $eig2path ]; then eigpath=$eig2path; fi
debug $eig2path
if [ ! $eigpath ]; then return; fi
eswrld=`grep "define *EIGEN_WORLD_VERSION *[0-9]*" $eigpath | awk '{print $3}'` eswrld=`grep "define *EIGEN_WORLD_VERSION *[0-9]*" $eigpath | awk '{print $3}'`
esmaj=`grep "define *EIGEN_MAJOR_VERSION *[0-9]*" $eigpath | awk '{print $3}'` esmaj=`grep "define *EIGEN_MAJOR_VERSION *[0-9]*" $eigpath | awk '{print $3}'`
esmin=`grep "define *EIGEN_MINOR_VERSION *[0-9]*" $eigpath | awk '{print $3}'` esmin=`grep "define *EIGEN_MINOR_VERSION *[0-9]*" $eigpath | awk '{print $3}'`
@ -71,20 +67,25 @@ glib2_sysver()
#Get architecture triplet - e.g. x86_64-linux-gnu #Get architecture triplet - e.g. x86_64-linux-gnu
glib2archtriplet=`gcc -dumpmachine 2>/dev/null` glib2archtriplet=`gcc -dumpmachine 2>/dev/null`
if [ -z "$VAR" ]; then if [ -z "$VAR" ]; then
glib2archtriplet=`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null` if [ "`command -v dpkg-architectures`" ]; then
glib2archtriplet=`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`
fi
fi fi
glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
if [ ! -e $glib2path ]; then if [ ! -e $glib2path ]; then
#No glib found #No glib found
#glib can be installed in /usr/lib/i386-linux-gnu/glib-2.0/ on arch i686-linux-gnu (sometimes?) #glib can be installed in /usr/lib/i386-linux-gnu/glib-2.0/ on arch i686-linux-gnu (sometimes?)
if [ $glib2archtriplet = "i686-linux-gnu" ]; then if [ "$glib2archtriplet" = "i686-linux-gnu" ]; then
glib2archtriplet=i386-linux-gnu glib2archtriplet=i386-linux-gnu
glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
if [ ! -e $glib2path ]; then return; fi
else
return;
fi fi
fi fi
if [ ! -e $glib2path ]; then
glib2path=$1/lib/glib-2.0/include/glibconfig.h
fi
if [ ! -e $glib2path ]; then
return
fi
glib2major=`grep "define *GLIB_MAJOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'` glib2major=`grep "define *GLIB_MAJOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
glib2minor=`grep "define *GLIB_MINOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'` glib2minor=`grep "define *GLIB_MINOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
glib2micro=`grep "define *GLIB_MICRO_VERSION *[0-9.]*" $glib2path | awk '{print $3}'` glib2micro=`grep "define *GLIB_MICRO_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`

7
scripts/github-release.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# Usage (in github root folder): ./scripts/github-release.sh <version>
#
# Requires release.token and releases/<version>.md
curl https://api.github.com/repos/openscad/openscad/releases -H "Authorization: token $(<release.token)" -d "$(./scripts/makereleasejson.py $1)"

View File

@ -30,7 +30,6 @@ OPTION_LLVM=false
OPTION_CLANG=false OPTION_CLANG=false
OPTION_GCC=false OPTION_GCC=false
OPTION_DEPLOY=false OPTION_DEPLOY=false
export QMAKESPEC=macx-g++
printUsage() printUsage()
{ {
@ -114,6 +113,21 @@ build_qt()
make -j6 install make -j6 install
} }
build_qt5()
{
version=$1
echo "Building Qt" $version "..."
cd $BASEDIR/src
rm -rf qt-everywhere-opensource-src-$version
if [ ! -f qt-everywhere-opensource-src-$version.tar.gz ]; then
curl -O -L http://download.qt-project.org/official_releases/qt/5.2/$version/single/qt-everywhere-opensource-src-$version.tar.gz
fi
tar xzf qt-everywhere-opensource-src-$version.tar.gz
cd qt-everywhere-opensource-src-$version
./configure -prefix $DEPLOYDIR -release -opensource -confirm-license -nomake examples -nomake tests -no-xcb -no-c++11
make -j6 install
}
# Hack warning: gmplib is built separately in 32-bit and 64-bit mode # Hack warning: gmplib is built separately in 32-bit and 64-bit mode
# and then merged afterwards. gmplib's header files are dependent on # and then merged afterwards. gmplib's header files are dependent on
# the CPU architecture on which configure was run and will be patched accordingly. # the CPU architecture on which configure was run and will be patched accordingly.
@ -523,7 +537,6 @@ elif $USING_CLANG; then
echo "Using clang compiler" echo "Using clang compiler"
export CC=clang export CC=clang
export CXX=clang++ export CXX=clang++
export QMAKESPEC=unsupported/macx-clang
fi fi
echo "Building for $MAC_OSX_VERSION_MIN or later" echo "Building for $MAC_OSX_VERSION_MIN or later"
@ -548,7 +561,7 @@ fi
echo "Using basedir:" $BASEDIR echo "Using basedir:" $BASEDIR
mkdir -p $SRCDIR $DEPLOYDIR mkdir -p $SRCDIR $DEPLOYDIR
build_qt 4.8.5 build_qt5 5.2.1
# NB! For eigen, also update the path in the function # NB! For eigen, also update the path in the function
build_eigen 3.2.0 build_eigen 3.2.0
build_gmp 5.1.3 build_gmp 5.1.3

View File

@ -20,7 +20,6 @@ OPENSCADDIR=$PWD
BASEDIR=$OPENSCADDIR/../libraries BASEDIR=$OPENSCADDIR/../libraries
DEPLOYDIR=$BASEDIR/homebrew DEPLOYDIR=$BASEDIR/homebrew
MAC_OSX_VERSION_MIN=10.7 MAC_OSX_VERSION_MIN=10.7
export QMAKESPEC=unsupported/macx-clang
OPTION_DEPLOY=false OPTION_DEPLOY=false
@ -65,8 +64,8 @@ export MACOSX_DEPLOYMENT_TARGET=$MAC_OSX_VERSION_MIN
# Don't use bottles, as they might be built with the wrong deployment target # Don't use bottles, as they might be built with the wrong deployment target
export HOMEBREW_BUILD_FROM_SOURCE=1 export HOMEBREW_BUILD_FROM_SOURCE=1
for formula in qt eigen boost cgal glew glib opencsg freetype libxml2 fontconfig harfbuzz; do for formula in qt5 eigen boost cgal glew glib opencsg freetype libxml2 fontconfig harfbuzz; do
brew install --verbose openscad/tap/$formula --macosx-deployment-target=$MAC_OSX_VERSION_MIN brew install openscad/tap/$formula
done done
if $OPTION_DEPLOY; then if $OPTION_DEPLOY; then
brew install --HEAD openscad/tap/sparkle brew install --HEAD openscad/tap/sparkle

13
scripts/makereleasejson.py Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python
# Used by github-release.sh
import sys
import json
v = sys.argv[1]
print(json.JSONEncoder().encode({
'tag_name': 'openscad-'+v,
'name': 'OpenSCAD '+v,
'body': open('./releases/'+v+'.md').read()
}))

9
scripts/makereleasenotes.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
echo "" > RELEASE_NOTES
for v in `ls -r releases/*.md`; do
mdfile=${v#releases/}
version=${mdfile%.md}
echo "# OpenSCAD $version\n" >> RELEASE_NOTES
cat $v >> RELEASE_NOTES
done

View File

@ -58,19 +58,21 @@ fi
echo "entering" $MXEDIR echo "entering" $MXEDIR
cd $MXEDIR cd $MXEDIR
echo 'checkout stable branch'
git checkout stable
if [ "`echo $* | grep 64`" ]; then if [ "`echo $* | grep 64`" ]; then
MXE_TARGETS='x86_64-w64-mingw32' MXE_TARGETS='x86_64-w64-mingw32'
if [ "`echo $* | grep download`" ]; then if [ "`echo $* | grep download`" ]; then
PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-glib download-freetype download-fontconfig download-harfbuzz' PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-qt5 download-glib download-freetype download-fontconfig download-harfbuzz'
else else
PACKAGES='mpfr eigen opencsg cgal qt glib freetype fontconfig harfbuzz' PACKAGES='qt qt5 mpfr eigen opencsg cgal glib freetype fontconfig harfbuzz'
fi fi
else else
MXE_TARGETS='i686-pc-mingw32' # fixme - does this work? test it. MXE_TARGETS='i686-pc-mingw32'
if [ "`echo $* | grep download`" ]; then if [ "`echo $* | grep download`" ]; then
PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-nsis download-glib download-freetype download-fontconfig download-harfbuzz' PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-qt5 download-nsis download-glib download-freetype download-fontconfig download-harfbuzz'
else else
PACKAGES='mpfr eigen opencsg cgal qt nsis glib freetype fontconfig harfbuzz' PACKAGES='qt qt5 mpfr eigen opencsg cgal nsis glib freetype fontconfig harfbuzz'
fi fi
fi fi
echo make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS echo make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS

View File

@ -1,8 +1,9 @@
#!/bin/sh #!/bin/sh
# NB! To build a release build, the VERSION and VERSIONDATE environment variables needs to be set. # NB! To build a release build, the VERSION and VERSIONDATE environment variables needs to be set.
# See doc/release-checklist.txt # See doc/release-checklist.txt
export NUMCPU=$(sysctl -n hw.ncpu)
human_filesize() human_filesize()
{ {
awk -v sum=$1 'BEGIN { awk -v sum=$1 'BEGIN {
@ -20,8 +21,9 @@ update_www_download_links()
local $* local $*
filesize=$(human_filesize $filesize) filesize=$(human_filesize $filesize)
webdir=../openscad.github.com webdir=../openscad.github.com
# FIXME: release vs. snapshot
incfile=inc/mac_snapshot_links.js incfile=inc/mac_snapshot_links.js
BASEURL='http://files.openscad.org/' BASEURL='http://files.openscad.org/snapshots'
DATECODE=`date +"%Y.%m.%d"` DATECODE=`date +"%Y.%m.%d"`
if [ -f $webdir/$incfile ]; then if [ -f $webdir/$incfile ]; then
@ -43,20 +45,27 @@ if test -z "$VERSIONDATE"; then
fi fi
if test -z "$VERSION"; then if test -z "$VERSION"; then
VERSION=$VERSIONDATE VERSION=$VERSIONDATE
COMMIT=-c SNAPSHOT=snapshot
SNAPSHOT=true
fi fi
# Turn off ccache, just for safety # Turn off ccache, just for safety
PATH=${PATH//\/opt\/local\/libexec\/ccache:} PATH=${PATH//\/opt\/local\/libexec\/ccache:}
# FIXME: Somehow, setting the deployment target to a lower version causes a
# seldom crash in debug mode (e.g. the minkowski2-test):
# frame #4: 0x00007fff8b7d5be5 libc++.1.dylib`std::runtime_error::~runtime_error() + 55
# frame #5: 0x0000000100150df5 OpenSCAD`CGAL::Uncertain_conversion_exception::~Uncertain_conversion_exception(this=0x0000000105044488) + 21 at Uncertain.h:78
# The reason for the crash appears to be linking with libgcc_s,
# but it's unclear what's really going on
export MACOSX_DEPLOYMENT_TARGET=10.6
# This is the same location as DEPLOYDIR in macosx-build-dependencies.sh # This is the same location as DEPLOYDIR in macosx-build-dependencies.sh
export OPENSCAD_LIBRARIES=$PWD/../libraries/install export OPENSCAD_LIBRARIES=$PWD/../libraries/install
# Make sure that the correct Qt tools are used # Make sure that the correct Qt tools are used
export PATH=$OPENSCAD_LIBRARIES/bin:$PATH export PATH=$OPENSCAD_LIBRARIES/bin:$PATH
`dirname $0`/release-common.sh -v $VERSION $COMMIT `dirname $0`/release-common.sh -v $VERSION $SNAPSHOT
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
exit 1 exit 1
fi fi
@ -83,7 +92,7 @@ if [[ $VERSION == $VERSIONDATE ]]; then
fi fi
echo "Uploading..." echo "Uploading..."
scp OpenSCAD-$VERSION.dmg openscad@files.openscad.org:www scp OpenSCAD-$VERSION.dmg openscad@files.openscad.org:www/snapshots
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
exit 1 exit 1
fi fi

View File

@ -1,28 +1,47 @@
#!/bin/bash #!/bin/bash
# #
# This script creates a binary release of OpenSCAD. This should work # This script creates a binary release of OpenSCAD. This should work
# under Mac OS X, Linux 32, Linux 64, and Linux->Win32 MXE cross-build. # under Mac OS X, Linux 32bit, Linux 64bit, and Linux->Win32 MXE cross-build.
# Windows under msys has not been tested recently. # Windows under msys has not been tested recently.
# #
# The script will create a file called openscad-<versionstring>.<extension> in # The script will create a file called openscad-<versionstring>.<extension> in
# the current directory (or under ./mingw32) # the current directory (or under ./mingw32 or ./mingw64)
# #
# Usage: release-common.sh [-v <versionstring>] [-c] [-mingw[32|64]] # Usage: release-common.sh [-v <versionstring>] [-c] [-mingw[32|64]] [-tests]
# -v Version string (e.g. -v 2010.01) # -v Version string (e.g. -v 2010.01)
# -d Version date (e.g. -d 2010.01.23) # -d Version date (e.g. -d 2010.01.23)
# -c Build with commit info
# -mingw32 Cross-compile for win32 using MXE # -mingw32 Cross-compile for win32 using MXE
# -mingw64 Cross-compile for win64 using Tony Theodore's MXE fork # -mingw64 Cross-compile for win64 using MXE
# -snapshot Build a snapshot binary (make e.g. experimental features available, build with commit info)
# -tests Build additional package containing the regression tests
# #
# If no version string or version date is given, todays date will be used (YYYY-MM-DD) # If no version string or version date is given, todays date will be used (YYYY-MM-DD)
# If only verion date is given, it will be used also as version string. # If only version date is given, it will be used also as version string.
# If no make target is given, release will be used on Windows, none one Mac OS X # If no make target is given, release will be used on Windows, none one Mac OS X
# #
# The commit info will extracted from git and be passed to qmake as OPENSCAD_COMMIT
# to identify a build in the about box.
#
# The mingw cross compile depends on the MXE cross-build tools. Please # The mingw cross compile depends on the MXE cross-build tools. Please
# see the README.md file on how to install these dependencies. # see the README.md file on how to install these dependencies. To debug
# the mingw-cross build process, set env var FAKEMAKE=1 to fake-make the
# .exe files
#
# convert end-of-line in given file from unix \n to dos/windows(TM) \r\n
# see https://kb.iu.edu/data/acux.html
lf2crlf()
{
fname=$1
if [ "`command -v unix2dos`" ]; then
unix2dos $fname
return
fi
if [ "`command -v awk`" ]; then
echo using awk to convert end of line markers in $fname
awk 'sub("$", "\r")' $fname > $fname".temp"
mv $fname".temp" $fname
return
fi
echo 'warning- cant change eol to cr eol'
}
printUsage() printUsage()
{ {
@ -37,6 +56,8 @@ if [ ! -f $OPENSCADDIR/openscad.pro ]; then
exit 1 exit 1
fi fi
CONFIG=deploy
if [[ "$OSTYPE" =~ "darwin" ]]; then if [[ "$OSTYPE" =~ "darwin" ]]; then
OS=MACOSX OS=MACOSX
elif [[ $OSTYPE == "msys" ]]; then elif [[ $OSTYPE == "msys" ]]; then
@ -63,6 +84,16 @@ if [ "`echo $* | grep mingw64`" ]; then
echo Mingw-cross build using ARCH=64 echo Mingw-cross build using ARCH=64
fi fi
if [ "`echo $* | grep snapshot`" ]; then
CONFIG="$CONFIG experimental"
OPENSCAD_COMMIT=`git log -1 --pretty=format:"%h"`
fi
BUILD_TESTS=
if [ "`echo $* | grep tests`" ]; then
BUILD_TESTS=1
fi
if [ $OS ]; then if [ $OS ]; then
echo "Detected OS: $OS" echo "Detected OS: $OS"
else else
@ -75,7 +106,6 @@ do
case $c in case $c in
v) VERSION=$OPTARG;; v) VERSION=$OPTARG;;
d) VERSIONDATE=$OPTARG;; d) VERSIONDATE=$OPTARG;;
c) OPENSCAD_COMMIT=`git log -1 --pretty=format:"%h"`
esac esac
done done
@ -86,6 +116,14 @@ if test -z "$VERSION"; then
VERSION=$VERSIONDATE VERSION=$VERSIONDATE
fi fi
export VERSIONDATE
export VERSION
if [ $FAKEMAKE ]; then
echo 'fake make on:' $FAKEMAKE
else
FAKEMAKE=
fi
echo "Checking pre-requisites..." echo "Checking pre-requisites..."
@ -95,9 +133,17 @@ case $OS in
if [ "`command -v makensis`" ]; then if [ "`command -v makensis`" ]; then
MAKENSIS=makensis MAKENSIS=makensis
elif [ "`command -v i686-pc-mingw32-makensis`" ]; then elif [ "`command -v i686-pc-mingw32-makensis`" ]; then
# we cant find systems nsis so look for the MXE's version.
# MXE has its own makensis, but its only available under
# 32-bit MXE. note that the cross-version in theory works
# the same as the linux version so we can use them, in
# theory, interchangeably. its not really a 'cross' nsis
# todo - when doing 64 bit mingw build, see if we can call
# 32bit nsis here.
MAKENSIS=i686-pc-mingw32-makensis MAKENSIS=i686-pc-mingw32-makensis
else else
echo "makensis not found. please install nsis" echo "makensis not found. please install nsis on your system."
echo "(for example, on debian linux, try apt-get install nsis)"
exit 1 exit 1
fi fi
echo NSIS makensis found: $MAKENSIS echo NSIS makensis found: $MAKENSIS
@ -116,7 +162,7 @@ if [ -d .git ]; then
git submodule update git submodule update
fi fi
echo "Building openscad-$VERSION ($VERSIONDATE) $CONFIGURATION..." echo "Building openscad-$VERSION ($VERSIONDATE) $CONFIG..."
if [ ! $NUMCPU ]; then if [ ! $NUMCPU ]; then
echo "note: you can 'export NUMCPU=x' for multi-core compiles (x=number)"; echo "note: you can 'export NUMCPU=x' for multi-core compiles (x=number)";
@ -124,7 +170,8 @@ if [ ! $NUMCPU ]; then
fi fi
echo "NUMCPU: " $NUMCPU echo "NUMCPU: " $NUMCPU
CONFIG=deploy
case $OS in case $OS in
LINUX|MACOSX) LINUX|MACOSX)
TARGET= TARGET=
@ -148,11 +195,11 @@ esac
case $OS in case $OS in
UNIX_CROSS_WIN) UNIX_CROSS_WIN)
cd $DEPLOYDIR && qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+=$CONFIG CONFIG+=mingw-cross-env CONFIG-=debug ../openscad.pro cd $DEPLOYDIR && qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+="$CONFIG" CONFIG+=mingw-cross-env CONFIG-=debug ../openscad.pro
cd $OPENSCADDIR cd $OPENSCADDIR
;; ;;
*) *)
qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+=$CONFIG CONFIG-=debug openscad.pro qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+="$CONFIG" CONFIG-=debug openscad.pro
;; ;;
esac esac
@ -184,11 +231,17 @@ case $OS in
;; ;;
esac esac
echo "Building GUI binary..."
case $OS in case $OS in
UNIX_CROSS_WIN) UNIX_CROSS_WIN)
# make main openscad.exe # make main openscad.exe
cd $DEPLOYDIR cd $DEPLOYDIR
make $TARGET -j$NUMCPU ## comment 4 test if [ $FAKEMAKE ]; then
echo "notexe. debugging build process" > $TARGET/openscad.exe
else
make $TARGET -j$NUMCPU
fi
if [ ! -e $TARGET/openscad.exe ]; then if [ ! -e $TARGET/openscad.exe ]; then
echo "cant find $TARGET/openscad.exe. build failed. stopping." echo "cant find $TARGET/openscad.exe. build failed. stopping."
exit exit
@ -200,9 +253,15 @@ case $OS in
echo "cant find $TARGET/openscad.com. build failed. stopping." echo "cant find $TARGET/openscad.com. build failed. stopping."
exit exit
fi fi
cd $OPENSCADDIR cd $OPENSCADDIR
;; ;;
LINUX)
if [ $FAKEMAKE ]; then
echo "notexe. debugging build process" > $TARGET/openscad
else
make $TARGET -j$NUMCPU
fi
;;
*) *)
make -j$NUMCPU $TARGET make -j$NUMCPU $TARGET
;; ;;
@ -213,6 +272,45 @@ if [[ $? != 0 ]]; then
exit 1 exit 1
fi fi
echo "Building test suite..."
if [ $BUILD_TESTS ]; then
case $OS in
UNIX_CROSS_WIN)
TESTBUILD_MACHINE=x86_64-w64-mingw32
# dont use build-machine trilpe in TESTBINDIR because the 'mingw32'
# will confuse people who are on 64 bit machines
TESTBINDIR=tests-build
export TESTBUILD_MACHINE
export TESTBINDIR
if [[ $ARCH == 32 ]]; then
TESTBUILD_MACHINE=i686-pc-mingw32
fi
cd $DEPLOYDIR
mkdir $TESTBINDIR
cd $TESTBINDIR
cmake $OPENSCADDIR/tests/ \
-DCMAKE_TOOLCHAIN_FILE=../tests/CMingw-cross-env.cmake \
-DMINGW_CROSS_ENV_DIR=$MXEDIR \
-DMACHINE=$TESTBUILD_MACHINE
if [ $FAKEMAKE ]; then
echo "notexe. debugging build process" > openscad_nogui.exe
else
make -j$NUMCPU
fi
if [ ! -e openscad_nogui.exe ]; then
echo 'test cross-build failed'
exit 1
fi
cd $OPENSCADDIR
;;
*)
echo 'test suite build not implemented for osx/linux'
;;
esac
fi # BUILD_TESTS
echo "Creating directory structure..." echo "Creating directory structure..."
case $OS in case $OS in
@ -222,6 +320,7 @@ case $OS in
FONTDIR=OpenSCAD.app/Contents/Resources/fonts FONTDIR=OpenSCAD.app/Contents/Resources/fonts
;; ;;
UNIX_CROSS_WIN) UNIX_CROSS_WIN)
cd $OPENSCADDIR
EXAMPLESDIR=$DEPLOYDIR/openscad-$VERSION/examples/ EXAMPLESDIR=$DEPLOYDIR/openscad-$VERSION/examples/
LIBRARYDIR=$DEPLOYDIR/openscad-$VERSION/libraries/ LIBRARYDIR=$DEPLOYDIR/openscad-$VERSION/libraries/
FONTDIR=$DEPLOYDIR/openscad-$VERSION/fonts/ FONTDIR=$DEPLOYDIR/openscad-$VERSION/fonts/
@ -238,10 +337,10 @@ case $OS in
esac esac
if [ -n $EXAMPLESDIR ]; then if [ -n $EXAMPLESDIR ]; then
echo $EXAMPLESDIR echo $EXAMPLESDIR
mkdir -p $EXAMPLESDIR mkdir -p $EXAMPLESDIR
cp examples/* $EXAMPLESDIR cp examples/* $EXAMPLESDIR
chmod -R 644 $EXAMPLESDIR/* chmod -R 644 $EXAMPLESDIR/*
fi fi
if [ -n $FONTDIR ]; then if [ -n $FONTDIR ]; then
echo $FONTDIR echo $FONTDIR
@ -249,16 +348,16 @@ if [ -n $FONTDIR ]; then
cp -a fonts/* $FONTDIR cp -a fonts/* $FONTDIR
fi fi
if [ -n $LIBRARYDIR ]; then if [ -n $LIBRARYDIR ]; then
echo $LIBRARYDIR echo $LIBRARYDIR
mkdir -p $LIBRARYDIR mkdir -p $LIBRARYDIR
# exclude the .git stuff from MCAD which is a git submodule. # exclude the .git stuff from MCAD which is a git submodule.
# tar is a relatively portable way to do exclusion, without the # tar is a relatively portable way to do exclusion, without the
# risks of rm # risks of rm
rm -f libraries.tar rm -f libraries.tar
tar cf libraries.tar --exclude=.git* libraries tar cf libraries.tar --exclude=.git* libraries
cd $LIBRARYDIR/.. && tar xf $OPENSCADDIR/libraries.tar && cd $OPENSCADDIR cd $LIBRARYDIR/.. && tar xf $OPENSCADDIR/libraries.tar && cd $OPENSCADDIR
rm -f libraries.tar rm -f libraries.tar
chmod -R u=rwx,go=r,+X $LIBRARYDIR/* chmod -R u=rwx,go=r,+X $LIBRARYDIR/*
fi fi
echo "Creating archive.." echo "Creating archive.."
@ -282,12 +381,13 @@ case $OS in
echo "Binary created: openscad-$VERSION.zip" echo "Binary created: openscad-$VERSION.zip"
;; ;;
UNIX_CROSS_WIN) UNIX_CROSS_WIN)
cd $OPENSCADDIR
cd $DEPLOYDIR
BINFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH.zip BINFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH.zip
INSTFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH-Installer.exe INSTFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH-Installer.exe
#package #package
echo "Creating binary zip package" echo "Creating binary zip package"
cd $DEPLOYDIR
cp $TARGET/openscad.exe openscad-$VERSION cp $TARGET/openscad.exe openscad-$VERSION
cp $TARGET/openscad.com openscad-$VERSION cp $TARGET/openscad.com openscad-$VERSION
rm -f OpenSCAD-$VERSION.x86-$ARCH.zip rm -f OpenSCAD-$VERSION.x86-$ARCH.zip
@ -351,3 +451,118 @@ case $OS in
echo echo
;; ;;
esac esac
if [ $BUILD_TESTS ]; then
echo "Creating regression tests package..."
case $OS in
MACOSX)
echo 'building regression test package on OSX not implemented'
;;
WIN)
echo 'building regression test package on Win not implemented'
;;
UNIX_CROSS_WIN)
# Build a .zip file containing all the files we need to run a
# ctest on Windows(TM). For the sake of simplicity, we do not
# create an installer for the tests.
echo "Copying files..."
cd $OPENSCADDIR
# This copies a lot of unnecessary stuff but that's OK.
# as above, we use tar as a somewhat portable way to do 'exclude'
# while copying.
rm -f ./ostests.tar
for subdir in tests testdata libraries examples doc; do
tar prvf ./ostests.tar --exclude=.git* --exclude=*/mingw64/* --exclude=*/mingw32/* --exclude=*.cc.obj --exclude=*.a $subdir
done
cd $DEPLOYDIR
tar prvf $OPENSCADDIR/ostests.tar --exclude=.git* --exclude=*/mingw* --exclude=*.cc.obj --exclude=*.a $TESTBINDIR
cd $DEPLOYDIR
if [ -e ./OpenSCAD-Tests-$VERSION ]; then
rm -rf ./OpenSCAD-Tests-$VERSION
fi
mkdir OpenSCAD-Tests-$VERSION
cd OpenSCAD-Tests-$VERSION
tar pxf $OPENSCADDIR/ostests.tar
rm -f $OPENSCADDIR/ostests.tar
# Now we have the basic files copied into our tree that will become
# our .zip file. We also want to move some files around for easier
# access for the user:
cd $DEPLOYDIR
cd ./OpenSCAD-Tests-$VERSION
echo "Copying files for ease of use when running from cmdline"
cp -v ./tests/OpenSCAD_Test_Console.py .
cp -v ./tests/WinReadme.txt .
cp -v ./tests/mingw_convert_ctest.py ./$TESTBINDIR
cp -v ./tests/mingwcon.bat ./$TESTBINDIR
echo "Creating mingw_cross_info.py file"
cd $DEPLOYDIR
cd ./OpenSCAD-Tests-$VERSION
cd $TESTBINDIR
if [ -e ./mingw_cross_info.py ]; then
rm -f ./mingw_cross_info.py
fi
echo "# created automatically by release-common.sh from within linux " >> mingw_cross_info.py
echo "linux_abs_basedir='"$OPENSCADDIR"'" >> mingw_cross_info.py
echo "linux_abs_builddir='"$DEPLOYDIR/$TESTBINDIR"'" >> mingw_cross_info.py
echo "bindir='"$TESTBINDIR"'" >> mingw_cross_info.py
# fixme .. parse CTestTestfiles to find linux+convert python strings
# or have CMake itself dump them during it's cross build cmake call
echo "linux_python='"`which python`"'" >> mingw_cross_info.py
# note- this has to match the CMakeLists.txt line that sets the
# convert executable... and CMingw-cross-env.cmake's skip-imagemagick
# setting. what a kludge!
echo "linux_convert='/bin/echo'" >> mingw_cross_info.py
echo "win_installdir='OpenSCAD_Tests_"$VERSIONDATE"'" >> mingw_cross_info.py
echo 'Converting linefeed to carriage-return+linefeed'
for textfile in `find . | grep txt$`; do lf2crlf $textfile; done
for textfile in `find . | grep py$`; do lf2crlf $textfile; done
for textfile in `find . | grep cmake$`; do lf2crlf $textfile; done
for textfile in `find . | grep bat$`; do lf2crlf $textfile; done
# Test binaries can be hundreds of megabytes due to debugging info.
# By default, we strip that. In most cases we wont need it and it
# causes too many problems to have >100MB files.
echo "stripping .exe binaries"
cd $DEPLOYDIR
cd ./OpenSCAD-Tests-$VERSION
cd $TESTBINDIR
if [ "`command -v $TESTBUILD_MACHINE'-strip' `" ]; then
for exefile in *exe; do
ls -sh $exefile
echo $TESTBUILD_MACHINE'-strip' $exefile
$TESTBUILD_MACHINE'-strip' $exefile
ls -sh $exefile
done
fi
# Build the actual .zip archive based on the file tree we've built above
cd $DEPLOYDIR
ZIPFILE=OpenSCAD-Tests-$VERSION-x86-$ARCH.zip
echo "Creating binary zip package for Tests:" $ZIPFILE
rm -f ./$ZIPFILE
"$ZIP" $ZIPARGS $ZIPFILE OpenSCAD-Tests-$VERSION
if [ -e $ZIPFILE ]; then
echo "ZIP package created:" `pwd`/$ZIPFILE
else
echo "Build of Regression Tests package failed. Cannot find" `pwd`/$ZIPFILE
exit 1
fi
cd $OPENSCADDIR
;;
LINUX)
echo 'building regression test package on linux not implemented'
;;
esac
else
echo "Not building regression tests package"
fi # BUILD_TESTS

View File

@ -7,6 +7,7 @@
# source ./scripts/setenv-mingw-xbuild.sh # 32 bit build # source ./scripts/setenv-mingw-xbuild.sh # 32 bit build
# source ./scripts/setenv-mingw-xbuild.sh 64 # 64 bit build # source ./scripts/setenv-mingw-xbuild.sh 64 # 64 bit build
# source ./scripts/setenv-mingw-xbuild.sh clean # Clean up exported variables # source ./scripts/setenv-mingw-xbuild.sh clean # Clean up exported variables
# source ./scripts/setenv-mingw-xbuild.sh qt5 # use qt5 (experimental)
# #
# Prerequisites: # Prerequisites:
# #
@ -40,6 +41,15 @@ if [ ! $MXEDIR ]; then
fi fi
fi fi
if [ ! $MXEQTSUBDIR ]; then
if [ "`echo $* | grep qt5 `" ]; then
MXEQTSUBDIR=qt5
else
# qt4 is just 'qt', see http://mxe.cc
MXEQTSUBDIR=qt
fi
fi
if [ ! -e $DEPLOYDIR ]; then if [ ! -e $DEPLOYDIR ]; then
mkdir -p $DEPLOYDIR mkdir -p $DEPLOYDIR
fi fi
@ -56,7 +66,7 @@ if [ ! $MINGWX_SAVED_ORIGINAL_PATH ]; then
fi fi
PATH=$MXEDIR/usr/bin:$PATH PATH=$MXEDIR/usr/bin:$PATH
PATH=$MXETARGETDIR/qt/bin:$PATH PATH=$MXETARGETDIR/$MXEQTSUBDIR/bin:$PATH
OPENSCAD_LIBRARIES=$MXETARGETDIR OPENSCAD_LIBRARIES=$MXETARGETDIR
@ -68,6 +78,7 @@ if [ "`echo $* | grep clean`" ]; then
DEPLOYDIR= DEPLOYDIR=
PATH=$MINGWX_SAVED_ORIGINAL_PATH PATH=$MINGWX_SAVED_ORIGINAL_PATH
MINGWX_SAVED_ORIGINAL_PATH= MINGWX_SAVED_ORIGINAL_PATH=
MXEQTSUBDIR=
else else
echo 'linking' $MXETARGETDIR echo 'linking' $MXETARGETDIR
echo ' to' $DEPLOYDIR/mingw-cross-env echo ' to' $DEPLOYDIR/mingw-cross-env
@ -82,16 +93,18 @@ export MXETARGETDIR
export DEPLOYDIR export DEPLOYDIR
export PATH export PATH
export MINGWX_SAVED_ORIGINAL_PATH export MINGWX_SAVED_ORIGINAL_PATH
export MXEQTSUBDIR
echo OPENSCAD_LIBRARIES: $OPENSCAD_LIBRARIES echo OPENSCAD_LIBRARIES: $OPENSCAD_LIBRARIES
echo BASEDIR: $BASEDIR echo BASEDIR: $BASEDIR
echo MXEDIR: $MXEDIR echo MXEDIR: $MXEDIR
echo MXETARGETDIR: $MXETARGETDIR echo MXETARGETDIR: $MXETARGETDIR
echo DEPLOYDIR: $DEPLOYDIR echo DEPLOYDIR: $DEPLOYDIR
echo MXEQTSUBDIR: $MXEQTSUBDIR
if [ "`echo $* | grep clean`" ]; then if [ "`echo $* | grep clean`" ]; then
echo PATH restored to pre-setenv-mingw-x state echo PATH restored to pre-setenv-mingw-x state
else else
echo PATH modified: $MXEDIR/usr/bin echo PATH modified: $MXEDIR/usr/bin
echo PATH modified: $MXETARGETDIR/qt/bin echo PATH modified: $MXETARGETDIR/$MXEQTSUBDIR/bin
fi fi

View File

@ -32,11 +32,13 @@
# If your system lacks qt4, build like this: # If your system lacks qt4, build like this:
# #
# ./scripts/uni-build-dependencies.sh qt4 # ./scripts/uni-build-dependencies.sh qt4
# . ./scripts/setenv-unibuild.sh # . ./scripts/setenv-unibuild.sh #(Rerun to re-detect qt4)
# #
# If your system lacks glu, try to build like this: # If your system lacks glu, gettext, or glib2, you can build them as well:
# #
# ./scripts/uni-build-dependencies.sh glu # ./scripts/uni-build-dependencies.sh glu
# ./scripts/uni-build-dependencies.sh glib2
# ./scripts/uni-build-dependencies.sh gettext
# #
# If you want to try Clang compiler (experimental, only works on linux): # If you want to try Clang compiler (experimental, only works on linux):
# #
@ -528,6 +530,101 @@ build_eigen()
} }
# glib2 and dependencies
build_gettext()
{
version=$1
echo "Building gettext $version..."
cd "$BASEDIR"/src
rm -rf "gettext-$version"
if [ ! -f "glib-$version.tar.gz" ]; then
curl --insecure -LO "http://ftpmirror.gnu.org/gettext/gettext-$version.tar.gz"
fi
tar xzf "gettext-$version.tar.gz"
cd "gettext-$version"
./configure --prefix="$DEPLOYDIR"
make -j$NUMCPU
make install
}
build_pkgconfig()
{
if [ "`command -v pkg-config`" ]; then
echo "pkg-config already installed. not building"
return
fi
version=$1
echo "Building pkg-config $version..."
cd "$BASEDIR"/src
rm -rf "pkg-config-$version"
if [ ! -f "pkg-config-$version.tar.gz" ]; then
curl --insecure -LO "http://pkgconfig.freedesktop.org/releases/pkg-config-$version.tar.gz"
fi
tar xzf "pkg-config-$version.tar.gz"
cd "pkg-config-$version"
./configure --prefix="$DEPLOYDIR" --with-internal-glib
make -j$NUMCPU
make install
}
build_libffi()
{
if [ -e $DEPLOYDIR/include/ffi.h ]; then
echo "libffi already installed. not building"
return
fi
version=$1
echo "Building libffi $version..."
cd "$BASEDIR"/src
rm -rf "libffi-$version"
if [ ! -f "libffi-$version.tar.gz" ]; then
curl --insecure -LO "ftp://sourceware.org/pub/libffi/libffi-$version.tar.gz"
curl --insecure -LO "http://www.linuxfromscratch.org/patches/blfs/svn/libffi-$version-includedir-1.patch"
fi
tar xzf "libffi-$version.tar.gz"
cd "libffi-$version"
if [ ! "`command -v patch`" ]; then
echo cannot proceed, need 'patch' program
exit 1
fi
patch -Np1 -i ../libffi-3.0.13-includedir-1.patch
./configure --prefix="$DEPLOYDIR"
make -j$NUMCPU
make install
}
build_glib2()
{
version="$1"
maj_min_version="${version%.*}" #Drop micro
if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
echo "glib2 already installed. not building"
return
fi
echo "Building glib2 $version..."
cd "$BASEDIR"/src
rm -rf "glib-$version"
if [ ! -f "glib-$version.tar.xz" ]; then
curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
fi
tar xJf "glib-$version.tar.xz"
cd "glib-$version"
./configure --disable-gtk-doc --disable-man --prefix="$DEPLOYDIR" CFLAGS="-I$DEPLOYDIR/include" LDFLAGS="-L$DEPLOYDIR/lib"
make -j$NUMCPU
make install
}
## end of glib2 stuff
# this section allows 'out of tree' builds, as long as the system has # this section allows 'out of tree' builds, as long as the system has
# the 'dirname' command installed # the 'dirname' command installed
@ -582,7 +679,8 @@ fi
if [ ! "`command -v cmake`" ]; then if [ ! "`command -v cmake`" ]; then
build_cmake 2.8.8 build_cmake 2.8.8
fi fi
if [ "`cmake --version | grep 'version 2.[1-6][^0-9]'`" ]; then # see README for needed version (this should match 1<minimum)
if [ "`cmake --version | grep 'version 2.[1-8][^0-9][1-4] '`" ]; then
build_cmake 2.8.8 build_cmake 2.8.8
fi fi
@ -610,6 +708,19 @@ if [ $1 ]; then
build_glu 9.0.0 build_glu 9.0.0
exit $? exit $?
fi fi
if [ $1 = "gettext" ]; then
# such a huge build, put here by itself
build_gettext 0.18.3.1
exit $?
fi
if [ $1 = "glib2" ]; then
# such a huge build, put here by itself
build_pkgconfig 0.28
build_libffi 3.0.13
#build_gettext 0.18.3.1
build_glib2 2.38.2
exit $?
fi
fi fi
@ -618,7 +729,11 @@ fi
# #
# Main build of libraries # Main build of libraries
# edit version numbers here as needed. # edit version numbers here as needed.
# This is only for libraries most systems won't have new enough versions of.
# For big things like Qt4, see the notes at the head of this file on
# building individual dependencies.
# #
build_eigen 3.1.1 build_eigen 3.1.1
build_gmp 5.0.5 build_gmp 5.0.5
build_mpfr 3.1.1 build_mpfr 3.1.1

View File

@ -1,15 +1,15 @@
# auto-install dependency packages using the systems package manager. # auto-install dependency packages using the systems package manager.
# after running this, run ./script/check-dependencies.sh. see README.md # after running this, run ./script/check-dependencies.sh. see README.md
# #
# this assumes you have sudo installed or are running as root. # this assumes you have sudo installed and running, or are running as root.
# #
get_fedora_deps() get_fedora_deps()
{ {
sudo yum install qt-devel bison flex eigen3-devel python-paramiko \ yum install qt-devel bison flex eigen3-devel \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \ boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \ opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \
xorg-x11-server-Xvfb xorg-x11-server-Xvfb gettext
} }
get_qomo_deps() get_qomo_deps()
@ -21,7 +21,7 @@ get_altlinux_deps()
{ {
for i in boost-devel boost-filesystem-devel gcc4.5 gcc4.5-c++ boost-program_options-devel \ for i in boost-devel boost-filesystem-devel gcc4.5 gcc4.5-c++ boost-program_options-devel \
boost-thread-devel boost-system-devel boost-regex-devel eigen3 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \ boost-thread-devel boost-system-devel boost-regex-devel eigen3 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \
libglew-devel flex bison curl imagemagick glib2-devel; do sudo apt-get install $i; done libglew-devel flex bison curl imagemagick gettext glib2-devel; do apt-get install $i; done
} }
get_freebsd_deps() get_freebsd_deps()
@ -29,29 +29,29 @@ get_freebsd_deps()
pkg_add -r bison boost-libs cmake git bash eigen3 flex gmake gmp mpfr \ pkg_add -r bison boost-libs cmake git bash eigen3 flex gmake gmp mpfr \
xorg libGLU libXmu libXi xorg-vfbserver glew \ xorg libGLU libXmu libXi xorg-vfbserver glew \
qt4-corelib qt4-gui qt4-moc qt4-opengl qt4-qmake qt4-rcc qt4-uic \ qt4-corelib qt4-gui qt4-moc qt4-opengl qt4-qmake qt4-rcc qt4-uic \
opencsg cgal curl imagemagick glib2-devel opencsg cgal curl imagemagick glib2-devel gettext
} }
get_netbsd_deps() get_netbsd_deps()
{ {
sudo pkgin install bison boost cmake git bash eigen flex gmake gmp mpfr \ pkgin install bison boost cmake git bash eigen3 flex gmake gmp mpfr \
qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl \ qt4 glew cgal opencsg python27 curl \
imagemagick ImageMagick glib2-devel ImageMagick glib2 gettext
} }
get_opensuse_deps() get_opensuse_deps()
{ {
sudo zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \ zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \ libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \
glib2-devel glib2-devel gettext
} }
get_mageia_deps() get_mageia_deps()
{ {
sudo urpmi ctags urpmi ctags
sudo urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \ urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \
libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \ libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \
cmake imagemagick glib2-devel python curl git x11-server-xvfb cmake imagemagick glib2-devel python curl git x11-server-xvfb gettext
} }
get_debian_deps() get_debian_deps()
@ -60,8 +60,8 @@ get_debian_deps()
libxmu-dev cmake bison flex git-core libboost-all-dev \ libxmu-dev cmake bison flex git-core libboost-all-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev \ libXi-dev libmpfr-dev libboost-dev libglew-dev \
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \ libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \
python-paramiko curl imagemagick libfontconfig-dev libfreetype6-dev \ imagemagick libfontconfig-dev libfreetype6-dev \
libharfbuzz-dev gtk-doc-tools libglib2.0-dev; do libharfbuzz-dev gtk-doc-tools libglib2.0-dev gettext; do
sudo apt-get -y install $pkg; sudo apt-get -y install $pkg;
done done
} }

10
setenv_mac-qt5.sh Normal file
View File

@ -0,0 +1,10 @@
export OPENSCAD_LIBRARIES=$PWD/../libraries/install
export DYLD_LIBRARY_PATH=$OPENSCAD_LIBRARIES/lib
export DYLD_FRAMEWORK_PATH=$OPENSCAD_LIBRARIES/lib
# Our own Qt
export PATH=$OPENSCAD_LIBRARIES/bin:$PATH
# ccache:
export PATH=/opt/local/libexec/ccache:$PATH
export CCACHE_BASEDIR=$PWD/..

View File

@ -23,7 +23,7 @@
</p> </p>
<p> <p>
Copyright (C) 2009-2013 <a href="https://github.com/kintel">Marius Kintel</a> &lt;marius@kintel.net&gt; and <a href="http://clifford.at">Clifford Wolf</a> &lt;clifford@clifford.at&gt; Copyright (C) 2009-2014 <a href="https://github.com/kintel">Marius Kintel</a> &lt;marius@kintel.net&gt; and <a href="http://clifford.at">Clifford Wolf</a> &lt;clifford@clifford.at&gt;
</p> </p>
<p> <p>
@ -82,6 +82,7 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
<li><a href="http://www.github.com/GilesBathgate">Giles Bathgate</a> <li><a href="http://www.github.com/GilesBathgate">Giles Bathgate</a>
<li><a href="https://github.com/brad">Brad Pitcher</a> <li><a href="https://github.com/brad">Brad Pitcher</a>
<li><a href="https://github.com/donbright">Don Bright</a> <li><a href="https://github.com/donbright">Don Bright</a>
<li><a href="https://github.com/t-paul">Torsten Paul</a>
</ul> </ul>
<p> <p>
@ -89,29 +90,46 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
</p> </p>
<ul> <ul>
<li><a href="http://www.debian.org">Debian</a> maintainer:</b> <li><a href="http://www.debian.org">Debian</a> maintainer:</b>
<a href="http://christian.amsuess.com/">chrysn</a></lu> <a href="http://christian.amsuess.com/">chrysn</a></li>
<li><a href="http://fedoraproject.org/">Fedora</a> maintainer:</b> <li><a href="http://fedoraproject.org/">Fedora</a> maintainer:</b>
<a href="http://hroncok.cz/">Miro Hrončok</a></lu> <a href="http://hroncok.cz/">Miro Hrončok</a></li>
<li><a href="https://www.archlinux.org">Arch Linux</a> maintainer:</b>
<a href="http://kmkeen.com">Kyle Keen</a></li>
<li><a href="http://www.opensuse.org">openSUSE</a> maintainer:</b>
<a href="http://stick.gk2.sk">Pavol Rusnak</a></li>
</ul> </ul>
<p> <p>
<b>Patches</b> <b>Patches</b>
</p> </p>
<lu> <p>
<li><a href="https://github.com/meta23">meta23</a> <a href="https://github.com/meta23">meta23</a>,
<li><a href="https://github.com/jasonblewis">jasonblewis</a> <a href="https://github.com/jasonblewis">jasonblewis</a>,
<li><a href="https://github.com/gregjurman">gregjurman</a> <a href="https://github.com/gregjurman">gregjurman</a>,
<li><a href="https://github.com/brianolson">brianolson</a> <a href="https://github.com/brianolson">brianolson</a>,
<li><a href="https://github.com/tjhowse">tjhowse</a> <a href="https://github.com/tjhowse">tjhowse</a>,
<li><a href="https://github.com/logxen">logxen</a> <a href="https://github.com/logxen">logxen</a>,
<li><a href="https://github.com/iamwilhelm">iamwilhelm</a> <a href="https://github.com/iamwilhelm">iamwilhelm</a>,
<li><a href="https://github.com/clothbot">clothbot</a> <a href="https://github.com/clothbot">clothbot</a>,
<li><a href="https://github.com/colah">colah</a> <a href="https://github.com/colah">colah</a>,
<li><a href="https://github.com/peteruithoven">Peter Uithoven</a> <a href="https://github.com/peteruithoven">Peter Uithoven</a>,
<a href="https://github.com/tim-caper">tim-caper</a>,
</lu> <a href="https://github.com/sjkelly">sjkelly</a>,
<a href="https://github.com/OskarLinde">OskarLinde</a>,
<a href="https://github.com/Ivoah">Ivoah</a>,
<a href="https://github.com/brodykenrick">brodykenrick</a>,
<a href="https://github.com/a-e-m">a-e-m</a>,
<a href="https://github.com/gringer">gringer</a>,
<a href="https://github.com/Gazer">Gazer</a>,
<a href="https://github.com/pdbogen">pdbogen</a>,
<a href="https://github.com/hzeller">hzeller</a>,
<a href="https://github.com/vicnet">vicnet</a>,
<a href="https://github.com/dmopalmer">dmopalmer</a>,
<a href="https://github.com/steelman">steelman</a>,
<a href="https://github.com/ivoknutsel">ivoknutsel</a>,
<a href="https://github.com/achiestdragon">achiestdragon</a>
</p>
<p> <p>
<b>Mailing list, bug reports, testing, contribs, help, &c</b> <b>Mailing list, bug reports, testing, contribs, help, &c</b>

View File

@ -24,7 +24,7 @@ void installAppleEventHandlers()
{ {
// Reload handler // Reload handler
OSErr err = AEInstallEventHandler('SCAD', 'relo', NewAEEventHandlerUPP(eventHandler), 0, true); OSErr err = AEInstallEventHandler('SCAD', 'relo', NewAEEventHandlerUPP(eventHandler), 0, true);
require_noerr(err, CantInstallAppleEventHandler); __Require_noErr(err, CantInstallAppleEventHandler);
return; return;
CantInstallAppleEventHandler: CantInstallAppleEventHandler:

View File

@ -178,10 +178,10 @@ Response CSGTermEvaluator::visit(State &state, const RenderNode &node)
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (this->geomevaluator) { if (this->geomevaluator) {
geom = this->geomevaluator->evaluateGeometry(node, false); geom = this->geomevaluator->evaluateGeometry(node, false);
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
node.progress_report(); node.progress_report();
} }
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
this->stored_term[node.index()] = t1; this->stored_term[node.index()] = t1;
addToParent(state, node); addToParent(state, node);
} }
@ -196,10 +196,10 @@ Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node)
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (this->geomevaluator) { if (this->geomevaluator) {
geom = this->geomevaluator->evaluateGeometry(node, false); geom = this->geomevaluator->evaluateGeometry(node, false);
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
node.progress_report();
} }
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
node.progress_report();
this->stored_term[node.index()] = t1; this->stored_term[node.index()] = t1;
addToParent(state, node); addToParent(state, node);
} }

View File

@ -363,6 +363,7 @@ void GLView::showSmallaxes()
{ {
// Fixme - this doesnt work in Vector Camera mode // Fixme - this doesnt work in Vector Camera mode
float dpi = this->getDPI();
// Small axis cross in the lower left corner // Small axis cross in the lower left corner
glDepthFunc(GL_ALWAYS); glDepthFunc(GL_ALWAYS);
@ -374,14 +375,14 @@ void GLView::showSmallaxes()
glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0); glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0); glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
glLineWidth(1); glLineWidth(dpi);
glBegin(GL_LINES); glBegin(GL_LINES);
glColor3d(1.0, 0.0, 0.0); glColor3d(1.0, 0.0, 0.0);
glVertex3d(0, 0, 0); glVertex3d(10, 0, 0); glVertex3d(0, 0, 0); glVertex3d(10*dpi, 0, 0);
glColor3d(0.0, 1.0, 0.0); glColor3d(0.0, 1.0, 0.0);
glVertex3d(0, 0, 0); glVertex3d(0, 10, 0); glVertex3d(0, 0, 0); glVertex3d(0, 10*dpi, 0);
glColor3d(0.0, 0.0, 1.0); glColor3d(0.0, 0.0, 1.0);
glVertex3d(0, 0, 0); glVertex3d(0, 0, 10); glVertex3d(0, 0, 0); glVertex3d(0, 0, 10*dpi);
glEnd(); glEnd();
GLdouble mat_model[16]; GLdouble mat_model[16];
@ -394,15 +395,15 @@ void GLView::showSmallaxes()
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
GLdouble xlabel_x, xlabel_y, xlabel_z; GLdouble xlabel_x, xlabel_y, xlabel_z;
gluProject(12, 0, 0, mat_model, mat_proj, viewport, &xlabel_x, &xlabel_y, &xlabel_z); gluProject(12*dpi, 0, 0, mat_model, mat_proj, viewport, &xlabel_x, &xlabel_y, &xlabel_z);
xlabel_x = round(xlabel_x); xlabel_y = round(xlabel_y); xlabel_x = round(xlabel_x); xlabel_y = round(xlabel_y);
GLdouble ylabel_x, ylabel_y, ylabel_z; GLdouble ylabel_x, ylabel_y, ylabel_z;
gluProject(0, 12, 0, mat_model, mat_proj, viewport, &ylabel_x, &ylabel_y, &ylabel_z); gluProject(0, 12*dpi, 0, mat_model, mat_proj, viewport, &ylabel_x, &ylabel_y, &ylabel_z);
ylabel_x = round(ylabel_x); ylabel_y = round(ylabel_y); ylabel_x = round(ylabel_x); ylabel_y = round(ylabel_y);
GLdouble zlabel_x, zlabel_y, zlabel_z; GLdouble zlabel_x, zlabel_y, zlabel_z;
gluProject(0, 0, 12, mat_model, mat_proj, viewport, &zlabel_x, &zlabel_y, &zlabel_z); gluProject(0, 0, 12*dpi, mat_model, mat_proj, viewport, &zlabel_x, &zlabel_y, &zlabel_z);
zlabel_x = round(zlabel_x); zlabel_y = round(zlabel_y); zlabel_x = round(zlabel_x); zlabel_y = round(zlabel_y);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
@ -419,18 +420,19 @@ void GLView::showSmallaxes()
// r=g=b=0; // r=g=b=0;
// bgcol.getRgb(&r, &g, &b); // bgcol.getRgb(&r, &g, &b);
// glColor3f((255.0f-r)/255.0f, (255.0f-g)/255.0f, (255.0f-b)/255.0f); // glColor3f((255.0f-r)/255.0f, (255.0f-g)/255.0f, (255.0f-b)/255.0f);
float d = 3*dpi;
glColor3f(0.0f, 0.0f, 0.0f); glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINES); glBegin(GL_LINES);
// X Label // X Label
glVertex3d(xlabel_x-3, xlabel_y-3, 0); glVertex3d(xlabel_x+3, xlabel_y+3, 0); glVertex3d(xlabel_x-d, xlabel_y-d, 0); glVertex3d(xlabel_x+d, xlabel_y+d, 0);
glVertex3d(xlabel_x-3, xlabel_y+3, 0); glVertex3d(xlabel_x+3, xlabel_y-3, 0); glVertex3d(xlabel_x-d, xlabel_y+d, 0); glVertex3d(xlabel_x+d, xlabel_y-d, 0);
// Y Label // Y Label
glVertex3d(ylabel_x-3, ylabel_y-3, 0); glVertex3d(ylabel_x+3, ylabel_y+3, 0); glVertex3d(ylabel_x-d, ylabel_y-d, 0); glVertex3d(ylabel_x+d, ylabel_y+d, 0);
glVertex3d(ylabel_x-3, ylabel_y+3, 0); glVertex3d(ylabel_x, ylabel_y, 0); glVertex3d(ylabel_x-d, ylabel_y+d, 0); glVertex3d(ylabel_x, ylabel_y, 0);
// Z Label // Z Label
glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y-3, 0); glVertex3d(zlabel_x-d, zlabel_y-d, 0); glVertex3d(zlabel_x+d, zlabel_y-d, 0);
glVertex3d(zlabel_x-3, zlabel_y+3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); glVertex3d(zlabel_x-d, zlabel_y+d, 0); glVertex3d(zlabel_x+d, zlabel_y+d, 0);
glVertex3d(zlabel_x-3, zlabel_y-3, 0); glVertex3d(zlabel_x+3, zlabel_y+3, 0); glVertex3d(zlabel_x-d, zlabel_y-d, 0); glVertex3d(zlabel_x+d, zlabel_y+d, 0);
// FIXME - depends on gimbal camera 'viewer distance'.. how to fix this // FIXME - depends on gimbal camera 'viewer distance'.. how to fix this
// for VectorCamera? // for VectorCamera?
glEnd(); glEnd();
@ -445,10 +447,11 @@ void GLView::showAxes()
// FIXME: doesn't work under Vector Camera // FIXME: doesn't work under Vector Camera
// Large gray axis cross inline with the model // Large gray axis cross inline with the model
// FIXME: This is always gray - adjust color to keep contrast with background // FIXME: This is always gray - adjust color to keep contrast with background
glLineWidth(1); float dpi = this->getDPI();
glLineWidth(1*dpi);
glColor3d(0.5, 0.5, 0.5); glColor3d(0.5, 0.5, 0.5);
glBegin(GL_LINES); glBegin(GL_LINES);
double l = cam.viewer_distance/10; double l = cam.viewer_distance*dpi/10;
glVertex3d(-l, 0, 0); glVertex3d(-l, 0, 0);
glVertex3d(+l, 0, 0); glVertex3d(+l, 0, 0);
glVertex3d(0, -l, 0); glVertex3d(0, -l, 0);

View File

@ -55,6 +55,7 @@ public:
virtual bool save(const char *filename) = 0; virtual bool save(const char *filename) = 0;
virtual std::string getRendererInfo() const = 0; virtual std::string getRendererInfo() const = 0;
virtual float getDPI() { return 1.0f; }
Renderer *renderer; Renderer *renderer;
Camera cam; Camera cam;

View File

@ -2,6 +2,9 @@
#include "printutils.h" #include "printutils.h"
#include "Geometry.h" #include "Geometry.h"
#ifdef DEBUG #ifdef DEBUG
#ifndef ENABLE_CGAL
#define ENABLE_CGAL
#endif
#include "CGAL_Nef_polyhedron.h" #include "CGAL_Nef_polyhedron.h"
#endif #endif

View File

@ -6,6 +6,7 @@
#include "Polygon2d.h" #include "Polygon2d.h"
#include "module.h" #include "module.h"
#include "state.h" #include "state.h"
#include "offsetnode.h"
#include "transformnode.h" #include "transformnode.h"
#include "linearextrudenode.h" #include "linearextrudenode.h"
#include "rotateextrudenode.h" #include "rotateextrudenode.h"
@ -92,37 +93,24 @@ GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren(const Abstrac
*/ */
GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op) GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op)
{ {
if (op == OPENSCAD_HULL) {
return ResultObject(applyHull3D(node));
}
Geometry::ChildList children = collectChildren3D(node); Geometry::ChildList children = collectChildren3D(node);
if (children.size() == 0) return ResultObject(); if (children.size() == 0) return ResultObject();
if (op == OPENSCAD_HULL) {
CGAL_Polyhedron P;
if (CGALUtils::applyHull(children, P)) {
return ResultObject(new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(P)));
}
else {
return ResultObject();
}
}
// Only one child -> this is a noop // Only one child -> this is a noop
if (children.size() == 1) return ResultObject(children.front().second); if (children.size() == 1) return ResultObject(children.front().second);
CGAL_Nef_polyhedron *N = NULL; CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron;
BOOST_FOREACH(const Geometry::ChildItem &item, children) { CGALUtils::applyOperator(children, *N, op);
const shared_ptr<const Geometry> &chgeom = item.second;
shared_ptr<const CGAL_Nef_polyhedron> chN;
if (!chgeom) {
chN.reset(new CGAL_Nef_polyhedron); // Create null polyhedron
}
else {
chN = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(chgeom);
if (!chN) {
const PolySet *chps = dynamic_cast<const PolySet*>(chgeom.get());
if (chps) chN.reset(createNefPolyhedronFromGeometry(*chps));
}
}
if (N) CGALUtils::applyBinaryOperator(*N, *chN, op);
// Initialize N on first iteration with first expected geometric object
else if (chN) N = chN->copy();
item.first->progress_report();
}
return ResultObject(N); return ResultObject(N);
} }
@ -415,6 +403,33 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
return ContinueTraversal; return ContinueTraversal;
} }
Response GeometryEvaluator::visit(State &state, const OffsetNode &node)
{
if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const Geometry> geom;
if (!isSmartCached(node)) {
const Geometry *geometry = applyToChildren2D(node, OPENSCAD_UNION);
if (geometry) {
const Polygon2d *polygon = dynamic_cast<const Polygon2d*>(geometry);
// ClipperLib documentation: The formula for the number of steps in a full
// circular arc is ... Pi / acos(1 - arc_tolerance / abs(delta))
double n = Calc::get_fragments_from_r(10, node.fn, node.fs, node.fa);
double arc_tolerance = abs(node.delta) * (1 - cos(M_PI / n));
const Polygon2d *result = ClipperUtils::applyOffset(*polygon, node.delta, node.join_type, node.miter_limit, arc_tolerance);
assert(result);
geom.reset(result);
delete geometry;
}
}
else {
geom = smartCacheGet(node);
}
addToParent(state, node, geom);
}
return ContinueTraversal;
}
/*! /*!
RenderNodes just pass on convexity RenderNodes just pass on convexity
*/ */

View File

@ -31,6 +31,7 @@ public:
virtual Response visit(State &state, const ProjectionNode &node); virtual Response visit(State &state, const ProjectionNode &node);
virtual Response visit(State &state, const RenderNode &node); virtual Response visit(State &state, const RenderNode &node);
virtual Response visit(State &state, const TextNode &node); virtual Response visit(State &state, const TextNode &node);
virtual Response visit(State &state, const OffsetNode &node);
const Tree &getTree() const { return this->tree; } const Tree &getTree() const { return this->tree; }

View File

@ -10,6 +10,14 @@
#include "memory.h" #include "memory.h"
#include <vector> #include <vector>
#include <QMutex> #include <QMutex>
#include <QSet>
enum export_type_e {
EXPORT_TYPE_UNKNOWN,
EXPORT_TYPE_STL,
EXPORT_TYPE_AMF,
EXPORT_TYPE_OFF
};
class MainWindow : public QMainWindow, public Ui::MainWindow class MainWindow : public QMainWindow, public Ui::MainWindow
{ {
@ -19,7 +27,6 @@ public:
static void requestOpenFile(const QString &filename); static void requestOpenFile(const QString &filename);
QString fileName; QString fileName;
class Highlighter *highlighter;
class Preferences *prefs; class Preferences *prefs;
@ -70,7 +77,6 @@ private slots:
void updateTVal(); void updateTVal();
void setFileName(const QString &filename); void setFileName(const QString &filename);
void setFont(const QString &family, uint size); void setFont(const QString &family, uint size);
void setSyntaxHighlight(const QString &s);
void showProgress(); void showProgress();
void openCSGSettingsChanged(); void openCSGSettingsChanged();
@ -88,6 +94,9 @@ private:
static void consoleOutput(const std::string &msg, void *userdata); static void consoleOutput(const std::string &msg, void *userdata);
void loadViewSettings(); void loadViewSettings();
void loadDesignSettings(); void loadDesignSettings();
void saveBackup();
void writeBackup(class QFile *file);
QString get2dExportFilename(QString format, QString extension);
class QMessageBox *openglbox; class QMessageBox *openglbox;
class FontListDialog *font_list_dialog; class FontListDialog *font_list_dialog;
@ -138,18 +147,22 @@ private slots:
void actionRenderDone(shared_ptr<const class Geometry>); void actionRenderDone(shared_ptr<const class Geometry>);
void cgalRender(); void cgalRender();
#endif #endif
void actionCheckValidity();
void actionDisplayAST(); void actionDisplayAST();
void actionDisplayCSGTree(); void actionDisplayCSGTree();
void actionDisplayCSGProducts(); void actionDisplayCSGProducts();
void actionExportSTLorOFF(bool stl_mode); void actionExport(export_type_e, const char *, const char *);
void actionExportSTL(); void actionExportSTL();
void actionExportOFF(); void actionExportOFF();
void actionExportAMF();
void actionExportDXF(); void actionExportDXF();
void actionExportSVG();
void actionExportCSG(); void actionExportCSG();
void actionExportImage(); void actionExportImage();
void actionFlushCaches(); void actionFlushCaches();
public: public:
static QSet<MainWindow*> *windows;
static void setExamplesDir(const QString &dir) { MainWindow::qexamplesdir = dir; } static void setExamplesDir(const QString &dir) { MainWindow::qexamplesdir = dir; }
void viewModeActionsUncheck(); void viewModeActionsUncheck();
void setCurrentOutput(); void setCurrentOutput();
@ -179,7 +192,7 @@ public slots:
void viewCenter(); void viewCenter();
void viewPerspective(); void viewPerspective();
void viewOrthogonal(); void viewOrthogonal();
void viewResetView(); void viewResetView();
void hideConsole(); void hideConsole();
void animateUpdateDocChanged(); void animateUpdateDocChanged();
void animateUpdate(); void animateUpdate();
@ -200,10 +213,14 @@ private:
char const * afterCompileSlot; char const * afterCompileSlot;
bool procevents; bool procevents;
class QTemporaryFile *tempFile;
class ProgressWidget *progresswidget; class ProgressWidget *progresswidget;
class CGALWorker *cgalworker; class CGALWorker *cgalworker;
QMutex consolemutex; QMutex consolemutex;
signals:
void highlightError(int);
void unhighlightLastError();
}; };
class GuiLocker class GuiLocker

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>681</width> <width>846</width>
<height>647</height> <height>647</height>
</rect> </rect>
</property> </property>
@ -15,16 +15,7 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin"> <property name="margin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -32,14 +23,14 @@
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<widget class="QWidget" name="editorlayoutwidget"> <widget class="QWidget" name="editorPane" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2"> <property name="enabled">
<property name="spacing"> <bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<property name="topMargin">
<number>2</number>
</property>
<item> <item>
<widget class="QFrame" name="find_panel"> <widget class="QFrame" name="find_panel">
<property name="enabled"> <property name="enabled">
@ -60,72 +51,8 @@
<property name="lineWidth"> <property name="lineWidth">
<number>0</number> <number>0</number>
</property> </property>
<layout class="QGridLayout" name="horizontalLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin"> <item row="0" column="0">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="1" column="3" colspan="2">
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>Replace</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="replaceAllButton">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="findInputField">
<property name="placeholderText">
<string>Search string</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="replaceInputField">
<property name="placeholderText">
<string>Replacement string</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="prevButton">
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="nextButton">
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="hideFindButton">
<property name="text">
<string>Done</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="findTypeComboBox"> <widget class="QComboBox" name="findTypeComboBox">
<item> <item>
<property name="text"> <property name="text">
@ -139,6 +66,55 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QLineEdit" name="findInputField">
<property name="placeholderText">
<string>Search string</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="prevButton">
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="nextButton">
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="hideFindButton">
<property name="text">
<string>Done</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="replaceInputField">
<property name="placeholderText">
<string>Replacement string</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>Replace</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="replaceAllButton">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -150,6 +126,9 @@
<pointsize>8</pointsize> <pointsize>8</pointsize>
</font> </font>
</property> </property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -163,6 +142,9 @@
</property> </property>
<widget class="QGLView" name="qglview" native="true"/> <widget class="QGLView" name="qglview" native="true"/>
<widget class="QTextEdit" name="console"> <widget class="QTextEdit" name="console">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -183,17 +165,8 @@
<property name="lineWidth"> <property name="lineWidth">
<number>0</number> <number>0</number>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayoutAnimate">
<property name="leftMargin"> <property name="margin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -247,7 +220,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>681</width> <width>846</width>
<height>22</height> <height>22</height>
</rect> </rect>
</property> </property>
@ -265,16 +238,33 @@
<string>Examples</string> <string>Examples</string>
</property> </property>
</widget> </widget>
<widget class="QMenu" name="menuExport">
<property name="title">
<string>Export</string>
</property>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportAMF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportSVG"/>
<addaction name="designActionExportCSG"/>
<addaction name="separator"/>
<addaction name="designActionExportImage"/>
</widget>
<addaction name="fileActionNew"/> <addaction name="fileActionNew"/>
<addaction name="fileActionOpen"/> <addaction name="fileActionOpen"/>
<addaction name="menuOpenRecent"/> <addaction name="menuOpenRecent"/>
<addaction name="menuExamples"/> <addaction name="menuExamples"/>
<addaction name="fileShowLibraryFolder"/> <addaction name="fileActionReload"/>
<addaction name="separator"/>
<addaction name="fileActionClose"/> <addaction name="fileActionClose"/>
<addaction name="separator"/>
<addaction name="fileActionSave"/> <addaction name="fileActionSave"/>
<addaction name="fileActionSaveAs"/> <addaction name="fileActionSaveAs"/>
<addaction name="fileActionReload"/> <addaction name="separator"/>
<addaction name="menuExport"/>
<addaction name="separator"/>
<addaction name="fileShowLibraryFolder"/>
<addaction name="separator"/>
<addaction name="fileActionQuit"/> <addaction name="fileActionQuit"/>
</widget> </widget>
<widget class="QMenu" name="menu_Edit"> <widget class="QMenu" name="menu_Edit">
@ -316,16 +306,11 @@
<addaction name="designActionPreview"/> <addaction name="designActionPreview"/>
<addaction name="designActionRender"/> <addaction name="designActionRender"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="designCheckValidity"/>
<addaction name="designActionDisplayAST"/> <addaction name="designActionDisplayAST"/>
<addaction name="designActionDisplayCSGTree"/> <addaction name="designActionDisplayCSGTree"/>
<addaction name="designActionDisplayCSGProducts"/> <addaction name="designActionDisplayCSGProducts"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportCSG"/>
<addaction name="designActionExportImage"/>
<addaction name="separator"/>
<addaction name="designActionFlushCaches"/> <addaction name="designActionFlushCaches"/>
</widget> </widget>
<widget class="QMenu" name="menu_View"> <widget class="QMenu" name="menu_View">
@ -351,6 +336,9 @@
<addaction name="viewActionDiagonal"/> <addaction name="viewActionDiagonal"/>
<addaction name="viewActionCenter"/> <addaction name="viewActionCenter"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="viewActionZoomIn"/>
<addaction name="viewActionZoomOut"/>
<addaction name="separator"/>
<addaction name="viewActionResetView"/> <addaction name="viewActionResetView"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="viewActionPerspective"/> <addaction name="viewActionPerspective"/>
@ -372,8 +360,8 @@
</widget> </widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menu_Edit"/> <addaction name="menu_Edit"/>
<addaction name="menu_View"/>
<addaction name="menu_Design"/> <addaction name="menu_Design"/>
<addaction name="menu_View"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
@ -558,6 +546,11 @@
<string>F6</string> <string>F6</string>
</property> </property>
</action> </action>
<action name="designCheckValidity">
<property name="text">
<string>Check Validity</string>
</property>
</action>
<action name="designActionDisplayAST"> <action name="designActionDisplayAST">
<property name="text"> <property name="text">
<string>Display &amp;AST...</string> <string>Display &amp;AST...</string>
@ -888,6 +881,32 @@
<string>Font List</string> <string>Font List</string>
</property> </property>
</action> </action>
<action name="designActionExportSVG">
<property name="text">
<string>Export as SVG...</string>
</property>
</action>
<action name="designActionExportAMF">
<property name="text">
<string>Export as AMF...</string>
</property>
</action>
<action name="viewActionZoomIn">
<property name="text">
<string>Zoom In</string>
</property>
<property name="shortcut">
<string>Ctrl+]</string>
</property>
</action>
<action name="viewActionZoomOut">
<property name="text">
<string>Zoom Out</string>
</property>
<property name="shortcut">
<string>Ctrl+[</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -22,51 +22,68 @@
ModuleCache *ModuleCache::inst = NULL; ModuleCache *ModuleCache::inst = NULL;
/*! /*!
Reevaluate the given file and recompile if necessary. Reevaluate the given file and all it's dependencies and recompile anything
Returns NULL on any error (e.g. compile error or file not found) needing reevaluation. Updates the cache if necessary.
The given filename must be absolute.
If the given filename is relative, it means that the module hasn't been Sets the module reference to the new module, or NULL on any error (e.g. compile
previously located. error or file not found).
Returns true if anything was compiled (module or dependencies) and false otherwise.
*/ */
FileModule *ModuleCache::evaluate(const std::string &filename) bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
{ {
FileModule *lib_mod = (this->entries.find(filename) != this->entries.end()) ? FileModule *lib_mod = NULL;
&(*this->entries[filename].module) : NULL; bool found = false;
if (this->entries.find(filename) != this->entries.end()) {
found = true;
lib_mod = this->entries[filename].module;
}
// Don't try to recursively evaluate - if the file changes // Don't try to recursively evaluate - if the file changes
// during evaluation, that would be really bad. // during evaluation, that would be really bad.
if (lib_mod && lib_mod->isHandlingDependencies()) return lib_mod; if (lib_mod && lib_mod->isHandlingDependencies()) return false;
bool shouldCompile = true;
// Create cache ID // Create cache ID
struct stat st; struct stat st;
memset(&st, 0, sizeof(struct stat)); memset(&st, 0, sizeof(struct stat));
bool valid = (stat(filename.c_str(), &st) == 0); bool valid = (stat(filename.c_str(), &st) == 0);
// If file isn't there, just return and let the cache retain the old module // If file isn't there, just return and let the cache retain the old module
if (!valid) return NULL; if (!valid) return false;
// If the file is present, we'll always cache some result
std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
// Lookup in cache cache_entry &entry = this->entries[filename];
if (lib_mod) { // Initialize entry, if new
if (this->entries[filename].cache_id == cache_id) { if (!found) {
entry.module = NULL;
entry.cache_id = cache_id;
}
bool shouldCompile = true;
if (found) {
// Files should only be recompiled if the cache ID changed
if (entry.cache_id == cache_id) {
shouldCompile = false; shouldCompile = false;
if (lib_mod->includesChanged()) { // Recompile if includes changed
if (lib_mod && lib_mod->includesChanged()) {
lib_mod = NULL; lib_mod = NULL;
shouldCompile = true; shouldCompile = true;
} }
} }
} }
else {
shouldCompile = valid; #ifdef DEBUG
} // Causes too much debug output
//if (!shouldCompile) PRINTB("Using cached library: %s (%p)", filename % lib_mod);
#endif
// If cache lookup failed (non-existing or old timestamp), compile module // If cache lookup failed (non-existing or old timestamp), compile module
if (shouldCompile) { if (shouldCompile) {
#ifdef DEBUG #ifdef DEBUG
if (this->entries.find(filename) != this->entries.end()) { if (found) {
PRINTB("Recompiling cached library: %s (%s)", filename % cache_id); PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
} }
else { else {
@ -79,42 +96,33 @@ FileModule *ModuleCache::evaluate(const std::string &filename)
std::ifstream ifs(filename.c_str()); std::ifstream ifs(filename.c_str());
if (!ifs.is_open()) { if (!ifs.is_open()) {
PRINTB("WARNING: Can't open library file '%s'\n", filename); PRINTB("WARNING: Can't open library file '%s'\n", filename);
return NULL; return false;
} }
textbuf << ifs.rdbuf(); textbuf << ifs.rdbuf();
} }
textbuf << "\n" << commandline_commands; textbuf << "\n" << commandline_commands;
print_messages_push(); print_messages_push();
FileModule *oldmodule = NULL; FileModule *oldmodule = lib_mod;
cache_entry e = { NULL, cache_id };
if (this->entries.find(filename) != this->entries.end()) {
oldmodule = this->entries[filename].module;
}
this->entries[filename] = e;
std::string pathname = boosty::stringy(fs::path(filename).parent_path()); std::string pathname = boosty::stringy(fs::path(filename).parent_path());
lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname.c_str(), false)); lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname.c_str(), false));
PRINTB_NOCACHE(" compiled module: %p", lib_mod); PRINTB_NOCACHE(" compiled module: %p", lib_mod);
if (lib_mod) { // We defer deletion so we can ensure that the new module won't
// We defer deletion so we can ensure that the new module won't // have the same address as the old
// have the same address as the old if (oldmodule) delete oldmodule;
delete oldmodule; entry.module = lib_mod;
this->entries[filename].module = lib_mod; entry.cache_id = cache_id;
} else {
this->entries.erase(filename);
}
print_messages_pop(); print_messages_pop();
} }
module = lib_mod;
bool depschanged = lib_mod ? lib_mod->handleDependencies() : false;
if (lib_mod) { return shouldCompile || depschanged;
lib_mod->handleDependencies();
}
return lib_mod;
} }
void ModuleCache::clear() void ModuleCache::clear()
@ -124,7 +132,11 @@ void ModuleCache::clear()
FileModule *ModuleCache::lookup(const std::string &filename) FileModule *ModuleCache::lookup(const std::string &filename)
{ {
return (this->entries.find(filename) != this->entries.end()) ? return isCached(filename) ? this->entries[filename].module : NULL;
&(*this->entries[filename].module) : NULL; }
bool ModuleCache::isCached(const std::string &filename)
{
return this->entries.find(filename) != this->entries.end();
} }

View File

@ -8,8 +8,9 @@ class ModuleCache
{ {
public: public:
static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; } static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
class FileModule *evaluate(const std::string &filename); bool evaluate(const std::string &filename, class FileModule *&module);
class FileModule *lookup(const std::string &filename); class FileModule *lookup(const std::string &filename);
bool isCached(const std::string &filename);
size_t size() { return this->entries.size(); } size_t size() { return this->entries.size(); }
void clear(); void clear();

View File

@ -1,6 +1,11 @@
#include "PlatformUtils.h" #include "PlatformUtils.h"
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
std::string PlatformUtils::pathSeparatorChar()
{
return ":";
}
std::string PlatformUtils::documentsPath() std::string PlatformUtils::documentsPath()
{ {
return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]); return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]);

View File

@ -1,6 +1,11 @@
#include "PlatformUtils.h" #include "PlatformUtils.h"
#include "boosty.h" #include "boosty.h"
std::string PlatformUtils::pathSeparatorChar()
{
return ":";
}
std::string PlatformUtils::documentsPath() std::string PlatformUtils::documentsPath()
{ {
const char *home = getenv("HOME"); const char *home = getenv("HOME");

View File

@ -6,6 +6,11 @@
#endif #endif
#include <shlobj.h> #include <shlobj.h>
std::string PlatformUtils::pathSeparatorChar()
{
return ";";
}
// convert from windows api w_char strings (usually utf16) to utf8 std::string // convert from windows api w_char strings (usually utf16) to utf8 std::string
std::string winapi_wstr_to_utf8( std::wstring wstr ) std::string winapi_wstr_to_utf8( std::wstring wstr )
{ {

View File

@ -1,7 +1,9 @@
#include <glib.h>
#include "PlatformUtils.h" #include "PlatformUtils.h"
#include "boosty.h" #include "boosty.h"
#include <glib.h> extern std::vector<std::string> librarypath;
bool PlatformUtils::createLibraryPath() bool PlatformUtils::createLibraryPath()
{ {
@ -41,6 +43,40 @@ std::string PlatformUtils::libraryPath()
return boosty::stringy( path ); return boosty::stringy( path );
} }
std::string PlatformUtils::backupPath()
{
fs::path path;
try {
std::string pathstr = PlatformUtils::documentsPath();
if (pathstr=="") return "";
path = boosty::canonical(fs::path( pathstr ));
if (path.empty()) return "";
path /= "OpenSCAD";
path /= "backups";
} catch (const fs::filesystem_error& ex) {
PRINTB("ERROR: %s",ex.what());
}
return boosty::stringy( path );
}
bool PlatformUtils::createBackupPath()
{
std::string path = PlatformUtils::backupPath();
bool OK = false;
try {
if (!fs::exists(fs::path(path))) {
OK = fs::create_directories( path );
}
if (!OK) {
PRINTB("ERROR: Cannot create %s", path );
}
} catch (const fs::filesystem_error& ex) {
PRINTB("ERROR: %s",ex.what());
}
return OK;
}
#include "version_check.h" #include "version_check.h"
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x) #define TOSTRING(x) STRINGIFY(x)
@ -108,6 +144,8 @@ std::string PlatformUtils::info()
std::string cgal_2d_kernelEx = ""; std::string cgal_2d_kernelEx = "";
#endif // ENABLE_CGAL #endif // ENABLE_CGAL
const char *env_path = getenv("OPENSCADPATH");
s << "OpenSCAD Version: " << TOSTRING(OPENSCAD_VERSION) s << "OpenSCAD Version: " << TOSTRING(OPENSCAD_VERSION)
<< "\nCompiler, build date: " << compiler_info << ", " << __DATE__ << "\nCompiler, build date: " << compiler_info << ", " << __DATE__
<< "\nBoost version: " << BOOST_LIB_VERSION << "\nBoost version: " << BOOST_LIB_VERSION
@ -117,8 +155,12 @@ std::string PlatformUtils::info()
<< "\nQt version: " << qtVersion << "\nQt version: " << qtVersion
<< "\nMingW build: " << mingwstatus << "\nMingW build: " << mingwstatus
<< "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION << "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION
<< "\nOPENSCADPATH: " << getenv("OPENSCADPATH") << "\n" << "\nOPENSCADPATH: " << (env_path == NULL ? "<not set>" : env_path)
; << "\nOpenSCAD library path:\n";
for (std::vector<std::string>::iterator it = librarypath.begin();it != librarypath.end();it++) {
s << " " << *it << "\n";
}
return s.str(); return s.str();
} }

View File

@ -8,7 +8,18 @@ namespace PlatformUtils {
std::string documentsPath(); std::string documentsPath();
std::string libraryPath(); std::string libraryPath();
bool createLibraryPath(); bool createLibraryPath();
std::string backupPath();
bool createBackupPath();
std::string info(); std::string info();
/**
* Single character separating path specifications in a list
* (e.g. OPENSCADPATH). On Windows that's ';' and on most other
* systems ':'.
*
* @return the path separator
*/
std::string pathSeparatorChar();
} }
#endif #endif

View File

@ -50,11 +50,11 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
// Editor pane // Editor pane
// Setup default font (Try to use a nice monospace font) // Setup default font (Try to use a nice monospace font)
QString fontfamily; QString fontfamily;
#ifdef Q_WS_X11 #ifdef Q_OS_X11
fontfamily = "Mono"; fontfamily = "Mono";
#elif defined (Q_WS_WIN) #elif defined (Q_OS_WIN)
fontfamily = "Console"; fontfamily = "Console";
#elif defined (Q_WS_MAC) #elif defined (Q_OS_MAC)
fontfamily = "Monaco"; fontfamily = "Monaco";
#endif #endif
QFont font; QFont font;
@ -65,6 +65,12 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
this->defaultmap["editor/fontsize"] = 12; this->defaultmap["editor/fontsize"] = 12;
this->defaultmap["editor/syntaxhighlight"] = "For Light Background"; this->defaultmap["editor/syntaxhighlight"] = "For Light Background";
#if defined (Q_OS_MAC)
this->defaultmap["editor/ctrlmousewheelzoom"] = false;
#else
this->defaultmap["editor/ctrlmousewheelzoom"] = true;
#endif
uint savedsize = getValue("editor/fontsize").toUInt(); uint savedsize = getValue("editor/fontsize").toUInt();
QFontDatabase db; QFontDatabase db;
foreach(uint size, db.standardSizes()) { foreach(uint size, db.standardSizes()) {
@ -95,8 +101,16 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
QActionGroup *group = new QActionGroup(this); QActionGroup *group = new QActionGroup(this);
addPrefPage(group, prefsAction3DView, page3DView); addPrefPage(group, prefsAction3DView, page3DView);
addPrefPage(group, prefsActionEditor, pageEditor); addPrefPage(group, prefsActionEditor, pageEditor);
#if defined(OPENSCAD_DEPLOY) && defined(Q_OS_MAC)
addPrefPage(group, prefsActionUpdate, pageUpdate); addPrefPage(group, prefsActionUpdate, pageUpdate);
#else
this->toolBar->removeAction(prefsActionUpdate);
#endif
#ifdef ENABLE_EXPERIMENTAL
addPrefPage(group, prefsActionFeatures, pageFeatures); addPrefPage(group, prefsActionFeatures, pageFeatures);
#else
this->toolBar->removeAction(prefsActionFeatures);
#endif
addPrefPage(group, prefsActionAdvanced, pageAdvanced); addPrefPage(group, prefsActionAdvanced, pageAdvanced);
connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)));
@ -365,9 +379,15 @@ void Preferences::on_forceGoldfeatherBox_toggled(bool state)
emit openCSGSettingsChanged(); emit openCSGSettingsChanged();
} }
void Preferences::on_mouseWheelZoomBox_toggled(bool state)
{
QSettings settings;
settings.setValue("editor/ctrlmousewheelzoom", state);
}
void Preferences::keyPressEvent(QKeyEvent *e) void Preferences::keyPressEvent(QKeyEvent *e)
{ {
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) {
close(); close();
} else } else
@ -427,6 +447,8 @@ void Preferences::updateGUI()
int shidx = this->syntaxHighlight->findText(shighlight); int shidx = this->syntaxHighlight->findText(shighlight);
if (shidx >= 0) this->syntaxHighlight->setCurrentIndex(shidx); if (shidx >= 0) this->syntaxHighlight->setCurrentIndex(shidx);
this->mouseWheelZoomBox->setChecked(getValue("editor/ctrlmousewheelzoom").toBool());
if (AutoUpdater *updater = AutoUpdater::updater()) { if (AutoUpdater *updater = AutoUpdater::updater()) {
this->updateCheckBox->setChecked(updater->automaticallyChecksForUpdates()); this->updateCheckBox->setChecked(updater->automaticallyChecksForUpdates());
this->snapshotCheckBox->setChecked(updater->enableSnapshots()); this->snapshotCheckBox->setChecked(updater->enableSnapshots());

View File

@ -32,6 +32,7 @@ public slots:
void on_polysetCacheSizeEdit_textChanged(const QString &); void on_polysetCacheSizeEdit_textChanged(const QString &);
void on_opencsgLimitEdit_textChanged(const QString &); void on_opencsgLimitEdit_textChanged(const QString &);
void on_forceGoldfeatherBox_toggled(bool); void on_forceGoldfeatherBox_toggled(bool);
void on_mouseWheelZoomBox_toggled(bool);
void on_updateCheckBox_toggled(bool); void on_updateCheckBox_toggled(bool);
void on_snapshotCheckBox_toggled(bool); void on_snapshotCheckBox_toggled(bool);
void on_checkNowButton_clicked(); void on_checkNowButton_clicked();

View File

@ -165,7 +165,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="syntaxHighlightLayout">
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -182,24 +182,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QComboBox" name="syntaxHighlight"> <widget class="QComboBox" name="syntaxHighlight">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item> <item>
<property name="text"> <property name="text">
<string>For Light Background</string> <string>For Light Background</string>
@ -217,6 +207,62 @@
</item> </item>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="mouseWheelZoomLayout">
<property name="topMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Use Ctrl/Cmd-Mouse-wheel to zoom text</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mouseWheelZoomBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -386,7 +432,7 @@
<item row="0" column="0"> <item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_9"> <layout class="QVBoxLayout" name="verticalLayout_9">
<item> <item>
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_10">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight> <weight>75</weight>
@ -418,7 +464,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>803</width> <width>803</width>
<height>311</height> <height>325</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_10"> <layout class="QVBoxLayout" name="verticalLayout_10">
@ -577,6 +623,12 @@
<property name="allowedAreas"> <property name="allowedAreas">
<set>Qt::TopToolBarArea</set> <set>Qt::TopToolBarArea</set>
</property> </property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolButtonStyle"> <property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum> <enum>Qt::ToolButtonTextUnderIcon</enum>
</property> </property>

View File

@ -170,25 +170,6 @@ void QGLView::paintGL()
if (running_under_wine) swapBuffers(); if (running_under_wine) swapBuffers();
} }
void QGLView::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Plus: // On many keyboards, this requires to press Shift-equals
case Qt::Key_Equal: // ...so simplify this a bit.
cam.viewer_distance *= 0.9;
updateGL();
break;
case Qt::Key_Minus:
cam.viewer_distance /= 0.9;
updateGL();
break;
case Qt::Key_C: // 'center'
cam.object_trans << 0, 0, 0;
updateGL();
break;
}
}
void QGLView::wheelEvent(QWheelEvent *event) void QGLView::wheelEvent(QWheelEvent *event)
{ {
cam.viewer_distance *= pow(0.9, event->delta() / 120.0); cam.viewer_distance *= pow(0.9, event->delta() / 120.0);
@ -197,7 +178,6 @@ void QGLView::wheelEvent(QWheelEvent *event)
void QGLView::mousePressEvent(QMouseEvent *event) void QGLView::mousePressEvent(QMouseEvent *event)
{ {
setFocus();
mouse_drag_active = true; mouse_drag_active = true;
last_mouse = event->globalPos(); last_mouse = event->globalPos();
} }
@ -217,7 +197,7 @@ void QGLView::mouseMoveEvent(QMouseEvent *event)
double dy = (this_mouse.y()-last_mouse.y()) * 0.7; double dy = (this_mouse.y()-last_mouse.y()) * 0.7;
if (mouse_drag_active) { if (mouse_drag_active) {
if (event->buttons() & Qt::LeftButton if (event->buttons() & Qt::LeftButton
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
&& !(event->modifiers() & Qt::MetaModifier) && !(event->modifiers() & Qt::MetaModifier)
#endif #endif
) { ) {
@ -298,3 +278,14 @@ bool QGLView::save(const char *filename)
return img.save(filename, "PNG"); return img.save(filename, "PNG");
} }
void QGLView::ZoomIn(void)
{
cam.viewer_distance *= 0.9;
updateGL();
}
void QGLView::ZoomOut(void)
{
cam.viewer_distance /= 0.9;
updateGL();
}

View File

@ -40,8 +40,15 @@ public:
else this->cam.projection = Camera::PERSPECTIVE; else this->cam.projection = Camera::PERSPECTIVE;
} }
std::string getRendererInfo() const; std::string getRendererInfo() const;
#if QT_VERSION >= 0x050001
float getDPI() { return this->devicePixelRatio(); }
#endif
bool save(const char *filename); bool save(const char *filename);
void resetView(); void resetView();
public slots:
void ZoomIn(void);
void ZoomOut(void);
public: public:
QLabel *statusLabel; QLabel *statusLabel;
@ -52,7 +59,6 @@ private:
bool mouse_drag_active; bool mouse_drag_active;
QPoint last_mouse; QPoint last_mouse;
void keyPressEvent(QKeyEvent *event);
void wheelEvent(QWheelEvent *event); void wheelEvent(QWheelEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);

View File

@ -16,11 +16,21 @@ Builtins *Builtins::instance(bool erase)
void Builtins::init(const char *name, class AbstractModule *module) void Builtins::init(const char *name, class AbstractModule *module)
{ {
#ifndef ENABLE_EXPERIMENTAL
if (module->is_experimental()) {
return;
}
#endif
Builtins::instance()->globalscope.modules[name] = module; Builtins::instance()->globalscope.modules[name] = module;
} }
void Builtins::init(const char *name, class AbstractFunction *function) void Builtins::init(const char *name, class AbstractFunction *function)
{ {
#ifndef ENABLE_EXPERIMENTAL
if (function->is_experimental()) {
return;
}
#endif
Builtins::instance()->globalscope.functions[name] = function; Builtins::instance()->globalscope.functions[name] = function;
} }
@ -35,6 +45,7 @@ extern void register_builtin_render();
extern void register_builtin_import(); extern void register_builtin_import();
extern void register_builtin_projection(); extern void register_builtin_projection();
extern void register_builtin_cgaladv(); extern void register_builtin_cgaladv();
extern void register_builtin_offset();
extern void register_builtin_dxf_linear_extrude(); extern void register_builtin_dxf_linear_extrude();
extern void register_builtin_dxf_rotate_extrude(); extern void register_builtin_dxf_rotate_extrude();
extern void register_builtin_text(); extern void register_builtin_text();
@ -61,6 +72,7 @@ void Builtins::initialize()
register_builtin_import(); register_builtin_import();
register_builtin_projection(); register_builtin_projection();
register_builtin_cgaladv(); register_builtin_cgaladv();
register_builtin_offset();
register_builtin_dxf_linear_extrude(); register_builtin_dxf_linear_extrude();
register_builtin_dxf_rotate_extrude(); register_builtin_dxf_rotate_extrude();
register_builtin_text(); register_builtin_text();
@ -82,18 +94,18 @@ std::string Builtins::isDeprecated(const std::string &name)
Builtins::Builtins() Builtins::Builtins()
{ {
this->globalscope.assignments.push_back(Assignment("$fn", new Expression(Value(0.0)))); this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr<Expression>(new Expression(Value(0.0)))));
this->globalscope.assignments.push_back(Assignment("$fs", new Expression(Value(2.0)))); this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr<Expression>(new Expression(Value(2.0)))));
this->globalscope.assignments.push_back(Assignment("$fa", new Expression(Value(12.0)))); this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr<Expression>(new Expression(Value(12.0)))));
this->globalscope.assignments.push_back(Assignment("$t", new Expression(Value(0.0)))); this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr<Expression>(new Expression(Value(0.0)))));
Value::VectorType zero3; Value::VectorType zero3;
zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0));
zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0));
zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0));
Value zero3val(zero3); Value zero3val(zero3);
this->globalscope.assignments.push_back(Assignment("$vpt", new Expression(zero3val))); this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr<Expression>(new Expression(zero3val))));
this->globalscope.assignments.push_back(Assignment("$vpr", new Expression(zero3val))); this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr<Expression>(new Expression(zero3val))));
} }
Builtins::~Builtins() Builtins::~Builtins()

View File

@ -49,16 +49,16 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant
AssignmentList args; AssignmentList args;
if (type == MINKOWSKI) if (type == MINKOWSKI)
args += Assignment("convexity", NULL); args += Assignment("convexity");
if (type == GLIDE) if (type == GLIDE)
args += Assignment("path", NULL), Assignment("convexity", NULL); args += Assignment("path"), Assignment("convexity");
if (type == SUBDIV) if (type == SUBDIV)
args += Assignment("type", NULL), Assignment("level", NULL), Assignment("convexity", NULL); args += Assignment("type"), Assignment("level"), Assignment("convexity");
if (type == RESIZE) if (type == RESIZE)
args += Assignment("newsize", NULL), Assignment("auto", NULL); args += Assignment("newsize"), Assignment("auto");
Context c(ctx); Context c(ctx);
c.setVariables(args, evalctx); c.setVariables(args, evalctx);

View File

@ -20,33 +20,15 @@ namespace CGALUtils {
bool applyHull(const Geometry::ChildList &children, CGAL_Polyhedron &result) bool applyHull(const Geometry::ChildList &children, CGAL_Polyhedron &result)
{ {
// Collect point cloud // Collect point cloud
std::list<CGAL_Polyhedron::Vertex::Point_3> points; std::vector<CGAL_Polyhedron::Vertex::Point_3> points;
CGAL_Polyhedron P; CGAL_Polyhedron P;
BOOST_FOREACH(const Geometry::ChildItem &item, children) { BOOST_FOREACH(const Geometry::ChildItem &item, children) {
const shared_ptr<const Geometry> &chgeom = item.second; const shared_ptr<const Geometry> &chgeom = item.second;
const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(chgeom.get()); const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(chgeom.get());
if (N) { if (N) {
if (!N->p3->is_simple()) { for (CGAL_Nef_polyhedron3::Vertex_const_iterator i = N->p3->vertices_begin(); i != N->p3->vertices_end(); ++i) {
PRINT("Hull() currently requires a valid 2-manifold. Please modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export"); points.push_back(i->point());
}
else {
bool err = true;
std::string errmsg("");
try {
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(N->p3), P );
// N->p3->convert_to_Polyhedron(P);
}
catch (const CGAL::Failure_exception &e) {
err = true;
errmsg = std::string(e.what());
}
if (err) {
PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed.");
if (errmsg!="") PRINTB("ERROR: %s",errmsg);
} else {
std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points),
boost::bind(static_cast<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
}
} }
} }
else { else {
@ -58,16 +40,103 @@ namespace CGALUtils {
} }
} }
} }
if (points.size() > 0) {
// Apply hull if (points.size() <= 3) return false;
if (points.size() > 3) {
CGAL::convex_hull_3(points.begin(), points.end(), result); // Remove all duplicated points (speeds up the convex_hull computation significantly)
return true; std::vector<CGAL_Polyhedron::Vertex::Point_3> unique_points;
Grid3d<int> grid(GRID_FINE);
BOOST_FOREACH(CGAL_Polyhedron::Vertex::Point_3 const& p, points) {
double x = to_double(p.x()), y = to_double(p.y()), z = to_double(p.z());
int& v = grid.align(x,y,z);
if (v == 0) {
unique_points.push_back(CGAL_Polyhedron::Vertex::Point_3(x,y,z));
v = 1;
} }
} }
return false;
// Apply hull
if (points.size() >= 4) {
CGAL::convex_hull_3(unique_points.begin(), unique_points.end(), result);
return true;
} else {
return false;
}
} }
/*!
Applies op to all children and stores the result in dest.
The child list should be guaranteed to contain non-NULL 3D or empty Geometry objects
*/
void applyOperator(const Geometry::ChildList &children, CGAL_Nef_polyhedron &dest, OpenSCADOperator op)
{
// Speeds up n-ary union operations significantly
CGAL::Nef_nary_union_3<CGAL_Nef_polyhedron3> nary_union;
CGAL_Nef_polyhedron *N = NULL;
BOOST_FOREACH(const Geometry::ChildItem &item, children) {
const shared_ptr<const Geometry> &chgeom = item.second;
shared_ptr<const CGAL_Nef_polyhedron> chN =
dynamic_pointer_cast<const CGAL_Nef_polyhedron>(chgeom);
if (!chN) {
const PolySet *chps = dynamic_cast<const PolySet*>(chgeom.get());
if (chps) chN.reset(createNefPolyhedronFromGeometry(*chps));
}
if (op == OPENSCAD_UNION) {
if (!chN->isEmpty()) nary_union.add_polyhedron(*chN->p3);
continue;
}
// Initialize N with first expected geometric object
if (!N) {
N = chN->copy();;
continue;
}
// Intersecting something with nothing results in nothing
if (chN->isEmpty()) {
if (op == OPENSCAD_INTERSECTION) *N = *chN;
continue;
}
// empty op <something> => empty
if (N->isEmpty()) continue;
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
switch (op) {
case OPENSCAD_INTERSECTION:
*N *= *chN;
break;
case OPENSCAD_DIFFERENCE:
*N -= *chN;
break;
case OPENSCAD_MINKOWSKI:
N->minkowski(*chN);
break;
default:
PRINTB("ERROR: Unsupported CGAL operator: %d", op);
}
}
catch (const CGAL::Failure_exception &e) {
// union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad
std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN";
PRINTB("CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what());
// Errors can result in corrupt polyhedrons, so put back the old one
*N = *chN;
}
CGAL::set_error_behaviour(old_behaviour);
}
if (op == OPENSCAD_UNION) {
// FIXME: Catch exceptions
N = new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(nary_union.get_union()));
}
dest = *N;
}
/*! /*!
Modifies target by applying op to target and src: Modifies target by applying op to target and src:
target = target [op] src target = target [op] src
@ -118,6 +187,27 @@ namespace CGALUtils {
CGAL::set_error_behaviour(old_behaviour); CGAL::set_error_behaviour(old_behaviour);
} }
static void add_outline_to_poly(CGAL_Nef_polyhedron2::Explorer &explorer,
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator circ,
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator end,
bool positive,
Polygon2d *poly) {
Outline2d outline;
CGAL_For_all(circ, end) {
if (explorer.is_standard(explorer.target(circ))) {
CGAL_Nef_polyhedron2::Explorer::Point ep = explorer.point(explorer.target(circ));
outline.vertices.push_back(Vector2d(to_double(ep.x()),
to_double(ep.y())));
}
}
if (!outline.vertices.empty()) {
outline.positive = positive;
poly->addOutline(outline);
}
}
static Polygon2d *convertToPolygon2d(const CGAL_Nef_polyhedron2 &p2) static Polygon2d *convertToPolygon2d(const CGAL_Nef_polyhedron2 &p2)
{ {
Polygon2d *poly = new Polygon2d; Polygon2d *poly = new Polygon2d;
@ -126,19 +216,23 @@ namespace CGALUtils {
typedef Explorer::Face_const_iterator fci_t; typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
Explorer E = p2.explorer(); Explorer E = p2.explorer();
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) { for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) {
heafcc_t fcirc(E.halfedge(fit)), fend(fcirc); if (!fit->mark()) continue;
Outline2d outline;
CGAL_For_all(fcirc, fend) { heafcc_t fcirc(E.face_cycle(fit)), fend(fcirc);
if (E.is_standard(E.target(fcirc))) {
Explorer::Point ep = E.point(E.target(fcirc)); add_outline_to_poly(E, fcirc, fend, true, poly);
outline.vertices.push_back(Vector2d(to_double(ep.x()),
to_double(ep.y()))); for (CGAL_Nef_polyhedron2::Explorer::Hole_const_iterator j = E.holes_begin(fit);
} j != E.holes_end(fit); ++j) {
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator hcirc(j), hend(hcirc);
add_outline_to_poly(E, hcirc, hend, false, poly);
} }
if (outline.vertices.size() > 0) poly->addOutline(outline);
} }
poly->setSanitized(true);
return poly; return poly;
} }
@ -326,13 +420,14 @@ much slower in many cases.
#include <CGAL/Delaunay_mesh_face_base_2.h> #include <CGAL/Delaunay_mesh_face_base_2.h>
typedef CGAL_Kernel3 Kernel; typedef CGAL_Kernel3 Kernel;
typedef typename CGAL::Triangulation_vertex_base_2<Kernel> Vb; //typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef typename CGAL::Constrained_triangulation_face_base_2<Kernel> Fb; typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb; typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb;
typedef typename CGAL::Triangulation_data_structure_2<Vb,Fb> TDS; typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_intersections_tag ITAG; typedef CGAL::Exact_intersections_tag ITAG;
typedef typename CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT; typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT;
//typedef typename CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT; //typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT;
typedef CDT::Vertex_handle Vertex_handle; typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Point CDTPoint; typedef CDT::Point CDTPoint;

View File

@ -8,6 +8,7 @@
namespace CGALUtils { namespace CGALUtils {
bool applyHull(const Geometry::ChildList &children, CGAL_Polyhedron &P); bool applyHull(const Geometry::ChildList &children, CGAL_Polyhedron &P);
void applyOperator(const Geometry::ChildList &children, CGAL_Nef_polyhedron &dest, OpenSCADOperator op);
void applyBinaryOperator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op); void applyBinaryOperator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op);
Polygon2d *project(const CGAL_Nef_polyhedron &N, bool cut); Polygon2d *project(const CGAL_Nef_polyhedron &N, bool cut);
CGAL_Iso_cuboid_3 boundingBox(const CGAL_Nef_polyhedron3 &N); CGAL_Iso_cuboid_3 boundingBox(const CGAL_Nef_polyhedron3 &N);

View File

@ -234,4 +234,11 @@ namespace ClipperUtils {
return toPolygon2d(polytree); return toPolygon2d(polytree);
} }
Polygon2d *applyOffset(const Polygon2d& poly, double offset, ClipperLib::JoinType joinType, double miter_limit, double arc_tolerance) {
ClipperLib::ClipperOffset co(miter_limit, arc_tolerance * CLIPPER_SCALE);
co.AddPaths(fromPolygon2d(poly), joinType, ClipperLib::etClosedPolygon);
ClipperLib::PolyTree result;
co.Execute(result, offset * CLIPPER_SCALE);
return toPolygon2d(result);
}
}; };

View File

@ -6,7 +6,7 @@
namespace ClipperUtils { namespace ClipperUtils {
static const unsigned int CLIPPER_SCALE = 100000; static const unsigned int CLIPPER_SCALE = 2 << 17;
ClipperLib::Path fromOutline2d(const Outline2d &poly, bool keep_orientation); ClipperLib::Path fromOutline2d(const Outline2d &poly, bool keep_orientation);
ClipperLib::Paths fromPolygon2d(const Polygon2d &poly); ClipperLib::Paths fromPolygon2d(const Polygon2d &poly);
@ -15,6 +15,7 @@ namespace ClipperUtils {
Polygon2d *toPolygon2d(const ClipperLib::PolyTree &poly); Polygon2d *toPolygon2d(const ClipperLib::PolyTree &poly);
ClipperLib::Paths process(const ClipperLib::Paths &polygons, ClipperLib::Paths process(const ClipperLib::Paths &polygons,
ClipperLib::ClipType, ClipperLib::PolyFillType); ClipperLib::ClipType, ClipperLib::PolyFillType);
Polygon2d *applyOffset(const Polygon2d& poly, double offset, ClipperLib::JoinType joinType, double miter_limit, double arc_tolerance);
Polygon2d *applyMinkowski(const std::vector<const Polygon2d*> &polygons); Polygon2d *applyMinkowski(const std::vector<const Polygon2d*> &polygons);
Polygon2d *apply(const std::vector<const Polygon2d*> &polygons, ClipperLib::ClipType); Polygon2d *apply(const std::vector<const Polygon2d*> &polygons, ClipperLib::ClipType);
Polygon2d *apply(const std::vector<ClipperLib::Paths> &pathsvector, ClipperLib::ClipType); Polygon2d *apply(const std::vector<ClipperLib::Paths> &pathsvector, ClipperLib::ClipType);

View File

@ -56,7 +56,7 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia
AssignmentList args; AssignmentList args;
args += Assignment("c", NULL), Assignment("alpha", NULL); args += Assignment("c"), Assignment("alpha");
Context c(ctx); Context c(ctx);
c.setVariables(args, evalctx); c.setVariables(args, evalctx);

View File

@ -36,8 +36,6 @@
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
#include "boosty.h" #include "boosty.h"
std::vector<const Context*> Context::ctx_stack;
// $children is not a config_variable. config_variables have dynamic scope, // $children is not a config_variable. config_variables have dynamic scope,
// meaning they are passed down the call chain implicitly. // meaning they are passed down the call chain implicitly.
// $children is simply misnamed and shouldn't have included the '$'. // $children is simply misnamed and shouldn't have included the '$'.
@ -46,18 +44,30 @@ static bool is_config_variable(const std::string &name) {
} }
/*! /*!
Initializes this context. Optionally initializes a context for an external library Initializes this context. Optionally initializes a context for an
external library. Note that if parent is null, a new stack will be
created, and all children will share the root parent's stack.
*/ */
Context::Context(const Context *parent) Context::Context(const Context *parent)
: parent(parent) : parent(parent)
{ {
ctx_stack.push_back(this); if (parent) {
if (parent) document_path = parent->document_path; assert(parent->ctx_stack && "Parent context stack was null!");
this->ctx_stack = parent->ctx_stack;
this->document_path = parent->document_path;
}
else {
this->ctx_stack = new Stack;
}
this->ctx_stack->push_back(this);
} }
Context::~Context() Context::~Context()
{ {
ctx_stack.pop_back(); assert(this->ctx_stack && "Context stack was null at destruction!");
this->ctx_stack->pop_back();
if (!parent) delete this->ctx_stack;
} }
/*! /*!
@ -103,9 +113,13 @@ void Context::set_constant(const std::string &name, const Value &value)
Value Context::lookup_variable(const std::string &name, bool silent) const Value Context::lookup_variable(const std::string &name, bool silent) const
{ {
if (!this->ctx_stack) {
PRINT("ERROR: Context had null stack in lookup_variable()!!");
return Value();
}
if (is_config_variable(name)) { if (is_config_variable(name)) {
for (int i = ctx_stack.size()-1; i >= 0; i--) { for (int i = this->ctx_stack->size()-1; i >= 0; i--) {
const ValueMap &confvars = ctx_stack[i]->config_variables; const ValueMap &confvars = ctx_stack->at(i)->config_variables;
if (confvars.find(name) != confvars.end()) if (confvars.find(name) != confvars.end())
return confvars.find(name)->second; return confvars.find(name)->second;
} }
@ -150,33 +164,35 @@ std::string Context::getAbsolutePath(const std::string &filename) const
} }
#ifdef DEBUG #ifdef DEBUG
void Context::dump(const AbstractModule *mod, const ModuleInstantiation *inst) std::string Context::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
{ {
if (inst) std::stringstream s;
PRINTB("ModuleContext %p (%p) for %s inst (%p)", this % this->parent % inst->name() % inst); if (inst)
else s << boost::format("ModuleContext %p (%p) for %s inst (%p)") % this % this->parent % inst->name() % inst;
PRINTB("Context: %p (%p)", this % this->parent); else
PRINTB(" document path: %s", this->document_path); s << boost::format("Context: %p (%p)") % this % this->parent;
s << boost::format(" document path: %s") % this->document_path;
if (mod) { if (mod) {
const Module *m = dynamic_cast<const Module*>(mod); const Module *m = dynamic_cast<const Module*>(mod);
if (m) { if (m) {
PRINT(" module args:"); s << " module args:";
BOOST_FOREACH(const Assignment &arg, m->definition_arguments) { BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
PRINTB(" %s = %s", arg.first % variables[arg.first]); s << boost::format(" %s = %s") % arg.first % variables[arg.first];
} }
} }
} }
typedef std::pair<std::string, Value> ValueMapType; typedef std::pair<std::string, Value> ValueMapType;
PRINT(" vars:"); s << " vars:";
BOOST_FOREACH(const ValueMapType &v, constants) { BOOST_FOREACH(const ValueMapType &v, constants) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
BOOST_FOREACH(const ValueMapType &v, variables) { BOOST_FOREACH(const ValueMapType &v, variables) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
BOOST_FOREACH(const ValueMapType &v, config_variables) { BOOST_FOREACH(const ValueMapType &v, config_variables) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
return s.str();
} }
#endif #endif

View File

@ -10,9 +10,11 @@
class Context class Context
{ {
public: public:
typedef std::vector<const Context*> Stack;
Context(const Context *parent = NULL); Context(const Context *parent = NULL);
virtual ~Context(); virtual ~Context();
const Context *getParent() const { return this->parent; }
virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const; virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const;
virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const; virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const;
@ -29,11 +31,11 @@ public:
std::string getAbsolutePath(const std::string &filename) const; std::string getAbsolutePath(const std::string &filename) const;
public: public:
const Context *parent;
static std::vector<const Context*> ctx_stack;
protected: protected:
const Context *parent;
Stack *ctx_stack;
typedef boost::unordered_map<std::string, Value> ValueMap; typedef boost::unordered_map<std::string, Value> ValueMap;
ValueMap constants; ValueMap constants;
ValueMap variables; ValueMap variables;
@ -41,9 +43,9 @@ protected:
std::string document_path; // FIXME: This is a remnant only needed by dxfdim std::string document_path; // FIXME: This is a remnant only needed by dxfdim
#ifdef DEBUG
public: public:
virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst); #ifdef DEBUG
virtual std::string dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
#endif #endif
}; };

View File

@ -78,7 +78,7 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst
Context c(ctx); Context c(ctx);
if (it_values.type() == Value::RANGE) { if (it_values.type() == Value::RANGE) {
Value::RangeType range = it_values.toRange(); Value::RangeType range = it_values.toRange();
uint32_t steps = range.nbsteps(); boost::uint32_t steps = range.nbsteps();
if (steps >= 10000) { if (steps >= 10000) {
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
} else { } else {
@ -109,8 +109,8 @@ const EvalContext* ControlModule::getLastModuleCtx(const EvalContext *evalctx)
// Find the last custom module invocation, which will contain // Find the last custom module invocation, which will contain
// an eval context with the children of the module invokation // an eval context with the children of the module invokation
const Context *tmpc = evalctx; const Context *tmpc = evalctx;
while (tmpc->parent) { while (tmpc->getParent()) {
const ModuleContext *modulectx = dynamic_cast<const ModuleContext*>(tmpc->parent); const ModuleContext *modulectx = dynamic_cast<const ModuleContext*>(tmpc->getParent());
if (modulectx) { if (modulectx) {
// This will trigger if trying to invoke child from the root of any file // This will trigger if trying to invoke child from the root of any file
// assert(filectx->evalctx); // assert(filectx->evalctx);
@ -119,7 +119,7 @@ const EvalContext* ControlModule::getLastModuleCtx(const EvalContext *evalctx)
} }
return NULL; return NULL;
} }
tmpc = tmpc->parent; tmpc = tmpc->getParent();
} }
return NULL; return NULL;
} }
@ -161,6 +161,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
if (type == CHILD) if (type == CHILD)
{ {
printDeprecation("DEPRECATED: child() will be removed in future releases. Use children() instead.");
int n = 0; int n = 0;
if (evalctx->numArgs() > 0) { if (evalctx->numArgs() > 0) {
double v; double v;
@ -229,7 +230,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
else if (value.type() == Value::RANGE) { else if (value.type() == Value::RANGE) {
AbstractNode* node = new AbstractNode(inst); AbstractNode* node = new AbstractNode(inst);
Value::RangeType range = value.toRange(); Value::RangeType range = value.toRange();
uint32_t steps = range.nbsteps(); boost::uint32_t steps = range.nbsteps();
if (steps >= 10000) { if (steps >= 10000) {
PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps); PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps);
return NULL; return NULL;

View File

@ -2,6 +2,18 @@
#include "csgterm.h" #include "csgterm.h"
#include "printutils.h" #include "printutils.h"
// Helper function to debug normalization bugs
#if 0
static bool validate_tree(const shared_ptr<CSGTerm> &term)
{
if (term->type == CSGTerm::TYPE_PRIMITIVE) return true;
if (!term->left || !term->right) return false;
if (!validate_tree(term->left)) return false;
if (!validate_tree(term->right)) return false;
return true;
}
#endif
/*! /*!
NB! for e.g. empty intersections, this can normalize a tree to nothing and return NULL. NB! for e.g. empty intersections, this can normalize a tree to nothing and return NULL.
*/ */
@ -25,6 +37,7 @@ shared_ptr<CSGTerm> CSGTermNormalizer::normalize(const shared_ptr<CSGTerm> &root
tmproot = newroot; tmproot = newroot;
newroot = collapse_null_terms(tmproot); newroot = collapse_null_terms(tmproot);
} }
newroot = cleanup_term(newroot);
return newroot; return newroot;
} }
} }

View File

@ -176,22 +176,23 @@ DxfData::DxfData(double fn, double fs, double fa,
in_blocks_section = iddata == "BLOCKS"; in_blocks_section = iddata == "BLOCKS";
} }
else if (mode == "LINE") { else if (mode == "LINE") {
ADD_LINE(xverts[0], yverts[0], xverts[1], yverts[1]); ADD_LINE(xverts.at(0), yverts.at(0), xverts.at(1), yverts.at(1));
} }
else if (mode == "LWPOLYLINE") { else if (mode == "LWPOLYLINE") {
assert(xverts.size() == yverts.size()); // assert(xverts.size() == yverts.size());
// polyline flag is stored in 'dimtype' // Get maximum to enforce managed exception if xverts.size() != yverts.size()
int numverts = xverts.size(); int numverts = std::max(xverts.size(), yverts.size());
for (int i=1;i<numverts;i++) { for (int i=1;i<numverts;i++) {
ADD_LINE(xverts[i-1], yverts[i-1], xverts[i%numverts], yverts[i%numverts]); ADD_LINE(xverts.at(i-1), yverts.at(i-1), xverts.at(i%numverts), yverts.at(i%numverts));
} }
// polyline flag is stored in 'dimtype'
if (dimtype & 0x01) { // closed polyline if (dimtype & 0x01) { // closed polyline
ADD_LINE(xverts[numverts-1], yverts[numverts-1], xverts[0], yverts[0]); ADD_LINE(xverts.at(numverts-1), yverts.at(numverts-1), xverts.at(0), yverts.at(0));
} }
} }
else if (mode == "CIRCLE") { else if (mode == "CIRCLE") {
int n = Calc::get_fragments_from_r(radius, fn, fs, fa); int n = Calc::get_fragments_from_r(radius, fn, fs, fa);
Vector2d center(xverts[0], yverts[0]); Vector2d center(xverts.at(0), yverts.at(0));
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
double a1 = (2*M_PI*i)/n; double a1 = (2*M_PI*i)/n;
double a2 = (2*M_PI*(i+1))/n; double a2 = (2*M_PI*(i+1))/n;
@ -200,7 +201,7 @@ DxfData::DxfData(double fn, double fs, double fa,
} }
} }
else if (mode == "ARC") { else if (mode == "ARC") {
Vector2d center(xverts[0], yverts[0]); Vector2d center(xverts.at(0), yverts.at(0));
int n = Calc::get_fragments_from_r(radius, fn, fs, fa); int n = Calc::get_fragments_from_r(radius, fn, fs, fa);
while (arc_start_angle > arc_stop_angle) while (arc_start_angle > arc_stop_angle)
arc_stop_angle += 360.0; arc_stop_angle += 360.0;
@ -218,9 +219,9 @@ DxfData::DxfData(double fn, double fs, double fa,
// Commented code is meant as documentation of vector math // Commented code is meant as documentation of vector math
while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI; while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI;
// Vector2d center(xverts[0], yverts[0]); // Vector2d center(xverts[0], yverts[0]);
Vector2d center(xverts[0], yverts[0]); Vector2d center(xverts.at(0), yverts.at(0));
// Vector2d ce(xverts[1], yverts[1]); // Vector2d ce(xverts[1], yverts[1]);
Vector2d ce(xverts[1], yverts[1]); Vector2d ce(xverts.at(1), yverts.at(1));
// double r_major = ce.length(); // double r_major = ce.length();
double r_major = sqrt(ce[0]*ce[0] + ce[1]*ce[1]); double r_major = sqrt(ce[0]*ce[0] + ce[1]*ce[1]);
// double rot_angle = ce.angle(); // double rot_angle = ce.angle();
@ -271,10 +272,10 @@ DxfData::DxfData(double fn, double fs, double fa,
double ly1 = this->points[blockdata[iddata][i].idx[0]][1] * ellipse_stop_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 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 ly2 = this->points[blockdata[iddata][i].idx[1]][1] * ellipse_stop_angle;
double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts[0]; double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts.at(0);
double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts[0]; double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts.at(0);
double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts[0]; double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts.at(0);
double py2 = (sin(a)*lx2 + cos(a)*ly2) * scale + yverts[0]; double py2 = (sin(a)*lx2 + cos(a)*ly2) * scale + yverts.at(0);
ADD_LINE(px1, py1, px2, py2); ADD_LINE(px1, py1, px2, py2);
} }
} }
@ -386,6 +387,9 @@ DxfData::DxfData(double fn, double fs, double fa,
catch (boost::bad_lexical_cast &blc) { catch (boost::bad_lexical_cast &blc) {
PRINTB("WARNING: Illegal value %s in '%s'", data % filename); PRINTB("WARNING: Illegal value %s in '%s'", data % filename);
} }
catch (const std::out_of_range& oor) {
PRINTB("WARNING: not enough input values for %s in '%s'", data % filename);
}
} }
BOOST_FOREACH(const EntityList::value_type &i, unsupported_entities_list) { BOOST_FOREACH(const EntityList::value_type &i, unsupported_entities_list) {

View File

@ -1,6 +1,15 @@
#include "editor.h" #include "editor.h"
#include "Preferences.h" #include "Preferences.h"
Editor::Editor(QWidget *parent) : QTextEdit(parent)
{
setAcceptRichText(false);
// This needed to avoid QTextEdit accepting filename drops as we want
// to handle these ourselves in MainWindow
setAcceptDrops(false);
this->highlighter = new Highlighter(this->document());
}
void Editor::indentSelection() void Editor::indentSelection()
{ {
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
@ -98,7 +107,9 @@ void Editor::zoomOut()
void Editor::wheelEvent ( QWheelEvent * event ) void Editor::wheelEvent ( QWheelEvent * event )
{ {
if (event->modifiers() == Qt::ControlModifier) { QSettings settings;
bool wheelzoom_enabled = Preferences::inst()->getValue("editor/ctrlmousewheelzoom").toBool();
if ((event->modifiers() == Qt::ControlModifier) && wheelzoom_enabled ) {
if (event->delta() > 0 ) if (event->delta() > 0 )
zoomIn(); zoomIn();
else if (event->delta() < 0 ) else if (event->delta() < 0 )
@ -122,3 +133,27 @@ void Editor::setPlainText(const QString &text)
verticalScrollBar()->setSliderPosition(y); verticalScrollBar()->setSliderPosition(y);
} }
} }
void Editor::highlightError(int error_pos)
{
highlighter->highlightError( error_pos );
QTextCursor cursor = this->textCursor();
cursor.setPosition( error_pos );
this->setTextCursor( cursor );
}
void Editor::unhighlightLastError()
{
highlighter->unhighlightLastError();
}
void Editor::setHighlightScheme(const QString &name)
{
highlighter->assignFormatsToTokens( name );
highlighter->rehighlight(); // slow on large files
}
Editor::~Editor()
{
delete highlighter;
}

View File

@ -3,14 +3,15 @@
#include <QWidget> #include <QWidget>
#include <QWheelEvent> #include <QWheelEvent>
#include <QScrollBar> #include <QScrollBar>
#include <QTextEdit> #include <QTextEdit>
#include "highlighter.h"
class Editor : public QTextEdit class Editor : public QTextEdit
{ {
Q_OBJECT Q_OBJECT
public: public:
Editor(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); } Editor(QWidget *parent);
void setPlainText(const QString &text); ~Editor();
public slots: public slots:
void zoomIn(); void zoomIn();
void zoomOut(); void zoomOut();
@ -21,6 +22,11 @@ public slots:
void unindentSelection(); void unindentSelection();
void commentSelection(); void commentSelection();
void uncommentSelection(); void uncommentSelection();
void setPlainText(const QString &text);
void highlightError(int error_pos);
void unhighlightLastError();
void setHighlightScheme(const QString &name);
private: private:
void wheelEvent ( QWheelEvent * event ); void wheelEvent ( QWheelEvent * event );
Highlighter *highlighter;
}; };

View File

@ -32,33 +32,35 @@ ModuleInstantiation *EvalContext::getChild(size_t i) const
} }
#ifdef DEBUG #ifdef DEBUG
void EvalContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst) std::string EvalContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
{ {
if (inst) std::stringstream s;
PRINTB("EvalContext %p (%p) for %s inst (%p)", this % this->parent % inst->name() % inst); if (inst)
else s << boost::format("EvalContext %p (%p) for %s inst (%p)") % this % this->parent % inst->name() % inst;
PRINTB("Context: %p (%p)", this % this->parent); else
PRINTB(" document path: %s", this->document_path); s << boost::format("Context: %p (%p)") % this % this->parent;
s << boost::format(" document path: %s") % this->document_path;
PRINT(" eval args:"); s << boost::format(" eval args:");
for (size_t i=0;i<this->eval_arguments.size();i++) { for (size_t i=0;i<this->eval_arguments.size();i++) {
PRINTB(" %s = %s", this->eval_arguments[i].first % this->eval_arguments[i].second); s << boost::format(" %s = %s") % this->eval_arguments[i].first % this->eval_arguments[i].second;
} }
if (this->scope && this->scope->children.size() > 0) { if (this->scope && this->scope->children.size() > 0) {
PRINT(" children:"); s << boost::format(" children:");
BOOST_FOREACH(const ModuleInstantiation *ch, this->scope->children) { BOOST_FOREACH(const ModuleInstantiation *ch, this->scope->children) {
PRINTB(" %s", ch->name()); s << boost::format(" %s") % ch->name();
} }
} }
if (mod) { if (mod) {
const Module *m = dynamic_cast<const Module*>(mod); const Module *m = dynamic_cast<const Module*>(mod);
if (m) { if (m) {
PRINT(" module args:"); s << boost::format(" module args:");
BOOST_FOREACH(const Assignment &arg, m->definition_arguments) { BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
PRINTB(" %s = %s", arg.first % variables[arg.first]); s << boost::format(" %s = %s") % arg.first % variables[arg.first];
} }
} }
} }
return s.str();
} }
#endif #endif

View File

@ -25,7 +25,7 @@ public:
ModuleInstantiation *getChild(size_t i) const; ModuleInstantiation *getChild(size_t i) const;
#ifdef DEBUG #ifdef DEBUG
virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst); virtual std::string dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
#endif #endif
private: private:

View File

@ -31,12 +31,22 @@
#include "dxfdata.h" #include "dxfdata.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
#ifdef ENABLE_CGAL #ifdef ENABLE_CGAL
#include "CGAL_Nef_polyhedron.h" #include "CGAL_Nef_polyhedron.h"
#include "cgal.h" #include "cgal.h"
#include "cgalutils.h" #include "cgalutils.h"
struct triangle {
std::string vs1;
std::string vs2;
std::string vs3;
};
void exportFile(const class Geometry *root_geom, std::ostream &output, FileFormat format) void exportFile(const class Geometry *root_geom, std::ostream &output, FileFormat format)
{ {
if (const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(root_geom)) { if (const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(root_geom)) {
@ -48,6 +58,9 @@ void exportFile(const class Geometry *root_geom, std::ostream &output, FileForma
case OPENSCAD_OFF: case OPENSCAD_OFF:
export_off(N, output); export_off(N, output);
break; break;
case OPENSCAD_AMF:
export_amf(N, output);
break;
case OPENSCAD_DXF: case OPENSCAD_DXF:
assert(false && "Export Nef polyhedron as DXF not supported"); assert(false && "Export Nef polyhedron as DXF not supported");
break; break;
@ -64,12 +77,24 @@ void exportFile(const class Geometry *root_geom, std::ostream &output, FileForma
case OPENSCAD_OFF: case OPENSCAD_OFF:
export_off(*ps, output); export_off(*ps, output);
break; break;
case OPENSCAD_AMF:
export_amf(*ps, output);
break;
default: default:
assert(false && "Unsupported file format"); assert(false && "Unsupported file format");
} }
} }
else if (const Polygon2d *poly = dynamic_cast<const Polygon2d *>(root_geom)) { else if (const Polygon2d *poly = dynamic_cast<const Polygon2d *>(root_geom)) {
export_dxf(*poly, output); switch (format) {
case OPENSCAD_SVG:
export_svg(*poly, output);
break;
case OPENSCAD_DXF:
export_dxf(*poly, output);
break;
default:
assert(false && "Unsupported file format");
}
} else { } else {
assert(false && "Not implemented"); assert(false && "Not implemented");
} }
@ -222,7 +247,8 @@ void export_off(const class PolySet &ps, std::ostream &output)
void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output) void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output)
{ {
if (!root_N->p3->is_simple()) { if (!root_N->p3->is_simple()) {
PRINT("Object isn't a valid 2-manifold! Modify your design.\n"); PRINT("Object isn't a valid 2-manifold! Modify your design.");
return;
} }
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try { try {
@ -236,6 +262,133 @@ void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output)
CGAL::set_error_behaviour(old_behaviour); CGAL::set_error_behaviour(old_behaviour);
} }
void export_amf(const class PolySet &ps, std::ostream &output)
{
// FIXME: Implement this without creating a Nef polyhedron
CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(ps);
export_amf(N, output);
delete N;
}
/*!
Saves the current 3D CGAL Nef polyhedron as AMF to the given file.
The file must be open.
*/
void export_amf(const CGAL_Nef_polyhedron *root_N, std::ostream &output)
{
if (!root_N->p3->is_simple()) {
PRINT("Object isn't a valid 2-manifold! Modify your design.");
return;
}
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
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;
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
std::vector<std::string> vertices;
std::vector<triangle> triangles;
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());
std::stringstream stream;
stream << x1 << " " << y1 << " " << z1;
std::string vs1 = stream.str();
stream.str("");
stream << x2 << " " << y2 << " " << z2;
std::string vs2 = stream.str();
stream.str("");
stream << x3 << " " << y3 << " " << z3;
std::string vs3 = stream.str();
if (std::find(vertices.begin(), vertices.end(), vs1) == vertices.end())
vertices.push_back(vs1);
if (std::find(vertices.begin(), vertices.end(), vs2) == vertices.end())
vertices.push_back(vs2);
if (std::find(vertices.begin(), vertices.end(), vs3) == vertices.end())
vertices.push_back(vs3);
if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) {
// The above condition ensures that there are 3 distinct vertices, but
// they may be collinear. If they are, the unit normal is meaningless
// so the default value of "1 0 0" can be used. If the vertices are not
// collinear then the unit normal must be calculated from the
// components.
triangle tri = {vs1, vs2, vs3};
triangles.push_back(tri);
}
} while (hc != hc_end);
}
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
<< "<amf unit=\"millimeter\">\r\n"
<< " <metadata type=\"producer\">OpenSCAD " << QUOTED(OPENSCAD_VERSION)
#ifdef OPENSCAD_COMMIT
<< " (git " << QUOTED(OPENSCAD_COMMIT) << ")"
#endif
<< "</metadata>\r\n"
<< " <object id=\"0\">\r\n"
<< " <mesh>\r\n";
output << " <vertices>\r\n";
for (size_t i = 0; i < vertices.size(); i++) {
std::string s = vertices[i];
output << " <vertex><coordinates>\r\n";
char* chrs = new char[s.length() + 1];
strcpy(chrs, s.c_str());
std::string coords = strtok(chrs, " ");
output << " <x>" << coords << "</x>\r\n";
coords = strtok(NULL, " ");
output << " <y>" << coords << "</y>\r\n";
coords = strtok(NULL, " ");
output << " <z>" << coords << "</z>\r\n";
output << " </coordinates></vertex>\r\n";
}
output << " </vertices>\r\n";
output << " <volume>\r\n";
for (size_t i = 0; i < triangles.size(); i++) {
triangle t = triangles[i];
output << " <triangle>\r\n";
size_t index;
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs1));
output << " <v1>" << index << "</v1>\r\n";
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs2));
output << " <v2>" << index << "</v2>\r\n";
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs3));
output << " <v3>" << index << "</v3>\r\n";
output << " </triangle>\r\n";
}
output << " </volume>\r\n";
output << " </mesh>\r\n"
<< " </object>\r\n"
<< "</amf>\r\n";
} catch (CGAL::Assertion_exception e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
setlocale(LC_NUMERIC, ""); // Set default locale
}
#endif // ENABLE_CGAL #endif // ENABLE_CGAL
/*! /*!
@ -257,7 +410,7 @@ void export_dxf(const Polygon2d &poly, std::ostream &output)
<< "ENTITIES\n"; << "ENTITIES\n";
BOOST_FOREACH(const Outline2d &o, poly.outlines()) { BOOST_FOREACH(const Outline2d &o, poly.outlines()) {
for (int i=0;i<o.vertices.size();i++) { for (unsigned int i=0;i<o.vertices.size();i++) {
const Vector2d &p1 = o.vertices[i]; const Vector2d &p1 = o.vertices[i];
const Vector2d &p2 = o.vertices[(i+1)%o.vertices.size()]; const Vector2d &p2 = o.vertices[(i+1)%o.vertices.size()];
double x1 = p1[0]; double x1 = p1[0];
@ -298,3 +451,44 @@ void export_dxf(const Polygon2d &poly, std::ostream &output)
setlocale(LC_NUMERIC, ""); // Set default locale setlocale(LC_NUMERIC, ""); // Set default locale
} }
void export_svg(const Polygon2d &poly, std::ostream &output)
{
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
BoundingBox bbox = poly.getBoundingBox();
int minx = floor(bbox.min().x());
int miny = floor(-bbox.max().y());
int maxx = ceil(bbox.max().x());
int maxy = ceil(-bbox.min().y());
output
<< "<?xml version=\"1.0\" standalone=\"no\"?>\n"
<< "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
<< "<svg width=\"" << (maxx - minx) << "\" height=\"" << (maxy - miny) << "\" viewBox=\"" << (minx - 1) << " " << (miny - 1) << " " << (maxx - minx + 2) << " " << (maxy - miny + 2) << "\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n"
<< "<title>OpenSCAD Model</title>\n";
output << "<path d=\"\n";
BOOST_FOREACH(const Outline2d &o, poly.outlines()) {
if (o.vertices.empty()) {
continue;
}
const Eigen::Vector2d& p0 = o.vertices[0];
output << "M " << p0.x() << "," << -p0.y();
for (unsigned int idx = 1;idx < o.vertices.size();idx++) {
const Eigen::Vector2d& p = o.vertices[idx];
output << " L " << p.x() << "," << -p.y();
if ((idx % 6) == 5) {
output << "\n";
}
}
output << " z\n";
}
output << "\" stroke=\"black\" fill=\"lightgray\" stroke-width=\"0.5\"/>";
output << "</svg>\n";
setlocale(LC_NUMERIC, ""); // Set default locale
}

View File

@ -10,7 +10,9 @@
enum FileFormat { enum FileFormat {
OPENSCAD_STL, OPENSCAD_STL,
OPENSCAD_OFF, OPENSCAD_OFF,
OPENSCAD_DXF OPENSCAD_AMF,
OPENSCAD_DXF,
OPENSCAD_SVG
}; };
void exportFile(const class Geometry *root_geom, std::ostream &output, FileFormat format); void exportFile(const class Geometry *root_geom, std::ostream &output, FileFormat format);
@ -20,7 +22,10 @@ void export_stl(const class CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_stl(const class PolySet &ps, std::ostream &output); void export_stl(const class PolySet &ps, std::ostream &output);
void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output); void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_off(const class PolySet &ps, std::ostream &output); void export_off(const class PolySet &ps, std::ostream &output);
void export_amf(const class CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_amf(const class PolySet &ps, std::ostream &output);
void export_dxf(const class Polygon2d &poly, std::ostream &output); void export_dxf(const class Polygon2d &poly, std::ostream &output);
void export_svg(const class Polygon2d &poly, std::ostream &output);
void export_png(const CGAL_Nef_polyhedron *root_N, Camera &c, std::ostream &output); void export_png(const CGAL_Nef_polyhedron *root_N, Camera &c, std::ostream &output);
void export_png_with_opencsg(Tree &tree, Camera &c, std::ostream &output); void export_png_with_opencsg(Tree &tree, Camera &c, std::ostream &output);
void export_png_with_throwntogether(Tree &tree, Camera &c, std::ostream &output); void export_png_with_throwntogether(Tree &tree, Camera &c, std::ostream &output);

View File

@ -22,7 +22,7 @@ std::string lookup_file(const std::string &filename,
if (!fs::exists(absfile) && fs::exists(absfile_fallback)) { if (!fs::exists(absfile) && fs::exists(absfile_fallback)) {
resultfile = absfile_fallback.string(); resultfile = absfile_fallback.string();
PRINTB("WARNING: Imported file (%s) found in document root instead of relative to the importing module. This behavior is deprecated", filename); PRINT_DEPRECATION("DEPRECATED: Imported file (%s) found in document root instead of relative to the importing module. This behavior is deprecated", filename);
} }
else { else {
resultfile = absfile.string(); resultfile = absfile.string();

View File

@ -36,6 +36,10 @@
#include "printutils.h" #include "printutils.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
using boost::math::isnan;
using boost::math::isinf;
/* /*
Random numbers Random numbers
@ -79,7 +83,6 @@ std::string AbstractFunction::dump(const std::string &indent, const std::string
Function::~Function() Function::~Function()
{ {
BOOST_FOREACH(const Assignment &arg, this->definition_arguments) delete arg.second;
delete expr; delete expr;
} }
@ -193,9 +196,11 @@ Value builtin_min(const Context *, const EvalContext *evalctx)
{ {
if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) { if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) {
double val = evalctx->getArgValue(0).toDouble(); double val = evalctx->getArgValue(0).toDouble();
for (size_t i = 1; i < evalctx->numArgs(); i++) for (size_t i = 1; i < evalctx->numArgs(); i++) {
if (evalctx->getArgValue(1).type() == Value::NUMBER) Value v = evalctx->getArgValue(i);
val = fmin(val, evalctx->getArgValue(i).toDouble()); if (v.type() == Value::NUMBER)
val = fmin(val, v.toDouble());
}
return Value(val); return Value(val);
} }
return Value(); return Value();
@ -205,9 +210,11 @@ Value builtin_max(const Context *, const EvalContext *evalctx)
{ {
if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) { if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) {
double val = evalctx->getArgValue(0).toDouble(); double val = evalctx->getArgValue(0).toDouble();
for (size_t i = 1; i < evalctx->numArgs(); i++) for (size_t i = 1; i < evalctx->numArgs(); i++) {
if (evalctx->getArgValue(1).type() == Value::NUMBER) Value v = evalctx->getArgValue(i);
val = fmax(val, evalctx->getArgValue(i).toDouble()); if (v.type() == Value::NUMBER)
val = fmax(val, v.toDouble());
}
return Value(val); return Value(val);
} }
return Value(); return Value();
@ -598,6 +605,71 @@ Value builtin_parent_module(const Context *, const EvalContext *evalctx)
return Value(Module::stack_element(s - 1 - n)); return Value(Module::stack_element(s - 1 - n));
} }
Value builtin_norm(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::VECTOR) {
double sum = 0;
Value::VectorType v = evalctx->getArgValue(0).toVector();
for (size_t i = 0; i < v.size(); i++)
if (v[i].type() == Value::NUMBER)
sum += pow(v[i].toDouble(),2);
else {
PRINT(" WARNING: Incorrect arguments to norm()");
return Value();
}
return Value(sqrt(sum));
}
return Value();
}
Value builtin_cross(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() != 2) {
PRINT("WARNING: Invalid number of parameters for cross()");
return Value();
}
Value arg0 = evalctx->getArgValue(0);
Value arg1 = evalctx->getArgValue(1);
if ((arg0.type() != Value::VECTOR) || (arg1.type() != Value::VECTOR)) {
PRINT("WARNING: Invalid type of parameters for cross()");
return Value();
}
Value::VectorType v0 = arg0.toVector();
Value::VectorType v1 = arg1.toVector();
if ((v0.size() != 3) || (v1.size() != 3)) {
PRINT("WARNING: Invalid vector size of parameter for cross()");
return Value();
}
for (unsigned int a = 0;a < 3;a++) {
if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) {
PRINT("WARNING: Invalid value in parameter vector for cross()");
return Value();
}
double d0 = v0[a].toDouble();
double d1 = v1[a].toDouble();
if (boost::math::isnan(d0) || boost::math::isnan(d1)) {
PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()");
return Value();
}
if (boost::math::isinf(d0) || boost::math::isinf(d1)) {
PRINT("WARNING: Invalid value (INF) in parameter vector for cross()");
return Value();
}
}
double x = v0[1].toDouble() * v1[2].toDouble() - v0[2].toDouble() * v1[1].toDouble();
double y = v0[2].toDouble() * v1[0].toDouble() - v0[0].toDouble() * v1[2].toDouble();
double z = v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble();
Value::VectorType result;
result.push_back(Value(x));
result.push_back(Value(y));
result.push_back(Value(z));
return Value(result);
}
void register_builtin_functions() void register_builtin_functions()
{ {
Builtins::init("abs", new BuiltinFunction(&builtin_abs)); Builtins::init("abs", new BuiltinFunction(&builtin_abs));
@ -627,5 +699,7 @@ void register_builtin_functions()
Builtins::init("search", new BuiltinFunction(&builtin_search)); Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version)); Builtins::init("version", new BuiltinFunction(&builtin_version));
Builtins::init("version_num", new BuiltinFunction(&builtin_version_num)); Builtins::init("version_num", new BuiltinFunction(&builtin_version_num));
Builtins::init("norm", new BuiltinFunction(&builtin_norm));
Builtins::init("cross", new BuiltinFunction(&builtin_cross));
Builtins::init("parent_module", new BuiltinFunction(&builtin_parent_module)); Builtins::init("parent_module", new BuiltinFunction(&builtin_parent_module));
} }

View File

@ -17,7 +17,8 @@ public:
AbstractFunction() : feature(NULL) {} AbstractFunction() : feature(NULL) {}
AbstractFunction(const Feature& feature) : feature(&feature) {} AbstractFunction(const Feature& feature) : feature(&feature) {}
virtual ~AbstractFunction(); virtual ~AbstractFunction();
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }; virtual bool is_experimental() const { return feature != NULL; }
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }
virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const; virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const;
virtual std::string dump(const std::string &indent, const std::string &name) const; virtual std::string dump(const std::string &indent, const std::string &name) const;
}; };

View File

@ -118,6 +118,20 @@
into a blank document. into a blank document.
expected result: don't crash esp. on mac expected result: don't crash esp. on mac
12. action: start openscad, open example 001. open edit/prefs/editor/
syntax-highlighter, 'for light background'. make your OS use a light
(white) background for the openscad text editor.
expected result: text is clearly visible, colors have good contrast
13. repeat test 12, but with 'for dark background' and dark background
expected result: text is clearly visible, colors have good contrast
14. repeat test 12, but turn the syntax highlighter off.
expected result: text is clearly visible, single color has good contrast
14. repeat test 13, but turn the syntax highlighter off.
expected result: text is clearly visible, single color has good contrast
*/ */
#include "highlighter.h" #include "highlighter.h"

View File

@ -2,20 +2,24 @@
#define HIGHLIGHTER_H_ #define HIGHLIGHTER_H_
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include <QTextDocument>
#include <QTextFormat> #include <QTextFormat>
#include <QTextEdit>
#include <QHash> #include <QHash>
class Highlighter : public QSyntaxHighlighter class Highlighter : public QSyntaxHighlighter
{ {
Q_OBJECT
public: public:
enum state_e {NORMAL=-1,QUOTE,COMMENT}; enum state_e {NORMAL=-1,QUOTE,COMMENT};
QHash<QString, QTextCharFormat> tokenFormats; QHash<QString, QTextCharFormat> tokenFormats;
QTextCharFormat errorFormat; QTextCharFormat errorFormat;
Highlighter(QTextDocument *parent); Highlighter(QTextDocument *parent);
void highlightBlock(const QString &text); void highlightBlock(const QString &text);
void assignFormatsToTokens(const QString &);
void portable_rehighlightBlock( const QTextBlock &text );
void highlightError(int error_pos); void highlightError(int error_pos);
void unhighlightLastError(); void unhighlightLastError();
void assignFormatsToTokens(const QString &);
private: private:
QTextBlock lastErrorBlock; QTextBlock lastErrorBlock;
int errorPos; int errorPos;
@ -23,7 +27,6 @@ private:
QMap<QString,QStringList> tokentypes; QMap<QString,QStringList> tokentypes;
QMap<QString,QTextCharFormat> typeformats; QMap<QString,QTextCharFormat> typeformats;
int lastDocumentPos(); int lastDocumentPos();
void portable_rehighlightBlock( const QTextBlock &text );
}; };
#endif #endif

View File

@ -67,8 +67,8 @@ public:
AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{ {
AssignmentList args; AssignmentList args;
args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("convexity", NULL), Assignment("origin", NULL), Assignment("scale", NULL); args += Assignment("file"), Assignment("layer"), Assignment("convexity"), Assignment("origin"), Assignment("scale");
args += Assignment("filename",NULL), Assignment("layername", NULL); args += Assignment("filename"), Assignment("layername");
// FIXME: This is broken. Tag as deprecated and fix // FIXME: This is broken. Tag as deprecated and fix
// Map old argnames to new argnames for compatibility // Map old argnames to new argnames for compatibility
@ -95,7 +95,7 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti
if (v.isUndefined()) { if (v.isUndefined()) {
v = c.lookup_variable("filename"); v = c.lookup_variable("filename");
if (!v.isUndefined()) { if (!v.isUndefined()) {
PRINT("DEPRECATED: filename= is deprecated. Please use file="); printDeprecation("DEPRECATED: filename= is deprecated. Please use file=");
} }
} }
std::string filename = lookup_file(v.isUndefined() ? "" : v.toString(), inst->path(), ctx->documentPath()); std::string filename = lookup_file(v.isUndefined() ? "" : v.toString(), inst->path(), ctx->documentPath());
@ -119,7 +119,7 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti
if (layerval.isUndefined()) { if (layerval.isUndefined()) {
layerval = c.lookup_variable("layername"); layerval = c.lookup_variable("layername");
if (!layerval.isUndefined()) { if (!layerval.isUndefined()) {
PRINT("DEPRECATED: layername= is deprecated. Please use layer="); printDeprecation("DEPRECATED: layername= is deprecated. Please use layer=");
} }
} }
node->layername = layerval.isUndefined() ? "" : layerval.toString(); node->layername = layerval.isUndefined() ? "" : layerval.toString();
@ -210,7 +210,7 @@ Geometry *ImportNode::createGeometry() const
bool binary = false; bool binary = false;
std::streampos file_size = f.tellg(); std::streampos file_size = f.tellg();
f.seekg(80); f.seekg(80);
if (!f.eof()) { if (f.good() && !f.eof()) {
uint32_t facenum = 0; uint32_t facenum = 0;
f.read((char *)&facenum, sizeof(uint32_t)); f.read((char *)&facenum, sizeof(uint32_t));
#ifdef BOOST_BIG_ENDIAN #ifdef BOOST_BIG_ENDIAN
@ -224,13 +224,12 @@ Geometry *ImportNode::createGeometry() const
char data[5]; char data[5];
f.read(data, 5); f.read(data, 5);
if (!binary && !f.eof() && !memcmp(data, "solid", 5)) { if (!binary && !f.eof() && f.good() && !memcmp(data, "solid", 5)) {
int i = 0; int i = 0;
double vdata[3][3]; double vdata[3][3];
std::string line; std::string line;
std::getline(f, line); std::getline(f, line);
while (!f.eof()) { while (!f.eof()) {
std::getline(f, line); std::getline(f, line);
boost::trim(line); boost::trim(line);
if (boost::regex_search(line, ex_sfe)) { if (boost::regex_search(line, ex_sfe)) {
@ -261,7 +260,7 @@ Geometry *ImportNode::createGeometry() const
} }
} }
} }
else else if (binary && !f.eof() && f.good())
{ {
f.ignore(80-5+4); f.ignore(80-5+4);
while (1) { while (1) {
@ -288,7 +287,6 @@ Geometry *ImportNode::createGeometry() const
else { else {
file >> poly; file >> poly;
file.close(); file.close();
bool err = createPolySetFromPolyhedron(poly, *p); bool err = createPolySetFromPolyhedron(poly, *p);
} }
#else #else

View File

@ -149,7 +149,12 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
{D}+{E}? | {D}+{E}? |
{D}*\.{D}+{E}? | {D}*\.{D}+{E}? |
{D}+\.{D}*{E}? { parserlval.number = boost::lexical_cast<double>(yytext); return TOK_NUMBER; } {D}+\.{D}*{E}? {
try {
parserlval.number = boost::lexical_cast<double>(yytext);
return TOK_NUMBER;
} catch (boost::bad_lexical_cast) {}
}
"$"?[a-zA-Z0-9_]+ { parserlval.text = strdup(yytext); return TOK_ID; } "$"?[a-zA-Z0-9_]+ { parserlval.text = strdup(yytext); return TOK_ID; }
\" { BEGIN(cond_string); stringcontents.clear(); } \" { BEGIN(cond_string); stringcontents.clear(); }

View File

@ -54,7 +54,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI
LinearExtrudeNode *node = new LinearExtrudeNode(inst); LinearExtrudeNode *node = new LinearExtrudeNode(inst);
AssignmentList args; AssignmentList args;
args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("height", NULL), Assignment("origin", NULL), Assignment("scale", NULL), Assignment("center", NULL), Assignment("twist", NULL), Assignment("slices", NULL); args += Assignment("file"), Assignment("layer"), Assignment("height"), Assignment("origin"), Assignment("scale"), Assignment("center"), Assignment("twist"), Assignment("slices");
Context c(ctx); Context c(ctx);
c.setVariables(args, evalctx); c.setVariables(args, evalctx);
@ -74,7 +74,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI
Value slices = c.lookup_variable("slices", true); Value slices = c.lookup_variable("slices", true);
if (!file.isUndefined() && file.type() == Value::STRING) { if (!file.isUndefined() && file.type() == Value::STRING) {
PRINT("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead."); printDeprecation("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead.");
node->filename = lookup_file(file.toString(), inst->path(), c.documentPath()); node->filename = lookup_file(file.toString(), inst->path(), c.documentPath());
} }

View File

@ -14,7 +14,6 @@ LocalScope::LocalScope()
LocalScope::~LocalScope() LocalScope::~LocalScope()
{ {
BOOST_FOREACH (ModuleInstantiation *v, children) delete v; BOOST_FOREACH (ModuleInstantiation *v, children) delete v;
BOOST_FOREACH (const Assignment &v, assignments) delete v.second;
BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second; BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second;
BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second; BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second;
} }

View File

@ -76,6 +76,7 @@
#include <QSettings> #include <QSettings>
#include <QProgressDialog> #include <QProgressDialog>
#include <QMutexLocker> #include <QMutexLocker>
#include <QTemporaryFile>
#include <fstream> #include <fstream>
@ -103,6 +104,9 @@
#include "boosty.h" #include "boosty.h"
#include "FontCache.h" #include "FontCache.h"
// Keeps track of open window
QSet<MainWindow*> *MainWindow::windows = NULL;
// Global application state // Global application state
unsigned int GuiLocker::gui_locked = 0; unsigned int GuiLocker::gui_locked = 0;
QString MainWindow::qexamplesdir; QString MainWindow::qexamplesdir;
@ -117,7 +121,7 @@ static char helptitle[] =
#endif #endif
"\nhttp://www.openscad.org\n\n"; "\nhttp://www.openscad.org\n\n";
static char copyrighttext[] = static char copyrighttext[] =
"Copyright (C) 2009-2013 The OpenSCAD Developers\n" "Copyright (C) 2009-2014 The OpenSCAD Developers\n"
"\n" "\n"
"This program is free software; you can redistribute it and/or modify " "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 " "it under the terms of the GNU General Public License as published by "
@ -156,10 +160,15 @@ settings_valueList(const QString &key, const QList<int> &defaultList = QList<int
} }
MainWindow::MainWindow(const QString &filename) MainWindow::MainWindow(const QString &filename)
: root_inst("group"), progresswidget(NULL), font_list_dialog(NULL) : root_inst("group"), tempFile(NULL), progresswidget(NULL), font_list_dialog(NULL)
{ {
setupUi(this); setupUi(this);
this->setAttribute(Qt::WA_DeleteOnClose);
if (!MainWindow::windows) MainWindow::windows = new QSet<MainWindow*>;
MainWindow::windows->insert(this);
#ifdef ENABLE_CGAL #ifdef ENABLE_CGAL
this->cgalworker = new CGALWorker(); this->cgalworker = new CGALWorker();
connect(this->cgalworker, SIGNAL(done(shared_ptr<const Geometry>)), connect(this->cgalworker, SIGNAL(done(shared_ptr<const Geometry>)),
@ -188,7 +197,10 @@ MainWindow::MainWindow(const QString &filename)
fps = 0; fps = 0;
fsteps = 1; fsteps = 1;
highlighter = new Highlighter(editor->document()); editActionZoomIn->setShortcuts(QList<QKeySequence>() << editActionZoomIn->shortcuts() << QKeySequence("CTRL+="));
connect(this, SIGNAL(highlightError(int)), editor, SLOT(highlightError(int)));
connect(this, SIGNAL(unhighlightLastError()), editor, SLOT(unhighlightLastError()));
editor->setTabStopWidth(30); editor->setTabStopWidth(30);
editor->setLineWrapping(true); // Not designable editor->setLineWrapping(true); // Not designable
@ -254,7 +266,7 @@ MainWindow::MainWindow(const QString &filename)
this, SLOT(clearRecentFiles())); this, SLOT(clearRecentFiles()));
if (!qexamplesdir.isEmpty()) { if (!qexamplesdir.isEmpty()) {
bool found_example = false; bool found_example = false;
QStringList examples = QDir(qexamplesdir).entryList(QStringList("*.scad"), QStringList examples = QDir(qexamplesdir).entryList(QStringList("*.scad"),
QDir::Files | QDir::Readable, QDir::Name); QDir::Files | QDir::Readable, QDir::Name);
foreach (const QString &ex, examples) { foreach (const QString &ex, examples) {
this->menuExamples->addAction(ex, this, SLOT(actionOpenExample())); this->menuExamples->addAction(ex, this, SLOT(actionOpenExample()));
@ -301,12 +313,15 @@ MainWindow::MainWindow(const QString &filename)
#else #else
this->designActionRender->setVisible(false); this->designActionRender->setVisible(false);
#endif #endif
connect(this->designCheckValidity, SIGNAL(triggered()), this, SLOT(actionCheckValidity()));
connect(this->designActionDisplayAST, SIGNAL(triggered()), this, SLOT(actionDisplayAST())); connect(this->designActionDisplayAST, SIGNAL(triggered()), this, SLOT(actionDisplayAST()));
connect(this->designActionDisplayCSGTree, SIGNAL(triggered()), this, SLOT(actionDisplayCSGTree())); connect(this->designActionDisplayCSGTree, SIGNAL(triggered()), this, SLOT(actionDisplayCSGTree()));
connect(this->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts())); connect(this->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts()));
connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL())); connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL()));
connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF())); connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF()));
connect(this->designActionExportAMF, SIGNAL(triggered()), this, SLOT(actionExportAMF()));
connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF())); connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF()));
connect(this->designActionExportSVG, SIGNAL(triggered()), this, SLOT(actionExportSVG()));
connect(this->designActionExportCSG, SIGNAL(triggered()), this, SLOT(actionExportCSG())); connect(this->designActionExportCSG, SIGNAL(triggered()), this, SLOT(actionExportCSG()));
connect(this->designActionExportImage, SIGNAL(triggered()), this, SLOT(actionExportImage())); connect(this->designActionExportImage, SIGNAL(triggered()), this, SLOT(actionExportImage()));
connect(this->designActionFlushCaches, SIGNAL(triggered()), this, SLOT(actionFlushCaches())); connect(this->designActionFlushCaches, SIGNAL(triggered()), this, SLOT(actionFlushCaches()));
@ -345,6 +360,8 @@ MainWindow::MainWindow(const QString &filename)
connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective())); connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective()));
connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal())); connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal()));
connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole())); connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole()));
connect(this->viewActionZoomIn, SIGNAL(triggered()), qglview, SLOT(ZoomIn()));
connect(this->viewActionZoomOut, SIGNAL(triggered()), qglview, SLOT(ZoomOut()));
// Help menu // Help menu
connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout())); connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout()));
@ -373,12 +390,12 @@ MainWindow::MainWindow(const QString &filename)
connect(this->qglview, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate())); connect(this->qglview, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate()));
connect(Preferences::inst(), SIGNAL(requestRedraw()), this->qglview, SLOT(updateGL())); connect(Preferences::inst(), SIGNAL(requestRedraw()), this->qglview, SLOT(updateGL()));
connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)), connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)),
this, SLOT(setFont(const QString&,uint))); this, SLOT(setFont(const QString&,uint)));
connect(Preferences::inst(), SIGNAL(openCSGSettingsChanged()), connect(Preferences::inst(), SIGNAL(openCSGSettingsChanged()),
this, SLOT(openCSGSettingsChanged())); this, SLOT(openCSGSettingsChanged()));
connect(Preferences::inst(), SIGNAL(syntaxHighlightChanged(const QString&)), connect(Preferences::inst(), SIGNAL(syntaxHighlightChanged(const QString&)),
this, SLOT(setSyntaxHighlight(const QString&))); editor, SLOT(setHighlightScheme(const QString&)));
Preferences::inst()->apply(); Preferences::inst()->apply();
connect(this->findTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFindType(int))); connect(this->findTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFindType(int)));
@ -466,6 +483,7 @@ MainWindow::~MainWindow()
{ {
if (root_module) delete root_module; if (root_module) delete root_module;
if (root_node) delete root_node; if (root_node) delete root_node;
if (root_chain) delete root_chain;
#ifdef ENABLE_CGAL #ifdef ENABLE_CGAL
this->root_geom.reset(); this->root_geom.reset();
delete this->cgalRenderer; delete this->cgalRenderer;
@ -473,6 +491,8 @@ MainWindow::~MainWindow()
#ifdef ENABLE_OPENCSG #ifdef ENABLE_OPENCSG
delete this->opencsgRenderer; delete this->opencsgRenderer;
#endif #endif
delete this->thrownTogetherRenderer;
MainWindow::windows->remove(this);
} }
void MainWindow::showProgress() void MainWindow::showProgress()
@ -484,7 +504,7 @@ void MainWindow::report_func(const class AbstractNode*, void *vp, int mark)
{ {
MainWindow *thisp = static_cast<MainWindow*>(vp); MainWindow *thisp = static_cast<MainWindow*>(vp);
int v = (int)((mark*1000.0) / progress_report_count); int v = (int)((mark*1000.0) / progress_report_count);
int permille = v < 1000 ? v : 999; int permille = v < 1000 ? v : 999;
if (permille > thisp->progresswidget->value()) { if (permille > thisp->progresswidget->value()) {
QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection, QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection,
Q_ARG(int, permille)); Q_ARG(int, permille));
@ -526,6 +546,7 @@ MainWindow::openFile(const QString &new_filename)
#endif #endif
setFileName(actual_filename); setFileName(actual_filename);
editor->setPlainText(""); editor->setPlainText("");
this->last_compiled_doc = "";
fileChangedOnDisk(); // force cached autoReloadId to update fileChangedOnDisk(); // force cached autoReloadId to update
refreshDocument(); refreshDocument();
@ -555,7 +576,7 @@ MainWindow::setFileName(const QString &filename)
} else { } else {
this->fileName = fileinfo.fileName(); this->fileName = fileinfo.fileName();
} }
this->top_ctx.setDocumentPath(fileinfo.dir().absolutePath().toLocal8Bit().constData()); this->top_ctx.setDocumentPath(fileinfo.dir().absolutePath().toLocal8Bit().constData());
QDir::setCurrent(fileinfo.dir().absolutePath()); QDir::setCurrent(fileinfo.dir().absolutePath());
} }
@ -619,7 +640,7 @@ void MainWindow::refreshDocument()
if (!this->fileName.isEmpty()) { if (!this->fileName.isEmpty()) {
QFile file(this->fileName); QFile file(this->fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
PRINTB("Failed to open file %s: %s", PRINTB("Failed to open file %s: %s",
this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData()); this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData());
} }
else { else {
@ -667,6 +688,7 @@ void MainWindow::compile(bool reload, bool forcedone)
if (shouldcompiletoplevel) { if (shouldcompiletoplevel) {
console->clear(); console->clear();
if (editor->isContentModified()) saveBackup();
compileTopLevelDocument(); compileTopLevelDocument();
didcompile = true; didcompile = true;
} }
@ -687,6 +709,16 @@ void MainWindow::compile(bool reload, bool forcedone)
return; return;
} }
} }
if (!reload && didcompile) {
if (!animate_panel->isVisible()) {
emit unhighlightLastError();
if (!this->root_module) {
emit highlightError( parser_error_pos );
}
}
}
compileDone(didcompile | forcedone); compileDone(didcompile | forcedone);
} }
@ -759,15 +791,15 @@ void MainWindow::instantiateRoot()
// Evaluate CSG tree // Evaluate CSG tree
PRINT("Compiling design (CSG Tree generation)..."); PRINT("Compiling design (CSG Tree generation)...");
if (this->procevents) QApplication::processEvents(); if (this->procevents) QApplication::processEvents();
AbstractNode::resetIndexCounter(); AbstractNode::resetIndexCounter();
// split these two lines - gcc 4.7 bug // split these two lines - gcc 4.7 bug
ModuleInstantiation mi = ModuleInstantiation( "group" ); ModuleInstantiation mi = ModuleInstantiation( "group" );
this->root_inst = mi; this->root_inst = mi;
this->absolute_root_node = this->root_module->instantiate(&top_ctx, &this->root_inst, NULL); this->absolute_root_node = this->root_module->instantiate(&top_ctx, &this->root_inst, NULL);
if (this->absolute_root_node) { if (this->absolute_root_node) {
// Do we have an explicit root node (! modifier)? // Do we have an explicit root node (! modifier)?
if (!(this->root_node = find_root_tag(this->absolute_root_node))) { if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
@ -838,7 +870,7 @@ void MainWindow::compileCSG(bool procevents)
if (root_raw_term) { if (root_raw_term) {
PRINT("Compiling design (CSG Products normalization)..."); PRINT("Compiling design (CSG Products normalization)...");
if (procevents) QApplication::processEvents(); if (procevents) QApplication::processEvents();
size_t normalizelimit = 2 * Preferences::inst()->getValue("advanced/openCSGLimit").toUInt(); size_t normalizelimit = 2 * Preferences::inst()->getValue("advanced/openCSGLimit").toUInt();
CSGTermNormalizer normalizer(normalizelimit); CSGTermNormalizer normalizer(normalizelimit);
this->root_norm_term = normalizer.normalize(this->root_raw_term); this->root_norm_term = normalizer.normalize(this->root_raw_term);
@ -851,24 +883,24 @@ void MainWindow::compileCSG(bool procevents)
PRINT("WARNING: CSG normalization resulted in an empty tree"); PRINT("WARNING: CSG normalization resulted in an empty tree");
if (procevents) QApplication::processEvents(); if (procevents) QApplication::processEvents();
} }
if (highlight_terms.size() > 0) if (highlight_terms.size() > 0)
{ {
PRINTB("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); PRINTB("Compiling highlights (%d CSG Trees)...", highlight_terms.size());
if (procevents) QApplication::processEvents(); if (procevents) QApplication::processEvents();
highlights_chain = new CSGChain(); highlights_chain = new CSGChain();
for (unsigned int i = 0; i < highlight_terms.size(); i++) { for (unsigned int i = 0; i < highlight_terms.size(); i++) {
highlight_terms[i] = normalizer.normalize(highlight_terms[i]); highlight_terms[i] = normalizer.normalize(highlight_terms[i]);
highlights_chain->import(highlight_terms[i]); highlights_chain->import(highlight_terms[i]);
} }
} }
if (background_terms.size() > 0) if (background_terms.size() > 0)
{ {
PRINTB("Compiling background (%d CSG Trees)...", background_terms.size()); PRINTB("Compiling background (%d CSG Trees)...", background_terms.size());
if (procevents) QApplication::processEvents(); if (procevents) QApplication::processEvents();
background_chain = new CSGChain(); background_chain = new CSGChain();
for (unsigned int i = 0; i < background_terms.size(); i++) { for (unsigned int i = 0; i < background_terms.size(); i++) {
background_terms[i] = normalizer.normalize(background_terms[i]); background_terms[i] = normalizer.normalize(background_terms[i]);
@ -876,22 +908,22 @@ void MainWindow::compileCSG(bool procevents)
} }
} }
if (this->root_chain && if (this->root_chain &&
(this->root_chain->objects.size() > (this->root_chain->objects.size() >
Preferences::inst()->getValue("advanced/openCSGLimit").toUInt())) { Preferences::inst()->getValue("advanced/openCSGLimit").toUInt())) {
PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->objects.size()); PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->objects.size());
PRINT("WARNING: OpenCSG rendering has been disabled."); PRINT("WARNING: OpenCSG rendering has been disabled.");
} }
else { else {
PRINTB("Normalized CSG tree has %d elements", PRINTB("Normalized CSG tree has %d elements",
(this->root_chain ? this->root_chain->objects.size() : 0)); (this->root_chain ? this->root_chain->objects.size() : 0));
this->opencsgRenderer = new OpenCSGRenderer(this->root_chain, this->opencsgRenderer = new OpenCSGRenderer(this->root_chain,
this->highlights_chain, this->highlights_chain,
this->background_chain, this->background_chain,
this->qglview->shaderinfo); this->qglview->shaderinfo);
} }
this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain, this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain,
this->highlights_chain, this->highlights_chain,
this->background_chain); this->background_chain);
PRINT("CSG generation finished."); PRINT("CSG generation finished.");
int s = t.elapsed() / 1000; int s = t.elapsed() / 1000;
@ -939,7 +971,7 @@ void MainWindow::actionOpen()
if (!new_filename.isEmpty()) { if (!new_filename.isEmpty()) {
if (!maybeSave()) if (!maybeSave())
return; return;
setCurrentOutput(); setCurrentOutput();
openFile(new_filename); openFile(new_filename);
clearCurrentOutput(); clearCurrentOutput();
@ -1015,6 +1047,45 @@ void MainWindow::actionOpenExample()
} }
} }
void MainWindow::writeBackup(QFile *file)
{
// see MainWindow::saveBackup()
file->resize(0);
QTextStream writer(file);
writer.setCodec("UTF-8");
writer << this->editor->toPlainText();
PRINTB("Saved backup file: %s", file->fileName().toLocal8Bit().constData());
}
void MainWindow::saveBackup()
{
std::string path = PlatformUtils::backupPath();
if ((!fs::exists(path)) && (!PlatformUtils::createBackupPath())) {
PRINTB("WARNING: Cannot create backup path: %s", path);
return;
}
QString backupPath = QString::fromStdString(path);
if (!backupPath.endsWith("/")) backupPath.append("/");
QString basename = "unsaved";
if (!this->fileName.isEmpty()) {
QFileInfo fileInfo = QFileInfo(this->fileName);
basename = fileInfo.baseName();
}
if (!this->tempFile) {
this->tempFile = new QTemporaryFile(backupPath.append(basename + "-backup-XXXXXXXX.scad"));
}
if ((!this->tempFile->isOpen()) && (! this->tempFile->open())) {
PRINT("WARNING: Failed to create backup file");
return;
}
return writeBackup(this->tempFile);
}
void MainWindow::actionSave() void MainWindow::actionSave()
{ {
if (this->fileName.isEmpty()) { if (this->fileName.isEmpty()) {
@ -1091,10 +1162,10 @@ void MainWindow::hideEditor()
{ {
QSettings settings; QSettings settings;
if (editActionHide->isChecked()) { if (editActionHide->isChecked()) {
editor->hide(); editorPane->hide();
settings.setValue("view/hideEditor",true); settings.setValue("view/hideEditor",true);
} else { } else {
editor->show(); editorPane->show();
settings.setValue("view/hideEditor",false); settings.setValue("view/hideEditor",false);
} }
} }
@ -1215,13 +1286,13 @@ bool MainWindow::eventFilter(QObject* obj, QEvent *event)
void MainWindow::updateTemporalVariables() void MainWindow::updateTemporalVariables()
{ {
this->top_ctx.set_variable("$t", Value(this->e_tval->text().toDouble())); this->top_ctx.set_variable("$t", Value(this->e_tval->text().toDouble()));
Value::VectorType vpt; Value::VectorType vpt;
vpt.push_back(Value(-qglview->cam.object_trans.x())); vpt.push_back(Value(-qglview->cam.object_trans.x()));
vpt.push_back(Value(-qglview->cam.object_trans.y())); vpt.push_back(Value(-qglview->cam.object_trans.y()));
vpt.push_back(Value(-qglview->cam.object_trans.z())); vpt.push_back(Value(-qglview->cam.object_trans.z()));
this->top_ctx.set_variable("$vpt", Value(vpt)); this->top_ctx.set_variable("$vpt", Value(vpt));
Value::VectorType vpr; Value::VectorType vpr;
vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360)));
vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360)));
@ -1229,6 +1300,10 @@ void MainWindow::updateTemporalVariables()
top_ctx.set_variable("$vpr", Value(vpr)); top_ctx.set_variable("$vpr", Value(vpr));
} }
/*!
Returns true if the current document is a file on disk and that file has new content.
Returns false if a file on disk has disappeared or if we haven't yet saved.
*/
bool MainWindow::fileChangedOnDisk() bool MainWindow::fileChangedOnDisk()
{ {
if (!this->fileName.isEmpty()) { if (!this->fileName.isEmpty()) {
@ -1254,30 +1329,22 @@ bool MainWindow::fileChangedOnDisk()
void MainWindow::compileTopLevelDocument() void MainWindow::compileTopLevelDocument()
{ {
updateTemporalVariables(); updateTemporalVariables();
resetPrintedDeprecations();
this->last_compiled_doc = editor->toPlainText(); this->last_compiled_doc = editor->toPlainText();
std::string fulltext = std::string fulltext =
std::string(this->last_compiled_doc.toLocal8Bit().constData()) + std::string(this->last_compiled_doc.toLocal8Bit().constData()) +
"\n" + commandline_commands; "\n" + commandline_commands;
delete this->root_module; delete this->root_module;
this->root_module = NULL; this->root_module = NULL;
this->root_module = parse(fulltext.c_str(), this->root_module = parse(fulltext.c_str(),
this->fileName.isEmpty() ? this->fileName.isEmpty() ?
"" : "" :
QFileInfo(this->fileName).absolutePath().toLocal8Bit(), QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
false); false);
if (!animate_panel->isVisible()) {
highlighter->unhighlightLastError();
if (!this->root_module) {
QTextCursor cursor = editor->textCursor();
cursor.setPosition(parser_error_pos);
editor->setTextCursor(cursor);
highlighter->highlightError( parser_error_pos );
}
}
} }
void MainWindow::checkAutoReload() void MainWindow::checkAutoReload()
@ -1384,7 +1451,7 @@ void MainWindow::csgRender()
filename.sprintf("frame%05d.png", int(round(s*t))); filename.sprintf("frame%05d.png", int(round(s*t)));
img.save(filename, "PNG"); img.save(filename, "PNG");
} }
compileEnded(); compileEnded();
} }
@ -1407,6 +1474,7 @@ void MainWindow::actionRender()
void MainWindow::cgalRender() void MainWindow::cgalRender()
{ {
if (!this->root_module || !this->root_node) { if (!this->root_module || !this->root_node) {
compileEnded();
return; return;
} }
@ -1443,7 +1511,6 @@ void MainWindow::actionRenderDone(shared_ptr<const Geometry> root_geom)
if (N->getDimension() == 3) { if (N->getDimension() == 3) {
PRINT(" Top level object is a 3D object:"); PRINT(" Top level object is a 3D object:");
PRINTB(" Simple: %6s", (N->p3->is_simple() ? "yes" : "no")); PRINTB(" Simple: %6s", (N->p3->is_simple() ? "yes" : "no"));
PRINTB(" Valid: %6s", (N->p3->is_valid() ? "yes" : "no"));
PRINTB(" Vertices: %6d", N->p3->number_of_vertices()); PRINTB(" Vertices: %6d", N->p3->number_of_vertices());
PRINTB(" Halfedges: %6d", N->p3->number_of_halfedges()); PRINTB(" Halfedges: %6d", N->p3->number_of_halfedges());
PRINTB(" Edges: %6d", N->p3->number_of_edges()); PRINTB(" Edges: %6d", N->p3->number_of_edges());
@ -1528,20 +1595,47 @@ void MainWindow::actionDisplayCSGProducts()
e->setWindowTitle("CSG Products Dump"); e->setWindowTitle("CSG Products Dump");
e->setReadOnly(true); 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") 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::fromLocal8Bit(root_raw_term->dump().c_str()) : "N/A", .arg(root_raw_term ? QString::fromLocal8Bit(root_raw_term->dump().c_str()) : "N/A",
root_norm_term ? QString::fromLocal8Bit(root_norm_term->dump().c_str()) : "N/A", root_norm_term ? QString::fromLocal8Bit(root_norm_term->dump().c_str()) : "N/A",
this->root_chain ? QString::fromLocal8Bit(this->root_chain->dump().c_str()) : "N/A", this->root_chain ? QString::fromLocal8Bit(this->root_chain->dump().c_str()) : "N/A",
highlights_chain ? QString::fromLocal8Bit(highlights_chain->dump().c_str()) : "N/A", highlights_chain ? QString::fromLocal8Bit(highlights_chain->dump().c_str()) : "N/A",
background_chain ? QString::fromLocal8Bit(background_chain->dump().c_str()) : "N/A")); background_chain ? QString::fromLocal8Bit(background_chain->dump().c_str()) : "N/A"));
e->show(); e->show();
e->resize(600, 400); e->resize(600, 400);
clearCurrentOutput(); clearCurrentOutput();
} }
void MainWindow::actionCheckValidity() {
if (GuiLocker::isLocked()) return;
GuiLocker lock;
#ifdef ENABLE_CGAL #ifdef ENABLE_CGAL
void MainWindow::actionExportSTLorOFF(bool stl_mode) setCurrentOutput();
if (!this->root_geom) {
PRINT("Nothing to validate! Try building first (press F6).");
clearCurrentOutput();
return;
}
if (this->root_geom->getDimension() != 3) {
PRINT("Current top level object is not a 3D object.");
clearCurrentOutput();
return;
}
bool valid = false;
if (const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(this->root_geom.get()))
valid = N->p3->is_valid();
PRINTB(" Valid: %6s", (valid ? "yes" : "no"));
clearCurrentOutput();
#endif /* ENABLE_CGAL */
}
#ifdef ENABLE_CGAL
void MainWindow::actionExport(export_type_e export_type, const char *type_name, const char *suffix)
#else #else
void MainWindow::actionExportSTLorOFF(bool) void MainWindow::actionExport(export_type_e, QString, QString)
#endif #endif
{ {
if (GuiLocker::isLocked()) return; if (GuiLocker::isLocked()) return;
@ -1568,27 +1662,38 @@ void MainWindow::actionExportSTLorOFF(bool)
return; return;
} }
QString suffix = stl_mode ? ".stl" : ".off"; QString title = QString("Export %1 File").arg(type_name);
QString stl_filename = QFileDialog::getSaveFileName(this, QString filter = QString("%1 Files (*%2)").arg(type_name, suffix);
stl_mode ? "Export STL File" : "Export OFF File", QString filename = this->fileName.isEmpty() ? QString("Untitled") + suffix : QFileInfo(this->fileName).baseName() + suffix;
this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix, QString export_filename = QFileDialog::getSaveFileName(this, title, filename, filter);
stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)"); if (export_filename.isEmpty()) {
if (stl_filename.isEmpty()) { PRINTB("No filename specified. %s export aborted.", type_name);
PRINTB("No filename specified. %s export aborted.", (stl_mode ? "STL" : "OFF"));
clearCurrentOutput(); clearCurrentOutput();
return; return;
} }
std::ofstream fstream(stl_filename.toUtf8()); std::ofstream fstream(export_filename.toUtf8());
if (!fstream.is_open()) { if (!fstream.is_open()) {
PRINTB("Can't open file \"%s\" for export", stl_filename.toLocal8Bit().constData()); PRINTB("Can't open file \"%s\" for export", export_filename.toLocal8Bit().constData());
} }
else { else {
if (stl_mode) exportFile(this->root_geom.get(), fstream, OPENSCAD_STL); switch (export_type) {
else exportFile(this->root_geom.get(), fstream, OPENSCAD_OFF); case EXPORT_TYPE_STL:
exportFile(this->root_geom.get(), fstream, OPENSCAD_STL);
break;
case EXPORT_TYPE_OFF:
exportFile(this->root_geom.get(), fstream, OPENSCAD_OFF);
break;
case EXPORT_TYPE_AMF:
exportFile(this->root_geom.get(), fstream, OPENSCAD_AMF);
break;
default:
assert(false && "Unknown export type");
break;
}
fstream.close(); fstream.close();
PRINTB("%s export finished.", (stl_mode ? "STL" : "OFF")); PRINTB("%s export finished.", type_name);
} }
clearCurrentOutput(); clearCurrentOutput();
@ -1597,38 +1702,54 @@ void MainWindow::actionExportSTLorOFF(bool)
void MainWindow::actionExportSTL() void MainWindow::actionExportSTL()
{ {
actionExportSTLorOFF(true); actionExport(EXPORT_TYPE_STL, "STL", ".stl");
} }
void MainWindow::actionExportOFF() void MainWindow::actionExportOFF()
{ {
actionExportSTLorOFF(false); actionExport(EXPORT_TYPE_OFF, "OFF", ".off");
} }
void MainWindow::actionExportDXF() void MainWindow::actionExportAMF()
{ {
#ifdef ENABLE_CGAL actionExport(EXPORT_TYPE_AMF, "AMF", ".amf");
}
QString MainWindow::get2dExportFilename(QString format, QString extension) {
setCurrentOutput(); setCurrentOutput();
if (!this->root_geom) { if (!this->root_geom) {
PRINT("Nothing to export! Try building first (press F6)."); PRINT("Nothing to export! Try building first (press F6).");
clearCurrentOutput(); clearCurrentOutput();
return; return QString();
} }
if (this->root_geom->getDimension() != 2) { if (this->root_geom->getDimension() != 2) {
PRINT("Current top level object is not a 2D object."); PRINT("Current top level object is not a 2D object.");
clearCurrentOutput(); clearCurrentOutput();
return; return QString();
} }
QString dxf_filename = QFileDialog::getSaveFileName(this, QString caption = QString("Export %1 File").arg(format);
"Export DXF File", QString suggestion = this->fileName.isEmpty()
this->fileName.isEmpty() ? "Untitled.dxf" : QFileInfo(this->fileName).baseName()+".dxf", ? QString("Untitled%1").arg(extension)
"DXF Files (*.dxf)"); : QFileInfo(this->fileName).baseName() + extension;
if (dxf_filename.isEmpty()) { QString filter = QString("%1 Files (*%2)").arg(format, extension);
QString exportFilename = QFileDialog::getSaveFileName(this, caption, suggestion, filter);
if (exportFilename.isEmpty()) {
PRINT("No filename specified. DXF export aborted."); PRINT("No filename specified. DXF export aborted.");
clearCurrentOutput(); clearCurrentOutput();
return QString();
}
return exportFilename;
}
void MainWindow::actionExportDXF()
{
#ifdef ENABLE_CGAL
QString dxf_filename = get2dExportFilename("DXF", ".dxf");
if (dxf_filename.isEmpty()) {
return; return;
} }
@ -1646,6 +1767,26 @@ void MainWindow::actionExportDXF()
#endif /* ENABLE_CGAL */ #endif /* ENABLE_CGAL */
} }
void MainWindow::actionExportSVG()
{
QString svg_filename = get2dExportFilename("SVG", ".svg");
if (svg_filename.isEmpty()) {
return;
}
std::ofstream fstream(svg_filename.toUtf8());
if (!fstream.is_open()) {
PRINTB("Can't open file \"%s\" for export", svg_filename.toLocal8Bit().constData());
}
else {
exportFile(this->root_geom.get(), fstream, OPENSCAD_SVG);
fstream.close();
PRINT("SVG export finished.");
}
clearCurrentOutput();
}
void MainWindow::actionExportCSG() void MainWindow::actionExportCSG()
{ {
setCurrentOutput(); setCurrentOutput();
@ -1656,7 +1797,7 @@ void MainWindow::actionExportCSG()
return; return;
} }
QString csg_filename = QFileDialog::getSaveFileName(this, "Export CSG File", QString csg_filename = QFileDialog::getSaveFileName(this, "Export CSG File",
this->fileName.isEmpty() ? "Untitled.csg" : QFileInfo(this->fileName).baseName()+".csg", this->fileName.isEmpty() ? "Untitled.csg" : QFileInfo(this->fileName).baseName()+".csg",
"CSG Files (*.csg)"); "CSG Files (*.csg)");
if (csg_filename.isEmpty()) { if (csg_filename.isEmpty()) {
@ -2001,6 +2142,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
settings.setValue("window/position", pos()); settings.setValue("window/position", pos());
settings_setValueList("window/splitter1sizes",splitter1->sizes()); settings_setValueList("window/splitter1sizes",splitter1->sizes());
settings_setValueList("window/splitter2sizes",splitter2->sizes()); settings_setValueList("window/splitter2sizes",splitter2->sizes());
if (this->tempFile) {
delete this->tempFile;
this->tempFile = NULL;
}
event->accept(); event->accept();
} else { } else {
event->ignore(); event->ignore();
@ -2025,12 +2170,6 @@ void MainWindow::setFont(const QString &family, uint size)
editor->setFont(font); editor->setFont(font);
} }
void MainWindow::setSyntaxHighlight(const QString &s)
{
this->highlighter->assignFormatsToTokens( s );
this->highlighter->rehighlight(); // slow on large files
}
void MainWindow::quit() void MainWindow::quit()
{ {
QCloseEvent ev; QCloseEvent ev;
@ -2049,6 +2188,7 @@ void MainWindow::consoleOutput(const std::string &msg, void *userdata)
MainWindow *thisp = static_cast<MainWindow*>(userdata); MainWindow *thisp = static_cast<MainWindow*>(userdata);
QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection, QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection,
Q_ARG(QString, QString::fromLocal8Bit(msg.c_str()))); Q_ARG(QString, QString::fromLocal8Bit(msg.c_str())));
if (thisp->procevents) QApplication::processEvents();
} }
void MainWindow::setCurrentOutput() void MainWindow::setCurrentOutput()

View File

@ -115,7 +115,7 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co
} }
std::string replacement = Builtins::instance()->isDeprecated(name); std::string replacement = Builtins::instance()->isDeprecated(name);
if (!replacement.empty()) { if (!replacement.empty()) {
PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement); PRINT_DEPRECATION("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement);
} }
return m; return m;
} }
@ -139,34 +139,35 @@ AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst,
} }
#ifdef DEBUG #ifdef DEBUG
void ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst) std::string ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
{ {
if (inst) std::stringstream s;
PRINTB("ModuleContext %p (%p) for %s inst (%p) ", this % this->parent % inst->name() % inst); if (inst)
else s << boost::format("ModuleContext %p (%p) for %s inst (%p) ") % this % this->parent % inst->name() % inst;
PRINTB("ModuleContext: %p (%p)", this % this->parent); else
PRINTB(" document path: %s", this->document_path); s << boost::format("ModuleContext: %p (%p)") % this % this->parent;
s << boost::format(" document path: %s") % this->document_path;
if (mod) { if (mod) {
const Module *m = dynamic_cast<const Module*>(mod); const Module *m = dynamic_cast<const Module*>(mod);
if (m) { if (m) {
PRINT(" module args:"); s << " module args:";
BOOST_FOREACH(const Assignment &arg, m->definition_arguments) { BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
PRINTB(" %s = %s", arg.first % variables[arg.first]); s << boost::format(" %s = %s") % arg.first % variables[arg.first];
} }
} }
} }
typedef std::pair<std::string, Value> ValueMapType; typedef std::pair<std::string, Value> ValueMapType;
PRINT(" vars:"); s << " vars:";
BOOST_FOREACH(const ValueMapType &v, constants) { BOOST_FOREACH(const ValueMapType &v, constants) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
BOOST_FOREACH(const ValueMapType &v, variables) { BOOST_FOREACH(const ValueMapType &v, variables) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
BOOST_FOREACH(const ValueMapType &v, config_variables) { BOOST_FOREACH(const ValueMapType &v, config_variables) {
PRINTB(" %s = %s", v.first % v.second); s << boost::format(" %s = %s") % v.first % v.second;
} }
return s.str();
} }
#endif #endif
@ -180,18 +181,18 @@ Value FileContext::evaluate_function(const std::string &name, const EvalContext
{ {
const AbstractFunction *foundf = findLocalFunction(name); const AbstractFunction *foundf = findLocalFunction(name);
if (foundf) return foundf->evaluate(this, evalctx); if (foundf) return foundf->evaluate(this, evalctx);
BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) {
// usedmod is NULL if the library wasn't be compiled (error or file-not-found) // usedmod is NULL if the library wasn't be compiled (error or file-not-found)
FileModule *usedmod = ModuleCache::instance()->lookup(m); FileModule *usedmod = ModuleCache::instance()->lookup(m);
if (usedmod && if (usedmod &&
usedmod->scope.functions.find(name) != usedmod->scope.functions.end()) { usedmod->scope.functions.find(name) != usedmod->scope.functions.end()) {
FileContext ctx(*usedmod, this->parent); FileContext ctx(*usedmod, this->parent);
ctx.initializeModule(*usedmod); ctx.initializeModule(*usedmod);
// FIXME: Set document path // FIXME: Set document path
#if 0 && DEBUG #ifdef DEBUG
PRINTB("New lib Context for %s func:", name); PRINTDB("New lib Context for %s func:", name);
ctx.dump(NULL, NULL); PRINTDB("%s",ctx.dump(NULL, NULL));
#endif #endif
return usedmod->scope.functions[name]->evaluate(&ctx, evalctx); return usedmod->scope.functions[name]->evaluate(&ctx, evalctx);
} }
@ -208,14 +209,14 @@ AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, c
BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) {
FileModule *usedmod = ModuleCache::instance()->lookup(m); FileModule *usedmod = ModuleCache::instance()->lookup(m);
// usedmod is NULL if the library wasn't be compiled (error or file-not-found) // usedmod is NULL if the library wasn't be compiled (error or file-not-found)
if (usedmod && if (usedmod &&
usedmod->scope.modules.find(inst.name()) != usedmod->scope.modules.end()) { usedmod->scope.modules.find(inst.name()) != usedmod->scope.modules.end()) {
FileContext ctx(*usedmod, this->parent); FileContext ctx(*usedmod, this->parent);
ctx.initializeModule(*usedmod); ctx.initializeModule(*usedmod);
// FIXME: Set document path // FIXME: Set document path
#if 0 && DEBUG #ifdef DEBUG
PRINT("New file Context:"); PRINTD("New file Context:");
ctx.dump(NULL, &inst); PRINTDB("%s",ctx.dump(NULL, &inst));
#endif #endif
return usedmod->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx); return usedmod->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx);
} }

View File

@ -34,7 +34,7 @@ public:
const class EvalContext *evalctx; const class EvalContext *evalctx;
#ifdef DEBUG #ifdef DEBUG
virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst); virtual std::string dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
#endif #endif
private: private:
// Experimental code. See issue #399 // Experimental code. See issue #399

View File

@ -78,7 +78,6 @@ std::string AbstractModule::dump(const std::string &indent, const std::string &n
ModuleInstantiation::~ModuleInstantiation() ModuleInstantiation::~ModuleInstantiation()
{ {
BOOST_FOREACH(const Assignment &arg, this->arguments) delete arg.second;
} }
IfElseModuleInstantiation::~IfElseModuleInstantiation() IfElseModuleInstantiation::~IfElseModuleInstantiation()
@ -291,45 +290,58 @@ bool FileModule::handleDependencies()
if (this->is_handling_dependencies) return false; if (this->is_handling_dependencies) return false;
this->is_handling_dependencies = true; this->is_handling_dependencies = true;
bool changed = false; bool somethingchanged = false;
std::vector<std::pair<std::string,std::string> > updates;
// If a lib in usedlibs was previously missing, we need to relocate it // If a lib in usedlibs was previously missing, we need to relocate it
// by searching the applicable paths. We can identify a previously missing module // by searching the applicable paths. We can identify a previously missing module
// as it will have a relative path. // as it will have a relative path.
BOOST_FOREACH(std::string filename, this->usedlibs) {
// Iterating manually since we want to modify the container while iterating
FileModule::ModuleContainer::iterator iter = this->usedlibs.begin();
while (iter != this->usedlibs.end()) {
FileModule::ModuleContainer::iterator curr = iter++;
bool wasmissing = false; bool wasmissing = false;
bool found = true;
// Get an absolute filename for the module // Get an absolute filename for the module
std::string filename = *curr;
if (!boosty::is_absolute(filename)) { if (!boosty::is_absolute(filename)) {
wasmissing = true; wasmissing = true;
fs::path fullpath = find_valid_path(this->path, filename); fs::path fullpath = find_valid_path(this->path, filename);
if (!fullpath.empty()) filename = boosty::stringy(fullpath); if (!fullpath.empty()) {
updates.push_back(std::make_pair(filename, boosty::stringy(fullpath)));
filename = boosty::stringy(fullpath);
}
else {
found = false;
}
} }
FileModule *oldmodule = ModuleCache::instance()->lookup(filename); if (found) {
FileModule *newmodule = ModuleCache::instance()->evaluate(filename); bool wascached = ModuleCache::instance()->isCached(filename);
// Detect appearance but not removal of files FileModule *oldmodule = ModuleCache::instance()->lookup(filename);
if (newmodule && oldmodule != newmodule) { FileModule *newmodule;
changed = true; bool changed = ModuleCache::instance()->evaluate(filename, newmodule);
// Detect appearance but not removal of files, and keep old module
// on compile errors (FIXME: Is this correct behavior?)
if (changed) {
#ifdef DEBUG #ifdef DEBUG
PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule); PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule);
#endif #endif
} }
if (!newmodule) { somethingchanged |= changed;
// Only print warning if we're not part of an automatic reload // Only print warning if we're not part of an automatic reload
if (!oldmodule && !wasmissing) { if (!newmodule && !wascached && !wasmissing) {
PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
} }
} }
} }
// Relative filenames which were located is reinserted as absolute filenames
typedef std::pair<std::string,std::string> stringpair;
BOOST_FOREACH(const stringpair &files, updates) {
this->usedlibs.erase(files.first);
this->usedlibs.insert(files.second);
}
this->is_handling_dependencies = false; this->is_handling_dependencies = false;
return changed; return somethingchanged;
} }
AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const

View File

@ -67,7 +67,8 @@ public:
AbstractModule() : feature(NULL) {} AbstractModule() : feature(NULL) {}
AbstractModule(const Feature& feature) : feature(&feature) {} AbstractModule(const Feature& feature) : feature(&feature) {}
virtual ~AbstractModule(); virtual ~AbstractModule();
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }; virtual bool is_experimental() const { return feature != NULL; }
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }
virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const; virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const;
virtual std::string dump(const std::string &indent, const std::string &name) const; virtual std::string dump(const std::string &indent, const std::string &name) const;
virtual double lookup_double_variable_with_default(Context &c, std::string variable, double def) const; virtual double lookup_double_variable_with_default(Context &c, std::string variable, double def) const;

View File

@ -102,8 +102,6 @@ void AbstractNode::progress_report() const
std::ostream &operator<<(std::ostream &stream, const AbstractNode &node) std::ostream &operator<<(std::ostream &stream, const AbstractNode &node)
{ {
// FIXME: Don't use deep access to modinst members
if (node.modinst->isBackground()) stream << "%";
stream << node.toString(); stream << node.toString();
return stream; return stream;
} }

View File

@ -1,5 +1,6 @@
#include "nodedumper.h" #include "nodedumper.h"
#include "state.h" #include "state.h"
#include "module.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
@ -47,6 +48,7 @@ std::string NodeDumper::dumpChildren(const AbstractNode &node)
iter != this->visitedchildren[node.index()].end(); iter != this->visitedchildren[node.index()].end();
iter++) { iter++) {
assert(isCached(**iter)); assert(isCached(**iter));
if ((*iter)->modinst->isBackground()) dump << "%";
dump << this->cache[**iter] << "\n"; dump << this->cache[**iter] << "\n";
} }

121
src/offset.cc Normal file
View File

@ -0,0 +1,121 @@
/*
* 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 "offsetnode.h"
#include "module.h"
#include "evalcontext.h"
#include "printutils.h"
#include "fileutils.h"
#include "builtin.h"
#include "calc.h"
#include "polyset.h"
#include "mathc99.h"
#include <sstream>
#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class OffsetModule : public AbstractModule
{
public:
OffsetModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
OffsetNode *node = new OffsetNode(inst);
AssignmentList args;
args += Assignment("delta");
Context c(ctx);
c.setVariables(args, evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();
node->fa = c.lookup_variable("$fa").toDouble();
Value delta = c.lookup_variable("delta");
node->delta = 1;
delta.getDouble(node->delta);
Value miter_limit = c.lookup_variable("miter_limit", true);
node->miter_limit = 2;
miter_limit.getDouble(node->miter_limit);
Value join_type = c.lookup_variable("join_type", true);
if (join_type.type() == Value::STRING) {
std::string jt = join_type.toString();
if (std::string("bevel") == jt) {
node->join_type = ClipperLib::jtSquare;
} else if (std::string("round") == jt) {
node->join_type = ClipperLib::jtRound;
} else if (std::string("miter") == jt) {
node->join_type = ClipperLib::jtMiter;
} else {
PRINTB("WARNING: Unknown join_type for offset(): '%s'", jt);
}
if ((node->join_type != ClipperLib::jtMiter) && !miter_limit.isUndefined()) {
PRINTB("WARNING: miter_limit is ignored in offset() for join_type: '%s'", jt);
}
}
std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx);
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
return node;
}
std::string OffsetNode::toString() const
{
std::stringstream stream;
stream << this->name()
<< "(delta = " << std::dec << this->delta
<< ", join_type = "
<< (this->join_type == ClipperLib::jtSquare
? "bevel"
: this->join_type == ClipperLib::jtRound
? "round"
: "miter")
<< ", miter_limit = " << this->miter_limit
<< ", $fn = " << this->fn
<< ", $fa = " << this->fa
<< ", $fs = " << this->fs << ")";
return stream.str();
}
void register_builtin_offset()
{
Builtins::init("offset", new OffsetModule());
}

23
src/offsetnode.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef OFFSETNODE_H_
#define OFFSETNODE_H_
#include "node.h"
#include "visitor.h"
#include "value.h"
#include "clipper-utils.h"
class OffsetNode : public AbstractPolyNode
{
public:
OffsetNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi), fn(0), fs(0), fa(0), delta(1), miter_limit(2.0), join_type(ClipperLib::jtMiter) { }
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 "offset"; }
double fn, fs, fa, delta, miter_limit;
ClipperLib::JoinType join_type;
};
#endif

View File

@ -111,11 +111,14 @@ static void help(const char *progname)
"%2% --camera=eyex,y,z,centerx,y,z ] \\\n" "%2% --camera=eyex,y,z,centerx,y,z ] \\\n"
"%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n"
"%2%[ --render | --preview[=throwntogether] ] \\\n" "%2%[ --render | --preview[=throwntogether] ] \\\n"
"%2%[ --enable=<feature> ]" "%2%[ --csglimit=num ]"
#ifdef DEBUG #ifdef ENABLE_EXPERIMENTAL
" [ --debug=module ]" " [ --enable=<feature> ]"
#endif
"\\\n"
#ifdef DEBUG
"%2%[ --debug=module ] \\\n"
#endif #endif
" \\\n"
"%2%filename\n", "%2%filename\n",
progname % (const char *)tabstr); progname % (const char *)tabstr);
exit(1); exit(1);
@ -200,6 +203,13 @@ Camera get_camera( po::variables_map vm )
return camera; return camera;
} }
#ifdef OPENSCAD_TESTING
#undef OPENSCAD_QTGUI
#else
#define OPENSCAD_QTGUI 1
#include <QApplication>
#endif
int cmdline(const char *deps_output_file, const std::string &filename, Camera &camera, const char *output_file, const fs::path &original_path, Render::type renderer, int argc, char ** argv ) int cmdline(const char *deps_output_file, const std::string &filename, Camera &camera, const char *output_file, const fs::path &original_path, Render::type renderer, int argc, char ** argv )
{ {
#ifdef OPENSCAD_QTGUI #ifdef OPENSCAD_QTGUI
@ -215,7 +225,9 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
#endif #endif
const char *stl_output_file = NULL; const char *stl_output_file = NULL;
const char *off_output_file = NULL; const char *off_output_file = NULL;
const char *amf_output_file = NULL;
const char *dxf_output_file = NULL; const char *dxf_output_file = NULL;
const char *svg_output_file = NULL;
const char *csg_output_file = NULL; const char *csg_output_file = NULL;
const char *png_output_file = NULL; const char *png_output_file = NULL;
const char *ast_output_file = NULL; const char *ast_output_file = NULL;
@ -227,7 +239,9 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
if (suffix == ".stl") stl_output_file = output_file; if (suffix == ".stl") stl_output_file = output_file;
else if (suffix == ".off") off_output_file = output_file; else if (suffix == ".off") off_output_file = output_file;
else if (suffix == ".amf") amf_output_file = output_file;
else if (suffix == ".dxf") dxf_output_file = output_file; else if (suffix == ".dxf") dxf_output_file = output_file;
else if (suffix == ".svg") svg_output_file = output_file;
else if (suffix == ".csg") csg_output_file = output_file; else if (suffix == ".csg") csg_output_file = output_file;
else if (suffix == ".png") png_output_file = output_file; else if (suffix == ".png") png_output_file = output_file;
else if (suffix == ".ast") ast_output_file = output_file; else if (suffix == ".ast") ast_output_file = output_file;
@ -241,8 +255,8 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
// Top context - this context only holds builtins // Top context - this context only holds builtins
ModuleContext top_ctx; ModuleContext top_ctx;
top_ctx.registerBuiltin(); top_ctx.registerBuiltin();
#if 0 && DEBUG #ifdef DEBUG
top_ctx.dump(NULL, NULL); PRINTDB("Top ModuleContext:\n%s",top_ctx.dump(NULL, NULL));
#endif #endif
shared_ptr<Echostream> echostream; shared_ptr<Echostream> echostream;
if (echo_output_file) if (echo_output_file)
@ -314,7 +328,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
std::vector<shared_ptr<CSGTerm> > highlight_terms; std::vector<shared_ptr<CSGTerm> > highlight_terms;
std::vector<shared_ptr<CSGTerm> > background_terms; std::vector<shared_ptr<CSGTerm> > background_terms;
CSGTermEvaluator csgRenderer(tree, &geomevaluator); CSGTermEvaluator csgRenderer(tree);
shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
fs::current_path(original_path); fs::current_path(original_path);
@ -347,7 +361,9 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
std::string geom_out; std::string geom_out;
if ( stl_output_file ) geom_out = std::string(stl_output_file); if ( stl_output_file ) geom_out = std::string(stl_output_file);
else if ( off_output_file ) geom_out = std::string(off_output_file); else if ( off_output_file ) geom_out = std::string(off_output_file);
else if ( amf_output_file ) geom_out = std::string(amf_output_file);
else if ( dxf_output_file ) geom_out = std::string(dxf_output_file); else if ( dxf_output_file ) geom_out = std::string(dxf_output_file);
else if ( svg_output_file ) geom_out = std::string(svg_output_file);
else if ( png_output_file ) geom_out = std::string(png_output_file); else if ( png_output_file ) geom_out = std::string(png_output_file);
else { else {
PRINTB("Output file:%s\n",output_file); PRINTB("Output file:%s\n",output_file);
@ -391,6 +407,21 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
} }
} }
if (amf_output_file) {
if (root_geom->getDimension() != 3) {
PRINT("Current top level object is not a 3D object.\n");
return 1;
}
std::ofstream fstream(amf_output_file);
if (!fstream.is_open()) {
PRINTB("Can't open file \"%s\" for export", amf_output_file);
}
else {
exportFile(root_geom.get(), fstream, OPENSCAD_AMF);
fstream.close();
}
}
if (dxf_output_file) { if (dxf_output_file) {
if (root_geom->getDimension() != 2) { if (root_geom->getDimension() != 2) {
PRINT("Current top level object is not a 2D object.\n"); PRINT("Current top level object is not a 2D object.\n");
@ -405,6 +436,21 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
fstream.close(); fstream.close();
} }
} }
if (svg_output_file) {
if (root_geom->getDimension() != 2) {
PRINT("Current top level object is not a 2D object.\n");
return 1;
}
std::ofstream fstream(svg_output_file);
if (!fstream.is_open()) {
PRINTB("Can't open file \"%s\" for export", svg_output_file);
}
else {
exportFile(root_geom.get(), fstream, OPENSCAD_SVG);
fstream.close();
}
}
if (png_output_file) { if (png_output_file) {
std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary); std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary);
@ -432,19 +478,17 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
return 0; return 0;
} }
#ifdef OPENSCAD_TESTING
#undef OPENSCAD_QTGUI
#else
#define OPENSCAD_QTGUI 1
#endif
#ifdef OPENSCAD_QTGUI #ifdef OPENSCAD_QTGUI
#include <QtPlugin>
#if defined(__MINGW64__) || defined(__MINGW32__) || defined(_MSCVER)
#if QT_VERSION < 0x050000
Q_IMPORT_PLUGIN(qtaccessiblewidgets)
#endif // QT_VERSION
#endif // MINGW64/MINGW32/MSCVER
#include "MainWindow.h" #include "MainWindow.h"
#ifdef __APPLE__ #ifdef __APPLE__
#include "EventFilter.h" #include "EventFilter.h"
#endif #endif
#include <QApplication>
#include <QString> #include <QString>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -456,15 +500,16 @@ Q_DECLARE_METATYPE(shared_ptr<const Geometry>);
static QString assemblePath(const fs::path& absoluteBaseDir, static QString assemblePath(const fs::path& absoluteBaseDir,
const string& fileName) { const string& fileName) {
if (fileName.empty()) return ""; if (fileName.empty()) return "";
QString qsDir( boosty::stringy( absoluteBaseDir ).c_str() ); QString qsDir = QString::fromLocal8Bit( boosty::stringy( absoluteBaseDir ).c_str() );
QString qsFile( fileName.c_str() ); QString qsFile = QString::fromLocal8Bit( fileName.c_str() );
QFileInfo info( qsDir, qsFile ); // if qsfile is absolute, dir is ignored. // if qsfile is absolute, dir is ignored. (see documentation of QFileInfo)
QFileInfo info( qsDir, qsFile );
return info.absoluteFilePath(); return info.absoluteFilePath();
} }
bool QtUseGUI() bool QtUseGUI()
{ {
#ifdef Q_WS_X11 #ifdef Q_OS_X11
// see <http://qt.nokia.com/doc/4.5/qapplication.html#QApplication-2>: // see <http://qt.nokia.com/doc/4.5/qapplication.html#QApplication-2>:
// On X11, the window system is initialized if GUIenabled is true. If GUIenabled // On X11, the window system is initialized if GUIenabled is true. If GUIenabled
// is false, the application does not connect to the X server. On Windows and // is false, the application does not connect to the X server. On Windows and
@ -487,7 +532,7 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
} }
#endif #endif
QApplication app(argc, argv, true); //useGUI); QApplication app(argc, argv, true); //useGUI);
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
app.installEventFilter(new EventFilter(&app)); app.installEventFilter(new EventFilter(&app));
#endif #endif
// set up groups for QSettings // set up groups for QSettings
@ -503,7 +548,7 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
QDir exdir(app_path); QDir exdir(app_path);
QString qexamplesdir; QString qexamplesdir;
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
exdir.cd("../Resources"); // Examples can be bundled exdir.cd("../Resources"); // Examples can be bundled
if (!exdir.exists("examples")) exdir.cd("../../.."); if (!exdir.exists("examples")) exdir.cd("../../..");
#elif defined(Q_OS_UNIX) #elif defined(Q_OS_UNIX)
@ -523,11 +568,11 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
MainWindow::setExamplesDir(qexamplesdir); MainWindow::setExamplesDir(qexamplesdir);
parser_init(app_path.toLocal8Bit().constData()); parser_init(app_path.toLocal8Bit().constData());
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
installAppleEventHandlers(); installAppleEventHandlers();
#endif #endif
#if defined(OPENSCAD_DEPLOY) && defined(Q_WS_MAC) #if defined(OPENSCAD_DEPLOY) && defined(Q_OS_MAC)
AutoUpdater *updater = new SparkleAutoUpdater; AutoUpdater *updater = new SparkleAutoUpdater;
AutoUpdater::setUpdater(updater); AutoUpdater::setUpdater(updater);
if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates(); if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates();
@ -545,12 +590,17 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
BOOST_FOREACH(const string &infile, inputFiles) { BOOST_FOREACH(const string &infile, inputFiles) {
new MainWindow(assemblePath(original_path, infile)); new MainWindow(assemblePath(original_path, infile));
} }
app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
#else #else
MainWindow *m = new MainWindow(assemblePath(original_path, inputFiles[0])); new MainWindow(assemblePath(original_path, inputFiles[0]));
app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit()));
#endif #endif
return app.exec(); app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
int rc = app.exec();
if (MainWindow::windows) {
foreach (MainWindow *mainw, *MainWindow::windows) {
delete mainw;
}
}
return rc;
} }
#else // OPENSCAD_QTGUI #else // OPENSCAD_QTGUI
bool QtUseGUI() { return false; } bool QtUseGUI() { return false; }
@ -564,7 +614,7 @@ int gui(const vector<string> &inputFiles, const fs::path &original_path, int arg
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int rc = 0; int rc = 0;
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
set_output_handler(CocoaUtils::nslog, NULL); set_output_handler(CocoaUtils::nslog, NULL);
#endif #endif
#ifdef ENABLE_CGAL #ifdef ENABLE_CGAL
@ -586,6 +636,7 @@ int main(int argc, char **argv)
("info", "print information about the building process") ("info", "print information about the building process")
("render", "if exporting a png image, do a full CGAL render") ("render", "if exporting a png image, do a full CGAL render")
("preview", po::value<string>(), "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview") ("preview", po::value<string>(), "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview")
("csglimit", po::value<unsigned int>(), "if exporting a png image, stop rendering at the given number of CSG elements")
("camera", po::value<string>(), "parameters for camera when exporting png") ("camera", po::value<string>(), "parameters for camera when exporting png")
("imgsize", po::value<string>(), "=width,height for exporting png") ("imgsize", po::value<string>(), "=width,height for exporting png")
("projection", po::value<string>(), "(o)rtho or (p)erspective when exporting png") ("projection", po::value<string>(), "(o)rtho or (p)erspective when exporting png")
@ -596,7 +647,10 @@ int main(int argc, char **argv)
("d,d", po::value<string>(), "deps-file") ("d,d", po::value<string>(), "deps-file")
("m,m", po::value<string>(), "makefile") ("m,m", po::value<string>(), "makefile")
("D,D", po::value<vector<string> >(), "var=val") ("D,D", po::value<vector<string> >(), "var=val")
("enable", po::value<vector<string> >(), "enable experimental features"); #ifdef ENABLE_EXPERIMENTAL
("enable", po::value<vector<string> >(), "enable experimental features")
#endif
;
po::options_description hidden("Hidden options"); po::options_description hidden("Hidden options");
hidden.add_options() hidden.add_options()
@ -618,7 +672,10 @@ int main(int argc, char **argv)
} }
OpenSCAD::debug = ""; OpenSCAD::debug = "";
if (vm.count("debug")) OpenSCAD::debug = vm["debug"].as<string>(); if (vm.count("debug")) {
OpenSCAD::debug = vm["debug"].as<string>();
PRINTB("Debug on. --debug=%s",OpenSCAD::debug);
}
if (vm.count("help")) help(argv[0]); if (vm.count("help")) help(argv[0]);
if (vm.count("version")) version(); if (vm.count("version")) version();
if (vm.count("info")) info(); if (vm.count("info")) info();
@ -630,18 +687,22 @@ int main(int argc, char **argv)
if (vm["preview"].as<string>() == "throwntogether") if (vm["preview"].as<string>() == "throwntogether")
renderer = Render::THROWNTOGETHER; renderer = Render::THROWNTOGETHER;
if (vm.count("csglimit")) {
RenderSettings::inst()->openCSGTermLimit = vm["csglimit"].as<unsigned int>();
}
if (vm.count("o")) { if (vm.count("o")) {
// FIXME: Allow for multiple output files? // FIXME: Allow for multiple output files?
if (output_file) help(argv[0]); if (output_file) help(argv[0]);
output_file = vm["o"].as<string>().c_str(); output_file = vm["o"].as<string>().c_str();
} }
if (vm.count("s")) { if (vm.count("s")) {
PRINT("DEPRECATED: The -s option is deprecated. Use -o instead.\n"); printDeprecation("DEPRECATED: The -s option is deprecated. Use -o instead.\n");
if (output_file) help(argv[0]); if (output_file) help(argv[0]);
output_file = vm["s"].as<string>().c_str(); output_file = vm["s"].as<string>().c_str();
} }
if (vm.count("x")) { if (vm.count("x")) {
PRINT("DEPRECATED: The -x option is deprecated. Use -o instead.\n"); printDeprecation("DEPRECATED: The -x option is deprecated. Use -o instead.\n");
if (output_file) help(argv[0]); if (output_file) help(argv[0]);
output_file = vm["x"].as<string>().c_str(); output_file = vm["x"].as<string>().c_str();
} }
@ -662,11 +723,13 @@ int main(int argc, char **argv)
commandline_commands += ";\n"; commandline_commands += ";\n";
} }
} }
#ifdef ENABLE_EXPERIMENTAL
if (vm.count("enable")) { if (vm.count("enable")) {
BOOST_FOREACH(const string &feature, vm["enable"].as<vector<string> >()) { BOOST_FOREACH(const string &feature, vm["enable"].as<vector<string> >()) {
Feature::enable_feature(feature); Feature::enable_feature(feature);
} }
} }
#endif
vector<string> inputFiles; vector<string> inputFiles;
if (vm.count("input-file")) { if (vm.count("input-file")) {
inputFiles = vm["input-file"].as<vector<string> >(); inputFiles = vm["input-file"].as<vector<string> >();

Some files were not shown because too many files have changed in this diff Show More