Merge branch 'vector-concat' of git://github.com/t-paul/openscad into t-paul-vector-concat

Conflicts:
	src/Preferences.ui
export-menu
Marius Kintel 2014-01-03 13:17:58 -05:00
commit bee5233a91
18 changed files with 483 additions and 18 deletions

2
.gitignore vendored
View File

@ -12,4 +12,6 @@ parser_yacc.h
/tmp
/OpenSCAD.app
*/#*#
/nbproject
/openscad
/tests/openscad_nogui

3
icons/license.txt Normal file
View File

@ -0,0 +1,3 @@
Taken from http://tango.freedesktop.org/Tango_Icon_Library, version 0.8.90 / public domain:
- prefsFeatures.png (converted from preferences-system.svg)

BIN
icons/prefsFeatures.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -222,6 +222,7 @@ HEADERS += src/typedefs.h \
src/highlighter.h \
src/localscope.h \
src/module.h \
src/feature.h \
src/node.h \
src/csgnode.h \
src/linearextrudenode.h \
@ -280,6 +281,7 @@ SOURCES += src/version_check.cc \
src/func.cc \
src/localscope.cc \
src/module.cc \
src/feature.cc \
src/node.cc \
src/context.cc \
src/modcontext.cc \

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>icons/prefsFeatures.png</file>
<file>icons/stopbutton.png</file>
<file>icons/prefsAdvanced.png</file>
<file>icons/prefs3DView.png</file>

View File

@ -33,12 +33,16 @@
#include <QStatusBar>
#include "PolySetCache.h"
#include "AutoUpdater.h"
#include "feature.h"
#ifdef ENABLE_CGAL
#include "CGALCache.h"
#endif
Preferences *Preferences::instance = NULL;
const char * Preferences::featurePropertyName = "FeatureProperty";
Q_DECLARE_METATYPE(Feature *);
Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
@ -89,10 +93,11 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
// Toolbar
QActionGroup *group = new QActionGroup(this);
group->addAction(prefsAction3DView);
group->addAction(prefsActionEditor);
group->addAction(prefsActionUpdate);
group->addAction(prefsActionAdvanced);
addPrefPage(group, prefsAction3DView, page3DView);
addPrefPage(group, prefsActionEditor, pageEditor);
addPrefPage(group, prefsActionUpdate, pageUpdate);
addPrefPage(group, prefsActionFeatures, pageFeatures);
addPrefPage(group, prefsActionAdvanced, pageAdvanced);
connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)));
prefsAction3DView->setChecked(true);
@ -140,6 +145,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
this->polysetCacheSizeEdit->setValidator(validator);
this->opencsgLimitEdit->setValidator(validator);
setupFeaturesPage();
updateGUI();
RenderSettings::inst()->setColors(this->colorschemes[getValue("3dview/colorscheme").toString()]);
@ -150,21 +156,103 @@ Preferences::~Preferences()
removeDefaultSettings();
}
/**
* Add a page for the preferences GUI. This handles both the action grouping
* and the registration of the widget for each action to have a generalized
* callback to switch pages.
*
* @param group The action group for all page actions. This one will have the
* callback attached after creating all actions/pages.
* @param action The action specific for the added page.
* @param widget The widget that should be shown when the action is triggered.
* This must be a child page of the stackedWidget.
*/
void
Preferences::addPrefPage(QActionGroup *group, QAction *action, QWidget *widget)
{
group->addAction(action);
prefPages[action] = widget;
}
/**
* Callback to switch pages in the preferences GUI.
*
* @param action The action triggered by the user.
*/
void
Preferences::actionTriggered(QAction *action)
{
if (action == this->prefsAction3DView) {
this->stackedWidget->setCurrentWidget(this->page3DView);
this->stackedWidget->setCurrentWidget(prefPages[action]);
}
/**
* Callback for the dynamically created checkboxes on the features
* page. The specific Feature object is associated as property with
* the callback.
*
* @param state the state of the checkbox.
*/
void Preferences::featuresCheckBoxToggled(bool state)
{
const QObject *sender = QObject::sender();
if (sender == NULL) {
return;
}
else if (action == this->prefsActionEditor) {
this->stackedWidget->setCurrentWidget(this->pageEditor);
QVariant v = sender->property(featurePropertyName);
if (!v.isValid()) {
return;
}
else if (action == this->prefsActionUpdate) {
this->stackedWidget->setCurrentWidget(this->pageUpdate);
}
else if (action == this->prefsActionAdvanced) {
this->stackedWidget->setCurrentWidget(this->pageAdvanced);
Feature *feature = v.value<Feature *>();
feature->enable(state);
QSettings settings;
settings.setValue(QString("feature/%1").arg(QString::fromStdString(feature->get_name())), state);
}
/**
* Setup feature GUI and synchronize the Qt settings with the feature values.
*
* In case a feature was enabled on the commandline this will have precedence
* and cause the checkbox in the settings GUI to be not editable.
* Otherwise the value from the Qt settings is pushed into the feature state
* and the checkbox is initialized accordingly.
*/
void
Preferences::setupFeaturesPage()
{
int row = 0;
for (Feature::iterator it = Feature::begin();it != Feature::end();it++) {
Feature *feature = *it;
QString featurekey = QString("feature/%1").arg(QString::fromStdString(feature->get_name()));
this->defaultmap[featurekey] = false;
// spacer item between the features, just for some optical separation
gridLayoutExperimentalFeatures->addItem(new QSpacerItem(1, 8, QSizePolicy::Expanding, QSizePolicy::Fixed), row, 1, 1, 1, Qt::AlignCenter);
row++;
QCheckBox *cb = new QCheckBox(QString::fromStdString(feature->get_name()), pageFeatures);
QFont bold_font(cb->font());
bold_font.setBold(true);
cb->setFont(bold_font);
// synchronize Qt settings with the feature settings
bool value = getValue(featurekey).toBool();
feature->enable(value);
cb->setChecked(value);
cb->setProperty(featurePropertyName, QVariant::fromValue<Feature *>(feature));
connect(cb, SIGNAL(toggled(bool)), this, SLOT(featuresCheckBoxToggled(bool)));
gridLayoutExperimentalFeatures->addWidget(cb, row, 0, 1, 2, Qt::AlignLeading);
row++;
QLabel *l = new QLabel(QString::fromStdString(feature->get_description()), pageFeatures);
l->setTextFormat(Qt::RichText);
gridLayoutExperimentalFeatures->addWidget(l, row, 1, 1, 1, Qt::AlignLeading);
row++;
}
// Force fixed indentation, the checkboxes use column span of 2 so
// first row is not constrained in size by the visible controls. The
// fixed size space essentially gives the first row the width of the
// spacer item itself.
gridLayoutExperimentalFeatures->addItem(new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, 1, Qt::AlignLeading);
}
void Preferences::on_colorSchemeChooser_itemSelectionChanged()
@ -316,7 +404,6 @@ QVariant Preferences::getValue(const QString &key) const
void Preferences::updateGUI()
{
QSettings settings;
QList<QListWidgetItem *> found =
this->colorSchemeChooser->findItems(getValue("3dview/colorscheme").toString(),
Qt::MatchExactly);

View File

@ -21,6 +21,7 @@ public:
public slots:
void actionTriggered(class QAction *);
void featuresCheckBoxToggled(bool);
void on_colorSchemeChooser_itemSelectionChanged();
void on_fontChooser_activated(const QString &);
void on_fontSize_editTextChanged(const QString &);
@ -46,11 +47,15 @@ private:
void keyPressEvent(QKeyEvent *e);
void updateGUI();
void removeDefaultSettings();
void setupFeaturesPage();
void addPrefPage(QActionGroup *group, QAction *action, QWidget *widget);
QSettings::SettingsMap defaultmap;
QHash<QString, std::map<RenderSettings::RenderColor, Color4f> > colorschemes;
QHash<const QAction *, QWidget *> prefPages;
static Preferences *instance;
static const char *featurePropertyName;
};
#endif

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>320</height>
<width>823</width>
<height>433</height>
</rect>
</property>
<property name="sizePolicy">
@ -378,6 +378,78 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="pageFeatures">
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QLabel" name="label_9">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Features</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>803</width>
<height>311</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<layout class="QGridLayout" name="gridLayoutExperimentalFeatures" rowminimumheight="0">
<property name="spacing">
<number>8</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>282</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageAdvanced">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
@ -520,6 +592,7 @@
<addaction name="prefsAction3DView"/>
<addaction name="prefsActionEditor"/>
<addaction name="prefsActionUpdate"/>
<addaction name="prefsActionFeatures"/>
<addaction name="prefsActionAdvanced"/>
</widget>
<action name="prefsAction3DView">
@ -570,6 +643,21 @@
<string>Update</string>
</property>
</action>
<action name="prefsActionFeatures">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/icons/prefsFeatures.png</normaloff>:/icons/prefsFeatures.png</iconset>
</property>
<property name="text">
<string>Features</string>
</property>
<property name="toolTip">
<string>Enable/Disable experimental features</string>
</property>
</action>
</widget>
<resources>
<include location="../openscad.qrc"/>

79
src/feature.cc Normal file
View File

@ -0,0 +1,79 @@
#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
#include "feature.h"
#include "printutils.h"
/**
* Feature registration map/list for later lookup. This must be initialized
* before the static feature instances as those register with this map.
*/
Feature::map_t Feature::feature_map;
Feature::list_t Feature::feature_list;
/*
* List of features, the names given here are used in both command line
* argument to enable the option and for saving the option value in GUI
* context.
*/
const Feature Feature::ExperimentalConcatFunction("concat", "Enable the <code>concat()</code> function.");
Feature::Feature(const std::string &name, const std::string &description)
: enabled(false), name(name), description(description)
{
feature_map[name] = this;
feature_list.push_back(this);
}
Feature::~Feature()
{
}
const std::string &Feature::get_name() const
{
return name;
}
const std::string &Feature::get_description() const
{
return description;
}
bool Feature::is_enabled() const
{
return enabled;
}
void Feature::enable(bool status)
{
enabled = status;
}
void Feature::enable_feature(const std::string &feature_name, bool status)
{
map_t::iterator it = feature_map.find(feature_name);
if (it != feature_map.end()) {
it->second->enable(status);
} else {
PRINTB("WARNING: Ignoring request to enable unknown feature '%s'.", feature_name);
}
}
Feature::iterator Feature::begin()
{
return feature_list.begin();
}
Feature::iterator Feature::end()
{
return feature_list.end();
}
void Feature::dump_features()
{
for (map_t::iterator it = feature_map.begin(); it != feature_map.end(); it++) {
std::cout << "Feature('" << it->first << "') = " << (it->second->is_enabled() ? "enabled" : "disabled") << std::endl;
}
}

44
src/feature.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef FEATURE_H_
#define FEATURE_H_
#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
class Feature
{
public:
typedef std::vector<Feature *> list_t;
typedef list_t::iterator iterator;
static const Feature ExperimentalConcatFunction;
const std::string& get_name() const;
const std::string& get_description() const;
bool is_enabled() const;
void enable(bool status);
static iterator begin();
static iterator end();
static void dump_features();
static void enable_feature(const std::string &feature_name, bool status = true);
private:
bool enabled;
const std::string name;
const std::string description;
typedef std::map<std::string, Feature *> map_t;
static map_t feature_map;
static list_t feature_list;
Feature(const std::string &name, const std::string &description);
virtual ~Feature();
};
#endif

View File

@ -343,6 +343,24 @@ Value builtin_str(const Context *, const EvalContext *evalctx)
return Value(stream.str());
}
Value builtin_concat(const Context *, const EvalContext *evalctx)
{
Value::VectorType result;
for (size_t i = 0; i < evalctx->numArgs(); i++) {
const Value v = evalctx->getArgValue(i);
if (v.type() == Value::VECTOR) {
Value::VectorType vec = v.toVector();
for (Value::VectorType::const_iterator it = vec.begin(); it != vec.end(); it++) {
result.push_back(*it);
}
} else {
result.push_back(v);
}
}
return Value(result);
}
Value builtin_lookup(const Context *, const EvalContext *evalctx)
{
double p, low_p, low_v, high_p, high_v;
@ -604,6 +622,7 @@ void register_builtin_functions()
Builtins::init("log", new BuiltinFunction(&builtin_log));
Builtins::init("ln", new BuiltinFunction(&builtin_ln));
Builtins::init("str", new BuiltinFunction(&builtin_str));
Builtins::init("concat", new BuiltinFunction(&builtin_concat, Feature::ExperimentalConcatFunction));
Builtins::init("lookup", new BuiltinFunction(&builtin_lookup));
Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version));

View File

@ -3,13 +3,21 @@
#include "value.h"
#include "typedefs.h"
#include "feature.h"
#include <string>
#include <vector>
class AbstractFunction
{
private:
const Feature *feature;
public:
AbstractFunction() : feature(NULL) {}
AbstractFunction(const Feature& feature) : feature(&feature) {}
virtual ~AbstractFunction();
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); };
virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
};
@ -21,6 +29,7 @@ public:
eval_func_t eval_func;
BuiltinFunction(eval_func_t f) : eval_func(f) { }
BuiltinFunction(eval_func_t f, const Feature& feature) : AbstractFunction(feature), eval_func(f) { }
virtual ~BuiltinFunction();
virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const;

View File

@ -95,7 +95,12 @@ void ModuleContext::registerBuiltin()
const AbstractFunction *ModuleContext::findLocalFunction(const std::string &name) const
{
if (this->functions_p && this->functions_p->find(name) != this->functions_p->end()) {
return this->functions_p->find(name)->second;
AbstractFunction *f = this->functions_p->find(name)->second;
if (!f->is_enabled()) {
PRINTB("WARNING: Experimental builtin function '%s' is not enabled.", name);
return NULL;
}
return f;
}
return NULL;
}
@ -104,6 +109,10 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co
{
if (this->modules_p && this->modules_p->find(name) != this->modules_p->end()) {
AbstractModule *m = this->modules_p->find(name)->second;
if (!m->is_enabled()) {
PRINTB("WARNING: Experimental builtin module '%s' is not enabled.", name);
return NULL;
}
std::string replacement = Builtins::instance()->isDeprecated(name);
if (!replacement.empty()) {
PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement);

View File

@ -13,6 +13,7 @@
#include "value.h"
#include "typedefs.h"
#include "localscope.h"
#include "feature.h"
class ModuleInstantiation
{
@ -60,8 +61,13 @@ public:
class AbstractModule
{
private:
const Feature *feature;
public:
AbstractModule() : feature(NULL) {}
AbstractModule(const Feature& feature) : feature(&feature) {}
virtual ~AbstractModule();
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); };
virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
};
@ -70,6 +76,7 @@ class Module : public AbstractModule
{
public:
Module() { }
Module(const Feature& feature) : AbstractModule(feature) { }
virtual ~Module();
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const;

View File

@ -33,6 +33,7 @@
#include "builtin.h"
#include "printutils.h"
#include "handle_dep.h"
#include "feature.h"
#include "parsersettings.h"
#include "rendersettings.h"
#include "PlatformUtils.h"
@ -111,6 +112,7 @@ static void help(const char *progname)
"%2% --camera=eyex,y,z,centerx,y,z ] \\\n"
"%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n"
"%2%[ --render | --preview[=throwntogether] ] \\\n"
"%2%[ --enable=<feature> \\\n"
"%2%filename\n",
progname % (const char *)tabstr);
exit(1);
@ -587,7 +589,8 @@ int main(int argc, char **argv)
("x,x", po::value<string>(), "dxf-file")
("d,d", po::value<string>(), "deps-file")
("m,m", po::value<string>(), "makefile")
("D,D", po::value<vector<string> >(), "var=val");
("D,D", po::value<vector<string> >(), "var=val")
("enable", po::value<vector<string> >(), "enable experimental features");
po::options_description hidden("Hidden options");
hidden.add_options()
@ -651,6 +654,11 @@ int main(int argc, char **argv)
commandline_commands += ";\n";
}
}
if (vm.count("enable")) {
BOOST_FOREACH(const string &feature, vm["enable"].as<vector<string> >()) {
Feature::enable_feature(feature);
}
}
vector<string> inputFiles;
if (vm.count("input-file")) {
inputFiles = vm["input-file"].as<vector<string> >();

View File

@ -0,0 +1,53 @@
u = undef;
echo("--- empty");
echo(concat());
echo(concat([]));
echo(concat([], []));
echo(concat([], [], []));
echo("--- single elements");
echo(concat(u));
echo(concat(true));
echo(concat(3));
echo(concat("abc"));
echo(concat([0:1:10]));
echo("--- single vectors");
echo(concat([1, 2, 3]));
echo(concat([[1, 2, 3]]));
echo(concat([[[1, 2, 3]]]));
echo(concat([[[1, 2, [3, 4], 5]]]));
echo("--- multiple elements");
echo(concat(3, 3));
echo(concat(1, 2, 3));
echo(concat(1, 2, 3, 4, 5));
echo(concat(1, "text", false, [1:0.5:3]));
echo("--- vector / element");
echo(concat([3, 4], u));
echo(concat([3, 4, 5], 6));
echo(concat([3, 4, 5, 6], true));
echo(concat([3, 4, "5", 6], "test"));
echo(concat([3, 4, true, 6], [4:1:3]));
echo("--- element / vector");
echo(concat(3, []));
echo(concat(3, [3, 4]));
echo(concat(true, [3, [4]]));
echo(concat("9", [1, 2, 3]));
echo(concat([6:2:9], [3, [4]]));
echo("--- vector / vector");
echo(concat([], [3, 4]));
echo(concat([[]], [3, 4]));
echo(concat([[2, 4]], [3, 4]));
echo(concat([5, 6], ["d", [3, 4]]));
echo(concat([[1, 0, 0], [2, 0, 0]], [3, 0, 0]));
echo(concat([[1, 0, 0], [2, 0, 0]], [[3, 0, 0]]));
echo(concat([[1, 0, 0], [2, 0, 0], [3, 0, 0]], [[4, 4, 4], [5, 5, 5]]));
echo("--- recursive function");
function r(i) = i > 0 ? concat(r(i - 1), [[i, i * i]]) : [];
echo(r(10));

View File

@ -517,6 +517,7 @@ set(CORE_SOURCES
../src/context.cc
../src/modcontext.cc
../src/evalcontext.cc
../src/feature.cc
../src/csgterm.cc
../src/csgtermnormalizer.cc
../src/polyset.cc
@ -990,6 +991,11 @@ add_cmdline_test(throwntogethertest EXE ${OPENSCAD_BINPATH} ARGS --preview=throw
# with anything. It's self-contained and returns != 0 on error
add_cmdline_test(cgalstlsanitytest EXE ${CMAKE_SOURCE_DIR}/cgalstlsanitytest SUFFIX txt ARGS ${OPENSCAD_BINPATH} FILES ${CGALSTLSANITYTEST_FILES})
# Add experimental tests
add_cmdline_test(echotest EXE ${OPENSCAD_BINPATH} ARGS --enable=concat -o SUFFIX echo FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/experimental/concat-tests.scad)
# Tests using the actual OpenSCAD binary
# non-ASCII filenames

View File

@ -0,0 +1,43 @@
ECHO: "--- empty"
ECHO: []
ECHO: []
ECHO: []
ECHO: []
ECHO: "--- single elements"
ECHO: [undef]
ECHO: [true]
ECHO: [3]
ECHO: ["abc"]
ECHO: [[0 : 1 : 10]]
ECHO: "--- single vectors"
ECHO: [1, 2, 3]
ECHO: [[1, 2, 3]]
ECHO: [[[1, 2, 3]]]
ECHO: [[[1, 2, [3, 4], 5]]]
ECHO: "--- multiple elements"
ECHO: [3, 3]
ECHO: [1, 2, 3]
ECHO: [1, 2, 3, 4, 5]
ECHO: [1, "text", false, [1 : 0.5 : 3]]
ECHO: "--- vector / element"
ECHO: [3, 4, undef]
ECHO: [3, 4, 5, 6]
ECHO: [3, 4, 5, 6, true]
ECHO: [3, 4, "5", 6, "test"]
ECHO: [3, 4, true, 6, [4 : 1 : 3]]
ECHO: "--- element / vector"
ECHO: [3]
ECHO: [3, 3, 4]
ECHO: [true, 3, [4]]
ECHO: ["9", 1, 2, 3]
ECHO: [[6 : 2 : 9], 3, [4]]
ECHO: "--- vector / vector"
ECHO: [3, 4]
ECHO: [[], 3, 4]
ECHO: [[2, 4], 3, 4]
ECHO: [5, 6, "d", [3, 4]]
ECHO: [[1, 0, 0], [2, 0, 0], 3, 0, 0]
ECHO: [[1, 0, 0], [2, 0, 0], [3, 0, 0]]
ECHO: [[1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 4, 4], [5, 5, 5]]
ECHO: "--- recursive function"
ECHO: [[1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81], [10, 100]]