@fonsecabarreto/genesis-gl-react 0.1.31 → 0.1.32

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.
@@ -0,0 +1,269 @@
1
+ // src/hooks/useGenesisGL.ts
2
+ import { useRef, useEffect, useState } from "react";
3
+ import {
4
+ WebGLCore as WebGLCoreImpl,
5
+ Scene as SceneImpl,
6
+ Renderer as RendererImpl,
7
+ Viewport as ViewportImpl
8
+ } from "@fonsecabarreto/genesis-gl-core/Core";
9
+ import { loadOBJWithMTL } from "@fonsecabarreto/genesis-gl-core/Core/utils/parse-obj";
10
+ var nullLoadOBJ = () => Promise.reject(new Error("GenesisGL not initialized"));
11
+ function useGenesisGL(options) {
12
+ const [context, setContext] = useState({
13
+ renderer: null,
14
+ scene: null,
15
+ viewport: null,
16
+ webglCore: null,
17
+ loadOBJ: nullLoadOBJ
18
+ });
19
+ const contextRef = useRef(context);
20
+ useEffect(() => {
21
+ const {
22
+ canvasRef,
23
+ onReady,
24
+ onError,
25
+ width,
26
+ height,
27
+ initialYaw,
28
+ initialPitch,
29
+ initialZoom
30
+ } = options;
31
+ if (!canvasRef.current) return;
32
+ try {
33
+ const canvas = canvasRef.current;
34
+ const vpWidth = width ?? window.innerWidth;
35
+ const vpHeight = height ?? window.innerHeight;
36
+ const webglCore = new WebGLCoreImpl(canvas);
37
+ const viewport = new ViewportImpl(canvas, vpWidth, vpHeight, webglCore, {
38
+ pointerLock: false
39
+ });
40
+ const renderer = new RendererImpl(webglCore, viewport);
41
+ const scene = SceneImpl.withDefaultLights(webglCore);
42
+ viewport.camera.target = [0, 0, 0];
43
+ viewport.camera.position = [4, 2, 8];
44
+ if (initialYaw !== void 0) viewport.camera.yaw = initialYaw;
45
+ if (initialPitch !== void 0) viewport.camera.pitch = initialPitch;
46
+ if (initialZoom !== void 0) viewport.camera.setZoom(initialZoom);
47
+ viewport.camera.invalidate();
48
+ const loadOBJ = (objUrl, opts = {}) => loadOBJWithMTL(
49
+ webglCore,
50
+ objUrl,
51
+ opts.mtlUrl ?? null,
52
+ opts.translation,
53
+ opts.scale
54
+ );
55
+ const ctx = {
56
+ renderer,
57
+ scene,
58
+ viewport,
59
+ webglCore,
60
+ loadOBJ
61
+ };
62
+ contextRef.current = ctx;
63
+ setContext(ctx);
64
+ if (onReady) onReady(ctx);
65
+ } catch (error) {
66
+ const err = error instanceof Error ? error : new Error(String(error));
67
+ console.error("Failed to initialize GenesisGL:", err);
68
+ if (onError) onError(err);
69
+ }
70
+ return () => {
71
+ const { scene, viewport, webglCore } = contextRef.current;
72
+ if (scene && webglCore) scene.dispose(webglCore);
73
+ if (viewport) viewport.dispose();
74
+ if (webglCore) webglCore.dispose();
75
+ };
76
+ }, []);
77
+ return context;
78
+ }
79
+
80
+ // src/hooks/useRenderer.ts
81
+ import { useRef as useRef2, useEffect as useEffect2 } from "react";
82
+ function useRenderer(options) {
83
+ const { renderer, onFrame, enabled = true } = options;
84
+ const animationIdRef = useRef2(null);
85
+ useEffect2(() => {
86
+ if (!renderer || !enabled) return;
87
+ const animate = (time) => {
88
+ if (onFrame) onFrame(time);
89
+ animationIdRef.current = requestAnimationFrame(animate);
90
+ };
91
+ animationIdRef.current = requestAnimationFrame(animate);
92
+ return () => {
93
+ if (animationIdRef.current !== null) {
94
+ cancelAnimationFrame(animationIdRef.current);
95
+ }
96
+ };
97
+ }, [renderer, onFrame, enabled]);
98
+ }
99
+
100
+ // src/hooks/useWorldToScreen.ts
101
+ import { useState as useState2, useCallback } from "react";
102
+ function mat4TransformVec4(m, x, y, z, w) {
103
+ return {
104
+ x: m[0] * x + m[4] * y + m[8] * z + m[12] * w,
105
+ y: m[1] * x + m[5] * y + m[9] * z + m[13] * w,
106
+ z: m[2] * x + m[6] * y + m[10] * z + m[14] * w,
107
+ w: m[3] * x + m[7] * y + m[11] * z + m[15] * w
108
+ };
109
+ }
110
+ function useWorldToScreen(context) {
111
+ const [, forceUpdate] = useState2(0);
112
+ const project = useCallback(
113
+ (worldX, worldY, worldZ) => {
114
+ if (!context?.viewport) return { x: 0, y: 0, visible: false };
115
+ const { viewport } = context;
116
+ const camera = viewport.camera;
117
+ const canvas = viewport.getCanvas();
118
+ const view = camera.getViewMatrix();
119
+ const proj = camera.getProjectionMatrix();
120
+ const v = mat4TransformVec4(view, worldX, worldY, worldZ, 1);
121
+ const c = mat4TransformVec4(proj, v.x, v.y, v.z, v.w);
122
+ if (Math.abs(c.w) < 1e-6) return { x: 0, y: 0, visible: false };
123
+ const ndcX = c.x / c.w;
124
+ const ndcY = c.y / c.w;
125
+ const ndcZ = c.z / c.w;
126
+ const visible = ndcZ > -1 && ndcZ < 1 && Math.abs(ndcX) < 1.2 && Math.abs(ndcY) < 1.2;
127
+ return {
128
+ x: (ndcX + 1) * 0.5 * canvas.clientWidth,
129
+ y: (1 - ndcY) * 0.5 * canvas.clientHeight,
130
+ visible
131
+ };
132
+ },
133
+ [context]
134
+ );
135
+ const tick = useCallback(() => forceUpdate((n) => n + 1), []);
136
+ return { project, tick };
137
+ }
138
+
139
+ // src/hooks/useCameraControls.ts
140
+ import { useCallback as useCallback2, useState as useState3 } from "react";
141
+ var DEFAULT_YAW = 0;
142
+ var DEFAULT_PITCH = 0;
143
+ var DEFAULT_ZOOM = 1;
144
+ function useCameraControls(ctx) {
145
+ const [camera, setCameraState] = useState3({
146
+ yaw: DEFAULT_YAW,
147
+ pitch: DEFAULT_PITCH,
148
+ zoom: DEFAULT_ZOOM
149
+ });
150
+ const sync = useCallback2((c) => {
151
+ setCameraState({ yaw: c.yaw, pitch: c.pitch, zoom: c.zoom });
152
+ }, []);
153
+ const rotate = useCallback2(
154
+ (deltaYaw, deltaPitch) => {
155
+ const c = ctx?.viewport?.camera;
156
+ if (!c) return;
157
+ c.rotate(deltaYaw, deltaPitch);
158
+ sync(c);
159
+ },
160
+ [ctx, sync]
161
+ );
162
+ const zoomBy = useCallback2(
163
+ (delta) => {
164
+ const c = ctx?.viewport?.camera;
165
+ if (!c) return;
166
+ c.zoomBy(delta);
167
+ sync(c);
168
+ },
169
+ [ctx, sync]
170
+ );
171
+ const setZoom = useCallback2(
172
+ (zoom) => {
173
+ const c = ctx?.viewport?.camera;
174
+ if (!c) return;
175
+ c.setZoom(zoom);
176
+ sync(c);
177
+ },
178
+ [ctx, sync]
179
+ );
180
+ const setYaw = useCallback2(
181
+ (yaw) => {
182
+ const c = ctx?.viewport?.camera;
183
+ if (!c) return;
184
+ c.yaw = yaw;
185
+ c.invalidate();
186
+ sync(c);
187
+ },
188
+ [ctx, sync]
189
+ );
190
+ const setPitch = useCallback2(
191
+ (pitch) => {
192
+ const c = ctx?.viewport?.camera;
193
+ if (!c) return;
194
+ c.pitch = pitch;
195
+ c.invalidate();
196
+ sync(c);
197
+ },
198
+ [ctx, sync]
199
+ );
200
+ const reset = useCallback2(() => {
201
+ const c = ctx?.viewport?.camera;
202
+ if (!c) return;
203
+ c.yaw = DEFAULT_YAW;
204
+ c.pitch = DEFAULT_PITCH;
205
+ c.invalidate();
206
+ c.setZoom(DEFAULT_ZOOM);
207
+ sync(c);
208
+ }, [ctx, sync]);
209
+ return { camera, rotate, setYaw, setPitch, zoomBy, setZoom, reset };
210
+ }
211
+
212
+ // src/hooks/useCameraMouseDrag.ts
213
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
214
+ import { MouseDragControl } from "@fonsecabarreto/genesis-gl-core/Core";
215
+ var SENSITIVITY = 8e-3;
216
+ function useCameraMouseDrag(ctx, canvasRef) {
217
+ const { rotate } = useCameraControls(ctx);
218
+ const rotateRef = useRef3(rotate);
219
+ rotateRef.current = rotate;
220
+ useEffect3(() => {
221
+ const el = canvasRef.current;
222
+ if (!el || !ctx) return;
223
+ const drag = new MouseDragControl(el);
224
+ const onDrag = (dx, dy, button) => {
225
+ if (button !== 2) return;
226
+ rotateRef.current(-dx * SENSITIVITY, dy * SENSITIVITY);
227
+ };
228
+ drag.onChange(onDrag);
229
+ drag.enable();
230
+ const onMouseDown = (e) => {
231
+ if (e.button !== 2) return;
232
+ el.style.cursor = "grabbing";
233
+ };
234
+ const onMouseUp = (e) => {
235
+ if (e.button !== 2) return;
236
+ el.style.cursor = "grab";
237
+ };
238
+ const onMouseEnter = () => {
239
+ el.style.cursor = "grab";
240
+ };
241
+ const onMouseLeave = () => {
242
+ el.style.cursor = "";
243
+ };
244
+ const onContextMenu = (e) => e.preventDefault();
245
+ el.addEventListener("mousedown", onMouseDown);
246
+ el.addEventListener("mouseenter", onMouseEnter);
247
+ el.addEventListener("mouseleave", onMouseLeave);
248
+ window.addEventListener("mouseup", onMouseUp);
249
+ el.addEventListener("contextmenu", onContextMenu);
250
+ return () => {
251
+ drag.disable();
252
+ el.removeEventListener("mousedown", onMouseDown);
253
+ el.removeEventListener("mouseenter", onMouseEnter);
254
+ el.removeEventListener("mouseleave", onMouseLeave);
255
+ window.removeEventListener("mouseup", onMouseUp);
256
+ el.removeEventListener("contextmenu", onContextMenu);
257
+ el.style.cursor = "";
258
+ };
259
+ }, [ctx]);
260
+ }
261
+
262
+ export {
263
+ useGenesisGL,
264
+ useRenderer,
265
+ useWorldToScreen,
266
+ useCameraControls,
267
+ useCameraMouseDrag
268
+ };
269
+ //# sourceMappingURL=chunk-MAGVIIUT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useGenesisGL.ts","../src/hooks/useRenderer.ts","../src/hooks/useWorldToScreen.ts","../src/hooks/useCameraControls.ts","../src/hooks/useCameraMouseDrag.ts"],"sourcesContent":["import { useRef, useEffect, useState } from 'react';\nimport {\n WebGLCore,\n WebGLCore as WebGLCoreImpl,\n Scene,\n Scene as SceneImpl,\n Renderer,\n Renderer as RendererImpl,\n Viewport,\n Viewport as ViewportImpl,\n type Model,\n} from '@fonsecabarreto/genesis-gl-core/Core';\nimport { loadOBJWithMTL } from '@fonsecabarreto/genesis-gl-core/Core/utils/parse-obj';\nexport interface LoadOBJOptions {\n mtlUrl?: string | null;\n translation?: [number, number, number];\n scale?: [number, number, number];\n}\n\nexport interface GenesisGLContext {\n renderer: Renderer | null;\n scene: Scene | null;\n viewport: Viewport | null;\n webglCore: WebGLCore | null;\n loadOBJ: (objUrl: string, options?: LoadOBJOptions) => Promise<Model>;\n}\n\nexport interface UseGenesisGLOptions {\n canvasRef: React.RefObject<HTMLCanvasElement>;\n width?: number;\n height?: number;\n initialYaw?: number;\n initialPitch?: number;\n initialZoom?: number;\n onReady?: (context: GenesisGLContext) => void;\n onError?: (error: Error) => void;\n}\n\nconst nullLoadOBJ = (): Promise<never> =>\n Promise.reject(new Error('GenesisGL not initialized'));\n\nexport function useGenesisGL(options: UseGenesisGLOptions): GenesisGLContext {\n const [context, setContext] = useState<GenesisGLContext>({\n renderer: null,\n scene: null,\n viewport: null,\n webglCore: null,\n loadOBJ: nullLoadOBJ,\n });\n\n const contextRef = useRef<GenesisGLContext>(context);\n\n useEffect(() => {\n const {\n canvasRef,\n onReady,\n onError,\n width,\n height,\n initialYaw,\n initialPitch,\n initialZoom,\n } = options;\n if (!canvasRef.current) return;\n\n try {\n const canvas = canvasRef.current;\n const vpWidth = width ?? window.innerWidth;\n const vpHeight = height ?? window.innerHeight;\n\n const webglCore = new WebGLCoreImpl(canvas);\n const viewport = new ViewportImpl(canvas, vpWidth, vpHeight, webglCore, {\n pointerLock: false,\n });\n const renderer = new RendererImpl(webglCore, viewport);\n const scene = SceneImpl.withDefaultLights(webglCore);\n\n viewport.camera.target = [0, 0, 0];\n viewport.camera.position = [4, 2, 8];\n if (initialYaw !== undefined) viewport.camera.yaw = initialYaw;\n if (initialPitch !== undefined) viewport.camera.pitch = initialPitch;\n if (initialZoom !== undefined) viewport.camera.setZoom(initialZoom);\n viewport.camera.invalidate();\n\n const loadOBJ = (objUrl: string, opts: LoadOBJOptions = {}) =>\n loadOBJWithMTL(\n webglCore,\n objUrl,\n opts.mtlUrl ?? null,\n opts.translation,\n opts.scale,\n );\n\n const ctx: GenesisGLContext = {\n renderer,\n scene,\n viewport,\n webglCore,\n loadOBJ,\n };\n contextRef.current = ctx;\n setContext(ctx);\n\n if (onReady) onReady(ctx);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n console.error('Failed to initialize GenesisGL:', err);\n if (onError) onError(err);\n }\n\n return () => {\n const { scene, viewport, webglCore } = contextRef.current;\n if (scene && webglCore) scene.dispose(webglCore);\n if (viewport) viewport.dispose();\n if (webglCore) webglCore.dispose();\n };\n }, []);\n\n return context;\n}\n","import { useRef, useEffect } from 'react';\nimport type { Renderer } from '@fonsecabarreto/genesis-gl-core/Core';\n\nexport interface UseRendererOptions {\n renderer: Renderer | null;\n onFrame?: (time: number) => void;\n enabled?: boolean;\n}\n\n/**\n * Hook to manage the render loop with requestAnimationFrame.\n * Handles starting/stopping animation and cleanup.\n */\nexport function useRenderer(options: UseRendererOptions): void {\n const { renderer, onFrame, enabled = true } = options;\n const animationIdRef = useRef<number | null>(null);\n\n useEffect(() => {\n if (!renderer || !enabled) return;\n\n const animate = (time: number) => {\n if (onFrame) onFrame(time);\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n return () => {\n if (animationIdRef.current !== null) {\n cancelAnimationFrame(animationIdRef.current);\n }\n };\n }, [renderer, onFrame, enabled]);\n}\n","import { useState, useCallback } from 'react';\nimport type { GenesisGLContext } from './useGenesisGL';\n\nexport interface ScreenPoint {\n x: number;\n y: number;\n visible: boolean;\n}\n\nfunction mat4TransformVec4(m: Float32Array, x: number, y: number, z: number, w: number) {\n return {\n x: m[0] * x + m[4] * y + m[8] * z + m[12] * w,\n y: m[1] * x + m[5] * y + m[9] * z + m[13] * w,\n z: m[2] * x + m[6] * y + m[10] * z + m[14] * w,\n w: m[3] * x + m[7] * y + m[11] * z + m[15] * w,\n };\n}\n\nexport function useWorldToScreen(context: GenesisGLContext | null) {\n const [, forceUpdate] = useState(0);\n\n const project = useCallback(\n (worldX: number, worldY: number, worldZ: number): ScreenPoint => {\n if (!context?.viewport) return { x: 0, y: 0, visible: false };\n\n const { viewport } = context;\n const camera = viewport.camera;\n const canvas = viewport.getCanvas();\n\n const view = camera.getViewMatrix() as unknown as Float32Array;\n const proj = camera.getProjectionMatrix() as unknown as Float32Array;\n\n // view * worldPos\n const v = mat4TransformVec4(view, worldX, worldY, worldZ, 1);\n // proj * (view * worldPos)\n const c = mat4TransformVec4(proj, v.x, v.y, v.z, v.w);\n\n if (Math.abs(c.w) < 1e-6) return { x: 0, y: 0, visible: false };\n\n const ndcX = c.x / c.w;\n const ndcY = c.y / c.w;\n const ndcZ = c.z / c.w;\n\n const visible = ndcZ > -1 && ndcZ < 1 && Math.abs(ndcX) < 1.2 && Math.abs(ndcY) < 1.2;\n\n return {\n x: ((ndcX + 1) * 0.5) * canvas.clientWidth,\n y: ((1 - ndcY) * 0.5) * canvas.clientHeight,\n visible,\n };\n },\n [context],\n );\n\n // expose a tick function callers can plug into onFrame to re-render\n const tick = useCallback(() => forceUpdate((n) => n + 1), []);\n\n return { project, tick };\n}\n","import { useCallback, useState } from 'react';\nimport type { GenesisGLContext } from './useGenesisGL';\n\nexport interface CameraState {\n yaw: number;\n pitch: number;\n zoom: number;\n}\n\nexport interface UseCameraControlsResult {\n camera: CameraState;\n rotate: (deltaYaw: number, deltaPitch: number) => void;\n setYaw: (yaw: number) => void;\n setPitch: (pitch: number) => void;\n zoomBy: (delta: number) => void;\n setZoom: (zoom: number) => void;\n reset: () => void;\n}\n\nconst DEFAULT_YAW = 0;\nconst DEFAULT_PITCH = 0;\nconst DEFAULT_ZOOM = 1;\n\nexport function useCameraControls(\n ctx: GenesisGLContext | null,\n): UseCameraControlsResult {\n const [camera, setCameraState] = useState<CameraState>({\n yaw: DEFAULT_YAW,\n pitch: DEFAULT_PITCH,\n zoom: DEFAULT_ZOOM,\n });\n\n const sync = useCallback((c: { yaw: number; pitch: number; zoom: number }) => {\n setCameraState({ yaw: c.yaw, pitch: c.pitch, zoom: c.zoom });\n }, []);\n\n const rotate = useCallback(\n (deltaYaw: number, deltaPitch: number) => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.rotate(deltaYaw, deltaPitch);\n sync(c);\n },\n [ctx, sync],\n );\n\n const zoomBy = useCallback(\n (delta: number) => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.zoomBy(delta);\n sync(c);\n },\n [ctx, sync],\n );\n\n const setZoom = useCallback(\n (zoom: number) => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.setZoom(zoom);\n sync(c);\n },\n [ctx, sync],\n );\n\n const setYaw = useCallback(\n (yaw: number) => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.yaw = yaw;\n c.invalidate();\n sync(c);\n },\n [ctx, sync],\n );\n\n const setPitch = useCallback(\n (pitch: number) => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.pitch = pitch;\n c.invalidate();\n sync(c);\n },\n [ctx, sync],\n );\n\n const reset = useCallback(() => {\n const c = ctx?.viewport?.camera;\n if (!c) return;\n c.yaw = DEFAULT_YAW;\n c.pitch = DEFAULT_PITCH;\n c.invalidate();\n c.setZoom(DEFAULT_ZOOM);\n sync(c);\n }, [ctx, sync]);\n\n return { camera, rotate, setYaw, setPitch, zoomBy, setZoom, reset };\n}\n","import { useEffect, useRef } from 'react';\nimport { MouseDragControl } from '@fonsecabarreto/genesis-gl-core/Core';\nimport { useCameraControls } from './useCameraControls';\nimport type { GenesisGLContext } from './useGenesisGL';\n\nconst SENSITIVITY = 0.008;\n\nexport function useCameraMouseDrag(\n ctx: GenesisGLContext | null,\n canvasRef: React.RefObject<HTMLElement | null>,\n) {\n const { rotate } = useCameraControls(ctx);\n const rotateRef = useRef(rotate);\n rotateRef.current = rotate;\n\n useEffect(() => {\n const el = canvasRef.current;\n if (!el || !ctx) return;\n\n const drag = new MouseDragControl(el);\n\n const onDrag = (dx: number, dy: number, button: number) => {\n if (button !== 2) return;\n rotateRef.current(-dx * SENSITIVITY, dy * SENSITIVITY);\n };\n\n drag.onChange(onDrag);\n drag.enable();\n\n const onMouseDown = (e: MouseEvent) => {\n if (e.button !== 2) return;\n el.style.cursor = 'grabbing';\n };\n\n const onMouseUp = (e: MouseEvent) => {\n if (e.button !== 2) return;\n el.style.cursor = 'grab';\n };\n\n const onMouseEnter = () => {\n el.style.cursor = 'grab';\n };\n\n const onMouseLeave = () => {\n el.style.cursor = '';\n };\n\n const onContextMenu = (e: Event) => e.preventDefault();\n\n el.addEventListener('mousedown', onMouseDown);\n el.addEventListener('mouseenter', onMouseEnter);\n el.addEventListener('mouseleave', onMouseLeave);\n window.addEventListener('mouseup', onMouseUp);\n el.addEventListener('contextmenu', onContextMenu);\n\n return () => {\n drag.disable();\n el.removeEventListener('mousedown', onMouseDown);\n el.removeEventListener('mouseenter', onMouseEnter);\n el.removeEventListener('mouseleave', onMouseLeave);\n window.removeEventListener('mouseup', onMouseUp);\n el.removeEventListener('contextmenu', onContextMenu);\n el.style.cursor = '';\n };\n }, [ctx]);\n}\n"],"mappings":"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","names":["useRef","useEffect","useState","useCallback","useState","useEffect","useRef","useRef","useEffect"]}
@@ -0,0 +1,394 @@
1
+ import {
2
+ useCameraControls,
3
+ useCameraMouseDrag,
4
+ useGenesisGL,
5
+ useRenderer,
6
+ useWorldToScreen
7
+ } from "./chunk-MAGVIIUT.js";
8
+
9
+ // src/components/GenesisGLCanvas.tsx
10
+ import React, { useRef, useCallback } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var GenesisGLCanvas = React.forwardRef(
13
+ ({
14
+ onReady,
15
+ onFrame,
16
+ width,
17
+ height,
18
+ initialYaw,
19
+ initialPitch,
20
+ initialZoom,
21
+ style,
22
+ className
23
+ }, ref) => {
24
+ const canvasRef = useRef(null);
25
+ const setRef = useCallback(
26
+ (el) => {
27
+ canvasRef.current = el;
28
+ if (typeof ref === "function") ref(el);
29
+ else if (ref) ref.current = el;
30
+ },
31
+ [ref]
32
+ );
33
+ const onFrameRef = useRef(onFrame);
34
+ onFrameRef.current = onFrame;
35
+ const genesisContext = useGenesisGL({
36
+ canvasRef,
37
+ width,
38
+ height,
39
+ initialYaw,
40
+ initialPitch,
41
+ initialZoom,
42
+ onReady
43
+ });
44
+ const genesisContextRef = useRef(genesisContext);
45
+ genesisContextRef.current = genesisContext;
46
+ const stableOnFrame = useCallback((time) => {
47
+ const { renderer, scene } = genesisContextRef.current;
48
+ if (renderer && scene) {
49
+ renderer.render(scene);
50
+ onFrameRef.current?.(time);
51
+ }
52
+ }, []);
53
+ useRenderer({ renderer: genesisContext.renderer, onFrame: stableOnFrame });
54
+ useCameraMouseDrag(genesisContext, canvasRef);
55
+ return /* @__PURE__ */ jsx(
56
+ "canvas",
57
+ {
58
+ ref: setRef,
59
+ style: { display: "block", width: "100%", height: "100%", ...style },
60
+ className
61
+ }
62
+ );
63
+ }
64
+ );
65
+ GenesisGLCanvas.displayName = "GenesisGLCanvas";
66
+
67
+ // src/components/RotationControls.tsx
68
+ import { useState } from "react";
69
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
70
+ var STEP = 0.05;
71
+ function IconBtn({
72
+ children,
73
+ title,
74
+ onClick,
75
+ color
76
+ }) {
77
+ const [active, setActive] = useState(false);
78
+ const [hover, setHover] = useState(false);
79
+ return /* @__PURE__ */ jsx2(
80
+ "button",
81
+ {
82
+ title,
83
+ onClick,
84
+ onMouseEnter: () => setHover(true),
85
+ onMouseLeave: () => {
86
+ setHover(false);
87
+ setActive(false);
88
+ },
89
+ onMouseDown: () => setActive(true),
90
+ onMouseUp: () => setActive(false),
91
+ style: {
92
+ display: "flex",
93
+ alignItems: "center",
94
+ justifyContent: "center",
95
+ width: "22px",
96
+ height: "22px",
97
+ border: `1px solid ${hover ? color + "88" : color + "33"}`,
98
+ borderRadius: "5px",
99
+ background: active ? color + "30" : hover ? color + "18" : color + "0c",
100
+ color: hover ? color : color + "aa",
101
+ cursor: "pointer",
102
+ fontSize: "11px",
103
+ fontWeight: 700,
104
+ lineHeight: 1,
105
+ padding: 0,
106
+ transition: "all 0.12s",
107
+ transform: active ? "scale(0.88)" : "scale(1)",
108
+ flexShrink: 0
109
+ },
110
+ children
111
+ }
112
+ );
113
+ }
114
+ function AxisPill({
115
+ label,
116
+ color,
117
+ value,
118
+ onDec,
119
+ onInc,
120
+ onReset
121
+ }) {
122
+ const deg = (value * 180 / Math.PI).toFixed(1);
123
+ const normalized = (value % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2);
124
+ const pct = normalized / (Math.PI * 2) * 100;
125
+ return /* @__PURE__ */ jsxs(
126
+ "div",
127
+ {
128
+ style: {
129
+ display: "flex",
130
+ alignItems: "center",
131
+ gap: "5px",
132
+ background: color + "0a",
133
+ border: `1px solid ${color}22`,
134
+ borderRadius: "8px",
135
+ padding: "5px 7px",
136
+ flex: 1,
137
+ minWidth: 0
138
+ },
139
+ children: [
140
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "18px", height: "18px", flexShrink: 0 }, children: [
141
+ /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", children: [
142
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "7", fill: "none", stroke: color + "22", strokeWidth: "2" }),
143
+ /* @__PURE__ */ jsx2(
144
+ "circle",
145
+ {
146
+ cx: "9",
147
+ cy: "9",
148
+ r: "7",
149
+ fill: "none",
150
+ stroke: color,
151
+ strokeWidth: "2",
152
+ strokeDasharray: `${pct / 100 * 44} 44`,
153
+ strokeLinecap: "round",
154
+ transform: "rotate(-90 9 9)",
155
+ style: { transition: "stroke-dasharray 0.1s ease" }
156
+ }
157
+ )
158
+ ] }),
159
+ /* @__PURE__ */ jsx2(
160
+ "span",
161
+ {
162
+ style: {
163
+ position: "absolute",
164
+ inset: 0,
165
+ display: "flex",
166
+ alignItems: "center",
167
+ justifyContent: "center",
168
+ fontSize: "7px",
169
+ fontWeight: 800,
170
+ color,
171
+ letterSpacing: "-0.02em"
172
+ },
173
+ children: label
174
+ }
175
+ )
176
+ ] }),
177
+ /* @__PURE__ */ jsxs(
178
+ "span",
179
+ {
180
+ style: {
181
+ fontSize: "10px",
182
+ color: color + "cc",
183
+ fontVariantNumeric: "tabular-nums",
184
+ width: "36px",
185
+ textAlign: "right",
186
+ flexShrink: 0
187
+ },
188
+ children: [
189
+ deg,
190
+ "\xB0"
191
+ ]
192
+ }
193
+ ),
194
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "3px", marginLeft: "2px" }, children: [
195
+ /* @__PURE__ */ jsx2(IconBtn, { color, title: `\u2212${label}`, onClick: onDec, children: "\u2212" }),
196
+ /* @__PURE__ */ jsx2(IconBtn, { color, title: `+${label}`, onClick: onInc, children: "+" }),
197
+ /* @__PURE__ */ jsx2(IconBtn, { color: color + "88", title: "Reset", onClick: onReset, children: "\u21BA" })
198
+ ] })
199
+ ]
200
+ }
201
+ );
202
+ }
203
+ function RotationControls({ context, style }) {
204
+ const { camera, rotate, setYaw, setPitch, reset } = useCameraControls(context);
205
+ return /* @__PURE__ */ jsxs(
206
+ "div",
207
+ {
208
+ style: {
209
+ display: "flex",
210
+ alignItems: "center",
211
+ gap: "6px",
212
+ background: "rgba(8,8,12,0.78)",
213
+ border: "1px solid rgba(255,255,255,0.07)",
214
+ borderRadius: "12px",
215
+ padding: "6px 8px",
216
+ backdropFilter: "blur(16px)",
217
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
218
+ fontFamily: '"Inter", system-ui, monospace',
219
+ ...style
220
+ },
221
+ children: [
222
+ /* @__PURE__ */ jsx2(
223
+ "div",
224
+ {
225
+ title: context ? "Camera ready" : "Waiting for context\u2026",
226
+ style: {
227
+ width: "6px",
228
+ height: "6px",
229
+ borderRadius: "50%",
230
+ background: context ? "#c3e88d" : "#555",
231
+ boxShadow: context ? "0 0 6px #c3e88daa" : "none",
232
+ flexShrink: 0,
233
+ transition: "all 0.3s"
234
+ }
235
+ }
236
+ ),
237
+ /* @__PURE__ */ jsx2(
238
+ AxisPill,
239
+ {
240
+ label: "Yaw",
241
+ color: "#ff5370",
242
+ value: camera.yaw,
243
+ onDec: () => rotate(-STEP, 0),
244
+ onInc: () => rotate(STEP, 0),
245
+ onReset: () => setYaw(0)
246
+ }
247
+ ),
248
+ /* @__PURE__ */ jsx2(
249
+ AxisPill,
250
+ {
251
+ label: "Pitch",
252
+ color: "#82aaff",
253
+ value: camera.pitch,
254
+ onDec: () => rotate(0, -STEP),
255
+ onInc: () => rotate(0, STEP),
256
+ onReset: () => setPitch(0)
257
+ }
258
+ ),
259
+ /* @__PURE__ */ jsx2(IconBtn, { color: "#ffffff", title: "Reset all", onClick: reset, children: "\u2298" })
260
+ ]
261
+ }
262
+ );
263
+ }
264
+
265
+ // src/components/FaceLabel.tsx
266
+ import { jsx as jsx3 } from "react/jsx-runtime";
267
+ function transformLocalToWorld(m, x, y, z) {
268
+ return {
269
+ x: m[0] * x + m[4] * y + m[8] * z + m[12],
270
+ y: m[1] * x + m[5] * y + m[9] * z + m[13],
271
+ z: m[2] * x + m[6] * y + m[10] * z + m[14]
272
+ };
273
+ }
274
+ function cross(ax, ay, az, bx, by, bz) {
275
+ return {
276
+ x: ay * bz - az * by,
277
+ y: az * bx - ax * bz,
278
+ z: ax * by - ay * bx
279
+ };
280
+ }
281
+ function solveHomography(w, h, p) {
282
+ const [p0, p1, p2, p3] = p;
283
+ const sx = [0, w, w, 0];
284
+ const sy = [0, 0, h, h];
285
+ const dx = [p0.x, p1.x, p2.x, p3.x];
286
+ const dy = [p0.y, p1.y, p2.y, p3.y];
287
+ const M = [];
288
+ for (let i = 0; i < 4; i++) {
289
+ M.push([sx[i], sy[i], 1, 0, 0, 0, -dx[i] * sx[i], -dx[i] * sy[i], dx[i]]);
290
+ M.push([0, 0, 0, sx[i], sy[i], 1, -dy[i] * sx[i], -dy[i] * sy[i], dy[i]]);
291
+ }
292
+ const n = 8;
293
+ for (let col = 0; col < n; col++) {
294
+ let maxRow = col;
295
+ for (let row = col + 1; row < n; row++) {
296
+ if (Math.abs(M[row][col]) > Math.abs(M[maxRow][col])) maxRow = row;
297
+ }
298
+ [M[col], M[maxRow]] = [M[maxRow], M[col]];
299
+ if (Math.abs(M[col][col]) < 1e-10) return null;
300
+ const pivot = M[col][col];
301
+ for (let k = col; k <= n; k++) M[col][k] /= pivot;
302
+ for (let row = 0; row < n; row++) {
303
+ if (row === col) continue;
304
+ const f = M[row][col];
305
+ for (let k = col; k <= n; k++) M[row][k] -= f * M[col][k];
306
+ }
307
+ }
308
+ return M.map((row) => row[n]);
309
+ }
310
+ function buildMatrix3d(w, h, p) {
311
+ const hv = solveHomography(w, h, p);
312
+ if (!hv) return "none";
313
+ const [h00, h01, h02, h10, h11, h12, h20, h21] = hv;
314
+ return `matrix3d(${h00},${h10},0,${h20},${h01},${h11},0,${h21},0,0,1,0,${h02},${h12},0,1)`;
315
+ }
316
+ function FaceSkin({
317
+ context,
318
+ model,
319
+ corners,
320
+ children,
321
+ style,
322
+ className,
323
+ surfaceWidth = 300,
324
+ surfaceHeight = 200
325
+ }) {
326
+ const { project } = useWorldToScreen(context);
327
+ if (!model || !context) return null;
328
+ const mm = model.getModelMatrix();
329
+ const worldPts = corners.map(
330
+ ([lx, ly, lz]) => transformLocalToWorld(mm, lx, ly, lz)
331
+ );
332
+ const e1x = worldPts[1].x - worldPts[0].x;
333
+ const e1y = worldPts[1].y - worldPts[0].y;
334
+ const e1z = worldPts[1].z - worldPts[0].z;
335
+ const e2x = worldPts[3].x - worldPts[0].x;
336
+ const e2y = worldPts[3].y - worldPts[0].y;
337
+ const e2z = worldPts[3].z - worldPts[0].z;
338
+ const normal = cross(e1x, e1y, e1z, e2x, e2y, e2z);
339
+ const camera = context.viewport.camera;
340
+ const radius = Math.sqrt(
341
+ (camera.position[0] - camera.target[0]) ** 2 + (camera.position[1] - camera.target[1]) ** 2 + (camera.position[2] - camera.target[2]) ** 2
342
+ );
343
+ const eyeX = camera.target[0] + radius * Math.cos(camera.pitch) * Math.sin(camera.yaw);
344
+ const eyeY = camera.target[1] + radius * Math.sin(camera.pitch);
345
+ const eyeZ = camera.target[2] + radius * Math.cos(camera.pitch) * Math.cos(camera.yaw);
346
+ const toCamera = {
347
+ x: eyeX - worldPts[0].x,
348
+ y: eyeY - worldPts[0].y,
349
+ z: eyeZ - worldPts[0].z
350
+ };
351
+ const dot = normal.x * toCamera.x + normal.y * toCamera.y + normal.z * toCamera.z;
352
+ if (dot <= 0) return null;
353
+ const screenPts = worldPts.map((w) => project(w.x, w.y, w.z));
354
+ if (screenPts.some((pt) => !pt.visible)) return null;
355
+ const remapped = [screenPts[3], screenPts[2], screenPts[1], screenPts[0]];
356
+ const matrix = buildMatrix3d(surfaceWidth, surfaceHeight, remapped);
357
+ return /* @__PURE__ */ jsx3(
358
+ "div",
359
+ {
360
+ style: {
361
+ position: "absolute",
362
+ inset: 0,
363
+ pointerEvents: "none",
364
+ overflow: "hidden"
365
+ },
366
+ children: /* @__PURE__ */ jsx3(
367
+ "div",
368
+ {
369
+ className,
370
+ style: {
371
+ position: "absolute",
372
+ top: 0,
373
+ left: 0,
374
+ width: surfaceWidth,
375
+ height: surfaceHeight,
376
+ transformOrigin: "0 0",
377
+ transform: matrix,
378
+ overflow: "hidden",
379
+ pointerEvents: "auto",
380
+ ...style
381
+ },
382
+ children
383
+ }
384
+ )
385
+ }
386
+ );
387
+ }
388
+
389
+ export {
390
+ GenesisGLCanvas,
391
+ RotationControls,
392
+ FaceSkin
393
+ };
394
+ //# sourceMappingURL=chunk-R4MI3LWV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/GenesisGLCanvas.tsx","../src/components/RotationControls.tsx","../src/components/FaceLabel.tsx"],"sourcesContent":["import React, { useRef, useCallback } from 'react';\nimport { useGenesisGL } from '../hooks/useGenesisGL';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\nimport { useRenderer } from '../hooks/useRenderer';\nimport { useCameraMouseDrag } from '../hooks/useCameraMouseDrag';\n\nexport interface GenesisGLCanvasProps {\n onReady?: (context: GenesisGLContext) => void;\n onFrame?: (time: number) => void;\n width?: number;\n height?: number;\n initialYaw?: number;\n initialPitch?: number;\n initialZoom?: number;\n style?: React.CSSProperties;\n className?: string;\n}\n\nexport const GenesisGLCanvas = React.forwardRef<\n HTMLCanvasElement,\n GenesisGLCanvasProps\n>(\n (\n {\n onReady,\n onFrame,\n width,\n height,\n initialYaw,\n initialPitch,\n initialZoom,\n style,\n className,\n },\n ref,\n ) => {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n // Always use the internal ref for GL init; expose to parent via callback ref\n const setRef = useCallback(\n (el: HTMLCanvasElement | null) => {\n (\n canvasRef as React.MutableRefObject<HTMLCanvasElement | null>\n ).current = el;\n if (typeof ref === 'function') ref(el);\n else if (ref) ref.current = el;\n },\n [ref],\n );\n\n const onFrameRef = useRef(onFrame);\n onFrameRef.current = onFrame;\n\n const genesisContext = useGenesisGL({\n canvasRef,\n width,\n height,\n initialYaw,\n initialPitch,\n initialZoom,\n onReady,\n });\n\n const genesisContextRef = useRef(genesisContext);\n genesisContextRef.current = genesisContext;\n\n const stableOnFrame = useCallback((time: number) => {\n const { renderer, scene } = genesisContextRef.current;\n if (renderer && scene) {\n renderer.render(scene);\n onFrameRef.current?.(time);\n }\n }, []);\n\n useRenderer({ renderer: genesisContext.renderer, onFrame: stableOnFrame });\n useCameraMouseDrag(genesisContext, canvasRef);\n\n return (\n <canvas\n ref={setRef}\n style={{ display: 'block', width: '100%', height: '100%', ...style }}\n className={className}\n />\n );\n },\n);\n\nGenesisGLCanvas.displayName = 'GenesisGLCanvas';\n","import React, { useState } from 'react';\nimport { useCameraControls } from '../hooks/useCameraControls';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\n\nconst STEP = 0.05;\n\nfunction IconBtn({\n children,\n title,\n onClick,\n color,\n}: {\n children: React.ReactNode;\n title?: string;\n onClick: () => void;\n color: string;\n}) {\n const [active, setActive] = useState(false);\n const [hover, setHover] = useState(false);\n\n return (\n <button\n title={title}\n onClick={onClick}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => { setHover(false); setActive(false); }}\n onMouseDown={() => setActive(true)}\n onMouseUp={() => setActive(false)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '22px',\n height: '22px',\n border: `1px solid ${hover ? color + '88' : color + '33'}`,\n borderRadius: '5px',\n background: active ? color + '30' : hover ? color + '18' : color + '0c',\n color: hover ? color : color + 'aa',\n cursor: 'pointer',\n fontSize: '11px',\n fontWeight: 700,\n lineHeight: 1,\n padding: 0,\n transition: 'all 0.12s',\n transform: active ? 'scale(0.88)' : 'scale(1)',\n flexShrink: 0,\n }}\n >\n {children}\n </button>\n );\n}\n\nfunction AxisPill({\n label,\n color,\n value,\n onDec,\n onInc,\n onReset,\n}: {\n label: string;\n color: string;\n value: number;\n onDec: () => void;\n onInc: () => void;\n onReset: () => void;\n}) {\n const deg = ((value * 180) / Math.PI).toFixed(1);\n const normalized = ((value % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);\n const pct = (normalized / (Math.PI * 2)) * 100;\n\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '5px',\n background: color + '0a',\n border: `1px solid ${color}22`,\n borderRadius: '8px',\n padding: '5px 7px',\n flex: 1,\n minWidth: 0,\n }}\n >\n <div style={{ position: 'relative', width: '18px', height: '18px', flexShrink: 0 }}>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\">\n <circle cx=\"9\" cy=\"9\" r=\"7\" fill=\"none\" stroke={color + '22'} strokeWidth=\"2\" />\n <circle\n cx=\"9\" cy=\"9\" r=\"7\"\n fill=\"none\"\n stroke={color}\n strokeWidth=\"2\"\n strokeDasharray={`${(pct / 100) * 44} 44`}\n strokeLinecap=\"round\"\n transform=\"rotate(-90 9 9)\"\n style={{ transition: 'stroke-dasharray 0.1s ease' }}\n />\n </svg>\n <span\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '7px',\n fontWeight: 800,\n color,\n letterSpacing: '-0.02em',\n }}\n >\n {label}\n </span>\n </div>\n\n <span\n style={{\n fontSize: '10px',\n color: color + 'cc',\n fontVariantNumeric: 'tabular-nums',\n width: '36px',\n textAlign: 'right',\n flexShrink: 0,\n }}\n >\n {deg}°\n </span>\n\n <div style={{ display: 'flex', gap: '3px', marginLeft: '2px' }}>\n <IconBtn color={color} title={`−${label}`} onClick={onDec}>−</IconBtn>\n <IconBtn color={color} title={`+${label}`} onClick={onInc}>+</IconBtn>\n <IconBtn color={color + '88'} title=\"Reset\" onClick={onReset}>↺</IconBtn>\n </div>\n </div>\n );\n}\n\nexport interface RotationControlsProps {\n context: GenesisGLContext | null;\n style?: React.CSSProperties;\n}\n\nexport function RotationControls({ context, style }: RotationControlsProps) {\n const { camera, rotate, setYaw, setPitch, reset } = useCameraControls(context);\n\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n background: 'rgba(8,8,12,0.78)',\n border: '1px solid rgba(255,255,255,0.07)',\n borderRadius: '12px',\n padding: '6px 8px',\n backdropFilter: 'blur(16px)',\n boxShadow: '0 4px 24px rgba(0,0,0,0.5)',\n fontFamily: '\"Inter\", system-ui, monospace',\n ...style,\n }}\n >\n <div\n title={context ? 'Camera ready' : 'Waiting for context…'}\n style={{\n width: '6px',\n height: '6px',\n borderRadius: '50%',\n background: context ? '#c3e88d' : '#555',\n boxShadow: context ? '0 0 6px #c3e88daa' : 'none',\n flexShrink: 0,\n transition: 'all 0.3s',\n }}\n />\n\n <AxisPill\n label=\"Yaw\"\n color=\"#ff5370\"\n value={camera.yaw}\n onDec={() => rotate(-STEP, 0)}\n onInc={() => rotate(STEP, 0)}\n onReset={() => setYaw(0)}\n />\n\n <AxisPill\n label=\"Pitch\"\n color=\"#82aaff\"\n value={camera.pitch}\n onDec={() => rotate(0, -STEP)}\n onInc={() => rotate(0, STEP)}\n onReset={() => setPitch(0)}\n />\n\n <IconBtn color=\"#ffffff\" title=\"Reset all\" onClick={reset}>⊘</IconBtn>\n </div>\n );\n}\n","import React from 'react';\nimport type { Model } from '@fonsecabarreto/genesis-gl-core/Core';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\nimport { useWorldToScreen } from '../hooks/useWorldToScreen';\n\nexport interface FaceSkinProps {\n context: GenesisGLContext | null;\n model: Model | null;\n corners: [\n [number, number, number],\n [number, number, number],\n [number, number, number],\n [number, number, number],\n ];\n /** Width of the virtual surface div warped onto the face (px) */\n surfaceWidth?: number;\n /** Height of the virtual surface div warped onto the face (px) */\n surfaceHeight?: number;\n children?: React.ReactNode;\n style?: React.CSSProperties;\n className?: string;\n}\n\nfunction transformLocalToWorld(\n m: Float32Array,\n x: number,\n y: number,\n z: number,\n) {\n return {\n x: m[0] * x + m[4] * y + m[8] * z + m[12],\n y: m[1] * x + m[5] * y + m[9] * z + m[13],\n z: m[2] * x + m[6] * y + m[10] * z + m[14],\n };\n}\n\nfunction cross(\n ax: number,\n ay: number,\n az: number,\n bx: number,\n by: number,\n bz: number,\n) {\n return {\n x: ay * bz - az * by,\n y: az * bx - ax * bz,\n z: ax * by - ay * bx,\n };\n}\n\n/**\n * Solve for the homography matrix H (8 unknowns, h22=1) that maps\n * a w×h rectangle to four destination points using Gaussian elimination.\n * Returns [h00,h01,h02,h10,h11,h12,h20,h21] or null on failure.\n */\nfunction solveHomography(\n w: number,\n h: number,\n p: { x: number; y: number }[],\n): number[] | null {\n const [p0, p1, p2, p3] = p;\n const sx = [0, w, w, 0];\n const sy = [0, 0, h, h];\n const dx = [p0.x, p1.x, p2.x, p3.x];\n const dy = [p0.y, p1.y, p2.y, p3.y];\n\n // Build augmented 8×9 matrix\n const M: number[][] = [];\n for (let i = 0; i < 4; i++) {\n M.push([sx[i], sy[i], 1, 0, 0, 0, -dx[i] * sx[i], -dx[i] * sy[i], dx[i]]);\n M.push([0, 0, 0, sx[i], sy[i], 1, -dy[i] * sx[i], -dy[i] * sy[i], dy[i]]);\n }\n\n const n = 8;\n for (let col = 0; col < n; col++) {\n let maxRow = col;\n for (let row = col + 1; row < n; row++) {\n if (Math.abs(M[row][col]) > Math.abs(M[maxRow][col])) maxRow = row;\n }\n [M[col], M[maxRow]] = [M[maxRow], M[col]];\n if (Math.abs(M[col][col]) < 1e-10) return null;\n const pivot = M[col][col];\n for (let k = col; k <= n; k++) M[col][k] /= pivot;\n for (let row = 0; row < n; row++) {\n if (row === col) continue;\n const f = M[row][col];\n for (let k = col; k <= n; k++) M[row][k] -= f * M[col][k];\n }\n }\n\n return M.map((row) => row[n]);\n}\n\n/**\n * Build the CSS matrix3d string that perspectively maps a w×h div\n * (origin top-left) onto four screen-space points [p0,p1,p2,p3]\n * corresponding to the div corners (0,0),(w,0),(w,h),(0,h).\n */\nfunction buildMatrix3d(\n w: number,\n h: number,\n p: { x: number; y: number }[],\n): string {\n const hv = solveHomography(w, h, p);\n if (!hv) return 'none';\n const [h00, h01, h02, h10, h11, h12, h20, h21] = hv;\n // CSS matrix3d is column-major 4×4\n return (\n `matrix3d(` +\n `${h00},${h10},0,${h20},` +\n `${h01},${h11},0,${h21},` +\n `0,0,1,0,` +\n `${h02},${h12},0,1)`\n );\n}\n\nexport function FaceSkin({\n context,\n model,\n corners,\n children,\n style,\n className,\n surfaceWidth = 300,\n surfaceHeight = 200,\n}: FaceSkinProps) {\n const { project } = useWorldToScreen(context);\n\n if (!model || !context) return null;\n\n const mm = model.getModelMatrix() as unknown as Float32Array;\n\n const worldPts = corners.map(([lx, ly, lz]) =>\n transformLocalToWorld(mm, lx, ly, lz),\n );\n\n const e1x = worldPts[1].x - worldPts[0].x;\n const e1y = worldPts[1].y - worldPts[0].y;\n const e1z = worldPts[1].z - worldPts[0].z;\n const e2x = worldPts[3].x - worldPts[0].x;\n const e2y = worldPts[3].y - worldPts[0].y;\n const e2z = worldPts[3].z - worldPts[0].z;\n const normal = cross(e1x, e1y, e1z, e2x, e2y, e2z);\n\n const camera = context.viewport!.camera;\n const radius = Math.sqrt(\n (camera.position[0] - camera.target[0]) ** 2 +\n (camera.position[1] - camera.target[1]) ** 2 +\n (camera.position[2] - camera.target[2]) ** 2,\n );\n const eyeX =\n camera.target[0] + radius * Math.cos(camera.pitch) * Math.sin(camera.yaw);\n const eyeY = camera.target[1] + radius * Math.sin(camera.pitch);\n const eyeZ =\n camera.target[2] + radius * Math.cos(camera.pitch) * Math.cos(camera.yaw);\n\n const toCamera = {\n x: eyeX - worldPts[0].x,\n y: eyeY - worldPts[0].y,\n z: eyeZ - worldPts[0].z,\n };\n const dot =\n normal.x * toCamera.x + normal.y * toCamera.y + normal.z * toCamera.z;\n\n if (dot <= 0) return null;\n\n const screenPts = worldPts.map((w) => project(w.x, w.y, w.z));\n if (screenPts.some((pt) => !pt.visible)) return null;\n\n // Remap corners so the div renders right-side-up from the isometric view:\n // div (0,0)→screenPts[3], (w,0)→[2], (w,h)→[1], (0,h)→[0]\n const remapped = [screenPts[3], screenPts[2], screenPts[1], screenPts[0]];\n const matrix = buildMatrix3d(surfaceWidth, surfaceHeight, remapped);\n\n return (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n overflow: 'hidden',\n }}\n >\n <div\n className={className}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: surfaceWidth,\n height: surfaceHeight,\n transformOrigin: '0 0',\n transform: matrix,\n overflow: 'hidden',\n pointerEvents: 'auto',\n ...style,\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n\nexport { FaceSkin as FaceLabel };\nexport type { FaceSkinProps as FaceLabelProps };\n"],"mappings":"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","names":["jsx","jsx"]}
@@ -2,8 +2,8 @@ import {
2
2
  FaceSkin,
3
3
  GenesisGLCanvas,
4
4
  RotationControls
5
- } from "../chunk-7BVTZKWZ.js";
6
- import "../chunk-XJKE4L2D.js";
5
+ } from "../chunk-R4MI3LWV.js";
6
+ import "../chunk-MAGVIIUT.js";
7
7
  export {
8
8
  FaceSkin as FaceLabel,
9
9
  FaceSkin,
@@ -8,7 +8,7 @@ import {
8
8
  useGenesisGL,
9
9
  useRenderer,
10
10
  useWorldToScreen
11
- } from "../chunk-XJKE4L2D.js";
11
+ } from "../chunk-MAGVIIUT.js";
12
12
  export {
13
13
  useCameraControls,
14
14
  useCameraMouseDrag,
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  FaceSkin,
3
3
  GenesisGLCanvas,
4
4
  RotationControls
5
- } from "./chunk-7BVTZKWZ.js";
5
+ } from "./chunk-R4MI3LWV.js";
6
6
  import {
7
7
  useModel,
8
8
  useModelRotation
@@ -13,7 +13,7 @@ import {
13
13
  useGenesisGL,
14
14
  useRenderer,
15
15
  useWorldToScreen
16
- } from "./chunk-XJKE4L2D.js";
16
+ } from "./chunk-MAGVIIUT.js";
17
17
 
18
18
  // src/index.ts
19
19
  import { loadOBJWithMTL } from "@fonsecabarreto/genesis-gl-core/Core/utils/parse-obj";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonsecabarreto/genesis-gl-react",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "React integration for GenesisGL — WebGL 3D engine",
5
5
  "author": "Lucas Fonseca Barreto",
6
6
  "license": "MIT",