diff --git a/src/celengine/astro.cpp b/src/celengine/astro.cpp index bdc687707e..52dea5683d 100644 --- a/src/celengine/astro.cpp +++ b/src/celengine/astro.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include "astro.h" #include "univcoord.h" #include diff --git a/src/celengine/configuration.cpp b/src/celengine/configuration.cpp new file mode 100644 index 0000000000..ff6a30d208 --- /dev/null +++ b/src/celengine/configuration.cpp @@ -0,0 +1,97 @@ +#include "configuration.h" +#include "property.h" +#include +#include + + +namespace celestia +{ +namespace engine +{ + +#ifdef DEBUG +void Config::dump() const +{ + for (const auto &m : m_values) + { + std::cout << m.first << ' '; + auto d = m.second; + std::cout << d->getType() << ' '; + switch (d->getType()) + { + case Value::NullType: + std::cout << "null"; + break; + case Value::NumberType: + std::cout << d->getNumber(); + break; + case Value::StringType: + std::cout << d->getString(); + break; + case Value::BooleanType: + std::cout << d->getBoolean(); + break; + default: + std::cout << "not supported yet"; + break; + } + std::cout << '\n'; + } +} +#endif + +void Config::addProperty(IProperty *p) +{ + if (std::find(m_props.begin(), m_props.end(), p) == m_props.end()) + m_props.push_back(p); + p->update(); +} + +void Config::removeProperty(IProperty *p) +{ + auto pos = std::find(m_props.begin(), m_props.end(), p); + if (pos != m_props.end()) + m_props.erase(pos); +} + +const Value* Config::find(const std::string& name) const +{ + static Value v; + + auto it = m_values.find(name); + if (it != m_values.end()) + return it->second; + + return &v; +} + +void Config::beginUpdate() +{ + m_update = true; +} + +void Config::set(const std::string& name, const Value* value) +{ + m_values[name] = value; +} + +void Config::endUpdate() +{ + m_update = false; + onUpdate(); +} + +void Config::onUpdate() +{ + for (auto *p : m_props) + p->update(); +} + +Config::~Config() +{ + for (const auto &p : m_values) + delete p.second; +} + +} +} // namespace; diff --git a/src/celengine/configuration.h b/src/celengine/configuration.h new file mode 100644 index 0000000000..bae873514a --- /dev/null +++ b/src/celengine/configuration.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include "value.h" +#include + + +namespace celestia +{ +namespace engine +{ +class IConfigUpdater; +class IConfigWriter; +class IProperty; + +class Config +{ + public: + Config() = default; + Config(const Config&) = delete; + Config(Config&&) = delete; + ~Config(); + + Config& operator=(const Config&) = delete; + Config& operator=(Config&&) = delete; + + void addProperty(IProperty*); + void removeProperty(IProperty*); + const Value* find(const std::string& name) const; + +#ifdef DEBUG + void dump() const; +#else + void dump() const {}; +#endif + + private: + struct Cmp + { + bool operator()(const std::string &a, const std::string &b) const + { + return compareIgnoringCase(a, b) < 0; + } + }; + + void onUpdate(); + void beginUpdate(); + void set(const std::string& name, const Value *value); + void endUpdate(); + + std::vector m_props; + std::map m_values; + + bool m_update { false }; + + friend class IConfigUpdater; + friend class IConfigWriter; + friend class IProperty; +}; + + +// Proxy class to invoke private Config's methods from derived classes +class IConfigUpdater +{ + std::shared_ptr m_cfg; + public: + IConfigUpdater(const std::shared_ptr &cfg) : m_cfg(cfg) {} + inline void beginUpdate() { m_cfg->beginUpdate(); } + inline void set(const std::string &name, Value *value) { m_cfg->set(name, value); } + inline void endUpdate() { m_cfg->endUpdate(); } +}; + + +} +} // namespace; diff --git a/src/celengine/hash.cpp b/src/celengine/hash.cpp new file mode 100644 index 0000000000..761eddf96c --- /dev/null +++ b/src/celengine/hash.cpp @@ -0,0 +1,508 @@ +// hash.cpp +// +// Copyright (C) 2001-2019, the Celestia Development Team +// Original version by Chris Laurel +// +// 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. + +#include +#include +#include +#include +#include + +using namespace Eigen; +using namespace std; +using namespace celmath; + +namespace celestia +{ +namespace engine +{ + + +AssociativeArray::~AssociativeArray() +{ + for (const auto &t : assoc) + delete t.second; +} + +Value* AssociativeArray::getValue(const string& key) const +{ + auto iter = assoc.find(key); + if (iter == assoc.end()) + return nullptr; + + return iter->second; +} + +void AssociativeArray::addValue(const string& key, Value& val) +{ + assoc.insert(map::value_type(key, &val)); +} + +bool AssociativeArray::getNumber(const string& key, double& val) const +{ + Value* v = getValue(key); + if (v == nullptr || v->getType() != Value::NumberType) + return false; + + val = v->getNumber(); + return true; +} + +bool AssociativeArray::getNumber(const string& key, float& val) const +{ + double dval; + + if (!getNumber(key, dval)) + return false; + + val = (float) dval; + return true; +} + +bool AssociativeArray::getNumber(const string& key, int& val) const +{ + double ival; + + if (!getNumber(key, ival)) + return false; + + val = (int) ival; + return true; +} + +bool AssociativeArray::getNumber(const string& key, uint32_t& val) const +{ + double ival; + + if (!getNumber(key, ival)) + return false; + + val = (uint32_t) ival; + return true; +} + +bool AssociativeArray::getString(const string& key, string& val) const +{ + Value* v = getValue(key); + if (v == nullptr || v->getType() != Value::StringType) + return false; + + val = v->getString(); + return true; +} + +bool AssociativeArray::getBoolean(const string& key, bool& val) const +{ + Value* v = getValue(key); + if (v == nullptr || v->getType() != Value::BooleanType) + return false; + + val = v->getBoolean(); + return true; +} + +bool AssociativeArray::getVector(const string& key, Vector3d& val) const +{ + Value* v = getValue(key); + if (v == nullptr || v->getType() != Value::ArrayType) + return false; + + Array* arr = v->getArray(); + if (arr->size() != 3) + return false; + + Value* x = (*arr)[0]; + Value* y = (*arr)[1]; + Value* z = (*arr)[2]; + + if (x->getType() != Value::NumberType || + y->getType() != Value::NumberType || + z->getType() != Value::NumberType) + return false; + + val = Vector3d(x->getNumber(), y->getNumber(), z->getNumber()); + return true; +} + + +bool AssociativeArray::getVector(const string& key, Vector3f& val) const +{ + Vector3d vecVal; + + if (!getVector(key, vecVal)) + return false; + + val = vecVal.cast(); + return true; +} + + +/** + * Retrieves a quaternion, scaled to an associated angle unit. + * + * The quaternion is specified in the catalog file in axis-angle format as follows: + * \verbatim {PropertyName} [ angle axisX axisY axisZ ] \endverbatim + * + * @param[in] key Hash key for the rotation. + * @param[out] val A quaternion representing the value if present, unaffected if not. + * @return True if the key exists in the hash, false otherwise. + */ +bool AssociativeArray::getRotation(const string& key, Eigen::Quaternionf& val) const +{ + Value* v = getValue(key); + if (v == nullptr || v->getType() != Value::ArrayType) + return false; + + Array* arr = v->getArray(); + if (arr->size() != 4) + return false; + + Value* w = (*arr)[0]; + Value* x = (*arr)[1]; + Value* y = (*arr)[2]; + Value* z = (*arr)[3]; + + if (w->getType() != Value::NumberType || + x->getType() != Value::NumberType || + y->getType() != Value::NumberType || + z->getType() != Value::NumberType) + return false; + + Vector3f axis((float) x->getNumber(), + (float) y->getNumber(), + (float) z->getNumber()); + + double ang = w->getNumber(); + double angScale = 1.0; + getAngleScale(key, angScale); + float angle = degToRad((float) (ang * angScale)); + + val = Quaternionf(AngleAxisf(angle, axis.normalized())); + + return true; +} + + +bool AssociativeArray::getColor(const string& key, Color& val) const +{ + Vector3d vecVal; + + if (!getVector(key, vecVal)) + return false; + + val = Color((float) vecVal.x(), (float) vecVal.y(), (float) vecVal.z()); + + return true; +} + + +/** + * Retrieves a numeric quantity scaled to an associated angle unit. + * @param[in] key Hash key for the quantity. + * @param[out] val The returned quantity if present, unaffected if not. + * @param[in] outputScale Returned value is scaled to this value. + * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. + * @return True if the key exists in the hash, false otherwise. + */ +bool +AssociativeArray::getAngle(const string& key, double& val, double outputScale, double defaultScale) const +{ + if (!getNumber(key, val)) + return false; + + double angleScale; + if(getAngleScale(key, angleScale)) + { + angleScale /= outputScale; + } + else + { + angleScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; + } + + val *= angleScale; + + return true; +} + + +/** @copydoc AssociativeArray::getAngle() */ +bool +AssociativeArray::getAngle(const string& key, float& val, double outputScale, double defaultScale) const +{ + double dval; + + if (!getAngle(key, dval, outputScale, defaultScale)) + return false; + + val = ((float) dval); + + return true; +} + + +/** + * Retrieves a numeric quantity scaled to an associated length unit. + * @param[in] key Hash key for the quantity. + * @param[out] val The returned quantity if present, unaffected if not. + * @param[in] outputScale Returned value is scaled to this value. + * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. + * @return True if the key exists in the hash, false otherwise. + */ +bool +AssociativeArray::getLength(const string& key, double& val, double outputScale, double defaultScale) const +{ + if(!getNumber(key, val)) + return false; + + double lengthScale; + if(getLengthScale(key, lengthScale)) + { + lengthScale /= outputScale; + } + else + { + lengthScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; + } + + val *= lengthScale; + + return true; +} + + +/** @copydoc AssociativeArray::getLength() */ +bool AssociativeArray::getLength(const string& key, float& val, double outputScale, double defaultScale) const +{ + double dval; + + if (!getLength(key, dval, outputScale, defaultScale)) + return false; + + val = ((float) dval); + + return true; +} + + +/** + * Retrieves a numeric quantity scaled to an associated time unit. + * @param[in] key Hash key for the quantity. + * @param[out] val The returned quantity if present, unaffected if not. + * @param[in] outputScale Returned value is scaled to this value. + * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. + * @return True if the key exists in the hash, false otherwise. + */ +bool AssociativeArray::getTime(const string& key, double& val, double outputScale, double defaultScale) const +{ + if(!getNumber(key, val)) + return false; + + double timeScale; + if(getTimeScale(key, timeScale)) + { + timeScale /= outputScale; + } + else + { + timeScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; + } + + val *= timeScale; + + return true; +} + + +/** @copydoc AssociativeArray::getTime() */ +bool AssociativeArray::getTime(const string& key, float& val, double outputScale, double defaultScale) const +{ + double dval; + + if(!getLength(key, dval, outputScale, defaultScale)) + return false; + + val = ((float) dval); + + return true; +} + + +/** + * Retrieves a vector quantity scaled to an associated length unit. + * @param[in] key Hash key for the quantity. + * @param[out] val The returned vector if present, unaffected if not. + * @param[in] outputScale Returned value is scaled to this value. + * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. + * @return True if the key exists in the hash, false otherwise. + */ +bool AssociativeArray::getLengthVector(const string& key, Eigen::Vector3d& val, double outputScale, double defaultScale) const +{ + if(!getVector(key, val)) + return false; + + double lengthScale; + if(getLengthScale(key, lengthScale)) + { + lengthScale /= outputScale; + } + else + { + lengthScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; + } + + val *= lengthScale; + + return true; +} + + +/** @copydoc AssociativeArray::getLengthVector() */ +bool AssociativeArray::getLengthVector(const string& key, Eigen::Vector3f& val, double outputScale, double defaultScale) const +{ + Vector3d vecVal; + + if(!getLengthVector(key, vecVal, outputScale, defaultScale)) + return false; + + val = vecVal.cast(); + return true; +} + + +/** + * Retrieves a spherical tuple \verbatim [longitude, latitude, altitude] \endverbatim scaled to associated angle and length units. + * @param[in] key Hash key for the quantity. + * @param[out] val The returned tuple in units of degrees and kilometers if present, unaffected if not. + * @return True if the key exists in the hash, false otherwise. + */ +bool AssociativeArray::getSphericalTuple(const string& key, Vector3d& val) const +{ + if(!getVector(key, val)) + return false; + + double angleScale; + if(getAngleScale(key, angleScale)) + { + val[0] *= angleScale; + val[1] *= angleScale; + } + + double lengthScale = 1.0; + getLengthScale(key, lengthScale); + val[2] *= lengthScale; + + return true; +} + + +/** @copydoc AssociativeArray::getSphericalTuple */ +bool AssociativeArray::getSphericalTuple(const string& key, Vector3f& val) const +{ + Vector3d vecVal; + + if(!getSphericalTuple(key, vecVal)) + return false; + + val = vecVal.cast(); + return true; +} + + +/** + * Retrieves the angle unit associated with a given property. + * @param[in] key Hash key for the property. + * @param[out] scale The returned angle unit scaled to degrees if present, unaffected if not. + * @return True if an angle unit has been specified for the property, false otherwise. + */ +bool AssociativeArray::getAngleScale(const string& key, double& scale) const +{ + string unitKey(key + "%Angle"); + string unit; + + if (!getString(unitKey, unit)) + return false; + + return astro::getAngleScale(unit, scale); +} + + +/** @copydoc AssociativeArray::getAngleScale() */ +bool AssociativeArray::getAngleScale(const string& key, float& scale) const +{ + double dscale; + if (!getAngleScale(key, dscale)) + return false; + + scale = ((float) dscale); + return true; +} + + +/** + * Retrieves the length unit associated with a given property. + * @param[in] key Hash key for the property. + * @param[out] scale The returned length unit scaled to kilometers if present, unaffected if not. + * @return True if a length unit has been specified for the property, false otherwise. + */ +bool AssociativeArray::getLengthScale(const string& key, double& scale) const +{ + string unitKey(key + "%Length"); + string unit; + + if (!getString(unitKey, unit)) + return false; + + return astro::getLengthScale(unit, scale); +} + + +/** @copydoc AssociativeArray::getLengthScale() */ +bool AssociativeArray::getLengthScale(const string& key, float& scale) const +{ + double dscale; + if (!getLengthScale(key, dscale)) + return false; + + scale = ((float) dscale); + return true; +} + + +/** + * Retrieves the time unit associated with a given property. + * @param[in] key Hash key for the property. + * @param[out] scale The returned time unit scaled to days if present, unaffected if not. + * @return True if a time unit has been specified for the property, false otherwise. + */ +bool AssociativeArray::getTimeScale(const string& key, double& scale) const +{ + string unitKey(key + "%Time"); + string unit; + + if (!getString(unitKey, unit)) + return false; + + return astro::getTimeScale(unit, scale); +} + + +/** @copydoc AssociativeArray::getTimeScale() */ +bool AssociativeArray::getTimeScale(const string& key, float& scale) const +{ + double dscale; + if (!getTimeScale(key, dscale)) + return false; + + scale = ((float) dscale); + return true; +} + +} +} diff --git a/src/celengine/hash.h b/src/celengine/hash.h new file mode 100644 index 0000000000..8126c24f6b --- /dev/null +++ b/src/celengine/hash.h @@ -0,0 +1,81 @@ +// hash.h +// +// Copyright (C) 2001-2019, the Celestia Development Team +// Original version by Chris Laurel +// +// 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. + +#pragma once + +#include +#include +#include + + +class Color; + +namespace celestia +{ +namespace engine +{ + +class Value; + +using HashIterator = std::map::const_iterator; + +class AssociativeArray +{ + public: + AssociativeArray() = default; + ~AssociativeArray(); + + Value* getValue(const std::string&) const; + void addValue(const std::string&, Value&); + + bool getNumber(const std::string&, double&) const; + bool getNumber(const std::string&, float&) const; + bool getNumber(const std::string&, int&) const; + bool getNumber(const std::string&, uint32_t&) const; + bool getString(const std::string&, std::string&) const; + bool getBoolean(const std::string&, bool&) const; + bool getVector(const std::string&, Eigen::Vector3d&) const; + bool getVector(const std::string&, Eigen::Vector3f&) const; + bool getRotation(const std::string&, Eigen::Quaternionf&) const; + bool getColor(const std::string&, Color&) const; + bool getAngle(const std::string&, double&, double = 1.0, double = 0.0) const; + bool getAngle(const std::string&, float&, double = 1.0, double = 0.0) const; + bool getLength(const std::string&, double&, double = 1.0, double = 0.0) const; + bool getLength(const std::string&, float&, double = 1.0, double = 0.0) const; + bool getTime(const std::string&, double&, double = 1.0, double = 0.0) const; + bool getTime(const std::string&, float&, double = 1.0, double = 0.0) const; + bool getLengthVector(const std::string&, Eigen::Vector3d&, double = 1.0, double = 0.0) const; + bool getLengthVector(const std::string&, Eigen::Vector3f&, double = 1.0, double = 0.0) const; + bool getSphericalTuple(const std::string&, Eigen::Vector3d&) const; + bool getSphericalTuple(const std::string&, Eigen::Vector3f&) const; + bool getAngleScale(const std::string&, double&) const; + bool getAngleScale(const std::string&, float&) const; + bool getLengthScale(const std::string&, double&) const; + bool getLengthScale(const std::string&, float&) const; + bool getTimeScale(const std::string&, double&) const; + bool getTimeScale(const std::string&, float&) const; + + HashIterator begin() + { + return assoc.begin(); + } + HashIterator end() + { + return assoc.end(); + } + + private: + std::map assoc; +}; + +using Hash = AssociativeArray; + +} +} diff --git a/src/celengine/parser.cpp b/src/celengine/parser.cpp index ce24f0fa37..2028c878c0 100644 --- a/src/celengine/parser.cpp +++ b/src/celengine/parser.cpp @@ -1,6 +1,6 @@ // parser.cpp // -// Copyright (C) 2001-2009, the Celestia Development Team +// Copyright (C) 2001-2019, the Celestia Development Team // Original version by Chris Laurel // // This program is free software; you can redistribute it and/or @@ -8,114 +8,16 @@ // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. -#include "parser.h" -#include "astro.h" +#include +#include using namespace Eigen; using namespace celmath; - -/****** Value method implementations *******/ - -Value::Value(double d) -{ - type = NumberType; - data.d = d; -} - -Value::Value(const string& s) -{ - type = StringType; - data.s = new string(s); -} - -Value::Value(ValueArray* a) -{ - type = ArrayType; - data.a = a; -} - -Value::Value(Hash* h) -{ - type = HashType; - data.h = h; -} - -Value::Value(bool b) -{ - type = BooleanType; - data.d = b ? 1.0 : 0.0; -} - -Value::~Value() -{ - if (type == StringType) - { - delete data.s; - } - else if (type == ArrayType) - { - if (data.a != nullptr) - { - for (unsigned int i = 0; i < data.a->size(); i++) - delete (*data.a)[i]; - delete data.a; - } - } - else if (type == HashType) - { - if (data.h != nullptr) - { -#if 0 - Hash::iterator iter = data.h->begin(); - while (iter != data.h->end()) - { - delete iter->second; - iter++; - } -#endif - delete data.h; - } - } -} - -Value::ValueType Value::getType() const -{ - return type; -} - -double Value::getNumber() const -{ - // ASSERT(type == NumberType); - return data.d; -} - -string Value::getString() const +namespace celestia { - // ASSERT(type == StringType); - return *data.s; -} - -ValueArray* Value::getArray() const +namespace engine { - // ASSERT(type == ArrayType); - return data.a; -} - -Hash* Value::getHash() const -{ - // ASSERT(type == HashType); - return data.h; -} - -bool Value::getBoolean() const -{ - // ASSERT(type == BooleanType); - return (data.d != 0.0); -} - - -/****** Parser method implementation ******/ Parser::Parser(Tokenizer* _tokenizer) : tokenizer(_tokenizer) @@ -123,7 +25,7 @@ Parser::Parser(Tokenizer* _tokenizer) : } -ValueArray* Parser::readArray() +Array* Parser::readArray() { Tokenizer::TokenType tok = tokenizer->nextToken(); if (tok != Tokenizer::TokenBeginArray) @@ -132,7 +34,7 @@ ValueArray* Parser::readArray() return nullptr; } - auto* array = new ValueArray(); + auto* array = new Array(); Value* v = readValue(); while (v != nullptr) @@ -279,7 +181,7 @@ Value* Parser::readValue() case Tokenizer::TokenBeginArray: tokenizer->pushBack(); { - ValueArray* array = readArray(); + Array* array = readArray(); if (array == nullptr) return nullptr; else @@ -302,505 +204,5 @@ Value* Parser::readValue() } } - -AssociativeArray::~AssociativeArray() -{ -#if 0 - Hash::iterator iter = data.h->begin(); - while (iter != data.h->end()) - { - delete iter->second; - iter++; - } -#endif - for (const auto iter : assoc) - delete iter.second; -} - -Value* AssociativeArray::getValue(const string& key) const -{ - map::const_iterator iter = assoc.find(key); - if (iter == assoc.end()) - return nullptr; - - return iter->second; -} - -void AssociativeArray::addValue(const string& key, Value& val) -{ - assoc.insert(map::value_type(key, &val)); -} - -bool AssociativeArray::getNumber(const string& key, double& val) const -{ - Value* v = getValue(key); - if (v == nullptr || v->getType() != Value::NumberType) - return false; - - val = v->getNumber(); - return true; -} - -bool AssociativeArray::getNumber(const string& key, float& val) const -{ - double dval; - - if (!getNumber(key, dval)) - return false; - - val = (float) dval; - return true; -} - -bool AssociativeArray::getNumber(const string& key, int& val) const -{ - double ival; - - if (!getNumber(key, ival)) - return false; - - val = (int) ival; - return true; -} - -bool AssociativeArray::getNumber(const string& key, uint32_t& val) const -{ - double ival; - - if (!getNumber(key, ival)) - return false; - - val = (uint32_t) ival; - return true; -} - -bool AssociativeArray::getString(const string& key, string& val) const -{ - Value* v = getValue(key); - if (v == nullptr || v->getType() != Value::StringType) - return false; - - val = v->getString(); - return true; -} - -bool AssociativeArray::getBoolean(const string& key, bool& val) const -{ - Value* v = getValue(key); - if (v == nullptr || v->getType() != Value::BooleanType) - return false; - - val = v->getBoolean(); - return true; -} - -bool AssociativeArray::getVector(const string& key, Vector3d& val) const -{ - Value* v = getValue(key); - if (v == nullptr || v->getType() != Value::ArrayType) - return false; - - ValueArray* arr = v->getArray(); - if (arr->size() != 3) - return false; - - Value* x = (*arr)[0]; - Value* y = (*arr)[1]; - Value* z = (*arr)[2]; - - if (x->getType() != Value::NumberType || - y->getType() != Value::NumberType || - z->getType() != Value::NumberType) - return false; - - val = Vector3d(x->getNumber(), y->getNumber(), z->getNumber()); - return true; -} - - -bool AssociativeArray::getVector(const string& key, Vector3f& val) const -{ - Vector3d vecVal; - - if (!getVector(key, vecVal)) - return false; - - val = vecVal.cast(); - return true; -} - - -/** - * Retrieves a quaternion, scaled to an associated angle unit. - * - * The quaternion is specified in the catalog file in axis-angle format as follows: - * \verbatim {PropertyName} [ angle axisX axisY axisZ ] \endverbatim - * - * @param[in] key Hash key for the rotation. - * @param[out] val A quaternion representing the value if present, unaffected if not. - * @return True if the key exists in the hash, false otherwise. - */ -bool AssociativeArray::getRotation(const string& key, Eigen::Quaternionf& val) const -{ - Value* v = getValue(key); - if (v == nullptr || v->getType() != Value::ArrayType) - return false; - - ValueArray* arr = v->getArray(); - if (arr->size() != 4) - return false; - - Value* w = (*arr)[0]; - Value* x = (*arr)[1]; - Value* y = (*arr)[2]; - Value* z = (*arr)[3]; - - if (w->getType() != Value::NumberType || - x->getType() != Value::NumberType || - y->getType() != Value::NumberType || - z->getType() != Value::NumberType) - return false; - - Vector3f axis((float) x->getNumber(), - (float) y->getNumber(), - (float) z->getNumber()); - - double ang = w->getNumber(); - double angScale = 1.0; - getAngleScale(key, angScale); - float angle = degToRad((float) (ang * angScale)); - - val = Quaternionf(AngleAxisf(angle, axis.normalized())); - - return true; -} - - -bool AssociativeArray::getColor(const string& key, Color& val) const -{ - Vector3d vecVal; - - if (!getVector(key, vecVal)) - return false; - - val = Color((float) vecVal.x(), (float) vecVal.y(), (float) vecVal.z()); - - return true; -} - - -/** - * Retrieves a numeric quantity scaled to an associated angle unit. - * @param[in] key Hash key for the quantity. - * @param[out] val The returned quantity if present, unaffected if not. - * @param[in] outputScale Returned value is scaled to this value. - * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. - * @return True if the key exists in the hash, false otherwise. - */ -bool -AssociativeArray::getAngle(const string& key, double& val, double outputScale, double defaultScale) const -{ - if (!getNumber(key, val)) - return false; - - double angleScale; - if(getAngleScale(key, angleScale)) - { - angleScale /= outputScale; - } - else - { - angleScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; - } - - val *= angleScale; - - return true; -} - - -/** @copydoc AssociativeArray::getAngle() */ -bool -AssociativeArray::getAngle(const string& key, float& val, double outputScale, double defaultScale) const -{ - double dval; - - if (!getAngle(key, dval, outputScale, defaultScale)) - return false; - - val = ((float) dval); - - return true; -} - - -/** - * Retrieves a numeric quantity scaled to an associated length unit. - * @param[in] key Hash key for the quantity. - * @param[out] val The returned quantity if present, unaffected if not. - * @param[in] outputScale Returned value is scaled to this value. - * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. - * @return True if the key exists in the hash, false otherwise. - */ -bool -AssociativeArray::getLength(const string& key, double& val, double outputScale, double defaultScale) const -{ - if(!getNumber(key, val)) - return false; - - double lengthScale; - if(getLengthScale(key, lengthScale)) - { - lengthScale /= outputScale; - } - else - { - lengthScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; - } - - val *= lengthScale; - - return true; -} - - -/** @copydoc AssociativeArray::getLength() */ -bool AssociativeArray::getLength(const string& key, float& val, double outputScale, double defaultScale) const -{ - double dval; - - if (!getLength(key, dval, outputScale, defaultScale)) - return false; - - val = ((float) dval); - - return true; -} - - -/** - * Retrieves a numeric quantity scaled to an associated time unit. - * @param[in] key Hash key for the quantity. - * @param[out] val The returned quantity if present, unaffected if not. - * @param[in] outputScale Returned value is scaled to this value. - * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. - * @return True if the key exists in the hash, false otherwise. - */ -bool AssociativeArray::getTime(const string& key, double& val, double outputScale, double defaultScale) const -{ - if(!getNumber(key, val)) - return false; - - double timeScale; - if(getTimeScale(key, timeScale)) - { - timeScale /= outputScale; - } - else - { - timeScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; - } - - val *= timeScale; - - return true; -} - - -/** @copydoc AssociativeArray::getTime() */ -bool AssociativeArray::getTime(const string& key, float& val, double outputScale, double defaultScale) const -{ - double dval; - - if(!getLength(key, dval, outputScale, defaultScale)) - return false; - - val = ((float) dval); - - return true; -} - - -/** - * Retrieves a vector quantity scaled to an associated length unit. - * @param[in] key Hash key for the quantity. - * @param[out] val The returned vector if present, unaffected if not. - * @param[in] outputScale Returned value is scaled to this value. - * @param[in] defaultScale If no units are specified, use this scale. Defaults to outputScale. - * @return True if the key exists in the hash, false otherwise. - */ -bool AssociativeArray::getLengthVector(const string& key, Eigen::Vector3d& val, double outputScale, double defaultScale) const -{ - if(!getVector(key, val)) - return false; - - double lengthScale; - if(getLengthScale(key, lengthScale)) - { - lengthScale /= outputScale; - } - else - { - lengthScale = (defaultScale == 0.0) ? 1.0 : defaultScale / outputScale; - } - - val *= lengthScale; - - return true; -} - - -/** @copydoc AssociativeArray::getLengthVector() */ -bool AssociativeArray::getLengthVector(const string& key, Eigen::Vector3f& val, double outputScale, double defaultScale) const -{ - Vector3d vecVal; - - if(!getLengthVector(key, vecVal, outputScale, defaultScale)) - return false; - - val = vecVal.cast(); - return true; -} - - -/** - * Retrieves a spherical tuple \verbatim [longitude, latitude, altitude] \endverbatim scaled to associated angle and length units. - * @param[in] key Hash key for the quantity. - * @param[out] val The returned tuple in units of degrees and kilometers if present, unaffected if not. - * @return True if the key exists in the hash, false otherwise. - */ -bool AssociativeArray::getSphericalTuple(const string& key, Vector3d& val) const -{ - if(!getVector(key, val)) - return false; - - double angleScale; - if(getAngleScale(key, angleScale)) - { - val[0] *= angleScale; - val[1] *= angleScale; - } - - double lengthScale = 1.0; - getLengthScale(key, lengthScale); - val[2] *= lengthScale; - - return true; -} - - -/** @copydoc AssociativeArray::getSphericalTuple */ -bool AssociativeArray::getSphericalTuple(const string& key, Vector3f& val) const -{ - Vector3d vecVal; - - if(!getSphericalTuple(key, vecVal)) - return false; - - val = vecVal.cast(); - return true; -} - - -/** - * Retrieves the angle unit associated with a given property. - * @param[in] key Hash key for the property. - * @param[out] scale The returned angle unit scaled to degrees if present, unaffected if not. - * @return True if an angle unit has been specified for the property, false otherwise. - */ -bool AssociativeArray::getAngleScale(const string& key, double& scale) const -{ - string unitKey(key + "%Angle"); - string unit; - - if (!getString(unitKey, unit)) - return false; - - return astro::getAngleScale(unit, scale); -} - - -/** @copydoc AssociativeArray::getAngleScale() */ -bool AssociativeArray::getAngleScale(const string& key, float& scale) const -{ - double dscale; - if (!getAngleScale(key, dscale)) - return false; - - scale = ((float) dscale); - return true; } - - -/** - * Retrieves the length unit associated with a given property. - * @param[in] key Hash key for the property. - * @param[out] scale The returned length unit scaled to kilometers if present, unaffected if not. - * @return True if a length unit has been specified for the property, false otherwise. - */ -bool AssociativeArray::getLengthScale(const string& key, double& scale) const -{ - string unitKey(key + "%Length"); - string unit; - - if (!getString(unitKey, unit)) - return false; - - return astro::getLengthScale(unit, scale); -} - - -/** @copydoc AssociativeArray::getLengthScale() */ -bool AssociativeArray::getLengthScale(const string& key, float& scale) const -{ - double dscale; - if (!getLengthScale(key, dscale)) - return false; - - scale = ((float) dscale); - return true; -} - - -/** - * Retrieves the time unit associated with a given property. - * @param[in] key Hash key for the property. - * @param[out] scale The returned time unit scaled to days if present, unaffected if not. - * @return True if a time unit has been specified for the property, false otherwise. - */ -bool AssociativeArray::getTimeScale(const string& key, double& scale) const -{ - string unitKey(key + "%Time"); - string unit; - - if (!getString(unitKey, unit)) - return false; - - return astro::getTimeScale(unit, scale); -} - - -/** @copydoc AssociativeArray::getTimeScale() */ -bool AssociativeArray::getTimeScale(const string& key, float& scale) const -{ - double dscale; - if (!getTimeScale(key, dscale)) - return false; - - scale = ((float) dscale); - return true; -} - - -HashIterator -AssociativeArray::begin() -{ - return assoc.begin(); -} - - -HashIterator -AssociativeArray::end() -{ - return assoc.end(); } diff --git a/src/celengine/parser.h b/src/celengine/parser.h index a5d66250b1..dc6a9c7732 100644 --- a/src/celengine/parser.h +++ b/src/celengine/parser.h @@ -1,6 +1,6 @@ // parser.h // -// Copyright (C) 2001-2009, the Celestia Development Team +// Copyright (C) 2001-2019, the Celestia Development Team // Original version by Chris Laurel // // This program is free software; you can redistribute it and/or @@ -8,105 +8,16 @@ // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. -#ifndef _PARSER_H_ -#define _PARSER_H_ +#pragma once -#include -#include -#include -#include #include -#include -#include +#include +#include -class Value; - -typedef map::const_iterator HashIterator; - -class AssociativeArray +namespace celestia { - public: - AssociativeArray() = default; - ~AssociativeArray(); - - Value* getValue(const std::string&) const; - void addValue(const std::string&, Value&); - - bool getNumber(const std::string&, double&) const; - bool getNumber(const std::string&, float&) const; - bool getNumber(const std::string&, int&) const; - bool getNumber(const std::string&, uint32_t&) const; - bool getString(const std::string&, std::string&) const; - bool getBoolean(const std::string&, bool&) const; - bool getVector(const std::string&, Eigen::Vector3d&) const; - bool getVector(const std::string&, Eigen::Vector3f&) const; - bool getRotation(const std::string&, Eigen::Quaternionf&) const; - bool getColor(const std::string&, Color&) const; - bool getAngle(const std::string&, double&, double = 1.0, double = 0.0) const; - bool getAngle(const std::string&, float&, double = 1.0, double = 0.0) const; - bool getLength(const std::string&, double&, double = 1.0, double = 0.0) const; - bool getLength(const std::string&, float&, double = 1.0, double = 0.0) const; - bool getTime(const std::string&, double&, double = 1.0, double = 0.0) const; - bool getTime(const std::string&, float&, double = 1.0, double = 0.0) const; - bool getLengthVector(const std::string&, Eigen::Vector3d&, double = 1.0, double = 0.0) const; - bool getLengthVector(const std::string&, Eigen::Vector3f&, double = 1.0, double = 0.0) const; - bool getSphericalTuple(const std::string&, Eigen::Vector3d&) const; - bool getSphericalTuple(const std::string&, Eigen::Vector3f&) const; - bool getAngleScale(const std::string&, double&) const; - bool getAngleScale(const std::string&, float&) const; - bool getLengthScale(const std::string&, double&) const; - bool getLengthScale(const std::string&, float&) const; - bool getTimeScale(const std::string&, double&) const; - bool getTimeScale(const std::string&, float&) const; - - HashIterator begin(); - HashIterator end(); - - private: - map assoc; -}; - -typedef vector Array; -typedef vector ValueArray; -typedef AssociativeArray Hash; - -class Value +namespace engine { -public: - enum ValueType { - NumberType = 0, - StringType = 1, - ArrayType = 2, - HashType = 3, - BooleanType = 4 - }; - - Value(double); - Value(const string&); - Value(ValueArray*); - Value(Hash*); - Value(bool); - ~Value(); - - ValueType getType() const; - - double getNumber() const; - string getString() const; - ValueArray* getArray() const; - Hash* getHash() const; - bool getBoolean() const; - -private: - ValueType type; - - union { - string* s; - double d; - ValueArray* a; - Hash* h; - } data; -}; - class Parser { @@ -119,8 +30,9 @@ class Parser Tokenizer* tokenizer; bool readUnits(const std::string&, Hash*); - ValueArray* readArray(); + Array* readArray(); Hash* readHash(); }; -#endif // _PARSER_H_ +} +} diff --git a/src/celengine/property.cpp b/src/celengine/property.cpp new file mode 100644 index 0000000000..5fbb242b86 --- /dev/null +++ b/src/celengine/property.cpp @@ -0,0 +1,41 @@ +#include "property.h" + + +namespace celestia +{ +namespace engine +{ + +#define PROPERTY_UPDATE(ValueType) \ + auto v = m_config->find(m_name); \ + m_has_value = v->getType() == Value:: ValueType ## Type; \ + if (m_has_value) \ + { \ + if (m_validate != nullptr) \ + m_value = m_validate(v->get ## ValueType ()); \ + else \ + m_value = v->get ## ValueType (); \ + } + +template<> +void Property::update() +{ + PROPERTY_UPDATE(Number); +} + +template<> +void Property::update() +{ + PROPERTY_UPDATE(String); +} + +template<> +void Property::update() +{ + PROPERTY_UPDATE(Boolean); +} + +#undef PROPERTY_UPDATE + +} +} // namespace; diff --git a/src/celengine/property.h b/src/celengine/property.h new file mode 100644 index 0000000000..82ba131429 --- /dev/null +++ b/src/celengine/property.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include +#include "configuration.h" + + +namespace celestia +{ +namespace engine +{ + +class IProperty +{ + public: + virtual void update() = 0; + friend class Config; +}; + +template +class Property : IProperty +{ + public: + using validate_fn_t = std::function; + + Property() = default; + + Property(std::shared_ptr config, std::string name, T _default, validate_fn_t validate = nullptr) : + m_config (std::move(config)), + m_name (std::move(name)), + m_default (std::move(_default)), + m_validate (std::move(validate)) + { + assert(m_config != nullptr); + m_config->addProperty(this); + }; + + ~Property() + { + if (m_config != nullptr) + m_config->removeProperty(this); + }; + + // Getters and setters + inline Property& set(T value) + { + m_value = value; + }; + + Property& operator() (T value) + { + return set(value); + }; + + Property& operator=(T value) + { + return set(value); + }; + + T get() const + { + return m_has_value ? m_value : m_default; + }; + + T operator()() const + { + return get(); + }; + + // Used by Config to propagate changes + void update() override; + + private: + std::shared_ptr m_config { nullptr }; + std::string m_name; + T m_value {}; + T m_default {}; + bool m_has_value { false }; + validate_fn_t m_validate { nullptr }; +}; + +using NumericProperty = Property; +using StringProperty = Property; +using BooleanProperty = Property; + +} +} // namespace; diff --git a/src/celengine/testprops.cc b/src/celengine/testprops.cc new file mode 100644 index 0000000000..1c035d45d5 --- /dev/null +++ b/src/celengine/testprops.cc @@ -0,0 +1,81 @@ +#include "property.h" +#include +#include + +using namespace celestia::engine; +using namespace std; + +class myconfig : IConfigUpdater +{ + public: + myconfig(const std::shared_ptr &cfg) : IConfigUpdater(cfg) {} + void read(); +}; + +void myconfig::read() +{ + beginUpdate(); + auto *v1 = new Value(10.18); + set("Distance", v1); + auto *v2 = new Value(std::string("foobar")); + set("Name", v2); + auto *v3 = new Value(true); + set("Visible", v3); + endUpdate(); +} + +class Foo +{ + Property m_p1; + NumericProperty m_distance; + StringProperty m_name, m_type; + BooleanProperty m_visible; + shared_ptr m_cfg; + + public: + Foo(const shared_ptr &cfg): + m_cfg (cfg), + m_p1 (Property(cfg, "P1", 10.5)), + m_distance (NumericProperty(cfg, "distance", 55.1, [](const double &v){ return v > 0 ? -v : v; })), + m_name (StringProperty(cfg, "name", "baz")), + m_type (StringProperty(cfg, "type", "baz")), + m_visible (BooleanProperty(cfg, "visible", false)) + { + } + double p1() + { + return m_p1.get(); + } + double distance() + { + return m_distance(); + } + string name() + { + return m_name(); + } + string type() + { + return m_type(); + } + bool visible() + { + return m_visible(); + } +}; + +int main() +{ + auto c = make_shared(); + myconfig my(c); + my.read(); + c->dump(); + Foo f(c); + cout << f.p1() << ' ' << f.distance() << ' ' << f.name() << ' ' << f.type() << ' ' << f.visible() << '\n'; + auto *v = c->find("Name"); + if (v->getType() == Value::StringType) + cout << v->getString() << '\n'; + else + cout << "dunno\n"; + c->dump(); +} diff --git a/src/celengine/value.cpp b/src/celengine/value.cpp new file mode 100644 index 0000000000..acc3636278 --- /dev/null +++ b/src/celengine/value.cpp @@ -0,0 +1,44 @@ +// value.cpp +// +// Copyright (C) 2001-2019, the Celestia Development Team +// Original version by Chris Laurel +// +// 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. + +#include + +namespace celestia +{ +namespace engine +{ + +/****** Value method implementations *******/ + +Value::~Value() +{ + switch (type) + { + case StringType: + delete data.s; + break; + case ArrayType: + if (data.a != nullptr) + { + for (auto *p : *data.a) + delete p; + delete data.a; + } + break; + case HashType: + delete data.h; + break; + default: + break; + } +} + +} +} diff --git a/src/celengine/value.h b/src/celengine/value.h new file mode 100644 index 0000000000..8b37cc5056 --- /dev/null +++ b/src/celengine/value.h @@ -0,0 +1,117 @@ +// value.h +// +// Copyright (C) 2001-2019, the Celestia Development Team +// Original version by Chris Laurel +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace celestia +{ +namespace engine +{ + +class Value; +using Array = std::vector; + +class Value +{ + public: + enum ValueType + { + NullType = 0, + NumberType = 1, + StringType = 2, + ArrayType = 3, + HashType = 4, + BooleanType = 5 + }; + + Value() = default; + ~Value(); + Value(const Value&) = delete; // TODO: implement + Value(Value&&) = delete; // TODO: implement + + Value(double d) : type(NumberType) + { + data.d = d; + } + Value(const char *s) : type(StringType) + { + data.s = new std::string(s); + } + explicit Value(const std::string &s) : type(StringType) + { + data.s = new std::string(s); + } + Value(Array *a) : type(ArrayType) + { + data.a = a; + } + Value(Hash *h) : type(HashType) + { + data.h = h; + } + Value(bool b) : type(BooleanType) + { + data.d = b ? 1.0 : 0.0; + } + + ValueType getType() const + { + return type; + } + bool isNull() const + { + return type == NullType; + } + double getNumber() const + { + assert(type == NumberType); + return data.d; + } + std::string getString() const + { + assert(type == StringType); + return *data.s; + } + Array* getArray() const + { + assert(type == ArrayType); + return data.a; + } + Hash* getHash() const + { + assert(type == HashType); + return data.h; + } + bool getBoolean() const + { + assert(type == BooleanType); + return (data.d != 0.0); + } + + private: + union Data + { + std::string *s; + double d; + Array *a; + Hash *h; + }; + + ValueType type { NullType }; + Data data; +}; + +} +}