<> <> <> <<>> DIRECTORY Convert USING [RopeFromReal], Graph USING [CaretIndex, CaretSpec, Entity, EntityColor, EntityList, FontIndex, JustifX, JustifY, LastEntityColor, Mark, NullVec, ROPE, SegmentData, SegmentDataList, Text, Texts, UnitLineWidth, ValueList, XY], GraphCarets USING [Resume, Suspend], GraphConvert USING [CharRopeFromMark, RopeOfPlace, RopeOfSlope], GraphPrivate USING [BackgroundIndex, BWLineState, ColorMode, Fonts, GraphHandle, LineStateRec, MaxStep, OutputType, PaintInfo, StepCount, systemColor], GraphUtil USING [Almost, BackgroundColor, BlinkMsg, DataBounds, FontHeight, ForegroundColor, FullName, RealToScreen, RealToScreenI, RealVecToScreenVec], Imager USING [Box, ClipRectangle, Color, Context, DoSaveAll, MaskRectangle, MaskStroke, MaskVector, PathProc, Rectangle, SetColor, SetFont, SetStrokeEnd, SetStrokeJoint, SetStrokeWidth, SetXY, ShowRope, TranslateT, VEC], ImagerBackdoor USING [GetBounds], ImagerFont USING [Extents, Font, RopeBoundingBox], Real USING [FixI, RoundI, RoundLI], RealFns USING [Log, Power, SqRt], Rope USING [Cat, Concat, IsEmpty]; GraphDraw: CEDAR PROGRAM IMPORTS Convert, Imager, ImagerBackdoor, ImagerFont, GraphCarets, GraphConvert, GraphPrivate, GraphUtil, Real, RealFns, Rope EXPORTS GraphPrivate = { OPEN Graph, GraphPrivate, GraphUtil; <> TextHeightFudge: REAL = 8.0; TextWidthFudge: REAL = 8.0; LabelWidth: REAL = 60.0; LabelHeight: REAL = 16; <> bwLineState: BWLineState _ NEW[LineStateRec _ []]; <> DrawProc: TYPE = PROC [context: Imager.Context _ NIL, handle: GraphHandle _ NIL]; <> Draw: PUBLIC PROC [context: Imager.Context _ NIL, handle: GraphHandle _ NIL] = { IF context # NIL AND handle # NIL THEN IF handle.paintInfo # NIL THEN { OPEN handle; autoRepaint: BOOL _ paintInfo.item = all AND (NOT paintInfo.clear) AND chart.dirty; <> GraphCarets.Suspend[]; SELECT handle.paintInfo.item FROM all => DrawGraph[context, handle]; graphEntity => DrawEntity[context, handle]; graphText => DrawText[context, handle]; tails => DrawTails[context, handle]; rectangle => DrawGraph[context, handle]; target => DrawTargets[context, handle]; grid => DrawTicks[context, handle, handle.paintInfo.xy, FALSE]; -- false: no label. < DrawCrossSegments[context, handle, c];>> < DrawXhairPlaces[context, handle, x];>> ENDCASE; <> GraphCarets.Resume[]; }; }; -- Draw <> DrawGraph: DrawProc = { OPEN handle; info: PaintInfo _ paintInfo; <> <> rect: Imager.Rectangle _ IF info.output = interpress THEN [0.0, -518.16, 670.56, 518.16] ELSE ImagerBackdoor.GetBounds[context]; <> axesRectSave: Imager.Rectangle; scaleSave: ARRAY XY OF REAL; wtlSave: BOOL; IF info.output = interpress THEN {axesRectSave _ axesRect; scaleSave _ scale; wtlSave _ windowTooLow;} ELSE IF info.clear THEN { -- happens in screen painting only. clearProc: PROC = { context.SetColor[BackgroundColor[backgroundIndex, screen]]; context.MaskRectangle[rect]; }; context.DoSaveAll[clearProc]; }; axesRect _ [rect.x + 50.0, rect.y + rect.h*0.05, rect.w - 100.0, rect.h*0.9]; windowTooLow _ (info.output = screen) AND rect.h < 440; IF NOT windowTooLow THEN { -- leave space for title/legend. legendEtc: REAL _ FontHeight[imagerFonts[paintInfo.output][0]]*7 + TextHeightFudge; axesRect.y _ axesRect.y + legendEtc; axesRect.h _ axesRect.h - rect.h*0.1 - legendEtc; }; IF axesRect.w > 0.0 AND axesRect.h > 0.0 THEN { wholePicture: PROC = { ScaleAxes[handle]; DrawAxes[context, handle]; DrawTicks[context, handle, x, TRUE]; DrawTicks[context, handle, y, TRUE]; DrawTexts[context, handle]; IF NOT windowTooLow THEN DrawLegends[context, handle]; IF info.output = interpress THEN PrintCaretsInfo[context, handle]; IF graph.target[x].on THEN {paintInfo.xy _ x; DrawTargets[context, handle]}; IF graph.target[y].on THEN {paintInfo.xy _ y; DrawTargets[context, handle]}; DrawEntities[context, handle]; }; context.DoSaveAll[wholePicture]; }; <> IF info.output = interpress THEN {axesRect _ axesRectSave; scale _ scaleSave; windowTooLow _ wtlSave}; <> FOR i: CaretIndex IN CaretIndex DO chart.caretState[i].x _ RealToScreenI[handle, graph.caret[i].place.x, x]; chart.caretState[i].y _ RealToScreenI[handle, graph.caret[i].place.y, y]; ENDLOOP; }; -- DrawGraph DrawTargets: DrawProc = { OPEN handle; <> targetProc: PROC = { info: PaintInfo _ handle.paintInfo; rect: Imager.Rectangle; tcolor: Imager.Color _ IF info.action = erase THEN BackgroundColor[backgroundIndex, screen] ELSE systemColor[graph.target[info.xy].colorIndex]; <> IF info.xy = x THEN { OPEN graph.target[x]; IF value < realRect.x OR value > realRect.x + realRect.w THEN RETURN; rect _ [x: RealToScreen[handle, value, x] - UnitLineWidth*width*0.5, y: axesRect.y+1.0, w: UnitLineWidth*width, h: axesRect.h-2.0]; } ELSE { OPEN graph.target[y]; IF value < realRect.y OR value > realRect.y + realRect.h THEN RETURN; rect _ [x: axesRect.x+1.0, y: RealToScreen[handle, value, y] - UnitLineWidth*width*0.5, w: axesRect.w-2.0, h: UnitLineWidth*width]; }; context.SetColor[tcolor]; context.MaskRectangle[rect]; }; context.DoSaveAll[targetProc]; }; -- DrawTargets DrawRope: PROC[context: Imager.Context, text: ROPE, x0, y0: REAL, justifX: JustifX, justifY: JustifY, color: Imager.Color, font: ImagerFont.Font] = { <> <> IF ~text.IsEmpty[] THEN { showTextProc: PROC = { context.TranslateT[[x0, y0]]; context.SetXY[[xmin, ymin]]; context.SetColor[color]; context.SetFont[font]; context.ShowRope[text]; }; -- showTextProc extents: ImagerFont.Extents; xmin, ymin, width, height: REAL; extents _ ImagerFont.RopeBoundingBox[font: font, rope: text]; width _ extents.rightExtent - extents.leftExtent; height _ extents.descent + extents.ascent; xmin_ SELECT justifX FROM right => - width - extents.leftExtent, center => - width/2.0 - extents.leftExtent, ENDCASE => - extents.leftExtent; ymin_ SELECT justifY FROM top => - height + extents.descent, center => - height/2.0 + extents.descent, ENDCASE => extents.descent; context.DoSaveAll[showTextProc]; <> }; }; -- DrawRope DrawTexts: DrawProc = { OPEN handle, handle.axesRect; FOR tl: Texts _ graph.texts, tl.rest UNTIL tl = NIL DO t: Text _ tl.first; DrawRope[context, t.text, t.place.x * w + x, t.place.y * h + y, t.justifX, t.justifY, systemColor[t.colorIndex], imagerFonts[paintInfo.output][t.fontIndex]]; ENDLOOP; }; -- DrawTexts DrawText: DrawProc = { OPEN handle, handle.axesRect; t: Text _ paintInfo.text; DrawRope[context, t.text, t.place.x * w + x, t.place.y * h + y, t.justifX, t.justifY, GetTextColor[handle, t], imagerFonts[paintInfo.output][t.fontIndex]]; }; -- DrawText ScaleAxes: PROC[handle: GraphHandle] = { OPEN handle; <> <> nDivX, nDivY: INT; invalidDiv: BOOL _ graph.division[x] < 1 OR graph.division[y] < 1; invalidBounds: BOOL _ (graph.bounds.xmin >= graph.bounds.xmax) OR (graph.bounds.ymin >= graph.bounds.ymax); ScaleAxis: PROC [minDataValue, maxDataValue, axisSize: REAL, nDiv: INT] RETURNS [min, range, step, factor: REAL] = { max: REAL; step _ IF graph.auto[divisions] THEN FindStepSize[maxDataValue - minDataValue, nDiv] ELSE (maxDataValue - minDataValue)/nDiv; IF graph.auto[bounds] THEN { min _ AlignEnd[minDataValue, step, FALSE]; max _ AlignEnd[maxDataValue, step, TRUE]; IF Almost[min, max] THEN {max _ max + 50.0; min _ min - 50.0}; } ELSE { min _ minDataValue; max _ maxDataValue; }; range _ max - min; factor _ axisSize/range; }; -- ScaleAxis <<>> <> IF invalidBounds THEN { graph.bounds _ DataBounds[graph.entityList]; IF NOT graph.auto[bounds] THEN BlinkMsg["Bounds invalid. (xmin >= xmax or ymin >= ymax.)"]; }; <> IF graph.auto[divisions] OR invalidDiv THEN { -- 5/8 = 0.625 nDivX _ MAX[1, MIN[10, Real.RoundLI[axesRect.w/LabelWidth*0.625]]]; nDivY _ MAX[1, MIN[10, Real.RoundLI[axesRect.h/LabelHeight*0.625]]]; IF NOT graph.auto[divisions] THEN BlinkMsg["Divisions must be >= 2"]; } ELSE { nDivX _ graph.division[x]; nDivY _ graph.division[y]; }; <> [realRect.x, realRect.w, step[x], scale[x]] _ ScaleAxis[ graph.bounds.xmin, graph.bounds.xmax, axesRect.w, nDivX]; [realRect.y, realRect.h, step[y], scale[y]] _ ScaleAxis[ graph.bounds.ymin, graph.bounds.ymax, axesRect.h, nDivY]; }; -- ScaleAxes FindStepSize: PROC [range: REAL, nSteps: CARDINAL] RETURNS [step: REAL] = { logRange: REAL; logMext, minStep: REAL; exp: INTEGER; steps: ARRAY [0..6) OF REAL = [0.2, 0.5, 1.0, 2.0, 5.0, 10.0]; IF Almost[range, 0.0] THEN range _ 100.0; logRange _ RealFns.Log[10.0, range]; exp _ Real.FixI[logRange]; logMext _ logRange - exp; IF logRange < 0.0 THEN { exp _ exp - 1; logMext _ logMext + 1.0}; minStep _ RealFns.Power[10.0, logMext]/nSteps; FOR i: CARDINAL IN [0..5) DO step _ steps[i]; IF step > minStep OR Almost[step, minStep] THEN EXIT ENDLOOP; IF exp >= 0 THEN THROUGH [1..exp] DO step _ step*10.0 ENDLOOP ELSE THROUGH [1..-exp] DO step _ step/10.0 ENDLOOP; step _ MAX[1.0, step]; }; -- FindStepSize AlignEnd: PROC [e, step: REAL, roundUp: BOOL] RETURNS [ae: REAL] = { absE: REAL = ABS[e]; nSteps: INTEGER; xend: REAL; IF e = 0.0 THEN RETURN[0.0]; IF e < 0.0 THEN roundUp _ ~roundUp; nSteps _ Real.RoundLI[absE/step - 0.5]; xend _ step*nSteps; IF Almost[nSteps, absE/step] THEN ae _ xend ELSE IF roundUp THEN ae _ IF xend >= absE THEN xend ELSE step*(nSteps + 1) ELSE ae _ IF xend <= absE THEN xend ELSE step*(nSteps - 1); IF e < 0.0 THEN ae _ -ae; }; -- AlignEnd DrawAxes: DrawProc = { <> axesProc: PROC = { OPEN handle; color: Imager.Color _ ForegroundColor[backgroundIndex, paintInfo.output]; <> rect: Imager.Rectangle _ axesRect; border: Imager.PathProc = { OPEN rect; xmax: REAL = x + w; ymax: REAL = y + h; moveTo[[x, y]]; lineTo[[x, ymax]]; lineTo[[xmax, ymax]]; lineTo[[xmax, y]]; }; context.SetColor[color]; context.SetStrokeEnd[square]; context.SetStrokeJoint[round]; context.SetStrokeWidth[UnitLineWidth*2.0]; context.MaskStroke[path: border, closed: TRUE]; }; context.DoSaveAll[axesProc]; }; -- DrawAxes DrawTicks: PROC[context: Imager.Context, handle: GraphHandle, xyChoice: XY _ x, drawLabel: BOOL _ FALSE] = { OPEN handle; <> <> <<>> ticksProc: PROC = { tickCount: CARDINAL; -- max tick count, starting from 0. tickLen, textPlace, tick: REAL; gridsOn: BOOL _ graph.grids[xyChoice]; stdColor: Imager.Color _ GetStandardColor[handle]; stdFont: ImagerFont.Font _ imagerFonts[paintInfo.output][0]; context.SetColor[stdColor]; context.SetStrokeEnd[butt]; context.SetStrokeWidth[UnitLineWidth]; IF xyChoice = x THEN { -- x ticks (on horizontal axis) tickLen _ IF gridsOn THEN axesRect.h ELSE axesRect.h/50.0; tickCount _ Real.RoundI[realRect.w/step[x]]; textPlace _ axesRect.y - TextHeightFudge; FOR i: CARDINAL IN [0..tickCount] DO tick _ step[x]*scale[x]*i; IF drawLabel THEN DrawRope[context, Convert.RopeFromReal[step[x]*i + realRect.x], axesRect.x + tick, textPlace, center, top, stdColor, stdFont]; IF i # 0 AND i # tickCount THEN { xPos: REAL = axesRect.x + tick; yStart: REAL _ axesRect.y; yEnd: REAL _ axesRect.y + tickLen; Tick: Imager.PathProc = {moveTo[[xPos, yStart]]; lineTo[[xPos, yEnd]]}; context.MaskStroke[path: Tick, closed: FALSE]; IF NOT gridsOn THEN { yStart _ axesRect.y + axesRect.h; yEnd _ yStart - tickLen; context.MaskStroke[path: Tick, closed: FALSE]; }; }; ENDLOOP; } ELSE { tickLen _ IF gridsOn THEN axesRect.w ELSE axesRect.w/50.0; tickCount _ Real.RoundI[realRect.h/step[y]]; textPlace _ axesRect.x - TextWidthFudge; <> FOR i: CARDINAL IN [0..tickCount] DO tick _ step[y]*scale[y]*i; IF drawLabel THEN DrawRope[context, Convert.RopeFromReal[step[y]*i + realRect.y], textPlace, axesRect.y + tick, right, center, stdColor, stdFont]; IF i # 0 AND i # tickCount THEN { yPos: REAL = axesRect.y + tick; xStart: REAL _ axesRect.x; xEnd: REAL _ axesRect.x + tickLen; Tick: Imager.PathProc = {moveTo[[xStart, yPos]]; lineTo[[xEnd, yPos]]}; context.MaskStroke[path: Tick, closed: FALSE]; IF NOT gridsOn THEN { xStart _ axesRect.x + axesRect.w; xEnd _ xStart - tickLen; context.MaskStroke[path: Tick, closed: FALSE]; }; }; ENDLOOP; }; }; context.DoSaveAll[ticksProc]; }; -- DrawTicks DrawLegends: DrawProc = { OPEN handle; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO entity: Entity _ el.first; IF entity.colorIndex = 0 THEN -- auto color selection for white curves. lastEntityColor _ entity.colorIndex _ ((lastEntityColor + 1) MOD LastEntityColor) + 1; DrawLegend[context, handle, entity]; ENDLOOP; }; -- DrawLegends DrawLegend: PROC [context: Imager.Context, handle: GraphHandle, entity: Entity] = { OPEN handle; legendProc: PROC = { lColor: Imager.Color _ GetEntityColor[handle, entity]; lFont: ImagerFont.Font _ imagerFonts[paintInfo.output][0]; fontHeight: REAL _ FontHeight[lFont]; MaxEntriesPerColumn: CARDINAL = 6; lineLength: REAL _ IF entity.colorIndex IN [8..9] THEN 31.0 ELSE 34.0; lx1: REAL _ IF entity.colorIndex <= MaxEntriesPerColumn THEN axesRect.x ELSE axesRect.x + axesRect.w*0.5; lx2: REAL _ lx1 + 34.0; tx: REAL _ lx2 + TextWidthFudge; row: CARDINAL _ entity.colorIndex MOD MaxEntriesPerColumn; ty: REAL _ axesRect.y - (TextHeightFudge + TextHeightFudge) - fontHeight*(IF row = 0 THEN 7 ELSE (row + 1)); ly: REAL _ ty + fontHeight*0.5; context.SetColor[BackgroundColor[backgroundIndex, paintInfo.output]]; context.MaskRectangle[[lx1, ty, axesRect.w*0.5, fontHeight]]; context.ClipRectangle[[lx1, ty, axesRect.w*0.5, fontHeight]]; IF paintInfo.action = paint THEN { context.SetStrokeEnd[butt]; context.SetStrokeWidth[UnitLineWidth*entity.width]; context.SetColor[lColor]; IF colorMode = color THEN context.MaskVector[[lx1, ly], [lx2, ly]] ELSE { bwLineState^ _ []; MaskLineSeg[context, [tx, ly], [lx2, ly], entity.colorIndex, bwLineState] }; DrawRope[context, FullName[entity], tx, ty, left, bottom, lColor, lFont]; }; }; -- proc IF NOT windowTooLow THEN context.DoSaveAll[legendProc]; }; -- DrawLegend DrawEntities: DrawProc = { OPEN handle; <> <> IF graph.entityList # NIL THEN { entitiesProc: PROC = { context.ClipRectangle[axesRect]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO entity: Entity _ el.first; IF entity = NIL THEN LOOP; -- error ?! IF entity.colorIndex = 0 THEN -- auto color selection for white curves. lastEntityColor _ entity.colorIndex _ ((lastEntityColor + 1) MOD LastEntityColor) + 1; IF handle.colorMode = bw THEN DrawBWEntity[context, handle, entity] ELSE DrawColorEntity[context, handle, entity]; ENDLOOP; }; -- entitiesProc context.DoSaveAll[entitiesProc]; }; }; -- DrawEntities DrawBWEntity: PROC [context: Imager.Context, handle: GraphHandle, entity: Entity] = { OPEN handle; <> oneCurve: PROC = { first: BOOL _ TRUE; lastVec, newVec: Imager.VEC; -- places on screen. xseg: SegmentDataList _ entity.group.x.segments; eColor: Imager.Color _ GetEntityColor[handle, entity]; bwLineState^ _ []; context.SetColor[eColor]; context.TranslateT[[axesRect.x, axesRect.y]]; context.SetStrokeJoint[mitered]; context.SetStrokeEnd[butt]; context.SetStrokeWidth[UnitLineWidth*entity.width]; FOR yseg: SegmentDataList _ entity.segments, yseg.rest UNTIL yseg = NIL DO newVec _ [(xseg.first.end-realRect.x)*scale[x], (yseg.first.end-realRect.y)*scale[y]]; IF first THEN first _ FALSE ELSE MaskLineSeg[context, lastVec, newVec, entity.colorIndex, bwLineState]; IF entity.mark # none THEN { eFont: ImagerFont.Font _ imagerFonts[paintInfo.output][IF entity.mark > cross THEN 0 ELSE LAST[FontIndex]]; DrawRope[context, GraphConvert.CharRopeFromMark[entity.mark], newVec.x, newVec.y, center, center, eColor, eFont]; }; lastVec _ newVec; xseg _ xseg.rest; ENDLOOP; }; context.DoSaveAll[oneCurve]; }; -- DrawBWEntity DrawColorEntity: PROC[context: Imager.Context, handle: GraphHandle, entity: Entity] = { OPEN handle; <> oneCurve: PROC = { xseg: SegmentDataList; curve: Imager.PathProc = { first: BOOL _ TRUE; xseg _ entity.group.x.segments; FOR yseg: SegmentDataList _ entity.segments, yseg.rest UNTIL yseg = NIL DO vec: Imager.VEC _ [(xseg.first.end-realRect.x)*scale[x], (yseg.first.end-realRect.y)*scale[y]]; IF first THEN {moveTo[vec]; first _ FALSE} ELSE lineTo[vec]; xseg _ xseg.rest; ENDLOOP; }; eColor: Imager.Color _ GetEntityColor[handle, entity]; context.SetColor[eColor]; context.SetStrokeJoint[mitered]; context.SetStrokeEnd[butt]; context.SetStrokeWidth[entity.width]; context.TranslateT[[axesRect.x, axesRect.y]]; context.SetXY[[axesRect.x, axesRect.y]]; context.MaskStroke[path: curve, closed: FALSE]; IF entity.mark # none THEN { xseg _ entity.group.x.segments; FOR yseg: SegmentDataList _ entity.segments, yseg.rest UNTIL yseg = NIL DO vec: Imager.VEC _ [(xseg.first.end-realRect.x)*scale[x], (yseg.first.end-realRect.y)*scale[y]]; eFont: ImagerFont.Font _ imagerFonts[paintInfo.output][IF entity.mark > cross THEN 0 ELSE LAST[FontIndex]]; DrawRope[context, GraphConvert.CharRopeFromMark[entity.mark], vec.x, vec.y, center, center, eColor, eFont]; xseg _ xseg.rest; ENDLOOP; }; }; context.DoSaveAll[oneCurve]; }; -- DrawColorEntity LineShape: TYPE = {solid, dot, dash, longDash}; StepSize: ARRAY LineShape[dot..longDash] OF REAL = [3.0, 6.0, 9.0]; LineLimit: ARRAY LineShape[dot..longDash] OF REAL = [1.0, 4.0, 7.0]; -- beyond which is space. shape: ARRAY EntityColor OF ARRAY StepCount OF LineShape = [ [solid, solid, solid, solid, solid, solid], -- solid, useless [dot, dot, dot, dot, dot, dot], -- dot [dash, dash, dash, dash, dash, dash], -- dash [longDash, longDash, longDash, longDash, longDash, longDash], -- longDash [dot, dash, dot, dash, dot, dash], -- dot-dash [dot, longDash, dot, longDash, dot, longDash], -- dot-longDash [dot, dot, dash, dot, dot, dash], -- dot-dot-dash [dot, dot, longDash, dot, dot, longDash], -- dot-dot-longDash [dot, dash, dash, dot, dash, dash], -- dot-dash-dash [dot, longDash, longDash, dot, longDash, longDash], -- dot-longDash-longDash [dash, longDash, dash, longDash, dash, longDash], -- dash-longDash [dot, dash, longDash, dot, dash, longDash] -- dot-dash-longDash ]; MaskLineSeg: PROC[context: Imager.Context, v0, v1: Imager.VEC _ [0.0, 0.0], style: EntityColor _ 1, state: BWLineState _ NIL] = { <> IF Almost[v0.x, v1.x] AND Almost[v0.y, v1.y] THEN RETURN; IF state = NIL THEN {bwLineState^ _ []; state _ bwLineState}; IF style = 1 THEN context.MaskVector[v0, v1] ELSE { -- Note that normally state.step and state.progress will be updated. dx: REAL = v1.x - v0.x; dy: REAL = v1.y - v0.y; lengthToGo: REAL _ RealFns.SqRt[dx*dx + dy*dy]; cosine: REAL _ dx/lengthToGo; sine: REAL _ dy/lengthToGo; newVec, oldVec: Imager.VEC _ v0; stepSize, lineLimit, increment: REAL; drawIt: BOOL; UNTIL Almost[lengthToGo, 0.0] DO stepSize_ StepSize[shape[style][state.step]]; lineLimit_ LineLimit[shape[style][state.step]]; IF state.progress < lineLimit THEN { drawIt _ TRUE; increment _ MIN[lengthToGo, lineLimit - state.progress]; } ELSE { drawIt _ FALSE; increment _ MIN[lengthToGo, stepSize - state.progress]; }; lengthToGo _ lengthToGo - increment; state.progress _ state.progress + increment; newVec _ [oldVec.x + cosine*increment, oldVec.y + sine*increment]; IF drawIt THEN context.MaskVector[oldVec, newVec] ELSE IF Almost[state.progress, stepSize] THEN { state.step _ (state.step + 1) MOD MaxStep; state.progress _ 0.0; }; oldVec _ newVec; ENDLOOP; }; }; -- MaskLineSeg DrawEntity: DrawProc = { OPEN handle; entity: Entity _ handle.paintInfo.entity; IF entity # NIL THEN { entityProc: PROC = { IF entity.colorIndex = 0 THEN -- auto color selection for white curves. lastEntityColor _ entity.colorIndex _ ((lastEntityColor + 1) MOD LastEntityColor) + 1; DrawLegend[context, handle, entity]; context.ClipRectangle[axesRect]; IF colorMode = bw THEN DrawBWEntity[context, handle, entity] ELSE DrawColorEntity[context, handle, entity]; }; context.DoSaveAll[entityProc]; }; }; -- DrawEntity <> <> <> <> <> <> <<[v1, v2] _ CrossSegment[vx, vy, >> <> <<}; -- DrawCrossSegments>> DrawTails: DrawProc = { OPEN handle; info: PaintInfo _ handle.paintInfo; <> drawTails: PROC = { v1: ValueList _ info.v1; v2: ValueList _ info.v2; entity: Entity; vec1, vec2: Imager.VEC; oneSegment: PROC = { context.SetStrokeWidth[UnitLineWidth*entity.width]; context.SetColor[GetEntityColor[handle, entity]]; IF colorMode = bw THEN { bwLineState^ _ []; MaskLineSeg[context, vec1, vec2, entity.colorIndex, bwLineState]; } ELSE context.MaskVector[vec1, vec2]; }; -- oneSegment context.SetStrokeEnd[square]; context.ClipRectangle[axesRect]; context.TranslateT[[axesRect.x, axesRect.y]]; vec1.x _ (info.x1 - realRect.x)*scale[x]; vec2.x _ (info.x2 - realRect.x)*scale[x]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO entity _ el.first; vec1.y _ (v1.first - realRect.y)*scale[y]; vec2.y _ (v2.first - realRect.y)*scale[y]; IF entity.colorIndex = 0 THEN -- auto color selection for white curves. lastEntityColor _ entity.colorIndex _ ((lastEntityColor + 1) MOD LastEntityColor) + 1; context.DoSaveAll[oneSegment]; IF (v1 _ v1.rest) = NIL OR (v2 _ v2.rest) = NIL THEN EXIT; ENDLOOP; }; -- drawTails context.DoSaveAll[drawTails]; }; -- DrawTails GetEntityColor: PROC [handle: GraphHandle, entity: Entity] RETURNS [Imager.Color] = { <> OPEN handle; RETURN[ IF paintInfo.action = erase THEN BackgroundColor[backgroundIndex, paintInfo.output] ELSE IF colorMode = bw THEN ForegroundColor[backgroundIndex, paintInfo.output] ELSE systemColor[entity.colorIndex] ]; }; -- GetEntityColor GetTextColor: PROC [handle: GraphHandle, text: Text] RETURNS [Imager.Color] = { <> OPEN handle; RETURN[ IF paintInfo.action = erase THEN BackgroundColor[backgroundIndex, paintInfo.output] ELSE systemColor[text.colorIndex] ]; }; -- GetTextColor GetStandardColor: PROC [handle: GraphHandle] RETURNS [Imager.Color] = { <> OPEN handle; RETURN[ IF paintInfo.action = paint THEN ForegroundColor[handle.backgroundIndex, paintInfo.output] ELSE BackgroundColor[handle.backgroundIndex, paintInfo.output] ]; }; -- GetStandardColor PrintCaretsInfo: DrawProc = { OPEN handle; caretsProc: PROC = { xh1: CaretSpec _ graph.caret[primary]; xh2: CaretSpec _ graph.caret[secondary]; xh3: CaretSpec _ graph.caret[text]; cvec: Imager.VEC; cColor: Imager.Color _ ForegroundColor[backgroundIndex, interpress]; context.SetColor[cColor]; context.SetStrokeWidth[UnitLineWidth]; context.SetStrokeEnd[butt]; IF xh1.on THEN { cvec _ RealVecToScreenVec[handle, xh1.place]; context.MaskVector[[cvec.x - 17.0, cvec.y], [cvec.x + 17.0, cvec.y]]; context.MaskVector[[cvec.x, cvec.y - 17.0], [cvec.x, cvec.y + 17.0]]; }; IF xh2.on THEN { cvec _ RealVecToScreenVec[handle, xh2.place]; bwLineState^ _ []; MaskLineSeg[context, [cvec.x - 17.0, cvec.y], [cvec.x + 17.0, cvec.y], 2, bwLineState]; bwLineState^ _ []; MaskLineSeg[context, [cvec.x, cvec.y - 17.0], [cvec.x, cvec.y + 17.0], 2, bwLineState]; }; IF xh3.on THEN { cvec _ RealVecToScreenVec[handle, xh3.place]; context.MaskVector[[cvec.x - 17.0, cvec.y], [cvec.x + 17.0, cvec.y]]; context.MaskVector[[cvec.x - 12.0, cvec.y + 12.0], [cvec.x + 12.0, cvec.y - 12.0]]; context.MaskVector[[cvec.x - 12.0, cvec.y - 12.0], [cvec.x + 12.0, cvec.y + 12.0]]; }; IF xh1.on OR xh2.on THEN { bothOn: BOOL _ xh1.on AND xh2.on; rope: ROPE _ Rope.Concat["Crosshair", IF bothOn THEN "s: " ELSE ": "]; IF xh1.on THEN rope _ rope.Concat[GraphConvert.RopeOfPlace[xh1.place]]; IF bothOn THEN rope _ rope.Concat[", "]; IF xh2.on THEN rope _ rope.Concat[GraphConvert.RopeOfPlace[xh2.place]]; IF bothOn THEN rope _ rope.Cat["; slope: ", GraphConvert.RopeOfSlope[xh1.place, xh2.place]]; DrawRope[context, rope, axesRect.x, axesRect.y + axesRect.h, left, bottom, cColor, imagerFonts[paintInfo.output][0]]; }; }; context.DoSaveAll[caretsProc]; }; -- PrintCaretsInfo }. CHANGE LOG. SChen, created at October 9, 1985 6:13:12 pm PDT.