Skip to main content
← API reference

1D linear cutting

Lengths cut from bars where every bar can serve every part.

1D — linear

POST /v1/optimize/1d

{
  "parts": [{ "name": "Rail", "length": 1200, "qty": 6 }],
  "stock": [{ "length": 3000, "qty": 5, "price": 12.5 }],
  "options": { "kerf": 3, "trim": { "start": 10, "end": 0 } }
}
Uses a public demo key — rate-limited, small jobs only, never billed.

Parts take length (plus qty, name, priority); stock takes length, qty and price. Both parts and stock also accept an optional material tag (stock also priority) — material restricts a part to stock of the same material, and the response then adds materials and unmatchedMaterials as in 2D. Options are kerf, tolerance, trim.start / trim.end, minimizeCost, respectStock and minOffcut. The response returns rods instead of sheets, each with its parts, remaining length and offcuts.

1D — response

{
  "engine": "heuristic",
  "engineVersion": "1.0.0+10e0c941",
  "contractVersion": "1",
  "deterministic": true,
  "rods": [
    {
      "length": 3000,
      "price": 12.5,
      "remaining": 584,
      "parts": [
        { "name": "Rail", "pos": 10, "length": 1200 },
        { "name": "Rail", "pos": 1213, "length": 1200 }
      ],
      "offcuts": [584]
    },
    {
      "length": 3000,
      "price": 12.5,
      "remaining": 584,
      "parts": [
        { "name": "Rail", "pos": 10, "length": 1200 },
        { "name": "Rail", "pos": 1213, "length": 1200 }
      ],
      "offcuts": [584]
    },
    {
      "length": 3000,
      "price": 12.5,
      "remaining": 584,
      "parts": [
        { "name": "Rail", "pos": 10, "length": 1200 },
        { "name": "Rail", "pos": 1213, "length": 1200 }
      ],
      "offcuts": [584]
    }
  ],
  "metrics": {
    "rodCount": 3,
    "yieldPct": 80,
    "placed": 6,
    "total": 6,
    "cuts": 6,
    "totalPrice": 37.5,
    "toleranceAcceptedCount": 0
  },
  "unplaced": [],
  "warnings": [],
  "timing": { "solveMs": 2.19 },
  "cutPlan": [
    { "sheet": 0, "step": 1, "axis": "v", "pos": 1210, "length": 0, "stage": 1 },
    { "sheet": 0, "step": 2, "axis": "v", "pos": 2413, "length": 0, "stage": 1 },
    { "sheet": 1, "step": 1, "axis": "v", "pos": 1210, "length": 0, "stage": 1 },
    { "sheet": 1, "step": 2, "axis": "v", "pos": 2413, "length": 0, "stage": 1 },
    { "sheet": 2, "step": 1, "axis": "v", "pos": 1210, "length": 0, "stage": 1 },
    { "sheet": 2, "step": 2, "axis": "v", "pos": 2413, "length": 0, "stage": 1 }
  ]
}

rods replaces sheets and there is no guillotineValid, because a linear cut is always producible. Each part’s pos is the offset of its near end from the bar end that trim.start trims, so the first part starts exactly at trim.start and each following pos adds one kerf. remaining is the USABLE off-cut: the kerf of the cut that frees it from the last piece is already deducted, so it is the reclaimable length, not the raw gap.

It is reported on every rod even when it is below minOffcut — minOffcut only filters the offcuts array, which holds at most one entry. When several plans tie on parts placed, material, rods and price, the one whose leftover is longest (compared longest first) is returned, so the remnant tends to sit on one rod instead of being spread over many; this never changes the rod count or the price.

In the cut plan, sheet is the rod index, axis is always "v", stage is always 1 and length is always 0: a bar crosscut has no travel distance to report, which is also why the 1D metrics carry cuts but no cutLength.

rods[]

FieldTypeDescription
lengthnumberFULL bar length as supplied in stock.
pricenumber | nullPrice of the stock row, or null.
remainingnumberUSABLE off-cut left on this bar, rounded to 3 decimals: the kerf of the cut that frees it from the last piece is already deducted, so it is the reclaimable length, not the raw gap. Reported even when it is below options.minOffcut — minOffcut filters offcuts, not this.
partsPlacedPart1D[]In cutting order along the bar.
offcutsnumber[]At most one entry: [remaining] when it is greater than 0 and at least options.minOffcut, otherwise []. ABSENT when include excludes offcuts.
materialstringThe material this rod was cut from. Absent when the job carries no material.
metaobjectOpaque JSON object (any keys) echoed back VERBATIM on the matching output (a part's meta on every placed piece of it; a stock row's meta on every sheet/rod cut from it). Attach ERP article numbers, order-line ids, customer refs to reconcile the cut plan with your system. Never parsed, never affects placement. Part meta follows its request row: the meta of each row lands on exactly its qty of placed pieces, even when rows share a name; stock meta by stock-row order.

rods[].parts[]

FieldTypeDescription
namestringThe requested name, or the generated default.
posnumberOffset of the part’s NEAR end from the bar end that trim.start trims. The first part therefore starts at trim.start, and each subsequent pos adds one kerf. Unit-agnostic: pick one unit (mm, inch, …) for every length in the request and every length in the response is in that same unit. Nothing is converted server-side.
lengthnumberThe part length, as requested.
metaobjectOpaque JSON object (any keys) echoed back VERBATIM on the matching output (a part's meta on every placed piece of it; a stock row's meta on every sheet/rod cut from it). Attach ERP article numbers, order-line ids, customer refs to reconcile the cut plan with your system. Never parsed, never affects placement. Part meta follows its request row: the meta of each row lands on exactly its qty of placed pieces, even when rows share a name; stock meta by stock-row order.

metrics (1D)

FieldTypeDescription
rodCountintegerBars used. Equals rods.length.
yieldPctnumberPlaced length ÷ total FULL bar length × 100, rounded to 2 decimals. Trim and kerf count as waste.
placedintegerPieces placed, after qty expansion.
totalintegerPieces requested, after qty expansion.
cutsintegerTotal crosscuts across all used bars — one per placed piece, since each piece is freed by a cut at its far end. There is no length to report for a bar cut, which is why 1D has no cutLength.
totalPricenumberSum of used bar prices, rounded to 2 decimals.
toleranceAcceptedCountintegerHow many pieces fitted only because options.tolerance allowed an overshoot.

unplaced[] (1D)

FieldTypeDescription
namestringThe part name.
lengthnumberAs requested.
qtyintegerHow many could not be placed.