<<>> <> <> <> <> DIRECTORY Controls, Draw2d, FileNames, G2dGraph, Imager, ImagerFont, IO, RealFns, Rope, ViewerClasses; G2dGraphImpl: CEDAR PROGRAM IMPORTS Controls, Draw2d, FileNames, Imager, ImagerFont, IO, RealFns, Rope EXPORTS G2dGraph ~ BEGIN <> ROPE: TYPE ~ Rope.ROPE; GraphData: TYPE ~ G2dGraph.GraphData; GraphProc: TYPE ~ G2dGraph.GraphProc; Function: TYPE ~ G2dGraph.Function; <> Bump: PUBLIC GraphProc ~ { xx: REAL ~ 1.0-ABS[1.0-x-x]; RETURN[xx*xx]; }; Gauss: PUBLIC GraphProc ~ { mean: REAL ~ 0.5; pi: REAL ~ 3.1415926535; standardDeviation: REAL ~ 0.16; factor1: REAL ~ 1.0/(2.0*standardDeviation*standardDeviation); factor2: REAL ~ 1.0/(standardDeviation*RealFns.SqRt[2.0*pi]); xx: REAL ~ x-mean; RETURN[factor2*RealFns.Exp[-xx*xx*factor1]]; }; Poisson: PUBLIC GraphProc ~ {RETURN[g.a*x*RealFns.Exp[-g.a*x]]}; Power: PUBLIC GraphProc ~ {RETURN[RealFns.Power[x, g.a]]}; Sin: PUBLIC GraphProc ~ {RETURN[RealFns.Power[0.5+0.5*RealFns.Sin[x], g.a]]}; Ln: PUBLIC GraphProc ~ {RETURN[RealFns.Ln[x]]}; Log: PUBLIC GraphProc ~ {RETURN[RealFns.Log[g.a, x]]}; Exp: PUBLIC GraphProc ~ { a: REAL ¬ IF g.a = 1.0 THEN 1.01 ELSE g.a; RETURN[(RealFns.Power[a, x]-1.0)/(a-1.0)]; }; Perlin: PUBLIC GraphProc ~ { IF x <= 0.0 OR g.a <= 0.0 THEN RETURN[0.0]; RETURN[RealFns.Power[0.5, RealFns.Log[0.5, x]*RealFns.Log[0.5, g.a]]]; }; WyvillData: TYPE ~ RECORD [R2, R4, R6: REAL]; Wyvill: PUBLIC GraphProc ~ { w: REF WyvillData ¬ NARROW[g.clientData]; r2: REAL ~ x*x; r4: REAL ~ r2*r2; r6: REAL ~ r2*r4; RETURN[w.R6*r6+w.R4*r4+w.R2*r2+1.0]; }; SlowInOut: PUBLIC GraphProc ~ { RETURN[(3.0-2.0*x)*x*x]; }; Compress: PUBLIC GraphProc ~ { RETURN[2.0*RealFns.Exp[-x*x*0.5]]; }; Pavicic: PUBLIC GraphProc ~ { pi: REAL ~ 3.1415926535; a: REAL ~ 0.5823997; -- a=(1-v2)/(v1-v2), v1=pi/3, v2=(pi/2)-(2/pi) t1: REAL ¬ a*(1.0-x); t2: REAL ¬ (1.0-a)*(1.0+RealFns.Cos[pi*x])/2.0; RETURN[t1+t2]; }; PerspZ: PUBLIC GraphProc ~ { d: REAL ¬ 10.0; den: REAL ¬ x/d+1.0; y ¬ IF ABS[den] < 0.0001 THEN 100.0 ELSE x/den; }; SquashStretch: PUBLIC GraphProc ~ { angle: REAL ¬ 2.0*3.14159256535*x; dot: REAL ¬ RealFns.Cos[angle]; y ¬ ((1-g.a)*0.5)*RealFns.Cos[3.1415926535*dot]+((g.a+1)/2.0); }; Ease: PUBLIC GraphProc ~ { F: PROC [x, v: REAL] RETURNS [REAL] ~ { RETURN[1.0/(1.0+RealFns.Exp[-v*x])]; }; fMinus1: REAL ¬ F[-1.0, g.a]; RETURN[(F[(2.0*x)-1.0, g.a] - fMinus1)/(F[1.0, g.a] - fMinus1)]; }; <> functions: LIST OF Function ¬ NIL; RegisterFunction: PUBLIC PROC [function: Function] ~ { functions ¬ CONS[function, functions]; }; GetFunctions: PUBLIC PROC RETURNS [LIST OF Function] ~ {RETURN[functions]}; GetFunction: PUBLIC PROC [name: ROPE] RETURNS [f: Function] ~ { FOR l: LIST OF Function ¬ GetFunctions[], l.rest WHILE l # NIL DO IF Rope.Equal[l.first.name, name, FALSE] THEN RETURN[l.first]; ENDLOOP; }; <> Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ outerData: Controls.OuterData ¬ NIL, ticks: BOOL ¬ TRUE, graphData: GraphData ¬ NIL, graphProc: GraphProc ¬ NIL]; GraphFunction: PUBLIC PROC [ function: Function, xMin: REAL ¬ 0.0, xMax: REAL ¬ 1.0, scale: REAL ¬ 1.0, a: REAL ¬ 1.0, clientData: REF ANY ¬ NIL] RETURNS [error: ROPE] ~ { g: GraphData ¬ NEW[G2dGraph.GraphDataRep ¬ [a: a, xMin: xMin, xMax: xMax, scale: scale, clientData: clientData]]; d: Data ¬ NEW[DataRep ¬ [graphData: g, graphProc: function.proc]]; IF ABS[xMax-xMin] < 0.0001 THEN RETURN["x range too small"]; IF d.graphProc = Wyvill THEN { w: REF WyvillData ¬ g.clientData ¬ NEW[WyvillData]; aa: REAL ~ -4.0/9.0; bb: REAL ~ 17.0/9.0; cc: REAL ~ -22.0/9.0; IF g.a = 0.0 THEN g.a ¬ 1.0; w.R2 ¬ 1.0/(d.graphData.a*d.graphData.a); w.R4 ¬ w.R2*w.R2; w.R6 ¬ w.R2*w.R4; w.R2 ¬ cc*w.R2; w.R4 ¬ bb*w.R4; w.R6 ¬ aa*w.R6; }; d.outerData ¬ Controls.OuterViewer[ name: IO.PutFLR["2dGraph %g -xMin %g -xMax %g -scale %g -a %g", LIST[IO.rope[function.name], IO.real[xMin], IO.real[xMax], IO.real[scale], IO.real[a]]], graphicsHeight: 200, drawProc: DrawProc, typescriptHeight: 18, buttons: LIST[ Controls.ClickButton["IPOut", IPOut, d], Controls.ClickButton["Ticks: On", TicksToggle, d]], clientData: d]; }; TicksToggle: Controls.ClickProc ~ { d: Data ¬ NARROW[clientData]; Controls.ButtonToggle[d.outerData, d.ticks ¬ NOT d.ticks, "Ticks: On", "Ticks: Off"]; }; IPOut: Controls.ClickProc ~ { d: Data ¬ NARROW[clientData]; r: Rope.ROPE ¬ Controls.TypescriptReadFileName[d.outerData.typescript]; IF r # NIL THEN Draw2d.IPOut[FileNames.ResolveRelativePath[r], DrawProc, clientData]; }; DrawProc: Controls.DrawProc ~ { RealFunction: TYPE ~ PROC [v: REAL] RETURNS [REAL]; XToScreen: RealFunction ~ {RETURN[leftMargin+v*xToScreen]}; YToScreen: RealFunction ~ {RETURN[botMargin+v*yToScreen]}; XFromScreen: RealFunction ~ {RETURN[g.xMin+(v-leftMargin)*xFromScreen]}; YFromScreen: RealFunction ~ {RETURN[(v-botMargin)*yFromScreen]}; tick: INTEGER ~ 5; leftMargin: INTEGER ~ 27; botMargin: INTEGER ~ 17; d: Data ~ NARROW[clientData]; g: GraphData ¬ d.graphData; graphics: ViewerClasses.Viewer ¬ d.outerData.graphics; xToScreen: REAL ~ (graphics.cw-leftMargin)/(g.xMax-g.xMin); yToScreen: REAL ~ (graphics.ch-botMargin)*g.scale; xFromScreen: REAL ~ 1.0/xToScreen; yFromScreen: REAL ~ 1.0/yToScreen; IF d.ticks THEN { Imager.SetFont[context, ImagerFont.Find["xerox/tiogafonts/helvetica7"]]; Imager.MaskRectangle[context, [leftMargin, botMargin, graphics.cw, 1]]; Imager.MaskRectangle[context, [leftMargin, botMargin, 1, graphics.ch]]; FOR n: NAT ¬ botMargin, n+20 WHILE n < graphics.ch DO -- left edge ticks y: REAL ~ YFromScreen[n]; Imager.MaskRectangle[context, [leftMargin-tick, n, tick, 1]]; Draw2d.Label[context, [3, n], IO.PutFR1["%3.2f", IO.real[y]]]; ENDLOOP; FOR n: NAT ¬ leftMargin, n+40 WHILE n < graphics.cw DO -- bottom edge ticks x: REAL ~ XFromScreen[n]; Imager.MaskRectangle[context, [n, botMargin-tick, 1, tick]]; Draw2d.Label[context, [n, 3], IO.PutFR1["%3.2f", IO.real[x]]]; ENDLOOP; }; IF whatChanged # $IPOut THEN FOR i: NAT IN [leftMargin..graphics.cw] DO y: REAL ~ d.graphProc[XFromScreen[i], g ! UNCAUGHT => LOOP]; < LOOP]; >> Imager.MaskRectangle[context, [i, YToScreen[y], 1, 1]]; ENDLOOP ELSE { y0: REAL ¬ YToScreen[d.graphProc[XFromScreen[leftMargin], g]]; Imager.SetStrokeWidth[context, 2.0]; FOR i: NAT ¬ leftMargin+1, i+3 WHILE i <= graphics.cw DO y: REAL ~ YToScreen[d.graphProc[XFromScreen[i], g ! UNCAUGHT => LOOP]]; Imager.MaskVector[context, [i, y0], [i+3, y]]; y0 ¬ y; ENDLOOP; }; }; <> functions ¬ LIST[ ["Bump", Bump, "\t\ta second order curve"], ["Gauss", Gauss, "\t\tthe normal distribution curve"], ["Poisson", Poisson, "\t\tthe poisson curve, with a the scalar"], ["Power", Power, "\t\ta power curve, with a the exponent"], ["Sin", Sin, "\t\t\ta sin curve raised to the a power"], ["Ln", Ln, "\t\t\tnatural logarithm"], ["Log", Log, "\t\t\tlogarithm to the base a"], ["Exp", Exp, "\t\t\tan exponential function"], ["Perlin", Perlin, "\t\tKen's curve, with a the scalar"], ["Wyvill", Wyvill, "\t\tWyvill's soft function"], ["SlowInOut", SlowInOut, "\tslow in and out curve"], ["Compress", Compress, "\t\tlike Gauss"], ["Pavicic", Pavicic, "\t\tPavicic's radial weighting filter"], ["PerspZ", PerspZ, "\t\ttransformed perspective Z"], ["SquashStretch", SquashStretch, "squash/stretch ala JB + BW"], ["Ease", Ease, "\t\t\tGlassner's slow-in-out, a > 0: slower easing"] ]; END.