Files
create/src/App/ComplexGeoData.h

485 lines
18 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* Copyright (c) 2022 Zheng, Lei <realthunder.dev@gmail.com> *
* Copyright (c) 2023 FreeCAD Project Association *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef APP_COMPLEX_GEO_DATA_H
#define APP_COMPLEX_GEO_DATA_H
#include <algorithm>
#include <Base/Handle.h>
#include <Base/Matrix.h>
#include <Base/Persistence.h>
#include "MappedName.h"
#include "MappedElement.h"
#include "ElementMap.h"
#include "StringHasher.h"
#ifdef __GNUC__
# include <cstdint>
#endif
namespace Base
{
class Placement;
class Rotation;
template <class _Precision> class BoundBox3;// NOLINT
using BoundBox3d = BoundBox3<double>;
}
namespace Data
{
//struct MappedChildElements;
/// Option for App::GeoFeature::searchElementCache()
enum class SearchOptions {
/// Whether to compare shape geometry
CheckGeometry = 1,
SingleResult = 2,
};
/** Segments
* Sub-element type of the ComplexGeoData type
* It is used to split an object in further sub-parts.
*/
class AppExport Segment: public Base::BaseClass
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();// NOLINT
public:
~Segment() override = default;
virtual std::string getName() const=0;
};
enum ElementMapResetPolicy
{
AllowNoMap,
ForceEmptyMap
};
/** ComplexGeoData Object
*/
class AppExport ComplexGeoData: public Base::Persistence, public Base::Handled
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();// NOLINT
public:
struct Line {uint32_t I1; uint32_t I2;};
struct Facet {uint32_t I1; uint32_t I2; uint32_t I3;};
struct Domain {
std::vector<Base::Vector3d> points;
std::vector<Facet> 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
*/
virtual std::vector<const char*> getElementTypes() const=0;
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.
static std::pair<std::string, unsigned long> getTypeAndIndex(const char* Name);
/// get the sub-element by type and number
virtual Segment* getSubElement(const char* Type, unsigned long) const=0;
/// get sub-element by combined name
virtual Segment* getSubElementByName(const char* Name) const;
/** Get lines from segment */
virtual void getLinesFromSubElement(
const Segment*,
std::vector<Base::Vector3d> &Points,
std::vector<Line> &lines) const;
/** Get faces from segment */
virtual void getFacesFromSubElement(
const Segment*,
std::vector<Base::Vector3d> &Points,
std::vector<Base::Vector3d> &PointNormals,
std::vector<Facet> &faces) const;
//@}
/** @name Placement control */
//@{
/** Applies an additional transformation to the current transformation. */
void applyTransform(const Base::Matrix4D& rclTrf);
/** Applies an additional translation to the current transformation. */
void applyTranslation(const Base::Vector3d&);
/** Applies an additional rotation to the current transformation. */
void applyRotation(const Base::Rotation&);
/** Override the current transformation with a placement
* using the setTransform() method.
*/
void setPlacement(const Base::Placement& rclPlacement);
/** 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.
*/
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.
*/
virtual Base::Matrix4D getTransform() const = 0;
//@}
/** @name Modification */
//@{
/// Applies a transformation on the real geometric data type
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
virtual double getAccuracy() const;
/// Get the bound box
virtual Base::BoundBox3d getBoundBox() const=0;
/** Get point from line object intersection */
virtual Base::Vector3d getPointFromLineIntersection(
const Base::Vector3f& base,
const Base::Vector3f& dir) const;
/** Get points from object with given accuracy */
virtual void getPoints(std::vector<Base::Vector3d> &Points,
std::vector<Base::Vector3d> &Normals,
double Accuracy, uint16_t flags=0) const;
/** Get lines from object with given accuracy */
virtual void getLines(std::vector<Base::Vector3d> &Points,std::vector<Line> &lines,
double Accuracy, uint16_t flags=0) const;
/** Get faces from object with given accuracy */
virtual void getFaces(std::vector<Base::Vector3d> &Points,std::vector<Facet> &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.
*/
virtual bool getCenterOfGravity(Base::Vector3d& center) const;
//@}
static const std::string &elementMapPrefix();
/** @name Element name mapping */
//@{
/** Get element indexed name
*
* @param name: the input name
* @param sid: optional output of and App::StringID involved forming this mapped name
*
* @return Returns an indexed name.
*/
IndexedName getIndexedName(const MappedName & name,
ElementIDRefs *sid = nullptr) const;
/** Get element mapped name
*
* @param name: the input name
* @param allowUnmapped: If the queried element is not mapped, then return
* an empty name if \c allowUnmapped is false, or
* else, return the indexed name.
* @param sid: optional output of and App::StringID involved forming this mapped name
* @return Returns the mapped name.
*/
MappedName getMappedName(const IndexedName & element,
bool allowUnmapped = false,
ElementIDRefs *sid = nullptr) const;
/** Return a pair of indexed name and mapped name
*
* @param name: the input name.
* @param sid: optional output of 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.
*
* This function guesses whether the input name is an indexed name or
* mapped, and perform a lookup and return the names found. If the input
* name contains only alphabets and underscore followed by optional digits,
* it will be treated as indexed name. Or else, it will be treated as
* mapped name.
*/
MappedElement getElementName(const char * name,
ElementIDRefs *sid = nullptr,
bool copy = false) const;
/** 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.
*
* 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.
*/
MappedName setElementName(const IndexedName& element,
const MappedName& name,
long masterTag,
const ElementIDRefs* sid = nullptr,
bool overwrite = false) {
return _elementMap -> setElementName(element, name, masterTag, sid, overwrite);
}
bool hasElementMap() {
return _elementMap != nullptr;
}
/** Get mapped element names
*
* @param element: original element name with \c Type + \c Index
* @param needUnmapped: if true, return the original element name if no
* mapping is found
*
* @return a list of mapped names of the give element along with their
* associated string ID references
*/
std::vector<std::pair<MappedName, ElementIDRefs> >
getElementMappedNames(const IndexedName & element, bool needUnmapped=false) const;
/// Hash the child element map postfixes to shorten element name from hierarchical maps
void hashChildMaps();
/// 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
virtual void reTagElementMap(long tag,
App::StringHasherRef hasher,
const char *postfix=nullptr) {
(void)tag;
(void)hasher;
(void)postfix;
}
// NOTE: getElementHistory is now in ElementMap
long getElementHistory(const MappedName & name,
MappedName *original=nullptr, std::vector<MappedName> *history=nullptr) const {
return _elementMap->getElementHistory(name, Tag, original, history);
};
void setMappedChildElements(const std::vector<Data::ElementMap::MappedChildElements> & children);
std::vector<Data::ElementMap::MappedChildElements> getMappedChildElements() const;
char elementType(const Data::MappedName &) const;
char elementType(const Data::IndexedName &) const;
char elementType(const char *name) const;
/** Reset/swap the element map
*
* @param elementMap: optional new element map
*
* @return Returns the existing element map.
*/
virtual ElementMapPtr resetElementMap(ElementMapPtr elementMap = ElementMapPtr())
{
_elementMap.swap(elementMap);
return elementMap;
}
/// Get the entire element map
std::vector<MappedElement> getElementMap() const;
/// Set the entire element map
void setElementMap(const std::vector<MappedElement> &elements);
/// Get the current element map size
size_t getElementMapSize(bool flush=true) const;
/// Return the higher level element names of the given element
virtual std::vector<IndexedName> getHigherElements(const char *name, bool silent=false) const;
/// Return the current element map version
virtual std::string getElementMapVersion() const;
/// Return true to signal element map version change
virtual bool checkElementMapVersion(const char * ver) const;
/// 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
*
* @param name: the input element name
* @param cb: trace callback with call signature.
* @sa TraceCallback
*/
void traceElement(const MappedName& name, TraceCallback cb) const
{
_elementMap->traceElement(name, Tag, cb);
}
/** Flush an internal buffering for element mapping */
virtual void flushElementMap() const;
//@}
/** @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;
void setPersistenceFileName(const char *name) const;
virtual void beforeSave() const;
bool isRestoreFailed() const { return _restoreFailed; }
void resetRestoreFailure() const { _restoreFailed = true; }
//@}
/**
* Debugging method to dump an entire element map in human readable form to a stream
* @param 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
*/
const std::string dumpElementMap() const;
protected:
/// from local to outside
inline Base::Vector3d transformPointToOutside(const Base::Vector3f& vec) const
{
return getTransform() * Base::Vector3d(static_cast<double>(vec.x),
static_cast<double>(vec.y),
static_cast<double>(vec.z));
}
/// from local to outside
template<typename Vec>
inline std::vector<Base::Vector3d> transformPointsToOutside(const std::vector<Vec>& input) const
{
std::vector<Base::Vector3d> output;
output.reserve(input.size());
Base::Matrix4D mat(getTransform());
std::transform(input.cbegin(), input.cend(), std::back_inserter(output), [&mat](const Vec& vec) {
return mat * Base::Vector3d(static_cast<double>(vec.x),
static_cast<double>(vec.y),
static_cast<double>(vec.z));
});
return output;
}
inline Base::Vector3d transformVectorToOutside(const Base::Vector3f& vec) const
{
Base::Matrix4D mat(getTransform());
mat.setCol(3, Base::Vector3d());
return mat * Base::Vector3d(static_cast<double>(vec.x),
static_cast<double>(vec.y),
static_cast<double>(vec.z));
}
template<typename Vec>
std::vector<Base::Vector3d> transformVectorsToOutside(const std::vector<Vec>& input) const
{
std::vector<Base::Vector3d> output;
output.reserve(input.size());
Base::Matrix4D mat(getTransform());
mat.setCol(3, Base::Vector3d());
std::transform(input.cbegin(), input.cend(), std::back_inserter(output), [&mat](const Vec& vec) {
return mat * Base::Vector3d(static_cast<double>(vec.x),
static_cast<double>(vec.y),
static_cast<double>(vec.z));
});
return output;
}
/// from local to inside
inline Base::Vector3f transformPointToInside(const Base::Vector3d& vec) const
{
Base::Matrix4D tmpM(getTransform());
tmpM.inverse();
Base::Vector3d tmp = tmpM * vec;
return Base::Vector3f(static_cast<float>(tmp.x),
static_cast<float>(tmp.y),
static_cast<float>(tmp.z));
}
public:
mutable long Tag{0};
/// String hasher for element name shortening
mutable App::StringHasherRef Hasher;
protected:
void restoreStream(std::istream & stream, std::size_t count);
void readElements(Base::XMLReader& reader, size_t count);
/// from local to outside
inline Base::Vector3d transformToOutside(const Base::Vector3f& vec) const
{
return getTransform() * Base::Vector3d(static_cast<double>(vec.x),
static_cast<double>(vec.y),
static_cast<double>(vec.z));
}
/// from local to inside
inline Base::Vector3f transformToInside(const Base::Vector3d& vec) const
{
Base::Matrix4D tmpM(getTransform());
tmpM.inverse();
Base::Vector3d tmp = tmpM * vec;
return Base::Vector3f(static_cast<float>(tmp.x),
static_cast<float>(tmp.y),
static_cast<float>(tmp.z));
}
protected:
ElementMapPtr elementMap(bool flush=true) const;
private:
ElementMapPtr _elementMap;
protected:
mutable std::string _persistenceName;
mutable bool _restoreFailed = false;
};
} //namespace App
#endif