@cutoptim/engine 1.0.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -402,6 +402,10 @@ export interface Part2D {
402
402
  meta?: Record<string, unknown>;
403
403
  /** The row's id, so a `matchedSets` layout can name it. Any JSON value is accepted here. Only an id a set REFERS TO is checked: it must be a non-empty string of printable characters, at most 64 long, or a safe integer (`7` and `"7"` are the same id), and it must be on exactly one part. Every other id is ignored, whatever it holds (a float, an object, null, repeated, long, empty): always without a non-empty `matchedSets`, and on the rows no set names with one — so an ERP article number is safe to send. */
404
404
  id?: unknown;
405
+ /** QR payload template for every copy of this row: returned resolved in `labels[].qr` and printed as a QR code (error correction level M) on each of the row's labels in `labelsPdf`. Exactly two tokens are filled in: `{piece}` (the copy, 1-based — the `k` of the `k/n` printed on the label) and `{of}` (the row's `qty`); any other `{…}` stays as sent. At most 300 UTF-8 bytes once filled in, printable characters only (no control characters); not together with `qrs`. A wrong value is a 400 `invalid_request` naming the field, before any quota is used. Metadata only: it never changes the plan or `planHash`. On a small label format a long payload can print below 0.4 mm per module, and `warnings` then says so. 2D only. */
406
+ qr?: string;
407
+ /** One QR payload per copy of this row, used verbatim: copy `k` (the `k/n` printed on its label) gets entry `k`, in `labels[].qr` and on `labelsPdf`. Exactly `qty` entries (`qty` defaults to 1), each 1 to 300 UTF-8 bytes of printable characters; not together with `qr`. A wrong value is a 400 `invalid_request` naming the field, before any quota is used. 2D only. */
408
+ qrs?: Array<string>;
405
409
  }
406
410
  /** Parts cut out of ONE piece of board, side by side or one above the other, exactly one kerf apart, so the figure of the veneer runs on across them (a drawer front over two doors). The set travels through the optimizer as one rectangle — the block — and comes back as its members. Every refusal is a 400 before the quota is reserved; a size mismatch is a warning (`matchedSets[].warnings`), not an error. At most 24 members per set; a part can be in one set only. Engines `heuristic` and `balanced` (which does not rank `priority`, so under `respectStock` a set can lose its place there); `max` refuses a request with sets (400 `details.unsupported`). */
407
411
  export interface MatchedSet {
@@ -477,6 +481,44 @@ export interface Options2D {
477
481
  effort?: "fast" | "balanced";
478
482
  /** A boolean. With `matchedSets` and the `svg` export: draw the dashed frame and the "SET A" badge of every set instance, in ONE separate group (`<g id="matched-sets" data-role="annotation">`) a laser or CNC import can drop in one move. `false` leaves that group out (the position codes in the part labels stay). Read only in a request with sets — there a value other than `true`, `false` or `null` is a 400; without sets the field is ignored, whatever it holds. */
479
483
  setOutlines?: boolean;
484
+ labels?: LabelOptions;
485
+ }
486
+ /** Label settings — the same settings as the Label format panel of the CutOptim app, under these names. They shape `labelsPdf` and add `piece`, `of`, `sheetNo`, `stockRow` and `sheetMeta` to every `labels[]` entry. Every field is optional; `{}` is valid and only adds those fields. Absent: the label PDF is the A4 sheet and `labels[]` is exactly as without this object. Validated STRICTLY, unlike the rest of `options`: an unknown key, a wrong type or a value out of range is a 400 `invalid_request` with `details.path`, before any call is counted (also on `/v1/validate/2d`). `null` for a field means the field is absent. Sent without `include` `labels` or `labelsPdf`, it changes nothing and the answer says so in `warnings`. 2D only. */
487
+ export interface LabelOptions {
488
+ /** `sheet` = the A4 sheet of labels (3 columns); `roll` = one label per page, `width` × `height` mm, for a label printer; `apli-03056` = APLI 03056 sheets (24 labels of 64.6 × 33.9 mm on A4, 3 × 8); `brother-dk-11201` = Brother DK-11201 roll labels (29 × 90 mm, one per page). */
489
+ format?: "sheet" | "roll" | "apli-03056" | "brother-dk-11201";
490
+ /** Label width in mm, `roll` only. */
491
+ width?: number;
492
+ /** Label height in mm, `roll` only. */
493
+ height?: number;
494
+ offset?: LabelOffset;
495
+ /** Size of the dimension text on the label. */
496
+ dimSize?: "normal" | "large" | "xl";
497
+ /** Print the part number, top left and largest: the 1-based row of the FIRST `parts` row with that name (rows sharing a name share a number; with unique names it equals `labels[].partNo`). */
498
+ partNo?: boolean;
499
+ /** `input` = labels in `parts` order; `cut` = sheet by sheet, in the order the saw plan frees the pieces. */
500
+ order?: "input" | "cut";
501
+ /** Work stages printed as a row of circles to tick off by pen; empty or absent = no row. Each entry is a built-in key — `rc` (rough cut), `c2s` (cut to size), `eb` (edge banded, printed only on a banded part), `s` (sanded), `d` (drilled, not printed yet) — or free text of at most 6 characters, printed as sent (keys are lower case: `RC` is free text). Spaces are trimmed; printable characters only. */
502
+ stages?: Array<string>;
503
+ /** Your own word for a built-in stage key, at most 6 characters; empty or absent = the English abbreviation (RC, C2S, EB, S, D). */
504
+ stageWords?: {
505
+ rc?: string;
506
+ c2s?: string;
507
+ eb?: string;
508
+ s?: string;
509
+ d?: string;
510
+ };
511
+ /** Mark FE on the edges of a piece that lie on a sheet edge with no `trim` on that side (the board's factory edge). */
512
+ factoryEdges?: boolean;
513
+ /** Print the "hidden side" mark. */
514
+ hiddenFaceHint?: boolean;
515
+ /** Print only the labels of these part numbers (the numbers `partNo` prints), e.g. to re-print after sanding. Empty or absent = all. */
516
+ parts?: Array<number>;
517
+ }
518
+ /** Printer offset in mm: moves everything printed on the label right (`x`) and down (`y`); negative values move it back. Applied rounded to 0.1 mm. */
519
+ export interface LabelOffset {
520
+ x?: number;
521
+ y?: number;
480
522
  }
481
523
  export interface Optimize2DRequest {
482
524
  parts: Array<Part2D>;
@@ -503,6 +545,36 @@ export interface Part1D {
503
545
  material?: string;
504
546
  /** Opaque 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. */
505
547
  meta?: Record<string, unknown>;
548
+ ends?: MitreEnds;
549
+ /** Mitre pairing only: which 180° turns of the part on the bar are allowed, so its angled end can meet a neighbour's. `none` (default — a profiled moulding or one-sided stock), `face` (end for end, the crown or top edge stays up — roof members), `edge` (end for end over the edge — flat stock, frames), `any`. */
550
+ flip?: "none" | "face" | "edge" | "any";
551
+ /** Mitre pairing only: the side of the bar the `angle` of `ends` is seen on. Required with `ends` (a 1D part has no cross-section to take it from). 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. */
552
+ depth?: number;
553
+ /** Mitre pairing only: the other side, the one the `bevel` is seen on. Required with `ends`. 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. */
554
+ thickness?: number;
555
+ }
556
+ /** One end cut of a part, in degrees from square (0 = a square end), signed. The part is measured long point to long point (`length` is its overall length). A part whose two end cuts would cross inside its section (shorter than its leads) cannot be made: a 400 `invalid_request` naming `parts[i].ends` says how long it must at least be. */
557
+ export interface EndCut {
558
+ /** The face angle: the mitre or plumb cut as seen on the `depth` side (wood: on `sh` as entered). */
559
+ angle: number;
560
+ /** The edge angle: the bevel as seen on the `thickness` side (wood: on `sw` as entered). A compound cut has both. */
561
+ bevel?: number;
562
+ }
563
+ /** The angled ends of a part, read only with `options.mitrePairing`. Two parts of one section whose facing ends lie in one plane (after a turn `flip` allows) can be cut with ONE saw cut between them: the triangle between the long and the short point is not wasted. Without `mitrePairing` the ends are not used (a warning says so) and the part packs as its `length`. */
564
+ export interface MitreEnds {
565
+ start?: EndCut;
566
+ end?: EndCut;
567
+ }
568
+ /** This part's far end is ONE saw cut shared with the next part on the bar. The next part starts at `pos + length − lead + kerfAlongAxis` computed from the rounded values shown (not one kerf after `pos + length`), and the shared wood is counted once in `yieldPct`. */
569
+ export interface SharedCut {
570
+ /** Face angle of the cut in degrees, as the part lies on the bar. */
571
+ angle: number;
572
+ /** Edge angle of the cut in degrees, as the part lies on the bar (0 = none). */
573
+ bevel: number;
574
+ /** The kerf measured along the bar: an angled blade removes more length than its width. 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. */
575
+ kerfAlongAxis: number;
576
+ /** Distance along the bar between the cut's long point and short point. 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. */
577
+ lead: number;
506
578
  }
507
579
  export interface Stock1D {
508
580
  /** Bar length. 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. */
@@ -538,6 +610,8 @@ export interface Options1D {
538
610
  respectStock?: boolean;
539
611
  /** Output filter: report a leftover only if it is at least this long. */
540
612
  minOffcut?: number;
613
+ /** Pair parts of one section whose facing ends (`parts[].ends`) can share ONE saw cut. Off by default: a request without it is solved exactly as before. On, a plan with pairs is returned only when it ranks strictly better than the unpaired plan by the packer's own ranking (parts placed first, then material — or price with `minimizeCost`); otherwise the unpaired plan comes back. Pairs only, no chains of three or more. */
614
+ mitrePairing?: boolean;
541
615
  }
542
616
  export interface Optimize1DRequest {
543
617
  parts: Array<Part1D>;
@@ -566,6 +640,10 @@ export interface PartWood {
566
640
  material?: string;
567
641
  /** Opaque 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. */
568
642
  meta?: Record<string, unknown>;
643
+ /** Angled ends (mitre pairing). The `angle` is seen on `sh` AS ENTERED and the `bevel` on `sw`, so send the section the way the angles were measured. */
644
+ ends?: MitreEnds;
645
+ /** Mitre pairing only: which 180° turns of the part on the bar are allowed, so its angled end can meet a neighbour's. `none` (default — a profiled moulding or one-sided stock), `face` (end for end, the crown or top edge stays up — roof members), `edge` (end for end over the edge — flat stock, frames), `any`. */
646
+ flip?: "none" | "face" | "edge" | "any";
569
647
  }
570
648
  export interface StockWood {
571
649
  /** Optional stock label, echoed back as `sections[].stockName` so a plan can name the material an operator has to fetch. */
@@ -600,6 +678,8 @@ export interface OptionsWood {
600
678
  respectStock?: boolean;
601
679
  /** Output filter: report a leftover only if it is at least this long. */
602
680
  minOffcut?: number;
681
+ /** Pair parts of one section whose facing ends (`parts[].ends`) can share ONE saw cut. Off by default: a request without it is solved exactly as before. On, a plan with pairs is returned only when it ranks strictly better than the unpaired plan by the packer's own ranking (parts placed first, then material — or price with `minimizeCost`); otherwise the unpaired plan comes back. Pairs only, no chains of three or more. */
682
+ mitrePairing?: boolean;
603
683
  }
604
684
  export interface OptimizeWoodRequest {
605
685
  parts: Array<PartWood>;
@@ -625,6 +705,10 @@ export interface CutStep {
625
705
  length: number;
626
706
  /** 1-based machine pass. Starts at 1 and increments ONLY when the axis flips relative to the parent cut, so ripping a board into six strips is one stage and crosscutting them is the next. This is the panel-saw sense of "3-stage cutting", not tree depth, and it is what `options.maxCutStages` constrains. */
627
707
  stage: number;
708
+ /** 1D/wood with `options.mitrePairing` only: the face angle in degrees of an ANGLED cut, as the part lies on the bar — a shared cut (between a part with `sharedCut` and the next one) or an end of a part with `ends` that no neighbour shares. A part's angled start, or a square start right after an angled end, is a saw pass of its own: a HEAD step at that part's `pos`, before its far step (so `metrics.cuts` can exceed `placed`). A far step's `pos` is the long point of the part it frees (`pos + length`). Absent on every square cut. */
709
+ angle?: number;
710
+ /** Comes with `angle`: the edge angle of that cut in degrees (0 = none). */
711
+ bevel?: number;
628
712
  }
629
713
  /** A part on a board. `x`/`y` is its top-left corner measured from the top-left corner of the sheet, `x` growing right and `y` growing downward. */
630
714
  export interface PlacedPart2D {
@@ -793,6 +877,18 @@ export interface Label2D {
793
877
  };
794
878
  /** Opaque 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. */
795
879
  meta?: Record<string, unknown>;
880
+ /** Which copy of its `parts` row this piece is, 1-based, counted in plan order (sheet, then placement). Present only when the request sends `options.labels`. */
881
+ piece?: number;
882
+ /** The `qty` of its `parts` row (the label prints `piece/of`). Present only when the request sends `options.labels`. */
883
+ of?: number;
884
+ /** The sheet number printed on the label: `sheet` + 1. Present only when the request sends `options.labels`. */
885
+ sheetNo?: number;
886
+ /** Index into the request `stock` array of the row this sheet was cut from. Present only when the request sends `options.labels`. */
887
+ stockRow?: number;
888
+ /** The `meta` of that stock row, verbatim (the same as `sheets[sheet].meta`). Present only when the request sends `options.labels` and the stock row has `meta`. */
889
+ sheetMeta?: Record<string, unknown>;
890
+ /** The resolved QR payload of this copy (`parts[].qr` with `{piece}` / `{of}` filled in, or its `parts[].qrs` entry) — the text the code on `labelsPdf` encodes. Present only when its `parts` row sends `qr` or `qrs`. */
891
+ qr?: string;
796
892
  }
797
893
  /** Linear metres of edge banding the order consumes, grouped by the caller-supplied type reference and reported per part AND as an order total. Present ONLY when at least one part requested banding, so a banding-free response omits the key. Exact geometry, no waste margin — the workshop adds its own. ⚠️ The metres ASSUME MILLIMETRE input: top and bottom edges run the part width, left and right run the height, multiplied by qty, divided by 1000. This is the one response figure named for a unit (edge banding is bought by the metre, and the division only means metres in millimetres); in another unit the raw edge length is w/h × qty and these numbers are that divided by 1000. 2D only. */
798
894
  export interface EdgeBanding {
@@ -877,6 +973,9 @@ export interface PlacedPart1D {
877
973
  length: number;
878
974
  /** Opaque 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. */
879
975
  meta?: Record<string, unknown>;
976
+ sharedCut?: SharedCut;
977
+ /** Mitre pairing only: the part lies turned on the bar so its end meets its neighbour's — `face` end for end (crown kept up), `edge` end for end over the edge, `roll` rolled about its axis. Absent = as entered. */
978
+ flipped?: "face" | "edge" | "roll";
880
979
  }
881
980
  export interface Rod {
882
981
  /** FULL bar length as supplied in `stock`. */
@@ -909,6 +1008,8 @@ export interface Metrics1D {
909
1008
  totalPrice: number;
910
1009
  /** How many pieces fitted only because `options.tolerance` allowed an overshoot. */
911
1010
  toleranceAcceptedCount: number;
1011
+ /** Present only with `options.mitrePairing`: how many cuts in the plan are shared by two parts (`sharedCut`). */
1012
+ sharedCuts?: number;
912
1013
  }
913
1014
  export interface Unplaced1D {
914
1015
  /** The part name. */
@@ -1028,6 +1129,8 @@ export interface MetricsWood {
1028
1129
  totalPrice: number;
1029
1130
  /** How many pieces fitted only because `options.tolerance` allowed an overshoot. */
1030
1131
  toleranceAcceptedCount: number;
1132
+ /** Present only with `options.mitrePairing`: how many cuts in the plan are shared by two parts (`sharedCut`). */
1133
+ sharedCuts?: number;
1031
1134
  }
1032
1135
  export interface OptimizeWoodResponse {
1033
1136
  engine: "heuristic" | "balanced";
package/dist/types.gen.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // GENERATED by scripts/gen-sdks.ts from the CutOptim Engine API OpenAPI document — DO NOT EDIT.
2
- // contract 1 · spec sha256 d12a371f3242e7b21df0aa4ecf2df4b2f60f8670bbed7e47bbe3d4cbf08678fb · @cutoptim/engine 1.0.0
2
+ // contract 1 · spec sha256 57abd7b68ca9e86fe7040dd2e979516274a2bdea5394258959f21bbefc28f624 · @cutoptim/engine 1.5.0
3
3
  export {};
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.0.0";
1
+ export declare const VERSION = "1.5.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // The package version — rewritten by scripts/gen-sdks.ts together with package.json (sent as the User-Agent).
2
- export const VERSION = '1.0.0';
2
+ export const VERSION = '1.5.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cutoptim/engine",
3
- "version": "1.0.0",
3
+ "version": "1.5.0",
4
4
  "description": "Official client for the CutOptim Engine API — cutting optimization (2D panels, 1D linear, timber, true-shape nesting) over HTTPS.",
5
5
  "license": "MIT",
6
6
  "author": "CutOptim <support@cutoptim.com>",