module Reanimate.Builtin.TernaryPlot where import Codec.Picture import Graphics.SvgTree (Tree) import Reanimate.Raster import Reanimate.Svg -- (top, left, right) -- a+b+c=1 type ACoord = Double type BCoord = Double type CCoord = Double -- Creates a centered ternary plot with a width of 5. ternaryPlot :: Int -- ^ Pixels in the X-axis. -> (ACoord -> BCoord -> CCoord -> PixelRGBA8) -> Tree ternaryPlot density fn = scaleToWidth stdWidth $ translate (-cX) (-cY) $ scaleToWidth 1 $ flipYAxis $ translate (fromIntegral density/2) (-fromIntegral height/2) $ embedImage $ generateImage gen density height where stdWidth = 5 (cX, cY) = toCartesianCoords (1/3) (1/3) height = round (fromIntegral density * (sqrt 3 / 2) :: Double) gen x y = let x' = (fromIntegral x / fromIntegral density) y' = (fromIntegral y / fromIntegral density) aCoord = (x'*2-bCoord)/2 bCoord = y' / (sqrt 3 / 2) cCoord = 1 - aCoord - bCoord in if aCoord + bCoord > 1 || aCoord < 0 || bCoord < 0 || cCoord < 0 then PixelRGBA8 0 0 0 0 else fn aCoord bCoord cCoord atCenter :: Double -> Tree -> Tree atCenter stdWidth = translate (-cX*stdWidth) (-cY*stdWidth) where (cX, cY) = toCartesianCoords (1/3) (1/3) radius :: Double radius = sqrt (cX*cX + cY*cY) where (cX, cY) = toCartesianCoords (1/3) (1/3) toCartesianCoords :: ACoord -> BCoord -> (Double, Double) toCartesianCoords a b = (x, y) where x = (a+2*b)/2 y = (sqrt 3 / 2) * a toOffsetCartesianCoords :: ACoord -> BCoord -> (Double, Double) toOffsetCartesianCoords a b = (tx-zx, ty-zy) where (zx,zy) = toCartesianCoords (1/3) (1/3) (tx,ty) = toCartesianCoords a b fromCartesianCoords :: Double -> Double -> (ACoord, BCoord, CCoord) fromCartesianCoords x y = (a,b,1-a-b) where a = (x*2-b)/2 b = y / (sqrt 3 / 2)