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, rayWorld: Ray, searchDepth: SearchDepth, root: Assembly] RETURNS [surfacePtWorld: Point3d, normalWorld: 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 [skitterWORLD: 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;
skitterWORLDTightRopeAux[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 [planePtWorld: 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.