abstract-3d 3.1.2 → 4.0.0-beta.0

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 (216) 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 +49 -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 +89 -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 +174 -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 +12 -0
  42. package/lib/renderers/three/build/materials.d.ts.map +1 -0
  43. package/lib/renderers/three/build/materials.js +55 -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 +37 -0
  130. package/lib/renderers/three/quality/quality.d.ts.map +1 -0
  131. package/lib/renderers/three/quality/quality.js +64 -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/text/text.d.ts +55 -0
  142. package/lib/renderers/three/text/text.d.ts.map +1 -0
  143. package/lib/renderers/three/text/text.js +90 -0
  144. package/lib/renderers/three/text/text.js.map +1 -0
  145. package/lib/renderers/three/text/text.test.d.ts +2 -0
  146. package/lib/renderers/three/text/text.test.d.ts.map +1 -0
  147. package/lib/renderers/three/text/text.test.js +70 -0
  148. package/lib/renderers/three/text/text.test.js.map +1 -0
  149. package/lib/renderers/three/text.d.ts +53 -0
  150. package/lib/renderers/three/text.d.ts.map +1 -0
  151. package/lib/renderers/three/text.js +151 -0
  152. package/lib/renderers/three/text.js.map +1 -0
  153. package/lib/renderers/three/viewer/options.d.ts +71 -0
  154. package/lib/renderers/three/viewer/options.d.ts.map +1 -0
  155. package/lib/renderers/three/viewer/options.js +31 -0
  156. package/lib/renderers/three/viewer/options.js.map +1 -0
  157. package/lib/renderers/three/viewer/options.test.d.ts +2 -0
  158. package/lib/renderers/three/viewer/options.test.d.ts.map +1 -0
  159. package/lib/renderers/three/viewer/options.test.js +38 -0
  160. package/lib/renderers/three/viewer/options.test.js.map +1 -0
  161. package/lib/renderers/three/viewer/overlays.d.ts +31 -0
  162. package/lib/renderers/three/viewer/overlays.d.ts.map +1 -0
  163. package/lib/renderers/three/viewer/overlays.js +43 -0
  164. package/lib/renderers/three/viewer/overlays.js.map +1 -0
  165. package/lib/renderers/three/viewer/viewer.d.ts +90 -0
  166. package/lib/renderers/three/viewer/viewer.d.ts.map +1 -0
  167. package/lib/renderers/three/viewer/viewer.js +432 -0
  168. package/lib/renderers/three/viewer/viewer.js.map +1 -0
  169. package/lib/renderers/three/viewer.d.ts +38 -0
  170. package/lib/renderers/three/viewer.d.ts.map +1 -0
  171. package/lib/renderers/three/viewer.js +435 -0
  172. package/lib/renderers/three/viewer.js.map +1 -0
  173. package/package.json +36 -16
  174. package/src/custom_typings/n8ao.d.ts +11 -0
  175. package/src/renderers/dxf/dxf-encoding/dxf-build.ts +1 -2
  176. package/src/renderers/index.ts +1 -0
  177. package/src/renderers/react/index.ts +2 -5
  178. package/src/renderers/react/react.tsx +96 -181
  179. package/src/renderers/three/appearance/appearance.test.ts +116 -0
  180. package/src/renderers/three/appearance/appearance.ts +132 -0
  181. package/src/renderers/three/build/build.test.ts +141 -0
  182. package/src/renderers/three/build/build.ts +270 -0
  183. package/src/renderers/three/build/materials.ts +85 -0
  184. package/src/renderers/three/build/parts.ts +277 -0
  185. package/src/renderers/three/camera/camera.test.ts +53 -0
  186. package/src/renderers/three/camera/camera.ts +82 -0
  187. package/src/renderers/three/camera/controls.ts +65 -0
  188. package/src/renderers/three/camera/gizmo.ts +93 -0
  189. package/src/renderers/three/compile/compile.test.ts +113 -0
  190. package/src/renderers/three/compile/compile.ts +151 -0
  191. package/src/renderers/three/geometry/geometry.test.ts +95 -0
  192. package/src/renderers/three/geometry/geometry.ts +179 -0
  193. package/src/renderers/three/index.ts +15 -0
  194. package/src/renderers/three/quality/quality.test.ts +30 -0
  195. package/src/renderers/three/quality/quality.ts +95 -0
  196. package/src/renderers/three/text/text.test.ts +79 -0
  197. package/src/renderers/three/text/text.ts +128 -0
  198. package/src/renderers/three/viewer/options.ts +98 -0
  199. package/src/renderers/three/viewer/overlays.ts +71 -0
  200. package/src/renderers/three/viewer/viewer.ts +540 -0
  201. package/lib/renderers/react/react-hotspot.d.ts +0 -27
  202. package/lib/renderers/react/react-hotspot.d.ts.map +0 -1
  203. package/lib/renderers/react/react-hotspot.js +0 -37
  204. package/lib/renderers/react/react-hotspot.js.map +0 -1
  205. package/src/custom_typings/react-three-fiber.d.ts +0 -9
  206. package/src/custom_typings/react-three-postprocessing.d.ts +0 -1
  207. package/src/renderers/react/react-camera.tsx +0 -391
  208. package/src/renderers/react/react-dimension.tsx +0 -122
  209. package/src/renderers/react/react-group.test.ts +0 -56
  210. package/src/renderers/react/react-group.tsx +0 -202
  211. package/src/renderers/react/react-image-material.tsx +0 -139
  212. package/src/renderers/react/react-material.tsx +0 -91
  213. package/src/renderers/react/react-mesh.tsx +0 -512
  214. package/src/renderers/react/react-relation.tsx +0 -86
  215. package/src/renderers/react/react-scene.tsx +0 -111
  216. package/src/renderers/react/react-types.ts +0 -9
@@ -1,391 +0,0 @@
1
- import {
2
- type GizmoHelperProps,
3
- PerspectiveCamera,
4
- OrthographicCamera,
5
- type OrbitControlsProps,
6
- OrbitControls,
7
- GizmoHelper,
8
- GizmoViewcube,
9
- GizmoViewport,
10
- } from "@react-three/drei";
11
- import { type ThreeEvent, useThree } from "@react-three/fiber";
12
- import React, { useLayoutEffect, useRef, useState } from "react";
13
- import type { OrthographicCamera as ThreeOrthographicCamera, PerspectiveCamera as ThreePerspectiveCamera } from "three";
14
- import { Vector3 } from "three";
15
- import { exhaustiveCheck } from "ts-exhaustive-check";
16
- import type { View, Scene, Vec3 } from "../../abstract-3d.js";
17
- import { vec3 } from "../../abstract-3d.js";
18
-
19
- export type Camera = A3dPerspectiveCamera | A3dOrthographicCamera;
20
- export type CameraType = Camera["type"];
21
-
22
- export type A3dPerspectiveCamera = {
23
- readonly type: "Perspective";
24
- readonly near?: number;
25
- readonly far?: number;
26
- readonly fov?: number;
27
- };
28
-
29
- export type A3dOrthographicCamera = {
30
- readonly type: "Orthographic";
31
- readonly near?: number;
32
- readonly far?: number;
33
- };
34
-
35
- export type BufferZones = {
36
- readonly left?: number;
37
- readonly right?: number;
38
- readonly top?: number;
39
- readonly bottom?: number;
40
- };
41
-
42
- export type ControlsHelper = (Viewcube | Viewport) & {
43
- readonly props: Pick<GizmoHelperProps, "alignment" | "margin">;
44
- };
45
- type Viewcube = {
46
- readonly type: "Viewcube";
47
- readonly viewcubeProps: GenericProps;
48
- };
49
- type Viewport = {
50
- readonly type: "Viewport";
51
- readonly viewportProps: GizmoViewportProps;
52
- };
53
-
54
- type RefInstance<C> = C extends React.ForwardRefExoticComponent<infer P> ? (P extends React.RefAttributes<infer R> ? R : never) : never;
55
- type OrbitControlsInstance = RefInstance<typeof OrbitControls>;
56
-
57
- export function ReactCamera({
58
- useAnimations: _useAnimations,
59
- camera,
60
- view,
61
- scene,
62
- controlsHelper,
63
- orbitContolsProps,
64
- bufferZones = {},
65
- fitPadding = 0,
66
- }: {
67
- readonly useAnimations: boolean;
68
- readonly camera: Camera;
69
- readonly view: View;
70
- readonly scene: Scene;
71
- readonly controlsHelper?: ControlsHelper;
72
- readonly orbitContolsProps?: OrbitControlsProps;
73
- readonly bufferZones?: BufferZones;
74
- readonly fitPadding?: number;
75
- }): React.JSX.Element {
76
- // oxlint-disable-next-line typescript/no-redundant-type-constituents -- oxlint's type-aware checker resolves OrbitControlsInstance (a conditional type over a forward-ref component) as `any`; tsc resolves it correctly and the `| null` is not actually redundant
77
- const [controls, setControls] = useState<OrbitControlsInstance | null>(null);
78
- const perspectiveRef = useRef<ThreePerspectiveCamera | undefined>(undefined);
79
- const orthographicRef = useRef<ThreeOrthographicCamera | undefined>(undefined);
80
-
81
- const initialTargetRef = useRef(new Vector3());
82
-
83
- const viewPortAspect = useThree(({ viewport: { aspect } }) => aspect);
84
- const canvasSize = useThree(({ size }) => size);
85
- const invalidate = useThree(({ invalidate }) => invalidate);
86
-
87
- // Fits the active camera's frustum to the latest scene/canvas/buffer zones.
88
- // Returns the fitted camera, its fit distance and the view's default direction;
89
- // callers position the camera and call updateProjectionMatrix().
90
- const fitCamera = ():
91
- | {
92
- readonly cam: ThreePerspectiveCamera | ThreeOrthographicCamera;
93
- readonly dist: number;
94
- readonly viewDir: Vector3;
95
- }
96
- | undefined => {
97
- const [posX, posY, posZ, size, sceneAspect] = getViewTransform(view, scene);
98
-
99
- // size.x/y are scene width/height in screen space (remapped per view direction)
100
- const screenW = size.x;
101
- const screenH = size.y;
102
-
103
- const fov = camera.type === "Perspective" ? (camera.fov ?? 45) : 45;
104
-
105
- // Buffer zones in CSS pixels
106
- const bufLeft = bufferZones.left ?? 0;
107
- const bufRight = bufferZones.right ?? 0;
108
- const bufTop = bufferZones.top ?? 0;
109
- const bufBottom = bufferZones.bottom ?? 0;
110
-
111
- // Canvas size in CSS pixels — use R3F's reactive size so the effect
112
- // re-runs on resize and is correct on first mount.
113
- const canvasW = canvasSize.width;
114
- const canvasH = canvasSize.height;
115
-
116
- if (canvasW === 0 || canvasH === 0) {
117
- return undefined;
118
- }
119
-
120
- // Usable area in CSS pixels
121
- const usableW = Math.max(1, canvasW - bufLeft - bufRight);
122
- const usableH = Math.max(1, canvasH - bufTop - bufBottom);
123
- const usableAspect = usableW / usableH;
124
- const viewDir = new Vector3(posX, posY, posZ);
125
-
126
- // ------------------------------------------------------------------
127
- // ORTHOGRAPHIC
128
- // ------------------------------------------------------------------
129
- if (camera.type === "Orthographic" && orthographicRef.current) {
130
- const cam = orthographicRef.current;
131
- const dist = cameraDist(size, fov);
132
-
133
- // Fit scene into the usable area
134
- const padFactor = 1 + fitPadding;
135
- const { sceneHalfW, sceneHalfH } =
136
- sceneAspect > usableAspect
137
- ? // Scene wider than usable area — constrain by width
138
- {
139
- sceneHalfW: (screenW / 2) * padFactor,
140
- sceneHalfH: (screenW / 2 / usableAspect) * padFactor,
141
- }
142
- : // Scene taller — constrain by height
143
- {
144
- sceneHalfH: (screenH / 2) * padFactor,
145
- sceneHalfW: ((usableAspect * screenH) / 2) * padFactor,
146
- };
147
-
148
- // World units per CSS pixel in the usable area
149
- const wpp = (sceneHalfW * 2) / usableW;
150
- const hpp = (sceneHalfH * 2) / usableH;
151
-
152
- // Extend frustum outward by buffer pixel amounts
153
- cam.left = -sceneHalfW - bufLeft * wpp;
154
- cam.right = sceneHalfW + bufRight * wpp;
155
- cam.top = sceneHalfH + bufTop * hpp;
156
- cam.bottom = -sceneHalfH - bufBottom * hpp;
157
- cam.zoom = 1;
158
- return { cam, dist, viewDir };
159
-
160
- // ------------------------------------------------------------------
161
- // PERSPECTIVE
162
- // ------------------------------------------------------------------
163
- } else if (camera.type === "Perspective" && perspectiveRef.current) {
164
- const cam = perspectiveRef.current;
165
- const fovRad = (fov * Math.PI) / 180;
166
-
167
- // Horizontal FOV across just the *usable* area (not the full canvas) —
168
- // this is what the camera's fov/aspect should describe.
169
- const fovHRad = 2 * Math.atan(Math.tan(fovRad / 2) * usableAspect);
170
-
171
- // Fit the scene into the usable area only. No fraction math needed —
172
- // fov/aspect now directly describe the usable area's frustum.
173
- const distForH = screenH / 2 / Math.tan(fovRad / 2) + size.z * 0.5;
174
- const distForW = screenW / 2 / Math.tan(fovHRad / 2) + size.z * 0.5;
175
-
176
- const dist = Math.max(distForH, distForW) * (1 + fitPadding);
177
-
178
- cam.fov = fov;
179
- cam.aspect = usableAspect;
180
- cam.zoom = 1;
181
-
182
- // Slice/extend the rendered frustum from the usable-area-sized window
183
- // out to the full canvas, asymmetrically per buffer side.
184
- cam.setViewOffset(usableW, usableH, -bufLeft, -bufTop, canvasW, canvasH);
185
- return { cam, dist, viewDir };
186
- }
187
- return undefined;
188
- };
189
-
190
- const resetZoomOnGizmoClick = (): void => {
191
- const fit = fitCamera();
192
- if (!controls || !fit) {
193
- return;
194
- }
195
-
196
- const target = initialTargetRef.current.clone();
197
- // oxlint-disable-next-line typescript/no-unsafe-call -- oxlint's type-aware checker resolves OrbitControlsInstance as `any` (see the note on its declaration above); tsc resolves it correctly
198
- controls.target.copy(target);
199
-
200
- // Keep the current direction, the gizmo animates the rotation itself
201
- const dir = fit.cam.position.clone().sub(target).normalize();
202
- fit.cam.position.copy(target.add(dir.multiplyScalar(fit.dist)));
203
- fit.cam.updateProjectionMatrix();
204
- // oxlint-disable-next-line typescript/no-unsafe-call -- oxlint's type-aware checker resolves OrbitControlsInstance as `any` (see the note on its declaration above); tsc resolves it correctly
205
- controls.update();
206
- invalidate();
207
- };
208
-
209
- useLayoutEffect(() => {
210
- const fit = fitCamera();
211
- if (!fit) {
212
- return;
213
- }
214
- fit.cam.position.copy(fit.viewDir.multiplyScalar(fit.dist));
215
- fit.cam.updateProjectionMatrix();
216
- //}, [camera, viewPortAspect, canvasSize, bufferZones, view, scene, fitPadding]);
217
- // oxlint-disable-next-line react-hooks/exhaustive-deps -- deps intentionally trimmed, see b290bc3e;
218
- }, [camera, viewPortAspect]);
219
-
220
- return (
221
- <>
222
- <PerspectiveCamera
223
- ref={perspectiveRef as unknown as React.RefObject<ThreePerspectiveCamera | null>}
224
- near={camera.near}
225
- far={camera.far}
226
- fov={camera.type === "Perspective" ? camera.fov : 75}
227
- aspect={viewPortAspect}
228
- manual={true}
229
- makeDefault={camera.type === "Perspective"}
230
- />
231
- <OrthographicCamera
232
- ref={orthographicRef as unknown as React.RefObject<ThreeOrthographicCamera | null>}
233
- up={[0, 1, 0]}
234
- near={camera.near}
235
- far={camera.far}
236
- manual={true}
237
- makeDefault={camera.type === "Orthographic"}
238
- />
239
-
240
- <ControlsWrapper
241
- {...orbitContolsProps}
242
- setControls={(c) => {
243
- setControls(c);
244
- }}
245
- />
246
-
247
- {(() => {
248
- switch (controlsHelper?.type) {
249
- case "Viewcube":
250
- return (
251
- <GizmoHelper
252
- {...controlsHelper.props}
253
- onTarget={() => {
254
- if (controlsHelper.viewcubeProps.resetZoomAndPanOnClick) {
255
- resetZoomOnGizmoClick();
256
- }
257
- return controls?.target as Vector3;
258
- }}
259
- // oxlint-disable-next-line typescript/no-unsafe-call typescript/no-unsafe-return -- oxlint's type-aware checker resolves OrbitControlsInstance as `any` (see the note on its declaration above); tsc resolves it correctly
260
- onUpdate={() => controls?.update?.()}
261
- >
262
- <GizmoViewcube {...controlsHelper.viewcubeProps} />
263
- </GizmoHelper>
264
- );
265
- case "Viewport":
266
- return (
267
- <GizmoHelper
268
- {...controlsHelper.props}
269
- onTarget={() => {
270
- if (controlsHelper.viewportProps.resetZoomAndPanOnClick) {
271
- resetZoomOnGizmoClick();
272
- }
273
- return controls?.target as Vector3;
274
- }}
275
- // oxlint-disable-next-line typescript/no-unsafe-call typescript/no-unsafe-return -- oxlint's type-aware checker resolves OrbitControlsInstance as `any` (see the note on its declaration above); tsc resolves it correctly
276
- onUpdate={() => controls?.update?.()}
277
- >
278
- <GizmoViewport {...controlsHelper.viewportProps} />
279
- </GizmoHelper>
280
- );
281
- case undefined:
282
- default:
283
- return null;
284
- }
285
- })()}
286
- </>
287
- );
288
- }
289
-
290
- /** OrbitControlsProps resolves to an index signature (drei omits from a type that has one), so its callbacks are typed here */
291
- type ControlsCallbacks = {
292
- readonly onStart?: (e?: unknown) => void;
293
- readonly onChange?: (e?: unknown) => void;
294
- readonly onEnd?: (e?: unknown) => void;
295
- };
296
-
297
- const ControlsWrapper = ({
298
- setControls,
299
- ...props
300
- }: OrbitControlsProps & {
301
- setControls: (controls: OrbitControlsInstance) => void;
302
- }): React.JSX.Element => {
303
- const { onStart, onChange, onEnd } = props as ControlsCallbacks;
304
- const ref = useRef<OrbitControlsInstance>(null);
305
- const getState = useThree(({ get }) => get);
306
- const setEvents = useThree(({ setEvents }) => setEvents);
307
- // While the camera is moved by a drag, the scene's pointer events are paused so every pointer move doesn't raycast the scene and run the hover handlers
308
- const dragRef = useRef({ dragging: false, paused: false });
309
-
310
- useLayoutEffect(() => {
311
- if (!ref.current) {
312
- return;
313
- }
314
- setControls(ref.current);
315
- });
316
- return (
317
- <OrbitControls
318
- {...props}
319
- makeDefault
320
- ref={ref}
321
- onStart={(e?: unknown) => {
322
- dragRef.current.dragging = true;
323
- onStart?.(e);
324
- }}
325
- onChange={(e?: unknown) => {
326
- if (dragRef.current.dragging && !dragRef.current.paused && getState().events.enabled) {
327
- dragRef.current.paused = true;
328
- setEvents({ enabled: false });
329
- }
330
- onChange?.(e);
331
- }}
332
- onEnd={(e?: unknown) => {
333
- if (dragRef.current.paused) {
334
- setEvents({ enabled: true });
335
- }
336
- dragRef.current = { dragging: false, paused: false };
337
- onEnd?.(e);
338
- }}
339
- />
340
- );
341
- };
342
- type GizmoViewportProps = React.JSX.IntrinsicElements["group"] & {
343
- readonly axisColors?: [string, string, string];
344
- readonly axisScale?: [number, number, number];
345
- readonly labels?: [string, string, string];
346
- readonly axisHeadScale?: number;
347
- readonly labelColor?: string;
348
- readonly hideNegativeAxes?: boolean;
349
- readonly hideAxisHeads?: boolean;
350
- readonly disabled?: boolean;
351
- readonly font?: string;
352
- readonly onClick?: (e: ThreeEvent<MouseEvent>) => null;
353
- readonly resetZoomAndPanOnClick?: boolean;
354
- };
355
-
356
- type GenericProps = {
357
- readonly font?: string;
358
- readonly opacity?: number;
359
- readonly color?: string;
360
- readonly hoverColor?: string;
361
- readonly textColor?: string;
362
- readonly strokeColor?: string;
363
- readonly onClick?: (e: ThreeEvent<MouseEvent>) => null;
364
- readonly faces?: Array<string>;
365
- readonly resetZoomAndPanOnClick?: boolean;
366
- };
367
-
368
- export const cameraDist = (size: Vec3, fov: number): number =>
369
- size.z * 0.5 + (size.x > size.y ? size.x : size.y) / (1 / 2 / Math.tan((Math.PI * fov) / 180 / 2));
370
-
371
- type ViewTransform = readonly [number, number, number, Vec3, number];
372
- function getViewTransform(view: View, scene: Scene): ViewTransform {
373
- const size = scene.size_deprecated;
374
-
375
- switch (view) {
376
- case "front":
377
- return [0, 0, 1, size, size.x / size.y] as const;
378
- case "back":
379
- return [0, 0, -1, size, size.x / size.y] as const;
380
- case "top":
381
- return [0, 1, 0, vec3(size.x, size.z, size.y), size.x / size.z] as const;
382
- case "bottom":
383
- return [0, -1, 0, vec3(size.x, size.z, size.y), size.x / size.z] as const;
384
- case "right":
385
- return [1, 0, 0, vec3(size.z, size.y, size.x), size.z / size.y] as const;
386
- case "left":
387
- return [-1, 0, 0, vec3(size.z, size.y, size.x), size.z / size.y] as const;
388
- default:
389
- return exhaustiveCheck(view);
390
- }
391
- }
@@ -1,122 +0,0 @@
1
- import type { ThreeEvent } from "@react-three/fiber";
2
- import React from "react";
3
- import type { Dimensions, Vec3, Dimension, Mesh, Material } from "../../abstract-3d.js";
4
- import { dimensionConvertToTypeMesh, vec3Zero } from "../../abstract-3d.js";
5
- import { ReactMaterial, selectMat } from "./react-material.js";
6
- import { ReactMesh } from "./react-mesh.js";
7
-
8
- type DimensionCallbacks = {
9
- readonly selectedIds?: Record<string, boolean> | undefined;
10
- readonly relationsActive?: boolean;
11
- readonly onClickGroup?: (id: string | undefined, rootData: undefined, data: undefined, e: ThreeEvent<MouseEvent>) => void;
12
- readonly onHoverGroup?: (id: string | undefined, rootData: undefined, data: undefined, e: ThreeEvent<MouseEvent>) => void;
13
- readonly onContextMenuGroup?: (id: string, rootData: undefined, data: undefined, left: number, top: number, e: ThreeEvent<MouseEvent>) => void;
14
- };
15
-
16
- export const ReactDimensions = React.memo(
17
- ({
18
- dimensions,
19
- showDimensions,
20
- sceneRotation,
21
- sceneCenter,
22
- ...callbacks
23
- }: {
24
- readonly dimensions: Dimensions | undefined;
25
- readonly showDimensions: boolean;
26
- readonly sceneRotation: Vec3 | undefined;
27
- readonly sceneCenter: Vec3 | undefined;
28
- } & DimensionCallbacks): React.JSX.Element => {
29
- const material = dimensions?.material;
30
- const dimensionMaterial = React.useMemo(() => (material ? <ReactMaterial isText={true} material={material} /> : <></>), [material]);
31
- return (
32
- <>
33
- {dimensions?.dimensions.map((dimension, i) => (
34
- <ReactDimension
35
- key={i}
36
- dimension={dimension}
37
- material={dimensions.material}
38
- visible={showDimensions}
39
- sceneRotation={sceneRotation}
40
- _sceneCenter={sceneCenter}
41
- {...callbacks}
42
- >
43
- {dimensionMaterial}
44
- </ReactDimension>
45
- ))}
46
- </>
47
- );
48
- }
49
- );
50
-
51
- /** A dimension with an id is clickable like a group when there is an onClickGroup, and drawn in the select color when selected or hovered. */
52
- export function ReactDimension({
53
- dimension,
54
- material,
55
- visible,
56
- children,
57
- sceneRotation,
58
- selectedIds,
59
- relationsActive,
60
- onClickGroup,
61
- onHoverGroup,
62
- onContextMenuGroup,
63
- }: {
64
- readonly dimension: Dimension;
65
- readonly material: Material;
66
- readonly visible: boolean;
67
- readonly children: React.JSX.Element;
68
- readonly sceneRotation: Vec3 | undefined;
69
- readonly _sceneCenter: Vec3 | undefined;
70
- } & DimensionCallbacks): React.JSX.Element {
71
- const [hovered, setHovered] = React.useState<boolean>(false);
72
- const id = relationsActive || !onClickGroup ? undefined : dimension.id;
73
- const highlighted = !!id && (hovered || !!selectedIds?.[id]);
74
- const dim = dimensionConvertToTypeMesh(highlighted ? { ...dimension, material: selectMat } : dimension, sceneRotation ?? vec3Zero, material);
75
- return visible ? (
76
- <group
77
- position={[dim.pos.x, dim.pos.y, dim.pos.z]}
78
- rotation={[dim.rot.x, dim.rot.y, dim.rot.z]}
79
- {...(id && {
80
- onClick: (e) => {
81
- if (onClickGroup) {
82
- e.stopPropagation();
83
- onClickGroup(id, undefined, undefined, e);
84
- }
85
- },
86
- onPointerOver: (e) => {
87
- e.stopPropagation();
88
- document.body.style.cursor = "pointer";
89
- onHoverGroup?.(id, undefined, undefined, e);
90
- setHovered(true);
91
- },
92
- onPointerOut: (e) => {
93
- document.body.style.cursor = "auto";
94
- setHovered(false);
95
- onHoverGroup?.(undefined, undefined, undefined, e);
96
- },
97
- onContextMenu: (e) => {
98
- if (onContextMenuGroup) {
99
- e.stopPropagation();
100
- onContextMenuGroup(id, undefined, undefined, e.nativeEvent.x, e.nativeEvent.y, e);
101
- }
102
- },
103
- })}
104
- >
105
- <DimensionMeshes meshes={dim.meshes}>{highlighted ? <ReactMaterial isText={true} material={selectMat} /> : children}</DimensionMeshes>
106
- </group>
107
- ) : (
108
- <></>
109
- );
110
- }
111
-
112
- const DimensionMeshes = React.memo(
113
- ({ meshes, children }: { readonly meshes: ReadonlyArray<Mesh>; readonly children: React.JSX.Element }): React.JSX.Element => (
114
- <>
115
- {meshes.map((m, i) => (
116
- <ReactMesh key={i} mesh={m}>
117
- {children}
118
- </ReactMesh>
119
- ))}
120
- </>
121
- )
122
- );
@@ -1,56 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { group } from "../../abstract-3d.js";
3
- import { groupPropsEqual, type ReactGroupProps } from "./react-group.js";
4
-
5
- const base: ReactGroupProps = {
6
- g: group([]),
7
- hoveredIdsExternal: undefined,
8
- selectedIds: { a: true },
9
- relationsActive: false,
10
- activeComponents: { a: "Error" },
11
- id: "a",
12
- rootData: undefined,
13
- onClickGroup: () => undefined,
14
- };
15
-
16
- describe("groupPropsEqual", () => {
17
- it("is equal for the same props", () => {
18
- expect(groupPropsEqual(base, { ...base })).toBe(true);
19
- });
20
-
21
- it("ignores selectedIds and activeComponents objects that hold the same values for the group's id", () => {
22
- expect(groupPropsEqual(base, { ...base, selectedIds: { a: true, b: false }, activeComponents: { a: "Error", b: "Warning" } })).toBe(true);
23
- });
24
-
25
- it("is not equal when the group's own selection changes", () => {
26
- expect(groupPropsEqual(base, { ...base, selectedIds: {} })).toBe(false);
27
- expect(groupPropsEqual(base, { ...base, selectedIds: { a: false } })).toBe(false);
28
- });
29
-
30
- it("is not equal when the group's own material state changes", () => {
31
- expect(groupPropsEqual(base, { ...base, activeComponents: { a: "Warning" } })).toBe(false);
32
- expect(groupPropsEqual(base, { ...base, activeComponents: undefined })).toBe(false);
33
- });
34
-
35
- it("is equal when only other groups' selection or material state changes", () => {
36
- expect(groupPropsEqual(base, { ...base, selectedIds: { a: true, b: true }, activeComponents: { a: "Error", b: "Accept" } })).toBe(true);
37
- });
38
-
39
- it("is not equal when another prop changes identity", () => {
40
- expect(groupPropsEqual(base, { ...base, g: group([]) })).toBe(false);
41
- expect(groupPropsEqual(base, { ...base, onClickGroup: () => undefined })).toBe(false);
42
- expect(groupPropsEqual(base, { ...base, relationsActive: true })).toBe(false);
43
- expect(groupPropsEqual(base, { ...base, id: "b" })).toBe(false);
44
- });
45
-
46
- it("is not equal when a prop is added or removed", () => {
47
- expect(groupPropsEqual(base, { ...base, useAlphaTest: true })).toBe(false);
48
- expect(groupPropsEqual({ ...base, useAlphaTest: true }, base)).toBe(false);
49
- });
50
-
51
- it("looks up the next id when the group has none", () => {
52
- const anonymous = { ...base, id: undefined, selectedIds: { "": true }, activeComponents: undefined };
53
- expect(groupPropsEqual(anonymous, { ...anonymous, selectedIds: { "": true, a: true } })).toBe(true);
54
- expect(groupPropsEqual(anonymous, { ...anonymous, selectedIds: {} })).toBe(false);
55
- });
56
- });