DIRECTORY ImagerBackdoor, ImagerFont, ImagerPath, Hist, Imager, ImagerTransformation, IO, Process, Real, RealFns, Rope, TIPUser, TRCViewer, ViewerClasses, ViewerOps; TRCViewerImpl: CEDAR MONITOR IMPORTS ImagerBackdoor, ImagerFont, ImagerPath, Imager, ImagerTransformation, IO, Process, Real, RealFns, TIPUser, ViewerOps EXPORTS TRCViewer = BEGIN OPEN TRCViewer; --TYPES-- EditMode: TYPE = {sketch, pin}; State: TYPE = REF StateRec; StateRec: TYPE = RECORD [ viewer: ViewerClasses.Viewer, scale: REAL _ 1.0, title: Rope.ROPE _ NIL, -- filename or whatever userXLabel, userYLabel: Rope.ROPE _ NIL, hasTRC, hasHisto, newData, finished: BOOLEAN _ FALSE, trc: Hist.TRCVec _ NIL, histo: Hist.Histogram _ NIL, histoPath: ImagerPath.Trajectory, haveSelection: BOOLEAN _ FALSE, latestX, latestY: NAT _ 0, selected, selectedX, selectedY: NAT _ 0, lb, ub: INTEGER _ 0, -- index of the left and right neigbours of selected feedbackModeIn, feedbackModeOut: FeedBackMode _ reflectance, connectMode: ConnectMode _ linear, maxDIn, maxDOut: REAL _ 2.5, -- maximum I & O densities used to calc feedback data minRIn, minROut: REAL _ 0.0, -- minimum input/output refelectances changeProc: TRCViewer.ChangeProc _ NIL, cPClientData: REF ANY _ NIL, mode: EditMode _ pin ]; CallBack: TYPE = REF CallBackRec; CallBackRec: TYPE = RECORD [proc: Painter] ; Painter: TYPE = PROC [context: Imager.Context, state: State]; -- CLASS GLOBALS -- newDataAvailable: CONDITION; graphOriginX: NAT _ 100; graphOriginY: NAT _ 100; graphSize: NAT _ 256; graphScale: REAL _ 1; axisWidth: NAT _ 2; titleImagerFont: ImagerFont.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"], 14]; labelImagerFont: ImagerFont.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"], 12]; dataImagerFont: ImagerFont.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"], 10]; disclaimerImagerFont: ImagerFont.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"], 8]; defaultMaxDensity: REAL = 2.0; -- pretty arbitrary Create: PUBLIC PROC [] RETURNS [ViewerClasses.Viewer] ~ { RETURN[ViewerOps.CreateViewer[flavor: $TRCViewer, info: [name:"TRC Viewer"]]]; }; Get: PUBLIC PROC [v: ViewerClasses.Viewer] RETURNS [trc: Hist.TRCVec] ~ { -- Get the trc structure trc _ NEW[Hist.TRCVecRec]; trc^ _ NARROW[v.data, State].trc^; }; Set: PUBLIC PROC [v: ViewerClasses.Viewer, trc: Hist.TRCVec, paint:BOOLEAN _ TRUE] ~ { -- Set the trc structure state: State _ NARROW[v.data]; state.trc _ trc; state.ub _ 256; state.lb _ -1; IF paint THEN TRUSTED { ViewerOps.PaintViewer[ viewer: state.viewer, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: WriteGraph]] ]; }; IF state.changeProc#NIL THEN state.changeProc[state.cPClientData]; }; RegisterChangeProc: PUBLIC PROC [v: ViewerClasses.Viewer, changeProc: ChangeProc, clientData: REF ANY _ NIL] ~ { state: State _ NARROW[v.data]; state.changeProc _ changeProc; state.cPClientData _ clientData; }; SetTitle: PUBLIC PROC [v: ViewerClasses.Viewer, rope: Rope.ROPE] ~ { state: State _ NARROW[v.data]; state.title _ rope; TRUSTED { ViewerOps.PaintViewer[ viewer: state.viewer, hint: client] }; }; SetMinReflectanceIn: PUBLIC PROC [v: ViewerClasses.Viewer, minR:REAL] ~ { state: State _ NARROW[v.data]; state.minRIn _ minR; state.maxDIn _ RefToDens[minR]; }; SetMinReflectanceOut: PUBLIC PROC [v: ViewerClasses.Viewer, minR:REAL] ~ { state: State _ NARROW[v.data]; state.minROut _ minR; state.maxDOut _ RefToDens[minR]; IF state.changeProc#NIL THEN state.changeProc[state.cPClientData]; }; SetMaxDensityIn: PUBLIC PROC [v: ViewerClasses.Viewer, maxD:REAL] ~ { state: State _ NARROW[v.data]; state.maxDIn _ maxD; state.minRIn _ DensToRef[maxD]; }; SetMaxDensityOut: PUBLIC PROC [v: ViewerClasses.Viewer, maxD:REAL] ~ { state: State _ NARROW[v.data]; state.maxDOut _ maxD; state.minROut _ DensToRef[maxD]; IF state.changeProc#NIL THEN state.changeProc[state.cPClientData]; }; SetFeedBackModes: PUBLIC PROC [v: ViewerClasses.Viewer, in,out: FeedBackMode] ~ { state: State _ NARROW[v.data]; state.feedbackModeIn _ in; state.feedbackModeOut _ out; }; DensToRef: PUBLIC PROC [density:REAL] RETURNS [reflectance:REAL] = { reflectance _ RealFns.Power[10, -MIN[density, 10]]; }; RefToDens: PUBLIC PROC [reflectance:REAL] RETURNS [density:REAL] = { density _ -RealFns.Log[10, reflectance]; }; SetConnectMode: PUBLIC PROC [v: ViewerClasses.Viewer, mode: ConnectMode] ~ { SetOutput: EnumerateProc ~ { xIndex: NAT _ Real.RoundC[x]; IF ~state.trc[xIndex].pinned THEN state.trc[xIndex].out _ Real.RoundC[y]; }; state: State _ NARROW[v.data]; state.connectMode _ mode; state.lb _ -1; state.ub _ 256; Enumerate[state.viewer, SetOutput, 0, 255]; TRUSTED { ViewerOps.PaintViewer[ viewer: state.viewer, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: WriteGraph]] ]; }; IF state.changeProc#NIL THEN state.changeProc[state.cPClientData]; }; SetHistogram: PUBLIC PROC [v: ViewerClasses.Viewer, histo: Hist.Histogram _ NIL] ~ { ToggleHisto: Painter ~ { TransformToGraphSpace[context, state]; PaintGraphData[context, state, -1, 256]; }; state: State _ NARROW[v.data]; IF histo#NIL THEN { traj: ImagerPath.Trajectory; max: REAL _ 0; FOR i: NAT IN [0..255] DO max _ MAX[max, histo[i]]; ENDLOOP; traj _ ImagerPath.MoveTo[[0, 0]]; FOR i: NAT IN [0..255] DO traj _ traj.LineTo[[i, 255*histo[i]/max]].LineTo[[i+1, 255*histo[i]/max]]; ENDLOOP; traj _ traj.LineTo[[256, 0]].LineTo[[0, 0]]; state.histoPath _ traj; state.histo _ histo; }; state.hasHisto _ (histo#NIL); TRUSTED { ViewerOps.PaintViewer[ viewer: state.viewer, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: ToggleHisto]] ]; }; }; Enumerate: PUBLIC PROC [v: ViewerClasses.Viewer, proc: EnumerateProc, first,last:NAT] ~ { Line: PROC [x0,y0,x1,y1:INTEGER, doFirst:BOOLEAN_TRUE] ~ { xRange: REAL _ x1-x0; yRange: REAL _ y1-y0; IF doFirst THEN proc[x0, y0, vertices[x0].pinned]; FOR i: NAT IN (x0..x1) DO proc[i, Real.RoundC[y0+yRange*(i-x0)/xRange], vertices[i].pinned]; ENDLOOP; IF x0#x1 THEN proc[x1, y1, vertices[x1].pinned]; }; state: State _ NARROW[v.data]; vertices: Hist.TRCVec _ state.trc; IF NOT state.hasTRC THEN RETURN; SELECT state.connectMode FROM linear => { pinA: NAT _ first; proc[pinA,vertices[pinA].out, vertices[pinA].pinned]; FOR i: NAT IN (first..last] DO -- now linear interp to next pin IF vertices[i].pinned OR i=last THEN { Line[pinA, vertices[pinA].out, i, vertices[i].out, FALSE]; pinA _ i; }; ENDLOOP; }; log => { pinA: NAT _ first; proc[pinA, vertices[pinA].out, vertices[pinA].pinned]; FOR i: NAT IN (first..last] DO IF vertices[i].pinned OR i=last THEN { pinB: NAT _ i; intervalV: NAT _ pinB-pinA; startD: REAL _ Density[vertices[pinA].out, state.maxDOut]; endD: REAL _ Density[vertices[i].out, state.maxDOut]; intervalD: REAL _ startD - endD; startR: REAL _ DensToRef[startD]; intervalR: REAL _ DensToRef[endD] - startR; IF intervalD#0 THEN FOR j: NAT IN [1..intervalV] DO density: REAL _ RefToDens[startR+(intervalR*j)/intervalV]; position: REAL _ vertices[pinA].out + ((startD - density) / intervalD) * (vertices[i].out - vertices[pinA].out); proc[pinA+j, position, vertices[pinA+j].pinned]; ENDLOOP ELSE Line[pinA, vertices[pinA].out, pinB, vertices[pinB].out, FALSE]; pinA _ pinB; }; ENDLOOP; }; ENDCASE => ERROR; }; Init: ViewerClasses.InitProc ~ { SetOutput: EnumerateProc ~ { state.trc[Real.RoundC[x]].out _ Real.RoundC[y]; }; state: State _ NEW[StateRec _ [ maxDIn: defaultMaxDensity, maxDOut: defaultMaxDensity, minRIn: DensToRef[defaultMaxDensity], minROut: DensToRef[defaultMaxDensity], trc: NEW[Hist.TRCVecRec], hasTRC: TRUE, viewer: self, title: "Tone reproduction curve", userXLabel: "In", userYLabel: "Out" ]]; state.trc[0].out _ 0; state.trc[0].pinned _ TRUE; state.trc[255].out _ 255; state.trc[255].pinned _ TRUE; self.data _ state; Enumerate[self, SetOutput, 0, 255]; }; Notify: ViewerClasses.NotifyProc ~ { FindUserXY: Painter ~ { clientPair: ImagerTransformation.VEC; Clip: PROC [r:REAL] RETURNS [n:NAT] ~ INLINE { -- truncate real to [0..255] RETURN [Real.RoundC[MIN[MAX[r, 0], 255]]] }; TransformToGraphSpace[context, state]; clientPair _ ImagerBackdoor.ClientFromView[ context: context, p: [x:xy.mouseX, y:xy.mouseY] ]; x _ Clip[clientPair.x]; y _ Clip[clientPair.y]; }; xy:TIPUser.TIPScreenCoords _ NARROW[input.first]; state: State _ NARROW[self.data]; x,y:NAT; -- converted mouse coordinates TRUSTED { -- convert mouse coords to graph coords ViewerOps.PaintViewer[ viewer: self, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: FindUserXY]] ]}; FOR input _ input.rest, input.rest WHILE input#NIL DO -- process action tokens SELECT input.first FROM $Feedback => ShowFeedback[state, x, y]; $InitPosition => InitPosition[state, x, y]; $QueuePosition => QueuePosition[state, x, y]; $AddSelected => LocalPinVertex[state]; $FindNeighboringPins => FindNeighboringPins[state]; $ClearPinSelection => ClearPinSelection[state]; $SelectNearest => SelectNearestPin[state]; $ShiftSelected => MovePin[state]; $SelectVertexX => SelectVertexX[state]; $DeleteSelected => DeleteSelectedPin[state]; $Refresh => Refresh[state]; $StartFeedBackProcess => StartFeedBack[state]; $StopFeedBack => state.finished _ TRUE; $SetSketchMode => { state.mode _ sketch; state.lb _ state.ub _ state.selectedX }; $SetVertexMode => state.mode _ pin; ENDCASE => ERROR; ENDLOOP; }; ShowFeedback: PROC [state:State, x,y:NAT] ~ { Feedback : Painter ~ { context.SetColor[Imager.white]; context.MaskRectangle[[70, 60, 120, 35]]; context.SetColor[Imager.black]; context.SetXY[[70, 80]]; context.SetFont[dataImagerFont]; IF state.feedbackModeIn=density THEN context.ShowRope[ IO.PutFR[ "%3g (%4.2fD)", IO.card[x], IO.real[Density[x, state.maxDIn]]]] ELSE context.ShowRope[ IO.PutFR[ "%3g (%4.2fR)", IO.card[x], IO.real[Reflectance[x, state.minRIn]]]]; context.SetXY[[70, 65]]; IF state.feedbackModeOut=density THEN context.ShowRope[ IO.PutFR[ "%3g (%4.2fD)", IO.card[y], IO.real[Density[y, state.maxDOut]]]] ELSE context.ShowRope[ IO.PutFR[ "%3g (%4.2fR)", IO.card[y], IO.real[Reflectance[y, state.minROut]]]]; }; TRUSTED { -- convert mouse coords to graph coords ViewerOps.PaintViewer[ viewer: state.viewer, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: Feedback]] ]}; }; Paint: ENTRY ViewerClasses.PaintProc ~ { CallAndRestore: PROC ~ { box: Imager.Rectangle _ ImagerBackdoor.GetBounds[context]; sx: REAL _ (box.w) / 400; sy: REAL _ (box.h) / 400; state.scale _ MIN[sx,sy]; IF whatChanged=NIL THEN { context.ScaleT[state.scale]; DrawAll[context, state]; } ELSE { callBack: CallBack _ NARROW[whatChanged, CallBack]; context.ScaleT[state.scale]; callBack.proc[context, state]; } }; state: State _ NARROW[self.data]; context.DoSaveAll[CallAndRestore]; }; WriteGraph: Painter ~ { -- writes the section of the graph that lies between [state.lb..state.ub] TransformToGraphSpace[context, state]; PaintGraphData[context, state, state.lb, state.ub]; }; XORGraph: Painter ~ { TransformToGraphSpace[context, state]; PaintGraphData[context, state, state.lb, state.ub, TRUE]; }; XORPin: Painter ~ { TransformToGraphSpace[context, state]; IF state.trc[state.selected].pinned THEN PaintBlobAt[context, state.selected, state.trc[state.selected].out, TRUE]; }; XORVertex: Painter ~ { TransformToGraphSpace[context, state]; PaintDotAt[context, state.selectedX, state.trc[state.selectedX].out, TRUE]; }; DrawAll: Painter ~ { PaintAxesAndLabels[context, state]; TransformToGraphSpace[context, state]; PaintGraphData[context, state, 0, 255]; }; PaintAxesAndLabels: PROC [context: Imager.Context, state:State] ~ { -- draw the graph's axes lines: Imager.Trajectory _ ImagerPath.MoveTo[[ graphOriginX-axisWidth/2-1, graphOriginY+graphSize ]]; lines _ lines.LineTo[[ graphOriginX-axisWidth/2-1, graphOriginY-axisWidth/2-1 ]]; lines _ lines.LineTo[[ graphOriginX+graphSize, graphOriginY-axisWidth/2-1 ]]; context.SetColor[Imager.black]; context.SetStrokeWidth[axisWidth]; context.MaskStrokeTrajectory[lines]; context.SetXY[[graphOriginX, 20]]; IF state.title#NIL THEN { context.SetFont[titleImagerFont]; context.ShowRope[state.title]; }; context.SetXY[[graphOriginX+graphSize, 80]]; IF state.userXLabel#NIL THEN { context.SetFont[labelImagerFont]; context.ShowRope[state.userXLabel]; }; context.SetXY[[80, graphOriginY+graphSize+10]]; IF state.userYLabel#NIL THEN { context.SetFont[labelImagerFont]; context.ShowRope[state.userYLabel]; }; context.SetFont[dataImagerFont]; context.SetXY[[40, 80]]; context.ShowRope["In: "]; context.SetXY[[40, 65]]; context.ShowRope["Out: "]; }; TransformToGraphSpace: PROC [context: Imager.Context, state:State] ~ { -- transform context to area of graph context.TranslateT[[graphOriginX, graphOriginY]]; context.ScaleT[graphScale]; context.ClipRectangle[[0, 0, graphSize, graphSize]]; }; PaintGraphData: PROC [context: Imager.Context, state:State, min,max:INTEGER, xor:BOOLEAN _ FALSE] ~ { -- draw the vertices and/or histogram first: INTEGER _ MAX[min, 0]; last: INTEGER _ MIN[max, 255]; IF NOT xor THEN { context.SetColor[Imager.white]; context.MaskRectangle[[first, 0, last+1-first, graphSize]]; IF state.hasHisto THEN { context.SetColor[Imager.MakeGray[0.5]]; context.MaskFillTrajectory[state.histoPath]; }; PaintVertices[context, state, MAX[first-1, 0], MIN[last+1,255], xor]; }; PaintPlot[context, state, MAX[first-1, 0], MIN[last+1,255], xor]; }; PaintVertices: PROC [context: Imager.Context, state:State, first,last:INTEGER, xor:BOOLEAN _ FALSE] ~ { vertices: Hist.TRCVec _ state.trc; IF NOT state.hasTRC THEN RETURN; FOR i: NAT IN [first..last] DO IF vertices[i].pinned THEN PaintBlobAt[context, i, vertices[i].out, xor]; ENDLOOP; }; PaintBlobAt: PROC [context: Imager.Context, x,y:NAT, xor:BOOLEAN_FALSE] ~ { PaintBlob: PROC ~ { context.TranslateT[[x,y]]; IF xor THEN context.SetColor[ImagerBackdoor.invert] ELSE context.SetColor[Imager.black]; context.MaskRectangle[[-2, -2, 4, 4]]; }; context.DoSaveAll[PaintBlob]; }; PaintDotAt: PROC [context: Imager.Context, x,y:NAT, xor:BOOLEAN_FALSE] ~ { PaintDot: PROC ~ { context.TranslateT[[x,y]]; IF xor THEN context.SetColor[ImagerBackdoor.invert] ELSE context.SetColor[Imager.black]; context.MaskRectangle[[0, 0, 1, 1]]; }; context.DoSaveAll[PaintDot]; }; PaintPlot: PROC [context: Imager.Context, state:State, first,last:INTEGER, xor:BOOLEAN _ FALSE] ~ { graph: Imager.Trajectory; IF NOT state.hasTRC THEN RETURN; graph _ ImagerPath.MoveTo[[first, state.trc[first].out]]; FOR i: NAT IN (first..last] DO graph _ graph.LineTo[[i, state.trc[i].out]]; ENDLOOP; IF xor THEN context.SetColor[ImagerBackdoor.invert] ELSE context.SetColor[Imager.black]; context.SetStrokeWidth[0]; context.MaskStrokeTrajectory[graph]; }; InitPosition: PROC [state:State, x,y:NAT] ~ { QueuePosition[state, x, y]; state.selectedX _ x; state.selectedY _ y; }; QueuePosition: ENTRY PROC [state:State, x,y:NAT] ~ { state.latestX _ x; state.latestY _ y; state.newData _ TRUE; BROADCAST newDataAvailable }; GetLatestRecordedMousePos: ENTRY PROC [state:State] ~ { WHILE NOT state.newData DO WAIT newDataAvailable ENDLOOP; state.selectedX _ state.latestX; state.selectedY _ state.latestY; state.newData _ FALSE; }; LocalPinVertex: PROC [state: State] ~ { IF ~state.hasTRC THEN state.trc _ NEW[Hist.TRCVecRec]; GetLatestRecordedMousePos[state]; state.trc[state.selected].out _ state.selectedY; state.trc[state.selected].pinned _ TRUE; state.hasTRC _ TRUE; TRUSTED { ViewerOps.PaintViewer[ viewer: state.viewer, hint: client, clearClient: FALSE, whatChanged: NEW[CallBackRec _ [proc: XORPin]] ]; }; }; FindNeighboringPins: PROC [state:State] ~ { [state.lb, state.ub] _ SetBounds[state.trc, state.selected] }; SetBounds: PROC [trc: Hist.TRCVec, target:NAT] RETURNS[lb: INT _ -1, ub:INT _ 256] ~ { nextLower: INTEGER _ MAX[target-1, -1]; nextHigher: INTEGER _ MIN[target+1, 256]; IF trc#NIL THEN { WHILE nextLower>=0 DO IF trc[nextLower].pinned THEN EXIT; nextLower _ nextLower - 1; ENDLOOP; WHILE nextHigher<=255 DO IF trc[nextHigher].pinned THEN EXIT; nextHigher _ nextHigher + 1; ENDLOOP; RETURN [nextLower, nextHigher] }; }; ClearPinSelection: PROC [state:State] ~ { state.haveSelection _ FALSE }; SelectNearestPin: PROC [state:State] ~ { nearest: NAT; distance:REAL; x:REAL _ state.selectedX; y:REAL _ state.selectedY; foundOne: BOOLEAN _ FALSE; IF state.hasTRC THEN FOR i: NAT IN [0..255] DO IF state.trc[i].pinned THEN { thisDist: REAL _ (x-i)*(x-i) + (y-state.trc[i].out)*(y-state.trc[i].out); IF NOT foundOne OR thisDist—J™JšΟk œœ˜₯J˜JšΟn ˜J˜Jšœ`™`J™_J™ J™VJ™J™™Jšœu˜|Jšœ ˜Jšœœœ ˜J˜J˜ J˜Jšœ œ˜J˜Jšœœœ ˜šœ œœ˜Jšœ˜Jšœœ˜Jšœ œœΟc˜1Jšœœœ˜(Jšœ%œœ˜5Jšœ˜Jšœ˜Jšœ!˜!Jšœœœ˜Jšœœ˜Jšœœ˜(JšœœŸ4˜JJšœ;˜Jšœœ ˜Jšœ˜Jšœ˜J˜—J˜Jšžœ œ œ˜F™?Jšœœ ˜Jšœ˜Jšœ ˜ Jšœœœ&˜BJ˜—J˜Jšžœ œ4˜Q™'Jšœœ ˜Jšœ˜Jšœ˜J˜J˜—š ž œ œ œœœ˜DJšœ!œ˜3Jšœ˜J˜—š ž œ œœœ œ˜DJšœ(˜(Jšœ˜—J™Jšžœ œ1˜L™šž œ˜Jšœœ˜šœœ˜"Jšœ'˜'—J˜—Jšœœ ˜Jšœ˜J˜Jšœ+˜+šœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ"˜2Jšœ˜—Jšœ˜—Jšœœœ&˜BJ˜—J™Jšž œ œ3œ˜TšœB™Bšž œ ˜Jšœ&˜&Jšœ(˜(J˜—Jšœœ ˜šœœ˜Jšœ˜Jšœœ˜šœœœ ˜Jšœœ˜Jš˜—Jšœ!˜!šœœœ ˜JšœJ˜JJš˜—Jšœ,˜,Jšœ˜Jšœ˜J˜—Jšœœ˜šœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ#˜3Jšœ˜—J˜—J˜—J™J™Jšž œœœ;œ˜YJšœ`™`šœ3™3Jš žœœœ œœ˜:Jšœ9™9Jšœ=™=šœ\™\Jšœœ ˜Jšœœ ˜Jšœœ#˜2šœœœ ˜JšœB˜BJš˜—Jšœœ#˜0J˜—J™Jšœœ ˜Jšœ"˜"Jšœœœœ˜ šœ˜˜ Jšœœ ˜Jšœ5˜5š œœœœŸ ˜?šœœœ˜&Jšœ3œ˜:Jšœ ˜ J˜—Jšœ˜—J˜—šœ ˜ Jšœœ ˜Jšœ6˜6šœœœ˜šœœœ˜&Jšœœ˜Jšœ œ ˜Jšœœ.˜:Jšœœ+˜5Jšœœ.™:Jšœœ+™5Jšœ œ˜ Jšœœ˜!Jšœ œ˜+šœ œ˜šœœœ˜Jšœ œ-˜:Jšœ œb˜pJšœ0˜0Jš˜——š˜Jšœ9œ˜@—Jšœ ˜ J˜—Jšœ˜—J˜—Jšœœ˜—J˜J˜J˜—J™J™J˜Jšžœ˜ ™šž œ˜Jšœ/˜/J˜—šœœ ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ&˜&Jšœœ˜Jšœ ˜ Jšœ ˜ J˜!Jšœ˜Jšœ˜Jšœ˜—Jšœ,œ˜1Jšœ2œ˜7Jšœ˜J˜#J˜—J˜J˜Jšžœ˜$J™™šž œ ˜J™/Jšœ!œ˜%š žœœœœœœ˜.Jšœ˜Jšœœœ˜)J˜J˜—Jšœ&˜&šœ+˜+Jšœ˜Jšœ˜Jšœ˜—Jšœ/˜/J˜J˜—J˜Jšœœ˜1Jšœœ ˜!JšœœŸ˜(J˜šœŸ'˜2šœ˜Jšœ ˜ Jšœ ˜ Jšœ œ˜Jšœ œ"˜2Jšœ˜J˜——š œ œœœŸ˜Nšœ ˜Jšœ'˜'Jšœ,˜,Jšœ.˜.Jšœ&˜&Jšœ3˜3J˜/Jšœ*˜*Jšœ!˜!Jšœ'˜'Jšœ,˜,J˜Jšœ.˜.Jšœ"œ˜'šœ˜Jšœ˜J˜%J˜—J˜#Jšœœ˜—Jšœ˜—J˜—J˜šž œœœ˜-šžœ˜J™'Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ ˜ šœœ˜%šœ˜šœ˜ Jšœ˜Jšœ ˜ Jšœ#˜#———šœ˜šœ˜šœ˜ Jšœ˜Jšœ ˜ Jšœ(˜(———Jšœ˜šœœ˜&šœ˜šœ˜ Jšœ˜Jšœ ˜ Jšœ$˜$———šœ˜šœ˜šœ˜ Jšœ˜Jšœ ˜ Jšœ)˜)———J˜—šœŸ'˜2šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ ˜0Jšœ˜——J˜—J˜Jšžœœ˜(J™J™(™Ašžœœ˜J™BJšœ:˜:Jšœœ˜Jšœœ˜Jšœœ˜J™šœ œœ˜Jšœ˜Jšœ˜J˜—šœ˜Jšœœ˜3Jšœ˜Jšœ˜J˜—J˜—Jšœœ ˜!J˜Jšœ"˜"Jšœ˜—J˜J™™ J™—Jšž œ ˜šœI˜IJšœ&˜&Jšœ3˜3J˜—J™Jšžœ ˜šœF™FJšœ&˜&Jšœ3œ˜9J˜—J™Jšžœ ˜šœ™Jšœ&˜&šœ!˜(JšœDœ˜J—J˜—J™J™Jšž œ ˜šœ™Jšœ&˜&JšœEœ˜KJ˜—J™J™Jšžœ ˜šœ1™1Jšœ#˜#Jšœ&˜&Jšœ'˜'J˜J™J™&Jšžœœ+˜C˜šœ.˜.Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ"˜"Jšœ$˜$Jšœ"˜"šœ œ˜Jšœ!˜!Jšœ˜J˜—Jšœ"™"šœ™Jšœ4™4Jšœ™—Jšœ,˜,šœœ˜Jšœ!˜!Jšœ#˜#J˜—Jšœ/˜/šœœ˜Jšœ!˜!Jšœ#˜#J˜—Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—J™Jšžœœ+˜F˜%J˜1J˜Jšœ4˜4J˜—J™Jš žœœ0œœœ˜e˜%Jšœœœ ˜Jšœœœ ˜šœœœ˜Jšœ˜Jšœ;˜;šœœ˜Jšœ'˜'Jšœ,˜,J˜—Jšœœœ˜EJ˜—Jšœœœ˜AJ˜—J™Jš ž œœ3œœœ˜gšœ:™:Jšœ"˜"Jšœœœœ˜ šœœœ˜Jšœœ/˜IJš˜—J˜—J™Jš ž œœœœœ˜Kšœ=™=šž œœ˜Jšœ˜Jšœœ)œ ˜XJšœ&˜&J˜—Jšœ˜J˜—J™Jš ž œœœœœ˜Jšœ=™=šžœœ˜Jšœ˜Jšœœ)œ ˜XJšœ$˜$J˜—Jšœ˜J˜—J™Jš ž œœ3 œœœ˜cšœ:™:Jšœ˜Jšœœœœ˜ Jšœ9˜9šœœœ˜Jšœ,˜,Jš˜—Jšœœ)œ ˜XJšœ˜Jšœ$˜$J˜——J™J™J™Jšž œœœ˜-™ZJšœ˜Jšœ˜Jšœ˜J˜—J˜Jšž œ œœ˜4šœK™KJšœ˜Jšœ˜Jšœœ˜Jš œ˜J˜J™Jšžœ œ˜7™)Jš œœœœœ˜9Jšœ ˜ Jšœ ˜ Jšœœ˜J˜——J™Jšžœœ˜'Jšœž œ>™[Jšœ™Jšœ/žœ ™S™YJšœœ œ˜7Jšœ!˜!šœ™ šœ™Jšœ™Jšœ ™ Jšœ œ™Jšœ œ ™0Jšœ™—J™—Jšœ0˜0Jšœ#œ˜(Jšœœ˜šœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ˜.Jšœ˜—J˜—Jšœ˜—J™Jšžœœ˜+J™C™)J˜;J˜J˜š ž œœœœœ œ ˜VJ™HJšœ œœ˜'Jšœ œœ˜)šœœœ˜šœ˜Jšœœ˜#Jšœ˜Jš˜—šœ˜Jšœœ˜$Jšœ˜Jš˜—Jšœ˜J˜—J˜—J˜—Jšžœœ˜)™#Jšœ˜J˜—J™Jšžœœ˜(™=Jšœ œ˜ Jšœ œ˜Jšœœœ˜3Jšœ œœ˜šœ œ˜šœœœ ˜šœœ˜Jšœ œ;˜Išœœ œœ˜+Jšœ˜J˜ J˜—Jšœ œ˜J˜—Jš˜——šœ"œ˜*Jšœ˜Jšœ˜J˜—J˜—J™Jšžœœ˜Jšœ/žœ ™SJ™'J™Y™*šœœ˜Jšœ#Ÿ˜?šœŸ˜*šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ˜.Jšœ˜—J˜—Jšœ#œ˜)Jšœœœ+˜CJšœ#œ˜(Jšœ0˜0šœŸ ˜+šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ˜.Jšœ˜—J˜—Jšœ6˜6Jšœ˜—J˜—J™Jšž œœ˜"Jšœ/žœ ™SJ™'J™Y™*Jšœ#Ÿ˜?šœŸ˜*šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ!˜1Jšœ˜—J˜—J˜1Jšœ œ˜*Jšœ œ˜*šœŸ ˜+šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ!˜1Jšœ˜—J˜—Jšœ6˜6Jšœ˜—J™Jšž œœ˜%™NJ˜!Jšœ˜J˜—J™Jšžœœ˜)J™)™1šœœ˜šž œ˜Jšœœ˜šœœ˜"Jšœ'˜'—J˜—Jšœ#œ˜)Jšœœ˜Jšœ#œœ˜Išœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ"˜2Jšœ˜—Jšœ˜—Jšœœœ&˜BJšœ˜—J˜J˜—Jšžœœ˜J™+™1šœœ˜šž œ˜Jšœœ˜šœœ˜"Jšœ'˜'—J˜—Jšœ#œ˜(Jšœ#œœ˜Išœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ"˜2Jšœ˜—Jšœ˜—Jšœœœ&˜BJšœ˜—J˜—J˜J™Jšž œœ˜%™Jšœœ˜Jšœœ˜1J˜—J™Jšž œœ˜"™Jšž œ˜Jšœœ˜šœœ˜"Jšœ'˜'—J˜—šœœ˜š˜Jšœœ˜Jšœ˜Jš˜—Jšœ#œœ˜IJ˜—šœ˜Jš œœ œœœœ˜/š˜Jšœœ˜Jšœ˜Jšœœ˜Jš˜—šœœœ˜%Jšœ ˜ Jš˜—Jšœ#œœ˜Išœœœ˜%Jšœœ˜Jš˜—Jšœ œ$œœ˜UJšœ˜—šœ˜ šœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœ œ"˜2Jšœ˜—J˜—Jšœœœ&˜BJ˜—J™J™Jš žœœ œœœ œ˜@™&Jšœ&˜&J˜—J˜Jš žœœ œœœ œ™@™&Jšœ5™5J™—J˜Jš ž œœœœœœ˜G™,Jšœ.˜.J˜—J™Jšž œœ˜™4šœ,œ!˜PJšœ ˜ Jšœ:˜:Jšœ˜Jšœ ˜ Jšœ˜—Jšœ:˜:J˜J˜šœ˜J˜—Jšœ˜——…—QόƒΚ