File: SVViewerInputImplB.mesa
Last edited by Bier on May 23, 1985 5:48:16 pm PDT
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: Procedures for responding to button clicks made in a solidviewer.
DIRECTORY
AIS,
CastRays,
CoordSys,
CSG,
CSGGraphics,
DisplayList3d,
DisplayListToTree,
Graphics,
GraphicsColor,
InputFocus,
IO,
Matrix3d,
Preprocess3d,
Real,
SV2d,
SV3d,
SVArtworkUser,
SVError,
SVInterfaceTypes,
SVLines2d,
SVMatrix2d,
SVModelTypes,
SVRayTypes,
SVSceneTypes,
SVSelections,
SVVector3d,
SVViewerInput,
SVViewerUser,
TIPUser,
ViewerClasses;
SVViewerInputImplB:
PROGRAM
IMPORTS AIS, CastRays, CoordSys, CSG, CSGGraphics, DisplayList3d, DisplayListToTree, Graphics, GraphicsColor, InputFocus, --IO, --Matrix3d, Preprocess3d, Real, SVArtworkUser, SVError, SVLines2d, SVMatrix2d, SVSelections, SVViewerInput, SVViewerUser, SVVector3d
EXPORTS SVViewerInput =
BEGIN
Artwork: TYPE = SVModelTypes.Artwork;
ArtworkToolData: TYPE = SVInterfaceTypes.ArtworkToolData;
Assembly: TYPE = SVSceneTypes.Assembly;
AssemblyList: TYPE = SVSceneTypes.AssemblyList;
BoundBox: TYPE = SVModelTypes.BoundBox;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVRayTypes.Classification;
Color: TYPE = GraphicsColor.Color;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
EditToolData: TYPE = SVInterfaceTypes.EditToolData;
FileCamera: TYPE = SVSceneTypes.FileCamera;
FrameBox: TYPE = SVModelTypes.FrameBox;
MasterObject: TYPE = SVSceneTypes.MasterObject;
Matrix4by4: TYPE = SV3d.Matrix4by4;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Primitive: TYPE = SVRayTypes.Primitive;
Ray: TYPE = SVRayTypes.Ray;
Scene: TYPE = SVSceneTypes.Scene;
SearchDepth: TYPE = SVInterfaceTypes.SearchDepth;
Selection: TYPE = SVInterfaceTypes.Selection;
SelectionGenerator: TYPE = SVInterfaceTypes.SelectionGenerator;
Shape: TYPE = SVSceneTypes.Shape;
SkitterMode: TYPE = SVInterfaceTypes.SkitterMode;
ToolData: TYPE = SVSceneTypes.ToolData;
TrigLine: TYPE = SV2d.TrigLine;
Vector: TYPE = SV3d.Vector;
ViewerToolData: TYPE = SVInterfaceTypes.ViewerToolData;
SurfacePointAndNormalAtSearchDepth:
PROC [class: Classification, ray
World: Ray, searchDepth: SearchDepth, root: Assembly]
RETURNS [surfacePt
World: Point3d, normal
World: Vector] = {
SELECT searchDepth FROM
first =>
[surfacePtWorld, normalWorld] ← CastRays.NthSurfacePointAndNormal[class, rayWorld, 1];
lastOfFirst =>
[surfacePtWorld, normalWorld] ← CastRays.LastOfFirstPointAndNormal[class, rayWorld];
last =>
IF class.count = 0 THEN ERROR
ELSE [surfacePtWorld, normalWorld] ← CastRays.NthSurfacePointAndNormal[class, rayWorld, class.count];
lastOfLevel1 =>
[surfacePtWorld, normalWorld] ← CastRays.LastTopLevelPointAndNormal[class, rayWorld, root];
ENDCASE => ERROR;
};
AssemblyAndPrimitiveAtSearchDepth:
PROC [class: Classification, searchDepth: SearchDepth, root: Assembly]
RETURNS [assembly: Assembly, primitive: Primitive] = {
SELECT searchDepth FROM
first => [assembly, primitive] ← CastRays.NthAssemblyAndPrimitive[class, 1];
lastOfFirst => [assembly, primitive] ← CastRays.NthAssemblyAndPrimitive[class, 1];
last => [assembly, primitive] ← CastRays.NthAssemblyAndPrimitive[class, class.count];
lastOfLevel1 => [assembly, primitive] ← CastRays.LastTopLevelAssemAndPrim[class, root];
ENDCASE => ERROR;
};
StartTightRope:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
Use the distinguished target assembly's coordinate system as the platform for each tight rope step. We key off of the plane of the target's tool. If plane = 1, we stay along the x-axis, 2 the y-axis, 3 the z-axis. 4 currently works as the x-axis, but might someday compute the nearest axis.
planeSel: Selection;
planeAssem: Assembly;
scene: Scene ← viewerToolData.scene;
planeSel ← SVSelections.TopPlane[];
SVSelections.SetModeSkitter[tightrope];
IF planeSel =
NIL
THEN {
SVError.Append["Please make a plane selection to specify tightrope.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
planeAssem ← planeSel.coincident;
IF planeAssem.toolMasterObject =
NIL
OR planeAssem.toolMasterObject.mainBody =
NIL
THEN
DisplayList3d.AddOrResizeToolToAssembly[planeAssem, scene];
DuringTightRope[viewerToolData, cameraPoint];
};
TightRopeAux:
PRIVATE
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d, target: Assembly]
RETURNS [skitter
WORLD: Matrix4by4] = {
Compute the new position of the skitter given the mouse position in CAMERA coordinates. This is somewhat tricky. The effect I would like is this: We are constraining the skitter to move along an axis (a line) in 3-space. This line projects onto a line on the screen (using the current camera projection). If we drop a perpendicular from the mousept to this 2d line, that is where the skitter should appear in 2-space. Compute the corresponding point in 3-space and we are done.
skitterTarget: Matrix4by4;
newOriginCamera, newOriginTarget: Point3d;
plane: NAT;
toolData: ToolData;
cameraDir: Vector;
camera1, camera2: Point3d;
screen1, screen2, q: Point2d;
ropeShadow, normalLine: TrigLine;
parallel: BOOL;
camera: Camera ← viewerToolData.camera;
targetCS: CoordSystem ← target.coordSys;
worldCS: CoordSystem ← viewerToolData.scene.coordSysRoot;
cameraCS: CoordSystem ← camera.coordSys;
DoDrawIntersection: PROC [dc: Graphics.Context] = {
SVDraw3d.DrawX[dc, q, camera];
SVDraw3d.DrawX[dc, cameraPoint, camera];
};
camera1 ← Matrix3d.OriginOfMatrix[targetCS.wrtCamera];
toolData ← NARROW[target.toolMasterObject.mainBody];
plane ← toolData.plane;
plane ← IF plane > 3 THEN 3 ELSE plane;
SELECT plane
FROM
1 => cameraDir ← Matrix3d.XAxisOfMatrix[targetCS.wrtCamera];
2 => cameraDir ← Matrix3d.YAxisOfMatrix[targetCS.wrtCamera];
3 => cameraDir ← Matrix3d.ZAxisOfMatrix[targetCS.wrtCamera];
ENDCASE => ERROR;
camera2 ← SVVector3d.Add[camera1, cameraDir];
screen1 ← CSGGraphics.DoProjection[camera1, camera];
screen2 ← CSGGraphics.DoProjection[camera2, camera];
ropeShadow ← SVLines2d.TrigLineFromPoints[screen1, screen2];
normalLine ← SVLines2d.TrigLineNormalToTrigLineThruPoint[ropeShadow, cameraPoint];
[q, parallel] ← SVLines2d.TrigLineMeetsTrigLine[ropeShadow, normalLine];
IF parallel
THEN
ERROR;
For debugging...
SVViewerUser.Painter[DoDrawIntersection, viewerToolData];
Now for the hard part. If we represent the tightrope parametrically as:
R = p + t*d.
x = p1 + t*d1.
y = p2 + t*d2.
z = p3 + t*d3.
Then we wish to find the value of t so that CSGGraphics.DoProjection[R, camera] = skitterScreenPt (call it q); Let R = [x, y, z] then we wish, for perspective:
xf/(f-z) = q1. and yf/(f-z) = q2. Using the first equation:
(p1 + t*d1)f = (f - p3 - t*d3)*q1.
t(d1*f + d3*q1) = (f-p3)*q1 - (p1*f).
For orthogonal projection, we wish:
x = q1, y = q2.
(p1 + t*d1) = q1.
t*d1 = q1-p1.
t ← (q1-p1)/d1;
BEGIN
p: Point3d;
d: Vector;
t: REAL;
p ← camera1;
d ← cameraDir;
SELECT camera.projection FROM
orthogonal => {
IF ABS[d[1]] > ABS[d[2]] THEN t ← (q[1]-p[1])/d[1]
ELSE t ← (q[2]-p[2])/d[2];
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
perspective => {
denom1, denom2: REAL;
f: REAL ← camera.focalLength;
denom1 ← (d[1]*f + d[3]*q[1]);
denom2 ← (d[2]*f + d[3]*q[2]);
IF ABS[denom1] > ABS[denom2] THEN t ← ((f-p[3])*q[1] - (p[1]*f))/denom1
ELSE t ← ((f-p[3])*q[2] - (p[2]*f))/denom2;
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
ENDCASE => ERROR;
END;
newOriginTarget ← CoordSys.FromCSToCS[newOriginCamera, cameraCS, targetCS];
The new skitter coordinate frame is the old target frame translated by newOriginCamera. We then express the results in WORLD coordinates.
skitterTarget ← Matrix3d.MakeTranslateMat[newOriginTarget[1], newOriginTarget[2], newOriginTarget[3]];
skitterWORLD ← CoordSys.FromCSToCSMat[skitterTarget, targetCS, worldCS];
};
DuringTightRope:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
First find the cameraPoint in the coordinate system of the target. Use only the plane-th component of the resulting point in 3-space. Draw a line from the target origin to the new skitter origin.
planeAssem: Assembly;
planeSel: Selection;
skitterWORLD: Matrix4by4;
camera: Camera ← viewerToolData.camera;
planeSel ← SVSelections.TopPlane[];
IF planeSel = NIL THEN RETURN;
planeAssem ← planeSel.coincident;
skitterWORLD ← TightRopeAux[viewerToolData, cameraPoint, planeAssem];
SVViewerInput.ComplementSkitter[]; -- erase any old skitter
SVSelections.UpdateSkitter[NIL, NIL, viewerToolData];
SVSelections.PositionSkitter[cameraPoint, skitterWORLD];
SVViewerInput.ComplementSkitter[]; -- draw new skitter
};
EndTightRope:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
DuringTightRope[viewerToolData, cameraPoint];
};
StartWallWalk:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
planeSel: Selection;
planeAssem: Assembly;
scene: Scene ← viewerToolData.scene;
planeSel ← SVSelections.TopPlane[];
SVSelections.SetModeSkitter[tightrope];
IF planeSel =
NIL
THEN {
SVError.Append["Please make a plane selection to specify walk wall.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
planeAssem ← planeSel.coincident;
IF planeAssem.toolMasterObject =
NIL
OR planeAssem.toolMasterObject.mainBody =
NIL
THEN
DisplayList3d.AddOrResizeToolToAssembly[planeAssem, scene];
DuringWallWalk[viewerToolData, cameraPoint];
};
DuringWallWalk:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
Cast a ray at the target plane (like for dragging). Use the intersection point as the new skitter origin.
planeAssem: Assembly;
planeSel: Selection;
targetWORLD, skitterWORLD: Matrix4by4;
hitPointWORLD: Point3d;
camera: Camera ← viewerToolData.camera;
scene: Scene ← viewerToolData.scene;
worldCS: CoordSystem ← scene.coordSysRoot;
toolData: ToolData;
parallel: BOOL;
planeSel ← SVSelections.TopPlane[];
IF planeSel = NIL THEN RETURN;
planeAssem ← planeSel.coincident;
toolData ← NARROW[planeAssem.toolMasterObject.mainBody];
IF toolData =
NIL
THEN {
SVError.Append["Wall walk plane is not defined.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
Cast a ray at the wall walk plane.
[hitPointWORLD, parallel] ← RayMeetsTopPlane[cameraPoint, planeAssem, 0, toolData.plane, camera];
IF parallel
THEN
{
SVError.Append["DuringWallWalk: plane is parallel to ray.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
targetWORLD ← planeAssem.coordSys.wrtWorld;
skitterWORLD ← Matrix3d.Translate[targetWORLD, hitPointWORLD[1], hitPointWORLD[2], hitPointWORLD[3]];
SVViewerInput.ComplementSkitter[]; -- erase any old skitter
SVSelections.UpdateSkitter[NIL, NIL, viewerToolData];
SVSelections.PositionSkitter[cameraPoint, skitterWORLD];
SVViewerInput.ComplementSkitter[]; -- draw new skitter
};
EndWallWalk:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
DuringWallWalk[viewerToolData, cameraPoint];
};
Sign:
PRIVATE
PROC [r:
REAL]
RETURNS [
INT] = {
IF r = 0.0 THEN RETURN[2];
IF r < 0.0 THEN RETURN[-1]
ELSE RETURN[1];
};
AntiParallel:
PRIVATE
PROC [v1, v2: Vector]
RETURNS [
BOOL] = {
RETURN[Sign[v1[1]] = -Sign[v2[1]]
OR
Sign[v1[2]] = -Sign[v2[2]] OR
Sign[v1[3]] = -Sign[v2[3]] ];
};
MakeAlignedMat:
PRIVATE
PROC [worldNormal: Vector, surfacePtInWorld: Point3d, cs: CoordSystem]
RETURNS [mat: Matrix4by4] =
TRUSTED {
Create a Matrix4by4 with origin at surfacePtInWorld whose z axis is parallel to worldNormal and whose x zxis is orthogonal to both worldNormal and the y axis of cs in WORLD coordinates. Assume that cs.wrtWorld is accurate
yAxisOfCS: Vector ← Matrix3d.YAxisOfMatrix[cs.wrtWorld];
xAxis: Vector;
IF SVVector3d.Parallel[yAxisOfCS, worldNormal]
THEN {
xAxis ← Matrix3d.XAxisOfMatrix[cs.wrtWorld];
IF AntiParallel[yAxisOfCS, worldNormal] THEN xAxis ← SVVector3d.Negate[xAxis];
Allows positioning code to distinguish between top surfaces and bottom surfaces.
}
ELSE xAxis ← SVVector3d.CrossProduct[yAxisOfCS, worldNormal];
mat ← Matrix3d.MakeMatFromZandXAxis[worldNormal, xAxis, surfacePtInWorld];
};
RayMeetsTopPlane:
PRIVATE
PROC [cameraPoint: Point2d, target: Assembly, depth:
REAL, plane:
NAT, camera: Camera]
RETURNS [planePt
World: Point3d, parallel:
BOOL] = TRUSTED {
targetPlanePt is in CAMERA coords.
newRay: Ray;
newRay ← CSG.GetRayFromPool[];
CSG.StuffWorldRayFromCamera[newRay, cameraPoint, camera];
[planePtWorld, parallel] ← CastRays.RayMeetsPlaneOfCoordSystem[target.coordSys, newRay, plane, depth];
CSG.ReturnRayToPool[newRay];
};
StartSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d, searchDepth: SearchDepth] =
TRUSTED {
Compute the ray tracing tree for the current scene here so we don't have to recompute it each time the mouse position is sampled (SetTargetSelection).
selectionMat: Matrix4by4;
surfacePtWorld: Point3d;
normalWorld: Vector;
assembly: Assembly;
primitive: Primitive;
class: Classification;
rayWorld: Ray;
scene: Scene ← viewerToolData.scene;
camera: Camera ← viewerToolData.camera;
IF viewerToolData.treeOutOfDate
THEN {
viewerToolData.tree ← DisplayListToTree.AssemblyToTree[viewerToolData.scene.assembly, scene, viewerToolData.camera];
[] ← Preprocess3d.PreprocessForInteraction[viewerToolData.tree, viewerToolData.camera];
viewerToolData.treeOutOfDate ← FALSE;
};
[class, rayWorld] ← CastRays.SingleRay2[cameraPoint, viewerToolData.tree, viewerToolData.camera];
[assembly, primitive] ← AssemblyAndPrimitiveAtSearchDepth[class, searchDepth, scene.assembly];
IF class.count > 0
THEN
[surfacePtWorld, normalWorld] ← SurfacePointAndNormalAtSearchDepth[class, rayWorld, searchDepth, scene.assembly];
CSG.ReturnRayToPool[rayWorld];
CastRays.ReturnClassToPool[class];
SVViewerInput.ComplementSkitter[]; -- erase any old skitter
IF assembly #
NIL
THEN {
selectionMat ← MakeAlignedMat[normalWorld, surfacePtWorld, assembly.coordSys];
SVSelections.UpdateSkitter[assembly, primitive, viewerToolData];
SVSelections.PositionSkitter[cameraPoint, selectionMat];
};
SVSelections.SetModeSkitter[surface];
SVViewerInput.ComplementSkitter[]; -- draw new skitter
SVViewerUser.Selected[NIL, viewerToolData, red, FALSE, FALSE];
InputFocus.SetInputFocus[viewerToolData.viewerPicture];
}; -- end of StartSkitter
DuringSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d, searchDepth: SearchDepth] =
TRUSTED {
Cast a ray into the scene from this point. If it hits anything, use the surface normal at that point to derive a coordinate system whose z axis is the parallel to the normal.
surfacePtWorld: Point3d;
normalWorld: Vector;
assembly: Assembly;
primitive: Primitive;
selectionMat: Matrix4by4;
camera: Camera ← viewerToolData.camera;
scene: Scene ← viewerToolData.scene;
class: Classification;
rayWorld: Ray;
[class, rayWorld] ← CastRays.SingleRay2[cameraPoint, viewerToolData.tree, camera];
[assembly, primitive] ← AssemblyAndPrimitiveAtSearchDepth[class, searchDepth, scene.assembly];
IF class.count > 0
THEN
[surfacePtWorld, normalWorld] ←
SurfacePointAndNormalAtSearchDepth[class, rayWorld, searchDepth, scene.assembly];
CSG.ReturnRayToPool[rayWorld];
CastRays.ReturnClassToPool[class];
SVViewerInput.ComplementSkitter[]; -- erase any old skitter
IF assembly #
NIL
THEN {
editToolData: EditToolData ← viewerToolData.editToolData;
selectionMat ← MakeAlignedMat[normalWorld, surfacePtWorld, assembly.coordSys];
SVSelections.UpdateSkitter[assembly, primitive, viewerToolData];
SVSelections.PositionSkitter[cameraPoint, selectionMat];
}
ELSE SVSelections.UpdateSkitter[NIL, NIL, viewerToolData];
SVViewerInput.ComplementSkitter[]; -- draw new skitter
}; -- end DuringSkitter
EndSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d, searchDepth: SearchDepth] =
TRUSTED {
DuringSkitter[viewerToolData, cameraPoint, searchDepth]; -- set the skitter to the final point.
};
ExtendSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d, searchDepth: SearchDepth] =
TRUSTED {
Using the previous skitter assembly as one node and the current value as another, find the common root and set the skitter assembly to this value. If current is NIL, leave the skitter assembly as it is. Set skitter assembly to the new value.
oldAssembly, assem, commonAncestor: Assembly;
oldPrimitive, prim: Primitive;
oldViewerToolData, vtd: ViewerToolData;
scene: Scene;
scene ← viewerToolData.scene;
[oldAssembly, oldPrimitive, oldViewerToolData] ← SVSelections.GetSkitterData[];
DuringSkitter[viewerToolData, cameraPoint, searchDepth];
[assem, prim, vtd] ← SVSelections.GetSkitterData[];
IF vtd # oldViewerToolData THEN {
SVError.Append["Can't extend across viewers.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
IF assem = NIL THEN SVSelections.UpdateSkitter[oldAssembly, oldPrimitive, vtd]
ELSE {
commonAncestor ← DisplayListToTree.CommonAncestor[assem, oldAssembly, scene.assembly];
SVSelections.UpdateSkitter[commonAncestor, prim, vtd];
};
};
ExtendCoordSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
Using the previous skitter assembly as one node and the current value as another, find the common root and set the skitter assembly to this value. If current is NIL, leave the skitter assembly as it is. Set skitter assembly to the new value.
oldAssembly, assem, commonAncestor: Assembly;
oldPrimitive, prim: Primitive;
oldViewerToolData, vtd: ViewerToolData;
scene: Scene;
scene ← viewerToolData.scene;
[oldAssembly, oldPrimitive, oldViewerToolData] ← SVSelections.GetSkitterData[];
DuringCoordSkitter[viewerToolData, cameraPoint];
[assem, prim, vtd] ← SVSelections.GetSkitterData[];
IF vtd # oldViewerToolData
THEN {
SVError.Append["Can't extend across viewers.", TRUE, TRUE];
SVError.Blink[];
RETURN;
};
IF assem = NIL THEN SVSelections.UpdateSkitter[oldAssembly, oldPrimitive, vtd]
ELSE {
commonAncestor ← DisplayListToTree.CommonAncestor[assem, oldAssembly, scene.assembly];
SVSelections.UpdateSkitter[commonAncestor, prim, vtd];
};
};
NearestAssemblyToRay:
PRIVATE
PROC [cameraRay: Ray, root: Assembly]
RETURNS [nearest: Assembly, dSquared:
REAL] = {
sonD: REAL;
sonA: Assembly;
origin: Point3d;
WITH root.shape SELECT FROM
shape: Shape => {
-- A primitive. Just compute distance.
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[root.coordSys.wrtCamera];
dSquared ← RayToPoint[cameraRay, origin];
};
alist: AssemblyList => {
-- Take the minimum of myself and my subtrees, giving preference to the subtrees
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[root.coordSys.wrtCamera];
dSquared ← RayToPoint[cameraRay, origin];
FOR sons:
LIST
OF Assembly ← alist.list, sons.rest
UNTIL sons =
NIL
DO
[sonA, sonD] ← NearestAssemblyToRay[cameraRay, sons.first];
IF sonD <= dSquared
THEN {
nearest ← sonA;
dSquared ← sonD;
};
ENDLOOP;
};
ENDCASE => ERROR;
};
RayToPoint:
PUBLIC
PROC [cameraRay: Ray, cameraPt: Point3d]
RETURNS [dSquared:
REAL] =
TRUSTED {
Use dot products to find the distance from the ray to this point.
magRaySquared, dotProd, dotProdSquared, rCosThetaSquared, rSquared: REAL;
screenToPoint: Vector;
basePt: Point3d;
direction: Vector;
[basePt, direction] ← CSG.GetCameraRay[cameraRay];
magRaySquared ← SVVector3d.MagnitudeSquared[direction];
screenToPoint ← SVVector3d.Sub[cameraPt, basePt];
rSquared ← SVVector3d.MagnitudeSquared[screenToPoint];
dotProd ← SVVector3d.DotProduct[direction, screenToPoint];
dotProdSquared ← dotProd*dotProd;
rCosThetaSquared ← dotProdSquared/magRaySquared;
dSquared ← rSquared - rCosThetaSquared; -- Pythagorean Theorem
};
StartCoordSkitter:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = {
SVViewerUser.Selected[NIL, viewerToolData, red, FALSE, FALSE];
InputFocus.SetInputFocus[viewerToolData.viewerPicture];
DuringCoordSkitter[viewerToolData, cameraPoint];
};
DuringCoordSkitter:
PUBLIC PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = {
The user is trying to point at an existing coordinate frame. The skitter will highlight the frame which is nearest (in Euclidean distance) to the ray from eyepoint thru mouse point.
cameraRay: Ray ← CSG.GetRayFromPool[];
camera: Camera ← viewerToolData.camera;
scene: Scene ← viewerToolData.scene;
nearest: Assembly;
CSG.StuffCameraRay[cameraRay, cameraPoint, camera];
[nearest, ----] ← NearestAssemblyToRay[cameraRay, scene.assembly];
CSG.ReturnRayToPool[cameraRay];
SVViewerInput.ComplementSkitter[]; -- erase any old skitter
SVSelections.UpdateSkitter[nearest, NIL, viewerToolData];
SVSelections.PositionSkitter[cameraPoint, nearest.coordSys.wrtWorld];
SVSelections.SetModeSkitter[coordframe];
SVViewerInput.ComplementSkitter[]; -- draw new skitter
};
EndCoordSkitter: PUBLIC PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = {};
Paint:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = {
Move the skitter as usual. Compute the corresponding artwork point as the artwork "Position" operation. Find the corresponding ais pixels as in ray tracing. Color in a block of them with the current artworkToolData.paintColor.
editToolData: EditToolData ← viewerToolData.editToolData;
artworkToolData: ArtworkToolData ← NARROW[editToolData.artworkTool.data];
camera: Camera ← viewerToolData.camera;
artwork: Artwork ← artworkToolData.artwork;
assembly: Assembly;
skitterWORLD, assemWORLD, skitterassem, worldCamera: Matrix4by4;
imagePoint: Point2d;
skitterPoint, surfPtassem: Point3d;
normal: Vector;
paintColor: Color ← artworkToolData.paintColor;
path: Graphics.Path ← Graphics.NewPath[4];
DoDrawBrush:
PROC [dc: Graphics.Context] = {
Graphics.SetColor[dc, artworkToolData.paintColor];
CSGGraphics.DrawStroke[dc, path, 1, TRUE];
};
DuringSkitter[viewerToolData, cameraPoint, first];
[assembly, ----, viewerToolData] ← SVSelections.GetSkitterData[];
[----, skitterWORLD] ← SVSelections.GetPositionSkitter[];
assemWORLD ← assembly.coordSys.wrtWorld;
skitterassem ← Matrix3d.WorldToLocal[assemWORLD, skitterWORLD];
normal ← Matrix3d.ZAxisOfMatrix[skitterassem];
Our paint brush is the set of points [{-2,-1,0,1,2}, {-2,-1,0,1,2}] in the coordinate system of the skitter. We convert them to master object coordinates and then to image points.
FOR i:
INT
IN [-2..2]
DO
FOR j:
INT
IN [-2..2]
DO
skitterPoint ← [i, j, 0.0];
surfPtassem ← Matrix3d.Update[skitterassem, skitterPoint];
imagePoint ← SVArtworkUser.GetImagePointFromSurfacePoint[artwork, surfPtassem, normal];
SVError.AppendTypescript[IO.PutFR["%g, %g", [real[imagePoint[1]]], [real[imagePoint[2]]] ]];
SetImageColorAtPoint[artwork, imagePoint, paintColor];
ENDLOOP;
ENDLOOP;
worldCamera ← CoordSys.FindWorldInTermsOf[camera.coordSys];
CSGGraphics.MoveToAbsolute[path, Matrix3d.Update[worldCamera, Matrix3d.Update[skitterWORLD, [-2, -2, 0]]], camera];
CSGGraphics.LineToAbsolute[path, Matrix3d.Update[worldCamera, Matrix3d.Update[skitterWORLD, [-2, 2, 0]]], camera];
CSGGraphics.LineToAbsolute[path, Matrix3d.Update[worldCamera, Matrix3d.Update[skitterWORLD, [2, 2, 0]]], camera];
CSGGraphics.LineToAbsolute[path, Matrix3d.Update[worldCamera, Matrix3d.Update[skitterWORLD, [2, -2, 0]]], camera];
SVViewerUser.Painter[DoDrawBrush, viewerToolData];
};
SetImageColorAtPoint:
PROC [artwork: Artwork, imagePoint: Point2d, paintColor: Color] = {
Assume for now that artwork.file is an 8 bit-per-pixel ais file with rd scanning with origin in its lower left corner.
imagePoint is in screen dots.
Find the bounding box of the AIS file.
artworkPoint: Point2d;
IF artwork.source = NIL THEN RETURN;
BEGIN
uAIS, vAIS: REAL; -- u and v in AIS dots at this ais file's resolution
uInch, vInch: REAL; -- u and v in inches
artworkPoint ← SVMatrix2d.Update[artwork.artWRTPad.padWRTLocal, imagePoint]; -- in screen dots
uInch ← artworkPoint[1]/72.0; vInch ← artworkPoint[2]/72.0;
uAIS ← uInch*artwork.resolution; vAIS ← vInch*artwork.resolution;
IF artwork.isColorFile
THEN {
redValue, greenValue, blueValue: CARDINAL;
realRedValue, realGreenValue, realBlueValue: REAL;
[realRedValue, realGreenValue, realBlueValue] ← GraphicsColor.ColorToRGB[paintColor];
If [u,v] is outside the AIS image, do nothing.
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight THEN RETURN;
redValue ← Real.Fix[realRedValue*255.0];
AIS.WriteSample[artwork.redWindow, redValue, Real.FixC[-vAIS + artwork.halfHeightDots], Real.FixC[uAIS + artwork.halfWidthDots]];
greenValue ← Real.Fix[realGreenValue*255.0];
AIS.WriteSample[artwork.greenWindow, greenValue, Real.FixC[-vAIS + artwork.halfHeightDots], Real.FixC[uAIS + artwork.halfWidthDots]];
blueValue ← Real.Fix[realBlueValue*255.0];
AIS.WriteSample[artwork.blueWindow, blueValue, Real.FixC[-vAIS + artwork.halfHeightDots], Real.FixC[uAIS + artwork.halfWidthDots]];
}
ELSE {
-- artwork is a black and white file
blackValue: CARDINAL;
realBlackValue: REAL;
realBlackValue ← GraphicsColor.ColorToIntensity[paintColor];
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight
THEN RETURN;
blackValue ← Real.Fix[realBlackValue*255.0];
AIS.WriteSample[artwork.blackWindow, blackValue, Real.FixC[-vAIS + artwork.halfHeightDots], Real.FixC[uAIS + artwork.halfWidthDots]];
};
END;
}; -- end of SetImageColorAtPoint
FrameUpLeft:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
Controlpoint is in camera coordinates.
camera: Camera ← viewerToolData.camera;
oldMode: Graphics.PaintMode;
DoDrawFrame:
SAFE
PROC [dc: Graphics.Context] =
TRUSTED {
Graphics.SetColor[dc, GraphicsColor.black];
oldMode ← Graphics.SetPaintMode[dc, invert];
CSGGraphics.DrawFrame [dc, camera];
[] ← Graphics.SetPaintMode[dc, oldMode];
};
SVViewerUser.Painter[DoDrawFrame, viewerToolData]; -- erase old frame
camera.frame.fullScreen ← FALSE;
camera.frame.downLeft[1] ← cameraPoint[1];
camera.frame.upRight[2] ← cameraPoint[2];
camera.frame.downLeft[2] ← cameraPoint[2] - 50.0; -- Just to give the frame an initial size.
camera.frame.upRight[1] ← cameraPoint[1] + 50.0;
SVViewerUser.Painter[DoDrawFrame, viewerToolData];
};
FrameDownRightMove:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
Controlpoint is in camera coordinates.
camera: Camera ← viewerToolData.camera;
oldMode: Graphics.PaintMode;
DoDrawFrame:
SAFE
PROC [dc: Graphics.Context] =
TRUSTED {
Graphics.SetColor[dc, GraphicsColor.black];
oldMode ← Graphics.SetPaintMode[dc, invert];
CSGGraphics.DrawFrame [dc, camera];
[] ← Graphics.SetPaintMode[dc, oldMode];
};
Erase the old frame
SVViewerUser.Painter[DoDrawFrame, viewerToolData];
camera.frame.downLeft[2] ← cameraPoint[2];
camera.frame.upRight[1] ← cameraPoint[1];
Draw the new frame.
SVViewerUser.Painter[DoDrawFrame, viewerToolData];
};
FrameDownRightEnd:
PUBLIC
PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] =
TRUSTED {
camera: Camera ← viewerToolData.camera;
fileCamera: FileCamera;
success: BOOL;
FrameDownRightMove[viewerToolData, cameraPoint];
Once last update with the final position.
Tell the appropriate file camera about the new frame.
[fileCamera, success] ← DisplayList3d.FindFileCameraFromName[camera.viewName, viewerToolData.scene];
IF success
THEN {
fileCamera.frame.downLeft ← camera.frame.downLeft;
fileCamera.frame.upRight ← camera.frame.upRight;
fileCamera.frame.fullScreen ← FALSE;
};
};
DeleteFrame:
PUBLIC PROC [viewerToolData: ViewerToolData] =
TRUSTED {
camera: Camera ← viewerToolData.camera;
frame: FrameBox ← camera.frame;
oldMode: Graphics.PaintMode;
DoDrawFrame:
SAFE
PROC [dc: Graphics.Context] =
TRUSTED {
Graphics.SetColor[dc, GraphicsColor.black];
oldMode ← Graphics.SetPaintMode[dc, invert];
CSGGraphics.DrawFrame [dc, camera];
[] ← Graphics.SetPaintMode[dc, oldMode];
};
Erase the old frame.
SVViewerUser.Painter[DoDrawFrame, viewerToolData];
frame.fullScreen ← TRUE;
};
END.