@things-factory/figure-ui 10.1.25 → 10.1.27
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 +4 -1
- package/client/modeller/figure-animations.ts +229 -29
- package/client/modeller/figure-history-panel.ts +2 -2
- package/client/modeller/figure-inspector.ts +151 -5
- package/client/modeller/figure-preview.ts +5 -5
- package/client/modeller/figure-source.ts +33 -45
- package/client/modeller/figure-view.ts +20 -0
- package/client/modeller/joint-edits.ts +201 -0
- package/client/modeller/proposal-session.ts +8 -2
- package/client/pages/figure-modeller-page.ts +50 -15
- package/dist-client/graphql/index.js +4 -1
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-animations.d.ts +19 -0
- package/dist-client/modeller/figure-animations.js +206 -22
- package/dist-client/modeller/figure-animations.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.js +2 -2
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-inspector.d.ts +11 -0
- package/dist-client/modeller/figure-inspector.js +124 -4
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-preview.js +5 -5
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-source.d.ts +12 -1
- package/dist-client/modeller/figure-source.js +13 -40
- package/dist-client/modeller/figure-source.js.map +1 -1
- package/dist-client/modeller/figure-view.d.ts +16 -0
- package/dist-client/modeller/figure-view.js +15 -0
- package/dist-client/modeller/figure-view.js.map +1 -1
- package/dist-client/modeller/joint-edits.d.ts +29 -0
- package/dist-client/modeller/joint-edits.js +175 -0
- package/dist-client/modeller/joint-edits.js.map +1 -0
- package/dist-client/modeller/proposal-session.d.ts +5 -0
- package/dist-client/modeller/proposal-session.js +3 -2
- package/dist-client/modeller/proposal-session.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +2 -22
- package/dist-client/pages/figure-modeller-page.js +46 -15
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/test/ai-proposal-contract.test.ts +3 -2
- package/test/figure-source.test.ts +43 -57
- package/test/i18n-prefix-guard.test.ts +2 -3
- package/test/joint-edits.test.ts +130 -0
- package/test/preview-board-depth.test.ts +27 -0
- package/translations/en.json +119 -80
- package/translations/ja.json +208 -58
- package/translations/ko.json +139 -100
- package/translations/ms.json +206 -56
- package/translations/zh.json +207 -57
|
@@ -6,10 +6,23 @@ import { live } from 'lit/directives/live.js';
|
|
|
6
6
|
import { i18next, localize } from '@operato/i18n';
|
|
7
7
|
import { ScrollbarStyles } from '@operato/styles';
|
|
8
8
|
import { EXTRUDE_PRIMITIVES, MATERIAL_PRESETS, PRIMITIVE_KINDS, SEGMENTED_PRIMITIVES, SEGMENT_PRESETS, SIZING_RULES, trianglesOf } from '@hatiolab/figure-model';
|
|
9
|
-
import { ANCHOR_RULES, PART_ROLES, REPEAT_LIMIT, anchorOfPart, repeatPlanOfPart } from '@hatiolab/figure-model';
|
|
9
|
+
import { ANCHOR_RULES, JOINT_TYPES, PART_ROLES, REPEAT_LIMIT, anchorOfPart, repeatPlanOfPart } from '@hatiolab/figure-model';
|
|
10
10
|
import { activePalette } from '@hatiolab/things-scene';
|
|
11
11
|
import { partToFigure, partFromFigure } from './figure-source.js';
|
|
12
12
|
import { canMirror } from './part-edits.js';
|
|
13
|
+
import { jointOf, parentChoices, patchJoint, setJointType } from './joint-edits.js';
|
|
14
|
+
/** Axis choices for a joint. Written as a direction the author reads, stored as a vector. */
|
|
15
|
+
const JOINT_AXES = [
|
|
16
|
+
{ key: '+x', axis: { x: 1, y: 0, z: 0 } },
|
|
17
|
+
{ key: '-x', axis: { x: -1, y: 0, z: 0 } },
|
|
18
|
+
{ key: '+y', axis: { x: 0, y: 1, z: 0 } },
|
|
19
|
+
{ key: '-y', axis: { x: 0, y: -1, z: 0 } },
|
|
20
|
+
{ key: '+z', axis: { x: 0, y: 0, z: 1 } },
|
|
21
|
+
{ key: '-z', axis: { x: 0, y: 0, z: -1 } }
|
|
22
|
+
];
|
|
23
|
+
function axisKeyOf(axis) {
|
|
24
|
+
return JOINT_AXES.find(one => one.axis.x === axis.x && one.axis.y === axis.y && one.axis.z === axis.z)?.key;
|
|
25
|
+
}
|
|
13
26
|
/**
|
|
14
27
|
* 고른 부품의 속성.
|
|
15
28
|
*
|
|
@@ -796,7 +809,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
796
809
|
묶음은 아예 나오지 않는다 — 해 둬도 아무 일도 안 일어나는 칸을 보여 주지 않는다.
|
|
797
810
|
*/
|
|
798
811
|
return html `
|
|
799
|
-
${this.renderFigure(true)} ${this.renderIdentity(shown)} ${this.renderRole(shown)}
|
|
812
|
+
${this.renderFigure(true)} ${this.renderIdentity(shown)} ${this.renderRole(shown)} ${this.renderJoint(shown)}
|
|
800
813
|
${this.renderPlacement(shown)} ${this.renderShape(shown)} ${this.renderMaterial(shown)}
|
|
801
814
|
${this.renderBehaviour(shown)}
|
|
802
815
|
`;
|
|
@@ -855,6 +868,107 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
855
868
|
</section>
|
|
856
869
|
`;
|
|
857
870
|
}
|
|
871
|
+
/**
|
|
872
|
+
* How this part hangs from another and moves relative to it (ADR-0066).
|
|
873
|
+
*
|
|
874
|
+
* The parent is written on the part. The joint says how the part moves relative to that parent, and the
|
|
875
|
+
* parameter it comes with is what drives it; the preview shows that parameter as a slider.
|
|
876
|
+
*/
|
|
877
|
+
renderJoint(part) {
|
|
878
|
+
const joint = jointOf(this.draft, part.name);
|
|
879
|
+
const choices = parentChoices(this.parts, part.name);
|
|
880
|
+
const unit = joint?.type === 'prismatic' ? 'mm' : 'deg';
|
|
881
|
+
const axisKey = joint ? axisKeyOf(joint.axis) : undefined;
|
|
882
|
+
const driver = joint
|
|
883
|
+
? this.draft?.parameters?.find(one => one.clip?.channels?.some(channel => channel.target === joint.name))
|
|
884
|
+
: undefined;
|
|
885
|
+
return html `
|
|
886
|
+
<section>
|
|
887
|
+
<h3>${i18next.t('figure.label.joint')}${this.help('figure.text.joint-explained')}</h3>
|
|
888
|
+
|
|
889
|
+
<div inline-field>
|
|
890
|
+
<label>${i18next.t('figure.label.joint-parent')}</label>
|
|
891
|
+
<select @change=${(e) => this.setParent(e.target.value)}>
|
|
892
|
+
<option value="" ?selected=${part.parent === undefined}>${i18next.t('figure.label.joint-parent-none')}</option>
|
|
893
|
+
${part.parent !== undefined && !choices.includes(part.parent)
|
|
894
|
+
? html `<option value=${part.parent} selected>${part.parent}</option>`
|
|
895
|
+
: nothing}
|
|
896
|
+
${choices.map(name => html `<option value=${name} ?selected=${name === part.parent}>${name}</option>`)}
|
|
897
|
+
</select>
|
|
898
|
+
</div>
|
|
899
|
+
|
|
900
|
+
<div inline-field>
|
|
901
|
+
<label>${i18next.t('figure.label.joint-type')}</label>
|
|
902
|
+
<select @change=${(e) => this.setJointType(part, e.target.value)}>
|
|
903
|
+
<option value="" ?selected=${!joint}>${i18next.t('figure.label.joint-type-none')}</option>
|
|
904
|
+
${JOINT_TYPES.map(type => html `<option value=${type} ?selected=${joint?.type === type}>${i18next.t('figure.label.joint-type-' + type)}</option>`)}
|
|
905
|
+
</select>
|
|
906
|
+
</div>
|
|
907
|
+
|
|
908
|
+
${joint
|
|
909
|
+
? html `
|
|
910
|
+
<div inline-field>
|
|
911
|
+
<label>${i18next.t('figure.label.joint-axis')}</label>
|
|
912
|
+
<select @change=${(e) => this.setJointAxis(joint.name, e.target.value)}>
|
|
913
|
+
${axisKey === undefined
|
|
914
|
+
? html `<option value="" selected>${joint.axis.x}, ${joint.axis.y}, ${joint.axis.z}</option>`
|
|
915
|
+
: nothing}
|
|
916
|
+
${JOINT_AXES.map(one => html `<option value=${one.key} ?selected=${one.key === axisKey}>${one.key.toUpperCase()}</option>`)}
|
|
917
|
+
</select>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<div vector-row>
|
|
921
|
+
<label>${i18next.t('figure.label.joint-origin')}</label>
|
|
922
|
+
<div triple>
|
|
923
|
+
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), joint.origin[axis], value => this.tellDraft(patchJoint(this.draft, joint.name, { origin: { ...joint.origin, [axis]: value } }))))}
|
|
924
|
+
</div>
|
|
925
|
+
</div>
|
|
926
|
+
|
|
927
|
+
${joint.limits
|
|
928
|
+
? html `
|
|
929
|
+
<div vector-row>
|
|
930
|
+
<label>${i18next.t('figure.label.joint-limits-' + unit)}</label>
|
|
931
|
+
<div triple>
|
|
932
|
+
${this.number(i18next.t('figure.label.joint-limit-min'), joint.limits.min, value => this.tellDraft(patchJoint(this.draft, joint.name, { limits: { ...joint.limits, min: value } })))}
|
|
933
|
+
${this.number(i18next.t('figure.label.joint-limit-max'), joint.limits.max, value => this.tellDraft(patchJoint(this.draft, joint.name, { limits: { ...joint.limits, max: value } })))}
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
${joint.limits.min > 0 || joint.limits.max < 0
|
|
937
|
+
? html `<p warn>${i18next.t('figure.text.joint-limits-must-include-zero')}</p>`
|
|
938
|
+
: nothing}
|
|
939
|
+
`
|
|
940
|
+
: nothing}
|
|
941
|
+
|
|
942
|
+
<p quiet>
|
|
943
|
+
${driver
|
|
944
|
+
? i18next.t('figure.text.joint-driven-by', { name: driver.label ?? driver.name })
|
|
945
|
+
: i18next.t('figure.text.joint-not-driven')}
|
|
946
|
+
</p>
|
|
947
|
+
`
|
|
948
|
+
: nothing}
|
|
949
|
+
</section>
|
|
950
|
+
`;
|
|
951
|
+
}
|
|
952
|
+
setParent(name) {
|
|
953
|
+
this.change(part => {
|
|
954
|
+
const { parent: _dropped, ...rest } = part;
|
|
955
|
+
return name === '' ? rest : { ...rest, parent: name };
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
setJointType(part, value) {
|
|
959
|
+
if (!this.draft)
|
|
960
|
+
return;
|
|
961
|
+
this.tellDraft(setJointType(this.draft, part, value === '' ? undefined : value));
|
|
962
|
+
}
|
|
963
|
+
setJointAxis(name, key) {
|
|
964
|
+
const choice = JOINT_AXES.find(one => one.key === key);
|
|
965
|
+
if (!choice || !this.draft)
|
|
966
|
+
return;
|
|
967
|
+
this.tellDraft(patchJoint(this.draft, name, { axis: { ...choice.axis } }));
|
|
968
|
+
}
|
|
969
|
+
tellDraft(draft) {
|
|
970
|
+
this.dispatchEvent(new CustomEvent('update-draft', { detail: { draft }, bubbles: true, composed: true }));
|
|
971
|
+
}
|
|
858
972
|
/**
|
|
859
973
|
* 역할 하나를 켜고 끈다.
|
|
860
974
|
*
|
|
@@ -1600,8 +1714,14 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1600
1714
|
return this.opened.has(key) ? html `<p hint>${i18next.t(key)}</p>` : nothing;
|
|
1601
1715
|
}
|
|
1602
1716
|
renderAnchors(part) {
|
|
1717
|
+
/*
|
|
1718
|
+
The scene draft stores the box height as depth, so format y is draft.depth and z is draft.height.
|
|
1719
|
+
This used to pass them swapped, and every automatic anchor still came out the same: the
|
|
1720
|
+
nearest-face rule reads the sign of a centred position, which does not depend on the box size.
|
|
1721
|
+
It is fixed so the next rule that does read the box does not inherit the mistake.
|
|
1722
|
+
*/
|
|
1603
1723
|
const base = this.draft
|
|
1604
|
-
? { x: this.draft.width, y: this.draft.
|
|
1724
|
+
? { x: this.draft.width, y: this.draft.depth, z: this.draft.height }
|
|
1605
1725
|
: { x: 0, y: 0, z: 0 };
|
|
1606
1726
|
const nameOf = (rule, axis) => `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`;
|
|
1607
1727
|
const setAnchor = (axis, rule) => {
|
|
@@ -1645,7 +1765,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1645
1765
|
*/
|
|
1646
1766
|
return html `
|
|
1647
1767
|
<div anchor-row>
|
|
1648
|
-
<label>${axis.toUpperCase()}
|
|
1768
|
+
<label>${i18next.t('figure.label.axis-name', { axis: axis.toUpperCase() })}</label>
|
|
1649
1769
|
<div anchor-options>
|
|
1650
1770
|
${['auto', 'scale', 'min', 'center', 'max', 'span'].map(rule => {
|
|
1651
1771
|
const selected = rule === 'auto' ? !said : rule === said;
|