Camera

Introduction

vtkCamera is a virtual camera for 3D rendering. It provides methods
to position and orient the view point and focal point. Convenience
methods for moving about the focal point also are provided. More
complex methods allow the manipulation of the computer graphics model
including view up vector, clipping planes, and camera perspective.

Methods

applyTransform

Apply a transform to the camera.
The camera position, focal-point, and view-up are re-calculated
using the transform’s matrix to multiply the old points by the new transform.

Argument Type Required Description
transformMat4 mat4 Yes Transform matrix.

azimuth

Rotate the camera about the view up vector centered at the focal point.

Argument Type Required Description
angle Number Yes The angle value.

computeCameraLightTransform

computeClippingRange

Argument Type Required Description
bounds Bounds Yes The bounds value.

computeDistance

This method must be called when the focal point or camera position changes

computeViewParametersFromPhysicalMatrix

the provided matrix should include translation and orientation only mat
is physical to view

Argument Type Required Description
mat mat4 Yes The physical matrix.

computeViewParametersFromViewMatrix

Argument Type Required Description
vmat mat4 Yes The view matrix.

deepCopy

Not implemented yet

Argument Type Required Description
sourceCamera vtkCamera Yes The camera source.

dolly

Move the position of the camera along the view plane normal. Moving
towards the focal point (e.g., > 1) is a dolly-in, moving away
from the focal point (e.g., < 1) is a dolly-out.

Argument Type Required Description
amount Number Yes The amount value.

elevation

Rotate the camera about the cross product of the negative of the
direction of projection and the view up vector, using the focal point as
the center of rotation.

Argument Type Required Description
angle Number Yes The angle value.

extend

Method use to decorate a given object (publicAPI+model) with vtkRenderer characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues ICameraInitialValues No (default: {})

getCameraLightTransformMatrix

Not implemented yet

getClippingRange

Get the location of the near and far clipping planes along the direction
of projection.

getClippingRangeByReference

Get the location of the near and far clipping planes along the direction
of projection.

getCompositeProjectionMatrix

Argument Type Required Description
aspect Number Yes Camera frustum aspect ratio.
nearz Number Yes Camera frustum near plane.
farz Number Yes Camera frustum far plane.

getDirectionOfProjection

Get the vector in the direction from the camera position to the focal
point.

getDirectionOfProjectionByReference

Get the vector in the direction from the camera position to the focal
point.

getDistance

Get the distance from the camera position to the focal point.

getFocalPoint

Get the focal of the camera in world coordinates.

getFocalPointByReference

Get the focal of the camera in world coordinates.

getFreezeFocalPoint

Get the value of the FreezeDolly instance variable.

getFrustumPlanes

Not implemented yet
Get the plane equations that bound the view frustum.

Argument Type Required Description
aspect Number Yes Camera frustum aspect ratio.

getOrientation

Not implemented yet
Get the orientation of the camera.

getOrientationWXYZ

Not implemented yet
Get the orientation of the camera.

getParallelProjection

Get the value of the ParallelProjection instance variable.
This determines if the camera should do a perspective or parallel projection.

getParallelScale

Get the scaling used for a parallel projection.

getPhysicalScale

getPhysicalToWorldMatrix

Argument Type Required Description
result mat4 Yes The world matrix.

getPhysicalTranslation

getPhysicalTranslationByReference

getPhysicalViewNorth

getPhysicalViewNorthByReference

getPhysicalViewUp

getPhysicalViewUpByReference

getPosition

Get the position of the camera in world coordinates.

getPositionByReference

Get the position of the camera in world coordinates.

getProjectionMatrix

Get the projection matrix.

Argument Type Required Description
aspect Number Yes Camera frustum aspect ratio.
nearz Number Yes Camera frustum near plane.
farz Number Yes Camera frustum far plane.

getRoll

Not implemented yet
Get the roll angle of the camera about the direction of projection.

getScreenBottomLeft

Get top left corner point of the screen.

getScreenBottomLeftByReference

getScreenBottomRight

Get bottom left corner point of the screen

getScreenBottomRightByReference

getScreenTopRight

getScreenTopRightByReference

getThickness

Get the center of the window in viewport coordinates.

getUseHorizontalViewAngle

Get the value of the UseHorizontalViewAngle.

getUseOffAxisProjection

Get use offaxis frustum.

getViewAngle

Get the camera view angle.

getViewMatrix

getViewPlaneNormal

Get the ViewPlaneNormal.
This vector will point opposite to the direction of projection,
unless you have created a sheared output view using SetViewShear/SetObliqueAngles.

getViewPlaneNormalByReference

Get the ViewPlaneNormal by reference.

getViewUp

Get ViewUp vector.

getViewUpByReference

Get ViewUp vector by reference.

getWindowCenter

Get the center of the window in viewport coordinates.
The viewport coordinate range is ([-1,+1],[-1,+1]).

getWindowCenterByReference

getWorldToPhysicalMatrix

Argument Type Required Description
result mat4 Yes

newInstance

Method use to create a new instance of vtkCamera with its focal point at the origin,
and position=(0,0,1). The view up is along the y-axis, view angle is 30 degrees,
and the clipping range is (.1,1000).

Argument Type Required Description
initialValues ICameraInitialValues No for pre-setting some of its content

orthogonalizeViewUp

Recompute the ViewUp vector to force it to be perpendicular to
camera.focalpoint vector.

physicalOrientationToWorldDirection

Argument Type Required Description
ori Array. Yes

pitch

Rotate the focal point about the cross product of the view up vector and
the direction of projection, using the camera’s position as the center of
rotation.

Argument Type Required Description
angle Number Yes The value of the angle.

roll

Rotate the camera about the direction of projection.

Argument Type Required Description
angle Number Yes The value of the angle.

setClippingRange

Set the location of the near and far clipping planes along the direction
of projection.

Argument Type Required Description
clippingRange Range Yes

setClippingRange

Set the location of the near and far clipping planes along the direction
of projection.

Argument Type Required Description
near Number Yes The near clipping planes.
far Number Yes The far clipping planes.

setClippingRangeFrom

Set the location of the near and far clipping planes along the direction
of projection.

Argument Type Required Description
clippingRange Range Yes

setDeviceAngles

Used to handle convert js device orientation angles
when you use this method the camera will adjust to the
device orientation such that the physicalViewUp you set
in world coordinates looks up, and the physicalViewNorth
you set in world coorindates will (maybe) point north

NOTE WARNING - much of the documentation out there on how
orientation works is seriously wrong. Even worse the Chrome
device orientation simulator is completely wrong and should
never be used. OMG it is so messed up.

how it seems to work on iOS is that the device orientation
is specified in extrinsic angles with a alpha, beta, gamma
convention with axes of Z, X, Y (the code below substitutes
the physical coordinate system for these axes to get the right
modified coordinate system.

Argument Type Required Description
alpha Number Yes The value of the alpha.
beta Number Yes The value of the beta.
gamma Number Yes The value of the gamma.
screen Number Yes The value of the screen.

setDirectionOfProjection

Set the direction of projection.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setDistance

Move the focal point so that it is the specified distance from the camera
position.

This distance must be positive.

Argument Type Required Description
distance Number Yes The value of the distance.

setFocalPoint

Set the focal of the camera in world coordinates.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setFocalPointFrom

Set the focal of the camera in world coordinates.

Argument Type Required Description
focalPoint Vector3 Yes

setFreezeFocalPoint

Set the value of the FreezeDolly instance variable.

Argument Type Required Description
freezeFocalPoint Boolean Yes

setObliqueAngles

Not implement yet
Set the oblique viewing angles.
The first angle, alpha, is the angle (measured from the horizontal) that
rays along the direction of projection will follow once projected onto
the 2D screen. The second angle, beta, is the angle between the view
plane and the direction of projection. This creates a shear transform x’
= x + dzcos(alpha)/tan(beta), y’ = dzsin(alpha)/tan(beta) where dz is
the distance of the point from the focal plane. The angles are (45,90) by
default. Oblique projections commonly use (30,63.435).

Argument Type Required Description
alpha Number Yes The aplha angle value.
beta Number Yes The beta angle value.

setOrientationWXYZ

Set the value of the OrientationWXYZ.

Argument Type Required Description
degrees Number Yes
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setParallelProjection

Set the value of the ParallelProjection.

Argument Type Required Description
parallelProjection Boolean Yes The value of the parallelProjection.

setParallelScale

Set the value of the parallelScale.

Argument Type Required Description
parallelScale Number Yes The value of the parallelScale.

setPhysicalScale

Set the value of the physicalScale.

Argument Type Required Description
physicalScale Number Yes The value of the the physicalScale.

setPhysicalTranslation

Set the value of the physicalTranslation.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setPhysicalTranslationFrom

Set the value of the physicalTranslation.

Argument Type Required Description
physicalTranslation Array. Yes The value of the physicalTranslation.

setPhysicalViewNorth

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setPhysicalViewNorthFrom

Argument Type Required Description
physicalViewNorth Array. Yes

setPhysicalViewUp

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setPhysicalViewUpFrom

Argument Type Required Description
physicalViewUp Array. Yes

setPosition

Set the position of the camera in world coordinates.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setProjectionMatrix

Argument Type Required Description
mat mat4 Yes

setRoll

Set the roll angle of the camera about the direction of projection.

Argument Type Required Description
angle Number Yes The value of the roll angle.

setScreenBottomLeft

Set top left corner point of the screen.

This will be used only for offaxis frustum calculation.

Argument Type Required Description
screenBottomLeft Vector3 Yes The screenBottomLeft coordiante.

setScreenBottomLeft

Set top left corner point of the screen.

This will be used only for offaxis frustum calculation.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setScreenBottomLeftFrom

Set top left corner point of the screen.

Argument Type Required Description
screenBottomLeft Vector3 Yes The screenBottomLeft coordiante.

setScreenBottomRight

Set bottom right corner point of the screen.

Argument Type Required Description
screenBottomRight Vector3 Yes The screenBottomRight coordiante.

setScreenBottomRight

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setScreenBottomRightFrom

Set bottom right corner point of the screen.

Argument Type Required Description
screenBottomRight Vector3 Yes The screenBottomRight coordiante.

setScreenTopRight

Set top right corner point of the screen.

This will be used only for offaxis frustum calculation.

Argument Type Required Description
screenTopRight Vector3 Yes The screenTopRight coordiante.

setScreenTopRight

Set top right corner point of the screen.

This will be used only for offaxis frustum calculation.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setScreenTopRightFrom

Set top right corner point of the screen.

Argument Type Required Description
screenTopRight Vector3 Yes The screenTopRight coordiante.

setThickness

Set the distance between clipping planes.

This method adjusts the far clipping plane to be set a distance
‘thickness’ beyond the near clipping plane.

Argument Type Required Description
thickness Number Yes

setThicknessFromFocalPoint

Argument Type Required Description
thickness Number Yes The value of the thickness.

setUseHorizontalViewAngle

Set the value of the useHorizontalViewAngle.

Argument Type Required Description
useHorizontalViewAngle Boolean Yes The value of the useHorizontalViewAngle.

setUseOffAxisProjection

Set use offaxis frustum.

OffAxis frustum is used for off-axis frustum calculations specifically for
stereo rendering. For reference see “High Resolution Virtual Reality”, in
Proc. SIGGRAPH ‘92, Computer Graphics, pages 195-202, 1992.

Argument Type Required Description
useOffAxisProjection Boolean Yes The value of the useOffAxisProjection.

setViewAngle

Set the camera view angle, which is the angular height of the camera view
measured in degrees.

Argument Type Required Description
viewAngle Number Yes The value of the viewAngle.

setViewMatrix

Set the view matrix for the camera.

Argument Type Required Description
mat mat4 Yes The value of the view matrix.

setViewUp

Set the view up direction for the camera.

Argument Type Required Description
viewUp Vector3 Yes The viewUp coordinate.

setViewUp

Set the view up direction for the camera.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

setViewUpFrom

Set the view up direction for the camera.

Argument Type Required Description
viewUp Vector3 Yes The viewUp coordinate.

setWindowCenter

Set the center of the window in viewport coordinates.

The viewport coordinate range is ([-1,+1],[-1,+1]).

This method is for if you have one window which consists of several
viewports, or if you have several screens which you want to act together
as one large screen.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.

setWindowCenterFrom

Set the center of the window in viewport coordinates from an array.

Argument Type Required Description
windowCenter Range Yes

translate

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

yaw

Rotate the focal point about the view up vector, using the camera’s
position as the center of rotation.

Argument Type Required Description
angle Number Yes The value of the angle.

zoom

In perspective mode, decrease the view angle by the specified factor.

Argument Type Required Description
factor Number Yes The value of the zoom factor.

Source

index.d.ts
import { mat4 } from 'gl-matrix';
import { vtkObject } from "../../../interfaces" ;
import { Bounds, Vector3, Range } from '../../../types';

/**
*
*/
export interface ICameraInitialValues {
position?: number[];
focalPoint?: number[];
viewUp?: number[];
directionOfProjection?: number[];
parallelProjection?: boolean;
useHorizontalViewAngle?: boolean;
viewAngle?: number;
parallelScale?: number;
clippingRange?: number[];
windowCenter?: number[];
viewPlaneNormal?: number[];
useOffAxisProjection?: boolean;
screenBottomLeft?: number[];
screenBottomRight?: number[];
screenTopRight?: number[];
freezeFocalPoint?: boolean;
physicalTranslation?: number[];
physicalScale?: number;
physicalViewUp?: number[];
physicalViewNorth?: number[];
}

export interface vtkCamera extends vtkObject {

/**
* Apply a transform to the camera.
* The camera position, focal-point, and view-up are re-calculated
* using the transform's matrix to multiply the old points by the new transform.
* @param {mat4} transformMat4 Transform matrix.
*/
applyTransform(transformMat4: mat4): void;

/**
* Rotate the camera about the view up vector centered at the focal point.
* @param {Number} angle The angle value.
*/
azimuth(angle: number): void;

/**
*
* @param {Bounds} bounds The bounds value.
*/
computeClippingRange(bounds: Bounds): Range;

/**
* This method must be called when the focal point or camera position changes
*/
computeDistance(): void;

/**
*
*/
computeCameraLightTransform(): void;

/**
* the provided matrix should include translation and orientation only mat
* is physical to view
* @param {mat4} mat The physical matrix.
*/
computeViewParametersFromPhysicalMatrix(mat: mat4): void;

/**
*
* @param {mat4} vmat The view matrix.
*/
computeViewParametersFromViewMatrix(vmat: mat4): void;

/**
* Not implemented yet
* @param {vtkCamera} sourceCamera The camera source.
*/
deepCopy(sourceCamera: vtkCamera): void;

/**
* Move the position of the camera along the view plane normal. Moving
* towards the focal point (e.g., > 1) is a dolly-in, moving away
* from the focal point (e.g., < 1) is a dolly-out.
* @param {Number} amount The amount value.
*/
dolly(amount: number): void;

/**
* Rotate the camera about the cross product of the negative of the
* direction of projection and the view up vector, using the focal point as
* the center of rotation.
* @param {Number} angle The angle value.
*/
elevation(angle: number): void;

/**
* Not implemented yet
*/
getCameraLightTransformMatrix(): void;

/**
* Get the location of the near and far clipping planes along the direction
* of projection.
*/
getClippingRange(): Range;

/**
* Get the location of the near and far clipping planes along the direction
* of projection.
*/
getClippingRangeByReference(): Range;

/**
*
* @param {Number} aspect Camera frustum aspect ratio.
* @param {Number} nearz Camera frustum near plane.
* @param {Number} farz Camera frustum far plane.
*/
getCompositeProjectionMatrix(aspect: number, nearz: number, farz: number): mat4;

/**
* Get the vector in the direction from the camera position to the focal
* point.
*/
getDirectionOfProjection(): Vector3;

/**
* Get the vector in the direction from the camera position to the focal
* point.
*/
getDirectionOfProjectionByReference(): Vector3;

/**
* Get the distance from the camera position to the focal point.
*/
getDistance(): number;

/**
* Get the focal of the camera in world coordinates.
*/
getFocalPoint(): Vector3;

/**
* Get the focal of the camera in world coordinates.
*/
getFocalPointByReference(): Vector3;

/**
* Get the value of the FreezeDolly instance variable.
*/
getFreezeFocalPoint(): boolean;

/**
* Not implemented yet
* Get the plane equations that bound the view frustum.
* @param {Number} aspect Camera frustum aspect ratio.
*/
getFrustumPlanes(aspect: number): void;

/**
* Not implemented yet
* Get the orientation of the camera.
*/
getOrientation(): void;

/**
* Not implemented yet
* Get the orientation of the camera.
*/
getOrientationWXYZ(): void;

/**
* Get the value of the ParallelProjection instance variable.
* This determines if the camera should do a perspective or parallel projection.
*/
getParallelProjection(): boolean;

/**
* Get the scaling used for a parallel projection.
*/
getParallelScale(): number;

/**
*
*/
getPhysicalScale(): number;

/**
*
* @param {mat4} result The world matrix.
*/
getPhysicalToWorldMatrix(result: mat4): void;

/**
*
*/
getPhysicalTranslation(): number[];

/**
*
*/
getPhysicalTranslationByReference(): number[];

/**
*
*/
getPhysicalViewNorth(): number[];

/**
*
*/
getPhysicalViewNorthByReference(): number[];

/**
*
*/
getPhysicalViewUp(): number[];

/**
*
*/
getPhysicalViewUpByReference(): number[];

/**
* Get the position of the camera in world coordinates.
*/
getPosition(): Vector3;

/**
* Get the position of the camera in world coordinates.
*/
getPositionByReference(): Vector3;

/**
* Get the projection matrix.
* @param {Number} aspect Camera frustum aspect ratio.
* @param {Number} nearz Camera frustum near plane.
* @param {Number} farz Camera frustum far plane.
*/
getProjectionMatrix(aspect: number, nearz: number, farz: number): mat4;

/**
* Not implemented yet
* Get the roll angle of the camera about the direction of projection.
*/
getRoll(): void;

/**
* Get top left corner point of the screen.
*/
getScreenBottomLeft(): Vector3;

/**
*
*/
getScreenBottomLeftByReference(): Vector3;

/**
* Get bottom left corner point of the screen
*/
getScreenBottomRight(): Vector3;

/**
*
*/
getScreenBottomRightByReference(): Vector3;

/**
*
*/
getScreenTopRight(): Vector3;

/**
*
*/
getScreenTopRightByReference(): Vector3;

/**
* Get the center of the window in viewport coordinates.
*/
getThickness(): number;

/**
* Get the value of the UseHorizontalViewAngle.
*/
getUseHorizontalViewAngle(): boolean;

/**
* Get use offaxis frustum.
*/
getUseOffAxisProjection(): boolean;

/**
* Get the camera view angle.
*/
getViewAngle(): number;

/**
*
*/
getViewMatrix(): mat4;

/**
* Get the ViewPlaneNormal.
* This vector will point opposite to the direction of projection,
* unless you have created a sheared output view using SetViewShear/SetObliqueAngles.
*/
getViewPlaneNormal(): Vector3;

/**
* Get the ViewPlaneNormal by reference.
*/
getViewPlaneNormalByReference(): Vector3;

/**
* Get ViewUp vector.
*/
getViewUp(): Vector3;

/**
* Get ViewUp vector by reference.
*/
getViewUpByReference(): Vector3;

/**
* Get the center of the window in viewport coordinates.
* The viewport coordinate range is ([-1,+1],[-1,+1]).
*/
getWindowCenter(): Range;

/**
*
*/
getWindowCenterByReference(): Range;

/**
*
* @param {mat4} result
*/
getWorldToPhysicalMatrix(result: mat4): void;

/**
* Recompute the ViewUp vector to force it to be perpendicular to
* camera.focalpoint vector.
*/
orthogonalizeViewUp(): void;

/**
*
* @param {Number[]} ori
*/
physicalOrientationToWorldDirection(ori: number[]): any;

/**
* Rotate the focal point about the cross product of the view up vector and
* the direction of projection, using the camera's position as the center of
* rotation.
* @param {Number} angle The value of the angle.
*/
pitch(angle: number): void;

/**
* Rotate the camera about the direction of projection.
* @param {Number} angle The value of the angle.
*/
roll(angle: number): void;

/**
* Set the location of the near and far clipping planes along the direction
* of projection.
* @param {Number} near The near clipping planes.
* @param {Number} far The far clipping planes.
*/
setClippingRange(near: number, far: number): boolean;

/**
* Set the location of the near and far clipping planes along the direction
* of projection.
* @param {Range} clippingRange
*/
setClippingRange(clippingRange: Range): boolean;

/**
* Set the location of the near and far clipping planes along the direction
* of projection.
* @param {Range} clippingRange
*/
setClippingRangeFrom(clippingRange: Range): boolean;

/**
* Used to handle convert js device orientation angles
* when you use this method the camera will adjust to the
* device orientation such that the physicalViewUp you set
* in world coordinates looks up, and the physicalViewNorth
* you set in world coorindates will (maybe) point north
*
* NOTE WARNING - much of the documentation out there on how
* orientation works is seriously wrong. Even worse the Chrome
* device orientation simulator is completely wrong and should
* never be used. OMG it is so messed up.
*
* how it seems to work on iOS is that the device orientation
* is specified in extrinsic angles with a alpha, beta, gamma
* convention with axes of Z, X, Y (the code below substitutes
* the physical coordinate system for these axes to get the right
* modified coordinate system.
* @param {Number} alpha The value of the alpha.
* @param {Number} beta The value of the beta.
* @param {Number} gamma The value of the gamma.
* @param {Number} screen The value of the screen.
*/
setDeviceAngles(alpha: number, beta: number, gamma: number, screen: number): boolean;

/**
* Set the direction of projection.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setDirectionOfProjection(x: number, y: number, z: number): boolean;

/**
* Move the focal point so that it is the specified distance from the camera
* position.
*
* This distance must be positive.
* @param {Number} distance The value of the distance.
*/
setDistance(distance: number): boolean;

/**
* Set the focal of the camera in world coordinates.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setFocalPoint(x: number, y: number, z: number): boolean;

/**
* Set the focal of the camera in world coordinates.
* @param {Vector3} focalPoint
*/
setFocalPointFrom(focalPoint: Vector3): boolean;

/**
* Set the value of the FreezeDolly instance variable.
* @param {Boolean} freezeFocalPoint
*/
setFreezeFocalPoint(freezeFocalPoint: boolean): boolean;

/**
* Not implement yet
* Set the oblique viewing angles.
* The first angle, alpha, is the angle (measured from the horizontal) that
* rays along the direction of projection will follow once projected onto
* the 2D screen. The second angle, beta, is the angle between the view
* plane and the direction of projection. This creates a shear transform x'
* = x + dz*cos(alpha)/tan(beta), y' = dz*sin(alpha)/tan(beta) where dz is
* the distance of the point from the focal plane. The angles are (45,90) by
* default. Oblique projections commonly use (30,63.435).
*
* @param {Number} alpha The aplha angle value.
* @param {Number} beta The beta angle value.
*/
setObliqueAngles(alpha: number, beta: number): boolean;

/**
* Set the value of the OrientationWXYZ.
* @param {Number} degrees
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setOrientationWXYZ(degrees: number, x: number, y: number, z: number): boolean;

/**
* Set the value of the ParallelProjection.
* @param {Boolean} parallelProjection The value of the parallelProjection.
*/
setParallelProjection(parallelProjection: boolean): boolean;

/**
* Set the value of the parallelScale.
* @param {Number} parallelScale The value of the parallelScale.
*/
setParallelScale(parallelScale: number): boolean;

/**
* Set the value of the physicalScale.
* @param {Number} physicalScale The value of the the physicalScale.
*/
setPhysicalScale(physicalScale: number): boolean;

/**
* Set the value of the physicalTranslation.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setPhysicalTranslation(x: number, y: number, z: number): boolean;

/**
* Set the value of the physicalTranslation.
* @param {Number[]} physicalTranslation The value of the physicalTranslation.
*/
setPhysicalTranslationFrom(physicalTranslation: number[]): boolean;

/**
*
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setPhysicalViewNorth(x: number, y: number, z: number): boolean;

/**
*
* @param {Number[]} physicalViewNorth
*/
setPhysicalViewNorthFrom(physicalViewNorth: number[]): boolean;

/**
*
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setPhysicalViewUp(x: number, y: number, z: number): boolean;

/**
*
* @param {Number[]} physicalViewUp
*/
setPhysicalViewUpFrom(physicalViewUp: number[]): boolean;

/**
* Set the position of the camera in world coordinates.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setPosition(x: number, y: number, z: number): boolean;

/**
*
* @param {mat4} mat
*/
setProjectionMatrix(mat: mat4): boolean;

/**
* Set the roll angle of the camera about the direction of projection.
* @todo Not implemented yet
* @param {Number} angle The value of the roll angle.
*/
setRoll(angle: number): boolean;

/**
* Set top left corner point of the screen.
*
* This will be used only for offaxis frustum calculation.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setScreenBottomLeft(x: number, y: number, z: number): boolean;

/**
* Set top left corner point of the screen.
*
* This will be used only for offaxis frustum calculation.
* @param {Vector3} screenBottomLeft The screenBottomLeft coordiante.
*/
setScreenBottomLeft(screenBottomLeft: Vector3): boolean;

/**
* Set top left corner point of the screen.
* @param {Vector3} screenBottomLeft The screenBottomLeft coordiante.
*/
setScreenBottomLeftFrom(screenBottomLeft: Vector3): boolean;

/**
*
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setScreenBottomRight(x: number, y: number, z: number): boolean;

/**
* Set bottom right corner point of the screen.
* @param {Vector3} screenBottomRight The screenBottomRight coordiante.
*/
setScreenBottomRight(screenBottomRight: Vector3): boolean;

/**
* Set bottom right corner point of the screen.
* @param {Vector3} screenBottomRight The screenBottomRight coordiante.
*/
setScreenBottomRightFrom(screenBottomRight: Vector3): boolean;

/**
* Set top right corner point of the screen.
*
* This will be used only for offaxis frustum calculation.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setScreenTopRight(x: number, y: number, z: number): boolean;

/**
* Set top right corner point of the screen.
*
* This will be used only for offaxis frustum calculation.
* @param {Vector3} screenTopRight The screenTopRight coordiante.
*/
setScreenTopRight(screenTopRight: Vector3): boolean;

/**
* Set top right corner point of the screen.
* @param {Vector3} screenTopRight The screenTopRight coordiante.
*/
setScreenTopRightFrom(screenTopRight: Vector3): boolean;

/**
* Set the distance between clipping planes.
*
* This method adjusts the far clipping plane to be set a distance
* 'thickness' beyond the near clipping plane.
* @param {Number} thickness
*/
setThickness(thickness: number): boolean;

/**
*
* @param {Number} thickness The value of the thickness.
*/
setThicknessFromFocalPoint(thickness: number): boolean;

/**
* Set the value of the useHorizontalViewAngle.
* @param {Boolean} useHorizontalViewAngle The value of the useHorizontalViewAngle.
*/
setUseHorizontalViewAngle(useHorizontalViewAngle: boolean): boolean;

/**
* Set use offaxis frustum.
*
* OffAxis frustum is used for off-axis frustum calculations specifically for
* stereo rendering. For reference see "High Resolution Virtual Reality", in
* Proc. SIGGRAPH '92, Computer Graphics, pages 195-202, 1992.
* @param {Boolean} useOffAxisProjection The value of the useOffAxisProjection.
*/
setUseOffAxisProjection(useOffAxisProjection: boolean): boolean;

/**
* Set the camera view angle, which is the angular height of the camera view
* measured in degrees.
* @param {Number} viewAngle The value of the viewAngle.
*/
setViewAngle(viewAngle: number): boolean;

/**
* Set the view matrix for the camera.
* @param {mat4} mat The value of the view matrix.
*/
setViewMatrix(mat: mat4): boolean;

/**
* Set the view up direction for the camera.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
setViewUp(x: number, y: number, z: number): boolean;

/**
* Set the view up direction for the camera.
* @param {Vector3} viewUp The viewUp coordinate.
*/
setViewUp(viewUp: Vector3): boolean;

/**
* Set the view up direction for the camera.
* @param {Vector3} viewUp The viewUp coordinate.
*/
setViewUpFrom(viewUp: Vector3): boolean;

/**
* Set the center of the window in viewport coordinates.
*
* The viewport coordinate range is ([-1,+1],[-1,+1]).
*
* This method is for if you have one window which consists of several
* viewports, or if you have several screens which you want to act together
* as one large screen.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
*/
setWindowCenter(x: number, y: number): boolean;

/**
* Set the center of the window in viewport coordinates from an array.
* @param {Range} windowCenter
*/
setWindowCenterFrom(windowCenter: Range): boolean;

/**
*
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
translate(x: number, y: number, z: number): void;

/**
* Rotate the focal point about the view up vector, using the camera's
* position as the center of rotation.
* @param {Number} angle The value of the angle.
*/
yaw(angle: number): void;

/**
* In perspective mode, decrease the view angle by the specified factor.
* @param {Number} factor The value of the zoom factor.
*/
zoom(factor: number): void;
}

/**
* Method use to decorate a given object (publicAPI+model) with vtkRenderer characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {ICameraInitialValues} [initialValues] (default: {})
*/
export function extend(publicAPI: object, model: object, initialValues?: ICameraInitialValues): void;

/**
* Method use to create a new instance of vtkCamera with its focal point at the origin,
* and position=(0,0,1). The view up is along the y-axis, view angle is 30 degrees,
* and the clipping range is (.1,1000).
* @param {ICameraInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(initialValues?: ICameraInitialValues): vtkCamera;

/**
* vtkCamera is a virtual camera for 3D rendering. It provides methods
* to position and orient the view point and focal point. Convenience
* methods for moving about the focal point also are provided. More
* complex methods allow the manipulation of the computer graphics model
* including view up vector, clipping planes, and camera perspective.
*/
export declare const vtkCamera: {
newInstance: typeof newInstance,
extend: typeof extend,
};
export default vtkCamera;
index.js
import { quat, vec3, vec4, mat4 } from 'gl-matrix';

import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

const { vtkDebugMacro } = macro;

/* eslint-disable new-cap */

/*
* Convenience function to access elements of a gl-matrix. If it turns
* out I have rows and columns swapped everywhere, then I'll just change
* the order of 'row' and 'col' parameters in this function
*/
// function getMatrixElement(matrix, row, col) {
// const idx = (row * 4) + col;
// return matrix[idx];
// }

// ----------------------------------------------------------------------------
// vtkCamera methods
// ----------------------------------------------------------------------------

function vtkCamera(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkCamera');

// Set up private variables and methods
const origin = new Float64Array(3);
const dopbasis = new Float64Array([0.0, 0.0, -1.0]);
const upbasis = new Float64Array([0.0, 1.0, 0.0]);
const tmpMatrix = mat4.identity(new Float64Array(16));
const tmpMatrix2 = mat4.identity(new Float64Array(16));
const tmpvec1 = new Float64Array(3);
const tmpvec2 = new Float64Array(3);
const tmpvec3 = new Float64Array(3);

const rotateMatrix = mat4.identity(new Float64Array(16));
const trans = mat4.identity(new Float64Array(16));
const newPosition = new Float64Array(3);
const newFocalPoint = new Float64Array(3);

// Internal Functions that don't need to be public
function computeViewPlaneNormal() {
// VPN is -DOP
model.viewPlaneNormal[0] = -model.directionOfProjection[0];
model.viewPlaneNormal[1] = -model.directionOfProjection[1];
model.viewPlaneNormal[2] = -model.directionOfProjection[2];
}

publicAPI.orthogonalizeViewUp = () => {
const vt = publicAPI.getViewMatrix();
model.viewUp[0] = vt[4];
model.viewUp[1] = vt[5];
model.viewUp[2] = vt[6];

publicAPI.modified();
};

publicAPI.setPosition = (x, y, z) => {
if (
x === model.position[0] &&
y === model.position[1] &&
z === model.position[2]
) {
return;
}

model.position[0] = x;
model.position[1] = y;
model.position[2] = z;

// recompute the focal distance
publicAPI.computeDistance();
publicAPI.modified();
};

publicAPI.setFocalPoint = (x, y, z) => {
if (
x === model.focalPoint[0] &&
y === model.focalPoint[1] &&
z === model.focalPoint[2]
) {
return;
}

model.focalPoint[0] = x;
model.focalPoint[1] = y;
model.focalPoint[2] = z;

// recompute the focal distance
publicAPI.computeDistance();
publicAPI.modified();
};

publicAPI.setDistance = (d) => {
if (model.distance === d) {
return;
}

model.distance = d;

if (model.distance < 1e-20) {
model.distance = 1e-20;
vtkDebugMacro('Distance is set to minimum.');
}

// we want to keep the camera pointing in the same direction
const vec = model.directionOfProjection;

// recalculate FocalPoint
model.focalPoint[0] = model.position[0] + vec[0] * model.distance;
model.focalPoint[1] = model.position[1] + vec[1] * model.distance;
model.focalPoint[2] = model.position[2] + vec[2] * model.distance;
publicAPI.modified();
};

//----------------------------------------------------------------------------
// This method must be called when the focal point or camera position changes
publicAPI.computeDistance = () => {
const dx = model.focalPoint[0] - model.position[0];
const dy = model.focalPoint[1] - model.position[1];
const dz = model.focalPoint[2] - model.position[2];

model.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

if (model.distance < 1e-20) {
model.distance = 1e-20;
vtkDebugMacro('Distance is set to minimum.');

const vec = model.directionOfProjection;

// recalculate FocalPoint
model.focalPoint[0] = model.position[0] + vec[0] * model.distance;
model.focalPoint[1] = model.position[1] + vec[1] * model.distance;
model.focalPoint[2] = model.position[2] + vec[2] * model.distance;
}

model.directionOfProjection[0] = dx / model.distance;
model.directionOfProjection[1] = dy / model.distance;
model.directionOfProjection[2] = dz / model.distance;

computeViewPlaneNormal();
};

//----------------------------------------------------------------------------
// Move the position of the camera along the view plane normal. Moving
// towards the focal point (e.g., > 1) is a dolly-in, moving away
// from the focal point (e.g., < 1) is a dolly-out.
publicAPI.dolly = (amount) => {
if (amount <= 0.0) {
return;
}

// dolly moves the camera towards the focus
const d = model.distance / amount;

publicAPI.setPosition(
model.focalPoint[0] - d * model.directionOfProjection[0],
model.focalPoint[1] - d * model.directionOfProjection[1],
model.focalPoint[2] - d * model.directionOfProjection[2]
);
};

publicAPI.roll = (angle) => {
const eye = model.position;
const at = model.focalPoint;
const up = model.viewUp;
const viewUpVec4 = new Float64Array([up[0], up[1], up[2], 0.0]);

mat4.identity(rotateMatrix);
const viewDir = new Float64Array([
at[0] - eye[0],
at[1] - eye[1],
at[2] - eye[2],
]);
mat4.rotate(
rotateMatrix,
rotateMatrix,
vtkMath.radiansFromDegrees(angle),
viewDir
);
vec4.transformMat4(viewUpVec4, viewUpVec4, rotateMatrix);

model.viewUp[0] = viewUpVec4[0];
model.viewUp[1] = viewUpVec4[1];
model.viewUp[2] = viewUpVec4[2];

publicAPI.modified();
};

publicAPI.azimuth = (angle) => {
const fp = model.focalPoint;

mat4.identity(trans);

// translate the focal point to the origin,
// rotate about view up,
// translate back again
mat4.translate(trans, trans, fp);
mat4.rotate(trans, trans, vtkMath.radiansFromDegrees(angle), model.viewUp);
mat4.translate(trans, trans, [-fp[0], -fp[1], -fp[2]]);

// apply the transform to the position
vec3.transformMat4(newPosition, model.position, trans);
publicAPI.setPosition(newPosition[0], newPosition[1], newPosition[2]);
};

publicAPI.yaw = (angle) => {
const position = model.position;

mat4.identity(trans);

// translate the camera to the origin,
// rotate about axis,
// translate back again
mat4.translate(trans, trans, position);
mat4.rotate(trans, trans, vtkMath.radiansFromDegrees(angle), model.viewUp);
mat4.translate(trans, trans, [-position[0], -position[1], -position[2]]);

// apply the transform to the position
vec3.transformMat4(newFocalPoint, model.focalPoint, trans);
publicAPI.setFocalPoint(
newFocalPoint[0],
newFocalPoint[1],
newFocalPoint[2]
);
};

publicAPI.elevation = (angle) => {
const fp = model.focalPoint;

// get the eye / camera position from the viewMatrix
const vt = publicAPI.getViewMatrix();
const axis = [-vt[0], -vt[1], -vt[2]];

mat4.identity(trans);

// translate the focal point to the origin,
// rotate about view up,
// translate back again
mat4.translate(trans, trans, fp);
mat4.rotate(trans, trans, vtkMath.radiansFromDegrees(angle), axis);
mat4.translate(trans, trans, [-fp[0], -fp[1], -fp[2]]);

// apply the transform to the position
vec3.transformMat4(newPosition, model.position, trans);
publicAPI.setPosition(newPosition[0], newPosition[1], newPosition[2]);
};

publicAPI.pitch = (angle) => {
const position = model.position;

const vt = publicAPI.getViewMatrix();
const axis = [vt[0], vt[1], vt[2]];

mat4.identity(trans);

// translate the camera to the origin,
// rotate about axis,
// translate back again
mat4.translate(trans, trans, position);
mat4.rotate(trans, trans, vtkMath.radiansFromDegrees(angle), axis);
mat4.translate(trans, trans, [-position[0], -position[1], -position[2]]);

// apply the transform to the focal point
vec3.transformMat4(newFocalPoint, model.focalPoint, trans);
publicAPI.setFocalPoint(...newFocalPoint);
};

publicAPI.zoom = (factor) => {
if (factor <= 0) {
return;
}
if (model.parallelProjection) {
model.parallelScale /= factor;
} else {
model.viewAngle /= factor;
}
publicAPI.modified();
};

publicAPI.translate = (x, y, z) => {
const offset = [x, y, z];

vtkMath.add(model.position, offset, model.position);
vtkMath.add(model.focalPoint, offset, model.focalPoint);

publicAPI.computeDistance();
publicAPI.modified();
};

publicAPI.applyTransform = (transformMat4) => {
const vuOld = [...model.viewUp, 1.0];
const posNew = [];
const fpNew = [];
const vuNew = [];

vuOld[0] += model.position[0];
vuOld[1] += model.position[1];
vuOld[2] += model.position[2];

vec4.transformMat4(posNew, [...model.position, 1.0], transformMat4);
vec4.transformMat4(fpNew, [...model.focalPoint, 1.0], transformMat4);
vec4.transformMat4(vuNew, vuOld, transformMat4);

vuNew[0] -= posNew[0];
vuNew[1] -= posNew[1];
vuNew[2] -= posNew[2];

publicAPI.setPosition(...posNew.slice(0, 3));
publicAPI.setFocalPoint(...fpNew.slice(0, 3));
publicAPI.setViewUp(...vuNew.slice(0, 3));
};

publicAPI.getThickness = () =>
model.clippingRange[1] - model.clippingRange[0];

publicAPI.setThickness = (thickness) => {
let t = thickness;
if (t < 1e-20) {
t = 1e-20;
vtkDebugMacro('Thickness is set to minimum.');
}
publicAPI.setClippingRange(
model.clippingRange[0],
model.clippingRange[0] + t
);
};

publicAPI.setThicknessFromFocalPoint = (thickness) => {
let t = thickness;
if (t < 1e-20) {
t = 1e-20;
vtkDebugMacro('Thickness is set to minimum.');
}
publicAPI.setClippingRange(model.distance - t / 2, model.distance + t / 2);
};

// Unimplemented functions
publicAPI.setRoll = (angle) => {}; // dependency on GetOrientation() and a model.ViewTransform object, see https://github.com/Kitware/VTK/blob/master/Common/Transforms/vtkTransform.cxx and https://vtk.org/doc/nightly/html/classvtkTransform.html
publicAPI.getRoll = () => {};
publicAPI.setObliqueAngles = (alpha, beta) => {};
publicAPI.getOrientation = () => {};
publicAPI.getOrientationWXYZ = () => {};
publicAPI.getFrustumPlanes = (aspect) => {
// Return array of 24 params (4 params for each of 6 plane equations)
};
publicAPI.getCameraLightTransformMatrix = (matrix) => {
mat4.copy(matrix, model.cameraLightTransform);
return matrix;
};

publicAPI.computeCameraLightTransform = () => {
// not sure if this is the correct transformation, based on the same funciton in VTK
mat4.copy(tmpMatrix, publicAPI.getViewMatrix());
mat4.invert(tmpMatrix, tmpMatrix);

mat4.fromScaling(tmpMatrix2, [
model.distance,
model.distance,
model.distance,
]);
mat4.multiply(tmpMatrix, tmpMatrix, tmpMatrix2);
mat4.identity(model.cameraLightTransform);
mat4.translate(model.cameraLightTransform, tmpMatrix, [0.0, 0.0, -1.0]);
};

publicAPI.deepCopy = (sourceCamera) => {};

publicAPI.physicalOrientationToWorldDirection = (ori) => {
// push the x axis through the orientation quat
const oriq = quat.fromValues(ori[0], ori[1], ori[2], ori[3]);
const coriq = quat.create();
const qdir = quat.fromValues(0.0, 0.0, 1.0, 0.0);
quat.conjugate(coriq, oriq);

// rotate the z axis by the quat
quat.multiply(qdir, oriq, qdir);
quat.multiply(qdir, qdir, coriq);

// return the z axis in world coords
return [qdir[0], qdir[1], qdir[2]];
};

publicAPI.getPhysicalToWorldMatrix = (result) => {
publicAPI.getWorldToPhysicalMatrix(result);
mat4.invert(result, result);
};

publicAPI.getWorldToPhysicalMatrix = (result) => {
mat4.identity(result);

// now the physical to vtk world rotation tform
const physVRight = [3];
vtkMath.cross(model.physicalViewNorth, model.physicalViewUp, physVRight);
result[0] = physVRight[0];
result[1] = physVRight[1];
result[2] = physVRight[2];
result[4] = model.physicalViewUp[0];
result[5] = model.physicalViewUp[1];
result[6] = model.physicalViewUp[2];
result[8] = -model.physicalViewNorth[0];
result[9] = -model.physicalViewNorth[1];
result[10] = -model.physicalViewNorth[2];
mat4.transpose(result, result);

vec3.set(
tmpvec1,
1 / model.physicalScale,
1 / model.physicalScale,
1 / model.physicalScale
);

mat4.scale(result, result, tmpvec1);
mat4.translate(result, result, model.physicalTranslation);
};

publicAPI.computeViewParametersFromViewMatrix = (vmat) => {
// invert to get view to world
mat4.invert(tmpMatrix, vmat);

// note with glmatrix operations happen in
// the reverse order
// mat.scale
// mat.translate
// will result in the translation then the scale
// mat.mult(a,b)
// results in perform the B transformation then A

// then extract the params position, orientation
// push 0,0,0 through to get a translation
vec3.transformMat4(tmpvec1, origin, tmpMatrix);
publicAPI.computeDistance();
const oldDist = model.distance;
publicAPI.setPosition(tmpvec1[0], tmpvec1[1], tmpvec1[2]);

// push basis vectors to get orientation
vec3.transformMat4(tmpvec2, dopbasis, tmpMatrix);
vec3.subtract(tmpvec2, tmpvec2, tmpvec1);
vec3.normalize(tmpvec2, tmpvec2);
publicAPI.setDirectionOfProjection(tmpvec2[0], tmpvec2[1], tmpvec2[2]);

vec3.transformMat4(tmpvec3, upbasis, tmpMatrix);
vec3.subtract(tmpvec3, tmpvec3, tmpvec1);
vec3.normalize(tmpvec3, tmpvec3);
publicAPI.setViewUp(tmpvec3[0], tmpvec3[1], tmpvec3[2]);

publicAPI.setDistance(oldDist);
};

// the provided matrix should include
// translation and orientation only
// mat is physical to view
publicAPI.computeViewParametersFromPhysicalMatrix = (mat) => {
// get the WorldToPhysicalMatrix
publicAPI.getWorldToPhysicalMatrix(tmpMatrix);

// first convert the physical -> view matrix to be
// world -> view
mat4.multiply(tmpMatrix, mat, tmpMatrix);

publicAPI.computeViewParametersFromViewMatrix(tmpMatrix);
};

publicAPI.setViewMatrix = (mat) => {
model.viewMatrix = mat;
if (model.viewMatrix) {
mat4.copy(tmpMatrix, model.viewMatrix);
publicAPI.computeViewParametersFromViewMatrix(tmpMatrix);
mat4.transpose(model.viewMatrix, model.viewMatrix);
}
};

publicAPI.getViewMatrix = () => {
if (model.viewMatrix) {
return model.viewMatrix;
}

mat4.lookAt(
tmpMatrix,
model.position, // eye
model.focalPoint, // at
model.viewUp // up
);

mat4.transpose(tmpMatrix, tmpMatrix);

const result = new Float64Array(16);
mat4.copy(result, tmpMatrix);
return result;
};

publicAPI.setProjectionMatrix = (mat) => {
model.projectionMatrix = mat;
};

publicAPI.getProjectionMatrix = (aspect, nearz, farz) => {
const result = new Float64Array(16);
mat4.identity(result);

if (model.projectionMatrix) {
const scale = 1 / model.physicalScale;
vec3.set(tmpvec1, scale, scale, scale);

mat4.copy(result, model.projectionMatrix);
mat4.scale(result, result, tmpvec1);
mat4.transpose(result, result);
return result;
}

mat4.identity(tmpMatrix);

// FIXME: Not sure what to do about adjust z buffer here
// adjust Z-buffer range
// this->ProjectionTransform->AdjustZBuffer( -1, +1, nearz, farz );
const cWidth = model.clippingRange[1] - model.clippingRange[0];
const cRange = [
model.clippingRange[0] + ((nearz + 1) * cWidth) / 2.0,
model.clippingRange[0] + ((farz + 1) * cWidth) / 2.0,
];

if (model.parallelProjection) {
// set up a rectangular parallelipiped
const width = model.parallelScale * aspect;
const height = model.parallelScale;

const xmin = (model.windowCenter[0] - 1.0) * width;
const xmax = (model.windowCenter[0] + 1.0) * width;
const ymin = (model.windowCenter[1] - 1.0) * height;
const ymax = (model.windowCenter[1] + 1.0) * height;

mat4.ortho(tmpMatrix, xmin, xmax, ymin, ymax, cRange[0], cRange[1]);
mat4.transpose(tmpMatrix, tmpMatrix);
} else if (model.useOffAxisProjection) {
throw new Error('Off-Axis projection is not supported at this time');
} else {
const tmp = Math.tan(vtkMath.radiansFromDegrees(model.viewAngle) / 2.0);
let width;
let height;
if (model.useHorizontalViewAngle === true) {
width = model.clippingRange[0] * tmp;
height = (model.clippingRange[0] * tmp) / aspect;
} else {
width = model.clippingRange[0] * tmp * aspect;
height = model.clippingRange[0] * tmp;
}

const xmin = (model.windowCenter[0] - 1.0) * width;
const xmax = (model.windowCenter[0] + 1.0) * width;
const ymin = (model.windowCenter[1] - 1.0) * height;
const ymax = (model.windowCenter[1] + 1.0) * height;
const znear = cRange[0];
const zfar = cRange[1];

tmpMatrix[0] = (2.0 * znear) / (xmax - xmin);
tmpMatrix[5] = (2.0 * znear) / (ymax - ymin);
tmpMatrix[2] = (xmin + xmax) / (xmax - xmin);
tmpMatrix[6] = (ymin + ymax) / (ymax - ymin);
tmpMatrix[10] = -(znear + zfar) / (zfar - znear);
tmpMatrix[14] = -1.0;
tmpMatrix[11] = (-2.0 * znear * zfar) / (zfar - znear);
tmpMatrix[15] = 0.0;
}

mat4.copy(result, tmpMatrix);

return result;
};

publicAPI.getCompositeProjectionMatrix = (aspect, nearz, farz) => {
const vMat = publicAPI.getViewMatrix();
const pMat = publicAPI.getProjectionMatrix(aspect, nearz, farz);
// mats are transposed so the order is A then B
// we reuse pMat as it is a copy so we can do what we want with it
mat4.multiply(pMat, vMat, pMat);
return pMat;
};

publicAPI.setDirectionOfProjection = (x, y, z) => {
if (
model.directionOfProjection[0] === x &&
model.directionOfProjection[1] === y &&
model.directionOfProjection[2] === z
) {
return;
}

model.directionOfProjection[0] = x;
model.directionOfProjection[1] = y;
model.directionOfProjection[2] = z;

const vec = model.directionOfProjection;

// recalculate FocalPoint
model.focalPoint[0] = model.position[0] + vec[0] * model.distance;
model.focalPoint[1] = model.position[1] + vec[1] * model.distance;
model.focalPoint[2] = model.position[2] + vec[2] * model.distance;
computeViewPlaneNormal();
};

// used to handle convert js device orientation angles
// when you use this method the camera will adjust to the
// device orientation such that the physicalViewUp you set
// in world coordinates looks up, and the physicalViewNorth
// you set in world coorindates will (maybe) point north
//
// NOTE WARNING - much of the documentation out there on how
// orientation works is seriously wrong. Even worse the Chrome
// device orientation simulator is completely wrong and should
// never be used. OMG it is so messed up.
//
// how it seems to work on iOS is that the device orientation
// is specified in extrinsic angles with a alpha, beta, gamma
// convention with axes of Z, X, Y (the code below substitutes
// the physical coordinate system for these axes to get the right
// modified coordinate system.
publicAPI.setDeviceAngles = (alpha, beta, gamma, screen) => {
const physVRight = [3];
vtkMath.cross(model.physicalViewNorth, model.physicalViewUp, physVRight);

// phone to physical coordinates
const rotmat = mat4.identity(new Float64Array(16));
mat4.rotate(
rotmat,
rotmat,
vtkMath.radiansFromDegrees(alpha),
model.physicalViewUp
);
mat4.rotate(rotmat, rotmat, vtkMath.radiansFromDegrees(beta), physVRight);
mat4.rotate(
rotmat,
rotmat,
vtkMath.radiansFromDegrees(gamma),
model.physicalViewNorth
);

mat4.rotate(
rotmat,
rotmat,
vtkMath.radiansFromDegrees(-screen),
model.physicalViewUp
);

const dop = new Float64Array([
-model.physicalViewUp[0],
-model.physicalViewUp[1],
-model.physicalViewUp[2],
]);
const vup = new Float64Array(model.physicalViewNorth);
vec3.transformMat4(dop, dop, rotmat);
vec3.transformMat4(vup, vup, rotmat);

publicAPI.setDirectionOfProjection(dop[0], dop[1], dop[2]);
publicAPI.setViewUp(vup[0], vup[1], vup[2]);
publicAPI.modified();
};

publicAPI.setOrientationWXYZ = (degrees, x, y, z) => {
const quatMat = mat4.identity(new Float64Array(16));

if (degrees !== 0.0 && (x !== 0.0 || y !== 0.0 || z !== 0.0)) {
// convert to radians
const angle = vtkMath.radiansFromDegrees(degrees);
const q = quat.create();
quat.setAxisAngle(q, [x, y, z], angle);
mat4.fromQuat(quatMat, q);
}

const newdop = new Float64Array(3);
vec3.transformMat4(newdop, [0.0, 0.0, -1.0], quatMat);

const newvup = new Float64Array(3);
vec3.transformMat4(newvup, [0.0, 1.0, 0.0], quatMat);

publicAPI.setDirectionOfProjection(...newdop);
publicAPI.setViewUp(...newvup);
publicAPI.modified();
};

publicAPI.computeClippingRange = (bounds) => {
let vn = null;
let position = null;

vn = model.viewPlaneNormal;
position = model.position;

const a = -vn[0];
const b = -vn[1];
const c = -vn[2];
const d = -(a * position[0] + b * position[1] + c * position[2]);

// Set the max near clipping plane and the min far clipping plane
const range = [a * bounds[0] + b * bounds[2] + c * bounds[4] + d, 1e-18];

// Find the closest / farthest bounding box vertex
for (let k = 0; k < 2; k++) {
for (let j = 0; j < 2; j++) {
for (let i = 0; i < 2; i++) {
const dist =
a * bounds[i] + b * bounds[2 + j] + c * bounds[4 + k] + d;
range[0] = dist < range[0] ? dist : range[0];
range[1] = dist > range[1] ? dist : range[1];
}
}
}

return range;
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

export const DEFAULT_VALUES = {
position: [0, 0, 1],
focalPoint: [0, 0, 0],
viewUp: [0, 1, 0],
directionOfProjection: [0, 0, -1],
parallelProjection: false,
useHorizontalViewAngle: false,
viewAngle: 30,
parallelScale: 1,
clippingRange: [0.01, 1000.01],
windowCenter: [0, 0],
viewPlaneNormal: [0, 0, 1],
useOffAxisProjection: false,
screenBottomLeft: [-0.5, -0.5, -0.5],
screenBottomRight: [0.5, -0.5, -0.5],
screenTopRight: [0.5, 0.5, -0.5],
freezeFocalPoint: false,
projectionMatrix: null,
viewMatrix: null,
cameraLightTransform: mat4.create(),

// used for world to physical transformations
physicalTranslation: [0, 0, 0],
physicalScale: 1.0,
physicalViewUp: [0, 1, 0],
physicalViewNorth: [0, 0, -1],
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Build VTK API
macro.obj(publicAPI, model);

macro.get(publicAPI, model, ['distance']);

macro.setGet(publicAPI, model, [
'parallelProjection',
'useHorizontalViewAngle',
'viewAngle',
'parallelScale',
'useOffAxisProjection',
'freezeFocalPoint',
'physicalScale',
]);

macro.getArray(publicAPI, model, [
'directionOfProjection',
'viewPlaneNormal',
'position',
'focalPoint',
]);

macro.setGetArray(publicAPI, model, ['clippingRange', 'windowCenter'], 2);

macro.setGetArray(
publicAPI,
model,
[
'viewUp',
'screenBottomLeft',
'screenBottomRight',
'screenTopRight',
'physicalTranslation',
'physicalViewUp',
'physicalViewNorth',
],
3
);

// Object methods
vtkCamera(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkCamera');

// ----------------------------------------------------------------------------

export default { newInstance, extend };