2023-01-08 16:16:14 +00:00
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
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 )
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
|
|
|
|
-- 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((^+^), (*^)) )
|
2020-11-14 22:32:23 +00:00
|
|
|
|
import MetaBrush.Brush
|
2022-12-11 01:33:34 +00:00
|
|
|
|
( Brush(..), SomeBrush(..) )
|
|
|
|
|
import MetaBrush.Records
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
2023-01-08 16:16:14 +00:00
|
|
|
|
type CircleBrushFields = '[ "r" ]
|
2021-05-25 11:45:21 +00:00
|
|
|
|
|
2022-12-11 01:33:34 +00:00
|
|
|
|
lookupBrush :: Text -> Maybe SomeBrush
|
|
|
|
|
lookupBrush nm = HashMap.lookup nm brushes
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
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
|
2021-05-25 11:45:21 +00:00
|
|
|
|
|
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 }
|
2021-05-25 11:45:21 +00:00
|
|
|
|
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 ()
|
2021-05-25 11:45:21 +00:00
|
|
|
|
|
2022-12-11 01:33:34 +00:00
|
|
|
|
circle :: Brush CircleBrushFields
|
|
|
|
|
circle = BrushData "circle" (WithParams deflts shape)
|
2020-08-29 17:41:07 +00:00
|
|
|
|
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) )
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
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
|