or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

boundaries-constraints.mdcamera-fitting.mdcamera-movement.mdcore-controls.mdevent-system.mdindex.mdinput-configuration.mdstate-management.md
tile.json

camera-fitting.mddocs/

Camera Fitting

Automatic camera positioning to fit objects, bounding boxes, and spheres with configurable padding and framing options.

Capabilities

Box Fitting

Automatically position camera to frame bounding boxes or Three.js objects with configurable padding.

/**
 * Fit camera to view a bounding box or Three.js object
 * @param box3OrObject - Three.js Box3 or Object3D to fit to
 * @param enableTransition - Whether to animate the camera movement
 * @param options - Optional padding and framing configuration
 * @returns Promise array that resolves when all movements complete
 */
fitToBox(
  box3OrObject: THREE.Box3 | THREE.Object3D,
  enableTransition: boolean,
  options?: Partial<FitToOptions>
): Promise<void[]>;

interface FitToOptions {
  cover: boolean;        // true = fill viewport, false = fit entirely
  paddingLeft: number;   // Left padding (0-1 or pixels)
  paddingRight: number;  // Right padding (0-1 or pixels)
  paddingBottom: number; // Bottom padding (0-1 or pixels)
  paddingTop: number;    // Top padding (0-1 or pixels)
}

Usage Examples:

import * as THREE from 'three';

// Fit to a mesh with default settings
const mesh = new THREE.Mesh(geometry, material);
await cameraControls.fitToBox(mesh, true);

// Fit with custom padding (values 0-1 for viewport percentage)
await cameraControls.fitToBox(mesh, true, {
  paddingLeft: 0.1,    // 10% left padding
  paddingRight: 0.1,   // 10% right padding
  paddingTop: 0.2,     // 20% top padding
  paddingBottom: 0.05  // 5% bottom padding
});

// Cover mode (fills viewport, may crop object)
await cameraControls.fitToBox(mesh, true, {
  cover: true,
  paddingLeft: 0.05,
  paddingRight: 0.05,
  paddingTop: 0.05,
  paddingBottom: 0.05
});

// Fit to explicit bounding box
const customBox = new THREE.Box3(
  new THREE.Vector3(-5, -3, -2),
  new THREE.Vector3(5, 7, 8)
);
await cameraControls.fitToBox(customBox, true);

// Multiple objects using group
const group = new THREE.Group();
group.add(mesh1, mesh2, mesh3);
await cameraControls.fitToBox(group, true);

Sphere Fitting

Automatically position camera to frame bounding spheres or Three.js objects.

/**
 * Fit camera to view a bounding sphere or Three.js object
 * @param sphereOrMesh - Three.js Sphere or Object3D to fit to
 * @param enableTransition - Whether to animate the camera movement
 * @returns Promise array that resolves when all movements complete
 */
fitToSphere(
  sphereOrMesh: THREE.Sphere | THREE.Object3D,
  enableTransition: boolean
): Promise<void[]>;

Usage Examples:

// Fit to mesh using computed bounding sphere
const mesh = new THREE.Mesh(geometry, material);
await cameraControls.fitToSphere(mesh, true);

// Fit to explicit sphere
const customSphere = new THREE.Sphere(
  new THREE.Vector3(0, 5, 0), // center
  10                          // radius
);
await cameraControls.fitToSphere(customSphere, true);

// Instant fitting without transition
await cameraControls.fitToSphere(mesh, false);

// Create bounding sphere from object using static method
const boundingSphere = CameraControls.createBoundingSphere(complexObject);
await cameraControls.fitToSphere(boundingSphere, true);

Advanced Fitting Examples

Multi-Object Fitting:

// Fit to multiple objects using group
const scene = new THREE.Scene();
const group = new THREE.Group();

// Add multiple objects to group
group.add(
  new THREE.Mesh(geometry1, material1),
  new THREE.Mesh(geometry2, material2),
  new THREE.Mesh(geometry3, material3)
);

scene.add(group);

// Fit camera to encompass all objects
await cameraControls.fitToBox(group, true, {
  paddingLeft: 0.1,
  paddingRight: 0.1,
  paddingTop: 0.1,
  paddingBottom: 0.1
});

Responsive Fitting:

// Fit with different padding based on viewport size
const aspectRatio = window.innerWidth / window.innerHeight;
const padding = aspectRatio > 1.5 ? 0.05 : 0.15; // Less padding on wide screens

await cameraControls.fitToBox(mesh, true, {
  paddingLeft: padding,
  paddingRight: padding,
  paddingTop: padding,
  paddingBottom: padding
});

Sequential Fitting:

// Fit to multiple objects in sequence
const objects = [mesh1, mesh2, mesh3];

for (const obj of objects) {
  await cameraControls.fitToBox(obj, true, {
    paddingLeft: 0.2,
    paddingRight: 0.2,
    paddingTop: 0.2,
    paddingBottom: 0.2
  });
  
  // Wait before fitting to next object
  await new Promise(resolve => setTimeout(resolve, 2000));
}

Cover vs Contain:

// Contain mode (default): entire object visible with padding
await cameraControls.fitToBox(mesh, true, {
  cover: false,
  paddingLeft: 0.1,
  paddingRight: 0.1,
  paddingTop: 0.1,
  paddingBottom: 0.1
});

// Cover mode: object fills viewport, may be partially cropped
await cameraControls.fitToBox(mesh, true, {
  cover: true,  // Fill viewport
  paddingLeft: 0,
  paddingRight: 0,
  paddingTop: 0,
  paddingBottom: 0
});

Fitting with Distance Calculation

Use distance calculation utilities for manual camera positioning:

// From boundaries-constraints.md - repeated here for context
getDistanceToFitBox(width: number, height: number, depth: number, cover?: boolean): number;
getDistanceToFitSphere(radius: number): number;

Manual Fitting Examples:

// Calculate and apply distance manually
const box = new THREE.Box3().setFromObject(mesh);
const size = box.getSize(new THREE.Vector3());
const center = box.getCenter(new THREE.Vector3());

// Calculate distance
const distance = cameraControls.getDistanceToFitBox(size.x, size.y, size.z);

// Apply manually with custom target
await Promise.all([
  cameraControls.setTarget(center.x, center.y, center.z, true),
  cameraControls.dollyTo(distance, true)
]);

// Sphere fitting manually
geometry.computeBoundingSphere();
const sphere = geometry.boundingSphere;
const sphereDistance = cameraControls.getDistanceToFitSphere(sphere.radius);

await Promise.all([
  cameraControls.setTarget(sphere.center.x, sphere.center.y, sphere.center.z, true),
  cameraControls.dollyTo(sphereDistance, true)
]);

Viewport-Specific Fitting

Combine fitting with viewport configuration for advanced layout scenarios:

// Set viewport before fitting
cameraControls.setViewport(0, 0, 800, 600);
await cameraControls.fitToBox(mesh, true);

// Reset viewport after fitting
cameraControls.setViewport(null);

// Multi-viewport fitting
const leftViewport = new THREE.Vector4(0, 0, 400, 600);
const rightViewport = new THREE.Vector4(400, 0, 400, 600);

// Fit for left viewport
cameraControls.setViewport(leftViewport);
await cameraControls.fitToBox(leftObject, true);

// Fit for right viewport  
cameraControls.setViewport(rightViewport);
await cameraControls.fitToBox(rightObject, true);

Error Handling and Edge Cases

// Check for valid geometry before fitting
if (mesh.geometry && mesh.geometry.boundingBox) {
  await cameraControls.fitToBox(mesh, true);
} else {
  console.warn('Mesh has no bounding box for fitting');
  mesh.geometry.computeBoundingBox();
  await cameraControls.fitToBox(mesh, true);
}

// Handle empty or degenerate boxes
const box = new THREE.Box3().setFromObject(mesh);
if (!box.isEmpty()) {
  await cameraControls.fitToBox(box, true);
} else {
  console.warn('Object has empty bounding box');
}

// Ensure minimum padding for small objects
const minPadding = 0.05;
await cameraControls.fitToBox(mesh, true, {
  paddingLeft: Math.max(calculatedPadding, minPadding),
  paddingRight: Math.max(calculatedPadding, minPadding),
  paddingTop: Math.max(calculatedPadding, minPadding),
  paddingBottom: Math.max(calculatedPadding, minPadding)
});