Skip to content

Commit

Permalink
Merge pull request #641 from roman-dvorak/roman/docs/packs
Browse files Browse the repository at this point in the history
Update documentation for pack function
  • Loading branch information
gumyr authored Jun 12, 2024
2 parents 56d24c0 + 37d464e commit 0c4cfb9
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
93 changes: 93 additions & 0 deletions docs/assemblies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions docs/import_export.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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() <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
Expand Down
78 changes: 78 additions & 0 deletions docs/pack_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
name: pack_demo.py
by: roman-dvorak <[email protected]>
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())

0 comments on commit 0c4cfb9

Please sign in to comment.