2
0
Эх сурвалжийг харах

Prototype new triangulation.

Kim Kulling 4 жил өмнө
parent
commit
8e4ee11bf3

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 229 - 275
code/AssetLib/IFC/IFCOpenings.cpp


+ 2 - 2
code/AssetLib/IFC/IFCUtil.cpp

@@ -192,7 +192,7 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &normals, bool norm
     size_t vidx = std::accumulate(mVertcnt.begin(), begin, 0);
     for (iit = begin; iit != end; vidx += *iit++) {
         if (!*iit) {
-            normals.push_back(IfcVector3());
+            normals.emplace_back();
             continue;
         }
         for (size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
@@ -206,7 +206,7 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &normals, bool norm
             ++cnt;
         }
 
-        normals.push_back(IfcVector3());
+        normals.emplace_back();
         NewellNormal<4, 4, 4>(normals.back(), *iit, &temp[0], &temp[1], &temp[2], Capa);
     }
 

+ 141 - 151
code/AssetLib/IFC/IFCUtil.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -47,40 +46,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef INCLUDED_IFCUTIL_H
 #define INCLUDED_IFCUTIL_H
 
-#include "AssetLib/IFC/IFCReaderGen_2x3.h"
 #include "AssetLib/IFC/IFCLoader.h"
+#include "AssetLib/IFC/IFCReaderGen_2x3.h"
 #include "AssetLib/Step/STEPFile.h"
 
-#include <assimp/mesh.h>
 #include <assimp/material.h>
+#include <assimp/mesh.h>
 
 struct aiNode;
 
 namespace Assimp {
 namespace IFC {
 
-    typedef double IfcFloat;
-
-    // IfcFloat-precision math data types
-    typedef aiVector2t<IfcFloat> IfcVector2;
-    typedef aiVector3t<IfcFloat> IfcVector3;
-    typedef aiMatrix4x4t<IfcFloat> IfcMatrix4;
-    typedef aiMatrix3x3t<IfcFloat> IfcMatrix3;
-    typedef aiColor4t<IfcFloat> IfcColor4;
+using IfcFloat = double;
 
+// IfcFloat-precision math data types
+using IfcVector2 = aiVector2t<IfcFloat>;
+using IfcVector3 = aiVector3t<IfcFloat>;
+using IfcMatrix4 = aiMatrix4x4t<IfcFloat>;
+using IfcMatrix3 = aiMatrix3x3t<IfcFloat>;
+using IfcColor4 = aiColor4t<IfcFloat>;
 
 // ------------------------------------------------------------------------------------------------
 // Helper for std::for_each to delete all heap-allocated items in a container
 // ------------------------------------------------------------------------------------------------
-template<typename T>
+template <typename T>
 struct delete_fun {
-    void operator()(T* del) {
+    void operator()(T *del) {
         delete del;
     }
 };
 
-
-
 // ------------------------------------------------------------------------------------------------
 // Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
 // ------------------------------------------------------------------------------------------------
@@ -89,32 +85,29 @@ struct TempMesh {
     std::vector<unsigned int> mVertcnt;
 
     // utilities
-    aiMesh* ToMesh();
+    aiMesh *ToMesh();
     void Clear();
-    void Transform(const IfcMatrix4& mat);
+    void Transform(const IfcMatrix4 &mat);
     IfcVector3 Center() const;
-    void Append(const TempMesh& other);
+    void Append(const TempMesh &other);
     bool IsEmpty() const;
     void RemoveAdjacentDuplicates();
     void RemoveDegenerates();
     void FixupFaceOrientation();
-    static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
+    static IfcVector3 ComputePolygonNormal(const IfcVector3 *vtcs, size_t cnt, bool normalize = true);
     IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
-    void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
-    void Swap(TempMesh& other);
+    void ComputePolygonNormals(std::vector<IfcVector3> &normals, bool normalize = true, size_t ofs = 0) const;
+    void Swap(TempMesh &other);
 };
 
-inline
-bool TempMesh::IsEmpty() const {
+inline bool TempMesh::IsEmpty() const {
     return mVerts.empty() && mVertcnt.empty();
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // Temporary representation of an opening in a wall or a floor
 // ------------------------------------------------------------------------------------------------
-struct TempOpening
-{
+struct TempOpening {
     const IFC::Schema_2x3::IfcSolidModel *solid;
     IfcVector3 extrusionDir;
 
@@ -129,90 +122,98 @@ struct TempOpening
     std::vector<IfcVector3> wallPoints;
 
     // ------------------------------------------------------------------------------
-    TempOpening()
-        : solid()
-        , extrusionDir()
-        , profileMesh()
-    {
+    TempOpening() :
+            solid(),
+            extrusionDir(),
+            profileMesh() {
+        // empty
     }
 
     // ------------------------------------------------------------------------------
-    TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir,
-        std::shared_ptr<TempMesh> profileMesh,
-        std::shared_ptr<TempMesh> profileMesh2D)
-        : solid(solid)
-        , extrusionDir(extrusionDir)
-        , profileMesh(profileMesh)
-        , profileMesh2D(profileMesh2D)
-    {
+    TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
+            std::shared_ptr<TempMesh> profileMesh,
+            std::shared_ptr<TempMesh> profileMesh2D) :
+            solid(solid),
+            extrusionDir(extrusionDir),
+            profileMesh(profileMesh),
+            profileMesh2D(profileMesh2D) {
+        // empty
     }
 
     // ------------------------------------------------------------------------------
-    void Transform(const IfcMatrix4& mat); // defined later since TempMesh is not complete yet
-
-
+    void Transform(const IfcMatrix4 &mat); // defined later since TempMesh is not complete yet
 
     // ------------------------------------------------------------------------------
     // Helper to sort openings by distance from a given base point
     struct DistanceSorter {
 
-        DistanceSorter(const IfcVector3& base) : base(base) {}
+        DistanceSorter(const IfcVector3 &base) :
+                base(base) {}
 
-        bool operator () (const TempOpening& a, const TempOpening& b) const {
-            return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength();
+        bool operator()(const TempOpening &a, const TempOpening &b) const {
+            return (a.profileMesh->Center() - base).SquareLength() < (b.profileMesh->Center() - base).SquareLength();
         }
 
         IfcVector3 base;
     };
 };
 
-
 // ------------------------------------------------------------------------------------------------
 // Intermediate data storage during conversion. Keeps everything and a bit more.
 // ------------------------------------------------------------------------------------------------
-struct ConversionData
-{
-    ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings)
-        : len_scale(1.0)
-        , angle_scale(-1.0)
-        , db(db)
-        , proj(proj)
-        , out(out)
-        , settings(settings)
-        , apply_openings()
-        , collect_openings()
-    {}
+struct ConversionData {
+    ConversionData(const STEP::DB &db, const IFC::Schema_2x3::IfcProject &proj, aiScene *out, const IFCImporter::Settings &settings) :
+            len_scale(1.0),
+            angle_scale(-1.0),
+            plane_angle_in_radians(true),
+            db(db),
+            proj(proj),
+            out(out),
+            wcs(),
+            meshes(),
+            materials(),
+            cached_meshes(),
+            cached_materials(),
+            settings(settings),
+            apply_openings(nullptr),
+            collect_openings(nullptr),
+            already_processed() {
+        // empty
+    }
 
     ~ConversionData() {
-        std::for_each(meshes.begin(),meshes.end(),delete_fun<aiMesh>());
-        std::for_each(materials.begin(),materials.end(),delete_fun<aiMaterial>());
+        std::for_each(meshes.begin(), meshes.end(), delete_fun<aiMesh>());
+        std::for_each(materials.begin(), materials.end(), delete_fun<aiMaterial>());
     }
 
     IfcFloat len_scale, angle_scale;
     bool plane_angle_in_radians;
 
-    const STEP::DB& db;
-    const IFC::Schema_2x3::IfcProject& proj;
-    aiScene* out;
+    const STEP::DB &db;
+    const IFC::Schema_2x3::IfcProject &proj;
+    aiScene *out;
 
     IfcMatrix4 wcs;
-    std::vector<aiMesh*> meshes;
-    std::vector<aiMaterial*> materials;
+    std::vector<aiMesh *> meshes;
+    std::vector<aiMaterial *> materials;
 
     struct MeshCacheIndex {
-        const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex;
-        MeshCacheIndex() : item(nullptr), matindex(0) { }
-        MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { }
-        bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; }
-        bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
+        const IFC::Schema_2x3::IfcRepresentationItem *item;
+        unsigned int matindex;
+        MeshCacheIndex() :
+                item(nullptr), matindex(0) {}
+        MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem *i, unsigned int mi) :
+                item(i), matindex(mi) {}
+        bool operator==(const MeshCacheIndex &o) const { return item == o.item && matindex == o.matindex; }
+        bool operator<(const MeshCacheIndex &o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
     };
-    typedef std::map<MeshCacheIndex, std::set<unsigned int> > MeshCache;
+    using MeshCache = std::map<MeshCacheIndex, std::set<unsigned int>>;
     MeshCache cached_meshes;
 
-    typedef std::map<const IFC::Schema_2x3::IfcSurfaceStyle*, unsigned int> MaterialCache;
+    using MaterialCache = std::map<const IFC::Schema_2x3::IfcSurfaceStyle *, unsigned int>;
     MaterialCache cached_materials;
 
-    const IFCImporter::Settings& settings;
+    const IFCImporter::Settings &settings;
 
     // Intermediate arrays used to resolve openings in walls: only one of them
     // can be given at a time. apply_openings if present if the current element
@@ -220,34 +221,33 @@ struct ConversionData
     // collect_openings is present only if the current element is an
     // IfcOpeningElement, for which all the geometry needs to be preserved
     // for later processing by a parent, which is a wall.
-    std::vector<TempOpening>* apply_openings;
-    std::vector<TempOpening>* collect_openings;
+    std::vector<TempOpening> *apply_openings;
+    std::vector<TempOpening> *collect_openings;
 
     std::set<uint64_t> already_processed;
 };
 
-
 // ------------------------------------------------------------------------------------------------
 // Binary predicate to compare vectors with a given, quadratic epsilon.
 // ------------------------------------------------------------------------------------------------
 struct FuzzyVectorCompare {
 
-    FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
-    bool operator()(const IfcVector3& a, const IfcVector3& b) {
-        return std::abs((a-b).SquareLength()) < epsilon;
+    FuzzyVectorCompare(IfcFloat epsilon) :
+            epsilon(epsilon) {}
+    bool operator()(const IfcVector3 &a, const IfcVector3 &b) {
+        return std::abs((a - b).SquareLength()) < epsilon;
     }
 
     const IfcFloat epsilon;
 };
 
-
 // ------------------------------------------------------------------------------------------------
 // Ordering predicate to totally order R^2 vectors first by x and then by y
 // ------------------------------------------------------------------------------------------------
 struct XYSorter {
 
     // sort first by X coordinates, then by Y coordinates
-    bool operator () (const IfcVector2&a, const IfcVector2& b) const {
+    bool operator()(const IfcVector2 &a, const IfcVector2 &b) const {
         if (a.x == b.x) {
             return a.y < b.y;
         }
@@ -255,67 +255,61 @@ struct XYSorter {
     }
 };
 
-
-
 // conversion routines for common IFC entities, implemented in IFCUtil.cpp
-void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in);
-void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base);
-void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in);
-void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in);
-void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in);
-void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z);
-void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in);
-void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in);
-void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& in);
-void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv);
-void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op);
-bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in);
-IfcFloat ConvertSIPrefix(const std::string& prefix);
-
+void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourRgb &in);
+void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourOrFactor &in, ConversionData &conv, const aiColor4D *base);
+void ConvertCartesianPoint(IfcVector3 &out, const Schema_2x3::IfcCartesianPoint &in);
+void ConvertDirection(IfcVector3 &out, const Schema_2x3::IfcDirection &in);
+void ConvertVector(IfcVector3 &out, const Schema_2x3::IfcVector &in);
+void AssignMatrixAxes(IfcMatrix4 &out, const IfcVector3 &x, const IfcVector3 &y, const IfcVector3 &z);
+void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement3D &in);
+void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement2D &in);
+void ConvertAxisPlacement(IfcVector3 &axis, IfcVector3 &pos, const IFC::Schema_2x3::IfcAxis1Placement &in);
+void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement &in, ConversionData &conv);
+void ConvertTransformOperator(IfcMatrix4 &out, const Schema_2x3::IfcCartesianTransformationOperator &op);
+bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN &in);
+IfcFloat ConvertSIPrefix(const std::string &prefix);
 
 // IFCProfile.cpp
-bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
-bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
+bool ProcessProfile(const Schema_2x3::IfcProfileDef &prof, TempMesh &meshout, ConversionData &conv);
+bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
 
 // IFCMaterial.cpp
-unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
+unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData &conv, bool forceDefaultMat);
 
 // IFCGeometry.cpp
-IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
-bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set<unsigned int>& mesh_indices, ConversionData& conv);
-void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
+IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh &curmesh, bool &ok, IfcVector3 &norOut);
+bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem &item, unsigned int matid, std::set<unsigned int> &mesh_indices, ConversionData &conv);
+void AssignAddedMeshes(std::set<unsigned int> &mesh_indices, aiNode *nd, ConversionData & /*conv*/);
 
-void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout,
-                           ConversionData& conv);
+void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid &swept, TempMesh &meshout,
+        ConversionData &conv);
 
-void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result,
-                              ConversionData& conv, bool collect_openings);
+void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid &solid, TempMesh &result,
+        ConversionData &conv, bool collect_openings);
 
 // IFCBoolean.cpp
 
-void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv);
-void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result,
-                                       const TempMesh& first_operand,
-                                       ConversionData& conv);
-
-void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace* hs, TempMesh& result,
-                                                       const TempMesh& first_operand,
-                                                       ConversionData& conv);
-void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid* as, TempMesh& result,
-                                               const TempMesh& first_operand,
-                                               ConversionData& conv);
+void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &result, ConversionData &conv);
+void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid *hs, TempMesh &result,
+        const TempMesh &first_operand,
+        ConversionData &conv);
 
+void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace *hs, TempMesh &result,
+        const TempMesh &first_operand,
+        ConversionData &conv);
+void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid *as, TempMesh &result,
+        const TempMesh &first_operand,
+        ConversionData &conv);
 
 // IFCOpenings.cpp
 
-bool GenerateOpenings(std::vector<TempOpening>& openings,
-                      const std::vector<IfcVector3>& nors,
-                      TempMesh& curmesh,
-                      bool check_intersection,
-                      bool generate_connection_geometry,
-                      const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0));
-
-
+bool GenerateOpenings(std::vector<TempOpening> &openings,
+        const std::vector<IfcVector3> &nors,
+        TempMesh &curmesh,
+        bool check_intersection,
+        bool generate_connection_geometry,
+        const IfcVector3 &wall_extrusion_axis = IfcVector3(0, 1, 0));
 
 // IFCCurve.cpp
 
@@ -324,8 +318,8 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 // ------------------------------------------------------------------------------------------------
 class CurveError {
 public:
-    CurveError(const std::string& s)
-    : mStr(s) {
+    CurveError(const std::string &s) :
+            mStr(s) {
         // empty
     }
 
@@ -338,18 +332,17 @@ public:
 // ------------------------------------------------------------------------------------------------
 class Curve {
 protected:
-    Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv)
-    : base_entity(base_entity)
-    , conv(conv) {
+    Curve(const Schema_2x3::IfcCurve &base_entity, ConversionData &conv) :
+            base_entity(base_entity),
+            conv(conv) {
         // empty
     }
 
 public:
-    typedef std::pair<IfcFloat, IfcFloat> ParamRange;
+    using ParamRange = std::pair<IfcFloat, IfcFloat>;
 
     virtual ~Curve() {}
 
-
     // check if a curve is closed
     virtual bool IsClosed() const = 0;
 
@@ -359,56 +352,53 @@ public:
     // try to match a point on the curve to a given parameter
     // for self-intersecting curves, the result is not ambiguous and
     // it is undefined which parameter is returned.
-    virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const;
+    virtual bool ReverseEval(const IfcVector3 &val, IfcFloat &paramOut) const;
 
     // get the range of the curve (both inclusive).
     // +inf and -inf are valid return values, the curve is not bounded in such a case.
-    virtual std::pair<IfcFloat,IfcFloat> GetParametricRange() const = 0;
+    virtual std::pair<IfcFloat, IfcFloat> GetParametricRange() const = 0;
     IfcFloat GetParametricRangeDelta() const;
 
     // estimate the number of sample points that this curve will require
-    virtual size_t EstimateSampleCount(IfcFloat start,IfcFloat end) const;
+    virtual size_t EstimateSampleCount(IfcFloat start, IfcFloat end) const;
 
     // intelligently sample the curve based on the current settings
     // and append the result to the mesh
-    virtual void SampleDiscrete(TempMesh& out,IfcFloat start,IfcFloat end) const;
+    virtual void SampleDiscrete(TempMesh &out, IfcFloat start, IfcFloat end) const;
 
 #ifdef ASSIMP_BUILD_DEBUG
     // check if a particular parameter value lies within the well-defined range
     bool InRange(IfcFloat) const;
 #endif
-    static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv);
+    static Curve *Convert(const IFC::Schema_2x3::IfcCurve &, ConversionData &conv);
 
 protected:
-    const Schema_2x3::IfcCurve& base_entity;
-    ConversionData& conv;
+    const Schema_2x3::IfcCurve &base_entity;
+    ConversionData &conv;
 };
 
-
 // --------------------------------------------------------------------------------
 // A BoundedCurve always holds the invariant that GetParametricRange()
 // never returns infinite values.
 // --------------------------------------------------------------------------------
 class BoundedCurve : public Curve {
 public:
-    BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv)
-        : Curve(entity,conv)
-    {}
+    BoundedCurve(const Schema_2x3::IfcBoundedCurve &entity, ConversionData &conv) :
+            Curve(entity, conv) {}
 
 public:
-
-    bool IsClosed() const;
+    bool IsClosed() const override;
 
 public:
-
     // sample the entire curve
-    void SampleDiscrete(TempMesh& out) const;
+    void SampleDiscrete(TempMesh &out) const;
     using Curve::SampleDiscrete;
 };
 
 // IfcProfile.cpp
-bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
-}
-}
+bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
+
+} // namespace IFC
+} // namespace Assimp
 
 #endif

+ 86 - 124
code/Common/PolyTools.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -51,134 +50,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-// -------------------------------------------------------------------------------
-/** Compute the signed area of a triangle.
- *  The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline double GetArea2D(const T &v1, const T &v2, const T &v3) {
-    return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y));
-}
-
-// -------------------------------------------------------------------------------
-/** Test if a given point p2 is on the left side of the line formed by p0-p1.
- *  The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline bool OnLeftSideOfLine2D(const T &p0, const T &p1, const T &p2) {
-    return GetArea2D(p0, p2, p1) > 0;
-}
+template<class T>
+class TBoundingBox2D {
+    T mMin, mMax;
 
-// -------------------------------------------------------------------------------
-/** Test if a given point is inside a given triangle in R2.
- * The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline bool PointInTriangle2D(const T &p0, const T &p1, const T &p2, const T &pp) {
-    // Point in triangle test using baryzentric coordinates
-    const aiVector2D v0 = p1 - p0;
-    const aiVector2D v1 = p2 - p0;
-    const aiVector2D v2 = pp - p0;
-
-    double dot00 = v0 * v0;
-    double dot01 = v0 * v1;
-    double dot02 = v0 * v2;
-    double dot11 = v1 * v1;
-    double dot12 = v1 * v2;
-
-    const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
-    dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
-    dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
-
-    return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1);
-}
-
-// -------------------------------------------------------------------------------
-/** Check whether the winding order of a given polygon is counter-clockwise.
- *  The function accepts an unconstrained template parameter, but is intended
- *  to be used only with aiVector2D and aiVector3D (z axis is ignored, only
- *  x and y are taken into account).
- * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++
- */
-template <typename T>
-inline bool IsCCW(T *in, size_t npoints) {
-    double aa, bb, cc, b, c, theta;
-    double convex_turn;
-    double convex_sum = 0;
-
-    ai_assert(npoints >= 3);
-
-    for (size_t i = 0; i < npoints - 2; i++) {
-        aa = ((in[i + 2].x - in[i].x) * (in[i + 2].x - in[i].x)) +
-             ((-in[i + 2].y + in[i].y) * (-in[i + 2].y + in[i].y));
-
-        bb = ((in[i + 1].x - in[i].x) * (in[i + 1].x - in[i].x)) +
-             ((-in[i + 1].y + in[i].y) * (-in[i + 1].y + in[i].y));
-
-        cc = ((in[i + 2].x - in[i + 1].x) *
-                     (in[i + 2].x - in[i + 1].x)) +
-             ((-in[i + 2].y + in[i + 1].y) *
-                     (-in[i + 2].y + in[i + 1].y));
-
-        b = std::sqrt(bb);
-        c = std::sqrt(cc);
-        theta = std::acos((bb + cc - aa) / (2 * b * c));
-
-        if (OnLeftSideOfLine2D(in[i], in[i + 2], in[i + 1])) {
-            //  if (convex(in[i].x, in[i].y,
-            //      in[i+1].x, in[i+1].y,
-            //      in[i+2].x, in[i+2].y)) {
-            convex_turn = AI_MATH_PI_F - theta;
-            convex_sum += convex_turn;
-        } else {
-            convex_sum -= AI_MATH_PI_F - theta;
-        }
-    }
-    aa = ((in[1].x - in[npoints - 2].x) *
-                 (in[1].x - in[npoints - 2].x)) +
-         ((-in[1].y + in[npoints - 2].y) *
-                 (-in[1].y + in[npoints - 2].y));
-
-    bb = ((in[0].x - in[npoints - 2].x) *
-                 (in[0].x - in[npoints - 2].x)) +
-         ((-in[0].y + in[npoints - 2].y) *
-                 (-in[0].y + in[npoints - 2].y));
-
-    cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) +
-         ((-in[1].y + in[0].y) * (-in[1].y + in[0].y));
-
-    b = std::sqrt(bb);
-    c = std::sqrt(cc);
-    theta = std::acos((bb + cc - aa) / (2 * b * c));
-
-    //if (convex(in[npoints-2].x, in[npoints-2].y,
-    //  in[0].x, in[0].y,
-    //  in[1].x, in[1].y)) {
-    if (OnLeftSideOfLine2D(in[npoints - 2], in[1], in[0])) {
-        convex_turn = AI_MATH_PI_F - theta;
-        convex_sum += convex_turn;
-    } else {
-        convex_sum -= AI_MATH_PI_F - theta;
+    TBoundingBox2D( const T &min, const T &max ) :
+            mMin( min ),
+            mMax( max ) {
+        // empty
     }
+};
 
-    return convex_sum >= (2 * AI_MATH_PI_F);
-}
+using BoundingBox2D = TBoundingBox2D<aiVector2D>;
 
 // -------------------------------------------------------------------------------
-/** Compute the normal of an arbitrary polygon in R3.
- *
- *  The code is based on Newell's formula, that is a polygons normal is the ratio
- *  of its area when projected onto the three coordinate axes.
- *
- *  @param out Receives the output normal
- *  @param num Number of input vertices
- *  @param x X data source. x[ofs_x*n] is the n'th element.
- *  @param y Y data source. y[ofs_y*n] is the y'th element
- *  @param z Z data source. z[ofs_z*n] is the z'th element
- *
- *  @note The data arrays must have storage for at least num+2 elements. Using
- *  this method is much faster than the 'other' NewellNormal()
- */
+/// Compute the normal of an arbitrary polygon in R3.
+///
+/// The code is based on Newell's formula, that is a polygons normal is the ratio
+/// of its area when projected onto the three coordinate axes.
+///
+/// @param out Receives the output normal
+/// @param num Number of input vertices
+/// @param x X data source. x[ofs_x*n] is the n'th element.
+/// @param y Y data source. y[ofs_y*n] is the y'th element
+/// @param z Z data source. z[ofs_z*n] is the z'th element
+///
+/// @note The data arrays must have storage for at least num+2 elements. Using
+/// this method is much faster than the 'other' NewellNormal()
+// -------------------------------------------------------------------------------
 template <size_t ofs_x, size_t ofs_y, size_t ofs_z, typename TReal>
 inline void NewellNormal(aiVector3t<TReal> &out, size_t num, TReal *x, TReal *y, TReal *z, size_t bufferSize) {
     ai_assert(bufferSize > num);
@@ -223,6 +122,69 @@ inline void NewellNormal(aiVector3t<TReal> &out, size_t num, TReal *x, TReal *y,
     out = aiVector3t<TReal>(sum_yz, sum_zx, sum_xy);
 }
 
+// -------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+template <class T>
+inline aiMatrix4x4t<T> DerivePlaneCoordinateSpace(const aiVector3t<T> *vertices, size_t numVertices, bool &ok, aiVector3t<T> &norOut) {
+    const aiVector3t<T> *out = vertices;
+    aiMatrix4x4t<T> m;
+
+    ok = true;
+
+    const size_t s = numVertices;
+
+    const aiVector3t<T> &any_point = out[numVertices - 1u];
+    aiVector3t<T> nor;
+
+    // The input polygon is arbitrarily shaped, therefore we might need some tries
+    // until we find a suitable normal. Note that Newell's algorithm would give
+    // a more robust result, but this variant also gives us a suitable first
+    // axis for the 2D coordinate space on the polygon plane, exploiting the
+    // fact that the input polygon is nearly always a quad.
+    bool done = false;
+    size_t idx = 0;
+    for (size_t i = 0; !done && i < s - 2; done || ++i) {
+        idx = i;
+        for (size_t j = i + 1; j < s - 1; ++j) {
+            nor = -((out[i] - any_point) ^ (out[j] - any_point));
+            if (std::fabs(nor.Length()) > 1e-8f) {
+                done = true;
+                break;
+            }
+        }
+    }
+
+    if (!done) {
+        ok = false;
+        return m;
+    }
+
+    nor.Normalize();
+    norOut = nor;
+
+    aiVector3t<T> r = (out[idx] - any_point);
+    r.Normalize();
+
+    // Reconstruct orthonormal basis
+    // XXX use Gram Schmidt for increased robustness
+    aiVector3t<T> u = r ^ nor;
+    u.Normalize();
+
+    m.a1 = r.x;
+    m.a2 = r.y;
+    m.a3 = r.z;
+
+    m.b1 = u.x;
+    m.b2 = u.y;
+    m.b3 = u.z;
+
+    m.c1 = -nor.x;
+    m.c2 = -nor.y;
+    m.c3 = -nor.z;
+
+    return m;
+}
+
 } // namespace Assimp
 
 #endif

+ 59 - 67
code/PostProcessing/FindDegenerates.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -45,25 +43,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the FindDegenerates post-process step.
 */
 
-
-
 // internal headers
-#include "ProcessHelper.h"
 #include "FindDegenerates.h"
+#include "ProcessHelper.h"
 #include <assimp/Exceptional.h>
 
 using namespace Assimp;
 
 //remove mesh at position 'index' from the scene
-static void removeMesh(aiScene* pScene, unsigned const index);
+static void removeMesh(aiScene *pScene, unsigned const index);
 //correct node indices to meshes and remove references to deleted mesh
-static void updateSceneGraph(aiNode* pNode, unsigned const index);
+static void updateSceneGraph(aiNode *pNode, unsigned const index);
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-FindDegeneratesProcess::FindDegeneratesProcess()
-: mConfigRemoveDegenerates( false )
-, mConfigCheckAreaOfTriangle( false ){
+FindDegeneratesProcess::FindDegeneratesProcess() :
+        mConfigRemoveDegenerates(false),
+        mConfigCheckAreaOfTriangle(false) {
     // empty
 }
 
@@ -75,24 +71,23 @@ FindDegeneratesProcess::~FindDegeneratesProcess() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
+bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const {
     return 0 != (pFlags & aiProcess_FindDegenerates);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup import configuration
-void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
+void FindDegeneratesProcess::SetupProperties(const Importer *pImp) {
     // Get the current value of AI_CONFIG_PP_FD_REMOVE
-    mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
-    mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
+    mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
+    mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void FindDegeneratesProcess::Execute( aiScene* pScene) {
+void FindDegeneratesProcess::Execute(aiScene *pScene) {
     ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
-    for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-    {
+    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
         //Do not process point cloud, ExecuteOnMesh works only with faces data
         if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
             removeMesh(pScene, i);
@@ -102,12 +97,12 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
     ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
 }
 
-static void removeMesh(aiScene* pScene, unsigned const index) {
+static void removeMesh(aiScene *pScene, unsigned const index) {
     //we start at index and copy the pointers one position forward
     //save the mesh pointer to delete it later
     auto delete_me = pScene->mMeshes[index];
     for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) {
-        pScene->mMeshes[i] = pScene->mMeshes[i+1];
+        pScene->mMeshes[i] = pScene->mMeshes[i + 1];
     }
     pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr;
     --(pScene->mNumMeshes);
@@ -117,15 +112,15 @@ static void removeMesh(aiScene* pScene, unsigned const index) {
     updateSceneGraph(pScene->mRootNode, index);
 }
 
-static void updateSceneGraph(aiNode* pNode, unsigned const index) {
+static void updateSceneGraph(aiNode *pNode, unsigned const index) {
     for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
         if (pNode->mMeshes[i] > index) {
             --(pNode->mMeshes[i]);
             continue;
         }
         if (pNode->mMeshes[i] == index) {
-            for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) {
-                pNode->mMeshes[j] = pNode->mMeshes[j+1];
+            for (unsigned j = i; j < pNode->mNumMeshes - 1; ++j) {
+                pNode->mMeshes[j] = pNode->mMeshes[j + 1];
             }
             --(pNode->mNumMeshes);
             --i;
@@ -138,50 +133,50 @@ static void updateSceneGraph(aiNode* pNode, unsigned const index) {
     }
 }
 
-static ai_real heron( ai_real a, ai_real b, ai_real c ) {
+static ai_real heron(ai_real a, ai_real b, ai_real c) {
     ai_real s = (a + b + c) / 2;
-    ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
+    ai_real area = pow((s * (s - a) * (s - b) * (s - c)), (ai_real)0.5);
     return area;
 }
 
-static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
-    const ai_real lx = ( vB.x - vA.x );
-    const ai_real ly = ( vB.y - vA.y );
-    const ai_real lz = ( vB.z - vA.z );
-    ai_real a = lx*lx + ly*ly + lz*lz;
-    ai_real d = pow( a, (ai_real)0.5 );
+static ai_real distance3D(const aiVector3D &vA, aiVector3D &vB) {
+    const ai_real lx = (vB.x - vA.x);
+    const ai_real ly = (vB.y - vA.y);
+    const ai_real lz = (vB.z - vA.z);
+    ai_real a = lx * lx + ly * ly + lz * lz;
+    ai_real d = pow(a, (ai_real)0.5);
 
     return d;
 }
 
-static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
+static ai_real calculateAreaOfTriangle(const aiFace &face, aiMesh *mesh) {
     ai_real area = 0;
 
-    aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
-    aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
-    aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
+    aiVector3D vA(mesh->mVertices[face.mIndices[0]]);
+    aiVector3D vB(mesh->mVertices[face.mIndices[1]]);
+    aiVector3D vC(mesh->mVertices[face.mIndices[2]]);
 
-    ai_real a( distance3D( vA, vB ) );
-    ai_real b( distance3D( vB, vC ) );
-    ai_real c( distance3D( vC, vA ) );
-    area = heron( a, b, c );
+    ai_real a(distance3D(vA, vB));
+    ai_real b(distance3D(vB, vC));
+    ai_real c(distance3D(vC, vA));
+    area = heron(a, b, c);
 
     return area;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported mesh
-bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
+bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
     mesh->mPrimitiveTypes = 0;
 
     std::vector<bool> remove_me;
     if (mConfigRemoveDegenerates) {
-        remove_me.resize( mesh->mNumFaces, false );
+        remove_me.resize(mesh->mNumFaces, false);
     }
 
     unsigned int deg = 0, limit;
-    for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
-        aiFace& face = mesh->mFaces[a];
+    for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+        aiFace &face = mesh->mFaces[a];
         bool first = true;
 
         // check whether the face contains degenerated entries
@@ -191,43 +186,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
             // double points may not come directly after another.
             limit = face.mNumIndices;
             if (face.mNumIndices > 4) {
-                limit = std::min( limit, i+2 );
+                limit = std::min(limit, i + 2);
             }
 
-            for (unsigned int t = i+1; t < limit; ++t) {
-                if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
+            for (unsigned int t = i + 1; t < limit; ++t) {
+                if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
                     // we have found a matching vertex position
                     // remove the corresponding index from the array
                     --face.mNumIndices;
                     --limit;
                     for (unsigned int m = t; m < face.mNumIndices; ++m) {
-                        face.mIndices[ m ] = face.mIndices[ m+1 ];
+                        face.mIndices[m] = face.mIndices[m + 1];
                     }
                     --t;
 
                     // NOTE: we set the removed vertex index to an unique value
                     // to make sure the developer gets notified when his
                     // application attempts to access this data.
-                    face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
+                    face.mIndices[face.mNumIndices] = 0xdeadbeef;
 
-                    if(first) {
+                    if (first) {
                         ++deg;
                         first = false;
                     }
 
-                    if ( mConfigRemoveDegenerates ) {
-                        remove_me[ a ] = true;
+                    if (mConfigRemoveDegenerates) {
+                        remove_me[a] = true;
                         goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
                     }
                 }
             }
 
-            if ( mConfigCheckAreaOfTriangle ) {
-                if ( face.mNumIndices == 3 ) {
-                    ai_real area = calculateAreaOfTriangle( face, mesh );
-                    if ( area < 1e-6 ) {
-                        if ( mConfigRemoveDegenerates ) {
-                            remove_me[ a ] = true;
+            if (mConfigCheckAreaOfTriangle) {
+                if (face.mNumIndices == 3) {
+                    ai_real area = calculateAreaOfTriangle(face, mesh);
+                    if (area < 1e-6) {
+                        if (mConfigRemoveDegenerates) {
+                            remove_me[a] = true;
                             ++deg;
                             goto evil_jump_outside;
                         }
@@ -239,8 +234,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
         }
 
         // We need to update the primitive flags array of the mesh.
-        switch (face.mNumIndices)
-        {
+        switch (face.mNumIndices) {
         case 1u:
             mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
             break;
@@ -254,30 +248,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
             mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
             break;
         };
-evil_jump_outside:
+    evil_jump_outside:
         continue;
     }
 
     // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
     if (mConfigRemoveDegenerates && deg) {
         unsigned int n = 0;
-        for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
-        {
-            aiFace& face_src = mesh->mFaces[a];
+        for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+            aiFace &face_src = mesh->mFaces[a];
             if (!remove_me[a]) {
-                aiFace& face_dest = mesh->mFaces[n++];
+                aiFace &face_dest = mesh->mFaces[n++];
 
                 // Do a manual copy, keep the index array
                 face_dest.mNumIndices = face_src.mNumIndices;
-                face_dest.mIndices    = face_src.mIndices;
+                face_dest.mIndices = face_src.mIndices;
 
                 if (&face_src != &face_dest) {
                     // clear source
                     face_src.mNumIndices = 0;
                     face_src.mIndices = nullptr;
                 }
-            }
-            else {
+            } else {
                 // Otherwise delete it if we don't need this face
                 delete[] face_src.mIndices;
                 face_src.mIndices = nullptr;
@@ -295,7 +287,7 @@ evil_jump_outside:
     }
 
     if (deg && !DefaultLogger::isNullLogger()) {
-        ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives");
+        ASSIMP_LOG_WARN_F("Found ", deg, " degenerated primitives");
     }
     return false;
 }

+ 151 - 315
code/PostProcessing/TriangulateProcess.cpp

@@ -47,34 +47,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  The triangulation algorithm will handle concave or convex polygons.
  *  Self-intersecting or non-planar polygons are not rejected, but
  *  they're probably not triangulated correctly.
- *
- * DEBUG SWITCHES - do not enable any of them in release builds:
- *
- * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
- *   - generates vertex colors to represent the face winding order.
- *     the first vertex of a polygon becomes red, the last blue.
- * AI_BUILD_TRIANGULATE_DEBUG_POLYS
- *   - dump all polygons and their triangulation sequences to
- *     a file
  */
+
 #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
 
 #include "PostProcessing/TriangulateProcess.h"
 #include "Common/PolyTools.h"
 #include "PostProcessing/ProcessHelper.h"
 
+#include "contrib/poly2tri/poly2tri/poly2tri.h"
+
 #include <cstdint>
 #include <memory>
 
-//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
-#define AI_BUILD_TRIANGULATE_DEBUG_POLYS
-
-#define POLY_GRID_Y 40
-#define POLY_GRID_X 70
-#define POLY_GRID_XPAD 20
-#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt"
-
-using namespace Assimp;
+namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
@@ -128,6 +114,7 @@ static bool validateNumIndices(aiMesh *mesh) {
     return bNeed;
 }
 
+// ------------------------------------------------------------------------------------------------
 static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, bool &getNormals) {
     numOut = maxOut = 0;
     getNormals = true;
@@ -146,6 +133,113 @@ static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut,
     }
 }
 
+// ------------------------------------------------------------------------------------------------
+static void quad2Triangles(const aiFace &face, const aiVector3D *verts, aiFace *curOut) {
+    // quads can have at maximum one concave vertex. Determine
+    // this vertex (if it exists) and start tri-fanning from
+    // it.
+    unsigned int start_vertex = 0;
+    for (unsigned int i = 0; i < 4; ++i) {
+        const aiVector3D &v0 = verts[face.mIndices[(i + 3) % 4]];
+        const aiVector3D &v1 = verts[face.mIndices[(i + 2) % 4]];
+        const aiVector3D &v2 = verts[face.mIndices[(i + 1) % 4]];
+
+        const aiVector3D &v = verts[face.mIndices[i]];
+
+        aiVector3D left = (v0 - v);
+        aiVector3D diag = (v1 - v);
+        aiVector3D right = (v2 - v);
+
+        left.Normalize();
+        diag.Normalize();
+        right.Normalize();
+
+        const float angle = std::acos(left * diag) + std::acos(right * diag);
+        if (angle > AI_MATH_PI_F) {
+            // this is the concave point
+            start_vertex = i;
+            break;
+        }
+    }
+
+    const unsigned int temp[] = { face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3] };
+
+    aiFace &nface = *curOut++;
+    nface.mNumIndices = 3;
+    nface.mIndices = face.mIndices;
+
+    nface.mIndices[0] = temp[start_vertex];
+    nface.mIndices[1] = temp[(start_vertex + 1) % 4];
+    nface.mIndices[2] = temp[(start_vertex + 2) % 4];
+
+    aiFace &sface = *curOut++;
+    sface.mNumIndices = 3;
+    sface.mIndices = new unsigned int[3];
+
+    sface.mIndices[0] = temp[start_vertex];
+    sface.mIndices[1] = temp[(start_vertex + 2) % 4];
+    sface.mIndices[2] = temp[(start_vertex + 3) % 4];
+}
+
+// ------------------------------------------------------------------------------------------------
+bool getContourFromePolyline(aiFace &face, aiMesh *pMesh, std::vector<p2t::Point *> &contour,
+        aiMatrix4x4 &m, aiVector3D &vmin, aiVector3D &vmax, ai_real &zcoord) {
+    aiVector3D normal;
+    bool ok = true;
+    m = DerivePlaneCoordinateSpace<ai_real>(pMesh->mVertices, pMesh->mNumVertices, ok, normal);
+    if (!ok) {
+        false;
+    }
+    for (unsigned int i = 0; i < face.mNumIndices; ++i) {
+        unsigned int index = face.mIndices[i];
+
+        const aiVector3D vv = m * pMesh->mVertices[index];
+        // keep Z offset in the plane coordinate system. Ignoring precision issues
+        // (which  are present, of course), this should be the same value for
+        // all polygon vertices (assuming the polygon is planar).
+
+        // XXX this should be guarded, but we somehow need to pick a suitable
+        // epsilon
+        // if(coord != -1.0f) {
+        //  assert(std::fabs(coord - vv.z) < 1e-3f);
+        // }
+        zcoord += vv.z;
+        vmin = std::min(vv, vmin);
+        vmax = std::max(vv, vmax);
+
+        contour.push_back(new p2t::Point(vv.x, vv.y));
+    }
+
+    zcoord /= pMesh->mNumVertices;
+
+    // Further improve the projection by mapping the entire working set into
+    // [0,1] range. This gives us a consistent data range so all epsilons
+    // used below can be constants.
+    vmax -= vmin;
+    const aiVector2D one_vec(1, 1);
+
+    for (p2t::Point* &vv : contour) {
+        vv->x = (vv->x - vmin.x) / vmax.x;
+        vv->y = (vv->y - vmin.y) / vmax.y;
+
+        // sanity rounding
+        aiVector2D cur_vv((ai_real) vv->x, (ai_real)vv->y);
+        cur_vv = std::max(cur_vv, aiVector2D());
+        cur_vv = std::min(cur_vv, one_vec);
+    }
+
+    aiMatrix4x4 mult;
+    mult.a1 = static_cast<ai_real>(1.0) / vmax.x;
+    mult.b2 = static_cast<ai_real>(1.0) / vmax.y;
+
+    mult.a4 = -vmin.x * mult.a1;
+    mult.b4 = -vmin.y * mult.b2;
+    mult.c4 = -zcoord;
+    m = mult * m;
+
+    return true;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Triangulates the given mesh.
 bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
@@ -153,9 +247,11 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
 
     if (!pMesh->mPrimitiveTypes) {
         if (!validateNumIndices(pMesh)) {
+            ASSIMP_LOG_DEBUG("Error while validating number of indices.");
             return false;
         }
     } else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
+        ASSIMP_LOG_DEBUG("???!");
         return false;
     }
 
@@ -163,17 +259,9 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
     size_t numOut = 0, max_out = 0;
     bool getNormals = true;
     calulateNumOutputFaces(pMesh, numOut, max_out, getNormals);
-
-    // Just another check whether aiMesh::mPrimitiveTypes is correct
-    ai_assert(numOut != pMesh->mNumFaces);
-
-    aiVector3D *nor_out = nullptr;
-
-    // if we don't have normals yet, but expect them to be a cheap side
-    // product of triangulation anyway, allocate storage for them.
-    if (!pMesh->mNormals && getNormals) {
-        // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals
-        //  nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+    if (numOut == pMesh->mNumFaces) {
+        ASSIMP_LOG_DEBUG("Error while generating contour.");
+        return false;
     }
 
     // the output mesh will contain triangles, but no polys anymore
@@ -186,18 +274,6 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
     std::vector<aiVector2D> temp_verts(max_out + 2);
 
     // Apply vertex colors to represent the face winding?
-#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
-    if (!pMesh->mColors[0])
-        pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
-    else
-        new (pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices];
-
-    aiColor4D *clr = pMesh->mColors[0];
-#endif
-
-#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
-    FILE *fout = fopen(POLY_OUTPUT_FILE, "a");
-#endif
 
     const aiVector3D *verts = pMesh->mVertices;
 
@@ -206,20 +282,6 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
     for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
         aiFace &face = pMesh->mFaces[a];
 
-        unsigned int *idx = face.mIndices;
-        int num = (int)face.mNumIndices, ear = 0, tmp, prev = num - 1, next = 0, max = num;
-
-        // Apply vertex colors to represent the face winding?
-#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
-        for (unsigned int i = 0; i < face.mNumIndices; ++i) {
-            aiColor4D &c = clr[idx[i]];
-            c.r = (i + 1) / (float)max;
-            c.b = 1.f - c.r;
-        }
-#endif
-
-        aiFace *const last_face = curOut;
-
         // if it's a simple point,line or triangle: just copy it
         if (face.mNumIndices <= 3) {
             aiFace &nface = *curOut++;
@@ -227,278 +289,52 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
             nface.mIndices = face.mIndices;
 
             face.mIndices = nullptr;
-            continue;
-        }
-        // optimized code for quadrilaterals
-        else if (face.mNumIndices == 4) {
-
-            // quads can have at maximum one concave vertex. Determine
-            // this vertex (if it exists) and start tri-fanning from
-            // it.
-            unsigned int start_vertex = 0;
-            for (unsigned int i = 0; i < 4; ++i) {
-                const aiVector3D &v0 = verts[face.mIndices[(i + 3) % 4]];
-                const aiVector3D &v1 = verts[face.mIndices[(i + 2) % 4]];
-                const aiVector3D &v2 = verts[face.mIndices[(i + 1) % 4]];
-
-                const aiVector3D &v = verts[face.mIndices[i]];
-
-                aiVector3D left = (v0 - v);
-                aiVector3D diag = (v1 - v);
-                aiVector3D right = (v2 - v);
-
-                left.Normalize();
-                diag.Normalize();
-                right.Normalize();
-
-                const float angle = std::acos(left * diag) + std::acos(right * diag);
-                if (angle > AI_MATH_PI_F) {
-                    // this is the concave point
-                    start_vertex = i;
-                    break;
-                }
-            }
-
-            const unsigned int temp[] = { face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3] };
-
-            aiFace &nface = *curOut++;
-            nface.mNumIndices = 3;
-            nface.mIndices = face.mIndices;
-
-            nface.mIndices[0] = temp[start_vertex];
-            nface.mIndices[1] = temp[(start_vertex + 1) % 4];
-            nface.mIndices[2] = temp[(start_vertex + 2) % 4];
-
-            aiFace &sface = *curOut++;
-            sface.mNumIndices = 3;
-            sface.mIndices = new unsigned int[3];
-
-            sface.mIndices[0] = temp[start_vertex];
-            sface.mIndices[1] = temp[(start_vertex + 2) % 4];
-            sface.mIndices[2] = temp[(start_vertex + 3) % 4];
-
-            // prevent double deletion of the indices field
+        } else if (face.mNumIndices == 4) {
+            // optimized code for quadrilaterals
+            quad2Triangles(face, verts, curOut);
             face.mIndices = nullptr;
-            continue;
         } else {
-            // A polygon with more than 3 vertices can be either concave or convex.
-            // Usually everything we're getting is convex and we could easily
-            // triangulate by tri-fanning. However, LightWave is probably the only
-            // modeling suite to make extensive use of highly concave, monster polygons ...
-            // so we need to apply the full 'ear cutting' algorithm to get it right.
-
-            // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
-            // We project it onto a plane to get a 2d triangle.
-
-            // Collect all vertices of of the polygon.
-            for (tmp = 0; tmp < max; ++tmp) {
-                temp_verts3d[tmp] = verts[idx[tmp]];
-            }
-
-            // Get Newell-Normal of the polygon. Store it for future use if it's a polygon-only mesh
-            aiVector3D n;
-            NewellNormal<3, 3, 3>(n, max, &temp_verts3d.front().x, &temp_verts3d.front().y, &temp_verts3d.front().z, Capa);
-            if (nor_out) {
-                for (tmp = 0; tmp < max; ++tmp)
-                    nor_out[idx[tmp]] = n;
-            }
-
-            // Select largest normal coordinate to ignore for projection
-            const float ax = (n.x > 0 ? n.x : -n.x);
-            const float ay = (n.y > 0 ? n.y : -n.y);
-            const float az = (n.z > 0 ? n.z : -n.z);
-
-            unsigned int ac = 0, bc = 1; // no z coord. projection to xy
-            float inv = n.z;
-            if (ax > ay) {
-                if (ax > az) { // no x coord. projection to yz
-                    ac = 1;
-                    bc = 2;
-                    inv = n.x;
-                }
-            } else if (ay > az) { // no y coord. projection to zy
-                ac = 2;
-                bc = 0;
-                inv = n.y;
-            }
-
-            // Swap projection axes to take the negated projection vector into account
-            if (inv < 0.f) {
-                std::swap(ac, bc);
-            }
-
-            for (tmp = 0; tmp < max; ++tmp) {
-                temp_verts[tmp].x = verts[idx[tmp]][ac];
-                temp_verts[tmp].y = verts[idx[tmp]][bc];
-                done[tmp] = false;
-            }
-
-#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
-            // plot the plane onto which we mapped the polygon to a 2D ASCII pic
-            aiVector2D bmin, bmax;
-            ArrayBounds(&temp_verts[0], max, bmin, bmax);
-
-            char grid[POLY_GRID_Y][POLY_GRID_X + POLY_GRID_XPAD];
-            std::fill_n((char *)grid, POLY_GRID_Y * (POLY_GRID_X + POLY_GRID_XPAD), ' ');
-
-            for (int i = 0; i < max; ++i) {
-                const aiVector2D &v = (temp_verts[i] - bmin) / (bmax - bmin);
-                const size_t x = static_cast<size_t>(v.x * (POLY_GRID_X - 1)), y = static_cast<size_t>(v.y * (POLY_GRID_Y - 1));
-                char *loc = grid[y] + x;
-                if (grid[y][x] != ' ') {
-                    for (; *loc != ' '; ++loc)
-                        ;
-                    *loc++ = '_';
-                }
-                *(loc + ::ai_snprintf(loc, POLY_GRID_XPAD, "%i", i)) = ' ';
-            }
-
-            for (size_t y = 0; y < POLY_GRID_Y; ++y) {
-                grid[y][POLY_GRID_X + POLY_GRID_XPAD - 1] = '\0';
-                fprintf(fout, "%s\n", grid[y]);
-            }
-
-            fprintf(fout, "\ntriangulation sequence: ");
-#endif
-
-            //
-            // FIXME: currently this is the slow O(kn) variant with a worst case
-            // complexity of O(n^2) (I think). Can be done in O(n).
-            while (num > 3) {
-
-                // Find the next ear of the polygon
-                int num_found = 0;
-                for (ear = next;; prev = ear, ear = next) {
-
-                    // break after we looped two times without a positive match
-                    for (next = ear + 1; done[(next >= max ? next = 0 : next)]; ++next)
-                        ;
-                    if (next < ear) {
-                        if (++num_found == 2) {
-                            break;
-                        }
-                    }
-                    const aiVector2D *pnt1 = &temp_verts[ear],
-                                     *pnt0 = &temp_verts[prev],
-                                     *pnt2 = &temp_verts[next];
-
-                    // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
-                    if (OnLeftSideOfLine2D(*pnt0, *pnt2, *pnt1)) {
-                        continue;
-                    }
-
-                    // and no other point may be contained in this triangle
-                    for (tmp = 0; tmp < max; ++tmp) {
-
-                        // We need to compare the actual values because it's possible that multiple indexes in
-                        // the polygon are referring to the same position. concave_polygon.obj is a sample
-                        //
-                        // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
-                        // PointInTriangle() I'm guessing that it's actually possible to construct
-                        // input data that would cause us to end up with no ears. The problem is,
-                        // which epsilon? If we chose a too large value, we'd get wrong results
-                        const aiVector2D &vtmp = temp_verts[tmp];
-                        if (vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0, *pnt1, *pnt2, vtmp)) {
-                            break;
-                        }
-                    }
-                    if (tmp != max) {
-                        continue;
-                    }
-
-                    // this vertex is an ear
-                    break;
-                }
-                if (num_found == 2) {
-
-                    // Due to the 'two ear theorem', every simple polygon with more than three points must
-                    // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet.
-                    //
-
-                    // Instead we're continuing with the standard tri-fanning algorithm which we'd
-                    // use if we had only convex polygons. That's life.
-                    ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
-
-#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
-                    fprintf(fout, "critical error here, no ear found! ");
-#endif
-                    num = 0;
-                    break;
-                }
-
-                aiFace &nface = *curOut++;
-                nface.mNumIndices = 3;
-
-                if (!nface.mIndices) {
-                    nface.mIndices = new unsigned int[3];
-                }
-
-                // setup indices for the new triangle ...
-                nface.mIndices[0] = prev;
-                nface.mIndices[1] = ear;
-                nface.mIndices[2] = next;
-
-                // exclude the ear from most further processing
-                done[ear] = true;
-                --num;
+            std::vector<p2t::Point *> contour;
+            aiMatrix4x4 m;
+            aiVector3D vmin, vmax;
+            ai_real zcoord = -1;
+            if (!getContourFromePolyline(face, pMesh, contour, m, vmin, vmax, zcoord)) {
+                ASSIMP_LOG_DEBUG("Error while generating contour.");
+                continue;
             }
-            if (num > 0) {
-                // We have three indices forming the last 'ear' remaining. Collect them.
-                aiFace &nface = *curOut++;
-                nface.mNumIndices = 3;
-                if (!nface.mIndices) {
-                    nface.mIndices = new unsigned int[3];
+            p2t::CDT cdt(contour);
+            cdt.Triangulate();
+            const std::vector<p2t::Triangle *> tris = cdt.GetTriangles();
+            const aiMatrix4x4 matInv = m.Inverse();
+            for (p2t::Triangle *tri : tris) {
+                curOut->mNumIndices = 3;
+                curOut->mIndices = new unsigned int[curOut->mNumIndices];
+                for (int i = 0; i < 3; ++i) {
+                    const aiVector2D v = aiVector2D(static_cast<ai_real>(tri->GetPoint(i)->x), static_cast<ai_real>(tri->GetPoint(i)->y));
+//                    ai_assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0);
+                    const aiVector3D v3 = matInv * aiVector3D(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y, zcoord);
+                    temp_verts3d.emplace_back(v3);
+                    curOut->mIndices[i] = (unsigned int) temp_verts3d.size()-1;
                 }
-
-                for (tmp = 0; done[tmp]; ++tmp)
-                    ;
-                nface.mIndices[0] = tmp;
-
-                for (++tmp; done[tmp]; ++tmp)
-                    ;
-                nface.mIndices[1] = tmp;
-
-                for (++tmp; done[tmp]; ++tmp)
-                    ;
-                nface.mIndices[2] = tmp;
+                curOut++;
             }
+            face.mIndices = nullptr;
         }
-
-#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
-
-        for (aiFace *f = last_face; f != curOut; ++f) {
-            unsigned int *i = f->mIndices;
-            fprintf(fout, " (%i %i %i)", i[0], i[1], i[2]);
-        }
-
-        fprintf(fout, "\n*********************************************************************\n");
-        fflush(fout);
-
-#endif
-
-        for (aiFace *f = last_face; f != curOut;) {
-            unsigned int *i = f->mIndices;
-            i[0] = idx[i[0]];
-            i[1] = idx[i[1]];
-            i[2] = idx[i[2]];
-            ++f;
-        }
-
-        delete[] face.mIndices;
-        face.mIndices = nullptr;
     }
 
-#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
-    fclose(fout);
-#endif
-
-    // kill the old faces
     delete[] pMesh->mFaces;
-
-    // ... and store the new ones
     pMesh->mFaces = out;
+    pMesh->mNumVertices = (unsigned int)temp_verts3d.size();
+    delete[] pMesh->mVertices;
+    pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+    for (size_t i = 0; i < temp_verts3d.size(); ++i) {
+        pMesh->mVertices[i] = temp_verts3d[i];
+    }
     pMesh->mNumFaces = (unsigned int)(curOut - out); /* not necessarily equal to numOut */
+
     return true;
 }
 
+} // namespace Assimp
+
 #endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS

BIN
doc/AssimpDoc_Html/AssimpDoc.chm


+ 3 - 0
doc/CMakeLists.txt

@@ -1,5 +1,8 @@
 find_package( Doxygen REQUIRED )
 
+set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
+set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
+
 set( HTML_OUTPUT "AssimpDoc_Html" CACHE STRING "Output directory for generated HTML documentation. Defaults to AssimpDoc_Html." )
 
 # Enable Microsoft CHM help style only on Windows

+ 1 - 1
doc/Doxyfile.in

@@ -1484,7 +1484,7 @@ MAN_LINKS              = NO
 # generate an XML file that captures the structure of 
 # the code including all documentation.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. 
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be 

+ 4 - 4
include/assimp/Hash.h

@@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #   pragma GCC system_header
 #endif
 
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
 
 // ------------------------------------------------------------------------------------------------
 // Hashing function taken from
@@ -74,8 +74,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // ------------------------------------------------------------------------------------------------
 inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) {
-uint32_t tmp;
-int rem;
+    uint32_t tmp;
+    int rem;
 
     if (!data) return 0;
     if (!len)len = (uint32_t)::strlen(data);

+ 39 - 40
include/assimp/LineSplitter.h

@@ -49,12 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_LINE_SPLITTER_H
 
 #ifdef __GNUC__
-#   pragma GCC system_header
+#pragma GCC system_header
 #endif
 
-#include <stdexcept>
-#include <assimp/StreamReader.h>
 #include <assimp/ParsingUtils.h>
+#include <assimp/StreamReader.h>
+#include <stdexcept>
 
 namespace Assimp {
 
@@ -79,37 +79,37 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
 // ------------------------------------------------------------------------------------------------
 class LineSplitter {
 public:
-    typedef size_t line_idx;
+    using line_idx = size_t;
 
     // -----------------------------------------
     /** construct from existing stream reader
     note: trim is *always* assumed true if skyp_empty_lines==true
     */
-    LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
+    LineSplitter(StreamReaderLE &stream, bool skip_empty_lines = true, bool trim = true);
 
     ~LineSplitter();
 
     // -----------------------------------------
     /** pseudo-iterator increment */
-    LineSplitter& operator++();
+    LineSplitter &operator++();
 
     // -----------------------------------------
-    LineSplitter& operator++(int);
+    LineSplitter &operator++(int);
 
     // -----------------------------------------
     /** get a pointer to the beginning of a particular token */
-    const char* operator[] (size_t idx) const;
+    const char *operator[](size_t idx) const;
 
     // -----------------------------------------
     /** extract the start positions of N tokens from the current line*/
     template <size_t N>
-    void get_tokens(const char* (&tokens)[N]) const;
+    void get_tokens(const char *(&tokens)[N]) const;
 
     // -----------------------------------------
     /** member access */
-    const std::string* operator -> () const;
+    const std::string *operator->() const;
 
-    std::string operator* () const;
+    std::string operator*() const;
 
     // -----------------------------------------
     /** boolean context */
@@ -123,47 +123,45 @@ public:
 
     // -----------------------------------------
     /** access the underlying stream object */
-    StreamReaderLE& get_stream();
+    StreamReaderLE &get_stream();
 
     // -----------------------------------------
     /** !strcmp((*this)->substr(0,strlen(check)),check) */
-    bool match_start(const char* check);
+    bool match_start(const char *check);
 
     // -----------------------------------------
     /** swallow the next call to ++, return the previous value. */
     void swallow_next_increment();
 
-    LineSplitter( const LineSplitter & ) = delete;
+    LineSplitter(const LineSplitter &) = delete;
     LineSplitter(LineSplitter &&) = delete;
-    LineSplitter &operator = ( const LineSplitter & ) = delete;
+    LineSplitter &operator=(const LineSplitter &) = delete;
 
 private:
     line_idx mIdx;
     std::string mCur;
-    StreamReaderLE& mStream;
+    StreamReaderLE &mStream;
     bool mSwallow, mSkip_empty_lines, mTrim;
 };
 
-AI_FORCE_INLINE
-LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim )
-: mIdx(0)
-, mCur()
-, mStream(stream)
-, mSwallow()
-, mSkip_empty_lines(skip_empty_lines)
-, mTrim(trim) {
+AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE &stream, bool skip_empty_lines, bool trim) :
+        mIdx(0),
+        mCur(),
+        mStream(stream),
+        mSwallow(),
+        mSkip_empty_lines(skip_empty_lines),
+        mTrim(trim) {
     mCur.reserve(1024);
     operator++();
     mIdx = 0;
 }
 
-AI_FORCE_INLINE
-LineSplitter::~LineSplitter() {
+AI_FORCE_INLINE LineSplitter::~LineSplitter() {
     // empty
 }
 
 AI_FORCE_INLINE
-LineSplitter& LineSplitter::operator++() {
+LineSplitter &LineSplitter::operator++() {
     if (mSwallow) {
         mSwallow = false;
         return *this;
@@ -178,7 +176,8 @@ LineSplitter& LineSplitter::operator++() {
     while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
         if (s == '\n' || s == '\r') {
             if (mSkip_empty_lines) {
-                while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'));
+                while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'))
+                    ;
                 if (mStream.GetRemainingSize()) {
                     mStream.IncPtr(-1);
                 }
@@ -188,7 +187,8 @@ LineSplitter& LineSplitter::operator++() {
                     mStream.IncPtr(-1);
                 }
                 if (mTrim) {
-                    while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'));
+                    while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'))
+                        ;
                     if (mStream.GetRemainingSize()) {
                         mStream.IncPtr(-1);
                     }
@@ -203,14 +203,13 @@ LineSplitter& LineSplitter::operator++() {
     return *this;
 }
 
-AI_FORCE_INLINE
-LineSplitter &LineSplitter::operator++(int) {
+AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
     return ++(*this);
 }
 
 AI_FORCE_INLINE
-const char *LineSplitter::operator[] (size_t idx) const {
-    const char* s = operator->()->c_str();
+const char *LineSplitter::operator[](size_t idx) const {
+    const char *s = operator->()->c_str();
 
     SkipSpaces(&s);
     for (size_t i = 0; i < idx; ++i) {
@@ -226,9 +225,8 @@ const char *LineSplitter::operator[] (size_t idx) const {
 }
 
 template <size_t N>
-AI_FORCE_INLINE
-void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
-    const char* s = operator->()->c_str();
+AI_FORCE_INLINE void LineSplitter::get_tokens(const char *(&tokens)[N]) const {
+    const char *s = operator->()->c_str();
 
     SkipSpaces(&s);
     for (size_t i = 0; i < N; ++i) {
@@ -237,18 +235,19 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
         }
         tokens[i] = s;
 
-        for (; *s && !IsSpace(*s); ++s);
+        for (; *s && !IsSpace(*s); ++s)
+            ;
         SkipSpaces(&s);
     }
 }
 
 AI_FORCE_INLINE
-const std::string* LineSplitter::operator -> () const {
+const std::string *LineSplitter::operator->() const {
     return &mCur;
 }
 
 AI_FORCE_INLINE
-std::string LineSplitter::operator* () const {
+std::string LineSplitter::operator*() const {
     return mCur;
 }
 
@@ -273,7 +272,7 @@ StreamReaderLE &LineSplitter::get_stream() {
 }
 
 AI_FORCE_INLINE
-bool LineSplitter::match_start(const char* check) {
+bool LineSplitter::match_start(const char *check) {
     const size_t len = ::strlen(check);
 
     return len <= mCur.length() && std::equal(check, check + len, mCur.begin());

+ 2 - 2
include/assimp/vector3.h

@@ -119,7 +119,7 @@ public:
     /** @brief Normalize the vector with extra check for zero vectors */
     aiVector3t& NormalizeSafe();
 
-    /** @brief Componentwise multiplication of two vectors
+    /** @brief Component-wise multiplication of two vectors
      *
      *  Note that vec*vec yields the dot product.
      *  @param o Second factor */
@@ -129,7 +129,7 @@ public:
 };
 
 
-typedef aiVector3t<ai_real> aiVector3D;
+using aiVector3D = aiVector3t<ai_real>;
 
 #else
 

+ 1 - 2
include/assimp/vector3.inl

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -306,4 +304,5 @@ aiVector3t<TReal> operator - ( const aiVector3t<TReal>& v) {
 // ------------------------------------------------------------------------------------------------
 
 #endif // __cplusplus
+
 #endif // AI_VECTOR3D_INL_INC

+ 25 - 0
test/unit/Common/utPolyTools.cpp

@@ -65,3 +65,28 @@ TEST_F( utPolyTools, NewellNormalTest ) {
     z[0] = z[1] = z[2] = z[3] = 0;
     NewellNormal<3, 3, 3>(out, 4, x, y, z, Capa);
 }
+
+TEST_F(utPolyTools, DerivePlaneCoordinateSpaceTest) {
+    const aiVector3D vertices_ok[3] = {
+        aiVector3D(-1, -1, 0),
+        aiVector3D(0, 1, 0),
+        aiVector3D(1, -1, 0)
+        
+    };
+    aiVector3D normal;
+    bool ok = true;
+    aiMatrix4x4 m_ok = DerivePlaneCoordinateSpace<ai_real>(vertices_ok, 3, ok, normal);
+    EXPECT_TRUE(ok);
+    EXPECT_FLOAT_EQ(normal.x, 0.0f);
+    EXPECT_FLOAT_EQ(normal.y, 0.0f);
+    EXPECT_FLOAT_EQ(normal.z, 1.0f);
+
+    const aiVector3D vertices_not_ok[3] = {
+        aiVector3D(-1, -1, 0),
+        aiVector3D(-1, -1, 0),
+        aiVector3D(-1, -1, 0)
+
+    };
+    aiMatrix4x4 m_not_ok = DerivePlaneCoordinateSpace<ai_real>(vertices_not_ok, 3, ok, normal);
+    EXPECT_FALSE(ok);
+}

+ 3 - 3
test/unit/utTriangulate.cpp

@@ -49,8 +49,8 @@ using namespace Assimp;
 
 class TriangulateProcessTest : public ::testing::Test {
 public:
-    virtual void SetUp();
-    virtual void TearDown();
+    void SetUp() override;
+    void TearDown() override;
 
 protected:
     aiMesh *pcMesh;
@@ -132,6 +132,6 @@ TEST_F(TriangulateProcessTest, testTriangulation) {
         }
     }
 
-    // we should have no valid normal vectors now necause we aren't a pure polygon mesh
+    // we should have no valid normal vectors now because we aren't a pure polygon mesh
     EXPECT_TRUE(pcMesh->mNormals == NULL);
 }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно