From ed4529404a4d447b04da33aa86dd1dced4a3df16 Mon Sep 17 00:00:00 2001 From: Patrick Bogen Date: Wed, 30 Oct 2013 19:40:03 -0700 Subject: [PATCH 01/54] More robust searching through README files for dependency versions. --- scripts/check-dependencies.sh | 59 ++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index c4f28934..49d2d857 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -261,32 +261,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() From 8521cb132efa8c119f4194776c1535d4cca2fc5d Mon Sep 17 00:00:00 2001 From: Patrick Bogen Date: Wed, 30 Oct 2013 19:40:24 -0700 Subject: [PATCH 02/54] Use quotes for values that might end up empty, so that pretty print displays missing version numbers properly. --- scripts/check-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index 49d2d857..826a6651 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -548,7 +548,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 From c6df50759023a49bb8d0ae4c070782e920dc1ddb Mon Sep 17 00:00:00 2001 From: Patrick Bogen Date: Wed, 30 Oct 2013 19:53:23 -0700 Subject: [PATCH 03/54] Use find instead of ls and grep to locate gmp.h (or gmp-blah.h) for gmp_sysver --- scripts/check-dependencies.sh | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index 826a6651..120aed9f 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -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" From a40530d73d35edc5f7bfbba410a418d3058741cb Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 31 Oct 2013 14:41:02 -0400 Subject: [PATCH 04/54] bugfix: CMAKE_BUILDTYPE typo --- scripts/uni-build-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh index 48f162a0..620c7058 100755 --- a/scripts/uni-build-dependencies.sh +++ b/scripts/uni-build-dependencies.sh @@ -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 From be4c72d9e9e03a2f70eb55d8aa70ca43d084045f Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Thu, 31 Oct 2013 22:54:05 +0100 Subject: [PATCH 05/54] Add menu entry "Reset View" to reset viewport to initial settings (fixes Issue #434). --- src/MainWindow.h | 1 + src/MainWindow.ui | 7 +++++++ src/QGLView.cc | 11 ++++++++--- src/QGLView.h | 1 + src/mainwin.cc | 7 +++++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/MainWindow.h b/src/MainWindow.h index 4f88fbf0..ac999bf3 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -164,6 +164,7 @@ public slots: void viewCenter(); void viewPerspective(); void viewOrthogonal(); + void viewResetView(); void hideConsole(); void animateUpdateDocChanged(); void animateUpdate(); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 68bc0645..4cb043f9 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -213,6 +213,8 @@ + + @@ -697,6 +699,11 @@ Show Library Folder... + + + Reset View + + diff --git a/src/QGLView.cc b/src/QGLView.cc index 8aaeaf2f..25ed3237 100644 --- a/src/QGLView.cc +++ b/src/QGLView.cc @@ -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(); diff --git a/src/QGLView.h b/src/QGLView.h index f3e3681f..12be085c 100644 --- a/src/QGLView.h +++ b/src/QGLView.h @@ -41,6 +41,7 @@ public: } std::string getRendererInfo() const; bool save(const char *filename); + void resetView(); public: QLabel *statusLabel; diff --git a/src/mainwin.cc b/src/mainwin.cc index 65c511f4..5b986b1e 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -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())); @@ -1772,6 +1773,12 @@ void MainWindow::viewOrthogonal() this->qglview->updateGL(); } +void MainWindow::viewResetView() +{ + this->qglview->resetView(); + this->qglview->updateGL(); +} + void MainWindow::hideConsole() { QSettings settings; From e05a9745a348d7805fcc7ef8879fe74c5e66d8f7 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 1 Nov 2013 11:35:58 -0400 Subject: [PATCH 06/54] Bumped to eigen-3.2.0 --- scripts/macosx-build-dependencies.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index 994d2a16..d1c5985f 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -312,7 +312,8 @@ build_eigen() 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 +440,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 From 5aa01edb938cc0ddb01deef98452bafb6d34f351 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 1 Nov 2013 12:10:03 -0400 Subject: [PATCH 07/54] Fix potential memory alignment issue with eigen. This might make us dependent on eigen3, but it's about time anyway --- src/dxfdata.h | 4 ---- src/linalg.h | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dxfdata.h b/src/dxfdata.h index 64853dcc..ac7260c2 100644 --- a/src/dxfdata.h +++ b/src/dxfdata.h @@ -28,11 +28,7 @@ public: } }; -#ifdef __APPLE__ - std::vector > points; -#else std::vector points; -#endif std::vector paths; std::vector dims; diff --git a/src/linalg.h b/src/linalg.h index 1f9ed30b..cb82452c 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -4,10 +4,13 @@ #include #include #include +#include +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 BoundingBox; using Eigen::Matrix3f; using Eigen::Matrix3d; From e722d906ceb520b3f89775a0583b0c539f9c9223 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 8 Nov 2013 00:34:01 -0500 Subject: [PATCH 08/54] Get rid of leading zeros in date tags, causing them to be interpreted as octal in C++ --- tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c560b4f2..0e5981fe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -450,9 +450,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}) From 7872e7a229ea26127a19d8b3fc707fe9dc5da583 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 8 Nov 2013 00:53:58 -0500 Subject: [PATCH 09/54] Re-fix: don't add path to empty files. Fixes #536 --- src/openscad.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openscad.cc b/src/openscad.cc index 6bbaedb6..ece68189 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -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)); } From e5d535e900ae5bc5923fc76c80fb3b3f9153a80e Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 8 Nov 2013 00:57:38 -0500 Subject: [PATCH 10/54] Fixes #533 --- icons/openscad.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icons/openscad.desktop b/icons/openscad.desktop index 07df5aa8..f0282acd 100644 --- a/icons/openscad.desktop +++ b/icons/openscad.desktop @@ -4,4 +4,4 @@ Version=1.0 Name=OpenSCAD Icon=openscad Exec=openscad %f -Categories=Graphics;3DGraphics;Engineering; +Categories=Graphics;3DGraphics;Engineering;Programming; From af8359993a6dfffed8f57bc5605f2ca77a353ffa Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sun, 10 Nov 2013 16:43:46 -0600 Subject: [PATCH 11/54] download eigen 3 package , eigen2 no longer works --- scripts/uni-get-dependencies.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh index 31337c81..8e0b9d30 100755 --- a/scripts/uni-get-dependencies.sh +++ b/scripts/uni-get-dependencies.sh @@ -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 } From 00a329f0bd4ab940c1063106ee6ba7db7811a090 Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Sun, 10 Nov 2013 02:42:58 +0100 Subject: [PATCH 12/54] Add support for handling negative step values in ranges (fixes #500). --- src/control.cc | 24 ++++++------ src/expr.cc | 17 ++++++--- src/parser.y | 2 - src/value.cc | 102 +++++++++++++++++++++++++++++++++++++++++++++++-- src/value.h | 77 ++++++++++++++++++++++++++----------- 5 files changed, 177 insertions(+), 45 deletions(-) diff --git a/src/control.cc b/src/control.cc index 10aadf08..6b10a28d 100644 --- a/src/control.cc +++ b/src/control.cc @@ -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); - } + unsigned long 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)); + unsigned long 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); } diff --git a/src/expr.cc b/src/expr.cc index 594fccf2..51accf33 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -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") { diff --git a/src/parser.y b/src/parser.y index 5645104b..6446e828 100644 --- a/src/parser.y +++ b/src/parser.y @@ -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 ']' diff --git a/src/value.cc b/src/value.cc index ac33a3b6..931c616b 100644 --- a/src/value.cc +++ b/src/value.cc @@ -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."); + } +} + +unsigned long Value::RangeType::nbsteps() const { + if (begin_val == end_val) { + return 0; + } + + if (step_val == 0) { + return std::numeric_limits::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); +} diff --git a/src/value.h b/src/value.h index 388b721e..4671cdcc 100644 --- a/src/value.h +++ b/src/value.h @@ -31,33 +31,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 ⦥ + 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::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 int value if step is null + unsigned long nbsteps() const; + + friend class tostring_visitor; + friend class bracket_visitor; }; typedef std::vector VectorType; From bcba02fac33e107af959c93e7ddadce3aa18926f Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Sun, 10 Nov 2013 03:12:03 +0100 Subject: [PATCH 13/54] Add test cases for new range expression handling. --- testdata/scad/misc/range-tests.scad | 20 +++++ tests/CMakeLists.txt | 3 +- .../echotest/range-tests-expected.echo | 81 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 testdata/scad/misc/range-tests.scad create mode 100644 tests/regression/echotest/range-tests-expected.echo diff --git a/testdata/scad/misc/range-tests.scad b/testdata/scad/misc/range-tests.scad new file mode 100644 index 00000000..42ef2a40 --- /dev/null +++ b/testdata/scad/misc/range-tests.scad @@ -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); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0e5981fe..6aff17bf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -804,7 +804,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 diff --git a/tests/regression/echotest/range-tests-expected.echo b/tests/regression/echotest/range-tests-expected.echo new file mode 100644 index 00000000..ddff38e4 --- /dev/null +++ b/tests/regression/echotest/range-tests-expected.echo @@ -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 From 3c0e9f4f182dccea6dc0636daf8dbc06dd7c190f Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Sun, 10 Nov 2013 23:33:48 +0100 Subject: [PATCH 14/54] Update 'children' test case using range [4 : -1 : 0] which is now valid. --- testdata/scad/misc/children-tests.scad | 2 +- tests/regression/echotest/children-tests-expected.echo | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/testdata/scad/misc/children-tests.scad b/testdata/scad/misc/children-tests.scad index a9a3cf96..1c3d9ea6 100644 --- a/testdata/scad/misc/children-tests.scad +++ b/testdata/scad/misc/children-tests.scad @@ -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() { diff --git a/tests/regression/echotest/children-tests-expected.echo b/tests/regression/echotest/children-tests-expected.echo index 7b8278af..cdd2eb18 100644 --- a/tests/regression/echotest/children-tests-expected.echo +++ b/tests/regression/echotest/children-tests-expected.echo @@ -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" From e6b2884805e79d4ce1b55916977ddede46fc7396 Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Mon, 11 Nov 2013 01:16:57 +0100 Subject: [PATCH 15/54] Fix output of range expressions. --- src/expr.cc | 6 +++++- tests/regression/moduledumptest/allexpressions-expected.ast | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/expr.cc b/src/expr.cc index 51accf33..08615ba8 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -199,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 << "["; diff --git a/tests/regression/moduledumptest/allexpressions-expected.ast b/tests/regression/moduledumptest/allexpressions-expected.ast index 6d9de408..69ec746f 100644 --- a/tests/regression/moduledumptest/allexpressions-expected.ast +++ b/tests/regression/moduledumptest/allexpressions-expected.ast @@ -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"; From 1fcad169e623d776eb297c324304fb6836ec1e62 Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Mon, 11 Nov 2013 01:28:30 +0100 Subject: [PATCH 16/54] Update 'for' test cases to match the new range expression handling. --- testdata/scad/features/for-tests.scad | 6 +++--- .../cgalpngtest/for-tests-expected.png | Bin 10995 -> 7124 bytes .../dumptest/for-tests-expected.csg | 18 +++++++++++++++++- .../opencsgtest/for-tests-expected.png | Bin 11584 -> 7416 bytes .../throwntogethertest/for-tests-expected.png | Bin 6927 -> 7533 bytes 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/testdata/scad/features/for-tests.scad b/testdata/scad/features/for-tests.scad index fe36789f..10295b12 100644 --- a/testdata/scad/features/for-tests.scad +++ b/testdata/scad/features/for-tests.scad @@ -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 diff --git a/tests/regression/cgalpngtest/for-tests-expected.png b/tests/regression/cgalpngtest/for-tests-expected.png index bf1970a77397d2ebbcf3cf8fcbcb3070784b24fe..65db59f561ebce9384e980ddf4cca0565f7c0ea0 100644 GIT binary patch literal 7124 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 G^M3$K9n#$Z diff --git a/tests/regression/throwntogethertest/for-tests-expected.png b/tests/regression/throwntogethertest/for-tests-expected.png index 664155545934446cab4457e447d5d3dbb01357bd..7b58005ef9b81f9350e92e2d563422757db8483f 100644 GIT binary patch literal 7533 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 PZvkB`BhBJVHjn=c$906q literal 6927 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 From f46bd3788f75e691b65aaf0e4ffb1db1029ef717 Mon Sep 17 00:00:00 2001 From: Torsten Paul Date: Fri, 15 Nov 2013 19:41:37 +0100 Subject: [PATCH 17/54] Change nbsteps() to uint32_t to make it independent from 32/64-bit platform. --- src/control.cc | 4 ++-- src/value.cc | 4 ++-- src/value.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/control.cc b/src/control.cc index 6b10a28d..d5f664e0 100644 --- a/src/control.cc +++ b/src/control.cc @@ -78,7 +78,7 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst Context c(ctx); if (it_values.type() == Value::RANGE) { Value::RangeType range = it_values.toRange(); - unsigned long steps = range.nbsteps(); + uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); } else { @@ -229,7 +229,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns else if (value.type() == Value::RANGE) { AbstractNode* node = new AbstractNode(inst); Value::RangeType range = value.toRange(); - unsigned long steps = range.nbsteps(); + uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps); return NULL; diff --git a/src/value.cc b/src/value.cc index 931c616b..5afb650c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -625,13 +625,13 @@ void Value::RangeType::normalize() { } } -unsigned long Value::RangeType::nbsteps() const { +uint32_t Value::RangeType::nbsteps() const { if (begin_val == end_val) { return 0; } if (step_val == 0) { - return std::numeric_limits::max(); + return std::numeric_limits::max(); } double steps; diff --git a/src/value.h b/src/value.h index 4671cdcc..8197806d 100644 --- a/src/value.h +++ b/src/value.h @@ -84,8 +84,8 @@ public: iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); } iterator end() { return iterator(*this, RANGE_TYPE_END); } - /// return number of steps, max int value if step is null - unsigned long nbsteps() const; + /// return number of steps, max uint32_ value if step is 0 + uint32_t nbsteps() const; friend class tostring_visitor; friend class bracket_visitor; From 3e3caaa62500431b047072d3ecf1e5e3fdd7e502 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 15 Nov 2013 15:48:43 -0500 Subject: [PATCH 18/54] Removed eigen2 from build system and documentation. We now require eigen3 --- README.md | 2 +- scripts/check-dependencies.sh | 2 +- scripts/macosx-build-dependencies.sh | 1 - scripts/uni-build-dependencies.sh | 7 ------- scripts/uni-get-dependencies.sh | 2 +- src/AboutDialog.html | 2 +- src/version_check.h | 6 +++--- tests/CMakeLists.txt | 25 ++----------------------- 8 files changed, 9 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c22871a9..27f12cec 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,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/) diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index 120aed9f..b63c6770 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -472,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" diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index d1c5985f..19c97097 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -309,7 +309,6 @@ 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; diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh index 620c7058..e652c473 100755 --- a/scripts/uni-build-dependencies.sh +++ b/scripts/uni-build-dependencies.sh @@ -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. diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh index 8e0b9d30..a0306ef8 100755 --- a/scripts/uni-get-dependencies.sh +++ b/scripts/uni-get-dependencies.sh @@ -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; diff --git a/src/AboutDialog.html b/src/AboutDialog.html index b5a5d7cf..99e7c3b2 100644 --- a/src/AboutDialog.html +++ b/src/AboutDialog.html @@ -47,7 +47,7 @@ Please visit this link for a copy of the license: GNU GMP
  • GNU MPFR
  • CGAL -
  • Eigen2 +
  • Eigen
  • OpenCSG
  • OpenGL
  • GLEW diff --git a/src/version_check.h b/src/version_check.h index 6e072080..86e769c2 100644 --- a/src/version_check.h +++ b/src/version_check.h @@ -35,8 +35,8 @@ a time, to avoid confusion. #include -#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,7 +104,7 @@ a time, to avoid confusion. #endif // ENABLE_CGAL #endif // Boost -#endif // Eigen2 +#endif // Eigen #endif // MPFR #endif // GMP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6aff17bf..4be82454 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -212,30 +212,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 +240,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() From 2162aaed64d4c60c4821fbb89f7eae3e37130011 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Wed, 20 Nov 2013 01:27:46 -0500 Subject: [PATCH 19/54] Split up projection tests to avoid long running times --- .../features/projection-extrude-tests.scad | 3 + testdata/scad/features/projection-tests.scad | 19 +++---- tests/CMakeLists.txt | 4 -- .../projection-extrude-tests-expected.png | Bin 0 -> 8789 bytes .../cgalpngtest/projection-tests-expected.png | Bin 5836 -> 7793 bytes .../projection-extrude-tests-expected.csg | 20 +++++++ .../dumptest/projection-tests-expected.csg | 53 ++++++------------ .../projection-extrude-tests-expected.png | Bin 0 -> 9460 bytes .../opencsgtest/projection-tests-expected.png | Bin 6531 -> 8176 bytes .../projection-extrude-tests-expected.png | Bin 0 -> 9460 bytes .../projection-tests-expected.png | Bin 5865 -> 8176 bytes 11 files changed, 49 insertions(+), 50 deletions(-) create mode 100644 testdata/scad/features/projection-extrude-tests.scad create mode 100644 tests/regression/cgalpngtest/projection-extrude-tests-expected.png create mode 100644 tests/regression/dumptest/projection-extrude-tests-expected.csg create mode 100644 tests/regression/opencsgtest/projection-extrude-tests-expected.png create mode 100644 tests/regression/throwntogethertest/projection-extrude-tests-expected.png diff --git a/testdata/scad/features/projection-extrude-tests.scad b/testdata/scad/features/projection-extrude-tests.scad new file mode 100644 index 00000000..d9c216c2 --- /dev/null +++ b/testdata/scad/features/projection-extrude-tests.scad @@ -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); diff --git a/testdata/scad/features/projection-tests.scad b/testdata/scad/features/projection-tests.scad index e6c52ea8..bc2111cd 100644 --- a/testdata/scad/features/projection-tests.scad +++ b/testdata/scad/features/projection-tests.scad @@ -5,16 +5,15 @@ projection() { } // 2D child projection(cut=true) { 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); - +projection(cut=false) cube(10); +projection(cut=true) translate([20,0,0]) cube(10, center=true); // 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); +translate([0,20,0]) projection(cut=true) translate([0,0,-4.999999]) cube(10, center=true); // holes -translate([0,-44,0]) linear_extrude(height=5) projection(cut=true) - union() { - difference() { cube(5,center=true); cube(4,center=true); } - translate([2.1,2.1]) difference() { cube(5,center=true); cube(4,center=true); } - } +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); } + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4be82454..3d3aad1e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -834,12 +834,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 diff --git a/tests/regression/cgalpngtest/projection-extrude-tests-expected.png b/tests/regression/cgalpngtest/projection-extrude-tests-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..b5d733865f98a4251a0f2de481ee52155c53f74d GIT binary patch literal 8789 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 ZE^$D&n4ZuA{z?YU+c})A`rYU5e*tXoC{6$X literal 5836 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 B-GKlA diff --git a/tests/regression/dumptest/projection-extrude-tests-expected.csg b/tests/regression/dumptest/projection-extrude-tests-expected.csg new file mode 100644 index 00000000..0199ea0e --- /dev/null +++ b/tests/regression/dumptest/projection-extrude-tests-expected.csg @@ -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); + } + } + } + } +} diff --git a/tests/regression/dumptest/projection-tests-expected.csg b/tests/regression/dumptest/projection-tests-expected.csg index 86423a15..da3e5ce0 100644 --- a/tests/regression/dumptest/projection-tests-expected.csg +++ b/tests/regression/dumptest/projection-tests-expected.csg @@ -4,52 +4,33 @@ group() { projection(cut = true, 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) { - sphere($fn = 0, $fa = 12, $fs = 2, r = 10); + projection(cut = false, convexity = 0) { + cube(size = [10, 10, 10], 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, 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, 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, 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, -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, 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() { + 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, 2.1], [0, 1, 0, 2.1], [0, 0, 1, 0], [0, 0, 0, 1]]) { - difference() { - cube(size = [5, 5, 5], center = true); - cube(size = [4, 4, 4], center = true); - } - } } } } diff --git a/tests/regression/opencsgtest/projection-extrude-tests-expected.png b/tests/regression/opencsgtest/projection-extrude-tests-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d68bd5998d226e6066c1b5e47a814d5aea258489 GIT binary patch literal 9460 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 VYfGGc!M~XxolAyV75_Ly{vWtJ6mb9m literal 0 HcmV?d00001 diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png index 9aabe364aa3f32c89fde0352a61267cd49e245c4..b808ffe3dd1f29849e4c5b7dff14900f0fa87c07 100644 GIT binary patch literal 8176 zcmeHM`#;nD`+x6%%{dBDrYzkehg4HeE8S^{ltPIuDwP~EnS^ahDsF|8$e~Z?gG0zL zY${0yhg(@}Y;xG-G>pw=+xLCnkIygPKj8CQ`(@9^`+dEx*L6L29bS7~*L}P_wA78& z0RYg#?b&q@0H7d*0*HUUE*ZO*0sxAJ+qL6RJY=%prE0k6yea|L^6@dgfshXzcJ+$!{#amG4E!%OJn8lLUj;mJ-ZLr^WV_F{oC?Mj9wEpDvN$H zes-RUj{h#KJwm;i!#Nz8R}jJNc?)Oyy|yjK6N@wM#_=<}oV%}4vLYWX^S`qO*m`=J z?rvdghO{{1e6f-8ylJ4J&`O=6)95&}kk^on&O0%4qMFj2Y2f|33CKI`D{8bqk8|@c z+%+@Y3q2(}*Toq}Ql@33&q4Ov99?zyRmRVjP>^m2$u}BHixL-ky{$JJo?~6aK@&b= z&aX&3UkaRFO3u6QhO=w?*`@*^Hc^HG%b^`Qlnl(f@U*L~*@O}y;UM$b{>u269lU!G zVV=`*&Nh3yt(J-}m!CF{zoP1_o=$@^tEQ=K$mr00mESJ@{e?Alf#a>OhHW+>#Au_N z(Vu;bH!mcG{Ux{5FR}*qDR;8^^)wuN87F5Ax1XLhjK0IjH3KpfF&f;a@l)|Ioc!@1 zx6gHsfqT^g*og{`I*LO`Z}0Dub=Al*)!cVuFvtkX7Dt*i*`61m*n-gSr z=2947ByPL&-et`6T9r#>rODv6flP7zGS%vrFy<>A5y{0)PHJNWu^NS{F`w_YM5Voi z@xqjnewuQ@jTT!k`BOQ^P>^&bbL1tl(6Q@so4&)0*3wPxykzQH;MEF-dG>%t*!6y0 zz7C^Zek89 zs-X)VU&RXa$bJdr8@)=6<~gryH~`N;JKAT$v_=L!9n?M&Vs8!2|u#f zCrSjS=Ni4H8kYrIF~-l+A)V>Vi>FIrjWHQz#WDkJIqp6?o0rk8jPS^fx|2luG>>^3 z_&HKJI|~Tn8j?hwUJFeJg57Zb+DTP>Mc(*T^p9lojL$`hALN_bW*3HYF>l$A*^9H8 zfPlnElA@e@y3&$8l1FOSZx^@Hern-5SG<=f7s}-b*!jvHaO3^q;UPT24#T2 z;<%l9^{_*3rRFmK!0n>Dt0l5tt3G`D6|3oMZ4b^)w$WiF;%CcYU1^FIUqu6voS42h z<#pS}j#YJ}Rq~vT;pTw!Z`0KBVJGTKusx_{Mb2YQSZ%Y#D6x(kDL(8G z`7C|<_Lg0B!8z+e>BgL-=;)hY1fQt2$+rt-R)NH&lO3KZ?ZX3?eo18`5~IzvJ8J*| z()K!4Lt7q+VRgJFDnH14^kO9*Q?JIS@0vUvN%7=sQcNu@R5SPSKV1!Sd#^3eEv=h4 zd2?Tichq2LbF{rJ{3l;T> z4`+_sl|3I^2I=&TxJfN1D`}@k=%-j+sQzAk)^vL-eRTM<31^4NH$8^vJfu_n z^R3ZoVJE616b0*_%O+$HdmSc=WT7dabIe+jHphKZd~t`BM0uXjL>Ky_5RrAE4y)x+ zSa-eHj@Cr$JqcEg+t$fvyFf+54My=_9^IgZN3!1Y73fY~)i88(=+qP33+w#`2ujlm zwGiI(oL~H%J4j>Q?Q;9>*Xa0PT3h1ID(`IqeGR#wc{~3A3ejGFpqMOT;3D>=9Hf0W zIb47@(4aIS=j?qhVw22%B&FYR61N@%_vhwIAqPgB!e4!Fcep7d0VZb{iplcgZTY^K zK?@TyUSQ6;&R2w{3AY%zrX)0vK)=IyF~V@$ct_Rxn03vyVVR28*TXicBPba`?0_nx zABe5u7{20u20sUU_UJG6;e|o;v&A#K_e4fG+kxCy6>i!%76*YuU(&F#SgZoI42D$(S!&7_)OYJ2dM|t3_vV5%> z@FG_F&NaN1@=*CMUb5g0u5~w|M+Jtx$C5^@$vb%*F2-VD-UtFVFu$VuEQjopUZd!# z;_X%jp4Mi79>%so1)ld^@O-@(%B&-Q=GC5-L7^pb(=EP( zD{;{yPpQNXd6|}CXP86Pvs*+soVhJC)o+`*>h`iuw4+!+nldz=4hdWVv=}G%#1&-x zBcA!Gi`>%?@YD+e={$v>J@g%`=O~`bNZ-o__llS|58r}mp*(aOXFj8Afg-z6MF(1N z_MOMAl}IX4xpFSVkfwp4umgqfq27B_XrTFS=lv$eyg>q=D&CbR1#3D=QLY;2s@B!W_f zF0Jw;1nIrfp*-Rzhq07pN?)t88)v6YY2YWTVZ_}< zgM#VvX@G!nKb<;7&n{U%JO1BY%Jt%}Q(3^rOQMcK*=hm1i>uF2Ve)K{s_Hu0VEkRC z447IA-8Ew&x5S)jN{y+!aqJkN`ICs_!J4td&6N}&U|}W*ng$=uRJ3tt89`&AfM)q! zI4o7I0Q`L|Y-}wm_7bKz(*q5og6ZM|AY3F0OyY z7T{_wvo)=*6pGdwbZD1Jv4wjPi+BfYE~GKOQB3H$Qraa|U@%QXRz|<(tfD zW#9N`#+7(^&rIvTE}dr|vqDgg$q!BzLW%rj|L!&qr4Buj&cTqEzSk8LgZypC<{kTW z%3mM&TC@AO7P6tRr98Lrf{3B;q@{i?G==pCqnpFsJzWfd$9)l11u6#ZKhtk2Xn&XQ zgQ5@vLe$)2Aby`!1bR0w4fWo3$#ij#rY2(?pLuU;|7E_qqm}At&ol}1ZKeCU?`)94 z2$9WwcivO7o)gI@?o#h<_`9t7M3H#cJG|P_T4@tHAV7G*$^Ff?qfb74he8Qkat(Wy zOU?iyRjZD#xQao$AfiZYr3taKuJvJl0;LD%1(%$EFe{d% zp|Or)y12|uD%1E2Rzgl5{H0-EAuOBeQa%PCo$k(4{t#j9oxaXGXgZ`5e|B+?ON>Z7 z-T|)dN`(gJ5C%^YJ(PE$a3(?iN)5_8BCjs!R`G*l$zM~4E0_BP7&W1LZ_x<6%0$yZZ5iJrIEV0OoPZiBQ=zue^mmpT-qDnUC+~h_dCK2vh z)DRUonZtW;6!1rhteZDOdiYQf!Du??#k@&5I({v|SU+IKykw?XTA!v!L?vfb34gYs zjRqw3qmr4{lW7-Rn_?OyOG?Xa-KFn3NN~fiE|?t*GZo| z9#9ty(4VM!C{x|z-aUphwFEZz4)aFLf{6)Dk;Ftr95*OOcas+k@h;LD8B>L;z+!Ts90v;i>f-fMDQFTjyV z_iUGE%1ZNd`BBQURCIh`jWWhdt3X|FC+OJ@&3$oehH@Am1FG|p$1H`?;_kZ9`u-LE z!PQPu)_n#4j(k3foHUi^0$9F(QAq1n1UqQPn-NR z#-@i}0`Wc$5$gg7i@RLq_zUy&m9FZV{1}d<^RW=km87njH_pU+V9CwsOEq0<=bDAb z7%YeNuak}^%Vmtl;Hg)wnTjejvD6%hnfH97g|tpIyOvJv7BiwnB-X5nvb(w9XeSqX z_QmQOXATlXwiO=^+n^re6@6cBT=-zcaIKQjlz4?#?|zMdvWtrXveM&h`Qe279#~*_ zEO+m;o@A`9?pVv)=LycLad74^E@{cO#@mK&Jw(?6>z-=Ce;$2+*MR>Lq+FqRQ1<>`!ROYf)*DVpuoattQEz@~H+fObbqJD2 ze=o@`Is-~pb5_53v!((ANPq3mpx%Vok!*_N!MM$*`uVgHN4c`@j~C3%p`b>{Pxos$>wnhh#Uj%5Q#+}#1`kbgUyg=SiPs7q;~{`SS5cFX)R2j@hw_|<*lXKs9x=WXWfmJdi=5o zX0NBb|LQAshp%B(|BQK}qqVja*f^T8vmN@eT1H zztf6*xf#;w=XS`Qm(DEHfoCSqRpFQ@Xw=Wy5GYGM*4{4BLK1uOsdD`qF4<6s_iWNpuoKjt_9&*P}h|gTfj-oEpY6DKAo)xLR~P1)*dRwQ2;6&3N_o2b9)a65fF&( zgR@@XU+SPM6de??!*GSo*m5r4g7z$Ld|}g`kU3? kvHCl$eiy6%OnT->;9CO3H+6F=&&N&_LVX$HjLo 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 diff --git a/tests/regression/throwntogethertest/projection-extrude-tests-expected.png b/tests/regression/throwntogethertest/projection-extrude-tests-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d68bd5998d226e6066c1b5e47a814d5aea258489 GIT binary patch literal 9460 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 VYfGGc!M~XxolAyV75_Ly{vWtJ6mb9m literal 0 HcmV?d00001 diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png index 3be3ae007d47b6ac5424d57bcb40b137d6720043..b808ffe3dd1f29849e4c5b7dff14900f0fa87c07 100644 GIT binary patch literal 8176 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 Date: Sat, 23 Nov 2013 15:12:44 +0100 Subject: [PATCH 20/54] Fix compilation error on Ubuntu where uint32_t is not defined. --- src/value.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/value.h b/src/value.h index 8197806d..75dff2bc 100644 --- a/src/value.h +++ b/src/value.h @@ -11,6 +11,7 @@ #include #include #endif +#include class QuotedString : public std::string { @@ -84,7 +85,7 @@ public: iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); } iterator end() { return iterator(*this, RANGE_TYPE_END); } - /// return number of steps, max uint32_ value if step is 0 + /// return number of steps, max uint32_t value if step is 0 uint32_t nbsteps() const; friend class tostring_visitor; From b204aba444bd838bd3fe27675323dce6d4123b19 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sat, 23 Nov 2013 18:38:45 -0600 Subject: [PATCH 21/54] mxe64 build fix. make 'qt disabled' message less confusing on test binary. --- scripts/mingw-x-build-dependencies.sh | 6 +----- src/PlatformUtils.cc | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/mingw-x-build-dependencies.sh b/scripts/mingw-x-build-dependencies.sh index e9f124b0..df8572f6 100755 --- a/scripts/mingw-x-build-dependencies.sh +++ b/scripts/mingw-x-build-dependencies.sh @@ -48,11 +48,7 @@ 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 diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc index cfa57313..b02b822e 100644 --- a/src/PlatformUtils.cc +++ b/src/PlatformUtils.cc @@ -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 From 7b526bc27ab70f362332640987bc3f8c363b3659 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sat, 23 Nov 2013 20:00:19 -0500 Subject: [PATCH 22/54] Added test for polygon with hole --- testdata/scad/features/polygon-tests.scad | 6 ++++++ .../cgalpngtest/polygon-tests-expected.png | Bin 7755 -> 8321 bytes .../dumptest/polygon-tests-expected.csg | 3 +++ .../opencsgtest/polygon-tests-expected.png | Bin 8736 -> 9532 bytes .../polygon-tests-expected.png | Bin 8797 -> 9626 bytes 5 files changed, 9 insertions(+) diff --git a/testdata/scad/features/polygon-tests.scad b/testdata/scad/features/polygon-tests.scad index b4e92b6b..0cd259f9 100644 --- a/testdata/scad/features/polygon-tests.scad +++ b/testdata/scad/features/polygon-tests.scad @@ -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 diff --git a/tests/regression/cgalpngtest/polygon-tests-expected.png b/tests/regression/cgalpngtest/polygon-tests-expected.png index 5ceabe87ae477b9e3759cd1392975736fd82f3ca..28e4e9fe0c64cc699506ee5d24cf4b6337b3111f 100644 GIT binary patch literal 8321 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 bNSCe2JA9&|AV%^@3^<>4J5&AdfdBj#q?{}B diff --git a/tests/regression/dumptest/polygon-tests-expected.csg b/tests/regression/dumptest/polygon-tests-expected.csg index 56995a57..e19bcb07 100644 --- a/tests/regression/dumptest/polygon-tests-expected.csg +++ b/tests/regression/dumptest/polygon-tests-expected.csg @@ -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); + } } diff --git a/tests/regression/opencsgtest/polygon-tests-expected.png b/tests/regression/opencsgtest/polygon-tests-expected.png index fe84a80bc10fccecc4aaf9b5cc38be365d7c0a9f..94bd13104d85f36dd747dada8a8817add8c72a53 100644 GIT binary patch literal 9532 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 literal 8736 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 literal 8797 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 Date: Sat, 23 Nov 2013 22:10:05 -0500 Subject: [PATCH 23/54] Added test for implicit union of render() children --- testdata/scad/features/render-2d-tests.scad | 9 +++++++-- .../cgalpngtest/render-2d-tests-expected.png | Bin 7029 -> 7730 bytes .../dumptest/render-2d-tests-expected.csg | 10 ++++++++-- .../opencsgtest/render-2d-tests-expected.png | Bin 7646 -> 7951 bytes .../render-2d-tests-expected.png | Bin 7646 -> 7951 bytes 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/testdata/scad/features/render-2d-tests.scad b/testdata/scad/features/render-2d-tests.scad index 683ffe4d..f8df115e 100644 --- a/testdata/scad/features/render-2d-tests.scad +++ b/testdata/scad/features/render-2d-tests.scad @@ -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); +} diff --git a/tests/regression/cgalpngtest/render-2d-tests-expected.png b/tests/regression/cgalpngtest/render-2d-tests-expected.png index 19ea16a8420f316cfa12234a7bfec301ca8b5336..241896832808fb6ca9006d1d259b52c812713ace 100644 GIT binary patch literal 7730 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 E0C)6rsQ>@~ diff --git a/tests/regression/dumptest/render-2d-tests-expected.csg b/tests/regression/dumptest/render-2d-tests-expected.csg index 75739b3c..bcc5e96e 100644 --- a/tests/regression/dumptest/render-2d-tests-expected.csg +++ b/tests/regression/dumptest/render-2d-tests-expected.csg @@ -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); } } } diff --git a/tests/regression/opencsgtest/render-2d-tests-expected.png b/tests/regression/opencsgtest/render-2d-tests-expected.png index 0bf6288ffa1570e2dda5c892fde05ef473238ec3..47e57dd79751774713fd8441f92bc45e96d58d4f 100644 GIT binary patch literal 7951 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 GlK%%95#fjc literal 7646 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 diff --git a/tests/regression/throwntogethertest/render-2d-tests-expected.png b/tests/regression/throwntogethertest/render-2d-tests-expected.png index 0bf6288ffa1570e2dda5c892fde05ef473238ec3..47e57dd79751774713fd8441f92bc45e96d58d4f 100644 GIT binary patch literal 7951 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 GlK%%95#fjc literal 7646 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 From 825fbc7b9e7756ef8c2b49a85efd9abe3ebf8d95 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sun, 24 Nov 2013 19:01:11 -0600 Subject: [PATCH 24/54] revert to GMPQ number type kernel due to bugs like issue #481 --- src/cgal.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cgal.h b/src/cgal.h index 45228be1..efc53d39 100644 --- a/src/cgal.h +++ b/src/cgal.h @@ -48,9 +48,10 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2; typedef CGAL::Polygon_2 CGAL_Poly2; typedef CGAL::Polygon_with_holes_2 CGAL_Poly2h; - //typedef CGAL::Cartesian 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 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_Nef_polyhedron3; typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; From de2db22b5f82855b9cf42829c4dde3b4bd231273 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Mon, 25 Nov 2013 00:27:15 -0500 Subject: [PATCH 25/54] Split projection tests into cut vs. project --- .../scad/features/projection-cut-tests.scad | 15 ++++++++ testdata/scad/features/projection-tests.scad | 28 +++++++++----- .../projection-cut-tests-expected.png | Bin 0 -> 7286 bytes .../cgalpngtest/projection-tests-expected.png | Bin 7793 -> 7837 bytes .../projection-cut-tests-expected.csg | 33 ++++++++++++++++ .../dumptest/projection-tests-expected.csg | 36 +++++++----------- .../projection-cut-tests-expected.png | Bin 0 -> 7644 bytes .../opencsgtest/projection-tests-expected.png | Bin 8176 -> 8825 bytes .../projection-cut-tests-expected.png | Bin 0 -> 7644 bytes .../projection-tests-expected.png | Bin 8176 -> 8825 bytes 10 files changed, 79 insertions(+), 33 deletions(-) create mode 100644 testdata/scad/features/projection-cut-tests.scad create mode 100644 tests/regression/cgalpngtest/projection-cut-tests-expected.png create mode 100644 tests/regression/dumptest/projection-cut-tests-expected.csg create mode 100644 tests/regression/opencsgtest/projection-cut-tests-expected.png create mode 100644 tests/regression/throwntogethertest/projection-cut-tests-expected.png diff --git a/testdata/scad/features/projection-cut-tests.scad b/testdata/scad/features/projection-cut-tests.scad new file mode 100644 index 00000000..0409a3ef --- /dev/null +++ b/testdata/scad/features/projection-cut-tests.scad @@ -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); } + } +} diff --git a/testdata/scad/features/projection-tests.scad b/testdata/scad/features/projection-tests.scad index bc2111cd..27d03fee 100644 --- a/testdata/scad/features/projection-tests.scad +++ b/testdata/scad/features/projection-tests.scad @@ -3,17 +3,25 @@ projection(); // No children projection() { } // 2D child -projection(cut=true) { square(); } +projection() { square(); } +// Simple projection(cut=false) cube(10); -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); } - } +// Two children +translate([-12,0]) projection(cut=false) { + cube(10); + difference() { + sphere(10); + cylinder(h=30, r=5, center=true); + } +} + +// Holes +translate([6,-12]) projection(cut=false) { + cube(10); + difference() { + sphere(10); + cylinder(h=30, r=5, center=true); + } } diff --git a/tests/regression/cgalpngtest/projection-cut-tests-expected.png b/tests/regression/cgalpngtest/projection-cut-tests-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7189447b734623b9fb118f8c914c0b7aff2cc276 GIT binary patch literal 7286 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

    }L-))+k#or42bFtT}c6+F%mi41jb z$iPo#Ar!ktHVl49#FF{*{40ML5p{4z_uD?Iwa2&AEfOL9=37c>x76q~8Za`P$OE9xkKLT+N7 zj{~sF<&Ko9M;u9nA+_ZhWeCu5OR%=0p$2~^oDddMGbt?I{ zl^%z;9%?}6?LNJFOtZ6Gw#=wdoqwZpQA2S$$86T*TJ^oB#YQ;_ITF;cC6g$6EAvKS z&(A5-u$sR6O@X>6_l)tD+P)ch`xg~d^#%g2HUPLs?#^f;{3%4rQ06E}o-n}+wQQdr z)d5>?7Tyrt+?f1T+H|8hLBMB_kA#`qRMRkA#vy?a+LB@JaSeEQc6Ut8UV*Uwii~zq zWq2h2xu&{?Y5E8UoW8LBycrw^e~AM!zStY8i=UgcG;yx)ZMLZW+*Pg5rz39auMS;h oV3mPY238qZW#In?11r!&c`s6K9@=lI{+|fApYn38I}w=jA8s7ZyZ`_I literal 0 HcmV?d00001 diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png index 3be3654023387ec91ac96a384d354db75b3da6f8..c0d52897a54214d06c27ff42bc91e5143e83019b 100644 GIT binary patch literal 7837 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 F`F{#0=O_RG literal 7793 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 ZE^$D&n4ZuA{z?YU+c})A`rYU5e*tXoC{6$X diff --git a/tests/regression/dumptest/projection-cut-tests-expected.csg b/tests/regression/dumptest/projection-cut-tests-expected.csg new file mode 100644 index 00000000..4c37fa2b --- /dev/null +++ b/tests/regression/dumptest/projection-cut-tests-expected.csg @@ -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); + } + } + } + } + } +} diff --git a/tests/regression/dumptest/projection-tests-expected.csg b/tests/regression/dumptest/projection-tests-expected.csg index da3e5ce0..04cd4041 100644 --- a/tests/regression/dumptest/projection-tests-expected.csg +++ b/tests/regression/dumptest/projection-tests-expected.csg @@ -1,37 +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); } projection(cut = false, convexity = 0) { cube(size = [10, 10, 10], 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, -12], [0, 1, 0, 0], [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); } } } - 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); - } - } + 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); } } } diff --git a/tests/regression/opencsgtest/projection-cut-tests-expected.png b/tests/regression/opencsgtest/projection-cut-tests-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..5063301d65527ee624ad98aa690f71c50466121a GIT binary patch literal 7644 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 AhX4Qo literal 0 HcmV?d00001 diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png index b808ffe3dd1f29849e4c5b7dff14900f0fa87c07..b3af1e1105b998b6e0cacbb34e9d5cd08a6f639f 100644 GIT binary patch literal 8825 zcmeHN`9DMwX(kL6%#jBDA5zjMPQ4v{IHKm3`^j*Evk34JDJf82@`@GKU^?JX~>v`@wIatfdsLB8U zAZKfH>O25I#SjWe|96Q~wJHOEU1-}=mKUQTOQUvg0*9_hEmQMSDjXVYZOFH!5sJ6A zZV$(|Y587L(4ND|Ffq?& zzZQ^T>@g&UTY&*F8J-~{4ZcowHWouTc=Y!&zkTyNZhj}j??Umri2naM8IrPgX;ng! zTv;*Ql@D@Qe0C^V8UnpE{=_V*$q5^Z_B;)pC_))yy^QvFpQr+X&Jh8pKapI7x}0x> ze0nh79t~Wluk#l3Zv52Lb45P-g6!4O0uF`8BpfwpSV+GmjXS6^<+@`l;JU=%EjDx( zp+0NNe!l)F#1;(%mgLY&3rp-#(@)`lK5Pk+mIi~^x9l%flNQqbWd0-SiHUkaBV9$r zF?+0Rth<*H8$;RbXz<)T1%ZU_!k4DY$eQGRe^|b${4Kah$(795bZ#=ZY?J`0Ox{!G z`#>owcgLmh`HQZDXB`KIC+C6R{FQg8jrplu`mEs zL!9h>5@f*Ry0%XHKFjm?iNvp#5Hh0((>-)-tE8FM0nuLID$~I`Obw3CE6{$keblm! zMd?0^0Q;BtH4VZfqxpa%(fWXDVAso{BO}+!>j`O6=)7CA+JmG8=YRgL)CKO^FJNx< zcTnDyU!v3wf7t@|*D~MScO1b?@)vId^N#y%k!NXKy&ebPY~UFpPf~tTqBiN%z~yAX z#kE7efVGd`Z57oAgW-F^<7`9QiZDmJER}&`b|JP_ZYQextFcpW?3t|qeWCx)!+8l| z^>NDP#=v45B_FQK~h?V}5HD0(|u)`>&p=h4lM@rm_saw>_z^&dBAY2cZ9|fpN@`u#i#{vdxBJ z<|a+!U?MJK{N5M>+)e(YvFojgfPCY*_FkX-nTV-+V?p_O?5`B{5HBa>Sw=GOLb|4} zz4qf&x~;nrVs@0cCiyWq9liuy(Q84@hg ziE1h>c5~6=J}hH?%)ke{1YX1}4DWM*n;%CwD1Z}9wElmJb=RtgN*_WXMy-?8mR~_r z)Am=kq2a)zCI09m)j(znDw01`OmizT={EaUa`~ZbX7Ox3Z#;+Dv*9fCd!lAK@%p7V z8rWGMsyd>a_U`pYsr}hx9Mn@(M0;{%aMH&jlHWu85&eP^?|dkm^kh$47%W&-J7|FZ8h6+&y~w?KWsKgHE1S=fZptyKad8cVc0 z|A0a9<*naw18d3O@6WEcyT~#S-~vBUC$Mpk0}-*nlNK4GH>GbC!=$}t)3%p=+77TxR*@{eM@A!18r=j}-) zKW-MBMd|y<;`Z@{mNoI5o>a|*IVH`_(QY5~M};!<`v>dqtZAEK+4S(09Y=AR>**Be)*h3v^vNGcNcGrXbOE_U0AmXALE| z*AQ}NUSmmNW$KsQL`=~2AjTFj9lI7>73&!PjY{xB=6+yh)tg6qFt@Mv&%R z)`()#$nh@V*U2L5^o=W9l#{3Rk(#WP8AcxO8D$!pV2o_jc-rKhn1Bs44+` zL+KTcoNz%N5{NkHwjZ$;!i$KV@zezxFh`NFpR8&fU~K(vmS{KxTY{NwPcQBjjhCe9 zwip3_jB=xH3O7nqps9^p-Z+G`c=Sl|b)%RUm?R_bHh63AUS>!4@7y-2tU)7#kIYgsGULfia88RPY17t*UE z-B?>n9g?q}4rnx6-I!6>Z13re#k$cSHrtmy3#HRG_ICu|?^8~ia$M8g0gG5!I-%tf zPpdE)QJ1|o9@a}7?8)LqhUM0w5?3zJD2B}bih6$3`6(50L8y7YO9*juLm|6ix1&BO zq5uA!%!g%%fb6SB92+Nf(k)cM6v*834m3q*Lih92pyKzT8yFsLJ)jI~#yc@LoKHC@{P&(H!wsC3$f5yd2L|e_N z=CE>S-e|ut0g9M>!As1I=2%Tgraw=GYz}>+Q+^!gzr#l!H^8jzpCIE>C7K^#V5Ze! z%JrKE`H6YH-2BFWui^8j>Tup`f>=9e40~{R6%~=JtC5)n?@}4ifJrTNdi&iX+d5ni znOM}^Xj@{f*H#30?FswlbpulQj|7*=6%G+-X4W~9A>Z<+Ubh@`7-mb&1uVBVSHvxK zOa9!c9?gjoo)cL%FaJ%wm*nL>(fNQGHL>ZVqE8U%&-pECf3Kq0+gF;r+)?)F1E0Ic z;-{Bwva00aybrlW4F0I-TU3`+h-2ty2M-Qkemx@x3My6e6byE9-bIv>$5bDthh#F-W*# z4;&%fUF60-S26gNB668=SKNt&buaO~e+FLP#Lr=QVNHC~aTXSjIutpglY~UBUYT0& zVUpe}181z^%_ZF*8vBM4(B7`jFn+Mj)XtWNMeL@?Yl+kxddI-XXwP-l19VuXt&uP)5={w>HW z6Sa$k{-T=&!g(J)DtLTwH+U?^#kfsdrSd@;sX9_<7<^)S0WTbG*a)kjCpL1n^m04Y zO?FjQak8!~F@&jK!^Zri!5DK^lhjSBO6Bu15<5aTN^;fLfpv-QDo8ZYJh-9&#mp@- zIC^!@3V&iib8K-*VMp?~k-ew&;#Y z3&GBmVue|T4vl5@OkrVU1>~$7(18gg+EzB$lZ^d*xGWFYiwOxZ7AC+MiabTd->0(r zE0WcTHry>

    kwDk^w)_yhRASafAd#a1G^45#XB}` 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 AhX4Qo literal 0 HcmV?d00001 diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png index b808ffe3dd1f29849e4c5b7dff14900f0fa87c07..b3af1e1105b998b6e0cacbb34e9d5cd08a6f639f 100644 GIT binary patch literal 8825 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

    kwDk^w)_yhRASafAd#a1G^45#XB}` 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 kvHCl$eiy6%O Date: Tue, 26 Nov 2013 20:04:57 -0600 Subject: [PATCH 26/54] prevent crash in CGAL nef3. fix #issue 410 . also deal w qmake bug re .h files --- openscad.pro | 2 ++ src/CGALEvaluator.cc | 16 +++++++++++++--- src/CGAL_Nef_polyhedron.cc | 17 ++++++++++++++--- src/cgal.h | 8 +++++--- src/export.cc | 10 +++++++++- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/openscad.pro b/openscad.pro index fd9f4947..b38419ef 100644 --- a/openscad.pro +++ b/openscad.pro @@ -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 \ diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index ec013153..242fe0f9 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -159,9 +159,19 @@ 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(&CGAL_Polyhedron::Vertex::point), _1)); + bool err = false; + try{ + err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P ); + //chN.p3->convert_to_Polyhedron(P); + } catch (...) { + err = true; + } + if (err) { + PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed"); + } else { + std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d), + boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1)); + } } } chnode->progress_report(); diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc index 440f4edd..4761d265 100644 --- a/src/CGAL_Nef_polyhedron.cc +++ b/src/CGAL_Nef_polyhedron.cc @@ -98,11 +98,22 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset() CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); try { CGAL_Polyhedron P; - this->p3->convert_to_Polyhedron(P); - ps = createPolySetFromPolyhedron(P); + bool err = nefworkaround::convert_to_Polyhedron( *(this->p3), P ); + //this->p3->convert_to_Polyhedron(P); + if (err) { + PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed"); + } else { + ps = createPolySetFromPolyhedron(P); + } } catch (const CGAL::Precondition_exception &e) { - PRINTB("CGAL error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what()); + PRINTB("CGAL Precondition error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what()); + } + catch (const CGAL::Assertion_exception &e) { + PRINTB("CGAL Assertion error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what()); + } + catch (...) { + PRINT("CGAL unknown error in CGAL_Nef_polyhedron::convertToPolyset()"); } CGAL::set_error_behaviour(old_behaviour); } diff --git a/src/cgal.h b/src/cgal.h index 45228be1..69c8c270 100644 --- a/src/cgal.h +++ b/src/cgal.h @@ -27,6 +27,7 @@ using boost::uintmax_t; #include #include #include +#include #include #include #include @@ -48,9 +49,10 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2; typedef CGAL::Polygon_2 CGAL_Poly2; typedef CGAL::Polygon_with_holes_2 CGAL_Poly2h; - //typedef CGAL::Cartesian 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 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_Nef_polyhedron3; typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; diff --git a/src/export.cc b/src/export.cc index ec6e576a..cef323e8 100644 --- a/src/export.cc +++ b/src/export.cc @@ -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( *(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); } From faf008ce24e5169dcfe75d90bfbc988abdfd7f93 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Tue, 26 Nov 2013 20:29:29 -0600 Subject: [PATCH 27/54] simplify nef polyhedron code. attempt to add test for bug --- src/CGAL_Nef_polyhedron.cc | 26 ++++++++++++-------------- tests/CMakeLists.txt | 1 + 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc index 4761d265..49b9a534 100644 --- a/src/CGAL_Nef_polyhedron.cc +++ b/src/CGAL_Nef_polyhedron.cc @@ -96,24 +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; - bool err = nefworkaround::convert_to_Polyhedron( *(this->p3), P ); + err = nefworkaround::convert_to_Polyhedron( *(this->p3), P ); //this->p3->convert_to_Polyhedron(P); - if (err) { - PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed"); - } else { - ps = createPolySetFromPolyhedron(P); - } } - catch (const CGAL::Precondition_exception &e) { - PRINTB("CGAL Precondition error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what()); + catch (const CGAL::Failure_exception &e) { + err = true; + errmsg = std::string(e.what()); } - catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL Assertion error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what()); - } - catch (...) { - PRINT("CGAL unknown error in CGAL_Nef_polyhedron::convertToPolyset()"); + 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); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3d3aad1e..731a418a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -800,6 +800,7 @@ list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILE 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) From fd715c6526e961cb7f3d6ba6a0563788d7d1674d Mon Sep 17 00:00:00 2001 From: Don Bright Date: Tue, 26 Nov 2013 20:55:26 -0600 Subject: [PATCH 28/54] finish adding new test, add png for new test --- tests/CMakeLists.txt | 5 +++++ ...cgal-convert_to_Polyhedron-crash-expected.png | Bin 0 -> 4350 bytes ...cgal-convert_to_Polyhedron-crash-expected.csg | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png create mode 100644 tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 731a418a..0477a45d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -794,6 +794,7 @@ 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}) @@ -823,6 +824,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 diff --git a/tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png b/tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..318cbaabb1c5aa09c123f8da6863298527c7242d GIT binary patch literal 4350 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 YOPsfOE;2&n5B+UezsWXd9rMh80A%9J-v9sr literal 0 HcmV?d00001 diff --git a/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg b/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg new file mode 100644 index 00000000..acad52fd --- /dev/null +++ b/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg @@ -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); + } +} From 7b64944738d91eb2eef92f93f526cc29436adf15 Mon Sep 17 00:00:00 2001 From: Ricardo Markiewicz Date: Fri, 29 Nov 2013 00:16:40 -0300 Subject: [PATCH 29/54] Try to keep cursor and scroll in place on refresh When the file is saving, the cursor position was resetting to the top and you should keep scrolling by hand every time you save the file. This patch saves the scroll and cursor position and set it again after the file is refreshed so we can continue edition from where we were. --- src/mainwin.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mainwin.cc b/src/mainwin.cc index 5b986b1e..8c21b207 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -75,6 +75,7 @@ #include #include #include +#include #include @@ -605,7 +606,15 @@ void MainWindow::refreshDocument() reader.setCodec("UTF-8"); QString text = reader.readAll(); PRINTB("Loaded design '%s'.", this->fileName.toLocal8Bit().constData()); + int y = editor->verticalScrollBar()->sliderPosition(); + // Save current cursor position + QTextCursor cursor = editor->textCursor(); + int n = cursor.position(); editor->setPlainText(text); + // Restore cursor position + cursor.setPosition(n); + editor->setTextCursor(cursor); + editor->verticalScrollBar()->setSliderPosition(y); } } setCurrentOutput(); From afe1fbcb53ff286b2f6efdb99efb6ed9947304f9 Mon Sep 17 00:00:00 2001 From: Ricardo Markiewicz Date: Fri, 29 Nov 2013 01:40:28 -0300 Subject: [PATCH 30/54] Move main code inside Editor class Before open a new file now we cleanup the Editor, so the new file get the cursor on the first character. --- src/editor.cc | 14 ++++++++++++++ src/editor.h | 2 ++ src/mainwin.cc | 10 +--------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/editor.cc b/src/editor.cc index 08bf005f..069101f8 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -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); + } +} diff --git a/src/editor.h b/src/editor.h index 09484f52..8d092a9a 100644 --- a/src/editor.h +++ b/src/editor.h @@ -2,6 +2,7 @@ #include #include #include +#include #include 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(); diff --git a/src/mainwin.cc b/src/mainwin.cc index 8c21b207..1ad8bc8d 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -75,7 +75,6 @@ #include #include #include -#include #include @@ -505,6 +504,7 @@ MainWindow::openFile(const QString &new_filename) } #endif setFileName(actual_filename); + editor->setPlainText(""); fileChangedOnDisk(); // force cached autoReloadId to update refreshDocument(); @@ -606,15 +606,7 @@ void MainWindow::refreshDocument() reader.setCodec("UTF-8"); QString text = reader.readAll(); PRINTB("Loaded design '%s'.", this->fileName.toLocal8Bit().constData()); - int y = editor->verticalScrollBar()->sliderPosition(); - // Save current cursor position - QTextCursor cursor = editor->textCursor(); - int n = cursor.position(); editor->setPlainText(text); - // Restore cursor position - cursor.setPosition(n); - editor->setTextCursor(cursor); - editor->verticalScrollBar()->setSliderPosition(y); } } setCurrentOutput(); From 6f6a8dff7669322a35ddc33ad195f2cb3de45de8 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sat, 30 Nov 2013 13:42:09 -0600 Subject: [PATCH 31/54] MXE c/o 'stable' instd of 'master' from github b/c master has gcc 4.8.2 which is causing crashes. also allow 'build only' builder.sh script command --- scripts/builder.sh | 90 ++++++++++++++++++++------- scripts/mingw-x-build-dependencies.sh | 21 +++++-- 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/scripts/builder.sh b/scripts/builder.sh index 552d559d..ca7e5b29 100755 --- a/scripts/builder.sh +++ b/scripts/builder.sh @@ -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 diff --git a/scripts/mingw-x-build-dependencies.sh b/scripts/mingw-x-build-dependencies.sh index df8572f6..c0f658dc 100755 --- a/scripts/mingw-x-build-dependencies.sh +++ b/scripts/mingw-x-build-dependencies.sh @@ -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: # @@ -54,11 +59,19 @@ 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 From f175bae46a8f15823780c5a9c89b11476acb3107 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sat, 30 Nov 2013 15:13:03 -0600 Subject: [PATCH 32/54] disallow gcc 4.8.2 --- src/version_check.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/version_check.h b/src/version_check.h index 86e769c2..fbea077f 100644 --- a/src/version_check.h +++ b/src/version_check.h @@ -108,5 +108,13 @@ a time, to avoid confusion. #endif // MPFR #endif // GMP +// see github issue #552 +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#if GCC_VERSION == 40802 +#error OpenSCAD isn't compatible with gcc 4.8.2. Please try a different version +#endif + #endif // OPENSCAD_SKIP_VERSION_CHECK From 9ea7713335122eabdd243cfcf1e5ae87a8bd23d1 Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sat, 30 Nov 2013 15:43:00 -0600 Subject: [PATCH 33/54] print errmsg for applyHull. add quotes around err msg in version check. --- src/CGALEvaluator.cc | 5 +++-- src/version_check.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 242fe0f9..4a052745 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -160,14 +160,15 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node) } else { bool err = false; + std::string errmsg(""); try{ err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P ); //chN.p3->convert_to_Polyhedron(P); - } catch (...) { + catch (const CGAL::Failure_exception &e) { err = true; } if (err) { - PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed"); + PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", e.what()); } else { std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d), boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1)); diff --git a/src/version_check.h b/src/version_check.h index fbea077f..be52e61d 100644 --- a/src/version_check.h +++ b/src/version_check.h @@ -113,7 +113,7 @@ a time, to avoid confusion. + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #if GCC_VERSION == 40802 -#error OpenSCAD isn't compatible with gcc 4.8.2. Please try a different version +#error "OpenSCAD isnt compatible with gcc 4.8.2. Please try a different version" #endif #endif // OPENSCAD_SKIP_VERSION_CHECK From 791a49b9e8489818e41deae2b1d4ba2b6ff50e5f Mon Sep 17 00:00:00 2001 From: Don Bright Date: Sat, 30 Nov 2013 17:26:50 -0600 Subject: [PATCH 34/54] build bug fix --- src/CGALEvaluator.cc | 7 +- src/CGAL_Nef3_workaround.h | 352 +++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 src/CGAL_Nef3_workaround.h diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 4a052745..86118d78 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -161,14 +161,15 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node) else { bool err = false; std::string errmsg(""); - try{ + try { err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P ); //chN.p3->convert_to_Polyhedron(P); - catch (const CGAL::Failure_exception &e) { + } catch (const CGAL::Failure_exception &e) { err = true; + errmsg = std::string(e.what()); } if (err) { - PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", e.what()); + 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(&CGAL_Polyhedron::Vertex::point), _1)); diff --git a/src/CGAL_Nef3_workaround.h b/src/CGAL_Nef3_workaround.h new file mode 100644 index 00000000..c2482ac4 --- /dev/null +++ b/src/CGAL_Nef3_workaround.h @@ -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 +// Miguel Granados +// Susan Hert +// Lutz Kettner +// Ralf Osbild +// Peter Hachenberger + +/* + 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "printutils.h" + +namespace nefworkaround { + +template +class Triangulation_handler2 { + + typedef typename CGAL::Triangulation_vertex_base_2 Vb; + typedef typename CGAL::Constrained_triangulation_face_base_2 Fb; + typedef typename CGAL::Triangulation_data_structure_2 TDS; + typedef typename CGAL::Constrained_triangulation_2 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 visited; + CGAL::Unique_hash_map 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 + 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 + 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 Build_polyhedron : public CGAL::Modifier_base { +public: + bool error; // added for OpenSCAD + class Visitor { + typedef typename CGAL::Projection_traits_xy_3 XY; + typedef typename CGAL::Projection_traits_yz_3 YZ; + typedef typename CGAL::Projection_traits_xz_3 XZ; + + const CGAL::Object_index& VI; + CGAL::Polyhedron_incremental_builder_3& B; + const typename Nef::SNC_const_decorator& D; + + public: + bool error;//added for OpenSCAD + Visitor(CGAL::Polyhedron_incremental_builder_3& BB, + const typename Nef::SNC_const_decorator& sd, + CGAL::Object_index& 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 th(f); + th.handle_triangles(B, VI); + } else if(c == 1) { + Triangulation_handler2 th(f); + th.handle_triangles(B, VI); + } else if(c == 2) { + Triangulation_handler2 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 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 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 +bool convert_to_Polyhedron( const CGAL::Nef_polyhedron_3 &N, CGAL::Polyhedron_3 &P ) +{ + // several lines here added for OpenSCAD + typedef typename CGAL::Nef_polyhedron_3 Nef3; + typedef typename CGAL::Polyhedron_3 Polyhedron; + typedef typename Polyhedron::HalfedgeDS HalfedgeDS; + CGAL_precondition(N.is_simple()); + P.clear(); + Build_polyhedron bp(N); + P.delegate(bp); + return bp.error; +} + + + + + +} //namespace nefworkaround + + + + +#endif + From 69cbb17497ab52620e39874a2323db1aadf6dcbe Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Wed, 4 Dec 2013 01:15:19 -0500 Subject: [PATCH 35/54] Killed warnings --- src/CocoaUtils.mm | 4 ++-- src/modcontext.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CocoaUtils.mm b/src/CocoaUtils.mm index 92640fdd..9856b3d7 100644 --- a/src/CocoaUtils.mm +++ b/src/CocoaUtils.mm @@ -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()); } diff --git a/src/modcontext.cc b/src/modcontext.cc index 5b480097..7941cf5c 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -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(); } From f2fe074e1d947f74e34833453cc613e46e5450a6 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Wed, 4 Dec 2013 01:15:34 -0500 Subject: [PATCH 36/54] clang fix: Clang claims to be gcc --- src/stl-utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stl-utils.cc b/src/stl-utils.cc index 790fd179..027339cc 100644 --- a/src/stl-utils.cc +++ b/src/stl-utils.cc @@ -1,4 +1,4 @@ -#if defined(__APPLE__) && defined(__GNUC__) +#if defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__) #include From 301ef946f05a320768d4b8f974a95e2a04a5fc0d Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 5 Dec 2013 12:20:40 -0500 Subject: [PATCH 37/54] Qt4 patches to make it build on 10.9 --- patches/qt4/patch-libtiff.diff | 18 + .../patch-src_corelib_global_qglobal.h.diff | 14 + ...ns_bearer_corewlan_qcorewlanengine.mm.diff | 1382 +++++++++++++++++ 3 files changed, 1414 insertions(+) create mode 100644 patches/qt4/patch-libtiff.diff create mode 100644 patches/qt4/patch-src_corelib_global_qglobal.h.diff create mode 100644 patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff diff --git a/patches/qt4/patch-libtiff.diff b/patches/qt4/patch-libtiff.diff new file mode 100644 index 00000000..5b7f9ec3 --- /dev/null +++ b/patches/qt4/patch-libtiff.diff @@ -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 does not define. */ + /* #undef off_t */ + diff --git a/patches/qt4/patch-src_corelib_global_qglobal.h.diff b/patches/qt4/patch-src_corelib_global_qglobal.h.diff new file mode 100644 index 00000000..8c55c5a6 --- /dev/null +++ b/patches/qt4/patch-src_corelib_global_qglobal.h.diff @@ -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 diff --git a/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff b/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff new file mode 100644 index 00000000..61b2eef3 --- /dev/null +++ b/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff @@ -0,0 +1,1382 @@ +--- src/plugins/bearer/corewlan/qcorewlanengine.mm ++++ src/plugins/bearer/corewlan/qcorewlanengine.mm +@@ -52,29 +52,17 @@ + #include + + #include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include ++ ++extern "C" { // Otherwise it won't find CWKeychain* symbols at link time ++#import ++} ++ + #include "private/qcore_mac_p.h" + + #include + #include + +-inline QString qt_NSStringToQString(const NSString *nsstr) +-{ return QCFString::toQString(reinterpret_cast(nsstr)); } +- +-inline NSString *qt_QStringToNSString(const QString &qstr) +-{ return [const_cast(reinterpret_cast(QCFString::toCFStringRef(qstr))) autorelease]; } +- ++#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + + @interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject + { +@@ -86,6 +74,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) + - (void)notificationHandler;//:(NSNotification *)notification; + - (void)remove; + - (void)setEngine:(QCoreWlanEngine *)coreEngine; ++- (QCoreWlanEngine *)engine; + - (void)dealloc; + + @property (assign) QCoreWlanEngine* engine; +@@ -93,7 +82,6 @@ inline NSString *qt_QStringToNSString(const QString &qstr) + @end + + @implementation QT_MANGLE_NAMESPACE(QNSListener) +-@synthesize engine; + + - (id) init + { +@@ -101,7 +89,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + notificationCenter = [NSNotificationCenter defaultCenter]; + currentInterface = [CWInterface interfaceWithName:nil]; +- [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil]; ++ [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:CWPowerDidChangeNotification object:nil]; + [locker unlock]; + [autoreleasepool release]; + return self; +@@ -120,6 +108,11 @@ inline NSString *qt_QStringToNSString(const QString &qstr) + [locker unlock]; + } + ++-(QCoreWlanEngine *)engine ++{ ++ return engine; ++} ++ + -(void)remove + { + [locker lock]; +@@ -133,7 +126,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) + } + @end + +-QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; ++static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; + + QT_BEGIN_NAMESPACE + +@@ -170,36 +163,28 @@ void QScanThread::run() + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + QStringList found; + mutex.lock(); +- CWInterface *currentInterface = [CWInterface interfaceWithName:qt_QStringToNSString(interfaceName)]; ++ CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)]; + mutex.unlock(); + +- if([currentInterface power]) { ++ if (currentInterface.powerOn) { + NSError *err = nil; +- NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys: +- [NSNumber numberWithBool:YES], kCWScanKeyMerge, +- [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, +- [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil]; + +- NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err]; +- CWNetwork *apNetwork; ++ NSSet* apSet = [currentInterface scanForNetworksWithName:nil error:&err]; + + if (!err) { +- +- for(uint row=0; row < [apArray count]; row++ ) { +- apNetwork = [apArray objectAtIndex:row]; +- +- const QString networkSsid = qt_NSStringToQString([apNetwork ssid]); ++ for (CWNetwork *apNetwork in apSet) { ++ const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid])); + const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); + found.append(id); + + QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; + bool known = isKnownSsid(networkSsid); +- if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { +- if( networkSsid == qt_NSStringToQString( [currentInterface ssid])) { ++ if (currentInterface.serviceActive) { ++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { + state = QNetworkConfiguration::Active; + } + } +- if(state == QNetworkConfiguration::Undefined) { ++ if (state == QNetworkConfiguration::Undefined) { + if(known) { + state = QNetworkConfiguration::Discovered; + } else { +@@ -207,7 +192,7 @@ void QScanThread::run() + } + } + QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose; +- if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) { ++ if ([apNetwork supportsSecurity:kCWSecurityNone]) { + purpose = QNetworkConfiguration::PublicPurpose; + } else { + purpose = QNetworkConfiguration::PrivatePurpose; +@@ -237,8 +222,8 @@ void QScanThread::run() + interfaceName = ij.value(); + } + +- if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { +- if( networkSsid == qt_NSStringToQString([currentInterface ssid])) { ++ if (currentInterface.serviceActive) { ++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { + state = QNetworkConfiguration::Active; + } + } +@@ -300,14 +285,14 @@ void QScanThread::getUserConfigurations() + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + userProfiles.clear(); + +- NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; +- for(uint row=0; row < [wifiInterfaces count]; row++ ) { ++ NSSet *wifiInterfaces = [CWInterface interfaceNames]; ++ for (NSString *ifName in wifiInterfaces) { + +- CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]]; +- if ( ![wifiInterface power] ) ++ CWInterface *wifiInterface = [CWInterface interfaceWithName: ifName]; ++ if (!wifiInterface.powerOn) + continue; + +- NSString *nsInterfaceName = [wifiInterface name]; ++ NSString *nsInterfaceName = wifiInterface.ssid; + // add user configured system networks + SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil); + NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]); +@@ -316,11 +301,11 @@ void QScanThread::getUserConfigurations() + NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"]; + + NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; +- for(NSString *ssidkey in thisSsidarray) { +- QString thisSsid = qt_NSStringToQString(ssidkey); ++ for (NSString *ssidkey in thisSsidarray) { ++ QString thisSsid = QCFString::toQString(CFStringRef(ssidkey)); + if(!userProfiles.contains(thisSsid)) { + QMap map; +- map.insert(thisSsid, qt_NSStringToQString(nsInterfaceName)); ++ map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName))); + userProfiles.insert(thisSsid, map); + } + } +@@ -329,7 +314,7 @@ void QScanThread::getUserConfigurations() + + // 802.1X user profiles + QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist"; +- NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile:qt_QStringToNSString(userProfilePath)] autorelease]; ++ NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease]; + if(eapDict != nil) { + NSString *profileStr= @"Profiles"; + NSString *nameStr = @"UserDefinedName"; +@@ -348,15 +333,15 @@ void QScanThread::getUserConfigurations() + QString ssid; + for(int i = 0; i < dictSize; i++) { + if([nameStr isEqualToString:keys[i]]) { +- networkName = qt_NSStringToQString(objects[i]); ++ networkName = QCFString::toQString(CFStringRef(objects[i])); + } + if([networkSsidStr isEqualToString:keys[i]]) { +- ssid = qt_NSStringToQString(objects[i]); ++ ssid = QCFString::toQString(CFStringRef(objects[i])); + } + if(!userProfiles.contains(networkName) + && !ssid.isEmpty()) { + QMap map; +- map.insert(ssid, qt_NSStringToQString(nsInterfaceName)); ++ map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName))); + userProfiles.insert(networkName, map); + } + } +@@ -444,7 +429,7 @@ void QCoreWlanEngine::initialize() + QMutexLocker locker(&mutex); + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + +- if([[CWInterface supportedInterfaces] count] > 0 && !listener) { ++ if ([[CWInterface interfaceNames] count] > 0 && !listener) { + listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init]; + listener.engine = this; + hasWifi = true; +@@ -479,141 +464,68 @@ void QCoreWlanEngine::connectToId(const QString &id) + QString interfaceString = getInterfaceFromId(id); + + CWInterface *wifiInterface = +- [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)]; ++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; + +- if ([wifiInterface power]) { ++ if (wifiInterface.powerOn) { + NSError *err = nil; +- NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0]; +- + QString wantedSsid; +- + QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); + + const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name)); + const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name))); + +- bool using8021X = false; +- if (idHash2 != id) { +- NSArray *array = [CW8021XProfile allUser8021XProfiles]; +- +- for (NSUInteger i = 0; i < [array count]; ++i) { +- const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] userDefinedName]))); +- +- const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] ssid]))); +- +- if (id == networkNameHashCheck || id == ssidHash) { +- const QString thisName = scanThread->getSsidFromNetworkName(id); +- if (thisName.isEmpty()) +- wantedSsid = id; +- else +- wantedSsid = thisName; +- +- [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile]; +- using8021X = true; +- break; +- } ++ QString wantedNetwork; ++ QMapIterator > i(scanThread->userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ wantedNetwork = i.key(); ++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); ++ if (id == networkNameHash) { ++ wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); ++ break; + } + } + +- if (!using8021X) { +- QString wantedNetwork; +- QMapIterator > i(scanThread->userProfiles); +- while (i.hasNext()) { +- i.next(); +- wantedNetwork = i.key(); +- const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); +- if (id == networkNameHash) { +- wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); +- break; +- } +- } +- } +- NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys: +- [NSNumber numberWithBool:YES], kCWScanKeyMerge, +- [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, +- [NSNumber numberWithInteger:100], kCWScanKeyRestTime, +- qt_QStringToNSString(wantedSsid), kCWScanKeySSID, +- nil]; +- +- NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err]; ++ NSSet *scanSet = [wifiInterface scanForNetworksWithName:(NSString *)QCFString::toCFStringRef(wantedSsid) error:&err]; + + if(!err) { +- for(uint row=0; row < [scanArray count]; row++ ) { +- CWNetwork *apNetwork = [scanArray objectAtIndex:row]; +- +- if(wantedSsid == qt_NSStringToQString([apNetwork ssid])) { +- +- if(!using8021X) { +- SecKeychainAttribute attributes[3]; +- +- NSString *account = [apNetwork ssid]; +- NSString *keyKind = @"AirPort network password"; +- NSString *keyName = account; +- +- attributes[0].tag = kSecAccountItemAttr; +- attributes[0].data = (void *)[account UTF8String]; +- attributes[0].length = [account length]; +- +- attributes[1].tag = kSecDescriptionItemAttr; +- attributes[1].data = (void *)[keyKind UTF8String]; +- attributes[1].length = [keyKind length]; +- +- attributes[2].tag = kSecLabelItemAttr; +- attributes[2].data = (void *)[keyName UTF8String]; +- attributes[2].length = [keyName length]; +- +- SecKeychainAttributeList attributeList = {3,attributes}; +- +- SecKeychainSearchRef searchRef; +- SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef); +- +- NSString *password = @""; +- SecKeychainItemRef searchItem; +- +- if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) { +- UInt32 realPasswordLength; +- SecKeychainAttribute attributesW[8]; +- attributesW[0].tag = kSecAccountItemAttr; +- SecKeychainAttributeList listW = {1,attributesW}; +- char *realPassword; +- OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword); +- +- if (status == noErr) { +- if (realPassword != NULL) { +- +- QByteArray pBuf; +- pBuf.resize(realPasswordLength); +- pBuf.prepend(realPassword); +- pBuf.insert(realPasswordLength,'\0'); +- +- password = [NSString stringWithUTF8String:pBuf]; +- } +- SecKeychainItemFreeContent(&listW, realPassword); +- } +- +- CFRelease(searchItem); +- } else { +- qDebug() << "SecKeychainSearchCopyNext error"; +- } +- [params setValue: password forKey: kCWAssocKeyPassphrase]; +- } // end using8021X +- +- +- bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err]; ++ for (CWNetwork *apNetwork in scanSet) { ++ CFDataRef ssidData = (CFDataRef)[apNetwork ssidData]; ++ bool result = false; ++ ++ SecIdentityRef identity = 0; ++ // Check first whether we require IEEE 802.1X authentication for the wanted SSID ++ if (CWKeychainCopyEAPIdentity(ssidData, &identity) == errSecSuccess) { ++ CFStringRef username = 0; ++ CFStringRef password = 0; ++ if (CWKeychainCopyEAPUsernameAndPassword(ssidData, &username, &password) == errSecSuccess) { ++ result = [wifiInterface associateToEnterpriseNetwork:apNetwork ++ identity:identity username:(NSString *)username password:(NSString *)password ++ error:&err]; ++ CFRelease(username); ++ CFRelease(password); ++ } ++ CFRelease(identity); ++ } else { ++ CFStringRef password = 0; ++ if (CWKeychainCopyPassword(ssidData, &password) == errSecSuccess) { ++ result = [wifiInterface associateToNetwork:apNetwork password:(NSString *)password error:&err]; ++ CFRelease(password); ++ } ++ } + +- if(!err) { +- if(!result) { +- emit connectionError(id, ConnectError); +- } else { +- return; +- } ++ if (!err) { ++ if (!result) { ++ emit connectionError(id, ConnectError); + } else { +- qDebug() <<"associate ERROR"<< qt_NSStringToQString([err localizedDescription ]); ++ return; + } ++ } else { ++ qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); + } + } //end scan network + } else { +- qDebug() <<"scan ERROR"<< qt_NSStringToQString([err localizedDescription ]); ++ qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); + } + emit connectionError(id, InterfaceLookupError); + } +@@ -631,10 +543,10 @@ void QCoreWlanEngine::disconnectFromId(const QString &id) + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + CWInterface *wifiInterface = +- [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)]; ++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; + + [wifiInterface disassociate]; +- if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) { ++ if (wifiInterface.serviceActive) { + locker.unlock(); + emit connectionError(id, DisconnectionError); + locker.relock(); +@@ -654,9 +566,9 @@ void QCoreWlanEngine::doRequestUpdate() + + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + +- NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; +- for (uint row = 0; row < [wifiInterfaces count]; ++row) { +- scanThread->interfaceName = qt_NSStringToQString([wifiInterfaces objectAtIndex:row]); ++ NSSet *wifiInterfaces = [CWInterface interfaceNames]; ++ for (NSString *ifName in wifiInterfaces) { ++ scanThread->interfaceName = QCFString::toQString(CFStringRef(ifName)); + scanThread->start(); + } + locker.unlock(); +@@ -669,8 +581,8 @@ bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName) + bool haswifi = false; + if(hasWifi) { + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; +- CWInterface *defaultInterface = [CWInterface interfaceWithName: qt_QStringToNSString(wifiDeviceName)]; +- if([defaultInterface power]) { ++ CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)]; ++ if (defaultInterface.powerOn) { + haswifi = true; + } + [autoreleasepool release]; +@@ -898,7 +810,7 @@ quint64 QCoreWlanEngine::startTime(const QString &identifier) + bool ok = false; + for(int i = 0; i < dictSize; i++) { + if([ssidStr isEqualToString:keys[i]]) { +- const QString ident = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString(objects[i]))); ++ const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i])))); + if(ident == identifier) { + ok = true; + } +@@ -944,3 +856,7 @@ quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b) + } + + QT_END_NAMESPACE ++ ++#else // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE ++#include "qcorewlanengine_10_6.mm" ++#endif +diff --git a/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm b/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm +new file mode 100644 +index 0000000..a3bf615 +--- /dev/null ++++ src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm +@@ -0,0 +1,916 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ++** Contact: http://www.qt-project.org/legal ++** ++** This file is part of the plugins of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.html. ++** ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include ++ ++@interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject ++{ ++ NSNotificationCenter *notificationCenter; ++ CWInterface *currentInterface; ++ QCoreWlanEngine *engine; ++ NSLock *locker; ++} ++- (void)notificationHandler;//:(NSNotification *)notification; ++- (void)remove; ++- (void)setEngine:(QCoreWlanEngine *)coreEngine; ++- (QCoreWlanEngine *)engine; ++- (void)dealloc; ++ ++@property (assign) QCoreWlanEngine* engine; ++ ++@end ++ ++@implementation QT_MANGLE_NAMESPACE(QNSListener) ++ ++- (id) init ++{ ++ [locker lock]; ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ notificationCenter = [NSNotificationCenter defaultCenter]; ++ currentInterface = [CWInterface interfaceWithName:nil]; ++ [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil]; ++ [locker unlock]; ++ [autoreleasepool release]; ++ return self; ++} ++ ++-(void)dealloc ++{ ++ [super dealloc]; ++} ++ ++-(void)setEngine:(QCoreWlanEngine *)coreEngine ++{ ++ [locker lock]; ++ if(!engine) ++ engine = coreEngine; ++ [locker unlock]; ++} ++ ++-(QCoreWlanEngine *)engine ++{ ++ return engine; ++} ++ ++-(void)remove ++{ ++ [locker lock]; ++ [notificationCenter removeObserver:self]; ++ [locker unlock]; ++} ++ ++- (void)notificationHandler//:(NSNotification *)notification ++{ ++ engine->requestUpdate(); ++} ++@end ++ ++static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; ++ ++QT_BEGIN_NAMESPACE ++ ++void networkChangeCallback(SCDynamicStoreRef/* store*/, CFArrayRef changedKeys, void *info) ++{ ++ for ( long i = 0; i < CFArrayGetCount(changedKeys); i++) { ++ ++ QString changed = QCFString::toQString(CFStringRef((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i))); ++ if( changed.contains("/Network/Global/IPv4")) { ++ QCoreWlanEngine* wlanEngine = static_cast(info); ++ wlanEngine->requestUpdate(); ++ } ++ } ++ return; ++} ++ ++ ++QScanThread::QScanThread(QObject *parent) ++ :QThread(parent) ++{ ++} ++ ++QScanThread::~QScanThread() ++{ ++} ++ ++void QScanThread::quit() ++{ ++ wait(); ++} ++ ++void QScanThread::run() ++{ ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ QStringList found; ++ mutex.lock(); ++ CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)]; ++ mutex.unlock(); ++ ++ if([currentInterface power]) { ++ NSError *err = nil; ++ NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys: ++ [NSNumber numberWithBool:YES], kCWScanKeyMerge, ++ [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, ++ [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil]; ++ ++ NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err]; ++ CWNetwork *apNetwork; ++ ++ if (!err) { ++ ++ for(uint row=0; row < [apArray count]; row++ ) { ++ apNetwork = [apArray objectAtIndex:row]; ++ ++ const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid])); ++ const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); ++ found.append(id); ++ ++ QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; ++ bool known = isKnownSsid(networkSsid); ++ if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { ++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { ++ state = QNetworkConfiguration::Active; ++ } ++ } ++ if(state == QNetworkConfiguration::Undefined) { ++ if(known) { ++ state = QNetworkConfiguration::Discovered; ++ } else { ++ state = QNetworkConfiguration::Undefined; ++ } ++ } ++ QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose; ++ if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) { ++ purpose = QNetworkConfiguration::PublicPurpose; ++ } else { ++ purpose = QNetworkConfiguration::PrivatePurpose; ++ } ++ ++ found.append(foundNetwork(id, networkSsid, state, interfaceName, purpose)); ++ ++ } ++ } ++ } ++ // add known configurations that are not around. ++ QMapIterator > i(userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ ++ QString networkName = i.key(); ++ const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkName)); ++ ++ if(!found.contains(id)) { ++ QString networkSsid = getSsidFromNetworkName(networkName); ++ const QString ssidId = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); ++ QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; ++ QString interfaceName; ++ QMapIterator ij(i.value()); ++ while (ij.hasNext()) { ++ ij.next(); ++ interfaceName = ij.value(); ++ } ++ ++ if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { ++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { ++ state = QNetworkConfiguration::Active; ++ } ++ } ++ if(state == QNetworkConfiguration::Undefined) { ++ if( userProfiles.contains(networkName) ++ && found.contains(ssidId)) { ++ state = QNetworkConfiguration::Discovered; ++ } ++ } ++ ++ if(state == QNetworkConfiguration::Undefined) { ++ state = QNetworkConfiguration::Defined; ++ } ++ ++ found.append(foundNetwork(id, networkName, state, interfaceName, QNetworkConfiguration::UnknownPurpose)); ++ } ++ } ++ emit networksChanged(); ++ [autoreleasepool release]; ++} ++ ++QStringList QScanThread::foundNetwork(const QString &id, const QString &name, const QNetworkConfiguration::StateFlags state, const QString &interfaceName, const QNetworkConfiguration::Purpose purpose) ++{ ++ QStringList found; ++ QMutexLocker locker(&mutex); ++ QNetworkConfigurationPrivate *ptr = new QNetworkConfigurationPrivate; ++ ++ ptr->name = name; ++ ptr->isValid = true; ++ ptr->id = id; ++ ptr->state = state; ++ ptr->type = QNetworkConfiguration::InternetAccessPoint; ++ ptr->bearerType = QNetworkConfiguration::BearerWLAN; ++ ptr->purpose = purpose; ++ ++ fetchedConfigurations.append( ptr); ++ configurationInterface.insert(ptr->id, interfaceName); ++ ++ locker.unlock(); ++ locker.relock(); ++ found.append(id); ++ return found; ++} ++ ++QList QScanThread::getConfigurations() ++{ ++ QMutexLocker locker(&mutex); ++ ++ QList foundConfigurations = fetchedConfigurations; ++ fetchedConfigurations.clear(); ++ ++ return foundConfigurations; ++} ++ ++void QScanThread::getUserConfigurations() ++{ ++ QMutexLocker locker(&mutex); ++ ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ userProfiles.clear(); ++ ++ NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; ++ for(uint row=0; row < [wifiInterfaces count]; row++ ) { ++ ++ CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]]; ++ if ( ![wifiInterface power] ) ++ continue; ++ ++ NSString *nsInterfaceName = [wifiInterface name]; ++// add user configured system networks ++ SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil); ++ NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]); ++ CFRelease(dynRef); ++ if(airportPlist != nil) { ++ NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"]; ++ ++ NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; ++ for(NSString *ssidkey in thisSsidarray) { ++ QString thisSsid = QCFString::toQString(CFStringRef(ssidkey)); ++ if(!userProfiles.contains(thisSsid)) { ++ QMap map; ++ map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName))); ++ userProfiles.insert(thisSsid, map); ++ } ++ } ++ CFRelease(airportPlist); ++ } ++ ++ // 802.1X user profiles ++ QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist"; ++ NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease]; ++ if(eapDict != nil) { ++ NSString *profileStr= @"Profiles"; ++ NSString *nameStr = @"UserDefinedName"; ++ NSString *networkSsidStr = @"Wireless Network"; ++ for (id profileKey in eapDict) { ++ if ([profileStr isEqualToString:profileKey]) { ++ NSDictionary *itemDict = [eapDict objectForKey:profileKey]; ++ for (id itemKey in itemDict) { ++ ++ NSInteger dictSize = [itemKey count]; ++ id objects[dictSize]; ++ id keys[dictSize]; ++ ++ [itemKey getObjects:objects andKeys:keys]; ++ QString networkName; ++ QString ssid; ++ for(int i = 0; i < dictSize; i++) { ++ if([nameStr isEqualToString:keys[i]]) { ++ networkName = QCFString::toQString(CFStringRef(objects[i])); ++ } ++ if([networkSsidStr isEqualToString:keys[i]]) { ++ ssid = QCFString::toQString(CFStringRef(objects[i])); ++ } ++ if(!userProfiles.contains(networkName) ++ && !ssid.isEmpty()) { ++ QMap map; ++ map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName))); ++ userProfiles.insert(networkName, map); ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ [autoreleasepool release]; ++} ++ ++QString QScanThread::getSsidFromNetworkName(const QString &name) ++{ ++ QMutexLocker locker(&mutex); ++ ++ QMapIterator > i(userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ QMap map = i.value(); ++ QMapIterator ij(i.value()); ++ while (ij.hasNext()) { ++ ij.next(); ++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") +i.key())); ++ if(name == i.key() || name == networkNameHash) { ++ return ij.key(); ++ } ++ } ++ } ++ return QString(); ++} ++ ++QString QScanThread::getNetworkNameFromSsid(const QString &ssid) ++{ ++ QMutexLocker locker(&mutex); ++ ++ QMapIterator > i(userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ QMap map = i.value(); ++ QMapIterator ij(i.value()); ++ while (ij.hasNext()) { ++ ij.next(); ++ if(ij.key() == ssid) { ++ return i.key(); ++ } ++ } ++ } ++ return QString(); ++} ++ ++bool QScanThread::isKnownSsid(const QString &ssid) ++{ ++ QMutexLocker locker(&mutex); ++ ++ QMapIterator > i(userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ QMap map = i.value(); ++ if(map.keys().contains(ssid)) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++ ++QCoreWlanEngine::QCoreWlanEngine(QObject *parent) ++: QBearerEngineImpl(parent), scanThread(0) ++{ ++ scanThread = new QScanThread(this); ++ connect(scanThread, SIGNAL(networksChanged()), ++ this, SLOT(networksChanged())); ++} ++ ++QCoreWlanEngine::~QCoreWlanEngine() ++{ ++ while (!foundConfigurations.isEmpty()) ++ delete foundConfigurations.takeFirst(); ++ [listener remove]; ++ [listener release]; ++} ++ ++void QCoreWlanEngine::initialize() ++{ ++ QMutexLocker locker(&mutex); ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ ++ if([[CWInterface supportedInterfaces] count] > 0 && !listener) { ++ listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init]; ++ listener.engine = this; ++ hasWifi = true; ++ } else { ++ hasWifi = false; ++ } ++ storeSession = NULL; ++ ++ startNetworkChangeLoop(); ++ [autoreleasepool release]; ++} ++ ++ ++QString QCoreWlanEngine::getInterfaceFromId(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ ++ return scanThread->configurationInterface.value(id); ++} ++ ++bool QCoreWlanEngine::hasIdentifier(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ ++ return scanThread->configurationInterface.contains(id); ++} ++ ++void QCoreWlanEngine::connectToId(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ QString interfaceString = getInterfaceFromId(id); ++ ++ CWInterface *wifiInterface = ++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; ++ ++ if ([wifiInterface power]) { ++ NSError *err = nil; ++ NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0]; ++ ++ QString wantedSsid; ++ ++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); ++ ++ const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name)); ++ const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name))); ++ ++ bool using8021X = false; ++ if (idHash2 != id) { ++ NSArray *array = [CW8021XProfile allUser8021XProfiles]; ++ ++ for (NSUInteger i = 0; i < [array count]; ++i) { ++ const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] userDefinedName])))); ++ ++ const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] ssid])))); ++ ++ if (id == networkNameHashCheck || id == ssidHash) { ++ const QString thisName = scanThread->getSsidFromNetworkName(id); ++ if (thisName.isEmpty()) ++ wantedSsid = id; ++ else ++ wantedSsid = thisName; ++ ++ [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile]; ++ using8021X = true; ++ break; ++ } ++ } ++ } ++ ++ if (!using8021X) { ++ QString wantedNetwork; ++ QMapIterator > i(scanThread->userProfiles); ++ while (i.hasNext()) { ++ i.next(); ++ wantedNetwork = i.key(); ++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); ++ if (id == networkNameHash) { ++ wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); ++ break; ++ } ++ } ++ } ++ NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys: ++ [NSNumber numberWithBool:YES], kCWScanKeyMerge, ++ [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, ++ [NSNumber numberWithInteger:100], kCWScanKeyRestTime, ++ (NSString *)QCFString::toCFStringRef(wantedSsid), kCWScanKeySSID, ++ nil]; ++ ++ NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err]; ++ ++ if(!err) { ++ for(uint row=0; row < [scanArray count]; row++ ) { ++ CWNetwork *apNetwork = [scanArray objectAtIndex:row]; ++ ++ if(wantedSsid == QCFString::toQString(CFStringRef([apNetwork ssid]))) { ++ ++ if(!using8021X) { ++ SecKeychainAttribute attributes[3]; ++ ++ NSString *account = [apNetwork ssid]; ++ NSString *keyKind = @"AirPort network password"; ++ NSString *keyName = account; ++ ++ attributes[0].tag = kSecAccountItemAttr; ++ attributes[0].data = (void *)[account UTF8String]; ++ attributes[0].length = [account length]; ++ ++ attributes[1].tag = kSecDescriptionItemAttr; ++ attributes[1].data = (void *)[keyKind UTF8String]; ++ attributes[1].length = [keyKind length]; ++ ++ attributes[2].tag = kSecLabelItemAttr; ++ attributes[2].data = (void *)[keyName UTF8String]; ++ attributes[2].length = [keyName length]; ++ ++ SecKeychainAttributeList attributeList = {3,attributes}; ++ ++ SecKeychainSearchRef searchRef; ++ SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef); ++ ++ NSString *password = @""; ++ SecKeychainItemRef searchItem; ++ ++ if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) { ++ UInt32 realPasswordLength; ++ SecKeychainAttribute attributesW[8]; ++ attributesW[0].tag = kSecAccountItemAttr; ++ SecKeychainAttributeList listW = {1,attributesW}; ++ char *realPassword; ++ OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword); ++ ++ if (status == noErr) { ++ if (realPassword != NULL) { ++ ++ QByteArray pBuf; ++ pBuf.resize(realPasswordLength); ++ pBuf.prepend(realPassword); ++ pBuf.insert(realPasswordLength,'\0'); ++ ++ password = [NSString stringWithUTF8String:pBuf]; ++ } ++ SecKeychainItemFreeContent(&listW, realPassword); ++ } ++ ++ CFRelease(searchItem); ++ } else { ++ qDebug() << "SecKeychainSearchCopyNext error"; ++ } ++ [params setValue: password forKey: kCWAssocKeyPassphrase]; ++ } // end using8021X ++ ++ ++ bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err]; ++ ++ if(!err) { ++ if(!result) { ++ emit connectionError(id, ConnectError); ++ } else { ++ return; ++ } ++ } else { ++ qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); ++ } ++ } ++ } //end scan network ++ } else { ++ qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); ++ } ++ emit connectionError(id, InterfaceLookupError); ++ } ++ ++ locker.unlock(); ++ emit connectionError(id, InterfaceLookupError); ++ [autoreleasepool release]; ++} ++ ++void QCoreWlanEngine::disconnectFromId(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ ++ QString interfaceString = getInterfaceFromId(id); ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ ++ CWInterface *wifiInterface = ++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; ++ ++ [wifiInterface disassociate]; ++ if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) { ++ locker.unlock(); ++ emit connectionError(id, DisconnectionError); ++ locker.relock(); ++ } ++ [autoreleasepool release]; ++} ++ ++void QCoreWlanEngine::requestUpdate() ++{ ++ scanThread->getUserConfigurations(); ++ doRequestUpdate(); ++} ++ ++void QCoreWlanEngine::doRequestUpdate() ++{ ++ QMutexLocker locker(&mutex); ++ ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ ++ NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; ++ for (uint row = 0; row < [wifiInterfaces count]; ++row) { ++ scanThread->interfaceName = QCFString::toQString(CFStringRef([wifiInterfaces objectAtIndex:row])); ++ scanThread->start(); ++ } ++ locker.unlock(); ++ [autoreleasepool release]; ++} ++ ++bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName) ++{ ++ QMutexLocker locker(&mutex); ++ bool haswifi = false; ++ if(hasWifi) { ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)]; ++ if([defaultInterface power]) { ++ haswifi = true; ++ } ++ [autoreleasepool release]; ++ } ++ return haswifi; ++} ++ ++ ++QNetworkSession::State QCoreWlanEngine::sessionStateForId(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); ++ ++ if (!ptr) ++ return QNetworkSession::Invalid; ++ ++ if (!ptr->isValid) { ++ return QNetworkSession::Invalid; ++ } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { ++ return QNetworkSession::Connected; ++ } else if ((ptr->state & QNetworkConfiguration::Discovered) == ++ QNetworkConfiguration::Discovered) { ++ return QNetworkSession::Disconnected; ++ } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) { ++ return QNetworkSession::NotAvailable; ++ } else if ((ptr->state & QNetworkConfiguration::Undefined) == ++ QNetworkConfiguration::Undefined) { ++ return QNetworkSession::NotAvailable; ++ } ++ ++ return QNetworkSession::Invalid; ++} ++ ++QNetworkConfigurationManager::Capabilities QCoreWlanEngine::capabilities() const ++{ ++ return QNetworkConfigurationManager::ForcedRoaming; ++} ++ ++void QCoreWlanEngine::startNetworkChangeLoop() ++{ ++ ++ SCDynamicStoreContext dynStoreContext = { 0, this/*(void *)storeSession*/, NULL, NULL, NULL }; ++ storeSession = SCDynamicStoreCreate(NULL, ++ CFSTR("networkChangeCallback"), ++ networkChangeCallback, ++ &dynStoreContext); ++ if (!storeSession ) { ++ qWarning() << "could not open dynamic store: error:" << SCErrorString(SCError()); ++ return; ++ } ++ ++ CFMutableArrayRef notificationKeys; ++ notificationKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); ++ CFMutableArrayRef patternsArray; ++ patternsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); ++ ++ CFStringRef storeKey; ++ storeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, ++ kSCDynamicStoreDomainState, ++ kSCEntNetIPv4); ++ CFArrayAppendValue(notificationKeys, storeKey); ++ CFRelease(storeKey); ++ ++ storeKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, ++ kSCDynamicStoreDomainState, ++ kSCCompAnyRegex, ++ kSCEntNetIPv4); ++ CFArrayAppendValue(patternsArray, storeKey); ++ CFRelease(storeKey); ++ ++ if (!SCDynamicStoreSetNotificationKeys(storeSession , notificationKeys, patternsArray)) { ++ qWarning() << "register notification error:"<< SCErrorString(SCError()); ++ CFRelease(storeSession ); ++ CFRelease(notificationKeys); ++ CFRelease(patternsArray); ++ return; ++ } ++ CFRelease(notificationKeys); ++ CFRelease(patternsArray); ++ ++ runloopSource = SCDynamicStoreCreateRunLoopSource(NULL, storeSession , 0); ++ if (!runloopSource) { ++ qWarning() << "runloop source error:"<< SCErrorString(SCError()); ++ CFRelease(storeSession ); ++ return; ++ } ++ ++ CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode); ++ return; ++} ++ ++QNetworkSessionPrivate *QCoreWlanEngine::createSessionBackend() ++{ ++ return new QNetworkSessionPrivateImpl; ++} ++ ++QNetworkConfigurationPrivatePointer QCoreWlanEngine::defaultConfiguration() ++{ ++ return QNetworkConfigurationPrivatePointer(); ++} ++ ++bool QCoreWlanEngine::requiresPolling() const ++{ ++ return true; ++} ++ ++void QCoreWlanEngine::networksChanged() ++{ ++ QMutexLocker locker(&mutex); ++ ++ QStringList previous = accessPointConfigurations.keys(); ++ ++ QList foundConfigurations = scanThread->getConfigurations(); ++ while (!foundConfigurations.isEmpty()) { ++ QNetworkConfigurationPrivate *cpPriv = foundConfigurations.takeFirst(); ++ ++ previous.removeAll(cpPriv->id); ++ ++ if (accessPointConfigurations.contains(cpPriv->id)) { ++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(cpPriv->id); ++ ++ bool changed = false; ++ ++ ptr->mutex.lock(); ++ ++ if (ptr->isValid != cpPriv->isValid) { ++ ptr->isValid = cpPriv->isValid; ++ changed = true; ++ } ++ ++ if (ptr->name != cpPriv->name) { ++ ptr->name = cpPriv->name; ++ changed = true; ++ } ++ ++ if (ptr->bearerType != cpPriv->bearerType) { ++ ptr->bearerType = cpPriv->bearerType; ++ changed = true; ++ } ++ ++ if (ptr->state != cpPriv->state) { ++ ptr->state = cpPriv->state; ++ changed = true; ++ } ++ ++ ptr->mutex.unlock(); ++ ++ if (changed) { ++ locker.unlock(); ++ emit configurationChanged(ptr); ++ locker.relock(); ++ } ++ ++ delete cpPriv; ++ } else { ++ QNetworkConfigurationPrivatePointer ptr(cpPriv); ++ ++ accessPointConfigurations.insert(ptr->id, ptr); ++ ++ locker.unlock(); ++ emit configurationAdded(ptr); ++ locker.relock(); ++ } ++ } ++ ++ while (!previous.isEmpty()) { ++ QNetworkConfigurationPrivatePointer ptr = ++ accessPointConfigurations.take(previous.takeFirst()); ++ ++ locker.unlock(); ++ emit configurationRemoved(ptr); ++ locker.relock(); ++ } ++ ++ locker.unlock(); ++ emit updateCompleted(); ++ ++} ++ ++quint64 QCoreWlanEngine::bytesWritten(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ const QString interfaceStr = getInterfaceFromId(id); ++ return getBytes(interfaceStr,false); ++} ++ ++quint64 QCoreWlanEngine::bytesReceived(const QString &id) ++{ ++ QMutexLocker locker(&mutex); ++ const QString interfaceStr = getInterfaceFromId(id); ++ return getBytes(interfaceStr,true); ++} ++ ++quint64 QCoreWlanEngine::startTime(const QString &identifier) ++{ ++ QMutexLocker locker(&mutex); ++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; ++ quint64 timestamp = 0; ++ ++ NSString *filePath = @"/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist"; ++ NSDictionary* plistDict = [[[NSDictionary alloc] initWithContentsOfFile:filePath] autorelease]; ++ if(plistDict == nil) ++ return timestamp; ++ NSString *input = @"KnownNetworks"; ++ NSString *timeStampStr = @"_timeStamp"; ++ ++ NSString *ssidStr = @"SSID_STR"; ++ ++ for (id key in plistDict) { ++ if ([input isEqualToString:key]) { ++ ++ NSDictionary *knownNetworksDict = [plistDict objectForKey:key]; ++ if(knownNetworksDict == nil) ++ return timestamp; ++ for (id networkKey in knownNetworksDict) { ++ bool isFound = false; ++ NSDictionary *itemDict = [knownNetworksDict objectForKey:networkKey]; ++ if(itemDict == nil) ++ return timestamp; ++ NSInteger dictSize = [itemDict count]; ++ id objects[dictSize]; ++ id keys[dictSize]; ++ ++ [itemDict getObjects:objects andKeys:keys]; ++ bool ok = false; ++ for(int i = 0; i < dictSize; i++) { ++ if([ssidStr isEqualToString:keys[i]]) { ++ const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i])))); ++ if(ident == identifier) { ++ ok = true; ++ } ++ } ++ if(ok && [timeStampStr isEqualToString:keys[i]]) { ++ timestamp = (quint64)[objects[i] timeIntervalSince1970]; ++ isFound = true; ++ break; ++ } ++ } ++ if(isFound) ++ break; ++ } ++ } ++ } ++ [autoreleasepool release]; ++ return timestamp; ++} ++ ++quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b) ++{ ++ struct ifaddrs *ifAddressList, *ifAddress; ++ struct if_data *if_data; ++ ++ quint64 bytes = 0; ++ ifAddressList = nil; ++ if(getifaddrs(&ifAddressList) == 0) { ++ for(ifAddress = ifAddressList; ifAddress; ifAddress = ifAddress->ifa_next) { ++ if(interfaceName == ifAddress->ifa_name) { ++ if_data = (struct if_data*)ifAddress->ifa_data; ++ if(b) { ++ bytes = if_data->ifi_ibytes; ++ break; ++ } else { ++ bytes = if_data->ifi_obytes; ++ break; ++ } ++ } ++ } ++ freeifaddrs(ifAddressList); ++ } ++ return bytes; ++} ++ ++QT_END_NAMESPACE From 8c060691d732c95ca5868a26334a1c0381e0f586 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 5 Dec 2013 12:21:10 -0500 Subject: [PATCH 38/54] Update Mac deployment target to make it build on 10.9, apply Qt4 patches --- scripts/macosx-build-dependencies.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index 19c97097..d4ca1f7c 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -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 From 38a342215970396a5590281cd66d0ca9c9ab7e98 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 6 Dec 2013 00:39:21 -0500 Subject: [PATCH 39/54] #559 fixes for 10.9 --- tests/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0477a45d..b84775b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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) @@ -14,9 +14,16 @@ include(CMakeParseArguments.cmake) # Detect Lion and force gcc IF (APPLE) + # 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) 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++") + 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") From e3317ecc64659ae8be2a7b02d4df17dad6d875db Mon Sep 17 00:00:00 2001 From: "David Eccles (gringer)" Date: Fri, 6 Dec 2013 13:14:09 +1300 Subject: [PATCH 40/54] Fail if any polygon points for rotate_extrude are less than 0 --- src/PolySetCGALEvaluator.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index bc9206f3..e5eba2b9 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -457,7 +457,13 @@ 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"); + PRINT((boost::format("[Point %d on path %d has X coordinate %f]") % j % i % point_x).str()); + return NULL; + } + max_x = fmax(max_x, point_x); } int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa); From ede5c4b6882692dc28fc7017c4aeb87ec9222f8e Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Fri, 6 Dec 2013 00:52:51 -0500 Subject: [PATCH 41/54] delete ps when short-circuiting return, no need for explicit boost::format --- src/PolySetCGALEvaluator.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index e5eba2b9..599fd7f5 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -458,9 +458,10 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD double max_x = 0; for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) { double point_x = dxf.points[dxf.paths[i].indices[j]][0]; - if(point_x < 0){ + if (point_x < 0) { PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates"); - PRINT((boost::format("[Point %d on path %d has X coordinate %f]") % j % i % point_x).str()); + 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); From 40ae8c7b343fc5dafc8c9bdc7d8a63f6e8032fca Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sat, 7 Dec 2013 14:38:22 -0800 Subject: [PATCH 42/54] Add html report upload to test_pretty_print.py, removed wiki support --- tests/test_pretty_print.py | 878 ++++++++++++++++--------------------- 1 file changed, 381 insertions(+), 497 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index a31b1a86..c26b919d 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -25,544 +25,428 @@ # 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 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:(.*?)", - '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:(.*?)", + '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 = """ -

    [[WIKI_ROOTPATH]] test run report

    - -'''Sysid''': SYSID - -'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % )
    - -'''System info''': -
    -SYSINFO
    -
    - -start time: STARTDATE
    -end time : ENDDATE
    - -'''Image tests''' - - -{| border=1 cellspacing=0 cellpadding=1 -|- -| colspan=2 | FTESTNAME -|- -| Expected image || Actual image -|- -| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI -|} - -
    -TESTLOG
    -
    + 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 = '''''' + if data == '': + alt = 'alt="error: no image generated" ' + else: + alt = 'alt="openscad_test_image" ' + tag %= (data_uri, width, alt) + return tag -
    - - -'''Text tests''' - - -{|border=1 cellspacing=0 cellpadding=1 -|- -| FTESTNAME -|} - -
    -TESTLOG
    -
    - - -
    - -'''build.make and flags.make''' - -*[[MAKEFILE_NAME]] - -""" - 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('(.*?)',s) - repeat2 = ezsearch('(.*?)',s) - repeat3 = ezsearch('(.*?)',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('\n','',s) - s = re.sub('','',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
    \n'+makefiles[mf]+'\n
    ' - - 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 = '' - tail = '' - - 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

    ' - s+= '\nSystem info\n' - s+= '\n

    ' - s+= '

    '+sysinfo+'
    \n' - - s+= '\n
    '
    -	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
    ' - - if not include_passed: - s+= '

    Failed tests:

    \n' - - if len(tests_to_report)==0: - s+= '

    none

    ' - - for t in tests_to_report: - if t.type in ('txt', 'ast', 'csg', 'term', 'echo'): - s+='\n
    '+t.fullname+'
    \n' - s+='

    '+t.fulltestlog+'
    \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 = ' - # imatag_a = ' - imgtag_e = png_encode64( t.expectedfile, 250 ) - imgtag_a = png_encode64( t.actualfile, 250 ) - s+='' - s+='\n' - s+='\n ' - s+='\n
    '+t.fullname - s+='\n
    ExpectedActual' - s+='\n
    ' + imgtag_e + '' + imgtag_a + '
    ' - s+='\n
    '
    -			s+=t.fulltestlog
    -			s+='\n
    ' - else: - raise Exception("Unknown test type %r"%t.type) - - s+='\n\n

    \n\n' - - s+= '

    CMake .build files

    \n' - s+= '\n
    '
    -	makefiles_wikinames = {}
    -	for mf in sorted(makefiles.keys()):
    -		mfname = mf.strip().lstrip(os.path.sep)
    -		text = open(os.path.join(builddir,mfname)).read()
    -		s+= '

    '+mfname+'

    '
    -		s+= text
    -		tmp = mf.replace('CMakeFiles','').replace('.dir','')
    -		wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
    -		# s += '\n'+wikiname+'
    ' - s+= '\n
    ' - 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 = ''' + Test run for {sysid} + {style} + + +

    {project_name} test run report

    +

    + Sysid: {sysid} +

    +

    + Result summary: {numpassed} / {numtests} tests passed ({percent}%) +

    + +

    System info

    +
    {sysinfo}
    + +

    start time: {startdate}

    +

    end time: {enddate}

    + +

    Image tests

    + {image_tests} + +

    Text tests

    + {text_tests} + +

    build.make and flags.make

    + {makefiles} + ''' + + style = ''' + ''' + + image_template = ''' + + + + + +
    {test_name}
    Expected image Actual image
    {expected} {actual}
    + +
    +    {test_log}
    +    
    + ''' + + text_template = ''' + {test_name} + +
    +    {test_log}
    +    
    + ''' + + makefile_template = ''' +

    {name}

    +
    +        {text}
    +    
    + ''' + + 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[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 +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 + include_passed = False + 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) + + # --- End Command Line Parsing --- - 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 ) + 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' - 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' + 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(htmldata)) + ' bytes') + trysave(html_filename, html) - imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles) + 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' - 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]) - - 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 '--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() From 8d3365a79ab7afd287545da3f8b2ba12f0c43585 Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sat, 7 Dec 2013 14:50:49 -0800 Subject: [PATCH 43/54] Update design philosopy comments --- tests/test_pretty_print.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index c26b919d..44df156f 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -17,11 +17,9 @@ # Design philosophy # # 1. parse the data (images, logs) into easy-to-use data structures -# 2. wikifiy the data -# 3. save the wikified data to disk -# 4. generate html, including base64 encoding of images -# 5. save html file -# 6. upload html to public site and share with others +# 2. generate html, including base64 encoding of images +# 3. save html file +# 4. upload html to public site and share with others # todo # From 8a21092dc01dfe54550b8fceada35999bed3056b Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sat, 7 Dec 2013 17:59:07 -0800 Subject: [PATCH 44/54] Adding imports --- tests/test_pretty_print.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 44df156f..c2deab8c 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -33,6 +33,13 @@ import hashlib import subprocess import time import platform +try: + from urllib.request import urlopen + from urllib.parse import urlencode +except ImportError: + from urllib2 import urlopen + from urllib import urlencode + def tryread(filename): data = None From b131464f954b2d10f6b065b45038652af2379742 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sun, 8 Dec 2013 13:50:03 -0500 Subject: [PATCH 45/54] #559 CMAKE_OSX_DEPLOYMENT_TARGET needs to be cached --- tests/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b84775b8..f92eddf5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,14 +14,14 @@ include(CMakeParseArguments.cmake) # Detect Lion and force gcc IF (APPLE) - # 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) EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION) 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") From cb2b094b269646c79f48f525306b74cd7bc0f633 Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 10:51:25 -0800 Subject: [PATCH 46/54] Fixed small error in Templates.get --- tests/test_pretty_print.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index c2deab8c..349a730a 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -304,10 +304,10 @@ class Templates(object): def add(self, template, var, *args, **kwargs): self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs) - return self.filled[var] + return self.get(var) def get(self, var): - return self.filled[var] + return self.filled.get(var, '') def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles): From 8971c67fa209fe29ead54034cd0f9be39c0909ff Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 11:18:58 -0800 Subject: [PATCH 47/54] Fixed variable name error --- tests/test_pretty_print.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 349a730a..359165e8 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -441,7 +441,7 @@ def main(): 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(htmldata)) + ' bytes') + debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes') trysave(html_filename, html) page_url = create_page() From a22ebd608d941b5ae1767a2903f4bacc925840ed Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 11:28:19 -0800 Subject: [PATCH 48/54] Moved include_passed --- tests/test_pretty_print.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 359165e8..c3035c55 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -395,6 +395,7 @@ def debug(x): print 'test_pretty_print: ' + x debug_test_pp = False +include_passed = False builddir = os.getcwd() def main(): @@ -411,7 +412,6 @@ def main(): debug_test_pp = True maxretry = 10 - include_passed = False if '--include-passed' in sys.argv: include_passed = True From e27f971d9c3dc26a9a023a2aea4c265dac53437b Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 12:15:04 -0800 Subject: [PATCH 49/54] Changed upload code to use gists --- tests/test_pretty_print.py | 90 +++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index c3035c55..a88a22ea 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -17,9 +17,11 @@ # Design philosophy # # 1. parse the data (images, logs) into easy-to-use data structures -# 2. generate html, including base64 encoding of images -# 3. save html file -# 4. upload html to public site and share with others +# 2. wikifiy the data +# 3. save the wikified data to disk +# 4. generate html, including base64 encoding of images +# 5. save html file +# 6. upload html to public site and share with others # todo # @@ -34,12 +36,12 @@ import subprocess import time import platform try: - from urllib.request import urlopen - from urllib.parse import urlencode -except ImportError: - from urllib2 import urlopen - from urllib import urlencode - + from urllib.request import urlopen, Request +except: + from urllib2 import urlopen, Request +import json +import base64 + def tryread(filename): data = None @@ -304,7 +306,7 @@ class Templates(object): def add(self, template, var, *args, **kwargs): self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs) - return self.get(var) + return self.filled[var] def get(self, var): return self.filled.get(var, '') @@ -361,32 +363,46 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles): # --- Web Upload --- -def postify(data): - return urlencode(data).encode() +API_URL = 'https://api.github.com/%s' +# Username is personal access token, from https://github.com/settings/applications +# This way, no password is needed +USERNAME = 'b2af28787fb1efd9a5b3a3b4f1be8a3ac9b5b335' +PASSWORD = '' -def create_page(): - data = { - 'action': 'create', - 'type': 'html' - } +def make_auth(username, password): + auth = '%s:%s' % (USERNAME, PASSWORD) + return base64.b64encode(auth.encode()) + +def post_gist(name, content): + gist = '''{ + "description": "", + "public": true, + "files": { + "%s": { + "content": "%s" + } + } + }''' + gist = gist % (name, content) + + req = Request(API_URL % 'gists') + req.add_header('Authorization', b'Basic ' + make_auth(USERNAME, PASSWORD)) try: - response = urlopen('http://www.dinkypage.com', data=postify(data)) + result = urlopen(req, data=gist) except: + print 'Could not upload results' return None - return response.geturl() + return json.loads(result.read()) -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() + +def get_raw_urls(result): + files = result.get('files', {}) + for file in files: + yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com') + +result = post_gist('aaabbb.html', '''

    I\'m asdf

    ''') +for url in get_raw_urls(result): + print(url) # --- End Web Upload --- @@ -438,18 +454,20 @@ def main(): print 'found', len(makefiles),'makefiles,', print 'found', len(tests),'test results' + 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) - 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: + result = post_gist(name=html_basename, content=html) + if result is None: print 'could not upload html report' + return + + for url in get_raw_urls(result): + print 'html report uploaded at', url debug('test_pretty_print complete') From 88cc7edafd47ec167609c05eb22c3f8eb642d225 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sun, 8 Dec 2013 15:16:58 -0500 Subject: [PATCH 50/54] #559 Fix Qt font rendering on OS X 10.9 --- patches/qt4/patch-qeventdispatcher.diff | 86 +++++++++++++++++++++++++ patches/qt4/patch-qfontdatabase.diff | 29 +++++++++ 2 files changed, 115 insertions(+) create mode 100644 patches/qt4/patch-qeventdispatcher.diff create mode 100644 patches/qt4/patch-qfontdatabase.diff diff --git a/patches/qt4/patch-qeventdispatcher.diff b/patches/qt4/patch-qeventdispatcher.diff new file mode 100644 index 00000000..89ed4788 --- /dev/null +++ b/patches/qt4/patch-qeventdispatcher.diff @@ -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 cocoaModalSessionStack; ++ static QStack 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 QEventDispatcherMacPrivate::cocoaModalSessionStack; ++QStack 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(); diff --git a/patches/qt4/patch-qfontdatabase.diff b/patches/qt4/patch-qfontdatabase.diff new file mode 100644 index 00000000..c0788906 --- /dev/null +++ b/patches/qt4/patch-qfontdatabase.diff @@ -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 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 ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); ++ QCFType ctFont = CTFontCreateWithName(QCFString(db->families[k]->postscriptName), 12, NULL); + if (ctFont) { + fontName = CTFontCopyFullName(ctFont); + goto found; From eb046015d2fd4fa3e4e3c8844cd6dc8f4d3eca99 Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sun, 8 Dec 2013 15:17:17 -0500 Subject: [PATCH 51/54] #559 Fix Qt font rendering on OS X 10.9 --- src/openscad.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/openscad.cc b/src/openscad.cc index ece68189..ab842357 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -474,6 +474,13 @@ bool QtUseGUI() int gui(vector &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)); From d6bffc4691cf1467cf93d527724f7278b418273d Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 12:17:19 -0800 Subject: [PATCH 52/54] Taking out test accidentially left in, my user token --- tests/test_pretty_print.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index a88a22ea..b601c842 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -366,7 +366,7 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles): API_URL = 'https://api.github.com/%s' # Username is personal access token, from https://github.com/settings/applications # This way, no password is needed -USERNAME = 'b2af28787fb1efd9a5b3a3b4f1be8a3ac9b5b335' +USERNAME = '' # add OpenScad user token PASSWORD = '' def make_auth(username, password): @@ -400,9 +400,6 @@ def get_raw_urls(result): for file in files: yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com') -result = post_gist('aaabbb.html', '''

    I\'m asdf

    ''') -for url in get_raw_urls(result): - print(url) # --- End Web Upload --- From b7c818bf00b5c9e23ee97ce6669eb66a9b404562 Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 16:15:50 -0800 Subject: [PATCH 53/54] Revert to uploading to dinkypage, gists won't work --- tests/test_pretty_print.py | 73 ++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index b601c842..675867e7 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -36,12 +36,11 @@ import subprocess import time import platform try: - from urllib.request import urlopen, Request + from urllib.request import urlopen + from urllib.parse import urlencode except: - from urllib2 import urlopen, Request -import json -import base64 - + from urllib2 import urlopen + from urllib import urlencode def tryread(filename): data = None @@ -363,43 +362,32 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles): # --- Web Upload --- -API_URL = 'https://api.github.com/%s' -# Username is personal access token, from https://github.com/settings/applications -# This way, no password is needed -USERNAME = '' # add OpenScad user token -PASSWORD = '' +def postify(data): + return urlencode(data).encode() -def make_auth(username, password): - auth = '%s:%s' % (USERNAME, PASSWORD) - return base64.b64encode(auth.encode()) - -def post_gist(name, content): - gist = '''{ - "description": "", - "public": true, - "files": { - "%s": { - "content": "%s" - } - } - }''' - gist = gist % (name, content) - - req = Request(API_URL % 'gists') - req.add_header('Authorization', b'Basic ' + make_auth(USERNAME, PASSWORD)) +def create_page(): + data = { + 'action': 'create', + 'type': 'html' + } try: - result = urlopen(req, data=gist) + response = urlopen('http://www.dinkypage.com', data=postify(data)) except: - print 'Could not upload results' return None - return json.loads(result.read()) - - -def get_raw_urls(result): - files = result.get('files', {}) - for file in files: - yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com') + 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 --- @@ -458,13 +446,12 @@ def main(): debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes') trysave(html_filename, html) - result = post_gist(name=html_basename, content=html) - if result is None: + 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' - return - - for url in get_raw_urls(result): - print 'html report uploaded at', url debug('test_pretty_print complete') From 58bf7386a11d6f6fb247cf95b89bbbb2682832ca Mon Sep 17 00:00:00 2001 From: a-e-m Date: Sun, 8 Dec 2013 16:32:36 -0800 Subject: [PATCH 54/54] Added --upload command line option --- tests/test_pretty_print.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 675867e7..c0d35bb8 100755 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -426,6 +426,11 @@ def main(): if not 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 --- @@ -446,12 +451,13 @@ def main(): debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes') trysave(html_filename, html) - 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 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' debug('test_pretty_print complete')