Documentação da API
O otimizador como um serviço HTTP: envie as peças e o stock, receba um plano de corte validado ou um PDF pronto a imprimir. Estável, versionado e com retrocompatibilidade: os campos são adicionados, nunca removidos.
Introdução
URL base: https://api.linearcutting.com. Tudo é JSON, à exceção do endpoint para PDF. Os pedidos são stateless: não armazenamos as suas listas de corte.
Não precisa de uma chave de API para experimentar. A linha abaixo executa, tal como está, agora mesmo. Cole-a num terminal.
curl -X POST https://api.linearcutting.com/v1/optimise \
-H "Content-Type: application/json" \
-d '{"parts": "2400x5, 1800x3", "stock": 6000}' Isto é uma lista de corte completa: cinco peças de 2400, três de 1800, cortadas de barras de 6000mm. Recebe de volta os layouts, o número de barras necessárias, o desperdício, e se o plano é comprovadamente ótimo.
As chamadas não autenticadas funcionam num nível público gratuito, para que possa avaliar o serviço antes de pagar: 30 pedidos por hora e 250 por mês, por endereço IP. A partir daí, devolve um erro 429 que informa quando o limite é reposto. As chamadas falhadas nunca contam para o limite. Quando estiver a construir algo a sério, obtenha uma chave a partir de 5 $ por mês: aumenta ambos os limites e os tamanhos máximos, e dá-lhe concorrência própria para que uma tarde movimentada no nível público não o atrase.
Clientes oficiais
Sem dependências, licença MIT e também não precisam de chave. Se estiver a usar Python ou JavaScript, não programe um fetch à mão:
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 no PyPI · JavaScript no npm · Código fonte Python · Código fonte JavaScript
Duas garantias com que pode contar. verified é sempre verdadeiro em caso de sucesso: cada plano é verificado com base nos seus dados de entrada antes de ser devolvido. Assim, garante que nunca recebe um plano que não corresponda à sua lista de corte. optimal é uma prova, não uma suposição, verdadeiro apenas quando a pesquisa estabelece que não existe um plano melhor, seja por esgotar as possibilidades ou por meio de um certificado. Quando não pode ser provado dentro do tempo disponível, indicamos false em vez de assumir.
optimal e stockBarsLowerBound são coisas diferentes, e divergem propositadamente. O limite inferior é uma aproximação, sensivelmente "comprimento total ÷ comprimento da barra", e muitas vezes não é exequível. Cinco peças de 2400 e três de 1800 são 17.400mm, o que equivale a 2.9 barras de 6000, portanto o limite indica 3. Na realidade, não pode cortar essas peças a partir de 3 barras; 4 é o verdadeiro mínimo, e o optimizador prova-o. Portanto, stockBarsUsed: 4 com stockBarsLowerBound: 3 e optimal: true não é uma contradição. Significa que o limite era uma aproximação grosseira, não que o plano estava errado. Use optimal para decidir se deve continuar a procurar; use o limite apenas como uma verificação de consistência.
Abreviatura
A forma longa é explícita e algo complexa. Para o caso comum, use a forma curta: um número é um comprimento, e x significa a quantidade:
{"parts": "2400x5, 1800x3, 600", "stock": 6000} Todos estes significam o mesmo, por isso use o que for mais adequado para o seu código:
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]} Um terceiro elemento é uma etiqueta numa peça, e um preço no material: [2400, 5, "rail"], [6000, 20, 45.50]. E stock é o nome amigável para a antiga divisão stockLength / stocks. Um número simples significa "este comprimento, compre tantos quantos forem necessários".
A forma curta é expandida antes de qualquer outra coisa acontecer, por isso é uma forma de escrever em vez de um modo diferente: um pedido curto e o pedido longo para o qual ele expande devolvem planos idênticos ao byte. Misture e combine livremente. Todos os pedidos existentes continuam a funcionar exatamente como antes.
Uma lista de corte num URL
Trabalhos pequenos cabem numa query string, pelo que GET também funciona. É útil para um link que queira partilhar, orçamentar ou incluir num relatório de erro:
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000" Mesma autenticação, mesmos limites, mesmo plano que o POST. Pode abri-lo num browser.
Autenticação
O plano público não precisa de chave. Os planos pagos enviam a chave num cabeçalho; as chaves também contêm as origens de browser permitidas, para que as chamadas do seu próprio site funcionem sem um proxy.
X-API-Key: lk_live_9f2c... Uma chave inválida ou expirada devolve 401. As chaves são emitidas por área de trabalho: ver planos.
POST /v1/optimise
Resolve uma lista de corte. Devolve o plano, estatísticas e limites comprováveis.
Corpo do pedido
| Field | Tipo | Padrão | Descrição |
|---|---|---|---|
parts obrigatório | array | - | As peças de que precisa. Cada uma: length (> 0), quantity (int ≥ 1), label opcional, material, leftAngle, rightAngle. |
stockLength | número | - | Um único comprimento de stock ilimitado. Use este ou stocks, não ambos. |
stocks | array | - | Comprimentos disponíveis para compra. Cada um: length, quantity (null = ilimitado), price (null = otimizar o material em vez do custo), material opcional, priority. |
offcuts | array | [] | Comprimentos que já possui: length, quantity, material opcional, priority. Material gratuito, nunca contabilizado como compra. |
kerf | número ≥ 0 | 0 | Largura da lâmina consumida por corte. |
trim | booleano | false | Retirar a espessura de um corte de ambas as extremidades de cada material de origem antes de cortar. |
method | string | balanced | balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). Os preços em stocks mudam o modo balanced para o de menor custo. |
minRemnant | número ≥ 0 | 0 | As sobras com este comprimento ou superior são devolvidas como usableRemnants em vez de desperdício. |
angleCuts | object | desligado | {enabled, width, axialSymmetry}. Ver cortes em ângulo. |
unit | string | "mm" | É devolvido e impresso nos PDFs. Não converte nenhum valor. |
unitFormat | string | decimal | O modo fraction formata as dimensões do PDF como a fração mais próxima de 1/64. |
timeLimit | segundos | automático | Tempo de cálculo, limitado pelo seu plano. A pesquisa pode ser interrompida a qualquer momento: mais tempo de cálculo apenas melhora o resultado. |
Resposta
{
"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 | Descrição |
|---|---|
plan.combinations[] | Uma entrada por cada layout distinto. usedComb.combination lista as peças cortadas dessa barra, length é o comprimento, source é a origem (stock ou offcut), e count é o número de barras a cortar dessa forma. |
usedComb.waste | total = origem − peças; dividido em saw (largura de corte + aparo) e remnant (a ponta que sobra). |
plan.purchases[] | A lista de compras: length, count, mais price/cost quando forneceu preços. |
plan.usableRemnants | Sobras ≥ minRemnant, das maiores para as menores: o material de retalhos para amanhã. |
plan.unusedOffcuts | Retalhos que o plano não precisou. |
plan.stockBarsUsed | Total de barras a comprar. |
plan.optimal | Prova. Verdadeiro apenas quando o plano atingiu um mínimo comprovável (ou a pesquisa foi exaustiva). Seguro para mostrar aos utilizadores como "o melhor possível". |
plan.verified | Sempre verdadeiro em caso de sucesso: pedido correspondido, cada layout cabe fisicamente, stock de retalhos respeitado. |
plan.stockBarsLowerBound, costLowerBound | Mínimos comprováveis. Quando optimal é falso, a diferença diz-lhe qual a margem de erro máxima deste plano. |
stats | Totais do trabalho: peças, comprimentos, yieldPercent, cutCount, layoutCount, totalCost, trueWaste, e byMaterial quando são usados grupos de materiais. |
POST /v1/optimise/pdf
Corpo do pedido igual, mais um jobName opcional. Devolve application/pdf: a pág. 1 é um resumo para o gestor de projeto (ordem de compra, custos, certificado de otimalidade); as páginas seguintes são folhas para o operador de serra com posições de corte contínuas, etiquetas de peças e uma caixa de verificação por barra. Chamadas com chave recebem a sua atribuição ou marca.
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" } Grupos de materiais
Etiquete parts, stocks e offcuts com material e cada material é resolvido como um trabalho separado dentro de um único pedido: os cortes são sempre feitos em material do mesmo tipo. Os esquemas de corte e as compras são devolvidos com etiquetas, e stats.byMaterial detalha os números por grupo.
{
"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 num material ou sobra é uma preferência não vinculativa: entre planos igualmente bons, os itens prioritários são usados primeiro. Nunca compromete a qualidade do plano.
Cortes em ângulo / esquadria
Atribua leftAngle e rightAngle às peças e ative angleCuts. Os ângulos são os ângulos inferiores da peça na sua posição de corte; insira um ângulo superior como um número negativo (120 e -60 são o mesmo corte). Intervalo válido após a transformação: 10 a 170 graus.
| angleCuts field | Descrição |
|---|---|
enabled | Interruptor geral. Ângulos nas peças só são aceites quando for 'true'. |
width | Largura do material, na mesma unidade que os comprimentos. Um corte em ângulo desvia-se lateralmente em width / tan(angle). |
axialSymmetry | none, X, Y, Z ou XYZ: que rotações o material permite, o que decide como as esquadrias se podem encaixar. O tubo quadrado é tipicamente XYZ; material com um só lado é none. |
Quando duas esquadrias adjacentes são paralelas, um só corte de serra faz ambas as arestas. Os esquemas de corte em modo de ângulo adicionam uma matriz ordenada parts (com a orientação utilizada) e cutPositions: cada corte medido a partir do início da barra, como um par [topo, base] se for em ângulo, ou um único número se for a direito.
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300] Erros
Os corpos dos erros são { "error": "...", "detail": "..." }. O detail é escrito para ser mostrado a uma pessoa: nomeia a peça ou o material em questão.
| Status | erro | Quando |
|---|---|---|
| 400 | validation | Comprimentos ou quantidades incorretos, método desconhecido, uma peça que não cabe em nenhum material de origem, pedido excede o material finito, regras de ângulo violadas, stocks e stockLength enviados em simultâneo. |
| 401 | badKey | Chave de API em falta, desconhecida ou desativada. |
| 413 | partsLimit | Mais peças do que o seu nível permite. |
| 429 | rateLimited | Limite de pedidos ou limite mensal atingido. Respeite o Retry-After. |
| 503 | busy | Otimizador no limite da capacidade. Tente novamente em breve. |
Limites de pedidos
Os limites restringem; nunca geram uma fatura surpresa. Os pedidos que falham a validação não contam para o seu limite mensal. Os números completos estão na página de planos.
| Guard | Gratuito | Níveis pagos |
|---|---|---|
| Taxa | 10 pedidos / 5 min por IP | 20-240 / min por plano |
| Total de peças | 300 | 500 - 5,000 |
| Linhas de peças distintas | 60 | até 400 |
| Orçamento de resolução | 20 s | 60 - 300 s |
O número de linhas de peças distintas é limitado, assim como o total de peças, porque o custo da resolução depende da diversidade de comprimentos: 300 cortes idênticos são otimizados em milissegundos, 300 cortes diferentes não.
Exemplos
Combinação mais barata de vários comprimentos de stock (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 Peças de caixilho em esquadria (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"]) Lidar com um trabalho grande e lento
Listas grandes podem demorar segundos ou minutos. A procura é contínua, por isso defina o timeLimit para o que a sua UI consegue esperar e mostre optimal para indicar aos utilizadores se a solução é final ou apenas a melhor encontrada até ao momento.
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
"kerf": 4, "timeLimit": 60 }