Appearance
MeasurementService
Manages 3D measurements (distance, angle, area, multi-point, bounding box, volume, circle, arc) using a descriptor-based type system. Owns measurement state, raycasting, and point interaction; rendering and label layout are owned by the per-type descriptors.
Access
Registered under the name "measurement" on ViewerCore.
ts
import { MeasurementService } from "@optellix/xviewr-sdk";
await viewer.ready();
const measurement = viewer.getService<MeasurementService>("measurement");Since 4.0
- Spline measurement mode (
splineDescriptor,SplineResult) — variable-point Catmull-Rom sampling with computed arc length. - STEP-edge measurement modes (
stepEdgeLengthDescriptor,StepEdgeLengthResult,StepMeasurementCandidate) withStepMeasurementSubModesub-modes:coordinate,single-edge-length,multi-edge-length,linear-point-to-point,linear-point-to-edge,linear-point-to-circle.StepMeasurementPointKindis"edge-start" | "edge-end". - Async volume measurement:
computeVolumeMeasurementAsync(objects, options)plusAsyncVolumeMeasurementOptions(signal: AbortSignal,onProgress,yieldEvery,useWorker) andVolumeMeasurementProgress(phase: "collect" | "analyze" | "integrate" | "complete",ratio). - Edge snapping:
MeasurementConfig.edgeSnapandedgeSnapPixelThresholdsnap points to loaded STEP edge overlays when the pointer is within N screen pixels of an edge. A parallelcontourSnapPixelThresholdsnaps to the active section contour. - Display-unit formatting:
MeasurementConfig.displayUnitandsetDisplayUnit(unit: Partial<MeasurementDisplayUnit>)control label scale, length suffix, area/volume suffixes, and precision. setStepMeasurementMode(mode)/getStepMeasurementMode()— toggle STEP sub-mode independently of the main measurement mode.
Public API
Lifecycle
activate(): void— turn on pointer-driven measurement placement.deactivate(): void— turn off placement and detach listeners.reset(): void— clear all measurements and pending points.
Configuration
getConfig(): MeasurementConfigupdateConfig(config: Partial<MeasurementConfig>): voidsetMeasurementMode(mode: string): void—"distance","angle","area","multipoint","aabb","obb","volume","circle","arc", or any registered descriptor name.getMeasurementMode(): string
Points
addPoint(point: THREE.Vector3, hitMesh?: THREE.Mesh | null, hitFace?: number | null): voidsetTemporaryPoint(point: THREE.Vector3 | null): voidgetAllPoints(): THREE.Vector3[]getCurrentPoints(): THREE.Vector3[]getTemporaryPoint(): THREE.Vector3 | nullupdatePointer(x: number, y: number): void— canvas-relative coords.raycast(): THREE.Intersection | null
Measurements
createMeasurement(points: THREE.Vector3[], type?: string): Measurement—typedefaults to"distance".createMeasurementWithResult(points: THREE.Vector3[], type: string, result: MeasurementResultValue): MeasurementmeasureVolume(objects: THREE.Object3D[], options?: VolumeMeasurementOptions): Measurement | nullcompleteMeasurement(): Promise<void>— manual finalize for variable-point modes (area, multipoint).getMeasurements(): Measurement[]removeMeasurement(id: string): booleansetMeasurementVisibility(id: string, visible: boolean): booleanupdateMeasurementPoint(measurementId: string, pointIndex: number, point: THREE.Vector3): boolean
Descriptors
registerDescriptor(descriptor: MeasurementTypeDescriptor): voidgetDescriptor(name: string): MeasurementTypeDescriptor | undefined
Built-in descriptors exported from the SDK: distanceDescriptor, angleDescriptor, areaDescriptor, multiPointDescriptor, splineDescriptor, aabbDescriptor, obbDescriptor, volumeDescriptor, circleDescriptor, arcDescriptor, stepEdgeLengthDescriptor. Bounding-box computers: AABBComputer, OBBComputer. Volume helpers: computeVolumeMeasurement(objects, options), computeVolumeMeasurementAsync(objects, options), normalizeVolumeOptions(options).
Events
| Event | Payload |
|---|---|
measurement:activated | null |
measurement:deactivated | null |
measurement:config-updated | { config: MeasurementConfig } |
measurement:point-added | { point, totalPoints } |
measurement:temporary-point-changed | { point } |
measurement:created | { measurement } |
measurement:volume-created | { measurement, options } |
measurement:removed | { measurement } |
measurement:visibility-changed | { id, visible } |
measurement:reset | { clearedMeasurements } |
measurement:in-progress | { mode, pointCount, canComplete } |
measurement:mode-changed | { mode, requiredPoints } |
measurement:point-updated | { measurement } |
measurement:hover-point | { measurementId, pointIndex } | null |
Example
ts
const measurement = viewer.getService<MeasurementService>("measurement");
measurement.setMeasurementMode("distance");
measurement.activate();
viewer.eventBus.on("measurement:created", ({ measurement: m }) => {
if (m.result.type === "distance") {
console.log(`distance: ${m.result.distance.toFixed(2)}`);
}
});
// Programmatic volume measurement on a selection.
const volume = measurement.measureVolume(selectedObjects, {
method: "auto",
voxelResolution: 64,
});Configuration
ts
interface MeasurementConfig {
textScalingFactor: number; // label scale base
raycastThreshold: number;
sectionEnabled: boolean; // limit raycast to contour objects
contourObjects: THREE.Object3D | THREE.Object3D[] | null;
selectableOnly: boolean; // only raycast userData.isSelectable
measurementMode: string;
autoCreateMeasurements: boolean; // finalize when requiredPoints reached
showComponents: boolean; // render dx/dy/dz legs on 2-point distance
surfaceSnap: boolean; // drape distance/multipoint along mesh
volumeMeasurement: VolumeMeasurementOptions;
}
interface VolumeMeasurementOptions {
method?: "auto" | "tetrahedron" | "voxel";
voxelResolution?: number; // default 48
voxelMaxCells?: number; // default 250000
voxelSampleMode?: "center" | "corners";
voxelAxisVotes?: boolean; // 3-axis parity voting
watertightTolerance?: number; // default 1e-5
}Result types
MeasurementResultValue is the union of DistanceResult, AngleResult, AreaResult, MultiPointResult, BoundingBoxResult, VolumeResult, CircleResult, ArcResult. Each carries a type discriminator matching the descriptor name.