Merge branch 'master' of github.com:openscad/openscad into msys2
This commit is contained in:
commit
5e93aa14af
29 changed files with 1298 additions and 1167 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -37,6 +37,7 @@ testdata/scad/misc/use-tests.scad
|
|||
/lexer_lex.cpp
|
||||
/parser_yacc.cpp
|
||||
/OpenSCAD.app
|
||||
/openscad.appdata.xml
|
||||
/openscad.pro.user
|
||||
/openscad
|
||||
/locale/*/*/*.mo
|
||||
|
|
|
|||
|
|
@ -231,9 +231,9 @@ For a 64-bit Windows cross-build, replace 32 with 64 in the above instructions.
|
|||
|
||||
### 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
|
||||
'openscad' binary (OpenSCAD.app on Mac OS X) to the bin directory of your choice.
|
||||
|
|
|
|||
9
contrib/appdata.its
Normal file
9
contrib/appdata.its
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!-- Copyright 2013 Richard Hughes <richard@hughsie.com> -->
|
||||
<its:rules
|
||||
xmlns:its="http://www.w3.org/2005/11/its"
|
||||
version="1.0">
|
||||
<its:translateRule translate="no" selector="/component"/>
|
||||
<its:translateRule translate="yes"
|
||||
selector="/component/summary |
|
||||
/component/description"/>
|
||||
</its:rules>
|
||||
|
|
@ -50,6 +50,7 @@ files.
|
|||
$ make clean && qmake && make
|
||||
|
||||
Then run the script to scan the source files, and regenerate .pot & .po files.
|
||||
You'll need itstool (http://itstool.org/) installed.
|
||||
|
||||
$ ./scripts/translation-update.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<application>
|
||||
<id type="desktop">openscad.desktop</id>
|
||||
<licence>CC0</licence>
|
||||
<component type="desktop">
|
||||
<id>openscad.desktop</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-2.0+ and CC0-1.0</project_license>
|
||||
<name>OpenSCAD</name>
|
||||
<summary>The Programmers Solid 3D CAD Modeller</summary>
|
||||
<description>
|
||||
<p>OpenSCAD is a software for creating solid 3D CAD models. Unlike most free software for creating 3D models (such as Blender) it does not focus on the artistic aspects of 3D modelling but instead on the CAD aspects. Thus it might be the application you are looking for when you are planning to create 3D models of machine parts but pretty sure is not what you are looking for when you are more interested in creating computer-animated movies.</p>
|
||||
|
|
@ -9,8 +11,12 @@
|
|||
<p>OpenSCAD provides two main modelling techniques: First there is constructive solid geometry (aka CSG) and second there is extrusion of 2D outlines. As data exchange format format for this 2D outlines Autocad DXF files are used. In addition to 2D paths for extrusion it is also possible to read design parameters from DXF files. Besides DXF files OpenSCAD can read and create 3D models in the STL and OFF file formats.</p>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default" width="800" height="437">http://www.openscad.org/images/appdata-screenshot-1.png</screenshot>
|
||||
<screenshot width="800" height="465">http://www.openscad.org/images/appdata-screenshot-2.png</screenshot>
|
||||
<screenshot type="default">
|
||||
<image>http://www.openscad.org/images/appdata-screenshot-1.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>http://www.openscad.org/images/appdata-screenshot-2.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">http://www.openscad.org/</url>
|
||||
</application>
|
||||
</component>
|
||||
10
openscad.pro
10
openscad.pro
|
|
@ -188,13 +188,19 @@ CONFIG += fontconfig
|
|||
CONFIG += gettext
|
||||
|
||||
#Uncomment the following line to enable the QScintilla editor
|
||||
CONFIG += scintilla
|
||||
!nogui {
|
||||
CONFIG += scintilla
|
||||
}
|
||||
|
||||
# Make experimental features available
|
||||
experimental {
|
||||
DEFINES += ENABLE_EXPERIMENTAL
|
||||
}
|
||||
|
||||
nogui {
|
||||
DEFINES += OPENSCAD_NOGUI
|
||||
}
|
||||
|
||||
mdi {
|
||||
DEFINES += ENABLE_MDI
|
||||
}
|
||||
|
|
@ -482,6 +488,8 @@ HEADERS += src/cgal.h \
|
|||
src/Polygon2d-CGAL.h
|
||||
|
||||
SOURCES += src/cgalutils.cc \
|
||||
src/cgalutils-applyops.cc \
|
||||
src/cgalutils-project.cc \
|
||||
src/cgalutils-tess.cc \
|
||||
src/cgalutils-polyhedron.cc \
|
||||
src/CGALCache.cc \
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ updatepot()
|
|||
| awk '{ printf "#: examples/examples.json:%d\nmsgid %s\nmsgstr \"\"\n\n", $1, $2 }' \
|
||||
> ./locale/json-strings.pot
|
||||
|
||||
# extract strings from appdata file
|
||||
itstool -o ./locale/appdata-strings.pot ./openscad.appdata.xml.in --its=./contrib/appdata.its
|
||||
|
||||
VER=`date +"%Y.%m.%d"`
|
||||
OPTS=
|
||||
OPTS=$OPTS' --package-name=OpenSCAD'
|
||||
|
|
@ -41,7 +44,7 @@ updatepot()
|
|||
exit 1
|
||||
fi
|
||||
|
||||
cmd="${GETTEXT_PATH}msgcat -o ./locale/openscad.pot ./locale/openscad-tmp.pot ./locale/json-strings.pot"
|
||||
cmd="${GETTEXT_PATH}msgcat -o ./locale/openscad.pot ./locale/openscad-tmp.pot ./locale/json-strings.pot ./locale/appdata-strings.pot"
|
||||
echo $cmd
|
||||
$cmd
|
||||
if [ ! $? = 0 ]; then
|
||||
|
|
@ -50,7 +53,7 @@ updatepot()
|
|||
fi
|
||||
|
||||
sed -e s/"CHARSET"/"UTF-8"/g ./locale/openscad.pot > ./locale/openscad.pot.new && mv ./locale/openscad.pot.new ./locale/openscad.pot
|
||||
rm -f ./locale/json-strings.pot ./locale/openscad-tmp.pot
|
||||
rm -f ./locale/json-strings.pot ./locale/openscad-tmp.pot ./locale/appdata-strings.pot
|
||||
}
|
||||
|
||||
updatepo()
|
||||
|
|
@ -80,6 +83,26 @@ updatemo()
|
|||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if which itstool > /dev/null 2>&1; then
|
||||
# ugly workaround for bug https://bugs.freedesktop.org/show_bug.cgi?id=90937
|
||||
for LANGCODE in `cat locale/LINGUAS | grep -v "#"`; do
|
||||
ln -s openscad.mo ./locale/$LANGCODE/LC_MESSAGES/$LANGCODE.mo
|
||||
done
|
||||
|
||||
# generate translated appdata file
|
||||
itstool -j ./openscad.appdata.xml.in -o ./openscad.appdata.xml ./locale/*/LC_MESSAGES/[a-z][a-z].mo
|
||||
|
||||
# clean the mess
|
||||
for LANGCODE in `cat locale/LINGUAS | grep -v "#"`; do
|
||||
unlink ./locale/$LANGCODE/LC_MESSAGES/$LANGCODE.mo
|
||||
done
|
||||
else
|
||||
if [ x"$(uname -s)" = x"Linux" ]; then
|
||||
echo "itstool missing, won't apply translations to openscad.appdata.xml"
|
||||
fi
|
||||
cp -f ./openscad.appdata.xml.in ./openscad.appdata.xml
|
||||
fi
|
||||
}
|
||||
|
||||
GETTEXT_PATH=""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
qmake CONFIG+=experimental CONFIG+=nogui
|
||||
make
|
||||
|
||||
cd tests
|
||||
cmake .
|
||||
if [[ $? != 0 ]]; then
|
||||
|
|
|
|||
|
|
@ -79,10 +79,22 @@ check_env()
|
|||
detect_glu()
|
||||
{
|
||||
detect_glu_result=
|
||||
if [ -e $DEPLOYDIR/include/GL/glu.h ]; then detect_glu_result=1; fi
|
||||
if [ -e /usr/include/GL/glu.h ]; then detect_glu_result=1; fi
|
||||
if [ -e /usr/local/include/GL/glu.h ]; then detect_glu_result=1; fi
|
||||
if [ -e /usr/pkg/X11R7/include/GL/glu.h ]; then detect_glu_result=1; fi
|
||||
if [ -e $DEPLOYDIR/include/GL/glu.h ]; then
|
||||
detect_glu_include=$DEPLOYDIR/include
|
||||
detect_glu_result=1;
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -505,10 +517,20 @@ build_opencsg()
|
|||
cp opencsg.pro opencsg.pro.bak
|
||||
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
|
||||
OPENCSG_QMAKE=qmake-qt4
|
||||
elif [ "`command -v qmake4`" ]; then
|
||||
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
|
||||
OPENCSG_QMAKE=qmake
|
||||
else
|
||||
|
|
@ -518,15 +540,18 @@ build_opencsg()
|
|||
cp src/Makefile src/Makefile.bak
|
||||
|
||||
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
|
||||
cat src/Makefile.bak2 | sed s@^LIBS.*@LIBS\ =\ -L$BASEDIR/lib\ -L/usr/X11R6/lib\ -lGLU\ -lGL@ > src/Makefile
|
||||
tmp=$version
|
||||
detect_glu
|
||||
if [ ! $detect_glu_result ]; then build_glu 9.0.0 ; fi
|
||||
version=$tmp
|
||||
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
|
||||
$OPENCSG_QMAKE
|
||||
|
||||
|
|
@ -776,6 +801,11 @@ if [ $1 ]; then
|
|||
build_gettext 0.18.3.1
|
||||
exit $?
|
||||
fi
|
||||
if [ $1 = "harfbuzz" ]; then
|
||||
# debian 7 lacks only harfbuzz
|
||||
build_harfbuzz 0.9.23 --with-glib=yes
|
||||
exit $?
|
||||
fi
|
||||
if [ $1 = "glib2" ]; then
|
||||
# such a huge build, put here by itself
|
||||
build_pkgconfig 0.28
|
||||
|
|
|
|||
|
|
@ -4,13 +4,24 @@
|
|||
# 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 \
|
||||
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 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()
|
||||
|
|
@ -43,8 +54,13 @@ get_netbsd_deps()
|
|||
get_opensuse_deps()
|
||||
{
|
||||
zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
|
||||
libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \
|
||||
glib2-devel gettext
|
||||
libqt4-devel glew-devel cmake git bison flex cgal-devel curl \
|
||||
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()
|
||||
|
|
@ -58,12 +74,30 @@ get_mageia_deps()
|
|||
get_debian_deps()
|
||||
{
|
||||
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 \
|
||||
libXi-dev libmpfr-dev libboost-dev libglew-dev \
|
||||
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-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
|
||||
}
|
||||
|
||||
get_msys2_x86_64_deps()
|
||||
|
|
@ -116,62 +150,3 @@ unknown()
|
|||
echo "in README.md using your system's package manager."
|
||||
}
|
||||
|
||||
|
||||
try_using_etc_issue()
|
||||
{
|
||||
if [ "`grep -i ubuntu /etc/issue`" ]; then
|
||||
get_debian_deps
|
||||
elif [ "`grep -i debian /etc/issue`" ]; then
|
||||
get_debian_deps
|
||||
elif [ "`grep -i raspbian /etc/issue`" ]; then
|
||||
get_debian_deps
|
||||
elif [ "`grep -i mint /etc/issue`" ]; then
|
||||
get_debian_deps
|
||||
elif [ "`grep -i suse /etc/issue`" ]; then
|
||||
get_opensuse_deps
|
||||
elif [ "`grep -i fedora /etc/issue`" ]; then
|
||||
get_fedora_deps
|
||||
elif [ "`grep -i red.hat /etc/issue`" ]; then
|
||||
get_fedora_deps
|
||||
elif [ "`grep -i mageia /etc/issue`" ]; then
|
||||
get_mageia_deps
|
||||
elif [ "`grep -i qomo /etc/issue`" ]; then
|
||||
get_qomo_deps
|
||||
else
|
||||
unknown
|
||||
fi
|
||||
}
|
||||
|
||||
try_using_uname()
|
||||
{
|
||||
if [ "`uname -a | grep -i x86_64.*Msys`" ]; then
|
||||
get_msys2_x86_64_deps
|
||||
elif [ "`uname -a | grep -i i686.*Msys`" ]; then
|
||||
get_msys2_i686_deps
|
||||
elif [ "`uname | grep -i freebsd `" ]; then
|
||||
get_freebsd_deps
|
||||
elif [ "`uname | grep -i netbsd`" ]; then
|
||||
get_netbsd_deps
|
||||
else
|
||||
unknown
|
||||
fi
|
||||
}
|
||||
|
||||
try_using_rpm()
|
||||
{
|
||||
if [ "`rpm -qa | grep altlinux`" ]; then
|
||||
get_altlinux_deps
|
||||
else
|
||||
unknown
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -e /etc/issue ]; then
|
||||
try_using_etc_issue
|
||||
elif [ "`command -v uname`" ]; then
|
||||
try_using_uname
|
||||
elif [ "`command -v rpm`" ]; then
|
||||
try_using_uname
|
||||
else
|
||||
unknown
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <QMutex>
|
||||
#include <QSet>
|
||||
#include <QTime>
|
||||
#include <QIODevice>
|
||||
|
||||
enum export_type_e {
|
||||
EXPORT_TYPE_UNKNOWN,
|
||||
|
|
@ -113,6 +114,7 @@ private:
|
|||
void compile(bool reload, bool forcedone = false);
|
||||
void compileCSG(bool procevents);
|
||||
bool maybeSave();
|
||||
void saveError(const QIODevice &file, const std::string &msg);
|
||||
bool checkEditorModified();
|
||||
QString dumpCSGTree(AbstractNode *root);
|
||||
static void consoleOutput(const std::string &msg, void *userdata);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@ std::string PlatformUtils::userLibraryPath()
|
|||
try {
|
||||
std::string pathstr = PlatformUtils::documentsPath();
|
||||
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("lib path found: [%s]", path );
|
||||
if (path.empty()) return "";
|
||||
|
|
@ -134,7 +136,9 @@ std::string PlatformUtils::backupPath()
|
|||
try {
|
||||
std::string pathstr = PlatformUtils::documentsPath();
|
||||
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 "";
|
||||
path /= OPENSCAD_FOLDER_NAME;
|
||||
path /= "backups";
|
||||
|
|
|
|||
422
src/cgalutils-applyops.cc
Normal file
422
src/cgalutils-applyops.cc
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -11,13 +11,8 @@
|
|||
|
||||
#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
|
||||
namespace /* anonymous */ {
|
||||
|
||||
template <typename Polyhedron>
|
||||
class CGAL_Build_PolySet : public CGAL::Modifier_base<typename Polyhedron::HalfedgeDS>
|
||||
|
|
|
|||
266
src/cgalutils-project.cc
Normal file
266
src/cgalutils-project.cc
Normal 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
|
||||
1320
src/cgalutils.cc
1320
src/cgalutils.cc
File diff suppressed because it is too large
Load diff
|
|
@ -11,6 +11,13 @@ typedef CGAL::Point_3<K> Vertex3K;
|
|||
typedef std::vector<Vertex3K> PolygonK;
|
||||
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 {
|
||||
bool applyHull(const Geometry::ChildList &children, PolySet &P);
|
||||
CGAL_Nef_polyhedron *applyOperator(const Geometry::ChildList &children, OpenSCADOperator op);
|
||||
|
|
|
|||
|
|
@ -319,10 +319,7 @@ std::string ImportNode::toString() const
|
|||
"scale = " << this->scale << ", "
|
||||
"convexity = " << this->convexity << ", "
|
||||
"$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs
|
||||
#ifndef OPENSCAD_TESTING
|
||||
// timestamp is needed for caching, but disturbs the test framework
|
||||
<< ", " "timestamp = " << (fs::exists(path) ? fs::last_write_time(path) : 0)
|
||||
#endif
|
||||
<< ")";
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -142,10 +142,7 @@ std::string LinearExtrudeNode::toString() const
|
|||
"file = " << this->filename << ", "
|
||||
"layer = " << QuotedString(this->layername) << ", "
|
||||
"origin = [" << this->origin_x << ", " << this->origin_y << "], "
|
||||
#ifndef OPENSCAD_TESTING
|
||||
// timestamp is needed for caching, but disturbs the test framework
|
||||
<< "timestamp = " << (fs::exists(path) ? fs::last_write_time(path) : 0) << ", "
|
||||
#endif
|
||||
;
|
||||
}
|
||||
stream <<
|
||||
|
|
|
|||
|
|
@ -93,6 +93,16 @@
|
|||
#include <QClipboard>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
|
||||
// Set dummy for Qt versions that do not have QSaveFile
|
||||
#define QT_FILE_SAVE_CLASS QFile
|
||||
#define QT_FILE_SAVE_COMMIT true
|
||||
#else
|
||||
#include <QSaveFile>
|
||||
#define QT_FILE_SAVE_CLASS QSaveFile
|
||||
#define QT_FILE_SAVE_COMMIT if (saveOk) { saveOk = file.commit(); } else { file.cancelWriting(); }
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -1322,6 +1332,16 @@ void MainWindow::saveBackup()
|
|||
return writeBackup(this->tempFile);
|
||||
}
|
||||
|
||||
void MainWindow::saveError(const QIODevice &file, const std::string &msg) {
|
||||
const std::string messageFormat = msg + " %s (%s)";
|
||||
const char *fileName = this->fileName.toLocal8Bit().constData();
|
||||
PRINTB(messageFormat.c_str(), fileName % file.errorString().toLocal8Bit().constData());
|
||||
|
||||
const std::string dialogFormatStr = msg + "\n\"%1\"\n(%2)";
|
||||
const QString dialogFormat(dialogFormatStr.c_str());
|
||||
QMessageBox::warning(this, windowTitle(), dialogFormat.arg(this->fileName).arg(file.errorString()));
|
||||
}
|
||||
|
||||
/*!
|
||||
Save current document.
|
||||
Should _always_ write to disk, since this is called by SaveAs - i.e. don't try to be
|
||||
|
|
@ -1331,26 +1351,37 @@ void MainWindow::actionSave()
|
|||
{
|
||||
if (this->fileName.isEmpty()) {
|
||||
actionSaveAs();
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentOutput();
|
||||
|
||||
// If available (>= Qt 5.1), use QSaveFile to ensure the file is not
|
||||
// destroyed if the device is full. Unfortunately this is not working
|
||||
// as advertised (at least in Qt 5.3) as it does not detect the device
|
||||
// full properly and happily commits a 0 byte file.
|
||||
// Checking the QTextStream status flag after flush() seems to catch
|
||||
// this condition.
|
||||
QT_FILE_SAVE_CLASS file(this->fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
saveError(file, _("Failed to open file for writing"));
|
||||
}
|
||||
else {
|
||||
setCurrentOutput();
|
||||
QFile file(this->fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
PRINTB("Failed to open file for writing: %s (%s)",
|
||||
this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData());
|
||||
QMessageBox::warning(this, windowTitle(), tr("Failed to open file for writing:\n %1 (%2)")
|
||||
.arg(this->fileName).arg(file.errorString()));
|
||||
}
|
||||
else {
|
||||
QTextStream writer(&file);
|
||||
writer.setCodec("UTF-8");
|
||||
writer << this->editor->toPlainText();
|
||||
PRINTB("Saved design '%s'.", this->fileName.toLocal8Bit().constData());
|
||||
QTextStream writer(&file);
|
||||
writer.setCodec("UTF-8");
|
||||
writer << this->editor->toPlainText();
|
||||
writer.flush();
|
||||
bool saveOk = writer.status() == QTextStream::Ok;
|
||||
QT_FILE_SAVE_COMMIT;
|
||||
if (saveOk) {
|
||||
PRINTB(_("Saved design '%s'."), this->fileName.toLocal8Bit().constData());
|
||||
this->editor->setContentModified(false);
|
||||
} else {
|
||||
saveError(file, _("Error saving design"));
|
||||
}
|
||||
clearCurrentOutput();
|
||||
updateRecentFiles();
|
||||
}
|
||||
clearCurrentOutput();
|
||||
updateRecentFiles();
|
||||
}
|
||||
|
||||
void MainWindow::actionSaveAs()
|
||||
|
|
|
|||
|
|
@ -272,12 +272,10 @@ Camera get_camera(po::variables_map vm)
|
|||
return camera;
|
||||
}
|
||||
|
||||
#ifdef OPENSCAD_TESTING
|
||||
#undef OPENSCAD_QTGUI
|
||||
#else
|
||||
#define OPENSCAD_QTGUI 1
|
||||
#ifndef OPENSCAD_NOGUI
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#define OPENSCAD_QTGUI 1
|
||||
#endif
|
||||
static bool checkAndExport(shared_ptr<const Geometry> root_geom, unsigned nd,
|
||||
enum FileFormat format, const char *filename)
|
||||
|
|
|
|||
|
|
@ -102,9 +102,7 @@ void parser_init()
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef OPENSCAD_TESTING
|
||||
add_librarydir(PlatformUtils::userLibraryPath());
|
||||
#endif
|
||||
|
||||
add_librarydir(boosty::absolute(PlatformUtils::resourcePath("libraries")).string());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,10 +104,7 @@ std::string RotateExtrudeNode::toString() const
|
|||
"layer = " << QuotedString(this->layername) << ", "
|
||||
"origin = [" << std::dec << this->origin_x << ", " << this->origin_y << "], "
|
||||
"scale = " << this->scale << ", "
|
||||
#ifndef OPENSCAD_TESTING
|
||||
// timestamp is needed for caching, but disturbs the test framework
|
||||
<< "timestamp = " << (fs::exists(path) ? fs::last_write_time(path) : 0) << ", "
|
||||
#endif
|
||||
;
|
||||
}
|
||||
stream <<
|
||||
|
|
|
|||
|
|
@ -311,10 +311,7 @@ std::string SurfaceNode::toString() const
|
|||
stream << this->name() << "(file = " << this->filename
|
||||
<< ", center = " << (this->center ? "true" : "false")
|
||||
<< ", invert = " << (this->invert ? "true" : "false")
|
||||
#ifndef OPENSCAD_TESTING
|
||||
// timestamp is needed for caching, but disturbs the test framework
|
||||
<< ", " "timestamp = " << (fs::exists(path) ? fs::last_write_time(path) : 0)
|
||||
#endif
|
||||
<< ")";
|
||||
|
||||
return stream.str();
|
||||
|
|
|
|||
21
src/value.cc
21
src/value.cc
|
|
@ -201,33 +201,12 @@ public:
|
|||
if (op1 == 0) {
|
||||
return "0"; // Don't return -0 (exactly -0 and 0 equal 0)
|
||||
}
|
||||
#ifdef OPENSCAD_TESTING
|
||||
// Quick and dirty hack to work around floating point rounding differences
|
||||
// across platforms for testing purposes.
|
||||
std::stringstream tmp;
|
||||
tmp.precision(12);
|
||||
tmp.setf(std::ios_base::fixed);
|
||||
tmp << op1;
|
||||
std::string tmpstr = tmp.str();
|
||||
size_t endpos = tmpstr.find_last_not_of('0');
|
||||
if (tmpstr[endpos] == '.') endpos--;
|
||||
tmpstr = tmpstr.substr(0, endpos+1);
|
||||
size_t dotpos = tmpstr.find('.');
|
||||
if (dotpos != std::string::npos) {
|
||||
if (tmpstr.size() - dotpos > 12) tmpstr.erase(dotpos + 12);
|
||||
while (tmpstr[tmpstr.size()-1] == '0') tmpstr.erase(tmpstr.size()-1);
|
||||
}
|
||||
if (tmpstr.compare("-0") == 0) tmpstr = "0";
|
||||
tmpstr = two_digit_exp_format(tmpstr);
|
||||
return tmpstr;
|
||||
#else
|
||||
// attempt to emulate Qt's QString.sprintf("%g"); from old OpenSCAD.
|
||||
// see https://github.com/openscad/openscad/issues/158
|
||||
std::stringstream tmp;
|
||||
tmp.unsetf(std::ios::floatfield);
|
||||
tmp << op1;
|
||||
return tmp.str();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string operator()(const boost::blank &) const {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ a time, to avoid confusion.
|
|||
#else
|
||||
#endif // ENABLE_OPENCSG
|
||||
|
||||
#ifndef OPENSCAD_TESTING
|
||||
#ifndef OPENSCAD_NOGUI
|
||||
#include <QtCore/qglobal.h>
|
||||
#if QT_VERSION < 0x040400
|
||||
#error QT library missing or version too old. See README.md. To force compile, run qmake CONFIG+=skip-version-check
|
||||
|
|
|
|||
|
|
@ -344,9 +344,19 @@ if (NOT OPENGL_GLU_FOUND)
|
|||
message(FATAL "GLU library not found")
|
||||
endif()
|
||||
set(OPENGL_LIBRARIES ${OPENGL_glu_LIBRARY} ${OPENGL_LIBRARIES})
|
||||
message(STATUS "OpenGL LIBRARIES: ")
|
||||
foreach(GLLIB ${OPENGL_LIBRARIES})
|
||||
message(STATUS " " ${GLLIB})
|
||||
endif()
|
||||
message(STATUS "OPENGL_LIBRARIES: ")
|
||||
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()
|
||||
endif()
|
||||
|
||||
|
|
@ -514,8 +524,12 @@ foreach(CGAL3RDPLIB ${CGAL_3RD_PARTY_LIBRARIES})
|
|||
endforeach()
|
||||
|
||||
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 "--param=ssp-buffer-size=4" "" 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})
|
||||
# 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()
|
||||
|
||||
# GLib2
|
||||
|
|
@ -648,7 +662,7 @@ if (DEFINED OPENSCAD_DAY)
|
|||
add_definitions(-DOPENSCAD_DAY=${OPENSCAD_DAY})
|
||||
endif()
|
||||
|
||||
add_definitions(-DOPENSCAD_TESTING -DENABLE_EXPERIMENTAL)
|
||||
add_definitions(-DENABLE_EXPERIMENTAL -DOPENSCAD_NOGUI)
|
||||
|
||||
# Search for MCAD in correct place
|
||||
set(CTEST_ENVIRONMENT "${CTEST_ENVIRONMENT};OPENSCADPATH=${CMAKE_CURRENT_SOURCE_DIR}/../libraries")
|
||||
|
|
@ -665,6 +679,8 @@ elseif(UNIX)
|
|||
set(OFFSCREEN_CTX_SOURCE "OffscreenContextGLX.cc" CACHE TYPE STRING)
|
||||
set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-lodepng.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)
|
||||
message(STATUS "Offscreen OpenGL Context - using Microsoft WGL")
|
||||
set(OFFSCREEN_CTX_SOURCE "OffscreenContextWGL.cc" CACHE TYPE STRING)
|
||||
|
|
@ -743,6 +759,8 @@ set(CGAL_SOURCES
|
|||
../src/CSGTermEvaluator.cc
|
||||
../src/CGAL_Nef_polyhedron.cc
|
||||
../src/cgalutils.cc
|
||||
../src/cgalutils-applyops.cc
|
||||
../src/cgalutils-project.cc
|
||||
../src/cgalutils-tess.cc
|
||||
../src/cgalutils-polyhedron.cc
|
||||
../src/CGALCache.cc
|
||||
|
|
@ -819,6 +837,7 @@ endif()
|
|||
|
||||
add_library(tests-offscreen STATIC ${OFFSCREEN_SOURCES})
|
||||
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
|
||||
|
|
@ -840,43 +859,29 @@ set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CG
|
|||
target_link_libraries(cgalcachetest tests-cgal ${GLEW_LIBRARIES} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY})
|
||||
|
||||
#
|
||||
# openscad no-qt
|
||||
# openscad_nogui - an OpenSCAD binary build without Qt
|
||||
# Enabled by using -DNOGUI=1 as a cmake parameter. Only kept for backwards compatibility and in case
|
||||
# someone needs that binary.
|
||||
#
|
||||
add_executable(openscad_nogui ../src/openscad.cc)
|
||||
set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN ${ENABLE_OPENCSG_FLAG} -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
|
||||
target_link_libraries(openscad_nogui tests-offscreen tests-cgal ${GLEW_LIBRARIES} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY})
|
||||
|
||||
# also run translation compilation to verify the files are without syntax errors
|
||||
add_custom_target(locale_files ALL COMMAND ${CMAKE_SOURCE_DIR}/../scripts/translation-make.sh)
|
||||
|
||||
#
|
||||
# GUI binary tests
|
||||
#
|
||||
#if(APPLE)
|
||||
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../OpenSCAD.app/Contents/MacOS/OpenSCAD")
|
||||
#elseif (MINGW_CROSS_ENV_DIR)
|
||||
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../mingw32/release/openscad.exe")
|
||||
#elseif(WIN32)
|
||||
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../Release/openscad.exe")
|
||||
#else()
|
||||
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../openscad")
|
||||
#endif()
|
||||
|
||||
#if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/openscad")
|
||||
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_BINARY_DIR}/openscad")
|
||||
#endif()
|
||||
|
||||
#if(EXISTS "${OPENSCAD_BINPATH}")
|
||||
# message(STATUS "Found OpenSCAD binary: ${OPENSCAD_BINPATH}")
|
||||
#else()
|
||||
# message(STATUS "Couldn't find the OpenSCAD binary: ${OPENSCAD_BINPATH}")
|
||||
# message(FATAL_ERROR "Please build the OpenSCAD binary and place it here: ${OPENSCAD_BINPATH}" )
|
||||
#endif()
|
||||
|
||||
if(WIN32)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_BINARY_DIR}/openscad_nogui.exe")
|
||||
if(APPLE)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../OpenSCAD.app/Contents/MacOS/OpenSCAD")
|
||||
elseif (MINGW_CROSS_ENV_DIR)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../mingw32/release/openscad.exe")
|
||||
elseif(WIN32)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../Release/openscad.exe")
|
||||
else()
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_BINARY_DIR}/openscad_nogui")
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../openscad")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${OPENSCAD_BINPATH}")
|
||||
message(STATUS "Found OpenSCAD binary: ${OPENSCAD_BINPATH}")
|
||||
else()
|
||||
message(STATUS "Couldn't find the OpenSCAD binary: ${OPENSCAD_BINPATH}")
|
||||
message(FATAL_ERROR "Please build the OpenSCAD binary and place it here: ${OPENSCAD_BINPATH}" )
|
||||
endif()
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ if args.format != 'csg':
|
|||
create_png_cmd = [args.openscad, newscadfile, '-o', pngfile] + remaining_args
|
||||
print >> sys.stderr, 'Running OpenSCAD #2:'
|
||||
print >> sys.stderr, ' '.join(create_png_cmd)
|
||||
fontdir = os.path.join(os.path.dirname(args.openscad), "..", "testdata");
|
||||
fontdir = os.path.join(os.path.dirname(__file__), "..", "testdata");
|
||||
fontenv = os.environ.copy();
|
||||
fontenv["OPENSCAD_FONT_PATH"] = fontdir;
|
||||
result = subprocess.call(create_png_cmd, env = fontenv);
|
||||
|
|
|
|||
|
|
@ -93,12 +93,38 @@ def execute_and_redirect(cmd, params, outfile):
|
|||
if outfile == subprocess.PIPE: return (retval, out)
|
||||
else: return retval
|
||||
|
||||
def normalize_string(s):
|
||||
"""Apply all modifications to an output string which would have been
|
||||
applied if OPENSCAD_TESTING was defined at build time of the executable.
|
||||
|
||||
This truncates all floats, removes ', timestamp = ...' parts. The function
|
||||
is idempotent.
|
||||
|
||||
This also normalizes away import paths from 'file = ' arguments."""
|
||||
|
||||
s = re.sub(', timestamp = [0-9]+', '', s)
|
||||
def floatrep(match):
|
||||
value = float(match.groups()[0])
|
||||
if abs(value) < 10**-12:
|
||||
return "0"
|
||||
if abs(value) >= 10**6:
|
||||
return "%d"%value
|
||||
return "%.6g"%value
|
||||
s = re.sub('(-?[0-9]+\\.[0-9]+(e[+-][0-9]+)?)', floatrep, s)
|
||||
|
||||
def pathrep(match):
|
||||
return match.groups()[0] + match.groups()[2]
|
||||
s = re.sub('(file = ")([^"/]*/)*([^"]*")', pathrep, s)
|
||||
|
||||
return s
|
||||
|
||||
def get_normalized_text(filename):
|
||||
try:
|
||||
f = open(filename)
|
||||
text = f.read()
|
||||
except:
|
||||
text = ''
|
||||
text = normalize_string(text)
|
||||
return text.strip("\r\n").replace("\r\n", "\n") + "\n"
|
||||
|
||||
def compare_text(expected, actual):
|
||||
|
|
|
|||
Loading…
Reference in a new issue