API-documentatie
De optimizer als HTTP-service: stuur zaagstukken en stafmateriaal, ontvang een geverifieerd zaagplan of een printklare PDF. Stabiel, geversioneerd en achterwaarts compatibel: velden worden toegevoegd, nooit verwijderd.
Inleiding
Basis-URL https://api.linearcutting.com. Alles is JSON, behalve het PDF-eindpunt. Requests zijn stateless: we slaan uw zaaglijsten niet op.
U heeft geen API-sleutel nodig om dit te proberen. De onderstaande regel code werkt, zoals hij er staat. Plak hem in een terminal.
curl -X POST https://api.linearcutting.com/v1/optimise \
-H "Content-Type: application/json" \
-d '{"parts": "2400x5, 1800x3", "stock": 6000}' Dat is een volledige zaaglijst: vijf delen van 2400, drie van 1800, te zagen uit staven van 6000mm. Je krijgt de zaagplannen terug, hoeveel staven er nodig zijn, hoeveel er wordt verspild en of het plan bewijsbaar optimaal is.
Aanroepen zonder sleutel draaien op een gratis publiek niveau. Zo kunt u de software testen voordat u betaalt: 30 aanvragen per uur en 250 per kalendermaand, per IP-adres. Daarna geeft de API een 429-fout terug met de reset-tijd. Mislukte aanroepen tellen niet mee. Voor serieus werk, koop een sleutel vanaf $5 per maand: dit verhoogt beide limieten, verruimt de maximale invoergrootte en geeft u uw eigen capaciteit zodat een drukke middag op het publieke niveau u niet vertraagt.
Officiële clients
Geen afhankelijkheden, MIT-licentie, en ze hebben ook geen sleutel nodig. Als je Python of JavaScript gebruikt, schrijf dan niet zelf een fetch:
import linearcutting as lc
plan = lc.optimise(parts="2400x5, 1800x3", stock=6000)
plan.bars_used # 4
plan.optimal # True, proved, not guessed import { optimise } from "linearcutting";
const plan = await optimise({ parts: "2400x5, 1800x3", stock: 6000 });
plan.barsUsed; // 4
plan.optimal; // true Python op PyPI · JavaScript op npm · Python broncode · JavaScript broncode
Twee garanties waarop u kunt bouwen. verified is altijd waar bij succes: elk zaagplan wordt opnieuw gecontroleerd met uw invoer voordat het wordt teruggestuurd. Een plan dat uw zaaglijst niet kan realiseren, bereikt u dus nooit. optimal is een bewijs, geen gok. Het is alleen waar als de zoektocht heeft vastgesteld dat er geen beter plan bestaat, ofwel door alle mogelijkheden uit te putten ofwel via een certificaat. Als dit niet binnen de tijd kan worden bewezen, is de waarde false. Geen giswerk.
optimal en stockBarsLowerBound zijn verschillende dingen, en ze spreken elkaar met opzet tegen. De ondergrens is een relaxatie, ongeveer "totale lengte ÷ staaflengte", en is vaak niet haalbaar. Vijf stuks van 2400 en drie van 1800 is 17.400 mm. Dat is 2,9 staven van 6000, dus de ondergrens is 3. U kunt die delen niet uit 3 staven zagen; 4 is het werkelijke minimum, en de solver bewijst dat. Dus stockBarsUsed: 4 met stockBarsLowerBound: 3 en optimal: true is geen tegenstrijdigheid. Het betekent dat de ondergrens te ruim was, niet het plan. Gebruik optimal om te beslissen of u verder wilt zoeken. Gebruik de ondergrens alleen als realiteitscheck.
Verkorte notatie
De lange vorm is expliciet en wat omslachtig. Voor de meest gangbare gevallen is er een kortere notatie. Een getal is een lengte, en x betekent het aantal:
{"parts": "2400x5, 1800x3, 600", "stock": 6000} Al deze betekenen hetzelfde, dus gebruik de notatie die het best bij uw code past:
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]} Een derde element is een label voor een deel, en een prijs voor een handelslengte: [2400, 5, "rail"], [6000, 20, 45.50]. En stock is de nieuwe naam voor de oude scheiding stockLength / stocks. Een enkel getal betekent "deze lengte, koop er zoveel van als nodig".
De korte notatie wordt eerst uitgebreid, voordat er iets anders gebeurt. Het is dus een manier van typen, geen andere modus. Een kort verzoek en het lange verzoek waarnaar het wordt uitgebreid, leveren byte-identieke plannen op. Gebruik beide notaties door elkaar; elk bestaand verzoek werkt nog precies zoals voorheen.
Een zaaglijst in een URL
Kleine klussen passen in een query string, dus GET werkt ook. Handig voor een link die u wilt delen, citeren of in een bugrapport plakken:
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000" Zelfde authenticatie, zelfde limieten, zelfde plan als de POST. U kunt de link gewoon in een browser openen.
Authenticatie
Het gratis pakket heeft geen sleutel nodig. Betaalde pakketten sturen hun sleutel in een header; sleutels bevatten ook de toegestane browser-origins, zodat aanroepen vanaf uw eigen site zonder proxy werken.
X-API-Key: lk_live_9f2c... Een foute of vervallen sleutel geeft 401 terug. Sleutels worden per werkruimte uitgegeven: zie pakketten.
POST /v1/optimise
Optimaliseer een zaaglijst. Geeft het zaagplan, statistieken en bewijsbare grenzen terug.
Aanvraag body
| Field | Type | Standaard | Beschrijving |
|---|---|---|---|
parts verplicht | array | - | De delen die u nodig heeft. Elk: length (> 0), quantity (geheel getal ≥ 1), optioneel label, material, leftAngle, rightAngle. |
stockLength | getal | - | Een enkele, onbeperkte handelslengte. Gebruik deze of stocks, niet beide. |
stocks | array | - | Beschikbare handelslengtes. Per stuk: length, quantity (null = onbeperkt), price (null = optimaliseer op materiaal in plaats van op kosten), optioneel material, priority. |
offcuts | array | [] | Reststukken in eigen voorraad: length, quantity, optioneel material, priority. Gratis materiaal, telt nooit als aankoop. |
kerf | getal ≥ 0 | 0 | Zaagbreedte die per zaagsnede verloren gaat. |
trim | boolean | false | Snij aan beide kanten van elk stuk stafmateriaal een zaagbreedte af voor het verzagen. |
method | string | balanced | balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). Prijzen op stocks zetten balanced om naar laagste kosten. |
minRemnant | getal ≥ 0 | 0 | Reststukken gelijk aan of langer dan deze lengte worden geretourneerd als usableRemnants in plaats van afval. |
angleCuts | object | uit | {enabled, width, axialSymmetry}. Zie verstekzagen. |
unit | string | "mm" | Wordt teruggegeven en op PDF's afgedrukt. Rekent niets om. |
unitFormat | string | decimal | fraction geeft PDF-afmetingen weer als breuken, afgerond op 1/64. |
timeLimit | seconden | auto | Rekenbudget, beperkt door abonnement. De zoekopdracht is 'anytime': een groter budget verbetert alleen het resultaat. |
Respons
{
"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" }
} | Field | Omschrijving |
|---|---|
plan.combinations[] | Eén item per uniek zaagplan. usedComb.combination geeft de onderdelen weer die van die staf gezaagd zijn, length is de bronlengte, source is stock of offcut, en count is het aantal staven dat op die manier gezaagd wordt. |
usedComb.waste | total = bron − onderdelen; opgesplitst in saw (zaagsnede + afkortverlies) en remnant (het overblijvende einde). |
plan.purchases[] | De boodschappenlijst: length, count, plus price/cost als je prijzen hebt opgegeven. |
plan.usableRemnants | Reststukken ≥ minRemnant, de grootste eerst: de voorraad reststukken voor morgen. |
plan.unusedOffcuts | Reststukken die het plan niet nodig had. |
plan.stockBarsUsed | Totaal aan te schaffen staven. |
plan.optimal | Bewijs. Alleen 'true' als het plan een bewijsbare ondergrens heeft bereikt (of de zoekopdracht volledig is voltooid). Veilig om aan gebruikers te tonen als "best mogelijk". |
plan.verified | Altijd 'true' bij succes: vraag voldaan, elk zaagplan past fysiek, voorraad reststukken gerespecteerd. |
plan.stockBarsLowerBound, costLowerBound | Bewijsbare minima. Als optimal 'false' is, geeft het verschil aan hoeveel dit plan er maximaal naast kan zitten. |
stats | Totalen voor de opdracht: onderdelen, lengtes, yieldPercent, cutCount, layoutCount, totalCost, trueWaste, en byMaterial als materiaalgroepen worden gebruikt. |
POST /v1/optimise/pdf
Dezelfde request body, plus een optionele jobName. Retourneert application/pdf: pagina 1 is een overzicht voor de projectmanager (inkooporder, kosten, het optimaliteitscertificaat); de volgende pagina's zijn werkbladen voor de zager met doorlopende zaagposities, onderdeelstickers en een afvinkvakje per staf. Bij aanroepen met een sleutel wordt de huisstijl van de klant weergegeven.
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
{ "status": "ok", "version": "1.3.0", "solver": "Linear Cutlist Optimizer v2" } Materiaalgroepen
Tag parts, stocks en offcuts met materiaal en elk materiaal wordt als een aparte taak binnen één aanvraag opgelost: zaagsnedes worden enkel toegepast op materiaal van hetzelfde type. Layouts en inkopen komen getagd terug, en stats.byMaterial splitst de cijfers per groep uit.
{
"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 op een handelslengte of reststuk is een zachte voorkeur: bij gelijkwaardige plannen worden geprioriteerde items eerst gebruikt. Dit gaat nooit ten koste van de kwaliteit van het plan.
Hoek- / versteksnedes
Geef delen een leftAngle en rightAngle en schakel angleCuts in. Hoeken zijn de onderste hoeken van het stuk zoals het ligt; voer een bovenhoek in als een negatief getal (120 en -60 zijn dezelfde snede). Geldig bereik na die omzetting: 10 tot 170 graden.
| angleCuts field | Omschrijving |
|---|---|
enabled | Hoofdschakelaar. Hoeken op delen worden alleen geaccepteerd indien true. |
width | Materiaalbreedte, in dezelfde eenheid als de lengtes. Een hoeksnede neemt zijdelings width / tan(angle) in beslag. |
axialSymmetry | none, X, Y, Z of XYZ: welke rotaties het materiaal toestaat, wat bepaalt hoe verstekken genest kunnen worden. Vierkante koker is doorgaans XYZ; enkelzijdig materiaal is none. |
Waar twee aangrenzende verstekken parallel lopen, maakt één zaagsnede beide kanten. Layouts in hoekmodus voegen een geordende parts-array toe (met de gebruikte oriëntatie) en cutPositions: elke snede gemeten vanaf het nulpunt van de staaf, als een [boven, onder]-paar bij een hoeksnede, of een enkel getal bij een haakse snede.
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300] Fouten
Foutmeldingen hebben de structuur { "error": "...", "detail": "..." }. De detail is bedoeld voor een menselijke lezer: het benoemt het foute deel of materiaal.
| Status | Fout | Wanneer |
|---|---|---|
| 400 | validation | Foute lengtes of aantallen, onbekende methode, een deel dat op geen enkele bron past, vraag overschrijdt eindige voorraad, hoekregels geschonden, stocks en stockLength beide verstuurd. |
| 401 | badKey | Ontbrekende, onbekende of uitgeschakelde API-sleutel. |
| 413 | partsLimit | Meer delen dan je abonnement toestaat. |
| 429 | rateLimited | Snelheids- of maandlimiet bereikt. Respecteer Retry-After. |
| 503 | busy | Solver op volle capaciteit. Probeer zo opnieuw. |
Snelheidslimieten
Limieten beperken de toegang, ze genereren nooit een naheffing. Aanvragen die niet door de validatie komen, tellen niet mee voor je maandlimiet. Alle details staan op de abonnementenpagina.
| Guard | Gratis | Betaalde abonnementen |
|---|---|---|
| Limiet | 10 aanvragen / 5 min per IP | 20-240 / min per pakket |
| Totaal aantal delen | 300 | 500 - 5,000 |
| Aantal unieke delen | 60 | tot 400 |
| Rekenbudget | 20 s | 60 - 300 s |
Het aantal unieke regels met delen is begrensd, net als het totaal aantal delen. De rekenkosten hangen namelijk af van de diversiteit aan lengtes: 300 identieke delen zijn in milliseconden geoptimaliseerd, 300 verschillende niet.
Voorbeelden
Goedkoopste mix uit meerdere handelslengtes (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 Frame-onderdelen in verstek (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"]) Omgaan met een trage, grote klus
Grote lijsten duren seconden tot minuten. Het zoekproces kan op elk moment stoppen, dus stel timeLimit in op wat uw UI kan afwachten en toon optimal om gebruikers te laten weten of het resultaat bewezen is, of gewoon het beste tot nu toe.
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
"kerf": 4, "timeLimit": 60 }