@things-factory/figure-ui 10.1.91 → 10.1.93

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.
Files changed (56) hide show
  1. package/client/modeller/editor.ts +61 -90
  2. package/client/modeller/fasten-geometry.ts +152 -0
  3. package/client/modeller/fields.ts +8 -1
  4. package/client/modeller/figure-cavity-drawing.ts +259 -0
  5. package/client/modeller/figure-fasten-drawing.ts +406 -0
  6. package/client/modeller/figure-links.ts +71 -35
  7. package/client/modeller/figure-section-close-choice.ts +52 -0
  8. package/client/modeller/figure-section-editor.ts +58 -3
  9. package/client/modeller/figure-section-panel.ts +5 -0
  10. package/client/modeller/section-controls.ts +19 -0
  11. package/client/modeller/side-panel-styles.ts +4 -11
  12. package/dist-client/modeller/editor.d.ts +10 -4
  13. package/dist-client/modeller/editor.js +59 -88
  14. package/dist-client/modeller/editor.js.map +1 -1
  15. package/dist-client/modeller/fasten-geometry.d.ts +60 -0
  16. package/dist-client/modeller/fasten-geometry.js +138 -0
  17. package/dist-client/modeller/fasten-geometry.js.map +1 -0
  18. package/dist-client/modeller/fields.js +8 -1
  19. package/dist-client/modeller/fields.js.map +1 -1
  20. package/dist-client/modeller/figure-cavity-drawing.d.ts +48 -0
  21. package/dist-client/modeller/figure-cavity-drawing.js +253 -0
  22. package/dist-client/modeller/figure-cavity-drawing.js.map +1 -0
  23. package/dist-client/modeller/figure-fasten-drawing.d.ts +74 -0
  24. package/dist-client/modeller/figure-fasten-drawing.js +452 -0
  25. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -0
  26. package/dist-client/modeller/figure-links.d.ts +7 -0
  27. package/dist-client/modeller/figure-links.js +70 -33
  28. package/dist-client/modeller/figure-links.js.map +1 -1
  29. package/dist-client/modeller/figure-section-close-choice.d.ts +17 -0
  30. package/dist-client/modeller/figure-section-close-choice.js +58 -0
  31. package/dist-client/modeller/figure-section-close-choice.js.map +1 -0
  32. package/dist-client/modeller/figure-section-editor.d.ts +11 -1
  33. package/dist-client/modeller/figure-section-editor.js +57 -3
  34. package/dist-client/modeller/figure-section-editor.js.map +1 -1
  35. package/dist-client/modeller/figure-section-panel.d.ts +1 -0
  36. package/dist-client/modeller/figure-section-panel.js +5 -0
  37. package/dist-client/modeller/figure-section-panel.js.map +1 -1
  38. package/dist-client/modeller/section-controls.d.ts +10 -0
  39. package/dist-client/modeller/section-controls.js +17 -0
  40. package/dist-client/modeller/section-controls.js.map +1 -1
  41. package/dist-client/modeller/side-panel-styles.js +4 -11
  42. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  43. package/dist-client/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +2 -2
  45. package/test/browser/acceptance-page.js +10 -0
  46. package/test/cavity-drawing.test.ts +69 -0
  47. package/test/fasten-drawing.test.ts +45 -0
  48. package/test/fasten-geometry.test.ts +79 -0
  49. package/test/layout.test.ts +19 -4
  50. package/test/links.test.ts +3 -1
  51. package/test/section-editor.test.ts +31 -8
  52. package/translations/en.json +29 -15
  53. package/translations/ja.json +25 -11
  54. package/translations/ko.json +29 -15
  55. package/translations/ms.json +25 -11
  56. package/translations/zh.json +25 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-cavity-drawing.js","sourceRoot":"","sources":["../../client/modeller/figure-cavity-drawing.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { figureCavitySectionOf } from '@hatiolab/figure-model'\nimport type { FigureAuthoringAction, FigureCavityRequest, FigureEvaluatedShape, FigureSection } from '@hatiolab/figure-model'\nimport { cavityRefusalOf, cavityWorkingOf, END_FACE, endToggled, mm, SIDE_FACE, wallDragged } from './section-controls.js'\n\ntype Axis = 'x' | 'y' | 'z'\ntype End = 'start' | 'end'\ntype Body = { id: string; shape: FigureEvaluatedShape }\n\n/** How the section is laid on the screen, by the axis it is pushed along: across to the right, then down. */\nconst SCREEN: Record<Axis, [Axis, Axis]> = { y: ['x', 'z'], x: ['z', 'y'], z: ['x', 'y'] }\n\n/**\n * 속 공간 그림 — the inside of a pushed part drawn, and edited by drawing (user 2026-10-01: 「이해와 편집이 직관적이지 않다」).\n *\n * Seen along the push, the section and its inside: a handle in the middle of each wall is dragged to its thickness,\n * every wall together unless 면마다 벽 is on. Cut along the push, the walls and the two ends: an end pressed opens or\n * closes it, a closed end's inner line is dragged to its thickness. While dragging, the figure behind shows what the\n * inside makes (`command-live`); letting go sends one `set-cavity`. What the kernel would refuse is drawn in the error\n * colour and said, by the kernel's own check, and is not sent.\n */\n@customElement('figure-cavity-drawing')\nexport class FigureCavityDrawing extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: grid;\n gap: 6px;\n }\n svg {\n width: 100%;\n display: block;\n touch-action: none;\n user-select: none;\n }\n svg text {\n font: inherit;\n }\n small[refusal] {\n color: var(--md-sys-color-error);\n }\n `\n\n @property({ attribute: false }) body?: Body\n @property({ type: Boolean }) locked = false\n /** Walls one per side: a handle moves its own wall only. */\n @property({ type: Boolean }) perSide = false\n\n /** What is being dragged, and the inside it makes; undefined when nothing is. */\n @state() private working?: ReturnType<typeof cavityWorkingOf>\n @state() private refusal = ''\n private dragging: { kind: 'wall'; side: string } | { kind: 'end'; end: End } | null = null\n private pressedAt = 0\n\n private get now() {\n return this.working ?? (this.body ? cavityWorkingOf(this.body.shape) : undefined)\n }\n\n private request(w: NonNullable<ReturnType<typeof cavityWorkingOf>>): FigureCavityRequest {\n return { wall: { ...w.wall }, start: w.start, end: w.end }\n }\n\n private action(w: NonNullable<ReturnType<typeof cavityWorkingOf>>): FigureAuthoringAction {\n return { kind: 'set-cavity', part: this.body!.id, cavity: this.request(w) }\n }\n\n private judge(w: NonNullable<ReturnType<typeof cavityWorkingOf>>): string {\n const ask = cavityRefusalOf(this.body!.shape, this.request(w))\n if (!ask) return ''\n if (ask.code === 'wall-too-thick') return i18next.t('figure.text.section-wall-over', { max: String(ask.max) })\n if (ask.code === 'ends-too-thick') return i18next.t('figure.text.section-ends-over', { max: String(ask.max) })\n return i18next.t('figure.text.section-not-positive')\n }\n\n /** The working inside while dragging: drawn, judged, and previewed behind when the kernel would take it. */\n private hold(w: NonNullable<ReturnType<typeof cavityWorkingOf>>) {\n this.working = w\n this.refusal = this.judge(w)\n this.emit('command-live', this.refusal ? null : this.action(w))\n }\n\n private release() {\n const w = this.working\n this.dragging = null\n this.working = undefined\n if (!w) return\n /* Refused: nothing is sent, and the reason stays said until the next drag -- letting go must not look like taking it. */\n if (this.refusal) {\n this.emit('command-live', null)\n return\n }\n this.emit('command', this.action(w))\n }\n\n private emit(name: 'command' | 'command-live', action: FigureAuthoringAction | null) {\n this.dispatchEvent(new CustomEvent(name, { detail: { action }, bubbles: true, composed: true }))\n }\n\n private point(ev: PointerEvent, el: SVGSVGElement) {\n const pt = el.createSVGPoint()\n pt.x = ev.clientX\n pt.y = ev.clientY\n return pt.matrixTransform(el.getScreenCTM()!.inverse())\n }\n\n render() {\n const e = this.body?.shape.extrusion\n const w = this.now\n if (!e || !w || (e.section.kind !== 'rectangle' && e.section.kind !== 'ellipse')) return nothing\n return html`${this.top(e, w)}${this.side(e, w)}\n ${this.refusal ? html`<small refusal role=\"alert\">${this.refusal}</small>` : nothing}`\n }\n\n /** Seen along the push: the section, its inside and a handle on each wall. */\n private top(e: NonNullable<FigureEvaluatedShape['extrusion']>, w: NonNullable<ReturnType<typeof cavityWorkingOf>>) {\n const [h, v] = SCREEN[e.axis]\n const s = e.section\n const across = (a: Axis) => (s.kind === 'rectangle' ? s.size[a]! : 2 * (s as { radius: Record<string, number> }).radius[a]!)\n const W = 300,\n H = 170,\n pad = 26\n const k = Math.min((W - 2 * pad) / across(h), (H - 2 * pad) / across(v))\n const ox = W / 2,\n oy = H / 2\n const inside = figureCavitySectionOf(s as FigureSection, e.axis, w.wall)\n const bad = !!this.refusal || !('section' in inside)\n const shape = (sec: FigureSection, c: Partial<Record<Axis, number>>, attrs: string) => {\n const cx = ox + (c[h] ?? 0) * k,\n cy = oy + (c[v] ?? 0) * k\n if (sec.kind === 'ellipse') return svg`<ellipse cx=${cx} cy=${cy} rx=${sec.radius[h]! * k} ry=${sec.radius[v]! * k} style=${attrs} />`\n const r = sec as { size: Record<string, number>; corner?: Record<string, number> }\n return svg`<rect x=${cx - (r.size[h]! * k) / 2} y=${cy - (r.size[v]! * k) / 2} width=${r.size[h]! * k} height=${r.size[v]! * k}\n rx=${(r.corner?.[h] ?? 0) * k} ry=${(r.corner?.[v] ?? 0) * k} style=${attrs} />`\n }\n const half = { [h]: (across(h) * k) / 2, [v]: (across(v) * k) / 2 } as Record<Axis, number>\n /* Each wall's handle sits on the inside's edge, half way along it; the value is the wall's own. */\n const handles = [`${h}-`, `${h}+`, `${v}-`, `${v}+`].map(side => {\n const a = side[0] as Axis,\n low = side[1] === '-'\n const edge = (low ? -1 : 1) * (half[a] - w.wall[side]! * k)\n const x = a === h ? ox + edge : ox,\n y = a === v ? oy + edge : oy\n /* The number stands in the wall itself, half way through it; a wall too thin for it has it just outside. */\n const band = w.wall[side]! * k\n const mid = (low ? -1 : 1) * (half[a] - (band < 16 ? -9 : band / 2))\n const tx = a === h ? ox + mid : x + 14,\n ty = a === v ? oy + mid + 4 : y - 10\n return svg`<g wall=${side} style=${`cursor:${a === h ? 'ew-resize' : 'ns-resize'}`}\n @pointerdown=${(ev: PointerEvent) => this.grab(ev, { kind: 'wall', side })}>\n <rect x=${x - 6} y=${y - 6} width=\"12\" height=\"12\" rx=\"2\" style=\"fill:var(--md-sys-color-surface);stroke:var(--md-sys-color-primary);stroke-width:1.5\" />\n <text x=${tx} y=${ty} font-size=\"11\" text-anchor=${a === h ? 'middle' : 'start'} style=\"fill:var(--md-sys-color-on-surface)\">${mm(w.wall[side]!)}</text>\n <title>${i18next.t('figure.label.face-' + SIDE_FACE[side])} ${i18next.t('figure.label.section-wall')}</title>\n </g>`\n })\n return svg`<svg viewBox=${`0 0 ${W} ${H}`} role=\"img\" aria-label=${i18next.t('figure.label.section-cavity-top')}\n @pointermove=${(ev: PointerEvent) => this.drag(ev, e, k, ox, oy, half)}\n @pointerup=${() => this.release()} @pointercancel=${() => this.release()}>\n ${shape(s as FigureSection, {}, 'fill:var(--md-sys-color-primary-container);stroke:var(--md-sys-color-primary);stroke-width:1.5')}\n ${'section' in inside\n ? shape(inside.section, inside.centre, `fill:var(--md-sys-color-surface);stroke:${bad ? 'var(--md-sys-color-error)' : 'var(--md-sys-color-primary)'};stroke-width:1.2;stroke-dasharray:4 3`)\n : nothing}\n ${this.locked ? nothing : handles}\n </svg>`\n }\n\n /** Cut along the push: the walls across the first plane axis, and the two ends -- pressed to open or close. */\n private side(e: NonNullable<FigureEvaluatedShape['extrusion']>, w: NonNullable<ReturnType<typeof cavityWorkingOf>>) {\n const [h] = SCREEN[e.axis]\n const s = e.section\n const width = s.kind === 'rectangle' ? s.size[h]! : 2 * (s as { radius: Record<string, number> }).radius[h]!\n const length = e.end - e.start\n const W = 300,\n H = 170,\n padX = 40,\n padY = 26\n const k = Math.min((W - 2 * padX) / width, (H - 2 * padY) / length)\n const x0 = W / 2 - (width * k) / 2,\n x1 = W / 2 + (width * k) / 2,\n yTop = H / 2 - (length * k) / 2,\n yBot = H / 2 + (length * k) / 2\n const wl = w.wall[`${h}-`]! * k,\n wr = w.wall[`${h}+`]! * k\n const thick = (end: End) => (w[end] === 'open' ? 0 : (w[end] as { closed: number }).closed * k)\n /* The push's start is drawn at the bottom: a tote stands on its floor. */\n const floor = thick('start'),\n lid = thick('end')\n const fill = 'fill:var(--md-sys-color-primary-container);stroke:var(--md-sys-color-primary);stroke-width:1.2'\n const endZone = (end: End, y: number, t: number) => {\n const open = w[end] === 'open'\n const words = open\n ? i18next.t('figure.label.section-end-open')\n : `${i18next.t('figure.label.section-end-closed')} ${mm((w[end] as { closed: number }).closed)}`\n const label = `${i18next.t('figure.label.face-' + END_FACE(e.axis, end))}: ${words}`\n const inner = end === 'start' ? y - t : y + t\n return svg`<g end=${end} style=${`cursor:${open ? 'pointer' : 'ns-resize'}`}\n @pointerdown=${(ev: PointerEvent) => this.grab(ev, { kind: 'end', end })}>\n <rect x=${x0 + wl} y=${Math.min(y, inner) - 8} width=${x1 - x0 - wl - wr} height=${Math.abs(t) + 16} style=\"fill:transparent\" />\n ${open ? svg`<line x1=${x0 + wl} y1=${y} x2=${x1 - wr} y2=${y} style=\"stroke:var(--md-sys-color-outline);stroke-dasharray:3 3\" />` : nothing}\n <text x=${W / 2} y=${end === 'start' ? yBot + 16 : yTop - 8} font-size=\"11\" text-anchor=\"middle\" style=\"fill:var(--md-sys-color-primary)\">${label}</text>\n <title>${label}</title>\n </g>`\n }\n return svg`<svg viewBox=${`0 0 ${W} ${H + 8}`} role=\"img\" aria-label=${i18next.t('figure.label.section-cavity-side')}\n @pointermove=${(ev: PointerEvent) => this.dragEnd(ev, k, yTop, yBot)}\n @pointerup=${(ev: PointerEvent) => this.releaseEnd(ev)} @pointercancel=${() => this.release()}>\n <rect x=${x0} y=${yTop} width=${wl} height=${yBot - yTop} style=${fill} />\n <rect x=${x1 - wr} y=${yTop} width=${wr} height=${yBot - yTop} style=${fill} />\n ${floor ? svg`<rect x=${x0 + wl} y=${yBot - floor} width=${x1 - x0 - wl - wr} height=${floor} style=${fill} />` : nothing}\n ${lid ? svg`<rect x=${x0 + wl} y=${yTop} width=${x1 - x0 - wl - wr} height=${lid} style=${fill} />` : nothing}\n ${this.locked ? nothing : [endZone('start', yBot, floor), endZone('end', yTop, lid)]}\n </svg>`\n }\n\n private grab(ev: PointerEvent, what: NonNullable<FigureCavityDrawing['dragging']>) {\n if (this.locked) return\n ev.stopPropagation()\n ;(ev.currentTarget as Element).closest('svg')!.setPointerCapture(ev.pointerId)\n this.dragging = what\n this.pressedAt = ev.clientY\n this.refusal = ''\n }\n\n private drag(ev: PointerEvent, e: NonNullable<FigureEvaluatedShape['extrusion']>, k: number, ox: number, oy: number, half: Record<Axis, number>) {\n const d = this.dragging\n if (d?.kind !== 'wall') return\n const [h] = SCREEN[e.axis]\n const p = this.point(ev, ev.currentTarget as SVGSVGElement)\n const a = d.side[0] as Axis,\n low = d.side[1] === '-'\n const at = a === h ? p.x - ox : p.y - oy\n const value = (half[a] - (low ? -at : at)) / k\n this.hold({ ...this.now!, wall: wallDragged(this.now!.wall, d.side, value, this.perSide) })\n }\n\n private dragEnd(ev: PointerEvent, k: number, yTop: number, yBot: number) {\n const d = this.dragging\n if (d?.kind !== 'end' || this.now![d.end] === 'open') return\n if (Math.abs(ev.clientY - this.pressedAt) < 3 && !this.working) return\n const p = this.point(ev, ev.currentTarget as SVGSVGElement)\n const t = d.end === 'start' ? (yBot - p.y) / k : (p.y - yTop) / k\n this.hold({ ...this.now!, [d.end]: { closed: Math.max(1, Math.round(t)) } })\n }\n\n /** A press on an end that did not move is a toggle; a drag was a thickness. */\n private releaseEnd(ev: PointerEvent) {\n const d = this.dragging\n if (d?.kind === 'end' && !this.working && Math.abs(ev.clientY - this.pressedAt) < 3) {\n this.dragging = null\n const next = { ...this.now!, [d.end]: endToggled(this.body!.shape, this.now![d.end]) }\n this.refusal = this.judge(next)\n if (!this.refusal) this.emit('command', this.action(next))\n return\n }\n this.release()\n }\n}\n\n"]}
@@ -0,0 +1,74 @@
1
+ import { LitElement } from 'lit';
2
+ import { type FaceView, type FastenMode } from './fasten-geometry.js';
3
+ import type { Alignment } from './links.js';
4
+ type Axis = 'x' | 'y' | 'z';
5
+ /** A change the drawing asks of the fastening form. */
6
+ export type FastenChange = {
7
+ targetFace: string;
8
+ } | {
9
+ mode: FastenMode;
10
+ } | {
11
+ axis: Axis;
12
+ how: Alignment;
13
+ mm?: number;
14
+ } | {
15
+ gap: number;
16
+ };
17
+ declare const FigureFastenDrawing_base: (new (...args: any[]) => LitElement) & typeof LitElement;
18
+ /**
19
+ * 붙이기 그림 (user 2026-10-01) — which face of what the part is fastened to, how, and where on it; each of them chosen
20
+ * by pressing or dragging the drawing, none by a list of faces.
21
+ *
22
+ * The target is drawn as a box whose faces are pressed (the three it hides as tags beside it), or as the one plane the
23
+ * figure stands by. How is three small side views -- set on it, flush with it, its centre on it (only for a part that
24
+ * turns about a joint) -- and the chosen one's gap is dragged. Where is the face seen from above with the part's
25
+ * footprint on it: dragged, caught at the centre and at the edges from inside and outside, a guide drawn where it is
26
+ * caught and a dimension where it is not, which is pressed to type the number. It may go past the edges: the part that
27
+ * does is shaded, and a part off the face altogether is said so. Nothing is decided here: every change goes to the form
28
+ * (`fasten-change`), and a press or a drag let go says so (`fasten-release`), for the form to apply a fastening it holds.
29
+ */
30
+ export declare class FigureFastenDrawing extends FigureFastenDrawing_base {
31
+ static styles: import("lit").CSSResult;
32
+ /** The target's name, or empty for the plane the figure stands by. */
33
+ target: string;
34
+ /** The plane's name by the figure's placement: 바닥 · 천장 · 벽 · 중심. */
35
+ planeWord: string;
36
+ faces: string[];
37
+ face: string;
38
+ modes: FastenMode[];
39
+ mode: FastenMode;
40
+ view?: FaceView;
41
+ align: Partial<Record<Axis, Alignment>>;
42
+ alignMm: Partial<Record<Axis, number>>;
43
+ gap: number;
44
+ /** The part being fastened (not `part`: that is every element's own). */
45
+ partName: string;
46
+ locked: boolean;
47
+ /** The form already says how the fastening it has lies on the face: the drawing says it only while dragging. */
48
+ said: boolean;
49
+ /** Where the footprint is while dragging, and what it is caught by; undefined when nothing is dragged. */
50
+ private dragged?;
51
+ private typing?;
52
+ private scale;
53
+ private gapFrom?;
54
+ private change;
55
+ private released;
56
+ render(): import("lit-html").TemplateResult<1>;
57
+ /** The target box, its faces pressed; or the plane. */
58
+ private renderTarget;
59
+ /** How: three side views; the chosen one's gap is dragged. */
60
+ private renderModes;
61
+ /** Where the part's centre is on each axis now: the drag's, or the form's. */
62
+ private centreOf;
63
+ /** What each axis is caught by now: at the centre, level with an edge, touching one from outside, or nothing. */
64
+ private caughtOf;
65
+ private renderWhere;
66
+ private renderTyping;
67
+ private grab;
68
+ /** The footprint follows the pointer, caught at the centre and edges within a few pixels. */
69
+ private drag;
70
+ private drop;
71
+ /** Arrows move the part a millimetre, ten with Shift; the centre and edges still catch it. */
72
+ private keys;
73
+ }
74
+ export {};
@@ -0,0 +1,452 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { css, html, LitElement, nothing, svg } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { i18next, localize } from '@operato/i18n';
5
+ import { alignmentAt, offsetOf, overhangOf, screenOf } from './fasten-geometry.js';
6
+ const SEEN = [
7
+ ['top', 'M60 30 L100 10 L200 10 L160 30 Z'],
8
+ ['front', 'M60 30 L160 30 L160 100 L60 100 Z'],
9
+ ['right', 'M160 30 L200 10 L200 80 L160 100 Z']
10
+ ];
11
+ /** The faces the drawing cannot show on the box, as tags beside it where they are. */
12
+ const HIDDEN = [
13
+ ['bottom', 30, 114],
14
+ ['left', 24, 62],
15
+ ['back', 236, 14]
16
+ ];
17
+ const PANEL_W = 300;
18
+ /**
19
+ * 붙이기 그림 (user 2026-10-01) — which face of what the part is fastened to, how, and where on it; each of them chosen
20
+ * by pressing or dragging the drawing, none by a list of faces.
21
+ *
22
+ * The target is drawn as a box whose faces are pressed (the three it hides as tags beside it), or as the one plane the
23
+ * figure stands by. How is three small side views -- set on it, flush with it, its centre on it (only for a part that
24
+ * turns about a joint) -- and the chosen one's gap is dragged. Where is the face seen from above with the part's
25
+ * footprint on it: dragged, caught at the centre and at the edges from inside and outside, a guide drawn where it is
26
+ * caught and a dimension where it is not, which is pressed to type the number. It may go past the edges: the part that
27
+ * does is shaded, and a part off the face altogether is said so. Nothing is decided here: every change goes to the form
28
+ * (`fasten-change`), and a press or a drag let go says so (`fasten-release`), for the form to apply a fastening it holds.
29
+ */
30
+ let FigureFastenDrawing = class FigureFastenDrawing extends localize(i18next)(LitElement) {
31
+ constructor() {
32
+ super(...arguments);
33
+ /** The target's name, or empty for the plane the figure stands by. */
34
+ this.target = '';
35
+ /** The plane's name by the figure's placement: 바닥 · 천장 · 벽 · 중심. */
36
+ this.planeWord = '';
37
+ this.faces = [];
38
+ this.face = '';
39
+ this.modes = ['meet', 'flush'];
40
+ this.mode = 'meet';
41
+ this.align = {};
42
+ this.alignMm = {};
43
+ this.gap = 0;
44
+ /** The part being fastened (not `part`: that is every element's own). */
45
+ this.partName = '';
46
+ this.locked = false;
47
+ /** The form already says how the fastening it has lies on the face: the drawing says it only while dragging. */
48
+ this.said = false;
49
+ this.scale = 1;
50
+ }
51
+ static { this.styles = css `
52
+ :host {
53
+ display: grid;
54
+ gap: 8px;
55
+ outline: none;
56
+ }
57
+ svg {
58
+ width: 100%;
59
+ display: block;
60
+ touch-action: none;
61
+ user-select: none;
62
+ }
63
+ svg text {
64
+ font: inherit;
65
+ }
66
+ div[modes] {
67
+ display: grid;
68
+ grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
69
+ gap: 6px;
70
+ }
71
+ div[modes] button {
72
+ display: grid;
73
+ justify-items: center;
74
+ padding: 2px 4px 4px;
75
+ font: inherit;
76
+ font-size: 0.72rem;
77
+ color: var(--md-sys-color-on-surface);
78
+ background: none;
79
+ border: 1px solid var(--md-sys-color-outline-variant);
80
+ border-radius: 8px;
81
+ cursor: pointer;
82
+ }
83
+ div[modes] button[on] {
84
+ border: 1.5px solid var(--md-sys-color-primary);
85
+ background: var(--md-sys-color-primary-container);
86
+ color: var(--md-sys-color-on-primary-container);
87
+ }
88
+ div[where] {
89
+ position: relative;
90
+ }
91
+ div[where] input {
92
+ position: absolute;
93
+ width: 72px;
94
+ font: inherit;
95
+ font-size: 0.75rem;
96
+ }
97
+ small[warn] {
98
+ color: var(--md-sys-color-tertiary);
99
+ }
100
+ small[off] {
101
+ color: var(--md-sys-color-error);
102
+ }
103
+ `; }
104
+ change(detail) {
105
+ this.dispatchEvent(new CustomEvent('fasten-change', { detail, bubbles: true, composed: true }));
106
+ }
107
+ released() {
108
+ this.dispatchEvent(new CustomEvent('fasten-release', { bubbles: true, composed: true }));
109
+ }
110
+ render() {
111
+ return html `${this.renderTarget()}${this.renderModes()}${this.renderWhere()}`;
112
+ }
113
+ /** The target box, its faces pressed; or the plane. */
114
+ renderTarget() {
115
+ if (!this.target)
116
+ return svg `<svg viewBox="0 0 ${PANEL_W} 90" role="img" aria-label=${this.planeWord}>
117
+ <path d="M30 80 L110 30 L270 30 L190 80 Z" style="fill:var(--md-sys-color-primary);fill-opacity:.35;stroke:var(--md-sys-color-primary);stroke-width:1.5" />
118
+ <text x="150" y="62" text-anchor="middle" font-size="12" style="fill:var(--md-sys-color-on-primary-container)">${this.planeWord}</text>
119
+ </svg>`;
120
+ const has = (f) => this.faces.includes(f);
121
+ /* A press is a whole change, as a drag let go is: the form applies a fastening the part already has. */
122
+ const pick = (f) => () => {
123
+ if (this.locked || !has(f) || f === this.face)
124
+ return;
125
+ this.change({ targetFace: f });
126
+ this.released();
127
+ };
128
+ const seen = SEEN.map(([f, d]) => {
129
+ const on = this.face === f;
130
+ return svg `<path face=${f} d=${d} @click=${pick(f)}
131
+ style=${`cursor:${has(f) ? 'pointer' : 'default'};fill:${on ? 'var(--md-sys-color-primary)' : 'var(--md-sys-color-primary-container)'};fill-opacity:${on ? 0.55 : has(f) ? 1 : 0.35};stroke:var(--md-sys-color-primary);stroke-width:1.5`}>
132
+ <title>${i18next.t('figure.label.face-' + f)}</title></path>`;
133
+ });
134
+ const hidden = HIDDEN.map(([f, x, y]) => {
135
+ const on = this.face === f;
136
+ return svg `<g face=${f} @click=${pick(f)} style=${`cursor:${has(f) ? 'pointer' : 'default'};opacity:${has(f) ? 1 : 0.35}`}>
137
+ <rect x=${x - 26} y=${y - 10} width="52" height="20" rx="10"
138
+ style=${`fill:${on ? 'var(--md-sys-color-primary-container)' : 'transparent'};stroke:${on ? 'var(--md-sys-color-primary)' : 'var(--md-sys-color-outline)'};stroke-dasharray:${on ? '' : '3 2'}`} />
139
+ <text x=${x} y=${y + 4} text-anchor="middle" font-size="11" style=${`fill:${on ? 'var(--md-sys-color-on-primary-container)' : 'var(--md-sys-color-on-surface-variant)'}`}>${i18next.t('figure.label.face-' + f)}</text>
140
+ </g>`;
141
+ });
142
+ /* A shape's faces beyond its six outer ones -- a door frame's opening -- are tags of their own under the box. */
143
+ const others = this.faces.filter(f => !SEEN.some(([s]) => s === f) && !HIDDEN.some(([h]) => h === f));
144
+ return html `${svg `<svg viewBox="0 0 ${PANEL_W} 126" role="img" aria-label=${this.target}>
145
+ ${seen}
146
+ <text x="110" y="70" text-anchor="middle" font-size="12" pointer-events="none" style="fill:var(--md-sys-color-on-primary-container)">${this.target}</text>
147
+ ${hidden}
148
+ </svg>`}
149
+ ${others.length
150
+ ? html `<div modes>
151
+ ${others.map(f => html `<button ?on=${f === this.face} ?disabled=${this.locked} @click=${pick(f)}>${i18next.t('figure.label.face-' + f, { defaultValue: f })}</button>`)}
152
+ </div>`
153
+ : nothing}`;
154
+ }
155
+ /** How: three side views; the chosen one's gap is dragged. */
156
+ renderModes() {
157
+ const icon = (m, on) => {
158
+ const g = on && m !== 'centre' ? Math.min(this.gap / 4, 10) : 0;
159
+ const base = svg `<rect x="6" y="30" width="52" height="14" style="fill:var(--md-sys-color-primary-container);stroke:var(--md-sys-color-primary)" />`;
160
+ const gapDrag = on && !this.locked ? 'ns-resize' : 'pointer';
161
+ if (m === 'meet')
162
+ return svg `<svg width="64" height="48">${base}<rect gap x="22" y=${18 - g} width="20" height="12" style=${`fill:var(--md-sys-color-surface);stroke:currentColor;cursor:${gapDrag}`} />
163
+ ${g ? svg `<text x="46" y=${26 - g / 2} font-size="10" style="fill:currentColor">${this.gap}</text>` : nothing}</svg>`;
164
+ if (m === 'flush')
165
+ return svg `<svg width="64" height="48">${base}<rect gap x="22" y=${30 - g} width="20" height="10" style=${`fill:var(--md-sys-color-surface);stroke:currentColor;cursor:${gapDrag}`} />
166
+ <line x1="4" y1="30" x2="60" y2="30" style="stroke:currentColor;stroke-dasharray:2 2" /></svg>`;
167
+ return svg `<svg width="64" height="48">${base}<circle cx="32" cy="30" r="11" style="fill:var(--md-sys-color-surface);fill-opacity:.85;stroke:currentColor" /><circle cx="32" cy="30" r="2" style="fill:currentColor" /></svg>`;
168
+ };
169
+ return html `<div modes>
170
+ ${this.modes.map(m => {
171
+ const on = m === this.mode;
172
+ return html `<button mode=${m} ?on=${on} ?disabled=${this.locked}
173
+ @click=${() => {
174
+ if (on)
175
+ return;
176
+ this.change({ mode: m });
177
+ this.released();
178
+ }}
179
+ @pointerdown=${(ev) => {
180
+ if (!on || this.locked || !ev.target.hasAttribute?.('gap'))
181
+ return;
182
+ ev.preventDefault();
183
+ ev.currentTarget.setPointerCapture(ev.pointerId);
184
+ this.gapFrom = { y: ev.clientY, gap: this.gap };
185
+ }}
186
+ @pointermove=${(ev) => {
187
+ if (!this.gapFrom)
188
+ return;
189
+ this.change({ gap: Math.max(0, Math.round(this.gapFrom.gap + (this.gapFrom.y - ev.clientY) * 2)) });
190
+ }}
191
+ @pointerup=${() => {
192
+ if (!this.gapFrom)
193
+ return;
194
+ this.gapFrom = undefined;
195
+ this.released();
196
+ }}>
197
+ ${icon(m, on)}<span>${i18next.t('figure.label.fasten-mode-' + m)}</span>
198
+ </button>`;
199
+ })}
200
+ </div>`;
201
+ }
202
+ /** Where the part's centre is on each axis now: the drag's, or the form's. */
203
+ centreOf(view) {
204
+ if (this.dragged)
205
+ return this.dragged.at;
206
+ const at = { x: 0, y: 0, z: 0 };
207
+ for (const a of view.axes)
208
+ at[a] = offsetOf(this.align[a], this.alignMm[a], view.face[a], view.part[a]);
209
+ return at;
210
+ }
211
+ /** What each axis is caught by now: at the centre, level with an edge, touching one from outside, or nothing. */
212
+ caughtOf(view, at) {
213
+ if (this.dragged)
214
+ return this.dragged.caught;
215
+ const out = {};
216
+ for (const a of view.axes) {
217
+ const how = this.align[a] ?? 'centre';
218
+ if (how === 'centre')
219
+ out[a] = 'centre';
220
+ else if (how === 'min' || how === 'max')
221
+ out[a] = 'inside';
222
+ else if (view.face[a] !== undefined && Math.abs(Math.abs(at[a]) - (view.face[a] + view.part[a]) / 2) < 0.5)
223
+ out[a] = 'outside';
224
+ }
225
+ return out;
226
+ }
227
+ renderWhere() {
228
+ const view = this.view;
229
+ if (!view)
230
+ return nothing;
231
+ const { h, v, sign } = screenOf(view.axes);
232
+ const W = PANEL_W, H = 180;
233
+ /* Room around the face for the part to go past its edges by about half itself; a plane with no size, three parts wide. */
234
+ const span = (a) => (view.face[a] !== undefined ? view.face[a] + view.part[a] : view.part[a] * 3);
235
+ const k = Math.min((W - 40) / span(h), (H - 40) / span(v));
236
+ this.scale = k;
237
+ const cx = W / 2, cy = H / 2;
238
+ const at = this.centreOf(view);
239
+ const caught = this.caughtOf(view, at);
240
+ const fw = (view.face[h] ?? 0) * k, fh = (view.face[v] ?? 0) * k;
241
+ const pw = view.part[h] * k, ph = view.part[v] * k;
242
+ const px = cx + sign[h] * at[h] * k - pw / 2, py = cy + sign[v] * at[v] * k - ph / 2;
243
+ const faceSized = view.face[h] !== undefined && view.face[v] !== undefined;
244
+ const fx = cx - fw / 2, fy = cy - fh / 2;
245
+ const ix = Math.max(fx, px), iy = Math.max(fy, py);
246
+ const iw = Math.min(fx + fw, px + pw) - ix, ih = Math.min(fy + fh, py + ph) - iy;
247
+ const { touching, sides } = faceSized ? overhangOf(view, at) : { touching: true, sides: [] };
248
+ const round = this.mode === 'centre';
249
+ const guide = (a) => {
250
+ const c = caught[a];
251
+ if (!c)
252
+ return nothing;
253
+ const along = a === h;
254
+ const edge = c === 'centre' ? 0 : Math.sign(at[a]) * (view.face[a] ?? 0) / 2;
255
+ const pos = (along ? cx : cy) + sign[a] * edge * k;
256
+ const style = `stroke:var(--md-sys-color-primary);stroke-width:${c === 'centre' ? 1 : 2.5};stroke-dasharray:${c === 'centre' ? '4 3' : ''}`;
257
+ return along ? svg `<line x1=${pos} y1="4" x2=${pos} y2=${H - 4} style=${style} />` : svg `<line x1="4" y1=${pos} x2=${W - 4} y2=${pos} style=${style} />`;
258
+ };
259
+ const dim = (a) => {
260
+ /* Caught by the centre or an edge, the guide says where it is; a number only where nothing caught it. */
261
+ if (caught[a])
262
+ return nothing;
263
+ const along = a === h;
264
+ const mm = Math.round(at[a]);
265
+ const label = `${mm > 0 ? '+' : ''}${mm} mm`;
266
+ const to = sign[a] * at[a] * k;
267
+ const tx = along ? cx + to / 2 : 8, ty = along ? H - 6 : cy + to / 2 + 4;
268
+ return svg `${along
269
+ ? svg `<line x1=${cx} y1=${H - 16} x2=${cx + to} y2=${H - 16} style="stroke:var(--md-sys-color-on-surface)" />`
270
+ : svg `<line x1="20" y1=${cy} x2="20" y2=${cy + to} style="stroke:var(--md-sys-color-on-surface)" />`}
271
+ <text dim=${a} x=${tx} y=${ty} font-size="11" text-anchor=${along ? 'middle' : 'start'}
272
+ style="fill:var(--md-sys-color-primary);cursor:text;text-decoration:underline dotted" @click=${() => !this.locked && (this.typing = a)}>${label}</text>`;
273
+ };
274
+ const faceName = this.target ? `${this.target} ${i18next.t('figure.label.face-' + this.face)}` : this.planeWord;
275
+ const sizeWords = faceSized ? ` ${Math.round(view.face[h])} × ${Math.round(view.face[v])}` : '';
276
+ return html `<div where tabindex="0" @keydown=${(ev) => this.keys(ev, view)}>
277
+ ${svg `<svg viewBox="0 0 ${W} ${H}" role="img" aria-label=${faceName}
278
+ @pointermove=${(ev) => this.drag(ev, view, cx, cy)}
279
+ @pointerup=${() => this.drop()} @pointercancel=${() => this.drop()}>
280
+ ${faceSized
281
+ ? svg `<rect x=${fx} y=${fy} width=${fw} height=${fh} rx="2" style="fill:var(--md-sys-color-primary-container);stroke:var(--md-sys-color-primary);stroke-width:1.5" />`
282
+ : svg `<rect x="4" y="4" width=${W - 8} height=${H - 8} rx="6" style="fill:var(--md-sys-color-primary-container);fill-opacity:.5" />`}
283
+ <text x=${faceSized ? fx + 4 : 10} y=${faceSized ? fy + fh - 6 : 18} font-size="11" pointer-events="none" style="fill:var(--md-sys-color-primary)">${faceName}${sizeWords}</text>
284
+ ${guide(h)}${guide(v)}
285
+ <g footprint style=${`cursor:${this.locked ? 'default' : 'grab'}`} @pointerdown=${(ev) => this.grab(ev, at, caught)}>
286
+ <rect x=${px} y=${py} width=${pw} height=${ph} rx=${round ? pw / 2 : 2} style="fill:var(--md-sys-color-tertiary-container)" />
287
+ ${faceSized && iw > 0 && ih > 0
288
+ ? svg `<rect x=${ix} y=${iy} width=${iw} height=${ih} rx=${round ? Math.min(iw, ih) / 2 : 2} style="fill:var(--md-sys-color-surface)" />`
289
+ : !faceSized
290
+ ? svg `<rect x=${px} y=${py} width=${pw} height=${ph} rx=${round ? pw / 2 : 2} style="fill:var(--md-sys-color-surface)" />`
291
+ : nothing}
292
+ <rect x=${px} y=${py} width=${pw} height=${ph} rx=${round ? pw / 2 : 2}
293
+ style=${`fill:none;stroke:var(--md-sys-color-on-surface);stroke-width:1.5;stroke-dasharray:${touching ? '' : '4 3'}`} />
294
+ <text x=${px + pw / 2} y=${py + ph / 2 + 4} text-anchor="middle" font-size="12" pointer-events="none" style="fill:var(--md-sys-color-on-surface)">${this.partName}</text>
295
+ </g>
296
+ ${dim(h)}${dim(v)}
297
+ </svg>`}
298
+ ${this.typing ? this.renderTyping(view, this.typing, at) : nothing}
299
+ </div>
300
+ ${this.said && !this.dragged
301
+ ? nothing
302
+ : !touching
303
+ ? html `<small off role="status">${i18next.t('figure.text.fasten-off-face', { face: faceName })}</small>`
304
+ : sides.length
305
+ ? html `<small warn role="status">${i18next.t('figure.text.fasten-past-edges', {
306
+ sides: sides.map(s => i18next.t(`figure.text.overhang-side-${s.side}`, { axis: s.axis.toUpperCase(), mm: String(s.mm) })).join(' · ')
307
+ })}</small>`
308
+ : nothing}`;
309
+ }
310
+ renderTyping(view, a, at) {
311
+ const along = a === screenOf(view.axes).h;
312
+ return html `<input type="number" step="1" aria-label=${`${a.toUpperCase()} mm`} .value=${String(Math.round(at[a]))}
313
+ style=${along ? 'left: calc(50% - 36px); bottom: 0' : 'left: 4px; top: calc(50% - 12px)'}
314
+ @keydown=${(ev) => {
315
+ ev.stopPropagation();
316
+ if (ev.key === 'Escape')
317
+ this.typing = undefined;
318
+ if (ev.key !== 'Enter')
319
+ return;
320
+ const value = Number(ev.target.value);
321
+ this.typing = undefined;
322
+ if (!Number.isFinite(value))
323
+ return;
324
+ this.change({ axis: a, how: 'mm', mm: Math.round(value) });
325
+ this.released();
326
+ }}
327
+ @blur=${() => (this.typing = undefined)} />`;
328
+ }
329
+ grab(ev, at, caught) {
330
+ if (this.locked)
331
+ return;
332
+ ev.currentTarget.closest('svg').setPointerCapture(ev.pointerId);
333
+ this.dragged = { at: { ...at }, caught: { ...caught } };
334
+ }
335
+ /** The footprint follows the pointer, caught at the centre and edges within a few pixels. */
336
+ drag(ev, view, cx, cy) {
337
+ if (!this.dragged)
338
+ return;
339
+ const svgEl = ev.currentTarget;
340
+ const pt = svgEl.createSVGPoint();
341
+ pt.x = ev.clientX;
342
+ pt.y = ev.clientY;
343
+ const p = pt.matrixTransform(svgEl.getScreenCTM().inverse());
344
+ const { h, v, sign } = screenOf(view.axes);
345
+ const at = { ...this.dragged.at }, caught = {};
346
+ for (const [a, d] of [
347
+ [h, (sign[h] * (p.x - cx)) / this.scale],
348
+ [v, (sign[v] * (p.y - cy)) / this.scale]
349
+ ]) {
350
+ const got = alignmentAt(d, view.face[a], view.part[a], view.edges[a], 8 / this.scale);
351
+ at[a] = offsetOf(got.how, got.mm, view.face[a], view.part[a]);
352
+ if (got.caught)
353
+ caught[a] = got.caught;
354
+ }
355
+ this.dragged = { at, caught };
356
+ }
357
+ drop() {
358
+ const d = this.dragged;
359
+ if (!d || !this.view)
360
+ return;
361
+ this.dragged = undefined;
362
+ for (const a of this.view.axes) {
363
+ const got = alignmentAt(d.at[a], this.view.face[a], this.view.part[a], this.view.edges[a], 0.5);
364
+ this.change({ axis: a, how: got.how, ...(got.how === 'mm' ? { mm: got.mm } : {}) });
365
+ }
366
+ this.released();
367
+ }
368
+ /** Arrows move the part a millimetre, ten with Shift; the centre and edges still catch it. */
369
+ keys(ev, view) {
370
+ const step = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[ev.key];
371
+ if (!step || this.locked || this.typing)
372
+ return;
373
+ ev.preventDefault();
374
+ ev.stopPropagation();
375
+ const by = ev.shiftKey ? 10 : 1;
376
+ const at = this.centreOf(view);
377
+ const { h, v, sign } = screenOf(view.axes);
378
+ [h, v].forEach((a, i) => {
379
+ if (!step[i])
380
+ return;
381
+ /* Up the screen is up the face: a Y that rises goes up with the arrow. */
382
+ const got = alignmentAt(at[a] + sign[a] * step[i] * by, view.face[a], view.part[a], view.edges[a], by / 2);
383
+ this.change({ axis: a, how: got.how, ...(got.how === 'mm' ? { mm: got.mm } : {}) });
384
+ });
385
+ this.released();
386
+ }
387
+ };
388
+ __decorate([
389
+ property(),
390
+ __metadata("design:type", Object)
391
+ ], FigureFastenDrawing.prototype, "target", void 0);
392
+ __decorate([
393
+ property(),
394
+ __metadata("design:type", Object)
395
+ ], FigureFastenDrawing.prototype, "planeWord", void 0);
396
+ __decorate([
397
+ property({ attribute: false }),
398
+ __metadata("design:type", Array)
399
+ ], FigureFastenDrawing.prototype, "faces", void 0);
400
+ __decorate([
401
+ property(),
402
+ __metadata("design:type", Object)
403
+ ], FigureFastenDrawing.prototype, "face", void 0);
404
+ __decorate([
405
+ property({ attribute: false }),
406
+ __metadata("design:type", Array)
407
+ ], FigureFastenDrawing.prototype, "modes", void 0);
408
+ __decorate([
409
+ property(),
410
+ __metadata("design:type", String)
411
+ ], FigureFastenDrawing.prototype, "mode", void 0);
412
+ __decorate([
413
+ property({ attribute: false }),
414
+ __metadata("design:type", Object)
415
+ ], FigureFastenDrawing.prototype, "view", void 0);
416
+ __decorate([
417
+ property({ attribute: false }),
418
+ __metadata("design:type", Object)
419
+ ], FigureFastenDrawing.prototype, "align", void 0);
420
+ __decorate([
421
+ property({ attribute: false }),
422
+ __metadata("design:type", Object)
423
+ ], FigureFastenDrawing.prototype, "alignMm", void 0);
424
+ __decorate([
425
+ property({ type: Number }),
426
+ __metadata("design:type", Object)
427
+ ], FigureFastenDrawing.prototype, "gap", void 0);
428
+ __decorate([
429
+ property(),
430
+ __metadata("design:type", Object)
431
+ ], FigureFastenDrawing.prototype, "partName", void 0);
432
+ __decorate([
433
+ property({ type: Boolean }),
434
+ __metadata("design:type", Object)
435
+ ], FigureFastenDrawing.prototype, "locked", void 0);
436
+ __decorate([
437
+ property({ type: Boolean }),
438
+ __metadata("design:type", Object)
439
+ ], FigureFastenDrawing.prototype, "said", void 0);
440
+ __decorate([
441
+ state(),
442
+ __metadata("design:type", Object)
443
+ ], FigureFastenDrawing.prototype, "dragged", void 0);
444
+ __decorate([
445
+ state(),
446
+ __metadata("design:type", String)
447
+ ], FigureFastenDrawing.prototype, "typing", void 0);
448
+ FigureFastenDrawing = __decorate([
449
+ customElement('figure-fasten-drawing')
450
+ ], FigureFastenDrawing);
451
+ export { FigureFastenDrawing };
452
+ //# sourceMappingURL=figure-fasten-drawing.js.map