------------------------------------------------------------------------------- |-- Module : Graphics.Rendering.Chart.Layout-- Copyright : (c) Tim Docker 2006-- License : BSD-style (see chart/COPYRIGHT)---- This module glues together axes and plots to actually create a renderable-- for a chart.---- Note that template haskell is used to derive accessor functions-- (see 'Data.Accessor') for each field of the following data types:---- * 'Layout1'---- * 'LayoutAxis'---- These accessors are not shown in this API documentation. They have-- the same name as the field, but with the trailing underscore-- dropped. Hence for data field f_::F in type D, they have type---- @-- f :: Data.Accessor.Accessor D F-- @--{-# OPTIONS_GHC -XTemplateHaskell #-}moduleGraphics.Rendering.Chart.Layout(Layout1(..),LayoutAxis(..),MAxisFn,defaultLayout1,mAxis,noAxis,updateAllAxesStyles,updateXAxesData,updateYAxesData,setForeground,laxis_title_style,laxis_title,laxis_style,laxis_data,laxis_reverse,layout1_background,layout1_title,layout1_title_style,layout1_left_axis,layout1_right_axis,layout1_top_axis,layout1_bottom_axis,layout1_margin,layout1_plots,layout1_legend,layout1_grid_last)whereimportqualifiedGraphics.Rendering.CairoasCimportGraphics.Rendering.Chart.AxisimportGraphics.Rendering.Chart.TypesimportGraphics.Rendering.Chart.PlotimportGraphics.Rendering.Chart.LegendimportGraphics.Rendering.Chart.RenderableimportGraphics.Rendering.Chart.GridimportControl.MonadimportControl.Monad.Reader(local)importData.Accessor.TemplateimportData.Accessor-- | A @MAxisFn@ is a function that generates an (optional) axis-- given the points plotted against that axis.typeMAxisFnt=[t]->Maybe(AxisDatat)dataLayoutAxisx=LayoutAxis{laxis_title_style_::CairoFontStyle,laxis_title_::String,laxis_style_::AxisStyle,laxis_data_::MAxisFnx,-- | True if left to right (bottom to top) is to show descending valueslaxis_reverse_::Bool}-- | A Layout1 value is a single plot area, with optional: axes on-- each of the 4 sides; title at the top; legend at the bottom. It's-- parameterised by the types of values to be plotted on the horizonal-- and vertical axes.dataLayout1xy=Layout1{layout1_background_::CairoFillStyle,layout1_title_::String,layout1_title_style_::CairoFontStyle,layout1_bottom_axis_::LayoutAxisx,layout1_top_axis_::LayoutAxisx,layout1_left_axis_::LayoutAxisy,layout1_right_axis_::LayoutAxisy,layout1_margin_::Double,layout1_plots_::[(String,Either(Plotxy)(Plotxy))],layout1_legend_::Maybe(LegendStyle),-- | True if the grid is to be rendered on top of the Plotslayout1_grid_last_::Bool}dataLayout1Pickxy=L1P_LegendString|L1P_PlotAreaxyy|L1P_BottomAxisx|L1P_TopAxisx|L1P_LeftAxisy|L1P_RightAxisyinstance(Ordx,Ordy)=>ToRenderable(Layout1xy)wheretoRenderable=setPickFnnullPickFn.layout1ToRenderablelayout1ToRenderable::(Ordx,Ordy)=>Layout1xy->Renderable(Layout1Pickxy)layout1ToRenderablel=fillBackground(layout1_background_l)(gridToRenderable$aboveN[tval$addMargins(lm/2,0,0,0)title,weights(1,1)$tval$addMargins(lm,lm,lm,lm)plotArea,tval$legends])wheretitle=label(layout1_title_style_l)HTA_CentreVTA_Centre(layout1_title_l)plotArea=gridToRenderable(layer2`overlay`layer1)layer1=aboveN[besideN[er,er,er],besideN[er,er,er],besideN[er,er,weights(1,1)plots]]layer2=aboveN[besideN[er,er,ttitle,er,er],besideN[er,tl,taxis,tr,er],besideN[ltitle,laxis,er,raxis,rtitle],besideN[er,bl,baxis,br,er],besideN[er,er,btitle,er,er]]ttitle=atitleHTA_CentreVTA_Bottom0layout1_top_axis_btitle=atitleHTA_CentreVTA_Top0layout1_bottom_axis_ltitle=atitleHTA_RightVTA_Centre90layout1_left_axis_rtitle=atitleHTA_LeftVTA_Centre90layout1_right_axis_er=tval$emptyRenderableatitlehavarotaf=ifttext==""thenerelsetval$rlabeltstylehavarotttextwheretstyle=laxis_title_style_(afl)ttext=laxis_title_(afl)plots=tval$plotsToRenderablel(ba,la,ta,ra)=getAxeslbaxis=tval$maybeemptyRenderable(mapPickFnL1P_BottomAxis.axisToRenderable)bataxis=tval$maybeemptyRenderable(mapPickFnL1P_TopAxis.axisToRenderable)talaxis=tval$maybeemptyRenderable(mapPickFnL1P_LeftAxis.axisToRenderable)laraxis=tval$maybeemptyRenderable(mapPickFnL1P_RightAxis.axisToRenderable)ratl=tval$axesSpacerfsttafstlabl=tval$axesSpacerfstbasndlatr=tval$axesSpacersndtafstrabr=tval$axesSpacersndbasndralegends=gridToRenderable(besideN[tval$mkLegendlefts,weights(1,1)$tval$emptyRenderable,tval$mkLegendrights])lefts=[(s,p)|(s,Leftp)<-(layout1_plots_l)]rights=[(s,p)|(s,Rightp)<-(layout1_plots_l)]mkLegendplots=case(layout1_legend_l)ofNothing->emptyRenderable(Justls)->caseplotsof[]->emptyRenderableps->addMargins(0,lm,lm,lm)(mapPickFnL1P_Legend$legendToRenderable(LegendTruelsps))lm=layout1_margin_lplotsToRenderable::Layout1xy->Renderable(Layout1Pickxy)plotsToRenderablel=Renderable{minsize=return(0,0),render=renderPlotsl}renderPlots::Layout1xy->RectSize->CRender(PickFn(Layout1Pickxy))renderPlotslsz@(w,h)=preserveCState$do-- render the plotssetClipRegion(Point00)(Pointwh)when(not(layout1_grid_last_l))renderGridslocal(constvectorEnv)$domapM_rPlot(layout1_plots_l)when(layout1_grid_last_l)renderGridsreturnnullPickFnwhere(bAxis,lAxis,tAxis,rAxis)=getAxeslrPlot(_,Leftp)=rPlot1bAxislAxisprPlot(_,Rightp)=rPlot1bAxisrAxisprPlot1(Just(AxisT_xsxrevxaxis))(Just(AxisT_ysyrevyaxis))p=letxrange=ifxrevthen(w,0)else(0,w)yrange=ifyrevthen(0,h)else(h,0)pmfn(x,y)=Point(axis_viewport_xaxisxrangex)(axis_viewport_yaxisyrangey)inplot_render_ppmfnrPlot1___=return()renderGrids=domaybeM()(renderAxisGridsz)tAxismaybeM()(renderAxisGridsz)bAxismaybeM()(renderAxisGridsz)lAxismaybeM()(renderAxisGridsz)rAxisaxesSpacerf1a1f2a2=embedRenderable$dooh1<-maybeM(0,0)axisOverhanga1oh2<-maybeM(0,0)axisOverhanga2return(spacer(f1oh1,f2oh2))getAxes::Layout1xy->(Maybe(AxisTx),Maybe(AxisTy),Maybe(AxisTx),Maybe(AxisTy))getAxesl=(bAxis,lAxis,tAxis,rAxis)where(xvals0,xvals1,yvals0,yvals1)=allPlottedValues(layout1_plots_l)xvals=xvals0++xvals1-- Link the axes if either has no data, and use the axis that-- actually has data to decide whether to reverse it(yvals0',yrev0)=ifnullyvals0then(yvals0++yvals1,layout1_right_axis_)else(yvals0,layout1_left_axis_)(yvals1',yrev1)=ifnullyvals1then(yvals0++yvals1,layout1_left_axis_)else(yvals1,layout1_right_axis_)bAxis=mkAxisE_Bottomlayout1_bottom_axis_layout1_bottom_axis_xvalstAxis=mkAxisE_Toplayout1_top_axis_layout1_bottom_axis_xvalslAxis=mkAxisE_Leftlayout1_left_axis_yrev0yvals0'rAxis=mkAxisE_Rightlayout1_right_axis_yrev1yvals1'mkAxistaxisfrevfvals=doadata<-laxis_data_(axisfl)valsreturn(AxisTt(laxis_style_(axisfl))(laxis_reverse_(revfl))adata)allPlottedValues::[(String,Either(Plotxy)(Plotx'y'))]->([x],[x'],[y],[y'])allPlottedValuesplots=(xvals0,xvals1,yvals0,yvals1)wherexvals0=[x|(_,Leftp)<-plots,(x,_)<-plot_all_points_p]yvals0=[y|(_,Leftp)<-plots,(_,y)<-plot_all_points_p]xvals1=[x|(_,Rightp)<-plots,(x,_)<-plot_all_points_p]yvals1=[y|(_,Rightp)<-plots,(_,y)<-plot_all_points_p]defaultLayout1::(PlotValuex,PlotValuey)=>Layout1xydefaultLayout1=Layout1{layout1_background_=solidFillStylewhite,layout1_title_="",layout1_title_style_=defaultFontStyle{font_size_=15,font_weight_=C.FontWeightBold},layout1_top_axis_=defaultLayoutAxis,layout1_bottom_axis_=defaultLayoutAxis,layout1_left_axis_=defaultLayoutAxis,layout1_right_axis_=defaultLayoutAxis,layout1_margin_=10,layout1_plots_=[],layout1_legend_=JustdefaultLegendStyle,layout1_grid_last_=False}defaultLayoutAxis::PlotValuet=>LayoutAxistdefaultLayoutAxis=LayoutAxis{laxis_title_style_=defaultFontStyle{font_size_=10},laxis_title_="",laxis_style_=defaultAxisStyle,laxis_data_=mAxisautoAxis,laxis_reverse_=False}-- | Create an axis when there are points to be plotted against it.mAxis::PlotValuet=>AxisFnt->MAxisFntmAxisaxisfn[]=NothingmAxisaxisfnps=Just(axisfnps)-- | Never create an axisnoAxis::PlotValuet=>LayoutAxistnoAxis=LayoutAxis{laxis_title_style_=defaultFontStyle{font_size_=10},laxis_title_="",laxis_style_=defaultAxisStyle,laxis_data_=constNothing,laxis_reverse_=False}------------------------------------------------------------------------ Template haskell to derive an instance of Data.Accessor.Accessor for each field$(deriveAccessors''Layout1)$(deriveAccessors''LayoutAxis)-- | Helper to update all axis styles on a Layout1 simultaneouslyupdateAllAxesStyles::(AxisStyle->AxisStyle)->Layout1xy->Layout1xyupdateAllAxesStylesuf=(layout1_top_axis.>laxis_style^:uf).(layout1_bottom_axis.>laxis_style^:uf).(layout1_left_axis.>laxis_style^:uf).(layout1_right_axis.>laxis_style^:uf)-- | Helper to update data member of both horizontal axes in a Layout1updateXAxesData::(MAxisFnx->MAxisFnx)->Layout1xy->Layout1xyupdateXAxesDatauf=(layout1_top_axis.>laxis_data^:uf).(layout1_bottom_axis.>laxis_data^:uf)-- | Helper to update data member of both vertical axes in a Layout1updateYAxesData::(MAxisFny->MAxisFny)->Layout1xy->Layout1xyupdateYAxesDatauf=(layout1_left_axis.>laxis_data^:uf).(layout1_right_axis.>laxis_data^:uf)-- | Helper to set the forground color uniformly on a Layout1setForeground::Color->Layout1xy->Layout1xysetForegroundfg=updateAllAxesStyles((axis_line_style.>line_color^=fg).(axis_label_style.>font_color^=fg)).(layout1_title_style.>font_color^=fg).(layout1_legend^:fmap(legend_label_style.>font_color^=fg))