@qbead/bloch-sphere
Version:
A 3D Bloch Sphere visualisation built with Three.js and TypeScript.
934 lines (908 loc) • 26.6 kB
text/typescript
import * as THREE from 'three';
import { Quaternion, Vector3, ColorRepresentation } from 'three';
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { OrbitControls } from 'three/examples/jsm/Addons.js';
/**
* Abstract base class for all components
*/
declare class BaseComponent extends THREE.Object3D {
protected _color: THREE.Color;
constructor(name?: string);
/**
* Get color of the component
*/
get color(): THREE.ColorRepresentation;
/**
* Set color of the component
*/
set color(color: THREE.ColorRepresentation);
}
/**
* Label component for displaying text in 3D space
*
* @example
* ```typescript
* const label = new Label('Hello World');
* label.position.set(0, 1, 0);
* label.color = 'red';
* label.fontSize = 2;
* blochSphere.add(label);
* ```
*
* @extends BaseComponent
*/
declare class Label extends BaseComponent {
private htmlobj;
/**
* Create a new label
* @param text The text to display
* @param type The type of label, corresponding to the html class (default: 'label')
*/
constructor(text: string, type?: string);
get text(): string;
set text(text: string);
get fontSize(): number;
set fontSize(size: number);
get color(): THREE.Color;
set color(color: THREE.ColorRepresentation);
/**
* Cleanup tasks
*/
destroy(): void;
}
declare const BlockSphereSceneOptions: {
backgroundColor: THREE.Color;
gridColor: THREE.Color;
gridDivisions: number;
sphereSkinColor: THREE.Color;
sphereSkinOpacity: number;
};
/**
* A scene for the Bloch sphere which extends the THREE.Scene class
*/
declare class BlochSphereScene extends THREE.Scene {
sphere: THREE.Group;
grids: THREE.Group;
axes: THREE.Group;
labels: Record<string, Label>;
plotStage: THREE.Group;
constructor(options?: Partial<typeof BlockSphereSceneOptions>);
get backgroundColor(): THREE.Color;
set backgroundColor(color: THREE.ColorRepresentation);
private initLabels;
clearPlot(): void;
}
/**
* A type that can be converted to a Complex number.
*/
type IntoComplex = Complex | number | [number, number];
/**
* A class representing a complex number.
*/
declare class Complex {
real: number;
imag: number;
static get ZERO(): Complex;
static get ONE(): Complex;
static get I(): Complex;
constructor(real: number, imag?: number);
static from(value: IntoComplex): Complex;
static from(real: number, imag?: number): Complex;
static random(): Complex;
static unitRandom(): Complex;
static fromPolar(magnitude: number, phase: number): Complex;
copy(other: Complex): this;
clone(): Complex;
plus(other: IntoComplex): Complex;
minus(other: IntoComplex): Complex;
times(other: IntoComplex): Complex;
dividedBy(other: IntoComplex): Complex;
get magnitude(): number;
get phase(): number;
conjugate(): Complex;
reciprocal(): Complex;
pow(exponent: number): Complex;
sqrt(): Complex;
toString(): string;
}
/**
* Quantum operators are 2x2 matrices of complex numbers
*
* ```
* [ a b ]
* [ c d ]
* ```
*/
declare class Operator {
elements: Complex[][];
static identity(): Operator;
constructor(elements: Complex[][]);
/**
* The first row, first column element of the operator
*/
get a(): Complex;
/**
* The first row, second column element of the operator
*/
get b(): Complex;
/**
* The second row, first column element of the operator
*/
get c(): Complex;
/**
* The second row, second column element of the operator
*/
get d(): Complex;
copy(other: Operator): this;
clone(): Operator;
/**
* Multiply the operator by a scalar
*/
scale(scalar: number): this;
/**
* Multiply the operator by another operator
*/
times(other: Operator): Operator;
/**
* Get the conjugate transpose of the operator as a new operator
*/
conjugateTranspose(): Operator;
/**
* Apply this operator to a density matrix
*/
applyTo(rho: Complex[][]): Complex[][];
/**
* Add another operator to this operator
*/
plus(other: Operator): Operator;
/**
* Get the determinant of the operator
*/
determinant(): Complex;
/**
* Get this operator as a THREE.Quaternion
*/
quaternion(): Quaternion;
}
/**
* A class representing a Bloch vector
*
* Angle `theta` is the angle between the Bloch vector and the z-axis
* Angle `phi` is the "timezone" angle, the angle from the x-axis in the xy-plane
*
* This class extends the Vector3 class from three.js and provides additional
* functionality.
*
* @example
* ```ts
* const blochVector = BlochVector.from(1, 0, 0)
* const blochVector2 = BlochVector.from([0, 1, 0])
* const blochVector3 = BlochVector.fromAngles(0.5 * Math.PI, 1.5 * Math.PI)
* const blochVector4 = BlochVector.MINUS_I
* const blochVector5 = BlochVector.random()
* ```
*/
declare class BlochVector extends Vector3 {
/**
* A bloch vector representing the zero state
*/
static get ZERO(): BlochVector;
/**
* A bloch vector representing the one state
*/
static get ONE(): BlochVector;
/**
* A bloch vector representing the plus state (|+>) or (|0> + |1>)/sqrt(2)
*/
static get PLUS(): BlochVector;
/**
* A bloch vector representing the minus state (|->) or (|0> - |1>)/sqrt(2)
*/
static get MINUS(): BlochVector;
/**
* A bloch vector representing the imaginary state (|i>) or (|0> + i|1>)/sqrt(2)
*/
static get I(): BlochVector;
/**
* A bloch vector representing the minus imaginary state (|-i>) or (|0> - i|1>)/sqrt(2)
*/
static get MINUS_I(): BlochVector;
/**
* Generate a random Bloch vector with magnitude 1
*/
static random(): BlochVector;
/**
* Create a zero state Bloch vector
*/
static zero(): BlochVector;
/**
* Utility function to create a bloch vector in many ways
*
* - `BlochVector.from(x, y, z)` creates a Bloch vector from x, y, z coordinates
* - `BlochVector.from([x, y, z])` creates a Bloch vector from an array of coordinates
* - `BlochVector.from(new Vector3(x, y, z))` creates a Bloch vector from a three.js Vector3
* - `BlochVector.from(new BlochVector(x, y, z))` creates a Bloch vector from another Bloch vector
*/
static from(x: number, y: number, z: number): BlochVector;
static from(y: Vector3): BlochVector;
static from(array: [number, number, number]): BlochVector;
static from(q: BlochVector): BlochVector;
/**
* Create a Bloch vector from angles (theta, phi)
*/
static fromAngles(theta: number, phi: number): BlochVector;
/** The polar angle. The angle between the BlochVector and the z-axis */
get theta(): number;
/** The azimuthal xy-plane angle. The angle between the projection of the BlochVector on the xy-plane
and the x-axis */
get phi(): number;
/** The amplitude of the Bloch vector */
get amplitude(): number;
/** The density matrix representation of the Bloch vector */
get rho(): Complex[][];
/** The density matrix representation of the Bloch vector */
densityMatrix(): Complex[][];
/**
* Create a Bloch vector from a density matrix
*
* @param rho - The density matrix to create the Bloch vector from
*/
static fromDensityMatrix(rho: Complex[][]): BlochVector;
/**
* Apply an operator to the Bloch vector returning a new Bloch vector
*
* @param op - The operator to apply
* @returns The new Bloch vector
*/
applyOperator(op: Operator): BlochVector;
/**
* Get both angles of the Bloch vector as an array `[theta, phi]`
*/
angles(): [number, number];
/**
* Set the Bloch vector from angles `[theta, phi]` (polar, azimuthal)
*
* @param angles - The angles to set the Bloch vector to
*/
setAngles(angles: [number, number]): this;
toString(): string;
/**
* Spherical linear interpolation of this Bloch vector to another Bloch vector
*
* @param other - The other Bloch vector to interpolate to
* @param t - The interpolation factor (0 <= t <= 1)
* @returns The interpolated Bloch vector
*/
slerpTo(other: BlochVector, t: number): BlochVector;
}
/**
* Camera state for the Bloch sphere
*/
type CameraState = {
/** Angle from Z-axis in radians (0 to π) - polar angle */
theta?: number;
/** Angle from X-axis in XY-plane in radians (0 to 2π) - azimuthal angle */
phi?: number;
/** Camera zoom level (1.0 = default, >1 = zoomed in, <1 = zoomed out) */
zoom?: number;
};
/**
* Interactivity options for OrbitControls
*/
type InteractivityOptions = {
/** Enable/disable zoom via mouse wheel/pinch */
zoom?: boolean;
/** Enable/disable camera rotation via mouse drag */
rotate?: boolean;
};
/**
* Options for the Bloch Sphere widget
*/
type BlochSphereOptions = {
/** font size in em */
fontSize?: number;
/** show grid */
showGrid?: boolean;
/** initial camera state */
cameraState?: CameraState;
/** Enable/disable all user interactions (default: true) */
interactive?: boolean;
/** Enable/disable zoom via mouse wheel/pinch (default: true) */
enableZoom?: boolean;
/** Enable/disable camera rotation via mouse drag (default: true) */
enableRotate?: boolean;
} & Partial<typeof BlockSphereSceneOptions>;
/**
* A Bloch Sphere Widget
*
* This class is a wrapper around the THREE.js WebGLRenderer and CSS2DRenderer
* to create a Bloch sphere visualization.
*
* It provides methods to add and remove objects from the scene.
*
* It must be attached to a parent element in the DOM to be visible. It will
* automatically resize to fit the parent element.
*
* To resize on window resize, you can call the `resize` method in the
* window resize event listener.
*
* @example
* ```ts
* import { BlochSphere } from 'bloch-sphere'
*
* const blochSphere = new BlochSphere({
* fontSize: 1.25,
* })
*
* blochSphere.attach(document.body)
* window.addEventListener(
* 'resize',
* () => {
* blochSphere.resize()
* },
* { passive: true }
* )
*
* // create a qubit display
* const qubit = new QubitDisplay(BlochVector.fromAngles(1, 0))
* // add the qubit to the Bloch sphere
* blochSphere.add(qubit)
* ```
*/
declare class BlochSphere {
renderer: THREE.WebGLRenderer;
cssRenderer: CSS2DRenderer;
el: HTMLElement;
scene: BlochSphereScene;
camera: THREE.OrthographicCamera;
controls: OrbitControls;
private _cameraAnimation;
constructor(options?: BlochSphereOptions);
setOptions(options?: BlochSphereOptions): void;
private initRenderer;
get showGrid(): boolean;
set showGrid(value: boolean);
add(item: THREE.Object3D): void;
remove(item: THREE.Object3D): void;
/**
* Removes all objects from the plot
*
* This will not remove the grid or the sphere.
*/
clearPlot(): void;
/**
* Rescales the sphere
*/
scale(size: number): void;
/**
* Attaches the widget to a parent element
*
* Must be called to make the widget visible.
*/
attach(parent?: HTMLElement): void;
/**
* Resizes the widget to fit the parent element
*
* Optionally, you can specify the width and height to resize to.
*/
resize(width?: number, height?: number): void;
/**
* Renders the scene
*
* This is called automatically in the animation loop unless that
* loop is stopped.
*/
render(): void;
/**
* Starts the animation loop
*
* Automatically started when the widget is attached to a parent element.
*
* This will call the render method automatically.
*/
start(): void;
/**
* Stops the animation loop
*
* This will stop the render loop
*/
stop(): void;
/**
* Core method to set camera state with optional animation
* This is the single source of truth for camera positioning - other methods delegate to this
*/
private _setCameraState;
/**
* Immediately apply camera state without animation
*/
private _applyCameraState;
/**
* Get current camera zoom level
*/
getCameraZoom(): number;
/**
* Get current camera angles as [theta, phi]
*/
getCameraAngles(): [number, number];
/**
* Get the Bloch vector pointing from origin to camera
*/
getCameraBlochVector(): BlochVector;
/**
* Set camera state (unified method)
*/
setCameraState(cameraState: CameraState, duration?: number, easing?: string): Promise<void> | void;
/**
* Position camera such that the given Bloch vector points directly at camera
*/
setCameraToBlochVector(blochVector: BlochVector, duration?: number, easing?: string): Promise<void> | void;
/**
* Set camera position using spherical coordinates
*/
setCameraAngles(theta: number, phi: number, duration?: number, easing?: string): Promise<void> | void;
/**
* Set camera zoom level
*/
setCameraZoom(zoomLevel: number, duration?: number, easing?: string): Promise<void> | void;
/**
* Control user interactivity with the camera
*
* @param options - Interactivity options or boolean to enable/disable all interactions
* @returns Current interactivity state if no arguments provided
*/
interactivity(options?: InteractivityOptions | boolean): InteractivityOptions;
/**
* Performs cleanup and disposes everything contained in the widget
*/
dispose(): void;
}
/**
* A display for just a qubit arrow
*
* @see {@link QubitDisplay} for a full qubit display
*
* @example
* ```ts
* const q = new BlochVector(0, 0, 1)
* const arrow = new QubitArrow()
* arrow.color = 0xe1b53e
* arrow.follow(q)
* blochSphere.add(arrow)
* ```
*/
declare class QubitArrow extends BaseComponent {
private arrowHelper;
label: Label;
constructor();
set color(color: THREE.ColorRepresentation);
follow(v: BlochVector): void;
}
type DegreesUnits = 'deg' | 'DEG' | 'degrees';
type RadiansUnits = 'rad' | 'RAD' | 'radians';
type AngleUnits = DegreesUnits | RadiansUnits;
/**
* Display angle indicators for a Bloch vector
*
* @example
* ```ts
* const angleIndicators = new AngleIndicators()
* angleIndicators.update(blochVector)
* angleIndicators.color = 0xe1b53e
* blochSphere.add(angleIndicators)
* ```
*/
declare class AngleIndicators extends BaseComponent {
units: AngleUnits;
private phiWedge;
private phiLabel;
private thetaLabelContainer;
private thetaWedge;
private thetaLabel;
private phiLabelContainer;
private _phiColor;
private _thetaColor;
/**
* Creates a new AngleIndicators component
*
* @param scale - The scale of the angle indicators (default is 0.25)
*/
constructor(scale?: number);
/**
* Update the angle indicators for the given Bloch vector
*/
update(v: BlochVector): void;
get opacity(): number;
set opacity(opacity: number);
/**
* The distance of the labels from the center of the sphere
*/
get labelRadius(): number;
set labelRadius(radius: number);
set color(color: THREE.ColorRepresentation);
get phiColor(): THREE.ColorRepresentation;
set phiColor(color: THREE.ColorRepresentation);
get thetaColor(): THREE.ColorRepresentation;
set thetaColor(color: THREE.ColorRepresentation);
}
/**
* Creates a wedge, that is the outline of
* a quarter of a circle.
*/
declare class Wedge extends BaseComponent {
constructor();
}
/**
* A wedge which is a quarter of a circle
*/
declare class QubitProjWedge extends Wedge {
constructor();
follow(v: BlochVector): void;
}
type AnimationCallback = (progress: number) => void;
/** @deprecated Use CancellablePromise instead */
type CancelAnimation = () => void;
interface CancellablePromise<T> extends Promise<T> {
cancel(): void;
}
/**
* Animation helper function
*
* This helper will set up an animation loop and call the callback function
* with the progress of the animation.
*
* Returns a promise that resolves when the animation completes. The promise
* has a cancel() method that can be called to stop the animation early.
* If the callback throws an error, the promise will reject with that error.
*
* @param callback - The function to call with the progress of the animation.
* @param duration - The duration of the animation in milliseconds (default is 1000).
* @param easing - The easing function to use (default is 'linear').
* @param loop - Whether to loop the animation (default is false). Looping animations never resolve unless cancelled.
* @returns A cancellable promise that resolves when the animation completes or rejects if the callback throws an error.
*
* @example
* ```ts
* // await animation completion
* await animate((progress) => {
* myElement.style.opacity = progress
* }, 1000, 'easeInOut')
*
* // looping animation with cancellation
* const anim = animate((progress) => {
* myElement.style.opacity = progress
* }, 1000, 'easeInOut', true)
* // later...
* anim.cancel()
*
* // handle errors
* try {
* await animate((progress) => {
* if (someCondition) throw new Error('Animation failed')
* myElement.style.opacity = progress
* }, 1000)
* } catch (error) {
* console.error('Animation error:', error)
* }
* ```
*/
declare function animate(callback: AnimationCallback, duration?: number, easing?: string, loop?: boolean): CancellablePromise<void>;
/**
* A display for a qubit state on the Bloch sphere
*
* This component shows the arrow, angle indicators, and a label.
*
* @example
* ```ts
* const q = new BlochVector(0, 0, 1)
* const qubit = new QubitDisplay(q)
* qubit.color = 0xe1b53e
* blochSphere.add(qubit)
* ```
*/
declare class QubitDisplay extends BaseComponent {
arrow: QubitArrow;
wedge: QubitProjWedge;
angleIndicators: AngleIndicators;
state: BlochVector;
private _anim;
constructor(q?: BlochVector);
set color(color: ColorRepresentation);
/**
* Set the bloch vector state of the display
*
* Can also be used to animate the state of the qubit.
*
* @param q - The new Bloch vector state to set.
* @param duration - The duration of the animation (default is 0).
* @param easing - The easing function to use for the animation (default is 'quadInOut').
*/
set(q: BlochVector, duration?: number, easing?: string): CancellablePromise<void> | undefined;
}
/**
* A display for a path on the Bloch sphere
*
* The path is defined by a series of Bloch vectors.
*
* @example
* ```ts
* const path = new PathDisplay([
* BlochVector.fromAngles(0, 0),
* BlochVector.fromAngles(1, 0.1),
* BlochVector.fromAngles(1, 1),
* ])
* path.color = 0xc33175
* blochSphere.add(path)
* ```
*/
declare class PathDisplay extends BaseComponent {
constructor(path?: BlochVector[]);
set(vertices: BlochVector[]): void;
}
/**
* A display for a region on the Bloch sphere
*
* This component shows a spherical polygon on the Bloch sphere.
*
* @example
* ```ts
* const region = new RegionDisplay([
* BlochVector.fromAngles(0, 0),
* BlochVector.fromAngles(1, 0.1),
* BlochVector.fromAngles(1, 1),
* ])
* region.color = 0xe1b53e
* blochSphere.add(region)
* ```
*/
declare class RegionDisplay extends BaseComponent {
private sphere;
constructor(region?: BlochVector[]);
get color(): THREE.ColorRepresentation;
set color(color: THREE.ColorRepresentation);
/**
* Set the region of the display
*
* @param points - The bloch vectors that define the region.
*/
setRegion(points: BlochVector[]): void;
}
/**
* A display for points on the Bloch sphere
*
* @example
* ```ts
* // generate 100 randomly
* const points = new PointsDisplay(
* Array.from({ length: 100 }, () => BlochVector.random())
* )
* points.color = 0xe1b53e
* points.pointSize = 10
* blochSphere.add(points)
* ```
*/
declare class PointsDisplay extends BaseComponent {
private pointMaterial;
constructor(points?: BlochVector[]);
/**
* Set the size of the points
*/
get pointSize(): number;
set pointSize(size: number);
/**
* Set the color of the points
*/
set color(color: THREE.ColorRepresentation);
/**
* Set the points to display
*/
set(points: BlochVector[]): void;
}
/**
* A display for a quantum operator
*
* @example
* ```ts
* const op = gates.hadamard()
* const display = new OperatorDisplay(op)
* blochSphere.add(display)
* ```
*/
declare class OperatorDisplay extends BaseComponent {
operator: Operator;
innerGroup: THREE.Group;
label: Label;
private anim;
constructor(op?: Operator);
/**
* Set the operator to display
* @param op The operator to display
*/
set(op: Operator): void;
/**
* Perform cleanup tasks
*/
dispose(): void;
}
/**
* A display for the path a qbit takes when it is rotated by an operator
* @example
* ```ts
* const op = gates.hadamard()
* const v = new BlochVector(0, 0, 1)
* const display = new OperatorPathDisplay(v, op)
* blochSphere.add(display)
* ```
*/
declare class OperatorPathDisplay extends BaseComponent {
operator: Operator;
vector: BlochVector;
innerGroup: THREE.Group;
path: THREE.Mesh;
disc: THREE.Mesh;
constructor(op?: Operator, v?: BlochVector);
/**
* Set the operator and vector
*/
set(op: Operator, v: BlochVector): void;
/**
* Set the operator
*/
setOperator(op: Operator): void;
/**
* Set the vector
*/
setVector(v: BlochVector): void;
}
/**
* The identity operator
*/
declare function identity(): Operator;
/**
* The Pauli-X operator (also known as NOT or bit-flip operator)
*/
declare function x(): Operator;
/**
* Alias for the Pauli-X operator
*/
declare const not: typeof x;
/**
* The Pauli-Y operator
*/
declare function y(): Operator;
/**
* The Pauli-Z operator
*/
declare function z(): Operator;
/**
* The Hadamard operator
*
* Often used to create superposition states from the |0> state
*/
declare function hadamard(): Operator;
/**
* The phase operator
*
* Applies a phase of `phi` to the |1> state
*/
declare function phase(phi: number): Operator;
/**
* Rotation around the X axis
* Applies a rotation of `theta` radians around the X axis
*/
declare function rx(theta: number): Operator;
/**
* Rotation around the Y axis
* Applies a rotation of `theta` radians around the Y axis
*/
declare function ry(theta: number): Operator;
/**
* Rotation around the Z axis
* Applies a rotation of `theta` radians around the Z axis
*/
declare function rz(theta: number): Operator;
declare const gates_hadamard: typeof hadamard;
declare const gates_identity: typeof identity;
declare const gates_not: typeof not;
declare const gates_phase: typeof phase;
declare const gates_rx: typeof rx;
declare const gates_ry: typeof ry;
declare const gates_rz: typeof rz;
declare const gates_x: typeof x;
declare const gates_y: typeof y;
declare const gates_z: typeof z;
declare namespace gates {
export {
gates_hadamard as hadamard,
gates_identity as identity,
gates_not as not,
gates_phase as phase,
gates_rx as rx,
gates_ry as ry,
gates_rz as rz,
gates_x as x,
gates_y as y,
gates_z as z,
};
}
/**
* Properties of a arc on a sphere
*/
type ArcProperties = {
/**
* The radius of the arc
*/
radius: number;
/**
* The distance from the center of the sphere to the plane containing the arc
*/
height: number;
/**
* The normal vector of the plane containing the arc
*/
norm: THREE.Vector3;
/**
* The angle of the arc offset from the x-axis
*/
arcOffset: number;
/**
* The angle of the arc
*/
arcAngle: number;
};
/**
* Gets properties of an arc starting from v and rotating about n by angle
*/
declare function getRotationArc(v: THREE.Vector3, axis: THREE.Vector3, angle: number): ArcProperties;
/**
* Gets the properties of an great arc between two vectors
* @param v1 The first vector
* @param v2 The second vector
* @returns The properties of the great arc
*/
declare function getArcBetween(v1: THREE.Vector3, v2: THREE.Vector3): {
norm: THREE.Vector3;
arcOffset: number;
arcAngle: number;
};
/**
* Computes the shortest distance between two angles, considering wrap-around
* @param a0 The first angle
* @param a1 The second angle
* @param modulo The modulo value (e.g., 2π for radians)
* @returns The shortest distance from a0 to a1
*/
declare function shortestModDist(a0: number, a1: number, modulo: number): number;
type RotationInfo = {
axis: THREE.Vector3;
angle: number;
};
/**
* Get axis of rotation and angle from a quaternion
* @param q The quaternion
* @returns The axis of rotation
*/
declare function axisFromQuaternion(q: THREE.Quaternion): RotationInfo;
/**
* Standard linear interpolation function
*/
declare function lerp(a: number, b: number, t: number): number;
/**
* Linear interpolation function that wraps around 2π
*/
declare function lerpAngle(a: number, b: number, t: number): number;
/**
* Format a vector as a string with a given precision.
*/
declare function formatVector(v: Vector3, precision?: number): string;
/**
* Format an angle given in radians as a string in degrees
* with a given precision.
*/
declare function formatDegrees(radians: number, precision?: number): string;
/**
* Format an angle given in radians as a string in radians
* with a given precision.
*/
declare function formatRadians(radians: number, precision?: number): string;
export { AngleIndicators, BaseComponent, BlochSphere, BlochSphereScene, BlochVector, Complex, Label, Operator, OperatorDisplay, OperatorPathDisplay, PathDisplay, PointsDisplay, QubitArrow, QubitDisplay, QubitProjWedge, RegionDisplay, Wedge, animate, axisFromQuaternion, formatDegrees, formatRadians, formatVector, gates, getArcBetween, getRotationArc, lerp, lerpAngle, shortestModDist };
export type { AnimationCallback, ArcProperties, BlochSphereOptions, CancelAnimation, CancellablePromise, IntoComplex, RotationInfo };