diff options
Diffstat (limited to 'src')
-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 |
15 files changed, 478 insertions, 196 deletions
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; } } |