Merge branch 'master' into unstable

Conflicts:
	src/CGALEvaluator.cc
	src/MainWindow.h
	src/MainWindow.ui
	src/import.cc
	src/mainwin.cc
	src/winconsole.c
	tests/CMakeLists.txt
svg-export
Marius Kintel 2014-02-23 15:49:26 -05:00
commit bab951b344
53 changed files with 756 additions and 270 deletions

View File

@ -1,22 +1,23 @@
OpenSCAD YYYY.MM
OpenSCAD 2014.0X
================
Language Features:
o Added diameter argument: circle(d), cylinder(d, d1, d2) and sphere(d)
o Added parent_module() and $parent_modules
o Added children() as a replacement for child()
o FIXME: Unicode support
o FIXME: Ranges with negative steps
o FIXME: Experimental concat()
o Unicode strings (using UTF-8) are now correctly handled
o Ranges can have a negative step value
o Added experimental concat() function for concatenating vectors
Program Features:
o Added --info parameter to the cmd-line for system/library info
o Added --enable parameter to enable experimental features
o Added --csglimit parameter to change CSG rendering limit
o Added Reset View in GUI
o Added Feature tab in Preferences
o Cmd-line: --info parameter prints system/library info
o Cmd-line: --enable parameter to enable experimental features
o Cmd-line: --csglimit parameter to change CSG rendering limit
o GUI: Added Reset View
o GUI: Added Search&Replace in editor
o GUI: Syntax highlighting now has a dark background theme
Bugfixes:
Bugfixes/improvements:
o polyhedron() is now much more robust handling almost planar polygons
o Automatic reloads of large designs are more robust
o Boolean logic in if() statements are now correctly short-circuited
@ -25,20 +26,24 @@ o resize(, auto=true) didn't work when shrinking objects
o The $children variable sometimes misbehaved due to dynamic scoping
o The --camera cmd-line option behaved differently then the corresponding GUI function
o PNG export now doesn't leak transparency settings into the target image
o Improved performance of OpenCSG (F5) compilation in some cases
o Improved performance of 3D hull() operations
o Some editor misbehaviors were fixed
o Stability fixes of CGAL-related crashes
o Windows cmd-line can now handle spaces in filenames
o Default CSG rendering limit is now 100K elements
o Fixed a crash reading DXF files using comma as decimal separator
o Fixed a crash running the cmd-line without a HOME env. variable
o Intersecting something with nothing now correctly results in an empty object
Deprecations:
o child() is no longer supported. Use children() instead.
o polyhedron(triangles=[...]): Use polyhedron(faces=[...]) instead.
Misc:
o We now use CGAL's EPEC kernel
o Additional output formats: .ast, .term, null (these are most useful for testing)
o Test framework now shares more code with the GUI app
o Test report can now be automatically uploaded to dinkypage.com
o Better compatibility with BSD systems
o Qt5 support
OpenSCAD 2013.06
================

View File

@ -2,7 +2,7 @@
module step(len, mod)
{
for (i = [0:$children-1])
translate([ len*(i - ($children-1)/2), 0, 0 ]) child((i+mod) % $children);
translate([ len*(i - ($children-1)/2), 0, 0 ]) children((i+mod) % $children);
}
for (i = [1:4])

View File

@ -85,8 +85,14 @@ get_openscad_source_code()
if [ "`echo $? | grep 0`" ]; then
echo clone of source code is ok
else
echo clone of openscad source code failed. exiting
exit 1
if [ $DOUPLOAD ]; then
if [ ! $DOBUILD ]; then
echo upload only - skipping openscad git clone
fi
else
echo clone of openscad source code failed. exiting
exit 1
fi
fi
cd openscad
git submodule update --init # MCAD

View File

@ -3,6 +3,8 @@
# NB! To build a release build, the VERSION and VERSIONDATE environment variables needs to be set.
# See doc/release-checklist.txt
export NUMCPU=$(sysctl -n hw.ncpu)
human_filesize()
{
awk -v sum=$1 'BEGIN {
@ -20,6 +22,7 @@ update_www_download_links()
local $*
filesize=$(human_filesize $filesize)
webdir=../openscad.github.com
# FIXME: release vs. snapshot
incfile=inc/mac_snapshot_links.js
BASEURL='http://files.openscad.org/'
DATECODE=`date +"%Y.%m.%d"`
@ -51,7 +54,7 @@ fi
PATH=${PATH//\/opt\/local\/libexec\/ccache:}
# This is the same location as DEPLOYDIR in macosx-build-dependencies.sh
export OPENSCAD_LIBRARIES=$PWD/../libraries/install
export OPENSCAD_LIBRARIES=$PWD/../libraries/homebrew
# Make sure that the correct Qt tools are used
export PATH=$OPENSCAD_LIBRARIES/bin:$PATH

View File

@ -10,6 +10,7 @@
#include "memory.h"
#include <vector>
#include <QMutex>
#include <QSet>
class MainWindow : public QMainWindow, public Ui::MainWindow
{
@ -88,6 +89,8 @@ private:
static void consoleOutput(const std::string &msg, void *userdata);
void loadViewSettings();
void loadDesignSettings();
void saveBackup();
void writeBackup(class QFile *file);
class QMessageBox *openglbox;
@ -150,6 +153,7 @@ private slots:
void actionFlushCaches();
public:
static QSet<MainWindow*> *windows;
static void setExamplesDir(const QString &dir) { MainWindow::qexamplesdir = dir; }
void viewModeActionsUncheck();
void setCurrentOutput();
@ -196,13 +200,17 @@ public slots:
private:
static void report_func(const class AbstractNode*, void *vp, int mark);
char const * afterCompileSlot;
bool procevents;
class QTemporaryFile *tempFile;
class ProgressWidget *progresswidget;
class CGALWorker *cgalworker;
QMutex consolemutex;
signals:
void highlightError(int);
void unhighlightLastError();
};
class GuiLocker

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>681</width>
<width>846</width>
<height>647</height>
</rect>
</property>
@ -15,16 +15,7 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
@ -32,14 +23,14 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="editorlayoutwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<widget class="QWidget" name="editorPane" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<item>
<widget class="QFrame" name="find_panel">
<property name="enabled">
@ -60,72 +51,8 @@
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="horizontalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="1" column="3" colspan="2">
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>Replace</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="replaceAllButton">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="findInputField">
<property name="placeholderText">
<string>Search string</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="replaceInputField">
<property name="placeholderText">
<string>Replacement string</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="prevButton">
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="nextButton">
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="hideFindButton">
<property name="text">
<string>Done</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QComboBox" name="findTypeComboBox">
<item>
<property name="text">
@ -139,6 +66,55 @@
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="findInputField">
<property name="placeholderText">
<string>Search string</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="prevButton">
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="nextButton">
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="hideFindButton">
<property name="text">
<string>Done</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="replaceInputField">
<property name="placeholderText">
<string>Replacement string</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>Replace</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="replaceAllButton">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -183,17 +159,8 @@
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<layout class="QHBoxLayout" name="horizontalLayoutAnimate">
<property name="margin">
<number>0</number>
</property>
<item>
@ -247,7 +214,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>681</width>
<width>846</width>
<height>22</height>
</rect>
</property>
@ -265,16 +232,31 @@
<string>Examples</string>
</property>
</widget>
<widget class="QMenu" name="menuExport">
<property name="title">
<string>Export</string>
</property>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportCSG"/>
<addaction name="separator"/>
<addaction name="designActionExportImage"/>
</widget>
<addaction name="fileActionNew"/>
<addaction name="fileActionOpen"/>
<addaction name="menuOpenRecent"/>
<addaction name="menuExamples"/>
<addaction name="fileShowLibraryFolder"/>
<addaction name="separator"/>
<addaction name="fileActionReload"/>
<addaction name="fileActionClose"/>
<addaction name="separator"/>
<addaction name="fileActionSave"/>
<addaction name="fileActionSaveAs"/>
<addaction name="fileActionReload"/>
<addaction name="separator"/>
<addaction name="menuExport"/>
<addaction name="separator"/>
<addaction name="fileShowLibraryFolder"/>
<addaction name="separator"/>
<addaction name="fileActionQuit"/>
</widget>
<widget class="QMenu" name="menu_Edit">
@ -316,17 +298,11 @@
<addaction name="designActionPreview"/>
<addaction name="designActionRender"/>
<addaction name="separator"/>
<addaction name="designCheckValidity"/>
<addaction name="designCheckValidity"/>
<addaction name="designActionDisplayAST"/>
<addaction name="designActionDisplayCSGTree"/>
<addaction name="designActionDisplayCSGProducts"/>
<addaction name="separator"/>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportCSG"/>
<addaction name="designActionExportImage"/>
<addaction name="separator"/>
<addaction name="designActionFlushCaches"/>
</widget>
<widget class="QMenu" name="menu_View">
@ -559,9 +535,9 @@
</property>
</action>
<action name="designCheckValidity">
<property name="text">
<string>Check Validity</string>
</property>
<property name="text">
<string>Check Validity</string>
</property>
</action>
<action name="designActionDisplayAST">
<property name="text">

View File

@ -22,51 +22,68 @@
ModuleCache *ModuleCache::inst = NULL;
/*!
Reevaluate the given file and recompile if necessary.
Returns NULL on any error (e.g. compile error or file not found)
Reevaluate the given file and all it's dependencies and recompile anything
needing reevaluation.
The given filename must be absolute.
If the given filename is relative, it means that the module hasn't been
previously located.
Sets the module reference to the new modile, or NULL on any error (e.g. compile
error or file not found)
Returns true if anything was compiled (module or dependencies) and false otherwise.
*/
FileModule *ModuleCache::evaluate(const std::string &filename)
bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
{
FileModule *lib_mod = (this->entries.find(filename) != this->entries.end()) ?
&(*this->entries[filename].module) : NULL;
FileModule *lib_mod = NULL;
bool found = false;
if (this->entries.find(filename) != this->entries.end()) {
found = true;
lib_mod = this->entries[filename].module;
}
// Don't try to recursively evaluate - if the file changes
// during evaluation, that would be really bad.
if (lib_mod && lib_mod->isHandlingDependencies()) return lib_mod;
bool shouldCompile = true;
// Create cache ID
struct stat st;
memset(&st, 0, sizeof(struct stat));
bool valid = (stat(filename.c_str(), &st) == 0);
// If file isn't there, just return and let the cache retain the old module
if (!valid) return NULL;
// If file isn't there, just return and let the cache retain the old module
if (!valid) return false;
// If the file is present, we'll always cache some result
std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
// Lookup in cache
if (lib_mod) {
if (this->entries[filename].cache_id == cache_id) {
cache_entry &entry = this->entries[filename];
// Initialize entry, if new
if (!found) {
entry.module = NULL;
entry.cache_id = cache_id;
}
bool shouldCompile = true;
if (found) {
// Files should only be recompiled if the cache ID changed
if (entry.cache_id == cache_id) {
shouldCompile = false;
if (lib_mod->includesChanged()) {
// Recompile if includes changed
if (lib_mod && lib_mod->includesChanged()) {
lib_mod = NULL;
shouldCompile = true;
}
}
}
else {
shouldCompile = valid;
}
#ifdef DEBUG
// Causes too much debug output
//if (!shouldCompile) PRINTB("Using cached library: %s (%p)", filename % lib_mod);
#endif
// If cache lookup failed (non-existing or old timestamp), compile module
if (shouldCompile) {
#ifdef DEBUG
if (this->entries.find(filename) != this->entries.end()) {
if (found) {
PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
}
else {
@ -79,42 +96,33 @@ FileModule *ModuleCache::evaluate(const std::string &filename)
std::ifstream ifs(filename.c_str());
if (!ifs.is_open()) {
PRINTB("WARNING: Can't open library file '%s'\n", filename);
return NULL;
return false;
}
textbuf << ifs.rdbuf();
}
textbuf << "\n" << commandline_commands;
print_messages_push();
FileModule *oldmodule = NULL;
cache_entry e = { NULL, cache_id };
if (this->entries.find(filename) != this->entries.end()) {
oldmodule = this->entries[filename].module;
}
this->entries[filename] = e;
FileModule *oldmodule = lib_mod;
std::string pathname = boosty::stringy(fs::path(filename).parent_path());
lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname.c_str(), false));
PRINTB_NOCACHE(" compiled module: %p", lib_mod);
if (lib_mod) {
// We defer deletion so we can ensure that the new module won't
// have the same address as the old
delete oldmodule;
this->entries[filename].module = lib_mod;
} else {
this->entries.erase(filename);
}
// We defer deletion so we can ensure that the new module won't
// have the same address as the old
if (oldmodule) delete oldmodule;
entry.module = lib_mod;
entry.cache_id = cache_id;
print_messages_pop();
}
module = lib_mod;
bool depschanged = lib_mod ? lib_mod->handleDependencies() : false;
if (lib_mod) {
lib_mod->handleDependencies();
}
return lib_mod;
return shouldCompile || depschanged;
}
void ModuleCache::clear()
@ -124,7 +132,11 @@ void ModuleCache::clear()
FileModule *ModuleCache::lookup(const std::string &filename)
{
return (this->entries.find(filename) != this->entries.end()) ?
&(*this->entries[filename].module) : NULL;
return isCached(filename) ? this->entries[filename].module : NULL;
}
bool ModuleCache::isCached(const std::string &filename)
{
return this->entries.find(filename) != this->entries.end();
}

View File

@ -8,8 +8,9 @@ class ModuleCache
{
public:
static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
class FileModule *evaluate(const std::string &filename);
bool evaluate(const std::string &filename, class FileModule *&module);
class FileModule *lookup(const std::string &filename);
bool isCached(const std::string &filename);
size_t size() { return this->entries.size(); }
void clear();

View File

@ -1,6 +1,11 @@
#include "PlatformUtils.h"
#import <Foundation/Foundation.h>
std::string PlatformUtils::pathSeparatorChar()
{
return ":";
}
std::string PlatformUtils::documentsPath()
{
return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]);

View File

@ -1,6 +1,11 @@
#include "PlatformUtils.h"
#include "boosty.h"
std::string PlatformUtils::pathSeparatorChar()
{
return ":";
}
std::string PlatformUtils::documentsPath()
{
const char *home = getenv("HOME");

View File

@ -6,6 +6,11 @@
#endif
#include <shlobj.h>
std::string PlatformUtils::pathSeparatorChar()
{
return ";";
}
// convert from windows api w_char strings (usually utf16) to utf8 std::string
std::string winapi_wstr_to_utf8( std::wstring wstr )
{

View File

@ -1,7 +1,9 @@
#include <glib.h>
#include "PlatformUtils.h"
#include "boosty.h"
#include <glib.h>
extern std::vector<std::string> librarypath;
bool PlatformUtils::createLibraryPath()
{
@ -41,6 +43,40 @@ std::string PlatformUtils::libraryPath()
return boosty::stringy( path );
}
std::string PlatformUtils::backupPath()
{
fs::path path;
try {
std::string pathstr = PlatformUtils::documentsPath();
if (pathstr=="") return "";
path = boosty::canonical(fs::path( pathstr ));
if (path.empty()) return "";
path /= "OpenSCAD";
path /= "backups";
} catch (const fs::filesystem_error& ex) {
PRINTB("ERROR: %s",ex.what());
}
return boosty::stringy( path );
}
bool PlatformUtils::createBackupPath()
{
std::string path = PlatformUtils::backupPath();
bool OK = false;
try {
if (!fs::exists(fs::path(path))) {
OK = fs::create_directories( path );
}
if (!OK) {
PRINTB("ERROR: Cannot create %s", path );
}
} catch (const fs::filesystem_error& ex) {
PRINTB("ERROR: %s",ex.what());
}
return OK;
}
#include "version_check.h"
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
@ -108,6 +144,8 @@ std::string PlatformUtils::info()
std::string cgal_2d_kernelEx = "";
#endif // ENABLE_CGAL
const char *env_path = getenv("OPENSCADPATH");
s << "OpenSCAD Version: " << TOSTRING(OPENSCAD_VERSION)
<< "\nCompiler, build date: " << compiler_info << ", " << __DATE__
<< "\nBoost version: " << BOOST_LIB_VERSION
@ -117,8 +155,12 @@ std::string PlatformUtils::info()
<< "\nQt version: " << qtVersion
<< "\nMingW build: " << mingwstatus
<< "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION
<< "\nOPENSCADPATH: " << getenv("OPENSCADPATH") << "\n"
;
<< "\nOPENSCADPATH: " << (env_path == NULL ? "<not set>" : env_path)
<< "\nOpenSCAD library path:\n";
for (std::vector<std::string>::iterator it = librarypath.begin();it != librarypath.end();it++) {
s << " " << *it << "\n";
}
return s.str();
}

View File

@ -8,7 +8,18 @@ namespace PlatformUtils {
std::string documentsPath();
std::string libraryPath();
bool createLibraryPath();
std::string backupPath();
bool createBackupPath();
std::string info();
/**
* Single character separating path specifications in a list
* (e.g. OPENSCADPATH). On Windows that's ';' and on most other
* systems ':'.
*
* @return the path separator
*/
std::string pathSeparatorChar();
}
#endif

View File

@ -417,8 +417,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>803</width>
<height>311</height>
<width>98</width>
<height>36</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
@ -577,6 +577,12 @@
<property name="allowedAreas">
<set>Qt::TopToolBarArea</set>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>

View File

@ -161,6 +161,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
if (type == CHILD)
{
PRINT("DEPRECATED: child() will be removed in future releases. Use children() instead.");
int n = 0;
if (evalctx->numArgs() > 0) {
double v;

View File

@ -1,6 +1,12 @@
#include "editor.h"
#include "Preferences.h"
Editor::Editor(QWidget *parent) : QTextEdit(parent)
{
setAcceptRichText(false);
this->highlighter = new Highlighter(this->document());
}
void Editor::indentSelection()
{
QTextCursor cursor = textCursor();
@ -122,3 +128,21 @@ void Editor::setPlainText(const QString &text)
verticalScrollBar()->setSliderPosition(y);
}
}
void Editor::highlightError(int error_pos)
{
highlighter->highlightError( error_pos );
QTextCursor cursor = this->textCursor();
cursor.setPosition( error_pos );
this->setTextCursor( cursor );
}
void Editor::unhighlightLastError()
{
highlighter->unhighlightLastError();
}
Editor::~Editor()
{
delete highlighter;
}

View File

@ -3,14 +3,15 @@
#include <QWidget>
#include <QWheelEvent>
#include <QScrollBar>
#include <QTextEdit>
#include "highlighter.h"
class Editor : public QTextEdit
{
Q_OBJECT
public:
Editor(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); }
void setPlainText(const QString &text);
Editor(QWidget *parent);
~Editor();
public slots:
void zoomIn();
void zoomOut();
@ -21,6 +22,11 @@ public slots:
void unindentSelection();
void commentSelection();
void uncommentSelection();
void setPlainText(const QString &text);
void highlightError(int error_pos);
void unhighlightLastError();
private:
void wheelEvent ( QWheelEvent * event );
Highlighter *highlighter;
};

View File

@ -36,6 +36,10 @@
#include "printutils.h"
#include <boost/foreach.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
using boost::math::isnan;
using boost::math::isinf;
/*
Random numbers
@ -598,6 +602,71 @@ Value builtin_parent_module(const Context *, const EvalContext *evalctx)
return Value(Module::stack_element(s - 1 - n));
}
Value builtin_norm(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::VECTOR) {
double sum = 0;
Value::VectorType v = evalctx->getArgValue(0).toVector();
for (size_t i = 0; i < v.size(); i++)
if (v[i].type() == Value::NUMBER)
sum += pow(v[i].toDouble(),2);
else {
PRINT(" WARNING: Incorrect arguments to norm()");
return Value();
}
return Value(sqrt(sum));
}
return Value();
}
Value builtin_cross(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() != 2) {
PRINT("WARNING: Invalid number of parameters for cross()");
return Value();
}
Value arg0 = evalctx->getArgValue(0);
Value arg1 = evalctx->getArgValue(1);
if ((arg0.type() != Value::VECTOR) || (arg1.type() != Value::VECTOR)) {
PRINT("WARNING: Invalid type of parameters for cross()");
return Value();
}
Value::VectorType v0 = arg0.toVector();
Value::VectorType v1 = arg1.toVector();
if ((v0.size() != 3) || (v1.size() != 3)) {
PRINT("WARNING: Invalid vector size of parameter for cross()");
return Value();
}
for (unsigned int a = 0;a < 3;a++) {
if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) {
PRINT("WARNING: Invalid value in parameter vector for cross()");
return Value();
}
double d0 = v0[a].toDouble();
double d1 = v1[a].toDouble();
if (isnan(d0) || isnan(d1)) {
PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()");
return Value();
}
if (isinf(d0) || isinf(d1)) {
PRINT("WARNING: Invalid value (INF) in parameter vector for cross()");
return Value();
}
}
double x = v0[1].toDouble() * v1[2].toDouble() - v0[2].toDouble() * v1[1].toDouble();
double y = v0[2].toDouble() * v1[0].toDouble() - v0[0].toDouble() * v1[2].toDouble();
double z = v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble();
Value::VectorType result;
result.push_back(Value(x));
result.push_back(Value(y));
result.push_back(Value(z));
return Value(result);
}
void register_builtin_functions()
{
Builtins::init("abs", new BuiltinFunction(&builtin_abs));
@ -627,5 +696,7 @@ void register_builtin_functions()
Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version));
Builtins::init("version_num", new BuiltinFunction(&builtin_version_num));
Builtins::init("norm", new BuiltinFunction(&builtin_norm));
Builtins::init("cross", new BuiltinFunction(&builtin_cross));
Builtins::init("parent_module", new BuiltinFunction(&builtin_parent_module));
}

View File

@ -2,20 +2,24 @@
#define HIGHLIGHTER_H_
#include <QSyntaxHighlighter>
#include <QTextDocument>
#include <QTextFormat>
#include <QTextEdit>
#include <QHash>
class Highlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
enum state_e {NORMAL=-1,QUOTE,COMMENT};
QHash<QString, QTextCharFormat> tokenFormats;
QTextCharFormat errorFormat;
Highlighter(QTextDocument *parent);
void highlightBlock(const QString &text);
void assignFormatsToTokens(const QString &);
void portable_rehighlightBlock( const QTextBlock &text );
void highlightError(int error_pos);
void unhighlightLastError();
void assignFormatsToTokens(const QString &);
private:
QTextBlock lastErrorBlock;
int errorPos;
@ -23,7 +27,6 @@ private:
QMap<QString,QStringList> tokentypes;
QMap<QString,QTextCharFormat> typeformats;
int lastDocumentPos();
void portable_rehighlightBlock( const QTextBlock &text );
};
#endif

View File

@ -210,7 +210,7 @@ Geometry *ImportNode::createGeometry() const
bool binary = false;
std::streampos file_size = f.tellg();
f.seekg(80);
if (!f.eof()) {
if (f.good() && !f.eof()) {
uint32_t facenum = 0;
f.read((char *)&facenum, sizeof(uint32_t));
#ifdef BOOST_BIG_ENDIAN
@ -224,13 +224,12 @@ Geometry *ImportNode::createGeometry() const
char data[5];
f.read(data, 5);
if (!binary && !f.eof() && !memcmp(data, "solid", 5)) {
if (!binary && !f.eof() && f.good() && !memcmp(data, "solid", 5)) {
int i = 0;
double vdata[3][3];
std::string line;
std::getline(f, line);
while (!f.eof()) {
std::getline(f, line);
boost::trim(line);
if (boost::regex_search(line, ex_sfe)) {
@ -261,7 +260,7 @@ Geometry *ImportNode::createGeometry() const
}
}
}
else
else if (binary && !f.eof() && f.good())
{
f.ignore(80-5+4);
while (1) {
@ -288,7 +287,6 @@ Geometry *ImportNode::createGeometry() const
else {
file >> poly;
file.close();
bool err = createPolySetFromPolyhedron(poly, *p);
}
#else

View File

@ -75,6 +75,7 @@
#include <QSettings>
#include <QProgressDialog>
#include <QMutexLocker>
#include <QTemporaryFile>
#include <fstream>
@ -101,6 +102,9 @@
#include "boosty.h"
// Keeps track of open window
QSet<MainWindow*> *MainWindow::windows = NULL;
// Global application state
unsigned int GuiLocker::gui_locked = 0;
QString MainWindow::qexamplesdir;
@ -154,9 +158,13 @@ settings_valueList(const QString &key, const QList<int> &defaultList = QList<int
}
MainWindow::MainWindow(const QString &filename)
: root_inst("group"), progresswidget(NULL)
: root_inst("group"), tempFile(NULL), progresswidget(NULL)
{
setupUi(this);
this->setAttribute(Qt::WA_DeleteOnClose);
if (!MainWindow::windows) MainWindow::windows = new QSet<MainWindow*>;
MainWindow::windows->insert(this);
#ifdef ENABLE_CGAL
this->cgalworker = new CGALWorker();
@ -186,7 +194,8 @@ MainWindow::MainWindow(const QString &filename)
fps = 0;
fsteps = 1;
highlighter = new Highlighter(editor->document());
connect(this, SIGNAL(highlightError(int)), editor, SLOT(highlightError(int)));
connect(this, SIGNAL(unhighlightLastError()), editor, SLOT(unhighlightLastError()));
editor->setTabStopWidth(30);
editor->setLineWrapping(true); // Not designable
@ -471,6 +480,7 @@ MainWindow::~MainWindow()
#ifdef ENABLE_OPENCSG
delete this->opencsgRenderer;
#endif
MainWindow::windows->remove(this);
}
void MainWindow::showProgress()
@ -524,6 +534,7 @@ MainWindow::openFile(const QString &new_filename)
#endif
setFileName(actual_filename);
editor->setPlainText("");
this->last_compiled_doc = "";
fileChangedOnDisk(); // force cached autoReloadId to update
refreshDocument();
@ -665,6 +676,7 @@ void MainWindow::compile(bool reload, bool forcedone)
if (shouldcompiletoplevel) {
console->clear();
if (editor->isContentModified()) saveBackup();
compileTopLevelDocument();
didcompile = true;
}
@ -685,6 +697,16 @@ void MainWindow::compile(bool reload, bool forcedone)
return;
}
}
if (!reload && didcompile) {
if (!animate_panel->isVisible()) {
emit unhighlightLastError();
if (!this->root_module) {
emit highlightError( parser_error_pos );
}
}
}
compileDone(didcompile | forcedone);
}
@ -1013,6 +1035,45 @@ void MainWindow::actionOpenExample()
}
}
void MainWindow::writeBackup(QFile *file)
{
// see MainWindow::saveBackup()
file->resize(0);
QTextStream writer(file);
writer.setCodec("UTF-8");
writer << this->editor->toPlainText();
PRINTB("Saved backup file: %s", file->fileName().toLocal8Bit().constData());
}
void MainWindow::saveBackup()
{
std::string path = PlatformUtils::backupPath();
if ((!fs::exists(path)) && (!PlatformUtils::createBackupPath())) {
PRINTB("WARNING: Cannot create backup path: %s", path);
return;
}
QString backupPath = QString::fromStdString(path);
if (!backupPath.endsWith("/")) backupPath.append("/");
QString basename = "unsaved";
if (!this->fileName.isEmpty()) {
QFileInfo fileInfo = QFileInfo(this->fileName);
basename = fileInfo.baseName();
}
if (!this->tempFile) {
this->tempFile = new QTemporaryFile(backupPath.append(basename + "-backup-XXXXXXXX.scad"));
}
if ((!this->tempFile->isOpen()) && (! this->tempFile->open())) {
PRINT("WARNING: Failed to create backup file");
return;
}
return writeBackup(this->tempFile);
}
void MainWindow::actionSave()
{
if (this->fileName.isEmpty()) {
@ -1089,10 +1150,10 @@ void MainWindow::hideEditor()
{
QSettings settings;
if (editActionHide->isChecked()) {
editor->hide();
editorPane->hide();
settings.setValue("view/hideEditor",true);
} else {
editor->show();
editorPane->show();
settings.setValue("view/hideEditor",false);
}
}
@ -1227,6 +1288,10 @@ void MainWindow::updateTemporalVariables()
top_ctx.set_variable("$vpr", Value(vpr));
}
/*!
Returns true if the current document is a file on disk and that file has new content.
Returns false if a file on disk has disappeared or if we haven't yet saved.
*/
bool MainWindow::fileChangedOnDisk()
{
if (!this->fileName.isEmpty()) {
@ -1267,15 +1332,6 @@ void MainWindow::compileTopLevelDocument()
QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
false);
if (!animate_panel->isVisible()) {
highlighter->unhighlightLastError();
if (!this->root_module) {
QTextCursor cursor = editor->textCursor();
cursor.setPosition(parser_error_pos);
editor->setTextCursor(cursor);
highlighter->highlightError( parser_error_pos );
}
}
}
void MainWindow::checkAutoReload()
@ -1405,6 +1461,7 @@ void MainWindow::actionRender()
void MainWindow::cgalRender()
{
if (!this->root_module || !this->root_node) {
compileEnded();
return;
}
@ -2014,6 +2071,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
settings.setValue("window/position", pos());
settings_setValueList("window/splitter1sizes",splitter1->sizes());
settings_setValueList("window/splitter2sizes",splitter2->sizes());
if (this->tempFile) {
delete this->tempFile;
this->tempFile = NULL;
}
event->accept();
} else {
event->ignore();

View File

@ -263,45 +263,58 @@ bool FileModule::handleDependencies()
if (this->is_handling_dependencies) return false;
this->is_handling_dependencies = true;
bool changed = false;
bool somethingchanged = false;
std::vector<std::pair<std::string,std::string> > updates;
// If a lib in usedlibs was previously missing, we need to relocate it
// by searching the applicable paths. We can identify a previously missing module
// as it will have a relative path.
// Iterating manually since we want to modify the container while iterating
FileModule::ModuleContainer::iterator iter = this->usedlibs.begin();
while (iter != this->usedlibs.end()) {
FileModule::ModuleContainer::iterator curr = iter++;
BOOST_FOREACH(std::string filename, this->usedlibs) {
bool wasmissing = false;
bool found = true;
// Get an absolute filename for the module
std::string filename = *curr;
if (!boosty::is_absolute(filename)) {
wasmissing = true;
fs::path fullpath = find_valid_path(this->path, filename);
if (!fullpath.empty()) filename = boosty::stringy(fullpath);
if (!fullpath.empty()) {
updates.push_back(std::make_pair(filename, boosty::stringy(fullpath)));
filename = boosty::stringy(fullpath);
}
else {
found = false;
}
}
FileModule *oldmodule = ModuleCache::instance()->lookup(filename);
FileModule *newmodule = ModuleCache::instance()->evaluate(filename);
// Detect appearance but not removal of files
if (newmodule && oldmodule != newmodule) {
changed = true;
if (found) {
bool wascached = ModuleCache::instance()->isCached(filename);
FileModule *oldmodule = ModuleCache::instance()->lookup(filename);
FileModule *newmodule;
bool changed = ModuleCache::instance()->evaluate(filename, newmodule);
// Detect appearance but not removal of files, and keep old module
// on compile errors (FIXME: Is this correct behavior?)
if (changed) {
#ifdef DEBUG
PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule);
PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule);
#endif
}
if (!newmodule) {
}
somethingchanged |= changed;
// Only print warning if we're not part of an automatic reload
if (!oldmodule && !wasmissing) {
if (!newmodule && !wascached && !wasmissing) {
PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
}
}
}
// Relative filenames which were located is reinserted as absolute filenames
typedef std::pair<std::string,std::string> stringpair;
BOOST_FOREACH(const stringpair &files, updates) {
this->usedlibs.erase(files.first);
this->usedlibs.insert(files.second);
}
this->is_handling_dependencies = false;
return changed;
return somethingchanged;
}
AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const

View File

@ -546,12 +546,17 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
BOOST_FOREACH(const string &infile, inputFiles) {
new MainWindow(assemblePath(original_path, infile));
}
app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
#else
MainWindow *m = new MainWindow(assemblePath(original_path, inputFiles[0]));
app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit()));
#endif
return app.exec();
app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
int rc = app.exec();
if (MainWindow::windows) {
foreach (MainWindow *mainw, *MainWindow::windows) {
delete mainw;
}
}
return rc;
}
#else // OPENSCAD_QTGUI
bool QtUseGUI() { return false; }

View File

@ -90,17 +90,15 @@ fs::path find_valid_path(const fs::path &sourcepath,
void parser_init(const std::string &applicationpath)
{
// Add paths from OPENSCADPATH before adding built-in paths
// Add paths from OPENSCADPATH before adding built-in paths
const char *openscadpaths = getenv("OPENSCADPATH");
if (openscadpaths) {
std::string paths(openscadpaths);
typedef boost::split_iterator<std::string::iterator> string_split_iterator;
for (string_split_iterator it =
make_split_iterator(paths, first_finder(":", boost::is_iequal()));
it != string_split_iterator();
++it) {
add_librarydir(boosty::absolute(fs::path(boost::copy_range<std::string>(*it))).string());
}
std::string sep = PlatformUtils::pathSeparatorChar();
typedef boost::split_iterator<std::string::iterator> string_split_iterator;
for (string_split_iterator it = boost::make_split_iterator(paths, boost::first_finder(sep, boost::is_iequal())); it != string_split_iterator(); ++it) {
add_librarydir(boosty::absolute(fs::path(boost::copy_range<std::string>(*it))).string());
}
}
// This is the built-in user-writable library path

View File

@ -28,7 +28,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCMDLEN 64000
// want to use system definitions instead like #include <system.h>
#ifndef WIFEXITED
#define WIFEXITED(S) (((S) & 0xff) == 0)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(S) (((S) >> 8) & 0xff)
#endif
#define MAXCMDLEN /*64000*/ 32768 /* MS Windows limit */
#define BUFFSIZE 42
int main( int argc, char * argv[] )
@ -36,57 +45,67 @@ int main( int argc, char * argv[] )
FILE *cmd_stdout;
char cmd[MAXCMDLEN];
char buffer[BUFFSIZE];
char *fgets_result;
int eof = 0;
int pclose_result;
int i;
const char * argchar;
int result = 0;
int quotify_arg = 0;
unsigned n; // total number of characters in cmd
static const char exe_str[] = "openscad.exe";
static const char redirect_str[] = " 2>&1"; // capture stderr and stdout
strcat( cmd, "\0" );
strcat( cmd, "openscad.exe" );
memcpy(cmd, exe_str, (n = sizeof(exe_str)-1)); // without \0
for ( i = 1 ; i < argc ; ++i ) {
quotify_arg = 0;
for ( argchar = argv[i]; *argchar!=0; argchar++ ) {
if ((char)(*argchar)==' ') quotify_arg = 1;
register char *s;
/*bool*/ int quote;
cmd[n++] = ' ';
// MS Windows special characters need quotation
// See issues #440, #441 & #479
quote = NULL != strpbrk((s = argv[i]), " \"&'<>^|\t");
if (quote) cmd[n++] = '"';
while (*s) { // copy & check
// The following test is compomise between brevity, clarity and performance.
// It could be boiled down to: if ('"' == s[strspn(s,"\\")])
// or expanded to avoid repetitive passes of strspn() over same data.
if ('"' == *s || ('\\' == *s && '"' == s[strspn(s,"\\")])) cmd[n++] = '\\';
cmd[n++] = *s++;
if (n >= MAXCMDLEN-sizeof(redirect_str)) {
fprintf(stderr, "Command line length exceeds limit of %d\n", MAXCMDLEN);
return 1;
}
}
strcat( cmd, " " );
if (quotify_arg) strcat( cmd, "\"");
strcat( cmd, argv[i] );
if (quotify_arg) strcat( cmd, "\"");
if (quote) cmd[n++] = '"';
}
strcat( cmd, " 2>&1"); // capture stderr and stdout
printf("openscad.com: running command: %s\n", cmd );
memcpy(&cmd[n], redirect_str, sizeof(redirect_str)); // including \0
cmd_stdout = _popen( cmd, "rt" );
if ( cmd_stdout == NULL ) {
printf( "Error opening _popen for command: %s", cmd );
perror( "Error message:" );
fprintf(stderr, "Error opening _popen for command: %s", cmd );
perror( "Error message" );
return 1;
}
while ( !eof )
{
fgets_result = fgets( buffer, BUFFSIZE, cmd_stdout );
if ( fgets_result == NULL ) {
for(;;) {
if (NULL == fgets(buffer, BUFFSIZE, cmd_stdout)) {
if ( ferror( cmd_stdout ) ) {
printf("Error reading from stdout of %s\n", cmd);
fprintf(stderr, "Error reading from stdout of %s\n", cmd);
result = 1;
}
if ( feof( cmd_stdout ) ) {
eof = 1;
break;
}
} else {
fprintf( stdout, "%s", buffer );
fputs(buffer, stdout);
}
}
pclose_result = _pclose( cmd_stdout );
if ( pclose_result < 0 ) {
perror("Error while closing stdout for command:");
// perror() applicable with return value of -1 only!
// Avoid stupid "Error: No Error" message
if (pclose_result == -1) {
perror("Error while closing stdout for command");
result = 1;
} else if (!result) {
result = WIFEXITED(pclose_result) ? WEXITSTATUS(pclose_result) : 1;
}
return result;
}

View File

@ -124,3 +124,22 @@ o rm cascade*.scad
o Verify that no rerendering was triggered (the 4 objects are still there)
o ./cascade2.sh
o Verify that everything reloads at once without flickering
Test 15: Correct handling of compile errors in auto-reloaded modules
--------
o Turn on Automatic Reload and Compile
o Open mainusingerror.scad
o Verify that you get:
- Compiling library '.../error.scad'.
- Parser error in line 3: syntax error
- WARNING: Failed to compile library '.../error.scad'.
- Main file should keep compiling
o Verify that the above doesn't repeat
Test 16: Dependency tracking of underlying dependencies
--------
o Turn on Automatic Reload and Compile
o Open mainsubsub.scad
o Verify that you see a red cylinder
o edit subdit/subsub.scad: Change color
o Verify that color changes

View File

@ -0,0 +1,2 @@
use <subdir/sub.scad>
sub();

View File

@ -0,0 +1,3 @@
//mainusingerror.scad
echo(version());
use <error.scad>

View File

@ -1,7 +1,7 @@
use <mymodule-lib.scad>
module mymodule() {
sphere();
sphere(5);
}
mymodule();

View File

@ -1,3 +1,3 @@
module mymodule() {
cylinder(center=true);
cylinder(r=5, center=true);
}

View File

@ -0,0 +1,5 @@
use <subsub.scad>
module sub() {
subsub();
}

View File

@ -0,0 +1,3 @@
module subsub() {
color("red") cylinder(r = 10, h = 5);
}

6
testdata/scad/bugs/empty-stl.scad vendored Normal file
View File

@ -0,0 +1,6 @@
// openscad should exit normally.
// it should not crash and/or freeze and/or gobble RAM
import("../../stl/empty.stl");
import("../../stl/empty2.stl");
echo("empty stl test ok");

14
testdata/scad/bugs/issue112.scad vendored Normal file
View File

@ -0,0 +1,14 @@
module donut() {
rotate_extrude(convexity=2)
translate([5,0,0])
difference() {
circle(r=2);
circle(r=1);
}
}
difference()
{
donut();
translate([-10,-10,0]) cube(10);
}

View File

@ -1,4 +1,8 @@
// The inner cube won't render correctly in OpenCSG mode as long as this bug is present
// Note: This causes a different bug in unstable:
// If we render a preview first, the render() node will be cached as a PolySet. This will
// cause the same problems as in issue495.scad. If we clear cache and render using CGAL,
// it doesn't trigger the bug since we stay in CGAL all the time
difference() {
render(convexity=2) difference() {
cube(20, center = true);

8
testdata/scad/bugs/issue495a.scad vendored Normal file
View File

@ -0,0 +1,8 @@
// Hollow cube.
// STL export is wrong (inner cube is positive instead of negative)
// This is fixed by applying and using the patch to CGAL containing
// convert_all_inner_shells_to_polyhedron()
difference() {
cube(20, center = true);
cube(10, center = true);
}

View File

@ -1,4 +1,6 @@
// In CGAL mode, there should be a cavity
// The problem appears to be that we don't create a correct Nef Polyhedron from
// the polyset, probably already being wrong when converting to a Polyhedron
difference() {
polyhedron(convexity=2, faces=[[2,1,0],[3,1,2],[4,0,1],[4,1,5],[4,2,0],[6,2,4],[6,3,2],[7,3,6],[5,1,3],[5,3,7],[6,4,5],[6,5,7],[9,10,8],[9,11,10],[8,12,9],[9,12,13],[10,12,8],[10,14,12],[11,14,10],[11,15,14],[9,13,11],[11,13,15],[12,14,13],[13,14,15]],
points=[[-5,-5,-5],[-5,-5,5],[-5,5,-5],[-5,5,5],[5,-5,-5],[5,-5,5],[5,5,-5],[5,5,5],[-2.5,-2.5,-2.5],[-2.5,-2.5,2.5],[-2.5,2.5,-2.5],[-2.5,2.5,2.5],[2.5,-2.5,-2.5],[2.5,-2.5,2.5],[2.5,2.5,-2.5],[2.5,2.5,2.5]]);

16
testdata/scad/bugs/issue666.scad vendored Normal file
View File

@ -0,0 +1,16 @@
// >2 objects which don't all intersect should be empty
intersection() {
translate([0,0,0]) cube(5, center=true);
translate([4,0,0]) cube(5, center=true);
translate([8,00,]) cube(5, center=true);
translate([12,0,0]) cube(5, center=true);
}
// Subtracting something from empty intersection should be empty
translate([0,-12,0]) difference() {
intersection() {
cube();
translate([2,0,0]) cube();
}
cylinder(r=4, h=10, center=true);
}

View File

@ -0,0 +1,17 @@
echo(cross([2, 3, 4], [5, 6, 7]));
echo(cross([2, 1, -3], [0, 4, 5]));
echo(cross([2, 1, -3], cross([2, 3, 4], [5, 6, 7])));
echo(cross([2, 0/0, -3], [0, 4, 5]));
echo(cross([2, 1/0, -3], [0, 4, 5]));
echo(cross([2, -1/0, -3], [0, 4, 5]));
echo(cross(0));
echo(cross(0, 1));
echo(cross(0, "a"));
echo(cross(0, 1, 3));
echo(cross([0], [1]));
echo(cross([1, 2, 3], [1, 2]));
echo(cross([1, 2, 3], [1, 2, "a"]));
echo(cross([1, 2, 3], [1, 2, [0]]));
echo(cross([1, 2, 3], [1, 2, [1:2]]));

27
testdata/scad/functions/norm-tests.scad vendored Normal file
View File

@ -0,0 +1,27 @@
u=undef;
echo(norm([]));
echo(norm([1]));
echo(norm([1,2]));
echo(norm([1,2,3]));
echo(norm([1,2,3,4]));
echo(norm());
echo(norm([1,2,0/0]));
echo(norm([1,2,1/0]));
echo(norm([1,2,-1/0]));
echo(norm(""));
echo(norm("abcd"));
echo(norm(true));
echo(norm([1:4]));
echo(norm([1, 2, "a"]));
echo(norm([1, 2, []]));
echo(norm([1, 2, [1]]));
echo(norm([1, 2, [1:3]]));
echo(norm([[1,2,3,4],[1,2,3],[1,2],[1]]));
echo(norm(u));
echo(norm(u, u));
echo(norm(a, a));

4
testdata/scad/misc/override.scad vendored Normal file
View File

@ -0,0 +1,4 @@
// Used to test variable override with the -D parameter
a = 1;
b = 2;
echo(a,b);

View File

@ -14,14 +14,14 @@ special_module(23, $fn=5);
echo("$children scope");
module child_module_2() {
echo("$children should be 4: ", $children);
for(i=[0:$children-1]) child(i);
for(i=[0:$children-1]) children(i);
}
module child_module_1() {
echo("$children should be 1: ", $children);
child_module_2() {
echo("$children should be 1: ", $children);
child(0);
children(0);
echo("child_module_2 child 0");
echo("child_module_2 child 1");
}

0
testdata/stl/empty.stl vendored Normal file
View File

3
testdata/stl/empty2.stl vendored Normal file
View File

@ -0,0 +1,3 @@
solid OpenSCAD_Model
endsolid OpenSCAD_Model

View File

@ -559,6 +559,8 @@ set(CORE_SOURCES
../src/fileutils.cc
../src/progress.cc
../src/boost-utils.cc
../src/PlatformUtils.cc
../src/${PLATFORMUTILS_SOURCE}
${FLEX_OpenSCADlexer_OUTPUTS}
${BISON_OpenSCADparser_OUTPUTS})
@ -605,8 +607,6 @@ set(OFFSCREEN_SOURCES
../src/ThrownTogetherRenderer.cc
../src/renderer.cc
../src/render.cc
../src/PlatformUtils.cc
../src/${PLATFORMUTILS_SOURCE}
../src/OpenCSGRenderer.cc)
if(NULLGL)
@ -626,7 +626,7 @@ endif()
add_library(tests-core STATIC ${CORE_SOURCES})
target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} )
set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} )
set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} ${COCOA_LIBRARY})
add_library(tests-common STATIC ${COMMON_SOURCES})
target_link_libraries(tests-common tests-core)
@ -672,7 +672,7 @@ target_link_libraries(csgtexttest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
#
add_executable(cgalcachetest cgalcachetest.cc)
set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${CLIPPER_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY})
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY})
#
# openscad no-qt
@ -847,7 +847,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${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/range-tests.scad)
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/range-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/empty-stl.scad)
list(APPEND DUMPTEST_FILES ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
@ -911,7 +912,9 @@ list(APPEND BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue495.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue585.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue591.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue593.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue612.scad)
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue612.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue112.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue666.scad)
list(APPEND OPENCSGTEST_FILES ${BUGS_FILES})
list(APPEND CGALPNGTEST_FILES ${BUGS_FILES})
@ -1023,7 +1026,18 @@ add_cmdline_test(openscad-nonascii EXE ${OPENSCAD_BINPATH} ARGS -o
FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/sfære.scad)
# Image output
# Variable override (-D arg)
# FIXME - this breaks on older cmake that is very common 'in the wild' on linux
# Override simple variable
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.10)
add_cmdline_test(openscad-override EXE ${OPENSCAD_BINPATH}
ARGS -D a=3$<SEMICOLON> -o
SUFFIX echo
FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/override.scad)
endif()
# Image output parameters
add_cmdline_test(openscad-imgsize EXE ${OPENSCAD_BINPATH}
ARGS --imgsize 100,100 -o
SUFFIX png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,27 @@
ECHO: [-3, 6, -3]
ECHO: [17, -10, 8]
ECHO: [15, 15, 15]
WARNING: Invalid value (NaN) in parameter vector for cross()
ECHO: undef
WARNING: Invalid value (INF) in parameter vector for cross()
ECHO: undef
WARNING: Invalid value (INF) in parameter vector for cross()
ECHO: undef
WARNING: Invalid number of parameters for cross()
ECHO: undef
WARNING: Invalid type of parameters for cross()
ECHO: undef
WARNING: Invalid type of parameters for cross()
ECHO: undef
WARNING: Invalid number of parameters for cross()
ECHO: undef
WARNING: Invalid vector size of parameter for cross()
ECHO: undef
WARNING: Invalid vector size of parameter for cross()
ECHO: undef
WARNING: Invalid value in parameter vector for cross()
ECHO: undef
WARNING: Invalid value in parameter vector for cross()
ECHO: undef
WARNING: Invalid value in parameter vector for cross()
ECHO: undef

View File

@ -0,0 +1 @@
ECHO: "empty stl test ok"

View File

@ -0,0 +1,26 @@
ECHO: 0
ECHO: 1
ECHO: 2.2360679775
ECHO: 3.74165738677
ECHO: 5.47722557505
ECHO: undef
ECHO: nan
ECHO: inf
ECHO: inf
ECHO: undef
ECHO: undef
ECHO: undef
ECHO: undef
WARNING: Incorrect arguments to norm()
ECHO: undef
WARNING: Incorrect arguments to norm()
ECHO: undef
WARNING: Incorrect arguments to norm()
ECHO: undef
WARNING: Incorrect arguments to norm()
ECHO: undef
WARNING: Incorrect arguments to norm()
ECHO: undef
ECHO: undef
ECHO: undef
ECHO: undef

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1 @@
ECHO: 3, 2