Merge branch 'master' into travis

vector-concat
Marius Kintel 2013-12-08 23:12:13 -05:00
commit 462d4d4478
80 changed files with 3006 additions and 759 deletions

View File

@ -94,7 +94,7 @@ Follow the instructions for the platform you're compiling on below.
* [boost (1.35 - 1.53)](http://www.boost.org/)
* [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
* [Eigen (2.0.x->3.x)](http://eigen.tuxfamily.org/)
* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/)
* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
* [Bison (2.4)](http://www.gnu.org/software/bison/)
* [Flex (2.5.35)](http://flex.sourceforge.net/)

View File

@ -4,4 +4,4 @@ Version=1.0
Name=OpenSCAD
Icon=openscad
Exec=openscad %f
Categories=Graphics;3DGraphics;Engineering;
Categories=Graphics;3DGraphics;Engineering;Programming;

View File

@ -39,6 +39,7 @@ debug: DEFINES += DEBUG
TEMPLATE = app
INCLUDEPATH += src
DEPENDPATH += src
# Handle custom library location.
# Used when manually installing 3rd party libraries
@ -368,6 +369,7 @@ HEADERS += src/cgal.h \
src/PolySetCGALEvaluator.h \
src/CGALRenderer.h \
src/CGAL_Nef_polyhedron.h \
src/CGAL_Nef3_workaround.h \
src/cgalworker.h
SOURCES += src/cgalutils.cc \

View File

@ -0,0 +1,18 @@
--- src/3rdparty/libtiff/libtiff/tif_config.h
+++ src/3rdparty/libtiff/libtiff/tif_config.h
@@ -317,15 +317,6 @@
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
-/* Define to `__inline__' or `__inline' if that's what the C compiler
- calls it, or to nothing if 'inline' is not supported under any name. */
-#ifndef Q_OS_SYMBIAN
-#ifndef __cplusplus
-#undef inline
-#define inline
-#endif
-#endif
-
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */

View File

@ -0,0 +1,86 @@
--- src/gui/kernel/qeventdispatcher_mac_p.h 2013-06-07 01:16:59.000000000 -0400
+++ src/gui/kernel/qeventdispatcher_mac_p_new-8184b49c12d887928921ed5b695c8c6f04a07514.h 2013-12-08 14:31:01.000000000 -0500
@@ -173,6 +173,7 @@
#ifdef QT_MAC_USE_COCOA
// The following variables help organizing modal sessions:
static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
+ static QStack<QCocoaModalSessionInfo> cocoaModalSessionStackPendingEnd;
static bool currentExecIsNSAppRun;
static bool nsAppRunCalledByQt;
static bool cleanupModalSessionsNeeded;
@@ -180,6 +181,7 @@
static NSModalSession currentModalSession();
static void updateChildrenWorksWhenModal();
static void temporarilyStopAllModalSessions();
+ static void stopAllPendingEndModalSessions();
static void beginModalSession(QWidget *widget);
static void endModalSession(QWidget *widget);
static void cancelWaitForMoreEvents();
--- src/gui/kernel/qeventdispatcher_mac.mm 2013-06-07 01:16:59.000000000 -0400
+++ src/gui/kernel/qeventdispatcher_mac_new-833e02de99494686f8dd7a567f6e19e847508f11.mm 2013-12-08 14:30:59.000000000 -0500
@@ -603,6 +603,9 @@
while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
qt_mac_waitForMoreModalSessionEvents();
+ // stop all pending end modal sessions
+ d->stopAllPendingEndModalSessions();
+
if (!d->interrupt && session == d->currentModalSessionCached) {
// Someone called [NSApp stopModal:] from outside the event
// dispatcher (e.g to stop a native dialog). But that call wrongly stopped
@@ -678,6 +681,9 @@
if (!d->interrupt)
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+ // stop all pending end modal sessions
+ d->stopAllPendingEndModalSessions();
+
// Since the window that holds modality might have changed while processing
// events, we we need to interrupt when we return back the previous process
// event recursion to ensure that we spin the correct modal session.
@@ -781,6 +787,7 @@
#ifdef QT_MAC_USE_COCOA
QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
+QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStackPendingEnd;
bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false;
@@ -828,6 +835,20 @@
currentModalSessionCached = 0;
}
+void QEventDispatcherMacPrivate::stopAllPendingEndModalSessions()
+{
+ // stop all modal sessions pending end
+ int stackSize = cocoaModalSessionStackPendingEnd.size();
+ for (int i=stackSize-1; i>=0; --i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStackPendingEnd[i];
+ cocoaModalSessionStackPendingEnd.remove(i);
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ [(NSWindow *)info.nswindow release];
+ }
+ }
+}
+
NSModalSession QEventDispatcherMacPrivate::currentModalSession()
{
// If we have one or more modal windows, this function will create
@@ -925,10 +946,12 @@
}
cocoaModalSessionStack.remove(i);
currentModalSessionCached = 0;
- if (info.session) {
- [NSApp endModalSession:info.session];
- [(NSWindow *)info.nswindow release];
- }
+
+ // Cannot stop the sessions here since we might still be inside a
+ // [NSApp runModalSession:] call. Add the session to the pending end stack and
+ // process the stack after the call to [NSApp runModalSession:] returns.
+ if (info.session)
+ cocoaModalSessionStackPendingEnd.push(info);
}
updateChildrenWorksWhenModal();

View File

@ -0,0 +1,29 @@
--- src/gui/text/qfontdatabase.cpp 2013-06-07 01:16:59.000000000 -0400
+++ src/gui/text/qfontdatabase_new-bb2beddc3ae55c4676d190d0ac99aa32d322a6a5.cpp 2013-12-08 14:51:10.000000000 -0500
@@ -441,6 +441,7 @@
#endif
#if !defined(QWS) && defined(Q_OS_MAC)
bool fixedPitchComputed : 1;
+ QString postscriptName;
#endif
#ifdef Q_WS_X11
bool symbol_checked : 1;
--- src/gui/text/qfontdatabase_mac.cpp 2013-06-07 01:16:59.000000000 -0400
+++ src/gui/text/qfontdatabase_mac_new-41f29865db84152efb41c048470f713353a0a84c.cpp 2013-12-08 14:51:05.000000000 -0500
@@ -147,6 +147,7 @@
QCFString family_name = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontFamilyNameAttribute, NULL);
QCFString style_name = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontStyleNameAttribute, NULL);
QtFontFamily *family = db->family(family_name, true);
+ family->postscriptName = QCFString((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontNameAttribute));
if (QCFType<CFArrayRef> languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
CFIndex length = CFArrayGetCount(languages);
@@ -327,7 +328,7 @@
if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
QByteArray family_name = db->families[k]->name.toUtf8();
#if defined(QT_MAC_USE_COCOA)
- QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
+ QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->postscriptName), 12, NULL);
if (ctFont) {
fontName = CTFontCopyFullName(ctFont);
goto found;

View File

@ -0,0 +1,14 @@
--- src/corelib/global/qglobal.h.orig 2013-06-07 07:16:52.000000000 +0200
+++ src/corelib/global/qglobal.h 2013-10-27 14:05:22.000000000 +0100
@@ -327,7 +327,10 @@
# if !defined(MAC_OS_X_VERSION_10_8)
# define MAC_OS_X_VERSION_10_8 MAC_OS_X_VERSION_10_7 + 1
# endif
-# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_8)
+# if !defined(MAC_OS_X_VERSION_10_9)
+# define MAC_OS_X_VERSION_10_9 MAC_OS_X_VERSION_10_8 + 1
+# endif
+# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9)
# warning "This version of Mac OS X is unsupported"
# endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,41 @@
#!/usr/bin/env bash
# build&upload script for linux & windows snapshot binaries
# tested under linux
#
# Usage:
#
# Start with a clean directory. For example:
#
# mkdir builder
# cd builder
#
# Then run this script, or optionally the 'build only' or 'upload only' version
#
# /some/path/openscad/builder.sh # standard build & upload
# /some/path/openscad/builder.sh buildonly # only do build, dont upload
# /some/path/openscad/builder.sh uploadonly # only upload, dont build
# Notes:
#
# This script is designed to build a 'clean' version of openscad directly
# from the openscad github master source code. It will then optionally
# upload the build to the OpenSCAD official file repository, and modify
# the OpenSCAD website with links to the most recently built files.
#
#
# For the mingw- cross build for Windows(TM) this script does a massive
# 'from nothing' build, including downloading and building an MXE cross
# environment, and dependencies, into $HOME/openscad_deps. This can take
# many many many hours and use several gigabytes of disk space.
#
# This script itself is designed to call other scripts that do the heavy
# lifting. This script itself should be kept relatively simple.
#
#
# requirements -
# see http://mxe.cc for required tools (scons, perl, yasm, etc etc etc)
#
# todo - can we build 32 bit linux from within 64 bit linux?
#
# todo - make linux work
@ -19,18 +49,24 @@ init_variables()
{
STARTPATH=$PWD
export STARTPATH
DOBUILD=1
DOUPLOAD=1
DRYRUN=
if [ "`echo $* | grep uploadonly`" ]; then
UPLOADONLY=1
DOUPLOAD=1
DOBUILD=
DATECODE=`date +"%Y.%m.%d"`
fi
if [ "`echo $* | grep buildonly`" ]; then
DOUPLOAD=
DOBUILD=1
DATECODE=`date +"%Y.%m.%d"`
else
UPLOADONLY=
fi
if [ "`echo $* | grep dry`" ]; then
DRYRUN=1
else
DRYRUN=
fi
export UPLOADONLY
export DOBUILD
export DOUPLOAD
export DRYRUN
export DATECODE
}
@ -43,9 +79,15 @@ check_starting_path()
fi
}
get_source_code()
get_openscad_source_code()
{
git clone http://github.com/openscad/openscad.git
if [ "`echo $? | grep 0`" ]; then
echo clone of source code is ok
else
echo clone of openscad source code failed. exiting
exit 1
fi
cd openscad
git submodule update --init # MCAD
#git checkout branch ##debugging
@ -80,7 +122,7 @@ build_lin32()
export DATECODE
}
upload_win_generic()
upload_win_common()
{
summary="$1"
username=$2
@ -110,8 +152,8 @@ upload_win32()
BASEDIR=./mingw32/
WIN32_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-32-Installer.exe
WIN32_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-32.zip
upload_win_generic "$SUMMARY1" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE1
upload_win_generic "$SUMMARY2" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE2
upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE1
upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN32_PACKAGEFILE2
export WIN32_PACKAGEFILE1
export WIN32_PACKAGEFILE2
WIN32_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN32_PACKAGEFILE1 | awk ' {print $1} ';`
@ -129,8 +171,8 @@ upload_win64()
BASEDIR=./mingw64/
WIN64_PACKAGEFILE1=OpenSCAD-$DATECODE-x86-64-Installer.exe
WIN64_PACKAGEFILE2=OpenSCAD-$DATECODE-x86-64.zip
upload_win_generic "$SUMMARY1" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE1
upload_win_generic "$SUMMARY2" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE2
upload_win_common "$SUMMARY1" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE1
upload_win_common "$SUMMARY2" $USERNAME $BASEDIR/$WIN64_PACKAGEFILE2
export WIN64_PACKAGEFILE1
export WIN64_PACKAGEFILE2
WIN64_PACKAGEFILE1_SIZE=`ls -sh $BASEDIR/$WIN64_PACKAGEFILE1 | awk ' {print $1} ';`
@ -188,6 +230,7 @@ update_win_www_download_links()
BASEURL='http://files.openscad.org/'
DATECODE=`date +"%Y.%m.%d"`
mv win_snapshot_links.js win_snapshot_links.js.backup
rm win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js
echo "fileinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js
@ -229,19 +272,20 @@ check_ssh_agent()
}
init_variables $*
check_ssh_agent
if [ $DOUPLOAD ]; then
check_ssh_agent
fi
check_starting_path
read_username_from_user
read_password_from_user
get_source_code
if [ ! $UPLOADONLY ]; then
get_openscad_source_code
if [ $DOBUILD ]; then
build_win32
build_win64
fi
upload_win32
upload_win64
update_win_www_download_links
if [ $DOUPLOAD ]; then
upload_win32
upload_win64
update_win_www_download_links
fi

View File

@ -86,21 +86,16 @@ mpfr_sysver()
gmp_sysver()
{
# on some systems you have VERSION in gmp-$arch.h not gmp.h. use gmp*.h
if [ -e $1/include/multiarch-x86_64-linux ]; then
subdir=include/multiarch-x86_64-linux
else
subdir=include
gmppaths="`find $1 -name 'gmp.h' -o -name 'gmp-*.h'`"
if [ ! "$gmppaths" ]; then
debug "gmp_sysver no gmp.h beneath $1"
return
fi
if [ ! -e $1/$subdir ]; then return; fi
gmppaths=`ls $1/$subdir | grep ^gmp`
if [ ! "$gmppaths" ]; then return; fi
for gmpfile in $gmppaths; do
gmppath=$1/$subdir/$gmpfile
if [ "`grep __GNU_MP_VERSION $gmppath`" ]; then
gmpmaj=`grep "define *__GNU_MP_VERSION *[0-9]*" $gmppath | awk '{print $3}'`
gmpmin=`grep "define *__GNU_MP_VERSION_MINOR *[0-9]*" $gmppath | awk '{print $3}'`
gmppat=`grep "define *__GNU_MP_VERSION_PATCHLEVEL *[0-9]*" $gmppath | awk '{print $3}'`
if [ "`grep __GNU_MP_VERSION $gmpfile`" ]; then
gmpmaj=`grep "define *__GNU_MP_VERSION *[0-9]*" $gmpfile | awk '{print $3}'`
gmpmin=`grep "define *__GNU_MP_VERSION_MINOR *[0-9]*" $gmpfile | awk '{print $3}'`
gmppat=`grep "define *__GNU_MP_VERSION_PATCHLEVEL *[0-9]*" $gmpfile | awk '{print $3}'`
fi
done
gmp_sysver_result="$gmpmaj.$gmpmin.$gmppat"
@ -261,32 +256,47 @@ pkg_config_search()
get_minversion_from_readme()
{
if [ -e README.md ]; then READFILE=README.md; fi
if [ -e ../README.md ]; then READFILE=../README.md; fi
if [ ! $READFILE ]; then
if [ "`command -v dirname`" ]; then
READFILE=`dirname $0`/../README.md
fi
fi
if [ ! $READFILE ]; then echo "cannot find README.md"; exit 1; fi
debug get_minversion_from_readme $*
# Extract dependency name
if [ ! $1 ]; then return; fi
depname=$1
local grv_tmp=
debug $depname
# example--> * [CGAL (3.6 - 3.9)] (www.cgal.org) becomes 3.6
# steps: eliminate *, find left (, find -, make 'x' into 0, delete junk
grv_tmp=`grep -i ".$depname.*([0-9]" $READFILE | sed s/"*"//`
debug $grv_tmp
grv_tmp=`echo $grv_tmp | awk -F"(" '{print $2}'`
debug $grv_tmp
grv_tmp=`echo $grv_tmp | awk -F"-" '{print $1}'`
debug $grv_tmp
grv_tmp=`echo $grv_tmp | sed s/"x"/"0"/g`
debug $grv_tmp
grv_tmp=`echo $grv_tmp | sed s/"[^0-9.]"//g`
debug $grv_tmp
get_minversion_from_readme_result=$grv_tmp
local grv_tmp=
for READFILE in README.md ../README.md "`dirname "$0"`/../README.md"
do
if [ ! -e "$READFILE" ]
then
debug "get_minversion_from_readme $READFILE not found"
continue
fi
debug "get_minversion_from_readme $READFILE found"
grep -qi ".$depname.*([0-9]" $READFILE || continue
grv_tmp="`grep -i ".$depname.*([0-9]" $READFILE | sed s/"*"//`"
debug $grv_tmp
grv_tmp="`echo $grv_tmp | awk -F"(" '{print $2}'`"
debug $grv_tmp
grv_tmp="`echo $grv_tmp | awk -F"-" '{print $1}'`"
debug $grv_tmp
grv_tmp="`echo $grv_tmp | sed s/"x"/"0"/g`"
debug $grv_tmp
grv_tmp="`echo $grv_tmp | sed s/"[^0-9.]"//g`"
debug $grv_tmp
if [ "z$grv_tmp" = "z" ]
then
debug "get_minversion_from_readme no result for $depname from $READFILE"
continue
fi
get_minversion_from_readme_result=$grv_tmp
return 0
done
if [ "z$grv_tmp" = "z" ]
then
debug "get_minversion_from_readme no result for $depname found anywhere"
get_minversion_from_readme_result=""
return 0
fi
}
find_min_version()
@ -462,7 +472,7 @@ check_old_local()
{
warnon=
if [ "`uname | grep -i linux`" ]; then
header_list="opencsg.h CGAL boost GL/glew.h gmp.h mpfr.h eigen2 eigen3"
header_list="opencsg.h CGAL boost GL/glew.h gmp.h mpfr.h eigen3"
for i in $header_list; do
if [ -e /usr/local/include/$i ]; then
echo "Warning: you have a copy of "$i" under /usr/local/include"
@ -533,7 +543,7 @@ main()
dep_minver=$find_min_version_result
compare_version $dep_minver $dep_sysver
dep_compare=$compare_version_result
pretty_print $depname $dep_minver $dep_sysver $dep_compare
pretty_print $depname "$dep_minver" "$dep_sysver" $dep_compare
done
check_old_local
check_misc

View File

@ -24,7 +24,7 @@ BASEDIR=$PWD/../libraries
OPENSCADDIR=$PWD
SRCDIR=$BASEDIR/src
DEPLOYDIR=$BASEDIR/install
MAC_OSX_VERSION_MIN=10.6
MAC_OSX_VERSION_MIN=10.7
OPTION_32BIT=false
OPTION_LLVM=false
OPTION_CLANG=false
@ -54,6 +54,9 @@ build_qt()
fi
tar xzf qt-everywhere-opensource-src-$version.tar.gz
cd qt-everywhere-opensource-src-$version
patch -p0 < $OPENSCADDIR/patches/qt4/patch-src_corelib_global_qglobal.h.diff
patch -p0 < $OPENSCADDIR/patches/qt4/patch-libtiff.diff
patch -p0 < $OPENSCADDIR/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff
if $USING_CLANG; then
# FIX for clang
sed -i "" -e "s/::TabletProximityRec/TabletProximityRec/g" src/gui/kernel/qt_cocoa_helpers_mac_p.h
@ -220,7 +223,7 @@ build_boost()
BOOST_TOOLSET="toolset=clang"
echo "using clang ;" >> tools/build/v2/user-config.jam
fi
./b2 -d+2 $BOOST_TOOLSET cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS -headerpad_max_install_names" install
./b2 -j6 -d+2 $BOOST_TOOLSET cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS -headerpad_max_install_names" install
install_name_tool -id $DEPLOYDIR/lib/libboost_thread.dylib $DEPLOYDIR/lib/libboost_thread.dylib
install_name_tool -change libboost_system.dylib $DEPLOYDIR/lib/libboost_system.dylib $DEPLOYDIR/lib/libboost_thread.dylib
install_name_tool -change libboost_chrono.dylib $DEPLOYDIR/lib/libboost_chrono.dylib $DEPLOYDIR/lib/libboost_thread.dylib
@ -309,10 +312,10 @@ build_eigen()
rm -rf eigen-$version
EIGENDIR="none"
if [ $version = "2.0.17" ]; then EIGENDIR=eigen-eigen-b23437e61a07; fi
if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4;
elif [ $version = "3.1.3" ]; then EIGENDIR=eigen-eigen-2249f9c22fe8;
elif [ $version = "3.1.4" ]; then EIGENDIR=eigen-eigen-36bf2ceaf8f5; fi
elif [ $version = "3.1.4" ]; then EIGENDIR=eigen-eigen-36bf2ceaf8f5;
elif [ $version = "3.2.0" ]; then EIGENDIR=eigen-eigen-ffa86ffb5570; fi
if [ $EIGENDIR = "none" ]; then
echo Unknown eigen version. Please edit script.
@ -439,7 +442,7 @@ echo "Using basedir:" $BASEDIR
mkdir -p $SRCDIR $DEPLOYDIR
build_qt 4.8.5
# NB! For eigen, also update the path in the function
build_eigen 3.1.4
build_eigen 3.2.0
build_gmp 5.1.3
build_mpfr 3.1.2
build_boost 1.54.0

View File

@ -6,8 +6,13 @@
# This script must be run from the OpenSCAD source root directory
#
# Usage:
# ./scripts/mingw-x-build-dependencies.sh # 32 bit
# ./scripts/mingw-x-build-dependencies.sh 64 # 64 bit
# ./scripts/mingw-x-build-dependencies.sh # 32 bit
# ./scripts/mingw-x-build-dependencies.sh 64 # 64 bit
#
# If you just want to download, and build later:
#
# ./scripts/mingw-x-build-dependencies.sh download # 32 bit download
# ./scripts/mingw-x-build-dependencies.sh 64 download # 64 bit download
#
# Prerequisites:
#
@ -48,21 +53,25 @@ if [ ! -e $MXEDIR ]; then
mkdir -p $MXEDIR
cd $MXEDIR/..
echo "Downloading MXE into " $PWD
if [ "`echo $* | grep 64`" ]; then
git clone -b multi-rebase git://github.com/tonytheodore/mxe.git $MXEDIR
else
git clone git://github.com/mxe/mxe.git $MXEDIR
fi
git clone git://github.com/mxe/mxe.git $MXEDIR
fi
echo "entering" $MXEDIR
cd $MXEDIR
if [ "`echo $* | grep 64`" ]; then
MXE_TARGETS='x86_64-w64-mingw32'
MXE_TARGETS='x86_64-w64-mingw32'
if [ "`echo $* | grep download`" ]; then
PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt'
else
PACKAGES='mpfr eigen opencsg cgal qt'
fi
else
MXE_TARGETS=
MXE_TARGETS='i686-pc-mingw32' # fixme - does this work? test it.
if [ "`echo $* | grep download`" ]; then
PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-nsis'
else
PACKAGES='mpfr eigen opencsg cgal qt nsis'
fi
fi
echo make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS
make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS

View File

@ -343,7 +343,7 @@ build_cgal()
if [ "`echo $2 | grep use-sys-libs`" ]; then
cmake -DCMAKE_INSTALL_PREFIX=$DEPLOYDIR -DWITH_CGAL_Qt3=OFF -DWITH_CGAL_Qt4=OFF -DWITH_CGAL_ImageIO=OFF -DCMAKE_BUILD_TYPE=$CGAL_BUILDTYPE -DBoost_DEBUG=$DEBUGBOOSTFIND ..
else
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_LIBRARYDIR=$DEPLOYDIR/lib -DBOOST_INCLUDEDIR=$DEPLOYDIR/include -DCMAKE_BUILD_TYPE=$CGAL_BUILD_TYPE -DBoost_DEBUG=$DEBUGBOOSTFIND -DBoost_NO_SYSTEM_PATHS=1 ..
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_LIBRARYDIR=$DEPLOYDIR/lib -DBOOST_INCLUDEDIR=$DEPLOYDIR/include -DCMAKE_BUILD_TYPE=$CGAL_BUILDTYPE -DBoost_DEBUG=$DEBUGBOOSTFIND -DBoost_NO_SYSTEM_PATHS=1 ..
fi
make -j$NUMCPU
make install
@ -476,12 +476,6 @@ build_opencsg()
build_eigen()
{
version=$1
if [ -e $DEPLOYDIR/include/eigen2 ]; then
if [ `echo $version | grep 2....` ]; then
echo "Eigen2 already installed. not building"
return
fi
fi
if [ -e $DEPLOYDIR/include/eigen3 ]; then
if [ `echo $version | grep 3....` ]; then
echo "Eigen3 already installed. not building"
@ -492,7 +486,6 @@ build_eigen()
cd $BASEDIR/src
rm -rf eigen-$version
EIGENDIR="none"
if [ $version = "2.0.17" ]; then EIGENDIR=eigen-eigen-b23437e61a07; fi
if [ $version = "3.1.1" ]; then EIGENDIR=eigen-eigen-43d9075b23ef; fi
if [ $EIGENDIR = "none" ]; then
echo Unknown eigen version. Please edit script.

View File

@ -6,7 +6,7 @@
get_fedora_deps()
{
sudo yum install qt-devel bison flex eigen2-devel python-paramiko \
sudo yum install qt-devel bison flex eigen3-devel python-paramiko \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
opencsg-devel git libXmu-devel curl imagemagick ImageMagick make \
xorg-x11-server-Xvfb
@ -20,13 +20,13 @@ get_qomo_deps()
get_altlinux_deps()
{
for i in boost-devel boost-filesystem-devel gcc4.5 gcc4.5-c++ boost-program_options-devel \
boost-thread-devel boost-system-devel boost-regex-devel eigen2 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \
boost-thread-devel boost-system-devel boost-regex-devel eigen3 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \
libglew-devel flex bison curl imagemagick; do sudo apt-get install $i; done
}
get_freebsd_deps()
{
pkg_add -r bison boost-libs cmake git bash eigen2 flex gmake gmp mpfr \
pkg_add -r bison boost-libs cmake git bash eigen3 flex gmake gmp mpfr \
xorg libGLU libXmu libXi xorg-vfbserver glew \
qt4-corelib qt4-gui qt4-moc qt4-opengl qt4-qmake qt4-rcc qt4-uic \
opencsg cgal curl imagemagick
@ -41,7 +41,7 @@ get_netbsd_deps()
get_opensuse_deps()
{
sudo zypper install libeigen2-devel mpfr-devel gmp-devel boost-devel \
sudo zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl
}
@ -57,7 +57,7 @@ get_debian_deps()
{
for pkg in build-essential libqt4-dev libqt4-opengl-dev \
libxmu-dev cmake bison flex git-core libboost-all-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev \
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \
python-paramiko curl imagemagick; do
sudo apt-get -y install $pkg;

View File

@ -47,7 +47,7 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
<li><a href="http://gmplib.org/">GNU GMP</a>
<li><a href="http://www.mpfr.org/">GNU MPFR</a>
<li><a href="http://www.cgal.org">CGAL</a>
<li><a href="http://eigen.tuxfamily.org">Eigen2</a>
<li><a href="http://eigen.tuxfamily.org">Eigen</a>
<li><a href="http://www.opencsg.org">OpenCSG</a>
<li><a href="http://www.opengl.org/">OpenGL</a>
<li><a href="http://glew.sourceforge.net">GLEW</a>

View File

@ -159,9 +159,21 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
PRINT("Hull() currently requires a valid 2-manifold. Please modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export");
}
else {
chN.p3->convert_to_Polyhedron(P);
std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
boost::bind(static_cast<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
bool err = false;
std::string errmsg("");
try {
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(chN.p3), P );
//chN.p3->convert_to_Polyhedron(P);
} catch (const CGAL::Failure_exception &e) {
err = true;
errmsg = std::string(e.what());
}
if (err) {
PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", errmsg);
} else {
std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
boost::bind(static_cast<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
}
}
}
chnode->progress_report();

352
src/CGAL_Nef3_workaround.h Normal file
View File

@ -0,0 +1,352 @@
// Copyright (c) 1997-2002,2005 Max-Planck-Institute Saarbruecken (Germany).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/releases/CGAL-4.0-branch/Nef_3/include/CGAL/Nef_helper_3.h $
// $Id: Nef_helper_3.h 67117 2012-01-13 18:14:48Z lrineau $
//
//
// Author(s) : Michael Seel <seel@mpi-sb.mpg.de>
// Miguel Granados <granados@mpi-sb.mpg.de>
// Susan Hert <hert@mpi-sb.mpg.de>
// Lutz Kettner <kettner@mpi-sb.mpg.de>
// Ralf Osbild <osbild@mpi-sb.mpg.de>
// Peter Hachenberger <hachenberger@mpi-sb.mpg.de>
/*
modified by don bright for OpenSCAD, 2013.
This works around issue #410, where CGAL's Nef_Polyhedron3.convert_to_Polyhedron
throws an uncatchable exception, due to an CGAL_Assertion being thrown in
Polyhedron Incremental Builder's destructor while a Triangulation exception
is still active, resulting in program termination (crashing).
The purpose here is not to improve/change the way CGAL's Nef code works,
but instead to tweak it slightly to prevent OpenSCAD from crashing. The
behavior of the code should otherwise be exactly the same as CGAL's standard
code.
This file was created by copying three sections
from CGAL's Nef_polyhedron3.h that were protected/private:
Triangulation_handler2
Build_Polyhedron
convert_to_polyhedron
Very small code changes have been made. First, there are many template
type specifiers added to enable the movement of the code to the outside
of the Nef Polyhedron class. Second, there is a try{}catch(...){} block
added in the Builder around the Triangulation code. Third, there is an error
variable added for non-Exception communication with the caller.
Eventually, if CGAL itself is updated and the update is widely
distributed, this file may become obsolete and can be deleted from OpenSCAD
*/
#ifndef _CGAL_NEF3_WORKAROUND_H
#define _CGAL_NEF3_WORKAROUND_H
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/assertions.h>
#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Triangulation_data_structure_2.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/Projection_traits_xz_3.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include "printutils.h"
namespace nefworkaround {
template<typename Kernel, typename Nef>
class Triangulation_handler2 {
typedef typename CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef typename CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef typename CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef typename CGAL::Constrained_triangulation_2<Kernel,TDS> CT;
typedef typename CT::Face_handle Face_handle;
typedef typename CT::Vertex_handle CTVertex_handle;
typedef typename CT::Finite_faces_iterator Finite_face_iterator;
typedef typename CT::Edge Edge;
CT ct;
CGAL::Unique_hash_map<Face_handle, bool> visited;
CGAL::Unique_hash_map<CTVertex_handle, typename Nef::Vertex_const_handle> ctv2v;
Finite_face_iterator fi;
typename Nef::Plane_3 supporting_plane;
public:
Triangulation_handler2(typename Nef::Halffacet_const_handle f) :
visited(false), supporting_plane(f->plane()) {
typename Nef::Halffacet_cycle_const_iterator fci;
for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
if(fci.is_shalfedge()) {
typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
CGAL_For_all(sfc,send) {
CGAL_NEF_TRACEN(" insert point" << sfc->source()->source()->point());
CTVertex_handle ctv = ct.insert(sfc->source()->source()->point());
ctv2v[ctv] = sfc->source()->source();
}
}
}
for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
if(fci.is_shalfedge()) {
typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
CGAL_For_all(sfc,send) {
CGAL_NEF_TRACEN(" insert constraint" << sfc->source()->source()->point()
<< "->" << sfc->source()->twin()->source()->point());
ct.insert_constraint(sfc->source()->source()->point(),
sfc->source()->twin()->source()->point());
}
}
}
CGAL_assertion(ct.is_valid());
CGAL_NEF_TRACEN("number of finite triangles " << ct.number_of_faces());
typename CT::Face_handle infinite = ct.infinite_face();
typename CT::Vertex_handle ctv = infinite->vertex(1);
if(ct.is_infinite(ctv)) ctv = infinite->vertex(2);
CGAL_assertion(!ct.is_infinite(ctv));
typename CT::Face_handle opposite;
typename CT::Face_circulator vc(ctv,infinite);
do { opposite = vc++;
} while(!ct.is_constrained(typename CT::Edge(vc,vc->index(opposite))));
typename CT::Face_handle first = vc;
CGAL_assertion(!ct.is_infinite(first));
traverse_triangulation(first, first->index(opposite));
fi = ct.finite_faces_begin();
}
void traverse_triangulation(Face_handle f, int parent) {
visited[f] = true;
if(!ct.is_constrained(Edge(f,ct.cw(parent))) && !visited[f->neighbor(ct.cw(parent))]) {
Face_handle child(f->neighbor(ct.cw(parent)));
traverse_triangulation(child, child->index(f));
}
if(!ct.is_constrained(Edge(f,ct.ccw(parent))) && !visited[f->neighbor(ct.ccw(parent))]) {
Face_handle child(f->neighbor(ct.ccw(parent)));
traverse_triangulation(child, child->index(f));
}
}
template<typename Triangle_3>
bool get_next_triangle(Triangle_3& tr) {
while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
if(fi == ct.finite_faces_end()) return false;
tr = Triangle_3(fi->vertex(0)->point(), fi->vertex(1)->point(), fi->vertex(2)->point());
++fi;
return true;
}
bool same_orientation(typename Nef::Plane_3 p1) const {
if(p1.a() != 0)
return CGAL::sign(p1.a()) == CGAL::sign(supporting_plane.a());
if(p1.b() != 0)
return CGAL::sign(p1.b()) == CGAL::sign(supporting_plane.b());
return CGAL::sign(p1.c()) == CGAL::sign(supporting_plane.c());
}
template<typename PIB, typename Index>
void handle_triangles(PIB& pib, Index& VI) {
while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
while(fi != ct.finite_faces_end()) {
typename Nef::Plane_3 plane(fi->vertex(0)->point(),
fi->vertex(1)->point(),
fi->vertex(2)->point());
pib.begin_facet();
if(same_orientation(plane)) {
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
} else {
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
}
pib.end_facet();
do {
++fi;
} while(fi != ct.finite_faces_end() && visited[fi] == false);
}
}
};
template <class HDS, typename Kernel, typename Nef>
class Build_polyhedron : public CGAL::Modifier_base<HDS> {
public:
bool error; // added for OpenSCAD
class Visitor {
typedef typename CGAL::Projection_traits_xy_3<Kernel> XY;
typedef typename CGAL::Projection_traits_yz_3<Kernel> YZ;
typedef typename CGAL::Projection_traits_xz_3<Kernel> XZ;
const CGAL::Object_index<typename Nef::Vertex_const_iterator>& VI;
CGAL::Polyhedron_incremental_builder_3<HDS>& B;
const typename Nef::SNC_const_decorator& D;
public:
bool error;//added for OpenSCAD
Visitor(CGAL::Polyhedron_incremental_builder_3<HDS>& BB,
const typename Nef::SNC_const_decorator& sd,
CGAL::Object_index<typename Nef::Vertex_const_iterator>& vi) : VI(vi), B(BB), D(sd), error(false) {}
void visit(typename Nef::Halffacet_const_handle opposite_facet) {
CGAL_NEF_TRACEN("Build_polyhedron: visit facet " << opposite_facet->plane());
CGAL_assertion(Nef::Infi_box::is_standard(opposite_facet->plane()));
typename Nef::SHalfedge_const_handle se;
typename Nef::Halffacet_cycle_const_iterator fc;
typename Nef::Halffacet_const_handle f = opposite_facet->twin();
typename Nef::SHalfedge_around_facet_const_circulator
sfc1(f->facet_cycles_begin()), sfc2(sfc1);
if(++f->facet_cycles_begin() != f->facet_cycles_end() ||
++(++(++sfc1)) != sfc2) {
typename Nef::Vector_3 orth = f->plane().orthogonal_vector();
int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1;
c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c;
try{ // added for OpenSCAD
if(c == 0) {
Triangulation_handler2<YZ,Nef> th(f);
th.handle_triangles(B, VI);
} else if(c == 1) {
Triangulation_handler2<XZ,Nef> th(f);
th.handle_triangles(B, VI);
} else if(c == 2) {
Triangulation_handler2<XY,Nef> th(f);
th.handle_triangles(B, VI);
} else
CGAL_error_msg( "wrong value");
} catch(...) { // added for OpenSCAD
PRINT("ERROR: CGAL NefPolyhedron Triangulation failed"); // added for OpenSCAD
this->error=true; //added for OpenSCAD
} // added for OpenSCAD
} else {
B.begin_facet();
fc = f->facet_cycles_begin();
se = typename Nef::SHalfedge_const_handle(fc);
CGAL_assertion(se!=0);
typename Nef::SHalfedge_around_facet_const_circulator hc_start(se);
typename Nef::SHalfedge_around_facet_const_circulator hc_end(hc_start);
CGAL_For_all(hc_start,hc_end) {
CGAL_NEF_TRACEN(" add vertex " << hc_start->source()->center_vertex()->point());
B.add_vertex_to_facet(VI[hc_start->source()->center_vertex()]);
}
B.end_facet();
}
}
void visit(typename Nef::SFace_const_handle) {}
void visit(typename Nef::Halfedge_const_handle) {}
void visit(typename Nef::Vertex_const_handle) {}
void visit(typename Nef::SHalfedge_const_handle) {}
void visit(typename Nef::SHalfloop_const_handle) {}
};
public:
const typename Nef::SNC_const_decorator& scd;
CGAL::Object_index<typename Nef::Vertex_const_iterator> VI;
Build_polyhedron(const typename Nef::SNC_const_decorator& s) : error(false),
scd(s), VI(s.vertices_begin(),s.vertices_end(),'V') {}
void operator()( HDS& hds) {
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
int skip_volumes;
if(Nef::Infi_box::extended_kernel()) {
B.begin_surface(scd.number_of_vertices()-8,
scd.number_of_facets()-6,
scd.number_of_edges()-12);
skip_volumes = 2;
}
else {
B.begin_surface(scd.number_of_vertices(),
2*scd.number_of_vertices()-4,
3*scd.number_of_vertices()-6);
skip_volumes = 1;
}
int vertex_index = 0;
typename Nef::Vertex_const_iterator v;
CGAL_forall_vertices(v,scd) {
if(Nef::Infi_box::is_standard(v->point())) {
VI[v]=vertex_index++;
B.add_vertex(v->point());
}
}
Visitor V(B,scd,VI);
typename Nef::Volume_const_handle c;
CGAL_forall_volumes(c,scd)
if(skip_volumes-- <= 0) {
scd.visit_shell_objects(typename Nef:: SFace_const_handle(c->shells_begin()),V);
}
B.end_surface();
this->error=B.error()||V.error; // added for OpenSCAD
if (B.error()) B.rollback(); // added for OpenSCAD
}
};
template <typename Kernel>
bool convert_to_Polyhedron( const CGAL::Nef_polyhedron_3<Kernel> &N, CGAL::Polyhedron_3<Kernel> &P )
{
// several lines here added for OpenSCAD
typedef typename CGAL::Nef_polyhedron_3<Kernel> Nef3;
typedef typename CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
CGAL_precondition(N.is_simple());
P.clear();
Build_polyhedron<HalfedgeDS,Kernel,Nef3> bp(N);
P.delegate(bp);
return bp.error;
}
} //namespace nefworkaround
#endif

View File

@ -96,13 +96,22 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
}
else if (this->dim == 3) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
bool err = true;
std::string errmsg("");
CGAL_Polyhedron P;
try {
CGAL_Polyhedron P;
this->p3->convert_to_Polyhedron(P);
ps = createPolySetFromPolyhedron(P);
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(this->p3), P );
//this->p3->convert_to_Polyhedron(P);
}
catch (const CGAL::Precondition_exception &e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
catch (const CGAL::Failure_exception &e) {
err = true;
errmsg = std::string(e.what());
}
if (err) {
PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed.");
if (errmsg!="") PRINTB("ERROR: %s",errmsg);
} else {
ps = createPolySetFromPolyhedron(P);
}
CGAL::set_error_behaviour(old_behaviour);
}

View File

@ -8,7 +8,7 @@ void CocoaUtils::endApplication()
object:nil];
}
void CocoaUtils::nslog(const std::string &str, void *userdata)
void CocoaUtils::nslog(const std::string &str, void * /* userdata */)
{
NSLog([NSString stringWithUTF8String: str.c_str()]);
NSLog(@"%s", str.c_str());
}

View File

@ -164,6 +164,7 @@ public slots:
void viewCenter();
void viewPerspective();
void viewOrthogonal();
void viewResetView();
void hideConsole();
void animateUpdateDocChanged();
void animateUpdate();

View File

@ -213,6 +213,8 @@
<addaction name="viewActionDiagonal"/>
<addaction name="viewActionCenter"/>
<addaction name="separator"/>
<addaction name="viewActionResetView"/>
<addaction name="separator"/>
<addaction name="viewActionPerspective"/>
<addaction name="viewActionOrthogonal"/>
<addaction name="separator"/>
@ -697,6 +699,11 @@
<string>Show Library Folder...</string>
</property>
</action>
<action name="viewActionResetView">
<property name="text">
<string>Reset View</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -84,7 +84,7 @@ std::string PlatformUtils::info()
#ifdef QT_VERSION
std::string qtVersion = qVersion();
#else
std::string qtVersion = "Qt disabled";
std::string qtVersion = "Qt disabled - Commandline Test Version";
#endif
#ifdef ENABLE_CGAL

View File

@ -457,7 +457,14 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
{
double max_x = 0;
for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]);
double point_x = dxf.points[dxf.paths[i].indices[j]][0];
if (point_x < 0) {
PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates");
PRINTB("[Point %d on path %d has X coordinate %f]", j % i % point_x);
delete ps;
return NULL;
}
max_x = fmax(max_x, point_x);
}
int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);

View File

@ -64,9 +64,7 @@ static bool running_under_wine = false;
void QGLView::init()
{
cam.type = Camera::GIMBAL;
cam.object_rot << 35, 0, -25;
cam.object_trans << 0, 0, 0;
cam.viewer_distance = 500;
resetView();
this->mouse_drag_active = false;
this->statusLabel = NULL;
@ -83,6 +81,13 @@ void QGLView::init()
#endif
}
void QGLView::resetView()
{
cam.object_rot << 35, 0, -25;
cam.object_trans << 0, 0, 0;
cam.viewer_distance = 500;
}
void QGLView::initializeGL()
{
GLenum err = glewInit();

View File

@ -41,6 +41,7 @@ public:
}
std::string getRendererInfo() const;
bool save(const char *filename);
void resetView();
public:
QLabel *statusLabel;

View File

@ -27,6 +27,7 @@ using boost::uintmax_t;
#include <CGAL/Cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <CGAL_Nef3_workaround.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
@ -48,9 +49,10 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2;
typedef CGAL::Polygon_2<CGAL_ExactKernel2> CGAL_Poly2;
typedef CGAL::Polygon_with_holes_2<CGAL_ExactKernel2> CGAL_Poly2h;
//typedef CGAL::Cartesian<NT> CGAL_Kernel3;
typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
typedef CGAL::Gmpq NT3;
typedef CGAL::Cartesian<NT3> CGAL_Kernel3;
//typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
//typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3;
typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;

View File

@ -78,12 +78,14 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst
Context c(ctx);
if (it_values.type() == Value::RANGE) {
Value::RangeType range = it_values.toRange();
range.normalize();
if (range.nbsteps()<10000) {
for (double i = range.begin; i <= range.end; i += range.step) {
c.set_variable(it_name, Value(i));
for_eval(node, inst, l+1, &c, evalctx);
}
uint32_t steps = range.nbsteps();
if (steps >= 10000) {
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
} else {
for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
c.set_variable(it_name, Value(*it));
for_eval(node, inst, l+1, &c, evalctx);
}
}
}
else if (it_values.type() == Value::VECTOR) {
@ -227,13 +229,13 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
else if (value.type() == Value::RANGE) {
AbstractNode* node = new AbstractNode(inst);
Value::RangeType range = value.toRange();
range.normalize();
if (range.nbsteps()>=10000) {
PRINTB("WARNING: Bad range parameter for children: too many elements (%d).", (int)((range.begin-range.end)/range.step));
uint32_t steps = range.nbsteps();
if (steps >= 10000) {
PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps);
return NULL;
}
for (double i = range.begin; i <= range.end; i += range.step) {
AbstractNode* childnode = getChild(Value(i),modulectx); // with error cases
for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
AbstractNode* childnode = getChild(Value(*it),modulectx); // with error cases
if (childnode==NULL) continue; // error
node->children.push_back(childnode);
}

View File

@ -28,11 +28,7 @@ public:
}
};
#ifdef __APPLE__
std::vector<Vector2d, Eigen::aligned_allocator<Vector2d> > points;
#else
std::vector<Vector2d> points;
#endif
std::vector<Path> paths;
std::vector<Dim> dims;

View File

@ -108,3 +108,17 @@ void Editor::wheelEvent ( QWheelEvent * event )
}
}
void Editor::setPlainText(const QString &text)
{
int y = verticalScrollBar()->sliderPosition();
// Save current cursor position
QTextCursor cursor = textCursor();
int n = cursor.position();
QTextEdit::setPlainText(text);
// Restore cursor position
if (n < text.length()) {
cursor.setPosition(n);
setTextCursor(cursor);
verticalScrollBar()->setSliderPosition(y);
}
}

View File

@ -2,6 +2,7 @@
#include <QString>
#include <QWidget>
#include <QWheelEvent>
#include <QScrollBar>
#include <QTextEdit>
class Editor : public QTextEdit
@ -9,6 +10,7 @@ class Editor : public QTextEdit
Q_OBJECT
public:
Editor(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); }
void setPlainText(const QString &text);
public slots:
void zoomIn();
void zoomOut();

View File

@ -42,7 +42,12 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output)
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
root_N->p3->convert_to_Polyhedron(P);
//root_N->p3->convert_to_Polyhedron(P);
bool err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(root_N->p3), P );
if (err) {
PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
return;
}
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
@ -114,6 +119,9 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output)
catch (const CGAL::Assertion_exception &e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what());
}
catch (...) {
PRINT("CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()");
}
CGAL::set_error_behaviour(old_behaviour);
}

View File

@ -117,11 +117,18 @@ Value Expression::evaluate(const Context *context) const
if (this->type == "R") {
Value v1 = this->children[0]->evaluate(context);
Value v2 = this->children[1]->evaluate(context);
Value v3 = this->children[2]->evaluate(context);
if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER && v3.type() == Value::NUMBER) {
Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble());
return Value(range);
}
if (this->children.size() == 2) {
if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER) {
Value::RangeType range(v1.toDouble(), v2.toDouble());
return Value(range);
}
} else {
Value v3 = this->children[2]->evaluate(context);
if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER && v3.type() == Value::NUMBER) {
Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble());
return Value(range);
}
}
return Value();
}
if (this->type == "V") {
@ -192,7 +199,11 @@ std::string Expression::toString() const
stream << this->const_value;
}
else if (this->type == "R") {
stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << *this->children[2] << "]";
stream << "[" << *this->children[0] << " : " << *this->children[1];
if (this->children.size() > 2) {
stream << " : " << *this->children[2];
}
stream << "]";
}
else if (this->type == "V") {
stream << "[";

View File

@ -4,10 +4,13 @@
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Dense>
#include<Eigen/StdVector>
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d)
using Eigen::Vector2d;
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector3d)
using Eigen::Vector3d;
using Eigen::Vector3f;
typedef Eigen::AlignedBox<double, 3> BoundingBox;
using Eigen::Matrix3f;
using Eigen::Matrix3d;

View File

@ -334,6 +334,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->viewActionBack, SIGNAL(triggered()), this, SLOT(viewAngleBack()));
connect(this->viewActionDiagonal, SIGNAL(triggered()), this, SLOT(viewAngleDiagonal()));
connect(this->viewActionCenter, SIGNAL(triggered()), this, SLOT(viewCenter()));
connect(this->viewActionResetView, SIGNAL(triggered()), this, SLOT(viewResetView()));
connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective()));
connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal()));
connect(this->viewActionHide, SIGNAL(triggered()), this, SLOT(hideConsole()));
@ -503,6 +504,7 @@ MainWindow::openFile(const QString &new_filename)
}
#endif
setFileName(actual_filename);
editor->setPlainText("");
fileChangedOnDisk(); // force cached autoReloadId to update
refreshDocument();
@ -1772,6 +1774,12 @@ void MainWindow::viewOrthogonal()
this->qglview->updateGL();
}
void MainWindow::viewResetView()
{
this->qglview->resetView();
this->qglview->updateGL();
}
void MainWindow::hideConsole()
{
QSettings settings;

View File

@ -162,7 +162,7 @@ void ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *i
#endif
FileContext::FileContext(const class FileModule &module, const Context *parent)
: usedlibs(module.usedlibs), ModuleContext(parent)
: ModuleContext(parent), usedlibs(module.usedlibs)
{
if (!module.modulePath().empty()) this->document_path = module.modulePath();
}

View File

@ -453,7 +453,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
// Only if "fileName" is not absolute, prepend the "absoluteBase".
static QString assemblePath(const fs::path& absoluteBase,
const string& fileName) {
return QDir(QString::fromStdString((const string&) absoluteBase))
return fileName.empty() ? "" : QDir(QString::fromStdString((const string&) absoluteBase))
.absoluteFilePath(QString::fromStdString(fileName));
}
@ -474,6 +474,13 @@ bool QtUseGUI()
int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, char ** argv)
{
#ifdef Q_OS_MACX
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
// fix Mac OS X 10.9 (mavericks) font issue
// https://bugreports.qt-project.org/browse/QTBUG-32789
QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
}
#endif
QApplication app(argc, argv, true); //useGUI);
#ifdef Q_WS_MAC
app.installEventFilter(new EventFilter(&app));

View File

@ -325,11 +325,9 @@ expr:
}
| '[' expr ':' expr ']'
{
Expression *e_one = new Expression(Value(1.0));
$$ = new Expression();
$$->type = "R";
$$->children.push_back($2);
$$->children.push_back(e_one);
$$->children.push_back($4);
}
| '[' expr ':' expr ':' expr ']'

View File

@ -1,4 +1,4 @@
#if defined(__APPLE__) && defined(__GNUC__)
#if defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__)
#include <iostream>

View File

@ -232,7 +232,7 @@ public:
}
std::string operator()(const Value::RangeType &v) const {
return (boost::format("[%1% : %2% : %3%]") % v.begin % v.step % v.end).str();
return (boost::format("[%1% : %2% : %3%]") % v.begin_val % v.step_val % v.end_val).str();
}
};
@ -600,9 +600,9 @@ public:
Value operator()(const Value::RangeType &range, const double &idx) const {
switch(int(idx)) {
case 0: return Value(range.begin);
case 1: return Value(range.step);
case 2: return Value(range.end);
case 0: return Value(range.begin_val);
case 1: return Value(range.step_val);
case 2: return Value(range.end_val);
}
return Value::undefined;
}
@ -617,3 +617,97 @@ Value Value::operator[](const Value &v)
{
return boost::apply_visitor(bracket_visitor(), this->value, v.value);
}
void Value::RangeType::normalize() {
if ((step_val>0) && (end_val < begin_val)) {
std::swap(begin_val,end_val);
PRINT("DEPRECATED: Using ranges of the form [begin:end] with begin value greater than the end value is deprecated.");
}
}
uint32_t Value::RangeType::nbsteps() const {
if (begin_val == end_val) {
return 0;
}
if (step_val == 0) {
return std::numeric_limits<uint32_t>::max();
}
double steps;
if (step_val < 0) {
if (begin_val < end_val) {
return 0;
}
steps = (begin_val - end_val) / (-step_val);
} else {
if (begin_val > end_val) {
return 0;
}
steps = (end_val - begin_val) / step_val;
}
return steps;
}
Value::RangeType::iterator::iterator(Value::RangeType &range, type_t type) : range(range), val(range.begin_val)
{
this->type = type;
update_type();
}
void Value::RangeType::iterator::update_type()
{
if (range.step_val == 0) {
type = RANGE_TYPE_END;
} else if (range.step_val < 0) {
if (val < range.end_val) {
type = RANGE_TYPE_END;
}
} else {
if (val > range.end_val) {
type = RANGE_TYPE_END;
}
}
}
Value::RangeType::iterator::reference Value::RangeType::iterator::operator*()
{
return val;
}
Value::RangeType::iterator::pointer Value::RangeType::iterator::operator->()
{
return &(operator*());
}
Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++()
{
if (type < 0) {
type = RANGE_TYPE_RUNNING;
}
val += range.step_val;
update_type();
return *this;
}
Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++(int)
{
self_type tmp(*this);
operator++();
return tmp;
}
bool Value::RangeType::iterator::operator==(const self_type &other) const
{
if (type == RANGE_TYPE_RUNNING) {
return (type == other.type) && (val == other.val) && (range == other.range);
} else {
return (type == other.type) && (range == other.range);
}
}
bool Value::RangeType::iterator::operator!=(const self_type &other) const
{
return !(*this == other);
}

View File

@ -11,6 +11,7 @@
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#endif
#include <boost/cstdint.hpp>
class QuotedString : public std::string
{
@ -31,33 +32,64 @@ std::ostream &operator<<(std::ostream &stream, const Filename &filename);
class Value
{
public:
struct RangeType {
RangeType(double begin, double step, double end)
: begin(begin), step(step), end(end) {}
class RangeType {
private:
double begin_val;
double step_val;
double end_val;
/// inverse begin/end if begin is upper than end
void normalize();
bool operator==(const RangeType &other) const {
return this->begin == other.begin &&
this->step == other.step &&
this->end == other.end;
public:
typedef enum { RANGE_TYPE_BEGIN, RANGE_TYPE_RUNNING, RANGE_TYPE_END } type_t;
class iterator {
public:
typedef iterator self_type;
typedef double value_type;
typedef double& reference;
typedef double* pointer;
typedef std::forward_iterator_tag iterator_category;
typedef double difference_type;
iterator(RangeType &range, type_t type);
self_type operator++();
self_type operator++(int junk);
reference operator*();
pointer operator->();
bool operator==(const self_type& other) const;
bool operator!=(const self_type& other) const;
private:
RangeType &range;
double val;
type_t type;
void update_type();
};
RangeType(double begin, double end)
: begin_val(begin), step_val(1.0), end_val(end)
{
normalize();
}
/// inverse begin/end if begin is upper than end
void normalize() {
if ((step>0) && (end < begin)) {
std::swap(begin,end);
}
}
/// return number of steps, max int value if step is null
int nbsteps() const {
if (step<=0) {
return std::numeric_limits<int>::max();
}
return (int)((begin-end)/step);
}
RangeType(double begin, double step, double end)
: begin_val(begin), step_val(step), end_val(end) {}
double begin;
double step;
double end;
bool operator==(const RangeType &other) const {
return this->begin_val == other.begin_val &&
this->step_val == other.step_val &&
this->end_val == other.end_val;
}
iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); }
iterator end() { return iterator(*this, RANGE_TYPE_END); }
/// return number of steps, max uint32_t value if step is 0
uint32_t nbsteps() const;
friend class tostring_visitor;
friend class bracket_visitor;
};
typedef std::vector<Value> VectorType;

View File

@ -35,8 +35,8 @@ a time, to avoid confusion.
#include <Eigen/Core>
#if not EIGEN_VERSION_AT_LEAST( 2,0,13 )
#error eigen2 library missing or version too old. See README.md. To force compile, run qmake CONFIG+=skip-version-check
#if not EIGEN_VERSION_AT_LEAST( 3,0,0 )
#error eigen library missing or version too old. See README.md. To force compile, run qmake CONFIG+=skip-version-check
#else
@ -104,9 +104,17 @@ a time, to avoid confusion.
#endif // ENABLE_CGAL
#endif // Boost
#endif // Eigen2
#endif // Eigen
#endif // MPFR
#endif // GMP
// see github issue #552
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION == 40802
#error "OpenSCAD isnt compatible with gcc 4.8.2. Please try a different version"
#endif
#endif // OPENSCAD_SKIP_VERSION_CHECK

View File

@ -22,10 +22,10 @@ for(r=[1:2:6]) translate([r*10-30,30,0]) difference() {cylinder(r=r, center=true
for(r=[1.5:0.2:2.5]) translate([r*10-30,30,0]) cube([1, 4*r, 2], center=true);
// Negative range, negative step
for(r=[5:-1:1]) translate([r*10-60,40,0]) cylinder(r=r);
for(r=[5:-1:1]) translate([r*10-30,50,0]) cylinder(r=r);
// Negative range, positive step
for(r=[5:1:1]) translate([r*10-30,40,0]) cylinder(r=r);
// Negative range, positive step (using backward compatible auto swap of begin and end)
for(r=[5:1]) translate([r*10-30,40,0]) cylinder(r=r);
// Zero step

View File

@ -15,4 +15,10 @@ translate([-2,0,0]) polygon(points);
translate([-2,-2,0]) polygon(points=points, paths=[[0,1,2,3], [4,5,6,7]]);
translate([2,-4,0]) polygon([[0,0], [1,0], [1,1], [0,0]]);
// With hole
translate([-2,-4,0])
polygon(points=[[0,0], [1,0], [1,1], [0,1], [0.2,0.2], [0.8,0.2], [0.8,0.8], [0.2,0.8]],
paths=[[0,1,2,3],[4,5,6,7]]
);
// FIXME: convexity

View File

@ -0,0 +1,15 @@
// 2D child
projection(cut=true) { square(); }
projection(cut=true) translate([20,0,0]) cube(10, center=true);
// Boundary case: clipping the top of a cube
translate([0,20,0]) projection(cut=true) translate([0,0,-4.999999]) cube(10, center=true);
// holes
translate([0,-10,0]) 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); }
}
}

View File

@ -0,0 +1,3 @@
// Linear extrude
translate([22,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,9]) sphere(r=10);
translate([44,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,7]) sphere(r=10);

View File

@ -3,18 +3,25 @@ projection();
// No children
projection() { }
// 2D child
projection(cut=true) { square(); }
projection() { square(); }
linear_extrude(height=20) projection(cut=false) sphere(r=10);
translate([22,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,9]) sphere(r=10);
translate([44,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,7]) sphere(r=10);
// Simple
projection(cut=false) cube(10);
// 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);
// Two children
translate([-12,0]) projection(cut=false) {
cube(10);
difference() {
sphere(10);
cylinder(h=30, r=5, 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); }
}
// Holes
translate([6,-12]) projection(cut=false) {
cube(10);
difference() {
sphere(10);
cylinder(h=30, r=5, center=true);
}
}

View File

@ -1,6 +1,11 @@
render() {
difference() {
square(100, center=true);
circle(r=30);
square(10, center=true);
circle(r=3);
}
}
translate([12,0,0]) render() {
square(10, center=true);
circle(r=3);
}

View File

@ -53,7 +53,7 @@ module test_children_range() {
children([0:4]); // all
children([1:2]); // child2, child3
children([0:2:4]); // child1, child3, child5
children([4:-1:0]); // out, out
children([0:-1:4]); // out, out
echo("Children range: end");
}
test_children_range() {

20
testdata/scad/misc/range-tests.scad vendored Normal file
View File

@ -0,0 +1,20 @@
echo("[a01] ----- [1:4]"); for (a = [1:4]) echo ("[a01] ", a);
echo("[a02] ----- [4:1]"); for (a = [4:1]) echo ("[a02] ", a);
echo("[a03] ----- [0:0]"); for (a = [0:0]) echo ("[a03] ", a);
echo("[a04] ----- [0:3]"); for (a = [0:3]) echo ("[a04] ", a);
echo("[a05] ----- [-3:0]"); for (a = [-3:0]) echo ("[a05] ", a);
echo("[a06] ----- [0:-3]"); for (a = [0:-3]) echo ("[a06] ", a);
echo("[a07] ----- [-2:2]"); for (a = [-2:2]) echo ("[a07] ", a);
echo("[a08] ----- [2:-2]"); for (a = [2:-2]) echo ("[a08] ", a);
echo("[b01] ----- [1:1:5]"); for (a = [1:1:5]) echo ("[b01] ", a);
echo("[b02] ----- [1:2:5]"); for (a = [1:2:5]) echo ("[b02] ", a);
echo("[b03] ----- [1:-1:5]"); for (a = [1:-1:5]) echo ("[b03] ", a);
echo("[b04] ----- [5:1:1]"); for (a = [5:1:1]) echo ("[b04] ", a);
echo("[b05] ----- [5:2:1]"); for (a = [5:2:1]) echo ("[b05] ", a);
echo("[b06] ----- [5:-1:1]"); for (a = [5:-1:1]) echo ("[b06] ", a);
echo("[b07] ----- [0:0:0]"); for (a = [0:0:0]) echo ("[b07] ", a);
echo("[b08] ----- [1:0:1]"); for (a = [1:0:1]) echo ("[b08] ", a);
echo("[b09] ----- [1:0:5]"); for (a = [1:0:5]) echo ("[b09] ", a);
echo("[b10] ----- [0:1:0]"); for (a = [0:1:0]) echo ("[b10] ", a);
echo("[b11] ----- [3:-.5:-3]"); for (a = [3:-.5:-3]) echo ("[b11] ", a);

View File

@ -1,6 +1,6 @@
# instructions - see ../doc/testing.txt
# set(DEBUG_OSCD 1) # print debug info during cmake
#set(DEBUG_OSCD 1) # print debug info during cmake
cmake_minimum_required(VERSION 2.8)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
@ -15,8 +15,15 @@ include(CMakeParseArguments.cmake)
# Detect Lion and force gcc
IF (APPLE)
EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION)
IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
message("Detected Mountain Lion (10.8) or later")
IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.9.0")
message("Detected Maverick (10.9) or later")
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
# Somehow, since we build dependencies for 10.7, we need to also build executables
# for 10.7. This used to not be necessary, but since 10.9 it apparently is..
SET(CMAKE_OSX_DEPLOYMENT_TARGET 10.7 CACHE STRING "Deployment target")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
message("Detected Mountain Lion (10.8)")
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0")
@ -212,30 +219,21 @@ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
endif()
# Priority
# 3. EIGENDIR if set (EIGEN2DIR for backwards compatability)
# 3. EIGENDIR if set
# 1. OPENSCAD_LIBRARIES eigen3
# 2. OPENSCAD_LIBRARIES eigen2
# 4. system's standard include paths for eigen3
# 5. system's standard include paths for eigen2
set(EIGEN2_DIR "$ENV{EIGEN2DIR}")
set(EIGEN_DIR "$ENV{EIGENDIR}")
set(OPENSCAD_LIBDIR "$ENV{OPENSCAD_LIBRARIES}")
if (EIGEN_DIR)
set(EIGHINT ${EIGEN_DIR}/include/eigen3 ${EIGEN_DIR}/include/eigen2 ${EIGEN_DIR})
set(EIGHINT ${EIGEN_DIR}/include/eigen3 ${EIGEN_DIR})
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${EIGHINT})
endif()
if (EIGEN2_DIR)
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${EIGEN2_DIR}/include/eigen2 ${EIGEN2_DIR})
endif()
if (NOT EIGEN_INCLUDE_DIR)
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${OPENSCAD_LIBDIR}/include/eigen3)
endif()
if (NOT EIGEN_INCLUDE_DIR)
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${OPENSCAD_LIBDIR}/include/eigen2)
endif()
if (NOT EIGEN_INCLUDE_DIR)
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
@ -249,18 +247,6 @@ if (NOT EIGEN_INCLUDE_DIR)
endif()
endif()
if (NOT EIGEN_INCLUDE_DIR)
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS /usr/local/include/eigen2)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS /usr/pkg/include/eigen2)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS /opt/local/include/eigen2)
else()
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS /usr/include/eigen2)
endif()
endif()
if (NOT EIGEN_INCLUDE_DIR)
message(STATUS "Eigen not found")
else()
@ -450,9 +436,11 @@ message(STATUS "OpenSCAD version: ${VERSION}")
string(REGEX MATCHALL "^[0-9]+|[0-9]+|[0-9]+$" MYLIST "${VERSION}")
list(GET MYLIST 0 OPENSCAD_YEAR)
list(GET MYLIST 1 OPENSCAD_MONTH)
math(EXPR OPENSCAD_MONTH ${OPENSCAD_MONTH}) # get rid of leading zero
list(LENGTH MYLIST VERSIONLEN)
if (${VERSIONLEN} EQUAL 3)
list(GET MYLIST 2 OPENSCAD_DAY)
math(EXPR OPENSCAD_DAY ${OPENSCAD_DAY}) # get rid of leading zero
endif()
add_definitions(-DOPENSCAD_VERSION=${VERSION} -DOPENSCAD_YEAR=${OPENSCAD_YEAR} -DOPENSCAD_MONTH=${OPENSCAD_MONTH})
@ -802,7 +790,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/expression-shortcircuit-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/parent_module-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/children-tests.scad)
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/children-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/range-tests.scad)
list(APPEND DUMPTEST_FILES ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
@ -812,12 +801,14 @@ list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allexpressions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allfunctions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allmodules.scad)
list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILES})
list(APPEND CGALPNGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad)
@ -840,6 +831,10 @@ disable_tests(openscad-csgpng_child-background)
disable_tests(opencsgtest_example006 cgalpngtest_example006)
disable_tests(openscad-csgpng_example006 openscad-cgalpng_example006)
# NefPolyhedron->Polyhedron conversion failures. No images for OpenCSG/Thrown
disable_tests(opencsgtest_stl-cgal-convert_to_Polyhedron-crash)
disable_tests(throwntogethertest_stl-cgal-convert_to_Polyhedron-crash)
# These tests only makes sense in OpenCSG mode
disable_tests(cgalpngtest_child-background
cgalpngtest_highlight-and-background-modifier
@ -852,12 +847,8 @@ disable_tests(cgalpngtest_child-background
# Test config handling
set_test_config(Heavy opencsgtest_minkowski3-tests
opencsgtest_projection-tests
openscad-csgpng_minkowski3-tests
openscad-csgpng_projection-tests
throwntogethertest_minkowski3-tests
throwntogethertest_projection-tests
cgalpngtest_projection-tests
cgalpngtest_rotate_extrude-tests
cgalpngtest_surface-tests
cgalpngtest_sphere-tests

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -80,7 +80,23 @@ group() {
cube(size = [1, 9.2, 2], center = true);
}
}
group();
group() {
multmatrix([[1, 0, 0, 20], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
}
multmatrix([[1, 0, 0, 10], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 4, r2 = 4, center = false);
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 3, r2 = 3, center = false);
}
multmatrix([[1, 0, 0, -10], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 2, r2 = 2, center = false);
}
multmatrix([[1, 0, 0, -20], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = false);
}
}
group() {
multmatrix([[1, 0, 0, -20], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = false);

View File

@ -33,4 +33,7 @@ group() {
multmatrix([[1, 0, 0, 2], [0, 1, 0, -4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polygon(points = [[0, 0], [1, 0], [1, 1], [0, 0]], paths = undef, convexity = 1);
}
multmatrix([[1, 0, 0, -2], [0, 1, 0, -4], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polygon(points = [[0, 0], [1, 0], [1, 1], [0, 1], [0.2, 0.2], [0.8, 0.2], [0.8, 0.8], [0.2, 0.8]], paths = [[0, 1, 2, 3], [4, 5, 6, 7]], convexity = 1);
}
}

View File

@ -0,0 +1,33 @@
group() {
projection(cut = true, convexity = 0) {
square(size = [1, 1], center = false);
}
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cube(size = [10, 10, 10], center = true);
}
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]]) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -4.999999], [0, 0, 0, 1]]) {
cube(size = [10, 10, 10], center = true);
}
}
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, -10], [0, 0, 1, 0], [0, 0, 0, 1]]) {
projection(cut = true, convexity = 0) {
union() {
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);
}
}
}
}
}
}

View File

@ -0,0 +1,20 @@
group() {
multmatrix([[1, 0, 0, 22], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 9], [0, 0, 0, 1]]) {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
}
}
}
}
multmatrix([[1, 0, 0, 44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 7], [0, 0, 0, 1]]) {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
}
}
}
}
}

View File

@ -1,56 +1,27 @@
group() {
projection(cut = false, convexity = 0);
projection(cut = false, convexity = 0);
projection(cut = true, convexity = 0) {
projection(cut = false, convexity = 0) {
square(size = [1, 1], center = false);
}
linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = false, convexity = 0) {
cube(size = [10, 10, 10], center = false);
}
multmatrix([[1, 0, 0, -12], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
projection(cut = false, convexity = 0) {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
}
}
multmatrix([[1, 0, 0, 22], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 9], [0, 0, 0, 1]]) {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
}
cube(size = [10, 10, 10], center = false);
difference() {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 5, r2 = 5, center = true);
}
}
}
multmatrix([[1, 0, 0, 44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 7], [0, 0, 0, 1]]) {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
}
}
}
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, -22], [0, 0, 1, 0], [0, 0, 0, 1]]) {
linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
projection(cut = true, convexity = 0) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -4.999999], [0, 0, 0, 1]]) {
cube(size = [10, 10, 10], center = true);
}
}
}
}
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, scale = [1, 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);
}
}
}
multmatrix([[1, 0, 0, 6], [0, 1, 0, -12], [0, 0, 1, 0], [0, 0, 0, 1]]) {
projection(cut = false, convexity = 0) {
cube(size = [10, 10, 10], center = false);
difference() {
sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 5, r2 = 5, center = true);
}
}
}

View File

@ -1,8 +1,14 @@
group() {
render(convexity = 1) {
difference() {
square(size = [100, 100], center = true);
circle($fn = 0, $fa = 12, $fs = 2, r = 30);
square(size = [10, 10], center = true);
circle($fn = 0, $fa = 12, $fs = 2, r = 3);
}
}
multmatrix([[1, 0, 0, 12], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
render(convexity = 1) {
square(size = [10, 10], center = true);
circle($fn = 0, $fa = 12, $fs = 2, r = 3);
}
}
}

View File

@ -0,0 +1,5 @@
group() {
render(convexity = 1) {
import(file = "stl-cgal-convert_to_Polyhedron-crash.stl", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
}
}

View File

@ -31,5 +31,4 @@ ECHO: "child3"
ECHO: "child1"
ECHO: "child3"
ECHO: "child5"
WARNING: Bad range parameter for children: too many elements (-4).
ECHO: "Children range: end"

View File

@ -0,0 +1,81 @@
ECHO: "[a01] ----- [1:4]"
ECHO: "[a01] ", 1
ECHO: "[a01] ", 2
ECHO: "[a01] ", 3
ECHO: "[a01] ", 4
ECHO: "[a02] ----- [4:1]"
DEPRECATED: Using ranges of the form [begin:end] with begin value greater than the end value is deprecated.
ECHO: "[a02] ", 1
ECHO: "[a02] ", 2
ECHO: "[a02] ", 3
ECHO: "[a02] ", 4
ECHO: "[a03] ----- [0:0]"
ECHO: "[a03] ", 0
ECHO: "[a04] ----- [0:3]"
ECHO: "[a04] ", 0
ECHO: "[a04] ", 1
ECHO: "[a04] ", 2
ECHO: "[a04] ", 3
ECHO: "[a05] ----- [-3:0]"
ECHO: "[a05] ", -3
ECHO: "[a05] ", -2
ECHO: "[a05] ", -1
ECHO: "[a05] ", 0
ECHO: "[a06] ----- [0:-3]"
DEPRECATED: Using ranges of the form [begin:end] with begin value greater than the end value is deprecated.
ECHO: "[a06] ", -3
ECHO: "[a06] ", -2
ECHO: "[a06] ", -1
ECHO: "[a06] ", 0
ECHO: "[a07] ----- [-2:2]"
ECHO: "[a07] ", -2
ECHO: "[a07] ", -1
ECHO: "[a07] ", 0
ECHO: "[a07] ", 1
ECHO: "[a07] ", 2
ECHO: "[a08] ----- [2:-2]"
DEPRECATED: Using ranges of the form [begin:end] with begin value greater than the end value is deprecated.
ECHO: "[a08] ", -2
ECHO: "[a08] ", -1
ECHO: "[a08] ", 0
ECHO: "[a08] ", 1
ECHO: "[a08] ", 2
ECHO: "[b01] ----- [1:1:5]"
ECHO: "[b01] ", 1
ECHO: "[b01] ", 2
ECHO: "[b01] ", 3
ECHO: "[b01] ", 4
ECHO: "[b01] ", 5
ECHO: "[b02] ----- [1:2:5]"
ECHO: "[b02] ", 1
ECHO: "[b02] ", 3
ECHO: "[b02] ", 5
ECHO: "[b03] ----- [1:-1:5]"
ECHO: "[b04] ----- [5:1:1]"
ECHO: "[b05] ----- [5:2:1]"
ECHO: "[b06] ----- [5:-1:1]"
ECHO: "[b06] ", 5
ECHO: "[b06] ", 4
ECHO: "[b06] ", 3
ECHO: "[b06] ", 2
ECHO: "[b06] ", 1
ECHO: "[b07] ----- [0:0:0]"
ECHO: "[b08] ----- [1:0:1]"
ECHO: "[b09] ----- [1:0:5]"
WARNING: Bad range parameter in for statement: too many elements (4294967295).
ECHO: "[b10] ----- [0:1:0]"
ECHO: "[b10] ", 0
ECHO: "[b11] ----- [3:-.5:-3]"
ECHO: "[b11] ", 3
ECHO: "[b11] ", 2.5
ECHO: "[b11] ", 2
ECHO: "[b11] ", 1.5
ECHO: "[b11] ", 1
ECHO: "[b11] ", 0.5
ECHO: "[b11] ", 0
ECHO: "[b11] ", -0.5
ECHO: "[b11] ", -1
ECHO: "[b11] ", -1.5
ECHO: "[b11] ", -2
ECHO: "[b11] ", -2.5
ECHO: "[b11] ", -3

View File

@ -6,7 +6,7 @@ e = $fn;
f1 = [1];
f2 = [1, 2, 3];
g = ((f2.x + f2.y) + f2.z);
h1 = [2 : 1 : 5];
h1 = [2 : 5];
h2 = [1 : 2 : 10];
i = ((h2.begin - h2.step) - h2.end);
j = "test";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -25,544 +25,441 @@
# todo
#
# 1. Note: Wiki code is deprecated. All future development should move to
# create html output (or even xml output). Wiki design was based on the
# wrong assumption of easily accessible public wiki servers with
# auto-upload scripts available. wiki code should be removed and code
# simplified.
#
# to still use wiki, use args '--wiki' and/or '--wiki-upload'
#
# 2. why is hash differing
# 1. why is hash differing
import string,sys,re,os,hashlib,subprocess,textwrap,time,platform
import string
import sys
import re
import os
import hashlib
import subprocess
import time
import platform
try:
from urllib.request import urlopen
from urllib.parse import urlencode
except:
from urllib2 import urlopen
from urllib import urlencode
def tryread(filename):
data = None
try:
f = open(filename,'rb')
data = f.read()
f.close()
except Exception, e:
print 'couldn\'t open ',filename
print type(e), e
return data
data = None
try:
f = open(filename,'rb')
data = f.read()
f.close()
except Exception as e:
print 'couldn\'t open ',filename
print type(e), e
return data
def trysave(filename,data):
dir = os.path.dirname(filename)
try:
if not os.path.isdir(dir):
if not dir == '':
debug( 'creating' + dir)
os.mkdir(dir)
f=open(filename,'wb')
f.write(data)
f.close()
except Exception, e:
print 'problem writing to',filename
print type(e), e
return None
return True
def trysave(filename, data):
dir = os.path.dirname(filename)
try:
if not os.path.isdir(dir):
if not dir == '':
debug( 'creating' + dir)
os.mkdir(dir)
f=open(filename,'wb')
f.write(data)
f.close()
except Exception as e:
print 'problem writing to',filename
print type(e), e
return None
return True
def ezsearch(pattern,str):
x = re.search(pattern,str,re.DOTALL|re.MULTILINE)
if x and len(x.groups())>0: return x.group(1).strip()
return ''
def ezsearch(pattern, str):
x = re.search(pattern,str,re.DOTALL|re.MULTILINE)
if x and len(x.groups())>0: return x.group(1).strip()
return ''
def read_gitinfo():
# won't work if run from outside of branch.
try:
data = subprocess.Popen(['git','remote','-v'],stdout=subprocess.PIPE).stdout.read()
origin = ezsearch('^origin *?(.*?)\(fetch.*?$',data)
upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$',data)
data = subprocess.Popen(['git','branch'],stdout=subprocess.PIPE).stdout.read()
branch = ezsearch('^\*(.*?)$',data)
out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
out += 'Git upstream: ' + upstream + '\n'
except:
out = 'Problem running git'
return out
# won't work if run from outside of branch.
try:
data = subprocess.Popen(['git', 'remote', '-v'], stdout=subprocess.PIPE).stdout.read()
origin = ezsearch('^origin *?(.*?)\(fetch.*?$', data)
upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$', data)
data = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE).stdout.read()
branch = ezsearch('^\*(.*?)$', data)
out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
out += 'Git upstream: ' + upstream + '\n'
except:
out = 'Problem running git'
return out
def read_sysinfo(filename):
data = tryread(filename)
if not data:
sinfo = platform.sys.platform
sinfo += '\nsystem cannot create offscreen GL framebuffer object'
sinfo += '\nsystem cannot create GL based images'
sysid = platform.sys.platform+'_no_GL_renderer'
return sinfo, sysid
data = tryread(filename)
if not data:
sinfo = platform.sys.platform
sinfo += '\nsystem cannot create offscreen GL framebuffer object'
sinfo += '\nsystem cannot create GL based images'
sysid = platform.sys.platform+'_no_GL_renderer'
return sinfo, sysid
machine = ezsearch('Machine:(.*?)\n',data)
machine = machine.replace(' ','-').replace('/','-')
machine = ezsearch('Machine:(.*?)\n',data)
machine = machine.replace(' ','-').replace('/','-')
osinfo = ezsearch('OS info:(.*?)\n',data)
osplain = osinfo.split(' ')[0].strip().replace('/','-')
if 'windows' in osinfo.lower(): osplain = 'win'
osinfo = ezsearch('OS info:(.*?)\n',data)
osplain = osinfo.split(' ')[0].strip().replace('/','-')
if 'windows' in osinfo.lower():
osplain = 'win'
renderer = ezsearch('GL Renderer:(.*?)\n',data)
tmp = renderer.split(' ')
tmp = string.join(tmp[0:3],'-')
tmp = tmp.split('/')[0]
renderer = tmp
renderer = ezsearch('GL Renderer:(.*?)\n',data)
tmp = renderer.split(' ')
tmp = string.join(tmp[0:3],'-')
tmp = tmp.split('/')[0]
renderer = tmp
data += read_gitinfo()
data += read_gitinfo()
data += 'Image comparison: ImageMagick'
data += 'Image comparison: ImageMagick'
data = data.strip()
data = data.strip()
# create 4 letter hash and stick on end of sysid
nondate_data = re.sub("\n.*?ompile date.*?\n","\n",data).strip()
hexhash = hashlib.md5()
hexhash.update(nondate_data)
hexhash = hexhash.hexdigest()[-4:].upper()
hash = ''
for c in hexhash: hash += chr(ord(c)+97-48)
# create 4 letter hash and stick on end of sysid
nondate_data = re.sub("\n.*?ompile date.*?\n", "\n", data).strip()
hexhash = hashlib.md5(nondate_data).hexdigest()[-4:].upper()
hash_ = ''.join(chr(ord(c) + 97 - 48) for c in hexhash)
sysid = osplain + '_' + machine + '_' + renderer + '_' + hash
sysid = sysid.replace('(','_').replace(')','_')
sysid = sysid.lower()
sysid = '_'.join([osplain, machine, renderer, hash_])
sysid = sysid.replace('(', '_').replace(')', '_')
sysid = sysid.lower()
return data, sysid
return data, sysid
class Test:
def __init__(self,fullname,time,passed,output,type,actualfile,expectedfile,scadfile,log):
self.fullname,self.time,self.passed,self.output = \
fullname, time, passed, output
self.type, self.actualfile, self.expectedfile, self.scadfile = \
type, actualfile, expectedfile, scadfile
self.fulltestlog = log
def __str__(self):
x = 'fullname: ' + self.fullname
x+= '\nactualfile: ' + self.actualfile
x+= '\nexpectedfile: ' + self.expectedfile
x+= '\ntesttime: ' + self.time
x+= '\ntesttype: ' + self.type
x+= '\npassed: ' + str(self.passed)
x+= '\nscadfile: ' + self.scadfile
x+= '\noutput bytes: ' + str(len(self.output))
x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
x+= '\n'
return x
def __init__(self, fullname, subpr, passed, output, type, actualfile,
expectedfile, scadfile, log):
self.fullname, self.time = fullname, time
self.passed, self.output = passed, output
self.type, self.actualfile = type, actualfile
self.expectedfile, self.scadfile = expectedfile, scadfile
self.fulltestlog = log
def __str__(self):
x = 'fullname: ' + self.fullname
x+= '\nactualfile: ' + self.actualfile
x+= '\nexpectedfile: ' + self.expectedfile
x+= '\ntesttime: ' + self.time
x+= '\ntesttype: ' + self.type
x+= '\npassed: ' + str(self.passed)
x+= '\nscadfile: ' + self.scadfile
x+= '\noutput bytes: ' + str(len(self.output))
x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
x+= '\n'
return x
def parsetest(teststring):
patterns = ["Test:(.*?)\n", # fullname
"Test time =(.*?) sec\n",
"Test time.*?Test (Passed)", # pass/fail
"Output:(.*?)<end of output>",
'Command:.*?-s" "(.*?)"', # type
"^ actual .*?:(.*?)\n",
"^ expected .*?:(.*?)\n",
'Command:.*?(testdata.*?)"' # scadfile
]
hits = map( lambda pattern: ezsearch(pattern,teststring), patterns )
test = Test(hits[0],hits[1],hits[2]=='Passed',hits[3],hits[4],hits[5],hits[6],hits[7],teststring)
if len(test.actualfile) > 0: test.actualfile_data = tryread(test.actualfile)
if len(test.expectedfile) > 0: test.expectedfile_data = tryread(test.expectedfile)
return test
patterns = ["Test:(.*?)\n", # fullname
"Test time =(.*?) sec\n",
"Test time.*?Test (Passed)", # pass/fail
"Output:(.*?)<end of output>",
'Command:.*?-s" "(.*?)"', # type
"^ actual .*?:(.*?)\n",
"^ expected .*?:(.*?)\n",
'Command:.*?(testdata.*?)"' # scadfile
]
hits = map( lambda pattern: ezsearch(pattern, teststring), patterns)
test = Test(hits[0], hits[1], hits[2]=='Passed', hits[3], hits[4], hits[5],
hits[6], hits[7], teststring)
if len(test.actualfile) > 0:
test.actualfile_data = tryread(test.actualfile)
if len(test.expectedfile) > 0:
test.expectedfile_data = tryread(test.expectedfile)
return test
def parselog(data):
startdate = ezsearch('Start testing: (.*?)\n',data)
enddate = ezsearch('End testing: (.*?)\n',data)
pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
test_chunks = re.findall(pattern,data,re.S)
tests = map( parsetest, test_chunks )
tests = sorted(tests, key = lambda t:t.passed)
return startdate, tests, enddate
startdate = ezsearch('Start testing: (.*?)\n', data)
enddate = ezsearch('End testing: (.*?)\n', data)
pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
test_chunks = re.findall(pattern,data, re.S)
tests = map( parsetest, test_chunks )
tests = sorted(tests, key = lambda t: t.passed)
return startdate, tests, enddate
def load_makefiles(builddir):
filelist = []
for root, dirs, files in os.walk(builddir):
for fname in files: filelist += [ os.path.join(root, fname) ]
files = filter(lambda x: 'build.make' in os.path.basename(x), filelist)
files += filter(lambda x: 'flags.make' in os.path.basename(x), filelist)
files = filter(lambda x: 'esting' not in x and 'emporary' not in x, files)
result = {}
for fname in files:
result[fname.replace(builddir,'')] = open(fname,'rb').read()
return result
def wikify_filename(fname, wiki_rootpath, sysid):
wikifname = fname.replace('/','_').replace('\\','_').strip('.')
return wiki_rootpath + '_' + sysid + '_' + wikifname
def towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
wiki_template = """
<h3>[[WIKI_ROOTPATH]] test run report</h3>
'''Sysid''': SYSID
'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % ) <br>
'''System info''':
<pre>
SYSINFO
</pre>
start time: STARTDATE <br>
end time : ENDDATE <br>
'''Image tests'''
<REPEAT1>
{| border=1 cellspacing=0 cellpadding=1
|-
| colspan=2 | FTESTNAME
|-
| Expected image || Actual image
|-
| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI
|}
<pre>
TESTLOG
</pre>
filelist = []
for root, dirs, files in os.walk(builddir):
for fname in files: filelist += [ os.path.join(root, fname) ]
files = [file for file in filelist if 'build.make' in os.path.basename(file)
or 'flags.make' in os.path.basename(file)]
files = [file for file in files if 'esting' not in file and 'emporary' not in file]
result = {}
for fname in files:
result[fname.replace(builddir, '')] = tryread(fname)
return result
def png_encode64(fname, width=250, data=None):
# en.wikipedia.org/wiki/Data_URI_scheme
data = data or tryread(fname) or ''
data_uri = data.encode('base64').replace('\n', '')
tag = '''<img src="data:image/png;base64,%s" width="%s" %s/>'''
if data == '':
alt = 'alt="error: no image generated" '
else:
alt = 'alt="openscad_test_image" '
tag %= (data_uri, width, alt)
return tag
</REPEAT1>
'''Text tests'''
<REPEAT2>
{|border=1 cellspacing=0 cellpadding=1
|-
| FTESTNAME
|}
<pre>
TESTLOG
</pre>
</REPEAT2>
'''build.make and flags.make'''
<REPEAT3>
*[[MAKEFILE_NAME]]
</REPEAT3>
"""
txtpages = {}
imgs = {}
passed_tests = filter(lambda x: x.passed, tests)
failed_tests = filter(lambda x: not x.passed, tests)
tests_to_report = failed_tests
if include_passed: tests_to_report = tests
try: percent = str(int(100.0*len(passed_tests) / len(tests)))
except ZeroDivisionError: percent = 'n/a'
s = wiki_template
repeat1 = ezsearch('(<REPEAT1>.*?</REPEAT1>)',s)
repeat2 = ezsearch('(<REPEAT2>.*?</REPEAT2>)',s)
repeat3 = ezsearch('(<REPEAT3>.*?</REPEAT3>)',s)
dic = { 'STARTDATE': startdate, 'ENDDATE': enddate, 'WIKI_ROOTPATH': wiki_rootpath,
'SYSINFO': sysinfo, 'SYSID':sysid,
'NUMTESTS':len(tests), 'NUMPASSED':len(passed_tests), 'PERCENTPASSED':percent }
for key in dic.keys():
s = s.replace(key,str(dic[key]))
for t in tests_to_report:
if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
newchunk = re.sub('FTESTNAME',t.fullname,repeat2)
newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
s = s.replace(repeat2, newchunk+repeat2)
elif t.type=='png':
tmp = t.actualfile.replace(builddir,'')
wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
if hasattr(t, 'expectedfile_data'):
imgs[wikiname_e] = t.expectedfile_data
if t.actualfile:
actualfile_wiki = '[[File:'+wikiname_a+'|250px]]'
if hasattr(t, 'actualfile_data'):
imgs[wikiname_a] = t.actualfile_data
else:
actualfile_wiki = 'No image generated.'
newchunk = re.sub('FTESTNAME',t.fullname,repeat1)
newchunk = newchunk.replace('ACTUALFILE_WIKI',actualfile_wiki)
newchunk = newchunk.replace('EXPECTEDFILE',wikiname_e)
newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
s = s.replace(repeat1, newchunk+repeat1)
else:
raise Exception("Unknown test type %r"%t.type)
makefiles_wikinames = {}
for mf in sorted(makefiles.keys()):
tmp = mf.replace('CMakeFiles','').replace('.dir','')
wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
newchunk = re.sub('MAKEFILE_NAME',wikiname,repeat3)
s = s.replace(repeat3, newchunk+repeat3)
makefiles_wikinames[mf] = wikiname
s = s.replace(repeat1,'')
s = s.replace(repeat2,'')
s = s.replace(repeat3,'')
s = re.sub('<REPEAT.*?>\n','',s)
s = re.sub('</REPEAT.*?>','',s)
mainpage_wikiname = wiki_rootpath + '_' + sysid + '_test_report'
txtpages[ mainpage_wikiname ] = s
for mf in sorted(makefiles.keys()):
txtpages[ makefiles_wikinames[ mf ] ] = '\n*Subreport from [['+mainpage_wikiname+']]\n\n\n<pre>\n'+makefiles[mf]+'\n</pre>'
return imgs, txtpages
def png_encode64( fname, width=250 ):
# en.wikipedia.org/wiki/Data_URI_scheme
try:
f = open( fname, "rb" )
data = f.read()
except:
data = ''
data_uri = data.encode("base64").replace("\n","")
tag = '<img'
tag += ' style="border:1px solid gray"'
tag += ' src="data:image/png;base64,'
tag += data_uri + '"'
tag += ' width="'+str(width)+'"'
if data =='':
tag += ' alt="error: no image generated"'
else:
tag += ' alt="openscad_test_image"'
tag += ' />\n'
return tag
def tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
# kludge. assume wiki stuff has alreayd run and dumped files properly
head = '<html><head><title>'+wiki_rootpath+' test run for '+sysid +'</title></head><body>'
tail = '</body></html>'
passed_tests = filter(lambda x: x.passed, tests)
failed_tests = filter(lambda x: not x.passed, tests)
try: percent = str(int(100.0*len(passed_tests) / len(tests)))
except ZeroDivisionError: percent = 'n/a'
tests_to_report = failed_tests
if include_passed: tests_to_report = tests
s=''
s+= '\n<h3>'
s+= '\nSystem info\n'
s+= '\n</h3><p>'
s+= '<pre>'+sysinfo+'</pre>\n'
s+= '\n<pre>'
s+= '\nSTARTDATE: '+ startdate
s+= '\nENDDATE: '+ enddate
s+= '\nWIKI_ROOTPATH: '+ wiki_rootpath
s+= '\nSYSID: '+sysid
s+= '\nNUMTESTS: '+str(len(tests))
s+= '\nNUMPASSED: '+str(len(passed_tests))
s+= '\nPERCENTPASSED: '+ percent
s+= '\n</pre>'
if not include_passed:
s+= '<h3>Failed tests:</h3>\n'
if len(tests_to_report)==0:
s+= '<p>none</p>'
for t in tests_to_report:
if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
s+='\n<pre>'+t.fullname+'</pre>\n'
s+='<p><pre>'+t.fulltestlog+'</pre>\n\n'
elif t.type=='png':
tmp = t.actualfile.replace(builddir,'')
wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
# imgtag_e = <img src='+wikiname_e+' width=250/>'
# imatag_a = <img src='+wikiname_a+' width=250/>'
imgtag_e = png_encode64( t.expectedfile, 250 )
imgtag_a = png_encode64( t.actualfile, 250 )
s+='<table>'
s+='\n<tr><td colspan=2>'+t.fullname
s+='\n<tr><td>Expected<td>Actual'
s+='\n<tr><td>' + imgtag_e + '</td>'
s+='\n <td>' + imgtag_a + '</td>'
s+='\n</table>'
s+='\n<pre>'
s+=t.fulltestlog
s+='\n</pre>'
else:
raise Exception("Unknown test type %r"%t.type)
s+='\n\n<p>\n\n'
s+= '<h3> CMake .build files </h3>\n'
s+= '\n<pre>'
makefiles_wikinames = {}
for mf in sorted(makefiles.keys()):
mfname = mf.strip().lstrip(os.path.sep)
text = open(os.path.join(builddir,mfname)).read()
s+= '</pre><h4>'+mfname+'</h4><pre>'
s+= text
tmp = mf.replace('CMakeFiles','').replace('.dir','')
wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
# s += '\n<a href='+wikiname+'>'+wikiname+'</a><br>'
s+= '\n</pre>'
s+='\n'
return head + s + tail
def wiki_login(wikiurl,api_php_path,botname,botpass):
site = mwclient.Site(wikiurl,api_php_path)
site.login(botname,botpass)
return site
def wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikipgname):
counter = 0
done = False
descrip = 'test'
time.sleep(1)
while not done:
try:
print 'login',botname,'to',wikiurl
site = wiki_login(wikiurl,api_php_path,botname,botpass)
print 'uploading...',
if wikipgname.endswith('png'):
site.upload(filedata,wikipgname,descrip,ignore=True)
else:
page = site.Pages[wikipgname]
text = page.edit()
page.save(filedata)
done = True
print 'transfer ok'
except Exception, e:
print 'Error:', type(e),e
counter += 1
if counter>maxretry:
print 'giving up. please try a different wiki site'
done = True
else:
print 'wiki',wikiurl,'down. retrying in 15 seconds'
time.sleep(15)
def upload(wikiurl,api_php_path='/',wiki_rootpath='test', sysid='null', botname='cakebaby',botpass='anniew',wikidir='.',dryrun=True):
wetrun = not dryrun
if dryrun: print 'dry run'
try:
global mwclient
import mwclient
except:
print 'please download mwclient 0.6.5 and unpack here:', os.getcwd()
sys.exit()
if wetrun: site = wiki_login(wikiurl,api_php_path,botname,botpass)
wikifiles = os.listdir(wikidir)
testreport_page = filter( lambda x: 'test_report' in x, wikifiles )
if (len(testreport_page)>1):
print 'multiple test reports found, please clean dir',wikidir
sys.exit()
rootpage = testreport_page[0]
print 'add',rootpage,' to main report page ',wiki_rootpath
if wetrun:
page = site.Pages[wiki_rootpath]
text = page.edit()
if not '[['+rootpage+']]' in text:
page.save(text +'\n*[['+rootpage+']]\n')
wikifiles = os.listdir(wikidir)
wikifiles = filter(lambda x: not x.endswith('html'), wikifiles)
print 'upload wiki pages:'
for wikiname in wikifiles:
filename = os.path.join(wikidir,wikiname)
filedata = tryread(filename)
print 'upload',len(filedata),'bytes from',wikiname
if wetrun and len(filedata)>0:
wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikiname)
if len(filedata)==0:
print 'cancelling empty upload'
def findlogfile(builddir):
logpath = os.path.join(builddir,'Testing','Temporary')
logfilename = os.path.join(logpath,'LastTest.log.tmp')
if not os.path.isfile(logfilename):
logfilename = os.path.join(logpath,'LastTest.log')
if not os.path.isfile(logfilename):
print 'cant find and/or open logfile',logfilename
sys.exit()
return logpath, logfilename
logpath = os.path.join(builddir, 'Testing', 'Temporary')
logfilename = os.path.join(logpath, 'LastTest.log.tmp')
if not os.path.isfile(logfilename):
logfilename = os.path.join(logpath, 'LastTest.log')
if not os.path.isfile(logfilename):
print 'can\'t find and/or open logfile', logfilename
sys.exit()
return logfilename
# --- Templating ---
class Templates(object):
html_template = '''<html>
<head><title>Test run for {sysid}</title>
{style}
</head>
<body>
<h1>{project_name} test run report</h1>
<p>
<b>Sysid</b>: {sysid}
</p>
<p>
<b>Result summary</b>: {numpassed} / {numtests} tests passed ({percent}%)
</p>
<h2>System info</h2>
<pre>{sysinfo}</pre>
<p>start time: {startdate}</p>
<p>end time: {enddate}</p>
<h2>Image tests</h2>
{image_tests}
<h2>Text tests</h2>
{text_tests}
<h2>build.make and flags.make</h2>
{makefiles}
</body></html>'''
style = '''
<style>
body {
color: black;
}
table {
border-collapse: collapse;
}
table td, th {
border: 2px solid gray;
}
.text-name {
border: 2px solid black;
padding: 0.14em;
}
</style>'''
image_template = '''<table>
<tbody>
<tr><td colspan="2">{test_name}</td></tr>
<tr><td> Expected image </td><td> Actual image </td></tr>
<tr><td> {expected} </td><td> {actual} </td></tr>
</tbody>
</table>
<pre>
{test_log}
</pre>
'''
text_template = '''
<span class="text-name">{test_name}</span>
<pre>
{test_log}
</pre>
'''
makefile_template = '''
<h4>{name}</h4>
<pre>
{text}
</pre>
'''
def __init__(self, **defaults):
self.filled = {}
self.defaults = defaults
def fill(self, template, *args, **kwargs):
kwds = self.defaults.copy()
kwds.update(kwargs)
return getattr(self, template).format(*args, **kwds)
def add(self, template, var, *args, **kwargs):
self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs)
return self.filled[var]
def get(self, var):
return self.filled.get(var, '')
def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
passed_tests = [test for test in tests if test.passed]
failed_tests = [test for test in tests if not test.passed]
report_tests = failed_tests
if include_passed:
report_tests = tests
percent = '%.0f' % (100.0 * len(passed_tests) / len(tests)) if tests else 'n/a'
templates = Templates()
for test in report_tests:
if test.type in ('txt', 'ast', 'csg', 'term', 'echo'):
templates.add('text_template', 'text_tests',
test_name=test.fullname,
test_log=test.fulltestlog)
elif test.type == 'png':
actual_img = png_encode64(test.actualfile,
data=vars(test).get('actualfile_data'))
expected_img = png_encode64(test.expectedfile,
data=vars(test).get('expectedfile_data'))
templates.add('image_template', 'image_tests',
test_name=test.fullname,
test_log=test.fulltestlog,
actual=actual_img,
expected=expected_img)
else:
raise TypeError('Unknown test type %r' % test.type)
for mf in sorted(makefiles.keys()):
mfname = mf.strip().lstrip(os.path.sep)
text = open(os.path.join(builddir, mfname)).read()
templates.add('makefile_template', 'makefiles', name=mfname, text=text)
text_tests = templates.get('text_tests')
image_tests = templates.get('image_tests')
makefiles_str = templates.get('makefiles')
return templates.fill('html_template', style=Templates.style,
sysid=sysid, sysinfo=sysinfo,
startdate=startdate, enddate=enddate,
project_name=project_name,
numtests=len(tests),
numpassed=len(passed_tests),
percent=percent, image_tests=image_tests,
text_tests=text_tests, makefiles=makefiles_str)
# --- End Templating ---
# --- Web Upload ---
def postify(data):
return urlencode(data).encode()
def create_page():
data = {
'action': 'create',
'type': 'html'
}
try:
response = urlopen('http://www.dinkypage.com', data=postify(data))
except:
return None
return response.geturl()
def upload_html(page_url, title, html):
data = {
'mode': 'editor',
'title': title,
'html': html,
'ajax': '1'
}
try:
response = urlopen(page_url, data=postify(data))
except:
return False
return 'success' in response.read().decode()
# --- End Web Upload ---
def debug(x):
if debug_test_pp: print 'test_pretty_print: '+x
if debug_test_pp:
print 'test_pretty_print: ' + x
debug_test_pp=False
builddir=os.getcwd()
debug_test_pp = False
include_passed = False
builddir = os.getcwd()
def main():
#wikisite = 'cakebaby.referata.com'
#wiki_api_path = ''
global wikisite, wiki_api_path, wiki_rootpath, builddir, debug_test_pp
global maxretry, dry, include_passed
global builddir, debug_test_pp
global maxretry, dry, include_passed
project_name = 'OpenSCAD'
if bool(os.getenv("TEST_GENERATE")):
sys.exit(0)
# --- Command Line Parsing ---
if '--debug' in ' '.join(sys.argv):
debug_test_pp = True
maxretry = 10
wikisite = 'cakebaby.wikia.com'
wiki_api_path = '/'
wiki_rootpath = 'OpenSCAD'
if '--debug' in string.join(sys.argv): debug_test_pp=True
maxretry = 10
if '--include-passed' in sys.argv:
include_passed = True
if bool(os.getenv("TEST_GENERATE")): sys.exit(0)
dry = False
debug('running test_pretty_print')
if '--dryrun' in sys.argv:
dry = True
include_passed = False
if '--include-passed' in sys.argv: include_passed = True
suffix = ezsearch('--suffix=(.*?) ', ' '.join(sys.argv) + ' ')
builddir = ezsearch('--builddir=(.*?) ', ' '.join(sys.argv) + ' ')
if not builddir:
builddir = os.getcwd()
debug('build dir set to ' + builddir)
dry = False
debug( 'running test_pretty_print' )
if '--dryrun' in sys.argv: dry=True
suffix = ezsearch('--suffix=(.*?) ',string.join(sys.argv)+' ')
builddir = ezsearch('--builddir=(.*?) ',string.join(sys.argv)+' ')
if builddir=='': builddir=os.getcwd()
debug( 'build dir set to ' + builddir )
upload = False
if '--upload' in sys.argv:
upload = True
debug('will upload test report')
# --- End Command Line Parsing ---
sysinfo, sysid = read_sysinfo(os.path.join(builddir,'sysinfo.txt'))
makefiles = load_makefiles(builddir)
logpath, logfilename = findlogfile(builddir)
testlog = tryread(logfilename)
startdate, tests, enddate = parselog(testlog)
if debug_test_pp:
print 'found sysinfo.txt,',
print 'found', len(makefiles),'makefiles,',
print 'found', len(tests),'test results'
sysinfo, sysid = read_sysinfo(os.path.join(builddir, 'sysinfo.txt'))
makefiles = load_makefiles(builddir)
logfilename = findlogfile(builddir)
testlog = tryread(logfilename)
startdate, tests, enddate = parselog(testlog)
if debug_test_pp:
print 'found sysinfo.txt,',
print 'found', len(makefiles),'makefiles,',
print 'found', len(tests),'test results'
imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
wikidir = os.path.join(logpath,sysid+'_report')
debug( 'erasing files in ' + wikidir )
try: map(lambda x:os.remove(os.path.join(wikidir,x)), os.listdir(wikidir))
except: pass
debug( 'output dir:\n' + wikidir.replace(os.getcwd(),'') )
debug( 'writing ' + str(len(imgs)) + ' images' )
debug( 'writing ' + str(len(txtpages)-1) + ' text pages' )
debug( 'writing index.html ' )
if '--wiki' in string.join(sys.argv):
print "wiki output is deprecated"
for pgname in sorted(imgs): trysave( os.path.join(wikidir,pgname), imgs[pgname])
for pgname in sorted(txtpages): trysave( os.path.join(wikidir,pgname), txtpages[pgname])
html = to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles)
html_basename = sysid + '_report.html'
html_filename = os.path.join(builddir, 'Testing', 'Temporary', html_basename)
debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes')
trysave(html_filename, html)
htmldata = tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
html_basename = sysid+'_report.html'
html_filename = os.path.join(builddir,'Testing','Temporary',html_basename)
debug('saving ' +html_filename + ' ' + str(len(htmldata)) + ' bytes')
trysave( html_filename, htmldata )
print "report saved:", html_filename.replace(os.getcwd()+os.path.sep,'')
if upload:
page_url = create_page()
if upload_html(page_url, title='OpenSCAD test results', html=html):
share_url = page_url.partition('?')[0]
print 'html report uploaded at', share_url
else:
print 'could not upload html report'
if '--wiki-upload' in sys.argv:
print "wiki upload is deprecated."
upload(wikisite,wiki_api_path,wiki_rootpath,sysid,'openscadbot',
'tobdacsnepo',wikidir,dryrun=dry)
print 'upload attempt complete'
debug( 'test_pretty_print complete' )
debug('test_pretty_print complete')
if __name__=='__main__':
main()
main()