From 37cd1a657a849b27ca5e78b231d7471163724c75 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Thu, 26 Jun 2025 12:09:26 +0200
Subject: [PATCH] Import: DXF importer, refactor preferences UI
---
src/Mod/Draft/Resources/ui/preferences-dxf.ui | 774 +++++++++---------
src/Mod/Draft/importDXF.py | 81 +-
src/Mod/Import/App/dxf/ImpExpDxf.cpp | 271 +++---
src/Mod/Import/App/dxf/ImpExpDxf.h | 11 +-
4 files changed, 614 insertions(+), 523 deletions(-)
diff --git a/src/Mod/Draft/Resources/ui/preferences-dxf.ui b/src/Mod/Draft/Resources/ui/preferences-dxf.ui
index 45b348d707..c4a78612cd 100644
--- a/src/Mod/Draft/Resources/ui/preferences-dxf.ui
+++ b/src/Mod/Draft/Resources/ui/preferences-dxf.ui
@@ -6,27 +6,28 @@
0
0
- 649
- 800
+ 600
+ 880
DXF
-
+
-
-
+
- General options
+ General
-
+
-
- This preferences dialog will be shown when importing/ exporting DXF files
+ If checked, this preferences dialog will be shown each time you import or export
+a DXF file.
- Show this dialog when importing and exporting
+ Show the importer dialog when importing a file
true
@@ -42,14 +43,10 @@
-
- Python importer is used, otherwise the newer C++ is used.
-Note: C++ importer is faster, but is not as featureful yet
+ Use the legacy Python importer. This importer is more feature-complete but slower and requires an external library.
- Use legacy Python importer
-
-
- false
+ Use legacy importer
dxfUseLegacyImporter
@@ -62,11 +59,10 @@ Note: C++ importer is faster, but is not as featureful yet
-
- Python exporter is used, otherwise the newer C++ is used.
-Note: C++ exporter is faster, but is not as featureful yet
+ Use the legacy Python exporter. This exporter is more feature-complete but slower and requires an external library.
- Use legacy Python exporter
+ Use legacy exporter
dxfUseLegacyExporter
@@ -80,227 +76,160 @@ Note: C++ exporter is faster, but is not as featureful yet
-
-
+
Automatic update (legacy importer/exporter only)
-
+
-
-
-
-
-
-
- Allow FreeCAD to download the Python converter for DXF import and export.
-You can also do this manually by installing the "dxf_library" workbench
-from the Addon Manager.
-
-
- Allow FreeCAD to automatically download and update the DXF libraries
-
-
- dxfAllowDownload
-
-
- Mod/Draft
-
-
-
-
+
+
+ If checked, FreeCAD is allowed to download and update the Python libraries
+required by the legacy importer. This can also be done manually by installing
+the 'dxf_library' addon from the Addon Manager.
+
+
+ Allow FreeCAD to automatically download and update the DXF libraries
+
+
+ dxfAllowDownload
+
+
+ Mod/Draft
+
+
-
-
+
- Import options
+ Import as
-
-
- 6
-
-
- 9
-
-
- 9
-
-
- 9
-
-
- 9
-
+
-
-
-
-
- true
-
+
+
+ false
+
+
+ Creates fully parametric Draft objects. Block definitions are imported as
+reusable objects (Part Compounds) and instances become `App::Link` objects,
+maintaining the block structure. Best for full integration with the Draft
+Workbench. (Legacy importer only)
- Some options are not yet available for the new importer
+ Editable draft objects (Highest fidelity, slowest)
+
+
+ DxfImportMode
+
+
+ Mod/Draft
+
+
+ DxfImportMode
+
+
+ 0
-
-
-
-
-
-
- Import
-
-
-
- -
-
-
- If unchecked, texts and mtexts won't be imported
-
-
- Texts and dimensions
-
-
- dxftext
-
-
- Mod/Draft
-
-
-
- -
-
-
- If unchecked, points won't be imported
-
-
- points
-
-
- dxfImportPoints
-
-
- Mod/Draft
-
-
-
- -
-
-
- If checked, paper space objects will be imported too
-
-
- Layouts
-
-
- dxflayout
-
-
- Mod/Draft
-
-
-
- -
-
-
- If you want the non-named blocks (beginning with a *) to be imported too
-
-
- *blocks
-
-
- dxfstarblocks
-
-
- Mod/Draft
-
-
-
-
+
+
+ false
+
+
+ Creates parametric Part objects (e.g., Part::Line, Part::Circle). Block
+definitions are imported as reusable objects (Part Compounds) and instances
+become `App::Link` objects, maintaining the block structure. Best for
+script-based post-processing. (Not yet implemented)
+
+
+ Editable part primitives (High fidelity, slower)
+
+
+ DxfImportMode
+
+
+ Mod/Draft
+
+
+ DxfImportMode
+
+
+ 1
+
+
-
-
-
-
-
-
- false
-
-
- Create
-
-
-
- -
-
-
- false
-
-
- Only standard Part objects will be created (fastest)
-
-
- Simple Part shapes
-
-
- true
-
-
- dxfCreatePart
-
-
- Mod/Draft
-
-
-
- -
-
-
- false
-
-
- Parametric Draft objects will be created whenever possible
-
-
- Draft objects
-
-
- dxfCreateDraft
-
-
- Mod/Draft
-
-
-
- -
-
-
- false
-
-
- Sketches will be created whenever possible
-
-
- Sketches
-
-
- dxfCreateSketch
-
-
- Mod/Draft
-
-
-
-
+
+
+ Creates a non-parametric shape for each DXF entity. Block definitions are
+imported as reusable objects (Part Compounds) and instances become `App::Link`
+objects, maintaining the block structure. Good for referencing and measuring.
+
+
+ Individual part shapes (Balanced, recommended)
+
+
+ true
+
+
+ DxfImportMode
+
+
+ Mod/Draft
+
+
+ DxfImportMode
+
+
+ 2
+
+
-
-
+
+
+ Merges all geometry per layer into a single, non-editable shape. Block
+structures are not preserved; their geometry becomes part of the layer's
+shape. Best for viewing very large files with maximum performance.
+
+
+ Fused part shapes (Lowest fidelity, fastest)
+
+
+ DxfImportMode
+
+
+ Mod/Draft
+
+
+ DxfImportMode
+
+
+ 3
+
+
+
+
+
+
+ -
+
+
+ Import settings
+
+
+
-
+
-
-
-
-
- Scale factor to apply to imported files
+ Global scaling factor:
@@ -320,13 +249,13 @@ from the Addon Manager.
-
- Scale factor to apply to DXF files on import.
-The factor is the conversion between the unit of your DXF file and millimeters.
-Example: for files in millimeters: 1, in centimeters: 10,
- in meters: 1000, in inches: 25.4, in feet: 304.8
+ Scale factor to apply to DXF files on import. The factor is the conversion
+between the unit of your DXF file and millimeters. Example: for files in
+millimeters: 1, in centimeters: 10, in meters: 1000, in inches: 25.4,
+in feet: 304.8
- 12
+ 6
999999.999998999992386
@@ -345,16 +274,126 @@ Example: for files in millimeters: 1, in centimeters: 10,
-
-
-
-
+
+
+ Import:
+
+
+
+ -
+
+
-
+
+
+ If checked, text, mtext, and dimension entities will be imported as Draft objects.
+
+
+ Texts and dimensions
+
+
+ dxftext
+
+
+ Mod/Draft
+
+
+
+ -
+
+
+ If checked, point entities will be imported.
+
+
+ Points
+
+
+ true
+
+
+ dxfImportPoints
+
+
+ Mod/Draft
+
+
+
+ -
+
+
+ If checked, entities from the paper space will also be imported. By default,
+only model space is imported.
+
+
+ Paper space objects
+
+
+ dxflayout
+
+
+ Mod/Draft
+
+
+
+ -
+
+
+ If checked, anonymous blocks (whose names begin with *) will also be imported.
+These are often used for hatches and dimensions.
+
+
+ Anonymous blocks (*-blocks)
+
+
+ dxfstarblocks
+
+
+ Mod/Draft
+
+
+
+ -
+
+
+ false
+
+
+ If checked, the boundaries of hatch objects will be imported as closed wires.
+(Legacy importer only)
+
+
+ Hatch boundaries
+
+
+ importDxfHatches
+
+
+ Mod/Draft
+
+
+
+
+
+ -
+
+
+ Appearance:
+
+
+
+ -
+
+
-
- Colors will set as specified in the DXF file whenever possible.
-Otherwise default colors will be applied.
+ If checked, colors will be set as specified in the DXF file whenever
+possible. Otherwise, default FreeCAD colors are applied.
Use colors from the DXF file
+
+ true
+
dxfGetOriginalColors
@@ -363,63 +402,14 @@ Otherwise default colors will be applied.
-
-
- -
-
-
-
-
-
- false
-
-
- FreeCAD will try to join coincident objects into wires.
-Note that this can take a while!
-
-
- Join geometry
-
-
- joingeometry
-
-
- Mod/Draft
-
-
-
-
-
- -
-
-
-
-
-
- Objects from the same layers will be joined into Part Compounds,
-turning the display faster, but making them less easily editable.
-
-
- Merge layer contents into blocks
-
-
- groupLayers
-
-
- Mod/Draft
-
-
-
-
-
- -
-
-
-
+
-
false
- Imported texts will get the standard Draft Text size,
-instead of the size they have in the DXF document
+ If checked, imported texts will get the standard Draft Text size, instead of
+the size defined in the DXF document. (Legacy importer only)
Use standard font size for texts
@@ -435,61 +425,42 @@ instead of the size they have in the DXF document
-
-
-
-
-
-
- If this is checked, DXF layers will be imported as Draft Layers
-
-
- Use layers
-
-
- true
-
-
- dxfUseDraftVisGroups
-
-
- Mod/Draft
-
-
-
-
+
+
+ Advanced processing:
+
+
-
-
-
-
-
+
+
-
+
false
- Hatches will be converted into simple wires
+ If checked, the legacy importer will attempt to join coincident geometric
+objects into wires. This can be slow for large files. (Legacy importer only)
- Import hatch boundaries as wires
+ Join geometry
- importDxfHatches
+ joingeometry
Mod/Draft
-
-
- -
-
-
-
+
-
false
- If polylines have a width defined, they will be rendered
-as closed wires with correct width
+ If checked, polylines that have a width property will be rendered as faces
+representing that width. (Legacy importer only)
Render polylines with width
@@ -502,31 +473,39 @@ as closed wires with correct width
+ -
+
+
+ false
+
+
+ If checked, the legacy importer will attempt to create Sketcher objects
+instead of Draft or Part objects. This overrides the 'Import as' setting.
+
+
+ Create sketches (legacy importer only)
+
+
+ dxfCreateSketch
+
+
+ Mod/Draft
+
+
+
-
-
+
Export options
-
+
-
-
-
-
- true
-
-
-
- Some options are not yet available for the new exporter
-
-
-
- -
-
+
-
@@ -598,7 +577,7 @@ If it is set to '0' the whole spline is treated as a straight segment.
-
-
+
-
@@ -621,7 +600,7 @@ If it is set to '0' the whole spline is treated as a straight segment.
-
-
+
-
@@ -645,7 +624,7 @@ This might fail for post DXF R12 templates.
-
-
+
-
@@ -671,21 +650,20 @@ This might fail for post DXF R12 templates.
-
-
+
Qt::Vertical
20
- 40
+ 0
-
Gui::PrefCheckBox
@@ -693,82 +671,18 @@ This might fail for post DXF R12 templates.
- Gui::PrefDoubleSpinBox
- QDoubleSpinBox
+ Gui::PrefRadioButton
+ QRadioButton
- Gui::PrefRadioButton
- QRadioButton
+ Gui::PrefDoubleSpinBox
+ QDoubleSpinBox
-
- checkBox_dxfUseLegacyImporter
- toggled(bool)
- label_Create
- setEnabled(bool)
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
- checkBox_dxfUseLegacyImporter
- toggled(bool)
- radioButton_dxfCreatePart
- setEnabled(bool)
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
- checkBox_dxfUseLegacyImporter
- toggled(bool)
- radioButton_dxfCreateDraft
- setEnabled(bool)
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
- checkBox_dxfUseLegacyImporter
- toggled(bool)
- radioButton_dxfCreateSketch
- setEnabled(bool)
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
checkBox_dxfUseLegacyImporter
toggled(bool)
@@ -785,6 +699,22 @@ This might fail for post DXF R12 templates.
+
+ checkBox_dxfUseLegacyImporter
+ toggled(bool)
+ checkBox_renderPolylineWidth
+ setEnabled(bool)
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
checkBox_dxfUseLegacyImporter
toggled(bool)
@@ -820,7 +750,55 @@ This might fail for post DXF R12 templates.
checkBox_dxfUseLegacyImporter
toggled(bool)
- checkBox_renderPolylineWidth
+ checkBox_dxfCreateSketch
+ setEnabled(bool)
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
+
+ checkBox_dxfCreateSketch
+ toggled(bool)
+ groupBox_ImportAs
+ setDisabled(bool)
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
+
+ checkBox_dxfUseLegacyExporter
+ toggled(bool)
+ checkBox_dxfmesh
+ setEnabled(bool)
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
+
+ checkBox_dxfUseLegacyExporter
+ toggled(bool)
+ checkBox_dxfproject
setEnabled(bool)
diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py
index 1325611448..39fcc9c3a9 100644
--- a/src/Mod/Draft/importDXF.py
+++ b/src/Mod/Draft/importDXF.py
@@ -4163,6 +4163,8 @@ def getViewDXF(view):
return block, insert
+# In src/Mod/Draft/importDXF.py
+
def readPreferences():
"""Read the preferences of the this module from the parameter database.
@@ -4184,6 +4186,7 @@ def readPreferences():
# reading parameters
if gui and params.get_param("dxfShowDialog"):
FreeCADGui.showPreferencesByName("Import-Export", ":/ui/preferences-dxf.ui")
+
global dxfCreatePart, dxfCreateDraft, dxfCreateSketch
global dxfDiscretizeCurves, dxfStarBlocks
global dxfMakeBlocks, dxfJoin, dxfRenderPolylineWidth
@@ -4193,28 +4196,66 @@ def readPreferences():
global dxfMakeFaceMode, dxfBrightBackground, dxfDefaultColor
global dxfUseLegacyImporter, dxfExportBlocks, dxfScaling
global dxfUseLegacyExporter
- dxfCreatePart = params.get_param("dxfCreatePart")
- dxfCreateDraft = params.get_param("dxfCreateDraft")
- dxfCreateSketch = params.get_param("dxfCreateSketch")
- dxfDiscretizeCurves = params.get_param("DiscretizeEllipses")
- dxfStarBlocks = params.get_param("dxfstarblocks")
- dxfMakeBlocks = params.get_param("groupLayers")
- dxfJoin = params.get_param("joingeometry")
- dxfRenderPolylineWidth = params.get_param("renderPolylineWidth")
- dxfImportTexts = params.get_param("dxftext")
- dxfImportLayouts = params.get_param("dxflayout")
- dxfImportPoints = params.get_param("dxfImportPoints")
- dxfImportHatches = params.get_param("importDxfHatches")
- dxfUseStandardSize = params.get_param("dxfStdSize")
- dxfGetColors = params.get_param("dxfGetOriginalColors")
- dxfUseDraftVisGroups = params.get_param("dxfUseDraftVisGroups")
- dxfMakeFaceMode = params.get_param("MakeFaceMode")
- dxfUseLegacyImporter = params.get_param("dxfUseLegacyImporter")
- dxfUseLegacyExporter = params.get_param("dxfUseLegacyExporter")
+
+ # --- Read all feature and appearance toggles ---
+ # These are independent settings and can be read directly.
+ dxfDiscretizeCurves = params.get_param("DiscretizeEllipses", False)
+ dxfStarBlocks = params.get_param("dxfstarblocks", False)
+ dxfJoin = params.get_param("joingeometry", False)
+ dxfRenderPolylineWidth = params.get_param("renderPolylineWidth", False)
+ dxfImportTexts = params.get_param("dxftext", True)
+ dxfImportLayouts = params.get_param("dxflayout", False)
+ dxfImportPoints = params.get_param("dxfImportPoints", True)
+ dxfImportHatches = params.get_param("importDxfHatches", True)
+ dxfUseStandardSize = params.get_param("dxfStdSize", False)
+ dxfGetColors = params.get_param("dxfGetOriginalColors", True)
+ dxfUseDraftVisGroups = params.get_param("dxfUseDraftVisGroups", True)
+ dxfMakeFaceMode = params.get_param("MakeFaceMode", False)
+ dxfExportBlocks = params.get_param("dxfExportBlocks", True)
+ dxfScaling = params.get_param("dxfScaling", 1.0)
+
+ # These control which importer is used. The script should only proceed if
+ # the legacy importer is selected.
+ dxfUseLegacyImporter = params.get_param("dxfUseLegacyImporter", False)
+ dxfUseLegacyExporter = params.get_param("dxfUseLegacyExporter", False)
+
+ if not dxfUseLegacyImporter:
+ # If the legacy importer is called when not selected, exit.
+ # This prevents accidental execution.
+ return
+
+ # --- New, Centralized Logic for Structural Mode ---
+
+ # Read the legacy-specific override for sketch creation.
+ dxfCreateSketch = params.get_param("dxfCreateSketch", False)
+
+ if dxfCreateSketch:
+ # Sketch mode takes highest priority, other modes are irrelevant.
+ dxfCreatePart = False
+ dxfCreateDraft = False
+ dxfMakeBlocks = False
+ else:
+ # Not in sketch mode, so determine structure from DxfImportMode.
+ # This is where the new parameter is read, with its default value defined.
+ # 0=Draft, 1=Primitives, 2=Shapes, 3=Fused
+ import_mode = params.get_param("DxfImportMode", 2) # Default to "Individual part shapes"
+
+ if import_mode == 3: # Fused part shapes
+ dxfMakeBlocks = True # 'groupLayers' is the legacy equivalent
+ dxfCreatePart = False # In legacy, dxfMakeBlocks overrides these
+ dxfCreateDraft = False
+ elif import_mode == 0: # Editable draft objects
+ dxfMakeBlocks = False
+ dxfCreatePart = False
+ dxfCreateDraft = True
+ else: # Covers modes 1 (Primitives) and 2 (Shapes). Legacy maps both to "Simple part shapes"
+ dxfMakeBlocks = False
+ dxfCreatePart = True
+ dxfCreateDraft = False
+
+ # --- Other settings that are not checkboxes ---
dxfBrightBackground = isBrightBackground()
dxfDefaultColor = getColor()
- dxfExportBlocks = params.get_param("dxfExportBlocks")
- dxfScaling = params.get_param("dxfScaling")
class DxfImportReporter:
diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp
index 7a6add1c19..3319fe27a2 100644
--- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp
+++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp
@@ -106,12 +106,23 @@ void ImpExpDxfRead::StartImport()
bool ImpExpDxfRead::ReadEntitiesSection()
{
+ // TODO: remove this once the unsupported modes have been implemented.
+ // Perform a one-time check for unsupported modes
+ if (m_importMode == ImportMode::EditableDraft) {
+ UnsupportedFeature("Import as 'Editable draft objects' is not yet implemented.");
+ // We can continue, and the switch statements below will do nothing,
+ // resulting in an empty import for geometry, which is correct behavior.
+ }
+ else if (m_importMode == ImportMode::EditablePrimitives) {
+ UnsupportedFeature("Import as 'Editable part primitives' is not yet implemented.");
+ }
+
// After parsing the BLOCKS section, compose all block definitions
// into FreeCAD objects before processing the ENTITIES section.
ComposeBlocks();
DrawingEntityCollector collector(*this);
- if (m_mergeOption < SingleShapes) {
+ if (m_importMode == ImportMode::FusedShapes) {
std::map> ShapesToCombine;
{
ShapeSavingEntityCollector savingCollector(*this, ShapesToCombine);
@@ -186,31 +197,18 @@ void ImpExpDxfRead::setOptions()
m_preserveColors = hGrp->GetBool("dxfGetOriginalColors", true);
m_stats.importSettings["Use colors from the DXF file"] = m_preserveColors ? "Yes" : "No";
- // Default for creation type is to create draft objects.
- // The radio-button structure of the options dialog should generally prevent this condition.
- m_mergeOption = DraftObjects;
- m_stats.importSettings["Merge option"] = "Create Draft objects"; // Default
- if (hGrp->GetBool("groupLayers", true)) {
- // Group all compatible objects together
- m_mergeOption = MergeShapes;
- m_stats.importSettings["Merge option"] = "Group layers into blocks";
- }
- else if (hGrp->GetBool("dxfCreatePart", true)) {
- // Create (non-draft) Shape objects when possible
- m_mergeOption = SingleShapes;
- m_stats.importSettings["Merge option"] = "Create Part shapes";
- }
- else if (hGrp->GetBool("dxfCreateDraft", true)) {
- // Create only Draft objects, making the result closest to drawn-from-scratch
- m_mergeOption = DraftObjects;
- m_stats.importSettings["Merge option"] = "Create Draft objects";
- }
+ // Read the new master import mode parameter, set the default.
+ int mode = hGrp->GetInt("DxfImportMode", static_cast(ImportMode::IndividualShapes));
+ m_importMode = static_cast(mode);
+
// TODO: joingeometry should give an intermediate between MergeShapes and SingleShapes which
// will merge shapes that happen to join end-to-end. As such it should be in the radio button
// set, except that the legacy importer can do joining either for sketches or for shapes. What
// this really means is there should be an "Import as sketch" checkbox, and only the
// MergeShapes, JoinShapes, and SingleShapes radio buttons should be allowed, i.e. Draft Objects
// would be ignored.
+ // Update: The "Join geometry" option is now a checkbox that is only enabled for the legacy
+ // importer. Whether the modern importer should support this is still up for debate.
bool joinGeometry = hGrp->GetBool("joingeometry", false);
m_stats.importSettings["Join geometry"] = joinGeometry ? "Yes" : "No";
@@ -604,13 +602,24 @@ void ImpExpDxfRead::OnReadLine(const Base::Vector3d& start,
return;
}
- gp_Pnt p0 = makePoint(start);
- gp_Pnt p1 = makePoint(end);
- if (p0.IsEqual(p1, 0.00000001)) {
- // TODO: Really?? What about the people designing integrated circuits?
- return;
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ gp_Pnt p0 = makePoint(start);
+ gp_Pnt p1 = makePoint(end);
+ // TODO: Really?? What about the people designing integrated circuits?
+ if (p0.IsEqual(p1, 1e-8)) {
+ return;
+ }
+ Collector->AddObject(BRepBuilderAPI_MakeEdge(p0, p1).Edge(), "Line");
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ // Do nothing until these modes have been implemented, the one-time warning has already
+ // been issued.
+ break;
}
- Collector->AddObject(BRepBuilderAPI_MakeEdge(p0, p1).Edge(), "Line");
}
@@ -620,7 +629,19 @@ void ImpExpDxfRead::OnReadPoint(const Base::Vector3d& start)
return;
}
- Collector->AddObject(BRepBuilderAPI_MakeVertex(makePoint(start)).Vertex(), "Point");
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ // For non-parametric modes, create a Part::Feature with a Vertex shape.
+ Collector->AddObject(BRepBuilderAPI_MakeVertex(makePoint(start)).Vertex(), "Point");
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ // Do nothing until these modes have been implemented, the one-time warning has already
+ // been issued.
+ break;
+ }
}
@@ -634,19 +655,30 @@ void ImpExpDxfRead::OnReadArc(const Base::Vector3d& start,
return;
}
- gp_Pnt p0 = makePoint(start);
- gp_Pnt p1 = makePoint(end);
- gp_Dir up(0, 0, 1);
- if (!dir) {
- up = -up;
- }
- gp_Pnt pc = makePoint(center);
- gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc));
- if (circle.Radius() > 0) {
- Collector->AddObject(BRepBuilderAPI_MakeEdge(circle, p0, p1).Edge(), "Arc");
- }
- else {
- Base::Console().warning("ImpExpDxf - ignore degenerate arc of circle\n");
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ gp_Pnt p0 = makePoint(start);
+ gp_Pnt p1 = makePoint(end);
+ gp_Dir up(0, 0, 1);
+ if (!dir) {
+ up.Reverse();
+ }
+ gp_Pnt pc = makePoint(center);
+ gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc));
+ if (circle.Radius() > 1e-9) {
+ Collector->AddObject(BRepBuilderAPI_MakeEdge(circle, p0, p1).Edge(), "Arc");
+ }
+ else {
+ Base::Console().warning("ImpExpDxf - ignore degenerate arc of circle\n");
+ }
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ // Do nothing until these modes have been implemented, the one-time warning has already
+ // been issued.
+ break;
}
}
@@ -660,18 +692,27 @@ void ImpExpDxfRead::OnReadCircle(const Base::Vector3d& start,
return;
}
- gp_Pnt p0 = makePoint(start);
- gp_Dir up(0, 0, 1);
- if (!dir) {
- up = -up;
- }
- gp_Pnt pc = makePoint(center);
- gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc));
- if (circle.Radius() > 0) {
- Collector->AddObject(BRepBuilderAPI_MakeEdge(circle).Edge(), "Circle");
- }
- else {
- Base::Console().warning("ImpExpDxf - ignore degenerate circle\n");
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ gp_Pnt p0 = makePoint(start);
+ gp_Dir up(0, 0, 1);
+ if (!dir) {
+ up.Reverse();
+ }
+ gp_Pnt pc = makePoint(center);
+ gp_Circ circle(gp_Ax2(pc, up), p0.Distance(pc));
+ if (circle.Radius() > 1e-9) {
+ Collector->AddObject(BRepBuilderAPI_MakeEdge(circle).Edge(), "Circle");
+ }
+ else {
+ Base::Console().warning("ImpExpDxf - ignore degenerate circle\n");
+ }
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ break;
}
}
@@ -776,23 +817,32 @@ void ImpExpDxfRead::OnReadSpline(struct SplineData& sd)
return;
}
- try {
- Handle(Geom_BSplineCurve) geom;
- if (sd.control_points > 0) {
- geom = getSplineFromPolesAndKnots(sd);
- }
- else if (sd.fit_points > 0) {
- geom = getInterpolationSpline(sd);
- }
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ try {
+ Handle(Geom_BSplineCurve) geom;
+ if (sd.control_points > 0) {
+ geom = getSplineFromPolesAndKnots(sd);
+ }
+ else if (sd.fit_points > 0) {
+ geom = getInterpolationSpline(sd);
+ }
- if (geom.IsNull()) {
- throw Standard_Failure();
- }
+ if (geom.IsNull()) {
+ throw Standard_Failure();
+ }
- Collector->AddObject(BRepBuilderAPI_MakeEdge(geom).Edge(), "Spline");
- }
- catch (const Standard_Failure&) {
- Base::Console().warning("ImpExpDxf - failed to create bspline\n");
+ Collector->AddObject(BRepBuilderAPI_MakeEdge(geom).Edge(), "Spline");
+ }
+ catch (const Standard_Failure&) {
+ Base::Console().warning("ImpExpDxf - failed to create bspline\n");
+ }
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ break;
}
}
@@ -810,22 +860,30 @@ void ImpExpDxfRead::OnReadEllipse(const Base::Vector3d& center,
return;
}
- gp_Dir up(0, 0, 1);
- if (!dir) {
- up = -up;
- }
- gp_Pnt pc = makePoint(center);
- gp_Elips ellipse(gp_Ax2(pc, up), major_radius, minor_radius);
- ellipse.Rotate(gp_Ax1(pc, up), rotation);
- if (ellipse.MinorRadius() > 0) {
- Collector->AddObject(BRepBuilderAPI_MakeEdge(ellipse).Edge(), "Ellipse");
- }
- else {
- Base::Console().warning("ImpExpDxf - ignore degenerate ellipse\n");
+ switch (m_importMode) {
+ case ImportMode::IndividualShapes:
+ case ImportMode::FusedShapes: {
+ gp_Dir up(0, 0, 1);
+ if (!dir) {
+ up.Reverse();
+ }
+ gp_Pnt pc = makePoint(center);
+ gp_Elips ellipse(gp_Ax2(pc, up), major_radius, minor_radius);
+ ellipse.Rotate(gp_Ax1(pc, up), rotation);
+ if (ellipse.MinorRadius() > 1e-9) {
+ Collector->AddObject(BRepBuilderAPI_MakeEdge(ellipse).Edge(), "Ellipse");
+ }
+ else {
+ Base::Console().warning("ImpExpDxf - ignore degenerate ellipse\n");
+ }
+ break;
+ }
+ case ImportMode::EditableDraft:
+ case ImportMode::EditablePrimitives:
+ break;
}
}
-
void ImpExpDxfRead::OnReadText(const Base::Vector3d& point,
const double height,
const std::string& text,
@@ -901,10 +959,10 @@ void ImpExpDxfRead::OnReadDimension(const Base::Vector3d& start,
PyObject* draftModule = getDraftModule();
if (draftModule != nullptr) {
// TODO: Capture and apply OCSOrientationTransform to OCS coordinates
- // Note, some of the locations in the DXF are OCS and some are UCS, but UCS doesn't
- // mean UCS when in a block expansion, it means 'transform'
- // So we want transform*vector for "UCS" coordinates and transform*ocdCapture*vector
- // for "OCS" coordinates
+ // Note, some of the locations in the DXF are OCS and some are UCS, but UCS
+ // doesn't mean UCS when in a block expansion, it means 'transform' So we want
+ // transform*vector for "UCS" coordinates and transform*ocdCapture*vector for
+ // "OCS" coordinates
//
// We implement the transform by mapping all the points from OCS to UCS
// TODO: Set the Normal property to transform*(0,0,1,0)
@@ -941,6 +999,11 @@ void ImpExpDxfRead::OnReadPolyline(std::list& vertices, int flags)
return;
}
+ // Polyline explosion logic is complex and calls back to other OnRead... handlers.
+ // The mode switch should happen inside the final geometry creation handlers
+ // (OnReadLine, OnReadArc), so this function doesn't need its own switch statement.
+ // It simply acts as a dispatcher.
+
std::map> ShapesToCombine;
{
// TODO: Currently ExpandPolyline calls OnReadArc etc to generate the pieces, and these
@@ -948,20 +1011,19 @@ void ImpExpDxfRead::OnReadPolyline(std::list& vertices, int flags)
// Eventually when m_mergeOption being DraftObjects is implemented OnReadArc etc might
// generate Draft objects which ShapeSavingEntityCollector does not save.
// We need either a collector that collects everything (and we have to figure out
- // how to join Draft objects) or we need to temporarily set m_mergeOption to SingleShapes
- // if it is set to DraftObjects (and safely restore it on exceptions)
- // A clean way would be to give the collector a "makeDraftObjects" property,
- // and our special collector could give this the value 'false' whereas the main
- // collector would base this on the option setting.
- // Also ShapeSavingEntityCollector classifies by entityAttributes which is not needed here
- // because they are constant throughout.
+ // how to join Draft objects) or we need to temporarily set m_mergeOption to
+ // SingleShapes if it is set to DraftObjects (and safely restore it on exceptions) A
+ // clean way would be to give the collector a "makeDraftObjects" property, and our
+ // special collector could give this the value 'false' whereas the main collector would
+ // base this on the option setting. Also ShapeSavingEntityCollector classifies by
+ // entityAttributes which is not needed here because they are constant throughout.
ShapeSavingEntityCollector savingCollector(*this, ShapesToCombine);
ExplodePolyline(vertices, flags);
}
// Join the shapes.
if (!ShapesToCombine.empty()) {
- // TODO: If we want Draft objects and all segments are straight lines we can make a draft
- // wire.
+ // TODO: If we want Draft objects and all segments are straight lines we can make a
+ // draft wire.
CombineShapes(ShapesToCombine.begin()->second, "Polyline");
}
}
@@ -1013,12 +1075,12 @@ ImpExpDxfRead::MakeLayer(const std::string& name, ColorIndex_t color, std::strin
PyObject* layer = nullptr;
draftModule = getDraftModule();
if (draftModule != nullptr) {
- // After the colours, I also want to pass the draw_style, but there is an intervening
- // line-width parameter. It is easier to just pass that parameter's default value than
- // to do the handstands to pass a named parameter.
+ // After the colours, I also want to pass the draw_style, but there is an
+ // intervening line-width parameter. It is easier to just pass that parameter's
+ // default value than to do the handstands to pass a named parameter.
// TODO: Pass the appropriate draw_style (from "Solid" "Dashed" "Dotted" "DashDot")
- // This needs an ObjectDrawStyleName analogous to ObjectColor but at the ImpExpDxfGui
- // level.
+ // This needs an ObjectDrawStyleName analogous to ObjectColor but at the
+ // ImpExpDxfGui level.
layer =
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
@@ -1043,9 +1105,9 @@ ImpExpDxfRead::MakeLayer(const std::string& name, ColorIndex_t color, std::strin
Py_False);
}
- // We make our own layer class even if we could not make a layer. MoveToLayer will ignore
- // such layers but we have to do this because it is not a polymorphic type so we can't tell
- // what we pull out of m_entityAttributes.m_Layer.
+ // We make our own layer class even if we could not make a layer. MoveToLayer will
+ // ignore such layers but we have to do this because it is not a polymorphic type so we
+ // can't tell what we pull out of m_entityAttributes.m_Layer.
return result;
}
return CDxfRead::MakeLayer(name, color, std::move(lineType));
@@ -1055,8 +1117,8 @@ void ImpExpDxfRead::MoveToLayer(App::DocumentObject* object) const
if (m_preserveLayers) {
static_cast(m_entityAttributes.m_Layer)->Contents.push_back(object);
}
- // TODO: else Hide the object if it is in a Hidden layer? That won't work because we've cleared
- // out m_entityAttributes.m_Layer
+ // TODO: else Hide the object if it is in a Hidden layer? That won't work because we've
+ // cleared out m_entityAttributes.m_Layer
}
@@ -1111,7 +1173,8 @@ void ImpExpDxfRead::DrawingEntityCollector::AddObject(App::DocumentObject* obj,
const char* /*nameBase*/)
{
// This overload is for C++ created objects like App::Link
- // The object is already in the document, so we just need to style it and move it to a layer.
+ // The object is already in the document, so we just need to style it and move it to a
+ // layer.
Reader.MoveToLayer(obj);
// Safely apply styles by checking the object's actual type
diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.h b/src/Mod/Import/App/dxf/ImpExpDxf.h
index 21b533d311..5a1855a651 100644
--- a/src/Mod/Import/App/dxf/ImpExpDxf.h
+++ b/src/Mod/Import/App/dxf/ImpExpDxf.h
@@ -40,6 +40,15 @@ class BRepAdaptor_Curve;
namespace Import
{
+
+enum class ImportMode
+{
+ EditableDraft,
+ EditablePrimitives,
+ IndividualShapes,
+ FusedShapes
+};
+
class ImportExport ImpExpDxfRead: public CDxfRead
{
public:
@@ -108,6 +117,7 @@ public:
void FinishImport() override;
private:
+ ImportMode m_importMode = ImportMode::IndividualShapes;
bool shouldSkipEntity() const
{
// This entity is in paper space, and the user setting says to ignore it.
@@ -370,7 +380,6 @@ protected:
private:
const EntityCollector* previousEntityCollector;
- const eEntityMergeType_t previousMmergeOption;
};
#endif
class BlockDefinitionCollector: public EntityCollector