packages feed

chart-unit 0.1.0.0 → 0.3.0

raw patch · 8 files changed

+1290/−552 lines, 8 filesdep +HUnitdep +ListLikedep +QuickCheckdep −diagrams-rasterificdep −random-funew-component:exe:chart-unit-examplesPVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, ListLike, QuickCheck, SVGFonts, ad, formatting, linear, mwc-probability, mwc-random, numhask, numhask-range, reflection, smallcheck, tasty, tasty-hspec, tasty-hunit, tasty-quickcheck, tasty-smallcheck, tdigest

Dependencies removed: diagrams-rasterific, random-fu

API changes (from Hackage documentation)

- Chart.Types: BarConfig :: ChartConfig -> Double -> BarConfig
- Chart.Types: LinesConfig :: ChartConfig -> [OneLine] -> LinesConfig
- Chart.Types: OneLine :: AlphaColour Double -> Double -> OneLine
- Chart.Types: TickNumber :: Int -> TickStyle
- Chart.Types: [_axisStrutSize] :: AxisConfig -> Double
- Chart.Types: [_barChart] :: BarConfig -> ChartConfig
- Chart.Types: [_barSep] :: BarConfig -> Double
- Chart.Types: [_chartColor] :: ChartConfig -> AlphaColour Double
- Chart.Types: [_lColor] :: OneLine -> AlphaColour Double
- Chart.Types: [_lSize] :: OneLine -> Double
- Chart.Types: [_lineChart] :: LineConfig -> ChartConfig
- Chart.Types: [_linesChart] :: LinesConfig -> ChartConfig
- Chart.Types: [_linesLines] :: LinesConfig -> [OneLine]
- Chart.Types: [_scatterChart] :: ScatterConfig -> ChartConfig
- Chart.Types: axisStrutSize :: Lens' AxisConfig Double
- Chart.Types: barChart :: Lens' BarConfig ChartConfig
- Chart.Types: barSep :: Lens' BarConfig Double
- Chart.Types: chartColor :: Lens' ChartConfig (AlphaColour Double)
- Chart.Types: data BarConfig
- Chart.Types: data LinesConfig
- Chart.Types: data OneLine
- Chart.Types: filePng :: FilePath -> (Double, Double) -> ChartPng -> IO ()
- Chart.Types: fileSvg :: FilePath -> (Double, Double) -> ChartSvg -> IO ()
- Chart.Types: instance Data.Default.Class.Default Chart.Types.BarConfig
- Chart.Types: instance Data.Default.Class.Default Chart.Types.LinesConfig
- Chart.Types: instance Data.Default.Class.Default Chart.Types.OneLine
- Chart.Types: lColor :: Lens' OneLine (AlphaColour Double)
- Chart.Types: lSize :: Lens' OneLine Double
- Chart.Types: lineChart :: Lens' LineConfig ChartConfig
- Chart.Types: linesChart :: Lens' LinesConfig ChartConfig
- Chart.Types: linesLines :: Lens' LinesConfig [OneLine]
- Chart.Types: rgba :: (Floating a, Ord a) => (a, a, a, a) -> AlphaColour a
- Chart.Types: scatterChart :: Lens' ScatterConfig ChartConfig
- Chart.Types: type ChartPng = QDiagram Rasterific V2 Double Any
- Chart.Types: type ChartSvg = QDiagram SVG V2 Double Any
- Chart.Unit: axisXY :: (Renderable (Text Double) a, Renderable (Path V2 Double) a) => AxisConfig -> (Double, Double) -> QDiagram a V2 Double Any
- Chart.Unit: barLabelled :: (Renderable (Path V2 Double) a, Renderable (Text Double) a) => BarConfig -> [Double] -> [Text] -> QDiagram a V2 Double Any
- Chart.Unit: barRange :: (Renderable (Path V2 Double) a, Renderable (Text Double) a) => BarConfig -> [(Double, Double)] -> QDiagram a V2 Double Any
- Chart.Unit: bars :: (Renderable (Path V2 Double) a) => BarConfig -> [Double] -> QDiagram a V2 Double Any
- Chart.Unit: chartXY :: (Renderable (Path V2 Double) a, Renderable (Text Double) a) => ChartConfig -> ([(Double, Double)] -> QDiagram a V2 Double Any) -> [(Double, Double)] -> QDiagram a V2 Double Any
- Chart.Unit: line :: (Renderable (Path V2 Double) a) => [(Double, Double)] -> QDiagram a V2 Double Any
- Chart.Unit: lineXY :: (Renderable (Path V2 Double) a, Renderable (Text Double) a) => LineConfig -> [(Double, Double)] -> QDiagram a V2 Double Any
- Chart.Unit: lines :: (Renderable (Path V2 Double) a) => LinesConfig -> [[(Double, Double)]] -> QDiagram a V2 Double Any
- Chart.Unit: linesXY :: (Renderable (Text Double) a, Renderable (Path V2 Double) a) => LinesConfig -> [[(Double, Double)]] -> QDiagram a V2 Double Any
- Chart.Unit: mkTicks :: (Double, Double) -> Int -> [Double]
- Chart.Unit: mkTicks' :: (Double, Double) -> Int -> (Double, Double, Int)
- Chart.Unit: range1D :: (Fractional t, Ord t, Foldable f) => f t -> (t, t)
- Chart.Unit: range1Ds :: (Fractional t, Ord t, Foldable f, Foldable f') => f' (f t) -> (t, t)
- Chart.Unit: scatter :: (Typeable (N r), Monoid r, Semigroup r, Transformable r, HasStyle r, HasOrigin r, TrailLike r, V r ~ V2, N r ~ Double) => ScatterConfig -> [(N r, N r)] -> r
- Chart.Unit: scatterXY :: (Renderable (Path V2 Double) a, Renderable (Text Double) a) => ScatterConfig -> [(Double, Double)] -> QDiagram a V2 Double Any
- Chart.Unit: unit :: (Fractional b, Functor f, Ord b, Foldable f) => f b -> f b
- Chart.Unit: unitXY :: (Fractional b, Fractional a, Ord b, Ord a) => [(a, b)] -> [(a, b)]
- Chart.Unit: units :: (Fractional b, Functor f, Ord b, Foldable f, Functor f', Foldable f') => f' (f b) -> f' (f b)
- Chart.Unit: unitsXY :: (Fractional a, Ord a, Fractional b, Ord b) => [[(a, b)]] -> [[(a, b)]]
+ Chart: V4 :: ~a -> ~a -> ~a -> ~a -> V4 a
+ Chart: data SVG :: *
+ Chart: data V4 a :: * -> *
+ Chart: zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
+ Chart.Types: ArrowConfig :: a -> a -> a -> a -> a -> a -> a -> a -> a -> Color -> ArrowConfig a
+ Chart.Types: Aspect :: Rect Double -> Aspect
+ Chart.Types: Color :: Double -> Double -> Double -> Double -> Color
+ Chart.Types: PixelConfig :: Range Color -> V2 Int -> PixelConfig
+ Chart.Types: QChart :: ((Renderable (Text Double) a, Renderable (Path V2 Double) a) => Aspect -> b -> QDiagram a V2 Double Any) -> Rect Double -> b -> QChart a
+ Chart.Types: RectConfig :: Double -> Color -> Color -> RectConfig
+ Chart.Types: TickExact :: Int -> TickStyle
+ Chart.Types: TickPlaced :: [(Double, Text)] -> TickStyle
+ Chart.Types: TickRound :: Int -> TickStyle
+ Chart.Types: [_aaa] :: Color -> Double
+ Chart.Types: [_arrowColor] :: ArrowConfig a -> Color
+ Chart.Types: [_arrowHeadSize] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMaxHeadSize] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMaxStaffLength] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMaxStaffWidth] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMinHeadSize] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMinStaffLength] :: ArrowConfig a -> a
+ Chart.Types: [_arrowMinStaffWidth] :: ArrowConfig a -> a
+ Chart.Types: [_arrowStaffLength] :: ArrowConfig a -> a
+ Chart.Types: [_arrowStaffWidth] :: ArrowConfig a -> a
+ Chart.Types: [_axisInsideStrut] :: AxisConfig -> Double
+ Chart.Types: [_axisLabelStrut] :: AxisConfig -> Double
+ Chart.Types: [_axisPad] :: AxisConfig -> Double
+ Chart.Types: [_blue] :: Color -> Double
+ Chart.Types: [_chartAspect] :: ChartConfig -> Aspect
+ Chart.Types: [_chartCanvasColor] :: ChartConfig -> Color
+ Chart.Types: [_chartRange] :: ChartConfig -> Maybe (Rect Double)
+ Chart.Types: [_green] :: Color -> Double
+ Chart.Types: [_lineColor] :: LineConfig -> Color
+ Chart.Types: [_pixelGradient] :: PixelConfig -> Range Color
+ Chart.Types: [_pixelGrain] :: PixelConfig -> V2 Int
+ Chart.Types: [_qChart] :: QChart a -> (Renderable (Text Double) a, Renderable (Path V2 Double) a) => Aspect -> b -> QDiagram a V2 Double Any
+ Chart.Types: [_qData] :: QChart a -> b
+ Chart.Types: [_qXY] :: QChart a -> Rect Double
+ Chart.Types: [_rectBorderColor] :: RectConfig -> Color
+ Chart.Types: [_rectBorderWidth] :: RectConfig -> Double
+ Chart.Types: [_rectColor] :: RectConfig -> Color
+ Chart.Types: [_red] :: Color -> Double
+ Chart.Types: [_scatterColor] :: ScatterConfig -> Color
+ Chart.Types: [unAspect] :: Aspect -> Rect Double
+ Chart.Types: arrowColor :: forall a_anfB. Lens' (ArrowConfig a_anfB) Color
+ Chart.Types: arrowHeadSize :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMaxHeadSize :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMaxStaffLength :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMaxStaffWidth :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMinHeadSize :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMinStaffLength :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowMinStaffWidth :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowStaffLength :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: arrowStaffWidth :: forall a_anfB. Lens' (ArrowConfig a_anfB) a_anfB
+ Chart.Types: aspect :: Double -> Aspect
+ Chart.Types: asquare :: Aspect
+ Chart.Types: axisInsideStrut :: Lens' AxisConfig Double
+ Chart.Types: axisLabelStrut :: Lens' AxisConfig Double
+ Chart.Types: axisPad :: Lens' AxisConfig Double
+ Chart.Types: chartAspect :: Lens' ChartConfig Aspect
+ Chart.Types: chartCanvasColor :: Lens' ChartConfig Color
+ Chart.Types: chartRange :: Lens' ChartConfig (Maybe (Rect Double))
+ Chart.Types: color :: Color -> AlphaColour Double
+ Chart.Types: data ArrowConfig a
+ Chart.Types: data Aspect
+ Chart.Types: data Color
+ Chart.Types: data PixelConfig
+ Chart.Types: data QChart a
+ Chart.Types: data RectConfig
+ Chart.Types: data TextConfig
+ Chart.Types: golden :: Aspect
+ Chart.Types: instance Data.Default.Class.Default (Chart.Types.ArrowConfig GHC.Types.Double)
+ Chart.Types: instance Data.Default.Class.Default Chart.Types.PixelConfig
+ Chart.Types: instance Data.Default.Class.Default Chart.Types.RectConfig
+ Chart.Types: instance Data.Default.Class.Default Chart.Types.TextConfig
+ Chart.Types: instance GHC.Classes.Eq Chart.Types.Color
+ Chart.Types: instance GHC.Show.Show Chart.Types.Color
+ Chart.Types: lineColor :: Lens' LineConfig Color
+ Chart.Types: opac :: Double -> Color -> Color
+ Chart.Types: opacs :: Double -> [Color] -> [Color]
+ Chart.Types: palette :: [Color]
+ Chart.Types: pixelGradient :: Lens' PixelConfig (Range Color)
+ Chart.Types: pixelGrain :: Lens' PixelConfig (V2 Int)
+ Chart.Types: rectBorderColor :: Lens' RectConfig Color
+ Chart.Types: rectBorderWidth :: Lens' RectConfig Double
+ Chart.Types: rectColor :: Lens' RectConfig Color
+ Chart.Types: scatterColor :: Lens' ScatterConfig Color
+ Chart.Types: sixbyfour :: Aspect
+ Chart.Types: textBottom :: Lens' TextConfig Double
+ Chart.Types: textColor :: Lens' TextConfig Color
+ Chart.Types: textOrientation :: Lens' TextConfig Orientation
+ Chart.Types: textPad :: Lens' TextConfig Double
+ Chart.Types: textPlacement :: Lens' TextConfig Placement
+ Chart.Types: textRight :: Lens' TextConfig Double
+ Chart.Types: textSize :: Lens' TextConfig Double
+ Chart.Types: type Chart' a = (Renderable (Path V2 Double) a, Renderable (Text Double) a) => QDiagram a V2 Double Any
+ Chart.Types: uncolor :: AlphaColour Double -> Color
+ Chart.Types: widescreen :: Aspect
+ Chart.Unit: arrow1 :: (Traversable f) => ArrowConfig Double -> f (V4 Double) -> Chart b
+ Chart.Unit: arrowChart :: (Traversable f) => ArrowConfig Double -> V4 (Range Double) -> f (V4 Double) -> Chart a
+ Chart.Unit: axes :: ChartConfig -> Chart' a
+ Chart.Unit: blob :: (Floating (N a), Ord (N a), Typeable (N a), HasStyle a, V a ~ V2) => Color -> a -> a
+ Chart.Unit: box :: (Field (N t), N t ~ Double, V t ~ V2, HasOrigin t, Transformable t, TrailLike t) => Rect Double -> t
+ Chart.Unit: bubble :: forall a. (FromInteger (N a), MultiplicativeGroup (N a), RealFloat (N a), Traced a, V a ~ V2) => [a] -> Int -> [V a (N a)]
+ Chart.Unit: combine :: Aspect -> [QChart a] -> Chart' a
+ Chart.Unit: fileSvg :: FilePath -> (Double, Double) -> Chart SVG -> IO ()
+ Chart.Unit: histChart :: (Traversable f) => [RectConfig] -> Aspect -> [f (Rect Double)] -> Chart a
+ Chart.Unit: histCompare :: DealOvers -> Histogram -> Histogram -> Chart' a
+ Chart.Unit: line1 :: (Traversable f, R2 r) => LineConfig -> f (r Double) -> Chart b
+ Chart.Unit: lineChart :: (R2 r, Traversable f) => [LineConfig] -> Aspect -> [f (r Double)] -> Chart a
+ Chart.Unit: pixel1 :: (Traversable f) => f (Rect Double, Color) -> Chart b
+ Chart.Unit: pixelf :: PixelConfig -> Aspect -> Rect Double -> (V2 Double -> Double) -> Chart a
+ Chart.Unit: rangeV4 :: (Traversable f) => f (V4 Double) -> V4 (Range Double)
+ Chart.Unit: rangeV42Rect :: V4 (Range Double) -> Rect Double
+ Chart.Unit: rect1 :: (Traversable f) => RectConfig -> f (Rect Double) -> Chart b
+ Chart.Unit: rescalePixels :: Rect Double -> [(Rect Double, Color)] -> [(Rect Double, Color)]
+ Chart.Unit: scaleV4s :: (Traversable f) => V4 (Range Double) -> f (V4 Double) -> f (V4 Double)
+ Chart.Unit: scaleX :: Double -> [Point V2 Double] -> [Point V2 Double]
+ Chart.Unit: scaleY :: Double -> [Point V2 Double] -> [Point V2 Double]
+ Chart.Unit: scatter1 :: (Traversable f, R2 r) => ScatterConfig -> f (r Double) -> Chart b
+ Chart.Unit: scatterChart :: (R2 r, Traversable f) => [ScatterConfig] -> Aspect -> [f (r Double)] -> Chart a
+ Chart.Unit: toPixels :: Rect Double -> (V2 Double -> Double) -> PixelConfig -> [(Rect Double, Color)]
+ Chart.Unit: withChart :: (Traversable f, R2 r) => ChartConfig -> (Aspect -> [f (r Double)] -> QDiagram a V2 Double Any) -> [f (r Double)] -> Chart' a
- Chart.Types: AxisConfig :: Orientation -> Placement -> Double -> AlphaColour Double -> Double -> AlphaColour Double -> Double -> Double -> AlphaColour Double -> TickStyle -> Double -> Double -> AxisConfig
+ Chart.Types: AxisConfig :: Double -> Orientation -> Placement -> Double -> Color -> Double -> Color -> Double -> Double -> Double -> Color -> TickStyle -> Double -> Double -> AxisConfig
- Chart.Types: ChartConfig :: Double -> AlphaColour Double -> [AxisConfig] -> ChartConfig
+ Chart.Types: ChartConfig :: Double -> [AxisConfig] -> Maybe (Rect Double) -> Aspect -> Color -> ChartConfig
- Chart.Types: LineConfig :: ChartConfig -> Double -> LineConfig
+ Chart.Types: LineConfig :: Double -> Color -> LineConfig
- Chart.Types: ScatterConfig :: ChartConfig -> Double -> ScatterConfig
+ Chart.Types: ScatterConfig :: Double -> Color -> ScatterConfig
- Chart.Types: [_axisColor] :: AxisConfig -> AlphaColour Double
+ Chart.Types: [_axisColor] :: AxisConfig -> Color
- Chart.Types: [_axisMarkColor] :: AxisConfig -> AlphaColour Double
+ Chart.Types: [_axisMarkColor] :: AxisConfig -> Color
- Chart.Types: [_axisTextColor] :: AxisConfig -> AlphaColour Double
+ Chart.Types: [_axisTextColor] :: AxisConfig -> Color
- Chart.Types: axisColor :: Lens' AxisConfig (AlphaColour Double)
+ Chart.Types: axisColor :: Lens' AxisConfig Color
- Chart.Types: axisMarkColor :: Lens' AxisConfig (AlphaColour Double)
+ Chart.Types: axisMarkColor :: Lens' AxisConfig Color
- Chart.Types: axisTextColor :: Lens' AxisConfig (AlphaColour Double)
+ Chart.Types: axisTextColor :: Lens' AxisConfig Color

Files

chart-unit.cabal view
@@ -1,11 +1,11 @@ name:   chart-unit version:-  0.1.0.0+  0.3.0 synopsis:   A set of native haskell charts. description:-  readme.lhs+  See https://tonyday567.github.io/chart-unit/index.html for some description. category:   charts homepage:@@ -29,6 +29,7 @@   hs-source-dirs:     src   exposed-modules:+    Chart,     Chart.Unit,     Chart.Types   ghc-options:@@ -43,12 +44,16 @@     diagrams-core,     diagrams-lib,     diagrams-svg,-    diagrams-rasterific,     foldl,+    formatting,     lens,-    primitive,+    linear,+    numhask >= 0.0.4 && < 1,+    numhask-range >= 0.0.2 && < 1,     protolude,-    text+    text,+    QuickCheck,+    SVGFonts   default-language:     Haskell2010   default-extensions:@@ -85,34 +90,104 @@     TypeFamilies,     TypeOperators -executable readme+executable chart-unit-examples   default-language:     Haskell2010   ghc-options:     -funbox-strict-fields     -fforce-recomp-    -threaded-    -rtsopts-    -with-rtsopts=-N   hs-source-dirs:-    ./+    examples   main-is:-    readme.lhs+    examples.hs+  other-modules:+    FakeData   build-depends:     base >= 4.7 && < 5,     chart-unit,+    protolude,     containers,     diagrams,     diagrams-core,     diagrams-lib,-    diagrams-rasterific,     diagrams-svg,+    linear,     foldl,     lens,+    text,+    formatting,+    SVGFonts,+    numhask,+    numhask-range,+    -- for data examples+    mwc-random,+    mwc-probability,     primitive,+    ad,+    ListLike,+    reflection,+    tdigest+    -- diagrams-rasterific++  default-extensions:+    NoImplicitPrelude,+    UnicodeSyntax,+    BangPatterns,+    BinaryLiterals,+    DeriveFoldable,+    DeriveFunctor,+    DeriveGeneric,+    DeriveTraversable,+    DisambiguateRecordFields,+    EmptyCase,+    FlexibleContexts,+    FlexibleInstances,+    FunctionalDependencies,+    GADTSyntax,+    InstanceSigs,+    KindSignatures,+    LambdaCase,+    MonadComprehensions,+    MultiParamTypeClasses,+    MultiWayIf,+    NegativeLiterals,+    OverloadedStrings,+    ParallelListComp,+    PartialTypeSignatures,+    PatternSynonyms,+    RankNTypes,+    RecordWildCards,+    RecursiveDo,+    ScopedTypeVariables,+    TupleSections,+    TypeFamilies,+    TypeOperators++test-suite test+  default-language:+    Haskell2010+  type:+    exitcode-stdio-1.0+  hs-source-dirs:+    test+  main-is:+    test.hs+  build-depends:+    base >= 4.7 && < 5,+    HUnit,+    QuickCheck,+    chart-unit,     protolude,-    random-fu,-    text+    smallcheck,+    tasty,+    tasty-hunit,+    tasty-hspec,+    tasty-quickcheck,+    tasty-smallcheck,+    numhask,+    numhask-range,+    data-default,+    diagrams-lib   default-extensions:     NoImplicitPrelude,     UnicodeSyntax,
+ examples/FakeData.hs view
@@ -0,0 +1,118 @@+{-+various fake data+-}++{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}+{-# LANGUAGE DataKinds #-}++module FakeData where++import Chart+import NumHask.Prelude++import Control.Monad.Primitive (PrimState)+import Data.Reflection+import Numeric.AD+import Numeric.AD.Internal.Reverse+import System.Random.MWC+import System.Random.MWC.Probability+import qualified Control.Foldl as L+import qualified Protolude as P+import Data.TDigest++{-+Standard normal random variates in one dimension.+-}++rvs :: Gen (PrimState IO) -> Int -> IO [Double]+rvs gen n = samples n standard gen++{-+This generates n V2 random variates where the x and y parts are correlated.+-}++rvsCorr :: Gen (PrimState IO) -> Int -> Double -> IO [V2 Double]+rvsCorr gen n c = do+  s0 <- rvs gen n+  s1 <- rvs gen n+  let s1' = zipWith (\x y -> c * x + sqrt (1 - c * c) * y) s0 s1+  pure $ zipWith V2 s0 s1'++mkScatterData :: IO [[V2 Double]]+mkScatterData = do+    g <- create+    xys <- rvsCorr g 1000 0.7+    xys1 <- rvsCorr g 1000 -0.5+    pure [ over _y (+1) . over _x (\x -> x^^2 + 3*x - 1) <$> xys+         , over _x (\x -> x^^2 + 3*x + 1) <$> xys1]++makeHist :: Int -> [Double] -> [Rect Double]+makeHist n xs = fromHist (IncludeOvers 1) (fill cuts xs)+  where+    r = Chart.range xs+    cuts = linearSpace OuterPos r n++makeRvs :: IO [[Double]]+makeRvs = do+    g <- create+    xys <- rvs g 1000+    xys1 <- rvs g 1000+    pure [xys, (1.5*) <$> xys1]++mkHistData :: IO [[Rect Double]]+mkHistData = do+    d0 <- makeRvs+    pure $ makeHist 30 <$> d0 ++mkHistogramData :: IO [Histogram]+mkHistogramData = do+    d0 <- makeRvs+    let cuts = linearSpace OuterPos (Range (-3.0,3.0)) 6+    pure $ fill cuts  <$> d0++makeRectQuantiles :: Double -> IO [Rect Double]+makeRectQuantiles n = do+    vs <- makeQuantiles n+    let begin = ([],Nothing)+    let step :: ([V4 Double],Maybe Double) -> Double -> ([V4 Double],Maybe Double)+        step (_,   Nothing) a = ([], Just a)+        step (acc, Just l)  a = (acc <> [V4 l 0 a (0.1/(a-l))], Just a)+    let h = L.fold (L.Fold step begin fst) vs+    pure $ view rect <$> h++makeQuantiles :: Double -> IO [Double]+makeQuantiles n = do+    g <- create+    xs <- rvs g 100000+    let qs = ((1/n)*) <$> [0..n]+    let vs = L.fold (tDigestQuantiles qs) xs+    pure vs++tDigestQuantiles :: [Double] -> L.Fold Double [Double]+tDigestQuantiles qs = L.Fold step begin done+  where+    step x a = Data.TDigest.insert a x+    begin = tdigest ([]::[Double]) :: TDigest 25+    done x = fromMaybe nan . (`quantile` compress x) <$> qs++arrowData :: [V4 Double]+arrowData = zipWith (\(V2 x y) (V2 z w) -> V4 x y z w) pos dir'+  where+    pos = gridP OuterPos (Rect (V2 (-1 ... 1) (-1 ... 1))) (V2 20 20)+    dir' = gradF rosenbrock 0.01 <$> pos++gradF ::+    (forall s. (Reifies s Tape) => [Reverse s Double] -> Reverse s Double) ->+    Double ->+    V2 Double ->+    V2 Double+gradF f step (V2 x y) =+    - r2 ((\[x',y'] -> (x',y')) $+          gradWith (\x0 x1 -> x0 + (x1 - x0) * step) f [x,y])++rosenbrock :: (Num a) => [a] -> a+rosenbrock [] = 0+rosenbrock [x] = 100 P.* (P.negate x P.^ 2) P.^ 2 P.+ (x P.- 1) P.^ 2+rosenbrock (x:y:_) = 100 P.* (y P.- x P.^ 2) P.^ 2 P.+ (x P.- 1) P.^ 2+
+ examples/examples.hs view
@@ -0,0 +1,328 @@+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}++import Chart+import NumHask.Prelude++import FakeData++import qualified Control.Foldl as L+import Data.List ((!!))+import Data.Text (pack)++scratch :: Chart SVG -> IO ()+scratch = fileSvg "other/scratchpad.svg" (600,400)++exampleBox :: Chart a+exampleBox = box one++exampleAxes :: Chart' a+exampleAxes = axes def++exampleEmpty :: Chart' a+exampleEmpty =+    withChart def (\_ _ -> mempty) [corners one]++exampleGrid :: Chart a+exampleGrid =+    scatterChart+    [ScatterConfig 0.01 (palette!!4)]+    sixbyfour+    [V2 <$> [0..10] <*> [0..10]]++lineDefs :: [LineConfig]+lineDefs =+    [ LineConfig 0.01 (Color 0.945 0.345 0.329 0.8)+    , LineConfig 0.02 (Color 0.698 0.569 0.184 0.5)+    , LineConfig 0.005 (Color 0.5 0.5 0.5 1.0)+    ]++lineData :: [[V2 Double]]+lineData =+    fmap r2 <$>+    [ [(0.0,1.0),(1.0,1.0),(2.0,5.0)]+    , [(0.0,0.0),(3.0,3.0)]+    , [(0.5,4.0),(0.5,0)]+    ]++exampleLine :: Chart a+exampleLine = lineChart lineDefs sixbyfour lineData++exampleLineAxes :: Chart' a+exampleLineAxes =+    lineChart lineDefs sixbyfour lineData <>+    axes (chartRange .~ Just (rangeR2s lineData) $ def)++exampleLineAxes2 :: Chart' a+exampleLineAxes2 =+    withChart def (lineChart lineDefs) lineData++scatterDefs :: [ScatterConfig]+scatterDefs = zipWith ScatterConfig [0.01,0.02,0.03] (opacs 0.5 palette)++exampleScatter :: [[V2 Double]] -> Chart' a+exampleScatter xys =+    withChart (chartAspect .~ asquare $ def) (scatterChart scatterDefs) xys++exampleScatter2 :: [[V2 Double]] -> Chart' a+exampleScatter2 xys =+    withChart (chartAspect .~ asquare $ def) (scatterChart scatterDefs) xys1+  where+    xys1 = fmap (over _x (*1e8) . over _y (*1e-8)) <$> xys++histDefs :: [RectConfig]+histDefs =+    [ def+    , rectBorderColor .~ Color 0 0 0 0+      $ rectColor .~ Color 0.333 0.333 0.333 0.4+      $ def+    ]++-- | withCharts doesn't work with a V4 where w and z effect the XY plane range+exampleHist :: [[Rect Double]] -> Chart' a+exampleHist rs =+    histChart histDefs widescreen rs <>+    axes+    ( chartRange .~ Just (fold . fold $ rs)+    $ chartAspect .~ widescreen+    $ def)++exampleHistGrey :: [Rect Double] -> Chart' a+exampleHistGrey xys =+    histChart [histDefs!!1] widescreen [xys] <>+    axes+    ( chartRange .~ Just (fold xys)+    $ chartAspect .~ widescreen+    $ def)++exampleHistCompare :: DealOvers -> Histogram -> Histogram -> Chart' a+exampleHistCompare o h1 h2 =+    let h = fromHist o h1+        h' = fromHist o h2+        h'' = zipWith (\(Rect (V2 (Range (x,y)) (Range (z,w)))) (Rect (V2 _ (Range (_,w')))) -> Rect (V2 (Range (x,y)) (Range (z,w-w')))) h h'+        flat = Aspect $ Rect (V2 (Range (-0.75,0.75)) (Range (-0.25,0.25)))+    in+      pad 1.1 $+        beside (r2 (0,-1)) (histChart+        [ def+        , rectBorderColor .~ Color 0 0 0 0+        $ rectColor .~ Color 0.333 0.333 0.333 0.1+        $ def ] sixbyfour [h,h'] <>+        axes (ChartConfig 1.1+              [def]+              (Just (fold $ fold [h,h']))+              sixbyfour (uncolor transparent)))+        (histChart+        [ rectBorderColor .~ Color 0 0 0 0+        $ rectColor .~ Color 0.888 0.333 0.333 0.8+        $ def ] flat [h''] <>+        axes (ChartConfig 1.1+              [ axisAlignedTextBottom .~ 0.65 $+                axisAlignedTextRight .~ 1 $+                axisOrientation .~ Y $+                axisPlacement .~ AxisLeft $+                def+              ]+              (Just (fold h''))+              flat (uncolor transparent)))++exampleHistGrey2 :: [Rect Double] -> Chart' a+exampleHistGrey2 rs =+    lineChart lineDefs widescreen+    [(\(Rect (V2 (Range (x,z)) (Range (_,w)))) -> V2 ((x+z)/(one+one)) w) <$>+      rs] <>+    axes+    ( chartRange .~ Just (fold rs)+    $ chartAspect .~ widescreen+    $ def)++exampleLabelledBar :: Chart' a+exampleLabelledBar =+    histChart [def]+    sixbyfour+    [rs] <>+    axes+    ( chartAxes .~+      [ axisTickStyle .~+        TickLabels labels' $ def+      ]+      $ chartAspect .~ sixbyfour+      $ chartRange .~ Just (fold rs)+      $ def+    )+  where+    labels' = fmap pack <$> take 10 $ (:[]) <$> ['a'..]+    rs :: [Rect Double]+    rs = abs . view rect <$>+        zipWith4 V4 [0..10] (replicate 11 0) [1..11] [1,2,3,5,8,0,-2,11,2,1]++exampleArrow :: [V4 Double] -> Chart' a+exampleArrow xs =+    arrowChart def (V4 one one one one) xs <>+    axes ( chartRange .~ Just+           ( rangeV42Rect $ rangeV4 $ scaleV4s (V4 one one one one) xs)+           $ chartAspect .~ asquare $ def)++-- clipping+exampleClipping :: Chart a+exampleClipping =+    L.fold (L.Fold step1 mempty identity) $+    (\y -> L.fold (L.Fold step mempty identity)+      (Rect . (`V2` y) <$> qb)) <$>+    qb+  where+    step x a = beside (r2 (1,0)) x (pad 1.05 $ center $ blob (Color 0 0 0 0.02) box a <> clip ch a)+    step1 x a = beside (r2 (0,1)) x (pad 1.05 $ center a)+    qs = fromIntegral <$> ([0..4] :: [Int]) :: [Double]+    qb = (\x -> (-0.5 + x*0.2) ... (-0.5 + (x+1.0)*0.2)) <$> qs+    clip ch1 sq = clipped (pathFromLocTrail $ box sq) ch1+    ch = lineChart lineDefs asquare lineData++-- compound charts+exampleCompound :: IO [QChart a]+exampleCompound = do+    xys <- mkScatterData+    let qsc = QChart (scatterChart scatterDefs) (rangeR2s xys) xys+    let xy0 = rangeR2s $ lineData <> xys+    pure [qsc, qaxes xy0, qline]+  where+      qline = QChart (lineChart lineDefs) (rangeR2s lineData) lineData+      qaxes xy =+          QChart+          (\a _ -> axes+            ( chartRange .~ Just xy+            $ chartAspect .~ a+            $ def)) xy ()++exampleScatterHist :: [[V2 Double]] -> Chart' a+exampleScatterHist xys =+    beside (r2 (1,0))+    (beside (r2 (0,-1))+    (sc1 <> axes1)+    (reflectY histx))+    (reflectY $ rotateBy (3/4) histy)+  where+    sc1 = scatterChart [scatterColor .~ Color 0.365 0.647 0.855 0.1 $ def, def] asquare xys+    histx = histChart defHist xyHist hx+    histy = histChart defHist xyHist hy+    defHist =+        [ def+        , rectBorderColor .~ Color 0 0 0 0+          $ rectColor .~ Color 0.333 0.333 0.333 0.4+          $ def+        ]+    xyHist = Aspect (Rect (V2 one ((0.2*)<$>one)))+    hx = makeHist 50 . fmap (view _x) <$> xys+    hy = makeHist 50 . fmap (view _y) <$> xys+    axes2 =+        [ axisAlignedTextBottom .~ 0.65 $+          axisAlignedTextRight .~ 0.5 $+          axisLabelStrut .~ 0.04 $+          axisOrientation .~ X $+          axisPlacement .~ AxisTop $+          def,+          axisInsideStrut .~ 0.05 $+          axisAlignedTextBottom .~ 0.65 $+          axisAlignedTextRight .~ 1 $+          axisOrientation .~ Y $+          axisPlacement .~ AxisLeft $+          def]+    axes1 =+        axes+        (ChartConfig 1 axes2 (Just $ rangeR2s xys) asquare (Color 0.5 0.5 1 0.04))++exampleGgplot :: Chart' a+exampleGgplot =+    lineChart (repeat (LineConfig 0.002 (Color 0.98 0.98 0.98 1))) sixbyfour (gridX <> gridY) <>+    blob (Color 0.92 0.92 0.92 1) (box (Rect (V2 ((1.5*) <$> one) one))) <>+    axes ggdef+  where+    gridX = (\x -> [V2 0 x, V2 10 x]) . fromIntegral <$> ([0..10] :: [Int])+    gridY = (\x -> [V2 x 0, V2 x 10]) . fromIntegral <$> ([0..10] :: [Int])+    ggdef =+        ChartConfig+        1.1+        [defx]+        (Just $ Rect (V2 (0 ... 10) (0 ... 10)))+        sixbyfour+        (Color 1 1 1 0)+    defx =+        AxisConfig+        1.0+        X+        AxisBottom+        0+        (Color 0 0 0 0.2)+        0.018+        (Color 0 0 0 1)+        0+        0.01+        0.04+        (Color 0.2 0.2 0.2 0.7)+        (TickRound 5)+        0.5+        1++examplePixels :: Chart' a+examplePixels =+    pixelf+    (pixelGrain .~ V2 10 10 $ def)+    asquare (Rect (V2 (-1 ... 1) (-1 ... 1)))+    (\(V2 x y) -> x*y+x*x) <>+    axes+    ( chartRange .~ Just (Rect (V2 (-1 ... 1) (-1 ... 1)))+    $ chartAspect .~ asquare+    $ def)++makeOneDim :: IO [[(Double,Text)]]+makeOneDim = do+    qs <- makeQuantiles 20+    qs5 <- makeQuantiles 4+    let labels5 = ["min","3rd Q","median","1st Q","max"]+    pure [zip qs5 labels5, zip qs (repeat "")]++exampleOneDim :: [[(Double,Text)]] -> Chart' a+exampleOneDim qss =+    axes (ChartConfig 1.1 [def] (Just (Rect (V2 (range $ fst <$> (qss!!0)) (Range (0,0))))) skinny (uncolor transparent)) <>+    axes (ChartConfig 1.1 [axisColor .~ uncolor transparent $ axisMarkSize .~ 0.05 $ axisMarkColor .~ uncolor (withOpacity red 0.3) $ axisTickStyle .~ TickPlaced (qss!!1) $ def] (Just (Rect (V2 (range $ fst <$> (qss!!1)) (Range (0,0))))) skinny (uncolor transparent)) <>+    axes (ChartConfig 1.1 [axisColor .~ uncolor transparent $ axisMarkSize .~ 0.1 $ axisTextSize .~ 0.08 $ axisMarkColor .~ uncolor (withOpacity black 0.5) $ axisTickStyle .~ TickPlaced (qss!!0) $ def] (Just (Rect (V2 (range $ fst <$> (qss!!0)) (Range (0,0))))) skinny (uncolor transparent))+  where+    skinny = Aspect (Rect (V2 ((5*) <$> one) one))++main :: IO ()+main = do+  let sOne = (400,400)+  let s6by4 = (600,400)+  let sWide = (750,250)++  fileSvg "other/exampleBox.svg" s6by4 exampleBox+  fileSvg "other/exampleAxes.svg" s6by4 exampleAxes+  fileSvg "other/exampleEmpty.svg" s6by4 exampleEmpty+  fileSvg "other/exampleGgplot.svg" sWide exampleGgplot+  fileSvg "other/exampleGrid.svg" sOne exampleGrid+  fileSvg "other/exampleLine.svg" s6by4 exampleLine+  fileSvg "other/exampleLineAxes.svg" s6by4 exampleLineAxes+  fileSvg "other/exampleLineAxes2.svg" s6by4 exampleLineAxes2+  xys <- mkScatterData+  fileSvg "other/exampleScatter.svg" sOne (exampleScatter xys)+  fileSvg "other/exampleScatter2.svg" sOne (exampleScatter2 xys)+  xs <- mkHistData+  fileSvg "other/exampleHist.svg" sWide (exampleHist xs)+  rectqs <- makeRectQuantiles 50+  fileSvg "other/exampleHistUnequal.svg" sWide (exampleHistGrey rectqs)+  fileSvg "other/exampleHistUnequal2.svg" sWide (exampleHistGrey2 rectqs)+  hs <- mkHistogramData+  fileSvg "other/exampleHistCompare.svg" s6by4 (exampleHistCompare (IncludeOvers 1) (hs!!0) (hs!!1))+  fileSvg "other/exampleScatterHist.svg" sOne (pad 1.1 $ center $ exampleScatterHist xys)+  fileSvg "other/exampleLabelledBar.svg" s6by4 exampleLabelledBar+  fileSvg "other/exampleArrow.svg" sOne (exampleArrow arrowData)+  exc <- exampleCompound+  fileSvg "other/exampleCompound.svg" s6by4 (pad 1.1 $ center $ combine golden exc)+  fileSvg "other/exampleClipping.svg" sOne exampleClipping+  fileSvg "other/examplePixels.svg" sOne examplePixels+  qss <- makeOneDim+  fileSvg "other/exampleOneDim.svg" (750,150) (exampleOneDim qss)++  scratch (exampleHistCompare (IncludeOvers 1) (hs!!0) (hs!!1))+  -- fileSvg "other/scratchpad.svg" (600,150) $ pad 1.1 $+  --     showOrigin $ exampleOneDim qss
− readme.lhs
@@ -1,226 +0,0 @@-```include-other/header.md-```--[chart-unit](https://tonyday567.github.io/chart-unit.html) [![Build Status](https://travis-ci.org/tonyday567/chart-unit.png)](https://travis-ci.org/tonyday567/chart-unit)-===--scratchpad------My newest chart `padsvg $ linesXY def [[(0,0),(1,1)],[(0,0),(1,2)]]`--![](other/scratchpad.svg)--This slowly growing collection of charts:--- renders nicely over a wide chart size range, svg and png formats.-- render similarly at different scale-- are opinionated minimalism-- are unit shapes in the spirit of the [diagrams](http://projects.haskell.org/diagrams/doc/quickstart.html) design space.-- can be quickly integrated into ad-hoc haskell data analytics, providing a visual feedback loop.--charts------Scatter--![](other/scatter.svg)--Histogram--![](other/hist.svg)--Line--![](other/line.svg)--Lines--![](other/lines.svg)--Labelled Bar Chart--![](other/bar.svg)--rasterific png renders------![](other/scratchpad.png)--Scatter--![](other/scatter.png)--Histogram--![](other/hist.png)--Line--![](other/line.png)--Lines--![](other/lines.png)--Labelled Bar Chart--![](other/bar.png)--> {-# OPTIONS_GHC -Wall #-}-> {-# OPTIONS_GHC -fno-warn-type-defaults #-}-> {-# OPTIONS_GHC -fno-warn-missing-signatures #-}-> import Protolude-> import Control.Monad.Primitive (unsafeInlineIO)-> import Diagrams.Prelude hiding ((<>))-> import qualified Control.Foldl as L-> import qualified Data.Random as R-> import qualified Data.Map.Strict as Map-> import qualified Data.Text as Text->-> import Chart.Unit--some test data------Standard normal random variates.  Called ys to distinguish from the horizontal axis of the chart (xs) which are often implicitly [0..]--> ys :: Int -> IO [Double]-> ys n =->   replicateM n $ R.runRVar R.stdNormal R.StdRandom->--A bunch of ys, accumulated.--> yss :: (Int, Int) -> [[Double]]-> yss (n,m) = unsafeInlineIO $ do->   yss' <- replicateM m $ ys n->   pure $ (drop 1 . L.scan L.sum) <$> yss'->--xys is a list of X,Y pairs, correlated normal random variates to add some shape to chart examples.--> rXYs :: Int -> Double -> [(Double,Double)]-> rXYs n c = unsafeInlineIO $ do->   s0 <- replicateM n $ R.runRVar R.stdNormal R.StdRandom->   s1 <- replicateM n $ R.runRVar R.stdNormal R.StdRandom->   let s1' = zipWith (\x y -> c * x + sqrt (1 - c * c) * y) s0 s1->   pure $ zip s0 s1'->-> xys = rXYs 1000 0.8->--XY random walk--> rwxy = L.scan (L.Fold (\(x,y) (x',y') -> (x+x',y+y')) (0.0,0.0) identity) (take 100 xys)->--xysHist is a histogram of 10000 one-dim random normals.--The data out is a (X,Y) pair list, with mid-point of the bucket as X, and bucket count as Y.--> xysHist :: [(Double,Double)]-> xysHist = unsafeInlineIO $ do->   ys' <- replicateM 10000 $ R.runRVar R.stdNormal R.StdRandom :: IO [Double]->   let (f,s,n) = mkTicks' (range1D ys') 100->   let cuts = (\x -> f+s*fromIntegral x) <$> [0..n]->   let mids = (+(s/2)) <$> cuts->   let count = L.Fold (\x a -> Map.insertWith (+) a 1 x) Map.empty identity->   let countBool = L.Fold (\x a -> x + if a then 1 else 0) 0 identity->   let histMap = L.fold count $ (\x -> L.fold countBool (fmap (x >) cuts)) <$> ys'->   let histList = (\x -> Map.findWithDefault 0 x histMap) <$> [0..n]->   return (zip mids (fromIntegral <$> histList))->--Scale Robustness------xys rendered on the XY plane as dots - a scatter chart with no axes - is invariant to scale.  The data could be multiplied by any scalar, and look exactly the same.--![](other/dots.svg)--Axes break this scale invariance. Ticks and tick labels can hide this to some extent and look almost the same across scales.--![](other/scatter.svg)--This chart will look the same on a data scale change, except for tick magnitudes.--main------>-> main :: IO ()-> main = do- -See develop section below for my workflow.-->   padsvg $->       linesXY def [[(0,0),(1,1)],[(0,0),(1,2)]]->   fileSvg "other/line.svg" (200,200) $->     (lineXY def rwxy)->   filePng "other/line.png" (200,200) $->     (lineXY def rwxy)->   fileSvg "other/lines.svg" (200,200) $->     (linesXY def $ zip [0..] <$> yss (1000, 10))->   filePng "other/lines.png" (200,200) $->     (linesXY def $ zip [0..] <$> yss (1000, 10))->   fileSvg "other/dots.svg" (200,200) $->     (scatter def xys)->   filePng "other/dots.png" (200,200) $->     (scatter def xys)->   fileSvg "other/scatter.svg" (200,200) $->     (scatterXY def xys)->   filePng "other/scatter.png" (200,200) $->     (scatterXY def xys)->   fileSvg "other/bar.svg" (200,200) $->     barLabelled def (unsafeInlineIO $ ys 10) (fmap Text.pack <$> take 10 $ (:[]) <$> ['a'..])->   filePng "other/bar.png" (200,200) $->     barLabelled def (unsafeInlineIO $ ys 10) (fmap Text.pack <$> take 10 $ (:[]) <$> ['a'..])->   fileSvg "other/hist.svg" (200,200) $->     barRange def xysHist->   filePng "other/hist.png" (200,200) $->     barRange def xysHist--diagrams development recipe------In constructing new `units`:--- diagrams go from abstract to concrete-- start with the unitSquare: 4 points, 1x1, origin in the center-- work out where the origin should be, given the scaling needed.-- turn the pointful shape into a Trail-- close the Trail into a SVG-like loop-- turn the Trail into a QDiagram--You can slide up and down the various diagrams abstraction levels creating transformations at each level.  For example, here's something I use to work at the point level:--> unitp f = unitSquare # f # fromVertices # closeTrail # strokeTrail--workflow------> padsvg :: ChartSvg -> IO ()-> padsvg t =->   fileSvg "other/scratchpad.svg" (400,400) t->-> padpng :: ChartPng -> IO ()-> padpng t =->   filePng "other/scratchpad.png" (400,400) t->--Create a markdown version of readme.lhs:--~~~-pandoc -f markdown+lhs -t html -i readme.lhs -o index.html-~~~--Then fire up an intero session, and use padq to display coding results on-the-fly, mashing the refresh button on a browser pointed to readme.html.--or go for a compilation loop like:--~~~-stack install && readme && pandoc -f markdown+lhs -t html -i readme.lhs -o index.html --mathjax --filter pandoc-include && pandoc -f markdown+lhs -t markdown -i readme.lhs -o readme.md --mathjax --filter pandoc-include-~~~-
+ src/Chart.hs view
@@ -0,0 +1,21 @@+{-# OPTIONS_GHC -Wall #-}++module Chart+  ( module X+  , SVG+  , V4(..)+  , zipWith4+  , module Diagrams.Prelude+  ) where++import Data.List (zipWith4)+import Diagrams.Backend.SVG (SVG)+import Diagrams.Prelude hiding+    ((<>), arrow, Color(..), lineColor, scaleX, scaleY, width, zero, unsnoc, uncons, Additive, Loop, Magma, Metric, getLast, getFirst, normalize, inv, First, Last, (&), Strict, unit, from, to, size, distance, (.-.), (-~), (*.), rect, element, aspect, project, intersection)+import Linear (V4(..))++import NumHask.Range as X+import NumHask.Rect as X+import NumHask.Histogram as X+import Chart.Types as X+import Chart.Unit as X
src/Chart/Types.hs view
@@ -1,137 +1,318 @@+{-# LANGUAGE GADTs #-} {-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# LANGUAGE TemplateHaskell #-} -module Chart.Types where+module Chart.Types+  ( Chart+  , Chart'+  , Aspect(..)+  , aspect+  , asquare+  , sixbyfour+  , golden+  , widescreen+  , QChart(..)+  , Orientation(..)+  , Placement(..)+  , TickStyle(..)+  , Color(..)+  , color+  , uncolor+  , opac+  , opacs+  , palette+  , AxisConfig(..)+  , axisPad+  , axisOrientation+  , axisPlacement+  , axisHeight+  , axisColor+  , axisMarkSize+  , axisMarkColor+  , axisInsideStrut+  , axisLabelStrut+  , axisTextSize+  , axisTextColor+  , axisTickStyle+  , axisAlignedTextRight+  , axisAlignedTextBottom+  , ChartConfig(..)+  , chartPad+  , chartAxes+  , chartRange+  , chartAspect+  , chartCanvasColor+  , LineConfig(..)+  , lineSize+  , Chart.Types.lineColor+  , ScatterConfig(..)+  , scatterSize+  , scatterColor+  , RectConfig(..)+  , rectBorderWidth+  , rectBorderColor+  , rectColor+  , ArrowConfig(..)+  , arrowMinHeadSize+  , arrowMaxHeadSize+  , arrowHeadSize+  , arrowMinStaffLength+  , arrowMaxStaffLength+  , arrowStaffLength+  , arrowMinStaffWidth+  , arrowMaxStaffWidth+  , arrowStaffWidth+  , arrowColor+  , PixelConfig(..)+  , pixelGradient+  , pixelGrain+  , TextConfig+  , textPad+  , textOrientation+  , textPlacement+  , textSize+  , textColor+  , textRight+  , textBottom+  ) where -import Protolude-import Diagrams.Prelude--- import Data.Default-import Diagrams.Backend.SVG (SVG, renderSVG)-import Diagrams.Backend.Rasterific (Rasterific, renderRasterific)+import NumHask.Prelude+import Diagrams.Prelude hiding (Color(..), aspect)+import qualified Diagrams.TwoD.Text+import NumHask.Range+import NumHask.Rect+import Data.Colour -type ChartSvg = QDiagram SVG V2 Double Any-type ChartPng = QDiagram Rasterific V2 Double Any+-- | a Chart has a concrete scale, and combinatory options amount to mappend (on top of) and beside+type Chart a =+    ( Renderable (Path V2 Double) a+    ) =>+    QDiagram a V2 Double Any+ +-- | an alternative synonym where text is involved.+type Chart' a =+    ( Renderable (Path V2 Double) a+    , Renderable (Diagrams.TwoD.Text.Text Double) a+    ) =>+    QDiagram a V2 Double Any -fileSvg ∷ FilePath → (Double, Double) → ChartSvg → IO ()-fileSvg f s = renderSVG f (mkSizeSpec (Just <$> r2 s))+-- | the rendering aspect (or plane) of the chart.  Wrapped to distinguish this from a plain XY+data Aspect = Aspect { unAspect :: Rect Double} -filePng ∷ FilePath → (Double,Double) → ChartPng → IO ()-filePng f s = renderRasterific f (mkSizeSpec (Just <$> r2 s))+aspect :: Double -> Aspect+aspect a = Aspect (Rect (V2 ((a*) <$> one) one)) -rgba :: (Floating a, Ord a) => (a, a, a, a) -> AlphaColour a-rgba (r,g,b,a) = withOpacity (sRGB (r/255) (g/255) (b/255)) a+asquare :: Aspect+asquare = aspect 1 +sixbyfour :: Aspect+sixbyfour = aspect 1.5++golden :: Aspect+golden = aspect 1.61803398875++widescreen :: Aspect+widescreen = aspect 3++-- | The concrete nature of a QDiagram, and a desire to scale data and hud items naturally, a QChart is mostly a late binding of the Aspect that the chart is to be projected on to and the data.+data QChart a = forall b. QChart+    { _qChart :: ( ( Renderable (Diagrams.TwoD.Text.Text Double) a)+                  , Renderable (Path V2 Double) a) =>+                Aspect -> b -> QDiagram a V2 Double Any+    , _qXY :: Rect Double+    , _qData :: b+    }++makeLenses ''QChart+ data Orientation = X | Y  data Placement = AxisLeft | AxisRight | AxisTop | AxisBottom -data TickStyle = TickNone | TickLabels [Text] | TickNumber Int+data TickStyle = TickNone | TickLabels [Text] | TickRound Int | TickExact Int | TickPlaced [(Double,Text)] +data Color =+    Color+    { _red :: Double+    , _green :: Double+    , _blue :: Double+    , _aaa :: Double+    } deriving (Eq, Show)++color ∷ Color → AlphaColour Double+color (Color r g b a)= withOpacity (sRGB r g b) a++uncolor ∷ AlphaColour Double → Color+uncolor c = Color r g b a+  where+    a = alphaChannel c+    (RGB r g b) = toSRGB (Data.Colour.over c black)++palette ∷ [Color]+palette =+    [ Color 0.333 0.333 0.333 1.00 -- grey+    , Color 0.365 0.647 0.855 1.00 -- blue+    , Color 0.980 0.647 0.855 1.00 -- orange+    , Color 0.376 0.741 0.408 1.00 -- green+    , Color 0.945 0.486 0.690 1.00 -- pink+    , Color 0.698 0.569 0.184 1.00 -- brown+    , Color 0.698 0.463 0.698 1.00 -- purple+    , Color 0.871 0.812 0.247 1.00 -- yellow+    , Color 0.945 0.345 0.329 1.00 -- red+    ]++opacs :: Double -> [Color] -> [Color]+opacs t cs = (\(Color r g b o) -> Color r g b (o*t)) <$> cs++opac :: Double -> Color -> Color+opac t (Color r g b o) = Color r g b (o*t)+ data AxisConfig = AxisConfig-  { _axisOrientation :: Orientation-  , _axisPlacement :: Placement-  , _axisHeight :: Double-  , _axisColor :: AlphaColour Double-  , _axisMarkSize :: Double -- mark length-  , _axisMarkColor :: AlphaColour Double-  , _axisStrutSize :: Double -- distance of label from mark-  , _axisTextSize :: Double-  , _axisTextColor :: AlphaColour Double-  , _axisTickStyle :: TickStyle-  , _axisAlignedTextRight :: Double-  , _axisAlignedTextBottom :: Double-  }+    { _axisPad :: Double+    , _axisOrientation :: Orientation+    , _axisPlacement :: Placement+    , _axisHeight :: Double+    , _axisColor :: Color+    , _axisMarkSize :: Double -- mark length+    , _axisMarkColor :: Color+    , _axisInsideStrut :: Double -- distance of axis from plane+    , _axisLabelStrut :: Double -- distance of label from mark+    , _axisTextSize :: Double+    , _axisTextColor :: Color+    , _axisTickStyle :: TickStyle+    , _axisAlignedTextRight :: Double+    , _axisAlignedTextBottom :: Double+    }  instance Default AxisConfig where-  def =-    AxisConfig -    X-    AxisBottom-    0.02-    (rgba(94, 19, 94, 0.5))-    0.02-    (rgba (0, 102, 200, 0.5))-    0.02-    0.04-    (rgba (30,30,30,0.7))-    (TickNumber 10)-    0.5-    1+    def =+        AxisConfig+        1+        X+        AxisBottom+        0.02+        (Color 0.333 0.333 0.333 0.2)+        0.02+        (Color 0.1 0.4 0.8 0.5)+        0.05+        0.02+        0.04+        (Color 0.2 0.2 0.2 0.7)+        (TickRound 8)+        0.5+        1 -makeLenses ''AxisConfig +makeLenses ''AxisConfig  data ChartConfig = ChartConfig-  { _chartPad :: Double-  , _chartColor :: AlphaColour Double-  , _chartAxes :: [AxisConfig]-  }+    { _chartPad :: Double+    , _chartAxes :: [AxisConfig]+    , _chartRange :: Maybe (Rect Double)+    , _chartAspect :: Aspect+    , _chartCanvasColor :: Color+    }  instance Default ChartConfig where-  def =-    ChartConfig-    1.3-    (rgba(128, 128, 128, 0.6))-    [def,-     axisAlignedTextBottom .~ 0.65 $-     axisAlignedTextRight .~ 1 $-     axisOrientation .~ Y $-     axisPlacement .~ AxisLeft $-     def]+    def =+        ChartConfig+        1.3+        [def,+         axisAlignedTextBottom .~ 0.65 $+         axisAlignedTextRight .~ 1 $+         axisOrientation .~ Y $+         axisPlacement .~ AxisLeft $+         def]+        Nothing+        sixbyfour+        (Color 1 1 1 0.02)  makeLenses ''ChartConfig +data LineConfig = LineConfig+    { _lineSize :: Double+    , _lineColor :: Color+    }++instance Default LineConfig where+    def = LineConfig 0.02 (Color 0.365 0.647 0.855 1.00)++makeLenses ''LineConfig+ data ScatterConfig = ScatterConfig-  { _scatterChart :: ChartConfig-  , _scatterSize :: Double-  }+    { _scatterSize :: Double+    , _scatterColor :: Color+    }  instance Default ScatterConfig where-  def = ScatterConfig (chartColor .~ rgba(128, 128, 128, 0.1) $ def) 0.03+    def = ScatterConfig 0.03 (Color 0.33 0.33 0.33 0.2)  makeLenses ''ScatterConfig -data BarConfig = BarConfig-  { _barChart :: ChartConfig-  , _barSep :: Double-  }+data RectConfig = RectConfig+    { _rectBorderWidth :: Double+    , _rectBorderColor :: Color+    , _rectColor :: Color+    } -instance Default BarConfig where-  def = BarConfig (chartColor .~ rgba(59, 89, 152, 0.6) $ def) 0.01+instance Default RectConfig where+    def = RectConfig 1 (Color 0.333 0.333 0.333 0.4) (Color 0.365 0.647 0.855 0.5) -makeLenses ''BarConfig+makeLenses ''RectConfig -data LineConfig = LineConfig-  { _lineChart :: ChartConfig-  , _lineSize :: Double-  }+data ArrowConfig a = ArrowConfig+    { _arrowMinHeadSize :: a+    , _arrowMaxHeadSize :: a+    , _arrowHeadSize :: a+    , _arrowMinStaffLength :: a+    , _arrowMaxStaffLength :: a+    , _arrowStaffLength :: a+    , _arrowMinStaffWidth :: a+    , _arrowMaxStaffWidth :: a+    , _arrowStaffWidth :: a+    , _arrowColor :: Color+    } -instance Default LineConfig where-  def = LineConfig (chartColor .~ rgba(59, 89, 152, 0.6) $ def) 0.001+instance Default (ArrowConfig Double) where+    def = ArrowConfig 0.01 0.05 0.03 0.1 0.1 0.1 0.01 0.005 0.2+          (Color 0.333 0.333 0.888 0.8) -makeLenses ''LineConfig+makeLenses ''ArrowConfig -data OneLine = OneLine-    { _lColor :: AlphaColour Double-    , _lSize :: Double+data PixelConfig =+    PixelConfig+    { _pixelGradient :: Range Color+    , _pixelGrain :: V2 Int     } -instance Default OneLine where-  def = OneLine (rgba(59, 89, 152, 0.6)) 0.001+instance Default PixelConfig where+    def = PixelConfig+        (Range ( Color 1 1 1 1+               , Color 0 0 0 1))+        (V2 20 20) -makeLenses ''OneLine+makeLenses ''PixelConfig -data LinesConfig = LinesConfig-  { _linesChart :: ChartConfig-  , _linesLines :: [OneLine]-  }+data TextConfig = TextConfig+    { _textPad :: Double+    , _textOrientation :: Orientation+    , _textPlacement :: Placement+    , _textSize :: Double+    , _textColor :: Color+    , _textRight :: Double+    , _textBottom :: Double+    } -instance Default LinesConfig where-  def = LinesConfig def [ OneLine (rgba(60,90,150,1)) 0.002-                        , OneLine (rgba(0,128,255,1)) 0.002-                        , OneLine (rgba(255,128,0,1)) 0.002-                        , OneLine (rgba(128,0,255,1)) 0.002-                        , OneLine (rgba(0,255,255,1)) 0.002-                        ]+instance Default TextConfig where+    def =+        TextConfig+        1+        X+        AxisBottom+        0.08+        (Color 0.333 0.333 0.333 0.2)+        0.5+        1 -makeLenses ''LinesConfig+makeLenses ''TextConfig+
src/Chart/Unit.hs view
@@ -1,267 +1,385 @@ {-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} -module Chart.Unit (-    range1D,-    range1Ds,-    unit,-    units,-    unitXY,-    unitsXY,-    chartXY,-    scatter,-    scatterXY,-    barRange,-    barLabelled,-    bars,-    line,-    lines,-    lineXY,-    linesXY,-    axisXY,-    -- toFile,-    mkTicks,-    mkTicks',-    module Chart.Types,-    module Control.Lens,-    module Data.Default-) where+module Chart.Unit+  ( scaleX+  , scaleY+  , blob+  , line1+  , scatter1+  , rect1+  , pixel1+  , arrow1+  , box+  , lineChart+  , scatterChart+  , histChart+  , arrowChart+  , rangeV4+  , rangeV42Rect+  , scaleV4s+  , toPixels+  , rescalePixels+  , pixelf+  , withChart+  , axes+  , combine+  , fileSvg+  , bubble+  -- , text1+  , histCompare+  ) where +import NumHask.Prelude hiding (min,max,from,to,(&))++import NumHask.Range+import NumHask.Rect+import NumHask.Histogram import Chart.Types++import Control.Lens hiding (beside, none, (#), at)+import Data.Ord (max, min)+import Diagrams.Backend.SVG (SVG, renderSVG)+import Diagrams.Prelude hiding (width, unit, D, Color, scale, zero, scaleX, scaleY, aspect, rect, project)+import Formatting+import Linear hiding (zero, identity, unit, project)+ import qualified Control.Foldl as L-import Control.Lens hiding (beside, none, (#))-import Data.Default (def) import qualified Data.Text as Text-import Diagrams.Prelude hiding (unit) -import Protolude hiding (min,max)-import Text.Printf-import qualified Diagrams.TwoD.Text+import qualified Diagrams.Prelude as Diagrams -range1D :: (Fractional t, Ord t, Foldable f) => f t -> (t, t)-range1D = L.fold (L.Fold step initial extract)-  where-    step Nothing x = Just (x,x)-    step (Just (min,max)) x =-      Just (min' x min, max' x max)-    max' x1 x2 = if x1 > x2 then x1 else x2-    min' x1 x2 = if x1 < x2 then x1 else x2-    initial = Nothing-    extract = fromMaybe (-0.5,0.5)+-- | avoiding the scaleX zero throw+eps :: N [Point V2 Double]+eps = 1e-8 -range1Ds :: (Fractional t, Ord t, Foldable f, Foldable f') => f' (f t) -> (t, t)-range1Ds xss = L.fold (L.Fold step initial extract) xss-  where-    step Nothing x = Just (range1D x)-    step (Just (min, max)) x =-      Just (min', max')-      where-        (min'', max'') = range1D x-        min' = if min'' < min then min'' else min-        max' = if max'' > max then max'' else max-    initial = Nothing-    extract = fromMaybe (-0.5,0.5)+scaleX :: Double -> [Point V2 Double] -> [Point V2 Double]+scaleX s = Diagrams.scaleX (if s==zero then eps else s) --- unit scales and translates to a [-0.5,0.5] range-unit :: (Fractional b, Functor f, Ord b, Foldable f) => f b -> f b-unit xs =-  let (minX,maxX) = range1D xs in-  (\x -> (x-minX)/(maxX-minX) - 0.5) <$> xs+scaleY :: Double -> [Point V2 Double] -> [Point V2 Double]+scaleY s = Diagrams.scaleY (if s==zero then eps else s) --- units scales multiple xs to a common range-units :: (Fractional b, Functor f, Ord b, Foldable f, Functor f', Foldable f') =>-    f' (f b) -> f' (f b)-units xss =-  let (minX,maxX) = range1Ds xss in-  (fmap (\x -> (x-minX)/(maxX-minX) - 0.5)) <$> xss+-- * chartlets are recipes for constructing QDiagrams from traversable containers of vectors and a configuration+-- a solid blob (shape) with a colour fill and no border+blob ∷ (Floating (N a), Ord (N a), Typeable (N a), HasStyle a, V a ~ V2) ⇒+    Chart.Types.Color → a → a+blob c = fcA (color c) # lcA (withOpacity black 0) # lw none --- scale 2d points (XY) to ((-0.5,-0.5), (0.5,0.5))-unitXY :: (Fractional b, Fractional a, Ord b, Ord a) => [(a, b)] -> [(a, b)]-unitXY xys = zip (unit $ fst <$> xys) (unit $ snd <$> xys)+-- a line is just a scatter chart rendered with a line+-- (and with a usually stable x-value series)+line1 ∷ (Traversable f, R2 r) => LineConfig → f (r Double) → Chart b+line1 (LineConfig s c) ps = case NumHask.Prelude.head ps of+  Nothing -> mempty+  Just p0 -> stroke (trailFromVertices (toList $ (p2 . unr2) . view _xy <$> ps)+              `at`+              p2 (unr2 (view _xy p0))) # lcA (color c) # lwN s --- scale multiple 2d series-unitsXY :: (Fractional a, Ord a, Fractional b, Ord b) => [[(a, b)]] -> [[(a, b)]]-unitsXY xyss = zipWith (\x y -> zip x y) xs' ys'-  where-    xs = fmap fst <$> xyss-    ys = fmap snd <$> xyss-    xs' = units xs-    ys' = units ys+-- dots on the XY plane+scatter1 ∷ (Traversable f, R2 r) => ScatterConfig → f (r Double) → Chart b+scatter1 (ScatterConfig s c) ps =+  atPoints (toList $ (p2 . unr2) . view _xy <$> ps)+    (repeat $ circle s #+     blob c+    ) -chartXY :: (Renderable (Path V2 Double) a, (Renderable (Diagrams.TwoD.Text.Text Double) a)) => ChartConfig-  -> ([(Double, Double)] -> QDiagram a V2 Double Any)-  -> [(Double, Double)]-  -> QDiagram a V2 Double Any-chartXY (ChartConfig p _ axes) chart xys =-  L.fold (L.Fold step (chart xys) (pad p)) axes+-- | rectangles specified using a V4 x y z w where+-- (x,y) is location of lower left corner+-- (z,w) is location of upper right corner+rect1 :: (Traversable f) => RectConfig -> f (Rect Double) -> Chart b+rect1 cfg rs = mconcat $ toList $+    (\(Rect (V2 (Range (x,z)) (Range (y,w)))) ->+       (unitSquare #+        moveTo (p2 (0.5,0.5)) #+        Diagrams.scaleX (if z-x==zero then eps else z-x) #+        Diagrams.scaleY (if w-y==zero then eps else w-y) #+        moveTo (p2 (x,y)) #+        fcA (color $ cfg ^. rectColor) #+        lcA (color $ cfg ^. rectBorderColor) #+        lw 1+       )) <$> rs++arrow1 :: (Traversable f) => ArrowConfig Double -> f (V4 Double) -> Chart b+arrow1 cfg qs =+    fcA (color $ cfg ^. arrowColor) $ position $+    zip+    ((\(V4 x y _ _) -> p2 (x,y)) <$> toList qs)+    (arrowStyle cfg <$> toList qs)++arrowStyle :: ArrowConfig Double -> V4 Double -> Chart b+arrowStyle cfg (V4 _ _ z w) =+    arrowAt' opts (p2 (0, 0)) (sL *^ V2 z w)   where-    step x a =-      beside (v (a ^. axisPlacement))-      x-      (axisXY a (d (a ^. axisOrientation)))+    trunc minx maxx a = min (max minx a) maxx+    m = norm (V2 z w)+    hs = trunc (cfg ^. arrowMinHeadSize) (cfg ^. arrowMaxHeadSize) (cfg ^. arrowHeadSize * m)+    sW = trunc (cfg ^. arrowMinStaffWidth) (cfg ^. arrowMaxStaffWidth) (cfg ^. arrowStaffWidth * m)+    sL = trunc (cfg ^. arrowMinStaffLength) (cfg ^. arrowMaxStaffLength) (cfg ^. arrowStaffLength * m)+    opts = with & arrowHead .~ tri &+           headLength .~ global hs &+           shaftStyle %~ (lwG sW & lcA (color $ cfg ^. arrowColor)) &+           headStyle %~ (lcA (color $ cfg ^. arrowColor) & fcA (color $ cfg ^. arrowColor)) -    v AxisBottom = r2 (0,-1)-    v AxisTop = r2 (0,1)-    v AxisLeft = r2 (-1,0)-    v AxisRight = r2 (1,0)+-- | convert from an XY to a polymorphic qdiagrams rectangle+box ::+    ( Field (N t)+    , N t ~ Double+    , V t ~ V2+    , HasOrigin t+    , Transformable t+    , TrailLike t) =>+    Rect Double -> t+box (Rect (V2 x y)) =+    moveOriginTo (p2 ( -x^.low - (x^.width/2)+                     , -y^.low - (y^.width/2))) $+    Diagrams.scaleX (if x^.width==zero then eps else x^.width) $+    Diagrams.scaleY (if y^.width==zero then eps else y^.width)+    unitSquare -    d X = range1D $ fst <$> xys-    d Y = range1D $ snd <$> xys+-- | a pixel is a rectangle with a color.+pixel1 :: (Traversable f) => f (Rect Double, Color) -> Chart b+pixel1 rs = mconcat $ toList $+    (\(Rect (V2 (Range (x,z)) (Range (y,w))), c) ->+       (unitSquare #+        moveTo (p2 (0.5,0.5)) #+        Diagrams.scaleX (if z-x==zero then eps else z-x) #+        Diagrams.scaleY (if w-y==zero then eps else w-y) #+        moveTo (p2 (x,y)) #+        fcA (color c) #+        lcA transparent #+        lw 0+       )) <$> rs --- axis rendering-axisXY :: ((Renderable (Diagrams.TwoD.Text.Text Double) a), Renderable (Path V2 Double) a) => AxisConfig -> (Double,Double) -> QDiagram a V2 Double Any-axisXY cfg range = centerXY $-  atPoints-    (p2 . t <$> tickLocations)-    ((\x -> mkLabel x cfg) <$> tickLabels)-  `atop`-  (axisRect (cfg ^. axisHeight) (-0.5,0.5)-   # unitRect (cfg ^. axisColor))-  where-    t = case cfg ^. axisOrientation of-      X -> \x -> (x,0)-      Y -> \y -> (-(cfg ^. axisMarkSize), y)-    tickLocations = case cfg ^. axisTickStyle of-      TickNone -> []-      TickNumber n -> unit $ mkTicks range n-      TickLabels ls -> unit $ fromIntegral <$> [1..length ls]-    tickLabels = case cfg ^. axisTickStyle of-      TickNone -> []-      TickNumber n -> Text.pack . printf "%7.1g" <$> mkTicks range n-      TickLabels ls -> ls-    axisRect h (min, max) = case cfg ^. axisOrientation of-      X -> moveTo (p2 (max,0)) .-          strokeTrail .-          closeTrail .-          fromVertices .-          scaleX (max-min) .-          scaleY h $-          unitSquare-      Y -> moveTo (p2 (0,min)) .-          strokeTrail .-          closeTrail .-          fromVertices .-          scaleY (max-min) .-          scaleX h $-          unitSquare+-- * charts are recipes for constructing a QDiagram from a specification of the XY plane to be projected on to (XY), a list of traversable vector containers and a list of configurations. --- somewhat a base class of a wide variety of chart marks-unitRect ∷ (Floating (N a), Ord (N a), Typeable (N a), HasStyle a, V a ~ V2) ⇒-    AlphaColour Double → a → a-unitRect c = fcA c # lcA (withOpacity black 0) # lw none+scatterChart ::+    (R2 r, Traversable f) =>+    [ScatterConfig] ->+    Aspect ->+    [f (r Double)] ->+    Chart a+scatterChart defs (Aspect xy) xyss = mconcat $ zipWith scatter1 defs (scaleR2s xy xyss) --- polymorphic dots-scatter :: (Typeable (N r), Monoid r, Semigroup r, Transformable r,HasStyle r, HasOrigin r, TrailLike r, V r ~ V2, N r ~ Double) =>-    ScatterConfig -> [(N r, N r)] -> r-scatter cfg xys =-  atPoints (p2 <$> unitXY xys)-    (repeat $ circle (cfg ^. scatterSize) #-     unitRect (cfg ^. scatterChart ^. chartColor)-    )+lineChart ::+    (R2 r, Traversable f) =>+    [LineConfig] ->+    Aspect ->+    [f (r Double)] ->+    Chart a+lineChart defs (Aspect xy) xyss = mconcat $ zipWith line1 defs (scaleR2s xy xyss) -scatterXY :: (Renderable (Path V2 Double) a, (Renderable (Diagrams.TwoD.Text.Text Double) a)) => ScatterConfig -> [(Double,Double)] -> QDiagram a V2 Double Any-scatterXY cfg xys =-  chartXY (cfg ^. scatterChart) (scatter cfg) xys+histChart ::+    (Traversable f) =>+    [RectConfig] ->+    Aspect ->+    [f (Rect Double)] ->+    Chart a+histChart defs (Aspect xy) rs =+    centerXY . mconcat . zipWith rect1 defs $ scaleRectss xy rs --- bar-bars :: (Renderable (Path V2 Double) a) => BarConfig -> [Double] -> QDiagram a V2 Double Any-bars cfg ys =-  cat' (r2 (1,0)) (with Diagrams.Prelude.& sep .~ cfg ^. barSep)-  ((\y ->-    unitSquare-    # moveOriginTo (p2 (-0.5,-0.5))-    # if y==0 then scaleY epsilon else scaleY y) <$> ys)-    # unitRect (cfg ^. barChart ^. chartColor)-    # centerXY-    # scaleX (1/fromIntegral (length ys)) # scaleY (1/(max-min))+toPixels :: Rect Double -> (V2 Double -> Double) -> PixelConfig -> [(Rect Double, Color)]+toPixels xy f cfg = zip g cs+    where+      g = grid xy (view pixelGrain cfg)+      xs = f . midRect <$> g+      (Range (lx,ux)) = range xs+      (Range (lc0,uc0)) = view pixelGradient cfg+      cs = uncolor . (\x -> blend ((x - lx)/(ux - lx)) (color lc0) (color uc0)) <$> xs++rescalePixels :: Rect Double -> [(Rect Double, Color)] -> [(Rect Double, Color)]+rescalePixels xy xys = zip vs cs   where-    (min,max) = range1D ys-    epsilon = 1e-8+    vs = scaleRects xy (fst <$> xys)+    cs = snd <$> xys -barRange :: (Renderable (Path V2 Double) a, (Renderable (Diagrams.TwoD.Text.Text Double) a)) => BarConfig -> [(Double, Double)] -> QDiagram a V2 Double Any-barRange cfg xys = chartXY (cfg ^. barChart) (\x -> bars cfg (snd <$> x)) xys+-- | pixels over an XY using a function+pixelf ::+    PixelConfig ->+    Aspect ->+    Rect Double ->+    (V2 Double -> Double) ->+    Chart a+pixelf cfg (Aspect asp) xy f =+    pixel1 $ rescalePixels asp (toPixels xy f cfg) -barLabelled :: (Renderable (Path V2 Double) a, (Renderable (Diagrams.TwoD.Text.Text Double) a)) => BarConfig -> [Double] -> [Text] -> QDiagram a V2 Double Any-barLabelled cfg ys labels = barRange-     ( barChart . chartAxes .~-       [ axisTickStyle .~-         TickLabels labels $ def-       ]-       $ cfg-     ) (zip [0..] ys)+-- | arrow lengths and sizes also need to be scaled, and so arrows doesnt fit as neatly into the whole scaling idea+arrowChart ::+    (Traversable f) =>+    ArrowConfig Double ->+    V4 (Range Double) ->+    f (V4 Double) ->+    Chart a+arrowChart cfg xy xs =+    arrow1 cfg $ scaleV4s xy xs --- a line is just a scatter chart rendered with a line (and with a usually stable x-value series)-line :: (Renderable (Path V2 Double) a) => [(Double,Double)] -> QDiagram a V2 Double Any-line xys = strokeT $ trailFromVertices $ p2 <$> unitXY xys+-- | rescale a V4 from rold to rnew+rescaleV4P :: V4 (Range Double) -> V4 (Range Double) -> V4 Double -> V4 Double+rescaleV4P rold rnew q =+    over _x (project (rold^._x) (rnew^._x)) $+    over _y (project (rold^._y) (rnew^._y)) $+    over _z (project (rold^._z) (rnew^._z)) $+    over _w (project (rold^._w) (rnew^._w))+    q -lineXY :: (Renderable (Path V2 Double) a, (Renderable (Diagrams.TwoD.Text.Text Double) a)) => LineConfig -> [(Double,Double)] -> QDiagram a V2 Double Any-lineXY cfg xys =-    chartXY (cfg ^. lineChart)-    (\x -> line x # centerXY # lcA (cfg ^. lineChart ^. chartColor) # lwN (cfg ^. lineSize))-    xys+-- | rescale a container of V4s+rescaleV4 :: (Functor f) =>+    V4 (Range Double) -> V4 (Range Double) -> f (V4 Double) -> f (V4 Double)+rescaleV4 rold rnew qs = rescaleV4P rold rnew <$> qs --- multiple lines with a common range for both x and y values-lines :: (Renderable (Path V2 Double) a) => LinesConfig -> [[(Double,Double)]] -> QDiagram a V2 Double Any-lines cfg xyss = centerXY $ mconcat $-    zipWith (\d c -> d # lcA (c ^. lColor) # lwN (c ^. lSize))-    (l xyss)-    (cycle $ cfg ^.linesLines)-  where-    l xyss' = strokeT . trailFromVertices . fmap p2 <$> unitsXY xyss'+-- | scale a double container of V4s from the current range+scaleV4s :: (Traversable f) =>+    V4 (Range Double) -> f (V4 Double) -> f (V4 Double)+scaleV4s r f = rescaleV4 (rangeV4 f) r f -linesXY :: ((Renderable (Diagrams.TwoD.Text.Text Double) a), Renderable (Path V2 Double) a) =>-  LinesConfig-  -> [[(Double, Double)]]-  -> QDiagram a V2 Double Any-linesXY cfg@(LinesConfig (ChartConfig p _ axes) _) xyss =-  L.fold (L.Fold step (lines cfg xyss) (pad p)) axes+-- | V4 range of a V4 container+rangeV4 :: (Traversable f) => f (V4 Double) -> V4 (Range Double)+rangeV4 qs = V4 rx ry rz rw   where-    step x a =-      beside (v (a ^. axisPlacement))-      x-      (axisXY a (d (a ^. axisOrientation)))+    rx = range $ toList (view _x <$> qs)+    ry = range $ toList (view _y <$> qs)+    rz = range $ toList (view _z <$> qs)+    rw = range $ toList (view _w <$> qs) -    v AxisBottom = r2 (0,-1)-    v AxisTop = r2 (0,1)-    v AxisLeft = r2 (-1,0)-    v AxisRight = r2 (1,0)+rangeV42Rect :: V4 (Range Double) -> Rect Double+rangeV42Rect (V4 x y z w) = Rect (V2 (x<>z) (y<>w)) -    d X = range1Ds $ fmap fst <$> xyss-    d Y = range1Ds $ fmap snd <$> xyss+-- * axis rendering +-- | render with a chart configuration+withChart ::+    ( Traversable f+    , R2 r) =>+    ChartConfig ->+    (Aspect -> [f (r Double)] -> QDiagram a V2 Double Any) ->+    [f (r Double)] ->+    Chart' a+withChart conf renderer d = case conf^.chartRange of+  Nothing ->+      renderer (conf^.chartAspect) d <>+      axes (chartRange .~ Just (rangeR2s d) $ conf)+  Just axesRange ->+      combine (conf ^. chartAspect)+      [ QChart renderer r d+      , QChart+        (\asp _ ->+           axes+           ( chartAspect.~asp+           $ chartRange .~ Just axesRange+           $ conf))+        r+        ()+      ]+    where+      r = rangeR2s d -mkTicks :: (Double,Double) -> Int -> [Double]-mkTicks r n = (f +) . (s *) . fromIntegral <$> [0..n']+axes ::+    ChartConfig ->+    Chart' a+axes (ChartConfig p a r (Aspect asp@(Rect (V2 ax ay))) cc) =+    L.fold (L.Fold step begin (pad p)) a   where-    (f,s,n') = mkTicks' r n+    begin = box asp # fcA (color cc) # lcA (withOpacity black 0) # lw none+    step x cfg = beside dir x (mo $ axis1 cfg rendr tickr)+      where+        rendr = case view axisOrientation cfg of+              X -> ax+              Y -> ay+        tickr = case view axisOrientation cfg of+              X -> rx+              Y -> ry+        dir   = case view axisPlacement cfg of+              AxisBottom -> r2 (0,-1)+              AxisTop -> r2 (0,1)+              AxisLeft -> r2 (-1,0)+              AxisRight -> r2 (1,0)+        mo    = case view axisOrientation cfg of+              X -> moveOriginTo (p2 ((-ax^.low)-(ax^.width)/2,0))+              Y -> moveOriginTo (p2 (0,(-ay^.low)-(ay^.width)/2))+        (Rect (V2 rx ry)) = fromMaybe one r -mkTicks' :: (Double,Double) -> Int -> (Double, Double, Int)-mkTicks' (min, max) n = (f, step', n')+axis1 ::+    AxisConfig ->+    Range Double ->+    Range Double ->+    Chart' b+axis1 cfg rendr tickr = pad (cfg ^. axisPad) $ strut2 $ centerXY $+  atPoints+    (t <$> tickLocations)+    ((`mkLabel` cfg) <$> tickLabels)+  `atop`+  (axisRect (cfg ^. axisHeight) rendr+   # blob (cfg ^. axisColor))   where-    span' = max - min-    step' = 10 ^^ floor (logBase 10 (span'/fromIntegral n))-    err = fromIntegral n / span' * step'-    step-      | err <= 0.15 = 10 * step'-      | err <= 0.35 = 5 * step'-      | err <= 0.75 = 2 * step'-      | otherwise = step'-    f = step * fromIntegral (floor (min/step))-    l = step * fromIntegral (floor (max/step))-    n' = round ((l - f)/step)+    strut2 x = beside dir x $ strut1 (cfg ^. axisInsideStrut)+    dir = case cfg ^. axisPlacement of+      AxisBottom -> r2 (0,1)+      AxisTop -> r2 (0,-1)+      AxisLeft -> r2 (1,0)+      AxisRight -> r2 (-1,0)+    strut1 = case cfg ^. axisOrientation of+      X -> strutY+      Y -> strutX+    t = case cfg ^. axisOrientation of+      X -> \x -> p2 (x, 0)+      Y -> \y -> p2 (-(cfg ^. axisMarkSize), y)+    ticks0 = case cfg ^. axisTickStyle of+      TickNone -> []+      TickRound n -> linearSpaceSensible OuterPos tickr n+      TickExact n -> linearSpace OuterPos tickr n+      TickLabels _ -> []+      TickPlaced xs -> fst <$> xs+    tickLocations = case cfg ^. axisTickStyle of+      TickNone -> []+      {- To Do:+        rounded ticks introduce the possibility of marks beyond the existing range.+        if this happens, it should really be fed into the chart rendering as a new,+        revised range.+      -}+      TickRound _ -> project tickr rendr <$> ticks0+      TickExact _ -> project tickr rendr <$> ticks0+      TickLabels ls ->+          project+          (Range (0, fromIntegral $ length ls))+          rendr <$>+          ((\x -> x - 0.5) . fromIntegral <$> [1..length ls])+      TickPlaced _ -> project tickr rendr <$> ticks0+    tickLabels = case cfg ^. axisTickStyle of+      TickNone -> []+      TickRound _ -> tickFormat <$> ticks0+      TickExact _ -> tickFormat <$> ticks0+      TickLabels ls -> ls+      TickPlaced xs -> snd <$> xs+    tickFormat = sformat (prec 2)+    axisRect h (Range (l,u)) = case cfg ^. axisOrientation of+      X -> moveTo (p2 (u,0)) .+          strokeTrail .+          closeTrail .+          fromVertices .+          scaleY h .+          scaleX (u-l) $+          unitSquare+      Y -> moveTo (p2 (0,l)) .+          strokeTrail .+          closeTrail .+          fromVertices .+          scaleX h .+          scaleY (u-l) $+          unitSquare -mkLabel :: ((Renderable (Diagrams.TwoD.Text.Text Double) a), Renderable (Path V2 Double) a) => Text -> AxisConfig -> QDiagram a V2 Double Any+mkLabel ::+    Text ->+    AxisConfig ->+    Chart' b mkLabel label cfg =   beside dir   (beside dir    (rule (cfg ^. axisMarkSize) #-   lcA (cfg ^. axisMarkColor))+   lcA (color $ cfg ^. axisMarkColor))     s)   (Diagrams.Prelude.alignedText     (cfg ^. axisAlignedTextRight)     (cfg ^. axisAlignedTextBottom)     (Text.unpack label) #-  scale (cfg ^. axisTextSize) #-  fcA (cfg ^.axisTextColor))+  Diagrams.scale (cfg ^. axisTextSize) #+  fcA (color $ cfg ^.axisTextColor))   where     dir = case cfg ^. axisOrientation of       X -> r2 (0,-1)@@ -270,6 +388,77 @@       X -> vrule       Y -> hrule     s = case cfg ^. axisOrientation of-      X -> strutY (cfg ^. axisStrutSize)-      Y -> strutX (cfg ^. axisStrutSize)+      X -> strutY (cfg ^. axisLabelStrut)+      Y -> strutX (cfg ^. axisLabelStrut) +{-+text1 ::+    TextConfig ->+    Text ->+    Chart' b+text1 cfg label =+  Diagrams.Prelude.alignedText+    (cfg ^. textRight)+    (cfg ^. textBottom)+    (Text.unpack label) #+  Diagrams.scale (cfg ^. textSize) #+  fcA (color $ cfg ^.textColor)+  where+    dir = case cfg ^. textOrientation of+      X -> r2 (0,-1)+      Y -> r2 (-1,0)+-}++-- * rendering+-- | render a list of qcharts using a common scale+combine :: Aspect -> [QChart a] -> Chart' a+combine (Aspect xy) qcs = mconcat $+    (\(QChart c xy1 x) -> c+          (Aspect $ xy `times` xy1 `times` recip xysum)+          x) <$> qcs+    where+      xysum = mconcat $ (\(QChart _ xy1 _) -> xy1) <$> qcs++fileSvg ∷ FilePath → (Double, Double) → Chart SVG → IO ()+fileSvg f s = renderSVG f (mkSizeSpec (Just <$> r2 s))++-- outline of a chart+bubble ∷ ∀ a. (FromInteger (N a), MultiplicativeGroup (N a), RealFloat (N a), Traced a, V a ~ V2) ⇒ [a] → Int → [V a (N a)]+bubble chart' n = bubble'+  where+    bubble' = ps+    ps = catMaybes $ maxRayTraceV (p2 (0,0)) <$>+        ((\x -> view (Diagrams.Prelude.from r2PolarIso) (1, x @@ rad)) .+         (\x -> fromIntegral x/10.0) <$> [0..n]) <*>+        chart'++histCompare :: DealOvers -> Histogram -> Histogram -> Chart' a+histCompare o h1 h2 =+    let h = fromHist o h1+        h' = fromHist o h2+        h'' = zipWith (\(Rect (V2 (Range (x,y)) (Range (z,w)))) (Rect (V2 _ (Range (_,w')))) -> Rect (V2 (Range (x,y)) (Range (z,w-w')))) h h'+        flat = Aspect $ Rect (V2 (Range (-0.75,0.75)) (Range (-0.25,0.25)))+    in+      pad 1.1 $+        beside (r2 (0,-1)) (histChart+        [ def+        , rectBorderColor .~ Color 0 0 0 0+        $ rectColor .~ Color 0.333 0.333 0.333 0.1+        $ def ] sixbyfour [h,h'] <>+        axes (ChartConfig 1.1+              [def]+              (Just (fold $ fold [abs <$> h,abs <$> h']))+              sixbyfour (uncolor transparent)))+        (histChart+        [ rectBorderColor .~ Color 0 0 0 0+        $ rectColor .~ Color 0.888 0.333 0.333 0.8+        $ def ] flat [abs <$> h''] <>+        axes (ChartConfig 1.1+              [ axisAlignedTextBottom .~ 0.65 $+                axisAlignedTextRight .~ 1 $+                axisOrientation .~ Y $+                axisPlacement .~ AxisLeft $+                def+              ]+              (Just (fold $ abs <$> h''))+              flat (uncolor transparent)))
+ test/test.hs view
@@ -0,0 +1,52 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE DataKinds #-}++module Main where++import Chart+import NumHask.Prelude++import Test.Tasty (testGroup, defaultMain)+import Test.Tasty.Hspec++testWithChart :: SpecWith ()+testWithChart = describe "withChart" $ do+    it "axes and chartWith should render the same" $ do+        fileSvg "test/empty.svg" (400,400) emptyChart +        fileSvg "test/justAxes.svg" (400,400) justAxesChart +        t1 <- readFile "test/empty.svg"+        t2 <- readFile "test/justAxes.svg"+        t1 `shouldBe` t2++    it "chartWith lines and lines <> axes" $ do+        fileSvg "test/line.svg" (400,400) line1Chart+        fileSvg "test/line2.svg" (400,400) line2Chart +        t1 <- readFile "test/line.svg"+        t2 <- readFile "test/line2.svg"+        t1 `shouldBe` t2+      where+        emptyChart = withChart def (\_ _ -> mempty) [corners one]+        justAxesChart = axes def+        line1Chart = withChart def (lineChart lineDefs) lineData+        line2Chart =+            lineChart lineDefs sixbyfour lineData <>+            axes (chartRange .~ Just (rangeR2s lineData) $ def)++        lineDefs :: [LineConfig]+        lineDefs =+            [ LineConfig 0.01 (Color 0.945 0.345 0.329 0.8)+            , LineConfig 0.02 (Color 0.698 0.569 0.184 0.5)+            , LineConfig 0.005 (Color 0.5 0.5 0.5 1.0)+            ]+        lineData :: [[V2 Double]]+        lineData =+            fmap r2 <$>+            [ [(0.0,1.0),(1.0,1.0),(2.0,5.0)]+            , [(0.0,0.0),(3.0,3.0)]+            , [(0.5,4.0),(0.5,0)]+            ]++main :: IO ()+main = do+    t1 <- testSpec "withChart" testWithChart+    defaultMain $ testGroup "chart-unit" [t1]