@things-factory/figure-ui 10.1.206 → 10.1.208
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/editor.ts +11 -97
- package/client/modeller/figure-facts.ts +1 -1
- package/client/modeller/figure-history-panel.ts +1 -29
- package/client/modeller/figure-preview.ts +5 -7
- package/client/modeller/figure-verdict.ts +2 -17
- package/client/modeller/maturity.ts +11 -27
- package/client/modeller/refusals.ts +1 -7
- package/client/pages/figure-modeller-page.ts +7 -22
- package/dist-client/modeller/editor.d.ts +1 -8
- package/dist-client/modeller/editor.js +12 -98
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-facts.js +1 -1
- package/dist-client/modeller/figure-facts.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.d.ts +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +0 -16
- package/dist-client/modeller/figure-history-panel.js +1 -27
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +1 -1
- package/dist-client/modeller/figure-preview.js +5 -8
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-verdict.d.ts +0 -5
- package/dist-client/modeller/figure-verdict.js +2 -21
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/maturity.d.ts +2 -4
- package/dist-client/modeller/maturity.js +12 -26
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +1 -3
- package/dist-client/modeller/refusals.js +1 -7
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +1 -3
- package/dist-client/pages/figure-modeller-page.js +7 -23
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/test/browser/acceptance-page.js +3 -3
- package/test/browser/detail-page.js +1 -3
- package/test/browser/detail-tabs-page.js +1 -2
- package/test/browser/links.check.mjs +2 -37
- package/test/browser/modeller.check.mjs +1 -3
- package/test/browser/parts-page.js +2 -2
- package/test/browser/parts.check.mjs +2 -12
- package/test/browser/product-page.check.mjs +6 -9
- package/test/browser/product-page.js +1 -2
- package/test/browser/screen-page.js +2 -6
- package/test/browser/screen.check.mjs +8 -65
- package/test/first-minute.test.ts +2 -3
- package/test/fixtures/joint-column.json +1 -66
- package/test/fixtures/joint-roller.json +1 -66
- package/test/fixtures/robot-arm-6dof.json +2 -67
- package/test/harness-serve.test.ts +1 -1
- package/test/heading-scale-guard.test.ts +3 -3
- package/test/links.test.ts +1 -1
- package/test/parts-wiring.test.ts +1 -1
- package/test/properties.test.ts +9 -12
- package/test/refusals.test.ts +3 -21
- package/test/resize-with-placement.test.ts +13 -15
- package/test/revision.test.ts +1 -2
- package/test/verdict.test.ts +1 -15
- package/translations/en.json +0 -19
- package/translations/es.json +0 -19
- package/translations/ja.json +0 -19
- package/translations/ko.json +9 -28
- package/translations/ms.json +0 -19
- package/translations/zh.json +0 -19
- package/client/modeller/occupancy.ts +0 -51
- package/dist-client/modeller/occupancy.d.ts +0 -31
- package/dist-client/modeller/occupancy.js +0 -36
- package/dist-client/modeller/occupancy.js.map +0 -1
- package/test/release-panel.test.ts +0 -53
|
@@ -45,17 +45,12 @@ const limitOf = (use) => Math.floor(use.limit).toLocaleString();
|
|
|
45
45
|
let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
|
|
46
46
|
constructor() {
|
|
47
47
|
super(...arguments);
|
|
48
|
-
/** Bodies in the asset, drawn or hidden. With none there is nothing to fit the space to, so the button is not offered. */
|
|
49
|
-
this.bodies = 0;
|
|
50
48
|
/** Whether the trend chart is open at its own size. */
|
|
51
49
|
this.expanded = false;
|
|
52
50
|
this.failure = '';
|
|
53
51
|
this.asking = false;
|
|
54
52
|
/** Answers can come back out of order; only the newest question's answer is shown. */
|
|
55
53
|
this.answers = newestAnswer();
|
|
56
|
-
this.fit = () => {
|
|
57
|
-
this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }));
|
|
58
|
-
};
|
|
59
54
|
}
|
|
60
55
|
static { this.styles = [
|
|
61
56
|
ScrollbarStyles,
|
|
@@ -408,9 +403,8 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
408
403
|
this.dispatchEvent(new CustomEvent('release-verdict', { detail: { blocking: (this.inspection?.findings ?? []).filter(finding => finding.blocking) } }));
|
|
409
404
|
}
|
|
410
405
|
measure() {
|
|
411
|
-
const { report,
|
|
406
|
+
const { report, told } = verdictMeasureOf(this.revision?.asset);
|
|
412
407
|
this.report = report;
|
|
413
|
-
this.bodies = bodies;
|
|
414
408
|
if (!report)
|
|
415
409
|
this.expanded = false;
|
|
416
410
|
this.dispatchEvent(new CustomEvent('capacity-changed', { detail: told }));
|
|
@@ -719,7 +713,7 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
719
713
|
${blocking.length
|
|
720
714
|
? html `<ul gate>
|
|
721
715
|
<li head>${i18next.t('figure.text.must-fix-to-release')}</li>
|
|
722
|
-
${blocking.map(finding => renderGateFinding(finding, true,
|
|
716
|
+
${blocking.map(finding => renderGateFinding(finding, true, nothing, this.partNames()))}
|
|
723
717
|
</ul>`
|
|
724
718
|
: nothing}
|
|
725
719
|
${advisory.length
|
|
@@ -734,15 +728,6 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
734
728
|
partNames() {
|
|
735
729
|
return (this.revision?.asset.appearance ?? []).map(look => look.target);
|
|
736
730
|
}
|
|
737
|
-
/** The same way out the release panel offers for the same two findings — declare the space from the parts. */
|
|
738
|
-
actionsFor(code) {
|
|
739
|
-
if (code !== 'part-outside-occupancy' && code !== 'occupancy-undeclared')
|
|
740
|
-
return nothing;
|
|
741
|
-
/* A new figure with no parts is refused as undeclared too, but fitting needs parts to measure (seen 2026-09-24). */
|
|
742
|
-
if (!this.bodies)
|
|
743
|
-
return nothing;
|
|
744
|
-
return html `<button fit @click=${this.fit}>${i18next.t('figure.button.fit-base-to-parts')}</button>`;
|
|
745
|
-
}
|
|
746
731
|
};
|
|
747
732
|
__decorate([
|
|
748
733
|
property({ attribute: false }),
|
|
@@ -760,10 +745,6 @@ __decorate([
|
|
|
760
745
|
state(),
|
|
761
746
|
__metadata("design:type", Object)
|
|
762
747
|
], FigureVerdict.prototype, "report", void 0);
|
|
763
|
-
__decorate([
|
|
764
|
-
state(),
|
|
765
|
-
__metadata("design:type", Object)
|
|
766
|
-
], FigureVerdict.prototype, "bodies", void 0);
|
|
767
748
|
__decorate([
|
|
768
749
|
state(),
|
|
769
750
|
__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-verdict.js","sourceRoot":"","sources":["../../client/modeller/figure-verdict.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { LIMITS, FIGURE_PER_INSTANCE } from '@hatiolab/figure-model'\n\nimport { inspectFigureDraft } from '../graphql/index.js'\nimport type { FigureInspection } from '../types.js'\nimport { gateFindingStyles, renderGateFinding } from './gate-findings.js'\nimport { MEASURED } from './scene-budget.js'\nimport type { FigureRevision } from './revision.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { CURVE_SIZE, curveLayout, gradeBoundsOf, shortNumber, verdictMeasureOf } from './maturity.js'\nimport { newestAnswer } from './newest-answer.js'\nimport type { FigureBudgetUse, FigureMaturityAxis, FigureReport } from './maturity.js'\n\n/** How long an edit settles before the server is asked again. One question per pause, not one per keystroke. */\nexport const VERDICT_SETTLE_MS = 400\n/** How long the figure rests before its report is measured again. */\nconst MEASURE_SETTLE_MS = 200\n\n/**\n * 성숙도 — the modeller's verdict while editing.\n *\n * It shows, as the author edits: how many fit on one screen with the grade beside it and the budget that\n * decided the grade, the grade scale, six maturity axes as a radar and a list, the budget bars, the drawing cost,\n * the reuse trend, and the findings. An earlier tab kept only the count and the findings (user, 2026-09-24:\n * 「분석부분도 많이 부족하고, 왜 이렇게 너프되었는지」).\n *\n * ## What is computed where\n *\n * The numbers come from `maturity.ts`, which reads the evaluated asset the way the renderer draws it — the\n * screen calls and shows, it does not count. The count uses the same `capacityFrom` the catalogue cards use, so the\n * tab and the card cannot disagree.\n *\n * The findings do not come from here: they come from the server's release door (`inspectFigureDraft`), the same\n * door and the same sentences as the release panel. A second judgement on the screen would say something different\n * the first time either changed. The release judgement is the server's, so that section is the server's answer.\n *\n * ## The grade\n *\n * figure-model's `figureScoreOf`, which the catalogue uses too: headroom against placing `LIMITS.screenInstances` of the\n * figure on one screen (chief architect, 2026-09-24) -- the screen's measured groups and its triangle target shared\n * per instance -- and the rule limits for independently moved parts and transparent materials, decided by the\n * tightest. The two budgets has no field for are left out and the tab says so.\n */\n/** A per-instance share of the screen is not a whole number; what an instance may draw is the whole part of it. */\nconst limitOf = (use: FigureBudgetUse) => Math.floor(use.limit).toLocaleString()\n\n@customElement('figure-verdict')\nexport class FigureVerdict extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n SidePanelStyles,\n gateFindingStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n }\n section p {\n margin: 0;\n }\n\n /* ── How many fit ───────────────────────────────────────────────\n The value of low poly is how many can stand on one screen, so that answer leads. The grade says whether\n the shape keeps inside its declared limits, so it is the small badge beside it. */\n p[lead],\n p[basis],\n p[quiet] {\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[basis] em {\n font-style: normal;\n color: var(--md-sys-color-tertiary);\n }\n div[verdict] {\n display: flex;\n align-items: baseline;\n gap: 6px;\n }\n span[capacity] {\n font-size: var(--ui-t-hero, 26px);\n font-weight: 800;\n line-height: 1;\n letter-spacing: -0.01em;\n color: var(--md-sys-color-primary);\n }\n span[unit] {\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--md-sys-color-primary);\n }\n /* The badge says what it measures (headroom), so its number is not read as a score out of 100. */\n span[grade] {\n display: flex;\n align-items: baseline;\n gap: 5px;\n margin-left: auto;\n padding: 3px 10px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 700;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-radius: 999em;\n }\n span[grade] small {\n display: inline;\n margin: 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 500;\n color: inherit;\n }\n /* C keeps within the limit. Red is for over it (D) only; red on a kept limit asks for a fix nothing needs. */\n span[grade][tight] {\n color: var(--md-sys-color-on-tertiary-container);\n background-color: var(--md-sys-color-tertiary-container);\n }\n span[grade][over] {\n color: var(--md-sys-color-on-error-container);\n background-color: var(--md-sys-color-error-container);\n }\n p[because] {\n font-size: var(--ui-t-small, 11.5px);\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[because] strong {\n color: var(--md-sys-color-on-surface);\n }\n /* The grade scale. Not folded away: it is compared against every time, not learned once. */\n p[scale] {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 10px;\n font-size: var(--ui-t-micro, 11px);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[scale] b {\n font-weight: 700;\n }\n p[scale] span[now] {\n color: var(--md-sys-color-on-surface);\n }\n p[scale] span[now] b {\n color: var(--md-sys-color-primary);\n }\n\n /* ── Maturity radar ─────────────────────────────────────────────\n Only the measured axes are drawn; an unmeasured one drawn as a spoke reads as a dent, which is a zero. */\n div[radar] {\n display: flex;\n justify-content: center;\n padding: 2px 0 6px 0;\n color: var(--md-sys-color-primary);\n }\n div[radar] svg {\n width: 100%;\n max-width: 240px;\n height: auto;\n }\n div[radar] polygon[web] {\n fill: none;\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 0.6;\n }\n div[radar] line[spoke] {\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 0.6;\n }\n div[radar] polygon[shape] {\n fill: color-mix(in srgb, var(--md-sys-color-primary) 22%, transparent);\n stroke: var(--md-sys-color-primary);\n stroke-width: 1.4;\n stroke-linejoin: round;\n }\n div[radar] circle[dot] {\n fill: var(--md-sys-color-primary);\n }\n div[radar] text {\n /* svg-units: the radar's own coordinates (its viewBox), drawn to the chart's size -- not a page size. */\n font-size: 5.2px;\n fill: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[unmeasured] {\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.5;\n color: var(--md-sys-color-tertiary);\n }\n\n /* ── Axes and budgets ───────────────────────────────────────────\n Not merged into one number: merged, 「what to fix」 disappears. */\n ul[axes],\n ul[budgets] {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 7px;\n }\n ul[axes] li,\n ul[budgets] li {\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 2px 8px;\n font-size: var(--ui-t-small, 11.5px);\n }\n ul[axes] li[blocked] {\n opacity: 0.7;\n }\n ul[axes] em {\n grid-column: 1 / -1;\n font-style: normal;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n ul[axes] li[blocked] em {\n color: var(--md-sys-color-tertiary);\n }\n span[reading] {\n font-family: var(--ui-mono, ui-monospace, monospace);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[bar] {\n grid-column: 1 / -1;\n height: 4px;\n background-color: var(--md-sys-color-surface-container-highest);\n border-radius: 2px;\n overflow: hidden;\n }\n div[bar] > i {\n display: block;\n height: 100%;\n background-color: var(--md-sys-color-primary);\n }\n div[bar] > i[tight] {\n background-color: var(--md-sys-color-error);\n }\n\n /* ── Drawing cost ─────────────────────────────────────────────── */\n dl[metrics] {\n margin: 0;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 4px 8px;\n font-size: var(--ui-t-small, 11.5px);\n }\n dl[metrics] dt {\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n dl[metrics] dd {\n margin: 0;\n text-align: right;\n font-family: var(--ui-mono, ui-monospace, monospace);\n }\n\n /* ── Trend ──────────────────────────────────────────────────────\n The chart's natural size is 680px; in a 300px side its words are 4px. The side holds a button, the chart\n opens at its own size. */\n button[expand] {\n justify-self: start;\n padding: 4px 10px;\n font: inherit;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[expand]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n div[sheet] {\n position: fixed;\n inset: 0;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n background-color: color-mix(in srgb, var(--md-sys-color-scrim, black) 32%, transparent);\n }\n div[sheet] > figure {\n max-width: 100%;\n max-height: 100%;\n margin: 0;\n padding: 12px;\n overflow: auto;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border-radius: 6px;\n box-shadow: 0 8px 32px color-mix(in srgb, var(--md-sys-color-shadow, black) 28%, transparent);\n }\n div[sheet] svg {\n display: block;\n /* Not shrunk below its natural size — a narrow window scrolls instead. */\n min-width: 680px;\n font-family: inherit;\n font-size: var(--ui-t-micro, 11px);\n fill: currentColor;\n }\n div[sheet] svg [grid] {\n stroke: currentColor;\n stroke-opacity: 0.12;\n }\n div[sheet] svg [muted] {\n opacity: 0.55;\n }\n div[sheet] svg path[series] {\n fill: none;\n stroke-width: 2;\n stroke-linejoin: round;\n }\n div[sheet] svg [series='triangles'] {\n stroke: var(--md-sys-color-error);\n fill: var(--md-sys-color-error);\n }\n div[sheet] svg [series='drawCalls'] {\n stroke: var(--md-sys-color-tertiary);\n fill: var(--md-sys-color-tertiary);\n }\n div[sheet] svg [series='geometries'] {\n stroke: var(--md-sys-color-primary);\n fill: var(--md-sys-color-primary);\n }\n div[sheet] svg [series='materials'] {\n stroke: var(--md-sys-color-secondary);\n fill: var(--md-sys-color-secondary);\n }\n div[sheet] svg path[series],\n div[sheet] svg line[series] {\n fill: none;\n }\n /* A legend value is written in its line's colour, not outlined in it. */\n div[sheet] svg text[series] {\n stroke: none;\n }\n div[sheet] figcaption {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n }\n div[sheet] button[close] {\n padding: 5px 14px;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n cursor: pointer;\n }\n\n /* ── Release verdict ───────────────────────────────────────────── */\n p[clear] {\n color: var(--md-sys-color-primary);\n font-weight: 600;\n }\n p[failed] {\n color: var(--md-sys-color-error);\n }\n button[fit] {\n align-self: flex-start;\n margin-top: 4px;\n padding: 4px 10px;\n font: inherit;\n color: var(--md-sys-color-on-secondary-container);\n background: var(--md-sys-color-secondary-container);\n border: none;\n border-radius: 999em;\n cursor: pointer;\n }\n `\n ]\n\n /** The figure at the version on screen. A version whose text is another is a new question. */\n @property({ attribute: false }) revision?: FigureRevision\n /* The text last judged: a save hands out the same version again as a new object, which asks nothing new. */\n private judged?: string\n /** The saved row, if any — the server reads its type and last release from it. */\n @property({ type: String }) figureId?: string\n /** Changing the type changes the verdict (a temporary type is refused), so it is a reason to ask again. */\n @property({ type: String }) figureType?: string\n\n /** The report over the source being edited. Absent when nothing is drawn, or the format refuses the source. */\n @state() private report?: FigureReport\n /** Bodies in the asset, drawn or hidden. With none there is nothing to fit the space to, so the button is not offered. */\n @state() private bodies = 0\n /** Whether the trend chart is open at its own size. */\n @state() private expanded = false\n @state() private inspection?: FigureInspection\n @state() private failure = ''\n @state() private asking = false\n\n private timer?: ReturnType<typeof setTimeout>\n /** Answers can come back out of order; only the newest question's answer is shown. */\n private answers = newestAnswer<FigureInspection>()\n\n disconnectedCallback() {\n clearTimeout(this.timer)\n clearTimeout(this.measureTimer)\n super.disconnectedCallback()\n }\n\n /** The report is measured once the figure has settled: it is read beside the work, and each measure compiles the figure several times. */\n private measureTimer?: ReturnType<typeof setTimeout>\n updated(changes: Map<string, unknown>) {\n const text = changes.has('revision') ? this.revision?.text : this.judged\n const changed = text !== this.judged\n if (changed) {\n this.judged = text\n clearTimeout(this.measureTimer)\n if (this.report === undefined && !this.measureTimer) this.measure()\n else this.measureTimer = setTimeout(() => this.measure(), MEASURE_SETTLE_MS)\n }\n if (changed || changes.has('figureId') || changes.has('figureType')) this.schedule()\n /* What blocks release, told to the page so the bar can say it beside 「발행」 (first 15 minutes, 2026-10-16). */\n if (changes.has('inspection'))\n this.dispatchEvent(new CustomEvent('release-verdict', { detail: { blocking: (this.inspection?.findings ?? []).filter(finding => finding.blocking) } }))\n }\n\n private measure() {\n const { report, bodies, told } = verdictMeasureOf(this.revision?.asset)\n this.report = report\n this.bodies = bodies\n if (!report) this.expanded = false\n this.dispatchEvent(new CustomEvent('capacity-changed', { detail: told }))\n }\n\n private schedule() {\n clearTimeout(this.timer)\n if (!this.revision) {\n this.inspection = undefined\n return\n }\n this.timer = setTimeout(() => this.ask(), VERDICT_SETTLE_MS)\n }\n\n private async ask() {\n const source = this.revision!.text\n this.asking = true\n await this.answers(\n () => inspectFigureDraft(source, this.figureId),\n outcome => {\n this.asking = false\n if ('answer' in outcome) {\n this.inspection = outcome.answer\n this.failure = ''\n } else {\n /* The server's reason as it gave it — the screen does not make one up. The last answer is dropped with it. */\n this.inspection = undefined\n this.failure = outcome.failure instanceof Error ? outcome.failure.message : String(outcome.failure)\n }\n }\n )\n }\n\n render() {\n /*\n With nothing drawn the report is guidance, not a verdict: the sections below have meaning only over parts,\n and a number drawn from nothing reads as zero, which reads as bad. The release verdict stays — a new figure\n is already refused for its undeclared space, and the way out is offered there.\n */\n const report = this.report\n if (!report) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.maturity')}</h3>\n <p quiet>${i18next.t('figure.text.report-appears-when-you-add-a-part')}</p>\n </section>\n ${this.renderGate()}\n `\n }\n return html`\n ${this.renderVerdict(report)} ${this.renderAxes(report)} ${this.renderBudgets(report)} ${this.renderMetrics(report)}\n ${this.renderCurve()} ${this.renderGate()} ${this.renderSheet(report)}\n `\n }\n\n /**\n * The head — how many fit, with the grade beside it, the budget that decided it, the scale, and the basis.\n *\n * The count's denominator is measured (2026-09-18, Apple M5) and says so with the machine and the date; the\n * triangle side is still the design document's target and says that.\n */\n private renderVerdict(report: FigureReport) {\n const { capacity, score } = report\n return html`\n <section>\n <h3>${i18next.t('figure.label.maturity')}</h3>\n <p lead>${i18next.t('figure.label.axis-scale')} — ${i18next.t('figure.text.how-many-fit')}</p>\n <div verdict>\n <span capacity>${capacity.instances.toLocaleString()}</span>\n <span unit>${i18next.t('figure.text.instances-unit')}</span>\n <span\n grade\n ?tight=${score.grade === 'C'}\n ?over=${score.grade === 'D'}\n title=${i18next.t('figure.text.grade-explained', { target: LIMITS.screenInstances })}\n >\n ${score.grade}\n <small>${i18next.t('figure.text.headroom-percent', { percent: score.score })}</small>\n </span>\n </div>\n <p because>\n ${i18next.t('figure.text.grade-decided-by')}\n <strong>${i18next.t('figure.label.budget-' + score.tightest.key)}</strong>\n — ${score.tightest.used.toLocaleString()} / ${limitOf(score.tightest)} (${Math.round(score.tightest.ratio * 100)}%)\n </p>\n <p basis>\n ${i18next.t('figure.text.grade-target', {\n target: LIMITS.screenInstances,\n groups: Math.floor(FIGURE_PER_INSTANCE.groups),\n triangles: Math.floor(FIGURE_PER_INSTANCE.triangles).toLocaleString()\n })}\n </p>\n ${this.renderGradeScale(score.grade)}\n <p basis>\n ${i18next.t('figure.text.bound-by-' + capacity.bound, {\n per: capacity.per.toLocaleString(),\n budget: capacity.budget.toLocaleString()\n })}\n <em>\n ${capacity.bound === 'groups'\n ? i18next.t('figure.text.budget-measured-on', { gpu: MEASURED.gpu, on: MEASURED.on })\n : i18next.t('figure.text.budget-not-measured-yet')}\n </em>\n </p>\n </section>\n `\n }\n\n /** A · B · C · D and where this figure stands, drawn from the bounds the grade is computed with. */\n private renderGradeScale(now: string) {\n return html`\n <p scale>\n <span>${i18next.t('figure.text.grade-scale-lead')}</span>\n ${gradeBoundsOf().map(([grade, bound]) => html`<span ?now=${grade === now}><b>${grade}</b> ${bound}</span>`)}\n </p>\n `\n }\n\n /**\n * Maturity by axis — a radar of the measured axes, a line naming the unmeasured ones, and the list.\n *\n * Scale is left out of the list: it leads the tab, and the same number twice reads as two different things.\n */\n private renderAxes(report: FigureReport) {\n const measured = report.axes.filter(axis => axis.ratio !== undefined)\n const unmeasured = report.axes.filter(axis => axis.ratio === undefined)\n return html`\n <section>\n <h3>${i18next.t('figure.label.by-axis')}</h3>\n <div radar>${this.renderRadar(measured)}</div>\n ${unmeasured.length\n ? html`<p unmeasured>\n ${i18next.t('figure.text.axes-not-measurable-yet', {\n axes: unmeasured.map(axis => i18next.t('figure.label.axis-' + axis.key)).join(' · ')\n })}\n </p>`\n : nothing}\n <ul axes>\n ${report.axes.filter(axis => axis.key !== 'scale').map(axis => this.renderAxis(axis))}\n </ul>\n </section>\n `\n }\n\n /** The radar: rings at a third, two thirds and full, short names so the left edge is not cut. */\n private renderRadar(axes: FigureMaturityAxis[]) {\n const cx = 60\n const cy = 46\n const r = 28\n const at = (index: number, ratio: number) => {\n const angle = -Math.PI / 2 + (index * 2 * Math.PI) / axes.length\n return [cx + r * ratio * Math.cos(angle), cy + r * ratio * Math.sin(angle)]\n }\n const clamp = (ratio?: number) => Math.max(0.04, Math.min(1, ratio ?? 0))\n const points = axes.map((axis, index) => at(index, clamp(axis.ratio)).map(n => n.toFixed(1)).join(',')).join(' ')\n\n return svg`\n <svg viewBox=\"0 0 120 96\" aria-hidden=\"true\">\n ${[0.33, 0.66, 1].map(\n ring => svg`<polygon web points=${axes.map((_, i) => at(i, ring).map(n => n.toFixed(1)).join(',')).join(' ')}></polygon>`\n )}\n ${axes.map((_, index) => {\n const [x, y] = at(index, 1)\n return svg`<line spoke x1=${cx} y1=${cy} x2=${x.toFixed(1)} y2=${y.toFixed(1)}></line>`\n })}\n <polygon shape points=${points}></polygon>\n ${axes.map((axis, index) => {\n const [x, y] = at(index, clamp(axis.ratio))\n return svg`<circle dot cx=${x.toFixed(1)} cy=${y.toFixed(1)} r=\"1.4\"></circle>`\n })}\n ${axes.map((axis, index) => {\n const [x, y] = at(index, 1.28)\n const anchor = x < cx - 3 ? 'end' : x > cx + 3 ? 'start' : 'middle'\n return svg`<text x=${x.toFixed(1)} y=${(y + 1.8).toFixed(1)} text-anchor=${anchor}>${i18next.t('figure.label.axis-' + axis.key + '-short')}</text>`\n })}\n </svg>\n `\n }\n\n private renderAxis(axis: FigureMaturityAxis) {\n const name = i18next.t('figure.label.axis-' + axis.key)\n if (axis.blocked) {\n return html`\n <li blocked>\n <span>${name}</span>\n <span reading>${i18next.t('figure.text.not-measurable-yet')}</span>\n <em>${i18next.t(axis.blocked)}</em>\n </li>\n `\n }\n const ratio = Math.max(0, Math.min(1, axis.ratio ?? 0))\n return html`\n <li>\n <span>${name}</span>\n <span reading>${axis.readingKey ? i18next.t(axis.readingKey) : (axis.reading ?? '')}</span>\n <div bar><i style=\"width:${Math.round(ratio * 100)}%\"></i></div>\n ${axis.because ? html`<em>${i18next.t(axis.because)}</em>` : nothing}\n </li>\n `\n }\n\n /** The budget bars. The one closest to its limit is coloured — it decided the grade. */\n private renderBudgets(report: FigureReport) {\n const { score } = report\n return html`\n <section>\n <h3>${i18next.t('figure.label.budgets')}</h3>\n <ul budgets>\n ${score.budgets.map(\n use => html`\n <li>\n <span>${i18next.t('figure.label.budget-' + use.key)}</span>\n <span>${use.used.toLocaleString()} / ${limitOf(use)}</span>\n <div bar><i ?tight=${use.key === score.tightest.key} style=\"width:${Math.round(Math.min(1, use.ratio) * 100)}%\"></i></div>\n </li>\n `\n )}\n </ul>\n <p quiet>${i18next.t('figure.text.budgets-not-in')}</p>\n </section>\n `\n }\n\n private renderMetrics(report: FigureReport) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.weight')}</h3>\n <dl metrics>\n <dt>${i18next.t('figure.label.triangles')}</dt>\n <dd>${report.cost.triangles.toLocaleString()}</dd>\n <dt>${i18next.t('figure.label.distinct-shapes')}</dt>\n <dd>${report.distinctShapes}</dd>\n <dt>${i18next.t('figure.label.distinct-materials')}</dt>\n <dd>${report.distinctMaterials}</dd>\n </dl>\n </section>\n `\n }\n\n private renderCurve() {\n return html`\n <section>\n <h3>${i18next.t('figure.label.reuse-trend')}</h3>\n <button expand @click=${() => (this.expanded = true)}>${i18next.t('figure.button.see-the-trend-chart')}</button>\n </section>\n `\n }\n\n /**\n * The trend at its own size: both axes logarithmic, the growing lines against the flat ones, and a\n * legend with each line's value at the last count.\n */\n private renderSheet(report: FigureReport) {\n if (!this.expanded) return nothing\n const layout = curveLayout(report.curve)\n if (!layout) return nothing\n const { width, height, pad } = CURVE_SIZE\n const plotW = width - pad.left - pad.right\n const plotH = height - pad.top - pad.bottom\n const last = report.curve[report.curve.length - 1]!\n const label = (key: string) =>\n i18next.t(\n {\n triangles: 'figure.label.triangles',\n drawCalls: 'figure.label.draw-calls',\n geometries: 'figure.label.vertex-buffers',\n materials: 'figure.label.distinct-materials'\n }[key]!\n )\n\n return html`\n <div sheet @click=${() => (this.expanded = false)}>\n <figure @click=${(e: Event) => e.stopPropagation()}>\n ${svg`\n <svg viewBox=\"0 0 ${width} ${height}\" width=${width} height=${height} role=\"img\">\n <text x=${pad.left} y=\"22\" font-size=\"13\" font-weight=\"600\">${i18next.t('figure.text.trend-chart-title')}</text>\n <text x=${pad.left} y=\"38\" muted>${i18next.t('figure.text.trend-chart-lead')}</text>\n ${layout.yTicks.map(\n tick => svg`\n <line grid x1=${pad.left} y1=${tick.y.toFixed(1)} x2=${(pad.left + plotW).toFixed(1)} y2=${tick.y.toFixed(1)}></line>\n <text muted x=${pad.left - 8} y=${(tick.y + 3.5).toFixed(1)} text-anchor=\"end\">${tick.label}</text>\n `\n )}\n ${layout.xTicks.map(\n tick => svg`\n <line grid x1=${tick.x.toFixed(1)} y1=${pad.top} x2=${tick.x.toFixed(1)} y2=${(pad.top + plotH).toFixed(1)}></line>\n <text muted x=${tick.x.toFixed(1)} y=${(pad.top + plotH + 16).toFixed(1)} text-anchor=\"middle\">${tick.label}</text>\n `\n )}\n <text muted x=${(pad.left + plotW / 2).toFixed(1)} y=${height - 8} text-anchor=\"middle\">${i18next.t('figure.label.instance-count')}</text>\n ${layout.lines.map(\n line => svg`<path series=${line.key} d=${line.d} stroke-dasharray=${line.dashed ? '3 7' : 'none'}></path>`\n )}\n <g transform=\"translate(${width - pad.right + 14}, ${pad.top - 6})\">\n <text x=\"0\" y=\"0\" font-weight=\"600\">${i18next.t('figure.text.trend-at-count', { n: shortNumber(last.instances) })}</text>\n ${layout.lines.map((line, i) => {\n const y = 24 + i * 34\n return svg`\n <line series=${line.key} x1=\"0\" y1=${y - 4} x2=\"18\" y2=${y - 4} stroke-width=\"2\" stroke-dasharray=${line.dashed ? '3 7' : 'none'}></line>\n <text x=\"24\" y=${y}>${label(line.key)}</text>\n <text series=${line.key} x=\"24\" y=${y + 12} font-weight=\"600\">\n ${line.last.toLocaleString()}${line.flat ? ` · ${i18next.t('figure.text.trend-stays')}` : ''}\n </text>\n `\n })}\n ${layout.overlap !== undefined\n ? svg`<text muted x=\"0\" y=${24 + layout.lines.length * 34 + 6}>${i18next.t('figure.text.trend-flat-lines-overlap', { value: layout.overlap.toLocaleString() })}</text>`\n : nothing}\n </g>\n </svg>\n `}\n <figcaption>\n <button close @click=${() => (this.expanded = false)}>${i18next.t('figure.button.close')}</button>\n </figcaption>\n </figure>\n </div>\n `\n }\n\n private renderGate() {\n const findings = this.inspection?.findings ?? []\n const blocking = findings.filter(finding => finding.blocking)\n const advisory = findings.filter(finding => !finding.blocking)\n return html`\n <section>\n <h3>${i18next.t('figure.label.release-verdict')}</h3>\n ${this.failure\n ? html`<p failed role=\"alert\">${i18next.t('figure.text.verdict-not-received', { reason: this.failure })}</p>`\n : !this.inspection\n ? html`<p quiet>${i18next.t(this.asking ? 'figure.text.verdict-asking' : 'figure.text.verdict-waiting')}</p>`\n : blocking.length\n ? html`<p>${i18next.t('figure.text.cannot-release-now')}</p>`\n : html`<p clear>${i18next.t('figure.text.nothing-blocks-release')}</p>`}\n ${blocking.length\n ? html`<ul gate>\n <li head>${i18next.t('figure.text.must-fix-to-release')}</li>\n ${blocking.map(finding => renderGateFinding(finding, true, this.actionsFor(finding.code), this.partNames()))}\n </ul>`\n : nothing}\n ${advisory.length\n ? html`<ul gate>\n <li head quiet>${i18next.t('figure.text.does-not-block')}</li>\n ${advisory.map(finding => renderGateFinding(finding, false, nothing, this.partNames()))}\n </ul>`\n : nothing}\n </section>\n `\n }\n\n private partNames(): string[] {\n return (this.revision?.asset.appearance ?? []).map(look => look.target)\n }\n\n /** The same way out the release panel offers for the same two findings — declare the space from the parts. */\n private actionsFor(code: string) {\n if (code !== 'part-outside-occupancy' && code !== 'occupancy-undeclared') return nothing\n /* A new figure with no parts is refused as undeclared too, but fitting needs parts to measure (seen 2026-09-24). */\n if (!this.bodies) return nothing\n return html`<button fit @click=${this.fit}>${i18next.t('figure.button.fit-base-to-parts')}</button>`\n }\n\n private fit = () => {\n this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-verdict.js","sourceRoot":"","sources":["../../client/modeller/figure-verdict.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { LIMITS, FIGURE_PER_INSTANCE } from '@hatiolab/figure-model'\n\nimport { inspectFigureDraft } from '../graphql/index.js'\nimport type { FigureInspection } from '../types.js'\nimport { gateFindingStyles, renderGateFinding } from './gate-findings.js'\nimport { MEASURED } from './scene-budget.js'\nimport type { FigureRevision } from './revision.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { CURVE_SIZE, curveLayout, gradeBoundsOf, shortNumber, verdictMeasureOf } from './maturity.js'\nimport { newestAnswer } from './newest-answer.js'\nimport type { FigureBudgetUse, FigureMaturityAxis, FigureReport } from './maturity.js'\n\n/** How long an edit settles before the server is asked again. One question per pause, not one per keystroke. */\nexport const VERDICT_SETTLE_MS = 400\n/** How long the figure rests before its report is measured again. */\nconst MEASURE_SETTLE_MS = 200\n\n/**\n * 성숙도 — the modeller's verdict while editing.\n *\n * It shows, as the author edits: how many fit on one screen with the grade beside it and the budget that\n * decided the grade, the grade scale, six maturity axes as a radar and a list, the budget bars, the drawing cost,\n * the reuse trend, and the findings. An earlier tab kept only the count and the findings (user, 2026-09-24:\n * 「분석부분도 많이 부족하고, 왜 이렇게 너프되었는지」).\n *\n * ## What is computed where\n *\n * The numbers come from `maturity.ts`, which reads the evaluated asset the way the renderer draws it — the\n * screen calls and shows, it does not count. The count uses the same `capacityFrom` the catalogue cards use, so the\n * tab and the card cannot disagree.\n *\n * The findings do not come from here: they come from the server's release door (`inspectFigureDraft`), the same\n * door and the same sentences as the release panel. A second judgement on the screen would say something different\n * the first time either changed. The release judgement is the server's, so that section is the server's answer.\n *\n * ## The grade\n *\n * figure-model's `figureScoreOf`, which the catalogue uses too: headroom against placing `LIMITS.screenInstances` of the\n * figure on one screen (chief architect, 2026-09-24) -- the screen's measured groups and its triangle target shared\n * per instance -- and the rule limits for independently moved parts and transparent materials, decided by the\n * tightest. The two budgets has no field for are left out and the tab says so.\n */\n/** A per-instance share of the screen is not a whole number; what an instance may draw is the whole part of it. */\nconst limitOf = (use: FigureBudgetUse) => Math.floor(use.limit).toLocaleString()\n\n@customElement('figure-verdict')\nexport class FigureVerdict extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n SidePanelStyles,\n gateFindingStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n }\n section p {\n margin: 0;\n }\n\n /* ── How many fit ───────────────────────────────────────────────\n The value of low poly is how many can stand on one screen, so that answer leads. The grade says whether\n the shape keeps inside its declared limits, so it is the small badge beside it. */\n p[lead],\n p[basis],\n p[quiet] {\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[basis] em {\n font-style: normal;\n color: var(--md-sys-color-tertiary);\n }\n div[verdict] {\n display: flex;\n align-items: baseline;\n gap: 6px;\n }\n span[capacity] {\n font-size: var(--ui-t-hero, 26px);\n font-weight: 800;\n line-height: 1;\n letter-spacing: -0.01em;\n color: var(--md-sys-color-primary);\n }\n span[unit] {\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--md-sys-color-primary);\n }\n /* The badge says what it measures (headroom), so its number is not read as a score out of 100. */\n span[grade] {\n display: flex;\n align-items: baseline;\n gap: 5px;\n margin-left: auto;\n padding: 3px 10px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 700;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-radius: 999em;\n }\n span[grade] small {\n display: inline;\n margin: 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 500;\n color: inherit;\n }\n /* C keeps within the limit. Red is for over it (D) only; red on a kept limit asks for a fix nothing needs. */\n span[grade][tight] {\n color: var(--md-sys-color-on-tertiary-container);\n background-color: var(--md-sys-color-tertiary-container);\n }\n span[grade][over] {\n color: var(--md-sys-color-on-error-container);\n background-color: var(--md-sys-color-error-container);\n }\n p[because] {\n font-size: var(--ui-t-small, 11.5px);\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[because] strong {\n color: var(--md-sys-color-on-surface);\n }\n /* The grade scale. Not folded away: it is compared against every time, not learned once. */\n p[scale] {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 10px;\n font-size: var(--ui-t-micro, 11px);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[scale] b {\n font-weight: 700;\n }\n p[scale] span[now] {\n color: var(--md-sys-color-on-surface);\n }\n p[scale] span[now] b {\n color: var(--md-sys-color-primary);\n }\n\n /* ── Maturity radar ─────────────────────────────────────────────\n Only the measured axes are drawn; an unmeasured one drawn as a spoke reads as a dent, which is a zero. */\n div[radar] {\n display: flex;\n justify-content: center;\n padding: 2px 0 6px 0;\n color: var(--md-sys-color-primary);\n }\n div[radar] svg {\n width: 100%;\n max-width: 240px;\n height: auto;\n }\n div[radar] polygon[web] {\n fill: none;\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 0.6;\n }\n div[radar] line[spoke] {\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 0.6;\n }\n div[radar] polygon[shape] {\n fill: color-mix(in srgb, var(--md-sys-color-primary) 22%, transparent);\n stroke: var(--md-sys-color-primary);\n stroke-width: 1.4;\n stroke-linejoin: round;\n }\n div[radar] circle[dot] {\n fill: var(--md-sys-color-primary);\n }\n div[radar] text {\n /* svg-units: the radar's own coordinates (its viewBox), drawn to the chart's size -- not a page size. */\n font-size: 5.2px;\n fill: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n p[unmeasured] {\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.5;\n color: var(--md-sys-color-tertiary);\n }\n\n /* ── Axes and budgets ───────────────────────────────────────────\n Not merged into one number: merged, 「what to fix」 disappears. */\n ul[axes],\n ul[budgets] {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 7px;\n }\n ul[axes] li,\n ul[budgets] li {\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 2px 8px;\n font-size: var(--ui-t-small, 11.5px);\n }\n ul[axes] li[blocked] {\n opacity: 0.7;\n }\n ul[axes] em {\n grid-column: 1 / -1;\n font-style: normal;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n ul[axes] li[blocked] em {\n color: var(--md-sys-color-tertiary);\n }\n span[reading] {\n font-family: var(--ui-mono, ui-monospace, monospace);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[bar] {\n grid-column: 1 / -1;\n height: 4px;\n background-color: var(--md-sys-color-surface-container-highest);\n border-radius: 2px;\n overflow: hidden;\n }\n div[bar] > i {\n display: block;\n height: 100%;\n background-color: var(--md-sys-color-primary);\n }\n div[bar] > i[tight] {\n background-color: var(--md-sys-color-error);\n }\n\n /* ── Drawing cost ─────────────────────────────────────────────── */\n dl[metrics] {\n margin: 0;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 4px 8px;\n font-size: var(--ui-t-small, 11.5px);\n }\n dl[metrics] dt {\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n dl[metrics] dd {\n margin: 0;\n text-align: right;\n font-family: var(--ui-mono, ui-monospace, monospace);\n }\n\n /* ── Trend ──────────────────────────────────────────────────────\n The chart's natural size is 680px; in a 300px side its words are 4px. The side holds a button, the chart\n opens at its own size. */\n button[expand] {\n justify-self: start;\n padding: 4px 10px;\n font: inherit;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[expand]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n div[sheet] {\n position: fixed;\n inset: 0;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n background-color: color-mix(in srgb, var(--md-sys-color-scrim, black) 32%, transparent);\n }\n div[sheet] > figure {\n max-width: 100%;\n max-height: 100%;\n margin: 0;\n padding: 12px;\n overflow: auto;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border-radius: 6px;\n box-shadow: 0 8px 32px color-mix(in srgb, var(--md-sys-color-shadow, black) 28%, transparent);\n }\n div[sheet] svg {\n display: block;\n /* Not shrunk below its natural size — a narrow window scrolls instead. */\n min-width: 680px;\n font-family: inherit;\n font-size: var(--ui-t-micro, 11px);\n fill: currentColor;\n }\n div[sheet] svg [grid] {\n stroke: currentColor;\n stroke-opacity: 0.12;\n }\n div[sheet] svg [muted] {\n opacity: 0.55;\n }\n div[sheet] svg path[series] {\n fill: none;\n stroke-width: 2;\n stroke-linejoin: round;\n }\n div[sheet] svg [series='triangles'] {\n stroke: var(--md-sys-color-error);\n fill: var(--md-sys-color-error);\n }\n div[sheet] svg [series='drawCalls'] {\n stroke: var(--md-sys-color-tertiary);\n fill: var(--md-sys-color-tertiary);\n }\n div[sheet] svg [series='geometries'] {\n stroke: var(--md-sys-color-primary);\n fill: var(--md-sys-color-primary);\n }\n div[sheet] svg [series='materials'] {\n stroke: var(--md-sys-color-secondary);\n fill: var(--md-sys-color-secondary);\n }\n div[sheet] svg path[series],\n div[sheet] svg line[series] {\n fill: none;\n }\n /* A legend value is written in its line's colour, not outlined in it. */\n div[sheet] svg text[series] {\n stroke: none;\n }\n div[sheet] figcaption {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n }\n div[sheet] button[close] {\n padding: 5px 14px;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n cursor: pointer;\n }\n\n /* ── Release verdict ───────────────────────────────────────────── */\n p[clear] {\n color: var(--md-sys-color-primary);\n font-weight: 600;\n }\n p[failed] {\n color: var(--md-sys-color-error);\n }\n button[fit] {\n align-self: flex-start;\n margin-top: 4px;\n padding: 4px 10px;\n font: inherit;\n color: var(--md-sys-color-on-secondary-container);\n background: var(--md-sys-color-secondary-container);\n border: none;\n border-radius: 999em;\n cursor: pointer;\n }\n `\n ]\n\n /** The figure at the version on screen. A version whose text is another is a new question. */\n @property({ attribute: false }) revision?: FigureRevision\n /* The text last judged: a save hands out the same version again as a new object, which asks nothing new. */\n private judged?: string\n /** The saved row, if any — the server reads its type and last release from it. */\n @property({ type: String }) figureId?: string\n /** Changing the type changes the verdict (a temporary type is refused), so it is a reason to ask again. */\n @property({ type: String }) figureType?: string\n\n /** The report over the source being edited. Absent when nothing is drawn, or the format refuses the source. */\n @state() private report?: FigureReport\n /** Whether the trend chart is open at its own size. */\n @state() private expanded = false\n @state() private inspection?: FigureInspection\n @state() private failure = ''\n @state() private asking = false\n\n private timer?: ReturnType<typeof setTimeout>\n /** Answers can come back out of order; only the newest question's answer is shown. */\n private answers = newestAnswer<FigureInspection>()\n\n disconnectedCallback() {\n clearTimeout(this.timer)\n clearTimeout(this.measureTimer)\n super.disconnectedCallback()\n }\n\n /** The report is measured once the figure has settled: it is read beside the work, and each measure compiles the figure several times. */\n private measureTimer?: ReturnType<typeof setTimeout>\n updated(changes: Map<string, unknown>) {\n const text = changes.has('revision') ? this.revision?.text : this.judged\n const changed = text !== this.judged\n if (changed) {\n this.judged = text\n clearTimeout(this.measureTimer)\n if (this.report === undefined && !this.measureTimer) this.measure()\n else this.measureTimer = setTimeout(() => this.measure(), MEASURE_SETTLE_MS)\n }\n if (changed || changes.has('figureId') || changes.has('figureType')) this.schedule()\n /* What blocks release, told to the page so the bar can say it beside 「발행」 (first 15 minutes, 2026-10-16). */\n if (changes.has('inspection'))\n this.dispatchEvent(new CustomEvent('release-verdict', { detail: { blocking: (this.inspection?.findings ?? []).filter(finding => finding.blocking) } }))\n }\n\n private measure() {\n const { report, told } = verdictMeasureOf(this.revision?.asset)\n this.report = report\n if (!report) this.expanded = false\n this.dispatchEvent(new CustomEvent('capacity-changed', { detail: told }))\n }\n\n private schedule() {\n clearTimeout(this.timer)\n if (!this.revision) {\n this.inspection = undefined\n return\n }\n this.timer = setTimeout(() => this.ask(), VERDICT_SETTLE_MS)\n }\n\n private async ask() {\n const source = this.revision!.text\n this.asking = true\n await this.answers(\n () => inspectFigureDraft(source, this.figureId),\n outcome => {\n this.asking = false\n if ('answer' in outcome) {\n this.inspection = outcome.answer\n this.failure = ''\n } else {\n /* The server's reason as it gave it — the screen does not make one up. The last answer is dropped with it. */\n this.inspection = undefined\n this.failure = outcome.failure instanceof Error ? outcome.failure.message : String(outcome.failure)\n }\n }\n )\n }\n\n render() {\n /*\n With nothing drawn the report is guidance, not a verdict: the sections below have meaning only over parts,\n and a number drawn from nothing reads as zero, which reads as bad. The release verdict stays — a new figure\n is already refused for its undeclared space, and the way out is offered there.\n */\n const report = this.report\n if (!report) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.maturity')}</h3>\n <p quiet>${i18next.t('figure.text.report-appears-when-you-add-a-part')}</p>\n </section>\n ${this.renderGate()}\n `\n }\n return html`\n ${this.renderVerdict(report)} ${this.renderAxes(report)} ${this.renderBudgets(report)} ${this.renderMetrics(report)}\n ${this.renderCurve()} ${this.renderGate()} ${this.renderSheet(report)}\n `\n }\n\n /**\n * The head — how many fit, with the grade beside it, the budget that decided it, the scale, and the basis.\n *\n * The count's denominator is measured (2026-09-18, Apple M5) and says so with the machine and the date; the\n * triangle side is still the design document's target and says that.\n */\n private renderVerdict(report: FigureReport) {\n const { capacity, score } = report\n return html`\n <section>\n <h3>${i18next.t('figure.label.maturity')}</h3>\n <p lead>${i18next.t('figure.label.axis-scale')} — ${i18next.t('figure.text.how-many-fit')}</p>\n <div verdict>\n <span capacity>${capacity.instances.toLocaleString()}</span>\n <span unit>${i18next.t('figure.text.instances-unit')}</span>\n <span\n grade\n ?tight=${score.grade === 'C'}\n ?over=${score.grade === 'D'}\n title=${i18next.t('figure.text.grade-explained', { target: LIMITS.screenInstances })}\n >\n ${score.grade}\n <small>${i18next.t('figure.text.headroom-percent', { percent: score.score })}</small>\n </span>\n </div>\n <p because>\n ${i18next.t('figure.text.grade-decided-by')}\n <strong>${i18next.t('figure.label.budget-' + score.tightest.key)}</strong>\n — ${score.tightest.used.toLocaleString()} / ${limitOf(score.tightest)} (${Math.round(score.tightest.ratio * 100)}%)\n </p>\n <p basis>\n ${i18next.t('figure.text.grade-target', {\n target: LIMITS.screenInstances,\n groups: Math.floor(FIGURE_PER_INSTANCE.groups),\n triangles: Math.floor(FIGURE_PER_INSTANCE.triangles).toLocaleString()\n })}\n </p>\n ${this.renderGradeScale(score.grade)}\n <p basis>\n ${i18next.t('figure.text.bound-by-' + capacity.bound, {\n per: capacity.per.toLocaleString(),\n budget: capacity.budget.toLocaleString()\n })}\n <em>\n ${capacity.bound === 'groups'\n ? i18next.t('figure.text.budget-measured-on', { gpu: MEASURED.gpu, on: MEASURED.on })\n : i18next.t('figure.text.budget-not-measured-yet')}\n </em>\n </p>\n </section>\n `\n }\n\n /** A · B · C · D and where this figure stands, drawn from the bounds the grade is computed with. */\n private renderGradeScale(now: string) {\n return html`\n <p scale>\n <span>${i18next.t('figure.text.grade-scale-lead')}</span>\n ${gradeBoundsOf().map(([grade, bound]) => html`<span ?now=${grade === now}><b>${grade}</b> ${bound}</span>`)}\n </p>\n `\n }\n\n /**\n * Maturity by axis — a radar of the measured axes, a line naming the unmeasured ones, and the list.\n *\n * Scale is left out of the list: it leads the tab, and the same number twice reads as two different things.\n */\n private renderAxes(report: FigureReport) {\n const measured = report.axes.filter(axis => axis.ratio !== undefined)\n const unmeasured = report.axes.filter(axis => axis.ratio === undefined)\n return html`\n <section>\n <h3>${i18next.t('figure.label.by-axis')}</h3>\n <div radar>${this.renderRadar(measured)}</div>\n ${unmeasured.length\n ? html`<p unmeasured>\n ${i18next.t('figure.text.axes-not-measurable-yet', {\n axes: unmeasured.map(axis => i18next.t('figure.label.axis-' + axis.key)).join(' · ')\n })}\n </p>`\n : nothing}\n <ul axes>\n ${report.axes.filter(axis => axis.key !== 'scale').map(axis => this.renderAxis(axis))}\n </ul>\n </section>\n `\n }\n\n /** The radar: rings at a third, two thirds and full, short names so the left edge is not cut. */\n private renderRadar(axes: FigureMaturityAxis[]) {\n const cx = 60\n const cy = 46\n const r = 28\n const at = (index: number, ratio: number) => {\n const angle = -Math.PI / 2 + (index * 2 * Math.PI) / axes.length\n return [cx + r * ratio * Math.cos(angle), cy + r * ratio * Math.sin(angle)]\n }\n const clamp = (ratio?: number) => Math.max(0.04, Math.min(1, ratio ?? 0))\n const points = axes.map((axis, index) => at(index, clamp(axis.ratio)).map(n => n.toFixed(1)).join(',')).join(' ')\n\n return svg`\n <svg viewBox=\"0 0 120 96\" aria-hidden=\"true\">\n ${[0.33, 0.66, 1].map(\n ring => svg`<polygon web points=${axes.map((_, i) => at(i, ring).map(n => n.toFixed(1)).join(',')).join(' ')}></polygon>`\n )}\n ${axes.map((_, index) => {\n const [x, y] = at(index, 1)\n return svg`<line spoke x1=${cx} y1=${cy} x2=${x.toFixed(1)} y2=${y.toFixed(1)}></line>`\n })}\n <polygon shape points=${points}></polygon>\n ${axes.map((axis, index) => {\n const [x, y] = at(index, clamp(axis.ratio))\n return svg`<circle dot cx=${x.toFixed(1)} cy=${y.toFixed(1)} r=\"1.4\"></circle>`\n })}\n ${axes.map((axis, index) => {\n const [x, y] = at(index, 1.28)\n const anchor = x < cx - 3 ? 'end' : x > cx + 3 ? 'start' : 'middle'\n return svg`<text x=${x.toFixed(1)} y=${(y + 1.8).toFixed(1)} text-anchor=${anchor}>${i18next.t('figure.label.axis-' + axis.key + '-short')}</text>`\n })}\n </svg>\n `\n }\n\n private renderAxis(axis: FigureMaturityAxis) {\n const name = i18next.t('figure.label.axis-' + axis.key)\n if (axis.blocked) {\n return html`\n <li blocked>\n <span>${name}</span>\n <span reading>${i18next.t('figure.text.not-measurable-yet')}</span>\n <em>${i18next.t(axis.blocked)}</em>\n </li>\n `\n }\n const ratio = Math.max(0, Math.min(1, axis.ratio ?? 0))\n return html`\n <li>\n <span>${name}</span>\n <span reading>${axis.readingKey ? i18next.t(axis.readingKey) : (axis.reading ?? '')}</span>\n <div bar><i style=\"width:${Math.round(ratio * 100)}%\"></i></div>\n ${axis.because ? html`<em>${i18next.t(axis.because)}</em>` : nothing}\n </li>\n `\n }\n\n /** The budget bars. The one closest to its limit is coloured — it decided the grade. */\n private renderBudgets(report: FigureReport) {\n const { score } = report\n return html`\n <section>\n <h3>${i18next.t('figure.label.budgets')}</h3>\n <ul budgets>\n ${score.budgets.map(\n use => html`\n <li>\n <span>${i18next.t('figure.label.budget-' + use.key)}</span>\n <span>${use.used.toLocaleString()} / ${limitOf(use)}</span>\n <div bar><i ?tight=${use.key === score.tightest.key} style=\"width:${Math.round(Math.min(1, use.ratio) * 100)}%\"></i></div>\n </li>\n `\n )}\n </ul>\n <p quiet>${i18next.t('figure.text.budgets-not-in')}</p>\n </section>\n `\n }\n\n private renderMetrics(report: FigureReport) {\n return html`\n <section>\n <h3>${i18next.t('figure.label.weight')}</h3>\n <dl metrics>\n <dt>${i18next.t('figure.label.triangles')}</dt>\n <dd>${report.cost.triangles.toLocaleString()}</dd>\n <dt>${i18next.t('figure.label.distinct-shapes')}</dt>\n <dd>${report.distinctShapes}</dd>\n <dt>${i18next.t('figure.label.distinct-materials')}</dt>\n <dd>${report.distinctMaterials}</dd>\n </dl>\n </section>\n `\n }\n\n private renderCurve() {\n return html`\n <section>\n <h3>${i18next.t('figure.label.reuse-trend')}</h3>\n <button expand @click=${() => (this.expanded = true)}>${i18next.t('figure.button.see-the-trend-chart')}</button>\n </section>\n `\n }\n\n /**\n * The trend at its own size: both axes logarithmic, the growing lines against the flat ones, and a\n * legend with each line's value at the last count.\n */\n private renderSheet(report: FigureReport) {\n if (!this.expanded) return nothing\n const layout = curveLayout(report.curve)\n if (!layout) return nothing\n const { width, height, pad } = CURVE_SIZE\n const plotW = width - pad.left - pad.right\n const plotH = height - pad.top - pad.bottom\n const last = report.curve[report.curve.length - 1]!\n const label = (key: string) =>\n i18next.t(\n {\n triangles: 'figure.label.triangles',\n drawCalls: 'figure.label.draw-calls',\n geometries: 'figure.label.vertex-buffers',\n materials: 'figure.label.distinct-materials'\n }[key]!\n )\n\n return html`\n <div sheet @click=${() => (this.expanded = false)}>\n <figure @click=${(e: Event) => e.stopPropagation()}>\n ${svg`\n <svg viewBox=\"0 0 ${width} ${height}\" width=${width} height=${height} role=\"img\">\n <text x=${pad.left} y=\"22\" font-size=\"13\" font-weight=\"600\">${i18next.t('figure.text.trend-chart-title')}</text>\n <text x=${pad.left} y=\"38\" muted>${i18next.t('figure.text.trend-chart-lead')}</text>\n ${layout.yTicks.map(\n tick => svg`\n <line grid x1=${pad.left} y1=${tick.y.toFixed(1)} x2=${(pad.left + plotW).toFixed(1)} y2=${tick.y.toFixed(1)}></line>\n <text muted x=${pad.left - 8} y=${(tick.y + 3.5).toFixed(1)} text-anchor=\"end\">${tick.label}</text>\n `\n )}\n ${layout.xTicks.map(\n tick => svg`\n <line grid x1=${tick.x.toFixed(1)} y1=${pad.top} x2=${tick.x.toFixed(1)} y2=${(pad.top + plotH).toFixed(1)}></line>\n <text muted x=${tick.x.toFixed(1)} y=${(pad.top + plotH + 16).toFixed(1)} text-anchor=\"middle\">${tick.label}</text>\n `\n )}\n <text muted x=${(pad.left + plotW / 2).toFixed(1)} y=${height - 8} text-anchor=\"middle\">${i18next.t('figure.label.instance-count')}</text>\n ${layout.lines.map(\n line => svg`<path series=${line.key} d=${line.d} stroke-dasharray=${line.dashed ? '3 7' : 'none'}></path>`\n )}\n <g transform=\"translate(${width - pad.right + 14}, ${pad.top - 6})\">\n <text x=\"0\" y=\"0\" font-weight=\"600\">${i18next.t('figure.text.trend-at-count', { n: shortNumber(last.instances) })}</text>\n ${layout.lines.map((line, i) => {\n const y = 24 + i * 34\n return svg`\n <line series=${line.key} x1=\"0\" y1=${y - 4} x2=\"18\" y2=${y - 4} stroke-width=\"2\" stroke-dasharray=${line.dashed ? '3 7' : 'none'}></line>\n <text x=\"24\" y=${y}>${label(line.key)}</text>\n <text series=${line.key} x=\"24\" y=${y + 12} font-weight=\"600\">\n ${line.last.toLocaleString()}${line.flat ? ` · ${i18next.t('figure.text.trend-stays')}` : ''}\n </text>\n `\n })}\n ${layout.overlap !== undefined\n ? svg`<text muted x=\"0\" y=${24 + layout.lines.length * 34 + 6}>${i18next.t('figure.text.trend-flat-lines-overlap', { value: layout.overlap.toLocaleString() })}</text>`\n : nothing}\n </g>\n </svg>\n `}\n <figcaption>\n <button close @click=${() => (this.expanded = false)}>${i18next.t('figure.button.close')}</button>\n </figcaption>\n </figure>\n </div>\n `\n }\n\n private renderGate() {\n const findings = this.inspection?.findings ?? []\n const blocking = findings.filter(finding => finding.blocking)\n const advisory = findings.filter(finding => !finding.blocking)\n return html`\n <section>\n <h3>${i18next.t('figure.label.release-verdict')}</h3>\n ${this.failure\n ? html`<p failed role=\"alert\">${i18next.t('figure.text.verdict-not-received', { reason: this.failure })}</p>`\n : !this.inspection\n ? html`<p quiet>${i18next.t(this.asking ? 'figure.text.verdict-asking' : 'figure.text.verdict-waiting')}</p>`\n : blocking.length\n ? html`<p>${i18next.t('figure.text.cannot-release-now')}</p>`\n : html`<p clear>${i18next.t('figure.text.nothing-blocks-release')}</p>`}\n ${blocking.length\n ? html`<ul gate>\n <li head>${i18next.t('figure.text.must-fix-to-release')}</li>\n ${blocking.map(finding => renderGateFinding(finding, true, nothing, this.partNames()))}\n </ul>`\n : nothing}\n ${advisory.length\n ? html`<ul gate>\n <li head quiet>${i18next.t('figure.text.does-not-block')}</li>\n ${advisory.map(finding => renderGateFinding(finding, false, nothing, this.partNames()))}\n </ul>`\n : nothing}\n </section>\n `\n }\n\n private partNames(): string[] {\n return (this.revision?.asset.appearance ?? []).map(look => look.target)\n }\n}\n"]}
|
|
@@ -121,13 +121,11 @@ export interface FigureBudgetLine {
|
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* What the verdict tab measures of the figure on screen once it settles: the report,
|
|
125
|
-
*
|
|
126
|
-
* tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
124
|
+
* What the verdict tab measures of the figure on screen once it settles: the report, and what it tells the page: the
|
|
125
|
+
* count for its tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
127
126
|
*/
|
|
128
127
|
export declare function verdictMeasureOf(asset: FigureAsset | undefined): {
|
|
129
128
|
report?: FigureReport;
|
|
130
|
-
bodies: number;
|
|
131
129
|
told: {
|
|
132
130
|
instances?: number;
|
|
133
131
|
budget?: FigureBudgetLine;
|
|
@@ -74,7 +74,7 @@ export function figureReportOf(asset) {
|
|
|
74
74
|
},
|
|
75
75
|
{ key: 'detail', blocked: 'figure.text.maturity-blocked-no-detail-level' },
|
|
76
76
|
{ key: 'reactive', blocked: 'figure.text.maturity-blocked-no-binding' },
|
|
77
|
-
readable(
|
|
77
|
+
readable(drawn, writers)
|
|
78
78
|
];
|
|
79
79
|
return {
|
|
80
80
|
cost,
|
|
@@ -157,32 +157,22 @@ const roundFootprint = (shape) => (shape.extrusion?.axis === 'y' && shape.extrus
|
|
|
157
157
|
* finer segment count does not raise it: H = −Σ p·log p over each material's share of the footprint plus the
|
|
158
158
|
* background, divided by log(`LIMITS.distinctMaterials` + 1), the most this measure can reach.
|
|
159
159
|
*
|
|
160
|
-
* The footprint is the
|
|
161
|
-
* occupancy), otherwise the drawn bodies' own extent. Bodies are their axis-aligned world boxes
|
|
160
|
+
* The footprint is the drawn bodies' own extent, the box a board gives it (ADR-0145). Bodies are their axis-aligned world boxes
|
|
162
161
|
* (`figureWorldBoxesOf`, the gate's reading); the higher one covers the lower.
|
|
163
162
|
*/
|
|
164
|
-
function readable(
|
|
163
|
+
function readable(drawn, writers) {
|
|
165
164
|
const placed = drawn
|
|
166
165
|
.filter(body => 'pose' in body)
|
|
167
166
|
// One placed, drawn body gives exactly one box; boxes are taken one by one to keep each with its material.
|
|
168
167
|
.map(body => ({ body, box: figureWorldBoxesOf([body], writers)[0] }));
|
|
169
168
|
if (placed.length === 0)
|
|
170
169
|
return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' };
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
frame =
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity };
|
|
180
|
-
for (const { box } of placed) {
|
|
181
|
-
frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2);
|
|
182
|
-
frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2);
|
|
183
|
-
frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2);
|
|
184
|
-
frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2);
|
|
185
|
-
}
|
|
170
|
+
const frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity };
|
|
171
|
+
for (const { box } of placed) {
|
|
172
|
+
frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2);
|
|
173
|
+
frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2);
|
|
174
|
+
frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2);
|
|
175
|
+
frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2);
|
|
186
176
|
}
|
|
187
177
|
const regions = topViewRegions(placed, frame);
|
|
188
178
|
if (regions.total === 0)
|
|
@@ -313,23 +303,19 @@ export function curveLayout(points) {
|
|
|
313
303
|
return { xTicks, yTicks, lines, overlap: flat.size === 1 ? [...flat][0] : undefined };
|
|
314
304
|
}
|
|
315
305
|
/**
|
|
316
|
-
* What the verdict tab measures of the figure on screen once it settles: the report,
|
|
317
|
-
*
|
|
318
|
-
* tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
306
|
+
* What the verdict tab measures of the figure on screen once it settles: the report, and what it tells the page: the
|
|
307
|
+
* count for its tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
319
308
|
*/
|
|
320
309
|
export function verdictMeasureOf(asset) {
|
|
321
310
|
let report;
|
|
322
|
-
let bodies = 0;
|
|
323
311
|
try {
|
|
324
|
-
const cost = asset ? figureCostOf(asset) : undefined;
|
|
325
|
-
bodies = cost ? cost.groups + cost.hidden : 0;
|
|
326
312
|
report = asset ? figureReportOf(asset) : undefined;
|
|
327
313
|
}
|
|
328
314
|
catch {
|
|
329
315
|
/* A source the format refuses has no report; the server's finding says why. */
|
|
330
316
|
report = undefined;
|
|
331
317
|
}
|
|
332
|
-
return { report,
|
|
318
|
+
return { report, told: { instances: report?.capacity.instances, budget: budgetOf(report) } };
|
|
333
319
|
}
|
|
334
320
|
export function budgetOf(report) {
|
|
335
321
|
if (!report)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maturity.js","sourceRoot":"","sources":["../../client/modeller/maturity.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n compileFigureAsset,\n compileFigureDocument,\n LIMITS,\n SEGMENT_PRESETS,\n FIGURE_BUDGET_KEYS,\n FIGURE_GRADE_SCALE,\n figureCostOf,\n figureGradeOf,\n figureMaterialKeyOf,\n figureScoreOf,\n figureWorldBoxesOf,\n figureWritersOf\n} from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureAssetGeometry, FigureBudgetKey, FigureBudgetUse, FigureCost, FigureGeometry, FigureScore } from '@hatiolab/figure-model'\n\nimport { capacityFrom } from './scene-budget.js'\nimport type { FigureCapacity } from './scene-budget.js'\n\n/*\n * The modeller's maturity report, read off the evaluated asset (user, 2026-09-24: the tab showed a count and nothing\n * else — 「왜 이렇게 너프되었는지」):\n *\n * grade A–D and its scale figure-model's `figureScoreOf`: one screen's target shared per instance (groups,\n * triangles) and the declared rule limits (independent, transparent)\n * radar · axes six axes; reactive is not measured, and detail has no field\n * drawing cost triangles, distinct shapes, distinct materials\n * trend what grows with the instance count and what the instances share\n *\n * Kept apart from the element so it can be tested without a browser.\n *\n * ## What the renderer shares, which is what the numbers count\n *\n * things-scene draws one mesh per drawn body (`figure-rig.ts`) — no merging by material — so a body is\n * a draw call. It shares geometry by provider, dimensions, segments and profile (`figure-geometry.ts`, `keyOf`)\n * and materials by token, preset, transparency and light (`materialFor`). The keys below are those keys; a\n * distinct-shape count that used another key would describe a renderer that does not exist.\n *\n * ## Budgets does not have\n *\n * Two budgets are not measured here, and are not shown as zero:\n * - off-palette surfaces: the material has no surface field, so there is nothing to count;\n * - part count by detail level: does not carry the detail level (chief architect, 2026-09-24, ADR-0087).\n */\n\n/*\n The grade, its budgets and its bounds are figure-model's (`figureScoreOf`), so the catalogue grades a figure as this tab\n does. The grade is the headroom against one screen's target (`LIMITS.screenInstances`), not a draw-call limit.\n*/\nexport { FIGURE_BUDGET_KEYS, FIGURE_GRADE_SCALE as GRADE_SCALE, figureGradeOf as gradeOf }\nexport type { FigureBudgetKey, FigureBudgetUse, FigureScore }\n\n/** A · B · C · D as the tab draws its scale: each grade with the bound it is computed with, and D past the last. */\nexport function gradeBoundsOf(): [string, string][] {\n return [...FIGURE_GRADE_SCALE.map(([grade, bound]): [string, string] => [grade, `≤ ${bound * 100}%`]), ['D', '> 100%']]\n}\n\nexport type FigureAxisKey = 'scale' | 'consistency' | 'reuse' | 'detail' | 'reactive' | 'readable'\n\n/** One maturity axis. */\nexport interface FigureMaturityAxis {\n key: FigureAxisKey\n /** 0–1. Absent when the axis cannot be measured. */\n ratio?: number\n reading?: string\n /** A reading that is words, not a number. */\n readingKey?: string\n because?: string\n /** Why the axis is not measured. When present there is no ratio — an unmeasured axis is not a zero. */\n blocked?: string\n}\n\n/** Instances on one screen, and what each series costs at that count. */\nexport interface FigureCurvePoint {\n instances: number\n triangles: number\n drawCalls: number\n /** Vertex buffers: one per distinct shape, shared by every instance of the same size. */\n geometries: number\n materials: number\n}\n\nexport interface FigureReport {\n cost: FigureCost\n capacity: FigureCapacity\n score: FigureScore\n /** Distinct shapes and materials among the drawn bodies — the 「그리는 비용」 rows. */\n distinctShapes: number\n distinctMaterials: number\n reuse: { distinct: number; parts: number; saved: number }\n axes: FigureMaturityAxis[]\n curve: FigureCurvePoint[]\n}\n\n/** The board target for the scale axis: 500 assets on one drawing (design document §4.15). */\nexport const BOARD_ASSETS = 500\n\n/**\n * The report for one asset, or nothing when no body is drawn — then there is nothing to measure, and the screen\n * says so rather than drawing zeros.\n *\n * Evaluated at the asset's own design and state defaults, as `figureCostOf` is.\n */\nexport function figureReportOf(asset: FigureAsset): FigureReport | undefined {\n const cost = figureCostOf(asset)\n const capacity = capacityFrom(cost.groups, cost.triangles)\n if (!capacity) return undefined\n\n const drawn = compileFigureAsset(asset)\n .evaluate()\n .geometry.filter(body => body.visible !== false)\n const plan = compileFigureDocument(asset.document)\n const writers = figureWritersOf(asset)\n\n const shapes = new Set(drawn.map(shapeKeyOf))\n const materials = new Set(drawn.map(materialKeyOf))\n\n const score = figureScoreOf(asset)!\n\n const reuse = { distinct: shapes.size, parts: drawn.length, saved: drawn.length - shapes.size }\n\n const axes: FigureMaturityAxis[] = [\n {\n key: 'scale',\n // The board target filled is 1; room beyond it is better spent on the other axes, so it does not count more.\n ratio: Math.min(1, capacity.instances / BOARD_ASSETS),\n reading: String(capacity.instances),\n because: 'figure.text.maturity-scale-because'\n },\n consistency(asset, materials.size),\n {\n key: 'reuse',\n // A low distinct-to-parts ratio is much reuse; turned over so that more is higher.\n ratio: reuse.parts === 0 ? 0 : 1 - reuse.distinct / reuse.parts,\n reading: String(reuse.saved),\n because: 'figure.text.maturity-reuse-because'\n },\n { key: 'detail', blocked: 'figure.text.maturity-blocked-no-detail-level' },\n { key: 'reactive', blocked: 'figure.text.maturity-blocked-no-binding' },\n readable(asset, plan, drawn, writers)\n ]\n\n return {\n cost,\n capacity,\n score,\n distinctShapes: shapes.size,\n distinctMaterials: materials.size,\n reuse,\n axes,\n curve: decadeSteps(1, 10000).map(instances => ({\n instances,\n triangles: cost.triangles * instances,\n drawCalls: cost.groups * instances,\n geometries: shapes.size,\n materials: materials.size\n }))\n }\n}\n\n/**\n * The key things-scene shares a body's geometry by (`figure-geometry.ts` `keyOf`): the operation, what is not in its\n * numbers (the axis, the kind of section, which ends of an inside are open), its numbers to a micrometre, and segments\n * (12 when unset).\n */\nexport function shapeKeyOf(body: FigureAssetGeometry | FigureGeometry): string {\n if (!('shape' in body)) {\n // A scalar body has no mesh of its own; it is still one drawn body, told apart by what it declares.\n const dims = Object.keys(body.dimensions).sort().map(k => `${k}=${body.dimensions[k]}`).join(',')\n return `${body.provider}|${dims}`\n }\n const shape = body.shape\n const round = (v: number) => Math.round(v * 1e6) / 1e6\n const dims = Object.keys(shape.dimensions)\n .sort()\n .map(k => `${k}=${round(shape.dimensions[k]!)}`)\n .join(',')\n const e = shape.extrusion,\n c = shape.cavity,\n r = shape.revolution\n const l = shape.loft\n const meaning = e\n ? `${e.axis}:${e.section.kind}:${c ? `${c.start === 'open' ? 'o' : 'c'}${c.end === 'open' ? 'o' : 'c'}` : '-'}`\n : r\n ? `${r.axis}:${r.profile.kind}`\n : l\n ? `${l.axis}:point`\n : ''\n const segments = (body as { segments?: number }).segments ?? 12\n return `${shape.provider}|${meaning}|${dims}|s${segments}`\n}\n\n/**\n * The key things-scene shares a material by (`figure-rig.ts` `materialFor`), within one asset: token, preset,\n * transparency, and the light when it is on. The colour follows from the token, so it is not a second key.\n */\nexport function materialKeyOf(body: FigureAssetGeometry): string {\n return figureMaterialKeyOf(body)\n}\n\n/**\n * Does it look like one set — baselines 3 and 4, three checks.\n *\n * Two of them hold by construction: the material has no surface field, so there is no texture, and the\n * format refuses a segment count outside the presets (`compileFigureAsset`, `QUALITY`). They are still counted, so\n * the ratio stays one fault in three.\n */\nfunction consistency(asset: FigureAsset, distinctMaterials: number): FigureMaturityAxis {\n const textures = 0\n const offPreset = asset.appearance.filter(look => look.segments !== undefined && !SEGMENT_PRESETS.includes(look.segments)).length\n const faults = (textures > 0 ? 1 : 0) + (offPreset > 0 ? 1 : 0) + (distinctMaterials > LIMITS.distinctMaterials ? 1 : 0)\n return {\n key: 'consistency',\n ratio: (3 - faults) / 3,\n readingKey: faults === 0 ? 'figure.text.maturity-consistency-clean' : 'figure.text.maturity-consistency-faults',\n because: 'figure.text.maturity-consistency-because'\n }\n}\n\n/** Seen from above, these do not fill their footprint box, by provider name. */\n/** Round seen from above: a circle or an ellipse pushed along Y, anything turned about Y. */\nconst roundFootprint = (shape: { extrusion?: { axis: string; section: { kind: string } }; revolution?: { axis: string } }) =>\n (shape.extrusion?.axis === 'y' && shape.extrusion.section.kind === 'ellipse') || shape.revolution?.axis === 'y'\n\n/**\n * Can it be told apart from above — baseline 1.\n *\n * Viewpoint entropy (Vázquez et al.) over the top view, counted by colour region rather than by face so that a\n * finer segment count does not raise it: H = −Σ p·log p over each material's share of the footprint plus the\n * background, divided by log(`LIMITS.distinctMaterials` + 1), the most this measure can reach.\n *\n * The footprint is the base box: the space the asset declares when it declares one (ADR-0044's box is the model's\n * occupancy), otherwise the drawn bodies' own extent. Bodies are their axis-aligned world boxes\n * (`figureWorldBoxesOf`, the gate's reading); the higher one covers the lower.\n */\nfunction readable(\n asset: FigureAsset,\n plan: ReturnType<typeof compileFigureDocument>,\n drawn: FigureAssetGeometry[],\n writers: Map<string, string>\n): FigureMaturityAxis {\n const placed = drawn\n .filter(body => 'pose' in body)\n // One placed, drawn body gives exactly one box; boxes are taken one by one to keep each with its material.\n .map(body => ({ body, box: figureWorldBoxesOf([body], writers)[0]! }))\n if (placed.length === 0) return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }\n\n let frame: { x0: number; x1: number; z0: number; z1: number }\n if (asset.occupancy) {\n const values = plan.evaluate({ ...asset.designInputs, ...asset.stateDefaults }).values\n const at = (ref: string) => values[ref] as number\n const b = asset.occupancy.bounds\n frame = { x0: at(b.x.min), x1: at(b.x.max), z0: at(b.z.min), z1: at(b.z.max) }\n } else {\n frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity }\n for (const { box } of placed) {\n frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2)\n frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2)\n frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2)\n frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2)\n }\n }\n\n const regions = topViewRegions(placed, frame)\n if (regions.total === 0) return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }\n\n const shares = [...regions.byMaterial.values(), regions.background].map(area => area / regions.footprint).filter(share => share > 0)\n const entropy = -shares.reduce((sum, p) => sum + p * Math.log(p), 0)\n const ceiling = Math.log(LIMITS.distinctMaterials + 1)\n return {\n key: 'readable',\n // Clamped both ways: one region alone gives −0, which is not a reading anyone should see.\n ratio: Math.max(0, Math.min(1, entropy / ceiling)),\n reading: String(regions.byMaterial.size),\n because: 'figure.text.maturity-readable-because'\n }\n}\n\n/** Which material shows from above, on a 64 × 64 grid over the footprint, over world boxes. */\nfunction topViewRegions(\n placed: { body: FigureAssetGeometry; box: ReturnType<typeof figureWorldBoxesOf>[number] }[],\n frame: { x0: number; x1: number; z0: number; z1: number }\n) {\n const GRID = 64\n const cells = new Array<string | null>(GRID * GRID).fill(null)\n const height = new Float64Array(GRID * GRID)\n const sx = GRID / Math.max(frame.x1 - frame.x0, 1e-6)\n const sz = GRID / Math.max(frame.z1 - frame.z0, 1e-6)\n\n for (const { body, box } of placed) {\n const top = box.centre.y + box.extent.y / 2\n const key = materialKeyOf(body)\n const x0 = Math.max(0, Math.floor((box.centre.x - box.extent.x / 2 - frame.x0) * sx))\n const x1 = Math.min(GRID, Math.ceil((box.centre.x + box.extent.x / 2 - frame.x0) * sx))\n const y0 = Math.max(0, Math.floor((box.centre.z - box.extent.z / 2 - frame.z0) * sz))\n const y1 = Math.min(GRID, Math.ceil((box.centre.z + box.extent.z / 2 - frame.z0) * sz))\n const round = 'shape' in body && roundFootprint(body.shape as never)\n\n for (let y = y0; y < y1; y++) {\n for (let x = x0; x < x1; x++) {\n // A round body does not fill its box; the corners are cut.\n if (round) {\n const cx = (x + 0.5 - (x0 + x1) / 2) / ((x1 - x0) / 2 || 1)\n const cy = (y + 0.5 - (y0 + y1) / 2) / ((y1 - y0) / 2 || 1)\n if (cx * cx + cy * cy > 1) continue\n }\n const i = y * GRID + x\n if (cells[i] === null || top > height[i]!) {\n cells[i] = key\n height[i] = top\n }\n }\n }\n }\n\n const byMaterial = new Map<string, number>()\n let covered = 0\n for (const key of cells) {\n if (key === null) continue\n covered++\n byMaterial.set(key, (byMaterial.get(key) ?? 0) + 1)\n }\n return { byMaterial, total: covered, background: GRID * GRID - covered, footprint: GRID * GRID }\n}\n\n/** 1 · 2 · 5 per decade: even steps on a log axis. */\nexport function decadeSteps(from: number, to: number): number[] {\n const out: number[] = []\n for (let decade = 1; decade <= to; decade *= 10) {\n for (const m of [1, 2, 5]) {\n const v = decade * m\n if (v >= from && v <= to) out.push(v)\n }\n }\n if (out.length === 0 || out[out.length - 1] !== to) out.push(to)\n return [...new Set(out)].sort((a, b) => a - b)\n}\n\n// ─── The trend chart ─────────────────────────────────────────────────────────\n\nexport type FigureCurveSeries = 'triangles' | 'drawCalls' | 'geometries' | 'materials'\n\n/**\n * The four lines: two that grow with the instance count and two that stay flat — the flat ones are what the\n * instances share. Materials are dashed and drawn last, so a line under them still shows where the two meet.\n */\nexport const CURVE_SERIES: readonly { key: FigureCurveSeries; flat: boolean; dashed?: boolean }[] = [\n { key: 'triangles', flat: false },\n { key: 'drawCalls', flat: false },\n { key: 'geometries', flat: true },\n { key: 'materials', flat: true, dashed: true }\n]\n\n/** The chart's size. The chart is shown at this size in a sheet; shrunk into the side, its words are unreadable. */\nexport const CURVE_SIZE = { width: 680, height: 380, pad: { top: 52, right: 208, bottom: 44, left: 62 } } as const\n\nexport interface FigureCurveLayout {\n xTicks: { x: number; label: string }[]\n yTicks: { y: number; label: string }[]\n lines: { key: FigureCurveSeries; d: string; flat: boolean; dashed: boolean; last: number }[]\n /** The value the flat lines share when they lie on one another, so the screen can say why one line is missing. */\n overlap?: number\n}\n\nconst log10 = (v: number) => Math.log(Math.max(v, 1)) / Math.LN10\n\n/** 1, 10, 100 … written short. */\nexport function shortNumber(v: number): string {\n if (v >= 1_000_000) return `${v / 1_000_000}M`\n if (v >= 1_000) return `${v / 1_000}k`\n return String(v)\n}\n\n/** Where the chart puts each line and tick. Both axes are logarithmic. */\nexport function curveLayout(points: FigureCurvePoint[]): FigureCurveLayout | undefined {\n if (points.length === 0) return undefined\n const { width, height, pad } = CURVE_SIZE\n const minX = points[0]!.instances\n const maxX = points[points.length - 1]!.instances\n let maxY = 1\n for (const p of points) for (const s of CURVE_SERIES) maxY = Math.max(maxY, p[s.key])\n\n const plotW = width - pad.left - pad.right\n const plotH = height - pad.top - pad.bottom\n const spanX = Math.max(log10(maxX) - log10(minX), 1e-9)\n const spanY = Math.max(log10(maxY), 1e-9)\n const px = (v: number) => pad.left + ((log10(v) - log10(minX)) / spanX) * plotW\n const py = (v: number) => pad.top + plotH - (log10(v) / spanY) * plotH\n\n const yTicks: FigureCurveLayout['yTicks'] = []\n for (let e = 0; Math.pow(10, e) <= maxY * 1.001; e++) yTicks.push({ y: py(Math.pow(10, e)), label: shortNumber(Math.pow(10, e)) })\n const xTicks: FigureCurveLayout['xTicks'] = []\n for (let e = 0; Math.pow(10, e) <= maxX * 1.001; e++) {\n const v = Math.pow(10, e)\n if (v >= minX) xTicks.push({ x: px(v), label: shortNumber(v) })\n }\n\n const last = points[points.length - 1]!\n const lines = CURVE_SERIES.map(s => ({\n key: s.key,\n d: points.map((p, i) => `${i === 0 ? 'M' : 'L'}${px(p.instances).toFixed(1)},${py(p[s.key]).toFixed(1)}`).join(' '),\n flat: s.flat,\n dashed: !!s.dashed,\n last: last[s.key]\n }))\n const flat = new Set(CURVE_SERIES.filter(s => s.flat).map(s => last[s.key]))\n return { xTicks, yTicks, lines, overlap: flat.size === 1 ? [...flat][0] : undefined }\n}\n\n/** What the parts list shows under its title: the cost, how many fit, the grade and what decides it. */\nexport interface FigureBudgetLine {\n triangles: number\n groups: number\n instances: number\n grade: string\n tightest: { key: string; used: number; limit: number }\n}\n\n/**\n * What the verdict tab measures of the figure on screen once it settles: the report, the bodies there are to fit the\n * space to (drawn or hidden -- a hidden part is still a part to measure), and what it tells the page: the count for its\n * tab and the parts list's line, from the same report. A source the format refuses has no report.\n */\nexport function verdictMeasureOf(asset: FigureAsset | undefined): {\n report?: FigureReport\n bodies: number\n told: { instances?: number; budget?: FigureBudgetLine }\n} {\n let report: FigureReport | undefined\n let bodies = 0\n try {\n const cost = asset ? figureCostOf(asset) : undefined\n bodies = cost ? cost.groups + cost.hidden : 0\n report = asset ? figureReportOf(asset) : undefined\n } catch {\n /* A source the format refuses has no report; the server's finding says why. */\n report = undefined\n }\n return { report, bodies, told: { instances: report?.capacity.instances, budget: budgetOf(report) } }\n}\n\nexport function budgetOf(report: FigureReport | undefined): FigureBudgetLine | undefined {\n if (!report) return undefined\n const { tightest } = report.score\n return {\n triangles: report.cost.triangles,\n groups: report.cost.groups,\n instances: report.capacity.instances,\n grade: report.score.grade,\n tightest: { key: tightest.key, used: tightest.used, limit: Math.floor(tightest.limit) }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"maturity.js","sourceRoot":"","sources":["../../client/modeller/maturity.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n compileFigureAsset,\n compileFigureDocument,\n LIMITS,\n SEGMENT_PRESETS,\n FIGURE_BUDGET_KEYS,\n FIGURE_GRADE_SCALE,\n figureCostOf,\n figureGradeOf,\n figureMaterialKeyOf,\n figureScoreOf,\n figureWorldBoxesOf,\n figureWritersOf\n} from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureAssetGeometry, FigureBudgetKey, FigureBudgetUse, FigureCost, FigureGeometry, FigureScore } from '@hatiolab/figure-model'\n\nimport { capacityFrom } from './scene-budget.js'\nimport type { FigureCapacity } from './scene-budget.js'\n\n/*\n * The modeller's maturity report, read off the evaluated asset (user, 2026-09-24: the tab showed a count and nothing\n * else — 「왜 이렇게 너프되었는지」):\n *\n * grade A–D and its scale figure-model's `figureScoreOf`: one screen's target shared per instance (groups,\n * triangles) and the declared rule limits (independent, transparent)\n * radar · axes six axes; reactive is not measured, and detail has no field\n * drawing cost triangles, distinct shapes, distinct materials\n * trend what grows with the instance count and what the instances share\n *\n * Kept apart from the element so it can be tested without a browser.\n *\n * ## What the renderer shares, which is what the numbers count\n *\n * things-scene draws one mesh per drawn body (`figure-rig.ts`) — no merging by material — so a body is\n * a draw call. It shares geometry by provider, dimensions, segments and profile (`figure-geometry.ts`, `keyOf`)\n * and materials by token, preset, transparency and light (`materialFor`). The keys below are those keys; a\n * distinct-shape count that used another key would describe a renderer that does not exist.\n *\n * ## Budgets does not have\n *\n * Two budgets are not measured here, and are not shown as zero:\n * - off-palette surfaces: the material has no surface field, so there is nothing to count;\n * - part count by detail level: does not carry the detail level (chief architect, 2026-09-24, ADR-0087).\n */\n\n/*\n The grade, its budgets and its bounds are figure-model's (`figureScoreOf`), so the catalogue grades a figure as this tab\n does. The grade is the headroom against one screen's target (`LIMITS.screenInstances`), not a draw-call limit.\n*/\nexport { FIGURE_BUDGET_KEYS, FIGURE_GRADE_SCALE as GRADE_SCALE, figureGradeOf as gradeOf }\nexport type { FigureBudgetKey, FigureBudgetUse, FigureScore }\n\n/** A · B · C · D as the tab draws its scale: each grade with the bound it is computed with, and D past the last. */\nexport function gradeBoundsOf(): [string, string][] {\n return [...FIGURE_GRADE_SCALE.map(([grade, bound]): [string, string] => [grade, `≤ ${bound * 100}%`]), ['D', '> 100%']]\n}\n\nexport type FigureAxisKey = 'scale' | 'consistency' | 'reuse' | 'detail' | 'reactive' | 'readable'\n\n/** One maturity axis. */\nexport interface FigureMaturityAxis {\n key: FigureAxisKey\n /** 0–1. Absent when the axis cannot be measured. */\n ratio?: number\n reading?: string\n /** A reading that is words, not a number. */\n readingKey?: string\n because?: string\n /** Why the axis is not measured. When present there is no ratio — an unmeasured axis is not a zero. */\n blocked?: string\n}\n\n/** Instances on one screen, and what each series costs at that count. */\nexport interface FigureCurvePoint {\n instances: number\n triangles: number\n drawCalls: number\n /** Vertex buffers: one per distinct shape, shared by every instance of the same size. */\n geometries: number\n materials: number\n}\n\nexport interface FigureReport {\n cost: FigureCost\n capacity: FigureCapacity\n score: FigureScore\n /** Distinct shapes and materials among the drawn bodies — the 「그리는 비용」 rows. */\n distinctShapes: number\n distinctMaterials: number\n reuse: { distinct: number; parts: number; saved: number }\n axes: FigureMaturityAxis[]\n curve: FigureCurvePoint[]\n}\n\n/** The board target for the scale axis: 500 assets on one drawing (design document §4.15). */\nexport const BOARD_ASSETS = 500\n\n/**\n * The report for one asset, or nothing when no body is drawn — then there is nothing to measure, and the screen\n * says so rather than drawing zeros.\n *\n * Evaluated at the asset's own design and state defaults, as `figureCostOf` is.\n */\nexport function figureReportOf(asset: FigureAsset): FigureReport | undefined {\n const cost = figureCostOf(asset)\n const capacity = capacityFrom(cost.groups, cost.triangles)\n if (!capacity) return undefined\n\n const drawn = compileFigureAsset(asset)\n .evaluate()\n .geometry.filter(body => body.visible !== false)\n const plan = compileFigureDocument(asset.document)\n const writers = figureWritersOf(asset)\n\n const shapes = new Set(drawn.map(shapeKeyOf))\n const materials = new Set(drawn.map(materialKeyOf))\n\n const score = figureScoreOf(asset)!\n\n const reuse = { distinct: shapes.size, parts: drawn.length, saved: drawn.length - shapes.size }\n\n const axes: FigureMaturityAxis[] = [\n {\n key: 'scale',\n // The board target filled is 1; room beyond it is better spent on the other axes, so it does not count more.\n ratio: Math.min(1, capacity.instances / BOARD_ASSETS),\n reading: String(capacity.instances),\n because: 'figure.text.maturity-scale-because'\n },\n consistency(asset, materials.size),\n {\n key: 'reuse',\n // A low distinct-to-parts ratio is much reuse; turned over so that more is higher.\n ratio: reuse.parts === 0 ? 0 : 1 - reuse.distinct / reuse.parts,\n reading: String(reuse.saved),\n because: 'figure.text.maturity-reuse-because'\n },\n { key: 'detail', blocked: 'figure.text.maturity-blocked-no-detail-level' },\n { key: 'reactive', blocked: 'figure.text.maturity-blocked-no-binding' },\n readable(drawn, writers)\n ]\n\n return {\n cost,\n capacity,\n score,\n distinctShapes: shapes.size,\n distinctMaterials: materials.size,\n reuse,\n axes,\n curve: decadeSteps(1, 10000).map(instances => ({\n instances,\n triangles: cost.triangles * instances,\n drawCalls: cost.groups * instances,\n geometries: shapes.size,\n materials: materials.size\n }))\n }\n}\n\n/**\n * The key things-scene shares a body's geometry by (`figure-geometry.ts` `keyOf`): the operation, what is not in its\n * numbers (the axis, the kind of section, which ends of an inside are open), its numbers to a micrometre, and segments\n * (12 when unset).\n */\nexport function shapeKeyOf(body: FigureAssetGeometry | FigureGeometry): string {\n if (!('shape' in body)) {\n // A scalar body has no mesh of its own; it is still one drawn body, told apart by what it declares.\n const dims = Object.keys(body.dimensions).sort().map(k => `${k}=${body.dimensions[k]}`).join(',')\n return `${body.provider}|${dims}`\n }\n const shape = body.shape\n const round = (v: number) => Math.round(v * 1e6) / 1e6\n const dims = Object.keys(shape.dimensions)\n .sort()\n .map(k => `${k}=${round(shape.dimensions[k]!)}`)\n .join(',')\n const e = shape.extrusion,\n c = shape.cavity,\n r = shape.revolution\n const l = shape.loft\n const meaning = e\n ? `${e.axis}:${e.section.kind}:${c ? `${c.start === 'open' ? 'o' : 'c'}${c.end === 'open' ? 'o' : 'c'}` : '-'}`\n : r\n ? `${r.axis}:${r.profile.kind}`\n : l\n ? `${l.axis}:point`\n : ''\n const segments = (body as { segments?: number }).segments ?? 12\n return `${shape.provider}|${meaning}|${dims}|s${segments}`\n}\n\n/**\n * The key things-scene shares a material by (`figure-rig.ts` `materialFor`), within one asset: token, preset,\n * transparency, and the light when it is on. The colour follows from the token, so it is not a second key.\n */\nexport function materialKeyOf(body: FigureAssetGeometry): string {\n return figureMaterialKeyOf(body)\n}\n\n/**\n * Does it look like one set — baselines 3 and 4, three checks.\n *\n * Two of them hold by construction: the material has no surface field, so there is no texture, and the\n * format refuses a segment count outside the presets (`compileFigureAsset`, `QUALITY`). They are still counted, so\n * the ratio stays one fault in three.\n */\nfunction consistency(asset: FigureAsset, distinctMaterials: number): FigureMaturityAxis {\n const textures = 0\n const offPreset = asset.appearance.filter(look => look.segments !== undefined && !SEGMENT_PRESETS.includes(look.segments)).length\n const faults = (textures > 0 ? 1 : 0) + (offPreset > 0 ? 1 : 0) + (distinctMaterials > LIMITS.distinctMaterials ? 1 : 0)\n return {\n key: 'consistency',\n ratio: (3 - faults) / 3,\n readingKey: faults === 0 ? 'figure.text.maturity-consistency-clean' : 'figure.text.maturity-consistency-faults',\n because: 'figure.text.maturity-consistency-because'\n }\n}\n\n/** Seen from above, these do not fill their footprint box, by provider name. */\n/** Round seen from above: a circle or an ellipse pushed along Y, anything turned about Y. */\nconst roundFootprint = (shape: { extrusion?: { axis: string; section: { kind: string } }; revolution?: { axis: string } }) =>\n (shape.extrusion?.axis === 'y' && shape.extrusion.section.kind === 'ellipse') || shape.revolution?.axis === 'y'\n\n/**\n * Can it be told apart from above — baseline 1.\n *\n * Viewpoint entropy (Vázquez et al.) over the top view, counted by colour region rather than by face so that a\n * finer segment count does not raise it: H = −Σ p·log p over each material's share of the footprint plus the\n * background, divided by log(`LIMITS.distinctMaterials` + 1), the most this measure can reach.\n *\n * The footprint is the drawn bodies' own extent, the box a board gives it (ADR-0145). Bodies are their axis-aligned world boxes\n * (`figureWorldBoxesOf`, the gate's reading); the higher one covers the lower.\n */\nfunction readable(\n drawn: FigureAssetGeometry[],\n writers: Map<string, string>\n): FigureMaturityAxis {\n const placed = drawn\n .filter(body => 'pose' in body)\n // One placed, drawn body gives exactly one box; boxes are taken one by one to keep each with its material.\n .map(body => ({ body, box: figureWorldBoxesOf([body], writers)[0]! }))\n if (placed.length === 0) return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }\n\n const frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity }\n for (const { box } of placed) {\n frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2)\n frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2)\n frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2)\n frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2)\n }\n\n const regions = topViewRegions(placed, frame)\n if (regions.total === 0) return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }\n\n const shares = [...regions.byMaterial.values(), regions.background].map(area => area / regions.footprint).filter(share => share > 0)\n const entropy = -shares.reduce((sum, p) => sum + p * Math.log(p), 0)\n const ceiling = Math.log(LIMITS.distinctMaterials + 1)\n return {\n key: 'readable',\n // Clamped both ways: one region alone gives −0, which is not a reading anyone should see.\n ratio: Math.max(0, Math.min(1, entropy / ceiling)),\n reading: String(regions.byMaterial.size),\n because: 'figure.text.maturity-readable-because'\n }\n}\n\n/** Which material shows from above, on a 64 × 64 grid over the footprint, over world boxes. */\nfunction topViewRegions(\n placed: { body: FigureAssetGeometry; box: ReturnType<typeof figureWorldBoxesOf>[number] }[],\n frame: { x0: number; x1: number; z0: number; z1: number }\n) {\n const GRID = 64\n const cells = new Array<string | null>(GRID * GRID).fill(null)\n const height = new Float64Array(GRID * GRID)\n const sx = GRID / Math.max(frame.x1 - frame.x0, 1e-6)\n const sz = GRID / Math.max(frame.z1 - frame.z0, 1e-6)\n\n for (const { body, box } of placed) {\n const top = box.centre.y + box.extent.y / 2\n const key = materialKeyOf(body)\n const x0 = Math.max(0, Math.floor((box.centre.x - box.extent.x / 2 - frame.x0) * sx))\n const x1 = Math.min(GRID, Math.ceil((box.centre.x + box.extent.x / 2 - frame.x0) * sx))\n const y0 = Math.max(0, Math.floor((box.centre.z - box.extent.z / 2 - frame.z0) * sz))\n const y1 = Math.min(GRID, Math.ceil((box.centre.z + box.extent.z / 2 - frame.z0) * sz))\n const round = 'shape' in body && roundFootprint(body.shape as never)\n\n for (let y = y0; y < y1; y++) {\n for (let x = x0; x < x1; x++) {\n // A round body does not fill its box; the corners are cut.\n if (round) {\n const cx = (x + 0.5 - (x0 + x1) / 2) / ((x1 - x0) / 2 || 1)\n const cy = (y + 0.5 - (y0 + y1) / 2) / ((y1 - y0) / 2 || 1)\n if (cx * cx + cy * cy > 1) continue\n }\n const i = y * GRID + x\n if (cells[i] === null || top > height[i]!) {\n cells[i] = key\n height[i] = top\n }\n }\n }\n }\n\n const byMaterial = new Map<string, number>()\n let covered = 0\n for (const key of cells) {\n if (key === null) continue\n covered++\n byMaterial.set(key, (byMaterial.get(key) ?? 0) + 1)\n }\n return { byMaterial, total: covered, background: GRID * GRID - covered, footprint: GRID * GRID }\n}\n\n/** 1 · 2 · 5 per decade: even steps on a log axis. */\nexport function decadeSteps(from: number, to: number): number[] {\n const out: number[] = []\n for (let decade = 1; decade <= to; decade *= 10) {\n for (const m of [1, 2, 5]) {\n const v = decade * m\n if (v >= from && v <= to) out.push(v)\n }\n }\n if (out.length === 0 || out[out.length - 1] !== to) out.push(to)\n return [...new Set(out)].sort((a, b) => a - b)\n}\n\n// ─── The trend chart ─────────────────────────────────────────────────────────\n\nexport type FigureCurveSeries = 'triangles' | 'drawCalls' | 'geometries' | 'materials'\n\n/**\n * The four lines: two that grow with the instance count and two that stay flat — the flat ones are what the\n * instances share. Materials are dashed and drawn last, so a line under them still shows where the two meet.\n */\nexport const CURVE_SERIES: readonly { key: FigureCurveSeries; flat: boolean; dashed?: boolean }[] = [\n { key: 'triangles', flat: false },\n { key: 'drawCalls', flat: false },\n { key: 'geometries', flat: true },\n { key: 'materials', flat: true, dashed: true }\n]\n\n/** The chart's size. The chart is shown at this size in a sheet; shrunk into the side, its words are unreadable. */\nexport const CURVE_SIZE = { width: 680, height: 380, pad: { top: 52, right: 208, bottom: 44, left: 62 } } as const\n\nexport interface FigureCurveLayout {\n xTicks: { x: number; label: string }[]\n yTicks: { y: number; label: string }[]\n lines: { key: FigureCurveSeries; d: string; flat: boolean; dashed: boolean; last: number }[]\n /** The value the flat lines share when they lie on one another, so the screen can say why one line is missing. */\n overlap?: number\n}\n\nconst log10 = (v: number) => Math.log(Math.max(v, 1)) / Math.LN10\n\n/** 1, 10, 100 … written short. */\nexport function shortNumber(v: number): string {\n if (v >= 1_000_000) return `${v / 1_000_000}M`\n if (v >= 1_000) return `${v / 1_000}k`\n return String(v)\n}\n\n/** Where the chart puts each line and tick. Both axes are logarithmic. */\nexport function curveLayout(points: FigureCurvePoint[]): FigureCurveLayout | undefined {\n if (points.length === 0) return undefined\n const { width, height, pad } = CURVE_SIZE\n const minX = points[0]!.instances\n const maxX = points[points.length - 1]!.instances\n let maxY = 1\n for (const p of points) for (const s of CURVE_SERIES) maxY = Math.max(maxY, p[s.key])\n\n const plotW = width - pad.left - pad.right\n const plotH = height - pad.top - pad.bottom\n const spanX = Math.max(log10(maxX) - log10(minX), 1e-9)\n const spanY = Math.max(log10(maxY), 1e-9)\n const px = (v: number) => pad.left + ((log10(v) - log10(minX)) / spanX) * plotW\n const py = (v: number) => pad.top + plotH - (log10(v) / spanY) * plotH\n\n const yTicks: FigureCurveLayout['yTicks'] = []\n for (let e = 0; Math.pow(10, e) <= maxY * 1.001; e++) yTicks.push({ y: py(Math.pow(10, e)), label: shortNumber(Math.pow(10, e)) })\n const xTicks: FigureCurveLayout['xTicks'] = []\n for (let e = 0; Math.pow(10, e) <= maxX * 1.001; e++) {\n const v = Math.pow(10, e)\n if (v >= minX) xTicks.push({ x: px(v), label: shortNumber(v) })\n }\n\n const last = points[points.length - 1]!\n const lines = CURVE_SERIES.map(s => ({\n key: s.key,\n d: points.map((p, i) => `${i === 0 ? 'M' : 'L'}${px(p.instances).toFixed(1)},${py(p[s.key]).toFixed(1)}`).join(' '),\n flat: s.flat,\n dashed: !!s.dashed,\n last: last[s.key]\n }))\n const flat = new Set(CURVE_SERIES.filter(s => s.flat).map(s => last[s.key]))\n return { xTicks, yTicks, lines, overlap: flat.size === 1 ? [...flat][0] : undefined }\n}\n\n/** What the parts list shows under its title: the cost, how many fit, the grade and what decides it. */\nexport interface FigureBudgetLine {\n triangles: number\n groups: number\n instances: number\n grade: string\n tightest: { key: string; used: number; limit: number }\n}\n\n/**\n * What the verdict tab measures of the figure on screen once it settles: the report, and what it tells the page: the\n * count for its tab and the parts list's line, from the same report. A source the format refuses has no report.\n */\nexport function verdictMeasureOf(asset: FigureAsset | undefined): {\n report?: FigureReport\n told: { instances?: number; budget?: FigureBudgetLine }\n} {\n let report: FigureReport | undefined\n try {\n report = asset ? figureReportOf(asset) : undefined\n } catch {\n /* A source the format refuses has no report; the server's finding says why. */\n report = undefined\n }\n return { report, told: { instances: report?.capacity.instances, budget: budgetOf(report) } }\n}\n\nexport function budgetOf(report: FigureReport | undefined): FigureBudgetLine | undefined {\n if (!report) return undefined\n const { tightest } = report.score\n return {\n triangles: report.cost.triangles,\n groups: report.cost.groups,\n instances: report.capacity.instances,\n grade: report.score.grade,\n tightest: { key: tightest.key, used: tightest.used, limit: Math.floor(tightest.limit) }\n }\n}\n"]}
|
|
@@ -21,12 +21,10 @@ export interface FigureRefusalRecord {
|
|
|
21
21
|
export declare function refusalRecordOf(error: unknown): FigureRefusalRecord;
|
|
22
22
|
/** The clips an edit would stop, as figure-model named them (CLIPS_DROPPED) -- the person is asked before any go; nothing for any other refusal. */
|
|
23
23
|
export declare function clipLossOf(error: unknown): FigureClipLoss[] | null;
|
|
24
|
-
/** A part refused because the declared room reads it -- the one refusal deleting it with the room refitted answers. */
|
|
25
|
-
export declare function inDeclaredRoom(error: unknown): boolean;
|
|
26
24
|
/**
|
|
27
25
|
* Why a part cannot be deleted, in the author's words. Since the chief architect's ruling of 2026-09-27 a part that
|
|
28
26
|
* holds others, leads a motion or is followed goes, and what depended on it is let go of (`removalNoticeOf`); what is
|
|
29
|
-
* left is
|
|
27
|
+
* left is a row -- one laid along the part, or hanging in its row.
|
|
30
28
|
*/
|
|
31
29
|
export declare function partRefusalOf(error: unknown, part: string, say: Say): string | null;
|
|
32
30
|
/** A part the gizmo cannot move because a joint or a fastening places it, named as the author knows it (`part`). */
|
|
@@ -64,21 +64,15 @@ export function refusalRecordOf(error) {
|
|
|
64
64
|
export function clipLossOf(error) {
|
|
65
65
|
return codeOf(error) === 'CLIPS_DROPPED' ? (detailsOf(error, 'clips-dropped')?.clips ?? []) : null;
|
|
66
66
|
}
|
|
67
|
-
/** A part refused because the declared room reads it -- the one refusal deleting it with the room refitted answers. */
|
|
68
|
-
export function inDeclaredRoom(error) {
|
|
69
|
-
return codeOf(error) === 'PART_REFERENCED' && !!detailsOf(error, 'in-declared-space');
|
|
70
|
-
}
|
|
71
67
|
/**
|
|
72
68
|
* Why a part cannot be deleted, in the author's words. Since the chief architect's ruling of 2026-09-27 a part that
|
|
73
69
|
* holds others, leads a motion or is followed goes, and what depended on it is let go of (`removalNoticeOf`); what is
|
|
74
|
-
* left is
|
|
70
|
+
* left is a row -- one laid along the part, or hanging in its row.
|
|
75
71
|
*/
|
|
76
72
|
export function partRefusalOf(error, part, say) {
|
|
77
73
|
const code = codeOf(error);
|
|
78
74
|
if (code !== 'PART_REFERENCED' && code !== 'PART_ROW')
|
|
79
75
|
return null;
|
|
80
|
-
if (detailsOf(error, 'in-declared-space'))
|
|
81
|
-
return say('figure.text.part-in-declared-space', { part });
|
|
82
76
|
const along = detailsOf(error, 'row-along');
|
|
83
77
|
if (along)
|
|
84
78
|
return say('figure.text.part-row-along', { part, rows: along.rows.join(', ') });
|