Skip to content

Commit

Permalink
Merge pull request #787 from erooke/path_support
Browse files Browse the repository at this point in the history
Path support
  • Loading branch information
gumyr authored Nov 15, 2024
2 parents 4c43b22 + 80fc373 commit e1da165
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 60 deletions.
20 changes: 10 additions & 10 deletions src/build123d/exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@

import math
import xml.etree.ElementTree as ET
from enum import Enum, auto
from typing import Callable, Iterable, Optional, Union, List, Tuple
from copy import copy
from enum import Enum, auto
from os import PathLike, fsdecode, fspath
from pathlib import Path
from typing import Callable, Iterable, List, Optional, Tuple, Union

import ezdxf
import svgpathtools as PT
Expand Down Expand Up @@ -633,22 +635,21 @@ def _add_single_shape(self, shape: Shape, layer: str = ""):

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def write(self, file_name: str):
def write(self, file_name: Union[PathLike, str, bytes]):
"""write
Writes the DXF data to the specified file name.
Args:
file_name (str): The file name (including path) where the DXF data will
file_name (Union[PathLike, str, bytes]): The file name (including path) where the DXF data will
be written.
"""
# Reset the main CAD viewport of the model space to the
# extents of its entities.
# https://github.com/gumyr/build123d/issues/382 tracks
# exposing viewport control to the user.
zoom.extents(self._modelspace)

self._document.saveas(file_name)
self._document.saveas(fsdecode(file_name))

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Expand Down Expand Up @@ -877,7 +878,7 @@ def __init__(
line_type: LineType,
):
def convert_color(
c: Union[ColorIndex, RGB, Color, None]
c: Union[ColorIndex, RGB, Color, None],
) -> Union[Color, None]:
if isinstance(c, ColorIndex):
# The easydxf color indices BLACK and WHITE have the same
Expand Down Expand Up @@ -1390,7 +1391,6 @@ def _stroke_dasharray(self, layer: _Layer):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def _group_for_layer(self, layer: _Layer, attribs: dict = None) -> ET.Element:

def _color_attribs(c: Color) -> Tuple[str, str]:
if c:
(r, g, b, a) = tuple(c)
Expand Down Expand Up @@ -1428,13 +1428,13 @@ def _color_attribs(c: Color) -> Tuple[str, str]:

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def write(self, path: str):
def write(self, path: Union[PathLike, str, bytes]):
"""write
Writes the SVG data to the specified file path.
Args:
path (str): The file path where the SVG data will be written.
path (Union[PathLike, str, bytes]): The file path where the SVG data will be written.
"""
# pylint: disable=too-many-locals
bb = self._bounds
Expand Down
33 changes: 19 additions & 14 deletions src/build123d/exporters3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
# pylint has trouble with the OCP imports
# pylint: disable=no-name-in-module, import-error

import warnings
from io import BytesIO
import warnings
from os import PathLike, fsdecode, fspath
from typing import Union

import OCP.TopAbs as ta
Expand Down Expand Up @@ -153,16 +154,21 @@ def _create_xde(to_export: Shape, unit: Unit = Unit.MM) -> TDocStd_Document:
return doc


def export_brep(to_export: Shape, file_path: Union[str, BytesIO]) -> bool:
def export_brep(
to_export: Shape,
file_path: Union[PathLike, str, bytes, BytesIO],
) -> bool:
"""Export this shape to a BREP file
Args:
to_export (Shape): object or assembly
file_path: Union[str, BytesIO]: brep file path or memory buffer
file_path: Union[PathLike, str, bytes, BytesIO]: brep file path or memory buffer
Returns:
bool: write status
"""
if not isinstance(file_path, BytesIO):
file_path = fsdecode(file_path)

return_value = BRepTools.Write_s(to_export.wrapped, file_path)

Expand All @@ -171,7 +177,7 @@ def export_brep(to_export: Shape, file_path: Union[str, BytesIO]) -> bool:

def export_gltf(
to_export: Shape,
file_path: str,
file_path: Union[PathLike, str, bytes],
unit: Unit = Unit.MM,
binary: bool = False,
linear_deflection: float = 0.001,
Expand All @@ -187,7 +193,7 @@ def export_gltf(
Args:
to_export (Shape): object or assembly
file_path (str): glTF file path
file_path (Union[PathLike, str, bytes]): glTF file path
unit (Unit, optional): shape units. Defaults to Unit.MM.
binary (bool, optional): output format. Defaults to False.
linear_deflection (float, optional): A linear deflection setting which limits
Expand Down Expand Up @@ -223,7 +229,7 @@ def export_gltf(

# Write the glTF file
writer = RWGltf_CafWriter(
theFile=TCollection_AsciiString(file_path), theIsBinary=binary
theFile=TCollection_AsciiString(fsdecode(file_path)), theIsBinary=binary
)
writer.SetParallel(True)
index_map = TColStd_IndexedDataMapOfStringString()
Expand All @@ -249,7 +255,7 @@ def export_gltf(

def export_step(
to_export: Shape,
file_path: str,
file_path: Union[PathLike, str, bytes],
unit: Unit = Unit.MM,
write_pcurves: bool = True,
precision_mode: PrecisionMode = PrecisionMode.AVERAGE,
Expand All @@ -262,7 +268,7 @@ def export_step(
Args:
to_export (Shape): object or assembly
file_path (str): step file path
file_path (Union[PathLike, str, bytes]): step file path
unit (Unit, optional): shape units. Defaults to Unit.MM.
write_pcurves (bool, optional): write parametric curves to the STEP file.
Defaults to True.
Expand Down Expand Up @@ -308,7 +314,7 @@ def export_step(
Interface_Static.SetIVal_s("write.precision.mode", precision_mode.value)
writer.Transfer(doc, STEPControl_StepModelType.STEPControl_AsIs)

status = writer.Write(file_path) == IFSelect_ReturnStatus.IFSelect_RetDone
status = writer.Write(fspath(file_path)) == IFSelect_ReturnStatus.IFSelect_RetDone
if not status:
raise RuntimeError("Failed to write STEP file")

Expand All @@ -317,7 +323,7 @@ def export_step(

def export_stl(
to_export: Shape,
file_path: str,
file_path: Union[PathLike, str, bytes],
tolerance: float = 1e-3,
angular_tolerance: float = 0.1,
ascii_format: bool = False,
Expand Down Expand Up @@ -349,9 +355,8 @@ def export_stl(

writer = StlAPI_Writer()

if ascii_format:
writer.ASCIIMode = True
else:
writer.ASCIIMode = False
writer.ASCIIMode = ascii_format

file_path = str(file_path)

return writer.Write(to_export.wrapped, file_path)
30 changes: 17 additions & 13 deletions src/build123d/importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
# pylint: disable=no-name-in-module, import-error

import os
from os import PathLike, fsdecode
import unicodedata
from math import degrees
from pathlib import Path
from typing import TextIO, Union, Optional
from typing import Optional, TextIO, Union

from OCP.BRep import BRep_Builder
from OCP.BRepGProp import BRepGProp
Expand Down Expand Up @@ -65,7 +67,6 @@
)
from ocpsvg import ColorAndLabel, import_svg_document
from svgpathtools import svg2paths
import unicodedata

from build123d.geometry import Color, Location
from build123d.topology import (
Expand All @@ -92,11 +93,11 @@
}


def import_brep(file_name: str) -> Shape:
def import_brep(file_name: Union[PathLike, str, bytes]) -> Shape:
"""Import shape from a BREP file
Args:
file_name (str): brep file
file_name (Union[PathLike, str, bytes]): brep file
Raises:
ValueError: file not found
Expand All @@ -107,21 +108,21 @@ def import_brep(file_name: str) -> Shape:
shape = TopoDS_Shape()
builder = BRep_Builder()

BRepTools.Read_s(shape, file_name, builder)
BRepTools.Read_s(shape, fsdecode(file_name), builder)

if shape.IsNull():
raise ValueError(f"Could not import {file_name}")

return Shape.cast(shape)


def import_step(filename: str) -> Compound:
def import_step(filename: Union[PathLike, str, bytes]) -> Compound:
"""import_step
Extract shapes from a STEP file and return them as a Compound object.
Args:
file_name (str): file path of STEP file to import
file_name (Union[PathLike, str, bytes]): file path of STEP file to import
Raises:
ValueError: can't open file
Expand Down Expand Up @@ -214,7 +215,7 @@ def build_assembly(parent_tdf_label: Optional[TDF_Label] = None) -> list[Shape]:
reader.SetNameMode(True)
reader.SetColorMode(True)
reader.SetLayerMode(True)
reader.ReadFile(filename)
reader.ReadFile(fsdecode(filename))
reader.Transfer(doc)

root = Compound()
Expand All @@ -227,7 +228,7 @@ def build_assembly(parent_tdf_label: Optional[TDF_Label] = None) -> list[Shape]:
return root


def import_stl(file_name: str) -> Face:
def import_stl(file_name: Union[PathLike, str, bytes]) -> Face:
"""import_stl
Extract shape from an STL file and return it as a Face reference object.
Expand All @@ -237,7 +238,7 @@ def import_stl(file_name: str) -> Face:
of the STL file.
Args:
file_name (str): file path of STL file to import
file_name (Union[PathLike, str, bytes]): file path of STL file to import
Raises:
ValueError: Could not import file
Expand All @@ -246,20 +247,22 @@ def import_stl(file_name: str) -> Face:
Face: STL model
"""
# Read and return the shape
reader = RWStl.ReadFile_s(file_name)
reader = RWStl.ReadFile_s(fsdecode(file_name))
face = TopoDS_Face()
BRep_Builder().MakeFace(face, reader)
stl_obj = Face.cast(face)
return stl_obj


def import_svg_as_buildline_code(file_name: str) -> tuple[str, str]:
def import_svg_as_buildline_code(
file_name: Union[PathLike, str, bytes],
) -> tuple[str, str]:
"""translate_to_buildline_code
Translate the contents of the given svg file into executable build123d/BuildLine code.
Args:
file_name (str): svg file name
file_name (Union[PathLike, str, bytes]): svg file name
Returns:
tuple[str, str]: code, builder instance name
Expand All @@ -280,6 +283,7 @@ def import_svg_as_buildline_code(file_name: str) -> tuple[str, str]:
"sweep",
],
}
file_name = fsdecode(file_name)
paths_info = svg2paths(file_name)
paths, _path_attributes = paths_info[0], paths_info[1]
builder_name = os.path.basename(file_name).split(".")[0]
Expand Down
35 changes: 19 additions & 16 deletions src/build123d/mesher.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@
import sys
import uuid
import warnings
from os import PathLike, fsdecode
from typing import Iterable, Union

import OCP.TopAbs as ta
from OCP.BRep import BRep_Tool
from OCP.BRepBuilderAPI import (
BRepBuilderAPI_MakeFace,
Expand All @@ -100,16 +102,15 @@
from OCP.BRepGProp import BRepGProp
from OCP.BRepMesh import BRepMesh_IncrementalMesh
from OCP.gp import gp_Pnt
import OCP.TopAbs as ta
from OCP.GProp import GProp_GProps
from OCP.TopAbs import TopAbs_ShapeEnum
from OCP.TopExp import TopExp_Explorer
from OCP.TopoDS import TopoDS_Compound
from OCP.TopLoc import TopLoc_Location

from OCP.TopoDS import TopoDS_Compound
from py_lib3mf import Lib3MF

from build123d.build_enums import MeshType, Unit
from build123d.geometry import Color, TOLERANCE
from build123d.geometry import TOLERANCE, Color
from build123d.topology import Compound, Shape, Shell, Solid, downcast


Expand Down Expand Up @@ -482,22 +483,23 @@ def _get_shape(self, mesh_3mf: Lib3MF.MeshObject) -> Shape:

return shape_obj

def read(self, file_name: str) -> list[Shape]:
def read(self, file_name: Union[PathLike, str, bytes]) -> list[Shape]:
"""read
Args:
file_name (str): file path
file_name Union[PathLike, str, bytes]: file path
Raises:
ValueError: Unknown file format - must be 3mf or stl
Returns:
list[Shape]: build123d shapes extracted from mesh file
"""
input_file_format = file_name.split(".")[-1].lower()
if input_file_format not in ["3mf", "stl"]:
raise ValueError(f"Unknown file format {input_file_format}")
reader = self.model.QueryReader(input_file_format)
file_name = fsdecode(file_name)
_, input_file_extension = os.path.splitext(file_name)
if input_file_extension not in [".3mf", ".stl"]:
raise ValueError(f"Unknown file format {input_file_extension}")
reader = self.model.QueryReader(input_file_extension[1:])
reader.ReadFromFile(file_name)
self.unit = Mesher._map_3mf_to_b3d_unit[self.model.GetUnit()]

Expand Down Expand Up @@ -525,17 +527,18 @@ def read(self, file_name: str) -> list[Shape]:

return shapes

def write(self, file_name: str):
def write(self, file_name: Union[PathLike, str, bytes]):
"""write
Args:
file_name (str): file path
file_name Union[Pathlike, str, bytes]: file path
Raises:
ValueError: Unknown file format - must be 3mf or stl
"""
output_file_format = file_name.split(".")[-1].lower()
if output_file_format not in ["3mf", "stl"]:
raise ValueError(f"Unknown file format {output_file_format}")
writer = self.model.QueryWriter(output_file_format)
file_name = fsdecode(file_name)
_, output_file_extension = os.path.splitext(file_name)
if output_file_extension not in [".3mf", ".stl"]:
raise ValueError(f"Unknown file format {output_file_extension}")
writer = self.model.QueryWriter(output_file_extension[1:])
writer.WriteToFile(file_name)
Loading

0 comments on commit e1da165

Please sign in to comment.