module Diagrams.TwoD.Ellipse
(
circle
, ellipse
, Ellipse(..)
, ellipseCenter
, ellipseAngle
, ellipseAxes
, ellipseScale
, ellipseCoeffs
) where
import Graphics.Rendering.Diagrams
import Graphics.Rendering.Diagrams.Util
import Diagrams.TwoD.Types
import Diagrams.TwoD.Transform
import Data.Monoid (Any(..), mempty)
import Data.VectorSpace (magnitudeSq, magnitude, (^-^))
data Ellipse = Ellipse (Transformation R2)
type instance V Ellipse = R2
instance Transformable Ellipse where
transform t (Ellipse e) = Ellipse (t <> e)
circle :: (Backend b R2, Renderable Ellipse b) => Diagram b R2
circle = mkAD (Prim $ Ellipse mempty)
(Bounds circleBounds)
(fromNames [ ("C", P ( 0, 0))
, ("E", P ( 1, 0))
, ("N", P ( 0, 1))
, ("W", P (1, 0))
, ("S", P ( 0,1)) ])
(Query circleQuery)
where circleBounds (x,y) = 1 / sqrt(x*x + y*y)
circleQuery (P (x,y)) = Any $ x*x + y*y <= 1
ellipse :: (Backend b R2, Renderable Ellipse b) => Double -> Diagram b R2
ellipse e
| e >= 0 && e < 1 = scaleX (sqrt (1 e*e)) circle
| otherwise = error "Eccentricity of ellipse must be >= 0 and < 1."
ellipseCoeffs :: Ellipse -> (Double, Double, Double, Double, Double, Double)
ellipseCoeffs (Ellipse eT) = ( a*a + d*d
, 2 * (a*b + d*e)
, b*b + e*e
, 2 * (a*c + d*f)
, 2 * (b*c + e*f)
, c*c + f*f 1
)
where eT' = inv eT
(a,d) = apply eT' (1,0)
(b,e) = apply eT' (0,1)
(c,f) = transl eT'
ellipseCenter :: Ellipse -> P2
ellipseCenter (Ellipse e) = papply e origin
ellipseAngle :: Ellipse -> Angle
ellipseAngle ell
| y < 0 = pi + atan2 y x
| otherwise = atan2 y x
where ((x,y),_) = ellipseAxes ell
ellipseAxes :: Ellipse -> (R2, R2)
ellipseAxes (Ellipse eT) = if magnitudeSq va >= magnitudeSq vb then (va,vb) else (vb,va)
where a = apply eT (1,0)
b = apply eT (0,1)
v = apply eT (0,0)
va = a ^-^ v
vb = b ^-^ v
ellipseScale :: Ellipse -> (Double, Double)
ellipseScale ell = (magnitude a, magnitude b)
where (a,b) = ellipseAxes ell