Type Declarations
Viewer: TYPE ~ Controls.Viewer;
Mouse: TYPE ~ Controls.Mouse;
Control: TYPE ~ Controls.Control;
ControlProc: TYPE ~ Controls.ControlProc;
ControlType: TYPE ~ Controls.ControlType;
ControlList: TYPE ~ Controls.ControlList;
DrawType: TYPE ~ Draw2d.DrawType;
Triple: TYPE ~ G3dBasic.Triple;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
Pair: TYPE ~ G3dBasic.Pair;
PairSequence: TYPE ~ G3dBasic.PairSequence;
ArcBall: TYPE ~ G3dControl.ArcBall;
Method: TYPE ~ G3dControl.Method;
ProxyMode: TYPE ~ G3dControl.ProxyMode;
CameraControl: TYPE ~ G3dControl.CameraControl;
CameraControlRep: TYPE ~ G3dControl.CameraControlRep;
Hold: TYPE ~ G3dControl.Hold;
HoldRep: TYPE ~ G3dControl.HoldRep;
Slice: TYPE ~ G3dControl.Slice;
SliceRep: TYPE ~ G3dControl.SliceRep;
Pick: TYPE ~ G3dControl.Pick;
Matrix: TYPE ~ G3dMatrix.Matrix;
MatrixRep: TYPE ~ G3dMatrix.MatrixRep;
Viewport: TYPE ~ G3dMatrix.Viewport;
Plane: TYPE ~ G3dPlane.Plane;
ScreenSequence: TYPE ~ G3dShape.ScreenSequence;
Camera: TYPE ~ G3dView.Camera;
CameraRep: TYPE ~ G3dView.CameraRep;
Context: TYPE ~ Imager.Context;
ROPE: TYPE ~ Rope.ROPE;
xAxis: Triple ~ G3dBasic.xAxis;
yAxis: Triple ~ G3dBasic.yAxis;
zAxis: Triple ~ G3dBasic.zAxis;
CameraControl Procedures
Note:
Controls.KillInputFocus has changed so that ActivateControl is called ONLY if the user
typed a value followed by a CR. This is as advertised in Controls. The previous behavior, wherein ActivateControl was called upon any kill of input focus, would result in serious problems in controlling the input focus, namely: the control proc called by ActivateControl might change the input focus and cause an endless propagation of KillInputFocus; one solution was to save the input focus and restore it after the control proc is called, but what if the user actually wanted to change the input focus? So . . . the present solution is to ActivateControl only upon a typed CR; this means that clients must read the values of controls (and buttons) whenever a new function is executed.
SetCameraControlMethod:
PUBLIC PROC [camera: CameraControl, method: Method] ~ {
camera.method ← method;
};
CameraControlOrientation:
PUBLIC
PROC [
camera: CameraControl,
eyePoint, lookAt, up: Triple,
roll: REAL]
~ {
camera.method ← geometry;
camera.eyePoint ← eyePoint;
camera.lookAt ← lookAt;
camera.roll ← roll;
};
InitCameraControl:
PUBLIC
PROC [
proc: ControlProc ← NIL,
clientData: REF ANY ← NIL,
proxyMode: ProxyMode ← wor,
fieldOfView: REAL ← 60.0,
scale: REAL ← 1.0,
move: Triple ← [0.0, 2.0, 0.0],
rotate: Triple ← [0.0, 0.0, 0.0],
moveRange: REAL ← 10.0]
RETURNS [c: CameraControl]
~ {
NewControl:
PROC [
name: Rope.ROPE,
type: ControlType,
taper: Controls.SliderTaper ← lin,
min, max, init: REAL ← 0.0,
col: Triple ¬ [0, 0, 1]]
RETURNS [ctrl: Control]
~ {
ctrl ← Controls.NewControl[
name: name, type: type, clientData: c, min: min, max: max, init: init, proc: CameraControlProc, taper: taper, color: ImagerColor.ColorFromRGB[[col.x, col.y, col.z]]];
To get back the color, use ImagerColor.NarrowToOpConstantColor.
};
r: REAL ← moveRange;
c ← NEW[CameraControlRep];
IF move = [0.0, 2.0, 0.0] THEN move ← [0.0, 0.0, 2.0];
c.proc ← proc;
c.clientData ← clientData;
c.matrix ← NEW[MatrixRep];
c.view ← NEW[MatrixRep];
c.save ← NEW[CameraRep];
c.incr ← NEW[MatrixRep];
c.proxyMode ← proxyMode;
c.proxySelect ← Controls.NewControl[NameFromProxyMode[proxyMode],, c,, 2, SELECT proxyMode FROM par => 0, wor => 1, ENDCASE => 2, ProxyProc, FALSE, 0,,,,,,,, LIST[[1, .5]]];
c.par.xMov ← NewControl["X", vSlider, , -r, r, move.x];
c.par.yMov ← NewControl["Y", vSlider, , -r, r, move.y];
c.par.zMov ← NewControl["Z", vSlider, , -r, r, move.z];
c.par.xRot ← NewControl["Xrot", dial, , 0.0, 360.0, rotate.x];
c.par.yRot ← NewControl["Yrot", dial, , 0.0, 360.0, rotate.y];
c.par.zRot ← NewControl["Zrot", dial, , 0.0, 360.0, rotate.z];
c.wor.xMov ← NewControl["WX", vSlider, , -r, r, 0.0];
c.wor.yMov ← NewControl["WY", vSlider, , -r, r, 0.0];
c.wor.zMov ← NewControl["WZ", vSlider, , -r, r, 0.0];
c.wor.xRot ← NewControl["WXrot", dial, , 0.0, 360.0, 0.0];
c.wor.yRot ← NewControl["WYrot", dial, , 0.0, 360.0, 0.0];
c.wor.zRot ← NewControl["WZrot", dial, , 0.0, 360.0, 0.0];
c.eye.xMov ← NewControl["EX", vSlider, , -r, r, 0.0];
c.eye.yMov ← NewControl["EY", vSlider, , -r, r, 0.0];
c.eye.zMov ← NewControl["EZ", vSlider, , -r, r, 0.0];
c.eye.xRot ← NewControl["Pitch", dial, , 0.0, 360.0, 0.0];
c.eye.yRot ← NewControl["Roll", dial, , 0.0, 360.0, 0.0];
c.eye.zRot ← NewControl["Yaw", dial, , 0.0, 360.0, 0.0];
c.proxy.xMov ← NewControl["X", vSlider, , -r, r, move.x];
c.proxy.yMov ← NewControl["Y", vSlider, , -r, r, move.y];
c.proxy.zMov ← NewControl["Z", vSlider, , -r, r, move.z];
c.proxy.xRot ← NewControl["Xrot", dial, , 0.0, 360.0, rotate.x];
c.proxy.yRot ← NewControl["Yrot", dial, , 0.0, 360.0, rotate.y];
c.proxy.zRot ← NewControl["Zrot", dial, , 0.0, 360.0, rotate.z];
c.scale ← NewControl["Scale", vSlider, exp, 0.0, 100.0, scale];
c.fieldOfView ← NewControl["Fov", vSlider, , 0.0, 180.0, fieldOfView, [1, 0, 0]];
c.hScreen ← NewControl["Xscr", vSlider, , -2.0, 2.0, 0.0];
c.vScreen ← NewControl["Yscr", vSlider, , -2.0, 2.0, 0.0];
c.arcBall ← G3dControl.InitArcBall[c, proc, clientData];
UpdateCameraControl[c];
};
AddCameraControl:
PUBLIC
PROC [
controls: ControlList,
camera: CameraControl,
useArcBalls: BOOL]
RETURNS [ret: ControlList]
~ {
ret ← CONS[camera.fieldOfView, controls];
IF useArcBalls
THEN {
ret ← CONS[camera.scale, ret];
ret ← CONS[camera.arcBall.translate.control, ret];
ret ← CONS[camera.arcBall.rotate.control, ret];
camera.fieldOfView.h ← camera.scale.h ← camera.arcBall.translate.control.h;
camera.proxyMode ← par;
}
ELSE {
ret ← CONS[camera.scale, ret];
ret ← CONS[camera.proxy.zMov, ret];
ret ← CONS[camera.proxy.yMov, ret];
ret ← CONS[camera.proxy.xMov, ret];
ret ← CONS[camera.proxy.zRot, ret];
ret ← CONS[camera.proxy.yRot, ret];
ret ← CONS[camera.proxy.xRot, ret];
ret ← CONS[camera.proxySelect, ret];
};
NameFromProxyMode:
PROC [proxyMode: ProxyMode]
RETURNS [r:
ROPE] ~ {
r ← SELECT proxyMode FROM par => "Par", wor => "Wor", ENDCASE => "Cam";
};
ProxyProc: ControlProc ~ {
c: CameraControl ~ NARROW[control.clientData];
c.proxyMode ← SELECT control.value FROM 0 => par, 1 => wor, ENDCASE => eye;
ViewerTools.SetContents[c.proxySelect.title, NameFromProxyMode[c.proxyMode]];
SELECT c.proxyMode
FROM
par => {
Controls.SetSliderDialValue[c.proxy.xMov, c.par.xMov.value];
Controls.SetSliderDialValue[c.proxy.yMov, c.par.yMov.value];
Controls.SetSliderDialValue[c.proxy.zMov, c.par.zMov.value];
Controls.SetSliderDialValue[c.proxy.xRot, c.par.xRot.value];
Controls.SetSliderDialValue[c.proxy.yRot, c.par.yRot.value];
Controls.SetSliderDialValue[c.proxy.zRot, c.par.zRot.value];
};
ENDCASE =>
FOR l: ControlList ←
LIST[c.proxy.xMov, c.proxy.yMov, c.proxy.zMov,
c.proxy.xRot, c.proxy.yRot, c.proxy.zRot], l.rest
WHILE l #
NIL
DO
Benign[l.first];
ENDLOOP;
};
InitContext:
PUBLIC
PROC [
context: Context,
camera: CameraControl,
viewer: Viewer ← NIL,
clear: BOOL ← TRUE,
out: Matrix ← NIL]
RETURNS [Matrix] ~ {
IF clear THEN Draw2d.Clear[context];
SetViewMatrix[camera];
IF viewer #
NIL
THEN camera.view ← G3dView.TransformByViewport[viewer, camera.view, camera.view];
RETURN[G3dMatrix.CopyMatrix[camera.view, out]];
};
CameraControlProc: ControlProc ~ {
Ignore: PROC RETURNS [BOOL ← FALSE] ~ {
IF control.mouse.state = up THEN RETURN;
IF NOT ControlsPrivate.CalledFromQueue[control] THEN RETURN;
If event was queued, service only last event if it is held:
IF ControlsPrivate.GetQueue[control] = NIL AND control.mouse.state = held THEN RETURN;
RETURN[TRUE];
};
Update the camera params when a control is adjusted by the user.
c: CameraControl ~ NARROW[control.clientData];
IF control = c.par.yRot AND (control.value = 90.0 OR control.value = 180.0 OR control.value = 270.0) THEN RETURN; -- this year's great kluge
IF
--
NOT Ignore[] -- control.mouse.state # up
THEN {
-- else user surprised by change
c.current ← control;
lastAdjustedCamera ← c;
IF control.whatChanged = $InputKilled THEN MessageWindow.Append["InputKilled..."];
IF
NOT alwaysUpdate
THEN {
If always update then we don't need initval (set on mouse down, used on mouse up):
IF control.mouse.state = down
THEN
SELECT CameraAction[c]
FROM
$Par, $Ignore => NULL;
ENDCASE => {
c.initval ← c.current.value; -- just zero the eye or wor control
RETURN; -- maybe returning causes the update problem?
};
};
UpdateCameraControl[c]; -- update the camera parameters and camera.matrix
IF c.graphics #
NIL
THEN ViewerOps.PaintViewer[c.graphics, client, FALSE, control.whatChanged];
};
IF control.mouse.state = up THEN c.inverse ← G3dMatrix.Invert[c.matrix, c.inverse];
IF c.proc # NIL THEN c.proc[control, c.clientData]; -- call client proc, if any
IF EverythingUp[]
THEN
SELECT CameraAction[c]
FROM
-- if missed up during draw
$Par, $Proxy, $Ignore => NULL;
ENDCASE => Benign[control];
};
UpdateCameraControl:
PUBLIC PROC [camera: CameraControl] ~ {
IF alwaysUpdate
THEN DoUpdateCamera[camera] -- update based on current control
ELSE {
This method requires initval to be right:
In event user slides off a control without an up mouse event occurring, or slides into a
control without a down event (we'd like an enter/exit notifier, if not too hard to use):
curr: Control ← camera.current;
cleanupPrev:
BOOL ←
camera.prev # NIL AND camera.prev.mouse.state # up AND curr # camera.prev;
slidIntoCurrent: BOOL ← camera.prev # curr AND curr.mouse.state # down;
IF cleanupPrev
THEN {
-- enter
new
slider
with
no
prev
up,
simulate
prev
up
camera.prev.mouse.state ← up; -- camera.prev has its own mouse record
camera.current ← camera.prev;
DoUpdateCamera[camera]; -- clean up prev
camera.current ← curr;
};
IF slidIntoCurrent THEN curr.mouse.state ← down; -- simulate current down
DoUpdateCamera[camera]; -- update for current control
camera.prev ← curr;
};
};
UpdateControl:
PUBLIC
PROC [camera: CameraControl, control: Control, value:
REAL] ~ {
IF control = NIL THEN RETURN;
IF control.sliderDialRef =
NIL
THEN control.value ← value
ELSE Controls.SetSliderDialValue[control, value];
IF camera.proxyMode = par
THEN Controls.SetSliderDialValue[ProxyFromPar[camera, control], value];
IF
NOT alwaysUpdate
THEN {
control.mouse.state ← up; -- so incremental controls will be absorbed into camera.par
camera.current ← control; -- so Delta is properly computed
camera.initval ← 0.; -- so Delta is properly computed (simulate down mouse)
};
DoUpdateCamera[camera]; -- as opposed to UpdateCameraControl
};
ProxyFromPar:
PROC [camera: CameraControl, control: Control]
RETURNS [Control] ~ {
RETURN[
SELECT control
FROM
camera.par.xMov => camera.proxy.xMov,
camera.par.yMov => camera.proxy.yMov,
camera.par.zMov => camera.proxy.zMov,
camera.par.xRot => camera.proxy.xRot,
camera.par.yRot => camera.proxy.yRot,
camera.par.zRot => camera.proxy.zRot,
ENDCASE => NIL];
};
viewersWorld: ViewersWorldRefType.Ref ¬ ViewersWorldInstance.GetWorld[];
userInput: UserInputOps.Handle ¬ ViewersWorld.GetInputHandle[viewersWorld];
leftMouse: KeyTypes.KeySym ¬ KeyNames.KeySymFromName["LeftMouse"];
middleMouse: KeyTypes.KeySym ¬ KeyNames.KeySymFromName["MiddleMouse"];
rightMouse: KeyTypes.KeySym ¬ KeyNames.KeySymFromName["RightMouse"];
EverythingUp:
PROC
RETURNS [allUp:
BOOL] ~ {
allUp ¬ UserInputOps.GetLatestKeySymState[userInput, leftMouse] = up
AND
UserInputOps.GetLatestKeySymState[userInput, middleMouse] = up AND
UserInputOps.GetLatestKeySymState[userInput, rightMouse] = up;
};
Benign:
PROC [control: Control] ~ {
-- make control seem 'benign.'
Controls.SetSliderDialValue[control, 0.0];
};
SetCameraControlGraphics:
PUBLIC PROC [camera: CameraControl, graphics: Viewer] ~ {
camera.graphics ← graphics;
};
CameraAction:
PROC [c: CameraControl]
RETURNS [
ATOM] ~ {
mode: ProxyMode ← c.proxyMode;
RETURN[
SELECT c.current
FROM
-- meaning of c.current
c.hScreen, c.vScreen => $Ignore,
c.wor.xMov => $WorXMv,
c.wor.yMov => $WorYMv,
c.wor.zMov => $WorZMv,
c.wor.xRot => $WorXRot,
c.wor.yRot => $WorYRot,
c.wor.zRot => $WorZRot,
c.eye.xMov => $EyeXMv,
c.eye.yMov => $EyeZMv, -- switch y and z so the user won't get confused
c.eye.zMov => $EyeYMv, -- i.e., we're in eye-space (LHS), but pretend otherwise to user
c.eye.xRot => $EyeXRot,
c.eye.yRot => $EyeZRot, -- switch y and z so the user won't get confused
c.eye.zRot => $EyeYRot, -- i.e., we're in eye-space (LHS), but pretend otherwise to user
c.proxy.xMov => SELECT mode FROM par=>$Proxy, eye=>$EyeXMv, ENDCASE=>$WorXMv,
switch $EyeZMv and $EyeYMv so the user won't get confused:
c.proxy.yMov => SELECT mode FROM par=>$Proxy, eye=>$EyeZMv, ENDCASE=>$WorYMv,
c.proxy.zMov => SELECT mode FROM par=>$Proxy, eye=>$EyeYMv, ENDCASE=>$WorZMv,
c.proxy.xRot => SELECT mode FROM par=>$Proxy, eye=>$EyeXRot, ENDCASE=>$WorXRot,
switch $EyeZRot and $EyeYRot so the user won't get confused:
c.proxy.yRot => SELECT mode FROM par=>$Proxy, eye=>$EyeZRot, ENDCASE=>$WorYRot,
c.proxy.zRot => SELECT mode FROM par=>$Proxy, eye=>$EyeYRot, ENDCASE=>$WorZRot,
ENDCASE => $Par];
};
DoUpdateCamera:
PROC [camera: CameraControl] ~ {
This presumes the change has occured in camera.current only.
If NOT alwaysUpdate, event presumed to begin with mouse down and end with mouse up.
Delta:
PROC
RETURNS [
REAL] ~ {
IF alwaysUpdate
THEN RETURN[Controls.GetSliderDialDeltaValue[c.current]]
ELSE RETURN[c.current.value-c.initval];
};
c: CameraControl ← camera;
incrSpace: {world, eye, none} ← none; -- which space are incremental xforms in?
action: ATOM ← CameraAction[c];
SELECT action
FROM
$WorXMv => {incrSpace ← world; [] ← G3dMatrix.MakeTranslate[[Delta[], 0, 0], c.incr]};
$WorYMv => {incrSpace ← world; [] ← G3dMatrix.MakeTranslate[[0, Delta[], 0], c.incr]};
$WorZMv => {incrSpace ← world; [] ← G3dMatrix.MakeTranslate[[0, 0, Delta[]], c.incr]};
$WorXRot => {incrSpace ← world; [] ← G3dMatrix.MakeRotate[xAxis, Delta[],, c.incr]};
$WorYRot => {incrSpace ← world; [] ← G3dMatrix.MakeRotate[yAxis, Delta[],,c.incr]};
$WorZRot => {incrSpace ← world; [] ← G3dMatrix.MakeRotate[zAxis, Delta[],, c.incr]};
$EyeXMv => {incrSpace ← eye; [] ← G3dMatrix.MakeTranslate[[Delta[], 0, 0], c.incr]};
$EyeYMv => {incrSpace ← eye; [] ← G3dMatrix.MakeTranslate[[0, Delta[], 0], c.incr]};
$EyeZMv => {incrSpace ← eye; [] ← G3dMatrix.MakeTranslate[[0, 0, Delta[]], c.incr]};
$EyeXRot => {incrSpace ← eye; [] ← G3dMatrix.MakeRotate[xAxis, Delta[],, c.incr]};
$EyeYRot => {incrSpace ← eye; [] ← G3dMatrix.MakeRotate[yAxis, Delta[],, c.incr]};
$EyeZRot => {incrSpace ← eye; [] ← G3dMatrix.MakeRotate[zAxis, Delta[],, c.incr]};
$Ignore => RETURN;
ENDCASE;
IF action # $Par
AND action # $Proxy
AND (
alwaysUpdate
OR c.current.mouse.state = up)
THEN {
Shift incremental transform into standard parameters:
ParameterizeIncrementalTransform[c, action];
incrSpace ← none;
};
IF action = $Proxy
THEN {
-- update the corresponding par control
parControl: Control ← SELECT c.current FROM c.proxy.xMov => c.par.xMov, c.proxy.yMov => c.par.yMov, c.proxy.zMov => c.par.zMov, c.proxy.xRot => c.par.xRot, c.proxy.yRot => c.par.yRot, ENDCASE => c.par.zRot;
Controls.SetSliderDialValue[parControl, c.current.value];
};
c.matrix ← G3dView.MakeCameraMatrix[
IF incrSpace = world THEN c.incr ELSE NIL,
c.scale.value,
[c.par.xMov.value, c.par.yMov.value, c.par.zMov.value],
[c.par.xRot.value, c.par.yRot.value, c.par.zRot.value],
IF incrSpace = eye THEN c.incr ELSE NIL,
IF c.use = shape THEN 0.0 ELSE c.fieldOfView.value,
FALSE, -- c.use = view, FAREWELL TO FRANK's COORDINATE SYSTEM
c.matrix];
IF c.mouse.state = up
OR c.mouse.state = none
THEN
[c.eyePoint, c.lookAt, c.up] ← G3dView.FromScaleMovesRots[
c.scale.value,
[c.par.xMov.value, c.par.yMov.value, c.par.zMov.value],
[c.par.xRot.value, c.par.yRot.value, c.par.zRot.value]
! G3dMatrix.singular => CONTINUE];
};
ParameterizeIncrementalTransform:
PROC [c: CameraControl, action:
ATOM] ~ {
DON'T FUCK WITH THIS CODE, PLEASE!!!!!!!!
Absorb current incremental move/rotate into standard transform (see WorldToViewMatrix)
See the G3dControl CameraControl Model notes by Paul Heckbert.
rotateMode: {body, world} ← world; -- former default was body
construct old rotate matrix:
oldrot: Matrix ← G3dMatrix.Identity[G3dMatrix.ObtainMatrix[]];
oldrot ← G3dMatrix.Rotate[oldrot, xAxis, c.par.xRot.value,,, oldrot];
oldrot ← G3dMatrix.Rotate[oldrot, yAxis, c.par.yRot.value,,, oldrot];
oldrot ← G3dMatrix.Rotate[oldrot, zAxis, c.par.zRot.value,,, oldrot];
merge incremental transformation into standard rotate and translate
SELECT action
FROM
$WorXMv, $WorYMv, $WorZMv => {
-- merge world move into standard move
t: Triple ← [c.par.xMov.value, c.par.yMov.value, c.par.zMov.value];
ti: Triple ← G3dVector.Mul[ [c.incr[3][0], c.incr[3][1], c.incr[3][2]], c.scale.value ];
IF rotateMode = world
THEN {
rInv: Matrix ← G3dMatrix.Transpose[oldrot, G3dMatrix.ObtainMatrix[]]; -- = inverse
t ← G3dVector.Add[t, G3dMatrix.Transform[ti, rInv]];
G3dMatrix.ReleaseMatrix[rInv];
}
ELSE t ← G3dVector.Add[t, ti];
Controls.SetSliderDialValue[c.par.xMov, t.x];
Controls.SetSliderDialValue[c.par.yMov, t.y];
Controls.SetSliderDialValue[c.par.zMov, t.z];
};
$WorXRot, $WorYRot, $WorZRot => {
-- merge world rotate into standard rotate and move
r: Matrix ← G3dMatrix.ObtainMatrix[];
rRot: Matrix ← G3dMatrix.ObtainMatrix[];
parMov: Triple ← [c.par.xMov.value, c.par.yMov.value, c.par.zMov.value];
t: Triple;
IF rotateMode = world
THEN [] ← G3dMatrix.Mul[oldrot, c.incr, r] -- newer rotate matrix
ELSE [] ← G3dMatrix.Mul[c.incr, oldrot, r]; -- new rotate matrix
t ← G3dMatrix.ExtractRotate[r];
Controls.SetSliderDialValue[c.par.xRot, t.x];
Controls.SetSliderDialValue[c.par.yRot, t.y];
Controls.SetSliderDialValue[c.par.zRot, t.z];
IF rotateMode = world
THEN {
rRot: Matrix ← G3dMatrix.Transpose[r, G3dMatrix.ObtainMatrix[]];
t ← G3dMatrix.Transform[parMov, oldrot];
t ← G3dMatrix.Transform[t, rRot];
G3dMatrix.ReleaseMatrix[rRot];
}
ELSE {
[] ← G3dMatrix.Transpose[c.incr, r]; -- r now holds inverse of incremental rotate
t ← G3dMatrix.Transform[parMov, r];
};
Controls.SetSliderDialValue[c.par.xMov, t.x];
Controls.SetSliderDialValue[c.par.yMov, t.y];
Controls.SetSliderDialValue[c.par.zMov, t.z];
G3dMatrix.ReleaseMatrix[r];
};
$EyeXMv, $EyeYMv, $EyeZMv => {
-- merge eye move (dolly) into standard move
rInv: Matrix ← G3dMatrix.Transpose[oldrot, G3dMatrix.ObtainMatrix[]]; -- = inverse
parMov: Triple ← [c.par.xMov.value, c.par.yMov.value, c.par.zMov.value];
eyeMov: Triple ← [c.incr[3][0], c.incr[3][1], c.incr[3][2]]; -- 2 out of 3 are zero
compute Tx ← XTiX (incremental translate vector pre- and post-multiplied by swap X):
eyeMov ← [eyeMov.x, eyeMov.z, eyeMov.y];
compute new globalMove and set the controls:
parMov ← G3dVector.Add[parMov, G3dMatrix.Transform[eyeMov, rInv]];
Controls.SetSliderDialValue[c.par.xMov, parMov.x];
Controls.SetSliderDialValue[c.par.yMov, parMov.y];
Controls.SetSliderDialValue[c.par.zMov, parMov.z];
G3dMatrix.ReleaseMatrix[rInv];
};
$EyeXRot, $EyeYRot, $EyeZRot => {
-- merge eye rotate (gimbal) into standard rotate
t: Triple;
form new standard rotate (R' ← RXRiX, with R= standard rotate, Ri= eye rotate, X=swap)
using the identity RXRi = RXRiXX = (RXRiX)X = R'X
r: Matrix ← G3dMatrix.ObtainMatrix[];
swap: Matrix ← G3dMatrix.ObtainMatrix[];
make a "swap" matrix (to swap y and z axes)
swap^ ← [[1., 0., 0., 0.], [0., 0., 1., 0.], [0., 1., 0., 0.], [0., 0., 0., 1.]];
[] ← G3dMatrix.Mul[oldrot, swap, r];
[] ← G3dMatrix.Mul[r, c.incr, r];
[] ← G3dMatrix.Mul[r, swap, r]; -- RXRiX
t ← G3dMatrix.ExtractRotate[r];
Controls.SetSliderDialValue[c.par.xRot, t.x];
Controls.SetSliderDialValue[c.par.yRot, t.y];
Controls.SetSliderDialValue[c.par.zRot, t.z];
G3dMatrix.ReleaseMatrix[r];
G3dMatrix.ReleaseMatrix[swap];
};
ENDCASE => NULL;
G3dMatrix.ReleaseMatrix[oldrot];
};
GetCameraControlMatrix:
PUBLIC
PROC [camera: CameraControl]
RETURNS [Matrix] ~ {
RETURN[camera.matrix];
};
GetViewMatrix:
PUBLIC
PROC [camera: CameraControl, out: Matrix]
RETURNS [Matrix] ~ {
SetViewMatrix[camera];
RETURN[G3dMatrix.CopyMatrix[camera.view, out]];
};
SetViewMatrix:
PROC [c: CameraControl] ~ {
c.view ← G3dMatrix.Translate[c.matrix, [c.hScreen.value, c.vScreen.value, 0.0], c.view];
};
EyeViewFromCameraControl:
PUBLIC
PROC [camera: CameraControl]
RETURNS [Triple] ~ {
eyePoint, lookAt: Triple;
[eyePoint, lookAt, []] ← G3dView.FromScaleMovesRots[
camera.scale.value,
[camera.par.xMov.value, camera.par.yMov.value, camera.par.zMov.value],
[camera.par.xRot.value, camera.par.yRot.value, camera.par.zRot.value]];
RETURN[G3dVector.Sub[lookAt, eyePoint]];
};
SaveState:
PUBLIC
PROC [camera: CameraControl, state: Camera] ~ {
ab: ArcBall ← camera.arcBall;
state^ ← [
move: [camera.par.xMov.value, camera.par.yMov.value, camera.par.zMov.value],
rotate: [camera.par.xRot.value, camera.par.yRot.value, camera.par.zRot.value],
scale: camera.scale.value,
fieldOfView: camera.fieldOfView.value,
roll: camera.roll,
eyePoint: camera.eyePoint,
lookAt: camera.lookAt,
up: camera.up,
matrix: G3dMatrix.CopyMatrix[camera.matrix, state.matrix]];
};
RestoreState:
PUBLIC
PROC [camera: CameraControl, state: Camera] ~ {
ab: ArcBall ← camera.arcBall;
Controls.SetSliderDialValue[camera.par.xMov, state.move.x];
Controls.SetSliderDialValue[camera.par.yMov, state.move.y];
Controls.SetSliderDialValue[camera.par.zMov, state.move.z];
Controls.SetSliderDialValue[camera.par.xRot, state.rotate.x];
Controls.SetSliderDialValue[camera.par.yRot, state.rotate.y];
Controls.SetSliderDialValue[camera.par.zRot, state.rotate.z];
Controls.SetSliderDialValue[camera.scale, state.scale];
Controls.SetSliderDialValue[camera.fieldOfView, state.fieldOfView];
camera.roll ← state.roll;
camera.eyePoint ← state.eyePoint;
camera.lookAt ← state.lookAt;
camera.up ← state.up;
camera.matrix ← G3dMatrix.CopyMatrix[state.matrix, camera.matrix];
};
LastAdjustedCameraControl:
PUBLIC PROC
RETURNS [CameraControl] ~ {
RETURN[lastAdjustedCamera];
};
CameraControlMessage:
PUBLIC
PROC [camera: CameraControl]
RETURNS [out:
ROPE] ~ {
Add: PROC [r: ROPE] ~ {IF r#NIL THEN out←Rope.Cat[out,IF out=NIL THEN NIL ELSE "; ",r]};
Ok: PROC [c: Control] RETURNS [b: BOOL] ~ {b ← c.viewer # NIL AND c.value # 0.0};
Val:
PROC [c: Control, id:
ROPE]
RETURNS [o:
ROPE] ~ {
IF Ok[c] THEN o ← IO.PutFR["%g%g", IO.real[Real.Round[1000*c.value]/1000], IO.rope[id]];
};
Val3:
PROC [header:
ROPE, c1, c2, c3: Control, id1, id2, ig3d:
ROPE]
RETURNS [out:
ROPE] ~ {
Inner:
PROC [c: Control, id:
ROPE] ~ {
IF NOT Ok[c] THEN RETURN;
IF once THEN out ← Rope.Concat[out, ", "];
once ← TRUE;
out ← IO.PutFR["%g%g", IO.rope[out], IO.rope[Val[c, id]]];
};
once: BOOL ← FALSE;
Inner[c1, id1]; Inner[c2, id2]; Inner[c3, ig3d];
IF once THEN out ← Rope.Cat[header, ": ", out];
};
c: CameraControl ← camera;
Add[Val[c.scale, "scale"]];
Add[Val3["move", c.par.xMov, c.par.yMov, c.par.zMov, "x", "y", "z"]];
Add[Val3["rot", c.par.xRot, c.par.yRot, c.par.zRot, "x", "y", "z"]];
Add[Val[c.fieldOfView, "fov"]];
};
Slice Procedures
InitSlice:
PUBLIC
PROC [data:
REF
ANY ←
NIL, proc: Controls.ControlProc ←
NIL]
RETURNS [slice: Slice]
~ {
slice ← NEW[SliceRep];
slice.x ← Controls.NewControl["x", vSlider, data, -1.0, 1.0, 0.0, proc];
slice.y ← Controls.NewControl["y", vSlider, data, -1.0, 1.0, 0.0, proc];
slice.z ← Controls.NewControl["z", vSlider, data, -1.0, 1.0, 0.0, proc];
slice.phi ← Controls.NewControl["phi", dial, data, 0.0, 360.0, 90.0, proc];
slice.theta ← Controls.NewControl["theta", dial, data, 0.0, 360.0, 0.0, proc];
slice.size ← Controls.NewControl["size", vSlider, data, 0.0, 1.0, 0.1, proc];
UpdateSlice[slice];
};
SetSlice:
PUBLIC
PROC [slice: Slice, plane: Plane, size:
REAL ← 1.0] ~ {
o: Triple ← G3dPlane.CenterOfPlane[plane];
t: Triple ← G3dVector.PolarFromCartesian[[plane.x, plane.y, plane.z]];
SetTripleControls[slice.x, slice.y, slice.z, o];
SetTripleControls[slice.phi, slice.theta, slice.size, [t.x, t.y, size]];
UpdateSlice[slice];
};
UpdateSlice:
PUBLIC PROC [slice: Slice] ~ {
o: Triple ~ [slice.x.value, slice.y.value, slice.z.value];
z: Triple ~ G3dVector.CartesianFromPolar[[slice.phi.value, slice.theta.value, 1.0]];
x: Triple ~ G3dVector.Unit[G3dVector.Cross[IF z # zAxis THEN zAxis ELSE yAxis, z]];
y: Triple ~ G3dVector.Unit[G3dVector.Cross[x, z]];
slice.plane ← G3dPlane.FromPointAndNormal[o, z];
slice.matrix ← G3dMatrix.MakeFromTriad[x, y, z, o, FALSE, slice.matrix];
slice.matrix ← G3dMatrix.LocalScale[slice.matrix, slice.size.value, slice.matrix];
};
DrawSlice:
PUBLIC
PROC [
context: Context,
slice: Slice,
view: Matrix,
viewport: Viewport ← [],
drawType: DrawType ← solid]
~ {
zip: Draw2d.Zip ← Draw2d.GetZip[context];
IF slice #
NIL
AND slice.matrix #
NIL
THEN {
m: Matrix ~ G3dMatrix.Mul[slice.matrix, view];
p: Triple ~ [-slice.plane.x, -slice.plane.y, -slice.plane.z];
q: ARRAY [0..4) OF Pair ← [[-1.0, 1.0], [1.0, 1.0], [1.0, -1.0], [-1.0, -1.0]];
a: ARRAY [0..4) OF Pair;
c: Triple ← G3dMatrix.TransformPair[[0.0, 0.0], slice.matrix];
FOR i: NAT IN [0..4) DO a[i] ← G3dMatrix.TransformPairD[q[i], m]; ENDLOOP;
FOR i:
NAT
IN [0..4)
DO
Draw2d.Line[context, a[i], a[(i+1) MOD 4], drawType, zip];
ENDLOOP;
G3dDraw.Vector[context, c, p, view, viewport,, 0.1];
};
};
MoveSliceInY:
PUBLIC PROC [slice: Slice, amount:
REAL] ~ {
v: Triple ~ G3dMatrix.TransformVec[yAxis, slice.matrix];
o: Triple ← G3dVector.Combine[[slice.x.value, slice.y.value, slice.z.value], 1., v, amount];
Controls.SetSliderDialValue[slice.x, o.x];
Controls.SetSliderDialValue[slice.y, o.y];
Controls.SetSliderDialValue[slice.z, o.z];
UpdateSlice[slice];
};
MoveSliceInZ:
PUBLIC PROC [slice: Slice, amount:
REAL] ~ {
v: Triple ~ G3dMatrix.TransformVec[zAxis, slice.matrix];
o: Triple ← G3dVector.Combine[[slice.x.value, slice.y.value, slice.z.value], 1., v, amount];
Controls.SetSliderDialValue[slice.x, o.x];
Controls.SetSliderDialValue[slice.y, o.y];
Controls.SetSliderDialValue[slice.z, o.z];
UpdateSlice[slice];
};