experimental svg to dxf conversion for drawings

Example - https://imgur.com/a/AkvLlbZ
This commit is contained in:
Andrej730
2024-04-01 14:03:22 +05:00
parent 74fa0a106e
commit bddf423e6c
5 changed files with 119 additions and 5 deletions
+2
View File
@@ -363,6 +363,8 @@ endif
cd dist/working && . env/bin/activate && $(PIP) install pytest --target=./site-packages
# Provides Brickschema functionality
cd dist/working && . env/bin/activate && $(PIP) install "brickschema[persistence]==0.7.6a2" --target=./site-packages
# Required for SVG to DXF conversion
cd dist/working && . env/bin/activate && $(PIP) install "ezxdf" --target=./site-packages
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
rm -rf dist/working
@@ -41,6 +41,7 @@ classes = (
operator.CleanWireframes,
operator.ContractSheet,
operator.ContractTargetView,
operator.ConvertSVGToDXF,
operator.CreateDrawing,
operator.CreateSheets,
operator.DisableAddAnnotationType,
@@ -44,7 +44,7 @@ import blenderbim.bim.module.drawing.helper as helper
import blenderbim.bim.export_ifc
from blenderbim.bim.module.drawing.decoration import CutDecorator
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
from typing import NamedTuple, List
from typing import NamedTuple, List, Union
from lxml import etree
from mathutils import Vector, Color, Matrix
from timeit import default_timer as timer
@@ -2685,3 +2685,50 @@ class AddReferenceImage(bpy.types.Operator, Operator):
)
tool.Style.reload_material_from_ifc(material)
tool.Geometry.record_object_materials(obj)
class ConvertSVGToDXF(bpy.types.Operator):
bl_idname = "bim.convert_svg_to_dxf"
bl_label = "Convert SVG to DXF"
bl_options = {"REGISTER", "UNDO"}
view: bpy.props.StringProperty()
bl_description = "Convert current drawing's .svg to .dxf.\n\nSHIFT+CLICK to convert all selected drawings"
convert_all: bpy.props.BoolProperty(name="Convert All", default=False, options={"SKIP_SAVE"})
def invoke(self, context, event):
# convert all drawings on shift+click
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
if event.type == "LEFTMOUSE" and event.shift:
self.open_all = True
return self.execute(context)
def execute(self, context):
if self.convert_all:
drawings = [
tool.Ifc.get().by_id(d.ifc_definition_id) for d in context.scene.DocProperties.drawings if d.is_selected
]
else:
drawings = [tool.Ifc.get().by_id(context.scene.DocProperties.drawings.get(self.view).ifc_definition_id)]
drawing_uris: list[Path] = []
drawings_not_found: list[str] = []
for drawing in drawings:
drawing_uri = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_document(drawing))
if drawing_uri is None or not os.path.exists(drawing_uri):
drawings_not_found.append(drawing.Name)
else:
drawing_uris.append(Path(drawing_uri))
if drawings_not_found:
msg = "Some drawings .svg files were not found, need to print them first: \n{}.".format(
"\n".join(drawings_not_found)
)
self.report({"ERROR"}, msg)
return {"CANCELLED"}
for drawing_uri in drawing_uris:
tool.Drawing.convert_svg_to_dxf(drawing_uri, drawing_uri.with_suffix(".dxf"))
self.report({"INFO"}, f"{len(drawing_uris)} drawings were converted to .dxf.")
return {"FINISHED"}
@@ -268,6 +268,11 @@ class BIM_PT_drawings(Panel):
col = row.column()
col.alignment = "RIGHT"
convert_to_dxf = row.row(align=True)
op = convert_to_dxf.operator("bim.convert_svg_to_dxf", text="", icon="IMAGE_DATA")
op.view = active_drawing.name
convert_to_dxf.enabled = active_drawing.ifc_definition_id > 0
op = row.operator("bim.select_all_drawings", icon="SELECT_SUBTRACT", text="")
open_drawing_button = row.row(align=True)
+63 -4
View File
@@ -27,7 +27,6 @@ import shutil
import logging
import shapely
import platform
from shapely.ops import unary_union
import mathutils
import subprocess
import numpy as np
@@ -43,6 +42,7 @@ import blenderbim.bim.module.drawing.sheeter as sheeter
import blenderbim.bim.module.drawing.scheduler as scheduler
import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.helper as helper
from shapely.ops import unary_union
from blenderbim.bim.module.drawing.data import FONT_SIZES, DecoratorData
from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT_POSITIONS, ANNOTATION_TYPES_DATA
from lxml import etree
@@ -50,6 +50,7 @@ from mathutils import Vector, Matrix
from fractions import Fraction
import collections
from typing import Optional, Union
from pathlib import Path
class Drawing(blenderbim.core.tool.Drawing):
@@ -385,7 +386,9 @@ class Drawing(blenderbim.core.tool.Drawing):
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
@classmethod
def get_document_uri(cls, document, description=None):
def get_document_uri(
cls, document: ifcopenshell.entity_instance, description: Optional[str] = None
) -> Union[str, None]:
if getattr(document, "Location", None):
if os.path.isabs(document.Location):
return document.Location
@@ -430,7 +433,7 @@ class Drawing(blenderbim.core.tool.Drawing):
return rel.RelatingGroup
@classmethod
def get_drawing_document(cls, drawing):
def get_drawing_document(cls, drawing: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
for rel in drawing.HasAssociations:
if rel.is_a("IfcRelAssociatesDocument"):
return rel.RelatingDocument
@@ -1103,7 +1106,6 @@ class Drawing(blenderbim.core.tool.Drawing):
reference_element: ifcopenshell.entity_instance,
context: ifcopenshell.entity_instance,
) -> ifcopenshell.entity_instance:
if reference_element.is_a("IfcGridAxis"):
return cls.generate_grid_axis_reference_annotation(drawing, reference_element, context)
@@ -1991,3 +1993,60 @@ class Drawing(blenderbim.core.tool.Drawing):
rotate180z = mathutils.Matrix.Rotation(math.radians(180.0), 4, "Z")
return mathutils.Matrix.Translation(location) @ rotation.to_matrix().to_4x4() @ rotate180z
return mathutils.Matrix.Translation(location) @ rotation.to_matrix().to_4x4()
@classmethod
def convert_svg_to_dxf(cls, svg_filepath: Path, dxf_filepath: Path) -> None:
import ezdxf
import xml.etree.ElementTree as ET
SVG = "{http://www.w3.org/2000/svg}"
IFC = "{http://www.ifcopenshell.org/ns}"
doc = ezdxf.new("R2010")
msp = doc.modelspace()
svg = ET.parse(svg_filepath).getroot()
def finalize_dxf():
doc.saveas(dxf_filepath)
drawing = svg.findall(f"{SVG}g[@{IFC}name]")
if not drawing:
finalize_dxf()
return
drawing = drawing[0]
NUMBER = r"-?\d+\.?\d+"
COORD = rf"{NUMBER},{NUMBER}"
POLYLINE_PATTERN = rf"M{COORD} (?:L{COORD} ?)+Z? ?"
MULTI_POLYLINE_PATTERN = rf"^({POLYLINE_PATTERN})+$"
for element_g in drawing.findall(f"{SVG}g"):
paths = element_g.findall(f"{SVG}path")
for path in paths:
path = path.attrib["d"]
if not re.match(MULTI_POLYLINE_PATTERN, path):
# print(f'Path "{path}" doesn\'t match expected pattern {MULTI_POLYLINE_PATTERN}')
continue
for polyline_path in re.findall(POLYLINE_PATTERN, path):
points = re.findall(rf"{NUMBER}", polyline_path)
points = [float(p) for p in points]
POINT_SIZE = 2
grouped_points = []
for i in range(0, len(points), POINT_SIZE):
point = points[i : i + POINT_SIZE]
point[1] *= -1
grouped_points.append(point)
points = grouped_points
# Z marks closed polylines
is_closed_polyline = polyline_path.rstrip().endswith("Z")
if is_closed_polyline or len(points) > 2:
msp.add_lwpolyline(points, close=is_closed_polyline)
else: # LINE
msp.add_line(*points)
finalize_dxf()