summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--icons/openscad.desktop2
-rw-r--r--openscad.pro2
-rw-r--r--patches/qt4/patch-libtiff.diff18
-rw-r--r--patches/qt4/patch-qeventdispatcher.diff86
-rw-r--r--patches/qt4/patch-qfontdatabase.diff29
-rw-r--r--patches/qt4/patch-src_corelib_global_qglobal.h.diff14
-rw-r--r--patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff1382
-rwxr-xr-xscripts/builder.sh90
-rwxr-xr-xscripts/check-dependencies.sh84
-rwxr-xr-xscripts/macosx-build-dependencies.sh13
-rwxr-xr-xscripts/mingw-x-build-dependencies.sh27
-rwxr-xr-xscripts/uni-build-dependencies.sh9
-rwxr-xr-xscripts/uni-get-dependencies.sh10
-rw-r--r--src/AboutDialog.html2
-rw-r--r--src/CGALEvaluator.cc18
-rw-r--r--src/CGAL_Nef3_workaround.h352
-rw-r--r--src/CGAL_Nef_polyhedron.cc19
-rw-r--r--src/CocoaUtils.mm4
-rw-r--r--src/MainWindow.h1
-rw-r--r--src/MainWindow.ui7
-rw-r--r--src/PlatformUtils.cc2
-rw-r--r--src/PolySetCGALEvaluator.cc9
-rw-r--r--src/QGLView.cc11
-rw-r--r--src/QGLView.h1
-rw-r--r--src/cgal.h8
-rw-r--r--src/control.cc24
-rw-r--r--src/dxfdata.h4
-rw-r--r--src/editor.cc14
-rw-r--r--src/editor.h2
-rw-r--r--src/export.cc10
-rw-r--r--src/expr.cc23
-rw-r--r--src/linalg.h5
-rw-r--r--src/mainwin.cc8
-rw-r--r--src/modcontext.cc2
-rw-r--r--src/openscad.cc9
-rw-r--r--src/parser.y2
-rw-r--r--src/stl-utils.cc2
-rw-r--r--src/value.cc102
-rw-r--r--src/value.h76
-rw-r--r--src/version_check.h14
-rw-r--r--testdata/scad/features/for-tests.scad6
-rw-r--r--testdata/scad/features/polygon-tests.scad6
-rw-r--r--testdata/scad/features/projection-cut-tests.scad15
-rw-r--r--testdata/scad/features/projection-extrude-tests.scad3
-rw-r--r--testdata/scad/features/projection-tests.scad31
-rw-r--r--testdata/scad/features/render-2d-tests.scad9
-rw-r--r--testdata/scad/misc/children-tests.scad2
-rw-r--r--testdata/scad/misc/range-tests.scad20
-rw-r--r--tests/CMakeLists.txt53
-rw-r--r--tests/regression/cgalpngtest/for-tests-expected.pngbin10995 -> 7124 bytes
-rw-r--r--tests/regression/cgalpngtest/polygon-tests-expected.pngbin7755 -> 8321 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-cut-tests-expected.pngbin0 -> 7286 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-extrude-tests-expected.pngbin0 -> 8789 bytes
-rw-r--r--tests/regression/cgalpngtest/projection-tests-expected.pngbin5836 -> 7837 bytes
-rw-r--r--tests/regression/cgalpngtest/render-2d-tests-expected.pngbin7029 -> 7730 bytes
-rw-r--r--tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.pngbin0 -> 4350 bytes
-rw-r--r--tests/regression/dumptest/for-tests-expected.csg18
-rw-r--r--tests/regression/dumptest/polygon-tests-expected.csg3
-rw-r--r--tests/regression/dumptest/projection-cut-tests-expected.csg33
-rw-r--r--tests/regression/dumptest/projection-extrude-tests-expected.csg20
-rw-r--r--tests/regression/dumptest/projection-tests-expected.csg59
-rw-r--r--tests/regression/dumptest/render-2d-tests-expected.csg10
-rw-r--r--tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg5
-rw-r--r--tests/regression/echotest/children-tests-expected.echo1
-rw-r--r--tests/regression/echotest/range-tests-expected.echo81
-rw-r--r--tests/regression/moduledumptest/allexpressions-expected.ast2
-rw-r--r--tests/regression/opencsgtest/for-tests-expected.pngbin11584 -> 7416 bytes
-rw-r--r--tests/regression/opencsgtest/polygon-tests-expected.pngbin8736 -> 9532 bytes
-rw-r--r--tests/regression/opencsgtest/projection-cut-tests-expected.pngbin0 -> 7644 bytes
-rw-r--r--tests/regression/opencsgtest/projection-extrude-tests-expected.pngbin0 -> 9460 bytes
-rw-r--r--tests/regression/opencsgtest/projection-tests-expected.pngbin6531 -> 8825 bytes
-rw-r--r--tests/regression/opencsgtest/render-2d-tests-expected.pngbin7646 -> 7951 bytes
-rw-r--r--tests/regression/throwntogethertest/for-tests-expected.pngbin6927 -> 7533 bytes
-rw-r--r--tests/regression/throwntogethertest/polygon-tests-expected.pngbin8797 -> 9626 bytes
-rw-r--r--tests/regression/throwntogethertest/projection-cut-tests-expected.pngbin0 -> 7644 bytes
-rw-r--r--tests/regression/throwntogethertest/projection-extrude-tests-expected.pngbin0 -> 9460 bytes
-rw-r--r--tests/regression/throwntogethertest/projection-tests-expected.pngbin5865 -> 8825 bytes
-rw-r--r--tests/regression/throwntogethertest/render-2d-tests-expected.pngbin7646 -> 7951 bytes
-rwxr-xr-xtests/test_pretty_print.py917
80 files changed, 3020 insertions, 773 deletions
diff --git a/README.md b/README.md
index 293efbb..690af65 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ Follow the instructions for the platform you're compiling on below.
* [boost (1.35 - 1.53)](http://www.boost.org/)
* [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
-* [Eigen (2.0.x->3.x)](http://eigen.tuxfamily.org/)
+* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/)
* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
* [Bison (2.4)](http://www.gnu.org/software/bison/)
* [Flex (2.5.35)](http://flex.sourceforge.net/)
diff --git a/icons/openscad.desktop b/icons/openscad.desktop
index 07df5aa..f0282ac 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;
diff --git a/openscad.pro b/openscad.pro
index fd9f494..b38419e 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/patches/qt4/patch-libtiff.diff b/patches/qt4/patch-libtiff.diff
new file mode 100644
index 0000000..5b7f9ec
--- /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 <sys/types.h> does not define. */
+ /* #undef off_t */
+
diff --git a/patches/qt4/patch-qeventdispatcher.diff b/patches/qt4/patch-qeventdispatcher.diff
new file mode 100644
index 0000000..89ed478
--- /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<QCocoaModalSessionInfo> cocoaModalSessionStack;
++ static QStack<QCocoaModalSessionInfo> cocoaModalSessionStackPendingEnd;
+ static bool currentExecIsNSAppRun;
+ static bool nsAppRunCalledByQt;
+ static bool cleanupModalSessionsNeeded;
+@@ -180,6 +181,7 @@
+ static NSModalSession currentModalSession();
+ static void updateChildrenWorksWhenModal();
+ static void temporarilyStopAllModalSessions();
++ static void stopAllPendingEndModalSessions();
+ static void beginModalSession(QWidget *widget);
+ static void endModalSession(QWidget *widget);
+ static void cancelWaitForMoreEvents();
+--- src/gui/kernel/qeventdispatcher_mac.mm 2013-06-07 01:16:59.000000000 -0400
++++ src/gui/kernel/qeventdispatcher_mac_new-833e02de99494686f8dd7a567f6e19e847508f11.mm 2013-12-08 14:30:59.000000000 -0500
+@@ -603,6 +603,9 @@
+ while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
+ qt_mac_waitForMoreModalSessionEvents();
+
++ // stop all pending end modal sessions
++ d->stopAllPendingEndModalSessions();
++
+ if (!d->interrupt && session == d->currentModalSessionCached) {
+ // Someone called [NSApp stopModal:] from outside the event
+ // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
+@@ -678,6 +681,9 @@
+ if (!d->interrupt)
+ QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+
++ // stop all pending end modal sessions
++ d->stopAllPendingEndModalSessions();
++
+ // Since the window that holds modality might have changed while processing
+ // events, we we need to interrupt when we return back the previous process
+ // event recursion to ensure that we spin the correct modal session.
+@@ -781,6 +787,7 @@
+
+ #ifdef QT_MAC_USE_COCOA
+ QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
++QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStackPendingEnd;
+ bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
+ bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
+ bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false;
+@@ -828,6 +835,20 @@
+ currentModalSessionCached = 0;
+ }
+
++void QEventDispatcherMacPrivate::stopAllPendingEndModalSessions()
++{
++ // stop all modal sessions pending end
++ int stackSize = cocoaModalSessionStackPendingEnd.size();
++ for (int i=stackSize-1; i>=0; --i) {
++ QCocoaModalSessionInfo &info = cocoaModalSessionStackPendingEnd[i];
++ cocoaModalSessionStackPendingEnd.remove(i);
++ if (info.session) {
++ [NSApp endModalSession:info.session];
++ [(NSWindow *)info.nswindow release];
++ }
++ }
++}
++
+ NSModalSession QEventDispatcherMacPrivate::currentModalSession()
+ {
+ // If we have one or more modal windows, this function will create
+@@ -925,10 +946,12 @@
+ }
+ cocoaModalSessionStack.remove(i);
+ currentModalSessionCached = 0;
+- if (info.session) {
+- [NSApp endModalSession:info.session];
+- [(NSWindow *)info.nswindow release];
+- }
++
++ // Cannot stop the sessions here since we might still be inside a
++ // [NSApp runModalSession:] call. Add the session to the pending end stack and
++ // process the stack after the call to [NSApp runModalSession:] returns.
++ if (info.session)
++ cocoaModalSessionStackPendingEnd.push(info);
+ }
+
+ updateChildrenWorksWhenModal();
diff --git a/patches/qt4/patch-qfontdatabase.diff b/patches/qt4/patch-qfontdatabase.diff
new file mode 100644
index 0000000..c078890
--- /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<CFArrayRef> languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
+ CFIndex length = CFArrayGetCount(languages);
+@@ -327,7 +328,7 @@
+ if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
+ QByteArray family_name = db->families[k]->name.toUtf8();
+ #if defined(QT_MAC_USE_COCOA)
+- QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
++ QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->postscriptName), 12, NULL);
+ if (ctFont) {
+ fontName = CTFontCopyFullName(ctFont);
+ goto found;
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 0000000..8c55c5a
--- /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 0000000..61b2eef
--- /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 <QtCore/qdebug.h>
+
+ #include <QDir>
+-#include <CoreWLAN/CoreWLAN.h>
+-#include <CoreWLAN/CWInterface.h>
+-#include <CoreWLAN/CWNetwork.h>
+-#include <CoreWLAN/CWNetwork.h>
+-#include <CoreWLAN/CW8021XProfile.h>
+-
+-#include <Foundation/NSEnumerator.h>
+-#include <Foundation/NSKeyValueObserving.h>
+-#include <Foundation/NSAutoreleasePool.h>
+-#include <Foundation/NSLock.h>
+-
+-#include <SystemConfiguration/SCNetworkConfiguration.h>
++
++extern "C" { // Otherwise it won't find CWKeychain* symbols at link time
++#import <CoreWLAN/CoreWLAN.h>
++}
++
+ #include "private/qcore_mac_p.h"
+
+ #include <net/if.h>
+ #include <ifaddrs.h>
+
+-inline QString qt_NSStringToQString(const NSString *nsstr)
+-{ return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
+-
+-inline NSString *qt_QStringToNSString(const QString &qstr)
+-{ return [const_cast<NSString *>(reinterpret_cast<const NSString *>(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 <QString,QString> 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<QString,QString> 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<QString, QMap<QString, QString> > 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<QString, QMap<QString,QString> > 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 <SystemConfiguration/SCNetworkConfiguration.h>
++
++@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<QCoreWlanEngine*>(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<QString, QMap<QString,QString> > 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<QString, QString> 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<QNetworkConfigurationPrivate *> QScanThread::getConfigurations()
++{
++ QMutexLocker locker(&mutex);
++
++ QList<QNetworkConfigurationPrivate *> 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 <QString,QString> 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<QString,QString> map;
++ map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName)));
++ userProfiles.insert(networkName, map);
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ [autoreleasepool release];
++}
++
++QString QScanThread::getSsidFromNetworkName(const QString &name)
++{
++ QMutexLocker locker(&mutex);
++
++ QMapIterator<QString, QMap<QString,QString> > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap<QString,QString> map = i.value();
++ QMapIterator<QString, QString> 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<QString, QMap<QString,QString> > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap<QString,QString> map = i.value();
++ QMapIterator<QString, QString> 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<QString, QMap<QString,QString> > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap<QString,QString> 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<QString, QMap<QString,QString> > 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<QNetworkConfigurationPrivate *> 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
diff --git a/scripts/builder.sh b/scripts/builder.sh
index 552d559..ca7e5b2 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/check-dependencies.sh b/scripts/check-dependencies.sh
index c4f2893..b63c677 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"
@@ -261,32 +256,47 @@ pkg_config_search()
get_minversion_from_readme()
{
- if [ -e README.md ]; then READFILE=README.md; fi
- if [ -e ../README.md ]; then READFILE=../README.md; fi
- if [ ! $READFILE ]; then
- if [ "`command -v dirname`" ]; then
- READFILE=`dirname $0`/../README.md
- fi
- fi
- if [ ! $READFILE ]; then echo "cannot find README.md"; exit 1; fi
debug get_minversion_from_readme $*
+
+ # Extract dependency name
if [ ! $1 ]; then return; fi
depname=$1
- local grv_tmp=
+
debug $depname
- # example--> * [CGAL (3.6 - 3.9)] (www.cgal.org) becomes 3.6
- # steps: eliminate *, find left (, find -, make 'x' into 0, delete junk
- grv_tmp=`grep -i ".$depname.*([0-9]" $READFILE | sed s/"*"//`
- debug $grv_tmp
- grv_tmp=`echo $grv_tmp | awk -F"(" '{print $2}'`
- debug $grv_tmp
- grv_tmp=`echo $grv_tmp | awk -F"-" '{print $1}'`
- debug $grv_tmp
- grv_tmp=`echo $grv_tmp | sed s/"x"/"0"/g`
- debug $grv_tmp
- grv_tmp=`echo $grv_tmp | sed s/"[^0-9.]"//g`
- debug $grv_tmp
- get_minversion_from_readme_result=$grv_tmp
+ local grv_tmp=
+ for READFILE in README.md ../README.md "`dirname "$0"`/../README.md"
+ do
+ if [ ! -e "$READFILE" ]
+ then
+ debug "get_minversion_from_readme $READFILE not found"
+ continue
+ fi
+ debug "get_minversion_from_readme $READFILE found"
+ grep -qi ".$depname.*([0-9]" $READFILE || continue
+ grv_tmp="`grep -i ".$depname.*([0-9]" $READFILE | sed s/"*"//`"
+ debug $grv_tmp
+ grv_tmp="`echo $grv_tmp | awk -F"(" '{print $2}'`"
+ debug $grv_tmp
+ grv_tmp="`echo $grv_tmp | awk -F"-" '{print $1}'`"
+ debug $grv_tmp
+ grv_tmp="`echo $grv_tmp | sed s/"x"/"0"/g`"
+ debug $grv_tmp
+ grv_tmp="`echo $grv_tmp | sed s/"[^0-9.]"//g`"
+ debug $grv_tmp
+ if [ "z$grv_tmp" = "z" ]
+ then
+ debug "get_minversion_from_readme no result for $depname from $READFILE"
+ continue
+ fi
+ get_minversion_from_readme_result=$grv_tmp
+ return 0
+ done
+ if [ "z$grv_tmp" = "z" ]
+ then
+ debug "get_minversion_from_readme no result for $depname found anywhere"
+ get_minversion_from_readme_result=""
+ return 0
+ fi
}
find_min_version()
@@ -462,7 +472,7 @@ check_old_local()
{
warnon=
if [ "`uname | grep -i linux`" ]; then
- header_list="opencsg.h CGAL boost GL/glew.h gmp.h mpfr.h eigen2 eigen3"
+ header_list="opencsg.h CGAL boost GL/glew.h gmp.h mpfr.h eigen3"
for i in $header_list; do
if [ -e /usr/local/include/$i ]; then
echo "Warning: you have a copy of "$i" under /usr/local/include"
@@ -533,7 +543,7 @@ main()
dep_minver=$find_min_version_result
compare_version $dep_minver $dep_sysver
dep_compare=$compare_version_result
- pretty_print $depname $dep_minver $dep_sysver $dep_compare
+ pretty_print $depname "$dep_minver" "$dep_sysver" $dep_compare
done
check_old_local
check_misc
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 994d2a1..d4ca1f7 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
@@ -309,10 +312,10 @@ build_eigen()
rm -rf eigen-$version
EIGENDIR="none"
- if [ $version = "2.0.17" ]; then EIGENDIR=eigen-eigen-b23437e61a07; fi
if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4;
elif [ $version = "3.1.3" ]; then EIGENDIR=eigen-eigen-2249f9c22fe8;
- elif [ $version = "3.1.4" ]; then EIGENDIR=eigen-eigen-36bf2ceaf8f5; fi
+ elif [ $version = "3.1.4" ]; then EIGENDIR=eigen-eigen-36bf2ceaf8f5;
+ elif [ $version = "3.2.0" ]; then EIGENDIR=eigen-eigen-ffa86ffb5570; fi
if [ $EIGENDIR = "none" ]; then
echo Unknown eigen version. Please edit script.
@@ -439,7 +442,7 @@ echo "Using basedir:" $BASEDIR
mkdir -p $SRCDIR $DEPLOYDIR
build_qt 4.8.5
# NB! For eigen, also update the path in the function
-build_eigen 3.1.4
+build_eigen 3.2.0
build_gmp 5.1.3
build_mpfr 3.1.2
build_boost 1.54.0
diff --git a/scripts/mingw-x-build-dependencies.sh b/scripts/mingw-x-build-dependencies.sh
index e9f124b..c0f658d 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:
#
@@ -48,21 +53,25 @@ if [ ! -e $MXEDIR ]; then
mkdir -p $MXEDIR
cd $MXEDIR/..
echo "Downloading MXE into " $PWD
- if [ "`echo $* | grep 64`" ]; then
- git clone -b multi-rebase git://github.com/tonytheodore/mxe.git $MXEDIR
- else
- git clone git://github.com/mxe/mxe.git $MXEDIR
- fi
+ git clone git://github.com/mxe/mxe.git $MXEDIR
fi
echo "entering" $MXEDIR
cd $MXEDIR
if [ "`echo $* | grep 64`" ]; then
- MXE_TARGETS='x86_64-w64-mingw32'
+ MXE_TARGETS='x86_64-w64-mingw32'
+ if [ "`echo $* | grep download`" ]; then
+ PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt'
+ else
PACKAGES='mpfr eigen opencsg cgal qt'
+ fi
else
- MXE_TARGETS=
+ MXE_TARGETS='i686-pc-mingw32' # fixme - does this work? test it.
+ if [ "`echo $* | grep download`" ]; then
+ PACKAGES='download-mpfr download-eigen download-opencsg download-cgal download-qt download-nsis'
+ else
PACKAGES='mpfr eigen opencsg cgal qt nsis'
+ fi
fi
echo make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS
make $PACKAGES MXE_TARGETS=$MXE_TARGETS -j $NUMCPU JOBS=$NUMJOBS
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index 48f162a..e652c47 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
@@ -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 31337c8..a0306ef 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
}
@@ -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 b5a5d7c..99e7c3b 100644
--- a/src/AboutDialog.html
+++ b/src/AboutDialog.html
@@ -47,7 +47,7 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
<li><a href="http://gmplib.org/">GNU GMP</a>
<li><a href="http://www.mpfr.org/">GNU MPFR</a>
<li><a href="http://www.cgal.org">CGAL</a>
-<li><a href="http://eigen.tuxfamily.org">Eigen2</a>
+<li><a href="http://eigen.tuxfamily.org">Eigen</a>
<li><a href="http://www.opencsg.org">OpenCSG</a>
<li><a href="http://www.opengl.org/">OpenGL</a>
<li><a href="http://glew.sourceforge.net">GLEW</a>
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index ec01315..86118d7 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -159,9 +159,21 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
PRINT("Hull() currently requires a valid 2-manifold. Please modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export");
}
else {
- chN.p3->convert_to_Polyhedron(P);
- std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
- boost::bind(static_cast<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
+ bool err = false;
+ std::string errmsg("");
+ try {
+ err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(chN.p3), P );
+ //chN.p3->convert_to_Polyhedron(P);
+ } catch (const CGAL::Failure_exception &e) {
+ err = true;
+ errmsg = std::string(e.what());
+ }
+ if (err) {
+ PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", errmsg);
+ } else {
+ std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
+ boost::bind(static_cast<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
+ }
}
}
chnode->progress_report();
diff --git a/src/CGAL_Nef3_workaround.h b/src/CGAL_Nef3_workaround.h
new file mode 100644
index 0000000..c2482ac
--- /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 <seel@mpi-sb.mpg.de>
+// Miguel Granados <granados@mpi-sb.mpg.de>
+// Susan Hert <hert@mpi-sb.mpg.de>
+// Lutz Kettner <kettner@mpi-sb.mpg.de>
+// Ralf Osbild <osbild@mpi-sb.mpg.de>
+// Peter Hachenberger <hachenberger@mpi-sb.mpg.de>
+
+/*
+ modified by don bright for OpenSCAD, 2013.
+
+This works around issue #410, where CGAL's Nef_Polyhedron3.convert_to_Polyhedron
+throws an uncatchable exception, due to an CGAL_Assertion being thrown in
+Polyhedron Incremental Builder's destructor while a Triangulation exception
+is still active, resulting in program termination (crashing).
+
+The purpose here is not to improve/change the way CGAL's Nef code works,
+but instead to tweak it slightly to prevent OpenSCAD from crashing. The
+behavior of the code should otherwise be exactly the same as CGAL's standard
+code.
+
+This file was created by copying three sections
+from CGAL's Nef_polyhedron3.h that were protected/private:
+
+ Triangulation_handler2
+ Build_Polyhedron
+ convert_to_polyhedron
+
+Very small code changes have been made. First, there are many template
+type specifiers added to enable the movement of the code to the outside
+of the Nef Polyhedron class. Second, there is a try{}catch(...){} block
+added in the Builder around the Triangulation code. Third, there is an error
+variable added for non-Exception communication with the caller.
+
+Eventually, if CGAL itself is updated and the update is widely
+distributed, this file may become obsolete and can be deleted from OpenSCAD
+
+*/
+
+
+#ifndef _CGAL_NEF3_WORKAROUND_H
+#define _CGAL_NEF3_WORKAROUND_H
+
+#include <CGAL/Polyhedron_incremental_builder_3.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/assertions.h>
+
+#include <CGAL/Constrained_triangulation_2.h>
+#include <CGAL/Triangulation_data_structure_2.h>
+#include <CGAL/Projection_traits_xy_3.h>
+#include <CGAL/Projection_traits_yz_3.h>
+#include <CGAL/Projection_traits_xz_3.h>
+#include <CGAL/Constrained_triangulation_face_base_2.h>
+
+#include "printutils.h"
+
+namespace nefworkaround {
+
+template<typename Kernel, typename Nef>
+class Triangulation_handler2 {
+
+ typedef typename CGAL::Triangulation_vertex_base_2<Kernel> Vb;
+ typedef typename CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
+ typedef typename CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
+ typedef typename CGAL::Constrained_triangulation_2<Kernel,TDS> CT;
+
+ typedef typename CT::Face_handle Face_handle;
+ typedef typename CT::Vertex_handle CTVertex_handle;
+ typedef typename CT::Finite_faces_iterator Finite_face_iterator;
+ typedef typename CT::Edge Edge;
+ CT ct;
+ CGAL::Unique_hash_map<Face_handle, bool> visited;
+ CGAL::Unique_hash_map<CTVertex_handle, typename Nef::Vertex_const_handle> ctv2v;
+ Finite_face_iterator fi;
+ typename Nef::Plane_3 supporting_plane;
+
+ public:
+ Triangulation_handler2(typename Nef::Halffacet_const_handle f) :
+ visited(false), supporting_plane(f->plane()) {
+
+ typename Nef::Halffacet_cycle_const_iterator fci;
+ for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
+ if(fci.is_shalfedge()) {
+ typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
+ CGAL_For_all(sfc,send) {
+ CGAL_NEF_TRACEN(" insert point" << sfc->source()->source()->point());
+ CTVertex_handle ctv = ct.insert(sfc->source()->source()->point());
+ ctv2v[ctv] = sfc->source()->source();
+ }
+ }
+ }
+
+ for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
+ if(fci.is_shalfedge()) {
+ typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
+ CGAL_For_all(sfc,send) {
+ CGAL_NEF_TRACEN(" insert constraint" << sfc->source()->source()->point()
+ << "->" << sfc->source()->twin()->source()->point());
+ ct.insert_constraint(sfc->source()->source()->point(),
+ sfc->source()->twin()->source()->point());
+ }
+ }
+ }
+ CGAL_assertion(ct.is_valid());
+
+ CGAL_NEF_TRACEN("number of finite triangles " << ct.number_of_faces());
+
+ typename CT::Face_handle infinite = ct.infinite_face();
+ typename CT::Vertex_handle ctv = infinite->vertex(1);
+ if(ct.is_infinite(ctv)) ctv = infinite->vertex(2);
+ CGAL_assertion(!ct.is_infinite(ctv));
+
+ typename CT::Face_handle opposite;
+ typename CT::Face_circulator vc(ctv,infinite);
+ do { opposite = vc++;
+ } while(!ct.is_constrained(typename CT::Edge(vc,vc->index(opposite))));
+ typename CT::Face_handle first = vc;
+
+ CGAL_assertion(!ct.is_infinite(first));
+ traverse_triangulation(first, first->index(opposite));
+
+ fi = ct.finite_faces_begin();
+ }
+
+ void traverse_triangulation(Face_handle f, int parent) {
+ visited[f] = true;
+ if(!ct.is_constrained(Edge(f,ct.cw(parent))) && !visited[f->neighbor(ct.cw(parent))]) {
+ Face_handle child(f->neighbor(ct.cw(parent)));
+ traverse_triangulation(child, child->index(f));
+ }
+ if(!ct.is_constrained(Edge(f,ct.ccw(parent))) && !visited[f->neighbor(ct.ccw(parent))]) {
+ Face_handle child(f->neighbor(ct.ccw(parent)));
+ traverse_triangulation(child, child->index(f));
+ }
+ }
+
+ template<typename Triangle_3>
+ bool get_next_triangle(Triangle_3& tr) {
+ while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
+ if(fi == ct.finite_faces_end()) return false;
+ tr = Triangle_3(fi->vertex(0)->point(), fi->vertex(1)->point(), fi->vertex(2)->point());
+ ++fi;
+ return true;
+ }
+
+ bool same_orientation(typename Nef::Plane_3 p1) const {
+ if(p1.a() != 0)
+ return CGAL::sign(p1.a()) == CGAL::sign(supporting_plane.a());
+ if(p1.b() != 0)
+ return CGAL::sign(p1.b()) == CGAL::sign(supporting_plane.b());
+ return CGAL::sign(p1.c()) == CGAL::sign(supporting_plane.c());
+ }
+
+ template<typename PIB, typename Index>
+ void handle_triangles(PIB& pib, Index& VI) {
+ while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
+ while(fi != ct.finite_faces_end()) {
+ typename Nef::Plane_3 plane(fi->vertex(0)->point(),
+ fi->vertex(1)->point(),
+ fi->vertex(2)->point());
+ pib.begin_facet();
+ if(same_orientation(plane)) {
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
+ } else {
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
+ }
+ pib.end_facet();
+ do {
+ ++fi;
+ } while(fi != ct.finite_faces_end() && visited[fi] == false);
+ }
+ }
+};
+
+
+
+
+
+
+
+
+template <class HDS, typename Kernel, typename Nef>
+class Build_polyhedron : public CGAL::Modifier_base<HDS> {
+public:
+ bool error; // added for OpenSCAD
+ class Visitor {
+ typedef typename CGAL::Projection_traits_xy_3<Kernel> XY;
+ typedef typename CGAL::Projection_traits_yz_3<Kernel> YZ;
+ typedef typename CGAL::Projection_traits_xz_3<Kernel> XZ;
+
+ const CGAL::Object_index<typename Nef::Vertex_const_iterator>& VI;
+ CGAL::Polyhedron_incremental_builder_3<HDS>& B;
+ const typename Nef::SNC_const_decorator& D;
+
+ public:
+ bool error;//added for OpenSCAD
+ Visitor(CGAL::Polyhedron_incremental_builder_3<HDS>& BB,
+ const typename Nef::SNC_const_decorator& sd,
+ CGAL::Object_index<typename Nef::Vertex_const_iterator>& vi) : VI(vi), B(BB), D(sd), error(false) {}
+
+ void visit(typename Nef::Halffacet_const_handle opposite_facet) {
+
+ CGAL_NEF_TRACEN("Build_polyhedron: visit facet " << opposite_facet->plane());
+
+ CGAL_assertion(Nef::Infi_box::is_standard(opposite_facet->plane()));
+
+ typename Nef::SHalfedge_const_handle se;
+ typename Nef::Halffacet_cycle_const_iterator fc;
+
+ typename Nef::Halffacet_const_handle f = opposite_facet->twin();
+
+ typename Nef::SHalfedge_around_facet_const_circulator
+ sfc1(f->facet_cycles_begin()), sfc2(sfc1);
+
+ if(++f->facet_cycles_begin() != f->facet_cycles_end() ||
+ ++(++(++sfc1)) != sfc2) {
+ typename Nef::Vector_3 orth = f->plane().orthogonal_vector();
+ int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1;
+ c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c;
+
+ try{ // added for OpenSCAD
+ if(c == 0) {
+ Triangulation_handler2<YZ,Nef> th(f);
+ th.handle_triangles(B, VI);
+ } else if(c == 1) {
+ Triangulation_handler2<XZ,Nef> th(f);
+ th.handle_triangles(B, VI);
+ } else if(c == 2) {
+ Triangulation_handler2<XY,Nef> th(f);
+ th.handle_triangles(B, VI);
+ } else
+ CGAL_error_msg( "wrong value");
+ } catch(...) { // added for OpenSCAD
+ PRINT("ERROR: CGAL NefPolyhedron Triangulation failed"); // added for OpenSCAD
+ this->error=true; //added for OpenSCAD
+ } // added for OpenSCAD
+ } else {
+
+ B.begin_facet();
+ fc = f->facet_cycles_begin();
+ se = typename Nef::SHalfedge_const_handle(fc);
+ CGAL_assertion(se!=0);
+ typename Nef::SHalfedge_around_facet_const_circulator hc_start(se);
+ typename Nef::SHalfedge_around_facet_const_circulator hc_end(hc_start);
+ CGAL_For_all(hc_start,hc_end) {
+ CGAL_NEF_TRACEN(" add vertex " << hc_start->source()->center_vertex()->point());
+ B.add_vertex_to_facet(VI[hc_start->source()->center_vertex()]);
+ }
+ B.end_facet();
+ }
+ }
+
+ void visit(typename Nef::SFace_const_handle) {}
+ void visit(typename Nef::Halfedge_const_handle) {}
+ void visit(typename Nef::Vertex_const_handle) {}
+ void visit(typename Nef::SHalfedge_const_handle) {}
+ void visit(typename Nef::SHalfloop_const_handle) {}
+ };
+
+ public:
+
+ const typename Nef::SNC_const_decorator& scd;
+ CGAL::Object_index<typename Nef::Vertex_const_iterator> VI;
+
+ Build_polyhedron(const typename Nef::SNC_const_decorator& s) : error(false),
+ scd(s), VI(s.vertices_begin(),s.vertices_end(),'V') {}
+
+ void operator()( HDS& hds) {
+ CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
+
+ int skip_volumes;
+ if(Nef::Infi_box::extended_kernel()) {
+ B.begin_surface(scd.number_of_vertices()-8,
+ scd.number_of_facets()-6,
+ scd.number_of_edges()-12);
+ skip_volumes = 2;
+ }
+ else {
+ B.begin_surface(scd.number_of_vertices(),
+ 2*scd.number_of_vertices()-4,
+ 3*scd.number_of_vertices()-6);
+ skip_volumes = 1;
+ }
+
+ int vertex_index = 0;
+ typename Nef::Vertex_const_iterator v;
+ CGAL_forall_vertices(v,scd) {
+ if(Nef::Infi_box::is_standard(v->point())) {
+ VI[v]=vertex_index++;
+ B.add_vertex(v->point());
+ }
+ }
+
+ Visitor V(B,scd,VI);
+ typename Nef::Volume_const_handle c;
+ CGAL_forall_volumes(c,scd)
+ if(skip_volumes-- <= 0) {
+ scd.visit_shell_objects(typename Nef:: SFace_const_handle(c->shells_begin()),V);
+ }
+ B.end_surface();
+ this->error=B.error()||V.error; // added for OpenSCAD
+ if (B.error()) B.rollback(); // added for OpenSCAD
+ }
+
+};
+
+template <typename Kernel>
+bool convert_to_Polyhedron( const CGAL::Nef_polyhedron_3<Kernel> &N, CGAL::Polyhedron_3<Kernel> &P )
+{
+ // several lines here added for OpenSCAD
+ typedef typename CGAL::Nef_polyhedron_3<Kernel> Nef3;
+ typedef typename CGAL::Polyhedron_3<Kernel> Polyhedron;
+ typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
+ CGAL_precondition(N.is_simple());
+ P.clear();
+ Build_polyhedron<HalfedgeDS,Kernel,Nef3> bp(N);
+ P.delegate(bp);
+ return bp.error;
+}
+
+
+
+
+
+} //namespace nefworkaround
+
+
+
+
+#endif
+
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 440f4ed..49b9a53 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -96,13 +96,22 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
}
else if (this->dim == 3) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
+ bool err = true;
+ std::string errmsg("");
+ CGAL_Polyhedron P;
try {
- CGAL_Polyhedron P;
- this->p3->convert_to_Polyhedron(P);
- ps = createPolySetFromPolyhedron(P);
+ err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(this->p3), P );
+ //this->p3->convert_to_Polyhedron(P);
+ }
+ catch (const CGAL::Failure_exception &e) {
+ err = true;
+ errmsg = std::string(e.what());
}
- catch (const CGAL::Precondition_exception &e) {
- PRINTB("CGAL error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed.");
+ if (errmsg!="") PRINTB("ERROR: %s",errmsg);
+ } else {
+ ps = createPolySetFromPolyhedron(P);
}
CGAL::set_error_behaviour(old_behaviour);
}
diff --git a/src/CocoaUtils.mm b/src/CocoaUtils.mm
index 92640fd..9856b3d 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/MainWindow.h b/src/MainWindow.h
index 4f88fbf..ac999bf 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 68bc064..4cb043f 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -213,6 +213,8 @@
<addaction name="viewActionDiagonal"/>
<addaction name="viewActionCenter"/>
<addaction name="separator"/>
+ <addaction name="viewActionResetView"/>
+ <addaction name="separator"/>
<addaction name="viewActionPerspective"/>
<addaction name="viewActionOrthogonal"/>
<addaction name="separator"/>
@@ -697,6 +699,11 @@
<string>Show Library Folder...</string>
</property>
</action>
+ <action name="viewActionResetView">
+ <property name="text">
+ <string>Reset View</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc
index cfa5731..b02b822 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
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index bc9206f..599fd7f 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -457,7 +457,14 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
{
double max_x = 0;
for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
- max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]);
+ double point_x = dxf.points[dxf.paths[i].indices[j]][0];
+ if (point_x < 0) {
+ PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates");
+ PRINTB("[Point %d on path %d has X coordinate %f]", j % i % point_x);
+ delete ps;
+ return NULL;
+ }
+ max_x = fmax(max_x, point_x);
}
int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);
diff --git a/src/QGLView.cc b/src/QGLView.cc
index 5cb2e1b..6ffd586 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 f3e3681..12be085 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/cgal.h b/src/cgal.h
index 45228be..69c8c27 100644
--- a/src/cgal.h
+++ b/src/cgal.h
@@ -27,6 +27,7 @@ using boost::uintmax_t;
#include <CGAL/Cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
+#include <CGAL_Nef3_workaround.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
@@ -48,9 +49,10 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2;
typedef CGAL::Polygon_2<CGAL_ExactKernel2> CGAL_Poly2;
typedef CGAL::Polygon_with_holes_2<CGAL_ExactKernel2> CGAL_Poly2h;
- //typedef CGAL::Cartesian<NT> CGAL_Kernel3;
-typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
-typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
+typedef CGAL::Gmpq NT3;
+typedef CGAL::Cartesian<NT3> CGAL_Kernel3;
+//typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
+//typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3;
typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;
diff --git a/src/control.cc b/src/control.cc
index 10aadf0..d5f664e 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);
- }
+ uint32_t steps = range.nbsteps();
+ if (steps >= 10000) {
+ PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
+ } else {
+ for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
+ c.set_variable(it_name, Value(*it));
+ for_eval(node, inst, l+1, &c, evalctx);
+ }
}
}
else if (it_values.type() == Value::VECTOR) {
@@ -227,13 +229,13 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
else if (value.type() == Value::RANGE) {
AbstractNode* node = new AbstractNode(inst);
Value::RangeType range = value.toRange();
- range.normalize();
- if (range.nbsteps()>=10000) {
- PRINTB("WARNING: Bad range parameter for children: too many elements (%d).", (int)((range.begin-range.end)/range.step));
+ uint32_t steps = range.nbsteps();
+ if (steps >= 10000) {
+ PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps);
return NULL;
}
- for (double i = range.begin; i <= range.end; i += range.step) {
- AbstractNode* childnode = getChild(Value(i),modulectx); // with error cases
+ for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
+ AbstractNode* childnode = getChild(Value(*it),modulectx); // with error cases
if (childnode==NULL) continue; // error
node->children.push_back(childnode);
}
diff --git a/src/dxfdata.h b/src/dxfdata.h
index 64853dc..ac7260c 100644
--- a/src/dxfdata.h
+++ b/src/dxfdata.h
@@ -28,11 +28,7 @@ public:
}
};
-#ifdef __APPLE__
- std::vector<Vector2d, Eigen::aligned_allocator<Vector2d> > points;
-#else
std::vector<Vector2d> points;
-#endif
std::vector<Path> paths;
std::vector<Dim> dims;
diff --git a/src/editor.cc b/src/editor.cc
index 08bf005..069101f 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 09484f5..8d092a9 100644
--- a/src/editor.h
+++ b/src/editor.h
@@ -2,6 +2,7 @@
#include <QString>
#include <QWidget>
#include <QWheelEvent>
+#include <QScrollBar>
#include <QTextEdit>
class Editor : public QTextEdit
@@ -9,6 +10,7 @@ class Editor : public QTextEdit
Q_OBJECT
public:
Editor(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); }
+ void setPlainText(const QString &text);
public slots:
void zoomIn();
void zoomOut();
diff --git a/src/export.cc b/src/export.cc
index ec6e576..cef323e 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<CGAL_Kernel3>( *(root_N->p3), P );
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
+ return;
+ }
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
@@ -114,6 +119,9 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output)
catch (const CGAL::Assertion_exception &e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what());
}
+ catch (...) {
+ PRINT("CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()");
+ }
CGAL::set_error_behaviour(old_behaviour);
}
diff --git a/src/expr.cc b/src/expr.cc
index 594fccf..08615ba 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") {
@@ -192,7 +199,11 @@ std::string Expression::toString() const
stream << this->const_value;
}
else if (this->type == "R") {
- stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << *this->children[2] << "]";
+ stream << "[" << *this->children[0] << " : " << *this->children[1];
+ if (this->children.size() > 2) {
+ stream << " : " << *this->children[2];
+ }
+ stream << "]";
}
else if (this->type == "V") {
stream << "[";
diff --git a/src/linalg.h b/src/linalg.h
index 1f9ed30..cb82452 100644
--- a/src/linalg.h
+++ b/src/linalg.h
@@ -4,10 +4,13 @@
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Dense>
+#include<Eigen/StdVector>
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d)
using Eigen::Vector2d;
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector3d)
using Eigen::Vector3d;
-using Eigen::Vector3f;
+
typedef Eigen::AlignedBox<double, 3> BoundingBox;
using Eigen::Matrix3f;
using Eigen::Matrix3d;
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 65c511f..1ad8bc8 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()));
@@ -503,6 +504,7 @@ MainWindow::openFile(const QString &new_filename)
}
#endif
setFileName(actual_filename);
+ editor->setPlainText("");
fileChangedOnDisk(); // force cached autoReloadId to update
refreshDocument();
@@ -1772,6 +1774,12 @@ void MainWindow::viewOrthogonal()
this->qglview->updateGL();
}
+void MainWindow::viewResetView()
+{
+ this->qglview->resetView();
+ this->qglview->updateGL();
+}
+
void MainWindow::hideConsole()
{
QSettings settings;
diff --git a/src/modcontext.cc b/src/modcontext.cc
index 5b48009..7941cf5 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();
}
diff --git a/src/openscad.cc b/src/openscad.cc
index 6bbaedb..ab84235 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));
}
@@ -474,6 +474,13 @@ bool QtUseGUI()
int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, char ** argv)
{
+#ifdef Q_OS_MACX
+ if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
+ // fix Mac OS X 10.9 (mavericks) font issue
+ // https://bugreports.qt-project.org/browse/QTBUG-32789
+ QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
+ }
+#endif
QApplication app(argc, argv, true); //useGUI);
#ifdef Q_WS_MAC
app.installEventFilter(new EventFilter(&app));
diff --git a/src/parser.y b/src/parser.y
index 5645104..6446e82 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/stl-utils.cc b/src/stl-utils.cc
index 790fd17..027339c 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 <iostream>
diff --git a/src/value.cc b/src/value.cc
index ac33a3b..5afb650 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.");
+ }
+}
+
+uint32_t Value::RangeType::nbsteps() const {
+ if (begin_val == end_val) {
+ return 0;
+ }
+
+ if (step_val == 0) {
+ return std::numeric_limits<uint32_t>::max();
+ }
+
+ double steps;
+ if (step_val < 0) {
+ if (begin_val < end_val) {
+ return 0;
+ }
+ steps = (begin_val - end_val) / (-step_val);
+ } else {
+ if (begin_val > end_val) {
+ return 0;
+ }
+ steps = (end_val - begin_val) / step_val;
+ }
+
+ return steps;
+}
+
+Value::RangeType::iterator::iterator(Value::RangeType &range, type_t type) : range(range), val(range.begin_val)
+{
+ this->type = type;
+ update_type();
+}
+
+void Value::RangeType::iterator::update_type()
+{
+ if (range.step_val == 0) {
+ type = RANGE_TYPE_END;
+ } else if (range.step_val < 0) {
+ if (val < range.end_val) {
+ type = RANGE_TYPE_END;
+ }
+ } else {
+ if (val > range.end_val) {
+ type = RANGE_TYPE_END;
+ }
+ }
+}
+
+Value::RangeType::iterator::reference Value::RangeType::iterator::operator*()
+{
+ return val;
+}
+
+Value::RangeType::iterator::pointer Value::RangeType::iterator::operator->()
+{
+ return &(operator*());
+}
+
+Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++()
+{
+ if (type < 0) {
+ type = RANGE_TYPE_RUNNING;
+ }
+ val += range.step_val;
+ update_type();
+ return *this;
+}
+
+Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++(int)
+{
+ self_type tmp(*this);
+ operator++();
+ return tmp;
+}
+
+bool Value::RangeType::iterator::operator==(const self_type &other) const
+{
+ if (type == RANGE_TYPE_RUNNING) {
+ return (type == other.type) && (val == other.val) && (range == other.range);
+ } else {
+ return (type == other.type) && (range == other.range);
+ }
+}
+
+bool Value::RangeType::iterator::operator!=(const self_type &other) const
+{
+ return !(*this == other);
+}
diff --git a/src/value.h b/src/value.h
index 388b721..75dff2b 100644
--- a/src/value.h
+++ b/src/value.h
@@ -11,6 +11,7 @@
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#endif
+#include <boost/cstdint.hpp>
class QuotedString : public std::string
{
@@ -31,33 +32,64 @@ std::ostream &operator<<(std::ostream &stream, const Filename &filename);
class Value
{
public:
- struct RangeType {
+ class RangeType {
+ private:
+ double begin_val;
+ double step_val;
+ double end_val;
+
+ /// inverse begin/end if begin is upper than end
+ void normalize();
+
+ public:
+ typedef enum { RANGE_TYPE_BEGIN, RANGE_TYPE_RUNNING, RANGE_TYPE_END } type_t;
+
+ class iterator {
+ public:
+ typedef iterator self_type;
+ typedef double value_type;
+ typedef double& reference;
+ typedef double* pointer;
+ typedef std::forward_iterator_tag iterator_category;
+ typedef double difference_type;
+ iterator(RangeType &range, type_t type);
+ self_type operator++();
+ self_type operator++(int junk);
+ reference operator*();
+ pointer operator->();
+ bool operator==(const self_type& other) const;
+ bool operator!=(const self_type& other) const;
+ private:
+ RangeType &range;
+ double val;
+ type_t type;
+
+ void update_type();
+ };
+
+ RangeType(double begin, double end)
+ : begin_val(begin), step_val(1.0), end_val(end)
+ {
+ normalize();
+ }
+
RangeType(double begin, double step, double end)
- : begin(begin), step(step), end(end) {}
+ : begin_val(begin), step_val(step), end_val(end) {}
bool operator==(const RangeType &other) const {
- return this->begin == other.begin &&
- this->step == other.step &&
- this->end == other.end;
+ return this->begin_val == other.begin_val &&
+ this->step_val == other.step_val &&
+ this->end_val == other.end_val;
}
- /// inverse begin/end if begin is upper than end
- void normalize() {
- if ((step>0) && (end < begin)) {
- std::swap(begin,end);
- }
- }
- /// return number of steps, max int value if step is null
- int nbsteps() const {
- if (step<=0) {
- return std::numeric_limits<int>::max();
- }
- return (int)((begin-end)/step);
- }
-
- double begin;
- double step;
- double end;
+ iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); }
+ iterator end() { return iterator(*this, RANGE_TYPE_END); }
+
+ /// return number of steps, max uint32_t value if step is 0
+ uint32_t nbsteps() const;
+
+ friend class tostring_visitor;
+ friend class bracket_visitor;
};
typedef std::vector<Value> VectorType;
diff --git a/src/version_check.h b/src/version_check.h
index 6e07208..be52e61 100644
--- a/src/version_check.h
+++ b/src/version_check.h
@@ -35,8 +35,8 @@ a time, to avoid confusion.
#include <Eigen/Core>
-#if not EIGEN_VERSION_AT_LEAST( 2,0,13 )
-#error eigen2 library missing or version too old. See README.md. To force compile, run qmake CONFIG+=skip-version-check
+#if not EIGEN_VERSION_AT_LEAST( 3,0,0 )
+#error eigen library missing or version too old. See README.md. To force compile, run qmake CONFIG+=skip-version-check
#else
@@ -104,9 +104,17 @@ a time, to avoid confusion.
#endif // ENABLE_CGAL
#endif // Boost
-#endif // Eigen2
+#endif // Eigen
#endif // MPFR
#endif // GMP
+// see github issue #552
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION == 40802
+#error "OpenSCAD isnt compatible with gcc 4.8.2. Please try a different version"
+#endif
+
#endif // OPENSCAD_SKIP_VERSION_CHECK
diff --git a/testdata/scad/features/for-tests.scad b/testdata/scad/features/for-tests.scad
index fe36789..10295b1 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/testdata/scad/features/polygon-tests.scad b/testdata/scad/features/polygon-tests.scad
index b4e92b6..0cd259f 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/testdata/scad/features/projection-cut-tests.scad b/testdata/scad/features/projection-cut-tests.scad
new file mode 100644
index 0000000..0409a3e
--- /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-extrude-tests.scad b/testdata/scad/features/projection-extrude-tests.scad
new file mode 100644
index 0000000..d9c216c
--- /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 e6c52ea..27d03fe 100644
--- a/testdata/scad/features/projection-tests.scad
+++ b/testdata/scad/features/projection-tests.scad
@@ -3,18 +3,25 @@ projection();
// No children
projection() { }
// 2D child
-projection(cut=true) { square(); }
+projection() { square(); }
-linear_extrude(height=20) projection(cut=false) sphere(r=10);
-translate([22,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,9]) sphere(r=10);
-translate([44,0,0]) linear_extrude(height=20) projection(cut=true) translate([0,0,7]) sphere(r=10);
+// Simple
+projection(cut=false) cube(10);
-// Boundary case: clipping the top of a cube
-translate([0,-22,0]) linear_extrude(height=5) projection(cut=true) translate([0,0,-4.999999]) cube(10, center=true);
+// Two children
+translate([-12,0]) projection(cut=false) {
+ cube(10);
+ difference() {
+ sphere(10);
+ cylinder(h=30, r=5, center=true);
+ }
+}
-// holes
-translate([0,-44,0]) linear_extrude(height=5) projection(cut=true)
- union() {
- difference() { cube(5,center=true); cube(4,center=true); }
- translate([2.1,2.1]) difference() { cube(5,center=true); cube(4,center=true); }
- }
+// Holes
+translate([6,-12]) projection(cut=false) {
+ cube(10);
+ difference() {
+ sphere(10);
+ cylinder(h=30, r=5, center=true);
+ }
+}
diff --git a/testdata/scad/features/render-2d-tests.scad b/testdata/scad/features/render-2d-tests.scad
index 683ffe4..f8df115 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/testdata/scad/misc/children-tests.scad b/testdata/scad/misc/children-tests.scad
index a9a3cf9..1c3d9ea 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/testdata/scad/misc/range-tests.scad b/testdata/scad/misc/range-tests.scad
new file mode 100644
index 0000000..42ef2a4
--- /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 c560b4f..f92eddf 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)
@@ -15,8 +15,15 @@ include(CMakeParseArguments.cmake)
# Detect Lion and force gcc
IF (APPLE)
EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION)
- IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
- message("Detected Mountain Lion (10.8) or later")
+ IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.9.0")
+ message("Detected Maverick (10.9) or later")
+ set(CMAKE_C_COMPILER "clang")
+ set(CMAKE_CXX_COMPILER "clang++")
+ # Somehow, since we build dependencies for 10.7, we need to also build executables
+ # for 10.7. This used to not be necessary, but since 10.9 it apparently is..
+ SET(CMAKE_OSX_DEPLOYMENT_TARGET 10.7 CACHE STRING "Deployment target")
+ ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
+ message("Detected Mountain Lion (10.8)")
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0")
@@ -212,30 +219,21 @@ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
endif()
# Priority
-# 3. EIGENDIR if set (EIGEN2DIR for backwards compatability)
+# 3. EIGENDIR if set
# 1. OPENSCAD_LIBRARIES eigen3
-# 2. OPENSCAD_LIBRARIES eigen2
# 4. system's standard include paths for eigen3
-# 5. system's standard include paths for eigen2
-set(EIGEN2_DIR "$ENV{EIGEN2DIR}")
set(EIGEN_DIR "$ENV{EIGENDIR}")
set(OPENSCAD_LIBDIR "$ENV{OPENSCAD_LIBRARIES}")
if (EIGEN_DIR)
- set(EIGHINT ${EIGEN_DIR}/include/eigen3 ${EIGEN_DIR}/include/eigen2 ${EIGEN_DIR})
+ set(EIGHINT ${EIGEN_DIR}/include/eigen3 ${EIGEN_DIR})
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${EIGHINT})
endif()
-if (EIGEN2_DIR)
- find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${EIGEN2_DIR}/include/eigen2 ${EIGEN2_DIR})
-endif()
if (NOT EIGEN_INCLUDE_DIR)
find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${OPENSCAD_LIBDIR}/include/eigen3)
endif()
-if (NOT EIGEN_INCLUDE_DIR)
- find_path(EIGEN_INCLUDE_DIR Eigen/Core HINTS ${OPENSCAD_LIBDIR}/include/eigen2)
-endif()
if (NOT EIGEN_INCLUDE_DIR)
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
@@ -250,18 +248,6 @@ if (NOT EIGEN_INCLUDE_DIR)
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()
message(STATUS "Eigen found in " ${EIGEN_INCLUDE_DIR})
@@ -450,9 +436,11 @@ message(STATUS "OpenSCAD version: ${VERSION}")
string(REGEX MATCHALL "^[0-9]+|[0-9]+|[0-9]+$" MYLIST "${VERSION}")
list(GET MYLIST 0 OPENSCAD_YEAR)
list(GET MYLIST 1 OPENSCAD_MONTH)
+math(EXPR OPENSCAD_MONTH ${OPENSCAD_MONTH}) # get rid of leading zero
list(LENGTH MYLIST VERSIONLEN)
if (${VERSIONLEN} EQUAL 3)
list(GET MYLIST 2 OPENSCAD_DAY)
+ math(EXPR OPENSCAD_DAY ${OPENSCAD_DAY}) # get rid of leading zero
endif()
add_definitions(-DOPENSCAD_VERSION=${VERSION} -DOPENSCAD_YEAR=${OPENSCAD_YEAR} -DOPENSCAD_MONTH=${OPENSCAD_MONTH})
@@ -802,7 +790,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/expression-shortcircuit-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/parent_module-tests.scad
- ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/children-tests.scad)
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/children-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/range-tests.scad)
list(APPEND DUMPTEST_FILES ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
@@ -812,12 +801,14 @@ list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allexpressions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allfunctions.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allmodules.scad)
list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILES})
list(APPEND CGALPNGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad)
@@ -840,6 +831,10 @@ disable_tests(openscad-csgpng_child-background)
disable_tests(opencsgtest_example006 cgalpngtest_example006)
disable_tests(openscad-csgpng_example006 openscad-cgalpng_example006)
+# NefPolyhedron->Polyhedron conversion failures. No images for OpenCSG/Thrown
+disable_tests(opencsgtest_stl-cgal-convert_to_Polyhedron-crash)
+disable_tests(throwntogethertest_stl-cgal-convert_to_Polyhedron-crash)
+
# These tests only makes sense in OpenCSG mode
disable_tests(cgalpngtest_child-background
cgalpngtest_highlight-and-background-modifier
@@ -852,12 +847,8 @@ disable_tests(cgalpngtest_child-background
# Test config handling
set_test_config(Heavy opencsgtest_minkowski3-tests
- opencsgtest_projection-tests
openscad-csgpng_minkowski3-tests
- openscad-csgpng_projection-tests
throwntogethertest_minkowski3-tests
- throwntogethertest_projection-tests
- cgalpngtest_projection-tests
cgalpngtest_rotate_extrude-tests
cgalpngtest_surface-tests
cgalpngtest_sphere-tests
diff --git a/tests/regression/cgalpngtest/for-tests-expected.png b/tests/regression/cgalpngtest/for-tests-expected.png
index bf1970a..65db59f 100644
--- a/tests/regression/cgalpngtest/for-tests-expected.png
+++ b/tests/regression/cgalpngtest/for-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/polygon-tests-expected.png b/tests/regression/cgalpngtest/polygon-tests-expected.png
index 5ceabe8..28e4e9f 100644
--- a/tests/regression/cgalpngtest/polygon-tests-expected.png
+++ b/tests/regression/cgalpngtest/polygon-tests-expected.png
Binary files differ
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 0000000..7189447
--- /dev/null
+++ b/tests/regression/cgalpngtest/projection-cut-tests-expected.png
Binary files differ
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 0000000..b5d7338
--- /dev/null
+++ b/tests/regression/cgalpngtest/projection-extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/projection-tests-expected.png b/tests/regression/cgalpngtest/projection-tests-expected.png
index 2610507..c0d5289 100644
--- a/tests/regression/cgalpngtest/projection-tests-expected.png
+++ b/tests/regression/cgalpngtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/render-2d-tests-expected.png b/tests/regression/cgalpngtest/render-2d-tests-expected.png
index 19ea16a..2418968 100644
--- a/tests/regression/cgalpngtest/render-2d-tests-expected.png
+++ b/tests/regression/cgalpngtest/render-2d-tests-expected.png
Binary files differ
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 0000000..318cbaa
--- /dev/null
+++ b/tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png
Binary files differ
diff --git a/tests/regression/dumptest/for-tests-expected.csg b/tests/regression/dumptest/for-tests-expected.csg
index 7aa29d7..b61d9cd 100644
--- a/tests/regression/dumptest/for-tests-expected.csg
+++ b/tests/regression/dumptest/for-tests-expected.csg
@@ -80,7 +80,23 @@ group() {
cube(size = [1, 9.2, 2], center = true);
}
}
- group();
+ group() {
+ multmatrix([[1, 0, 0, 20], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
+ }
+ multmatrix([[1, 0, 0, 10], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 4, r2 = 4, center = false);
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 3, r2 = 3, center = false);
+ }
+ multmatrix([[1, 0, 0, -10], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 2, r2 = 2, center = false);
+ }
+ multmatrix([[1, 0, 0, -20], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = false);
+ }
+ }
group() {
multmatrix([[1, 0, 0, -20], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) {
cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = false);
diff --git a/tests/regression/dumptest/polygon-tests-expected.csg b/tests/regression/dumptest/polygon-tests-expected.csg
index 56995a5..e19bcb0 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/dumptest/projection-cut-tests-expected.csg b/tests/regression/dumptest/projection-cut-tests-expected.csg
new file mode 100644
index 0000000..4c37fa2
--- /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-extrude-tests-expected.csg b/tests/regression/dumptest/projection-extrude-tests-expected.csg
new file mode 100644
index 0000000..0199ea0
--- /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 86423a1..04cd404 100644
--- a/tests/regression/dumptest/projection-tests-expected.csg
+++ b/tests/regression/dumptest/projection-tests-expected.csg
@@ -1,56 +1,27 @@
group() {
projection(cut = false, convexity = 0);
projection(cut = false, convexity = 0);
- projection(cut = true, convexity = 0) {
+ projection(cut = false, convexity = 0) {
square(size = [1, 1], center = false);
}
- linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
- projection(cut = false, convexity = 0) {
- sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
- }
- }
- multmatrix([[1, 0, 0, 22], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
- linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
- projection(cut = true, convexity = 0) {
- multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 9], [0, 0, 0, 1]]) {
- sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
- }
- }
- }
+ projection(cut = false, convexity = 0) {
+ cube(size = [10, 10, 10], center = false);
}
- multmatrix([[1, 0, 0, 44], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
- linear_extrude(height = 20, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
- projection(cut = true, convexity = 0) {
- multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 7], [0, 0, 0, 1]]) {
- sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
- }
- }
- }
- }
- multmatrix([[1, 0, 0, 0], [0, 1, 0, -22], [0, 0, 1, 0], [0, 0, 0, 1]]) {
- linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
- projection(cut = true, convexity = 0) {
- multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -4.999999], [0, 0, 0, 1]]) {
- cube(size = [10, 10, 10], center = true);
- }
+ multmatrix([[1, 0, 0, -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, -44], [0, 0, 1, 0], [0, 0, 0, 1]]) {
- linear_extrude(height = 5, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
- projection(cut = true, convexity = 0) {
- union() {
- difference() {
- cube(size = [5, 5, 5], center = true);
- cube(size = [4, 4, 4], center = true);
- }
- multmatrix([[1, 0, 0, 2.1], [0, 1, 0, 2.1], [0, 0, 1, 0], [0, 0, 0, 1]]) {
- difference() {
- cube(size = [5, 5, 5], center = true);
- cube(size = [4, 4, 4], center = true);
- }
- }
- }
+ multmatrix([[1, 0, 0, 6], [0, 1, 0, -12], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ projection(cut = false, convexity = 0) {
+ cube(size = [10, 10, 10], center = false);
+ difference() {
+ sphere($fn = 0, $fa = 12, $fs = 2, r = 10);
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 5, r2 = 5, center = true);
}
}
}
diff --git a/tests/regression/dumptest/render-2d-tests-expected.csg b/tests/regression/dumptest/render-2d-tests-expected.csg
index 75739b3..bcc5e96 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/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 0000000..acad52f
--- /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);
+ }
+}
diff --git a/tests/regression/echotest/children-tests-expected.echo b/tests/regression/echotest/children-tests-expected.echo
index 7b8278a..cdd2eb1 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"
diff --git a/tests/regression/echotest/range-tests-expected.echo b/tests/regression/echotest/range-tests-expected.echo
new file mode 100644
index 0000000..ddff38e
--- /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
diff --git a/tests/regression/moduledumptest/allexpressions-expected.ast b/tests/regression/moduledumptest/allexpressions-expected.ast
index 6d9de40..69ec746 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";
diff --git a/tests/regression/opencsgtest/for-tests-expected.png b/tests/regression/opencsgtest/for-tests-expected.png
index 968659d..5218b8b 100644
--- a/tests/regression/opencsgtest/for-tests-expected.png
+++ b/tests/regression/opencsgtest/for-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/polygon-tests-expected.png b/tests/regression/opencsgtest/polygon-tests-expected.png
index fe84a80..94bd131 100644
--- a/tests/regression/opencsgtest/polygon-tests-expected.png
+++ b/tests/regression/opencsgtest/polygon-tests-expected.png
Binary files differ
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 0000000..5063301
--- /dev/null
+++ b/tests/regression/opencsgtest/projection-cut-tests-expected.png
Binary files differ
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 0000000..d68bd59
--- /dev/null
+++ b/tests/regression/opencsgtest/projection-extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/projection-tests-expected.png b/tests/regression/opencsgtest/projection-tests-expected.png
index 9aabe36..b3af1e1 100644
--- a/tests/regression/opencsgtest/projection-tests-expected.png
+++ b/tests/regression/opencsgtest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/render-2d-tests-expected.png b/tests/regression/opencsgtest/render-2d-tests-expected.png
index 0bf6288..47e57dd 100644
--- a/tests/regression/opencsgtest/render-2d-tests-expected.png
+++ b/tests/regression/opencsgtest/render-2d-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/for-tests-expected.png b/tests/regression/throwntogethertest/for-tests-expected.png
index 6641555..7b58005 100644
--- a/tests/regression/throwntogethertest/for-tests-expected.png
+++ b/tests/regression/throwntogethertest/for-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/polygon-tests-expected.png b/tests/regression/throwntogethertest/polygon-tests-expected.png
index 779b878..2ce6b75 100644
--- a/tests/regression/throwntogethertest/polygon-tests-expected.png
+++ b/tests/regression/throwntogethertest/polygon-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/projection-cut-tests-expected.png b/tests/regression/throwntogethertest/projection-cut-tests-expected.png
new file mode 100644
index 0000000..5063301
--- /dev/null
+++ b/tests/regression/throwntogethertest/projection-cut-tests-expected.png
Binary files differ
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 0000000..d68bd59
--- /dev/null
+++ b/tests/regression/throwntogethertest/projection-extrude-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/projection-tests-expected.png b/tests/regression/throwntogethertest/projection-tests-expected.png
index 3be3ae0..b3af1e1 100644
--- a/tests/regression/throwntogethertest/projection-tests-expected.png
+++ b/tests/regression/throwntogethertest/projection-tests-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/render-2d-tests-expected.png b/tests/regression/throwntogethertest/render-2d-tests-expected.png
index 0bf6288..47e57dd 100644
--- a/tests/regression/throwntogethertest/render-2d-tests-expected.png
+++ b/tests/regression/throwntogethertest/render-2d-tests-expected.png
Binary files differ
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index a31b1a8..c0d35bb 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -25,544 +25,441 @@
# todo
#
-# 1. Note: Wiki code is deprecated. All future development should move to
-# create html output (or even xml output). Wiki design was based on the
-# wrong assumption of easily accessible public wiki servers with
-# auto-upload scripts available. wiki code should be removed and code
-# simplified.
-#
-# to still use wiki, use args '--wiki' and/or '--wiki-upload'
-#
-# 2. why is hash differing
-
-import string,sys,re,os,hashlib,subprocess,textwrap,time,platform
+# 1. why is hash differing
+
+import string
+import sys
+import re
+import os
+import hashlib
+import subprocess
+import time
+import platform
+try:
+ from urllib.request import urlopen
+ from urllib.parse import urlencode
+except:
+ from urllib2 import urlopen
+ from urllib import urlencode
def tryread(filename):
- data = None
- try:
- f = open(filename,'rb')
- data = f.read()
- f.close()
- except Exception, e:
- print 'couldn\'t open ',filename
- print type(e), e
- return data
-
-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 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 ''
-
+ 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 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 read_gitinfo():
- # won't work if run from outside of branch.
- try:
- data = subprocess.Popen(['git','remote','-v'],stdout=subprocess.PIPE).stdout.read()
- origin = ezsearch('^origin *?(.*?)\(fetch.*?$',data)
- upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$',data)
- data = subprocess.Popen(['git','branch'],stdout=subprocess.PIPE).stdout.read()
- branch = ezsearch('^\*(.*?)$',data)
- out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
- out += 'Git upstream: ' + upstream + '\n'
- except:
- out = 'Problem running git'
- return out
+ # won't work if run from outside of branch.
+ try:
+ data = subprocess.Popen(['git', 'remote', '-v'], stdout=subprocess.PIPE).stdout.read()
+ origin = ezsearch('^origin *?(.*?)\(fetch.*?$', data)
+ upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$', data)
+ data = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE).stdout.read()
+ branch = ezsearch('^\*(.*?)$', data)
+ out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
+ out += 'Git upstream: ' + upstream + '\n'
+ except:
+ out = 'Problem running git'
+ return out
def read_sysinfo(filename):
- data = tryread(filename)
- if not data:
- sinfo = platform.sys.platform
- sinfo += '\nsystem cannot create offscreen GL framebuffer object'
- sinfo += '\nsystem cannot create GL based images'
- sysid = platform.sys.platform+'_no_GL_renderer'
- return sinfo, sysid
+ data = tryread(filename)
+ if not data:
+ sinfo = platform.sys.platform
+ sinfo += '\nsystem cannot create offscreen GL framebuffer object'
+ sinfo += '\nsystem cannot create GL based images'
+ sysid = platform.sys.platform+'_no_GL_renderer'
+ return sinfo, sysid
- machine = ezsearch('Machine:(.*?)\n',data)
- machine = machine.replace(' ','-').replace('/','-')
+ machine = ezsearch('Machine:(.*?)\n',data)
+ machine = machine.replace(' ','-').replace('/','-')
- osinfo = ezsearch('OS info:(.*?)\n',data)
- osplain = osinfo.split(' ')[0].strip().replace('/','-')
- if 'windows' in osinfo.lower(): osplain = 'win'
+ osinfo = ezsearch('OS info:(.*?)\n',data)
+ osplain = osinfo.split(' ')[0].strip().replace('/','-')
+ if 'windows' in osinfo.lower():
+ osplain = 'win'
- renderer = ezsearch('GL Renderer:(.*?)\n',data)
- tmp = renderer.split(' ')
- tmp = string.join(tmp[0:3],'-')
- tmp = tmp.split('/')[0]
- renderer = tmp
+ renderer = ezsearch('GL Renderer:(.*?)\n',data)
+ tmp = renderer.split(' ')
+ tmp = string.join(tmp[0:3],'-')
+ tmp = tmp.split('/')[0]
+ renderer = tmp
- data += read_gitinfo()
+ data += read_gitinfo()
- data += 'Image comparison: ImageMagick'
+ data += 'Image comparison: ImageMagick'
- data = data.strip()
+ data = data.strip()
- # create 4 letter hash and stick on end of sysid
- nondate_data = re.sub("\n.*?ompile date.*?\n","\n",data).strip()
- hexhash = hashlib.md5()
- hexhash.update(nondate_data)
- hexhash = hexhash.hexdigest()[-4:].upper()
- hash = ''
- for c in hexhash: hash += chr(ord(c)+97-48)
+ # create 4 letter hash and stick on end of sysid
+ nondate_data = re.sub("\n.*?ompile date.*?\n", "\n", data).strip()
+ hexhash = hashlib.md5(nondate_data).hexdigest()[-4:].upper()
+ hash_ = ''.join(chr(ord(c) + 97 - 48) for c in hexhash)
- sysid = osplain + '_' + machine + '_' + renderer + '_' + hash
- sysid = sysid.replace('(','_').replace(')','_')
- sysid = sysid.lower()
+ sysid = '_'.join([osplain, machine, renderer, hash_])
+ sysid = sysid.replace('(', '_').replace(')', '_')
+ sysid = sysid.lower()
- return data, sysid
+ return data, sysid
class Test:
- def __init__(self,fullname,time,passed,output,type,actualfile,expectedfile,scadfile,log):
- self.fullname,self.time,self.passed,self.output = \
- fullname, time, passed, output
- self.type, self.actualfile, self.expectedfile, self.scadfile = \
- type, actualfile, expectedfile, scadfile
- self.fulltestlog = log
-
- def __str__(self):
- x = 'fullname: ' + self.fullname
- x+= '\nactualfile: ' + self.actualfile
- x+= '\nexpectedfile: ' + self.expectedfile
- x+= '\ntesttime: ' + self.time
- x+= '\ntesttype: ' + self.type
- x+= '\npassed: ' + str(self.passed)
- x+= '\nscadfile: ' + self.scadfile
- x+= '\noutput bytes: ' + str(len(self.output))
- x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
- x+= '\n'
- return x
+ def __init__(self, fullname, subpr, passed, output, type, actualfile,
+ expectedfile, scadfile, log):
+ self.fullname, self.time = fullname, time
+ self.passed, self.output = passed, output
+ self.type, self.actualfile = type, actualfile
+ self.expectedfile, self.scadfile = expectedfile, scadfile
+ self.fulltestlog = log
+
+ def __str__(self):
+ x = 'fullname: ' + self.fullname
+ x+= '\nactualfile: ' + self.actualfile
+ x+= '\nexpectedfile: ' + self.expectedfile
+ x+= '\ntesttime: ' + self.time
+ x+= '\ntesttype: ' + self.type
+ x+= '\npassed: ' + str(self.passed)
+ x+= '\nscadfile: ' + self.scadfile
+ x+= '\noutput bytes: ' + str(len(self.output))
+ x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
+ x+= '\n'
+ return x
def parsetest(teststring):
- patterns = ["Test:(.*?)\n", # fullname
- "Test time =(.*?) sec\n",
- "Test time.*?Test (Passed)", # pass/fail
- "Output:(.*?)<end of output>",
- 'Command:.*?-s" "(.*?)"', # type
- "^ actual .*?:(.*?)\n",
- "^ expected .*?:(.*?)\n",
- 'Command:.*?(testdata.*?)"' # scadfile
- ]
- hits = map( lambda pattern: ezsearch(pattern,teststring), patterns )
- test = Test(hits[0],hits[1],hits[2]=='Passed',hits[3],hits[4],hits[5],hits[6],hits[7],teststring)
- if len(test.actualfile) > 0: test.actualfile_data = tryread(test.actualfile)
- if len(test.expectedfile) > 0: test.expectedfile_data = tryread(test.expectedfile)
- return test
+ patterns = ["Test:(.*?)\n", # fullname
+ "Test time =(.*?) sec\n",
+ "Test time.*?Test (Passed)", # pass/fail
+ "Output:(.*?)<end of output>",
+ 'Command:.*?-s" "(.*?)"', # type
+ "^ actual .*?:(.*?)\n",
+ "^ expected .*?:(.*?)\n",
+ 'Command:.*?(testdata.*?)"' # scadfile
+ ]
+ hits = map( lambda pattern: ezsearch(pattern, teststring), patterns)
+ test = Test(hits[0], hits[1], hits[2]=='Passed', hits[3], hits[4], hits[5],
+ hits[6], hits[7], teststring)
+ if len(test.actualfile) > 0:
+ test.actualfile_data = tryread(test.actualfile)
+ if len(test.expectedfile) > 0:
+ test.expectedfile_data = tryread(test.expectedfile)
+ return test
def parselog(data):
- startdate = ezsearch('Start testing: (.*?)\n',data)
- enddate = ezsearch('End testing: (.*?)\n',data)
- pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
- test_chunks = re.findall(pattern,data,re.S)
- tests = map( parsetest, test_chunks )
- tests = sorted(tests, key = lambda t:t.passed)
- return startdate, tests, enddate
+ startdate = ezsearch('Start testing: (.*?)\n', data)
+ enddate = ezsearch('End testing: (.*?)\n', data)
+ pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
+ test_chunks = re.findall(pattern,data, re.S)
+ tests = map( parsetest, test_chunks )
+ tests = sorted(tests, key = lambda t: t.passed)
+ return startdate, tests, enddate
def load_makefiles(builddir):
- filelist = []
- for root, dirs, files in os.walk(builddir):
- for fname in files: filelist += [ os.path.join(root, fname) ]
- files = filter(lambda x: 'build.make' in os.path.basename(x), filelist)
- files += filter(lambda x: 'flags.make' in os.path.basename(x), filelist)
- files = filter(lambda x: 'esting' not in x and 'emporary' not in x, files)
- result = {}
- for fname in files:
- result[fname.replace(builddir,'')] = open(fname,'rb').read()
- return result
-
-def wikify_filename(fname, wiki_rootpath, sysid):
- wikifname = fname.replace('/','_').replace('\\','_').strip('.')
- return wiki_rootpath + '_' + sysid + '_' + wikifname
-
-def towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
-
- wiki_template = """
-<h3>[[WIKI_ROOTPATH]] test run report</h3>
-
-'''Sysid''': SYSID
-
-'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % ) <br>
-
-'''System info''':
-<pre>
-SYSINFO
-</pre>
-
-start time: STARTDATE <br>
-end time : ENDDATE <br>
-
-'''Image tests'''
-
-<REPEAT1>
-{| border=1 cellspacing=0 cellpadding=1
-|-
-| colspan=2 | FTESTNAME
-|-
-| Expected image || Actual image
-|-
-| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI
-|}
-
-<pre>
-TESTLOG
-</pre>
-
-
-
-</REPEAT1>
-
-
-'''Text tests'''
-
-<REPEAT2>
-{|border=1 cellspacing=0 cellpadding=1
-|-
-| FTESTNAME
-|}
-
-<pre>
-TESTLOG
-</pre>
-
-
-</REPEAT2>
-
-'''build.make and flags.make'''
-<REPEAT3>
-*[[MAKEFILE_NAME]]
-</REPEAT3>
-"""
- txtpages = {}
- imgs = {}
- passed_tests = filter(lambda x: x.passed, tests)
- failed_tests = filter(lambda x: not x.passed, tests)
-
- tests_to_report = failed_tests
- if include_passed: tests_to_report = tests
-
- try: percent = str(int(100.0*len(passed_tests) / len(tests)))
- except ZeroDivisionError: percent = 'n/a'
- s = wiki_template
- repeat1 = ezsearch('(<REPEAT1>.*?</REPEAT1>)',s)
- repeat2 = ezsearch('(<REPEAT2>.*?</REPEAT2>)',s)
- repeat3 = ezsearch('(<REPEAT3>.*?</REPEAT3>)',s)
- dic = { 'STARTDATE': startdate, 'ENDDATE': enddate, 'WIKI_ROOTPATH': wiki_rootpath,
- 'SYSINFO': sysinfo, 'SYSID':sysid,
- 'NUMTESTS':len(tests), 'NUMPASSED':len(passed_tests), 'PERCENTPASSED':percent }
- for key in dic.keys():
- s = s.replace(key,str(dic[key]))
-
- for t in tests_to_report:
- if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
- newchunk = re.sub('FTESTNAME',t.fullname,repeat2)
- newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
- s = s.replace(repeat2, newchunk+repeat2)
- elif t.type=='png':
- tmp = t.actualfile.replace(builddir,'')
- wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
- tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
- wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
- if hasattr(t, 'expectedfile_data'):
- imgs[wikiname_e] = t.expectedfile_data
- if t.actualfile:
- actualfile_wiki = '[[File:'+wikiname_a+'|250px]]'
- if hasattr(t, 'actualfile_data'):
- imgs[wikiname_a] = t.actualfile_data
- else:
- actualfile_wiki = 'No image generated.'
- newchunk = re.sub('FTESTNAME',t.fullname,repeat1)
- newchunk = newchunk.replace('ACTUALFILE_WIKI',actualfile_wiki)
- newchunk = newchunk.replace('EXPECTEDFILE',wikiname_e)
- newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
- s = s.replace(repeat1, newchunk+repeat1)
- else:
- raise Exception("Unknown test type %r"%t.type)
-
- makefiles_wikinames = {}
- for mf in sorted(makefiles.keys()):
- tmp = mf.replace('CMakeFiles','').replace('.dir','')
- wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
- newchunk = re.sub('MAKEFILE_NAME',wikiname,repeat3)
- s = s.replace(repeat3, newchunk+repeat3)
- makefiles_wikinames[mf] = wikiname
-
- s = s.replace(repeat1,'')
- s = s.replace(repeat2,'')
- s = s.replace(repeat3,'')
- s = re.sub('<REPEAT.*?>\n','',s)
- s = re.sub('</REPEAT.*?>','',s)
-
- mainpage_wikiname = wiki_rootpath + '_' + sysid + '_test_report'
- txtpages[ mainpage_wikiname ] = s
- for mf in sorted(makefiles.keys()):
- txtpages[ makefiles_wikinames[ mf ] ] = '\n*Subreport from [['+mainpage_wikiname+']]\n\n\n<pre>\n'+makefiles[mf]+'\n</pre>'
-
- return imgs, txtpages
-
-def png_encode64( fname, width=250 ):
- # en.wikipedia.org/wiki/Data_URI_scheme
- try:
- f = open( fname, "rb" )
- data = f.read()
- except:
- data = ''
- data_uri = data.encode("base64").replace("\n","")
- tag = '<img'
- tag += ' style="border:1px solid gray"'
- tag += ' src="data:image/png;base64,'
- tag += data_uri + '"'
- tag += ' width="'+str(width)+'"'
- if data =='':
- tag += ' alt="error: no image generated"'
- else:
- tag += ' alt="openscad_test_image"'
- tag += ' />\n'
- return tag
-
-def tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
- # kludge. assume wiki stuff has alreayd run and dumped files properly
- head = '<html><head><title>'+wiki_rootpath+' test run for '+sysid +'</title></head><body>'
- tail = '</body></html>'
-
- passed_tests = filter(lambda x: x.passed, tests)
- failed_tests = filter(lambda x: not x.passed, tests)
- try: percent = str(int(100.0*len(passed_tests) / len(tests)))
- except ZeroDivisionError: percent = 'n/a'
-
- tests_to_report = failed_tests
- if include_passed: tests_to_report = tests
-
- s=''
-
- s+= '\n<h3>'
- s+= '\nSystem info\n'
- s+= '\n</h3><p>'
- s+= '<pre>'+sysinfo+'</pre>\n'
-
- s+= '\n<pre>'
- s+= '\nSTARTDATE: '+ startdate
- s+= '\nENDDATE: '+ enddate
- s+= '\nWIKI_ROOTPATH: '+ wiki_rootpath
- s+= '\nSYSID: '+sysid
- s+= '\nNUMTESTS: '+str(len(tests))
- s+= '\nNUMPASSED: '+str(len(passed_tests))
- s+= '\nPERCENTPASSED: '+ percent
- s+= '\n</pre>'
-
- if not include_passed:
- s+= '<h3>Failed tests:</h3>\n'
-
- if len(tests_to_report)==0:
- s+= '<p>none</p>'
-
- for t in tests_to_report:
- if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
- s+='\n<pre>'+t.fullname+'</pre>\n'
- s+='<p><pre>'+t.fulltestlog+'</pre>\n\n'
- elif t.type=='png':
- tmp = t.actualfile.replace(builddir,'')
- wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
- tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
- wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
- # imgtag_e = <img src='+wikiname_e+' width=250/>'
- # imatag_a = <img src='+wikiname_a+' width=250/>'
- imgtag_e = png_encode64( t.expectedfile, 250 )
- imgtag_a = png_encode64( t.actualfile, 250 )
- s+='<table>'
- s+='\n<tr><td colspan=2>'+t.fullname
- s+='\n<tr><td>Expected<td>Actual'
- s+='\n<tr><td>' + imgtag_e + '</td>'
- s+='\n <td>' + imgtag_a + '</td>'
- s+='\n</table>'
- s+='\n<pre>'
- s+=t.fulltestlog
- s+='\n</pre>'
- else:
- raise Exception("Unknown test type %r"%t.type)
-
- s+='\n\n<p>\n\n'
-
- s+= '<h3> CMake .build files </h3>\n'
- s+= '\n<pre>'
- makefiles_wikinames = {}
- for mf in sorted(makefiles.keys()):
- mfname = mf.strip().lstrip(os.path.sep)
- text = open(os.path.join(builddir,mfname)).read()
- s+= '</pre><h4>'+mfname+'</h4><pre>'
- s+= text
- tmp = mf.replace('CMakeFiles','').replace('.dir','')
- wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
- # s += '\n<a href='+wikiname+'>'+wikiname+'</a><br>'
- s+= '\n</pre>'
- s+='\n'
-
- return head + s + tail
-
-def wiki_login(wikiurl,api_php_path,botname,botpass):
- site = mwclient.Site(wikiurl,api_php_path)
- site.login(botname,botpass)
- return site
-
-def wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikipgname):
- counter = 0
- done = False
- descrip = 'test'
- time.sleep(1)
- while not done:
- try:
- print 'login',botname,'to',wikiurl
- site = wiki_login(wikiurl,api_php_path,botname,botpass)
- print 'uploading...',
- if wikipgname.endswith('png'):
- site.upload(filedata,wikipgname,descrip,ignore=True)
- else:
- page = site.Pages[wikipgname]
- text = page.edit()
- page.save(filedata)
- done = True
- print 'transfer ok'
- except Exception, e:
- print 'Error:', type(e),e
- counter += 1
- if counter>maxretry:
- print 'giving up. please try a different wiki site'
- done = True
- else:
- print 'wiki',wikiurl,'down. retrying in 15 seconds'
- time.sleep(15)
-
-def upload(wikiurl,api_php_path='/',wiki_rootpath='test', sysid='null', botname='cakebaby',botpass='anniew',wikidir='.',dryrun=True):
- wetrun = not dryrun
- if dryrun: print 'dry run'
- try:
- global mwclient
- import mwclient
- except:
- print 'please download mwclient 0.6.5 and unpack here:', os.getcwd()
- sys.exit()
-
- if wetrun: site = wiki_login(wikiurl,api_php_path,botname,botpass)
-
- wikifiles = os.listdir(wikidir)
- testreport_page = filter( lambda x: 'test_report' in x, wikifiles )
- if (len(testreport_page)>1):
- print 'multiple test reports found, please clean dir',wikidir
- sys.exit()
-
- rootpage = testreport_page[0]
- print 'add',rootpage,' to main report page ',wiki_rootpath
- if wetrun:
- page = site.Pages[wiki_rootpath]
- text = page.edit()
- if not '[['+rootpage+']]' in text:
- page.save(text +'\n*[['+rootpage+']]\n')
-
- wikifiles = os.listdir(wikidir)
- wikifiles = filter(lambda x: not x.endswith('html'), wikifiles)
-
- print 'upload wiki pages:'
- for wikiname in wikifiles:
- filename = os.path.join(wikidir,wikiname)
- filedata = tryread(filename)
- print 'upload',len(filedata),'bytes from',wikiname
- if wetrun and len(filedata)>0:
- wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikiname)
- if len(filedata)==0:
- print 'cancelling empty upload'
+ filelist = []
+ for root, dirs, files in os.walk(builddir):
+ for fname in files: filelist += [ os.path.join(root, fname) ]
+ files = [file for file in filelist if 'build.make' in os.path.basename(file)
+ or 'flags.make' in os.path.basename(file)]
+ files = [file for file in files if 'esting' not in file and 'emporary' not in file]
+ result = {}
+ for fname in files:
+ result[fname.replace(builddir, '')] = tryread(fname)
+ return result
+
+
+def png_encode64(fname, width=250, data=None):
+ # en.wikipedia.org/wiki/Data_URI_scheme
+ data = data or tryread(fname) or ''
+ data_uri = data.encode('base64').replace('\n', '')
+ tag = '''<img src="data:image/png;base64,%s" width="%s" %s/>'''
+ if data == '':
+ alt = 'alt="error: no image generated" '
+ else:
+ alt = 'alt="openscad_test_image" '
+ tag %= (data_uri, width, alt)
+ return tag
+
def findlogfile(builddir):
- logpath = os.path.join(builddir,'Testing','Temporary')
- logfilename = os.path.join(logpath,'LastTest.log.tmp')
- if not os.path.isfile(logfilename):
- logfilename = os.path.join(logpath,'LastTest.log')
- if not os.path.isfile(logfilename):
- print 'cant find and/or open logfile',logfilename
- sys.exit()
- return logpath, logfilename
+ logpath = os.path.join(builddir, 'Testing', 'Temporary')
+ logfilename = os.path.join(logpath, 'LastTest.log.tmp')
+ if not os.path.isfile(logfilename):
+ logfilename = os.path.join(logpath, 'LastTest.log')
+ if not os.path.isfile(logfilename):
+ print 'can\'t find and/or open logfile', logfilename
+ sys.exit()
+ return logfilename
+
+# --- Templating ---
+
+class Templates(object):
+ html_template = '''<html>
+ <head><title>Test run for {sysid}</title>
+ {style}
+ </head>
+ <body>
+ <h1>{project_name} test run report</h1>
+ <p>
+ <b>Sysid</b>: {sysid}
+ </p>
+ <p>
+ <b>Result summary</b>: {numpassed} / {numtests} tests passed ({percent}%)
+ </p>
+
+ <h2>System info</h2>
+ <pre>{sysinfo}</pre>
+
+ <p>start time: {startdate}</p>
+ <p>end time: {enddate}</p>
+
+ <h2>Image tests</h2>
+ {image_tests}
+
+ <h2>Text tests</h2>
+ {text_tests}
+
+ <h2>build.make and flags.make</h2>
+ {makefiles}
+ </body></html>'''
+
+ style = '''
+ <style>
+ body {
+ color: black;
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+
+ table td, th {
+ border: 2px solid gray;
+ }
+
+ .text-name {
+ border: 2px solid black;
+ padding: 0.14em;
+ }
+ </style>'''
+
+ image_template = '''<table>
+ <tbody>
+ <tr><td colspan="2">{test_name}</td></tr>
+ <tr><td> Expected image </td><td> Actual image </td></tr>
+ <tr><td> {expected} </td><td> {actual} </td></tr>
+ </tbody>
+ </table>
+
+ <pre>
+ {test_log}
+ </pre>
+ '''
+
+ text_template = '''
+ <span class="text-name">{test_name}</span>
+
+ <pre>
+ {test_log}
+ </pre>
+ '''
+
+ makefile_template = '''
+ <h4>{name}</h4>
+ <pre>
+ {text}
+ </pre>
+ '''
+
+ def __init__(self, **defaults):
+ self.filled = {}
+ self.defaults = defaults
+
+ def fill(self, template, *args, **kwargs):
+ kwds = self.defaults.copy()
+ kwds.update(kwargs)
+ return getattr(self, template).format(*args, **kwds)
+
+ def add(self, template, var, *args, **kwargs):
+ self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs)
+ return self.filled[var]
+
+ def get(self, var):
+ return self.filled.get(var, '')
+
+
+def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
+ passed_tests = [test for test in tests if test.passed]
+ failed_tests = [test for test in tests if not test.passed]
+
+ report_tests = failed_tests
+ if include_passed:
+ report_tests = tests
+
+ percent = '%.0f' % (100.0 * len(passed_tests) / len(tests)) if tests else 'n/a'
+
+ templates = Templates()
+ for test in report_tests:
+ if test.type in ('txt', 'ast', 'csg', 'term', 'echo'):
+ templates.add('text_template', 'text_tests',
+ test_name=test.fullname,
+ test_log=test.fulltestlog)
+ elif test.type == 'png':
+ actual_img = png_encode64(test.actualfile,
+ data=vars(test).get('actualfile_data'))
+ expected_img = png_encode64(test.expectedfile,
+ data=vars(test).get('expectedfile_data'))
+ templates.add('image_template', 'image_tests',
+ test_name=test.fullname,
+ test_log=test.fulltestlog,
+ actual=actual_img,
+ expected=expected_img)
+ else:
+ raise TypeError('Unknown test type %r' % test.type)
+
+ for mf in sorted(makefiles.keys()):
+ mfname = mf.strip().lstrip(os.path.sep)
+ text = open(os.path.join(builddir, mfname)).read()
+ templates.add('makefile_template', 'makefiles', name=mfname, text=text)
+
+ text_tests = templates.get('text_tests')
+ image_tests = templates.get('image_tests')
+ makefiles_str = templates.get('makefiles')
+
+ return templates.fill('html_template', style=Templates.style,
+ sysid=sysid, sysinfo=sysinfo,
+ startdate=startdate, enddate=enddate,
+ project_name=project_name,
+ numtests=len(tests),
+ numpassed=len(passed_tests),
+ percent=percent, image_tests=image_tests,
+ text_tests=text_tests, makefiles=makefiles_str)
+
+# --- End Templating ---
+
+# --- Web Upload ---
+
+def postify(data):
+ return urlencode(data).encode()
+
+def create_page():
+ data = {
+ 'action': 'create',
+ 'type': 'html'
+ }
+ try:
+ response = urlopen('http://www.dinkypage.com', data=postify(data))
+ except:
+ return None
+ return response.geturl()
+
+def upload_html(page_url, title, html):
+ data = {
+ 'mode': 'editor',
+ 'title': title,
+ 'html': html,
+ 'ajax': '1'
+ }
+ try:
+ response = urlopen(page_url, data=postify(data))
+ except:
+ return False
+ return 'success' in response.read().decode()
+
+# --- End Web Upload ---
def debug(x):
- if debug_test_pp: print 'test_pretty_print: '+x
+ if debug_test_pp:
+ print 'test_pretty_print: ' + x
-debug_test_pp=False
-builddir=os.getcwd()
+debug_test_pp = False
+include_passed = False
+builddir = os.getcwd()
def main():
- #wikisite = 'cakebaby.referata.com'
- #wiki_api_path = ''
- global wikisite, wiki_api_path, wiki_rootpath, builddir, debug_test_pp
- global maxretry, dry, include_passed
-
- wikisite = 'cakebaby.wikia.com'
- wiki_api_path = '/'
- wiki_rootpath = 'OpenSCAD'
- if '--debug' in string.join(sys.argv): debug_test_pp=True
- maxretry = 10
-
- if bool(os.getenv("TEST_GENERATE")): sys.exit(0)
-
- include_passed = False
- if '--include-passed' in sys.argv: include_passed = True
-
- 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)
- 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'
-
- imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
-
- wikidir = os.path.join(logpath,sysid+'_report')
- debug( 'erasing files in ' + wikidir )
- try: map(lambda x:os.remove(os.path.join(wikidir,x)), os.listdir(wikidir))
- except: pass
- debug( 'output dir:\n' + wikidir.replace(os.getcwd(),'') )
- debug( 'writing ' + str(len(imgs)) + ' images' )
- debug( 'writing ' + str(len(txtpages)-1) + ' text pages' )
- debug( 'writing index.html ' )
- if '--wiki' in string.join(sys.argv):
- print "wiki output is deprecated"
- for pgname in sorted(imgs): trysave( os.path.join(wikidir,pgname), imgs[pgname])
- for pgname in sorted(txtpages): trysave( os.path.join(wikidir,pgname), txtpages[pgname])
-
- 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' )
+ 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
+
+ if '--include-passed' in sys.argv:
+ include_passed = True
+
+ dry = False
+ debug('running test_pretty_print')
+ if '--dryrun' in sys.argv:
+ dry = 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)
+
+ upload = False
+ if '--upload' in sys.argv:
+ upload = True
+ debug('will upload test report')
+
+ # --- End Command Line Parsing ---
+
+ sysinfo, sysid = read_sysinfo(os.path.join(builddir, 'sysinfo.txt'))
+ makefiles = load_makefiles(builddir)
+ 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(html)) + ' bytes')
+ trysave(html_filename, html)
+
+ 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')
if __name__=='__main__':
- main()
+ main()
contact: Jan Huwald // Impressum