diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index cc6446d344..ade8839d18 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -57,18 +57,21 @@ namespace Data { // struct MappedChildElements; + /// Option for App::GeoFeature::searchElementCache() enum class SearchOption { - /// Whether to compare shape geometry - CheckGeometry = 1, - SingleResult = 2, + CheckGeometry = 1, ///< Whether to compare shape geometry + SingleResult = 2, ///< Stop at first found result }; + typedef Base::Flags SearchOptions; -/** Segments - * Sub-element type of the ComplexGeoData type - * It is used to split an object in further sub-parts. +/** + * @brief A class for segments. + * + * A segment is a sub-element type of the ComplexGeoData type. It is used to + * split an object in further sub-parts. */ class AppExport Segment: public Base::BaseClass { @@ -76,200 +79,356 @@ class AppExport Segment: public Base::BaseClass public: ~Segment() override = default; + + /// Get the name of the segment. virtual std::string getName() const = 0; }; -enum ElementMapResetPolicy -{ - AllowNoMap, - ForceEmptyMap -}; - -/** ComplexGeoData Object +/** + * @brief A class for complex geometric data. + * @ingroup ElementMapping */ class AppExport ComplexGeoData: public Base::Persistence, public Base::Handled { TYPESYSTEM_HEADER_WITH_OVERRIDE(); // NOLINT public: + /** + * @brief A line as a facet boundary in a 3D mesh. + * + * The line is represented by two point indices. + */ struct Line { uint32_t I1; uint32_t I2; }; + + /** + * @brief A triangular facet in a 3D mesh. + * + * The facet is represented by three point indices. + */ struct Facet { uint32_t I1; uint32_t I2; uint32_t I3; }; + + /** + * @brief A domain in a 3D mesh. + * + * A domain consists of a list of points and a list of facets where the + * indices of a facet index into the points list. + */ struct Domain { std::vector points; std::vector facets; }; - /// Constructor ComplexGeoData(); - /// Destructor ~ComplexGeoData() override = default; - /** @name Sub-element management */ - //@{ - /** Sub type list - * List of different sub-element types - * its NOT a list of the sub-elements itself + /** + * @name Sub-element management + * @{ + */ + + /** + * @brief Get a list of sub-element types. + * + * This list does not contain the sub-elements themselves. */ virtual std::vector getElementTypes() const = 0; + + /** + * @brief Get the number of sub-elements of a given type. + * + * @param[in] Type The type of sub-element. + * @return The number of sub-elements of the given type. + */ virtual unsigned long countSubElements(const char* Type) const = 0; - /// Returns a generic element type and index. The determined element type isn't - /// necessarily supported by this geometry. + + /** + * @brief Get the type and index from a combined name. + * + * An example is "Edge12", which would return the type "Edge" and the + * index 12. + * + * @param[in] Name The combined name of the sub-element. + * @returns A pair of a generic element type and index. + * + * @note The determined element type isn't necessarily supported by this + * geometry. + */ static std::pair getTypeAndIndex(const char* Name); - /// get the sub-element by type and number + + /// Get the sub-element by type and number. virtual Segment* getSubElement(const char* Type, unsigned long) const = 0; - /// get sub-element by combined name + + /// Get sub-element by combined name virtual Segment* getSubElementByName(const char* Name) const; - /** Get lines from segment */ - virtual void getLinesFromSubElement(const Segment*, + + /** + * @brief Get the lines from a segment. + * + * @param[in] segment The segment to get the lines from. + * + * @param[in,out] Points The list of points used by the lines. + * @param[in,out] lines The list of lines retrieved from the segment. + */ + virtual void getLinesFromSubElement(const Segment* segment, std::vector& Points, std::vector& lines) const; - /** Get faces from segment */ - virtual void getFacesFromSubElement(const Segment*, + + /** + * @brief Get the faces from a segment. + * + * @param[in] segment The segment to get the faces from. + * @param[in,out] Points The list of points used by the faces. + * @param[in,out] PointNormals The list of point normals used by the faces. + * @param[in,out] faces The list of faces retrieved from the segment. + */ + virtual void getFacesFromSubElement(const Segment* segment, std::vector& Points, std::vector& PointNormals, std::vector& faces) const; - //@} + /// @} - /** @name Placement control */ - //@{ - /** Applies an additional transformation to the current transformation. */ + /** + * @name Placement control + * + * @{ + */ + + /// Apply an additional transformation to the current transformation. void applyTransform(const Base::Matrix4D& rclTrf); - /** Applies an additional translation to the current transformation. */ + + /// Apply an additional translation to the current transformation. void applyTranslation(const Base::Vector3d&); - /** Applies an additional rotation to the current transformation. */ + + /// Applies an additional rotation to the current transformation. void applyRotation(const Base::Rotation&); - /** Override the current transformation with a placement - * using the setTransform() method. + + /** + * @brief Override the current transformation with a placement. + * + * Override the current transformation with a placement using the + * setTransform() method. + * + * @param[in] rclPlacement The new placement to set. */ void setPlacement(const Base::Placement& rclPlacement); - /** Return the current transformation as placement using - * getTransform(). + + /** + * @brief Get the current transformation as placement. + * + * @return The current transformation as placement using getTransform(). */ Base::Placement getPlacement() const; - /** Override the current transformation with the new one. - * This method has to be handled by the child classes. - * the actual placement and matrix is not part of this class. + + /** + * @brief Override the current transformation. + * + * Override the current transformation with the new one. This method has + * to be handled by the child classes. the actual placement and matrix is + * not part of this class. + * + * @param[in] rclTrf The new transformation matrix to set. */ + virtual void setTransform(const Base::Matrix4D& rclTrf) = 0; - /** Return the current matrix - * This method has to be handled by the child classes. - * the actual placement and matrix is not part of this class. + + /** + * @brief Get the current transformation matrix. + * + * This method has to be handled by the child classes. The actual + * placement and matrix is not part of this class. */ virtual Base::Matrix4D getTransform() const = 0; - //@} + /// @} - /** @name Modification */ - //@{ - /// Applies a transformation on the real geometric data type + /** + *@name Modification + * + * @{ + */ + + /** + * @brief Applies a transformation on the real geometric data type. + * + * @param[in] rclMat The transformation matrix to apply. + */ virtual void transformGeometry(const Base::Matrix4D& rclMat) = 0; - //@} + /// @} - /** @name Getting basic geometric entities */ - //@{ - /// Get the standard accuracy to be used with getPoints, getLines or getFaces + /** + * @name Getting basic geometric entities + * + * @{ + */ + + /** + * @brief Get the standard accuracy for meshing. + * + * Get the standard accuracy to be used with getPoints(), getLines() or + * getFaces(). + * + * @return The standard accuracy. + */ virtual double getAccuracy() const; + /// Get the bound box virtual Base::BoundBox3d getBoundBox() const = 0; - /** Get point from line object intersection */ + + /** + * @brief Get point from line object intersection. + * + * @param[in] base The base point of the line. + * @param[in] dir The direction of the line. + * + * @return The intersection point. + */ virtual Base::Vector3d getPointFromLineIntersection(const Base::Vector3f& base, const Base::Vector3f& dir) const; - /** Get points from object with given accuracy */ + + /** + * @brief Get points from object with given accuracy. + * + * @param[in,out] Points The list of points retrieved from the object. + * @param[in,out] Normals The list of normals associated with faces + * retrieved from the object. If there are no faces, then this list will + * be empty. + * @param[in] Accuracy The accuracy to use when retrieving the points. + * @param[in] flags Additional flags for point retrieval. + */ virtual void getPoints(std::vector& Points, std::vector& Normals, double Accuracy, uint16_t flags = 0) const; - /** Get lines from object with given accuracy */ + + /** + * @brief Get lines from object with given accuracy + * + * @param[in,out] Points The list of points retrieved from the object. + * @param[in,out] lines The list of lines retrieved from the object. + * @param[in] Accuracy The accuracy to use when retrieving the lines. + * @param[in] flags Additional flags for line retrieval. + */ virtual void getLines(std::vector& Points, std::vector& lines, double Accuracy, uint16_t flags = 0) const; - /** Get faces from object with given accuracy */ + + /** + * @brief Get faces from object with given accuracy. + * + * @param[in,out] Points The list of points retrieved from the object. + * @param[in,out] faces The list of faces retrieved from the object. + * @param[in] Accuracy The accuracy to use when retrieving the faces. + * @param[in] flags Additional flags for face retrieval. + */ virtual void getFaces(std::vector& Points, std::vector& faces, double Accuracy, uint16_t flags = 0) const; - /** Get the center of gravity - * If this method is implemented then true is returned and the center of gravity. - * The default implementation only returns false. + + /** + * @brief Get the center of gravity. + * + * @param[out] center The center of gravity. + * + * @return True if this method is implemented. The default implementation returns false. */ virtual bool getCenterOfGravity(Base::Vector3d& center) const; - virtual std::optional centerOfGravity() const; - //@} + /** + * @brief Get the center of gravity. + * + * @return The center of gravity if available. + */ + virtual std::optional centerOfGravity() const; + /// @} + + /// Get the element map prefix. static const std::string& elementMapPrefix(); - /** @name Element name mapping */ - //@{ - - /** Get element indexed name + /** + * @name Element name mapping * - * @param name: the input name - * @param sid: optional output of and App::StringID involved forming this mapped name + * @{ + */ + + /** + * @brief Get the element's indexed name. + * + * @param[in] name The mapped name. + * + * @param[out] 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 + /** + * @brief Get the element's 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. + * @param[in] element The indexed name name of the element. + * @param[in] allowUnmapped If the queried element is not mapped, then + * return an empty name if @p allowUnmapped is false, or else, return the + * indexed name. + * @param sid Optional output of and App::StringID involved forming this + * mapped name. + * + * @return 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 any App::StringID involved in 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. + /** + * @brief Get a pair of an 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 + * mapped one, performs a lookup, and returns 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 + * it will be treated as indexed name. Otherwise, it will be treated as a * mapped name. + * + * @param[in] name The input name. + * @param[out] sid Optional output of any App::StringID involved in forming + * this mapped name. + * @param[in] 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 The MappedElement that contains both the indexed and mapped + * name. */ MappedElement getElementName(const char* name, ElementIDRefs* sid = nullptr, bool copy = false) const; - /** Add a sub-element name mapping. - * - * @param element: the original \c Type + \c Index element name - * @param name: the mapped sub-element name. May or may not start with - * elementMapPrefix(). - * @param sid: in case you use a hasher to hash the element name, pass in - * the string id reference using this parameter. You can have more than one - * string id associated with the same name. - * @param overwrite: if true, it will overwrite existing names - * - * @return Returns the stored mapped element name. + /** + * @brief Add a sub-element name mapping. * * An element can have multiple mapped names. However, a name can only be * mapped to one element * - * Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access, - * now you must pass in `long masterTag` explicitly. + * @param[in] element The original @c Type + @c Index element name. + * @param[in] name The mapped sub-element name. It may or may not start + * with elementMapPrefix(). + * @param[in] masterTag The master tag of the element. + * @param[in] sid In case you use a hasher to hash the element name, pass + * in the string id reference using this parameter. You can have more than + * one string id associated with the same name. + * @param[in] overwrite If true, it will overwrite existing names. + * + * @return The stored mapped element name. + * + * @note The original function was in the context of ComplexGeoData, which + * provided `Tag` access, now you must pass in `long masterTag` explicitly. */ MappedName setElementName(const IndexedName& element, const MappedName& name, @@ -280,30 +439,42 @@ public: return _elementMap->setElementName(element, name, masterTag, sid, overwrite); } + /// Check if there is an element map. bool hasElementMap() const { return _elementMap != nullptr; } - /** Get mapped element names + /** + * @brief 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 + * @param[in] element The original element name with @c Type + @c Index. + * @param[in] 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 + * @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; - /// Hash the child element map postfixes to shorten element name from hierarchical maps + /** + * @brief Hash the child element map postfixes. + * + * The hashing is done to shorten element names from hierarchical maps. + */ void hashChildMaps(); - /// Check if there is child element map + /// Check if there is child element map. bool hasChildElementMap() const; - /// Append the Tag (if and only if it is non zero) into the element map + /** + * @brief Append the Tag (if and only if it is non zero) into the element map. + * + * @param[in] tag The master tag to append. + * @param[in] hasher The string hasher to use. + * @param[in] postfix An optional postfix to append after the tag. + */ virtual void reTagElementMap(long tag, App::StringHasherRef hasher, const char* postfix = nullptr) { @@ -312,7 +483,17 @@ public: (void)postfix; } - // NOTE: getElementHistory is now in ElementMap + /** + * @brief Get the history of an element name. + * + * @param[in] name The mapped element name to query. + * @param[out] original Optional output parameter to store the original + * element name. + * @param[out] history Optional output parameter to store the history of + * element names. + * + * @note This function is now in ElementMap. + */ long getElementHistory(const MappedName& name, MappedName* original = nullptr, std::vector* history = nullptr) const @@ -323,49 +504,68 @@ public: return 0; }; + /// Set the mapped child elements. void setMappedChildElements(const std::vector& children); + + /// Get the mapped child elements. std::vector getMappedChildElements() const; + /// Get the element type from a mapped name. char elementType(const Data::MappedName&) const; + + /// Get the element type from an indexed name. char elementType(const Data::IndexedName&) const; + + /// Get the element type from a raw name. char elementType(const char* name) const; - /** Reset/swap the element map + /** + * @brief Reset/swap the element map. * - * @param elementMap: optional new element map + * @param[in] elementMap: optional new element map. * - * @return Returns the existing element map. + * @return The existing element map. */ virtual ElementMapPtr resetElementMap(ElementMapPtr elementMap = ElementMapPtr()); - /// Get the entire element map + /// Get the entire element map. std::vector getElementMap() const; - /// Set the entire element map + /// Set the entire element map. void setElementMap(const std::vector& elements); - /// Get the current element map size + /// Get the current element map size. size_t getElementMapSize(bool flush = true) const; - /// Return the higher level element names of the given element + /** + * @brief Get the higher level element names of the given element. + * + * @param name: the input element name. + * @param silent: if true, suppress throwing exceptions. + */ virtual std::vector getHigherElements(const char* name, bool silent = false) const; - /// Return the current element map version + /// Get the current element map version. virtual std::string getElementMapVersion() const; - /// Return true to signal element map version change + /// Check the element map version. virtual bool checkElementMapVersion(const char* ver) const; - /// Check if the given sub-name only contains an element name + /// Check if the given sub-name only contains an element name. static bool isElementName(const char* subName) { return (subName != nullptr) && (*subName != 0) && findElementName(subName) == subName; } - /** Iterate through the history of the give element name with a given callback + /** + * @brief Iterate through the history given an element name. + * + * Iterate through the history of the given element name with a given + * callback. + * + * @param[in] name: the input element name + * @param[in] cb: trace callback function. * - * @param name: the input element name - * @param cb: trace callback with call signature. * @sa TraceCallback */ void traceElement(const MappedName& name, TraceCallback cb) const @@ -373,42 +573,62 @@ public: _elementMap->traceElement(name, Tag, std::move(cb)); } - /** Flush an internal buffering for element mapping */ + /// Flush internal buffers for element mapping. virtual void flushElementMap() const; - //@} + /// @} + + /** + * @name Save/restore + * + * @{ + */ - /** @name Save/restore */ - //@{ void Save(Base::Writer& writer) const override; void Restore(Base::XMLReader& reader) override; void SaveDocFile(Base::Writer& writer) const override; void RestoreDocFile(Base::Reader& reader) override; unsigned int getMemSize() const override; + + /// Set the filename for persistence. void setPersistenceFileName(const char* name) const; + + /// Called before saving. virtual void beforeSave() const; + + /// Check if restore has failed. bool isRestoreFailed() const { return _restoreFailed; } + + /// Reset the restore failure flag. void resetRestoreFailure() const { _restoreFailed = true; } - //@} + /// @} /** - * Debugging method to dump an entire element map in human readable form to a stream - * @param stream + * @brief Dump the entire element map. + * + * Debugging method to dump an entire element map in human readable form to a stream. + * + * @param[in,out] stream The output stream. */ void dumpElementMap(std::ostream& stream) const; + /** - * Debugging method to dump an entire element map in human readable form into a string - * @return The string + * @brief Dump the entire element map to a string. + * + * Debugging method to dump an entire element map in human readable form + * into a string. + * + * @return The string with the element map. */ const std::string dumpElementMap() const; protected: - /// from local to outside + /// Transform the point from local to outside. inline Base::Vector3d transformPointToOutside(const Base::Vector3f& vec) const { // clang-format off @@ -417,7 +637,8 @@ protected: static_cast(vec.z)); // clang-format on } - /// from local to outside + + /// Transform the points from local to outside. template inline std::vector transformPointsToOutside(const std::vector& input) const { @@ -435,6 +656,8 @@ protected: return output; // clang-format on } + + /// Transform the vector from local to outside. inline Base::Vector3d transformVectorToOutside(const Base::Vector3f& vec) const { // clang-format off @@ -445,6 +668,8 @@ protected: static_cast(vec.z)); // clang-format on } + + /// Transform the vectors from local to outside. template std::vector transformVectorsToOutside(const std::vector& input) const { @@ -463,7 +688,8 @@ protected: return output; // clang-format on } - /// from local to inside + + /// Transform the point from local to inside. inline Base::Vector3f transformPointToInside(const Base::Vector3d& vec) const { Base::Matrix4D tmpM(getTransform()); @@ -475,44 +701,48 @@ protected: } public: + /** + * @brief The master tag. + * + * The master tag is used to identify the shape to its owner document + * object. It should be unique within the document. A tag of zero meanss + * that automatic element mapping is disabled. + */ mutable long Tag {0}; /// String hasher for element name shortening mutable App::StringHasherRef Hasher; protected: + /** + * @brief Restore the element map from a stream. + * + * @param[in,out] stream The input stream. + * @param[in] count The number of items to restore. + */ void restoreStream(std::istream& stream, std::size_t count); + + /** + * @brief Read the elements from an XML reader. + * + * @param[in,out] reader The XML reader. + * @param[in] count The number of elements to read. + */ void readElements(Base::XMLReader& reader, size_t count); - /// from local to outside - inline Base::Vector3d transformToOutside(const Base::Vector3f& vec) const - { - // clang-format off - return getTransform() * Base::Vector3d(static_cast(vec.x), - static_cast(vec.y), - static_cast(vec.z)); - // clang-format on - } - /// 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: + /// Get the element map. ElementMapPtr elementMap(bool flush = true) const; + /// Ensure there is an element map. ElementMapPtr ensureElementMap(bool flush = true); private: ElementMapPtr _elementMap; protected: + /// The persistence file name. mutable std::string _persistenceName; + /// Flag to indicate restore failure. mutable bool _restoreFailed = false; }; diff --git a/src/App/Document.h b/src/App/Document.h index 7a647d2b50..7347ff7513 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -465,14 +465,14 @@ public: /** * @brief Add an object of a given type to the document. * - * Add an object of of a given type with @p pObjectName that should be - * ASCII to this document and set it active. Unicode names are set through - * the Label property. + * Add an object of a given type with @p pObjectName that should be ASCII + * to this document and set it active. Unicode names are set through the + * Label property. * * @tparam T The type of created object. - * @param[in] pObjectName if `nullptr` generate a new unique name based on @p - * T, otherwise use this name. - * @param[in] isNew if `false don't call the DocumentObject::setupObject() + * @param[in] pObjectName if `nullptr` generate a new unique name based on + * @p T, otherwise use this name. + * @param[in] isNew if `false` don't call the DocumentObject::setupObject() * callback (default * is true) * @param[in] viewType override object's view provider name * @param[in] isPartial indicate if this object is meant to be partially loaded diff --git a/src/App/ElementMap.h b/src/App/ElementMap.h index 29c7c2ab9a..f1a3f0154f 100644 --- a/src/App/ElementMap.h +++ b/src/App/ElementMap.h @@ -45,80 +45,108 @@ namespace Data class ElementMap; using ElementMapPtr = std::shared_ptr; -/** Element trace callback +/** + * @brief The 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. + * @param[in] name The current element name. + * + * @param[in] offset The offset skipping the encoded element name for the next + * iteration. + * + * @param[in] encodedTag The tag encoded inside the current element, which is + * usually the tag of the previous step in the shape history. + * + * @param[in] tag The tag of the current shape element. * * @sa traceElement() */ typedef std::function TraceCallback; -/* This class provides for ComplexGeoData's ability to provide proper naming. +/** + * @brief A class to manage element name mapping. + * @ingroup ElementMapping + * + * This class provides for ComplexGeoData's ability to provide proper naming. * Specifically, ComplexGeoData uses this class for it's `_id` property. * Most of the operations work with the `indexedNames` and `mappedNames` maps. - * `indexedNames` maps a string to both a name queue and children. - * each of those children store an IndexedName, offset details, postfix, ids, and - * possibly a recursive elementmap - * `mappedNames` maps a MappedName to a specific IndexedName. + * + * - `indexedNames` maps a string to both a name queue and children. Each of + * those children store an IndexedName, offset details, postfix, ids, and + * possibly a recursive elementmap. + * - `mappedNames` maps a MappedName to a specific IndexedName. */ class AppExport ElementMap : public std::enable_shared_from_this // TODO can remove shared_from_this? { public: - /** Default constructor: hooks internal functions to \c signalSaveDocument and - * \c signalStartRestoreDocument. This is related to the save and restore process - * of the map. + /** + * @brief Construct an element map. + * + * Default constructor: hooks internal functions to \c signalSaveDocument + * and \c signalStartRestoreDocument. This is related to the save and + * restore process of the map. */ ElementMap(); - /** Ensures that naming is properly assigned. It then marks as "used" all the StringID - * that are used to make up this particular map and are stored in the hasherRef passed - * as a parameter. Finally do this recursively for all childEelementMaps as well. + /** + * @brief Prepare this map for saving. * - * @param hasherRef where all the StringID needed to build the map are stored. + * Ensures that naming is properly assigned. It then marks as "used" all + * the @ref App::StringID "StringIDs" that are used to make up this + * particular map and are stored in the hasherRef passed as a + * parameter. Finally do this recursively for all childEelementMaps as + * well. + * + * @param hasherRef Where all the @ref App::StringID "StringIDs" that are + * needed to build the map are stored. */ // FIXME this should be made part of \c save, to achieve symmetry with the restore method void beforeSave(const ::App::StringHasherRef& hasherRef) const; - /** Serialize this map. Calls \c collectChildMaps to get \c childMapSet and - * \c postfixMap, then calls the other (private) save function with those parameters. - * @param stream: serialized stream + /** + * @brief Serialize this map. + * + * Serialize this map. Calls @c collectChildMaps to get @c childMapSet and + * @c postfixMap, then calls the other (private) save function with those + * parameters. + * + * @param[in,out] stream The stream to serialize to. */ void save(std::ostream& stream) const; - /** Deserialize and restore this map. This function restores \c childMaps and - * \c postfixes from the stream, then calls the other (private) restore function with those - * parameters. - * @param hasherRef: where all the StringIDs are stored - * @param stream: stream to deserialize + /** + * @brief Deserialize and restore this map. + * + * This function restores @c childMaps and @c postfixes from the stream, + * then calls the other (private) restore function with those parameters. + * + * @param[in] hasherRef Where all the StringIDs are stored. + * @param[in,out] stream The stream to deserialize from. */ ElementMapPtr restore(::App::StringHasherRef hasherRef, std::istream& stream); - - /** Add a sub-element name mapping. - * - * @param element: the original \c Type + \c Index element name - * @param name: the mapped sub-element name. May or may not start with - * elementMapPrefix(). - * @param sid: in case you use a hasher to hash the element name, pass in - * the string id reference using this parameter. You can have more than one - * string id associated with the same name. - * @param overwrite: if true, it will overwrite existing names - * - * @return Returns the stored mapped element name. + /** + * @brief Add a sub-element name mapping. * * An element can have multiple mapped names. However, a name can only be * mapped to one element * - * Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access, - * now you must pass in `long masterTag` explicitly. + * @param[in] element The original @c Type + @c Index element name. + * @param[in] name The mapped sub-element name. May or may not start with + * elementMapPrefix(). + * @param[in] masterTag The master tag of the element. + * @param[in] sid In case you use a hasher to hash the element name, pass + * in the string id reference using this parameter. You can have more than + * one string id associated with the same name. + * @param[in] overwrite If true, it will overwrite existing names. + * + * @return The stored mapped element name. + * + * @note The original function was in the context of ComplexGeoData, which + * provided `Tag` access, now you must pass in `long masterTag` explicitly. */ MappedName setElementName(const IndexedName& element, const MappedName& name, @@ -126,12 +154,22 @@ public: const ElementIDRefs* sid = nullptr, bool overwrite = false); - /* Generates a new MappedName from the current details. + /** + * @brief Generates a new MappedName from the current details. * * The result is streamed to `ss` and stored in `name`. * - * Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access, - * now you must pass in `long masterTag` explicitly. + * @param[in] element_type The element type character. + * @param[in,out] name The mapped name to encode. + * @param[in,out] ss The output stream to write the encoded name to. + * @param[in,out] sids The string id references that make up the name. + * @param[in] masterTag The master tag of the element. + * @param[in] postfix Optional postfix to append to the name. + * @param[in] tag Optional tag to encode in the name. + * @param[in] forceTag If true, encode the tag. + * + * @note The original function was in the context of ComplexGeoData, which + * provided `Tag` access, now you must pass in `long masterTag` explicitly. */ void encodeElementName(char element_type, MappedName& name, @@ -142,20 +180,47 @@ public: long tag = 0, bool forceTag = false) const; - /// Remove \c name from the map + /// Remove @p name from the map. void erase(const MappedName& name); - /// Remove \c idx and all the MappedNames associated with it + /// Remove @p idx and all the MappedNames associated with it. void erase(const IndexedName& idx); + /// Get the size of the map. unsigned long size() const; + /// Check if the map is empty. bool empty() const; + /** + * @brief Find the IndexedName associated with the given MappedName. + * + * @param[in] name The mapped name to search for. + * @param[in,out] sids Optional pointer to store the StringID references that + * make up the name. + * + * @return The IndexedName associated with the given MappedName, or an + * empty IndexedName if not found. + */ IndexedName find(const MappedName& name, ElementIDRefs* sids = nullptr) const; + /** + * @brief Find the MappedName associated with the given IndexedName. + * + * @param[in] idx The indexed name to search for. + * @param[in,out] sids Optional pointer to store the StringID references that + * make up the name. + */ MappedName find(const IndexedName& idx, ElementIDRefs* sids = nullptr) const; + /** + * @brief Find all MappedNames associated with the given IndexedName. + * + * @param[in] idx The indexed name to search for. + * + * @return A vector of pairs of MappedName and their corresponding + * ElementIDRefs associated with the given IndexedName. + */ std::vector> findAll(const IndexedName& idx) const; // prefix searching is disabled, as TopoShape::getRelatedElement() is @@ -166,16 +231,23 @@ public: std::vector findAllStartsWith(const char *prefix) const; #endif + /// Check if there are any child element maps. bool hasChildElementMap() const; - /* Ensures that for each IndexedName mapped to IndexedElements, that - * each child is properly hashed (cached). + /** + * @brief Hash the child maps. * - * Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access, - * now you must pass in `long masterTag` explicitly. + * Ensures that for each IndexedName mapped to IndexedElements, that each + * child is properly hashed (cached). + * + * @param[in] masterTag The master tag of the element. + * + * @note The original function was in the context of ComplexGeoData, which + * provided `Tag` access, now you must pass in `long masterTag` explicitly. */ void hashChildMaps(long masterTag); + /// A struct to represent mapped child elements. struct AppExport MappedChildElements { IndexedName indexedName; @@ -189,23 +261,48 @@ public: // prefix() has been moved to ElementNamingUtils.h }; - /* Note: the original addChildElements passed `ComplexGeoData& master` for getting the `Tag`, - * now it just passes `long masterTag`.*/ + /** + * @brief Add child elements to the map. + * + * @param[in] masterTag The master tag of the element. + * @param[in] children The vector of child elements to add. + * + * @note The original addChildElements passed `ComplexGeoData& master` for + * getting the `Tag`, now it just passes `long masterTag`. + */ void addChildElements(long masterTag, const std::vector& children); + /// Get the child elements. std::vector getChildElements() const; + /// Get all mapped elements. std::vector getAll() const; + /** + * @brief Get the history of the given element name. + * + * @param[in] name The input element name. + * @param[in] masterTag The master tag of the element. + * @param[out] original Optional output parameter to store the original element name. + * @param[out] history Optional output parameter to store the history of element names. + * + * @return The tag associated with the original element name. + */ long getElementHistory(const MappedName& name, long masterTag, MappedName* original = nullptr, std::vector* history = nullptr) const; - /** Iterate through the history of the give element name with a given callback + /** + * @brief Iterate through the history of the given element name. + * + * Iterate through the history of the give element name with a given + * callback + * + * @param[in] name The input element name. + * @param[in] masterTag The master tag of the element. + * @param[in] cb The trace callback. * - * @param name: the input element name - * @param cb: trace callback with call signature. * @sa TraceCallback */ void traceElement(const MappedName& name, long masterTag, TraceCallback cb) const; diff --git a/src/App/ElementNamingUtils.h b/src/App/ElementNamingUtils.h index db57fd0884..c0484c55b6 100644 --- a/src/App/ElementNamingUtils.h +++ b/src/App/ElementNamingUtils.h @@ -10,9 +10,7 @@ namespace App { -/** Return type for lookups of new and old style sub-element names - * - */ +/// Return type for lookups of new and old style sub-element names struct ElementNamePair { std::string newName; @@ -42,8 +40,17 @@ struct ElementNamePair namespace Data { +/** + * @name Element name constants + * @ingroup ElementMapping + * @anchor ElementNameConstants + * + * @{ + */ + /// Special prefix to mark the beginning of a mapped sub-element name constexpr const char* ELEMENT_MAP_PREFIX = ";"; +/// The size of the element map prefix constexpr size_t ELEMENT_MAP_PREFIX_SIZE = 1; /// Special prefix to mark a missing element @@ -52,27 +59,40 @@ constexpr const char* MISSING_PREFIX = "?"; // IMPORTANT: For all the constants below, the semicolon ";" // at the start is ELEMENT_MAP_PREFIX +/// Prefix to mark child elements. constexpr const char* MAPPED_CHILD_ELEMENTS_PREFIX = ";:R"; /// Special postfix to mark the following tag constexpr const char* POSTFIX_TAG = ";:H"; +/// The size of the postfix tag constexpr size_t POSTFIX_TAG_SIZE = 3; +/// Postfix to mark a decimal tag. constexpr const char* POSTFIX_DECIMAL_TAG = ";:T"; +/// Postfix to mark an external tag. constexpr const char* POSTFIX_EXTERNAL_TAG = ";:X"; +/// Postfix to mark a child element. constexpr const char* POSTFIX_CHILD = ";:C"; /// Special postfix to mark the index of an array element constexpr const char* POSTFIX_INDEX = ";:I"; +/// Postfix to mark an element higher in the hierarcy. constexpr const char* POSTFIX_UPPER = ";:U"; +/// Postfix to mark an element lower in the hierarcy. constexpr const char* POSTFIX_LOWER = ";:L"; +/// Postfix to mark an element as being modified. constexpr const char* POSTFIX_MOD = ";:M"; +/// Postfix to mark an element as being generated. constexpr const char* POSTFIX_GEN = ";:G"; +/// Postfix to mark an element as being modified and generated. constexpr const char* POSTFIX_MODGEN = ";:MG"; +/// Postfix to mark a duplicate element. constexpr const char* POSTFIX_DUPLICATE = ";D"; - +/// Label to use for element index in element mapping. constexpr const char* ELEMENT_MAP_INDEX = "_"; +/// @} + /// Check if a subname contains missing element AppExport bool hasMissingElement(const char *subname); diff --git a/src/App/IndexedName.h b/src/App/IndexedName.h index 51aa4b30cc..b7af45260d 100644 --- a/src/App/IndexedName.h +++ b/src/App/IndexedName.h @@ -40,30 +40,44 @@ namespace Data { -/// The IndexedName class provides a very memory-efficient data structure to hold a name and an -/// index value, and to perform various comparisons and validations of those values. The name must -/// only consist of upper- and lower-case ASCII characters and the underscore ('_') character. The -/// index must be a positive integer. The string representation of this IndexedName is the name -/// followed by the index, with no spaces between: an IndexedName may be constructed from this -/// string. For example "EDGE1" or "FACE345" might be the names of elements that use an IndexedName. -/// If there is then an "EDGE2", only a pointer to the original stored name "EDGE" is retained. -/// -/// The memory efficiency of the class comes from reusing the same character storage for names that -/// match, while retaining their differing indices. This is achieved by either using user-provided -/// const char * names (provided as a list of typeNames and presumed to never be deallocated), or by -/// maintaining an internal list of names that have been used before, and can be reused later. +/** + * @brief A data structure to hold a name and an index value. + * @ingroup ElementMapping + * + * The IndexedName class provides a very memory-efficient data structure to hold a name and an + * index value, and to perform various comparisons and validations of those values. The name must + * only consist of upper- and lower-case ASCII characters and the underscore ('_') character. The + * index must be a positive integer. The string representation of this IndexedName is the name + * followed by the index, with no spaces between: an IndexedName may be constructed from this + * string. For example "Edge1" or "Face345" might be the names of elements that use an IndexedName. + * If there is then an "Edge2", only a pointer to the original stored name "Edge" is retained. + * + * The memory efficiency of the class comes from reusing the same character storage for names that + * match, while retaining their differing indices. This is achieved by either using user-provided + * const char * names (provided as a list of typeNames and presumed to never be deallocated), or by + * maintaining an internal list of names that have been used before, and can be reused later. + */ class AppExport IndexedName { public: - /// Construct from a name and an optional index. If the name contains an index it is read, but - /// is used as the index *only* if _index parameter is unset. If the _index parameter is given - /// it overrides any trailing integer in the name. Index must be positive, and name must contain - /// only ASCII letters and the underscore character. If these conditions are not met, name is - /// set to the empty string, and isNull() will return true. - /// - /// \param name The new name - ASCII letters and underscores only, with optional integer suffix. - /// This memory will be copied into a new internal storage location and need not be persistent. - /// \param _index The new index - if provided, it overrides any suffix provided by name + /** + * @brief Construct an indexed name from a name and optional index. + * + * Construct an indexed name from a name and an optional index. If the name + * contains an index, it is read, but is used as the index *only* if @p + * _index is unset. If the @p _index is given, it overrides any trailing + * integer in the name. The index must be positive, and the name must + * contain only ASCII letters and the underscore character. If these + * conditions are not met, the name is set to the empty string, and + * isNull() will return true. + * + * @param[in] name The new name - ASCII letters and underscores only, with + * optional integer suffix. This memory will be copied into a new internal + * storage location and need not be persistent. + * + * @param[in] _index The new index. If provided, it overrides any suffix + * provided by name. + */ explicit IndexedName(const char* name = nullptr, int _index = 0) : index(0) { @@ -79,20 +93,31 @@ public: } } - /// Create an indexed name that is restricted to a list of preset type names. If it appears in - /// that list, only a pointer to the character storage in the list is retained: the memory - /// locations pointed at by the list must never be destroyed once they have been used to create - /// names. If allowOthers is true (the default) then a requested name that is not in the list - /// will be added to a static internal storage table, and its memory then reused for later - /// objects with the same name. If allowOthers is false, then the name request is rejected, and - /// the name is treated as null. - /// - /// \param name The new name - ASCII letters and underscores only, with optional integer suffix - /// \param allowedTypeNames A vector of allowed names. Storage locations must persist for the - /// entire run of the program. - /// \param allowOthers Whether a name not in allowedTypeNames is permitted. If true (the - /// default) then a name not in allowedTypeNames is added to a static internal storage vector - /// so that it can be reused later without additional memory allocation. + /** + * @brief Construct an indexed name from a name and a list of preset type + * names. + * + * Create an indexed name that is restricted to a list of preset type + * names. If it appears in that list, only a pointer to the character + * storage in the list is retained: the memory locations pointed at by the + * list must never be destroyed once they have been used to create + * names. If @p allowOthers is true (the default) then a requested name + * that is not in the list will be added to a static internal storage + * table, and its memory then reused for later objects with the same + * name. If @p allowOthers is false, then the name request is rejected, and + * the name is treated as null. + * + * @param[in] name The new name - ASCII letters and underscores only, with + * optional integer suffix. + * + * @param allowedTypeNames A vector of allowed names. Storage locations + * must persist for the entire run of the program. + * + * @param allowOthers Whether a name not in allowedTypeNames is + * permitted. If true (the default) then a name not in allowedTypeNames is + * added to a static internal storage vector so that it can be reused later + * without additional memory allocation. + */ IndexedName(const char* name, const std::vector& allowedTypeNames, bool allowOthers = true) @@ -102,11 +127,15 @@ public: set(name, -1, allowedTypeNames, allowOthers); } - /// Construct from a QByteArray, but explicitly making a copy of the name on its first - /// occurrence. If this is a name that has already been stored internally, no additional copy - /// is made. - /// - /// \param data The QByteArray to copy the data from + /** + * @brief Construct a mapped name from a QByteArray. + * + * Construct from a QByteArray, but explicitly making a copy of the name on + * its first occurrence. If this is a name that has already been stored + * internally, no additional copy is made. + * + * @param[in] data The QByteArray to copy the data from. + */ explicit IndexedName(const QByteArray& data) : type("") , index(0) @@ -114,13 +143,21 @@ public: set(data.constData(), data.size()); } - /// Given constant name and an index, reuse the existing memory for the name, not making a copy - /// of it, or scanning any existing storage for it. The name must never become invalid for the - /// lifetime of the object it names. This memory will never be reused by another object. - /// - /// \param name The name of the object. This memory is NOT copied and must be persistent. - /// \param index A positive, non-zero integer - /// \return An IndexedName with the given name and index, reusing the existing memory for name + /** + * @brief Create an indexed name from a string and index. + * + * Given constant name and an index, reuse the existing memory for the + * name, not making a copy of it, or scanning any existing storage for + * it. The name must never become invalid for the lifetime of the object it + * names. This memory will never be reused by another object. + * + * @param[in] name The name of the object. This memory is NOT copied and must be persistent. + * + * @param[in] index A positive, non-zero integer. + * + * @return An IndexedName with the given name and index, reusing the + * existing memory for name. + */ static IndexedName fromConst(const char* name, int index) { assert(index >= 0); @@ -130,11 +167,17 @@ public: return res; } - /// Given an existing std::string, *append* this name to it. If index is not zero, this will - /// include the index. - /// - /// \param buffer A (possibly non-empty) string buffer to append the name to. - /// \return A const char pointer to the name we appended to the buffer. + /** + * @brief Append this index name to a buffer. + * + * Given an existing std::string, *append* this name to it. If the index is + * not zero, this will include the index. + * + * @param[in,out] buffer A (possibly non-empty) string buffer to append the + * name to. + * + * @return A const char pointer to the name we appended to the buffer. + */ const char* appendToStringBuffer(std::string& buffer) const { // Note! buffer is not cleared on purpose. @@ -146,9 +189,11 @@ public: return buffer.c_str() + offset; } - /// Create and return a new std::string with this name in it. - /// - /// \return A newly-created string with the IndexedName in it (e.g. "EDGE42") + /** + * @brief Create and return a new std::string with this name in it. + * + * @return A newly-created string with the IndexedName in it (e.g. "EDGE42") + */ std::string toString() const { std::string result; @@ -156,8 +201,12 @@ public: return result; } - /// An indexedName is represented as the simple concatenation of the name and its index, e.g. - /// "EDGE1" or "FACE42". + /** + * @brief Append this indexed name to an output stream. + * + * An indexedName is represented as the simple concatenation of the name and its index, e.g. + * "EDGE1" or "FACE42". + */ friend std::ostream& operator<<(std::ostream& stream, const IndexedName& indexedName) { stream << indexedName.type; @@ -167,14 +216,26 @@ public: return stream; } - /// True only if both the name and index compare exactly equal. + /** + * Check if two indexed names are equal. + * + * @param[in] other The other IndexedName to compare against. + * + * @return True only if both the name and index compare exactly equal. + */ bool operator==(const IndexedName& other) const { return this->index == other.index && (this->type == other.type || std::strcmp(this->type, other.type) == 0); } - /// Increments the index by the given offset. Does not affect the text part of the name. + /** + * @brief Increments the index by the given offset. + * + * Does not affect the text part of the name. + * + * @param[in] offset The amount to increase the index by. + */ IndexedName& operator+=(int offset) { this->index += offset; @@ -189,8 +250,12 @@ public: return *this; } - /// Pre-decrement operator: decreases the index of this element by one. Must not make the index - /// negative (only checked when compiled in debug mode). + /** + * @brief Pre-decrement operator: decreases the index of this element by one. + * + * Must not make the index negative (only checked when compiled in debug + * mode). + */ IndexedName& operator--() { --this->index; @@ -198,13 +263,28 @@ public: return *this; } - /// True if either the name or the index compare not equal. + /** + * @brief Check if two indexed names are not equal. + * + * @param[in] other The other IndexedName to compare against. + * + * @return True if either the name or the index compare not equal. + */ bool operator!=(const IndexedName& other) const { return !(this->operator==(other)); } - /// Equivalent to C++20's operator <=> + /** + * @brief Compare two IndexedNames. + * + * Equivalent to C++20's operator <=>. The comparison is first + * lexicographical for the text and then numerical for the index. + * + * @param[in] other The other IndexedName to compare against. + * + * @return Negative value if this < other, positive if this > other, zero if equal. + */ int compare(const IndexedName& other) const { int res = std::strcmp(this->type, other.type); @@ -220,15 +300,19 @@ public: return 0; } - /// Provided to enable sorting operations: the comparison is first lexicographical for the text - /// element of the names, then numerical for the indices. + /// Check if this IndexedName is less than another. bool operator<(const IndexedName& other) const { return compare(other) < 0; } - /// Allow direct memory access to the individual characters of the text portion of the name. - /// NOTE: input is not range-checked when compiled in release mode. + /** + * @brief index into the text part of the name. + * + * Allow direct memory access to the individual characters of the text portion of the name. + * + * @note The input is not range-checked when compiled in release mode. + */ char operator[](int input) const { assert(input >= 0); @@ -238,7 +322,11 @@ public: return this->type[input]; } - /// Get a pointer to text part of the name - does NOT make a copy, returns direct memory access + /** + * @brief Get a pointer to text part of the name. + * + * Does NOT make a copy, returns direct memory access. + */ const char* getType() const { return this->type; @@ -250,17 +338,25 @@ public: return this->index; } - /// Set the numerical part of the name (note that there is no equivalent function to allow - /// changing the text part of the name, which is immutable once created). - /// - /// \param input The new index. Must be a positive non-zero integer + /** + * @brief Set the numerical part of the name + * + * @note There is no equivalent function to allow changing the text part of + * the name, which is immutable once created). + * + * @param[in] input The new index. Must be a positive non-zero integer. + */ void setIndex(int input) { assert(input >= 0); this->index = input; } - /// A name is considered "null" if its text component is an empty string. + /** + * @brief Check whether this index name is null. + * + * A name is considered "null" if its text component is an empty string. + */ // When we support C++20 we can use std::span<> to eliminate the clang-tidy warning // NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic bool isNull() const @@ -268,26 +364,37 @@ public: return this->type[0] == '\0'; } - /// Boolean conversion provides the opposite of isNull(), yielding true when the text part of - /// the name is NOT the empty string. + /** + * @brief Boolean conversion of the indexed name. + * + * Boolean conversion provides the opposite of isNull(), yielding true when + * the text part of the name is NOT the empty string. + */ explicit operator bool() const { return !isNull(); } protected: - /// Apply the IndexedName rules and either store the characters of a new type or a reference to - /// the characters in a type named in types, or stored statically within this function. If len - /// is not set, or set to -1 (the default), then the provided string in name is scanned for its - /// length using strlen (e.g. it must be null-terminated). - /// - /// \param name The new name. If necessary a copy is made, this char * need not be persistent - /// \param length The length of name - /// \param allowedNames A vector of storage locations of allowed names. These storage locations - /// must be persistent for the duration of the program run. - /// \param allowOthers If true (the default), then if name is not in allowedNames it is allowed, - /// and it is added to internal storage (making a copy of the name if this is its first - /// occurrence). + /** + * @brief Set the text part of the indexed name. + * + * Apply the IndexedName rules and either store the characters of a new + * type or a reference to the characters in a type named in types, or + * stored statically within this function. If len is not set, or set to -1 + * (the default), then the provided string in name is scanned for its + * length using strlen (e.g. it must be null-terminated). + * + * @param[in] name The new name. If necessary, a copy is made. The + * provided string need not be persistent. + * @param[in] length The length of name. + * @param allowedNames A vector of storage locations of allowed + * names. These storage locations must be persistent for the duration of + * the program run. + * @param[in] allowOthers If true (the default), then if name is not in + * allowedNames, it is allowed and it is added to internal storage (making + * a copy of the name if this is its first occurrence). + */ void set(const char* name, int length = -1, const std::vector& allowedNames = {}, diff --git a/src/App/MappedElement.h b/src/App/MappedElement.h index 4880354592..72d8943c61 100644 --- a/src/App/MappedElement.h +++ b/src/App/MappedElement.h @@ -35,21 +35,36 @@ class DocumentObject; namespace Data { -/// A MappedElement combines a MappedName and and IndexedName into a single entity and provides -/// simple comparison operators for the combination (including operator< so that the entity can -/// be sorted, or used in sorted containers). +/** + * @brief A combination of a MappedName and an IndexedName. + * @ingroup ElementMapping + * + * A mapped element combines a mapped name and an indexed name into a single + * entity and provides simple comparison operators for the combination + * (including operator< so that the entity can be sorted, or used in sorted + * containers). + */ struct AppExport MappedElement { + /// The indexed name. IndexedName index; + /// The mapped name. MappedName name; MappedElement() = default; + /** + * @brief Construct a mapped element from an indexed name and a mapped name. + * + * @param[in] idx The indexed name. + * @param[in] n The mapped name. + */ MappedElement(const IndexedName& idx, MappedName n) : index(idx) , name(std::move(n)) {} + ///@copydoc MappedElement(const IndexedName& idx, MappedName n) MappedElement(MappedName n, const IndexedName& idx) : index(idx) , name(std::move(n)) @@ -83,9 +98,14 @@ struct AppExport MappedElement return this->index != other.index || this->name != other.name; } - /// For sorting purposes, one MappedElement is considered "less" than another if its index - /// compares less (which is first alphabetical, and then by numeric index). If the index of this - /// MappedElement is the same, then the names are compared lexicographically. + /** + * @brief Compare two mapped elements. + * + * For sorting purposes, one MappedElement is considered "less" than + * another if its index compares less (which is first alphabetical, and + * then by numeric index). If the index of this MappedElement is the same, + * then the names are compared lexicographically. + */ bool operator<(const MappedElement& other) const { int res = this->index.compare(other.index); @@ -99,6 +119,7 @@ struct AppExport MappedElement } }; +/// Struct to represent an item in the history of an object. struct AppExport HistoryItem { App::DocumentObject* obj; @@ -109,19 +130,20 @@ struct AppExport HistoryItem HistoryItem(App::DocumentObject* obj, const Data::MappedName& name); }; +///Comparator struct to make element name sorting more stable. struct AppExport ElementNameComparator { - /** Comparison function to make topo name more stable + /** + * @brief Comparison function to make topo name more stable. * - * The sorting decomposes the name into either of the following two forms - * '#' + hex_digits + tail - * non_digits + digits + tail + * The sorting decomposes the name into either of the following two forms: + * - '#' + hex_digits + tail + * - non_digits + digits + tail * - * The non-digits part is compared lexically, while the digits part is - * compared by its integer value. - * - * The reason for this is to prevent names with bigger digits (which usually means - * they come later in history) from coming earlier when sorting. + * The non-digits part is compared lexicographically, while the digits part + * is compared by its integer value. The reason for this is to prevent + * names with bigger digits (which usually means they come later in + * history) from coming earlier when sorting. */ bool operator()(const MappedName& leftName, const MappedName& rightName) const; }; diff --git a/src/App/MappedName.h b/src/App/MappedName.h index b7df053cc4..29ec0518af 100644 --- a/src/App/MappedName.h +++ b/src/App/MappedName.h @@ -45,20 +45,30 @@ namespace Data // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) -/// The MappedName class maintains a two-part name: the first part ("data") is considered immutable -/// once created, while the second part ("postfix") can be modified/appended to by later operations. -/// It uses shared data when possible (see the fromRawData() members). Despite storing data and -/// postfix separately, they can be accessed via calls to size(), operator[], etc. as though they -/// were a single array. +/** + * @brief A class for managing element map names. + * @ingroup ElementMapping + * + * The MappedName class maintains a two-part name: the first part ("data") is + * considered immutable once created, while the second part ("postfix") can be + * modified/appended to by later operations. It uses shared data when possible + * (see the fromRawData() members). Despite storing data and postfix + * separately, they can be accessed via calls to size(), operator[], etc. as + * though they were a single array. + */ class AppExport MappedName { public: - /// Create a MappedName from a C string, optionally prefixed by an element map prefix, which - /// will be omitted from the stored MappedName. - /// - /// \param name The new name. A deep copy is made. - /// \param size Optional, the length of the name string. If not provided, the string must be - /// null-terminated. + /** + * @brief Create a MappedName from a C string. + * + * Create a MappedName from a C string, optionally prefixed by an element + * map prefix, which will be omitted from the stored MappedName. + * + * @param[in] name The new name. A deep copy is made. + * @param[in] size Optional, the length of the name string. If not + * provided, the string must be null-terminated. + */ explicit MappedName(const char* name, int size = -1) : raw(false) { @@ -72,10 +82,14 @@ public: data = size < 0 ? QByteArray(name) : QByteArray(name, size); } - /// Create a MappedName from a C++ std::string, optionally prefixed by an element map prefix, - /// which will be omitted from the stored MappedName. - /// - /// \param name The new name. A deep copy is made. + /** + * @brief Create a MappedName from a C++ std::string. + * + * Create a MappedName from a C++ std::string, optionally prefixed by an + * element map prefix, which will be omitted from the stored MappedName. + * + * @param nameString The new name. A deep copy is made. + */ explicit MappedName(const std::string& nameString) : raw(false) { @@ -88,9 +102,14 @@ public: data = QByteArray(name, static_cast(size)); } - /// Create a MappedName from an IndexedName. If non-zero, the numerical part of the IndexedName - /// is appended as text to the MappedName. In that case the memory is *not* shared between the - /// original IndexedName and the MappedName. + /** + * @brief Create a MappedName from an IndexedName. + * + * Create a MappedName from an IndexedName. If non-zero, the numerical part + * of the IndexedName is appended as text to the MappedName. In that case + * the memory is *not* shared between the original IndexedName and the + * MappedName. + */ explicit MappedName(const IndexedName& element) : data(QByteArray::fromRawData(element.getType(), static_cast(qstrlen(element.getType())))) @@ -102,6 +121,8 @@ public: } } + + /// Create a MappedName from a StringIdRef. explicit MappedName(const App::StringIDRef& sid) : raw(false) { @@ -114,29 +135,39 @@ public: MappedName(const MappedName& other) = default; - /// Copy constructor with start position offset and optional size. The data is *not* reused. - /// - /// \param other The MappedName to copy - /// \param startPosition an integer offset to start the copy from - /// \param size the number of bytes to copy. - /// \see append() for details about how the copy behaves for various sizes and start positions + /** + * @brief Copy constructor with start position offset and size. + * + * Copy constructor with start position offset and optional size. The data + * is *not* reused. + * + * @param[in] other The MappedName to copy. + * @param[in] startPosition An integer offset to start the copy from. + * @param[in] size The number of bytes to copy. + * + * @see append() for details about how the copy behaves for various sizes + * and start positions + */ MappedName(const MappedName& other, int startPosition, int size = -1) : raw(false) { append(other, startPosition, size); } - /// Copy constructor with additional postfix - /// - /// \param other The mapped name to copy. Its data and postfix become the new MappedName's data - /// \param postfix The postfix for the new MappedName + /** + * @brief Copy constructor with additional postfix + * + * @param[in] other The mapped name to copy. Its data and postfix become + * the new MappedName's data. + * + * @param postfix The postfix for the new MappedName. + */ MappedName(const MappedName& other, const char* postfix) : data(other.data + other.postfix) , postfix(postfix) , raw(false) {} - /// Move constructor MappedName(MappedName&& other) noexcept : data(std::move(other.data)) , postfix(std::move(other.postfix)) @@ -145,12 +176,17 @@ public: ~MappedName() = default; - /// Construct a MappedName from raw character data (including null characters, if size is - /// provided). No copy is made: the data is used in place. - /// - /// \param name The raw data to use. - /// \param size The number of bytes to access. If omitted, name must be null-terminated. - /// \return a new MappedName with name as its data. + /** + * @brief Construct a MappedName from raw character data. + * + * Construct a MappedName from raw character data (including null characters, if size is + * provided). No copy is made: the data is used in place. + * + * @param name The raw data to use. + * @param size The number of bytes to access. If omitted, name must be null-terminated. + * + * @return a new MappedName with name as its data. + */ static MappedName fromRawData(const char* name, int size = -1) { MappedName res; @@ -162,25 +198,36 @@ public: return res; } - /// Construct a MappedName from QByteArray data (including any embedded null characters). - /// - /// \param data The original data. No copy is made, the data is shared with the other instance. - /// \return a new MappedName with data as its data. + /** + * @brief Construct a MappedName from QByteArray data. + * + * Construct a MappedName from QByteArray data (including any embedded null characters). + * + * @param[in] data The original data. No copy is made, the data is shared + * with the other instance. + * + * @return a new MappedName with data as its data. + */ static MappedName fromRawData(const QByteArray& data) { return fromRawData(data.constData(), data.size()); } - /// Construct a MappedName from another MappedName - /// - /// \param other The MappedName to copy from. The data is usually not copied, but in some - /// cases a partial copy may be made to support a slice that extends across other's data into - /// its postfix. - /// \param startPosition The position to start the reference at. - /// \param size The number of bytes to access. If omitted, continues from startPosition - /// to the end of available data (including postfix). - /// \return a new MappedName sharing (possibly a subset of) data with other. - /// \see append() for details about how the copy behaves for various sizes and start positions + /** + * @brief Construct a MappedName from another MappedName. + * + * @param[in] other The MappedName to copy from. The data is usually not + * copied, but in some cases a partial copy may be made to support a slice + * that extends across other's data into its postfix. + * @param[in] startPosition The position to start the reference at. + * @param[in] size The number of bytes to access. If omitted, continues + * from startPosition to the end of available data (including postfix). + * + * @return a new MappedName sharing (possibly a subset of) data with other. + * + * @see append() For details about how the copy behaves for various sizes + * and start positions. + */ static MappedName fromRawData(const MappedName& other, int startPosition, int size = -1) { if (startPosition < 0) { @@ -218,7 +265,7 @@ public: return res; } - /// Share data with another MappedName + /// Share data with another MappedName. MappedName& operator=(const MappedName& other) = default; /// Create a new MappedName from a std::string: the string's data is copied. @@ -235,8 +282,7 @@ public: return *this; } - - /// Move-construct a MappedName + /// Move-construct a MappedName. MappedName& operator=(MappedName&& other) noexcept { this->data = std::move(other.data); @@ -245,8 +291,12 @@ public: return *this; } - /// Write to a stream as the name with postfix directly appended to it. Note that there is no - /// special handling for null or non-ASCII characters, they are simply written to the stream. + /** + * @brief Write to a stream as the name with postfix directly appended to it. + * + * Note that there is no special handling for null or non-ASCII characters, + * they are simply written to the stream. + */ friend std::ostream& operator<<(std::ostream& stream, const MappedName& mappedName) { stream.write(mappedName.data.constData(), mappedName.data.size()); @@ -254,8 +304,11 @@ public: return stream; } - /// Two MappedNames are equal if the concatenation of their data and postfix is equal. The - /// individual data and postfix may NOT be equal in this case. + /** + * @brief Two MappedNames are equal if the concatenation of their data and postfix is equal. + * + * The individual data and postfix may NOT be equal in this case. + */ bool operator==(const MappedName& other) const { if (this->size() != other.size()) { @@ -286,13 +339,21 @@ public: return tmp == larger.postfix; } + /// Check if two mapped names are inequal. bool operator!=(const MappedName& other) const { return !(this->operator==(other)); } - /// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS - /// argument's postfix with the RHS argument's data and postfix appended to it. + /** + * @brief Concatenate two mapped names. + * + * @param[in] other The mapped name to append. + * + * @return A new MappedName whose data is the LHS argument's data and whose + * postfix is the LHS argument's postfix with the RHS argument's data and + * postfix appended to it. + */ MappedName operator+(const MappedName& other) const { MappedName res(*this); @@ -300,8 +361,15 @@ public: return res; } - /// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS - /// argument's postfix with the RHS argument appended to it. The character data is copied. + /** + * @brief Concatenate two mapped names. + * + * @param[in] other The mapped name as a string to append. + * + * @return A new MappedName whose data is the LHS argument's data and whose + * postfix is the LHS argument's postfix with the RHS argument appended to + * it. The character data is copied. + */ MappedName operator+(const char* other) const { MappedName res(*this); @@ -309,8 +377,7 @@ public: return res; } - /// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS - /// argument's postfix with the RHS argument appended to it. The character data is copied. + /// @copydoc operator+(const char*) const MappedName operator+(const std::string& other) const { MappedName res(*this); @@ -318,8 +385,15 @@ public: return res; } - /// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS - /// argument's postfix with the RHS argument appended to it. + /** + * @brief Concatenate two mapped names. + * + * @param[in] other The mapped name as a QByteArray. + * + * @return A new MappedName whose data is the LHS argument's data and whose + * postfix is the LHS argument's postfix with the RHS argument appended to + * it. + */ MappedName operator+(const QByteArray& other) const { MappedName res(*this); @@ -327,8 +401,16 @@ public: return res; } - /// Appends other to this instance's postfix. other must be a null-terminated C string. The - /// character data from the string is copied. + /** + * @brief Appends other to this instance's postfix. + * + * @p other must be a null-terminated C string. The character data from the + * string is copied. + * + * @param[in] other The mapped name as a string. + * + * @return This with the other's postfix appended. + */ MappedName& operator+=(const char* other) { if (other && (other[0] != 0)) { @@ -337,7 +419,7 @@ public: return *this; } - /// Appends other to this instance's postfix. The character data from the string is copied. + /// @copydoc operator+=(const char* other) MappedName& operator+=(const std::string& other) { if (!other.empty()) { @@ -347,29 +429,50 @@ public: return *this; } - /// Appends other to this instance's postfix. The data may be either copied or shared, depending - /// on whether this->postfix is empty (in which case the data is shared) or non-empty (in which - /// case it is copied). + /** + * @brief Appends other to this instance's postfix. + * + * The data may be either copied or shared, depending on whether + * this->postfix is empty (in which case the data is shared) or non-empty + * (in which case it is copied). + * + * @param[in] other The mapped name as a QByteArray. + * @return This with the other's data appended. + */ MappedName& operator+=(const QByteArray& other) { this->postfix += other; return *this; } - /// Appends other to this instance's postfix, unless this is empty, in which case this acts - /// like operator=, and makes this instance's data equal to other's data, and this instance's - /// postfix equal to the other instance's postfix. + /** + * @brief Appends other to this instance's postfix. + * + * Appends other to this instance's postfix, unless this is empty, in which + * case this acts like operator=, and makes this instance's data equal to + * other's data, and this instance's postfix equal to the other instance's + * postfix. + * + * @param[in] other The mapped name to append. + * @return This with the other's data appended. + */ MappedName& operator+=(const MappedName& other) { append(other); return *this; } - /// Add dataToAppend to this MappedName. If the current name is empty, this becomes the new - /// data element. If this MappedName already has data, then the data is appended to the postfix. - /// - /// \param dataToAppend The data to add. A deep copy is made. - /// \param size The number of bytes to copy. If omitted, dataToAppend must be null-terminated. + /** + * @brief Append to this mapped name. + * + * Add @p dataToAppend to this mapped name. If the current name is empty, + * this becomes the new data element. If this MappedName already has data, + * then the data is appended to the postfix. + * + * @param[in] dataToAppend The data to add. A deep copy is made. + * @param[in] size The number of bytes to copy. If omitted, @p dataToAppend + * must be null-terminated. + */ void append(const char* dataToAppend, int size = -1) { if (dataToAppend && (size != 0)) { @@ -385,20 +488,30 @@ public: } } - /// Treating both this and other as single continuous byte arrays, append other to this. If this - /// is empty, then other's data is shared with this instance's data beginning at startPosition. - /// If this is *not* empty, then all data is appended to the postfix. If the copy crosses the - /// boundary between other's data and its postfix, then if this instance was empty, the new - /// data stops where other's data stops, and the remainder of the copy is placed in the suffix. - /// Otherwise the copy simply continues as though there was no distinction between other's - /// data and suffix. - /// - /// \param other The MappedName to obtain the data from. The data is shared when possible, - /// depending on the details of startPosition, size, and this->empty(). - /// \param startPosition The byte to start the copy at. Must be a positive non-zero integer less - /// than the length of other's combined data + postfix. - /// \param size The number of bytes to copy. Must not overrun the end of other's combined data - /// storage when taking startPosition into consideration. + /** + * @brief Append to this mapped name. + * + * Treating both this and other as single continuous byte arrays, append + * other to this. If this is empty, then other's data is shared with this + * instance's data beginning at @p startPosition. If this is *not* empty, + * then all data is appended to the postfix. If the copy crosses the + * boundary between other's data and its postfix, then if this instance was + * empty, the new data stops where other's data stops, and the remainder of + * the copy is placed in the suffix. Otherwise the copy simply continues + * as though there was no distinction between other's data and suffix. + * + * @param[in] other The MappedName to obtain the data from. The data is + * shared when possible, depending on the details of startPosition, size, + * and this->empty(). + * + * @param startPosition The byte to start the copy at. Must be a positive + * non-zero integer less than the length of other's combined data + + * postfix. + * + * @param size The number of bytes to copy. Must not overrun the end of + * other's combined data storage when taking @p startPosition into + * consideration. + */ void append(const MappedName& other, int startPosition = 0, int size = -1) { // enforce 0 <= startPosition <= other.size @@ -460,31 +573,49 @@ public: } } - /// Create a std::string from this instance, starting at startPosition, and extending len bytes. - /// - /// \param startPosition The offset into the data - /// \param len The number of bytes to output - /// \return A new std::string containing the bytes copied from this instance's data and postfix - /// (depending on startPosition and len). - /// \note No effort is made to ensure that these are valid ASCII characters, and it is possible - /// the data includes embedded null characters, non-ASCII data, etc. + /** + * @brief Create a string representation. + * + * Create a std::string from this instance, starting at startPosition, and + * extending len bytes. + * + * @param[in] startPosition The offset into the data. + * @param[in] len The number of bytes to output. + * + * @return A new std::string containing the bytes copied from this + * instance's data and postfix (depending on startPosition and len). + * + * @note No effort is made to ensure that these are valid ASCII characters, + * and it is possible the data includes embedded null characters, non-ASCII + * data, etc. + */ std::string toString(int startPosition = 0, int len = -1) const { std::string res; return appendToBuffer(res, startPosition, len); } - /// Given a (possibly non-empty) std::string buffer, append this instance to it, starting at a - /// specified position, and continuing for a specified number of bytes. - /// - /// \param buffer The string buffer to append to. - /// \param startPosition The position in this instance's data/postfix to start at (defaults to - /// zero). Must be less than the total length of the data plus the postfix. - /// \param len The number of bytes to append. If omitted, defaults to appending all available - /// data starting at startPosition. - /// \return A pointer to the beginning of the appended data within buffer. - /// \note No effort is made to ensure that these are valid ASCII characters, and it is possible - /// the data includes embedded null characters, non-ASCII data, etc. + /** + * @brief Append this mapped name to a buffer. + * + * Given a (possibly non-empty) std::string buffer, append this instance to it, starting at a + * specified position, and continuing for a specified number of bytes. + * + * @param[in,out] buffer The string buffer to append to. + * + * @param[in] startPosition The position in this instance's data/postfix to + * start at (defaults to * zero). Must be less than the total length of the + * data plus the postfix. + * + * @param[in] len The number of bytes to append. If omitted, defaults to + * appending all available data starting at startPosition. + * + * @return A pointer to the beginning of the appended data within buffer. + * + * @note No effort is made to ensure that these are valid ASCII characters, + * and it is possible the data includes embedded null characters, non-ASCII + * data, etc. + */ const char* appendToBuffer(std::string& buffer, int startPosition = 0, int len = -1) const { std::size_t offset = buffer.size(); @@ -531,16 +662,24 @@ public: return this->data.constData() + offset; } - /// Get access to raw byte data. When possible, data is shared between this instance and the - /// returned QByteArray. If the combination of offset and size results in data that crosses the - /// boundary between this->data and this->postfix, the data must be copied in order to provide - /// access as a continuous array of bytes. - /// - /// \param offset The start position of the raw data access. - /// \param size The number of bytes to access. If omitted, the resulting QByteArray includes - /// everything starting from offset to the end, including any postfix data. - /// \return A new QByteArray that shares data with this instance if possible, or is a new copy - /// if required by offset and size. + /** + * @brief Convert this mapped name to raw bytes. + * + * Get access to raw byte data. When possible, data is shared between this + * instance and the returned QByteArray. If the combination of offset and + * size results in data that crosses the boundary between this->data and + * this->postfix, the data must be copied in order to provide access as a + * continuous array of bytes. + * + * @param[in] offset The start position of the raw data access. + * + * @param[in] size The number of bytes to access. If omitted, the resulting + * QByteArray includes everything starting from offset to the end, + * including any postfix data. + * + * @return A new QByteArray that shares data with this instance if + * possible, or is a new copy if required by offset and size. + */ QByteArray toRawBytes(int offset = 0, int size = -1) const { if (offset < 0) { @@ -585,10 +724,16 @@ public: // No constData() because 'data' is allowed to contain raw data, which may not end with 0. - /// Provide access to the content of this instance. If either postfix or data is empty, no copy - /// is made and the original QByteArray is returned, sharing data with this instance. If this - /// instance contains both data and postfix, a new QByteArray is created and stores a copy of - /// the data and postfix concatenated together. + /** + * @brief Provide access to the content of this instance. + * + * If either postfix or data is empty, no copy is made and the original + * QByteArray is returned, sharing data with this instance. If this + * instance contains both data and postfix, a new QByteArray is created and + * stores a copy of the data and postfix concatenated together. + * + * @return Either a new or the current QByteArray. + */ QByteArray toBytes() const { if (this->postfix.isEmpty()) { @@ -600,13 +745,19 @@ public: return this->data + this->postfix; } - /// Create an IndexedName from the data portion of this MappedName. If this data has a postfix, - /// the function returns an empty IndexedName. The function will fail if this->data contains - /// anything other than the ASCII letter a-z, A-Z, and the underscore, with an optional integer - /// suffix, returning an empty IndexedName (e.g. an IndexedName that evaluates to boolean - /// false and isNull() == true). - /// - /// \return a new IndexedName that shares its data with this instance's data member. + /** + * @brief Create an IndexedName from the mapped name. + * + * Create an IndexedName from the data portion of this MappedName. If this + * data has a postfix, the function returns an empty IndexedName. The + * function will fail if this->data contains anything other than the ASCII + * letter a-z, A-Z, and the underscore, with an optional integer suffix, + * returning an empty IndexedName (e.g. an IndexedName that evaluates to + * boolean false and isNull() == true). + * + * @return a new IndexedName that shares its data with this instance's data + * member. + */ IndexedName toIndexedName() const { if (this->postfix.isEmpty()) { @@ -615,8 +766,13 @@ public: return IndexedName(); } - /// Create and return a string version of this MappedName prefixed by the ComplexGeoData element - /// map prefix, if this MappedName cannot be converted to an indexed name. + /** + * @brief Create a prefixed string from the mapped name. + * + * Create and return a string version of this MappedName prefixed by the + * ComplexGeoData element map prefix, if this MappedName cannot be + * converted to an indexed name. + */ std::string toPrefixedString() const { std::string res; @@ -624,11 +780,18 @@ public: return res; } - /// Append this MappedName to a provided string buffer, including the ComplexGeoData element - /// map prefix if the MappedName cannot be converted to an IndexedName. - /// - /// \param buf A (possibly non-empty) string to append this MappedName to. - /// \return A pointer to the beginning of the buffer. + /** + * @brief Append this mapped name to a string buffer. + * + * Append this MappedName to a provided string buffer, including the + * ComplexGeoData element map prefix if the MappedName cannot be converted + * to an IndexedName. + * + * @param[in,out] buf A (possibly non-empty) string to append this + * MappedName to. + * + * @return A pointer to the beginning of the buffer. + */ const char* appendToBufferWithPrefix(std::string& buf) const { if (!toIndexedName()) { @@ -638,10 +801,20 @@ public: return buf.c_str(); } - /// Equivalent to C++20 operator<=>. Performs byte-by-byte comparison of this and other, - /// starting at the first byte and continuing through both data and postfix, ignoring which is - /// which. If the combined data and postfix members are of unequal size but start with the same - /// data, the shorter array is considered "less than" the longer. + /** + * @brief Compare two mapped names. + * + * Equivalent to C++20 operator<=>. Performs byte-by-byte comparison of + * this and other, starting at the first byte and continuing through both + * data and postfix, ignoring which is which. If the combined data and + * postfix members are of unequal size but start with the same data, the + * shorter array is considered "less than" the longer. + * + * @param[in] other The mapped name to compare. + * + * @return < 0 if this is less than other, 0 if they are equal and > 0 if + * this is greater than other. + */ int compare(const MappedName& other) const { int thisSize = this->size(); @@ -665,14 +838,23 @@ public: return 0; } - /// \see compare() + /// Check if this mapped name is less than @p other. bool operator<(const MappedName& other) const { return compare(other) < 0; } - /// Treat this MappedName as a single continuous array of bytes, beginning with data and - /// continuing through postfix. No bounds checking is performed when compiled in release mode. + /** + * @brief Index into the mapped name. + * + * Treat this MappedName as a single continuous array of bytes, beginning + * with data and continuing through postfix. + * + * @param index The byte offset to access. + * + * @return The byte at the specified offset, or 0 if the offset is out of + * range. + */ char operator[](int index) const { if (index < 0) { @@ -687,31 +869,52 @@ public: return this->data[index]; } - /// Treat this MappedName as a single continuous array of bytes, returning the combined size - /// of the data and postfix. + /** + * @brief Get the combined size of data and postfix. + * + * Treat this MappedName as a single continuous array of bytes, returning the combined size + * of the data and postfix. + * + * @return The total number of bytes in data + postfix. + */ int size() const { return this->data.size() + this->postfix.size(); } - /// Treat this MappedName as a single continuous array of bytes, returning true only if both - /// data and prefix are empty. + /** + * @brief Check if the mapped name is empty. + * + * Treat this MappedName as a single continuous array of bytes, returning true only if both + * data and prefix are empty. + * + * @return true if there are no bytes in data or postfix. + */ bool empty() const { return this->data.isEmpty() && this->postfix.isEmpty(); } - /// Returns true if this is shared data, or false if a unique copy has been made. - /// It is safe to access data only if it has been copied prior. To force a copy - /// please \see compact() + /** + * @brief Check whether this mapped name is shared data. + * + * It is safe to access data only if it has been copied prior. To force a copy + * please \see compact(). + * + * @return True if this is shared data, or false if a unique copy has been made. + */ bool isRaw() const { return this->raw; } - /// If this is shared data, a new unshared copy is made and returned. If it is already unshared - /// no new copy is made, a new instance is returned that shares is data with the current - /// instance. + /** + * @brief Copy the mapped name. + * + * If this is shared data, a new unshared copy is made and returned. If it + * is already unshared no new copy is made, a new instance is returned that + * shares is data with the current instance. + */ MappedName copy() const { if (!this->raw) { @@ -726,14 +929,22 @@ public: /// Ensure that this data is unshared, making a copy if necessary. void compact() const; - /// Boolean conversion is the inverse of empty(), returning true if there is data in either the - /// data or postfix, and false if there is nothing in either. + /** + * @brief Boolean conversion is the inverse of empty(). + * + * @return True if there is data in either the data or postfix, and false + * if there is nothing in either. + */ explicit operator bool() const { return !empty(); } - /// Reset this instance, clearing anything in data and postfix. + /** + * @brief Reset this instance. + * + * Clear anything in data and postfix. + */ void clear() { this->data.clear(); @@ -741,12 +952,19 @@ public: this->raw = false; } - /// Find a string of characters in this MappedName. The bytes must occur either entirely in the - /// data, or entirely in the postfix: a string that overlaps the two will not be found. - /// - /// \param searchTarget A null-terminated C string to search for. - /// \param startPosition A byte offset to start the search at. - /// \return The position of the target in this instance, or -1 if the target is not found. + /** + * @brief Find a string of characters in this mapped name. + * + * Find a string of characters in this mapped name. The bytes must occur + * either entirely in the data, or entirely in the postfix: a string that + * overlaps the two will not be found. + * + * @param[in] searchTarget A null-terminated C string to search for. + * @param[in] startPosition A byte offset to start the search at. + * + * @return The position of the target in this instance, or -1 if the target + * is not found. + */ int find(const char* searchTarget, int startPosition = 0) const { if (!searchTarget) { @@ -772,25 +990,29 @@ public: return res + this->data.size(); } - /// Find a string of characters in this MappedName. The bytes must occur either entirely in the - /// data, or entirely in the postfix: a string that overlaps the two will not be found. - /// - /// \param searchTarget A string to search for. - /// \param startPosition A byte offset to start the search at. - /// \return The position of the target in this instance, or -1 if the target is not found. + /// @copydoc find(const char*,int) const int find(const std::string& searchTarget, int startPosition = 0) const { return find(searchTarget.c_str(), startPosition); } - /// Find a string of characters in this MappedName, starting at the back of postfix and - /// proceeding in reverse through the data. The bytes must occur either entirely in the - /// data, or entirely in the postfix: a string that overlaps the two will not be found. - /// - /// \param searchTarget A null-terminated C string to search for. - /// \param startPosition A byte offset to start the search at. Negative numbers are supported - /// and count back from the end of the concatenated data (as in QByteArray::lastIndexOf()). - /// \return The position of the target in this instance, or -1 if the target is not found. + /** + * @brief Reverse find a string of characters in this mapped name. + * + * Find a string of characters in this mapped name, starting at the back of + * postfix and proceeding in reverse through the data. The bytes must occur + * either entirely in the data, or entirely in the postfix: a string that + * overlaps the two will not be found. + * + * @param[in] searchTarget A null-terminated C string to search for. + * + * @param[in] startPosition A byte offset to start the search at. Negative + * numbers are supported and count back from the end of the concatenated + * data (as in QByteArray::lastIndexOf()). + * + * @return The position of the target in this instance, or -1 if the target + * is not found. + */ int rfind(const char* searchTarget, int startPosition = -1) const { if (!searchTarget) { @@ -809,22 +1031,23 @@ public: return this->data.lastIndexOf(searchTarget, startPosition); } - /// Find a string in this MappedName, starting at the back of postfix and proceeding in reverse - /// through the data. The bytes must occur either entirely in the data, or entirely in the - /// postfix: a string that overlaps the two will not be found. - /// - /// \param searchTarget A null-terminated C string to search for. - /// \param startPosition A byte offset to start the search at. Negative numbers are supported - /// and count back from the end of the concatenated data (as in QByteArray::lastIndexOf()). - /// \return The position of the target in this instance, or -1 if the target is not found. + /// @copydoc rfind(const char*,int) const int rfind(const std::string& searchTarget, int startPosition = -1) const { return rfind(searchTarget.c_str(), startPosition); } - /// Returns true if this MappedName ends with the search target. If there is a postfix, only the - /// postfix is considered. If not, then only the data is considered. A search string that - /// overlaps the two will not be found. + /** + * @brief Check if this mapped name ends with the search target. + * + * If there is a postfix, only the postfix is considered. If not, then only + * the data is considered. A search string that overlaps the two will not + * be found. + * + * @param[in] searchTarget The string to search for at the end of this mapped name. + * + * @return true if this MappedName ends with the search target. + */ bool endsWith(const char* searchTarget) const { if (!searchTarget) { @@ -836,21 +1059,24 @@ public: return this->data.endsWith(searchTarget); } - /// Returns true if this MappedName ends with the search target. If there is a postfix, only the - /// postfix is considered. If not, then only the data is considered. A search string that - /// overlaps the two will not be found. + /// @copydoc endsWith(const char*) const bool endsWith(const std::string& searchTarget) const { return endsWith(searchTarget.c_str()); } - /// Returns true if this MappedName starts with the search target. If there is a postfix, only - /// the postfix is considered. If not, then only the data is considered. A search string that - /// overlaps the two will not be found. - /// - /// \param searchTarget An array of bytes to match - /// \param offset An offset to perform the match at - /// \return True if this MappedName begins with the target bytes + /** + * @brief Check if this mapped name starts with the search target. + * + * If there is a postfix, only the postfix is considered. If not, then only + * the data is considered. A search string that overlaps the two will not + * be found. + * + * @param[in] searchTarget The search target to match. + * @param[in] offset An offset to perform the match at. + * + * @return True if this MappedName begins with the target bytes. + */ bool startsWith(const QByteArray& searchTarget, int offset = 0) const { if (searchTarget.size() > size() - offset) { @@ -866,13 +1092,7 @@ public: return this->postfix.startsWith(searchTarget); } - /// Returns true if this MappedName starts with the search target. If there is a postfix, only - /// the postfix is considered. If not, then only the data is considered. A search string that - /// overlaps the two will not be found. - /// - /// \param searchTarget An array of bytes to match - /// \param offset An offset to perform the match at - /// \return True if this MappedName begins with the target bytes + /// @copydoc startsWith(const QByteArray&,int) const bool startsWith(const char* searchTarget, int offset = 0) const { if (!searchTarget) { @@ -883,13 +1103,7 @@ public: offset); } - /// Returns true if this MappedName starts with the search target. If there is a postfix, only - /// the postfix is considered. If not, then only the data is considered. A search string that - /// overlaps the two will not be found. - /// - /// \param searchTarget A string to match - /// \param offset An offset to perform the match at - /// \return True if this MappedName begins with the target bytes + /// @copydoc startsWith(const QByteArray&,int) const bool startsWith(const std::string& searchTarget, int offset = 0) const { return startsWith( @@ -897,19 +1111,26 @@ public: offset); } - /// Extract tagOut and other information from a encoded element name - /// - /// \param tagOut: optional pointer to receive the extracted tagOut - /// \param lenOut: optional pointer to receive the length field after the tagOut field. - /// This gives the length of the previous hashed element name starting - /// from the beginning of the give element name. - /// \param postfixOut: optional pointer to receive the postfixOut starting at the found tagOut - /// field. \param typeOut: optional pointer to receive the element typeOut character \param - /// negative: return negative tagOut as it is. If disabled, then always return positive tagOut. - /// Negative tagOut is sometimes used for element disambiguation. - /// \param recursive: recursively find the last non-zero tagOut - /// - /// \return Return the end position of the tagOut field, or return -1 if not found. + /** + * @brief Extract information from an encoded element name. + * + * Extract tagOut and other information from an encoded element name. + * + * @param[out] tagOut: optional pointer to receive the extracted tagOut + * @param[out] lenOut: optional pointer to receive the length field after + * the tagOut field. This gives the length of the previous hashed element + * name starting from the beginning of the give element name. + * @param[out] postfixOut: optional pointer to receive the postfixOut + * starting at the found tagOut field. + * @param[out] typeOut: optional pointer to receive the element typeOut character + * @param[in] negative: return negative tagOut as it is. If disabled, then + * always return positive tagOut. Negative tagOut is sometimes used for + * element disambiguation. + * @param[in] recursive: recursively find the last non-zero tagOut + * + * @return Return the end position of the tagOut field, or return -1 if not + * found. + */ int findTagInElementName(long* tagOut = nullptr, int* lenOut = nullptr, std::string* postfixOut = nullptr, diff --git a/src/App/core-app.dox b/src/App/core-app.dox index 695b7658b3..55cf943198 100644 --- a/src/App/core-app.dox +++ b/src/App/core-app.dox @@ -3,6 +3,10 @@ * @ingroup CORE * @brief The part of FreeCAD that works without GUI (console or server mode) * + * @htmlonly + *
+ * @endhtmlonly + * * @details It contains the App namespace and defines core concepts such as * @ref ApplicationGroup "Application", @ref DocumentGroup "Document", @ref * DocumentObjectGroup "DocumentObject", @ref PropertyFramework "Property @@ -14,6 +18,10 @@ * in @ref App::Document "Document" and @ref App::DocumentObject * "DocumentObject". In addition, %App has a representation of the running * @ref App::Application "Application". + * + * @htmlonly + *
+ * @endhtmlonly */ /** @@ -827,6 +835,45 @@ * object are tracked and applied on the copy. */ +/** + * @defgroup ElementMapping Element Mapping for Topological naming. + * @ingroup APP + * @brief Element mapping system for topological naming + * + * Since the names of vertices, edges, and faces that we obtain from + * OpenCascade are not stable under modifications of the shape, FreeCAD + * implements a system called "topological naming" to provide stable names for + * these elements. + * + * A shape in FreeCAD is represented by Part::TopoShape, a subclass of + * Data::ComplexGeoData that contains much of the logic for element mapping. + * This logic mainly resides in package Data. + * + * A @ref Data::ComplexGeoData "ComplexGeoData" object contains a @ref + * Data::ComplexGeoData::Tag "Tag" that uniquely identifies the shape in the + * document. A @ref Data::ComplexGeoData "ComplexGeoData" object also contains + * a @ref Data::ElementMap "ElementMap" that maintains a mapping from an @ref + * Data::IndexedName "IndexedName" to a @ref Data::MappedName "MappedName". + * + * An indexed name, is a name that we obtain from the shapes from OpenCascade. + * A mapped name is a name that is formed by means of topological relations. A + * mapped name consists of an immutable base name (called the "data") while the + * second part is appended on operations on the shape. This is called the + * "postfix" of the mapped name. For a selection of used postfix tags, see + * the @ref ElementNameConstants "Element name constants" section in Data. + * + * An example of a mapped name is + * `Pocket.;g2;SKT;:H7cf,E;:G;XTR;:H7cf:7,F;:M;CUT;:H-7d0:7,F.Face8` which + * roughly means that the selected face `Face8` was modified `M` with a `CUT` + * operation. The `T` or `H` tags provide the tag of the shape in decimal and + * hexadecimal respectively. + * + * Because multiple operations can result in long mapped names, FreeCAD makes + * use of a string hasher that creates short hashes out of the long mapped + * names. An example of the hashed name for the above mapped name is: + * `Pocket.;#20:2;:M;CUT;:H-e61:7,F.Face8`. + */ + /** * @namespace App * @ingroup APP @@ -841,3 +888,10 @@ * For a more high-level discussion see the topic @ref APP "App". */ +/** + * @namespace Data + * @ingroup ElementMapping + * @brief The namespace for element names + * + */ +