From 9ea7b6424be11b7d2f383cc6174e087d5c9de7dc Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Thu, 15 Jun 2023 10:55:38 -0500 Subject: [PATCH] App/Toponaming: ComplexGeoData realthunder original Minor modifications to make it compile with previous refactorings. The only substantial change to the original is moving the getElementHistory function from ComplexGeoData to MappedName so that the dehash function can remain private. --- src/App/ComplexGeoData.cpp | 253 ++++++++++++++++++++++++++++++++++--- src/App/ComplexGeoData.h | 214 ++++++++++++++++++++++++++++--- src/App/ElementMap.cpp | 53 ++++++++ src/App/ElementMap.h | 6 +- src/App/MappedElement.h | 1 - src/App/MappedName.h | 7 +- 6 files changed, 491 insertions(+), 43 deletions(-) diff --git a/src/App/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp index df0c78265b..fe5d07ece6 100644 --- a/src/App/ComplexGeoData.cpp +++ b/src/App/ComplexGeoData.cpp @@ -1,23 +1,26 @@ -/*************************************************************************** - * Copyright (c) 2002 Jürgen Riegel * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library 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 Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2002 Jürgen Riegel * + * Copyright (c) 2022 Zheng, Lei * + * Copyright (c) 2023 FreeCAD Project Association * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * ***************************************************************************/ @@ -30,11 +33,16 @@ #include #include "ComplexGeoData.h" +#include "ElementMap.h" +#include "ElementNamingUtils.h" #include #include #include +#include +#include + using namespace Data; @@ -43,6 +51,11 @@ 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() :Tag(0) @@ -166,3 +179,203 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d&) const return false; } +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 MappedName(); + flushElementMap(); + if(!_elementMap) { + if (allowUnmapped) + return MappedName(element); + return MappedName(); + } + + 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 s; + return IndexedName(name.appendToBuffer(s), 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 MappedElement(getMappedName(element, false, sid), element); + + const char * mapped = isMappedElement(name); + if (mapped) + name = mapped; + + MappedElement res; + // Strip out the trailing '.XXXX' if any + const char *dot = strchr(name,'.'); + if(dot) + res.name = MappedName(name, dot-name); + else if (copy) + res.name = name; + else + res.name = MappedName(name); + res.index = getIndexedName(res.name, sid); + return res; +} + +std::vector > +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())}; +} + +std::vector +ComplexGeoData::getElementNamesWithPrefix(const char *prefix) const { +#if 0 + std::vector names; + flushElementMap(); + if(!prefix || !prefix[0] || !_elementMap) + return names; + const auto &p = elementMapPrefix(); + if(boost::starts_with(prefix,p)) + prefix += p.size(); + names = _elementMap->findAllStartsWith(prefix); + return names; +#else + (void)prefix; + return {}; +#endif +} + +std::vector ComplexGeoData::getElementMap() const { + flushElementMap(); + if(!_elementMap) + return {}; + return _elementMap->getAll(); +} + +ElementMapPtr ComplexGeoData::elementMap(bool flush) const +{ + if (flush) + flushElementMap(); + return _elementMap; +} + +void ComplexGeoData::flushElementMap() const +{ +} + +void ComplexGeoData::setElementMap(const std::vector &map) { + resetElementMap(); + for(auto &v : map) + _elementMap->setElementName(v.index, v.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(0,0,0,&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 n; + const char *dot = strchr(name,'.'); + if(dot) { + n = MappedName(name, dot-name); + type = dot+1; + } + else + n = MappedName::fromRawData(name); + char res = elementType(n); + if (res) + return res; + } + + if(type && type[0]) { + for(auto &t : getElementTypes()) { + if(boost::starts_with(type, t)) + return type[0]; + } + } + return 0; +} + +MappedName ComplexGeoData::renameDuplicateElement(int index, + const IndexedName & element, + const IndexedName & element2, + const MappedName & name, + ElementIDRefs &sids) +{ + std::ostringstream ss; + ss << ELEMENT_MAP_PREFIX << 'D' << std::hex << index; + MappedName renamed(name); + this->elementMap()->encodeElementName(element.getType()[0],renamed,ss,&sids,Tag); + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("duplicate element mapping '" << name << " -> " << renamed << ' ' + << element << '/' << element2); + return renamed; +} diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index ae59fde241..e7aa240e94 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -1,23 +1,26 @@ -/*************************************************************************** - * Copyright (c) 2002 Jürgen Riegel * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library 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 Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2002 Jürgen Riegel * + * Copyright (c) 2022 Zheng, Lei * + * Copyright (c) 2023 FreeCAD Project Association * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * ***************************************************************************/ @@ -28,6 +31,10 @@ #include #include #include +#include "MappedName.h" +#include "MappedElement.h" +#include "ElementMap.h" +#include "StringHasher.h" #ifdef __GNUC__ # include @@ -45,6 +52,8 @@ using BoundBox3d = BoundBox3; namespace Data { +struct MappedChildElements; + /** Segments * Subelement type of the ComplexGeoData type * It is used to split an object in further sub-parts. @@ -164,6 +173,135 @@ public: virtual bool getCenterOfGravity(Base::Vector3d& center) const; //@} + + /** @name Element name mapping */ + //@{ + + /** Get element indexed name + * + * @param name: the input name + * @param sid: optional output of and App::StringID involved forming this mapped name + * + * @return Returns an indexed name. + */ + IndexedName getIndexedName(const MappedName & name, + ElementIDRefs *sid = nullptr) const; + + /** Get element mapped name + * + * @param name: the input name + * @param allowUnmapped: If the queried element is not mapped, then return + * an empty name if \c allowUnmapped is false, or + * else, return the indexed name. + * @param sid: optional output of and App::StringID involved forming this mapped name + * @return Returns the mapped name. + */ + MappedName getMappedName(const IndexedName & element, + bool allowUnmapped = false, + ElementIDRefs *sid = nullptr) const; + + /** Return a pair of indexed name and mapped name + * + * @param name: the input name. + * @param sid: optional output of and App::StringID involved forming this + * mapped name + * @param copy: if true, copy the name string, or else use it as constant + * string, and caller must make sure the memory is not freed. + * + * @return Returns the MappedElement which contains both the indexed and + * mapped name. + * + * This function guesses whether the input name is an indexed name or + * mapped, and perform a lookup and return the names found. If the input + * name contains only alphabets and underscore followed by optional digits, + * it will be treated as indexed name. Or else, it will be treated as + * mapped name. + */ + MappedElement getElementName(const char * name, + ElementIDRefs *sid = nullptr, + bool copy = false) const; + + /** Get mapped element with a given prefix */ + std::vector getElementNamesWithPrefix(const char *prefix) const; + + /** Get mapped element names + * + * @param element: original element name with \c Type + \c Index + * @param needUnmapped: if true, return the original element name if no + * mapping is found + * + * @return a list of mapped names of the give element along with their + * associated string ID references + */ + std::vector > + getElementMappedNames(const IndexedName & element, bool needUnmapped=false) const; + + /// Append the Tag (if and only if it is non zero) into the element map + virtual void reTagElementMap(long tag, App::StringHasherRef hasher, const char *postfix=0) { + (void)tag; + (void)hasher; + (void)postfix; + } + + // NOTE: getElementHistory is now in ElementMap + + char elementType(const Data::MappedName &) const; + char elementType(const Data::IndexedName &) const; + char elementType(const char *name) const; + + /** Reset/swap the element map + * + * @param elementMap: optional new element map + * + * @return Returns the existing element map. + */ + virtual ElementMapPtr resetElementMap(ElementMapPtr elementMap=ElementMapPtr()) { + _elementMap.swap(elementMap); + return elementMap; + } + + /// Get the entire element map + std::vector getElementMap() const; + + /// Set the entire element map + void setElementMap(const std::vector &elements); + + /// Get the current element map size + size_t getElementMapSize(bool flush=true) const; + + /// Check if the given subname only contains an element name + static bool isElementName(const char *subname) { + return subname && *subname && findElementName(subname)==subname; + } + + /** Element trace callback + * + * The callback has the following call signature + * (const std::string &name, size_t offset, long encodedTag, long tag) -> bool + * + * @param name: the current element name. + * @param offset: the offset skipping the encoded element name for the next iteration. + * @param encodedTag: the tag encoded inside the current element, which is usually the tag + * of the previous step in the shape history. + * @param tag: the tag of the current shape element. + * + * @sa traceElement() + */ + using TraceCallback = std::function; + + /** Iterate through the history of the give element name with a given callback + * + * @param name: the input element name + * @param cb: trace callback with call signature. + * @sa TraceCallback + */ + void traceElement(const MappedName &name, TraceCallback cb) const; + + /** Flush an internal buffering for element mapping */ + virtual void flushElementMap() const; + virtual unsigned long getElementMapReserve() const { return 0; } + //@} + protected: /// from local to outside @@ -223,6 +361,42 @@ protected: } public: mutable long Tag; + + +public: + /// String hasher for element name shortening + mutable App::StringHasherRef Hasher; + +protected: + virtual MappedName renameDuplicateElement(int index, + const IndexedName & element, + const IndexedName & element2, + const MappedName & name, + ElementIDRefs &sids); + + /// from local to outside + inline Base::Vector3d transformToOutside(const Base::Vector3f& vec) const + { + return getTransform() * Base::Vector3d(static_cast(vec.x), + static_cast(vec.y), + static_cast(vec.z)); + } + /// from local to inside + inline Base::Vector3f transformToInside(const Base::Vector3d& vec) const + { + Base::Matrix4D tmpM(getTransform()); + tmpM.inverse(); + Base::Vector3d tmp = tmpM * vec; + return Base::Vector3f(static_cast(tmp.x), + static_cast(tmp.y), + static_cast(tmp.z)); + } + +protected: + ElementMapPtr elementMap(bool flush=true) const; + +private: + ElementMapPtr _elementMap; }; } //namespace App diff --git a/src/App/ElementMap.cpp b/src/App/ElementMap.cpp index 9f8a98fdd9..cc22f6b569 100644 --- a/src/App/ElementMap.cpp +++ b/src/App/ElementMap.cpp @@ -1155,5 +1155,58 @@ std::vector ElementMap::getAll() const return ret; } +long ElementMap::getElementHistory(const MappedName & name, + long masterTag, + MappedName *original, + std::vector *history) const +{ + long tag = 0; + int len = 0; + int pos = name.findTagInElementName(&tag,&len,nullptr,nullptr,true); + if(pos < 0) { + if(original) + *original = name; + return tag; + } + if(!original && !history) + return tag; + + MappedName tmp; + MappedName &ret = original?*original:tmp; + if(name.startsWith(ELEMENT_MAP_PREFIX)) { + unsigned offset = ELEMENT_MAP_PREFIX_SIZE; + ret = MappedName::fromRawData(name, offset); + } else + ret = name; + + while(1) { + if(!len || len>pos) { + FC_WARN("invalid name length " << name); + return 0; + } + bool dehashed = false; + if (ret.startsWith(MAPPED_CHILD_ELEMENTS_PREFIX, len)) { + int offset = (int)POSTFIX_TAG_SIZE; + MappedName tmp = MappedName::fromRawData(ret, len+offset, pos-len-offset); + MappedName postfix = dehashElementName(tmp); + if (postfix != tmp) { + dehashed = true; + ret = MappedName::fromRawData(ret, 0, len) + postfix; + } + } + if (!dehashed) + ret = dehashElementName(MappedName::fromRawData(ret, 0, len)); + + long tag2 = 0; + pos = ret.findTagInElementName(&tag2,&len,nullptr,nullptr,true); + if(pos < 0 || (tag2!=tag && tag2!=-tag && tag!=masterTag && -tag!=masterTag)) + return tag; + tag = tag2; + if(history) + history->push_back(ret.copy()); + } +} + + }// Namespace Data diff --git a/src/App/ElementMap.h b/src/App/ElementMap.h index ff17a1bccb..aea3e9ff22 100644 --- a/src/App/ElementMap.h +++ b/src/App/ElementMap.h @@ -170,7 +170,7 @@ public: QByteArray postfix; ElementIDRefs sids; - // prefix() has been moved to PostfixStringReferences.h + // prefix() has been moved to ElementNamingUtils.h }; /* Note: the original addChildElements passed `ComplexGeoData& master` for getting the `Tag`, @@ -181,6 +181,10 @@ public: std::vector getAll() const; + long getElementHistory(const MappedName & name, + long masterTag, + MappedName *original=0, std::vector *history=0) const; + private: /** Serialize this map * @param s: serialized stream diff --git a/src/App/MappedElement.h b/src/App/MappedElement.h index ef5784d7d9..7f6ee68f01 100644 --- a/src/App/MappedElement.h +++ b/src/App/MappedElement.h @@ -23,7 +23,6 @@ #ifndef APP_MAPPED_ELEMENT_H #define APP_MAPPED_ELEMENT_H -#include "ComplexGeoData.h" #include "IndexedName.h" #include "MappedName.h" diff --git a/src/App/MappedName.h b/src/App/MappedName.h index 184a763b87..54a58ea19f 100644 --- a/src/App/MappedName.h +++ b/src/App/MappedName.h @@ -35,7 +35,6 @@ #include #include -#include "ComplexGeoData.h" #include "IndexedName.h" #include "StringHasher.h" #include "ElementNamingUtils.h" @@ -102,6 +101,12 @@ public: } } + explicit MappedName(const App::StringIDRef & sid) + :raw(false) + { + sid.toBytes(this->data); + } + MappedName() : raw(false) {}