2,022 bytes · the Python implementation · view raw
from typing import List, Sequence
from .construction_timber_length_types import CutBar, CuttingList
def timber_cutting_list(cuts: Sequence[int], stock_length: int, kerf: int) -> CuttingList:
"""Plan which pieces to cut from which stock length, first-fit decreasing. A bar holds pieces p1..pn when their lengths plus a kerf between each pair fit: sum + kerf * (n - 1) <= stock_length. The last piece may end exactly at the end of the bar, which needs no final cut. Ignoring kerf is the usual mistake: three 1200 mm pieces do not come out of one 3600 mm length. """if isinstance(stock_length, bool) ornot isinstance(stock_length, int) or stock_length <= 0:
raise ValueError("stockLength must be a whole number of millimetres greater than 0, received %r" % (stock_length,))
if isinstance(kerf, bool) ornot isinstance(kerf, int) or kerf < 0:
raise ValueError("kerf must be a whole number of millimetres, 0 or more, received %r" % (kerf,))
for i, cut in enumerate(cuts):
if isinstance(cut, bool) ornot isinstance(cut, int) or cut <= 0:
raise ValueError("cut %d must be a whole number of millimetres greater than 0, received %r" % (i + 1, cut))
if cut > stock_length:
raise ValueError("cut %d (%d mm) is longer than the stock length (%d mm)" % (i + 1, cut, stock_length))
# Equal lengths are interchangeable, so a plain descending sort is deterministic.
pieces: List[List[int]] = []
used: List[int] = []
for cut in sorted(cuts, reverse=True):
for b in range(len(pieces)):
if used[b] + kerf + cut <= stock_length:
pieces[b].append(cut)
used[b] += kerf + cut
breakelse:
pieces.append([cut])
used.append(cut)
bars = [CutBar(cuts=p, offcut=max(0, stock_length - used[b] - kerf)) for b, p in enumerate(pieces)]
return CuttingList(bars=bars, bar_count=len(bars), waste=len(bars) * stock_length - sum(cuts))