Skip to content

TransformControlsService

Wraps Three.js TransformControls to provide interactive translate, rotate, and scale gizmos in the scene. Automatically attaches to the selected object when selection changes to a single object, and detaches on multi-selection or clear. Camera orbit controls are disabled while a drag is in progress.

Access

Registered under the name "transformcontrols". Depends on "selection" and "camera".

TransformControlsService is not exported from the SDK entry point. Reach it by its registered name and use the class only as a type.

ts
import type { TransformControlsService } from "@optellix/xviewr-sdk";

await viewer.ready();
const tc = viewer.getService<TransformControlsService>("transformcontrols");
tc?.setMode("rotate");

The service activates automatically on init. Attach and detach happen through selection events, but you can also call attachToObject directly.

Public API

Lifecycle

  • init(context: ServiceContext): Promise<void>
  • destroy(): Promise<void>
  • getStatus(): ServiceStatus

Activation

  • activate(): void — enables controls, adds the helper to the scene, and subscribes to selection events.
  • deactivate(): void — disables controls, detaches from any target, and unsubscribes selection listeners.
  • isTransformActive(): boolean

Attachment

  • attachToObject(object: THREE.Object3D): void — attaches the gizmo to object. Activates controls if not already active.
  • detachFromObject(): void — detaches the gizmo from the current target.
  • getCurrentTarget(): THREE.Object3D | null

Mode and space

  • setMode(mode: TransformMode): void — sets the active gizmo type: "translate", "rotate", or "scale".
  • getMode(): TransformMode
  • setSpace(space: TransformSpace): void — sets the coordinate frame: "world" or "local".
  • getSpace(): TransformSpace

Appearance

  • setSize(size: number): void — scales the gizmo. Default 1.
  • getSize(): number
  • setAxisVisibility(axis: "x" | "y" | "z", visible: boolean): void — shows or hides individual axis handles.

Snapping

  • setTranslationSnap(snap: number | null): void — snaps translation in world units. null disables snapping.
  • setRotationSnap(snap: number | null): void — snaps rotation in radians.
  • setScaleSnap(snap: number | null): void

Configuration

  • updateConfig(newConfig: Partial<TransformControlsConfig>): void — bulk-update config and reapply all settings.
  • getConfig(): TransformControlsConfig

Transform data

  • getTransformData(): TransformData | null — returns a snapshot of the current target's position, rotation, scale, and quaternion. Returns null if no target is attached.
  • applyTransformData(data: TransformData): void — applies a previously captured transform to the current target.
  • resetTransform(): void — resets the current target to identity (position 0,0,0, rotation 0,0,0, scale 1,1,1).

Types

TransformControlsConfig

ts
interface TransformControlsConfig {
  mode: TransformMode;
  size: number;
  space: TransformSpace;
  enabled: boolean;
  showX: boolean;
  showY: boolean;
  showZ: boolean;
  translationSnap: number | null;
  rotationSnap: number | null;
  scaleSnap: number | null;
}

TransformData

ts
interface TransformData {
  position: THREE.Vector3;
  rotation: THREE.Euler;
  scale: THREE.Vector3;
  quaternion: THREE.Quaternion;
}

TransformMode / TransformSpace

ts
type TransformMode = "translate" | "rotate" | "scale";
type TransformSpace = "world" | "local";

Events

EventPayload
transformcontrols:service-ready{}
transformcontrols:activated{}
transformcontrols:deactivated{}
transformcontrols:attached{ object: THREE.Object3D }
transformcontrols:detached{ object: THREE.Object3D }
transformcontrols:changed{ object: THREE.Object3D | null; transformData: TransformData | null }
transformcontrols:dragging-changed{ dragging: boolean; object: THREE.Object3D | null }
transformcontrols:object-changed{ object: THREE.Object3D | null; transformData: TransformData | null }
transformcontrols:mode-changed{ mode: TransformMode }
transformcontrols:space-changed{ space: TransformSpace }
transformcontrols:size-changed{ size: number }
transformcontrols:axis-visibility-changed{ axis: "x" | "y" | "z"; visible: boolean }
transformcontrols:translation-snap-changed{ snap: number | null }
transformcontrols:rotation-snap-changed{ snap: number | null }
transformcontrols:scale-snap-changed{ snap: number | null }
transformcontrols:config-updated{ config: TransformControlsConfig }
transformcontrols:reset{ object: THREE.Object3D | null }
transformcontrols:transform-applied{ object: THREE.Object3D | null; data: TransformData }

Example

ts
const tc = viewer.getService<TransformControlsService>("transformcontrols");
if (!tc) return;

// Switch to rotate mode with 15-degree snapping
tc.setMode("rotate");
tc.setRotationSnap(Math.PI / 12);

// Work in local space
tc.setSpace("local");

// Attach directly to a known object
const mesh = viewer.getService("mesh")?.getById("hinge-001");
if (mesh) tc.attachToObject(mesh);

// Save transform before edit and restore on undo
const before = tc.getTransformData();
// ... user drags ...
tc.applyTransformData(before!); // undo

// Listen for every incremental change
viewer.on("transformcontrols:object-changed", ({ transformData }) => {
  console.log("position:", transformData?.position);
});

XViewr SDK and xConvert CLI documentation.