2022-12-11 01:33:34 +00:00
|
|
|
{-# LANGUAGE BangPatterns #-}
|
2020-11-12 17:34:46 +00:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
{-# LANGUAGE GADTs #-}
|
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE PolyKinds #-}
|
2020-08-29 17:41:07 +00:00
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
2021-04-25 23:17:27 +00:00
|
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2020-08-29 17:41:07 +00:00
|
|
|
|
2020-11-12 17:34:46 +00:00
|
|
|
module MetaBrush.Asset.Brushes where
|
|
|
|
|
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
|
|
|
|
import Math.Vector2D
|
2020-11-14 22:32:23 +00:00
|
|
|
import MetaBrush.Brush
|
2022-12-11 01:33:34 +00:00
|
|
|
( Brush(..), SomeBrush(..) )
|
|
|
|
import MetaBrush.Records
|
|
|
|
( Rec, WithParams(..), I(..) )
|
|
|
|
import qualified MetaBrush.Records as Rec
|
2020-08-29 17:41:07 +00:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-02-11 21:05:13 +00:00
|
|
|
type CircleBrushFields = '[ '("r", Double) ]
|
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.
|
|
|
|
c :: Double
|
|
|
|
c = 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
|
|
|
|
[ Bezier3To (p 1 c) (p c 1 ) (NextPoint (p 0 1 )) ()
|
|
|
|
, Bezier3To (p (-c) 1) (p (-1) c ) (NextPoint (p (-1) 0 )) ()
|
|
|
|
, Bezier3To (p (-1) (-c)) (p (-c) (-1)) (NextPoint (p 0 (-1))) ()
|
|
|
|
]
|
|
|
|
lastCrv =
|
|
|
|
Bezier3To (p c (-1)) (p 1 (-c)) 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
|
2022-12-11 01:33:34 +00:00
|
|
|
deflts :: Rec CircleBrushFields
|
|
|
|
deflts = Rec.insert @"r" (I 1) Rec.empty
|
|
|
|
shape :: Rec CircleBrushFields -> SplinePts 'Closed
|
|
|
|
shape params =
|
|
|
|
let !(I !r) = Rec.lookup @"r" params
|
|
|
|
in circleSpline ( \ x y -> Point2D (r * x) (r * y) )
|
2020-08-29 17:41:07 +00:00
|
|
|
|
2022-12-11 01:33:34 +00:00
|
|
|
type EllipseBrushFields = '[ '("a", Double), '("b", Double), '("phi", Double) ]
|
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
|
|
|
|
deflts :: Rec EllipseBrushFields
|
|
|
|
deflts = Rec.insert @"a" (I 1)
|
|
|
|
$ Rec.insert @"b" (I 1)
|
|
|
|
$ Rec.insert @"phi" (I 0)
|
|
|
|
$ Rec.empty
|
|
|
|
shape :: Rec EllipseBrushFields -> SplinePts 'Closed
|
|
|
|
shape params =
|
|
|
|
let
|
|
|
|
!(I !a ) = Rec.lookup @"a" params
|
|
|
|
!(I !b ) = Rec.lookup @"b" params
|
|
|
|
!(I !phi) = Rec.lookup @"phi" params
|
|
|
|
in circleSpline ( \ x y -> Point2D (a * x * cos phi - b * y * sin phi)
|
|
|
|
(b * y * cos phi + a * x * sin phi) )
|