Repository navigation
Expand file tree
/
Copy pathschema.json
More file actions
553 lines (553 loc) · 30.8 KB
/
Copy pathschema.json
File metadata and controls
553 lines (553 loc) · 30.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"oneOf": [
{"$ref": "#/definitions/project"},
{"$ref": "#/definitions/design"},
{"$ref": "#/definitions/inventory"},
{"$ref": "#/definitions/tool-limits-doc"},
{"$ref": "#/definitions/cut-spec"}
],
"definitions": {
"project": {
"type": "object",
"description": "A given project with a fixed amount of purchased materials in hand and its pattern defined by components and panels.",
"required": ["name", "components", "panels", "boards"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Short name of this project."
},
"description": {
"type": "string",
"description": "Longer human description of what this project builds."
},
"finishedThickness": {
"description": "Final thickness of the finished board after flattening.",
"$ref": "#/definitions/measurement"
},
"finishedWidth": {
"description": "Final width of the finished board after trimming. The target size; edge trims, when declared, must sum to grid width minus this value, but need not be equal.",
"$ref": "#/definitions/measurement"
},
"finishedLength": {
"description": "Final length of the finished board after trimming. The target size; edge trims, when declared, must sum to grid height minus this value, but need not be equal.",
"$ref": "#/definitions/measurement"
},
"edgeTrims": {
"description": "Optional cut length off each of the four finished-face edges, in grid order: left and right are the two width edges, top and bottom are the two length edges. Each entry is optional; a missing edge is untrimmed (0). Finished width is grid width minus left minus right; finished length is grid height minus top minus bottom.",
"type": "object",
"additionalProperties": false,
"properties": {
"left": { "$ref": "#/definitions/measurement" },
"right": { "$ref": "#/definitions/measurement" },
"top": { "$ref": "#/definitions/measurement" },
"bottom": { "$ref": "#/definitions/measurement" }
}
},
"sliceDepth": {
"description": "How thick each slice is cut from a panel (the crosscut pitch along the panel's length). This dimension becomes the finished board's thickness before flattening, so it must be at least finishedThickness plus the flattening allowance. A panel's length is derived as the total number of slices times (sliceDepth + miter kerf), plus any snipe allowance.",
"$ref": "#/definitions/measurement"
},
"sliceGrid": {
"type": "array",
"description": "Reconstructs one finished board's face from the slices produced by the panels. A 2D grid laid out from the top-left corner: an array of rows (top to bottom), each row an array of slice placements (left to right).",
"items": {
"type": "array",
"items": { "$ref": "#/definitions/slice-grid-item" }
}
},
"batch": {
"type": "integer",
"description": "How many finished pieces this project produces."
},
"components": {
"type": "array",
"description": "The stripe vocabulary: every unique component this project's panels are built from.",
"items": { "$ref": "#/definitions/component" }
},
"panels": {
"type": "array",
"description": "The glue-up panels, each an ordered list of components cut to length.",
"items": { "$ref": "#/definitions/panel" }
},
"panelSpecs": {
"type": "array",
"description": "Optional. The chosen set of physical panels to build, described independently of how their component pieces are cut (no board/strip/cut references). Each entry fixes a panel type and the length of each of its profile regions; the solver resolves these into actual cuts and panel codes. When present, the solver builds exactly these panels; when absent, the solver chooses an optimal set that packs into the available boards.",
"items": { "$ref": "#/definitions/panel-spec" }
},
"boards": {
"type": "array",
"description": "The actual inventory boards in hand for this project.",
"items": { "$ref": "#/definitions/board" }
}
}
},
"design": {
"type": "object",
"description": "A pattern only: the design of a board, with no inventory and no chosen panels. Panels are derived from the grid demand (see bundle.derive_panel_specs); boards come from an inventory.",
"required": ["name", "components", "panels"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Short name of this design."
},
"description": {
"type": "string",
"description": "Longer human description of what this design builds."
},
"finishedThickness": {
"description": "Final thickness of the finished board after flattening.",
"$ref": "#/definitions/measurement"
},
"finishedWidth": {
"description": "Final width of the finished board after trimming. The target size; edge trims, when declared, must sum to grid width minus this value, but need not be equal.",
"$ref": "#/definitions/measurement"
},
"finishedLength": {
"description": "Final length of the finished board after trimming. The target size; edge trims, when declared, must sum to grid height minus this value, but need not be equal.",
"$ref": "#/definitions/measurement"
},
"edgeTrims": {
"description": "Optional cut length off each of the four finished-face edges, in grid order: left and right are the two width edges, top and bottom are the two length edges. Each entry is optional; a missing edge is untrimmed (0). Finished width is grid width minus left minus right; finished length is grid height minus top minus bottom.",
"type": "object",
"additionalProperties": false,
"properties": {
"left": { "$ref": "#/definitions/measurement" },
"right": { "$ref": "#/definitions/measurement" },
"top": { "$ref": "#/definitions/measurement" },
"bottom": { "$ref": "#/definitions/measurement" }
}
},
"sliceDepth": {
"description": "How thick each slice is cut from a panel (the crosscut pitch along the panel's length).",
"$ref": "#/definitions/measurement"
},
"sliceGrid": {
"type": "array",
"description": "Reconstructs one finished board's face from the slices produced by the panels.",
"items": {
"type": "array",
"items": { "$ref": "#/definitions/slice-grid-item" }
}
},
"batch": {
"type": "integer",
"description": "How many finished pieces this design produces."
},
"components": {
"type": "array",
"description": "The stripe vocabulary: every unique component this design's panels are built from.",
"items": { "$ref": "#/definitions/component" }
},
"panels": {
"type": "array",
"description": "The glue-up panels, each an ordered list of components cut to length.",
"items": { "$ref": "#/definitions/panel" }
}
}
},
"inventory": {
"type": "object",
"description": "A reusable pile of boards, referenced by name. The same inventory is usable by more than one design.",
"required": ["name", "boards"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Inventory name, used to select it by name (e.g. actual)."
},
"description": {
"type": "string",
"description": "Where this pile came from."
},
"boards": {
"type": "array",
"description": "The boards in this pile.",
"items": { "$ref": "#/definitions/board" }
}
}
},
"panel-spec": {
"type": "object",
"description": "One physical panel to build, defined by its type and its per-profile region lengths — with no reference to how its component pieces are cut from boards. The solver turns a panel spec into concrete rips/crosscuts and a panel code.",
"required": ["panel", "profileLengths"],
"additionalProperties": false,
"properties": {
"panel": {
"$ref": "#/definitions/id-reference",
"description": "Id of the panels[] entry this spec builds. The panel's component sequence, profiles, thickness and sliceStrips come from that definition."
},
"profileLengths": {
"type": "array",
"minItems": 1,
"description": "The length of each profile region, in the panel's profile order (fullest region first). Exactly one entry for a single-profile panel; one entry per profile otherwise. Each entry is that region's own extent (not cumulative); the physical panel length is the sum of the region lengths. A region's length divided by the slice pitch gives how many slices of that profile this panel yields.",
"items": { "$ref": "#/definitions/measurement" }
},
"count": {
"type": "integer",
"minimum": 1,
"description": "How many identical physical panels of this exact spec to build. Omitted means 1. Two panels of the same type with different profileLengths are separate specs."
}
}
},
"component": {
"type": "object",
"description": "A single stripe type used in the panels, at its final dimensions.",
"required": ["id", "species", "width", "thickness"],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/definitions/id",
"description": "Unique identifier for this component, referenced by panel entries."
},
"species": { "$ref": "#/definitions/species" },
"width": {
"description": "Final stripe width.",
"$ref": "#/definitions/measurement"
},
"thickness": {
"description": "Final stripe thickness.",
"$ref": "#/definitions/measurement"
}
}
},
"panel": {
"type": "object",
"description": "A glue-up panel: an ordered sequence of components, sliced crosswise into end-grain slices. A panel either yields a single set of slices (slices) or several profile regions (profiles) where trailing components drop out.",
"required": ["id", "components"],
"additionalProperties": false,
"oneOf": [
{ "required": ["slices"] },
{ "required": ["profiles"] }
],
"properties": {
"id": {
"$ref": "#/definitions/id",
"description": "Unique identifier for this panel. A slice is addressed as {panel-id} for a single-profile panel, or {panel-id}-{profile-id} when the panel has profiles."
},
"count": {
"type": "integer",
"description": "How many of this ideal panel the finished design needs (per finished board; multiplied by batch). Omitted means 1. The ideal panel is the perfect-world glue-up whose length is derived from its slice demand; a cut-spec may realize it as several shorter actual panels.",
"minimum": 1
},
"thickness": {
"description": "The thickness this panel is planed to before slicing. Each slice stands at this height. If sliceStrips is present, each slice is then ripped into equal strips whose width becomes the installed row height instead.",
"$ref": "#/definitions/measurement"
},
"sliceStrips": {
"type": "object",
"description": "Optional. If present, every slice cut from this panel (across all profiles) is ripped into equal strips, and the panel's entire output is these strips. The rip runs along the slice's length (the direction the stripes run), dividing the slice's height (the panel thickness) into equal strips stacked height-wise. Each strip's final width becomes its installed row height in the finished board face.",
"required": ["count", "width"],
"additionalProperties": false,
"properties": {
"count": {
"type": "integer",
"description": "How many equal strips each slice is ripped into. count x width plus kerfs equals the panel thickness.",
"minimum": 2
},
"width": {
"description": "Final width of each ripped strip (its installed row height in the finished board face).",
"$ref": "#/definitions/measurement"
}
}
},
"slices": {
"type": "integer",
"description": "Number of slices for a single-profile panel. Mutually exclusive with profiles.",
"minimum": 1
},
"profiles": {
"type": "array",
"description": "Ordered profile regions, from the one containing the most components to the fewest. Every component is present in the first profile (components are aligned on one squared edge); trailing components drop out in later profiles. Mutually exclusive with slices.",
"minItems": 2,
"items": { "$ref": "#/definitions/profile" }
},
"components": {
"type": "array",
"description": "The components glued side by side to form the panel, in order from the squared edge.",
"minItems": 1,
"items": { "$ref": "#/definitions/panel-component" }
}
}
},
"slice-grid-item": {
"type": "object",
"description": "One slice placed in the finished-board grid.",
"required": ["sliceId"],
"additionalProperties": false,
"properties": {
"sliceId": {
"$ref": "#/definitions/id-reference",
"description": "Which slice goes here: the panel id for a single-profile panel, or panel-id-profile-id (e.g. S-L, H-R) for a panel with profiles. When the referenced panel has sliceStrips, the placed slice is one of those equal ripped strips (they are interchangeable, so no further addressing is needed)."
},
"flip": {
"type": "string",
"description": "Orientation mutation applied to the slice relative to its preferred orientation. Only a horizontal flip (H) affects the drawing with the current specs (it reverses the component sequence for the mirrored half). Omitted means no flip. Vertical/both flips may be re-added if panel edge angles are introduced.",
"enum": ["H"]
}
}
},
"profile": {
"type": "object",
"description": "One profile region of a panel: a run of slices sharing the same component makeup.",
"required": ["id", "slices"],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/definitions/id",
"description": "Identifier for this profile, used to address its slices as {panel-id}-{profile-id}."
},
"slices": {
"type": "integer",
"description": "Number of slices produced from this profile region.",
"minimum": 1
}
}
},
"panel-component": {
"type": "object",
"description": "One component placed in a panel.",
"required": ["type"],
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/id",
"description": "Id of the component (from the project's components list) used at this position."
},
"maxProfile": {
"type": "integer",
"description": "How many leading profiles this component appears in (counting from the first). Omitted means it appears in all profiles. Used only on panels that declare profiles.",
"minimum": 1
}
}
},
"cut-spec": {
"type": "object",
"description": "Solver output: a minimal, non-redundant specification of how to cut each board. Anything derivable from the project, tool-limits, and pattern is intentionally omitted.",
"required": ["boards"],
"additionalProperties": false,
"properties": {
"boards": {
"type": "array",
"description": "Every board that is produced and used. Each board has exactly one origin: ripped from a project board (source), the width-remainder of another cut-spec board (sourceRemainder), or a bought board (buySpecies).",
"items": { "$ref": "#/definitions/board-cut-spec" }
},
"design": {
"type": "string",
"description": "Design this cut-spec was made for (metadata; absent on legacy cut-specs)."
},
"inventory": {
"type": "string",
"description": "Inventory this cut-spec was made from (metadata; absent on legacy cut-specs)."
},
"kind": {
"type": "string",
"enum": ["hand", "solver"],
"description": "How this cut-spec was made: hand-crafted, or solver-generated."
},
"date": {
"type": "string",
"description": "Date this cut-spec was written, YYYY-MM-DD."
},
"panels": {
"type": "array",
"description": "Optional. The ACTUAL panels this plan builds, each described by a panel code. A panel code is '{panelId}:{ref};{ref};...' where each ref is '{boardId}/{stripIndex}/{cutIndex}' addressing one produced component piece (board id from boards[], then the 0-based strip index within that board and cut index within that strip). The referenced pieces are glued side by side in order, aligned on the top edge; the panel's length and profile regions are derived from their lengths (a shorter trailing piece ends the fuller profile region). The resolved component sequence must match the ideal panel's sequence. One code = one physical panel; repeat the panel id across codes for multiple actual panels.",
"items": {
"type": "string",
"pattern": "^[A-Za-z\\d]+:[A-Za-z\\d]+(-[A-Za-z\\d]+)?/\\d+/\\d+(;[A-Za-z\\d]+(-[A-Za-z\\d]+)?/\\d+/\\d+)*$"
}
}
}
},
"board-cut-spec": {
"type": "object",
"description": "One physical board in the cutting plan. Its id is generated from its origin (e.g. BM1-A for the first board cut from source BM1, or GH-C for a bought Cherry board). A board whose length equals its source length is the whole source board; a shorter length means it was crosscut off the source, reducing that source's remaining length by this length plus one kerf. Boards sharing a source are consumed in order.",
"required": ["id", "length", "strips"],
"additionalProperties": false,
"oneOf": [
{ "required": ["source"] },
{ "required": ["sourceRemainder"] },
{ "required": ["buySpecies"] }
],
"properties": {
"id": {
"$ref": "#/definitions/id-reference",
"description": "Generated id for this board. Sourced boards use the source id plus a sequential letter suffix (BM1-A, BM1-B). Bought boards use GH- plus the species initial (GH-C)."
},
"source": {
"$ref": "#/definitions/id-reference",
"description": "Id of the project.yaml inventory board this board is ripped or crosscut from. Mutually exclusive with sourceRemainder and buySpecies."
},
"sourceRemainder": {
"$ref": "#/definitions/id-reference",
"description": "Id of another cut-spec board whose leftover width-slab (the material left after that board's rips and its mandatory unfinished edge) this board consumes. Mutually exclusive with source and buySpecies."
},
"buySpecies": {
"description": "Species of a board that must be bought because it is not drawn from inventory (a ghost board). Mutually exclusive with source and sourceRemainder.",
"$ref": "#/definitions/species"
},
"length": {
"description": "Length of this board. Equal to the source length means the whole board; less means it was crosscut off the source.",
"$ref": "#/definitions/measurement"
},
"strips": {
"type": "array",
"description": "Rips taken off this board across its width, in order. A board always has at least one strip because every board has one unfinished edge that must be cut.",
"minItems": 1,
"items": { "$ref": "#/definitions/strip" }
}
}
},
"strip": {
"type": "object",
"description": "A single rip off a board: a strip of the given width, optionally crosscut into smaller pieces.",
"required": ["width"],
"additionalProperties": false,
"properties": {
"width": {
"description": "The ripped width of this strip.",
"$ref": "#/definitions/measurement"
},
"cuts": {
"type": "array",
"description": "Crosscuts dividing this strip into smaller pieces, in order along its length. Omitted or empty means the whole strip is used as a single piece. When present, there is one entry per component produced from the strip.",
"items": { "$ref": "#/definitions/measurement" }
},
"offcutComponent": {
"type": "boolean",
"description": "If true, the leftover piece after the listed cuts (or the whole strip, when cuts is empty) is itself a component rather than waste."
}
}
},
"tool-limits-doc": {
"type": "object",
"description": "Top-level document describing the static limits of the available toolset.",
"required": ["limits"],
"additionalProperties": false,
"properties": {
"limits": { "$ref": "#/definitions/tool-limits" }
}
},
"tool-limits": {
"type": "object",
"description": "Statically-known limits of the available toolset.",
"additionalProperties": false,
"properties": {
"maxCutLength": {
"description": "Kreg ACS track saw on-table rip/crosscut capacity.",
"$ref": "#/definitions/measurement"
},
"sawKerf": {
"description": "Kerf removed per cut by the ACS track saw blade.",
"$ref": "#/definitions/measurement"
},
"miterKerf": {
"description": "Kerf removed per cut by the DeWalt miter saw blade.",
"$ref": "#/definitions/measurement"
},
"maxPlaneWidth": {
"description": "DeWalt DW733 Type 2 planer maximum stock width.",
"$ref": "#/definitions/measurement"
},
"minPlaneLength": {
"description": "DeWalt DW733 Type 2 planer minimum feed length.",
"$ref": "#/definitions/measurement"
},
"minPlaneThickness": {
"description": "DeWalt DW733 Type 2 planer minimum planing height.",
"$ref": "#/definitions/measurement"
},
"minPlaneLoss": {
"description": "DeWalt DW733 Type 2 planer recommended material removed from stock thickness in a single pass. Used as the per-strip face-cleanup loss when deriving a cut board's loss and margin bands.",
"$ref": "#/definitions/measurement"
},
"maxMiterCrosscut": {
"description": "DeWalt DWS779 maximum clean crosscut width at 90 degrees.",
"$ref": "#/definitions/measurement"
},
"miterAngleRange": {
"description": "DeWalt DWS779 miter angle range in degrees; negative = left, positive = right.",
"$ref": "#/definitions/degree-range"
},
"sawAngleRange": {
"description": "Kreg ACS track saw bevel range in degrees.",
"$ref": "#/definitions/degree-range"
},
"maxCutDepth": {
"description": "Kreg ACS track saw maximum plunge (cut depth) at 90 degrees.",
"$ref": "#/definitions/measurement"
},
"snipeTail": {
"description": "Extra length added to a panel beyond its slices to absorb planer snipe (the less-accurate leading/trailing few inches). Added once per panel when deriving panel length.",
"$ref": "#/definitions/measurement"
}
}
},
"degree-range": {
"type": "array",
"description": "An inclusive range of integer degrees, given as [min, max].",
"minItems": 2,
"maxItems": 2,
"items": { "type": "integer" }
},
"board": {
"type": "object",
"description": "An actual inventory board in hand.",
"required": ["id", "species", "length", "width", "thickness"],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/definitions/id",
"description": "Unique identifier for this inventory board, referenced by cut-spec boards as their source."
},
"species": { "$ref": "#/definitions/species" },
"length": {
"description": "The board's length.",
"$ref": "#/definitions/measurement"
},
"width": {
"description": "The board's width.",
"$ref": "#/definitions/measurement"
},
"thickness": {
"description": "The board's thickness.",
"$ref": "#/definitions/measurement"
}
}
},
"species": {
"type": "string",
"description": "A board species.",
"enum": ["Brown Maple", "Walnut", "Cherry"]
},
"id": {
"type": "string",
"description": "An identifier. Strictly alphanumeric so ids can be joined with hyphens to address composed things like slices.",
"pattern": "^[A-Za-z\\d]+$"
},
"id-reference": {
"type": "string",
"description": "A reference to an id or a composed id: an alphanumeric id followed by up to one optional hyphenated alphanumeric suffix (e.g. S, S-L, H-R).",
"pattern": "^[A-Za-z\\d]+(-[A-Za-z\\d]+)?$"
},
"measurement": {
"type": "array",
"description": "A measurement tuple of integer inches with additional sixteenths (rounded to nearest sixteenth of an inch). [92, 4] represents 92 1/4\".",
"minItems": 2,
"maxItems": 2,
"items": [
{
"type": "integer",
"description": "The number of inches, as an integer."
},
{
"type": "integer",
"description": "Any remainder of the measurement, given as an integer number of sixteenths, after rounding to the nearest value (actual measurement must only be plus or minus 1/32 inch, if not exact)."
}
]
}
}
}