From 34eafe127eedb009a4ae5bc0f426c97f21dc8998 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sun, 26 Oct 2025 14:26:02 +0100 Subject: [PATCH 1/4] Import: Use proper location for DXF prefs Currently if users enabled the "Treat ellipses and splines as polylines" option in DXF export preferences and exported them, they were still being exported as native primitives. This was because we were reading from a wrong preferences location, UI stored it in `BaseApp/Preferences/Mod/Draft`, but it was read from `BaseApp/Preferences/Mod/Import`. Since the pref didn't exist, we were taking default value which was set to `false`. So the solution is obvious - just set it to the correct path. --- src/Mod/Import/App/AppImportPy.cpp | 4 ++-- src/Mod/Import/App/dxf/ImpExpDxf.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Import/App/AppImportPy.cpp b/src/Mod/Import/App/AppImportPy.cpp index da6bc79b26..197d705a2b 100644 --- a/src/Mod/Import/App/AppImportPy.cpp +++ b/src/Mod/Import/App/AppImportPy.cpp @@ -439,7 +439,7 @@ private: std::string filePath; std::string layerName; const char* optionSource = nullptr; - std::string defaultOptions = "User parameter:BaseApp/Preferences/Mod/Import"; + std::string defaultOptions = "User parameter:BaseApp/Preferences/Mod/Draft"; int versionParm = -1; bool versionOverride = false; bool polyOverride = false; @@ -551,7 +551,7 @@ private: std::string filePath; std::string layerName; const char* optionSource = nullptr; - std::string defaultOptions = "User parameter:BaseApp/Preferences/Mod/Import"; + std::string defaultOptions = "User parameter:BaseApp/Preferences/Mod/Draft"; int versionParm = -1; bool versionOverride = false; bool polyOverride = false; diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 0cbb575308..88234c72b5 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -1648,7 +1648,7 @@ point3D gPntTopoint3D(gp_Pnt& p) ImpExpDxfWrite::ImpExpDxfWrite(std::string filepath) : CDxfWrite(filepath.c_str()) { - setOptionSource("User parameter:BaseApp/Preferences/Mod/Import"); + setOptionSource("User parameter:BaseApp/Preferences/Mod/Draft"); setOptions(); } From e221cfd47aa3f3c6800a933385610781ffdf5976 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sun, 26 Oct 2025 14:29:24 +0100 Subject: [PATCH 2/4] Import: Export number of vertices in polylines as int instead of double --- src/Mod/Import/App/dxf/dxf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index 4f6a1f778a..19e43ea209 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -164,7 +164,7 @@ struct SplineDataOut struct LWPolyDataOut { - double nVert; + int nVert; int Flag; double Width; double Elev; From 01777cb320b40f3e487dd2d86ee7e73a35225739 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sun, 26 Oct 2025 14:38:08 +0100 Subject: [PATCH 3/4] Fix: Closed polylines export duplicate vertices AS the title says - when exporting closed curves (ellipses for example) as polylines with the "Treat ellipses and splines as polylines" option, the generated DXF file contained duplicate vertices. For example, an ellipse polyline would have vertex1 and vertex40 which are identical in terms of coordinates. This has caused exception upon importing. Cause of that was that discretizer was blindly iterating through all discretized points without checking if the first and last points are coincident. So, this patch adds a check for that to detect and skip the last coincident point if it is in fact coincident during Export. --- src/Mod/Import/App/dxf/ImpExpDxf.cpp | 60 +++++++++++++++------------- src/Mod/Import/App/dxf/ImpExpDxf.h | 3 ++ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 88234c72b5..a6f0c69b2c 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -2028,9 +2029,10 @@ void ImpExpDxfWrite::exportLine(BRepAdaptor_Curve& c) writeLine(start, end); } -void ImpExpDxfWrite::exportLWPoly(BRepAdaptor_Curve& c) +// Helper function to discretize a curve into polyline vertices +// Returns true if discretization was successful and pd was populated +bool ImpExpDxfWrite::discretizeCurveToPolyline(BRepAdaptor_Curve& c, LWPolyDataOut& pd) const { - LWPolyDataOut pd; pd.Flag = c.IsClosed(); pd.Elev = 0.0; pd.Thick = 0.0; @@ -2041,14 +2043,35 @@ void ImpExpDxfWrite::exportLWPoly(BRepAdaptor_Curve& c) GCPnts_UniformAbscissa discretizer; discretizer.Initialize(c, optionMaxLength); - std::vector points; - if (discretizer.IsDone() && discretizer.NbPoints() > 0) { - int nbPoints = discretizer.NbPoints(); - for (int i = 1; i <= nbPoints; i++) { - gp_Pnt p = c.Value(discretizer.Parameter(i)); - pd.Verts.push_back(gPntTopoint3D(p)); + + if (!discretizer.IsDone() || discretizer.NbPoints() <= 0) { + return false; + } + + int nbPoints = discretizer.NbPoints(); + // for closed curves, don't include the last point if it duplicates the first + int endIndex = nbPoints; + if (pd.Flag && nbPoints > 1) { + gp_Pnt pFirst = c.Value(discretizer.Parameter(1)); + gp_Pnt pLast = c.Value(discretizer.Parameter(nbPoints)); + if (pFirst.Distance(pLast) < Precision::Confusion()) { + endIndex = nbPoints - 1; } - pd.nVert = discretizer.NbPoints(); + } + + for (int i = 1; i <= endIndex; i++) { + gp_Pnt p = c.Value(discretizer.Parameter(i)); + pd.Verts.push_back(gPntTopoint3D(p)); + } + pd.nVert = static_cast(pd.Verts.size()); + + return true; +} + +void ImpExpDxfWrite::exportLWPoly(BRepAdaptor_Curve& c) +{ + LWPolyDataOut pd; + if (discretizeCurveToPolyline(c, pd)) { writeLWPolyLine(pd); } } @@ -2056,24 +2079,7 @@ void ImpExpDxfWrite::exportLWPoly(BRepAdaptor_Curve& c) void ImpExpDxfWrite::exportPolyline(BRepAdaptor_Curve& c) { LWPolyDataOut pd; - pd.Flag = c.IsClosed(); - pd.Elev = 0.0; - pd.Thick = 0.0; - pd.Extr.x = 0.0; - pd.Extr.y = 0.0; - pd.Extr.z = 1.0; - pd.nVert = 0; - - GCPnts_UniformAbscissa discretizer; - discretizer.Initialize(c, optionMaxLength); - std::vector points; - if (discretizer.IsDone() && discretizer.NbPoints() > 0) { - int nbPoints = discretizer.NbPoints(); - for (int i = 1; i <= nbPoints; i++) { - gp_Pnt p = c.Value(discretizer.Parameter(i)); - pd.Verts.push_back(gPntTopoint3D(p)); - } - pd.nVert = discretizer.NbPoints(); + if (discretizeCurveToPolyline(c, pd)) { writePolyline(pd); } } diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.h b/src/Mod/Import/App/dxf/ImpExpDxf.h index 9054b253a7..c8eb1e3ee3 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.h +++ b/src/Mod/Import/App/dxf/ImpExpDxf.h @@ -542,6 +542,9 @@ protected: void exportLWPoly(BRepAdaptor_Curve& c); // LWPolyline not supported in R12? void exportPolyline(BRepAdaptor_Curve& c); + // helper function to discretize a curve into polyline vertices + bool discretizeCurveToPolyline(BRepAdaptor_Curve& c, LWPolyDataOut& pd) const; + // std::string m_optionSource; double optionMaxLength; bool optionPolyLine; From 0cfa5ba5fe9cb94d41d4e71bdd27d58ba719b234 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sun, 26 Oct 2025 14:45:28 +0100 Subject: [PATCH 4/4] Fix: Closed polylines import duplicate vertices Similar case to the previous commit related to closed polylines having coincident vertices, but this time for import. Basically if importing DXF files containing closed polylines, FC would throw an exception. This was because `BuildWireFromPolyline()` function in the DXF importer attempted to create a closing edge for closed polylines by connecting the last vertex back to first vertex. So, this patch adds a skip for the closing edge if the vertices are coincident. --- src/Mod/Import/App/dxf/ImpExpDxf.cpp | 61 +++++++++++++++------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index a6f0c69b2c..8fbf5c9c06 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -294,38 +294,43 @@ TopoDS_Wire ImpExpDxfRead::BuildWireFromPolyline(std::list& vertices if (is_closed && vertices.size() > 1) { const VertexInfo& start_vertex = vertices.back(); const VertexInfo& end_vertex = vertices.front(); - TopoDS_Edge edge; - if (start_vertex.bulge == 0.0) { - edge = BRepBuilderAPI_MakeEdge(makePoint(start_vertex.location), - makePoint(end_vertex.location)) - .Edge(); - } - else { - double cot = ((1.0 / start_vertex.bulge) - start_vertex.bulge) / 2.0; - double center_x = ((start_vertex.location.x + end_vertex.location.x) - - (end_vertex.location.y - start_vertex.location.y) * cot) - / 2.0; - double center_y = ((start_vertex.location.y + end_vertex.location.y) - + (end_vertex.location.x - start_vertex.location.x) * cot) - / 2.0; - double center_z = (start_vertex.location.z + end_vertex.location.z) / 2.0; - Base::Vector3d center(center_x, center_y, center_z); + // check if the vertices are coincident (distance < tolerance) + // if they are, the polyline is already closed and we don't need a closing edge + gp_Pnt p0 = makePoint(start_vertex.location); + gp_Pnt p1 = makePoint(end_vertex.location); + double distance = p0.Distance(p1); - gp_Pnt p0 = makePoint(start_vertex.location); - gp_Pnt p1 = makePoint(end_vertex.location); - gp_Dir up(0, 0, 1); - if (start_vertex.bulge < 0) { - up.Reverse(); + if (distance > Precision::Confusion()) { + TopoDS_Edge edge; + + if (start_vertex.bulge == 0.0) { + edge = BRepBuilderAPI_MakeEdge(p0, p1).Edge(); } - gp_Pnt pc = makePoint(center); - gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc)); - if (circle.Radius() > 1e-9) { - edge = BRepBuilderAPI_MakeEdge(circle, p0, p1).Edge(); + else { + double cot = ((1.0 / start_vertex.bulge) - start_vertex.bulge) / 2.0; + double center_x = ((start_vertex.location.x + end_vertex.location.x) + - (end_vertex.location.y - start_vertex.location.y) * cot) + / 2.0; + double center_y = ((start_vertex.location.y + end_vertex.location.y) + + (end_vertex.location.x - start_vertex.location.x) * cot) + / 2.0; + double center_z = (start_vertex.location.z + end_vertex.location.z) / 2.0; + Base::Vector3d center(center_x, center_y, center_z); + + gp_Dir up(0, 0, 1); + if (start_vertex.bulge < 0) { + up.Reverse(); + } + gp_Pnt pc = makePoint(center); + gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc)); + if (circle.Radius() > 1e-9) { + edge = BRepBuilderAPI_MakeEdge(circle, p0, p1).Edge(); + } + } + if (!edge.IsNull()) { + wireBuilder.Add(edge); } - } - if (!edge.IsNull()) { - wireBuilder.Add(edge); } }