abstract-3d 4.0.9 → 4.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/lib/renderers/ifc/ifc.test.d.ts +2 -0
  2. package/lib/renderers/ifc/ifc.test.d.ts.map +1 -0
  3. package/lib/renderers/ifc/ifc.test.js +380 -0
  4. package/lib/renderers/ifc/ifc.test.js.map +1 -0
  5. package/lib/renderers/react/react-camera.d.ts +71 -0
  6. package/lib/renderers/react/react-camera.d.ts.map +1 -0
  7. package/lib/renderers/react/react-camera.js +201 -0
  8. package/lib/renderers/react/react-camera.js.map +1 -0
  9. package/lib/renderers/react/react-dimension.d.ts +27 -0
  10. package/lib/renderers/react/react-dimension.d.ts.map +1 -0
  11. package/lib/renderers/react/react-dimension.js +44 -0
  12. package/lib/renderers/react/react-dimension.js.map +1 -0
  13. package/lib/renderers/react/react-group.d.ts +29 -0
  14. package/lib/renderers/react/react-group.d.ts.map +1 -0
  15. package/lib/renderers/react/react-group.js +82 -0
  16. package/lib/renderers/react/react-group.js.map +1 -0
  17. package/lib/renderers/react/react-image-material.d.ts +30 -0
  18. package/lib/renderers/react/react-image-material.d.ts.map +1 -0
  19. package/lib/renderers/react/react-image-material.js +81 -0
  20. package/lib/renderers/react/react-image-material.js.map +1 -0
  21. package/lib/renderers/react/react-material.d.ts +19 -0
  22. package/lib/renderers/react/react-material.d.ts.map +1 -0
  23. package/lib/renderers/react/react-material.js +39 -0
  24. package/lib/renderers/react/react-material.js.map +1 -0
  25. package/lib/renderers/react/react-mesh.d.ts +11 -0
  26. package/lib/renderers/react/react-mesh.d.ts.map +1 -0
  27. package/lib/renderers/react/react-mesh.js +302 -0
  28. package/lib/renderers/react/react-mesh.js.map +1 -0
  29. package/lib/renderers/react/react-relation.d.ts +10 -0
  30. package/lib/renderers/react/react-relation.d.ts.map +1 -0
  31. package/lib/renderers/react/react-relation.js +31 -0
  32. package/lib/renderers/react/react-relation.js.map +1 -0
  33. package/lib/renderers/react/react-scene.d.ts +25 -0
  34. package/lib/renderers/react/react-scene.d.ts.map +1 -0
  35. package/lib/renderers/react/react-scene.js +13 -0
  36. package/lib/renderers/react/react-scene.js.map +1 -0
  37. package/lib/renderers/react/react-types.d.ts +7 -0
  38. package/lib/renderers/react/react-types.d.ts.map +1 -0
  39. package/lib/renderers/react/react-types.js +3 -0
  40. package/lib/renderers/react/react-types.js.map +1 -0
  41. package/lib/renderers/three/appearance/appearance.test.d.ts +2 -0
  42. package/lib/renderers/three/appearance/appearance.test.d.ts.map +1 -0
  43. package/lib/renderers/three/appearance/appearance.test.js +97 -0
  44. package/lib/renderers/three/appearance/appearance.test.js.map +1 -0
  45. package/lib/renderers/three/appearance.d.ts +41 -0
  46. package/lib/renderers/three/appearance.d.ts.map +1 -0
  47. package/lib/renderers/three/appearance.js +100 -0
  48. package/lib/renderers/three/appearance.js.map +1 -0
  49. package/lib/renderers/three/build/build.test.d.ts +2 -0
  50. package/lib/renderers/three/build/build.test.d.ts.map +1 -0
  51. package/lib/renderers/three/build/build.test.js +120 -0
  52. package/lib/renderers/three/build/build.test.js.map +1 -0
  53. package/lib/renderers/three/build/parts.js +7 -6
  54. package/lib/renderers/three/build/parts.js.map +1 -1
  55. package/lib/renderers/three/build.d.ts +50 -0
  56. package/lib/renderers/three/build.d.ts.map +1 -0
  57. package/lib/renderers/three/build.js +210 -0
  58. package/lib/renderers/three/build.js.map +1 -0
  59. package/lib/renderers/three/camera/camera.test.d.ts +2 -0
  60. package/lib/renderers/three/camera/camera.test.d.ts.map +1 -0
  61. package/lib/renderers/three/camera/camera.test.js +46 -0
  62. package/lib/renderers/three/camera/camera.test.js.map +1 -0
  63. package/lib/renderers/three/camera.d.ts +63 -0
  64. package/lib/renderers/three/camera.d.ts.map +1 -0
  65. package/lib/renderers/three/camera.js +85 -0
  66. package/lib/renderers/three/camera.js.map +1 -0
  67. package/lib/renderers/three/compile/compile.test.d.ts +2 -0
  68. package/lib/renderers/three/compile/compile.test.d.ts.map +1 -0
  69. package/lib/renderers/three/compile/compile.test.js +93 -0
  70. package/lib/renderers/three/compile/compile.test.js.map +1 -0
  71. package/lib/renderers/three/compile.d.ts +58 -0
  72. package/lib/renderers/three/compile.d.ts.map +1 -0
  73. package/lib/renderers/three/compile.js +89 -0
  74. package/lib/renderers/three/compile.js.map +1 -0
  75. package/lib/renderers/three/controls.d.ts +27 -0
  76. package/lib/renderers/three/controls.d.ts.map +1 -0
  77. package/lib/renderers/three/controls.js +115 -0
  78. package/lib/renderers/three/controls.js.map +1 -0
  79. package/lib/renderers/three/geometry/geometry.test.d.ts +2 -0
  80. package/lib/renderers/three/geometry/geometry.test.d.ts.map +1 -0
  81. package/lib/renderers/three/geometry/geometry.test.js +80 -0
  82. package/lib/renderers/three/geometry/geometry.test.js.map +1 -0
  83. package/lib/renderers/three/geometry.d.ts +20 -0
  84. package/lib/renderers/three/geometry.d.ts.map +1 -0
  85. package/lib/renderers/three/geometry.js +164 -0
  86. package/lib/renderers/three/geometry.js.map +1 -0
  87. package/lib/renderers/three/gizmo.d.ts +49 -0
  88. package/lib/renderers/three/gizmo.d.ts.map +1 -0
  89. package/lib/renderers/three/gizmo.js +172 -0
  90. package/lib/renderers/three/gizmo.js.map +1 -0
  91. package/lib/renderers/three/input.d.ts +25 -0
  92. package/lib/renderers/three/input.d.ts.map +1 -0
  93. package/lib/renderers/three/input.js +71 -0
  94. package/lib/renderers/three/input.js.map +1 -0
  95. package/lib/renderers/three/lines.d.ts +16 -0
  96. package/lib/renderers/three/lines.d.ts.map +1 -0
  97. package/lib/renderers/three/lines.js +63 -0
  98. package/lib/renderers/three/lines.js.map +1 -0
  99. package/lib/renderers/three/materials.d.ts +15 -0
  100. package/lib/renderers/three/materials.d.ts.map +1 -0
  101. package/lib/renderers/three/materials.js +70 -0
  102. package/lib/renderers/three/materials.js.map +1 -0
  103. package/lib/renderers/three/options.d.ts +68 -0
  104. package/lib/renderers/three/options.d.ts.map +1 -0
  105. package/lib/renderers/three/options.js +64 -0
  106. package/lib/renderers/three/options.js.map +1 -0
  107. package/lib/renderers/three/overlays.d.ts +25 -0
  108. package/lib/renderers/three/overlays.d.ts.map +1 -0
  109. package/lib/renderers/three/overlays.js +57 -0
  110. package/lib/renderers/three/overlays.js.map +1 -0
  111. package/lib/renderers/three/quality/quality.test.d.ts +2 -0
  112. package/lib/renderers/three/quality/quality.test.d.ts.map +1 -0
  113. package/lib/renderers/three/quality/quality.test.js +23 -0
  114. package/lib/renderers/three/quality/quality.test.js.map +1 -0
  115. package/lib/renderers/three/quality.d.ts +24 -0
  116. package/lib/renderers/three/quality.d.ts.map +1 -0
  117. package/lib/renderers/three/quality.js +67 -0
  118. package/lib/renderers/three/quality.js.map +1 -0
  119. package/lib/renderers/three/text/text.d.ts +4 -2
  120. package/lib/renderers/three/text/text.d.ts.map +1 -1
  121. package/lib/renderers/three/text/text.js +6 -4
  122. package/lib/renderers/three/text/text.js.map +1 -1
  123. package/lib/renderers/three/text/text.test.d.ts +2 -0
  124. package/lib/renderers/three/text/text.test.d.ts.map +1 -0
  125. package/lib/renderers/three/text/text.test.js +70 -0
  126. package/lib/renderers/three/text/text.test.js.map +1 -0
  127. package/lib/renderers/three/text.d.ts +53 -0
  128. package/lib/renderers/three/text.d.ts.map +1 -0
  129. package/lib/renderers/three/text.js +151 -0
  130. package/lib/renderers/three/text.js.map +1 -0
  131. package/lib/renderers/three/viewer/options.test.d.ts +2 -0
  132. package/lib/renderers/three/viewer/options.test.d.ts.map +1 -0
  133. package/lib/renderers/three/viewer/options.test.js +38 -0
  134. package/lib/renderers/three/viewer/options.test.js.map +1 -0
  135. package/lib/renderers/three/viewer/viewer.js +6 -0
  136. package/lib/renderers/three/viewer/viewer.js.map +1 -1
  137. package/lib/renderers/three/viewer.d.ts +38 -0
  138. package/lib/renderers/three/viewer.d.ts.map +1 -0
  139. package/lib/renderers/three/viewer.js +435 -0
  140. package/lib/renderers/three/viewer.js.map +1 -0
  141. package/package.json +2 -2
  142. package/src/renderers/three/build/parts.ts +7 -6
  143. package/src/renderers/three/text/text.test.ts +22 -17
  144. package/src/renderers/three/text/text.ts +6 -4
  145. package/src/renderers/three/viewer/viewer.ts +6 -0
@@ -0,0 +1,68 @@
1
+ import type { Object3D, Texture } from "three";
2
+ import type { Group, Relation, Scene, Vec3, View } from "../../abstract-3d.js";
3
+ import type { MaterialState } from "./appearance.js";
4
+ import type { BufferZones, CameraOptions } from "./camera.js";
5
+ import type { ControlsOptions } from "./controls.js";
6
+ import type { ControlsHelper } from "./gizmo.js";
7
+ import type { MountOverlays } from "./overlays.js";
8
+ import type { Quality } from "./quality.js";
9
+ /** The viewer's options, and which of them changed in an update, so each change does only the work it needs */
10
+ export type Popover<Content> = {
11
+ readonly id: string;
12
+ readonly pos: Vec3;
13
+ readonly content: Content;
14
+ };
15
+ export type SceneEvent = {
16
+ readonly nativeEvent: MouseEvent;
17
+ /** Where the pointer hit, in world space */
18
+ readonly point: Vec3;
19
+ };
20
+ export type GroupCallback = (id: string | undefined, rootData: Record<string, string> | undefined, data: Record<string, string> | undefined, event: SceneEvent) => void;
21
+ export type ViewerOptions<Content> = {
22
+ /** Undefined shows the fallback */
23
+ readonly scene?: Scene;
24
+ /** The id of a top-level group, which its hover, selection, material state and events are by; groups without an id can't be clicked */
25
+ readonly createGroupId?: (g: Group) => string;
26
+ readonly selectedIds?: Record<string, boolean>;
27
+ readonly hoveredIds?: ReadonlyArray<string>;
28
+ readonly activeComponents?: Record<string, MaterialState>;
29
+ /**
30
+ * The ids of the relations whose shape is drawn to be clicked, e.g. where a component can be inserted. While defined, even when empty, only the
31
+ * groups whose material state is Accept can be clicked
32
+ */
33
+ readonly activeRelations?: ReadonlyArray<string>;
34
+ readonly showDimensions?: boolean;
35
+ readonly camera?: CameraOptions;
36
+ readonly view?: View;
37
+ readonly bufferZones?: BufferZones;
38
+ readonly fitPadding?: number;
39
+ /** Smooth controls by default; `orbit` keeps the OrbitControls of the r3f renderer */
40
+ readonly controls?: ControlsOptions;
41
+ readonly gizmo?: ControlsHelper;
42
+ /** Replace the default lights */
43
+ readonly lights?: ReadonlyArray<Object3D> | ((scene: Scene) => ReadonlyArray<Object3D>);
44
+ /** An environment map, or `room` for a neutral studio, lighting metal surfaces */
45
+ readonly environment?: Texture | "room";
46
+ readonly environmentIntensity?: number;
47
+ /** Of the default directional lights */
48
+ readonly lightIntensity?: number;
49
+ readonly quality?: Quality;
50
+ /** WebGL 2 by default; read once, when the viewer is created */
51
+ readonly backend?: "webgl" | "webgpu";
52
+ readonly useAlphaTest?: boolean;
53
+ readonly materialStateImages?: Record<string, string>;
54
+ readonly popovers?: ReadonlyArray<Popover<Content>>;
55
+ readonly getTooltips?: (id: string) => ReadonlyArray<Popover<Content>>;
56
+ /** Shown while loading, and while there's no scene */
57
+ readonly fallback?: Content;
58
+ /** Mounts popover, tooltip and fallback content, e.g. as React portals; without it, strings and DOM nodes are mounted directly */
59
+ readonly onOverlays?: MountOverlays<Content>;
60
+ readonly onClickGroup?: GroupCallback;
61
+ readonly onHoverGroup?: GroupCallback;
62
+ readonly onContextMenuGroup?: (id: string, rootData: Record<string, string> | undefined, data: Record<string, string> | undefined, left: number, top: number, event: SceneEvent) => void;
63
+ readonly onClickRelation?: (relation: Relation, event: SceneEvent) => void;
64
+ };
65
+ /** What an update has to redo, from the most work to the least */
66
+ export type Change = "compile" | "build" | "camera" | "rig" | "fit" | "gizmo" | "lights" | "quality" | "paint" | "dimensions" | "overlays";
67
+ export declare function getChanges<Content>(previous: ViewerOptions<Content>, next: ViewerOptions<Content>): ReadonlySet<Change>;
68
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/renderers/three/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /** How each option is compared, and what a change of it redoes; callbacks are read when used, so they need nothing */
2
+ const effects = {
3
+ scene: ["reference", ["compile", "build", "lights", "overlays"]],
4
+ createGroupId: ["reference", ["compile", "build"]],
5
+ activeComponents: ["contents", ["build"]],
6
+ activeRelations: ["value", ["build"]],
7
+ useAlphaTest: ["value", ["build"]],
8
+ materialStateImages: ["contents", ["build"]],
9
+ selectedIds: ["contents", ["paint"]],
10
+ hoveredIds: ["value", ["paint"]],
11
+ showDimensions: ["value", ["dimensions"]],
12
+ camera: ["value", ["camera", "rig", "fit"]],
13
+ view: ["value", ["fit"]],
14
+ bufferZones: ["value", ["fit"]],
15
+ fitPadding: ["value", ["fit"]],
16
+ controls: ["value", ["rig"]],
17
+ gizmo: ["value", ["gizmo"]],
18
+ lights: ["reference", ["lights"]],
19
+ environment: ["reference", ["lights"]],
20
+ environmentIntensity: ["value", ["lights"]],
21
+ lightIntensity: ["value", ["lights"]],
22
+ quality: ["value", ["quality"]],
23
+ backend: ["value", []],
24
+ popovers: ["reference", ["overlays"]],
25
+ getTooltips: ["reference", ["overlays"]],
26
+ fallback: ["reference", ["overlays"]],
27
+ onOverlays: ["reference", []],
28
+ onClickGroup: ["reference", ["paint"]],
29
+ onHoverGroup: ["reference", []],
30
+ onContextMenuGroup: ["reference", []],
31
+ onClickRelation: ["reference", []],
32
+ };
33
+ export function getChanges(previous, next) {
34
+ const changes = new Set();
35
+ Object.keys(effects).forEach((key) => {
36
+ const [comparison, redo] = effects[key];
37
+ if (!isEqual(comparison, previous[key], next[key])) {
38
+ redo.forEach((change) => changes.add(change));
39
+ }
40
+ });
41
+ return changes;
42
+ }
43
+ function isEqual(comparison, a, b) {
44
+ if (a === b) {
45
+ return true;
46
+ }
47
+ switch (comparison) {
48
+ case "reference":
49
+ return false;
50
+ case "contents":
51
+ return isShallowEqual(a, b);
52
+ default:
53
+ return JSON.stringify(a) === JSON.stringify(b);
54
+ }
55
+ }
56
+ function isShallowEqual(a, b) {
57
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
58
+ return false;
59
+ }
60
+ const [ra, rb] = [a, b];
61
+ const keys = Object.keys(ra);
62
+ return keys.length === Object.keys(rb).length && keys.every((key) => Object.is(ra[key], rb[key]));
63
+ }
64
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/renderers/three/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type Camera, Vector3 } from "three";
2
+ /** HTML elements over the canvas at projected 3D anchors, e.g. popovers and tooltips; their content is mounted by the app, or directly */
3
+ export type Overlay<Content> = {
4
+ readonly key: string;
5
+ /** In world space; without one the overlay is centered on the canvas */
6
+ readonly position?: Vector3;
7
+ readonly content: Content;
8
+ };
9
+ export type MountedOverlay<Content> = {
10
+ readonly key: string;
11
+ readonly element: HTMLElement;
12
+ readonly content: Content;
13
+ };
14
+ /** Mounts the overlays' content into their elements, called when the set of overlays or their content changes */
15
+ export type MountOverlays<Content> = (overlays: ReadonlyArray<MountedOverlay<Content>>) => void;
16
+ export type OverlayLayer<Content> = {
17
+ readonly set: (overlays: ReadonlyArray<Overlay<Content>>) => void;
18
+ /** Moves each element to its anchor's place on screen, hidden while the anchor is behind the camera */
19
+ readonly update: (camera: Camera, width: number, height: number) => void;
20
+ readonly dispose: () => void;
21
+ };
22
+ /** Mounts strings and DOM nodes as they are */
23
+ export declare const mountDirectly: <Content>(overlays: ReadonlyArray<MountedOverlay<Content>>) => void;
24
+ export declare function createOverlayLayer<Content>(container: HTMLElement, mount: MountOverlays<Content>): OverlayLayer<Content>;
25
+ //# sourceMappingURL=overlays.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../../src/renderers/three/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE7C,0IAA0I;AAE1I,MAAM,MAAM,OAAO,CAAC,OAAO,IAAI;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,OAAO,IAAI;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,iHAAiH;AACjH,MAAM,MAAM,aAAa,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;AAEhG,MAAM,MAAM,YAAY,CAAC,OAAO,IAAI;IAClC,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IAClE,uGAAuG;IACvG,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,aAAa,GAAI,OAAO,YAAY,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAG,IACsC,CAAC;AAEjI,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAqDxH"}
@@ -0,0 +1,57 @@
1
+ import { Vector3 } from "three";
2
+ /** Mounts strings and DOM nodes as they are */
3
+ export const mountDirectly = (overlays) => overlays.forEach(({ element, content }) => element.replaceChildren(content instanceof Node ? content : String(content ?? "")));
4
+ export function createOverlayLayer(container, mount) {
5
+ const layer = document.createElement("div");
6
+ Object.assign(layer.style, { position: "absolute", inset: "0", overflow: "hidden", pointerEvents: "none" });
7
+ container.append(layer);
8
+ const mounted = new Map();
9
+ const projected = new Vector3();
10
+ return {
11
+ set: (overlays) => {
12
+ const keys = new Set(overlays.map((o) => o.key));
13
+ const changed = overlays.some((o) => mounted.get(o.key)?.overlay.content !== o.content) || [...mounted.keys()].some((key) => !keys.has(key));
14
+ mounted.forEach(({ element }, key) => {
15
+ if (!keys.has(key)) {
16
+ element.remove();
17
+ mounted.delete(key);
18
+ }
19
+ });
20
+ overlays.forEach((overlay) => {
21
+ const existing = mounted.get(overlay.key);
22
+ if (existing) {
23
+ existing.overlay = overlay;
24
+ return;
25
+ }
26
+ const element = document.createElement("div");
27
+ Object.assign(element.style, { position: "absolute", left: "0", top: "0", pointerEvents: "none", visibility: "hidden" });
28
+ layer.append(element);
29
+ mounted.set(overlay.key, { element, overlay });
30
+ });
31
+ if (changed) {
32
+ mount([...mounted.entries()].map(([key, { element, overlay }]) => ({ key, element, content: overlay.content })));
33
+ }
34
+ },
35
+ update: (camera, width, height) => mounted.forEach(({ element, overlay }) => {
36
+ if (overlay.position) {
37
+ projected.copy(overlay.position).project(camera);
38
+ }
39
+ else {
40
+ projected.set(0, 0, 0);
41
+ }
42
+ const behind = projected.z < -1 || projected.z > 1;
43
+ element.style.visibility = behind ? "hidden" : "visible";
44
+ if (!behind) {
45
+ const x = ((projected.x + 1) / 2) * width;
46
+ const y = ((1 - projected.y) / 2) * height;
47
+ element.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
48
+ }
49
+ }),
50
+ dispose: () => {
51
+ layer.remove();
52
+ mounted.clear();
53
+ mount([]);
54
+ },
55
+ };
56
+ }
57
+ //# sourceMappingURL=overlays.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlays.js","sourceRoot":"","sources":["../../../src/renderers/three/overlays.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=quality.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.test.d.ts","sourceRoot":"","sources":["../../../../src/renderers/three/quality/quality.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { recordFrame } from "./quality.js";
3
+ const governor = (quality) => ({ quality, tier: quality === "auto" ? "medium" : quality, sum: 0, count: 0 });
4
+ const record = (g, intervals) => intervals.map((ms) => recordFrame(g, ms));
5
+ describe("recordFrame", () => {
6
+ it("keeps a fixed quality", () => {
7
+ const g = governor("high");
8
+ record(g, Array(100).fill(100));
9
+ expect(g.tier).toBe("high");
10
+ });
11
+ it("drops auto to low for good when moving frames are slow", () => {
12
+ const g = governor("auto");
13
+ expect(record(g, Array(30).fill(40)).filter(Boolean)).toHaveLength(1);
14
+ record(g, Array(100).fill(5));
15
+ expect(g.tier).toBe("low");
16
+ });
17
+ it("keeps auto at medium at 60 frames per second, ignoring the pauses between moves", () => {
18
+ const g = governor("auto");
19
+ record(g, Array.from({ length: 100 }, (_, i) => (i % 10 === 0 ? 2000 : 16.7)));
20
+ expect(g.tier).toBe("medium");
21
+ });
22
+ });
23
+ //# sourceMappingURL=quality.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.test.js","sourceRoot":"","sources":["../../../../src/renderers/three/quality/quality.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,QAAQ,GAAG,CAAC,OAAgB,EAAgB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACpI,MAAM,MAAM,GAAG,CAAC,CAAe,EAAE,SAAgC,EAA0B,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAExI,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,CAAC,EAAE,KAAK,CAAS,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAS,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,CAAC,EAAE,KAAK,CAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,CACJ,CAAC,EACD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CACpE,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { OrthographicCamera, PerspectiveCamera, Scene } from "three";
2
+ import { type WebGPURenderer } from "three/webgpu";
3
+ /**
4
+ * Quality tiers that spend the GPU where it shows: the camera is still most of the time, and a still frame can afford ambient occlusion that a moving
5
+ * one can't on a weak GPU
6
+ */
7
+ export type Tier = "low" | "medium" | "high";
8
+ export type Quality = Tier | "auto";
9
+ export declare const getPixelRatio: (tier: Tier, devicePixelRatio: number) => number;
10
+ export type TierGovernor = {
11
+ readonly tier: () => Tier;
12
+ /** Records the time between two frames while the camera moves; returns the new tier when it changes */
13
+ readonly record: (intervalMs: number) => Tier | undefined;
14
+ };
15
+ /** `auto` starts at medium, and drops to low for good once moving frames average slower than 40 per second */
16
+ export declare function createTierGovernor(quality: Quality): TierGovernor;
17
+ export type RenderPath = {
18
+ /** Renders a frame; true while the frame at rest needs more frames to fade its ambient occlusion in */
19
+ readonly render: (moving: boolean) => boolean;
20
+ readonly dispose: () => void;
21
+ };
22
+ /** Low renders directly; medium adds ambient occlusion at rest, faded in; high has it while moving too */
23
+ export declare function createRenderPath(renderer: WebGPURenderer, scene: Scene, camera: PerspectiveCamera | OrthographicCamera, tier: Tier, aoRadius: number): RenderPath;
24
+ //# sourceMappingURL=quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../../src/renderers/three/quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAG1E,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9E;;;GAGG;AAEH,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAC7C,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAIpC,eAAO,MAAM,aAAa,SAAU,IAAI,oBAAoB,MAAM,KAAG,MAA0D,CAAC;AAEhI,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,uGAAuG;IACvG,QAAQ,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;CAC3D,CAAC;AAOF,8GAA8G;AAC9G,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAkBjE;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,uGAAuG;IACvG,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAIF,0GAA0G;AAC1G,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,iBAAiB,GAAG,kBAAkB,EAC9C,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,GACf,UAAU,CAkCZ"}
@@ -0,0 +1,67 @@
1
+ import { ao } from "three/addons/tsl/display/GTAONode.js";
2
+ import { float, mix, pass, screenUV, uniform, vec4 } from "three/tsl";
3
+ import { RenderPipeline } from "three/webgpu";
4
+ const pixelRatioCaps = { low: 1, medium: 1.5, high: 2 };
5
+ export const getPixelRatio = (tier, devicePixelRatio) => Math.min(devicePixelRatio, pixelRatioCaps[tier]);
6
+ const windowSize = 30;
7
+ const slowFrameMs = 25;
8
+ /** Longer intervals are pauses between moves, not slow frames */
9
+ const pauseMs = 250;
10
+ /** `auto` starts at medium, and drops to low for good once moving frames average slower than 40 per second */
11
+ export function createTierGovernor(quality) {
12
+ const state = { tier: quality === "auto" ? "medium" : quality, sum: 0, count: 0 };
13
+ return {
14
+ tier: () => state.tier,
15
+ record: (intervalMs) => {
16
+ if (quality !== "auto" || state.tier === "low" || intervalMs > pauseMs) {
17
+ return undefined;
18
+ }
19
+ state.sum += intervalMs;
20
+ state.count += 1;
21
+ if (state.count < windowSize) {
22
+ return undefined;
23
+ }
24
+ const slow = state.sum / state.count > slowFrameMs;
25
+ Object.assign(state, { sum: 0, count: 0 }, slow ? { tier: "low" } : {});
26
+ return slow ? state.tier : undefined;
27
+ },
28
+ };
29
+ }
30
+ const fadeFrames = 6;
31
+ /** Low renders directly; medium adds ambient occlusion at rest, faded in; high has it while moving too */
32
+ export function createRenderPath(renderer, scene, camera, tier, aoRadius) {
33
+ if (tier === "low") {
34
+ return { render: () => (renderer.render(scene, camera), false), dispose: () => undefined };
35
+ }
36
+ const pipeline = new RenderPipeline(renderer);
37
+ // Multisampled, as SMAA would lose the antialiased alpha at edges over the transparent background
38
+ const scenePass = pass(scene, camera, { samples: 4 });
39
+ const color = scenePass.getTextureNode("output");
40
+ // Normals are reconstructed from depth, so there's no extra normal pass; the typings miss that the normal node is optional
41
+ const aoPass = ao(scenePass.getTextureNode("depth"), null, camera);
42
+ aoPass.resolutionScale = 0.5;
43
+ aoPass.radius.value = aoRadius;
44
+ const strength = uniform(0);
45
+ const occlusion = mix(float(1), aoPass.getTextureNode().sample(screenUV).r, strength);
46
+ pipeline.outputNode = vec4(color.rgb.mul(occlusion), color.a);
47
+ const fade = { frame: 0 };
48
+ return {
49
+ render: (moving) => {
50
+ if (moving && tier === "medium") {
51
+ fade.frame = 0;
52
+ renderer.render(scene, camera);
53
+ return false;
54
+ }
55
+ fade.frame = tier === "high" ? fadeFrames : Math.min(fadeFrames, fade.frame + 1);
56
+ strength.value = fade.frame / fadeFrames;
57
+ pipeline.render();
58
+ return fade.frame < fadeFrames;
59
+ },
60
+ dispose: () => {
61
+ aoPass.dispose();
62
+ scenePass.dispose();
63
+ pipeline.dispose();
64
+ },
65
+ };
66
+ }
67
+ //# sourceMappingURL=quality.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.js","sourceRoot":"","sources":["../../../src/renderers/three/quality.ts"],"names":[],"mappings":"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"}
@@ -5,8 +5,10 @@ import { DataTexture } from "three";
5
5
  * GL JS's approach (its @mapbox/tiny-sdf rasterizes), and the distance field text Valve's (Green, "Improved Alpha-Tested Magnification for Vector
6
6
  * Textures and Special Effects", SIGGRAPH 2007)
7
7
  */
8
- export declare const RASTER_SIZE = 40;
9
- export declare const SDF_BUFFER = 4;
8
+ export declare const RASTER_SIZE = 96;
9
+ export declare const SDF_BUFFER = 8;
10
+ /** The distance, in raster pixels, the distance field's values span */
11
+ export declare const SDF_RADIUS = 24;
10
12
  /** The distance field's value at the glyph's edge, from tiny-sdf's default cutoff of 0.25 */
11
13
  export declare const SDF_EDGE = 0.75;
12
14
  export type GlyphBitmap = ReturnType<TinySDF["draw"]>;
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,WAAW,EAA6C,MAAM,OAAO,CAAC;AAE/E;;;;GAIG;AAEH,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,OAAO,CAAC;AAM7B,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,CAAC;AAEtD,MAAM,MAAM,KAAK,GAAG;IAClB,4GAA4G;IAC5G,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,qHAAqH;IACrH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,sGAAsG;AACtG,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,6HAA6H;AAC7H,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,SAAO,GAAG,UAAU,CAI/E;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAoB/D;AAqBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAwBtG"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,WAAW,EAA6C,MAAM,OAAO,CAAC;AAE/E;;;;GAIG;AAEH,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,uEAAuE;AACvE,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,OAAO,CAAC;AAM7B,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,CAAC;AAEtD,MAAM,MAAM,KAAK,GAAG;IAClB,4GAA4G;IAC5G,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,qHAAqH;IACrH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,sGAAsG;AACtG,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,6HAA6H;AAC7H,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,SAAO,GAAG,UAAU,CAI/E;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAoB/D;AAqBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAwBtG"}
@@ -5,8 +5,10 @@ import { DataTexture, LinearFilter, RedFormat, UnsignedByteType } from "three";
5
5
  * GL JS's approach (its @mapbox/tiny-sdf rasterizes), and the distance field text Valve's (Green, "Improved Alpha-Tested Magnification for Vector
6
6
  * Textures and Special Effects", SIGGRAPH 2007)
7
7
  */
8
- export const RASTER_SIZE = 40;
9
- export const SDF_BUFFER = 4;
8
+ export const RASTER_SIZE = 96;
9
+ export const SDF_BUFFER = 8;
10
+ /** The distance, in raster pixels, the distance field's values span */
11
+ export const SDF_RADIUS = 24;
10
12
  /** The distance field's value at the glyph's edge, from tiny-sdf's default cutoff of 0.25 */
11
13
  export const SDF_EDGE = 0.75;
12
14
  const LINE_HEIGHT = 1.5;
@@ -14,7 +16,7 @@ const LETTER_SPACING = 0.001;
14
16
  /** Of the font size, above the baseline, to center a line's glyphs in it */
15
17
  const ASCENT = 0.8;
16
18
  /** Without `rasterize`, glyphs are rasterized with the OS's built-in Helvetica or Arial, on a canvas created at first use */
17
- export function createGlyphAtlas(rasterize, size = 1024) {
19
+ export function createGlyphAtlas(rasterize, size = 2048) {
18
20
  const texture = new DataTexture(new Uint8Array(size * size), size, size, RedFormat, UnsignedByteType);
19
21
  Object.assign(texture, { minFilter: LinearFilter, magFilter: LinearFilter, generateMipmaps: false });
20
22
  return { texture, size, glyphs: new Map(), shelf: { x: 0, y: 0, height: 0 }, rasterize, sdf: {} };
@@ -26,7 +28,7 @@ export function getGlyph(atlas, char) {
26
28
  }
27
29
  atlas.sdf.current ??= atlas.rasterize
28
30
  ? undefined
29
- : new TinySDF({ fontSize: RASTER_SIZE, buffer: SDF_BUFFER, radius: 10, fontFamily: "Helvetica, Arial, sans-serif" });
31
+ : new TinySDF({ fontSize: RASTER_SIZE, buffer: SDF_BUFFER, radius: SDF_RADIUS, fontFamily: "Helvetica, Arial, sans-serif" });
30
32
  const bitmap = atlas.rasterize?.(char) ?? atlas.sdf.current.draw(char);
31
33
  const rect = bitmap.width > 2 * SDF_BUFFER && bitmap.height > 2 * SDF_BUFFER ? packGlyph(atlas, bitmap) : undefined;
32
34
  const glyph = {
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=text.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.test.d.ts","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createGlyphAtlas, getGlyph, layoutText, RASTER_SIZE, SDF_BUFFER } from "./text.js";
3
+ /** Glyphs of 20 px ink, 30 px of it above the baseline, a 24 px advance, filled with their char code */
4
+ const rasterize = (char) => {
5
+ const [glyphWidth, glyphHeight] = char === " " ? [0, 0] : [20, 40];
6
+ const [width, height] = [glyphWidth + 2 * SDF_BUFFER, glyphHeight + 2 * SDF_BUFFER];
7
+ return {
8
+ data: new Uint8ClampedArray(width * height).fill(char.charCodeAt(0)),
9
+ width,
10
+ height,
11
+ glyphWidth,
12
+ glyphHeight,
13
+ glyphLeft: 2,
14
+ glyphTop: 30,
15
+ glyphAdvance: 24,
16
+ };
17
+ };
18
+ const em = (px) => px / RASTER_SIZE;
19
+ const round = (v) => Math.round(v * 1e4) / 1e4;
20
+ describe("getGlyph", () => {
21
+ it("packs each glyph once, left to right, and writes its bitmap", () => {
22
+ const atlas = createGlyphAtlas(rasterize, 64);
23
+ const a = getGlyph(atlas, "A");
24
+ expect(getGlyph(atlas, "A")).toBe(a);
25
+ const b = getGlyph(atlas, "B");
26
+ const data = atlas.texture.image.data;
27
+ expect([data[0], data[28]]).toEqual([65, 66]);
28
+ expect(a.rect.map(round)).toEqual([0, round(48 / 64), round(28 / 64), round(-48 / 64)]);
29
+ expect(b.rect[0]).toBe(28 / 64);
30
+ });
31
+ it("starts a new row when a row is full", () => {
32
+ const atlas = createGlyphAtlas(rasterize, 128);
33
+ ["A", "B", "C", "D"].forEach((char) => getGlyph(atlas, char));
34
+ expect(getGlyph(atlas, "E").rect.slice(0, 2)).toEqual([0, 96 / 128]);
35
+ });
36
+ it("gives glyphs their bitmap's position and size in em, from the pen at the baseline", () => {
37
+ expect(getGlyph(createGlyphAtlas(rasterize, 64), "A")).toMatchObject({
38
+ left: em(2 - SDF_BUFFER),
39
+ top: em(30 + SDF_BUFFER),
40
+ width: em(28),
41
+ height: em(48),
42
+ advance: em(24),
43
+ });
44
+ });
45
+ it("leaves glyphs without ink out of the atlas", () => {
46
+ const atlas = createGlyphAtlas(rasterize, 64);
47
+ expect(getGlyph(atlas, " ").width).toBe(0);
48
+ expect(getGlyph(atlas, "A").rect[0]).toBe(0);
49
+ });
50
+ });
51
+ describe("layoutText", () => {
52
+ const atlas = createGlyphAtlas(rasterize, 256);
53
+ it("centers a line on the origin", () => {
54
+ const advance = 24 + 0.001 * 40;
55
+ const quads = layoutText(atlas, "AA", 40);
56
+ expect(quads.map((q) => round(q.x))).toEqual([round(-advance + 2 - SDF_BUFFER + 14), round(2 - SDF_BUFFER + 14)]);
57
+ expect(quads.map((q) => [round(q.width), round(q.height)])).toEqual([
58
+ [28, 48],
59
+ [28, 48],
60
+ ]);
61
+ });
62
+ it("stacks lines 1.5 font sizes apart, centered vertically as a block", () => {
63
+ // The block is 120 high; each baseline is half the 20 of leading and an ascent of 32 below its line's top, and the glyphs' centers 10 above it
64
+ expect(layoutText(atlas, "A\nA", 40).map((q) => round(q.y))).toEqual([28, -32]);
65
+ });
66
+ it("skips glyphs without ink but keeps their advance", () => {
67
+ expect(layoutText(atlas, "A A", 40)).toHaveLength(2);
68
+ });
69
+ });
70
+ //# sourceMappingURL=text.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.test.js","sourceRoot":"","sources":["../../../../src/renderers/three/text/text.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { DataTexture, Matrix4 } from "three";
2
+ import type { Part } from "./build.js";
3
+ /** Text as signed distance field glyphs from the system font, packed into one atlas texture, so all text draws in one call */
4
+ export declare const RASTER_SIZE = 40;
5
+ export declare const SDF_BUFFER = 4;
6
+ /** The distance field's value at the glyph's edge, from tiny-sdf's default cutoff of 0.25 */
7
+ export declare const SDF_EDGE = 0.75;
8
+ export type GlyphBitmap = {
9
+ readonly data: Uint8ClampedArray;
10
+ readonly width: number;
11
+ readonly height: number;
12
+ readonly glyphLeft: number;
13
+ readonly glyphTop: number;
14
+ readonly glyphAdvance: number;
15
+ };
16
+ export type Rasterize = (char: string) => GlyphBitmap;
17
+ export type Glyph = {
18
+ /** The glyph's place in the atlas as a uv offset and scale, for a quad with uv (0, 0) at its bottom left */
19
+ readonly rect: readonly [number, number, number, number];
20
+ /** The bitmap's left and top edges and size, in em, from the pen at the baseline; no width for glyphs without ink */
21
+ readonly left: number;
22
+ readonly top: number;
23
+ readonly width: number;
24
+ readonly height: number;
25
+ readonly advance: number;
26
+ };
27
+ export type GlyphAtlas = {
28
+ readonly texture: DataTexture;
29
+ readonly getGlyph: (char: string) => Glyph;
30
+ readonly dispose: () => void;
31
+ };
32
+ /** A quad of a laid out text, with its center and size in the text's units, centered on the origin */
33
+ export type GlyphQuad = {
34
+ readonly glyph: Glyph;
35
+ readonly x: number;
36
+ readonly y: number;
37
+ readonly width: number;
38
+ readonly height: number;
39
+ };
40
+ /** Rasterizes with the OS's built-in Helvetica or Arial, on a canvas created at first use */
41
+ export declare function createSystemFontRasterizer(): Rasterize;
42
+ export declare function createGlyphAtlas(rasterize?: Rasterize, size?: number): GlyphAtlas;
43
+ /** Lays out the text centered on the origin, each line centered, as drei's Text did */
44
+ export declare function layoutText(text: string, fontSize: number, getGlyph: (char: string) => Glyph): ReadonlyArray<GlyphQuad>;
45
+ export type TextEntry = {
46
+ readonly text: string;
47
+ readonly fontSize: number;
48
+ /** The text's position and rotation, relative to its item's matrix */
49
+ readonly local: Matrix4;
50
+ };
51
+ /** All texts of a part as glyph quads of one instanced mesh, each slot a text */
52
+ export declare function createTextPart(entries: ReadonlyArray<TextEntry>, atlas: GlyphAtlas): Part;
53
+ //# sourceMappingURL=text.d.ts.map