DIRECTORY Commander, CtBasic, CtFilter, CtMod, G2dBasic, G2dScan, G2dVector, G3dBasic, G3dDraw, G3dMatrix, G3dPlane, G3dPolygon, G3dVector, Imager, ImagerColor, ImagerSample, ImplicitConvolve, ImplicitDebug, ImplicitDesign, IO, Real, RealFns, Rope, ImagerPath, ImagerMaskCapture, ImagerTransformation, SF; ImplicitConvolveImpl: CEDAR PROGRAM IMPORTS Commander, CtBasic, CtFilter, CtMod, G2dBasic, G2dScan, G2dVector, G3dDraw, G3dPlane, G3dPolygon, G3dVector, Imager, ImagerSample, ImplicitDebug, ImplicitDesign, IO, Real, RealFns EXPORTS ImplicitConvolve ~ BEGIN Filter: TYPE ~ CtFilter.Filter; Box: TYPE ~ G3dBasic.Box; Pair: TYPE ~ G3dBasic.Pair; IntegerPair: TYPE ~ G3dBasic.IntegerPair; Quad: TYPE ~ G3dBasic.Quad; RealSequence: TYPE ~ G3dBasic.RealSequence; Triple: TYPE ~ G3dBasic.Triple; TripleSequence: TYPE ~ G3dBasic.TripleSequence; DrawType: TYPE ~ G3dDraw.DrawType; Matrix: TYPE ~ G3dMatrix.Matrix; Viewport: TYPE ~ G3dMatrix.Viewport; Context: TYPE ~ Imager.Context; RGB: TYPE ~ ImagerColor.RGB; SampleMap: TYPE ~ ImagerSample.SampleMap; PairList: TYPE ~ ImplicitConvolve.PairList; Primitive: TYPE ~ ImplicitConvolve.Primitive; PrimitiveList: TYPE ~ ImplicitConvolve.PrimitiveList; PrimitiveRep: TYPE ~ ImplicitConvolve.PrimitiveRep; ThicknessRep: TYPE ~ ImplicitConvolve.ThicknessRep; Twist: TYPE ~ ImplicitConvolve.Twist; ROPE: TYPE ~ Rope.ROPE; defaultFilter: Filter ¬ CtFilter.FilterFind["cubic"]; debug: BOOL ¬ FALSE; MakePrimitive: PUBLIC PROC [ points: TripleSequence, extent: REAL ¬ 0.1, thickness: REAL ¬ 0.0, intensity: REAL ¬ 1.0, intensities: RealSequence, indices: LIST OF INTEGER, res: IntegerPair ¬ [50, 50], color: RGB ¬ [1.0, 1.0, 1.0], twist: Twist ¬ []] RETURNS [p: Primitive] ~ { p ¬ NEW[PrimitiveRep ¬ [ points: points, extent: extent, intensity: intensity, intensities: intensities, res: res, color: color, twist: twist, nVertices: points.length, filter: defaultFilter ]]; IF thickness # 0 THEN p.thickness ¬ NEW[ThicknessRep ¬ [value: thickness]]; IF p.twist.p0 = p.twist.p1 THEN p.twist.tw0 ¬ p.twist.tw1 ¬ 0.0; IF indices # NIL THEN { nVerts: NAT ¬ 0; FOR l: LIST OF INTEGER ¬ indices, l.rest WHILE l # NIL DO nVerts ¬ nVerts+1; ENDLOOP; p.indices ¬ NEW[G2dBasic.NatSequenceRep[nVerts]]; FOR n: NAT IN [0..p.indices.length ¬ nVerts) DO p.indices[n] ¬ indices.first; indices ¬ indices.rest; ENDLOOP; p.nVertices ¬ nVerts; }; SetGeometry[p]; }; GetBounds: PROC [p: Primitive] RETURNS [b: Box] ~ { extentF: REAL ¬ p.extent*2.0*1.0*1.8; -- usual extent factor*fudge*sqrt(3) (if on diag) huge: REAL ~ Real.LargestNumber; b ¬ [[huge, huge, huge], [-huge, -huge, -huge]]; FOR n: NAT IN [0..p.nVertices) DO t: Triple ¬ GetPoint[p, n]; b.min ¬ [MIN[b.min.x, t.x], MIN[b.min.y, t.y], MIN[b.min.z, t.z]]; b.max ¬ [MAX[b.max.x, t.x], MAX[b.max.y, t.y], MAX[b.max.z, t.z]]; ENDLOOP; b.min ¬ G3dVector.Sub[b.min, [extentF, extentF, extentF]]; b.max ¬ G3dVector.Add[b.max, [extentF, extentF, extentF]]; }; GetPoint: PUBLIC PROC [p: Primitive, n: INTEGER] RETURNS [Triple] ~ { RETURN[p.points[IF p.indices # NIL THEN p.indices[n] ELSE n]]; }; SetGeometry: PUBLIC PROC [p: Primitive] ~ { NearnessAccelerator: PROC [p0, p1: Triple] RETURNS [v: Triple, w: REAL] ~ { v ¬ G3dVector.Sub[p1, p0]; IF (w ¬ v.x*v.x+v.y*v.y+v.z*v.z) # 0.0 THEN v ¬ [v.x/w, v.y/w, v.z/w]; }; SELECT p.nVertices FROM > 2 => { PairsFromProjection: PROC RETURNS [list: PairList] ~ { FOR n: NAT IN [0..p.nVertices) DO q: Triple ¬ GetPoint[p, n]; t: Triple ¬ G3dPlane.ProjectPointToPlane[q, pln]; pp: Pair ¬ [G3dVector.Dot[t, xAxis], G3dVector.Dot[t, yAxis]]; list ¬ CONS[[G3dVector.Dot[t, xAxis], G3dVector.Dot[t, yAxis]], list]; IF debug THEN { Out[IO.PutFR["q = (%g, %g, %g\n", IO.real[q.x], IO.real[q.y], IO.real[q.z]]]; Out[IO.PutFLR["t = (%g, %g, %g), p = (%g, %g)\n", LIST[IO.real[t.x], IO.real[t.y], IO.real[t.z], IO.real[pp.x], IO.real[pp.y]]]]; }; ENDLOOP; }; normal: Triple ¬ p.normal ¬ G3dPolygon.PolygonNormal[p.points, p.indices, TRUE]; pln: G3dPlane.Plane ¬ G3dPlane.FromPointAndNormal[GetPoint[p, 0], normal]; plane: Quad ¬ p.plane ¬ [pln.x, pln.y, pln.z, pln.w]; xAxis: Triple ¬ p.xAxis ¬ IF p.twist.tw0 # 0 OR p.twist.tw1 # 0 -- x axis in the plane THEN G3dVector.Unit[G3dVector.Sub[p.twist.p1, p.twist.p0]] ELSE G3dVector.Ortho[normal]; yAxis: Triple ¬ p.yAxis ¬ G3dVector.Cross[xAxis, normal]; origin: Triple ¬ G3dVector.Mul[normal, -p.plane.w]; -- origin of plane mm: G2dBasic.Box ¬ MinMaxOfPairs[PairsFromProjection[]]; -- 2d image bounds min: Pair ¬ G2dVector.Sub[mm.min, [2*p.extent, 2*p.extent]]; -- extent margin p.size ¬ G2dVector.Add[[4*p.extent, 4*p.extent], G2dVector.Sub[mm.max, mm.min]]; p.scale ¬ MIN[REAL[p.res.x]/p.size.x, REAL[p.res.y]/p.size.y]; p.origin ¬ G3dVector.Add[origin, G3dVector.Combine[xAxis, min.x, yAxis, min.y]]; IF debug THEN { PrintTriple["xAxis", p.xAxis]; PrintTriple["yAxis", p.yAxis]; Out[IO.PutFLR["size: (%g, %g), scale: %g, extent = %g\n", LIST[IO.real[p.size.x], IO.real[p.size.y], IO.real[p.scale], IO.real[p.extent]]]]; Out[IO.PutFR["min: (%g, %g)\n", IO.real[min.x], IO.real[min.y]]]; PrintTriple["origin", p.origin]; Out[IO.PutFLR["plane: (%g, %g, %g, %g)\n", LIST[IO.real[p.plane.x], IO.real[p.plane.y], IO.real[p.plane.z], IO.real[p.plane.w]]]]; }; IF p.twist.tw0 # 0 OR p.twist.tw1 # 0 THEN { p.p0x ¬ G3dVector.Add[ p.twist.p0, G3dVector.Project[G3dVector.Sub[p.origin, p.twist.p0], xAxis]]; p.p1x ¬ G3dVector.Add[p.p0x, G3dVector.Mul[xAxis, p.size.x]]; p.h ¬ G3dVector.Dot[yAxis, G3dVector.Sub[p.twist.p0, p.origin]]; [p.accV, p.accW] ¬ NearnessAccelerator[p.twist.p0, p.twist.p1]; }; }; 2 => { p0: Triple ¬ GetPoint[p, 0]; p1: Triple ¬ GetPoint[p, 1]; axis: Triple ¬ G3dVector.Unit[G3dVector.Sub[p1, p0]]; p.origin ¬ p0 ¬ G3dVector.ScaleRay[[p0, axis], -2.0*p.extent]; p1 ¬ G3dVector.ScaleRay[[p1, axis], 2.0*p.extent]; [p.accV, p.accW] ¬ NearnessAccelerator[p0, p1]; p.res.y ¬ 1; p.scale ¬ REAL[p.res.x]/G3dVector.Distance[p1, p0]; IF debug THEN Out[IO.PutFR["segment, scale: %g, extent = %g\n", IO.real[p.scale], IO.real[p.extent]]]; }; 1 => NULL; ENDCASE => ERROR; p.recipExtent ¬ 1.0/p.extent; p.filter.support ¬ p.filter.blur ¬ p.scale*p.extent; p.bounds ¬ GetBounds[p]; }; GetImageProjection: PUBLIC PROC [q: Triple, p: Primitive] RETURNS [xy: Pair, distance: REAL] ~ { IF p.twist.tw0 # 0 OR p.twist.tw1 # 0 THEN { a: REAL ¬ G3dVector.Dot[p.accV, G3dVector.Sub[q, p.twist.p0]]; tw: REAL ¬ p.twist.tw0+a*(p.twist.tw1-p.twist.tw0); n: Triple ¬ G3dVector.Combine[p.normal, RealFns.Cos[tw], p.yAxis, RealFns.Sin[tw]]; y: Triple ¬ G3dVector.Cross[p.xAxis, n]; o: Triple ¬ G3dVector.Add[p.twist.p0, G3dVector.Mul[p.accV, a*p.accW]]; plane: G3dPlane.Plane ¬ G3dPlane.FromPointAndNormal[o, n, TRUE]; qq: Triple ¬ G3dPlane.ProjectPointToPlane[q, plane]; xy.x ¬ G3dVector.Distance[o, p.p0x]; xy.y ¬ p.h+G3dVector.Dot[G3dVector.Sub[qq, o], y]; IF NOT p.distanceSet THEN p.distance ¬ q.x*plane.x+q.y*plane.y+q.z*plane.z+plane.w; } ELSE { v: Triple ¬ G3dVector.Sub[q, p.origin]; xy ¬ [G3dVector.Dot[v, p.xAxis], G3dVector.Dot[v, p.yAxis]]; IF NOT p.distanceSet THEN p.distance ¬ q.x*p.plane.x+q.y*p.plane.y+q.z*p.plane.z+p.plane.w; }; p.distanceSet ¬ TRUE; distance ¬ p.distance; xy ¬ G2dVector.Mul[xy, p.scale]; }; MinMaxOfPairs: PROC [pairs: PairList] RETURNS [b: G2dBasic.Box] ~ { b ¬ [[Real.LargestNumber, Real.LargestNumber], [-Real.LargestNumber, -Real.LargestNumber]]; FOR l: PairList ¬ pairs, l.rest WHILE l # NIL DO b.min ¬ [MIN[b.min.x, l.first.x], MIN[b.min.y, l.first.y]]; b.max ¬ [MAX[b.max.x, l.first.x], MAX[b.max.y, l.first.y]]; ENDLOOP; }; ValueOfPrimitive: PUBLIC PROC [q: Triple, p: Primitive] RETURNS [v: REAL ¬ 0.0] ~ { distance, intensity: REAL ¬ 1.0; IF NOT p.active THEN RETURN; IF q.x < p.bounds.min.x OR q.y < p.bounds.min.y OR q.z < p.bounds.min.z OR q.x > p.bounds.max.x OR q.y > p.bounds.max.y OR q.z > p.bounds.max.z THEN RETURN; SELECT p.nVertices FROM > 2 => IF p.image # NIL THEN { xy: Pair; ix, iy: NAT; [xy, distance] ¬ GetImageProjection[q, p]; IF xy.x < 0 OR xy.y < 0 OR xy.x >= p.res.x OR xy.y >= p.res.y THEN RETURN[0.0]; IF NOT p.interp THEN {ix ¬ Real.Round[xy.x]; iy ¬ Real.Round[xy.y]}; intensity ¬ (1.0/255.0)*(IF p.interp THEN CtMod.BiLerpReal[p.image, xy.x, xy.y] ELSE REAL[p.image[iy][ix]]); IF p.thickness # NIL AND p.thickness.array # NIL THEN distance ¬ MAX[0.0, ABS[distance]-p.thickness.value*(0.5/255.0)*(IF p.interp THEN CtMod.BiLerpReal[p.thickness.array, xy.x, xy.y] ELSE REAL[p.thickness.array[iy][ix]])]; }; 2 => IF p.buffer # NIL THEN { Lerp: PROC [buffer: ImagerSample.SampleBuffer, x: REAL] RETURNS [l: REAL] ~ { ix0, ix1: INT16 ¬ Real.Floor[x]; l ¬ buffer[ix0]; IF (ix1 ¬ ix0+1) < buffer.length THEN l ¬ l+(x-ix0)*(buffer[ix1]-l); }; a: REAL ¬ G3dVector.Dot[p.accV, G3dVector.Sub[q, p.origin]]; IF a NOT IN (0.0..1.0) THEN RETURN[0.0]; distance ¬ G3dVector.Distance[q, G3dVector.ScaleRay[[p.origin, p.accV], a*p.accW]]; intensity ¬ (1.0/255.0)*(IF p.interp THEN Lerp[p.buffer, 0.5+(p.res.x-1)*a] ELSE REAL[p.buffer[Real.Round[0.5+(p.res.x-1)*a]]]); }; 1 => distance ¬ G3dVector.Distance[q, GetPoint[p, 0]]; ENDCASE => ERROR; v ¬ p.valueScale*intensity*p.filter.evalProc[distance*p.recipExtent]; }; ValueOfPrimitives: PUBLIC PROC [q: Triple, primitives: PrimitiveList] RETURNS [v: REAL ¬ 0.0] ~ { FOR l: PrimitiveList ¬ primitives, l.rest WHILE l # NIL DO v ¬ v+ValueOfPrimitive[q, l.first]; ENDLOOP; }; ConvolvePrimitive: PUBLIC PROC [p: Primitive, filter: BOOL] ~ { IF p.nVertices <= 2 THEN p.res.y ¬ 1; SELECT p.nVertices FROM > 2 => { pairs: PairList ¬ NIL; FOR n: NAT IN [0..p.nVertices) DO -- ignore twist when making image v: Triple ¬ G3dVector.Sub[GetPoint[p, n], p.origin]; pairs ¬ CONS[[G3dVector.Dot[v, p.xAxis], G3dVector.Dot[v, p.yAxis]], pairs]; ENDLOOP; SetImagePath[p, pairs, filter]; }; 2 => { x0: REAL ¬ 2.0*p.scale*p.extent; x1: REAL ¬ REAL[p.res.x-1]-2.0*p.scale*p.extent; SetImagePath[p, LIST[[x0, 0], [x1, 0], [x1, 1], [x0, 1]], filter]; }; ENDCASE; }; GetTwisted: PUBLIC PROC [p: Primitive, q: Triple] RETURNS [Triple] ~ { IF p.twist.tw0 # 0 OR p.twist.tw1 # 0 THEN { a: REAL ¬ G3dVector.Dot[p.accV, G3dVector.Sub[q, p.twist.p0]]; tw: REAL ¬ p.twist.tw0+a*(p.twist.tw1-p.twist.tw0); o: Triple ¬ G3dVector.Add[p.twist.p0, G3dVector.Mul[p.accV, a*p.accW]]; D: Triple ¬ G3dVector.Sub[q, o]; n: Triple ¬ G3dVector.SameLength[D, G3dVector.Cross[D, p.xAxis]]; v: Triple ¬ G3dVector.Combine[D, RealFns.Cos[tw], n, -RealFns.Sin[tw]]; RETURN[G3dVector.Add[o, v]]; } ELSE RETURN[q]; }; NActivePrimitives: PUBLIC PROC [primitives: PrimitiveList] RETURNS [i: INTEGER ¬ 0] ~ { FOR l: PrimitiveList ¬ primitives, l.rest WHILE l # NIL DO IF l.first.active THEN i ¬ i+1; ENDLOOP; }; SetIDs: PUBLIC PROC [list: LIST OF PrimitiveList, startID: INTEGER ¬ 0] ~ { FOR l: LIST OF PrimitiveList ¬ list, l.rest WHILE l # NIL DO FOR p: PrimitiveList ¬ l.first, p.rest WHILE p # NIL DO IF p.first.id = -1 THEN {p.first.id ¬ startID; startID ¬ startID+1}; ENDLOOP; ENDLOOP; }; CombinePrimitives: PUBLIC PROC [list: LIST OF PrimitiveList] RETURNS [ret: PrimitiveList] ~ { FOR l: LIST OF PrimitiveList ¬ list, l.rest WHILE l # NIL DO FOR p: PrimitiveList ¬ l.first, p.rest WHILE p # NIL DO IF p.first # NIL THEN ret ¬ CONS[p.first, ret] ENDLOOP; ENDLOOP; }; InvalidateDistance: PUBLIC PROC [primitives: PrimitiveList] ~ { FOR l: PrimitiveList ¬ primitives, l.rest WHILE l # NIL DO l.first.distanceSet ¬ FALSE; ENDLOOP; }; InsideSolid: PUBLIC PROC [q: Triple, convexSolid: PrimitiveList] RETURNS [BOOL] ~ { FOR l: PrimitiveList ¬ convexSolid, l.rest WHILE l # NIL DO l.first.distance ¬ q.x*l.first.plane.x+q.y*l.first.plane.y+q.z*l.first.plane.z+l.first.plane.w; l.first.distanceSet ¬ TRUE; IF l.first.distance > 0.0 THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; ScalePairList: PROC [pairs: PairList, s: REAL] RETURNS [PairList] ~ { FOR l: PairList ¬ pairs, l.rest WHILE l # NIL DO l.first ¬ G2dVector.Mul[l.first, s]; ENDLOOP; RETURN[pairs]; }; LengthPairList: PROC [pairs: PairList] RETURNS [len: INTEGER ¬ 0] ~ { FOR l: PairList ¬ pairs, l.rest WHILE l # NIL DO len ¬ len+1; ENDLOOP; }; CopyPairList: PROC [pairs: PairList] RETURNS [ret: PairList ¬ NIL] ~ { tmp: PairList ¬ NIL; FOR l: PairList ¬ pairs, l.rest WHILE l # NIL DO tmp ¬ CONS[l.first, tmp]; ENDLOOP; FOR l: PairList ¬ tmp, l.rest WHILE l # NIL DO ret ¬ CONS[l.first, ret]; ENDLOOP; }; DisplayPrimitive: PUBLIC PROC [p: Primitive, map: SampleMap] ~ { ShowReflected: PROC [pa: CtBasic.PixelArray, offset: IntegerPair] ~ { temp: SampleMap ¬ CtBasic.SampleMapFromPixelArray[pa]; CtMod.ReflectH[temp]; ImagerSample.Transfer[map, temp, [offset.y, offset.x]]; }; IF p.image # NIL THEN ShowReflected[p.image, p.display]; IF p.thickness # NIL AND p.thickness.array # NIL THEN ShowReflected[p.thickness.array, p.thickness.display]; IF p.buffer # NIL THEN { line: SampleMap ¬ ImagerSample.NewSampleMap[[[0, 0], [1, p.buffer.length]], 8]; ImagerSample.PutSamples[line, [0, 0],, p.buffer, 0, p.buffer.length]; ImagerSample.Transfer[map, line, [p.display.y, p.display.x]]; }; }; MaxIntensity: PROC [intensities: RealSequence] RETURNS [max: REAL ¬ Real.MinusInfinity] ~ { IF intensities # NIL THEN FOR i: INTEGER IN [0..intensities.length) DO max ¬ MAX[intensities[i], max]; ENDLOOP; }; SetImagePath: PUBLIC PROC [p: Primitive, path: PairList, filter: BOOL ¬ TRUE] ~ { flat: BOOL ¬ TRUE; intensities: RealSequence ¬ G2dBasic.CopyRealSequence[p.intensities]; p.path ¬ ScalePairList[CopyPairList[path], p.scale]; p.valueScale ¬ p.intensity; IF intensities # NIL THEN { max: REAL ¬ MaxIntensity[intensities]; FOR i: INT IN [0..intensities.length) DO IF max # intensities[i] THEN flat ¬ FALSE; ENDLOOP; p.valueScale ¬ max*p.intensity; -- normalize FOR i: INT IN [0..intensities.length) DO intensities[i] ¬ intensities[i]/max; ENDLOOP; }; SELECT p.nVertices FROM > 2 => { MakeImage: PROC ~ { i: INTEGER ¬ -1; pairs: LIST OF G2dBasic.IntegerPair ¬ NIL; vertices: LIST OF G2dScan.Vertex ¬ NIL; ImagerSample.Clear[map]; FOR l: PairList ¬ p.path, l.rest WHILE l # NIL DO p: IntegerPair ¬ [Real.Round[l.first.x], Real.Round[l.first.y]]; IF flat THEN pairs ¬ CONS[p, pairs] ELSE vertices ¬ CONS[[p.x, p.y, Real.Round[255.0*intensities[i ¬ i+1]]], vertices]; ENDLOOP; IF flat THEN [] ¬ G2dScan.FlatShade[map, pairs, 255] ELSE [] ¬ G2dScan.GouraudShade[map, vertices]; }; box: ImagerSample.Box ¬ [[0, 0], [p.res.y, p.res.x]]; map: SampleMap ¬ ImagerSample.ObtainScratchMap[box, 8]; MakeImage[]; IF filter THEN { max: CARDINAL ¬ 0; buf: ImagerSample.SampleBuffer ¬ ImagerSample.NewSamples[p.res.x]; CtFilter.ResampleMap[map, box, box, p.filter]; -- unnormalized filtering & quantization FOR y: INT IN [0..p.res.y) DO ImagerSample.GetSamples[map, [y, 0],, buf, 0, p.res.x]; FOR x: INT IN [0..p.res.x) DO IF buf[x] > max THEN max ¬ buf[x]; ENDLOOP; ENDLOOP; IF (max ¬ max+1) IN (0..255) THEN { -- normalization of image: MakeImage[]; -- inexpensive to recompute p.filter.scale ¬ 255.0/REAL[max]; -- scale filtered image so its max pixel = 255 p.valueScale ¬ p.valueScale/p.filter.scale; -- compensate during ValueOfPrimitive CtFilter.ResampleMap[map, box, box, p.filter]; -- normalized filtering (expensive) p.filter.scale ¬ 1.0; -- reset }; }; p.image ¬ CtBasic.PixelArrayFromSampleMap[map]; ImagerSample.ReleaseScratchMap[map]; }; 2 => { pairs: PairList ¬ path; box: ImagerSample.Box ¬ [[0, 0], [1, p.res.x]]; map: SampleMap ¬ ImagerSample.ObtainScratchMap[box, 8]; line: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[p.res.x]; FOR x: INT IN [0..p.res.x) DO line.samples[x] ¬ IF x IN [pairs.first.x..pairs.rest.first.x] THEN 255 ELSE 0; ENDLOOP; IF NOT flat THEN FOR x: INT IN [0..p.res.x) DO a: REAL ¬ REAL[x]/REAL[p.res.x-1]; factor: REAL ¬ p.intensities[0]+a*(p.intensities[1]-p.intensities[0]); line.samples[x] ¬ Real.Round[factor*REAL[line.samples[x]]]; ENDLOOP; ImagerSample.PutSamples[map, [0, 0],, line, 0, p.res.x]; IF filter THEN CtFilter.ResampleMap[map, box, box, p.filter]; ImagerSample.GetSamples[map,,, p.buffer ¬ ImagerSample.NewSamples[p.res.x]]; ImagerSample.ReleaseScratchSamples[line]; ImagerSample.ReleaseScratchMap[map]; }; 1 => NULL; ENDCASE; }; DrawPrimitive: PUBLIC PROC [ p: Primitive, context: Context, view: Matrix, viewport: Viewport ¬ [], whatChanged: REF ¬ NIL, type: DrawType, log: IO.STREAM ¬ NIL] ~ { T: PROC [i: INT] RETURNS [t: Triple] ~ {t ¬ GetTwisted[p, GetPoint[p, i]]}; close: BOOL ¬ p.nVertices > 2; G3dDraw.SetColor[context, [p.color.R, p.color.G, p.color.B]]; IF p.twist.tw0 # 0 OR p.twist.tw1 # 0 THEN G3dDraw.DrawWithConnectedPoints[context, p.nVertices, T, view, viewport, close, type] ELSE G3dDraw.ConnectedPoints[context, p.points, view, viewport, close, type]; }; DrawPrimitives: PUBLIC PROC [ list: LIST OF Primitive, context: Context, view: Matrix, viewport: Viewport ¬ [], whatChanged: REF ¬ NIL, type: DrawType, log: IO.STREAM ¬ NIL] ~ { IF whatChanged = $IPOut THEN Imager.SetStrokeWidth[context, 3.0]; FOR l: LIST OF Primitive ¬ list, l.rest WHILE l # NIL DO IF l.first.active THEN DrawPrimitive[l.first, context, view, viewport, whatChanged, type, log]; ENDLOOP; }; Out: PROC [message: ROPE] ~ {ImplicitDebug.Write[message]}; PrintTriple: PROC [name: ROPE, t: Triple] ~ { Out[IO.PutFLR["%g: (%g, %g, %g)\n", LIST[IO.rope[name], IO.real[t.x], IO.real[t.y], IO.real[t.z]]]]; }; Commander.Register["ImplicitConvolve", ImplicitDesign.DesignCmd, "ImplicitConvolve [option | ?]"]; END... ! ImplicitConvolveImpl.mesa Copyright Ó 1990 by Xerox Corporation. All rights reserved. Bloomenthal, April 20, 1993 4:40 pm PDT Types and Constants Public Procedures t: Triple ¬ G3dVector.Sub[p.origin, G3dPlane.ProjectPointToPlane[q, pln]]; If obviate GetImageProjection scale: p.xAxis ¬ G3dVector.Mul[xAxis, p.scale]; p.yAxis ¬ G3dVector.Mul[yAxis, p.scale]; p.normal ¬ G3dVector.Mul[normal, p.scale]; If using major plane projection in 2d: p.major ¬ G3dPlane.GetMajorPlane[p.plane]; p.xAxis ¬ G3dPlane.ProjectPointToMajorPlane[p.xAxis, p.major]; p.yAxis ¬ G3dPlane.ProjectPointToMajorPlane[p.yAxis, p.major]; [Artwork node; type 'Artwork on' to command tool] If using projection to major plane: v: Pair ¬ G3dPlane.ProjectPointToMajorPlane[G3dVector.Sub[q, p.origin], p.major]; xy ¬ [G2dVector.Dot[v, p.Axis], G2dVector.Dot[v, p.yAxis]]; IF p.oneSided AND distance < 0.0 THEN RETURN[0.0]; Out[IO.PutFR["value = %g\n", IO.real[v]]]; Surface normals are presumed to point towards the outside of the convex solid. Shaded Display local storage to avoid accumulation of "normalization" if repeated calls to SetImagePath: Line Drawing Debug PrintMatrix: PROC [matrix: G3dMatrix.Matrix, name: ROPE ¬ NIL] ~ { IF out = NIL OR matrix = NIL THEN RETURN; IF name # NIL THEN IO.PutF[out, "%g\n", IO.rope[name]]; FOR i: NAT IN [0..3] DO Out[IO.PutFR[out, "%6.3f\t%6.3f\t%6.3f\t%6.3f\n", IO.real[matrix[i][0]], IO.real[matrix[i][1]], IO.real[matrix[i][2]], IO.real[matrix[i][3]]]]; ENDLOOP; }; PrintPrimitive: PROC [p: Primitive] ~ { Out[IO.PutFR[out, "nVertices = %g\n", IO.int[p.nVertices]]]; }; Start Code SetImagePath: PUBLIC PROC [p: Primitive, path: PairList, filter: BOOL ¬ TRUE] ~ { p.path ¬ ScalePairList[CopyPairList[path], p.scale]; SELECT p.nVertices FROM > 2 => { -- this is an incredibly awkward way to smooth shade a polygon, but . . . Trajectory: TYPE ~ ImagerPath.Trajectory; TrajectoryFromList: PROC [pairs: PairList] RETURNS [t: Trajectory] ~ { t ¬ ImagerPath.MoveTo[pairs.first]; FOR l: PairList ¬ pairs.rest, l.rest WHILE l # NIL DO t ¬ ImagerPath.LineTo[t, l.first]; ENDLOOP; }; Bit: PROC [context: Context] ~ { Imager.SetGray[context, 1]; Imager.MaskRectangle[context, [0, 0, 1, 1]]; -- grotesque: fix minimum Imager.MaskRectangle[context, [p.res.x-1, p.res.y-1, 1, 1]]; -- grotesque: fix maximum Imager.MaskFillTrajectory[context, TrajectoryFromList[p.path]]; }; bit: SampleMap ¬ ImagerSample.ZeroOrigin[ ImagerMaskCapture.CaptureBitmap[Bit, ImagerTransformation.Rotate[-90.0]]]; box: ImagerSample.Box ¬ [[0, 0], [p.res.y, p.res.x]]; map: SampleMap ¬ ImagerSample.ObtainScratchMap[box, 8]; size: ImagerSample.Vec ¬ SF.Min[ImagerSample.GetSize[map], ImagerSample.GetSize[bit]]; line: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f]; ImagerSample.Clear[map]; FOR y: NAT IN [1..size.s-1) DO -- avoid "fix" points ImagerSample.GetSamples[bit, [y, 0],, line, 0, size.f]; FOR x: NAT IN [0..size.f) DO line.samples[x] ¬ IF line.samples[x] = 1 THEN 255 ELSE 0; ENDLOOP; ImagerSample.FlipSamples[line]; -- ever more ugliness ImagerSample.PutSamples[map, [y, 0],, line, 0, size.f]; ENDLOOP; IF p.intensities # NIL THEN { same: BOOL ¬ TRUE; max: REAL ¬ p.intensities[0]; FOR i: INT IN [1..p.intensities.length) DO IF max # p.intensities[i] THEN {max ¬ MAX[max, p.intensities[i]]; same ¬ FALSE}; ENDLOOP; p.intensity ¬ max*p.intensity; -- normalize FOR i: INT IN [0..p.intensities.length) DO p.intensities[i]¬p.intensities[i]/max; ENDLOOP; }; IF filter THEN CtFilter.ResampleMap[map, box, box, p.filter]; p.image ¬ CtBasic.PixelArrayFromSampleMap[map]; ImagerSample.ReleaseScratchSamples[line]; ImagerSample.ReleaseScratchMap[map]; }; 2 => { pairs: PairList ¬ path; box: ImagerSample.Box ¬ [[0, 0], [1, p.res.x]]; map: SampleMap ¬ ImagerSample.ObtainScratchMap[box, 8]; line: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[p.res.x]; FOR x: INT IN [0..p.res.x) DO line.samples[x] ¬ IF x IN [pairs.first.x..pairs.rest.first.x] THEN 255 ELSE 0; ENDLOOP; IF p.intensities # NIL AND p.intensities.length = 2 THEN FOR x: INT IN [0..p.res.x) DO a: REAL ¬ REAL[x]/REAL[p.res.x-1]; factor: REAL ¬ p.intensities[1]+a*(p.intensities[1]-p.intensities[0]); line.samples[x] ¬ Real.Round[factor*REAL[line.samples[x]]]; ENDLOOP; ImagerSample.PutSamples[map, [0, 0],, line, 0, p.res.x]; IF filter THEN CtFilter.ResampleMap[map, box, box, p.filter]; ImagerSample.GetSamples[map,,, p.buffer ¬ ImagerSample.NewSamples[p.res.x]]; ImagerSample.ReleaseScratchSamples[line]; ImagerSample.ReleaseScratchMap[map]; }; 1 => NULL; ENDCASE; }; polygon => { o: Triple ¬ GetPoint[p, 0]; n: Triple ¬ p.normal ¬ G3dPolygon.PolygonNormal[p.points, p.indices, TRUE]; x: Triple ¬ G3dVector.Unit[G3dVector.Ortho[n]]; y: Triple ¬ G3dVector.Unit[G3dVector.Cross[n, x]]; m: Matrix ¬ p.xform ¬ G3dMatrix.Invert[G3dMatrix.MakeFromTriad[x, y, n, o]]; mm: G2dBasic.Box ¬ MinMaxOfPairs[PairsFromXformedPrimitive[p]]; dif: Pair ¬ G2dVector.Add[[4*p.extent, 4*p.extent], G2dVector.Sub[mm.max, mm.min]]; p.scale ¬ MIN[REAL[p.res.x]/dif.x, REAL[p.res.y]/dif.y]; p.plane ¬ G3dPlane.FromPointAndNormal[o, n]; p.xform ¬ m ¬ G3dMatrix.Scale[p.xform, p.scale, p.xform]; mm ¬ MinMaxOfPairs[PairsFromXformedPrimitive[p]]; p.move ¬ [2*p.filter.support-mm.min.x, 2*p.filter.support-mm.min.y]; p.xform ¬ G3dMatrix.Translate[m, [p.move.x, p.move.y, 0], m]; }; PairsFromXformedPrimitive: PROC [p: Primitive] RETURNS [pairs: PairList] ~ { FOR n: NAT IN [0..p.nVertices) DO pairs ¬ CONS[XYFrom3d[GetPoint[p, n], p], pairs]; ENDLOOP; }; XYFrom3d: PROC [point: Triple, p: Primitive] RETURNS [pp: Pair] ~ { pp.x ¬ point.x*p.xform[0][0]+point.y*p.xform[1][0]+point.z*p.xform[2][0]+p.xform[3][0]; pp.y ¬ point.x*p.xform[0][1]+point.y*p.xform[1][1]+point.z*p.xform[2][1]+p.xform[3][1]; }; scratchReals: ARRAY [0..9] OF RealSequence ¬ ALL[NIL]; ObtainReals: ENTRY PROC [strength: REAL] RETURNS [reals: RealSequence] ~ { FOR i: NAT IN [0..9] DO IF (reals ¬ scratchReals[i]) # NIL THEN {scratchReals[i] ¬ NIL; EXIT}; ENDLOOP; IF reals = NIL THEN reals ¬ NEW[G3dBasic.RealSequenceRep[4]]; reals[0] ¬ reals[1] ¬ reals[2] ¬ strength; reals[3] ¬ 0.0; reals.length ¬ 4; }; ReleaseReals: ENTRY PROC [reals: RealSequence] ~ { FOR i: NAT IN [0..9] DO IF scratchReals[i] = NIL THEN {scratchReals[i] ¬ reals; EXIT}; ENDLOOP; }; context: G3dRender.Context; ConvolvePrimitive: PUBLIC PROC [primitive: Primitive, intensity: REAL,res:IntegerPair¬[50,50]]~{ For polygons with differing vertex intensities FancyPatch: TYPE ~ G3dRenderWithPixels.FancyPatch; MakeMap: PROC RETURNS [SampleMap] ~ { patch: REF FancyPatch ¬ NEW[G3dRenderWithPixels.FancyPatch[primitive.points.length]]; IF context = NIL THEN context ¬ G3dRender.CreateUndisplayedContext[, res.x, res.y, gray]; G3dRender.SetAntiAliasing[context, TRUE]; G3dSortandDisplay.ValidateContext[context]; FOR n: NAT IN [0..primitive.points.length) DO pair: Pair ¬ G3dMatrix.TransformD[p.points[n], p.xform]; patch[n] ¬ [pair.x, res.y-pair.y, ObtainReals[p.strengths[n]]]; ENDLOOP; G3dRenderWithPixels.FillInConstantBackGround[context, [0, 0, 0], 0]; G3dRenderWithPixels.RealFancyTiler[context, patch]; FOR n: NAT IN [0..primitive.points.length) DO ReleaseReals[patch[n].val]; ENDLOOP; RETURN[context.pixels[0]]; }; m: SampleMap; SetGeometry[primitive, intensity, res]; m ¬ MakeMap[]; CtFilter.ResampleMap[map,ImagerSample.GetBox[m], ImagerSample.GetBox[m], primitive.filter]; primitive.image ¬ CtBasic.PixelArrayFromSampleMap[m]; }; ProjectToMajorPlane: PROC [p: Triple, plane: Quad, majorPlane: MajorPlane] RETURNS [Pair] ~ { normal: Triple ~ [plane.x, plane.y, plane.z]; distance: REAL ~ G3dVector.Dot[p, normal]+plane.w; RETURN[SELECT majorPlane FROM xy => G2dVector.Sub[[p.x, p.y], G3dVector.Mul[[plane.x, plane.y], distance]]; xz => G2dVector.Sub[[p.x, p.z], G3dVector.Mul[[plane.x, plane.z], distance]]; ENDCASE => G2dVector.Sub[[p.y, p.z], G3dVector.Mul[[plane.y, plane.z], distance]]]; }; IntersectPolygonWithRay: PROC RETURNS [list: LIST OF REAL] ~ { normal: Triple ¬ G3dVector.Ortho[p.twist.axis.axis]; plane: Quad ¬ G3dPlane.FromPointAndNormal[p.twist.axisbase, normal]; FOR n: NAT IN [0..p.nVertices) DO p0: Triple ¬ GetPoint[p, n]; p1: Triple ¬ GetPoint[p, (n+1) MOD p.nVertices]; d0: REAL ¬ G3dPlane.DistanceToPoint[p0, plane]; d1: REAL ¬ G3dPlane.DistanceToPoint[p1, plane]; IF d0*d1 < 0.0 THEN { a0: REAL ¬ ABS[d0]; a1: REAL ¬ ABS[d1]; t: Triple ¬ G3dVector.Combine[p0, a1/(a0+a1), p1, a0/(a+a1)]; }; ENDLOOP; }; Êeû•NewlineDelimiter ™™Jšœ<™J˜J˜—š  œœœ˜+š œœœœ˜KJ˜Jšœ%œ˜FJ˜—šœ ˜šœ˜š œ¡¡¡œ¡œ ¡œ¡œ˜6šœœœ˜!J˜J™JJ˜1Jšœ¡œ¡œ¡œ˜>Jšœ¡œ¡œ¡œ¡œ¡œ¡œ˜Fšœœ˜Jšœ"œ œ œ ˜M˜1Jš œœ œ œ œ œ˜O—J˜—Jšœ˜—J˜—JšœJœ˜PJšœA¡œ˜JJšœ¡œ¡œ˜5šœœœ¢˜VJšœ6˜:Jšœ˜—Jš œ¡œ¡œ¡œ ¡œ˜9Jšœ7¢˜IJšœ:¢˜LJšœ>¢˜NJšœ¡œ¡œG˜PJš œ¡œ¡œœœ˜>J˜Pšœœ˜Jšœ˜J˜˜9Jš œœœœœ˜R—Jšœ œœ˜AJ˜ ˜*Jš œœœœœ˜W—J˜—™$J™(J™(J™*—™&J™*J™>J™>—šœœœ˜,I artworkFigure• Interpress…;Interpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨ x jÄ¢‰wĦ%H ¢ ¨Ä¡¨ ¡ ¡™ x j¡¡¨¢¯“¢°“¢·“Ĉ'Ä·t™Ä’GÄœö[—Ä‹‡Älµ:—Ä5.)ÄÐët—ÄjÞMÄ6/—ÄÌ-•Ä·t—Ä‚qÄ`y:—Ĉ'Ä·t—˜ k x j x j¡¡¨Äé ¤ÄÌ-•Ä·t ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Äy"YÄ·t™Äy"YÄ€™QÄÌ-•Ä€™QÄÙ J¡”Ä@×/Ä€™QÄ@×/Ä·tÄÙ J¡”Ä@×/Ä_·=ÄÌ-•Ä_·=ÄÙ J¡”Äy"YÄ_·=Äy"YÄ·tÄÙ J¡”¡¸ k x j x j¡¡¨Äé ¤ÄjÞMÄ©Â[ ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“ļ߉ĩÂ[™Ä¼ß‰Änœ;ÄjÞMÄnœ;ÄÙ J¡”ÄzóXÄnœ;ÄzóXÄ©Â[ÄÙ J¡”ÄzóXÄba5ÄjÞMÄba5ÄÙ J¡”ļ߉Äba5ļ߉ĩÂ[ÄÙ J¡”¡¸ k x j x j¡¡¨Äé ¤Ä5.)ÄÐët ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“ÄSµAÄÐët™ÄSµAÄ’¢QÄ5.)Ä’¢QÄÙ J¡”ÄZ#EÄ’¢QÄZ#EÄÐëtÄÙ J¡”ÄZ#EÄmL=Ä5.)ÄmL=ÄÙ J¡”ÄSµAÄmL=ÄSµAÄÐëtÄÙ J¡”¡¸ k x j x j¡¡¨Ä頤ĂqÄ`y: ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Ä€øqÄ`y:™Ä€øqÄUeÄ‚qÄUeÄÙ J¡”ăqÄUeăqÄ`y:ÄÙ J¡”ăqÄ^H9Ä‚qÄ^H9ÄÙ J¡”Ä€øqÄ^H9Ä€øqÄ`y:ÄÙ J¡”¡¸ k x j x j¡¡¨Ä頤Ĉ'Ä·t ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Ä<ÛAÄ·t™Ä<ÛAÄ€™QĈ'Ä€™QÄÙ J¡”ÄAáEÄ€™QÄAáEÄ·tÄÙ J¡”ÄAáEÄ_·=Ĉ'Ä_·=ÄÙ J¡”Ä<ÛAÄ_·=Ä<ÛAÄ·tÄÙ J¡”¡¸ k x j x j¡¡¨Ä頤ĒGÄœö[ ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“ÄAmAÄœö[™ÄAmAÄfP;Ä’GÄfP;ÄÙ J¡”ÄF»EÄfP;ÄF»EÄœö[ÄÙ J¡”ÄF»EÄZí5Ä’GÄZí5ÄÙ J¡”ÄAmAÄZí5ÄAmAÄœö[ÄÙ J¡”¡¸ k x j x j¡¡¨Ä頤ċ‡Älµ: ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Ä>aAÄlµ:™Ä>aAÄ keÄ‹‡Ä keÄÙ J¡”ÄCEÄ keÄCEÄlµ:ÄÙ J¡”ÄCEÄjN9Ä‹‡ÄjN9ÄÙ J¡”Ä>aAÄjN9Ä>aAÄlµ:ÄÙ J¡”¡¸ kÄ¡¨ ¡ ¡™ x j¡¡¨¡¯“¢°“¢·“Ä5]FÄeWC™Äˆ'Ä·t—££¢¡ ´¡º kÄ¡¨ ¡ ¡™ x j¡¡¨¡¯“¢°“¢·“Ä5]FÄeWC™ÄÌ-•Ä·t—¥¥¢¡ ¡º k x j x j¡¡¨Äé ¤Ä5]FÄeWC ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“ÄXU^ÄeWC™ÄXU^Ä/-Ä5]FÄ/-ÄÙ J¡”Ä«†³Ä/-Ä«†³ÄeWCÄÙ J¡”Ä«†³Ä@¤+Ä5]FÄ@¤+ÄÙ J¡”ÄXU^Ä@¤+ÄXU^ÄeWCÄÙ J¡”¡¸ k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄL´OÄU™: ¢ ¥ ¨  Š¡²“Ámin– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äᠤğw}Äйk ¢ ¥ ¨  Š¡²“Ámax– k x j x j¡¡¨Äé ¤ÄgÄ÷Ì­ ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Ä9ÏAÄ÷Ì­™Ä9ÏAĆ]ÄgĆ]ÄÙ J¡”Ä>¥EĆ]Ä>¥EÄ÷Ì­ÄÙ J¡”Ä>¥EÄ)EÄgÄ)EÄÙ J¡”Ä9ÏAÄ)EÄ9ÏAÄ÷Ì­ÄÙ J¡”¡¸ k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄDMÄ'’ ¢ ¥ ¨  Š¡²“Áorigin = planeOrigin+– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄDMÄ   ¢ ¥ ¨  Š¡²“Á – k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äᠤě#Ä‘Æo ¢ ¥ ¨  Š¡²“Á(min.x-2*filterWidth)*xAxis– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄShMÄBÏ5 ¢ ¥ ¨  Š¡²“Á(min.y-2*filterWidth)*yAxis– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄÛä•Ä8s ¢ ¥ ¨  Š¡²“Á(max.x+2*filterWidth)*xAxis– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄyARÄm×: ¢ ¥ ¨  Š¡²“Á(max.y+2*filterWidth)*yAxis– k x jÅXeroxÅXC1-2-2Å Helvetica£¡ “ •  —¡¡¨Äá ¤ÄÛä•ÄAá! ¢ ¥ ¨  Š¡²“Á planeOrigin+– k x j x j¡¡¨Äé ¤ÄnzMÄi65 ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“ÄÃK‰Äi65™ÄÃK‰Äµ}[ÄnzMĵ}[ÄÙ J¡”ÄXĵ}[ÄXÄi65ÄÙ J¡”ÄXÄŒIGÄnzMÄŒIGÄÙ J¡”ÄÃK‰ÄŒIGÄÃK‰Äi65ÄÙ J¡”¡¸ kÄ¡¨ ¡ ¡™ x j¡¡¨¡¯“¢°“¢·“Ä‹‡Älµ:™Ä/E"ăíD—¥¥¢¡ ¡º kÄ¡¨ ¡ ¡™ x j¡¡¨¡¯“¢°“¢·“ÄjÞMÄ6/™Ä‰ÄG‘%—££¢¡ ´¡º k x j x j¡¡¨Äé ¤ÄÍì•Ä89 ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k¡¡¨¢¯“¢°“¢·“Äz-YÄ89™Äz-YÄQ­ÄÍì•ÄQ­ÄÙ J¡”ÄAd/ÄQ­ÄAd/Ä89ÄÙ J¡”ÄAd/Ä^‹1ÄÍì•Ä^‹1ÄÙ J¡”Äz-YÄ^‹1Äz-YÄ89ÄÙ J¡”¡¸ kÄ¡¨ ¡ ¡™ x j¡¡¨¡¯“¢°“¢·“ÄnzMĵ}[™ÄJ˜3J˜/J˜ Jšœ œ%˜3šœ˜ Jšœ2œœ˜X—J˜—Jšœœ˜ Jšœœ˜—Jšœ¡œ ˜J˜4J˜J˜J˜—š œ œ˜9Jšœœ˜"Jšœ˜šœœ˜%šœ˜JšÐglœœ7˜>Jšœœ£œ˜3J˜SJšœ$¡œ˜(Jšœ<£œ ˜GJšœ:œ˜@J˜4J˜$J˜2Jšœœœ:˜SJ˜—šœ˜™#J™QJ™;—J˜'J˜<šœœ˜JšœB˜F—J˜——Jšœœ˜J˜J˜ J˜J˜—š  œœœ˜CJš œ¡œ¡œ¡œ¡œ¡œ˜[šœœœ˜0Jšœ œœ˜;Jšœ œœ˜;Jšœ˜—J˜J˜—š  œœœœœ ˜SJšœœ˜ Jšœœ œœ˜šœœœ˜JJš œœœœœ˜Q—šœ ˜šœœ œœ˜J˜ Jšœœ˜ J˜*Jšœ œœœ™2Jš œ œ œœœœ˜OJšœ œ0˜Dšœœ ˜$Jšœ&˜*Jšœœ˜—š œœœœ˜5šœ œœ*œ ˜LJšœ0˜4Jšœœ˜'——J˜—šœœ œœ˜š  œœ(œœœ˜MJšœ œ˜ J˜Jšœœ˜DJ˜—Jš£œœ5˜Jšœœ£œ˜3Jšœ<£œ ˜GJšÐgnœ˜ Jšœ!Ïgœ¥œ ˜AJšœ¥œ(˜GJšœ˜J˜—Jšœœ˜—J˜J˜—š œ œœœ ˜WJšœ'œœœœœ œ˜cJ˜J˜—š  œœœœœœ ˜Kš œœœœœ˜<šœ$œœ˜7Jšœœ-˜DJšœ˜—Jšœ˜—J˜J˜—š  œ œœœœ˜]š œœœœœ˜<šœ$œœ˜7Jšœ œœœ˜.Jšœ˜—Jšœ˜—J˜J˜—š œœœ ˜?šœ'œœ˜:Jšœœ˜Jšœ˜—J˜J˜—š   œœœ)œœ˜SJ™Nšœ(œœ˜;J˜_Jšœœ˜Jšœœœœ˜-Jšœ˜—Jšœœ˜ J˜J˜—š  œœœœ˜EJš œœœœ&œ˜^Jšœ˜J˜J˜—š œœœœ ˜EJš œœœœœ˜FJ˜J˜—š  œœœœ˜FJšœœ˜Jš œœœœœœ˜SJš œœœœœœ˜QJ˜——–16.0 24 .div 1 1 textColoršŸ™š œœœ#˜@š  œœ2˜EJ˜6J˜Jšœ¡œ¡œ˜7J˜—Jšœ œœ#˜8šœœœ˜0Jšœ7˜;—šœ œœ˜J˜OJ•StartOfExpansionÒ[pixelMap: ImagerPixelMap.PixelMap, s: INTEGER, f: INTEGER, buffer: ImagerSample.SampleBuffer, bi: NAT, bj: NAT, count: NAT, srcFunc: PrincOps.SrcFunc _ null, dstFunc: PrincOps.DstFunc _ null]šœE˜EJšœ=˜=J˜—J˜J˜—š  œ¡¡œ ¡œ ¡¡œ¡¡œ¡œ¡œ¡œ˜[š œœœœœœ˜FJšœœ˜Jšœ˜—J˜J˜—codeš  œ¡¡¡œ¡œ ¡œ¡œ¡¡œ¡œ¡œ˜QJšœœœ˜JšœY™YJ˜EM˜4J˜šœœœ˜Jšœœ˜&Jš¡œ¡¡¡œ¡Ðks¡œ¡œ¡œ¡¡œ¡œ¡œ¦œ˜\Jšœ ¢ ˜,Jš¡œ¡¡¡œ¡¡œ$¡œ˜VJ˜—šœ ˜šœ˜š  œœ˜Jšœœ˜Jšœœœœ˜*Jšœ œœœ˜'Jšœ˜šœœœ˜1J˜@šœ˜Jšœ œ ˜Jšœ œ?˜S—Jšœ˜—šœ˜Jšœ(˜,Jšœ*˜.—J˜—Jšœ¡œ¡œ ¡œ ˜5Jšœ¡œ¡œ"¡œ˜7J˜ šœœ˜Jšœœ˜J˜BJ• CharProps&/(Postfix0.0 24 .div 1 1 textColoršœ¡œ¡œ¡œ ¡¢Ðcs¢ §¢ §¢§¢ ˜Wšœœœ˜J–Š[pixelMap: ImagerPixelMap.PixelMap, s: INTEGER, f: INTEGER, buffer: ImagerSample.SampleBuffer, bi: NAT, bj: NAT, count: NAT]˜7Jš œœœœœœ˜IJšœ˜—– &$Postfix0.0 24 .div 1 1 textColoršœœ œ¢˜>J– & Postfix0.0 24 .div 1 1 textColoršœ ¢˜(J– &".Postfix0.0 24 .div 1 1 textColoršœœ¢.˜PJ– &,%Postfix0.0 24 .div 1 1 textColoršœ,¢%˜QJ– &/#Postfix0.0 24 .div 1 1 textColoršœ/¢ §¢˜RJ– &Postfix0.0 24 .div 1 1 textColoršœ¢˜Jšœ˜—J˜—J˜/Jšœ$˜$M˜—šœ˜J˜Jšœ¡œ¡œ¡œ ˜/Jšœ¡œ¡œ"¡œ˜7MšœM˜Mš ¡œ¡¡¡œ ¡˜Mš œ¡œ¡œœ%œœ˜NMšœ˜—šœœ˜šœœœ˜Jš£œœœœ ˜"Jšœœ£œ%˜FMšœ¡œ¡œœ˜;Jšœ˜——J˜8Jšœœ/˜=J˜LM˜)Jšœ$˜$Mšœ˜—Jšœœ˜ Mšœ˜—M˜——–16.0 24 .div 1 1 textColoršŸ ™ š  œœœ˜Jšœ ˜ Jšœ˜Jšœ ˜ J˜Jšœ œœ˜Jšœ˜Jšœœœœ˜Jšœ˜Jš œœœœ3˜KJšœœ˜J˜=šœœ˜%š˜Jš œ(¡œ ¡œ¡œ¡œ¡œ˜U—š˜J˜H——J˜J™—š œœœ˜Jšœœœ ˜Jšœ˜Jšœ ˜ J˜Jšœ œœ˜Jšœ˜Jšœœœœ˜Jšœ˜Jšœœ%˜Aš œœœœœ˜8šœ˜JšœI˜M—Jšœ˜—Jšœ˜——–16.0 24 .div 1 1 textColoršŸ™š œœ œ#˜;J˜—š  œœœ˜-šœ¡œ¡œ¡œ˜#Jš œœ ¡œ ¡œ ¡œ˜@—J˜J˜—š  œœ"œœ™BJš œœœ œœœ™)Jš œœœœœ ™7šœœœ™™1Jšœœœœ™]—Jšœ™—J™J™—š œœ™'Jšœ&œ™J™4J™Dšœœœ™!J™Jšœœ™0Jšœœ(™0Jšœœ(™0šœ œ™Jšœœœ™Jšœœœ™J™=J™—Jšœ™—J™J™—J˜——…—CþË