DIRECTORY
Atom					USING [ GetPropFromList, PropList, PutPropOnList, RemPropFromList ],
List					USING [ Nconc1 ],
Rope					USING [ ROPE, Equal ],
Real					USING [ Fix, Float, LargestNumber ],
RealFns				USING [ ArcTanDeg, Sin, Cos, Power, SqRt ],
Checksum			USING [ ComputeChecksum ],
IO						USING [ GetAtom, GetInt, GetReal, STREAM ],
ImagerPixel			USING [ GetPixels, ObtainScratchPixels, PixelBuffer, PixelMap,
								 ReleaseScratchPixels ],
ImagerSample		USING [ SampleBuffer ],
Vector2				USING [ Dot, Length, Mul, Unit ],
G3dVector			USING [ Cross, Normalize ],
ScanConvert			USING [ justNoticeable ],
ThreeDBasics		USING [ Box, Context, Error, IntSequence, LoadShadingClass,					 
								 Pair, PairSequence, Patch, PtrPatchSequence, 
								 RealSequence, Rectangle, RegisterShadingClass, RGB,      
								 ShadingClass, ShapeClass, ShapeInstance, ShapeProc, Spot,     
								 SpotProc, SummedTexture, SumSequence, TextureFunction,   
								 TextureMap, Triple, TripleSequence, VertexInfo, VertexInfoProc, 
								 VertexInfoSequence, VtxToRealSeqProc ],
RenderWithPixels	USING [ AntiAliasing, GetContext, AllocatePixelMemory, ShadeSpot ],
SurfaceRender		USING [ ValidateContext ],
ShapeUtilities		USING [ ShadeVtx ], 
SceneUtilities		USING [ FindShape, GetRope ], 
AISAnimation		USING [ GetAIS ],
MappedAndSolidTexture	USING [ ];

MappedAndSolidTextureImpl: CEDAR PROGRAM
IMPORTS AISAnimation, Atom, Checksum, G3dVector, ImagerPixel, IO, List, Real, RealFns, RenderWithPixels, Rope, SceneUtilities, ShapeUtilities, SurfaceRender, ThreeDBasics, Vector2 
EXPORTS MappedAndSolidTexture 

= BEGIN 

Context: TYPE ~ ThreeDBasics.Context;
RGB: TYPE ~ ThreeDBasics.RGB;
Box: TYPE ~ ThreeDBasics.Box;
Rectangle: TYPE ~ ThreeDBasics.Rectangle;
Pair: TYPE ~ ThreeDBasics.Pair;											-- [ x, y: REAL];
PairSequence: TYPE ~ ThreeDBasics.PairSequence; 
Triple: TYPE ~ ThreeDBasics.Triple;										-- [ x, y, z: REAL]
TripleSequence: TYPE ~ ThreeDBasics.TripleSequence; 
VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; 
VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence; 
IntSequence: TYPE ~ ThreeDBasics.IntSequence; 
RealSequence: TYPE ~ ThreeDBasics.RealSequence;
TextureFunction: TYPE ~ ThreeDBasics.TextureFunction;
TextureMap: TYPE ~ ThreeDBasics.TextureMap;
SumSequence: TYPE ~ ThreeDBasics.SumSequence;
SummedTexture: TYPE ~ ThreeDBasics.SummedTexture;
Patch: TYPE ~ ThreeDBasics.Patch;
Spot: TYPE ~ ThreeDBasics.Spot;
SpotProc: TYPE ~ ThreeDBasics.SpotProc;
ShadingClass: TYPE ~ ThreeDBasics.ShadingClass;
ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance;
ShapeProc: TYPE ~ ThreeDBasics.ShapeProc;

LORA: TYPE = LIST OF REF ANY;

Swap: PROCEDURE [p: Pair] RETURNS [Pair] ~ INLINE { RETURN[ [p.y, p.x] ]; };

Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Sgn: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { 
IF number < 0. THEN RETURN[-1.] ELSE RETURN[1.];   
};
GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~
																				 Atom.GetPropFromList;
PutProp: PROC [propList: Atom.PropList, prop: REF ANY, val: REF ANY] 
		   RETURNS [Atom.PropList] ~ Atom.PutPropOnList;
justNoticeable: REAL ~ ScanConvert.justNoticeable;									-- 0.02
registeredTextureFunctions: Atom.PropList _ NIL;	-- keeps active solid texture functions
defaultAuxiliaryData: REF Pair _ NEW[Pair _ [0.0, 0.0]];
maxTxtrRange: REAL _ 32.0;				-- texture coordinate range, small numbers expected
Init: PROC[] ~ {
txtrShadingClass: ShadingClass _ [
type: $MappedAndSolidTexture,
cnvrtVtx: GetLerpedVals,
getColor: RecoverColor,
loadShapeAux: LoadShapeAux,
loadVtxAux: LoadVtxAux,
lerpVtxAux: LerpVtxAux,
shadeVtx: ShapeUtilities.ShadeVtx
];
ThreeDBasics.RegisterShadingClass[txtrShadingClass, $MappedAndSolidTexture];
RegisterTextureFunction[ $Spots, Spots ];
RegisterTextureFunction[ $Wurlitzer, Wurlitzer ];
RegisterTextureFunction[ $TwistedStripes, TwistedStripes ];
RegisterTextureFunction[ $BurlWood, BurlWood ];
RegisterTextureFunction[ $ZebraBurl, ZebraBurl ];
RegisterTextureFunction[ $Marble, Marble ];
};
CheckAndAddProcs: PUBLIC PROC[shape: REF ShapeInstance ] ~ {
IF shape.shadingClass = NIL 
THEN ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture]
ELSE IF shape.shadingClass.type # $MappedAndSolidTexture 
THEN {
shape.shadingClass.type _ $MappedAndSolidTexture; 
shape.shadingClass.cnvrtVtx _ GetLerpedVals; 
shape.shadingClass.getColor _ RecoverColor;
shape.shadingClass.loadShapeAux _ LoadShapeAux;
shape.shadingClass.loadVtxAux _ LoadVtxAux;
shape.shadingClass.lerpVtxAux _ LerpVtxAux;
shape.shadingClass.shadeVtx _ ShapeUtilities.ShadeVtx;
};
};

AddSolidTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, name: ATOM ] ~{
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
txtrFn: TextureFunction _ GetRegisteredTextureFunction[name];
txtrFn.props _ PutProp[txtrFn.props, $Shape, shape];
CheckAndAddProcs[shape];
shape.shadingClass.texture _ List.Nconc1[ 	-- append to existing list
shape.shadingClass.texture, NEW[TextureFunction _ txtrFn] 
];
};

AddMappedTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, 
										  texture: REF TextureMap] ~ {
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
CheckAndAddProcs[shape];
shape.shadingClass.texture _ List.Nconc1[ shape.shadingClass.texture, texture ];	-- append
};

SumAllMappedTextures: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ {
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
texture: LIST OF REF ANY _ shape.shadingClass.texture; 
FOR txtrList: LIST OF REF ANY _ texture, txtrList.rest UNTIL txtrList = NIL  DO
WITH txtrList.first SELECT FROM
textureMap: REF TextureMap => SumMappedTexture[textureMap];
ENDCASE;
ENDLOOP;
};
 
RemoveAllTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ {
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
shape.shadingClass.texture _ NIL;
};
GetLerpedVals: ThreeDBasics.VtxToRealSeqProc ~ {
maxLength: NAT _ IF data = $PixelShading THEN 16 ELSE 10;
sourceAux: REF Pair _ NARROW[ source.aux ];
IF sourceAux = NIL THEN sourceAux _ defaultAuxiliaryData;
IF dest = NIL OR dest.maxLength < maxLength THEN dest _ NEW[RealSequence[maxLength]];
IF data = $PixelShading 
THEN {								-- shade will be computed anew at each pixel
dest[0]	_ source.shade.r;
dest[1]	_ source.shade.g;
dest[2]	_ source.shade.b;
dest[3]	_ source.shade.t;
dest[4]	_ source.shade.exn; 
dest[5] 	_ source.shade.eyn;
dest[6]	_ source.shade.ezn;
dest[7]	_ source.coord.ex;
dest[8]	_ source.coord.ey;
dest[9]	_ source.coord.ez;
dest[10]	_ source.coord.sz;								-- for depth buffering
dest[11]	_ sourceAux.x;
dest[12]	_ sourceAux.y;
dest[13]	_ source.coord.x;
dest[14]	_ source.coord.y;
dest[15]	_ source.coord.z;
}
ELSE { 					-- Shade will only be multiplied or interpolated
dest[0]	_ source.shade.er;
dest[1]	_ source.shade.eg;
dest[2]	_ source.shade.eb;
dest[3]	_ source.shade.et;
dest[4]	_ source.coord.sz;								-- for depth buffering
dest[5]	_ sourceAux.x;
dest[6]	_ sourceAux.y;
dest[7]	_ source.coord.x;
dest[8]	_ source.coord.y;
dest[9]	_ source.coord.z;
};
dest.length _ maxLength; 
RETURN [dest];
};

RecoverColor: SpotProc ~ {
IF shading.texture # NIL THEN GetTxtrAt[context, shading, spot];
IF shading.texture # NIL OR spot.val.length > 15 
THEN RenderWithPixels.ShadeSpot[context, shading, spot];
};
LoadShapeAux: PUBLIC ShapeProc ~ {		-- load aux field in vtx
xMin, yMin, xRng, yRng: REAL _ 0.5;
auxInfo: REF PairSequence _ NEW[ PairSequence[shape.shade.length] ];
WITH data SELECT FROM
vtces: REF VertexInfoSequence => {
FOR i: NAT IN [0..shape.shade.length) DO  
auxInfo[i] _ NARROW[vtces[i].aux, REF Pair]^;
ENDLOOP;
auxInfo.length _ shape.shade.length;
};
pairs: REF PairSequence => auxInfo _ pairs;
ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unrecognized type"]];
FOR i: NAT IN [0..shape.shade.length) DO  
IF i = 0 
THEN {  xRng _ xMin _ auxInfo[i].x;    yRng _ yMin _ auxInfo[i].y;  }
ELSE {
IF xRng < auxInfo[i].x THEN xRng _ auxInfo[i].x;
IF yRng < auxInfo[i].y THEN yRng _ auxInfo[i].y;
IF xMin > auxInfo[i].x THEN xMin _ auxInfo[i].x;
IF yMin > auxInfo[i].y THEN yMin _ auxInfo[i].y;
};
ENDLOOP;
xRng _ xRng - xMin;    yRng _ yRng - yMin;
shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordRange, 
								    NEW[ Pair _ [xRng, yRng ] ] ];
IF xRng > maxTxtrRange OR yRng > maxTxtrRange 
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Suspiciously big texture coords"]];
shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo];
RETURN[ shape ];
};

LoadVtxAux: PUBLIC ThreeDBasics.VertexInfoProc ~ {		-- load aux field in vtx
vtxAux: REF Pair _ NEW[Pair];
WITH data SELECT FROM
input: LORA  => {
auxInfo: REF PairSequence _ NARROW[ input.first ];
index: INTEGER _ NARROW[ input.rest.first, REF INTEGER ]^;
vtxAux^ _ auxInfo[index];					   -- texture coords
};
txtr: REF Pair  => {  vtxAux.x _ txtr.x;    vtxAux.y _ txtr.y;  };
ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unrecognized type"]];
vtx.aux _ vtxAux;
RETURN[ vtx ];
};

LerpVtxAux: PUBLIC ThreeDBasics.VertexInfoProc ~ { -- linear interpolation for texture coords
input: LORA _ NARROW[data];
vtxAux: REF Pair _ NEW[Pair];
vtxa: REF Pair _ NARROW[ input.first ];
vtxb: REF Pair _ NARROW[ input.rest.first ];
a: REAL _  NARROW[input.rest.rest.first, REF REAL]^;
b: REAL _  NARROW[input.rest.rest.rest.first, REF REAL]^;
vtxAux.x _ vtxa.x*a + vtxb.x*b;
vtxAux.y _ vtxa.y*a + vtxb.y*b;
vtx.aux _ vtxAux;
RETURN[ vtx ];
};

AdjustTexture: PUBLIC PROC[poly: REF Patch, texture: LORA,
								   halfXRange, halfYRange: REAL _ .5] ~ {
mappedTexture: BOOLEAN _ FALSE;
FOR txtrList: LORA _ NARROW[texture], txtrList.rest UNTIL txtrList = NIL  DO
WITH txtrList.first SELECT FROM									-- look for mapped texture
texture: REF ThreeDBasics.TextureMap => mappedTexture _ TRUE;
ENDCASE; 
ENDLOOP;
IF mappedTexture THEN {
maxXtxtr, maxYtxtr, minXtxtr, minYtxtr: REAL;
FOR i: CARDINAL IN [0..poly.nVtces) DO		-- find maxima and minima
txtr: REF Pair _ NARROW[poly[i].aux];
IF i = 0 
THEN {  maxXtxtr _ minXtxtr _ txtr.x;    maxYtxtr _ minYtxtr _ txtr.y;  }
ELSE {
IF maxXtxtr < txtr.x THEN maxXtxtr _ txtr.x;
IF maxYtxtr < txtr.y THEN maxYtxtr _ txtr.y;
IF minXtxtr > txtr.x THEN minXtxtr _ txtr.x;
IF minYtxtr > txtr.y THEN minYtxtr _ txtr.y;
};
ENDLOOP; 
IF maxXtxtr - minXtxtr > halfXRange THEN {			-- seam in x
FOR i: CARDINAL IN [0..poly.nVtces) DO	-- push small ones beyond maximum
txtr: REF Pair _ NARROW[poly[i].aux];
WHILE maxXtxtr - txtr.x > halfXRange DO  txtr.x _ txtr.x + 1.0;  ENDLOOP;
ENDLOOP; 
minXtxtr _ maxXtxtr;
FOR i: CARDINAL IN [0..poly.nVtces) DO		-- find minimum
txtr: REF Pair _ NARROW[poly[i].aux];
IF minXtxtr > txtr.x THEN minXtxtr _ txtr.x;
ENDLOOP; 
};
IF maxYtxtr - minYtxtr > halfYRange THEN {			-- seam in y
FOR i: CARDINAL IN [0..poly.nVtces) DO	-- push small ones beyond maximum
txtr: REF Pair _ NARROW[poly[i].aux];
WHILE maxYtxtr - txtr.y > halfYRange DO  txtr.y _ txtr.y + 1.0;  ENDLOOP;
ENDLOOP; 
minYtxtr _ maxYtxtr;
FOR i: CARDINAL IN [0..poly.nVtces) DO		-- find minimum
txtr: REF Pair _ NARROW[poly[i].aux];
IF minYtxtr > txtr.y THEN minYtxtr _ txtr.y;
ENDLOOP;
}; 
minXtxtr _ Real.Float[Real.Fix[minXtxtr]];    minYtxtr _ Real.Float[Real.Fix[minYtxtr]];
FOR i: CARDINAL IN [0..poly.nVtces) DO		-- adjust everything to minima under 1.0
txtr: REF Pair _ NARROW[poly[i].aux];
txtr.x _ txtr.x - minXtxtr;
txtr.y _ txtr.y - minYtxtr;
ENDLOOP; 
};
};
GetTxtrAt: PUBLIC SpotProc ~ {
texture: LORA _ shading.texture; 
FOR txtrList: LORA _ texture, txtrList.rest UNTIL txtrList = NIL  DO
WITH txtrList.first SELECT FROM		-- textures evaluated top to bottom
texture: REF TextureMap =>			-- modify with mapped texture
WITH texture.pixels SELECT FROM
buf: ImagerPixel.PixelMap	=> IF texture.type = $Bump
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Need summed area table"]]
ELSE SimpleTexture[spot, buf, texture.type];
sumMap: REF SummedTexture	=>  IF texture.type = $Bump 
THEN {
ref: REF ANY _ Atom.GetPropFromList[texture.props, $BumpScale];
bumpScale: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 1.0;
BumpTexture[spot, sumMap, bumpScale];
}
ELSE SumMapTexture[spot, sumMap, texture.type];
ENDCASE	=> ERROR;
txtrFn: REF TextureFunction =>  		 -- solid or other function-based texture
txtrFn.proc[context, shading, spot, txtrFn.props];
ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unknown texture type"]]; 
ENDLOOP;
};

SimpleTexture: PROC[spot: REF Spot, map: ImagerPixel.PixelMap, type: ATOM] ~ {
GetTxtrAddress: PROC[buf: ImagerPixel.PixelMap, x, y: REAL] 
	RETURNS[ txtrX, txtrY: INTEGER ] ~ { 
bufWidth: NAT _ buf.box.max.f - buf.box.min.f;
bufHeight: NAT _ buf.box.max.s - buf.box.min.s;
txtrX _ Real.Fix[x * bufWidth] MOD bufWidth; 
IF txtrX < 0 THEN txtrX _ txtrX + bufWidth;
txtrY _ Real.Fix[y * bufHeight] MOD bufHeight; 
IF txtrY < 0 THEN txtrY _ txtrY + bufHeight;
};

pixel: ImagerPixel.PixelBuffer _ ImagerPixel.ObtainScratchPixels[map.samplesPerPixel, 1];
x, y: INTEGER;
txtrX: NAT ~ spot.val.length - 5;
txtrY: NAT ~ txtrX+1;
 
[x, y] _ GetTxtrAddress[map, spot.val[txtrX], spot.val[txtrY]];
ImagerPixel.GetPixels[self: map, pixels: pixel, initIndex: [f: x, s: y+map.box.min.s], count: 1]; 
SELECT type FROM 
$Color	=> FOR i: NAT IN [0..3) DO  
txtrValue: REAL _ pixel[i][0] / 256.0 ;
spot.val[i] _ spot.val[i] * txtrValue;  
ENDLOOP;
$ColorAndTransmittance	=> {
surfTrans: REAL _ 1.0 - spot.val[3];
txtrTrans: REAL _ 1.0 - pixel[3][0] /256.0;
FOR i: NAT IN [0..3) DO  
txtrVal: REAL _ pixel[i][0] / 256.0 ;
spot.val[i] _ txtrTrans * surfTrans * spot.val[i] -- lerp spot reduced by transmittance
			  + (1.0 - txtrTrans) * spot.val[i] * txtrVal;	-- with spot times texture
ENDLOOP;
spot.val[3] _ spot.val[3] * txtrTrans;  			-- transmittances multiply
};
$IntensityandTransmittance	=> { -- alpha channels provide coverage = 1.0 - transmtnce
surfTrans: REAL _ 1.0 - spot.val[3];
txtrVal: REAL _ 1.0 - pixel[0][0] / 256.0 ;
txtrTrans: REAL _ 1.0 - pixel[1][0] / 256.0 ;
FOR i: NAT IN [0..3) DO  
spot.val[i] _ txtrTrans * surfTrans * spot.val[i]  -- lerp spot reduced by transmittance
			  + (1.0 - txtrTrans) * spot.val[i] * txtrVal;	-- with spot times texture
ENDLOOP;
spot.val[3] _ spot.val[3] * txtrTrans;  			-- transmittances multiply
};
$Intensity	=> {
txtrValue: REAL _ pixel[0][0] / 256.0 ;
FOR i: NAT IN [0..3) DO  
spot.val[i] _ spot.val[i] * txtrValue;  
ENDLOOP;
};
ENDCASE		=> SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture type"]];
ImagerPixel.ReleaseScratchPixels[pixel];
};

SumMapTexture: PROC[spot: REF Spot, txtrSum: REF SummedTexture, type: ATOM] ~ {
maxTxtrX: REAL _ txtrSum[0][0].length;				-- width (thus max x address) of texture
maxTxtrY: REAL _ txtrSum[0].length;					-- height (thus max y address) of texture
txtrX: NAT ~ spot.val.length - 5;
txtrY: NAT ~ txtrX+1;
txtrXIncr: REAL _ 0.5 * MAX[ ABS[spot.yIncr[txtrX]], ABS[spot.xIncr[txtrX]] ];
txtrYIncr: REAL _ 0.5 * MAX[ ABS[spot.yIncr[txtrY]], ABS[spot.xIncr[txtrY]] ]; 
area: REAL _ 4 * txtrXIncr * maxTxtrX * txtrYIncr * maxTxtrY;

WHILE spot.val[txtrX] - txtrXIncr < 0.0 DO spot.val[txtrX] _ spot.val[txtrX] + 1.0;  ENDLOOP;
WHILE spot.val[txtrY] - txtrYIncr < 0.0 DO spot.val[txtrY] _ spot.val[txtrY] + 1.0;  ENDLOOP;

SELECT type FROM 
$Color	=> FOR i: NAT IN [0..3) DO  
txtrValue: REAL _ 
			SumValues[spot, txtrSum[i], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0;
spot.val[i] _ MAX[ 0.0, spot.val[i] * txtrValue ];  
ENDLOOP;
$ColorAndTransmittance	=> {
surfTrans: REAL _ 1.0 - spot.val[3];
txtrTrans: REAL _ SumValues[
spot, txtrSum[3], txtrX, txtrY, txtrXIncr, txtrYIncr, area
] /256.0;
txtrTrans _ 1.0 - MAX[ 0.0, txtrTrans];  -- from alpha to transmittance
FOR i: NAT IN [0..3) DO  
txtrVal: REAL _ SumValues[
spot, txtrSum[i], txtrX, txtrY, txtrXIncr, txtrYIncr, area
] /256.0;
spot.val[i] _ 	  txtrTrans * surfTrans * spot.val[i]     -- lerp spot reduced by trans.
			  + (1.0 - txtrTrans) * spot.val[i] * txtrVal; 	-- with spot times texture
spot.val[i] _ MAX[ 0.0, spot.val[i]];
ENDLOOP;
spot.val[3] _ spot.val[3] * txtrTrans;  			-- transmittances multiply
};
$IntensityandTransmittance	=> {
txtrVal: REAL _ 
			SumValues[spot, txtrSum[0], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0;
surfTrans: REAL _ 1.0 - spot.val[3];
txtrTrans: REAL _ 
			SumValues[spot, txtrSum[1], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0;
txtrVal _ MAX[ 0.0, txtrVal];
txtrTrans _ 1.0 - MAX[ 0.0, txtrTrans]; -- from alpha to transmittance
FOR i: NAT IN [0..3) DO  
spot.val[i] _    txtrTrans * surfTrans * spot.val[i]      -- lerp spot reduced by trans.
			 + (1.0 - txtrTrans) * spot.val[i] * txtrVal;  	-- with spot times texture 
spot.val[i] _ MAX[ 0.0, spot.val[i]];
ENDLOOP;
spot.val[3] _ spot.val[3] * txtrTrans;  			-- transmittances multiply
};
$Intensity	=> {
txtrValue: REAL _ 
			SumValues[spot, txtrSum[0], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0;
IF txtrValue < 0.0 THEN txtrValue _ 0.0;
IF 
spot.xySwapped THEN txtrValue _ 0.5 * txtrValue;
FOR i: NAT IN [0..3) DO  
spot.val[i] _ MAX[ 0.0, spot.val[i] * txtrValue ];  
ENDLOOP;
};
ENDCASE		=> SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture type"]];
}; 

BumpTexture: PROC[spot: REF Spot, txtrSum: REF SummedTexture, bumpScale: REAL] ~ {
txtrX: NAT ~ spot.val.length - 5;
txtrY: NAT ~ txtrX+1;
Positivize: PROC[] ~ {
WHILE spot.val[txtrX] - maxXIncr < 0.0 DO spot.val[txtrX] _ spot.val[txtrX] + 1.0;  ENDLOOP;
WHILE spot.val[txtrY] - maxYIncr < 0.0 DO spot.val[txtrY] _ spot.val[txtrY] + 1.0;  ENDLOOP;
};
nmlX: NAT ~ 4;    nmlY: NAT ~ nmlX + 1;    nmlZ: NAT ~ nmlX + 2;
maxTxtrX: REAL _ txtrSum[0][0].length;				-- width (thus max x address) of texture
maxTxtrY: REAL _ txtrSum[0].length;					-- height (thus max y address) of texture

maxXIncr, maxYIncr, area, length: REAL;
txtrXIncr: Pair _ [spot.xIncr[txtrX], spot.xIncr[txtrY]];
txtrYIncr: Pair _ [-txtrXIncr.y, txtrXIncr.x];					-- rotate txtrXIncr 90 deg.
cosA: REAL _ Vector2.Dot[
Vector2.Unit[txtrYIncr], Vector2.Unit[[spot.yIncr[txtrX], spot.yIncr[txtrY]]] 
];
txtrYIncr _ Vector2.Mul[txtrYIncr, ABS[cosA]];
IF spot.xySwapped THEN {  			-- compensate for sideways scanning in tiler
txtrYIncr.x _ -txtrYIncr.x;    txtrYIncr.y _ -txtrYIncr.y;
[[txtrXIncr.x, txtrYIncr.x]] _ Swap[[txtrXIncr.x, txtrYIncr.x]];    
[[txtrXIncr.y, txtrYIncr.y]] _ Swap[[txtrXIncr.y, txtrYIncr.y]];  
};

maxXIncr _ 0.5 * MAX[ ABS[txtrXIncr.x], ABS[txtrYIncr.x] ];
maxYIncr _ 0.5 * MAX[ ABS[txtrXIncr.y], ABS[txtrYIncr.y] ];
area _ 4 * maxXIncr * maxTxtrX * maxYIncr * maxTxtrY;

length _ Vector2.Length[ txtrXIncr ];
IF length < 2.0/maxTxtrX THEN txtrXIncr _ Vector2.Mul[ txtrXIncr, 2.0 / (length*maxTxtrX) ];  
length _ Vector2.Length[ txtrYIncr ];
IF length < 2.0/maxTxtrX THEN txtrYIncr _ Vector2.Mul[ txtrYIncr, 2.0 / (length*maxTxtrX) ];

SELECT txtrSum.length FROM 
1	=> {
txtrValue, txtrValueX, txtrValueY, cosA, sinA, length: REAL;
perturbDir, tnml, nml: Triple;
Positivize[];							-- ensure positive texture coordinate values
txtrValue _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] / 256.0;

spot.val[txtrX] _ spot.val[txtrX] + txtrXIncr.x;	-- get txtr offset one pixel to right 
spot.val[txtrY] _ spot.val[txtrY] + txtrXIncr.y;
Positivize[];
txtrValueX _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] /256.0;

spot.val[txtrX] _ spot.val[txtrX] - txtrXIncr.x;	-- get original texture value back 
spot.val[txtrY] _ spot.val[txtrY] - txtrXIncr.y;
spot.val[txtrX] _ spot.val[txtrX] + txtrYIncr.x;	-- get txtr offset one pixel above 
spot.val[txtrY] _ spot.val[txtrY] + txtrYIncr.y;
Positivize[];
txtrValueY _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] /256.0;

perturbDir _ G3dVector.Cross[ 
[maxTxtrX*txtrXIncr.x, maxTxtrY*txtrXIncr.y, bumpScale*(txtrValueX - txtrValue)],
[maxTxtrX*txtrYIncr.x, maxTxtrY*txtrYIncr.y, bumpScale*(txtrValueY - txtrValue)]
];
perturbDir.z _ ABS[perturbDir.z];
perturbDir _ G3dVector.Normalize[perturbDir];

length _ RealFns.SqRt[ Sqr[perturbDir.x] + Sqr[perturbDir.z] ];
nml _ [spot.val[nmlX], spot.val[nmlY], spot.val[nmlZ]];
cosA _ perturbDir.z / length;
sinA _ perturbDir.x / length;
tnml.x _ nml.x * cosA + nml.z * sinA;
tnml.y _ nml.y;
tnml.z _ - nml.x * sinA + nml.z * cosA;
nml _ G3dVector.Normalize[tnml];
length _ RealFns.SqRt[ Sqr[perturbDir.z] + Sqr[perturbDir.y] ];
cosA _ perturbDir.z / length;
sinA _ perturbDir.y / length;
tnml.x _ nml.x;
tnml.y _ nml.y * cosA + nml.z * sinA;
tnml.z _ - nml.y * sinA + nml.z * cosA;
nml _ G3dVector.Normalize[tnml];

spot.val[nmlX] _ nml.x;
spot.val[nmlY] _ nml.y;
spot.val[nmlZ] _ nml.z;
};
ENDCASE		=> SIGNAL ThreeDBasics.Error[[$MisMatch, 
													"Wrong no. of samples in texture"]];
}; 
GetLerpedValue: PROC[ llVal, ulVal, urVal, lrVal: INT, xPos, yPos: REAL ] 
RETURNS[ intPart: INT, fracPart: REAL ] ~ {
lowerFrac, upperFrac: REAL;      lowerInt, upperInt: INT;

lowerFrac _ xPos * (lrVal - llVal); 				-- lerp upper values
lowerInt _ Real.Fix[ lowerFrac ];
lowerFrac _ lowerFrac - lowerInt;
lowerInt _ llVal + lowerInt;

upperFrac _ xPos * (urVal - ulVal); 				-- lerp lower values
upperInt _ Real.Fix[ upperFrac ]; 
upperFrac _ upperFrac - upperInt; 
upperInt _ ulVal + upperInt;

fracPart _  yPos * (upperInt - lowerInt); 		-- lerp upper and lower lerps
intPart _ Real.Fix[ fracPart ]; 
fracPart _ fracPart - intPart;
			
intPart _ intPart + lowerInt;
fracPart _ fracPart + lowerFrac + yPos * (upperFrac - lowerFrac);
}; 

CorrectSum: PROC[txtrSum: REF SumSequence, x, y: NAT]  RETURNS[INT] ~ {
maxTxtrX: NAT _ txtrSum[0].length-1;
maxTxtrY: NAT _ txtrSum.length-1;
IF x < txtrSum[0].length AND y < txtrSum.length THEN  {
RETURN[ txtrSum[y][x] ]
}
ELSE IF x >= txtrSum[0].length AND y >= txtrSum.length THEN {
x _ x - txtrSum[0].length;		y _ y - txtrSum.length;
RETURN[	   CorrectSum[txtrSum, x, y] 			+ txtrSum[maxTxtrY][maxTxtrX]
			+ CorrectSum[txtrSum, x, maxTxtrY] 	+ CorrectSum[txtrSum, maxTxtrX, y]  ];
}
ELSE IF x >= txtrSum[0].length THEN {
x _ x - txtrSum[0].length;		
RETURN[ CorrectSum[txtrSum, x, y] + txtrSum[y][maxTxtrX] ];
}
ELSE {												-- IF y >= txtrSum.length
y _ y - txtrSum.length;
RETURN[ CorrectSum[txtrSum, x, y] + txtrSum[maxTxtrY][x] ];
};
};

GetValueAt: PROC[txtrSum: REF SumSequence, x, y: REAL]  
				RETURNS[ intPart: INT, fracPart: REAL ] ~ { 
xPos, yPos: REAL;		lX, lY, rX, uY: NAT;
xPos _ x * txtrSum[0].length;
lX _ Real.Fix[xPos];     rX _ lX + 1; 
yPos _ y * txtrSum.length;
lY _ Real.Fix[yPos];    uY _ lY + 1; 
xPos _ xPos - Real.Fix[xPos];									-- get fractional part
yPos _ yPos - Real.Fix[yPos];
[ intPart, fracPart ] _ GetLerpedValue[ 
CorrectSum[txtrSum, lX, lY], CorrectSum[txtrSum, lX, uY], 
CorrectSum[txtrSum, rX, uY], CorrectSum[txtrSum, rX, lY],
xPos, yPos
];
}; 

SumValues: PROC[ spot: REF Spot, txtrSum: REF SumSequence, txtrX, txtrY: NAT,
					   txtrXIncr, txtrYIncr, area: REAL ] 
			   RETURNS[ txtrValue: REAL ] ~ {
i1, i2, i3, i4: INT;     f1, f2, f3, f4: REAL;
IF area < 4.0 THEN {	-- summed area tables don't work if all samples lie in the same pixel
area _ 4.0;					-- filtering works best if done over  two pixel widths
txtrXIncr _ 1.0 / txtrSum[0].length;    txtrYIncr _ 1.0 / txtrSum.length;
};
[ i1, f1 ] _ GetValueAt[ txtrSum, spot.val[txtrX] + txtrXIncr,  spot.val[txtrY] + txtrYIncr ];
[ i2, f2 ] _ GetValueAt[ txtrSum, spot.val[txtrX] -  txtrXIncr,	spot.val[txtrY] - txtrYIncr ];
[ i3, f3 ] _ GetValueAt[ txtrSum, spot.val[txtrX] + txtrXIncr,	spot.val[txtrY] - txtrYIncr ];
[ i4, f4 ] _ GetValueAt[ txtrSum, spot.val[txtrX] -  txtrXIncr,	spot.val[txtrY] + txtrYIncr ];
txtrValue _ (i1 + i2 - i3 - i4 ) / area;
txtrValue _ txtrValue + (f1 + f2 - f3 - f4 ) / area;
}; 

RegisterTextureFunction: PUBLIC PROC[ name: ATOM, proc: SpotProc, 
												  props: Atom.PropList _ NIL ] ~ {
registeredTextureFunctions _ Atom.PutPropOnList[
registeredTextureFunctions, name, NEW[ TextureFunction _ [name, proc, props] ] 
];	
};

GetRegisteredTextureFunction: PUBLIC PROC[name: ATOM] 
											 RETURNS[ txtrFn: TextureFunction ] ~ {
ref: REF TextureFunction _ NARROW[
Atom.GetPropFromList[registeredTextureFunctions, name]
];
IF ref # NIL 
THEN txtrFn _ ref^ 
ELSE SIGNAL ThreeDBasics.Error[[$MisMatch, "Unregistered procedure"]]
};

Spots: SpotProc ~ {
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
intensity: REAL _ RealFns.Sin[10.0 * spot.val[x] ] 
				  * RealFns.Sin[14.0 * spot.val[y] ]
				  * RealFns.Sin[20.0 * spot.val[z] ];
intensity _ (intensity + 1.0) / 2.0;
spot.val[r] _ spot.val[r] * intensity;
spot.val[g] _ spot.val[g] * intensity;
spot.val[b] _ spot.val[b] * intensity;
spot.val[t] _ spot.val[t] * intensity;
};

Wurlitzer: SpotProc ~ {		
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
intensity: REAL _ RealFns.Sin[10.0 * spot.val[x] ] 
				  * RealFns.Sin[14.0 * spot.val[y] ]
				  * RealFns.Sin[20.0 * spot.val[z] ];
intensity _ (intensity + 1.0) / 2.0;
spot.val[r] _ spot.val[r] * (RealFns.Sin[10.0*spot.val[x]] +1.0) / 2.0;
spot.val[g] _ spot.val[g] * (RealFns.Sin[14.0*spot.val[y]] +1.0) / 2.0;
spot.val[b] _ spot.val[b] * (RealFns.Sin[20.0*spot.val[z]] +1.0) / 2.0;
spot.val[t] _ spot.val[t] * intensity;
};

TwistedStripes: SpotProc ~ {
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
angle: REAL _ 3.1416  * spot.val[z];				-- rotation varies with z
cosAngle: REAL _ RealFns.Cos[angle];
sinAngle: REAL _ RealFns.Sin[angle];
intensity: REAL _ RealFns.Sin[40.0 * 				-- x component of rotated x-y vector
								 (cosAngle * spot.val[x] + sinAngle * spot.val[y]) ];
transmittance: REAL; 
intensity _ (intensity + 1.0) / 2.0;
transmittance _ (1.0 - intensity); 
spot.val[t] _ spot.val[t] * transmittance;
spot.val[r] _ spot.val[r] * intensity;
spot.val[g] _ spot.val[g] * (1.0 - intensity);
spot.val[b] _ spot.val[b] * intensity;
};

BurlWood: SpotProc ~ {
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
red, grn, blu: REAL;
chaos: REAL _ Chaos[  spot.val[x], spot.val[y], spot.val[z] ];
midBrown: REAL _ RealFns.Sin[ chaos*8 + 7*spot.val[x]  + 3* spot.val[y] ];
brownLayer: REAL _ ABS[ RealFns.Sin[midBrown] ];
greenLayer: REAL _ - brownLayer;

perturb: REAL _ IF brownLayer > 0.0 
THEN ABS[RealFns.Sin[40 * chaos + 50*spot.val[z] ]]
ELSE ABS[RealFns.Sin[30 * chaos + 30*spot.val[x] ]];
brownPerturb: REAL _ perturb * .6 + .3;		-- perturb up to .6
greenPerturb: REAL _ perturb * .2 + .8;		-- perturb up to .2
grnPerturb: REAL _ perturb * .15 + .85;		-- perturb up to .15

grn _ .5 * RealFns.Power[ABS[brownLayer], 0.3];	-- makes seams

brownLayer _ RealFns.Power[(brownLayer + 1.0) / 2.0, 0.6] * brownPerturb;
greenLayer _ RealFns.Power[(greenLayer + 1.0) / 2.0, 0.6] * greenPerturb;
red _ (.6 * brownLayer + .35 * greenLayer) * 2 * grn; 
blu  _ (.25 * brownLayer + .35 * greenLayer) * 2 * grn;
grn _ grn * MAX[brownLayer, greenLayer] * grnPerturb;

spot.val[r] _ spot.val[r] * red;
spot.val[g] _ spot.val[g] * grn;
spot.val[b] _ spot.val[b] * blu;
};

ZebraBurl: SpotProc ~ {
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
red, grn, blu: REAL;
chaos: REAL _ Chaos[  spot.val[x], spot.val[y], spot.val[z] ];
midBrown: REAL _ RealFns.Sin[ chaos*8 + 7*spot.val[x]  + 3* spot.val[y] ];
brownLayer: REAL _ RealFns.Sin[midBrown];
greenLayer: REAL _ - brownLayer;

perturb: REAL _ IF brownLayer > 0.0 
THEN ABS[RealFns.Sin[40 * chaos + 50*spot.val[z] ]]
ELSE ABS[RealFns.Sin[24 * chaos + 30*spot.val[x] ]];
brownPerturb: REAL _ perturb * .6 + .3;		-- perturb up to .6
greenPerturb: REAL _ perturb * .2 + .8;		-- perturb up to .2
grnPerturb: REAL _ perturb * .15 + .85;		-- perturb up to .15

grn _ .5 * RealFns.Power[ABS[brownLayer], 0.3];	-- makes seams

brownLayer _ RealFns.Power[(brownLayer + 1.0) / 2.0, 0.6] * brownPerturb;
greenLayer _ RealFns.Power[(greenLayer + 1.0) / 2.0, 0.6] * greenPerturb;
red _ (.6 * brownLayer + .35 * greenLayer) * 2 * grn; 
blu  _ (.25 * brownLayer + .35 * greenLayer) * 2 * grn;
grn _ grn * MAX[brownLayer, greenLayer] * grnPerturb;

spot.val[r] _ spot.val[r] * red;
spot.val[g] _ spot.val[g] * grn;
spot.val[b] _ spot.val[b] * blu;
};

Marble: SpotProc ~ {
x: NAT _ spot.val.length-3;    y: NAT _ x+1;    z: NAT _ x+2;	-- object space coordinate
r: NAT ~ 0;    g: NAT ~ 1;    b: NAT ~ 2;    t: NAT ~ 3;
intensity: REAL _ RealFns.Sin[Chaos[  spot.val[x],
											spot.val[y],
											spot.val[z] ]*8 + 7*spot.val[z]];
intensity _ (intensity + 1.0) / 2.0;
intensity _ RealFns.Power[intensity, 0.77];
spot.val[r] _ spot.val[r] * intensity;
spot.val[g] _ spot.val[g] * intensity;
spot.val[b] _ spot.val[b] * intensity;
};

Chaos: PUBLIC PROC[x, y, z: REAL] RETURNS [REAL] ~ {
f: REAL _ 1.;
s, t: REAL _ 0.;
FOR n: INT IN [0..7) DO
s _ Noise[x * f, y * f,  z * f];
t _ t + ABS[s] / f;
f _ 2 * f;
ENDLOOP;
RETURN [t];
};

realScale: REAL _ 2.0 / LAST[CARDINAL];
RTable: TYPE ~ RECORD[SEQUENCE length: NAT OF REAL];
rTable: REF RTable _ NIL;

Noise: PUBLIC PROC[vx, vy, vz: REAL] RETURNS [REAL] ~ {

R: PROC[i, j, k: REAL] RETURNS [CARDINAL] ~ TRUSTED {
A: TYPE ~ ARRAY [0..3) OF REAL;
a: A _ [i * .12345 , j * .12345 , k * .12345 ];
aPointer: LONG POINTER TO A ~ @a;
h: CARDINAL _ Checksum.ComputeChecksum[nWords: SIZE[A], p: LOOPHOLE[aPointer]];
RETURN [h];
};

SCurve: PROC[x: REAL] RETURNS [REAL] ~ {

RETURN [x * x * (3 - 2 * x)];
};

m: NAT;
ix, iy, iz: INT;
x, y, z, jx, jy, jz, sx, sy, sz, tx, ty, tz, s, f: REAL;
IF rTable = NIL THEN {
rTable _ NEW[RTable[259]];
FOR n:INT IN [0..259) DO
r:REAL _ n;
rTable[n] _ R[r, r, r] * realScale - 1.;
ENDLOOP;
};
x _ vx + 1000.;
y _ vy + 1000.;
z _ vz + 1000.;
ix _ Real.Fix[x];
iy _ Real.Fix[y];
iz _ Real.Fix[z];
sx _ SCurve[x - ix];
sy _ SCurve[y - iy];
sz _ SCurve[z - iz];
tx _ 1. - sx;
ty _ 1. - sy;
tz _ 1. - sz;
f _ 0.; -- initialize sum to zero.
FOR n: INT IN [0..8) DO -- sum together 8 local fields from neighboring lattice pts.
SELECT n FROM -- each of 8 corners of the surrounding unit cube.
0 => {jx _ ix		; jy _ iy		; jz _ iz		; s _ tx	* ty	* tz	};
1 => {jx _ ix+1	 								; s _ sx	* ty	* tz	};
2 => {jx _ ix		; jy _ iy+1	 				; s _ tx	* sy	* tz	};
3 => {jx _ ix+1	 								; s _ sx	* sy	* tz	};
4 => {jx _ ix		; jy _ iy		; jz _ iz+1	; s _ tx	* ty	* sz	};
5 => {jx _ ix+1	 								; s _ sx	* ty	* sz	};
6 => {jx _ ix		; jy _ iy+1					; s _ tx	* sy	* sz	};
7 => {jx _ ix+1									; s _ sx	* sy	* sz	};
ENDCASE;
m _ R[jx, jy, jz] MOD 256;
f _ f + s * ( rTable[m]/2 + rTable[m+1]*(x-jx) +
				rTable[m+2]*(y-jy) + rTable[m+3]*(z-jz) );
ENDLOOP;
RETURN [f];
};

EnableStreamProcs: PUBLIC PROC[ context: REF Context ] ~ {
context.props _ Atom.PutPropOnList[
context.props, 
$TextureMapFromStream, 
NEW[ShapeProc _ TextureMapFromStream]
];
context.props _ Atom.PutPropOnList[
context.props, 
$TextureFunctionFromStream, 
NEW[ShapeProc _ TextureFunctionFromStream]
];
};

TextureMapFromStream: ShapeProc ~ {
input: IO.STREAM _ NARROW[data];
txtrMap: REF TextureMap _ TextureFromAIS[ context: context, 
fileName: SceneUtilities.GetRope[input],  
type: IO.GetAtom[input]
];
AddMappedTexture[ context, shape.name, txtrMap ];
SELECT IO.GetAtom[input] FROM
$FromVtxNos => MakeTxtrCoordsFromVtxNos[ context: context, shapeName: shape.name,
vtcesInRow: IO.GetInt[input], numberOfRows: IO.GetInt[input],
row0col0: [IO.GetInt[input], IO.GetInt[input]], 
rowNcol0: [IO.GetInt[input], IO.GetInt[input]], 
rowNcolM: [IO.GetInt[input], IO.GetInt[input]], 
row0ColM: [IO.GetInt[input], IO.GetInt[input]]
];
$FromNormals => MakeTxtrCoordsFromNormals[ context: context, shapeName: shape.name,
botLeft: [IO.GetInt[input], IO.GetInt[input]], 
topLeft: [IO.GetInt[input], IO.GetInt[input]],
topRight: [IO.GetInt[input], IO.GetInt[input]], 
botRight: [IO.GetInt[input], IO.GetInt[input]], 
sw: [IO.GetInt[input], IO.GetInt[input]], 
nw: [IO.GetInt[input], IO.GetInt[input]], 
ne: [IO.GetInt[input], IO.GetInt[input]],     
se: [IO.GetInt[input], IO.GetInt[input]]
];
$NoCoords => {};
ENDCASE => SIGNAL ThreeDBasics.Error[
[$Unimplemented, "Unknown texture coordType"]
];
[] _ SceneUtilities.GetRope[input];			-- ignore word "Scale"
ScaleTxtrCoords[context, shape.name, IO.GetReal[input], IO.GetReal[input], IO.GetReal[input]];
RETURN[ shape ];
};

TextureFunctionFromStream: ShapeProc ~ {
input: IO.STREAM _ NARROW[data];
AddSolidTexture[ context, shape.name, IO.GetAtom[input] ];
RETURN[ shape ];
};

ScaleTxtrCoords: PUBLIC PROC [ context: REF Context, shapeName: Rope.ROPE, 
									   scale: REAL, xRatio, yRatio: REAL _ 1.0 ] ~ {
xScale, yScale: REAL;
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
auxInfo: REF PairSequence _ NARROW[GetProp[ shape.shadingProps, $AuxiliaryVtxData]];
refTriple: REF Triple _ NARROW[GetProp[shape.shadingProps, $TextureScale]];
IF refTriple = NIL THEN refTriple _ NEW[Triple _ [1.0, 1.0, 1.0]];
IF refTriple.x # 0.0 AND refTriple.y # 0.0 AND refTriple.z # 0.0 THEN {
xScale _ scale*xRatio/(refTriple.x*refTriple.y);	-- multiply by new, divide by old values
yScale _ scale*yRatio/(refTriple.x*refTriple.z);
};
refTriple^ _ [scale, xRatio, yRatio];
FOR i: NAT IN [0..auxInfo.length) DO 
auxInfo[i].x _ auxInfo[i].x * xScale;
auxInfo[i].y _ auxInfo[i].y * yScale;
ENDLOOP;
shape.shadingProps _ PutProp[shape.shadingProps, $TextureScale, refTriple];
};

MakeTxtrCoordsFromVtxNos: PUBLIC PROC[ context: REF Context, shapeName: Rope.ROPE, 
													  vtcesInRow, numberOfRows: NAT,
                     		  					row0col0, rowNcol0, rowNcolM, row0ColM: Pair] ~ {
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
auxInfo: REF PairSequence _ NARROW[GetProp[shape.shadingProps, $AuxiliaryVtxData]];
args: LIST OF REAL;

IF GetProp[ shape.fixedProps, $VertexTextureInFile] # NIL THEN {
SIGNAL ThreeDBasics.Error[[$MisMatch, "Overwriting original texture coords, OK?"]];
shape.shadingProps _ Atom.RemPropFromList[shape.shadingProps, $VertexTextureInFile];
};
IF auxInfo = NIL THEN auxInfo _ NEW[ PairSequence[shape.shade.length] ];
IF    row0ColM.x - row0col0.x > .3 * vtcesInRow 
 OR rowNcolM.x - rowNcol0.x > .3 * vtcesInRow
 OR rowNcolM.y - row0ColM.y > .3 * numberOfRows 
 OR rowNcol0.y - row0col0.y > .3 * numberOfRows
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Texture mapping dangerously dense"]];
FOR i: NAT IN [0..shape.shade.length) DO 
lPosX, lPosY, rPosX, rPosY: REAL;
auxInfo[i].x _ Real.Float[i MOD vtcesInRow] / vtcesInRow;	-- pct along row
auxInfo[i].y _ Real.Float[i / vtcesInRow] / numberOfRows;	-- pct across rows
lPosX _ row0col0.x + auxInfo[i].y * (rowNcol0.x - row0col0.x);-- interp across rows
lPosY _ row0col0.y + auxInfo[i].y * (rowNcol0.y - row0col0.y); 
rPosX _ row0ColM.x + auxInfo[i].y * (rowNcolM.x - row0ColM.x);
rPosY _ row0ColM.y + auxInfo[i].y * (rowNcolM.y - row0ColM.y); 
auxInfo[i].x _ lPosX + auxInfo[i].x * (rPosX - lPosX);  -- interp along row
auxInfo[i].y _ lPosY + auxInfo[i].x * (rPosY - lPosY);
ENDLOOP; 
auxInfo.length _ shape.shade.length;
shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo];
shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordType, $FromVtxNos];
args _ CONS[row0ColM.y, NIL];     args _ CONS[row0ColM.x, args];  
args _ CONS[rowNcolM.y, args];    args _ CONS[rowNcolM.x, args];  
args _ CONS[rowNcol0.y, args];     args _ CONS[rowNcol0.x, args];  
args _ CONS[row0col0.y, args];     args _ CONS[row0col0.x, args];   
args _ CONS[Real.Float[numberOfRows], args];     args _ CONS[Real.Float[vtcesInRow], args];
shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordParams, args];
};
        
GetTxtrCoordsFromNormal: PUBLIC PROC[ context: REF Context, vtx: VertexInfo ] ~ {
};

MakeTxtrCoordsFromNormals: PUBLIC PROC[ context: REF Context, shapeName: Rope.ROPE, 
                     		  				  botLeft: Pair _ [0.0, 0.0], topLeft: Pair _ [0.0, 1.0],
                     		  				 topRight: Pair _ [1.0, 1.0], botRight: Pair _ [1.0, 0.0], 
     										 sw: Pair _ [-180.0, -90.0], nw: Pair _ [-180.0, 90.0], 
     										 ne: Pair _ [180.0, 90.0],     se: Pair _ [180.0, -90.0] ] ~ {
shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ];
auxInfo: REF PairSequence _ NARROW[GetProp[shape.shadingProps, $AuxiliaryVtxData]];
args: LIST OF REAL;
poly: REF ThreeDBasics.PtrPatchSequence _ NARROW[shape.surface];
polyTags: ARRAY [0..16) OF BOOLEAN;
lngtShift: REAL _ 0.0;								-- latitude shift to allow < -180.0 and > 180.0

IF GetProp[shape.fixedProps, $VertexTextureInFile] # NIL THEN {
SIGNAL ThreeDBasics.Error[[$MisMatch, "Overwriting original texture coords, OK?"]];
shape.shadingProps _ Atom.RemPropFromList[shape.shadingProps, $VertexTextureInFile];
};
IF auxInfo = NIL THEN auxInfo _ NEW[ PairSequence[shape.shade.length] ];
IF GetProp[shape.shadingProps, $VtxInfoComputed] = NIL THEN { 
shape.shadingClass.shadingType _ $Smooth;   -- smooth shading forces computed normals 
SurfaceRender.ValidateContext[context];			-- make sure viewport, etc. is kosher
};
IF MAX[sw.x, nw.x, se.x, ne.x] - MIN[sw.x, nw.x, se.x, ne.x] > 360.0 
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Longitude range exceeds 360 degrees"]];
IF MIN[sw.x, nw.x, se.x, ne.x] < -180.0 
THEN lngtShift _ -180.0 - MIN[sw.x, nw.x, se.x, ne.x]		-- positive shift if past -180
ELSE IF MAX[sw.x, nw.x, se.x, ne.x] > 180.0 
THEN lngtShift _ 180.0 - MAX[sw.x, nw.x, se.x, ne.x];	 -- negative shift if past 180

IF ABS[nw.y - sw.y] < 0.001 THEN  nw.y _ sw.y + Sgn[nw.y - sw.y] * 0.001; -- stop div errs
IF ABS[ne.y - se.y]  < 0.001 THEN  ne.y _ se.y + Sgn[ne.y - se.y] * 0.001; 
IF ABS[sw.x - se.x]  < 0.001 THEN  sw.x _ se.x + Sgn[sw.x - se.x] * 0.001; 
IF ABS[nw.x - ne.x] < 0.001 THEN  nw.x _ ne.x + Sgn[nw.x - ne.x] * 0.001; 

FOR polyNumber: NAT IN [0..poly.length) DO 
minTxtrX, minTxtrY: REAL _ Real.LargestNumber;
maxTxtrX: REAL _ 0.;
FOR i: NAT IN [0..poly[polyNumber].nVtces) DO
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
hypotenuse: REAL _ RealFns.SqRt[Sqr[shape.shade[vtx].yn] + Sqr[shape.shade[vtx].xn]];
longitude: REAL _ RealFns.ArcTanDeg[shape.shade[vtx].yn, shape.shade[vtx].xn];
latitude: REAL _ RealFns.ArcTanDeg[shape.shade[vtx].zn, hypotenuse];
lPosY: REAL _ (latitude - sw.y) / (nw.y - sw.y); -- percentage of distance on left edge
rPosY: REAL _ (latitude - se.y) / (ne.y - se.y); 
lPosX: REAL _ sw.x + lPosY * (nw.x - sw.x); 		-- weighted average of positions
rPosX: REAL _ se.x + rPosY * (ne.x - se.x);
IF longitude > 180.0 - lngtShift 
THEN longitude _ -180.0 - (180.0 - longitude) 
ELSE IF longitude < -180.0 - lngtShift
THEN longitude _ 180.0 + (longitude + 180.0);
auxInfo[vtx].x _ (longitude - lPosX) / (rPosX - lPosX); 	-- percentage across
auxInfo[vtx].y _ lPosY + auxInfo[vtx].x * (rPosY - lPosY); -- wtd av. %
auxInfo[vtx].x _ MIN[ 1.0, MAX[0.0, auxInfo[vtx].x]];
auxInfo[vtx].y _ MIN[ 1.0, MAX[0.0, auxInfo[vtx].y]];
IF hypotenuse < 0.00001 THEN  polyTags[i] _ TRUE 		-- catch unstable arithmetic
ELSE  {
polyTags[i] _ FALSE;
IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x;
IF auxInfo[vtx].x > maxTxtrX THEN maxTxtrX _ auxInfo[vtx].x;
};
ENDLOOP;
auxInfo.length _ shape.shade.length;
shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo];
IF maxTxtrX - minTxtrX > .5              			-- wrapping around seam, fix up coords
THEN FOR i: NAT IN [0..poly[polyNumber].nVtces) DO
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
IF maxTxtrX - auxInfo[vtx].x > .5 
THEN auxInfo[vtx].x _ auxInfo[vtx].x + 1.;
ENDLOOP; 
minTxtrX _ Real.LargestNumber; 
maxTxtrX _ 0.;
FOR i: NAT IN [0..poly[polyNumber].nVtces) DO			-- get corrected max and min
IF polyTags[i] = FALSE THEN {
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x;
IF auxInfo[vtx].x > maxTxtrX THEN maxTxtrX _ auxInfo[vtx].x;
};
ENDLOOP;
FOR i: NAT IN [0..poly[polyNumber].nVtces) DO	-- fix up unstable vertical normals
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
IF polyTags[i] = TRUE THEN auxInfo[vtx].x _ (maxTxtrX + minTxtrX) / 2.;
ENDLOOP; 
minTxtrX _ minTxtrY _ Real.LargestNumber; 
FOR i: NAT IN [0..poly[polyNumber].nVtces) DO		-- slew according to corner coords
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
lPosX: REAL _ botLeft.x + auxInfo[vtx].y * (topLeft.x - botLeft.x); 
lPosY: REAL _ botLeft.y + auxInfo[vtx].y * (topLeft.y - botLeft.y); 
rPosX: REAL _ botRight.x + auxInfo[vtx].y * (topRight.x - botRight.x);
rPosY: REAL _ botRight.y + auxInfo[vtx].y * (topRight.y - botRight.y); 
auxInfo[vtx].x _ lPosX + auxInfo[vtx].x * (rPosX - lPosX); 
auxInfo[vtx].y _ lPosY + auxInfo[vtx].x * (rPosY - lPosY);
IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x;
IF auxInfo[vtx].y < minTxtrY THEN minTxtrY _ auxInfo[vtx].y;
ENDLOOP; 
minTxtrX _ Real.Float[Real.Fix[minTxtrX]];
minTxtrY _ Real.Float[Real.Fix[minTxtrY]];
FOR i: NAT IN [0..poly[polyNumber].nVtces) DO 		-- translate to origin
vtx: NAT _ poly[polyNumber].vtxPtr[i]; 
auxInfo[vtx].x _ auxInfo[vtx].x - minTxtrX; 
auxInfo[vtx].y _ auxInfo[vtx].y - minTxtrY; 
ENDLOOP; 
ENDLOOP; 
shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordType, $FromNormals];
args _ CONS[botRight.y, NIL];      args _ CONS[botRight.x, args];  
args _ CONS[topRight.y, args];     args _ CONS[topRight.x, args];  
args _ CONS[topLeft.y, args];       args _ CONS[topLeft.x, args];  
args _ CONS[botLeft.y, args];       args _ CONS[botLeft.x, args];   
args _ CONS[se.y, args];		args _ CONS[se.x, args];   
args _ CONS[ne.y, args];		args _ CONS[ne.x, args];   
args _ CONS[nw.y, args];		args _ CONS[nw.x, args];   
args _ CONS[sw.y, args];		args _ CONS[sw.x, args];   
shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordParams, args];
};

TextureFromAIS: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE, 
									 type: ATOM _ $Intensity, factor: REAL _ 1.0] 
					  RETURNS[texture: REF TextureMap] ~ {
width, height: INTEGER _ 1024;
renderMode: ATOM;
bufContext: REF Context;
FOR i: NAT IN [0..context.shapes.length) DO
IF context.shapes[i].shadingClass.texture # NIL THEN 
FOR txtrList: LORA _ context.shapes[i].shadingClass.texture, txtrList.rest 
	UNTIL txtrList = NIL  DO
WITH txtrList.first SELECT FROM
texture: REF TextureMap => IF texture.type = type AND Rope.Equal[
fileName,
NARROW[Atom.GetPropFromList[texture.props, $FileName], Rope.ROPE],
FALSE
]
THEN RETURN[texture];	-- if matched return texture map (may be summed)
ENDCASE; 
ENDLOOP;
ENDLOOP;
renderMode _ IF type = $Color OR type = $ColorAndTransmittance 
THEN $FullColor 
ELSE $Gray;
bufContext _ RenderWithPixels.GetContext[renderMode, width, height];
IF type = $ColorAndTransmittance THEN RenderWithPixels.AntiAliasing[bufContext];
bufContext.props _ context.props;						-- bring along working directory, etc.
[width, height] _ AISAnimation.GetAIS[				 -- load pixelbuffer with AIS bits
				context: bufContext, fileRoot: fileName, center: FALSE];
IF width > bufContext.viewPort.w OR height > bufContext.viewPort.h THEN {
bufContext.viewPort^ _ [0.0, 0.0, Real.Fix[width], Real.Fix[height]];
RenderWithPixels.AllocatePixelMemory[bufContext]; 			 -- get bigger pixel buffer
[width, height] _ AISAnimation.GetAIS[										 -- load it up
										context: bufContext, fileRoot: fileName, center: FALSE];
};
context.props _ Atom.RemPropFromList[context.props, $TempPixels];	-- GetAIS sideffect
bufContext.pixels.box.max.f _ width;    bufContext.pixels.box.max.s _ MAX[height, 1024];
bufContext.pixels.box.min.s _ MAX[0, 1024 - height];   -- getAIS pushes image up against max
texture _ NEW[TextureMap];		-- now, gin up TextureMap, using context.pixels
texture.pixels _ bufContext.pixels;
texture.type _ type;
texture.props _ Atom.PutPropOnList[texture.props, $FileName, fileName];
IF type = $Bump 
THEN texture.props _ Atom.PutPropOnList[texture.props, $BumpScale, NEW[REAL _ factor]];
};

SumMappedTexture: PUBLIC PROC[texture: REF TextureMap] ~ { 
SumOneLine: PROC[ line: ImagerSample.SampleBuffer, sumMap: REF SumSequence, y: NAT ] 
				 RETURNS[REF SumSequence] ~ {
IF sumMap = NIL THEN sumMap _ NEW[ SumSequence[box.max.s - box.min.s] ]; 
IF sumMap[y] = NIL THEN sumMap[y] _ NEW[ IntSequence[box.max.f] ];
sumMap[y].length _ box.max.f;

FOR x: NAT IN [0..line.length) DO
sumMap[y][x] _ INT32[line[x]]; 
IF x > 0 THEN {
sumMap[y][x] _ sumMap[y][x] + sumMap[y][x - 1]; --add area to left
IF y > 0 THEN  	-- x > 0 and y > 0:  add area below minus area below and to left
sumMap[y][x] _ sumMap[y][x] + (sumMap[y - 1][x] - sumMap[y - 1][x - 1]); 
}
ELSE IF y > 0 THEN  
sumMap[y][x] _ sumMap[y][x] + sumMap[y - 1][x]; 	--add area below
ENDLOOP;
RETURN[sumMap];
};

pixels: ImagerPixel.PixelMap;
box: Box;
scanLine: ImagerPixel.PixelBuffer;
summedTexture: REF SummedTexture;

IF texture = NIL THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "No texture to Sum"]];
WITH texture.pixels SELECT FROM
pxMap: ImagerPixel.PixelMap => pixels _ pxMap;
summed: REF SummedTexture => RETURN;			-- previously summed
ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture pixel type"]];
box _ pixels.box;
scanLine _ ImagerPixel.ObtainScratchPixels[
pixels.samplesPerPixel, box.max.f
];
summedTexture _ NEW[ SummedTexture[pixels.samplesPerPixel] ];

FOR y: NAT IN [box.min.s..box.max.s) DO  
ImagerPixel.GetPixels[self: pixels, pixels: scanLine, initIndex: [f: 0, s: y], count: box.max.f];
FOR i: NAT IN [0..pixels.samplesPerPixel) DO
summedTexture[i] _ SumOneLine[ scanLine[i], summedTexture[i], y-box.min.s ];  
ENDLOOP;
ENDLOOP;
texture.pixels _ summedTexture;
ImagerPixel.ReleaseScratchPixels[scanLine];
};

Init[];

END. 
���2��MappedAndSolidTextureImpl.mesa
Copyright c 1984, 1986 by Xerox Corporation.  All rights reserved.
Last Edited by: Crow, March 16, 1989 9:50:21 am PST
Internal Declarations
Renamed Procedures
Global Variables
Initialization
Register solid texture procs
Procedures for Controlling Texture
Procedures for VtxToRealSeq and computing color at a spot
PROC[dest: REF RealSequence, source: VertexInfo, data: REF ANY] RTRNS[REF RealSequence];
PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot]
Procedures for Auxiliary Clipping and Shading
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance];
PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANY _ NIL ] RETURNS[REF VertexInfo];
PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANY _ NIL ] RETURNS[REF VertexInfo];
Support Procedures for setting up texture for tiler

Procedures for Evaluating Texture at a Spot
PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot]
Increments in x and y
This section could be executed once per scan segment
txtrXIncr is vector for texture change to next pixel on scanline
txtrYIncr is vector for texture change to corresponding pixel on scanline above
scale txtrYIncr to projected length of texture increment along leading polygon edge
Find max size of texture offsets to adjacent pixels to estimate texture spot spread
Make sure area considered for surface orientation is large enough to avoid artifacts
(This is independent of texture orientation, extreme aspect ratios in texture images should be avoided)
perturb in x (rotate about y)
perturb in y (rotate about x)
Support Procedures for Summed Textures
lowerValue: REAL _ llVal + xPos * (lrVal - llVal); 	-- if we only had double precision!!
upperValue: REAL _ ulVal + xPos * (urVal - ulVal);
RETURN [ lowerValue + yPos * (upperValue - lowerValue) ];
Procedures for solid texture calculation
Regular array of dark spots
Wurlitzer colors, stripes in 3-d
Rotating stripes (barber pole)
Perlin's marble texture
returns band limited noise over R3.
map the unit interval into an "S shaped" cubic f[x] | f[0]=0, f'[0]=0, f[1]=1, f'[1]=0.
declare local variables.
initialize random gradient table
Force everything to be positive
ixyz _ the integer lattice point "just below" v (identifies the surrounding unit cube).
sxyz _ the vector difference v - ixyz biased with an S-Curve in each dimension.
txyz _ the complementary set of S-Curves in each dimension.
Add in each weighted component
Procedures for Recovering Texture Descriptions from streams
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance];
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance];
Procedures for Computing Texture Coordinates
Stretch as indicated by corner coordinates
Map from sphere to Cartesian coordinates 1st quadrant 0 - 1 range.
Map polar coordinates into quadrilateral given by sw, nw, ne, se 
Procedures for Reading and Preparing Texture Files
Previously used texture?
Create context and load texture into it
Ê*~��˜�Ihead™šœ
Ïmœ7™BJ™3J˜�šÏk	˜	Jšœ	žœ?˜MJšœ	žœ˜Jšœ	žœžœ
˜Jšœ	žœ˜-Jšœžœ&˜6Jšœžœ˜%Jšžœžœžœ˜3JšœžœZ˜mJšœžœ˜%Jšœžœ˜,Jšœ
žœ˜'Jšœžœ˜'Jšœžœªžœ˜ÏJšœžœ>˜TJšœžœ˜)Jšœžœ˜$Jšœžœ˜.Jšœžœ˜Jšœžœ˜ —J˜�—head2šœžœž˜(Iašžœ5žœt˜´Mšžœ˜J˜�Jšœž˜J˜�—head3šÏb™Mšœ	žœ˜%Jšžœžœžœ˜Mšœžœ˜Mšœžœ˜)MšœžœÏc˜;Jšœžœ˜0Mšœžœ! ˜@Jšœžœ ˜4Jšœžœ˜,Jšœžœ$˜<Jšœ
žœ˜.Idefaultšœžœ˜/Mšœžœ ˜5Mšœžœ˜+Mšœ
žœ˜-Mšœžœ˜1Mšœžœ˜!Mšœžœ˜Mšœ
žœ˜'Mšœžœ˜/Mšœžœ˜1Mšœžœ˜)M˜�Mšžœžœžœžœžœžœ˜M˜�Oš
Ïnœž	œžœ
žœžœ˜MOš¡œž	œ
žœžœžœžœžœ˜Sunitš¡œž	œ
žœžœžœžœ˜8Mšžœ
žœžœ˜3M˜——šŸ™Jš¡œžœ!žœžœžœžœžœ.˜uJš¡œžœ!žœžœžœžœž
œ&˜x—šŸ™Jšœžœ' ˜BOšœ,žœ '˜XMšœžœžœ˜8Mšœžœ 3˜Q—šŸ™š¡œžœ˜˜"J˜J˜J˜J˜J˜J˜J˜!J˜—˜LJš ™—J˜)J˜1J˜;J˜/J˜1J˜+J˜——šŸ"™"š¡œžœžœ˜<šžœžœ˜Jšžœ=˜Ašžœžœ2˜9šžœ˜Jšœžœ˜2Jšœžœ˜-Jšœžœ˜+Jšœžœ˜/Jšœžœ˜+Jšœžœ˜+Jšœžœ#˜6J˜———J˜—š
¡œžœ
žœžœžœ˜XJšœžœ@˜JJ˜=J˜4J˜šœ+ ˜EJšœžœ˜:J˜—J˜—š¡œžœžœ
žœžœžœ˜sJšœžœ@˜JJ˜JšœQ 	˜ZJ˜J˜�—š
¡œžœžœ
žœžœ˜QJšœžœ@˜JJš	œ	žœžœžœžœ˜7šžœžœžœžœžœžœžœž˜Ošžœžœž˜Jšœžœ,˜;Jšžœ˜—Jšžœ˜—J˜—š
¡œžœžœ
žœžœ˜MJšœžœ@˜JJšœžœ˜!J˜——šŸ9™9šŸ
œ#˜0Jšžœžœ)žœžœžœžœ™XJš	œžœžœžœžœ˜9Mšœžœžœ˜+Jšžœ
žœžœ"˜9Jš
žœžœžœžœžœ˜Ušžœ˜šžœ
 ,˜:J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœ# ˜9J˜J˜J˜J˜J˜J˜—šžœ 0˜<J˜J˜J˜J˜Jšœ" ˜8J˜J˜J˜J˜J˜J˜——Jšœ ˜Jšžœ˜J˜J˜�—šŸœ˜Mšžœ
žœžœžœ™EJšžœžœžœ#˜@šžœžœžœ˜1Jšžœ4˜8—J˜——šŸ-™-š¡œžœ ˜<Jšžœ
žœžœžœžœžœžœžœ™eJšœžœ˜#Jšœ	žœžœ%˜Dšžœžœž˜šœžœ˜"šžœžœžœžœ˜*Jšœ
žœžœ˜-Jšžœ˜—J˜$M˜—Mšœžœ!˜+Mšžœžœ;˜L—šžœžœžœžœ˜*šžœ˜	JšžœA˜Ešžœ˜Jšžœžœ˜0Jšžœžœ˜0Jšžœžœ˜0Jšžœžœ˜0J˜——Jšžœ˜—M˜*JšœCžœ˜nšžœžœ˜.JšžœžœD˜O—J˜NOšžœ
˜O˜—š¡
œžœ" ˜LJšžœžœžœžœžœžœžœžœ
™_Mšœžœžœ˜šžœžœž˜šœžœ˜Jšœ	žœžœ˜2Mš	œžœžœžœžœ˜:Mšœ! ˜2M˜—Mšœžœ9˜BMšžœžœ;˜L—O˜Ošžœ˜O˜—š¡
œžœ! *˜]Jšžœžœžœžœžœžœžœžœ
™_Jšœžœžœ˜Mšœžœžœ˜Mšœžœžœžœ˜'Mšœžœžœžœ˜,Mš	œžœžœžœžœ˜4Mš	œžœžœžœžœ˜9M˜M˜O˜Ošžœ˜O˜——šŸ3™3š¡
œžœžœžœžœ%žœ
˜lJšœžœžœ˜šžœžœžœžœžœž˜Lšžœžœžœ	 ˜BJšœ	žœ,žœ˜=Jšžœ˜	—Jšžœ˜—šžœžœ˜Jšœ(žœ˜-š	žœžœžœžœ ˜AJšœžœžœ˜%šžœ˜	JšžœE˜Išžœ˜Jšžœžœ˜,Jšžœžœ˜,Jšžœžœ˜,Jšžœžœ˜,J˜——Jšžœ˜	—šžœ"žœ ˜9š	žœžœžœžœ !˜HJšœžœžœ˜%Jšžœ žœžœ˜IJšžœ˜	—J˜š	žœžœžœžœ ˜7Jšœžœžœ˜%Jšžœžœ˜,Jšžœ˜	—J˜—šžœ"žœ ˜9š	žœžœžœžœ !˜HJšœžœžœ˜%Jšžœ žœžœ˜IJšžœ˜	—J˜š	žœžœžœžœ ˜7Jšœžœžœ˜%Jšžœžœ˜,Jšžœ˜—J˜J™�—J˜Xš	žœžœžœžœ (˜PJšœžœžœ˜%J˜J˜Jšžœ˜	—J˜—J˜——šŸ+™+š¡	œžœ˜Mšžœ
žœžœžœ™EJšœ	žœ˜!š	žœžœžœžœž˜Dšžœžœžœ #˜Dšœ	žœ ˜:šžœžœž˜šœžœ˜4Jšžœžœ:˜EJšžœ(˜,—šœžœžœ˜6šžœ˜Jšœžœžœ3˜?Jšœžœžœžœžœžœžœžœžœ˜DJ˜%J˜—Jšžœ+˜/—Jšžœžœ˜——šœžœ (˜KJ˜2—Jšžœžœ?˜P—Jšžœ˜—J˜—š¡
œžœžœ(žœ˜Nš
¡œžœ"žœžœžœ˜cOšœ
žœ!˜.Ošœžœ!˜/Ošœžœ˜-Jšžœžœ˜+Jšœ žœ˜/Jšžœžœ˜,J˜—O˜YOšœžœ˜Ošœžœ˜!Ošœžœ
˜O˜?O˜bšžœžœ˜š	œ
žœžœžœžœ˜#Ošœžœ˜'O˜(Ošžœ˜—˜Jšœžœ˜$Jšœžœ˜+šžœžœžœžœ˜Ošœ	žœ˜%Ošœ2 %œ3 ˜¤Ošžœ˜—Ošœ+ ˜EO˜—šœ  5˜UJšœžœ˜$Ošœ	žœ˜+Ošœžœ˜-šžœžœžœžœ˜Ošœ3 %œ3 ˜¥Ošžœ˜—Ošœ+ ˜EO˜—˜Ošœžœ˜'šžœžœžœžœ˜O˜(Ošžœ˜—O˜—Jšžœžœ9˜K—O˜(J˜—š
¡
œžœžœžœžœ˜OJšœ
žœ (˜RJšœ
žœ )˜QOšœžœ˜!šœžœ˜J™—Jš	œžœ	žœžœžœ˜NJš	œžœ	žœžœžœ˜OJšœžœ4˜>Jšžœ#žœ+žœ˜]Jšžœ#žœ+žœ˜^šžœžœ˜š	œ
žœžœžœžœ˜#OšœžœU˜dOšœžœ#˜4Ošžœ˜—˜Jšœžœ˜$šœžœ
˜J˜:J˜	—Ošœžœ  ˜Gšžœžœžœžœ˜šœ	žœ
˜O˜:O˜	—Ošœ5 "œ4 ˜¥Ošœžœ˜%Ošžœ˜—Ošœ+ ˜EO˜—˜Ošœ	žœU˜bJšœžœ˜$OšœžœU˜dOšœ
žœ˜Ošœžœ ˜Fšžœžœžœžœ˜Ošœ6 "œ4 œ˜§Ošœžœ˜%Ošžœ˜—Ošœ+ ˜EO˜—˜OšœžœU˜dOšžœžœ˜(Ošž˜Ošœžœ˜0šžœžœžœžœ˜Ošœžœ#˜4Ošžœ˜—O˜—Jšžœžœ9˜K—J˜—š
¡œžœžœžœžœ˜ROšœžœ˜!Ošœžœ˜š¡
œžœ˜Jšžœ"žœ+žœ˜\Jšžœ"žœ+žœ˜\O˜—Jšœžœžœžœ˜@Jšœ
žœ (˜RJšœ
žœ *˜RJšœ4™4šœ"žœ˜'Jšœ@™@—šœ9˜9JšœO™O—šœN˜NJšœS™S—šœžœ˜JšœN˜NJšœ˜—Jšœ#žœ˜.šžœžœ ,˜IJšœ:˜:JšœD˜DJšœB˜BJšœ˜JšœS™S—Jšœžœžœžœ˜;Jšœžœžœžœ˜;šœ6˜6J™TJ™g—Jšœ%˜%JšžœžœA˜^Jšœ%˜%Jšžœžœ@˜]šžœžœ˜˜Ošœ7ž˜<Ošœ˜Ošœ ,˜@OšœY˜YOšœ1 &˜WOšœ0˜0O˜
OšœY˜YOšœ1 #˜TOšœ0˜0Ošœ1 #˜TOšœ0˜0O˜
OšœY˜Yšœ˜OšœQ˜QOšœP˜PO˜—Ošœžœ˜!šœ-˜-O˜�—O™Jšœ?˜?Jšœ7˜7Jšœ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ'˜'J˜ O™Jšœ?˜?Jšœ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ'˜'J˜ J˜�Ošœ˜Ošœ˜Ošœ˜O˜—JšžœžœR˜d—J˜——šŸ&™&š¡œžœžœžœ˜JJšžœžœžœ˜+JšœžœH™XJšœžœ"™2Jšžœ3™9Jšœžœžœ˜:Jšœ( ˜<Jšœ!˜!Jšœ!˜!Jšœ˜Jšœ( ˜<Jšœ"˜"Jšœ"˜"Jšœ˜Jšœ, ˜IJšœ ˜ Jšœ"˜"Jšœ˜JšœA˜AJšœ˜—š¡
œžœ
žœžœžœžœ˜GJšœ
žœ˜$Jšœ
žœ˜!šžœžœžœ˜7Jšžœ˜J˜—šžœžœžœžœ˜=J˜3Jšžœ?˜EJ˜MJ˜—šžœžœžœ˜%J˜Jšžœ5˜;J˜—šžœ ˜+J˜Jšžœ5˜;J˜—J˜—š¡
œžœ
žœžœžœžœžœ˜iJšœžœž˜'J˜J˜&J˜J˜%Jšœ! ˜<J˜šœ(˜(Jšœ:˜:Jšœ9˜9Jšœ
˜
J˜—J˜—š¡	œžœžœžœžœ&žœ
žœ
žœ˜žOšœžœžœ˜.šžœžœ E˜ZOšœ 6˜FOšœI˜IO˜—O˜^O˜^O˜]O˜^O˜(J˜4J˜——šŸ(™(š
¡œžœžœžœ8žœ˜q˜0Jšœ"žœ*˜OJ˜—O˜—š
¡œžœžœžœžœ˜išœžœžœ˜"O˜6O˜—šžœžœ˜
Ošžœ˜Ošžœžœ:˜E—J˜—šŸœ˜Jš ™Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœw˜†J˜$J˜&J˜&J˜&J˜&J˜J˜�—šŸ	œ˜Jš  ™ Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœw˜†J˜$J˜GJ˜GJ˜GJ˜&J˜J˜�—šŸœ˜Jš ™Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœ ˜AJšœ
žœ˜$Jšœ
žœ˜$Jšœžœ $œ>˜‹Jšœžœ˜J˜$J˜#J˜*J˜&J˜.J˜&J˜—š¡œ˜Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœ˜Jšœžœ3˜>Jšœ
žœ<˜JJšœžœžœ˜0Jšœžœ˜ J˜�šœ	žœžœ˜$Jšžœ+˜3Jšžœžœ,˜4—Jšœžœ ˜<Jšœžœ ˜<Jšœžœ ˜>Jšœžœ ˜?J˜IJ˜IJ˜6J˜7Jšœžœ'˜6J˜ J˜ J˜ J˜—š¡	œ˜Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœ˜Jšœžœ3˜>Jšœ
žœ<˜JJšœžœ˜)Jšœžœ˜ J˜�šœ	žœžœ˜$Jšžœ+˜3Jšžœžœ,˜4—Jšœžœ ˜<Jšœžœ ˜<Jšœžœ ˜>Jšœžœ ˜?J˜IJ˜IJ˜6J˜7Jšœžœ'˜6J˜ J˜ J˜ J˜—šŸœ˜J™Jšœžœžœžœ ˜XJš	œžœžœžœžœ˜8Jšœžœh˜wJ˜$J˜+J˜&J˜&J˜&J˜—š¡œžœžœ
žœžœžœ˜4Jšœžœ˜
Jšœžœ˜šžœžœžœž˜J˜ J˜J˜
Jšžœ˜—Jšžœ˜J˜—J˜�Icodešœžœ	žœžœ˜'Q˜4	˜Q˜�—š
¡œžÏsžœžœžœ˜7™#J˜�š¡œžœ
žœžœžœžœ˜5Jš	œžœžœžœžœ˜Q˜/Qšœ
žœžœžœ˜!Qšœžœ$žœžœ˜OQšžœ˜J˜—š
¡œžœžœžœžœ˜(™WJ˜�Jšžœ˜—˜J˜�——™J˜Jšœžœ˜Jšœ3žœ˜8—™ šžœ
žœžœ˜Jšœ	žœ˜šžœžœžœ
ž˜J˜J˜(Jšžœ˜—J˜——™J˜J˜J˜—šœÏdœS™WJ˜J˜J˜—šœ£œ£œ*™OJ˜J˜J˜—šœ£œ7™;J˜
J˜
J˜
—Jšœ ˜"š	žœžœžœžœ <˜Tšžœžœ 2˜@J˜:J˜.J˜5J˜.J˜;J˜.J˜4J˜-Jšžœ˜—™J˜J˜_—Jšžœ˜—Jšžœ˜—J˜——šŸ;™;š¡œžœžœžœ˜:˜#O˜O˜Ošžœ"˜%O˜—˜#O˜O˜Ošžœ'˜*O˜—O˜—š¡œ˜#Jšžœ
žœžœžœžœžœžœžœ™eOšœžœžœžœ˜ šœ	žœ0˜<O˜*Ošœžœ˜O˜—O˜1šžœžœž˜˜QOšœ<ž˜=Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜.O˜—˜SOšœ
žœžœ˜/Ošœ
žœžœ˜.Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜*Ošœžœžœ˜*Ošœžœžœ˜.Ošœžœžœ˜(O˜—O˜šžœžœ˜%O˜-O˜——Ošœ& ˜<Ošœ%žœžœžœ˜^Ošžœ
˜O˜—š¡œ˜(Jšžœ
žœžœžœžœžœžœžœ™eOšœžœžœžœ˜ Ošœ&žœ˜:Ošžœ
˜O˜——šŸ,™,š¡œžœžœžœžœžœ˜…Jšœžœ˜Jšœžœ@˜JOšœ	žœžœ2˜TJšœžœ
žœ-˜KJšžœ
žœžœ
žœ˜Bšžœžœžœžœ˜GJšœ1 (˜YJ˜0J˜—J˜%šžœžœžœžœ˜%J˜%J˜%Ošžœ˜—Q˜KO˜—Oš¡œžœžœžœžœ,žœ˜˜OJšœžœ@˜JJšœ	žœžœ1˜SJšœžœžœžœ˜šžœ4žœžœ˜@JšžœM˜SJ˜TJ˜—Jšžœžœžœžœ%˜Hšžœ0žœ,žœ/žœ,˜¿JšžœžœF˜Q—šžœžœžœžœ˜)Jšœžœ˜!Jšœžœ ˜Jšœ: ˜LJ™*—Jšœ> ˜SJ˜?J˜>J˜?Jšœ8 ˜KJ˜6Jšžœ˜	—J˜$J˜NJ˜OJšœžœ
žœžœ˜BJšœžœžœ˜BJšœžœžœ˜CJšœžœžœ˜DJšœžœ-žœ˜[J˜JJ˜—š¡œžœžœžœ˜QJ˜—Jš
¡œžœžœžœžœ˜T˜ÅJšœžœ@˜JJšœ	žœžœ1˜SJšœžœžœžœ˜Ošœžœ!žœ˜@Jšœ
žœ	žœžœ˜#Jšœžœ /˜MJ˜�šžœ3žœžœ˜?JšžœM˜SJ˜TJ˜—Jšžœžœžœžœ%˜Hšžœ1žœžœ˜>Jšœ, *˜VJšœ* %˜OJ˜—šžœžœžœ!˜EJšžœžœH˜S—šžœžœ"˜(Jšžœžœ ˜Ušžœžœžœ!˜,Jšžœžœ ˜T——J˜�Jšžœžœžœ* ˜ZJšžœžœžœ*˜KJšžœžœžœ*˜KJšžœžœžœ+˜Kšžœ
žœžœžœ˜+Jšœžœ˜.Jšœ
žœ˜šžœžœžœž˜-J™BJšœžœ˜'JšœžœE˜UJšœžœ?˜Nšœ
žœ6˜DJ™A—Jšœžœ& &˜WJšœžœ&˜1Jšœžœ#  ˜NJšœžœ ˜+šžœ˜!Jšžœ*˜.šžœžœ˜&Jšžœ)˜-——Jšœ9 ˜MJšœ; ˜GJšœžœžœ˜5Jšœžœžœ˜5Jšžœžœžœ ˜Ošžœ˜Jšœžœ˜Jšžœžœ˜<Jšžœžœ˜?—Jšžœ˜—J˜$J˜NJšžœ* &˜Rš	žœžœžœžœž˜2Jšœžœ˜'šžœ ˜"Jšžœ&˜*—Jšžœ˜	—J˜J˜š	žœžœžœžœ ˜Lšžœžœžœ˜Jšœžœ˜'Jšžœžœ˜<Jšžœžœ˜<J˜—Jšžœ˜—š	žœžœžœžœ #˜QJšœžœ˜'Jšžœžœžœ-˜GJšžœ˜	—J˜*š	žœžœžœžœ "˜QJšœžœ˜'Jšœžœ9˜DJšœžœ9˜DJšœžœ;˜FJšœžœ<˜GJ˜;J˜:Jšžœžœ˜<Jšžœžœ˜<Jšžœ˜	—J˜*J˜*š	žœžœžœžœ ˜FJšœžœ˜'J˜,J˜,Jšžœ˜	—Jšžœ˜	—J˜PJšœžœ
žœžœ˜CJšœžœžœ˜CJšœžœ žœ˜CJšœžœ žœ˜DJšœžœžœ˜5Jšœžœžœ˜5Jšœžœžœ˜5Jšœžœžœ˜5J˜JJ˜——šŸ2™2š¡œžœžœ
žœžœžœžœžœ
žœ˜«Mšœžœ˜Jšœžœ˜Jšœžœ	˜J™šžœžœžœž˜+šžœ*žœžœ˜5š	žœžœ;žœžœž˜ešžœžœž˜šœ	žœžœžœ˜AJ˜	Jšžœ6žœ˜BJšž˜J˜Jšžœžœ 0˜F—Jšžœ˜	—Jšžœ˜——Jšžœ˜—J™'šœ
žœžœ˜?Mšžœ˜Mšžœ˜—M˜DJšžœžœ+˜PJšœ' &˜MJšœ+ !œ6žœ˜‰šžœžœ žœ˜IM˜EMšœ6 ˜PJšœ1 
œ<žœ˜J˜—MšœB ˜UJšœFžœ˜XJšœžœ %˜\Jšœ
žœ /˜KJ˜#J˜J˜Gšžœ˜Jšžœ?žœžœ˜W—J˜J˜�—š¡œžœžœ
žœ˜;š¡
œžœ+žœžœ	žœžœ˜wJšžœ
žœžœ
žœ(˜IJšžœ
žœžœ
žœ˜BJ˜J˜�šžœžœžœž˜!Jšœžœ˜šžœžœ˜Jšœ0 ˜Bšžœžœ @˜PJ˜I—J˜—šžœžœžœ˜Jšœ1 ˜A—Jšžœ˜—Jšžœ	˜J˜—J˜J˜	J˜"Ošœžœ˜!O˜�Ošžœžœžœžœ6˜Ršžœžœž˜J˜.Jšœžœžœ ˜;Jšžœžœ?˜P—J˜˜+M˜!M˜—Ošœžœ+˜>šžœžœžœžœ˜)O˜ašžœžœžœž˜,O˜NOšžœ˜—Ošžœ˜—J˜J˜+J˜J˜�——J˜Jšžœ˜—�…—����³��éÌ��