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

130 lines
4 KiB
Haskell
Raw Normal View History

2023-01-08 16:16:14 +00:00
{-# 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
( Point2D(..), (..), T(..) )
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
--------------------------------------------------------------------------------
2023-01-08 16:16:14 +00:00
type CircleBrushFields = '[ "r" ]
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
2022-12-11 01:33:34 +00:00
circleSpline :: (Double -> Double -> ptData) -> Spline 'Closed () ptData
circleSpline p =
Spline { splineStart = p 1 0
, splineCurves = ClosedCurves crvs lastCrv }
where
2022-12-11 01:33:34 +00:00
crvs = Seq.fromList
2023-01-08 16:16:14 +00:00
[ 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)) ()
2022-12-11 01:33:34 +00:00
]
lastCrv =
2023-01-08 16:16:14 +00:00
Bezier3To (p κ -1) (p 1 -κ) BackToStart ()
2022-12-11 01:33:34 +00:00
circle :: Brush CircleBrushFields
circle = BrushData "circle" (WithParams deflts shape)
where
2023-01-08 16:16:14 +00:00
deflts :: Record CircleBrushFields
deflts = MkR ( 1 1 )
shape :: Record CircleBrushFields -> SplinePts 'Closed
shape ( MkR ( 1 r ) ) =
circleSpline ( \ x y -> Point2D (r * x) (r * y) )
2023-01-08 16:16:14 +00:00
type EllipseBrushFields = '[ "a", "b", "phi" ]
2020-11-12 17:34:46 +00:00
2022-12-11 01:33:34 +00:00
ellipse :: Brush EllipseBrushFields
ellipse = BrushData "ellipse" (WithParams deflts shape)
where
2023-01-08 16:16:14 +00:00
deflts :: Record EllipseBrushFields
deflts = MkR ( 3 1 1 0 )
shape :: Record EllipseBrushFields -> SplinePts 'Closed
shape ( MkR ( 3 a b phi ) ) =
circleSpline ( \ x y -> Point2D (a * x * cos phi - b * y * sin phi)
(b * y * cos phi + a * x * sin phi) )
--------------------------------------------------------------------------------
-- Differentiable brushes.
circleSpline2 :: ( Double -> Double -> D ( 3 ) ptData ) -> D ( 3 ) ( Spline 'Closed () ptData )
circleSpline2 p = sequenceA $
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 ()
ellipseBrush :: 3 ~> Spline 'Closed () ( 2 )
ellipseBrush =
D \ params ->
let a, b, phi :: D ( 3 ) Double
a = runD ( var @1 ) params
b = runD ( var @2 ) params
phi = runD ( var @3 ) params
mkPt :: Double -> Double -> D ( 3 ) ( 2 )
mkPt ( konst -> x ) ( konst -> y )
= fmap coerce
$ ( x * a * cos phi - y * b * sin phi ) *^ e_x
^+^ ( y * b * cos phi + x * a * sin phi ) *^ e_y
in circleSpline2 mkPt
where
e_x, e_y :: D ( 3 ) ( T ( 2 ) )
e_x = pure $ T $ 2 1 0
e_y = pure $ T $ 2 0 1
--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