Merge remote-tracking branch 'origin/master' into c++11

Conflicts:
	openscad.pro
master
Marius Kintel 2015-01-06 01:10:04 -05:00
commit a41c8716ec
318 changed files with 15950 additions and 3682 deletions

7
.gitignore vendored
View File

@ -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
**/xcuserdata

View File

@ -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.

View File

@ -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

View File

@ -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
}
}

6
cgal/README.md Normal file
View File

@ -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.

198
cgal/data/cubes.stl Normal file
View File

@ -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

View File

@ -0,0 +1,4 @@
6, -25, 29.285714285714285
6, -26.732050855686023, 29.020513307787397
6, -26.732050855686026, 29.020513307787397
6, -26, 29.132600433972414

10
cgal/data/quads.polygon Normal file
View File

@ -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

3
cgal/data/tri.polygon Normal file
View File

@ -0,0 +1,3 @@
0,0,0
2,0,0
2,2,0

667
cgal/decompose.cpp Normal file
View File

@ -0,0 +1,667 @@
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <sstream>
#include <iostream>
#include <locale.h>
#include "cgalutils.h"
#include "export.h"
#include "polyset.h"
#include "CGAL_Nef_polyhedron.h"
using namespace CGALUtils;
// Nef polyhedron are using CGAL_Kernel3 (Cartesian<Gmpq>)
// Triangulation will use Epick
typedef CGAL::Epick K;
typedef CGAL::Polyhedron_3<K> PolyhedronK;
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope
std::vector<Color4f> 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 <boost/unordered_set.hpp>
#include <CGAL/convex_hull_3.h>
template<typename Polyhedron>
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<typename Polyhedron::Facet_const_handle, typename CGAL::Handle_hash_function> visited;
// c++11
// visited.reserve(p.size_of_facets());
std::queue<typename Polyhedron::Facet_const_handle> 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<K::Point_3> 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<class Output>
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<CGAL_Kernel3>(*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<PolyhedronK> convexP[2]; // Stores decomposed operands
std::list<PolyhedronK> result_parts;
for (int i = 0; i < 2; i++) {
shared_ptr<const CGAL_Nef_polyhedron> N;
if (const PolySet *ps = dynamic_cast<const PolySet *>(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<const CGAL_Nef_polyhedron *>(operands[i])) {
CGAL_Polyhedron poly;
if (n->p3->is_simple()) {
nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>(*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<K::Point_3> points[2];
std::vector<K::Point_3> 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<K::Point_3> 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<PolyhedronK>::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<const Geometry>(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<std::streamoff>(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<double>(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] << " <file.stl> <file.stl>" << std::endl;
exit(1);
}
Geometry::ChildList children;
CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(*ps);
std::vector<PolyhedronK> 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;
}

108
cgal/decompose.pro Normal file
View File

@ -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=<prefix>")
} 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=<prefix>")
OPENSCAD_LIBDIR = /opt/local
} else:exists(/usr/local/Cellar) {
message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=<prefix>")
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

View File

@ -0,0 +1,140 @@
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <sstream>
#include <iostream>
#include <locale.h>
#include "cgalutils.h"
// Nef polyhedron are using CGAL_Kernel3 (Cartesian<Gmpq>)
// 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<std::string> strs;
std::vector<double> 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<double>(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);
}

View File

@ -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=<prefix>")
} 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=<prefix>")
OPENSCAD_LIBDIR = /opt/local
} else:exists(/usr/local/Cellar) {
message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=<prefix>")
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

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

44
color-schemes/readme.txt Normal file
View File

@ -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

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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)

84
doc/translation.txt Normal file
View File

@ -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

View File

@ -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 {

28
gettext.pri Normal file
View File

@ -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
}

View File

@ -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
}
}

BIN
icons/SCAD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

2
locale/LINGUAS Normal file
View File

@ -0,0 +1,2 @@
# available languages
fr ru de cs

1116
locale/cs.po Normal file

File diff suppressed because it is too large Load Diff

2169
locale/de.po Normal file

File diff suppressed because it is too large Load Diff

1012
locale/fr.po Normal file

File diff suppressed because it is too large Load Diff

946
locale/openscad.pot Normal file
View File

@ -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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 ""
"<html><head/><body><p>This list shows the fonts currently registered with "
"OpenSCAD.</p><p>Example:</p><pre style=\" margin-top:12px; margin-"
"bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-"
"indent:0px;\"><span style=\" font-family:'Courier New,courier';\"> text(t = "
"&quot;OpenSCAD&quot;, font = &quot;DejaVu Sans&quot;);</span></pre><pre "
"style=\" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-"
"right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-"
"family:'Courier New,courier';\"> text(t = &quot;OpenSCAD&quot;, font = "
"&quot;Liberation Sans:style=Italic&quot;);</span></pre></body></html>"
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 ""
"<html><head/><body>\n"
"<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>\n"
"<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>\n"
"</body></html>\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 ""
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/"
"REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
"\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:'Lucida Grande'; font-size:13pt; "
"font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
"margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"></"
"p></body></html>"
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 ""

1000
locale/ru.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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)
}

View File

@ -1,201 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.0.1, 2014-08-11T22:59:13. -->
<qtcreator>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Qt 4.8.6 (qt4)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Qt 4.8.6 (qt4)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{90222843-28c9-4a66-ac82-99bd31ae7263}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory"></value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">false</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments">CONFIG+=experimental</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">openscad</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/shaina/openscad/openscad.pro</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">openscad.pro</value>
<value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
<value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal">false</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
<value type="QByteArray">{56f57d1a-fe9b-42b2-a96b-3ac76cf7565f}</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">15</value>
</data>
</qtcreator>

View File

@ -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
}
}
}
}

22
scripts/generate-potfiles.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.."

13
scripts/translation-make.sh Executable file
View File

@ -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

86
scripts/translation-update.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -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(); }
};

View File

@ -19,7 +19,7 @@
</p>
<p>
<a href="http://www.openscad.org">OpenSCAD</a> version __VERSION__
<b><a href="http://www.openscad.org">OpenSCAD</a> version __VERSION__</b>
</p>
<p>
@ -149,9 +149,9 @@ benhowes, 5263, Craig Trader, Miro Hrončok, Tony Theodore ... and many others
</p>
<lu>
<li><a href="http://www.github.com">Github</a>
<li><a href="http://rocklinux.net/pipermail/openscad/">Rock Linux</a>
<li><a href="http://www.thingiverse.com">Thingiverse</a>
<li><a href="http://www.github.com">Source code repos and website hosted on Github</a>
<li><a href="http://jesusabdullah.net">Downloads hosted by Joshua Holbrook</a>
<li><a href="https://travis-ci.org/">CI hosted on Travis-CI</a>
</lu>
<p>

View File

@ -6,16 +6,98 @@
<rect>
<x>0</x>
<y>0</y>
<width>567</width>
<height>359</height>
<width>628</width>
<height>419</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>About OpenSCAD</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>16</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="0" column="0" colspan="3">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="logoLabel">
<property name="minimumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../openscad.qrc">:/icons/openscad.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
&lt;p style=&quot;font-family: 'Open Sans', 'Droid Sans', 'sans-serif'; font-weight: bold; padding-bottom: 0; margin-bottom: 0; font-size: 22pt;&quot;&gt;&lt;span style=&quot;color: green;&quot;&gt;Open&lt;/span&gt;SCAD&lt;/p&gt;
&lt;p style=&quot;font-family: 'Open Sans', 'Droid Sans', 'sans-serif'; font-weight: normal; font-size: 14pt; padding-top: 0; margin-top: 0; margin-left: 2em;&quot;&gt;The Programmers Solid 3D CAD Modeller&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>2</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0" colspan="3">
<widget class="QTextBrowser" name="aboutText">
<property name="minimumSize">
<size>
<width>400</width>
<height>200</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -24,8 +106,31 @@
<string>qrc:/src/AboutDialog.html</string>
</url>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="okPushButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>

View File

@ -1,4 +1,6 @@
#include <Carbon/Carbon.h>
#include <AppleEvents.h>
#include <MacTypes.h>
#include <CoreServices/CoreServices.h>
#include <QApplication>
#include "MainWindow.h"

View File

@ -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

View File

@ -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(); }

View File

@ -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 {

View File

@ -95,7 +95,13 @@ static shared_ptr<CSGTerm> evaluate_csg_term_from_geometry(const State &state,
{
std::stringstream stream;
stream << node.name() << node.index();
shared_ptr<CSGTerm> t(new CSGTerm(geom, state.matrix(), state.color(), stream.str()));
// We cannot render Polygon2d directly, so we preprocess (tessellate) it here
shared_ptr<const Geometry> g = geom;
shared_ptr<const Polygon2d> p2d = dynamic_pointer_cast<const Polygon2d>(geom);
if (p2d) g.reset(p2d->tessellate());
shared_ptr<CSGTerm> t(new CSGTerm(g, state.matrix(), state.color(), stream.str()));
if (modinst->isHighlight()) {
t->flag = CSGTerm::FLAG_HIGHLIGHT;
highlights.push_back(t);

View File

@ -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();
}

View File

@ -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
};

View File

@ -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<CSGTerm> 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 );

View File

@ -30,6 +30,7 @@
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#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<const FcChar8 *>(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<const FcChar8 *> (path.c_str()))) {

View File

@ -59,6 +59,19 @@ private:
typedef std::vector<FontInfo> 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<FT_Face, time_t> cache_entry_t;
typedef std::map<std::string, cache_entry_t> 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;

View File

@ -27,6 +27,7 @@
#include <QClipboard>
#include <QSortFilterProxyModel>
#include "qtgettext.h"
#include "FontListDialog.h"
#include "FontCache.h"
@ -59,14 +60,16 @@ void FontListDialog::selection_changed(const QItemSelection &current, 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;
}

View File

@ -3,11 +3,9 @@
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#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;

View File

@ -35,9 +35,6 @@
</item>
<item row="4" column="3">
<widget class="QPushButton" name="copyButton">
<property name="toolTip">
<string>Paste font selector to Editor Window</string>
</property>
<property name="text">
<string>Copy to Clipboard</string>
</property>
@ -70,7 +67,17 @@
</widget>
</item>
<item row="2" column="0" colspan="5">
<widget class="QTableView" name="tableView"/>
<widget class="FontListTableView" name="tableView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label">
@ -103,6 +110,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FontListTableView</class>
<extends>QTableView</extends>
<header>FontListTableView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../openscad.qrc"/>
</resources>

67
src/FontListTableView.cc Normal file
View File

@ -0,0 +1,67 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QDrag>
#include <QPixmap>
#include <QPainter>
#include <QMimeData>
#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);
}

18
src/FontListTableView.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <QTableView>
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;
};

View File

@ -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);
}

View File

@ -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);
};

View File

@ -63,10 +63,12 @@ shared_ptr<const Geometry> 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<const Polygon2d *> children = collectChildren2D(node);
Polygon2d *geometry = NULL;
Polygon2d *geometry = new Polygon2d();
typedef CGAL::Point_2<CGAL::Cartesian<double> > 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<const Polygon2d*>(geometry);
// ClipperLib documentation: The formula for the number of steps in a full
// circular arc is ... Pi / acos(1 - arc_tolerance / abs(delta))
double n = Calc::get_fragments_from_r(10, node.fn, node.fs, node.fa);
double 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<const Geometry> geom;
if (!isSmartCached(node)) {
const Geometry *geometry = node.createGeometry();
assert(geometry);
if (const Polygon2d *polygon = dynamic_cast<const Polygon2d*>(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<const PolySet> ps = dynamic_pointer_cast<const PolySet>(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);
}
}
}
}

View File

@ -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()

View File

@ -2,6 +2,8 @@
#include <QDialog>
#include <QString>
#include "qtgettext.h"
#include "ui_LibraryInfoDialog.h"
class LibraryInfoDialog : public QDialog, public Ui::LibraryInfoDialog

View File

@ -1,5 +1,6 @@
#pragma once
#include "qtgettext.h"
#include <QMainWindow>
#include <QIcon>
#include "ui_MainWindow.h"
@ -13,6 +14,7 @@
#include <vector>
#include <QMutex>
#include <QSet>
#include <QTime>
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<QString, QString> 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<QAction *> &actions);
void updateStatusBar(class ProgressWidget *progressWidget);
EditorInterface *editor;

View File

@ -179,7 +179,7 @@
</property>
<widget class="QMenu" name="menuOpenRecent">
<property name="title">
<string>Open Recent</string>
<string>Recent Files</string>
</property>
</widget>
<widget class="QMenu" name="menuExamples">
@ -543,6 +543,9 @@
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>
</property>
</action>
<action name="editActionUndo">
<property name="text">
@ -955,6 +958,9 @@
<property name="text">
<string>About</string>
</property>
<property name="menuRole">
<enum>QAction::AboutRole</enum>
</property>
</action>
<action name="helpActionManual">
<property name="text">
@ -983,6 +989,9 @@
<property name="text">
<string>Preferences</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>
</property>
</action>
<action name="editActionFind">
<property name="text">

View File

@ -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<const class Geometry> 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"

View File

@ -44,6 +44,7 @@ const bool cull_backfaces = false;
const bool color_backfaces = false;
#ifdef _WIN32
#include <windows.h> // For the CALLBACK macro
#define CGAL_GLU_TESS_CALLBACK CALLBACK
#else
#define CGAL_GLU_TESS_CALLBACK

View File

@ -11,6 +11,7 @@ For more info:
http://blogs.msdn.com/b/oldnewthing/archive/2006/12/04/1205831.aspx by Tom
*/
#undef NOGDI
#include <windows.h>
#include <vector>

View File

@ -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);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "qtgettext.h"
#include "ui_OpenCSGWarningDialog.h"
class OpenCSGWarningDialog : public QDialog, public Ui::OpenCSGWarningDialog

View File

@ -1,4 +1,8 @@
#include "PlatformUtils.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#include <boost/lexical_cast.hpp>
#import <Foundation/Foundation.h>
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<std::string>(numcpu);
result += " CPU";
if (numcpu > 1) result += "s";
result += " ";
result += PlatformUtils::toMemorySizeString(physical_memory, 2);
result += " RAM";
return result;
}
void PlatformUtils::ensureStdIO(void) {}

View File

@ -1,3 +1,14 @@
#include <string>
#include <fstream>
#include <streambuf>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#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<char>(s)), std::istreambuf_iterator<char>());
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<std::string>(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) {}

View File

@ -3,6 +3,7 @@
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#undef NOGDI
#include <windows.h>
#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 <io.h>
#include <stdio.h>
#include <fstream>

View File

@ -1,10 +1,12 @@
#include <stdlib.h>
#include <iomanip>
#include "PlatformUtils.h"
#include "boosty.h"
#include <Eigen/Core>
#ifdef USE_SCINTILLA_EDITOR
#include <Qsci/qsciglobal.h>
#ifdef INSTALL_SUFFIX
#define RESOURCE_FOLDER(path) path INSTALL_SUFFIX
#else
#define RESOURCE_FOLDER(path) path
#endif
extern std::vector<std::string> 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();
}

View File

@ -2,6 +2,11 @@
#include <string>
#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);
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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<int>(value));
case Value::BOOL:
return Value(boost::lexical_cast<bool>(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

View File

@ -2,7 +2,10 @@
#include <QMainWindow>
#include <QSettings>
#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<const QAction *, QWidget *> prefPages;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
#pragma once
#include "qtgettext.h"
#include "ui_ProgressWidget.h"
#include <QTime>

View File

@ -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))

View File

@ -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;

View File

@ -1,8 +1,11 @@
#include "Tree.h"
#include "nodedumper.h"
#include "printutils.h"
#include <assert.h>
#include <algorithm>
#include <sstream>
#include <boost/regex.hpp>
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<std::string>(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];
}
/*!

View File

@ -31,6 +31,7 @@
#include <QFileDialog>
#include <QDesktopServices>
#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();
}

View File

@ -95,19 +95,19 @@ std::string Builtins::isDeprecated(const std::string &name)
Builtins::Builtins()
{
this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr<Expression>(new Expression(Value(0.0)))));
this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr<Expression>(new Expression(Value(2.0)))));
this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr<Expression>(new Expression(Value(12.0)))));
this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr<Expression>(new Expression(Value(0.0)))));
this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr<Expression>(new ExpressionConst(ValuePtr(0.0)))));
this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr<Expression>(new ExpressionConst(ValuePtr(2.0)))));
this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr<Expression>(new ExpressionConst(ValuePtr(12.0)))));
this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr<Expression>(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<Expression>(new Expression(zero3val))));
this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr<Expression>(new Expression(zero3val))));
this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr<Expression>(new Expression(500))));
ValuePtr zero3val(zero3);
this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr<Expression>(new ExpressionConst(zero3val))));
this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr<Expression>(new ExpressionConst(zero3val))));
this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr<Expression>(new ExpressionConst(ValuePtr(500)))));
}
Builtins::~Builtins()

View File

@ -27,7 +27,7 @@ using boost::uintmax_t;
#include <CGAL/Cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <CGAL_Nef3_workaround.h>
#include "CGAL_Nef3_workaround.h"
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
@ -57,11 +57,10 @@ typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3;
typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;
typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron;
typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS;
typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder;
typedef CGAL::Point_3<CGAL_Kernel3> CGAL_Point_3;
typedef CGAL::Iso_cuboid_3<CGAL_Kernel3> CGAL_Iso_cuboid_3;
typedef std::vector<CGAL_Point_3> 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

View File

@ -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 << ")";

View File

@ -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;

301
src/cgalutils-polyhedron.cc Normal file
View File

@ -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 <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <boost/foreach.hpp>
namespace /* anonymous */ {
template<typename Result, typename V>
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 <typename Polyhedron>
class CGAL_Build_PolySet : public CGAL::Modifier_base<typename Polyhedron::HalfedgeDS>
{
typedef typename Polyhedron::HalfedgeDS HDS;
typedef CGAL::Polyhedron_incremental_builder_3<typename Polyhedron::HalfedgeDS> 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<CGALPoint> vertices;
Grid3d<int> grid(GRID_FINE);
std::vector<size_t> 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<size_t>::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<vertices.size();i++) {
if (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<Vector3d> vertices;
std::vector<size_t> 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<vertices.size();vidx++) {
if (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 <class Polyhedron_input, class Polyhedron_output>
struct Copy_polyhedron_to : public CGAL::Modifier_base<typename Polyhedron_output::HalfedgeDS>
{
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<Output_HDS> 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<Vertex_const_iterator> 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 <class Polyhedron_A, class Polyhedron_B>
void copyPolyhedron(const Polyhedron_A &poly_a, Polyhedron_B &poly_b)
{
Copy_polyhedron_to<Polyhedron_A, Polyhedron_B> modifier(poly_a);
poly_b.delegate(modifier);
}
template void copyPolyhedron(const CGAL::Polyhedron_3<CGAL::Epick> &, CGAL_Polyhedron &);
template void copyPolyhedron(const CGAL_Polyhedron &, CGAL::Polyhedron_3<CGAL::Epick> &);
template <typename Polyhedron>
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<Polyhedron> 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<CGAL::Epick> &p);
template <typename Polyhedron>
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<CGAL::Epick> &p, PolySet &ps);
template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3<CGAL::Epeck> &p, PolySet &ps);
template bool createPolySetFromPolyhedron(const CGAL::Polyhedron_3<CGAL::Simple_cartesian<long> > &p, PolySet &ps);
}; // namespace CGALUtils
#endif /* ENABLE_CGAL */

553
src/cgalutils-tess-old.cc Normal file
View File

@ -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 <CGAL/Delaunay_mesher_no_edge_refinement_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
typedef CGAL_Kernel3 Kernel;
//typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_intersections_tag ITAG;
typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT;
//typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT;
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Point CDTPoint;
typedef CGAL::Ray_2<Kernel> CGAL_Ray_2;
typedef CGAL::Line_3<Kernel> CGAL_Line_3;
typedef CGAL::Point_2<Kernel> CGAL_Point_2;
typedef CGAL::Vector_2<Kernel> CGAL_Vector_2;
typedef CGAL::Segment_2<Kernel> CGAL_Segment_2;
typedef CGAL::Direction_2<Kernel> CGAL_Direction_2;
typedef CGAL::Direction_3<Kernel> CGAL_Direction_3;
typedef CGAL::Plane_3<Kernel> 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 <a:b:c:d>. 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<CGAL_Point_3>(&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 T> 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<CGAL_Point_2> &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<pgon.size();i++) {
CGAL_Point_2 tail = pgon[i];
CGAL_Point_2 head = pgon[(i+1)%pgon.size()];
CGAL_Segment_2 seg( tail, head );
CGAL::Object obj = intersection( eastray, seg );
const CGAL_Point_2 *point_test = CGAL::object_cast<CGAL_Point_2>(&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<CGAL_Polygon_3> &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<CDTPoint,CGAL_Point_3> 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<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
}
std::list<CDTPoint>::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<CDT>() );
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<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &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<CDTPoint,CGAL_Point_3> 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<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
}
std::list<CDTPoint>::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<CDT>() );
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;
}
};

View File

@ -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 <CGAL/Delaunay_mesher_no_edge_refinement_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
//#include "cgal.h"
//#include "tess.h"
typedef CGAL_Kernel3 Kernel;
//typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef CGAL::Triangulation_vertex_base_2<Kernel> Vb;
//typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Delaunay_mesh_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_intersections_tag ITAG;
typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS,ITAG> CDT;
//typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel,TDS> CDT;
#ifdef NDEBUG
#define PREV_NDEBUG NDEBUG
#undef NDEBUG
#endif
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_2_filtered_projection_traits_3.h>
#include <CGAL/Triangulation_face_base_with_info_2.h>
#ifdef PREV_NDEBUG
#define NDEBUG PREV_NDEBUG
#endif
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Point CDTPoint;
#include <boost/foreach.hpp>
typedef CGAL::Ray_2<Kernel> CGAL_Ray_2;
typedef CGAL::Line_3<Kernel> CGAL_Line_3;
typedef CGAL::Point_2<Kernel> CGAL_Point_2;
typedef CGAL::Vector_2<Kernel> CGAL_Vector_2;
typedef CGAL::Segment_2<Kernel> CGAL_Segment_2;
typedef CGAL::Direction_2<Kernel> CGAL_Direction_2;
typedef CGAL::Direction_3<Kernel> CGAL_Direction_3;
typedef CGAL::Plane_3<Kernel> 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 <a:b:c:d>. 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<K> Projection;
typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo, K> Fbb;
typedef CGAL::Triangulation_data_structure_2<
CGAL::Triangulation_vertex_base_2<Projection>,
CGAL::Constrained_triangulation_face_base_2<Projection, Fbb> > 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<CDT::Edge>& 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<CGAL_Point_3>(&obj);
if (point_test) {
p3 = *point_test;
return false;
}
PRINT("ERROR: deproject failure");
return true;
if (start->info().nesting_level != -1) return;
std::list<CDT::Face_handle> 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 T> 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<CGAL_Point_2> &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<pgon.size();i++) {
CGAL_Point_2 tail = pgon[i];
CGAL_Point_2 head = pgon[(i+1)%pgon.size()];
CGAL_Segment_2 seg( tail, head );
CGAL::Object obj = intersection( eastray, seg );
const CGAL_Point_2 *point_test = CGAL::object_cast<CGAL_Point_2>(&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<CDT::Edge> 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<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &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;i<poly.size(); i++) {
cdt.insert_constraint(poly[i], poly[(i+1)%poly.size()]);
}
}
//Mark facets that are inside the domain bounded by the polygon
mark_domains(cdt);
// Iterate over the resulting faces
for (CDT::Finite_faces_iterator 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++) {
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<CDTPoint,CGAL_Point_3> 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<CGAL_Point_2> > polygons2d;
for (size_t i=0;i<polygons.size();i++) {
std::vector<Vertex_handle> vhandles;
std::vector<CGAL_Point_2> polygon2d;
for (size_t j=0;j<polygons[i].size();j++) {
CGAL_Point_3 p3 = polygons[i][j];
CGAL_Point_2 p2 = get_projected_point( p3, goodproj );
CDTPoint cdtpoint = CDTPoint( p2.x(), p2.y() );
vertmap[ cdtpoint ] = p3;
Vertex_handle vh = cdt.push_back( cdtpoint );
vhandles.push_back( vh );
polygon2d.push_back( p2 );
}
polygons2d.push_back( polygon2d );
for (size_t k=0;k<vhandles.size();k++) {
int vindex1 = (k+0);
int vindex2 = (k+1)%vhandles.size();
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
cdt.insert_constraint( vhandles[vindex1], vhandles[vindex2] );
} catch (const CGAL::Failure_exception &e) {
PRINTB("WARNING: Constraint insertion failure %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
K::Vector_3 normalvec;
if (normal) {
normalvec = *normal;
}
size_t numholes = polygons2d.size()-1;
PRINTDB("seeding %i holes",numholes);
std::list<CDTPoint> list_of_seeds;
for (size_t i=1;i<polygons2d.size();i++) {
std::vector<CGAL_Point_2> &pgon = polygons2d[i];
for (size_t j=0;j<pgon.size();j++) {
CGAL_Point_2 p1 = pgon[(j+0)];
CGAL_Point_2 p2 = pgon[(j+1)%pgon.size()];
CGAL_Point_2 p3 = pgon[(j+2)%pgon.size()];
CGAL_Point_2 mp = CGAL::midpoint(p1,CGAL::midpoint(p2,p3));
if (inside(mp,pgon,NONZERO_WINDING)) {
CDTPoint cdtpt( mp.x(), mp.y() );
list_of_seeds.push_back( cdtpt );
break;
}
}
else {
// Calculate best guess at face normal using Newell's method
CGAL::normal_vector_newell_3(polygon.begin(), polygon.end(), normalvec);
}
std::list<CDTPoint>::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<polygon.size(); i++) {
cdt.insert_constraint(polygon[i], polygon[(i+1)%polygon.size()]);
}
PRINTD("seeding done");
PRINTD( "meshing" );
CGAL::refine_Delaunay_mesh_2_without_edge_refinement( cdt,
list_of_seeds.begin(), list_of_seeds.end(),
DummyCriteria<CDT>() );
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

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