Skip to content

Commit f01800b

Browse files
first iteration of orbit camera for threejs implemented
1 parent fc68fce commit f01800b

13 files changed

Lines changed: 1984 additions & 31 deletions

File tree

examples/vite/threejs/starter-template/src/main.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import "./style.css"; // Basic styling
2-
import { BitByBitBase, Inputs, initBitbybit, type InitBitbybitOptions } from "@bitbybit-dev/threejs";
3-
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
2+
import { BitByBitBase, Inputs, initBitbybit, type InitBitbybitOptions, type OrbitCameraController } from "@bitbybit-dev/threejs";
43
import {
54
Color,
65
HemisphereLight,
7-
PerspectiveCamera,
86
Scene,
97
WebGLRenderer,
108
} from "three";
119

10+
// Store the orbit camera controller globally so bitbybit can access it
11+
let orbitCameraController: OrbitCameraController | null = null;
12+
1213
// --- 1. Main Application Entry Point ---
1314
start();
1415

1516
async function start() {
1617
// Initialize basic Three.js scene
17-
const { scene } = initThreeJS();
18+
const { scene, renderer } = initThreeJS();
1819

1920
// Create an instance of BitByBitBase for Three.js
2021
const bitbybit = new BitByBitBase();
@@ -31,6 +32,27 @@ async function start() {
3132
// Initialize BitByBit in a single call - workers are created from CDN automatically!
3233
await initBitbybit(scene, bitbybit, options);
3334

35+
// --- 2.5. Setup BitByBit Orbit Camera ---
36+
// Setup orbit camera controls using bitbybit library
37+
const cameraOptions = new Inputs.ThreeJSCamera.OrbitCameraDto();
38+
cameraOptions.distance = 120;
39+
cameraOptions.pitch = 25;
40+
cameraOptions.yaw = 45;
41+
cameraOptions.frameOnStart = false;
42+
cameraOptions.inertiaFactor = 0.1;
43+
cameraOptions.distanceSensitivity = 0.15;
44+
cameraOptions.domElement = renderer.domElement;
45+
orbitCameraController = bitbybit.three.camera.orbitCamera.create(cameraOptions);
46+
47+
// Update the render loop to use the new camera
48+
const animate = () => {
49+
if (orbitCameraController) {
50+
orbitCameraController.update(0.016); // ~60fps delta time
51+
renderer.render(scene, orbitCameraController.camera);
52+
}
53+
};
54+
renderer.setAnimationLoop(animate);
55+
3456
// --- 3. Create Geometry with Active Kernels ---
3557
if (options.enableOCCT) {
3658
await createOCCTGeometry(bitbybit, "#ff0000"); // Red
@@ -55,12 +77,6 @@ async function start() {
5577
function initThreeJS() {
5678
const domNode = document.getElementById("three-canvas") as HTMLCanvasElement;
5779

58-
const camera = new PerspectiveCamera(
59-
70,
60-
window.innerWidth / window.innerHeight,
61-
0.1, // Adjusted near plane for typical scenes
62-
1000
63-
);
6480
const scene = new Scene();
6581
scene.background = new Color(0x1a1c1f); // Set background color
6682

@@ -73,29 +89,16 @@ function initThreeJS() {
7389
// Higher values = sharper but more GPU intensive. Use 1 for performance, devicePixelRatio for quality.
7490
renderer.setPixelRatio(window.devicePixelRatio);
7591

76-
camera.position.set(50, 50, 100); // Adjusted camera position
77-
camera.lookAt(0, 0, 0);
78-
79-
const controls = new OrbitControls(camera, renderer.domElement);
80-
controls.enableDamping = true;
81-
controls.dampingFactor = 0.05; // Smoother damping
82-
controls.target.set(0, 0, 0); // Ensure controls target the origin
83-
controls.update(); // Initial update
84-
8592
const onWindowResize = () => {
86-
camera.aspect = window.innerWidth / window.innerHeight;
87-
camera.updateProjectionMatrix();
93+
if (orbitCameraController) {
94+
orbitCameraController.camera.aspect = window.innerWidth / window.innerHeight;
95+
orbitCameraController.camera.updateProjectionMatrix();
96+
}
8897
renderer.setSize(window.innerWidth, window.innerHeight);
8998
};
9099
window.addEventListener("resize", onWindowResize, false);
91100

92-
const animate = () => {
93-
controls.update(); // Important for damping
94-
renderer.render(scene, camera);
95-
};
96-
renderer.setAnimationLoop(animate);
97-
98-
return { scene, camera, renderer }; // Return renderer and camera if needed elsewhere
101+
return { scene, renderer }; // Return renderer for camera setup
99102
}
100103

101104
// // --- 5. Bitbybit Kernel Initialization Logic ---

packages/dev/threejs/lib/api/__mocks__/test-helpers.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,70 @@ export function mockWorkerError(workerManager: any, method: string, error: Error
197197
});
198198
});
199199
}
200+
201+
/**
202+
* Creates a mock window object for testing
203+
*/
204+
export function mockWindow() {
205+
(global as any).window = {
206+
addEventListener: jest.fn(),
207+
removeEventListener: jest.fn(),
208+
innerWidth: 1920,
209+
innerHeight: 1080,
210+
};
211+
}
212+
213+
/**
214+
* Creates a mock DOM element for testing orbit camera inputs.
215+
* Supports event listener tracking for simulating user interactions.
216+
*/
217+
export function createMockDOMElement(): HTMLElement {
218+
const listeners: { [key: string]: Array<(...args: unknown[]) => void> } = {};
219+
return {
220+
addEventListener: jest.fn((type: string, handler: (...args: unknown[]) => void) => {
221+
if (!listeners[type]) {
222+
listeners[type] = [];
223+
}
224+
listeners[type].push(handler);
225+
}),
226+
removeEventListener: jest.fn((type: string, handler: (...args: unknown[]) => void) => {
227+
if (listeners[type]) {
228+
const idx = listeners[type].indexOf(handler);
229+
if (idx >= 0) {
230+
listeners[type].splice(idx, 1);
231+
}
232+
}
233+
}),
234+
dispatchEvent: jest.fn((event: Event) => {
235+
const handlers = listeners[event.type];
236+
if (handlers) {
237+
handlers.forEach(h => h(event));
238+
}
239+
}),
240+
getBoundingClientRect: jest.fn(() => ({
241+
left: 0,
242+
top: 0,
243+
width: 1920,
244+
height: 1080,
245+
})),
246+
} as unknown as HTMLElement;
247+
}
248+
249+
/**
250+
* Creates mock app and scene for orbit camera tests
251+
*/
252+
export function createOrbitCameraMocks() {
253+
mockWindow();
254+
const mockScene = new THREEJS.Scene();
255+
const mockDomElement = createMockDOMElement();
256+
const mockContext = {
257+
scene: mockScene,
258+
} as Context;
259+
260+
return {
261+
mockScene,
262+
mockDomElement,
263+
mockContext
264+
};
265+
}
266+

packages/dev/threejs/lib/api/bitbybit-base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class BitByBitBase {
7272
this.math = new MathBitByBit();
7373
this.vector = new Vector(this.math, geometryHelper);
7474
const drawHelper = new DrawHelper(this.context, this.jscad.text, this.vector, this.jscadWorkerManager, this.manifoldWorkerManager, this.occtWorkerManager);
75-
this.three = new ThreeJS(drawHelper);
75+
this.three = new ThreeJS(this.context, drawHelper);
7676
this.tag = new Tag(this.context);
7777
this.draw = new Draw(drawHelper, this.context, this.tag);
7878
this.color = new Color(this.math);
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export * from "./draw";
1+
export * from "./draw";
2+
export * from "./threejs";
3+
export * from "./threejs/index";
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1+
import { Context } from "../context";
12
import { DrawHelper } from "../draw-helper";
3+
import { ThreeJSCamera } from "./threejs/camera";
24

5+
/**
6+
* Contains various functions that expose ThreeJS objects
7+
*/
38
export class ThreeJS {
4-
constructor(private readonly drawHelper: DrawHelper) { }
9+
public camera: ThreeJSCamera;
10+
11+
constructor(
12+
private readonly context: Context,
13+
private readonly drawHelper: DrawHelper
14+
) {
15+
this.camera = new ThreeJSCamera(this.context);
16+
}
517
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Context } from "../../context";
2+
import { ThreeJSOrbitCamera } from "./orbit-camera";
3+
4+
export class ThreeJSCamera {
5+
6+
public orbitCamera: ThreeJSOrbitCamera;
7+
8+
constructor(
9+
private readonly context: Context,
10+
) {
11+
this.orbitCamera = new ThreeJSOrbitCamera(this.context);
12+
}
13+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./camera";
2+
export * from "./orbit-camera";

0 commit comments

Comments
 (0)