diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index a837cd56ac..44a32e9c5d 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -82,6 +82,7 @@ if gui: try: from draftviewproviders.view_base import ViewProviderDraft from draftviewproviders.view_wire import ViewProviderWire + from draftviewproviders.view_dimension import ViewProviderLinearDimension except ImportError: ViewProviderDraft = None ViewProviderWire = None @@ -2879,8 +2880,8 @@ def _import_dxf_file(filename, doc_name=None): newly_created_objects = objects_after - objects_before # --- Post-processing step --- - if is_draft_mode and newly_created_objects: - draft_postprocessor = DxfDraftPostProcessor(doc, newly_created_objects) + if not use_legacy and newly_created_objects: + draft_postprocessor = DxfDraftPostProcessor(doc, newly_created_objects, import_mode) draft_postprocessor.run() Draft.convert_draft_texts() # This is a general utility that should run for both importers @@ -4445,9 +4446,10 @@ class DxfDraftPostProcessor: converting them into fully parametric Draft objects while preserving the block and layer hierarchy. """ - def __init__(self, doc, new_objects): + def __init__(self, doc, new_objects, import_mode): self.doc = doc self.all_imported_objects = new_objects + self.import_mode = import_mode self.all_originals_to_delete = set() self.newly_created_draft_objects = [] @@ -4463,7 +4465,7 @@ class DxfDraftPostProcessor: if block_def_obj.isValid() and block_def_obj.isDerivedFrom("Part::Compound"): block_definitions[block_def_obj] = [ child for child in block_def_obj.Links - if child.isValid() and child.isDerivedFrom("Part::Feature") + if child.isValid() ] all_block_internal_objects_set = set() @@ -4479,7 +4481,7 @@ class DxfDraftPostProcessor: if obj.isDerivedFrom("App::FeaturePython") and hasattr(obj, "DxfEntityType"): placeholders.append(obj) - elif obj.isDerivedFrom("Part::Feature"): + elif obj.isDerivedFrom("Part::Feature") or obj.isDerivedFrom("App::Link"): top_level_geometry.append(obj) return block_definitions, top_level_geometry, placeholders @@ -4490,6 +4492,10 @@ class DxfDraftPostProcessor: ensuring correct underlying C++ object typing and property management. Returns a tuple: (new_draft_object, type_string) or (None, None). """ + if self.import_mode != 0: + # In non-Draft modes, do not convert geometry. Return it as is. + return part_obj, "KeptAsIs" + # Skip invalid objects or objects that are block definitions themselves (their # links/children will be converted) if not part_obj.isValid() or \ @@ -4662,7 +4668,8 @@ class DxfDraftPostProcessor: FCC.PrintWarning(f"Created object '{new_obj.Label}' of type '{obj_type_str}' does not have a 'Placement' property even after intended setup. This is unexpected.\n") # Add the original object (from C++ importer) to the list for deletion. - self.all_originals_to_delete.add(part_obj) + if new_obj is not part_obj: + self.all_originals_to_delete.add(part_obj) return new_obj, obj_type_str @@ -4716,14 +4723,51 @@ class DxfDraftPostProcessor: new_obj = None try: if placeholder.DxfEntityType == "DIMENSION": + # 1. Create the base object and attach the proxy, which adds the needed properties. dim = self.doc.addObject("App::FeaturePython", "Dimension") _Dimension(dim) - dim.addExtension("Part::AttachExtensionPython") - dim.Start = placeholder.Start - dim.End = placeholder.End - dim.Dimline = placeholder.Dimline - dim.Placement = placeholder.Placement + + if FreeCAD.GuiUp: + ViewProviderLinearDimension(dim.ViewObject) + + # 2. Get the transformation from the placeholder's Placement property. + plc = placeholder.Placement + + # 3. Transform the defining points from the placeholder's local coordinate system + # into the world coordinate system. + p_start = plc.multVec(placeholder.Start) + p_end = plc.multVec(placeholder.End) + p_dimline = plc.multVec(placeholder.Dimline) + + # 4. Assign these new, transformed points to the final dimension object. + dim.Start = p_start + dim.End = p_end + dim.Dimline = p_dimline + + # Do NOT try to set dim.Placement, as it does not exist. + new_obj = dim + + # Check for and apply the dimension type (horizontal, vertical, etc.) + # This information is now plumbed through from the C++ importer. + if hasattr(placeholder, "DxfDimensionType"): + # The lower bits of the type flag define the dimension's nature. + # 0 = Rotated, Horizontal, or Vertical + # 1 = Aligned + # Other values are for angular, diameter, etc., not handled here. + dim_type = placeholder.DxfDimensionType & 0x0F + + # A type of 0 indicates that the dimension is projected. The + # projection direction is given by its rotation angle. + if dim_type == 0 and hasattr(placeholder, "DxfRotation"): + angle = placeholder.DxfRotation.Value # Angle is in radians + + # The Direction property on a Draft.Dimension controls its + # projection. Setting it here ensures the ViewProvider + # will draw it correctly as horizontal, vertical, or rotated. + direction_vector = FreeCAD.Vector(math.cos(angle), math.sin(angle), 0) + dim.Direction = direction_vector + elif placeholder.DxfEntityType == "TEXT": text_obj = Draft.make_text(placeholder.Text) text_obj.Placement = placeholder.Placement @@ -4850,12 +4894,15 @@ class DxfDraftPostProcessor: for block_def_obj, original_children in block_defs.items(): new_draft_children = [self._create_and_parent_geometry(child) for child in original_children] block_def_obj.Links = [obj for obj in new_draft_children if obj] - self.all_originals_to_delete.update(original_children) + self.all_originals_to_delete.update(set(original_children) - set(new_draft_children)) # Process top-level geometry + converted_top_geo = [] for part_obj in top_geo: - self._create_and_parent_geometry(part_obj) - self.all_originals_to_delete.update(top_geo) + new_obj = self._create_and_parent_geometry(part_obj) + if new_obj: + converted_top_geo.append(new_obj) + self.all_originals_to_delete.update(set(top_geo) - set(converted_top_geo)) # Process placeholders like Text and Dimensions self._create_from_placeholders(placeholders) diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 41371374d4..1c1316e142 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -1279,7 +1279,8 @@ void ImpExpDxfRead::OnReadInsert(const Base::Vector3d& point, void ImpExpDxfRead::OnReadDimension(const Base::Vector3d& start, const Base::Vector3d& end, const Base::Vector3d& point, - double /*rotation*/) + int dimensionType, + double rotation) { if (shouldSkipEntity() || !m_importAnnotations) { return; @@ -1304,6 +1305,20 @@ void ImpExpDxfRead::OnReadDimension(const Base::Vector3d& start, p->addDynamicProperty("App::PropertyVector", "Dimline", "Data", "Point on dimension line"); static_cast(p->getPropertyByName("Dimline"))->setValue(point); + p->addDynamicProperty("App::PropertyInteger", + "DxfDimensionType", + "Internal", + "Original dimension type flag"); + static_cast(p->getPropertyByName("DxfDimensionType")) + ->setValue(dimensionType); + + p->addDynamicProperty("App::PropertyAngle", + "DxfRotation", + "Internal", + "Original dimension rotation"); + // rotation is already in radians from the caller + static_cast(p->getPropertyByName("DxfRotation"))->setValue(rotation); + p->addDynamicProperty("App::PropertyPlacement", "Placement", "Base", "Object placement"); Base::Placement pl; // Correctly construct the rotation directly from the 4x4 matrix. diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.h b/src/Mod/Import/App/dxf/ImpExpDxf.h index 15685f1d58..0e7edde6e6 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.h +++ b/src/Mod/Import/App/dxf/ImpExpDxf.h @@ -100,6 +100,7 @@ public: void OnReadDimension(const Base::Vector3d& start, const Base::Vector3d& end, const Base::Vector3d& point, + int dimensionType, double rotation) override; void OnReadPolyline(std::list& /*vertices*/, int flags) override; diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index 28be78f0b4..5511ab8feb 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -2331,7 +2331,7 @@ bool CDxfRead::ReadDimension() switch ((eDimensionType_t)dimensionType) { case eLinear: case eAligned: - OnReadDimension(start, end, linePosition, Base::toRadians(rotation)); + OnReadDimension(start, end, linePosition, dimensionType, Base::toRadians(rotation)); break; default: UnsupportedFeature("Dimension type '%d'", dimensionType); diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index dec4163db0..4f6a1f778a 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -938,6 +938,7 @@ public: virtual void OnReadDimension(const Base::Vector3d& /*start*/, const Base::Vector3d& /*end*/, const Base::Vector3d& /*point*/, + int /*dimensionType*/, double /*rotation*/) {} virtual void OnReadPolyline(std::list& /*vertices*/, int /*flags*/)