Merge pull request #1365 from openscad/issue1355

Issue1355
This commit is contained in:
Marius Kintel 2015-06-19 18:06:52 -04:00
commit ae9ab8fd5e
11 changed files with 1157 additions and 1014 deletions

View file

@ -231,9 +231,9 @@ For a 64-bit Windows cross-build, replace 32 with 64 in the above instructions.
### Compilation ### Compilation
First, run 'qmake openscad.pro' from Qt4 to generate a Makefile. First, run 'qmake openscad.pro' from Qt to generate a Makefile.
On some systems, depending on which version(s) of Qt you have installed, you may need to specify which version you want to use, e.g. by running 'qmake4', 'qmake-qt4' or something alike. On some systems, depending on which version(s) of Qt you have installed, you may need to specify which version you want to use, e.g. by running 'qmake4', 'qmake-qt4', 'qmake -qt=qt5', or something alike.
Then run make. Finally you might run 'make install' as root or simply copy the 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. 'openscad' binary (OpenSCAD.app on Mac OS X) to the bin directory of your choice.

View file

@ -482,6 +482,8 @@ HEADERS += src/cgal.h \
src/Polygon2d-CGAL.h src/Polygon2d-CGAL.h
SOURCES += src/cgalutils.cc \ SOURCES += src/cgalutils.cc \
src/cgalutils-applyops.cc \
src/cgalutils-project.cc \
src/cgalutils-tess.cc \ src/cgalutils-tess.cc \
src/cgalutils-polyhedron.cc \ src/cgalutils-polyhedron.cc \
src/CGALCache.cc \ src/CGALCache.cc \

View file

@ -79,10 +79,22 @@ check_env()
detect_glu() detect_glu()
{ {
detect_glu_result= detect_glu_result=
if [ -e $DEPLOYDIR/include/GL/glu.h ]; then detect_glu_result=1; fi if [ -e $DEPLOYDIR/include/GL/glu.h ]; then
if [ -e /usr/include/GL/glu.h ]; then detect_glu_result=1; fi detect_glu_include=$DEPLOYDIR/include
if [ -e /usr/local/include/GL/glu.h ]; then detect_glu_result=1; fi detect_glu_result=1;
if [ -e /usr/pkg/X11R7/include/GL/glu.h ]; then detect_glu_result=1; fi fi
if [ -e /usr/include/GL/glu.h ]; then
detect_glu_include=/usr/include
detect_glu_result=1;
fi
if [ -e /usr/local/include/GL/glu.h ]; then
detect_glu_include=/usr/local/include
detect_glu_result=1;
fi
if [ -e /usr/pkg/X11R7/include/GL/glu.h ]; then
detect_glu_include=/usr/pkg/X11R7/include
detect_glu_result=1;
fi
return return
} }
@ -505,10 +517,20 @@ build_opencsg()
cp opencsg.pro opencsg.pro.bak cp opencsg.pro opencsg.pro.bak
cat opencsg.pro.bak | sed s/example// > opencsg.pro cat opencsg.pro.bak | sed s/example// > opencsg.pro
detect_glu
GLU_INCLUDE=$detect_glu_include
if [ ! $detect_glu_result ]; then
build_glu 9.0.0
fi
if [ "`command -v qmake-qt4`" ]; then if [ "`command -v qmake-qt4`" ]; then
OPENCSG_QMAKE=qmake-qt4 OPENCSG_QMAKE=qmake-qt4
elif [ "`command -v qmake4`" ]; then elif [ "`command -v qmake4`" ]; then
OPENCSG_QMAKE=qmake4 OPENCSG_QMAKE=qmake4
elif [ "`command -v qmake-qt5`" ]; then
OPENCSG_QMAKE=qmake-qt5
elif [ "`command -v qmake5`" ]; then
OPENCSG_QMAKE=qmake5
elif [ "`command -v qmake`" ]; then elif [ "`command -v qmake`" ]; then
OPENCSG_QMAKE=qmake OPENCSG_QMAKE=qmake
else else
@ -518,15 +540,18 @@ build_opencsg()
cp src/Makefile src/Makefile.bak cp src/Makefile src/Makefile.bak
cat Makefile.bak | sed s/example// |sed s/glew// > Makefile cat Makefile.bak | sed s/example// |sed s/glew// > Makefile
cat src/Makefile.bak | sed s@^INCPATH.*@INCPATH\ =\ -I$BASEDIR/include\ -I../include\ -I..\ -I.@ > src/Makefile cat src/Makefile.bak | sed s@^INCPATH.*@INCPATH\ =\ -I$BASEDIR/include\ -I../include\ -I..\ -I$GLU_INCLUDE -I.@ > src/Makefile
cp src/Makefile src/Makefile.bak2 cp src/Makefile src/Makefile.bak2
cat src/Makefile.bak2 | sed s@^LIBS.*@LIBS\ =\ -L$BASEDIR/lib\ -L/usr/X11R6/lib\ -lGLU\ -lGL@ > src/Makefile cat src/Makefile.bak2 | sed s@^LIBS.*@LIBS\ =\ -L$BASEDIR/lib\ -L/usr/X11R6/lib\ -lGLU\ -lGL@ > src/Makefile
tmp=$version tmp=$version
detect_glu
if [ ! $detect_glu_result ]; then build_glu 9.0.0 ; fi
version=$tmp version=$tmp
fi fi
if [ ! $OPENCSG_QMAKE = "make" ]; then
OPENCSG_QMAKE=$OPENCSG_QMAKE' "QMAKE_CXXFLAGS+=-I'$GLU_INCLUDE'"'
fi
echo OPENCSG_QMAKE: $OPENCSG_QMAKE
cd $BASEDIR/src/OpenCSG-$version/src cd $BASEDIR/src/OpenCSG-$version/src
$OPENCSG_QMAKE $OPENCSG_QMAKE
@ -776,6 +801,11 @@ if [ $1 ]; then
build_gettext 0.18.3.1 build_gettext 0.18.3.1
exit $? exit $?
fi fi
if [ $1 = "harfbuzz" ]; then
# debian 7 lacks only harfbuzz
build_harfbuzz 0.9.23 --with-glib=yes
exit $?
fi
if [ $1 = "glib2" ]; then if [ $1 = "glib2" ]; then
# such a huge build, put here by itself # such a huge build, put here by itself
build_pkgconfig 0.28 build_pkgconfig 0.28

View file

@ -4,13 +4,24 @@
# this assumes you have sudo installed and running, or are running as root. # this assumes you have sudo installed and running, or are running as root.
# #
get_fedora_deps() get_fedora_deps_yum()
{ {
yum -y install qt5-qtbase-devel bison flex eigen3-devel harfbuzz-devel \ yum -y install qt5-qtbase-devel bison flex eigen3-devel harfbuzz-devel \
fontconfig-devel freetype-devel \ fontconfig-devel freetype-devel \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \ boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \ opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \
xorg-x11-server-Xvfb gettext xorg-x11-server-Xvfb gettext qscintilla-devel qscintilla-qt5-devel \
mesa-dri-drivers
}
get_fedora_deps_dnf()
{
dnf -y install qt5-qtbase-devel bison flex eigen3-devel harfbuzz-devel \
fontconfig-devel freetype-devel \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
opencsg-devel git libXmu-devel curl ImageMagick glib2-devel make \
xorg-x11-server-Xvfb gettext qscintilla-devel qscintilla-qt5-devel \
mesa-dri-drivers
} }
get_qomo_deps() get_qomo_deps()
@ -43,8 +54,13 @@ get_netbsd_deps()
get_opensuse_deps() get_opensuse_deps()
{ {
zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \ zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \ libqt4-devel glew-devel cmake git bison flex cgal-devel curl \
glib2-devel gettext glib2-devel gettext freetype-devel harfbuzz-devel libqscintilla-devel \
xvfb-run imagemagick opencsg-devel
echo if you are missing opencsg, please add the -graphics- repository
echo find your version from cat /etc/issue, then replace it below, then run
echo " zypper ar -f http://download.opensuse.org/repositories/graphics/openSUSE_13.2 graphics"
echo " zypper install opencsg-devel"
} }
get_mageia_deps() get_mageia_deps()
@ -58,12 +74,30 @@ get_mageia_deps()
get_debian_deps() get_debian_deps()
{ {
apt-get -y install \ apt-get -y install \
build-essential curl libffi-dev qtbase5-dev libqt5scintilla2-dev \ build-essential curl libffi-dev \
libxmu-dev cmake bison flex git-core libboost-all-dev \ libxmu-dev cmake bison flex git-core libboost-all-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev \ libXi-dev libmpfr-dev libboost-dev libglew-dev \
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \ libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \
imagemagick libfontconfig-dev libfreetype6-dev \ imagemagick libfontconfig-dev libfreetype6-dev \
libharfbuzz-dev gtk-doc-tools libglib2.0-dev gettext gtk-doc-tools libglib2.0-dev gettext xvfb pkg-config ragel
}
get_debian_8_deps()
{
get_debian_deps
apt-get -y install libharfbuzz-dev qtbase5-dev libqt5scintilla2-dev
}
get_debian_7_deps()
{
get_debian_deps
apt-get -y install libqt4-dev libqscintilla2-dev
}
get_ubuntu_14_deps()
{
get_debian_8_deps
apt-get -y install qt5-qmake
} }
unknown() unknown()
@ -73,18 +107,28 @@ unknown()
} }
if [ -e /etc/issue ]; then if [ -e /etc/issue ]; then
if [ "`grep -i ubuntu /etc/issue`" ]; then if [ "`grep -i ubuntu.1[4-9] /etc/issue`" ]; then
get_ubuntu_14_deps
elif [ "`grep -i ubuntu /etc/issue`" ]; then
get_debian_deps get_debian_deps
elif [ "`grep -i debian.GNU.Linux.7 /etc/issue`" ]; then
get_debian_7_deps
elif [ "`grep -i debian /etc/issue`" ]; then elif [ "`grep -i debian /etc/issue`" ]; then
get_debian_deps get_debian_8_deps
elif [ "`grep -i raspbian /etc/issue`" ]; then elif [ "`grep -i raspbian /etc/issue`" ]; then
get_debian_deps get_debian_deps
elif [ "`grep -i mint /etc/issue`" ]; then elif [ "`grep -i mint /etc/issue`" ]; then
get_debian_deps get_debian_deps
elif [ "`grep -i suse /etc/issue`" ]; then elif [ "`grep -i suse /etc/issue`" ]; then
get_opensuse_deps get_opensuse_deps
elif [ "`grep -i fedora.release.2[2-9] /etc/issue`" ]; then
get_fedora_deps_dnf
elif [ "`grep -i fedora.release.[3-9][0-9] /etc/issue`" ]; then
get_fedora_deps_dnf
elif [ "`grep -i fedora.release.2[0-1] /etc/issue`" ]; then
get_fedora_deps_yum
elif [ "`grep -i fedora /etc/issue`" ]; then elif [ "`grep -i fedora /etc/issue`" ]; then
get_fedora_deps get_fedora_deps_yum
elif [ "`grep -i red.hat /etc/issue`" ]; then elif [ "`grep -i red.hat /etc/issue`" ]; then
get_fedora_deps get_fedora_deps
elif [ "`grep -i mageia /etc/issue`" ]; then elif [ "`grep -i mageia /etc/issue`" ]; then

View file

@ -113,7 +113,9 @@ std::string PlatformUtils::userLibraryPath()
try { try {
std::string pathstr = PlatformUtils::documentsPath(); std::string pathstr = PlatformUtils::documentsPath();
if (pathstr=="") return ""; if (pathstr=="") return "";
path = boosty::canonical(fs::path( pathstr )); path = fs::path( pathstr );
if (!fs::exists(path)) return "";
path = boosty::canonical( path );
//PRINTB("path size %i",boosty::stringy(path).size()); //PRINTB("path size %i",boosty::stringy(path).size());
//PRINTB("lib path found: [%s]", path ); //PRINTB("lib path found: [%s]", path );
if (path.empty()) return ""; if (path.empty()) return "";
@ -134,7 +136,9 @@ std::string PlatformUtils::backupPath()
try { try {
std::string pathstr = PlatformUtils::documentsPath(); std::string pathstr = PlatformUtils::documentsPath();
if (pathstr=="") return ""; if (pathstr=="") return "";
path = boosty::canonical(fs::path( pathstr )); path = fs::path( pathstr );
if (!fs::exists(path)) return "";
path = boosty::canonical( path );
if (path.empty()) return ""; if (path.empty()) return "";
path /= OPENSCAD_FOLDER_NAME; path /= OPENSCAD_FOLDER_NAME;
path /= "backups"; path /= "backups";

422
src/cgalutils-applyops.cc Normal file
View file

@ -0,0 +1,422 @@
// this file is split into many separate cgalutils* files
// in order to workaround gcc 4.9.1 crashing on systems with only 2GB of RAM
#ifdef ENABLE_CGAL
#include "cgalutils.h"
#include "polyset.h"
#include "printutils.h"
#include "Polygon2d.h"
#include "polyset-utils.h"
#include "grid.h"
#include "node.h"
#include "cgal.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/normal_vector_newell_3.h>
#include <CGAL/Handle_hash_function.h>
#include <CGAL/config.h>
#include <CGAL/version.h>
// Apply CGAL bugfix for CGAL-4.5.x
#if CGAL_VERSION_NR > CGAL_VERSION_NUMBER(4,5,1) || CGAL_VERSION_NR < CGAL_VERSION_NUMBER(4,5,0)
#include <CGAL/convex_hull_3.h>
#else
#include "convex_hull_3_bugfix.h"
#endif
#include "svg.h"
#include "Reindexer.h"
#include "GeometryUtils.h"
#include <map>
#include <queue>
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
namespace CGALUtils {
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();
}
/*!
Applies op to all children and returns the result.
The child list should be guaranteed to contain non-NULL 3D or empty Geometry objects
*/
CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op)
{
CGAL_Nef_polyhedron *N = NULL;
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
// Speeds up n-ary union operations significantly
CGAL::Nef_nary_union_3<CGAL_Nef_polyhedron3> nary_union;
int nary_union_num_inserted = 0;
BOOST_FOREACH(const Geometry::ChildItem &item, children) {
const shared_ptr<const Geometry> &chgeom = item.second;
shared_ptr<const CGAL_Nef_polyhedron> chN =
dynamic_pointer_cast<const CGAL_Nef_polyhedron>(chgeom);
if (!chN) {
const PolySet *chps = dynamic_cast<const PolySet*>(chgeom.get());
if (chps) chN.reset(createNefPolyhedronFromGeometry(*chps));
}
if (op == OPENSCAD_UNION) {
if (!chN->isEmpty()) {
// nary_union.add_polyhedron() can issue assertion errors:
// https://github.com/openscad/openscad/issues/802
nary_union.add_polyhedron(*chN->p3);
nary_union_num_inserted++;
}
continue;
}
// Initialize N with first expected geometric object
if (!N) {
N = new CGAL_Nef_polyhedron(*chN);
continue;
}
// Intersecting something with nothing results in nothing
if (chN->isEmpty()) {
if (op == OPENSCAD_INTERSECTION) *N = *chN;
continue;
}
// empty op <something> => empty
if (N->isEmpty()) continue;
switch (op) {
case OPENSCAD_INTERSECTION:
*N *= *chN;
break;
case OPENSCAD_DIFFERENCE:
*N -= *chN;
break;
case OPENSCAD_MINKOWSKI:
N->minkowski(*chN);
break;
default:
PRINTB("ERROR: Unsupported CGAL operator: %d", op);
}
item.first->progress_report();
}
if (op == OPENSCAD_UNION && nary_union_num_inserted > 0) {
N = new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(nary_union.get_union()));
}
}
// union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad
catch (const CGAL::Failure_exception &e) {
std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_UNION ? "union" : "UNKNOWN";
PRINTB("ERROR: CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what());
}
CGAL::set_error_behaviour(old_behaviour);
return N;
}
bool applyHull(const Geometry::ChildList &children, PolySet &result)
{
typedef CGAL::Epick K;
// Collect point cloud
// NB! CGAL's convex_hull_3() doesn't like std::set iterators, so we use a list
// instead.
std::list<K::Point_3> points;
BOOST_FOREACH(const Geometry::ChildItem &item, children) {
const shared_ptr<const Geometry> &chgeom = item.second;
const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(chgeom.get());
if (N) {
if (!N->isEmpty()) {
for (CGAL_Nef_polyhedron3::Vertex_const_iterator i = N->p3->vertices_begin(); i != N->p3->vertices_end(); ++i) {
points.push_back(vector_convert<K::Point_3>(i->point()));
}
}
} else {
const PolySet *ps = dynamic_cast<const PolySet *>(chgeom.get());
if (ps) {
BOOST_FOREACH(const Polygon &p, ps->polygons) {
BOOST_FOREACH(const Vector3d &v, p) {
points.push_back(K::Point_3(v[0], v[1], v[2]));
}
}
}
}
}
if (points.size() <= 3) return false;
// Apply hull
bool success = false;
if (points.size() >= 4) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL::Polyhedron_3<K> r;
CGAL::convex_hull_3(points.begin(), points.end(), r);
PRINTDB("After hull vertices: %d", r.size_of_vertices());
PRINTDB("After hull facets: %d", r.size_of_facets());
PRINTDB("After hull closed: %d", r.is_closed());
PRINTDB("After hull valid: %d", r.is_valid());
success = !createPolySetFromPolyhedron(r, result);
}
catch (const CGAL::Assertion_exception &e) {
PRINTB("ERROR: CGAL error in applyHull(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
}
return success;
}
/*!
children cannot contain NULL objects
*/
Geometry const * applyMinkowski(const Geometry::ChildList &children)
{
CGAL::Timer t,t_tot;
assert(children.size() >= 2);
Geometry::ChildList::const_iterator it = children.begin();
t_tot.start();
Geometry const* operands[2] = {it->second.get(), NULL};
try {
while (++it != children.end()) {
operands[1] = it->second.get();
typedef CGAL::Epick Hull_kernel;
std::list<CGAL_Polyhedron> P[2];
std::list<CGAL::Polyhedron_3<Hull_kernel> > result_parts;
for (size_t i = 0; i < 2; i++) {
CGAL_Polyhedron poly;
const PolySet * ps = dynamic_cast<const PolySet *>(operands[i]);
const CGAL_Nef_polyhedron * nef = dynamic_cast<const CGAL_Nef_polyhedron *>(operands[i]);
if (ps) CGALUtils::createPolyhedronFromPolySet(*ps, poly);
else if (nef && nef->p3->is_simple()) nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>(*nef->p3, poly);
else throw 0;
if ((ps && ps->is_convex()) ||
(!ps && is_weakly_convex(poly))) {
PRINTDB("Minkowski: child %d is convex and %s",i % (ps?"PolySet":"Nef"));
P[i].push_back(poly);
} else {
CGAL_Nef_polyhedron3 decomposed_nef;
if (ps) {
PRINTDB("Minkowski: child %d is nonconvex PolySet, transforming to Nef and decomposing...", i);
CGAL_Nef_polyhedron *p = createNefPolyhedronFromGeometry(*ps);
if (!p->isEmpty()) decomposed_nef = *p->p3;
delete p;
} else {
PRINTDB("Minkowski: child %d is nonconvex Nef, decomposing...",i);
decomposed_nef = *nef->p3;
}
t.start();
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();
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[i].push_back(poly);
}
}
PRINTDB("Minkowski: decomposed into %d convex parts", P[i].size());
t.stop();
PRINTDB("Minkowski: decomposition took %f s", t.time());
}
}
std::vector<Hull_kernel::Point_3> points[2];
std::vector<Hull_kernel::Point_3> minkowski_points;
for (size_t i = 0; i < P[0].size(); i++) {
for (size_t j = 0; j < P[1].size(); j++) {
t.start();
points[0].clear();
points[1].clear();
for (int k = 0; k < 2; k++) {
std::list<CGAL_Polyhedron>::iterator it = P[k].begin();
std::advance(it, k==0?i:j);
CGAL_Polyhedron const& poly = *it;
points[k].reserve(poly.size_of_vertices());
for (CGAL_Polyhedron::Vertex_const_iterator pi = poly.vertices_begin(); pi != poly.vertices_end(); ++pi) {
CGAL_Polyhedron::Point_3 const& p = pi->point();
points[k].push_back(Hull_kernel::Point_3(to_double(p[0]),to_double(p[1]),to_double(p[2])));
}
}
minkowski_points.clear();
minkowski_points.reserve(points[0].size() * points[1].size());
for (size_t i = 0; i < points[0].size(); i++) {
for (size_t j = 0; j < points[1].size(); j++) {
minkowski_points.push_back(points[0][i]+(points[1][j]-CGAL::ORIGIN));
}
}
if (minkowski_points.size() <= 3) {
t.stop();
continue;
}
CGAL::Polyhedron_3<Hull_kernel> result;
t.stop();
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<Hull_kernel::Point_3> strict_points;
strict_points.reserve(minkowski_points.size());
for (CGAL::Polyhedron_3<Hull_kernel>::Vertex_iterator i = result.vertices_begin(); i != result.vertices_end(); ++i) {
Hull_kernel::Point_3 const& p = i->point();
CGAL::Polyhedron_3<Hull_kernel>::Vertex::Halfedge_handle h,e;
h = i->halfedge();
e = h;
bool collinear = false;
bool coplanar = true;
do {
Hull_kernel::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 (CGAL::Polyhedron_3<Hull_kernel>::Vertex::Halfedge_handle j = h->next_on_vertex();
j != h && !collinear && ! coplanar;
j = j->next_on_vertex()) {
Hull_kernel::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 (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<CGAL::Polyhedron_3<Hull_kernel> >::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);
// FIXME: This hould really never throw.
// Assert once we figured out what went wrong with issue #1069?
if (!N) throw 0;
t.stop();
PRINTDB("Minkowski: Union done: %f s",t.time());
t.reset();
operands[0] = N;
} else {
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;
}
}
}; // namespace CGALUtils
#endif // ENABLE_CGAL

View file

@ -11,13 +11,8 @@
#include <boost/foreach.hpp> #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 #undef GEN_SURFACE_DEBUG
namespace /* anonymous */ {
template <typename Polyhedron> template <typename Polyhedron>
class CGAL_Build_PolySet : public CGAL::Modifier_base<typename Polyhedron::HalfedgeDS> class CGAL_Build_PolySet : public CGAL::Modifier_base<typename Polyhedron::HalfedgeDS>

266
src/cgalutils-project.cc Normal file
View file

@ -0,0 +1,266 @@
// this file is split into many separate cgalutils* files
// in order to workaround gcc 4.9.1 crashing on systems with only 2GB of RAM
#ifdef ENABLE_CGAL
#include "cgalutils.h"
#include "polyset.h"
#include "printutils.h"
#include "Polygon2d.h"
#include "polyset-utils.h"
#include "grid.h"
#include "node.h"
#include "cgal.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/normal_vector_newell_3.h>
#include <CGAL/Handle_hash_function.h>
#include <CGAL/config.h>
#include <CGAL/version.h>
// Apply CGAL bugfix for CGAL-4.5.x
#if CGAL_VERSION_NR > CGAL_VERSION_NUMBER(4,5,1) || CGAL_VERSION_NR < CGAL_VERSION_NUMBER(4,5,0)
#include <CGAL/convex_hull_3.h>
#else
#include "convex_hull_3_bugfix.h"
#endif
#include "svg.h"
#include "Reindexer.h"
#include "GeometryUtils.h"
#include <map>
#include <queue>
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
static void add_outline_to_poly(CGAL_Nef_polyhedron2::Explorer &explorer,
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator circ,
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator end,
bool positive,
Polygon2d *poly) {
Outline2d outline;
CGAL_For_all(circ, end) {
if (explorer.is_standard(explorer.target(circ))) {
CGAL_Nef_polyhedron2::Explorer::Point ep = explorer.point(explorer.target(circ));
outline.vertices.push_back(Vector2d(to_double(ep.x()),
to_double(ep.y())));
}
}
if (!outline.vertices.empty()) {
outline.positive = positive;
poly->addOutline(outline);
}
}
static Polygon2d *convertToPolygon2d(const CGAL_Nef_polyhedron2 &p2)
{
Polygon2d *poly = new Polygon2d;
typedef CGAL_Nef_polyhedron2::Explorer Explorer;
typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
Explorer E = p2.explorer();
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) {
if (!fit->mark()) continue;
heafcc_t fcirc(E.face_cycle(fit)), fend(fcirc);
add_outline_to_poly(E, fcirc, fend, true, poly);
for (CGAL_Nef_polyhedron2::Explorer::Hole_const_iterator j = E.holes_begin(fit);j != E.holes_end(fit); ++j) {
CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator hcirc(j), hend(hcirc);
add_outline_to_poly(E, hcirc, hend, false, poly);
}
}
poly->setSanitized(true);
return poly;
}
/*
ZRemover
This class converts one or more Nef3 polyhedra into a Nef2 polyhedron by
stripping off the 'z' coordinates from the vertices. The resulting Nef2
poly is accumulated in the 'output_nefpoly2d' member variable.
The 'z' coordinates will either be all 0s, for an xy-plane intersected Nef3,
or, they will be a mixture of -eps and +eps, for a thin-box intersected Nef3.
Notes on CGAL's Nef Polyhedron2:
1. The 'mark' on a 2d Nef face is important when doing unions/intersections.
If the 'mark' of a face is wrong the resulting nef2 poly will be unexpected.
2. The 'mark' can be dependent on the points fed to the Nef2 constructor.
This is why we iterate through the 3d faces using the halfedge cycle
source()->target() instead of the ordinary source()->source(). The
the latter can generate sequences of points that will fail the
the CGAL::is_simple_2() test, resulting in improperly marked nef2 polys.
3. 3d facets have 'two sides'. we throw out the 'down' side to prevent dups.
The class uses the 'visitor' pattern from the CGAL manual. See also
http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron3.html
OGL_helper.h
*/
class ZRemover {
public:
CGAL_Nef_polyhedron2::Boundary boundary;
boost::shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d;
boost::shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d;
CGAL::Direction_3<CGAL_Kernel3> up;
ZRemover()
{
output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
boundary = CGAL_Nef_polyhedron2::INCLUDED;
up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1);
}
void visit( CGAL_Nef_polyhedron3::Vertex_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::Halfedge_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 ) {}
void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet );
};
void ZRemover::visit(CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet)
{
PRINTDB(" <!-- ZRemover Halffacet visit. Mark: %i --> ",hfacet->mark());
if (hfacet->plane().orthogonal_direction() != this->up) {
PRINTD(" <!-- ZRemover down-facing half-facet. skipping -->");
PRINTD(" <!-- ZRemover Halffacet visit end-->");
return;
}
// possible optimization - throw out facets that are vertically oriented
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci;
int contour_counter = 0;
CGAL_forall_facet_cycles_of(fci, hfacet) {
if (fci.is_shalfedge()) {
PRINTD(" <!-- ZRemover Halffacet cycle begin -->");
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1);
std::vector<CGAL_Nef_polyhedron2::Explorer::Point> contour;
CGAL_For_all(c1, cend) {
CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point();
CGAL_Nef_polyhedron2::Explorer::Point point2d(CGAL::to_double(point3d.x()),
CGAL::to_double(point3d.y()));
contour.push_back(point2d);
}
if (contour.size()==0) continue;
if (OpenSCAD::debug!="")
PRINTDB(" <!-- is_simple_2: %i -->", CGAL::is_simple_2(contour.begin(), contour.end()));
tmpnef2d.reset(new CGAL_Nef_polyhedron2(contour.begin(), contour.end(), boundary));
if (contour_counter == 0) {
PRINTDB(" <!-- contour is a body. make union(). %i points -->", contour.size());
*(output_nefpoly2d) += *(tmpnef2d);
} else {
PRINTDB(" <!-- contour is a hole. make intersection(). %i points -->", contour.size());
*(output_nefpoly2d) *= *(tmpnef2d);
}
/*log << "\n<!-- ======== output tmp nef: ==== -->\n"
<< OpenSCAD::dump_svg(*tmpnef2d) << "\n"
<< "\n<!-- ======== output accumulator: ==== -->\n"
<< OpenSCAD::dump_svg(*output_nefpoly2d) << "\n";*/
contour_counter++;
} else {
PRINTD(" <!-- ZRemover trivial facet cycle skipped -->");
}
PRINTD(" <!-- ZRemover Halffacet cycle end -->");
}
PRINTD(" <!-- ZRemover Halffacet visit end -->");
}
namespace CGALUtils {
Polygon2d *project(const CGAL_Nef_polyhedron &N, bool cut)
{
Polygon2d *poly = NULL;
if (N.getDimension() != 3) return poly;
CGAL_Nef_polyhedron newN;
if (cut) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3(0,0,1,0);
newN.p3.reset(new CGAL_Nef_polyhedron3(N.p3->intersection(xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY)));
}
catch (const CGAL::Failure_exception &e) {
PRINTDB("CGALUtils::project during plane intersection: %s", e.what());
try {
PRINTD("Trying alternative intersection using very large thin box: ");
std::vector<CGAL_Point_3> pts;
// dont use z of 0. there are bugs in CGAL.
double inf = 1e8;
double eps = 0.001;
CGAL_Point_3 minpt(-inf, -inf, -eps);
CGAL_Point_3 maxpt( inf, inf, eps);
CGAL_Iso_cuboid_3 bigcuboid(minpt, maxpt);
for (int i=0;i<8;i++) pts.push_back(bigcuboid.vertex(i));
CGAL_Polyhedron bigbox;
CGAL::convex_hull_3(pts.begin(), pts.end(), bigbox);
CGAL_Nef_polyhedron3 nef_bigbox(bigbox);
newN.p3.reset(new CGAL_Nef_polyhedron3(nef_bigbox.intersection(*N.p3)));
}
catch (const CGAL::Failure_exception &e) {
PRINTB("ERROR: CGAL error in CGALUtils::project during bigbox intersection: %s", e.what());
}
}
if (!newN.p3 || newN.p3->is_empty()) {
CGAL::set_error_behaviour(old_behaviour);
PRINT("WARNING: projection() failed.");
return poly;
}
PRINTDB("%s",OpenSCAD::svg_header(480, 100000));
try {
ZRemover zremover;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
CGAL_Nef_polyhedron3::SFace_const_handle sface_handle;
for (i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i) {
PRINTDB("<!-- volume. mark: %s -->",i->mark());
for (j = i->shells_begin(); j != i->shells_end(); ++j) {
PRINTDB("<!-- shell. (vol mark was: %i)", i->mark());;
sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle(j);
newN.p3->visit_shell_objects(sface_handle , zremover);
PRINTD("<!-- shell. end. -->");
}
PRINTD("<!-- volume end. -->");
}
poly = convertToPolygon2d(*zremover.output_nefpoly2d);
} catch (const CGAL::Failure_exception &e) {
PRINTB("ERROR: CGAL error in CGALUtils::project while flattening: %s", e.what());
}
PRINTD("</svg>");
CGAL::set_error_behaviour(old_behaviour);
}
// In projection mode all the triangles are projected manually into the XY plane
else {
PolySet ps(3);
bool err = CGALUtils::createPolySetFromNefPolyhedron3(*N.p3, ps);
if (err) {
PRINT("ERROR: Nef->PolySet failed");
return poly;
}
poly = PolysetUtils::project(ps);
}
return poly;
}
} // namespace
#endif // ENABLE_CGAL

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,13 @@ typedef CGAL::Point_3<K> Vertex3K;
typedef std::vector<Vertex3K> PolygonK; typedef std::vector<Vertex3K> PolygonK;
typedef std::vector<PolygonK> PolyholeK; typedef std::vector<PolygonK> PolyholeK;
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]));
}
}
namespace CGALUtils { namespace CGALUtils {
bool applyHull(const Geometry::ChildList &children, PolySet &P); bool applyHull(const Geometry::ChildList &children, PolySet &P);
CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op); CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op);

View file

@ -329,9 +329,19 @@ if (NOT OPENGL_GLU_FOUND)
message(FATAL "GLU library not found") message(FATAL "GLU library not found")
endif() endif()
set(OPENGL_LIBRARIES ${OPENGL_glu_LIBRARY} ${OPENGL_LIBRARIES}) set(OPENGL_LIBRARIES ${OPENGL_glu_LIBRARY} ${OPENGL_LIBRARIES})
message(STATUS "OpenGL LIBRARIES: ") endif()
foreach(GLLIB ${OPENGL_LIBRARIES}) message(STATUS "OPENGL_LIBRARIES: ")
message(STATUS " " ${GLLIB}) foreach(GLLIB ${OPENGL_LIBRARIES})
message(STATUS " " ${GLLIB})
endforeach()
if (UNIX)
# see github issue 1355. as of writing the code for offscreen GL context
# setup requires X11 on Un*x like systems (not Apple/Win).
find_package(X11 REQUIRED)
message(STATUS "X11_INCLUDE_DIR: " ${X11_INCLUDE_DIR})
message(STATUS "X11_LIBRARIES: ")
foreach(XLIB ${X11_LIBRARIES})
message(STATUS " " ${XLIB})
endforeach() endforeach()
endif() endif()
@ -490,8 +500,12 @@ foreach(CGAL3RDPLIB ${CGAL_3RD_PARTY_LIBRARIES})
endforeach() endforeach()
if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" ) if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" )
string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT}) string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT}) string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
# clang fails to build several included standard C++ libs on some
# machines when FORTIFY_SOURCE is enabled.
message(STATUS "disabling FORTIFY_SOURCE: https://bugzilla.redhat.com/show_bug.cgi?id=1188075")
string(REPLACE "FORTIFY_SOURCE=2" "FORTIFY_SOURCE=0" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
endif() endif()
# GLib2 # GLib2
@ -632,6 +646,8 @@ elseif(UNIX)
set(OFFSCREEN_CTX_SOURCE "OffscreenContextGLX.cc" CACHE TYPE STRING) set(OFFSCREEN_CTX_SOURCE "OffscreenContextGLX.cc" CACHE TYPE STRING)
set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-lodepng.cc" CACHE TYPE STRING) set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-lodepng.cc" CACHE TYPE STRING)
set(PLATFORMUTILS_SOURCE "PlatformUtils-posix.cc" CACHE TYPE STRING) set(PLATFORMUTILS_SOURCE "PlatformUtils-posix.cc" CACHE TYPE STRING)
# X11 needed for Offscreen OpenGL on current Un*x, see github issue 1355
set(OFFSCREEN_CTX_LIBRARIES ${X11_LIBRARIES} CACHE TYPE STRING)
elseif(WIN32) elseif(WIN32)
message(STATUS "Offscreen OpenGL Context - using Microsoft WGL") message(STATUS "Offscreen OpenGL Context - using Microsoft WGL")
set(OFFSCREEN_CTX_SOURCE "OffscreenContextWGL.cc" CACHE TYPE STRING) set(OFFSCREEN_CTX_SOURCE "OffscreenContextWGL.cc" CACHE TYPE STRING)
@ -710,6 +726,8 @@ set(CGAL_SOURCES
../src/CSGTermEvaluator.cc ../src/CSGTermEvaluator.cc
../src/CGAL_Nef_polyhedron.cc ../src/CGAL_Nef_polyhedron.cc
../src/cgalutils.cc ../src/cgalutils.cc
../src/cgalutils-applyops.cc
../src/cgalutils-project.cc
../src/cgalutils-tess.cc ../src/cgalutils-tess.cc
../src/cgalutils-polyhedron.cc ../src/cgalutils-polyhedron.cc
../src/CGALCache.cc ../src/CGALCache.cc
@ -786,6 +804,7 @@ endif()
add_library(tests-offscreen STATIC ${OFFSCREEN_SOURCES}) add_library(tests-offscreen STATIC ${OFFSCREEN_SOURCES})
set_target_properties(tests-offscreen PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG} -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") set_target_properties(tests-offscreen PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG} -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(tests-offscreen ${OFFSCREEN_CTX_LIBRARIES})
# #
# modulecachetest # modulecachetest