module Diagrams.Segment
(
Open, Closed
, Offset(..), segOffset
, Segment(..), straight, bezier3, bézier3, reverseSegment
, FixedSegment(..)
, mkFixedSeg, fromFixedSeg
, SegCount(..)
, ArcLength(..)
, getArcLengthCached, getArcLengthFun, getArcLengthBounded
, TotalOffset(..)
, OffsetEnvelope(..), oeOffset, oeEnvelope
, SegMeasure
) where
import Control.Lens (makeLenses, Wrapped(..), Rewrapped, iso, op)
import Control.Applicative (liftA2)
import Data.AffineSpace
import Data.FingerTree
import Data.Monoid.MList
import Data.Semigroup
import Data.VectorSpace hiding (Sum (..))
import Numeric.Interval (Interval (..))
import qualified Numeric.Interval as I
import Diagrams.Core
import Diagrams.Located
import Diagrams.Parametric
import Diagrams.Solve
data Open
data Closed
data Offset c v where
OffsetOpen :: Offset Open v
OffsetClosed :: !v -> Offset Closed v
deriving instance Show v => Show (Offset c v)
deriving instance Eq v => Eq (Offset c v)
deriving instance Ord v => Ord (Offset c v)
instance Functor (Offset c) where
fmap _ OffsetOpen = OffsetOpen
fmap f (OffsetClosed v) = OffsetClosed (f v)
type instance V (Offset c v) = v
instance HasLinearMap v => Transformable (Offset c v) where
transform = fmap . apply
data Segment c v
= Linear !(Offset c v)
| Cubic !v !v !(Offset c v)
deriving (Show, Functor, Eq, Ord)
type instance V (Segment c v) = v
instance HasLinearMap v => Transformable (Segment c v) where
transform = fmap . apply
instance HasLinearMap v => Renderable (Segment c v) NullBackend where
render _ _ = mempty
straight :: v -> Segment Closed v
straight = Linear . OffsetClosed
bezier3 :: v -> v -> v -> Segment Closed v
bezier3 c1 c2 x = Cubic c1 c2 (OffsetClosed x)
bézier3 :: v -> v -> v -> Segment Closed v
bézier3 = bezier3
type instance Codomain (Segment Closed v) = v
instance (VectorSpace v, Num (Scalar v)) => Parametric (Segment Closed v) where
atParam (Linear (OffsetClosed x)) t = t *^ x
atParam (Cubic c1 c2 (OffsetClosed x2)) t = (3 * t'*t'*t ) *^ c1
^+^ (3 * t'*t *t ) *^ c2
^+^ ( t *t *t ) *^ x2
where t' = 1t
instance Num (Scalar v) => DomainBounds (Segment Closed v)
instance (VectorSpace v, Num (Scalar v)) => EndValues (Segment Closed v) where
atStart = const zeroV
atEnd (Linear (OffsetClosed v)) = v
atEnd (Cubic _ _ (OffsetClosed v)) = v
segOffset :: Segment Closed v -> v
segOffset (Linear (OffsetClosed v)) = v
segOffset (Cubic _ _ (OffsetClosed v)) = v
instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (Segment Closed v) where
getEnvelope (s@(Linear {})) = mkEnvelope $ \v ->
maximum (map (\t -> ((s `atParam` t) <.> v)) [0,1]) / magnitudeSq v
getEnvelope (s@(Cubic c1 c2 (OffsetClosed x2))) = mkEnvelope $ \v ->
maximum .
map (\t -> ((s `atParam` t) <.> v) / magnitudeSq v) $
[0,1] ++
filter (liftA2 (&&) (>0) (<1))
(quadForm (3 * ((3 *^ c1 ^-^ 3 *^ c2 ^+^ x2) <.> v))
(6 * (((2) *^ c1 ^+^ c2) <.> v))
((3 *^ c1) <.> v))
instance (VectorSpace v, Fractional (Scalar v)) => Sectionable (Segment Closed v) where
splitAtParam (Linear (OffsetClosed x1)) t = (left, right)
where left = straight p
right = straight (x1 ^-^ p)
p = lerp zeroV x1 t
splitAtParam (Cubic c1 c2 (OffsetClosed x2)) t = (left, right)
where left = bezier3 a b e
right = bezier3 (c ^-^ e) (d ^-^ e) (x2 ^-^ e)
p = lerp c1 c2 t
a = lerp zeroV c1 t
b = lerp a p t
d = lerp c2 x2 t
c = lerp p d t
e = lerp b c t
reverseDomain = reverseSegment
reverseSegment :: AdditiveGroup v => Segment Closed v -> Segment Closed v
reverseSegment (Linear (OffsetClosed v)) = straight (negateV v)
reverseSegment (Cubic c1 c2 (OffsetClosed x2)) = bezier3 (c2 ^-^ x2) (c1 ^-^ x2) (negateV x2)
instance (InnerSpace v, Floating (Scalar v), Ord (Scalar v), AdditiveGroup v)
=> HasArcLength (Segment Closed v) where
arcLengthBounded _ (Linear (OffsetClosed x1)) = I.singleton $ magnitude x1
arcLengthBounded m s@(Cubic c1 c2 (OffsetClosed x2))
| ub lb < m = I lb ub
| otherwise = arcLengthBounded (m/2) l + arcLengthBounded (m/2) r
where (l,r) = s `splitAtParam` 0.5
ub = sum (map magnitude [c1, c2 ^-^ c1, x2 ^-^ c2])
lb = magnitude x2
arcLengthToParam m s _ | arcLength m s == 0 = 0.5
arcLengthToParam m s@(Linear {}) len = len / arcLength m s
arcLengthToParam m s@(Cubic {}) len
| len `I.elem` (I (m/2) (m/2)) = 0
| len < 0 = arcLengthToParam m (fst (splitAtParam s (1))) (len)
| len `I.elem` slen = 1
| len > I.sup slen = 2 * arcLengthToParam m (fst (splitAtParam s 2)) len
| len < I.sup llen = (*0.5) $ arcLengthToParam m l len
| otherwise = (+0.5) . (*0.5)
$ arcLengthToParam (9*m/10) r (len I.midpoint llen)
where (l,r) = s `splitAtParam` 0.5
llen = arcLengthBounded (m/10) l
slen = arcLengthBounded m s
data FixedSegment v = FLinear (Point v) (Point v)
| FCubic (Point v) (Point v) (Point v) (Point v)
deriving Show
type instance V (FixedSegment v) = v
instance HasLinearMap v => Transformable (FixedSegment v) where
transform t (FLinear p1 p2)
= FLinear
(transform t p1)
(transform t p2)
transform t (FCubic p1 c1 c2 p2)
= FCubic
(transform t p1)
(transform t c1)
(transform t c2)
(transform t p2)
instance VectorSpace v => HasOrigin (FixedSegment v) where
moveOriginTo o (FLinear p1 p2)
= FLinear
(moveOriginTo o p1)
(moveOriginTo o p2)
moveOriginTo o (FCubic p1 c1 c2 p2)
= FCubic
(moveOriginTo o p1)
(moveOriginTo o c1)
(moveOriginTo o c2)
(moveOriginTo o p2)
instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (FixedSegment v) where
getEnvelope f = moveTo p (getEnvelope s)
where (p, s) = viewLoc $ fromFixedSeg f
mkFixedSeg :: AdditiveGroup v => Located (Segment Closed v) -> FixedSegment v
mkFixedSeg ls =
case viewLoc ls of
(p, Linear (OffsetClosed v)) -> FLinear p (p .+^ v)
(p, Cubic c1 c2 (OffsetClosed x2)) -> FCubic p (p .+^ c1) (p .+^ c2) (p .+^ x2)
fromFixedSeg :: AdditiveGroup v => FixedSegment v -> Located (Segment Closed v)
fromFixedSeg (FLinear p1 p2) = straight (p2 .-. p1) `at` p1
fromFixedSeg (FCubic x1 c1 c2 x2) = bezier3 (c1 .-. x1) (c2 .-. x1) (x2 .-. x1) `at` x1
type instance Codomain (FixedSegment v) = Point v
instance VectorSpace v => Parametric (FixedSegment v) where
atParam (FLinear p1 p2) t = alerp p1 p2 t
atParam (FCubic x1 c1 c2 x2) t = p3
where p11 = alerp x1 c1 t
p12 = alerp c1 c2 t
p13 = alerp c2 x2 t
p21 = alerp p11 p12 t
p22 = alerp p12 p13 t
p3 = alerp p21 p22 t
instance Num (Scalar v) => DomainBounds (FixedSegment v)
instance (VectorSpace v, Num (Scalar v)) => EndValues (FixedSegment v) where
atStart (FLinear p0 _) = p0
atStart (FCubic p0 _ _ _) = p0
atEnd (FLinear _ p1) = p1
atEnd (FCubic _ _ _ p1 ) = p1
instance (VectorSpace v, Fractional (Scalar v)) => Sectionable (FixedSegment v) where
splitAtParam (FLinear p0 p1) t = (left, right)
where left = FLinear p0 p
right = FLinear p p1
p = alerp p0 p1 t
splitAtParam (FCubic p0 c1 c2 p1) t = (left, right)
where left = FCubic p0 a b cut
right = FCubic cut c d p1
a = alerp p0 c1 t
p = alerp c1 c2 t
d = alerp c2 p1 t
b = alerp a p t
c = alerp p d t
cut = alerp b c t
reverseDomain (FLinear p0 p1) = FLinear p1 p0
reverseDomain (FCubic p0 c1 c2 p1) = FCubic p1 c2 c1 p0
newtype SegCount = SegCount (Sum Int)
deriving (Semigroup, Monoid)
instance Wrapped SegCount where
type Unwrapped SegCount = Sum Int
_Wrapped' = iso (\(SegCount x) -> x) SegCount
instance Rewrapped SegCount SegCount
newtype ArcLength v
= ArcLength (Sum (Interval (Scalar v)), Scalar v -> Sum (Interval (Scalar v)))
instance Wrapped (ArcLength v) where
type Unwrapped (ArcLength v) =
(Sum (Interval (Scalar v)), Scalar v -> Sum (Interval (Scalar v)))
_Wrapped' = iso (\(ArcLength x) -> x) ArcLength
instance Rewrapped (ArcLength v) (ArcLength v')
getArcLengthCached :: ArcLength v -> Interval (Scalar v)
getArcLengthCached = getSum . fst . op ArcLength
getArcLengthFun :: ArcLength v -> Scalar v -> Interval (Scalar v)
getArcLengthFun = fmap getSum . snd . op ArcLength
getArcLengthBounded :: (Num (Scalar v), Ord (Scalar v))
=> Scalar v -> ArcLength v -> Interval (Scalar v)
getArcLengthBounded eps al
| I.width cached <= eps = cached
| otherwise = getArcLengthFun al eps
where
cached = getArcLengthCached al
deriving instance (Num (Scalar v), Ord (Scalar v)) => Semigroup (ArcLength v)
deriving instance (Num (Scalar v), Ord (Scalar v)) => Monoid (ArcLength v)
newtype TotalOffset v = TotalOffset v
instance Wrapped (TotalOffset v) where
type Unwrapped (TotalOffset v) = v
_Wrapped' = iso (\(TotalOffset x) -> x) TotalOffset
instance Rewrapped (TotalOffset v) (TotalOffset v')
instance AdditiveGroup v => Semigroup (TotalOffset v) where
TotalOffset v1 <> TotalOffset v2 = TotalOffset (v1 ^+^ v2)
instance AdditiveGroup v => Monoid (TotalOffset v) where
mempty = TotalOffset zeroV
mappend = (<>)
data OffsetEnvelope v = OffsetEnvelope
{ _oeOffset :: !(TotalOffset v)
, _oeEnvelope :: Envelope v
}
makeLenses ''OffsetEnvelope
instance (InnerSpace v, OrderedField (Scalar v)) => Semigroup (OffsetEnvelope v) where
(OffsetEnvelope o1 e1) <> (OffsetEnvelope o2 e2)
= let !negOff = negateV . op TotalOffset $ o1
e2Off = moveOriginBy negOff e2
!() = maybe () (\f -> f `seq` ()) $ appEnvelope e2Off
in OffsetEnvelope
(o1 <> o2)
(e1 <> e2Off)
type SegMeasure v = SegCount
::: ArcLength v
::: OffsetEnvelope v
::: ()
instance (InnerSpace v, OrderedField (Scalar v))
=> Measured (SegMeasure v) (SegMeasure v) where
measure = id
instance (OrderedField (Scalar v), InnerSpace v)
=> Measured (SegMeasure v) (Segment Closed v) where
measure s = (SegCount . Sum $ 1)
*: (ArcLength $ ( Sum $ arcLengthBounded (stdTolerance/100) s
, Sum . flip arcLengthBounded s
)
)
*: (OffsetEnvelope
(TotalOffset . segOffset $ s)
(getEnvelope s)
)
*: ()