diff --git a/.gitignore b/.gitignore index dee510fe..767c89c4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,11 @@ parser_yacc.h /tmp /OpenSCAD.app */#*# +/locale/*/*/*.mo +/locale/POTFILES /nbproject +/openscad.pro.user /openscad -/mingw32 -/mingw64 /tests/openscad_nogui testdata/scad/features/import_dxf-tests.scad testdata/scad/features/import_stl-tests.scad @@ -25,4 +26,4 @@ testdata/scad/misc/use-tests.scad /mingw32 /mingw64 **/project.xcworkspace -**/xcuserdata \ No newline at end of file +**/xcuserdata diff --git a/README.md b/README.md index 8205b29a..97d6c9e7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![Travis CI](https://api.travis-ci.org/openscad/openscad.png)](https://travis-ci.org/openscad/openscad) [![Coverity Status](https://scan.coverity.com/projects/2510/badge.svg)](https://scan.coverity.com/projects/2510) +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/openscad/openscad/trend.png)](https://bitdeli.com/free "Bitdeli Badge") # What is OpenSCAD? [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=openscad&url=http://openscad.org&title=OpenSCAD&language=&tags=github&category=software) @@ -212,15 +213,16 @@ complete, build OpenSCAD and package it to an installer: If you wish you can only build the openscad.exe binary: cd mingw32 - qmake .. CONFIG+=mingw-cross-env + qmake ../openscad.pro CONFIG+=mingw-cross-env make For a 64-bit Windows cross-build, replace 32 with 64 in the above instructions. ### Compilation -First, run 'qmake' from Qt4 to generate a Makefile. On some systems you need to -run 'qmake4', 'qmake-qt4' or something alike to run the qt4 version of the tool. +First, run 'qmake openscad.pro' from Qt4 to generate a Makefile. On some systems +you need to run 'qmake4', 'qmake-qt4' or something alike to run the qt4 version +of the tool. Then run make. Finally you might run 'make install' as root or simply copy the 'openscad' binary (OpenSCAD.app on Mac OS X) to the bin directory of your choice. diff --git a/boost.pri b/boost.pri index daa6b1df..46fb7ecc 100644 --- a/boost.pri +++ b/boost.pri @@ -11,15 +11,21 @@ boost { # See https://svn.boost.org/trac/boost/ticket/6219 macx: DEFINES += __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0 + + # MXE cross build CONFIG(mingw-cross-env) { DEFINES += BOOST_STATIC DEFINES += BOOST_THREAD_USE_LIB DEFINES += Boost_USE_STATIC_LIBS BOOST_LINK_FLAGS = -lboost_thread_win32-mt -lboost_program_options-mt -lboost_filesystem-mt -lboost_system-mt -lboost_regex-mt -lboost_chrono-mt - } + } - isEmpty(BOOST_LINK_FLAGS):win* { - BOOST_LINK_FLAGS = -llibboost_thread-vc90-mt-s-1_46_1 -llibboost_program_options-vc90-mt-s-1_46_1 -llibboost_filesystem-vc90-mt-s-1_46_1 -llibboost_system-vc90-mt-s-1_46_1 -llibboost_regex-vc90-mt-s-1_46_1 + # MSYS2 + isEmpty(BOOST_LINK_FLAGS):win32-g++ { + DEFINES += BOOST_STATIC + DEFINES += BOOST_THREAD_USE_LIB + DEFINES += Boost_USE_STATIC_LIBS + BOOST_LINK_FLAGS = -lboost_thread-mt -lboost_program_options-mt -lboost_filesystem-mt -lboost_system-mt -lboost_regex-mt } # check for OPENSCAD_LIBDIR + multithread diff --git a/cgal.pri b/cgal.pri index e61b6def..355cf010 100644 --- a/cgal.pri +++ b/cgal.pri @@ -6,7 +6,6 @@ cgal { CGAL_DIR = $$(CGALDIR) !isEmpty(CGAL_DIR) { QMAKE_INCDIR += $$CGAL_DIR/include - win*: QMAKE_INCDIR += $$CGAL_DIR/auxiliary/gmp/include QMAKE_LIBDIR += $$CGAL_DIR/lib message("CGAL location: $$CGAL_DIR") } @@ -19,14 +18,13 @@ cgal { *-g++* { QMAKE_CXXFLAGS += -frounding-math } - LIBS += $$CGAL_DIR/auxiliary/gmp/lib/libmpfr-4.lib -lCGAL-vc110-mt-gd } else { - LIBS += -lgmp -lmpfr -lCGAL QMAKE_CXXFLAGS += -frounding-math } + LIBS += -lCGAL -lmpfr -lgmp } - *clang* { - QMAKE_CXXFLAGS -= -frounding-math - } + *clang* { + QMAKE_CXXFLAGS -= -frounding-math + } } diff --git a/cgal/README.md b/cgal/README.md new file mode 100644 index 00000000..6b213744 --- /dev/null +++ b/cgal/README.md @@ -0,0 +1,6 @@ +This folder contains some CGAL test programs to easier develop and debug CGAL-based components. + +## polyhole-tessellator + +Tessellate an almost planar 3D polygon with holes into a vector of double precision 3D triangles. + diff --git a/cgal/data/cubes.stl b/cgal/data/cubes.stl new file mode 100644 index 00000000..be040add --- /dev/null +++ b/cgal/data/cubes.stl @@ -0,0 +1,198 @@ +solid OpenSCAD_Model + facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 0 0 + vertex 0 0 10 + endloop + endfacet + facet normal -1 -0 -0 + outer loop + vertex 0 10 10 + vertex 0 10 0 + vertex 0 0 10 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0 0 10 + vertex 0 0 0 + vertex 10 0 0 + endloop + endfacet + facet normal -0 -1 -0 + outer loop + vertex 10 0 10 + vertex 0 0 10 + vertex 10 0 0 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 5 10 0 + vertex 0 0 0 + vertex 0 10 0 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 5 15 0 + vertex 15 15 0 + vertex 5 10 0 + endloop + endfacet + facet normal 0 -0 -1 + outer loop + vertex 5 10 0 + vertex 10 5 0 + vertex 0 0 0 + endloop + endfacet + facet normal 0 -0 -1 + outer loop + vertex 15 15 0 + vertex 15 5 0 + vertex 10 5 0 + endloop + endfacet + facet normal 0 -0 -1 + outer loop + vertex 10 5 0 + vertex 10 0 0 + vertex 0 0 0 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 15 15 0 + vertex 10 5 0 + vertex 5 10 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 5 10 0 + vertex 0 10 0 + vertex 0 10 10 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 5 10 10 + vertex 5 10 0 + vertex 0 10 10 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 10 5 10 + vertex 0 0 10 + vertex 10 0 10 + endloop + endfacet + facet normal 0 -0 1 + outer loop + vertex 15 5 10 + vertex 15 15 10 + vertex 10 5 10 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 10 5 10 + vertex 5 10 10 + vertex 0 0 10 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 15 15 10 + vertex 5 15 10 + vertex 5 10 10 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 5 10 10 + vertex 0 10 10 + vertex 0 0 10 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 15 15 10 + vertex 5 10 10 + vertex 10 5 10 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 10 0 10 + vertex 10 0 0 + vertex 10 5 0 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 10 5 10 + vertex 10 0 10 + vertex 10 5 0 + endloop + endfacet + facet normal -1 0 0 + outer loop + vertex 5 15 0 + vertex 5 10 0 + vertex 5 10 10 + endloop + endfacet + facet normal -1 -0 -0 + outer loop + vertex 5 15 10 + vertex 5 15 0 + vertex 5 10 10 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 10 5 10 + vertex 10 5 0 + vertex 15 5 0 + endloop + endfacet + facet normal -0 -1 -0 + outer loop + vertex 15 5 10 + vertex 10 5 10 + vertex 15 5 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 15 15 0 + vertex 5 15 0 + vertex 5 15 10 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 15 15 10 + vertex 15 15 0 + vertex 5 15 10 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 15 5 10 + vertex 15 5 0 + vertex 15 15 0 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 15 15 10 + vertex 15 5 10 + vertex 15 15 0 + endloop + endfacet +endsolid OpenSCAD_Model diff --git a/cgal/data/issue1105.polygon b/cgal/data/issue1105.polygon new file mode 100644 index 00000000..15809c1f --- /dev/null +++ b/cgal/data/issue1105.polygon @@ -0,0 +1,4 @@ +6, -25, 29.285714285714285 +6, -26.732050855686023, 29.020513307787397 +6, -26.732050855686026, 29.020513307787397 +6, -26, 29.132600433972414 diff --git a/cgal/data/quads.polygon b/cgal/data/quads.polygon new file mode 100644 index 00000000..a21c6ddb --- /dev/null +++ b/cgal/data/quads.polygon @@ -0,0 +1,10 @@ +0,0,0 +2,0,0 +2,2,0 +0,2,0 + +0.5,0.5,0 +1.5,0.5,0 +1.5,1.5,0 +0.5,1.5,0 + diff --git a/cgal/data/tri.polygon b/cgal/data/tri.polygon new file mode 100644 index 00000000..79d21286 --- /dev/null +++ b/cgal/data/tri.polygon @@ -0,0 +1,3 @@ +0,0,0 +2,0,0 +2,2,0 diff --git a/cgal/decompose.cpp b/cgal/decompose.cpp new file mode 100644 index 00000000..8435814b --- /dev/null +++ b/cgal/decompose.cpp @@ -0,0 +1,667 @@ +#include +#include +#include +#include +#include + +#include "cgalutils.h" +#include "export.h" +#include "polyset.h" +#include "CGAL_Nef_polyhedron.h" + +using namespace CGALUtils; + +// Nef polyhedron are using CGAL_Kernel3 (Cartesian) +// Triangulation will use Epick +typedef CGAL::Epick K; +typedef CGAL::Polyhedron_3 PolyhedronK; + +#include +#include +using namespace boost::assign; // bring 'operator+=()' into scope +std::vector colors = boost::assign::list_of + (Color4f(240, 248, 255)) + (Color4f(250, 235, 215)) + (Color4f(0, 255, 255)) + (Color4f(127, 255, 212)) + (Color4f(240, 255, 255)) + (Color4f(245, 245, 220)) + (Color4f(255, 228, 196)) + (Color4f(0, 0, 0)) + (Color4f(255, 235, 205)) + (Color4f(0, 0, 255)) + (Color4f(138, 43, 226)) + (Color4f(165, 42, 42)) + (Color4f(222, 184, 135)) + (Color4f(95, 158, 160)) + (Color4f(127, 255, 0)) + (Color4f(210, 105, 30)) + (Color4f(255, 127, 80)) + (Color4f(100, 149, 237)) + (Color4f(255, 248, 220)) + (Color4f(220, 20, 60)) + (Color4f(0, 255, 255)) + (Color4f(0, 0, 139)) + (Color4f(0, 139, 139)) + (Color4f(184, 134, 11)) + (Color4f(169, 169, 169)) + (Color4f(0, 100, 0)) + (Color4f(169, 169, 169)) + (Color4f(189, 183, 107)) + (Color4f(139, 0, 139)) + (Color4f(85, 107, 47)) + (Color4f(255, 140, 0)) + (Color4f(153, 50, 204)) + (Color4f(139, 0, 0)) + (Color4f(233, 150, 122)) + (Color4f(143, 188, 143)) + (Color4f(72, 61, 139)) + (Color4f(47, 79, 79)) + (Color4f(47, 79, 79)) + (Color4f(0, 206, 209)) + (Color4f(148, 0, 211)) + (Color4f(255, 20, 147)) + (Color4f(0, 191, 255)) + (Color4f(105, 105, 105)) + (Color4f(105, 105, 105)) + (Color4f(30, 144, 255)) + (Color4f(178, 34, 34)) + (Color4f(255, 250, 240)) + (Color4f(34, 139, 34)) + (Color4f(255, 0, 255)) + (Color4f(220, 220, 220)) + (Color4f(248, 248, 255)) + (Color4f(255, 215, 0)) + (Color4f(218, 165, 32)) + (Color4f(128, 128, 128)) + (Color4f(0, 128, 0)) + (Color4f(173, 255, 47)) + (Color4f(128, 128, 128)) + (Color4f(240, 255, 240)) + (Color4f(255, 105, 180)) + (Color4f(205, 92, 92)) + (Color4f(75, 0, 130)) + (Color4f(255, 255, 240)) + (Color4f(240, 230, 140)) + (Color4f(230, 230, 250)) + (Color4f(255, 240, 245)) + (Color4f(124, 252, 0)) + (Color4f(255, 250, 205)) + (Color4f(173, 216, 230)) + (Color4f(240, 128, 128)) + (Color4f(224, 255, 255)) + (Color4f(250, 250, 210)) + (Color4f(211, 211, 211)) + (Color4f(144, 238, 144)) + (Color4f(211, 211, 211)) + (Color4f(255, 182, 193)) + (Color4f(255, 160, 122)) + (Color4f(32, 178, 170)) + (Color4f(135, 206, 250)) + (Color4f(119, 136, 153)) + (Color4f(119, 136, 153)) + (Color4f(176, 196, 222)) + (Color4f(255, 255, 224)) + (Color4f(0, 255, 0)) + (Color4f(50, 205, 50)) + (Color4f(250, 240, 230)) + (Color4f(255, 0, 255)) + (Color4f(128, 0, 0)) + (Color4f(102, 205, 170)) + (Color4f(0, 0, 205)) + (Color4f(186, 85, 211)) + (Color4f(147, 112, 219)) + (Color4f(60, 179, 113)) + (Color4f(123, 104, 238)) + (Color4f(0, 250, 154)) + (Color4f(72, 209, 204)) + (Color4f(199, 21, 133)) + (Color4f(25, 25, 112)) + (Color4f(245, 255, 250)) + (Color4f(255, 228, 225)) + (Color4f(255, 228, 181)) + (Color4f(255, 222, 173)) + (Color4f(0, 0, 128)) + (Color4f(253, 245, 230)) + (Color4f(128, 128, 0)) + (Color4f(107, 142, 35)) + (Color4f(255, 165, 0)) + (Color4f(255, 69, 0)) + (Color4f(218, 112, 214)) + (Color4f(238, 232, 170)) + (Color4f(152, 251, 152)) + (Color4f(175, 238, 238)) + (Color4f(219, 112, 147)) + (Color4f(255, 239, 213)) + (Color4f(255, 218, 185)) + (Color4f(205, 133, 63)) + (Color4f(255, 192, 203)) + (Color4f(221, 160, 221)) + (Color4f(176, 224, 230)) + (Color4f(128, 0, 128)) + (Color4f(255, 0, 0)) + (Color4f(188, 143, 143)) + (Color4f(65, 105, 225)) + (Color4f(139, 69, 19)) + (Color4f(250, 128, 114)) + (Color4f(244, 164, 96)) + (Color4f(46, 139, 87)) + (Color4f(255, 245, 238)) + (Color4f(160, 82, 45)) + (Color4f(192, 192, 192)) + (Color4f(135, 206, 235)) + (Color4f(106, 90, 205)) + (Color4f(112, 128, 144)) + (Color4f(112, 128, 144)) + (Color4f(255, 250, 250)) + (Color4f(0, 255, 127)) + (Color4f(70, 130, 180)) + (Color4f(210, 180, 140)) + (Color4f(0, 128, 128)) + (Color4f(216, 191, 216)) + (Color4f(255, 99, 71)) + (Color4f(0, 0, 0, 0)) + (Color4f(64, 224, 208)) + (Color4f(238, 130, 238)) + (Color4f(245, 222, 179)) + (Color4f(255, 255, 255)) + (Color4f(245, 245, 245)) + (Color4f(255, 255, 0)) + (Color4f(154, 205, 50)); + +#include +#include +template +bool is_weakly_convex(Polyhedron const& p) { + for (typename Polyhedron::Edge_const_iterator i = p.edges_begin(); i != p.edges_end(); ++i) { + typename Polyhedron::Plane_3 p(i->opposite()->vertex()->point(), i->vertex()->point(), i->next()->vertex()->point()); + if (p.has_on_positive_side(i->opposite()->next()->vertex()->point()) && + CGAL::squared_distance(p, i->opposite()->next()->vertex()->point()) > 1e-8) { + return false; + } + } + // Also make sure that there is only one shell: + boost::unordered_set visited; + // c++11 + // visited.reserve(p.size_of_facets()); + + std::queue to_explore; + to_explore.push(p.facets_begin()); // One arbitrary facet + visited.insert(to_explore.front()); + + while (!to_explore.empty()) { + typename Polyhedron::Facet_const_handle f = to_explore.front(); + to_explore.pop(); + typename Polyhedron::Facet::Halfedge_around_facet_const_circulator he, end; + end = he = f->facet_begin(); + CGAL_For_all(he,end) { + typename Polyhedron::Facet_const_handle o = he->opposite()->facet(); + + if (!visited.count(o)) { + visited.insert(o); + to_explore.push(o); + } + } + } + + return visited.size() == p.size_of_facets(); +} + +class Shell_explorer +{ +public: + std::vector vertices; + + Shell_explorer() {} + void visit(CGAL_Nef_polyhedron3::Vertex_const_handle v) { + vertices.push_back(K::Point_3(to_double(v->point()[0]), + to_double(v->point()[1]), + to_double(v->point()[2]))); + } + void visit(CGAL_Nef_polyhedron3::Halfedge_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::Halffacet_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::SFace_const_handle ) {} +}; + +template +void decompose(const CGAL_Nef_polyhedron3 *N, Output out_iter) +{ + int parts = 0; + assert(N); + CGAL_Polyhedron poly; + if (N->is_simple()) { + nefworkaround::convert_to_Polyhedron(*N, poly); + } + if (is_weakly_convex(poly)) { + PRINTD("Minkowski: Object is convex and Nef"); + PolyhedronK poly2; + CGALUtils::copyPolyhedron(poly, poly2); + *out_iter++ = poly2; + return; + } + else { + PRINTD("Minkowski: Object is nonconvex Nef, decomposing..."); + CGAL_Nef_polyhedron3 decomposed_nef = *N; + CGAL::convex_decomposition_3(decomposed_nef); + + // the first volume is the outer volume, which ignored in the decomposition + CGAL_Nef_polyhedron3::Volume_const_iterator ci = ++decomposed_nef.volumes_begin(); + // Convert each convex volume to a Polyhedron + for(; ci != decomposed_nef.volumes_end(); ++ci) { + if(ci->mark()) { + // CGAL_Polyhedron poly; + // decomposed_nef.convert_inner_shell_to_polyhedron(ci->shells_begin(), poly); + // P.push_back(poly); + + + auto s = CGAL_Nef_polyhedron3::SFace_const_handle(ci->shells_begin()); + + CGAL_Nef_polyhedron3::SFace_const_iterator sf = ci->shells_begin(); + Shell_explorer SE; + decomposed_nef.visit_shell_objects(CGAL_Nef_polyhedron3::SFace_const_handle(sf),SE); + + PolyhedronK poly; + CGAL::convex_hull_3(SE.vertices.begin(), SE.vertices.end(), poly); + *out_iter++ = poly; + parts++; + } + } + + PRINTDB("Minkowski: decomposed into %d convex parts", parts); + } +} + +Geometry const * minkowskitest(const Geometry::ChildList &children) +{ + CGAL::Timer t,t_tot; + assert(children.size() >= 2); + // Iterate over children, perform pairwise minkowski on children: + // operands = [ch, ch+1] + Geometry::ChildList::const_iterator minkowski_ch_it = children.begin(); + t_tot.start(); + Geometry const *operands[2] = {minkowski_ch_it->second.get(), NULL}; + try { + while (++minkowski_ch_it != children.end()) { + operands[1] = minkowski_ch_it->second.get(); + + std::vector convexP[2]; // Stores decomposed operands + std::list result_parts; + + for (int i = 0; i < 2; i++) { + shared_ptr N; + if (const PolySet *ps = dynamic_cast(operands[i])) { + if (ps->is_convex()) { + PRINTDB("Minkowski: child %d is convex and PolySet", i); + PolyhedronK poly; + CGALUtils::createPolyhedronFromPolySet(*ps, poly); + convexP[i].push_back(poly); + } + else { + PRINTDB("Minkowski: child %d is nonconvex PolySet, transforming to Nef", i); + N.reset(createNefPolyhedronFromGeometry(*ps)); + } + } + else if (const CGAL_Nef_polyhedron *n = dynamic_cast(operands[i])) { + CGAL_Polyhedron poly; + if (n->p3->is_simple()) { + nefworkaround::convert_to_Polyhedron(*n->p3, poly); + // FIXME: Can we calculate weakly_convex on a PolyhedronK instead? + if (is_weakly_convex(poly)) { + PRINTDB("Minkowski: child %d is convex and Nef", i); + PolyhedronK poly2; + CGALUtils::copyPolyhedron(poly, poly2); + convexP[i].push_back(poly2); + } + else { + PRINTDB("Minkowski: child %d is nonconvex Nef",i); + N.reset(n); + } + } + else throw 0; // We cannot handle this, fall back to CGAL's minkowski + } + + // If not convex... + if (N && N->p3) { + PRINTD("Decomposing..."); + decompose(N->p3.get(), std::back_inserter(convexP[i])); + } + + PRINTD("Hulling convex parts..."); + std::vector points[2]; + std::vector minkowski_points; + + // For each permutation of convex operands.. + BOOST_FOREACH(const PolyhedronK &p0, convexP[0]) { + BOOST_FOREACH(const PolyhedronK &p1, convexP[1]) { + t.start(); + + // Create minkowski pointcloud + minkowski_points.clear(); + minkowski_points.reserve(p0.size_of_vertices() * p0.size_of_vertices()); + BOOST_FOREACH(const K::Point_3 &p0p, std::make_pair(p0.points_begin(), p0.points_end())) { + BOOST_FOREACH(const K::Point_3 &p1p, std::make_pair(p1.points_begin(), p1.points_end())) { + minkowski_points.push_back(p0p+(p1p-CGAL::ORIGIN)); + } + } + + t.stop(); + + // Ignore empty volumes + if (minkowski_points.size() <= 3) continue; + + // Hull point cloud + PolyhedronK result; + PRINTDB("Minkowski: Point cloud creation (%d ⨉ %d -> %d) took %f ms", + points[0].size() % points[1].size() % minkowski_points.size() % (t.time()*1000)); + t.reset(); + t.start(); + CGAL::convex_hull_3(minkowski_points.begin(), minkowski_points.end(), result); + + std::vector strict_points; + strict_points.reserve(minkowski_points.size()); + + for (PolyhedronK::Vertex_iterator i = result.vertices_begin(); i != result.vertices_end(); ++i) { + K::Point_3 const &p = i->point(); + + PolyhedronK::Vertex::Halfedge_handle h,e; + h = i->halfedge(); + e = h; + bool collinear = false; + bool coplanar = true; + + do { + K::Point_3 const& q = h->opposite()->vertex()->point(); + if (coplanar && !CGAL::coplanar(p,q, + h->next_on_vertex()->opposite()->vertex()->point(), + h->next_on_vertex()->next_on_vertex()->opposite()->vertex()->point())) { + coplanar = false; + } + + + for (PolyhedronK::Vertex::Halfedge_handle j = h->next_on_vertex(); + j != h && !collinear && ! coplanar; + j = j->next_on_vertex()) { + + K::Point_3 const& r = j->opposite()->vertex()->point(); + if (CGAL::collinear(p,q,r)) { + collinear = true; + } + } + + h = h->next_on_vertex(); + } while (h != e && !collinear); + + if (!collinear && !coplanar) strict_points.push_back(p); + } + + result.clear(); + CGAL::convex_hull_3(strict_points.begin(), strict_points.end(), result); + + t.stop(); + PRINTDB("Minkowski: Computing convex hull took %f s", t.time()); + t.reset(); + + result_parts.push_back(result); + } + } + } + + if (minkowski_ch_it != boost::next(children.begin())) delete operands[0]; + + if (result_parts.size() == 1) { + PolySet *ps = new PolySet(3,true); + createPolySetFromPolyhedron(*result_parts.begin(), *ps); + operands[0] = ps; + } else if (!result_parts.empty()) { + t.start(); + PRINTDB("Minkowski: Computing union of %d parts",result_parts.size()); + Geometry::ChildList fake_children; + for (std::list::iterator i = result_parts.begin(); i != result_parts.end(); ++i) { + PolySet ps(3,true); + createPolySetFromPolyhedron(*i, ps); + fake_children.push_back(std::make_pair((const AbstractNode*)NULL, + shared_ptr(createNefPolyhedronFromGeometry(ps)))); + } + CGAL_Nef_polyhedron *N = CGALUtils::applyOperator(fake_children, OPENSCAD_UNION); + t.stop(); + if (N) PRINTDB("Minkowski: Union done: %f s",t.time()); + else PRINTDB("Minkowski: Union failed: %f s",t.time()); + t.reset(); + operands[0] = N; + } else { + operands[0] = new CGAL_Nef_polyhedron(); + } + } + + t_tot.stop(); + PRINTDB("Minkowski: Total execution time %f s", t_tot.time()); + t_tot.reset(); + return operands[0]; + } + catch (...) { + // If anything throws we simply fall back to Nef Minkowski + PRINTD("Minkowski: Falling back to Nef Minkowski"); + + CGAL_Nef_polyhedron *N = applyOperator(children, OPENSCAD_MINKOWSKI); + return N; + } +} + +#define STL_FACET_NUMBYTES 4*3*4+2 +// as there is no 'float32_t' standard, we assume the systems 'float' +// is a 'binary32' aka 'single' standard IEEE 32-bit floating point type +union stl_facet { + uint8_t data8[ STL_FACET_NUMBYTES ]; + uint32_t data32[4*3]; + struct facet_data { + float i, j, k; + float x1, y1, z1; + float x2, y2, z2; + float x3, y3, z3; + uint16_t attribute_byte_count; + } data; +}; + +void uint32_byte_swap( uint32_t &x ) +{ +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + x = __builtin_bswap32( x ); +#elif defined(__clang__) + x = __builtin_bswap32( x ); +#elif defined(_MSC_VER) + x = _byteswap_ulong( x ); +#else + uint32_t b1 = ( 0x000000FF & x ) << 24; + uint32_t b2 = ( 0x0000FF00 & x ) << 8; + uint32_t b3 = ( 0x00FF0000 & x ) >> 8; + uint32_t b4 = ( 0xFF000000 & x ) >> 24; + x = b1 | b2 | b3 | b4; +#endif +} + +void read_stl_facet( std::ifstream &f, stl_facet &facet ) +{ + f.read( (char*)facet.data8, STL_FACET_NUMBYTES ); +#ifdef BOOST_BIG_ENDIAN + for ( int i = 0; i < 12; i++ ) { + uint32_byte_swap( facet.data32[ i ] ); + } + // we ignore attribute byte count +#endif +} + +PolySet *import_stl(const std::string &filename) +{ + PolySet *p = new PolySet(3); + + // Open file and position at the end + std::ifstream f(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + if (!f.good()) { + PRINTB("WARNING: Can't open import file '%s'.", filename); + return NULL; + } + + boost::regex ex_sfe("solid|facet|endloop"); + boost::regex ex_outer("outer loop"); + boost::regex ex_vertex("vertex"); + boost::regex ex_vertices("\\s*vertex\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)"); + + bool binary = false; + std::streampos file_size = f.tellg(); + f.seekg(80); + if (f.good() && !f.eof()) { + uint32_t facenum = 0; + f.read((char *)&facenum, sizeof(uint32_t)); +#ifdef BOOST_BIG_ENDIAN + uint32_byte_swap( facenum ); +#endif + if (file_size == static_cast(80 + 4 + 50*facenum)) { + binary = true; + } + } + f.seekg(0); + + char data[5]; + f.read(data, 5); + if (!binary && !f.eof() && f.good() && !memcmp(data, "solid", 5)) { + int i = 0; + double vdata[3][3]; + std::string line; + std::getline(f, line); + while (!f.eof()) { + std::getline(f, line); + boost::trim(line); + if (boost::regex_search(line, ex_sfe)) { + continue; + } + if (boost::regex_search(line, ex_outer)) { + i = 0; + continue; + } + boost::smatch results; + if (boost::regex_search(line, results, ex_vertices)) { + try { + for (int v=0;v<3;v++) { + vdata[i][v] = boost::lexical_cast(results[v+1]); + } + } + catch (const boost::bad_lexical_cast &blc) { + PRINTB("WARNING: Can't parse vertex line '%s'.", line); + i = 10; + continue; + } + if (++i == 3) { + p->append_poly(); + p->append_vertex(vdata[0][0], vdata[0][1], vdata[0][2]); + p->append_vertex(vdata[1][0], vdata[1][1], vdata[1][2]); + p->append_vertex(vdata[2][0], vdata[2][1], vdata[2][2]); + } + } + } + } + else if (binary && !f.eof() && f.good()) + { + f.ignore(80-5+4); + while (1) { + stl_facet facet; + read_stl_facet( f, facet ); + if (f.eof()) break; + p->append_poly(); + p->append_vertex(facet.data.x1, facet.data.y1, facet.data.z1); + p->append_vertex(facet.data.x2, facet.data.y2, facet.data.z2); + p->append_vertex(facet.data.x3, facet.data.y3, facet.data.z3); + } + } + return p; +} + +/*! + file format: + 1. polygon coordinates (x,y,z) are comma separated (+/- spaces) and + each coordinate is on a separate line + 2. each polygon is separated by one or more blank lines +*/ +bool import_polygon(PolyholeK &polyhole, const std::string &filename) +{ + std::ifstream ifs(filename.c_str()); + if (!ifs) return false; + + std::string line; + PolygonK polygon; + while (std::getline(ifs, line)) { + std::stringstream ss(line); + double X = 0.0, Y = 0.0, Z = 0.0; + if (!(ss >> X)) { + //ie blank lines => flag start of next polygon + if (polygon.size() > 0) polyhole.push_back(polygon); + polygon.clear(); + continue; + } + char c = ss.peek(); + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma + if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma + if (!(ss >> Y)) { + std::cerr << "Y error\n"; + return false; + } + c = ss.peek(); + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma + if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma + if (!(ss >> Z)) { + std::cerr << "Z error\n"; + return false; + } + polygon.push_back(Vertex3K(X, Y, Z)); + } + if (polygon.size() > 0) polyhole.push_back(polygon); + ifs.close(); + return true; +} +//------------------------------------------------------------------------------ + +int main(int argc, char *argv[]) +{ + + OpenSCAD::debug = "decompose"; + + PolySet *ps = NULL; + if (argc == 2) { + if (!(ps = import_stl(argv[1]))) { + std::cerr << "Error importing STL " << argv[1] << std::endl; + exit(1); + } + std::cerr << "Imported " << ps->numPolygons() << " polygons" << std::endl; + } + else { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + exit(1); + } + + Geometry::ChildList children; + + CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(*ps); + + std::vector result; + decompose(N->p3.get(), std::back_inserter(result)); + + std::cerr << "Decomposed into " << result.size() << " convex parts" << std::endl; + + int idx = 0; + BOOST_FOREACH(const PolyhedronK &P, result) { + PolySet result_ps(3); + if (CGALUtils::createPolySetFromPolyhedron(P, result_ps)) { + std::cerr << "Error converting to PolySet\n"; + } + else { + std::stringstream ss; + ss << "out" << idx++ << ".stl"; + exportFileByName(&result_ps, OPENSCAD_STL, ss.str().c_str(), ss.str().c_str()); + std::cout << "color([" << colors[idx%147][0] << "," << colors[idx%147][1] << "," << colors[idx%147][2] << "]) " << "import(\"" << ss.str() << "\");\n"; + } + } + std::cerr << "Done." << std::endl; +} diff --git a/cgal/decompose.pro b/cgal/decompose.pro new file mode 100644 index 00000000..83b2b0a5 --- /dev/null +++ b/cgal/decompose.pro @@ -0,0 +1,108 @@ +CONFIG += debug +CONFIG -= qt +debug: DEFINES += DEBUG + +TEMPLATE = app + +INCLUDEPATH += ../src +DEPENDPATH += ../src + +# Handle custom library location. +# Used when manually installing 3rd party libraries +isEmpty(OPENSCAD_LIBDIR) OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES) +macx:isEmpty(OPENSCAD_LIBDIR) { + exists(/opt/local):exists(/usr/local/Cellar) { + error("It seems you might have libraries in both /opt/local and /usr/local. Please specify which one to use with qmake OPENSCAD_LIBDIR=") + } else { + exists(/opt/local) { + #Default to MacPorts on Mac OS X + message("Automatically searching for libraries in /opt/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /opt/local + } else:exists(/usr/local/Cellar) { + message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /usr/local + } + } +} +!isEmpty(OPENSCAD_LIBDIR) { + QMAKE_INCDIR = $$OPENSCAD_LIBDIR/include + QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib +} + +TARGET = decompose +mac { + CONFIG -= app_bundle +} + +macx { + # Mac needs special care to link against the correct C++ library + # We attempt to auto-detect it by inspecting Boost + dirs = $${BOOSTDIR} $${QMAKE_LIBDIR} + for(dir, dirs) { + system(grep -q __112basic_string $${dir}/libboost_thread* >& /dev/null) { + message("Detected libc++-linked boost in $${dir}") + CONFIG += libc++ + } + } + + libc++ { + QMAKE_CXXFLAGS += -stdlib=libc++ + QMAKE_LFLAGS += -stdlib=libc++ + QMAKE_OBJECTIVE_CFLAGS += -stdlib=libc++ + # libc++ on requires Mac OS X 10.7+ + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 + } +} + +# See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs. +*g++* { + QMAKE_CXXFLAGS *= -fno-strict-aliasing + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs # ignored before 4.8 +} + +*clang* { + # http://llvm.org/bugs/show_bug.cgi?id=9182 + QMAKE_CXXFLAGS_WARN_ON += -Wno-overloaded-virtual + # disable enormous amount of warnings about CGAL / boost / etc + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++11-extensions + # might want to actually turn this on once in a while + QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare +} + +# Application configuration +CONFIG += cgal +CONFIG += boost +CONFIG += eigen +CONFIG += gettext + +mac: { + LIBS += -framework OpenGL +} + +include(../common.pri) + +HEADERS += ../src/cgal.h \ + ../src/cgalutils.h \ + ../src/linalg.h \ + ../src/polyset.h \ + ../src/polyset-utils.h \ + ../src/printutils.h + +SOURCES += decompose.cpp \ + ../src/polygon2d.cc \ + ../src/polygon2d-CGAL.cc \ + ../src/CGAL_Nef_polyhedron.cc \ + ../src/CGAL_Nef_polyhedron_DxfData.cc \ + ../src/cgalutils.cc \ + ../src/cgalutils-tess.cc \ + ../src/cgalutils-polyhedron.cc \ + ../src/polyset.cc \ + ../src/svg.cc \ + ../src/node.cc \ + ../src/export.cc \ + ../src/polyset-utils.cc \ + ../src/progress.cc \ + ../src/printutils.cc diff --git a/cgal/polyhole-tessellator.cpp b/cgal/polyhole-tessellator.cpp new file mode 100644 index 00000000..7a433a24 --- /dev/null +++ b/cgal/polyhole-tessellator.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include + +#include "cgalutils.h" + + + +// Nef polyhedron are using CGAL_Kernel3 (Cartesian) +// Triangulation will use Epick + +static void export_stl(const Polygons &triangles, std::ostream &output) +{ + setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output + output << "solid OpenSCAD_Model\n"; + BOOST_FOREACH(const Polygon &p, triangles) { + assert(p.size() == 3); // STL only allows triangles + std::stringstream stream; + stream << p[0][0] << " " << p[0][1] << " " << p[0][2]; + std::string vs1 = stream.str(); + stream.str(""); + stream << p[1][0] << " " << p[1][1] << " " << p[1][2]; + std::string vs2 = stream.str(); + stream.str(""); + stream << p[2][0] << " " << p[2][1] << " " << p[2][2]; + std::string vs3 = stream.str(); + if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) { + // The above condition ensures that there are 3 distinct vertices, but + // they may be collinear. If they are, the unit normal is meaningless + // so the default value of "1 0 0" can be used. If the vertices are not + // collinear then the unit normal must be calculated from the + // components. + Vector3d normal = (p[1] - p[0]).cross(p[2] - p[0]); + normal.normalize(); + output << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "\n"; + output << " outer loop\n"; + + BOOST_FOREACH(const Vector3d &v, p) { + output << " vertex " << v[0] << " " << v[1] << " " << v[2] << "\n"; + } + output << " endloop\n"; + output << " endfacet\n"; + } + } + output << "endsolid OpenSCAD_Model\n"; + setlocale(LC_NUMERIC, ""); // Set default locale +} + + +/*! + file format: + 1. polygon coordinates (x,y,z) are comma separated (+/- spaces) and + each coordinate is on a separate line + 2. each polygon is separated by one or more blank lines +*/ +bool import_polygon(PolyholeK &polyhole, const std::string &filename) +{ + std::ifstream ifs(filename.c_str()); + if (!ifs) return false; + + std::string line; + PolygonK polygon; + while (std::getline(ifs, line)) { + std::stringstream ss(line); + double X = 0.0, Y = 0.0, Z = 0.0; + if (!(ss >> X)) { + //ie blank lines => flag start of next polygon + if (polygon.size() > 0) polyhole.push_back(polygon); + polygon.clear(); + continue; + } + char c = ss.peek(); + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma + if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma + if (!(ss >> Y)) { + std::cerr << "Y error\n"; + return false; + } + c = ss.peek(); + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma + if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma + while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma + if (!(ss >> Z)) { + std::cerr << "Z error\n"; + return false; + } + polygon.push_back(Vertex3K(X, Y, Z)); + } + if (polygon.size() > 0) polyhole.push_back(polygon); + ifs.close(); + return true; +} +//------------------------------------------------------------------------------ + +int main(int argc, char *argv[]) +{ + PolyholeK polyhole; + K::Vector_3 *normal = NULL; + if (argc >= 2) { + if (!import_polygon(polyhole, argv[1])) { + std::cerr << "Error importing polygon" << std::endl; + exit(1); + } + std::cerr << "Imported " << polyhole.size() << " polygons" << std::endl; + + if (argc == 3) { + std::vector strs; + std::vector normalvec; + std::string arg(argv[2]); + boost::split(strs, arg, boost::is_any_of(",")); + assert(strs.size() == 3); + BOOST_FOREACH(const std::string &s, strs) normalvec.push_back(boost::lexical_cast(s)); + normal = new K::Vector_3(normalvec[0], normalvec[1], normalvec[2]); + } + } + else { + //construct two non-intersecting nested polygons + PolygonK polygon1; + polygon1.push_back(Vertex3K(0,0,0)); + polygon1.push_back(Vertex3K(2,0,0)); + polygon1.push_back(Vertex3K(2,2,0)); + polygon1.push_back(Vertex3K(0,2,0)); + PolygonK polygon2; + polygon2.push_back(Vertex3K(0.5,0.5,0)); + polygon2.push_back(Vertex3K(1.5,0.5,0)); + polygon2.push_back(Vertex3K(1.5,1.5,0)); + polygon2.push_back(Vertex3K(0.5,1.5,0)); + polyhole.push_back(polygon1); + polyhole.push_back(polygon2); + } + + Polygons triangles; + bool ok = CGALUtils::tessellatePolygonWithHoles(polyhole, triangles, normal); + std::cerr << "Tessellated into " << triangles.size() << " triangles" << std::endl; + + export_stl(triangles, std::cout); +} diff --git a/cgal/polyhole-tessellator.pro b/cgal/polyhole-tessellator.pro new file mode 100644 index 00000000..906d089f --- /dev/null +++ b/cgal/polyhole-tessellator.pro @@ -0,0 +1,90 @@ +debug: DEFINES += DEBUG + +TEMPLATE = app + +INCLUDEPATH += ../src +DEPENDPATH += ../src + +# Handle custom library location. +# Used when manually installing 3rd party libraries +isEmpty(OPENSCAD_LIBDIR) OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES) +macx:isEmpty(OPENSCAD_LIBDIR) { + exists(/opt/local):exists(/usr/local/Cellar) { + error("It seems you might have libraries in both /opt/local and /usr/local. Please specify which one to use with qmake OPENSCAD_LIBDIR=") + } else { + exists(/opt/local) { + #Default to MacPorts on Mac OS X + message("Automatically searching for libraries in /opt/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /opt/local + } else:exists(/usr/local/Cellar) { + message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /usr/local + } + } +} +!isEmpty(OPENSCAD_LIBDIR) { + QMAKE_INCDIR = $$OPENSCAD_LIBDIR/include + QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib +} + +TARGET = polyhole-tessellator +mac { + CONFIG -= app_bundle +} + +macx { + # Mac needs special care to link against the correct C++ library + # We attempt to auto-detect it by inspecting Boost + dirs = $${BOOSTDIR} $${QMAKE_LIBDIR} + for(dir, dirs) { + system(grep -q __112basic_string $${dir}/libboost_thread* >& /dev/null) { + message("Detected libc++-linked boost in $${dir}") + CONFIG += libc++ + } + } + + libc++ { + QMAKE_CXXFLAGS += -stdlib=libc++ + QMAKE_LFLAGS += -stdlib=libc++ + QMAKE_OBJECTIVE_CFLAGS += -stdlib=libc++ + # libc++ on requires Mac OS X 10.7+ + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 + } +} + +# See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs. +*g++* { + QMAKE_CXXFLAGS *= -fno-strict-aliasing + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs # ignored before 4.8 +} + +*clang* { + # http://llvm.org/bugs/show_bug.cgi?id=9182 + QMAKE_CXXFLAGS_WARN_ON += -Wno-overloaded-virtual + # disable enormous amount of warnings about CGAL / boost / etc + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++11-extensions + # might want to actually turn this on once in a while + QMAKE_CXXFLAGS_WARN_ON += -Wno-format-security +} + + + +# Application configuration +CONFIG += cgal +CONFIG += boost +CONFIG += eigen +CONFIG += gettext + +include(../common.pri) + +HEADERS += ../src/cgal.h \ + ../src/cgalutils.h \ + ../src/linalg.h \ + ../src/printutils.h + +SOURCES += polyhole-tessellator.cpp \ + ../src/cgalutils-tess.cc \ + ../src/printutils.cc diff --git a/color-schemes/editor/dark-background.json b/color-schemes/editor/dark-background.json index c9fc3e01..2c199fcc 100644 --- a/color-schemes/editor/dark-background.json +++ b/color-schemes/editor/dark-background.json @@ -1,31 +1,36 @@ { "name" : "For Dark Background", "index" : 1100, - "paper" : "#272822", - "text" : "#ffffff", + "paper" : "#222222", + "text" : "#e0e0e0", "caret" : { "width" : 2, "foreground" : "#ffff00", - "line-background" : "#68e1687f" + "line-background" : "#303030" }, "colors" : { - "keyword1" : "#f12971", + "keyword1" : "#90ee90", "keyword2" : "#56dbf0", - "keyword3" : "#56d8f0", - "comment" : "#ccdf32", - "number" : "#af7dff", + "keyword3" : "#add8e6", + "comment" : "#808080", + "commentline" : "#808080", + "commentdoc" : "#808080", + "commentdockeyword" : "#808080", + "number" : "#ff0000", "string" : "#e6db74", - "operator" : "#d8d8d8", - "commentline" : "#e6db74", - "selection-foreground" : "#ffff00", - "selection-background" : "#a0a0ff", - "margin-background" : "#14141496", - "margin-foreground" : "#fff", - "matched-brace-background" : "#333", - "matched-brace-foreground" : "#fff", - "unmatched-brace-background" : "#333", - "unmatched-brace-foreground" : "#fff", + "operator" : "#e8b609", + "whitespace-foreground" : "#e0e0e0", + "selection-foreground" : "#ffffff", + "selection-background" : "#4a90d9", + "margin-background" : "#272822", + "margin-foreground" : "#e0e0e0", + "matched-brace-background" : "#505050", + "matched-brace-foreground" : "#ffffff", + "unmatched-brace-background" : "#dc322f", + "unmatched-brace-foreground" : "#fdf6e3", "error-marker" : "#ff0000", + "error-indicator" : "#60ff0000", + "error-indicator-outline" : "#ff000000", "edge" : "#ffffff" } } \ No newline at end of file diff --git a/color-schemes/editor/light-background.json b/color-schemes/editor/light-background.json index 35471b50..2c0ffb30 100644 --- a/color-schemes/editor/light-background.json +++ b/color-schemes/editor/light-background.json @@ -1,31 +1,36 @@ { "name" : "For Light Background", "index" : 1000, - "paper" : "#fff", + "paper" : "#ffffff", "text" : "#272822", "caret" : { "width" : 2, "foreground" : "#000000", - "line-background" : "#ffe4e4" + "line-background" : "#f8f8f8" }, "colors" : { "keyword1" : "Green", "keyword2" : "Green", "keyword3" : "DarkBlue", "comment" : "DarkCyan", + "commentline" : "DarkCyan", + "commentdoc" : "DarkCyan", + "commentdockeyword" : "DarkCyan", "number" : "DarkRed", "string" : "DarkMagenta", "operator" : "Blue", - "commentline" : "DarkCyan", - "selection-foreground" : "#ffff00", - "selection-background" : "#a0a0ff", - "margin-background" : "#ccc", - "margin-foreground" : "#111", - "matched-brace-background" : "#333", - "matched-brace-foreground" : "#fff", - "unmatched-brace-background" : "#333", - "unmatched-brace-foreground" : "#fff", + "whitespace-foreground" : "#272822", + "selection-foreground" : "#ffffff", + "selection-background" : "#4a90d9", + "margin-background" : "#f8f8f8", + "margin-foreground" : "#000000", + "matched-brace-background" : "#c7f6cb", + "matched-brace-foreground" : "Blue", + "unmatched-brace-background" : "#ffcdcc", + "unmatched-brace-foreground" : "Blue", "error-marker" : "#ff0000", + "error-indicator" : "#60ff0000", + "error-indicator-outline" : "#ff000000", "edge" : "#ffffff" } } \ No newline at end of file diff --git a/color-schemes/editor/monokai.json b/color-schemes/editor/monokai.json index 17bf8f9a..8ddac03c 100644 --- a/color-schemes/editor/monokai.json +++ b/color-schemes/editor/monokai.json @@ -5,27 +5,32 @@ "text" : "#f8f8f2", "caret" : { "width" : 2, - "foreground" : "#ffff00", - "line-background" : "#3e3d32" + "foreground" : "#f8f8f2", + "line-background" : "#49483e" }, "colors" : { - "keyword1" : "#66c3b3", - "keyword2" : "#79abff", - "keyword3" : "#ffffff", - "comment" : "#ccdf32", - "number" : "#7fb347", - "string" : "#e6db74", - "operator" : "#d8d8d8", + "keyword1" : "#f92672", + "keyword2" : "#a6e22e", + "keyword3" : "#66d9ef", + "comment" : "#75715e", "commentline" : "#75715e", - "selection-foreground" : "#ffff00", - "selection-background" : "#a0a0ff", - "margin-background" : "#757575", + "commentdoc" : "#75715e", + "commentdockeyword" : "#7b9a3c", + "number" : "#ae81ff", + "string" : "#e6db74", + "operator" : "#f8f8f2", + "whitespace-foreground" : "#f8f8f2", + "selection-foreground" : "#272822", + "selection-background" : "#f8f8f2", + "margin-background" : "#3e3d32", "margin-foreground" : "#f8f8f2", - "matched-brace-background" : "#333", - "matched-brace-foreground" : "#fff", - "unmatched-brace-background" : "#333", - "unmatched-brace-foreground" : "#fff", + "matched-brace-background" : "#606060", + "matched-brace-foreground" : "#ffff00", + "unmatched-brace-background" : "#b06060", + "unmatched-brace-foreground" : "#ffff00", "error-marker" : "#ff0000", + "error-indicator" : "#80ffe0e0", + "error-indicator-outline" : "#ff000000", "edge" : "#ffffff" } } \ No newline at end of file diff --git a/color-schemes/editor/solarized-dark.json b/color-schemes/editor/solarized-dark.json new file mode 100644 index 00000000..73d369fb --- /dev/null +++ b/color-schemes/editor/solarized-dark.json @@ -0,0 +1,36 @@ +{ + "name" : "Solarized (dark)", + "index" : 1310, + "paper" : "#002b36", + "text" : "#839496", + "caret" : { + "width" : 2, + "foreground" : "#fff070", + "line-background" : "#073642" + }, + "colors" : { + "keyword1" : "#268ad1", + "keyword2" : "#2aa198", + "keyword3" : "#859900", + "comment" : "#657b83", + "commentline" : "#657b83", + "commentdoc" : "#657b83", + "commentdockeyword" : "#6c71c4", + "number" : "#d33682", + "string" : "#b58900", + "operator" : "#cb4b16", + "whitespace-foreground" : "#839496", + "selection-foreground" : "#fdf6e3", + "selection-background" : "#657b83", + "margin-background" : "#002b36", + "margin-foreground" : "#839496", + "matched-brace-background" : "#0c4e22", + "matched-brace-foreground" : "#fff070", + "unmatched-brace-background" : "#92211f", + "unmatched-brace-foreground" : "#fff070", + "error-marker" : "#ff0000", + "error-indicator" : "#90ff8080", + "error-indicator-outline" : "#ff000000", + "edge" : "#d33682" + } +} \ No newline at end of file diff --git a/color-schemes/editor/solarized-light.json b/color-schemes/editor/solarized-light.json new file mode 100644 index 00000000..d99bcbcd --- /dev/null +++ b/color-schemes/editor/solarized-light.json @@ -0,0 +1,36 @@ +{ + "name" : "Solarized (light)", + "index" : 1300, + "paper" : "#fdf6e3", + "text" : "#657b83", + "caret" : { + "width" : 2, + "foreground" : "#000000", + "line-background" : "#eee8d5" + }, + "colors" : { + "keyword1" : "#268ad1", + "keyword2" : "#2aa198", + "keyword3" : "#859900", + "comment" : "#93a1a1", + "commentline" : "#93a1a1", + "commentdoc" : "#93a1a1", + "commentdockeyword" : "#6c71c4", + "number" : "#d33682", + "string" : "#b58900", + "operator" : "#cb4b16", + "whitespace-foreground" : "#657b83", + "selection-foreground" : "#fdf6e3", + "selection-background" : "#657b83", + "margin-background" : "#eee8d5", + "margin-foreground" : "#657b83", + "matched-brace-background" : "#c7f6cb", + "matched-brace-foreground" : "#cb4b16", + "unmatched-brace-background" : "#ffcdcc", + "unmatched-brace-foreground" : "#cb4b16", + "error-marker" : "#ff0000", + "error-indicator" : "#80ff0000", + "error-indicator-outline" : "#ff000000", + "edge" : "#d33682" + } +} \ No newline at end of file diff --git a/color-schemes/editor/solarized.json b/color-schemes/editor/solarized.json deleted file mode 100644 index c9ee3dd3..00000000 --- a/color-schemes/editor/solarized.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name" : "Solarized", - "index" : 1300, - "paper" : "#fdf6e3", - "text" : "#657b83", - "caret" : { - "width" : 2, - "foreground" : "#0000ff", - "line-background" : "#eeead5" - }, - "colors" : { - "keyword1" : "#268ad1", - "keyword2" : "#6c71c4", - "keyword3" : "#b58800", - "comment" : "#b58900", - "number" : "#cb4b16", - "string" : "#2aa198", - "operator" : "#859900", - "commentline" : "#b58800", - "selection-foreground" : "#fdf6e3", - "selection-background" : "#657b83", - "margin-background" : "#eee8d5", - "margin-foreground" : "#93a1a1", - "matched-brace-background" : "#333", - "matched-brace-foreground" : "#fff", - "unmatched-brace-background" : "#333", - "unmatched-brace-foreground" : "#fff", - "error-marker" : "#ff0000", - "edge" : "#ffffff" - } -} \ No newline at end of file diff --git a/color-schemes/editor/tomorrow-night.json b/color-schemes/editor/tomorrow-night.json new file mode 100644 index 00000000..69199850 --- /dev/null +++ b/color-schemes/editor/tomorrow-night.json @@ -0,0 +1,36 @@ +{ + "name" : "Tomorrow Night", + "index" : 1600, + "paper" : "#1d1f21", + "text" : "#c5c8c6", + "caret" : { + "width" : 2, + "foreground" : "#ffffff", + "line-background" : "#282a2e" + }, + "colors" : { + "keyword1" : "#de935f", + "keyword2" : "#b294bb", + "keyword3" : "#81a2be", + "comment" : "#969896", + "commentline" : "#969896", + "commentdoc" : "#969896", + "commentdockeyword" : "#f0c674", + "number" : "#cc6666", + "string" : "#b5bd68", + "operator" : "#8abeb7", + "whitespace-foreground" : "#c5c8c6", + "selection-foreground" : "#373b41", + "selection-background" : "#c5c8c6", + "margin-background" : "#1d1f21", + "margin-foreground" : "#969896", + "matched-brace-background" : "#50545c", + "matched-brace-foreground" : "#e2e6e3", + "unmatched-brace-background" : "#8a1111", + "unmatched-brace-foreground" : "#e2e6e3", + "error-marker" : "#ff0000", + "error-indicator" : "#80ff0000", + "error-indicator-outline" : "#ff000000", + "edge" : "#d33682" + } +} diff --git a/color-schemes/editor/tomorrow.json b/color-schemes/editor/tomorrow.json new file mode 100644 index 00000000..d3cef5f4 --- /dev/null +++ b/color-schemes/editor/tomorrow.json @@ -0,0 +1,36 @@ +{ + "name" : "Tomorrow", + "index" : 1500, + "paper" : "#f8f8f8", + "text" : "#4d4d4c", + "caret" : { + "width" : 2, + "foreground" : "#000000", + "line-background" : "#efefef" + }, + "colors" : { + "keyword1" : "#f5871f", + "keyword2" : "#8959a8", + "keyword3" : "#4271ae", + "comment" : "#8e908c", + "commentline" : "#8e908c", + "commentdoc" : "#8e908c", + "commentdockeyword" : "#eab700", + "number" : "#c82829", + "string" : "#718c00", + "operator" : "#3e999f", + "whitespace-foreground" : "#4d4d4c", + "selection-foreground" : "#4d4d4c", + "selection-background" : "#d6d6d6", + "margin-background" : "#f8f8f8", + "margin-foreground" : "#4d4d4c", + "matched-brace-background" : "#c7f6cb", + "matched-brace-foreground" : "#4d4d4c", + "unmatched-brace-background" : "#ffcdcc", + "unmatched-brace-foreground" : "#4d4d4c", + "error-marker" : "#ff0000", + "error-indicator" : "#80ff0000", + "error-indicator-outline" : "#ff000000", + "edge" : "#d33682" + } +} diff --git a/color-schemes/editor/visualstudio.json b/color-schemes/editor/visualstudio.json new file mode 100644 index 00000000..9a1da1b1 --- /dev/null +++ b/color-schemes/editor/visualstudio.json @@ -0,0 +1,41 @@ +{ + "name" : "Visual Studio", + "index" : 1400, + "paper" : "#ffffff", + "text" : "#101010", + "caret" : { + "width" : 2, + "foreground" : "#000000", + "line-background" : "#eeeeee" + }, + "colors" : { + "keyword1" : "blue", + "keyword2" : "blue", + "keyword3" : "#2B91AF", + "comment" : "DarkGreen", + "commentline" : "DarkGreen", + "commentdoc" : "#DarkGreen", + "commentdockeyword" : "#DarkGreen", + "number" : "DarkRed", + "string" : "#A31515", + "operator" : "Blue", + "whitespace-foreground" : "#101010", + "selection-foreground" : "black", + "selection-background" : "lightblue", + "margin-background" : "white", + "margin-foreground" : "#2B91AF", + "matched-brace-background" : "darkgrey", + "matched-brace-foreground" : "black", + "unmatched-brace-background" : "red", + "unmatched-brace-foreground" : "#ffffff", + "error-marker" : "#ff0000", + "error-indicator" : "#60ff0000", + "error-indicator-outline" : "#ff000000", + "edge" : "#ffffff" + }, + "keywords" : { + "keyword-set1" : "if else let for module function true false undef include use", + "keyword-set2" : "abs sign rands min max sin cos asin acos tan atan atan2 round ceil floor pow sqrt exp len log ln str chr concat lookup search version version_num norm cross parent_module dxf_dim dxf_cross", + "keyword-set3" : "cube sphere cylinder polyhedron square circle polygon text minkowski hull resize child echo union difference intersection linear_extrude rotate_extrude import group projection render surface scale rotate mirror translate multmatrix color offset" + } +} diff --git a/color-schemes/readme.txt b/color-schemes/readme.txt new file mode 100644 index 00000000..ebe45b95 --- /dev/null +++ b/color-schemes/readme.txt @@ -0,0 +1,44 @@ +Color Schemes +============= + +Solarized +--------- + +http://ethanschoonover.com/solarized + +Monokai +------- + +http://www.monokai.nl/blog/2006/07/15/textmate-color-theme/ + +Tomorrow / Tomorrow Night +------------------------- + +https://github.com/chriskempson/tomorrow-theme + +Editor: + +keyword1 Orange +keyword2 Purple +keyword3 Blue +comment Comment +commentline Comment +commentdoc Comment +commentdockeyword Yellow +number Red +string Green +operator Aqua +selection-foreground Foreground +selection-background Selection + +Render: + +opencsg-face-front Blue +opencsg-face-back Orange +cgal-face-front Aqua +cgal-face-back Yellow +cgal-face-2d Green +cgal-edge-front Foreground +cgal-edge-back Foreground +cgal-edge-2d Red +crosshair Purple diff --git a/color-schemes/render/tomorrow-night.json b/color-schemes/render/tomorrow-night.json new file mode 100644 index 00000000..991755f2 --- /dev/null +++ b/color-schemes/render/tomorrow-night.json @@ -0,0 +1,18 @@ +{ + "name" : "Tomorrow Night", + "index" : 1900, + "show-in-gui" : true, + + "colors" : { + "background" : "#1d1f21", + "opencsg-face-front" : "#81a2be", + "opencsg-face-back" : "#de935f", + "cgal-face-front" : "#8abeb7", + "cgal-face-back" : "#f0c674", + "cgal-face-2d" : "#b5bd68", + "cgal-edge-front" : "#c5c8c6", + "cgal-edge-back" : "#c5c8c6", + "cgal-edge-2d" : "#cc6666", + "crosshair" : "#b294bb" + } +} diff --git a/color-schemes/render/tomorrow.json b/color-schemes/render/tomorrow.json new file mode 100644 index 00000000..db21d9a7 --- /dev/null +++ b/color-schemes/render/tomorrow.json @@ -0,0 +1,18 @@ +{ + "name" : "Tomorrow", + "index" : 1800, + "show-in-gui" : true, + + "colors" : { + "background" : "#f8f8f8", + "opencsg-face-front" : "#4271ae", + "opencsg-face-back" : "#f5871f", + "cgal-face-front" : "#3e999f", + "cgal-face-back" : "#eab700", + "cgal-face-2d" : "#718c00", + "cgal-edge-front" : "#4d4d4c", + "cgal-edge-back" : "#4d4d4c", + "cgal-edge-2d" : "#c82829", + "crosshair" : "#8959a8" + } +} diff --git a/common.pri b/common.pri index ff10dc84..40966036 100644 --- a/common.pri +++ b/common.pri @@ -12,6 +12,7 @@ include(glew.pri) include(eigen.pri) include(boost.pri) include(glib-2.0.pri) +include(gettext.pri) include(sparkle.pri) include(harfbuzz.pri) include(freetype.pri) diff --git a/doc/translation.txt b/doc/translation.txt new file mode 100644 index 00000000..8abb3eaa --- /dev/null +++ b/doc/translation.txt @@ -0,0 +1,84 @@ +OpenSCAD human language translation +=================================== + +We use the GNU gettext system, both for c++ code as well as QT's .ui files. +The latter is accomplished by the '-tr' feature of QT's uic to insert +a gettext wrapper into the ui_xxxxx.h files. + +For somewhat similar designs, see the source code of projects like celestia, +stellarium, licq, merkaartor, etc (although they typically use cmake). + +Currently the build system does not auto-update anything. The .mo files must +be generated by running the gettext tools: xgettext, msgmerge, and msgfmt. +There is a script included, translation-update.sh, that automates this process. + +File layout: +============ + + ./locale/*.po - .po files, one per language + ./locale/openscad.pot - .pot template, generated by xgettext + ./locale/POTFILES - list of source files with translatable strings (generated) + ./locale/LINGUAS - list of language codes for which .po files exist + ./src/qtgettext.h - wrapper code between QT and GNU gettext + ./scripts/translation-update.sh - simple unix helper script + ./locale/xx/LC_MESSAGES/openscad.mo - 'binaries' of .po files, built by script + +To translate the strings: +========================= + +Use a text editor or special program (poedit or lokalize) to edit the .po file. +( See http://en.opensuse.org/SDB:Localization_work_with_po_files ) +Then submit your new .po file as an OpenSCAD github issue or pull request: + +https://github.com/openscad/openscad/issues/ + +In the future there might be a site to allow translations in a browser: + +https://translations.launchpad.net/openscad + +If all else fails, email the OpenSCAD mailing list with your new .po +file attached. + +To make source code changes: +============================ + +In .cc files, #include "printutils.h" and change each "text" into +_("text"). In .ui files, #include "qtgettext.h" first in the .h file +(see MainWindow.h). Then clean and rebuild to recreate the ui_xxxx.h +files. + + $ make clean && qmake && make + +Then run the script to scan the source files, and regenerate .pot & .po files. + + $ ./scripts/translation-update.sh + +This will create new .po files with any new untranslated strings you +added to the source code. These .po files can be distributed to +translators for translation. After the translated .po file is obtained, +overwrite the old .po and run the same script to update the .mo files. + + $ ./scripts/translation-update.sh + +To add a new language: +====================== + +First add the language code to file ./locale/LINGUAS. Then run msginit, +replacing $LANGCODE with the language code you want. + + $ msginit -l $LANGCODE -o ./locale/$LANGCODE.po -i ./locale/openscad.pot + +You will now have a new ./locale/xx.po file to edit and translate + +Testing: +======== + +On unix, set the locale related environment variables. For example in +French, run this: + + $ LANGUAGE=fr ./openscad + +Linux system trace tools can help find errors. To show open()s on .mo files: + + $ LANGUAGE=fr strace -f ./openscad 2>&1 | grep LC_MESSAGES + diff --git a/eigen.pri b/eigen.pri index 79f0a5f0..03315ff5 100644 --- a/eigen.pri +++ b/eigen.pri @@ -1,18 +1,14 @@ -# Detect eigen3 + eigen2, then use this priority list to determine -# which eigen to use: +# Detect eigen3 # # Priority -# 0. EIGENDIR if set (also EIGEN2DIR for backwards compatability) +# 0. EIGENDIR if set # 1. OPENSCAD_LIBRARIES eigen3 -# 2. OPENSCAD_LIBRARIES eigen2 # 3. system's standard include paths for eigen3 -# 4. system's standard include paths for eigen2 eigen { # read environment variables OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES) -EIGEN2_DIR = $$(EIGEN2DIR) EIGEN_DIR = $$(EIGENDIR) # Optionally specify location of Eigen3 using the @@ -23,20 +19,8 @@ EIGEN_DIR = $$(EIGENDIR) EIGEN_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/eigen3 } } - isEmpty(EIGEN_INCLUDEPATH) { - exists($$OPENSCAD_LIBRARIES_DIR/include/eigen2) { - EIGEN_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/eigen2 - } - } } - -# Optionally specify location of Eigen using the -# EIGENDIR env. variable (EIGEN2 for backwards compatability) -!isEmpty(EIGEN2_DIR) { - EIGEN_INCLUDEPATH = $$EIGEN2_DIR - message("User set EIGEN location: $$EIGEN_INCLUDEPATH") -} !isEmpty(EIGEN_DIR) { EIGEN_INCLUDEPATH = $$EIGEN_DIR message("User set EIGEN location: $$EIGEN_INCLUDEPATH") @@ -47,17 +31,6 @@ isEmpty(EIGEN_INCLUDEPATH) { freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen3 netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen3 macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen3 - !exists($$EIGEN_INCLUDEPATH) { - linux*|hurd*|unix*: EIGEN_INCLUDEPATH = /usr/include/eigen2 - freebsd-g++: EIGEN_INCLUDEPATH = /usr/local/include/eigen2 - netbsd*: EIGEN_INCLUDEPATH = /usr/pkg/include/eigen2 - macx: EIGEN_INCLUDEPATH = /opt/local/include/eigen2 - } -} - -!exists($$EIGEN_INCLUDEPATH/Eigen/Core) { - EIGEN_CFLAGS = $$system("pkg-config --cflags eigen2") - EIGEN_INCLUDEPATH = $$replace(EIGEN_CFLAGS,"-I","") } !exists($$EIGEN_INCLUDEPATH/Eigen/Core) { @@ -72,7 +45,7 @@ isEmpty(EIGEN_INCLUDEPATH) { } } -# EIGEN being under 'include/eigen[2-3]' needs special prepending +# EIGEN being under 'include/eigen3' needs special prepending contains(QT_VERSION, ^5\\..*) { QMAKE_INCDIR = $$EIGEN_INCLUDEPATH $$QMAKE_INCDIR } else { diff --git a/fonts/05-osx-fonts.conf b/fonts-osx/conf.d/05-osx-fonts.conf similarity index 100% rename from fonts/05-osx-fonts.conf rename to fonts-osx/conf.d/05-osx-fonts.conf diff --git a/gettext.pri b/gettext.pri new file mode 100644 index 00000000..84fffa55 --- /dev/null +++ b/gettext.pri @@ -0,0 +1,28 @@ +# Detect gettext, then use this priority list to determine +# which library to use: +# +# Priority +# 1. GETTEXT_INCLUDEPATH / GETTEXT_LIBPATH (qmake parameter, not checked it given on commandline) +# 2. OPENSCAD_LIBRARIES (environment variable) +# 3. system's standard include paths from pkg-config + +gettext { + +# read environment variables +OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES) +GETTEXT_DIR = $$(GETTEXTDIR) + +macx: { + isEmpty(GETTEXT_INCLUDEPATH) { + !isEmpty(OPENSCAD_LIBRARIES_DIR) { + GETTEXT_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include + GETTEXT_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib + } + } + GETTEXT_CXXFLAGS=-I$$GETTEXT_INCLUDEPATH + GETTEXT_LIBS=-L$$GETTEXT_LIBPATH -lintl -liconv +} + +QMAKE_CXXFLAGS += $$GETTEXT_CXXFLAGS +LIBS += $$GETTEXT_LIBS +} diff --git a/glew.pri b/glew.pri index 9898af59..1bd3236e 100644 --- a/glew.pri +++ b/glew.pri @@ -9,6 +9,9 @@ glew { } unix:LIBS += -lGLEW - win32:LIBS += -lglew32s - CONFIG(mingw-cross-env):DEFINES += GLEW_STATIC + CONFIG(mingw-cross-env): { + DEFINES += GLEW_STATIC + } else { + win32:LIBS += -lglew32 + } } diff --git a/icons/SCAD.png b/icons/SCAD.png new file mode 100644 index 00000000..1d4e986f Binary files /dev/null and b/icons/SCAD.png differ diff --git a/locale/LINGUAS b/locale/LINGUAS new file mode 100644 index 00000000..9f82de46 --- /dev/null +++ b/locale/LINGUAS @@ -0,0 +1,2 @@ +# available languages +fr ru de cs diff --git a/locale/cs.po b/locale/cs.po new file mode 100644 index 00000000..d9a8ed71 --- /dev/null +++ b/locale/cs.po @@ -0,0 +1,1116 @@ +# Czech translations for OpenSCAD package. +# Copyright (C) 2014 THE OpenSCAD'S COPYRIGHT HOLDER +# This file is distributed under the same license as the OpenSCAD package. +# Miro Hrončok , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: OpenSCAD 2013.02.24\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 23:28+0100\n" +"PO-Revision-Date: 2014-12-23 12:24+0100\n" +"Last-Translator: Miro Hrončok \n" +"Language-Team: Czech \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n>=2 && n<=4 ? 1 : 2;\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: objects/ui_AboutDialog.h:51 src/AboutDialog.h:15 +msgid "About OpenSCAD" +msgstr "O OpenSCADu" + +#: objects/ui_FontListDialog.h:102 +msgid "OpenSCAD Font List" +msgstr "Seznam OpenSCAD písem" + +#: objects/ui_FontListDialog.h:103 objects/ui_LibraryInfoDialog.h:77 +msgid "&OK" +msgstr "&OK" + +#: objects/ui_FontListDialog.h:104 +msgid "Copy to Clipboard" +msgstr "Kopírovat do schránky" + +#: objects/ui_FontListDialog.h:105 +msgid "Filter:" +msgstr "Filtr:" + +#: objects/ui_FontListDialog.h:106 +msgid "" +"

This list shows the fonts currently registered with " +"OpenSCAD.

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" +msgstr "" +"

Tento seznam zobrazuje písma momentálně dostupná pro " +"OpenSCAD.

Příklad:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" + +#: objects/ui_launchingscreen.h:276 +msgid "Welcome to OpenSCAD" +msgstr "Vítá vás OpenSCAD" + +#: objects/ui_launchingscreen.h:277 +msgid "New" +msgstr "Nový" + +#: objects/ui_launchingscreen.h:278 +msgid "Open" +msgstr "Otevřít" + +#: objects/ui_launchingscreen.h:279 +msgid "Help" +msgstr "Nápověda" + +#: objects/ui_launchingscreen.h:280 +msgid "Recents" +msgstr "Nedávné" + +#: objects/ui_launchingscreen.h:281 +msgid "Open Recent" +msgstr "Otevřít nedávný soubor" + +#: objects/ui_launchingscreen.h:282 objects/ui_launchingscreen.h:284 +#: objects/ui_MainWindow.h:855 +msgid "Examples" +msgstr "Příklady" + +#: objects/ui_launchingscreen.h:285 +msgid "Open Example" +msgstr "Otevřít příklad" + +#: objects/ui_launchingscreen.h:287 +msgid "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" +msgstr "" + +#: objects/ui_launchingscreen.h:294 +msgid "Don't show again" +msgstr "Příště nezobrazovat" + +#: objects/ui_LibraryInfoDialog.h:75 +msgid "Lib & Build Info" +msgstr "Info o knihovnách a sestavení" + +#: objects/ui_LibraryInfoDialog.h:76 +msgid "OpenSCAD Detailed Library and Build Information" +msgstr "Podrobné informace o tomto sestavení OpenSCADu a použitých knihovnách" + +#: objects/ui_MainWindow.h:731 +msgid "&New" +msgstr "&Nový" + +#: objects/ui_MainWindow.h:732 +msgid "Ctrl+N" +msgstr "Ctrl+N" + +#: objects/ui_MainWindow.h:733 +msgid "&Open..." +msgstr "&Otevřít..." + +#: objects/ui_MainWindow.h:734 +msgid "Ctrl+O" +msgstr "Ctrl+O" + +#: objects/ui_MainWindow.h:735 +msgid "&Save" +msgstr "&Uložit" + +#: objects/ui_MainWindow.h:736 +msgid "Ctrl+S" +msgstr "Ctrl+S" + +#: objects/ui_MainWindow.h:737 +msgid "Save &As..." +msgstr "Uložit &jako..." + +#: objects/ui_MainWindow.h:738 +msgid "Ctrl+Shift+S" +msgstr "Ctrl+Shift+S" + +#: objects/ui_MainWindow.h:739 +msgid "&Reload" +msgstr "&Znovu načíst" + +#: objects/ui_MainWindow.h:740 +msgid "Ctrl+R" +msgstr "Ctrl+R" + +#: objects/ui_MainWindow.h:741 +msgid "&Quit" +msgstr "U&končit" + +#: objects/ui_MainWindow.h:742 +msgid "Ctrl+Q" +msgstr "Ctrl+Q" + +#: objects/ui_MainWindow.h:743 +msgid "&Undo" +msgstr "&Zpět" + +#: objects/ui_MainWindow.h:744 +msgid "Ctrl+Z" +msgstr "Ctrl+Z" + +#: objects/ui_MainWindow.h:745 +msgid "&Redo" +msgstr "Zn&ovu" + +#: objects/ui_MainWindow.h:746 +msgid "Ctrl+Shift+Z" +msgstr "Ctrl+Shift+Z" + +#: objects/ui_MainWindow.h:747 +msgid "Cu&t" +msgstr "&Vyjmout" + +#: objects/ui_MainWindow.h:748 +msgid "Ctrl+X" +msgstr "Ctrl+X" + +#: objects/ui_MainWindow.h:749 +msgid "&Copy" +msgstr "&Kopírovat" + +#: objects/ui_MainWindow.h:750 +msgid "Ctrl+C" +msgstr "Ctrl+C" + +#: objects/ui_MainWindow.h:751 +msgid "&Paste" +msgstr "V&ložit" + +#: objects/ui_MainWindow.h:752 +msgid "Ctrl+V" +msgstr "Ctrl+V" + +#: objects/ui_MainWindow.h:753 +msgid "&Indent" +msgstr "Odsad&it" + +#: objects/ui_MainWindow.h:754 +msgid "Ctrl+I" +msgstr "Ctrl+I" + +#: objects/ui_MainWindow.h:755 +msgid "U&nindent" +msgstr "Z&rušit odsazení" + +#: objects/ui_MainWindow.h:756 +msgid "Ctrl+Shift+I" +msgstr "Ctrl+Shift+I" + +#: objects/ui_MainWindow.h:757 +msgid "C&omment" +msgstr "&Zakomentovat" + +#: objects/ui_MainWindow.h:758 +msgid "Ctrl+D" +msgstr "Ctrl+D" + +#: objects/ui_MainWindow.h:759 +msgid "Unco&mment" +msgstr "&Odkomentovat" + +#: objects/ui_MainWindow.h:760 +msgid "Ctrl+Shift+D" +msgstr "Ctrl+Shift+D" + +#: objects/ui_MainWindow.h:761 +msgid "Paste viewport translation" +msgstr "Vložit posun pohledu" + +#: objects/ui_MainWindow.h:762 +msgid "Ctrl+T" +msgstr "Ctrl+T" + +#: objects/ui_MainWindow.h:763 +msgid "Paste viewport rotation" +msgstr "Vložit rotaci pohledu" + +#: objects/ui_MainWindow.h:764 objects/ui_MainWindow.h:842 +msgid "Zoom In" +msgstr "Přiblížit" + +#: objects/ui_MainWindow.h:765 +msgid "Ctrl++" +msgstr "Ctrl++" + +#: objects/ui_MainWindow.h:766 objects/ui_MainWindow.h:844 +msgid "Zoom Out" +msgstr "Oddálit" + +#: objects/ui_MainWindow.h:767 +msgid "Ctrl+-" +msgstr "Ctrl+-" + +#: objects/ui_MainWindow.h:768 +msgid "Hide editor" +msgstr "Skrýt editor" + +#: objects/ui_MainWindow.h:769 +msgid "&Reload and Preview" +msgstr "Znovu &načíst a zobrazit" + +#: objects/ui_MainWindow.h:770 +msgid "F4" +msgstr "F4" + +#: objects/ui_MainWindow.h:771 +msgid "&Preview" +msgstr "&Zobrazit" + +#: objects/ui_MainWindow.h:772 +msgid "F5" +msgstr "F5" + +#: objects/ui_MainWindow.h:773 +msgid "&Render" +msgstr "Vy&renderovat" + +#: objects/ui_MainWindow.h:774 +msgid "F6" +msgstr "F6" + +#: objects/ui_MainWindow.h:775 +msgid "Check Validity" +msgstr "Zkontrolovat správnost" + +#: objects/ui_MainWindow.h:776 +msgid "Display &AST..." +msgstr "Ukázat &ATS..." + +#: objects/ui_MainWindow.h:777 +msgid "Display CSG &Tree..." +msgstr "Ukázat CSG s&trom..." + +#: objects/ui_MainWindow.h:778 +msgid "Display CSG &Products..." +msgstr "Ukázat CSG &produkty..." + +#: objects/ui_MainWindow.h:779 +msgid "Export as &STL..." +msgstr "Exportovat jako &STL..." + +#: objects/ui_MainWindow.h:780 +msgid "Export as &OFF..." +msgstr "Exportovat jako &OFF..." + +#: objects/ui_MainWindow.h:781 +msgid "Preview" +msgstr "Náhled" + +#: objects/ui_MainWindow.h:782 +msgid "F9" +msgstr "F9" + +#: objects/ui_MainWindow.h:783 +msgid "Surfaces" +msgstr "Povrchy" + +#: objects/ui_MainWindow.h:784 +msgid "F10" +msgstr "F10" + +#: objects/ui_MainWindow.h:785 +msgid "Wireframe" +msgstr "Drátové zobrazení" + +#: objects/ui_MainWindow.h:786 +msgid "F11" +msgstr "F11" + +#: objects/ui_MainWindow.h:787 +msgid "Thrown Together" +msgstr "Vše společně" + +#: objects/ui_MainWindow.h:788 +msgid "F12" +msgstr "F12" + +#: objects/ui_MainWindow.h:789 +msgid "Show Edges" +msgstr "Zobrazit hrany" + +#: objects/ui_MainWindow.h:790 +msgid "Ctrl+1" +msgstr "Ctrl+1" + +#: objects/ui_MainWindow.h:791 +msgid "Show Axes" +msgstr "Zobrazit osy" + +#: objects/ui_MainWindow.h:792 +msgid "Ctrl+2" +msgstr "Ctrl+2" + +#: objects/ui_MainWindow.h:793 +msgid "Show Crosshairs" +msgstr "Zobrazit zaměřovač" + +#: objects/ui_MainWindow.h:794 +msgid "Ctrl+3" +msgstr "Ctrl+3" + +#: objects/ui_MainWindow.h:795 +msgid "Animate" +msgstr "Animovat" + +#: objects/ui_MainWindow.h:796 +msgid "Top" +msgstr "Shora" + +#: objects/ui_MainWindow.h:797 +msgid "Ctrl+4" +msgstr "Ctrl+4" + +#: objects/ui_MainWindow.h:798 +msgid "Bottom" +msgstr "Zespoda" + +#: objects/ui_MainWindow.h:799 +msgid "Ctrl+5" +msgstr "Ctrl+5" + +#: objects/ui_MainWindow.h:800 +msgid "Left" +msgstr "Zleva" + +#: objects/ui_MainWindow.h:801 +msgid "Ctrl+6" +msgstr "Ctrl+6" + +#: objects/ui_MainWindow.h:802 +msgid "Right" +msgstr "Zprava" + +#: objects/ui_MainWindow.h:803 +msgid "Ctrl+7" +msgstr "Ctrl+7" + +#: objects/ui_MainWindow.h:804 +msgid "Front" +msgstr "Zepředu" + +#: objects/ui_MainWindow.h:805 +msgid "Ctrl+8" +msgstr "Ctrl+8" + +#: objects/ui_MainWindow.h:806 +msgid "Back" +msgstr "Zezadu" + +#: objects/ui_MainWindow.h:807 +msgid "Ctrl+9" +msgstr "Ctrl+9" + +#: objects/ui_MainWindow.h:808 +msgid "Diagonal" +msgstr "Diagonálně" + +#: objects/ui_MainWindow.h:809 +msgid "Ctrl+0" +msgstr "Ctrl+0" + +#: objects/ui_MainWindow.h:810 +msgid "Center" +msgstr "Vycentrovat" + +#: objects/ui_MainWindow.h:811 +msgid "Perspective" +msgstr "Perspektivně" + +#: objects/ui_MainWindow.h:812 +msgid "Orthogonal" +msgstr "Ortogonálně" + +#: objects/ui_MainWindow.h:813 +msgid "Hide console" +msgstr "Skrýt terminál" + +#: objects/ui_MainWindow.h:814 +msgid "About" +msgstr "O aplikaci" + +#: objects/ui_MainWindow.h:815 +msgid "Documentation" +msgstr "Dokumentace" + +#: objects/ui_MainWindow.h:816 +msgid "Clear Recent" +msgstr "Zapomenout nedávné" + +#: objects/ui_MainWindow.h:817 +msgid "Export as DXF..." +msgstr "Exportovat jako &DXF" + +#: objects/ui_MainWindow.h:818 objects/ui_OpenCSGWarningDialog.h:94 +msgid "Close" +msgstr "Zavřít" + +#: objects/ui_MainWindow.h:819 +msgid "Ctrl+W" +msgstr "Ctrl+W" + +#: objects/ui_MainWindow.h:820 objects/ui_Preferences.h:608 +msgid "Preferences" +msgstr "Předvolby" + +#: objects/ui_MainWindow.h:821 +msgid "Find..." +msgstr "Najít..." + +#: objects/ui_MainWindow.h:822 +msgid "Ctrl+F" +msgstr "Ctrl+F" + +#: objects/ui_MainWindow.h:823 +msgid "Find and Replace..." +msgstr "Najít a nahradit..." + +#: objects/ui_MainWindow.h:824 +msgid "Ctrl+Alt+F" +msgstr "Ctrl+Alt+F" + +#: objects/ui_MainWindow.h:825 +msgid "Find Next" +msgstr "Najít další" + +#: objects/ui_MainWindow.h:826 +msgid "Ctrl+G" +msgstr "Ctrl+G" + +#: objects/ui_MainWindow.h:827 +msgid "Find Previous" +msgstr "Najít předchozí" + +#: objects/ui_MainWindow.h:828 +msgid "Ctrl+Shift+G" +msgstr "Ctrl+Shift+G" + +#: objects/ui_MainWindow.h:829 +msgid "Use Selection for Find" +msgstr "Hledat vybraný řetězec" + +#: objects/ui_MainWindow.h:830 +msgid "Ctrl+E" +msgstr "Ctrl+E" + +#: objects/ui_MainWindow.h:831 +msgid "Flush Caches" +msgstr "Vyprázdnit mezipaměť" + +#: objects/ui_MainWindow.h:832 +msgid "OpenSCAD Homepage" +msgstr "Domovská stránka OpenSCADu" + +#: objects/ui_MainWindow.h:833 +msgid "Automatic Reload and Preview" +msgstr "Automaticky načítat a zobrazovat" + +#: objects/ui_MainWindow.h:834 +msgid "Export as Image..." +msgstr "Exportovat jako obrázek..." + +#: objects/ui_MainWindow.h:835 +msgid "Export as CSG..." +msgstr "Exportovat jako CSG..." + +#: objects/ui_MainWindow.h:836 +msgid "Library info" +msgstr "Informace o knihovnách" + +#: objects/ui_MainWindow.h:837 +msgid "Show Library Folder..." +msgstr "Adresář s knihovnami..." + +#: objects/ui_MainWindow.h:838 +msgid "Reset View" +msgstr "Výchozí pohled" + +#: objects/ui_MainWindow.h:839 +msgid "Font List" +msgstr "Seznam písem" + +#: objects/ui_MainWindow.h:840 +msgid "Export as SVG..." +msgstr "Exportovat jako SVG..." + +#: objects/ui_MainWindow.h:841 +msgid "Export as AMF..." +msgstr "Exportovat jako AMF..." + +#: objects/ui_MainWindow.h:843 +msgid "Ctrl+]" +msgstr "Ctrl+]" + +#: objects/ui_MainWindow.h:845 +msgid "Ctrl+[" +msgstr "Ctrl+[" + +#: objects/ui_MainWindow.h:846 +msgid "View All" +msgstr "Zobrazit vše" + +#: objects/ui_MainWindow.h:847 +msgid "Convert Tabs to Spaces" +msgstr "Převést tabulátory na mezery" + +#: objects/ui_MainWindow.h:848 +msgid "Hide toolbars" +msgstr "Skrýt nástrojové lišty" + +#: objects/ui_MainWindow.h:849 +msgid "Time:" +msgstr "Čas:" + +#: objects/ui_MainWindow.h:850 +msgid "FPS:" +msgstr "FPS:" + +#: objects/ui_MainWindow.h:851 +msgid "Steps:" +msgstr "Kroky:" + +#: objects/ui_MainWindow.h:852 +msgid "Dump Pictures" +msgstr "Ukládat obrázky" + +#: objects/ui_MainWindow.h:853 +msgid "&File" +msgstr "&Soubor" + +#: objects/ui_MainWindow.h:854 +msgid "Recent Files" +msgstr "Nedávné soubory" + +#: objects/ui_MainWindow.h:856 +msgid "Export" +msgstr "Exportovat" + +#: objects/ui_MainWindow.h:857 +msgid "&Edit" +msgstr "&Upravit" + +#: objects/ui_MainWindow.h:858 +msgid "&Design" +msgstr "&Design" + +#: objects/ui_MainWindow.h:859 +msgid "&View" +msgstr "&Zobrazit" + +#: objects/ui_MainWindow.h:860 +msgid "&Help" +msgstr "&Nápověda" + +#: objects/ui_MainWindow.h:863 +msgid "Find" +msgstr "Najít" + +#: objects/ui_MainWindow.h:864 objects/ui_MainWindow.h:871 +msgid "Replace" +msgstr "Nahradit" + +#: objects/ui_MainWindow.h:866 +msgid "Search string" +msgstr "Hledaný řetězec" + +#: objects/ui_MainWindow.h:867 +msgid "<" +msgstr "<" + +#: objects/ui_MainWindow.h:868 +msgid ">" +msgstr ">" + +#: objects/ui_MainWindow.h:869 +msgid "Done" +msgstr "Hotovo" + +#: objects/ui_MainWindow.h:870 +msgid "Replacement string" +msgstr "Nahradit za" + +#: objects/ui_MainWindow.h:872 +msgid "All" +msgstr "Vše" + +#: objects/ui_OpenCSGWarningDialog.h:86 +msgid "OpenGL Warning" +msgstr "OpenGL varování" + +#: objects/ui_OpenCSGWarningDialog.h:87 +msgid "" +"\n" +"\n" +"

" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:92 +msgid "Enable OpenCSG" +msgstr "Povolit OpenCSG" + +#: objects/ui_OpenCSGWarningDialog.h:93 +msgid "Show this message again" +msgstr "Zobrazit tuto zprávu znovu" + +#: objects/ui_Preferences.h:609 +msgid "3D View" +msgstr "3D zobrazení" + +#: objects/ui_Preferences.h:610 src/UIUtils.cc:85 +msgid "Advanced" +msgstr "Pokročilé" + +#: objects/ui_Preferences.h:611 src/mainwin.cc:2315 +msgid "Editor" +msgstr "Editor" + +#: objects/ui_Preferences.h:612 +msgid "Update" +msgstr "Aktualizace" + +#: objects/ui_Preferences.h:613 objects/ui_Preferences.h:633 +msgid "Features" +msgstr "Funkce" + +#: objects/ui_Preferences.h:615 +msgid "Enable/Disable experimental features" +msgstr "Povolit/Zakázat experimentální funkce" + +#: objects/ui_Preferences.h:617 +msgid "Color scheme:" +msgstr "Barevné téma:" + +#: objects/ui_Preferences.h:618 +msgid "Editor Type" +msgstr "Varianta editoru" + +#: objects/ui_Preferences.h:621 +msgid "Simple Editor" +msgstr "Jednoduchý editor" + +#: objects/ui_Preferences.h:622 +msgid "QScintilla Editor" +msgstr "Editor QScintilla" + +#: objects/ui_Preferences.h:624 +msgid "(requires restart)" +msgstr "(vyžaduje restart aplikace)" + +#: objects/ui_Preferences.h:625 +msgid "Font" +msgstr "Písmo" + +#: objects/ui_Preferences.h:626 +msgid "Color syntax highlighting" +msgstr "Barva zvýrazňování syntaxe" + +#: objects/ui_Preferences.h:627 +msgid "Use Ctrl/Cmd-Mouse-wheel to zoom text" +msgstr "Použít Ctrl/Cmd a kolečko myši k změně velikosti písma" + +#: objects/ui_Preferences.h:629 +msgid "Automatically check for updates" +msgstr "Automaticky vyhledávat aktualizace" + +#: objects/ui_Preferences.h:630 +msgid "Include development snapshots" +msgstr "Včetně nestabilních verzí" + +#: objects/ui_Preferences.h:631 +msgid "Check Now" +msgstr "Zkontrolovat nyní" + +#: objects/ui_Preferences.h:632 +msgid "Last checked: " +msgstr "Poslední kontrola:" + +#: objects/ui_Preferences.h:634 +msgid "OpenCSG" +msgstr "OpenCSG" + +#: objects/ui_Preferences.h:635 +msgid "Show capability warning" +msgstr "Zobrazovat varování o vlastnostech" + +#: objects/ui_Preferences.h:636 +msgid "Enable for OpenGL 1.x" +msgstr "Povolit pro OpenGL 1.x" + +#: objects/ui_Preferences.h:637 +msgid "Turn off rendering at " +msgstr "Vypnout renderování při" + +#: objects/ui_Preferences.h:638 +msgid "elements" +msgstr "prvcích" + +#: objects/ui_Preferences.h:639 +msgid "Force Goldfeather" +msgstr "Vynutit zobrazení Goldfeather" + +#: objects/ui_Preferences.h:640 +msgid "CGAL Cache size" +msgstr "Velikost CGAL cache" + +#: objects/ui_Preferences.h:641 objects/ui_Preferences.h:643 +msgid "bytes" +msgstr "bytů" + +#: objects/ui_Preferences.h:642 +msgid "PolySet Cache size" +msgstr "Velikost PolySet cache" + +#: objects/ui_Preferences.h:644 +msgid "Allow to open multiple documents" +msgstr "Povolit současné otevření více dokumentů" + +#: objects/ui_Preferences.h:645 +msgid "Enable docking of Editor and Console in different places" +msgstr "Povolit zaparkování editoru a konzole na různá místa" + +#: objects/ui_Preferences.h:646 +msgid "Enable undocking of Editor and Console to separate windows" +msgstr "Povolit plovoucí editor a konzoli" + +#: objects/ui_Preferences.h:647 +msgid "Show Welcome Screen" +msgstr "Zobrazovat uvítací obrazovku" + +#: objects/ui_Preferences.h:648 +msgid "Enable user interface localization (requires restart of OpenSCAD)" +msgstr "Povolit lokalizaci rozhraní OpenSCADu (vyžaduje restart aplikace)" + +#: objects/ui_Preferences.h:649 +msgid "toolBar" +msgstr "" + +#: objects/ui_ProgressWidget.h:72 +msgid "Form" +msgstr "" + +#: objects/ui_ProgressWidget.h:73 +msgid "%v / %m" +msgstr "%v / %m" + +#: src/AboutDialog.h:15 +msgid "About OpenSCAD " +msgstr "O OpenSCADu " + +#: src/mainwin.cc:773 src/mainwin.cc:1315 +msgid "Untitled.scad" +msgstr "Bezejmenný.scad" + +#: src/mainwin.cc:1299 +msgid "Save File" +msgstr "Uložit soubor" + +#: src/mainwin.cc:1301 +msgid "OpenSCAD Designs (*.scad)" +msgstr "OpenSCAD designy(*.scad)" + +#: src/mainwin.cc:1311 +msgid "" +"%1 already exists.\n" +"Do you want to replace it?" +msgstr "" +"%1 již existuje.\n" +"Chcete jej nahradit?" + +#: src/mainwin.cc:1630 +msgid "Application" +msgstr "Aplikace" + +#: src/mainwin.cc:1631 +msgid "" +"The document has been modified.\n" +"Do you really want to reload the file?" +msgstr "" +"Dokument byl pozměněn.\n" +"Opravdu jej chcete znovu načíst?" + +#: src/mainwin.cc:1942 src/mainwin.cc:1999 +msgid "Export %1 File" +msgstr "Exportovat %s soubor(ů)" + +#: src/mainwin.cc:1943 src/mainwin.cc:2003 +msgid "%1 Files (*%2)" +msgstr "%1 Soubor(ů) (*%2)" + +#: src/mainwin.cc:1944 +msgid "Untitled" +msgstr "Bezejmenný" + +#: src/mainwin.cc:2001 +msgid "Untitled%1" +msgstr "Bezejmenný%1" + +#: src/mainwin.cc:2052 +msgid "Export CSG File" +msgstr "Exportovat CSG soubor" + +#: src/mainwin.cc:2053 +msgid "Untitled.csg" +msgstr "Bezejmenný.csg" + +#: src/mainwin.cc:2054 +msgid "CSG Files (*.csg)" +msgstr "CSG soubory (*.csg)" + +#: src/mainwin.cc:2080 +msgid "Export Image" +msgstr "Exportovat obrázek" + +#: src/mainwin.cc:2080 +msgid "PNG Files (*.png)" +msgstr "PNG Soubory (*.png)" + +#: src/mainwin.cc:2320 +msgid "Console" +msgstr "Konzole" + +#: src/mainwin.cc:2447 +msgid "The document has been modified." +msgstr "Dokument byl pozměněn." + +#: src/mainwin.cc:2448 +msgid "Do you want to save your changes?" +msgstr "Chcete uložit změny?" + +#: src/QGLView.cc:114 +msgid "" +"\n" +"Using QGLWidget\n" +"\n" +msgstr "" +"\n" +"Používám QGLWidget\n" +"\n" + +#: src/QGLView.cc:131 +msgid "" +"Warning: You may experience OpenCSG rendering errors.\n" +"\n" +msgstr "" +"Varování: Můžete zaznamenat chyby zobrazení pomocí OpenCSG.\n" +"\n" + +#: src/QGLView.cc:134 +msgid "" +"Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been " +"disabled.\n" +"\n" +msgstr "" +"Varování: Chybí OpenGL funkce pro OpenCSG - OpenCSG bylo vypnuto.\n" +"\n" + +#: src/QGLView.cc:137 +msgid "" +"It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 or " +"later.\n" +"Your renderer information is as follows:\n" +msgstr "" +"Vysoce doporučujeme používat OpenSCAD na systému s OpenGL 2.0 nebo novější.\n" +"Zde je informace o vašem vykreslovacím systému:\n" + +#: src/QGLView.cc:141 +#, c-format +msgid "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" +msgstr "" +"GLEW verze %s\n" +"%s (%s)\n" +"OpenGL verze %s\n" + +#: src/QGLView.cc:171 +#, c-format +msgid "" +"Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], " +"distance = %.2f" +msgstr "" +"Pohled: posun = [ %.2f %.2f %.2f ], rotace = [ %.2f %.2f %.2f ], vzdálenost " +"= %.2f" + +#: src/UIUtils.cc:85 +msgid "Basics" +msgstr "Základy" + +#: src/UIUtils.cc:85 +msgid "Shapes" +msgstr "Tvary" + +#: src/UIUtils.cc:85 +msgid "Extrusion" +msgstr "Vytažení" + +#~ msgid "Paste font selector to Editor Window" +#~ msgstr "Vložit výběr písma do zdrojového kódu" + +#~ msgid "Check for Update.." +#~ msgstr "Zkontrolovat aktualizaci..." + +#~ msgid "Cornfield" +#~ msgstr "Kukuřice" + +#~ msgid "Metallic" +#~ msgstr "Kov" + +#~ msgid "Sunset" +#~ msgstr "Západ slunce" + +#~ msgid "&Compile" +#~ msgstr "Z&kompilovat" + +#~ msgid "Compile and &Render (CGAL)" +#~ msgstr "Zkompilovat a vy&renderovat" + +#~ msgid "CGAL Grid Only" +#~ msgstr "CGAL mřížka" + +#~ msgid "Editor for SCAD code" +#~ msgstr "Editor SCAD kódu" + +#~ msgid "OpenGL preview of SCAD model" +#~ msgstr "OpenGL náhled SCAD modelu" + +#~ msgid "Console messages" +#~ msgstr "Zprávy terminálu" + +#~ msgid " vertices:" +#~ msgstr "vrcholy:" + +#~ msgid "{ Outer boundary = " +#~ msgstr "{ Vnější hranice = " + +#~ msgid "{ Unbounded polygon." +#~ msgstr "{ Neohraničený polygon." + +#~ msgid " holes:" +#~ msgstr "díry:" + +#~ msgid " Hole #" +#~ msgstr " Díra #" + +#~ msgid "" +#~ "WARNING: minkowski() and hull() is not implemented for 2d objects with " +#~ "holes!" +#~ msgstr "" +#~ "VAROVÁNÍ: minkowski() a hull() nejsou implementovány pro 2D objekty s " +#~ "dírami" + +#~ msgid "WARNING: minkowski() could not get any points from object 1!" +#~ msgstr "VAROVÁNÍ: minkowski() nenalezl žádné body v objektu 1!" + +#~ msgid "WARNING: minkowski() could not get any points from object 2!" +#~ msgstr "VAROVÁNÍ: minkowski() nenalezl žádné body v objektu 2!" + +#~ msgid "CGAL error in CGAL_Nef_polyhedron's %s operator: %s" +#~ msgstr "CGAL chyba v CGAL_Nef_polyhedron operátoru %s: %s" + +#~ msgid "WARNING: hull() does not support mixing 2D and 3D objects." +#~ msgstr "VAROVÁNÍ: hull() neumožňuje kombinovat 2D a 3D objekty." + +#~ msgid "" +#~ "Hull() currently requires a valid 2-manifold. Please modify your design. " +#~ "See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/" +#~ "STL_Import_and_Export" +#~ msgstr "" +#~ "Hull() nyní podporuje jen validní 2-manifold objekty. Upravte svůj " +#~ "design. Viz http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/" +#~ "STL_Import_and_Export" + +#~ msgid "Rendering cancelled." +#~ msgstr "Renderování zrušeno." + +#~ msgid "" +#~ "DEPRECATED: The %s() module will be removed in future releases. Use %s() " +#~ "instead." +#~ msgstr "" +#~ "ZASTARALÉ: Zápis %s() bude v dalších verzích odstraněn. Místo něj " +#~ "použijte %s()." + +#~ msgid "OpenSCAD - New Document[*]" +#~ msgstr "OpenSCAD - Nový dokument[*]" + +#~ msgid "Failed to open file %s: %s" +#~ msgstr "Nepodařilo se otevřít soubor %s: %s" + +#~ msgid "Loaded design '%s'." +#~ msgstr "Načten design '%s'." + +#~ msgid "ERROR: Compilation failed! (no top level object found)" +#~ msgstr "CHYBA: Kompilace selhala! (nenalezen hlavní objekt)" + +#~ msgid "ERROR: Compilation failed!" +#~ msgstr "CHYBA: Kompilace selhala!" + +#~ msgid "yes" +#~ msgstr "ano" + +#~ msgid "no" +#~ msgstr "ne" + +#~ msgid " Vertices: %6d" +#~ msgstr " Vrcholů: %6d" + +#~ msgid " Halfedges: %6d" +#~ msgstr " Půlhran: %6d" + +#~ msgid " Edges: %6d" +#~ msgstr " Hran: %6d" + +#~ msgid " Faces: %6d" +#~ msgstr " Trojúhelníků: %6d" + +#~ msgid " Simple: %6s" +#~ msgstr " Jednoduchý: %6s" + +#~ msgid " Valid: %6s" +#~ msgstr " Validní: %6s" + +#~ msgid " Facets: %6d" +#~ msgstr " Stěn: %6d" + +#~ msgid " Volumes: %6d" +#~ msgstr " Objemů: %6d" + +#~ msgid "Rendering finished." +#~ msgstr "Renderování dokončeno." + +#~ msgid "Export OFF File" +#~ msgstr "Exportovat OFF soubor" diff --git a/locale/de.po b/locale/de.po new file mode 100644 index 00000000..df48ce1c --- /dev/null +++ b/locale/de.po @@ -0,0 +1,2169 @@ +# German translations for OpenSCAD package. +# opyright (C) 2013 THE OpenSCAD'S COPYRIGHT HOLDER +# This file is distributed under the same license as the OpenSCAD package. +# Torsten Paul , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: OpenSCAD 2014.01.05\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 23:28+0100\n" +"PO-Revision-Date: 2014-12-22 23:12+0100\n" +"Last-Translator: Torsten Paul \n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.6.10\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: objects/ui_AboutDialog.h:51 src/AboutDialog.h:15 +msgid "About OpenSCAD" +msgstr "Über OpenSCAD" + +#: objects/ui_FontListDialog.h:102 +msgid "OpenSCAD Font List" +msgstr "OpenSCAD Fontliste" + +#: objects/ui_FontListDialog.h:103 objects/ui_LibraryInfoDialog.h:77 +msgid "&OK" +msgstr "OK" + +#: objects/ui_FontListDialog.h:104 +msgid "Copy to Clipboard" +msgstr "Kopieren" + +#: objects/ui_FontListDialog.h:105 +msgid "Filter:" +msgstr "Filter:" + +#: objects/ui_FontListDialog.h:106 +msgid "" +"

This list shows the fonts currently registered with " +"OpenSCAD.

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" +msgstr "" +"

Diese Liste zeigt die Fonts, die momentan mit OpenSCAD " +"registriert sind.

Beispiele:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" + +#: objects/ui_launchingscreen.h:276 +msgid "Welcome to OpenSCAD" +msgstr "Willkommen zu OpenSCAD" + +#: objects/ui_launchingscreen.h:277 +msgid "New" +msgstr "Neu" + +#: objects/ui_launchingscreen.h:278 +msgid "Open" +msgstr "Öffnen" + +#: objects/ui_launchingscreen.h:279 +msgid "Help" +msgstr "Hilfe" + +#: objects/ui_launchingscreen.h:280 +msgid "Recents" +msgstr "Zuletzt benutze Dateien" + +#: objects/ui_launchingscreen.h:281 +msgid "Open Recent" +msgstr "Datei öffnen" + +#: objects/ui_launchingscreen.h:282 objects/ui_launchingscreen.h:284 +#: objects/ui_MainWindow.h:855 +msgid "Examples" +msgstr "Beispiele" + +#: objects/ui_launchingscreen.h:285 +msgid "Open Example" +msgstr "Beispiel öffnen" + +#: objects/ui_launchingscreen.h:287 +msgid "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" +msgstr "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" + +#: objects/ui_launchingscreen.h:294 +msgid "Don't show again" +msgstr "Dialog nicht wieder anzeigen" + +#: objects/ui_LibraryInfoDialog.h:75 +msgid "Lib & Build Info" +msgstr "Versionsinformationen" + +#: objects/ui_LibraryInfoDialog.h:76 +msgid "OpenSCAD Detailed Library and Build Information" +msgstr "Detailierte Informationen über diese OpenSCAD Version" + +#: objects/ui_MainWindow.h:731 +msgid "&New" +msgstr "&Neu" + +#: objects/ui_MainWindow.h:732 +msgid "Ctrl+N" +msgstr "Ctrl+N" + +#: objects/ui_MainWindow.h:733 +msgid "&Open..." +msgstr "Ö&ffnen" + +#: objects/ui_MainWindow.h:734 +msgid "Ctrl+O" +msgstr "Ctrl+O" + +#: objects/ui_MainWindow.h:735 +msgid "&Save" +msgstr "&Speichern" + +#: objects/ui_MainWindow.h:736 +msgid "Ctrl+S" +msgstr "Ctrl+S" + +#: objects/ui_MainWindow.h:737 +msgid "Save &As..." +msgstr "Speichern &Unter..." + +#: objects/ui_MainWindow.h:738 +msgid "Ctrl+Shift+S" +msgstr "Ctrl+Shift+S" + +#: objects/ui_MainWindow.h:739 +msgid "&Reload" +msgstr "Neu &laden" + +#: objects/ui_MainWindow.h:740 +msgid "Ctrl+R" +msgstr "Ctrl+R" + +#: objects/ui_MainWindow.h:741 +msgid "&Quit" +msgstr "&Beenden" + +#: objects/ui_MainWindow.h:742 +msgid "Ctrl+Q" +msgstr "Ctrl+Q" + +#: objects/ui_MainWindow.h:743 +msgid "&Undo" +msgstr "&Rückgängig" + +#: objects/ui_MainWindow.h:744 +msgid "Ctrl+Z" +msgstr "Ctrl+Z" + +#: objects/ui_MainWindow.h:745 +msgid "&Redo" +msgstr "&Wiederholen" + +#: objects/ui_MainWindow.h:746 +msgid "Ctrl+Shift+Z" +msgstr "Ctrl+Shift+Z" + +#: objects/ui_MainWindow.h:747 +msgid "Cu&t" +msgstr "Ausschneiden" + +#: objects/ui_MainWindow.h:748 +msgid "Ctrl+X" +msgstr "Ctrl+X" + +#: objects/ui_MainWindow.h:749 +msgid "&Copy" +msgstr "Kopieren" + +#: objects/ui_MainWindow.h:750 +msgid "Ctrl+C" +msgstr "Ctrl+C" + +#: objects/ui_MainWindow.h:751 +msgid "&Paste" +msgstr "Einfügen" + +#: objects/ui_MainWindow.h:752 +msgid "Ctrl+V" +msgstr "Ctrl+V" + +#: objects/ui_MainWindow.h:753 +msgid "&Indent" +msgstr "Einzug erhöhen" + +#: objects/ui_MainWindow.h:754 +msgid "Ctrl+I" +msgstr "Ctrl+I" + +#: objects/ui_MainWindow.h:755 +msgid "U&nindent" +msgstr "Einzug vermindern" + +#: objects/ui_MainWindow.h:756 +msgid "Ctrl+Shift+I" +msgstr "Ctrl+Shift+I" + +#: objects/ui_MainWindow.h:757 +msgid "C&omment" +msgstr "K&ommentieren" + +#: objects/ui_MainWindow.h:758 +msgid "Ctrl+D" +msgstr "Ctrl+D" + +#: objects/ui_MainWindow.h:759 +msgid "Unco&mment" +msgstr "Kommentar entfernen" + +#: objects/ui_MainWindow.h:760 +msgid "Ctrl+Shift+D" +msgstr "Ctrl+Shift+D" + +#: objects/ui_MainWindow.h:761 +msgid "Paste viewport translation" +msgstr "Aktuelle Verschiebung einfügen" + +#: objects/ui_MainWindow.h:762 +msgid "Ctrl+T" +msgstr "Ctrl+T" + +#: objects/ui_MainWindow.h:763 +msgid "Paste viewport rotation" +msgstr "Aktuelle Rotation einfügen" + +#: objects/ui_MainWindow.h:764 objects/ui_MainWindow.h:842 +msgid "Zoom In" +msgstr "Vergrößern" + +#: objects/ui_MainWindow.h:765 +msgid "Ctrl++" +msgstr "Ctrl++" + +#: objects/ui_MainWindow.h:766 objects/ui_MainWindow.h:844 +msgid "Zoom Out" +msgstr "Verkleinern" + +#: objects/ui_MainWindow.h:767 +msgid "Ctrl+-" +msgstr "Ctrl+-" + +#: objects/ui_MainWindow.h:768 +msgid "Hide editor" +msgstr "Editor verstecken" + +#: objects/ui_MainWindow.h:769 +msgid "&Reload and Preview" +msgstr "Neu laden und Vorschau" + +#: objects/ui_MainWindow.h:770 +msgid "F4" +msgstr "F4" + +#: objects/ui_MainWindow.h:771 +msgid "&Preview" +msgstr "Vorschau" + +#: objects/ui_MainWindow.h:772 +msgid "F5" +msgstr "F5" + +#: objects/ui_MainWindow.h:773 +msgid "&Render" +msgstr "Rendern" + +#: objects/ui_MainWindow.h:774 +msgid "F6" +msgstr "F6" + +#: objects/ui_MainWindow.h:775 +msgid "Check Validity" +msgstr "Design überprüfen" + +#: objects/ui_MainWindow.h:776 +msgid "Display &AST..." +msgstr "&AST Baum anzeigen..." + +#: objects/ui_MainWindow.h:777 +msgid "Display CSG &Tree..." +msgstr "CSG Baum anzeigen..." + +#: objects/ui_MainWindow.h:778 +msgid "Display CSG &Products..." +msgstr "CSG Gleichungen anzeigen..." + +#: objects/ui_MainWindow.h:779 +msgid "Export as &STL..." +msgstr "&STL exportieren..." + +#: objects/ui_MainWindow.h:780 +msgid "Export as &OFF..." +msgstr "&OFF exportieren..." + +#: objects/ui_MainWindow.h:781 +msgid "Preview" +msgstr "Vorschau" + +#: objects/ui_MainWindow.h:782 +msgid "F9" +msgstr "F9" + +#: objects/ui_MainWindow.h:783 +msgid "Surfaces" +msgstr "Flächen anzeigen" + +#: objects/ui_MainWindow.h:784 +msgid "F10" +msgstr "F10" + +#: objects/ui_MainWindow.h:785 +msgid "Wireframe" +msgstr "Gittermodell" + +#: objects/ui_MainWindow.h:786 +msgid "F11" +msgstr "F11" + +#: objects/ui_MainWindow.h:787 +msgid "Thrown Together" +msgstr "Kombinierte Anzeige" + +#: objects/ui_MainWindow.h:788 +msgid "F12" +msgstr "F12" + +#: objects/ui_MainWindow.h:789 +msgid "Show Edges" +msgstr "Kanten anzeigen" + +#: objects/ui_MainWindow.h:790 +msgid "Ctrl+1" +msgstr "Ctrl+1" + +#: objects/ui_MainWindow.h:791 +msgid "Show Axes" +msgstr "Koordinatenachsen anzeigen" + +#: objects/ui_MainWindow.h:792 +msgid "Ctrl+2" +msgstr "Ctrl+2" + +#: objects/ui_MainWindow.h:793 +msgid "Show Crosshairs" +msgstr "Rotationsmittelpunkt anzeigen" + +#: objects/ui_MainWindow.h:794 +msgid "Ctrl+3" +msgstr "Ctrl+3" + +#: objects/ui_MainWindow.h:795 +msgid "Animate" +msgstr "Animation" + +#: objects/ui_MainWindow.h:796 +msgid "Top" +msgstr "Oben" + +#: objects/ui_MainWindow.h:797 +msgid "Ctrl+4" +msgstr "Ctrl+4" + +#: objects/ui_MainWindow.h:798 +msgid "Bottom" +msgstr "Unten" + +#: objects/ui_MainWindow.h:799 +msgid "Ctrl+5" +msgstr "Ctrl+5" + +#: objects/ui_MainWindow.h:800 +msgid "Left" +msgstr "Links" + +#: objects/ui_MainWindow.h:801 +msgid "Ctrl+6" +msgstr "Ctrl+6" + +#: objects/ui_MainWindow.h:802 +msgid "Right" +msgstr "Rechts" + +#: objects/ui_MainWindow.h:803 +msgid "Ctrl+7" +msgstr "Ctrl+7" + +#: objects/ui_MainWindow.h:804 +msgid "Front" +msgstr "Vorn" + +#: objects/ui_MainWindow.h:805 +msgid "Ctrl+8" +msgstr "Ctrl+8" + +#: objects/ui_MainWindow.h:806 +msgid "Back" +msgstr "Hinten" + +#: objects/ui_MainWindow.h:807 +msgid "Ctrl+9" +msgstr "Ctrl+9" + +#: objects/ui_MainWindow.h:808 +msgid "Diagonal" +msgstr "Diagonal" + +#: objects/ui_MainWindow.h:809 +msgid "Ctrl+0" +msgstr "Ctrl+0" + +#: objects/ui_MainWindow.h:810 +msgid "Center" +msgstr "Zentriert" + +#: objects/ui_MainWindow.h:811 +msgid "Perspective" +msgstr "Perspektivisch" + +#: objects/ui_MainWindow.h:812 +msgid "Orthogonal" +msgstr "Orthogonal" + +#: objects/ui_MainWindow.h:813 +msgid "Hide console" +msgstr "Konsole verstecken" + +#: objects/ui_MainWindow.h:814 +msgid "About" +msgstr "Über OpenSCAD" + +#: objects/ui_MainWindow.h:815 +msgid "Documentation" +msgstr "Dokumentation" + +#: objects/ui_MainWindow.h:816 +msgid "Clear Recent" +msgstr "Zuletzt benutze Dateien löschen" + +#: objects/ui_MainWindow.h:817 +msgid "Export as DXF..." +msgstr "DXF exportieren..." + +#: objects/ui_MainWindow.h:818 objects/ui_OpenCSGWarningDialog.h:94 +msgid "Close" +msgstr "Schließen" + +#: objects/ui_MainWindow.h:819 +msgid "Ctrl+W" +msgstr "Ctrl+W" + +#: objects/ui_MainWindow.h:820 objects/ui_Preferences.h:608 +msgid "Preferences" +msgstr "Einstellungen" + +#: objects/ui_MainWindow.h:821 +msgid "Find..." +msgstr "Suchen..." + +#: objects/ui_MainWindow.h:822 +msgid "Ctrl+F" +msgstr "Ctrl+F" + +#: objects/ui_MainWindow.h:823 +msgid "Find and Replace..." +msgstr "Suchen und Ersetzen..." + +#: objects/ui_MainWindow.h:824 +msgid "Ctrl+Alt+F" +msgstr "Ctrl+Alt+F" + +#: objects/ui_MainWindow.h:825 +msgid "Find Next" +msgstr "Weiter suchen" + +#: objects/ui_MainWindow.h:826 +msgid "Ctrl+G" +msgstr "Ctrl+G" + +#: objects/ui_MainWindow.h:827 +msgid "Find Previous" +msgstr "Rückwärts suchen" + +#: objects/ui_MainWindow.h:828 +msgid "Ctrl+Shift+G" +msgstr "Ctrl+Shift+G" + +#: objects/ui_MainWindow.h:829 +msgid "Use Selection for Find" +msgstr "Auswahl suchen" + +#: objects/ui_MainWindow.h:830 +msgid "Ctrl+E" +msgstr "Ctrl+E" + +#: objects/ui_MainWindow.h:831 +msgid "Flush Caches" +msgstr "Cache leeren" + +#: objects/ui_MainWindow.h:832 +msgid "OpenSCAD Homepage" +msgstr "OpenSCAD Homepage" + +#: objects/ui_MainWindow.h:833 +msgid "Automatic Reload and Preview" +msgstr "Automatisch neu Laden und Vorschau" + +#: objects/ui_MainWindow.h:834 +msgid "Export as Image..." +msgstr "Image exportieren..." + +#: objects/ui_MainWindow.h:835 +msgid "Export as CSG..." +msgstr "CSG exportieren..." + +#: objects/ui_MainWindow.h:836 +msgid "Library info" +msgstr "Versionsinformationen" + +#: objects/ui_MainWindow.h:837 +msgid "Show Library Folder..." +msgstr "Bibliotheken anzeigen..." + +#: objects/ui_MainWindow.h:838 +msgid "Reset View" +msgstr "Ansicht zurücksetzen" + +#: objects/ui_MainWindow.h:839 +msgid "Font List" +msgstr "Fontliste" + +#: objects/ui_MainWindow.h:840 +msgid "Export as SVG..." +msgstr "SVG exportieren..." + +#: objects/ui_MainWindow.h:841 +msgid "Export as AMF..." +msgstr "AMF exportieren..." + +#: objects/ui_MainWindow.h:843 +msgid "Ctrl+]" +msgstr "Ctrl+]" + +#: objects/ui_MainWindow.h:845 +msgid "Ctrl+[" +msgstr "Ctrl+[" + +#: objects/ui_MainWindow.h:846 +msgid "View All" +msgstr "Alles anzeigen" + +#: objects/ui_MainWindow.h:847 +msgid "Convert Tabs to Spaces" +msgstr "Tabs in Leerzeichen umwandeln" + +#: objects/ui_MainWindow.h:848 +msgid "Hide toolbars" +msgstr "Symbolleisten verstecken" + +#: objects/ui_MainWindow.h:849 +msgid "Time:" +msgstr "Zeit:" + +#: objects/ui_MainWindow.h:850 +msgid "FPS:" +msgstr "FPS:" + +#: objects/ui_MainWindow.h:851 +msgid "Steps:" +msgstr "Schritte:" + +#: objects/ui_MainWindow.h:852 +msgid "Dump Pictures" +msgstr "Bilder ausgeben" + +#: objects/ui_MainWindow.h:853 +msgid "&File" +msgstr "&Datei" + +#: objects/ui_MainWindow.h:854 +msgid "Recent Files" +msgstr "Zuletzt benutze Dateien" + +#: objects/ui_MainWindow.h:856 +msgid "Export" +msgstr "Exportieren" + +#: objects/ui_MainWindow.h:857 +msgid "&Edit" +msgstr "&Bearbeiten" + +#: objects/ui_MainWindow.h:858 +msgid "&Design" +msgstr "D&esign" + +#: objects/ui_MainWindow.h:859 +msgid "&View" +msgstr "&Ansicht" + +#: objects/ui_MainWindow.h:860 +msgid "&Help" +msgstr "&Hilfe" + +#: objects/ui_MainWindow.h:863 +msgid "Find" +msgstr "Suchen" + +#: objects/ui_MainWindow.h:864 objects/ui_MainWindow.h:871 +msgid "Replace" +msgstr "Ersetzen" + +#: objects/ui_MainWindow.h:866 +msgid "Search string" +msgstr "Suchtext" + +#: objects/ui_MainWindow.h:867 +msgid "<" +msgstr "<" + +#: objects/ui_MainWindow.h:868 +msgid ">" +msgstr ">" + +#: objects/ui_MainWindow.h:869 +msgid "Done" +msgstr "Fertig" + +#: objects/ui_MainWindow.h:870 +msgid "Replacement string" +msgstr "Ersetzen mit" + +#: objects/ui_MainWindow.h:872 +msgid "All" +msgstr "Alles" + +#: objects/ui_OpenCSGWarningDialog.h:86 +msgid "OpenGL Warning" +msgstr "OpenGL Warnung" + +#: objects/ui_OpenCSGWarningDialog.h:87 +msgid "" +"\n" +"\n" +"

" +msgstr "" +"\n" +"\n" +"

" + +#: objects/ui_OpenCSGWarningDialog.h:92 +msgid "Enable OpenCSG" +msgstr "OpenCSG benutzen" + +#: objects/ui_OpenCSGWarningDialog.h:93 +msgid "Show this message again" +msgstr "Diesen Dialog wieder anzeigen" + +#: objects/ui_Preferences.h:609 +msgid "3D View" +msgstr "3D Ansicht" + +#: objects/ui_Preferences.h:610 src/UIUtils.cc:85 +msgid "Advanced" +msgstr "Erweitert" + +#: objects/ui_Preferences.h:611 src/mainwin.cc:2315 +msgid "Editor" +msgstr "Editor" + +#: objects/ui_Preferences.h:612 +msgid "Update" +msgstr "Aktualisieren" + +#: objects/ui_Preferences.h:613 objects/ui_Preferences.h:633 +msgid "Features" +msgstr "Funktionen" + +#: objects/ui_Preferences.h:615 +msgid "Enable/Disable experimental features" +msgstr "Experimentelle Erweiterungen ein-/ausschalten" + +#: objects/ui_Preferences.h:617 +msgid "Color scheme:" +msgstr "Farbschema:" + +#: objects/ui_Preferences.h:618 +msgid "Editor Type" +msgstr "Editor" + +#: objects/ui_Preferences.h:621 +msgid "Simple Editor" +msgstr "Einfacher Editor" + +#: objects/ui_Preferences.h:622 +msgid "QScintilla Editor" +msgstr "QScintilla Editor" + +#: objects/ui_Preferences.h:624 +msgid "(requires restart)" +msgstr "(Neustart erforderlich)" + +#: objects/ui_Preferences.h:625 +msgid "Font" +msgstr "Font" + +#: objects/ui_Preferences.h:626 +msgid "Color syntax highlighting" +msgstr "Syntaxhervorhebung" + +#: objects/ui_Preferences.h:627 +msgid "Use Ctrl/Cmd-Mouse-wheel to zoom text" +msgstr "Ctrl/Cmd+Mausrad zum Vergrößern des Textes benutzen" + +#: objects/ui_Preferences.h:629 +msgid "Automatically check for updates" +msgstr "Automatisch nach Aktualisierungen suchen" + +#: objects/ui_Preferences.h:630 +msgid "Include development snapshots" +msgstr "Entwickler-Versionen einschließen" + +#: objects/ui_Preferences.h:631 +msgid "Check Now" +msgstr "Jetzt suchen" + +#: objects/ui_Preferences.h:632 +msgid "Last checked: " +msgstr "Zuletzt gesucht: " + +#: objects/ui_Preferences.h:634 +msgid "OpenCSG" +msgstr "OpenCSG" + +#: objects/ui_Preferences.h:635 +msgid "Show capability warning" +msgstr "Kompatibilitätswarnung anzeigen" + +#: objects/ui_Preferences.h:636 +msgid "Enable for OpenGL 1.x" +msgstr "Aktivieren bei OpenGL 1.x" + +#: objects/ui_Preferences.h:637 +msgid "Turn off rendering at " +msgstr "Rendern abbrechen ab " + +#: objects/ui_Preferences.h:638 +msgid "elements" +msgstr "Elementen" + +#: objects/ui_Preferences.h:639 +msgid "Force Goldfeather" +msgstr "Goldfeather Algorithmus erzwingen" + +#: objects/ui_Preferences.h:640 +msgid "CGAL Cache size" +msgstr "CGAL Cache Größe" + +#: objects/ui_Preferences.h:641 objects/ui_Preferences.h:643 +msgid "bytes" +msgstr "Byte" + +#: objects/ui_Preferences.h:642 +msgid "PolySet Cache size" +msgstr "PolySet Cache Größe" + +#: objects/ui_Preferences.h:644 +msgid "Allow to open multiple documents" +msgstr "Öffnen von mehreren Dokumenten erlauben" + +#: objects/ui_Preferences.h:645 +msgid "Enable docking of Editor and Console in different places" +msgstr "Verschieben des Editor und Konsole Fensters erlauben" + +#: objects/ui_Preferences.h:646 +msgid "Enable undocking of Editor and Console to separate windows" +msgstr "Separate Editor und Konsole Fenster erlauben" + +#: objects/ui_Preferences.h:647 +msgid "Show Welcome Screen" +msgstr "Startbildschirm anzeigen" + +#: objects/ui_Preferences.h:648 +msgid "Enable user interface localization (requires restart of OpenSCAD)" +msgstr "Lokalisierung der GUI einschalten (benötigt Neustart von OpenSCAD)" + +# This should not be visible (window title of the ToolBar). +#: objects/ui_Preferences.h:649 +msgid "toolBar" +msgstr "toolBar" + +#: objects/ui_ProgressWidget.h:72 +msgid "Form" +msgstr "Form" + +#: objects/ui_ProgressWidget.h:73 +msgid "%v / %m" +msgstr "%v / %m" + +#: src/mainwin.cc:768 src/mainwin.cc:1300 +msgid "Untitled.scad" +msgstr "Unbenannt.scad" + +#: src/mainwin.cc:1299 +msgid "Save File" +msgstr "Datei speichern" + +#: src/mainwin.cc:1301 +msgid "OpenSCAD Designs (*.scad)" +msgstr "OpenSCAD Designs (*.scad)" + +#: src/mainwin.cc:1311 +msgid "" +"%1 already exists.\n" +"Do you want to replace it?" +msgstr "" +"%1 existiert bereits.\n" +"Mochten Sie die Datei ersetzen?" + +#: src/mainwin.cc:1630 +msgid "Application" +msgstr "Application" + +#: src/mainwin.cc:1631 +msgid "" +"The document has been modified.\n" +"Do you really want to reload the file?" +msgstr "" +"Das Dokument ist verändert.\n" +"Möchten Sie die Datei wirklich neu laden?" + +#: src/mainwin.cc:1942 src/mainwin.cc:1999 +msgid "Export %1 File" +msgstr "%1 Datei exportieren" + +#: src/mainwin.cc:1943 src/mainwin.cc:2003 +msgid "%1 Files (*%2)" +msgstr "%1 Dateien (*%2)" + +#: src/mainwin.cc:1944 +msgid "Untitled" +msgstr "Unbenannt" + +#: src/mainwin.cc:2001 +msgid "Untitled%1" +msgstr "Unbenannt%1" + +#: src/mainwin.cc:2052 +msgid "Export CSG File" +msgstr "Export CSG File" + +#: src/mainwin.cc:2053 +msgid "Untitled.csg" +msgstr "Unbenannt.csg" + +#: src/mainwin.cc:2054 +msgid "CSG Files (*.csg)" +msgstr "CSG Dateien (*.csg)" + +#: src/mainwin.cc:2080 +msgid "Export Image" +msgstr "Image exportieren" + +#: src/mainwin.cc:2080 +msgid "PNG Files (*.png)" +msgstr "PNG Dateien (*.png)" + +#: src/mainwin.cc:2320 +msgid "Console" +msgstr "Konsole" + +#: src/mainwin.cc:2447 +msgid "The document has been modified." +msgstr "Das Dokument ist verändert." + +#: src/mainwin.cc:2448 +msgid "Do you want to save your changes?" +msgstr "Möchten Sie die Änderungen speichern?" + +#: src/QGLView.cc:114 +msgid "" +"\n" +"Using QGLWidget\n" +"\n" +msgstr "" +"\n" +"Benutze QGLWidget\n" +"\n" + +#: src/QGLView.cc:131 +msgid "" +"Warning: You may experience OpenCSG rendering errors.\n" +"\n" +msgstr "" +"Achtung: Es können Darstellungsfehler beim Anzeigen der Vorschau auftreten.\n" +"\n" + +#: src/QGLView.cc:134 +msgid "" +"Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been " +"disabled.\n" +"\n" +msgstr "" +"Achtung: Der Funktionsumfang des OpenGL Treibers reicht für die Vorschau " +"nicht aus - OpenCSG wurde deaktiviert.\n" +"\n" + +#: src/QGLView.cc:137 +msgid "" +"It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 or " +"later.\n" +"Your renderer information is as follows:\n" +msgstr "" +"Für die korrekte Anzeige der Vorschau benötigt OpenSCAD mindestens OpenGL " +"2.0.\n" +"Informationen zum OpenGL Treiber:\n" + +#: src/QGLView.cc:141 +#, c-format +msgid "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" +msgstr "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" + +#: src/QGLView.cc:171 +#, c-format +msgid "" +"Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], " +"distance = %.2f" +msgstr "" +"Ansicht: Verschiebung = [ %.2f %.2f %.2f ], Rotation = [ %.2f %.2f %.2f ], " +"Abstand = %.2f" + +#: src/UIUtils.cc:85 +msgid "Basics" +msgstr "Grundlagen" + +#: src/UIUtils.cc:85 +msgid "Shapes" +msgstr "Formen" + +#: src/UIUtils.cc:85 +msgid "Extrusion" +msgstr "Extrusion" + +#~ msgid "Paste font selector to Editor Window" +#~ msgstr "Font Selektor in Editor kopieren" + +#~ msgid "About OpenSCAD " +#~ msgstr "Über OpenSCAD" + +#~ msgid "Check for Update.." +#~ msgstr "Neue Version suchen..." + +#~ msgid "Cornfield" +#~ msgstr "Cornfield" + +#~ msgid "Metallic" +#~ msgstr "Metallic" + +#~ msgid "Sunset" +#~ msgstr "Sunset" + +#~ msgid "Starnight" +#~ msgstr "Starnight" + +#~ msgid "BeforeDawn" +#~ msgstr "BeforeDawn" + +#~ msgid "Nature" +#~ msgstr "Nature" + +#~ msgid "DeepOcean" +#~ msgstr "DeepOcean" + +#~ msgid "For Light Background" +#~ msgstr "Für hellen Hintergrund" + +#~ msgid "For Dark Background" +#~ msgstr "Für dunklen Hintergrund" + +#~ msgid "Monokai" +#~ msgstr "Monokai" + +#~ msgid "Solarized" +#~ msgstr "Solarized" + +#~ msgid "Off" +#~ msgstr "Aus" + +#~ msgid "MainWindow" +#~ msgstr "MainWindow" + +#~ msgid "Ctrl+P" +#~ msgstr "Ctrl+P" + +#~ msgid "Editor for SCAD code" +#~ msgstr "Editor for SCAD code" + +#~ msgid "Console messages" +#~ msgstr "Console messages" + +#~ msgid "Trimming cache: %1% (%2% bytes)" +#~ msgstr "Trimming cache: %1% (%2% bytes)" + +#~ msgid "WARNING: CGAL NefPolyhedron Triangulation failed: %s" +#~ msgstr "WARNING: CGAL NefPolyhedron Triangulation failed: %s" + +#~ msgid "Error: CSG generation failed! (no top level object found)" +#~ msgstr "Error: CSG generation failed! (no top level object found)" + +#~ msgid "Compiling design (CSG Products normalization)..." +#~ msgstr "Compiling design (CSG Products normalization)..." + +#~ msgid "Normalized CSG tree has %d elements" +#~ msgstr "Normalized CSG tree has %d elements" + +#~ msgid "WARNING: CSG normalization resulted in an empty tree" +#~ msgstr "WARNING: CSG normalization resulted in an empty tree" + +#~ msgid "Compiling highlights (%i CSG Trees)..." +#~ msgstr "Compiling highlights (%i CSG Trees)..." + +#~ msgid "Compiling background (%i CSG Trees)..." +#~ msgstr "Compiling background (%i CSG Trees)..." + +#~ msgid "CGAL Cache hit: %s (%d bytes)" +#~ msgstr "CGAL Cache hit: %s (%d bytes)" + +#~ msgid "CGAL Cache insert: %s (%d bytes)" +#~ msgstr "CGAL Cache insert: %s (%d bytes)" + +#~ msgid "CGAL Cache insert failed: %s (%d bytes)" +#~ msgstr "CGAL Cache insert failed: %s (%d bytes)" + +#~ msgid "CGAL Polyhedrons in cache: %d" +#~ msgstr "CGAL Polyhedrons im Cache: %d" + +#~ msgid "CGAL cache size in bytes: %d" +#~ msgstr "CGAL Cache Größe in Bytes: %d" + +#~ msgid "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed." +#~ msgstr "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed." + +#~ msgid "ERROR: %s" +#~ msgstr "ERROR: %s" + +#~ msgid "Warning: Scaling a 3D object with 0 - removing object" +#~ msgstr "Warning: Scaling a 3D object with 0 - removing object" + +#~ msgid "" +#~ "Hull() currently requires a valid 2-manifold. Please modify your design. " +#~ "See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/" +#~ "STL_Import_and_Export" +#~ msgstr "" +#~ "Hull() currently requires a valid 2-manifold. Please modify your design. " +#~ "See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/" +#~ "STL_Import_and_Export" + +#~ msgid "ERROR: Unsupported CGAL operator: %d" +#~ msgstr "ERROR: Unsupported CGAL operator: %d" + +#~ msgid "CGAL error in CGALUtils::applyBinaryOperator %s: %s" +#~ msgstr "CGAL error in CGALUtils::applyBinaryOperator %s: %s" + +#~ msgid "CGALUtils::project during plane intersection: %s" +#~ msgstr "CGALUtils::project during plane intersection: %s" + +#~ msgid "Trying alternative intersection using very large thin box: " +#~ msgstr "Trying alternative intersection using very large thin box: " + +#~ msgid "CGAL error in CGALUtils::project during bigbox intersection: %s" +#~ msgstr "CGAL error in CGALUtils::project during bigbox intersection: %s" + +#~ msgid "WARNING: projection() failed." +#~ msgstr "WARNING: projection() failed." + +#~ msgid "CGAL error in CGALUtils::project while flattening: %s" +#~ msgstr "CGAL error in CGALUtils::project while flattening: %s" + +#~ msgid "ERROR: deproject failure" +#~ msgstr "ERROR: deproject failure" + +#~ msgid "ERROR: failed to find projection" +#~ msgstr "ERROR: failed to find projection" + +#~ msgid "input polygon has 3 points. shortcut tessellation." +#~ msgstr "input polygon has 3 points. shortcut tessellation." + +#~ msgid "finding good projection" +#~ msgstr "finding good projection" + +#~ msgid "plane %s" +#~ msgstr "plane %s" + +#~ msgid "proj: %i %i" +#~ msgstr "proj: %i %i" + +#~ msgid "Inserting points and edges into Constrained Delaunay Triangulation" +#~ msgstr "Inserting points and edges into Constrained Delaunay Triangulation" + +#~ msgid "WARNING: Constraint insertion failure %s" +#~ msgstr "WARNING: Constraint insertion failure %s" + +#~ msgid "seeding %i holes" +#~ msgstr "seeding %i holes" + +#~ msgid "seed %f,%f" +#~ msgstr "seed %f,%f" + +#~ msgid "seeding done" +#~ msgstr "seeding done" + +#~ msgid "meshing" +#~ msgstr "meshing" + +#~ msgid "meshing done" +#~ msgstr "meshing done" + +#~ msgid "WARNING: 2d->3d deprojection failure" +#~ msgstr "WARNING: 2d->3d deprojection failure" + +#~ msgid "built %i triangles\n" +#~ msgstr "built %i triangles\n" + +#~ msgid "WARNING: triangle doesn't have 3 points. skipping" +#~ msgstr "WARNING: triangle doesn't have 3 points. skipping" + +#~ msgid "CGAL error in CGALUtils::createPolyhedronFromPolySet: %s" +#~ msgstr "CGAL error in CGALUtils::createPolyhedronFromPolySet: %s" + +#~ msgid "PolySet has nonplanar faces. Attempting alternate construction" +#~ msgstr "PolySet has nonplanar faces. Attempting alternate construction" + +#~ msgid "CGAL error in CGAL_Nef_polyhedron3(): %s" +#~ msgstr "CGAL error in CGAL_Nef_polyhedron3(): %s" + +#~ msgid "" +#~ "Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s" +#~ msgstr "" +#~ "Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s" + +#~ msgid "Rendering cancelled." +#~ msgstr "Rendering cancelled." + +#~ msgid "" +#~ "WARNING: color() expects numbers between 0.0 and 1.0. Value of %.1f is " +#~ "too large." +#~ msgstr "" +#~ "WARNING: color() expects numbers between 0.0 and 1.0. Value of %.1f is " +#~ "too large." + +#~ msgid "WARNING: Color name \"%s\" unknown. Please see" +#~ msgstr "WARNING: Color name \"%s\" unknown. Please see" + +#~ msgid "WARNING: http://en.wikipedia.org/wiki/Web_colors" +#~ msgstr "WARNING: http://en.wikipedia.org/wiki/Web_colors" + +#~ msgid "WARNING: Attempt to modify constant '%s'." +#~ msgstr "WARNING: Attempt to modify constant '%s'." + +#~ msgid "WARNING: Ignoring unknown variable '%s'." +#~ msgstr "WARNING: Ignoring unknown variable '%s'." + +#~ msgid "WARNING: Ignoring unknown function '%s'." +#~ msgstr "WARNING: Ignoring unknown function '%s'." + +#~ msgid "WARNING: Ignoring unknown module '%s'." +#~ msgstr "WARNING: Ignoring unknown module '%s'." + +#~ msgid "ModuleContext %p (%p) for %s inst (%p)" +#~ msgstr "ModuleContext %p (%p) for %s inst (%p)" + +#~ msgid "Context: %p (%p)" +#~ msgstr "Context: %p (%p)" + +#~ msgid " document path: %s" +#~ msgstr " document path: %s" + +#~ msgid " module args:" +#~ msgstr " module args:" + +#~ msgid " vars:" +#~ msgstr " vars:" + +#~ msgid "" +#~ "WARNING: Bad range parameter in for statement: too many elements (%lu)." +#~ msgstr "" +#~ "WARNING: Bad range parameter in for statement: too many elements (%lu)." + +#~ msgid "" +#~ "WARNING: Bad parameter type (%s) for children, only accept: empty, " +#~ "number, vector, range." +#~ msgstr "" +#~ "WARNING: Bad parameter type (%s) for children, only accept: empty, " +#~ "number, vector, range." + +#~ msgid "WARNING: Negative children index (%d) not allowed" +#~ msgstr "WARNING: Negative children index (%d) not allowed" + +#~ msgid "WARNING: Children index (%d) out of bounds (%d children)" +#~ msgstr "WARNING: Children index (%d) out of bounds (%d children)" + +#~ msgid "WARNING: Negative child index (%d) not allowed" +#~ msgstr "WARNING: Negative child index (%d) not allowed" + +#~ msgid "WARNING: Child index (%d) out of bounds (%d children)" +#~ msgstr "WARNING: Child index (%d) out of bounds (%d children)" + +#~ msgid "WARNING: Bad range parameter for children: too many elements (%lu)." +#~ msgstr "WARNING: Bad range parameter for children: too many elements (%lu)." + +#~ msgid "" +#~ "WARNING: Normalized tree is growing past %d elements. Aborting " +#~ "normalization.\n" +#~ msgstr "" +#~ "WARNING: Normalized tree is growing past %d elements. Aborting " +#~ "normalization.\n" + +#~ msgid "WARNING: Can't open DXF file '%s'." +#~ msgstr "WARNING: Can't open DXF file '%s'." + +#~ msgid "WARNING: Illegal ID '%s' in `%s'" +#~ msgstr "WARNING: Illegal ID '%s' in `%s'" + +#~ msgid "WARNING: Illegal value %s in '%s'" +#~ msgstr "WARNING: Illegal value %s in '%s'" + +#~ msgid "WARNING: Unsupported DXF Entity '%s' (%x) in %s." +#~ msgstr "WARNING: Unsupported DXF Entity '%s' (%x) in %s." + +#~ msgid "WARNING: Unsupported DXF Entity '%s' (%x) in layer '%s' of %s." +#~ msgstr "WARNING: Unsupported DXF Entity '%s' (%x) in layer '%s' of %s." + +#~ msgid "WARNING: Dimension '%s' in '%s', layer '%s' has unsupported type!" +#~ msgstr "WARNING: Dimension '%s' in '%s', layer '%s' has unsupported type!" + +#~ msgid "WARNING: Can't find dimension '%s' in '%s', layer '%s'!" +#~ msgstr "WARNING: Can't find dimension '%s' in '%s', layer '%s'!" + +#~ msgid "WARNING: Can't find cross in '%s', layer '%s'!" +#~ msgstr "WARNING: Can't find cross in '%s', layer '%s'!" + +#~ msgid "EvalContext %p (%p) for %s inst (%p)" +#~ msgstr "EvalContext %p (%p) for %s inst (%p)" + +#~ msgid " eval args:" +#~ msgstr " eval args:" + +#~ msgid " children:" +#~ msgstr " children:" + +#~ msgid "Object isn't a valid 2-manifold! Modify your design.\n" +#~ msgstr "Object isn't a valid 2-manifold! Modify your design.\n" + +#~ msgid "ERROR: Nef->PolySet failed" +#~ msgstr "ERROR: Nef->PolySet failed" + +#~ msgid "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed" +#~ msgstr "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed" + +#~ msgid "CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s" +#~ msgstr "CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s" + +#~ msgid "CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()" +#~ msgstr "CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()" + +#~ msgid "ERROR: Recursion detected calling function '%s'" +#~ msgstr "ERROR: Recursion detected calling function '%s'" + +#~ msgid "Enable the concat() function." +#~ msgstr "Funktion concat() aktivieren." + +#~ msgid "WARNING: Ignoring request to enable unknown feature '%s'." +#~ msgstr "WARNING: Ignoring request to enable unknown feature '%s'." + +#~ msgid "" +#~ "WARNING: Imported file (%s) found in document root instead of relative to " +#~ "the importing module. This behavior is deprecated" +#~ msgstr "" +#~ "WARNING: Imported file (%s) found in document root instead of relative to " +#~ "the importing module. This behavior is deprecated" + +#~ msgid " WARNING: search term not found: \"%s\"" +#~ msgstr " WARNING: search term not found: \"%s\"" + +#~ msgid " WARNING: search term not found: %s" +#~ msgstr " WARNING: search term not found: %s" + +#~ msgid " WARNING: search: none performed on input %s" +#~ msgstr " WARNING: search: none performed on input %s" + +#~ msgid "WARNING: Negative parent module index (%d) not allowed" +#~ msgstr "WARNING: Negative parent module index (%d) not allowed" + +#~ msgid "" +#~ "WARNING: Parent module index (%d) greater than the number of modules on " +#~ "the stack" +#~ msgstr "" +#~ "WARNING: Parent module index (%d) greater than the number of modules on " +#~ "the stack" + +#~ msgid "Geometry Cache hit: %s (%d bytes)" +#~ msgstr "Geometry Cache hit: %s (%d bytes)" + +#~ msgid "Geometry Cache insert: %s (%d bytes)" +#~ msgstr "Geometry Cache insert: %s (%d bytes)" + +#~ msgid "Geometry Cache insert failed: %s (%d bytes)" +#~ msgstr "Geometry Cache insert failed: %s (%d bytes)" + +#~ msgid "Geometries in cache: %d" +#~ msgstr "Geometries in cache: %d" + +#~ msgid "Geometry cache size in bytes: %d" +#~ msgstr "Geometry cache size in bytes: %d" + +#~ msgid "WARNING: Mixing 2D and 3D objects is not supported." +#~ msgstr "WARNING: Mixing 2D and 3D objects is not supported." + +#~ msgid "" +#~ "WARNING: Resize in direction normal to flat object is not implemented" +#~ msgstr "" +#~ "WARNING: Resize in direction normal to flat object is not implemented" + +#~ msgid "WARNING: Ignoring 3D child object for 2D operation" +#~ msgstr "WARNING: Ignoring 3D child object for 2D operation" + +#~ msgid "WARNING: GeometryEvaluator: Node didn't fit into cache" +#~ msgstr "WARNING: GeometryEvaluator: Node didn't fit into cache" + +#~ msgid "WARNING: Ignoring 2D child object for 3D operation" +#~ msgstr "WARNING: Ignoring 2D child object for 3D operation" + +#~ msgid "Error: Unknown boolean operation %d" +#~ msgstr "Error: Unknown boolean operation %d" + +#~ msgid "" +#~ "Warning: Transformation matrix contains Not-a-Number and/or Infinity - " +#~ "removing object." +#~ msgstr "" +#~ "Warning: Transformation matrix contains Not-a-Number and/or Infinity - " +#~ "removing object." + +#~ msgid "" +#~ "ERROR: all points for rotate_extrude() must have the same X coordinate " +#~ "sign (range is %.2f -> %.2f)" +#~ msgstr "" +#~ "ERROR: all points for rotate_extrude() must have the same X coordinate " +#~ "sign (range is %.2f -> %.2f)" + +#~ msgid "Can't open dependencies file `%s' for writing!\n" +#~ msgstr "Can't open dependencies file `%s' for writing!\n" + +#~ msgid "DEPRECATED: filename= is deprecated. Please use file=" +#~ msgstr "DEPRECATED: filename= is deprecated. Please use file=" + +#~ msgid "DEPRECATED: layername= is deprecated. Please use layer=" +#~ msgstr "DEPRECATED: layername= is deprecated. Please use layer=" + +#~ msgid "WARNING: Can't open import file '%s'." +#~ msgstr "WARNING: Can't open import file '%s'." + +#~ msgid "WARNING: Can't parse vertex line '%s'." +#~ msgstr "WARNING: Can't parse vertex line '%s'." + +#~ msgid "WARNING: OFF import requires CGAL." +#~ msgstr "WARNING: OFF import requires CGAL." + +#~ msgid "ERROR: Unsupported file format while trying to import file '%s'" +#~ msgstr "ERROR: Unsupported file format while trying to import file '%s'" + +#~ msgid "" +#~ "DEPRECATED: Support for reading files in linear_extrude will be removed " +#~ "in future releases. Use a child import() instead." +#~ msgstr "" +#~ "DEPRECATED: Support for reading files in linear_extrude will be removed " +#~ "in future releases. Use a child import() instead." + +#~ msgid "" +#~ "Copyright (C) 2009-2013 The OpenSCAD Developers\n" +#~ "\n" +#~ "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." +#~ msgstr "" +#~ "Copyright (C) 2009-2013 The OpenSCAD Developers\n" +#~ "\n" +#~ "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." + +#~ msgid "OpenSCAD - New Document[*]" +#~ msgstr "OpenSCAD - Neue Datei[*]" + +#~ msgid "[*]" +#~ msgstr "[*]" + +#~ msgid "Failed to open file %s: %s" +#~ msgstr "Failed to open file %s: %s" + +#~ msgid "Loaded design '%s'." +#~ msgstr "Loaded design '%s'." + +#~ msgid "Module cache size: %d modules" +#~ msgstr "Module cache size: %d modules" + +#~ msgid "Compiling design (CSG Tree generation)..." +#~ msgstr "Compiling design (CSG Tree generation)..." + +#~ msgid "ERROR: Compilation failed! (no top level object found)" +#~ msgstr "ERROR: Compilation failed! (no top level object found)" + +#~ msgid "ERROR: Compilation failed!" +#~ msgstr "ERROR: Compilation failed!" + +#~ msgid "Compiling design (CSG Products generation)..." +#~ msgstr "Compiling design (CSG Products generation)..." + +#~ msgid "ERROR: CSG generation failed! (no top level object found)" +#~ msgstr "ERROR: CSG generation failed! (no top level object found)" + +#~ msgid "CSG generation cancelled." +#~ msgstr "CSG generation cancelled." + +#~ msgid "Compiling highlights (%d CSG Trees)..." +#~ msgstr "Compiling highlights (%d CSG Trees)..." + +#~ msgid "Compiling background (%d CSG Trees)..." +#~ msgstr "Compiling background (%d CSG Trees)..." + +#~ msgid "WARNING: Normalized tree has %d elements!" +#~ msgstr "WARNING: Normalized tree has %d elements!" + +#~ msgid "WARNING: OpenCSG rendering has been disabled." +#~ msgstr "WARNING: OpenCSG rendering has been disabled." + +#~ msgid "CSG generation finished." +#~ msgstr "CSG generation finished." + +#~ msgid "Total rendering time: %d hours, %d minutes, %d seconds" +#~ msgstr "Total rendering time: %d hours, %d minutes, %d seconds" + +#~ msgid "Open File" +#~ msgstr "Open File" + +#~ msgid "OpenSCAD Designs (*.scad *.csg)" +#~ msgstr "OpenSCAD Designs (*.scad *.csg)" + +#~ msgid "Failed to open file for writing: %s (%s)" +#~ msgstr "Failed to open file for writing: %s (%s)" + +#~ msgid "" +#~ "Failed to open file for writing:\n" +#~ " %1 (%2)" +#~ msgstr "" +#~ "Failed to open file for writing:\n" +#~ " %1 (%2)" + +#~ msgid "Saved design '%s'." +#~ msgstr "Saved design '%s'." + +#~ msgid "WARNING: Library path %s doesn't exist. Creating" +#~ msgstr "WARNING: Library path %s doesn't exist. Creating" + +#~ msgid "ERROR: Cannot create library path: %s" +#~ msgstr "ERROR: Cannot create library path: %s" + +#~ msgid "Parsing design (AST generation)..." +#~ msgstr "Parsing design (AST generation)..." + +#~ msgid "frame%05d.png" +#~ msgstr "frame%05d.png" + +#~ msgid "Rendering Polygon Mesh using CGAL..." +#~ msgstr "Rendering Polygon Mesh using CGAL..." + +#~ msgid " Top level object is a 3D object:" +#~ msgstr " Top level object is a 3D object:" + +#~ msgid " Simple: %6s" +#~ msgstr " Simple: %6s" + +#~ msgid "yes" +#~ msgstr "yes" + +#~ msgid "no" +#~ msgstr "no" + +#~ msgid " Valid: %6s" +#~ msgstr " Valid: %6s" + +#~ msgid " Vertices: %6d" +#~ msgstr " Vertices: %6d" + +#~ msgid " Halfedges: %6d" +#~ msgstr " Halfedges: %6d" + +#~ msgid " Edges: %6d" +#~ msgstr " Edges: %6d" + +#~ msgid " Halffacets: %6d" +#~ msgstr " Halffacets: %6d" + +#~ msgid " Facets: %6d" +#~ msgstr " Facets: %6d" + +#~ msgid " Volumes: %6d" +#~ msgstr " Volumes: %6d" + +#~ msgid " Top level object is a 2D object:" +#~ msgstr " Top level object is a 2D object:" + +#~ msgid " Contours: %6d" +#~ msgstr " Contours: %6d" + +#~ msgid "Unknown geometry type" +#~ msgstr "Unknown geometry type" + +#~ msgid "Rendering finished." +#~ msgstr "Rendering finished." + +#~ msgid "WARNING: No top level geometry to render" +#~ msgstr "WARNING: No top level geometry to render" + +#~ msgid "AST Dump" +#~ msgstr "AST Dump" + +#~ msgid "No AST to dump. Please try compiling first..." +#~ msgstr "No AST to dump. Please try compiling first..." + +#~ msgid "CSG Tree Dump" +#~ msgstr "CSG Tree Dump" + +#~ msgid "No CSG to dump. Please try compiling first..." +#~ msgstr "No CSG to dump. Please try compiling first..." + +#~ msgid "CSG Products Dump" +#~ msgstr "CSG Products Dump" + +#~ msgid "" +#~ "\n" +#~ "CSG before normalization:\n" +#~ "%1\n" +#~ "\n" +#~ "\n" +#~ "CSG after normalization:\n" +#~ "%2\n" +#~ "\n" +#~ "\n" +#~ "CSG rendering chain:\n" +#~ "%3\n" +#~ "\n" +#~ "\n" +#~ "Highlights CSG rendering chain:\n" +#~ "%4\n" +#~ "\n" +#~ "\n" +#~ "Background CSG rendering chain:\n" +#~ "%5\n" +#~ msgstr "" +#~ "\n" +#~ "CSG before normalization:\n" +#~ "%1\n" +#~ "\n" +#~ "\n" +#~ "CSG after normalization:\n" +#~ "%2\n" +#~ "\n" +#~ "\n" +#~ "CSG rendering chain:\n" +#~ "%3\n" +#~ "\n" +#~ "\n" +#~ "Highlights CSG rendering chain:\n" +#~ "%4\n" +#~ "\n" +#~ "\n" +#~ "Background CSG rendering chain:\n" +#~ "%5\n" + +#~ msgid "Nothing to export! Try building first (press F6)." +#~ msgstr "Nothing to export! Try building first (press F6)." + +#~ msgid "Current top level object is not a 3D object." +#~ msgstr "Current top level object is not a 3D object." + +#~ msgid "" +#~ "Object isn't a valid 2-manifold! Modify your design. See http://en." +#~ "wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export" +#~ msgstr "" +#~ "Object isn't a valid 2-manifold! Modify your design. See http://en." +#~ "wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export" + +#~ msgid "Export OFF File" +#~ msgstr "Export OFF File" + +#~ msgid "OFF Files (*.off)" +#~ msgstr "OFF Files (*.off)" + +#~ msgid "No filename specified. %s export aborted." +#~ msgstr "No filename specified. %s export aborted." + +#~ msgid "Can't open file \"%s\" for export" +#~ msgstr "Can't open file \"%s\" for export" + +#~ msgid "%s export finished." +#~ msgstr "%s export finished." + +#~ msgid "Current top level object is not a 2D object." +#~ msgstr "Current top level object is not a 2D object." + +#~ msgid "Export DXF File" +#~ msgstr "Export DXF File" + +#~ msgid "DXF Files (*.dxf)" +#~ msgstr "DXF Files (*.dxf)" + +#~ msgid "No filename specified. DXF export aborted." +#~ msgstr "No filename specified. DXF export aborted." + +#~ msgid "DXF export finished." +#~ msgstr "DXF export finished." + +#~ msgid "Nothing to export. Please try compiling first..." +#~ msgstr "Nothing to export. Please try compiling first..." + +#~ msgid "No filename specified. CSG export aborted." +#~ msgstr "No filename specified. CSG export aborted." + +#~ msgid "CSG export finished." +#~ msgstr "CSG export finished." + +#~ msgid "No filename specified. Image export aborted." +#~ msgstr "No filename specified. Image export aborted." + +#~ msgid "http://openscad.org/" +#~ msgstr "http://openscad.org/" + +#~ msgid "http://www.openscad.org/documentation.html" +#~ msgstr "http://www.openscad.org/documentation.html" + +#~ msgid "OpenGL Info" +#~ msgstr "OpenGL Info" + +#~ msgid "WARNING: Experimental builtin function '%s' is not enabled." +#~ msgstr "WARNING: Experimental builtin function '%s' is not enabled." + +#~ msgid "WARNING: Experimental builtin module '%s' is not enabled." +#~ msgstr "WARNING: Experimental builtin module '%s' is not enabled." + +#~ msgid "" +#~ "DEPRECATED: The %s() module will be removed in future releases. Use %s() " +#~ "instead." +#~ msgstr "" +#~ "DEPRECATED: The %s() module will be removed in future releases. Use %s() " +#~ "instead." + +#~ msgid "ModuleContext %p (%p) for %s inst (%p) " +#~ msgstr "ModuleContext %p (%p) for %s inst (%p) " + +#~ msgid "ModuleContext: %p (%p)" +#~ msgstr "ModuleContext: %p (%p)" + +#~ msgid "New lib Context for %s func:" +#~ msgstr "New lib Context for %s func:" + +#~ msgid "New file Context:" +#~ msgstr "New file Context:" + +#~ msgid "Recompiling cached library: %s (%s)" +#~ msgstr "Recompiling cached library: %s (%s)" + +#~ msgid "Compiling library '%s'." +#~ msgstr "Compiling library '%s'." + +#~ msgid "WARNING: Can't open library file '%s'\n" +#~ msgstr "WARNING: Can't open library file '%s'\n" + +#~ msgid " compiled module: %p" +#~ msgstr " compiled module: %p" + +#~ msgid "New eval ctx:" +#~ msgstr "New eval ctx:" + +#~ msgid "ERROR: Recursion detected calling module '%s'" +#~ msgstr "ERROR: Recursion detected calling module '%s'" + +#~ msgid "WARNING: Failed to compile library '%s'." +#~ msgstr "WARNING: Failed to compile library '%s'." + +#~ msgid "OpenSCAD recommended OpenGL version is 2.0." +#~ msgstr "Die empfohlene OpenGL version für OpenSCAD ist 2.0." + +#~ msgid "" +#~ "Usage: %1% [ -o output_file [ -d deps_file ] ]\\\n" +#~ "%2%[ -m make_command ] [ -D var=val [..] ] \\\n" +#~ "%2%[ --version ] [ --info ] \\\n" +#~ "%2%[ --camera=translatex,y,z,rotx,y,z,dist | \\\n" +#~ "%2% --camera=eyex,y,z,centerx,y,z ] \\\n" +#~ "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" +#~ "%2%[ --render | --preview[=throwntogether] ] \\\n" +#~ "%2%[ --enable= ] \\\n" +#~ "%2%filename\n" +#~ msgstr "" +#~ "Usage: %1% [ -o output_file [ -d deps_file ] ]\\\n" +#~ "%2%[ -m make_command ] [ -D var=val [..] ] \\\n" +#~ "%2%[ --version ] [ --info ] \\\n" +#~ "%2%[ --camera=translatex,y,z,rotx,y,z,dist | \\\n" +#~ "%2% --camera=eyex,y,z,centerx,y,z ] \\\n" +#~ "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" +#~ "%2%[ --render | --preview[=throwntogether] ] \\\n" +#~ "%2%[ --enable= ] \\\n" +#~ "%2%filename\n" + +#~ msgid "OpenSCAD version %s\n" +#~ msgstr "OpenSCAD version %s\n" + +#~ msgid "Can't create OpenGL OffscreenView. Code: %i. Exiting.\n" +#~ msgstr "Can't create OpenGL OffscreenView. Code: %i. Exiting.\n" + +#~ msgid "Camera setup requires either 7 numbers for Gimbal Camera\n" +#~ msgstr "Camera setup requires either 7 numbers for Gimbal Camera\n" + +#~ msgid "or 6 numbers for Vector Camera\n" +#~ msgstr "or 6 numbers for Vector Camera\n" + +#~ msgid "projection needs to be 'o' or 'p' for ortho or perspective\n" +#~ msgstr "projection needs to be 'o' or 'p' for ortho or perspective\n" + +#~ msgid "Need 2 numbers for imgsize\n" +#~ msgstr "Need 2 numbers for imgsize\n" + +#~ msgid "Unknown suffix for output file %s\n" +#~ msgstr "Unknown suffix for output file %s\n" + +#~ msgid "Can't open input file '%s'!\n" +#~ msgstr "Can't open input file '%s'!\n" + +#~ msgid "Can't parse file '%s'!\n" +#~ msgstr "Can't parse file '%s'!\n" + +#~ msgid "Output file:%s\n" +#~ msgstr "Output file:%s\n" + +#~ msgid "Sorry, don't know how to write deps for that file type. Exiting\n" +#~ msgstr "Sorry, don't know how to write deps for that file type. Exiting\n" + +#~ msgid "error writing deps" +#~ msgstr "error writing deps" + +#~ msgid "Current top level object is not a 3D object.\n" +#~ msgstr "Current top level object is not a 3D object.\n" + +#~ msgid "Current top level object is not a 2D object.\n" +#~ msgstr "Current top level object is not a 2D object.\n" + +#~ msgid "Allowed options" +#~ msgstr "Allowed options" + +#~ msgid "help message" +#~ msgstr "help message" + +#~ msgid "print the version" +#~ msgstr "print the version" + +#~ msgid "print information about the building process" +#~ msgstr "print information about the building process" + +#~ msgid "if exporting a png image, do a full CGAL render" +#~ msgstr "if exporting a png image, do a full CGAL render" + +#~ msgid "" +#~ "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview" +#~ msgstr "" +#~ "if exporting a png image, do an OpenCSG(default) or ThrownTogether preview" + +#~ msgid "parameters for camera when exporting png" +#~ msgstr "parameters for camera when exporting png" + +#~ msgid "=width,height for exporting png" +#~ msgstr "=width,height for exporting png" + +#~ msgid "(o)rtho or (p)erspective when exporting png" +#~ msgstr "(o)rtho or (p)erspective when exporting png" + +#~ msgid "out-file" +#~ msgstr "out-file" + +#~ msgid "stl-file" +#~ msgstr "stl-file" + +#~ msgid "dxf-file" +#~ msgstr "dxf-file" + +#~ msgid "deps-file" +#~ msgstr "deps-file" + +#~ msgid "makefile" +#~ msgstr "makefile" + +#~ msgid "var=val" +#~ msgstr "var=val" + +#~ msgid "enable experimental features" +#~ msgstr "enable experimental features" + +#~ msgid "Hidden options" +#~ msgstr "Hidden options" + +#~ msgid "input file" +#~ msgstr "input file" + +#~ msgid "DEPRECATED: The -s option is deprecated. Use -o instead.\n" +#~ msgstr "DEPRECATED: The -s option is deprecated. Use -o instead.\n" + +#~ msgid "DEPRECATED: The -x option is deprecated. Use -o instead.\n" +#~ msgstr "DEPRECATED: The -x option is deprecated. Use -o instead.\n" + +#~ msgid "Requested GUI mode but can't open display!\n" +#~ msgstr "Requested GUI mode but can't open display!\n" + +#~ msgid "ERROR: Cannot create %s" +#~ msgstr "ERROR: Cannot create %s" + +#~ msgid "PolySet:" +#~ msgstr "PolySet:" + +#~ msgid "" +#~ "\n" +#~ " dimensions:" +#~ msgstr "" +#~ "\n" +#~ " dimensions:" + +#~ msgid "" +#~ "\n" +#~ " convexity:" +#~ msgstr "" +#~ "\n" +#~ " convexity:" + +#~ msgid "" +#~ "\n" +#~ " num polygons: " +#~ msgstr "" +#~ "\n" +#~ " num polygons: " + +#~ msgid "" +#~ "\n" +#~ " num outlines: " +#~ msgstr "" +#~ "\n" +#~ " num outlines: " + +#~ msgid "" +#~ "\n" +#~ " polygons data:" +#~ msgstr "" +#~ "\n" +#~ " polygons data:" + +#~ msgid "" +#~ "\n" +#~ " polygon begin:" +#~ msgstr "" +#~ "\n" +#~ " polygon begin:" + +#~ msgid "" +#~ "\n" +#~ " vertex:" +#~ msgstr "" +#~ "\n" +#~ " vertex:" + +#~ msgid "" +#~ "\n" +#~ " outlines data:" +#~ msgstr "" +#~ "\n" +#~ " outlines data:" + +#~ msgid "" +#~ "\n" +#~ "PolySet end" +#~ msgstr "" +#~ "\n" +#~ "PolySet end" + +#~ msgid "" +#~ "WARNING: Ignoring radius variable '%s' as diameter '%s' is defined too." +#~ msgstr "" +#~ "WARNING: Ignoring radius variable '%s' as diameter '%s' is defined too." + +#~ msgid "WARNING: $fs too small - clamping to %f" +#~ msgstr "WARNING: $fs too small - clamping to %f" + +#~ msgid "WARNING: $fa too small - clamping to %f" +#~ msgstr "WARNING: $fa too small - clamping to %f" + +#~ msgid "" +#~ "DEPRECATED: polyhedron(triangles=[]) will be removed in future releases. " +#~ "Use polyhedron(faces=[]) instead." +#~ msgstr "" +#~ "DEPRECATED: polyhedron(triangles=[]) will be removed in future releases. " +#~ "Use polyhedron(faces=[]) instead." + +#~ msgid "ERROR: Unable to convert point at index %d to a vec3 of numbers" +#~ msgstr "ERROR: Unable to convert point at index %d to a vec3 of numbers" + +#~ msgid "ERROR: Unable to convert point %s at index %d to a vec2 of numbers" +#~ msgstr "ERROR: Unable to convert point %s at index %d to a vec2 of numbers" + +#~ msgid "" +#~ "DEPRECATED: Support for reading files in rotate_extrude will be removed " +#~ "in future releases. Use a child import() instead." +#~ msgstr "" +#~ "DEPRECATED: Support for reading files in rotate_extrude will be removed " +#~ "in future releases. Use a child import() instead." + +#~ msgid "WARNING: Can't open DAT file '%s'." +#~ msgstr "WARNING: Can't open DAT file '%s'." + +#~ msgid "WARNING: Illegal value in '%s': %s" +#~ msgstr "WARNING: Illegal value in '%s': %s" + +#~ msgid "" +#~ "DEPRECATED: Using ranges of the form [begin:end] with begin value greater " +#~ "than the end value is deprecated." +#~ msgstr "" +#~ "DEPRECATED: Using ranges of the form [begin:end] with begin value greater " +#~ "than the end value is deprecated." + +#~ msgid "&Compile" +#~ msgstr "Übersetzen" + +#~ msgid "Compile and &Render (CGAL)" +#~ msgstr "Übersetzen und Rendern (CGAL)" + +#~ msgid "CGAL Grid Only" +#~ msgstr "CGAL Gitter anzeigen" + +#~ msgid " vertices:" +#~ msgstr " vertices:" + +#~ msgid "{ Outer boundary = " +#~ msgstr "{ Outer boundary = " + +#~ msgid "{ Unbounded polygon." +#~ msgstr "{ Unbounded polygon." + +#~ msgid " holes:" +#~ msgstr " holes:" + +#~ msgid " Hole #" +#~ msgstr " Hole #" + +#~ msgid "" +#~ "WARNING: minkowski() and hull() is not implemented for 2d objects with " +#~ "holes!" +#~ msgstr "" +#~ "WARNING: minkowski() and hull() is not implemented for 2d objects with " +#~ "holes!" + +#~ msgid "WARNING: minkowski() could not get any points from object 1!" +#~ msgstr "WARNING: minkowski() could not get any points from object 1!" + +#~ msgid "WARNING: minkowski() could not get any points from object 2!" +#~ msgstr "WARNING: minkowski() could not get any points from object 2!" + +#~ msgid "CGAL error in CGAL_Nef_polyhedron's %s operator: %s" +#~ msgstr "CGAL error in CGAL_Nef_polyhedron's %s operator: %s" + +#~ msgid "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s" +#~ msgstr "ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s" + +#~ msgid "WARNING: Cannot resize to sizes less than 0." +#~ msgstr "WARNING: Cannot resize to sizes less than 0." + +#~ msgid "WARNING: glide() is not implemented yet!" +#~ msgstr "WARNING: glide() is not implemented yet!" + +#~ msgid "WARNING: subdiv() is not implemented yet!" +#~ msgstr "WARNING: subdiv() is not implemented yet!" + +#~ msgid "Warning: Scaling a 2D object with 0 - removing object" +#~ msgstr "Warning: Scaling a 2D object with 0 - removing object" + +#~ msgid "" +#~ "WARNING: Duplicate vertices and/or intersecting lines found during DXF " +#~ "Tessellation." +#~ msgstr "" +#~ "WARNING: Duplicate vertices and/or intersecting lines found during DXF " +#~ "Tessellation." + +#~ msgid "" +#~ "WARNING: Modify the polygon to be a Simple Polygon. Render is incomplete." +#~ msgstr "" +#~ "WARNING: Modify the polygon to be a Simple Polygon. Render is incomplete." + +#~ msgid "CGAL error in dxf_tesselate(): %s" +#~ msgstr "CGAL error in dxf_tesselate(): %s" + +#~ msgid "CGAL error in dxftess triangulate_polygon: %s" +#~ msgstr "CGAL error in dxftess triangulate_polygon: %s" + +#~ msgid "WARNING: PolySet has polygon with <3 points" +#~ msgstr "WARNING: PolySet has polygon with <3 points" + +#~ msgid "WARNING: PolySet has degenerate polygon" +#~ msgstr "WARNING: PolySet has degenerate polygon" + +#~ msgid "GLU tesselation error %s" +#~ msgstr "GLU tesselation error %s" + +#~ msgid "WARNING: No suitable PolySetEvaluator found for %s module!" +#~ msgstr "WARNING: No suitable PolySetEvaluator found for %s module!" + +#~ msgid " Empty: %6s" +#~ msgstr " Empty: %6s" + +#~ msgid " Plane: %6s" +#~ msgstr " Plane: %6s" + +#~ msgid " Faces: %6d" +#~ msgstr " Faces: %6d" + +#~ msgid " FaceCycles: %6d" +#~ msgstr " FaceCycles: %6d" + +#~ msgid "" +#~ "\n" +#~ " num borders: " +#~ msgstr "" +#~ "\n" +#~ " num borders: " + +#~ msgid "" +#~ "\n" +#~ " borders data:" +#~ msgstr "" +#~ "\n" +#~ " borders data:" + +#~ msgid "" +#~ "\n" +#~ " border polygon begin:" +#~ msgstr "" +#~ "\n" +#~ " border polygon begin:" + +#~ msgid "" +#~ "WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify " +#~ "your design.." +#~ msgstr "" +#~ "WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify " +#~ "your design.." + +#~ msgid "ERROR: linear_extrude() is not defined for 3D child objects!" +#~ msgstr "ERROR: linear_extrude() is not defined for 3D child objects!" + +#~ msgid "" +#~ "WARNING: Open paths in dxf_linear_extrude(file = \"%s\", layer = \"%s\"):" +#~ msgstr "" +#~ "WARNING: Open paths in dxf_linear_extrude(file = \"%s\", layer = \"%s\"):" + +#~ msgid "ERROR: rotate_extrude() is not defined for 3D child objects!" +#~ msgstr "ERROR: rotate_extrude() is not defined for 3D child objects!" + +#~ msgid "WARNING: Body of render() isn't valid 2-manifold!" +#~ msgstr "WARNING: Body of render() isn't valid 2-manifold!" + +#~ msgid "GLEW Error: %s\n" +#~ msgstr "GLEW Error: %s\n" + +#~ msgid "" +#~ "GLEW version %s\n" +#~ "OpenGL version %s\n" +#~ "%s (%s)\n" +#~ "\n" +#~ "RGBA(%d%d%d%d), depth(%d), stencil(%d)\n" +#~ "Extensions:\n" +#~ "%s\n" +#~ msgstr "" +#~ "GLEW version %s\n" +#~ "OpenGL version %s\n" +#~ "%s (%s)\n" +#~ "\n" +#~ "RGBA(%d%d%d%d), depth(%d), stencil(%d)\n" +#~ "Extensions:\n" +#~ "%s\n" + +#~ msgid "" +#~ "OpenGL Program Linker Error:\n" +#~ "%.*s" +#~ msgstr "" +#~ "OpenGL Program Linker Error:\n" +#~ "%.*s" + +#~ msgid "" +#~ "OpenGL Program Link OK:\n" +#~ "%.*s" +#~ msgstr "" +#~ "OpenGL Program Link OK:\n" +#~ "%.*s" + +#~ msgid "" +#~ "OpenGL Program Validation results:\n" +#~ "%.*s" +#~ msgstr "" +#~ "OpenGL Program Validation results:\n" +#~ "%.*s" diff --git a/locale/fr.po b/locale/fr.po new file mode 100644 index 00000000..b3b892a5 --- /dev/null +++ b/locale/fr.po @@ -0,0 +1,1012 @@ +# French translations for OpenSCAD package. +# Copyright (C) 2013 THE OpenSCAD'S COPYRIGHT HOLDER +# This file is distributed under the same license as the OpenSCAD package. +# don , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: OpenSCAD 2013.02.07\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 23:28+0100\n" +"PO-Revision-Date: 2014-12-14 10:20-0500\n" +"Last-Translator: Keven Villeneuve \n" +"Language-Team: French\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Poedit-SourceCharset: utf-8\n" +"X-Generator: Poedit 1.7.1\n" + +#: objects/ui_AboutDialog.h:51 src/AboutDialog.h:15 +msgid "About OpenSCAD" +msgstr "À propos de OpenSCAD" + +#: objects/ui_FontListDialog.h:102 +msgid "OpenSCAD Font List" +msgstr "Liste des polices OpenSCAD" + +#: objects/ui_FontListDialog.h:103 objects/ui_LibraryInfoDialog.h:77 +msgid "&OK" +msgstr "&OK" + +#: objects/ui_FontListDialog.h:104 +msgid "Copy to Clipboard" +msgstr "Coller dans le presse-papier" + +#: objects/ui_FontListDialog.h:105 +msgid "Filter:" +msgstr "Filtre:" + +#: objects/ui_FontListDialog.h:106 +msgid "" +"

This list shows the fonts currently registered with " +"OpenSCAD.

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" +msgstr "" +"

Cette liste affiche les polices présentement chargées " +"avec OpenSCAD

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" + +#: objects/ui_launchingscreen.h:276 +msgid "Welcome to OpenSCAD" +msgstr "Bienvenue dans OpenSCAD" + +#: objects/ui_launchingscreen.h:277 +msgid "New" +msgstr "Nouveau" + +#: objects/ui_launchingscreen.h:278 +msgid "Open" +msgstr "Ouvrir..." + +#: objects/ui_launchingscreen.h:279 +msgid "Help" +msgstr "Aide" + +#: objects/ui_launchingscreen.h:280 +msgid "Recents" +msgstr "Récents" + +#: objects/ui_launchingscreen.h:281 +msgid "Open Recent" +msgstr "Ouvrir un fichier récent" + +#: objects/ui_launchingscreen.h:282 objects/ui_launchingscreen.h:284 +#: objects/ui_MainWindow.h:855 +msgid "Examples" +msgstr "Exemples" + +#: objects/ui_launchingscreen.h:285 +msgid "Open Example" +msgstr "&Ouvrir un example" + +#: objects/ui_launchingscreen.h:287 +msgid "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" +msgstr "" +"\n" +"

OpenSCAD

\n" +"

Le modeleur CAD 3D des programmeurs

\n" +"\n" +"\n" +"\n" + +#: objects/ui_launchingscreen.h:294 +msgid "Don't show again" +msgstr "Ne plus afficher" + +#: objects/ui_LibraryInfoDialog.h:75 +msgid "Lib & Build Info" +msgstr "Bibliothèques et Infos de la version" + +#: objects/ui_LibraryInfoDialog.h:76 +msgid "OpenSCAD Detailed Library and Build Information" +msgstr "Informations détaillées des bibliothèques et de la version d'OpenSCAD" + +#: objects/ui_MainWindow.h:731 +msgid "&New" +msgstr "&Nouveau" + +#: objects/ui_MainWindow.h:732 +msgid "Ctrl+N" +msgstr "Ctrl+N" + +#: objects/ui_MainWindow.h:733 +msgid "&Open..." +msgstr "&Ouvrir..." + +#: objects/ui_MainWindow.h:734 +msgid "Ctrl+O" +msgstr "Ctrl+O" + +#: objects/ui_MainWindow.h:735 +msgid "&Save" +msgstr "Enregi&strer" + +#: objects/ui_MainWindow.h:736 +msgid "Ctrl+S" +msgstr "Ctrl+S" + +#: objects/ui_MainWindow.h:737 +msgid "Save &As..." +msgstr "Enregistrer &sous..." + +#: objects/ui_MainWindow.h:738 +msgid "Ctrl+Shift+S" +msgstr "Ctrl+Shift+S" + +#: objects/ui_MainWindow.h:739 +msgid "&Reload" +msgstr "&Recharger" + +#: objects/ui_MainWindow.h:740 +msgid "Ctrl+R" +msgstr "Ctrl+R" + +#: objects/ui_MainWindow.h:741 +msgid "&Quit" +msgstr "&Quitter" + +#: objects/ui_MainWindow.h:742 +msgid "Ctrl+Q" +msgstr "Ctrl+Q" + +#: objects/ui_MainWindow.h:743 +msgid "&Undo" +msgstr "Ann&uler" + +#: objects/ui_MainWindow.h:744 +msgid "Ctrl+Z" +msgstr "Ctrl+Z" + +#: objects/ui_MainWindow.h:745 +msgid "&Redo" +msgstr "&Répéter" + +#: objects/ui_MainWindow.h:746 +msgid "Ctrl+Shift+Z" +msgstr "Ctrl+Shift+Z" + +#: objects/ui_MainWindow.h:747 +msgid "Cu&t" +msgstr "Co&uper" + +#: objects/ui_MainWindow.h:748 +msgid "Ctrl+X" +msgstr "Ctrl+X" + +#: objects/ui_MainWindow.h:749 +msgid "&Copy" +msgstr "&Copier" + +#: objects/ui_MainWindow.h:750 +msgid "Ctrl+C" +msgstr "Ctrl+C" + +#: objects/ui_MainWindow.h:751 +msgid "&Paste" +msgstr "&Coller" + +#: objects/ui_MainWindow.h:752 +msgid "Ctrl+V" +msgstr "Ctrl+V" + +#: objects/ui_MainWindow.h:753 +msgid "&Indent" +msgstr "&Indenter" + +#: objects/ui_MainWindow.h:754 +msgid "Ctrl+I" +msgstr "Ctrl+I" + +#: objects/ui_MainWindow.h:755 +msgid "U&nindent" +msgstr "Dési&ndenter" + +#: objects/ui_MainWindow.h:756 +msgid "Ctrl+Shift+I" +msgstr "Ctrl+Shift+I" + +#: objects/ui_MainWindow.h:757 +msgid "C&omment" +msgstr "C&ommenter" + +#: objects/ui_MainWindow.h:758 +msgid "Ctrl+D" +msgstr "Ctrl+D" + +#: objects/ui_MainWindow.h:759 +msgid "Unco&mment" +msgstr "Déco&mmenter" + +#: objects/ui_MainWindow.h:760 +msgid "Ctrl+Shift+D" +msgstr "Ctrl+Shift+D" + +#: objects/ui_MainWindow.h:761 +msgid "Paste viewport translation" +msgstr "Coller la translation de la fenêtre de rendu" + +#: objects/ui_MainWindow.h:762 +msgid "Ctrl+T" +msgstr "Ctrl+T" + +#: objects/ui_MainWindow.h:763 +msgid "Paste viewport rotation" +msgstr "Coller la rotation de la fenêtre de rendu" + +#: objects/ui_MainWindow.h:764 objects/ui_MainWindow.h:842 +msgid "Zoom In" +msgstr "Zoom Avant" + +#: objects/ui_MainWindow.h:765 +msgid "Ctrl++" +msgstr "Ctrl++" + +#: objects/ui_MainWindow.h:766 objects/ui_MainWindow.h:844 +msgid "Zoom Out" +msgstr "Zoom Arrière" + +#: objects/ui_MainWindow.h:767 +msgid "Ctrl+-" +msgstr "Ctrl+-" + +#: objects/ui_MainWindow.h:768 +msgid "Hide editor" +msgstr "Cacher l'éditeur" + +#: objects/ui_MainWindow.h:769 +msgid "&Reload and Preview" +msgstr "&Recharger et Aperçu" + +#: objects/ui_MainWindow.h:770 +msgid "F4" +msgstr "F4" + +#: objects/ui_MainWindow.h:771 +msgid "&Preview" +msgstr "&Aperçu" + +#: objects/ui_MainWindow.h:772 +msgid "F5" +msgstr "F5" + +#: objects/ui_MainWindow.h:773 +msgid "&Render" +msgstr "&Rendu" + +#: objects/ui_MainWindow.h:774 +msgid "F6" +msgstr "F6" + +#: objects/ui_MainWindow.h:775 +msgid "Check Validity" +msgstr "Vérifier la Validité" + +#: objects/ui_MainWindow.h:776 +msgid "Display &AST..." +msgstr "Afficher &AST..." + +#: objects/ui_MainWindow.h:777 +msgid "Display CSG &Tree..." +msgstr "Afficher CSG &Arbre" + +#: objects/ui_MainWindow.h:778 +msgid "Display CSG &Products..." +msgstr "Afficher CSG &Produits" + +#: objects/ui_MainWindow.h:779 +msgid "Export as &STL..." +msgstr "Exporter comme &STL..." + +#: objects/ui_MainWindow.h:780 +msgid "Export as &OFF..." +msgstr "Exporter comme &OFF..." + +#: objects/ui_MainWindow.h:781 +msgid "Preview" +msgstr "Aperçu" + +#: objects/ui_MainWindow.h:782 +msgid "F9" +msgstr "F9" + +#: objects/ui_MainWindow.h:783 +msgid "Surfaces" +msgstr "Surfaces" + +#: objects/ui_MainWindow.h:784 +msgid "F10" +msgstr "F10" + +#: objects/ui_MainWindow.h:785 +msgid "Wireframe" +msgstr "Wireframe" + +#: objects/ui_MainWindow.h:786 +msgid "F11" +msgstr "F11" + +#: objects/ui_MainWindow.h:787 +msgid "Thrown Together" +msgstr "Jeté ensemble" + +#: objects/ui_MainWindow.h:788 +msgid "F12" +msgstr "F12" + +#: objects/ui_MainWindow.h:789 +msgid "Show Edges" +msgstr "Afficher les Arêtes" + +#: objects/ui_MainWindow.h:790 +msgid "Ctrl+1" +msgstr "Ctrl+1" + +#: objects/ui_MainWindow.h:791 +msgid "Show Axes" +msgstr "Afficher les Axes" + +#: objects/ui_MainWindow.h:792 +msgid "Ctrl+2" +msgstr "Ctrl+2" + +#: objects/ui_MainWindow.h:793 +msgid "Show Crosshairs" +msgstr "Afficher la Mire" + +#: objects/ui_MainWindow.h:794 +msgid "Ctrl+3" +msgstr "Ctrl+3" + +#: objects/ui_MainWindow.h:795 +msgid "Animate" +msgstr "Animer" + +#: objects/ui_MainWindow.h:796 +msgid "Top" +msgstr "Dessus" + +#: objects/ui_MainWindow.h:797 +msgid "Ctrl+4" +msgstr "Ctrl+4" + +#: objects/ui_MainWindow.h:798 +msgid "Bottom" +msgstr "Dessous" + +#: objects/ui_MainWindow.h:799 +msgid "Ctrl+5" +msgstr "Ctrl+5" + +#: objects/ui_MainWindow.h:800 +msgid "Left" +msgstr "Gauche" + +#: objects/ui_MainWindow.h:801 +msgid "Ctrl+6" +msgstr "Ctrl+6" + +#: objects/ui_MainWindow.h:802 +msgid "Right" +msgstr "Droite" + +#: objects/ui_MainWindow.h:803 +msgid "Ctrl+7" +msgstr "Ctrl+7" + +#: objects/ui_MainWindow.h:804 +msgid "Front" +msgstr "Face" + +#: objects/ui_MainWindow.h:805 +msgid "Ctrl+8" +msgstr "Ctrl+8" + +#: objects/ui_MainWindow.h:806 +msgid "Back" +msgstr "Arrière" + +#: objects/ui_MainWindow.h:807 +msgid "Ctrl+9" +msgstr "Ctrl+9" + +#: objects/ui_MainWindow.h:808 +msgid "Diagonal" +msgstr "Diagonale" + +#: objects/ui_MainWindow.h:809 +msgid "Ctrl+0" +msgstr "Ctrl+0" + +#: objects/ui_MainWindow.h:810 +msgid "Center" +msgstr "Centre" + +#: objects/ui_MainWindow.h:811 +msgid "Perspective" +msgstr "Perspective" + +#: objects/ui_MainWindow.h:812 +msgid "Orthogonal" +msgstr "Orthogonale" + +#: objects/ui_MainWindow.h:813 +msgid "Hide console" +msgstr "Cache la console" + +#: objects/ui_MainWindow.h:814 +msgid "About" +msgstr "À propos" + +#: objects/ui_MainWindow.h:815 +msgid "Documentation" +msgstr "Documentation" + +#: objects/ui_MainWindow.h:816 +msgid "Clear Recent" +msgstr "Effacer Récents" + +#: objects/ui_MainWindow.h:817 +msgid "Export as DXF..." +msgstr "Exporter comme DXF..." + +#: objects/ui_MainWindow.h:818 objects/ui_OpenCSGWarningDialog.h:94 +msgid "Close" +msgstr "Fermer" + +#: objects/ui_MainWindow.h:819 +msgid "Ctrl+W" +msgstr "Ctrl+W" + +#: objects/ui_MainWindow.h:820 objects/ui_Preferences.h:608 +msgid "Preferences" +msgstr "Préférences" + +#: objects/ui_MainWindow.h:821 +msgid "Find..." +msgstr "Rechercher..." + +#: objects/ui_MainWindow.h:822 +msgid "Ctrl+F" +msgstr "Ctrl+F" + +#: objects/ui_MainWindow.h:823 +msgid "Find and Replace..." +msgstr "Rechercher et remplacer..." + +#: objects/ui_MainWindow.h:824 +msgid "Ctrl+Alt+F" +msgstr "Ctrl+Alt+F" + +#: objects/ui_MainWindow.h:825 +msgid "Find Next" +msgstr "Rechercher Suivant" + +#: objects/ui_MainWindow.h:826 +msgid "Ctrl+G" +msgstr "Ctrl+G" + +#: objects/ui_MainWindow.h:827 +msgid "Find Previous" +msgstr "Rechercher Précédent" + +#: objects/ui_MainWindow.h:828 +msgid "Ctrl+Shift+G" +msgstr "Ctrl+Shift+G" + +#: objects/ui_MainWindow.h:829 +msgid "Use Selection for Find" +msgstr "Utiliser la sélection pour Rechercher" + +#: objects/ui_MainWindow.h:830 +msgid "Ctrl+E" +msgstr "Ctrl+E" + +#: objects/ui_MainWindow.h:831 +msgid "Flush Caches" +msgstr "Vider les caches" + +#: objects/ui_MainWindow.h:832 +msgid "OpenSCAD Homepage" +msgstr "Page d'accueil OpenSCAD" + +#: objects/ui_MainWindow.h:833 +msgid "Automatic Reload and Preview" +msgstr "Recharger et Aperçu Automatique" + +#: objects/ui_MainWindow.h:834 +msgid "Export as Image..." +msgstr "Exporter comme Image..." + +#: objects/ui_MainWindow.h:835 +msgid "Export as CSG..." +msgstr "Exporter comme CSG..." + +#: objects/ui_MainWindow.h:836 +msgid "Library info" +msgstr "Informations Bibliothèques" + +#: objects/ui_MainWindow.h:837 +msgid "Show Library Folder..." +msgstr "Afficher le dossier des bibliothèques" + +#: objects/ui_MainWindow.h:838 +msgid "Reset View" +msgstr "Réinitialiser la vue" + +#: objects/ui_MainWindow.h:839 +msgid "Font List" +msgstr "Liste des polices" + +#: objects/ui_MainWindow.h:840 +msgid "Export as SVG..." +msgstr "Exporter comme SVG..." + +#: objects/ui_MainWindow.h:841 +msgid "Export as AMF..." +msgstr "Exporter comme AMF..." + +#: objects/ui_MainWindow.h:843 +msgid "Ctrl+]" +msgstr "Ctrl+]" + +#: objects/ui_MainWindow.h:845 +msgid "Ctrl+[" +msgstr "Ctrl+[" + +#: objects/ui_MainWindow.h:846 +msgid "View All" +msgstr "&Voir Tout" + +#: objects/ui_MainWindow.h:847 +msgid "Convert Tabs to Spaces" +msgstr "Convertir les tabulations par des espaces" + +#: objects/ui_MainWindow.h:848 +msgid "Hide toolbars" +msgstr "Cacher les boîtes à outils" + +#: objects/ui_MainWindow.h:849 +msgid "Time:" +msgstr "Temps:" + +#: objects/ui_MainWindow.h:850 +msgid "FPS:" +msgstr "FPS:" + +#: objects/ui_MainWindow.h:851 +msgid "Steps:" +msgstr "Étapes:" + +#: objects/ui_MainWindow.h:852 +msgid "Dump Pictures" +msgstr "Vider les photos" + +#: objects/ui_MainWindow.h:853 +msgid "&File" +msgstr "&Fichier" + +#: objects/ui_MainWindow.h:854 +msgid "Recent Files" +msgstr "Fichiers Récents" + +#: objects/ui_MainWindow.h:856 +msgid "Export" +msgstr "Exporter" + +#: objects/ui_MainWindow.h:857 +msgid "&Edit" +msgstr "&Édition" + +#: objects/ui_MainWindow.h:858 +msgid "&Design" +msgstr "Conception" + +#: objects/ui_MainWindow.h:859 +msgid "&View" +msgstr "&Vue" + +#: objects/ui_MainWindow.h:860 +msgid "&Help" +msgstr "&Aide" + +#: objects/ui_MainWindow.h:863 +msgid "Find" +msgstr "Rechercher" + +#: objects/ui_MainWindow.h:864 objects/ui_MainWindow.h:871 +msgid "Replace" +msgstr "Remplacer" + +#: objects/ui_MainWindow.h:866 +msgid "Search string" +msgstr "Rechercher une chaîne de caractères" + +#: objects/ui_MainWindow.h:867 +msgid "<" +msgstr "<" + +#: objects/ui_MainWindow.h:868 +msgid ">" +msgstr ">" + +#: objects/ui_MainWindow.h:869 +msgid "Done" +msgstr "Terminer" + +#: objects/ui_MainWindow.h:870 +msgid "Replacement string" +msgstr "Remplacer une chaîne de caractères" + +#: objects/ui_MainWindow.h:872 +msgid "All" +msgstr "Tout" + +#: objects/ui_OpenCSGWarningDialog.h:86 +msgid "OpenGL Warning" +msgstr "Avertissements OpenGL" + +#: objects/ui_OpenCSGWarningDialog.h:87 +msgid "" +"\n" +"\n" +"

" +msgstr "" +"\n" +"\n" +"

" + +#: objects/ui_OpenCSGWarningDialog.h:92 +msgid "Enable OpenCSG" +msgstr "Activer OpenCSG" + +#: objects/ui_OpenCSGWarningDialog.h:93 +msgid "Show this message again" +msgstr "Affiche ce message à nouveau" + +#: objects/ui_Preferences.h:609 +msgid "3D View" +msgstr "&Vue 3D" + +#: objects/ui_Preferences.h:610 src/UIUtils.cc:85 +msgid "Advanced" +msgstr "Avancé" + +#: objects/ui_Preferences.h:611 src/mainwin.cc:2315 +msgid "Editor" +msgstr "Éditeur" + +#: objects/ui_Preferences.h:612 +msgid "Update" +msgstr "Mettre à jour" + +#: objects/ui_Preferences.h:613 objects/ui_Preferences.h:633 +msgid "Features" +msgstr "Fonctionnalités" + +#: objects/ui_Preferences.h:615 +msgid "Enable/Disable experimental features" +msgstr "Activer/Désactiver les fonctionnalités expérimentales" + +#: objects/ui_Preferences.h:617 +msgid "Color scheme:" +msgstr "Palette de couleurs:" + +#: objects/ui_Preferences.h:618 +msgid "Editor Type" +msgstr "Type d'éditeur" + +#: objects/ui_Preferences.h:621 +msgid "Simple Editor" +msgstr "Éditeur Simple" + +#: objects/ui_Preferences.h:622 +msgid "QScintilla Editor" +msgstr "Éditeur QScintilla" + +#: objects/ui_Preferences.h:624 +msgid "(requires restart)" +msgstr "(nécessite un redémarrage)" + +#: objects/ui_Preferences.h:625 +msgid "Font" +msgstr "Police" + +#: objects/ui_Preferences.h:626 +msgid "Color syntax highlighting" +msgstr "Colorer la coloration syntaxique" + +#: objects/ui_Preferences.h:627 +msgid "Use Ctrl/Cmd-Mouse-wheel to zoom text" +msgstr "Utiliser Ctrl/Cmd-Roulette-Souris pour agrandir le texte" + +#: objects/ui_Preferences.h:629 +msgid "Automatically check for updates" +msgstr "Vérifier les mises à jour automatiquement" + +#: objects/ui_Preferences.h:630 +msgid "Include development snapshots" +msgstr "Inclure les versions de développement" + +#: objects/ui_Preferences.h:631 +msgid "Check Now" +msgstr "Vérifier Maintenant" + +#: objects/ui_Preferences.h:632 +msgid "Last checked: " +msgstr "Dernière vérification:" + +#: objects/ui_Preferences.h:634 +msgid "OpenCSG" +msgstr "OpenCSG" + +#: objects/ui_Preferences.h:635 +msgid "Show capability warning" +msgstr "Afficher les avertissements de capacité" + +#: objects/ui_Preferences.h:636 +msgid "Enable for OpenGL 1.x" +msgstr "Activer pour OpenGL 1.x" + +#: objects/ui_Preferences.h:637 +msgid "Turn off rendering at " +msgstr "Désactiver le rendu à" + +#: objects/ui_Preferences.h:638 +msgid "elements" +msgstr "éléments" + +#: objects/ui_Preferences.h:639 +msgid "Force Goldfeather" +msgstr "Forcer Goldfeather" + +#: objects/ui_Preferences.h:640 +msgid "CGAL Cache size" +msgstr "Taille du Cache de CGAL" + +#: objects/ui_Preferences.h:641 objects/ui_Preferences.h:643 +msgid "bytes" +msgstr "octets" + +#: objects/ui_Preferences.h:642 +msgid "PolySet Cache size" +msgstr "Taille du Cache PolySet" + +#: objects/ui_Preferences.h:644 +msgid "Allow to open multiple documents" +msgstr "Autoriser l'ouverture de plusieurs documents" + +#: objects/ui_Preferences.h:645 +msgid "Enable docking of Editor and Console in different places" +msgstr "Activer l'ancrage de l'Éditeur et de la Console à différents endroits" + +#: objects/ui_Preferences.h:646 +msgid "Enable undocking of Editor and Console to separate windows" +msgstr "" +"Activer désancrage de l'Éditeur et de la Console dans des fenêtres séparés" + +#: objects/ui_Preferences.h:647 +msgid "Show Welcome Screen" +msgstr "Affiche l'écran d'accueil" + +#: objects/ui_Preferences.h:648 +msgid "Enable user interface localization (requires restart of OpenSCAD)" +msgstr "" +"Activer la localisation de l'interface utilisateur (Nécessite un redémarrage " +"d'OpenSCAD)" + +#: objects/ui_Preferences.h:649 +msgid "toolBar" +msgstr "boîte à outils" + +#: objects/ui_ProgressWidget.h:72 +msgid "Form" +msgstr "Formulaire" + +#: objects/ui_ProgressWidget.h:73 +msgid "%v / %m" +msgstr "%v / %m" + +#: src/mainwin.cc:768 src/mainwin.cc:1300 +msgid "Untitled.scad" +msgstr "Untitled.scad" + +#: src/mainwin.cc:1299 +msgid "Save File" +msgstr "Enregistrer le Fichier" + +#: src/mainwin.cc:1301 +msgid "OpenSCAD Designs (*.scad)" +msgstr "OpenSCAD Designs (*.scad)" + +#: src/mainwin.cc:1311 +msgid "" +"%1 already exists.\n" +"Do you want to replace it?" +msgstr "" +"%1 existe déjà.\n" +"Voulez-vous le remplacer?" + +#: src/mainwin.cc:1630 +msgid "Application" +msgstr "Application" + +#: src/mainwin.cc:1631 +msgid "" +"The document has been modified.\n" +"Do you really want to reload the file?" +msgstr "" +"Ce document a été modifié.\n" +"Voulez-vous vraiment recharger le fichier?" + +#: src/mainwin.cc:1942 src/mainwin.cc:1999 +msgid "Export %1 File" +msgstr "Exporter %1 Fichier" + +#: src/mainwin.cc:1943 src/mainwin.cc:2003 +msgid "%1 Files (*%2)" +msgstr "%1 Fichiers (*%2)" + +#: src/mainwin.cc:1944 +msgid "Untitled" +msgstr "Sans Titre" + +#: src/mainwin.cc:2001 +msgid "Untitled%1" +msgstr "Sans Titre%1" + +#: src/mainwin.cc:2052 +msgid "Export CSG File" +msgstr "Exporter un fichier CSG" + +#: src/mainwin.cc:2053 +msgid "Untitled.csg" +msgstr "SansTitre.csg" + +#: src/mainwin.cc:2054 +msgid "CSG Files (*.csg)" +msgstr "Fichiers CSG (*.csg)" + +#: src/mainwin.cc:2080 +msgid "Export Image" +msgstr "Exporter une Image" + +#: src/mainwin.cc:2080 +msgid "PNG Files (*.png)" +msgstr "Fichiers PNG (*.png)" + +#: src/mainwin.cc:2320 +msgid "Console" +msgstr "Console" + +#: src/mainwin.cc:2447 +msgid "The document has been modified." +msgstr "Ce document a été modifié" + +#: src/mainwin.cc:2448 +msgid "Do you want to save your changes?" +msgstr "Voulez-vous enregistrer vos changements?" + +#: src/QGLView.cc:114 +msgid "" +"\n" +"Using QGLWidget\n" +"\n" +msgstr "" +"\n" +"Utilise QGLWidget\n" +"\n" + +#: src/QGLView.cc:131 +msgid "" +"Warning: You may experience OpenCSG rendering errors.\n" +"\n" +msgstr "" +"Avertissement: Vous pourriez avoir des erreurs de rendu OpenCSG\n" +"\n" + +#: src/QGLView.cc:134 +msgid "" +"Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been " +"disabled.\n" +"\n" +msgstr "" +"Avertissement: Capacités OpenGL manquantes pour OpenCSG - OpenCSG a été " +"désactivé.\n" +"\n" + +#: src/QGLView.cc:137 +msgid "" +"It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 or " +"later.\n" +"Your renderer information is as follows:\n" +msgstr "" +"Il est hautement recommandé d'utiliser OpenSCAD sur un système compatible " +"OpenGL 2.0 ou ultérieur.\n" +"Voici les informations de votre moteur de rendu:\n" + +#: src/QGLView.cc:141 +#, c-format +msgid "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" +msgstr "" +"Version GLEW %s\n" +"%s (%s)\n" +"Version OpenGL %s\n" + +#: src/QGLView.cc:171 +#, c-format +msgid "" +"Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], " +"distance = %.2f" +msgstr "" +"Fenêtre de rendu: translation = [ %.2f %.2f %.2f ], rotation = [ %.2f %.2f " +"%.2f ], distance = %.2f" + +#: src/UIUtils.cc:85 +msgid "Basics" +msgstr "Bases" + +#: src/UIUtils.cc:85 +msgid "Shapes" +msgstr "Formes" + +#: src/UIUtils.cc:85 +msgid "Extrusion" +msgstr "Extrusion" + +#~ msgid "Paste font selector to Editor Window" +#~ msgstr "Coller le sélecteur de polices dans l'éditeur de fenêtres" + +#~ msgid "About OpenSCAD " +#~ msgstr "À propos de OpenSCAD" + +#~ msgid "Check for Update.." +#~ msgstr "Vérifier les mises à jours" + +#~ msgid "OpenGL Info" +#~ msgstr "Information OpenGL" diff --git a/locale/openscad.pot b/locale/openscad.pot new file mode 100644 index 00000000..b3587cc0 --- /dev/null +++ b/locale/openscad.pot @@ -0,0 +1,946 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: OpenSCAD 2014.12.22\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 23:37+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: objects/ui_AboutDialog.h:51 src/AboutDialog.h:15 +msgid "About OpenSCAD" +msgstr "" + +#: objects/ui_FontListDialog.h:102 +msgid "OpenSCAD Font List" +msgstr "" + +#: objects/ui_FontListDialog.h:103 objects/ui_LibraryInfoDialog.h:77 +msgid "&OK" +msgstr "" + +#: objects/ui_FontListDialog.h:104 +msgid "Copy to Clipboard" +msgstr "" + +#: objects/ui_FontListDialog.h:105 +msgid "Filter:" +msgstr "" + +#: objects/ui_FontListDialog.h:106 +msgid "" +"

This list shows the fonts currently registered with " +"OpenSCAD.

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" +msgstr "" + +#: objects/ui_launchingscreen.h:276 +msgid "Welcome to OpenSCAD" +msgstr "" + +#: objects/ui_launchingscreen.h:277 +msgid "New" +msgstr "" + +#: objects/ui_launchingscreen.h:278 +msgid "Open" +msgstr "" + +#: objects/ui_launchingscreen.h:279 +msgid "Help" +msgstr "" + +#: objects/ui_launchingscreen.h:280 +msgid "Recents" +msgstr "" + +#: objects/ui_launchingscreen.h:281 +msgid "Open Recent" +msgstr "" + +#: objects/ui_launchingscreen.h:282 objects/ui_launchingscreen.h:284 +#: objects/ui_MainWindow.h:855 +msgid "Examples" +msgstr "" + +#: objects/ui_launchingscreen.h:285 +msgid "Open Example" +msgstr "" + +#: objects/ui_launchingscreen.h:287 +msgid "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" +msgstr "" + +#: objects/ui_launchingscreen.h:294 +msgid "Don't show again" +msgstr "" + +#: objects/ui_LibraryInfoDialog.h:75 +msgid "Lib & Build Info" +msgstr "" + +#: objects/ui_LibraryInfoDialog.h:76 +msgid "OpenSCAD Detailed Library and Build Information" +msgstr "" + +#: objects/ui_MainWindow.h:731 +msgid "&New" +msgstr "" + +#: objects/ui_MainWindow.h:732 +msgid "Ctrl+N" +msgstr "" + +#: objects/ui_MainWindow.h:733 +msgid "&Open..." +msgstr "" + +#: objects/ui_MainWindow.h:734 +msgid "Ctrl+O" +msgstr "" + +#: objects/ui_MainWindow.h:735 +msgid "&Save" +msgstr "" + +#: objects/ui_MainWindow.h:736 +msgid "Ctrl+S" +msgstr "" + +#: objects/ui_MainWindow.h:737 +msgid "Save &As..." +msgstr "" + +#: objects/ui_MainWindow.h:738 +msgid "Ctrl+Shift+S" +msgstr "" + +#: objects/ui_MainWindow.h:739 +msgid "&Reload" +msgstr "" + +#: objects/ui_MainWindow.h:740 +msgid "Ctrl+R" +msgstr "" + +#: objects/ui_MainWindow.h:741 +msgid "&Quit" +msgstr "" + +#: objects/ui_MainWindow.h:742 +msgid "Ctrl+Q" +msgstr "" + +#: objects/ui_MainWindow.h:743 +msgid "&Undo" +msgstr "" + +#: objects/ui_MainWindow.h:744 +msgid "Ctrl+Z" +msgstr "" + +#: objects/ui_MainWindow.h:745 +msgid "&Redo" +msgstr "" + +#: objects/ui_MainWindow.h:746 +msgid "Ctrl+Shift+Z" +msgstr "" + +#: objects/ui_MainWindow.h:747 +msgid "Cu&t" +msgstr "" + +#: objects/ui_MainWindow.h:748 +msgid "Ctrl+X" +msgstr "" + +#: objects/ui_MainWindow.h:749 +msgid "&Copy" +msgstr "" + +#: objects/ui_MainWindow.h:750 +msgid "Ctrl+C" +msgstr "" + +#: objects/ui_MainWindow.h:751 +msgid "&Paste" +msgstr "" + +#: objects/ui_MainWindow.h:752 +msgid "Ctrl+V" +msgstr "" + +#: objects/ui_MainWindow.h:753 +msgid "&Indent" +msgstr "" + +#: objects/ui_MainWindow.h:754 +msgid "Ctrl+I" +msgstr "" + +#: objects/ui_MainWindow.h:755 +msgid "U&nindent" +msgstr "" + +#: objects/ui_MainWindow.h:756 +msgid "Ctrl+Shift+I" +msgstr "" + +#: objects/ui_MainWindow.h:757 +msgid "C&omment" +msgstr "" + +#: objects/ui_MainWindow.h:758 +msgid "Ctrl+D" +msgstr "" + +#: objects/ui_MainWindow.h:759 +msgid "Unco&mment" +msgstr "" + +#: objects/ui_MainWindow.h:760 +msgid "Ctrl+Shift+D" +msgstr "" + +#: objects/ui_MainWindow.h:761 +msgid "Paste viewport translation" +msgstr "" + +#: objects/ui_MainWindow.h:762 +msgid "Ctrl+T" +msgstr "" + +#: objects/ui_MainWindow.h:763 +msgid "Paste viewport rotation" +msgstr "" + +#: objects/ui_MainWindow.h:764 objects/ui_MainWindow.h:842 +msgid "Zoom In" +msgstr "" + +#: objects/ui_MainWindow.h:765 +msgid "Ctrl++" +msgstr "" + +#: objects/ui_MainWindow.h:766 objects/ui_MainWindow.h:844 +msgid "Zoom Out" +msgstr "" + +#: objects/ui_MainWindow.h:767 +msgid "Ctrl+-" +msgstr "" + +#: objects/ui_MainWindow.h:768 +msgid "Hide editor" +msgstr "" + +#: objects/ui_MainWindow.h:769 +msgid "&Reload and Preview" +msgstr "" + +#: objects/ui_MainWindow.h:770 +msgid "F4" +msgstr "" + +#: objects/ui_MainWindow.h:771 +msgid "&Preview" +msgstr "" + +#: objects/ui_MainWindow.h:772 +msgid "F5" +msgstr "" + +#: objects/ui_MainWindow.h:773 +msgid "&Render" +msgstr "" + +#: objects/ui_MainWindow.h:774 +msgid "F6" +msgstr "" + +#: objects/ui_MainWindow.h:775 +msgid "Check Validity" +msgstr "" + +#: objects/ui_MainWindow.h:776 +msgid "Display &AST..." +msgstr "" + +#: objects/ui_MainWindow.h:777 +msgid "Display CSG &Tree..." +msgstr "" + +#: objects/ui_MainWindow.h:778 +msgid "Display CSG &Products..." +msgstr "" + +#: objects/ui_MainWindow.h:779 +msgid "Export as &STL..." +msgstr "" + +#: objects/ui_MainWindow.h:780 +msgid "Export as &OFF..." +msgstr "" + +#: objects/ui_MainWindow.h:781 +msgid "Preview" +msgstr "" + +#: objects/ui_MainWindow.h:782 +msgid "F9" +msgstr "" + +#: objects/ui_MainWindow.h:783 +msgid "Surfaces" +msgstr "" + +#: objects/ui_MainWindow.h:784 +msgid "F10" +msgstr "" + +#: objects/ui_MainWindow.h:785 +msgid "Wireframe" +msgstr "" + +#: objects/ui_MainWindow.h:786 +msgid "F11" +msgstr "" + +#: objects/ui_MainWindow.h:787 +msgid "Thrown Together" +msgstr "" + +#: objects/ui_MainWindow.h:788 +msgid "F12" +msgstr "" + +#: objects/ui_MainWindow.h:789 +msgid "Show Edges" +msgstr "" + +#: objects/ui_MainWindow.h:790 +msgid "Ctrl+1" +msgstr "" + +#: objects/ui_MainWindow.h:791 +msgid "Show Axes" +msgstr "" + +#: objects/ui_MainWindow.h:792 +msgid "Ctrl+2" +msgstr "" + +#: objects/ui_MainWindow.h:793 +msgid "Show Crosshairs" +msgstr "" + +#: objects/ui_MainWindow.h:794 +msgid "Ctrl+3" +msgstr "" + +#: objects/ui_MainWindow.h:795 +msgid "Animate" +msgstr "" + +#: objects/ui_MainWindow.h:796 +msgid "Top" +msgstr "" + +#: objects/ui_MainWindow.h:797 +msgid "Ctrl+4" +msgstr "" + +#: objects/ui_MainWindow.h:798 +msgid "Bottom" +msgstr "" + +#: objects/ui_MainWindow.h:799 +msgid "Ctrl+5" +msgstr "" + +#: objects/ui_MainWindow.h:800 +msgid "Left" +msgstr "" + +#: objects/ui_MainWindow.h:801 +msgid "Ctrl+6" +msgstr "" + +#: objects/ui_MainWindow.h:802 +msgid "Right" +msgstr "" + +#: objects/ui_MainWindow.h:803 +msgid "Ctrl+7" +msgstr "" + +#: objects/ui_MainWindow.h:804 +msgid "Front" +msgstr "" + +#: objects/ui_MainWindow.h:805 +msgid "Ctrl+8" +msgstr "" + +#: objects/ui_MainWindow.h:806 +msgid "Back" +msgstr "" + +#: objects/ui_MainWindow.h:807 +msgid "Ctrl+9" +msgstr "" + +#: objects/ui_MainWindow.h:808 +msgid "Diagonal" +msgstr "" + +#: objects/ui_MainWindow.h:809 +msgid "Ctrl+0" +msgstr "" + +#: objects/ui_MainWindow.h:810 +msgid "Center" +msgstr "" + +#: objects/ui_MainWindow.h:811 +msgid "Perspective" +msgstr "" + +#: objects/ui_MainWindow.h:812 +msgid "Orthogonal" +msgstr "" + +#: objects/ui_MainWindow.h:813 +msgid "Hide console" +msgstr "" + +#: objects/ui_MainWindow.h:814 +msgid "About" +msgstr "" + +#: objects/ui_MainWindow.h:815 +msgid "Documentation" +msgstr "" + +#: objects/ui_MainWindow.h:816 +msgid "Clear Recent" +msgstr "" + +#: objects/ui_MainWindow.h:817 +msgid "Export as DXF..." +msgstr "" + +#: objects/ui_MainWindow.h:818 objects/ui_OpenCSGWarningDialog.h:94 +msgid "Close" +msgstr "" + +#: objects/ui_MainWindow.h:819 +msgid "Ctrl+W" +msgstr "" + +#: objects/ui_MainWindow.h:820 objects/ui_Preferences.h:608 +msgid "Preferences" +msgstr "" + +#: objects/ui_MainWindow.h:821 +msgid "Find..." +msgstr "" + +#: objects/ui_MainWindow.h:822 +msgid "Ctrl+F" +msgstr "" + +#: objects/ui_MainWindow.h:823 +msgid "Find and Replace..." +msgstr "" + +#: objects/ui_MainWindow.h:824 +msgid "Ctrl+Alt+F" +msgstr "" + +#: objects/ui_MainWindow.h:825 +msgid "Find Next" +msgstr "" + +#: objects/ui_MainWindow.h:826 +msgid "Ctrl+G" +msgstr "" + +#: objects/ui_MainWindow.h:827 +msgid "Find Previous" +msgstr "" + +#: objects/ui_MainWindow.h:828 +msgid "Ctrl+Shift+G" +msgstr "" + +#: objects/ui_MainWindow.h:829 +msgid "Use Selection for Find" +msgstr "" + +#: objects/ui_MainWindow.h:830 +msgid "Ctrl+E" +msgstr "" + +#: objects/ui_MainWindow.h:831 +msgid "Flush Caches" +msgstr "" + +#: objects/ui_MainWindow.h:832 +msgid "OpenSCAD Homepage" +msgstr "" + +#: objects/ui_MainWindow.h:833 +msgid "Automatic Reload and Preview" +msgstr "" + +#: objects/ui_MainWindow.h:834 +msgid "Export as Image..." +msgstr "" + +#: objects/ui_MainWindow.h:835 +msgid "Export as CSG..." +msgstr "" + +#: objects/ui_MainWindow.h:836 +msgid "Library info" +msgstr "" + +#: objects/ui_MainWindow.h:837 +msgid "Show Library Folder..." +msgstr "" + +#: objects/ui_MainWindow.h:838 +msgid "Reset View" +msgstr "" + +#: objects/ui_MainWindow.h:839 +msgid "Font List" +msgstr "" + +#: objects/ui_MainWindow.h:840 +msgid "Export as SVG..." +msgstr "" + +#: objects/ui_MainWindow.h:841 +msgid "Export as AMF..." +msgstr "" + +#: objects/ui_MainWindow.h:843 +msgid "Ctrl+]" +msgstr "" + +#: objects/ui_MainWindow.h:845 +msgid "Ctrl+[" +msgstr "" + +#: objects/ui_MainWindow.h:846 +msgid "View All" +msgstr "" + +#: objects/ui_MainWindow.h:847 +msgid "Convert Tabs to Spaces" +msgstr "" + +#: objects/ui_MainWindow.h:848 +msgid "Hide toolbars" +msgstr "" + +#: objects/ui_MainWindow.h:849 +msgid "Time:" +msgstr "" + +#: objects/ui_MainWindow.h:850 +msgid "FPS:" +msgstr "" + +#: objects/ui_MainWindow.h:851 +msgid "Steps:" +msgstr "" + +#: objects/ui_MainWindow.h:852 +msgid "Dump Pictures" +msgstr "" + +#: objects/ui_MainWindow.h:853 +msgid "&File" +msgstr "" + +#: objects/ui_MainWindow.h:854 +msgid "Recent Files" +msgstr "" + +#: objects/ui_MainWindow.h:856 +msgid "Export" +msgstr "" + +#: objects/ui_MainWindow.h:857 +msgid "&Edit" +msgstr "" + +#: objects/ui_MainWindow.h:858 +msgid "&Design" +msgstr "" + +#: objects/ui_MainWindow.h:859 +msgid "&View" +msgstr "" + +#: objects/ui_MainWindow.h:860 +msgid "&Help" +msgstr "" + +#: objects/ui_MainWindow.h:863 +msgid "Find" +msgstr "" + +#: objects/ui_MainWindow.h:864 objects/ui_MainWindow.h:871 +msgid "Replace" +msgstr "" + +#: objects/ui_MainWindow.h:866 +msgid "Search string" +msgstr "" + +#: objects/ui_MainWindow.h:867 +msgid "<" +msgstr "" + +#: objects/ui_MainWindow.h:868 +msgid ">" +msgstr "" + +#: objects/ui_MainWindow.h:869 +msgid "Done" +msgstr "" + +#: objects/ui_MainWindow.h:870 +msgid "Replacement string" +msgstr "" + +#: objects/ui_MainWindow.h:872 +msgid "All" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:86 +msgid "OpenGL Warning" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:87 +msgid "" +"\n" +"\n" +"

" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:92 +msgid "Enable OpenCSG" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:93 +msgid "Show this message again" +msgstr "" + +#: objects/ui_Preferences.h:609 +msgid "3D View" +msgstr "" + +#: objects/ui_Preferences.h:610 src/UIUtils.cc:85 +msgid "Advanced" +msgstr "" + +#: objects/ui_Preferences.h:611 src/mainwin.cc:2315 +msgid "Editor" +msgstr "" + +#: objects/ui_Preferences.h:612 +msgid "Update" +msgstr "" + +#: objects/ui_Preferences.h:613 objects/ui_Preferences.h:633 +msgid "Features" +msgstr "" + +#: objects/ui_Preferences.h:615 +msgid "Enable/Disable experimental features" +msgstr "" + +#: objects/ui_Preferences.h:617 +msgid "Color scheme:" +msgstr "" + +#: objects/ui_Preferences.h:618 +msgid "Editor Type" +msgstr "" + +#: objects/ui_Preferences.h:621 +msgid "Simple Editor" +msgstr "" + +#: objects/ui_Preferences.h:622 +msgid "QScintilla Editor" +msgstr "" + +#: objects/ui_Preferences.h:624 +msgid "(requires restart)" +msgstr "" + +#: objects/ui_Preferences.h:625 +msgid "Font" +msgstr "" + +#: objects/ui_Preferences.h:626 +msgid "Color syntax highlighting" +msgstr "" + +#: objects/ui_Preferences.h:627 +msgid "Use Ctrl/Cmd-Mouse-wheel to zoom text" +msgstr "" + +#: objects/ui_Preferences.h:629 +msgid "Automatically check for updates" +msgstr "" + +#: objects/ui_Preferences.h:630 +msgid "Include development snapshots" +msgstr "" + +#: objects/ui_Preferences.h:631 +msgid "Check Now" +msgstr "" + +#: objects/ui_Preferences.h:632 +msgid "Last checked: " +msgstr "" + +#: objects/ui_Preferences.h:634 +msgid "OpenCSG" +msgstr "" + +#: objects/ui_Preferences.h:635 +msgid "Show capability warning" +msgstr "" + +#: objects/ui_Preferences.h:636 +msgid "Enable for OpenGL 1.x" +msgstr "" + +#: objects/ui_Preferences.h:637 +msgid "Turn off rendering at " +msgstr "" + +#: objects/ui_Preferences.h:638 +msgid "elements" +msgstr "" + +#: objects/ui_Preferences.h:639 +msgid "Force Goldfeather" +msgstr "" + +#: objects/ui_Preferences.h:640 +msgid "CGAL Cache size" +msgstr "" + +#: objects/ui_Preferences.h:641 objects/ui_Preferences.h:643 +msgid "bytes" +msgstr "" + +#: objects/ui_Preferences.h:642 +msgid "PolySet Cache size" +msgstr "" + +#: objects/ui_Preferences.h:644 +msgid "Allow to open multiple documents" +msgstr "" + +#: objects/ui_Preferences.h:645 +msgid "Enable docking of Editor and Console in different places" +msgstr "" + +#: objects/ui_Preferences.h:646 +msgid "Enable undocking of Editor and Console to separate windows" +msgstr "" + +#: objects/ui_Preferences.h:647 +msgid "Show Welcome Screen" +msgstr "" + +#: objects/ui_Preferences.h:648 +msgid "Enable user interface localization (requires restart of OpenSCAD)" +msgstr "" + +#: objects/ui_Preferences.h:649 +msgid "toolBar" +msgstr "" + +#: objects/ui_ProgressWidget.h:72 +msgid "Form" +msgstr "" + +#: objects/ui_ProgressWidget.h:73 +msgid "%v / %m" +msgstr "" + +#: src/mainwin.cc:768 src/mainwin.cc:1300 +msgid "Untitled.scad" +msgstr "" + +#: src/mainwin.cc:1299 +msgid "Save File" +msgstr "" + +#: src/mainwin.cc:1301 +msgid "OpenSCAD Designs (*.scad)" +msgstr "" + +#: src/mainwin.cc:1311 +msgid "" +"%1 already exists.\n" +"Do you want to replace it?" +msgstr "" + +#: src/mainwin.cc:1630 +msgid "Application" +msgstr "" + +#: src/mainwin.cc:1631 +msgid "" +"The document has been modified.\n" +"Do you really want to reload the file?" +msgstr "" + +#: src/mainwin.cc:1942 src/mainwin.cc:1999 +msgid "Export %1 File" +msgstr "" + +#: src/mainwin.cc:1943 src/mainwin.cc:2003 +msgid "%1 Files (*%2)" +msgstr "" + +#: src/mainwin.cc:1944 +msgid "Untitled" +msgstr "" + +#: src/mainwin.cc:2001 +msgid "Untitled%1" +msgstr "" + +#: src/mainwin.cc:2052 +msgid "Export CSG File" +msgstr "" + +#: src/mainwin.cc:2053 +msgid "Untitled.csg" +msgstr "" + +#: src/mainwin.cc:2054 +msgid "CSG Files (*.csg)" +msgstr "" + +#: src/mainwin.cc:2080 +msgid "Export Image" +msgstr "" + +#: src/mainwin.cc:2080 +msgid "PNG Files (*.png)" +msgstr "" + +#: src/mainwin.cc:2320 +msgid "Console" +msgstr "" + +#: src/mainwin.cc:2447 +msgid "The document has been modified." +msgstr "" + +#: src/mainwin.cc:2448 +msgid "Do you want to save your changes?" +msgstr "" + +#: src/QGLView.cc:114 +msgid "" +"\n" +"Using QGLWidget\n" +"\n" +msgstr "" + +#: src/QGLView.cc:131 +msgid "" +"Warning: You may experience OpenCSG rendering errors.\n" +"\n" +msgstr "" + +#: src/QGLView.cc:134 +msgid "" +"Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been " +"disabled.\n" +"\n" +msgstr "" + +#: src/QGLView.cc:137 +msgid "" +"It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 or " +"later.\n" +"Your renderer information is as follows:\n" +msgstr "" + +#: src/QGLView.cc:141 +#, c-format +msgid "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" +msgstr "" + +#: src/QGLView.cc:171 +#, c-format +msgid "" +"Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], " +"distance = %.2f" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Basics" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Shapes" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Extrusion" +msgstr "" diff --git a/locale/ru.po b/locale/ru.po new file mode 100644 index 00000000..8fc11980 --- /dev/null +++ b/locale/ru.po @@ -0,0 +1,1000 @@ +# Russian translations for OpenSCAD package. +# Copyright (C) 2013 THE OpenSCAD'S COPYRIGHT HOLDER +# This file is distributed under the same license as the OpenSCAD package. +# , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: OpenSCAD 2014.01.05\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 23:28+0100\n" +"PO-Revision-Date: 2013-02-24 17:50+0100\n" +"Last-Translator: \n" +"Language-Team: Russian\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: objects/ui_AboutDialog.h:51 src/AboutDialog.h:15 +msgid "About OpenSCAD" +msgstr "О программе OpenSCAD" + +#: objects/ui_FontListDialog.h:102 +#, fuzzy +msgid "OpenSCAD Font List" +msgstr "Шрифт" + +#: objects/ui_FontListDialog.h:103 objects/ui_LibraryInfoDialog.h:77 +msgid "&OK" +msgstr "" + +#: objects/ui_FontListDialog.h:104 +msgid "Copy to Clipboard" +msgstr "" + +#: objects/ui_FontListDialog.h:105 +msgid "Filter:" +msgstr "" + +#: objects/ui_FontListDialog.h:106 +msgid "" +"

This list shows the fonts currently registered with " +"OpenSCAD.

Example:

  text(t = "
+""OpenSCAD", font = "DejaVu Sans");
  text(t = "OpenSCAD", font = "
+""Liberation Sans:style=Italic");
" +msgstr "" + +#: objects/ui_launchingscreen.h:276 +#, fuzzy +msgid "Welcome to OpenSCAD" +msgstr "О программе OpenSCAD" + +#: objects/ui_launchingscreen.h:277 +#, fuzzy +msgid "New" +msgstr "&Создать" + +#: objects/ui_launchingscreen.h:278 +#, fuzzy +msgid "Open" +msgstr "&Открыть..." + +#: objects/ui_launchingscreen.h:279 +#, fuzzy +msgid "Help" +msgstr "&Справка" + +#: objects/ui_launchingscreen.h:280 +#, fuzzy +msgid "Recents" +msgstr "Открыть файл" + +#: objects/ui_launchingscreen.h:281 +msgid "Open Recent" +msgstr "Открыть недавние" + +#: objects/ui_launchingscreen.h:282 objects/ui_launchingscreen.h:284 +#: objects/ui_MainWindow.h:855 +msgid "Examples" +msgstr "Примеры" + +#: objects/ui_launchingscreen.h:285 +#, fuzzy +msgid "Open Example" +msgstr "Примеры" + +#: objects/ui_launchingscreen.h:287 +msgid "" +"\n" +"

OpenSCAD

\n" +"

The Programmers Solid 3D CAD Modeller

\n" +"\n" +"\n" +"\n" +msgstr "" + +#: objects/ui_launchingscreen.h:294 +msgid "Don't show again" +msgstr "" + +#: objects/ui_LibraryInfoDialog.h:75 +msgid "Lib & Build Info" +msgstr "" + +#: objects/ui_LibraryInfoDialog.h:76 +msgid "OpenSCAD Detailed Library and Build Information" +msgstr "Подробная информация о библиотеках и сборке OpenSCAD" + +#: objects/ui_MainWindow.h:731 +msgid "&New" +msgstr "&Создать" + +#: objects/ui_MainWindow.h:732 +msgid "Ctrl+N" +msgstr "" + +#: objects/ui_MainWindow.h:733 +msgid "&Open..." +msgstr "&Открыть..." + +#: objects/ui_MainWindow.h:734 +msgid "Ctrl+O" +msgstr "" + +#: objects/ui_MainWindow.h:735 +msgid "&Save" +msgstr "&Сохранить" + +#: objects/ui_MainWindow.h:736 +msgid "Ctrl+S" +msgstr "" + +#: objects/ui_MainWindow.h:737 +msgid "Save &As..." +msgstr "Сохранить &как..." + +#: objects/ui_MainWindow.h:738 +msgid "Ctrl+Shift+S" +msgstr "" + +#: objects/ui_MainWindow.h:739 +msgid "&Reload" +msgstr "&Обновить" + +#: objects/ui_MainWindow.h:740 +msgid "Ctrl+R" +msgstr "" + +#: objects/ui_MainWindow.h:741 +msgid "&Quit" +msgstr "&Выход" + +#: objects/ui_MainWindow.h:742 +msgid "Ctrl+Q" +msgstr "" + +#: objects/ui_MainWindow.h:743 +msgid "&Undo" +msgstr "&Отменить" + +#: objects/ui_MainWindow.h:744 +msgid "Ctrl+Z" +msgstr "" + +#: objects/ui_MainWindow.h:745 +msgid "&Redo" +msgstr "&Повторить" + +#: objects/ui_MainWindow.h:746 +msgid "Ctrl+Shift+Z" +msgstr "" + +#: objects/ui_MainWindow.h:747 +msgid "Cu&t" +msgstr "Вы&резать" + +#: objects/ui_MainWindow.h:748 +msgid "Ctrl+X" +msgstr "" + +#: objects/ui_MainWindow.h:749 +msgid "&Copy" +msgstr "&Копировать" + +#: objects/ui_MainWindow.h:750 +msgid "Ctrl+C" +msgstr "" + +#: objects/ui_MainWindow.h:751 +msgid "&Paste" +msgstr "&Вставить" + +#: objects/ui_MainWindow.h:752 +msgid "Ctrl+V" +msgstr "" + +#: objects/ui_MainWindow.h:753 +msgid "&Indent" +msgstr "&Добавить отступ" + +#: objects/ui_MainWindow.h:754 +msgid "Ctrl+I" +msgstr "" + +#: objects/ui_MainWindow.h:755 +msgid "U&nindent" +msgstr "У&брать отступ" + +#: objects/ui_MainWindow.h:756 +msgid "Ctrl+Shift+I" +msgstr "" + +#: objects/ui_MainWindow.h:757 +msgid "C&omment" +msgstr "Зако&мментировать" + +#: objects/ui_MainWindow.h:758 +msgid "Ctrl+D" +msgstr "" + +#: objects/ui_MainWindow.h:759 +msgid "Unco&mment" +msgstr "Р&аскомментировать" + +#: objects/ui_MainWindow.h:760 +msgid "Ctrl+Shift+D" +msgstr "" + +#: objects/ui_MainWindow.h:761 +msgid "Paste viewport translation" +msgstr "Вставить смещение точки обзора" + +#: objects/ui_MainWindow.h:762 +msgid "Ctrl+T" +msgstr "" + +#: objects/ui_MainWindow.h:763 +msgid "Paste viewport rotation" +msgstr "Вставить поворот точки обзора" + +#: objects/ui_MainWindow.h:764 objects/ui_MainWindow.h:842 +msgid "Zoom In" +msgstr "Увеличить масштаб" + +#: objects/ui_MainWindow.h:765 +msgid "Ctrl++" +msgstr "" + +#: objects/ui_MainWindow.h:766 objects/ui_MainWindow.h:844 +msgid "Zoom Out" +msgstr "Уменьшить масштаб" + +#: objects/ui_MainWindow.h:767 +msgid "Ctrl+-" +msgstr "" + +#: objects/ui_MainWindow.h:768 +msgid "Hide editor" +msgstr "Скрыть редактор" + +#: objects/ui_MainWindow.h:769 +#, fuzzy +msgid "&Reload and Preview" +msgstr "&Обновить и компилировать" + +#: objects/ui_MainWindow.h:770 +msgid "F4" +msgstr "" + +#: objects/ui_MainWindow.h:771 +msgid "&Preview" +msgstr "" + +#: objects/ui_MainWindow.h:772 +msgid "F5" +msgstr "" + +#: objects/ui_MainWindow.h:773 +msgid "&Render" +msgstr "" + +#: objects/ui_MainWindow.h:774 +msgid "F6" +msgstr "" + +#: objects/ui_MainWindow.h:775 +msgid "Check Validity" +msgstr "" + +#: objects/ui_MainWindow.h:776 +msgid "Display &AST..." +msgstr "Показать &AST..." + +#: objects/ui_MainWindow.h:777 +msgid "Display CSG &Tree..." +msgstr "Показать &дерево CSG..." + +#: objects/ui_MainWindow.h:778 +msgid "Display CSG &Products..." +msgstr "Показать &результаты CSG..." + +#: objects/ui_MainWindow.h:779 +msgid "Export as &STL..." +msgstr "Экспортировать в &STL..." + +#: objects/ui_MainWindow.h:780 +msgid "Export as &OFF..." +msgstr "Экспортировать в &OFF..." + +#: objects/ui_MainWindow.h:781 +msgid "Preview" +msgstr "" + +#: objects/ui_MainWindow.h:782 +msgid "F9" +msgstr "" + +#: objects/ui_MainWindow.h:783 +#, fuzzy +msgid "Surfaces" +msgstr "Поверхности CGAL" + +#: objects/ui_MainWindow.h:784 +msgid "F10" +msgstr "" + +#: objects/ui_MainWindow.h:785 +msgid "Wireframe" +msgstr "" + +#: objects/ui_MainWindow.h:786 +msgid "F11" +msgstr "" + +#: objects/ui_MainWindow.h:787 +msgid "Thrown Together" +msgstr "Всё вместе" + +#: objects/ui_MainWindow.h:788 +msgid "F12" +msgstr "" + +#: objects/ui_MainWindow.h:789 +msgid "Show Edges" +msgstr "Показывать рёбра" + +#: objects/ui_MainWindow.h:790 +msgid "Ctrl+1" +msgstr "" + +#: objects/ui_MainWindow.h:791 +msgid "Show Axes" +msgstr "Показывать оси" + +#: objects/ui_MainWindow.h:792 +msgid "Ctrl+2" +msgstr "" + +#: objects/ui_MainWindow.h:793 +msgid "Show Crosshairs" +msgstr "Показывать перекрестия" + +#: objects/ui_MainWindow.h:794 +msgid "Ctrl+3" +msgstr "" + +#: objects/ui_MainWindow.h:795 +msgid "Animate" +msgstr "Анимация" + +#: objects/ui_MainWindow.h:796 +msgid "Top" +msgstr "Сверху" + +#: objects/ui_MainWindow.h:797 +msgid "Ctrl+4" +msgstr "" + +#: objects/ui_MainWindow.h:798 +msgid "Bottom" +msgstr "Снизу" + +#: objects/ui_MainWindow.h:799 +msgid "Ctrl+5" +msgstr "" + +#: objects/ui_MainWindow.h:800 +msgid "Left" +msgstr "Слева" + +#: objects/ui_MainWindow.h:801 +msgid "Ctrl+6" +msgstr "" + +#: objects/ui_MainWindow.h:802 +msgid "Right" +msgstr "Справа" + +#: objects/ui_MainWindow.h:803 +msgid "Ctrl+7" +msgstr "" + +#: objects/ui_MainWindow.h:804 +msgid "Front" +msgstr "Спереди" + +#: objects/ui_MainWindow.h:805 +msgid "Ctrl+8" +msgstr "" + +#: objects/ui_MainWindow.h:806 +msgid "Back" +msgstr "Сзади" + +#: objects/ui_MainWindow.h:807 +msgid "Ctrl+9" +msgstr "" + +#: objects/ui_MainWindow.h:808 +msgid "Diagonal" +msgstr "Аксонометрический" + +#: objects/ui_MainWindow.h:809 +msgid "Ctrl+0" +msgstr "" + +#: objects/ui_MainWindow.h:810 +msgid "Center" +msgstr "По центру" + +#: objects/ui_MainWindow.h:811 +msgid "Perspective" +msgstr "Перспектива" + +#: objects/ui_MainWindow.h:812 +msgid "Orthogonal" +msgstr "Прямоугольная проекция" + +#: objects/ui_MainWindow.h:813 +msgid "Hide console" +msgstr "Скрыть консоль" + +#: objects/ui_MainWindow.h:814 +msgid "About" +msgstr "О программе" + +#: objects/ui_MainWindow.h:815 +msgid "Documentation" +msgstr "Документация" + +#: objects/ui_MainWindow.h:816 +msgid "Clear Recent" +msgstr "Очистить список" + +#: objects/ui_MainWindow.h:817 +msgid "Export as DXF..." +msgstr "Экспортировать в DXF..." + +#: objects/ui_MainWindow.h:818 objects/ui_OpenCSGWarningDialog.h:94 +msgid "Close" +msgstr "Закрыть" + +#: objects/ui_MainWindow.h:819 +msgid "Ctrl+W" +msgstr "" + +#: objects/ui_MainWindow.h:820 objects/ui_Preferences.h:608 +msgid "Preferences" +msgstr "Настройки" + +#: objects/ui_MainWindow.h:821 +msgid "Find..." +msgstr "" + +#: objects/ui_MainWindow.h:822 +msgid "Ctrl+F" +msgstr "" + +#: objects/ui_MainWindow.h:823 +msgid "Find and Replace..." +msgstr "" + +#: objects/ui_MainWindow.h:824 +msgid "Ctrl+Alt+F" +msgstr "" + +#: objects/ui_MainWindow.h:825 +msgid "Find Next" +msgstr "" + +#: objects/ui_MainWindow.h:826 +msgid "Ctrl+G" +msgstr "" + +#: objects/ui_MainWindow.h:827 +msgid "Find Previous" +msgstr "" + +#: objects/ui_MainWindow.h:828 +msgid "Ctrl+Shift+G" +msgstr "" + +#: objects/ui_MainWindow.h:829 +msgid "Use Selection for Find" +msgstr "" + +#: objects/ui_MainWindow.h:830 +msgid "Ctrl+E" +msgstr "" + +#: objects/ui_MainWindow.h:831 +msgid "Flush Caches" +msgstr "Очистить кэш" + +#: objects/ui_MainWindow.h:832 +msgid "OpenSCAD Homepage" +msgstr "Домашняя страница OpenSCAD" + +#: objects/ui_MainWindow.h:833 +#, fuzzy +msgid "Automatic Reload and Preview" +msgstr "Автоматически обновлять и комилировать" + +#: objects/ui_MainWindow.h:834 +msgid "Export as Image..." +msgstr "Экспортировать в растр..." + +#: objects/ui_MainWindow.h:835 +msgid "Export as CSG..." +msgstr "Экспортировать в CSG..." + +#: objects/ui_MainWindow.h:836 +msgid "Library info" +msgstr "Информация о библиотеках" + +#: objects/ui_MainWindow.h:837 +msgid "Show Library Folder..." +msgstr "Открыть каталог библиотек..." + +#: objects/ui_MainWindow.h:838 +msgid "Reset View" +msgstr "Сбросить настройки вида" + +#: objects/ui_MainWindow.h:839 +#, fuzzy +msgid "Font List" +msgstr "Шрифт" + +#: objects/ui_MainWindow.h:840 +#, fuzzy +msgid "Export as SVG..." +msgstr "Экспортировать в CSG..." + +#: objects/ui_MainWindow.h:841 +#, fuzzy +msgid "Export as AMF..." +msgstr "Экспортировать в DXF..." + +#: objects/ui_MainWindow.h:843 +msgid "Ctrl+]" +msgstr "" + +#: objects/ui_MainWindow.h:845 +msgid "Ctrl+[" +msgstr "" + +#: objects/ui_MainWindow.h:846 +#, fuzzy +msgid "View All" +msgstr "&Вид" + +#: objects/ui_MainWindow.h:847 +msgid "Convert Tabs to Spaces" +msgstr "" + +#: objects/ui_MainWindow.h:848 +#, fuzzy +msgid "Hide toolbars" +msgstr "Скрыть редактор" + +#: objects/ui_MainWindow.h:849 +msgid "Time:" +msgstr "Время:" + +#: objects/ui_MainWindow.h:850 +msgid "FPS:" +msgstr "Кадров в секунду:" + +#: objects/ui_MainWindow.h:851 +msgid "Steps:" +msgstr "Шагов:" + +#: objects/ui_MainWindow.h:852 +msgid "Dump Pictures" +msgstr "Сохранять кадры" + +#: objects/ui_MainWindow.h:853 +msgid "&File" +msgstr "&Файл" + +#: objects/ui_MainWindow.h:854 +#, fuzzy +msgid "Recent Files" +msgstr "Открыть файл" + +#: objects/ui_MainWindow.h:856 +msgid "Export" +msgstr "" + +#: objects/ui_MainWindow.h:857 +msgid "&Edit" +msgstr "&Правка" + +#: objects/ui_MainWindow.h:858 +msgid "&Design" +msgstr "&Модель" + +#: objects/ui_MainWindow.h:859 +msgid "&View" +msgstr "&Вид" + +#: objects/ui_MainWindow.h:860 +msgid "&Help" +msgstr "&Справка" + +#: objects/ui_MainWindow.h:863 +msgid "Find" +msgstr "" + +#: objects/ui_MainWindow.h:864 objects/ui_MainWindow.h:871 +msgid "Replace" +msgstr "" + +#: objects/ui_MainWindow.h:866 +msgid "Search string" +msgstr "" + +#: objects/ui_MainWindow.h:867 +msgid "<" +msgstr "" + +#: objects/ui_MainWindow.h:868 +msgid ">" +msgstr "" + +#: objects/ui_MainWindow.h:869 +msgid "Done" +msgstr "" + +#: objects/ui_MainWindow.h:870 +#, fuzzy +msgid "Replacement string" +msgstr "элементах" + +#: objects/ui_MainWindow.h:872 +msgid "All" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:86 +msgid "OpenGL Warning" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:87 +msgid "" +"\n" +"\n" +"

" +msgstr "" + +#: objects/ui_OpenCSGWarningDialog.h:92 +#, fuzzy +msgid "Enable OpenCSG" +msgstr "Включить для OpenGL 1.x" + +#: objects/ui_OpenCSGWarningDialog.h:93 +msgid "Show this message again" +msgstr "" + +#: objects/ui_Preferences.h:609 +msgid "3D View" +msgstr "3D Вид" + +#: objects/ui_Preferences.h:610 src/UIUtils.cc:85 +msgid "Advanced" +msgstr "Дополнительные" + +#: objects/ui_Preferences.h:611 src/mainwin.cc:2315 +msgid "Editor" +msgstr "Редактор" + +#: objects/ui_Preferences.h:612 +msgid "Update" +msgstr "Обновления" + +#: objects/ui_Preferences.h:613 objects/ui_Preferences.h:633 +msgid "Features" +msgstr "Функции" + +#: objects/ui_Preferences.h:615 +msgid "Enable/Disable experimental features" +msgstr "" + +#: objects/ui_Preferences.h:617 +msgid "Color scheme:" +msgstr "Цветовая схема:" + +#: objects/ui_Preferences.h:618 +#, fuzzy +msgid "Editor Type" +msgstr "Редактор" + +#: objects/ui_Preferences.h:621 +#, fuzzy +msgid "Simple Editor" +msgstr "Скрыть редактор" + +#: objects/ui_Preferences.h:622 +msgid "QScintilla Editor" +msgstr "" + +#: objects/ui_Preferences.h:624 +msgid "(requires restart)" +msgstr "" + +#: objects/ui_Preferences.h:625 +msgid "Font" +msgstr "Шрифт" + +#: objects/ui_Preferences.h:626 +msgid "Color syntax highlighting" +msgstr "Подсветка синтаксиса" + +#: objects/ui_Preferences.h:627 +msgid "Use Ctrl/Cmd-Mouse-wheel to zoom text" +msgstr "" + +#: objects/ui_Preferences.h:629 +msgid "Automatically check for updates" +msgstr "Автоматически проверять обновления" + +#: objects/ui_Preferences.h:630 +msgid "Include development snapshots" +msgstr "Включая рабочие сборки" + +#: objects/ui_Preferences.h:631 +msgid "Check Now" +msgstr "Проверить сейчас" + +#: objects/ui_Preferences.h:632 +msgid "Last checked: " +msgstr "Последняя проверка: " + +#: objects/ui_Preferences.h:634 +msgid "OpenCSG" +msgstr "" + +#: objects/ui_Preferences.h:635 +msgid "Show capability warning" +msgstr "Показывать предупреждение о возможностях" + +#: objects/ui_Preferences.h:636 +msgid "Enable for OpenGL 1.x" +msgstr "Включить для OpenGL 1.x" + +#: objects/ui_Preferences.h:637 +msgid "Turn off rendering at " +msgstr "Отключать отрисовку на " + +#: objects/ui_Preferences.h:638 +msgid "elements" +msgstr "элементах" + +#: objects/ui_Preferences.h:639 +msgid "Force Goldfeather" +msgstr "Принудительно использовать алгоритм Goldfeather («Золотое перо»)" + +#: objects/ui_Preferences.h:640 +msgid "CGAL Cache size" +msgstr "Размер кэша CGAL" + +#: objects/ui_Preferences.h:641 objects/ui_Preferences.h:643 +msgid "bytes" +msgstr "байт" + +#: objects/ui_Preferences.h:642 +msgid "PolySet Cache size" +msgstr "Размер кэша PolySet" + +#: objects/ui_Preferences.h:644 +msgid "Allow to open multiple documents" +msgstr "" + +#: objects/ui_Preferences.h:645 +msgid "Enable docking of Editor and Console in different places" +msgstr "" + +#: objects/ui_Preferences.h:646 +msgid "Enable undocking of Editor and Console to separate windows" +msgstr "" + +#: objects/ui_Preferences.h:647 +msgid "Show Welcome Screen" +msgstr "" + +#: objects/ui_Preferences.h:648 +msgid "Enable user interface localization (requires restart of OpenSCAD)" +msgstr "" + +#: objects/ui_Preferences.h:649 +msgid "toolBar" +msgstr "" + +#: objects/ui_ProgressWidget.h:72 +msgid "Form" +msgstr "" + +#: objects/ui_ProgressWidget.h:73 +msgid "%v / %m" +msgstr "" + +#: src/mainwin.cc:768 src/mainwin.cc:1300 +msgid "Untitled.scad" +msgstr "Безымянный.scad" + +#: src/mainwin.cc:1299 +msgid "Save File" +msgstr "Сохранить файл" + +#: src/mainwin.cc:1301 +msgid "OpenSCAD Designs (*.scad)" +msgstr "Модели OpenSCAD (*.scad)" + +#: src/mainwin.cc:1311 +msgid "" +"%1 already exists.\n" +"Do you want to replace it?" +msgstr "" + +#: src/mainwin.cc:1630 +msgid "Application" +msgstr "" + +#: src/mainwin.cc:1631 +msgid "" +"The document has been modified.\n" +"Do you really want to reload the file?" +msgstr "" + +#: src/mainwin.cc:1942 src/mainwin.cc:1999 +#, fuzzy +msgid "Export %1 File" +msgstr "Экспортировать в DXF..." + +#: src/mainwin.cc:1943 src/mainwin.cc:2003 +msgid "%1 Files (*%2)" +msgstr "" + +#: src/mainwin.cc:1944 +#, fuzzy +msgid "Untitled" +msgstr "Безымянный.scad" + +#: src/mainwin.cc:2001 +#, fuzzy +msgid "Untitled%1" +msgstr "Безымянный.scad" + +#: src/mainwin.cc:2052 +msgid "Export CSG File" +msgstr "" + +#: src/mainwin.cc:2053 +msgid "Untitled.csg" +msgstr "" + +#: src/mainwin.cc:2054 +msgid "CSG Files (*.csg)" +msgstr "" + +#: src/mainwin.cc:2080 +msgid "Export Image" +msgstr "" + +#: src/mainwin.cc:2080 +msgid "PNG Files (*.png)" +msgstr "" + +#: src/mainwin.cc:2320 +msgid "Console" +msgstr "" + +#: src/mainwin.cc:2447 +#, fuzzy +msgid "The document has been modified." +msgstr "" +"Документ был изменен.\n" +"Сохранить изменения?" + +#: src/mainwin.cc:2448 +#, fuzzy +msgid "Do you want to save your changes?" +msgstr "" +"Документ был изменен.\n" +"Сохранить изменения?" + +#: src/QGLView.cc:114 +msgid "" +"\n" +"Using QGLWidget\n" +"\n" +msgstr "" + +#: src/QGLView.cc:131 +msgid "" +"Warning: You may experience OpenCSG rendering errors.\n" +"\n" +msgstr "" + +#: src/QGLView.cc:134 +msgid "" +"Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been " +"disabled.\n" +"\n" +msgstr "" + +#: src/QGLView.cc:137 +msgid "" +"It is highly recommended to use OpenSCAD on a system with OpenGL 2.0 or " +"later.\n" +"Your renderer information is as follows:\n" +msgstr "" + +#: src/QGLView.cc:141 +#, c-format +msgid "" +"GLEW version %s\n" +"%s (%s)\n" +"OpenGL version %s\n" +msgstr "" + +#: src/QGLView.cc:171 +#, c-format +msgid "" +"Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], " +"distance = %.2f" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Basics" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Shapes" +msgstr "" + +#: src/UIUtils.cc:85 +msgid "Extrusion" +msgstr "" + +#~ msgid "Check for Update.." +#~ msgstr "Проверить обновления..." + +#~ msgid "Off" +#~ msgstr "Выключить" + +#~ msgid "For Light Background" +#~ msgstr "Для светлого фона" + +#~ msgid "For Dark Background" +#~ msgstr "Для тёмного фона" + +#~ msgid "OpenSCAD Designs (*.scad *.csg)" +#~ msgstr "Модели OpenSCAD (*.scad *.csg)" + +#~ msgid "&Compile" +#~ msgstr "&Компилировать" + +#~ msgid "Compile and &Render (CGAL)" +#~ msgstr "Компилировать и &отрисовать (CGAL)" + +#~ msgid "CGAL Grid Only" +#~ msgstr "Только сетка CGAL" diff --git a/openscad.pro b/openscad.pro index d3482166..247632bf 100644 --- a/openscad.pro +++ b/openscad.pro @@ -8,7 +8,15 @@ # OPENCSGDIR # OPENSCAD_LIBRARIES # -# Please see the 'Building' sections of the OpenSCAD user manual +# qmake Variables to define the installation: +# +# PREFIX defines the base installation folder +# +# SUFFIX defines an optional suffix for the binary and the +# resource folder. E.g. using SUFFIX=-nightly will name the +# resulting binary openscad-nightly. +# +# Please see the 'Building' sections of the OpenSCAD user manual # for updated tips & workarounds. # # http://en.wikibooks.org/wiki/OpenSCAD_User_Manual @@ -74,8 +82,10 @@ macx { TARGET = OpenSCAD } else { - TARGET = openscad + TARGET = openscad$${SUFFIX} } +FULLNAME = openscad$${SUFFIX} +!isEmpty(SUFFIX): DEFINES += INSTALL_SUFFIX="\"\\\"$${SUFFIX}\\\"\"" macx { ICON = icons/OpenSCAD.icns @@ -89,11 +99,18 @@ macx { win* { RC_FILE = openscad_win32.rc - QTPLUGIN += qtaccessiblewidgets + QMAKE_CXXFLAGS += -DNOGDI +} + +mingw* { + # needed to prevent compilation error on MSYS2: + # as.exe: objects/cgalutils.o: too many sections (76541) + # using -Wa,-mbig-obj did not help + debug: QMAKE_CXXFLAGS += -O1 } CONFIG += qt -QT += opengl +QT += opengl concurrent # see http://fedoraproject.org/wiki/UnderstandingDSOLinkChange # and https://github.com/openscad/openscad/pull/119 @@ -140,6 +157,8 @@ netbsd* { QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + # gettext + QMAKE_CXXFLAGS_WARN_ON += -Wno-format-security # might want to actually turn this on once in a while QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare } @@ -160,6 +179,7 @@ CONFIG += glib-2.0 CONFIG += harfbuzz CONFIG += freetype CONFIG += fontconfig +CONFIG += gettext #Uncomment the following line to enable the QScintilla editor CONFIG += scintilla @@ -190,7 +210,12 @@ win* { RESOURCES = openscad.qrc -FORMS += src/MainWindow.ui \ +# Qt5 removed access to the QMAKE_UIC variable, the following +# way works for both Qt4 and Qt5 +load(uic) +uic.commands += -tr _ + +FORMS += src/MainWindow.ui \ src/Preferences.ui \ src/OpenCSGWarningDialog.ui \ src/AboutDialog.ui \ @@ -204,6 +229,7 @@ HEADERS += src/typedefs.h \ src/ProgressWidget.h \ src/parsersettings.h \ src/renderer.h \ + src/settings.h \ src/rendersettings.h \ src/colormap.h \ src/ThrownTogetherRenderer.h \ @@ -216,6 +242,7 @@ HEADERS += src/typedefs.h \ src/OpenCSGWarningDialog.h \ src/AboutDialog.h \ src/FontListDialog.h \ + src/FontListTableView.h \ src/builtin.h \ src/calc.h \ src/context.h \ @@ -227,7 +254,9 @@ HEADERS += src/typedefs.h \ src/dxfdim.h \ src/export.h \ src/expression.h \ + src/stackcheck.h \ src/function.h \ + src/exceptions.h \ src/grid.h \ src/highlighter.h \ src/localscope.h \ @@ -303,6 +332,7 @@ SOURCES += src/version_check.cc \ src/handle_dep.cc \ src/value.cc \ src/expr.cc \ + src/stackcheck.cc \ src/func.cc \ src/localscope.cc \ src/module.cc \ @@ -352,6 +382,7 @@ SOURCES += src/version_check.cc \ src/FreetypeRenderer.cc \ src/FontCache.cc \ \ + src/settings.cc \ src/rendersettings.cc \ src/highlighter.cc \ src/Preferences.cc \ @@ -382,6 +413,7 @@ SOURCES += src/version_check.cc \ src/UIUtils.cc \ src/Dock.cc \ src/FontListDialog.cc \ + src/FontListTableView.cc \ src/launchingscreen.cc \ src/legacyeditor.cc \ src/LibraryInfoDialog.cc @@ -422,6 +454,8 @@ HEADERS += src/cgal.h \ SOURCES += src/cgalutils.cc \ src/cgalutils-tess.cc \ + src/cgalutils-polyhedron.cc \ + src/cgalutils-tess-old.cc \ src/CGALCache.cc \ src/CGALRenderer.cc \ src/CGAL_Nef_polyhedron.cc \ @@ -450,42 +484,60 @@ isEmpty(PREFIX):PREFIX = /usr/local target.path = $$PREFIX/bin/ INSTALLS += target -examples.path = $$PREFIX/share/openscad/examples/ +# Run translation update scripts as last step after linking the target +QMAKE_POST_LINK += $$PWD/scripts/translation-make.sh + +# Create install targets for the languages defined in LINGUAS +LINGUAS = $$cat(locale/LINGUAS) +LOCALE_PREFIX = "$$PREFIX/share/$${FULLNAME}/locale" +for(language, LINGUAS) { + catalogdir = locale/$$language/LC_MESSAGES + exists(locale/$${language}.po) { + # Use .extra and copy manually as the source path might not exist, + # e.g. on a clean checkout. In that case qmake would not create + # the needed targets in the generated Makefile. + translation_path = translation_$${language}.path + translation_extra = translation_$${language}.extra + translation_depends = translation_$${language}.depends + $$translation_path = $$LOCALE_PREFIX/$$language/LC_MESSAGES/ + $$translation_extra = cp -f $${catalogdir}/openscad.mo \"\$(INSTALL_ROOT)$$LOCALE_PREFIX/$$language/LC_MESSAGES/openscad.mo\" + $$translation_depends = locale/$${language}.po + INSTALLS += translation_$$language + } +} + +examples.path = "$$PREFIX/share/$${FULLNAME}/examples/" examples.files = examples/* INSTALLS += examples -libraries.path = $$PREFIX/share/openscad/libraries/ +libraries.path = "$$PREFIX/share/$${FULLNAME}/libraries/" libraries.files = libraries/* INSTALLS += libraries -fonts.path = $$PREFIX/share/openscad/fonts/ +fonts.path = "$$PREFIX/share/$${FULLNAME}/fonts/" fonts.files = fonts/* INSTALLS += fonts -colorschemes.path = $$PREFIX/share/openscad/color-schemes/ +colorschemes.path = "$$PREFIX/share/$${FULLNAME}/color-schemes/" colorschemes.files = color-schemes/* INSTALLS += colorschemes applications.path = $$PREFIX/share/applications -applications.files = icons/openscad.desktop +applications.extra = cat icons/openscad.desktop | sed -e \"'s/^Icon=openscad/Icon=$${FULLNAME}/; s/^Exec=openscad/Exec=$${FULLNAME}/'\" > \"\$(INSTALL_ROOT)$${applications.path}/$${FULLNAME}.desktop\" INSTALLS += applications mimexml.path = $$PREFIX/share/mime/packages -mimexml.files = icons/openscad.xml +mimexml.extra = cp -f icons/openscad.xml \"\$(INSTALL_ROOT)$${mimexml.path}/$${FULLNAME}.xml\" INSTALLS += mimexml appdata.path = $$PREFIX/share/appdata -appdata.files = openscad.appdata.xml +appdata.extra = cp -f openscad.appdata.xml \"\$(INSTALL_ROOT)$${appdata.path}/$${FULLNAME}.appdata.xml\" INSTALLS += appdata icons.path = $$PREFIX/share/pixmaps -icons.files = icons/openscad.png +icons.extra = cp -f icons/openscad.png \"\$(INSTALL_ROOT)$${icons.path}/$${FULLNAME}.png\" INSTALLS += icons man.path = $$PREFIX/share/man/man1 -man.files = doc/openscad.1 +man.extra = cp -f doc/openscad.1 \"\$(INSTALL_ROOT)$${man.path}/$${FULLNAME}.1\" INSTALLS += man - -CONFIG(winconsole) { - include(winconsole.pri) -} diff --git a/openscad.pro.user b/openscad.pro.user deleted file mode 100644 index bc4fd482..00000000 --- a/openscad.pro.user +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - true - 1 - true - 0 - true - 0 - 8 - true - 1 - true - true - true - false - - - - ProjectExplorer.Project.PluginSettings - - - - ProjectExplorer.Project.Target.0 - - Qt 4.8.6 (qt4) - Qt 4.8.6 (qt4) - {90222843-28c9-4a66-ac82-99bd31ae7263} - 0 - 0 - 0 - - - - - true - qmake - - QtProjectManager.QMakeBuildStep - false - false - CONFIG+=experimental - false - - - true - Make - - Qt4ProjectManager.MakeStep - - -w - -r - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - -w - -r - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Release - - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - 1 - - - 0 - Deploy - - ProjectExplorer.BuildSteps.Deploy - - 1 - Deploy locally - - ProjectExplorer.DefaultDeployConfiguration - - 1 - - - - false - false - false - false - true - 0.01 - 10 - true - 1 - 25 - - 1 - true - false - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - 2 - - openscad - - Qt4ProjectManager.Qt4RunConfiguration:/home/shaina/openscad/openscad.pro - - openscad.pro - false - false - - 3768 - true - false - false - false - true - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.EnvironmentId - {56f57d1a-fe9b-42b2-a96b-3ac76cf7565f} - - - ProjectExplorer.Project.Updater.FileVersion - 15 - - diff --git a/qscintilla2.prf b/qscintilla2.prf index 5fd22307..5daa57e0 100644 --- a/qscintilla2.prf +++ b/qscintilla2.prf @@ -10,9 +10,33 @@ INCLUDEPATH += $$[QT_INSTALL_HEADERS] LIBS += -L$$[QT_INSTALL_LIBS] -greaterThan(QT_MAJOR_VERSION, 4) { - win32|macx:LIBS += -lqscintilla2 - else:LIBS += -lqt5scintilla2 +CONFIG(debug, debug|release) { + mac: { + #LIBS += -lqscintilla2_debug + LIBS += -lqscintilla2 + } else { + win32: { + LIBS += -lqscintilla2d + } else { + greaterThan(QT_MAJOR_VERSION, 4) { + LIBS += -lqt5scintilla2 + } else { + LIBS += -lqscintilla2 + } + } + } } else { - LIBS += -lqscintilla2 + mac: { + LIBS += -lqscintilla2 + } else { + win32: { + LIBS += -lqscintilla2 + } else { + greaterThan(QT_MAJOR_VERSION, 4) { + LIBS += -lqt5scintilla2 + } else { + LIBS += -lqscintilla2 + } + } + } } diff --git a/scripts/generate-potfiles.sh b/scripts/generate-potfiles.sh new file mode 100755 index 00000000..2298aba9 --- /dev/null +++ b/scripts/generate-potfiles.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Generate list of files for translation. The output is saved to po/POTFILES +# which is needed for the xgettext call. + +for ui in src/*.ui +do + UI="${ui#src/}" + UI="${UI%.ui}" + for d in mingw64 mingw32 . + do + if [ -f "$d/objects/ui_$UI.h" ] + then + echo "$d/objects/ui_$UI.h" + fi + done +done + +for src in src/*.h src/*.cc +do + echo $src +done diff --git a/scripts/installer.nsi b/scripts/installer.nsi index d1fb7712..7b482f01 100644 --- a/scripts/installer.nsi +++ b/scripts/installer.nsi @@ -13,6 +13,7 @@ File openscad.com File /r /x mingw-cross-env examples File /r /x mingw-cross-env libraries File /r /x mingw-cross-env fonts +File /r /x mingw-cross-env locale File /r /x mingw-cross-env color-schemes ${registerExtension} "$INSTDIR\openscad.exe" ".scad" "OpenSCAD_File" CreateShortCut $SMPROGRAMS\OpenSCAD.lnk $INSTDIR\openscad.exe @@ -31,6 +32,7 @@ RMDir /r $INSTDIR\fonts RMDir /r $INSTDIR\color-schemes RMDir /r $INSTDIR\examples RMDir /r $INSTDIR\libraries\mcad +RMDir /r $INSTDIR\locale Delete $INSTDIR\libraries\boxes.scad Delete $INSTDIR\libraries\shapes.scad RMDir $INSTDIR\libraries diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index e91123cc..109b95a5 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -325,10 +325,10 @@ build_boost() fi if $USING_LLVM; then BOOST_TOOLSET="toolset=darwin-llvm" - echo "using darwin : llvm : llvm-g++ ;" >> tools/build/v2/user-config.jam + echo "using darwin : llvm : llvm-g++ ;" >> tools/build/user-config.jam elif $USING_CLANG; then BOOST_TOOLSET="toolset=clang" - echo "using clang ;" >> tools/build/v2/user-config.jam + echo "using clang ;" >> tools/build/user-config.jam fi ./b2 -j"$NUMCPU" -d+2 $BOOST_TOOLSET cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS -headerpad_max_install_names" install install_name_tool -id $DEPLOYDIR/lib/libboost_thread.dylib $DEPLOYDIR/lib/libboost_thread.dylib @@ -356,8 +356,9 @@ build_cgal() cd $BASEDIR/src rm -rf CGAL-$version if [ ! -f CGAL-$version.tar.gz ]; then - # 4.5 - curl -O https://gforge.inria.fr/frs/download.php/file/34149/CGAL-$version.tar.gz + # 4.5.1 + curl -O https://gforge.inria.fr/frs/download.php/file/34400/CGAL-$version.tar.gz + # 4.5 curl -O https://gforge.inria.fr/frs/download.php/file/34149/CGAL-$version.tar.gz # 4.4 curl -O https://gforge.inria.fr/frs/download.php/file/33525/CGAL-$version.tar.gz # 4.3 curl -O https://gforge.inria.fr/frs/download.php/32994/CGAL-$version.tar.gz # 4.2 curl -O https://gforge.inria.fr/frs/download.php/32359/CGAL-$version.tar.gz @@ -450,6 +451,8 @@ build_eigen() elif [ $version = "3.1.4" ]; then EIGENDIR=eigen-eigen-36bf2ceaf8f5; elif [ $version = "3.2.0" ]; then EIGENDIR=eigen-eigen-ffa86ffb5570; elif [ $version = "3.2.1" ]; then EIGENDIR=eigen-eigen-6b38706d90a9; + elif [ $version = "3.2.2" ]; then EIGENDIR=eigen-eigen-1306d75b4a21; + elif [ $version = "3.2.3" ]; then EIGENDIR=eigen-eigen-36fd1ba04c12; fi if [ $EIGENDIR = "none" ]; then @@ -515,7 +518,7 @@ build_freetype() cd "$BASEDIR"/src rm -rf "freetype-$version" if [ ! -f "freetype-$version.tar.gz" ]; then - curl --insecure -LO "http://download.savannah.gnu.org/releases/freetype/freetype-$version.tar.gz" + curl --insecure -LO "http://downloads.sourceforge.net/project/freetype/freetype2/$version/freetype-$version.tar.gz" fi tar xzf "freetype-$version.tar.gz" cd "freetype-$version" @@ -541,7 +544,7 @@ build_libxml2() fi tar xzf "libxml2-$version.tar.gz" cd "libxml2-$version" - ./configure --prefix="$DEPLOYDIR" --without-ftp --without-http --without-python CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN LDFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN + ./configure --prefix="$DEPLOYDIR" --with-zlib=/usr -without-lzma --without-ftp --without-http --without-python CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN LDFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN make -j$NUMCPU make install } @@ -655,7 +658,7 @@ build_ragel() cd "$BASEDIR"/src rm -rf "ragel-$version" if [ ! -f "ragel-$version.tar.gz" ]; then - curl --insecure -LO "http://www.colm.net/wp-content/uploads/2014/10/ragel-$version.tar.gz" + curl --insecure -LO "http://www.colm.net/files/ragel/ragel-$version.tar.gz" fi tar xzf "ragel-$version.tar.gz" cd "ragel-$version" @@ -686,7 +689,7 @@ build_harfbuzz() # disable doc directories as they make problems on Mac OS Build sed -e "s/SUBDIRS = src util test docs/SUBDIRS = src util test/g" Makefile.am > Makefile.am.bak && mv Makefile.am.bak Makefile.am sed -e "s/^docs.*$//" configure.ac > configure.ac.bak && mv configure.ac.bak configure.ac - PKG_CONFIG_LIBDIR="$DEPLOYDIR/lib/pkgconfig" ./autogen.sh --prefix="$DEPLOYDIR" --with-freetype=yes --with-gobject=no --with-cairo=no --with-icu=no CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN LDFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN $extra_config_flags + PKG_CONFIG_LIBDIR="$DEPLOYDIR/lib/pkgconfig" ./autogen.sh --prefix="$DEPLOYDIR" --with-freetype=yes --with-gobject=no --with-cairo=no --with-icu=no CFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN CXXFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN LDFLAGS=-mmacosx-version-min=$MAC_OSX_VERSION_MIN $extra_config_flags make -j$NUMCPU make install } @@ -770,26 +773,26 @@ fi echo "Using basedir:" $BASEDIR mkdir -p $SRCDIR $DEPLOYDIR -build_qt5 5.3.1 +build_qt5 5.4.0 build_qscintilla 2.8.4 # NB! For eigen, also update the path in the function -build_eigen 3.2.1 +build_eigen 3.2.3 build_gmp 5.1.3 build_mpfr 3.1.2 -build_boost 1.54.0 +build_boost 1.57.0 # NB! For CGAL, also update the actual download URL in the function -build_cgal 4.5 -build_glew 1.10.0 -build_gettext 0.18.3.2 -build_libffi 3.1 -build_glib2 2.40.0 +build_cgal 4.5.1 +build_glew 1.11.0 +build_gettext 0.19.4 +build_libffi 3.2.1 +build_glib2 2.42.1 build_opencsg 1.4.0 -build_freetype 2.5.3 --without-png +build_freetype 2.5.4 --without-png build_ragel 6.9 -build_harfbuzz 0.9.35 "--with-coretext=auto --with-glib=no" +build_harfbuzz 0.9.37 "--with-coretext=auto --with-glib=no" export FREETYPE_CFLAGS="-I$DEPLOYDIR/include -I$DEPLOYDIR/include/freetype2" export FREETYPE_LIBS="-L$DEPLOYDIR/lib -lfreetype" -build_libxml2 2.9.1 +build_libxml2 2.9.2 build_fontconfig 2.11.1 if $OPTION_DEPLOY; then # build_sparkle andymatuschak 0ed83cf9f2eeb425d4fdd141c01a29d843970c20 diff --git a/scripts/release-common.sh b/scripts/release-common.sh index f3ab9f81..2da42495 100755 --- a/scripts/release-common.sh +++ b/scripts/release-common.sh @@ -254,8 +254,8 @@ case $OS in echo "cant find $TARGET/openscad.exe. build failed. stopping." exit fi - # make console pipe-able openscad.com - see winconsole.pri for info - qmake CONFIG+=winconsole ../openscad.pro + # make console pipe-able openscad.com - see winconsole.pro for info + qmake ../winconsole.pro make if [ ! -e $TARGET/openscad.com ]; then echo "cant find $TARGET/openscad.com. build failed. stopping." @@ -280,7 +280,6 @@ if [[ $? != 0 ]]; then exit 1 fi - echo "Building test suite..." if [ $BUILD_TESTS ]; then @@ -326,6 +325,7 @@ case $OS in EXAMPLESDIR=OpenSCAD.app/Contents/Resources/examples LIBRARYDIR=OpenSCAD.app/Contents/Resources/libraries FONTDIR=OpenSCAD.app/Contents/Resources/fonts + TRANSLATIONDIR=OpenSCAD.app/Contents/Resources/locale COLORSCHEMESDIR=OpenSCAD.app/Contents/Resources/color-schemes ;; UNIX_CROSS_WIN) @@ -333,6 +333,7 @@ case $OS in EXAMPLESDIR=$DEPLOYDIR/openscad-$VERSION/examples/ LIBRARYDIR=$DEPLOYDIR/openscad-$VERSION/libraries/ FONTDIR=$DEPLOYDIR/openscad-$VERSION/fonts/ + TRANSLATIONDIR=$DEPLOYDIR/openscad-$VERSION/locale/ COLORSCHEMESDIR=$DEPLOYDIR/openscad-$VERSION/color-schemes/ rm -rf $DEPLOYDIR/openscad-$VERSION mkdir $DEPLOYDIR/openscad-$VERSION @@ -341,6 +342,7 @@ case $OS in EXAMPLESDIR=openscad-$VERSION/examples/ LIBRARYDIR=openscad-$VERSION/libraries/ FONTDIR=openscad-$VERSION/fonts/ + TRANSLATIONDIR=openscad-$VERSION/locale/ COLORSCHEMESDIR=openscad-$VERSION/color-schemes/ rm -rf openscad-$VERSION mkdir openscad-$VERSION @@ -386,6 +388,13 @@ if [ -n $LIBRARYDIR ]; then rm -f libraries.tar chmod -R u=rwx,go=r,+X $LIBRARYDIR/* fi +if [ -n $TRANSLATIONDIR ]; then + echo $TRANSLATIONDIR + mkdir -p $TRANSLATIONDIR + cd locale && tar cvf $OPENSCADDIR/translations.tar */*/*.mo && cd $OPENSCADDIR + cd $TRANSLATIONDIR && tar xvf $OPENSCADDIR/translations.tar && cd $OPENSCADDIR + rm -f translations.tar +fi echo "Creating archive.." diff --git a/scripts/translation-make.sh b/scripts/translation-make.sh new file mode 100755 index 00000000..5fadf01d --- /dev/null +++ b/scripts/translation-make.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# Script for use from qmake to generate the translation +# related files. +# + +SCRIPTDIR="`dirname \"$0\"`" +TOPDIR="`dirname \"$SCRIPTDIR\"`" + +cd "$TOPDIR" || exit 1 + +echo "Compiling language files..." +./scripts/translation-update.sh updatemo diff --git a/scripts/translation-update.sh b/scripts/translation-update.sh new file mode 100755 index 00000000..11a30bc4 --- /dev/null +++ b/scripts/translation-update.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +# see doc/translation.txt for more info + +updatepot() +{ + # check we have all files from POTFILES present + while read f + do + if [ ! -f "$f" ]; then + echo "cannot find file '$f' from POTFILES" + exit 1 + fi + done < locale/POTFILES + + grep ui_MainWindow.h locale/POTFILES >/dev/null 2>/dev/null + if [ $? -ne 0 ] ; then + echo "cannot find .../ui_xxxxx.h files. perhaps if you run make...?" + exit 1 + fi + + VER=`date +"%Y.%m.%d"` + OPTS= + OPTS=$OPTS' --package-name=OpenSCAD' + OPTS=$OPTS' --package-version='$VER + OPTS=$OPTS' --default-domain=openscad' + OPTS=$OPTS' --keyword=_' + OPTS=$OPTS' --keyword=N_' + OPTS=$OPTS' --files-from=./locale/POTFILES' + cmd="${GETTEXT_PATH}xgettext "$OPTS' -o ./locale/openscad.pot' + echo $cmd + $cmd + if [ ! $? = 0 ]; then + echo error running xgettext + exit 1 + fi + sed -e s/"CHARSET"/"UTF-8"/g ./locale/openscad.pot > ./locale/openscad.pot.new && mv ./locale/openscad.pot.new ./locale/openscad.pot +} + +updatepo() +{ + for LANGCODE in `cat ./locale/LINGUAS | grep -v "#"`; do + OPTS='--update --backup=t' + cmd="$GETTEXT_PATH"'msgmerge '$OPTS' ./locale/'$LANGCODE'.po ./locale/openscad.pot' + echo $cmd + $cmd + if [ ! $? = 0 ]; then + echo error running msgmerge + exit 1 + fi + done +} + +updatemo() +{ + for LANGCODE in `cat locale/LINGUAS | grep -v "#"`; do + mkdir -p ./locale/$LANGCODE/LC_MESSAGES + OPTS='-c -v' + cmd="$GETTEXT_PATH"'msgfmt '$OPTS' -o ./locale/'$LANGCODE'/LC_MESSAGES/openscad.mo ./locale/'$LANGCODE'.po' + echo $cmd + $cmd + if [ ! $? = 0 ]; then + echo error running msgfmt + exit 1 + fi + done +} + +GETTEXT_PATH="" +#if [ "x$OPENSCAD_LIBRARIES" != x ]; then +# GETTEXT_PATH="$OPENSCAD_LIBRARIES/bin/" +#fi + +SCRIPTDIR="`dirname \"$0\"`" +TOPDIR="`dirname \"$SCRIPTDIR\"`" + +cd "$TOPDIR" || exit 1 + +if [ "x$1" = xupdatemo ]; then + updatemo +else + echo "Generating POTFILES..." + ./scripts/generate-potfiles.sh > locale/POTFILES + updatepot && updatepo && updatemo +fi + diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh index 1f7cbc22..cc904ad3 100755 --- a/scripts/travis-ci.sh +++ b/scripts/travis-ci.sh @@ -13,7 +13,7 @@ if [[ $? != 0 ]]; then fi # Exclude tests known the cause issues on Travis # opencsgtest_rotate_extrude-tests - Fails on Ubuntu 12.04 using Gallium 0.4 drivers -ctest -j8 -E "opencsgtest_rotate_extrude-tests|opencsgtest_render-tests|opencsgtest_rotate_extrude-hole|opencsgtest_internal-cavity|opencsgtest_internal-cavity-polyhedron|opencsgtest_minkowski3-erosion" +ctest -j8 -E "opencsgtest_rotate_extrude-tests|opencsgtest_render-tests|opencsgtest_rotate_extrude-hole|opencsgtest_internal-cavity|opencsgtest_internal-cavity-polyhedron|opencsgtest_minkowski3-erosion|opencsgtest_issue835|opencsgtest_issue911|opencsgtest_issue913" if [[ $? != 0 ]]; then echo "Test failure" exit 1 diff --git a/src/AboutDialog.h b/src/AboutDialog.h index 73c52a5e..aca97e70 100644 --- a/src/AboutDialog.h +++ b/src/AboutDialog.h @@ -1,22 +1,23 @@ #pragma once +#include "openscad.h" +#include "qtgettext.h" #include "ui_AboutDialog.h" -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - class AboutDialog : public QDialog, public Ui::AboutDialog { Q_OBJECT; public: AboutDialog(QWidget *) { setupUi(this); - this->setWindowTitle( QString("About OpenSCAD ") + QString(TOSTRING( OPENSCAD_VERSION)) ); - this->aboutText->setOpenExternalLinks(true); + this->setWindowTitle( QString(_("About OpenSCAD")) + " " + openscad_versionnumber.c_str()); QUrl flattr_qurl(":icons/flattr.png" ); this->aboutText->loadResource( QTextDocument::ImageResource, flattr_qurl ); QString tmp = this->aboutText->toHtml(); - tmp.replace("__VERSION__",QString(TOSTRING(OPENSCAD_VERSION))); + tmp.replace("__VERSION__", openscad_versionnumber.c_str()); this->aboutText->setHtml(tmp); } + +public slots: + void on_okPushButton_clicked() { accept(); } }; diff --git a/src/AboutDialog.html b/src/AboutDialog.html index d138eecb..22ca9272 100644 --- a/src/AboutDialog.html +++ b/src/AboutDialog.html @@ -19,7 +19,7 @@

-OpenSCAD version __VERSION__ +OpenSCAD version __VERSION__

@@ -149,9 +149,9 @@ benhowes, 5263, Craig Trader, Miro Hrončok, Tony Theodore ... and many others

-
  • Github -
  • Rock Linux -
  • Thingiverse +
  • Source code repos and website hosted on Github +
  • Downloads hosted by Joshua Holbrook +
  • CI hosted on Travis-CI

    diff --git a/src/AboutDialog.ui b/src/AboutDialog.ui index 587d27e2..0a3bedf9 100644 --- a/src/AboutDialog.ui +++ b/src/AboutDialog.ui @@ -6,16 +6,98 @@ 0 0 - 567 - 359 + 628 + 419 + + + 0 + 0 + + About OpenSCAD - - + + + 16 + + + 6 + + + + + 0 + + + 6 + + + + + + 96 + 96 + + + + + 96 + 96 + + + + + + + :/icons/openscad.png + + + true + + + + + + + <html><head/><body> +<p style="font-family: 'Open Sans', 'Droid Sans', 'sans-serif'; font-weight: bold; padding-bottom: 0; margin-bottom: 0; font-size: 22pt;"><span style="color: green;">Open</span>SCAD</p> +<p style="font-family: 'Open Sans', 'Droid Sans', 'sans-serif'; font-weight: normal; font-size: 14pt; padding-top: 0; margin-top: 0; margin-left: 2em;">The Programmers Solid 3D CAD Modeller</p> +</body></html> + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 2 + + + + + + + + + + 400 + 200 + + true @@ -24,8 +106,31 @@ qrc:/src/AboutDialog.html + + true + + + + + OK + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + diff --git a/src/AppleEvents.cc b/src/AppleEvents.cc index 92585de6..f1e7d0da 100644 --- a/src/AppleEvents.cc +++ b/src/AppleEvents.cc @@ -1,4 +1,6 @@ -#include +#include +#include +#include #include #include "MainWindow.h" diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc index dfe16536..d49521e0 100644 --- a/src/CGAL_Nef_polyhedron.cc +++ b/src/CGAL_Nef_polyhedron.cc @@ -48,6 +48,11 @@ size_t CGAL_Nef_polyhedron::memsize() const return memsize; } +bool CGAL_Nef_polyhedron::isEmpty() const +{ + return !this->p3 || this->p3->is_empty(); +} + /*! Creates a new PolySet and initializes it with the data from this polyhedron diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h index f3585469..2d7b37d6 100644 --- a/src/CGAL_Nef_polyhedron.h +++ b/src/CGAL_Nef_polyhedron.h @@ -19,7 +19,7 @@ public: virtual std::string dump() const; virtual unsigned int getDimension() const { return 3; } // Empty means it is a geometric node which has zero area/volume - virtual bool isEmpty() const { return !p3; } + virtual bool isEmpty() const; virtual Geometry *copy() const { return new CGAL_Nef_polyhedron(*this); } void reset() { p3.reset(); } diff --git a/src/CGAL_Nef_polyhedron_DxfData.cc b/src/CGAL_Nef_polyhedron_DxfData.cc index 5ca39e8e..cbb5ce77 100644 --- a/src/CGAL_Nef_polyhedron_DxfData.cc +++ b/src/CGAL_Nef_polyhedron_DxfData.cc @@ -46,7 +46,7 @@ void CGAL_Nef_polyhedron::transform( const Transform3d &matrix ) { if (!this->isEmpty()) { if (matrix.matrix().determinant() == 0) { - PRINT("Warning: Scaling a 3D object with 0 - removing object"); + PRINT("WARNING: Scaling a 3D object with 0 - removing object"); this->reset(); } else { diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc index eef880f8..4adfa62d 100644 --- a/src/CSGTermEvaluator.cc +++ b/src/CSGTermEvaluator.cc @@ -95,7 +95,13 @@ static shared_ptr evaluate_csg_term_from_geometry(const State &state, { std::stringstream stream; stream << node.name() << node.index(); - shared_ptr t(new CSGTerm(geom, state.matrix(), state.color(), stream.str())); + + // We cannot render Polygon2d directly, so we preprocess (tessellate) it here + shared_ptr g = geom; + shared_ptr p2d = dynamic_pointer_cast(geom); + if (p2d) g.reset(p2d->tessellate()); + + shared_ptr t(new CSGTerm(g, state.matrix(), state.color(), stream.str())); if (modinst->isHighlight()) { t->flag = CSGTerm::FLAG_HIGHLIGHT; highlights.push_back(t); diff --git a/src/Camera.cc b/src/Camera.cc index 29ecc013..7e53d363 100644 --- a/src/Camera.cc +++ b/src/Camera.cc @@ -3,7 +3,7 @@ #include "printutils.h" Camera::Camera(enum CameraType camtype) : - type(camtype), projection(Camera::PERSPECTIVE), fov(45), height(60), viewall(false) + type(camtype), projection(Camera::PERSPECTIVE), fov(45), viewall(false), height(60) { PRINTD("Camera()"); if (this->type == Camera::GIMBAL) { @@ -101,25 +101,34 @@ void Camera::viewAll(const BoundingBox &bbox, float scalefactor) void Camera::zoom(int delta) { - if (this->projection == PERSPECTIVE) { - this->viewer_distance *= pow(0.9, delta / 120.0); - } - else { - this->height *= pow(0.9, delta / 120.0); - } + this->viewer_distance *= pow(0.9, delta / 120.0); + this->height = this->viewer_distance; } void Camera::setProjection(ProjectionType type) { - if (this->projection != type) { - switch (type) { - case PERSPECTIVE: - this->viewer_distance = this->height; - break; - case ORTHOGONAL: - this->height = this->viewer_distance; - break; - } - this->projection = type; - } + this->projection = type; +} + +void Camera::resetView() +{ + type = Camera::GIMBAL; + object_rot << 35, 0, -25; + object_trans << 0, 0, 0; + height = 140; + viewer_distance = 140; +} + +double Camera::zoomValue() +{ + return this->projection == PERSPECTIVE ? viewer_distance : height; +} + +std::string Camera::statusText() +{ + boost::format fmt(_("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f")); + fmt % object_trans.x() % object_trans.y() % object_trans.z() + % object_rot.x() % object_rot.y() % object_rot.z() + % (this->projection == PERSPECTIVE ? viewer_distance : height); + return fmt.str(); } diff --git a/src/Camera.h b/src/Camera.h index 0979ccc6..ae9ffaac 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -30,7 +30,10 @@ public: void gimbalDefaultTranslate(); void setProjection(ProjectionType type); void zoom(int delta); + double zoomValue(); + void resetView(); void viewAll(const BoundingBox &bbox, float scalefactor = 1.0f); + std::string statusText(); // Vectorcam Eigen::Vector3d eye; @@ -40,14 +43,10 @@ public: // Gimbalcam Eigen::Vector3d object_trans; Eigen::Vector3d object_rot; - double viewer_distance; // Perspective settings double fov; // Field of view - // Orthographic settings - double height; // world-space height of viewport - // true if camera should try to view everything in a given // bounding box. bool viewall; @@ -58,4 +57,10 @@ public: unsigned int pixel_width; unsigned int pixel_height; + +protected: + // Perspective settings + double viewer_distance; + // Orthographic settings + double height; // world-space height of viewport }; diff --git a/src/CsgInfo.h b/src/CsgInfo.h index c50ab4d4..8a3512c8 100644 --- a/src/CsgInfo.h +++ b/src/CsgInfo.h @@ -12,13 +12,8 @@ class CsgInfo { public: - CsgInfo() + CsgInfo() : glview(NULL), root_chain(NULL), highlights_chain(NULL), background_chain(NULL), progress_function(NULL) { - root_chain = NULL; - highlights_chain = NULL; - background_chain = NULL; - glview = NULL; - progress_function = NULL; normalizelimit = RenderSettings::inst()->openCSGTermLimit; } OffscreenView *glview; @@ -43,12 +38,6 @@ public: CSGTermEvaluator evaluator(tree, &geomevaluator); boost::shared_ptr root_raw_term = evaluator.evaluateCSGTerm( *root_node, this->highlight_terms, this->background_terms ); - if (!root_raw_term && this->background_terms.empty()) { - PRINT("Error: CSG generation failed! (no objects found)"); - call_progress_function(); - return false; - } - PRINT("Compiling design (CSG Products normalization)..."); call_progress_function(); CSGTermNormalizer normalizer( normalizelimit ); diff --git a/src/FontCache.cc b/src/FontCache.cc index 8f7beee5..c353fe6f 100644 --- a/src/FontCache.cc +++ b/src/FontCache.cc @@ -30,6 +30,7 @@ #include #include +#include "boosty.h" #include "FontCache.h" #include "PlatformUtils.h" #include "parsersettings.h" @@ -80,8 +81,20 @@ const std::string &FontInfo::get_file() const } FontCache * FontCache::self = NULL; +FontCache::InitHandlerFunc *FontCache::cb_handler = FontCache::defaultInitHandler; +void *FontCache::cb_userdata = NULL; const std::string FontCache::DEFAULT_FONT("XXX"); +/** + * Default implementation for the font cache initialization. In case no other + * handler is registered, the cache build is just called synchronously in the + * current thread by this handler. + */ +void FontCache::defaultInitHandler(FontCacheInitializer *initializer, void *) +{ + initializer->run(); +} + FontCache::FontCache() { this->init_ok = false; @@ -89,7 +102,7 @@ FontCache::FontCache() // If we've got a bundled fonts.conf, initialize fontconfig with our own config // by overriding the built-in fontconfig path. // For system installs and dev environments, we leave this alone - fs::path fontdir(fs::path(PlatformUtils::resourcesPath()) / "fonts"); + fs::path fontdir(PlatformUtils::resourcePath("fonts")); if (fs::is_regular_file(fontdir / "fonts.conf")) { PlatformUtils::setenv("FONTCONFIG_PATH", boosty::stringy(boosty::absolute(fontdir)).c_str(), 0); } @@ -97,12 +110,12 @@ FontCache::FontCache() // Just load the configs. We'll build the fonts once all configs are loaded this->config = FcInitLoadConfig(); if (!this->config) { - PRINT("Can't initialize fontconfig library, text() objects will not be rendered"); + PRINT("WARNING: Can't initialize fontconfig library, text() objects will not be rendered"); return; } // Add the built-in fonts & config - fs::path builtinfontpath = fs::path(PlatformUtils::resourcesPath()) / "fonts"; + fs::path builtinfontpath(PlatformUtils::resourcePath("fonts")); if (fs::is_directory(builtinfontpath)) { FcConfigParseAndLoad(this->config, reinterpret_cast(boosty::stringy(builtinfontpath).c_str()), false); add_font_dir(boosty::stringy(boosty::canonical(builtinfontpath))); @@ -130,8 +143,8 @@ FontCache::FontCache() } } - // FIXME: Caching happens here. This would be a good place to notify the user - FcConfigBuildFonts(this->config); + FontCacheInitializer initializer(this->config); + cb_handler(&initializer, cb_userdata); // For use by LibraryInfo FcStrList *dirs = FcConfigGetFontDirs(this->config); @@ -142,7 +155,7 @@ FontCache::FontCache() const FT_Error error = FT_Init_FreeType(&this->library); if (error) { - PRINT("Can't initialize freetype library, text() objects will not be rendered"); + PRINT("WARNING: Can't initialize freetype library, text() objects will not be rendered"); return; } @@ -161,6 +174,12 @@ FontCache * FontCache::instance() return self; } +void FontCache::registerProgressHandler(InitHandlerFunc *handler, void *userdata) +{ + FontCache::cb_handler = handler; + FontCache::cb_userdata = userdata; +} + void FontCache::register_font_file(const std::string &path) { if (!FcConfigAppFontAddFile(this->config, reinterpret_cast (path.c_str()))) { diff --git a/src/FontCache.h b/src/FontCache.h index 2633c04d..ea74d147 100644 --- a/src/FontCache.h +++ b/src/FontCache.h @@ -59,6 +59,19 @@ private: typedef std::vector FontInfoList; +/** + * Slow call of the font cache initialization. This is separated here so it + * can be passed to the GUI to run in a separate thread while showing a + * progress dialog. + */ +class FontCacheInitializer { +public: + FontCacheInitializer(FcConfig *config) : config(config) { } + void run() { FcConfigBuildFonts(config); } +private: + FcConfig *config; +}; + class FontCache { public: const static std::string DEFAULT_FONT; @@ -75,12 +88,20 @@ public: FontInfoList *list_fonts() const; static FontCache *instance(); + + typedef void (InitHandlerFunc)(FontCacheInitializer *initializer, void *userdata); + static void registerProgressHandler(InitHandlerFunc *handler, void *userdata = NULL); + private: typedef std::pair cache_entry_t; typedef std::map cache_t; static FontCache *self; - + static InitHandlerFunc *cb_handler; + static void *cb_userdata; + + static void defaultInitHandler(FontCacheInitializer *delegate, void *userdata); + bool init_ok; cache_t cache; FcConfig *config; diff --git a/src/FontListDialog.cc b/src/FontListDialog.cc index bfa3c8e5..474c2ef3 100644 --- a/src/FontListDialog.cc +++ b/src/FontListDialog.cc @@ -27,6 +27,7 @@ #include #include +#include "qtgettext.h" #include "FontListDialog.h" #include "FontCache.h" @@ -59,14 +60,16 @@ void FontListDialog::selection_changed(const QItemSelection ¤t, const QIte { if (current.count() == 0) { copyButton->setEnabled(false); + tableView->setDragText(""); return; } const QModelIndex &idx = proxy->mapToSource(current.indexes().at(0)); const QString name = model->item(idx.row(), 0)->text(); const QString style = model->item(idx.row(), 1)->text(); - selection = QString("\"%1:style=%2\"").arg(name).arg(style); + selection = QString("\"%1:style=%2\"").arg(quote(name)).arg(quote(style)); copyButton->setEnabled(true); + tableView->setDragText(selection); } void FontListDialog::update_font_list() @@ -116,3 +119,28 @@ void FontListDialog::update_font_list() delete list; } + +/** + * Quote a string according to the requirements of font-config. + * See http://www.freedesktop.org/software/fontconfig/fontconfig-user.html + * + * The '\', '-', ':' and ',' characters in family names must be preceded + * by a '\' character to avoid having them misinterpreted. Similarly, values + * containing '\', '=', '_', ':' and ',' must also have them preceded by a + * '\' character. The '\' characters are stripped out of the family name and + * values as the font name is read. + * + * @param text unquoted string + * @return quoted text + */ +QString FontListDialog::quote(const QString& text) +{ + QString result = text; + result.replace('\\', "\\\\") + .replace('-', "\\-") + .replace(':', "\\:") + .replace(',', "\\,") + .replace('=', "\\=") + .replace('_', "\\_"); + return result; +} diff --git a/src/FontListDialog.h b/src/FontListDialog.h index 10010995..d1927631 100644 --- a/src/FontListDialog.h +++ b/src/FontListDialog.h @@ -3,11 +3,9 @@ #include #include +#include "qtgettext.h" #include "ui_FontListDialog.h" -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - class FontListDialog : public QDialog, public Ui::FontListDialog { Q_OBJECT; @@ -26,6 +24,8 @@ signals: void font_selected(const QString font); private: + QString quote(const QString& text); + QString selection; QStandardItemModel *model; QSortFilterProxyModel *proxy; diff --git a/src/FontListDialog.ui b/src/FontListDialog.ui index 7313f3b6..70ff4fff 100644 --- a/src/FontListDialog.ui +++ b/src/FontListDialog.ui @@ -35,9 +35,6 @@ - - Paste font selector to Editor Window - Copy to Clipboard @@ -70,7 +67,17 @@ - + + + true + + + QAbstractItemView::DragOnly + + + QAbstractItemView::SelectRows + + @@ -103,6 +110,13 @@ + + + FontListTableView + QTableView +

    FontListTableView.h
    + + diff --git a/src/FontListTableView.cc b/src/FontListTableView.cc new file mode 100644 index 00000000..b6162a24 --- /dev/null +++ b/src/FontListTableView.cc @@ -0,0 +1,67 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf and + * Marius Kintel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include + +#include "qtgettext.h" +#include "FontListDialog.h" + +FontListTableView::FontListTableView(QWidget *parent) : QTableView(parent) +{ +} + +void FontListTableView::setDragText(const QString &text) +{ + this->text = text.trimmed(); +} + +void FontListTableView::startDrag(Qt::DropActions supportedActions) +{ + if (text.isEmpty()) { + return; + } + + QMimeData *mimeData = new QMimeData; + mimeData->setText(text); + + QFontMetrics fm(font()); + QRect rect(0, 0, fm.width(text) + 8, fm.height() + 8); + QPixmap pixmap(rect.width(), rect.height()); + pixmap.fill(QColor(240, 240, 240, 160)); + + QPainter painter(&pixmap); + painter.setFont(font()); + painter.drawText(rect, Qt::AlignCenter, text); + painter.drawRect(0, 0, rect.width() - 1, rect.height() - 1); + + QDrag *drag = new QDrag(this); + drag->setPixmap(pixmap); + drag->setMimeData(mimeData); + drag->setHotSpot(QPoint(-10, rect.height() + 6)); + drag->exec(supportedActions, Qt::CopyAction); +} diff --git a/src/FontListTableView.h b/src/FontListTableView.h new file mode 100644 index 00000000..70348c46 --- /dev/null +++ b/src/FontListTableView.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +class FontListTableView : public QTableView +{ + Q_OBJECT; + +public: + FontListTableView(QWidget *parent = NULL); + void setDragText(const QString &text); + +protected: + void startDrag(Qt::DropActions supportedActions); + +private: + QString text; +}; diff --git a/src/GLView.cc b/src/GLView.cc index 7ca5a480..e76a10c3 100644 --- a/src/GLView.cc +++ b/src/GLView.cc @@ -91,21 +91,23 @@ void GLView::setupCamera() glLoadIdentity(); switch (this->cam.type) { - case Camera::GIMBAL: + case Camera::GIMBAL: { + double eyeY = 0.0; switch (this->cam.projection) { case Camera::PERSPECTIVE: { - double dist = cam.viewer_distance; - gluPerspective(cam.fov, aspectratio, 0.1*dist, 100*dist); + eyeY = cam.zoomValue(); + gluPerspective(cam.fov, aspectratio, 0.1 * eyeY, 100 * eyeY); break; } case Camera::ORTHOGONAL: { - glOrtho(-cam.height/2*aspectratio, cam.height*aspectratio/2, - -cam.height/2, cam.height/2, + eyeY = cam.zoomValue(); + glOrtho(-eyeY/2*aspectratio, eyeY*aspectratio/2, + -eyeY/2, eyeY/2, -far_far_away, +far_far_away); break; } } - gluLookAt(0.0, -cam.viewer_distance, 0.0, + gluLookAt(0.0, -eyeY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); @@ -113,8 +115,8 @@ void GLView::setupCamera() glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0); glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0); glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0); - glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z() ); break; + } case Camera::VECTOR: { switch (this->cam.projection) { case Camera::PERSPECTIVE: { @@ -123,8 +125,9 @@ void GLView::setupCamera() break; } case Camera::ORTHOGONAL: { - glOrtho(-cam.height/2*aspectratio, cam.height*aspectratio/2, - -cam.height/2, cam.height/2, + double height = cam.zoomValue(); + glOrtho(-height/2*aspectratio, height*aspectratio/2, + -height/2, height/2, -far_far_away, +far_far_away); break; } @@ -150,18 +153,24 @@ void GLView::setupCamera() void GLView::paintGL() { - glEnable(GL_LIGHTING); - - setupCamera(); + glDisable(GL_LIGHTING); Color4f bgcol = ColorMap::getColor(*this->colorscheme, BACKGROUND_COLOR); + Color4f bgcontrast = ColorMap::getContrastColor(bgcol); glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - // Only for GIMBAL cam - if (showcrosshairs) GLView::showCrosshairs(); - if (showaxes) GLView::showAxes(); + setupCamera(); + if (this->cam.type) { + // Only for GIMBAL cam + // The crosshair should be fixed at the center of the viewport... + if (showcrosshairs) GLView::showCrosshairs(); + glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z()); + // ...the axis lines need to follow the object translation. + if (showaxes) GLView::showAxes(bgcontrast); + } + glEnable(GL_LIGHTING); glDepthFunc(GL_LESS); glCullFace(GL_BACK); glDisable(GL_CULL_FACE); @@ -174,10 +183,11 @@ void GLView::paintGL() OpenCSG::setContext(this->opencsg_id); #endif this->renderer->draw(showfaces, showedges); - } + } // Only for GIMBAL - if (showaxes) GLView::showSmallaxes(); + glDisable(GL_LIGHTING); + if (showaxes) GLView::showSmallaxes(bgcontrast); } #ifdef ENABLE_OPENCSG @@ -358,7 +368,7 @@ void GLView::initializeGL() #endif } -void GLView::showSmallaxes() +void GLView::showSmallaxes(const Color4f &col) { // Fixme - this doesnt work in Vector Camera mode @@ -423,14 +433,9 @@ void GLView::showSmallaxes() glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - // FIXME: This was an attempt to keep contrast with background, but is suboptimal - // (e.g. nearly invisible against a gray background). - // int r,g,b; - // r=g=b=0; - // bgcol.getRgb(&r, &g, &b); - // glColor3f((255.0f-r)/255.0f, (255.0f-g)/255.0f, (255.0f-b)/255.0f); - float d = 3*dpi; - glColor3f(0.0f, 0.0f, 0.0f); + glColor3f(col[0], col[1], col[2]); + + float d = 3*dpi; glBegin(GL_LINES); // X Label glVertex3d(xlabel_x-d, xlabel_y-d, 0); glVertex3d(xlabel_x+d, xlabel_y+d, 0); @@ -447,36 +452,48 @@ void GLView::showSmallaxes() glEnd(); } -void GLView::showAxes() +void GLView::showAxes(const Color4f &col) { + double l = cam.zoomValue(); + // FIXME: doesn't work under Vector Camera // Large gray axis cross inline with the model - // FIXME: This is always gray - adjust color to keep contrast with background glLineWidth(this->getDPI()); - glColor3d(0.5, 0.5, 0.5); + glColor3f(col[0], col[1], col[2]); + glBegin(GL_LINES); - double l = cam.projection == Camera::PERSPECTIVE ? cam.viewer_distance : cam.height; - glVertex3d(-l, 0, 0); + glVertex3d(0, 0, 0); glVertex3d(+l, 0, 0); - glVertex3d(0, -l, 0); + glVertex3d(0, 0, 0); glVertex3d(0, +l, 0); - glVertex3d(0, 0, -l); + glVertex3d(0, 0, 0); glVertex3d(0, 0, +l); glEnd(); + + glPushAttrib(GL_LINE_BIT); + glEnable(GL_LINE_STIPPLE); + glLineStipple(3, 0xAAAA); + glBegin(GL_LINES); + glVertex3d(0, 0, 0); + glVertex3d(-l, 0, 0); + glVertex3d(0, 0, 0); + glVertex3d(0, -l, 0); + glVertex3d(0, 0, 0); + glVertex3d(0, 0, -l); + glEnd(); + glPopAttrib(); } void GLView::showCrosshairs() { // FIXME: this might not work with Vector camera - // FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them - // to change color based on view orientation. - glLineWidth(3); + glLineWidth(this->getDPI()); Color4f col = ColorMap::getColor(*this->colorscheme, CROSSHAIR_COLOR); glColor3f(col[0], col[1], col[2]); glBegin(GL_LINES); for (double xf = -1; xf <= +1; xf += 2) for (double yf = -1; yf <= +1; yf += 2) { - double vd = cam.viewer_distance/20; + double vd = cam.zoomValue()/8; glVertex3d(-xf*vd, -yf*vd, -vd); glVertex3d(+xf*vd, +yf*vd, +vd); } diff --git a/src/GLView.h b/src/GLView.h index f3f98ae2..5e3e4c53 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -42,10 +42,6 @@ public: void setCamera(const Camera &cam); void setupCamera(); - void showCrosshairs(); - void showAxes(); - void showSmallaxes(); - void setColorScheme(const ColorScheme &cs); void setColorScheme(const std::string &cs); void updateColorScheme(); @@ -76,4 +72,8 @@ public: bool opencsg_support; int opencsg_id; #endif +private: + void showCrosshairs(); + void showAxes(const Color4f &col); + void showSmallaxes(const Color4f &col); }; diff --git a/src/GeometryEvaluator.cc b/src/GeometryEvaluator.cc index 5e82897f..72490314 100644 --- a/src/GeometryEvaluator.cc +++ b/src/GeometryEvaluator.cc @@ -63,10 +63,12 @@ shared_ptr GeometryEvaluator::evaluateGeometry(const AbstractNod PolySet *ps = new PolySet(3); ps->setConvexity(N->getConvexity()); this->root.reset(ps); - bool err = CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *ps); - if (err) { - PRINT("ERROR: Nef->PolySet failed"); - } + if (!N->isEmpty()) { + bool err = CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *ps); + if (err) { + PRINT("ERROR: Nef->PolySet failed"); + } + } smartCacheInsert(node, this->root); } @@ -88,8 +90,12 @@ GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren(const Abstrac } } } - if (dim == 2) return ResultObject(applyToChildren2D(node, op)); - else if (dim == 3) return applyToChildren3D(node, op); + if (dim == 2) { + Polygon2d *p2d = applyToChildren2D(node, op); + assert(p2d); + return ResultObject(p2d); + } + else if (dim == 3) return applyToChildren3D(node, op); return ResultObject(); } @@ -119,16 +125,23 @@ GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren3D(const Abstr if (op == OPENSCAD_MINKOWSKI) return ResultObject(CGALUtils::applyMinkowski(children)); - CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron; - CGALUtils::applyOperator(children, *N, op); + CGAL_Nef_polyhedron *N = CGALUtils::applyOperator(children, op); + // FIXME: Clarify when we can return NULL and what that means + if (!N) N = new CGAL_Nef_polyhedron; return ResultObject(N); } + +/*! + Apply 2D hull. + + May return an empty geometry but will not return NULL. +*/ Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node) { std::vector children = collectChildren2D(node); - Polygon2d *geometry = NULL; + Polygon2d *geometry = new Polygon2d(); typedef CGAL::Point_2 > CGALPoint2; // Collect point cloud @@ -150,7 +163,6 @@ Polygon2d *GeometryEvaluator::applyHull2D(const AbstractNode &node) BOOST_FOREACH(const CGALPoint2 &p, result) { outline.vertices.push_back(Vector2d(p[0], p[1])); } - geometry = new Polygon2d(); geometry->addOutline(outline); } return geometry; @@ -271,12 +283,12 @@ Geometry::ChildList GeometryEvaluator::collectChildren3D(const AbstractNode &nod smartCacheInsert(*chnode, chgeom); if (chgeom) { - if (chgeom->isEmpty() || chgeom->getDimension() == 3) { - children.push_back(item); - } - else { + if (chgeom->getDimension() == 2) { PRINT("WARNING: Ignoring 2D child object for 3D operation"); } + else if (chgeom->isEmpty() || chgeom->getDimension() == 3) { + children.push_back(item); + } } } return children; @@ -345,6 +357,7 @@ void GeometryEvaluator::addToParent(const State &state, // Root node, insert into cache smartCacheInsert(node, geom); this->root = geom; + assert(this->visitedchildren.empty()); } } @@ -378,7 +391,7 @@ Response GeometryEvaluator::visit(State &state, const OffsetNode &node) const Polygon2d *polygon = dynamic_cast(geometry); // ClipperLib documentation: The formula for the number of steps in a full // circular arc is ... Pi / acos(1 - arc_tolerance / abs(delta)) - double n = Calc::get_fragments_from_r(10, node.fn, node.fs, node.fa); + double n = Calc::get_fragments_from_r(std::abs(node.delta), node.fn, node.fs, node.fa); double arc_tolerance = std::abs(node.delta) * (1 - cos(M_PI / n)); const Polygon2d *result = ClipperUtils::applyOffset(*polygon, node.delta, node.join_type, node.miter_limit, arc_tolerance); assert(result); @@ -443,6 +456,7 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node) shared_ptr geom; if (!isSmartCached(node)) { const Geometry *geometry = node.createGeometry(); + assert(geometry); if (const Polygon2d *polygon = dynamic_cast(geometry)) { if (!polygon->isSanitized()) { Polygon2d *p = ClipperUtils::sanitize(*polygon); @@ -516,7 +530,7 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node) if (!isSmartCached(node)) { if (matrix_contains_infinity(node.matrix) || matrix_contains_nan(node.matrix)) { // due to the way parse/eval works we can't currently distinguish between NaN and Inf - PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object."); + PRINT("WARNING: Transformation matrix contains Not-a-Number and/or Infinity - removing object."); } else { // First union all children @@ -537,7 +551,12 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node) node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,0), node.matrix(3,1), node.matrix(3,3); newpoly->transform(mat2); - geom = newpoly; + // A 2D transformation may flip the winding order of a polygon. + // If that happens with a sanitized polygon, we need to reverse + // the winding order for it to be correct. + if (newpoly->isSanitized() && mat2.matrix().determinant() <= 0) { + geom.reset(ClipperUtils::sanitize(*newpoly)); + } } else if (geom->getDimension() == 3) { shared_ptr ps = dynamic_pointer_cast(geom); @@ -573,7 +592,7 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node) static void translate_PolySet(PolySet &ps, const Vector3d &translation) { - BOOST_FOREACH(PolySet::Polygon &p, ps.polygons) { + BOOST_FOREACH(Polygon &p, ps.polygons) { BOOST_FOREACH(Vector3d &v, p) { v += translation; } @@ -651,7 +670,7 @@ static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d & PolySet *ps_bottom = poly.tessellate(); // bottom // Flip vertex ordering for bottom polygon - BOOST_FOREACH(PolySet::Polygon &p, ps_bottom->polygons) { + BOOST_FOREACH(Polygon &p, ps_bottom->polygons) { std::reverse(p.begin(), p.end()); } translate_PolySet(*ps_bottom, Vector3d(0,0,h1)); @@ -934,9 +953,10 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) } if (!Nptr->isEmpty()) { Polygon2d *poly = CGALUtils::project(*Nptr, node.cut_mode); - assert(poly); - poly->setConvexity(node.convexity); - geom.reset(poly); + if (poly) { + poly->setConvexity(node.convexity); + geom.reset(poly); + } } } } diff --git a/src/LibraryInfo.cc b/src/LibraryInfo.cc index f736cd22..52facbb1 100644 --- a/src/LibraryInfo.cc +++ b/src/LibraryInfo.cc @@ -29,12 +29,20 @@ std::string LibraryInfo::info() { std::stringstream s; +#if defined(__x86_64__) || defined(_M_X64) + std::string bits(" 64bit"); +#elif defined(__i386) || defined(_M_IX86) + std::string bits(" 32bit"); +#else + std::string bits(""); +#endif + #if defined(__GNUG__) && !defined(__clang__) - std::string compiler_info( "GCC " + std::string(TOSTRING(__VERSION__)) ); + std::string compiler_info( "GCC " + std::string(TOSTRING(__VERSION__)) + bits); #elif defined(_MSC_VER) - std::string compiler_info( "MSVC " + std::string(TOSTRING(_MSC_FULL_VER)) ); + std::string compiler_info( "MSVC " + std::string(TOSTRING(_MSC_FULL_VER)) + bits); #elif defined(__clang__) - std::string compiler_info( "Clang " + std::string(TOSTRING(__clang_version__)) ); + std::string compiler_info( "Clang " + std::string(TOSTRING(__clang_version__)) + bits); #else std::string compiler_info( "unknown compiler" ); #endif @@ -80,6 +88,7 @@ std::string LibraryInfo::info() const char *env_font_path = getenv("OPENSCAD_FONT_PATH"); s << "OpenSCAD Version: " << TOSTRING(OPENSCAD_VERSION) + << "\nSystem information: " << PlatformUtils::sysinfo() << "\nCompiler, build date: " << compiler_info << ", " << __DATE__ << "\nBoost version: " << BOOST_LIB_VERSION << "\nEigen version: " << EIGEN_WORLD_VERSION << "." << EIGEN_MAJOR_VERSION << "." << EIGEN_MINOR_VERSION @@ -93,7 +102,7 @@ std::string LibraryInfo::info() << "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION << "\nApplication Path: " << PlatformUtils::applicationPath() << "\nDocuments Path: " << PlatformUtils::documentsPath() - << "\nResource Path: " << PlatformUtils::resourcesPath() + << "\nResource Path: " << PlatformUtils::resourceBasePath() << "\nUser Library Path: " << PlatformUtils::userLibraryPath() << "\nUser Config Path: " << PlatformUtils::userConfigPath() << "\nBackup Path: " << PlatformUtils::backupPath() diff --git a/src/LibraryInfoDialog.h b/src/LibraryInfoDialog.h index b0465ca6..a47e0879 100644 --- a/src/LibraryInfoDialog.h +++ b/src/LibraryInfoDialog.h @@ -2,6 +2,8 @@ #include #include + +#include "qtgettext.h" #include "ui_LibraryInfoDialog.h" class LibraryInfoDialog : public QDialog, public Ui::LibraryInfoDialog diff --git a/src/MainWindow.h b/src/MainWindow.h index 9b5833bf..50d4bb6d 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -1,5 +1,6 @@ #pragma once +#include "qtgettext.h" #include #include #include "ui_MainWindow.h" @@ -13,6 +14,7 @@ #include #include #include +#include enum export_type_e { EXPORT_TYPE_UNKNOWN, @@ -39,6 +41,8 @@ public: std::string autoReloadId; QTimer *waitAfterReloadTimer; + QTime renderingTime; + ModuleContext top_ctx; FileModule *root_module; // Result of parsing ModuleInstantiation root_inst; // Top level instance @@ -67,6 +71,7 @@ public: QAction *actionRecentFile[UIUtils::maxRecentFiles]; QMap knownFileExtensions; + QLabel *versionLabel; QWidget *editorDockTitleWidget; QWidget *consoleDockTitleWidget; @@ -112,6 +117,7 @@ private: void show_examples(); void setDockWidgetTitle(QDockWidget *dockWidget, QString prefix, bool topLevel); void addKeyboardShortCut(const QList &actions); + void updateStatusBar(class ProgressWidget *progressWidget); EditorInterface *editor; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index c883a92a..74365644 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -179,7 +179,7 @@ - Open Recent + Recent Files @@ -543,6 +543,9 @@ Ctrl+Q + + QAction::QuitRole + @@ -955,6 +958,9 @@ About + + QAction::AboutRole + @@ -983,6 +989,9 @@ Preferences + + QAction::PreferencesRole + diff --git a/src/NULLGL.cc b/src/NULLGL.cc index 496810c2..65d6e5e7 100644 --- a/src/NULLGL.cc +++ b/src/NULLGL.cc @@ -4,17 +4,13 @@ GLView::GLView() {} void GLView::setRenderer(Renderer* r) {} void GLView::initializeGL() {} void GLView::resizeGL(int w, int h) {} -void GLView::setupGimbalCamPerspective() {} -void GLView::setupGimbalCamOrtho(double distance, bool offset) {} -void GLView::setupVectorCamPerspective() {} -void GLView::setupVectorCamOrtho(bool offset) {} -void GLView::setCamera( Camera &cam ) {} +void GLView::setCamera(const Camera &cam ) {assert(false && "not implemented");} void GLView::paintGL() {} -void GLView::vectorCamPaintGL() {} -void GLView::gimbalCamPaintGL() {} -void GLView::showSmallaxes() {} -void GLView::showAxes() {} +void GLView::showSmallaxes(const Color4f &col) {} +void GLView::showAxes(const Color4f &col) {} void GLView::showCrosshairs() {} +void GLView::setColorScheme(const ColorScheme &cs){assert(false && "not implemented");} +void GLView::setColorScheme(const std::string &cs) {assert(false && "not implemented");} #include "ThrownTogetherRenderer.h" @@ -23,12 +19,16 @@ ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain, void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const {} void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, bool background, bool showedges, bool fberror) const {} +BoundingBox ThrownTogetherRenderer::getBoundingBox() const {assert(false && "not implemented");} #include "CGALRenderer.h" CGALRenderer::CGALRenderer(shared_ptr geom) {} CGALRenderer::~CGALRenderer() {} void CGALRenderer::draw(bool showfaces, bool showedges) const {} +BoundingBox CGALRenderer::getBoundingBox() const {assert(false && "not implemented");} +void CGALRenderer::setColorScheme(const ColorScheme &cs){assert(false && "not implemented");} + #include "system-gl.h" diff --git a/src/OGL_helper.h b/src/OGL_helper.h index 488ee133..966657fc 100644 --- a/src/OGL_helper.h +++ b/src/OGL_helper.h @@ -44,6 +44,7 @@ const bool cull_backfaces = false; const bool color_backfaces = false; #ifdef _WIN32 +#include // For the CALLBACK macro #define CGAL_GLU_TESS_CALLBACK CALLBACK #else #define CGAL_GLU_TESS_CALLBACK diff --git a/src/OffscreenContextWGL.cc b/src/OffscreenContextWGL.cc index 6e657fe5..b9e0f0e8 100644 --- a/src/OffscreenContextWGL.cc +++ b/src/OffscreenContextWGL.cc @@ -11,6 +11,7 @@ For more info: http://blogs.msdn.com/b/oldnewthing/archive/2006/12/04/1205831.aspx by Tom */ +#undef NOGDI #include #include diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc index 3c6f0b48..aa2d6bbb 100644 --- a/src/OpenCSGRenderer.cc +++ b/src/OpenCSGRenderer.cc @@ -90,7 +90,12 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, const Color4f &c = j_obj.color; glPushMatrix(); glMultMatrixd(j_obj.matrix.data()); - csgmode_e csgmode = j_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL; + csgmode_e csgmode = csgmode_e( + (highlight ? + CSGMODE_HIGHLIGHT : + (background ? CSGMODE_BACKGROUND : CSGMODE_NORMAL)) | + (j_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : 0)); + ColorMode colormode = COLORMODE_NONE; if (background) { if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { @@ -99,11 +104,9 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, else { colormode = COLORMODE_BACKGROUND; } - csgmode = csgmode_e(csgmode + 10); } else if (j_obj.type == CSGTerm::TYPE_DIFFERENCE) { if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { colormode = COLORMODE_HIGHLIGHT; - csgmode = csgmode_e(csgmode + 20); } else { colormode = COLORMODE_CUTOUT; @@ -111,7 +114,6 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, } else { if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { colormode = COLORMODE_HIGHLIGHT; - csgmode = csgmode_e(csgmode + 20); } else { colormode = COLORMODE_MATERIAL; @@ -139,9 +141,12 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, prim->geom = i_obj.geom; prim->m = i_obj.matrix; - prim->csgmode = i_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL; - if (highlight) prim->csgmode = csgmode_e(prim->csgmode + 20); - else if (background) prim->csgmode = csgmode_e(prim->csgmode + 10); + prim->csgmode = csgmode_e( + (highlight ? + CSGMODE_HIGHLIGHT : + (background ? CSGMODE_BACKGROUND : CSGMODE_NORMAL)) | + (i_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : 0)); + primitives.push_back(prim); } } diff --git a/src/OpenCSGWarningDialog.h b/src/OpenCSGWarningDialog.h index 4cf9ec05..2ece9497 100644 --- a/src/OpenCSGWarningDialog.h +++ b/src/OpenCSGWarningDialog.h @@ -1,5 +1,6 @@ #pragma once +#include "qtgettext.h" #include "ui_OpenCSGWarningDialog.h" class OpenCSGWarningDialog : public QDialog, public Ui::OpenCSGWarningDialog diff --git a/src/PlatformUtils-mac.mm b/src/PlatformUtils-mac.mm index c6ba6277..6ddf252a 100644 --- a/src/PlatformUtils-mac.mm +++ b/src/PlatformUtils-mac.mm @@ -1,4 +1,8 @@ #include "PlatformUtils.h" +#include +#include +#include + #import std::string PlatformUtils::pathSeparatorChar() @@ -18,5 +22,49 @@ std::string PlatformUtils::userConfigPath() return std::string([[appSupportDir path] UTF8String]) + std::string("/") + PlatformUtils::OPENSCAD_FOLDER_NAME; } +unsigned long PlatformUtils::stackLimit() +{ + struct rlimit limit; + + int ret = getrlimit(RLIMIT_STACK, &limit); + if (ret == 0) { + if (limit.rlim_cur > STACK_BUFFER_SIZE) { + return limit.rlim_cur - STACK_BUFFER_SIZE; + } + if (limit.rlim_max > STACK_BUFFER_SIZE) { + return limit.rlim_max - STACK_BUFFER_SIZE; + } + } + + return STACK_LIMIT_DEFAULT; +} + +std::string PlatformUtils::sysinfo() +{ + std::string result; + + result += "Mac OS X "; + result += [[[NSProcessInfo processInfo] operatingSystemVersionString] UTF8String]; + + int64_t physical_memory; + int32_t numcpu; + size_t length64 = sizeof(int64_t); + size_t length32 = sizeof(int32_t);; + + sysctlbyname("hw.memsize", &physical_memory, &length64, NULL, 0); + sysctlbyname("hw.physicalcpu", &numcpu, &length32, NULL, 0); + + result += " "; + result += boost::lexical_cast(numcpu); + result += " CPU"; + if (numcpu > 1) result += "s"; + + result += " "; + result += PlatformUtils::toMemorySizeString(physical_memory, 2); + result += " RAM"; + + return result; +} + void PlatformUtils::ensureStdIO(void) {} diff --git a/src/PlatformUtils-posix.cc b/src/PlatformUtils-posix.cc index 64cc5dbe..c3767fa7 100644 --- a/src/PlatformUtils-posix.cc +++ b/src/PlatformUtils-posix.cc @@ -1,3 +1,14 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + #include "PlatformUtils.h" #include "boosty.h" @@ -41,5 +52,126 @@ std::string PlatformUtils::userConfigPath() return ""; } +unsigned long PlatformUtils::stackLimit() +{ + struct rlimit limit; + + int ret = getrlimit(RLIMIT_STACK, &limit); + if (ret == 0) { + if (limit.rlim_cur > STACK_BUFFER_SIZE) { + return limit.rlim_cur - STACK_BUFFER_SIZE; + } + if (limit.rlim_max > STACK_BUFFER_SIZE) { + return limit.rlim_max - STACK_BUFFER_SIZE; + } + } + + return STACK_LIMIT_DEFAULT; +} + +static std::string readText(const std::string &path) +{ + std::ifstream s(path.c_str()); + s.seekg(0, std::ios::end); + if (s.fail() || s.tellg() > 4096) { + return ""; + } + s.seekg(0, std::ios::beg); + + std::string text((std::istreambuf_iterator(s)), std::istreambuf_iterator()); + return text; +} + +/** + * Check /etc/os-release as defined by systemd. + * @see http://0pointer.de/blog/projects/os-release.html + * @see http://www.freedesktop.org/software/systemd/man/os-release.html + * @return the PRETTY_NAME from the os-release file or an empty string. + */ +static std::string checkOsRelease() +{ + std::string os_release(readText("/etc/os-release")); + + boost::smatch results; + boost::regex pretty_name("^PRETTY_NAME=\"([^\"]+)\""); + if (boost::regex_search(os_release, results, pretty_name)) { + return results[1]; + } + + return ""; +} + +static std::string checkEtcIssue() +{ + std::string issue(readText("/etc/issue")); + + boost::regex nl("\n.*$"); + issue = boost::regex_replace(issue, nl, ""); + boost::regex esc("\\\\."); + issue = boost::regex_replace(issue, esc, ""); + boost::algorithm::trim(issue); + + return issue; +} + +static std::string detectDistribution() +{ + std::string osrelease = checkOsRelease(); + if (!osrelease.empty()) { + return osrelease; + } + + std::string etcissue = checkEtcIssue(); + if (!etcissue.empty()) { + return etcissue; + } + + return ""; +} + +std::string PlatformUtils::sysinfo() +{ + std::string result; + + struct utsname osinfo; + if (uname(&osinfo) == 0) { + result += osinfo.sysname; + result += " "; + result += osinfo.release; + result += " "; + result += osinfo.version; + result += " "; + result += osinfo.machine; + } else { + result += "Unknown Linux"; + } + + long numcpu = sysconf(_SC_NPROCESSORS_ONLN); + if (numcpu > 0) { + result += " "; + result += boost::lexical_cast(numcpu); + result += " CPU"; + if (numcpu > 1) { + result += "s"; + } + } + + long pages = sysconf(_SC_PHYS_PAGES); + long pagesize = sysconf(_SC_PAGE_SIZE); + if ((pages > 0) && (pagesize > 0)) { + result += " "; + result += PlatformUtils::toMemorySizeString(pages * pagesize, 2); + result += " RAM"; + } + + std::string distribution = detectDistribution(); + if (!distribution.empty()) { + result += " "; + result += distribution; + } + + return result; +} + void PlatformUtils::ensureStdIO(void) {} diff --git a/src/PlatformUtils-win.cc b/src/PlatformUtils-win.cc index a504bb1f..2a54d8fd 100644 --- a/src/PlatformUtils-win.cc +++ b/src/PlatformUtils-win.cc @@ -3,6 +3,7 @@ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif +#undef NOGDI #include #ifndef _WIN32_IE #define _WIN32_IE 0x0501 // SHGFP_TYPE_CURRENT @@ -60,11 +61,12 @@ static const std::string getFolderPath(int nFolder) int result = SHGetFolderPathW( hwndOwner, nFolder, hToken, dwFlags, pszPath ); if (result == S_OK) { - path = std::wstring( path.c_str() ); // strip extra NULLs - //std::wcerr << "wchar path:" << "\n"; - const std::string retval = winapi_wstr_to_utf8( path ); - //PRINTB("Path found: %s",retval); - return retval; + path = std::wstring( path.c_str() ); // strip extra NULLs + // Use boost::filesystem to decide how to convert from wstring + // to string. Normally the path encoding is system local and + // we don't want to force conversion to UTF-8. + fs::path p(path); + return p.string(); } return ""; } @@ -92,6 +94,101 @@ std::string PlatformUtils::userConfigPath() return retval + std::string("/") + PlatformUtils::OPENSCAD_FOLDER_NAME; } +unsigned long PlatformUtils::stackLimit() +{ + return STACK_LIMIT_DEFAULT; +} + +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + +// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms684139%28v=vs.85%29.aspx +static BOOL IsWow64() +{ + BOOL bIsWow64 = FALSE; + + //IsWow64Process is not available on all supported versions of Windows. + //Use GetModuleHandle to get a handle to the DLL that contains the function + //and GetProcAddress to get a pointer to the function if available. + LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (NULL != fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) + { + return false; + } + } + return bIsWow64; +} + +std::string PlatformUtils::sysinfo() +{ + std::string result; + + OSVERSIONINFOEX osinfo; + osinfo.dwOSVersionInfoSize = sizeof(osinfo); + if (GetVersionEx((OSVERSIONINFO*)&osinfo) == 0) { + result += "Unknown Windows"; + } else { + unsigned int version = osinfo.dwMajorVersion * 1000 + osinfo.dwMinorVersion; + if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { + switch (version) { + case 5000: + result += "Windows 2000"; + break; + case 5001: + result += "Windows XP"; + break; + case 5002: + result += "Windows Server 2003"; + break; + case 6000: + result += (osinfo.wProductType == VER_NT_WORKSTATION ? "Windows Vista" : "Windows Server 2008"); + break; + case 6001: + result += (osinfo.wProductType == VER_NT_WORKSTATION ? "Windows 7" : "Windows Server 2008 R2"); + break; + case 6002: + result += (osinfo.wProductType == VER_NT_WORKSTATION ? "Windows 8" : "Windows Server 2012"); + break; + case 6003: + // For applications that have been manifested for Windows 8.1. + result += (osinfo.wProductType == VER_NT_WORKSTATION ? "Windows 8.1" : "Windows Server 2012 R2"); + break; + } + boost::format fmt(" (v%d.%d)"); + fmt % osinfo.dwMajorVersion % osinfo.dwMinorVersion; + result += fmt.str(); + } else { + boost::format fmt("Unknown Windows (dwPlatformId = %d, dwMajorVersion = %d, dwMinorVersion = %d"); + fmt % osinfo.dwPlatformId % osinfo.dwMajorVersion % osinfo.dwMinorVersion; + result += fmt.str(); + } + } + + SYSTEM_INFO systeminfo; + bool isWow64 = IsWow64(); + if (isWow64) { + GetNativeSystemInfo(&systeminfo); + } else { + GetSystemInfo(&systeminfo); + } + + int numcpu = systeminfo.dwNumberOfProcessors; + boost::format fmt(" %d CPU%s%s"); + fmt % numcpu % (numcpu > 1 ? "s" : "") % (isWow64 ? " WOW64" : ""); + result += fmt.str(); + + MEMORYSTATUSEX memoryinfo; + memoryinfo.dwLength = sizeof(memoryinfo); + if (GlobalMemoryStatusEx(&memoryinfo) != 0) { + result += " "; + result += PlatformUtils::toMemorySizeString(memoryinfo.ullTotalPhys, 2); + result += " RAM"; + } + + return result; +} + #include #include #include diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc index 91ec6149..e4d80225 100644 --- a/src/PlatformUtils.cc +++ b/src/PlatformUtils.cc @@ -1,10 +1,12 @@ #include +#include #include "PlatformUtils.h" -#include "boosty.h" -#include -#ifdef USE_SCINTILLA_EDITOR -#include + +#ifdef INSTALL_SUFFIX +#define RESOURCE_FOLDER(path) path INSTALL_SUFFIX +#else +#define RESOURCE_FOLDER(path) path #endif extern std::vector librarypath; @@ -23,7 +25,6 @@ static std::string lookupResourcesPath() fs::path resourcedir(applicationpath); PRINTDB("Looking up resource folder with application path '%s'", resourcedir.c_str()); -#ifndef WIN32 #ifdef __APPLE__ const char *searchpath[] = { "../Resources", // Resources can be bundled on Mac. @@ -32,15 +33,24 @@ static std::string lookupResourcesPath() NULL }; #else - const char *searchpath[] = { - "../share/openscad", - "../../share/openscad", +#ifdef WIN32 + const char *searchpath[] = { + ".", // Release location + RESOURCE_FOLDER("../share/openscad"), // MSYS2 location + "..", // Dev location + NULL + }; +#else + const char *searchpath[] = { + RESOURCE_FOLDER("../share/openscad"), + RESOURCE_FOLDER("../../share/openscad"), ".", "..", "../..", NULL }; #endif +#endif fs::path tmpdir; for (int a = 0;searchpath[a] != NULL;a++) { @@ -55,7 +65,6 @@ static std::string lookupResourcesPath() break; } } -#endif // !WIN32 // resourcedir defaults to applicationPath std::string result = boosty::stringy(boosty::canonical(resourcedir)); @@ -151,7 +160,7 @@ bool PlatformUtils::createBackupPath() } // This is the built-in read-only resources path -std::string PlatformUtils::resourcesPath() +std::string PlatformUtils::resourceBasePath() { if (!path_initialized) { throw std::runtime_error("PlatformUtils::resourcesPath(): application path not initialized!"); @@ -159,6 +168,21 @@ std::string PlatformUtils::resourcesPath() return resourcespath; } +fs::path PlatformUtils::resourcePath(const std::string &resource) +{ + fs::path base(resourceBasePath()); + if (!fs::is_directory(base)) { + return fs::path(); + } + + fs::path resource_dir = base / resource; + if (!fs::is_directory(resource_dir)) { + return fs::path(); + } + + return resource_dir; +} + int PlatformUtils::setenv(const char *name, const char *value, int overwrite) { #if defined(WIN32) @@ -174,3 +198,25 @@ int PlatformUtils::setenv(const char *name, const char *value, int overwrite) return ::setenv(name, value, overwrite); #endif } + +std::string PlatformUtils::toMemorySizeString(uint64_t bytes, int digits) +{ + static const char *units[] = { "B", "kB", "MB", "GB", "TB", NULL }; + + int idx = 0; + double val = bytes; + while (true) { + if (val < 1024.0) { + break; + } + if (units[idx + 1] == NULL) { + break; + } + idx++; + val /= 1024.0; + } + + boost::format fmt("%f %s"); + fmt % boost::io::group(std::setprecision(digits), val) % units[idx]; + return fmt.str(); +} diff --git a/src/PlatformUtils.h b/src/PlatformUtils.h index 11b58b08..0e9f9868 100644 --- a/src/PlatformUtils.h +++ b/src/PlatformUtils.h @@ -2,6 +2,11 @@ #include +#include "boosty.h" + +#define STACK_BUFFER_SIZE (64 * 1024) +#define STACK_LIMIT_DEFAULT (8 * 1024 * 1024 - STACK_BUFFER_SIZE) + namespace PlatformUtils { extern const char *OPENSCAD_FOLDER_NAME; @@ -9,7 +14,8 @@ namespace PlatformUtils { std::string applicationPath(); std::string documentsPath(); - std::string resourcesPath(); + std::string resourceBasePath(); + fs::path resourcePath(const std::string& resource); std::string userLibraryPath(); /** @@ -28,6 +34,20 @@ namespace PlatformUtils { std::string backupPath(); bool createBackupPath(); + /** + * Return a human readable text describing the operating system + * the application is currently running on. This is mainly intended + * to provide information for bug reports (e.g. to be included in + * the LibraryInfoDialog). + * + * If there is some error to retrieve the details, at least the + * OS type is reported based on what platform the application was + * built for. + * + * @return system information. + */ + std::string sysinfo(); + /** * Platform abstraction to set environment variables. Windows/MinGW * does not support setenv(), but needs _putenv(). @@ -38,6 +58,14 @@ namespace PlatformUtils { */ int setenv(const char *name, const char *value, int overwrite); + /** + * Return system defined stack limit. If the system does not define + * a specific limit, the platform specific code will select a value. + * + * @return maximum stack size in bytes. + */ + unsigned long stackLimit(); + /** * Single character separating path specifications in a list * (e.g. OPENSCADPATH). On Windows that's ';' and on most other @@ -51,4 +79,10 @@ namespace PlatformUtils { * Currently limited to MS Windows GUI application console only. */ void ensureStdIO(void); + + /** + * Convert the number of bytes to a human readable string with + * a given number of digits. + */ + std::string toMemorySizeString(uint64_t bytes, int digits); } diff --git a/src/Polygon2d-CGAL.cc b/src/Polygon2d-CGAL.cc index cd322d99..b6f2f663 100644 --- a/src/Polygon2d-CGAL.cc +++ b/src/Polygon2d-CGAL.cc @@ -105,6 +105,7 @@ mark_domains(CDT &cdt) */ PolySet *Polygon2d::tessellate() const { + PRINTDB("Polygon2d::tessellate(): %d outlines", this->outlines().size()); PolySet *polyset = new PolySet(*this); Polygon2DCGAL::CDT cdt; // Uses a constrained Delaunay triangulator. diff --git a/src/Polygon2d.cc b/src/Polygon2d.cc index 9218aae5..a1e2a664 100644 --- a/src/Polygon2d.cc +++ b/src/Polygon2d.cc @@ -11,7 +11,10 @@ We can store sanitized vs. unsanitized polygons. Sanitized polygons will have opposite winding order for holes and is guaranteed to not - have intersecting geometry. Sanitization is typically done by ClipperUtils. + have intersecting geometry. The winding order will be counter-clockwise + for positive outlines and clockwise for holes. Sanitization is typically + done by ClipperUtils, but if you create geometry which you know is sanitized, + the flag can be set manually. */ size_t Polygon2d::memsize() const @@ -56,7 +59,7 @@ bool Polygon2d::isEmpty() const void Polygon2d::transform(const Transform2d &mat) { if (mat.matrix().determinant() == 0) { - PRINT("Warning: Scaling a 2D object with 0 - removing object"); + PRINT("WARNING: Scaling a 2D object with 0 - removing object"); this->theoutlines.clear(); return; } diff --git a/src/Preferences.cc b/src/Preferences.cc index 73cbcfb8..f133ffdc 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -45,6 +45,55 @@ Preferences *Preferences::instance = NULL; const char * Preferences::featurePropertyName = "FeatureProperty"; Q_DECLARE_METATYPE(Feature *); +class SettingsReader : public Settings::Visitor +{ + QSettings settings; + const Value getValue(const Settings::SettingsEntry& entry, const std::string& value) const { + if (value.empty()) { + return entry.defaultValue(); + } + + try { + switch (entry.defaultValue().type()) { + case Value::STRING: + return Value(value); + case Value::NUMBER: + return Value(boost::lexical_cast(value)); + case Value::BOOL: + return Value(boost::lexical_cast(value)); + default: + assert(false && "invalid value type for settings"); + } + } catch (const boost::bad_lexical_cast& e) { + return entry.defaultValue(); + } + } + + virtual void handle(Settings::SettingsEntry& entry) const { + Settings::Settings *s = Settings::Settings::inst(); + + std::string key = entry.category() + "/" + entry.name(); + std::string value = settings.value(QString::fromStdString(key)).toString().toStdString(); + s->set(entry, getValue(entry, value)); + } +}; + +class SettingsWriter : public Settings::Visitor +{ + virtual void handle(Settings::SettingsEntry& entry) const { + Settings::Settings *s = Settings::Settings::inst(); + + QSettings settings; + QString key = QString::fromStdString(entry.category() + "/" + entry.name()); + if (entry.is_default()) { + settings.remove(key); + } else { + Value value = s->get(entry); + settings.setValue(key, QString::fromStdString(value.toString())); + } + } +}; + Preferences::Preferences(QWidget *parent) : QMainWindow(parent) { setupUi(this); @@ -108,6 +157,7 @@ void Preferences::init() { this->defaultmap["advanced/undockableWindows"] = false; this->defaultmap["advanced/reorderWindows"] = true; this->defaultmap["launcher/showOnStartup"] = true; + this->defaultmap["advanced/localization"] = true; // Toolbar QActionGroup *group = new QActionGroup(this); @@ -139,6 +189,22 @@ void Preferences::init() { #endif this->polysetCacheSizeEdit->setValidator(validator); this->opencsgLimitEdit->setValidator(validator); + + initComboBox(this->comboBoxIndentUsing, Settings::Settings::indentStyle); + initComboBox(this->comboBoxLineWrap, Settings::Settings::lineWrap); + initComboBox(this->comboBoxLineWrapIndentationStyle, Settings::Settings::lineWrapIndentationStyle); + initComboBox(this->comboBoxLineWrapVisualizationEnd, Settings::Settings::lineWrapVisualizationEnd); + initComboBox(this->comboBoxLineWrapVisualizationStart, Settings::Settings::lineWrapVisualizationBegin); + initComboBox(this->comboBoxShowWhitespace, Settings::Settings::showWhitespace); + initComboBox(this->comboBoxTabKeyFunction, Settings::Settings::tabKeyFunction); + initSpinBox(this->spinBoxIndentationWidth, Settings::Settings::indentationWidth); + initSpinBox(this->spinBoxLineWrapIndentationIndent, Settings::Settings::lineWrapIndentation); + initSpinBox(this->spinBoxShowWhitespaceSize, Settings::Settings::showWhitespaceSize); + initSpinBox(this->spinBoxTabWidth, Settings::Settings::tabWidth); + + SettingsReader settingsReader; + Settings::Settings::inst()->visit(settingsReader); + emit editorConfigChanged(); } Preferences::~Preferences() @@ -380,6 +446,12 @@ void Preferences::on_opencsgLimitEdit_textChanged(const QString &text) // FIXME: Set this globally? } +void Preferences::on_localizationCheckBox_toggled(bool state) +{ + QSettings settings; + settings.setValue("advanced/localization", state); +} + void Preferences::on_forceGoldfeatherBox_toggled(bool state) { QSettings settings; @@ -393,13 +465,101 @@ void Preferences::on_mouseWheelZoomBox_toggled(bool state) settings.setValue("editor/ctrlmousewheelzoom", state); } -void -Preferences::on_launcherBox_toggled(bool state) +void Preferences::on_launcherBox_toggled(bool state) { QSettings settings; settings.setValue("launcher/showOnStartup", state); } +void Preferences::on_spinBoxIndentationWidth_valueChanged(int val) +{ + Settings::Settings::inst()->set(Settings::Settings::indentationWidth, Value(val)); + writeSettings(); +} + +void Preferences::on_spinBoxTabWidth_valueChanged(int val) +{ + Settings::Settings::inst()->set(Settings::Settings::tabWidth, Value(val)); + writeSettings(); +} + +void Preferences::on_comboBoxLineWrap_activated(int val) +{ + applyComboBox(comboBoxLineWrap, val, Settings::Settings::lineWrap); +} + +void Preferences::on_comboBoxLineWrapIndentationStyle_activated(int val) +{ + applyComboBox(comboBoxLineWrapIndentationStyle, val, Settings::Settings::lineWrapIndentationStyle); +} + +void Preferences::on_spinBoxLineWrapIndentationIndent_valueChanged(int val) +{ + Settings::Settings::inst()->set(Settings::Settings::lineWrapIndentation, Value(val)); + writeSettings(); +} + +void Preferences::on_comboBoxLineWrapVisualizationStart_activated(int val) +{ + applyComboBox(comboBoxLineWrapVisualizationStart, val, Settings::Settings::lineWrapVisualizationBegin); +} + +void Preferences::on_comboBoxLineWrapVisualizationEnd_activated(int val) +{ + applyComboBox(comboBoxLineWrapVisualizationEnd, val, Settings::Settings::lineWrapVisualizationEnd); +} + +void Preferences::on_comboBoxShowWhitespace_activated(int val) +{ + applyComboBox(comboBoxShowWhitespace, val, Settings::Settings::showWhitespace); +} + +void Preferences::on_spinBoxShowWhitespaceSize_valueChanged(int val) +{ + Settings::Settings::inst()->set(Settings::Settings::showWhitespaceSize, Value(val)); + writeSettings(); +} + +void Preferences::on_checkBoxAutoIndent_toggled(bool val) +{ + Settings::Settings::inst()->set(Settings::Settings::autoIndent, Value(val)); + writeSettings(); +} + +void Preferences::on_comboBoxIndentUsing_activated(int val) +{ + applyComboBox(comboBoxIndentUsing, val, Settings::Settings::indentStyle); +} + +void Preferences::on_comboBoxTabKeyFunction_activated(int val) +{ + applyComboBox(comboBoxTabKeyFunction, val, Settings::Settings::tabKeyFunction); +} + +void Preferences::on_checkBoxHighlightCurrentLine_toggled(bool val) +{ + Settings::Settings::inst()->set(Settings::Settings::highlightCurrentLine, Value(val)); + writeSettings(); +} + +void Preferences::on_checkBoxEnableBraceMatching_toggled(bool val) +{ + Settings::Settings::inst()->set(Settings::Settings::enableBraceMatching, Value(val)); + writeSettings(); +} + +void Preferences::writeSettings() +{ + SettingsWriter settingsWriter; + Settings::Settings::inst()->visit(settingsWriter); + fireEditorConfigChanged(); +} + +void Preferences::fireEditorConfigChanged() const +{ + emit editorConfigChanged(); +} + void Preferences::keyPressEvent(QKeyEvent *e) { #ifdef Q_OS_MAC @@ -486,12 +646,75 @@ void Preferences::updateGUI() this->cgalCacheSizeEdit->setText(getValue("advanced/cgalCacheSize").toString()); this->polysetCacheSizeEdit->setText(getValue("advanced/polysetCacheSize").toString()); this->opencsgLimitEdit->setText(getValue("advanced/openCSGLimit").toString()); + this->localizationCheckBox->setChecked(getValue("advanced/localization").toBool()); this->forceGoldfeatherBox->setChecked(getValue("advanced/forceGoldfeather").toBool()); this->mdiCheckBox->setChecked(getValue("advanced/mdi").toBool()); this->reorderCheckBox->setChecked(getValue("advanced/reorderWindows").toBool()); this->undockCheckBox->setChecked(getValue("advanced/undockableWindows").toBool()); this->undockCheckBox->setEnabled(this->reorderCheckBox->isChecked()); this->launcherBox->setChecked(getValue("launcher/showOnStartup").toBool()); + + Settings::Settings *s = Settings::Settings::inst(); + updateComboBox(this->comboBoxLineWrap, Settings::Settings::lineWrap); + updateComboBox(this->comboBoxLineWrapIndentationStyle, Settings::Settings::lineWrapIndentationStyle); + updateComboBox(this->comboBoxLineWrapVisualizationStart, Settings::Settings::lineWrapVisualizationBegin); + updateComboBox(this->comboBoxLineWrapVisualizationEnd, Settings::Settings::lineWrapVisualizationEnd); + updateComboBox(this->comboBoxShowWhitespace, Settings::Settings::showWhitespace); + updateComboBox(this->comboBoxIndentUsing, Settings::Settings::indentStyle); + updateComboBox(this->comboBoxTabKeyFunction, Settings::Settings::tabKeyFunction); + this->spinBoxIndentationWidth->setValue(s->get(Settings::Settings::indentationWidth).toDouble()); + this->spinBoxTabWidth->setValue(s->get(Settings::Settings::tabWidth).toDouble()); + this->spinBoxLineWrapIndentationIndent->setValue(s->get(Settings::Settings::lineWrapIndentation).toDouble()); + this->spinBoxShowWhitespaceSize->setValue(s->get(Settings::Settings::showWhitespaceSize).toDouble()); + this->checkBoxAutoIndent->setChecked(s->get(Settings::Settings::autoIndent).toBool()); + this->checkBoxHighlightCurrentLine->setChecked(s->get(Settings::Settings::highlightCurrentLine).toBool()); + this->checkBoxEnableBraceMatching->setChecked(s->get(Settings::Settings::enableBraceMatching).toBool()); +} + +void Preferences::initComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry) +{ + comboBox->clear(); + Value::VectorType vector = entry.range().toVector(); + for (Value::VectorType::iterator it = vector.begin();it != vector.end();it++) { + QString val = QString::fromStdString((*it)[0].toString()); + QString text = QString::fromStdString((*it)[1].toString()); + comboBox->addItem(text, val); + } +} + +void Preferences::initSpinBox(QSpinBox *spinBox, const Settings::SettingsEntry& entry) +{ + Value::RangeType range = entry.range().toRange(); + spinBox->setMinimum(range.begin_value()); + spinBox->setMaximum(range.end_value()); +} + +void Preferences::updateComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry) +{ + Settings::Settings *s = Settings::Settings::inst(); + + Value value = s->get(entry); + QString text = QString::fromStdString(value.toString()); + int idx = comboBox->findData(text); + if (idx >= 0) { + comboBox->setCurrentIndex(idx); + } else { + Value defaultValue = entry.defaultValue(); + QString defaultText = QString::fromStdString(defaultValue.toString()); + int defIdx = comboBox->findData(defaultText); + if (defIdx >= 0) { + comboBox->setCurrentIndex(defIdx); + } else { + comboBox->setCurrentIndex(0); + } + } +} + +void Preferences::applyComboBox(QComboBox *comboBox, int val, Settings::SettingsEntry& entry) +{ + QString s = comboBox->itemData(val).toString(); + Settings::Settings::inst()->set(entry, Value(s.toStdString())); + writeSettings(); } void Preferences::apply() const diff --git a/src/Preferences.h b/src/Preferences.h index 0f862eb2..82cced50 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -2,7 +2,10 @@ #include #include + +#include "qtgettext.h" #include "ui_Preferences.h" +#include "settings.h" class Preferences : public QMainWindow, public Ui::Preferences { @@ -17,6 +20,7 @@ public: QVariant getValue(const QString &key) const; void init(); void apply() const; + void fireEditorConfigChanged() const; public slots: void actionTriggered(class QAction *); @@ -32,6 +36,7 @@ public slots: void on_opencsgLimitEdit_textChanged(const QString &); void on_forceGoldfeatherBox_toggled(bool); void on_mouseWheelZoomBox_toggled(bool); + void on_localizationCheckBox_toggled(bool); void on_updateCheckBox_toggled(bool); void on_snapshotCheckBox_toggled(bool); void on_mdiCheckBox_toggled(bool); @@ -41,6 +46,30 @@ public slots: void on_launcherBox_toggled(bool); void on_editorType_editTextChanged(const QString &); + // + // editor settings + // + + // Indentation + void on_checkBoxAutoIndent_toggled(bool); + void on_comboBoxIndentUsing_activated(int); + void on_spinBoxIndentationWidth_valueChanged(int); + void on_spinBoxTabWidth_valueChanged(int); + void on_comboBoxTabKeyFunction_activated(int); + void on_comboBoxShowWhitespace_activated(int); + void on_spinBoxShowWhitespaceSize_valueChanged(int); + + // Line wrap + void on_comboBoxLineWrap_activated(int); + void on_comboBoxLineWrapIndentationStyle_activated(int); + void on_spinBoxLineWrapIndentationIndent_valueChanged(int); + void on_comboBoxLineWrapVisualizationStart_activated(int); + void on_comboBoxLineWrapVisualizationEnd_activated(int); + + // Display + void on_checkBoxHighlightCurrentLine_toggled(bool); + void on_checkBoxEnableBraceMatching_toggled(bool); + signals: void requestRedraw() const; void updateMdiMode(bool mdi) const; @@ -51,6 +80,7 @@ signals: void openCSGSettingsChanged() const; void syntaxHighlightChanged(const QString &s) const; void editorTypeChanged(const QString &type); + void editorConfigChanged() const; private: Preferences(QWidget *parent = NULL); @@ -58,8 +88,18 @@ private: void updateGUI(); void removeDefaultSettings(); void setupFeaturesPage(); + void writeSettings(); void addPrefPage(QActionGroup *group, QAction *action, QWidget *widget); + /** Initialize combobox list values from the settings range values */ + void initComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry); + /** Initialize spinbox min/max values from the settings range values */ + void initSpinBox(QSpinBox *spinBox, const Settings::SettingsEntry& entry); + /** Update combobox from current settings */ + void updateComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry); + /** Set value from combobox to settings */ + void applyComboBox(QComboBox *comboBox, int val, Settings::SettingsEntry& entry); + QSettings::SettingsMap defaultmap; QHash prefPages; diff --git a/src/Preferences.ui b/src/Preferences.ui index 324a4a87..7c411229 100644 --- a/src/Preferences.ui +++ b/src/Preferences.ui @@ -6,8 +6,8 @@ 0 0 - 852 - 554 + 621 + 413 @@ -23,11 +23,11 @@ true - + - 4 + 0 @@ -75,237 +75,897 @@ - - - - Qt::Vertical - - - - 20 - 645 - - - - + + 0 + + + 0 + - - - - - - 75 - true - - - - Editor Type - - - - - - - - Simple Editor - + + + true + + + + + 0 + 0 + 659 + 769 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + + Editor Type + + + + + + + + + + + + Simple Editor + + + + + QScintilla Editor + + + + + + + + + 10 + 50 + false + + + + (requires restart) + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + + Font + + + false + + + + + + + + + + + + Liberation Sans + 12 + + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + + Color syntax highlighting + + + + + + + + + Qt::Horizontal + + + + 198 + 20 + + + + + + + + Ctrl/Cmd-Mouse-wheel zooms text + + + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + - - - QScintilla Editor - + + + + Indentation + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 99 + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 223 + 20 + + + + + + + + Auto Indent + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Indent using + + + + + + + + + + + + Spaces + + + + + Tabs + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Indentation width + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Tab width + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Tab key function + + + + + + + + + + + + Indent + + + + + Insert Tab + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show whitespace + + + + + + + + + + + + Never + + + + + Always + + + + + Only after indentation + + + + + + + + Size + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 9 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + - - - - - - - 10 - 50 - false - - - - (requires restart) - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 30 - 20 - - - - - - - - - - - - - 75 - true - - - - Font - - - false - - - - - - - - Nimbus Sans L - 12 - - - - - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - - 75 - true - - - - Color syntax highlighting - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 30 - 20 - - - - - - - - - - 5 - - - - - - 75 - true - - - - Use Ctrl/Cmd-Mouse-wheel to zoom text - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 723 - - - + + + + Display + + + + + + Enable brace matching + + + + + + + Highlight current line + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Line wrap + + + + + + + + + None + + + + + Wrap at character boundaries + + + + + Wrap at word boundaries + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Line wrap indentation + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Line wrap visualization + + + + + + + + + + + Style + + + + + + + + Fixed + + + + + Same + + + + + Indented + + + + + + + + Indent + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Start + + + + + + + + 0 + 0 + + + + + None + + + + + Text + + + + + Border + + + + + Margin + + + + + + + + End + + + + + + + + 0 + 0 + + + + + None + + + + + Text + + + + + Border + + + + + Margin + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Line wrap + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - + @@ -456,7 +1116,7 @@ - + 75 @@ -522,143 +1182,193 @@ + + 0 + - - - OpenCSG + + + QFrame::Plain - - - - - Show capability warning - - - true - - - - - - - Enable for OpenGL 1.x - - - - - - - - - Turn off rendering at - - - - - - - - - - elements - - - - - - - - - Force Goldfeather - - - - - - - - - - - - CGAL Cache size + + Qt::ScrollBarAlwaysOn + + + true + + + + + 0 + 0 + 596 + 499 + + + + + 9 - - - - - - - - - bytes + + 9 - - - - - - - - - - PolySet Cache size + + 9 - - - - - - - - - bytes - - - - - - - - - Allow to open multiple documents - + + + + OpenCSG + + + + + + Show capability warning + + + true + + + + + + + Enable for OpenGL 1.x + + + + + + + + + Turn off rendering at + + + + + + + + + + elements + + + + + + + + + Force Goldfeather + + + + + + + + + + + + CGAL Cache size + + + + + + + + + + bytes + + + + + + + + + 0 + + + + + PolySet Cache size + + + + + + + + + + bytes + + + + + + + + + Allow to open multiple documents + + + + + + + Enable docking of Editor and Console in different places + + + + + + + Enable undocking of Editor and Console to separate windows + + + + + + + Show Welcome Screen + + + + + + + Enable user interface localization (requires restart of OpenSCAD) + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - - - - Enable docking of Editor and Console in different places - - - - - - - Enable undocking of Editor and Console to separate windows - - - - - - - Show Welcome Screen - - - - - - - Qt::Vertical - - - - 20 - 11 - - - - diff --git a/src/ProgressWidget.h b/src/ProgressWidget.h index 357e4c27..098cd7fe 100644 --- a/src/ProgressWidget.h +++ b/src/ProgressWidget.h @@ -1,5 +1,6 @@ #pragma once +#include "qtgettext.h" #include "ui_ProgressWidget.h" #include diff --git a/src/QGLView.cc b/src/QGLView.cc index ab830fa4..25ca07a2 100644 --- a/src/QGLView.cc +++ b/src/QGLView.cc @@ -24,6 +24,7 @@ * */ +#include "qtgettext.h" #include "QGLView.h" #include "Preferences.h" #include "renderer.h" @@ -63,7 +64,6 @@ static bool running_under_wine = false; void QGLView::init() { - cam.type = Camera::GIMBAL; resetView(); this->mouse_drag_active = false; @@ -83,9 +83,7 @@ void QGLView::init() void QGLView::resetView() { - cam.object_rot << 35, 0, -25; - cam.object_trans << 0, 0, 0; - cam.viewer_distance = 140; + cam.resetView(); } void QGLView::viewAll() @@ -110,7 +108,7 @@ std::string QGLView::getRendererInfo() const { std::string glewinfo = glew_dump(); std::string glextlist = glew_extensions_dump(); - return glewinfo + std::string("\nUsing QGLWidget\n\n") + glextlist; + return glewinfo + std::string(_("\nUsing QGLWidget\n\n")) + glextlist; } #ifdef ENABLE_OPENCSG @@ -127,19 +125,19 @@ void QGLView::display_opencsg_warning_dialog() QString message; if (this->is_opencsg_capable) { - message += "Warning: You may experience OpenCSG rendering errors.\n\n"; + message += _("Warning: You may experience OpenCSG rendering errors.\n\n"); } else { - message += "Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been disabled.\n\n"; + message += _("Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been disabled.\n\n"); dialog->enableOpenCSGBox->hide(); } - message += "It is highly recommended to use OpenSCAD on a system with " + message += _("It is highly recommended to use OpenSCAD on a system with " "OpenGL 2.0 or later.\n" - "Your renderer information is as follows:\n"; + "Your renderer information is as follows:\n"); QString rendererinfo; - rendererinfo.sprintf("GLEW version %s\n" + rendererinfo.sprintf(_("GLEW version %s\n" "%s (%s)\n" - "OpenGL version %s\n", + "OpenGL version %s\n"), glewGetString(GLEW_VERSION), glGetString(GL_RENDERER), glGetString(GL_VENDOR), glGetString(GL_VERSION)); @@ -163,16 +161,9 @@ void QGLView::paintGL() GLView::paintGL(); if (statusLabel) { - QString msg; - Camera nc(cam); nc.gimbalDefaultTranslate(); - msg.sprintf("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f", - nc.object_trans.x(), nc.object_trans.y(), nc.object_trans.z(), - nc.object_rot.x(), nc.object_rot.y(), nc.object_rot.z(), - nc.viewer_distance ); - - statusLabel->setText(msg); + statusLabel->setText(QString::fromStdString(nc.statusText())); } if (running_under_wine) swapBuffers(); @@ -251,11 +242,11 @@ void QGLView::mouseMoveEvent(QMouseEvent *event) // Middle button pans in the xy plane // Shift-right and Shift-middle zooms if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) { - cam.viewer_distance += (GLdouble)dy; + cam.zoom(-12.0 * dy); } else { - double mx = +(dx) * cam.viewer_distance/1000; - double mz = -(dy) * cam.viewer_distance/1000; + double mx = +(dx) * 3.0 * cam.zoomValue() / QWidget::width(); + double mz = -(dy) * 3.0 * cam.zoomValue() / QWidget::height(); double my = 0; #if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0)) diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index 4b114041..dbdf527c 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -74,12 +74,15 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, const Color4f &c = obj.color; glPushMatrix(); glMultMatrixd(m.data()); - csgmode_e csgmode = obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL; + csgmode_e csgmode = csgmode_e( + (highlight ? + CSGMODE_HIGHLIGHT : + (background ? CSGMODE_BACKGROUND : CSGMODE_NORMAL)) | + (obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : 0)); ColorMode colormode = COLORMODE_NONE; ColorMode edge_colormode = COLORMODE_NONE; if (highlight) { - csgmode = csgmode_e(csgmode + 20); colormode = COLORMODE_HIGHLIGHT; edge_colormode = COLORMODE_HIGHLIGHT_EDGES; } else if (background) { @@ -89,16 +92,11 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, else { colormode = COLORMODE_BACKGROUND; } - csgmode = csgmode_e(csgmode + 10); edge_colormode = COLORMODE_BACKGROUND_EDGES; } else if (fberror) { - if (highlight) csgmode = csgmode_e(csgmode + 20); - else if (background) csgmode = csgmode_e(csgmode + 10); - else csgmode = csgmode_e(csgmode); } else if (obj.type == CSGTerm::TYPE_DIFFERENCE) { if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) { colormode = COLORMODE_HIGHLIGHT; - csgmode = csgmode_e(csgmode + 20); } else { colormode = COLORMODE_CUTOUT; @@ -107,7 +105,6 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, } else { if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) { colormode = COLORMODE_HIGHLIGHT; - csgmode = csgmode_e(csgmode + 20); } else { colormode = COLORMODE_MATERIAL; diff --git a/src/Tree.cc b/src/Tree.cc index d27e1984..7264d89d 100644 --- a/src/Tree.cc +++ b/src/Tree.cc @@ -1,8 +1,11 @@ #include "Tree.h" #include "nodedumper.h" +#include "printutils.h" #include #include +#include +#include Tree::~Tree() { @@ -29,11 +32,6 @@ const std::string &Tree::getString(const AbstractNode &node) const return this->nodecache[node]; } -static bool filter(char c) -{ - return c == ' ' || c == '\n' || c == '\t' || c == '\r'; -} - /*! Returns the cached ID string representation of the subtree rooted by \a node. If node is not cached, the cache will be rebuilt. @@ -45,12 +43,22 @@ static bool filter(char c) const std::string &Tree::getIdString(const AbstractNode &node) const { assert(this->root_node); + if (!this->nodeidcache.contains(node)) { - std::string str = getString(node); - str.erase(std::remove_if(str.begin(), str.end(), filter), str.end()); - return this->nodeidcache.insert(node, str); + const std::string &nodestr = getString(node); + const boost::regex re("[^\\s\\\"]+|\\\"(?:[^\\\"\\\\]|\\\\.)*\\\""); + std::stringstream sstream; + boost::sregex_token_iterator i(nodestr.begin(), nodestr.end(), re, 0); + std::copy(i, boost::sregex_token_iterator(), std::ostream_iterator(sstream)); + + const std::string & result = this->nodeidcache.insert(node, sstream.str()); + PRINTDB("Id Cache MISS: %s", result); + return result; + } else { + const std::string & result = this->nodeidcache[node]; + PRINTDB("Id Cache HIT: %s", result); + return result; } - return this->nodeidcache[node]; } /*! diff --git a/src/UIUtils.cc b/src/UIUtils.cc index 9785629b..3e8c8c01 100644 --- a/src/UIUtils.cc +++ b/src/UIUtils.cc @@ -31,6 +31,7 @@ #include #include +#include "qtgettext.h" #include "UIUtils.h" #include "PlatformUtils.h" @@ -81,15 +82,15 @@ QStringList UIUtils::exampleCategories() { QStringList categories; //categories in File menu item - Examples - categories << "Basics" << "Shapes" << "Extrusion" << "Advanced"; + categories << N_("Basics") << N_("Shapes") << N_("Extrusion") << N_("Advanced"); return categories; } QFileInfoList UIUtils::exampleFiles(const QString &category) { - QDir dir(QString::fromStdString(PlatformUtils::resourcesPath())); - if (!dir.cd("examples") || !dir.cd(category)) { + QDir dir(QString::fromStdString(PlatformUtils::resourcePath("examples").string())); + if (!dir.cd(category)) { return QFileInfoList(); } diff --git a/src/builtin.cc b/src/builtin.cc index d91d17ad..e495f4a5 100644 --- a/src/builtin.cc +++ b/src/builtin.cc @@ -95,19 +95,19 @@ std::string Builtins::isDeprecated(const std::string &name) Builtins::Builtins() { - this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr(new Expression(Value(0.0))))); - this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr(new Expression(Value(2.0))))); - this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr(new Expression(Value(12.0))))); - this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr(new Expression(Value(0.0))))); + this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr(new ExpressionConst(ValuePtr(0.0))))); + this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr(new ExpressionConst(ValuePtr(2.0))))); + this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr(new ExpressionConst(ValuePtr(12.0))))); + this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr(new ExpressionConst(ValuePtr(0.0))))); Value::VectorType zero3; zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0)); - Value zero3val(zero3); - this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr(new Expression(zero3val)))); - this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr(new Expression(zero3val)))); - this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr(new Expression(500)))); + ValuePtr zero3val(zero3); + this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr(new ExpressionConst(zero3val)))); + this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr(new ExpressionConst(zero3val)))); + this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr(new ExpressionConst(ValuePtr(500))))); } Builtins::~Builtins() diff --git a/src/cgal.h b/src/cgal.h index 3d096d6c..eb7203bd 100644 --- a/src/cgal.h +++ b/src/cgal.h @@ -27,7 +27,7 @@ using boost::uintmax_t; #include #include #include -#include +#include "CGAL_Nef3_workaround.h" #include #include #include @@ -57,11 +57,10 @@ typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3; typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; typedef CGAL::Polyhedron_3 CGAL_Polyhedron; -typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS; -typedef CGAL::Polyhedron_incremental_builder_3 CGAL_Polybuilder; typedef CGAL::Point_3 CGAL_Point_3; typedef CGAL::Iso_cuboid_3 CGAL_Iso_cuboid_3; +typedef std::vector CGAL_Polygon_3; // CGAL_Nef_polyhedron2 uses CGAL_Kernel2, but Iso_rectangle_2 needs to match // CGAL_Nef_polyhedron2::Explorer::Point which is different than diff --git a/src/cgaladv.cc b/src/cgaladv.cc index 1822cac0..20addb78 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -64,8 +64,11 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value convexity, path, subdiv_type, level; - + ValuePtr convexity = ValuePtr::undefined; + ValuePtr path = ValuePtr::undefined; + ValuePtr subdiv_type = ValuePtr::undefined; + ValuePtr level = ValuePtr::undefined; + if (type == MINKOWSKI) { convexity = c.lookup_variable("convexity", true); } @@ -82,31 +85,31 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant } if (type == RESIZE) { - Value ns = c.lookup_variable("newsize"); + ValuePtr ns = c.lookup_variable("newsize"); node->newsize << 0,0,0; - if ( ns.type() == Value::VECTOR ) { - Value::VectorType vs = ns.toVector(); + if ( ns->type() == Value::VECTOR ) { + const Value::VectorType &vs = ns->toVector(); if ( vs.size() >= 1 ) node->newsize[0] = vs[0].toDouble(); if ( vs.size() >= 2 ) node->newsize[1] = vs[1].toDouble(); if ( vs.size() >= 3 ) node->newsize[2] = vs[2].toDouble(); } - Value autosize = c.lookup_variable("auto"); + ValuePtr autosize = c.lookup_variable("auto"); node->autosize << false, false, false; - if ( autosize.type() == Value::VECTOR ) { - Value::VectorType va = autosize.toVector(); + if ( autosize->type() == Value::VECTOR ) { + const Value::VectorType &va = autosize->toVector(); if ( va.size() >= 1 ) node->autosize[0] = va[0].toBool(); if ( va.size() >= 2 ) node->autosize[1] = va[1].toBool(); if ( va.size() >= 3 ) node->autosize[2] = va[2].toBool(); } - else if ( autosize.type() == Value::BOOL ) { - node->autosize << autosize.toBool(),autosize.toBool(),autosize.toBool(); + else if ( autosize->type() == Value::BOOL ) { + node->autosize << autosize->toBool(),autosize->toBool(),autosize->toBool(); } } - node->convexity = (int)convexity.toDouble(); + node->convexity = (int)convexity->toDouble(); node->path = path; - node->subdiv_type = subdiv_type.toString(); - node->level = (int)level.toDouble(); + node->subdiv_type = subdiv_type->toString(); + node->level = (int)level->toDouble(); if (node->level <= 1) node->level = 1; @@ -151,7 +154,7 @@ std::string CgaladvNode::toString() const stream << "(convexity = " << this->convexity << ")"; break; case GLIDE: - stream << "(path = " << this->path << ", convexity = " << this->convexity << ")"; + stream << "(path = " << *this->path << ", convexity = " << this->convexity << ")"; break; case SUBDIV: stream << "(level = " << this->level << ", convexity = " << this->convexity << ")"; diff --git a/src/cgaladvnode.h b/src/cgaladvnode.h index 6794ae90..a2f81fe7 100644 --- a/src/cgaladvnode.h +++ b/src/cgaladvnode.h @@ -26,7 +26,7 @@ public: virtual std::string toString() const; virtual std::string name() const; - Value path; + ValuePtr path; std::string subdiv_type; int convexity, level; Vector3d newsize; diff --git a/src/cgalutils-polyhedron.cc b/src/cgalutils-polyhedron.cc new file mode 100644 index 00000000..43ab72d6 --- /dev/null +++ b/src/cgalutils-polyhedron.cc @@ -0,0 +1,301 @@ +#ifdef ENABLE_CGAL + +#include "cgalutils.h" +#include "polyset.h" +#include "printutils.h" +#include "polyset-utils.h" +#include "grid.h" + +#include "cgal.h" +#include + +#include + +namespace /* anonymous */ { + template + Result vector_convert(V const& v) { + return Result(CGAL::to_double(v[0]),CGAL::to_double(v[1]),CGAL::to_double(v[2])); + } + +#undef GEN_SURFACE_DEBUG + + template + class CGAL_Build_PolySet : public CGAL::Modifier_base + { + typedef typename Polyhedron::HalfedgeDS HDS; + typedef CGAL::Polyhedron_incremental_builder_3 CGAL_Polybuilder; + public: + typedef typename CGAL_Polybuilder::Point_3 CGALPoint; + + const PolySet &ps; + CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } + +/* + Using Grid here is important for performance reasons. See following model. + If we don't grid the geometry before converting to a Nef Polyhedron, the quads + in the cylinders to tessellated into triangles since floating point + incertainty causes the faces to not be 100% planar. The incertainty is exaggerated + by the transform. This wasn't a problem earlier since we used Nef for everything, + but optimizations since then has made us keep it in floating point space longer. + + minkowski() { + cube([200, 50, 7], center = true); + rotate([90,0,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); + rotate([0,90,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); + } +*/ +#if 1 // Use Grid + void operator()(HDS& hds) { + CGAL_Polybuilder B(hds, true); + + std::vector vertices; + Grid3d grid(GRID_FINE); + std::vector indices(3); + + BOOST_FOREACH(const Polygon &p, ps.polygons) { + BOOST_REVERSE_FOREACH(Vector3d v, p) { + if (!grid.has(v)) { + // align v to the grid; the CGALPoint will receive the aligned vertex + grid.align(v) = vertices.size(); + vertices.push_back(CGALPoint(v[0], v[1], v[2])); + } + } + } + +#ifdef GEN_SURFACE_DEBUG + printf("polyhedron(faces=["); + int pidx = 0; +#endif + B.begin_surface(vertices.size(), ps.polygons.size()); + BOOST_FOREACH(const CGALPoint &p, vertices) { + B.add_vertex(p); + } + BOOST_FOREACH(const Polygon &p, ps.polygons) { +#ifdef GEN_SURFACE_DEBUG + if (pidx++ > 0) printf(","); +#endif + indices.clear(); + BOOST_FOREACH(const Vector3d &v, p) { + indices.push_back(grid.data(v)); + } + + // We remove duplicate indices since there is a bug in CGAL's + // Polyhedron_incremental_builder_3::test_facet() which fails to detect this + std::vector::iterator last = std::unique(indices.begin(), indices.end()); + std::advance(last, -1); + if (*last != indices.front()) last++; // In case the first & last are equal + indices.erase(last, indices.end()); + if (indices.size() >= 3 && B.test_facet(indices.begin(), indices.end())) { + B.add_facet(indices.begin(), indices.end()); + } +#ifdef GEN_SURFACE_DEBUG + printf("["); + int fidx = 0; + BOOST_REVERSE_FOREACH(size_t i, indices) { + if (fidx++ > 0) printf(","); + printf("%ld", i); + } + printf("]"); +#endif + } + B.end_surface(); +#ifdef GEN_SURFACE_DEBUG + printf("],\n"); +#endif +#ifdef GEN_SURFACE_DEBUG + printf("points=["); + for (int i=0;i 0) printf(","); + const CGALPoint &p = vertices[i]; + printf("[%g,%g,%g]", CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z())); + } + printf("]);\n"); +#endif + } +#else // Don't use Grid + void operator()(HDS& hds) + { + CGAL_Polybuilder B(hds, true); + Reindexer vertices; + std::vector indices(3); + + // Estimating same # of vertices as polygons (very rough) + B.begin_surface(ps.polygons.size(), ps.polygons.size()); + int pidx = 0; +#ifdef GEN_SURFACE_DEBUG + printf("polyhedron(faces=["); +#endif + BOOST_FOREACH(const Polygon &p, ps.polygons) { +#ifdef GEN_SURFACE_DEBUG + if (pidx++ > 0) printf(","); +#endif + indices.clear(); + BOOST_REVERSE_FOREACH(const Vector3d &v, p) { + size_t s = vertices.size(); + size_t idx = vertices.lookup(v); + // If we added a vertex, also add it to the CGAL builder + if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2])); + indices.push_back(idx); + } + // We perform this test since there is a bug in CGAL's + // Polyhedron_incremental_builder_3::test_facet() which + // fails to detect duplicate indices + bool err = false; + for (std::size_t i = 0; i < indices.size(); ++i) { + // check if vertex indices[i] is already in the sequence [0..i-1] + for (std::size_t k = 0; k < i && !err; ++k) { + if (indices[k] == indices[i]) { + err = true; + break; + } + } + } + if (!err && B.test_facet(indices.begin(), indices.end())) { + B.add_facet(indices.begin(), indices.end()); +#ifdef GEN_SURFACE_DEBUG + printf("["); + int fidx = 0; + BOOST_FOREACH(size_t i, indices) { + if (fidx++ > 0) printf(","); + printf("%ld", i); + } + printf("]"); +#endif + } + } + B.end_surface(); +#ifdef GEN_SURFACE_DEBUG + printf("],\n"); + + printf("points=["); + for (int vidx=0;vidx 0) printf(","); + const Vector3d &v = vertices.getArray()[vidx]; + printf("[%g,%g,%g]", v[0], v[1], v[2]); + } + printf("]);\n"); +#endif + } +#endif + }; + + // This code is from CGAL/demo/Polyhedron/Scene_nef_polyhedron_item.cpp + // quick hacks to convert polyhedra from exact to inexact and vice-versa + template + struct Copy_polyhedron_to : public CGAL::Modifier_base + { + Copy_polyhedron_to(const Polyhedron_input& in_poly) : in_poly(in_poly) {} + + void operator()(typename Polyhedron_output::HalfedgeDS& out_hds) + { + typedef typename Polyhedron_output::HalfedgeDS Output_HDS; + + CGAL::Polyhedron_incremental_builder_3 builder(out_hds); + + typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator; + typedef typename Polyhedron_input::Facet_const_iterator Facet_const_iterator; + typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC; + + builder.begin_surface(in_poly.size_of_vertices(), + in_poly.size_of_facets(), + in_poly.size_of_halfedges()); + + for (Vertex_const_iterator + vi = in_poly.vertices_begin(), end = in_poly.vertices_end(); + vi != end ; ++vi) { + typename Polyhedron_output::Point_3 p(::CGAL::to_double(vi->point().x()), + ::CGAL::to_double(vi->point().y()), + ::CGAL::to_double(vi->point().z())); + builder.add_vertex(p); + } + + typedef CGAL::Inverse_index Index; + Index index(in_poly.vertices_begin(), in_poly.vertices_end()); + + for (Facet_const_iterator + fi = in_poly.facets_begin(), end = in_poly.facets_end(); + fi != end; ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + // std::size_t n = circulator_size(hc); + // CGAL_assertion(n >= 3); + builder.begin_facet (); + do { + builder.add_vertex_to_facet(index[hc->vertex()]); + ++hc; + } while(hc != hc_end); + builder.end_facet(); + } + builder.end_surface(); + } // end operator()(..) + private: + const Polyhedron_input& in_poly; + }; // end Copy_polyhedron_to<> + +} + +namespace CGALUtils { + + template + void copyPolyhedron(const Polyhedron_A &poly_a, Polyhedron_B &poly_b) + { + Copy_polyhedron_to modifier(poly_a); + poly_b.delegate(modifier); + } + + template void copyPolyhedron(const CGAL::Polyhedron_3 &, CGAL_Polyhedron &); + template void copyPolyhedron(const CGAL_Polyhedron &, CGAL::Polyhedron_3 &); + + template + bool createPolyhedronFromPolySet(const PolySet &ps, Polyhedron &p) + { + bool err = false; + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + CGAL_Build_PolySet builder(ps); + p.delegate(builder); + } + catch (const CGAL::Assertion_exception &e) { + PRINTB("CGAL error in CGALUtils::createPolyhedronFromPolySet: %s", e.what()); + err = true; + } + CGAL::set_error_behaviour(old_behaviour); + return err; + } + + template bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p); + template bool createPolyhedronFromPolySet(const PolySet &ps, CGAL::Polyhedron_3 &p); + + template + bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps) + { + bool err = false; + typedef typename Polyhedron::Vertex Vertex; + typedef typename Polyhedron::Vertex_const_iterator VCI; + typedef typename Polyhedron::Facet_const_iterator FCI; + typedef typename Polyhedron::Halfedge_around_facet_const_circulator HFCC; + + for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + ps.append_poly(); + do { + Vertex const& v = *((hc++)->vertex()); + double x = CGAL::to_double(v.point().x()); + double y = CGAL::to_double(v.point().y()); + double z = CGAL::to_double(v.point().z()); + ps.append_vertex(x, y, z); + } while (hc != hc_end); + } + return err; + } + + template bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); + template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 > &p, PolySet &ps); + +}; // namespace CGALUtils + +#endif /* ENABLE_CGAL */ + diff --git a/src/cgalutils-tess-old.cc b/src/cgalutils-tess-old.cc new file mode 100644 index 00000000..d779772f --- /dev/null +++ b/src/cgalutils-tess-old.cc @@ -0,0 +1,553 @@ +/* + +This is our custom tessellator of Nef Polyhedron faces. The problem with +Nef faces is that sometimes the 'default' tessellator of Nef Polyhedron +doesnt work. This is particularly true with situations where the polygon +face is not, actually, 'simple', according to CGAL itself. This can +occur on a bad quality STL import but also for other reasons. The +resulting Nef face will appear to the average human eye as an ordinary, +simple polygon... but in reality it has multiple edges that are +slightly-out-of-alignment and sometimes they backtrack on themselves. + +When the triangulator is fed a polygon with self-intersecting edges, +it's default behavior is to throw an exception. The other terminology +for this is to say that the 'constraints' in the triangulation are +'intersecting'. The 'constraints' represent the edges of the polygon. +The 'triangulation' is the covering of all the polygon points with +triangles. + +How do we allow interseting constraints during triangulation? We use an +'Itag' for the triangulation, per the CGAL docs. This allows the +triangulator to run without throwing an exception when it encounters +self-intersecting polygon edges. The trick here is that when it finds +an intersection, it actually creates a new point. + +The triangulator creates new points in 2d, but they aren't matched to +any 3d points on our 3d polygon plane. (The plane of the Nef face). How +to fix this problem? We actually 'project back up' or 'lift' into the 3d +plane from the 2d point. This is handled in the 'deproject()' function. + +There is also the issue of the Simplicity of Nef Polyhedron face +polygons. They are often not simple. The intersecting-constraints +Triangulation can triangulate non-simple polygons, but of course it's +result is also non-simple. This means that CGAL functions like +orientation_2() and bounded_side() simply will not work on the resulting +polygons because they all require input polygons to pass the +'is_simple2()' test. We have to use alternatives in order to create our +triangles. + +There is also the question of which underlying number type to use. Some +of the CGAL functions simply dont guarantee good results with a type +like double. Although much the math here is somewhat simple, like +line-line intersection, and involves only simple algebra, the +approximations required when using floating-point types can cause the +answers to be wrong. For example questions like 'is a point inside a +triangle' do not have good answers under floating-point systems where a +line may have a slope that is not expressible exactly as a floating +point number. There are ways to deal with floating point inaccuracy but +it is much, much simpler to use Rational numbers, although potentially +much slower in many cases. + +*/ + +#include "cgalutils.h" +#include +#include + +typedef CGAL_Kernel3 Kernel; +//typedef CGAL::Triangulation_vertex_base_2 Vb; +typedef CGAL::Triangulation_vertex_base_2 Vb; +//typedef CGAL::Constrained_triangulation_face_base_2 Fb; +typedef CGAL::Delaunay_mesh_face_base_2 Fb; +typedef CGAL::Triangulation_data_structure_2 TDS; +typedef CGAL::Exact_intersections_tag ITAG; +typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; +//typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; + +typedef CDT::Vertex_handle Vertex_handle; +typedef CDT::Point CDTPoint; + +typedef CGAL::Ray_2 CGAL_Ray_2; +typedef CGAL::Line_3 CGAL_Line_3; +typedef CGAL::Point_2 CGAL_Point_2; +typedef CGAL::Vector_2 CGAL_Vector_2; +typedef CGAL::Segment_2 CGAL_Segment_2; +typedef CGAL::Direction_2 CGAL_Direction_2; +typedef CGAL::Direction_3 CGAL_Direction_3; +typedef CGAL::Plane_3 CGAL_Plane_3; + +/* The idea of 'projection' is how we make 3d points appear as though +they were 2d points to the tessellation algorithm. We take the 3-d plane +on which the polygon lies, and then 'project' or 'cast its shadow' onto +one of three standard planes, the xyplane, the yzplane, or the xzplane, +depending on which projection will prevent the polygon looking like a +flat line. (imagine, the triangle 0,0,1 0,1,1 0,1,0 ... if viewed from +the 'top' it looks line a flat line. so we want to view it from the +side). Thus we create a sequence of x,y points to feed to the algorithm, +but those points might actually be x,z pairs or y,z pairs... it is an +illusion we present to the triangulation algorithm by way of 'projection'. +We get a resulting sequence of triangles with x,y coordinates, which we +then 'deproject' back to x,z or y,z, in 3d space as needed. For example +the square 0,0,0 0,0,1 0,1,1 0,1,0 becomes '0,0 0,1 1,1 1,0', is then +split into two triangles, 0,0 1,0 1,1 and 0,0 1,1 0,1. those two triangles +then are projected back to 3d as 0,0,0 0,1,0 0,1,1 and 0,0 0,1,1 0,0,1. + +There is an additional trick we do with projection related to Polygon +orientation and the orientation of our output triangles, and thus, which +way they are facing in space (aka their 'normals' or 'oriented side'). + +The basic issues is this: every 3d flat polygon can be thought of as +having two sides. In Computer Graphics the convention is that the +'outside' or 'oriented side' or 'normal' is determined by looking at the +triangle in terms of the 'ordering' or 'winding' of the points. If the +points come in a 'clockwise' order, you must be looking at the triangle +from 'inside'. If the points come in a 'counterclockwise' order, you +must be looking at the triangle from the outside. For example, the +triangle 0,0,0 1,0,0 0,1,0, when viewed from the 'top', has points in a +counterclockwise order, so the 'up' side is the 'normal' or 'outside'. +if you look at that same triangle from the 'bottom' side, the points +will appear to be 'clockwise', so the 'down' side is the 'inside', and is the +opposite of the 'normal' side. + +How do we keep track of all that when doing a triangulation? We could +check each triangle as it was generated, and fix it's orientation before +we feed it back to our output list. That is done by, for example, checking +the orientation of the input polygon and then forcing the triangle to +match that orientation during output. This is what CGAL's Nef Polyhedron +does, you can read it inside /usr/include/CGAL/Nef_polyhedron_3.h. + +Or.... we could actually add an additional 'projection' to the incoming +polygon points so that our triangulation algorithm is guaranteed to +create triangles with the proper orientation in the first place. How? +First, we assume that the triangulation algorithm will always produce +'counterclockwise' triangles in our plain old x-y plane. + +The method is based on the following curious fact: That is, if you take +the points of a polygon, and flip the x,y coordinate of each point, +making y:=x and x:=y, then you essentially get a 'mirror image' of the +original polygon... but the orientation will be flipped. Given a +clockwise polygon, the 'flip' will result in a 'counterclockwise' +polygon mirror-image and vice versa. + +Now, there is a second curious fact that helps us here. In 3d, we are +using the plane equation of ax+by+cz+d=0, where a,b,c determine its +direction. If you notice, there are actually mutiple sets of numbers +a:b:c that will describe the exact same plane. For example the 'ground' +plane, called the XYplane, where z is everywhere 0, has the equation +0x+0y+1z+0=0, simplifying to a solution for x,y,z of z=0 and x,y = any +numbers in your number system. However you can also express this as +0x+0y+-1z=0. The x,y,z solution is the same: z is everywhere 0, x and y +are any number, even though a,b,c are different. We can say that the +plane is 'oriented' differently, if we wish. + +But how can we link that concept to the points on the polygon? Well, if +you generate a plane using the standard plane-equation generation +formula, given three points M,N,P, then you will get a plane equation +with . However if you feed the points in the reverse order, +P,N,M, so that they are now oriented in the opposite order, you will get +a plane equation with the signs flipped. <-a:-b:-c:-d> This means you +can essentially consider that a plane has an 'orientation' based on it's +equation, by looking at the signs of a,b,c relative to some other +quantity. + +This means that you can 'flip' the projection of the input polygon +points so that the projection will match the orientation of the input +plane, thus guaranteeing that the output triangles will be oriented in +the same direction as the input polygon was. In other words, even though +we technically 'lose information' when we project from 3d->2d, we can +actually keep the concept of 'orientation' through the whole +triangulation process, and not have to recalculate the proper +orientation during output. + +For example take two side-squares of a cube and the plane equations +formed by feeding the points in counterclockwise, as if looking in from +outside the cube: + + 0,0,0 0,1,0 0,1,1 0,0,1 <-1:0:0:0> + 1,0,0 1,1,0 1,1,1 1,0,1 <1:0:0:1> + +They are both projected onto the YZ plane. They look the same: + 0,0 1,0 1,1 0,1 + 0,0 1,0 1,1 0,1 + +But the second square plane has opposite orientation, so we flip the x +and y for each point: + 0,0 1,0 1,1 0,1 + 0,0 0,1 1,1 1,0 + +Only now do we feed these two 2-d squares to the tessellation algorithm. +The result is 4 triangles. When de-projected back to 3d, they will have +the appropriate winding that will match that of the original 3d faces. +And the first two triangles will have opposite orientation from the last two. +*/ + +typedef enum { XYPLANE, YZPLANE, XZPLANE, NONE } plane_t; +struct projection_t { + plane_t plane; + bool flip; +}; + +CGAL_Point_2 get_projected_point( CGAL_Point_3 &p3, projection_t projection ) { + NT3 x,y; + if (projection.plane == XYPLANE) { x = p3.x(); y = p3.y(); } + else if (projection.plane == XZPLANE) { x = p3.x(); y = p3.z(); } + else if (projection.plane == YZPLANE) { x = p3.y(); y = p3.z(); } + else if (projection.plane == NONE) { x = 0; y = 0; } + if (projection.flip) return CGAL_Point_2( y,x ); + return CGAL_Point_2( x,y ); +} + +/* given 2d point, 3d plane, and 3d->2d projection, 'deproject' from + 2d back onto a point on the 3d plane. true on failure, false on success */ +bool deproject( CGAL_Point_2 &p2, projection_t &projection, CGAL_Plane_3 &plane, CGAL_Point_3 &p3 ) +{ + NT3 x,y; + CGAL_Line_3 l; + CGAL_Point_3 p; + CGAL_Point_2 pf( p2.x(), p2.y() ); + if (projection.flip) pf = CGAL_Point_2( p2.y(), p2.x() ); + if (projection.plane == XYPLANE) { + p = CGAL_Point_3( pf.x(), pf.y(), 0 ); + l = CGAL_Line_3( p, CGAL_Direction_3(0,0,1) ); + } else if (projection.plane == XZPLANE) { + p = CGAL_Point_3( pf.x(), 0, pf.y() ); + l = CGAL_Line_3( p, CGAL_Direction_3(0,1,0) ); + } else if (projection.plane == YZPLANE) { + p = CGAL_Point_3( 0, pf.x(), pf.y() ); + l = CGAL_Line_3( p, CGAL_Direction_3(1,0,0) ); + } + CGAL::Object obj = CGAL::intersection( l, plane ); + const CGAL_Point_3 *point_test = CGAL::object_cast(&obj); + if (point_test) { + p3 = *point_test; + return false; + } + PRINT("ERROR: deproject failure"); + return true; +} + +/* this simple criteria guarantees CGALs triangulation algorithm will +terminate (i.e. not lock up and freeze the program) */ +template class DummyCriteria { +public: + typedef double Quality; + class Is_bad { + public: + CGAL::Mesh_2::Face_badness operator()(const Quality) const { + return CGAL::Mesh_2::NOT_BAD; + } + CGAL::Mesh_2::Face_badness operator()(const typename T::Face_handle&, Quality&q) const { + q = 1; + return CGAL::Mesh_2::NOT_BAD; + } + }; + Is_bad is_bad_object() const { return Is_bad(); } +}; + +NT3 sign( const NT3 &n ) +{ + if (n>0) return NT3(1); + if (n<0) return NT3(-1); + return NT3(0); +} + +/* wedge, also related to 'determinant', 'signed parallelogram area', +'side', 'turn', 'winding', '2d portion of cross-product', etc etc. this +function can tell you whether v1 is 'counterclockwise' or 'clockwise' +from v2, based on the sign of the result. when the input Vectors are +formed from three points, A-B and B-C, it can tell you if the path along +the points A->B->C is turning left or right.*/ +NT3 wedge( CGAL_Vector_2 &v1, CGAL_Vector_2 &v2 ) { + return v1.x()*v2.y()-v2.x()*v1.y(); +} + +/* given a point and a possibly non-simple polygon, determine if the +point is inside the polygon or not, using the given winding rule. note +that even_odd is not implemented. */ +typedef enum { NONZERO_WINDING, EVEN_ODD } winding_rule_t; +bool inside(CGAL_Point_2 &p1,std::vector &pgon, winding_rule_t winding_rule) +{ + NT3 winding_sum = NT3(0); + CGAL_Point_2 p2; + CGAL_Ray_2 eastray(p1,CGAL_Direction_2(1,0)); + for (size_t i=0;i(&obj); + if (point_test) { + p2 = *point_test; + CGAL_Vector_2 v1( p1, p2 ); + CGAL_Vector_2 v2( p2, head ); + NT3 this_winding = wedge( v1, v2 ); + winding_sum += sign(this_winding); + } else { + continue; + } + } + if (winding_sum != NT3(0) && winding_rule == NONZERO_WINDING ) return true; + return false; +} + +projection_t find_good_projection( CGAL_Plane_3 &plane ) +{ + projection_t goodproj; + goodproj.plane = NONE; + goodproj.flip = false; + NT3 qxy = plane.a()*plane.a()+plane.b()*plane.b(); + NT3 qyz = plane.b()*plane.b()+plane.c()*plane.c(); + NT3 qxz = plane.a()*plane.a()+plane.c()*plane.c(); + NT3 min = std::min(qxy,std::min(qyz,qxz)); + if (min==qxy) { + goodproj.plane = XYPLANE; + if (sign(plane.c())>0) goodproj.flip = true; + } else if (min==qyz) { + goodproj.plane = YZPLANE; + if (sign(plane.a())>0) goodproj.flip = true; + } else if (min==qxz) { + goodproj.plane = XZPLANE; + if (sign(plane.b())<0) goodproj.flip = true; + } else PRINT("ERROR: failed to find projection"); + return goodproj; +} + +namespace CGALUtils { +/* given a single near-planar 3d polygon with holes, tessellate into a + sequence of polygons without holes. as of writing, this means conversion + into a sequence of 3d triangles. the given plane should be the same plane + holding the polygon and it's holes. */ + bool tessellate3DFaceWithHolesNew(std::vector &polygons, + Polygons &triangles, + CGAL_Plane_3 &plane) + { + if (polygons.size()==1 && polygons[0].size()==3) { + PRINTD("input polygon has 3 points. shortcut tessellation."); + Polygon t; + t.push_back(Vector3d(CGAL::to_double(polygons[0][0].x()), CGAL::to_double(polygons[0][0].y()), CGAL::to_double(polygons[0][0].z()))); + t.push_back(Vector3d(CGAL::to_double(polygons[0][1].x()), CGAL::to_double(polygons[0][1].y()), CGAL::to_double(polygons[0][1].z()))); + t.push_back(Vector3d(CGAL::to_double(polygons[0][2].x()), CGAL::to_double(polygons[0][2].y()), CGAL::to_double(polygons[0][2].z()))); + triangles.push_back( t ); + return false; + } + bool err = false; + CDT cdt; + std::map vertmap; + + PRINTD("finding good projection"); + projection_t goodproj = find_good_projection( plane ); + + PRINTDB("plane %s",plane ); + PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip); + PRINTD("Inserting points and edges into Constrained Delaunay Triangulation"); + std::vector< std::vector > polygons2d; + for (size_t i=0;i vhandles; + std::vector polygon2d; + for (size_t j=0;j list_of_seeds; + for (size_t i=1;i &pgon = polygons2d[i]; + for (size_t j=0;j::iterator li = list_of_seeds.begin(); + for (;li!=list_of_seeds.end();li++) { + //PRINTB("seed %s",*li); + double x = CGAL::to_double( li->x() ); + double y = CGAL::to_double( li->y() ); + PRINTDB("seed %f,%f",x%y); + } + PRINTD("seeding done"); + + PRINTD( "meshing" ); + CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt, + list_of_seeds.begin(), list_of_seeds.end(), + DummyCriteria() ); + + PRINTD("meshing done"); + // this fails because it calls is_simple and is_simple fails on many + // Nef Polyhedron faces + //CGAL::Orientation original_orientation = + // CGAL::orientation_2( orienpgon.begin(), orienpgon.end() ); + + CDT::Finite_faces_iterator fit; + for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ ) + { + if(fit->is_in_domain()) { + CDTPoint p1 = cdt.triangle( fit )[0]; + CDTPoint p2 = cdt.triangle( fit )[1]; + CDTPoint p3 = cdt.triangle( fit )[2]; + CGAL_Point_3 cp1,cp2,cp3; + if (vertmap.count(p1)) cp1 = vertmap[p1]; + else err = deproject( p1, goodproj, plane, cp1 ); + if (vertmap.count(p2)) cp2 = vertmap[p2]; + else err = deproject( p2, goodproj, plane, cp2 ); + if (vertmap.count(p3)) cp3 = vertmap[p3]; + else err = deproject( p3, goodproj, plane, cp3 ); + if (err) PRINT("WARNING: 2d->3d deprojection failure"); + Polygon tri; + tri.push_back(Vector3d(CGAL::to_double(cp1.x()), CGAL::to_double(cp1.y()), CGAL::to_double(cp1.z()))); + tri.push_back(Vector3d(CGAL::to_double(cp2.x()), CGAL::to_double(cp2.y()), CGAL::to_double(cp2.z()))); + tri.push_back(Vector3d(CGAL::to_double(cp3.x()), CGAL::to_double(cp3.y()), CGAL::to_double(cp3.z()))); + triangles.push_back( tri ); + } + } + + PRINTDB("built %i triangles",triangles.size()); + return err; + } + + +/* given a single near-planar 3d polygon with holes, tessellate into a + sequence of polygons without holes. as of writing, this means conversion + into a sequence of 3d triangles. the given plane should be the same plane + holding the polygon and it's holes. */ + bool tessellate3DFaceWithHoles(std::vector &polygons, + std::vector &triangles, + CGAL_Plane_3 &plane) + { + if (polygons.size()==1 && polygons[0].size()==3) { + PRINTD("input polygon has 3 points. shortcut tessellation."); + CGAL_Polygon_3 t; + t.push_back(polygons[0][2]); + t.push_back(polygons[0][1]); + t.push_back(polygons[0][0]); + triangles.push_back( t ); + return false; + } + bool err = false; + CDT cdt; + std::map vertmap; + + PRINTD("finding good projection"); + projection_t goodproj = find_good_projection( plane ); + + PRINTDB("plane %s",plane ); + PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip); + PRINTD("Inserting points and edges into Constrained Delaunay Triangulation"); + std::vector< std::vector > polygons2d; + for (size_t i=0;i vhandles; + std::vector polygon2d; + for (size_t j=0;j list_of_seeds; + for (size_t i=1;i &pgon = polygons2d[i]; + for (size_t j=0;j::iterator li = list_of_seeds.begin(); + for (;li!=list_of_seeds.end();li++) { + //PRINTB("seed %s",*li); + double x = CGAL::to_double( li->x() ); + double y = CGAL::to_double( li->y() ); + PRINTDB("seed %f,%f",x%y); + } + PRINTD("seeding done"); + + PRINTD( "meshing" ); + CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt, + list_of_seeds.begin(), list_of_seeds.end(), + DummyCriteria() ); + + PRINTD("meshing done"); + // this fails because it calls is_simple and is_simple fails on many + // Nef Polyhedron faces + //CGAL::Orientation original_orientation = + // CGAL::orientation_2( orienpgon.begin(), orienpgon.end() ); + + CDT::Finite_faces_iterator fit; + for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ ) + { + if(fit->is_in_domain()) { + CDTPoint p1 = cdt.triangle( fit )[0]; + CDTPoint p2 = cdt.triangle( fit )[1]; + CDTPoint p3 = cdt.triangle( fit )[2]; + CGAL_Point_3 cp1,cp2,cp3; + CGAL_Polygon_3 pgon; + if (vertmap.count(p1)) cp1 = vertmap[p1]; + else err = deproject( p1, goodproj, plane, cp1 ); + if (vertmap.count(p2)) cp2 = vertmap[p2]; + else err = deproject( p2, goodproj, plane, cp2 ); + if (vertmap.count(p3)) cp3 = vertmap[p3]; + else err = deproject( p3, goodproj, plane, cp3 ); + if (err) PRINT("WARNING: 2d->3d deprojection failure"); + pgon.push_back( cp1 ); + pgon.push_back( cp2 ); + pgon.push_back( cp3 ); + triangles.push_back( pgon ); + } + } + + PRINTDB("built %i triangles",triangles.size()); + return err; + } + +}; diff --git a/src/cgalutils-tess.cc b/src/cgalutils-tess.cc index c8188908..e230e9db 100644 --- a/src/cgalutils-tess.cc +++ b/src/cgalutils-tess.cc @@ -1,433 +1,198 @@ -/* - -This is our custom tessellator of Nef Polyhedron faces. The problem with -Nef faces is that sometimes the 'default' tessellator of Nef Polyhedron -doesnt work. This is particularly true with situations where the polygon -face is not, actually, 'simple', according to CGAL itself. This can -occur on a bad quality STL import but also for other reasons. The -resulting Nef face will appear to the average human eye as an ordinary, -simple polygon... but in reality it has multiple edges that are -slightly-out-of-alignment and sometimes they backtrack on themselves. - -When the triangulator is fed a polygon with self-intersecting edges, -it's default behavior is to throw an exception. The other terminology -for this is to say that the 'constraints' in the triangulation are -'intersecting'. The 'constraints' represent the edges of the polygon. -The 'triangulation' is the covering of all the polygon points with -triangles. - -How do we allow interseting constraints during triangulation? We use an -'Itag' for the triangulation, per the CGAL docs. This allows the -triangulator to run without throwing an exception when it encounters -self-intersecting polygon edges. The trick here is that when it finds -an intersection, it actually creates a new point. - -The triangulator creates new points in 2d, but they aren't matched to -any 3d points on our 3d polygon plane. (The plane of the Nef face). How -to fix this problem? We actually 'project back up' or 'lift' into the 3d -plane from the 2d point. This is handled in the 'deproject()' function. - -There is also the issue of the Simplicity of Nef Polyhedron face -polygons. They are often not simple. The intersecting-constraints -Triangulation can triangulate non-simple polygons, but of course it's -result is also non-simple. This means that CGAL functions like -orientation_2() and bounded_side() simply will not work on the resulting -polygons because they all require input polygons to pass the -'is_simple2()' test. We have to use alternatives in order to create our -triangles. - -There is also the question of which underlying number type to use. Some -of the CGAL functions simply dont guarantee good results with a type -like double. Although much the math here is somewhat simple, like -line-line intersection, and involves only simple algebra, the -approximations required when using floating-point types can cause the -answers to be wrong. For example questions like 'is a point inside a -triangle' do not have good answers under floating-point systems where a -line may have a slope that is not expressible exactly as a floating -point number. There are ways to deal with floating point inaccuracy but -it is much, much simpler to use Rational numbers, although potentially -much slower in many cases. - -*/ - #include "cgalutils.h" -#include -#include +//#include "cgal.h" +//#include "tess.h" -typedef CGAL_Kernel3 Kernel; -//typedef CGAL::Triangulation_vertex_base_2 Vb; -typedef CGAL::Triangulation_vertex_base_2 Vb; -//typedef CGAL::Constrained_triangulation_face_base_2 Fb; -typedef CGAL::Delaunay_mesh_face_base_2 Fb; -typedef CGAL::Triangulation_data_structure_2 TDS; -typedef CGAL::Exact_intersections_tag ITAG; -typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; -//typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; +#ifdef NDEBUG +#define PREV_NDEBUG NDEBUG +#undef NDEBUG +#endif +#include +#include +#include +#ifdef PREV_NDEBUG +#define NDEBUG PREV_NDEBUG +#endif -typedef CDT::Vertex_handle Vertex_handle; -typedef CDT::Point CDTPoint; +#include -typedef CGAL::Ray_2 CGAL_Ray_2; -typedef CGAL::Line_3 CGAL_Line_3; -typedef CGAL::Point_2 CGAL_Point_2; -typedef CGAL::Vector_2 CGAL_Vector_2; -typedef CGAL::Segment_2 CGAL_Segment_2; -typedef CGAL::Direction_2 CGAL_Direction_2; -typedef CGAL::Direction_3 CGAL_Direction_3; -typedef CGAL::Plane_3 CGAL_Plane_3; -/* The idea of 'projection' is how we make 3d points appear as though -they were 2d points to the tessellation algorithm. We take the 3-d plane -on which the polygon lies, and then 'project' or 'cast its shadow' onto -one of three standard planes, the xyplane, the yzplane, or the xzplane, -depending on which projection will prevent the polygon looking like a -flat line. (imagine, the triangle 0,0,1 0,1,1 0,1,0 ... if viewed from -the 'top' it looks line a flat line. so we want to view it from the -side). Thus we create a sequence of x,y points to feed to the algorithm, -but those points might actually be x,z pairs or y,z pairs... it is an -illusion we present to the triangulation algorithm by way of 'projection'. -We get a resulting sequence of triangles with x,y coordinates, which we -then 'deproject' back to x,z or y,z, in 3d space as needed. For example -the square 0,0,0 0,0,1 0,1,1 0,1,0 becomes '0,0 0,1 1,1 1,0', is then -split into two triangles, 0,0 1,0 1,1 and 0,0 1,1 0,1. those two triangles -then are projected back to 3d as 0,0,0 0,1,0 0,1,1 and 0,0 0,1,1 0,0,1. - -There is an additional trick we do with projection related to Polygon -orientation and the orientation of our output triangles, and thus, which -way they are facing in space (aka their 'normals' or 'oriented side'). - -The basic issues is this: every 3d flat polygon can be thought of as -having two sides. In Computer Graphics the convention is that the -'outside' or 'oriented side' or 'normal' is determined by looking at the -triangle in terms of the 'ordering' or 'winding' of the points. If the -points come in a 'clockwise' order, you must be looking at the triangle -from 'inside'. If the points come in a 'counterclockwise' order, you -must be looking at the triangle from the outside. For example, the -triangle 0,0,0 1,0,0 0,1,0, when viewed from the 'top', has points in a -counterclockwise order, so the 'up' side is the 'normal' or 'outside'. -if you look at that same triangle from the 'bottom' side, the points -will appear to be 'clockwise', so the 'down' side is the 'inside', and is the -opposite of the 'normal' side. - -How do we keep track of all that when doing a triangulation? We could -check each triangle as it was generated, and fix it's orientation before -we feed it back to our output list. That is done by, for example, checking -the orientation of the input polygon and then forcing the triangle to -match that orientation during output. This is what CGAL's Nef Polyhedron -does, you can read it inside /usr/include/CGAL/Nef_polyhedron_3.h. - -Or.... we could actually add an additional 'projection' to the incoming -polygon points so that our triangulation algorithm is guaranteed to -create triangles with the proper orientation in the first place. How? -First, we assume that the triangulation algorithm will always produce -'counterclockwise' triangles in our plain old x-y plane. - -The method is based on the following curious fact: That is, if you take -the points of a polygon, and flip the x,y coordinate of each point, -making y:=x and x:=y, then you essentially get a 'mirror image' of the -original polygon... but the orientation will be flipped. Given a -clockwise polygon, the 'flip' will result in a 'counterclockwise' -polygon mirror-image and vice versa. - -Now, there is a second curious fact that helps us here. In 3d, we are -using the plane equation of ax+by+cz+d=0, where a,b,c determine its -direction. If you notice, there are actually mutiple sets of numbers -a:b:c that will describe the exact same plane. For example the 'ground' -plane, called the XYplane, where z is everywhere 0, has the equation -0x+0y+1z+0=0, simplifying to a solution for x,y,z of z=0 and x,y = any -numbers in your number system. However you can also express this as -0x+0y+-1z=0. The x,y,z solution is the same: z is everywhere 0, x and y -are any number, even though a,b,c are different. We can say that the -plane is 'oriented' differently, if we wish. - -But how can we link that concept to the points on the polygon? Well, if -you generate a plane using the standard plane-equation generation -formula, given three points M,N,P, then you will get a plane equation -with . However if you feed the points in the reverse order, -P,N,M, so that they are now oriented in the opposite order, you will get -a plane equation with the signs flipped. <-a:-b:-c:-d> This means you -can essentially consider that a plane has an 'orientation' based on it's -equation, by looking at the signs of a,b,c relative to some other -quantity. - -This means that you can 'flip' the projection of the input polygon -points so that the projection will match the orientation of the input -plane, thus guaranteeing that the output triangles will be oriented in -the same direction as the input polygon was. In other words, even though -we technically 'lose information' when we project from 3d->2d, we can -actually keep the concept of 'orientation' through the whole -triangulation process, and not have to recalculate the proper -orientation during output. - -For example take two side-squares of a cube and the plane equations -formed by feeding the points in counterclockwise, as if looking in from -outside the cube: - - 0,0,0 0,1,0 0,1,1 0,0,1 <-1:0:0:0> - 1,0,0 1,1,0 1,1,1 1,0,1 <1:0:0:1> - -They are both projected onto the YZ plane. They look the same: - 0,0 1,0 1,1 0,1 - 0,0 1,0 1,1 0,1 - -But the second square plane has opposite orientation, so we flip the x -and y for each point: - 0,0 1,0 1,1 0,1 - 0,0 0,1 1,1 1,0 - -Only now do we feed these two 2-d squares to the tessellation algorithm. -The result is 4 triangles. When de-projected back to 3d, they will have -the appropriate winding that will match that of the original 3d faces. -And the first two triangles will have opposite orientation from the last two. -*/ - -typedef enum { XYPLANE, YZPLANE, XZPLANE, NONE } plane_t; -struct projection_t { - plane_t plane; - bool flip; +struct FaceInfo { + int nesting_level; + bool in_domain() { return nesting_level%2 == 1; } }; -CGAL_Point_2 get_projected_point( CGAL_Point_3 &p3, projection_t projection ) { - NT3 x,y; - if (projection.plane == XYPLANE) { x = p3.x(); y = p3.y(); } - else if (projection.plane == XZPLANE) { x = p3.x(); y = p3.z(); } - else if (projection.plane == YZPLANE) { x = p3.y(); y = p3.z(); } - else if (projection.plane == NONE) { x = 0; y = 0; } - if (projection.flip) return CGAL_Point_2( y,x ); - return CGAL_Point_2( x,y ); -} +typedef CGAL::Triangulation_2_filtered_projection_traits_3 Projection; +typedef CGAL::Triangulation_face_base_with_info_2 Fbb; +typedef CGAL::Triangulation_data_structure_2< + CGAL::Triangulation_vertex_base_2, + CGAL::Constrained_triangulation_face_base_2 > Tds; +typedef CGAL::Constrained_Delaunay_triangulation_2< + Projection, Tds, CGAL::Exact_predicates_tag> CDT; -/* given 2d point, 3d plane, and 3d->2d projection, 'deproject' from - 2d back onto a point on the 3d plane. true on failure, false on success */ -bool deproject( CGAL_Point_2 &p2, projection_t &projection, CGAL_Plane_3 &plane, CGAL_Point_3 &p3 ) + +static void mark_domains(CDT &ct, + CDT::Face_handle start, + int index, + std::list& border) { - NT3 x,y; - CGAL_Line_3 l; - CGAL_Point_3 p; - CGAL_Point_2 pf( p2.x(), p2.y() ); - if (projection.flip) pf = CGAL_Point_2( p2.y(), p2.x() ); - if (projection.plane == XYPLANE) { - p = CGAL_Point_3( pf.x(), pf.y(), 0 ); - l = CGAL_Line_3( p, CGAL_Direction_3(0,0,1) ); - } else if (projection.plane == XZPLANE) { - p = CGAL_Point_3( pf.x(), 0, pf.y() ); - l = CGAL_Line_3( p, CGAL_Direction_3(0,1,0) ); - } else if (projection.plane == YZPLANE) { - p = CGAL_Point_3( 0, pf.x(), pf.y() ); - l = CGAL_Line_3( p, CGAL_Direction_3(1,0,0) ); - } - CGAL::Object obj = CGAL::intersection( l, plane ); - const CGAL_Point_3 *point_test = CGAL::object_cast(&obj); - if (point_test) { - p3 = *point_test; - return false; - } - PRINT("ERROR: deproject failure"); - return true; + if (start->info().nesting_level != -1) return; + std::list queue; + queue.push_back(start); + while (!queue.empty()) { + CDT::Face_handle fh = queue.front(); + queue.pop_front(); + if (fh->info().nesting_level == -1) { + fh->info().nesting_level = index; + for (int i = 0; i < 3; i++) { + CDT::Edge e(fh,i); + CDT::Face_handle n = fh->neighbor(i); + if (n->info().nesting_level == -1) { + if (ct.is_constrained(e)) border.push_back(e); + else queue.push_back(n); + } + } + } + } } -/* this simple criteria guarantees CGALs triangulation algorithm will -terminate (i.e. not lock up and freeze the program) */ -template class DummyCriteria { -public: - typedef double Quality; - class Is_bad { - public: - CGAL::Mesh_2::Face_badness operator()(const Quality) const { - return CGAL::Mesh_2::NOT_BAD; - } - CGAL::Mesh_2::Face_badness operator()(const typename T::Face_handle&, Quality&q) const { - q = 1; - return CGAL::Mesh_2::NOT_BAD; - } - }; - Is_bad is_bad_object() const { return Is_bad(); } -}; -NT3 sign( const NT3 &n ) +//explore set of facets connected with non constrained edges, +//and attribute to each such set a nesting level. +//We start from facets incident to the infinite vertex, with a nesting +//level of 0. Then we recursively consider the non-explored facets incident +//to constrained edges bounding the former set and increase the nesting level by 1. +//Facets in the domain are those with an odd nesting level. +static void mark_domains(CDT& cdt) { - if (n>0) return NT3(1); - if (n<0) return NT3(-1); - return NT3(0); -} - -/* wedge, also related to 'determinant', 'signed parallelogram area', -'side', 'turn', 'winding', '2d portion of cross-product', etc etc. this -function can tell you whether v1 is 'counterclockwise' or 'clockwise' -from v2, based on the sign of the result. when the input Vectors are -formed from three points, A-B and B-C, it can tell you if the path along -the points A->B->C is turning left or right.*/ -NT3 wedge( CGAL_Vector_2 &v1, CGAL_Vector_2 &v2 ) { - return v1.x()*v2.y()-v2.x()*v1.y(); -} - -/* given a point and a possibly non-simple polygon, determine if the -point is inside the polygon or not, using the given winding rule. note -that even_odd is not implemented. */ -typedef enum { NONZERO_WINDING, EVEN_ODD } winding_rule_t; -bool inside(CGAL_Point_2 &p1,std::vector &pgon, winding_rule_t winding_rule) -{ - NT3 winding_sum = NT3(0); - CGAL_Point_2 p2; - CGAL_Ray_2 eastray(p1,CGAL_Direction_2(1,0)); - for (size_t i=0;i(&obj); - if (point_test) { - p2 = *point_test; - CGAL_Vector_2 v1( p1, p2 ); - CGAL_Vector_2 v2( p2, head ); - NT3 this_winding = wedge( v1, v2 ); - winding_sum += sign(this_winding); - } else { - continue; - } - } - if (winding_sum != NT3(0) && winding_rule == NONZERO_WINDING ) return true; - return false; -} - -projection_t find_good_projection( CGAL_Plane_3 &plane ) -{ - projection_t goodproj; - goodproj.plane = NONE; - goodproj.flip = false; - NT3 qxy = plane.a()*plane.a()+plane.b()*plane.b(); - NT3 qyz = plane.b()*plane.b()+plane.c()*plane.c(); - NT3 qxz = plane.a()*plane.a()+plane.c()*plane.c(); - NT3 min = std::min(qxy,std::min(qyz,qxz)); - if (min==qxy) { - goodproj.plane = XYPLANE; - if (sign(plane.c())>0) goodproj.flip = true; - } else if (min==qyz) { - goodproj.plane = YZPLANE; - if (sign(plane.a())>0) goodproj.flip = true; - } else if (min==qxz) { - goodproj.plane = XZPLANE; - if (sign(plane.b())<0) goodproj.flip = true; - } else PRINT("ERROR: failed to find projection"); - return goodproj; + for(CDT::All_faces_iterator it = cdt.all_faces_begin(); it != cdt.all_faces_end(); ++it) { + it->info().nesting_level = -1; + } + std::list border; + mark_domains(cdt, cdt.infinite_face(), 0, border); + while (!border.empty()) { + CDT::Edge e = border.front(); + border.pop_front(); + CDT::Face_handle n = e.first->neighbor(e.second); + if (n->info().nesting_level == -1) { + mark_domains(cdt, n, e.first->info().nesting_level+1, border); + } + } } namespace CGALUtils { -/* given a single near-planar 3d polygon with holes, tessellate into a - sequence of polygons without holes. as of writing, this means conversion - into a sequence of 3d triangles. the given plane should be the same plane - holding the polygon and it's holes. */ - bool tessellate3DFaceWithHoles(std::vector &polygons, - std::vector &triangles, - CGAL_Plane_3 &plane) + /*! + polygons define a polygon, potentially with holes. Each contour + should be added as a separate PolygonK instance. + The tessellator will handle almost planar polygons. + + If the normal is given, we will assume this as the normal vector of the polygon. + Otherwise, we will try to calculate it using Newell's method. + + The resulting triangles is added to the given triangles vector. + */ + bool tessellatePolygonWithHoles(const PolyholeK &polygons, + Polygons &triangles, + const K::Vector_3 *normal) { - if (polygons.size()==1 && polygons[0].size()==3) { + // No polygon. FIXME: Will this ever happen or can we assert here? + if (polygons.empty()) return false; + + // No hole + if (polygons.size() == 1) return tessellatePolygon(polygons.front(), triangles, normal); + + K::Vector_3 normalvec; + if (normal) { + normalvec = *normal; + } + else { + // Calculate best guess at face normal using Newell's method + CGAL::normal_vector_newell_3(polygons.front().begin(), polygons.front().end(), normalvec); + } + + // Pass the normal vector to the (undocumented) + // CGAL::Triangulation_2_filtered_projection_traits_3. This + // trait deals with projection from 3D to 2D using the normal + // vector as a hint, and allows for near-planar polygons to be passed to + // the Constrained Delaunay Triangulator. + Projection actualProjection(normalvec); + CDT cdt(actualProjection); + BOOST_FOREACH(const PolygonK &poly, polygons) { + for (size_t i=0;iinfo().in_domain()) { + Polygon tri; + for (int i=0;i<3;i++) { + Vertex3K v = cdt.triangle(fit)[i]; + tri.push_back(Vector3d(v.x(), v.y(), v.z())); + } + triangles.push_back(tri); + } + } + + return false; + } + + bool tessellatePolygon(const PolygonK &polygon, + Polygons &triangles, + const K::Vector_3 *normal) + { + if (polygon.size() == 3) { PRINTD("input polygon has 3 points. shortcut tessellation."); - CGAL_Polygon_3 t; - t.push_back(polygons[0][2]); - t.push_back(polygons[0][1]); - t.push_back(polygons[0][0]); - triangles.push_back( t ); + Polygon t; + t.push_back(Vector3d(polygon[0].x(), polygon[0].y(), polygon[0].z())); + t.push_back(Vector3d(polygon[1].x(), polygon[1].y(), polygon[1].z())); + t.push_back(Vector3d(polygon[2].x(), polygon[2].y(), polygon[2].z())); + triangles.push_back(t); return false; } - bool err = false; - CDT cdt; - std::map vertmap; - PRINTD("finding good projection"); - projection_t goodproj = find_good_projection( plane ); - - PRINTDB("plane %s",plane ); - PRINTDB("proj: %i %i",goodproj.plane % goodproj.flip); - PRINTD("Inserting points and edges into Constrained Delaunay Triangulation"); - std::vector< std::vector > polygons2d; - for (size_t i=0;i vhandles; - std::vector polygon2d; - for (size_t j=0;j list_of_seeds; - for (size_t i=1;i &pgon = polygons2d[i]; - for (size_t j=0;j::iterator li = list_of_seeds.begin(); - for (;li!=list_of_seeds.end();li++) { - //PRINTB("seed %s",*li); - double x = CGAL::to_double( li->x() ); - double y = CGAL::to_double( li->y() ); - PRINTDB("seed %f,%f",x%y); + + // Pass the normal vector to the (undocumented) + // CGAL::Triangulation_2_filtered_projection_traits_3. This + // trait deals with projection from 3D to 2D using the normal + // vector as a hint, and allows for near-planar polygons to be passed to + // the Constrained Delaunay Triangulator. + Projection actualProjection(normalvec); + CDT cdt(actualProjection); + for (size_t i=0;i() ); - - PRINTD("meshing done"); - // this fails because it calls is_simple and is_simple fails on many - // Nef Polyhedron faces - //CGAL::Orientation original_orientation = - // CGAL::orientation_2( orienpgon.begin(), orienpgon.end() ); + + //Mark facets that are inside the domain bounded by the polygon + mark_domains(cdt); + // Iterate over the resulting faces CDT::Finite_faces_iterator fit; - for( fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++ ) - { - if(fit->is_in_domain()) { - CDTPoint p1 = cdt.triangle( fit )[0]; - CDTPoint p2 = cdt.triangle( fit )[1]; - CDTPoint p3 = cdt.triangle( fit )[2]; - CGAL_Point_3 cp1,cp2,cp3; - CGAL_Polygon_3 pgon; - if (vertmap.count(p1)) cp1 = vertmap[p1]; - else err = deproject( p1, goodproj, plane, cp1 ); - if (vertmap.count(p2)) cp2 = vertmap[p2]; - else err = deproject( p2, goodproj, plane, cp2 ); - if (vertmap.count(p3)) cp3 = vertmap[p3]; - else err = deproject( p3, goodproj, plane, cp3 ); - if (err) PRINT("WARNING: 2d->3d deprojection failure"); - pgon.push_back( cp1 ); - pgon.push_back( cp2 ); - pgon.push_back( cp3 ); - triangles.push_back( pgon ); + for (fit=cdt.finite_faces_begin(); fit!=cdt.finite_faces_end(); fit++) { + if (fit->info().in_domain()) { + Polygon tri; + for (int i=0;i<3;i++) { + K::Point_3 v = cdt.triangle(fit)[i]; + tri.push_back(Vector3d(v.x(), v.y(), v.z())); + } + triangles.push_back(tri); } } - PRINTDB("built %i triangles",triangles.size()); - return err; + return false; } -}; + +}; // namespace CGALUtils + diff --git a/src/cgalutils.cc b/src/cgalutils.cc index dd156720..971f12f7 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -21,252 +21,19 @@ #include #include -namespace /* anonymous */ { - template - Result vector_convert(V const& v) { - return Result(CGAL::to_double(v[0]),CGAL::to_double(v[1]),CGAL::to_double(v[2])); - } - -#undef GEN_SURFACE_DEBUG - - namespace Eigen { +namespace Eigen { size_t hash_value(Vector3d const &v) { size_t seed = 0; for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]); return seed; } +} + +namespace /* anonymous */ { + template + Result vector_convert(V const& v) { + return Result(CGAL::to_double(v[0]),CGAL::to_double(v[1]),CGAL::to_double(v[2])); } - - class CGAL_Build_PolySet : public CGAL::Modifier_base - { - public: - typedef CGAL_Polybuilder::Point_3 CGALPoint; - - const PolySet &ps; - CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } - -/* - Using Grid here is important for performance reasons. See following model. - If we don't grid the geometry before converting to a Nef Polyhedron, the quads - in the cylinders to tessellated into triangles since floating point - incertainty causes the faces to not be 100% planar. The incertainty is exaggerated - by the transform. This wasn't a problem earlier since we used Nef for everything, - but optimizations since then has made us keep it in floating point space longer. - - minkowski() { - cube([200, 50, 7], center = true); - rotate([90,0,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); - rotate([0,90,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true); - } -*/ -#if 1 // Use Grid - void operator()(CGAL_HDS& hds) { - CGAL_Polybuilder B(hds, true); - - std::vector vertices; - Grid3d grid(GRID_FINE); - std::vector indices(3); - - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { - BOOST_REVERSE_FOREACH(Vector3d v, p) { - if (!grid.has(v[0], v[1], v[2])) { - // align v to the grid; the CGALPoint will receive the aligned vertex - grid.align(v[0], v[1], v[2]) = vertices.size(); - vertices.push_back(CGALPoint(v[0], v[1], v[2])); - } - } - } - -#ifdef GEN_SURFACE_DEBUG - printf("polyhedron(faces=["); - int pidx = 0; -#endif - B.begin_surface(vertices.size(), ps.polygons.size()); - BOOST_FOREACH(const CGALPoint &p, vertices) { - B.add_vertex(p); - } - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { -#ifdef GEN_SURFACE_DEBUG - if (pidx++ > 0) printf(","); -#endif - indices.clear(); - BOOST_FOREACH(const Vector3d &v, p) { - indices.push_back(grid.data(v[0], v[1], v[2])); - } - - // We perform this test since there is a bug in CGAL's - // Polyhedron_incremental_builder_3::test_facet() which - // fails to detect duplicate indices - bool err = false; - for (std::size_t i = 0; i < indices.size(); ++i) { - // check if vertex indices[i] is already in the sequence [0..i-1] - for (std::size_t k = 0; k < i && !err; ++k) { - if (indices[k] == indices[i]) { - err = true; - break; - } - } - } - if (!err && B.test_facet(indices.begin(), indices.end())) { - B.add_facet(indices.begin(), indices.end()); - } -#ifdef GEN_SURFACE_DEBUG - printf("["); - int fidx = 0; - BOOST_FOREACH(size_t i, indices) { - if (fidx++ > 0) printf(","); - printf("%ld", i); - } - printf("]"); -#endif - } - B.end_surface(); -#ifdef GEN_SURFACE_DEBUG - printf("],\n"); -#endif -#ifdef GEN_SURFACE_DEBUG - printf("points=["); - for (int i=0;i 0) printf(","); - const CGALPoint &p = vertices[i]; - printf("[%g,%g,%g]", CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z())); - } - printf("]);\n"); -#endif - } -#else // Don't use Grid - void operator()(CGAL_HDS& hds) - { - CGAL_Polybuilder B(hds, true); - typedef boost::tuple BuilderVertex; - Reindexer vertices; - std::vector indices(3); - - // Estimating same # of vertices as polygons (very rough) - B.begin_surface(ps.polygons.size(), ps.polygons.size()); - int pidx = 0; -#ifdef GEN_SURFACE_DEBUG - printf("polyhedron(faces=["); -#endif - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { -#ifdef GEN_SURFACE_DEBUG - if (pidx++ > 0) printf(","); -#endif - indices.clear(); - BOOST_REVERSE_FOREACH(const Vector3d &v, p) { - size_t s = vertices.size(); - size_t idx = vertices.lookup(v); - // If we added a vertex, also add it to the CGAL builder - if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2])); - indices.push_back(idx); - } - // We perform this test since there is a bug in CGAL's - // Polyhedron_incremental_builder_3::test_facet() which - // fails to detect duplicate indices - bool err = false; - for (std::size_t i = 0; i < indices.size(); ++i) { - // check if vertex indices[i] is already in the sequence [0..i-1] - for (std::size_t k = 0; k < i && !err; ++k) { - if (indices[k] == indices[i]) { - err = true; - break; - } - } - } - if (!err && B.test_facet(indices.begin(), indices.end())) { - B.add_facet(indices.begin(), indices.end()); -#ifdef GEN_SURFACE_DEBUG - printf("["); - int fidx = 0; - BOOST_FOREACH(size_t i, indices) { - if (fidx++ > 0) printf(","); - printf("%ld", i); - } - printf("]"); -#endif - } - } - B.end_surface(); -#ifdef GEN_SURFACE_DEBUG - printf("],\n"); - - printf("points=["); - for (int vidx=0;vidx 0) printf(","); - const Vector3d &v = vertices.getArray()[vidx]; - printf("[%g,%g,%g]", v[0], v[1], v[2]); - } - printf("]);\n"); -#endif - } -#endif - }; - - // This code is from CGAL/demo/Polyhedron/Scene_nef_polyhedron_item.cpp - // quick hacks to convert polyhedra from exact to inexact and vice-versa - template - struct Copy_polyhedron_to - : public CGAL::Modifier_base - { - Copy_polyhedron_to(const Polyhedron_input& in_poly) - : in_poly(in_poly) {} - - void operator()(typename Polyhedron_output::HalfedgeDS& out_hds) - { - typedef typename Polyhedron_output::HalfedgeDS Output_HDS; - - CGAL::Polyhedron_incremental_builder_3 builder(out_hds); - - typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator; - typedef typename Polyhedron_input::Facet_const_iterator Facet_const_iterator; - typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC; - - builder.begin_surface(in_poly.size_of_vertices(), - in_poly.size_of_facets(), - in_poly.size_of_halfedges()); - - for(Vertex_const_iterator - vi = in_poly.vertices_begin(), end = in_poly.vertices_end(); - vi != end ; ++vi) - { - typename Polyhedron_output::Point_3 p(::CGAL::to_double( vi->point().x()), - ::CGAL::to_double( vi->point().y()), - ::CGAL::to_double( vi->point().z())); - builder.add_vertex(p); - } - - typedef CGAL::Inverse_index Index; - Index index( in_poly.vertices_begin(), in_poly.vertices_end()); - - for(Facet_const_iterator - fi = in_poly.facets_begin(), end = in_poly.facets_end(); - fi != end; ++fi) - { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - // std::size_t n = circulator_size( hc); - // CGAL_assertion( n >= 3); - builder.begin_facet (); - do { - builder.add_vertex_to_facet(index[hc->vertex()]); - ++hc; - } while( hc != hc_end); - builder.end_facet(); - } - builder.end_surface(); - } // end operator()(..) - private: - const Polyhedron_input& in_poly; - }; // end Copy_polyhedron_to<> - - template - void copy_to(const Poly_A& poly_a, Poly_B& poly_b) - { - Copy_polyhedron_to modifier(poly_a); - poly_b.delegate(modifier); - } - } static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) @@ -274,7 +41,13 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) if (ps.isEmpty()) return new CGAL_Nef_polyhedron(); assert(ps.getDimension() == 3); - if (ps.is_convex()) { + // Since is_convex doesn't work well with non-planar faces, + // we tessellate the polyset before checking. + PolySet psq(ps); + psq.quantizeVertices(); + PolySet ps_tri(3, psq.convexValue()); + PolysetUtils::tessellate_faces(psq, ps_tri); + if (ps_tri.is_convex()) { typedef CGAL::Exact_predicates_inexact_constructions_kernel K; // Collect point cloud std::set points; @@ -289,8 +62,8 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) // Apply hull CGAL::Polyhedron_3 r; CGAL::convex_hull_3(points.begin(), points.end(), r); - CGAL::Polyhedron_3 r_exact; - copy_to(r,r_exact); + CGAL_Polyhedron r_exact; + CGALUtils::copyPolyhedron(r, r_exact); return new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(r_exact)); } @@ -300,32 +73,33 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) try { CGAL_Polyhedron P; bool err = CGALUtils::createPolyhedronFromPolySet(ps, P); - // if (!err) { - // PRINTB("Polyhedron is closed: %d", P.is_closed()); - // PRINTB("Polyhedron is valid: %d", P.is_valid(false, 0)); - // } + if (!err) { + PRINTDB("Polyhedron is closed: %d", P.is_closed()); + PRINTDB("Polyhedron is valid: %d", P.is_valid(false, 0)); + } if (!err) N = new CGAL_Nef_polyhedron3(P); } catch (const CGAL::Assertion_exception &e) { - if (std::string(e.what()).find("Plane_constructor")!=std::string::npos) { - if (std::string(e.what()).find("has_on")!=std::string::npos) { + if (std::string(e.what()).find("Plane_constructor")!=std::string::npos && + std::string(e.what()).find("has_on")!=std::string::npos) { PRINT("PolySet has nonplanar faces. Attempting alternate construction"); plane_error=true; - } } else { - PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); + PRINTB("ERROR: CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); } } if (plane_error) try { - PolySet ps2(3); CGAL_Polyhedron P; - PolysetUtils::tessellate_faces(ps, ps2); - bool err = CGALUtils::createPolyhedronFromPolySet(ps2,P); + bool err = CGALUtils::createPolyhedronFromPolySet(ps_tri, P); + if (!err) { + PRINTDB("Polyhedron is closed: %d", P.is_closed()); + PRINTDB("Polyhedron is valid: %d", P.is_valid(false, 0)); + } if (!err) N = new CGAL_Nef_polyhedron3(P); } catch (const CGAL::Assertion_exception &e) { - PRINTB("Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); + PRINTB("ERROR: Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); } CGAL::set_error_behaviour(old_behaviour); return new CGAL_Nef_polyhedron(N); @@ -339,22 +113,6 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolygon2d(const Polygon2d &po namespace CGALUtils { - bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p) - { - bool err = false; - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { - CGAL_Build_PolySet builder(ps); - p.delegate(builder); - } - catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGALUtils::createPolyhedronFromPolySet: %s", e.what()); - err = true; - } - CGAL::set_error_behaviour(old_behaviour); - return err; - } - bool applyHull(const Geometry::ChildList &children, PolySet &result) { typedef CGAL::Exact_predicates_inexact_constructions_kernel K; @@ -373,7 +131,7 @@ namespace CGALUtils { } else { const PolySet *ps = dynamic_cast(chgeom.get()); if (ps) { - BOOST_FOREACH(const PolySet::Polygon &p, ps->polygons) { + BOOST_FOREACH(const Polygon &p, ps->polygons) { BOOST_FOREACH(const Vector3d &v, p) { points.insert(K::Point_3(v[0], v[1], v[2])); } @@ -385,15 +143,20 @@ namespace CGALUtils { if (points.size() <= 3) return false; // Apply hull + bool success = false; if (points.size() >= 4) { - CGAL::Polyhedron_3 r; - CGAL::convex_hull_3(points.begin(), points.end(), r); - if (!createPolySetFromPolyhedron(r, result)) - return true; - return false; - } else { - return false; + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + CGAL::Polyhedron_3 r; + CGAL::convex_hull_3(points.begin(), points.end(), r); + success = !createPolySetFromPolyhedron(r, result); + } + catch (const CGAL::Assertion_exception &e) { + PRINTB("ERROR: CGAL error in applyHull(): %s", e.what()); + } + CGAL::set_error_behaviour(old_behaviour); } + return success; } template @@ -461,7 +224,7 @@ namespace CGALUtils { if ((ps && ps->is_convex()) || (!ps && is_weakly_convex(poly))) { - PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef") ); + PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef")); P[i].push_back(poly); } else { CGAL_Nef_polyhedron3 decomposed_nef; @@ -469,7 +232,7 @@ namespace CGALUtils { if (ps) { PRINTDB("Minkowski: child %d is nonconvex PolySet, transforming to Nef and decomposing...", i); CGAL_Nef_polyhedron *p = createNefPolyhedronFromGeometry(*ps); - decomposed_nef = *p->p3; + if (!p->isEmpty()) decomposed_nef = *p->p3; delete p; } else { PRINTDB("Minkowski: child %d is nonconvex Nef, decomposing...",i); @@ -480,7 +243,7 @@ namespace CGALUtils { // the first volume is the outer volume, which ignored in the decomposition CGAL_Nef_polyhedron3::Volume_const_iterator ci = ++decomposed_nef.volumes_begin(); - for( ; ci != decomposed_nef.volumes_end(); ++ci) { + for(; ci != decomposed_nef.volumes_end(); ++ci) { if(ci->mark()) { CGAL_Polyhedron poly; decomposed_nef.convert_inner_shell_to_polyhedron(ci->shells_begin(), poly); @@ -605,14 +368,16 @@ namespace CGALUtils { fake_children.push_back(std::make_pair((const AbstractNode*)NULL, shared_ptr(createNefPolyhedronFromGeometry(ps)))); } - CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron; - CGALUtils::applyOperator(fake_children, *N, OPENSCAD_UNION); + CGAL_Nef_polyhedron *N = CGALUtils::applyOperator(fake_children, OPENSCAD_UNION); + // FIXME: This hould really never throw. + // Assert once we figured out what went wrong with issue #1069? + if (!N) throw 0; t.stop(); PRINTDB("Minkowski: Union done: %f s",t.time()); t.reset(); operands[0] = N; } else { - return NULL; + operands[0] = new CGAL_Nef_polyhedron(); } } @@ -625,65 +390,57 @@ namespace CGALUtils { // If anything throws we simply fall back to Nef Minkowski PRINTD("Minkowski: Falling back to Nef Minkowski"); - CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron; - applyOperator(children, *N, OPENSCAD_MINKOWSKI); + CGAL_Nef_polyhedron *N = applyOperator(children, OPENSCAD_MINKOWSKI); return N; } } /*! - Applies op to all children and stores the result in dest. + Applies op to all children and returns the result. The child list should be guaranteed to contain non-NULL 3D or empty Geometry objects */ - void applyOperator(const Geometry::ChildList &children, CGAL_Nef_polyhedron &dest, OpenSCADOperator op) + CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op) { - // Speeds up n-ary union operations significantly - CGAL::Nef_nary_union_3 nary_union; - int nary_union_num_inserted = 0; CGAL_Nef_polyhedron *N = NULL; - - BOOST_FOREACH(const Geometry::ChildItem &item, children) { - const shared_ptr &chgeom = item.second; - shared_ptr chN = - dynamic_pointer_cast(chgeom); - if (!chN) { - const PolySet *chps = dynamic_cast(chgeom.get()); - if (chps) chN.reset(createNefPolyhedronFromGeometry(*chps)); - } - - if (op == OPENSCAD_UNION) { - if (!chN->isEmpty()) { - // nary_union.add_polyhedron() can issue assertion errors: - // https://github.com/openscad/openscad/issues/802 - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + // Speeds up n-ary union operations significantly + CGAL::Nef_nary_union_3 nary_union; + int nary_union_num_inserted = 0; + + BOOST_FOREACH(const Geometry::ChildItem &item, children) { + const shared_ptr &chgeom = item.second; + shared_ptr chN = + dynamic_pointer_cast(chgeom); + if (!chN) { + const PolySet *chps = dynamic_cast(chgeom.get()); + if (chps) chN.reset(createNefPolyhedronFromGeometry(*chps)); + } + + if (op == OPENSCAD_UNION) { + if (!chN->isEmpty()) { + // nary_union.add_polyhedron() can issue assertion errors: + // https://github.com/openscad/openscad/issues/802 nary_union.add_polyhedron(*chN->p3); nary_union_num_inserted++; } - catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in CGALUtils::applyBinaryOperator union: %s", e.what()); - } - CGAL::set_error_behaviour(old_behaviour); + continue; } - continue; - } - // Initialize N with first expected geometric object - if (!N) { - N = new CGAL_Nef_polyhedron(*chN); - continue; - } - - // Intersecting something with nothing results in nothing - if (chN->isEmpty()) { - if (op == OPENSCAD_INTERSECTION) *N = *chN; - continue; - } - - // empty op => empty - if (N->isEmpty()) continue; - - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { + // Initialize N with first expected geometric object + if (!N) { + N = new CGAL_Nef_polyhedron(*chN); + continue; + } + + // Intersecting something with nothing results in nothing + if (chN->isEmpty()) { + if (op == OPENSCAD_INTERSECTION) *N = *chN; + continue; + } + + // empty op => empty + if (N->isEmpty()) continue; + switch (op) { case OPENSCAD_INTERSECTION: *N *= *chN; @@ -697,38 +454,28 @@ namespace CGALUtils { default: PRINTB("ERROR: Unsupported CGAL operator: %d", op); } + item.first->progress_report(); } - catch (const CGAL::Failure_exception &e) { - // union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad - std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN"; - PRINTB("CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); - - // Errors can result in corrupt polyhedrons, so put back the old one - *N = *chN; - } - CGAL::set_error_behaviour(old_behaviour); - item.first->progress_report(); - } - - if (op == OPENSCAD_UNION && nary_union_num_inserted > 0) { - CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - try { + if (op == OPENSCAD_UNION && nary_union_num_inserted > 0) { N = new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(nary_union.get_union())); - - } catch (const CGAL::Failure_exception &e) { - std::string opstr = "union"; - PRINTB("CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); } - CGAL::set_error_behaviour(old_behaviour); } - if (N) dest = *N; + // union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad + catch (const CGAL::Failure_exception &e) { + std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN"; + PRINTB("ERROR: CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); + } + CGAL::set_error_behaviour(old_behaviour); + return N; } /*! Modifies target by applying op to target and src: target = target [op] src */ +//FIXME: Old, can be removed: +#if 0 void applyBinaryOperator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op) { if (target.getDimension() != 2 && target.getDimension() != 3) { @@ -767,13 +514,14 @@ namespace CGALUtils { catch (const CGAL::Failure_exception &e) { // union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad std::string opstr = op == OPENSCAD_UNION ? "union" : op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN"; - PRINTB("CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); + PRINTB("ERROR: CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); // Errors can result in corrupt polyhedrons, so put back the old one target = src; } CGAL::set_error_behaviour(old_behaviour); } +#endif static void add_outline_to_poly(CGAL_Nef_polyhedron2::Explorer &explorer, CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator circ, @@ -844,17 +592,17 @@ namespace CGALUtils { // dont use z of 0. there are bugs in CGAL. double inf = 1e8; double eps = 0.001; - CGAL_Point_3 minpt( -inf, -inf, -eps ); - CGAL_Point_3 maxpt( inf, inf, eps ); - CGAL_Iso_cuboid_3 bigcuboid( minpt, maxpt ); - for ( int i=0;i<8;i++ ) pts.push_back( bigcuboid.vertex(i) ); + CGAL_Point_3 minpt(-inf, -inf, -eps); + CGAL_Point_3 maxpt( inf, inf, eps); + CGAL_Iso_cuboid_3 bigcuboid(minpt, maxpt); + for (int i=0;i<8;i++) pts.push_back(bigcuboid.vertex(i)); CGAL_Polyhedron bigbox; CGAL::convex_hull_3(pts.begin(), pts.end(), bigbox); - CGAL_Nef_polyhedron3 nef_bigbox( bigbox ); + CGAL_Nef_polyhedron3 nef_bigbox(bigbox); newN.p3.reset(new CGAL_Nef_polyhedron3(nef_bigbox.intersection(*N.p3))); } catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in CGALUtils::project during bigbox intersection: %s", e.what()); + PRINTB("ERROR: CGAL error in CGALUtils::project during bigbox intersection: %s", e.what()); } } @@ -864,25 +612,25 @@ namespace CGALUtils { return poly; } - PRINTDB("%s",OpenSCAD::svg_header( 480, 100000 )); + PRINTDB("%s",OpenSCAD::svg_header(480, 100000)); try { ZRemover zremover; CGAL_Nef_polyhedron3::Volume_const_iterator i; CGAL_Nef_polyhedron3::Shell_entry_const_iterator j; CGAL_Nef_polyhedron3::SFace_const_handle sface_handle; - for ( i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i ) { + for (i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i) { PRINTDB("",i->mark()); - for ( j = i->shells_begin(); j != i->shells_end(); ++j ) { + for (j = i->shells_begin(); j != i->shells_end(); ++j) { PRINTDB(""); } PRINTD(""); } poly = convertToPolygon2d(*zremover.output_nefpoly2d); } catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in CGALUtils::project while flattening: %s", e.what()); + PRINTB("ERROR: CGAL error in CGALUtils::project while flattening: %s", e.what()); } PRINTD(""); @@ -909,7 +657,7 @@ namespace CGALUtils { // can be optimized by rewriting bounding_box to accept vertices CGAL_forall_vertices(vi, N) points.push_back(vi->point()); - if (points.size()) result = CGAL::bounding_box( points.begin(), points.end() ); + if (points.size()) result = CGAL::bounding_box(points.begin(), points.end()); return result; } @@ -934,6 +682,13 @@ namespace CGALUtils { }; + /*! + Check if all faces of a polyset is within 0.1 degree of being convex. + + NB! This function can give false positives if the polyset contains + non-planar faces. To be on the safe side, consider passing a tessellated polyset. + See issue #1061. + */ bool is_approximately_convex(const PolySet &ps) { const double angle_threshold = cos(.1/180*M_PI); // .1° @@ -1016,34 +771,6 @@ namespace CGALUtils { return explored_facets.size() == ps.polygons.size(); } - template - bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps) - { - bool err = false; - typedef typename Polyhedron::Vertex Vertex; - typedef typename Polyhedron::Vertex_const_iterator VCI; - typedef typename Polyhedron::Facet_const_iterator FCI; - typedef typename Polyhedron::Halfedge_around_facet_const_circulator HFCC; - - for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - ps.append_poly(); - do { - Vertex const& v = *((hc++)->vertex()); - double x = CGAL::to_double(v.point().x()); - double y = CGAL::to_double(v.point().y()); - double z = CGAL::to_double(v.point().z()); - ps.append_vertex(x, y, z); - } while (hc != hc_end); - } - return err; - } - - template bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps); - template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); - template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 &p, PolySet &ps); - template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3 > &p, PolySet &ps); /* Create a PolySet from a Nef Polyhedron 3. return false on success, @@ -1052,25 +779,26 @@ namespace CGALUtils { formats) do not allow for holes in their faces. The function documents the method used to deal with this */ +#if 0 bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps) { bool err = false; CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti; - CGAL_forall_halffacets( hfaceti, N ) { - CGAL::Plane_3 plane( hfaceti->plane() ); - std::vector polygons; + CGAL_forall_halffacets(hfaceti, N) { + CGAL::Plane_3 plane(hfaceti->plane()); + std::vector polyholes; // the 0-mark-volume is the 'empty' volume of space. skip it. if (hfaceti->incident_volume()->mark()) continue; CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei; - CGAL_forall_facet_cycles_of( cyclei, hfaceti ) { + CGAL_forall_facet_cycles_of(cyclei, hfaceti) { CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei); CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1); CGAL_Polygon_3 polygon; - CGAL_For_all( c1, c2 ) { + CGAL_For_all(c1, c2) { CGAL_Point_3 p = c1->source()->center_vertex()->point(); - polygon.push_back( p ); + polygon.push_back(p); } - polygons.push_back( polygon ); + polyholes.push_back(polygon); } /* at this stage, we have a sequence of polygons. the first @@ -1079,28 +807,125 @@ namespace CGALUtils { options here to get rid of the holes. we choose to go ahead and let the tessellater deal with the holes, and then just output the resulting 3d triangles*/ + + CGAL::Vector_3 nvec = plane.orthogonal_vector(); + K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z())); std::vector triangles; - bool err = CGALUtils::tessellate3DFaceWithHoles(polygons, triangles, plane); + bool err = CGALUtils::tessellate3DFaceWithHoles(polyholes, triangles, plane); if (!err) { - for (size_t i=0;i=0;j--) { - double x1,y1,z1; - x1 = CGAL::to_double(triangles[i][j].x()); - y1 = CGAL::to_double(triangles[i][j].y()); - z1 = CGAL::to_double(triangles[i][j].z()); - ps.append_vertex(x1,y1,z1); - } + ps.append_vertex(CGAL::to_double(p[2].x()), CGAL::to_double(p[2].y()), CGAL::to_double(p[2].z())); + ps.append_vertex(CGAL::to_double(p[1].x()), CGAL::to_double(p[1].y()), CGAL::to_double(p[1].z())); + ps.append_vertex(CGAL::to_double(p[0].x()), CGAL::to_double(p[0].y()), CGAL::to_double(p[0].z())); } } } return err; } +#endif +#if 0 + bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps) + { + bool err = false; + CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti; + CGAL_forall_halffacets(hfaceti, N) { + CGAL::Plane_3 plane(hfaceti->plane()); + std::vector polyholes; + // the 0-mark-volume is the 'empty' volume of space. skip it. + if (hfaceti->incident_volume()->mark()) continue; + CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei; + CGAL_forall_facet_cycles_of(cyclei, hfaceti) { + CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei); + CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1); + CGAL_Polygon_3 polygon; + CGAL_For_all(c1, c2) { + CGAL_Point_3 p = c1->source()->center_vertex()->point(); + polygon.push_back(p); + } + polyholes.push_back(polygon); + } + /* at this stage, we have a sequence of polygons. the first + is the "outside edge' or 'body' or 'border', and the rest of the + polygons are 'holes' within the first. there are several + options here to get rid of the holes. we choose to go ahead + and let the tessellater deal with the holes, and then + just output the resulting 3d triangles*/ + + CGAL::Vector_3 nvec = plane.orthogonal_vector(); + K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z())); + std::vector triangles; + bool err = CGALUtils::tessellate3DFaceWithHolesNew(polyholes, triangles, plane); + if (!err) { + BOOST_FOREACH(const Polygon &p, triangles) { + if (p.size() != 3) { + PRINT("WARNING: triangle doesn't have 3 points. skipping"); + continue; + } + ps.append_poly(); + ps.append_vertex(p[0].x(), p[0].y(), p[0].z()); + ps.append_vertex(p[1].x(), p[1].y(), p[1].z()); + ps.append_vertex(p[2].x(), p[2].y(), p[2].z()); + } + } + } + return err; + } +#endif +#if 1 + bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps) + { + bool err = false; + CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti; + CGAL_forall_halffacets(hfaceti, N) { + CGAL::Plane_3 plane(hfaceti->plane()); + PolyholeK polyholes; + // the 0-mark-volume is the 'empty' volume of space. skip it. + if (hfaceti->incident_volume()->mark()) continue; + CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei; + CGAL_forall_facet_cycles_of(cyclei, hfaceti) { + CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei); + CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1); + PolygonK polygon; + CGAL_For_all(c1, c2) { + CGAL_Point_3 p = c1->source()->center_vertex()->point(); + polygon.push_back(Vertex3K(CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z()))); + } + polyholes.push_back(polygon); + } + + /* at this stage, we have a sequence of polygons. the first + is the "outside edge' or 'body' or 'border', and the rest of the + polygons are 'holes' within the first. there are several + options here to get rid of the holes. we choose to go ahead + and let the tessellater deal with the holes, and then + just output the resulting 3d triangles*/ + + CGAL::Vector_3 nvec = plane.orthogonal_vector(); + K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z())); + std::vector triangles; + bool err = CGALUtils::tessellatePolygonWithHoles(polyholes, triangles, &normal); + if (!err) { + BOOST_FOREACH(const Polygon &p, triangles) { + if (p.size() != 3) { + PRINT("WARNING: triangle doesn't have 3 points. skipping"); + continue; + } + ps.append_poly(); + ps.append_vertex(p[0].x(), p[0].y(), p[0].z()); + ps.append_vertex(p[1].x(), p[1].y(), p[1].z()); + ps.append_vertex(p[2].x(), p[2].y(), p[2].z()); + } + } + } + return err; + } +#endif CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom) { const PolySet *ps = dynamic_cast(&geom); @@ -1117,10 +942,10 @@ namespace CGALUtils { }; // namespace CGALUtils -void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) +void ZRemover::visit(CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet) { PRINTDB(" ",hfacet->mark()); - if ( hfacet->plane().orthogonal_direction() != this->up ) { + if (hfacet->plane().orthogonal_direction() != this->up) { PRINTD(" "); PRINTD(" "); return; @@ -1130,36 +955,36 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci; int contour_counter = 0; - CGAL_forall_facet_cycles_of( fci, hfacet ) { - if ( fci.is_shalfedge() ) { + CGAL_forall_facet_cycles_of(fci, hfacet) { + if (fci.is_shalfedge()) { PRINTD(" "); CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1); std::vector contour; - CGAL_For_all( c1, cend ) { + CGAL_For_all(c1, cend) { CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); CGAL_Nef_polyhedron2::Explorer::Point point2d(CGAL::to_double(point3d.x()), CGAL::to_double(point3d.y())); - contour.push_back( point2d ); + contour.push_back(point2d); } if (contour.size()==0) continue; if (OpenSCAD::debug!="") - PRINTDB(" ", CGAL::is_simple_2( contour.begin(), contour.end() ) ); + PRINTDB(" ", CGAL::is_simple_2(contour.begin(), contour.end())); - tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) ); + tmpnef2d.reset(new CGAL_Nef_polyhedron2(contour.begin(), contour.end(), boundary)); - if ( contour_counter == 0 ) { - PRINTDB(" ", contour.size() ); + if (contour_counter == 0) { + PRINTDB(" ", contour.size()); *(output_nefpoly2d) += *(tmpnef2d); } else { - PRINTDB(" ", contour.size() ); + PRINTDB(" ", contour.size()); *(output_nefpoly2d) *= *(tmpnef2d); } /*log << "\n\n" - << OpenSCAD::dump_svg( *tmpnef2d ) << "\n" + << OpenSCAD::dump_svg(*tmpnef2d) << "\n" << "\n\n" - << OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n";*/ + << OpenSCAD::dump_svg(*output_nefpoly2d) << "\n";*/ contour_counter++; } else { diff --git a/src/cgalutils.h b/src/cgalutils.h index f660f5aa..c8eb69db 100644 --- a/src/cgalutils.h +++ b/src/cgalutils.h @@ -5,22 +5,40 @@ #include "CGAL_Nef_polyhedron.h" #include "enums.h" +#include +typedef CGAL::Epick K; +typedef CGAL::Point_3 Vertex3K; +typedef std::vector PolygonK; +typedef std::vector PolyholeK; + namespace CGALUtils { bool applyHull(const Geometry::ChildList &children, PolySet &P); - void applyOperator(const Geometry::ChildList &children, CGAL_Nef_polyhedron &dest, OpenSCADOperator op); - void applyBinaryOperator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op); + CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op); + //FIXME: Old, can be removed: + //void applyBinaryOperator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op); Polygon2d *project(const CGAL_Nef_polyhedron &N, bool cut); CGAL_Iso_cuboid_3 boundingBox(const CGAL_Nef_polyhedron3 &N); bool is_approximately_convex(const PolySet &ps); Geometry const* applyMinkowski(const Geometry::ChildList &children); - CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const class Geometry &geom); template bool createPolySetFromPolyhedron(const Polyhedron &p, PolySet &ps); - bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps); - bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p); + template bool createPolyhedronFromPolySet(const PolySet &ps, Polyhedron &p); + template + void copyPolyhedron(const Polyhedron_A &poly_a, Polyhedron_B &poly_b); - typedef std::vector CGAL_Polygon_3; - bool tessellate3DFaceWithHoles(std::vector &polygons, + CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const class Geometry &geom); + bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps); + + bool tessellatePolygon(const PolygonK &polygon, + Polygons &triangles, + const K::Vector_3 *normal = NULL); + bool tessellatePolygonWithHoles(const PolyholeK &polygons, + Polygons &triangles, + const K::Vector_3 *normal = NULL); + bool tessellate3DFaceWithHolesNew(std::vector &polygons, + Polygons &triangles, + CGAL::Plane_3 &plane); + bool tessellate3DFaceWithHoles(std::vector &polygons, std::vector &triangles, CGAL::Plane_3 &plane); }; diff --git a/src/clipper-utils.cc b/src/clipper-utils.cc index f960b530..7b1c1463 100644 --- a/src/clipper-utils.cc +++ b/src/clipper-utils.cc @@ -81,12 +81,17 @@ namespace ClipperUtils { return result; } + /*! + Apply the clipper operator to the given paths. + + May return an empty Polygon2d, but will not return NULL. + */ Polygon2d *apply(const std::vector &pathsvector, ClipperLib::ClipType clipType) { ClipperLib::Clipper clipper; - if (clipType == ClipperLib::ctIntersection && pathsvector.size() > 2) { + if (clipType == ClipperLib::ctIntersection && pathsvector.size() >= 2) { // intersection operations must be split into a sequence of binary operations ClipperLib::Paths source = pathsvector[0]; ClipperLib::PolyTree result; @@ -109,13 +114,17 @@ namespace ClipperUtils { } ClipperLib::PolyTree sumresult; clipper.Execute(clipType, sumresult, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - if (sumresult.Total() == 0) return NULL; // The returned result will have outlines ordered according to whether // they're positive or negative: Positive outlines counter-clockwise and // negative outlines clockwise. return ClipperUtils::toPolygon2d(sumresult); } + /*! + Apply the clipper operator to the given polygons. + + May return an empty Polygon2d, but will not return NULL. + */ Polygon2d *apply(const std::vector &polygons, ClipperLib::ClipType clipType) { @@ -125,7 +134,9 @@ namespace ClipperUtils { if (!polygon->isSanitized()) ClipperLib::PolyTreeToPaths(sanitize(polypaths), polypaths); pathsvector.push_back(polypaths); } - return apply(pathsvector, clipType); + Polygon2d *res = apply(pathsvector, clipType); + assert(res); + return res; } diff --git a/src/color.cc b/src/color.cc index 816b77b4..d5615aa3 100644 --- a/src/color.cc +++ b/src/color.cc @@ -221,15 +221,15 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value v = c.lookup_variable("c"); - if (v.type() == Value::VECTOR) { + ValuePtr v = c.lookup_variable("c"); + if (v->type() == Value::VECTOR) { for (size_t i = 0; i < 4; i++) { - node->color[i] = i < v.toVector().size() ? v.toVector()[i].toDouble() : 1.0; + node->color[i] = i < v->toVector().size() ? v->toVector()[i].toDouble() : 1.0; if (node->color[i] > 1) PRINTB_NOCACHE("WARNING: color() expects numbers between 0.0 and 1.0. Value of %.1f is too large.", node->color[i]); } - } else if (v.type() == Value::STRING) { - std::string colorname = v.toString(); + } else if (v->type() == Value::STRING) { + std::string colorname = v->toString(); boost::algorithm::to_lower(colorname); Color4f color; if (webcolors.find(colorname) != webcolors.end()) { @@ -239,9 +239,9 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia PRINT_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); } } - Value alpha = c.lookup_variable("alpha"); - if (alpha.type() == Value::NUMBER) { - node->color[3] = alpha.toDouble(); + ValuePtr alpha = c.lookup_variable("alpha"); + if (alpha->type() == Value::NUMBER) { + node->color[3] = alpha->toDouble(); } std::vector instantiatednodes = inst->instantiateChildren(evalctx); diff --git a/src/colormap.cc b/src/colormap.cc index e4ccda14..51868815 100644 --- a/src/colormap.cc +++ b/src/colormap.cc @@ -3,8 +3,33 @@ #include "printutils.h" #include "PlatformUtils.h" +#include + static const char *DEFAULT_COLOR_SCHEME_NAME = "Cornfield"; +// See http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv +static void rgbtohsv(float r, float g, float b, float &h, float &s, float &v) +{ + float K = 0.f; + + if (g < b) + { + std::swap(g, b); + K = -1.f; + } + + if (r < g) + { + std::swap(r, g); + K = -2.f / 6.f - K; + } + + float chroma = r - std::min(g, b); + h = fabs(K + (g - b) / (6.f * chroma + 1e-20f)); + s = chroma / (r + 1e-20f); + v = r; +} + RenderColorScheme::RenderColorScheme() : _path("") { _name = DEFAULT_COLOR_SCHEME_NAME; @@ -196,6 +221,42 @@ Color4f ColorMap::getColor(const ColorScheme &cs, const RenderColor rc) return Color4f(0, 0, 0, 127); } +Color4f ColorMap::getColorHSV(const Color4f &col) +{ + float h, s, v; + rgbtohsv(col[0], col[1], col[2], h, s, v); + return Color4f(h, s, v, col[3]); +} + +/** + * Calculate contrast color. Based on the article + * http://gamedev.stackexchange.com/questions/38536/given-a-rgb-color-x-how-to-find-the-most-contrasting-color-y + * + * @param col the input color + * @return a color with high contrast to the input color + */ +Color4f ColorMap::getContrastColor(const Color4f &col) +{ + Color4f hsv = ColorMap::getColorHSV(col); + float Y = 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2]; + float S = hsv[1]; + + if (S < 0.5) { + // low saturation, choose between black / white based on luminance Y + float val = Y > 0.5 ? 0.0f : 1.0f; + return Color4f(val, val, val, 1.0f); + } else { + float H = 360 * hsv[0]; + if ((H < 60) || (H > 300)) { + return Color4f(0.0f, 1.0f, 1.0f, 1.0f); // red -> cyan + } else if (H < 180) { + return Color4f(1.0f, 0.0f, 1.0f, 1.0f); // green -> magenta + } else { + return Color4f(1.0f, 1.0f, 0.0f, 1.0f); // blue -> yellow + } + } +} + void ColorMap::enumerateColorSchemesInPath(colorscheme_set_t &result_set, const fs::path basePath) { const fs::path color_schemes = basePath / "color-schemes" / "render"; @@ -235,8 +296,8 @@ ColorMap::colorscheme_set_t ColorMap::enumerateColorSchemes() RenderColorScheme *defaultColorScheme = new RenderColorScheme(); result_set.insert(colorscheme_set_t::value_type(defaultColorScheme->index(), boost::shared_ptr(defaultColorScheme))); - enumerateColorSchemesInPath(result_set, PlatformUtils::resourcesPath()); + enumerateColorSchemesInPath(result_set, PlatformUtils::resourceBasePath()); enumerateColorSchemesInPath(result_set, PlatformUtils::userConfigPath()); return result_set; -} \ No newline at end of file +} diff --git a/src/colormap.h b/src/colormap.h index 883932fc..bb869dd8 100644 --- a/src/colormap.h +++ b/src/colormap.h @@ -8,7 +8,6 @@ #include #include #include -#include namespace fs = boost::filesystem; @@ -79,6 +78,8 @@ public: std::list colorSchemeNames(bool guiOnly = false) const; static Color4f getColor(const ColorScheme &cs, const RenderColor rc); + static Color4f getContrastColor(const Color4f &col); + static Color4f getColorHSV(const Color4f &col); private: ColorMap(); diff --git a/src/context.cc b/src/context.cc index 6a05eb2e..c7903967 100644 --- a/src/context.cc +++ b/src/context.cc @@ -78,14 +78,14 @@ void Context::setVariables(const AssignmentList &args, const EvalContext *evalctx) { BOOST_FOREACH(const Assignment &arg, args) { - set_variable(arg.first, arg.second ? arg.second->evaluate(this->parent) : Value()); + set_variable(arg.first, arg.second ? arg.second->evaluate(this->parent) : ValuePtr::undefined); } if (evalctx) { size_t posarg = 0; for (size_t i=0; inumArgs(); i++) { const std::string &name = evalctx->getArgName(i); - const Value &val = evalctx->getArgValue(i); + ValuePtr val = evalctx->getArgValue(i); if (name.empty()) { if (posarg < args.size()) this->set_variable(args[posarg++].first, val); } else { @@ -95,13 +95,18 @@ void Context::setVariables(const AssignmentList &args, } } -void Context::set_variable(const std::string &name, const Value &value) +void Context::set_variable(const std::string &name, const ValuePtr &value) { if (is_config_variable(name)) this->config_variables[name] = value; else this->variables[name] = value; } -void Context::set_constant(const std::string &name, const Value &value) +void Context::set_variable(const std::string &name, const Value &value) +{ + set_variable(name, ValuePtr(value)); +} + +void Context::set_constant(const std::string &name, const ValuePtr &value) { if (this->constants.find(name) != this->constants.end()) { PRINTB("WARNING: Attempt to modify constant '%s'.", name); @@ -111,11 +116,23 @@ void Context::set_constant(const std::string &name, const Value &value) } } -Value Context::lookup_variable(const std::string &name, bool silent) const +void Context::set_constant(const std::string &name, const Value &value) +{ + set_constant(name, ValuePtr(value)); +} + +void Context::apply_variables(const Context &other) +{ + for (ValueMap::const_iterator it = other.variables.begin();it != other.variables.end();it++) { + set_variable((*it).first, (*it).second); + } +} + +ValuePtr Context::lookup_variable(const std::string &name, bool silent) const { if (!this->ctx_stack) { PRINT("ERROR: Context had null stack in lookup_variable()!!"); - return Value(); + return ValuePtr::undefined; } if (is_config_variable(name)) { for (int i = this->ctx_stack->size()-1; i >= 0; i--) { @@ -123,7 +140,7 @@ Value Context::lookup_variable(const std::string &name, bool silent) const if (confvars.find(name) != confvars.end()) return confvars.find(name)->second; } - return Value(); + return ValuePtr::undefined; } if (!this->parent && this->constants.find(name) != this->constants.end()) return this->constants.find(name)->second; @@ -133,7 +150,7 @@ Value Context::lookup_variable(const std::string &name, bool silent) const return this->parent->lookup_variable(name, silent); if (!silent) PRINTB("WARNING: Ignoring unknown variable '%s'.", name); - return Value(); + return ValuePtr::undefined; } bool Context::has_local_variable(const std::string &name) const @@ -145,17 +162,30 @@ bool Context::has_local_variable(const std::string &name) const return variables.find(name) != variables.end(); } -Value Context::evaluate_function(const std::string &name, const EvalContext *evalctx) const +/** + * This is separated because PRINTB uses quite a lot of stack space + * and the methods using it evaluate_function() and instantiate_module() + * are called often when recursive functions or modules are evaluated. + * + * @param what what is ignored + * @param name name of the ignored object + */ +static void print_ignore_warning(const char *what, const char *name) +{ + PRINTB("WARNING: Ignoring unknown %s '%s'.", what % name); +} + +ValuePtr Context::evaluate_function(const std::string &name, const EvalContext *evalctx) const { if (this->parent) return this->parent->evaluate_function(name, evalctx); - PRINTB("WARNING: Ignoring unknown function '%s'.", name); - return Value(); + print_ignore_warning("function", name.c_str()); + return ValuePtr::undefined; } AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const { if (this->parent) return this->parent->instantiate_module(inst, evalctx); - PRINTB("WARNING: Ignoring unknown module '%s'.", inst.name()); + print_ignore_warning("module", inst.name().c_str()); return NULL; } @@ -190,7 +220,7 @@ std::string Context::dump(const AbstractModule *mod, const ModuleInstantiation * } } } - typedef std::pair ValueMapType; + typedef std::pair ValueMapType; s << " vars:"; BOOST_FOREACH(const ValueMapType &v, constants) { s << boost::format(" %s = %s") % v.first % v.second; diff --git a/src/context.h b/src/context.h index 29c887ce..f3b1bcc0 100644 --- a/src/context.h +++ b/src/context.h @@ -5,6 +5,7 @@ #include #include "value.h" #include "typedefs.h" +#include "memory.h" class Context { @@ -14,29 +15,32 @@ public: virtual ~Context(); const Context *getParent() const { return this->parent; } - virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, const class EvalContext *evalctx) const; virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, EvalContext *evalctx) const; void setVariables(const AssignmentList &args, const class EvalContext *evalctx = NULL); + void set_variable(const std::string &name, const ValuePtr &value); void set_variable(const std::string &name, const Value &value); + void set_constant(const std::string &name, const ValuePtr &value); void set_constant(const std::string &name, const Value &value); - Value lookup_variable(const std::string &name, bool silent = false) const; + void apply_variables(const Context &other); + ValuePtr lookup_variable(const std::string &name, bool silent = false) const; bool has_local_variable(const std::string &name) const; void setDocumentPath(const std::string &path) { this->document_path = path; } const std::string &documentPath() const { return this->document_path; } std::string getAbsolutePath(const std::string &filename) const; - + public: protected: const Context *parent; Stack *ctx_stack; - typedef boost::unordered_map ValueMap; + typedef boost::unordered_map ValueMap; ValueMap constants; ValueMap variables; ValueMap config_variables; diff --git a/src/control.cc b/src/control.cc index b2518670..bcbae4a5 100644 --- a/src/control.cc +++ b/src/control.cc @@ -63,7 +63,7 @@ public: // methods static const EvalContext* getLastModuleCtx(const EvalContext *evalctx); - static AbstractNode* getChild(const Value& value, const EvalContext* modulectx); + static AbstractNode* getChild(const Value &value, const EvalContext* modulectx); private: // data Type type; @@ -75,27 +75,27 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst { if (evalctx->numArgs() > l) { const std::string &it_name = evalctx->getArgName(l); - const Value &it_values = evalctx->getArgValue(l, ctx); + ValuePtr it_values = evalctx->getArgValue(l, ctx); Context c(ctx); - if (it_values.type() == Value::RANGE) { - Value::RangeType range = it_values.toRange(); + if (it_values->type() == Value::RANGE) { + Value::RangeType range = it_values->toRange(); boost::uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); } else { for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { - c.set_variable(it_name, Value(*it)); + c.set_variable(it_name, ValuePtr(*it)); for_eval(node, inst, l+1, &c, evalctx); } } } - else if (it_values.type() == Value::VECTOR) { - for (size_t i = 0; i < it_values.toVector().size(); i++) { - c.set_variable(it_name, it_values.toVector()[i]); + else if (it_values->type() == Value::VECTOR) { + for (size_t i = 0; i < it_values->toVector().size(); i++) { + c.set_variable(it_name, it_values->toVector()[i]); for_eval(node, inst, l+1, &c, evalctx); } } - else if (it_values.type() != Value::UNDEFINED) { + else if (it_values->type() != Value::UNDEFINED) { c.set_variable(it_name, it_values); for_eval(node, inst, l+1, &c, evalctx); } @@ -173,7 +173,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns int n = 0; if (evalctx->numArgs() > 0) { double v; - if (evalctx->getArgValue(0).getDouble(v)) { + if (evalctx->getArgValue(0)->getDouble(v)) { n = trunc(v); if (n < 0) { PRINTB("WARNING: Negative child index (%d) not allowed", n); @@ -221,13 +221,13 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } else if (evalctx->numArgs()>0) { // one (or more ignored) parameter - const Value& value = evalctx->getArgValue(0); - if (value.type() == Value::NUMBER) { - return getChild(value,modulectx); + ValuePtr value = evalctx->getArgValue(0); + if (value->type() == Value::NUMBER) { + return getChild(*value, modulectx); } - else if (value.type() == Value::VECTOR) { + else if (value->type() == Value::VECTOR) { AbstractNode* node = new AbstractNode(inst); - const Value::VectorType& vect = value.toVector(); + const Value::VectorType& vect = value->toVector(); foreach (const Value::VectorType::value_type& vectvalue, vect) { AbstractNode* childnode = getChild(vectvalue,modulectx); if (childnode==NULL) continue; // error @@ -235,9 +235,9 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } return node; } - else if (value.type() == Value::RANGE) { + else if (value->type() == Value::RANGE) { AbstractNode* node = new AbstractNode(inst); - Value::RangeType range = value.toRange(); + Value::RangeType range = value->toRange(); boost::uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps); @@ -253,7 +253,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns else { // Invalid parameter // (e.g. first child of difference is invalid) - PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value.toString()); + PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value->toString()); return NULL; } } @@ -268,11 +268,11 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns for (size_t i = 0; i < inst->arguments.size(); i++) { if (i > 0) msg << ", "; if (!evalctx->getArgName(i).empty()) msg << evalctx->getArgName(i) << " = "; - Value val = evalctx->getArgValue(i); - if (val.type() == Value::STRING) { - msg << '"' << val.toString() << '"'; + ValuePtr val = evalctx->getArgValue(i); + if (val->type() == Value::STRING) { + msg << '"' << val->toString() << '"'; } else { - msg << val.toString(); + msg << val->toString(); } } PRINTB("%s", msg.str()); @@ -308,7 +308,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns case IF: { node = new AbstractNode(inst); const IfElseModuleInstantiation *ifelse = dynamic_cast(inst); - if (evalctx->numArgs() > 0 && evalctx->getArgValue(0).toBool()) { + if (evalctx->numArgs() > 0 && evalctx->getArgValue(0)->toBool()) { inst->scope.apply(*evalctx); std::vector instantiatednodes = ifelse->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/dxfdata.cc b/src/dxfdata.cc index e9e83506..b749df9c 100644 --- a/src/dxfdata.cc +++ b/src/dxfdata.cc @@ -588,6 +588,9 @@ std::string DxfData::dump() const return out.str(); } +/* + May return an empty polygon, but will not return NULL + */ Polygon2d *DxfData::toPolygon2d() const { Polygon2d *poly = new Polygon2d(); @@ -602,9 +605,5 @@ Polygon2d *DxfData::toPolygon2d() const } poly->addOutline(outline); } - if (poly->outlines().size() == 0) { - delete poly; - poly = NULL; - } return poly; } diff --git a/src/dxfdim.cc b/src/dxfdim.cc index 8f55a2fa..8bcd1bc5 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -39,11 +39,11 @@ #include #include -boost::unordered_map dxf_dim_cache; -boost::unordered_map dxf_cross_cache; +boost::unordered_map dxf_dim_cache; +boost::unordered_map dxf_cross_cache; namespace fs = boost::filesystem; -Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) { std::string filename; std::string layername; @@ -56,17 +56,16 @@ Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) // since the path is only available for ModuleInstantiations, not function expressions. // See issue #217 for (size_t i = 0; i < evalctx->numArgs(); i++) { - if (evalctx->getArgName(i) == "file") - filename = lookup_file(evalctx->getArgValue(i).toString(), + ValuePtr n = evalctx->getArgName(i); + ValuePtr v = evalctx->getArgValue(i); + if (evalctx->getArgName(i) == "file") { + filename = lookup_file(v->toString(), evalctx->documentPath(), ctx->documentPath()); - if (evalctx->getArgName(i) == "layer") - layername = evalctx->getArgValue(i).toString(); - if (evalctx->getArgName(i) == "origin") - evalctx->getArgValue(i).getVec2(xorigin, yorigin); - if (evalctx->getArgName(i) == "scale") - evalctx->getArgValue(i).getDouble(scale); - if (evalctx->getArgName(i) == "name") - name = evalctx->getArgValue(i).toString(); + } + if (n == "layer") layername = v->toString(); + if (n == "origin") v->getVec2(xorigin, yorigin); + if (n == "scale") v->getDouble(scale); + if (n == "name") name = v->toString(); } std::stringstream keystream; @@ -100,46 +99,46 @@ Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) double y = d->coords[4][1] - d->coords[3][1]; double angle = d->angle; double distance_projected_on_line = fabs(x * cos(angle*M_PI/180) + y * sin(angle*M_PI/180)); - return dxf_dim_cache[key] = Value(distance_projected_on_line); + return dxf_dim_cache[key] = ValuePtr(distance_projected_on_line); } else if (type == 1) { // Aligned double x = d->coords[4][0] - d->coords[3][0]; double y = d->coords[4][1] - d->coords[3][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + return dxf_dim_cache[key] = ValuePtr(sqrt(x*x + y*y)); } else if (type == 2) { // Angular double a1 = atan2(d->coords[0][0] - d->coords[5][0], d->coords[0][1] - d->coords[5][1]); double a2 = atan2(d->coords[4][0] - d->coords[3][0], d->coords[4][1] - d->coords[3][1]); - return dxf_dim_cache[key] = Value(fabs(a1 - a2) * 180 / M_PI); + return dxf_dim_cache[key] = ValuePtr(fabs(a1 - a2) * 180 / M_PI); } else if (type == 3 || type == 4) { // Diameter or Radius double x = d->coords[5][0] - d->coords[0][0]; double y = d->coords[5][1] - d->coords[0][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + return dxf_dim_cache[key] = ValuePtr(sqrt(x*x + y*y)); } else if (type == 5) { // Angular 3 Point } else if (type == 6) { // Ordinate - return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); + return dxf_dim_cache[key] = ValuePtr((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); } PRINTB("WARNING: Dimension '%s' in '%s', layer '%s' has unsupported type!", name % filename % layername); - return Value(); + return ValuePtr::undefined; } PRINTB("WARNING: Can't find dimension '%s' in '%s', layer '%s'!", name % filename % layername); - return Value(); + return ValuePtr::undefined; } -Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) { std::string filename; std::string layername; @@ -151,14 +150,12 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) // since the path is only available for ModuleInstantiations, not function expressions. // See isse #217 for (size_t i = 0; i < evalctx->numArgs(); i++) { - if (evalctx->getArgName(i) == "file") - filename = ctx->getAbsolutePath(evalctx->getArgValue(i).toString()); - if (evalctx->getArgName(i) == "layer") - layername = evalctx->getArgValue(i).toString(); - if (evalctx->getArgName(i) == "origin") - evalctx->getArgValue(i).getVec2(xorigin, yorigin); - if (evalctx->getArgName(i) == "scale") - evalctx->getArgValue(i).getDouble(scale); + ValuePtr n = evalctx->getArgName(i); + ValuePtr v = evalctx->getArgValue(i); + if (n == "file") filename = ctx->getAbsolutePath(v->toString()); + if (n == "layer") layername = v->toString(); + if (n == "origin") v->getVec2(xorigin, yorigin); + if (n == "scale") v->getDouble(scale); } std::stringstream keystream; @@ -174,8 +171,9 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) << "|" << filesize; std::string key = keystream.str(); - if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) + if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) { return dxf_cross_cache.find(key)->second; + } DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); @@ -204,13 +202,13 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) Value::VectorType ret; ret.push_back(Value(x)); ret.push_back(Value(y)); - return dxf_cross_cache[key] = Value(ret); + return dxf_cross_cache[key] = ValuePtr(ret); } } PRINTB("WARNING: Can't find cross in '%s', layer '%s'!", filename % layername); - return Value(); + return ValuePtr::undefined; } void initialize_builtin_dxf_dim() diff --git a/src/dxfdim.h b/src/dxfdim.h index d84d07ce..bdadcee3 100644 --- a/src/dxfdim.h +++ b/src/dxfdim.h @@ -3,5 +3,5 @@ #include #include "value.h" -extern boost::unordered_map dxf_dim_cache; -extern boost::unordered_map dxf_cross_cache; +extern boost::unordered_map dxf_dim_cache; +extern boost::unordered_map dxf_cross_cache; diff --git a/src/evalcontext.cc b/src/evalcontext.cc index 35c6f092..79f078b7 100644 --- a/src/evalcontext.cc +++ b/src/evalcontext.cc @@ -5,6 +5,7 @@ #include "printutils.h" #include "builtin.h" #include "localscope.h" +#include "exceptions.h" #include @@ -20,11 +21,15 @@ const std::string &EvalContext::getArgName(size_t i) const return this->eval_arguments[i].first; } -Value EvalContext::getArgValue(size_t i, const Context *ctx) const +ValuePtr EvalContext::getArgValue(size_t i, const Context *ctx) const { assert(i < this->eval_arguments.size()); const Assignment &arg = this->eval_arguments[i]; - return arg.second ? arg.second->evaluate(ctx ? ctx : this) : Value(); + ValuePtr v; + if (arg.second) { + v = arg.second->evaluate(ctx ? ctx : this); + } + return v; } size_t EvalContext::numChildren() const @@ -62,7 +67,7 @@ std::string EvalContext::dump(const AbstractModule *mod, const ModuleInstantiati if (m) { s << boost::format(" module args:"); BOOST_FOREACH(const Assignment &arg, m->definition_arguments) { - s << boost::format(" %s = %s") % arg.first % variables[arg.first]; + s << boost::format(" %s = %s") % arg.first % *(variables[arg.first]); } } } diff --git a/src/evalcontext.h b/src/evalcontext.h index d591c39f..f5054264 100644 --- a/src/evalcontext.h +++ b/src/evalcontext.h @@ -17,7 +17,7 @@ public: size_t numArgs() const { return this->eval_arguments.size(); } const std::string &getArgName(size_t i) const; - Value getArgValue(size_t i, const Context *ctx = NULL) const; + ValuePtr getArgValue(size_t i, const Context *ctx = NULL) const; size_t numChildren() const; ModuleInstantiation *getChild(size_t i) const; diff --git a/src/exceptions.h b/src/exceptions.h new file mode 100644 index 00000000..21c4a2ed --- /dev/null +++ b/src/exceptions.h @@ -0,0 +1,16 @@ +#include + +class RecursionException: public std::exception { +public: + RecursionException(const char *recursiontype, const std::string &name) + : rectype(recursiontype), name(name) {} + virtual ~RecursionException() throw() {} + virtual const char *what() const throw() { + std::stringstream out; + out << "ERROR: Recursion detected calling " << this->rectype << " '" << this->name << "'"; + return out.str().c_str(); + } +private: + const char *rectype; + const std::string name; +}; diff --git a/src/export.cc b/src/export.cc index 1da18a74..81c2c933 100644 --- a/src/export.cc +++ b/src/export.cc @@ -133,7 +133,7 @@ void export_stl(const PolySet &ps, std::ostream &output) setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output output << "solid OpenSCAD_Model\n"; - BOOST_FOREACH(const PolySet::Polygon &p, triangulated.polygons) { + BOOST_FOREACH(const Polygon &p, triangulated.polygons) { assert(p.size() == 3); // STL only allows triangles std::stringstream stream; stream << p[0][0] << " " << p[0][1] << " " << p[0][2]; @@ -246,7 +246,7 @@ static void export_stl(const CGAL_Polyhedron &P, std::ostream &output) void export_stl(const CGAL_Nef_polyhedron *root_N, std::ostream &output) { if (!root_N->p3->is_simple()) { - PRINT("Warning: Exported object may not be a valid 2-manifold and may need repair"); + PRINT("WARNING: Exported object may not be a valid 2-manifold and may need repair"); } bool usePolySet = true; @@ -271,10 +271,10 @@ void export_stl(const CGAL_Nef_polyhedron *root_N, std::ostream &output) export_stl(P, output); } catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); + PRINTB("ERROR: CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); } catch (...) { - PRINT("CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()"); + PRINT("ERROR: CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()"); } CGAL::set_error_behaviour(old_behaviour); } @@ -291,7 +291,7 @@ void export_off(const class PolySet &ps, std::ostream &output) void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output) { if (!root_N->p3->is_simple()) { - PRINT("Object isn't a valid 2-manifold! Modify your design."); + PRINT("WARNING: Export failed, the object isn't a valid 2-manifold."); return; } CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); @@ -301,7 +301,7 @@ void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output) output << P; } catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); + PRINTB("ERROR: CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); } CGAL::set_error_behaviour(old_behaviour); } @@ -321,7 +321,7 @@ void export_amf(const class PolySet &ps, std::ostream &output) void export_amf(const CGAL_Nef_polyhedron *root_N, std::ostream &output) { if (!root_N->p3->is_simple()) { - PRINT("Object isn't a valid 2-manifold! Modify your design."); + PRINT("WARNING: Export failed, the object isn't a valid 2-manifold."); return; } CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); @@ -427,7 +427,7 @@ void export_amf(const CGAL_Nef_polyhedron *root_N, std::ostream &output) << " \r\n" << "\r\n"; } catch (CGAL::Assertion_exception e) { - PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); + PRINTB("ERROR: CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what()); } CGAL::set_error_behaviour(old_behaviour); setlocale(LC_NUMERIC, ""); // Set default locale @@ -505,11 +505,15 @@ void export_svg(const Polygon2d &poly, std::ostream &output) int miny = floor(-bbox.max().y()); int maxx = ceil(bbox.max().x()); int maxy = ceil(-bbox.min().y()); - + + int width = maxx - minx; + int height = maxy - miny; output << "\n" << "\n" - << "\n" + << "\n" << "OpenSCAD Model\n"; output << " #include "stl-utils.h" #include "printutils.h" +#include "stackcheck.h" +#include "exceptions.h" #include #include -Expression::Expression() : recursioncount(0) -{ -} - -Expression::Expression(const std::string &type, Expression *left, Expression *right) - : type(type), recursioncount(0) -{ - this->children.push_back(left); - this->children.push_back(right); -} - -Expression::Expression(const std::string &type, Expression *expr) - : type(type), recursioncount(0) -{ - this->children.push_back(expr); -} - -Expression::Expression(const Value &val) : const_value(val), type("C"), recursioncount(0) -{ -} - -Expression::~Expression() -{ - std::for_each(this->children.begin(), this->children.end(), del_fun()); -} - -Value Expression::sub_evaluate_range(const Context *context) const -{ - Value v1 = this->children[0]->evaluate(context); - if (v1.type() == Value::NUMBER) { - Value v2 = this->children[1]->evaluate(context); - if (v2.type() == Value::NUMBER) { - if (this->children.size() == 2) { - Value::RangeType range(v1.toDouble(), v2.toDouble()); - return Value(range); - } else { - Value v3 = this->children[2]->evaluate(context); - if (v3.type() == Value::NUMBER) { - Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble()); - return Value(range); - } - } - } - } - return Value(); -} - -Value Expression::sub_evaluate_member(const Context *context) const -{ - Value v = this->children[0]->evaluate(context); - - if (v.type() == Value::VECTOR) { - if (this->var_name == "x") return v[0]; - if (this->var_name == "y") return v[1]; - if (this->var_name == "z") return v[2]; - } else if (v.type() == Value::RANGE) { - if (this->var_name == "begin") return Value(v[0]); - if (this->var_name == "step") return Value(v[1]); - if (this->var_name == "end") return Value(v[2]); - } - return Value(); -} - -Value Expression::sub_evaluate_vector(const Context *context) const -{ - Value::VectorType vec; - BOOST_FOREACH(const Expression *e, this->children) { - vec.push_back(e->evaluate(context)); - } - return Value(vec); -} - -Value Expression::sub_evaluate_function(const Context *context) const -{ - if (this->recursioncount >= 1000) { - PRINTB("ERROR: Recursion detected calling function '%s'", this->call_funcname); - // TO DO: throw function_recursion_detected(); - return Value(); - } - this->recursioncount += 1; - EvalContext c(context, this->call_arguments); - const Value &result = context->evaluate_function(this->call_funcname, &c); - this->recursioncount -= 1; - return result; -} - -#define TYPE2INT(c,c1) ((int)(c) | ((int)(c1)<<8)) - -static inline int type2int(register const char *typestr) -{ - // the following asserts basic ASCII only so sign extension does not matter - // utilize the fact that type strings must have one or two non-null characters -#if 0 // defined(DEBUG) // may need this code as template for future development - register int c1, result = *typestr; - if (result && 0 != (c1 = typestr[1])) { - // take the third character for error checking only - result |= (c1 << 8) | ((int)typestr[2] << 16); - } - return result; -#else - return TYPE2INT(typestr[0], typestr[1]); -#endif -} - // unnamed namespace namespace { Value::VectorType flatten(Value::VectorType const& vec) { int n = 0; - for (int i = 0; i < vec.size(); i++) { + for (unsigned int i = 0; i < vec.size(); i++) { assert(vec[i].type() == Value::VECTOR); n += vec[i].toVector().size(); } Value::VectorType ret; ret.reserve(n); - for (int i = 0; i < vec.size(); i++) { + for (unsigned int i = 0; i < vec.size(); i++) { std::copy(vec[i].toVector().begin(),vec[i].toVector().end(),std::back_inserter(ret)); } return ret; @@ -159,9 +56,9 @@ namespace { const bool allow_reassignment = false; - for (int i = 0; i < let_context.numArgs(); i++) { + for (unsigned int i = 0; i < let_context.numArgs(); i++) { if (!allow_reassignment && context->has_local_variable(let_context.getArgName(i))) { - PRINTB("WARNING: Ignoring duplicate variable assignment %s = %s", let_context.getArgName(i) % let_context.getArgValue(i, context).toString()); + PRINTB("WARNING: Ignoring duplicate variable assignment %s = %s", let_context.getArgName(i) % let_context.getArgValue(i, context)->toString()); } else { // NOTE: iteratively evaluated list of arguments context->set_variable(let_context.getArgName(i), let_context.getArgValue(i, context)); @@ -170,133 +67,32 @@ namespace { } } -Value Expression::sub_evaluate_let_expression(const Context *context) const +Expression::Expression() : first(NULL), second(NULL), third(NULL) { - Context c(context); - evaluate_sequential_assignment(this->call_arguments, &c); - - return this->children[0]->evaluate(&c); } -Value Expression::sub_evaluate_list_comprehension(const Context *context) const +Expression::Expression(Expression *expr) : first(expr), second(NULL), third(NULL) { - Value::VectorType vec; - - if (this->call_funcname == "if") { - if (this->children[0]->evaluate(context)) { - if (this->children[1]->type == "c") { - return this->children[1]->evaluate(context); - } else { - vec.push_back(this->children[1]->evaluate(context)); - } - } - return vec; - } else if (this->call_funcname == "for") { - EvalContext for_context(context, this->call_arguments); - - Context assign_context(context); - - // comprehension for statements are by the parser reduced to only contain one single element - const std::string &it_name = for_context.getArgName(0); - const Value &it_values = for_context.getArgValue(0, &assign_context); - - Context c(context); - - if (it_values.type() == Value::RANGE) { - Value::RangeType range = it_values.toRange(); - boost::uint32_t steps = range.nbsteps(); - if (steps >= 1000000) { - PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); - } else { - for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { - c.set_variable(it_name, Value(*it)); - vec.push_back(this->children[0]->evaluate(&c)); - } - } - } - else if (it_values.type() == Value::VECTOR) { - for (size_t i = 0; i < it_values.toVector().size(); i++) { - c.set_variable(it_name, it_values.toVector()[i]); - vec.push_back(this->children[0]->evaluate(&c)); - } - } - else if (it_values.type() != Value::UNDEFINED) { - c.set_variable(it_name, it_values); - vec.push_back(this->children[0]->evaluate(&c)); - } - if (this->children[0]->type == "c") { - return flatten(vec); - } else { - return vec; - } - } else if (this->call_funcname == "let") { - Context c(context); - evaluate_sequential_assignment(this->call_arguments, &c); - - return this->children[0]->evaluate(&c); - } else { - abort(); - } + children.push_back(expr); } - -Value Expression::evaluate(const Context *context) const +Expression::Expression(Expression *left, Expression *right) : first(left), second(right), third(NULL) { - switch (type2int(this->type.c_str())) { - case '!': - return ! this->children[0]->evaluate(context); - case TYPE2INT('&','&'): - return this->children[0]->evaluate(context) && this->children[1]->evaluate(context); - case TYPE2INT('|','|'): - return this->children[0]->evaluate(context) || this->children[1]->evaluate(context); - case '*': - return this->children[0]->evaluate(context) * this->children[1]->evaluate(context); - case '/': - return this->children[0]->evaluate(context) / this->children[1]->evaluate(context); - case '%': - return this->children[0]->evaluate(context) % this->children[1]->evaluate(context); - case '+': - return this->children[0]->evaluate(context) + this->children[1]->evaluate(context); - case '-': - return this->children[0]->evaluate(context) - this->children[1]->evaluate(context); - case '<': - return this->children[0]->evaluate(context) < this->children[1]->evaluate(context); - case TYPE2INT('<','='): - return this->children[0]->evaluate(context) <= this->children[1]->evaluate(context); - case TYPE2INT('=','='): - return this->children[0]->evaluate(context) == this->children[1]->evaluate(context); - case TYPE2INT('!','='): - return this->children[0]->evaluate(context) != this->children[1]->evaluate(context); - case TYPE2INT('>','='): - return this->children[0]->evaluate(context) >= this->children[1]->evaluate(context); - case '>': - return this->children[0]->evaluate(context) > this->children[1]->evaluate(context); - case TYPE2INT('?',':'): - return this->children[this->children[0]->evaluate(context) ? 1 : 2]->evaluate(context); - case TYPE2INT('[',']'): - return this->children[0]->evaluate(context)[this->children[1]->evaluate(context)]; - case 'I': - return -this->children[0]->evaluate(context); - case 'C': - return this->const_value; - case 'R': - return sub_evaluate_range(context); - case 'V': - return sub_evaluate_vector(context); - case 'L': - return context->lookup_variable(this->var_name); - case 'N': - return sub_evaluate_member(context); - case 'F': - return sub_evaluate_function(context); - case 'l': - return sub_evaluate_let_expression(context); - case 'i': // list comprehension expression - return this->children[0]->evaluate(context); - case 'c': - return sub_evaluate_list_comprehension(context); - } - abort(); + children.push_back(left); + children.push_back(right); +} + +Expression::Expression(Expression *expr1, Expression *expr2, Expression *expr3) + : first(expr1), second(expr2), third(expr3) +{ + children.push_back(expr1); + children.push_back(expr2); + children.push_back(expr3); +} + +Expression::~Expression() +{ + std::for_each(this->children.begin(), this->children.end(), del_fun()); } namespace /* anonymous*/ { @@ -313,89 +109,509 @@ namespace /* anonymous*/ { } -std::string Expression::toString() const +bool Expression::isListComprehension() const { - std::stringstream stream; + return false; +} - if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" || - this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" || - this->type == "!=" || this->type == ">=" || this->type == ">" || - this->type == "&&" || this->type == "||") { - stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")"; - } - else if (this->type == "?:") { - stream << "(" << *this->children[0] << " ? " << *this->children[1] << " : " << *this->children[2] << ")"; - } - else if (this->type == "[]") { - stream << *this->children[0] << "[" << *this->children[1] << "]"; - } - else if (this->type == "I") { - stream << "-" << *this->children[0]; - } - else if (this->type == "!") { - stream << "!" << *this->children[0]; - } - else if (this->type == "C") { - stream << this->const_value; - } - else if (this->type == "R") { - stream << "[" << *this->children[0] << " : " << *this->children[1]; - if (this->children.size() > 2) { - stream << " : " << *this->children[2]; - } - stream << "]"; - } - else if (this->type == "V") { - stream << "["; - for (size_t i=0; i < this->children.size(); i++) { - if (i > 0) stream << ", "; - stream << *this->children[i]; - } - stream << "]"; - } - else if (this->type == "L") { - stream << this->var_name; - } - else if (this->type == "N") { - stream << *this->children[0] << "." << this->var_name; - } - else if (this->type == "F") { - stream << this->call_funcname << "(" << this->call_arguments << ")"; - } - else if (this->type == "l") { - stream << "let(" << this->call_arguments << ") " << *this->children[0]; - } - else if (this->type == "i") { // list comprehension expression - Expression const* c = this->children[0]; +ExpressionNot::ExpressionNot(Expression *expr) : Expression(expr) +{ +} - stream << "["; +ValuePtr ExpressionNot::evaluate(const Context *context) const +{ + return !first->evaluate(context); +} - do { - if (c->call_funcname == "for") { - stream << "for(" << c->call_arguments << ") "; - c = c->children[0]; - } else if (c->call_funcname == "if") { - stream << "if(" << *c->children[0] << ") "; - c = c->children[1]; - } else if (c->call_funcname == "let") { - stream << "let(" << c->call_arguments << ") "; - c = c->children[0]; +void ExpressionNot::print(std::ostream &stream) const +{ + stream << "!" << *first; +} + +ExpressionLogicalAnd::ExpressionLogicalAnd(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLogicalAnd::evaluate(const Context *context) const +{ + return this->first->evaluate(context) && this->second->evaluate(context); +} + +void ExpressionLogicalAnd::print(std::ostream &stream) const +{ + stream << "(" << *first << " && " << *second << ")"; +} + +ExpressionLogicalOr::ExpressionLogicalOr(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLogicalOr::evaluate(const Context *context) const +{ + return this->first->evaluate(context) || this->second->evaluate(context); +} + +void ExpressionLogicalOr::print(std::ostream &stream) const +{ + stream << "(" << *first << " || " << *second << ")"; +} + +ExpressionMultiply::ExpressionMultiply(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionMultiply::evaluate(const Context *context) const +{ + return this->first->evaluate(context) * this->second->evaluate(context); +} + +void ExpressionMultiply::print(std::ostream &stream) const +{ + stream << "(" << *first << " * " << *second << ")"; +} + +ExpressionDivision::ExpressionDivision(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionDivision::evaluate(const Context *context) const +{ + return this->first->evaluate(context) / this->second->evaluate(context); +} + +void ExpressionDivision::print(std::ostream &stream) const +{ + stream << "(" << *first << " / " << *second << ")"; +} + +ExpressionModulo::ExpressionModulo(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionModulo::evaluate(const Context *context) const +{ + return this->first->evaluate(context) % this->second->evaluate(context); +} + +void ExpressionModulo::print(std::ostream &stream) const +{ + stream << "(" << *first << " % " << *second << ")"; +} + +ExpressionPlus::ExpressionPlus(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionPlus::evaluate(const Context *context) const +{ + return this->first->evaluate(context) + this->second->evaluate(context); +} + +void ExpressionPlus::print(std::ostream &stream) const +{ + stream << "(" << *first << " + " << *second << ")"; +} + +ExpressionMinus::ExpressionMinus(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionMinus::evaluate(const Context *context) const +{ + return this->first->evaluate(context) - this->second->evaluate(context); +} + +void ExpressionMinus::print(std::ostream &stream) const +{ + stream << "(" << *first << " - " << *second << ")"; +} + +ExpressionLess::ExpressionLess(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLess::evaluate(const Context *context) const +{ + return this->first->evaluate(context) < this->second->evaluate(context); +} + +void ExpressionLess::print(std::ostream &stream) const +{ + stream << "(" << *first << " < " << *second << ")"; +} + +ExpressionLessOrEqual::ExpressionLessOrEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLessOrEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) <= this->second->evaluate(context); +} + +void ExpressionLessOrEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " <= " << *second << ")"; +} + +ExpressionEqual::ExpressionEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) == this->second->evaluate(context); +} + +void ExpressionEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " == " << *second << ")"; +} + +ExpressionNotEqual::ExpressionNotEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionNotEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) != this->second->evaluate(context); +} + +void ExpressionNotEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " != " << *second << ")"; +} + +ExpressionGreaterOrEqual::ExpressionGreaterOrEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionGreaterOrEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) >= this->second->evaluate(context); +} + +void ExpressionGreaterOrEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " >= " << *second << ")"; +} + +ExpressionGreater::ExpressionGreater(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionGreater::evaluate(const Context *context) const +{ + return this->first->evaluate(context) > this->second->evaluate(context); +} + +void ExpressionGreater::print(std::ostream &stream) const +{ + stream << "(" << *first << " > " << *second << ")"; +} + +ExpressionTernary::ExpressionTernary(Expression *expr1, Expression *expr2, Expression *expr3) : Expression(expr1, expr2, expr3) +{ +} + +ValuePtr ExpressionTernary::evaluate(const Context *context) const +{ + return (this->first->evaluate(context) ? this->second : this->third)->evaluate(context); +} + +void ExpressionTernary::print(std::ostream &stream) const +{ + stream << "(" << *first << " ? " << *second << " : " << *third << ")"; +} + +ExpressionArrayLookup::ExpressionArrayLookup(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionArrayLookup::evaluate(const Context *context) const { + return this->first->evaluate(context)[this->second->evaluate(context)]; +} + +void ExpressionArrayLookup::print(std::ostream &stream) const +{ + stream << *first << "[" << *second << "]"; +} + +ExpressionInvert::ExpressionInvert(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionInvert::evaluate(const Context *context) const +{ + return -this->first->evaluate(context); +} + +void ExpressionInvert::print(std::ostream &stream) const +{ + stream << "-" << *first; +} + +ExpressionConst::ExpressionConst(const ValuePtr &val) : const_value(val) +{ +} + +ValuePtr ExpressionConst::evaluate(const class Context *) const +{ + return ValuePtr(this->const_value); +} + +void ExpressionConst::print(std::ostream &stream) const +{ + stream << *this->const_value; +} + +ExpressionRange::ExpressionRange(Expression *expr1, Expression *expr2) : Expression(expr1, expr2) +{ +} + +ExpressionRange::ExpressionRange(Expression *expr1, Expression *expr2, Expression *expr3) : Expression(expr1, expr2, expr3) +{ +} + +ValuePtr ExpressionRange::evaluate(const Context *context) const +{ + ValuePtr v1 = this->first->evaluate(context); + if (v1->type() == Value::NUMBER) { + ValuePtr v2 = this->second->evaluate(context); + if (v2->type() == Value::NUMBER) { + if (this->children.size() == 2) { + Value::RangeType range(v1->toDouble(), v2->toDouble()); + return ValuePtr(range); } else { - assert(false && "Illegal list comprehension element"); + ValuePtr v3 = this->third->evaluate(context); + if (v3->type() == Value::NUMBER) { + Value::RangeType range(v1->toDouble(), v2->toDouble(), v3->toDouble()); + return ValuePtr(range); + } } - } while (c->type == "c"); - - stream << *c << "]"; - } - else { - assert(false && "Illegal expression type"); + } } + return ValuePtr::undefined; +} - return stream.str(); +void ExpressionRange::print(std::ostream &stream) const +{ + stream << "[" << *first << " : " << *second; + if (this->children.size() > 2) stream << " : " << *third; + stream << "]"; +} + +ExpressionVector::ExpressionVector(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionVector::evaluate(const Context *context) const +{ + Value::VectorType vec; + BOOST_FOREACH(const Expression *e, this->children) { + vec.push_back(*(e->evaluate(context))); + } + return ValuePtr(vec); +} + +void ExpressionVector::print(std::ostream &stream) const +{ + stream << "["; + for (size_t i=0; i < this->children.size(); i++) { + if (i > 0) stream << ", "; + stream << *this->children[i]; + } + stream << "]"; +} + +ExpressionLookup::ExpressionLookup(const std::string &var_name) : var_name(var_name) +{ +} + +ValuePtr ExpressionLookup::evaluate(const Context *context) const +{ + return context->lookup_variable(this->var_name); +} + +void ExpressionLookup::print(std::ostream &stream) const +{ + stream << this->var_name; +} + +ExpressionMember::ExpressionMember(Expression *expr, const std::string &member) + : Expression(expr), member(member) +{ +} + +ValuePtr ExpressionMember::evaluate(const Context *context) const +{ + ValuePtr v = this->first->evaluate(context); + + if (v->type() == Value::VECTOR) { + if (this->member == "x") return v[0]; + if (this->member == "y") return v[1]; + if (this->member == "z") return v[2]; + } else if (v->type() == Value::RANGE) { + if (this->member == "begin") return v[0]; + if (this->member == "step") return v[1]; + if (this->member == "end") return v[2]; + } + return ValuePtr::undefined; +} + +void ExpressionMember::print(std::ostream &stream) const +{ + stream << *first << "." << this->member; +} + +ExpressionFunctionCall::ExpressionFunctionCall(const std::string &funcname, + const AssignmentList &arglist) + : funcname(funcname), call_arguments(arglist) +{ +} + +ValuePtr ExpressionFunctionCall::evaluate(const Context *context) const +{ + if (StackCheck::inst()->check()) { + throw RecursionException("function", funcname); + } + + EvalContext c(context, this->call_arguments); + ValuePtr result = context->evaluate_function(this->funcname, &c); + + return result; +} + +void ExpressionFunctionCall::print(std::ostream &stream) const +{ + stream << this->funcname << "(" << this->call_arguments << ")"; +} + +ExpressionLet::ExpressionLet(const AssignmentList &arglist, Expression *expr) + : Expression(expr), call_arguments(arglist) +{ +} + +ValuePtr ExpressionLet::evaluate(const Context *context) const +{ + Context c(context); + evaluate_sequential_assignment(this->call_arguments, &c); + + return this->first->evaluate(&c); +} + +void ExpressionLet::print(std::ostream &stream) const +{ + stream << "let(" << this->call_arguments << ") " << *first; +} + +ExpressionLcExpression::ExpressionLcExpression(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionLcExpression::evaluate(const Context *context) const +{ + return this->first->evaluate(context); +} + +void ExpressionLcExpression::print(std::ostream &stream) const +{ + stream << "[" << *this->first << "]"; +} + +ExpressionLc::ExpressionLc(const std::string &name, + const AssignmentList &arglist, Expression *expr) + : Expression(expr), name(name), call_arguments(arglist) +{ +} + +ExpressionLc::ExpressionLc(const std::string &name, + Expression *expr1, Expression *expr2) + : Expression(expr1, expr2), name(name) +{ +} + +bool ExpressionLc::isListComprehension() const +{ + return true; +} + +ValuePtr ExpressionLc::evaluate(const Context *context) const +{ + Value::VectorType vec; + + if (this->name == "if") { + if (this->first->evaluate(context)) { + if (this->second->isListComprehension()) { + return this->second->evaluate(context); + } else { + vec.push_back((*this->second->evaluate(context))); + } + } + return ValuePtr(vec); + } else if (this->name == "for") { + EvalContext for_context(context, this->call_arguments); + + Context assign_context(context); + + // comprehension for statements are by the parser reduced to only contain one single element + const std::string &it_name = for_context.getArgName(0); + ValuePtr it_values = for_context.getArgValue(0, &assign_context); + + Context c(context); + + if (it_values->type() == Value::RANGE) { + Value::RangeType range = it_values->toRange(); + boost::uint32_t steps = range.nbsteps(); + if (steps >= 1000000) { + PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); + } else { + for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { + c.set_variable(it_name, ValuePtr(*it)); + vec.push_back((*this->first->evaluate(&c))); + } + } + } + else if (it_values->type() == Value::VECTOR) { + for (size_t i = 0; i < it_values->toVector().size(); i++) { + c.set_variable(it_name, it_values->toVector()[i]); + vec.push_back((*this->first->evaluate(&c))); + } + } + else if (it_values->type() != Value::UNDEFINED) { + c.set_variable(it_name, it_values); + vec.push_back((*this->first->evaluate(&c))); + } + if (this->first->isListComprehension()) { + return ValuePtr(flatten(vec)); + } else { + return ValuePtr(vec); + } + } else if (this->name == "let") { + Context c(context); + evaluate_sequential_assignment(this->call_arguments, &c); + + return this->first->evaluate(&c); + } else { + abort(); + } +} + +void ExpressionLc::print(std::ostream &stream) const +{ + stream << this->name; + if (this->name == "if") { + stream << "(" << *this->first << ") " << *this->second; + } + else if (this->name == "for" || this->name == "let") { + stream << "(" << this->call_arguments << ") " << *this->first; + } else { + assert(false && "Illegal list comprehension element"); + } } std::ostream &operator<<(std::ostream &stream, const Expression &expr) { - stream << expr.toString(); + expr.print(stream); return stream; } diff --git a/src/expression.h b/src/expression.h index b17496e5..2cd1bded 100644 --- a/src/expression.h +++ b/src/expression.h @@ -9,51 +9,247 @@ class Expression { public: std::vector children; - - const Value const_value; - std::string var_name; - - std::string call_funcname; - AssignmentList call_arguments; - - // Boolean: ! && || - // Operators: * / % + - - // Relations: < <= == != >= > - // Vector element: [] - // Condition operator: ?: - // Invert (prefix '-'): I - // Constant value: C - // Create Range: R - // Create Vector: V - // Create Matrix: M - // Lookup Variable: L - // Lookup member per name: N - // Function call: F - // Let expression: l - // List comprehension expression: i - // List comprehension: c - std::string type; + Expression *first; + Expression *second; + Expression *third; Expression(); - Expression(const Value &val); - Expression(const std::string &type, Expression *left, Expression *right); - Expression(const std::string &type, Expression *expr); - ~Expression(); + Expression(Expression *expr); + Expression(Expression *left, Expression *right); + Expression(Expression *expr1, Expression *expr2, Expression *expr3); + virtual ~Expression(); - Value evaluate(const class Context *context) const; - - std::string toString() const; - -private: - mutable int recursioncount; - - // The following sub_* methods are needed to minimize stack usage only. - Value sub_evaluate_function(const class Context *context) const; - Value sub_evaluate_member(const class Context *context) const; - Value sub_evaluate_range(const class Context *context) const; - Value sub_evaluate_vector(const class Context *context) const; - Value sub_evaluate_let_expression(const class Context *context) const; - Value sub_evaluate_list_comprehension(const class Context *context) const; + virtual bool isListComprehension() const; + virtual ValuePtr evaluate(const class Context *context) const = 0; + virtual void print(std::ostream &stream) const = 0; }; std::ostream &operator<<(std::ostream &stream, const Expression &expr); + +class ExpressionNot : public Expression +{ +public: + ExpressionNot(Expression *expr); + virtual ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLogicalAnd : public Expression +{ +public: + ExpressionLogicalAnd(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLogicalOr : public Expression +{ +public: + ExpressionLogicalOr(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionMultiply : public Expression +{ +public: + ExpressionMultiply(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionDivision : public Expression +{ +public: + ExpressionDivision(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionModulo : public Expression +{ +public: + ExpressionModulo(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionPlus : public Expression +{ +public: + ExpressionPlus(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionMinus : public Expression +{ +public: + ExpressionMinus(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLess : public Expression +{ +public: + ExpressionLess(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLessOrEqual : public Expression +{ +public: + ExpressionLessOrEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionEqual : public Expression +{ +public: + ExpressionEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionNotEqual : public Expression +{ +public: + ExpressionNotEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionGreaterOrEqual : public Expression +{ +public: + ExpressionGreaterOrEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionGreater : public Expression +{ +public: + ExpressionGreater(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionTernary : public Expression +{ +public: + ExpressionTernary(Expression *expr1, Expression *expr2, Expression *expr3); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionArrayLookup : public Expression +{ +public: + ExpressionArrayLookup(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: +}; + +class ExpressionInvert : public Expression +{ +public: + ExpressionInvert(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionConst : public Expression +{ +public: + ExpressionConst(const ValuePtr &val); + ValuePtr evaluate(const class Context *) const; + virtual void print(std::ostream &stream) const; +private: + ValuePtr const_value; +}; + +class ExpressionRange : public Expression +{ +public: + ExpressionRange(Expression *expr1, Expression *expr2); + ExpressionRange(Expression *expr1, Expression *expr2, Expression *expr3); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionVector : public Expression +{ +public: + ExpressionVector(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLookup : public Expression +{ +public: + ExpressionLookup(const std::string &var_name); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string var_name; +}; + +class ExpressionMember : public Expression +{ +public: + ExpressionMember(Expression *expr, const std::string &member); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string member; +}; + +class ExpressionFunctionCall : public Expression +{ +public: + ExpressionFunctionCall(const std::string &funcname, const AssignmentList &arglist); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +public: + std::string funcname; + AssignmentList call_arguments; +}; + +class ExpressionLet : public Expression +{ +public: + ExpressionLet(const AssignmentList &arglist, Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + AssignmentList call_arguments; +}; + +class ExpressionLcExpression : public Expression +{ +public: + ExpressionLcExpression(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLc : public Expression +{ + virtual bool isListComprehension() const; +public: + ExpressionLc(const std::string &name, + const AssignmentList &arglist, Expression *expr); + ExpressionLc(const std::string &name, + Expression *expr1, Expression *expr2); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string name; + AssignmentList call_arguments; +}; diff --git a/src/feature.cc b/src/feature.cc index eb0ee8fd..79df7cce 100644 --- a/src/feature.cc +++ b/src/feature.cc @@ -18,7 +18,6 @@ Feature::list_t Feature::feature_list; * argument to enable the option and for saving the option value in GUI * context. */ -const Feature Feature::ExperimentalTextModule("text", "Enable the text() module."); Feature::Feature(const std::string &name, const std::string &description) : enabled(false), name(name), description(description) diff --git a/src/feature.h b/src/feature.h index 5922295a..579ba3c0 100644 --- a/src/feature.h +++ b/src/feature.h @@ -12,8 +12,6 @@ public: typedef std::vector list_t; typedef list_t::iterator iterator; - static const Feature ExperimentalTextModule; - const std::string& get_name() const; const std::string& get_description() const; diff --git a/src/func.cc b/src/func.cc index f26f44c5..75597720 100644 --- a/src/func.cc +++ b/src/func.cc @@ -35,6 +35,8 @@ #include #include "stl-utils.h" #include "printutils.h" +#include "stackcheck.h" +#include "exceptions.h" #include #include @@ -69,10 +71,11 @@ AbstractFunction::~AbstractFunction() { } -Value AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const +// FIXME: Is this needed? +ValuePtr AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const { (void)evalctx; // unusued parameter - return Value(); + return ValuePtr::undefined; } std::string AbstractFunction::dump(const std::string &indent, const std::string &name) const @@ -82,17 +85,24 @@ std::string AbstractFunction::dump(const std::string &indent, const std::string return dump.str(); } +Function::Function(const char *name, AssignmentList &definition_arguments, Expression *expr) + : name(name), definition_arguments(definition_arguments), expr(expr) +{ +} + Function::~Function() { delete expr; } -Value Function::evaluate(const Context *ctx, const EvalContext *evalctx) const +ValuePtr Function::evaluate(const Context *ctx, const EvalContext *evalctx) const { - if (!expr) return Value(); + if (!expr) return ValuePtr::undefined; Context c(ctx); c.setVariables(definition_arguments, evalctx); - return expr->evaluate(&c); + ValuePtr result = expr->evaluate(&c); + + return result; } std::string Function::dump(const std::string &indent, const std::string &name) const @@ -109,11 +119,74 @@ std::string Function::dump(const std::string &indent, const std::string &name) c return dump.str(); } +class FunctionTailRecursion : public Function +{ +private: + bool invert; + ExpressionFunctionCall *call; // memory owned by the main expression + Expression *endexpr; // memory owned by the main expression + +public: + FunctionTailRecursion(const char *name, AssignmentList &definition_arguments, Expression *expr, ExpressionFunctionCall *call, Expression *endexpr, bool invert); + virtual ~FunctionTailRecursion(); + + virtual ValuePtr evaluate(const Context *ctx, const EvalContext *evalctx) const; +}; + +FunctionTailRecursion::FunctionTailRecursion(const char *name, AssignmentList &definition_arguments, Expression *expr, ExpressionFunctionCall *call, Expression *endexpr, bool invert) + : Function(name, definition_arguments, expr), invert(invert), call(call), endexpr(endexpr) +{ +} + +FunctionTailRecursion::~FunctionTailRecursion() +{ +} + +ValuePtr FunctionTailRecursion::evaluate(const Context *ctx, const EvalContext *evalctx) const +{ + if (!expr) return ValuePtr::undefined; + + Context c(ctx); + c.setVariables(definition_arguments, evalctx); + + EvalContext ec(&c, call->call_arguments); + Context tmp(&c); + unsigned int counter = 0; + while (invert ^ expr->first->evaluate(&c)) { + tmp.setVariables(definition_arguments, &ec); + c.apply_variables(tmp); + + if (counter++ == 1000000) throw RecursionException("function", this->name); + } + + ValuePtr result = endexpr->evaluate(&c); + + return result; +} + +Function * Function::create(const char *name, AssignmentList &definition_arguments, Expression *expr) +{ + if (dynamic_cast(expr)) { + ExpressionFunctionCall *f1 = dynamic_cast(expr->second); + ExpressionFunctionCall *f2 = dynamic_cast(expr->third); + if (f1 && !f2) { + if (name == f1->funcname) { + return new FunctionTailRecursion(name, definition_arguments, expr, f1, expr->third, false); + } + } else if (f2 && !f1) { + if (name == f2->funcname) { + return new FunctionTailRecursion(name, definition_arguments, expr, f2, expr->second, true); + } + } + } + return new Function(name, definition_arguments, expr); +} + BuiltinFunction::~BuiltinFunction() { } -Value BuiltinFunction::evaluate(const Context *ctx, const EvalContext *evalctx) const +ValuePtr BuiltinFunction::evaluate(const Context *ctx, const EvalContext *evalctx) const { return eval_func(ctx, evalctx); } @@ -135,51 +208,51 @@ static inline double rad2deg(double x) return x * 180.0 / M_PI; } -Value builtin_abs(const Context *, const EvalContext *evalctx) +ValuePtr builtin_abs(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(fabs(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(fabs(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_sign(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sign(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) { - register double x = v.toDouble(); - return Value((x<0) ? -1.0 : ((x>0) ? 1.0 : 0.0)); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) { + register double x = v->toDouble(); + return ValuePtr((x<0) ? -1.0 : ((x>0) ? 1.0 : 0.0)); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_rands(const Context *, const EvalContext *evalctx) +ValuePtr builtin_rands(const Context *, const EvalContext *evalctx) { size_t n = evalctx->numArgs(); if (n == 3 || n == 4) { - const Value &v0 = evalctx->getArgValue(0); - if (v0.type() != Value::NUMBER) goto quit; - double min = v0.toDouble(); + ValuePtr v0 = evalctx->getArgValue(0); + if (v0->type() != Value::NUMBER) goto quit; + double min = v0->toDouble(); - const Value &v1 = evalctx->getArgValue(1); - if (v1.type() != Value::NUMBER) goto quit; - double max = v1.toDouble(); + ValuePtr v1 = evalctx->getArgValue(1); + if (v1->type() != Value::NUMBER) goto quit; + double max = v1->toDouble(); if (max < min) { register double tmp = min; min = max; max = tmp; } - const Value &v2 = evalctx->getArgValue(2); - if (v2.type() != Value::NUMBER) goto quit; - size_t numresults = std::max( 0, static_cast( v2.toDouble() ) ); + ValuePtr v2 = evalctx->getArgValue(2); + if (v2->type() != Value::NUMBER) goto quit; + size_t numresults = std::max(0, static_cast(v2->toDouble())); bool deterministic = false; if (n > 3) { - const Value &v3 = evalctx->getArgValue(3); - if (v3.type() != Value::NUMBER) goto quit; - deterministic_rng.seed( (unsigned int) v3.toDouble() ); + ValuePtr v3 = evalctx->getArgValue(3); + if (v3->type() != Value::NUMBER) goto quit; + deterministic_rng.seed((unsigned int) v3->toDouble()); deterministic = true; } boost::uniform_real<> distributor( min, max ); @@ -190,83 +263,81 @@ Value builtin_rands(const Context *, const EvalContext *evalctx) } else { for (size_t i=0; i < numresults; i++) { if ( deterministic ) { - vec.push_back( Value( distributor( deterministic_rng ) ) ); + vec.push_back(Value(distributor(deterministic_rng))); } else { - vec.push_back( Value( distributor( lessdeterministic_rng ) ) ); + vec.push_back(Value(distributor(lessdeterministic_rng))); } } } - return Value(vec); + return ValuePtr(vec); } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_min(const Context *, const EvalContext *evalctx) +ValuePtr builtin_min(const Context *, const EvalContext *evalctx) { // preserve special handling of the first argument // as a template for vector processing size_t n = evalctx->numArgs(); if (n >= 1) { - const Value &v0 = evalctx->getArgValue(0); + ValuePtr v0 = evalctx->getArgValue(0); - if (n == 1 && v0.type() == Value::VECTOR && !v0.toVector().empty()) { - Value min = v0.toVector()[0]; - for (size_t i = 1; i < v0.toVector().size(); i++) { - if (v0.toVector()[i] < min) - min = v0.toVector()[i]; + if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) { + Value min = v0->toVector()[0]; + for (size_t i = 1; i < v0->toVector().size(); i++) { + if (v0->toVector()[i] < min) min = v0->toVector()[i]; } - return min; + return ValuePtr(min); } - if (v0.type() == Value::NUMBER) { - double val = v0.toDouble(); + if (v0->type() == Value::NUMBER) { + double val = v0->toDouble(); for (size_t i = 1; i < n; ++i) { - const Value &v = evalctx->getArgValue(i); + ValuePtr v = evalctx->getArgValue(i); // 4/20/14 semantic change per discussion: // break on any non-number - if (v.type() != Value::NUMBER) goto quit; - register double x = v.toDouble(); + if (v->type() != Value::NUMBER) goto quit; + register double x = v->toDouble(); if (x < val) val = x; } - return Value(val); + return ValuePtr(val); } } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_max(const Context *, const EvalContext *evalctx) +ValuePtr builtin_max(const Context *, const EvalContext *evalctx) { // preserve special handling of the first argument // as a template for vector processing size_t n = evalctx->numArgs(); if (n >= 1) { - const Value &v0 = evalctx->getArgValue(0); + ValuePtr v0 = evalctx->getArgValue(0); - if (n == 1 && v0.type() == Value::VECTOR && !v0.toVector().empty()) { - Value max = v0.toVector()[0]; - for (size_t i = 1; i < v0.toVector().size(); i++) { - if (v0.toVector()[i] > max) - max = v0.toVector()[i]; + if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) { + Value max = v0->toVector()[0]; + for (size_t i = 1; i < v0->toVector().size(); i++) { + if (v0->toVector()[i] > max) max = v0->toVector()[i]; } - return max; + return ValuePtr(max); } - if (v0.type() == Value::NUMBER) { - double val = v0.toDouble(); + if (v0->type() == Value::NUMBER) { + double val = v0->toDouble(); for (size_t i = 1; i < n; ++i) { - const Value &v = evalctx->getArgValue(i); + ValuePtr v = evalctx->getArgValue(i); // 4/20/14 semantic change per discussion: // break on any non-number - if (v.type() != Value::NUMBER) goto quit; - register double x = v.toDouble(); + if (v->type() != Value::NUMBER) goto quit; + register double x = v->toDouble(); if (x > val) val = x; } - return Value(val); + return ValuePtr(val); } } quit: - return Value(); + return ValuePtr::undefined; } // this limit assumes 26+26=52 bits mantissa @@ -307,14 +378,14 @@ double sin_degrees(register double x) return oppose ? -x : x; } -Value builtin_sin(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sin(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(sin_degrees(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(sin_degrees(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } double cos_degrees(register double x) @@ -354,220 +425,221 @@ double cos_degrees(register double x) return oppose ? -x : x; } -Value builtin_cos(const Context *, const EvalContext *evalctx) +ValuePtr builtin_cos(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(cos_degrees(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(cos_degrees(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_asin(const Context *, const EvalContext *evalctx) +ValuePtr builtin_asin(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(asin(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(asin(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_acos(const Context *, const EvalContext *evalctx) +ValuePtr builtin_acos(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(acos(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(acos(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_tan(const Context *, const EvalContext *evalctx) +ValuePtr builtin_tan(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(tan(deg2rad(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(tan(deg2rad(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_atan(const Context *, const EvalContext *evalctx) +ValuePtr builtin_atan(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(atan(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(atan(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_atan2(const Context *, const EvalContext *evalctx) +ValuePtr builtin_atan2(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 2) { - Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); - if (v0.type() == Value::NUMBER && v1.type() == Value::NUMBER) - return Value(rad2deg(atan2(v0.toDouble(), v1.toDouble()))); + ValuePtr v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); + if (v0->type() == Value::NUMBER && v1->type() == Value::NUMBER) + return ValuePtr(rad2deg(atan2(v0->toDouble(), v1->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_pow(const Context *, const EvalContext *evalctx) +ValuePtr builtin_pow(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 2) { - Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); - if (v0.type() == Value::NUMBER && v1.type() == Value::NUMBER) - return Value(pow(v0.toDouble(), v1.toDouble())); + ValuePtr v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); + if (v0->type() == Value::NUMBER && v1->type() == Value::NUMBER) + return ValuePtr(pow(v0->toDouble(), v1->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_round(const Context *, const EvalContext *evalctx) +ValuePtr builtin_round(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(round(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(round(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_ceil(const Context *, const EvalContext *evalctx) +ValuePtr builtin_ceil(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(ceil(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(ceil(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_floor(const Context *, const EvalContext *evalctx) +ValuePtr builtin_floor(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(floor(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(floor(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_sqrt(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sqrt(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(sqrt(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(sqrt(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_exp(const Context *, const EvalContext *evalctx) +ValuePtr builtin_exp(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(exp(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(exp(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_length(const Context *, const EvalContext *evalctx) +ValuePtr builtin_length(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::VECTOR) return Value(int(v.toVector().size())); - if (v.type() == Value::STRING) { + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::VECTOR) return ValuePtr(int(v->toVector().size())); + if (v->type() == Value::STRING) { //Unicode glyph count for the length -- rather than the string (num. of bytes) length. - std::string text = v.toString(); - return Value(int( g_utf8_strlen( text.c_str(), text.size() ) )); + std::string text = v->toString(); + return ValuePtr(int( g_utf8_strlen( text.c_str(), text.size() ) )); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_log(const Context *, const EvalContext *evalctx) +ValuePtr builtin_log(const Context *, const EvalContext *evalctx) { size_t n = evalctx->numArgs(); if (n == 1 || n == 2) { - const Value &v0 = evalctx->getArgValue(0); - if (v0.type() == Value::NUMBER) { - double x = 10.0, y = v0.toDouble(); + ValuePtr v0 = evalctx->getArgValue(0); + if (v0->type() == Value::NUMBER) { + double x = 10.0, y = v0->toDouble(); if (n > 1) { - const Value &v1 = evalctx->getArgValue(1); - if (v1.type() != Value::NUMBER) goto quit; - x = y; y = v1.toDouble(); + ValuePtr v1 = evalctx->getArgValue(1); + if (v1->type() != Value::NUMBER) goto quit; + x = y; y = v1->toDouble(); } - return Value(log(y) / log(x)); + return ValuePtr(log(y) / log(x)); } } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_ln(const Context *, const EvalContext *evalctx) +ValuePtr builtin_ln(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(log(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(log(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_str(const Context *, const EvalContext *evalctx) +ValuePtr builtin_str(const Context *, const EvalContext *evalctx) { std::stringstream stream; for (size_t i = 0; i < evalctx->numArgs(); i++) { - stream << evalctx->getArgValue(i).toString(); + stream << evalctx->getArgValue(i)->toString(); } - return Value(stream.str()); + return ValuePtr(stream.str()); } -Value builtin_chr(const Context *, const EvalContext *evalctx) +ValuePtr builtin_chr(const Context *, const EvalContext *evalctx) { std::stringstream stream; for (size_t i = 0; i < evalctx->numArgs(); i++) { - const Value v = evalctx->getArgValue(i); - stream << v.chrString(); + ValuePtr v = evalctx->getArgValue(i); + stream << v->chrString(); } - return Value(stream.str()); + return ValuePtr(stream.str()); } -Value builtin_concat(const Context *, const EvalContext *evalctx) +ValuePtr builtin_concat(const Context *, const EvalContext *evalctx) { Value::VectorType result; for (size_t i = 0; i < evalctx->numArgs(); i++) { - const Value v = evalctx->getArgValue(i); - if (v.type() == Value::VECTOR) { - Value::VectorType vec = v.toVector(); + ValuePtr v = evalctx->getArgValue(i); + if (v->type() == Value::VECTOR) { + Value::VectorType vec = v->toVector(); for (Value::VectorType::const_iterator it = vec.begin(); it != vec.end(); it++) { result.push_back(*it); } } else { - result.push_back(v); + result.push_back(*v); } } - return Value(result); + return ValuePtr(result); } -Value builtin_lookup(const Context *, const EvalContext *evalctx) +ValuePtr builtin_lookup(const Context *, const EvalContext *evalctx) { double p, low_p, low_v, high_p, high_v; if (evalctx->numArgs() < 2 || // Needs two args - !evalctx->getArgValue(0).getDouble(p)) // First must be a number - return Value(); + !evalctx->getArgValue(0)->getDouble(p)) // First must be a number + return ValuePtr::undefined; - const Value::VectorType vec = evalctx->getArgValue(1).toVector(); + ValuePtr v1 = evalctx->getArgValue(1); + const Value::VectorType &vec = v1->toVector(); if (vec[0].toVector().size() < 2) // Second must be a vector of vectors - return Value(); + return ValuePtr::undefined; if (!vec[0].getVec2(low_p, low_v) || !vec[0].getVec2(high_p, high_v)) - return Value(); + return ValuePtr::undefined; for (size_t i = 1; i < vec.size(); i++) { double this_p, this_v; if (vec[i].getVec2(this_p, this_v)) { @@ -582,11 +654,11 @@ Value builtin_lookup(const Context *, const EvalContext *evalctx) } } if (p <= low_p) - return Value(high_v); + return ValuePtr(high_v); if (p >= high_p) - return Value(low_v); + return ValuePtr(low_v); double f = (p-low_p) / (high_p-low_p); - return Value(high_v * f + low_v * (1-f)); + return ValuePtr(high_v * f + low_v * (1-f)); } /* @@ -715,48 +787,48 @@ static Value::VectorType search(const std::string &find, const Value::VectorType return returnvec; } -Value builtin_search(const Context *, const EvalContext *evalctx) +ValuePtr builtin_search(const Context *, const EvalContext *evalctx) { - if (evalctx->numArgs() < 2) return Value(); + if (evalctx->numArgs() < 2) return ValuePtr::undefined; - const Value &findThis = evalctx->getArgValue(0); - const Value &searchTable = evalctx->getArgValue(1); - unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? evalctx->getArgValue(2).toDouble() : 1; - unsigned int index_col_num = (evalctx->numArgs() > 3) ? evalctx->getArgValue(3).toDouble() : 0; + ValuePtr findThis = evalctx->getArgValue(0); + ValuePtr searchTable = evalctx->getArgValue(1); + unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? evalctx->getArgValue(2)->toDouble() : 1; + unsigned int index_col_num = (evalctx->numArgs() > 3) ? evalctx->getArgValue(3)->toDouble() : 0; Value::VectorType returnvec; - if (findThis.type() == Value::NUMBER) { + if (findThis->type() == Value::NUMBER) { unsigned int matchCount = 0; - for (size_t j = 0; j < searchTable.toVector().size(); j++) { - const Value& search_element = searchTable.toVector()[j]; + for (size_t j = 0; j < searchTable->toVector().size(); j++) { + const Value &search_element = searchTable->toVector()[j]; - if ((index_col_num == 0 && findThis == search_element) || + if ((index_col_num == 0 && *findThis == search_element) || (index_col_num < search_element.toVector().size() && - findThis == search_element.toVector()[index_col_num])) { + *findThis == search_element.toVector()[index_col_num])) { returnvec.push_back(Value(double(j))); matchCount++; if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break; } } - } else if (findThis.type() == Value::STRING) { - if (searchTable.type() == Value::STRING) { - returnvec = search(findThis.toString(), searchTable.toString(), num_returns_per_match, index_col_num); + } else if (findThis->type() == Value::STRING) { + if (searchTable->type() == Value::STRING) { + returnvec = search(findThis->toString(), searchTable->toString(), num_returns_per_match, index_col_num); } else { - returnvec = search(findThis.toString(), searchTable.toVector(), num_returns_per_match, index_col_num); + returnvec = search(findThis->toString(), searchTable->toVector(), num_returns_per_match, index_col_num); } - } else if (findThis.type() == Value::VECTOR) { - for (size_t i = 0; i < findThis.toVector().size(); i++) { + } else if (findThis->type() == Value::VECTOR) { + for (size_t i = 0; i < findThis->toVector().size(); i++) { unsigned int matchCount = 0; Value::VectorType resultvec; - Value const& find_value = findThis.toVector()[i]; + Value const& find_value = findThis->toVector()[i]; - for (size_t j = 0; j < searchTable.toVector().size(); j++) { + for (size_t j = 0; j < searchTable->toVector().size(); j++) { - Value const& search_element = searchTable.toVector()[j]; + Value const& search_element = searchTable->toVector()[j]; if ((index_col_num == 0 && find_value == search_element) || (index_col_num < search_element.toVector().size() && @@ -773,53 +845,53 @@ Value builtin_search(const Context *, const EvalContext *evalctx) } } if (num_returns_per_match == 1 && matchCount == 0) { - if (findThis.toVector()[i].type() == Value::NUMBER) { - PRINTB(" WARNING: search term not found: %s",findThis.toVector()[i].toDouble()); + if (findThis->toVector()[i].type() == Value::NUMBER) { + PRINTB(" WARNING: search term not found: %s",findThis->toVector()[i].toDouble()); } - else if (findThis.toVector()[i].type() == Value::STRING) { - PRINTB(" WARNING: search term not found: \"%s\"",findThis.toVector()[i].toString()); + else if (findThis->toVector()[i].type() == Value::STRING) { + PRINTB(" WARNING: search term not found: \"%s\"",findThis->toVector()[i].toString()); } - returnvec.push_back(Value(resultvec)); + returnvec.push_back(resultvec); } if (num_returns_per_match == 0 || num_returns_per_match > 1) { - returnvec.push_back(Value(resultvec)); + returnvec.push_back(resultvec); } } } else { PRINTB(" WARNING: search: none performed on input %s", findThis); - return Value(); + return ValuePtr::undefined; } - return Value(returnvec); + return ValuePtr(returnvec); } #define QUOTE(x__) # x__ #define QUOTED(x__) QUOTE(x__) -Value builtin_version(const Context *, const EvalContext *evalctx) +ValuePtr builtin_version(const Context *, const EvalContext *evalctx) { (void)evalctx; // unusued parameter Value::VectorType val; - val.push_back(Value(double(OPENSCAD_YEAR))); - val.push_back(Value(double(OPENSCAD_MONTH))); + val.push_back(double(OPENSCAD_YEAR)); + val.push_back(double(OPENSCAD_MONTH)); #ifdef OPENSCAD_DAY - val.push_back(Value(double(OPENSCAD_DAY))); + val.push_back(double(OPENSCAD_DAY)); #endif - return Value(val); + return ValuePtr(val); } -Value builtin_version_num(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_version_num(const Context *ctx, const EvalContext *evalctx) { - Value val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0); + ValuePtr val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0); double y, m, d = 0; - if (!val.getVec3(y, m, d)) { - if (!val.getVec2(y, m)) { - return Value(); + if (!val->getVec3(y, m, d)) { + if (!val->getVec2(y, m)) { + return ValuePtr::undefined; } } - return Value(y * 10000 + m * 100 + d); + return ValuePtr(y * 10000 + m * 100 + d); } -Value builtin_parent_module(const Context *, const EvalContext *evalctx) +ValuePtr builtin_parent_module(const Context *, const EvalContext *evalctx) { int n; double d; @@ -827,30 +899,30 @@ Value builtin_parent_module(const Context *, const EvalContext *evalctx) if (evalctx->numArgs() == 0) d=1; // parent module else if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() != Value::NUMBER) return Value(); - v.getDouble(d); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() != Value::NUMBER) return ValuePtr::undefined; + v->getDouble(d); } else - return Value(); + return ValuePtr::undefined; n=trunc(d); if (n < 0) { PRINTB("WARNING: Negative parent module index (%d) not allowed", n); - return Value(); + return ValuePtr::undefined; } if (n >= s) { PRINTB("WARNING: Parent module index (%d) greater than the number of modules on the stack", n); - return Value(); + return ValuePtr::undefined; } - return Value(Module::stack_element(s - 1 - n)); + return ValuePtr(Module::stack_element(s - 1 - n)); } -Value builtin_norm(const Context *, const EvalContext *evalctx) +ValuePtr builtin_norm(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &val = evalctx->getArgValue(0); - if (val.type() == Value::VECTOR) { + ValuePtr val = evalctx->getArgValue(0); + if (val->type() == Value::VECTOR) { double sum = 0; - Value::VectorType v = val.toVector(); + Value::VectorType v = val->toVector(); size_t n = v.size(); for (size_t i = 0; i < n; i++) if (v[i].type() == Value::NUMBER) { @@ -858,49 +930,49 @@ Value builtin_norm(const Context *, const EvalContext *evalctx) register double x = v[i].toDouble(); sum += x*x; } else { - PRINT(" WARNING: Incorrect arguments to norm()"); - return Value(); + PRINT("WARNING: Incorrect arguments to norm()"); + return ValuePtr::undefined; } - return Value(sqrt(sum)); + return ValuePtr(sqrt(sum)); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_cross(const Context *, const EvalContext *evalctx) +ValuePtr builtin_cross(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() != 2) { PRINT("WARNING: Invalid number of parameters for cross()"); - return Value(); + return ValuePtr::undefined; } - Value arg0 = evalctx->getArgValue(0); - Value arg1 = evalctx->getArgValue(1); - if ((arg0.type() != Value::VECTOR) || (arg1.type() != Value::VECTOR)) { + ValuePtr arg0 = evalctx->getArgValue(0); + ValuePtr arg1 = evalctx->getArgValue(1); + if ((arg0->type() != Value::VECTOR) || (arg1->type() != Value::VECTOR)) { PRINT("WARNING: Invalid type of parameters for cross()"); - return Value(); + return ValuePtr::undefined; } - Value::VectorType v0 = arg0.toVector(); - Value::VectorType v1 = arg1.toVector(); + Value::VectorType v0 = arg0->toVector(); + Value::VectorType v1 = arg1->toVector(); if ((v0.size() != 3) || (v1.size() != 3)) { PRINT("WARNING: Invalid vector size of parameter for cross()"); - return Value(); + return ValuePtr::undefined; } for (unsigned int a = 0;a < 3;a++) { if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) { PRINT("WARNING: Invalid value in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } double d0 = v0[a].toDouble(); double d1 = v1[a].toDouble(); if (boost::math::isnan(d0) || boost::math::isnan(d1)) { PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } if (boost::math::isinf(d0) || boost::math::isinf(d1)) { PRINT("WARNING: Invalid value (INF) in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } } @@ -912,7 +984,7 @@ Value builtin_cross(const Context *, const EvalContext *evalctx) result.push_back(Value(x)); result.push_back(Value(y)); result.push_back(Value(z)); - return Value(result); + return ValuePtr(result); } void register_builtin_functions() diff --git a/src/function.h b/src/function.h index d8e3ad77..c9449c74 100644 --- a/src/function.h +++ b/src/function.h @@ -7,45 +7,47 @@ #include #include - class AbstractFunction { private: - const Feature *feature; + const Feature *feature; public: - AbstractFunction() : feature(NULL) {} - AbstractFunction(const Feature& feature) : feature(&feature) {} + AbstractFunction() : feature(NULL) {} + AbstractFunction(const Feature& feature) : feature(&feature) {} virtual ~AbstractFunction(); - virtual bool is_experimental() const { return feature != NULL; } - virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); } - virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const; + virtual bool is_experimental() const { return feature != NULL; } + virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); } + virtual ValuePtr evaluate(const class Context *ctx, const class EvalContext *evalctx) const; virtual std::string dump(const std::string &indent, const std::string &name) const; }; class BuiltinFunction : public AbstractFunction { public: - typedef Value (*eval_func_t)(const Context *ctx, const EvalContext *evalctx); + typedef ValuePtr (*eval_func_t)(const Context *ctx, const EvalContext *evalctx); eval_func_t eval_func; BuiltinFunction(eval_func_t f) : eval_func(f) { } BuiltinFunction(eval_func_t f, const Feature& feature) : AbstractFunction(feature), eval_func(f) { } virtual ~BuiltinFunction(); - virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const; + virtual ValuePtr evaluate(const Context *ctx, const EvalContext *evalctx) const; virtual std::string dump(const std::string &indent, const std::string &name) const; }; class Function : public AbstractFunction { public: + std::string name; AssignmentList definition_arguments; Expression *expr; - Function() { } + Function(const char *name, AssignmentList &definition_arguments, Expression *expr); virtual ~Function(); - virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const; + virtual ValuePtr evaluate(const Context *ctx, const EvalContext *evalctx) const; virtual std::string dump(const std::string &indent, const std::string &name) const; + + static Function * create(const char *name, AssignmentList &definition_arguments, Expression *expr); }; diff --git a/src/grid.h b/src/grid.h index 23690b3f..f9fe4d0e 100644 --- a/src/grid.h +++ b/src/grid.h @@ -1,6 +1,8 @@ #pragma once #include "mathc99.h" +#include "linalg.h" + #ifdef WIN32 typedef __int64 int64_t; #else @@ -96,10 +98,10 @@ public: res = resolution; } - T &align(double &x, double &y, double &z) { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - int64_t iz = (int64_t)round(z / res); + T &align(Vector3d &v) { + int64_t ix = (int64_t)round(v[0] / res); + int64_t iy = (int64_t)round(v[1] / res); + int64_t iz = (int64_t)round(v[2] / res); if (db.find(std::make_pair(std::make_pair(ix, iy), iz)) == db.end()) { int dist = 10; for (int64_t jx = ix - 1; jx <= ix + 1; jx++) { @@ -118,14 +120,14 @@ public: } } } - x = ix * res, y = iy * res, z = iz * res; + v[0] = ix * res, v[1] = iy * res, v[2] = iz * res; return db[std::make_pair(std::make_pair(ix, iy), iz)]; } - bool has(double x, double y, double z) { - int64_t ix = (int64_t)round(x / res); - int64_t iy = (int64_t)round(y / res); - int64_t iz = (int64_t)round(z / res); + bool has(const Vector3d &v) { + int64_t ix = (int64_t)round(v[0] / res); + int64_t iy = (int64_t)round(v[1] / res); + int64_t iz = (int64_t)round(v[2] / res); if (db.find(std::make_pair(std::make_pair(ix, iy), iz)) != db.end()) return true; for (int64_t jx = ix - 1; jx <= ix + 1; jx++) @@ -137,19 +139,8 @@ public: return false; } - bool eq(double x1, double y1, double z1, double x2, double y2, double z2) { - align(x1, y1, z1); - align(x2, y2, z2); - if (fabs(x1 - x2) < res && fabs(y1 - y2) < res && fabs(z1 - z2) < res) - return true; - return false; + T &data(Vector3d v) { + return align(v); } - T &data(double x, double y, double z) { - return align(x, y, z); - } - - T &operator()(double x, double y, double z) { - return align(x, y, z); - } }; diff --git a/src/import.cc b/src/import.cc index ad667592..ddc4a950 100644 --- a/src/import.cc +++ b/src/import.cc @@ -91,18 +91,18 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti c.dump(this, inst); #endif - Value v = c.lookup_variable("file"); - if (v.isUndefined()) { + ValuePtr v = c.lookup_variable("file"); + if (v->isUndefined()) { v = c.lookup_variable("filename"); - if (!v.isUndefined()) { + if (!v->isUndefined()) { printDeprecation("DEPRECATED: filename= is deprecated. Please use file="); } } - std::string filename = lookup_file(v.isUndefined() ? "" : v.toString(), inst->path(), ctx->documentPath()); + std::string filename = lookup_file(v->isUndefined() ? "" : v->toString(), inst->path(), ctx->documentPath()); import_type_e actualtype = this->type; if (actualtype == TYPE_UNKNOWN) { - std::string extraw = boosty::extension_str( fs::path(filename) ); - std::string ext = boost::algorithm::to_lower_copy( extraw ); + std::string extraw = boosty::extension_str(fs::path(filename)); + std::string ext = boost::algorithm::to_lower_copy(extraw); if (ext == ".stl") actualtype = TYPE_STL; else if (ext == ".off") actualtype = TYPE_OFF; else if (ext == ".dxf") actualtype = TYPE_DXF; @@ -110,31 +110,30 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti ImportNode *node = new ImportNode(inst, actualtype); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); node->filename = filename; - Value layerval = c.lookup_variable("layer", true); + Value layerval = *c.lookup_variable("layer", true); if (layerval.isUndefined()) { - layerval = c.lookup_variable("layername"); + layerval = *c.lookup_variable("layername"); if (!layerval.isUndefined()) { printDeprecation("DEPRECATED: layername= is deprecated. Please use layer="); } } node->layername = layerval.isUndefined() ? "" : layerval.toString(); - node->convexity = c.lookup_variable("convexity", true).toDouble(); + node->convexity = c.lookup_variable("convexity", true)->toDouble(); if (node->convexity <= 0) node->convexity = 1; - Value origin = c.lookup_variable("origin", true); + ValuePtr origin = c.lookup_variable("origin", true); node->origin_x = node->origin_y = 0; - origin.getVec2(node->origin_x, node->origin_y); + origin->getVec2(node->origin_x, node->origin_y); - node->scale = c.lookup_variable("scale", true).toDouble(); + node->scale = c.lookup_variable("scale", true)->toDouble(); - if (node->scale <= 0) - node->scale = 1; + if (node->scale <= 0) node->scale = 1; return node; } diff --git a/src/launchingscreen.cc b/src/launchingscreen.cc index 0a5a0f9a..3a27f7e4 100644 --- a/src/launchingscreen.cc +++ b/src/launchingscreen.cc @@ -2,6 +2,7 @@ #include #include +#include "openscad.h" #include "launchingscreen.h" #include "ui_launchingscreen.h" @@ -26,7 +27,9 @@ LaunchingScreen::LaunchingScreen(QWidget *parent) : QDialog(parent) setupUi(this); this->setStyleSheet("QDialog {background-image:url(':/icons/background.png')}" "QPushButton {color:white;}"); - + + this->versionNumberLabel->setText(openscad_version.c_str()); + QStringList recentFiles = UIUtils::recentFiles(); for (int a = 0;a < recentFiles.size();a++) { QFileInfo fileInfo(recentFiles[a]); @@ -39,7 +42,7 @@ LaunchingScreen::LaunchingScreen(QWidget *parent) : QDialog(parent) foreach(const QString &category, UIUtils::exampleCategories()) { QFileInfoList examples = UIUtils::exampleFiles(category); - QTreeWidgetItem *categoryItem = new QTreeWidgetItem(QStringList(category)); + QTreeWidgetItem *categoryItem = new QTreeWidgetItem(QStringList(gettext(category.toStdString().c_str()))); foreach(const QFileInfo &example, examples) { @@ -75,7 +78,7 @@ QString LaunchingScreen::selectedFile() return this->selection; } -void LaunchingScreen::enableRecentButton(const QModelIndex & current, const QModelIndex & previous) +void LaunchingScreen::enableRecentButton(const QModelIndex &, const QModelIndex &) { this->openRecentButton->setEnabled(true); this->openRecentButton->setDefault(true); @@ -91,7 +94,7 @@ void LaunchingScreen::openRecent() checkOpen(item->data(Qt::UserRole)); } -void LaunchingScreen::enableExampleButton(QTreeWidgetItem *current, QTreeWidgetItem *previous) +void LaunchingScreen::enableExampleButton(QTreeWidgetItem *current, QTreeWidgetItem *) { const bool enable = current->childCount() == 0; this->openExampleButton->setEnabled(enable); diff --git a/src/launchingscreen.h b/src/launchingscreen.h index bbf764bb..d28aae34 100644 --- a/src/launchingscreen.h +++ b/src/launchingscreen.h @@ -3,6 +3,8 @@ #include #include #include + +#include "qtgettext.h" #include "ui_launchingscreen.h" class LaunchingScreen : public QDialog, public Ui::LaunchingScreen diff --git a/src/launchingscreen.ui b/src/launchingscreen.ui index 67a7d1ea..3bb2d0d8 100644 --- a/src/launchingscreen.ui +++ b/src/launchingscreen.ui @@ -6,10 +6,16 @@ 0 0 - 618 + 620 418 + + + 600 + 0 + + Welcome to OpenSCAD @@ -332,26 +338,47 @@ QPushButton:pressed { - - - true - - - - 600 - 0 - - - - false - - - Don't show again - - - false - - + + + + + true + + + false + + + Don't show again + + + false + + + + + + + Qt::Horizontal + + + + 2 + 2 + + + + + + + + Version + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + diff --git a/src/linalg.h b/src/linalg.h index 69fe316f..9e7d9f94 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -17,6 +17,9 @@ using Eigen::Matrix4d; #define Transform3d Eigen::Affine3d #define Transform2d Eigen::Affine2d +typedef std::vector Polygon; +typedef std::vector Polygons; + bool matrix_contains_infinity( const Transform3d &m ); bool matrix_contains_nan( const Transform3d &m ); diff --git a/src/linearextrude.cc b/src/linearextrude.cc index 1e191f6b..e535c28d 100644 --- a/src/linearextrude.cc +++ b/src/linearextrude.cc @@ -60,46 +60,46 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value height = c.lookup_variable("height", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); - Value center = c.lookup_variable("center", true); - Value twist = c.lookup_variable("twist", true); - Value slices = c.lookup_variable("slices", true); + ValuePtr file = c.lookup_variable("file"); + ValuePtr layer = c.lookup_variable("layer", true); + ValuePtr height = c.lookup_variable("height", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr origin = c.lookup_variable("origin", true); + ValuePtr scale = c.lookup_variable("scale", true); + ValuePtr center = c.lookup_variable("center", true); + ValuePtr twist = c.lookup_variable("twist", true); + ValuePtr slices = c.lookup_variable("slices", true); - if (!file.isUndefined() && file.type() == Value::STRING) { + if (!file->isUndefined() && file->type() == Value::STRING) { printDeprecation("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead."); - node->filename = lookup_file(file.toString(), inst->path(), c.documentPath()); + node->filename = lookup_file(file->toString(), inst->path(), c.documentPath()); } // if height not given, and first argument is a number, // then assume it should be the height. - if (c.lookup_variable("height").isUndefined() && + if (c.lookup_variable("height")->isUndefined() && evalctx->numArgs() > 0 && evalctx->getArgName(0) == "") { - const Value &val = evalctx->getArgValue(0); - if (val.type() == Value::NUMBER) height = val; + ValuePtr val = evalctx->getArgValue(0); + if (val->type() == Value::NUMBER) height = val; } - node->layername = layer.isUndefined() ? "" : layer.toString(); + node->layername = layer->isUndefined() ? "" : layer->toString(); node->height = 100; - height.getDouble(node->height); - node->convexity = (int)convexity.toDouble(); - origin.getVec2(node->origin_x, node->origin_y); + height->getDouble(node->height); + node->convexity = (int)convexity->toDouble(); + origin->getVec2(node->origin_x, node->origin_y); node->scale_x = node->scale_y = 1; - scale.getDouble(node->scale_x); - scale.getDouble(node->scale_y); - scale.getVec2(node->scale_x, node->scale_y); + scale->getDouble(node->scale_x); + scale->getDouble(node->scale_y); + scale->getVec2(node->scale_x, node->scale_y); - if (center.type() == Value::BOOL) - node->center = center.toBool(); + if (center->type() == Value::BOOL) + node->center = center->toBool(); if (node->height <= 0) node->height = 0; @@ -109,10 +109,10 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI if (node->scale_x < 0) node->scale_x = 0; if (node->scale_y < 0) node->scale_y = 0; - if (slices.type() == Value::NUMBER) node->slices = (int)slices.toDouble(); + if (slices->type() == Value::NUMBER) node->slices = (int)slices->toDouble(); - if (twist.type() == Value::NUMBER) { - node->twist = twist.toDouble(); + if (twist->type() == Value::NUMBER) { + node->twist = twist->toDouble(); if (node->twist != 0.0) { if (node->slices == 0) { node->slices = (int)fmax(2, fabs(Calc::get_fragments_from_r(node->height, diff --git a/src/mainwin.cc b/src/mainwin.cc index 776a46ed..67bc93a8 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -24,6 +24,7 @@ * */ #include +#include "openscad.h" #include "GeometryCache.h" #include "ModuleCache.h" #include "MainWindow.h" @@ -37,10 +38,12 @@ #include "highlighter.h" #include "export.h" #include "builtin.h" +#include "memory.h" #include "expression.h" #include "progress.h" #include "dxfdim.h" #include "legacyeditor.h" +#include "settings.h" #ifdef USE_SCINTILLA_EDITOR #include "scintillaeditor.h" #endif @@ -124,15 +127,6 @@ QSet *MainWindow::getWindows() // Global application state unsigned int GuiLocker::gui_locked = 0; -#define QUOTE(x__) # x__ -#define QUOTED(x__) QUOTE(x__) - -static char helptitle[] = - "OpenSCAD " QUOTED(OPENSCAD_VERSION) -#ifdef OPENSCAD_COMMIT - " (git " QUOTED(OPENSCAD_COMMIT) ")" -#endif - "\nhttp://www.openscad.org\n\n"; static char copyrighttext[] = "Copyright (C) 2009-2014 The OpenSCAD Developers\n" "\n" @@ -189,6 +183,9 @@ MainWindow::MainWindow(const QString &filename) this->consoleDock->setConfigKey("view/hideConsole"); this->consoleDock->setAction(this->viewActionHideConsole); + this->versionLabel = NULL; // must be initialized before calling updateStatusBar() + updateStatusBar(NULL); + QSettings settings; editortype = settings.value("editor/editortype").toString(); useScintilla = (editortype != "Simple Editor"); @@ -196,6 +193,7 @@ MainWindow::MainWindow(const QString &filename) #ifdef USE_SCINTILLA_EDITOR if (useScintilla) { editor = new ScintillaEditor(editorDockContents); + } else #endif @@ -203,6 +201,13 @@ MainWindow::MainWindow(const QString &filename) Preferences::create(editor->colorSchemes()); +#ifdef USE_SCINTILLA_EDITOR + if (useScintilla) { + connect(Preferences::inst(), SIGNAL(editorConfigChanged()), editor, SLOT(applySettings())); + Preferences::inst()->fireEditorConfigChanged(); + } +#endif + editorDockContents->layout()->addWidget(editor); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); @@ -407,6 +412,7 @@ MainWindow::MainWindow(const QString &filename) setCurrentOutput(); + std::string helptitle = openscad_version + "\nhttp://www.openscad.org\n\n"; PRINT(helptitle); PRINT(copyrighttext); PRINT(""); @@ -683,11 +689,16 @@ MainWindow::~MainWindow() #endif delete this->thrownTogetherRenderer; MainWindow::getWindows()->remove(this); + if (MainWindow::getWindows()->size() == 0) { + // Quit application even in case some other windows like + // Preferences are still open. + qApp->quit(); + } } void MainWindow::showProgress() { - this->statusBar()->addPermanentWidget(qobject_cast(sender())); + updateStatusBar(qobject_cast(sender())); } void MainWindow::report_func(const class AbstractNode*, void *vp, int mark) @@ -765,7 +776,7 @@ void MainWindow::setFileName(const QString &filename) { if (filename.isEmpty()) { this->fileName.clear(); - setWindowFilePath("untitled.scad"); + setWindowFilePath(_("Untitled.scad")); this->top_ctx.setDocumentPath(currentdir); } else { @@ -862,6 +873,8 @@ void MainWindow::compile(bool reload, bool forcedone) bool shouldcompiletoplevel = false; bool didcompile = false; + this->renderingTime.start(); + // Reload checks the timestamp of the toplevel file and refreshes if necessary, if (reload) { // Refresh files if it has changed on disk @@ -938,6 +951,7 @@ void MainWindow::compileDone(bool didchange) const char *callslot; if (didchange) { instantiateRoot(); + updateCamera(); callslot = afterCompileSlot; } else { @@ -1034,9 +1048,6 @@ void MainWindow::compileCSG(bool procevents) if (procevents) QApplication::processEvents(); // Main CSG evaluation - QTime t; - t.start(); - this->progresswidget = new ProgressWidget(this); connect(this->progresswidget, SIGNAL(requestShow()), this, SLOT(showProgress())); @@ -1050,9 +1061,6 @@ void MainWindow::compileCSG(bool procevents) CSGTermEvaluator csgrenderer(this->tree, &geomevaluator); if (procevents) QApplication::processEvents(); this->root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); - if (!root_raw_term && background_terms.empty()) { - PRINT("ERROR: CSG generation failed! (no objects found)"); - } GeometryCache::instance()->print(); #ifdef ENABLE_CGAL CGALCache::instance()->print(); @@ -1063,9 +1071,7 @@ void MainWindow::compileCSG(bool procevents) PRINT("CSG generation cancelled."); } progress_report_fin(); - this->statusBar()->removeWidget(this->progresswidget); - delete this->progresswidget; - this->progresswidget = NULL; + updateStatusBar(NULL); PRINT("Compiling design (CSG Products normalization)..."); if (procevents) QApplication::processEvents(); @@ -1125,8 +1131,8 @@ void MainWindow::compileCSG(bool procevents) this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain, this->highlights_chain, this->background_chain); - PRINT("CSG generation finished."); - int s = t.elapsed() / 1000; + PRINT("Compile and preview finished."); + int s = this->renderingTime.elapsed() / 1000; PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60)); if (procevents) QApplication::processEvents(); } @@ -1197,7 +1203,7 @@ void MainWindow::show_examples() foreach (const QString &cat, UIUtils::exampleCategories()) { QFileInfoList examples = UIUtils::exampleFiles(cat); - QMenu *menu = this->menuExamples->addMenu(cat); + QMenu *menu = this->menuExamples->addMenu(gettext(cat.toStdString().c_str())); foreach(const QFileInfo &ex, examples) { QAction *openAct = new QAction(ex.fileName(), this); @@ -1235,7 +1241,7 @@ void MainWindow::writeBackup(QFile *file) writer.setCodec("UTF-8"); writer << this->editor->toPlainText(); - PRINTB("Saved backup file: %s", file->fileName().toLocal8Bit().constData()); + PRINTB("Saved backup file: %s", file->fileName().toUtf8().constData()); } void MainWindow::saveBackup() @@ -1246,7 +1252,7 @@ void MainWindow::saveBackup() return; } - QString backupPath = QString::fromStdString(path); + QString backupPath = QString::fromLocal8Bit(path.c_str()); if (!backupPath.endsWith("/")) backupPath.append("/"); QString basename = "unsaved"; @@ -1299,9 +1305,9 @@ void MainWindow::actionSave() void MainWindow::actionSaveAs() { - QString new_filename = QFileDialog::getSaveFileName(this, "Save File", - this->fileName.isEmpty()?"Untitled.scad":this->fileName, - "OpenSCAD Designs (*.scad)"); + QString new_filename = QFileDialog::getSaveFileName(this, _("Save File"), + this->fileName.isEmpty()?_("Untitled.scad"):this->fileName, + _("OpenSCAD Designs (*.scad)")); if (!new_filename.isEmpty()) { if (QFileInfo(new_filename).suffix().isEmpty()) { new_filename.append(".scad"); @@ -1311,7 +1317,7 @@ void MainWindow::actionSaveAs() QFileInfo info(new_filename); if (info.exists()) { if (QMessageBox::warning(this, windowTitle(), - tr("%1 already exists.\nDo you want to replace it?").arg(info.fileName()), + QString(_("%1 already exists.\nDo you want to replace it?")).arg(info.fileName()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { return; } @@ -1485,7 +1491,7 @@ bool MainWindow::eventFilter(QObject* obj, QEvent *event) void MainWindow::updateTemporalVariables() { - this->top_ctx.set_variable("$t", Value(this->e_tval->text().toDouble())); + this->top_ctx.set_variable("$t", ValuePtr(this->e_tval->text().toDouble())); Value::VectorType vpt; vpt.push_back(Value(-qglview->cam.object_trans.x())); @@ -1497,9 +1503,9 @@ void MainWindow::updateTemporalVariables() vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.z(), 360))); - top_ctx.set_variable("$vpr", Value(vpr)); + top_ctx.set_variable("$vpr", ValuePtr(vpr)); - top_ctx.set_variable("$vpd", Value(qglview->cam.viewer_distance)); + top_ctx.set_variable("$vpd", ValuePtr(qglview->cam.zoomValue())); } @@ -1523,36 +1529,29 @@ void MainWindow::updateCamera() double rx = cam.object_rot.x(); double ry = cam.object_rot.y(); double rz = cam.object_rot.z(); - double d = cam.viewer_distance; + double d = cam.zoomValue(); - ModuleContext mc(&top_ctx, NULL); - mc.initializeModule(*root_module); + double x, y, z; + const ValuePtr vpr = root_module->lookup_variable("$vpr"); + if (vpr->getVec3(x, y, z)) { + rx = x; + ry = y; + rz = z; + camera_set = true; + } - BOOST_FOREACH(const Assignment &a, root_module->scope.assignments) { - double x, y, z; - if ("$vpr" == a.first) { - const Value vpr = a.second.get()->evaluate(&mc); - if (vpr.getVec3(x, y, z)) { - rx = x; - ry = y; - rz = z; - camera_set = true; - } - } else if ("$vpt" == a.first) { - const Value vpt = a.second.get()->evaluate(&mc); - if (vpt.getVec3(x, y, z)) { - tx = x; - ty = y; - tz = z; - camera_set = true; - } - } else if ("$vpd" == a.first) { - const Value vpd = a.second.get()->evaluate(&mc); - if (vpd.type() == Value::NUMBER) { - d = vpd.toDouble(); - camera_set = true; - } - } + const ValuePtr vpt = root_module->lookup_variable("$vpt"); + if (vpt->getVec3(x, y, z)) { + tx = x; + ty = y; + tz = z; + camera_set = true; + } + + const ValuePtr vpd = root_module->lookup_variable("$vpd"); + if (vpd->type() == Value::NUMBER) { + d = vpd->toDouble(); + camera_set = true; } if (camera_set) { @@ -1604,17 +1603,15 @@ void MainWindow::compileTopLevelDocument() this->last_compiled_doc = editor->toPlainText(); std::string fulltext = - std::string(this->last_compiled_doc.toLocal8Bit().constData()) + + std::string(this->last_compiled_doc.toUtf8().constData()) + "\n" + commandline_commands; - + delete this->root_module; this->root_module = NULL; this->root_module = parse(fulltext.c_str(), this->fileName.isEmpty() ? "" : QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false); - - updateCamera(); } void MainWindow::checkAutoReload() @@ -1639,9 +1636,9 @@ bool MainWindow::checkEditorModified() { if (editor->isContentModified()) { QMessageBox::StandardButton ret; - ret = QMessageBox::warning(this, "Application", - "The document has been modified.\n" - "Do you really want to reload the file?", + ret = QMessageBox::warning(this, _("Application"), + _("The document has been modified.\n" + "Do you really want to reload the file?"), QMessageBox::Yes | QMessageBox::No); if (ret != QMessageBox::Yes) { designActionAutoReload->setChecked(false); @@ -1773,11 +1770,11 @@ void MainWindow::actionRenderDone(shared_ptr root_geom) CGALCache::instance()->print(); #endif - int s = this->progresswidget->elapsedTime() / 1000; + int s = this->renderingTime.elapsed() / 1000; PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60)); - if (const CGAL_Nef_polyhedron *N = dynamic_cast(root_geom.get())) { - if (!N->isEmpty()) { + if (root_geom && !root_geom->isEmpty()) { + if (const CGAL_Nef_polyhedron *N = dynamic_cast(root_geom.get())) { if (N->getDimension() == 3) { PRINT(" Top level object is a 3D object:"); PRINTB(" Simple: %6s", (N->p3->is_simple() ? "yes" : "no")); @@ -1789,16 +1786,16 @@ void MainWindow::actionRenderDone(shared_ptr root_geom) PRINTB(" Volumes: %6d", N->p3->number_of_volumes()); } } - } - else if (const PolySet *ps = dynamic_cast(root_geom.get())) { - assert(ps->getDimension() == 3); - PRINT(" Top level object is a 3D object:"); - PRINTB(" Facets: %6d", ps->numPolygons()); - } else if (const Polygon2d *poly = dynamic_cast(root_geom.get())) { - PRINT(" Top level object is a 2D object:"); - PRINTB(" Contours: %6d", poly->outlines().size()); - } else { - assert(false && "Unknown geometry type"); + else if (const PolySet *ps = dynamic_cast(root_geom.get())) { + assert(ps->getDimension() == 3); + PRINT(" Top level object is a 3D object:"); + PRINTB(" Facets: %6d", ps->numPolygons()); + } else if (const Polygon2d *poly = dynamic_cast(root_geom.get())) { + PRINT(" Top level object is a 2D object:"); + PRINTB(" Contours: %6d", poly->outlines().size()); + } else { + assert(false && "Unknown geometry type"); + } } PRINT("Rendering finished."); @@ -1812,15 +1809,47 @@ void MainWindow::actionRenderDone(shared_ptr root_geom) PRINT("WARNING: No top level geometry to render"); } - this->statusBar()->removeWidget(this->progresswidget); - delete this->progresswidget; - this->progresswidget = NULL; + updateStatusBar(NULL); + this->contentschanged = false; compileEnded(); } #endif /* ENABLE_CGAL */ +/** + * Switch version label and progress widget. When switching to the progress + * widget, the new instance is passed by the caller. + * In case of resetting back to the version label, NULL will be passed and + * multiple calls can happen. So this method must guard against adding the + * version label multiple times. + * + * @param progressWidget a pointer to the progress widget to show or NULL in + * case the display should switch back to the version label. + */ +void MainWindow::updateStatusBar(ProgressWidget *progressWidget) +{ + QStatusBar *sb = this->statusBar(); + if (progressWidget == NULL) { + if (this->progresswidget != NULL) { + sb->removeWidget(this->progresswidget); + delete this->progresswidget; + this->progresswidget = NULL; + } + if (versionLabel == NULL) { + versionLabel = new QLabel(openscad_version.c_str()); + sb->addPermanentWidget(this->versionLabel); + } + } else { + if (this->versionLabel != NULL) { + sb->removeWidget(this->versionLabel); + delete this->versionLabel; + this->versionLabel = NULL; + } + sb->addPermanentWidget(progressWidget); + } +} + void MainWindow::actionDisplayAST() { setCurrentOutput(); @@ -1830,7 +1859,7 @@ void MainWindow::actionDisplayAST() e->setWindowTitle("AST Dump"); e->setReadOnly(true); if (root_module) { - e->setPlainText(QString::fromLocal8Bit(root_module->dump("", "").c_str())); + e->setPlainText(QString::fromUtf8(root_module->dump("", "").c_str())); } else { e->setPlainText("No AST to dump. Please try compiling first..."); } @@ -1848,7 +1877,7 @@ void MainWindow::actionDisplayCSGTree() e->setWindowTitle("CSG Tree Dump"); e->setReadOnly(true); if (this->root_node) { - e->setPlainText(QString::fromLocal8Bit(this->tree.getString(*this->root_node).c_str())); + e->setPlainText(QString::fromUtf8(this->tree.getString(*this->root_node).c_str())); } else { e->setPlainText("No CSG to dump. Please try compiling first..."); } @@ -1867,11 +1896,11 @@ void MainWindow::actionDisplayCSGProducts() e->setReadOnly(true); e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n") - .arg(root_raw_term ? QString::fromLocal8Bit(root_raw_term->dump().c_str()) : "N/A", - root_norm_term ? QString::fromLocal8Bit(root_norm_term->dump().c_str()) : "N/A", - this->root_chain ? QString::fromLocal8Bit(this->root_chain->dump().c_str()) : "N/A", - highlights_chain ? QString::fromLocal8Bit(highlights_chain->dump().c_str()) : "N/A", - background_chain ? QString::fromLocal8Bit(background_chain->dump().c_str()) : "N/A")); + .arg(root_raw_term ? QString::fromUtf8(root_raw_term->dump().c_str()) : "N/A", + root_norm_term ? QString::fromUtf8(root_norm_term->dump().c_str()) : "N/A", + this->root_chain ? QString::fromUtf8(this->root_chain->dump().c_str()) : "N/A", + highlights_chain ? QString::fromUtf8(highlights_chain->dump().c_str()) : "N/A", + background_chain ? QString::fromUtf8(background_chain->dump().c_str()) : "N/A")); e->show(); e->resize(600, 400); @@ -1897,9 +1926,13 @@ void MainWindow::actionCheckValidity() { } bool valid = false; - if (const CGAL_Nef_polyhedron *N = dynamic_cast(this->root_geom.get())) - valid = N->p3->is_valid(); - + shared_ptr N; + if (const PolySet *ps = dynamic_cast(this->root_geom.get())) { + N.reset(CGALUtils::createNefPolyhedronFromGeometry(*ps)); + } + if (N || (N = dynamic_pointer_cast(this->root_geom))) { + valid = N->p3 ? N->p3->is_valid() : false; + } PRINTB(" Valid: %6s", (valid ? "yes" : "no")); clearCurrentOutput(); #endif /* ENABLE_CGAL */ @@ -1948,12 +1981,12 @@ void MainWindow::actionExport(export_type_e, QString, QString) const CGAL_Nef_polyhedron *N = dynamic_cast(this->root_geom.get()); if (N && !N->p3->is_simple()) { - PRINT("Warning: Object may not be a valid 2-manifold and may need repair! See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export"); + PRINT("WARNING: Object may not be a valid 2-manifold and may need repair! See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export"); } - QString title = QString("Export %1 File").arg(type_name); - QString filter = QString("%1 Files (*%2)").arg(type_name, suffix); - QString filename = this->fileName.isEmpty() ? QString("Untitled") + suffix : QFileInfo(this->fileName).baseName() + suffix; + QString title = QString(_("Export %1 File")).arg(type_name); + QString filter = QString(_("%1 Files (*%2)")).arg(type_name, suffix); + QString filename = this->fileName.isEmpty() ? QString(_("Untitled")) + suffix : QFileInfo(this->fileName).baseName() + suffix; QString export_filename = QFileDialog::getSaveFileName(this, title, filename, filter); if (export_filename.isEmpty()) { PRINTB("No filename specified. %s export aborted.", type_name); @@ -2008,11 +2041,11 @@ QString MainWindow::get2dExportFilename(QString format, QString extension) { return QString(); } - QString caption = QString("Export %1 File").arg(format); + QString caption = QString(_("Export %1 File")).arg(format); QString suggestion = this->fileName.isEmpty() - ? QString("Untitled%1").arg(extension) + ? QString(_("Untitled%1")).arg(extension) : QFileInfo(this->fileName).baseName() + extension; - QString filter = QString("%1 Files (*%2)").arg(format, extension); + QString filter = QString(_("%1 Files (*%2)")).arg(format, extension); QString exportFilename = QFileDialog::getSaveFileName(this, caption, suggestion, filter); if (exportFilename.isEmpty()) { PRINT("No filename specified. DXF export aborted."); @@ -2061,9 +2094,9 @@ void MainWindow::actionExportCSG() return; } - QString csg_filename = QFileDialog::getSaveFileName(this, "Export CSG File", - this->fileName.isEmpty() ? "Untitled.csg" : QFileInfo(this->fileName).baseName()+".csg", - "CSG Files (*.csg)"); + QString csg_filename = QFileDialog::getSaveFileName(this, _("Export CSG File"), + this->fileName.isEmpty() ? _("Untitled.csg") : QFileInfo(this->fileName).baseName()+".csg", + _("CSG Files (*.csg)")); if (csg_filename.isEmpty()) { PRINT("No filename specified. CSG export aborted."); @@ -2089,7 +2122,7 @@ void MainWindow::actionExportImage() setCurrentOutput(); QString img_filename = QFileDialog::getSaveFileName(this, - "Export Image", "", "PNG Files (*.png)"); + _("Export Image"), "", _("PNG Files (*.png)")); if (img_filename.isEmpty()) { PRINT("No filename specified. Image export aborted."); } else { @@ -2324,12 +2357,12 @@ void MainWindow::on_consoleDock_visibilityChanged(bool) void MainWindow::editorTopLevelChanged(bool topLevel) { - setDockWidgetTitle(editorDock, QString("Editor"), topLevel); + setDockWidgetTitle(editorDock, QString(_("Editor")), topLevel); } void MainWindow::consoleTopLevelChanged(bool topLevel) { - setDockWidgetTitle(consoleDock, QString("Console"), topLevel); + setDockWidgetTitle(consoleDock, QString(_("Console")), topLevel); } void MainWindow::setDockWidgetTitle(QDockWidget *dockWidget, QString prefix, bool topLevel) @@ -2415,7 +2448,6 @@ void MainWindow::helpAbout() qApp->setWindowIcon(QApplication::windowIcon()); AboutDialog *dialog = new AboutDialog(this); dialog->exec(); - //QMessageBox::information(this, "About OpenSCAD", QString(helptitle) + QString(copyrighttext)); } void MainWindow::helpHomepage() @@ -2456,8 +2488,8 @@ bool MainWindow::maybeSave() if (editor->isContentModified()) { QMessageBox::StandardButton ret; QMessageBox box(this); - box.setText("The document has been modified."); - box.setInformativeText("Do you want to save your changes?"); + box.setText(_("The document has been modified.")); + box.setInformativeText(_("Do you want to save your changes?")); box.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); box.setDefaultButton(QMessageBox::Save); box.setIcon(QMessageBox::Warning); @@ -2536,10 +2568,15 @@ void MainWindow::quit() void MainWindow::consoleOutput(const std::string &msg, void *userdata) { // Invoke the append function in the main thread in case the output - // originates in a worker thread. + // originates in a worker thread. MainWindow *thisp = static_cast(userdata); - QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection, - Q_ARG(QString, QString::fromLocal8Bit(msg.c_str()))); + QString qmsg = QString::fromUtf8(msg.c_str()); + if (qmsg.startsWith("WARNING:")) { + qmsg = "" + qmsg + ""; + } else if (qmsg.startsWith("ERROR:")) { + qmsg = "" + qmsg + ""; + } + QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection, Q_ARG(QString, qmsg)); if (thisp->procevents) QApplication::processEvents(); } diff --git a/src/memory.h b/src/memory.h index 1f54e4b3..86cd6aba 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,6 +1,8 @@ #pragma once #include +#include using boost::shared_ptr; +using boost::make_shared; using boost::dynamic_pointer_cast; using boost::static_pointer_cast; diff --git a/src/modcontext.cc b/src/modcontext.cc index 3dc711a6..04d46e60 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -24,8 +24,8 @@ void ModuleContext::evaluateAssignments(const AssignmentList &assignments) // First, assign all simple variables std::list undefined_vars; BOOST_FOREACH(const Assignment &ass, assignments) { - Value tmpval = ass.second->evaluate(this); - if (tmpval.isUndefined()) undefined_vars.push_back(ass.first); + ValuePtr tmpval = ass.second->evaluate(this); + if (tmpval->isUndefined()) undefined_vars.push_back(ass.first); else this->set_variable(ass.first, tmpval); } @@ -46,12 +46,12 @@ void ModuleContext::evaluateAssignments(const AssignmentList &assignments) boost::unordered_map::iterator found = tmpass.find(*curr); if (found != tmpass.end()) { const Expression *expr = found->second; - Value tmpval = expr->evaluate(this); + ValuePtr tmpval = expr->evaluate(this); // FIXME: it's not enough to check for undefined; // we need to check for any undefined variable in the subexpression // For now, ignore this and revisit the validity and order of variable // assignments later - if (!tmpval.isUndefined()) { + if (!tmpval->isUndefined()) { changed = true; this->set_variable(*curr, tmpval); undefined_vars.erase(curr); @@ -90,7 +90,7 @@ void ModuleContext::registerBuiltin() this->set_variable(ass.first, ass.second->evaluate(this)); } - this->set_constant("PI",Value(M_PI)); + this->set_constant("PI", ValuePtr(M_PI)); } const AbstractFunction *ModuleContext::findLocalFunction(const std::string &name) const @@ -123,7 +123,8 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co return NULL; } -Value ModuleContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +ValuePtr ModuleContext::evaluate_function(const std::string &name, + const EvalContext *evalctx) const { const AbstractFunction *foundf = findLocalFunction(name); if (foundf) return foundf->evaluate(this, evalctx); @@ -157,7 +158,7 @@ std::string ModuleContext::dump(const AbstractModule *mod, const ModuleInstantia } } } - typedef std::pair ValueMapType; + typedef std::pair ValueMapType; s << " vars:"; BOOST_FOREACH(const ValueMapType &v, constants) { s << boost::format(" %s = %s") % v.first % v.second; @@ -178,9 +179,9 @@ FileContext::FileContext(const class FileModule &module, const Context *parent) if (!module.modulePath().empty()) this->document_path = module.modulePath(); } -Value FileContext::sub_evaluate_function(const std::string &name, const EvalContext *evalctx, - - FileModule *usedmod) const +ValuePtr FileContext::sub_evaluate_function(const std::string &name, + const EvalContext *evalctx, + FileModule *usedmod) const { FileContext ctx(*usedmod, this->parent); @@ -193,7 +194,8 @@ Value FileContext::sub_evaluate_function(const std::string &name, const EvalCont return usedmod->scope.functions[name]->evaluate(&ctx, evalctx); } -Value FileContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +ValuePtr FileContext::evaluate_function(const std::string &name, + const EvalContext *evalctx) const { const AbstractFunction *foundf = findLocalFunction(name); if (foundf) return foundf->evaluate(this, evalctx); diff --git a/src/modcontext.h b/src/modcontext.h index 1a850314..400c4142 100644 --- a/src/modcontext.h +++ b/src/modcontext.h @@ -18,8 +18,8 @@ public: void initializeModule(const Module &m); void registerBuiltin(); - virtual Value evaluate_function(const std::string &name, - const EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, + const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const; @@ -45,7 +45,8 @@ class FileContext : public ModuleContext public: FileContext(const class FileModule &module, const Context *parent); virtual ~FileContext() {} - virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, + const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const; @@ -53,5 +54,7 @@ private: const FileModule::ModuleContainer &usedlibs; // This sub_* method is needed to minimize stack usage only. - Value sub_evaluate_function(const std::string &name, const EvalContext *evalctx, FileModule *usedmod) const; + ValuePtr sub_evaluate_function(const std::string &name, + const EvalContext *evalctx, + FileModule *usedmod) const; }; diff --git a/src/module.cc b/src/module.cc index 44c2da6b..8cf6498e 100644 --- a/src/module.cc +++ b/src/module.cc @@ -33,6 +33,8 @@ #include "function.h" #include "printutils.h" #include "parsersettings.h" +#include "exceptions.h" +#include "stackcheck.h" #include namespace fs = boost::filesystem; @@ -59,14 +61,14 @@ AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstan double AbstractModule::lookup_double_variable_with_default(Context &c, std::string variable, double def) const { - const Value v = c.lookup_variable(variable, true); - return (v.type() == Value::NUMBER) ? v.toDouble() : def; + ValuePtr v = c.lookup_variable(variable, true); + return (v->type() == Value::NUMBER) ? v->toDouble() : def; } std::string AbstractModule::lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const { - const Value v = c.lookup_variable(variable, true); - return (v.type() == Value::STRING) ? v.toString() : def; + ValuePtr v = c.lookup_variable(variable, true); + return (v->type() == Value::STRING) ? v->toString() : def; } std::string AbstractModule::dump(const std::string &indent, const std::string &name) const @@ -151,8 +153,13 @@ AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const PRINT("New eval ctx:"); c.dump(NULL, this); #endif - AbstractNode *node = ctx->instantiate_module(*this, &c); // Passes c as evalctx - return node; + try { + AbstractNode *node = ctx->instantiate_module(*this, &c); // Passes c as evalctx + return node; + } catch (RecursionException &e) { + PRINT(e.what()); + return NULL; + } } std::vector ModuleInstantiation::instantiateChildren(const Context *evalctx) const @@ -171,25 +178,10 @@ Module::~Module() { } -class ModRecursionGuard -{ -public: - ModRecursionGuard(const ModuleInstantiation &inst) : inst(inst) { - inst.recursioncount++; - } - ~ModRecursionGuard() { - inst.recursioncount--; - } - bool recursion_detected() const { return (inst.recursioncount > 1000); } -private: - const ModuleInstantiation &inst; -}; - AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { - ModRecursionGuard g(*inst); - if (g.recursion_detected()) { - PRINTB("ERROR: Recursion detected calling module '%s'", inst->name()); + if (StackCheck::inst()->check()) { + throw RecursionException("module", inst->name()); return NULL; } @@ -199,9 +191,9 @@ AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation ModuleContext c(ctx, evalctx); // set $children first since we might have variables depending on it - c.set_variable("$children", Value(double(inst->scope.children.size()))); + c.set_variable("$children", ValuePtr(double(inst->scope.children.size()))); module_stack.push_back(inst->name()); - c.set_variable("$parent_modules", Value(double(module_stack.size()))); + c.set_variable("$parent_modules", ValuePtr(double(module_stack.size()))); c.initializeModule(*this); // FIXME: Set document path to the path of the module #if 0 && DEBUG @@ -238,6 +230,11 @@ std::string Module::dump(const std::string &indent, const std::string &name) con return dump.str(); } +FileModule::~FileModule() +{ + delete context; +} + void FileModule::registerUse(const std::string path) { std::string extraw = boosty::extension_str(fs::path(path)); std::string ext = boost::algorithm::to_lower_copy(extraw); @@ -346,19 +343,36 @@ bool FileModule::handleDependencies() return somethingchanged; } -AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const +AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) { assert(evalctx == NULL); - FileContext c(*this, ctx); - c.initializeModule(*this); + + delete context; + context = new FileContext(*this, ctx); + context->initializeModule(*this); + // FIXME: Set document path to the path of the module #if 0 && DEBUG c.dump(this, inst); #endif AbstractNode *node = new AbstractNode(inst); - std::vector instantiatednodes = this->scope.instantiateChildren(&c); - node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); + try { + std::vector instantiatednodes = this->scope.instantiateChildren(context); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); + } + catch (RecursionException &e) { + PRINT(e.what()); + } return node; } + +ValuePtr FileModule::lookup_variable(const std::string &name) const +{ + if (!context) { + return ValuePtr::undefined; + } + + return context->lookup_variable(name, true); +} diff --git a/src/module.h b/src/module.h index 8d1f1e13..504eb2a8 100644 --- a/src/module.h +++ b/src/module.h @@ -18,7 +18,7 @@ class ModuleInstantiation { public: ModuleInstantiation(const std::string &name = "") - : tag_root(false), tag_highlight(false), tag_background(false), recursioncount(0), modname(name) { } + : tag_root(false), tag_highlight(false), tag_background(false), modname(name) { } virtual ~ModuleInstantiation(); virtual std::string dump(const std::string &indent) const; @@ -40,7 +40,6 @@ public: bool tag_root; bool tag_highlight; bool tag_background; - mutable int recursioncount; protected: std::string modname; std::string modpath; @@ -99,8 +98,8 @@ private: class FileModule : public Module { public: - FileModule() : is_handling_dependencies(false) {} - virtual ~FileModule() {} + FileModule() : context(NULL), is_handling_dependencies(false) {} + virtual ~FileModule(); void setModulePath(const std::string &path) { this->path = path; } const std::string &modulePath() const { return this->path; } @@ -108,14 +107,17 @@ public: void registerInclude(const std::string &localpath, const std::string &fullpath); bool includesChanged() const; bool handleDependencies(); - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL); bool hasIncludes() const { return !this->includes.empty(); } bool usesLibraries() const { return !this->usedlibs.empty(); } bool isHandlingDependencies() const { return this->is_handling_dependencies; } + ValuePtr lookup_variable(const std::string &name) const; typedef boost::unordered_set ModuleContainer; ModuleContainer usedlibs; private: + /** Reference to retain the context that was used in the last evaluation */ + class FileContext *context; struct IncludeFile { std::string filename; bool valid; diff --git a/src/offset.cc b/src/offset.cc index faf3a38c..d8be76e6 100644 --- a/src/offset.cc +++ b/src/offset.cc @@ -54,40 +54,35 @@ AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstanti OffsetNode *node = new OffsetNode(inst); AssignmentList args; - args += Assignment("delta"); + args += Assignment("r"); Context c(ctx); c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value delta = c.lookup_variable("delta"); + // default with no argument at all is (r = 1, chamfer = false) + // radius takes precedence if both r and delta are given. node->delta = 1; - delta.getDouble(node->delta); + node->chamfer = false; + node->join_type = ClipperLib::jtRound; + const ValuePtr r = c.lookup_variable("r", true); + const ValuePtr delta = c.lookup_variable("delta", true); + const ValuePtr chamfer = c.lookup_variable("chamfer", true); - Value miter_limit = c.lookup_variable("miter_limit", true); - node->miter_limit = 2; - miter_limit.getDouble(node->miter_limit); - - Value join_type = c.lookup_variable("join_type", true); - if (join_type.type() == Value::STRING) { - std::string jt = join_type.toString(); - if (std::string("bevel") == jt) { - node->join_type = ClipperLib::jtSquare; - } else if (std::string("round") == jt) { - node->join_type = ClipperLib::jtRound; - } else if (std::string("miter") == jt) { - node->join_type = ClipperLib::jtMiter; - } else { - PRINTB("WARNING: Unknown join_type for offset(): '%s'", jt); - } - - if ((node->join_type != ClipperLib::jtMiter) && !miter_limit.isUndefined()) { - PRINTB("WARNING: miter_limit is ignored in offset() for join_type: '%s'", jt); - } + if (r->isDefinedAs(Value::NUMBER)) { + r->getDouble(node->delta); + } else if (delta->isDefinedAs(Value::NUMBER)) { + delta->getDouble(node->delta); + + node->join_type = ClipperLib::jtMiter; + if (chamfer->isDefinedAs(Value::BOOL) && chamfer->toBool()) { + node->chamfer = true; + node->join_type = ClipperLib::jtSquare; + } } std::vector instantiatednodes = inst->instantiateChildren(evalctx); @@ -100,21 +95,17 @@ std::string OffsetNode::toString() const { std::stringstream stream; - stream << this->name() - << "(delta = " << std::dec << this->delta - << ", join_type = \"" - << (this->join_type == ClipperLib::jtSquare - ? "bevel" - : this->join_type == ClipperLib::jtRound - ? "round" - : "miter") << "\""; - if (this->join_type == ClipperLib::jtMiter) { - stream << ", miter_limit = " << this->miter_limit; + bool isRadius = this->join_type == ClipperLib::jtRound; + const char *var = isRadius ? "(r = " : "(delta = "; + + stream << this->name() << var << std::dec << this->delta; + if (!isRadius) { + stream << ", chamfer = " << (this->chamfer ? "true" : "false"); } stream << ", $fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; - + return stream.str(); } diff --git a/src/offsetnode.h b/src/offsetnode.h index be651903..d8981fd4 100644 --- a/src/offsetnode.h +++ b/src/offsetnode.h @@ -8,13 +8,15 @@ class OffsetNode : public AbstractPolyNode { public: - OffsetNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi), fn(0), fs(0), fa(0), delta(1), miter_limit(2.0), join_type(ClipperLib::jtMiter) { } + OffsetNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi), fn(0), fs(0), fa(0), delta(1), miter_limit(1000000.0), join_type(ClipperLib::jtRound) { } virtual Response accept(class State &state, Visitor &visitor) const { return visitor.visit(state, *this); } virtual std::string toString() const; virtual std::string name() const { return "offset"; } - double fn, fs, fa, delta, miter_limit; + bool chamfer; + double fn, fs, fa, delta; + double miter_limit; // currently fixed high value to disable chamfers with jtMiter ClipperLib::JoinType join_type; }; diff --git a/src/openscad.cc b/src/openscad.cc index bbd6c265..ab7e3acf 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -39,7 +39,9 @@ #include "PlatformUtils.h" #include "LibraryInfo.h" #include "nodedumper.h" +#include "stackcheck.h" #include "CocoaUtils.h" +#include "FontCache.h" #include #include @@ -88,6 +90,17 @@ std::string currentdir; static bool arg_info = false; static std::string arg_colorscheme; +#define QUOTE(x__) # x__ +#define QUOTED(x__) QUOTE(x__) + +std::string openscad_versionnumber = QUOTED(OPENSCAD_VERSION) +#ifdef OPENSCAD_COMMIT + " (git " QUOTED(OPENSCAD_COMMIT) ")" +#endif +; + +std::string openscad_version = "OpenSCAD " + openscad_versionnumber; + class Echostream : public std::ofstream { public: @@ -103,7 +116,7 @@ public: } }; -static void help(const char *progname) +static void help(const char *progname, bool failure = false) { int tablen = strlen(progname)+8; char tabstr[tablen+1]; @@ -112,6 +125,7 @@ static void help(const char *progname) PRINTB("Usage: %1% [ -o output_file [ -d deps_file ] ]\\\n" "%2%[ -m make_command ] [ -D var=val [..] ] \\\n" + "%2%[ --help ] print this help message and exit \\\n" "%2%[ --version ] [ --info ] \\\n" "%2%[ --camera=translatex,y,z,rotx,y,z,dist | \\\n" "%2% --camera=eyex,y,z,centerx,y,z ] \\\n" @@ -130,15 +144,15 @@ static void help(const char *progname) #endif "%2%filename\n", progname % (const char *)tabstr); - exit(1); + exit(failure ? 1 : 0); } #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) static void version() { - PRINTB("OpenSCAD version %s\n", TOSTRING(OPENSCAD_VERSION)); - exit(1); + PRINTB("OpenSCAD version %s", TOSTRING(OPENSCAD_VERSION)); + exit(0); } static void info() @@ -158,6 +172,25 @@ static void info() exit(0); } +/** + * Initialize gettext. This must be called after the appliation path was + * determined so we can lookup the resource path for the language translation + * files. + */ +void localization_init() { + fs::path po_dir(PlatformUtils::resourcePath("locale")); + std::string locale_path(po_dir.string()); + + if (fs::is_directory(locale_path)) { + setlocale(LC_ALL, ""); + bindtextdomain("openscad", locale_path.c_str()); + bind_textdomain_codeset("openscad", "UTF-8"); + textdomain("openscad"); + } else { + PRINT("Could not initialize localization."); + } +} + Camera get_camera(po::variables_map vm) { Camera camera; @@ -282,7 +315,9 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c const std::string application_path = boosty::stringy(boosty::absolute(boost::filesystem::path(argv[0]).parent_path())); #endif PlatformUtils::registerApplicationPath(application_path); - parser_init(PlatformUtils::applicationPath()); + parser_init(); + localization_init(); + Tree tree; #ifdef ENABLE_CGAL GeometryEvaluator geomevaluator(tree); @@ -422,10 +457,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c // echo or OpenCSG png -> don't necessarily need geometry evaluation } else { root_geom = geomevaluator.evaluateGeometry(*tree.root(), true); - if (!root_geom) { - PRINT("No top-level object found."); - return 1; - } + if (!root_geom) root_geom.reset(new CGAL_Nef_polyhedron()); if (renderer == Render::CGAL && root_geom->getDimension() == 3) { const CGAL_Nef_polyhedron *N = dynamic_cast(root_geom.get()); if (!N) { @@ -517,6 +549,7 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets) #endif // MINGW64/MINGW32/MSCVER #include "MainWindow.h" #include "launchingscreen.h" +#include "qsettings.h" #ifdef __APPLE__ #include "EventFilter.h" #endif @@ -524,6 +557,10 @@ Q_IMPORT_PLUGIN(qtaccessiblewidgets) #include #include #include +#include +#include +#include +#include Q_DECLARE_METATYPE(shared_ptr); @@ -553,6 +590,33 @@ bool QtUseGUI() return useGUI; } +void dialogThreadFunc(FontCacheInitializer *initializer) +{ + initializer->run(); +} + +void dialogInitHandler(FontCacheInitializer *initializer, void *) +{ + QProgressDialog dialog; + dialog.setLabelText(_("Fontconfig needs to update its font cache.\nThis can take up to a couple of minutes.")); + dialog.setMinimum(0); + dialog.setMaximum(0); + dialog.setCancelButton(0); + + QFutureWatcher futureWatcher; + QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); + QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); + QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int,int)), &dialog, SLOT(setRange(int,int))); + QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); + + QFuture future = QtConcurrent::run(boost::bind(dialogThreadFunc, initializer)); + futureWatcher.setFuture(future); + + dialog.exec(); + + futureWatcher.waitForFinished(); +} + int gui(vector &inputFiles, const fs::path &original_path, int argc, char ** argv) { #ifdef Q_OS_MACX @@ -563,6 +627,9 @@ int gui(vector &inputFiles, const fs::path &original_path, int argc, cha } #endif QApplication app(argc, argv, true); //useGUI); + // remove ugly frames in the QStatusBar when using additional widgets + app.setStyleSheet("QStatusBar::item { border: 0px solid black; }"); + #ifdef Q_OS_MAC app.installEventFilter(new EventFilter(&app)); #endif @@ -573,6 +640,8 @@ int gui(vector &inputFiles, const fs::path &original_path, int argc, cha QCoreApplication::setApplicationVersion(TOSTRING(OPENSCAD_VERSION)); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QGuiApplication::setApplicationDisplayName("OpenSCAD"); +#else + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); #endif // Other global settings @@ -581,7 +650,14 @@ int gui(vector &inputFiles, const fs::path &original_path, int argc, cha const QString &app_path = app.applicationDirPath(); PlatformUtils::registerApplicationPath(app_path.toLocal8Bit().constData()); - parser_init(PlatformUtils::applicationPath()); + FontCache::registerProgressHandler(dialogInitHandler); + + parser_init(); + + QSettings settings; + if (settings.value("advanced/localization", true).toBool()) { + localization_init(); + } #ifdef Q_OS_MAC installAppleEventHandlers(); @@ -594,14 +670,20 @@ int gui(vector &inputFiles, const fs::path &original_path, int argc, cha updater->init(); #endif + QGLFormat fmt; #if 0 /*** disabled by clifford wolf: adds rendering artefacts with OpenCSG ***/ // turn on anti-aliasing - QGLFormat f; - f.setSampleBuffers(true); - f.setSamples(4); - QGLFormat::setDefaultFormat(f); + fmt.setSampleBuffers(true); + fmt.setSamples(4); #endif - + // The default SwapInterval causes very bad interactive behavior as + // waiting for the buffer swap seems to block mouse events. So the + // effect is that we can process mouse events at the frequency of + // the screen retrace interval causing them to queue up. + // (see https://bugreports.qt-project.org/browse/QTBUG-39370 + fmt.setSwapInterval(0); + QGLFormat::setDefaultFormat(fmt); + set_render_color_scheme(arg_colorscheme, false); bool noInputFiles = false; @@ -610,7 +692,6 @@ int gui(vector &inputFiles, const fs::path &original_path, int argc, cha inputFiles.push_back(""); } - QSettings settings; QVariant showOnStartup = settings.value("launcher/showOnStartup"); if (noInputFiles && (showOnStartup.isNull() || showOnStartup.toBool())) { LaunchingScreen *launcher = new LaunchingScreen(); @@ -655,11 +736,14 @@ int main(int argc, char **argv) { int rc = 0; bool isGuiLaunched = getenv("GUI_LAUNCHED") != 0; + StackCheck::inst()->init(); + #ifdef Q_OS_MAC if (isGuiLaunched) set_output_handler(CocoaUtils::nslog, NULL); #else PlatformUtils::ensureStdIO(); #endif + #ifdef ENABLE_CGAL // Causes CGAL errors to abort directly instead of throwing exceptions // (which we don't catch). This gives us stack traces without rerunning in gdb. @@ -714,7 +798,7 @@ int main(int argc, char **argv) } catch(const std::exception &e) { // Catches e.g. unknown options PRINTB("%s\n", e.what()); - help(argv[0]); + help(argv[0], true); } OpenSCAD::debug = ""; @@ -742,27 +826,25 @@ int main(int argc, char **argv) if (vm.count("o")) { // FIXME: Allow for multiple output files? - if (output_file) help(argv[0]); + if (output_file) help(argv[0], true); output_file = vm["o"].as().c_str(); } if (vm.count("s")) { printDeprecation("DEPRECATED: The -s option is deprecated. Use -o instead.\n"); - if (output_file) help(argv[0]); + if (output_file) help(argv[0], true); output_file = vm["s"].as().c_str(); } if (vm.count("x")) { printDeprecation("DEPRECATED: The -x option is deprecated. Use -o instead.\n"); - if (output_file) help(argv[0]); + if (output_file) help(argv[0], true); output_file = vm["x"].as().c_str(); } if (vm.count("d")) { - if (deps_output_file) - help(argv[0]); + if (deps_output_file) help(argv[0], true); deps_output_file = vm["d"].as().c_str(); } if (vm.count("m")) { - if (make_command) - help(argv[0]); + if (make_command) help(argv[0], true); make_command = vm["m"].as().c_str(); } @@ -799,13 +881,11 @@ int main(int argc, char **argv) bool cmdlinemode = false; if (output_file) { // cmd-line mode cmdlinemode = true; - if (!inputFiles.size()) help(argv[0]); + if (!inputFiles.size()) help(argv[0], true); } if (arg_info || cmdlinemode) { - if (inputFiles.size() > 1) { - help(argv[0]); - } + if (inputFiles.size() > 1) help(argv[0], true); rc = cmdline(deps_output_file, inputFiles[0], camera, output_file, original_path, renderer, argc, argv); } else if (QtUseGUI()) { @@ -813,7 +893,7 @@ int main(int argc, char **argv) } else { PRINT("Requested GUI mode but can't open display!\n"); - help(argv[0]); + help(argv[0], true); } Builtins::instance(true); diff --git a/src/openscad.h b/src/openscad.h index 0e979659..f37f0253 100644 --- a/src/openscad.h +++ b/src/openscad.h @@ -34,3 +34,11 @@ extern std::string commandline_commands; // The CWD when application started. We shouldn't change CWD, but until we stop // doing this, use currentdir to get the original CWD. extern std::string currentdir; + +extern std::string versionnumber; + +// Just the number (might have the git commit as suffix), e.g. 2014.12.23. +extern std::string openscad_versionnumber; + +// The string "OpenSCAD " and the version number. +extern std::string openscad_version; diff --git a/src/parser.y b/src/parser.y index a7a1008b..73c07504 100644 --- a/src/parser.y +++ b/src/parser.y @@ -166,9 +166,7 @@ statement: } | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr { - Function *func = new Function(); - func->definition_arguments = *$4; - func->expr = $8; + Function *func = Function::create($2, *$4, $8); scope_stack.top()->functions[$2] = func; free($2); delete $4; @@ -302,70 +300,55 @@ single_module_instantiation: expr: TOK_TRUE { - $$ = new Expression(Value(true)); + $$ = new ExpressionConst(ValuePtr(true)); } | TOK_FALSE { - $$ = new Expression(Value(false)); + $$ = new ExpressionConst(ValuePtr(false)); } | TOK_UNDEF { - $$ = new Expression(Value::undefined); + $$ = new ExpressionConst(ValuePtr::undefined); } | TOK_ID { - $$ = new Expression(); - $$->type = "L"; - $$->var_name = $1; + $$ = new ExpressionLookup($1); free($1); } | expr '.' TOK_ID { - $$ = new Expression("N", $1); - $$->var_name = $3; + $$ = new ExpressionMember($1, $3); free($3); } | TOK_STRING { - $$ = new Expression(Value(std::string($1))); + $$ = new ExpressionConst(ValuePtr(std::string($1))); free($1); } | TOK_NUMBER { - $$ = new Expression(Value($1)); + $$ = new ExpressionConst(ValuePtr($1)); } | TOK_LET '(' arguments_call ')' expr %prec LET { - $$ = new Expression(); - $$->type = "l"; - $$->call_arguments = *$3; + $$ = new ExpressionLet(*$3, $5); delete $3; - $$->children.push_back($5); } | '[' expr ':' expr ']' { - $$ = new Expression(); - $$->type = "R"; - $$->children.push_back($2); - $$->children.push_back($4); + $$ = new ExpressionRange($2, $4); } | '[' expr ':' expr ':' expr ']' { - $$ = new Expression(); - $$->type = "R"; - $$->children.push_back($2); - $$->children.push_back($4); - $$->children.push_back($6); + $$ = new ExpressionRange($2, $4, $6); } | '[' list_comprehension_elements ']' { - $$ = new Expression(); - $$->type = "i"; - $$->children.push_back($2); + $$ = new ExpressionLcExpression($2); } | '[' optional_commas ']' { - $$ = new Expression(Value(Value::VectorType())); + $$ = new ExpressionConst(ValuePtr(Value::VectorType())); } | '[' vector_expr optional_commas ']' { @@ -373,55 +356,55 @@ expr: } | expr '*' expr { - $$ = new Expression("*", $1, $3); + $$ = new ExpressionMultiply($1, $3); } | expr '/' expr { - $$ = new Expression("/", $1, $3); + $$ = new ExpressionDivision($1, $3); } | expr '%' expr { - $$ = new Expression("%", $1, $3); + $$ = new ExpressionModulo($1, $3); } | expr '+' expr { - $$ = new Expression("+", $1, $3); + $$ = new ExpressionPlus($1, $3); } | expr '-' expr { - $$ = new Expression("-", $1, $3); + $$ = new ExpressionMinus($1, $3); } | expr '<' expr { - $$ = new Expression("<", $1, $3); + $$ = new ExpressionLess($1, $3); } | expr LE expr { - $$ = new Expression("<=", $1, $3); + $$ = new ExpressionLessOrEqual($1, $3); } | expr EQ expr { - $$ = new Expression("==", $1, $3); + $$ = new ExpressionEqual($1, $3); } | expr NE expr { - $$ = new Expression("!=", $1, $3); + $$ = new ExpressionNotEqual($1, $3); } | expr GE expr { - $$ = new Expression(">=", $1, $3); + $$ = new ExpressionGreaterOrEqual($1, $3); } | expr '>' expr { - $$ = new Expression(">", $1, $3); + $$ = new ExpressionGreater($1, $3); } | expr AND expr { - $$ = new Expression("&&", $1, $3); + $$ = new ExpressionLogicalAnd($1, $3); } | expr OR expr { - $$ = new Expression("||", $1, $3); + $$ = new ExpressionLogicalOr($1, $3); } | '+' expr { @@ -429,11 +412,11 @@ expr: } | '-' expr { - $$ = new Expression("I", $2); + $$ = new ExpressionInvert($2); } | '!' expr { - $$ = new Expression("!", $2); + $$ = new ExpressionNot($2); } | '(' expr ')' { @@ -441,22 +424,15 @@ expr: } | expr '?' expr ':' expr { - $$ = new Expression(); - $$->type = "?:"; - $$->children.push_back($1); - $$->children.push_back($3); - $$->children.push_back($5); + $$ = new ExpressionTernary($1, $3, $5); } | expr '[' expr ']' { - $$ = new Expression("[]", $1, $3); + $$ = new ExpressionArrayLookup($1, $3); } | TOK_ID '(' arguments_call ')' { - $$ = new Expression(); - $$->type = "F"; - $$->call_funcname = $1; - $$->call_arguments = *$3; + $$ = new ExpressionFunctionCall($1, *$3); free($1); delete $3; } @@ -467,9 +443,7 @@ list_comprehension_elements: be parsed as an expression) */ TOK_LET '(' arguments_call ')' list_comprehension_elements { - $$ = new Expression("c", $5); - $$->call_funcname = "let"; - $$->call_arguments = *$3; + $$ = new ExpressionLc("let", *$3, $5); delete $3; } | TOK_FOR '(' arguments_call ')' list_comprehension_elements_or_expr @@ -478,17 +452,16 @@ list_comprehension_elements: /* transform for(i=...,j=...) -> for(i=...) for(j=...) */ for (int i = $3->size()-1; i >= 0; i--) { - Expression *e = new Expression("c", $$); - e->call_funcname = "for"; - e->call_arguments.push_back((*$3)[i]); + AssignmentList arglist; + arglist.push_back((*$3)[i]); + Expression *e = new ExpressionLc("for", arglist, $$); $$ = e; } delete $3; } | TOK_IF '(' expr ')' list_comprehension_elements_or_expr { - $$ = new Expression("c", $3, $5); - $$->call_funcname = "if"; + $$ = new ExpressionLc("if", $3, $5); } ; @@ -505,7 +478,7 @@ optional_commas: vector_expr: expr { - $$ = new Expression("V", $1); + $$ = new ExpressionVector($1); } | vector_expr ',' optional_commas expr { diff --git a/src/parsersettings.cc b/src/parsersettings.cc index f8437f07..6e3f06bb 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -9,7 +9,7 @@ namespace fs = boost::filesystem; std::vector librarypath; -void add_librarydir(const std::string &libdir) +static void add_librarydir(const std::string &libdir) { librarypath.push_back(libdir); } @@ -89,7 +89,7 @@ fs::path find_valid_path(const fs::path &sourcepath, return fs::path(); } -void parser_init(const std::string &applicationpath) +void parser_init() { // Add paths from OPENSCADPATH before adding built-in paths const char *openscadpaths = getenv("OPENSCADPATH"); @@ -106,5 +106,5 @@ void parser_init(const std::string &applicationpath) add_librarydir(PlatformUtils::userLibraryPath()); #endif - add_librarydir(boosty::absolute(fs::path(PlatformUtils::resourcesPath()) / "libraries").string()); + add_librarydir(boosty::absolute(PlatformUtils::resourcePath("libraries")).string()); } diff --git a/src/parsersettings.h b/src/parsersettings.h index ab9d62bb..1b8dd53b 100644 --- a/src/parsersettings.h +++ b/src/parsersettings.h @@ -1,12 +1,17 @@ #pragma once #include -#include "boosty.h" +#include + +namespace fs = boost::filesystem; extern int parser_error_pos; -void parser_init(const std::string &applicationpath); -void add_librarydir(const std::string &libdir); +/** + * Initialize library path. + */ +void parser_init(); + fs::path search_libs(const fs::path &localpath); fs::path find_valid_path(const fs::path &sourcepath, const fs::path &localpath, diff --git a/src/polyset-utils.cc b/src/polyset-utils.cc index e830d0ae..b3bba6d7 100644 --- a/src/polyset-utils.cc +++ b/src/polyset-utils.cc @@ -2,25 +2,9 @@ #include "polyset.h" #include "Polygon2d.h" #include "printutils.h" -#include "cgal.h" - -#ifdef NDEBUG -#define PREV_NDEBUG NDEBUG -#undef NDEBUG +#ifdef ENABLE_CGAL +#include "cgalutils.h" #endif -#include -#include -#include -#ifdef PREV_NDEBUG -#define NDEBUG PREV_NDEBUG -#endif - -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Triangulation_2_filtered_projection_traits_3 Projection; -typedef CGAL::Triangulation_data_structure_2 < - CGAL::Triangulation_vertex_base_2, - CGAL::Constrained_triangulation_face_base_2 > Tds; -typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; #include @@ -32,7 +16,7 @@ namespace PolysetUtils { Polygon2d *project(const PolySet &ps) { Polygon2d *poly = new Polygon2d; - BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { + BOOST_FOREACH(const Polygon &p, ps.polygons) { Outline2d outline; BOOST_FOREACH(const Vector3d &v, p) { outline.vertices.push_back(Vector2d(v[0], v[1])); @@ -60,56 +44,34 @@ namespace PolysetUtils { faces. As of writing, our only tessellation method is triangulation using CGAL's Constrained Delaunay algorithm. This code assumes the input polyset has simple polygon faces with no holes. - The tessellation will be robust wrt. degenerate and self-intersecting + The tessellation will be robust wrt. degenerate and self-intersecting */ void tessellate_faces(const PolySet &inps, PolySet &outps) { +#ifdef ENABLE_CGAL int degeneratePolygons = 0; for (size_t i = 0; i < inps.polygons.size(); i++) { - const PolySet::Polygon pgon = inps.polygons[i]; + const Polygon pgon = inps.polygons[i]; if (pgon.size() < 3) { degeneratePolygons++; continue; } - std::vector triangles; + std::vector triangles; if (pgon.size() == 3) { triangles.push_back(pgon); } else { // Build a data structure that CGAL accepts - std::vector cgalpoints; + PolygonK cgalpoints; BOOST_FOREACH(const Vector3d &v, pgon) { - cgalpoints.push_back(K::Point_3(v[0], v[1], v[2])); + cgalpoints.push_back(Vertex3K(v[0], v[1], v[2])); } - // Calculate best guess at face normal using Newell's method - K::Vector_3 normal; - CGAL::normal_vector_newell_3(cgalpoints.begin(), cgalpoints.end(), normal); - // Pass the normal vector to the (undocumented) - // CGAL::Triangulation_2_filtered_projection_traits_3. This - // trait deals with projection from 3D to 2D using the normal - // vector as a hint, and allows for near-planar polygons to be passed to - // the Constrained Delaunay Triangulator. - Projection actualProjection(normal); - CDT cdt(actualProjection); - for (size_t i=0;i 0) PRINT("WARNING: PolySet has degenerate polygons"); +#else + assert(false); +#endif } + + bool is_approximately_convex(const PolySet &ps) { +#ifdef ENABLE_CGAL + return CGALUtils::is_approximately_convex(ps); +#else + return false; +#endif + } + } diff --git a/src/polyset-utils.h b/src/polyset-utils.h index 2bdaca03..41364f07 100644 --- a/src/polyset-utils.h +++ b/src/polyset-utils.h @@ -7,5 +7,6 @@ namespace PolysetUtils { Polygon2d *project(const PolySet &ps); void tessellate_faces(const PolySet &inps, PolySet &outps); + bool is_approximately_convex(const PolySet &ps); }; diff --git a/src/polyset.cc b/src/polyset.cc index 222bfcb2..ec31cd19 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -25,8 +25,11 @@ */ #include "polyset.h" +#include "polyset-utils.h" #include "linalg.h" #include "printutils.h" +#include "grid.h" + #include #include @@ -140,20 +143,10 @@ void PolySet::transform(const Transform3d &mat) } } -namespace CGALUtils { - extern bool is_approximately_convex(const PolySet &ps); -} - bool PolySet::is_convex() const { - if (convex) return true; + if (convex || this->isEmpty()) return true; if (!convex) return false; - -#ifdef ENABLE_CGAL - convex = CGALUtils::is_approximately_convex(*this); - return convex; -#else - return false; -#endif + return PolysetUtils::is_approximately_convex(*this); } void PolySet::resize(Vector3d newsize, const Eigen::Matrix &autosize) @@ -183,6 +176,17 @@ void PolySet::resize(Vector3d newsize, const Eigen::Matrix &autosize) this->transform(t); } +void PolySet::quantizeVertices() +{ + Grid3d grid(GRID_FINE); + BOOST_FOREACH(Polygon &p, this->polygons) { + BOOST_FOREACH(Vector3d &v, p) { + // align v to the grid + if (!grid.has(v)) grid.align(v); + } + } +} + // all GL functions grouped together here #ifndef NULLGL static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector3d &p1, const Vector3d &p2, bool e0, bool e1, bool e2, double z, bool mirrored) @@ -249,7 +253,7 @@ void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, #endif /* ENABLE_OPENCSG */ if (this->dim == 2) { // Render 2D objects 1mm thick, but differences slightly larger - double zbase = 1 + (csgmode & CSGMODE_DIFFERENCE_FLAG) * 0.1; + double zbase = 1 + ((csgmode & CSGMODE_DIFFERENCE_FLAG) ? 0.1 : 0); glBegin(GL_TRIANGLES); // Render top+bottom @@ -309,7 +313,7 @@ void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, else { // If we don't have borders, use the polygons as borders. // FIXME: When is this used? - const std::vector *borders_p = &polygons; + const Polygons *borders_p = &polygons; for (size_t i = 0; i < borders_p->size(); i++) { const Polygon *poly = &borders_p->at(i); for (size_t j = 1; j <= poly->size(); j++) { @@ -378,7 +382,7 @@ void PolySet::render_edges(Renderer::csgmode_e csgmode) const } else { // Render 2D objects 1mm thick, but differences slightly larger - double zbase = 1 + (csgmode & CSGMODE_DIFFERENCE_FLAG) * 0.1; + double zbase = 1 + ((csgmode & CSGMODE_DIFFERENCE_FLAG) ? 0.1 : 0); BOOST_FOREACH(const Outline2d &o, polygon.outlines()) { // Render top+bottom outlines diff --git a/src/polyset.h b/src/polyset.h index 91d17453..cf151dbe 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -14,8 +14,7 @@ BOOST_TRIBOOL_THIRD_STATE(unknown) class PolySet : public Geometry { public: - typedef std::vector Polygon; - std::vector polygons; + Polygons polygons; PolySet(unsigned int dim, boost::tribool convex = unknown); PolySet(const Polygon2d &origin); @@ -28,6 +27,7 @@ public: virtual bool isEmpty() const { return polygons.size() == 0; } virtual Geometry *copy() const { return new PolySet(*this); } + void quantizeVertices(); size_t numPolygons() const { return polygons.size(); } void append_poly(); void append_vertex(double x, double y, double z = 0.0); @@ -43,6 +43,7 @@ public: void resize(Vector3d newsize, const Eigen::Matrix &autosize); bool is_convex() const; + boost::tribool convexValue() const { return this->convex; } private: Polygon2d polygon; diff --git a/src/primitives.cc b/src/primitives.cc index a5045f86..5381db8c 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -108,7 +108,7 @@ public: double fn, fs, fa; primitive_type_e type; int convexity; - Value points, paths, faces; + ValuePtr points, paths, faces; virtual Geometry *createGeometry() const; }; @@ -125,19 +125,19 @@ public: */ Value PrimitiveModule::lookup_radius(const Context &ctx, const std::string &diameter_var, const std::string &radius_var) const { - const Value d = ctx.lookup_variable(diameter_var, true); - const Value r = ctx.lookup_variable(radius_var, true); - const bool r_defined = (r.type() == Value::NUMBER); + ValuePtr d = ctx.lookup_variable(diameter_var, true); + ValuePtr r = ctx.lookup_variable(radius_var, true); + const bool r_defined = (r->type() == Value::NUMBER); - if (d.type() == Value::NUMBER) { + if (d->type() == Value::NUMBER) { if (r_defined) { PRINTB("WARNING: Ignoring radius variable '%s' as diameter '%s' is defined too.", radius_var % diameter_var); } - return Value(d.toDouble() / 2.0); + return Value(d->toDouble() / 2.0); } else if (r_defined) { - return r; + return *r; } else { - return Value(); + return Value::undefined; } } @@ -179,9 +179,9 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta Context c(ctx); c.setVariables(args, evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); if (node->fs < F_MINIMUM) { PRINTB("WARNING: $fs too small - clamping to %f", F_MINIMUM); @@ -194,14 +194,14 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta switch (this->type) { case CUBE: { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getDouble(node->x); - size.getDouble(node->y); - size.getDouble(node->z); - size.getVec3(node->x, node->y, node->z); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr size = c.lookup_variable("size"); + ValuePtr center = c.lookup_variable("center"); + size->getDouble(node->x); + size->getDouble(node->y); + size->getDouble(node->z); + size->getVec3(node->x, node->y, node->z); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } @@ -213,9 +213,9 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta break; } case CYLINDER: { - const Value h = c.lookup_variable("h"); - if (h.type() == Value::NUMBER) { - node->h = h.toDouble(); + ValuePtr h = c.lookup_variable("h"); + if (h->type() == Value::NUMBER) { + node->h = h->toDouble(); } const Value r = lookup_radius(c, "d", "r"); @@ -232,32 +232,32 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta node->r2 = r2.toDouble(); } - const Value center = c.lookup_variable("center"); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr center = c.lookup_variable("center"); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } case POLYHEDRON: { node->points = c.lookup_variable("points"); node->faces = c.lookup_variable("faces"); - if (node->faces.type() == Value::UNDEFINED) { + if (node->faces->type() == Value::UNDEFINED) { // backwards compatible node->faces = c.lookup_variable("triangles", true); - if (node->faces.type() != Value::UNDEFINED) { + if (node->faces->type() != Value::UNDEFINED) { printDeprecation("DEPRECATED: polyhedron(triangles=[]) will be removed in future releases. Use polyhedron(faces=[]) instead."); } } break; } case SQUARE: { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getDouble(node->x); - size.getDouble(node->y); - size.getVec2(node->x, node->y); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr size = c.lookup_variable("size"); + ValuePtr center = c.lookup_variable("center"); + size->getDouble(node->x); + size->getDouble(node->y); + size->getVec2(node->x, node->y); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } @@ -275,7 +275,7 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta } } - node->convexity = c.lookup_variable("convexity", true).toDouble(); + node->convexity = c.lookup_variable("convexity", true)->toDouble(); if (node->convexity < 1) node->convexity = 1; @@ -505,19 +505,18 @@ Geometry *PrimitiveNode::createGeometry() const PolySet *p = new PolySet(3); g = p; p->setConvexity(this->convexity); - for (size_t i=0; ifaces.toVector().size(); i++) + for (size_t i=0; ifaces->toVector().size(); i++) { p->append_poly(); - const Value::VectorType &vec = this->faces.toVector()[i].toVector(); + const Value::VectorType &vec = this->faces->toVector()[i].toVector(); for (size_t j=0; jpoints.toVector().size()) { + if (pt < this->points->toVector().size()) { double px, py, pz; - if (!this->points.toVector()[pt].getVec3(px, py, pz) || + if (!this->points->toVector()[pt].getVec3(px, py, pz) || isinf(px) || isinf(py) || isinf(pz)) { PRINTB("ERROR: Unable to convert point at index %d to a vec3 of numbers", j); - delete p; - return NULL; + return p; } p->insert_vertex(px, py, pz); } @@ -544,8 +543,8 @@ Geometry *PrimitiveNode::createGeometry() const o.vertices[2] = v2; o.vertices[3] = Vector2d(v1[0], v2[1]); p->addOutline(o); - p->setSanitized(true); } + p->setSanitized(true); } break; case CIRCLE: { @@ -561,8 +560,8 @@ Geometry *PrimitiveNode::createGeometry() const o.vertices[i] = Vector2d(this->r1*cos(phi), this->r1*sin(phi)); } p->addOutline(o); - p->setSanitized(true); } + p->setSanitized(true); } break; case POLYGON: { @@ -571,24 +570,22 @@ Geometry *PrimitiveNode::createGeometry() const Outline2d outline; double x,y; - const Value::VectorType &vec = this->points.toVector(); + const Value::VectorType &vec = this->points->toVector(); for (unsigned int i=0;ipaths.toVector().size() == 0 && outline.vertices.size() > 2) { + if (this->paths->toVector().size() == 0 && outline.vertices.size() > 2) { p->addOutline(outline); } else { - BOOST_FOREACH(const Value &polygon, this->paths.toVector()) { + BOOST_FOREACH(const Value &polygon, this->paths->toVector()) { Outline2d curroutline; BOOST_FOREACH(const Value &index, polygon.toVector()) { unsigned int idx = index.toDouble(); @@ -601,11 +598,7 @@ Geometry *PrimitiveNode::createGeometry() const } } - if (p->outlines().size() == 0) { - delete p; - g = NULL; - } - else { + if (p->outlines().size() > 0) { p->setConvexity(convexity); } } @@ -635,8 +628,8 @@ std::string PrimitiveNode::toString() const << ", r2 = " << this->r2 << ", center = " << (center ? "true" : "false") << ")"; break; case POLYHEDRON: - stream << "(points = " << this->points - << ", faces = " << this->faces + stream << "(points = " << *this->points + << ", faces = " << *this->faces << ", convexity = " << this->convexity << ")"; break; case SQUARE: @@ -648,7 +641,7 @@ std::string PrimitiveNode::toString() const << ", $fs = " << this->fs << ", r = " << this->r1 << ")"; break; case POLYGON: - stream << "(points = " << this->points << ", paths = " << this->paths << ", convexity = " << this->convexity << ")"; + stream << "(points = " << *this->points << ", paths = " << *this->paths << ", convexity = " << this->convexity << ")"; break; default: assert(false); diff --git a/src/printutils.cc b/src/printutils.cc index 5004c745..4e400ccb 100644 --- a/src/printutils.cc +++ b/src/printutils.cc @@ -2,12 +2,16 @@ #include #include #include +#include +#include std::list print_messages_stack; OutputHandlerFunc *outputhandler = NULL; void *outputhandler_data = NULL; std::string OpenSCAD::debug(""); +boost::circular_buffer lastmessages(5); + void set_output_handler(OutputHandlerFunc *newhandler, void *userdata) { outputhandler = newhandler; @@ -46,6 +50,16 @@ void PRINT(const std::string &msg) void PRINT_NOCACHE(const std::string &msg) { if (msg.empty()) return; + + if (boost::starts_with(msg, "WARNING") || boost::starts_with(msg, "ERROR")) { + int i; + for (i=0;i #include +#include +#include +inline char * _( const char * msgid ) { return gettext( msgid ); } + typedef void (OutputHandlerFunc)(const std::string &msg, void *userdata); extern OutputHandlerFunc *outputhandler; extern void *outputhandler_data; diff --git a/src/projection.cc b/src/projection.cc index 57c1d1f9..8929c07a 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -55,13 +55,13 @@ AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInst c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value convexity = c.lookup_variable("convexity", true); - Value cut = c.lookup_variable("cut", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr cut = c.lookup_variable("cut", true); - node->convexity = (int)convexity.toDouble(); + node->convexity = (int)convexity->toDouble(); - if (cut.type() == Value::BOOL) - node->cut_mode = cut.toBool(); + if (cut->type() == Value::BOOL) + node->cut_mode = cut->toBool(); std::vector instantiatednodes = inst->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/qtgettext.h b/src/qtgettext.h new file mode 100644 index 00000000..6e667163 --- /dev/null +++ b/src/qtgettext.h @@ -0,0 +1,26 @@ +#ifndef __openscad_qtgettext_h__ +#define __openscad_qtgettext_h__ + +// see doc/translation.txt + +// MinGW defines sprintf to libintl_sprintf which breaks usage of the +// Qt sprintf in QString. This is skipped if sprintf and _GL_STDIO_H +// is already defined, so the workaround defines sprintf as itself. +#ifdef __MINGW32__ +#define _GL_STDIO_H +#define sprintf sprintf +#endif + +#include +#include "printutils.h" + +#define N_(String) String + +inline QString _( const char *msgid, int category ) +{ + Q_UNUSED( category ); + return QString::fromUtf8( _( msgid ) ); +} + +#endif + diff --git a/src/render.cc b/src/render.cc index 4d109fbf..12c5a567 100644 --- a/src/render.cc +++ b/src/render.cc @@ -52,9 +52,9 @@ AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstanti c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value v = c.lookup_variable("convexity"); - if (v.type() == Value::NUMBER) - node->convexity = (int)v.toDouble(); + ValuePtr v = c.lookup_variable("convexity"); + if (v->type() == Value::NUMBER) + node->convexity = (int)v->toDouble(); std::vector instantiatednodes = inst->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/renderer.cc b/src/renderer.cc index 3b7a2a8a..e7373ea1 100644 --- a/src/renderer.cc +++ b/src/renderer.cc @@ -101,31 +101,13 @@ void Renderer::setColorScheme(const ColorScheme &cs) { void Renderer::render_surface(shared_ptr geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) { - shared_ptr ps; - shared_ptr p2d = dynamic_pointer_cast(geom); - if (p2d) { - ps.reset(p2d->tessellate()); - } - else { - ps = dynamic_pointer_cast(geom); - } - if (ps) { - ps->render_surface(csgmode, m, shaderinfo); - } + shared_ptr ps = dynamic_pointer_cast(geom); + if (ps) ps->render_surface(csgmode, m, shaderinfo); } void Renderer::render_edges(shared_ptr geom, csgmode_e csgmode) { - shared_ptr ps; - shared_ptr p2d = dynamic_pointer_cast(geom); - if (p2d) { - ps.reset(p2d->tessellate()); - } - else { - ps = dynamic_pointer_cast(geom); - } - if (ps) { - ps->render_edges(csgmode); - } + shared_ptr ps = dynamic_pointer_cast(geom); + if (ps) ps->render_edges(csgmode); } diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc index b2e8d0eb..700fb7c9 100644 --- a/src/rotateextrude.cc +++ b/src/rotateextrude.cc @@ -58,25 +58,25 @@ AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleI c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); + ValuePtr file = c.lookup_variable("file"); + ValuePtr layer = c.lookup_variable("layer", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr origin = c.lookup_variable("origin", true); + ValuePtr scale = c.lookup_variable("scale", true); - if (!file.isUndefined()) { + if (!file->isUndefined()) { printDeprecation("DEPRECATED: Support for reading files in rotate_extrude will be removed in future releases. Use a child import() instead."); - node->filename = lookup_file(file.toString(), inst->path(), c.documentPath()); + node->filename = lookup_file(file->toString(), inst->path(), c.documentPath()); } - node->layername = layer.isUndefined() ? "" : layer.toString(); - node->convexity = (int)convexity.toDouble(); - origin.getVec2(node->origin_x, node->origin_y); - node->scale = scale.toDouble(); + node->layername = layer->isUndefined() ? "" : layer->toString(); + node->convexity = (int)convexity->toDouble(); + origin->getVec2(node->origin_x, node->origin_y); + node->scale = scale->toDouble(); if (node->convexity <= 0) node->convexity = 1; diff --git a/src/scadlexer.cpp b/src/scadlexer.cpp index ff774dff..9fc2d4d3 100644 --- a/src/scadlexer.cpp +++ b/src/scadlexer.cpp @@ -1,38 +1,63 @@ +#include + #include "scadlexer.h" -ScadLexer::ScadLexer(QObject *parent) - : QsciLexerCPP(parent) -{ } +ScadLexer::ScadLexer(QObject *parent) : QsciLexerCPP(parent) +{ + // -> Style: Keyword (lexer.l) + keywordSet[0] = + "if else let for module function true false undef " + "include use"; + + // -> Style: KeywordSet2 (func.cc) + keywordSet[1] = + "abs sign rands min max sin cos asin acos tan atan atan2 " + "round ceil floor pow sqrt exp len log ln str chr concat " + "lookup search version version_num norm cross parent_module " + "dxf_dim dxf_cross"; + + // -> used in comments only like /*! \cube */ + keywordSet[2] = + "struct union enum fn var def typedef file namespace package " + "interface param see return class brief"; + + // -> Style: GlobalClass + keywordSet[3] = + "cube sphere cylinder polyhedron square circle polygon text " + "minkowski hull resize child echo union difference " + "intersection linear_extrude rotate_extrude import group " + "projection render surface scale rotate mirror translate " + "multmatrix color offset "; +} ScadLexer::~ScadLexer() -{ } +{ +} const char *ScadLexer::language() const { - return "SCAD"; + return "SCAD"; +} + +void ScadLexer::setKeywords(int set, const std::string& keywords) +{ + if ((set < 1) || (set > 4)) { + return; + } + + std::string trimmedKeywords(keywords); + boost::algorithm::trim(trimmedKeywords); + if (trimmedKeywords.empty()) { + return; + } + + keywordSet[set - 1] = trimmedKeywords; } const char *ScadLexer::keywords(int set) const { - - if (set == 1) - return "if else for module function intersection_for assign echo search " - " str let true false "; // -> Style: Keyword - - if (set == 2) - return " abs sign acos asin atan atan2 sin cos tan floor round ceil len ln " - " log lookup min max pow sqrt exp rands version version_num " - " group difference union intersection render translate rotate scale multmatrix color " - " projection hull resize mirror minkowski glide subdiv child " - " include use dxf_dim dxf_cross " - " linear_extrude rotate_extrude "; // -> Style: KeywordSet2 - - if (set == 3) - return " param author "; // -> used in comments only like /*! \cube */ - - if (set == 4) - return "cube circle cylinder polygon polyhedron square sphere " - "surface import "; // -> Style: GlobalClass - - return 0; + if ((set < 1) || (set > 4)) { + return 0; + } + return keywordSet[set - 1].c_str(); } diff --git a/src/scadlexer.h b/src/scadlexer.h index 50e6347a..b6a349d7 100644 --- a/src/scadlexer.h +++ b/src/scadlexer.h @@ -12,8 +12,10 @@ public: virtual ~ScadLexer(); const char *language() const; const char *keywords(int set) const; - + + void setKeywords(int set, const std::string& keywords); private: + std::string keywordSet[4]; ScadLexer(const ScadLexer &); ScadLexer &operator=(const ScadLexer &); }; diff --git a/src/scintillaeditor.cpp b/src/scintillaeditor.cpp index d29d28f3..43f36f01 100644 --- a/src/scintillaeditor.cpp +++ b/src/scintillaeditor.cpp @@ -1,455 +1,574 @@ #include #include #include +#include "boosty.h" #include "scintillaeditor.h" #include #include "Preferences.h" #include "PlatformUtils.h" +#include "settings.h" + +class SettingsConverter { +public: + QsciScintilla::WrapMode toWrapMode(Value val); + QsciScintilla::WrapVisualFlag toLineWrapVisualization(Value val); + QsciScintilla::WrapIndentMode toLineWrapIndentationStyle(Value val); + QsciScintilla::WhitespaceVisibility toShowWhitespaces(Value val); +}; + +QsciScintilla::WrapMode SettingsConverter::toWrapMode(Value val) +{ + std::string v = val.toString(); + if (v == "Char") { + return QsciScintilla::WrapCharacter; + } else if (v == "Word") { + return QsciScintilla::WrapWord; + } else { + return QsciScintilla::WrapNone; + } +} + +QsciScintilla::WrapVisualFlag SettingsConverter::toLineWrapVisualization(Value val) +{ + std::string v = val.toString(); + if (v == "Text") { + return QsciScintilla::WrapFlagByText; + } else if (v == "Border") { + return QsciScintilla::WrapFlagByBorder; +#if QSCINTILLA_VERSION >= 0x020700 + } else if (v == "Margin") { + return QsciScintilla::WrapFlagInMargin; +#endif + } else { + return QsciScintilla::WrapFlagNone; + } +} + +QsciScintilla::WrapIndentMode SettingsConverter::toLineWrapIndentationStyle(Value val) +{ + std::string v = val.toString(); + if (v == "Same") { + return QsciScintilla::WrapIndentSame; + } else if (v == "Indented") { + return QsciScintilla::WrapIndentIndented; + } else { + return QsciScintilla::WrapIndentFixed; + } +} + +QsciScintilla::WhitespaceVisibility SettingsConverter::toShowWhitespaces(Value val) +{ + std::string v = val.toString(); + if (v == "Always") { + return QsciScintilla::WsVisible; + } else if (v == "AfterIndentation") { + return QsciScintilla::WsVisibleAfterIndent; + } else { + return QsciScintilla::WsInvisible; + } +} EditorColorScheme::EditorColorScheme(fs::path path) : path(path) { - try { - boost::property_tree::read_json(boosty::stringy(path).c_str(), pt); - _name = QString(pt.get("name").c_str()); - _index = pt.get("index"); - } catch (const std::exception & e) { - PRINTB("Error reading color scheme file '%s': %s", path.c_str() % e.what()); - _name = ""; - _index = 0; - } + try { + boost::property_tree::read_json(boosty::stringy(path).c_str(), pt); + _name = QString(pt.get("name").c_str()); + _index = pt.get("index"); + } catch (const std::exception & e) { + PRINTB("Error reading color scheme file '%s': %s", path.c_str() % e.what()); + _name = ""; + _index = 0; + } } EditorColorScheme::~EditorColorScheme() { - + } bool EditorColorScheme::valid() const { - return !_name.isEmpty(); + return !_name.isEmpty(); } const QString & EditorColorScheme::name() const { - return _name; + return _name; } int EditorColorScheme::index() const { - return _index; + return _index; } const boost::property_tree::ptree & EditorColorScheme::propertyTree() const { - return pt; + return pt; } ScintillaEditor::ScintillaEditor(QWidget *parent) : EditorInterface(parent) { - scintillaLayout = new QVBoxLayout(this); - qsci = new QsciScintilla(this); + scintillaLayout = new QVBoxLayout(this); + qsci = new QsciScintilla(this); + // Force EOL mode to Unix, since QTextStream will manage local EOL modes. + qsci->setEolMode(QsciScintilla::EolUnix); - // - // Remapping some scintilla key binding which conflict with OpenSCAD global - // key bindings, as well as some minor scintilla bugs - // - QsciCommand *c; + // + // Remapping some scintilla key binding which conflict with OpenSCAD global + // key bindings, as well as some minor scintilla bugs + // + QsciCommand *c; #ifdef Q_OS_MAC - // Alt-Backspace should delete left word (Alt-Delete already deletes right word) - c= qsci->standardCommands()->find(QsciCommand::DeleteWordLeft); - c->setKey(Qt::Key_Backspace | Qt::ALT); + // Alt-Backspace should delete left word (Alt-Delete already deletes right word) + c = qsci->standardCommands()->find(QsciCommand::DeleteWordLeft); + c->setKey(Qt::Key_Backspace | Qt::ALT); #endif - // Cmd/Ctrl-T is handled by the menu - c = qsci->standardCommands()->boundTo(Qt::Key_T | Qt::CTRL); - c->setKey(0); - // Cmd/Ctrl-D is handled by the menu - c = qsci->standardCommands()->boundTo(Qt::Key_D | Qt::CTRL); - c->setKey(0); - // Ctrl-Shift-Z should redo on all platforms - c= qsci->standardCommands()->find(QsciCommand::Redo); - c->setKey(Qt::Key_Z | Qt::CTRL | Qt::SHIFT); + // Cmd/Ctrl-T is handled by the menu + c = qsci->standardCommands()->boundTo(Qt::Key_T | Qt::CTRL); + c->setKey(0); + // Cmd/Ctrl-D is handled by the menu + c = qsci->standardCommands()->boundTo(Qt::Key_D | Qt::CTRL); + c->setKey(0); + // Ctrl-Shift-Z should redo on all platforms + c = qsci->standardCommands()->find(QsciCommand::Redo); + c->setKey(Qt::Key_Z | Qt::CTRL | Qt::SHIFT); - scintillaLayout->setContentsMargins(0, 0, 0, 0); - scintillaLayout->addWidget(qsci); + scintillaLayout->setContentsMargins(0, 0, 0, 0); + scintillaLayout->addWidget(qsci); - qsci->setBraceMatching (QsciScintilla::SloppyBraceMatch); - qsci->setWrapMode(QsciScintilla::WrapCharacter); - qsci->setWrapVisualFlags(QsciScintilla::WrapFlagByBorder, QsciScintilla::WrapFlagNone, 0); - qsci->setAutoIndent(true); - qsci->indicatorDefine(QsciScintilla::RoundBoxIndicator, indicatorNumber); - qsci->markerDefine(QsciScintilla::Circle, markerNumber); - qsci->setUtf8(true); - qsci->setTabIndents(true); - qsci->setTabWidth(8); - qsci->setIndentationWidth(4); - qsci->setIndentationsUseTabs(false); - - lexer = new ScadLexer(this); - initLexer(); - initMargin(); - qsci->setFolding(QsciScintilla::BoxedTreeFoldStyle, 4); - qsci->setCaretLineVisible(true); + qsci->indicatorDefine(QsciScintilla::RoundBoxIndicator, indicatorNumber); + qsci->markerDefine(QsciScintilla::Circle, markerNumber); + qsci->setUtf8(true); + qsci->setFolding(QsciScintilla::BoxedTreeFoldStyle, 4); - connect(qsci, SIGNAL(textChanged()), this, SIGNAL(contentsChanged())); - connect(qsci, SIGNAL(modificationChanged(bool)), this, SIGNAL(modificationChanged(bool))); + lexer = new ScadLexer(this); + qsci->setLexer(lexer); + initMargin(); + + connect(qsci, SIGNAL(textChanged()), this, SIGNAL(contentsChanged())); + connect(qsci, SIGNAL(modificationChanged(bool)), this, SIGNAL(modificationChanged(bool))); +} + +/** + * Apply the settings that are changeable in the preferences. This is also + * called in the event handler from the preferences. + */ +void ScintillaEditor::applySettings() +{ + SettingsConverter conv; + Settings::Settings *s = Settings::Settings::inst(); + + qsci->setIndentationWidth(s->get(Settings::Settings::indentationWidth).toDouble()); + qsci->setTabWidth(s->get(Settings::Settings::tabWidth).toDouble()); + qsci->setWrapMode(conv.toWrapMode(s->get(Settings::Settings::lineWrap))); + qsci->setWrapIndentMode(conv.toLineWrapIndentationStyle(s->get(Settings::Settings::lineWrapIndentationStyle))); + qsci->setWrapVisualFlags(conv.toLineWrapVisualization(s->get(Settings::Settings::lineWrapVisualizationEnd)), + conv.toLineWrapVisualization(s->get(Settings::Settings::lineWrapVisualizationBegin)), + s->get(Settings::Settings::lineWrapIndentation).toDouble()); + qsci->setWhitespaceVisibility(conv.toShowWhitespaces(s->get(Settings::Settings::showWhitespace))); + qsci->setWhitespaceSize(s->get(Settings::Settings::showWhitespaceSize).toDouble()); + qsci->setAutoIndent(s->get(Settings::Settings::autoIndent).toBool()); + + std::string indentStyle = s->get(Settings::Settings::indentStyle).toString(); + qsci->setIndentationsUseTabs(indentStyle == "Tabs"); + std::string tabKeyFunction = s->get(Settings::Settings::tabKeyFunction).toString(); + qsci->setTabIndents(tabKeyFunction == "Indent"); + + qsci->setBraceMatching(s->get(Settings::Settings::enableBraceMatching).toBool() ? QsciScintilla::SloppyBraceMatch : QsciScintilla::NoBraceMatch); + qsci->setCaretLineVisible(s->get(Settings::Settings::highlightCurrentLine).toBool()); } void ScintillaEditor::setPlainText(const QString &text) -{ - qsci->setText(text); - setContentModified(false); +{ + qsci->setText(text); + setContentModified(false); } QString ScintillaEditor::toPlainText() { - return qsci->text(); + return qsci->text(); } void ScintillaEditor::setContentModified(bool modified) { - // FIXME: Due to an issue with QScintilla, we need to do this on the document itself. + // FIXME: Due to an issue with QScintilla, we need to do this on the document itself. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - qsci->SCN_SAVEPOINTLEFT(); + qsci->SCN_SAVEPOINTLEFT(); #endif - qsci->setModified(modified); + qsci->setModified(modified); } bool ScintillaEditor::isContentModified() { - return qsci->isModified(); + return qsci->isModified(); } -void ScintillaEditor::highlightError(int error_pos) +void ScintillaEditor::highlightError(int error_pos) { - int line, index; - qsci->lineIndexFromPosition(error_pos, &line, &index); - qsci->fillIndicatorRange(line, index, line, index+1, indicatorNumber); - qsci->markerAdd(line, markerNumber); + int line, index; + qsci->lineIndexFromPosition(error_pos, &line, &index); + qsci->fillIndicatorRange(line, index, line, index + 1, indicatorNumber); + qsci->markerAdd(line, markerNumber); } -void ScintillaEditor::unhighlightLastError() +void ScintillaEditor::unhighlightLastError() { - int totalLength = qsci->text().length(); - int line, index; - qsci->lineIndexFromPosition(totalLength, &line, &index); - qsci->clearIndicatorRange(0, 0, line, index, indicatorNumber); - qsci->markerDeleteAll(markerNumber); + int totalLength = qsci->text().length(); + int line, index; + qsci->lineIndexFromPosition(totalLength, &line, &index); + qsci->clearIndicatorRange(0, 0, line, index, indicatorNumber); + qsci->markerDeleteAll(markerNumber); } QColor ScintillaEditor::readColor(const boost::property_tree::ptree &pt, const std::string name, const QColor defaultColor) { - try { - const std::string val = pt.get(name); - return QColor(val.c_str()); - } catch (std::exception e) { - return defaultColor; - } + try { + const std::string val = pt.get(name); + return QColor(val.c_str()); + } catch (std::exception e) { + return defaultColor; + } +} + +std::string ScintillaEditor::readString(const boost::property_tree::ptree &pt, const std::string name, const std::string defaultValue) +{ + try { + const std::string val = pt.get(name); + return val; + } catch (std::exception e) { + return defaultValue; + } } int ScintillaEditor::readInt(const boost::property_tree::ptree &pt, const std::string name, const int defaultValue) { - try { - const int val = pt.get(name); - return val; - } catch (std::exception e) { - return defaultValue; - } + try { + const int val = pt.get(name); + return val; + } catch (std::exception e) { + return defaultValue; + } } void ScintillaEditor::setColormap(const EditorColorScheme *colorScheme) { - const boost::property_tree::ptree & pt = colorScheme->propertyTree(); + const boost::property_tree::ptree & pt = colorScheme->propertyTree(); - try { - const QColor textColor(pt.get("text").c_str()); - const QColor paperColor(pt.get("paper").c_str()); + try { + QFont font = lexer->font(lexer->defaultStyle()); + const QColor textColor(pt.get("text").c_str()); + const QColor paperColor(pt.get("paper").c_str()); - lexer->setColor(textColor); - lexer->setPaper(paperColor); + ScadLexer *l = new ScadLexer(this); - const boost::property_tree::ptree& colors = pt.get_child("colors"); - lexer->setColor(readColor(colors, "keyword1", textColor), QsciLexerCPP::Keyword); - lexer->setColor(readColor(colors, "keyword2", textColor), QsciLexerCPP::KeywordSet2); - lexer->setColor(readColor(colors, "keyword3", textColor), QsciLexerCPP::GlobalClass); - lexer->setColor(readColor(colors, "comment", textColor), QsciLexerCPP::CommentDocKeyword); - lexer->setColor(readColor(colors, "number", textColor), QsciLexerCPP::Number); - lexer->setColor(readColor(colors, "string", textColor), QsciLexerCPP::DoubleQuotedString); - lexer->setColor(readColor(colors, "operator", textColor), QsciLexerCPP::Operator); - lexer->setColor(readColor(colors, "commentline", textColor), QsciLexerCPP::CommentLine); + // Keywords must be set before the lexer is attached to QScintilla + // as they seem to be read and cached at attach time. + boost::optional keywords = pt.get_child_optional("keywords"); + if (keywords.is_initialized()) { + l->setKeywords(1, readString(keywords.get(), "keyword-set1", "")); + l->setKeywords(2, readString(keywords.get(), "keyword-set2", "")); + l->setKeywords(3, readString(keywords.get(), "keyword-set-doc", "")); + l->setKeywords(4, readString(keywords.get(), "keyword-set3", "")); + } - const boost::property_tree::ptree& caret = pt.get_child("caret"); - - qsci->setCaretWidth(readInt(caret, "width", 1)); - qsci->setCaretForegroundColor(readColor(caret, "foreground", textColor)); - qsci->setCaretLineBackgroundColor(readColor(caret, "line-background", paperColor)); + qsci->setLexer(l); + delete lexer; + lexer = l; - qsci->setMarkerBackgroundColor(readColor(colors, "error-marker", QColor(255, 0, 0, 100)), markerNumber); - qsci->setMarginsBackgroundColor(readColor(colors, "margin-background", paperColor)); - qsci->setMarginsForegroundColor(readColor(colors, "margin-foreground", textColor)); - qsci->setMatchedBraceBackgroundColor(readColor(colors, "matched-brace-background", paperColor)); - qsci->setMatchedBraceForegroundColor(readColor(colors, "matched-brace-foreground", textColor)); - qsci->setUnmatchedBraceBackgroundColor(readColor(colors, "unmatched-brace-background", paperColor)); - qsci->setUnmatchedBraceForegroundColor(readColor(colors, "unmatched-brace-foreground", textColor)); - qsci->setSelectionForegroundColor(readColor(colors, "selection-foreground", paperColor)); - qsci->setSelectionBackgroundColor(readColor(colors, "selection-background", textColor)); - qsci->setFoldMarginColors(readColor(colors, "margin-foreground", textColor), - readColor(colors, "margin-background", paperColor)); - qsci->setEdgeColor(readColor(colors, "edge", textColor)); - } catch (std::exception e) { - noColor(); - } + // All other properties must be set after attaching to QSCintilla so + // the editor gets the change events and updates itself to match + l->setFont(font); + l->setColor(textColor); + l->setPaper(paperColor); + + const boost::property_tree::ptree& colors = pt.get_child("colors"); + l->setColor(readColor(colors, "keyword1", textColor), QsciLexerCPP::Keyword); + l->setColor(readColor(colors, "keyword2", textColor), QsciLexerCPP::KeywordSet2); + l->setColor(readColor(colors, "keyword3", textColor), QsciLexerCPP::GlobalClass); + l->setColor(readColor(colors, "number", textColor), QsciLexerCPP::Number); + l->setColor(readColor(colors, "string", textColor), QsciLexerCPP::SingleQuotedString); + l->setColor(readColor(colors, "string", textColor), QsciLexerCPP::DoubleQuotedString); + l->setColor(readColor(colors, "operator", textColor), QsciLexerCPP::Operator); + l->setColor(readColor(colors, "comment", textColor), QsciLexerCPP::Comment); + l->setColor(readColor(colors, "commentline", textColor), QsciLexerCPP::CommentLine); + l->setColor(readColor(colors, "commentdoc", textColor), QsciLexerCPP::CommentDoc); + l->setColor(readColor(colors, "commentdoc", textColor), QsciLexerCPP::CommentLineDoc); + l->setColor(readColor(colors, "commentdockeyword", textColor), QsciLexerCPP::CommentDocKeyword); + + const boost::property_tree::ptree& caret = pt.get_child("caret"); + qsci->setCaretWidth(readInt(caret, "width", 1)); + qsci->setCaretForegroundColor(readColor(caret, "foreground", textColor)); + qsci->setCaretLineBackgroundColor(readColor(caret, "line-background", paperColor)); + + qsci->setMarkerBackgroundColor(readColor(colors, "error-marker", QColor(255, 0, 0, 100)), markerNumber); + qsci->setIndicatorForegroundColor(readColor(colors, "error-indicator", QColor(255, 0, 0, 100)), indicatorNumber); + qsci->setIndicatorOutlineColor(readColor(colors, "error-indicator-outline", QColor(255, 0, 0, 100)), indicatorNumber); + qsci->setWhitespaceForegroundColor(readColor(colors, "whitespace-foreground", textColor)); + qsci->setMarginsBackgroundColor(readColor(colors, "margin-background", paperColor)); + qsci->setMarginsForegroundColor(readColor(colors, "margin-foreground", textColor)); + qsci->setMatchedBraceBackgroundColor(readColor(colors, "matched-brace-background", paperColor)); + qsci->setMatchedBraceForegroundColor(readColor(colors, "matched-brace-foreground", textColor)); + qsci->setUnmatchedBraceBackgroundColor(readColor(colors, "unmatched-brace-background", paperColor)); + qsci->setUnmatchedBraceForegroundColor(readColor(colors, "unmatched-brace-foreground", textColor)); + qsci->setSelectionForegroundColor(readColor(colors, "selection-foreground", paperColor)); + qsci->setSelectionBackgroundColor(readColor(colors, "selection-background", textColor)); + qsci->setFoldMarginColors(readColor(colors, "margin-foreground", textColor), + readColor(colors, "margin-background", paperColor)); + qsci->setEdgeColor(readColor(colors, "edge", textColor)); + } catch (std::exception e) { + noColor(); + } } void ScintillaEditor::noColor() { - lexer->setPaper(Qt::white); - lexer->setColor(Qt::black); - qsci->setCaretWidth(2); - qsci->setCaretForegroundColor(Qt::black); - qsci->setMarkerBackgroundColor(QColor(255, 0, 0, 100), markerNumber); - qsci->setCaretLineBackgroundColor(Qt::white); - qsci->setMarginsBackgroundColor(Qt::white); - qsci->setMarginsForegroundColor(Qt::black); - qsci->setSelectionForegroundColor(Qt::white); - qsci->setSelectionBackgroundColor(Qt::black); - qsci->setMatchedBraceBackgroundColor(Qt::white); - qsci->setMatchedBraceForegroundColor(Qt::black); - qsci->setUnmatchedBraceBackgroundColor(Qt::white); - qsci->setUnmatchedBraceForegroundColor(Qt::black); - qsci->setMarginsBackgroundColor(Qt::lightGray); - qsci->setMarginsForegroundColor(Qt::black); - qsci->setFoldMarginColors(Qt::black, Qt::lightGray); - qsci->setEdgeColor(Qt::black); + lexer->setPaper(Qt::white); + lexer->setColor(Qt::black); + qsci->setCaretWidth(2); + qsci->setCaretForegroundColor(Qt::black); + qsci->setMarkerBackgroundColor(QColor(255, 0, 0, 100), markerNumber); + qsci->setIndicatorForegroundColor(QColor(255, 0, 0, 128), indicatorNumber); + qsci->setIndicatorOutlineColor(QColor(0, 0, 0, 255), indicatorNumber); // only alpha part is used + qsci->setCaretLineBackgroundColor(Qt::white); + qsci->setWhitespaceBackgroundColor(Qt::white); + qsci->setWhitespaceForegroundColor(Qt::black); + qsci->setMarginsBackgroundColor(Qt::white); + qsci->setMarginsForegroundColor(Qt::black); + qsci->setSelectionForegroundColor(Qt::white); + qsci->setSelectionBackgroundColor(Qt::black); + qsci->setMatchedBraceBackgroundColor(Qt::white); + qsci->setMatchedBraceForegroundColor(Qt::black); + qsci->setUnmatchedBraceBackgroundColor(Qt::white); + qsci->setUnmatchedBraceForegroundColor(Qt::black); + qsci->setMarginsBackgroundColor(Qt::lightGray); + qsci->setMarginsForegroundColor(Qt::black); + qsci->setFoldMarginColors(Qt::black, Qt::lightGray); + qsci->setEdgeColor(Qt::black); } void ScintillaEditor::enumerateColorSchemesInPath(ScintillaEditor::colorscheme_set_t &result_set, const fs::path path) { - const fs::path color_schemes = path / "color-schemes" / "editor"; + const fs::path color_schemes = path / "color-schemes" / "editor"; - fs::directory_iterator end_iter; - - if (fs::exists(color_schemes) && fs::is_directory(color_schemes)) { - for (fs::directory_iterator dir_iter(color_schemes); dir_iter != end_iter; ++dir_iter) { - if (!fs::is_regular_file(dir_iter->status())) { - continue; - } - - const fs::path path = (*dir_iter).path(); - if (!(path.extension().string() == ".json")) { - continue; - } - - EditorColorScheme *colorScheme = new EditorColorScheme(path); - if (colorScheme->valid()) { - result_set.insert(colorscheme_set_t::value_type(colorScheme->index(), boost::shared_ptr(colorScheme))); - } else { - delete colorScheme; - } + fs::directory_iterator end_iter; + + if (fs::exists(color_schemes) && fs::is_directory(color_schemes)) { + for (fs::directory_iterator dir_iter(color_schemes); dir_iter != end_iter; ++dir_iter) { + if (!fs::is_regular_file(dir_iter->status())) { + continue; + } + + const fs::path path = (*dir_iter).path(); + if (!(path.extension().string() == ".json")) { + continue; + } + + EditorColorScheme *colorScheme = new EditorColorScheme(path); + if (colorScheme->valid()) { + result_set.insert(colorscheme_set_t::value_type(colorScheme->index(), boost::shared_ptr(colorScheme))); + } else { + delete colorScheme; + } + } } - } } ScintillaEditor::colorscheme_set_t ScintillaEditor::enumerateColorSchemes() { - colorscheme_set_t result_set; + colorscheme_set_t result_set; - enumerateColorSchemesInPath(result_set, PlatformUtils::resourcesPath()); - enumerateColorSchemesInPath(result_set, PlatformUtils::userConfigPath()); - - return result_set; + enumerateColorSchemesInPath(result_set, PlatformUtils::resourceBasePath()); + enumerateColorSchemesInPath(result_set, PlatformUtils::userConfigPath()); + + return result_set; } QStringList ScintillaEditor::colorSchemes() { - const colorscheme_set_t colorscheme_set = enumerateColorSchemes(); + const colorscheme_set_t colorscheme_set = enumerateColorSchemes(); - QStringList colorSchemes; - for (colorscheme_set_t::const_iterator it = colorscheme_set.begin();it != colorscheme_set.end();it++) { - colorSchemes << (*it).second.get()->name(); - } - colorSchemes << "Off"; - - return colorSchemes; + QStringList colorSchemes; + for (colorscheme_set_t::const_iterator it = colorscheme_set.begin(); it != colorscheme_set.end(); it++) { + colorSchemes << (*it).second.get()->name(); + } + colorSchemes << "Off"; + + return colorSchemes; } void ScintillaEditor::setHighlightScheme(const QString &name) { - const colorscheme_set_t colorscheme_set = enumerateColorSchemes(); + const colorscheme_set_t colorscheme_set = enumerateColorSchemes(); - for (colorscheme_set_t::const_iterator it = colorscheme_set.begin();it != colorscheme_set.end();it++) { - const EditorColorScheme *colorScheme = (*it).second.get(); - if (colorScheme->name() == name) { - setColormap(colorScheme); - return; + for (colorscheme_set_t::const_iterator it = colorscheme_set.begin(); it != colorscheme_set.end(); it++) { + const EditorColorScheme *colorScheme = (*it).second.get(); + if (colorScheme->name() == name) { + setColormap(colorScheme); + return; + } } - } - - noColor(); + + noColor(); } void ScintillaEditor::insert(const QString &text) { - qsci->insert(text); + qsci->insert(text); } void ScintillaEditor::replaceAll(const QString &text) { - qsci->selectAll(true); - qsci->replaceSelectedText(text); + qsci->selectAll(true); + qsci->replaceSelectedText(text); } void ScintillaEditor::undo() { - qsci->undo(); + qsci->undo(); } void ScintillaEditor::redo() { - qsci->redo(); + qsci->redo(); } void ScintillaEditor::cut() { - qsci->cut(); + qsci->cut(); } void ScintillaEditor::copy() { - qsci->copy(); + qsci->copy(); } void ScintillaEditor::paste() -{ - qsci->paste(); +{ + qsci->paste(); } void ScintillaEditor::zoomIn() { - qsci->zoomIn(); + qsci->zoomIn(); } -void ScintillaEditor::zoomOut() +void ScintillaEditor::zoomOut() { - qsci->zoomOut(); + qsci->zoomOut(); } void ScintillaEditor::initFont(const QString& fontName, uint size) { - QFont font(fontName, size); - font.setFixedPitch(true); - lexer->setFont(font); -} - -void ScintillaEditor::initLexer() -{ - qsci->setLexer(lexer); + QFont font(fontName, size); + font.setFixedPitch(true); + lexer->setFont(font); } void ScintillaEditor::initMargin() { - QFontMetrics fontmetrics = QFontMetrics(qsci->font()); - qsci->setMarginsFont(qsci->font()); - qsci->setMarginWidth(1, fontmetrics.width(QString::number(qsci->lines())) + 6); - qsci->setMarginLineNumbers(1, true); - - connect(qsci, SIGNAL(textChanged()), this, SLOT(onTextChanged())); + QFontMetrics fontmetrics = QFontMetrics(qsci->font()); + qsci->setMarginsFont(qsci->font()); + qsci->setMarginWidth(1, fontmetrics.width(QString::number(qsci->lines())) + 6); + qsci->setMarginLineNumbers(1, true); + + connect(qsci, SIGNAL(textChanged()), this, SLOT(onTextChanged())); } void ScintillaEditor::onTextChanged() { - QFontMetrics fontmetrics = qsci->fontMetrics(); - qsci->setMarginWidth(1, fontmetrics.width(QString::number(qsci->lines())) + 6); + QFontMetrics fontmetrics = qsci->fontMetrics(); + qsci->setMarginWidth(1, fontmetrics.width(QString::number(qsci->lines())) + 6); } bool ScintillaEditor::find(const QString &expr, bool findNext, bool findBackwards) { - int startline = -1, startindex = -1; + int startline = -1, startindex = -1; - // If findNext, start from the end of the current selection - if (qsci->hasSelectedText()) { - int lineFrom, indexFrom, lineTo, indexTo; - qsci->getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo); - - startline = !(findBackwards xor findNext) ? std::min(lineFrom, lineTo) : std::max(lineFrom, lineTo); - startindex = !(findBackwards xor findNext) ? std::min(indexFrom, indexTo) : std::max(indexFrom, indexTo); - } + // If findNext, start from the end of the current selection + if (qsci->hasSelectedText()) { + int lineFrom, indexFrom, lineTo, indexTo; + qsci->getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo); - return qsci->findFirst(expr, false, false, false, true, - !findBackwards, startline, startindex); + startline = !(findBackwards xor findNext) ? std::min(lineFrom, lineTo) : std::max(lineFrom, lineTo); + startindex = !(findBackwards xor findNext) ? std::min(indexFrom, indexTo) : std::max(indexFrom, indexTo); + } + + return qsci->findFirst(expr, false, false, false, true, + !findBackwards, startline, startindex); } void ScintillaEditor::replaceSelectedText(const QString &newText) { - if (qsci->selectedText() != newText) qsci->replaceSelectedText(newText); + if (qsci->selectedText() != newText) qsci->replaceSelectedText(newText); } void ScintillaEditor::getRange(int *lineFrom, int *lineTo) { - int indexFrom, indexTo; - if (qsci->hasSelectedText()) { - qsci->getSelection(lineFrom, &indexFrom, lineTo, &indexTo); - if (indexTo == 0) { - *lineTo = *lineTo - 1; + int indexFrom, indexTo; + if (qsci->hasSelectedText()) { + qsci->getSelection(lineFrom, &indexFrom, lineTo, &indexTo); + if (indexTo == 0) { + *lineTo = *lineTo - 1; + } + } else { + qsci->getCursorPosition(lineFrom, &indexFrom); + *lineTo = *lineFrom; } - } else { - qsci->getCursorPosition(lineFrom, &indexFrom); - *lineTo = *lineFrom; - } } void ScintillaEditor::indentSelection() { - int lineFrom, lineTo; - getRange(&lineFrom, &lineTo); - for (int line = lineFrom;line <= lineTo;line++) { - qsci->indent(line); - } + int lineFrom, lineTo; + getRange(&lineFrom, &lineTo); + for (int line = lineFrom; line <= lineTo; line++) { + qsci->indent(line); + } } void ScintillaEditor::unindentSelection() { - int lineFrom, lineTo; - getRange(&lineFrom, &lineTo); - for (int line = lineFrom;line <= lineTo;line++) { - qsci->unindent(line); - } + int lineFrom, lineTo; + getRange(&lineFrom, &lineTo); + for (int line = lineFrom; line <= lineTo; line++) { + qsci->unindent(line); + } } void ScintillaEditor::commentSelection() { - bool hasSelection = qsci->hasSelectedText(); - - int lineFrom, lineTo; - getRange(&lineFrom, &lineTo); - for (int line = lineFrom;line <= lineTo;line++) { - qsci->insertAt("//", line, 0); - } - - if (hasSelection) { - qsci->setSelection(lineFrom, 0, lineTo, std::max(0, qsci->lineLength(lineTo) - 1)); - } + bool hasSelection = qsci->hasSelectedText(); + + int lineFrom, lineTo; + getRange(&lineFrom, &lineTo); + for (int line = lineFrom; line <= lineTo; line++) { + qsci->insertAt("//", line, 0); + } + + if (hasSelection) { + qsci->setSelection(lineFrom, 0, lineTo, std::max(0, qsci->lineLength(lineTo) - 1)); + } } void ScintillaEditor::uncommentSelection() { - bool hasSelection = qsci->hasSelectedText(); + bool hasSelection = qsci->hasSelectedText(); - int lineFrom, lineTo; - getRange(&lineFrom, &lineTo); - for (int line = lineFrom;line <= lineTo;line++) { - QString lineText = qsci->text(line); - if (lineText.startsWith("//")) { - qsci->setSelection(line, 0, line, 2); - qsci->removeSelectedText(); + int lineFrom, lineTo; + getRange(&lineFrom, &lineTo); + for (int line = lineFrom; line <= lineTo; line++) { + QString lineText = qsci->text(line); + if (lineText.startsWith("//")) { + qsci->setSelection(line, 0, line, 2); + qsci->removeSelectedText(); + } + } + if (hasSelection) { + qsci->setSelection(lineFrom, 0, lineTo, std::max(0, qsci->lineLength(lineTo) - 1)); } - } - if (hasSelection) { - qsci->setSelection(lineFrom, 0, lineTo, std::max(0, qsci->lineLength(lineTo) - 1)); - } } QString ScintillaEditor::selectedText() { - return qsci->selectedText(); + return qsci->selectedText(); } diff --git a/src/scintillaeditor.h b/src/scintillaeditor.h index 3b582ea6..4a4cdaa0 100644 --- a/src/scintillaeditor.h +++ b/src/scintillaeditor.h @@ -58,6 +58,7 @@ private: void getRange(int *lineFrom, int *lineTo); void setColormap(const EditorColorScheme *colorScheme); int readInt(const boost::property_tree::ptree &pt, const std::string name, const int defaultValue); + std::string readString(const boost::property_tree::ptree &pt, const std::string name, const std::string defaultValue); QColor readColor(const boost::property_tree::ptree &pt, const std::string name, const QColor defaultColor); void enumerateColorSchemesInPath(colorscheme_set_t &result_set, const fs::path path); colorscheme_set_t enumerateColorSchemes(); @@ -86,6 +87,7 @@ public slots: private slots: void onTextChanged(); + void applySettings(); private: QVBoxLayout *scintillaLayout; diff --git a/src/settings.cc b/src/settings.cc new file mode 100644 index 00000000..8af5fea3 --- /dev/null +++ b/src/settings.cc @@ -0,0 +1,139 @@ +#include "settings.h" +#include "printutils.h" + +#include +using namespace boost::assign; // bring 'operator+=()' into scope + +namespace Settings { + +static std::list entries; + +SettingsEntry::SettingsEntry(const std::string category, const std::string name, const Value range, const Value def) + : _category(category), _name(name), _value(def), _range(range), _default(def) +{ + entries.push_back(this); +} + +SettingsEntry::~SettingsEntry() +{ +} + +const std::string & SettingsEntry::category() const +{ + return _category; +} + +const std::string & SettingsEntry::name() const +{ + return _name; +} + +const Value & SettingsEntry::defaultValue() const +{ + return _default; +} + +const Value & SettingsEntry::range() const +{ + return _range; +} + +bool SettingsEntry::is_default() const +{ + return _value == _default; +} + +static Value value(std::string s1, std::string s2) { + Value::VectorType v; + v += Value(s1), Value(s2); + return v; +} + +static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp) { + Value::VectorType v; + v += value(s1, s1disp), value(s2, s2disp); + return v; +} + +static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp, std::string s3, std::string s3disp) { + Value::VectorType v; + v += value(s1, s1disp), value(s2, s2disp), value(s3, s3disp); + return v; +} + +static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp, std::string s3, std::string s3disp, std::string s4, std::string s4disp) { + Value::VectorType v; + v += value(s1, s1disp), value(s2, s2disp), value(s3, s3disp), value(s4, s4disp); + return v; +} + +Settings *Settings::inst(bool erase) +{ + static Settings *instance = new Settings; + + if (erase) { + delete instance; + instance = NULL; + } + + return instance; +} + +Settings::Settings() +{ +} + +Settings::~Settings() +{ +} + +void Settings::visit(Visitor& visitor) +{ + for (std::list::iterator it = entries.begin();it != entries.end();it++) { + visitor.handle(*(*it)); + } +} + +Value Settings::get(const SettingsEntry& entry) +{ + return entry._value; +} + +void Settings::set(SettingsEntry& entry, const Value val) +{ + entry._value = val; +} + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +/* + * Supported settings entry types are: bool / int and string selection + * + * String selection is used to handle comboboxes and has two values + * per config selection. The first value is used internally for both + * finding the combobox selection and for storing the value in the + * external settings file. The second value is the display value that + * can be translated. + */ +SettingsEntry Settings::indentationWidth("editor", "indentationWidth", Value(Value::RangeType(1, 16)), Value(4)); +SettingsEntry Settings::tabWidth("editor", "tabWidth", Value(Value::RangeType(1, 16)), Value(8)); +SettingsEntry Settings::lineWrap("editor", "lineWrap", values("None", _("None"), "Char", _("Wrap at character boundaries"), "Word", _("Wrap at word boundaries")), Value("Word")); +SettingsEntry Settings::lineWrapIndentationStyle("editor", "lineWrapIndentationStyle", values("Fixed", _("Fixed"), "Same", _("Same"), "Indented", _("Indented")), Value("Fixed")); +SettingsEntry Settings::lineWrapIndentation("editor", "lineWrapIndentation", Value(Value::RangeType(0, 999)), Value(4)); +SettingsEntry Settings::lineWrapVisualizationBegin("editor", "lineWrapVisualizationBegin", values("None", _("None"), "Text", _("Text"), "Border", _("Border"), "Margin", _("Margin")), Value("None")); +SettingsEntry Settings::lineWrapVisualizationEnd("editor", "lineWrapVisualizationEnd", values("None", _("None"), "Text", _("Text"), "Border", _("Border"), "Margin", _("Margin")), Value("Border")); +SettingsEntry Settings::showWhitespace("editor", "showWhitespaces", values("Never", _("Never"), "Always", _("Always"), "AfterIndentation", _("After indentation")), Value("Never")); +SettingsEntry Settings::showWhitespaceSize("editor", "showWhitespacesSize", Value(Value::RangeType(1, 16)), Value(2)); +SettingsEntry Settings::autoIndent("editor", "autoIndent", Value(true), Value(true)); +SettingsEntry Settings::indentStyle("editor", "indentStyle", values("Spaces", _("Spaces"), "Tabs", _("Tabs")), Value("Spaces")); +SettingsEntry Settings::tabKeyFunction("editor", "tabKeyFunction", values("Indent", _("Indent"), "InsertTab", _("Insert Tab")), Value("Indent")); +SettingsEntry Settings::highlightCurrentLine("editor", "highlightCurrentLine", Value(true), Value(true)); +SettingsEntry Settings::enableBraceMatching("editor", "enableBraceMatching", Value(true), Value(true)); + +} diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 00000000..a3099fe8 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#include "value.h" + +namespace Settings { + +class SettingsEntry +{ +private: + std::string _category; + std::string _name; + Value _value; + Value _range; + Value _default; + +public: + const std::string & category() const; + const std::string & name() const; + + virtual const Value & defaultValue() const; + virtual const Value & range() const; + virtual bool is_default() const; + +protected: + SettingsEntry(const std::string category, const std::string name, const Value range, const Value def); + virtual ~SettingsEntry(); + + friend class Settings; +}; + +class Settings +{ +public: + static SettingsEntry indentationWidth; + static SettingsEntry tabWidth; + static SettingsEntry lineWrap; + static SettingsEntry lineWrapIndentationStyle; + static SettingsEntry lineWrapIndentation; + static SettingsEntry lineWrapVisualizationBegin; + static SettingsEntry lineWrapVisualizationEnd; + static SettingsEntry showWhitespace; + static SettingsEntry showWhitespaceSize; + static SettingsEntry autoIndent; + static SettingsEntry indentStyle; + static SettingsEntry tabKeyFunction; + static SettingsEntry highlightCurrentLine; + static SettingsEntry enableBraceMatching; + + static Settings *inst(bool erase = false); + + void visit(class Visitor& visitor); + + Value defaultValue(const SettingsEntry& entry); + Value get(const SettingsEntry& entry); + void set(SettingsEntry& entry, const Value val); + +private: + Settings(); + virtual ~Settings(); +}; + +class Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual void handle(SettingsEntry& entry) const = 0; +}; + +} \ No newline at end of file diff --git a/src/stackcheck.cc b/src/stackcheck.cc new file mode 100644 index 00000000..0f8ca2a8 --- /dev/null +++ b/src/stackcheck.cc @@ -0,0 +1,39 @@ +#include + +#include "stackcheck.h" +#include "PlatformUtils.h" + +StackCheck * StackCheck::self = 0; + +StackCheck::StackCheck() : ptr(0) +{ +} + +StackCheck::~StackCheck() +{ +} + +void StackCheck::init() +{ + unsigned char c; + ptr = &c; +} + +unsigned long StackCheck::size() +{ + unsigned char c; + return std::labs(ptr - &c); +} + +bool StackCheck::check() +{ + return size() >= PlatformUtils::stackLimit(); +} + +StackCheck * StackCheck::inst() +{ + if (self == 0) { + self = new StackCheck(); + } + return self; +} diff --git a/src/stackcheck.h b/src/stackcheck.h new file mode 100644 index 00000000..a2bfae22 --- /dev/null +++ b/src/stackcheck.h @@ -0,0 +1,19 @@ +#pragma once + +class StackCheck +{ +public: + StackCheck(); + virtual ~StackCheck(); + + static StackCheck * inst(); + + void init(); + bool check(); + unsigned long size(); + +private: + unsigned char * ptr; + + static StackCheck *self; +}; diff --git a/src/surface.cc b/src/surface.cc index 9f55d34a..264f9027 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -93,22 +93,22 @@ AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstant Context c(ctx); c.setVariables(args, evalctx); - Value fileval = c.lookup_variable("file"); - node->filename = lookup_file(fileval.isUndefined() ? "" : fileval.toString(), inst->path(), c.documentPath()); + ValuePtr fileval = c.lookup_variable("file"); + node->filename = lookup_file(fileval->isUndefined() ? "" : fileval->toString(), inst->path(), c.documentPath()); - Value center = c.lookup_variable("center", true); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr center = c.lookup_variable("center", true); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } - Value convexity = c.lookup_variable("convexity", true); - if (convexity.type() == Value::NUMBER) { - node->convexity = (int)convexity.toDouble(); + ValuePtr convexity = c.lookup_variable("convexity", true); + if (convexity->type() == Value::NUMBER) { + node->convexity = (int)convexity->toDouble(); } - Value invert = c.lookup_variable("invert", true); - if (invert.type() == Value::BOOL) { - node->invert = invert.toBool(); + ValuePtr invert = c.lookup_variable("invert", true); + if (invert->type() == Value::BOOL) { + node->invert = invert->toBool(); } return node; diff --git a/src/system-gl.h b/src/system-gl.h index 1cabafe5..9d8e6ce0 100644 --- a/src/system-gl.h +++ b/src/system-gl.h @@ -8,9 +8,6 @@ #else #include #include - #ifdef _WIN32 - #include // For the CALLBACK macro - #endif #endif #else // NULLGL diff --git a/src/text.cc b/src/text.cc index 152e64bf..87372fc0 100644 --- a/src/text.cc +++ b/src/text.cc @@ -40,7 +40,7 @@ using namespace boost::assign; // bring 'operator+=()' into scope class TextModule : public AbstractModule { public: - TextModule() : AbstractModule(Feature::ExperimentalTextModule) { } + TextModule() : AbstractModule() { } virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; @@ -54,9 +54,9 @@ AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiat Context c(ctx); c.setVariables(args, evalctx); - double fn = c.lookup_variable("$fn").toDouble(); - double fa = c.lookup_variable("$fa").toDouble(); - double fs = c.lookup_variable("$fs").toDouble(); + double fn = c.lookup_variable("$fn")->toDouble(); + double fa = c.lookup_variable("$fa")->toDouble(); + double fs = c.lookup_variable("$fs")->toDouble(); node->params.set_fn(fn); node->params.set_fa(fa); diff --git a/src/transform.cc b/src/transform.cc index 7259cd67..c86f0301 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -88,45 +88,45 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta if (this->type == SCALE) { Vector3d scalevec(1,1,1); - Value v = c.lookup_variable("v"); - if (!v.getVec3(scalevec[0], scalevec[1], scalevec[2], 1.0)) { + ValuePtr v = c.lookup_variable("v"); + if (!v->getVec3(scalevec[0], scalevec[1], scalevec[2], 1.0)) { double num; - if (v.getDouble(num)) scalevec.setConstant(num); + if (v->getDouble(num)) scalevec.setConstant(num); } node->matrix.scale(scalevec); } else if (this->type == ROTATE) { - Value val_a = c.lookup_variable("a"); - if (val_a.type() == Value::VECTOR) + ValuePtr val_a = c.lookup_variable("a"); + if (val_a->type() == Value::VECTOR) { Eigen::AngleAxisd rotx(0, Vector3d::UnitX()); Eigen::AngleAxisd roty(0, Vector3d::UnitY()); Eigen::AngleAxisd rotz(0, Vector3d::UnitZ()); double a; - if (val_a.toVector().size() > 0) { - val_a.toVector()[0].getDouble(a); + if (val_a->toVector().size() > 0) { + val_a->toVector()[0].getDouble(a); rotx = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitX()); } - if (val_a.toVector().size() > 1) { - val_a.toVector()[1].getDouble(a); + if (val_a->toVector().size() > 1) { + val_a->toVector()[1].getDouble(a); roty = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitY()); } - if (val_a.toVector().size() > 2) { - val_a.toVector()[2].getDouble(a); + if (val_a->toVector().size() > 2) { + val_a->toVector()[2].getDouble(a); rotz = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitZ()); } node->matrix.rotate(rotz * roty * rotx); } else { - Value val_v = c.lookup_variable("v"); + ValuePtr val_v = c.lookup_variable("v"); double a = 0; - val_a.getDouble(a); + val_a->getDouble(a); Vector3d axis(0,0,1); - if (val_v.getVec3(axis[0], axis[1], axis[2])) { + if (val_v->getVec3(axis[0], axis[1], axis[2])) { if (axis.squaredNorm() > 0) axis.normalize(); } @@ -137,10 +137,10 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta } else if (this->type == MIRROR) { - Value val_v = c.lookup_variable("v"); + ValuePtr val_v = c.lookup_variable("v"); double x = 1, y = 0, z = 0; - if (val_v.getVec3(x, y, z)) { + if (val_v->getVec3(x, y, z)) { if (x != 0.0 || y != 0.0 || z != 0.0) { double sn = 1.0 / sqrt(x*x + y*y + z*z); x *= sn, y *= sn, z *= sn; @@ -159,19 +159,20 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta } else if (this->type == TRANSLATE) { - Value v = c.lookup_variable("v"); + ValuePtr v = c.lookup_variable("v"); Vector3d translatevec(0,0,0); - v.getVec3(translatevec[0], translatevec[1], translatevec[2]); + v->getVec3(translatevec[0], translatevec[1], translatevec[2]); node->matrix.translate(translatevec); } else if (this->type == MULTMATRIX) { - Value v = c.lookup_variable("m"); - if (v.type() == Value::VECTOR) { + ValuePtr v = c.lookup_variable("m"); + if (v->type() == Value::VECTOR) { for (int i = 0; i < 16; i++) { size_t x = i / 4, y = i % 4; - if (y < v.toVector().size() && v.toVector()[y].type() == Value::VECTOR && x < v.toVector()[y].toVector().size()) - v.toVector()[y].toVector()[x].getDouble(node->matrix(y, x)); + if (y < v->toVector().size() && v->toVector()[y].type() == + Value::VECTOR && x < v->toVector()[y].toVector().size()) + v->toVector()[y].toVector()[x].getDouble(node->matrix(y, x)); } } } @@ -190,7 +191,7 @@ std::string TransformNode::toString() const for (int j=0;j<4;j++) { stream << "["; for (int i=0;i<4;i++) { - Value v( this->matrix(j, i) ); + Value v(this->matrix(j, i)); stream << v; if (i != 3) stream << ", "; } diff --git a/src/value.cc b/src/value.cc index 66391a95..4723b611 100644 --- a/src/value.cc +++ b/src/value.cc @@ -41,6 +41,9 @@ #include +Value Value::undefined; +ValuePtr ValuePtr::undefined; + std::ostream &operator<<(std::ostream &stream, const Filename &filename) { fs::path fnpath = fs::path( (std::string)filename ); @@ -77,8 +80,6 @@ std::ostream &operator<<(std::ostream &stream, const QuotedString &s) return stream; } -Value Value::undefined; - Value::Value() : value(boost::blank()) { // std::cout << "creating undef\n"; @@ -129,9 +130,19 @@ Value::ValueType Value::type() const return static_cast(this->value.which()); } +bool Value::isDefined() const +{ + return this->type() != UNDEFINED; +} + +bool Value::isDefinedAs(const ValueType type) const +{ + return this->type() == type; +} + bool Value::isUndefined() const { - return this->type() == UNDEFINED; + return !isDefined(); } bool Value::toBool() const @@ -688,7 +699,7 @@ public: } }; -Value Value::operator[](const Value &v) +Value Value::operator[](const Value &v) const { return boost::apply_visitor(bracket_visitor(), this->value, v.value); } @@ -790,3 +801,124 @@ bool Value::RangeType::iterator::operator!=(const self_type &other) const { return !(*this == other); } + +ValuePtr::ValuePtr() +{ + this->reset(new Value()); +} + +ValuePtr::ValuePtr(const Value &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(bool v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(int v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(double v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const std::string &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const char *v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const char v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const Value::VectorType &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const Value::RangeType &v) +{ + this->reset(new Value(v)); +} + +bool ValuePtr::operator==(const ValuePtr &v) const +{ + return ValuePtr(**this == *v); +} + +bool ValuePtr::operator!=(const ValuePtr &v) const +{ + return ValuePtr(**this != *v); +} + +bool ValuePtr::operator<(const ValuePtr &v) const +{ + return ValuePtr(**this < *v); +} + +bool ValuePtr::operator<=(const ValuePtr &v) const +{ + return ValuePtr(**this <= *v); +} + +bool ValuePtr::operator>=(const ValuePtr &v) const +{ + return ValuePtr(**this >= *v); +} + +bool ValuePtr::operator>(const ValuePtr &v) const +{ + return ValuePtr(**this > *v); +} + +ValuePtr ValuePtr::operator-() const +{ + return ValuePtr(-**this); +} + +ValuePtr ValuePtr::operator!() const +{ + return ValuePtr(!**this); +} + +ValuePtr ValuePtr::operator[](const ValuePtr &v) const +{ + return ValuePtr((**this)[*v]); +} + +ValuePtr ValuePtr::operator+(const ValuePtr &v) const +{ + return ValuePtr(**this + *v); +} + +ValuePtr ValuePtr::operator-(const ValuePtr &v) const +{ + return ValuePtr(**this - *v); +} + +ValuePtr ValuePtr::operator*(const ValuePtr &v) const +{ + return ValuePtr(**this * *v); +} + +ValuePtr ValuePtr::operator/(const ValuePtr &v) const +{ + return ValuePtr(**this / *v); +} + +ValuePtr ValuePtr::operator%(const ValuePtr &v) const +{ + return ValuePtr(**this % *v); +} + diff --git a/src/value.h b/src/value.h index 6c59ef2c..f646681f 100644 --- a/src/value.h +++ b/src/value.h @@ -11,6 +11,7 @@ #include #endif #include +#include "memory.h" class QuotedString : public std::string { @@ -81,6 +82,10 @@ public: this->end_val == other.end_val; } + double begin_value() { return begin_val; } + double step_value() { return step_val; } + double end_value() { return end_val; } + iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); } iterator end() { return iterator(*this, RANGE_TYPE_END); } @@ -116,6 +121,8 @@ public: ~Value() {} ValueType type() const; + bool isDefined() const; + bool isDefinedAs(const ValueType type) const; bool isUndefined() const; double toDouble() const; @@ -138,7 +145,7 @@ public: bool operator>=(const Value &v) const; bool operator>(const Value &v) const; Value operator-() const; - Value operator[](const Value &v); + Value operator[](const Value &v) const; Value operator+(const Value &v) const; Value operator-(const Value &v) const; Value operator*(const Value &v) const; @@ -160,3 +167,41 @@ private: Variant value; }; + +class ValuePtr : public shared_ptr +{ +public: + static ValuePtr undefined; + + ValuePtr(); + explicit ValuePtr(const Value &v); + ValuePtr(bool v); + ValuePtr(int v); + ValuePtr(double v); + ValuePtr(const std::string &v); + ValuePtr(const char *v); + ValuePtr(const char v); + ValuePtr(const Value::VectorType &v); + ValuePtr(const Value::RangeType &v); + + operator bool() const { return **this; } + + bool operator==(const ValuePtr &v) const; + bool operator!=(const ValuePtr &v) const; + bool operator<(const ValuePtr &v) const; + bool operator<=(const ValuePtr &v) const; + bool operator>=(const ValuePtr &v) const; + bool operator>(const ValuePtr &v) const; + ValuePtr operator-() const; + ValuePtr operator!() const; + ValuePtr operator[](const ValuePtr &v) const; + ValuePtr operator+(const ValuePtr &v) const; + ValuePtr operator-(const ValuePtr &v) const; + ValuePtr operator*(const ValuePtr &v) const; + ValuePtr operator/(const ValuePtr &v) const; + ValuePtr operator%(const ValuePtr &v) const; + + const Value &operator*() const { return *this->get(); } + +private: +}; diff --git a/testdata/scad/2D/features/highlight-modifier-2d.scad b/testdata/scad/2D/features/highlight-modifier-2d.scad new file mode 100644 index 00000000..7779fd69 --- /dev/null +++ b/testdata/scad/2D/features/highlight-modifier-2d.scad @@ -0,0 +1,10 @@ +difference() { + square(10, center=true); + #circle(3); +} +#if (true) square([11,12]); + +#translate([0,-12]) difference() { + square(10, center = true); + square(5, center = true); +} diff --git a/testdata/scad/2D/features/nullspace-2d.scad b/testdata/scad/2D/features/nullspace-2d.scad new file mode 100644 index 00000000..278981c3 --- /dev/null +++ b/testdata/scad/2D/features/nullspace-2d.scad @@ -0,0 +1,6 @@ +translate() square(0); + +translate() { + square(0); + circle(0); +} diff --git a/testdata/scad/2D/features/offset-tests.scad b/testdata/scad/2D/features/offset-tests.scad index b121b6d0..9148b26a 100644 --- a/testdata/scad/2D/features/offset-tests.scad +++ b/testdata/scad/2D/features/offset-tests.scad @@ -1,43 +1,56 @@ - -module shape1(x, y) { - translate([50 * x, 50 * y]) difference() { - square([30, 30], center = true); - square([8, 8], center = true); - } +module m(x, y) { + translate(60 * [x, y]) children(); } -module shape2(x, y) { - translate([50 * x, 50 * y]) { - polygon(points=[ - [-15, 80],[15, 80],[0,-15],[-8, 60],[8, 60],[0, 5] - ], paths=[ - [0,1,2],[3,4,5] - ]); - } +module shape1(w = 20) { + difference() { + square([ w, w], center = true); + square([10, 10], center = true); + } } -offset(delta = -1, join_type = "miter") shape2(-1, 2); -shape2(0, 2); -offset(delta = 1, join_type = "miter") shape2(1, 2); +module shape2() { + polygon(points=[ + [-15, 80],[15, 80],[0,-15],[-8, 60],[8, 60],[0, 5] + ], paths=[ + [0,1,2],[3,4,5] + ]); +} -offset(delta = -1, join_type = "miter", miter_limit = 10) shape2(2, 2); -offset(delta = 1, join_type = "miter", miter_limit = 10) shape2(3, 2); +m(-1, 0) shape1(); +m(-1, 2) shape2(); -offset(delta = -1, join_type = "bevel") shape2(2, -1); -offset(delta = 1, join_type = "bevel") shape2(3, -1); +m(0, 0) offset() shape1(); +m(0, 1) offset(5) shape1(); +m(0, 2) offset(5) shape2(); -offset(delta = -5, join_type = "round") shape1(-1, 1); -shape1(0, 1); -offset(delta = 5, join_type = "round") shape1(1, 1); +m(1, 0) offset(r = 1) shape1(30); +m(1, 1) offset(r = 5) shape1(30); +m(1, 2) offset(r = 5) shape2(); -offset(-4) shape1(-1, 0); -shape1(0, 0); -offset(4) shape1(1, 0); +m(2, 0) offset(r = -5) shape1(40); +m(2, 1) offset(r = -10.01) shape1(50); +m(2, 2) offset(r = -1) shape2(); -offset(delta = -5) shape1(2, 1); -shape1(0, -1); -offset(delta = 5) shape1(1, -1); +m(3, 0) offset(delta = 4) shape1(); +m(3, 1) offset(delta = 1) shape1(); +m(3, 2) offset(delta = 5) shape2(); + +m(4, 0) offset(delta = -2) shape1(30); +m(4, 1) offset(delta = -5) shape1(40); +m(4, 2) offset(delta = -1) shape2(); + +m(5, 0) offset(delta = 4, chamfer = true) shape1(); +m(5, 1) offset(delta = 1, chamfer = true) shape1(); +m(5, 2) offset(delta = 5, chamfer = true) shape2(); + +m(6, 0) offset(delta = -2, chamfer = true) shape1(30); +m(6, 1) offset(delta = -5, chamfer = true) shape1(40); +m(6, 2) offset(delta = -1, chamfer = true) shape2(); // Bug with fragment calculateion with delta < 1 due to abs() instead of std::abs() -translate([-50,-50]) scale([25,25,1]) - offset(delta = 0.9, join_type="round") square(.1); +m(-2, 1) scale([30, 30]) offset(r = 0.8) square(1); + +// Malformed offsets +offset(); +offset() square(0); diff --git a/testdata/scad/2D/features/polygon-tests.scad b/testdata/scad/2D/features/polygon-tests.scad index ff1d73f0..26bb979d 100644 --- a/testdata/scad/2D/features/polygon-tests.scad +++ b/testdata/scad/2D/features/polygon-tests.scad @@ -26,4 +26,10 @@ translate([0,0,0]) polygon(points = [[0,1], [0,0], [1,0], [1,1], [0.8,0.8], [0.8,0.2], [0.2,0.2], [0.2,0.8]], paths = [[7,6,5,4,3,2,1,0],[7,0,3,4] ]); + +// More malformed polygons +polyhedron(points = undef, paths = [[1, 2, 3]]); +polyhedron(points = [[0,0,0],[1,1,1]], paths = undef); +polyhedron(points=[0], paths = [[0]]); + // FIXME: convexity diff --git a/testdata/scad/2D/features/projection-cut-tests.scad b/testdata/scad/2D/features/projection-cut-tests.scad index 99e7fd37..59898551 100644 --- a/testdata/scad/2D/features/projection-cut-tests.scad +++ b/testdata/scad/2D/features/projection-cut-tests.scad @@ -6,6 +6,9 @@ projection(cut=true) translate([20,0,0]) cube(10, center=true); // Boundary case: clipping the top of a cube translate([0,20,0]) projection(cut=true) translate([0,0,-4.999999]) cube(10, center=true); +// Empty cut +projection(cut=true) translate([0,0,5]) cube(5, center=true); + // holes translate([0,-10,0]) projection(cut=true) { union() { diff --git a/testdata/scad/2D/features/scale2D-tests.scad b/testdata/scad/2D/features/scale2D-tests.scad index 87b22784..385809c7 100644 --- a/testdata/scad/2D/features/scale2D-tests.scad +++ b/testdata/scad/2D/features/scale2D-tests.scad @@ -6,5 +6,5 @@ translate([5,0,0]) scale([2,4/3]) obj2D(); translate([10,0,0]) scale(2) obj2D(); // Scale by zero; 2D object -linear_extrude() scale([0,0]) obj2D(); -linear_extrude() scale([0,1]) obj2D(); +translate([-5,0,0]) linear_extrude() scale([0,0]) obj2D(); +translate([-5,0,0]) linear_extrude() scale([0,1]) obj2D(); diff --git a/testdata/scad/2D/features/text-empty-tests.scad b/testdata/scad/2D/features/text-empty-tests.scad new file mode 100644 index 00000000..00a7134f --- /dev/null +++ b/testdata/scad/2D/features/text-empty-tests.scad @@ -0,0 +1,2 @@ +text(); +text(""); diff --git a/testdata/scad/experimental/text-font-alignment-tests.scad b/testdata/scad/2D/features/text-font-alignment-tests.scad similarity index 100% rename from testdata/scad/experimental/text-font-alignment-tests.scad rename to testdata/scad/2D/features/text-font-alignment-tests.scad diff --git a/testdata/scad/experimental/text-font-composition.scad b/testdata/scad/2D/features/text-font-composition.scad similarity index 100% rename from testdata/scad/experimental/text-font-composition.scad rename to testdata/scad/2D/features/text-font-composition.scad diff --git a/testdata/scad/experimental/text-font-direction-tests.scad b/testdata/scad/2D/features/text-font-direction-tests.scad similarity index 100% rename from testdata/scad/experimental/text-font-direction-tests.scad rename to testdata/scad/2D/features/text-font-direction-tests.scad diff --git a/testdata/scad/experimental/text-font-simple-tests.scad b/testdata/scad/2D/features/text-font-simple-tests.scad similarity index 100% rename from testdata/scad/experimental/text-font-simple-tests.scad rename to testdata/scad/2D/features/text-font-simple-tests.scad diff --git a/testdata/scad/experimental/text-font-symbol.scad b/testdata/scad/2D/features/text-font-symbol.scad similarity index 84% rename from testdata/scad/experimental/text-font-symbol.scad rename to testdata/scad/2D/features/text-font-symbol.scad index eb7d4d48..bbb3dc87 100644 --- a/testdata/scad/experimental/text-font-symbol.scad +++ b/testdata/scad/2D/features/text-font-symbol.scad @@ -1,6 +1,6 @@ -use <../../ttf/marvosym-3.10/marvosym.ttf> -use <../../ttf/liberation-2.00.1/LiberationSans-Regular.ttf> -use <../../ttf/amiri-0.106/amiri-regular.ttf> +use <../../../ttf/marvosym-3.10/marvosym.ttf> +use <../../../ttf/liberation-2.00.1/LiberationSans-Regular.ttf> +use <../../../ttf/amiri-0.106/amiri-regular.ttf> // FIXME: Needs a freely distributable font that is encoded like Webdings // FIXME: with Microsoft/System charmap and charcodes at 0xf000. Using diff --git a/testdata/scad/experimental/text-font-tests.scad b/testdata/scad/2D/features/text-font-tests.scad similarity index 86% rename from testdata/scad/experimental/text-font-tests.scad rename to testdata/scad/2D/features/text-font-tests.scad index ae10b0c9..d7b566f5 100644 --- a/testdata/scad/experimental/text-font-tests.scad +++ b/testdata/scad/2D/features/text-font-tests.scad @@ -1,5 +1,5 @@ -use <../../ttf/amiri-0.106/amiri-regular.ttf> -use <../../ttf/liberation-2.00.1/LiberationSans-Regular.ttf> +use <../../../ttf/amiri-0.106/amiri-regular.ttf> +use <../../../ttf/liberation-2.00.1/LiberationSans-Regular.ttf> t1="OpenSCAD"; t2="الخط الأميري"; diff --git a/testdata/scad/experimental/text-search-test.scad b/testdata/scad/2D/features/text-search-test.scad similarity index 100% rename from testdata/scad/experimental/text-search-test.scad rename to testdata/scad/2D/features/text-search-test.scad diff --git a/testdata/scad/3D/features/2d-3d.scad b/testdata/scad/3D/features/2d-3d.scad index ca9f9663..b2dbd66b 100644 --- a/testdata/scad/3D/features/2d-3d.scad +++ b/testdata/scad/3D/features/2d-3d.scad @@ -1,3 +1,9 @@ // Test a mix of toplevel 2D and 3D objects cube(); translate([2,0,0]) square(); + +// Test mixing of empty 2D and 3D objects +union() { + cube(0); + circle(0); +} diff --git a/testdata/scad/3D/features/nullspace-difference.scad b/testdata/scad/3D/features/nullspace-difference.scad new file mode 100644 index 00000000..7db36eae --- /dev/null +++ b/testdata/scad/3D/features/nullspace-difference.scad @@ -0,0 +1,4 @@ +difference() { + cube(1, center=true); + cube(2, center=true); +} diff --git a/testdata/scad/3D/features/nullspace-intersection.scad b/testdata/scad/3D/features/nullspace-intersection.scad new file mode 100644 index 00000000..b58b3e0e --- /dev/null +++ b/testdata/scad/3D/features/nullspace-intersection.scad @@ -0,0 +1,4 @@ +intersection() { + translate([-2,0,0]) cube(1); + translate([2,0,0]) cube(1); +} diff --git a/testdata/scad/3D/features/nullspace-minkowski-intersection.scad b/testdata/scad/3D/features/nullspace-minkowski-intersection.scad new file mode 100644 index 00000000..617eae82 --- /dev/null +++ b/testdata/scad/3D/features/nullspace-minkowski-intersection.scad @@ -0,0 +1,10 @@ +intersection() { + minkowski() { + intersection() { + translate([-2,0,0]) cube(1); + translate([2,0,0]) cube(1); + } + cube(); + } + cube(); +} diff --git a/testdata/scad/3D/features/nullspace-minkowski.scad b/testdata/scad/3D/features/nullspace-minkowski.scad new file mode 100644 index 00000000..c1cb6b50 --- /dev/null +++ b/testdata/scad/3D/features/nullspace-minkowski.scad @@ -0,0 +1,7 @@ +minkowski() { + intersection() { + translate([-2,0,0]) cube(1); + translate([2,0,0]) cube(1); + } + cube(); +} diff --git a/testdata/scad/3D/features/polyhedron-tests.scad b/testdata/scad/3D/features/polyhedron-tests.scad index 02680c1b..443619d0 100644 --- a/testdata/scad/3D/features/polyhedron-tests.scad +++ b/testdata/scad/3D/features/polyhedron-tests.scad @@ -46,3 +46,6 @@ translate([0,2,0]) difference() { // dont crash (issue #703) polyhedron(points = undef, triangles = [[1, 2, 3]]); polyhedron(points = [[0,0,0],[1,1,1]], triangles = undef); +// More malformed polyhedrons +polyhedron(); +polyhedron(points=[0], faces = [[0]]); diff --git a/testdata/scad/3D/features/scale-mirror2D-3D-tests.scad b/testdata/scad/3D/features/scale-mirror2D-3D-tests.scad new file mode 100644 index 00000000..bca2450f --- /dev/null +++ b/testdata/scad/3D/features/scale-mirror2D-3D-tests.scad @@ -0,0 +1,7 @@ +module obj2D() polygon([[-0.5,-0.5], [1,-0.5], [1,1], [-0.5, 0.5]]) +square([2,3], center=true); + +linear_extrude(1) scale([1, -1]) obj2D(); +translate([3,0,0]) linear_extrude(1) scale([-1, -0.5]) obj2D(); +translate([0,3,0]) linear_extrude(1) mirror() obj2D(); +translate([2,3,0]) linear_extrude(1) mirror([0,1]) obj2D(); diff --git a/testdata/scad/3D/features/surface-tests.scad b/testdata/scad/3D/features/surface-tests.scad index 5a67293d..5462f7b3 100644 --- a/testdata/scad/3D/features/surface-tests.scad +++ b/testdata/scad/3D/features/surface-tests.scad @@ -1,2 +1,4 @@ surface(); +surface("notfound.dat"); +surface("notfound.png"); surface("surface.dat", center=true); diff --git a/testdata/scad/experimental/tessellation-text-test.scad b/testdata/scad/3D/features/tessellation-text-test.scad similarity index 100% rename from testdata/scad/experimental/tessellation-text-test.scad rename to testdata/scad/3D/features/tessellation-text-test.scad diff --git a/testdata/scad/3D/features/transform-tests.scad b/testdata/scad/3D/features/transform-tests.scad index e2dd71e1..0c922c78 100644 --- a/testdata/scad/3D/features/transform-tests.scad +++ b/testdata/scad/3D/features/transform-tests.scad @@ -15,5 +15,3 @@ multmatrix([[1,0.4,0.1,-25], [0.4,0.8,0,-25], [0.2,0.2,0.5,0], [0,0,0,1]]) mycyl(); - -//FIXME: mirror() and scale() \ No newline at end of file diff --git a/testdata/scad/3D/issues/issue1004.scad b/testdata/scad/3D/issues/issue1004.scad new file mode 100644 index 00000000..cf431171 --- /dev/null +++ b/testdata/scad/3D/issues/issue1004.scad @@ -0,0 +1,2 @@ +%square(20); +translate([1,1,0]) cube(10); diff --git a/testdata/scad/bugs/issue1005.scad b/testdata/scad/3D/issues/issue1005.scad similarity index 100% rename from testdata/scad/bugs/issue1005.scad rename to testdata/scad/3D/issues/issue1005.scad diff --git a/testdata/scad/3D/issues/issue1061.scad b/testdata/scad/3D/issues/issue1061.scad new file mode 100644 index 00000000..70cc35cc --- /dev/null +++ b/testdata/scad/3D/issues/issue1061.scad @@ -0,0 +1 @@ +polyhedron(points = [[-5, -5, 0], [-5, 5, 0], [5, 5, 0], [5, -5, 0], [-1.87866, -4.21311, 3], [-4.21311, 5.51058, 3], [5.51058, 7.84504, 3], [7.84504, -1.87866, 3], [1.05099, -3.48892, 6], [-3.48892, 5.42115, 6], [5.42115, 9.96105, 6], [9.96105, 1.05099, 6], [3.39596, -3.09852, 9], [-3.09852, 4.50554, 9], [4.50554, 11, 9], [11, 3.39596, 9], [4.91038, -3.17979, 12], [-3.17979, 2.69807, 12], [2.69807, 10.7882, 12], [10.7882, 4.91038, 12], [5.53441, -3.70439, 15], [-3.70439, 0.122447, 15], [0.122447, 9.36124, 15], [9.36124, 5.53441, 15], [5.39234, -4.48455, 18], [-4.48455, -2.9202, 18], [-2.9202, 6.95668, 18], [6.95668, 5.39234, 18]], faces = [[0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7], [4, 5, 9, 8], [5, 6, 10, 9], [6, 7, 11, 10], [7, 4, 8, 11], [8, 9, 13, 12], [9, 10, 14, 13], [10, 11, 15, 14], [11, 8, 12, 15], [12, 13, 17, 16], [13, 14, 18, 17], [14, 15, 19, 18], [15, 12, 16, 19], [16, 17, 21, 20], [17, 18, 22, 21], [18, 19, 23, 22], [19, 16, 20, 23], [20, 21, 25, 24], [21, 22, 26, 25], [22, 23, 27, 26], [23, 20, 24, 27], [3, 2, 1, 0], [24, 25, 26, 27]]); diff --git a/testdata/scad/3D/issues/issue1069.scad b/testdata/scad/3D/issues/issue1069.scad new file mode 100644 index 00000000..b5048e71 --- /dev/null +++ b/testdata/scad/3D/issues/issue1069.scad @@ -0,0 +1,14 @@ +$fn = 5; + +module object() { + cylinder(r = 1, h = 5); + rotate(45, [1, 0, 0]) cylinder(r = 1, h = 5); +} + +minkowski() { + difference() { + cube(20, center = true); + object(); + } + cube(.5, center = true); +} diff --git a/testdata/scad/bugs/issue835.scad b/testdata/scad/3D/issues/issue835.scad similarity index 75% rename from testdata/scad/bugs/issue835.scad rename to testdata/scad/3D/issues/issue835.scad index 1f467dfc..0de6d455 100644 --- a/testdata/scad/bugs/issue835.scad +++ b/testdata/scad/3D/issues/issue835.scad @@ -1,7 +1,7 @@ difference() { cube([20,20,3], center=true); - linear_extrude(height=60, center=true, convexity=2) { + linear_extrude(height=10, center=true, convexity=2) { translate([5,0]) circle(r=3); translate([-5,5]) circle(r=3); translate([-5,-5]) circle(r=3); diff --git a/testdata/scad/bugs/issue911.scad b/testdata/scad/3D/issues/issue911.scad similarity index 100% rename from testdata/scad/bugs/issue911.scad rename to testdata/scad/3D/issues/issue911.scad diff --git a/testdata/scad/bugs/issue913.scad b/testdata/scad/3D/issues/issue913.scad similarity index 100% rename from testdata/scad/bugs/issue913.scad rename to testdata/scad/3D/issues/issue913.scad diff --git a/testdata/scad/bugs/issue964.scad b/testdata/scad/3D/issues/issue964.scad similarity index 100% rename from testdata/scad/bugs/issue964.scad rename to testdata/scad/3D/issues/issue964.scad diff --git a/testdata/scad/bugs/issue964b.scad b/testdata/scad/3D/issues/issue964b.scad similarity index 100% rename from testdata/scad/bugs/issue964b.scad rename to testdata/scad/3D/issues/issue964b.scad diff --git a/testdata/scad/bugs/issue990.scad b/testdata/scad/3D/issues/issue990.scad similarity index 100% rename from testdata/scad/bugs/issue990.scad rename to testdata/scad/3D/issues/issue990.scad diff --git a/testdata/scad/bugs/issue1004.scad b/testdata/scad/bugs/issue1004.scad deleted file mode 100644 index c4118712..00000000 --- a/testdata/scad/bugs/issue1004.scad +++ /dev/null @@ -1,2 +0,0 @@ -%square(20); -cube(10); diff --git a/testdata/scad/bugs/issue1089.scad b/testdata/scad/bugs/issue1089.scad new file mode 100644 index 00000000..17aa137c --- /dev/null +++ b/testdata/scad/bugs/issue1089.scad @@ -0,0 +1,8 @@ +hull() + polyhedron(points = [ +[-0.249434174697078959, -9.946811825017595865, 7.668480694904857842], +[-0.503165223042536525, 8.8964795095546467252, 0.896037100784631946], +[-0.503165223042536525, -0.66656805007570774091, 3.819754148011998751], +[-0.503165223042536525, -10.229615609706060653, 6.743471195239365557]], +faces = [[0,1,2,3]]); + diff --git a/testdata/scad/bugs/issue1105.scad b/testdata/scad/bugs/issue1105.scad new file mode 100644 index 00000000..6529dc31 --- /dev/null +++ b/testdata/scad/bugs/issue1105.scad @@ -0,0 +1,12 @@ +translate([-31.5, -30, 1]) { + rotate(90) { + translate([2, -51.5, 26]) { + difference() { + cube([4, 14, 4]); + translate([-0.1,-1,2.1]) cube([2.1, 16, 2.1]); + } + } + } +} +translate([3, -25, 1]) rotate_extrude($fn=12) polygon(points = [[0, 20], [0, 30], [7, 26]]); +translate([0, -28, 20]) cube([6, 3, 10]); diff --git a/testdata/scad/bugs/issue1137.scad b/testdata/scad/bugs/issue1137.scad new file mode 100644 index 00000000..9a131352 --- /dev/null +++ b/testdata/scad/bugs/issue1137.scad @@ -0,0 +1,7 @@ +minkowski() { +polyhedron(faces=[[3,2,0,1], [7,6,5,4],[4,5,2,3],[5,6,1,2],[6,7,0,1],[7,4,3,0]], + points=[[0,1,1],[1,1,1],[1,0,1],[0,0,1],[0,0,0],[1,0,0],[1,1,0],[0,1,0]]); + polyhedron(faces=[[3,2,1,0],[7,6,5,4],[4,5,2,3],[5,6,1,2],[6,7,0,1],[7,4,3,0]], + points=[[0,1,1],[1,1,1],[1,0,1],[0,0,1],[0,0,0],[1,0,0],[1,1,0],[0,1,0]]); +} + diff --git a/testdata/scad/bugs/issue1138.scad b/testdata/scad/bugs/issue1138.scad new file mode 100644 index 00000000..ca3f5002 --- /dev/null +++ b/testdata/scad/bugs/issue1138.scad @@ -0,0 +1,12 @@ +minkowski() { + polyhedron(faces=[[4,5,6,7],[3,2,5,4],[2,1,6,5],[1,0,7,6],[0,3,4,7], + [0,8,3], + [1,2,9], + [0,1,9,8], + [2,3,8,9]], + points=[[0,10,10],[10,10,10],[10,0,10],[0,0,10],[0,0,0],[10,0,0],[10,10,0],[0,10,0], + [4.9999999,5,9],[5.0000001,5,9]]); + polyhedron(faces=[[0,1,2,3],[4,5,6,7],[3,2,5,4],[2,1,6,5],[1,0,7,6],[0,3,4,7]], + points=[[0,1,1],[1,1,1],[1,0,1],[0,0,1],[0,0,0],[1,0,0],[1,1,0],[0,1,0]]); +} + diff --git a/testdata/scad/bugs/issue945b.scad b/testdata/scad/bugs/issue945b.scad new file mode 100644 index 00000000..b3f75d0a --- /dev/null +++ b/testdata/scad/bugs/issue945b.scad @@ -0,0 +1,2 @@ +rotate([0, 45, 0]) cube(50, true); +rotate([0, 0, 45]) cube(50, true); diff --git a/testdata/scad/bugs/issue936.scad b/testdata/scad/bugs/issue945c.scad similarity index 100% rename from testdata/scad/bugs/issue936.scad rename to testdata/scad/bugs/issue945c.scad diff --git a/testdata/scad/bugs/issue945d.scad b/testdata/scad/bugs/issue945d.scad new file mode 100644 index 00000000..771e0da6 --- /dev/null +++ b/testdata/scad/bugs/issue945d.scad @@ -0,0 +1,2 @@ +translate([-5,5,0]) cube(10); +rotate([0, -90, 0]) cube(10); diff --git a/testdata/scad/misc/expression-evaluation-tests.scad b/testdata/scad/misc/expression-evaluation-tests.scad new file mode 100644 index 00000000..0dd5ff37 --- /dev/null +++ b/testdata/scad/misc/expression-evaluation-tests.scad @@ -0,0 +1,41 @@ +values = [ + undef, // special undefined value + 1/0, // infinity + -1/0, // -infinity + 0/0, // not a number + 0, -4.2, -2, 3, 42.42, 242, // number + true, false, // boolean + "", "text", // string + [], [ 0 ], [ 1 ], // vector + [ 0 : 0 ], [ 1 : 2 ] // range +]; + +array = [ "a", "b", "c", "d" ]; + +for (v = values) { + echo(v = v, op = "not v", result = !v); + echo(v = v, op = "-v", result = -v); + echo(v = v, op = "v *", result = v * 3); + echo(v = v, op = "* v", result = 2 * v); + echo(v = v, op = "v /", result = v / 3); + echo(v = v, op = "/ v", result = 2 / v); + echo(v = v, op = "v %", result = v % 3); + echo(v = v, op = "% v", result = 2 % v); + echo(v = v, op = "v +", result = v + 3); + echo(v = v, op = "+ v", result = 2 + v); + echo(v = v, op = "v -", result = v - 3); + echo(v = v, op = "- v", result = 2 - v); + echo(v = v, op = "v and true", result = v && true); + echo(v = v, op = "v and false", result = v && false); + echo(v = v, op = "v or true", result = v || true); + echo(v = v, op = "v or false", result = v || false); +// echo(v = v, op = "<", result = v < 3); +// echo(v = v, op = "<=", result = v <= 3); +// echo(v = v, op = "==", result = v == 3); +// echo(v = v, op = "!=", result = v != 3); +// echo(v = v, op = ">=", result = v >= 3); +// echo(v = v, op = ">", result = v > 3); + echo(v = v, op = "[v]", result = array[v]); + echo(v = v, op = "v[0]", result = v[0]); + echo(v = v, op = "v[4]", result = v[4]); +} diff --git a/testdata/scad/misc/nonmanifold-polyhedron.scad b/testdata/scad/misc/nonmanifold-polyhedron.scad new file mode 100644 index 00000000..a1b56654 --- /dev/null +++ b/testdata/scad/misc/nonmanifold-polyhedron.scad @@ -0,0 +1,16 @@ +polyhedron(points=[ +[0,0,0], +[10,0,0], +[10,10,0], +[0,10,0], +[0,-20,20], +[10,-20,20], +[10,-20,30], +[0,-20,30] +], +faces = [[0,1,2,3],[4,5,6,7], +[1,2,5,4], +[2,3,6,5], +[3,0,6,5], +[0,1,4,7] +]); diff --git a/testdata/scad/misc/recursion-tests.scad b/testdata/scad/misc/recursion-tests.scad index 2a07b915..78b31e35 100644 --- a/testdata/scad/misc/recursion-tests.scad +++ b/testdata/scad/misc/recursion-tests.scad @@ -1,2 +1,5 @@ function crash() = crash(); echo(crash()); + +module crash() crash(); +crash(); diff --git a/testdata/scad/misc/tail-recursion-tests.scad b/testdata/scad/misc/tail-recursion-tests.scad new file mode 100644 index 00000000..5795530a --- /dev/null +++ b/testdata/scad/misc/tail-recursion-tests.scad @@ -0,0 +1,31 @@ +function substring(text, start, end = -1, idx = -1, res = "") = + idx < end && idx < len(text) + ? substring(text, start, end, idx < 0 ? start + 1 : idx + 1, str(res, text[idx < 0 ? start : idx])) + : res; + +// normal recursion, no tail-recursion elimination possible +function f3a(a, ret = 0) = a <= 0 ? 0 : a + f3a(a - 1); +echo("without tail-recursion eliminiation: ", f3a(100)); + +// this allows tail-recursion eliminiation +function f3b(a, ret = 0) = a <= 0 ? ret : f3b(a - 1, ret + a); +echo("with tail-recursion eliminiation: ", f3b(100)); + +// check tail-recursion eliminiation by using a high loop count +function f3c(a, ret = 0) = a <= 0 ? ret : f3c(a - 1, ret + a); +echo("with tail-recursion eliminiation: ", f3c(2000)); + +// use nested function call +function f1(x, y = []) = x <= 0 ? y : f1(x - 1, concat(y, [[x, x]])); +echo(f1(2000)[20]); + +// recursion in the "false" part of the ternary operator +function c(a, b) = chr(a % 26 + b); +function f2a(x, y = 0, t = "") = x <= 0 ? t : f2a(x - 1, y + 2, str(t, c(y, 65))); +s1 = f2a(50000); +echo(len(s1), substring(s1, 0, 40)); + +// recursion in the "true" part of the ternary operator +function f2b(x, y = 0, t = "") = x > 0 ? f2b(x - 1, y + 1, str(t, chr((y % 26) + 97))) : t; +s2 = f2b(50000); +echo(len(s2), substring(s2, 0, 40)); diff --git a/testdata/scad/templates/import_dxf-tests-template.scad b/testdata/scad/templates/import_dxf-tests-template.scad index 1a8b0df5..396ce1ef 100644 --- a/testdata/scad/templates/import_dxf-tests-template.scad +++ b/testdata/scad/templates/import_dxf-tests-template.scad @@ -1,4 +1,5 @@ import(); +import("notfound.dxf"); translate([-210,0,0]) import(file="../../../dxf/polygons.dxf"); translate([-210,0,0]) import(file="../../../dxf/polygons.dxf", origin=[0,110]); translate([-210,0,0]) import(file="../../../dxf/polygons.dxf", origin=[110,110], scale=0.5); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2d5f05f1..608b60c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,7 +78,6 @@ endif() # Most tests will fail, but it can be used for testing/experiments if(NULLGL) - set(ENABLE_OPENCSG_FLAG "") # OpenCSG is entirely an OpenGL software set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNULLGL") set(SKIP_IMAGEMAGICK "1") # we dont generate png, so nothing to compare else() @@ -620,6 +619,7 @@ set(CORE_SOURCES ../src/calc.cc ../src/expr.cc ../src/func.cc + ../src/stackcheck.cc ../src/localscope.cc ../src/module.cc ../src/ModuleCache.cc @@ -632,7 +632,6 @@ set(CORE_SOURCES ../src/csgtermnormalizer.cc ../src/Geometry.cc ../src/Polygon2d.cc - ../src/polyset.cc ../src/csgops.cc ../src/transform.cc ../src/color.cc @@ -666,7 +665,10 @@ set(NOCGAL_SOURCES ../src/builtin.cc ../src/import.cc ../src/export.cc - ../src/LibraryInfo.cc) + ../src/LibraryInfo.cc + ../src/polyset.cc + ../src/polyset-utils.cc) + set(CGAL_SOURCES ${NOCGAL_SOURCES} @@ -674,10 +676,11 @@ set(CGAL_SOURCES ../src/CGAL_Nef_polyhedron.cc ../src/cgalutils.cc ../src/cgalutils-tess.cc + ../src/cgalutils-polyhedron.cc + ../src/cgalutils-tess-old.cc ../src/CGALCache.cc ../src/CGAL_Nef_polyhedron_DxfData.cc ../src/Polygon2d-CGAL.cc - ../src/polyset-utils.cc ../src/svg.cc ../src/GeometryEvaluator.cc) @@ -735,16 +738,10 @@ target_link_libraries(tests-cgal tests-common ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_L # # Create non-CGAL tests # +add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) +target_link_libraries(tests-nocgal tests-common) if (NOT NULLGL) - add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) - set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS ${ENABLE_OPENCSG_FLAG}) - target_link_libraries(tests-nocgal tests-common) -else() - message(STATUS "NULLGL: cannot use GL/GLU tessellator. see dxftess.cc") - message(STATUS "NULLGL: non-CGAL tests will use CGAL's tessellator") - add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) - set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS ${ENABLE_OPENCSG_FLAG}) - target_link_libraries(tests-nocgal tests-common) + set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG}") endif() add_library(tests-offscreen STATIC ${OFFSCREEN_SOURCES}) @@ -754,20 +751,20 @@ set_target_properties(tests-offscreen PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG # modulecachetest # add_executable(modulecachetest modulecachetest.cc) -target_link_libraries(modulecachetest tests-nocgal) +target_link_libraries(modulecachetest tests-nocgal ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY}) # # csgtexttest # add_executable(csgtexttest csgtexttest.cc CSGTextRenderer.cc CSGTextCache.cc) -target_link_libraries(csgtexttest tests-nocgal) +target_link_libraries(csgtexttest tests-nocgal ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY}) # # cgalcachetest # add_executable(cgalcachetest cgalcachetest.cc) -set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG} -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") -target_link_libraries(cgalcachetest tests-cgal ${GLEW_LIBRARY}) +set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") +target_link_libraries(cgalcachetest tests-cgal ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY}) # # openscad no-qt @@ -917,7 +914,7 @@ function(add_cmdline_test TESTCMD_BASENAME) if( "${match_result1}" STREQUAL "" ) if( "${match_result2}" STREQUAL "" ) if( "${match_result3}" STREQUAL "" ) - set(EXPERIMENTAL_OPTION "--enable=text") + set(EXPERIMENTAL_OPTION "") endif() endif() endif() @@ -936,6 +933,11 @@ function(add_cmdline_test TESTCMD_BASENAME) set_test_config(Default ${TEST_FULLNAME}) endif() set_test_config(All ${TEST_FULLNAME}) + list(FIND FOUNDCONFIGS Bugs FOUND) + if (FOUND EQUAL -1) + set_test_config(Good ${TEST_FULLNAME}) + endif() + unset(FOUNDCONFIGS) get_test_config(${TEST_FULLNAME} FOUNDCONFIGS) set(CONFARG CONFIGURATIONS) @@ -1030,6 +1032,7 @@ list(APPEND EXAMPLE_FILES ${EXAMPLE_3D_FILES} ${EXAMPLE_2D_FILES}) list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/3D/features/for-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/expression-evaluation-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/echo-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/parser-tests.scad @@ -1043,6 +1046,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests-unicode.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/tail-recursion-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad @@ -1087,8 +1091,7 @@ list(APPEND CGALPNGTEST_3D_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/inclu ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/bad-stl-pcbvicebar.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/bad-stl-tardis.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/bad-stl-wing.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/rotate_extrude-hole.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/experimental/tessellation-text-test.scad) + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/rotate_extrude-hole.scad) list(APPEND CGALPNGTEST_FILES ${CGALPNGTEST_2D_FILES} ${CGALPNGTEST_3D_FILES}) list(APPEND OPENCSGTEST_FILES ${CGALPNGTEST_FILES}) @@ -1097,7 +1100,6 @@ list(APPEND THROWNTOGETHERTEST_FILES ${OPENCSGTEST_FILES}) list(APPEND CGALSTLSANITYTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/normal-nan.scad) -file(GLOB EXPERIMENTAL_TEXT_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/experimental/text-*.scad) # Issue #899 set_test_config(Bugs dxfpngtest_text-font-direction-tests cgalpngtest_text-font-direction-tests @@ -1118,13 +1120,19 @@ list(APPEND EXPORT3D_TEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/3D/features ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/bad-stl-wing.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/rotate_extrude-hole.scad) +list(APPEND EXPORTCSG_TEST_FILES + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/nonmanifold-polyhedron.scad) + disable_tests( - # This doesn't output anything + # These don't output anything + dxfpngtest_text-empty-tests dxfpngtest_nothing-decimal-comma-separated + dxfpngtest_nullspace-2d # Not useful throwntogethertest_internal-cavity throwntogethertest_internal-cavity-polyhedron + throwntogethertest_nullspace-difference # these take too long, for little relative gain in testing stlpngtest_iteration @@ -1136,7 +1144,7 @@ disable_tests( ) # 2D tests -list(APPEND FILES_2D ${FEATURES_2D_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_2D_FILES} ${EXPERIMENTAL_TEXT_FILES}) +list(APPEND FILES_2D ${FEATURES_2D_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_2D_FILES}) list(APPEND ALL_2D_FILES ${FILES_2D}) # FIXME: This test illustrates a weakness in child() combined with modifiers. @@ -1202,20 +1210,18 @@ list(APPEND BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue584.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue666.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue791.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue802.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue835.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue899.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue904.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue911.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue913.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue936.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue945.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue964.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue964b.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue990.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1004.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1005.scad) + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue945b.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue945c.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue945d.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1089.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105.scad) list(APPEND EXPORT3D_TEST_FILES ${BUGS_FILES}) -list(REMOVE_ITEM EXPORT3D_TEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue899.scad) +list(REMOVE_ITEM EXPORT3D_TEST_FILES + ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue899.scad) +#list(APPEND EXPORTCSG_TEST_FILES ) list(APPEND ALL_2D_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue899.scad) list(APPEND OPENCSGTEST_FILES ${BUGS_FILES}) @@ -1233,6 +1239,8 @@ foreach(FILE ${BUGS_FILES}) set_test_config(Bugs ${TEST_FULLNAME}) get_test_fullname(stlpngtest ${FILE} TEST_FULLNAME) set_test_config(Bugs ${TEST_FULLNAME}) + get_test_fullname(stlcsgpngtest ${FILE} TEST_FULLNAME) + set_test_config(Bugs ${TEST_FULLNAME}) endforeach() # Examples @@ -1311,6 +1319,7 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake ${TMP}) # o stlpngtest: Export to STL, Re-import and render to PNG (--render=cgal) # o offpngtest: Export to OFF, Re-import and render to PNG (--render=cgal) # o dxfpngtest: Export to DXF, Re-import and render to PNG (--render=cgal) +# o stlcsgpngtest: Export to STL, Re-import and render to PNG (--render) # add_cmdline_test(moduledumptest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX ast FILES @@ -1328,8 +1337,8 @@ add_cmdline_test(csgtermtest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX term FILES add_cmdline_test(echotest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX echo FILES ${ECHO_FILES}) add_cmdline_test(dumptest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX csg FILES ${DUMPTEST_FILES}) add_cmdline_test(dumptest-examples EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX csg FILES ${EXAMPLE_FILES}) -add_cmdline_test(cgalpngtest EXE ${OPENSCAD_BINPATH} ARGS --enable=text --render -o SUFFIX png FILES ${CGALPNGTEST_FILES}) -add_cmdline_test(opencsgtest EXE ${OPENSCAD_BINPATH} ARGS --enable=text -o SUFFIX png FILES ${OPENCSGTEST_FILES}) +add_cmdline_test(cgalpngtest EXE ${OPENSCAD_BINPATH} ARGS --render -o SUFFIX png FILES ${CGALPNGTEST_FILES}) +add_cmdline_test(opencsgtest EXE ${OPENSCAD_BINPATH} ARGS -o SUFFIX png FILES ${OPENCSGTEST_FILES}) add_cmdline_test(csgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=csg --render EXPECTEDDIR cgalpngtest SUFFIX png FILES ${CGALPNGTEST_FILES}) add_cmdline_test(throwntogethertest EXE ${OPENSCAD_BINPATH} ARGS --preview=throwntogether -o SUFFIX png FILES ${THROWNTOGETHERTEST_FILES}) # FIXME: We don't actually need to compare the output of cgalstlsanitytest @@ -1347,11 +1356,14 @@ set_test_config(Bugs offpngtest_polyhedron-tests offpngtest_bad-stl-pcbvicebar offpngtest_bad-stl-tardis offpngtest_bad-stl-wing) +# No issue - this was introduced when fixing #1033 +set_test_config(Bugs stlpngtest_bad-stl-wing) -add_cmdline_test(monotonepngtest EXE ${OPENSCAD_BINPATH} ARGS --colorscheme=Monotone --enable=text --render -o SUFFIX png FILES ${EXPORT3D_TEST_FILES}) +add_cmdline_test(monotonepngtest EXE ${OPENSCAD_BINPATH} ARGS --colorscheme=Monotone --render -o SUFFIX png FILES ${EXPORT3D_TEST_FILES}) add_cmdline_test(stlpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=STL --render=cgal EXPECTEDDIR monotonepngtest SUFFIX png FILES ${EXPORT3D_TEST_FILES}) add_cmdline_test(offpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=OFF --render=cgal EXPECTEDDIR monotonepngtest SUFFIX png FILES ${EXPORT3D_TEST_FILES}) add_cmdline_test(dxfpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=DXF --render=cgal EXPECTEDDIR cgalpngtest SUFFIX png FILES ${FILES_2D}) +add_cmdline_test(stlcsgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=STL --render EXPECTEDDIR monotonepngtest SUFFIX png FILES ${EXPORTCSG_TEST_FILES}) # @@ -1363,11 +1375,6 @@ add_failing_test(offfailedtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DI # # Add experimental tests # -add_cmdline_test(dumptest EXE ${OPENSCAD_BINPATH} ARGS --enable=text -o SUFFIX csg FILES ${EXPERIMENTAL_TEXT_FILES}) -add_cmdline_test(cgalpngtest EXE ${OPENSCAD_BINPATH} ARGS --render --enable=text -o SUFFIX png FILES ${EXPERIMENTAL_TEXT_FILES}) -add_cmdline_test(opencsgtest EXE ${OPENSCAD_BINPATH} ARGS --enable=text -o SUFFIX png FILES ${EXPERIMENTAL_TEXT_FILES}) -add_cmdline_test(csgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=csg --render EXPECTEDDIR cgalpngtest SUFFIX png FILES ${EXPERIMENTAL_TEXT_FILES}) -add_cmdline_test(throwntogethertest EXE ${OPENSCAD_BINPATH} ARGS --preview=throwntogether --enable=text -o SUFFIX png FILES ${EXPERIMENTAL_TEXT_FILES}) # Tests using the actual OpenSCAD binary diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc index 97aa5be1..61a9d3e4 100644 --- a/tests/cgalcachetest.cc +++ b/tests/cgalcachetest.cc @@ -38,6 +38,7 @@ #include "CGAL_Nef_polyhedron.h" #include "GeometryEvaluator.h" #include "CGALCache.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -52,6 +53,7 @@ namespace fs = boost::filesystem; #include namespace po = boost::program_options; #include "boosty.h" +#include "PlatformUtils.h" std::string commandline_commands; std::string currentdir; @@ -87,6 +89,8 @@ int main(int argc, char **argv) { const char *filename, *outfilename = NULL; size_t cgalcachesize = 1*1024*1024; + StackCheck::inst()->init(); + po::variables_map vm; try { vm = parse_options(argc, argv); @@ -116,8 +120,8 @@ int main(int argc, char **argv) currentdir = boosty::stringy(fs::current_path()); - parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); - add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); + PlatformUtils::registerApplicationPath(boosty::stringy(fs::path(argv[0]).branch_path())); + parser_init(); ModuleContext top_ctx; top_ctx.registerBuiltin(); diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc index 70c246dc..11e687d9 100644 --- a/tests/csgtexttest.cc +++ b/tests/csgtexttest.cc @@ -37,6 +37,7 @@ #include "builtin.h" #include "Tree.h" #include "PlatformUtils.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -49,6 +50,7 @@ #include namespace fs = boost::filesystem; #include "boosty.h" +#include "PlatformUtils.h" std::string commandline_commands; std::string currentdir; @@ -72,16 +74,16 @@ int main(int argc, char **argv) int rc = 0; + StackCheck::inst()->init(); Builtins::instance()->initialize(); fs::path original_path = fs::current_path(); currentdir = boosty::stringy( fs::current_path() ); - std::string appicationpath = boosty::stringy(fs::path(argv[0]).branch_path()); - PlatformUtils::registerApplicationPath(appicationpath); - parser_init(appicationpath); - add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); + std::string applicationpath = boosty::stringy(fs::path(argv[0]).branch_path()); + PlatformUtils::registerApplicationPath(applicationpath); + parser_init(); ModuleContext top_ctx; top_ctx.registerBuiltin(); diff --git a/tests/export_import_pngtest.py b/tests/export_import_pngtest.py index 710aa880..3b85f72b 100644 --- a/tests/export_import_pngtest.py +++ b/tests/export_import_pngtest.py @@ -83,7 +83,7 @@ if inputsuffix != '.scad' and inputsuffix != '.csg': # # First run: Just export the given filetype # -export_cmd = [args.openscad, inputfile, '--enable=text', '-o', exportfile] + remaining_args +export_cmd = [args.openscad, inputfile, '-o', exportfile] + remaining_args print('Running OpenSCAD #1:') print(' '.join(export_cmd)) result = subprocess.call(export_cmd) @@ -100,7 +100,7 @@ if args.format != 'csg': newscadfile += '.scad' createImport(exportfile, newscadfile) -create_png_cmd = [args.openscad, newscadfile, '--enable=text', '-o', pngfile] + remaining_args +create_png_cmd = [args.openscad, newscadfile, '-o', pngfile] + remaining_args print('Running OpenSCAD #2:') print(' '.join(create_png_cmd)) fontdir = os.path.join(os.path.dirname(args.openscad), "..", "testdata"); diff --git a/tests/modulecachetest.cc b/tests/modulecachetest.cc index 5531461c..78398241 100644 --- a/tests/modulecachetest.cc +++ b/tests/modulecachetest.cc @@ -34,6 +34,7 @@ #include "export.h" #include "builtin.h" #include "Tree.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -46,6 +47,7 @@ #include namespace fs = boost::filesystem; #include "boosty.h" +#include "PlatformUtils.h" std::string commandline_commands; std::string currentdir; @@ -67,14 +69,15 @@ int main(int argc, char **argv) int rc = 0; + StackCheck::inst()->init(); Builtins::instance()->initialize(); fs::path original_path = fs::current_path(); currentdir = boosty::stringy( fs::current_path() ); - parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); - add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); + PlatformUtils::registerApplicationPath(boosty::stringy(fs::path(argv[0]).branch_path())); + parser_init(); ModuleContext top_ctx; top_ctx.registerBuiltin(); diff --git a/tests/regression/cgalpngtest/highlight-modifier-2d-expected.png b/tests/regression/cgalpngtest/highlight-modifier-2d-expected.png new file mode 100644 index 00000000..9c2cedf1 Binary files /dev/null and b/tests/regression/cgalpngtest/highlight-modifier-2d-expected.png differ diff --git a/tests/regression/cgalpngtest/issue1061-expected.png b/tests/regression/cgalpngtest/issue1061-expected.png new file mode 100644 index 00000000..78bd8525 Binary files /dev/null and b/tests/regression/cgalpngtest/issue1061-expected.png differ diff --git a/tests/regression/cgalpngtest/issue1069-expected.png b/tests/regression/cgalpngtest/issue1069-expected.png new file mode 100644 index 00000000..677d52ca Binary files /dev/null and b/tests/regression/cgalpngtest/issue1069-expected.png differ diff --git a/tests/regression/cgalpngtest/issue1089-expected.png b/tests/regression/cgalpngtest/issue1089-expected.png new file mode 100644 index 00000000..5fdebc24 Binary files /dev/null and b/tests/regression/cgalpngtest/issue1089-expected.png differ diff --git a/tests/regression/cgalpngtest/issue1105-expected.png b/tests/regression/cgalpngtest/issue1105-expected.png new file mode 100644 index 00000000..9c8d7cb0 Binary files /dev/null and b/tests/regression/cgalpngtest/issue1105-expected.png differ diff --git a/tests/regression/cgalpngtest/issue945b-expected.png b/tests/regression/cgalpngtest/issue945b-expected.png new file mode 100644 index 00000000..88ee57b2 Binary files /dev/null and b/tests/regression/cgalpngtest/issue945b-expected.png differ diff --git a/tests/regression/cgalpngtest/issue936-expected.png b/tests/regression/cgalpngtest/issue945c-expected.png similarity index 100% rename from tests/regression/cgalpngtest/issue936-expected.png rename to tests/regression/cgalpngtest/issue945c-expected.png diff --git a/tests/regression/cgalpngtest/issue945d-expected.png b/tests/regression/cgalpngtest/issue945d-expected.png new file mode 100644 index 00000000..7e603120 Binary files /dev/null and b/tests/regression/cgalpngtest/issue945d-expected.png differ diff --git a/tests/regression/cgalpngtest/nullspace-2d-expected.png b/tests/regression/cgalpngtest/nullspace-2d-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/nullspace-2d-expected.png differ diff --git a/tests/regression/cgalpngtest/nullspace-difference-expected.png b/tests/regression/cgalpngtest/nullspace-difference-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/nullspace-difference-expected.png differ diff --git a/tests/regression/cgalpngtest/nullspace-intersection-expected.png b/tests/regression/cgalpngtest/nullspace-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/nullspace-intersection-expected.png differ diff --git a/tests/regression/cgalpngtest/nullspace-minkowski-expected.png b/tests/regression/cgalpngtest/nullspace-minkowski-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/nullspace-minkowski-expected.png differ diff --git a/tests/regression/cgalpngtest/nullspace-minkowski-intersection-expected.png b/tests/regression/cgalpngtest/nullspace-minkowski-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/nullspace-minkowski-intersection-expected.png differ diff --git a/tests/regression/cgalpngtest/offset-tests-expected.png b/tests/regression/cgalpngtest/offset-tests-expected.png index c1f667be..3da4452b 100644 Binary files a/tests/regression/cgalpngtest/offset-tests-expected.png and b/tests/regression/cgalpngtest/offset-tests-expected.png differ diff --git a/tests/regression/cgalpngtest/scale-mirror2D-3D-tests-expected.png b/tests/regression/cgalpngtest/scale-mirror2D-3D-tests-expected.png new file mode 100644 index 00000000..5d85630a Binary files /dev/null and b/tests/regression/cgalpngtest/scale-mirror2D-3D-tests-expected.png differ diff --git a/tests/regression/cgalpngtest/text-empty-tests-expected.png b/tests/regression/cgalpngtest/text-empty-tests-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/cgalpngtest/text-empty-tests-expected.png differ diff --git a/tests/regression/csgtexttest/allmodules-expected.txt b/tests/regression/csgtexttest/allmodules-expected.txt index c881bfa5..cfd35242 100644 --- a/tests/regression/csgtexttest/allmodules-expected.txt +++ b/tests/regression/csgtexttest/allmodules-expected.txt @@ -1 +1 @@ -group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40+offset) +group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40+offset+text) diff --git a/tests/regression/dumptest-examples/logo_and_text-expected.csg b/tests/regression/dumptest-examples/logo_and_text-expected.csg index 51c60980..aff99f53 100644 --- a/tests/regression/dumptest-examples/logo_and_text-expected.csg +++ b/tests/regression/dumptest-examples/logo_and_text-expected.csg @@ -29,7 +29,9 @@ group() { group() { group() { multmatrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { - linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "Open", size = 42, spacing = 1, font = "Liberation Serif:style=Bold", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 16, $fa = 12, $fs = 2); + } } } } @@ -42,7 +44,9 @@ group() { group() { group() { multmatrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { - linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "SCAD", size = 42, spacing = 1, font = "Liberation Serif:style=Bold", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 16, $fa = 12, $fs = 2); + } } } } @@ -55,7 +59,9 @@ group() { group() { group() { multmatrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { - linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "The Programmers", size = 18, spacing = 1, font = "Liberation Serif", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 16, $fa = 12, $fs = 2); + } } } } @@ -68,7 +74,9 @@ group() { group() { group() { multmatrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { - linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "Solid 3D CAD Modeller", size = 18, spacing = 1, font = "Liberation Serif", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 16, $fa = 12, $fs = 2); + } } } } diff --git a/tests/regression/dumptest-examples/offset-expected.csg b/tests/regression/dumptest-examples/offset-expected.csg index 41de8cc3..5c640f8e 100644 --- a/tests/regression/dumptest-examples/offset-expected.csg +++ b/tests/regression/dumptest-examples/offset-expected.csg @@ -1,7 +1,7 @@ group() { group(); linear_extrude(height = 20, center = false, convexity = 1, scale = [0.5, 0.5], $fn = 40, $fa = 12, $fs = 2) { - offset(delta = 10, join_type = "round", $fn = 40, $fa = 12, $fs = 2) { + offset(r = 10, $fn = 40, $fa = 12, $fs = 2) { square(size = [50, 50], center = true); } } @@ -9,12 +9,12 @@ group() { linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 40, $fa = 12, $fs = 2) { group() { difference() { - offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 40, $fa = 12, $fs = 2) { + offset(r = 1, $fn = 40, $fa = 12, $fs = 2) { group() { circle($fn = 40, $fa = 12, $fs = 2, r = 15); } } - offset(delta = -1, join_type = "miter", miter_limit = 2, $fn = 40, $fa = 12, $fs = 2) { + offset(r = -1, $fn = 40, $fa = 12, $fs = 2) { group() { circle($fn = 40, $fa = 12, $fs = 2, r = 15); } diff --git a/tests/regression/dumptest-examples/text_on_cube-expected.csg b/tests/regression/dumptest-examples/text_on_cube-expected.csg index f05e5658..a5bbed7c 100644 --- a/tests/regression/dumptest-examples/text_on_cube-expected.csg +++ b/tests/regression/dumptest-examples/text_on_cube-expected.csg @@ -8,40 +8,52 @@ group() { multmatrix([[1, 0, 0, 0], [0, 1, 0, -27.5], [0, 0, 1, 0], [0, 0, 0, 1]]) { multmatrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "C", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } } multmatrix([[1, 0, 0, 27.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { multmatrix([[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "U", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } } multmatrix([[1, 0, 0, 0], [0, 1, 0, 27.5], [0, 0, 1, 0], [0, 0, 0, 1]]) { multmatrix([[-1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "B", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } } multmatrix([[1, 0, 0, -27.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { multmatrix([[0, 0, -1, 0], [-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "E", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } } } multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 27.5], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "☺", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -32.5], [0, 0, 0, 1]]) { group() { - linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2); + linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + text(text = "☼", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } } } } diff --git a/tests/regression/dumptest/2d-3d-expected.csg b/tests/regression/dumptest/2d-3d-expected.csg index a2f6422c..06daccb7 100644 --- a/tests/regression/dumptest/2d-3d-expected.csg +++ b/tests/regression/dumptest/2d-3d-expected.csg @@ -3,4 +3,8 @@ group() { multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { square(size = [1, 1], center = false); } + union() { + cube(size = [0, 0, 0], center = false); + circle($fn = 0, $fa = 12, $fs = 2, r = 0); + } } diff --git a/tests/regression/dumptest/allmodules-expected.csg b/tests/regression/dumptest/allmodules-expected.csg index f2a5308d..9af5da4b 100644 --- a/tests/regression/dumptest/allmodules-expected.csg +++ b/tests/regression/dumptest/allmodules-expected.csg @@ -39,5 +39,6 @@ group() { multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]); multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]); color([-1, -1, -1, 1]); - offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2); + offset(r = 1, $fn = 0, $fa = 12, $fs = 2); + text(text = "", size = 10, spacing = 1, font = "", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 0, $fa = 12, $fs = 2); } diff --git a/tests/regression/dumptest/highlight-modifier-2d-expected.csg b/tests/regression/dumptest/highlight-modifier-2d-expected.csg new file mode 100644 index 00000000..833d1ea4 --- /dev/null +++ b/tests/regression/dumptest/highlight-modifier-2d-expected.csg @@ -0,0 +1,15 @@ +group() { + difference() { + square(size = [10, 10], center = true); +# circle($fn = 0, $fa = 12, $fs = 2, r = 3); + } +# group() { + square(size = [11, 12], center = false); + } +# multmatrix([[1, 0, 0, 0], [0, 1, 0, -12], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + square(size = [10, 10], center = true); + square(size = [5, 5], center = true); + } + } +} diff --git a/tests/regression/dumptest/import_dxf-tests-expected.csg b/tests/regression/dumptest/import_dxf-tests-expected.csg index 2148ea74..1defde00 100644 --- a/tests/regression/dumptest/import_dxf-tests-expected.csg +++ b/tests/regression/dumptest/import_dxf-tests-expected.csg @@ -1,5 +1,6 @@ group() { import(file = "", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2); + import(file = "notfound.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2); multmatrix([[1, 0, 0, -210], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { import(file = "../../../dxf/polygons.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2); } diff --git a/tests/regression/dumptest/nullspace-2d-expected.csg b/tests/regression/dumptest/nullspace-2d-expected.csg new file mode 100644 index 00000000..556f32b8 --- /dev/null +++ b/tests/regression/dumptest/nullspace-2d-expected.csg @@ -0,0 +1,9 @@ +group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + square(size = [0, 0], center = false); + } + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + square(size = [0, 0], center = false); + circle($fn = 0, $fa = 12, $fs = 2, r = 0); + } +} diff --git a/tests/regression/dumptest/nullspace-difference-expected.csg b/tests/regression/dumptest/nullspace-difference-expected.csg new file mode 100644 index 00000000..a8d054c6 --- /dev/null +++ b/tests/regression/dumptest/nullspace-difference-expected.csg @@ -0,0 +1,6 @@ +group() { + difference() { + cube(size = [1, 1, 1], center = true); + cube(size = [2, 2, 2], center = true); + } +} diff --git a/tests/regression/dumptest/nullspace-intersection-expected.csg b/tests/regression/dumptest/nullspace-intersection-expected.csg new file mode 100644 index 00000000..f4e4f89e --- /dev/null +++ b/tests/regression/dumptest/nullspace-intersection-expected.csg @@ -0,0 +1,10 @@ +group() { + intersection() { + multmatrix([[1, 0, 0, -2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + } +} diff --git a/tests/regression/dumptest/nullspace-minkowski-expected.csg b/tests/regression/dumptest/nullspace-minkowski-expected.csg new file mode 100644 index 00000000..e387596e --- /dev/null +++ b/tests/regression/dumptest/nullspace-minkowski-expected.csg @@ -0,0 +1,13 @@ +group() { + minkowski(convexity = 0) { + intersection() { + multmatrix([[1, 0, 0, -2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + } + cube(size = [1, 1, 1], center = false); + } +} diff --git a/tests/regression/dumptest/nullspace-minkowski-intersection-expected.csg b/tests/regression/dumptest/nullspace-minkowski-intersection-expected.csg new file mode 100644 index 00000000..0620307c --- /dev/null +++ b/tests/regression/dumptest/nullspace-minkowski-intersection-expected.csg @@ -0,0 +1,16 @@ +group() { + intersection() { + minkowski(convexity = 0) { + intersection() { + multmatrix([[1, 0, 0, -2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = false); + } + } + cube(size = [1, 1, 1], center = false); + } + cube(size = [1, 1, 1], center = false); + } +} diff --git a/tests/regression/dumptest/offset-tests-expected.csg b/tests/regression/dumptest/offset-tests-expected.csg index ff88f5cc..39617983 100644 --- a/tests/regression/dumptest/offset-tests-expected.csg +++ b/tests/regression/dumptest/offset-tests-expected.csg @@ -1,140 +1,311 @@ group() { - offset(delta = -1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, -50], [0, 1, 0, 100], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } group() { - multmatrix([[1, 0, 0, 0], [0, 1, 0, 100], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - offset(delta = 1, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 50], [0, 1, 0, 100], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } - offset(delta = -1, join_type = "miter", miter_limit = 10, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 100], [0, 1, 0, 100], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } - offset(delta = 1, join_type = "miter", miter_limit = 10, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 150], [0, 1, 0, 100], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } - offset(delta = -1, join_type = "bevel", $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 100], [0, 1, 0, -50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } - offset(delta = 1, join_type = "bevel", $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 150], [0, 1, 0, -50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); - } - } - } - offset(delta = -5, join_type = "round", $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, -50], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); + multmatrix([[1, 0, 0, -60], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } } } } } group() { - multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); - } - } - } - offset(delta = 5, join_type = "round", $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 50], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); - } - } - } - } - offset(delta = -4, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, -50], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); + multmatrix([[1, 0, 0, -60], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); } } } } group() { multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); - } - } - } - offset(delta = 4, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 50], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); - } - } - } - } - offset(delta = -5, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 100], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); + group() { + offset(r = 1, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } } } } } group() { - multmatrix([[1, 0, 0, 0], [0, 1, 0, -50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); - } - } - } - offset(delta = 5, join_type = "miter", miter_limit = 2, $fn = 0, $fa = 12, $fs = 2) { - group() { - multmatrix([[1, 0, 0, 50], [0, 1, 0, -50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - square(size = [30, 30], center = true); - square(size = [8, 8], center = true); + multmatrix([[1, 0, 0, 0], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = 5, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } } } } } - multmatrix([[1, 0, 0, -50], [0, 1, 0, -50], [0, 0, 1, 0], [0, 0, 0, 1]]) { - multmatrix([[25, 0, 0, 0], [0, 25, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - offset(delta = 0.9, join_type = "round", $fn = 0, $fa = 12, $fs = 2) { - square(size = [0.1, 0.1], center = false); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = 5, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } } } } + group() { + multmatrix([[1, 0, 0, 60], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = 1, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [30, 30], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 60], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = 5, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [30, 30], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 60], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = 5, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 120], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = -5, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [40, 40], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 120], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = -10.01, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [50, 50], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 120], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(r = -1, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 180], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 4, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 180], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 1, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 180], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 5, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 240], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -2, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [30, 30], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 240], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -5, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [40, 40], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 240], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -1, chamfer = false, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 300], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 4, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 300], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 1, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [20, 20], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 300], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = 5, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 360], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -2, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [30, 30], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 360], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -5, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + difference() { + square(size = [40, 40], center = true); + square(size = [10, 10], center = true); + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 360], [0, 1, 0, 120], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + offset(delta = -1, chamfer = true, $fn = 0, $fa = 12, $fs = 2) { + group() { + polygon(points = [[-15, 80], [15, 80], [0, -15], [-8, 60], [8, 60], [0, 5]], paths = [[0, 1, 2], [3, 4, 5]], convexity = 1); + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, -120], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + multmatrix([[30, 0, 0, 0], [0, 30, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + offset(r = 0.8, $fn = 0, $fa = 12, $fs = 2) { + square(size = [1, 1], center = false); + } + } + } + } + } + offset(r = 1, $fn = 0, $fa = 12, $fs = 2); + offset(r = 1, $fn = 0, $fa = 12, $fs = 2) { + square(size = [0, 0], center = false); + } } diff --git a/tests/regression/dumptest/polygon-tests-expected.csg b/tests/regression/dumptest/polygon-tests-expected.csg index 74b40691..e844927b 100644 --- a/tests/regression/dumptest/polygon-tests-expected.csg +++ b/tests/regression/dumptest/polygon-tests-expected.csg @@ -39,4 +39,7 @@ group() { multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { polygon(points = [[0, 1], [0, 0], [1, 0], [1, 1], [0.8, 0.8], [0.8, 0.2], [0.2, 0.2], [0.2, 0.8]], paths = [[7, 6, 5, 4, 3, 2, 1, 0], [7, 0, 3, 4]], convexity = 1); } + polyhedron(points = undef, faces = undef, convexity = 1); + polyhedron(points = [[0, 0, 0], [1, 1, 1]], faces = undef, convexity = 1); + polyhedron(points = [0], faces = undef, convexity = 1); } diff --git a/tests/regression/dumptest/polyhedron-tests-expected.csg b/tests/regression/dumptest/polyhedron-tests-expected.csg index 0dc507cc..82313982 100644 --- a/tests/regression/dumptest/polyhedron-tests-expected.csg +++ b/tests/regression/dumptest/polyhedron-tests-expected.csg @@ -32,4 +32,6 @@ group() { } polyhedron(points = undef, faces = [[1, 2, 3]], convexity = 1); polyhedron(points = [[0, 0, 0], [1, 1, 1]], faces = undef, convexity = 1); + polyhedron(points = undef, faces = undef, convexity = 1); + polyhedron(points = [0], faces = [[0]], convexity = 1); } diff --git a/tests/regression/dumptest/projection-cut-tests-expected.csg b/tests/regression/dumptest/projection-cut-tests-expected.csg index 5ee575af..fde98f48 100644 --- a/tests/regression/dumptest/projection-cut-tests-expected.csg +++ b/tests/regression/dumptest/projection-cut-tests-expected.csg @@ -14,6 +14,11 @@ group() { } } } + projection(cut = true, convexity = 0) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 5], [0, 0, 0, 1]]) { + cube(size = [5, 5, 5], center = true); + } + } multmatrix([[1, 0, 0, 0], [0, 1, 0, -10], [0, 0, 1, 0], [0, 0, 0, 1]]) { projection(cut = true, convexity = 0) { union() { diff --git a/tests/regression/dumptest/scale-mirror2D-3D-tests-expected.csg b/tests/regression/dumptest/scale-mirror2D-3D-tests-expected.csg new file mode 100644 index 00000000..2293a339 --- /dev/null +++ b/tests/regression/dumptest/scale-mirror2D-3D-tests-expected.csg @@ -0,0 +1,36 @@ +group() { + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + polygon(points = [[-0.5, -0.5], [1, -0.5], [1, 1], [-0.5, 0.5]], paths = undef, convexity = 1); + } + } + } + multmatrix([[1, 0, 0, 3], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[-1, 0, 0, 0], [0, -0.5, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + polygon(points = [[-0.5, -0.5], [1, -0.5], [1, 1], [-0.5, 0.5]], paths = undef, convexity = 1); + } + } + } + } + multmatrix([[1, 0, 0, 0], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + polygon(points = [[-0.5, -0.5], [1, -0.5], [1, 1], [-0.5, 0.5]], paths = undef, convexity = 1); + } + } + } + } + multmatrix([[1, 0, 0, 2], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 1, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + polygon(points = [[-0.5, -0.5], [1, -0.5], [1, 1], [-0.5, 0.5]], paths = undef, convexity = 1); + } + } + } + } +} diff --git a/tests/regression/dumptest/scale2D-tests-expected.csg b/tests/regression/dumptest/scale2D-tests-expected.csg index b20a9757..5d0b7866 100644 --- a/tests/regression/dumptest/scale2D-tests-expected.csg +++ b/tests/regression/dumptest/scale2D-tests-expected.csg @@ -18,17 +18,21 @@ group() { } } } - linear_extrude(height = 100, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { - multmatrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - group() { - square(size = [2, 3], center = true); + multmatrix([[1, 0, 0, -5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 100, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + square(size = [2, 3], center = true); + } } } } - linear_extrude(height = 100, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { - multmatrix([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { - group() { - square(size = [2, 3], center = true); + multmatrix([[1, 0, 0, -5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 100, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + multmatrix([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + square(size = [2, 3], center = true); + } } } } diff --git a/tests/regression/dumptest/surface-tests-expected.csg b/tests/regression/dumptest/surface-tests-expected.csg index a1ec6358..d731e9a9 100644 --- a/tests/regression/dumptest/surface-tests-expected.csg +++ b/tests/regression/dumptest/surface-tests-expected.csg @@ -1,4 +1,6 @@ group() { surface(file = "", center = false, invert = false); + surface(file = "notfound.dat", center = false, invert = false); + surface(file = "notfound.png", center = false, invert = false); surface(file = "surface.dat", center = true, invert = false); } diff --git a/tests/regression/dumptest/tessellation-text-test-expected.csg b/tests/regression/dumptest/tessellation-text-test-expected.csg new file mode 100644 index 00000000..bee0d15b --- /dev/null +++ b/tests/regression/dumptest/tessellation-text-test-expected.csg @@ -0,0 +1,22 @@ +group() { + linear_extrude(height = 10, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + group() { + text(text = "C", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 8, $fa = 12, $fs = 2); + } + multmatrix([[1, 0, 0, 50], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + text(text = "C", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 16, $fa = 12, $fs = 2); + } + } + multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + text(text = "C", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 24, $fa = 12, $fs = 2); + } + } + multmatrix([[1, 0, 0, 50], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + text(text = "C", size = 50, spacing = 1, font = "Liberation Sans", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 32, $fa = 12, $fs = 2); + } + } + } +} diff --git a/tests/regression/dumptest/text-empty-tests-expected.csg b/tests/regression/dumptest/text-empty-tests-expected.csg new file mode 100644 index 00000000..96a15653 --- /dev/null +++ b/tests/regression/dumptest/text-empty-tests-expected.csg @@ -0,0 +1,4 @@ +group() { + text(text = "", size = 10, spacing = 1, font = "", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 0, $fa = 12, $fs = 2); + text(text = "", size = 10, spacing = 1, font = "", direction = "ltr", language = "en", script = "latin", halign = "left", valign = "baseline", $fn = 0, $fa = 12, $fs = 2); +} diff --git a/tests/regression/echotest/dim-all-expected.echo b/tests/regression/echotest/dim-all-expected.echo index a16c5801..adba64d1 100644 --- a/tests/regression/echotest/dim-all-expected.echo +++ b/tests/regression/echotest/dim-all-expected.echo @@ -8,9 +8,6 @@ WARNING: Unsupported DXF Entity 'LEADER' (1) in "dim-all.dxf". ECHO: ordinateX = -49.17542445724 WARNING: Unsupported DXF Entity 'LEADER' (1) in "dim-all.dxf". ECHO: ordinateY = 30.86974532565 -WARNING: Unsupported DXF Entity 'LEADER' (1) in "dim-all.dxf". ECHO: radius = 60 -WARNING: Unsupported DXF Entity 'LEADER' (1) in "dim-all.dxf". ECHO: diameter = 120 -WARNING: Unsupported DXF Entity 'LEADER' (1) in "dim-all.dxf". ECHO: arc = 59.03624346792 diff --git a/tests/regression/echotest/expression-evaluation-tests-expected.echo b/tests/regression/echotest/expression-evaluation-tests-expected.echo new file mode 100644 index 00000000..49f91f0d --- /dev/null +++ b/tests/regression/echotest/expression-evaluation-tests-expected.echo @@ -0,0 +1,361 @@ +ECHO: v = undef, op = "not v", result = true +ECHO: v = undef, op = "-v", result = undef +ECHO: v = undef, op = "v *", result = undef +ECHO: v = undef, op = "* v", result = undef +ECHO: v = undef, op = "v /", result = undef +ECHO: v = undef, op = "/ v", result = undef +ECHO: v = undef, op = "v %", result = undef +ECHO: v = undef, op = "% v", result = undef +ECHO: v = undef, op = "v +", result = undef +ECHO: v = undef, op = "+ v", result = undef +ECHO: v = undef, op = "v -", result = undef +ECHO: v = undef, op = "- v", result = undef +ECHO: v = undef, op = "v and true", result = false +ECHO: v = undef, op = "v and false", result = false +ECHO: v = undef, op = "v or true", result = true +ECHO: v = undef, op = "v or false", result = false +ECHO: v = undef, op = "[v]", result = undef +ECHO: v = undef, op = "v[0]", result = undef +ECHO: v = undef, op = "v[4]", result = undef +ECHO: v = inf, op = "not v", result = false +ECHO: v = inf, op = "-v", result = -inf +ECHO: v = inf, op = "v *", result = inf +ECHO: v = inf, op = "* v", result = inf +ECHO: v = inf, op = "v /", result = inf +ECHO: v = inf, op = "/ v", result = 0 +ECHO: v = inf, op = "v %", result = nan +ECHO: v = inf, op = "% v", result = 2 +ECHO: v = inf, op = "v +", result = inf +ECHO: v = inf, op = "+ v", result = inf +ECHO: v = inf, op = "v -", result = inf +ECHO: v = inf, op = "- v", result = -inf +ECHO: v = inf, op = "v and true", result = true +ECHO: v = inf, op = "v and false", result = false +ECHO: v = inf, op = "v or true", result = true +ECHO: v = inf, op = "v or false", result = true +ECHO: v = inf, op = "[v]", result = undef +ECHO: v = inf, op = "v[0]", result = undef +ECHO: v = inf, op = "v[4]", result = undef +ECHO: v = -inf, op = "not v", result = false +ECHO: v = -inf, op = "-v", result = inf +ECHO: v = -inf, op = "v *", result = -inf +ECHO: v = -inf, op = "* v", result = -inf +ECHO: v = -inf, op = "v /", result = -inf +ECHO: v = -inf, op = "/ v", result = 0 +ECHO: v = -inf, op = "v %", result = nan +ECHO: v = -inf, op = "% v", result = 2 +ECHO: v = -inf, op = "v +", result = -inf +ECHO: v = -inf, op = "+ v", result = -inf +ECHO: v = -inf, op = "v -", result = -inf +ECHO: v = -inf, op = "- v", result = inf +ECHO: v = -inf, op = "v and true", result = true +ECHO: v = -inf, op = "v and false", result = false +ECHO: v = -inf, op = "v or true", result = true +ECHO: v = -inf, op = "v or false", result = true +ECHO: v = -inf, op = "[v]", result = undef +ECHO: v = -inf, op = "v[0]", result = undef +ECHO: v = -inf, op = "v[4]", result = undef +ECHO: v = nan, op = "not v", result = false +ECHO: v = nan, op = "-v", result = nan +ECHO: v = nan, op = "v *", result = nan +ECHO: v = nan, op = "* v", result = nan +ECHO: v = nan, op = "v /", result = nan +ECHO: v = nan, op = "/ v", result = nan +ECHO: v = nan, op = "v %", result = nan +ECHO: v = nan, op = "% v", result = nan +ECHO: v = nan, op = "v +", result = nan +ECHO: v = nan, op = "+ v", result = nan +ECHO: v = nan, op = "v -", result = nan +ECHO: v = nan, op = "- v", result = nan +ECHO: v = nan, op = "v and true", result = true +ECHO: v = nan, op = "v and false", result = false +ECHO: v = nan, op = "v or true", result = true +ECHO: v = nan, op = "v or false", result = true +ECHO: v = nan, op = "[v]", result = undef +ECHO: v = nan, op = "v[0]", result = undef +ECHO: v = nan, op = "v[4]", result = undef +ECHO: v = 0, op = "not v", result = true +ECHO: v = 0, op = "-v", result = 0 +ECHO: v = 0, op = "v *", result = 0 +ECHO: v = 0, op = "* v", result = 0 +ECHO: v = 0, op = "v /", result = 0 +ECHO: v = 0, op = "/ v", result = inf +ECHO: v = 0, op = "v %", result = 0 +ECHO: v = 0, op = "% v", result = nan +ECHO: v = 0, op = "v +", result = 3 +ECHO: v = 0, op = "+ v", result = 2 +ECHO: v = 0, op = "v -", result = -3 +ECHO: v = 0, op = "- v", result = 2 +ECHO: v = 0, op = "v and true", result = false +ECHO: v = 0, op = "v and false", result = false +ECHO: v = 0, op = "v or true", result = true +ECHO: v = 0, op = "v or false", result = false +ECHO: v = 0, op = "[v]", result = "a" +ECHO: v = 0, op = "v[0]", result = undef +ECHO: v = 0, op = "v[4]", result = undef +ECHO: v = -4.2, op = "not v", result = false +ECHO: v = -4.2, op = "-v", result = 4.2 +ECHO: v = -4.2, op = "v *", result = -12.6 +ECHO: v = -4.2, op = "* v", result = -8.4 +ECHO: v = -4.2, op = "v /", result = -1.4 +ECHO: v = -4.2, op = "/ v", result = -0.47619047619 +ECHO: v = -4.2, op = "v %", result = -1.2 +ECHO: v = -4.2, op = "% v", result = 2 +ECHO: v = -4.2, op = "v +", result = -1.2 +ECHO: v = -4.2, op = "+ v", result = -2.2 +ECHO: v = -4.2, op = "v -", result = -7.2 +ECHO: v = -4.2, op = "- v", result = 6.2 +ECHO: v = -4.2, op = "v and true", result = true +ECHO: v = -4.2, op = "v and false", result = false +ECHO: v = -4.2, op = "v or true", result = true +ECHO: v = -4.2, op = "v or false", result = true +ECHO: v = -4.2, op = "[v]", result = undef +ECHO: v = -4.2, op = "v[0]", result = undef +ECHO: v = -4.2, op = "v[4]", result = undef +ECHO: v = -2, op = "not v", result = false +ECHO: v = -2, op = "-v", result = 2 +ECHO: v = -2, op = "v *", result = -6 +ECHO: v = -2, op = "* v", result = -4 +ECHO: v = -2, op = "v /", result = -0.66666666666 +ECHO: v = -2, op = "/ v", result = -1 +ECHO: v = -2, op = "v %", result = -2 +ECHO: v = -2, op = "% v", result = 0 +ECHO: v = -2, op = "v +", result = 1 +ECHO: v = -2, op = "+ v", result = 0 +ECHO: v = -2, op = "v -", result = -5 +ECHO: v = -2, op = "- v", result = 4 +ECHO: v = -2, op = "v and true", result = true +ECHO: v = -2, op = "v and false", result = false +ECHO: v = -2, op = "v or true", result = true +ECHO: v = -2, op = "v or false", result = true +ECHO: v = -2, op = "[v]", result = undef +ECHO: v = -2, op = "v[0]", result = undef +ECHO: v = -2, op = "v[4]", result = undef +ECHO: v = 3, op = "not v", result = false +ECHO: v = 3, op = "-v", result = -3 +ECHO: v = 3, op = "v *", result = 9 +ECHO: v = 3, op = "* v", result = 6 +ECHO: v = 3, op = "v /", result = 1 +ECHO: v = 3, op = "/ v", result = 0.66666666666 +ECHO: v = 3, op = "v %", result = 0 +ECHO: v = 3, op = "% v", result = 2 +ECHO: v = 3, op = "v +", result = 6 +ECHO: v = 3, op = "+ v", result = 5 +ECHO: v = 3, op = "v -", result = 0 +ECHO: v = 3, op = "- v", result = -1 +ECHO: v = 3, op = "v and true", result = true +ECHO: v = 3, op = "v and false", result = false +ECHO: v = 3, op = "v or true", result = true +ECHO: v = 3, op = "v or false", result = true +ECHO: v = 3, op = "[v]", result = "d" +ECHO: v = 3, op = "v[0]", result = undef +ECHO: v = 3, op = "v[4]", result = undef +ECHO: v = 42.42, op = "not v", result = false +ECHO: v = 42.42, op = "-v", result = -42.42 +ECHO: v = 42.42, op = "v *", result = 127.26 +ECHO: v = 42.42, op = "* v", result = 84.84 +ECHO: v = 42.42, op = "v /", result = 14.14 +ECHO: v = 42.42, op = "/ v", result = 0.0471475719 +ECHO: v = 42.42, op = "v %", result = 0.42 +ECHO: v = 42.42, op = "% v", result = 2 +ECHO: v = 42.42, op = "v +", result = 45.42 +ECHO: v = 42.42, op = "+ v", result = 44.42 +ECHO: v = 42.42, op = "v -", result = 39.42 +ECHO: v = 42.42, op = "- v", result = -40.42 +ECHO: v = 42.42, op = "v and true", result = true +ECHO: v = 42.42, op = "v and false", result = false +ECHO: v = 42.42, op = "v or true", result = true +ECHO: v = 42.42, op = "v or false", result = true +ECHO: v = 42.42, op = "[v]", result = undef +ECHO: v = 42.42, op = "v[0]", result = undef +ECHO: v = 42.42, op = "v[4]", result = undef +ECHO: v = 242, op = "not v", result = false +ECHO: v = 242, op = "-v", result = -242 +ECHO: v = 242, op = "v *", result = 726 +ECHO: v = 242, op = "* v", result = 484 +ECHO: v = 242, op = "v /", result = 80.66666666666 +ECHO: v = 242, op = "/ v", result = 0.00826446281 +ECHO: v = 242, op = "v %", result = 2 +ECHO: v = 242, op = "% v", result = 2 +ECHO: v = 242, op = "v +", result = 245 +ECHO: v = 242, op = "+ v", result = 244 +ECHO: v = 242, op = "v -", result = 239 +ECHO: v = 242, op = "- v", result = -240 +ECHO: v = 242, op = "v and true", result = true +ECHO: v = 242, op = "v and false", result = false +ECHO: v = 242, op = "v or true", result = true +ECHO: v = 242, op = "v or false", result = true +ECHO: v = 242, op = "[v]", result = undef +ECHO: v = 242, op = "v[0]", result = undef +ECHO: v = 242, op = "v[4]", result = undef +ECHO: v = true, op = "not v", result = false +ECHO: v = true, op = "-v", result = undef +ECHO: v = true, op = "v *", result = undef +ECHO: v = true, op = "* v", result = undef +ECHO: v = true, op = "v /", result = undef +ECHO: v = true, op = "/ v", result = undef +ECHO: v = true, op = "v %", result = undef +ECHO: v = true, op = "% v", result = undef +ECHO: v = true, op = "v +", result = undef +ECHO: v = true, op = "+ v", result = undef +ECHO: v = true, op = "v -", result = undef +ECHO: v = true, op = "- v", result = undef +ECHO: v = true, op = "v and true", result = true +ECHO: v = true, op = "v and false", result = false +ECHO: v = true, op = "v or true", result = true +ECHO: v = true, op = "v or false", result = true +ECHO: v = true, op = "[v]", result = undef +ECHO: v = true, op = "v[0]", result = undef +ECHO: v = true, op = "v[4]", result = undef +ECHO: v = false, op = "not v", result = true +ECHO: v = false, op = "-v", result = undef +ECHO: v = false, op = "v *", result = undef +ECHO: v = false, op = "* v", result = undef +ECHO: v = false, op = "v /", result = undef +ECHO: v = false, op = "/ v", result = undef +ECHO: v = false, op = "v %", result = undef +ECHO: v = false, op = "% v", result = undef +ECHO: v = false, op = "v +", result = undef +ECHO: v = false, op = "+ v", result = undef +ECHO: v = false, op = "v -", result = undef +ECHO: v = false, op = "- v", result = undef +ECHO: v = false, op = "v and true", result = false +ECHO: v = false, op = "v and false", result = false +ECHO: v = false, op = "v or true", result = true +ECHO: v = false, op = "v or false", result = false +ECHO: v = false, op = "[v]", result = undef +ECHO: v = false, op = "v[0]", result = undef +ECHO: v = false, op = "v[4]", result = undef +ECHO: v = "", op = "not v", result = true +ECHO: v = "", op = "-v", result = undef +ECHO: v = "", op = "v *", result = undef +ECHO: v = "", op = "* v", result = undef +ECHO: v = "", op = "v /", result = undef +ECHO: v = "", op = "/ v", result = undef +ECHO: v = "", op = "v %", result = undef +ECHO: v = "", op = "% v", result = undef +ECHO: v = "", op = "v +", result = undef +ECHO: v = "", op = "+ v", result = undef +ECHO: v = "", op = "v -", result = undef +ECHO: v = "", op = "- v", result = undef +ECHO: v = "", op = "v and true", result = false +ECHO: v = "", op = "v and false", result = false +ECHO: v = "", op = "v or true", result = true +ECHO: v = "", op = "v or false", result = false +ECHO: v = "", op = "[v]", result = undef +ECHO: v = "", op = "v[0]", result = undef +ECHO: v = "", op = "v[4]", result = undef +ECHO: v = "text", op = "not v", result = false +ECHO: v = "text", op = "-v", result = undef +ECHO: v = "text", op = "v *", result = undef +ECHO: v = "text", op = "* v", result = undef +ECHO: v = "text", op = "v /", result = undef +ECHO: v = "text", op = "/ v", result = undef +ECHO: v = "text", op = "v %", result = undef +ECHO: v = "text", op = "% v", result = undef +ECHO: v = "text", op = "v +", result = undef +ECHO: v = "text", op = "+ v", result = undef +ECHO: v = "text", op = "v -", result = undef +ECHO: v = "text", op = "- v", result = undef +ECHO: v = "text", op = "v and true", result = true +ECHO: v = "text", op = "v and false", result = false +ECHO: v = "text", op = "v or true", result = true +ECHO: v = "text", op = "v or false", result = true +ECHO: v = "text", op = "[v]", result = undef +ECHO: v = "text", op = "v[0]", result = "t" +ECHO: v = "text", op = "v[4]", result = undef +ECHO: v = [], op = "not v", result = true +ECHO: v = [], op = "-v", result = [] +ECHO: v = [], op = "v *", result = [] +ECHO: v = [], op = "* v", result = [] +ECHO: v = [], op = "v /", result = [] +ECHO: v = [], op = "/ v", result = [] +ECHO: v = [], op = "v %", result = undef +ECHO: v = [], op = "% v", result = undef +ECHO: v = [], op = "v +", result = undef +ECHO: v = [], op = "+ v", result = undef +ECHO: v = [], op = "v -", result = undef +ECHO: v = [], op = "- v", result = undef +ECHO: v = [], op = "v and true", result = false +ECHO: v = [], op = "v and false", result = false +ECHO: v = [], op = "v or true", result = true +ECHO: v = [], op = "v or false", result = false +ECHO: v = [], op = "[v]", result = undef +ECHO: v = [], op = "v[0]", result = undef +ECHO: v = [], op = "v[4]", result = undef +ECHO: v = [0], op = "not v", result = false +ECHO: v = [0], op = "-v", result = [0] +ECHO: v = [0], op = "v *", result = [0] +ECHO: v = [0], op = "* v", result = [0] +ECHO: v = [0], op = "v /", result = [0] +ECHO: v = [0], op = "/ v", result = [inf] +ECHO: v = [0], op = "v %", result = undef +ECHO: v = [0], op = "% v", result = undef +ECHO: v = [0], op = "v +", result = undef +ECHO: v = [0], op = "+ v", result = undef +ECHO: v = [0], op = "v -", result = undef +ECHO: v = [0], op = "- v", result = undef +ECHO: v = [0], op = "v and true", result = true +ECHO: v = [0], op = "v and false", result = false +ECHO: v = [0], op = "v or true", result = true +ECHO: v = [0], op = "v or false", result = true +ECHO: v = [0], op = "[v]", result = undef +ECHO: v = [0], op = "v[0]", result = 0 +ECHO: v = [0], op = "v[4]", result = undef +ECHO: v = [1], op = "not v", result = false +ECHO: v = [1], op = "-v", result = [-1] +ECHO: v = [1], op = "v *", result = [3] +ECHO: v = [1], op = "* v", result = [2] +ECHO: v = [1], op = "v /", result = [0.33333333333] +ECHO: v = [1], op = "/ v", result = [2] +ECHO: v = [1], op = "v %", result = undef +ECHO: v = [1], op = "% v", result = undef +ECHO: v = [1], op = "v +", result = undef +ECHO: v = [1], op = "+ v", result = undef +ECHO: v = [1], op = "v -", result = undef +ECHO: v = [1], op = "- v", result = undef +ECHO: v = [1], op = "v and true", result = true +ECHO: v = [1], op = "v and false", result = false +ECHO: v = [1], op = "v or true", result = true +ECHO: v = [1], op = "v or false", result = true +ECHO: v = [1], op = "[v]", result = undef +ECHO: v = [1], op = "v[0]", result = 1 +ECHO: v = [1], op = "v[4]", result = undef +ECHO: v = [0 : 1 : 0], op = "not v", result = false +ECHO: v = [0 : 1 : 0], op = "-v", result = undef +ECHO: v = [0 : 1 : 0], op = "v *", result = undef +ECHO: v = [0 : 1 : 0], op = "* v", result = undef +ECHO: v = [0 : 1 : 0], op = "v /", result = undef +ECHO: v = [0 : 1 : 0], op = "/ v", result = undef +ECHO: v = [0 : 1 : 0], op = "v %", result = undef +ECHO: v = [0 : 1 : 0], op = "% v", result = undef +ECHO: v = [0 : 1 : 0], op = "v +", result = undef +ECHO: v = [0 : 1 : 0], op = "+ v", result = undef +ECHO: v = [0 : 1 : 0], op = "v -", result = undef +ECHO: v = [0 : 1 : 0], op = "- v", result = undef +ECHO: v = [0 : 1 : 0], op = "v and true", result = true +ECHO: v = [0 : 1 : 0], op = "v and false", result = false +ECHO: v = [0 : 1 : 0], op = "v or true", result = true +ECHO: v = [0 : 1 : 0], op = "v or false", result = true +ECHO: v = [0 : 1 : 0], op = "[v]", result = undef +ECHO: v = [0 : 1 : 0], op = "v[0]", result = 0 +ECHO: v = [0 : 1 : 0], op = "v[4]", result = undef +ECHO: v = [1 : 1 : 2], op = "not v", result = false +ECHO: v = [1 : 1 : 2], op = "-v", result = undef +ECHO: v = [1 : 1 : 2], op = "v *", result = undef +ECHO: v = [1 : 1 : 2], op = "* v", result = undef +ECHO: v = [1 : 1 : 2], op = "v /", result = undef +ECHO: v = [1 : 1 : 2], op = "/ v", result = undef +ECHO: v = [1 : 1 : 2], op = "v %", result = undef +ECHO: v = [1 : 1 : 2], op = "% v", result = undef +ECHO: v = [1 : 1 : 2], op = "v +", result = undef +ECHO: v = [1 : 1 : 2], op = "+ v", result = undef +ECHO: v = [1 : 1 : 2], op = "v -", result = undef +ECHO: v = [1 : 1 : 2], op = "- v", result = undef +ECHO: v = [1 : 1 : 2], op = "v and true", result = true +ECHO: v = [1 : 1 : 2], op = "v and false", result = false +ECHO: v = [1 : 1 : 2], op = "v or true", result = true +ECHO: v = [1 : 1 : 2], op = "v or false", result = true +ECHO: v = [1 : 1 : 2], op = "[v]", result = undef +ECHO: v = [1 : 1 : 2], op = "v[0]", result = 1 +ECHO: v = [1 : 1 : 2], op = "v[4]", result = undef diff --git a/tests/regression/echotest/for-tests-expected.echo b/tests/regression/echotest/for-tests-expected.echo index 39a8d584..d66cebf8 100644 --- a/tests/regression/echotest/for-tests-expected.echo +++ b/tests/regression/echotest/for-tests-expected.echo @@ -11,14 +11,3 @@ ECHO: "INF", 0 WARNING: Bad range parameter in for statement: too many elements (4294967295). ECHO: "-INF", 1 WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). -WARNING: Bad range parameter in for statement: too many elements (4294967295). diff --git a/tests/regression/echotest/norm-tests-expected.echo b/tests/regression/echotest/norm-tests-expected.echo index cfa2e572..5e27997c 100644 --- a/tests/regression/echotest/norm-tests-expected.echo +++ b/tests/regression/echotest/norm-tests-expected.echo @@ -11,15 +11,15 @@ ECHO: undef ECHO: undef ECHO: undef ECHO: undef - WARNING: Incorrect arguments to norm() +WARNING: Incorrect arguments to norm() ECHO: undef - WARNING: Incorrect arguments to norm() +WARNING: Incorrect arguments to norm() ECHO: undef - WARNING: Incorrect arguments to norm() +WARNING: Incorrect arguments to norm() ECHO: undef - WARNING: Incorrect arguments to norm() +WARNING: Incorrect arguments to norm() ECHO: undef - WARNING: Incorrect arguments to norm() +WARNING: Incorrect arguments to norm() ECHO: undef ECHO: undef ECHO: undef diff --git a/tests/regression/echotest/recursion-tests-expected.echo b/tests/regression/echotest/recursion-tests-expected.echo index e5c99b1e..747e6d47 100644 --- a/tests/regression/echotest/recursion-tests-expected.echo +++ b/tests/regression/echotest/recursion-tests-expected.echo @@ -1,2 +1,2 @@ ERROR: Recursion detected calling function 'crash' -ECHO: undef +ERROR: Recursion detected calling module 'crash' diff --git a/tests/regression/echotest/tail-recursion-tests-expected.echo b/tests/regression/echotest/tail-recursion-tests-expected.echo new file mode 100644 index 00000000..753e2514 --- /dev/null +++ b/tests/regression/echotest/tail-recursion-tests-expected.echo @@ -0,0 +1,6 @@ +ECHO: "without tail-recursion eliminiation: ", 5050 +ECHO: "with tail-recursion eliminiation: ", 5050 +ECHO: "with tail-recursion eliminiation: ", 2001000 +ECHO: [1980, 1980] +ECHO: 50000, "ACEGIKMOQSUWYACEGIKMOQSUWYACEGIKMOQSUWYA" +ECHO: 50000, "abcdefghijklmnopqrstuvwxyzabcdefghijklmn" diff --git a/tests/regression/monotonepngtest/issue1061-expected.png b/tests/regression/monotonepngtest/issue1061-expected.png new file mode 100644 index 00000000..78bd8525 Binary files /dev/null and b/tests/regression/monotonepngtest/issue1061-expected.png differ diff --git a/tests/regression/monotonepngtest/issue1069-expected.png b/tests/regression/monotonepngtest/issue1069-expected.png new file mode 100644 index 00000000..677d52ca Binary files /dev/null and b/tests/regression/monotonepngtest/issue1069-expected.png differ diff --git a/tests/regression/monotonepngtest/issue1089-expected.png b/tests/regression/monotonepngtest/issue1089-expected.png new file mode 100644 index 00000000..5fdebc24 Binary files /dev/null and b/tests/regression/monotonepngtest/issue1089-expected.png differ diff --git a/tests/regression/monotonepngtest/issue1105-expected.png b/tests/regression/monotonepngtest/issue1105-expected.png new file mode 100644 index 00000000..013d85bf Binary files /dev/null and b/tests/regression/monotonepngtest/issue1105-expected.png differ diff --git a/tests/regression/monotonepngtest/issue835-expected.png b/tests/regression/monotonepngtest/issue835-expected.png new file mode 100644 index 00000000..e776820d Binary files /dev/null and b/tests/regression/monotonepngtest/issue835-expected.png differ diff --git a/tests/regression/monotonepngtest/issue904-expected.png b/tests/regression/monotonepngtest/issue904-expected.png new file mode 100644 index 00000000..1a370d07 Binary files /dev/null and b/tests/regression/monotonepngtest/issue904-expected.png differ diff --git a/tests/regression/monotonepngtest/issue911-expected.png b/tests/regression/monotonepngtest/issue911-expected.png new file mode 100644 index 00000000..2eb92792 Binary files /dev/null and b/tests/regression/monotonepngtest/issue911-expected.png differ diff --git a/tests/regression/monotonepngtest/issue913-expected.png b/tests/regression/monotonepngtest/issue913-expected.png new file mode 100644 index 00000000..677d52ca Binary files /dev/null and b/tests/regression/monotonepngtest/issue913-expected.png differ diff --git a/tests/regression/monotonepngtest/issue945b-expected.png b/tests/regression/monotonepngtest/issue945b-expected.png new file mode 100644 index 00000000..88ee57b2 Binary files /dev/null and b/tests/regression/monotonepngtest/issue945b-expected.png differ diff --git a/tests/regression/monotonepngtest/issue936-expected.png b/tests/regression/monotonepngtest/issue945c-expected.png similarity index 100% rename from tests/regression/monotonepngtest/issue936-expected.png rename to tests/regression/monotonepngtest/issue945c-expected.png diff --git a/tests/regression/monotonepngtest/issue945d-expected.png b/tests/regression/monotonepngtest/issue945d-expected.png new file mode 100644 index 00000000..7e603120 Binary files /dev/null and b/tests/regression/monotonepngtest/issue945d-expected.png differ diff --git a/tests/regression/monotonepngtest/nonmanifold-polyhedron-expected.png b/tests/regression/monotonepngtest/nonmanifold-polyhedron-expected.png new file mode 100644 index 00000000..27adff5c Binary files /dev/null and b/tests/regression/monotonepngtest/nonmanifold-polyhedron-expected.png differ diff --git a/tests/regression/opencsgtest/difference-tests-expected.png b/tests/regression/opencsgtest/difference-tests-expected.png index 69a4ac2e..4ffb06fd 100644 Binary files a/tests/regression/opencsgtest/difference-tests-expected.png and b/tests/regression/opencsgtest/difference-tests-expected.png differ diff --git a/tests/regression/opencsgtest/highlight-modifier-2d-expected.png b/tests/regression/opencsgtest/highlight-modifier-2d-expected.png new file mode 100644 index 00000000..24342191 Binary files /dev/null and b/tests/regression/opencsgtest/highlight-modifier-2d-expected.png differ diff --git a/tests/regression/opencsgtest/issue1004-expected.png b/tests/regression/opencsgtest/issue1004-expected.png index 8b815e15..74bd3b0a 100644 Binary files a/tests/regression/opencsgtest/issue1004-expected.png and b/tests/regression/opencsgtest/issue1004-expected.png differ diff --git a/tests/regression/opencsgtest/issue1061-expected.png b/tests/regression/opencsgtest/issue1061-expected.png new file mode 100644 index 00000000..fc626374 Binary files /dev/null and b/tests/regression/opencsgtest/issue1061-expected.png differ diff --git a/tests/regression/opencsgtest/issue1069-expected.png b/tests/regression/opencsgtest/issue1069-expected.png new file mode 100644 index 00000000..989385ee Binary files /dev/null and b/tests/regression/opencsgtest/issue1069-expected.png differ diff --git a/tests/regression/opencsgtest/issue1089-expected.png b/tests/regression/opencsgtest/issue1089-expected.png new file mode 100644 index 00000000..d19aed9e Binary files /dev/null and b/tests/regression/opencsgtest/issue1089-expected.png differ diff --git a/tests/regression/opencsgtest/issue1105-expected.png b/tests/regression/opencsgtest/issue1105-expected.png new file mode 100644 index 00000000..69a4cab7 Binary files /dev/null and b/tests/regression/opencsgtest/issue1105-expected.png differ diff --git a/tests/regression/opencsgtest/issue945b-expected.png b/tests/regression/opencsgtest/issue945b-expected.png new file mode 100644 index 00000000..41d8a37e Binary files /dev/null and b/tests/regression/opencsgtest/issue945b-expected.png differ diff --git a/tests/regression/opencsgtest/issue936-expected.png b/tests/regression/opencsgtest/issue945c-expected.png similarity index 100% rename from tests/regression/opencsgtest/issue936-expected.png rename to tests/regression/opencsgtest/issue945c-expected.png diff --git a/tests/regression/opencsgtest/issue945d-expected.png b/tests/regression/opencsgtest/issue945d-expected.png new file mode 100644 index 00000000..63ecaad1 Binary files /dev/null and b/tests/regression/opencsgtest/issue945d-expected.png differ diff --git a/tests/regression/opencsgtest/nullspace-2d-expected.png b/tests/regression/opencsgtest/nullspace-2d-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/nullspace-2d-expected.png differ diff --git a/tests/regression/opencsgtest/nullspace-difference-expected.png b/tests/regression/opencsgtest/nullspace-difference-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/nullspace-difference-expected.png differ diff --git a/tests/regression/opencsgtest/nullspace-intersection-expected.png b/tests/regression/opencsgtest/nullspace-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/nullspace-intersection-expected.png differ diff --git a/tests/regression/opencsgtest/nullspace-minkowski-expected.png b/tests/regression/opencsgtest/nullspace-minkowski-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/nullspace-minkowski-expected.png differ diff --git a/tests/regression/opencsgtest/nullspace-minkowski-intersection-expected.png b/tests/regression/opencsgtest/nullspace-minkowski-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/nullspace-minkowski-intersection-expected.png differ diff --git a/tests/regression/opencsgtest/offset-tests-expected.png b/tests/regression/opencsgtest/offset-tests-expected.png index ea7eee89..d1f6f921 100644 Binary files a/tests/regression/opencsgtest/offset-tests-expected.png and b/tests/regression/opencsgtest/offset-tests-expected.png differ diff --git a/tests/regression/opencsgtest/scale-mirror2D-3D-tests-expected.png b/tests/regression/opencsgtest/scale-mirror2D-3D-tests-expected.png new file mode 100644 index 00000000..6521e4af Binary files /dev/null and b/tests/regression/opencsgtest/scale-mirror2D-3D-tests-expected.png differ diff --git a/tests/regression/opencsgtest/text-empty-tests-expected.png b/tests/regression/opencsgtest/text-empty-tests-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/opencsgtest/text-empty-tests-expected.png differ diff --git a/tests/regression/throwntogethertest/difference-tests-expected.png b/tests/regression/throwntogethertest/difference-tests-expected.png index 4702fbff..fb1753c5 100644 Binary files a/tests/regression/throwntogethertest/difference-tests-expected.png and b/tests/regression/throwntogethertest/difference-tests-expected.png differ diff --git a/tests/regression/throwntogethertest/highlight-modifier-2d-expected.png b/tests/regression/throwntogethertest/highlight-modifier-2d-expected.png new file mode 100644 index 00000000..404d9e18 Binary files /dev/null and b/tests/regression/throwntogethertest/highlight-modifier-2d-expected.png differ diff --git a/tests/regression/throwntogethertest/issue1004-expected.png b/tests/regression/throwntogethertest/issue1004-expected.png new file mode 100644 index 00000000..84b1fe3f Binary files /dev/null and b/tests/regression/throwntogethertest/issue1004-expected.png differ diff --git a/tests/regression/throwntogethertest/issue1005-expected.png b/tests/regression/throwntogethertest/issue1005-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/issue1005-expected.png differ diff --git a/tests/regression/throwntogethertest/issue1061-expected.png b/tests/regression/throwntogethertest/issue1061-expected.png new file mode 100644 index 00000000..fc626374 Binary files /dev/null and b/tests/regression/throwntogethertest/issue1061-expected.png differ diff --git a/tests/regression/throwntogethertest/issue1069-expected.png b/tests/regression/throwntogethertest/issue1069-expected.png new file mode 100644 index 00000000..989385ee Binary files /dev/null and b/tests/regression/throwntogethertest/issue1069-expected.png differ diff --git a/tests/regression/throwntogethertest/issue835-expected.png b/tests/regression/throwntogethertest/issue835-expected.png new file mode 100644 index 00000000..f35cb9b9 Binary files /dev/null and b/tests/regression/throwntogethertest/issue835-expected.png differ diff --git a/tests/regression/throwntogethertest/issue911-expected.png b/tests/regression/throwntogethertest/issue911-expected.png new file mode 100644 index 00000000..c6c7aed4 Binary files /dev/null and b/tests/regression/throwntogethertest/issue911-expected.png differ diff --git a/tests/regression/throwntogethertest/issue913-expected.png b/tests/regression/throwntogethertest/issue913-expected.png new file mode 100644 index 00000000..f2427b72 Binary files /dev/null and b/tests/regression/throwntogethertest/issue913-expected.png differ diff --git a/tests/regression/throwntogethertest/issue964-expected.png b/tests/regression/throwntogethertest/issue964-expected.png new file mode 100644 index 00000000..0960b0ff Binary files /dev/null and b/tests/regression/throwntogethertest/issue964-expected.png differ diff --git a/tests/regression/throwntogethertest/issue964b-expected.png b/tests/regression/throwntogethertest/issue964b-expected.png new file mode 100644 index 00000000..c611ecf2 Binary files /dev/null and b/tests/regression/throwntogethertest/issue964b-expected.png differ diff --git a/tests/regression/throwntogethertest/issue990-expected.png b/tests/regression/throwntogethertest/issue990-expected.png new file mode 100644 index 00000000..9de9a598 Binary files /dev/null and b/tests/regression/throwntogethertest/issue990-expected.png differ diff --git a/tests/regression/throwntogethertest/nullspace-2d-expected.png b/tests/regression/throwntogethertest/nullspace-2d-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/nullspace-2d-expected.png differ diff --git a/tests/regression/throwntogethertest/nullspace-difference-expected.png b/tests/regression/throwntogethertest/nullspace-difference-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/nullspace-difference-expected.png differ diff --git a/tests/regression/throwntogethertest/nullspace-intersection-expected.png b/tests/regression/throwntogethertest/nullspace-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/nullspace-intersection-expected.png differ diff --git a/tests/regression/throwntogethertest/nullspace-minkowski-expected.png b/tests/regression/throwntogethertest/nullspace-minkowski-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/nullspace-minkowski-expected.png differ diff --git a/tests/regression/throwntogethertest/nullspace-minkowski-intersection-expected.png b/tests/regression/throwntogethertest/nullspace-minkowski-intersection-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/nullspace-minkowski-intersection-expected.png differ diff --git a/tests/regression/throwntogethertest/offset-tests-expected.png b/tests/regression/throwntogethertest/offset-tests-expected.png index 3d6c93a2..2c25d292 100644 Binary files a/tests/regression/throwntogethertest/offset-tests-expected.png and b/tests/regression/throwntogethertest/offset-tests-expected.png differ diff --git a/tests/regression/throwntogethertest/scale-mirror2D-3D-tests-expected.png b/tests/regression/throwntogethertest/scale-mirror2D-3D-tests-expected.png new file mode 100644 index 00000000..471748a7 Binary files /dev/null and b/tests/regression/throwntogethertest/scale-mirror2D-3D-tests-expected.png differ diff --git a/tests/regression/throwntogethertest/text-empty-tests-expected.png b/tests/regression/throwntogethertest/text-empty-tests-expected.png new file mode 100644 index 00000000..08ee92b2 Binary files /dev/null and b/tests/regression/throwntogethertest/text-empty-tests-expected.png differ diff --git a/version.pri b/version.pri index e490d7dc..069d0bdd 100644 --- a/version.pri +++ b/version.pri @@ -1,7 +1,7 @@ # get VERSION from system date isEmpty(VERSION) { - win32-msvc*:!mingw-cross-env { + win32*:!mingw-cross-env { # # Windows XP date command only has one argument, /t # and it can print the date in various localized formats. diff --git a/winconsole.pri b/winconsole.pri deleted file mode 100644 index a3991ae7..00000000 --- a/winconsole.pri +++ /dev/null @@ -1,28 +0,0 @@ -# Windows console issues workaround stub. -# -# Usage: put at the end of .pro file, then run qmake CONFIG+=winconsole -# -# This attempts to solve the problem of piping OpenSCAD under windows -# command line (GUI mode programs in Windows dont allow this). We use -# the 'devenv' solution, which means building two binaries: -# openscad.exe, and openscad.com, the latter being a wrapper for the -# former. See src/winconsole.c for more details. -# -# Qmake doesn't like building two binaries in the same directory so we -# depend on release-common.sh to call qmake twice and package the file properly - -CONFIG(winconsole) { - TEMPLATE = app - TARGET = openscad_winconsole - FORMS = - HEADERS = - FLEXSOURCES = - BISONSOURCES = - RESOURCES = - SOURCES = src/winconsole.c - CONFIG += console # sets IMAGE_SUBSYSTEM_WINDOWS_CUI in binary - LIBS -= $$LIBS - RC_FILE -= $$RC_FILE - QMAKE_POST_LINK = cd $(DESTDIR) && mv openscad_winconsole.exe openscad.com -} - diff --git a/winconsole.pro b/winconsole.pro new file mode 100644 index 00000000..11078f36 --- /dev/null +++ b/winconsole.pro @@ -0,0 +1,23 @@ +# Windows console issues workaround stub. +# +# This attempts to solve the problem of piping OpenSCAD under windows +# command line (GUI mode programs in Windows dont allow this). We use +# the 'devenv' solution, which means building two binaries: +# openscad.exe, and openscad.com, the latter being a wrapper for the +# former. See src/winconsole.c for more details. +# +# Qmake doesn't like building two binaries in the same directory so we +# depend on release-common.sh to call qmake twice and package the file +# properly + +TEMPLATE = app +TARGET = openscad_winconsole +FORMS = +HEADERS = +FLEXSOURCES = +BISONSOURCES = +RESOURCES = +SOURCES = src/winconsole.c +CONFIG -= qt +CONFIG += console # sets IMAGE_SUBSYSTEM_WINDOWS_CUI in binary +QMAKE_POST_LINK = cd $(DESTDIR) && mv openscad_winconsole.exe openscad.com