API-Dokumentation

Der Optimierer als HTTP-Dienst: Senden Sie Zuschnitte und Rohmaterial und Sie erhalten einen geprüften Schnittplan oder ein druckfertiges PDF. Stabil, versioniert und abwärtskompatibel: Felder werden nur hinzugefügt, niemals entfernt.

Einführung

Basis-URL https://api.linearcutting.com. Alles ist JSON, außer dem PDF-Endpunkt. Anfragen sind zustandslos: Wir speichern Ihre Zuschnittlisten nicht.

Sie brauchen keinen API-Schlüssel, um dies zu testen. Die folgende Zeile kann sofort unverändert ausgeführt werden. Fügen Sie sie in ein Terminal ein.

Copy, paste, run
curl -X POST https://api.linearcutting.com/v1/optimise \
  -H "Content-Type: application/json" \
  -d '{"parts": "2400x5, 1800x3", "stock": 6000}'

Das ist eine vollständige Zuschnittliste: fünf Teile zu 2400, drei zu 1800, aus 6000-mm-Stangen geschnitten. Sie erhalten die Layouts, die Anzahl der benötigten Stangen, die Abfallmenge und die Information, ob der Plan nachweislich optimal ist.

Anfragen ohne Schlüssel sind kostenlos zum Testen: 30 Anfragen pro Stunde, 250 pro Monat und IP-Adresse. Bei Überschreitung meldet Fehler 429 die Rücksetzzeit. Fehlgeschlagene Anfragen zählen nicht. Für den Praxiseinsatz brauchen Sie einen Schlüssel (ab 5 $/Monat). Er hebt alle Limits auf, erhöht die maximale Auftragsgröße und gibt Ihnen eigene Rechenleistung, damit Sie nicht im allgemeinen Betrieb ausgebremst werden.

Offizielle Clients

Keine Abhängigkeiten, MIT-lizenziert, und sie benötigen auch keinen Schlüssel. Wenn Sie Python oder JavaScript verwenden, schreiben Sie nicht von Hand einen fetch:

pip install linearcutting
import linearcutting as lc

plan = lc.optimise(parts="2400x5, 1800x3", stock=6000)
plan.bars_used   # 4
plan.optimal     # True, proved, not guessed
npm install linearcutting
import { optimise } from "linearcutting";

const plan = await optimise({ parts: "2400x5, 1800x3", stock: 6000 });
plan.barsUsed;   // 4
plan.optimal;    // true

Python auf PyPI · JavaScript auf npm · Python-Quellcode · JavaScript-Quellcode

Zwei Garantien, auf die Sie sich verlassen können. verifiziert ist bei Erfolg immer „wahr“: Jeder Plan wird anhand Ihrer Eingaben erneut geprüft, bevor er zurückgegeben wird. So kann Sie kein Plan erreichen, der Ihre Zuschnittliste nicht erfüllt. optimal ist ein Beweis, keine Schätzung. Es ist nur dann „wahr“, wenn die Suche ergeben hat, dass kein besserer Plan existiert, sei es durch das Ausschöpfen aller Möglichkeiten oder durch ein Zertifikat. Wenn dies im Zeitrahmen nicht bewiesen werden kann, geben wir false zurück, anstatt zu hoffen.

optimal und stockBarsLowerBound sind zwei verschiedene Dinge, und sie weichen absichtlich voneinander ab. Die untere Schranke ist eine Vereinfachung, grob gesagt „Gesamtlänge ÷ Stangenlänge“, und sie ist oft nicht erreichbar. Fünf Teile à 2400 und drei à 1800 sind 17.400 mm, was 2,9 Stangen à 6000 entspricht, also sagt die Schranke 3. Tatsächlich können Sie diese Teile nicht aus 3 Stangen schneiden; 4 ist das wahre Minimum, und der Solver beweist es. Daher ist stockBarsUsed: 4 mit stockBarsLowerBound: 3 und optimal: true kein Widerspruch. Es bedeutet, dass die Schranke ungenau war, nicht der Plan. Verwenden Sie optimal, um zu entscheiden, ob die Suche fortgesetzt werden soll; verwenden Sie die Schranke nur zur Plausibilitätsprüfung.

Kurzschreibweise

Die Langform ist explizit und etwas umständlich. Für den Normalfall gibt es die Kurzform: eine Zahl ist eine Länge, und x bedeutet die Anzahl:

The short form
{"parts": "2400x5, 1800x3, 600", "stock": 6000}

Jeder dieser Ausdrücke bedeutet dasselbe, verwenden Sie also den, der am besten zu Ihrem Code passt:

All equivalent
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]}

Ein drittes Element ist eine Bezeichnung für ein Teil und ein Preis für Rohmaterial: [2400, 5, "rail"], [6000, 20, 45.50]. Und stock ist die Kurzform für die alte Trennung in stockLength / stocks. Eine einzelne Zahl bedeutet „diese Länge, unbegrenzte Anzahl“.

Die Kurzschreibweise wird zuerst erweitert. Sie ist also eine Eingabeerleichterung und kein anderer Modus: eine kurze Anfrage und die lange Anfrage, zu der sie erweitert wird, geben byte-identische Pläne zurück. Beide Schreibweisen sind frei kombinierbar. Jede bestehende Anfrage funktioniert weiterhin genau wie bisher.

Zuschnittliste in einer URL

Kleine Aufträge passen in einen Query-String, daher funktioniert auch GET. Das ist praktisch für Links zum Teilen, für Angebote oder für Fehlermeldungen:

GET /v1/optimise
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000"

Gleiche Authentifizierung, gleiche Limits, gleicher Plan wie bei POST. Kann auch im Browser geöffnet werden.

Authentifizierung

Der öffentliche Tarif benötigt keinen Schlüssel. Kostenpflichtige Tarife senden ihren Schlüssel in einem Header; die Schlüssel enthalten auch Ihre erlaubten Browser-Origins, sodass Anrufe von Ihrer eigenen Website ohne Proxy funktionieren.

http
X-API-Key: lk_live_9f2c...

Ein ungültiger oder abgelaufener Schlüssel gibt 401 zurück. Schlüssel werden pro Arbeitsbereich ausgestellt: siehe Tarife.

POST /v1/optimise

Löst eine Zuschnittliste. Gibt den Plan, Statistiken und beweisbare Schranken zurück.

Anfragekörper

FieldTypStandardBeschreibung
parts erforderlich Array- Die benötigten Teile. Jeweils: length (> 0), quantity (Ganzzahl ≥ 1), optional label, material, leftAngle, rightAngle.
stockLength Zahl- Eine einzelne Stangenlänge in unbegrenzter Stückzahl. Entweder diesen Parameter oder stocks verwenden, nicht beide.
stocks array- Käufliche Stangenlängen. Jeweils: length, quantity (null = unbegrenzt), price (null = Material statt Kosten optimieren), optional material, priority.
offcuts array[] Stangenlängen aus Ihrem Bestand: length, quantity, optional material, priority. Kostenloses Material, wird nie als Kauf gezählt.
kerfZahl ≥ 00Schnittbreite des Sägeblatts.
trimbooleanfalseJedes Rohmaterial an beiden Enden besäumen.
methodstringbalanced balanced, least_waste, offcuts_first, fewest_setups (Alias: fewest_setups). Preise in stocks schalten balanced auf niedrigste Kosten um.
minRemnantZahl ≥ 00Reststücke ab dieser Länge gelten als usableRemnants und nicht als Abfall.
angleCutsObjektaus{enabled, width, axialSymmetry}. Siehe Winkelschnitte.
unitstring"mm"Wird zurückgegeben und auf PDFs gedruckt. Nimmt keine Umrechnungen vor.
unitFormatstringdecimalfraction stellt PDF-Maße als auf 1/64 gerundete Brüche dar.
timeLimitSekundenautoRechenzeit, durch Tarif begrenzt. Die Suche ist "Anytime": Eine längere Rechenzeit kann das Ergebnis nur verbessern.

Antwort

200 application/json
{
  "plan": {
    "combinations": [
      {
        "usedComb": {
          "combination": [2400, 2400, 1200],
          "length": 6500,
          "source": "stock",
          "waste": { "total": 492, "remnant": 484, "saw": 8 }
        },
        "count": 2
      }
    ],
    "purchases":      [{ "length": 6500, "count": 2 }],
    "usableRemnants": [],
    "unusedOffcuts":  [],
    "stockBarsUsed":  2,
    "stockBarsLowerBound": 2,
    "totalWaste":     984,
    "optimal":        true,
    "verified":       true,
    "solveTimeMs":    12.5
  },
  "stats": {
    "totalParts": 8, "totalPartsLength": 15600, "totalUsedLength": 16600,
    "yieldPercent": 93.98, "cutCount": 8, "layoutCount": 1,
    "totalCost": null, "trueWaste": 984
  },
  "costLowerBound": null,
  "meta": { "method": "balanced", "kerf": 4, "tier": "public",
            "poweredBy": "linearcutting.com" }
}
FieldBeschreibung
plan.combinations[]Ein Eintrag pro eindeutigem Layout. usedComb.combination listet die aus dieser Stange geschnittenen Teile auf, length ist die Quelle, source ist stock oder offcut, und count gibt an, wie viele Stangen auf diese Weise zugeschnitten werden.
usedComb.wastetotal = Quelle − Teile; aufgeteilt in saw (Sägeschnittbreite + Anschnitt) und remnant (das übrig gebliebene Ende).
plan.purchases[]Die Einkaufsliste: length, count, plus price/cost, wenn Sie Preise angegeben haben.
plan.usableRemnantsReststücke ≥ minRemnant, die größten zuerst: Ihr Reststücklager für morgen.
plan.unusedOffcutsReststücke, die der Plan nicht benötigt hat.
plan.stockBarsUsedGesamtzahl der zu kaufenden Stangen.
plan.optimalNachweis. Nur „wahr“, wenn der Plan eine nachweisbare Untergrenze erreicht hat (oder die Suche vollständig abgeschlossen wurde). Kann dem Benutzer als „bestmöglich“ angezeigt werden.
plan.verifiedImmer „wahr“ bei Erfolg: Bedarf gedeckt, jedes Layout passt physisch, Reststückvorrat berücksichtigt.
plan.stockBarsLowerBound, costLowerBoundNachweisbare Minima. Wenn optimal „falsch“ ist, zeigt die Lücke, um wie viel dieser Plan höchstens abweichen könnte.
statsAuftragssummen: Teile, Längen, yieldPercent (Materialausnutzung), cutCount, layoutCount, totalCost, trueWaste, und byMaterial bei Verwendung von Materialgruppen.

POST /v1/optimise/pdf

Gleicher Anfrage-Body, plus ein optionales jobName. Gibt application/pdf zurück: Seite 1 ist eine Zusammenfassung für den Projektmanager (Bestellung, Kosten, Optimalitätszertifikat); die folgenden Seiten sind Arbeitsblätter für den Sägenbediener mit laufenden Schnittpositionen, Teile-Etiketten und einem Kontrollkästchen pro Stange. Bei Aufrufern mit Schlüssel wird deren Namensnennung oder Branding gerendert.

bash
curl -X POST https://api.linearcutting.com/v1/optimise/pdf \
  -H "Content-Type: application/json" \
  -H "X-API-Key: lk_live_9f2c..." \
  -d '{ "parts": [{"length": 2400, "quantity": 4}], "stockLength": 6500,
       "kerf": 4, "jobName": "Smith deck order" }' \
  --output cutting-plan.pdf

GET /v1/health

json
{ "status": "ok", "version": "1.3.0", "solver": "Linear Cutlist Optimizer v2" }

Materialgruppen

Markieren Sie parts, stocks und offcuts mit material, und jedes Material wird als eigener Job innerhalb einer Anfrage gelöst: Zuschnitte landen immer nur auf Rohmaterial desselben Materials. Zuschnittpläne und Einkäufe kommen markiert zurück, und stats.byMaterial schlüsselt die Zahlen pro Gruppe auf.

json
{
  "parts": [
    { "length": 2400, "quantity": 4, "material": "40x40 SHS" },
    { "length": 1300, "quantity": 6, "material": "Pine 90x45" }
  ],
  "stocks": [
    { "length": 6500, "quantity": null, "material": "40x40 SHS" },
    { "length": 5400, "quantity": null, "material": "Pine 90x45", "priority": true }
  ],
  "kerf": 3
}

priority: true für Rohmaterial oder Reststück ist eine bevorzugte Verwendung: Unter gleichwertigen Plänen werden priorisierte Posten zuerst verwendet. Die Planqualität wird dadurch niemals beeinträchtigt.

Winkel- / Gehrungsschnitte

Geben Sie Teilen leftAngle und rightAngle und aktivieren Sie angleCuts. Winkel sind die unteren Winkel des liegenden Teils; geben Sie einen oberen Winkel als negative Zahl ein (120 und -60 sind derselbe Schnitt). Gültiger Bereich nach dieser Umwandlung: 10 bis 170 Grad.

angleCuts fieldBeschreibung
enabledHauptschalter. Winkel bei Teilen werden nur akzeptiert, wenn auf true gesetzt.
widthMaterialbreite, gleiche Einheit wie Längen. Ein Winkelschnitt ragt seitlich um width / tan(angle) hinaus.
axialSymmetrynone, X, Y, Z oder XYZ: gibt an, welche Umdrehungen das Material erlaubt, was wiederum bestimmt, wie Gehrungen verschachtelt werden können. Vierkantrohr ist typischerweise XYZ; einseitiges Material ist none.

Wo zwei benachbarte Gehrungen parallel sind, erzeugt ein Sägeschnitt beide Kanten. Layouts im Winkelmodus fügen ein geordnetes parts-Array (mit der verwendeten Ausrichtung) und cutPositions hinzu: jeder Schnitt gemessen vom Nullpunkt der Stangenlänge, als [oben, unten]-Paar bei einem Winkelschnitt, oder als einzelne Zahl bei einem geraden Schnitt.

angle-mode layout (excerpt)
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300]

Fehler

Fehlermeldungen haben den Aufbau { "error": "...", "detail": "..." }. Das detail ist für einen Menschen geschrieben: es benennt das fehlerhafte Teil oder Material.

StatusFehlerWann
400validationFalsche Längen oder Mengen, unbekannte Methode, ein Teil, das auf kein Rohmaterial passt, Bedarf übersteigt endlichen Vorrat, Winkelregeln verletzt, stocks und stockLength beide gesendet.
401badKeyFehlender, unbekannter oder deaktivierter API-Schlüssel.
413partsLimitMehr Teile, als Ihr Tarif erlaubt.
429rateLimitedRaten- oder monatliches Limit erreicht. Retry-After beachten.
503busySolver ausgelastet. Bald erneut versuchen.

Ratenlimits

Limits drosseln; sie erzeugen niemals eine Nachzahlung. Anfragen, deren Validierung fehlschlägt, werden nicht auf Ihr monatliches Kontingent angerechnet. Alle Zahlen stehen auf der Seite mit den Plänen.

GuardKostenlosBezahl-Tarife
Anfragerate10 Anfragen / 5 Min. pro IP20-240 / Min. je nach Tarif
Teile gesamt300500 - 5,000
Einzelne Teileeinträge60bis zu 400
Rechenzeit20 s60 - 300 s

Die Obergrenze gilt für einzelne Teileeinträge wie auch für die Gesamtzahl der Teile, da die Rechenkosten von der Längenvielfalt abhängen: 300 identische Zuschnitte sind in Millisekunden optimiert, 300 verschiedene nicht.

Beispiele

Günstigste Mischung aus mehreren Stangenlängen (JavaScript)

javascript
const res = await fetch("https://api.linearcutting.com/v1/optimise", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "lk_live_9f2c...",
  },
  body: JSON.stringify({
    parts: [
      { length: 3000, quantity: 8 },
      { length: 1200, quantity: 10 },
    ],
    stocks: [
      { length: 6500, quantity: null, price: 45 },   // unlimited
      { length: 12000, quantity: 4, price: 70 },     // only four on hand
    ],
    offcuts: [{ length: 2600, quantity: 1 }],
    kerf: 4,
    minRemnant: 400,
    method: "balanced",   // prices present -> optimises dollars
  }),
});

const { plan, stats } = await res.json();
console.log(plan.purchases);     // what to buy
console.log(stats.totalCost);    // what it costs
console.log(plan.optimal);       // true = provably the cheapest

Rahmenteile mit Gehrungsschnitt (Python)

python
import requests

r = requests.post(
    "https://api.linearcutting.com/v1/optimise",
    json={
        "parts": [
            {"length": 1300, "quantity": 2, "leftAngle": 90, "rightAngle": 45},
            {"length": 1200, "quantity": 2, "leftAngle": 135, "rightAngle": 60},
        ],
        "stockLength": 3310,
        "kerf": 0,
        "angleCuts": {"enabled": True, "width": 100, "axialSymmetry": "none"},
    },
    timeout=30,
)
r.raise_for_status()

for combo in r.json()["plan"]["combinations"]:
    print(combo["count"], "x", combo["usedComb"]["cutPositions"])

Umgang mit großen, langsamen Aufträgen

Große Listen können Sekunden bis Minuten dauern. Die Suche ist jederzeit unterbrechbar, setzen Sie also timeLimit auf die Wartezeit Ihrer Benutzeroberfläche und zeigen Sie optimal an, um dem Benutzer mitzuteilen, ob die Lösung erwiesen oder nur die bisher beste ist.

json
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
  "kerf": 4, "timeLimit": 60 }