Skip to content

Commit f8ce0f7

Browse files
committed
Show shape icons in statistics table.
1 parent 1f13eb0 commit f8ce0f7

6 files changed

Lines changed: 132 additions & 3 deletions

File tree

src/ui/grid/ShapesIcon.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* SPDX-License-Identifier: GPL-3.0-or-later
3+
* SPDX-FileCopyrightText: Copyright (C) 2025 Gijs van Tulder
4+
*/
5+
6+
import { mergeBBoxItems } from "../../geom/math";
7+
import { Polygon } from "../../geom/Polygon";
8+
import { AngleUse, Shape } from "../../grid/Shape";
9+
import offsetPolygon from "../../lib/offset-polygon";
10+
import { roundPathCorners } from "../../lib/svg-rounded-corners";
11+
import { S, SVG } from "../shared/svg";
12+
import { polygonToPath } from "./TileDisplay";
13+
14+
export class AtlasIcon {
15+
svg: SVGElement;
16+
17+
constructor(shapes: readonly Shape[]) {
18+
const polygons: Polygon[] = [];
19+
20+
for (const shape of shapes) {
21+
if (polygons.length == 0) {
22+
polygons.push(
23+
shape.constructPreferredPolygon(
24+
0,
25+
0,
26+
1,
27+
AngleUse.SetupAtlas,
28+
),
29+
);
30+
} else {
31+
polygons.push(
32+
shape.constructPolygonEdge(
33+
polygons[0].outsideEdges[polygons.length],
34+
0,
35+
),
36+
);
37+
}
38+
}
39+
40+
const svg = SVG("svg", "shapes-icon");
41+
for (const poly of polygons) {
42+
svg.appendChild(this.drawPolygon(poly));
43+
}
44+
45+
const bbox = mergeBBoxItems(polygons)!;
46+
const viewBox = [
47+
bbox.minX,
48+
bbox.minY,
49+
bbox.maxX - bbox.minX,
50+
bbox.maxY - bbox.minY,
51+
];
52+
// center in a square viewbox
53+
if (viewBox[2] < viewBox[3]) {
54+
viewBox[0] -= (viewBox[3] - viewBox[2]) / 2;
55+
viewBox[2] = viewBox[3];
56+
} else if (viewBox[2] > viewBox[3]) {
57+
viewBox[1] -= (viewBox[2] - viewBox[3]) / 2;
58+
viewBox[3] = viewBox[2];
59+
}
60+
svg.setAttribute(
61+
"viewBox",
62+
viewBox.map((x) => (x * S).toFixed(4)).join(" "),
63+
);
64+
65+
this.svg = svg;
66+
}
67+
68+
drawPolygon(polygon: Polygon): SVGPathElement {
69+
// shrink outline
70+
const outline = offsetPolygon(polygon.vertices, -0.03);
71+
// outline = shrinkOutline(outline, 0.95);
72+
73+
const path = polygonToPath(outline);
74+
const roundPath = roundPathCorners(path, 0.08, true);
75+
76+
return SVG("path", null, undefined, {
77+
d: roundPath,
78+
});
79+
}
80+
}

src/ui/grid/TileDisplay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { addPointToPolygon } from "../../geom/polygon/addPointToPolygon";
1313
import { S, SVG } from "../shared/svg";
1414
import { UniqueIdSource } from "../shared/uniqueSvgId";
1515

16-
function polygonToPath(vertices: readonly Point[]): string {
16+
export function polygonToPath(vertices: readonly Point[]): string {
1717
const points = new Array<string>(vertices.length);
1818
for (let i = 0; i < points.length; i++) {
1919
points[i] =

src/ui/statistics/StatisticsDisplay.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { StatisticsMonitor } from "../../stats/StatisticsMonitor";
1111
import { SetupCatalog } from "../../saveGames";
1212
import { formatNumber } from "../../i18n";
1313
import { clip } from "../../geom/math";
14+
import { AtlasIcon } from "../grid/ShapesIcon";
15+
import { Shape } from "../../grid/Shape";
1416

1517
export type StatisticsSection = {
1618
heading?: {
@@ -21,6 +23,7 @@ export type StatisticsSection = {
2123
columns: (MessageDescriptor | null)[];
2224
rows: {
2325
title: MessageDescriptor;
26+
icon?: readonly Shape[];
2427
keys: string[];
2528
}[];
2629
footer?: {
@@ -155,7 +158,11 @@ export class StatisticsDisplay extends ScreenDisplay {
155158
const row = column[i];
156159
if (row) {
157160
const th = createElement("th", "label", tr);
158-
th.innerHTML = t(row.title);
161+
if (row.icon) {
162+
th.appendChild(new AtlasIcon(row.icon).svg);
163+
th.classList.add("with-icon");
164+
}
165+
th.appendChild(document.createTextNode(t(row.title)));
159166
for (const key of row.keys) {
160167
const td = createElement("td", null, tr);
161168
const n = stats.counters.get(key) || 0;
@@ -223,6 +230,7 @@ const StatisticsSections: StatisticsSection[] = [
223230
rows: [...SetupCatalog.atlas.values()].map((atlasDef) => {
224231
return {
225232
title: atlasDef.atlas.tilingName as MessageDescriptor,
233+
icon: atlasDef.atlas.shapes,
226234
keys: [
227235
`HighScore.{"atlas":"${atlasDef.atlas.id}","colors":"wong4","segments":0,"uniqueTileColors":false,"rules":"same","scorer":"shape"}`,
228236
`ShapeCompleted.{"atlas":"${atlasDef.atlas.id}","colors":"wong4","segments":0,"uniqueTileColors":false,"rules":"same","scorer":"shape"}`,
@@ -254,41 +262,47 @@ const StatisticsSections: StatisticsSection[] = [
254262
id: "ui.statistics.counter.TilePlaced.square",
255263
message: "Squares",
256264
}),
265+
icon: SetupCatalog.atlas.get("square")!.atlas.shapes,
257266
keys: [`TilePlaced.square`],
258267
},
259268
{
260269
title: msg({
261270
id: "ui.statistics.counter.TilePlaced.triangle",
262271
message: "Triangles",
263272
}),
273+
icon: SetupCatalog.atlas.get("triangle")!.atlas.shapes,
264274
keys: [`TilePlaced.triangle`],
265275
},
266276
{
267277
title: msg({
268278
id: "ui.statistics.counter.TilePlaced.rhombus",
269279
message: "Rhombi",
270280
}),
281+
icon: [SetupCatalog.atlas.get("penrose")!.atlas.shapes[0]],
271282
keys: [`TilePlaced.rhombus`],
272283
},
273284
{
274285
title: msg({
275286
id: "ui.statistics.counter.TilePlaced.pentagon",
276287
message: "Pentagons",
277288
}),
289+
icon: [SetupCatalog.atlas.get("pentagon")!.atlas.shapes[0]],
278290
keys: [`TilePlaced.pentagon`],
279291
},
280292
{
281293
title: msg({
282294
id: "ui.statistics.counter.TilePlaced.hexagon",
283295
message: "Hexagons",
284296
}),
297+
icon: [SetupCatalog.atlas.get("hexagon")!.atlas.shapes[0]],
285298
keys: [`TilePlaced.hexagon`],
286299
},
287300
{
288301
title: msg({
289302
id: "ui.statistics.counter.TilePlaced.kite",
290303
message: "Kites",
291304
}),
305+
icon: [SetupCatalog.atlas.get("deltotrihex")!.atlas.shapes[0]],
292306
keys: [`TilePlaced.kite`],
293307
},
294308
],

style/colors-dark.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ body.dark-mode {
9797
// warning symbol for invalid grid in setup menu
9898
--invalid-grid-warning-color: #e06300;
9999

100+
// STATISTICS :-moz-full-screen
101+
102+
--statistics-shape-color: #{color.mix(
103+
$button-base-color,
104+
$main-bg-color,
105+
35%
106+
)};
107+
100108
// GAME SCREEN
101109

102110
// background behind buttons on top of grid

style/colors.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ $blue: hsl(214deg, 64%, 50%);
105105
--invalid-grid-warning-color: #e06300;
106106
--undefined-tile-color: hsl(0deg, 0%, 50%);
107107

108+
// STATISTICS :-moz-full-screen
109+
110+
--statistics-shape-color: #{color.mix(
111+
$button-base-color,
112+
$main-bg-color,
113+
70%
114+
)};
115+
108116
// GAME SCREEN
109117

110118
// background behind buttons on top of grid

style/screens/screen-settings.scss

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@
171171
article {
172172
position: relative;
173173

174-
$stats-column-width: 6em;
174+
$stats-column-width: 5em;
175175
table,
176176
th,
177177
td {
@@ -203,6 +203,25 @@
203203
padding-left: 0.5em;
204204
border: none;
205205
}
206+
$icon-width: 1em;
207+
$icon-padding: 0.4em;
208+
table th.with-icon,
209+
table td.with-icon,
210+
table th.label.with-icon,
211+
table td.label.with-icon {
212+
padding-left: calc(0.5em + $icon-width + $icon-padding);
213+
text-indent: calc(-1 * $icon-width - $icon-padding);
214+
border: none;
215+
}
216+
.shapes-icon {
217+
display: inline-block;
218+
fill: var(--statistics-shape-color);
219+
max-width: $icon-width;
220+
max-height: $icon-width;
221+
padding-right: $icon-padding;
222+
aspect-ratio: 1;
223+
vertical-align: -0.15em;
224+
}
206225
thead th {
207226
text-align: right;
208227
padding-right: 0.5em;

0 commit comments

Comments
 (0)