/* * OpenSCAD (www.openscad.org) * Copyright (C) 2009-2011 Clifford Wolf and * Marius Kintel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * As a special exception, you have permission to link this program * with the CGAL library and distribute executables, as long as you * follow the requirements of the GNU GPL in regard to all of the * software in the executable aside from CGAL. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "value.h" #include #include #include #include #include #include #include #include #include std::ostream &operator<<(std::ostream &stream, const Filename &filename) { stream << QuotedString(QDir::current().relativeFilePath(QString::fromStdString(filename)).toStdString()); return stream; } // FIXME: This could probably be done more elegantly using boost::regex std::ostream &operator<<(std::ostream &stream, const QuotedString &s) { stream << '"'; BOOST_FOREACH(char c, s) { switch (c) { case '\t': stream << "\\t"; break; case '\n': stream << "\\n"; break; case '"': case '\\': stream << '\\'; stream << c; break; default: stream << c; } } stream << '"'; return stream; } Value Value::undefined; Value::Value() : value(boost::blank()) { // std::cout << "creating undef\n"; } Value::Value(bool v) : value(v) { // std::cout << "creating bool\n"; } Value::Value(int v) : value(double(v)) { // std::cout << "creating int\n"; } Value::Value(double v) : value(v) { // std::cout << "creating double " << v << "\n"; } Value::Value(const std::string &v) : value(v) { // std::cout << "creating string\n"; } Value::Value(const char *v) : value(std::string(v)) { // std::cout << "creating string from char *\n"; } Value::Value(char v) : value(std::string(1, v)) { // std::cout << "creating string from char\n"; } Value::Value(const VectorType &v) : value(v) { // std::cout << "creating vector\n"; } Value::Value(const RangeType &v) : value(v) { // std::cout << "creating range\n"; } Value::Value(double begin, double step, double end) : value(RangeType(begin, step, end)) { // std::cout << "creating range from numbers\n"; } Value::ValueType Value::type() const { return static_cast(this->value.which()); } bool Value::isUndefined() const { return this->type() == UNDEFINED; } bool Value::toBool() const { switch (this->type()) { case BOOL: return boost::get(this->value); break; case NUMBER: return boost::get(this->value)!= 0; break; case STRING: return boost::get(this->value).size() > 0; break; case VECTOR: return boost::get(this->value).size() > 0; break; case RANGE: return true; break; default: return false; break; } } double Value::toDouble() const { double d = 0; getDouble(d); return d; } bool Value::getDouble(double &v) const { const double *d = boost::get(&this->value); if (d) { v = *d; return true; } return false; } class tostring_visitor : public boost::static_visitor { public: template std::string operator()(const T &op1) const { // std::cout << "[generic tostring_visitor]\n"; return boost::lexical_cast(op1); } std::string operator()(const double &op1) const { #ifdef OPENSCAD_TESTING // Quick and dirty hack to work around floating point rounding differences // across platforms for testing purposes. if (op1 != op1) { // Fix for avoiding nan vs. -nan across platforms return "nan"; } std::stringstream tmp; tmp.precision(12); tmp.setf(std::ios_base::fixed); tmp << op1; std::string tmpstr = tmp.str(); size_t endpos = tmpstr.find_last_not_of('0'); if (endpos >= 0 && tmpstr[endpos] == '.') endpos--; tmpstr = tmpstr.substr(0, endpos+1); size_t dotpos = tmpstr.find('.'); if (dotpos != std::string::npos) { if (tmpstr.size() - dotpos > 12) tmpstr.erase(dotpos + 12); } return tmpstr; #else // attempt to emulate Qt's QString.sprintf("%g"); from old OpenSCAD. // see https://github.com/openscad/openscad/issues/158 std::stringstream tmp; tmp.unsetf(std::ios::floatfield); tmp << op1; return tmp.str(); #endif } std::string operator()(const boost::blank &) const { return "undef"; } std::string operator()(const bool &v) const { return v ? "true" : "false"; } std::string operator()(const Value::VectorType &v) const { std::stringstream stream; stream << '['; for (size_t i = 0; i < v.size(); i++) { if (i > 0) stream << ", "; stream << v[i]; } stream << ']'; return stream.str(); } std::string operator()(const Value::RangeType &v) const { return (boost::format("[%1% : %2% : %3%]") % v.begin % v.step % v.end).str(); } }; std::string Value::toString() const { return boost::apply_visitor(tostring_visitor(), this->value); } const Value::VectorType &Value::toVector() const { static VectorType empty; const VectorType *v = boost::get(&this->value); if (v) return *v; else return empty; } bool Value::getVec2(double &x, double &y) const { if (this->type() != VECTOR) return false; const VectorType &v = toVector(); if (v.size() != 2) return false; return (v[0].getDouble(x) && v[1].getDouble(y)); } bool Value::getVec3(double &x, double &y, double &z, double defaultval) const { if (this->type() != VECTOR) return false; const VectorType &v = toVector(); if (v.size() == 2) { getVec2(x, y); z = defaultval; return true; } else { if (v.size() != 3) return false; } return (v[0].getDouble(x) && v[1].getDouble(y) && v[2].getDouble(z)); } Value::RangeType Value::toRange() const { const RangeType *val = boost::get(&this->value); if (val) { return *val; } else return RangeType(0,0,0); } Value &Value::operator=(const Value &v) { if (this != &v) { this->value = v.value; } return *this; } Value Value::operator!() const { return Value(!this->toBool()); } class equals_visitor : public boost::static_visitor { public: template bool operator()(const T &, const U &) const { return false; } template bool operator()(const T &op1, const T &op2) const { return op1 == op2; } }; bool Value::operator==(const Value &v) const { return boost::apply_visitor(equals_visitor(), this->value, v.value); } bool Value::operator!=(const Value &v) const { return !(*this == v); } bool Value::operator&&(const Value &v) const { return this->toBool() && v.toBool(); } bool Value::operator||(const Value &v) const { return this->toBool() || v.toBool(); } class less_visitor : public boost::static_visitor { public: template bool operator()(const T &, const U &) const { return false; } bool operator()(const double &op1, const double &op2) const { return op1 < op2; } bool operator()(const std::string &op1, const std::string &op2) const { return op1 < op2; } }; class greater_visitor : public boost::static_visitor { public: template bool operator()(const T &, const U &) const { return false; } bool operator()(const double &op1, const double &op2) const { return op1 > op2; } bool operator()(const std::string &op1, const std::string &op2) const { return op1 > op2; } }; bool Value::operator<(const Value &v) const { return boost::apply_visitor(less_visitor(), this->value, v.value); } bool Value::operator>=(const Value &v) const { return !(*this < v); } bool Value::operator>(const Value &v) const { return boost::apply_visitor(greater_visitor(), this->value, v.value); } bool Value::operator<=(const Value &v) const { return !(*this > v); } class plus_visitor : public boost::static_visitor { public: template Value operator()(const T &, const U &) const { return Value::undefined; } Value operator()(const double &op1, const double &op2) const { return Value(op1 + op2); } 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]); } return Value(sum); } }; Value Value::operator+(const Value &v) const { return boost::apply_visitor(plus_visitor(), this->value, v.value); } class minus_visitor : public boost::static_visitor { public: template Value operator()(const T &, const U &) const { return Value::undefined; } Value operator()(const double &op1, const double &op2) const { return Value(op1 - op2); } 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]); } return Value(sum); } }; Value Value::operator-(const Value &v) const { return boost::apply_visitor(minus_visitor(), this->value, v.value); } 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); } return Value(dstv); } Value Value::multmatvec(const Value &matrixval, const Value &vectorval) { const VectorType &matrixvec = matrixval.toVector(); const VectorType &vectorvec = vectorval.toVector(); // Matrix * Vector VectorType dstv; for (size_t i=0;itype() == NUMBER && v.type() == NUMBER) { return Value(this->toDouble() * v.toDouble()); } else if (this->type() == VECTOR && v.type() == NUMBER) { return multvecnum(*this, v); } else if (this->type() == NUMBER && v.type() == VECTOR) { return multvecnum(v, *this); } 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 && vec1.size() == vec2.size()) { // Vector dot product. double r = 0.0; for (size_t i=0;itype() == NUMBER && v.type() == NUMBER) { return Value(this->toDouble() / v.toDouble()); } 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); } 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); } return Value(dstv); } return Value::undefined; } Value Value::operator%(const Value &v) const { if (this->type() == NUMBER && v.type() == NUMBER) { return Value(fmod(boost::get(this->value), boost::get(v.value))); } return Value::undefined; } Value Value::operator-() const { if (this->type() == NUMBER) { return Value(-this->toDouble()); } else if (this->type() == VECTOR) { const VectorType &vec = this->toVector(); VectorType dstv; BOOST_FOREACH(const Value &vecval, vec) { dstv.push_back(-vecval); } return Value(dstv); } return Value::undefined; } /*! Append a value to this vector. This must be of valtype VECTOR. */ /* void Value::append(Value *val) { assert(this->type() == VECTOR); this->vec.push_back(val); } */ class bracket_visitor : public boost::static_visitor { public: Value operator()(const std::string &str, const double &idx) const { int i = int(idx); Value v; if (i >= 0 && i < str.size()) { v = Value(str[int(idx)]); // std::cout << "bracket_visitor: " << v << "\n"; } return v; } Value operator()(const Value::VectorType &vec, const double &idx) const { int i = int(idx); if (i >= 0 && i < vec.size()) return vec[int(idx)]; return Value::undefined; } Value operator()(const Value::RangeType &range, const double &idx) const { switch(int(idx)) { case 0: return Value(range.begin); case 1: return Value(range.step); case 2: return Value(range.end); } return Value::undefined; } template Value operator()(const T &, const U &) const { // std::cout << "generic bracket_visitor\n"; return Value::undefined; } }; Value Value::operator[](const Value &v) { return boost::apply_visitor(bracket_visitor(), this->value, v.value); }