@things-factory/figure-ui 10.1.65 → 10.1.67
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/client/graphql/index.ts +0 -45
- package/client/modeller/figure-history-panel.ts +6 -145
- package/client/modeller/figure-v3-capability.ts +2 -2
- package/client/modeller/figure-v3-clips.ts +2 -2
- package/client/modeller/figure-v3-controls.ts +2 -2
- package/client/modeller/figure-v3-first-save.ts +1 -1
- package/client/modeller/figure-v3-links.ts +6 -6
- package/client/modeller/figure-v3-material.ts +11 -11
- package/client/modeller/figure-v3-parts.ts +7 -10
- package/client/modeller/figure-v3-preview.ts +3 -3
- package/client/modeller/figure-v3-properties.ts +5 -6
- package/client/modeller/figure-v3-verdict.ts +9 -10
- package/client/modeller/figure-view.ts +2 -3
- package/client/modeller/modeller-route.ts +2 -3
- package/client/modeller/scene-budget.ts +0 -14
- package/client/modeller/side-panel-styles.ts +5 -5
- package/client/modeller/v3-align.ts +16 -6
- package/client/modeller/v3-canvas.ts +12 -12
- package/client/modeller/v3-clips.ts +1 -1
- package/client/modeller/v3-controls.ts +1 -1
- package/client/modeller/v3-editor.ts +21 -21
- package/client/modeller/v3-fields.ts +1 -1
- package/client/modeller/v3-figure-facts.ts +1 -1
- package/client/modeller/v3-first-save.ts +3 -4
- package/client/modeller/v3-gizmo.ts +5 -5
- package/client/modeller/v3-help.ts +1 -1
- package/client/modeller/v3-joint-fan.ts +4 -5
- package/client/modeller/v3-keys.ts +1 -1
- package/client/modeller/v3-links.ts +9 -12
- package/client/modeller/v3-maturity.ts +22 -26
- package/client/modeller/v3-refusals.ts +17 -0
- package/client/modeller/v3-renderer.ts +1 -1
- package/client/modeller/v3-stage-model.ts +2 -2
- package/client/modeller/v3-thumbnail.ts +1 -3
- package/client/pages/figure-v3-modeller-page.ts +8 -11
- package/client/types.ts +2 -31
- package/dist-client/graphql/index.js +0 -45
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +4 -37
- package/dist-client/modeller/figure-history-panel.js +8 -154
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
- package/dist-client/modeller/figure-v3-capability.js +1 -1
- package/dist-client/modeller/figure-v3-capability.js.map +1 -1
- package/dist-client/modeller/figure-v3-clips.d.ts +2 -2
- package/dist-client/modeller/figure-v3-clips.js +2 -2
- package/dist-client/modeller/figure-v3-clips.js.map +1 -1
- package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
- package/dist-client/modeller/figure-v3-controls.js +2 -2
- package/dist-client/modeller/figure-v3-controls.js.map +1 -1
- package/dist-client/modeller/figure-v3-first-save.d.ts +1 -1
- package/dist-client/modeller/figure-v3-first-save.js +1 -1
- package/dist-client/modeller/figure-v3-first-save.js.map +1 -1
- package/dist-client/modeller/figure-v3-links.d.ts +5 -5
- package/dist-client/modeller/figure-v3-links.js +6 -6
- package/dist-client/modeller/figure-v3-links.js.map +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +6 -6
- package/dist-client/modeller/figure-v3-material.js +11 -11
- package/dist-client/modeller/figure-v3-material.js.map +1 -1
- package/dist-client/modeller/figure-v3-parts.d.ts +6 -8
- package/dist-client/modeller/figure-v3-parts.js +1 -1
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/figure-v3-preview.js.map +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +4 -5
- package/dist-client/modeller/figure-v3-properties.js +5 -6
- package/dist-client/modeller/figure-v3-properties.js.map +1 -1
- package/dist-client/modeller/figure-v3-verdict.d.ts +2 -2
- package/dist-client/modeller/figure-v3-verdict.js +9 -10
- package/dist-client/modeller/figure-v3-verdict.js.map +1 -1
- package/dist-client/modeller/figure-view.d.ts +2 -3
- package/dist-client/modeller/figure-view.js.map +1 -1
- package/dist-client/modeller/modeller-route.d.ts +2 -3
- package/dist-client/modeller/modeller-route.js +2 -3
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/scene-budget.d.ts +0 -11
- package/dist-client/modeller/scene-budget.js +0 -12
- package/dist-client/modeller/scene-budget.js.map +1 -1
- package/dist-client/modeller/side-panel-styles.d.ts +2 -2
- package/dist-client/modeller/side-panel-styles.js +5 -5
- package/dist-client/modeller/side-panel-styles.js.map +1 -1
- package/dist-client/modeller/v3-align.d.ts +9 -5
- package/dist-client/modeller/v3-align.js +13 -3
- package/dist-client/modeller/v3-align.js.map +1 -1
- package/dist-client/modeller/v3-canvas.d.ts +4 -4
- package/dist-client/modeller/v3-canvas.js +9 -9
- package/dist-client/modeller/v3-canvas.js.map +1 -1
- package/dist-client/modeller/v3-clips.js.map +1 -1
- package/dist-client/modeller/v3-controls.js.map +1 -1
- package/dist-client/modeller/v3-editor.d.ts +13 -13
- package/dist-client/modeller/v3-editor.js +19 -18
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-fields.js.map +1 -1
- package/dist-client/modeller/v3-figure-facts.d.ts +1 -1
- package/dist-client/modeller/v3-figure-facts.js +1 -1
- package/dist-client/modeller/v3-figure-facts.js.map +1 -1
- package/dist-client/modeller/v3-first-save.js +3 -4
- package/dist-client/modeller/v3-first-save.js.map +1 -1
- package/dist-client/modeller/v3-gizmo.d.ts +5 -5
- package/dist-client/modeller/v3-gizmo.js +1 -1
- package/dist-client/modeller/v3-gizmo.js.map +1 -1
- package/dist-client/modeller/v3-help.d.ts +1 -1
- package/dist-client/modeller/v3-help.js +1 -1
- package/dist-client/modeller/v3-help.js.map +1 -1
- package/dist-client/modeller/v3-joint-fan.js +4 -5
- package/dist-client/modeller/v3-joint-fan.js.map +1 -1
- package/dist-client/modeller/v3-keys.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +5 -7
- package/dist-client/modeller/v3-links.js +6 -8
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/modeller/v3-maturity.d.ts +7 -7
- package/dist-client/modeller/v3-maturity.js +20 -24
- package/dist-client/modeller/v3-maturity.js.map +1 -1
- package/dist-client/modeller/v3-refusals.d.ts +6 -0
- package/dist-client/modeller/v3-refusals.js +18 -0
- package/dist-client/modeller/v3-refusals.js.map +1 -1
- package/dist-client/modeller/v3-renderer.js +1 -1
- package/dist-client/modeller/v3-renderer.js.map +1 -1
- package/dist-client/modeller/v3-stage-model.js.map +1 -1
- package/dist-client/modeller/v3-thumbnail.js +1 -3
- package/dist-client/modeller/v3-thumbnail.js.map +1 -1
- package/dist-client/pages/figure-v3-modeller-page.d.ts +4 -5
- package/dist-client/pages/figure-v3-modeller-page.js +8 -11
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -29
- package/dist-client/types.js +3 -0
- package/dist-client/types.js.map +1 -1
- package/docs/v3-product-cutover.md +14 -24
- package/package.json +5 -5
- package/test/browser/modeller-page.js +6 -7
- package/test/browser/modeller.check.mjs +9 -16
- package/test/fixtures/joint-column.json +1138 -0
- package/test/fixtures/joint-roller.json +872 -0
- package/test/fixtures/robot-arm-6dof.json +2776 -0
- package/test/i18n-prefix-guard.test.ts +0 -31
- package/test/modeller-route.test.ts +2 -3
- package/test/v3-align.test.ts +9 -4
- package/test/v3-clips.test.ts +1 -1
- package/test/v3-controls.test.ts +1 -1
- package/test/v3-fields.test.ts +4 -4
- package/test/v3-first-save.test.ts +1 -1
- package/test/v3-gizmo.test.ts +5 -5
- package/test/v3-joint-fan.test.ts +12 -42
- package/test/v3-keys.test.ts +1 -1
- package/test/v3-layout.test.ts +4 -5
- package/test/v3-links.test.ts +3 -3
- package/test/v3-material.test.ts +3 -3
- package/test/v3-maturity.test.ts +8 -9
- package/test/v3-parts-wiring.test.ts +3 -3
- package/test/v3-preview.test.ts +1 -1
- package/test/v3-product-write.test.ts +1 -1
- package/test/v3-properties.test.ts +3 -3
- package/test/v3-refusals.test.ts +12 -0
- package/test/v3-release-panel.test.ts +5 -5
- package/test/v3-scene-canvas.test.ts +2 -2
- package/test/v3-theme.test.ts +2 -2
- package/test/v3-verdict.test.ts +2 -2
- package/test/v3-view.test.ts +2 -2
- package/translations/en.json +2 -143
- package/translations/ja.json +2 -143
- package/translations/ko.json +2 -143
- package/translations/ms.json +2 -143
- package/translations/zh.json +2 -143
- package/client/modeller/figure-source.ts +0 -437
- package/client/modeller/part-edits.ts +0 -312
- package/dist-client/modeller/figure-source.d.ts +0 -238
- package/dist-client/modeller/figure-source.js +0 -193
- package/dist-client/modeller/figure-source.js.map +0 -1
- package/dist-client/modeller/part-edits.d.ts +0 -89
- package/dist-client/modeller/part-edits.js +0 -270
- package/dist-client/modeller/part-edits.js.map +0 -1
- package/test/expect.ts +0 -57
- package/test/figure-source.test.ts +0 -511
- package/test/part-edits.test.ts +0 -350
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The one modeller.
|
|
3
|
-
*
|
|
4
|
-
* link written before the switch still lands.
|
|
2
|
+
* The one modeller. The catalogue, a new figure and an old address all open the same page. `figure-v3-modeller`
|
|
3
|
+
* stays as an alias so a link written under that name still lands.
|
|
5
4
|
*/
|
|
6
5
|
export const MODELLER = 'figure-modeller'
|
|
7
6
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { FigureCost } from '@hatiolab/figure-model'
|
|
2
1
|
|
|
3
2
|
/**
|
|
4
3
|
* 한 화면이 감당하는 양 — 「몇 개까지 편한가」의 분모.
|
|
@@ -56,19 +55,6 @@ export interface FigureCapacity {
|
|
|
56
55
|
budget: number
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/**
|
|
60
|
-
* 몇 개까지 놓을 수 있나.
|
|
61
|
-
*
|
|
62
|
-
* 두 예산 중 **먼저 차는 쪽**이 답이다. 평균이나 곱을 내지 않는다 — 하나만 꽉 차도
|
|
63
|
-
* 그것이 병목이고, 저작자가 고쳐야 할 곳은 그 하나다.
|
|
64
|
-
*
|
|
65
|
-
* 셈은 여기서 하지만 재료는 전부 figure-model 이 낸 것이다. `cost.groups` 가 인스턴스
|
|
66
|
-
* 하나당 draw call 이고 `cost.triangles` 가 인스턴스 하나의 삼각형 수다.
|
|
67
|
-
*/
|
|
68
|
-
export function capacityOf(cost: FigureCost): FigureCapacity | undefined {
|
|
69
|
-
return capacityFrom(cost.groups, cost.triangles)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
58
|
/**
|
|
73
59
|
* 저장된 값만으로 셈한다.
|
|
74
60
|
*
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { css } from 'lit'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The side panel's one look
|
|
4
|
+
* The side panel's one look, for every element that draws in the modeller's side.
|
|
5
5
|
*
|
|
6
6
|
* The side is drawn by several elements (the type field, the figure's properties, a part's values, fastening, places,
|
|
7
7
|
* motion, material, controls, clips, the environment) and each had styled its own fields: three label styles and
|
|
8
8
|
* three field heights in one column (user, 2026-09-24: "why are the styles and layout all different?"). They share
|
|
9
9
|
* this sheet now and keep only what is theirs.
|
|
10
10
|
*
|
|
11
|
-
* The vocabulary
|
|
11
|
+
* The vocabulary:
|
|
12
12
|
* section one titled group; the side is a stack of them
|
|
13
13
|
* h3 a group's title -- small capitals
|
|
14
14
|
* div[inline-field] a short label and its one field on one line
|
|
@@ -28,7 +28,7 @@ export const SidePanelStyles = css`
|
|
|
28
28
|
font-size: 0.76rem;
|
|
29
29
|
color: var(--md-sys-color-on-surface);
|
|
30
30
|
}
|
|
31
|
-
/* The side is a stack of sections, each a titled group
|
|
31
|
+
/* The side is a stack of sections, each a titled group. */
|
|
32
32
|
section {
|
|
33
33
|
display: grid;
|
|
34
34
|
gap: 6px;
|
|
@@ -101,7 +101,7 @@ export const SidePanelStyles = css`
|
|
|
101
101
|
div[vector-row] + div[inline-field] {
|
|
102
102
|
margin-top: 5px;
|
|
103
103
|
}
|
|
104
|
-
/* A row of icon tools (align, mirror, snap)
|
|
104
|
+
/* A row of icon tools (align, mirror, snap). */
|
|
105
105
|
div[tools] {
|
|
106
106
|
display: flex;
|
|
107
107
|
flex-wrap: wrap;
|
|
@@ -270,7 +270,7 @@ export const SidePanelStyles = css`
|
|
|
270
270
|
font-size: 0.76rem;
|
|
271
271
|
color: var(--md-sys-color-on-surface);
|
|
272
272
|
}
|
|
273
|
-
/* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed
|
|
273
|
+
/* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed. */
|
|
274
274
|
button[help] {
|
|
275
275
|
display: inline-flex;
|
|
276
276
|
align-items: center;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { compileV3Asset, v3OccupiedBoxesOf, v3WritersOf } from '@hatiolab/figure-model'
|
|
2
2
|
import type { V3Asset, V3InputChange, V3WorldBox } from '@hatiolab/figure-model'
|
|
3
3
|
|
|
4
|
-
import { niceStep } from './part-edits.js'
|
|
5
4
|
import type { GizmoReach } from './v3-gizmo.js'
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* 맞추기 — the
|
|
7
|
+
* 맞추기 — the align tools on a part: centre on X, centre on Z, stand on the floor,
|
|
9
8
|
* mirror on X or Z, snap to the grid.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
* y 0 and X and Z centred on 0, so centring puts the part's box centre on 0 and the floor puts its lowest point on
|
|
10
|
+
* The asset's frame has the floor at y 0 and X and Z centred on 0, so centring puts the part's box centre on 0 and the floor puts its lowest point on
|
|
13
11
|
* y 0. The part's box is its evaluated, rotated extent (`v3OccupiedBoxesOf`), so a turned part lands by what it
|
|
14
12
|
* actually covers, not by its unturned size.
|
|
15
13
|
*
|
|
@@ -25,7 +23,7 @@ export function boxOf(asset: V3Asset, part: string): V3WorldBox | undefined {
|
|
|
25
23
|
return v3OccupiedBoxesOf(geometry, v3WritersOf(asset)).find(box => box.part === part)
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
/** The grid
|
|
26
|
+
/** The snapping grid: about five steps across the figure, on 1 · 2 · 5 multiples. */
|
|
29
27
|
export function gridStepOf(asset: V3Asset): number {
|
|
30
28
|
const geometry = compileV3Asset(asset).evaluate().geometry
|
|
31
29
|
const boxes = v3OccupiedBoxesOf(geometry, v3WritersOf(asset))
|
|
@@ -77,8 +75,20 @@ export function mirrorChanges(asset: V3Asset, part: string, copy: string, axis:
|
|
|
77
75
|
return [{ kind: 'set-design', input: `${copy}.position.${axis}`, unit: 'mm', value: Math.round((asset.designInputs[id] ?? 0) - 2 * box.centre[axis]) }]
|
|
78
76
|
}
|
|
79
77
|
|
|
80
|
-
/** Mirroring a part that already sits on the centre plane would put the copy inside it
|
|
78
|
+
/** Mirroring a part that already sits on the centre plane would put the copy inside it, so it is refused. */
|
|
81
79
|
export function canMirror(asset: V3Asset, part: string, axis: 'x' | 'z', reach: GizmoReach): boolean {
|
|
82
80
|
const box = reach.translate ? boxOf(asset, part) : undefined
|
|
83
81
|
return !!box && Math.abs(box.centre[axis]) >= 0.5
|
|
84
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A **readable** grid step for a length: about five cells, rounded up to a 1, 2 or 5 multiple as rulers and charts do.
|
|
86
|
+
* Divided plainly it gives 27 or 33, and coordinates snapped to that read as 54 and 81. Never 0.
|
|
87
|
+
*/
|
|
88
|
+
export function niceStep(span: number): number {
|
|
89
|
+
const raw = Math.max(span, 1) / 5
|
|
90
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(raw)))
|
|
91
|
+
const n = raw / magnitude
|
|
92
|
+
const multiple = n < 1.5 ? 1 : n < 3.5 ? 2 : n < 7.5 ? 5 : 10
|
|
93
|
+
return Math.max(1, multiple * magnitude)
|
|
94
|
+
}
|
|
@@ -11,10 +11,10 @@ import { JOINT_FAN } from './v3-joint-fan.js'
|
|
|
11
11
|
import { ownerOf } from './v3-fields.js'
|
|
12
12
|
|
|
13
13
|
/*
|
|
14
|
-
* The
|
|
14
|
+
* The modeller's canvas -- a things-scene board in editing mode.
|
|
15
15
|
*
|
|
16
|
-
* Each body figure-model evaluates stands on the board as a `figure-v3-part` component, so everything the
|
|
17
|
-
*
|
|
16
|
+
* Each body figure-model evaluates stands on the board as a `figure-v3-part` component, so everything the canvas
|
|
17
|
+
* has comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and
|
|
18
18
|
* live readout, box and shift selection, the camera (damping, zoom toward the cursor, double-click to fit, the six
|
|
19
19
|
* view presets and their keys), the studio environment, the coordinate ground, lights and shadows, supersampling.
|
|
20
20
|
* An earlier canvas drew the bodies with three.js directly and lost all of that (user, 2026-09-24: "why nerf it?").
|
|
@@ -90,7 +90,7 @@ export class FigureV3Canvas extends LitElement {
|
|
|
90
90
|
/**
|
|
91
91
|
* View only: nothing that happens on the board is told out. The card's picture is taken from a second canvas stood up
|
|
92
92
|
* off screen (`v3-thumbnail.ts`); the scene settles its components as it builds them, and a change told from there
|
|
93
|
-
* would reach the editor as an edit
|
|
93
|
+
* would reach the editor as an edit.
|
|
94
94
|
*/
|
|
95
95
|
@property({ type: Boolean }) viewOnly = false
|
|
96
96
|
/** Where the selected part turns and how far (`v3-joint-fan`): shown, never picked, never saved. */
|
|
@@ -131,9 +131,9 @@ export class FigureV3Canvas extends LitElement {
|
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* The board as a picture, for the card: the 3D view at this size, or nothing when it cannot be taken. The scene's
|
|
134
|
-
* `toDataURL` renders the 2D board (a white sheet
|
|
134
|
+
* `toDataURL` renders the 2D board (a white sheet), and the 3D renderer keeps no drawing buffer, so the
|
|
135
135
|
* scene's own `snapshot3d` draws and reads in one task. A picture that cannot be taken is not an error and is not
|
|
136
|
-
* faked (
|
|
136
|
+
* faked (user, 2026-09-24: no card had a picture after this canvas moved onto the scene).
|
|
137
137
|
*/
|
|
138
138
|
async snapshot(width = 480, height = 360): Promise<string | undefined> {
|
|
139
139
|
const layer = this.scene?.root as (SceneLayer & { snapshot3d?: (w?: number, h?: number, type?: string, quality?: number) => Promise<string | undefined> }) | undefined
|
|
@@ -148,7 +148,7 @@ export class FigureV3Canvas extends LitElement {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/** The whole figure in view: the board's fit, then the 3D camera's own
|
|
151
|
+
/** The whole figure in view: the board's fit, then the 3D camera's own. */
|
|
152
152
|
fit() {
|
|
153
153
|
this.scene?.fit?.('ratio')
|
|
154
154
|
this.scene?.root?.fitCameraToModel?.()
|
|
@@ -188,7 +188,7 @@ export class FigureV3Canvas extends LitElement {
|
|
|
188
188
|
height: needed.height,
|
|
189
189
|
depth: needed.depth,
|
|
190
190
|
threed: true,
|
|
191
|
-
/* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large
|
|
191
|
+
/* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large. */
|
|
192
192
|
pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),
|
|
193
193
|
...sceneLook(this.view ?? DEFAULT_VIEW),
|
|
194
194
|
gridStep: niceGridStep(Math.max(needed.width, needed.height)),
|
|
@@ -196,7 +196,7 @@ export class FigureV3Canvas extends LitElement {
|
|
|
196
196
|
fillStyle: PAPER,
|
|
197
197
|
components: this.components()
|
|
198
198
|
},
|
|
199
|
-
/* Editing mode: the camera view buttons, gizmo modes, help and keys
|
|
199
|
+
/* Editing mode: the camera view buttons, gizmo modes, help and keys. */
|
|
200
200
|
mode: 1
|
|
201
201
|
}) as SceneHandle
|
|
202
202
|
this.standingFor = this.context
|
|
@@ -280,7 +280,7 @@ export class FigureV3Canvas extends LitElement {
|
|
|
280
280
|
})
|
|
281
281
|
/*
|
|
282
282
|
A change comes up to the board with the component in the last argument's `origin` (the scene appends
|
|
283
|
-
{ origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing
|
|
283
|
+
{ origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing.
|
|
284
284
|
*/
|
|
285
285
|
/*
|
|
286
286
|
A drag is written once, when it ends. The scene's gizmo writes the component on every move -- a size anchored on
|
|
@@ -323,8 +323,8 @@ export class FigureV3Canvas extends LitElement {
|
|
|
323
323
|
private commit() {
|
|
324
324
|
/*
|
|
325
325
|
Not while a pointer is down: a pause set by a change just before the press (the arrow keys, the scene settling
|
|
326
|
-
the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a
|
|
327
|
-
two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.
|
|
326
|
+
the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a part placed by a rule
|
|
327
|
+
became two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.
|
|
328
328
|
*/
|
|
329
329
|
if (this.pointerDown || !this.board || !this.pending.size) return
|
|
330
330
|
const changed = [...this.pending]
|
|
@@ -2,7 +2,7 @@ import { driverValue } from '@hatiolab/figure-model'
|
|
|
2
2
|
import type { V3Asset, V3Driver, V3InputChange } from '@hatiolab/figure-model'
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
|
-
* The figure's clips
|
|
5
|
+
* The figure's clips: controls played by keys over time, grouped into clips that each run on one
|
|
6
6
|
* clock. Kept apart from the element so the reading is tested without a browser.
|
|
7
7
|
*/
|
|
8
8
|
export interface ClipKey {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { V3Asset } from '@hatiolab/figure-model'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* The figure's controls -- its state inputs
|
|
4
|
+
* The figure's controls -- its state inputs: the values a board or a connector sets to pose
|
|
5
5
|
* the figure (a lift height, a joint angle, a gripper opening). Kept apart from the element so the reading is tested
|
|
6
6
|
* without a browser.
|
|
7
7
|
*/
|
|
@@ -29,7 +29,7 @@ import type { V3PaletteKind, V3PartRow } from './figure-v3-parts.js'
|
|
|
29
29
|
import { paletteRecipe } from './v3-palette.js'
|
|
30
30
|
import type { FigureV3Canvas } from './v3-canvas.js'
|
|
31
31
|
import { nudgeOf } from './v3-stage-model.js'
|
|
32
|
-
import { inDeclaredRoom, narrowingRetryOf, partRefusalOf, placementRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js'
|
|
32
|
+
import { inDeclaredRoom, inputRangeRefusalOf, narrowingRetryOf, partRefusalOf, placementRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js'
|
|
33
33
|
import type { V3RenderPart } from './v3-renderer.js'
|
|
34
34
|
import type { V3BudgetLine } from './v3-maturity.js'
|
|
35
35
|
import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './v3-links.js'
|
|
@@ -40,8 +40,7 @@ import { isLive, livePartsOf, liveOverSession, NOTHING_LIVE, scrubbed, scrubStep
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* The side's tabs
|
|
44
|
-
* the release history. A figure-level tab (verdict, history) gives way to properties when a part is chosen; a tab about
|
|
43
|
+
* The side's tabs: properties, what it does (places), motion, the verdict, the view, the release history. A figure-level tab (verdict, history) gives way to properties when a part is chosen; a tab about
|
|
45
44
|
* parts keeps showing the newly chosen part.
|
|
46
45
|
*/
|
|
47
46
|
type SideTab = 'properties' | 'capability' | 'motion' | 'verdict' | 'view' | 'history'
|
|
@@ -106,7 +105,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
106
105
|
min-width: 0;
|
|
107
106
|
min-height: 0;
|
|
108
107
|
}
|
|
109
|
-
/* The side: tabs over one panel at a time, each using the whole height
|
|
108
|
+
/* The side: tabs over one panel at a time, each using the whole height. */
|
|
110
109
|
aside {
|
|
111
110
|
display: flex;
|
|
112
111
|
flex-direction: column;
|
|
@@ -228,7 +227,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
228
227
|
`]
|
|
229
228
|
@property({ attribute: false }) session?: V3ProductSession
|
|
230
229
|
@property({ type: Number }) revision = 0
|
|
231
|
-
/** The verdict's count, drawn on its tab so it stays in sight while another tab is open
|
|
230
|
+
/** The verdict's count, drawn on its tab so it stays in sight while another tab is open. */
|
|
232
231
|
@property({ type: Number }) instances?: number
|
|
233
232
|
/** The cost line the parts list shows, from the maturity report. */
|
|
234
233
|
@property({ attribute: false }) budget?: V3BudgetLine
|
|
@@ -244,10 +243,10 @@ export class FigureV3Editor extends LitElement {
|
|
|
244
243
|
@state() private tab: SideTab = 'properties'
|
|
245
244
|
/** The frame the bar folds - found after the first render, since the bar is drawn before it. */
|
|
246
245
|
@state() private panes?: OxThreePane
|
|
247
|
-
/** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes
|
|
246
|
+
/** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes. */
|
|
248
247
|
@state() private middle: 'edit' | 'preview' = 'edit'
|
|
249
248
|
/**
|
|
250
|
-
* How the workbench shows the figure
|
|
249
|
+
* How the workbench shows the figure, in the board's own state names. The viewer's, kept in
|
|
251
250
|
* their browser, never in the figure.
|
|
252
251
|
*/
|
|
253
252
|
@state() private view: ViewSettings = FigureV3Editor.storedView()
|
|
@@ -268,8 +267,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
268
267
|
}
|
|
269
268
|
}
|
|
270
269
|
/**
|
|
271
|
-
* Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved
|
|
272
|
-
*
|
|
270
|
+
* Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved: a part
|
|
271
|
+
* hidden here is still in the figure, still checked, still drawn on a board.
|
|
273
272
|
*/
|
|
274
273
|
@state() private hiddenParts = new Set<string>()
|
|
275
274
|
/** A value in hand -- typed, held on a spinner, scrubbed, dragged -- drawn at once and recorded on release. */
|
|
@@ -290,7 +289,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
290
289
|
/*
|
|
291
290
|
The session's history is the only one. With the canvas focused the scene took Ctrl+Z as well, undoing on its own
|
|
292
291
|
board the last value the canvas had written -- which the canvas then told as a drag: an undo of a drag on a
|
|
293
|
-
|
|
292
|
+
part placed by a rule wrote the drag again (2026-09-25). Heard first (capture) and kept from the scene.
|
|
294
293
|
*/
|
|
295
294
|
e.stopPropagation()
|
|
296
295
|
if (action === 'translate' || action === 'rotate' || action === 'scale') canvas?.gizmo(action)
|
|
@@ -324,7 +323,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
324
323
|
this.error = ''
|
|
325
324
|
this.changed()
|
|
326
325
|
} catch (error) {
|
|
327
|
-
|
|
326
|
+
const say = (key: string, params: Record<string, string>) => i18next.t(key, params)
|
|
327
|
+
this.error = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ?? (error instanceof Error ? error.message : String(error))
|
|
328
328
|
} finally {
|
|
329
329
|
/* Committed or refused, the value in hand is gone: the figure as it stands is drawn again. */
|
|
330
330
|
this.live = NOTHING_LIVE
|
|
@@ -408,7 +408,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
408
408
|
}
|
|
409
409
|
/**
|
|
410
410
|
* The list's own operations, as V3 part edits — each one step of the session's history. The copy and the moved
|
|
411
|
-
* part stay selected
|
|
411
|
+
* part stay selected.
|
|
412
412
|
*/
|
|
413
413
|
private duplicatePart(id: string) {
|
|
414
414
|
this.run(() => {
|
|
@@ -486,14 +486,14 @@ export class FigureV3Editor extends LitElement {
|
|
|
486
486
|
return this.requestUpdate()
|
|
487
487
|
}
|
|
488
488
|
/*
|
|
489
|
-
A part whose place is worked out
|
|
489
|
+
A part whose place is worked out has no numbers for the gizmo to write: the drag is added beside
|
|
490
490
|
its rule (`nudge-part`). A part a joint or a fastening places, or a size that is worked out, is refused -- said in
|
|
491
491
|
words, not by the part silently springing back (user, 2026-09-24: 「기즈모 컨트롤도 전혀 작동이 안되는군」).
|
|
492
492
|
*/
|
|
493
493
|
const body = view.geometry.find(g => g.id === id) as unknown as V3RenderPart | undefined
|
|
494
494
|
if (!body) return this.requestUpdate()
|
|
495
495
|
const { by, turn } = nudgeOf(body, result)
|
|
496
|
-
/* A size worked out from the figure's: the drag's factor beside its rule, so it still follows
|
|
496
|
+
/* A size worked out from the figure's: the drag's factor beside its rule, so it still follows. */
|
|
497
497
|
const grow: Partial<Record<'x' | 'y' | 'z', number>> = {}
|
|
498
498
|
if (result.scale) (['x', 'y', 'z'] as const).forEach((axis, i) => Math.abs(result.scale![i]! - 1) > 1e-6 && (grow[axis] = Math.round(result.scale![i]! * 1e6) / 1e6))
|
|
499
499
|
if (!Object.keys(by).length && !Object.keys(turn).length && !Object.keys(grow).length) return this.requestUpdate()
|
|
@@ -525,7 +525,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
525
525
|
if (changes.length) this.session!.apply(changes)
|
|
526
526
|
})
|
|
527
527
|
}
|
|
528
|
-
/** A mirrored copy: duplicate, then place the copy on the other side — two steps
|
|
528
|
+
/** A mirrored copy: duplicate, then place the copy on the other side — two steps. */
|
|
529
529
|
private mirror(axis: 'x' | 'z') {
|
|
530
530
|
this.run(() => {
|
|
531
531
|
const id = this.selected
|
|
@@ -569,7 +569,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
569
569
|
</div>`
|
|
570
570
|
}
|
|
571
571
|
/**
|
|
572
|
-
* A part's name
|
|
572
|
+
* A part's name. It renames every value the part owns, and what refers to them follows
|
|
573
573
|
* (figure-model's rename-part), as one step of the history.
|
|
574
574
|
*/
|
|
575
575
|
/**
|
|
@@ -603,7 +603,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
603
603
|
: String(error)
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
|
-
/** Size response, fastening and motion for the selected part
|
|
606
|
+
/** Size response, fastening and motion for the selected part, as authoring commands. */
|
|
607
607
|
private renderLinks(unavailable: boolean, section: 'shape' | 'places' | 'motion') {
|
|
608
608
|
let asset
|
|
609
609
|
try {
|
|
@@ -627,7 +627,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
627
627
|
@v3-part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
628
628
|
></figure-v3-links>`
|
|
629
629
|
}
|
|
630
|
-
/** The figure's controls: try a value, name it, set its range and travel time, keep the pose
|
|
630
|
+
/** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
|
|
631
631
|
private renderControls(view: { fields: Array<{ id: string; role?: string; value: number }> }, unavailable: boolean) {
|
|
632
632
|
let asset
|
|
633
633
|
try {
|
|
@@ -827,8 +827,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
827
827
|
</button>`
|
|
828
828
|
}
|
|
829
829
|
/**
|
|
830
|
-
* The parts list, one row per part the author made: a repeat is one row with how many copies it makes now
|
|
831
|
-
*
|
|
830
|
+
* The parts list, one row per part the author made: a repeat is one row with how many copies it makes now
|
|
831
|
+
* (user, 2026-09-24: the conveyor's sixteen rollers were sixteen rows).
|
|
832
832
|
*/
|
|
833
833
|
private partRows(geometry: readonly { id: string }[]): V3PartRow[] {
|
|
834
834
|
const rows = new Map<string, V3PartRow>()
|
|
@@ -859,7 +859,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
859
859
|
}
|
|
860
860
|
}
|
|
861
861
|
/**
|
|
862
|
-
* A repeat: which way its copies run, how many there are now, and how far apart
|
|
862
|
+
* A repeat: which way its copies run, how many there are now, and how far apart.
|
|
863
863
|
* The pitch of a row that fills its length is edited as a number (`set-repeat` makes it the part's own the first
|
|
864
864
|
* time); a row of a fixed count says its pitch cannot be set yet (figure-model refuses it the same way).
|
|
865
865
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* The values a part or the figure is edited by, grouped
|
|
2
|
+
* The values a part or the figure is edited by, grouped: position, rotation and size
|
|
3
3
|
* as one row of three cells each (X · Y · Z read together), and the shape's own measures one to a line under their
|
|
4
4
|
* own title. Kept apart from the editor so the grouping is tested without a browser.
|
|
5
5
|
*/
|
|
@@ -38,7 +38,7 @@ export function v3FigureFacts(source: string): V3FigureFacts {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* A tag line as a list
|
|
41
|
+
* A tag line as a list: split on commas, drop a leading `#`, keep each once. An empty
|
|
42
42
|
* line is an empty list, so an author can clear every tag.
|
|
43
43
|
*/
|
|
44
44
|
export function tagsOf(raw: string): string[] {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { isPlaceholderType, TYPE_LENGTH, TYPE_PATTERN } from '@hatiolab/figure-model'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* The first save of a new figure asks for its type and name
|
|
5
|
-
* the figure by; it freezes when the figure is released (ADR-0063 decision 4). The
|
|
6
|
-
* under a temporary machine name
|
|
7
|
-
* (user, 2026-09-25: V2 features are not to go missing).
|
|
4
|
+
* The first save of a new figure asks for its type and name. The type is the name a board places
|
|
5
|
+
* the figure by; it freezes when the figure is released (ADR-0063 decision 4). The modeller once saved a new figure
|
|
6
|
+
* under a temporary machine name and asked nothing -- the author had to find the field afterwards (user, 2026-09-25).
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
/** A type as typed, made into the shape a type has: letters, digits and underscores, upper case. */
|
|
@@ -2,13 +2,13 @@ import { v3AttachmentsOf } from '@hatiolab/figure-model'
|
|
|
2
2
|
import type { V3Asset, V3InputChange } from '@hatiolab/figure-model'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* What the gizmo may write for a part
|
|
5
|
+
* What the gizmo may write for a part: move · turn · size, on a V3 asset.
|
|
6
6
|
*
|
|
7
7
|
* A drag can only be written back where the part is placed by its own numbers. A part the create commands made is:
|
|
8
8
|
* its pose is `rigid@1` into the asset frame from `<part>.position.*` and `<part>.rotation.*`, and a box's shape
|
|
9
9
|
* (`box-shape@1` or `rounded-box@1`) takes its width, height and depth from inputs of its own — `<part>.size.*` from
|
|
10
|
-
* `addV3Box`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or
|
|
11
|
-
*
|
|
10
|
+
* `addV3Box`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or placed by
|
|
11
|
+
* expressions has no such numbers; the gizmo does not offer what it could not write back, rather
|
|
12
12
|
* than move something the next evaluation would put back.
|
|
13
13
|
*/
|
|
14
14
|
export interface GizmoReach {
|
|
@@ -23,7 +23,7 @@ export interface GizmoReach {
|
|
|
23
23
|
*/
|
|
24
24
|
ownSizes?: (string | null)[]
|
|
25
25
|
/**
|
|
26
|
-
* A round part keeps its section round
|
|
26
|
+
* A round part keeps its section round: the radii across (X and Z) take the one factor
|
|
27
27
|
* the drag gave most; the length along Y takes its own. A sphere's three radii take one factor together.
|
|
28
28
|
*/
|
|
29
29
|
round?: { across: string[]; along: string[] }
|
|
@@ -109,7 +109,7 @@ export interface GizmoResult {
|
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* The input changes a drag makes — one list, applied as one step. Millimetres to the millimetre and degrees to a
|
|
112
|
-
* tenth
|
|
112
|
+
* tenth (a drag otherwise stores 722.4180046035418 mm). A size never goes below 1 mm.
|
|
113
113
|
*/
|
|
114
114
|
export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResult, sizes: Record<string, number>): V3InputChange[] {
|
|
115
115
|
const changes: V3InputChange[] = []
|
|
@@ -4,7 +4,7 @@ import { html, nothing, type ReactiveController, type ReactiveControllerHost } f
|
|
|
4
4
|
import { i18next } from '@operato/i18n'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Help toggles: a small 「?」 beside a title opens the longer explanation under it. Explanations left open on every
|
|
8
8
|
* section made the side read as clutter (user, 2026-09-24); the author opens the one they want. What was opened stays
|
|
9
9
|
* open while the author moves between parts.
|
|
10
10
|
*/
|
|
@@ -4,8 +4,7 @@ import type { V3Asset } from '@hatiolab/figure-model'
|
|
|
4
4
|
import type { V3RenderPart } from './v3-renderer.js'
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
|
-
* Where the selected part turns, and how far -- a translucent fan on the editing board
|
|
8
|
-
* 3.38). An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and
|
|
7
|
+
* Where the selected part turns, and how far -- a translucent fan on the editing board. An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and
|
|
9
8
|
* pushed each slider to its end to find out. The fan lies in the plane the part turns in, at the pivot, open from the
|
|
10
9
|
* control's lowest value to its highest; the edge at the value the figure holds now is where the part is.
|
|
11
10
|
*
|
|
@@ -15,7 +14,7 @@ import type { V3RenderPart } from './v3-renderer.js'
|
|
|
15
14
|
*
|
|
16
15
|
* Two ways a part turns are read:
|
|
17
16
|
* - a joint record (ADR-0093): at the joint's origin, about its axis;
|
|
18
|
-
* - a joint
|
|
17
|
+
* - a joint written as values (`<joint>.motion` · `<joint>.chain`): the part is composed on the joint's
|
|
19
18
|
* chain, the pivot is the joint's origin (`<joint>.frameOnParent`).
|
|
20
19
|
* A slide has no fan. Nothing is drawn when the turn cannot be found -- a fan in some guessed plane would point the
|
|
21
20
|
* wrong way.
|
|
@@ -117,7 +116,7 @@ function turnOf(asset: V3Asset, part: string, values: Record<string, unknown>, b
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
/*
|
|
120
|
-
|
|
119
|
+
A joint written as values (`<joint>.motion`, its origin `<joint>.frameOnParent`) carries its own part and
|
|
121
120
|
everything fastened below it, through its chain (the first joint of a chain has only its attachment). A part shows
|
|
122
121
|
the joint it is to that part of the mechanism (user, 2026-09-24: 「elbow-housing itself is a axis」 · 「please check
|
|
123
122
|
all that logic and concept」), tested in this order on the joints as they stand now:
|
|
@@ -222,7 +221,7 @@ export function jointFanOf(asset: V3Asset, part: string, values: Record<string,
|
|
|
222
221
|
const inPlane = sub(rel, turn.axis.map(v => v * dot(rel, turn.axis)))
|
|
223
222
|
const d = norm(inPlane) ?? norm(cross(turn.axis, Math.abs(turn.axis[0]!) < 0.9 ? [1, 0, 0] : [0, 1, 0]))!
|
|
224
223
|
/*
|
|
225
|
-
Sized from what the joint turns,
|
|
224
|
+
Sized from what the joint turns, not from the selected part: selecting the elbow housing or the forearm
|
|
226
225
|
shows the elbow's one fan, not a smaller one for the smaller part (user, 2026-09-24).
|
|
227
226
|
*/
|
|
228
227
|
const radius = Math.max(60, Math.round(Math.max(largestMeasureOf(asset, turn.turnedPart, inputs), Math.hypot(...inPlane)) * 0.6))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* The modeller's keys
|
|
2
|
+
* The modeller's keys: undo and redo on Ctrl+Z / Ctrl+Y, and what a person reaches for next. Read apart
|
|
3
3
|
* from the element so the rule is tested without a browser.
|
|
4
4
|
*
|
|
5
5
|
* Typing is never taken: a key pressed in a field belongs to the field -- Backspace in a name box deletes a letter,
|
|
@@ -3,8 +3,7 @@ import type { V3SizeRange } from '@hatiolab/figure-model'
|
|
|
3
3
|
import type { V3Asset, V3BindingPlace, V3Face, V3Joint } from '@hatiolab/figure-model'
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
|
-
* What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections
|
|
7
|
-
* ④ ⑤ ⑥). Kept apart from the element so they can be tested without a browser.
|
|
6
|
+
* What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections. Kept apart from the element so they can be tested without a browser.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
export type Axis = 'x' | 'y' | 'z'
|
|
@@ -22,11 +21,11 @@ const modelOf = (asset: V3Asset) =>
|
|
|
22
21
|
/** What one dimension of a part is worked out from. */
|
|
23
22
|
export type Follow =
|
|
24
23
|
| { of: 'own' }
|
|
25
|
-
/** `times` 1 is the instance size itself; any other is a share of it
|
|
24
|
+
/** `times` 1 is the instance size itself; any other is a share of it. */
|
|
26
25
|
| { of: 'instance'; axis: Axis; times: number; plus?: number }
|
|
27
26
|
| { of: 'part'; part: string; axis: Axis; times?: number; plus?: number }
|
|
28
27
|
| { of: 'input'; input: string }
|
|
29
|
-
/** Worked out some other way (
|
|
28
|
+
/** Worked out some other way (an expression) — shown, not offered as a choice. */
|
|
30
29
|
| { of: 'expression' }
|
|
31
30
|
|
|
32
31
|
/**
|
|
@@ -38,7 +37,7 @@ export function followOf(asset: V3Asset, part: string, axis: Axis): Follow {
|
|
|
38
37
|
const m = modelOf(asset)
|
|
39
38
|
/*
|
|
40
39
|
A link routes the dimension through `<part>.dimension.<axis>`; before any link the shape reads its measure directly,
|
|
41
|
-
and that may already be worked out
|
|
40
|
+
and that may already be worked out (ROBOT_ARM's flange fills what is left).
|
|
42
41
|
*/
|
|
43
42
|
const node = m.nodes.find(n => n.id === `${part}.dimension.${axis}`)
|
|
44
43
|
const start = node ? node.args[0] : m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]
|
|
@@ -79,8 +78,8 @@ export function followOf(asset: V3Asset, part: string, axis: Axis): Follow {
|
|
|
79
78
|
const onward = writer.args.filter(arg => !constant(arg))
|
|
80
79
|
if (onward.length !== 1) return { of: 'expression' }
|
|
81
80
|
/*
|
|
82
|
-
Only a product is a share of its source. A sum with a number is not (
|
|
83
|
-
|
|
81
|
+
Only a product is a share of its source. A sum with a number is not (「fill what is left」 is the size plus a negative
|
|
82
|
+
gap -- ROBOT_ARM's tool flange), nor anything else; those read as worked out some other way.
|
|
84
83
|
*/
|
|
85
84
|
const factors = writer.args.filter(arg => constant(arg)).map(arg => constant(arg)!.value)
|
|
86
85
|
if (writer.op === 'mul@1') times *= factors.reduce((a, b) => a * b, 1)
|
|
@@ -118,8 +117,7 @@ export function canFollow(asset: V3Asset, part: string): boolean {
|
|
|
118
117
|
|
|
119
118
|
/**
|
|
120
119
|
* The axes whose size can follow something, by the shape's own measures: a box's X · Y · Z; a cylinder's radius (read
|
|
121
|
-
* as X) and length (Y) -- its shape takes those two, so it has no Z of its own
|
|
122
|
-
* two as well).
|
|
120
|
+
* as X) and length (Y) -- its shape takes those two, so it has no Z of its own.
|
|
123
121
|
*/
|
|
124
122
|
export function followableAxes(asset: V3Asset, part: string): Axis[] {
|
|
125
123
|
const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)
|
|
@@ -234,8 +232,7 @@ export function alignOf(
|
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
/**
|
|
237
|
-
* The axes on which the part's position is worked out from other values rather than being its own number
|
|
238
|
-
* converter writes V2's anchor rules that way. `fix-position` makes one its own number where it is now.
|
|
235
|
+
* The axes on which the part's position is worked out from other values rather than being its own number. `fix-position` makes one its own number where it is now.
|
|
239
236
|
*/
|
|
240
237
|
export function positionsWorkedOut(asset: V3Asset, part: string): Axis[] {
|
|
241
238
|
const m = modelOf(asset)
|
|
@@ -417,7 +414,7 @@ export const acceptsOf = (typed: string) => [...new Set(typed.split(',').map(one
|
|
|
417
414
|
|
|
418
415
|
/**
|
|
419
416
|
* The sizes a board may give this figure: the range it declares now, and the range measured afresh -- the default
|
|
420
|
-
* (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by
|
|
417
|
+
* (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by figure-model's own functions.
|
|
421
418
|
* A range measured when a part filled what the others left (ROBOT_ARM: Y from 1,854 mm) stays narrow after the part is
|
|
422
419
|
* made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.
|
|
423
420
|
*/
|