Skip to content

compas_nest.offset

compas_nest.offset

Clipper2-based polygon offsetting, used to add clearance before nesting.

Convention (orientation-normalised, so it does not depend on input winding): distance > 0 grows a ring outward, distance < 0 shrinks it inward.

The directional helpers add a uniform clearance d so the real parts end up with gaps:

  • :func:offset_geo — element outer +d (grow), element holes -d (shrink)
  • :func:offset_sheets — sheet outer -d (shrink), sheet holes +d (grow)

offset_polyline(polyline, distance)

Offset a single closed polyline.

Parameters:

Name Type Description Default
polyline :class:`compas.geometry.Polyline`

Closed ring (a trailing duplicate point is tolerated).

required
distance float

Positive grows the ring outward, negative shrinks it inward.

required

Returns:

Type Description
class:`compas.geometry.Polyline` or None

The offset ring (the largest one if the offset splits it), or None if it vanishes.

Source code in compas_nest/offset.py
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
def offset_polyline(polyline: Polyline, distance: float) -> Optional[Polyline]:
    """Offset a single closed polyline.

    Parameters
    ----------
    polyline : :class:`compas.geometry.Polyline`
        Closed ring (a trailing duplicate point is tolerated).
    distance : float
        Positive grows the ring outward, negative shrinks it inward.

    Returns
    -------
    :class:`compas.geometry.Polyline` or None
        The offset ring (the largest one if the offset splits it), or ``None`` if it vanishes.
    """
    ring = _ring(polyline)
    if len(ring) < 3:
        return None
    # normalise to CCW so positive distance is always outward
    if _signed_area(ring) < 0:
        ring = ring[::-1]
    result = _clipper.inflate([[(x, y) for x, y in ring]], float(distance))
    if not result:
        return None
    best = max(result, key=lambda r: abs(_signed_area([list(p) for p in r])))
    points = [[float(p[0]), float(p[1]), 0.0] for p in best]
    points.append(points[0])  # close
    return Polyline(points)

offset_geo(geo, distance)

Return a copy of geo with each part outer grown and holes shrunk by distance.

Parameters:

Name Type Description Default
geo :class:`compas_nest.nest_geo`
required
distance float

Clearance to add around the parts.

required

Returns:

Type Description
class:`compas_nest.nest_geo`
Source code in compas_nest/offset.py
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
def offset_geo(geo: nest_geo, distance: float) -> nest_geo:
    """Return a copy of ``geo`` with each part outer grown and holes shrunk by ``distance``.

    Parameters
    ----------
    geo : :class:`compas_nest.nest_geo`
    distance : float
        Clearance to add around the parts.

    Returns
    -------
    :class:`compas_nest.nest_geo`
    """
    out = nest_geo(name=geo.name)
    for part in geo.parts:
        outline = offset_polyline(part["outline"], +distance)
        if outline is None:
            continue
        holes = []
        for hole in part.get("holes", []):
            shrunk = offset_polyline(hole, -distance)
            if shrunk is not None:
                holes.append(shrunk)
        out.add_part(outline, holes=holes, copies=int(part.get("copies", 1)), attributes=list(part.get("attributes", [])))
    return out

offset_sheets(sheets, distance)

Return a copy of sheets with each sheet outer shrunk and holes grown by distance.

Parameters:

Name Type Description Default
sheets :class:`compas_nest.nest_sheets`
required
distance float

Clearance to keep from sheet edges and holes.

required

Returns:

Type Description
class:`compas_nest.nest_sheets`
Source code in compas_nest/offset.py
 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
def offset_sheets(sheets: nest_sheets, distance: float) -> nest_sheets:
    """Return a copy of ``sheets`` with each sheet outer shrunk and holes grown by ``distance``.

    Parameters
    ----------
    sheets : :class:`compas_nest.nest_sheets`
    distance : float
        Clearance to keep from sheet edges and holes.

    Returns
    -------
    :class:`compas_nest.nest_sheets`
    """
    out = nest_sheets(name=sheets.name)
    for sheet in sheets.sheets:
        outline = offset_polyline(sheet["outline"], -distance)
        if outline is None:
            continue
        holes = []
        for hole in sheet.get("holes", []):
            grown = offset_polyline(hole, +distance)
            if grown is not None:
                holes.append(grown)
        out.add_sheet(outline, holes=holes)
    return out