1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
Bounding Box
============
.. module:: ezdxf.bbox
The :mod:`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
:mod:`ezdxf.disassemble` module and therefore has the same limitation.
.. warning::
If accurate boundary boxes for text entities are important for you,
read this first: :ref:`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 :class:`~ezdxf.math.BoundingBox` class
from the module :mod:`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 :class:`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:
.. code-block:: Python
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
---------
.. autofunction:: extents
.. autofunction:: multi_flat
.. autofunction:: multi_recursive
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 entity 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 :class:`Cache` object
it is possible to speedup this calculations, but this is not a magically feature,
it 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 :class:`Cache` object, e.g. calculation of the modelspace
extents:
.. code-block:: python
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 :class:`Cache` object may speedup the calculation:
.. code-block:: python
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 :code:`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:
.. code-block:: Python
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 an increase in performance be expected.
Cache Class
-----------
.. autoclass:: Cache
.. py:attribute:: has_data
``True`` if the cache contains any bounding boxes
.. py:attribute:: hits
.. py:attribute:: misses
.. automethod:: invalidate
|