diff options
-rw-r--r-- | RELEASE_NOTES | 26 | ||||
-rw-r--r-- | cgal.pri | 2 | ||||
-rw-r--r-- | doc/TODO.txt | 16 | ||||
-rw-r--r-- | doc/release-checklist.txt | 21 | ||||
-rw-r--r-- | flex.pri | 2 | ||||
-rw-r--r-- | openscad.pro | 4 | ||||
-rw-r--r-- | scripts/installer.nsi | 30 | ||||
-rwxr-xr-x | scripts/macosx-build-dependencies.sh | 8 | ||||
-rwxr-xr-x | scripts/publish-macosx.sh | 2 | ||||
-rwxr-xr-x | scripts/release-linux.sh | 6 | ||||
-rw-r--r-- | src/MainWindow.h | 3 | ||||
-rw-r--r-- | src/MainWindow.ui | 8 | ||||
-rw-r--r-- | src/Preferences.cc | 16 | ||||
-rw-r--r-- | src/Preferences.h | 1 | ||||
-rw-r--r-- | src/Preferences.ui | 56 | ||||
-rw-r--r-- | src/cgal.h | 6 | ||||
-rw-r--r-- | src/cgaladv.cc | 35 | ||||
-rw-r--r-- | src/cgaladv_convexhull2.cc | 55 | ||||
-rw-r--r-- | src/cgaladv_minkowski2.cc | 99 | ||||
-rw-r--r-- | src/export.cc | 53 | ||||
-rw-r--r-- | src/glview.cc | 52 | ||||
-rw-r--r-- | src/mainwin.cc | 127 | ||||
-rw-r--r-- | src/openscad.cc | 24 | ||||
-rw-r--r-- | src/primitives.cc | 119 | ||||
-rw-r--r-- | src/transform.cc | 20 | ||||
-rw-r--r-- | testdata/scad/convex_hull.scad | 43 | ||||
-rw-r--r-- | testdata/scad/minkowski.scad | 68 | ||||
-rw-r--r-- | testdata/scad/non-aff-matrix.scad | 6 | ||||
-rw-r--r-- | testdata/scad/testcolornames.scad | 159 |
29 files changed, 843 insertions, 224 deletions
diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3cfb955..5364e0a 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,8 +1,28 @@ -OpenSCAD 2011.XX +OpenSCAD 20xx.yy ================ -o Added rands() function -o Added sign() function + +OpenSCAD 2011.06 +================ + +o Added "Export as Image" menu. + +Bugfixes: +o Cylinder tesselation broke existing models which are using cylinders + for e.g. captured nut slots and are dependent on the orientation not + changing. +o 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. + + + +OpenSCAD 2011.04 +================ + +o Added hull() for convex hulls (2D object only) +o minkowski() now supports 2D objects +o Added functions: rands(), sign() o Now supports escaping of the following characters in strings: \n, \t, \r, \\, \" o Support nested includes o Improved parsing of numbers @@ -14,7 +14,7 @@ cgal { } win32 { - LIBS += $$CGAL_DIR/auxiliary/gmp/lib/libmpfr-4.lib -lCGAL-vc90-mt-gd + LIBS += $$CGAL_DIR/auxiliary/gmp/lib/libmpfr-4.lib -lCGAL-vc90-mt-s } else { LIBS += -lgmp -lmpfr -lCGAL } diff --git a/doc/TODO.txt b/doc/TODO.txt index cca4a2a..b9440c6 100644 --- a/doc/TODO.txt +++ b/doc/TODO.txt @@ -8,6 +8,7 @@ o It's now possible to start a new rendering while one is already running (which -> turn off most (or all) interaction while rendering -> Lock all or only one MainWindow (MDI)? o Look into the polygon winding and rotate_extrude() problem reported by Britton +o CGAL Aff_transformation_3 doesn't support non-affine transformations (non-aff-matrix.scad) STL Import BUGS --------------- @@ -102,9 +103,15 @@ o Editor wishlist in the source code in the 3D view - Tabbed editor for designs including other files - C-c/C-v should work on the focused widget, not always in the editor - +o Error reporting/debugging + - Provide better error messages when polygon ordering causes CGAL errors: + o Supply syntax highlighting of the exact polygon indices which are + reported to be wrong + o Provide some interaction for debug walk-through? + - Provide visual highlighting of geometry corresponding to code + -> could aid debugging a lot o Computation - - Run CGAL rendering in a backgroud thread + - Run CGAL rendering in a background thread - Enable viewing/editing while rendering - Progress: Call progresswidget more often to avoid app hanging for multiple seconds (i.e. make cancel button more responsive) @@ -146,7 +153,7 @@ o Language Frontend - Rethink for vs. intersection_for vs. group. Should for loops generate child lists instead, and make these passable to other modules or accessible by child()? - - constants: PI + - constants: PI, OpenSCAD version o DXF Import/Export - Use dxflib from RibbonSoft for import/export? -> investigate - Import @@ -167,6 +174,8 @@ o Misc - Add 'lines' object type for non-solid 2d drawings - Is there a reason why modules like echo, empty if, empty for loop returns an empty AbstractNode instead of being ignored? + - Bug: Using the background operator (%) on the only object in a scene triggers a + CSG error: No top level object found o Grammar - dim->name -> dim->label - A random(seed) function @@ -213,6 +222,7 @@ o Write a simple test script that collects verified and current STL renderings o Write simple driver scripts for comparing output of above command o Collect "all" available OpenSCAD scripts from the internets and run the integration tests on them all +o Write a regression test for the hexagonal cylinder orientation issue INFRASTRUCTURE -------------- diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt index d0e1174..a455ca9 100644 --- a/doc/release-checklist.txt +++ b/doc/release-checklist.txt @@ -12,25 +12,28 @@ o Tag release git tag "openscad-2011.01" o build source package - git archive --format=tar openscad-2011.01 --prefix=openscad-2011.01/ | gzip > openscad-2011.01.tar.gz + git archive --format=tar openscad-2011.01 --prefix=openscad-2011.01/ | gzip > openscad-2011.01.src.tar.gz o build binaries + tar xzf openscad-2011.01.src.tar.gz + cd openscad-2011.01 Mac OS X - - publish-macosx.sh -> OpenSCAD-2011.01.dmg + For Qt-4.7.3: Remove /Developers/Applications/Qt/plugins/qmltooling + ./scripts/publish-macosx.sh -> OpenSCAD-2011.01.dmg Linux: FIXME 32 vs. 64 bit - - release-linux.sh + ./scripts/release-linux.sh Windows: FIXME 32 vs. 64 bit +o FIXME: Run some tests + o Set back version: release-linux.sh, publish-macosx.sh, FIXME: Windows +o git push --tags + o Upload - Github Upload manually here: https://github.com/openscad/openscad/downloads FIXME: Write a script - - Google code - - Get password from https://code.google.com/hosting/settings - ./scripts/googlecode_upload.py -u kintel -w <passwd> -s "OpenSCAD 2011.11 Windows" -p openscad openscad-2011.01.win32.zip - ./scripts/googlecode_upload.py -u kintel -w <passwd> -s "OpenSCAD 2011.11 Linux x86" -p openscad openscad-2011.01.linux-x86.tar.gz - ./scripts/googlecode_upload.py -u kintel -w <passwd> -s "OpenSCAD 2011.11 Mac OS X" -p openscad openscad-2011.01.dmg - ./scripts/googlecode_upload.py -u kintel -w <passwd> -s "OpenSCAD 2011.11 Source code" -p openscad openscad-2011.01.src.tar.gz +o Update web page +o Write email to mailing list @@ -3,7 +3,7 @@ flex.name = Flex ${QMAKE_FILE_IN}
flex.input = FLEXSOURCES
flex.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.lexer.cpp
-flex.commands = flex -P ${QMAKE_FILE_BASE} -o ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.lexer.cpp ${QMAKE_FILE_IN}
+flex.commands = flex -P ${QMAKE_FILE_BASE} -o${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.lexer.cpp ${QMAKE_FILE_IN}
flex.CONFIG += target_predeps
flex.variable_out = GENERATED_SOURCES
silent:flex.commands = @echo Lex ${QMAKE_FILE_IN} && $$flex.commands
diff --git a/openscad.pro b/openscad.pro index 4fe0483..6b85d59 100644 --- a/openscad.pro +++ b/openscad.pro @@ -162,6 +162,7 @@ SOURCES += src/openscad.cc \ src/primitives.cc \ src/projection.cc \ src/cgaladv.cc \ + src/cgaladv_convexhull2.cc \ src/cgaladv_minkowski3.cc \ src/cgaladv_minkowski2.cc \ src/surface.cc \ @@ -181,7 +182,7 @@ SOURCES += src/openscad.cc \ src/Preferences.cc \ src/progress.cc \ src/editor.cc \ - src/mathc99.cc + src/mathc99.cc macx { HEADERS += src/AppleEvents.h \ @@ -199,4 +200,3 @@ INSTALLS += examples libraries.path = /usr/local/share/openscad/libraries/ libraries.files = libraries/* INSTALLS += libraries - diff --git a/scripts/installer.nsi b/scripts/installer.nsi new file mode 100644 index 0000000..269a30c --- /dev/null +++ b/scripts/installer.nsi @@ -0,0 +1,30 @@ +!include "FileAssociation.nsh" +Name "OpenSCAD" +OutFile "openscad_setup.exe" +InstallDir $PROGRAMFILES\OpenSCAD +DirText "This will install OpenSCAD on your computer. Choose a directory" +Section "install" +SetOutPath $INSTDIR +File openscad.exe +File /r examples +File /r libraries +${registerExtension} "$INSTDIR\openscad.exe" ".scad" "OpenSCAD_File" +CreateShortCut $SMPROGRAMS\OpenSCAD.lnk $INSTDIR\openscad.exe +WriteUninstaller $INSTDIR\Uninstall.exe +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "DisplayName" "OpenSCAD (remove only)" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "UninstallString" "$INSTDIR\Uninstall.exe" +SectionEnd +Section "Uninstall" +${unregisterExtension} ".scad" "OpenSCAD_File" +Delete $INSTDIR\Uninstall.exe +Delete $INSTDIR\MyProg.exe +Delete $SMPROGRAMS\OpenSCAD.lnk +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" +RMDir /r $INSTDIR\examples +RMDir /r $INSTDIR\libraries\mcad +Delete $INSTDIR\libraries\boxes.scad +Delete $INSTDIR\libraries\shapes.scad +RMDir $INSTDIR\libraries +Delete $INSTDIR\openscad.exe +RMDir $INSTDIR +SectionEnd diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index f5a44d0..e011582 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -17,8 +17,8 @@ # o Port to other platforms? # -BASEDIR=/Users/kintel/code/metalab/checkout/OpenSCAD/libraries -OPENSCADDIR=/Users/kintel/code/metalab/checkout/OpenSCAD/openscad-release +BASEDIR=/Users/kintel/code/OpenSCAD/libraries +OPENSCADDIR=/Users/kintel/code/OpenSCAD/openscad SRCDIR=$BASEDIR/src DEPLOYDIR=$BASEDIR/install @@ -46,7 +46,7 @@ build_gmp() # 64-bit version mkdir build-x86_64 cd build-x86_64 - ../configure --prefix=$DEPLOYDIR/x86_64 "CFLAGS=-mmacosx-version-min=10.5" LDFLAGS="-mmacosx-version-min=10.5" --enable-cxx + ../configure --prefix=$DEPLOYDIR/x86_64 "CFLAGS=-mmacosx-version-min=10.5 -arch x86_64" LDFLAGS="-mmacosx-version-min=10.5 -arch x86_64" ABI=64 --enable-cxx make install # merge @@ -158,7 +158,7 @@ build_opencsg() echo "Using basedir:" $BASEDIR mkdir -p $SRCDIR $DEPLOYDIR build_gmp 5.0.1 -build_mpfr 3.0.0 +build_mpfr 3.0.1 build_boost 1.46.1 build_cgal 3.7 build_glew 1.5.8 diff --git a/scripts/publish-macosx.sh b/scripts/publish-macosx.sh index 11820df..6415b52 100755 --- a/scripts/publish-macosx.sh +++ b/scripts/publish-macosx.sh @@ -1,7 +1,7 @@ #!/bin/sh VERSION=`date "+%Y.%m.%d"` -#VERSION=2010.05 +#VERSION=2011.06 # This is the same location as DEPLOYDIR in macosx-build-dependencies.sh export MACOSX_DEPLOY_DIR=$PWD/../libraries/install diff --git a/scripts/release-linux.sh b/scripts/release-linux.sh index 8f532e0..35d177f 100755 --- a/scripts/release-linux.sh +++ b/scripts/release-linux.sh @@ -2,7 +2,7 @@ # WARNING: This script might only work with the authors setup... VERSION=`date "+%Y.%m.%d"` -#VERSION=2010.05 +#VERSION=2011.06 set -ex @@ -30,8 +30,8 @@ gcc -o chrpath_linux scripts/chrpath_linux.c ./chrpath_linux -d release/lib/openscad/openscad ldd openscad | sed -re 's,.* => ,,; s,[\t ].*,,;' -e '/Qt|boost/ { p; d; };' \ - -e '/lib(audio|CGAL|GLEW|opencsg|png)\.so/ { p; d; };' \ - -e 'd;' | xargs cp -vt release/lib/openscad/ + -e '/lib(audio|CGAL|GLEW|opencsg|png|gmp|gmpxx|mpfr)\.so/ { p; d; };' \ + -e 'd;' | xargs cp -vt release/lib/openscad/ strip release/lib/openscad/* cat > release/install.sh << "EOT" diff --git a/src/MainWindow.h b/src/MainWindow.h index c0a9844..43ab273 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -79,6 +79,8 @@ private: static void consoleOutput(const QString &msg, void *userdata) { static_cast<MainWindow*>(userdata)->console->append(msg); } + void loadViewSettings(); + void loadDesignSettings(); private slots: void actionNew(); @@ -109,6 +111,7 @@ private slots: void actionExportSTL(); void actionExportOFF(); void actionExportDXF(); + void actionExportImage(); void actionFlushCaches(); public: diff --git a/src/MainWindow.ui b/src/MainWindow.ui index f61d240..6548c8e 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -112,7 +112,7 @@ <x>0</x> <y>0</y> <width>681</width> - <height>25</height> + <height>22</height> </rect> </property> <widget class="QMenu" name="menu_File"> @@ -178,6 +178,7 @@ <addaction name="designActionExportSTL"/> <addaction name="designActionExportOFF"/> <addaction name="designActionExportDXF"/> + <addaction name="designActionExportImage"/> <addaction name="designActionFlushCaches"/> </widget> <widget class="QMenu" name="menu_View"> @@ -651,6 +652,11 @@ <string>Automatic Reload and Compile</string> </property> </action> + <action name="designActionExportImage"> + <property name="text"> + <string>Export as Image...</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/src/Preferences.cc b/src/Preferences.cc index 6419944..eb6af61 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -40,6 +40,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->defaultmap["3dview/colorscheme"] = this->colorSchemeChooser->currentItem()->text(); this->defaultmap["editor/fontfamily"] = this->fontChooser->currentText(); this->defaultmap["editor/fontsize"] = this->fontSize->currentText().toUInt(); + this->defaultmap["editor/opengl20_warning_show"] = true; // Toolbar QActionGroup *group = new QActionGroup(this); @@ -97,7 +98,8 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this, SLOT(fontFamilyChanged(const QString &))); connect(this->fontSize, SIGNAL(editTextChanged(const QString &)), this, SLOT(fontSizeChanged(const QString &))); - + connect(this->OpenGL20WarningCheckbox, SIGNAL(clicked(bool)), + this, SLOT(OpenGL20WarningChanged(bool))); updateGUI(); } @@ -148,6 +150,13 @@ void Preferences::fontSizeChanged(const QString &size) emit fontChanged(getValue("editor/fontfamily").toString(), intsize); } +void +Preferences::OpenGL20WarningChanged(bool state) +{ + QSettings settings; + settings.setValue("editor/opengl20_warning_show",state); +} + void Preferences::keyPressEvent(QKeyEvent *e) { #ifdef Q_WS_MAC @@ -185,6 +194,7 @@ QVariant Preferences::getValue(const QString &key) const void Preferences::updateGUI() { + QSettings settings; QList<QListWidgetItem *> found = this->colorSchemeChooser->findItems(getValue("3dview/colorscheme").toString(), Qt::MatchExactly); @@ -204,6 +214,9 @@ void Preferences::updateGUI() else { this->fontSize->setEditText(fontsize); } + + bool opengl20_warning_show = getValue("editor/opengl20_warning_show").toBool(); + this->OpenGL20WarningCheckbox->setChecked(opengl20_warning_show); } void Preferences::apply() const @@ -211,4 +224,3 @@ void Preferences::apply() const emit fontChanged(getValue("editor/fontfamily").toString(), getValue("editor/fontsize").toUInt()); emit requestRedraw(); } - diff --git a/src/Preferences.h b/src/Preferences.h index 39599fd..79fa7ab 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -34,6 +34,7 @@ public slots: void colorSchemeChanged(); void fontFamilyChanged(const QString &); void fontSizeChanged(const QString &); + void OpenGL20WarningChanged(bool); signals: void requestRedraw() const; diff --git a/src/Preferences.ui b/src/Preferences.ui index 13db9f3..2ab7646 100644 --- a/src/Preferences.ui +++ b/src/Preferences.ui @@ -179,71 +179,29 @@ <number>0</number> </property> <item> - <spacer name="verticalSpacer_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>120</height> - </size> - </property> - </spacer> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_6"> <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_2"> - <property name="enabled"> - <bool>false</bool> - </property> + <widget class="QCheckBox" name="OpenGL20WarningCheckbox"> <property name="text"> - <string>advanced</string> + <string>Show OpenGL 2.0 warning && rendering info</string> </property> </widget> </item> <item> - <spacer name="horizontalSpacer_3"> + <spacer name="verticalSpacer_2"> <property name="orientation"> - <enum>Qt::Horizontal</enum> + <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> - <width>40</width> - <height>20</height> + <width>20</width> + <height>40</height> </size> </property> </spacer> </item> </layout> </item> - <item> - <spacer name="verticalSpacer_3"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>120</height> - </size> - </property> - </spacer> - </item> </layout> </widget> </widget> @@ -10,6 +10,9 @@ #include <CGAL/Polyhedron_3.h> #include <CGAL/Nef_polyhedron_3.h> #include <CGAL/IO/Polyhedron_iostream.h> +#include <CGAL/Exact_predicates_exact_constructions_kernel.h> +#include <CGAL/Polygon_2.h> +#include <CGAL/Polygon_with_holes_2.h> typedef CGAL::Extended_cartesian<CGAL::Gmpq> CGAL_Kernel2; typedef CGAL::Nef_polyhedron_2<CGAL_Kernel2> CGAL_Nef_polyhedron2; @@ -24,6 +27,9 @@ typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; typedef CGAL_Nef_polyhedron3::Vector_3 CGAL_Vector; typedef CGAL_Nef_polyhedron3::Plane_3 CGAL_Plane; typedef CGAL_Nef_polyhedron3::Point_3 CGAL_Point; +typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2; +typedef CGAL::Polygon_2<CGAL_ExactKernel2> CGAL_Poly2; +typedef CGAL::Polygon_with_holes_2<CGAL_ExactKernel2> CGAL_Poly2h; struct CGAL_Nef_polyhedron { diff --git a/src/cgaladv.cc b/src/cgaladv.cc index bf2e85a..dd797fd 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -34,12 +34,14 @@ #ifdef ENABLE_CGAL extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b); extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b); +extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a); #endif enum cgaladv_type_e { MINKOWSKI, GLIDE, - SUBDIV + SUBDIV, + HULL }; class CgaladvModule : public AbstractModule @@ -125,6 +127,7 @@ void register_builtin_cgaladv() builtin_modules["minkowski"] = new CgaladvModule(MINKOWSKI); builtin_modules["glide"] = new CgaladvModule(GLIDE); builtin_modules["subdiv"] = new CgaladvModule(SUBDIV); + builtin_modules["hull"] = new CgaladvModule(HULL); } #ifdef ENABLE_CGAL @@ -174,6 +177,29 @@ CGAL_Nef_polyhedron CgaladvNode::render_cgal_nef_polyhedron() const PRINT("WARNING: subdiv() is not implemented yet!"); } + if (type == HULL) + { + std::list<CGAL_Nef_polyhedron2> polys; + bool all2d = true; + foreach(AbstractNode * v, children) { + if (v->modinst->tag_background) + continue; + N = v->render_cgal_nef_polyhedron(); + if (N.dim == 3) { + //polys.push_back(tmp.p3); + PRINT("WARNING: hull() is not implemented yet for 3D objects!"); + all2d=false; + } + if (N.dim == 2) { + polys.push_back(N.p2); + } + v->progress_report(); + } + + if (all2d) + N.p2 = convexhull2(polys); + } + cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); print_messages_pop(); progress_report(); @@ -192,6 +218,9 @@ CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlight if (type == SUBDIV) return render_csg_term_from_nef(m, highlights, background, "subdiv", this->convexity); + if (type == HULL) + return render_csg_term_from_nef(m, highlights, background, "hull", this->convexity); + return NULL; } @@ -199,7 +228,7 @@ CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlight CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const { - PRINT("WARNING: Found minkowski(), glide() or subdiv() statement but compiled without CGAL support!"); + PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!"); return NULL; } @@ -217,6 +246,8 @@ QString CgaladvNode::dump(QString indent) const } if (type == SUBDIV) text.sprintf("subdiv(level = %d, convexity = %d) {\n", this->level, this->convexity); + if (type == HULL) + text.sprintf("hull() {\n"); foreach (AbstractNode *v, this->children) text += v->dump(indent + QString("\t")); text += indent + "}\n"; diff --git a/src/cgaladv_convexhull2.cc b/src/cgaladv_convexhull2.cc new file mode 100644 index 0000000..448dd4b --- /dev/null +++ b/src/cgaladv_convexhull2.cc @@ -0,0 +1,55 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and + * Marius Kintel <marius@kintel.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ENABLE_CGAL + +#include "cgal.h" +#include <CGAL/convex_hull_2.h> + +extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a); +extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p); + +CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a) +{ + std::list<CGAL_Nef_polyhedron2::Point> points; + + std::list<CGAL_Nef_polyhedron2>::iterator i; + for (i=a.begin(); i!=a.end(); i++) { + CGAL_Poly2 ap=nef2p2(*i); + for (size_t j=0;j<ap.size();j++) { + double x=to_double(ap[j].x()),y=to_double(ap[j].y()); + CGAL_Nef_polyhedron2::Point p=CGAL_Nef_polyhedron2::Point(x,y); + points.push_back(p); + } + } + + std::list<CGAL_Nef_polyhedron2::Point> result; + CGAL::convex_hull_2(points.begin(),points.end(),std::back_inserter(result)); + + return CGAL_Nef_polyhedron2(result.begin(),result.end(),CGAL_Nef_polyhedron2::INCLUDED); +} + +#endif diff --git a/src/cgaladv_minkowski2.cc b/src/cgaladv_minkowski2.cc index 6a4b31c..b722708 100644 --- a/src/cgaladv_minkowski2.cc +++ b/src/cgaladv_minkowski2.cc @@ -31,20 +31,53 @@ #include "grid.h" #include "cgal.h" -#if 0 -#include <CGAL/Exact_predicates_exact_constructions_kernel.h> #include <CGAL/minkowski_sum_2.h> extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b); +extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p); -struct K2 : public CGAL::Exact_predicates_exact_constructions_kernel {}; -typedef CGAL::Polygon_2<K2> Poly2; -typedef CGAL::Polygon_with_holes_2<K2> Poly2h; +//----------------------------------------------------------------------------- +// Pretty-print a CGAL polygon. +// +template<class Kernel, class Container> +void print_polygon (const CGAL::Polygon_2<Kernel, Container>& P) +{ + typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit; + + std::cout << "[ " << P.size() << " vertices:"; + for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit) + std::cout << " (" << *vit << ')'; + std::cout << " ]" << std::endl; +} -static Poly2 nef2p2(CGAL_Nef_polyhedron2 p) +//----------------------------------------------------------------------------- +// Pretty-print a polygon with holes. +// +template<class Kernel, class Container> +void print_polygon_with_holes (const CGAL::Polygon_with_holes_2<Kernel, Container>& pwh) { + if (! pwh.is_unbounded()) { + std::cout << "{ Outer boundary = "; + print_polygon (pwh.outer_boundary()); + } else + std::cout << "{ Unbounded polygon." << std::endl; + + typename CGAL::Polygon_with_holes_2<Kernel,Container>::Hole_const_iterator hit; + unsigned int k = 1; + + std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl; + for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) { + std::cout << " Hole #" << k << " = "; + print_polygon (*hit); + } + std::cout << " }" << std::endl; + + return; +} + +CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p) { - std::list<K2::Point_2> points; + std::list<CGAL_ExactKernel2::Point_2> points; Grid2d<int> grid(GRID_COARSE); typedef CGAL_Nef_polyhedron2::Explorer Explorer; @@ -52,9 +85,13 @@ static Poly2 nef2p2(CGAL_Nef_polyhedron2 p) typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; Explorer E = p.explorer(); - for (fci_t fit = E.faces_begin(), fend = E.faces_end(); fit != fend; ++fit) + for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) { - if (fit != E.faces_begin()) { + if (!E.mark(fit)) { + continue; + } + //if (fit != E.faces_begin()) { + if (points.size() != 0) { PRINT("WARNING: minkowski() is not implemented for 2d objects with holes!"); break; } @@ -65,33 +102,41 @@ static Poly2 nef2p2(CGAL_Nef_polyhedron2 p) Explorer::Point ep = E.point(E.target(fcirc)); double x = to_double(ep.x()), y = to_double(ep.y()); grid.align(x, y); - points.push_back(K2::Point_2(x, y)); + points.push_back(CGAL_ExactKernel2::Point_2(x, y)); } } } - return Poly2(points.begin(), points.end()); + return CGAL_Poly2(points.begin(), points.end()); } - -CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b) -{ - Poly2 ap = nef2p2(a), bp = nef2p2(b); - Poly2h x = minkowski_sum_2(ap, bp); - /** FIXME **/ - - PRINT("WARNING: minkowski() is not implemented yet for 2d objects!"); - return CGAL_Nef_polyhedron2(); +static CGAL_Nef_polyhedron2 p2nef2(CGAL_Poly2 p2) { + std::list<CGAL_Nef_polyhedron2::Point> points; + for (size_t j = 0; j < p2.size(); j++) { + double x = to_double(p2[j].x()); + double y = to_double(p2[j].y()); + CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y); + points.push_back(p); + } + return CGAL_Nef_polyhedron2(points.begin(), points.end(), CGAL_Nef_polyhedron2::INCLUDED); } -#else - -CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2, CGAL_Nef_polyhedron2) +CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b) { - PRINT("WARNING: minkowski() is not implemented yet for 2d objects!"); - return CGAL_Nef_polyhedron2(); + CGAL_Poly2 ap = nef2p2(a), bp = nef2p2(b); + + if (ap.size() == 0) { + PRINT("WARNING: minkowski() could not get any points from object 1!"); + return CGAL_Nef_polyhedron2(); + } else if (bp.size() == 0) { + PRINT("WARNING: minkowski() could not get any points from object 2!"); + return CGAL_Nef_polyhedron2(); + } else { + CGAL_Poly2h x = minkowski_sum_2(ap, bp); + + // Make a CGAL_Nef_polyhedron2 out of just the boundary for starters + return p2nef2(x.outer_boundary()); + } } #endif -#endif - diff --git a/src/export.cc b/src/export.cc index 884e139..8e0ab16 100644 --- a/src/export.cc +++ b/src/export.cc @@ -167,20 +167,6 @@ void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog * } setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output - - // Some importers (e.g. QCAD) needs a HEADER section specifying AutoCAD 2000 as - // the file format for LWPOLYLINE entities to work - fprintf(f, " 0\n" - "SECTION\n" - " 2\n" - "HEADER\n" - " 9\n" - "$ACADVER\n" - " 1\n" - "AC1015\n" - " 0\n" - "ENDSEC\n"); - // Some importers (e.g. Inkscape) needs a BLOCKS section to be present fprintf(f, " 0\n" "SECTION\n" @@ -197,29 +183,26 @@ void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog * DxfData dd(*root_N); for (int i=0; i<dd.paths.size(); i++) { - if (dd.paths[i].points.size() < 2) - // not a valid polygon - continue; - // Use the LWPOLYLINE class - this makes it easier to handle complete - // objects (as paths) in Inkscape. - fprintf(f, " 0\n"); - fprintf(f, "LWPOLYLINE\n"); - // Some importers (e.g. Inkscape) need a layer to be specified - fprintf(f, " 8\n"); - fprintf(f, "0\n"); - // number of vertices - fprintf(f, " 90\n"); - fprintf(f, "%d\n", dd.paths[i].points.size()); - // polygon flag (closed, ...) - fprintf(f, " 70\n"); - fprintf(f, "%d\n", dd.paths[i].is_closed ? 1 : 0); - // add all points - for (int j=0; j<dd.paths[i].points.size(); j++) { - DxfData::Point *p = dd.paths[i].points[j]; + for (int j=1; j<dd.paths[i].points.size(); j++) { + DxfData::Point *p1 = dd.paths[i].points[j-1]; + DxfData::Point *p2 = dd.paths[i].points[j]; + double x1 = p1->x; + double y1 = p1->y; + double x2 = p2->x; + double y2 = p2->y; + fprintf(f, " 0\n"); + fprintf(f, "LINE\n"); + // Some importers (e.g. Inkscape) needs a layer to be specified + fprintf(f, " 8\n"); + fprintf(f, "0\n"); fprintf(f, " 10\n"); - fprintf(f, "%f\n", p->x); + fprintf(f, "%f\n", x1); + fprintf(f, " 11\n"); + fprintf(f, "%f\n", x2); fprintf(f, " 20\n"); - fprintf(f, "%f\n", p->y); + fprintf(f, "%f\n", y1); + fprintf(f, " 21\n"); + fprintf(f, "%f\n", y2); } } diff --git a/src/glview.cc b/src/glview.cc index 870a1c9..ef023f2 100644 --- a/src/glview.cc +++ b/src/glview.cc @@ -29,9 +29,15 @@ #include <QApplication> #include <QWheelEvent> +#include <QCheckBox> +#include <QDialogButtonBox> #include <QMouseEvent> #include <QMessageBox> +#include <QPushButton> +#include <QSettings> #include <QTimer> +#include <QTextEdit> +#include <QVBoxLayout> #include "mathc99.h" #include <stdio.h> @@ -180,7 +186,10 @@ void GLView::initializeGL() } } else { opencsg_support = false; - QTimer::singleShot(0, this, SLOT(display_opengl20_warning())); + QSettings settings; + if (settings.value("editor/opengl20_warning_show",true).toBool()) { + QTimer::singleShot(0, this, SLOT(display_opengl20_warning())); + } } #endif /* ENABLE_OPENCSG */ } @@ -188,6 +197,9 @@ void GLView::initializeGL() #ifdef ENABLE_OPENCSG void GLView::display_opengl20_warning() { + // data + QString title = QString("GLEW: GL_VERSION_2_0 is not supported!"); + QString rendererinfo; rendererinfo.sprintf("GLEW version %s\n" "%s (%s)\n" @@ -196,11 +208,43 @@ void GLView::display_opengl20_warning() glGetString(GL_RENDERER), glGetString(GL_VENDOR), glGetString(GL_VERSION)); - QMessageBox::warning(NULL, "GLEW: GL_VERSION_2_0 is not supported!", - QString("Warning: No support for OpenGL 2.0 found! OpenCSG View has been disabled.\n\n" + QString message = QString("Warning: No support for OpenGL 2.0 found! OpenCSG View has been disabled.\n\n" "It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 " "support. Please check if OpenGL 2.0 drivers are available for your " - "graphics hardware.\n\n%1").arg(rendererinfo)); + "graphics hardware. Your renderer information is as follows:\n\n%1").arg(rendererinfo); + + QString note = QString("Uncheck to hide this message in the future"); + + // presentation + QDialog *dialog = new QDialog(this); + dialog->setSizeGripEnabled(true); + dialog->setWindowTitle(title); + dialog->resize(500,300); + + QVBoxLayout *layout = new QVBoxLayout(dialog); + dialog->setLayout(layout); + + QTextEdit *textEdit = new QTextEdit(dialog); + textEdit->setPlainText(message); + layout->addWidget(textEdit); + + QCheckBox *checkbox = new QCheckBox(note,dialog); + checkbox->setCheckState(Qt::Checked); + layout->addWidget(checkbox); + + QDialogButtonBox *buttonbox = + new QDialogButtonBox( QDialogButtonBox::Ok, Qt::Horizontal,dialog); + layout->addWidget(buttonbox); + buttonbox->button(QDialogButtonBox::Ok)->setFocus(); + buttonbox->button(QDialogButtonBox::Ok)->setDefault(true); + + // action + connect(buttonbox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(checkbox, SIGNAL(clicked(bool)), + Preferences::inst()->OpenGL20WarningCheckbox, SLOT(setChecked(bool))); + connect(checkbox, SIGNAL(clicked(bool)), + Preferences::inst(), SLOT(OpenGL20WarningChanged(bool))); + dialog->exec(); } #endif diff --git a/src/mainwin.cc b/src/mainwin.cc index d5da7aa..2255ac3 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -96,6 +96,37 @@ static char copyrighttext[] = "the Free Software Foundation; either version 2 of the License, or" "(at your option) any later version."; +static void +settings_setValueList(const QString &key,const QList<int> &list) +{ + QSettings settings; + settings.beginWriteArray(key); + for (int i=0;i<list.size(); ++i) { + settings.setArrayIndex(i); + settings.setValue("entry",list[i]); + } + settings.endArray(); +} + +QList<int> +settings_valueList(const QString &key, const QList<int> &defaultList = QList<int>()) +{ + QSettings settings; + QList<int> result; + if (settings.contains(key+"/size")){ + int length = settings.beginReadArray(key); + for (int i = 0; i < length; ++i) { + settings.setArrayIndex(i); + result += settings.value("entry").toInt(); + } + settings.endArray(); + return result; + } else { + return defaultList; + } + +} + MainWindow::MainWindow(const QString &filename) { setupUi(this); @@ -237,6 +268,7 @@ MainWindow::MainWindow(const QString &filename) connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL())); connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF())); connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF())); + connect(this->designActionExportImage, SIGNAL(triggered()), this, SLOT(actionExportImage())); connect(this->designActionFlushCaches, SIGNAL(triggered()), this, SLOT(actionFlushCaches())); // View menu @@ -312,27 +344,69 @@ MainWindow::MainWindow(const QString &filename) this, SLOT(setFont(const QString&,uint))); Preferences::inst()->apply(); + // make sure it looks nice.. + QSettings settings; + resize(settings.value("window/size", QSize(800, 600)).toSize()); + move(settings.value("window/position", QPoint(0, 0)).toPoint()); + QList<int> s1sizes = settings_valueList("window/splitter1sizes",QList<int>()<<400<<400); + QList<int> s2sizes = settings_valueList("window/splitter2sizes",QList<int>()<<400<<200); + splitter1->setSizes(s1sizes); + splitter2->setSizes(s2sizes); // display this window and check for OpenGL 2.0 (OpenCSG) support viewModeThrownTogether(); show(); - // make sure it looks nice.. - resize(800, 600); - splitter1->setSizes(QList<int>() << 400 << 400); - splitter2->setSizes(QList<int>() << 400 << 200); - #ifdef ENABLE_OPENCSG viewModeOpenCSG(); #else viewModeThrownTogether(); #endif - viewPerspective(); + loadViewSettings(); + loadDesignSettings(); setAcceptDrops(true); clearCurrentOutput(); } +void +MainWindow::loadViewSettings(){ + QSettings settings; + if (settings.value("view/showEdges").toBool()) { + viewActionShowEdges->setChecked(true); + viewModeShowEdges(); + } + if (settings.value("view/showAxes").toBool()) { + viewActionShowAxes->setChecked(true); + viewModeShowAxes(); + } + if (settings.value("view/showCrosshairs").toBool()) { + viewActionShowCrosshairs->setChecked(true); + viewModeShowCrosshairs(); + } + if (settings.value("view/orthogonalProjection").toBool()) { + viewOrthogonal(); + } else { + viewPerspective(); + } + if (settings.value("view/hideConsole").toBool()) { + viewActionHide->setChecked(true); + hideConsole(); + } + if (settings.value("view/hideEditor").toBool()) { + editActionHide->setChecked(true); + hideEditor(); + } +} + +void +MainWindow::loadDesignSettings() +{ + QSettings settings; + if (settings.value("design/autoReload").toBool()) + designActionAutoReload->setChecked(true); +} + MainWindow::~MainWindow() { if (root_module) @@ -929,10 +1003,13 @@ void MainWindow::actionReload() void MainWindow::hideEditor() { + QSettings settings; if (editActionHide->isChecked()) { editor->hide(); + settings.setValue("view/hideEditor",true); } else { editor->show(); + settings.setValue("view/hideEditor",false); } } @@ -973,6 +1050,8 @@ void MainWindow::checkAutoReload() void MainWindow::autoReloadSet(bool on) { + QSettings settings; + settings.setValue("design/autoReload",designActionAutoReload->isChecked()); if (on) { autoReloadInfo = QString(); autoReloadTimer->start(200); @@ -1320,6 +1399,24 @@ void MainWindow::actionExportDXF() #endif /* ENABLE_CGAL */ } +void MainWindow::actionExportImage() +{ + QImage img = screen->grabFrameBuffer(); + setCurrentOutput(); + + QString img_filename = QFileDialog::getSaveFileName(this, + "Export Image", "", "PNG Files (*.png)"); + if (img_filename.isEmpty()) { + PRINTF("No filename specified. Image export aborted."); + clearCurrentOutput(); + return; + } + + img.save(img_filename, "PNG"); + + clearCurrentOutput(); +} + void MainWindow::actionFlushCaches() { PolySet::ps_cache.clear(); @@ -1603,17 +1700,23 @@ void MainWindow::viewModeThrownTogether() void MainWindow::viewModeShowEdges() { + QSettings settings; + settings.setValue("view/showEdges",viewActionShowEdges->isChecked()); screen->updateGL(); } void MainWindow::viewModeShowAxes() { + QSettings settings; + settings.setValue("view/showAxes",viewActionShowAxes->isChecked()); screen->setShowAxes(viewActionShowAxes->isChecked()); screen->updateGL(); } void MainWindow::viewModeShowCrosshairs() { + QSettings settings; + settings.setValue("view/showCrosshairs",viewActionShowCrosshairs->isChecked()); screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); screen->updateGL(); } @@ -1717,6 +1820,8 @@ void MainWindow::viewCenter() void MainWindow::viewPerspective() { + QSettings settings; + settings.setValue("view/orthogonalProjection",false); viewActionPerspective->setChecked(true); viewActionOrthogonal->setChecked(false); screen->setOrthoMode(false); @@ -1725,6 +1830,8 @@ void MainWindow::viewPerspective() void MainWindow::viewOrthogonal() { + QSettings settings; + settings.setValue("view/orthogonalProjection",true); viewActionPerspective->setChecked(false); viewActionOrthogonal->setChecked(true); screen->setOrthoMode(true); @@ -1733,10 +1840,13 @@ void MainWindow::viewOrthogonal() void MainWindow::hideConsole() { + QSettings settings; if (viewActionHide->isChecked()) { console->hide(); + settings.setValue("view/hideConsole",true); } else { console->show(); + settings.setValue("view/hideConsole",false); } } @@ -1803,6 +1913,11 @@ MainWindow::maybeSave() void MainWindow::closeEvent(QCloseEvent *event) { if (maybeSave()) { + QSettings settings; + settings.setValue("window/size", size()); + settings.setValue("window/position", pos()); + settings_setValueList("window/splitter1sizes",splitter1->sizes()); + settings_setValueList("window/splitter2sizes",splitter2->sizes()); event->accept(); } else { event->ignore(); diff --git a/src/openscad.cc b/src/openscad.cc index bc1d845..bf22246 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -140,19 +140,25 @@ int main(int argc, char **argv) desc.add_options() ("help,h", "help message") ("version,v", "print the version") - ("s", po::value<string>(), "stl-file") - ("o", po::value<string>(), "off-file") - ("x", po::value<string>(), "dxf-file") - ("d", po::value<string>(), "deps-file") - ("m", po::value<string>(), "make file") - ("D", po::value<vector<string> >(), "var=val") - ; + ("s,s", po::value<string>(), "stl-file") + ("o,o", po::value<string>(), "off-file") + ("x,x", po::value<string>(), "dxf-file") + ("d,d", po::value<string>(), "deps-file") + ("m,m", po::value<string>(), "makefile") + ("D,D", po::value<vector<string> >(), "var=val"); + + po::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", po::value< vector<string> >(), "input file"); po::positional_options_description p; p.add("input-file", -1); + po::options_description all_options; + all_options.add(desc).add(hidden); + po::variables_map vm; - po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); + po::store(po::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm); // po::notify(vm); if (vm.count("help")) help(argv[0]); @@ -348,7 +354,7 @@ int main(int argc, char **argv) new MainWindow(qfilename); vector<string> inputFiles; if (vm.count("input-file")) { - inputFiles = vm["input-files"].as<vector<string> >(); + inputFiles = vm["input-file"].as<vector<string> >(); for (vector<string>::const_iterator i = inputFiles.begin()+1; i != inputFiles.end(); i++) { new MainWindow(QFileInfo(original_path, i->c_str()).absoluteFilePath()); } diff --git a/src/primitives.cc b/src/primitives.cc index 5ba32fe..5180c16 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -34,6 +34,8 @@ #include "printutils.h" #include <assert.h> +#define F_MINIMUM 0.01 + enum primitive_type_e { CUBE, SPHERE, @@ -57,7 +59,6 @@ class PrimitiveNode : public AbstractPolyNode public: bool center; double x, y, z, h, r1, r2; - static const double F_MINIMUM = 0.01; double fn, fs, fa; primitive_type_e type; int convexity; @@ -106,13 +107,13 @@ AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstanti node->fs = c.lookup_variable("$fs").num; node->fa = c.lookup_variable("$fa").num; - if (node->fs < PrimitiveNode::F_MINIMUM) { - PRINTF("WARNING: $fs too small - clamping to %f", PrimitiveNode::F_MINIMUM); - node->fs = PrimitiveNode::F_MINIMUM; + if (node->fs < F_MINIMUM) { + PRINTF("WARNING: $fs too small - clamping to %f", F_MINIMUM); + node->fs = F_MINIMUM; } - if (node->fa < PrimitiveNode::F_MINIMUM) { - PRINTF("WARNING: $fa too small - clamping to %f", PrimitiveNode::F_MINIMUM); - node->fa = PrimitiveNode::F_MINIMUM; + if (node->fa < F_MINIMUM) { + PRINTF("WARNING: $fa too small - clamping to %f", F_MINIMUM); + node->fa = F_MINIMUM; } @@ -219,6 +220,19 @@ int get_fragments_from_r(double r, double fn, double fs, double fa) return (int)ceil(fmax(fmin(360.0 / fa, r*M_PI / fs), 5)); } +struct point2d { + double x, y; +}; + +static void generate_circle(point2d *circle, double r, int fragments) +{ + for (int i=0; i<fragments; i++) { + double phi = (M_PI*2*i) / fragments; + circle[i].x = r*cos(phi); + circle[i].y = r*sin(phi); + } +} + PolySet *PrimitiveNode::render_polyset(render_mode_e) const { PolySet *p = new PolySet(); @@ -279,70 +293,66 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const if (type == SPHERE && r1 > 0) { - struct point2d { - double x, y; - }; - struct ring_s { - int fragments; point2d *points; - double r, z; + double z; }; - int rings = get_fragments_from_r(r1, fn, fs, fa); + int fragments = get_fragments_from_r(r1, fn, fs, fa); + int rings = fragments/2; +// Uncomment the following three lines to enable experimental sphere tesselation +// if (rings % 2 == 0) rings++; // To ensure that the middle ring is at phi == 0 degrees + ring_s *ring = new ring_s[rings]; +// double offset = 0.5 * ((fragments / 2) % 2); for (int i = 0; i < rings; i++) { +// double phi = (M_PI * (i + offset)) / (fragments/2); double phi = (M_PI * (i + 0.5)) / rings; - ring[i].r = r1 * sin(phi); + double r = r1 * sin(phi); ring[i].z = r1 * cos(phi); - ring[i].fragments = get_fragments_from_r(ring[i].r, fn, fs, fa); - ring[i].points = new point2d[ring[i].fragments]; - for (int j = 0; j < ring[i].fragments; j++) { - phi = (M_PI*2*j) / ring[i].fragments; - ring[i].points[j].x = ring[i].r * cos(phi); - ring[i].points[j].y = ring[i].r * sin(phi); - } + ring[i].points = new point2d[fragments]; + generate_circle(ring[i].points, r, fragments); } p->append_poly(); - for (int i = 0; i < ring[0].fragments; i++) + for (int i = 0; i < fragments; i++) p->append_vertex(ring[0].points[i].x, ring[0].points[i].y, ring[0].z); for (int i = 0; i < rings-1; i++) { ring_s *r1 = &ring[i]; ring_s *r2 = &ring[i+1]; int r1i = 0, r2i = 0; - while (r1i < r1->fragments || r2i < r2->fragments) + while (r1i < fragments || r2i < fragments) { - if (r1i >= r1->fragments) + if (r1i >= fragments) goto sphere_next_r2; - if (r2i >= r2->fragments) + if (r2i >= fragments) goto sphere_next_r1; - if ((double)r1i / r1->fragments < - (double)r2i / r2->fragments) + if ((double)r1i / fragments < + (double)r2i / fragments) { sphere_next_r1: p->append_poly(); - int r1j = (r1i+1) % r1->fragments; + int r1j = (r1i+1) % fragments; p->insert_vertex(r1->points[r1i].x, r1->points[r1i].y, r1->z); p->insert_vertex(r1->points[r1j].x, r1->points[r1j].y, r1->z); - p->insert_vertex(r2->points[r2i % r2->fragments].x, r2->points[r2i % r2->fragments].y, r2->z); + p->insert_vertex(r2->points[r2i % fragments].x, r2->points[r2i % fragments].y, r2->z); r1i++; } else { sphere_next_r2: p->append_poly(); - int r2j = (r2i+1) % r2->fragments; + int r2j = (r2i+1) % fragments; p->append_vertex(r2->points[r2i].x, r2->points[r2i].y, r2->z); p->append_vertex(r2->points[r2j].x, r2->points[r2j].y, r2->z); - p->append_vertex(r1->points[r1i % r1->fragments].x, r1->points[r1i % r1->fragments].y, r1->z); + p->append_vertex(r1->points[r1i % fragments].x, r1->points[r1i % fragments].y, r1->z); r2i++; } } } p->append_poly(); - for (int i = 0; i < ring[rings-1].fragments; i++) + for (int i = 0; i < fragments; i++) p->insert_vertex(ring[rings-1].points[i].x, ring[rings-1].points[i].y, ring[rings-1].z); delete[] ring; @@ -361,44 +371,33 @@ sphere_next_r2: z2 = h; } - struct point2d { - double x, y; - }; - point2d *circle1 = new point2d[fragments]; point2d *circle2 = new point2d[fragments]; - for (int i=0; i<fragments; i++) { - double phi = (M_PI*2*i) / fragments; - if (r1 > 0) { - circle1[i].x = r1*cos(phi); - circle1[i].y = r1*sin(phi); - } else { - circle1[i].x = 0; - circle1[i].y = 0; - } - if (r2 > 0) { - circle2[i].x = r2*cos(phi); - circle2[i].y = r2*sin(phi); - } else { - circle2[i].x = 0; - circle2[i].y = 0; - } - } + generate_circle(circle1, r1, fragments); + generate_circle(circle2, r2, fragments); for (int i=0; i<fragments; i++) { int j = (i+1) % fragments; - if (r1 > 0) { + if (r1 == r2) { p->append_poly(); p->insert_vertex(circle1[i].x, circle1[i].y, z1); p->insert_vertex(circle2[i].x, circle2[i].y, z2); - p->insert_vertex(circle1[j].x, circle1[j].y, z1); - } - if (r2 > 0) { - p->append_poly(); - p->insert_vertex(circle2[i].x, circle2[i].y, z2); p->insert_vertex(circle2[j].x, circle2[j].y, z2); p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } else { + if (r1 > 0) { + p->append_poly(); + p->insert_vertex(circle1[i].x, circle1[i].y, z1); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } + if (r2 > 0) { + p->append_poly(); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle2[j].x, circle2[j].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } } } diff --git a/src/transform.cc b/src/transform.cc index e841ef0..7b15a7e 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -91,7 +91,7 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti argnames = QVector<QString>() << "m"; } if (type == COLOR) { - argnames = QVector<QString>() << "c"; + argnames = QVector<QString>() << "c" << "alpha"; } Context c(ctx); @@ -227,6 +227,24 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti if (v.type == Value::VECTOR) { for (int i = 0; i < 4; i++) node->m[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0; + } else if (v.type == Value::STRING) { + QString colorname = v.text; + QColor color; + color.setNamedColor(colorname); + if (color.isValid()) { + node->m[16+0] = color.redF(); + node->m[16+1] = color.greenF(); + node->m[16+2] = color.blueF(); + } else { + PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data()); + PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); + } + } + Value alpha = c.lookup_variable("alpha"); + if (alpha.type == Value::NUMBER) { + node->m[16+3] = alpha.num; + } else { + node->m[16+3] = 1.0; } } diff --git a/testdata/scad/convex_hull.scad b/testdata/scad/convex_hull.scad new file mode 100644 index 0000000..3114ac5 --- /dev/null +++ b/testdata/scad/convex_hull.scad @@ -0,0 +1,43 @@ +// Works correctly +module convex2dSimple() { + hull() { + translate([15,10]) circle(10); + circle(10); + } +} + +// Works correctly +module convex2dHole() { + hull() { + translate([15,10,0]) circle(10); + difference() { + circle(10); + circle(5); + } + } +} + +// 3d not currently implemented +module convex3dSimple() { + hull() { + translate([15,10]) cylinder(r=10); + cylinder(r=10); + } +} + +// 3d not currently implemented +module convex3dHole() { + hull() { + translate([15,10,0]) cylinder(10); + difference() { + cylinder(10); + cylinder(5); + } + } +} + + +convex2dHole(); +translate([40,0,0]) convex2dSimple(); +translate([0,40,0]) convex3dHole(); +translate([40,40,0]) convex3dSimple(); diff --git a/testdata/scad/minkowski.scad b/testdata/scad/minkowski.scad index 26cd972..6d0dade 100644 --- a/testdata/scad/minkowski.scad +++ b/testdata/scad/minkowski.scad @@ -1 +1,67 @@ -minkowski(); + +// Rounded box using 3d minkowski +module roundedBox3dSimple() { + minkowski() { + cube([10,10,5]); + cylinder(r=5, h=5); + } +} + +// Currently segfaults +module roundedBox3dCut() { + minkowski() { + difference() { + cube([10,10,5]); + cube([5,5,5]); + } + cylinder(r=5, h=5); + } +} + +// Currently segfaults +module roundedBox3dHole() { + minkowski() { + difference() { + cube([10,10,5]); + translate([2,2,-2]) cube([6,6,10]); + } + cylinder(r=2); + } +} + +// Works correctly +module roundedBox2dSimple() { + minkowski() { + square([10,10]); + circle(r=5); + } +} + +// Works correctly +module roundedBox2dCut() { + minkowski() { + difference() { + square([10,10]); + square([5,5]); + } + circle(r=5); + } +} + +// Not quite correct, result does not contain a hole, since the impl currently returns the outer boundary of the polygon_with_holes. +module roundedBox2dHole() { + minkowski() { + difference() { + square([10,10]); + translate([2,2]) square([6,6]); + } + circle(r=2); + } +} + +translate([-25,0,0]) roundedBox2dHole(); +translate([0,0,0]) roundedBox2dCut(); +translate([25,0,0]) roundedBox2dSimple(); +translate([-25,25,0]) roundedBox3dHole(); +translate([0,25,0]) roundedBox3dCut(); +translate([25,25,0]) roundedBox3dSimple(); diff --git a/testdata/scad/non-aff-matrix.scad b/testdata/scad/non-aff-matrix.scad new file mode 100644 index 0000000..7dbced1 --- /dev/null +++ b/testdata/scad/non-aff-matrix.scad @@ -0,0 +1,6 @@ +multmatrix(m = [[1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, -0.02, 1]]) + linear_extrude(height=20) circle(r=10); + diff --git a/testdata/scad/testcolornames.scad b/testdata/scad/testcolornames.scad new file mode 100644 index 0000000..b9ad334 --- /dev/null +++ b/testdata/scad/testcolornames.scad @@ -0,0 +1,159 @@ +/* color samples for SVG named colors, for OpenSCAD +Please see http://en.wikipedia.org/wiki/Web_colors +and http://www.w3.org/TR/SVG/types.html#ColorKeywords +for more information. */ + +$fn=5; +radius=0.8; +//translate([0,0]) color("Red colors") sphere(radius); +translate([1,0]) color("IndianRed") sphere(radius); +translate([2,0]) color("LightCoral") sphere(radius); +translate([3,0]) color("Salmon") sphere(radius); +translate([4,0]) color("DarkSalmon") sphere(radius); +translate([5,0]) color("LightSalmon") sphere(radius); +translate([6,0]) color("Red") sphere(radius); +translate([7,0]) color("Crimson") sphere(radius); +translate([8,0]) color("FireBrick") sphere(radius); +translate([9,0]) color("DarkRed") sphere(radius); +//translate([10,0]) color("Pink colors") sphere(radius); +translate([0,1]) color("Pink") sphere(radius); +translate([1,1]) color("LightPink") sphere(radius); +translate([2,1]) color("HotPink") sphere(radius); +translate([3,1]) color("DeepPink") sphere(radius); +translate([4,1]) color("MediumVioletRed") sphere(radius); +translate([5,1]) color("PaleVioletRed") sphere(radius); +//translate([6,1]) color("Orange colors") sphere(radius); +translate([7,1]) color("LightSalmon") sphere(radius); +translate([8,1]) color("Coral") sphere(radius); +translate([9,1]) color("Tomato") sphere(radius); +translate([10,1]) color("OrangeRed") sphere(radius); +translate([0,2]) color("DarkOrange") sphere(radius); +translate([1,2]) color("Orange") sphere(radius); +//translate([2,2]) color("Yellow colors") sphere(radius); +translate([3,2]) color("Gold") sphere(radius); +translate([4,2]) color("Yellow") sphere(radius); +translate([5,2]) color("LightYellow") sphere(radius); +translate([6,2]) color("LemonChiffon") sphere(radius); +translate([7,2]) color("LightGoldenrodYellow") sphere(radius); +translate([8,2]) color("PapayaWhip") sphere(radius); +translate([9,2]) color("Moccasin") sphere(radius); +translate([10,2]) color("PeachPuff") sphere(radius); +translate([0,3]) color("PaleGoldenrod") sphere(radius); +translate([1,3]) color("Khaki") sphere(radius); +translate([2,3]) color("DarkKhaki") sphere(radius); +//translate([3,3]) color("Purple colors") sphere(radius); +translate([4,3]) color("Lavender") sphere(radius); +translate([5,3]) color("Thistle") sphere(radius); +translate([6,3]) color("Plum") sphere(radius); +translate([7,3]) color("Violet") sphere(radius); +translate([8,3]) color("Orchid") sphere(radius); +translate([9,3]) color("Fuchsia") sphere(radius); +translate([10,3]) color("Magenta") sphere(radius); +translate([0,4]) color("MediumOrchid") sphere(radius); +translate([1,4]) color("MediumPurple") sphere(radius); +translate([2,4]) color("BlueViolet") sphere(radius); +translate([3,4]) color("DarkViolet") sphere(radius); +translate([4,4]) color("DarkOrchid") sphere(radius); +translate([5,4]) color("DarkMagenta") sphere(radius); +translate([6,4]) color("Purple") sphere(radius); +translate([7,4]) color("Indigo") sphere(radius); +translate([8,4]) color("DarkSlateBlue") sphere(radius); +translate([9,4]) color("SlateBlue") sphere(radius); +translate([10,4]) color("MediumSlateBlue") sphere(radius); +//translate([0,5]) color("Green colors") sphere(radius); +translate([1,5]) color("GreenYellow") sphere(radius); +translate([2,5]) color("Chartreuse") sphere(radius); +translate([3,5]) color("LawnGreen") sphere(radius); +translate([4,5]) color("Lime") sphere(radius); +translate([5,5]) color("LimeGreen") sphere(radius); +translate([6,5]) color("PaleGreen") sphere(radius); +translate([7,5]) color("LightGreen") sphere(radius); +translate([8,5]) color("MediumSpringGreen") sphere(radius); +translate([9,5]) color("SpringGreen") sphere(radius); +translate([10,5]) color("MediumSeaGreen") sphere(radius); +translate([0,6]) color("SeaGreen") sphere(radius); +translate([1,6]) color("ForestGreen") sphere(radius); +translate([2,6]) color("Green") sphere(radius); +translate([3,6]) color("DarkGreen") sphere(radius); +translate([4,6]) color("YellowGreen") sphere(radius); +translate([5,6]) color("OliveDrab") sphere(radius); +translate([6,6]) color("Olive") sphere(radius); +translate([7,6]) color("DarkOliveGreen") sphere(radius); +translate([8,6]) color("MediumAquamarine") sphere(radius); +translate([9,6]) color("DarkSeaGreen") sphere(radius); +translate([10,6]) color("LightSeaGreen") sphere(radius); +translate([0,7]) color("DarkCyan") sphere(radius); +translate([1,7]) color("Teal") sphere(radius); +//translate([2,7]) color("Blue/Cyan colors") sphere(radius); +translate([3,7]) color("Aqua") sphere(radius); +translate([4,7]) color("Cyan") sphere(radius); +translate([5,7]) color("LightCyan") sphere(radius); +translate([6,7]) color("PaleTurquoise") sphere(radius); +translate([7,7]) color("Aquamarine") sphere(radius); +translate([8,7]) color("Turquoise") sphere(radius); +translate([9,7]) color("MediumTurquoise") sphere(radius); +translate([10,7]) color("DarkTurquoise") sphere(radius); +translate([0,8]) color("CadetBlue") sphere(radius); +translate([1,8]) color("SteelBlue") sphere(radius); +translate([2,8]) color("LightSteelBlue") sphere(radius); +translate([3,8]) color("PowderBlue") sphere(radius); +translate([4,8]) color("LightBlue") sphere(radius); +translate([5,8]) color("SkyBlue") sphere(radius); +translate([6,8]) color("LightSkyBlue") sphere(radius); +translate([7,8]) color("DeepSkyBlue") sphere(radius); +translate([8,8]) color("DodgerBlue") sphere(radius); +translate([9,8]) color("CornflowerBlue") sphere(radius); +translate([10,8]) color("RoyalBlue") sphere(radius); +translate([0,9]) color("Blue") sphere(radius); +translate([1,9]) color("MediumBlue") sphere(radius); +translate([2,9]) color("DarkBlue") sphere(radius); +translate([3,9]) color("Navy") sphere(radius); +translate([4,9]) color("MidnightBlue") sphere(radius); +//translate([5,9]) color("Brown colors") sphere(radius); +translate([6,9]) color("Cornsilk") sphere(radius); +translate([7,9]) color("BlanchedAlmond") sphere(radius); +translate([8,9]) color("Bisque") sphere(radius); +translate([9,9]) color("NavajoWhite") sphere(radius); +translate([10,9]) color("Wheat") sphere(radius); +translate([0,10]) color("BurlyWood") sphere(radius); +translate([1,10]) color("Tan") sphere(radius); +translate([2,10]) color("RosyBrown") sphere(radius); +translate([3,10]) color("SandyBrown") sphere(radius); +translate([4,10]) color("Goldenrod") sphere(radius); +translate([5,10]) color("DarkGoldenrod") sphere(radius); +translate([6,10]) color("Peru") sphere(radius); +translate([7,10]) color("Chocolate") sphere(radius); +translate([8,10]) color("SaddleBrown") sphere(radius); +translate([9,10]) color("Sienna") sphere(radius); +translate([10,10]) color("Brown") sphere(radius); +translate([0,11]) color("Maroon") sphere(radius); +//translate([1,11]) color("White colors") sphere(radius); +translate([2,11]) color("White") sphere(radius); +translate([3,11]) color("Snow") sphere(radius); +translate([4,11]) color("Honeydew") sphere(radius); +translate([5,11]) color("MintCream") sphere(radius); +translate([6,11]) color("Azure") sphere(radius); +translate([7,11]) color("AliceBlue") sphere(radius); +translate([8,11]) color("GhostWhite") sphere(radius); +translate([9,11]) color("WhiteSmoke") sphere(radius); +translate([10,11]) color("Seashell") sphere(radius); +translate([0,12]) color("Beige") sphere(radius); +translate([1,12]) color("OldLace") sphere(radius); +translate([2,12]) color("FloralWhite") sphere(radius); +translate([3,12]) color("Ivory") sphere(radius); +translate([4,12]) color("AntiqueWhite") sphere(radius); +translate([5,12]) color("Linen") sphere(radius); +translate([6,12]) color("LavenderBlush") sphere(radius); +translate([7,12]) color("MistyRose") sphere(radius); +//translate([8,12]) color("Gray colors") sphere(radius); +translate([9,12]) color("Gainsboro") sphere(radius); +translate([10,12]) color("LightGrey") sphere(radius); +translate([0,13]) color("Silver") sphere(radius); +translate([1,13]) color("DarkGray") sphere(radius); +translate([2,13]) color("Gray") sphere(radius); +translate([3,13]) color("DimGray") sphere(radius); +translate([4,13]) color("LightSlateGray") sphere(radius); +translate([5,13]) color("SlateGray") sphere(radius); +translate([6,13]) color("DarkSlateGray") sphere(radius); +translate([7,13]) color("Black") sphere(radius); + |