Skip to content

Commit

Permalink
improve transformations handling (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
snoyer authored Feb 2, 2025
1 parent 6b59f1b commit ded3f7e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 20 deletions.
51 changes: 34 additions & 17 deletions ocpsvg/svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@

import svgelements
from OCP.BRepAdaptor import BRepAdaptor_Curve
from OCP.BRepBuilderAPI import BRepBuilderAPI_Transform
from OCP.BRepBuilderAPI import BRepBuilderAPI_GTransform, BRepBuilderAPI_Transform
from OCP.Geom import Geom_BezierCurve
from OCP.GeomAbs import GeomAbs_CurveType
from OCP.GeomAdaptor import GeomAdaptor_Curve
from OCP.gp import gp_Ax1, gp_Dir, gp_Pnt, gp_Trsf, gp_Vec
from OCP.gp import gp_Ax1, gp_Dir, gp_GTrsf, gp_Pnt, gp_Trsf, gp_Vec
from OCP.StdFail import StdFail_NotDone
from OCP.TopoDS import TopoDS, TopoDS_Edge, TopoDS_Face, TopoDS_Shape, TopoDS_Wire

Expand Down Expand Up @@ -318,27 +318,44 @@ def check_unskewed_transform() -> Union[tuple[float, float, float], None]:
else:
return None

element.reify()

if isinstance(element, (svgelements.Circle, svgelements.Ellipse)) and (
scale_and_angle := check_unskewed_transform()
):
def transform_if_needed(wire: TopoDS_Wire):
if element.transform.is_identity():
return wire
elif check_unskewed_transform():
trsf = gp_Trsf()
trsf.SetValues(
*(element.transform.a, element.transform.c, 0.0, element.transform.e), # type: ignore
*(element.transform.b, element.transform.d, 0.0, element.transform.f), # type: ignore
*(0.0, 0.0, 1.0, 0.0), # type: ignore
)
return TopoDS.Wire_s(BRepBuilderAPI_Transform(wire, trsf).Shape())
else:
gtrsf = gp_GTrsf()
gtrsf.SetValue(1, 1, element.transform.a) # type: ignore
gtrsf.SetValue(2, 1, element.transform.b) # type: ignore
gtrsf.SetValue(1, 2, element.transform.c) # type: ignore
gtrsf.SetValue(2, 2, element.transform.d) # type: ignore
gtrsf.SetValue(1, 4, element.transform.e) # type: ignore
gtrsf.SetValue(1, 4, element.transform.f) # type: ignore
return TopoDS.Wire_s(BRepBuilderAPI_GTransform(wire, gtrsf).Shape())

if isinstance(element, (svgelements.Circle, svgelements.Ellipse)):
cx = float(element.cx) # type: ignore
cy = float(element.cy) # type: ignore
origin = svgelements.Point(cx, cy) * element.transform
center = gp_Pnt(origin.real, origin.imag, 0) # type: ignore
scale_x, scale_y, angle = scale_and_angle
r1 = float(element.rx) * scale_x # type: ignore
r2 = float(element.ry) * scale_y # type: ignore
rx = float(element.rx) # type: ignore
ry = float(element.ry) # type: ignore
center = gp_Pnt(cx, cy, 0)
circle_or_ellpise = (
circle_curve(r1, center=center)
if r1 == r2
else ellipse_curve(r1, r2, center=center, rotation=degrees(angle))
circle_curve(rx, center=center)
if rx == ry
else ellipse_curve(rx, ry, center=center)
)
yield transform_if_needed(
wire_from_continuous_edges((edge_from_curve(circle_or_ellpise),))
)
yield wire_from_continuous_edges((edge_from_curve(circle_or_ellpise),))

elif path := svg_element_to_path(element):
yield from wires_from_svg_path(path)
yield from map(transform_if_needed, wires_from_svg_path(path))


def svg_element_to_path(element: ShapeElement):
Expand Down
34 changes: 31 additions & 3 deletions tests/test_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ def test_circles_and_ellipses(
</svg>
"""
buf = StringIO(svg_src)
imported = list(import_svg_document(buf))
imported = list(import_svg_document(buf, flip_y=False))
assert len(imported) == 1
assert isinstance(imported[0], TopoDS_Wire)

Expand All @@ -991,8 +991,36 @@ def test_circles_and_ellipses(
curve = curves[0]
assert type(curve) is curve_type
assert isinstance(curve, (Geom_Circle, Geom_Ellipse))
loc = curve.Axis().Location()
assert (loc.X(), loc.Y()) == approx(center)
x0, y0, _, x1, y1, _ = bounding_box(imported[0]).Get()
assert ((x0 + x1) / 2, (y0 + y1) / 2) == approx(center)


@pytest.mark.parametrize(
"element, svg_d",
[
(
'circle r="40" cx="1" cy="5" transform="skewX(25)"',
"M -15.320313,-35 A 50.39898,31.746674 38.437866 0 0 -36.667969,5 "
"50.39898,31.746674 38.437866 0 0 21.984375,45 50.39898,31.746674 "
"38.437866 0 0 43.332031,5 50.39898,31.746674 38.437866 0 0 -15.320313,-35 Z",
),
],
)
def test_skewed_circles_and_ellipses(element: str, svg_d: str):
svg_src = f"""
<svg xmlns="http://www.w3.org/2000/svg">
<{element} fill="none"/>
<path d="{svg_d}" fill="none"/>
</svg>
"""
buf = StringIO(svg_src)
imported = list(import_svg_document(buf, flip_y=False))
assert len(imported) == 2
assert isinstance(imported[0], TopoDS_Wire)
assert isinstance(imported[1], TopoDS_Wire)
bounds0 = bounding_box(imported[0]).Get()
bounds1 = bounding_box(imported[1]).Get()
assert bounds0 == approx(bounds1, abs=1e-3)


@dataclass
Expand Down

0 comments on commit ded3f7e

Please sign in to comment.