DIRECTORY
AIS,
CastRays USING [EmptyClass, WriteStreamComp, ReturnClassToPool, UnionCombine, IntersectionCombine, DifferenceCombine, WriteStreamPrim],
CoordSys,
CSG,
DisplayListToTree,
Graphics,
IO,
Matrix3d,
SV2d,
SV3d,
SVModelTypes,
SVRayTypes,
SVSceneTypes;

CastRaysImplB: PROGRAM
IMPORTS CastRays, CoordSys, CSG, DisplayListToTree, Matrix3d
EXPORTS CastRays =

BEGIN

Assembly: TYPE = SVSceneTypes.Assembly;
BoundSphere: TYPE = SVModelTypes.BoundSphere;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE =  SVRayTypes.Classification;
Composite: TYPE = SVRayTypes.Composite;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Primitive: TYPE = SVRayTypes.Primitive;
Ray: TYPE = SVRayTypes.Ray;
Vector: TYPE = SV3d.Vector;

DoesHit: PROC [class: Classification] RETURNS [BOOL] = {
RETURN[class.count > 0 OR class.classifs[1] = TRUE];
};

RayCastBoundingSpheres: PUBLIC PROC [worldRay: Ray, node: REF ANY, consolidate: BOOL _ TRUE, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [class: Classification] = {
OPEN CastRays;
IF node = NIL THEN {class _ EmptyClass[]; RETURN};
WITH node SELECT FROM
comp: Composite => {
leftClass, rightClass: Classification;
leftBoxHit, leftHit, rightBoxHit, rightHit: BOOL;
totalMiss: BOOL _ FALSE;
boundSphere: BoundSphere;


WITH comp.leftSolid SELECT FROM
p: Primitive => boundSphere _ p.boundSphere;
c: Composite => boundSphere _ c.boundSphere;
ENDCASE => ERROR;
leftBoxHit _ CSG.RayHitsBoundSphere[worldRay, boundSphere];

IF leftBoxHit THEN {
leftClass _ RayCastBoundingSpheres[worldRay, comp.leftSolid, consolidate, makeStream, f, indent];
leftHit _ DoesHit[leftClass]; }
ELSE {leftHit _ FALSE;  leftClass _ EmptyClass[]};

IF NOT leftHit THEN IF comp.operation = intersection OR comp.operation = difference
THEN {
class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN};
WITH comp.rightSolid SELECT FROM
p: Primitive => boundSphere _ p.boundSphere;
c: Composite => boundSphere _ c.boundSphere;
ENDCASE => ERROR;
rightBoxHit _ CSG.RayHitsBoundSphere[worldRay, boundSphere];

IF NOT rightBoxHit THEN
SELECT comp.operation FROM
union => {class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN};
intersection =>
IF NOT leftHit THEN RETURN[leftClass]
ELSE {
ReturnClassToPool[leftClass]; class _ EmptyClass[]; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN};
difference => {class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN};
ENDCASE => ERROR;

rightClass _ RayCastBoundingSpheres[worldRay, comp.rightSolid, consolidate, makeStream, f, indent];
rightHit _ DoesHit[rightClass];

SELECT comp.operation FROM
union =>
IF rightHit THEN {
IF leftHit THEN class _ UnionCombine[leftClass, rightClass, consolidate]
ELSE {ReturnClassToPool[leftClass];  class _ rightClass}
}
 ELSE {
ReturnClassToPool[rightClass];  class _ leftClass};
intersection =>
IF rightHit THEN {
IF leftHit THEN class _ IntersectionCombine[leftClass, rightClass, consolidate]
ELSE {ReturnClassToPool[rightClass];  class _ leftClass;}
}
ELSE IF leftHit THEN {ReturnClassToPool[leftClass]; class _ rightClass}
   ELSE {ReturnClassToPool[rightClass]; class _ leftClass};
difference =>
IF rightHit THEN {
IF leftHit THEN class _ DifferenceCombine[leftClass, rightClass, consolidate]
ELSE {ReturnClassToPool[rightClass];  class _ leftClass} -- leftClass null
}
ELSE {ReturnClassToPool[rightClass];  class _ leftClass};
ENDCASE => ERROR;
WriteStreamComp[comp, class, makeStream, f, indent];
RETURN};
prim: Primitive => {
localRay: Ray;
localRay _ CSG.TransformRay[worldRay, prim.worldWRTPrim]; -- (takes a new ray from the pool)

class _ prim.rayCastBoundingSpheres[localRay, prim.mo, prim];
WriteStreamPrim[prim, class, makeStream, f, 0];
CSG.ReturnRayToPool[localRay]; -- returns ray to pool
RETURN};
ENDCASE => ERROR;
}; -- end of RayCastBoundingSpheres


ClassAssign: PROC [to: Classification, i: NAT, from: Classification, j: NAT] = {
to.params[i] _ from.params[j];
to.classifs[i] _ from.classifs[j];
to.normals[i] _ from.normals[j];
to.surfaces[i] _ from.surfaces[j];
to.primitives[i] _ from.primitives[j];
};

SortClassByPrimitive: PUBLIC PROC [class: Classification] = {
newClass: Classification;
used: ARRAY [1..SVRayTypes.maxSceneDepth] OF BOOL;
thisPrim: Primitive;

newClass _ CSG.CopyClass[class];
FOR i: NAT IN [1..SVRayTypes.maxSceneDepth] DO used[i] _ FALSE; ENDLOOP;
thisPrim _ newClass.primitives[1];
FOR i: NAT IN [1..class.count] DO
FOR j: NAT IN [1..class.count] DO
IF NOT used[j] AND newClass.primitives[j] = thisPrim THEN {
ClassAssign[class, i, newClass, j];
used[j] _ TRUE;
GOTO Found;
};
REPEAT
Found => NULL;
FINISHED =>
FOR k: NAT IN [1..class.count] DO
IF NOT used[k] THEN {
thisPrim _ newClass.primitives[k];
ClassAssign[class, i, newClass, k];
used[k] _ TRUE;
EXIT;
};
ENDLOOP;
ENDLOOP;
ENDLOOP;
};

NthSurfacePointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray, n: NAT] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector] = {
firstPrim: Primitive;
primitiveNormal: Vector;
t: REAL;
IF n = 0 OR n > class.count THEN ERROR;
t _ class.params[n]; -- the parameter of the ray intersection
primitiveNormal _ class.normals[1];
firstPrim _ class.primitives[1];
normalWorld _ Matrix3d.UpdateVectorWithInverse[firstPrim.worldWRTPrim, primitiveNormal];
surfacePtWorld _ CSG.EvaluateWorldRay[rayWorld, t];
};

LastOfFirstPointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector] = {
primitiveNormal: Vector;
t: REAL;
lastNum: NAT _ class.count;
lastOfFirstIndex: NAT _ 1;
firstPrim: Primitive _ class.primitives[1];
IF lastNum = 0 THEN RETURN;
FOR i: NAT DECREASING IN [2..lastNum] DO
IF class.primitives[i] = firstPrim THEN GOTO Found;
REPEAT
Found => lastOfFirstIndex _ i;
ENDLOOP;
t _ class.params[lastOfFirstIndex]; -- the parameter of the last ray intersection
primitiveNormal _ class.normals[lastOfFirstIndex];
normalWorld _ Matrix3d.UpdateVectorWithInverse[firstPrim.worldWRTPrim, primitiveNormal];
surfacePtWorld _ CSG.EvaluateWorldRay[rayWorld, t];
};

LastTopLevelPointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray, root: Assembly] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector] = {
primitiveNormal: Vector;
t: REAL;
lastNum: NAT _ class.count;
lastTopLevelIndex: NAT _ 1;
firstPrim: Primitive _ class.primitives[1];
lastPrim: Primitive;
firstAssembly, thisAssembly: Assembly;
firstTopLevel: Assembly;

firstAssembly _ NARROW[firstPrim.assembly];
firstTopLevel _ DisplayListToTree.AncestorAtLevel[firstAssembly, root, 1];
IF lastNum = 0 THEN RETURN;
FOR i: NAT DECREASING IN [2..lastNum] DO
thisAssembly _ NARROW[class.primitives[i].assembly];
IF DisplayListToTree.IsSuccessorOf[thisAssembly, firstTopLevel] THEN GOTO Found;
REPEAT
 Found => lastTopLevelIndex _ i;
ENDLOOP;
t _ class.params[lastTopLevelIndex]; -- the parameter of the last ray intersection
primitiveNormal _ class.normals[lastTopLevelIndex];
lastPrim _ class.primitives[lastTopLevelIndex];
normalWorld _ Matrix3d.UpdateVectorWithInverse[lastPrim.worldWRTPrim, primitiveNormal];
surfacePtWorld _ CSG.EvaluateWorldRay[rayWorld, t];
};

NthAssemblyAndPrimitive: PUBLIC PROC [class: Classification, n: NAT] RETURNS [assembly: Assembly, primitive: Primitive] = {
IF n = 0 OR n > class.count THEN RETURN[NIL, NIL];
primitive _ class.primitives[n];
assembly _ NARROW[primitive.assembly];
};

LastTopLevelAssemAndPrim: PUBLIC PROC [class: Classification, root: Assembly] RETURNS [assembly: Assembly, primitive: Primitive] = {
lastNum: NAT _ class.count;
lastTopLevelIndex: NAT _ 1;
firstPrim: Primitive _ class.primitives[1];
firstTopLevel, thisAssembly: Assembly;

IF lastNum = 0 THEN RETURN[NIL, NIL];
thisAssembly _ NARROW[firstPrim.assembly];
firstTopLevel _ DisplayListToTree.AncestorAtLevel[thisAssembly, root, 1];
FOR i: NAT DECREASING IN [2..lastNum] DO
thisAssembly _ NARROW[class.primitives[i].assembly];
IF DisplayListToTree.IsSuccessorOf[thisAssembly, firstTopLevel] THEN GOTO Found;
REPEAT
 Found => lastTopLevelIndex _ i;
ENDLOOP;
primitive _ class.primitives[lastTopLevelIndex];
assembly _ firstTopLevel;
};

RayMeetsZConstantPlane: PUBLIC PROC [localRay: Ray, z: REAL] RETURNS [hitPoint: Point3d, parallel: BOOL] = {
almostZero: REAL _ 1.0e-12;
t: REAL;
p: Point3d;
d: Vector;
[p, d] _ CSG.GetLocalRay[localRay];
IF ABS[d[3]] < almostZero THEN {
hitPoint _ [0,0,0];
parallel _ TRUE;
RETURN;
}
ELSE {
t _ (z - p[3])/d[3];
hitPoint[1] _ p[1] + t*d[1];
hitPoint[2] _ p[2] + t*d[2];
hitPoint[3] _ z;
parallel _ FALSE;
};
};


RayMeetsPlaneOfCoordSystem: PUBLIC PROC [coordSys: CoordSystem, rayWorld: Ray, whichPlane: NAT, depth: REAL _ 0] RETURNS [pointWorld: Point3d, parallel: BOOL] = {
almostZero: REAL _ 1.0e-12;
t: REAL;
localRay: Ray;
hitPoint: Point3d;
p: Point3d;
d: Vector;
localRay _ CSG.TransformRay[rayWorld, CoordSys.FindWorldInTermsOf[coordSys]];
[p, d] _ CSG.GetLocalRay[localRay];
SELECT whichPlane FROM
1 => { -- The x = depth plane
IF ABS[d[1]] < almostZero THEN {
pointWorld _ [depth,0,0];
parallel _ TRUE;
RETURN;
}
ELSE {
t _ (depth - p[1])/d[1];
hitPoint[1] _ depth;
hitPoint[2] _ p[2] + t*d[2];
hitPoint[3] _ p[3] + t*d[3];
parallel _ FALSE;
};
};
2 => {  -- The y = depth plane
IF ABS[d[2]] < almostZero THEN {
pointWorld _ [0,depth,0];
parallel _ TRUE;
RETURN;
}
ELSE {
t _ (depth - p[2])/d[2];
hitPoint[1] _ p[1] + t*d[1];
hitPoint[2] _ depth;
hitPoint[3] _ p[3] + t*d[3];
parallel _ FALSE;
};
};
3 => {  -- The z = depth plane
IF ABS[d[3]] < almostZero THEN {
pointWorld _ [0,0,depth];
parallel _ TRUE;
RETURN;
}
ELSE {
t _ (depth - p[3])/d[3];
hitPoint[1] _ p[1] + t*d[1];
hitPoint[2] _ p[2] + t*d[2];
hitPoint[3] _ depth;
parallel _ FALSE;
};
};
ENDCASE => ERROR;
CSG.ReturnRayToPool[localRay];
pointWorld _ Matrix3d.Update[coordSys.wrtWorld, hitPoint];
};

END.

��ª��File: CastRaysImplB.mesa
Last edited by Bier on May 23, 1985 3:23:09 pm PDT
Copyright c 1984 by Xerox Corporation.  All rights reserved.
Contents: The ray casting (as opposed to tree building) part of the CSG package.  CSG.mesa builds the trees
The main ray casting procedure.  worldRay must be in WORLD coordinates before this procedure is called.
Before casting each ray, see if the ray will be in the bounding sphere of the son node.
For optimizing, here is the plan:
1)  Check ray for left bound box.  Set leftBoxHit if appropriate.
2)  If leftBoxHit then cast the ray.  Set leftHit if appropriate.
3)  If not leftHit then if comp.operation = intersection or difference, return miss.
4)  If hit, or union, then right box test.  Set RightBoxMiss if appropriate.
5)  If miss then return: leftclass for difference, empty for intersection, leftClass for union.  
6)  Else cast ray.
7)  Return rightclass or combination if appropriate
1)  Check ray for left bound box.  Set leftBoxHit if appropriate.

2)  If leftBoxHit then cast the ray.  Set leftHit if appropriate.

3)  If not leftHit then if comp.operation = intersection or difference, return miss.

leftClass is (or is equivalent to) EmptyClass[];

4)  If hit, or union, then right sphere test.  Set RightBoxMiss if appropriate.  (we don't have to test for this state.  It is the only one left.)

5)  If miss then return EmptyClass.  Else cast ray.

This could be a union with or without a left miss or (intersection/difference) with an initial hit.
6)  Else cast ray.  We have Union, or (intersection/difference) with left hit.  Ray hits box.

7)  Return rightclass, combination or empty if appropriate

One optimation: each primitive will keep track of the last ray that hit it, and the vector corresponding to a unit x step in the CAMERA coord sys.  The ray will contain information about whether or not it is the first ray of a new line.
ClassSwap: PROC [class: Classification, i, j: NAT] = {
Swap the ith and jth columns of a classification record.
paramTemp: REAL;
classifTemp: BOOL;
normalTemp: Vector;
surfaceTemp: Surface;
primTemp: Primitive;

paramTemp _ class.params[i];
class.params[i] _ class.params[j];
class.params[j] _ paramTemp;

classifTemp _ class.classifs[i];
class.classifs[i] _ class.classifs[j];
class.classifs[j] _ class.classifs[i];

normalTemp _ class.classifs[i]
class.normals[i] _ class.normals[j];
class.normals[j] _ class.normals[i];

surfaceTemp _ class.surfaces[i];
class.surfaces[i] _ class.surfaces[j];
class.surfaces[j] _ class.surfaces[i];

primTemp _ class.primitives[i];
class.primitives[i] _ class.primitives[j];
class.primitives[j] _ class.primitives[i];
};

Reorders the classification so that all of the intersections corresponding to a particular primitive are grouped together (locally in ascending t value), and primitives are ordered by t value of the first hit on that primitive.  Copy from newClass to class as you go.
The SurfacePointAndNormal and AssemblyAndPrimitive procedures are distinct because Cedar can't handle a return record which both contains a pointer (assembly) and returns 6 REALS (long pointer-containing return record is unsafe)
Find the surface point and surface normal of the nth primitive hit by the ray.
Find the surface point and surface normal, belonging to the first primitive hit by the ray, when the ray last leaves that primitive.
Find the surface point and surface normal, belonging to the first top level assembly hit by the ray, when the ray last leaves that top level assembly.  Note that the assembly may well be composite (composed of multiple primitives).  "Top level" assemblies are direct children of "root".
SurfacePoint: PRIVATE PROC [t: REAL, camera: Camera, cameraPoint: Point2d] RETURNS [surfacePtInWorld: Point3d] = {
Given the parameter of intersection, camera, and the ray-screen intersection point (cameraPoint), find the surface point corresponding to t.  We assume that the ray went thru cameraPoint and that camera.projection has not changed since the ray was cast.
worldBasePt: Point3d;
worldDirection: Vector;
Express the ray which was cast in world coordinates.
worldBasePt _ Matrix3d.Update[camera.coordSys.mat, [cameraPoint[1], cameraPoint[2], 0]];
worldDirection depends on whether a perspective or orthogonal projection is in use.
SELECT camera.projection FROM
orthogonal => {
worldDirection _ Matrix3d.UpdateVectorEvenScaling[camera.coordSys.mat, [0, 0, -camera.focalLength]];
};
perspective => {
worldDirection _ Matrix3d.UpdateVectorEvenScaling[camera.coordSys.mat, [cameraPoint[1], cameraPoint[2], -camera.focalLength]];
};
ENDCASE => ERROR;
Use the ray: worldBasePt + t*worldDirection to find the surface point in WORLD coordinates.  
surfacePtInWorld[1] _ worldBasePt[1] + t*worldDirection[1];
surfacePtInWorld[2] _ worldBasePt[2] + t*worldDirection[2];
surfacePtInWorld[3] _ worldBasePt[3] + t*worldDirection[3];
};

Find the nth primitive hit by the ray and its corresponding displaylist assembly.
Find the first top level assembly hit by the ray and the primitive at which the ray last leaves that top level assembly.
assembly _ NARROW[primitive.assembly];
We assume the ray and the z constant plane are in the same coordinate frame.  As usual the ray equations are:
x = localRay.basePt[1] + t*localRay.direction[1];
y = localRay.basePt[2] + t*localRay.direction[2];
z = localRay.basePt[3] + t*localRay.direction[3];
If direction[3] is almost zero then report parallel.
Finds the intersection of the ray with one of the three orthogonal planes of coordSys.  If whichPlane = 1 this will be the x=0 (yz) plane, whichPlane = 2 then the y=0 plane, whichPlane = 3 then the z = 0 plane.  Assumes ray is initially in camera coordinates.  Returns the ray-plane intersection in camera coordinates, or parallel = TRUE if the ray is parallel to the plane.  As usual the ray equations are:
x = localRay.basePt[1] + t*localRay.direction[1];
y = localRay.basePt[2] + t*localRay.direction[2];
z = localRay.basePt[3] + t*localRay.direction[3];
If direction[3] is almost zero then report parallel.
�ÊŸ��˜�Iheadšœ™Iprocšœ2™2Jšœ
Ïmœ1™<Lšœk™kL˜�šÏk	˜	Lšžœ˜Lšœ	žœy˜‡L˜	Lšžœ˜L˜Lšœ	˜	Lšžœ˜Lšœ	˜	L˜L˜Lšœ
˜
Lšœ˜Lšœ
˜
—L˜�šœž˜Lšžœžœ˜<Lšžœ˜—L˜�Lšž˜˜�Lšœ
žœ˜'Lšœ
žœ˜-Lšœžœ˜#Lšœžœ˜2Lšœžœ˜'Lšœ
žœ˜-Lšœ	žœ˜Lšœ	žœ˜Lšœžœ˜'Lšœžœ˜Lšœžœ˜L˜�—šÏnœžœžœžœ˜8Lšžœžœžœ˜4L˜L˜�—šŸœžœžœžœžœžœžœžœžœžœžœžœ
žœžœ˜¿Lšœg™gLšžœ
˜Lšžœžœžœžœ˜2Lšžœžœž˜šœ˜Lšœ&˜&Lšœ,žœ˜1Lšœžœžœ˜Lšœ˜L˜�LšœW™WLšœ!™!LšœA™ALšœA™ALšœT™TLšœL™LLšœa™aLšœ™Lšœ3™3L˜�LšœA™AL™�šžœžœž˜Lšœ,˜,Lšœ,˜,Lšžœžœ˜—Lšœ
žœÏbœ˜;L˜�LšœA™AL™�šžœžœ˜Lšœ œ?˜aLšœ˜—Lšžœžœ˜2L˜�LšœT™TL™�Lš
žœžœ	žœžœžœ˜Sšžœ˜LšœHžœ˜PLšœ0™0L™�—Lšœ’™’L™�šžœžœž˜ Lšœ,˜,Lšœ,˜,Lšžœžœ˜—Lšœžœ œ˜<L˜�Lšœ3™3L™�Lšžœžœ
ž˜šœc™cšžœž˜LšœRžœ˜Zšœ˜Lšžœžœ	žœžœ˜%šžœ˜Lšœižœ˜q——LšœWžœ˜_Lšžœžœ˜L˜�——Lšœ]™]L™�Lšœ
 œ@˜cLšœ˜L˜�Lšœ:™:L™�šžœž˜šœ˜šžœ
žœ˜Lšžœ	žœ	 œ$˜HLšžœ4˜8Lšœ˜—šœžœ˜Lšœ3˜3——šœ˜šžœ
žœ˜Lšžœ	žœ	 œ$˜OLšžœ5˜9Lšœ˜—šžœžœ	žœ3˜GLšœžœ4˜;——šœ
˜
šžœ
žœ˜Lšžœ	žœ	 œ$˜MLšžœ5Ïc˜JLšœ˜—Lšžœ5˜9—Lšžœžœ˜—Lšœ4˜4Lšžœ˜—šœ˜Lšœ˜Lšœì™ìLšœžœ,¡"˜\L˜�Lšœ=˜=Lšœ/˜/Lšžœ¡˜5Lšžœ˜—Lšžœžœ˜Lšœ¡ ˜#L˜�L˜�—šŸœžœžœžœ˜PJšœ˜Jšœ"˜"Jšœ ˜ Jšœ"˜"Jšœ&˜&J˜J˜�—šœžœžœ™6J™8Lšœžœ™Lšœ
žœ™L™L™L™L™�Lšœ™Lšœ"™"Lšœ™L™�Lšœ ™ Lšœ&™&Lšœ&™&L™�Lšœ™Lšœ$™$Lšœ$™$L™�Lšœ ™ Lšœ&™&Lšœ&™&L™�Lšœ™Lšœ*™*Lšœ*™*L™J™�—šŸœžœžœ˜=Jšœ‹™‹Jšœ˜Jšœžœžœžœ˜2Jšœ˜J˜�Jšœžœ˜ Jšžœžœžœžœžœžœ˜HJšœ"˜"šžœžœžœž˜!šžœžœžœž˜!šžœžœ	žœ#žœ˜;Jšœ#˜#Jšœ
žœ˜Jšžœ˜J˜——šž˜Jšœ	žœ˜šžœ˜šžœžœžœž˜!šžœžœ	žœ˜Jšœ"˜"Jšœ#˜#Jšœ
žœ˜Jšžœ˜J˜——Jšžœ˜——Jšžœ˜—Jšžœ˜J˜—JšœŸœŸœ²™äJ˜�šŸœžœžœÏuœžœ¢œ¢œ
˜Jšœ1 œ 	œ™NJšœ˜Jšœ˜Jšœžœ˜Jšžœžœžœžœ˜'Jšœ œ¡)˜=Jšœ#˜#Jšœ ˜ Jš ÐbuœM˜XJš 	£œ¢œ˜3Jšœ˜—J˜�šŸœžœžœ¢œžœ¢œ¢œ
˜ˆJšœ< œ œ™„Jšœ˜Jšœžœ˜Jšœ	žœ˜Jšœžœ˜Jšœ+˜+Jšžœ
žœžœ˜š	žœžœž
œžœž˜(Jšžœ!žœžœ˜3—šž˜Jšœ˜—Jšžœ˜Jšœ œ¡-˜QJšœ  œ˜2Jš £œM˜XJš 	£œ¢œ˜3Jšœ˜J˜�—šÐbnœžœžœ¢œžœ¢œ¢œ
˜™Jšœ< œ œ¡™žJšœ˜Jšœžœ˜Jšœ	žœ˜Jš œžœ˜Jšœ+˜+Jšœ˜J˜&Jšœ˜J˜�Jšœžœ˜+JšœJ˜JJšžœ
žœžœ˜š	žœžœž
œžœž˜(Jšœžœ˜4Jšžœ>žœžœ˜P—šž˜Jšœ
 œ˜ —Jšžœ˜Jšœ%¡-˜RJšœ3˜3Jšœ/˜/Jš £œL˜WJš 	£œ¢œ˜3J˜J˜�—š
Ÿœžœžœžœ(žœ ™rJšœý™ýJšœ™šœ™Jšœ4™4—šœX™XJšœS™S—Jšžœž™™Jšœd™dJ™—™Jšœ~™~J™—šžœžœ™Jšœ]™]—Jšœ;™;Jšœ;™;Jšœ;™;Jšœ™J™�—š
Ÿœžœžœžœžœ/˜{Jšœ	 œ 	œ;™QJšžœžœžœžœžœžœ˜2Jšœ œ˜ Jšœžœ˜&Jšœ˜—J˜�š¤œžœžœ)žœ/˜„Jšœ	 œ3 œ™xJšœ	žœ˜Jšœžœ˜Jšœ+˜+Jšœ&˜&J˜�Jš
žœ
žœžœžœžœ˜%Jšœžœ˜*Jš 
œ<˜Iš	žœžœž
œžœž˜(Jšœžœ˜4Jšžœ 
œžœžœ˜P—šž˜Jšœ ˜ —Jšžœ˜Jšœ0˜0Jšœžœ™&Jšœ 
œ˜J˜J˜�—šŸœžœžœžœžœžœ˜lJ™mJšœ1™1Jšœ1™1Jšœ1™1Jšœ4™4Lšœžœ˜Lšœžœ˜L˜L˜
Lšœ	žœ˜#šžœžœžœ˜ Lšœ˜Lšœžœ˜Lšžœ˜L˜—šžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœžœ˜L˜—L˜Lšœ˜—šŸœžœžœ¢œžœ	žœžœ¢œžœ˜¢Lšœ—™—Lšœ1™1Jšœ1™1Jšœ1™1Jšœ4™4Lšœžœ˜Lšœžœ˜Lšœ˜L˜L˜L˜
Lšœžœ¢œ)˜MLšœ	žœ˜#Jšžœž˜šœ¡œ¡˜šžœžœžœ˜ Lšœ¢œ˜Lšœžœ˜Lšžœ˜L˜—šžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœžœ˜L˜—L˜—šœ¡œ¡˜šžœžœžœ˜ Lšœ¢œ˜Lšœžœ˜Lšžœ˜L˜—šžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœžœ˜L˜—L˜—šœ¡œ¡˜šžœžœžœ˜ Lšœ¢œ˜Lšœžœ˜Lšžœ˜L˜—šžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœžœ˜L˜—L˜—Lšžœžœ˜Lšžœ˜Lšœ¢œ0˜:L˜L˜�—Lšžœ˜L˜�—�…—����&Ž��M×��