Keep scene view names readable on plates and in rows of their own
Symbol names in the scene view were plain grey text. On a background of a similar colour they could not be read, and names of symbols close together were drawn over each other. - Each name stands on a rounded, nearly black plate at 70 % opacity, 3 px padding left and right, 1 px top and bottom. - The text is light (viewportLabel) instead of muted grey. - The plate of a selected object gets an accent border. - New rule stackLabels in src/viewport/labelRows.ts. A name that overlaps another name or an icon moves one row down, then one up, then two down and so on, until a row is free. - Names are placed top to bottom, so the upper one keeps its row. A name only moves vertically and stays on the side of its icon. - Names outside the view still claim their row. Otherwise the visible names would jump while panning. - New hook __gm.sceneLabels() for the e2e check. The frame names in the texture editor already had a plate, so they are unchanged. Not covered by tests: the accent border on selection. Readability on a light background was only judged from a screenshot on a dark one. Tests: - 5 unit tests in tests/viewport/label-rows.test.ts: free names stay, the lower of two colliding names moves, the stacking order, names avoid icons, 30 crowded names never overlap. - e2e 5c-ii: 8 symbols on one spot give 8 plates, no overlap with each other or with icons, each name on the side of its icon. - Counter-checks: without the overlap test in the rule 4 of 5 unit tests fail. With the scene view ignoring the rows exactly 5c-ii fails. - ./check.sh: 161 files, 2369 tests, CHECK EXIT 0. - e2e 1-5: ALL PASSED, 140 checks. The e2e sum is now 1382 (computed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af6ac12ab5
commit
50bddf68d4
6 files changed
+223
-12
No files matched your search
@@ -100,10 +100,52 @@ export async function drawing(t) {
|
||||
JSON.stringify(wrong.length ? wrong : looks.map((s) => `${s.id} ${s.white}/${s.outline}`)),
|
||||
);
|
||||
await shot('15-symbols');
|
||||
|
||||
// 5c-ii. Eight symbols on one spot stand on a ring, their names beside them on dark plates. Names
|
||||
// that would cover each other go a row up or down instead – none overlaps another or an
|
||||
// icon – and every name stays on its icon's side of the ring.
|
||||
const crowd = Array.from({ length: 8 }, (_, k) => `s-crowd-${k}`);
|
||||
await mutate(`
|
||||
const scene = __gm.state.activeScene;
|
||||
${MAKE_OBJECT}
|
||||
const assets = ${JSON.stringify(Object.values(PLACED))};
|
||||
${JSON.stringify(crowd)}.forEach((id, k) => {
|
||||
scene.objects.push(make(id, assets[k % assets.length], '', ${LANES.symbols}, 'crowded name ' + k));
|
||||
});
|
||||
__gm.lookAt(0, ${LANES.symbols});`);
|
||||
const plates = await json(`(() => {
|
||||
const crowd = ${JSON.stringify(crowd)};
|
||||
const icons = crowd.map((id) => __gm.sceneIcon(id));
|
||||
const labels = __gm.sceneLabels().filter((l) => crowd.includes(l.id));
|
||||
return { icons: icons.map((i) => ({ x: i.x, y: i.y, size: i.size })), labels };
|
||||
})()`);
|
||||
const cross = (a, b) =>
|
||||
a.x < b.x + b.w - 1e-6 && b.x < a.x + a.w - 1e-6 && a.y < b.y + b.h - 1e-6 && b.y < a.y + a.h - 1e-6;
|
||||
const iconBoxes = plates.icons.map((i) => ({
|
||||
x: i.x - i.size / 2,
|
||||
y: i.y - i.size / 2,
|
||||
w: i.size,
|
||||
h: i.size,
|
||||
}));
|
||||
const clashes = [];
|
||||
plates.labels.forEach((a, i) => {
|
||||
plates.labels.slice(i + 1).forEach((b) => cross(a, b) && clashes.push(`${a.id}/${b.id}`));
|
||||
iconBoxes.forEach((box, k) => cross(a, box) && clashes.push(`${a.id}/icon ${k}`));
|
||||
});
|
||||
const sides = plates.labels.filter((l) => {
|
||||
const icon = plates.icons[crowd.indexOf(l.id)];
|
||||
return l.left ? l.x + l.w > icon.x : l.x < icon.x;
|
||||
});
|
||||
check(
|
||||
'names crowded on one spot stand on plates in rows of their own, on their icons’ side',
|
||||
plates.labels.length === 8 && clashes.length === 0 && sides.length === 0,
|
||||
`${plates.labels.length} plates, clashes ${JSON.stringify(clashes)}, wrong side ${JSON.stringify(sides.map((l) => l.id))}`,
|
||||
);
|
||||
await shot('15a-crowded-names');
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
const scene = __gm.state.activeScene;
|
||||
scene.objects = scene.objects.filter((o) => !${JSON.stringify(ids)}.includes(o.id));
|
||||
scene.objects = scene.objects.filter((o) => ![...${JSON.stringify(ids)}, ...${JSON.stringify(crowd)}].includes(o.id));
|
||||
p.assets = p.assets.filter((a) => !['s-box-a', 's-rb-a', 's-pt-a'].includes(a.id));
|
||||
__gm.lookAt(0, 0);`);
|
||||
|
||||
|
||||
Reference in new issue
Block a user