| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <cstdlib>
|
| | #include <limits>
|
| |
|
| | #include <boost/regex.hpp>
|
| |
|
| | #include "ComplexGeoData.h"
|
| | #include "ElementMap.h"
|
| | #include "ElementNamingUtils.h"
|
| |
|
| | #include <Base/BoundBox.h>
|
| | #include <Base/Placement.h>
|
| | #include <Base/Reader.h>
|
| | #include <Base/Rotation.h>
|
| | #include <Base/Writer.h>
|
| |
|
| | #include <boost/iostreams/device/array.hpp>
|
| | #include <boost/iostreams/stream.hpp>
|
| |
|
| |
|
| | using namespace Data;
|
| |
|
| | TYPESYSTEM_SOURCE_ABSTRACT(Data::Segment, Base::BaseClass)
|
| | TYPESYSTEM_SOURCE_ABSTRACT(Data::ComplexGeoData, Base::Persistence)
|
| |
|
| | FC_LOG_LEVEL_INIT("ComplexGeoData", true, true)
|
| |
|
| | namespace bio = boost::iostreams;
|
| | using namespace Data;
|
| |
|
| |
|
| |
|
| | ComplexGeoData::ComplexGeoData() = default;
|
| |
|
| | std::pair<std::string, unsigned long> ComplexGeoData::getTypeAndIndex(const char* Name)
|
| | {
|
| | int index = 0;
|
| | std::string element;
|
| | boost::regex ex("^([^0-9]*)([0-9]*)$");
|
| | boost::cmatch what;
|
| |
|
| | if (Name && boost::regex_match(Name, what, ex)) {
|
| | element = what[1].str();
|
| | index = std::atoi(what[2].str().c_str());
|
| | }
|
| |
|
| | return std::make_pair(element, index);
|
| | }
|
| |
|
| | Data::Segment* ComplexGeoData::getSubElementByName(const char* name) const
|
| | {
|
| | auto type = getTypeAndIndex(name);
|
| | return getSubElement(type.first.c_str(), type.second);
|
| | }
|
| |
|
| | void ComplexGeoData::applyTransform(const Base::Matrix4D& rclTrf)
|
| | {
|
| | setTransform(rclTrf * getTransform());
|
| | }
|
| |
|
| | void ComplexGeoData::applyTranslation(const Base::Vector3d& mov)
|
| | {
|
| | Base::Matrix4D mat;
|
| | mat.move(mov);
|
| | setTransform(mat * getTransform());
|
| | }
|
| |
|
| | void ComplexGeoData::applyRotation(const Base::Rotation& rot)
|
| | {
|
| | Base::Matrix4D mat;
|
| | rot.getValue(mat);
|
| | setTransform(mat * getTransform());
|
| | }
|
| |
|
| | void ComplexGeoData::setPlacement(const Base::Placement& rclPlacement)
|
| | {
|
| | setTransform(rclPlacement.toMatrix());
|
| | }
|
| |
|
| | Base::Placement ComplexGeoData::getPlacement() const
|
| | {
|
| | Base::Matrix4D mat = getTransform();
|
| |
|
| | return {Base::Vector3d(mat[0][3], mat[1][3], mat[2][3]), Base::Rotation(mat)};
|
| | }
|
| |
|
| | double ComplexGeoData::getAccuracy() const
|
| | {
|
| | return 0.0;
|
| | }
|
| |
|
| | void ComplexGeoData::getLinesFromSubElement(const Segment* segment,
|
| | std::vector<Base::Vector3d>& Points,
|
| | std::vector<Line>& lines) const
|
| | {
|
| | (void)segment;
|
| | (void)Points;
|
| | (void)lines;
|
| | }
|
| |
|
| | void ComplexGeoData::getFacesFromSubElement(const Segment* segment,
|
| | std::vector<Base::Vector3d>& Points,
|
| | std::vector<Base::Vector3d>& PointNormals,
|
| | std::vector<Facet>& faces) const
|
| | {
|
| | (void)segment;
|
| | (void)Points;
|
| | (void)PointNormals;
|
| | (void)faces;
|
| | }
|
| |
|
| | Base::Vector3d ComplexGeoData::getPointFromLineIntersection(const Base::Vector3f& base,
|
| | const Base::Vector3f& dir) const
|
| | {
|
| | (void)base;
|
| | (void)dir;
|
| | return Base::Vector3d();
|
| | }
|
| |
|
| | void ComplexGeoData::getPoints(std::vector<Base::Vector3d>& Points,
|
| | std::vector<Base::Vector3d>& Normals,
|
| | double Accuracy,
|
| | uint16_t flags) const
|
| | {
|
| | (void)Points;
|
| | (void)Normals;
|
| | (void)Accuracy;
|
| | (void)flags;
|
| | }
|
| |
|
| | void ComplexGeoData::getLines(std::vector<Base::Vector3d>& Points,
|
| | std::vector<Line>& lines,
|
| | double Accuracy,
|
| | uint16_t flags) const
|
| | {
|
| | (void)Points;
|
| | (void)lines;
|
| | (void)Accuracy;
|
| | (void)flags;
|
| | }
|
| |
|
| | void ComplexGeoData::getFaces(std::vector<Base::Vector3d>& Points,
|
| | std::vector<Facet>& faces,
|
| | double Accuracy,
|
| | uint16_t flags) const
|
| | {
|
| | (void)Points;
|
| | (void)faces;
|
| | (void)Accuracy;
|
| | (void)flags;
|
| | }
|
| |
|
| | bool ComplexGeoData::getCenterOfGravity(Base::Vector3d& unused) const
|
| | {
|
| | (void)unused;
|
| | return false;
|
| | }
|
| |
|
| | std::optional<Base::Vector3d> ComplexGeoData::centerOfGravity() const
|
| | {
|
| | Base::Vector3d centerOfGravity;
|
| |
|
| | if (getCenterOfGravity(centerOfGravity)) {
|
| | return centerOfGravity;
|
| | }
|
| |
|
| | return {};
|
| | }
|
| |
|
| | const std::string& ComplexGeoData::elementMapPrefix()
|
| | {
|
| | static std::string prefix(ELEMENT_MAP_PREFIX);
|
| | return prefix;
|
| | }
|
| |
|
| | std::string ComplexGeoData::getElementMapVersion() const
|
| | {
|
| | return "5";
|
| | }
|
| |
|
| | bool ComplexGeoData::checkElementMapVersion(const char* ver) const
|
| | {
|
| | return !boost::ends_with(ver, "5");
|
| | }
|
| |
|
| | size_t ComplexGeoData::getElementMapSize(bool flush) const
|
| | {
|
| | if (flush) {
|
| | flushElementMap();
|
| | #ifdef _FC_MEM_TRACE
|
| | FC_MSG("memory size " << (_MemSize / 1024 / 1024) << "MB, " << (_MemMaxSize / 1024 / 1024));
|
| | for (auto& unit : _MemUnits) {
|
| | FC_MSG("unit " << unit.first << ": " << unit.second.count << ", "
|
| | << unit.second.maxcount);
|
| | }
|
| | #endif
|
| | }
|
| | return _elementMap ? _elementMap->size() : 0;
|
| | }
|
| |
|
| | MappedName ComplexGeoData::getMappedName(const IndexedName& element,
|
| | bool allowUnmapped,
|
| | ElementIDRefs* sid) const
|
| | {
|
| | if (!element) {
|
| | return {};
|
| | }
|
| | flushElementMap();
|
| | if (!_elementMap) {
|
| | if (allowUnmapped) {
|
| | return MappedName(element);
|
| | }
|
| | return {};
|
| | }
|
| |
|
| | MappedName name = _elementMap->find(element, sid);
|
| | if (allowUnmapped && !name) {
|
| | return MappedName(element);
|
| | }
|
| | return name;
|
| | }
|
| |
|
| | IndexedName ComplexGeoData::getIndexedName(const MappedName& name, ElementIDRefs* sid) const
|
| | {
|
| | flushElementMap();
|
| | if (!name) {
|
| | return IndexedName();
|
| | }
|
| | if (!_elementMap) {
|
| | std::string str;
|
| | return {name.appendToBuffer(str), getElementTypes()};
|
| | }
|
| | return _elementMap->find(name, sid);
|
| | }
|
| |
|
| | Data::MappedElement
|
| | ComplexGeoData::getElementName(const char* name, ElementIDRefs* sid, bool copy) const
|
| | {
|
| | IndexedName element(name, getElementTypes());
|
| | if (element) {
|
| | return {getMappedName(element, false, sid), element};
|
| | }
|
| |
|
| | const char* mapped = isMappedElement(name);
|
| | if (mapped) {
|
| | name = mapped;
|
| | }
|
| |
|
| | MappedElement result;
|
| |
|
| | const char* dot = strchr(name, '.');
|
| | if (dot) {
|
| | result.name = MappedName(name, static_cast<int>(dot - name));
|
| | }
|
| | else if (copy) {
|
| | result.name = name;
|
| | }
|
| | else {
|
| | result.name = MappedName(name);
|
| | }
|
| | result.index = getIndexedName(result.name, sid);
|
| | return result;
|
| | }
|
| |
|
| | std::vector<std::pair<MappedName, ElementIDRefs>>
|
| | ComplexGeoData::getElementMappedNames(const IndexedName& element, bool needUnmapped) const
|
| | {
|
| | flushElementMap();
|
| | if (_elementMap) {
|
| | auto res = _elementMap->findAll(element);
|
| | if (!res.empty()) {
|
| | return res;
|
| | }
|
| | }
|
| |
|
| | if (!needUnmapped) {
|
| | return {};
|
| | }
|
| | return {std::make_pair(MappedName(element), ElementIDRefs())};
|
| | }
|
| |
|
| | ElementMapPtr ComplexGeoData::resetElementMap(ElementMapPtr elementMap)
|
| | {
|
| | _elementMap.swap(elementMap);
|
| |
|
| |
|
| | if (_elementMap && !_elementMap->hasher) {
|
| | _elementMap->hasher = Hasher;
|
| | }
|
| | return elementMap;
|
| | }
|
| |
|
| | std::vector<MappedElement> ComplexGeoData::getElementMap() const
|
| | {
|
| | flushElementMap();
|
| | if (!_elementMap) {
|
| | return {};
|
| | }
|
| | return _elementMap->getAll();
|
| | }
|
| |
|
| | ElementMapPtr ComplexGeoData::elementMap(bool flush) const
|
| | {
|
| | if (flush) {
|
| | flushElementMap();
|
| | }
|
| | return _elementMap;
|
| | }
|
| |
|
| | ElementMapPtr ComplexGeoData::ensureElementMap(bool flush)
|
| | {
|
| | if (!_elementMap) {
|
| | resetElementMap(std::make_shared<Data::ElementMap>());
|
| | }
|
| | return elementMap(flush);
|
| | }
|
| |
|
| | void ComplexGeoData::flushElementMap() const
|
| | {}
|
| |
|
| | void ComplexGeoData::setElementMap(const std::vector<MappedElement>& map)
|
| | {
|
| | _elementMap = std::make_shared<Data::ElementMap>();
|
| |
|
| | for (auto& element : map) {
|
| | _elementMap->setElementName(element.index, element.name, Tag);
|
| | }
|
| | }
|
| |
|
| | char ComplexGeoData::elementType(const Data::MappedName& name) const
|
| | {
|
| | if (!name) {
|
| | return 0;
|
| | }
|
| | auto indexedName = getIndexedName(name);
|
| | if (indexedName) {
|
| | return elementType(indexedName);
|
| | }
|
| | char element_type = 0;
|
| | if (name.findTagInElementName(nullptr, nullptr, nullptr, &element_type) < 0) {
|
| | return elementType(name.toIndexedName());
|
| | }
|
| | return element_type;
|
| | }
|
| |
|
| | char ComplexGeoData::elementType(const Data::IndexedName& element) const
|
| | {
|
| | if (!element) {
|
| | return 0;
|
| | }
|
| | for (auto& type : getElementTypes()) {
|
| | if (boost::equals(element.getType(), type)) {
|
| | return type[0];
|
| | }
|
| | }
|
| | return 0;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | char ComplexGeoData::elementType(const char* name) const
|
| | {
|
| | if (!name) {
|
| | return 0;
|
| | }
|
| |
|
| | const char* type = nullptr;
|
| | IndexedName element(name, getElementTypes());
|
| | if (element) {
|
| | type = element.getType();
|
| | }
|
| | else {
|
| | const char* mapped = isMappedElement(name);
|
| | if (mapped) {
|
| | name = mapped;
|
| | }
|
| |
|
| | MappedName mappedName;
|
| | const char* dot = strchr(name, '.');
|
| | if (dot) {
|
| | mappedName = MappedName(name, static_cast<int>(dot - name));
|
| | type = dot + 1;
|
| | }
|
| | else {
|
| | mappedName = MappedName::fromRawData(name);
|
| | }
|
| | char res = elementType(mappedName);
|
| | if (res != 0) {
|
| | return res;
|
| | }
|
| | }
|
| |
|
| | if (type && (type[0] != 0)) {
|
| | for (auto& elementTypes : getElementTypes()) {
|
| | if (boost::starts_with(type, elementTypes)) {
|
| | return type[0];
|
| | }
|
| | }
|
| | }
|
| | return 0;
|
| | }
|
| |
|
| | void ComplexGeoData::setPersistenceFileName(const char* filename) const
|
| | {
|
| | if (!filename) {
|
| | filename = "";
|
| | }
|
| | _persistenceName = filename;
|
| | }
|
| |
|
| | void ComplexGeoData::Save(Base::Writer& writer) const
|
| | {
|
| |
|
| | if (getElementMapSize() == 0U) {
|
| | writer.Stream() << writer.ind() << "<ElementMap/>\n";
|
| | return;
|
| | }
|
| |
|
| |
|
| | writer.Stream() << writer.ind() << R"(<ElementMap new="1" count="1">)"
|
| | << R"(<Element key="Dummy" value="Dummy"/>)"
|
| | << "</ElementMap>\n";
|
| |
|
| |
|
| | writer.Stream() << writer.ind() << "<ElementMap2";
|
| |
|
| | if (!_persistenceName.empty()) {
|
| | writer.Stream() << " file=\"" << writer.addFile((_persistenceName + ".txt").c_str(), this)
|
| | << "\"/>\n";
|
| | return;
|
| | }
|
| | writer.Stream() << " count=\"" << _elementMap->size() << "\">\n";
|
| | _elementMap->save(writer.beginCharStream(Base::CharStreamFormat::Raw) << '\n');
|
| | writer.endCharStream() << '\n';
|
| | writer.Stream() << writer.ind() << "</ElementMap2>\n";
|
| | }
|
| |
|
| | void ComplexGeoData::Restore(Base::XMLReader& reader)
|
| | {
|
| | resetElementMap();
|
| |
|
| | reader.readElement("ElementMap");
|
| | bool newTag = false;
|
| | if (reader.hasAttribute("new") && reader.getAttribute<bool>("new")) {
|
| | reader.readEndElement("ElementMap");
|
| | reader.readElement("ElementMap2");
|
| | newTag = true;
|
| | }
|
| |
|
| | const char* file = "";
|
| | if (reader.hasAttribute("file")) {
|
| | file = reader.getAttribute<const char*>("file");
|
| | }
|
| | if (*file != 0) {
|
| | reader.addFile(file, this);
|
| | return;
|
| | }
|
| |
|
| | std::size_t count = 0;
|
| | if (reader.hasAttribute("count")) {
|
| | count = reader.getAttribute<unsigned long>("count");
|
| | }
|
| | if (count == 0) {
|
| | return;
|
| | }
|
| |
|
| | if (newTag) {
|
| | resetElementMap(std::make_shared<ElementMap>());
|
| | _elementMap =
|
| | _elementMap->restore(Hasher, reader.beginCharStream(Base::CharStreamFormat::Raw));
|
| | reader.endCharStream();
|
| | reader.readEndElement("ElementMap2");
|
| | return;
|
| | }
|
| |
|
| | if (reader.FileVersion > 1) {
|
| | restoreStream(reader.beginCharStream(Base::CharStreamFormat::Raw), count);
|
| | reader.endCharStream();
|
| | return;
|
| | }
|
| | readElements(reader, count);
|
| | reader.readEndElement("ElementMap");
|
| | }
|
| |
|
| | void ComplexGeoData::readElements(Base::XMLReader& reader, size_t count)
|
| | {
|
| | size_t invalid_count = 0;
|
| | bool warned = false;
|
| |
|
| | const auto& types = getElementTypes();
|
| |
|
| | for (size_t i = 0; i < count; ++i) {
|
| | reader.readElement("Element");
|
| | ElementIDRefs sids;
|
| | if (reader.hasAttribute("sid")) {
|
| | if (!Hasher) {
|
| | if (!warned) {
|
| | warned = true;
|
| | FC_ERR("missing hasher");
|
| | }
|
| | }
|
| | else {
|
| | const char* attr = reader.getAttribute<const char*>("sid");
|
| | bio::stream<bio::array_source> iss(attr, std::strlen(attr));
|
| | long id {};
|
| | while ((iss >> id)) {
|
| | if (id == 0) {
|
| | continue;
|
| | }
|
| | auto sid = Hasher->getID(id);
|
| | if (!sid) {
|
| | ++invalid_count;
|
| | }
|
| | else {
|
| | sids.push_back(sid);
|
| | }
|
| | char sep {};
|
| | iss >> sep;
|
| | }
|
| | }
|
| | }
|
| | ensureElementMap()->setElementName(IndexedName(reader.getAttribute<const char*>("value"), types),
|
| | MappedName(reader.getAttribute<const char*>("key")),
|
| | Tag,
|
| | &sids);
|
| | }
|
| | if (invalid_count != 0) {
|
| | FC_ERR("Found " << invalid_count << " invalid string id");
|
| | }
|
| | }
|
| |
|
| | void ComplexGeoData::restoreStream(std::istream& stream, std::size_t count)
|
| | {
|
| | resetElementMap();
|
| |
|
| | size_t invalid_count = 0;
|
| | std::string key;
|
| | std::string value;
|
| | std::string sid;
|
| | bool warned = false;
|
| |
|
| | const auto& types = getElementTypes();
|
| | try {
|
| | for (size_t i = 0; i < count; ++i) {
|
| | ElementIDRefs sids;
|
| | std::size_t sCount = 0;
|
| | if (!(stream >> value >> key >> sCount)) {
|
| |
|
| | FC_THROWM(Base::RuntimeError, "Failed to restore element map " << _persistenceName);
|
| | }
|
| | constexpr std::size_t oneGbOfInts {(1 << 30) / sizeof(int)};
|
| | if (sCount > oneGbOfInts) {
|
| |
|
| | FC_THROWM(Base::RuntimeError, "Failed to restore element map (>1GB) " << _persistenceName);
|
| | }
|
| | sids.reserve(static_cast<int>(sCount));
|
| | for (std::size_t j = 0; j < sCount; ++j) {
|
| | long id = 0;
|
| | if (!(stream >> id)) {
|
| |
|
| | FC_THROWM(Base::RuntimeError,
|
| | "Failed to restore element map " << _persistenceName);
|
| | }
|
| | if (Hasher) {
|
| | auto hasherSID = Hasher->getID(id);
|
| | if (!hasherSID) {
|
| | ++invalid_count;
|
| | }
|
| | else {
|
| | sids.push_back(hasherSID);
|
| | }
|
| | }
|
| | }
|
| | if (sCount != 0 && !Hasher) {
|
| | sids.clear();
|
| | if (!warned) {
|
| | warned = true;
|
| | FC_ERR("missing hasher");
|
| | }
|
| | }
|
| | _elementMap->setElementName(IndexedName(value.c_str(), types),
|
| | MappedName(key),
|
| | Tag,
|
| | &sids);
|
| | }
|
| | }
|
| | catch (Base::Exception& e) {
|
| | e.reportException();
|
| | _restoreFailed = true;
|
| | _elementMap.reset();
|
| | }
|
| | if (invalid_count != 0) {
|
| | FC_ERR("Found " << invalid_count << " invalid string id");
|
| | }
|
| | }
|
| |
|
| | void ComplexGeoData::SaveDocFile(Base::Writer& writer) const
|
| | {
|
| | flushElementMap();
|
| | if (_elementMap) {
|
| | writer.Stream() << "BeginElementMap v1\n";
|
| | _elementMap->save(writer.Stream());
|
| | }
|
| | }
|
| |
|
| | void ComplexGeoData::RestoreDocFile(Base::Reader& reader)
|
| | {
|
| | std::string marker;
|
| | std::string ver;
|
| | reader >> marker;
|
| | if (boost::equals(marker, "BeginElementMap")) {
|
| | resetElementMap();
|
| | reader >> ver;
|
| | if (ver != "v1") {
|
| | FC_WARN("Unknown element map format");
|
| | }
|
| | else {
|
| | resetElementMap(std::make_shared<ElementMap>());
|
| | _elementMap = _elementMap->restore(Hasher, reader);
|
| | return;
|
| | }
|
| | }
|
| | auto count = atoll(marker.c_str());
|
| | if (count < 0 || count > std::numeric_limits<int>::max()) {
|
| | FC_THROWM(Base::RuntimeError, "Failed to restore element map " << _persistenceName);
|
| | }
|
| | restoreStream(reader, static_cast<std::size_t>(count));
|
| | }
|
| |
|
| | unsigned int ComplexGeoData::getMemSize() const
|
| | {
|
| | flushElementMap();
|
| | if (_elementMap) {
|
| | static const int multiplier {10};
|
| | return _elementMap->size() * multiplier;
|
| | }
|
| | return 0;
|
| | }
|
| |
|
| | std::vector<IndexedName> ComplexGeoData::getHigherElements(const char*, bool) const
|
| | {
|
| | return {};
|
| | }
|
| |
|
| | void ComplexGeoData::setMappedChildElements(
|
| | const std::vector<Data::ElementMap::MappedChildElements>& children)
|
| | {
|
| |
|
| |
|
| | if (!_elementMap) {
|
| | resetElementMap(std::make_shared<Data::ElementMap>());
|
| | }
|
| | _elementMap->addChildElements(Tag, children);
|
| | }
|
| |
|
| | std::vector<Data::ElementMap::MappedChildElements> ComplexGeoData::getMappedChildElements() const
|
| | {
|
| | if (!_elementMap) {
|
| | return {};
|
| | }
|
| | return _elementMap->getChildElements();
|
| | }
|
| |
|
| | void ComplexGeoData::beforeSave() const
|
| | {
|
| | flushElementMap();
|
| | if (this->_elementMap) {
|
| | this->_elementMap->beforeSave(Hasher);
|
| | }
|
| | }
|
| |
|
| | void ComplexGeoData::hashChildMaps()
|
| | {
|
| | flushElementMap();
|
| | if (_elementMap) {
|
| | _elementMap->hashChildMaps(Tag);
|
| | }
|
| | }
|
| |
|
| | bool ComplexGeoData::hasChildElementMap() const
|
| | {
|
| | flushElementMap();
|
| | return _elementMap && _elementMap->hasChildElementMap();
|
| | }
|
| |
|
| | void ComplexGeoData::dumpElementMap(std::ostream& stream) const
|
| | {
|
| | auto map = getElementMap();
|
| | std::sort(map.begin(), map.end());
|
| | for (auto& element : map) {
|
| | stream << element.index << " : " << element.name << std::endl;
|
| | }
|
| | }
|
| |
|
| | const std::string ComplexGeoData::dumpElementMap() const
|
| | {
|
| | std::stringstream ss;
|
| | dumpElementMap(ss);
|
| | return ss.str();
|
| | }
|
| |
|
| |
|
| |
|