module Diagrams.Deform (Deformation(..), Deformable(..), asDeformation) where
import Control.Lens (under, _Unwrapped)
import Data.AffineSpace
import Data.Basis
import Data.MemoTrie
import Data.Monoid hiding ((<>))
import Data.Semigroup
import Data.VectorSpace
import Diagrams.Core
import Diagrams.Located
import Diagrams.Parametric
import Diagrams.Path
import Diagrams.Segment
import Diagrams.Trail
data Deformation v = Deformation (Point v -> Point v)
instance Semigroup (Deformation v) where
(Deformation p1) <> (Deformation p2) = Deformation (p1 . p2)
instance Monoid (Deformation v) where
mappend = (<>)
mempty = Deformation id
class Deformable a where
deform' :: Scalar (V a) -> Deformation (V a) -> a -> a
deform :: Deformation (V a) -> a -> a
asDeformation
:: ( HasTrie (Basis v), HasBasis v) => Transformation v -> Deformation v
asDeformation t = Deformation f' where
f' = papply t
instance Deformable (Point v) where
deform' = const deform
deform (Deformation l) = l
deformSegment :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>
s -> Deformation v -> FixedSegment v -> [FixedSegment v]
deformSegment epsilon t s
| goodEnough epsilon t s = [approx t s]
| otherwise = concatMap (deformSegment epsilon t) [s1, s2]
where
(s1, s2) = splitAtParam s 0.5
approx :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>
Deformation v -> FixedSegment v -> FixedSegment v
approx t (FLinear p0 p1) = FLinear (deform t p0) (deform t p1)
approx t (FCubic p0 c1 c2 p1) = FCubic (f p0) (f c1) (f c2) (f p1) where
f = deform t
goodEnough :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>
s -> Deformation v -> FixedSegment v -> Bool
goodEnough e t s =
all (< e) [magnitude $ deform t (s `atParam` u) .-. approx t s `atParam` u
| u <- [0.25, 0.5, 0.75]]
instance (VectorSpace v, InnerSpace v,
s ~ Scalar v, Ord s, Fractional s, Floating s, Show s, Show v) =>
Deformable (Located (Trail v)) where
deform' eps p t
| isLine $ unLoc t = line `at` p0
| otherwise = glueTrail line `at` p0
where
segs = concatMap (deformSegment eps p) $ fixTrail t
p0 = case segs of
(FLinear start _:_) -> start
(FCubic start _ _ _:_) -> start
_ -> loc t
line = trailFromSegments $ map (unLoc . fromFixedSeg) segs
deform p t = deform' (0.01 * extent) p t where
extent = maximum . map dist . trailVertices $ t
dist pt = magnitude $ pt .-. loc t
instance (VectorSpace v, InnerSpace v,
s ~ Scalar v, Ord s, Fractional s, Floating s, Show s, Show v) =>
Deformable (Path v) where
deform' eps p = under _Unwrapped $ map (deform' eps p)
deform p = under _Unwrapped $ map (deform p)