abstract-3d 3.1.2 → 4.0.0-beta.2

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 (220) hide show
  1. package/README.md +21 -1
  2. package/lib/renderers/dxf/dxf-encoding/dxf-build.d.ts.map +1 -1
  3. package/lib/renderers/dxf/dxf-encoding/dxf-build.js +1 -2
  4. package/lib/renderers/dxf/dxf-encoding/dxf-build.js.map +1 -1
  5. package/lib/renderers/ifc/ifc.test.d.ts +2 -0
  6. package/lib/renderers/ifc/ifc.test.d.ts.map +1 -0
  7. package/lib/renderers/ifc/ifc.test.js +380 -0
  8. package/lib/renderers/ifc/ifc.test.js.map +1 -0
  9. package/lib/renderers/index.d.ts +1 -0
  10. package/lib/renderers/index.d.ts.map +1 -1
  11. package/lib/renderers/index.js +1 -0
  12. package/lib/renderers/index.js.map +1 -1
  13. package/lib/renderers/react/index.d.ts +2 -5
  14. package/lib/renderers/react/index.d.ts.map +1 -1
  15. package/lib/renderers/react/index.js +1 -2
  16. package/lib/renderers/react/index.js.map +1 -1
  17. package/lib/renderers/react/react.d.ts +16 -42
  18. package/lib/renderers/react/react.d.ts.map +1 -1
  19. package/lib/renderers/react/react.js +50 -32
  20. package/lib/renderers/react/react.js.map +1 -1
  21. package/lib/renderers/three/appearance/appearance.d.ts +41 -0
  22. package/lib/renderers/three/appearance/appearance.d.ts.map +1 -0
  23. package/lib/renderers/three/appearance/appearance.js +92 -0
  24. package/lib/renderers/three/appearance/appearance.js.map +1 -0
  25. package/lib/renderers/three/appearance/appearance.test.d.ts +2 -0
  26. package/lib/renderers/three/appearance/appearance.test.d.ts.map +1 -0
  27. package/lib/renderers/three/appearance/appearance.test.js +97 -0
  28. package/lib/renderers/three/appearance/appearance.test.js.map +1 -0
  29. package/lib/renderers/three/appearance.d.ts +41 -0
  30. package/lib/renderers/three/appearance.d.ts.map +1 -0
  31. package/lib/renderers/three/appearance.js +100 -0
  32. package/lib/renderers/three/appearance.js.map +1 -0
  33. package/lib/renderers/three/build/build.d.ts +51 -0
  34. package/lib/renderers/three/build/build.d.ts.map +1 -0
  35. package/lib/renderers/three/build/build.js +178 -0
  36. package/lib/renderers/three/build/build.js.map +1 -0
  37. package/lib/renderers/three/build/build.test.d.ts +2 -0
  38. package/lib/renderers/three/build/build.test.d.ts.map +1 -0
  39. package/lib/renderers/three/build/build.test.js +120 -0
  40. package/lib/renderers/three/build/build.test.js.map +1 -0
  41. package/lib/renderers/three/build/materials.d.ts +16 -0
  42. package/lib/renderers/three/build/materials.d.ts.map +1 -0
  43. package/lib/renderers/three/build/materials.js +60 -0
  44. package/lib/renderers/three/build/materials.js.map +1 -0
  45. package/lib/renderers/three/build/parts.d.ts +65 -0
  46. package/lib/renderers/three/build/parts.d.ts.map +1 -0
  47. package/lib/renderers/three/build/parts.js +203 -0
  48. package/lib/renderers/three/build/parts.js.map +1 -0
  49. package/lib/renderers/three/build.d.ts +50 -0
  50. package/lib/renderers/three/build.d.ts.map +1 -0
  51. package/lib/renderers/three/build.js +210 -0
  52. package/lib/renderers/three/build.js.map +1 -0
  53. package/lib/renderers/three/camera/camera.d.ts +40 -0
  54. package/lib/renderers/three/camera/camera.d.ts.map +1 -0
  55. package/lib/renderers/three/camera/camera.js +51 -0
  56. package/lib/renderers/three/camera/camera.js.map +1 -0
  57. package/lib/renderers/three/camera/camera.test.d.ts +2 -0
  58. package/lib/renderers/three/camera/camera.test.d.ts.map +1 -0
  59. package/lib/renderers/three/camera/camera.test.js +46 -0
  60. package/lib/renderers/three/camera/camera.test.js.map +1 -0
  61. package/lib/renderers/three/camera/controls.d.ts +24 -0
  62. package/lib/renderers/three/camera/controls.d.ts.map +1 -0
  63. package/lib/renderers/three/camera/controls.js +39 -0
  64. package/lib/renderers/three/camera/controls.js.map +1 -0
  65. package/lib/renderers/three/camera/gizmo.d.ts +44 -0
  66. package/lib/renderers/three/camera/gizmo.d.ts.map +1 -0
  67. package/lib/renderers/three/camera/gizmo.js +42 -0
  68. package/lib/renderers/three/camera/gizmo.js.map +1 -0
  69. package/lib/renderers/three/camera.d.ts +63 -0
  70. package/lib/renderers/three/camera.d.ts.map +1 -0
  71. package/lib/renderers/three/camera.js +85 -0
  72. package/lib/renderers/three/camera.js.map +1 -0
  73. package/lib/renderers/three/compile/compile.d.ts +59 -0
  74. package/lib/renderers/three/compile/compile.d.ts.map +1 -0
  75. package/lib/renderers/three/compile/compile.js +88 -0
  76. package/lib/renderers/three/compile/compile.js.map +1 -0
  77. package/lib/renderers/three/compile/compile.test.d.ts +2 -0
  78. package/lib/renderers/three/compile/compile.test.d.ts.map +1 -0
  79. package/lib/renderers/three/compile/compile.test.js +93 -0
  80. package/lib/renderers/three/compile/compile.test.js.map +1 -0
  81. package/lib/renderers/three/compile.d.ts +58 -0
  82. package/lib/renderers/three/compile.d.ts.map +1 -0
  83. package/lib/renderers/three/compile.js +89 -0
  84. package/lib/renderers/three/compile.js.map +1 -0
  85. package/lib/renderers/three/controls.d.ts +27 -0
  86. package/lib/renderers/three/controls.d.ts.map +1 -0
  87. package/lib/renderers/three/controls.js +115 -0
  88. package/lib/renderers/three/controls.js.map +1 -0
  89. package/lib/renderers/three/geometry/geometry.d.ts +21 -0
  90. package/lib/renderers/three/geometry/geometry.d.ts.map +1 -0
  91. package/lib/renderers/three/geometry/geometry.js +132 -0
  92. package/lib/renderers/three/geometry/geometry.js.map +1 -0
  93. package/lib/renderers/three/geometry/geometry.test.d.ts +2 -0
  94. package/lib/renderers/three/geometry/geometry.test.d.ts.map +1 -0
  95. package/lib/renderers/three/geometry/geometry.test.js +80 -0
  96. package/lib/renderers/three/geometry/geometry.test.js.map +1 -0
  97. package/lib/renderers/three/geometry.d.ts +20 -0
  98. package/lib/renderers/three/geometry.d.ts.map +1 -0
  99. package/lib/renderers/three/geometry.js +164 -0
  100. package/lib/renderers/three/geometry.js.map +1 -0
  101. package/lib/renderers/three/gizmo.d.ts +49 -0
  102. package/lib/renderers/three/gizmo.d.ts.map +1 -0
  103. package/lib/renderers/three/gizmo.js +172 -0
  104. package/lib/renderers/three/gizmo.js.map +1 -0
  105. package/lib/renderers/three/index.d.ts +16 -0
  106. package/lib/renderers/three/index.d.ts.map +1 -0
  107. package/lib/renderers/three/index.js +6 -0
  108. package/lib/renderers/three/index.js.map +1 -0
  109. package/lib/renderers/three/input.d.ts +25 -0
  110. package/lib/renderers/three/input.d.ts.map +1 -0
  111. package/lib/renderers/three/input.js +71 -0
  112. package/lib/renderers/three/input.js.map +1 -0
  113. package/lib/renderers/three/lines.d.ts +16 -0
  114. package/lib/renderers/three/lines.d.ts.map +1 -0
  115. package/lib/renderers/three/lines.js +63 -0
  116. package/lib/renderers/three/lines.js.map +1 -0
  117. package/lib/renderers/three/materials.d.ts +15 -0
  118. package/lib/renderers/three/materials.d.ts.map +1 -0
  119. package/lib/renderers/three/materials.js +70 -0
  120. package/lib/renderers/three/materials.js.map +1 -0
  121. package/lib/renderers/three/options.d.ts +68 -0
  122. package/lib/renderers/three/options.d.ts.map +1 -0
  123. package/lib/renderers/three/options.js +64 -0
  124. package/lib/renderers/three/options.js.map +1 -0
  125. package/lib/renderers/three/overlays.d.ts +25 -0
  126. package/lib/renderers/three/overlays.d.ts.map +1 -0
  127. package/lib/renderers/three/overlays.js +57 -0
  128. package/lib/renderers/three/overlays.js.map +1 -0
  129. package/lib/renderers/three/quality/quality.d.ts +17 -0
  130. package/lib/renderers/three/quality/quality.d.ts.map +1 -0
  131. package/lib/renderers/three/quality/quality.js +27 -0
  132. package/lib/renderers/three/quality/quality.js.map +1 -0
  133. package/lib/renderers/three/quality/quality.test.d.ts +2 -0
  134. package/lib/renderers/three/quality/quality.test.d.ts.map +1 -0
  135. package/lib/renderers/three/quality/quality.test.js +23 -0
  136. package/lib/renderers/three/quality/quality.test.js.map +1 -0
  137. package/lib/renderers/three/quality.d.ts +24 -0
  138. package/lib/renderers/three/quality.d.ts.map +1 -0
  139. package/lib/renderers/three/quality.js +67 -0
  140. package/lib/renderers/three/quality.js.map +1 -0
  141. package/lib/renderers/three/shadow/shadow.d.ts +15 -0
  142. package/lib/renderers/three/shadow/shadow.d.ts.map +1 -0
  143. package/lib/renderers/three/shadow/shadow.js +81 -0
  144. package/lib/renderers/three/shadow/shadow.js.map +1 -0
  145. package/lib/renderers/three/text/text.d.ts +55 -0
  146. package/lib/renderers/three/text/text.d.ts.map +1 -0
  147. package/lib/renderers/three/text/text.js +90 -0
  148. package/lib/renderers/three/text/text.js.map +1 -0
  149. package/lib/renderers/three/text/text.test.d.ts +2 -0
  150. package/lib/renderers/three/text/text.test.d.ts.map +1 -0
  151. package/lib/renderers/three/text/text.test.js +70 -0
  152. package/lib/renderers/three/text/text.test.js.map +1 -0
  153. package/lib/renderers/three/text.d.ts +53 -0
  154. package/lib/renderers/three/text.d.ts.map +1 -0
  155. package/lib/renderers/three/text.js +151 -0
  156. package/lib/renderers/three/text.js.map +1 -0
  157. package/lib/renderers/three/viewer/options.d.ts +75 -0
  158. package/lib/renderers/three/viewer/options.d.ts.map +1 -0
  159. package/lib/renderers/three/viewer/options.js +33 -0
  160. package/lib/renderers/three/viewer/options.js.map +1 -0
  161. package/lib/renderers/three/viewer/options.test.d.ts +2 -0
  162. package/lib/renderers/three/viewer/options.test.d.ts.map +1 -0
  163. package/lib/renderers/three/viewer/options.test.js +38 -0
  164. package/lib/renderers/three/viewer/options.test.js.map +1 -0
  165. package/lib/renderers/three/viewer/overlays.d.ts +31 -0
  166. package/lib/renderers/three/viewer/overlays.d.ts.map +1 -0
  167. package/lib/renderers/three/viewer/overlays.js +43 -0
  168. package/lib/renderers/three/viewer/overlays.js.map +1 -0
  169. package/lib/renderers/three/viewer/viewer.d.ts +91 -0
  170. package/lib/renderers/three/viewer/viewer.d.ts.map +1 -0
  171. package/lib/renderers/three/viewer/viewer.js +436 -0
  172. package/lib/renderers/three/viewer/viewer.js.map +1 -0
  173. package/lib/renderers/three/viewer.d.ts +38 -0
  174. package/lib/renderers/three/viewer.d.ts.map +1 -0
  175. package/lib/renderers/three/viewer.js +435 -0
  176. package/lib/renderers/three/viewer.js.map +1 -0
  177. package/package.json +34 -16
  178. package/src/renderers/dxf/dxf-encoding/dxf-build.ts +1 -2
  179. package/src/renderers/index.ts +1 -0
  180. package/src/renderers/react/index.ts +2 -5
  181. package/src/renderers/react/react.tsx +97 -181
  182. package/src/renderers/three/appearance/appearance.test.ts +121 -0
  183. package/src/renderers/three/appearance/appearance.ts +135 -0
  184. package/src/renderers/three/build/build.test.ts +141 -0
  185. package/src/renderers/three/build/build.ts +273 -0
  186. package/src/renderers/three/build/materials.ts +91 -0
  187. package/src/renderers/three/build/parts.ts +277 -0
  188. package/src/renderers/three/camera/camera.test.ts +53 -0
  189. package/src/renderers/three/camera/camera.ts +82 -0
  190. package/src/renderers/three/camera/controls.ts +65 -0
  191. package/src/renderers/three/camera/gizmo.ts +93 -0
  192. package/src/renderers/three/compile/compile.test.ts +113 -0
  193. package/src/renderers/three/compile/compile.ts +151 -0
  194. package/src/renderers/three/geometry/geometry.test.ts +95 -0
  195. package/src/renderers/three/geometry/geometry.ts +179 -0
  196. package/src/renderers/three/index.ts +15 -0
  197. package/src/renderers/three/quality/quality.test.ts +37 -0
  198. package/src/renderers/three/quality/quality.ts +35 -0
  199. package/src/renderers/three/shadow/shadow.ts +116 -0
  200. package/src/renderers/three/text/text.test.ts +79 -0
  201. package/src/renderers/three/text/text.ts +128 -0
  202. package/src/renderers/three/viewer/options.ts +104 -0
  203. package/src/renderers/three/viewer/overlays.ts +71 -0
  204. package/src/renderers/three/viewer/viewer.ts +543 -0
  205. package/lib/renderers/react/react-hotspot.d.ts +0 -27
  206. package/lib/renderers/react/react-hotspot.d.ts.map +0 -1
  207. package/lib/renderers/react/react-hotspot.js +0 -37
  208. package/lib/renderers/react/react-hotspot.js.map +0 -1
  209. package/src/custom_typings/react-three-fiber.d.ts +0 -9
  210. package/src/custom_typings/react-three-postprocessing.d.ts +0 -1
  211. package/src/renderers/react/react-camera.tsx +0 -391
  212. package/src/renderers/react/react-dimension.tsx +0 -122
  213. package/src/renderers/react/react-group.test.ts +0 -56
  214. package/src/renderers/react/react-group.tsx +0 -202
  215. package/src/renderers/react/react-image-material.tsx +0 -139
  216. package/src/renderers/react/react-material.tsx +0 -91
  217. package/src/renderers/react/react-mesh.tsx +0 -512
  218. package/src/renderers/react/react-relation.tsx +0 -86
  219. package/src/renderers/react/react-scene.tsx +0 -111
  220. package/src/renderers/react/react-types.ts +0 -9
@@ -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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"}
@@ -0,0 +1,15 @@
1
+ import { type Scene, Mesh, MeshBasicMaterial, PlaneGeometry, type WebGLRenderer, WebGLRenderTarget } from "three";
2
+ import type { BuiltScene } from "../build/build.js";
3
+ /**
4
+ * A soft shadow on the ground the scene stands on, rendered once per build: the solids seen from below by their depth, darker the closer to the
5
+ * ground, blurred into a texture on a plane, so a frame costs one textured quad. three.js's webgl_shadow_contact example, rendered once as drei's
6
+ * ContactShadows with `frames={1}`
7
+ */
8
+ export type GroundShadow = {
9
+ readonly mesh: Mesh<PlaneGeometry, MeshBasicMaterial>;
10
+ readonly target: WebGLRenderTarget;
11
+ };
12
+ /** Undefined for a scene without solids; renders with `renderer`, and leaves its render target as it was */
13
+ export declare function createGroundShadow(renderer: WebGLRenderer, scene: Scene, built: BuiltScene): GroundShadow | undefined;
14
+ export declare function disposeGroundShadow(shadow: GroundShadow): void;
15
+ //# sourceMappingURL=shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../../../src/renderers/three/shadow/shadow.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,KAAK,EAEV,IAAI,EACJ,iBAAiB,EAGjB,aAAa,EAIb,KAAK,aAAa,EAClB,iBAAiB,EAClB,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;GAIG;AAEH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAYzH,4GAA4G;AAC5G,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAqErH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAK9D"}
@@ -0,0 +1,81 @@
1
+ import { Box3, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, OrthographicCamera, PlaneGeometry, ShaderMaterial, UniformsUtils, Vector3, WebGLRenderTarget, } from "three";
2
+ import { FullScreenQuad } from "three/addons/postprocessing/Pass.js";
3
+ import { HorizontalBlurShader } from "three/addons/shaders/HorizontalBlurShader.js";
4
+ import { VerticalBlurShader } from "three/addons/shaders/VerticalBlurShader.js";
5
+ /** The layer the solids are drawn on for the shadow, so text, lines, images and dimensions don't cast one */
6
+ const shadowLayer = 1;
7
+ /** Of the texture's longer side */
8
+ const resolution = 512;
9
+ /** How far behind (-z) a part's shadow falls per unit of its height, as from a light in front and above */
10
+ const lean = 0.5;
11
+ /** The blur's reach, of the solids' height */
12
+ const softness = 0.15;
13
+ const opacity = 0.8;
14
+ /** Undefined for a scene without solids; renders with `renderer`, and leaves its render target as it was */
15
+ export function createGroundShadow(renderer, scene, built) {
16
+ const bounds = new Box3();
17
+ built.parts.forEach((part) => {
18
+ if (part.kind === "solid" && part.object.parent !== built.dimensions && part.object.boundingBox) {
19
+ part.object.layers.enable(shadowLayer);
20
+ bounds.union(part.object.boundingBox);
21
+ }
22
+ });
23
+ if (bounds.isEmpty()) {
24
+ return undefined;
25
+ }
26
+ const size = bounds.getSize(new Vector3());
27
+ // The plane reaches past the solids by twice the blur's reach, and behind them as far as their tops' shadows fall
28
+ const [reach, behind] = [softness * size.y, lean * size.y];
29
+ const [width, depth] = [size.x + reach * 4, size.z + reach * 4 + behind];
30
+ const center = new Vector3((bounds.min.x + bounds.max.x) / 2, bounds.min.y, (bounds.min.z + bounds.max.z - behind) / 2);
31
+ const [texelsX, texelsZ] = width > depth ? [resolution, Math.ceil((resolution * depth) / width)] : [Math.ceil((resolution * width) / depth), resolution];
32
+ const [target, blurred] = [new WebGLRenderTarget(texelsX, texelsZ), new WebGLRenderTarget(texelsX, texelsZ)];
33
+ // Looks up from the ground, seeing as far as the solids are high; the shear moves what's at height h back by lean·h. Up is +z on its screen, and
34
+ // a height h is -h in its view space
35
+ const camera = new OrthographicCamera(-width / 2, width / 2, depth / 2, -depth / 2, 0, size.y);
36
+ camera.position.copy(center);
37
+ camera.rotation.x = Math.PI / 2;
38
+ camera.layers.set(shadowLayer);
39
+ camera.updateMatrixWorld();
40
+ camera.projectionMatrix.multiply(new Matrix4().set(1, 0, 0, 0, 0, 1, lean, 0, 0, 0, 1, 0, 0, 0, 0, 1));
41
+ // Depth tested, so at each point the surface closest to the ground darkens it, not the last one drawn
42
+ const darkness = new MeshDepthMaterial();
43
+ darkness.onBeforeCompile = (shader) => {
44
+ shader.fragmentShader = shader.fragmentShader.replace("gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );", "gl_FragColor = vec4( vec3( 0.0 ), 1.0 - fragCoordZ );");
45
+ };
46
+ const previous = renderer.getRenderTarget();
47
+ Object.assign(scene, { overrideMaterial: darkness });
48
+ renderer.setRenderTarget(target);
49
+ renderer.render(scene, camera);
50
+ Object.assign(scene, { overrideMaterial: null });
51
+ const horizontal = new ShaderMaterial({ ...HorizontalBlurShader, uniforms: UniformsUtils.clone(HorizontalBlurShader.uniforms) });
52
+ const vertical = new ShaderMaterial({ ...VerticalBlurShader, uniforms: UniformsUtils.clone(VerticalBlurShader.uniforms) });
53
+ const quad = new FullScreenQuad(horizontal);
54
+ // The blur shaders reach 4 steps; three passes of shrinking steps blend into one smooth falloff
55
+ const step = (reach * texelsX) / width / 4;
56
+ [step, step / 2, step / 4].forEach((amount) => {
57
+ Object.assign(horizontal.uniforms, { tDiffuse: { value: target.texture }, h: { value: amount / texelsX } });
58
+ quad.material = horizontal;
59
+ renderer.setRenderTarget(blurred);
60
+ quad.render(renderer);
61
+ Object.assign(vertical.uniforms, { tDiffuse: { value: blurred.texture }, v: { value: amount / texelsZ } });
62
+ quad.material = vertical;
63
+ renderer.setRenderTarget(target);
64
+ quad.render(renderer);
65
+ });
66
+ renderer.setRenderTarget(previous);
67
+ [blurred, darkness, horizontal, vertical, quad].forEach((d) => d.dispose());
68
+ // The plane faces down as the camera looked up, so its texture lines up, and is mirrored to face up
69
+ const mesh = new Mesh(new PlaneGeometry(width, depth).rotateX(Math.PI / 2), new MeshBasicMaterial({ map: target.texture, transparent: true, depthWrite: false, opacity }));
70
+ mesh.position.copy(center);
71
+ mesh.scale.y = -1;
72
+ scene.add(mesh);
73
+ return { mesh, target };
74
+ }
75
+ export function disposeGroundShadow(shadow) {
76
+ shadow.mesh.removeFromParent();
77
+ shadow.mesh.geometry.dispose();
78
+ shadow.mesh.material.dispose();
79
+ shadow.target.dispose();
80
+ }
81
+ //# sourceMappingURL=shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.js","sourceRoot":"","sources":["../../../../src/renderers/three/shadow/shadow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import TinySDF from "@mapbox/tiny-sdf";
2
+ import { DataTexture } from "three";
3
+ /**
4
+ * Text as signed distance field glyphs from the system font, packed into one atlas texture, so all text draws in one call. The glyph atlas is Mapbox
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
+ * Textures and Special Effects", SIGGRAPH 2007)
7
+ */
8
+ export declare const RASTER_SIZE = 40;
9
+ export declare const SDF_BUFFER = 4;
10
+ /** The distance field's value at the glyph's edge, from tiny-sdf's default cutoff of 0.25 */
11
+ export declare const SDF_EDGE = 0.75;
12
+ export type GlyphBitmap = ReturnType<TinySDF["draw"]>;
13
+ export type Rasterize = (char: string) => GlyphBitmap;
14
+ export type Glyph = {
15
+ /** The glyph's place in the atlas as a uv offset and scale, for a quad with uv (0, 0) at its bottom left */
16
+ readonly rect: readonly [number, number, number, number];
17
+ /** The bitmap's left and top edges and size, in em, from the pen at the baseline; no width for glyphs without ink */
18
+ readonly left: number;
19
+ readonly top: number;
20
+ readonly width: number;
21
+ readonly height: number;
22
+ readonly advance: number;
23
+ };
24
+ export type GlyphAtlas = {
25
+ readonly texture: DataTexture;
26
+ readonly size: number;
27
+ readonly glyphs: Map<string, Glyph>;
28
+ /** Where the next glyph goes: the current row's top and next x, and its height */
29
+ readonly shelf: {
30
+ x: number;
31
+ y: number;
32
+ height: number;
33
+ };
34
+ readonly rasterize: Rasterize | undefined;
35
+ readonly sdf: {
36
+ current?: TinySDF;
37
+ };
38
+ };
39
+ /** A quad of a laid out text, with its center and size in the text's units, centered on the origin */
40
+ export type GlyphQuad = {
41
+ readonly glyph: Glyph;
42
+ readonly x: number;
43
+ readonly y: number;
44
+ readonly width: number;
45
+ readonly height: number;
46
+ };
47
+ /** Without `rasterize`, glyphs are rasterized with the OS's built-in Helvetica or Arial, on a canvas created at first use */
48
+ export declare function createGlyphAtlas(rasterize?: Rasterize, size?: number): GlyphAtlas;
49
+ export declare function getGlyph(atlas: GlyphAtlas, char: string): Glyph;
50
+ /**
51
+ * Lays out the text centered on the origin, each line centered, as drei's Text (troika-three-text) did with the r3f renderer's line height and
52
+ * spacing
53
+ */
54
+ export declare function layoutText(atlas: GlyphAtlas, text: string, fontSize: number): ReadonlyArray<GlyphQuad>;
55
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,90 @@
1
+ import TinySDF from "@mapbox/tiny-sdf";
2
+ import { DataTexture, LinearFilter, RedFormat, UnsignedByteType } from "three";
3
+ /**
4
+ * Text as signed distance field glyphs from the system font, packed into one atlas texture, so all text draws in one call. The glyph atlas is Mapbox
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
+ * Textures and Special Effects", SIGGRAPH 2007)
7
+ */
8
+ export const RASTER_SIZE = 40;
9
+ export const SDF_BUFFER = 4;
10
+ /** The distance field's value at the glyph's edge, from tiny-sdf's default cutoff of 0.25 */
11
+ export const SDF_EDGE = 0.75;
12
+ const LINE_HEIGHT = 1.5;
13
+ const LETTER_SPACING = 0.001;
14
+ /** Of the font size, above the baseline, to center a line's glyphs in it */
15
+ const ASCENT = 0.8;
16
+ /** 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) {
18
+ const texture = new DataTexture(new Uint8Array(size * size), size, size, RedFormat, UnsignedByteType);
19
+ Object.assign(texture, { minFilter: LinearFilter, magFilter: LinearFilter, generateMipmaps: false });
20
+ return { texture, size, glyphs: new Map(), shelf: { x: 0, y: 0, height: 0 }, rasterize, sdf: {} };
21
+ }
22
+ export function getGlyph(atlas, char) {
23
+ const cached = atlas.glyphs.get(char);
24
+ if (cached) {
25
+ return cached;
26
+ }
27
+ atlas.sdf.current ??= atlas.rasterize
28
+ ? undefined
29
+ : new TinySDF({ fontSize: RASTER_SIZE, buffer: SDF_BUFFER, radius: 10, fontFamily: "Helvetica, Arial, sans-serif" });
30
+ const bitmap = atlas.rasterize?.(char) ?? atlas.sdf.current.draw(char);
31
+ const rect = bitmap.width > 2 * SDF_BUFFER && bitmap.height > 2 * SDF_BUFFER ? packGlyph(atlas, bitmap) : undefined;
32
+ const glyph = {
33
+ rect: rect ?? [0, 0, 0, 0],
34
+ left: (bitmap.glyphLeft - SDF_BUFFER) / RASTER_SIZE,
35
+ top: (bitmap.glyphTop + SDF_BUFFER) / RASTER_SIZE,
36
+ width: rect ? bitmap.width / RASTER_SIZE : 0,
37
+ height: rect ? bitmap.height / RASTER_SIZE : 0,
38
+ advance: bitmap.glyphAdvance / RASTER_SIZE,
39
+ };
40
+ atlas.glyphs.set(char, glyph);
41
+ return glyph;
42
+ }
43
+ /** Copies the bitmap to the atlas, left to right in rows, or leaves it out when the atlas is full; shelf packing, as @mapbox/shelf-pack does */
44
+ function packGlyph({ texture, size, shelf }, bitmap) {
45
+ if (shelf.x + bitmap.width > size) {
46
+ Object.assign(shelf, { x: 0, y: shelf.y + shelf.height, height: 0 });
47
+ }
48
+ if (shelf.y + bitmap.height > size) {
49
+ return undefined;
50
+ }
51
+ const { x, y } = shelf;
52
+ const data = texture.image.data;
53
+ for (let row = 0; row < bitmap.height; row++) {
54
+ data.set(bitmap.data.subarray(row * bitmap.width, (row + 1) * bitmap.width), (y + row) * size + x);
55
+ }
56
+ Object.assign(shelf, { x: x + bitmap.width, height: Math.max(shelf.height, bitmap.height) });
57
+ texture.needsUpdate = true;
58
+ // Bitmap rows run top down from the texture's v = 0, while the quad's uv runs bottom up
59
+ return [x / size, (y + bitmap.height) / size, bitmap.width / size, -bitmap.height / size];
60
+ }
61
+ /**
62
+ * Lays out the text centered on the origin, each line centered, as drei's Text (troika-three-text) did with the r3f renderer's line height and
63
+ * spacing
64
+ */
65
+ export function layoutText(atlas, text, fontSize) {
66
+ const lineHeight = LINE_HEIGHT * fontSize;
67
+ const lines = text.split("\n");
68
+ return lines.flatMap((line, i) => {
69
+ const glyphs = Array.from(line, (char) => getGlyph(atlas, char));
70
+ const advances = glyphs.map((g) => (g.advance + LETTER_SPACING) * fontSize);
71
+ const baseline = (lines.length * lineHeight) / 2 - i * lineHeight - (lineHeight - fontSize) / 2 - ASCENT * fontSize;
72
+ const pen = { x: -advances.reduce((sum, a) => sum + a, 0) / 2 };
73
+ return glyphs.flatMap((glyph, j) => {
74
+ const x = pen.x;
75
+ pen.x += advances[j];
76
+ return glyph.width === 0
77
+ ? []
78
+ : [
79
+ {
80
+ glyph,
81
+ x: x + (glyph.left + glyph.width / 2) * fontSize,
82
+ y: baseline + (glyph.top - glyph.height / 2) * fontSize,
83
+ width: glyph.width * fontSize,
84
+ height: glyph.height * fontSize,
85
+ },
86
+ ];
87
+ });
88
+ });
89
+ }
90
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/renderers/three/text.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EAKX,OAAO,EAKR,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,8HAA8H;AAE9H,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,OAAO,CAAC;AAO7B,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,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,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B,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,6FAA6F;AAC7F,wBAAgB,0BAA0B,IAAI,SAAS,CAMtD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,GAAE,SAAwC,EAAE,IAAI,SAAO,GAAG,UAAU,CA+C7G;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAuBtH;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CA8CzF"}