@hatiolab/figure-model 0.1.80 → 0.1.82
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.
- package/CHANGELOG.md +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/v3-authoring-actions.d.ts +17 -0
- package/dist/v3-authoring-actions.d.ts.map +1 -1
- package/dist/v3-authoring-actions.js +49 -6
- package/dist/v3-authoring-actions.js.map +1 -1
- package/dist/v3-create.d.ts +7 -8
- package/dist/v3-create.d.ts.map +1 -1
- package/dist/v3-create.js +3 -6
- package/dist/v3-create.js.map +1 -1
- package/dist/v3-gate.js +1 -1
- package/dist/v3-graph.d.ts.map +1 -1
- package/dist/v3-graph.js +21 -20
- package/dist/v3-graph.js.map +1 -1
- package/dist/v3-kernel-version.d.ts +1 -1
- package/dist/v3-kernel-version.js +1 -1
- package/dist/v3-proposal.d.ts +1 -1
- package/dist/v3-proposal.d.ts.map +1 -1
- package/dist/v3-proposal.js +8 -2
- package/dist/v3-proposal.js.map +1 -1
- package/dist/v3-recipe.d.ts +58 -0
- package/dist/v3-recipe.d.ts.map +1 -0
- package/dist/v3-recipe.js +194 -0
- package/dist/v3-recipe.js.map +1 -0
- package/docs/v3-dryroom-profile-validation.md +2 -0
- package/package.json +4 -2
- package/recipes/hinged-door.json +46 -0
- package/recipes/roller-row.json +19 -0
- package/recipes/sliding-door.json +69 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type V3ProposalStep } from './v3-proposal.ts';
|
|
2
|
+
import type { V3Asset } from './v3-asset-types.ts';
|
|
3
|
+
export type V3RecipeParamKind = 'face' | 'ratio' | 'mm' | 'choice' | 'flag';
|
|
4
|
+
export type V3RecipeValue = number | string | boolean;
|
|
5
|
+
export interface V3RecipeParam {
|
|
6
|
+
name: string;
|
|
7
|
+
kind: V3RecipeParamKind;
|
|
8
|
+
default: V3RecipeValue;
|
|
9
|
+
/** `ratio` and `mm`: the values it takes. */
|
|
10
|
+
min?: number;
|
|
11
|
+
max?: number;
|
|
12
|
+
/** `face` and `choice`: the values it takes. */
|
|
13
|
+
options?: V3RecipeValue[];
|
|
14
|
+
label?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
export interface V3Recipe {
|
|
17
|
+
name: string;
|
|
18
|
+
title: Record<string, string>;
|
|
19
|
+
description?: Record<string, string>;
|
|
20
|
+
/** `part`: inserted onto a part the person has chosen (`{target}`); `figure`: onto the figure as a whole. */
|
|
21
|
+
target: 'part' | 'figure';
|
|
22
|
+
params: V3RecipeParam[];
|
|
23
|
+
/** Proposal steps, each with an optional `when`. */
|
|
24
|
+
steps: Array<Record<string, unknown>>;
|
|
25
|
+
}
|
|
26
|
+
/** A recipe as stored, checked for its own form: its parameters, its steps' `when`, its placeholders' names. */
|
|
27
|
+
export declare function parseV3Recipe(value: unknown): V3Recipe;
|
|
28
|
+
/** The name a recipe's parts take in this figure: the recipe's name and the first number no part or input uses. */
|
|
29
|
+
export declare function v3RecipeIdFor(asset: V3Asset, recipe: Pick<V3Recipe, 'name'>): string;
|
|
30
|
+
/**
|
|
31
|
+
* The proposal a recipe makes with these values: the defaults filled in, the values checked (ADR-0095 condition 3),
|
|
32
|
+
* the steps whose `when` does not hold left out, and the placeholders and linear forms resolved. A string that is only
|
|
33
|
+
* `{<param>}` takes the value itself (a number stays a number); inside a longer string it is written in.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveV3Recipe(recipe: V3Recipe, use: {
|
|
36
|
+
id: string;
|
|
37
|
+
target?: string;
|
|
38
|
+
values?: Record<string, V3RecipeValue>;
|
|
39
|
+
}): V3ProposalStep[];
|
|
40
|
+
/** A recipe put into a figure: resolved, then applied as one proposal (ADR-0094). The parts it made are named `id`. */
|
|
41
|
+
export declare function applyV3Recipe(asset: V3Asset, recipe: V3Recipe, use?: {
|
|
42
|
+
target?: string;
|
|
43
|
+
values?: Record<string, V3RecipeValue>;
|
|
44
|
+
}): {
|
|
45
|
+
asset: V3Asset;
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
/** The room a recipe is tried in when it is saved: a 4 x 3 x 6 m box on the floor that follows the figure's size. */
|
|
49
|
+
export declare function v3RecipeTestRoom(): {
|
|
50
|
+
asset: V3Asset;
|
|
51
|
+
target: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* A recipe's test (ADR-0095): parsed, put into the test room with its defaults, and the result, with its room declared,
|
|
55
|
+
* passing the release gate. Throws the kernel's refusal, or `RECIPE_GATE` with the gate's findings.
|
|
56
|
+
*/
|
|
57
|
+
export declare function checkV3Recipe(value: unknown): V3Recipe;
|
|
58
|
+
//# sourceMappingURL=v3-recipe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-recipe.d.ts","sourceRoot":"","sources":["../src/v3-recipe.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAIvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAA;AAC3E,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,iBAAiB,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;IACtB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,OAAO,CAAC,EAAE,aAAa,EAAE,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,6GAA6G;IAC7G,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;IACzB,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,oDAAoD;IACpD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACtC;AAUD,gHAAgH;AAChH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAoCtD;AA8BD,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAMpF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAE,GAAG,cAAc,EAAE,CAgChJ;AAED,uHAAuH;AACvH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAKrK;AAED,qHAAqH;AACrH,wBAAgB,gBAAgB,IAAI;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAOtD"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A recipe: an ADR-0094 command list with parameters (ADR-0095).
|
|
3
|
+
*
|
|
4
|
+
* A door, a roller row or a hinge is the commands a person or the AI already gives, with a few numbers and choices left
|
|
5
|
+
* open. The cases are data, not code: a recipe adds to the proposal grammar only three placeholders (`{id}`, `{target}`,
|
|
6
|
+
* `{<param>}`), a number as a one-parameter linear form (`{ param, times?, plus? }`) and one condition per step
|
|
7
|
+
* (`when: { param, is }`). There is no expression language. Applied, it is resolved into a proposal and handed to
|
|
8
|
+
* `applyV3Proposal`: the first step the kernel refuses stops it, with the kernel's own sentence. Inserted, it is ordinary
|
|
9
|
+
* parts -- nothing ties them back to the recipe.
|
|
10
|
+
*/
|
|
11
|
+
import { V3ContractError } from "./v3-contract.js";
|
|
12
|
+
import { applyV3Proposal } from "./v3-proposal.js";
|
|
13
|
+
import { addV3Box, createV3Asset } from "./v3-create.js";
|
|
14
|
+
import { applyV3Authoring } from "./v3-authoring-actions.js";
|
|
15
|
+
import { inspectV3Asset } from "./v3-gate.js";
|
|
16
|
+
const KINDS = ['face', 'ratio', 'mm', 'choice', 'flag'];
|
|
17
|
+
const NAME = /^[a-z][a-z0-9-]*$/;
|
|
18
|
+
const fail = (path, message) => {
|
|
19
|
+
throw new V3ContractError('RECIPE_SCHEMA', path, message);
|
|
20
|
+
};
|
|
21
|
+
const isObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
22
|
+
/** A recipe as stored, checked for its own form: its parameters, its steps' `when`, its placeholders' names. */
|
|
23
|
+
export function parseV3Recipe(value) {
|
|
24
|
+
if (!isObject(value))
|
|
25
|
+
return fail('recipe', 'a recipe is an object');
|
|
26
|
+
const r = value;
|
|
27
|
+
if (typeof r.name !== 'string' || !NAME.test(r.name))
|
|
28
|
+
fail('name', 'a name in lower case, digits and hyphens');
|
|
29
|
+
if (!isObject(r.title) || !Object.values(r.title).every(t => typeof t === 'string' && t))
|
|
30
|
+
fail('title', 'a title per language');
|
|
31
|
+
if (r.target !== 'part' && r.target !== 'figure')
|
|
32
|
+
fail('target', "'part' or 'figure'");
|
|
33
|
+
if (!Array.isArray(r.params))
|
|
34
|
+
fail('params', 'a list of parameters');
|
|
35
|
+
const names = new Set();
|
|
36
|
+
r.params.forEach((p, i) => {
|
|
37
|
+
const at = `params[${i}]`;
|
|
38
|
+
if (!isObject(p) || typeof p.name !== 'string' || !NAME.test(p.name))
|
|
39
|
+
fail(at, 'a parameter has a name');
|
|
40
|
+
if (p.name === 'id' || p.name === 'target' || names.has(p.name))
|
|
41
|
+
fail(`${at}.name`, `${p.name} is taken`);
|
|
42
|
+
names.add(p.name);
|
|
43
|
+
if (!KINDS.includes(p.kind))
|
|
44
|
+
fail(`${at}.kind`, `one of ${KINDS.join(', ')}`);
|
|
45
|
+
checkValue(p, p.default, `${at}.default`);
|
|
46
|
+
});
|
|
47
|
+
if (!Array.isArray(r.steps) || !r.steps.length)
|
|
48
|
+
fail('steps', 'one step or more');
|
|
49
|
+
r.steps.forEach((step, i) => {
|
|
50
|
+
const at = `steps[${i}]`;
|
|
51
|
+
if (!isObject(step) || typeof step.kind !== 'string')
|
|
52
|
+
fail(at, 'a step is an object with a kind');
|
|
53
|
+
if (step.when !== undefined) {
|
|
54
|
+
const w = step.when;
|
|
55
|
+
if (!isObject(w) || typeof w.param !== 'string' || !names.has(w.param) || !('is' in w))
|
|
56
|
+
fail(`${at}.when`, 'when: { param, is } of a parameter');
|
|
57
|
+
const p = r.params.find(o => o.name === w.param);
|
|
58
|
+
if (p.kind !== 'choice' && p.kind !== 'flag')
|
|
59
|
+
fail(`${at}.when`, `${p.name} is a ${p.kind}; a step is chosen by a choice or a flag`);
|
|
60
|
+
}
|
|
61
|
+
walk(step, `${at}`, (s, path) => {
|
|
62
|
+
for (const [, name] of s.matchAll(/\{([a-z][a-z0-9-]*)\}/g))
|
|
63
|
+
if (name !== 'id' && name !== 'target' && !names.has(name))
|
|
64
|
+
fail(path, `{${name}} is not a parameter`);
|
|
65
|
+
}, (o, path) => {
|
|
66
|
+
if (typeof o.param !== 'string' || !names.has(o.param))
|
|
67
|
+
fail(path, `${String(o.param)} is not a parameter`);
|
|
68
|
+
const p = r.params.find(x => x.name === o.param);
|
|
69
|
+
if (p.kind !== 'ratio' && p.kind !== 'mm')
|
|
70
|
+
fail(path, `${p.name} is a ${p.kind}; a number is made of a ratio or a length`);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
if (r.target === 'figure' && JSON.stringify(r.steps).includes('{target}'))
|
|
74
|
+
fail('steps', '{target} in a recipe for the whole figure');
|
|
75
|
+
return r;
|
|
76
|
+
}
|
|
77
|
+
/** A value a parameter takes: its kind, its range or its options. */
|
|
78
|
+
function checkValue(p, v, path) {
|
|
79
|
+
if (p.kind === 'flag') {
|
|
80
|
+
if (typeof v !== 'boolean')
|
|
81
|
+
fail(path, `${p.name} is yes or no`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (p.kind === 'ratio' || p.kind === 'mm') {
|
|
85
|
+
if (typeof v !== 'number' || !Number.isFinite(v))
|
|
86
|
+
fail(path, `${p.name} is a number`);
|
|
87
|
+
if (p.min !== undefined && v < p.min)
|
|
88
|
+
fail(path, `${p.name} is at least ${p.min}`);
|
|
89
|
+
if (p.max !== undefined && v > p.max)
|
|
90
|
+
fail(path, `${p.name} is at most ${p.max}`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(p.options) || !p.options.length)
|
|
94
|
+
fail(path, `${p.name} names its options`);
|
|
95
|
+
if (!p.options.includes(v))
|
|
96
|
+
fail(path, `${p.name} is one of ${p.options.join(', ')}`);
|
|
97
|
+
}
|
|
98
|
+
/** The linear form a number may be: `{ param, times?, plus? }` and nothing else. */
|
|
99
|
+
const isLinear = (o) => 'param' in o && Object.keys(o).every(k => k === 'param' || k === 'times' || k === 'plus');
|
|
100
|
+
function walk(v, path, onString, onLinear) {
|
|
101
|
+
if (typeof v === 'string')
|
|
102
|
+
onString(v, path);
|
|
103
|
+
else if (Array.isArray(v))
|
|
104
|
+
v.forEach((x, i) => walk(x, `${path}[${i}]`, onString, onLinear));
|
|
105
|
+
else if (isObject(v)) {
|
|
106
|
+
if (isLinear(v))
|
|
107
|
+
onLinear(v, path);
|
|
108
|
+
else
|
|
109
|
+
for (const [k, x] of Object.entries(v))
|
|
110
|
+
if (k !== 'when')
|
|
111
|
+
walk(x, `${path}.${k}`, onString, onLinear);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** The name a recipe's parts take in this figure: the recipe's name and the first number no part or input uses. */
|
|
115
|
+
export function v3RecipeIdFor(asset, recipe) {
|
|
116
|
+
const taken = [...asset.appearance.map(a => a.target), ...asset.document.model.inputs.map(i => i.id), ...(asset.joints ?? []).map(j => j.id)];
|
|
117
|
+
for (let n = 1;; n++) {
|
|
118
|
+
const id = `${recipe.name}-${n}`;
|
|
119
|
+
if (!taken.some(t => t === id || t.startsWith(`${id}-`) || t.startsWith(`${id}.`)))
|
|
120
|
+
return id;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The proposal a recipe makes with these values: the defaults filled in, the values checked (ADR-0095 condition 3),
|
|
125
|
+
* the steps whose `when` does not hold left out, and the placeholders and linear forms resolved. A string that is only
|
|
126
|
+
* `{<param>}` takes the value itself (a number stays a number); inside a longer string it is written in.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveV3Recipe(recipe, use) {
|
|
129
|
+
const values = {};
|
|
130
|
+
for (const p of recipe.params) {
|
|
131
|
+
const v = use.values && Object.hasOwn(use.values, p.name) ? use.values[p.name] : p.default;
|
|
132
|
+
checkValue(p, v, `values.${p.name}`);
|
|
133
|
+
values[p.name] = v;
|
|
134
|
+
}
|
|
135
|
+
for (const name of Object.keys(use.values ?? {}))
|
|
136
|
+
if (!recipe.params.some(p => p.name === name))
|
|
137
|
+
fail(`values.${name}`, `${name} is not a parameter of ${recipe.name}`);
|
|
138
|
+
if (recipe.target === 'part' && !use.target)
|
|
139
|
+
fail('target', `${recipe.name} is put on a part; choose one`);
|
|
140
|
+
const named = { ...values, id: use.id, ...(use.target ? { target: use.target } : {}) };
|
|
141
|
+
const resolve = (v) => {
|
|
142
|
+
if (typeof v === 'string') {
|
|
143
|
+
const whole = /^\{([a-z][a-z0-9-]*)\}$/.exec(v);
|
|
144
|
+
if (whole)
|
|
145
|
+
return named[whole[1]];
|
|
146
|
+
return v.replace(/\{([a-z][a-z0-9-]*)\}/g, (_, name) => String(named[name]));
|
|
147
|
+
}
|
|
148
|
+
if (Array.isArray(v))
|
|
149
|
+
return v.map(resolve);
|
|
150
|
+
if (isObject(v)) {
|
|
151
|
+
if (isLinear(v))
|
|
152
|
+
return values[v.param] * (v.times ?? 1) + (v.plus ?? 0);
|
|
153
|
+
return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, resolve(x)]));
|
|
154
|
+
}
|
|
155
|
+
return v;
|
|
156
|
+
};
|
|
157
|
+
return recipe.steps
|
|
158
|
+
.filter(step => {
|
|
159
|
+
const w = step.when;
|
|
160
|
+
return !w || values[w.param] === w.is;
|
|
161
|
+
})
|
|
162
|
+
.map(step => {
|
|
163
|
+
const { when: _when, ...rest } = step;
|
|
164
|
+
return resolve(rest);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/** A recipe put into a figure: resolved, then applied as one proposal (ADR-0094). The parts it made are named `id`. */
|
|
168
|
+
export function applyV3Recipe(asset, recipe, use = {}) {
|
|
169
|
+
const checked = parseV3Recipe(recipe);
|
|
170
|
+
if (use.target && !asset.appearance.some(a => a.target === use.target))
|
|
171
|
+
throw new V3ContractError('TARGET_ABSENT', 'target', `${use.target} is not a part`);
|
|
172
|
+
const id = v3RecipeIdFor(asset, checked);
|
|
173
|
+
return { asset: applyV3Proposal(asset, resolveV3Recipe(checked, { id, ...use })), id };
|
|
174
|
+
}
|
|
175
|
+
/** The room a recipe is tried in when it is saved: a 4 x 3 x 6 m box on the floor that follows the figure's size. */
|
|
176
|
+
export function v3RecipeTestRoom() {
|
|
177
|
+
let asset = addV3Box(createV3Asset('Recipe check'), { id: 'room', size: { x: 4000, y: 3000, z: 6000 }, position: { x: 0, y: 1500, z: 0 } });
|
|
178
|
+
asset = applyV3Authoring(asset, { kind: 'attach', part: 'room', face: 'bottom', to: { plane: 'mounting-plane' } });
|
|
179
|
+
return { asset: applyV3Authoring(asset, { kind: 'follow-size', part: 'room' }), target: 'room' };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* A recipe's test (ADR-0095): parsed, put into the test room with its defaults, and the result, with its room declared,
|
|
183
|
+
* passing the release gate. Throws the kernel's refusal, or `RECIPE_GATE` with the gate's findings.
|
|
184
|
+
*/
|
|
185
|
+
export function checkV3Recipe(value) {
|
|
186
|
+
const recipe = parseV3Recipe(value);
|
|
187
|
+
const room = v3RecipeTestRoom();
|
|
188
|
+
const { asset } = applyV3Recipe(room.asset, recipe, recipe.target === 'part' ? { target: room.target } : {});
|
|
189
|
+
const findings = inspectV3Asset(applyV3Proposal(asset, [{ kind: 'fit-room', placement: 'floor' }])).violations;
|
|
190
|
+
if (findings.length)
|
|
191
|
+
throw new V3ContractError('RECIPE_GATE', recipe.name, findings.map(f => `${f.code}: ${f.detail}`).join('; '));
|
|
192
|
+
return recipe;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=v3-recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-recipe.js","sourceRoot":"","sources":["../src/v3-recipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> 현재 구현·검증·제품 연결 상태는 [V3 통합 현황](v3-status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
|
|
4
4
|
|
|
5
|
+
> **2026-09-26:** `portal-profile@1`(좌표 여덟)은 `portal-profile@2`로 바뀌었다. 입력은 폭 · 높이 · 깊이 · 기둥 두께 · 인방 두께의 다섯이고, 좌우로 가운데, 바닥에 서며, 좌표 여덟은 이 값에서 나온다. 앞 셋이 상자와 같아 크기를 따를 수 있다. 아래는 @1 때의 기록이다.
|
|
6
|
+
|
|
5
7
|
상태: 독립 V3 실험. 제품/DB/원본 시드 수정 없음.
|
|
6
8
|
`operato-application/packages/operato-figure/server/seed/figure-samples.json`의
|
|
7
9
|
DRY_ROOM portal-in/out과 lamp-in/out을 읽었다. 서버에서 편집된 revision은 검사하지 않았다.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatiolab/figure-model",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.82",
|
|
4
4
|
"description": "The figure format (figure-v3-asset-1): a kernel that evaluates a figure from its declared relations, the authoring commands a modeller and an AI share, the release gate and the score.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"figure",
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
31
|
"default": "./dist/index.js"
|
|
32
32
|
},
|
|
33
|
-
"./package.json": "./package.json"
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
"./recipes/*": "./recipes/*"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
36
37
|
"dist",
|
|
38
|
+
"recipes",
|
|
37
39
|
"docs",
|
|
38
40
|
"README.md",
|
|
39
41
|
"CHANGELOG.md"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hinged-door",
|
|
3
|
+
"title": { "ko": "여닫이 문", "en": "hinged door" },
|
|
4
|
+
"description": {
|
|
5
|
+
"ko": "고른 부품의 앞면에 문틀과 여닫이 문짝을 넣습니다. 문틀은 그 부품의 폭과 높이를 따르고, 문짝은 경첩 쪽을 축으로 90° 열립니다.",
|
|
6
|
+
"en": "A frame and a hinged leaf on the chosen part's front. The frame follows the part's width and height; the leaf swings 90° about its hinge."
|
|
7
|
+
},
|
|
8
|
+
"target": "part",
|
|
9
|
+
"params": [
|
|
10
|
+
{ "name": "share", "kind": "ratio", "default": 0.25, "min": 0.05, "max": 0.95, "label": { "ko": "문틀 폭 (부품 폭에 대해)", "en": "frame width (of the part's)" } },
|
|
11
|
+
{ "name": "height", "kind": "ratio", "default": 0.8, "min": 0.3, "max": 1, "label": { "ko": "문틀 높이 (부품 높이에 대해)", "en": "frame height (of the part's)" } },
|
|
12
|
+
{ "name": "hinge", "kind": "choice", "default": "left", "options": ["left", "right"], "label": { "ko": "경첩 쪽", "en": "hinge side" } }
|
|
13
|
+
],
|
|
14
|
+
"steps": [
|
|
15
|
+
{ "kind": "add-part", "part": { "id": "{id}-frame", "kind": "portal", "dimensions": { "width": 1000, "height": 2200, "depth": 150, "jamb": 100, "head": 100 } } },
|
|
16
|
+
{ "kind": "attach", "part": "{id}-frame", "face": "back", "to": { "part": "{target}", "face": "front" }, "align": { "y": "min" } },
|
|
17
|
+
{ "kind": "link-dimension", "part": "{id}-frame", "axis": "x", "source": { "of": "part", "part": "{target}", "axis": "x" }, "times": { "param": "share" }, "narrowRange": true },
|
|
18
|
+
{ "kind": "link-dimension", "part": "{id}-frame", "axis": "y", "source": { "of": "part", "part": "{target}", "axis": "y" }, "times": { "param": "height" }, "narrowRange": true },
|
|
19
|
+
|
|
20
|
+
{ "kind": "add-part", "part": { "id": "{id}-leaf", "kind": "box", "dimensions": { "width": 790, "height": 2090, "depth": 40 } } },
|
|
21
|
+
{ "kind": "link-dimension", "part": "{id}-leaf", "axis": "x", "source": { "of": "part", "part": "{id}-frame", "axis": "x" }, "plus": -210, "narrowRange": true },
|
|
22
|
+
{ "kind": "link-dimension", "part": "{id}-leaf", "axis": "y", "source": { "of": "part", "part": "{id}-frame", "axis": "y" }, "plus": -110, "narrowRange": true },
|
|
23
|
+
{
|
|
24
|
+
"when": { "param": "hinge", "is": "left" },
|
|
25
|
+
"kind": "set-joint",
|
|
26
|
+
"joint": {
|
|
27
|
+
"id": "{id}-leaf", "type": "revolute",
|
|
28
|
+
"body0": { "part": "{id}-frame", "face": "opening-left" }, "body1": { "part": "{id}-leaf", "face": "left" },
|
|
29
|
+
"origin": { "facing": "meet", "gap": 5, "align": { "y": "min", "z": "max" } },
|
|
30
|
+
"axis": "Y", "lowerLimit": -90, "upperLimit": 0,
|
|
31
|
+
"state": { "id": "{id}-open" }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"when": { "param": "hinge", "is": "right" },
|
|
36
|
+
"kind": "set-joint",
|
|
37
|
+
"joint": {
|
|
38
|
+
"id": "{id}-leaf", "type": "revolute",
|
|
39
|
+
"body0": { "part": "{id}-frame", "face": "opening-right" }, "body1": { "part": "{id}-leaf", "face": "right" },
|
|
40
|
+
"origin": { "facing": "meet", "gap": 5, "align": { "y": "min", "z": "max" } },
|
|
41
|
+
"axis": "Y", "lowerLimit": 0, "upperLimit": 90,
|
|
42
|
+
"state": { "id": "{id}-open" }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "roller-row",
|
|
3
|
+
"title": { "ko": "롤러 줄", "en": "roller row" },
|
|
4
|
+
"description": {
|
|
5
|
+
"ko": "고른 부품의 윗면에 롤러를 한 줄로 깝니다. 롤러는 그 부품의 폭을 가로지르고, 길이를 따라 간격마다 들어갑니다.",
|
|
6
|
+
"en": "A row of rollers on the chosen part's top, across its depth, as many as fit along its length at the pitch."
|
|
7
|
+
},
|
|
8
|
+
"target": "part",
|
|
9
|
+
"params": [
|
|
10
|
+
{ "name": "pitch", "kind": "mm", "default": 200, "min": 20, "max": 2000, "label": { "ko": "롤러 간격 (mm)", "en": "pitch (mm)" } },
|
|
11
|
+
{ "name": "radius", "kind": "mm", "default": 30, "min": 5, "max": 500, "label": { "ko": "롤러 반지름 (mm)", "en": "roller radius (mm)" } }
|
|
12
|
+
],
|
|
13
|
+
"steps": [
|
|
14
|
+
{ "kind": "add-part", "part": { "id": "{id}-roller", "kind": "cylinder", "dimensions": { "radius": "{radius}", "length": 500 }, "rotation": { "x": 90, "y": 0, "z": 0 } } },
|
|
15
|
+
{ "kind": "link-dimension", "part": "{id}-roller", "axis": "y", "source": { "of": "part", "part": "{target}", "axis": "z" }, "times": 0.9, "narrowRange": true },
|
|
16
|
+
{ "kind": "attach", "part": "{id}-roller", "face": "bottom", "to": { "part": "{target}", "face": "top" } },
|
|
17
|
+
{ "kind": "make-repeat", "part": "{id}-roller", "axis": "x", "pitch": "{pitch}", "narrowRange": true }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sliding-door",
|
|
3
|
+
"title": { "ko": "미닫이 문", "en": "sliding door" },
|
|
4
|
+
"description": {
|
|
5
|
+
"ko": "고른 부품의 앞면에 문틀과 미닫이 문짝을 넣습니다. 문틀은 그 부품의 폭과 높이를 따르고, 문짝은 열림 조작값 하나로 열립니다.",
|
|
6
|
+
"en": "A frame and sliding leaves on the chosen part's front. The frame follows the part's width and height; one control opens the leaves."
|
|
7
|
+
},
|
|
8
|
+
"target": "part",
|
|
9
|
+
"params": [
|
|
10
|
+
{ "name": "share", "kind": "ratio", "default": 0.35, "min": 0.05, "max": 0.95, "label": { "ko": "문틀 폭 (부품 폭에 대해)", "en": "frame width (of the part's)" } },
|
|
11
|
+
{ "name": "height", "kind": "ratio", "default": 0.95, "min": 0.3, "max": 1, "label": { "ko": "문틀 높이 (부품 높이에 대해)", "en": "frame height (of the part's)" } },
|
|
12
|
+
{ "name": "leaves", "kind": "choice", "default": 2, "options": [1, 2], "label": { "ko": "문짝 수", "en": "leaves" } },
|
|
13
|
+
{ "name": "lamp", "kind": "flag", "default": true, "label": { "ko": "열리면 켜지는 램프", "en": "a lamp lit while open" } }
|
|
14
|
+
],
|
|
15
|
+
"steps": [
|
|
16
|
+
{ "kind": "add-part", "part": { "id": "{id}-frame", "kind": "portal", "dimensions": { "width": 1400, "height": 2900, "depth": 300, "jamb": 300, "head": 700 } } },
|
|
17
|
+
{ "kind": "attach", "part": "{id}-frame", "face": "back", "to": { "part": "{target}", "face": "front" }, "align": { "y": "min" } },
|
|
18
|
+
{ "kind": "link-dimension", "part": "{id}-frame", "axis": "x", "source": { "of": "part", "part": "{target}", "axis": "x" }, "times": { "param": "share" }, "narrowRange": true },
|
|
19
|
+
{ "kind": "link-dimension", "part": "{id}-frame", "axis": "y", "source": { "of": "part", "part": "{target}", "axis": "y" }, "times": { "param": "height" }, "narrowRange": true },
|
|
20
|
+
|
|
21
|
+
{ "kind": "add-part", "part": { "id": "{id}-left", "kind": "box", "dimensions": { "width": 400, "height": 2200, "depth": 40 } } },
|
|
22
|
+
{ "when": { "param": "leaves", "is": 1 }, "kind": "link-dimension", "part": "{id}-left", "axis": "x", "source": { "of": "part", "part": "{id}-frame", "axis": "x" }, "plus": -600, "narrowRange": true },
|
|
23
|
+
{ "when": { "param": "leaves", "is": 2 }, "kind": "link-dimension", "part": "{id}-left", "axis": "x", "source": { "of": "part", "part": "{id}-frame", "axis": "x" }, "times": 0.5, "plus": -300, "narrowRange": true },
|
|
24
|
+
{ "kind": "link-dimension", "part": "{id}-left", "axis": "y", "source": { "of": "part", "part": "{id}-frame", "axis": "y" }, "plus": -700, "narrowRange": true },
|
|
25
|
+
{
|
|
26
|
+
"when": { "param": "leaves", "is": 1 },
|
|
27
|
+
"kind": "set-joint",
|
|
28
|
+
"joint": {
|
|
29
|
+
"id": "{id}-left", "type": "prismatic",
|
|
30
|
+
"body0": { "part": "{id}-frame", "face": "front" }, "body1": { "part": "{id}-left", "face": "back" },
|
|
31
|
+
"origin": { "facing": "meet", "align": { "y": "min" } },
|
|
32
|
+
"axis": "X", "lowerLimit": 0, "upperLimit": 1,
|
|
33
|
+
"travel": { "source": { "of": "part", "part": "{id}-left", "axis": "x" }, "times": -1 },
|
|
34
|
+
"state": { "id": "{id}-open" }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"when": { "param": "leaves", "is": 2 },
|
|
39
|
+
"kind": "set-joint",
|
|
40
|
+
"joint": {
|
|
41
|
+
"id": "{id}-left", "type": "prismatic",
|
|
42
|
+
"body0": { "part": "{id}-frame", "face": "front" }, "body1": { "part": "{id}-left", "face": "back" },
|
|
43
|
+
"origin": { "facing": "meet", "align": { "y": "min", "x": { "mm": { "from": { "of": "part", "part": "{id}-frame", "axis": "x" }, "times": -0.25, "plus": 150 } } } },
|
|
44
|
+
"axis": "X", "lowerLimit": 0, "upperLimit": 1,
|
|
45
|
+
"travel": { "source": { "of": "part", "part": "{id}-left", "axis": "x" }, "times": -1 },
|
|
46
|
+
"state": { "id": "{id}-open" }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
{ "when": { "param": "leaves", "is": 2 }, "kind": "add-part", "part": { "id": "{id}-right", "kind": "box", "dimensions": { "width": 400, "height": 2200, "depth": 40 } } },
|
|
51
|
+
{ "when": { "param": "leaves", "is": 2 }, "kind": "link-dimension", "part": "{id}-right", "axis": "x", "source": { "of": "part", "part": "{id}-left", "axis": "x" }, "narrowRange": true },
|
|
52
|
+
{ "when": { "param": "leaves", "is": 2 }, "kind": "link-dimension", "part": "{id}-right", "axis": "y", "source": { "of": "part", "part": "{id}-left", "axis": "y" }, "narrowRange": true },
|
|
53
|
+
{
|
|
54
|
+
"when": { "param": "leaves", "is": 2 },
|
|
55
|
+
"kind": "set-joint",
|
|
56
|
+
"joint": {
|
|
57
|
+
"id": "{id}-right", "type": "prismatic",
|
|
58
|
+
"body0": { "part": "{id}-frame", "face": "front" }, "body1": { "part": "{id}-right", "face": "back" },
|
|
59
|
+
"origin": { "facing": "meet", "align": { "y": "min", "x": { "mm": { "from": { "of": "part", "part": "{id}-frame", "axis": "x" }, "times": 0.25, "plus": -150 } } } },
|
|
60
|
+
"axis": "X",
|
|
61
|
+
"mimic": { "joint": "{id}-left", "multiplier": -1 }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
{ "when": { "param": "lamp", "is": true }, "kind": "add-part", "part": { "id": "{id}-lamp", "kind": "cylinder", "dimensions": { "radius": 80, "length": 80 } } },
|
|
66
|
+
{ "when": { "param": "lamp", "is": true }, "kind": "attach", "part": "{id}-lamp", "face": "bottom", "to": { "part": "{id}-frame", "face": "top" } },
|
|
67
|
+
{ "when": { "param": "lamp", "is": true }, "kind": "set-visibility", "target": "{id}-lamp", "shownWhen": { "input": "{id}-open", "atLeast": 0.01 } }
|
|
68
|
+
]
|
|
69
|
+
}
|