<<>> <> <> <> <> <<>> DIRECTORY Controls, ControlsPrivate, Convert, Draw2d, G2dVector, G3dBasic, G3dControl, G3dMatrix, G3dQuaternion, G3dVector, G3dView, Imager, ImagerBackdoor, ImagerColor, ImagerFont, IO, MessageWindow, MouseTrap, Real, RealFns, Rope, ViewerClasses, ViewerOps, ViewersWorld, ViewersWorldInstance, ViewersWorldRefType, ViewerTools; G3dArcBallImpl: CEDAR PROGRAM IMPORTS Controls, Draw2d, G2dVector, G3dControl, G3dMatrix, G3dQuaternion, G3dVector, G3dView, Imager, ImagerColor, ImagerFont, IO, MessageWindow, MouseTrap, Real, RealFns, Rope, ViewerOps, ViewersWorld, ViewersWorldInstance, ViewerTools EXPORTS G3dControl ~ BEGIN <> Control: TYPE ~ Controls.Control; ControlProc: TYPE ~ Controls.ControlProc; Pair: TYPE ~ G2dVector.Pair; Triple: TYPE ~ G3dBasic.Triple; ArcBall: TYPE ~ G3dControl.ArcBall; ArcControl: TYPE ~ G3dControl.ArcControl; ArcControlRep: TYPE ~ G3dControl.ArcControlRep; CameraControl: TYPE ~ G3dControl.CameraControl; Matrix: TYPE ~ G3dMatrix.Matrix; Quaternion: TYPE ~ G3dQuaternion.Quaternion; Context: TYPE ~ Imager.Context; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; Viewer: TYPE ~ ViewerClasses.Viewer; ViewerClass: TYPE ~ ViewerClasses.ViewerClass; ViewerClassRec: TYPE ~ ViewerClasses.ViewerClassRec; <> deg: REAL ~ 3.1415926535/180.0; stdFrame: Matrix ~ G3dMatrix.Identity[]; font: ImagerFont.Font ¬ ImagerFont.Find["Xerox/TiogaFonts/Gacha12B"]; SetValueFromRopeProc: TYPE ~ ControlsPrivate.SetValueFromRopeProc; RopeFromValueProc: TYPE ~ ControlsPrivate.RopeFromValueProc; viewersWorld: ViewersWorldRefType.Ref ¬ ViewersWorldInstance.GetWorld[]; <> InitArcBall: PUBLIC PROC [ camera: CameraControl, clientProc: ControlProc, clientData: REF ANY] RETURNS [a: ArcBall] ~ { NewArcControl: PROC [flavor: ATOM] RETURNS [ac: ArcControl] ~ { name: ROPE ¬ IF flavor = $ArcRot THEN "Rotate" ELSE "Translate"; proc: ControlProc ¬ IF flavor = $ArcRot THEN ArcRotControl ELSE ArcTranControl; ropeProc: RopeFromValueProc ¬ IF flavor = $ArcRot THEN RopeFromRotate ELSE RopeFromTranslate; valueProc: SetValueFromRopeProc ¬ IF flavor = $ArcRot THEN SetRotateFromRope ELSE SetTranslateFromRope; ac ¬ NEW[ArcControlRep ¬ [camera: camera, clientProc: clientProc, clientData: clientData, qAbs: qAbs, tAbs: tCam]]; ac.rope ¬ IF flavor = $ArcRot THEN "0.00 0.00 0.00 1.00" ELSE "0.000 0.000 0.000"; ac.frame ¬ G3dMatrix.Identity[]; ac.control ¬ Controls.NewControl[name: name, proc: proc, w: 136, h: 136, textLocation: [up, left], flavor: flavor, clientData: a, color: ImagerColor.ColorFromRGB[[0.0, 0.7, 0.0]]]; ac.control.contourRef ¬ NEW[RopeFromValueProc ¬ ropeProc]; ac.control.sketchRef ¬ NEW[SetValueFromRopeProc ¬ valueProc]; <> <> }; rCam: Triple ¬ [camera.par.xRot.value, camera.par.yRot.value, camera.par.zRot.value]; tCam: Triple ¬ [camera.par.xMov.value, camera.par.yMov.value, camera.par.zMov.value]; qAbs: Quaternion ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg, rCam.y*deg, rCam.z*deg]; a ¬ NEW[G3dControl.ArcBallRep]; a.rotate ¬ NewArcControl[$ArcRot]; a.translate ¬ NewArcControl[$ArcTran]; }; SetArcBall: PUBLIC PROC [arcBall: ArcBall, rotate, translate: Triple ¬ []] ~ { SetArcControl: PROC [ac: ArcControl] ~ { ac.firstPlace ¬ ac.lastPlace ¬ ac.firstPrev ¬ ac.lastPrev ¬ [0, 0, 1]; ac.frame ¬ G3dMatrix.Identity[ac.frame]; ac.ropeBasis ¬ [0, 0, 0]; }; SetArcControl[arcBall.rotate]; SetArcControl[arcBall.translate]; arcBall.rotate.qAbs ¬ G3dQuaternion.FromXYZAngles[rotate.x*deg, rotate.y*deg, rotate.z*deg]; arcBall.rotate.qInc ¬ [0, 0, 0, 1]; arcBall.rotate.qBasis ¬ arcBall.rotate.ropeBasis ¬ [0, 0, 0]; arcBall.translate.tAbs ¬ translate; arcBall.translate.tInc ¬ [0, 0, 0]; }; SetArcBallSize: PUBLIC PROC [arcBall: ArcBall, size: INTEGER] ~ { arcBall.rotate.control.w ¬ arcBall.rotate.control.h ¬ size; arcBall.translate.control.w ¬ arcBall.translate.control.h ¬ size; }; MakeClass: PROC [flavor: ATOM] RETURNS [ViewerClass] ~ { dialClassRec: ViewerClassRec ¬ ViewerOps.FetchViewerClass[$ControlsSliderDial]­; dialClassRec.paint ¬ IF flavor = $ArcRot THEN PaintArcRotViewer ELSE PaintArcTranViewer; RETURN[NEW[ViewerClassRec ¬ dialClassRec]]; }; ArcRotControl: ControlProc ~ { arcBall: ArcBall ¬ NARROW[control.clientData]; arcRot: ArcControl ¬ arcBall.rotate; cam: CameraControl ¬ arcRot.camera; viewer: Viewer ¬ control.viewer; center: Pair ¬ [viewer.cx+(viewer.cw-2)/2.0, viewer.cy+(viewer.ch-2)/2.0]; radius: REAL ¬ (viewer.cw-1)/2.0-1.5; place: Triple ¬ MouseOnSphere[[control.mouse.pos.x, control.mouse.pos.y], center, radius]; qInc: Quaternion; tAbs: Triple ¬ [cam.par.xMov.value, cam.par.yMov.value, cam.par.zMov.value]; mouse: Pair ¬ [control.mouse.pos.x-center.x, control.mouse.pos.y-center.y]; cam.current ¬ control; IF mouse.x*mouse.x+mouse.y*mouse.y > radius*radius THEN { SELECT control.mouse.state FROM down => arcRot.inCorner ¬ TRUE; up => { arcRot.inCorner ¬ FALSE; IF mouse.y > 0 AND mouse.x < 0 THEN { arcBall.originBody ¬ NOT arcBall.originBody; ViewerOps.PaintViewer[viewer, client, FALSE, $BodyOrigin]; }; IF mouse.y > 0 AND mouse.x > 0 THEN { control.whatChanged ¬ $ArcBallReset; IF arcRot.clientProc # NIL THEN arcRot.clientProc[control, arcRot.clientData]; }; }; ENDCASE; RETURN; }; IF arcRot.inCorner THEN { IF control.mouse.state = up THEN arcRot.inCorner ¬ FALSE; RETURN; }; IF control.whatChanged = $TypedIn THEN RETURN; SELECT control.mouse.state FROM down => { MouseTrap.TrapTillMouseUp[control.viewer, TRUE, TRUE]; arcRot.qAbs ¬ G3dQuaternion.FromXYZAngles[ cam.par.xRot.value*deg, cam.par.yRot.value*deg, cam.par.zRot.value*deg]; arcRot.frame ¬ G3dQuaternion.ToMatrix[ [arcRot.qAbs.x, arcRot.qAbs.z, -arcRot.qAbs.y, arcRot.qAbs.w], arcRot.frame]; IF control.mouse.controlKey OR control.mouse.shiftKey THEN { place ¬ ForceToAxis[place, IF control.mouse.controlKey THEN arcRot.frame ELSE NIL]; SetMousePosition[place, center, radius, viewer]; }; arcRot.firstPlace ¬ arcRot.lastPlace ¬ place; qInc ¬ G3dQuaternion.Id[]; arcRot.qInc ¬ arcRot.qAbs; <> arcRot.inhibitPrint ¬ TRUE; }; held => { arcRot.lastPlace ¬ SELECT TRUE FROM control.mouse.controlKey => ForceToPlane[place, arcRot.firstPlace, arcRot.frame], control.mouse.shiftKey => ForceToPlane[place, arcRot.firstPlace, NIL], ENDCASE => place; qInc ¬ QuatFromArc[arcRot.firstPlace, arcRot.lastPlace]; arcRot.qInc ¬ G3dQuaternion.Mul[qInc, arcRot.qAbs]; <> IF NOT arcBall.originBody THEN tAbs ¬ G3dQuaternion.Rot[qInc, tAbs]; arcRot.inhibitPrint ¬ TRUE; }; up => { arcRot.lastPlace ¬ SELECT TRUE FROM control.mouse.controlKey => ForceToPlane[place, arcRot.firstPlace, arcRot.frame], control.mouse.shiftKey => ForceToPlane[place, arcRot.firstPlace, NIL], ENDCASE => place; qInc ¬ QuatFromArc[arcRot.firstPlace, arcRot.lastPlace]; <> arcRot.qInc ¬ arcRot.qAbs ¬ G3dQuaternion.Mul[qInc, arcRot.qAbs]; IF NOT arcBall.originBody THEN tAbs ¬ G3dQuaternion.Rot[qInc, tAbs]; UpdateCameraValues[cam, arcRot.qInc, tAbs]; arcRot.inhibitPrint ¬ FALSE; ViewerTools.SetContents[control.status, RopeFromRotate[control].valueRope]; ViewerOps.PaintViewer[control.status, client]; RepaintTranslate[control.outerData.controls]; }; ENDCASE; ViewerOps.PaintViewer[viewer, client, FALSE, $Arc]; -- may prevent feedback lag cam.matrix ¬ MakeCameraMatrix[ cam.scale.value, tAbs, arcRot.qInc, IF cam.use = view THEN cam.fieldOfView.value ELSE 0.0, <> cam.matrix]; IF control.mouse.state = up THEN cam.inverse ¬ G3dMatrix.Invert[cam.matrix, cam.inverse]; IF arcRot.clientProc # NIL THEN arcRot.clientProc[control, arcRot.clientData]; }; ArcTranControl: ControlProc ~ { arcTran: ArcControl ¬ NARROW[control.clientData, ArcBall].translate; cam: CameraControl ¬ arcTran.camera; viewer: Viewer ¬ control.viewer; center: Pair ¬ [viewer.cx+(viewer.cw-2)/2.0, viewer.cy+(viewer.ch-2)/2.0]; radius: REAL ¬ (viewer.cw-1)/2.0-1.5; place: Triple ¬ MouseOnSphere[[control.mouse.pos.x, control.mouse.pos.y], center, radius]; rCam: Triple ¬ [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value]; cam.current ¬ control; IF control.whatChanged = $TypedIn THEN RETURN; arcTran.tInc ¬ [0, 0, 0]; IF arcTran.qBasis # rCam THEN { arcTran.qAbs ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg, rCam.y*deg, rCam.z*deg]; arcTran.qBasis ¬ rCam; }; SELECT control.mouse.state FROM down => { tCam: Triple ¬ [cam.par.xMov.value, cam.par.yMov.value, cam.par.zMov.value]; MouseTrap.TrapTillMouseUp[control.viewer, FALSE, FALSE]; arcTran.frame ¬ G3dQuaternion.ToMatrix[ [arcTran.qAbs.x, arcTran.qAbs.z, -arcTran.qAbs.y, arcTran.qAbs.w], arcTran.frame]; SELECT TRUE FROM control.mouse.controlKey => place ¬ ForceToAxis[place, arcTran.frame]; control.mouse.shiftKey => place ¬ ForceToAxis[place, NIL]; ENDCASE; arcTran.firstPlace ¬ place; arcTran.lastPlace ¬ [0, 0, 1]; arcTran.qTan ¬ G3dQuaternion.FromComponents[place.y, -place.x, 0, place.z+1]; arcTran.qTan ¬ G3dQuaternion.Unit[arcTran.qTan]; arcTran.frame ¬ G3dQuaternion.ToMatrix[ G3dQuaternion.Mul[arcTran.qTan, [arcTran.qAbs.x, arcTran.qAbs.z, -arcTran.qAbs.y, arcTran.qAbs.w]], arcTran.frame]; arcTran.tAbs ¬ arcTran.tInc ¬ tCam; <> SetMousePosition[[0, 0, 1], center, radius, viewer]; arcTran.inhibitPrint ¬ TRUE; }; held => { arcTran.lastPlace ¬ SELECT TRUE FROM control.mouse.controlKey => place ¬ ForceToPlane[place, [0, 0, 1], arcTran.frame], control.mouse.shiftKey => place ¬ ForceToPlane[place, [0, 0, 1], NIL], ENDCASE => place; arcTran.tInc ¬ G3dVector.Add[arcTran.tAbs, TransFromArc[arcTran.qTan, place]]; arcTran.inhibitPrint ¬ TRUE; }; up => { arcTran.lastPlace ¬ SELECT TRUE FROM control.mouse.controlKey => ForceToPlane[place, [0, 0, 1], arcTran.frame], control.mouse.shiftKey => ForceToPlane[place, [0, 0, 1], NIL], ENDCASE => place; arcTran.tInc ¬ G3dVector.Add[arcTran.tAbs, TransFromArc[arcTran.qTan, place]]; <> <> arcTran.tAbs ¬ arcTran.tInc; UpdateCameraTranslate[cam]; arcTran.inhibitPrint ¬ FALSE; ViewerTools.SetContents[control.status, RopeFromTranslate[control].valueRope]; ViewerOps.PaintViewer[control.status, client]; }; ENDCASE; ViewerOps.PaintViewer[viewer, client, FALSE, $Line]; -- may prevent feedback lag cam.matrix ¬ MakeCameraMatrix[ cam.scale.value, arcTran.tInc, arcTran.qAbs, IF cam.use = view THEN cam.fieldOfView.value ELSE 0.0, <> cam.matrix]; IF control.mouse.state = up THEN cam.inverse ¬ G3dMatrix.Invert[cam.matrix, cam.inverse]; IF arcTran.clientProc # NIL THEN arcTran.clientProc[control, arcTran.clientData]; }; <> FrameToStd: PROC [frame: Matrix ¬ NIL, p: Triple, w: REAL ¬ 0.0] RETURNS [pp: Triple] ~ { < standard axes.>> IF frame = NIL THEN RETURN[p]; SELECT p.x FROM 0.0 => pp ¬ [0.0, 0.0, 0.0]; 1.0 => pp ¬ [frame[0][0], frame[0][1], frame[0][2]]; ENDCASE => pp ¬ [p.x*frame[0][0], p.x*frame[0][1], p.x*frame[0][2]]; SELECT p.y FROM 0.0 => NULL; 1.0 => pp ¬ G3dVector.Add[pp, [frame[1][0], frame[1][1], frame[1][2]]]; ENDCASE => pp ¬ G3dVector.Add[pp, [p.y*frame[1][0], p.y*frame[1][1], p.y*frame[1][2]]]; SELECT p.z FROM 0.0 => NULL; 1.0 => pp ¬ G3dVector.Add[pp, [frame[2][0], frame[2][1], frame[2][2]]]; ENDCASE => pp ¬ G3dVector.Add[pp, [p.z*frame[2][0], p.z*frame[2][1], p.z*frame[2][2]]]; SELECT w FROM 0.0 => NULL; 1.0 => pp ¬ G3dVector.Add[pp, [frame[3][0], frame[3][1], frame[3][2]]]; ENDCASE => pp ¬ G3dVector.Add[pp, [w*frame[3][0], w*frame[3][1], w*frame[3][2]]]; }; StdToFrame: PROC [frame: Matrix ¬ NIL, p: Triple, w: REAL ¬ 0.0] RETURNS [pp: Triple] ~ { < standard axes.>> IF frame = NIL THEN RETURN[p]; SELECT w FROM 0.0 => NULL; 1.0 => p ¬ G3dVector.Sub[p, [frame[3][0], frame[3][1], frame[3][2]]]; ENDCASE => p ¬ G3dVector.Sub[p, [w*frame[3][0], w*frame[3][1], w*frame[3][2]]]; pp ¬ [ p.x*frame[0][0]+p.y*frame[0][1]+p.z*frame[0][2], p.x*frame[1][0]+p.y*frame[1][1]+p.z*frame[1][2], p.x*frame[2][0]+p.y*frame[2][1]+p.z*frame[2][2]]; }; MaxAxisIndex: PROC [frame: Matrix ¬ NIL, p: Triple] RETURNS [index: CARDINAL] ~ { <> pRelF: Triple ¬ StdToFrame[frame, p]; flip: ARRAY [0..3) OF CARDINAL ¬ ALL[0]; IF frame = NIL THEN frame ¬ stdFrame; <> IF (frame[0][2] < 0) OR (frame[0][2] = 0 AND pRelF.x < 0) THEN {pRelF.x ¬ -pRelF.x; flip[0] ¬ 4}; IF (frame[1][2] < 0) OR (frame[1][2] = 0 AND pRelF.y < 0) THEN {pRelF.y ¬ -pRelF.y; flip[1] ¬ 4}; IF (frame[2][2] < 0) OR (frame[2][2] = 0 AND pRelF.z < 0) THEN {pRelF.z ¬ -pRelF.z; flip[2] ¬ 4}; RETURN[IF pRelF.y > pRelF.z THEN IF pRelF.x > pRelF.y THEN 0+flip[0] ELSE 1+flip[1] ELSE IF pRelF.x > pRelF.z THEN 0+flip[0] ELSE 2+flip[2]]; }; ForceToAxis: PROC [p: Triple, frame: Matrix ¬ NIL] RETURNS [pp: Triple] ~ { < standard axes.>> index: CARDINAL ¬ MaxAxisIndex[frame, p]; i: CARDINAL ¬ index MOD 4; IF frame = NIL THEN frame ¬ stdFrame; pp ¬ [frame[i][0], frame[i][1], frame[i][2]]; IF index >= 4 THEN pp ¬ G3dVector.Negate[pp]; }; ForceToPlane: PROC [p, first: Triple, frame: Matrix ¬ NIL] RETURNS [pp: Triple] ~ { < standard axes.>> Norm: PROC [a, b: REAL] RETURNS [REAL] ~ INLINE {RETURN[RealFns.SqRt[a*a+b*b]]}; firstIndex: CARDINAL ¬ MaxAxisIndex[frame, first] MOD 4; pRelF: Triple ¬ StdToFrame[frame, p]; minIndex: INT ¬ SELECT firstIndex FROM 0 => IF ABS[pRelF.y] < ABS[pRelF.z] THEN 1 ELSE 2, 1 => IF ABS[pRelF.x] < ABS[pRelF.z] THEN 0 ELSE 2, 2 => IF ABS[pRelF.x] < ABS[pRelF.y] THEN 0 ELSE 1, ENDCASE => -1; SELECT minIndex FROM 0 => {s: REAL ¬ 1.0/Norm[pRelF.y, pRelF.z]; pp ¬ [0.0, s*pRelF.y, s*pRelF.z]}; 1 => {s: REAL ¬ 1.0/Norm[pRelF.x, pRelF.z]; pp ¬ [s*pRelF.x, 0.0, s*pRelF.z]}; 2 => {s: REAL ¬ 1.0/Norm[pRelF.x, pRelF.y]; pp ¬ [s*pRelF.x, s*pRelF.y, 0.0]}; ENDCASE; pp ¬ FrameToStd[frame, pp]; IF pp.z < 0 THEN pp ¬ G3dVector.Negate[pp]; }; <> SetCameraToIdentity: PROC [camera: CameraControl] ~ { G3dControl.SetTripleControls[camera.par.xRot, camera.par.yRot, camera.par.zRot, []]; G3dControl.SetTripleControls[camera.par.xMov, camera.par.yMov, camera.par.zMov, []]; G3dControl.UpdateControl[camera, camera.scale, 1.0]; }; UpdateCameraValues: PROC [cam: CameraControl, qAbs: Quaternion, tAbs: Triple] ~ { matTmp: Matrix ¬ G3dMatrix.ObtainMatrix[]; rCam: Triple ¬ G3dMatrix.ExtractRotate[G3dQuaternion.ToMatrix[qAbs, matTmp]]; <> <> tCam: Triple ¬ tAbs; Controls.SetSliderDialValue[cam.par.xRot, rCam.x]; Controls.SetSliderDialValue[cam.par.yRot, rCam.y]; Controls.SetSliderDialValue[cam.par.zRot, rCam.z]; Controls.SetSliderDialValue[cam.par.xMov, tCam.x]; Controls.SetSliderDialValue[cam.par.yMov, tCam.y]; Controls.SetSliderDialValue[cam.par.zMov, tCam.z]; UpdateCameraVectors[cam]; G3dMatrix.ReleaseMatrix[matTmp]; }; UpdateCameraTranslate: PROC [cam: CameraControl] ~ { Controls.SetSliderDialValue[cam.par.xMov, cam.arcBall.translate.tAbs.x]; Controls.SetSliderDialValue[cam.par.yMov, cam.arcBall.translate.tAbs.y]; Controls.SetSliderDialValue[cam.par.zMov, cam.arcBall.translate.tAbs.z]; UpdateCameraVectors[cam]; }; UpdateCameraVectors: PROC [cam: CameraControl] ~ { [cam.eyePoint, cam.lookAt, cam.up] ¬ G3dView.FromScaleMovesRots[ cam.scale.value, [cam.par.xMov.value, cam.par.yMov.value, cam.par.zMov.value], [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value] ! G3dMatrix.singular => CONTINUE]; }; MakeCameraMatrix: PROC [ scale: REAL ¬ 1.0, move: Triple ¬ [0.0, 0.0, 0.0], rotate: Quaternion ¬ [0.0, 0.0, 0.0, 1.0], fieldOfView: REAL ¬ 0.0, <> out: Matrix ¬ NIL] RETURNS [Matrix] ~ { <> <> out ¬ G3dQuaternion.ToMatrix[rotate, out]; <> [] ¬ G3dMatrix.Scale[out, scale, out]; [] ¬ G3dMatrix.Translate[out, move, out]; <> <> <> <> <> <> <> IF fieldOfView # 0.0 THEN { t: Matrix ¬ G3dMatrix.ObtainMatrix[]; [] ¬ G3dMatrix.Mul[out, G3dMatrix.MakePerspective[10., 0., fieldOfView, t], out]; G3dMatrix.ReleaseMatrix[t]; }; <> RETURN[out]; }; <> BlinkMessage: PROC [message: ROPE ¬ NIL] ~ { IF message # NIL THEN MessageWindow.Append[message, TRUE]; MessageWindow.Blink[]; }; RopeFromQuaternion: PROC [q: Quaternion] RETURNS [rope: ROPE ¬ NIL] ~ { rope ¬ IO.PutFLR["%.2f %.2f %.2f %.2f", LIST[IO.real[q.x], IO.real[q.y], IO.real[q.z], IO.real[q.w]]]; }; RopeFromRotate: ControlsPrivate.RopeFromValueProc ~ { arcRot: ArcControl ¬ NARROW[control.clientData, ArcBall].rotate; cam: CameraControl ¬ arcRot.camera; rCam: Triple ¬ [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value]; valueRope ¬ arcRot.rope; IF G3dVector.Equal[arcRot.ropeBasis, rCam] THEN RETURN[arcRot.rope, FALSE] ELSE { <> <> IF (repaint ¬ NOT arcRot.inhibitPrint) THEN { q: Quaternion ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg,rCam.y*deg,rCam.z*deg]; valueRope ¬ arcRot.rope ¬ RopeFromQuaternion[q]; arcRot.ropeBasis ¬ rCam; }; }; }; SetRotateFromRope: ControlsPrivate.SetValueFromRopeProc ~ { stream: STREAM ¬ IO.RIS[valueRope]; viewer: Viewer ¬ control.viewer; arcRot: ArcControl ¬ NARROW[control.clientData, ArcBall].rotate; cam: CameraControl ¬ arcRot.camera; rCam: Triple ¬ [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value]; tCam: Triple ¬ [cam.par.xMov.value, cam.par.yMov.value, cam.par.zMov.value]; qAbs: Quaternion ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg, rCam.y*deg, rCam.z*deg]; tAbs: Triple ¬ G3dQuaternion.Rot[qAbs, tCam]; { ENABLE IO.Error, IO.EndOfStream => GOTO BadScan; qAbs.x ¬ IO.GetReal[stream]; qAbs.y ¬ IO.GetReal[stream]; qAbs.z ¬ IO.GetReal[stream]; qAbs.w ¬ IO.GetReal[stream]; arcRot.qAbs ¬ arcRot.qInc ¬ qAbs ¬ G3dQuaternion.Unit[qAbs]; [arcRot.firstPlace, arcRot.lastPlace] ¬ ArcFromQuat[qAbs]; ViewerOps.PaintViewer[viewer, client, TRUE]; UpdateCameraValues[cam, qAbs, tAbs]; arcRot.rope ¬ RopeFromQuaternion[qAbs]; arcRot.ropeBasis ¬ rCam; ViewerTools.SetContents[control.status, arcRot.rope]; ViewerOps.PaintViewer[control.status, client]; RepaintTranslate[control.outerData.controls]; control.valuePrev ¬ Real.Floor[control.valuePrev]; control.value ¬ 1.0 - control.valuePrev; -- nonsensical value, but force change IF cam.proc # NIL THEN cam.proc[control, cam.clientData]; -- call client proc, if any EXITS BadScan => BlinkMessage["Type x y z w."]; }; }; RopeFromTriple: PROC [t: Triple] RETURNS [rope: ROPE ¬ NIL] ~ { rope ¬ IO.PutFR["%.3f %.3f %.3f", IO.real[t.x], IO.real[t.y], IO.real[t.z]]; }; RopeFromTranslate: ControlsPrivate.RopeFromValueProc ~ { arcTran: ArcControl ¬ NARROW[control.clientData, ArcBall].translate; cam: CameraControl ¬ arcTran.camera; tCam: Triple ¬ [cam.par.xMov.value, cam.par.yMov.value, cam.par.zMov.value]; rCam: Triple ¬ [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value]; valueRope ¬ arcTran.rope; IF arcTran.qBasis = rCam THEN { IF arcTran.ropeBasis = tCam THEN RETURN[valueRope, FALSE]; } ELSE { arcTran.qAbs ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg, rCam.y*deg, rCam.z*deg]; arcTran.qBasis ¬ rCam; }; <> IF (repaint ¬ NOT arcTran.inhibitPrint) THEN { arcTran.ropeBasis ¬ tCam; tCam ¬ G3dQuaternion.Rot[arcTran.qAbs, tCam]; valueRope ¬ arcTran.rope ¬ RopeFromTriple[tCam]; }; }; SetTranslateFromRope: ControlsPrivate.SetValueFromRopeProc ~ { stream: STREAM ¬ IO.RIS[valueRope]; viewer: Viewer ¬ control.viewer; arcTran: ArcControl ¬ NARROW[control.clientData, ArcBall].translate; cam: CameraControl ¬ arcTran.camera; rCam: Triple ¬ [cam.par.xRot.value, cam.par.yRot.value, cam.par.zRot.value]; tScan: Triple; IF arcTran.qBasis # rCam THEN { arcTran.qAbs ¬ G3dQuaternion.FromXYZAngles[rCam.x*deg, rCam.y*deg, rCam.z*deg]; arcTran.qBasis ¬ rCam; }; { ENABLE IO.Error, IO.EndOfStream => GOTO BadScan; tScan.x ¬ IO.GetReal[stream]; tScan.y ¬ IO.GetReal[stream]; tScan.z ¬ IO.GetReal[stream]; arcTran.tAbs ¬ arcTran.tInc ¬ G3dQuaternion.Rot[G3dQuaternion.Conjugate[arcTran.qAbs], tScan]; UpdateCameraTranslate[cam]; [arcTran.firstPlace, arcTran.lastPlace, arcTran.qTan] ¬ ArcFromTrans[arcTran.tAbs]; ViewerOps.PaintViewer[viewer, client, TRUE]; arcTran.rope ¬ RopeFromTriple[tScan]; arcTran.ropeBasis ¬ tScan; ViewerTools.SetContents[control.status, arcTran.rope]; ViewerOps.PaintViewer[control.status, client]; control.valuePrev ¬ Real.Floor[control.valuePrev]; control.value ¬ 1.0 - control.valuePrev; -- nonsensical value, but force change IF cam.proc # NIL THEN cam.proc[control, cam.clientData]; -- call client proc, if any EXITS BadScan => BlinkMessage["Type x y z."]; }; }; RepaintTranslate: PROC [controlList: Controls.ControlList] ~ { <> FOR cl: Controls.ControlList ¬ controlList, cl.rest WHILE cl # NIL DO IF cl.first.type = dial AND Rope.Equal[cl.first.name, "Translate"] THEN {Controls.SetSliderDialValue[cl.first, cl.first.value, TRUE]; EXIT}; ENDLOOP; }; <> MouseOnSphere: PROC [mouse: Pair, center: Pair ¬ [0, 0], radius: REAL ¬ 1.0] RETURNS [sphere: Triple] ~ { <> dSq: REAL; sphere.x ¬ (mouse.x-center.x)/radius; sphere.y ¬ (mouse.y-center.y)/radius; IF (dSq ¬ sphere.x*sphere.x+sphere.y*sphere.y) > 1.0 THEN { d: REAL ¬ RealFns.SqRt[dSq]; sphere.x ¬ sphere.x/d; sphere.y ¬ sphere.y/d; dSq ¬ 1.0; }; sphere.z ¬ RealFns.SqRt[1.0-dSq]; }; <<>> MouseFromSphere: PROC [sphere: Triple, center: Pair ¬ [0, 0], radius: REAL ¬ 1.0] RETURNS [mouse: Pair] ~ { <> mouse.x ¬ radius*sphere.x + center.x; mouse.y ¬ radius*sphere.y + center.y; }; <<>> QuatFromArc: PROC [p0, p1: Triple] RETURNS [q: Quaternion] ~ { <> q ¬ G3dQuaternion.Mul[G3dQuaternion.FromVector[p1], G3dQuaternion.Conjugate[G3dQuaternion.FromVector[p0]]]; q ¬ G3dQuaternion.FromComponents[x: q.x, y: -q.y, z: -q.z, w: q.w]; }; <<>> ArcFromQuat: PROC [q: Quaternion] RETURNS [p0, p1: Triple] ~ { <> q ¬ G3dQuaternion.FromComponents[x: q.x, y: -q.y, z: -q.z, w: q.w]; IF q.x = 0.0 AND q.y = 0.0 THEN p0 ¬ [0.0, 1.0, 0.0] ELSE {norm: REAL ¬ RealFns.SqRt[q.x*q.x+q.y*q.y]; p0 ¬ [-q.y/norm, q.x/norm, 0.0]}; p1 ¬ G3dQuaternion.Vector[G3dQuaternion.Mul[q, G3dQuaternion.FromVector[p0]]]; IF q.w < 0.0 THEN p0 ¬ [-p0.x, -p0.y, 0.0]; }; <<>> TransFromArc: PROC [q: Quaternion, p1: Triple] RETURNS [tInc: Triple] ~ { <> scale: REAL; <> <> IF p1.z = 0.0 THEN p1.z ¬ Real.FScale[MAX[ABS[p1.x], ABS[p1.y]]/RealFns.SqRt[Real.LargestNumber], 1]; scale ¬ 2.0/p1.z; tInc ¬ [p1.x*scale, p1.y*scale, 0]; tInc ¬ G3dQuaternion.Rot[G3dQuaternion.Conjugate[q], tInc]; tInc ¬ [tInc.x, tInc.y, tInc.z]; }; <<>> ArcFromTrans: PROC [tArg: Triple] RETURNS [p0, p1: Triple, q: Quaternion] ~ { <> tAbs: Triple ¬ [tArg.x, tArg.y, tArg.z]; xySqrs: REAL ¬ tAbs.x*tAbs.x+tAbs.y*tAbs.y; len: REAL ¬ RealFns.SqRt[xySqrs+tAbs.z*tAbs.z]; scale: REAL ¬ RealFns.SqRt[xySqrs]; IF scale = 0 THEN p0 ¬ [0, 1, 0] ELSE p0 ¬ [-tAbs.y/scale, tAbs.x/scale, 0]; q ¬ G3dQuaternion.Unit[G3dQuaternion.FromComponents[p0.y, -p0.x, 0, p0.z+1]]; tAbs ¬ G3dVector.Add[G3dQuaternion.Rot[q, tAbs], [0, 0, 1]]; p1 ¬ G3dVector.Unit[[tAbs.x/2.0, tAbs.y/2.0, tAbs.z]]; }; <<>> SetMousePosition: PROC [place: Triple, center: Pair ¬ [0, 0], radius: REAL ¬ 1.0, v: Viewer] ~ { <> mPlace: Pair ¬ MouseFromSphere[place, center, radius]; pos: MouseTrap.Position ¬ MouseTrap.UserToMouseCoords[v, Real.Round[mPlace.x], Real.Round[mPlace.y]]; ViewersWorld.SetMousePosition[viewersWorld, pos.x, pos.y]; }; PaintArcRotViewer: ViewerClasses.PaintProc ~ { ShowTopLeftChar: PROC [char: CHAR] ~ { Imager.SetFont[context, font]; Imager.SetXY[context, [self.cx+4, self.cy+self.ch-16]]; Imager.ShowChar[context, char]; }; arcBall: ArcBall ¬ NARROW[NARROW[self.data, Control].clientData]; arcRot: ArcControl ¬ arcBall.rotate; radius: REAL ¬ (self.cw-1)/2.0-1.5; center: Pair ¬ [self.cx+radius, self.cy+radius]; SELECT whatChanged FROM $BodyOrigin => { Imager.SetColor[context, Imager.white]; ShowTopLeftChar[IF arcBall.originBody THEN 'C ELSE 'B]; Imager.SetColor[context, arcRot.control.color]; ShowTopLeftChar[IF arcBall.originBody THEN 'B ELSE 'C]; }; $ClearArc => { Imager.SetColor[context, Imager.white]; FastArc[context, arcRot.firstPrev, arcRot.lastPrev, center, radius]; Imager.SetColor[context, arcRot.control.color]; Draw2d.Square[context, center, 2]; }; $Arc => { Imager.SetColor[context, Imager.white]; FastArc[context, arcRot.firstPrev, arcRot.lastPrev, center, radius]; Imager.SetColor[context, arcRot.control.color]; FastArc[context, arcRot.firstPlace, arcRot.lastPlace, center, radius]; arcRot.firstPrev ¬ arcRot.firstPlace; arcRot.lastPrev ¬ arcRot.lastPlace; }; NIL => { Imager.SetColor[context, arcRot.control.color]; Draw2d.Circle[context, center, radius]; FastArc[context, arcRot.firstPlace, arcRot.lastPlace, center, radius]; ShowTopLeftChar[IF arcBall.originBody THEN 'B ELSE 'C]; Imager.SetXY[context, [self.cx+self.cw-16, self.cy+self.ch-16]]; Imager.ShowRope[context, "UN"]; }; ENDCASE; }; <<>> PaintArcTranViewer: ViewerClasses.PaintProc ~ { ToScreen: PROC [p: Triple] RETURNS [Pair] ~ INLINE { RETURN[G2dVector.Add[G2dVector.Mul[[p.x, p.y], radius], center]]; }; control: Control ¬ NARROW[self.data]; arcTran: ArcControl ¬ NARROW[control.clientData, ArcBall].translate; center: Pair ¬ [self.cx+(self.cw-2)/2.0, self.cy+(self.ch-2)/2.0]; radius: REAL ¬ (self.cw-1)/2.0-1.5; firstPrev: Pair ¬ ToScreen[arcTran.firstPrev]; lastPrev: Pair ¬ ToScreen[arcTran.lastPrev]; firstPlace: Pair ¬ ToScreen[arcTran.firstPlace]; lastPlace: Pair ¬ ToScreen[arcTran.lastPlace]; IF arcTran.lastPlace = [0, 0, 1] THEN { Imager.SetColor[context, Imager.white]; Draw2d.Square[context, firstPrev, 2]; Imager.SetColor[context, arcTran.control.color]; Draw2d.Circle[context, center, radius]; }; SELECT whatChanged FROM $ClearArc => { Imager.SetColor[context, Imager.white]; Draw2d.Line[context, center, lastPrev]; Draw2d.Square[context, firstPlace, 2]; Imager.SetColor[context, arcTran.control.color]; Draw2d.Square[context, center, 2]; }; $Line => { Imager.SetColor[context, Imager.white]; Draw2d.Line[context, center, lastPrev]; Imager.SetColor[context, arcTran.control.color]; Draw2d.Line[context, center, lastPlace]; arcTran.firstPrev ¬ arcTran.firstPlace; arcTran.lastPrev ¬ arcTran.lastPlace; }; NIL => { Imager.SetColor[context, arcTran.control.color]; Draw2d.Circle[context, center, radius]; Draw2d.Line[context, center, lastPlace]; }; ENDCASE; Draw2d.Square[context, firstPlace, 2]; }; <<>> DrawBelt: PROC [context: Context, n: Triple, center: Pair, radius: REAL] ~ { p: Triple ¬ IF n.z = 1.0 THEN [0, 1, 0] ELSE G3dVector.Unit[[n.y, -n.x, 0]]; m: Triple ¬ G3dVector.Cross[p, n]; FastBelly[context, p, m, G3dVector.Negate[p], center, radius, solid]; }; Bisect: PROC [a, b: Triple] RETURNS [Triple] ~ INLINE { RETURN[G3dVector.Unit[G3dVector.Add[a, b]]]; }; FastArc: PROC [context: Context, p000, p200: Triple, center: Pair, radius: REAL] ~ { <> p100: Triple ¬ Bisect[p000, p200]; FastBelly[context, p000, p100, p200, center, radius, solid, TRUE]; }; FastBelly: PROC [ context: Context, p000, p100, p200: Triple, center: Pair, radius: REAL, drawType: Draw2d.DrawType, navel: BOOL ¬ FALSE] ~ { <> Double: PROC [a, b: Pair] RETURNS [Pair] ~ INLINE {RETURN[G2dVector.Sub[G2dVector.Mul[b, aDotB2], a]]}; Project: PROC [p: Triple] RETURNS [Pair] ~ INLINE {RETURN[[p.x, p.y]]}; Screen: PROC [p: Pair] RETURNS [Pair] ~ INLINE {RETURN[G2dVector.Add[G2dVector.Mul[[p.x, p.y], radius], center]]}; p010: Triple ¬ Bisect[p000, p100]; p001: Triple ¬ Bisect[p000, p010]; aDotB2: REAL ¬ Real.FScale[G3dVector.Dot[p000, p001], 1]; pp000: Pair ¬ Project[p000]; pp200: Pair ¬ Project[p200]; pp100: Pair ¬ Project[p100]; pp010: Pair ¬ Project[p010]; pp001: Pair ¬ Project[p001]; pp011: Pair ¬ Double[pp001, pp010]; pp101: Pair ¬ Double[pp011, pp100]; pp110: Pair ¬ Double[pp100, pp101]; pp111: Pair ¬ Double[pp101, pp110]; s000: Pair ¬ Screen[pp000]; s001: Pair ¬ Screen[pp001]; s010: Pair ¬ Screen[pp010]; s011: Pair ¬ Screen[pp011]; s100: Pair ¬ Screen[pp100]; s101: Pair ¬ Screen[pp101]; s110: Pair ¬ Screen[pp110]; s111: Pair ¬ Screen[pp111]; s200: Pair ¬ Screen[pp200]; DoArc: PROC ~ { zip: Draw2d.Zip ¬ Draw2d.GetZip[context]; Draw2d.Line[context, s000, s001, drawType, zip]; Draw2d.Line[context, s001, s010, drawType, zip]; Draw2d.Line[context, s010, s011, drawType, zip]; Draw2d.Line[context, s011, s100, drawType, zip]; Draw2d.Line[context, s100, s101, drawType, zip]; Draw2d.Line[context, s101, s110, drawType, zip]; Draw2d.Line[context, s110, s111, drawType, zip]; Draw2d.Line[context, s111, s200, drawType, zip]; Draw2d.ReleaseZip[zip]; IF navel THEN Draw2d.Square[context, [s000.x-1, s000.y-1], 2]; }; Imager.DoSave[context, DoArc]; }; <> ViewerOps.RegisterViewerClass[$ArcRot, MakeClass[$ArcRot]]; ViewerOps.RegisterViewerClass[$ArcTran, MakeClass[$ArcTran]]; END..