Documentazione API
L'ottimizzatore come servizio HTTP: invia tagli e materiale, ricevi un piano di taglio verificato o un PDF pronto per la stampa. Stabile, versionato e retrocompatibile: i campi vengono solo aggiunti, mai rimossi.
Introduzione
URL di base https://api.linearcutting.com. È tutto JSON, tranne l'endpoint PDF. Le richieste sono stateless: non memorizziamo le tue liste di taglio.
Non serve una chiave API per provare. La riga qui sotto funziona così com'è, subito. Incollala in un terminale.
curl -X POST https://api.linearcutting.com/v1/optimise \
-H "Content-Type: application/json" \
-d '{"parts": "2400x5, 1800x3", "stock": 6000}' Questa è una lista di taglio completa: cinque pezzi da 2400, tre da 1800, tagliati da barre da 6000mm. In risposta ottieni gli schemi di taglio, quante barre servono, quanto materiale viene sprecato, e se il piano è dimostrabilmente ottimo.
Le chiamate non autenticate utilizzano un piano pubblico gratuito, così puoi valutare il servizio prima di pagare: 30 richieste all'ora e 250 al mese per indirizzo IP. Superato questo limite, il sistema restituisce un errore 429 che indica quando si resetta. Le chiamate fallite non vengono conteggiate. Per progetti di produzione, acquista una chiave API a partire da 5 $ al mese: rimuove entrambi i limiti, aumenta le dimensioni massime e ti dà una concorrenza dedicata, così un pomeriggio intenso sul piano pubblico non può rallentarti.
Client ufficiali
Nessuna dipendenza, licenza MIT, e non richiedono neanche una chiave. Se usi Python o JavaScript, non creare a mano una richiesta 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 su PyPI · JavaScript su npm · Sorgente Python · Sorgente JavaScript
Due garanzie su cui puoi contare. verified è sempre vero in caso di successo: ogni piano viene riverificato confrontandolo con i dati da te inseriti prima di essere restituito, così che un piano che non taglia effettivamente la tua lista non possa mai arrivarti. optimal è una prova, non una stima. È vero solo quando la ricerca ha stabilito che non esiste un piano migliore, o perché ha esaurito le possibilità o tramite un certificato. Quando non può essere provato entro il tempo limite, restituiamo false invece di tirare a indovinare.
optimal e stockBarsLowerBound sono due cose diverse, e non concordano di proposito. Il limite inferiore è una semplificazione, circa "lunghezza totale ÷ lunghezza barra", e spesso non è raggiungibile. Cinque pezzi da 2400 e tre da 1800 fanno 17.400 mm, che equivalgono a 2,9 barre da 6000, quindi il limite è 3. In pratica non puoi tagliare quei pezzi da 3 barre; 4 è il vero minimo, e il solutore lo dimostra. Quindi stockBarsUsed: 4 con stockBarsLowerBound: 3 e optimal: true non è una contraddizione. Significa che il limite era approssimativo, non che il piano era sbagliato. Usa optimal per decidere se continuare la ricerca; usa il limite solo come verifica di massima.
Sintassi abbreviata
Il formato esteso è esplicito e un po' verboso. Per i casi più comuni, usa il formato breve: un numero indica una lunghezza e x la quantità:
{"parts": "2400x5, 1800x3, 600", "stock": 6000} Ognuno di questi esempi significa la stessa cosa, quindi usa quello che preferisci per il tuo codice:
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]} Un terzo elemento è un'etichetta per un pezzo e un prezzo per il materiale: [2400, 5, "rail"], [6000, 20, 45.50]. stock è il nome più comodo per la vecchia suddivisione stockLength / stocks. Un numero semplice significa "questa lunghezza, compra la quantità che ti serve".
Il formato breve viene espanso prima di ogni altra operazione, quindi è un modo di inserire i dati, non una modalità diversa: una richiesta breve e la richiesta estesa in cui viene espansa restituiscono piani identici al byte. Puoi usare entrambi i formati liberamente. Ogni richiesta esistente funziona esattamente come prima.
Una lista di taglio in un URL
I lavori piccoli entrano in una stringa di query, quindi funziona anche GET. Utile per un link da condividere, per un preventivo o da inserire in una segnalazione di bug:
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000" Stessa autenticazione, stessi limiti, stesso piano di taglio del POST. Puoi anche aprirlo in un browser.
Autenticazione
Il livello pubblico non richiede una chiave. I livelli a pagamento inviano la chiave in un header; le chiavi contengono anche le origini browser consentite, quindi le chiamate dal tuo sito funzionano senza proxy.
X-API-Key: lk_live_9f2c... Una chiave errata o scaduta restituisce 401. Le chiavi sono emesse per ogni area di lavoro: vedi i piani.
POST /v1/optimise
Calcola una lista di taglio. Restituisce il piano, le statistiche e i limiti dimostrabili.
Corpo della richiesta
| Field | Tipo | Predefinito | Descrizione |
|---|---|---|---|
parts richiesto | array | - | I pezzi da tagliare. Ciascuno: length (> 0), quantity (int ≥ 1), opzionali label, material, leftAngle, rightAngle. |
stockLength | numero | - | Una singola lunghezza di materiale illimitata. Usa questo o stocks, non entrambi. |
stocks | array | - | Barre acquistabili. Ognuna: length, quantity (null = illimitata), price (null = ottimizza il materiale invece del costo), material opzionale, priority. |
offcuts | array | [] | Barre già a magazzino: length, quantity, material opzionale, priority. Materiale a costo zero, mai conteggiato come acquisto. |
kerf | numero ≥ 0 | 0 | Larghezza della lama consumata per ogni taglio. |
trim | booleano | false | Esegue un taglio di intestatura su entrambe le estremità di ogni barra prima di iniziare. |
method | string | balanced | balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). I prezzi sulle stocks cambiano balanced in modalità minor costo. |
minRemnant | numero ≥ 0 | 0 | I pezzi rimanenti di questa lunghezza o superiori vengono restituiti come usableRemnants invece che come scarto. |
angleCuts | object | disattivato | {enabled, width, axialSymmetry}. Vedi tagli angolari. |
unit | string | "mm" | Riportato nella risposta e stampato sui PDF. Non converte nessuna unità di misura. |
unitFormat | string | decimal | fraction formatta le dimensioni nel PDF come frazioni al 1/64 più vicino. |
timeLimit | secondi | automatico | Tempo di calcolo, limitato dal piano. La ricerca è di tipo 'anytime': più tempo a disposizione migliora solo il risultato. |
Risposta
{
"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 | Descrizione |
|---|---|
plan.combinations[] | Una voce per ogni schema di taglio distinto. usedComb.combination elenca i pezzi tagliati da quella barra, length è la fonte, source è stock o offcut, e count indica quante barre tagliare in quel modo. |
usedComb.waste | total = fonte − pezzi; suddiviso in saw (spessore lama + rifilo) e remnant (l'eccedenza finale). |
plan.purchases[] | La lista della spesa: length, count, più price/cost se hai fornito i prezzi. |
plan.usableRemnants | Eccedenze ≥ minRemnant, dalla più grande alla più piccola: il magazzino sfridi di domani. |
plan.unusedOffcuts | Sfridi che il piano non ha richiesto. |
plan.stockBarsUsed | Barre totali da acquistare. |
plan.optimal | Prova. Vero solo quando il piano ha raggiunto un minimo dimostrabile (o la ricerca è stata completata in modo esaustivo). Si può mostrare agli utenti come "miglior risultato possibile". |
plan.verified | Sempre vero in caso di successo: domanda soddisfatta, ogni schema di taglio è fisicamente possibile, scorta di sfridi rispettata. |
plan.stockBarsLowerBound, costLowerBound | Minimi dimostrabili. Quando optimal è falso, il divario indica di quanto il piano potrebbe al massimo discostarsi. |
stats | Totali del lavoro: pezzi, lunghezze, yieldPercent, cutCount, layoutCount, totalCost, trueWaste, e byMaterial quando si usano i gruppi di materiali. |
POST /v1/optimise/pdf
Stesso corpo della richiesta, più un jobName opzionale. Restituisce application/pdf: pagina 1 è un riepilogo per il capoprogetto (ordine d'acquisto, costi, certificato di ottimalità); le pagine seguenti sono schede per l'operatore alla sega con posizioni di taglio progressive, etichette dei pezzi e una casella di spunta per ogni barra. Per i chiamanti con chiave, vengono renderizzati l'attribuzione o il marchio.
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" } Gruppi di materiali
Contrassegna parts, stocks e offcuts con material e ogni materiale viene risolto come un lavoro a sé stante all'interno di una singola richiesta: i tagli vengono eseguiti solo su stock dello stesso materiale. I layout e gli acquisti vengono restituiti con il tag e stats.byMaterial suddivide i numeri per gruppo.
{
"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 su uno stock o uno spezzone è una preferenza non vincolante: tra piani di pari qualità, gli elementi prioritari vengono usati per primi. Non compromette mai la qualità del piano.
Tagli angolati / obliqui
Assegna ai pezzi un leftAngle e un rightAngle e abilita angleCuts. Gli angoli sono quelli inferiori del pezzo nella sua posizione di taglio; inserisci un angolo superiore come numero negativo (120 e -60 sono lo stesso taglio). L'intervallo valido dopo la trasformazione: da 10 a 170 gradi.
| angleCuts field | Descrizione |
|---|---|
enabled | Interruttore generale. Gli angoli sui pezzi sono accettati solo se impostato su true. |
width | Larghezza del materiale, stessa unità delle lunghezze. Un taglio angolato si estende lateralmente di width / tan(angle). |
axialSymmetry | none, X, Y, Z o XYZ: definisce quali rotazioni del materiale sono consentite, il che determina come i tagli obliqui possono essere annidati. Il tubolare quadro è tipicamente XYZ; il materiale con un solo lato lavorabile è none. |
Quando due tagli obliqui adiacenti sono paralleli, un unico taglio di sega crea entrambi i bordi. I layout in modalità angolata aggiungono un array ordinato parts (con l'orientamento usato) e cutPositions: ogni taglio misurato dall'estremità zero della barra, come coppia [superiore, inferiore] se angolato, o come numero singolo se dritto.
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300] Errori
Il corpo degli errori è { "error": "...", "detail": "..." }. Il campo detail è scritto per essere mostrato a un operatore umano: nomina il pezzo o il materiale che causa il problema.
| Status | errore | Quando |
|---|---|---|
| 400 | validation | Lunghezze o quantità errate, metodo sconosciuto, un pezzo che non entra in nessun materiale di partenza, richiesta che supera lo stock finito, regole degli angoli violate, invio contemporaneo di stocks e stockLength. |
| 401 | badKey | Chiave API mancante, sconosciuta o disabilitata. |
| 413 | partsLimit | Più pezzi di quanto consentito dal tuo piano. |
| 429 | rateLimited | Limite di richieste o cap mensile raggiunto. Rispetta l'header Retry-After. |
| 503 | busy | Solutore al massimo della capacità. Riprova a breve. |
Limiti di richieste
I limiti bloccano; non generano mai un addebito per l'eccesso. Le richieste con convalida fallita non contano per il cap mensile. I numeri completi sono sulla pagina dei piani.
| Guard | Gratuito | Piani a pagamento |
|---|---|---|
| Frequenza | 10 richieste / 5 min per IP | 20-240 / min per livello |
| Pezzi totali | 300 | 500 - 5,000 |
| Linee pezzo distinte | 60 | fino a 400 |
| Budget di calcolo | 20 s | 60 - 300 s |
Le linee pezzo distinte sono limitate così come i pezzi totali, perché il costo del calcolo deriva dalla diversità delle lunghezze: 300 tagli identici si ottimizzano in millisecondi, 300 diversi no.
Esempi
Combinazione più economica da diverse lunghezze di materiale (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 Pezzi per telai con tagli inclinati (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"]) Gestire un lavoro grande e lento
Le liste lunghe possono richiedere da secondi a minuti. La ricerca è progressiva, quindi imposta timeLimit al tempo massimo che la tua interfaccia può attendere e mostra optimal per indicare se il risultato è dimostrato ottimo o solo il migliore trovato finora.
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
"kerf": 4, "timeLimit": 60 }