MapSpace
The VPS map coordinate frame, positioned once per localization so every child follows
import { MapSpace } from '@multisetai/vps/three';MapSpace marks one object as the origin of the scanned map. Nest your map-anchored content underneath it, and on every successful localization the frame is moved so its origin coincides with the map's origin. Every descendant follows by ordinary parenting.
A child's local position is therefore its map coordinate. You can paste a value straight from the portal's Map Viewer into a child's Transform.
This is what makes relocalization cheap. Only one transform changes, so anything computed relative to the frame stays valid: no re-anchoring, no recompute, and no visible jump when localization runs again in the background.
Two forms
Package class
import { MapSpace } from '@multisetai/vps/three'
Plain Three.js, or from code in a Needle project
Needle component
import { MapSpace } from './MapSpace.js'
Unity, as a template file copied into your src/scripts/ folder
The Needle component is a thin Behaviour that owns a package MapSpace bound to its GameObject and forwards to it, so behaviour is identical. The Unity to Three.js handedness correction lives in the package class, which keeps one verified implementation rather than a copy in every project.
Copy the template into your project with:
cp node_modules/@multisetai/vps/templates/MapSpace.ts <your-web-folder>/src/scripts/Constructor
const mapSpace = new MapSpace(new THREE.Object3D(), { hideUntilLocalized: true });
scene.add(mapSpace.object);
mapSpace.connect(adapter);object
THREE.Object3D
The object to use as the map frame. Add it to your scene yourself.
options
IMapSpaceOptions
Optional. See below.
Options and fields
hideUntilLocalized
boolean
true
Hide the object until the first localization, and re-hide it when the session ends. This is a public mutable field, read live, so you can set it to false at runtime to keep content visible between sessions.
Properties
object
THREE.Object3D
The wrapped object. Add this to your scene, and parent map content to it.
isLocalized
boolean
true once a localization has been applied in the current session.
Methods
connect(adapter: IVpsAdapter)
Subscribe to an adapter so the frame is positioned on every localization. Accepts ThreeAdapter or NeedleAdapter. Safe to call more than once, since previous subscriptions are removed first. If the adapter has already localized in the current session, that result is replayed immediately, so a MapSpace created mid-session is positioned at once.
disconnect()
Remove every subscription made by connect().
applyLocalization(result, worldFromMap)
Position the frame directly, bypassing events. Useful in tests, or from a component that receives the result by another route.
add(object: THREE.Object3D, mapCoordinate?: THREE.Vector3)
Parent an object to the frame, optionally at a map coordinate.
mapToWorld(v: THREE.Vector3, target?)
Convert map space to world space.
worldToMap(v: THREE.Vector3, target?)
Convert world space to map space. Use this to convert the camera pose before any map-space calculation.
dispose()
Same as disconnect().
Static methods
MapSpace.toLocal(unityCoord: THREE.Vector3, target?)
Convert a Unity (left-handed) coordinate copied from the portal's Map Viewer into map space.
Content authored in the Unity editor gets this conversion from Needle's exporter and must not use toLocal. Content created in code does need it:
Hiding behavior
In Unity
MultisetVPS discovers and wires all MapSpace components at startup. A scene should contain exactly one. MultisetVPS logs a warning if it finds more, because they would all be moved to the same origin.
The Needle component exposes connectAdapter(adapter) and applyLocalization(result, worldFromMap), matching MapAnchor, so it can also be registered at runtime with NeedleAdapter.registerAnchor(mapSpace).
Reach the underlying package object through .space:
MapSpace or MapAnchor
MapSpace is the preferred model for anything with more than one anchored object, because the layout you build in the editor is the layout you get in AR, with no per-object component and no offset fields to keep in sync.
Last updated
Was this helpful?

