NgtsBounds
import { ChangeDetectionStrategy, Component } from '@angular/core';import { SobaWrapper } from '@soba/wrapper.ts';import { NgtCanvas, provideNgtRenderer } from 'angular-three/dom';import { SceneGraph } from './scene-graph';
@Component({ selector: 'app-bounds', template: ` <ngt-canvas [camera]="{ position: [0, 0, 10], fov: 50 }"> <app-soba-wrapper *canvasContent> <app-scene-graph /> </app-soba-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'bounds-demo relative block h-full' }, imports: [NgtCanvas, SobaWrapper, SceneGraph],})export default class Bounds { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import { NgtArgs } from 'angular-three';import { NgtsBounds } from 'angular-three-soba/staging';
@Component({ selector: 'app-scene-graph', template: ` <ngts-bounds [options]="{ fit: true, clip: true, observe: true }"> <ngt-group> <ngt-mesh [position]="[-2, 0, 0]"> <ngt-box-geometry *args="[1, 1, 1]" /> <ngt-mesh-standard-material color="#ff6b6b" /> </ngt-mesh> <ngt-mesh [position]="[2, 0, 0]"> <ngt-sphere-geometry *args="[0.75, 32, 32]" /> <ngt-mesh-standard-material color="#4ecdc4" /> </ngt-mesh> <ngt-mesh [position]="[0, 2, 0]"> <ngt-cone-geometry *args="[0.5, 1, 32]" /> <ngt-mesh-standard-material color="#ffe66d" /> </ngt-mesh> </ngt-group> </ngts-bounds> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtsBounds],})export class SceneGraph {}NgtsBounds is a port of Drei’s Bounds which calculates a boundary box around its children and provides methods to fit the camera to view them.
If you are using camera controls, make sure to pass them the makeDefault option for proper integration.
Usage
Basic Bounds
<ngts-bounds [options]="{ fit: true, clip: true, observe: true }"> <ngt-mesh> <ngt-box-geometry /> <ngt-mesh-standard-material /> </ngt-mesh></ngts-bounds>
<ngts-orbit-controls [options]="{ makeDefault: true }" />Fit Multiple Objects
<ngts-bounds [options]="{ fit: true, margin: 1.5 }"> <ngt-mesh [position]="[-2, 0, 0]"> <ngt-sphere-geometry /> <ngt-mesh-standard-material color="red" /> </ngt-mesh>
<ngt-mesh [position]="[2, 0, 0]"> <ngt-box-geometry /> <ngt-mesh-standard-material color="blue" /> </ngt-mesh></ngts-bounds>
<ngts-orbit-controls [options]="{ makeDefault: true }" />With GLTF Model
<ngts-bounds [options]="{ fit: true, clip: true, observe: true }"> @if (gltf(); as gltf) { <ngt-primitive *args="[gltf.scene]" /> }</ngts-bounds>
<ngts-orbit-controls [options]="{ makeDefault: true }" />Custom Animation Duration
<ngts-bounds [options]="{ fit: true, maxDuration: 2.0, margin: 1.3 }"> <app-model /></ngts-bounds>Custom Interpolation Function
<ngts-bounds [options]="{ fit: true, interpolateFunc: easeOutCubic }"> <app-scene /></ngts-bounds>// In componenteaseOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);Responsive Bounds
The observe option recalculates bounds when the window resizes:
<ngts-bounds [options]="{ fit: true, clip: true, observe: true }"> <app-responsive-model /></ngts-bounds>
<ngts-orbit-controls [options]="{ makeDefault: true }" />API
The NgtsBounds component exposes a boundsApi that can be accessed via the bounds output or via dependency injection:
@Component({ template: ` <ngts-bounds (bounds)="onBoundsReady($event)"> <app-model /> </ngts-bounds> `,})export class Scene { onBoundsReady(api: BoundsApi) { // Programmatically refresh bounds api.refresh();
// Fit to specific object api.refresh(someObject3D).fit();
// Get current bounds const box = api.getSize(); }}Notes
- Use
makeDefault: trueon your camera controls for proper bounds integration - The
marginoption adds extra space around the fitted view (1.0 = exact fit, 1.2 = 20% margin) - The
clipoption is useful for preventing near/far clipping issues with the camera - For animated content, you may want to disable
fitand call the API manually
Options
options input accepts any properties from THREE.Group in addition to the following:
Properties
| name | type | description |
|---|---|---|
| fit | boolean | Fits the current view on first render. Default: false |
| clip | boolean | Sets the camera's near/far planes based on the bounding box. Default: false |
| observe | boolean | Triggers recalculation on window resize. Default: false |
| maxDuration | number | The animation length in seconds. Default: 1.0 |
| margin | number | Margin factor applied to the calculated camera distance. Default: 1.2 |
| interpolateFunc | (t: number) => number | Custom interpolation function for camera animation. Should be increasing in [0, 1]. Default: damping-based function |