From 2c28702a0ac7f15d48c52e5ba34cd21b0d97f3be Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Thu, 18 Jan 2024 10:37:13 +0100 Subject: [PATCH] DXF: support 3D meshes with POLYLINE and 3DFACE Relates to GitHub #251 --- src/io_dxf/dxf.cpp | 92 ++++++++++++++++++++++++++++- src/io_dxf/dxf.h | 56 +++++++++++++++++- src/io_dxf/io_dxf.cpp | 134 ++++++++++++++++++++++++++++++------------ 3 files changed, 240 insertions(+), 42 deletions(-) diff --git a/src/io_dxf/dxf.cpp b/src/io_dxf/dxf.cpp index 03ac930c..d65ed947 100644 --- a/src/io_dxf/dxf.cpp +++ b/src/io_dxf/dxf.cpp @@ -2791,8 +2791,13 @@ bool CDxfRead::ReadVertex(Dxf_VERTEX* vertex) y_found = y_found || n == 20; HandleCoordCode(n, &vertex->point); break; + case 40: + vertex->startingWidth = stringToDouble(m_str); + break; + case 41: + vertex->endingWidth = stringToDouble(m_str); + break; case 42: { - // bulge const int bulge = stringToInt(m_str); if (bulge == 0) vertex->bulge = Dxf_VERTEX::Bulge::StraightSegment; @@ -2801,9 +2806,63 @@ bool CDxfRead::ReadVertex(Dxf_VERTEX* vertex) } break; case 70: - // flags vertex->flags = stringToUnsigned(m_str); break; + case 71: + vertex->polyfaceMeshVertex1 = stringToInt(m_str); + break; + case 72: + vertex->polyfaceMeshVertex2 = stringToInt(m_str); + break; + case 73: + vertex->polyfaceMeshVertex3 = stringToInt(m_str); + break; + case 74: + vertex->polyfaceMeshVertex4 = stringToInt(m_str); + break; + default: + HandleCommonGroupCode(n); + break; + } + } + + return false; +} + +bool CDxfRead::Read3dFace() +{ + Dxf_3DFACE face; + while (!m_ifs.eof()) { + get_line(); + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + ResolveColorIndex(); + OnRead3dFace(face); + return true; + } + else if (isStringToErrorValue(n)) { + this->ReportError_readInteger("DXF::Read3dFace()"); + return false; + } + + get_line(); + switch (n) { + case 10: case 20: case 30: + HandleCoordCode<10, 20, 30>(n, &face.corner1); + break; + case 11: case 21: case 31: + HandleCoordCode<11, 21, 31>(n, &face.corner2); + break; + case 12: case 22: case 32: + HandleCoordCode<12, 22, 32>(n, &face.corner3); + break; + case 13: case 23: case 33: + HandleCoordCode<13, 23, 33>(n, &face.corner4); + face.hasCorner4 = true; + break; + case 70: + face.flags = stringToUnsigned(m_str); + break; default: HandleCommonGroupCode(n); break; @@ -2913,11 +2972,37 @@ bool CDxfRead::ReadPolyLine() return true; } + break; + case 39: + polyline.thickness = stringToDouble(m_str); break; case 70: - // flags polyline.flags = stringToUnsigned(m_str); break; + case 40: + polyline.defaultStartWidth = stringToDouble(m_str); + break; + case 41: + polyline.defaultEndWidth = stringToDouble(m_str); + break; + case 71: + polyline.polygonMeshMVertexCount = stringToInt(m_str); + break; + case 72: + polyline.polygonMeshNVertexCount = stringToInt(m_str); + break; + case 73: + polyline.smoothSurfaceMDensity = stringToDouble(m_str); + break; + case 74: + polyline.smoothSurfaceNDensity = stringToDouble(m_str); + break; + case 75: + polyline.type = static_cast(stringToUnsigned(m_str)); + break; + case 210: case 220: case 230: + HandleCoordCode<210, 220, 230>(n, &polyline.extrusionDirection); + break; default: HandleCommonGroupCode(n); break; @@ -3402,6 +3487,7 @@ void CDxfRead::DoRead(bool ignore_errors) mapEntityHandler.insert({ "POLYLINE", [=]{ return ReadPolyLine(); } }); mapEntityHandler.insert({ "SECTION", [=]{ return ReadSection(); } }); mapEntityHandler.insert({ "SOLID", [=]{ return ReadSolid(); } }); + mapEntityHandler.insert({ "3DFACE", [=]{ return Read3dFace(); } }); mapEntityHandler.insert({ "SPLINE", [=]{ return ReadSpline(); } }); mapEntityHandler.insert({ "STYLE", [=]{ return ReadStyle(); } }); mapEntityHandler.insert({ "TEXT", [=]{ return ReadText(); } }); diff --git a/src/io_dxf/dxf.h b/src/io_dxf/dxf.h index 303a5117..8c52ab02 100644 --- a/src/io_dxf/dxf.h +++ b/src/io_dxf/dxf.h @@ -192,13 +192,32 @@ struct Dxf_VERTEX { SplineFrameControlPoint = 16, Polyline3dVertex = 32, Polygon3dVertex = 64, - PolyfaceMesgVertex = 128 + PolyfaceMeshVertex = 128 }; using Flags = unsigned; + // Code: 10, 20, 30 DxfCoords point = {}; + // Code: 40 + double startingWidth = 0.; + // Code: 41 + double endingWidth = 0.; + // Code: 42 Bulge bulge = Bulge::StraightSegment; + // Code: 70 Flags flags = Flag::None; + // Code: 50 + double curveFitTangentDirection = 0.; + // Code: 71 + int polyfaceMeshVertex1 = 0; + // Code: 72 + int polyfaceMeshVertex2 = 0; + // Code: 73 + int polyfaceMeshVertex3 = 0; + // Code: 74 + int polyfaceMeshVertex4 = 0; + // Code: 91 + // int identifier = 0; }; struct Dxf_POLYLINE { @@ -222,16 +241,27 @@ struct Dxf_POLYLINE { BezierSurface = 8 }; - double elevation = 0.; + // Code: 39 double thickness = 0.; + // Code: 70 Flags flags = Flag::None; + // Code: 40 double defaultStartWidth = 0.; + // Code: 41 double defaultEndWidth = 0.; + // Code: 71(number of vertices in the mesh) int polygonMeshMVertexCount = 0; + // Code: 72(number of faces in the mesh) int polygonMeshNVertexCount = 0; + // Code: 73 double smoothSurfaceMDensity = 0.; + // Code: 74 double smoothSurfaceNDensity = 0.; + // Code: 75 + Type type = Type::NoSmoothSurfaceFitted; + // Code: 210, 220, 230 DxfCoords extrusionDirection = { 0., 0., 1. }; + std::vector vertices; }; @@ -256,7 +286,7 @@ struct Dxf_INSERT { DxfCoords extrusionDirection = { 0., 0., 1. }; }; -struct Dxf_SOLID { +struct Dxf_QuadBase { // Code: 10, 20, 30 DxfCoords corner1; // Code: 11, 21, 31 @@ -266,6 +296,23 @@ struct Dxf_SOLID { // Code: 13, 23, 33 DxfCoords corner4; bool hasCorner4 = false; +}; + +struct Dxf_3DFACE : public Dxf_QuadBase { + enum Flag { + None = 0, + InvisibleEdge1 = 1, + InvisibleEdge2 = 2, + InvisibleEdge3 = 4, + InvisibleEdge4 = 8 + }; + using Flags = unsigned; + + // Code: 70 + Flags flags = Flag::None; +}; + +struct Dxf_SOLID : public Dxf_QuadBase { // Code: 39 double thickness = 0.; // Code: 210, 220, 230 @@ -603,6 +650,7 @@ class CDxfRead bool ReadLwPolyLine(); bool ReadPolyLine(); bool ReadVertex(Dxf_VERTEX* vertex); + bool Read3dFace(); bool ReadSolid(); bool ReadSection(); bool ReadTable(); @@ -693,6 +741,8 @@ class CDxfRead virtual void OnReadPolyline(const Dxf_POLYLINE&) = 0; + virtual void OnRead3dFace(const Dxf_3DFACE&) = 0; + virtual void OnReadPoint(const DxfCoords& s) = 0; virtual void OnReadText(const Dxf_TEXT&) = 0; diff --git a/src/io_dxf/io_dxf.cpp b/src/io_dxf/io_dxf.cpp index 2ad9702f..b01f5e69 100644 --- a/src/io_dxf/io_dxf.cpp +++ b/src/io_dxf/io_dxf.cpp @@ -13,6 +13,7 @@ #include "../base/math_utils.h" #include "../base/mesh_utils.h" #include "../base/messenger.h" +#include "../base/occ_handle.h" #include "../base/property_builtins.h" #include "../base/property_enumeration.h" #include "../base/task_progress.h" @@ -22,27 +23,28 @@ #include "aci_table.h" #include "dxf.h" -#include -#include -#include -#include #include #include #include #include #include +#include #include -#include -#include #include #include #include #include +#include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include @@ -130,6 +132,7 @@ class DxfReader::Internal : public CDxfRead { void OnReadInsert(const Dxf_INSERT& ins) override; void OnReadDimension(const DxfCoords& s, const DxfCoords& e, const DxfCoords& point, double rotation) override; void OnReadSolid(const Dxf_SOLID& solid) override; + void OnRead3dFace(const Dxf_3DFACE& face) override; void ReportError(const std::string& msg) override; void AddGraphics() const override; @@ -139,6 +142,8 @@ class DxfReader::Internal : public CDxfRead { gp_Pnt toPnt(const DxfCoords& coords) const; void addShape(const TopoDS_Shape& shape); + + TopoDS_Face makeFace(const Dxf_QuadBase& quad) const; }; class DxfReader::Properties : public PropertyGroup { @@ -419,17 +424,45 @@ void DxfReader::Internal::OnReadLine(const DxfCoords& s, const DxfCoords& e, boo void DxfReader::Internal::OnReadPolyline(const Dxf_POLYLINE& polyline) { const auto& vertices = polyline.vertices; - const bool isPolylineClosed = polyline.flags & Dxf_POLYLINE::Flag::Closed; - const int nodeCount = CppUtils::safeStaticCast(vertices.size() + (isPolylineClosed ? 1 : 0)); - MeshUtils::Polygon3dBuilder polygonBuilder(nodeCount); - for (unsigned i = 0; i < vertices.size(); ++i) - polygonBuilder.setNode(i + 1, this->toPnt(vertices.at(i).point)); + if (polyline.flags & Dxf_POLYLINE::Flag::PolyfaceMesh) { + const int meshVertexCount = polyline.polygonMeshMVertexCount; + TColgp_Array1OfPnt nodes(1, meshVertexCount); + for (int i = 0; i < meshVertexCount; ++i) + nodes.ChangeValue(i + 1) = this->toPnt(vertices.at(i).point); + + const int meshFaceCount = polyline.polygonMeshNVertexCount; + std::vector vecTriangle; + vecTriangle.reserve(meshFaceCount); + for (int i = 0; i < meshFaceCount; ++i) { + const Dxf_VERTEX& face = vertices.at(meshVertexCount + i); + const auto meshVertex1 = std::abs(face.polyfaceMeshVertex1); + const auto meshVertex2 = std::abs(face.polyfaceMeshVertex2); + const auto meshVertex3 = std::abs(face.polyfaceMeshVertex3); + const auto meshVertex4 = std::abs(face.polyfaceMeshVertex4); + vecTriangle.emplace_back(meshVertex1, meshVertex2, meshVertex3); + if (meshVertex4 != 0 && meshVertex3 != meshVertex4) + vecTriangle.emplace_back(meshVertex1, meshVertex3, meshVertex4); + } - if (isPolylineClosed) - polygonBuilder.setNode(nodeCount, this->toPnt(vertices.at(0).point)); + Poly_Array1OfTriangle triangles(1, static_cast(vecTriangle.size())); + for (unsigned i = 0; i < vecTriangle.size(); ++i) + triangles.ChangeValue(i + 1) = vecTriangle.at(i); + + this->addShape(BRepUtils::makeFace(new Poly_Triangulation(nodes, triangles))); + } + else { + const bool isPolylineClosed = polyline.flags & Dxf_POLYLINE::Flag::Closed; + const int nodeCount = CppUtils::safeStaticCast(vertices.size() + (isPolylineClosed ? 1 : 0)); + MeshUtils::Polygon3dBuilder polygonBuilder(nodeCount); + for (unsigned i = 0; i < vertices.size(); ++i) + polygonBuilder.setNode(i + 1, this->toPnt(vertices.at(i).point)); - polygonBuilder.finalize(); - this->addShape(BRepUtils::makeEdge(polygonBuilder.get())); + if (isPolylineClosed) + polygonBuilder.setNode(nodeCount, this->toPnt(vertices.at(0).point)); + + polygonBuilder.finalize(); + this->addShape(BRepUtils::makeEdge(polygonBuilder.get())); + } } void DxfReader::Internal::OnReadPoint(const DxfCoords& s) @@ -767,32 +800,32 @@ void DxfReader::Internal::OnReadDimension(const DxfCoords& s, const DxfCoords& e void DxfReader::Internal::OnReadSolid(const Dxf_SOLID& solid) { - const gp_Pnt p1 = this->toPnt(solid.corner1); - const gp_Pnt p2 = this->toPnt(solid.corner2); - const gp_Pnt p3 = this->toPnt(solid.corner3); - const gp_Pnt p4 = this->toPnt(solid.corner4); + Dxf_QuadBase quad = solid; + if (solid.hasCorner4) { + // See https://ezdxf.readthedocs.io/en/stable/dxfentities/solid.html + std::swap(quad.corner3, quad.corner4); + } - TopoDS_Face face; try { - BRepBuilderAPI_MakeWire makeWire; - makeWire.Add(BRepBuilderAPI_MakeEdge(p1, p2)); - if (solid.hasCorner4 && !p3.IsEqual(p4, Precision::Confusion())) { - makeWire.Add(BRepBuilderAPI_MakeEdge(p2, p4)); - makeWire.Add(BRepBuilderAPI_MakeEdge(p4, p3)); - } - else { - makeWire.Add(BRepBuilderAPI_MakeEdge(p2, p3)); - } - - makeWire.Add(BRepBuilderAPI_MakeEdge(p3, p1)); - if (makeWire.IsDone()) - face = BRepBuilderAPI_MakeFace(makeWire.Wire(), true/*onlyPlane*/); - } catch (...) { + const TopoDS_Face face = makeFace(quad); + if (!face.IsNull()) + this->addShape(face); + } + catch (...) { m_messenger->emitError("OnReadSolid() failed"); } +} - if (!face.IsNull()) - this->addShape(face); +void DxfReader::Internal::OnRead3dFace(const Dxf_3DFACE& face) +{ + try { + const TopoDS_Face brepFace = makeFace(face); + if (!brepFace.IsNull()) + this->addShape(brepFace); + } + catch (...) { + m_messenger->emitError("OnReadFace() failed"); + } } void DxfReader::Internal::ReportError(const std::string& msg) @@ -834,6 +867,35 @@ void DxfReader::Internal::addShape(const TopoDS_Shape& shape) } } +TopoDS_Face DxfReader::Internal::makeFace(const Dxf_QuadBase& quad) const +{ + const gp_Pnt p1 = this->toPnt(quad.corner1); + const gp_Pnt p2 = this->toPnt(quad.corner2); + const gp_Pnt p3 = this->toPnt(quad.corner3); + const gp_Pnt p4 = this->toPnt(quad.corner4); + + const double pntTolerance = Precision::Confusion(); + if (p1.IsEqual(p2, pntTolerance) || p1.IsEqual(p3, pntTolerance) || p2.IsEqual(p3, pntTolerance)) + return {}; + + TopoDS_Face face; + BRepBuilderAPI_MakeWire makeWire; + makeWire.Add(BRepBuilderAPI_MakeEdge(p1, p2)); + makeWire.Add(BRepBuilderAPI_MakeEdge(p2, p3)); + if (quad.hasCorner4 && !p3.IsEqual(p4, pntTolerance) && !p1.IsEqual(p4, pntTolerance)) { + makeWire.Add(BRepBuilderAPI_MakeEdge(p3, p4)); + makeWire.Add(BRepBuilderAPI_MakeEdge(p4, p1)); + } + else { + makeWire.Add(BRepBuilderAPI_MakeEdge(p3, p1)); + } + + if (makeWire.IsDone()) + face = BRepBuilderAPI_MakeFace(makeWire.Wire(), true/*onlyPlane*/); + + return face; +} + // Excerpted from FreeCad/src/Mod/Import/App/ImpExpDxf Handle_Geom_BSplineCurve DxfReader::Internal::createSplineFromPolesAndKnots(const Dxf_SPLINE& spline) {