File: SVToolObjectImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: Defines the master object classes "tool" and "jack". A tool draws itself as from 1 to 3 orthogonal rectangles (even for ray tracing). Any assembly can be made into a tool temporarily, and can draw itself in wireframe normally but ray trace as a tool to allow easy pointing to important object planes. A jack draws itself as a coordinate frame. Jacks are invisible in ray-traces and shaded polygon drawings.
Created: July 2, 1984 1:03:38 pm PDT
Last edited by: Eric Bier on August 29, 1987 2:19:26 pm PDT
DIRECTORY
SVCoordSys, Feedback, GGParseIn, Imager, IO, Rope, SV2d, SV3d, SVAssembly, SVBasicTypes, SVBoundBox, SVCastRays, SVGraphics, SVMasterObject, SVModelTypes, SVPolygon3d, SVRay, SVScene, SVSceneTypes, SVToolObject;
SVToolObjectImpl:
CEDAR
PROGRAM
IMPORTS SVCoordSys, GGParseIn, IO, SVAssembly, SVBoundBox, SVCastRays, SVGraphics, SVMasterObject, SVPolygon3d, SVRay, SVScene
EXPORTS SVToolObject =
BEGIN
BoundHedron: TYPE = SVBasicTypes.BoundHedron;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVSceneTypes.Classification;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
CoordSysList: TYPE = SVModelTypes.CoordSysList;
FeedbackData: TYPE = Feedback.FeedbackData;
FrameBlock: TYPE = REF FrameBlockObj;
FrameBlockObj: TYPE = SVSceneTypes.FrameBlockObj;
LightSourceList: TYPE = SVModelTypes.LightSourceList;
MasterObject: TYPE = SVSceneTypes.MasterObject;
MasterObjectClass: TYPE = SVSceneTypes.MasterObjectClass;
MasterObjectClassObj: TYPE = SVSceneTypes.MasterObjectClassObj;
Matrix4by4: TYPE = SV3d.Matrix4by4;
PlanarSurface: TYPE = REF PlanarSurfaceObj;
PlanarSurfaceObj: TYPE = SVSceneTypes.PlanarSurfaceObj;
PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Poly3d: TYPE = SV3d.Poly3d;
Primitive: TYPE = SVSceneTypes.Primitive;
Ray: TYPE = SVSceneTypes.Ray;
Shape: TYPE = SVSceneTypes.Shape;
SelectMode: TYPE = SVSceneTypes.SelectMode;
Slice: TYPE = SVSceneTypes.Slice;
SliceList: TYPE = SVSceneTypes.SliceList;
SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor;
ToolData: TYPE = SVSceneTypes.ToolData;
Vector3d: TYPE = SV3d.Vector3d;
toolClass: MasterObjectClass;
jackClass: MasterObjectClass;
ToolMakeMasterObject:
PUBLIC
PROC [name: Rope.
ROPE, toolData: ToolData]
RETURNS [mo: MasterObject] = {
mainBody: REF ANY ← toolData;
lineBody: REF ANY ← toolData;
shadeBody: REF ANY ← toolData;
rayCastBody: REF ANY ← toolData;
mo ← SVScene.CreateMasterObject[name, toolClass, mainBody, lineBody, shadeBody, rayCastBody];
};
SizeToFit:
PUBLIC
PROC [toolData: ToolData, assembly: Slice] = {
The tool in question will just contain in the [loX, hiX, loY, hiY, loZ, hiZ] box, the assembly given. Compute this tight-fitting box from the union of the boundhedron's of all of assembly's master objects. Express all of the boundHedron points in assembly coordinates. Since the boundhedrons are in master object records, we must walk the tree to find them. Since they are in master object coordinates, we must carefully translate them to the coordinates of assembly.
block: FrameBlock;
loX, hiX, loY, hiY, loZ, hiZ: REAL;
dataFound: BOOL;
[loX, hiX, loY, hiY, loZ, hiZ, dataFound] ← BoundBlockOfAssembly[assembly, assembly];
IF dataFound
THEN
block ← NEW[FrameBlockObj ← [loX, hiX, loY, hiY, loZ, hiZ]]
ELSE block ← NEW[FrameBlockObj ← [-400, 400, -400, 400, -400, 400]];
toolData.block ← block;
toolData.infinite ← FALSE;
};
BoundBlockOfAssembly:
PROC [assembly: Slice, wrt: Slice]
RETURNS [loX, hiX, loY, hiY, loZ, hiZ:
REAL, dataFound:
BOOL] = {
For now, I will assume that any infinite objects are negative objects or are intersected with other objects. In either case, they can be ignored.
localDataFound: BOOL;
lX, hX, lY, hY, lZ, hZ: REAL;
dataFound ← FALSE;
WITH assembly.shape SELECT FROM
assems: SliceList => {
FOR subassemblies: LIST OF Slice ← assems.list, subassemblies.rest
UNTIL subassemblies =
NIL
DO
[lX, hX, lY, hY, lZ, hZ, localDataFound] ←
BoundBlockOfAssembly[subassemblies.first, wrt];
IF localDataFound
THEN {
IF (
NOT dataFound)
THEN {
loX ← lX; hiX ← hX; loY ← lY; hiY ← hY; loZ ← lZ; hiZ ← hZ;
}
ELSE {
loX ← MIN[loX, lX]; hiX ← MAX[hiX, hX];
loY ← MIN[loY, lY]; hiY ← MAX[hiY, hY];
loZ ← MIN[loZ, lZ]; hiZ ← MAX[hiZ, hZ];
};
};
dataFound ← dataFound OR localDataFound;
ENDLOOP;
};
shape: Shape => {
bh: BoundHedron ← shape.mo.class.getHedron[shape.mo];
dataFound ← TRUE;
IF bh = NIL THEN {dataFound ← FALSE; RETURN};
[loX, hiX, loY, hiY, loZ, hiZ] ← BoundBlockOfBoundHedron[bh, shape.coordSys, wrt.coordSys];
};
ENDCASE => ERROR;
};
BoundBlockOfBoundHedron:
PROC [bh: BoundHedron, currentCS: CoordSystem, newCS: CoordSystem]
RETURNS [loX, hiX, loY, hiY, loZ, hiZ:
REAL] = {
thisPt: Point3d;
thisPt ← SVCoordSys.FromCSToCS[bh[0], currentCS, newCS];
loX ← hiX ← thisPt[1];
loY ← hiY ← thisPt[2];
loZ ← hiZ ← thisPt[3];
FOR i:
NAT
IN [1..bh.len)
DO
thisPt ← SVCoordSys.FromCSToCS[bh[i], currentCS, newCS];
loX ← MIN[loX, thisPt[1]]; hiX ← MAX[hiX, thisPt[1]];
loY ← MIN[loY, thisPt[2]]; hiY ← MAX[hiY, thisPt[2]];
loZ ← MIN[loZ, thisPt[3]]; hiZ ← MAX[hiZ, thisPt[3]];
ENDLOOP;
};
ToolRayCast:
PROC [cameraPoint: Point2d, localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly:
BOOL ←
TRUE]
RETURNS [class: Classification] = {
mo: MasterObject ← prim.mo;
RETURN[ToolRayCastAux[localRay, NARROW[mo.rayCastBody], prim, positiveTOnly]];
};
ToolRayCastAux:
PROC [localRay: Ray, td: ToolData, prim: Primitive, positiveTOnly:
BOOL]
RETURNS [class: Classification] = {
hitPoint: Point3d;
almostZero: REAL ← 1.0e-12;
p: Point3d;
d: Vector3d;
t: REAL;
class ← SVCastRays.GetClassFromPool[];
[p, d] ← SVRay.GetLocalRay[localRay];
SELECT td.plane FROM
1 => {
-- x = 0 plane only
IF ABS[d[1]] < almostZero THEN SVCastRays.MakeClassAMiss[class]
ELSE {
t ← (- p[1])/d[1];
hitPoint[1] ← 0.0;
hitPoint[2] ← p[2] + t*d[2];
hitPoint[3] ← p[3] + t*d[3];
IF hitPoint[2] > td.block.hiY
OR hitPoint[2] < td.block.loY
OR hitPoint[3] > td.block.hiZ
OR hitPoint[3] < td.block.loZ
THEN
SVCastRays.MakeClassAMiss[class]
ELSE {
class.count ← 2;
The choice of surface normal depends on the position of the origin of the ray.
IF p[1] < 0.0
THEN {
class.normals[1] ← [-1,0,0];
class.normals[2] ← [1,0,0]}
ELSE {
class.normals[1] ← [1,0,0];
class.normals[2] ← [-1,0,0]};
class.params[1] ← t;
class.params[2] ← t;
class.surfaces[1] ← NIL;
class.surfaces[2] ← NIL;
}
};
};
2 => {
-- y = 0 plane only
IF ABS[d[2]] < almostZero THEN SVCastRays.MakeClassAMiss[class]
ELSE {
t ← (- p[2])/d[2];
hitPoint[1] ← p[1] + t*d[1];
hitPoint[2] ← 0.0;
hitPoint[3] ← p[3] + t*d[3];
IF hitPoint[1] > td.block.hiX
OR hitPoint[1] < td.block.loX
OR hitPoint[3] > td.block.hiZ
OR hitPoint[3] < td.block.loZ
THEN
SVCastRays.MakeClassAMiss[class]
ELSE {
class.count ← 2;
The choice of surface normal depends on the position of the origin of the ray.
IF p[2] < 0.0
THEN {
class.normals[1] ← [0,-1,0];
class.normals[2] ← [0,1,0]}
ELSE {
class.normals[1] ← [0,1,0];
class.normals[2] ← [0,-1,0]};
class.params[1] ← t;
class.params[2] ← t;
class.surfaces[1] ← NIL;
class.surfaces[2] ← NIL;
}
};
};
3 => {
-- z = 0 plane only
IF ABS[d[3]] < almostZero THEN SVCastRays.MakeClassAMiss[class]
ELSE {
t ← (- p[3])/d[3];
hitPoint[1] ← p[1] + t*d[1];
hitPoint[2] ← p[2] + t*d[2];
hitPoint[3] ← 0.0;
IF hitPoint[1] > td.block.hiX
OR hitPoint[1] < td.block.loX
OR hitPoint[2] > td.block.hiY
OR hitPoint[2] < td.block.loY
THEN
SVCastRays.MakeClassAMiss[class]
ELSE {
The choice of surface normal depends on the position of the origin of the ray.
IF p[3] < 0
THEN {
class.normals[1] ← [0,0,-1];
class.normals[2] ← [0,0,1]}
ELSE {
class.normals[1] ← [0,0,1];
class.normals[2] ← [0,0,-1]};
class.count ← 2;
class.params[1] ← t;
class.params[2] ← t;
class.surfaces[1] ← NIL;
class.surfaces[2] ← NIL;
}
};
};
4 => ERROR;
ENDCASE => ERROR;
};
ToolRayCastNoBBoxes:
PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly:
BOOL ←
TRUE]
RETURNS [class: Classification] = {
mo: MasterObject ← prim.mo;
RETURN ToolRayCastAux[localRay, NARROW[mo.rayCastBody], prim, positiveTOnly];
};
ToolRayCastBoundingSpheres:
PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly:
BOOL ←
TRUE]
RETURNS [class: Classification] = {
mo: MasterObject ← prim.mo;
RETURN ToolRayCastAux[localRay, NARROW[mo.rayCastBody], prim, positiveTOnly];
};
ToolBoundHedron:
PROC [mo: MasterObject]
RETURNS [hedron: BoundHedron] = {
toolData: ToolData ← NARROW[mo.mainBody];
block: FrameBlock ← toolData.block;
hedron ← SVBoundBox.RectangularBoundHedron2[block.loX, block.hiX, block.loY, block.hiY, block.loZ, block.hiZ];
};
ToolLineDraw:
PROC[slice: Slice, dc: Imager.Context, camera: Camera] = {
mo: MasterObject ← slice.toolMasterObject;
td: ToolData ← NARROW[mo.lineBody];
f: FrameBlock ← td.block;
localCS: CoordSystem ← slice.coordSys;
SELECT td.plane FROM
1 => DrawHitPadX[dc, f.hiZ, f.loZ, f.loY, f.hiY, camera, localCS];
2 => DrawHitPadY[dc, f.loX, f.hiX, f.loZ, f.hiZ, camera, localCS];
3 => DrawHitPadZ[dc, f.loX, f.hiX, f.loY, f.hiY, camera, localCS];
4 => {
DrawHitPadX[dc, f.hiZ, f.loZ, f.loY, f.hiY, camera, localCS];
DrawHitPadY[dc, f.loX, f.hiX, f.loZ, f.hiZ, camera, localCS];
DrawHitPadZ[dc, f.loX, f.hiX, f.loY, f.hiY, camera, localCS];
};
ENDCASE => ERROR;
};
DrawHitPadX:
PRIVATE
PROC [dc: Imager.Context, left, right, down, up:
REAL, camera: Camera, cs: CoordSystem] = {
localCamera: Matrix4by4 ← SVCoordSys.WRTCamera[cs, camera.coordSys];
SVGraphics.SetCP[dc, [0, up, left], camera, localCamera]; -- up left
SVGraphics.DrawTo[dc, [0, up, right], camera, localCamera]; --up right
SVGraphics.DrawTo[dc, [0, down, right], camera, localCamera]; -- down right
SVGraphics.DrawTo[dc, [0, down, left], camera, localCamera]; -- down left
SVGraphics.DrawTo[dc, [0, up, left], camera, localCamera]; -- up left
};
DrawHitPadY:
PRIVATE
PROC [dc: Imager.Context, left, right, down, up:
REAL, camera: Camera, cs: CoordSystem] = {
localCamera: Matrix4by4 ← SVCoordSys.WRTCamera[cs, camera.coordSys];
SVGraphics.SetCP[dc, [left, 0, up], camera, localCamera]; -- up left
SVGraphics.DrawTo[dc, [right, 0, up], camera, localCamera]; --up right
SVGraphics.DrawTo[dc, [right, 0, down], camera, localCamera]; -- down right
SVGraphics.DrawTo[dc, [left, 0, down], camera, localCamera]; -- down left
SVGraphics.DrawTo[dc, [left, 0, up], camera, localCamera]; -- up left
};
DrawHitPadZ:
PRIVATE
PROC [dc: Imager.Context, left, right, down, up:
REAL, camera: Camera, cs: CoordSystem] = {
localCamera: Matrix4by4 ← SVCoordSys.WRTCamera[cs, camera.coordSys];
SVGraphics.SetCP[dc, [left, up, 0], camera, localCamera]; -- up left
SVGraphics.DrawTo[dc, [right, up, 0], camera, localCamera]; --up right
SVGraphics.DrawTo[dc, [right, down, 0], camera, localCamera]; -- down right
SVGraphics.DrawTo[dc, [left, down, 0], camera, localCamera]; -- down left
SVGraphics.DrawTo[dc, [left, up, 0], camera, localCamera]; -- up left
};
ToolCountSurf:
PROC [masterObject: MasterObject]
RETURNS [len:
NAT] = {
toolData: ToolData ← NARROW[masterObject.mainBody];
IF toolData.plane < 4 THEN len ← 1
ELSE len ← 12;
};
SortedToolSurface: TYPE = REF SortedToolSurfaceObj;
SortedToolSurfaceObj:
TYPE =
RECORD [
plane: NAT,
f: FrameBlock,
left: BOOL,
down: BOOL
];
ToolGetSurf:
PROC [slice: Slice, camera: CoordSystem, eye
World: Point3d]
RETURNS [psl: PlanarSurfaceList] = {
The 3 tool rectangles are: (left, right, down, up)
hiZ loZ loY hiY
loX hiX loZ hiZ
loX hiX loY hiY
mo: MasterObject ← slice.toolMasterObject;
localCS: CoordSystem ← slice.coordSys;
thisSortedSurface: PlanarSurface;
poly: Poly3d ← SVPolygon3d.CreatePoly[4];
toolData: ToolData ← NARROW[mo.mainBody];
avgDepth: REAL;
f: FrameBlock ← toolData.block;
psl ← NIL;
SELECT toolData.plane FROM
1,2,3,4 => {
-- z = 0 plane
poly ← SVPolygon3d.AddPolyPoint[poly, [f.loX, f.loY, 0]];
poly ← SVPolygon3d.AddPolyPoint[poly, [f.loX, f.hiY, 0]];
poly ← SVPolygon3d.AddPolyPoint[poly, [f.hiX, f.hiY, 0]];
poly ← SVPolygon3d.AddPolyPoint[poly, [f.hiX, f.loY, 0]];
avgDepth ← AverageDepth[poly, localCS, camera];
thisSortedSurface ←
NEW[PlanarSurfaceObj ← [
whichSurface: NEW[SortedToolSurfaceObj ← [toolData.plane, f, FALSE, FALSE]],
assembly: slice,
normal: [0,0,1],
mo: mo,
depth: avgDepth,
frontDepth: avgDepth,
backDepth: avgDepth]];
psl ← CONS[thisSortedSurface, psl];
SVPolygon3d.ClearPoly[poly];
};
ENDCASE => ERROR;
};
AverageDepth:
PROC [poly: Poly3d, localCS, cameraCS: CoordSystem]
RETURNS [avgDepth:
REAL] = {
sum: REAL ← 0;
realLen: REAL ← poly.len;
localPoint: Point3d;
FOR i:
NAT
IN[0..poly.len)
DO
localPoint ← poly[i];
localPoint ← SVGraphics.LocalToCamera[localPoint, localCS, cameraCS];
sum ← sum + localPoint[3];
ENDLOOP;
avgDepth ← sum/realLen;
};
ToolDrawSurf:
PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera, eye
World: Point3d, hiddenLine:
BOOL] = {
whichSurface: SortedToolSurface ← NARROW[ps.whichSurface];
poly3d: Poly3d ← SVPolygon3d.CreatePoly[4];
f: FrameBlock ← whichSurface.f;
poly3d ← SVPolygon3d.AddPolyPoint[poly3d, [f.loX, f.loY, 0]];
poly3d ← SVPolygon3d.AddPolyPoint[poly3d, [f.loX, f.hiY, 0]];
poly3d ← SVPolygon3d.AddPolyPoint[poly3d, [f.hiX, f.hiY, 0]];
poly3d ← SVPolygon3d.AddPolyPoint[poly3d, [f.hiX, f.loY, 0]];
SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, SVCoordSys.WRTCamera[ps.assembly.coordSys, camera.coordSys], hiddenLine];
};
Jack Objects
JackMakeMasterObject:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [mo: MasterObject] = {
mainBody: REF ANY ← NIL;
lineBody: REF ANY ← NIL;
shadeBody: REF ANY ← NIL;
rayCastBody: REF ANY ← NIL;
mo ← SVScene.CreateMasterObject[name, jackClass, mainBody, lineBody, shadeBody, rayCastBody];
};
JackUpdate:
PUBLIC
PROC [mo: MasterObject, updateData:
REF
ANY] = {
};
JackBoundHedron:
PUBLIC
PROC [mo: MasterObject]
RETURNS [hedron: BoundHedron] = {
hedron ← SVBoundBox.RectangularBoundHedron[2.0, 2.0, 2.0];
};
JackLineDraw:
PUBLIC
PROC[slice: Slice, dc: Imager.Context, camera: Camera] = {
localCS: CoordSystem ← slice.coordSys;
localCamera: Matrix4by4 ← SVCoordSys.WRTCamera[localCS, camera.coordSys];
SVGraphics.SetCP[dc, [-1.0,0.0,0.0], camera, localCamera];
SVGraphics.DrawTo[dc, [1.0,0.0,0.0], camera, localCamera];
SVGraphics.SetCP[dc, [0.0,-1.0,0.0], camera, localCamera];
SVGraphics.DrawTo[dc, [0.0,1.0,0.0], camera, localCamera];
SVGraphics.SetCP[dc, [0.0,0.0,-1.0], camera, localCamera];
SVGraphics.DrawTo[dc, [0.0,0.0,1.0], camera, localCamera];
};
JackCountSurf:
PUBLIC
PROC [masterObject: MasterObject]
RETURNS [len:
NAT] = {
len ← 6;
};
JackGetSurf:
PUBLIC
PROC [slice: Slice, camera: CoordSystem, eye
World: Point3d]
RETURNS [psl: PlanarSurfaceList] = {
shape: Shape ← NARROW[slice.shape];
masterObject: MasterObject ← shape.mo;
psl ← NIL;
};
JackNewParts:
PUBLIC
PROC [slice: Slice, hitData:
REF
ANY, mode: SelectMode]
RETURNS [sliceD: SliceDescriptor] = {
sliceD ← SVAssembly.DescriptorFromParts[slice, NIL];
};
JackDrawSurf:
PUBLIC
PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera, eye
World: Point3d, hiddenLine:
BOOL] = {
};
JackFileout:
PUBLIC
PROC [f:
IO.
STREAM, mo: MasterObject] = {
Spheres can be recovered from scratch.
f.PutChar[IO.TAB];
f.PutF["data: procedural\n"];
};
JackFilein:
PUBLIC
PROC [f:
IO.
STREAM, name: Rope.
ROPE, csList: CoordSysList, defaultCS: CoordSystem, wdir: Rope.
ROPE, version:
REAL, feedback: FeedbackData]
RETURNS [mo: MasterObject] = {
GGParseIn.ReadWRope[f, "data: procedural"];
GGParseIn.ReadBlank[f];
mo ← JackMakeMasterObject[name];
};
Init:
PROC = {
jack: MasterObject;
toolClass ←
NEW[MasterObjectClassObj ← [
name: "tool",
update: SVMasterObject.NoOpUpdate,
filein: SVMasterObject.NoOpFilein,
fileout: SVMasterObject.NoOpFileout,
fileoutPoly: SVMasterObject.NoOpFileoutPoly,
rayCast: ToolRayCast,
rayCastNoBBoxes: ToolRayCastNoBBoxes,
rayCastBoundingSpheres: ToolRayCastBoundingSpheres,
getHedron: ToolBoundHedron,
preprocess: SVMasterObject.NoOpPreprocess,
lineDraw: ToolLineDraw,
normalsDraw: SVMasterObject.NoOpNormalsDraw,
countSurf: ToolCountSurf,
getSurf: ToolGetSurf,
pointsInDescriptor: SVMasterObject.NoOpPointsInDescriptor,
nextPoint: SVMasterObject.NoOpNextPoint,
drawSurf: ToolDrawSurf,
drawSubBoxes: SVMasterObject.NoOpDrawSubBoxes,
drawSubSpheres: SVMasterObject.NoOpDrawSubSpheres]];
jackClass ←
NEW[MasterObjectClassObj ← [
name: "jack",
update: JackUpdate,
filein: JackFilein,
fileout: JackFileout,
fileoutPoly: SVMasterObject.NoOpFileoutPoly,
rayCast: SVMasterObject.NoOpRayCast,
rayCastNoBBoxes: SVMasterObject.NoOpRayCastNoBBoxes,
rayCastBoundingSpheres: SVMasterObject.NoOpRayCastBoundingSpheres,
getHedron: JackBoundHedron,
preprocess: SVMasterObject.NoOpPreprocess,
lineDraw: JackLineDraw,
normalsDraw: SVMasterObject.NoOpNormalsDraw,
countSurf: JackCountSurf,
getSurf: JackGetSurf,
pointsInDescriptor: SVMasterObject.NoOpPointsInDescriptor,
nextPoint: SVMasterObject.NoOpNextPoint,
drawSurf: JackDrawSurf,
drawSubBoxes: SVMasterObject.NoOpDrawSubBoxes,
drawSubSpheres: SVMasterObject.NoOpDrawSubSpheres]];
SVMasterObject.RegisterMasterObjectClass[toolClass];
SVMasterObject.RegisterMasterObjectClass[jackClass];
jack ← JackMakeMasterObject["jack"];
SVMasterObject.RegisterMasterObject[jack];
};
Init[];
END.