Skip to content

compas_nest.pack

compas_nest.pack

Pack elements into a simple grid (array) layout — a deterministic alternative to nesting.

Mirrors the OpenNest grasshopper "pack / array" behaviour: each part instance is dropped into a grid cell sized by its bounding box, wrapping into rows, with gaps. The result is a :class:compas_nest.nest_result, so it plugs into the same viewer / placed_polylines / to_json / to_obj as a real nest.

pack(geo, columns=10, gap_x=10.0, gap_y=10.0, max_width=None)

Lay parts out in a simple row-major grid (no nesting).

Each part instance (copies expanded) is placed left to right; the x-advance uses each part's own width and the row height is the tallest part in that row. Two wrapping modes:

  • array (default) — start a new row every columns items.
  • distance — pass max_width and a row wraps once the next part would exceed that width (columns is ignored). A part wider than max_width still gets its own row.

Parameters:

Name Type Description Default
geo :class:`compas_nest.nest_geo`

The parts to arrange (holes and attributes are carried along).

required
columns int

Number of cells per row before wrapping (array mode).

10
gap_x float

Gaps between cells horizontally and vertically.

10.0
gap_y float

Gaps between cells horizontally and vertically.

10.0
max_width float

If set, wrap by row width instead of by columns (distance mode).

None

Returns:

Type Description
class:`compas_nest.nest_result`

All instances placed on a single sheet with rotation 0; use placed_polylines() / to_json() / to_obj() as usual.

Source code in compas_nest/pack.py
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
def pack(geo, columns=10, gap_x=10.0, gap_y=10.0, max_width=None):
    """Lay parts out in a simple row-major grid (no nesting).

    Each part instance (``copies`` expanded) is placed left to right; the x-advance uses each part's
    own width and the row height is the tallest part in that row. Two wrapping modes:

    * **array** (default) — start a new row every ``columns`` items.
    * **distance** — pass ``max_width`` and a row wraps once the next part would exceed that width
      (``columns`` is ignored). A part wider than ``max_width`` still gets its own row.

    Parameters
    ----------
    geo : :class:`compas_nest.nest_geo`
        The parts to arrange (holes and attributes are carried along).
    columns : int, optional
        Number of cells per row before wrapping (array mode).
    gap_x, gap_y : float, optional
        Gaps between cells horizontally and vertically.
    max_width : float, optional
        If set, wrap by row width instead of by ``columns`` (distance mode).

    Returns
    -------
    :class:`compas_nest.nest_result`
        All instances placed on a single sheet with rotation 0; use ``placed_polylines()`` /
        ``to_json()`` / ``to_obj()`` as usual.
    """
    columns = max(1, int(columns))
    placements = []
    x = 0.0
    y = 0.0
    row_h = 0.0
    col = 0
    for part_index, part in enumerate(geo.parts):
        copies = max(1, int(part.get("copies", 1)))
        minx, miny, maxx, maxy = _bbox(part["outline"])
        w = maxx - minx
        h = maxy - miny
        for _ in range(copies):
            # distance mode: wrap before placing if this part would overflow the row
            if max_width is not None and col > 0 and (x + w) > max_width:
                col = 0
                x = 0.0
                y += row_h + gap_y
                row_h = 0.0
            placements.append(
                {
                    "part_index": part_index,
                    "sheet_id": 0,
                    "angle": 0.0,
                    "tx": x - minx,  # land the part's min corner at (x, y)
                    "ty": y - miny,
                }
            )
            x += w + gap_x
            row_h = max(row_h, h)
            col += 1
            # array mode: wrap after a fixed number of columns
            if max_width is None and col >= columns:
                col = 0
                x = 0.0
                y += row_h + gap_y
                row_h = 0.0

    return nest_result(placements, geo, [(0.0, 0.0)], 1 if placements else 0)