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) {}