metabrush/src/metabrushes/MetaBrush/Asset/Brushes.hs
2023-01-13 23:10:06 +01:00

132 lines
4.3 KiB
Haskell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module MetaBrush.Asset.Brushes where
-- base
import GHC.Exts
( Proxy# )
-- containers
import qualified Data.Sequence as Seq
( fromList )
-- text
import Data.Text
( Text )
-- unordered-containers
import Data.HashMap.Strict
( HashMap )
import qualified Data.HashMap.Strict as HashMap
( fromList, lookup )
-- MetaBrush
import Math.Bezier.Spline
import Math.Linear
import Math.Linear.Dual
( D, type (~>)(..), Differentiable, Diffy(konst), var )
import Math.Module
( Module((^+^), (*^)) )
import MetaBrush.Brush
( Brush(..), SomeBrush(..), WithParams(..) )
import MetaBrush.Records
( Record(MkR) )
--------------------------------------------------------------------------------
lookupBrush :: Text -> Maybe SomeBrush
lookupBrush nm = HashMap.lookup nm brushes
-- | All brushes supported by this application.
brushes :: HashMap Text SomeBrush
brushes = HashMap.fromList
[ ( nm, b )
| b@( SomeBrush ( BrushData { brushName = nm } ) )
<- [ SomeBrush circle, SomeBrush ellipse ]
]
-- | 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.
κ :: Double
κ = 0.5519150244935105707435627227925
type CircleBrushFields = '[ "r" ]
circle :: Brush CircleBrushFields
circle = BrushData "circle" ( WithParams deflts circleBrush )
where
deflts :: Record CircleBrushFields
deflts = MkR ( 1 1 )
type EllipseBrushFields = '[ "a", "b", "phi" ]
ellipse :: Brush EllipseBrushFields
ellipse = BrushData "ellipse" ( WithParams deflts ellipseBrush )
where
deflts :: Record EllipseBrushFields
deflts = MkR ( 3 1 1 0 )
--------------------------------------------------------------------------------
-- Differentiable brushes.
circleSpline :: forall i u v
. Applicative ( D ( I i u ) )
=> ( Double -> Double -> D ( I i u ) ( I i v ) )
-> D ( I i u ) ( Spline 'Closed () ( I i v ) )
circleSpline 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 ()
circleBrush :: forall i
. ( Differentiable i ( Record CircleBrushFields ) )
=> Proxy# i
-> ( forall a. a -> I i a )
-> I i ( Record CircleBrushFields ) ~> Spline 'Closed () ( I i ( 2 ) )
circleBrush _ mkI =
D \ params ->
let r :: D ( I i ( Record CircleBrushFields ) ) ( I i Double )
r = runD ( var ( Fin 1## ) ) params
mkPt :: Double -> Double -> D ( I i ( Record CircleBrushFields ) ) ( I i ( 2 ) )
mkPt ( kon -> x ) ( kon -> y )
= ( x * r ) *^ e_x
^+^ ( y * r ) *^ e_y
in circleSpline @i @( Record CircleBrushFields ) @( 2 ) mkPt
where
e_x, e_y :: D ( I i ( Record CircleBrushFields ) ) ( I i ( 2 ) )
e_x = pure $ mkI $ 2 1 0
e_y = pure $ mkI $ 2 0 1
kon = konst @( I i Double ) @( I i ( Record CircleBrushFields ) ) . mkI
ellipseBrush :: forall i
. ( Differentiable i ( Record EllipseBrushFields ) )
=> Proxy# i
-> ( forall a. a -> I i a )
-> I i ( Record EllipseBrushFields ) ~> Spline 'Closed () ( I i ( 2 ) )
ellipseBrush _ mkI =
D \ params ->
let a, b, phi :: D ( I i ( Record EllipseBrushFields ) ) ( I i Double )
a = runD ( var ( Fin 1## ) ) params
b = runD ( var ( Fin 2## ) ) params
phi = runD ( var ( Fin 3## ) ) params
mkPt :: Double -> Double -> D ( I i ( Record EllipseBrushFields ) ) ( I i ( 2 ) )
mkPt ( kon -> x ) ( kon -> y )
= ( x * a * cos phi - y * b * sin phi ) *^ e_x
^+^ ( y * b * cos phi + x * a * sin phi ) *^ e_y
in circleSpline @i @( Record EllipseBrushFields ) @( 2 ) mkPt
where
e_x, e_y :: D ( I i ( Record EllipseBrushFields ) ) ( I i ( 2 ) )
e_x = pure $ mkI $ 2 1 0
e_y = pure $ mkI $ 2 0 1
kon = konst @( I i Double ) @( I i ( Record EllipseBrushFields ) ) . mkI