diff --git a/docs/assemblies.rst b/docs/assemblies.rst index 5dcbe9b9..9cef0e3d 100644 --- a/docs/assemblies.rst +++ b/docs/assemblies.rst @@ -157,3 +157,96 @@ adds the following attributes to :class:`~topology.Shape`: Any iterator can be assigned to the ``children`` attribute but subsequently the children are stored as immutable ``tuple`` objects. To add a child to an existing :class:`~topology.Compound` object, the ``children`` attribute will have to be reassigned. + + .. _pack: + +****** +pack +****** + +The :meth:`pack.pack` function arranges objects in a compact, non-overlapping layout within a square(ish) 2D area. It is designed to minimize the space between objects while ensuring that no two objects overlap. + +.. py:module:: pack + + +.. autofunction:: pack + + + +Detailed Description +--------------------- + +The ``pack`` function uses a bin-packing algorithm to efficiently place objects within a 2D plane, ensuring that there is no overlap and that the space between objects is minimized. This is particularly useful in scenarios where spatial efficiency is crucial, such as layout design and object arrangement in constrained spaces. + +The function begins by calculating the bounding boxes for each object, including the specified padding. It then uses a helper function ``_pack2d`` to determine the optimal positions for each object within the 2D plane. The positions are then translated back to the original objects, ensuring that they are arranged without overlapping. + +Usage Note +---------- + +The ``align_z`` parameter is especially useful when creating print-plates for 3D printing. By aligning the bottoms of the shapes to the same XY plane, you ensure that the objects are perfectly positioned for slicing software, which will no longer need to perform this alignment for you. This can streamline the process and improve the accuracy of the print setup. + +Example Usage +------------- + +.. code:: python + + # [import] + from build123d import * + from ocp_vscode import * + + + # [initial space] + b1 = Box(100, 100, 100, align=(Align.CENTER, Align.CENTER, Align.MIN)) + b2 = Box(54, 54, 54, align=(Align.CENTER, Align.CENTER, Align.MAX), mode=Mode.SUBTRACT) + b3 = Box(34, 34, 34, align=(Align.MIN, Align.MIN, Align.CENTER), mode=Mode.SUBTRACT) + b4 = Box(24, 24, 24, align=(Align.MAX, Align.MAX, Align.CENTER), mode=Mode.SUBTRACT) + + + +.. image:: assets/pack_demo_initial_state.svg + :align: center + + +.. code:: python + + # [pack 2D] + + xy_pack = pack( + [b1, b2, b3, b4], + padding=5, + align_z=False + ) + + +.. image:: assets/pack_demo_packed_xy.svg + :align: center + + +.. code:: python + + # [Pack and align_z] + + z_pack = pack( + [b1, b2, b3, b4], + padding=5, + align_z=True + ) + +.. image:: assets/pack_demo_packed_z.svg + :align: center + + +Tip +--- + +If you place the arranged objects into a ``Compound``, you can easily determine their bounding box and check whether the objects fit on your print bed. + + +.. code:: python + + # [bounding box] + print(Compound(xy_pack).bounding_box()) + # bbox: 0.0 <= x <= 159.0, 0.0 <= y <= 129.0, -54.0 <= z <= 100.0 + + print(Compound(z_pack).bounding_box()) + # bbox: 0.0 <= x <= 159.0, 0.0 <= y <= 129.0, 0.0 <= z <= 100.0 diff --git a/docs/import_export.rst b/docs/import_export.rst index 3e47f23d..6eca616f 100644 --- a/docs/import_export.rst +++ b/docs/import_export.rst @@ -250,6 +250,11 @@ For example: .. autoclass:: mesher.Mesher +.. note:: + + If you need to align multiple components for 3D printing, you can use the :ref:`pack() ` function to arrange the objects side by side and align them on the same plane. This ensures that your components are well-organized and ready for the printing process. + + 2D Importers ============ .. py:module:: importers diff --git a/docs/pack_demo.py b/docs/pack_demo.py new file mode 100644 index 00000000..a5231541 --- /dev/null +++ b/docs/pack_demo.py @@ -0,0 +1,78 @@ +""" + +name: pack_demo.py +by: roman-dvorak +date: June 3rd 2024 + +desc: + + This example shows ability of pack function to pack objects. + +""" + + + +# [import] +from build123d import * +from ocp_vscode import * + + +# [initial space] +b1 = Box(100, 100, 100, align=(Align.CENTER, Align.CENTER, Align.MIN)) +b2 = Box(54, 54, 54, align=(Align.CENTER, Align.CENTER, Align.MAX), mode=Mode.SUBTRACT) +b3 = Box(34, 34, 34, align=(Align.MIN, Align.MIN, Align.CENTER), mode=Mode.SUBTRACT) +b4 = Box(24, 24, 24, align=(Align.MAX, Align.MAX, Align.CENTER), mode=Mode.SUBTRACT) + + + + +# [Export SVG files] +def write_svg(part, filename: str, view_port_origin=(-100, 100, 150)): + """Save an image of the BuildPart object as SVG""" + visible, hidden = part.project_to_viewport(view_port_origin) + max_dimension = max(*Compound(children=visible + hidden).bounding_box().size) + exporter = ExportSVG(scale=100 / max_dimension) + exporter.add_layer("Visible", line_weight=0.2) + exporter.add_layer("Hidden", line_color=(99, 99, 99), line_type=LineType.ISO_DOT) + exporter.add_shape(visible, layer="Visible") + exporter.add_shape(hidden, layer="Hidden") + exporter.write(f"assets/{filename}.svg") + + + + +write_svg( + Compound( + [b1, b2, b3, b4,], + "pack_demo_initial_state" + ), + "pack_demo_initial_state.svg", + (50, 0, 100), +) + +# [pack 2D] + +xy_pack = pack( + [b1, b2, b3, b4], + padding=5, + align_z=False +) + +write_svg(Compound(xy_pack), "pack_demo_packed_xy.svg", (50, 0, 100)) + + +# [Pack and align_z] + + +z_pack = pack( + [b1, b2, b3, b4], + padding=5, + align_z=True +) + +write_svg(Compound(z_pack), "pack_demo_packed_z.svg", (50, 0, 100)) + + +# [bounding box] +print(Compound(xy_pack).bounding_box()) +print(Compound(z_pack).bounding_box()) \ No newline at end of file