summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/testing.txt27
-rw-r--r--glew.pri1
-rw-r--r--openscad.pro10
-rwxr-xr-xscripts/linux-build-dependencies.sh227
-rw-r--r--src/CGAL_Nef_polyhedron.h2
-rw-r--r--src/CGAL_Nef_polyhedron_DxfData.cc25
-rw-r--r--src/PolySetCGALEvaluator.cc203
-rw-r--r--src/PolySetCGALEvaluator.h2
-rw-r--r--src/context.cc6
-rw-r--r--src/dxfdata.cc25
-rw-r--r--src/dxfdata.h1
-rw-r--r--src/dxftess-cgal.cc1
-rw-r--r--src/glview.cc4
-rw-r--r--src/polyset.cc30
-rw-r--r--src/polyset.h2
-rw-r--r--testdata/scad/features/import_dxf-tests.scad1
-rw-r--r--testdata/scad/features/import_stl-tests.scad1
-rw-r--r--testdata/scad/features/projection-tests.scad7
-rw-r--r--testdata/scad/templates/import_dxf-tests-template.scad10
-rw-r--r--testdata/scad/templates/import_stl-tests-template.scad4
-rw-r--r--testdata/scad/templates/include-tests-template.scad (renamed from testdata/scad/misc/include-tests-template.scad)0
-rw-r--r--testdata/scad/templates/use-tests-template.scad (renamed from testdata/scad/misc/use-tests-template.scad)0
-rw-r--r--tests/CMakeLists.txt10
-rw-r--r--tests/regression/cgalpngtest/import_dxf-tests-expected.pngbin9919 -> 4286 bytes
-rw-r--r--tests/regression/cgalpngtest/import_stl-tests-expected.pngbin9164 -> 5758 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-tests-expected.pngbin9159 -> 5836 bytes
-rw-r--r--tests/regression/dumptest/import_dxf-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/import_stl-tests-expected.txt3
-rw-r--r--tests/regression/dumptest/projection-tests-expected.txt18
-rw-r--r--tests/regression/opencsgtest/import_dxf-tests-expected.pngbin9289 -> 4491 bytes
-rw-r--r--tests/regression/opencsgtest/import_stl-tests-expected.pngbin9438 -> 6207 bytes
-rw-r--r--tests/regression/opencsgtest/projection-tests-expected.pngbin9661 -> 6531 bytes
-rw-r--r--tests/regression/throwntogethertest/import_dxf-tests-expected.pngbin4414 -> 4503 bytes
-rw-r--r--tests/regression/throwntogethertest/import_stl-tests-expected.pngbin9438 -> 6207 bytes
-rw-r--r--tests/regression/throwntogethertest/projection-tests-expected.pngbin5062 -> 5865 bytes
35 files changed, 527 insertions, 96 deletions
diff --git a/doc/testing.txt b/doc/testing.txt
index 6990c2f..8ab1cee 100644
--- a/doc/testing.txt
+++ b/doc/testing.txt
@@ -77,6 +77,10 @@ virtual framebuffer program like Xvnc or Xvfb. For example:
$ Xvfb :5 -screen 0 800x600x24 &
$ DISPLAY=:5 ctest
+or
+
+$ xvfb-run ctest
+
Some versions of Xvfb may fail, however.
1. Trouble finding libraries on unix
@@ -84,13 +88,19 @@ Some versions of Xvfb may fail, however.
To help CMAKE find eigen2, OpenCSG, CGAL, Boost, and GLEW, you can use
environment variables, just like for the main qmake & openscad.pro. Examples:
- OPENSCAD_LIBRARIES=~ cmake .
- CGALDIR=~/CGAL-3.9 BOOSTDIR=~/boost-1.47.0 cmake .
-
+ OPENSCAD_LIBRARIES=$HOME cmake .
+ CGALDIR=$HOME/CGAL-3.9 BOOSTDIR=$HOME/boost-1.47.0 cmake .
+
Valid variables are as follows:
BOOSTDIR, CGALDIR, EIGEN2DIR, GLEWDIR, OPENCSGDIR, OPENSCAD_LIBRARIES
+ When running, this might help find your locally built libraries (assuming
+ you installed into $HOME)
+
+ Linux: export LD_LIBRARY_PATH=$HOME/lib:$HOME/lib64
+ Mac: export DYLD_LIBRARY_PATH=$HOME/lib
+
2. Location of logs
Logs of test runs are found in tests/build/Testing/Temporary
@@ -111,7 +121,16 @@ Comparison method.
Your version of imagemagick is old. Upgrade, or pass -DCOMPARATOR=old to
cmake. The comparison will be of lowered reliability.
-5. Other issues
+5. Locale errors
+
+"terminate called after throwing an instance of 'std::runtime_error'
+ what(): locale::facet::_S_create_c_locale name not valid"
+
+Is a boost/libstdc++ bug. Fix like so:
+
+ $ export LC_MESSAGES=
+
+6. Other issues
The OpenSCAD User Manual has a section on buildling. Please check there
for updates:
diff --git a/glew.pri b/glew.pri
index 981d14b..f4a6ccd 100644
--- a/glew.pri
+++ b/glew.pri
@@ -5,6 +5,7 @@ glew {
!isEmpty(GLEW_DIR) {
QMAKE_INCDIR += $$GLEW_DIR/include
QMAKE_LIBDIR += $$GLEW_DIR/lib
+ QMAKE_LIBDIR += $$GLEW_DIR/lib64
message("GLEW location: $$GLEW_DIR")
}
diff --git a/openscad.pro b/openscad.pro
index 453ab77..591ed5f 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -81,9 +81,13 @@ win32 {
CONFIG += qt
QT += opengl
-# Fedora Linux + DSO fix
-linux*:exists(/usr/lib64/libGLU*)|linux*:exists(/usr/lib/libGLU*) {
- LIBS += -lGLU
+# see http://fedoraproject.org/wiki/UnderstandingDSOLinkChange
+# and https://github.com/openscad/openscad/pull/119
+# ( QT += opengl does not automatically link glu on some DSO systems. )
+unix:!macx {
+ !contains ( QMAKE_LIBS_OPENGL, "-lGLU" ) {
+ QMAKE_LIBS_OPENGL += -lGLU
+ }
}
netbsd* {
diff --git a/scripts/linux-build-dependencies.sh b/scripts/linux-build-dependencies.sh
new file mode 100755
index 0000000..9097850
--- /dev/null
+++ b/scripts/linux-build-dependencies.sh
@@ -0,0 +1,227 @@
+#!/bin/sh -e
+#
+# This script builds all library dependencies of OpenSCAD for Linux
+#
+# This script must be run from the OpenSCAD source root directory
+#
+# Usage: linux-build-dependencies.sh
+#
+# Prerequisites:
+# - curl
+# -- you can uncomment 'build_curl' at the bottom
+# -- and add $BASEDIR/bin to your PATH, i.e. in .bash_profile
+# - Qt4
+# - cmake 2.8
+# -- you can uncomment 'build_cmake' at the bottom
+#
+
+BASEDIR=$HOME
+OPENSCADDIR=$PWD
+SRCDIR=$BASEDIR/src
+DEPLOYDIR=$BASEDIR
+NUMCPU=2 # paralell builds for some libraries
+
+printUsage()
+{
+ echo "Usage: $0"
+ echo
+}
+
+build_cmake()
+{
+ version=$1
+ echo "Building cmake" $version "..."
+ cd $BASEDIR/src
+ rm -rf cmake-$version
+ if [ ! -f cmake-$version.tar.gz ]; then
+ curl -O http://www.cmake.org/files/v2.8/cmake-$version.tar.gz
+ fi
+ tar zxf cmake-$version.tar.gz
+ cd cmake-$version
+ mkdir build
+ cd build
+ ../configure --prefix=$DEPLOYDIR
+ make -j$NUMCPU
+ make install
+}
+
+build_curl()
+{
+ version=$1
+ echo "Building curl" $version "..."
+ cd $BASEDIR/src
+ rm -rf curl-$version
+ if [ ! -f curl-$version.tar.bz2 ]; then
+ wget http://curl.haxx.se/download/curl-$version.tar.bz2
+ fi
+ tar xjf curl-$version.tar.bz2
+ cd curl-$version
+ mkdir build
+ cd build
+ ../configure --prefix=$DEPLOYDIR
+ make -j$NUMCPU
+ make install
+}
+
+build_gmp()
+{
+ version=$1
+ echo "Building gmp" $version "..."
+ cd $BASEDIR/src
+ rm -rf gmp-$version
+ if [ ! -f gmp-$version.tar.bz2 ]; then
+ curl -O ftp://ftp.gmplib.org/pub/gmp-$version/gmp-$version.tar.bz2
+ fi
+ tar xjf gmp-$version.tar.bz2
+ cd gmp-$version
+ mkdir build
+ cd build
+ ../configure --prefix=$DEPLOYDIR --enable-cxx
+ make install
+}
+
+build_mpfr()
+{
+ version=$1
+ echo "Building mpfr" $version "..."
+ cd $BASEDIR/src
+ rm -rf mpfr-$version
+ if [ ! -f mpfr-$version.tar.bz2 ]; then
+ curl -O http://www.mpfr.org/mpfr-current/mpfr-$version.tar.bz2
+ fi
+ tar xjf mpfr-$version.tar.bz2
+ cd mpfr-$version
+ curl -O http://www.mpfr.org/mpfr-current/allpatches
+ patch -N -Z -p1 < allpatches
+ mkdir build
+ cd build
+ ../configure --prefix=$DEPLOYDIR --with-gmp=$DEPLOYDIR
+ make install
+ cd ..
+}
+
+build_boost()
+{
+ version=$1
+ bversion=`echo $version | tr "." "_"`
+ echo "Building boost" $version "..."
+ cd $BASEDIR/src
+ rm -rf boost_$bversion
+ if [ ! -f boost_$bversion.tar.bz2 ]; then
+ curl -LO http://downloads.sourceforge.net/project/boost/boost/$version/boost_$bversion.tar.bz2
+ fi
+ tar xjf boost_$bversion.tar.bz2
+ cd boost_$bversion
+ # We only need certain portions of boost
+ ./bootstrap.sh --prefix=$DEPLOYDIR --with-libraries=thread,program_options,filesystem,system,regex
+ ./bjam -j$NUMCPU
+ ./bjam install
+}
+
+build_cgal()
+{
+ version=$1
+ echo "Building CGAL" $version "..."
+ cd $BASEDIR/src
+ rm -rf CGAL-$version
+ if [ ! -f CGAL-$version.tar.gz ]; then
+ #4.0
+ curl -O https://gforge.inria.fr/frs/download.php/30387/CGAL-$version.tar.gz
+ # 3.9 curl -O https://gforge.inria.fr/frs/download.php/29125/CGAL-$version.tar.gz
+ # 3.8 curl -O https://gforge.inria.fr/frs/download.php/28500/CGAL-$version.tar.gz
+ # 3.7 curl -O https://gforge.inria.fr/frs/download.php/27641/CGAL-$version.tar.gz
+ fi
+ tar xzf CGAL-$version.tar.gz
+ cd CGAL-$version
+ cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DGMP_INCLUDE_DIR=$DEPLOYDIR/include -DGMP_LIBRARIES=$DEPLOYDIR/lib/libgmp.so -DGMPXX_LIBRARIES=$DEPLOYDIR/lib/libgmpxx.so -DGMPXX_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_INCLUDE_DIR=$DEPLOYDIR/include -DMPFR_LIBRARIES=$DEPLOYDIR/lib/libmpfr.so -DWITH_CGAL_Qt3=OFF -DWITH_CGAL_Qt4=OFF -DWITH_CGAL_ImageIO=OFF -DBOOST_ROOT=$DEPLOYDIR -DCMAKE_BUILD_TYPE=Debug
+ make -j$NUMCPU
+ make install
+}
+
+build_glew()
+{
+ version=$1
+ echo "Building GLEW" $version "..."
+ cd $BASEDIR/src
+ rm -rf glew-$version
+ if [ ! -f glew-$version.tgz ]; then
+ curl -LO http://downloads.sourceforge.net/project/glew/glew/$version/glew-$version.tgz
+ fi
+ tar xzf glew-$version.tgz
+ cd glew-$version
+ mkdir -p $DEPLOYDIR/lib/pkgconfig
+ GLEW_DEST=$DEPLOYDIR make -j$NUMCPU
+ GLEW_DEST=$DEPLOYDIR make install
+}
+
+build_opencsg()
+{
+ version=$1
+ echo "Building OpenCSG" $version "..."
+ cd $BASEDIR/src
+ rm -rf OpenCSG-$version
+ if [ ! -f OpenCSG-$version.tar.gz ]; then
+ curl -O http://www.opencsg.org/OpenCSG-$version.tar.gz
+ fi
+ tar xzf OpenCSG-$version.tar.gz
+ cd OpenCSG-$version
+ sed -i s/example// opencsg.pro # examples might be broken without GLUT
+ qmake-qt4
+ make
+ install -v lib/* $DEPLOYDIR/lib
+ install -v include/* $DEPLOYDIR/include
+}
+
+build_eigen()
+{
+ version=$1
+ echo "Building eigen" $version "..."
+ cd $BASEDIR/src
+ rm -rf eigen-$version
+ ## Directory name for v2.0.17
+ rm -rf eigen-eigen-b23437e61a07
+ if [ ! -f eigen-$version.tar.bz2 ]; then
+ curl -LO http://bitbucket.org/eigen/eigen/get/$version.tar.bz2
+ mv $version.tar.bz2 eigen-$version.tar.bz2
+ fi
+ tar xjf eigen-$version.tar.bz2
+ ## File name for v2.0.17
+ ln -s eigen-eigen-b23437e61a07 eigen-$version
+ cd eigen-$version
+ cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR
+ make -j$NUMCPU
+ make install
+}
+
+if [ ! -f $OPENSCADDIR/openscad.pro ]; then
+ echo "Must be run from the OpenSCAD source root directory"
+ exit 0
+fi
+
+if [ ! -d $BASEDIR/bin ]; then
+ mkdir --parents $BASEDIR/bin
+fi
+
+echo "Using basedir:" $BASEDIR
+echo "Using deploydir:" $DEPLOYDIR
+echo "Using srcdir:" $SRCDIR
+echo "Number of CPUs for parallel builds:" $NUMCPU
+mkdir -p $SRCDIR $DEPLOYDIR
+
+#build_curl 7.26.0
+# NB! For cmake, also update the actual download URL in the function
+#build_cmake 2.8.8
+build_eigen 2.0.17
+build_gmp 5.0.5
+build_mpfr 3.1.0
+build_boost 1.47.0
+# NB! For CGAL, also update the actual download URL in the function
+build_cgal 4.0
+build_glew 1.7.0
+build_opencsg 1.3.2
+
+echo "Now do this:"
+echo "export LD_LIBRARY_PATH=$DEPLOYDIR/lib:$DEPLOYDIR/lib64"
+echo "GLEWDIR=$DEPLOYDIR OPENSCAD_LIBRARIES=$DEPLOYDIR qmake-qt4"
+echo "make -j$NUMCPU"
+
diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h
index f93905f..0b0784e 100644
--- a/src/CGAL_Nef_polyhedron.h
+++ b/src/CGAL_Nef_polyhedron.h
@@ -3,6 +3,7 @@
#include "cgalfwd.h"
#include "memory.h"
+#include <string>
class CGAL_Nef_polyhedron
{
@@ -18,6 +19,7 @@ public:
CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron copy() const;
+ std::string dump_p2() const;
int weight() const;
class PolySet *convertToPolyset();
class DxfData *convertToDxfData() const;
diff --git a/src/CGAL_Nef_polyhedron_DxfData.cc b/src/CGAL_Nef_polyhedron_DxfData.cc
index fe58636..411a340 100644
--- a/src/CGAL_Nef_polyhedron_DxfData.cc
+++ b/src/CGAL_Nef_polyhedron_DxfData.cc
@@ -77,4 +77,29 @@ DxfData *CGAL_Nef_polyhedron::convertToDxfData() const
return dxfdata;
}
+// dump the 2 dimensional nef_poly.
+std::string CGAL_Nef_polyhedron::dump_p2() const
+{
+ std::stringstream out;
+ CGAL_Nef_polyhedron2::Explorer explorer = this->p2->explorer();
+ CGAL_Nef_polyhedron2::Explorer::Vertex_const_iterator i;
+ out << "CGAL_Nef_polyhedron::p2 Vertices";
+ for (i = explorer.vertices_begin(); i != explorer.vertices_end(); ++i) {
+ if ( explorer.is_standard( i ) ) {
+ CGAL_Nef_polyhedron2::Explorer::Point point = explorer.point( i );
+ out << "\n Point x y: "
+ << CGAL::to_double(point.x()) << " "
+ << CGAL::to_double(point.y());
+ } else {
+ CGAL_Nef_polyhedron2::Explorer::Ray ray = explorer.ray( i );
+ CGAL_Nef_polyhedron2::Explorer::Point point = ray.point( 0 );
+ out << "\n Ray x y dx dy: "
+ << CGAL::to_double(point.x()) << " " << CGAL::to_double(point.y()) << " "
+ << CGAL::to_double(ray.direction().dx()) << " " << CGAL::to_double(ray.direction().dy());
+ }
+ }
+ out << "\nCGAL_Nef_polyhedron::p2 Vertices end";
+ return out.str();
+}
+
#endif // ENABLE_CGAL
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index 1cc6b16..3b272d2 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -16,9 +16,88 @@
#include "openscad.h" // get_fragments_from_r()
#include <boost/foreach.hpp>
+/*
+
+This Visitor is used in the 'cut' process. Essentially, one or more of
+our 3d nef polyhedrons have been 'cut' by the xy-plane, forming a number
+of polygons that are essentially 'flat'. This Visitor object, when
+called repeatedly, collects those flat polygons together and forms a new
+2d nef polyhedron out of them. It keeps track of the 'holes' so that
+in the final resulting polygon, they are preserved properly.
+
+The output polygon is stored in the output_nefpoly2d variable.
+
+For more information on 3d + 2d nef polyhedrons, facets, halffacets,
+facet cycles, etc, please see these websites:
+
+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_polyhedron_3-Traits---Halffacet.html
+
+The halffacet iteration 'circulator' code is based on OGL_helper.h
+
+Why do we throw out all 'down' half-facets? Imagine a triangle in 3d
+space - it has one 'half face' for one 'side' and another 'half face' for the
+other 'side'. If we iterated over both half-faces we would get the same vertex
+coordinates twice. Instead, we only need one side, in our case, we
+chose the 'up' side.
+*/
+class NefShellVisitor_for_cut {
+public:
+ std::stringstream out;
+ CGAL_Nef_polyhedron2::Boundary boundary;
+ shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d;
+ shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d;
+ CGAL::Direction_3<CGAL_Kernel3> up;
+ bool debug;
+ NefShellVisitor_for_cut(bool debug=false)
+ {
+ output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
+ boundary = CGAL_Nef_polyhedron2::INCLUDED;
+ up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1);
+ this->debug = debug;
+ }
+ std::string dump()
+ {
+ return out.str();
+ }
+ 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 ) {
+ if ( hfacet->plane().orthogonal_direction() != this->up ) {
+ if (debug) out << "down facing half-facet. skipping\n";
+ return;
+ }
+
+ int numcontours = 0;
+ CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;
+ CGAL_forall_facet_cycles_of( i, hfacet ) {
+ CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);
+ std::list<CGAL_Nef_polyhedron2::Point> contour;
+ CGAL_For_all( c1, c2 ) {
+ CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->source()->point();
+ CGAL_Nef_polyhedron2::Point point2d( point3d.x(), point3d.y() );
+ contour.push_back( point2d );
+ }
+ tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
+ if ( numcontours == 0 ) {
+ if (debug) out << " contour is a body. make union(). " << contour.size() << " points.\n" ;
+ *output_nefpoly2d += *tmpnef2d;
+ } else {
+ if (debug) out << " contour is a hole. make intersection(). " << contour.size() << " points.\n";
+ *output_nefpoly2d *= *tmpnef2d;
+ }
+ numcontours++;
+ } // next facet cycle (i.e. next contour)
+ } // visit()
+};
+
PolySetCGALEvaluator::PolySetCGALEvaluator(CGALEvaluator &cgalevaluator)
: PolySetEvaluator(cgalevaluator.getTree()), cgalevaluator(cgalevaluator)
{
+ this->debug = false;
}
PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
@@ -34,80 +113,46 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
}
}
if (sum.empty()) return NULL;
+ if (!sum.p3->is_simple()) {
+ if (!node.cut_mode) {
+ PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
+ return new PolySet();
+ }
+ }
- PolySet *ps = new PolySet();
- ps->convexity = node.convexity;
- ps->is2d = true;
+ CGAL_Nef_polyhedron nef_poly;
- // In cut mode, the model is intersected by a large but very thin box living on the
- // XY plane.
if (node.cut_mode)
{
- PolySet cube;
- double infval = 1e8, eps = 0.1;
- double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps;
-
- cube.append_poly(); // top
- cube.append_vertex(x1, y1, z2);
- cube.append_vertex(x2, y1, z2);
- cube.append_vertex(x2, y2, z2);
- cube.append_vertex(x1, y2, z2);
-
- cube.append_poly(); // bottom
- cube.append_vertex(x1, y2, z1);
- cube.append_vertex(x2, y2, z1);
- cube.append_vertex(x2, y1, z1);
- cube.append_vertex(x1, y1, z1);
-
- cube.append_poly(); // side1
- cube.append_vertex(x1, y1, z1);
- cube.append_vertex(x2, y1, z1);
- cube.append_vertex(x2, y1, z2);
- cube.append_vertex(x1, y1, z2);
-
- cube.append_poly(); // side2
- cube.append_vertex(x2, y1, z1);
- cube.append_vertex(x2, y2, z1);
- cube.append_vertex(x2, y2, z2);
- cube.append_vertex(x2, y1, z2);
-
- cube.append_poly(); // side3
- cube.append_vertex(x2, y2, z1);
- cube.append_vertex(x1, y2, z1);
- cube.append_vertex(x1, y2, z2);
- cube.append_vertex(x2, y2, z2);
-
- cube.append_poly(); // side4
- cube.append_vertex(x1, y2, z1);
- cube.append_vertex(x1, y1, z1);
- cube.append_vertex(x1, y1, z2);
- cube.append_vertex(x1, y2, z2);
- CGAL_Nef_polyhedron Ncube = this->cgalevaluator.evaluateCGALMesh(cube);
-
- sum *= Ncube;
-
- // FIXME: Instead of intersecting with a thin volume, we could intersect
- // with a plane. This feels like a better solution. However, as the result
- // of such an intersection isn't simple, we cannot convert the resulting
- // Nef polyhedron to a Polyhedron using convertToPolyset() and we need
- // another way of extracting the result. kintel 20120203.
-// *sum.p3 = sum.p3->intersection(CGAL_Nef_polyhedron3::Plane_3(0, 0, 1, 0),
-// CGAL_Nef_polyhedron3::PLANE_ONLY);
-
-
- if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
+ // intersect 'sum' with the x-y plane
+ CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
+ *sum.p3 = sum.p3->intersection( xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
+
+ // Visit each polygon in sum.p3 and union/intersect into a 2d polygon (with holes)
+ // For info on Volumes, Shells, Facets, and the 'visitor' pattern, please see
+ // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+ NefShellVisitor_for_cut shell_visitor;
+ 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 = sum.p3->volumes_begin(); i != sum.p3->volumes_end(); ++i ) {
+ for ( j = i->shells_begin(); j != i->shells_end(); ++j ) {
+ sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j );
+ sum.p3->visit_shell_objects( sface_handle , shell_visitor );
+ }
}
+ if (debug) std::cout << "shell visitor\n" << shell_visitor.dump() << "\nshell visitor end\n";
- PolySet *ps3 = sum.convertToPolyset();
- if (!ps3) return NULL;
+ nef_poly.p2 = shell_visitor.output_nefpoly2d;
+ nef_poly.dim = 2;
+ if (debug) std::cout << "--\n" << nef_poly.dump_p2() << "\n";
// Extract polygons in the XY plane, ignoring all other polygons
- // FIXME: If the polyhedron is really thin, there might be unwanted polygons
- // in the XY plane, causing the resulting 2D polygon to be self-intersection
- // and cause a crash in CGALEvaluator::PolyReducer. The right solution is to
- // filter these polygons here. kintel 20120203.
+ // FIXME: If the polyhedron is really thin, there might be unwanted polygons
+ // in the XY plane, causing the resulting 2D polygon to be self-intersection
+ // and cause a crash in CGALEvaluator::PolyReducer. The right solution is to
+ // filter these polygons here. kintel 20120203.
+ /*
Grid2d<unsigned int> conversion_grid(GRID_COARSE);
for (size_t i = 0; i < ps3->polygons.size(); i++) {
for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
@@ -129,19 +174,13 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
}
next_ps3_polygon_cut_mode:;
}
- delete ps3;
+ */
}
// In projection mode all the triangles are projected manually into the XY plane
else
{
- if (!sum.p3->is_simple()) {
- PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
PolySet *ps3 = sum.convertToPolyset();
if (!ps3) return NULL;
- CGAL_Nef_polyhedron np;
for (size_t i = 0; i < ps3->polygons.size(); i++)
{
int min_x_p = -1;
@@ -180,22 +219,22 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
plist.push_back(p);
}
// FIXME: Should the CGAL_Nef_polyhedron2 be cached?
- if (np.empty()) {
- np.dim = 2;
- np.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
+ if (nef_poly.empty()) {
+ nef_poly.dim = 2;
+ nef_poly.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
}
else {
- (*np.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ (*nef_poly.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
}
delete ps3;
- DxfData *dxf = np.convertToDxfData();
- dxf_tesselate(ps, *dxf, 0, true, false, 0);
- dxf_border_to_ps(ps, *dxf);
- delete dxf;
}
-cant_project_non_simple_polyhedron:
+ PolySet *ps = nef_poly.convertToPolyset();
+ assert( ps != NULL );
+ ps->convexity = node.convexity;
+ if (debug) std::cout << "--\n" << ps->dump() << "\n";
+
return ps;
}
diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h
index dddcfc5..00e79f1 100644
--- a/src/PolySetCGALEvaluator.h
+++ b/src/PolySetCGALEvaluator.h
@@ -17,7 +17,7 @@ public:
virtual PolySet *evaluatePolySet(const RotateExtrudeNode &node);
virtual PolySet *evaluatePolySet(const CgaladvNode &node);
virtual PolySet *evaluatePolySet(const RenderNode &node);
-
+ bool debug;
protected:
PolySet *extrudeDxfData(const LinearExtrudeNode &node, class DxfData &dxf);
PolySet *rotateDxfData(const RotateExtrudeNode &node, class DxfData &dxf);
diff --git a/src/context.cc b/src/context.cc
index 2decc48..f48cd86 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -32,7 +32,7 @@
#include "printutils.h"
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
#include "boosty.h"
std::vector<const Context*> Context::ctx_stack;
@@ -179,8 +179,8 @@ AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
*/
std::string Context::getAbsolutePath(const std::string &filename) const
{
- if (!filename.empty()) {
- return boosty::absolute(path(this->document_path) / filename).string();
+ if (!filename.empty() && !boosty::is_absolute(fs::path(filename))) {
+ return boosty::absolute(fs::path(this->document_path) / filename).string();
}
else {
return filename;
diff --git a/src/dxfdata.cc b/src/dxfdata.cc
index 2b84f7e..00b246f 100644
--- a/src/dxfdata.cc
+++ b/src/dxfdata.cc
@@ -38,6 +38,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <algorithm>
+#include <sstream>
#include <QDir>
#include "value.h"
@@ -555,3 +556,27 @@ int DxfData::addPoint(double x, double y)
return this->points.size()-1;
}
+std::string DxfData::dump() const
+{
+ std::stringstream out;
+ out << "DxfData"
+ << "\n num points: " << points.size()
+ << "\n num paths: " << paths.size()
+ << "\n num dims: " << dims.size()
+ << "\n points: ";
+ for ( size_t k = 0 ; k < points.size() ; k++ ) {
+ out << "\n x y: " << points[k].transpose();
+ }
+ out << "\n paths: ";
+ for ( size_t i = 0; i < paths.size(); i++ ) {
+ out << "\n path:" << i
+ << "\n is_closed: " << paths[i].is_closed
+ << "\n is_inner: " << paths[i].is_inner ;
+ DxfData::Path path = paths[i];
+ for ( size_t j = 0; j < path.indices.size(); j++ ) {
+ out << "\n index[" << j << "]==" << path.indices[j];
+ }
+ }
+ out << "\nDxfData end";
+ return out.str();
+}
diff --git a/src/dxfdata.h b/src/dxfdata.h
index 7eea6a9..64853dc 100644
--- a/src/dxfdata.h
+++ b/src/dxfdata.h
@@ -44,6 +44,7 @@ public:
int addPoint(double x, double y);
void fixup_path_direction();
+ std::string dump() const;
};
#endif
diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc
index d1e79ad..f221e3a 100644
--- a/src/dxftess-cgal.cc
+++ b/src/dxftess-cgal.cc
@@ -131,6 +131,7 @@ void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool /* do_tr
// ..maybe it would be better to assert here. But this would
// break compatibility with the glu tesselator that handled such
// cases just fine.
+ PRINT( "WARNING: Duplicate vertex found during Tessellation. Render may be incorrect." );
continue;
}
diff --git a/src/glview.cc b/src/glview.cc
index aa2e746..bea5856 100644
--- a/src/glview.cc
+++ b/src/glview.cc
@@ -600,7 +600,11 @@ void GLView::mouseMoveEvent(QMouseEvent *event)
double mz = -(dy) * viewer_distance/1000;
double my = 0;
+#if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0))
+ if (event->buttons() & Qt::MidButton) {
+#else
if (event->buttons() & Qt::MiddleButton) {
+#endif
my = mz;
mz = 0;
// actually lock the x-position
diff --git a/src/polyset.cc b/src/polyset.cc
index e5553aa..b412f5f 100644
--- a/src/polyset.cc
+++ b/src/polyset.cc
@@ -48,6 +48,36 @@ PolySet::~PolySet()
{
}
+std::string PolySet::dump() const
+{
+ std::stringstream out;
+ out << "PolySet:"
+ << "\n dimensions:" << std::string( this->is2d ? "2" : "3" )
+ << "\n convexity:" << this->convexity
+ << "\n num polygons: " << polygons.size()
+ << "\n num borders: " << borders.size()
+ << "\n polygons data:";
+ for (size_t i = 0; i < polygons.size(); i++) {
+ out << "\n polygon begin:";
+ const Polygon *poly = &polygons[i];
+ for (size_t j = 0; j < poly->size(); j++) {
+ Vector3d v = poly->at(j);
+ out << "\n vertex:" << v.transpose();
+ }
+ }
+ out << "\n borders data:";
+ for (size_t i = 0; i < borders.size(); i++) {
+ out << "\n border polygon begin:";
+ const Polygon *poly = &borders[i];
+ for (size_t j = 0; j < poly->size(); j++) {
+ Vector3d v = poly->at(j);
+ out << "\n vertex:" << v.transpose();
+ }
+ }
+ out << "\nPolySet end";
+ return out.str();
+}
+
void PolySet::append_poly()
{
polygons.push_back(Polygon());
diff --git a/src/polyset.h b/src/polyset.h
index 09a13cb..4ca57bf 100644
--- a/src/polyset.h
+++ b/src/polyset.h
@@ -5,6 +5,7 @@
#include "grid.h"
#include "linalg.h"
#include <vector>
+#include <string>
class PolySet
{
@@ -40,6 +41,7 @@ public:
void render_surface(csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const;
void render_edges(csgmode_e csgmode) const;
+ std::string dump() const;
};
#endif
diff --git a/testdata/scad/features/import_dxf-tests.scad b/testdata/scad/features/import_dxf-tests.scad
index 736f26e..50a5416 100644
--- a/testdata/scad/features/import_dxf-tests.scad
+++ b/testdata/scad/features/import_dxf-tests.scad
@@ -7,3 +7,4 @@ translate([-200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="0");
translate([0,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="0");
translate([200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="noname");
translate([0,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="Layer with a pretty long name including \\ \"special\" /'\\\\ characters");
+translate([200,0,0]) import(file="/Users/kintel/code/OpenSCAD/openscad/tests/../testdata/dxf/polygons.dxf");
diff --git a/testdata/scad/features/import_stl-tests.scad b/testdata/scad/features/import_stl-tests.scad
index 7104078..af42e8d 100644
--- a/testdata/scad/features/import_stl-tests.scad
+++ b/testdata/scad/features/import_stl-tests.scad
@@ -1,3 +1,4 @@
import_stl("import.stl");
translate([2,0,0]) import("import.stl");
translate([4,0,0]) import("import_bin.stl");
+translate([0,2,0]) import("/Users/kintel/code/OpenSCAD/openscad/tests/../testdata/scad/features/import.stl");
diff --git a/testdata/scad/features/projection-tests.scad b/testdata/scad/features/projection-tests.scad
index edb65ba..e6c52ea 100644
--- a/testdata/scad/features/projection-tests.scad
+++ b/testdata/scad/features/projection-tests.scad
@@ -11,3 +11,10 @@ translate([44,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,
// Boundary case: clipping the top of a cube
translate([0,-22,0]) linear_extrude(height=5) projection(cut=true) translate([0,0,-4.999999]) cube(10, center=true);
+
+// holes
+translate([0,-44,0]) linear_extrude(height=5) projection(cut=true)
+ union() {
+ difference() { cube(5,center=true); cube(4,center=true); }
+ translate([2.1,2.1]) difference() { cube(5,center=true); cube(4,center=true); }
+ }
diff --git a/testdata/scad/templates/import_dxf-tests-template.scad b/testdata/scad/templates/import_dxf-tests-template.scad
new file mode 100644
index 0000000..f10dd06
--- /dev/null
+++ b/testdata/scad/templates/import_dxf-tests-template.scad
@@ -0,0 +1,10 @@
+import();
+translate([-210,0,0]) import(file="../../dxf/polygons.dxf");
+translate([-210,0,0]) import(file="../../dxf/polygons.dxf", origin=[0,110]);
+translate([-210,0,0]) import(file="../../dxf/polygons.dxf", origin=[110,110], scale=0.5);
+import(file="../../dxf/multiple-layers.dxf");
+translate([-200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="0");
+translate([0,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="0");
+translate([200,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="noname");
+translate([0,200,0]) import(file="../../dxf/multiple-layers.dxf", layer="Layer with a pretty long name including \\ \"special\" /'\\\\ characters");
+translate([200,0,0]) import(file="@CMAKE_SOURCE_DIR@/../testdata/dxf/polygons.dxf");
diff --git a/testdata/scad/templates/import_stl-tests-template.scad b/testdata/scad/templates/import_stl-tests-template.scad
new file mode 100644
index 0000000..685b868
--- /dev/null
+++ b/testdata/scad/templates/import_stl-tests-template.scad
@@ -0,0 +1,4 @@
+import_stl("import.stl");
+translate([2,0,0]) import("import.stl");
+translate([4,0,0]) import("import_bin.stl");
+translate([0,2,0]) import("@CMAKE_SOURCE_DIR@/../testdata/scad/features/import.stl");
diff --git a/testdata/scad/misc/include-tests-template.scad b/testdata/scad/templates/include-tests-template.scad
index 43bda57..43bda57 100644
--- a/testdata/scad/misc/include-tests-template.scad
+++ b/testdata/scad/templates/include-tests-template.scad
diff --git a/testdata/scad/misc/use-tests-template.scad b/testdata/scad/templates/use-tests-template.scad
index 24591f8..24591f8 100644
--- a/testdata/scad/misc/use-tests-template.scad
+++ b/testdata/scad/templates/use-tests-template.scad
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 12b8543..5ec8be7 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -237,7 +237,7 @@ if (NOT GLEW_INCLUDE_DIR)
NO_DEFAULT_PATH)
find_library(GLEW_LIBRARY
NAMES GLEW glew
- HINTS ${GLEW_DIR}/lib
+ HINTS ${GLEW_DIR}/lib ${GLEW_DIR}/lib64
NO_DEFAULT_PATH)
if (NOT GLEW_LIBRARY)
find_package(GLEW REQUIRED)
@@ -611,10 +611,14 @@ endforeach()
set_directory_properties(PROPERTIES TEST_INCLUDE_FILE "${CMAKE_SOURCE_DIR}/EnforceConfig.cmake")
# Subst files
-configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests-template.scad
+configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/templates/include-tests-template.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad)
-configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests-template.scad
+configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/templates/use-tests-template.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad)
+configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/templates/import_stl-tests-template.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/import_stl-tests.scad)
+configure_file(${CMAKE_SOURCE_DIR}/../testdata/scad/templates/import_dxf-tests-template.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/features/import_dxf-tests.scad)
# Find all scad files
file(GLOB MINIMAL_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/*.scad)
diff --git a/tests/regression/cgalpngtest/import_dxf-tests-expected.png b/tests/regression/cgalpngtest/import_dxf-tests-expected.png
index 7c8a63e..f885b09 100644
--- a/tests/regression/cgalpngtest/import_dxf-tests-expected.png
+++ b/tests/regression/cgalpngtest/import_dxf-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/import_stl-tests-expected.png b/tests/regression/cgalpngtest/import_stl-tests-expected.png
index 08aa225..de7638a 100644
--- a/tests/regression/cgalpngtest/import_stl-tests-expected.png
+++ b/tests/regression/cgalpngtest/import_stl-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png
index 800f7ba..2610507 100644
--- a/tests/regression/cgalpngtest/projection-tests-expected.png
+++ b/tests/regression/cgalpngtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/dumptest/import_dxf-tests-expected.txt b/tests/regression/dumptest/import_dxf-tests-expected.txt
index 977efcc..d98b3e2 100644
--- a/tests/regression/dumptest/import_dxf-tests-expected.txt
+++ b/tests/regression/dumptest/import_dxf-tests-expected.txt
@@ -21,4 +21,7 @@
multmatrix([[1, 0, 0, 0], [0, 1, 0, 200], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "../../dxf/multiple-layers.dxf", layer = "Layer with a pretty long name including \\ \"special\" /'\\\\ characters", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
}
+ multmatrix([[1, 0, 0, 200], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ import(file = "../../dxf/polygons.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
diff --git a/tests/regression/dumptest/import_stl-tests-expected.txt b/tests/regression/dumptest/import_stl-tests-expected.txt
index 648a207..947f137 100644
--- a/tests/regression/dumptest/import_stl-tests-expected.txt
+++ b/tests/regression/dumptest/import_stl-tests-expected.txt
@@ -5,4 +5,7 @@
multmatrix([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "import_bin.stl", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
}
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ import(file = "import.stl", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
diff --git a/tests/regression/dumptest/projection-tests-expected.txt b/tests/regression/dumptest/projection-tests-expected.txt
index 77fdbb4..69cd4f6 100644
--- a/tests/regression/dumptest/projection-tests-expected.txt
+++ b/tests/regression/dumptest/projection-tests-expected.txt
@@ -35,4 +35,22 @@
}
}
}
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, -44], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(height = 5, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ projection(cut = true, convexity = 0) {
+ union() {
+ difference() {
+ cube(size = [5, 5, 5], center = true);
+ cube(size = [4, 4, 4], center = true);
+ }
+ multmatrix([[1, 0, 0, 2.1], [0, 1, 0, 2.1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ difference() {
+ cube(size = [5, 5, 5], center = true);
+ cube(size = [4, 4, 4], center = true);
+ }
+ }
+ }
+ }
+ }
+ }
diff --git a/tests/regression/opencsgtest/import_dxf-tests-expected.png b/tests/regression/opencsgtest/import_dxf-tests-expected.png
index 6e568c7..010a222 100644
--- a/tests/regression/opencsgtest/import_dxf-tests-expected.png
+++ b/tests/regression/opencsgtest/import_dxf-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/import_stl-tests-expected.png b/tests/regression/opencsgtest/import_stl-tests-expected.png
index 19e233a..0bd9ab6 100644
--- a/tests/regression/opencsgtest/import_stl-tests-expected.png
+++ b/tests/regression/opencsgtest/import_stl-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png
index 8239d3d..9aabe36 100644
--- a/tests/regression/opencsgtest/projection-tests-expected.png
+++ b/tests/regression/opencsgtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/import_dxf-tests-expected.png b/tests/regression/throwntogethertest/import_dxf-tests-expected.png
index e8173d9..f5ef9fc 100644
--- a/tests/regression/throwntogethertest/import_dxf-tests-expected.png
+++ b/tests/regression/throwntogethertest/import_dxf-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/import_stl-tests-expected.png b/tests/regression/throwntogethertest/import_stl-tests-expected.png
index 19e233a..0bd9ab6 100644
--- a/tests/regression/throwntogethertest/import_stl-tests-expected.png
+++ b/tests/regression/throwntogethertest/import_stl-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png
index 7bcb888..3be3ae0 100644
--- a/tests/regression/throwntogethertest/projection-tests-expected.png
+++ b/tests/regression/throwntogethertest/projection-tests-expected.png
Binary files differ
contact: Jan Huwald // Impressum