Bounding Box

New in version 0.16.

The ezdxf.bbox module provide tools to calculate bounding boxes for many DXF entities, but not for all. The bounding box calculation is based on the ezdxf.disassemble module and therefore has the same limitation.

Warning

If accurate boundary boxes for text entities are important for you, read this first: Text Boundary Calculation. TL;DR: Boundary boxes for text entities are not accurate!

Unsupported DXF entities:

  • All ACIS based types like BODY, 3DSOLID or REGION

  • External references (XREF) and UNDERLAY object

  • RAY and XRAY, extend into infinite

  • ACAD_TABLE, no basic support - only preserved by ezdxf

Unsupported entities are silently ignored, filtering of these DXF types is not necessary.

The base type for bounding boxes is the BoundingBox class from the module ezdxf.math.

The entities iterable as input can be the whole modelspace, an entity query or any iterable container of DXF entities.

The Calculation of bounding boxes of curves is done by flattening the curve by a default flattening distance of 0.01. Set argument flatten to 0 to speedup the bounding box calculation by accepting less precision for curved objects by using only the control vertices.

The optional caching object Cache has to be instantiated by the user, this is only useful if the same entities will be processed multiple times.

Example usage with caching:

from ezdxf import bbox

msp = doc.modelspace()
cache = bbox.Cache()
# get overall bounding box
first_bbox = bbox.extents(msp, cache=cache)
# bounding box of all LINE entities
second_bbox = bbox.extend(msp.query("LINE"), cache=cache)

Functions

ezdxf.bbox.extents(entities: Iterable[DXFEntity], *, flatten=0.01, cache: Cache = None) → BoundingBox

Returns a single bounding box for all given entities.

Calculate bounding boxes from flattened curves, if argument flatten is not 0 (max flattening distance), else from control points.

ezdxf.bbox.multi_flat(entities: Iterable[DXFEntity], *, flatten=0.01, cache: Cache = None) → Iterable[BoundingBox]

Yields a bounding box for each of the given entities.

Calculate bounding boxes from flattened curves, if argument flatten is not 0 (max flattening distance), else from control points.

ezdxf.bbox.multi_recursive(entities: Iterable[DXFEntity], *, flatten=0.01, cache: Cache = None) → Iterable[BoundingBox]

Yields all bounding boxes for the given entities or all bounding boxes for their sub entities. If an entity (INSERT) has sub entities, only the bounding boxes of these sub entities will be yielded, not the bounding box of entity (INSERT) itself.

Calculate bounding boxes from flattened curves, if argument flatten is not 0 (max flattening distance), else from control points.

Caching Strategies

Because ezdxf is not a CAD application, ezdxf does not manage data structures which are optimized for a usage by a CAD kernel. This means that the content of complex entities like block references or leaders has to be created on demand by DXF primitives on the fly. These temporarily created entities are called virtual entities and have no handle and are not stored in the entities database.

All this is required to calculate the bounding box of complex entities, and it is therefore a very time consuming task. By using a Cache object it is possible to speedup this calculations, but this is not a magically feature which requires an understanding of what is happening under the hood to achieve any performance gains.

For a single bounding box calculation, without any reuse of entities it makes no sense of using a Cache object, e.g. calculation of the modelspace extents:

from pathlib import Path
import ezdxf
from ezdxf import bbox

CADKitSamples = Path(ezdxf.EZDXF_TEST_FILES) / 'CADKitSamples'

doc = ezdxf.readfile(CADKitSamples / 'A_000217.dxf')
cache = bbox.Cache()
ext = bbox.extents(doc.modelspace(), cache)

print(cache)

1226 cached objects and not a single cache hit:

Cache(n=1226, hits=0, misses=3273)

The result for using UUIDs to cache virtual entities is not better:

Cache(n=2206, hits=0, misses=3273)

Same count of hits and misses, but now the cache also references ~1000 virtual entities, which block your memory until the cache is deleted, luckily this is a small DXF file (~838 kB).

Bounding box calculations for multiple entity queries, which have overlapping entity results, using a Cache object may speedup the calculation:

doc = ezdxf.readfile(CADKitSamples / 'A_000217.dxf.dxf')
msp = doc.modelspace()
cache = bbox.Cache(uuid=False)

ext = bbox.extents(msp, cache)
print(cache)

# process modelspace again
ext = bbox.extents(msp, cache)
print(cache)

Processing the same data again leads some hits:

1st run: Cache(n=1226, hits=0, misses=3273)
2nd run: Cache(n=1226, hits=1224, misses=3309)

Using uuid=True leads not to more hits, but more cache entries:

1st run: Cache(n=2206, hits=0, misses=3273)
2nd run: Cache(n=2206, hits=1224, misses=3309)

Creating stable virtual entities by disassembling the entities at first leads to more hits:

from ezdxf import disassemble

entities = list(disassemble.recursive_decompose(msp))
cache = bbox.Cache(uuid=False)

bbox.extents(entities, cache)
print(cache)

bbox.extents(entities, cache)
print(cache)

First without UUID for stable virtual entities:

1st run: Cache(n=1037, hits=0, misses=4074)
2nd run: Cache(n=1037, hits=1037, misses=6078)

Using UUID for stable virtual entities leads to more hits:

1st run: Cache(n=2019, hits=0, misses=4074)
2nd run: Cache(n=2019, hits=2018, misses=4116)

But caching virtual entities needs also more memory.

In conclusion: Using a cache is only useful, if you often process nearly the same data; only then can a performance gain be expected.

Cache Class

class ezdxf.bbox.Cache(uuid=False)

Caching object for ezdxf.math.BoundingBox objects.

Parameters

uuid – use UUIDs for virtual entities

hits
misses
invalidate(entities: Iterable[DXFEntity]) → None

Invalidate cache entries for the given DXF entities.

If entities are changed by the user, it is possible to invalidate individual entities. Use with care - discarding the whole cache is the safer workflow.

Ignores entities which are not stored in cache.