Skip to content

Commit f04f5c8

Browse files
committed
replace argument 'flatten' by 'fast' in module ezdxf.bbox
Argument ' flatten' is deprecated and will be removed in v1.0. The new precise bounding box calculation for Bézier curves does not require a max flattening distance anymore!
1 parent fc802de commit f04f5c8

File tree

9 files changed

+85
-70
lines changed

9 files changed

+85
-70
lines changed

NEWS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
News
33
====
44

5-
Version 0.18b6 - dev
5+
Version 0.18b7 - dev
66
--------------------
77

88
- Release notes: https://ezdxf.mozman.at/release-v0-18.html
@@ -54,8 +54,9 @@ Version 0.18b6 - dev
5454
- CHANGE: `recover` module - recovered integer and float values are logged as severe errors
5555
- CHANGE: method `Path.all_lines_to_curve3` replaced by function `path.lines_to_curve3()`
5656
- CHANGE: method `Path.all_lines_to_curve4` replaced by function `path.lines_to_curve4()`
57-
- CHANGE: replaced arguments `flatten` and `segments` by argument `fast` of tool
57+
- CHANGE: replaced arguments `flatten` and `segments` by argument `fast` of tool
5858
function `Path.bbox()`
59+
- CHANGE: replaced argument `flatten` argument `fast` in the `ezdxf.bbox` module
5960
- BUGFIX [#663](https://github.com/mozman/ezdxf/issues/663):
6061
improve handling of large coordinates in `Bezier4P` and `Bezier3P` classes
6162
- BUGFIX [#655](https://github.com/mozman/ezdxf/issues/655):

examples/addons/drawing/pillow.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,11 @@ def main():
9292
# is optimized for speed.
9393
print(f"detecting model space extents (fast={args.fast}) ...")
9494
t0 = perf_counter()
95-
flatten = 0.01
96-
use_matplotlib = ezdxf.options.use_matplotlib
97-
if args.fast:
98-
ezdxf.options.use_matplotlib = False
99-
flatten = 0
100-
extents = bbox.extents(msp, flatten=flatten)
95+
extents = bbox.extents(msp, fast=args.fast)
10196
print(f"... in {perf_counter() - t0:.1f}s")
10297
print(f"EXTMIN: ({extents.extmin.x:.3f}, {extents.extmin.y:.3f})")
10398
print(f"EXTMAX: ({extents.extmax.x:.3f}, {extents.extmax.y:.3f})")
10499
print(f"SIZE: ({extents.size.x:.3f}, {extents.size.y:.3f})")
105-
ezdxf.options.use_matplotlib = use_matplotlib
106100

107101
ctx = RenderContext(doc)
108102
try:

examples/addons/drawing/render_model_space_as_tiles.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ def main(rows: int, cols: int):
6767
# module, DXF entities are referenced by handle strings. (multiprocessing!)
6868
cache = bbox.Cache()
6969

70-
# The bounding box calculation can take along time for big DXF files!
71-
# If flatten=0 the bounding box calculation for curves (SPLINE, ELLIPSE, ...)
70+
# The bounding box calculation can take a long time for big DXF files!
71+
# If fast=True the bounding box calculation for curves (SPLINE, ELLIPSE, ...)
7272
# is based on the control points of the Path class, this is less precise but
7373
# can speed up the calculation and for this task is a precise bounding box
7474
# not required.
7575
# This has no impact on this example which uses only straight lines
76-
extents = bbox.extents(msp, cache=cache, flatten=0)
76+
extents = bbox.extents(msp, cache=cache, fast=True)
7777

7878
ctx = RenderContext(doc)
7979
for tile, render_area in enumerate(render_areas(extents, (rows, cols))):
@@ -85,10 +85,10 @@ def main(rows: int, cols: int):
8585
ax.set_xlim(render_area.extmin.x, render_area.extmax.x)
8686
ax.set_ylim(render_area.extmin.y, render_area.extmax.y)
8787

88-
# Disable rendering of entities outside of the render area:
88+
# Disable rendering of entities outside the render area:
8989
def is_intersecting_render_area(entity):
9090
"""Returns True if entity should be rendered. """
91-
entity_bbox = bbox.extents([entity], cache=cache, flatten=0)
91+
entity_bbox = bbox.extents([entity], cache=cache, fast=True)
9292
return render_area.has_intersection(entity_bbox)
9393

9494
# Finalizing invokes auto-scaling!

profiling/bbox_samples.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,37 +62,29 @@ def print_bbox(box):
6262
count = 0
6363
sum_precise = 0.0
6464
sum_fast = 0.0
65-
USE_MATPLOTLIB = False
66-
PRELOAD_CACHE = False
67-
print(f"preloading matplotlib cache: {PRELOAD_CACHE}")
68-
print(f"using matplotlib for fast bounding box calculation: {USE_MATPLOTLIB}")
65+
6966

7067
for _name in CADKIT_FILES:
71-
ezdxf.options.use_matplotlib = True
7268
count += 1
7369
filename = os.path.join(EZDXF_TEST_FILES, CADKIT, _name)
7470
print(f"reading file: {filename}")
7571
t0 = time.perf_counter()
7672
doc = ezdxf.readfile(filename)
7773
msp = doc.modelspace()
7874
print(f"loading time: {time.perf_counter() - t0:.3f} sec")
79-
if PRELOAD_CACHE:
80-
print("preloading matplotlib font caches ...")
81-
bbox.extents(doc.modelspace(), flatten=0)
82-
83-
t0 = time.perf_counter()
84-
box0 = bbox.extents(doc.modelspace(), flatten=0.01)
85-
precise_result = time.perf_counter() - t0
86-
sum_precise += precise_result
87-
print(f"precise bounding box calculation in {precise_result:.3f} sec")
8875

89-
ezdxf.options.use_matplotlib = USE_MATPLOTLIB
9076
t0 = time.perf_counter()
91-
box1 = bbox.extents(doc.modelspace(), flatten=0)
77+
box1 = bbox.extents(doc.modelspace(), fast=True)
9278
fast_result = time.perf_counter() - t0
9379
sum_fast += fast_result
9480
print(f"fast bounding box calculation in {fast_result:.3f} sec")
9581

82+
t0 = time.perf_counter()
83+
box0 = bbox.extents(doc.modelspace(), fast=False)
84+
precise_result = time.perf_counter() - t0
85+
sum_precise += precise_result
86+
print(f"precise bounding box calculation in {precise_result:.3f} sec")
87+
9688
extents = box0.size
9789
diff = extents - box1.size
9890
print(f"bounding box difference:")

src/ezdxf/appsettings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def update_extents(doc: "Drawing") -> BoundingBox:
105105
- the :mod:`ezdxf.zoom` module
106106
107107
"""
108-
extents = bbox.extents(doc.modelspace(), flatten=0)
108+
extents = bbox.extents(doc.modelspace(), fast=True)
109109
doc.header[EXTMIN] = extents.extmin
110110
doc.header[EXTMAX] = extents.extmax
111111
return extents

src/ezdxf/bbox.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# Copyright (c) 2021, Manfred Moitzi
22
# License: MIT License
33
from typing import TYPE_CHECKING, Iterable, Dict, Optional
4+
import warnings
5+
6+
import ezdxf
47
from ezdxf import disassemble
5-
from ezdxf.math import BoundingBox, Vec3
8+
from ezdxf.math import BoundingBox
69

710
if TYPE_CHECKING:
811
from ezdxf.eztypes import DXFEntity
@@ -82,29 +85,38 @@ def _get_key(self, entity: "DXFEntity") -> Optional[str]:
8285
return key
8386

8487

88+
def _resolve_fast_arg(fast: bool, kwargs) -> bool:
89+
if "flatten" in kwargs:
90+
warnings.warn(
91+
"Argument 'flatten' replaced by argument 'fast', "
92+
"'flatten' will be removed in v1.0!",
93+
DeprecationWarning,
94+
)
95+
fast = not bool(kwargs["flatten"])
96+
return fast
97+
98+
8599
def multi_recursive(
86100
entities: Iterable["DXFEntity"],
87101
*,
88-
flatten: float = MAX_FLATTENING_DISTANCE,
102+
fast=False,
89103
cache: Cache = None,
104+
**kwargs,
90105
) -> Iterable[BoundingBox]:
91106
"""Yields all bounding boxes for the given `entities` **or** all bounding
92107
boxes for their sub entities. If an entity (INSERT) has sub entities, only
93108
the bounding boxes of these sub entities will be yielded, **not** the
94109
bounding box of entity (INSERT) itself.
95110
96-
Calculate bounding boxes from flattened curves, if argument `flatten`
97-
is not 0 (max flattening distance), else from control points.
111+
If argument `fast` is ``True`` the calculation of Bézier curves is based on
112+
their control points, this may return a slightly larger bounding box.
98113
99-
"""
114+
.. versionchanged:: 0.18
100115
101-
def vertices(p: disassemble.Primitive) -> Iterable[Vec3]:
102-
if flatten:
103-
primitive.max_flattening_distance = abs(flatten)
104-
return primitive.vertices()
105-
else:
106-
return disassemble.to_control_vertices([p])
116+
replaced argument `flatten` by argument `fast`
107117
118+
"""
119+
fast = _resolve_fast_arg(fast, kwargs)
108120
flat_entities = disassemble.recursive_decompose(entities)
109121
primitives = disassemble.to_primitives(flat_entities)
110122
for primitive in primitives:
@@ -115,11 +127,11 @@ def vertices(p: disassemble.Primitive) -> Iterable[Vec3]:
115127
if cache is not None:
116128
box = cache.get(entity)
117129
if box is None:
118-
box = BoundingBox(vertices(primitive))
130+
box = primitive.bbox(fast=fast)
119131
if box.has_data:
120132
cache.store(entity, box)
121133
else:
122-
box = BoundingBox(vertices(primitive))
134+
box = primitive.bbox(fast=fast)
123135

124136
if box.has_data:
125137
yield box
@@ -128,37 +140,63 @@ def vertices(p: disassemble.Primitive) -> Iterable[Vec3]:
128140
def extents(
129141
entities: Iterable["DXFEntity"],
130142
*,
131-
flatten: float = MAX_FLATTENING_DISTANCE,
143+
fast=False,
132144
cache: Cache = None,
145+
**kwargs,
133146
) -> BoundingBox:
134147
"""Returns a single bounding box for all given `entities`.
135148
136-
Calculate bounding boxes from flattened curves, if argument `flatten`
137-
is not 0 (max flattening distance), else from control points.
149+
If argument `fast` is ``True`` the calculation of Bézier curves is based on
150+
their control points, this may return a slightly larger bounding box.
151+
The `fast` mode uses also a simpler and mostly inaccurate text size
152+
calculation instead of the more precise but very slow calculation by
153+
`matplotlib`.
154+
155+
.. hint::
156+
157+
The fast mode is not much faster for non-text based entities, so using
158+
the slower default mode is not a big disadvantage if a more precise text
159+
size calculation is important.
160+
161+
.. versionchanged:: 0.18
162+
163+
added fast mode, replaced argument `flatten` by argument `fast`
138164
139165
"""
166+
fast = _resolve_fast_arg(fast, kwargs)
167+
use_matplotlib = ezdxf.options.use_matplotlib # save current state
168+
if fast:
169+
ezdxf.options.use_matplotlib = False
140170
_extends = BoundingBox()
141-
for box in multi_flat(entities, flatten=flatten, cache=cache):
171+
for box in multi_flat(entities, fast=fast, cache=cache):
142172
_extends.extend(box)
173+
ezdxf.options.use_matplotlib = use_matplotlib # restore state
143174
return _extends
144175

145176

146177
def multi_flat(
147178
entities: Iterable["DXFEntity"],
148179
*,
149-
flatten: float = MAX_FLATTENING_DISTANCE,
180+
fast=False,
150181
cache: Cache = None,
182+
**kwargs,
151183
) -> Iterable[BoundingBox]:
152184
"""Yields a bounding box for each of the given `entities`.
153185
154-
Calculate bounding boxes from flattened curves, if argument `flatten`
155-
is not 0 (max flattening distance), else from control points.
186+
If argument `fast` is ``True`` the calculation of Bézier curves is based on
187+
their control points, this may return a slightly larger bounding box.
188+
189+
.. versionchanged:: 0.18
190+
191+
replaced argument `flatten` by argument `fast`
156192
157193
"""
158194

159195
def extends_(entities_: Iterable["DXFEntity"]) -> BoundingBox:
160196
_extends = BoundingBox()
161-
for _box in multi_recursive(entities_, flatten=flatten, cache=cache):
197+
for _box in multi_recursive(
198+
entities_, fast=_resolve_fast_arg(fast, kwargs), cache=cache
199+
):
162200
_extends.extend(_box)
163201
return _extends
164202

src/ezdxf/zoom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def zoom_to_entities(layout: Layout, entities: Iterable[DXFEntity], factor):
4141
main_viewport = layout.main_viewport()
4242
if main_viewport is not None:
4343
entities = (e for e in entities if e is not main_viewport)
44-
extents = bbox.extents(entities, flatten=0)
44+
extents = bbox.extents(entities, fast=True)
4545
if extents.has_data:
4646
center(layout, extents.center, extents.size * factor)
4747

tests/test_04_dxf_high_level_structs/test_417_bbox.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,26 +148,16 @@ def circle():
148148
return lay
149149

150150

151-
def test_bbox_from_control_points(circle):
152-
box = bbox.extents(circle, flatten=0)
151+
def test_fast_bounding_box_calculation(circle):
152+
box = bbox.extents(circle, fast=True)
153153
assert box.extmin == (-100, -100)
154154
assert box.extmax == (+100, +100)
155155

156156

157-
def test_bbox_from_rough_flattening(circle):
158-
box = bbox.extents(circle, flatten=5)
159-
assert box.extmin != (-100, -100), "did not expect a precise result"
160-
assert box.extmax != (+100, +100), "did not expect a precise result"
161-
162-
163-
def test_bbox_from_precise_flattening(circle):
164-
box = bbox.extents(circle, flatten=0.0001)
165-
assert box.extmin.isclose(
166-
(-100, -100), abs_tol=0.0001
167-
), "expected a very close result"
168-
assert box.extmax.isclose(
169-
(+100, +100), abs_tol=0.0001
170-
), "expected a very_close result"
157+
def test_precise_bounding_box_calculation(circle):
158+
box = bbox.extents(circle, fast=False)
159+
assert box.extmin == (-100, -100)
160+
assert box.extmax == (+100, +100)
171161

172162

173163
if __name__ == "__main__":

tests/test_08_addons/test_814_text2path.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def get_paths_bbox(text):
295295

296296
def get_hatches_bbox(text):
297297
hatches = text2path.make_hatches_from_entity(text)
298-
return bbox.extents(hatches, flatten=0)
298+
return bbox.extents(hatches, fast=True)
299299

300300

301301
@pytest.fixture(params=[get_path_bbox, get_paths_bbox, get_hatches_bbox])

0 commit comments

Comments
 (0)