Merge pull request #1432 from openscad/issue1407
Fix #1407 by storing vector values as heap objects (ValuePtr) rather than stack objects(Value)
This commit is contained in:
commit
7f9e7f9c83
18 changed files with 324 additions and 292 deletions
|
|
@ -32,6 +32,7 @@
|
|||
#include <QSettings>
|
||||
#include <QStatusBar>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include "GeometryCache.h"
|
||||
#include "AutoUpdater.h"
|
||||
#include "feature.h"
|
||||
|
|
@ -49,7 +50,7 @@ Q_DECLARE_METATYPE(Feature *);
|
|||
class SettingsReader : public Settings::Visitor
|
||||
{
|
||||
QSettings settings;
|
||||
const Value getValue(const Settings::SettingsEntry& entry, const std::string& value) const {
|
||||
Value getValue(const Settings::SettingsEntry& entry, const std::string& value) const {
|
||||
std::string trimmed_value(value);
|
||||
boost::trim(trimmed_value);
|
||||
|
||||
|
|
@ -101,7 +102,7 @@ class SettingsWriter : public Settings::Visitor
|
|||
settings.remove(key);
|
||||
PRINTDB("SettingsWriter D: %s", key.toStdString().c_str());
|
||||
} else {
|
||||
Value value = s->get(entry);
|
||||
const Value &value = s->get(entry);
|
||||
settings.setValue(key, QString::fromStdString(value.toString()));
|
||||
PRINTDB("SettingsWriter W: %s = '%s'", key.toStdString().c_str() % value.toString().c_str());
|
||||
}
|
||||
|
|
@ -689,18 +690,17 @@ void Preferences::updateGUI()
|
|||
void Preferences::initComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry)
|
||||
{
|
||||
comboBox->clear();
|
||||
Value::VectorType vector = entry.range().toVector();
|
||||
for (Value::VectorType::iterator it = vector.begin();it != vector.end();it++) {
|
||||
QString val = QString::fromStdString((*it)[0].toString());
|
||||
std::string text((*it)[1].toString());
|
||||
QString qtext = QString::fromStdString(gettext(text.c_str()));
|
||||
// Range is a vector of 2D vectors: [[name, value], ...]
|
||||
BOOST_FOREACH(const ValuePtr &v, entry.range().toVector()) {
|
||||
QString val = QString::fromStdString(v[0]->toString());
|
||||
QString qtext = QString::fromStdString(gettext(v[1]->toString().c_str()));
|
||||
comboBox->addItem(qtext, val);
|
||||
}
|
||||
}
|
||||
|
||||
void Preferences::initSpinBox(QSpinBox *spinBox, const Settings::SettingsEntry& entry)
|
||||
{
|
||||
Value::RangeType range = entry.range().toRange();
|
||||
RangeType range = entry.range().toRange();
|
||||
spinBox->setMinimum(range.begin_value());
|
||||
spinBox->setMaximum(range.end_value());
|
||||
}
|
||||
|
|
@ -709,13 +709,13 @@ void Preferences::updateComboBox(QComboBox *comboBox, const Settings::SettingsEn
|
|||
{
|
||||
Settings::Settings *s = Settings::Settings::inst();
|
||||
|
||||
Value value = s->get(entry);
|
||||
const Value &value = s->get(entry);
|
||||
QString text = QString::fromStdString(value.toString());
|
||||
int idx = comboBox->findData(text);
|
||||
if (idx >= 0) {
|
||||
comboBox->setCurrentIndex(idx);
|
||||
} else {
|
||||
Value defaultValue = entry.defaultValue();
|
||||
const Value &defaultValue = entry.defaultValue();
|
||||
QString defaultText = QString::fromStdString(defaultValue.toString());
|
||||
int defIdx = comboBox->findData(defaultText);
|
||||
if (defIdx >= 0) {
|
||||
|
|
|
|||
|
|
@ -101,9 +101,9 @@ Builtins::Builtins()
|
|||
this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr<Expression>(new ExpressionConst(ValuePtr(0.0)))));
|
||||
|
||||
Value::VectorType zero3;
|
||||
zero3.push_back(Value(0.0));
|
||||
zero3.push_back(Value(0.0));
|
||||
zero3.push_back(Value(0.0));
|
||||
zero3.push_back(ValuePtr(0.0));
|
||||
zero3.push_back(ValuePtr(0.0));
|
||||
zero3.push_back(ValuePtr(0.0));
|
||||
ValuePtr zero3val(zero3);
|
||||
this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr<Expression>(new ExpressionConst(zero3val))));
|
||||
this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr<Expression>(new ExpressionConst(zero3val))));
|
||||
|
|
|
|||
|
|
@ -89,17 +89,17 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant
|
|||
node->newsize << 0,0,0;
|
||||
if ( ns->type() == Value::VECTOR ) {
|
||||
const Value::VectorType &vs = ns->toVector();
|
||||
if ( vs.size() >= 1 ) node->newsize[0] = vs[0].toDouble();
|
||||
if ( vs.size() >= 2 ) node->newsize[1] = vs[1].toDouble();
|
||||
if ( vs.size() >= 3 ) node->newsize[2] = vs[2].toDouble();
|
||||
if ( vs.size() >= 1 ) node->newsize[0] = vs[0]->toDouble();
|
||||
if ( vs.size() >= 2 ) node->newsize[1] = vs[1]->toDouble();
|
||||
if ( vs.size() >= 3 ) node->newsize[2] = vs[2]->toDouble();
|
||||
}
|
||||
ValuePtr autosize = c.lookup_variable("auto");
|
||||
node->autosize << false, false, false;
|
||||
if ( autosize->type() == Value::VECTOR ) {
|
||||
const Value::VectorType &va = autosize->toVector();
|
||||
if ( va.size() >= 1 ) node->autosize[0] = va[0].toBool();
|
||||
if ( va.size() >= 2 ) node->autosize[1] = va[1].toBool();
|
||||
if ( va.size() >= 3 ) node->autosize[2] = va[2].toBool();
|
||||
if ( va.size() >= 1 ) node->autosize[0] = va[0]->toBool();
|
||||
if ( va.size() >= 2 ) node->autosize[1] = va[1]->toBool();
|
||||
if ( va.size() >= 3 ) node->autosize[2] = va[2]->toBool();
|
||||
}
|
||||
else if ( autosize->type() == Value::BOOL ) {
|
||||
node->autosize << autosize->toBool(),autosize->toBool(),autosize->toBool();
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia
|
|||
ValuePtr v = c.lookup_variable("c");
|
||||
if (v->type() == Value::VECTOR) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
node->color[i] = i < v->toVector().size() ? v->toVector()[i].toDouble() : 1.0;
|
||||
node->color[i] = i < v->toVector().size() ? v->toVector()[i]->toDouble() : 1.0;
|
||||
if (node->color[i] > 1)
|
||||
PRINTB_NOCACHE("WARNING: color() expects numbers between 0.0 and 1.0. Value of %.1f is too large.", node->color[i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public: // methods
|
|||
|
||||
static const EvalContext* getLastModuleCtx(const EvalContext *evalctx);
|
||||
|
||||
static AbstractNode* getChild(const Value &value, const EvalContext* modulectx);
|
||||
static AbstractNode* getChild(const ValuePtr &value, const EvalContext* modulectx);
|
||||
|
||||
private: // data
|
||||
Type type;
|
||||
|
|
@ -78,15 +78,15 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst
|
|||
ValuePtr it_values = evalctx->getArgValue(l, ctx);
|
||||
Context c(ctx);
|
||||
if (it_values->type() == Value::RANGE) {
|
||||
Value::RangeType range = it_values->toRange();
|
||||
boost::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, ValuePtr(*it));
|
||||
for_eval(node, inst, l+1, &c, evalctx);
|
||||
}
|
||||
RangeType range = it_values->toRange();
|
||||
boost::uint32_t steps = range.nbsteps();
|
||||
if (steps >= 10000) {
|
||||
PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps);
|
||||
} else {
|
||||
for (RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
c.set_variable(it_name, ValuePtr(*it));
|
||||
for_eval(node, inst, l+1, &c, evalctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (it_values->type() == Value::VECTOR) {
|
||||
|
|
@ -133,17 +133,17 @@ const EvalContext* ControlModule::getLastModuleCtx(const EvalContext *evalctx)
|
|||
}
|
||||
|
||||
// static
|
||||
AbstractNode* ControlModule::getChild(const Value& value, const EvalContext* modulectx)
|
||||
AbstractNode* ControlModule::getChild(const ValuePtr &value, const EvalContext* modulectx)
|
||||
{
|
||||
if (value.type()!=Value::NUMBER) {
|
||||
if (value->type()!=Value::NUMBER) {
|
||||
// Invalid parameter
|
||||
// (e.g. first child of difference is invalid)
|
||||
PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value.toString());
|
||||
PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value->toString());
|
||||
return NULL;
|
||||
}
|
||||
double v;
|
||||
if (!value.getDouble(v)) {
|
||||
PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value.toString());
|
||||
if (!value->getDouble(v)) {
|
||||
PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value->toString());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -223,12 +223,12 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
|
|||
// one (or more ignored) parameter
|
||||
ValuePtr value = evalctx->getArgValue(0);
|
||||
if (value->type() == Value::NUMBER) {
|
||||
return getChild(*value, modulectx);
|
||||
return getChild(value, modulectx);
|
||||
}
|
||||
else if (value->type() == Value::VECTOR) {
|
||||
AbstractNode* node = new AbstractNode(inst);
|
||||
const Value::VectorType& vect = value->toVector();
|
||||
foreach (const Value::VectorType::value_type& vectvalue, vect) {
|
||||
foreach (const ValuePtr &vectvalue, vect) {
|
||||
AbstractNode* childnode = getChild(vectvalue,modulectx);
|
||||
if (childnode==NULL) continue; // error
|
||||
node->children.push_back(childnode);
|
||||
|
|
@ -236,15 +236,15 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns
|
|||
return node;
|
||||
}
|
||||
else if (value->type() == Value::RANGE) {
|
||||
Value::RangeType range = value->toRange();
|
||||
RangeType range = value->toRange();
|
||||
boost::uint32_t steps = range.nbsteps();
|
||||
if (steps >= 10000) {
|
||||
PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps);
|
||||
return NULL;
|
||||
}
|
||||
AbstractNode* node = new AbstractNode(inst);
|
||||
for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
AbstractNode* childnode = getChild(Value(*it),modulectx); // with error cases
|
||||
for (RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
AbstractNode* childnode = getChild(ValuePtr(*it),modulectx); // with error cases
|
||||
if (childnode==NULL) continue; // error
|
||||
node->children.push_back(childnode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,8 +200,8 @@ ValuePtr builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx)
|
|||
double x = x1 + ua*(x2 - x1);
|
||||
double y = y1 + ua*(y2 - y1);
|
||||
Value::VectorType ret;
|
||||
ret.push_back(Value(x));
|
||||
ret.push_back(Value(y));
|
||||
ret.push_back(ValuePtr(x));
|
||||
ret.push_back(ValuePtr(y));
|
||||
return dxf_cross_cache[key] = ValuePtr(ret);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
src/expr.cc
27
src/expr.cc
|
|
@ -41,12 +41,12 @@ namespace {
|
|||
Value::VectorType flatten(Value::VectorType const& vec) {
|
||||
int n = 0;
|
||||
for (unsigned int i = 0; i < vec.size(); i++) {
|
||||
assert(vec[i].type() == Value::VECTOR);
|
||||
n += vec[i].toVector().size();
|
||||
assert(vec[i]->type() == Value::VECTOR);
|
||||
n += vec[i]->toVector().size();
|
||||
}
|
||||
Value::VectorType ret; ret.reserve(n);
|
||||
for (unsigned int i = 0; i < vec.size(); i++) {
|
||||
std::copy(vec[i].toVector().begin(),vec[i].toVector().end(),std::back_inserter(ret));
|
||||
std::copy(vec[i]->toVector().begin(),vec[i]->toVector().end(),std::back_inserter(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -357,7 +357,7 @@ ExpressionConst::ExpressionConst(const ValuePtr &val) : const_value(val)
|
|||
|
||||
ValuePtr ExpressionConst::evaluate(const class Context *) const
|
||||
{
|
||||
return ValuePtr(this->const_value);
|
||||
return this->const_value;
|
||||
}
|
||||
|
||||
void ExpressionConst::print(std::ostream &stream) const
|
||||
|
|
@ -380,12 +380,12 @@ ValuePtr ExpressionRange::evaluate(const Context *context) const
|
|||
ValuePtr v2 = this->second->evaluate(context);
|
||||
if (v2->type() == Value::NUMBER) {
|
||||
if (this->children.size() == 2) {
|
||||
Value::RangeType range(v1->toDouble(), v2->toDouble());
|
||||
RangeType range(v1->toDouble(), v2->toDouble());
|
||||
return ValuePtr(range);
|
||||
} else {
|
||||
ValuePtr v3 = this->third->evaluate(context);
|
||||
if (v3->type() == Value::NUMBER) {
|
||||
Value::RangeType range(v1->toDouble(), v2->toDouble(), v3->toDouble());
|
||||
RangeType range(v1->toDouble(), v2->toDouble(), v3->toDouble());
|
||||
return ValuePtr(range);
|
||||
}
|
||||
}
|
||||
|
|
@ -409,7 +409,8 @@ ValuePtr ExpressionVector::evaluate(const Context *context) const
|
|||
{
|
||||
Value::VectorType vec;
|
||||
BOOST_FOREACH(const Expression *e, this->children) {
|
||||
vec.push_back(*(e->evaluate(context)));
|
||||
ValuePtr tmpval = e->evaluate(context);
|
||||
vec.push_back(tmpval);
|
||||
}
|
||||
return ValuePtr(vec);
|
||||
}
|
||||
|
|
@ -545,7 +546,7 @@ ValuePtr ExpressionLc::evaluate(const Context *context) const
|
|||
if (this->second->isListComprehension()) {
|
||||
return this->second->evaluate(context);
|
||||
} else {
|
||||
vec.push_back((*this->second->evaluate(context)));
|
||||
vec.push_back(this->second->evaluate(context));
|
||||
}
|
||||
}
|
||||
return ValuePtr(vec);
|
||||
|
|
@ -561,26 +562,26 @@ ValuePtr ExpressionLc::evaluate(const Context *context) const
|
|||
Context c(context);
|
||||
|
||||
if (it_values->type() == Value::RANGE) {
|
||||
Value::RangeType range = it_values->toRange();
|
||||
RangeType range = it_values->toRange();
|
||||
boost::uint32_t steps = range.nbsteps();
|
||||
if (steps >= 1000000) {
|
||||
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++) {
|
||||
for (RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
c.set_variable(it_name, ValuePtr(*it));
|
||||
vec.push_back((*this->first->evaluate(&c)));
|
||||
vec.push_back(this->first->evaluate(&c));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (it_values->type() == Value::VECTOR) {
|
||||
for (size_t i = 0; i < it_values->toVector().size(); i++) {
|
||||
c.set_variable(it_name, it_values->toVector()[i]);
|
||||
vec.push_back((*this->first->evaluate(&c)));
|
||||
vec.push_back(this->first->evaluate(&c));
|
||||
}
|
||||
}
|
||||
else if (it_values->type() != Value::UNDEFINED) {
|
||||
c.set_variable(it_name, it_values);
|
||||
vec.push_back((*this->first->evaluate(&c)));
|
||||
vec.push_back(this->first->evaluate(&c));
|
||||
}
|
||||
if (this->first->isListComprehension()) {
|
||||
return ValuePtr(flatten(vec));
|
||||
|
|
|
|||
101
src/func.cc
101
src/func.cc
|
|
@ -258,14 +258,14 @@ ValuePtr builtin_rands(const Context *, const EvalContext *evalctx)
|
|||
Value::VectorType vec;
|
||||
if (min==max) { // Boost doesn't allow min == max
|
||||
for (size_t i=0; i < numresults; i++)
|
||||
vec.push_back( Value( min ) );
|
||||
vec.push_back(ValuePtr(min));
|
||||
} else {
|
||||
boost::uniform_real<> distributor( min, max );
|
||||
for (size_t i=0; i < numresults; i++) {
|
||||
if ( deterministic ) {
|
||||
vec.push_back(Value(distributor(deterministic_rng)));
|
||||
vec.push_back(ValuePtr(distributor(deterministic_rng)));
|
||||
} else {
|
||||
vec.push_back(Value(distributor(lessdeterministic_rng)));
|
||||
vec.push_back(ValuePtr(distributor(lessdeterministic_rng)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -285,11 +285,11 @@ ValuePtr builtin_min(const Context *, const EvalContext *evalctx)
|
|||
ValuePtr v0 = evalctx->getArgValue(0);
|
||||
|
||||
if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) {
|
||||
Value min = v0->toVector()[0];
|
||||
ValuePtr min = v0->toVector()[0];
|
||||
for (size_t i = 1; i < v0->toVector().size(); i++) {
|
||||
if (v0->toVector()[i] < min) min = v0->toVector()[i];
|
||||
}
|
||||
return ValuePtr(min);
|
||||
return min;
|
||||
}
|
||||
if (v0->type() == Value::NUMBER) {
|
||||
double val = v0->toDouble();
|
||||
|
|
@ -317,11 +317,11 @@ ValuePtr builtin_max(const Context *, const EvalContext *evalctx)
|
|||
ValuePtr v0 = evalctx->getArgValue(0);
|
||||
|
||||
if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) {
|
||||
Value max = v0->toVector()[0];
|
||||
ValuePtr max = v0->toVector()[0];
|
||||
for (size_t i = 1; i < v0->toVector().size(); i++) {
|
||||
if (v0->toVector()[i] > max) max = v0->toVector()[i];
|
||||
}
|
||||
return ValuePtr(max);
|
||||
return max;
|
||||
}
|
||||
if (v0->type() == Value::NUMBER) {
|
||||
double val = v0->toDouble();
|
||||
|
|
@ -614,14 +614,13 @@ ValuePtr builtin_concat(const Context *, const EvalContext *evalctx)
|
|||
Value::VectorType result;
|
||||
|
||||
for (size_t i = 0; i < evalctx->numArgs(); i++) {
|
||||
ValuePtr 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);
|
||||
ValuePtr val = evalctx->getArgValue(i);
|
||||
if (val->type() == Value::VECTOR) {
|
||||
BOOST_FOREACH(const ValuePtr &v, val->toVector()) {
|
||||
result.push_back(v);
|
||||
}
|
||||
} else {
|
||||
result.push_back(*v);
|
||||
result.push_back(val);
|
||||
}
|
||||
}
|
||||
return ValuePtr(result);
|
||||
|
|
@ -636,13 +635,13 @@ ValuePtr builtin_lookup(const Context *, const EvalContext *evalctx)
|
|||
|
||||
ValuePtr v1 = evalctx->getArgValue(1);
|
||||
const Value::VectorType &vec = v1->toVector();
|
||||
if (vec[0].toVector().size() < 2) // Second must be a vector of vectors
|
||||
if (vec[0]->toVector().size() < 2) // Second must be a vector of vectors
|
||||
return ValuePtr::undefined;
|
||||
if (!vec[0].getVec2(low_p, low_v) || !vec[0].getVec2(high_p, high_v))
|
||||
if (!vec[0]->getVec2(low_p, low_v) || !vec[0]->getVec2(high_p, high_v))
|
||||
return ValuePtr::undefined;
|
||||
for (size_t i = 1; i < vec.size(); i++) {
|
||||
double this_p, this_v;
|
||||
if (vec[i].getVec2(this_p, this_v)) {
|
||||
if (vec[i]->getVec2(this_p, this_v)) {
|
||||
if (this_p <= p && (this_p > low_p || low_p > p)) {
|
||||
low_p = this_p;
|
||||
low_v = this_v;
|
||||
|
|
@ -727,10 +726,10 @@ static Value::VectorType search(const std::string &find, const std::string &tabl
|
|||
if (ptr_ft && ptr_st && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
|
||||
matchCount++;
|
||||
if (num_returns_per_match == 1) {
|
||||
returnvec.push_back(Value(double(j)));
|
||||
returnvec.push_back(ValuePtr(double(j)));
|
||||
break;
|
||||
} else {
|
||||
resultvec.push_back(Value(double(j)));
|
||||
resultvec.push_back(ValuePtr(double(j)));
|
||||
}
|
||||
if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) {
|
||||
break;
|
||||
|
|
@ -742,7 +741,7 @@ static Value::VectorType search(const std::string &find, const std::string &tabl
|
|||
if (ptr_ft) g_utf8_strncpy(utf8_of_cp, ptr_ft, 1);
|
||||
}
|
||||
if (num_returns_per_match == 0 || num_returns_per_match > 1) {
|
||||
returnvec.push_back(Value(resultvec));
|
||||
returnvec.push_back(ValuePtr(resultvec));
|
||||
}
|
||||
}
|
||||
return returnvec;
|
||||
|
|
@ -760,19 +759,19 @@ static Value::VectorType search(const std::string &find, const Value::VectorType
|
|||
Value::VectorType resultvec;
|
||||
const gchar *ptr_ft = g_utf8_offset_to_pointer(find.c_str(), i);
|
||||
for (size_t j = 0; j < searchTableSize; j++) {
|
||||
Value::VectorType entryVec = table[j].toVector();
|
||||
const Value::VectorType &entryVec = table[j]->toVector();
|
||||
if (entryVec.size() <= index_col_num) {
|
||||
PRINTB("WARNING: Invalid entry in search vector at index %d, required number of values in the entry: %d. Invalid entry: %s", j % (index_col_num + 1) % table[j]);
|
||||
return Value::VectorType();
|
||||
}
|
||||
const gchar *ptr_st = g_utf8_offset_to_pointer(entryVec[index_col_num].toString().c_str(), 0);
|
||||
const gchar *ptr_st = g_utf8_offset_to_pointer(entryVec[index_col_num]->toString().c_str(), 0);
|
||||
if (ptr_ft && ptr_st && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
|
||||
matchCount++;
|
||||
if (num_returns_per_match == 1) {
|
||||
returnvec.push_back(Value(double(j)));
|
||||
returnvec.push_back(ValuePtr(double(j)));
|
||||
break;
|
||||
} else {
|
||||
resultvec.push_back(Value(double(j)));
|
||||
resultvec.push_back(ValuePtr(double(j)));
|
||||
}
|
||||
if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) {
|
||||
break;
|
||||
|
|
@ -785,7 +784,7 @@ static Value::VectorType search(const std::string &find, const Value::VectorType
|
|||
PRINTB(" WARNING: search term not found: \"%s\"", utf8_of_cp);
|
||||
}
|
||||
if (num_returns_per_match == 0 || num_returns_per_match > 1) {
|
||||
returnvec.push_back(Value(resultvec));
|
||||
returnvec.push_back(ValuePtr(resultvec));
|
||||
}
|
||||
}
|
||||
return returnvec;
|
||||
|
|
@ -806,12 +805,12 @@ ValuePtr builtin_search(const Context *, const EvalContext *evalctx)
|
|||
unsigned int matchCount = 0;
|
||||
|
||||
for (size_t j = 0; j < searchTable->toVector().size(); j++) {
|
||||
const Value &search_element = searchTable->toVector()[j];
|
||||
const ValuePtr &search_element = searchTable->toVector()[j];
|
||||
|
||||
if ((index_col_num == 0 && *findThis == search_element) ||
|
||||
(index_col_num < search_element.toVector().size() &&
|
||||
*findThis == search_element.toVector()[index_col_num])) {
|
||||
returnvec.push_back(Value(double(j)));
|
||||
if ((index_col_num == 0 && findThis == search_element) ||
|
||||
(index_col_num < search_element->toVector().size() &&
|
||||
findThis == search_element->toVector()[index_col_num])) {
|
||||
returnvec.push_back(ValuePtr(double(j)));
|
||||
matchCount++;
|
||||
if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break;
|
||||
}
|
||||
|
|
@ -828,16 +827,16 @@ ValuePtr builtin_search(const Context *, const EvalContext *evalctx)
|
|||
unsigned int matchCount = 0;
|
||||
Value::VectorType resultvec;
|
||||
|
||||
Value const& find_value = findThis->toVector()[i];
|
||||
const ValuePtr &find_value = findThis->toVector()[i];
|
||||
|
||||
for (size_t j = 0; j < searchTable->toVector().size(); j++) {
|
||||
|
||||
Value const& search_element = searchTable->toVector()[j];
|
||||
const ValuePtr &search_element = searchTable->toVector()[j];
|
||||
|
||||
if ((index_col_num == 0 && find_value == search_element) ||
|
||||
(index_col_num < search_element.toVector().size() &&
|
||||
find_value == search_element.toVector()[index_col_num])) {
|
||||
Value resultValue((double(j)));
|
||||
(index_col_num < search_element->toVector().size() &&
|
||||
find_value == search_element->toVector()[index_col_num])) {
|
||||
ValuePtr resultValue((double(j)));
|
||||
matchCount++;
|
||||
if (num_returns_per_match == 1) {
|
||||
returnvec.push_back(resultValue);
|
||||
|
|
@ -849,10 +848,10 @@ ValuePtr builtin_search(const Context *, const EvalContext *evalctx)
|
|||
}
|
||||
}
|
||||
if (num_returns_per_match == 1 && matchCount == 0) {
|
||||
returnvec.push_back(resultvec);
|
||||
returnvec.push_back(ValuePtr(resultvec));
|
||||
}
|
||||
if (num_returns_per_match == 0 || num_returns_per_match > 1) {
|
||||
returnvec.push_back(resultvec);
|
||||
returnvec.push_back(ValuePtr(resultvec));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -919,12 +918,12 @@ ValuePtr builtin_norm(const Context *, const EvalContext *evalctx)
|
|||
ValuePtr val = evalctx->getArgValue(0);
|
||||
if (val->type() == Value::VECTOR) {
|
||||
double sum = 0;
|
||||
Value::VectorType v = val->toVector();
|
||||
const Value::VectorType &v = val->toVector();
|
||||
size_t n = v.size();
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (v[i].type() == Value::NUMBER) {
|
||||
if (v[i]->type() == Value::NUMBER) {
|
||||
// sum += pow(v[i].toDouble(),2);
|
||||
register double x = v[i].toDouble();
|
||||
register double x = v[i]->toDouble();
|
||||
sum += x*x;
|
||||
} else {
|
||||
PRINT("WARNING: Incorrect arguments to norm()");
|
||||
|
|
@ -950,10 +949,10 @@ ValuePtr builtin_cross(const Context *, const EvalContext *evalctx)
|
|||
return ValuePtr::undefined;
|
||||
}
|
||||
|
||||
Value::VectorType v0 = arg0->toVector();
|
||||
Value::VectorType v1 = arg1->toVector();
|
||||
const Value::VectorType &v0 = arg0->toVector();
|
||||
const Value::VectorType &v1 = arg1->toVector();
|
||||
if ((v0.size() == 2) && (v1.size() == 2)) {
|
||||
return ValuePtr(Value(v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble()));
|
||||
return ValuePtr(v0[0]->toDouble() * v1[1]->toDouble() - v0[1]->toDouble() * v1[0]->toDouble());
|
||||
}
|
||||
|
||||
if ((v0.size() != 3) || (v1.size() != 3)) {
|
||||
|
|
@ -961,12 +960,12 @@ ValuePtr builtin_cross(const Context *, const EvalContext *evalctx)
|
|||
return ValuePtr::undefined;
|
||||
}
|
||||
for (unsigned int a = 0;a < 3;a++) {
|
||||
if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) {
|
||||
if ((v0[a]->type() != Value::NUMBER) || (v1[a]->type() != Value::NUMBER)) {
|
||||
PRINT("WARNING: Invalid value in parameter vector for cross()");
|
||||
return ValuePtr::undefined;
|
||||
}
|
||||
double d0 = v0[a].toDouble();
|
||||
double d1 = v1[a].toDouble();
|
||||
double d0 = v0[a]->toDouble();
|
||||
double d1 = v1[a]->toDouble();
|
||||
if (boost::math::isnan(d0) || boost::math::isnan(d1)) {
|
||||
PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()");
|
||||
return ValuePtr::undefined;
|
||||
|
|
@ -977,14 +976,14 @@ ValuePtr builtin_cross(const Context *, const EvalContext *evalctx)
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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));
|
||||
result.push_back(ValuePtr(x));
|
||||
result.push_back(ValuePtr(y));
|
||||
result.push_back(ValuePtr(z));
|
||||
return ValuePtr(result);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1604,15 +1604,15 @@ void MainWindow::updateTemporalVariables()
|
|||
this->top_ctx.set_variable("$t", ValuePtr(this->anim_tval));
|
||||
|
||||
Value::VectorType vpt;
|
||||
vpt.push_back(Value(-qglview->cam.object_trans.x()));
|
||||
vpt.push_back(Value(-qglview->cam.object_trans.y()));
|
||||
vpt.push_back(Value(-qglview->cam.object_trans.z()));
|
||||
this->top_ctx.set_variable("$vpt", Value(vpt));
|
||||
vpt.push_back(ValuePtr(-qglview->cam.object_trans.x()));
|
||||
vpt.push_back(ValuePtr(-qglview->cam.object_trans.y()));
|
||||
vpt.push_back(ValuePtr(-qglview->cam.object_trans.z()));
|
||||
this->top_ctx.set_variable("$vpt", ValuePtr(vpt));
|
||||
|
||||
Value::VectorType vpr;
|
||||
vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360)));
|
||||
vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360)));
|
||||
vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.z(), 360)));
|
||||
vpr.push_back(ValuePtr(fmodf(360 - qglview->cam.object_rot.x() + 90, 360)));
|
||||
vpr.push_back(ValuePtr(fmodf(360 - qglview->cam.object_rot.y(), 360)));
|
||||
vpr.push_back(ValuePtr(fmodf(360 - qglview->cam.object_rot.z(), 360)));
|
||||
top_ctx.set_variable("$vpr", ValuePtr(vpr));
|
||||
|
||||
top_ctx.set_variable("$vpd", ValuePtr(qglview->cam.zoomValue()));
|
||||
|
|
|
|||
|
|
@ -508,12 +508,12 @@ Geometry *PrimitiveNode::createGeometry() const
|
|||
for (size_t i=0; i<this->faces->toVector().size(); i++)
|
||||
{
|
||||
p->append_poly();
|
||||
const Value::VectorType &vec = this->faces->toVector()[i].toVector();
|
||||
const Value::VectorType &vec = this->faces->toVector()[i]->toVector();
|
||||
for (size_t j=0; j<vec.size(); j++) {
|
||||
size_t pt = vec[j].toDouble();
|
||||
size_t pt = vec[j]->toDouble();
|
||||
if (pt < this->points->toVector().size()) {
|
||||
double px, py, pz;
|
||||
if (!this->points->toVector()[pt].getVec3(px, py, pz) ||
|
||||
if (!this->points->toVector()[pt]->getVec3(px, py, pz) ||
|
||||
isinf(px) || isinf(py) || isinf(pz)) {
|
||||
PRINTB("ERROR: Unable to convert point at index %d to a vec3 of numbers", j);
|
||||
return p;
|
||||
|
|
@ -572,7 +572,7 @@ Geometry *PrimitiveNode::createGeometry() const
|
|||
double x,y;
|
||||
const Value::VectorType &vec = this->points->toVector();
|
||||
for (unsigned int i=0;i<vec.size();i++) {
|
||||
const Value &val = vec[i];
|
||||
const Value &val = *vec[i];
|
||||
if (!val.getVec2(x, y) || isinf(x) || isinf(y)) {
|
||||
PRINTB("ERROR: Unable to convert point %s at index %d to a vec2 of numbers",
|
||||
val.toString() % i);
|
||||
|
|
@ -585,10 +585,10 @@ Geometry *PrimitiveNode::createGeometry() const
|
|||
p->addOutline(outline);
|
||||
}
|
||||
else {
|
||||
BOOST_FOREACH(const Value &polygon, this->paths->toVector()) {
|
||||
BOOST_FOREACH(const ValuePtr &polygon, this->paths->toVector()) {
|
||||
Outline2d curroutline;
|
||||
BOOST_FOREACH(const Value &index, polygon.toVector()) {
|
||||
unsigned int idx = index.toDouble();
|
||||
BOOST_FOREACH(const ValuePtr &index, polygon->toVector()) {
|
||||
unsigned int idx = index->toDouble();
|
||||
if (idx < outline.vertices.size()) {
|
||||
curroutline.vertices.push_back(outline.vertices[idx]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,25 +45,25 @@ bool SettingsEntry::is_default() const
|
|||
|
||||
static Value value(std::string s1, std::string s2) {
|
||||
Value::VectorType v;
|
||||
v += Value(s1), Value(s2);
|
||||
v += ValuePtr(s1), ValuePtr(s2);
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp) {
|
||||
Value::VectorType v;
|
||||
v += value(s1, s1disp), value(s2, s2disp);
|
||||
v += ValuePtr(value(s1, s1disp)), ValuePtr(value(s2, s2disp));
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp, std::string s3, std::string s3disp) {
|
||||
Value::VectorType v;
|
||||
v += value(s1, s1disp), value(s2, s2disp), value(s3, s3disp);
|
||||
v += ValuePtr(value(s1, s1disp)), ValuePtr(value(s2, s2disp)), ValuePtr(value(s3, s3disp));
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value values(std::string s1, std::string s1disp, std::string s2, std::string s2disp, std::string s3, std::string s3disp, std::string s4, std::string s4disp) {
|
||||
Value::VectorType v;
|
||||
v += value(s1, s1disp), value(s2, s2disp), value(s3, s3disp), value(s4, s4disp);
|
||||
v += ValuePtr(value(s1, s1disp)), ValuePtr(value(s2, s2disp)), ValuePtr(value(s3, s3disp)), ValuePtr(value(s4, s4disp));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
@ -94,12 +94,17 @@ void Settings::visit(Visitor& visitor)
|
|||
}
|
||||
}
|
||||
|
||||
Value Settings::get(const SettingsEntry& entry)
|
||||
const Value &Settings::defaultValue(const SettingsEntry& entry)
|
||||
{
|
||||
return entry._default;
|
||||
}
|
||||
|
||||
const Value &Settings::get(const SettingsEntry& entry)
|
||||
{
|
||||
return entry._value;
|
||||
}
|
||||
|
||||
void Settings::set(SettingsEntry& entry, const Value val)
|
||||
void Settings::set(SettingsEntry& entry, const Value &val)
|
||||
{
|
||||
entry._value = val;
|
||||
}
|
||||
|
|
@ -122,15 +127,15 @@ Visitor::~Visitor()
|
|||
* can be translated.
|
||||
*/
|
||||
SettingsEntry Settings::showWarningsIn3dView("3dview", "showWarningsIn3dView", Value(true), Value(true));
|
||||
SettingsEntry Settings::indentationWidth("editor", "indentationWidth", Value(Value::RangeType(1, 16)), Value(4));
|
||||
SettingsEntry Settings::tabWidth("editor", "tabWidth", Value(Value::RangeType(1, 16)), Value(4));
|
||||
SettingsEntry Settings::indentationWidth("editor", "indentationWidth", Value(RangeType(1, 16)), Value(4));
|
||||
SettingsEntry Settings::tabWidth("editor", "tabWidth", Value(RangeType(1, 16)), Value(4));
|
||||
SettingsEntry Settings::lineWrap("editor", "lineWrap", values("None", _("None"), "Char", _("Wrap at character boundaries"), "Word", _("Wrap at word boundaries")), Value("Word"));
|
||||
SettingsEntry Settings::lineWrapIndentationStyle("editor", "lineWrapIndentationStyle", values("Fixed", _("Fixed"), "Same", _("Same"), "Indented", _("Indented")), Value("Fixed"));
|
||||
SettingsEntry Settings::lineWrapIndentation("editor", "lineWrapIndentation", Value(Value::RangeType(0, 999)), Value(4));
|
||||
SettingsEntry Settings::lineWrapIndentation("editor", "lineWrapIndentation", Value(RangeType(0, 999)), Value(4));
|
||||
SettingsEntry Settings::lineWrapVisualizationBegin("editor", "lineWrapVisualizationBegin", values("None", _("None"), "Text", _("Text"), "Border", _("Border"), "Margin", _("Margin")), Value("None"));
|
||||
SettingsEntry Settings::lineWrapVisualizationEnd("editor", "lineWrapVisualizationEnd", values("None", _("None"), "Text", _("Text"), "Border", _("Border"), "Margin", _("Margin")), Value("Border"));
|
||||
SettingsEntry Settings::showWhitespace("editor", "showWhitespaces", values("Never", _("Never"), "Always", _("Always"), "AfterIndentation", _("After indentation")), Value("Never"));
|
||||
SettingsEntry Settings::showWhitespaceSize("editor", "showWhitespacesSize", Value(Value::RangeType(1, 16)), Value(2));
|
||||
SettingsEntry Settings::showWhitespaceSize("editor", "showWhitespacesSize", Value(RangeType(1, 16)), Value(2));
|
||||
SettingsEntry Settings::autoIndent("editor", "autoIndent", Value(true), Value(true));
|
||||
SettingsEntry Settings::indentStyle("editor", "indentStyle", values("Spaces", _("Spaces"), "Tabs", _("Tabs")), Value("Spaces"));
|
||||
SettingsEntry Settings::tabKeyFunction("editor", "tabKeyFunction", values("Indent", _("Indent"), "InsertTab", _("Insert Tab")), Value("Indent"));
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ public:
|
|||
|
||||
void visit(class Visitor& visitor);
|
||||
|
||||
Value defaultValue(const SettingsEntry& entry);
|
||||
Value get(const SettingsEntry& entry);
|
||||
void set(SettingsEntry& entry, const Value val);
|
||||
const Value &defaultValue(const SettingsEntry& entry);
|
||||
const Value &get(const SettingsEntry& entry);
|
||||
void set(SettingsEntry& entry, const Value &val);
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
|
@ -73,4 +73,4 @@ public:
|
|||
virtual void handle(SettingsEntry& entry) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,15 +105,15 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta
|
|||
Eigen::AngleAxisd rotz(0, Vector3d::UnitZ());
|
||||
double a;
|
||||
if (val_a->toVector().size() > 0) {
|
||||
val_a->toVector()[0].getDouble(a);
|
||||
val_a->toVector()[0]->getDouble(a);
|
||||
rotx = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitX());
|
||||
}
|
||||
if (val_a->toVector().size() > 1) {
|
||||
val_a->toVector()[1].getDouble(a);
|
||||
val_a->toVector()[1]->getDouble(a);
|
||||
roty = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitY());
|
||||
}
|
||||
if (val_a->toVector().size() > 2) {
|
||||
val_a->toVector()[2].getDouble(a);
|
||||
val_a->toVector()[2]->getDouble(a);
|
||||
rotz = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitZ());
|
||||
}
|
||||
node->matrix.rotate(rotz * roty * rotx);
|
||||
|
|
@ -171,9 +171,9 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta
|
|||
Matrix4d rawmatrix = Matrix4d::Identity();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
size_t x = i / 4, y = i % 4;
|
||||
if (y < v->toVector().size() && v->toVector()[y].type() ==
|
||||
Value::VECTOR && x < v->toVector()[y].toVector().size())
|
||||
v->toVector()[y].toVector()[x].getDouble(rawmatrix(y, x));
|
||||
if (y < v->toVector().size() && v->toVector()[y]->type() ==
|
||||
Value::VECTOR && x < v->toVector()[y]->toVector().size())
|
||||
v->toVector()[y]->toVector()[x]->getDouble(rawmatrix(y, x));
|
||||
}
|
||||
double w = rawmatrix(3,3);
|
||||
if (w != 1.0) node->matrix = rawmatrix / w;
|
||||
|
|
|
|||
125
src/value.cc
125
src/value.cc
|
|
@ -250,15 +250,19 @@ public:
|
|||
stream << '[';
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
if (i > 0) stream << ", ";
|
||||
stream << v[i];
|
||||
stream << *v[i];
|
||||
}
|
||||
stream << ']';
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string operator()(const Value::RangeType &v) const {
|
||||
std::string operator()(const RangeType &v) const {
|
||||
return (boost::format("[%1% : %2% : %3%]") % v.begin_val % v.step_val % v.end_val).str();
|
||||
}
|
||||
|
||||
std::string operator()(const ValuePtr &v) const {
|
||||
return v->toString();
|
||||
}
|
||||
};
|
||||
|
||||
std::string Value::toString() const
|
||||
|
|
@ -290,12 +294,12 @@ public:
|
|||
{
|
||||
std::stringstream stream;
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
stream << v[i].chrString();
|
||||
stream << v[i]->chrString();
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string operator()(const Value::RangeType &v) const
|
||||
std::string operator()(const RangeType &v) const
|
||||
{
|
||||
const boost::uint32_t steps = v.nbsteps();
|
||||
if (steps >= 10000) {
|
||||
|
|
@ -304,8 +308,8 @@ public:
|
|||
}
|
||||
|
||||
std::stringstream stream;
|
||||
Value::RangeType range = v;
|
||||
for (Value::RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
RangeType range = v;
|
||||
for (RangeType::iterator it = range.begin();it != range.end();it++) {
|
||||
const Value value(*it);
|
||||
stream << value.chrString();
|
||||
}
|
||||
|
|
@ -337,8 +341,8 @@ bool Value::getVec2(double &x, double &y, bool ignoreInfinite) const
|
|||
|
||||
double rx, ry;
|
||||
bool valid = ignoreInfinite
|
||||
? v[0].getFiniteDouble(rx) && v[1].getFiniteDouble(ry)
|
||||
: v[0].getDouble(rx) && v[1].getDouble(ry);
|
||||
? v[0]->getFiniteDouble(rx) && v[1]->getFiniteDouble(ry)
|
||||
: v[0]->getDouble(rx) && v[1]->getDouble(ry);
|
||||
|
||||
if (valid) {
|
||||
x = rx;
|
||||
|
|
@ -363,10 +367,10 @@ bool Value::getVec3(double &x, double &y, double &z, double defaultval) const
|
|||
if (v.size() != 3) return false;
|
||||
}
|
||||
|
||||
return (v[0].getDouble(x) && v[1].getDouble(y) && v[2].getDouble(z));
|
||||
return (v[0]->getDouble(x) && v[1]->getDouble(y) && v[2]->getDouble(z));
|
||||
}
|
||||
|
||||
Value::RangeType Value::toRange() const
|
||||
RangeType Value::toRange() const
|
||||
{
|
||||
const RangeType *val = boost::get<RangeType>(&this->value);
|
||||
if (val) {
|
||||
|
|
@ -473,7 +477,7 @@ public:
|
|||
Value operator()(const Value::VectorType &op1, const Value::VectorType &op2) const {
|
||||
Value::VectorType sum;
|
||||
for (size_t i = 0; i < op1.size() && i < op2.size(); i++) {
|
||||
sum.push_back(op1[i] + op2[i]);
|
||||
sum.push_back(ValuePtr(*op1[i] + *op2[i]));
|
||||
}
|
||||
return Value(sum);
|
||||
}
|
||||
|
|
@ -498,7 +502,7 @@ public:
|
|||
Value operator()(const Value::VectorType &op1, const Value::VectorType &op2) const {
|
||||
Value::VectorType sum;
|
||||
for (size_t i = 0; i < op1.size() && i < op2.size(); i++) {
|
||||
sum.push_back(op1[i] - op2[i]);
|
||||
sum.push_back(ValuePtr(*op1[i] - *op2[i]));
|
||||
}
|
||||
return Value(sum);
|
||||
}
|
||||
|
|
@ -513,8 +517,8 @@ Value Value::multvecnum(const Value &vecval, const Value &numval)
|
|||
{
|
||||
// Vector * Number
|
||||
VectorType dstv;
|
||||
BOOST_FOREACH(const Value &val, vecval.toVector()) {
|
||||
dstv.push_back(val * numval);
|
||||
BOOST_FOREACH(const ValuePtr &val, vecval.toVector()) {
|
||||
dstv.push_back(ValuePtr(*val * numval));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -524,18 +528,18 @@ Value Value::multmatvec(const VectorType &matrixvec, const VectorType &vectorvec
|
|||
// Matrix * Vector
|
||||
VectorType dstv;
|
||||
for (size_t i=0;i<matrixvec.size();i++) {
|
||||
if (matrixvec[i].type() != VECTOR ||
|
||||
matrixvec[i].toVector().size() != vectorvec.size()) {
|
||||
if (matrixvec[i]->type() != VECTOR ||
|
||||
matrixvec[i]->toVector().size() != vectorvec.size()) {
|
||||
return Value();
|
||||
}
|
||||
double r_e = 0.0;
|
||||
for (size_t j=0;j<matrixvec[i].toVector().size();j++) {
|
||||
if (matrixvec[i].toVector()[j].type() != NUMBER || vectorvec[j].type() != NUMBER) {
|
||||
for (size_t j=0;j<matrixvec[i]->toVector().size();j++) {
|
||||
if (matrixvec[i]->toVector()[j]->type() != NUMBER || vectorvec[j]->type() != NUMBER) {
|
||||
return Value();
|
||||
}
|
||||
r_e += matrixvec[i].toVector()[j].toDouble() * vectorvec[j].toDouble();
|
||||
r_e += matrixvec[i]->toVector()[j]->toDouble() * vectorvec[j]->toDouble();
|
||||
}
|
||||
dstv.push_back(Value(r_e));
|
||||
dstv.push_back(ValuePtr(r_e));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -545,17 +549,17 @@ Value Value::multvecmat(const VectorType &vectorvec, const VectorType &matrixvec
|
|||
assert(vectorvec.size() == matrixvec.size());
|
||||
// Vector * Matrix
|
||||
VectorType dstv;
|
||||
for (size_t i=0;i<matrixvec[0].toVector().size();i++) {
|
||||
for (size_t i=0;i<matrixvec[0]->toVector().size();i++) {
|
||||
double r_e = 0.0;
|
||||
for (size_t j=0;j<vectorvec.size();j++) {
|
||||
if (matrixvec[j].type() != VECTOR ||
|
||||
matrixvec[j].toVector()[i].type() != NUMBER ||
|
||||
vectorvec[j].type() != NUMBER) {
|
||||
if (matrixvec[j]->type() != VECTOR ||
|
||||
matrixvec[j]->toVector()[i]->type() != NUMBER ||
|
||||
vectorvec[j]->type() != NUMBER) {
|
||||
return Value::undefined;
|
||||
}
|
||||
r_e += vectorvec[j].toDouble() * matrixvec[j].toVector()[i].toDouble();
|
||||
r_e += vectorvec[j]->toDouble() * matrixvec[j]->toVector()[i]->toDouble();
|
||||
}
|
||||
dstv.push_back(Value(r_e));
|
||||
dstv.push_back(ValuePtr(r_e));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -574,31 +578,31 @@ Value Value::operator*(const Value &v) const
|
|||
else if (this->type() == VECTOR && v.type() == VECTOR) {
|
||||
const VectorType &vec1 = this->toVector();
|
||||
const VectorType &vec2 = v.toVector();
|
||||
if (vec1[0].type() == NUMBER && vec2[0].type() == NUMBER &&
|
||||
if (vec1[0]->type() == NUMBER && vec2[0]->type() == NUMBER &&
|
||||
vec1.size() == vec2.size()) {
|
||||
// Vector dot product.
|
||||
double r = 0.0;
|
||||
for (size_t i=0;i<vec1.size();i++) {
|
||||
if (vec1[i].type() != NUMBER || vec2[i].type() != NUMBER) {
|
||||
if (vec1[i]->type() != NUMBER || vec2[i]->type() != NUMBER) {
|
||||
return Value::undefined;
|
||||
}
|
||||
r += (vec1[i].toDouble() * vec2[i].toDouble());
|
||||
r += (vec1[i]->toDouble() * vec2[i]->toDouble());
|
||||
}
|
||||
return Value(r);
|
||||
} else if (vec1[0].type() == VECTOR && vec2[0].type() == NUMBER &&
|
||||
vec1[0].toVector().size() == vec2.size()) {
|
||||
} else if (vec1[0]->type() == VECTOR && vec2[0]->type() == NUMBER &&
|
||||
vec1[0]->toVector().size() == vec2.size()) {
|
||||
return multmatvec(vec1, vec2);
|
||||
} else if (vec1[0].type() == NUMBER && vec2[0].type() == VECTOR &&
|
||||
} else if (vec1[0]->type() == NUMBER && vec2[0]->type() == VECTOR &&
|
||||
vec1.size() == vec2.size()) {
|
||||
return multvecmat(vec1, vec2);
|
||||
} else if (vec1[0].type() == VECTOR && vec2[0].type() == VECTOR &&
|
||||
vec1[0].toVector().size() == vec2.size()) {
|
||||
} else if (vec1[0]->type() == VECTOR && vec2[0]->type() == VECTOR &&
|
||||
vec1[0]->toVector().size() == vec2.size()) {
|
||||
// Matrix * Matrix
|
||||
VectorType dstv;
|
||||
BOOST_FOREACH(const Value &srcrow, vec1) {
|
||||
const VectorType &srcrowvec = srcrow.toVector();
|
||||
BOOST_FOREACH(const ValuePtr &srcrow, vec1) {
|
||||
const VectorType &srcrowvec = srcrow->toVector();
|
||||
if (srcrowvec.size() != vec2.size()) return Value::undefined;
|
||||
dstv.push_back(multvecmat(srcrowvec, vec2));
|
||||
dstv.push_back(ValuePtr(multvecmat(srcrowvec, vec2)));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -614,16 +618,16 @@ Value Value::operator/(const Value &v) const
|
|||
else if (this->type() == VECTOR && v.type() == NUMBER) {
|
||||
const VectorType &vec = this->toVector();
|
||||
VectorType dstv;
|
||||
BOOST_FOREACH(const Value &vecval, vec) {
|
||||
dstv.push_back(vecval / v);
|
||||
BOOST_FOREACH(const ValuePtr &vecval, vec) {
|
||||
dstv.push_back(ValuePtr(*vecval / v));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
else if (this->type() == NUMBER && v.type() == VECTOR) {
|
||||
const VectorType &vec = v.toVector();
|
||||
VectorType dstv;
|
||||
BOOST_FOREACH(const Value &vecval, vec) {
|
||||
dstv.push_back(*this / vecval);
|
||||
BOOST_FOREACH(const ValuePtr &vecval, vec) {
|
||||
dstv.push_back(ValuePtr(*this / *vecval));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -646,8 +650,8 @@ Value Value::operator-() const
|
|||
else if (this->type() == VECTOR) {
|
||||
const VectorType &vec = this->toVector();
|
||||
VectorType dstv;
|
||||
BOOST_FOREACH(const Value &vecval, vec) {
|
||||
dstv.push_back(-vecval);
|
||||
BOOST_FOREACH(const ValuePtr &vecval, vec) {
|
||||
dstv.push_back(ValuePtr(-*vecval));
|
||||
}
|
||||
return Value(dstv);
|
||||
}
|
||||
|
|
@ -694,11 +698,11 @@ public:
|
|||
|
||||
Value operator()(const Value::VectorType &vec, const double &idx) const {
|
||||
const boost::uint32_t i = convert_to_uint32(idx);
|
||||
if (i < vec.size()) return vec[i];
|
||||
if (i < vec.size()) return *vec[i];
|
||||
return Value::undefined;
|
||||
}
|
||||
|
||||
Value operator()(const Value::RangeType &range, const double &idx) const {
|
||||
Value operator()(const RangeType &range, const double &idx) const {
|
||||
const boost::uint32_t i = convert_to_uint32(idx);
|
||||
switch(i) {
|
||||
case 0: return Value(range.begin_val);
|
||||
|
|
@ -719,14 +723,14 @@ Value Value::operator[](const Value &v) const
|
|||
return boost::apply_visitor(bracket_visitor(), this->value, v.value);
|
||||
}
|
||||
|
||||
void Value::RangeType::normalize() {
|
||||
void RangeType::normalize() {
|
||||
if ((step_val>0) && (end_val < begin_val)) {
|
||||
std::swap(begin_val,end_val);
|
||||
printDeprecation("Using ranges of the form [begin:end] with begin value greater than the end value is deprecated.");
|
||||
}
|
||||
}
|
||||
|
||||
boost::uint32_t Value::RangeType::nbsteps() const {
|
||||
boost::uint32_t RangeType::nbsteps() const {
|
||||
if (boost::math::isnan(step_val) || boost::math::isinf(begin_val) || (boost::math::isinf(end_val))) {
|
||||
return std::numeric_limits<boost::uint32_t>::max();
|
||||
}
|
||||
|
|
@ -755,13 +759,13 @@ boost::uint32_t Value::RangeType::nbsteps() const {
|
|||
return steps;
|
||||
}
|
||||
|
||||
Value::RangeType::iterator::iterator(Value::RangeType &range, type_t type) : range(range), val(range.begin_val)
|
||||
RangeType::iterator::iterator(RangeType &range, type_t type) : range(range), val(range.begin_val)
|
||||
{
|
||||
this->type = type;
|
||||
update_type();
|
||||
}
|
||||
|
||||
void Value::RangeType::iterator::update_type()
|
||||
void RangeType::iterator::update_type()
|
||||
{
|
||||
if (range.step_val == 0) {
|
||||
type = RANGE_TYPE_END;
|
||||
|
|
@ -776,17 +780,17 @@ void Value::RangeType::iterator::update_type()
|
|||
}
|
||||
}
|
||||
|
||||
Value::RangeType::iterator::reference Value::RangeType::iterator::operator*()
|
||||
RangeType::iterator::reference RangeType::iterator::operator*()
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
Value::RangeType::iterator::pointer Value::RangeType::iterator::operator->()
|
||||
RangeType::iterator::pointer RangeType::iterator::operator->()
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++()
|
||||
RangeType::iterator::self_type RangeType::iterator::operator++()
|
||||
{
|
||||
if (type < 0) {
|
||||
type = RANGE_TYPE_RUNNING;
|
||||
|
|
@ -796,14 +800,14 @@ Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++()
|
|||
return *this;
|
||||
}
|
||||
|
||||
Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++(int)
|
||||
RangeType::iterator::self_type RangeType::iterator::operator++(int)
|
||||
{
|
||||
self_type tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool Value::RangeType::iterator::operator==(const self_type &other) const
|
||||
bool RangeType::iterator::operator==(const self_type &other) const
|
||||
{
|
||||
if (type == RANGE_TYPE_RUNNING) {
|
||||
return (type == other.type) && (val == other.val) && (range == other.range);
|
||||
|
|
@ -812,7 +816,7 @@ bool Value::RangeType::iterator::operator==(const self_type &other) const
|
|||
}
|
||||
}
|
||||
|
||||
bool Value::RangeType::iterator::operator!=(const self_type &other) const
|
||||
bool RangeType::iterator::operator!=(const self_type &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
|
@ -862,7 +866,7 @@ ValuePtr::ValuePtr(const Value::VectorType &v)
|
|||
this->reset(new Value(v));
|
||||
}
|
||||
|
||||
ValuePtr::ValuePtr(const Value::RangeType &v)
|
||||
ValuePtr::ValuePtr(const RangeType &v)
|
||||
{
|
||||
this->reset(new Value(v));
|
||||
}
|
||||
|
|
@ -937,3 +941,12 @@ ValuePtr ValuePtr::operator%(const ValuePtr &v) const
|
|||
return ValuePtr(**this % *v);
|
||||
}
|
||||
|
||||
ValuePtr::operator bool() const
|
||||
{
|
||||
return **this;
|
||||
}
|
||||
|
||||
const Value &ValuePtr::operator*() const
|
||||
{
|
||||
return *this->get();
|
||||
}
|
||||
|
|
|
|||
191
src/value.h
191
src/value.h
|
|
@ -29,75 +29,113 @@ public:
|
|||
};
|
||||
std::ostream &operator<<(std::ostream &stream, const Filename &filename);
|
||||
|
||||
class Value
|
||||
{
|
||||
class RangeType {
|
||||
private:
|
||||
double begin_val;
|
||||
double step_val;
|
||||
double end_val;
|
||||
|
||||
/// inverse begin/end if begin is upper than end
|
||||
void normalize();
|
||||
|
||||
public:
|
||||
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;
|
||||
typedef enum { RANGE_TYPE_BEGIN, RANGE_TYPE_RUNNING, RANGE_TYPE_END } type_t;
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
typedef iterator self_type;
|
||||
typedef double value_type;
|
||||
typedef double& reference;
|
||||
typedef double* pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef double difference_type;
|
||||
iterator(RangeType &range, type_t type);
|
||||
self_type operator++();
|
||||
self_type operator++(int junk);
|
||||
reference operator*();
|
||||
pointer operator->();
|
||||
bool operator==(const self_type& other) const;
|
||||
bool operator!=(const self_type& other) const;
|
||||
private:
|
||||
RangeType ⦥
|
||||
double val;
|
||||
type_t type;
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
typedef iterator self_type;
|
||||
typedef double value_type;
|
||||
typedef double& reference;
|
||||
typedef double* pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef double difference_type;
|
||||
iterator(RangeType &range, type_t type);
|
||||
self_type operator++();
|
||||
self_type operator++(int junk);
|
||||
reference operator*();
|
||||
pointer operator->();
|
||||
bool operator==(const self_type& other) const;
|
||||
bool operator!=(const self_type& other) const;
|
||||
private:
|
||||
RangeType ⦥
|
||||
double val;
|
||||
type_t type;
|
||||
|
||||
void update_type();
|
||||
};
|
||||
|
||||
RangeType(double begin, double end)
|
||||
: begin_val(begin), step_val(1.0), end_val(end)
|
||||
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_val(begin), step_val(step), end_val(end) {}
|
||||
|
||||
bool operator==(const RangeType &other) const {
|
||||
return this->begin_val == other.begin_val &&
|
||||
this->step_val == other.step_val &&
|
||||
this->end_val == other.end_val;
|
||||
}
|
||||
|
||||
double begin_value() { return begin_val; }
|
||||
double step_value() { return step_val; }
|
||||
double end_value() { return end_val; }
|
||||
|
||||
iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); }
|
||||
iterator end() { return iterator(*this, RANGE_TYPE_END); }
|
||||
|
||||
/// return number of steps, max uint32_t value if step is 0
|
||||
boost::uint32_t nbsteps() const;
|
||||
|
||||
friend class chr_visitor;
|
||||
friend class tostring_visitor;
|
||||
friend class bracket_visitor;
|
||||
};
|
||||
|
||||
RangeType(double begin, double step, double end)
|
||||
: begin_val(begin), step_val(step), end_val(end) {}
|
||||
class ValuePtr : public shared_ptr<const class Value>
|
||||
{
|
||||
public:
|
||||
static ValuePtr undefined;
|
||||
|
||||
bool operator==(const RangeType &other) const {
|
||||
return this->begin_val == other.begin_val &&
|
||||
this->step_val == other.step_val &&
|
||||
this->end_val == other.end_val;
|
||||
}
|
||||
ValuePtr();
|
||||
explicit ValuePtr(const Value &v);
|
||||
ValuePtr(bool v);
|
||||
ValuePtr(int v);
|
||||
ValuePtr(double v);
|
||||
ValuePtr(const std::string &v);
|
||||
ValuePtr(const char *v);
|
||||
ValuePtr(const char v);
|
||||
ValuePtr(const class std::vector<ValuePtr> &v);
|
||||
ValuePtr(const class RangeType &v);
|
||||
|
||||
double begin_value() { return begin_val; }
|
||||
double step_value() { return step_val; }
|
||||
double end_value() { return end_val; }
|
||||
operator bool() const;
|
||||
|
||||
iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); }
|
||||
iterator end() { return iterator(*this, RANGE_TYPE_END); }
|
||||
bool operator==(const ValuePtr &v) const;
|
||||
bool operator!=(const ValuePtr &v) const;
|
||||
bool operator<(const ValuePtr &v) const;
|
||||
bool operator<=(const ValuePtr &v) const;
|
||||
bool operator>=(const ValuePtr &v) const;
|
||||
bool operator>(const ValuePtr &v) const;
|
||||
ValuePtr operator-() const;
|
||||
ValuePtr operator!() const;
|
||||
ValuePtr operator[](const ValuePtr &v) const;
|
||||
ValuePtr operator+(const ValuePtr &v) const;
|
||||
ValuePtr operator-(const ValuePtr &v) const;
|
||||
ValuePtr operator*(const ValuePtr &v) const;
|
||||
ValuePtr operator/(const ValuePtr &v) const;
|
||||
ValuePtr operator%(const ValuePtr &v) const;
|
||||
|
||||
/// return number of steps, max uint32_t value if step is 0
|
||||
boost::uint32_t nbsteps() const;
|
||||
|
||||
friend class chr_visitor;
|
||||
friend class tostring_visitor;
|
||||
friend class bracket_visitor;
|
||||
};
|
||||
const Value &operator*() const;
|
||||
|
||||
typedef std::vector<Value> VectorType;
|
||||
private:
|
||||
};
|
||||
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
typedef std::vector<ValuePtr> VectorType;
|
||||
|
||||
enum ValueType {
|
||||
UNDEFINED,
|
||||
|
|
@ -169,40 +207,3 @@ private:
|
|||
Variant value;
|
||||
};
|
||||
|
||||
class ValuePtr : public shared_ptr<const Value>
|
||||
{
|
||||
public:
|
||||
static ValuePtr undefined;
|
||||
|
||||
ValuePtr();
|
||||
explicit ValuePtr(const Value &v);
|
||||
ValuePtr(bool v);
|
||||
ValuePtr(int v);
|
||||
ValuePtr(double v);
|
||||
ValuePtr(const std::string &v);
|
||||
ValuePtr(const char *v);
|
||||
ValuePtr(const char v);
|
||||
ValuePtr(const Value::VectorType &v);
|
||||
ValuePtr(const Value::RangeType &v);
|
||||
|
||||
operator bool() const { return **this; }
|
||||
|
||||
bool operator==(const ValuePtr &v) const;
|
||||
bool operator!=(const ValuePtr &v) const;
|
||||
bool operator<(const ValuePtr &v) const;
|
||||
bool operator<=(const ValuePtr &v) const;
|
||||
bool operator>=(const ValuePtr &v) const;
|
||||
bool operator>(const ValuePtr &v) const;
|
||||
ValuePtr operator-() const;
|
||||
ValuePtr operator!() const;
|
||||
ValuePtr operator[](const ValuePtr &v) const;
|
||||
ValuePtr operator+(const ValuePtr &v) const;
|
||||
ValuePtr operator-(const ValuePtr &v) const;
|
||||
ValuePtr operator*(const ValuePtr &v) const;
|
||||
ValuePtr operator/(const ValuePtr &v) const;
|
||||
ValuePtr operator%(const ValuePtr &v) const;
|
||||
|
||||
const Value &operator*() const { return *this->get(); }
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
|||
9
testdata/scad/misc/recursion-test-vector.scad
vendored
Normal file
9
testdata/scad/misc/recursion-test-vector.scad
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Test for recursion crashing when vectors are parameters to a module
|
||||
// See github issue1407
|
||||
|
||||
rec();
|
||||
|
||||
module rec(a=1)
|
||||
{
|
||||
rec([a,10,10]);
|
||||
}
|
||||
|
|
@ -839,7 +839,9 @@ endif()
|
|||
#
|
||||
# GUI binary tests
|
||||
#
|
||||
if(APPLE)
|
||||
if(EXISTS "$ENV{OPENSCAD_BINARY}")
|
||||
set(OPENSCAD_BINPATH "$ENV{OPENSCAD_BINARY}")
|
||||
elseif(APPLE)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../OpenSCAD.app/Contents/MacOS/OpenSCAD")
|
||||
elseif (MINGW_CROSS_ENV_DIR)
|
||||
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../mingw32/release/openscad.exe")
|
||||
|
|
@ -1112,6 +1114,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
|
|||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-test-function.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-test-function2.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-test-module.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-test-vector.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/tail-recursion-tests.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad
|
||||
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
ERROR: Recursion detected calling module 'rec'
|
||||
Loading…
Reference in a new issue