Refactored make_instance to Shape/copy & added docs

This commit is contained in:
Roger Maitland 2023-01-31 12:38:57 -05:00
parent eaf7267089
commit b0fc9bfdfd
3 changed files with 8326 additions and 18 deletions

View file

@ -68,6 +68,57 @@ and now the screw is part of the assembly.
├── outer hinge Hinge at 0x7fc9292c3f40, Location(p=(-150, 60, 50), o=(90, 0, 90)) ├── outer hinge Hinge at 0x7fc9292c3f40, Location(p=(-150, 60, 50), o=(90, 0, 90))
└── M6 screw Compound at 0x7fc8ee235310, Location(p=(-157, -40, 70), o=(-0, -90, -60)) └── M6 screw Compound at 0x7fc8ee235310, Location(p=(-157, -40, 70), o=(-0, -90, -60))
*********************************
Shallow vs. Deep Copies of Shapes
*********************************
Build123d supports the standard python ``copy`` module which provides two different types of
copy operations ``copy.copy()`` and ``copy.deepcopy()``.
Build123d's implementation of ``deepcopy()`` for the ``Shape`` class (e.g. ``Solid``, ``Face``, etc.)
does just that, creates a complete copy of the original all the way down to the CAD object.
``deepcopy`` is therefore suited to the case where the copy will be subsequently modified to
become its own unique item.
However, when building an assembly a common use case is to include many instances of an
object, each one identical but in a different location. This is where ``copy.copy()`` is
very useful as it copies all of the ``Shape`` except for the actual CAD object
which instead is a reference to the original (OpenCascade refers this as a ``TShape``). As
it's a reference any changes to the original will be seen in all of the shallow copies.
Consider this example where 100 screws are added to an assembly:
.. image:: reference_assembly.svg
:align: center
.. code::
screw = Compound.import_step("M6-1x12-countersunk-screw.step")
locs = HexLocations(6, 10, 10).local_locations
screw_copies = [copy.deepcopy(screw).locate(loc) for loc in locs]
copy_assembly = Compound(children=screw_copies)
copy_assembly.export_step("copy_assembly.step")
which takes about 5 seconds to run (on an older computer) and produces
a file of size 51938 KB. However, if a shallow copy is used instead:
.. code::
screw = Compound.import_step("M6-1x12-countersunk-screw.step")
locs = HexLocations(6, 10, 10).local_locations
screw_references = [copy.copy(screw).locate(loc) for loc in locs]
reference_assembly = Compound(children=screw_references)
reference_assembly.export_step("reference_assembly.step")
this takes about ¼ second and produces a file of size 550 KB - just over
1% of the size of the ``deepcopy()`` version and only 12% larger than the
screw's step file.
Using ``copy.copy()`` to create references to the original CAD object
for assemblies can substantially reduce the time and resources used
to create and store that assembly.
************************ ************************
Shapes are Anytree Nodes Shapes are Anytree Nodes

8260
docs/reference_assembly.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 MiB

View file

@ -2989,8 +2989,19 @@ class Shape(NodeMixin):
return result return result
def __copy__(self) -> Shape: def __copy__(self) -> Shape:
"""Return copy of self""" """Return shallow copy or reference of self
return copy.deepcopy(self, None)
Create an copy of this Shape that shares the underlying TopoDS_TShape.
Used when there is a need for many objects with the same CAD structure but at
different Locations, etc. - for examples fasteners in a larger assembly. By
sharing the TopoDS_TShape, the memory size of such assemblies can be greatly reduced.
Changes to the CAD structure of the base object will be reflected in all instances.
"""
reference = copy.deepcopy(self)
reference.wrapped.TShape(self.wrapped.TShape())
return reference
def copy(self) -> Shape: def copy(self) -> Shape:
"""Here for backwards compatibility with cq-editor""" """Here for backwards compatibility with cq-editor"""
@ -3001,19 +3012,6 @@ class Shape(NodeMixin):
) )
return copy.deepcopy(self, None) return copy.deepcopy(self, None)
def make_instance(self) -> Shape:
"""Create an instance of this Shape that shares the underlying TopoDS_TShape.
Used when there is a need for many objects with the same CAD structure but at
different Locations, etc. - for examples fasteners in a larger assembly. By
sharing the TopoDS_TShape, the memory size of such assemblies can be greatly reduced.
Changes to the CAD structure of the base object will be reflected in all instances.
"""
instance = copy.deepcopy(self)
instance.wrapped.TShape(self.wrapped.TShape())
return instance
def transform_shape(self, t_matrix: Matrix) -> Shape: def transform_shape(self, t_matrix: Matrix) -> Shape:
"""Apply affine transform without changing type """Apply affine transform without changing type
@ -6755,9 +6753,8 @@ class Vertex(Shape):
Example: Example:
part.faces(">z").vertices("<y and <x").val() + (0, 0, 15) part.faces(">z").vertices("<y and <x").val() + (0, 0, 15)
which creates a new Vertex 15mm above one extracted from a part. One can add or which creates a new Vertex 15 above one extracted from a part. One can add or
subtract a cadquery `Vertex` , `Vector` or `tuple` of float values to a subtract a `Vertex` , `Vector` or `tuple` of float values to a Vertex.
Vertex with the provided extensions.
""" """
if isinstance(other, Vertex): if isinstance(other, Vertex):
new_vertex = Vertex(self.X + other.X, self.Y + other.Y, self.Z + other.Z) new_vertex = Vertex(self.X + other.X, self.Y + other.Y, self.Z + other.Z)