Skip to content

Commit 8087a20

Browse files
authored
fix(renderer): restore small solid primitives (#44)
Co-authored-by: agustin-littlehat <minotopo@gmail.com>
1 parent a83508e commit 8087a20

16 files changed

Lines changed: 60 additions & 59 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ Voxel-shaped meshes are the exception to "all polygons stay mounted": meshes wit
3131

3232
| Tag | Strategy | When chosen | Paint mechanism | Atlas memory |
3333
|---|---|---|---|---|
34-
| `<b>` | **Quads** | Axis-aligned rectangle, or untextured convex quad when the homography passes stability guards on non-Safari engines | `background: currentColor` on a fixed 256px rectangle; affine and projective quads normalize their `matrix3d` to that primitive, with tiny solid bleed on projective quads to overlap antialias seams. Safari-family browsers skip the projective quad path and fall through because transformed projective rectangles composite incorrectly there. | None |
35-
| `<i>` | **Border-shape clipped solid** | Untextured non-rect not caught by the exact corner-shape solid path, on browsers with CSS `border-shape` (Chromium + `pointer:fine` + `hover:hover`) | `border-color: currentColor` on a fixed 256px border-shape primitive, clipped by `border-shape: polygon(...)`; polygon bbox scale and tiny solid bleed are folded into `matrix3d` | None |
34+
| `<b>` | **Quads** | Axis-aligned rectangle, or untextured convex quad when the homography passes stability guards on non-Safari engines | `background: currentColor` on a fixed 64px rectangle; affine and projective quads normalize their `matrix3d` to that primitive, with tiny solid bleed on projective quads to overlap antialias seams. Safari-family browsers skip the projective quad path and fall through because transformed projective rectangles composite incorrectly there. | None |
35+
| `<i>` | **Border-shape clipped solid** | Untextured non-rect not caught by the exact corner-shape solid path, on browsers with CSS `border-shape` (Chromium + `pointer:fine` + `hover:hover`) | `border-color: currentColor` on a fixed 16px border-shape primitive, clipped by `border-shape: polygon(...)`; polygon bbox scale and tiny solid bleed are folded into `matrix3d` | None |
3636
| `<s>` | **Atlas slice** | Textured polygons, or untextured non-rect on browsers without `border-shape` | `background-image` slice of packed bitmap on an auto-budgeted fixed primitive (128px for desktop-class `textureQuality="auto"`, 64px for mobile-class `auto` and explicit numeric quality); atlas position/size and `matrix3d` scale are normalized to the slice, shared textured edges get low-alpha atlas pixels repaired during atlas generation, and solid fallbacks get same-color edge bleed to avoid dark alpha fringes | Bounding-rect area |
37-
| `<u>` | **Stable solid triangle / corner-shape solid** | Triangles on non-WebKit engines; or untextured non-triangle polygons whose normalized outline is exactly a rectangle with one or more beveled corners on browsers with CSS `corner-shape` | Triangles use a 256px box with two beveled top corners and `background: currentColor` when CSS `corner-shape` support is present, progressively falling back to the CSS border-color triangle trick. Firefox uses the same 256px border-triangle primitive to avoid large-perspective compositor banding. Exact corner-shape solids use a bare fixed 256px box with inline per-corner radii + `corner-*-shape: bevel` and `background: currentColor`. Tiny solid bleed is folded into `matrix3d`. WebKit/Safari falls through to `<s>` for border triangles because transformed CSS border triangles composite incorrectly there. | None |
37+
| `<u>` | **Stable solid triangle / corner-shape solid** | Triangles on non-WebKit engines; or untextured non-triangle polygons whose normalized outline is exactly a rectangle with one or more beveled corners on browsers with CSS `corner-shape` | Triangles use a 32px box with two beveled top corners and `background: currentColor` when CSS `corner-shape` support is present, progressively falling back to the CSS border-color triangle trick. Firefox uses a 96px border-triangle primitive to avoid large-perspective compositor banding. Exact corner-shape solids use a bare fixed 32px box with inline per-corner radii + `corner-*-shape: bevel` and `background: currentColor`. Tiny solid bleed is folded into `matrix3d`. WebKit/Safari falls through to `<s>` for border triangles because transformed CSS border triangles composite incorrectly there. | None |
3838
| `<q>` | **Cast shadow leaf** | Per casting polygon when `castShadow: true`, in either lighting mode. Applies regardless of caster strategy — `<b>`/`<i>`/`<s>`/`<u>` all produce a `<q>` shadow because only the polygon's outline matters, not its surface. | Same `border-color: currentColor` + `border-shape: polygon(...)` as `<i>`. Dynamic mode chains `var(--shadow-proj)` (driven by `--clx/y/z` + `--shadow-ground-cssz`) so the projection follows the live light vars. Baked mode CPU-bakes the projection into the leaf's inline `matrix3d(...)` and drops back-facing polys from the DOM entirely instead of opacity-gating them. | None |
3939

4040
Strategies are ordered cheapest → most expensive. The mesher's job is to maximise `<b>` / `<u>` / `<i>` and minimise `<s>` (see "Meshing implications" below).

packages/core/src/atlas/constants.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ export const DEFAULT_MATRIX_DECIMALS = 3;
4444
export const DEFAULT_BORDER_SHAPE_DECIMALS = 2;
4545
export const DEFAULT_ATLAS_CSS_DECIMALS = 4;
4646
export const DECIMAL_SCALES = [1, 10, 100, 1000, 10000, 100000, 1000000];
47-
export const SOLID_QUAD_CANONICAL_SIZE = 256;
48-
export const SOLID_TRIANGLE_CANONICAL_SIZE = 256;
49-
export const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 256;
47+
export const SOLID_QUAD_CANONICAL_SIZE = 64;
48+
export const SOLID_TRIANGLE_CANONICAL_SIZE = 32;
49+
export const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96;
5050
export const ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
5151
export const ATLAS_CANONICAL_SIZE_AUTO_DESKTOP = 128;
5252
export const BORDER_SHAPE_CENTER_PERCENT = 50;
5353
export const BORDER_SHAPE_POINT_EPS = 1e-7;
54-
export const BORDER_SHAPE_CANONICAL_SIZE = 256;
54+
export const BORDER_SHAPE_CANONICAL_SIZE = 16;
5555
export const BORDER_SHAPE_BLEED = 0.9;
5656
export const CORNER_SHAPE_POINT_EPS = 0.75;
5757
export const CORNER_SHAPE_DUPLICATE_EPS = 0.2;

packages/polycss/src/api/createPolyScene.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,11 @@ describe("createPolyScene", () => {
426426
expect(styleEl?.textContent).toContain("transform-origin: 0 0");
427427
expect(styleEl?.textContent).toContain("backface-visibility: hidden");
428428
expect(styleEl?.textContent).toContain("background-repeat: no-repeat");
429-
expect(styleEl?.textContent).toContain("width: 256px;");
430-
expect(styleEl?.textContent).toContain("height: 256px;");
429+
expect(styleEl?.textContent).toContain("width: 64px;");
430+
expect(styleEl?.textContent).toContain("height: 64px;");
431431
expect(styleEl?.textContent).toContain("width: var(--polycss-atlas-size, 64px);");
432432
expect(styleEl?.textContent).toContain("height: var(--polycss-atlas-size, 64px);");
433-
expect(styleEl?.textContent).toContain("border-width: 0 128px 256px 128px;");
433+
expect(styleEl?.textContent).toContain("border-width: 0 16px 32px 16px;");
434434
expect(styleEl?.textContent).toContain("width: 0;");
435435
expect(styleEl?.textContent).toContain("height: 0;");
436436
});

packages/polycss/src/render/atlas/solidTrianglePrimitive.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe("solid triangle primitive — corner-bevel vs border", () => {
9898
expect(result).not.toBeNull();
9999
const element = result!.rendered[0].element;
100100
expect(element.className).toBe("");
101-
expect(element.style.borderWidth).toBe("0px 128px 256px");
101+
expect(element.style.borderWidth).toBe("0px 48px 96px");
102102
result!.dispose();
103103
});
104104

packages/polycss/src/render/atlas/stableTriangle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { applyPolygonDataAttrs, hasPolygonDataAttrs } from "./emit";
3636
import { resolveSolidTrianglePrimitive } from "./strategy";
3737

3838
const DEFAULT_SOLID_SEAM_BLEED = 1.5;
39-
const SOLID_TRIANGLE_BORDER_WIDTH = "0 128px 256px 128px";
39+
const SOLID_TRIANGLE_BORDER_WIDTH = "0 48px 96px 48px";
4040

4141
type RenderTextureAtlasOptionsWithSeams = RenderTextureAtlasOptions & {
4242
seamBleed?: number;

packages/polycss/src/render/polyDOM.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515

1616
const ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
1717
const ATLAS_CANONICAL_SIZE_AUTO_DESKTOP = 128;
18-
const SOLID_QUAD_CANONICAL_SIZE = 256;
18+
const SOLID_QUAD_CANONICAL_SIZE = 64;
19+
const BORDER_SHAPE_CANONICAL_SIZE = 16;
1920

2021
const CORNER_SHAPE_CORPUS = [
2122
["Bear.glb"],
@@ -792,8 +793,8 @@ describe("renderPolygonsWithTextureAtlas", () => {
792793
const yScale = Math.hypot(matrix[4], matrix[5], matrix[6]);
793794

794795
expect(element.tagName.toLowerCase()).toBe("i");
795-
expect(xScale).toBeGreaterThan(2 / 256);
796-
expect(yScale).toBeGreaterThan(2 / 256);
796+
expect(xScale).toBeGreaterThan(2 / BORDER_SHAPE_CANONICAL_SIZE);
797+
expect(yScale).toBeGreaterThan(2 / BORDER_SHAPE_CANONICAL_SIZE);
797798
expect(element.style.getPropertyValue("border-shape")).toContain("polygon(");
798799
result.dispose();
799800
});
@@ -1038,10 +1039,10 @@ describe("renderPolygonsWithTextureAtlas", () => {
10381039
expect(element.style.height).toBe("");
10391040
expect(element.style.getPropertyValue("--polycss-local-w")).toBe("");
10401041
expect(element.style.getPropertyValue("--polycss-local-h")).toBe("");
1041-
expect(matrix[0]).toBeGreaterThan(10 / 256);
1042+
expect(matrix[0]).toBeGreaterThan(10 / BORDER_SHAPE_CANONICAL_SIZE);
10421043
expect(matrix[1]).toBeCloseTo(0, 6);
10431044
expect(matrix[4]).toBeCloseTo(0, 6);
1044-
expect(matrix[5]).toBeGreaterThan(1 / 256);
1045+
expect(matrix[5]).toBeGreaterThan(1 / BORDER_SHAPE_CANONICAL_SIZE);
10451046
result.dispose();
10461047
});
10471048

packages/polycss/src/snapshot/exportPolySceneSnapshot.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,8 @@ ${selectorList(features.solidLeafTags)} {
364364
parts.push(`
365365
.polycss-scene b {
366366
background: currentColor;
367-
width: 256px;
368-
height: 256px;
367+
width: 64px;
368+
height: 64px;
369369
}
370370
`);
371371
}
@@ -402,8 +402,8 @@ ${selectorList(features.solidLeafTags)} {
402402
if (features.leafTags.includes("i")) {
403403
parts.push(`
404404
.polycss-scene i {
405-
width: 256px;
406-
height: 256px;
405+
width: 16px;
406+
height: 16px;
407407
border-color: currentColor;
408408
}
409409
`);
@@ -427,7 +427,7 @@ ${selectorList(features.solidLeafTags)} {
427427
box-sizing: content-box;
428428
border: 0 solid transparent;
429429
border-color: transparent transparent currentColor transparent;
430-
border-width: 0 128px 256px 128px;
430+
border-width: 0 16px 32px 16px;
431431
}
432432
`);
433433
}

packages/polycss/src/styles/styles.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ const CORE_BASE_STYLES = `
105105
106106
.polycss-scene b {
107107
background: currentColor;
108-
width: 256px;
109-
height: 256px;
108+
width: 64px;
109+
height: 64px;
110110
}
111111
112112
.polycss-mesh.polycss-voxel-mesh > .polycss-voxel-face {
@@ -135,8 +135,8 @@ const CORE_BASE_STYLES = `
135135
}
136136
137137
.polycss-scene i {
138-
width: 256px;
139-
height: 256px;
138+
width: 16px;
139+
height: 16px;
140140
border-color: currentColor;
141141
}
142142
@@ -152,7 +152,7 @@ const CORE_BASE_STYLES = `
152152
box-sizing: content-box;
153153
border: 0 solid transparent;
154154
border-color: transparent transparent currentColor transparent;
155-
border-width: 0 128px 256px 128px;
155+
border-width: 0 16px 32px 16px;
156156
}
157157
158158
/* <q> — dedicated shadow leaf. Same border-shape rendering trick as <i>

packages/react/src/scene/atlas/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ describe("updateStableTriangleDom", () => {
230230
};
231231

232232
expect(updateStableTriangleDom(root, [tri])).toBe(true);
233-
expect(leaf.style.borderWidth).toBe("0px 128px 256px");
233+
expect(leaf.style.borderWidth).toBe("0px 48px 96px");
234234
});
235235
});
236236

packages/react/src/scene/atlas/solidTriangleStyle.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ export const DEFAULT_AMBIENT_INTENSITY = 0.4;
2828
export const BASIS_EPS = 1e-9;
2929
// Matches the canonical SOLID_TRIANGLE_BLEED constant.
3030
export const SOLID_TRIANGLE_BLEED = 0.75;
31-
const SOLID_TRIANGLE_CANONICAL_SIZE = 256;
32-
const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 256;
33-
const SOLID_TRIANGLE_LARGE_BORDER_WIDTH = "0 128px 256px 128px";
31+
const SOLID_TRIANGLE_CANONICAL_SIZE = 32;
32+
const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96;
33+
const SOLID_TRIANGLE_LARGE_BORDER_WIDTH = "0 48px 96px 48px";
3434
const CORNER_TRIANGLE_STYLE = {
35-
width: "256px",
36-
height: "256px",
35+
width: "32px",
36+
height: "32px",
3737
backgroundColor: "currentColor",
3838
borderWidth: "0",
3939
borderTopLeftRadius: "50% 100%",
@@ -83,8 +83,8 @@ export function solidTrianglePaintStyle(): CSSProperties | undefined {
8383
export function applySolidTrianglePaintStyle(el: HTMLElement): void {
8484
const primitive = solidTrianglePrimitive();
8585
if (primitive === "corner-bevel") {
86-
el.style.width = "256px";
87-
el.style.height = "256px";
86+
el.style.width = "32px";
87+
el.style.height = "32px";
8888
el.style.backgroundColor = "currentColor";
8989
el.style.borderWidth = "0";
9090
el.style.borderTopLeftRadius = "50% 100%";

0 commit comments

Comments
 (0)