{-# LANGUAGE TypeFamilies #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Drawing.Shapes.Parallelogram -- Copyright : (c) Stephen Tetley 2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley -- Stability : highly unstable -- Portability : GHC -- -- Parallelogram. -- -- -------------------------------------------------------------------------------- module Wumpus.Drawing.Shapes.Parallelogram ( Parallelogram , DParallelogram , parallelogram , zparallelogram ) where import Wumpus.Drawing.Basis.Geometry import Wumpus.Drawing.Basis.ShapeTrails import Wumpus.Drawing.Paths import Wumpus.Drawing.Paths.Intersection import Wumpus.Drawing.Shapes.Base import Wumpus.Basic.Kernel -- package: wumpus-basic import Wumpus.Core -- package: wumpus-core import Data.VectorSpace -- package: vector-space import Control.Applicative -------------------------------------------------------------------------------- -- Parallelogram -- | A Paralleogram. -- data Parallelogram u = Parallelogram { pll_ctm :: ShapeCTM u , pll_base_width :: !u , pll_height :: !u , pll_base_left_ang :: Radian } type instance DUnit (Parallelogram u) = u type DParallelogram = Parallelogram Double instance Functor Parallelogram where fmap f (Parallelogram ctm bw h lang) = Parallelogram (fmap f ctm) (f bw) (f h) lang -------------------------------------------------------------------------------- -- Affine trans mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Parallelogram u -> Parallelogram u mapCTM f = (\s i -> s { pll_ctm = f i }) <*> pll_ctm instance (Real u, Floating u) => Rotate (Parallelogram u) where rotate ang = mapCTM (rotate ang) instance (Real u, Floating u) => RotateAbout (Parallelogram u) where rotateAbout ang pt = mapCTM (rotateAbout ang pt) instance Fractional u => Scale (Parallelogram u) where scale sx sy = mapCTM (scale sx sy) instance InterpretUnit u => Translate (Parallelogram u) where translate dx dy = mapCTM (translate dx dy) -------------------------------------------------------------------------------- -- Anchors -- | 'runDisplaceCenter' : @ ( base_width -- * height -- * base_minor -- -> Vec ) * parallelogram -> Point @ -- runDisplaceCenter :: (Real u, Floating u) => (u -> u -> Radian -> Vec2 u) -> Parallelogram u -> Anchor u runDisplaceCenter fn (Parallelogram { pll_ctm = ctm , pll_base_width = bw , pll_height = h , pll_base_left_ang = lang }) = projectFromCtr (fn bw h lang) ctm runDisplaceCenterHalves :: (Real u, Floating u) => (u -> u -> Radian -> Vec2 u) -> Parallelogram u -> Anchor u runDisplaceCenterHalves fn = runDisplaceCenter $ \bw h bl_ang -> fn (0.5*bw) (0.5*h) bl_ang instance (Real u, Floating u) => CenterAnchor (Parallelogram u) where center = runDisplaceCenter $ \_ _ _ -> V2 0 0 -- | WARNING - WRONG... -- top anchors swap the base minor and major... -- instance (Real u, Floating u) => TopCornerAnchor (Parallelogram u) where topLeftCorner = runDisplaceCenterHalves $ \hw hh lang -> let hypo = hh / (fromRadian $ sin lang) in hvec (-hw) ^+^ avec lang hypo topRightCorner = runDisplaceCenterHalves $ \hw hh lang -> let hypo = hh / (fromRadian $ sin lang) in hvec hw ^+^ avec lang hypo instance (Real u, Floating u) => BottomCornerAnchor (Parallelogram u) where bottomLeftCorner = runDisplaceCenterHalves $ \hw hh lang -> let hypo = hh / (fromRadian $ sin lang) in hvec (-hw) ^+^ avec lang (-hypo) bottomRightCorner = runDisplaceCenterHalves $ \hw hh lang -> let hypo = hh / (fromRadian $ sin lang) in hvec hw ^+^ avec lang (-hypo) instance (Real u, Floating u) => SideMidpointAnchor (Parallelogram u) where sideMidpoint n a = step (n `mod` 4) where step 1 = midpoint (topRightCorner a) (topLeftCorner a) step 2 = midpoint (topLeftCorner a) (bottomLeftCorner a) step 3 = midpoint (bottomLeftCorner a) (bottomRightCorner a) step _ = midpoint (bottomRightCorner a) (topRightCorner a) instance (Real u, Floating u, InterpretUnit u, Tolerance u) => CardinalAnchor (Parallelogram u) where north = radialAnchor (0.5 * pi) south = radialAnchor (1.5 * pi) east = radialAnchor 0 west = radialAnchor pi instance (Real u, Floating u, InterpretUnit u, Tolerance u) => CardinalAnchor2 (Parallelogram u) where northeast = radialAnchor (0.25*pi) southeast = radialAnchor (1.75*pi) southwest = radialAnchor (1.25*pi) northwest = radialAnchor (0.75*pi) instance (Real u, Floating u, InterpretUnit u, Tolerance u) => RadialAnchor (Parallelogram u) where radialAnchor ang = runDisplaceCenter $ \bw h bl_ang-> maybe zeroVec id $ pllRadialAnchor bw h bl_ang ang -- | Note - it is not worth changing this to a quadrantAlg. -- -- There are pathological parallelograms that the current -- QuadrantAlg code cannot handle, and a better abstraction is -- needed (rather than better implementation of QuadrantAlg). -- pllRadialAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) => u -> u -> Radian -> Radian -> Maybe (Vec2 u) pllRadialAnchor bw h bl_ang ang = fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp where rp = anaTrailPath zeroPt $ parallelogram_trail bw h bl_ang -------------------------------------------------------------------------------- -- Construction -- | 'parallelogram' : @ width * height * bottom_left_ang -> Parallelogram @ -- -- parallelogram :: (Real u, Floating u, InterpretUnit u, Tolerance u) => u -> u -> Radian -> Shape Parallelogram u parallelogram bw h lang = makeShape (mkParallelogram bw h lang) (mkParallelogramPath bw h lang) -- | 'zparallelogram' : @ base_width * height -> Parallelogram @ -- -- zparallelogram :: (Real u, Floating u, InterpretUnit u, Tolerance u) => u -> u -> Shape Parallelogram u zparallelogram bw h = parallelogram bw h ang where ang = d2r (60::Double) -------------------------------------------------------------------------------- mkParallelogram :: (Real u, Fractional u, InterpretUnit u, Tolerance u) => u -> u -> Radian -> LocThetaQuery u (Parallelogram u) mkParallelogram bw h bl_ang = qpromoteLocTheta $ \ctr theta -> pure $ Parallelogram { pll_ctm = makeShapeCTM ctr theta , pll_base_width = bw , pll_height = h , pll_base_left_ang = bl_ang } mkParallelogramPath :: (Real u, Floating u, InterpretUnit u, Tolerance u) => u -> u -> Radian -> LocThetaQuery u (AbsPath u) mkParallelogramPath bw h bl_ang = qpromoteLocTheta $ \ctr theta -> return $ anaTrailPath ctr $ rparallelogram_trail bw h bl_ang theta