Skip to content

Commit 0a2a8e3

Browse files
authored
add format_svg_path, deprecate format_svg (#33)
1 parent 2cda329 commit 0a2a8e3

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

ocpsvg/hlr.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from OCP.TopoDS import TopoDS, TopoDS_Edge, TopoDS_Shape
2525

2626
from .ocp import bounding_box, topoDS_iterator
27-
from .svg import edge_to_svg_path, format_svg
27+
from .svg import edge_to_svg_path, float_formatter, format_svg_path
2828

2929
HlrEdgeTypeName = Literal["undefined", "isoline", "sewn", "smooth", "sharp", "outline"]
3030

@@ -170,7 +170,7 @@ def to_svg(
170170
css_style: Optional[CssStyle] = None,
171171
background: Optional[bool] = None,
172172
tolerance: float = 1e-6,
173-
float_format: str = "g",
173+
decimals: Optional[int] = None,
174174
) -> ET.ElementTree:
175175
(x0, y0, x1, y1), scale, (W, H) = _viewbox_and_scale(
176176
self.bounds(),
@@ -180,8 +180,7 @@ def to_svg(
180180
)
181181
ty = -(y1 + y0) / scale
182182

183-
def fmt(v: float):
184-
return v.__format__(float_format)
183+
fmt = float_formatter(decimals)
185184

186185
svg = ET.Element("svg", xmlns="http://www.w3.org/2000/svg")
187186
svg.attrib["viewBox"] = f"{fmt(x0)} {fmt(y0)} {fmt(x1-x0)} {fmt(y1-y0)}"
@@ -224,9 +223,9 @@ def fmt(v: float):
224223

225224
attrs = {
226225
"id": f"e{i}",
227-
"d": format_svg(
226+
"d": format_svg_path(
228227
edge_to_svg_path(edge.projected_edge, tolerance=tolerance),
229-
float_format=float_format,
228+
decimals=decimals,
230229
),
231230
"class": " ".join(classnames),
232231
}

ocpsvg/svg.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import pathlib
3+
import warnings
34
from itertools import chain
45
from math import degrees, pi
56
from typing import (
@@ -58,7 +59,7 @@
5859
"edge_to_svg_path",
5960
"curve_to_svg_path",
6061
"SvgPathCommand",
61-
"format_svg",
62+
"format_svg_path",
6263
]
6364

6465

@@ -78,7 +79,29 @@
7879
]
7980

8081

82+
class float_formatter:
83+
"""float formatter with given precision and no trailing zeroes."""
84+
85+
def __init__(self, decimals: Optional[int] = None):
86+
self.format_spec = f".{decimals or 12}f"
87+
88+
def __call__(self, v: float):
89+
return v.__format__(self.format_spec).rstrip("0").rstrip(".")
90+
91+
92+
def format_svg_path(
93+
path: Iterable[SvgPathCommand], *, decimals: Optional[int] = None
94+
) -> str:
95+
ff = float_formatter(decimals)
96+
return " ".join(f"{cmd[0]}{','.join(map(ff, cmd[1:]))}" for cmd in path)
97+
98+
8199
def format_svg(path: Iterable[SvgPathCommand], float_format: str = "f") -> str:
100+
warnings.warn(
101+
"`format_svg` is deprecated, use `format_svg_path` instead.",
102+
FutureWarning,
103+
stacklevel=2,
104+
)
82105
return " ".join(
83106
f"{cmd[0]}{','.join(arg.__format__(float_format) for arg in cmd[1:])}"
84107
for cmd in path
@@ -808,7 +831,7 @@ def _path_from_SvgPathLike(path: SvgPathLike) -> svgelements.Path:
808831
return path
809832

810833
if not isinstance(path, str):
811-
path = format_svg(path)
834+
path = format_svg_path(path)
812835

813836
try:
814837
return svgelements.Path(str(path))

tests/test_svg.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131
from ocpsvg.svg import (
3232
ColorAndLabel,
3333
SvgPathCommand,
34-
_SegmentInPath,
34+
_SegmentInPath, # type: ignore private usage
3535
bezier_to_svg_path,
3636
edge_to_svg_path,
3737
edges_from_svg_path,
3838
face_to_svg_path,
3939
faces_from_svg_path,
4040
find_shapes_svg_in_document,
4141
format_svg,
42+
format_svg_path,
4243
import_svg_document,
4344
polyline_to_svg_path,
4445
svg_element_to_path,
@@ -52,7 +53,7 @@
5253

5354
class SvgPath(list[SvgPathCommand]):
5455
def __str__(self) -> str:
55-
return format_svg(self)
56+
return format_svg_path(self)
5657

5758

5859
def wire_from_curves(*curves: Geom_Curve):
@@ -821,7 +822,7 @@ def test_fix_closing_lines_doc():
821822
for e, _ in find_shapes_svg_in_document(StringIO(svg_src)):
822823
path = svg_element_to_path(e)
823824
assert path
824-
for segment in path:
825+
for segment in path: # type: ignore
825826
assert not isinstance(segment, svgelements.Line)
826827

827828

@@ -985,3 +986,18 @@ def split_floats(text: str):
985986
yield end
986987

987988
return list(split_floats(str(path)))
989+
990+
991+
def test_format_svg_path():
992+
assert format_svg_path([("M", 1, 2), ("L", 3.45, 6.78)]) == "M1,2 L3.45,6.78"
993+
994+
995+
def test_format_svg_path_number_format():
996+
path: list[SvgPathCommand] = [("M", 1.0, 2.0), ("L", 3e-8, 4.56789123456789)]
997+
assert format_svg_path(path, decimals=2) == "M1,2 L0,4.57"
998+
assert format_svg_path(path, decimals=8) == "M1,2 L0.00000003,4.56789123"
999+
1000+
1001+
def test_format_svg_deprecated():
1002+
with pytest.warns(FutureWarning):
1003+
format_svg([("M", 1, 2), ("L", 3.45, 6.78)])

0 commit comments

Comments
 (0)