metabrush/src/metabrushes/MetaBrush/Asset/Brushes.hs

129 lines
3.9 KiB
Haskell
Raw Normal View History

2023-01-09 03:27:08 +00:00
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-}
2020-11-12 17:34:46 +00:00
module MetaBrush.Asset.Brushes where
2023-01-08 16:16:14 +00:00
-- base
import Data.Coerce
( coerce )
2022-12-11 01:33:34 +00:00
-- containers
import qualified Data.Sequence as Seq
( fromList )
2021-04-25 23:17:27 +00:00
2020-11-12 17:34:46 +00:00
-- text
import Data.Text
( Text )
2022-12-11 01:33:34 +00:00
-- unordered-containers
import Data.HashMap.Strict
( HashMap )
import qualified Data.HashMap.Strict as HashMap
( fromList, lookup )
-- MetaBrush
2022-12-11 01:33:34 +00:00
import Math.Bezier.Spline
2023-01-08 16:16:14 +00:00
import Math.Linear
2023-01-09 03:27:08 +00:00
( (..), T(..) )
2023-01-08 16:16:14 +00:00
import Math.Linear.Dual
( D, type (~>)(..), Var(var), konst )
import Math.Module
( Module((^+^), (*^)) )
import MetaBrush.Brush
2022-12-11 01:33:34 +00:00
( Brush(..), SomeBrush(..) )
import MetaBrush.Records
--------------------------------------------------------------------------------
2022-12-11 01:33:34 +00:00
lookupBrush :: Text -> Maybe SomeBrush
lookupBrush nm = HashMap.lookup nm brushes
2022-12-11 01:33:34 +00:00
-- | All brushes supported by this application.
brushes :: HashMap Text SomeBrush
brushes = HashMap.fromList
[ ( nm, b )
| b@( SomeBrush ( BrushData { brushName = nm } ) )
<- [ SomeBrush circle, SomeBrush ellipse ]
]
2021-05-13 20:07:49 +00:00
2022-12-11 01:33:34 +00:00
-- | Root of @(Sqrt[2] (4 + 3 κ) - 16) (2 - 3 κ)^2 - 8 (1 - 3 κ) Sqrt[8 - 24 κ + 12 κ^2 + 8 κ^3 + 3 κ^4]@.
--
-- Used to approximate circles and ellipses with Bézier curves.
2023-01-08 16:16:14 +00:00
κ :: Double
κ = 0.5519150244935105707435627227925
2023-01-09 03:27:08 +00:00
type CircleBrushFields = '[ "r" ]
2022-12-11 01:33:34 +00:00
circle :: Brush CircleBrushFields
2023-01-09 03:27:08 +00:00
circle = BrushData "circle" ( WithParams deflts circleBrush )
where
2023-01-08 16:16:14 +00:00
deflts :: Record CircleBrushFields
deflts = MkR ( 1 1 )
2023-01-08 16:16:14 +00:00
type EllipseBrushFields = '[ "a", "b", "phi" ]
2022-12-11 01:33:34 +00:00
ellipse :: Brush EllipseBrushFields
2023-01-09 03:27:08 +00:00
ellipse = BrushData "ellipse" ( WithParams deflts ellipseBrush )
2022-12-11 01:33:34 +00:00
where
2023-01-08 16:16:14 +00:00
deflts :: Record EllipseBrushFields
deflts = MkR ( 3 1 1 0 )
--------------------------------------------------------------------------------
-- Differentiable brushes.
2023-01-09 03:27:08 +00:00
circleSpline :: Applicative ( D u ) => ( Double -> Double -> D u v ) -> D u ( Spline 'Closed () v )
circleSpline p = sequenceA $
2023-01-08 16:16:14 +00:00
Spline { splineStart = p 1 0
, splineCurves = ClosedCurves crvs lastCrv }
where
crvs = Seq.fromList
[ Bezier3To (p 1 κ) (p κ 1) (NextPoint (p 0 1)) ()
, Bezier3To (p -κ 1) (p -1 κ) (NextPoint (p -1 0)) ()
, Bezier3To (p -1 -κ) (p -κ -1) (NextPoint (p 0 -1)) ()
]
lastCrv =
Bezier3To (p κ -1) (p 1 -κ) BackToStart ()
2023-01-09 03:27:08 +00:00
circleBrush :: Record CircleBrushFields ~> Spline 'Closed () ( 2 )
circleBrush =
D \ params ->
let r :: D ( Record CircleBrushFields ) Double
r = runD ( var @1 ) params
mkPt :: Double -> Double -> D ( Record CircleBrushFields ) ( 2 )
mkPt ( kon -> x ) ( kon -> y )
= fmap coerce
$ ( x * r ) *^ e_x
^+^ ( y * r ) *^ e_y
in circleSpline @( Record CircleBrushFields ) mkPt
where
e_x, e_y :: D ( Record CircleBrushFields ) ( T ( 2 ) )
e_x = pure $ T $ 2 1 0
e_y = pure $ T $ 2 0 1
kon = konst @( Record CircleBrushFields )
ellipseBrush :: Record EllipseBrushFields ~> Spline 'Closed () ( 2 )
2023-01-08 16:16:14 +00:00
ellipseBrush =
D \ params ->
2023-01-09 03:27:08 +00:00
let a, b, phi :: D ( Record EllipseBrushFields ) Double
2023-01-08 16:16:14 +00:00
a = runD ( var @1 ) params
b = runD ( var @2 ) params
phi = runD ( var @3 ) params
2023-01-09 03:27:08 +00:00
mkPt :: Double -> Double -> D ( Record EllipseBrushFields ) ( 2 )
mkPt ( kon -> x ) ( kon -> y )
2023-01-08 16:16:14 +00:00
= fmap coerce
$ ( x * a * cos phi - y * b * sin phi ) *^ e_x
^+^ ( y * b * cos phi + x * a * sin phi ) *^ e_y
2023-01-09 03:27:08 +00:00
in circleSpline @( Record EllipseBrushFields ) mkPt
2023-01-08 16:16:14 +00:00
where
2023-01-09 03:27:08 +00:00
e_x, e_y :: D ( Record EllipseBrushFields ) ( T ( 2 ) )
2023-01-08 16:16:14 +00:00
e_x = pure $ T $ 2 1 0
e_y = pure $ T $ 2 0 1
2023-01-09 03:27:08 +00:00
kon = konst @( Record EllipseBrushFields )
2023-01-08 16:16:14 +00:00
--ellipseArc :: 2 ~> 2
--ellipseArc = brushStroke ( linear myPath ) ( uncurryD $ fmap bezier3 myBrush )
--testing :: Double -> Double -> (# Double, T ( 2 ) #)
--testing :: Double -> Double -> (# Double, T ( 2) #)
--testing t s = envelopeEquation ellipseArc t s