{-# LANGUAGE ScopedTypeVariables #-}
{-|
Paths in 2D space.
-}
module Waterfall.TwoD.Path2D
( Path2D
, Sense (..)
, module Waterfall.Path.Common
, arc
, arcTo
, arcRelative
, repeatLooping
-- $ reexports
, line2D
, lineTo2D
, lineRelative2D
, arcVia2D
, arcViaTo2D
, arcViaRelative2D
, bezier2D
, bezierTo2D
, bezierRelative2D
, pathFrom2D
, pathFromTo2D
, pathEndpoints2D
, closeLoop2D
, reversePath2D
, splice2D
, splitPath2D
) where 

import Waterfall.TwoD.Internal.Path2D (Path2D(..))
import Waterfall.TwoD.Transforms (rotate2D)
import Linear.V2 (V2(..))
import Control.Lens ((^.))
import Linear ((^*), _xy, distance, normalize, unangle, nearZero)
import Waterfall.Path.Common

data Sense = Clockwise | Counterclockwise deriving (Sense -> Sense -> Bool
(Sense -> Sense -> Bool) -> (Sense -> Sense -> Bool) -> Eq Sense
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Sense -> Sense -> Bool
== :: Sense -> Sense -> Bool
$c/= :: Sense -> Sense -> Bool
/= :: Sense -> Sense -> Bool
Eq, Int -> Sense -> ShowS
[Sense] -> ShowS
Sense -> String
(Int -> Sense -> ShowS)
-> (Sense -> String) -> ([Sense] -> ShowS) -> Show Sense
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Sense -> ShowS
showsPrec :: Int -> Sense -> ShowS
$cshow :: Sense -> String
show :: Sense -> String
$cshowList :: [Sense] -> ShowS
showList :: [Sense] -> ShowS
Show)

-- | Section of a circle, with a given radius, that lies between two points.
--
-- This may fail, if the radius is less than half of the distance between the points.
--
-- In general, `arcVia` is the \"safer\" way to construct an arc.
--
-- `arc` is not polymorphic, as it would not be possible to define an arc in 3D space in this way.
arc :: Sense -> Double -> V2 Double -> V2 Double -> Path2D 
arc :: Sense -> Double -> V2 Double -> V2 Double -> Path2D
arc Sense
sense Double
radius V2 Double
start V2 Double
end = 
    let mid :: V2 Double
mid = (V2 Double
start V2 Double -> V2 Double -> V2 Double
forall a. Num a => a -> a -> a
+ V2 Double
end) V2 Double -> Double -> V2 Double
forall (f :: * -> *) a. (Functor f, Num a) => f a -> a -> f a
^* Double
0.5
        (V2 Double
dx Double
dy) = V2 Double -> V2 Double
forall a (f :: * -> *).
(Floating a, Metric f, Epsilon a) =>
f a -> f a
normalize (V2 Double -> V2 Double) -> V2 Double -> V2 Double
forall a b. (a -> b) -> a -> b
$ V2 Double
end V2 Double -> V2 Double -> V2 Double
forall a. Num a => a -> a -> a
- V2 Double
start
        rotD :: V2 Double
rotD = case Sense
sense of    
                Sense
Clockwise -> Double -> Double -> V2 Double
forall a. a -> a -> V2 a
V2 Double
dy (-Double
dx)
                Sense
Counterclockwise -> Double -> Double -> V2 Double
forall a. a -> a -> V2 a
V2 (-Double
dy) Double
dx
        dse :: Double
dse = V2 Double -> V2 Double -> Double
forall a. Floating a => V2 a -> V2 a -> a
forall (f :: * -> *) a. (Metric f, Floating a) => f a -> f a -> a
distance V2 Double
start V2 Double
end 
        tangent :: Double
tangent = Double
radius Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double -> Double
forall a. Floating a => a -> a
sqrt (Double
radius Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
radius Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
dse Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
dse Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
4) 
        arcMid :: V2 Double
arcMid = V2 Double
mid V2 Double -> V2 Double -> V2 Double
forall a. Num a => a -> a -> a
+ V2 Double
rotD V2 Double -> Double -> V2 Double
forall (f :: * -> *) a. (Functor f, Num a) => f a -> a -> f a
^* Double
tangent
    in if Double
dse Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
2 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
radius
            then String -> Path2D
forall a. HasCallStack => String -> a
error String
"points too far apart in arc"
            else V2 Double -> V2 Double -> V2 Double -> Path2D
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> path
arcVia V2 Double
start V2 Double
arcMid V2 Double
end  

-- | Version of `arc` designed to work with `pathFrom`
arcTo :: Sense -> Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcTo :: Sense -> Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcTo Sense
sense Double
radius V2 Double
end = \V2 Double
start -> (V2 Double
end, Sense -> Double -> V2 Double -> V2 Double -> Path2D
arc Sense
sense Double
radius V2 Double
start V2 Double
end) 

-- | Version of `arc` designed to work with `pathFrom`
-- 
-- With relative points; specifying the distance of the endpoint
-- relative to the start of the line, rather than in absolute space.
arcRelative :: Sense -> Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcRelative :: Sense -> Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcRelative Sense
sense Double
radius V2 Double
dEnd = do
    V2 Double
end <- (V2 Double -> V2 Double -> V2 Double
forall a. Num a => a -> a -> a
+ V2 Double
dEnd)
    Sense -> Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcTo Sense
sense Double
radius V2 Double
end

-- | Given a Path where both endpoints are equidistant from the origin.
--
-- And which subtends an angle \( φ \) from the origin that evenly divides a complete revolution, such that \(n φ = 2 π \).
-- 
-- Replicates the path \( n \) times, rotating it by \( φ \), until the resulting path completes one revolution around the origin.
--
-- This can be used to construct paths with rotational symmetry, such as regular polygons, or gears.
repeatLooping :: Path2D -> Path2D
repeatLooping :: Path2D -> Path2D
repeatLooping Path2D
p = 
    case Path2D -> Maybe (V2 Double, V2 Double)
pathEndpoints2D Path2D
p of
        Maybe (V2 Double, V2 Double)
Nothing -> Path2D
p
        Just (V2 Double
s, V2 Double
e) ->
            let a :: Double
a = V2 Double -> Double
forall a. (Floating a, Ord a) => V2 a -> a
unangle (V2 Double
e V2 Double
-> Getting (V2 Double) (V2 Double) (V2 Double) -> V2 Double
forall s a. s -> Getting a s a -> a
^. Getting (V2 Double) (V2 Double) (V2 Double)
forall a. Lens' (V2 a) (V2 a)
forall (t :: * -> *) a. R2 t => Lens' (t a) (V2 a)
_xy) Double -> Double -> Double
forall a. Num a => a -> a -> a
- V2 Double -> Double
forall a. (Floating a, Ord a) => V2 a -> a
unangle (V2 Double
s V2 Double
-> Getting (V2 Double) (V2 Double) (V2 Double) -> V2 Double
forall s a. s -> Getting a s a -> a
^. Getting (V2 Double) (V2 Double) (V2 Double)
forall a. Lens' (V2 a) (V2 a)
forall (t :: * -> *) a. R2 t => Lens' (t a) (V2 a)
_xy)
            in if Double -> Bool
forall a. Epsilon a => a -> Bool
nearZero Double
a 
                then Path2D
forall a. Monoid a => a
mempty
                else let Integer
times :: Integer = Integer -> Integer
forall a. Num a => a -> a
abs (Integer -> Integer) -> (Double -> Integer) -> Double -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Integer
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
round (Double -> Integer) -> Double -> Integer
forall a b. (a -> b) -> a -> b
$ Double
forall a. Floating a => a
pi Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
2 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
a 
                      in [Path2D] -> Path2D
forall a. Monoid a => [a] -> a
mconcat ([Path2D] -> Path2D) -> [Path2D] -> Path2D
forall a b. (a -> b) -> a -> b
$ [Double -> Path2D -> Path2D
forall a. Transformable2D a => Double -> a -> a
rotate2D (Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
n Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
a) Path2D
p | Integer
n <- [Integer
0..Integer
times]]


-- $reexports
--
-- reexports from Waterfall.Path.Common, but monomorphised

-- | `line`, with the type fixed to `Path2D`
line2D :: V2 Double -> V2 Double -> Path2D
line2D :: V2 Double -> V2 Double -> Path2D
line2D = V2 Double -> V2 Double -> Path2D
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> path
line 

-- | `lineTo`, with the type fixed to `Path2D`
lineTo2D :: V2 Double -> V2 Double -> (V2 Double, Path2D)
lineTo2D :: V2 Double -> V2 Double -> (V2 Double, Path2D)
lineTo2D = V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> (point, path)
lineTo

-- | `lineRelative`, with the type fixed to `Path2D`
lineRelative2D :: V2 Double -> V2 Double -> (V2 Double, Path2D)
lineRelative2D :: V2 Double -> V2 Double -> (V2 Double, Path2D)
lineRelative2D = V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> (point, path)
lineRelative

-- | `arcVia`, with the type fixed to `Path2D`
arcVia2D :: V2 Double -> V2 Double -> V2 Double -> Path2D
arcVia2D :: V2 Double -> V2 Double -> V2 Double -> Path2D
arcVia2D = V2 Double -> V2 Double -> V2 Double -> Path2D
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> path
arcVia

-- | `arcViaTo`, with the type fixed to `Path2D`
arcViaTo2D :: V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcViaTo2D :: V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcViaTo2D = V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> (point, path)
arcViaTo

-- | `arcViaRelative`, with the type fixed to `Path2D`
arcViaRelative2D :: V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcViaRelative2D :: V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
arcViaRelative2D = V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> (point, path)
arcViaRelative

-- | `bezier`, with the type fixed to `Path2D`
bezier2D :: V2 Double -> V2 Double -> V2 Double -> V2 Double ->  Path2D
bezier2D :: V2 Double -> V2 Double -> V2 Double -> V2 Double -> Path2D
bezier2D = V2 Double -> V2 Double -> V2 Double -> V2 Double -> Path2D
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> point -> path
bezier

-- | `bezierTo`, with the type fixed to `Path2D`
bezierTo2D :: V2 Double -> V2 Double -> V2 Double -> V2 Double ->  (V2 Double, Path2D)
bezierTo2D :: V2 Double
-> V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
bezierTo2D = V2 Double
-> V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> point -> (point, path)
bezierTo

-- | `bezierRelative`, with the type fixed to `Path2D`
bezierRelative2D :: V2 Double -> V2 Double -> V2 Double -> V2 Double ->  (V2 Double, Path2D)
bezierRelative2D :: V2 Double
-> V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
bezierRelative2D = V2 Double
-> V2 Double -> V2 Double -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Epsilon point) =>
point -> point -> point -> point -> (point, path)
bezierRelative

-- | `pathFrom`, with the type fixed to `Path2D`
pathFrom2D :: V2 Double -> [V2 Double -> (V2 Double, Path2D)] -> Path2D
pathFrom2D :: V2 Double -> [V2 Double -> (V2 Double, Path2D)] -> Path2D
pathFrom2D = V2 Double -> [V2 Double -> (V2 Double, Path2D)] -> Path2D
forall path point.
Monoid path =>
point -> [point -> (point, path)] -> path
pathFrom

-- | `pathFromTo`, with the type fixed to `Path2D`
pathFromTo2D :: [V2 Double -> (V2 Double, Path2D)] -> V2 Double -> (V2 Double, Path2D)
pathFromTo2D :: [V2 Double -> (V2 Double, Path2D)]
-> V2 Double -> (V2 Double, Path2D)
pathFromTo2D = [V2 Double -> (V2 Double, Path2D)]
-> V2 Double -> (V2 Double, Path2D)
forall path point.
Monoid path =>
[point -> (point, path)] -> point -> (point, path)
pathFromTo

-- | `pathEndpoints`, with the type fixed to `Path2D` 
pathEndpoints2D :: Path2D -> Maybe (V2 Double, V2 Double)
pathEndpoints2D :: Path2D -> Maybe (V2 Double, V2 Double)
pathEndpoints2D = Path2D -> Maybe (V2 Double, V2 Double)
forall point path.
AnyPath point path =>
path -> Maybe (point, point)
pathEndpoints

-- | `closeLoop` with the type fixed to `Path2D`
closeLoop2D :: Path2D -> Path2D
closeLoop2D :: Path2D -> Path2D
closeLoop2D = Path2D -> Path2D
forall point path.
(AnyPath point path, Monoid path, Epsilon point) =>
path -> path
closeLoop 

-- | `reversePath` with the type fixed to `Path2D`
reversePath2D :: Path2D -> Path2D
reversePath2D :: Path2D -> Path2D
reversePath2D = Path2D -> Path2D
forall point path. AnyPath point path => path -> path
reversePath


-- | `splice` with the type fixed to `Path2D`
splice2D :: Path2D -> V2 Double -> (V2 Double, Path2D)
splice2D :: Path2D -> V2 Double -> (V2 Double, Path2D)
splice2D = Path2D -> V2 Double -> (V2 Double, Path2D)
forall point path.
(AnyPath point path, Num point) =>
path -> point -> (point, path)
splice

-- | `splitPath` with the type fixed to `Path2D`
splitPath2D :: Path2D -> [Path2D]
splitPath2D :: Path2D -> [Path2D]
splitPath2D = Path2D -> [Path2D]
forall point path. AnyPath point path => path -> [path]
splitPath