@things-factory/figure-ui 10.1.58 → 10.1.59
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/modeller/v3-editor.ts +15 -3
- package/client/modeller/v3-links.ts +27 -1
- package/dist-client/modeller/v3-clips.d.ts +1 -1
- package/dist-client/modeller/v3-editor.js +15 -4
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +11 -1
- package/dist-client/modeller/v3-links.js +29 -1
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/browser/modeller.check.mjs +36 -0
- package/test/v3-links.test.ts +21 -1
- package/translations/en.json +4 -1
- package/translations/ja.json +4 -1
- package/translations/ko.json +4 -1
- package/translations/ms.json +4 -1
- package/translations/zh.json +4 -1
|
@@ -32,7 +32,7 @@ import { nudgeOf } from './v3-stage-model.js'
|
|
|
32
32
|
import { narrowingRetryOf, partRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js'
|
|
33
33
|
import type { V3RenderPart } from './v3-renderer.js'
|
|
34
34
|
import type { V3BudgetLine } from './v3-maturity.js'
|
|
35
|
-
import { instanceRangeOf, instanceRangeToDeclare, rangeDiffers, rangeWith, turnsAboutAJoint } from './v3-links.js'
|
|
35
|
+
import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './v3-links.js'
|
|
36
36
|
import { jointFanOf } from './v3-joint-fan.js'
|
|
37
37
|
import { jointDragOf } from './v3-joint-drag.js'
|
|
38
38
|
import type { FirstSave } from './v3-first-save.js'
|
|
@@ -804,7 +804,18 @@ export class FigureV3Editor extends LitElement {
|
|
|
804
804
|
* it is offered it -- a mechanism does not shear -- and the author decides (chief architect's ruling 2026-09-24).
|
|
805
805
|
*/
|
|
806
806
|
private renderSizingRule(asset: V3Asset, unavailable: boolean) {
|
|
807
|
-
if (asset.sizing === 'uniform')
|
|
807
|
+
if (asset.sizing === 'uniform') {
|
|
808
|
+
/* One ratio reaches only what reads the size: every other length is offered to follow it, in one step. */
|
|
809
|
+
const follow = proportionToFollow(asset)
|
|
810
|
+
return html`<div inline-field><label>${i18next.t('figure.label.sizing-rule')}</label><span>${i18next.t('figure.label.sizing-uniform')}</span></div>
|
|
811
|
+
${follow?.count
|
|
812
|
+
? html`<p hint>${i18next.t('figure.text.proportion-to-follow', { count: follow.count })}</p>
|
|
813
|
+
<button act ?disabled=${unavailable} @click=${() => this.linkCommand({ kind: 'follow-size-in-proportion' })}>
|
|
814
|
+
<md-icon>open_in_full</md-icon>${i18next.t('figure.button.follow-size-in-proportion')}
|
|
815
|
+
</button>`
|
|
816
|
+
: nothing}
|
|
817
|
+
${follow?.kept.length ? html`<p hint>${i18next.t('figure.text.proportion-kept', { what: follow.kept.join(', ') })}</p>` : nothing}`
|
|
818
|
+
}
|
|
808
819
|
return html`${turnsAboutAJoint(asset) ? html`<p hint>${i18next.t('figure.text.sizing-uniform-proposed')}</p>` : nothing}
|
|
809
820
|
<button act ?disabled=${unavailable} title=${i18next.t('figure.text.sizing-uniform-explained')} @click=${() => this.linkCommand({ kind: 'scale-uniformly' })}>
|
|
810
821
|
<md-icon>aspect_ratio</md-icon>${i18next.t('figure.button.scale-uniformly')}
|
|
@@ -858,7 +869,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
858
869
|
const layout = model.nodes.find(n => n.id === `${this.selected}.layout`)
|
|
859
870
|
const axis = model.nodes.find(n => n.id === `${this.selected}.repeat`)?.params?.axis ?? '?'
|
|
860
871
|
const own = `${this.selected}.repeat.pitch`
|
|
861
|
-
|
|
872
|
+
/* The pitch as it stands: the part's own number, a constant, or what a pitch that follows a length comes to. */
|
|
873
|
+
const pitch = asset.designInputs[own] ?? model.constants.find(c => c.id === layout?.args[4])?.value ?? valueNow(asset, layout?.args[4])
|
|
862
874
|
const fits = layout?.op === 'fit-pitch@1'
|
|
863
875
|
/* What the pitch gives, in numbers: this many copies at the length it is authored, and how long it may be placed. */
|
|
864
876
|
const lengthRef = layout?.args[0] ?? ''
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V3_FACES, v3ShapeFaces, v3PartsExtentOf, compileV3Graph, defaultInstanceSizeRange, foldInstanceSizeRange, v3AttachmentsOf, v3BindingsOf, v3InstanceSizeLimitsOf } from '@hatiolab/figure-model'
|
|
1
|
+
import { V3_FACES, v3ShapeFaces, v3PartsExtentOf, compileV3Graph, proposeV3Proportion, defaultInstanceSizeRange, foldInstanceSizeRange, v3AttachmentsOf, v3BindingsOf, v3InstanceSizeLimitsOf } from '@hatiolab/figure-model'
|
|
2
2
|
import type { V3SizeRange } from '@hatiolab/figure-model'
|
|
3
3
|
import type { V3Asset, V3BindingPlace, V3Face, V3Joint } from '@hatiolab/figure-model'
|
|
4
4
|
|
|
@@ -454,6 +454,32 @@ export function rangeWith(declared: V3SizeRange, axis: Axis, bound: 'min' | 'max
|
|
|
454
454
|
return next
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
/**
|
|
458
|
+
* For a figure that grows by one ratio: how many of its lengths would follow the ratio, and which it leaves with their
|
|
459
|
+
* own number (not those that follow another length -- they grow with it). Nothing for a figure of another sizing.
|
|
460
|
+
*/
|
|
461
|
+
export function proportionToFollow(asset: V3Asset): { count: number; kept: string[] } | undefined {
|
|
462
|
+
if (asset.sizing !== 'uniform') return undefined
|
|
463
|
+
try {
|
|
464
|
+
const plan = proposeV3Proportion(asset)
|
|
465
|
+
return { count: plan.dimensions.length + plan.lengths.length, kept: plan.skipped.filter(s => !s.follows).map(s => s.what) }
|
|
466
|
+
} catch {
|
|
467
|
+
return undefined
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** A value of the figure as it stands, by its graph reference. */
|
|
472
|
+
export function valueNow(asset: V3Asset, ref: string | undefined): number | undefined {
|
|
473
|
+
if (!ref) return undefined
|
|
474
|
+
try {
|
|
475
|
+
const values = compileV3Graph(asset.document.model as never).evaluate({ ...asset.designInputs, ...asset.stateDefaults }).values as Record<string, number>
|
|
476
|
+
const value = values[ref]
|
|
477
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
478
|
+
} catch {
|
|
479
|
+
return undefined
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
457
483
|
export function rangeDiffers(a: V3SizeRange, b: V3SizeRange): boolean {
|
|
458
484
|
return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3)
|
|
459
485
|
}
|
|
@@ -27,7 +27,7 @@ import { alignChanges, boxOf, canMirror, mirrorChanges } from './v3-align.js';
|
|
|
27
27
|
import { paletteRecipe } from './v3-palette.js';
|
|
28
28
|
import { nudgeOf } from './v3-stage-model.js';
|
|
29
29
|
import { narrowingRetryOf, partRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js';
|
|
30
|
-
import { instanceRangeOf, instanceRangeToDeclare, rangeDiffers, rangeWith, turnsAboutAJoint } from './v3-links.js';
|
|
30
|
+
import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './v3-links.js';
|
|
31
31
|
import { jointFanOf } from './v3-joint-fan.js';
|
|
32
32
|
import { jointDragOf } from './v3-joint-drag.js';
|
|
33
33
|
import { liveOverSession, NOTHING_LIVE, scrubbed, scrubStep } from './v3-live.js';
|
|
@@ -816,8 +816,18 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
|
|
|
816
816
|
* it is offered it -- a mechanism does not shear -- and the author decides (chief architect's ruling 2026-09-24).
|
|
817
817
|
*/
|
|
818
818
|
renderSizingRule(asset, unavailable) {
|
|
819
|
-
if (asset.sizing === 'uniform')
|
|
820
|
-
|
|
819
|
+
if (asset.sizing === 'uniform') {
|
|
820
|
+
/* One ratio reaches only what reads the size: every other length is offered to follow it, in one step. */
|
|
821
|
+
const follow = proportionToFollow(asset);
|
|
822
|
+
return html `<div inline-field><label>${i18next.t('figure.label.sizing-rule')}</label><span>${i18next.t('figure.label.sizing-uniform')}</span></div>
|
|
823
|
+
${follow?.count
|
|
824
|
+
? html `<p hint>${i18next.t('figure.text.proportion-to-follow', { count: follow.count })}</p>
|
|
825
|
+
<button act ?disabled=${unavailable} @click=${() => this.linkCommand({ kind: 'follow-size-in-proportion' })}>
|
|
826
|
+
<md-icon>open_in_full</md-icon>${i18next.t('figure.button.follow-size-in-proportion')}
|
|
827
|
+
</button>`
|
|
828
|
+
: nothing}
|
|
829
|
+
${follow?.kept.length ? html `<p hint>${i18next.t('figure.text.proportion-kept', { what: follow.kept.join(', ') })}</p>` : nothing}`;
|
|
830
|
+
}
|
|
821
831
|
return html `${turnsAboutAJoint(asset) ? html `<p hint>${i18next.t('figure.text.sizing-uniform-proposed')}</p>` : nothing}
|
|
822
832
|
<button act ?disabled=${unavailable} title=${i18next.t('figure.text.sizing-uniform-explained')} @click=${() => this.linkCommand({ kind: 'scale-uniformly' })}>
|
|
823
833
|
<md-icon>aspect_ratio</md-icon>${i18next.t('figure.button.scale-uniformly')}
|
|
@@ -870,7 +880,8 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
|
|
|
870
880
|
const layout = model.nodes.find(n => n.id === `${this.selected}.layout`);
|
|
871
881
|
const axis = model.nodes.find(n => n.id === `${this.selected}.repeat`)?.params?.axis ?? '?';
|
|
872
882
|
const own = `${this.selected}.repeat.pitch`;
|
|
873
|
-
|
|
883
|
+
/* The pitch as it stands: the part's own number, a constant, or what a pitch that follows a length comes to. */
|
|
884
|
+
const pitch = asset.designInputs[own] ?? model.constants.find(c => c.id === layout?.args[4])?.value ?? valueNow(asset, layout?.args[4]);
|
|
874
885
|
const fits = layout?.op === 'fit-pitch@1';
|
|
875
886
|
/* What the pitch gives, in numbers: this many copies at the length it is authored, and how long it may be placed. */
|
|
876
887
|
const lengthRef = layout?.args[0] ?? '';
|