From dd4f923f6cd62d25c45dc4188bf223778047e619 Mon Sep 17 00:00:00 2001 From: gumyr Date: Sat, 21 Oct 2023 11:22:11 -0400 Subject: [PATCH] Changing import_svg to use ocpsvg --- docs/installation.rst | 3 +- pyproject.toml | 1 + src/build123d/importers.py | 86 +++++++++++++++++++++----------------- tests/svg_import_test.svg | 84 +++++++++++++++++++++++++++++++++++++ tests/test_importers.py | 32 +++++++------- 5 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 tests/svg_import_test.svg diff --git a/docs/installation.rst b/docs/installation.rst index 336c79b..53d4ba1 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -108,7 +108,8 @@ A procedure for avoiding this issue is to install in a conda environment, which conda install -c cadquery -c conda-forge cadquery=master pip install svgwrite svgpathtools anytree scipy ipython \ ocp_tessellate webcolors==1.12 numpy numpy-quaternion cachetools==5.2.0 \ - ocp_vscode requests orjson urllib3 certifi numpy-stl git+https://github.com/jdegenstein/py-lib3mf + ocp_vscode requests orjson urllib3 certifi numpy-stl git+https://github.com/jdegenstein/py-lib3mf \ + git+https://github.com/snoyer/ocpsvg pip install --no-deps git+https://github.com/gumyr/build123d `You can track the issue here `_ diff --git a/pyproject.toml b/pyproject.toml index 4961b20..448572e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ "numpy-stl >= 3.0.0, <4", "ipython >= 8.0.0, <9", "py_lib3mf @ git+https://github.com/jdegenstein/py-lib3mf", + "ocpsvg @ git+https://github.com/snoyer/ocpsvg", ] [project.urls] diff --git a/src/build123d/importers.py b/src/build123d/importers.py index 26f1d48..afa563a 100644 --- a/src/build123d/importers.py +++ b/src/build123d/importers.py @@ -30,35 +30,19 @@ license: import os from math import degrees -from typing import Union -from stl.mesh import Mesh -from svgpathtools import svg2paths -from OCP.TopoDS import TopoDS_Face, TopoDS_Shape +from pathlib import Path +from typing import TextIO, Union + +import OCP.IFSelect +from build123d.geometry import Color +from build123d.topology import Compound, Face, Shape, ShapeList, Wire from OCP.BRep import BRep_Builder from OCP.BRepTools import BRepTools -from OCP.STEPControl import STEPControl_Reader -import OCP.IFSelect from OCP.RWStl import RWStl -from OCP.BRepBuilderAPI import ( - BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeFace, - BRepBuilderAPI_MakeSolid, - BRepBuilderAPI_MakeVertex, - BRepBuilderAPI_MakeWire, - BRepBuilderAPI_Sewing, -) -from OCP.gp import gp_Pnt - -from build123d.topology import ( - Compound, - Edge, - Face, - Shape, - ShapeList, - Shell, - Solid, - downcast, -) +from OCP.STEPControl import STEPControl_Reader +from OCP.TopoDS import TopoDS_Face, TopoDS_Shape, TopoDS_Wire +from ocpsvg import ColorAndLabel, import_svg_document +from svgpathtools import svg2paths def import_brep(file_name: str) -> Shape: @@ -219,23 +203,49 @@ def import_svg_as_buildline_code(file_name: str) -> tuple[str, str]: return ("\n".join(buildline_code), builder_name) -def import_svg(file_name: str) -> ShapeList[Edge]: +def import_svg( + svg_file: Union[str, Path, TextIO], + *, + flip_y: bool = True, + ignore_visibility: bool = False, + label_by: str = "id", + is_inkscape_label: bool = False, +) -> ShapeList[Union[Wire, Face]]: """import_svg - Get a ShapeList of Edge from the paths in the provided svg file. - Args: - filepath (str): svg file + svg_file (Union[str, Path, TextIO]): svg file + flip_y (bool, optional): flip objects to compensate for svg orientation. Defaults to True. + ignore_visibility (bool, optional): Defaults to False. + label_by (str, optional): xml attribute. Defaults to "id". Raises: - ValueError: File not found + ValueError: unexpected shape type Returns: - ShapeList[Edge]: Edges in svg file + ShapeList[Union[Wire, Face]]: objects contained in svg """ - if not os.path.exists(file_name): - raise ValueError(f"{file_name} not found") - svg_code, builder_name = import_svg_as_buildline_code(file_name) - ex_locals = {} - exec(svg_code, None, ex_locals) - return ex_locals[builder_name].edges() + shapes = [] + label_by = ( + "{http://www.inkscape.org/namespaces/inkscape}" + label_by + if is_inkscape_label + else label_by + ) + for face_or_wire, color_and_label in import_svg_document( + svg_file, + flip_y=flip_y, + ignore_visibility=ignore_visibility, + metadata=ColorAndLabel.Label_by(label_by), + ): + if isinstance(face_or_wire, TopoDS_Wire): + shape = Wire(face_or_wire) + elif isinstance(face_or_wire, TopoDS_Face): + shape = Face(face_or_wire) + else: # should not happen + raise ValueError(f"unexpected shape type: {type(face_or_wire).__name__}") + + shape.color = Color(*color_and_label.color) + shape.label = color_and_label.label + shapes.append(shape) + + return ShapeList(shapes) diff --git a/tests/svg_import_test.svg b/tests/svg_import_test.svg new file mode 100644 index 0000000..8b075b4 --- /dev/null +++ b/tests/svg_import_test.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_importers.py b/tests/test_importers.py index aa46429..f9b5954 100644 --- a/tests/test_importers.py +++ b/tests/test_importers.py @@ -6,7 +6,7 @@ from build123d import ( Bezier, RadiusArc, ) -from build123d.importers import import_svg_as_buildline_code, import_brep +from build123d.importers import import_svg_as_buildline_code, import_brep, import_svg from build123d.exporters import ExportSVG @@ -57,25 +57,21 @@ class ImportSVG(unittest.TestCase): self.assertEqual(builder_name, "builder") - # def test_import_svg_as_buildline_code_ccw(self): - # # Create svg file - # with BuildLine() as test_obj: - # l3 = RadiusArc((0, 1), (0, 0), 1.5) - # show(test_obj) - # svg = ExportSVG() - # svg.add_shape(test_obj.wires()[0], "") - # svg.write("test.svg") + def test_import_svg(self): + for tag in ["id", "label"]: + # Import the svg object as a ShapeList + svg = import_svg( + "svg_import_test.svg", label_by=tag, is_inkscape_label=tag == "label" + ) - # # Read the svg as code - # buildline_code, builder_name = import_svg_as_buildline_code("test.svg") + # Exact the shape of the plate & holes + base_faces = svg.filter_by(lambda f: "base" in f.label) + hole_faces = svg.filter_by(lambda f: "hole" in f.label) + test_wires = svg.filter_by(lambda f: "wire" in f.label) - # # Execute it and convert to Edges - # ex_locals = {} - # exec(buildline_code, None, ex_locals) - # test_obj: BuildLine = ex_locals[builder_name] - # os.remove("test.svg") - - # self.assertEqual(test_obj.edges()[0].geom_type(), "ELLIPSE") + self.assertEqual(len(list(base_faces)), 1) + self.assertEqual(len(list(hole_faces)), 2) + self.assertEqual(len(list(test_wires)), 1) class ImportBREP(unittest.TestCase):