some optimisations

This commit is contained in:
sheaf 2023-01-29 04:03:36 +01:00
parent 4174d9b5b6
commit 8ac22b4738
8 changed files with 165 additions and 83 deletions

View file

@ -90,10 +90,12 @@ common common
ViewPatterns ViewPatterns
ghc-options: ghc-options:
-O1 -O2
-fexpose-all-unfoldings -fexpose-all-unfoldings
-funfolding-use-threshold=16 -- -funfolding-use-threshold=1000
-fspecialise-aggressively -fspecialise-aggressively
-flate-dmd-anal
-fmax-worker-args=200
-optc-O3 -optc-O3
-Wall -Wall
-Wcompat -Wcompat
@ -102,7 +104,6 @@ common common
-fwarn-incomplete-uni-patterns -fwarn-incomplete-uni-patterns
-fwarn-missing-deriving-strategies -fwarn-missing-deriving-strategies
-fno-warn-unticked-promoted-constructors -fno-warn-unticked-promoted-constructors
-rtsopts
if flag(asserts) if flag(asserts)
cpp-options: cpp-options:

View file

@ -57,7 +57,7 @@ import Control.Monad.Trans.State.Strict
-- MetaBrush -- MetaBrush
import Math.Algebra.Dual import Math.Algebra.Dual
( fun ) ( D2𝔸1(..), fun )
import qualified Math.Bezier.Cubic as Cubic import qualified Math.Bezier.Cubic as Cubic
( Bezier(..), fromQuadratic ) ( Bezier(..), fromQuadratic )
import Math.Bezier.Cubic.Fit import Math.Bezier.Cubic.Fit
@ -79,6 +79,8 @@ import Math.Interval
( Extent(Point) ) ( Extent(Point) )
import Math.Linear import Math.Linear
( (..), T(..) ) ( (..), T(..) )
import Math.Module
( Module((*^)), normalise )
import MetaBrush.Asset.Colours import MetaBrush.Asset.Colours
( Colours, ColourRecord(..) ) ( Colours, ColourRecord(..) )
import MetaBrush.Brush import MetaBrush.Brush
@ -648,22 +650,28 @@ drawFitPoint _ zoom ( FitTangent { fitPoint = 2 x y, fitTangent = V2 tx ty }
drawCusp :: Colours -> Double -> Cusp -> Cairo.Render () drawCusp :: Colours -> Double -> Cusp -> Cairo.Render ()
drawCusp _ zoom drawCusp _ zoom
( Cusp { cuspPathCoords = 2 px py ( Cusp { cuspPathCoords = D21 { _D21_v = 2 px py
, _D21_dx = tgt }
, cuspStrokeCoords = 2 cx cy } ) = do , cuspStrokeCoords = 2 cx cy } ) = do
-- Draw a white circle on the path at the cusp point. -- Draw a line perpendicular to the underlying path at the cusp.
let
!( V2 tx ty ) = ( 6 / zoom ) *^ normalise tgt
Cairo.save Cairo.save
Cairo.translate px py Cairo.translate px py
Cairo.arc 0 0 ( 4 / zoom ) 0 ( 2 * pi ) Cairo.moveTo -ty tx
Cairo.setSourceRGBA 1.0 1.0 1.0 1.0 Cairo.lineTo ty -tx
--withRGBA path Cairo.setSourceRGBA
Cairo.setSourceRGBA 0 0 0 0.75
Cairo.setLineWidth ( 2 / zoom )
Cairo.stroke Cairo.stroke
Cairo.restore Cairo.restore
-- Draw a black circle on the outline at the cusp point. -- Draw a circle around the outline cusp point.
Cairo.save Cairo.save
Cairo.translate cx cy Cairo.translate cx cy
Cairo.arc 0 0 ( 4 / zoom ) 0 ( 2 * pi ) Cairo.arc 0 0 ( 4 / zoom ) 0 ( 2 * pi )
Cairo.setSourceRGBA 0 0 0 1.0 Cairo.setSourceRGBA 0 0 0 0.75
Cairo.stroke Cairo.stroke
Cairo.restore Cairo.restore

View file

@ -1,9 +1,12 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module MetaBrush.Asset.Brushes where module MetaBrush.Asset.Brushes
( lookupBrush, brushes
, CircleBrushFields, circle
, EllipseBrushFields, ellipse
) where
-- base -- base
import Prelude import Prelude
@ -30,7 +33,7 @@ import qualified Data.HashMap.Strict as HashMap
import Math.Algebra.Dual import Math.Algebra.Dual
import Math.Bezier.Spline import Math.Bezier.Spline
import Math.Interval import Math.Interval
( type I )
import Math.Linear import Math.Linear
import Math.Module import Math.Module
( Module((^+^), (*^)) ) ( Module((^+^), (*^)) )
@ -65,6 +68,7 @@ circle = BrushData "circle" ( WithParams deflts circleBrush )
where where
deflts :: Record CircleBrushFields deflts :: Record CircleBrushFields
deflts = MkR ( 1 1 ) deflts = MkR ( 1 1 )
{-# INLINE circle #-}
type EllipseBrushFields = '[ "a", "b", "phi" ] type EllipseBrushFields = '[ "a", "b", "phi" ]
ellipse :: Brush EllipseBrushFields ellipse :: Brush EllipseBrushFields
@ -72,6 +76,7 @@ ellipse = BrushData "ellipse" ( WithParams deflts ellipseBrush )
where where
deflts :: Record EllipseBrushFields deflts :: Record EllipseBrushFields
deflts = MkR ( 3 1 1 0 ) deflts = MkR ( 3 1 1 0 )
{-# INLINE ellipse #-}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Differentiable brushes. -- Differentiable brushes.
@ -91,6 +96,7 @@ circleSpline p = sequenceA $
] ]
lastCrv = lastCrv =
Bezier3To ( p κ -1 ) ( p 1 -κ ) BackToStart () Bezier3To ( p κ -1 ) ( p 1 -κ ) BackToStart ()
{-# INLINE circleSpline #-}
circleBrush :: forall i k irec circleBrush :: forall i k irec
. ( irec ~ I i ( Record CircleBrushFields ) . ( irec ~ I i ( Record CircleBrushFields )
@ -120,23 +126,24 @@ circleBrush _ mkI =
e_y = pure $ mkI $ 2 0 1 e_y = pure $ mkI $ 2 0 1
kon = konst @( I i Double ) @k @irec . mkI kon = konst @( I i Double ) @k @irec . mkI
{-# INLINEABLE circleBrush #-}
ellipseBrush :: forall i k irec ellipseBrush :: forall i k irec
. ( irec ~ I i ( Record EllipseBrushFields ) . ( irec ~ I i ( Record EllipseBrushFields )
, Module , Module
( D k irec ( I i Double ) ) ( D k irec ( I i Double ) )
( D k irec ( I i ( 2 ) ) ) ( D k irec ( I i ( 2 ) ) )
, Module ( I i Double ) ( T ( I i Double ) ) , Module ( I i Double ) ( T ( I i Double ) )
, HasChainRule ( I i Double ) k irec , HasChainRule ( I i Double ) k irec
, Representable ( I i Double ) irec , Representable ( I i Double ) irec
, Applicative ( D k irec ) , Applicative ( D k irec )
, Transcendental ( D k irec ( I i Double ) ) , Transcendental ( D k irec ( I i Double ) )
-- TODO: make a synonym for the above... -- TODO: make a synonym for the above...
-- it seems DiffInterp isn't exactly right -- it seems DiffInterp isn't exactly right
) )
=> Proxy# i => Proxy# i
-> ( forall a. a -> I i a ) -> ( forall a. a -> I i a )
-> C k irec ( Spline 'Closed () ( I i ( 2 ) ) ) -> C k irec ( Spline 'Closed () ( I i ( 2 ) ) )
ellipseBrush _ mkI = ellipseBrush _ mkI =
D \ params -> D \ params ->
let a, b, phi :: D k irec ( I i Double ) let a, b, phi :: D k irec ( I i Double )
@ -154,3 +161,4 @@ ellipseBrush _ mkI =
e_y = pure $ mkI $ 2 0 1 e_y = pure $ mkI $ 2 0 1
kon = konst @( I i Double ) @k @irec . mkI kon = konst @( I i Double ) @k @irec . mkI
{-# INLINEABLE ellipseBrush #-}

View file

@ -37,6 +37,7 @@ import GHC.TypeNats
-- MetaBrush -- MetaBrush
import Math.Algebra.Dual.Internal import Math.Algebra.Dual.Internal
import Math.Interval.Internal
import Math.Linear import Math.Linear
import Math.Module import Math.Module
import Math.Monomial import Math.Monomial
@ -258,29 +259,33 @@ instance Ring r => Ring ( D2𝔸1 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D2𝔸1 Double ) #-}
{-# SPECIALISE instance Ring ( D2𝔸1 ( 𝕀 Double ) ) #-}
instance Ring r => Ring ( D3𝔸1 r ) where instance Ring r => Ring ( D3𝔸1 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D3𝔸1 Double ) #-}
{-# SPECIALISE instance Ring ( D3𝔸1 ( 𝕀 Double ) ) #-}
--instance Ring r => Ring ( D1𝔸2 r ) where --instance Ring r => Ring ( D1𝔸2 r ) where
-- !dr1 * !dr2 = -- !dr1 * !dr2 =
@ -300,29 +305,33 @@ instance Ring r => Ring ( D2𝔸2 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D2𝔸2 Double ) #-}
{-# SPECIALISE instance Ring ( D2𝔸2 ( 𝕀 Double ) ) #-}
instance Ring r => Ring ( D3𝔸2 r ) where instance Ring r => Ring ( D3𝔸2 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D3𝔸2 Double ) #-}
{-# SPECIALISE instance Ring ( D3𝔸2 ( 𝕀 Double ) ) #-}
--instance Ring r => Ring ( D1𝔸3 r ) where --instance Ring r => Ring ( D1𝔸3 r ) where
-- !dr1 * !dr2 = -- !dr1 * !dr2 =
@ -342,29 +351,33 @@ instance Ring r => Ring ( D2𝔸3 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D2𝔸3 Double ) #-}
{-# SPECIALISE instance Ring ( D2𝔸3 ( 𝕀 Double ) ) #-}
instance Ring r => Ring ( D3𝔸3 r ) where instance Ring r => Ring ( D3𝔸3 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D3𝔸3 Double ) #-}
{-# SPECIALISE instance Ring ( D3𝔸3 ( 𝕀 Double ) ) #-}
--instance Ring r => Ring ( D1𝔸4 r ) where --instance Ring r => Ring ( D1𝔸4 r ) where
-- !dr1 * !dr2 = -- !dr1 * !dr2 =
@ -384,29 +397,33 @@ instance Ring r => Ring ( D2𝔸4 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D2𝔸4 Double ) #-}
{-# SPECIALISE instance Ring ( D2𝔸4 ( 𝕀 Double ) ) #-}
instance Ring r => Ring ( D3𝔸4 r ) where instance Ring r => Ring ( D3𝔸4 r ) where
!dr1 * !dr2 = !dr1 * !dr2 =
let let
o :: r o :: r
o = fromInteger 0 !o = fromInteger 0
p :: r -> r -> r p :: r -> r -> r
p = (+) @r !p = (+) @r
m :: r -> r -> r m :: r -> r -> r
m = (*) @r !m = (*) @r
in in
$$( prodRuleQ $$( prodRuleQ
[|| o ||] [|| p ||] [|| m ||] [|| o ||] [|| p ||] [|| m ||]
[|| dr1 ||] [|| dr2 ||] ) [|| dr1 ||] [|| dr2 ||] )
{-# SPECIALISE instance Ring ( D3𝔸4 Double ) #-}
{-# SPECIALISE instance Ring ( D3𝔸4 ( 𝕀 Double ) ) #-}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Field & transcendental instances -- Field & transcendental instances

View file

@ -21,6 +21,10 @@ import GHC.Generics
import GHC.TypeNats import GHC.TypeNats
( KnownNat ) ( KnownNat )
-- deepseq
import Control.DeepSeq
( NFData )
-- template-haskell -- template-haskell
import Language.Haskell.TH import Language.Haskell.TH
( CodeQ ) ( CodeQ )
@ -50,6 +54,7 @@ import TH.Utils
-- | \( \mathbb{Z} \). -- | \( \mathbb{Z} \).
newtype D𝔸0 v = D0 { _D0_v :: v } newtype D𝔸0 v = D0 { _D0_v :: v }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D𝔸0 via Generically1 D𝔸0
@ -59,6 +64,7 @@ data D1𝔸1 v =
, _D11_dx :: !( T v ) , _D11_dx :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D1𝔸1 via Generically1 D1𝔸1
@ -69,6 +75,7 @@ data D2𝔸1 v =
, _D21_dxdx :: !( T v ) , _D21_dxdx :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D2𝔸1 via Generically1 D2𝔸1
@ -89,6 +96,7 @@ data D1𝔸2 v =
, _D12_dx, _D12_dy :: !( T v ) , _D12_dx, _D12_dy :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D1𝔸2 via Generically1 D1𝔸2
@ -110,6 +118,7 @@ data D3𝔸2 v =
, _D32_dxdxdx, _D32_dxdxdy, _D32_dxdydy, _D32_dydydy :: !( T v ) , _D32_dxdxdx, _D32_dxdxdy, _D32_dxdydy, _D32_dydydy :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D3𝔸2 via Generically1 D3𝔸2
@ -119,6 +128,7 @@ data D1𝔸3 v =
, _D13_dx, _D13_dy, _D13_dz :: !( T v ) , _D13_dx, _D13_dy, _D13_dz :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D1𝔸3 via Generically1 D1𝔸3
@ -129,6 +139,7 @@ data D2𝔸3 v =
, _D23_dxdx, _D23_dxdy, _D23_dydy, _D23_dxdz, _D23_dydz, _D23_dzdz :: !( T v ) , _D23_dxdx, _D23_dxdy, _D23_dydy, _D23_dxdz, _D23_dydz, _D23_dzdz :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D2𝔸3 via Generically1 D2𝔸3
@ -141,6 +152,7 @@ data D3𝔸3 v =
, _D33_dxdxdz, _D33_dxdydz, _D33_dxdzdz, _D33_dydydz, _D33_dydzdz, _D33_dzdzdz :: !( T v ) , _D33_dxdxdz, _D33_dxdydz, _D33_dxdzdz, _D33_dydydz, _D33_dydzdz, _D33_dzdzdz :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D3𝔸3 via Generically1 D3𝔸3
@ -150,6 +162,7 @@ data D1𝔸4 v =
, _D14_dx, _D14_dy, _D14_dz, _D14_dw :: !( T v ) , _D14_dx, _D14_dy, _D14_dz, _D14_dw :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D1𝔸4 via Generically1 D1𝔸4
@ -161,6 +174,7 @@ data D2𝔸4 v =
, _D24_dydz, _D24_dzdz, _D24_dxdw, _D24_dydw, _D24_dzdw, _D24_dwdw :: !( T v ) , _D24_dydz, _D24_dzdz, _D24_dxdw, _D24_dydw, _D24_dzdw, _D24_dwdw :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D2𝔸4 via Generically1 D2𝔸4
@ -176,6 +190,7 @@ data D3𝔸4 v =
_D34_dxdwdw, _D34_dydwdw, _D34_dzdwdw, _D34_dwdwdw :: !( T v ) _D34_dxdwdw, _D34_dydwdw, _D34_dzdwdw, _D34_dwdwdw :: !( T v )
} }
deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 ) deriving stock ( Show, Eq, Functor, Foldable, Traversable, Generic, Generic1 )
deriving anyclass NFData
deriving Applicative deriving Applicative
via Generically1 D3𝔸4 via Generically1 D3𝔸4

View file

@ -74,7 +74,7 @@ import Math.Epsilon
import Math.Linear.Solve import Math.Linear.Solve
( linearSolve ) ( linearSolve )
import Math.Module import Math.Module
( Module((*^), (^-^)) ( Module((*^), (^-^)), lerp
, Inner((^.^)), quadrance , Inner((^.^)), quadrance
) )
import Math.Roots import Math.Roots
@ -82,6 +82,8 @@ import Math.Roots
import Math.Linear import Math.Linear
( Mat22(..), (..), T(..) ) ( Mat22(..), (..), T(..) )
import Debug.Utils
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Parameters to the curve fitting algorithm. -- | Parameters to the curve fitting algorithm.
@ -121,37 +123,40 @@ data FitPoint
fitSpline fitSpline
:: FitParameters :: FitParameters
-> ( 1 -> ( 2, T ( 2 ) ) ) -- ^ curve \( t \mapsto ( C(t), C'(t) ) \) to fit -> ( 1 -> ( 2, T ( 2 ) ) ) -- ^ curve \( t \mapsto ( C(t), C'(t) ) \) to fit
-> ( 1, 1 ) -- ^ interval \( [t_min, t_max] \)
-> ( SplinePts Open, Seq FitPoint ) -> ( SplinePts Open, Seq FitPoint )
fitSpline ( FitParameters {..} ) = go 0 fitSpline ( FitParameters {..} ) curveFn = go 0
where where
dt :: Double dt :: Double
dt = recip ( fromIntegral nbSegments ) dt = recip ( fromIntegral nbSegments )
go go
:: Int :: Int
-> ( 1 -> ( 2, T ( 2 ) ) ) -> ( 1, 1 )
-> ( SplinePts Open, Seq FitPoint ) -> ( SplinePts Open, Seq FitPoint )
go subdiv curveFn = go subdiv (t_min, t_max) =
let let
p, r :: 2 p, r :: 2
tp, tr :: T ( 2 ) tp, tr :: T ( 2 )
qs :: [ 2 ] qs :: [ 2 ]
(p, tp) = curveFn $ 1 0 (p, tp) = curveFn $ T ( 1 0.0001 ) t_min
(r, tr) = curveFn $ 1 1 (r, tr) = curveFn $ T ( 1 -0.0001 ) t_max
qs = [ fst $ curveFn ( 1 $ dt * fromIntegral j ) | j <- [ 1 .. nbSegments - 1 ] ] qs = [ fst $ curveFn ( lerp @( T ( 1 ) ) ( dt * fromIntegral j ) t_min t_max )
| j <- [ 1 .. nbSegments - 1 ] ]
in in
case fitPiece dist_tol t_tol maxIters p tp qs r tr of case fitPiece dist_tol t_tol maxIters p tp qs r tr of
( bez, Max ( Arg max_sq_error t_split ) ) ( bez, Max ( Arg max_sq_error t_split_0 ) )
| subdiv >= maxSubdiv | subdiv >= maxSubdiv
|| max_sq_error <= dist_tol ^ ( 2 :: Int ) || max_sq_error <= dist_tol ^ ( 2 :: Int )
-> ( openCubicBezierCurveSpline () bez, ( FitTangent p tp :<| Seq.fromList ( map FitPoint qs ) ) :|> FitTangent r tr ) -> trace ( unlines [ "fitSpline: piece is OK", "t_min = " ++ show t_min, "start = " ++ show p, "start tgt = " ++ show tp, "t_max = " ++ show t_max, "end = " ++ show r, "end tgt = " ++ show tr ] )
$ ( openCubicBezierCurveSpline () bez, ( FitTangent p tp :<| Seq.fromList ( map FitPoint qs ) ) :|> FitTangent r tr )
| let | let
t_split_eff :: Double t_split :: 1
t_split_eff = min ( 1 - dt ) $ max dt t_split t_split = 1 $ min ( 1 - dt ) $ max dt t_split_0
c1, c2 :: SplinePts Open c1, c2 :: SplinePts Open
ps1, ps2 :: Seq FitPoint ps1, ps2 :: Seq FitPoint
( ( c1, ps1 ), ( c2, ps2 ) ) ( ( c1, ps1 ), ( c2, ps2 ) )
= ( go ( subdiv + 1 ) ( \ ( 1 t ) -> curveFn $ 1 $ t * t_split_eff ) = ( go ( subdiv + 1 ) (t_min , t_split)
, go ( subdiv + 1 ) ( \ ( 1 t ) -> curveFn $ 1 $ t_split_eff + t * ( 1 - t_split_eff ) ) , go ( subdiv + 1 ) (t_split, t_max )
) `Parallel.Strategy.using` ) `Parallel.Strategy.using`
( Parallel.Strategy.parTuple2 Parallel.Strategy.rdeepseq Parallel.Strategy.rdeepseq ) ( Parallel.Strategy.parTuple2 Parallel.Strategy.rdeepseq Parallel.Strategy.rdeepseq )
-> ( c1 <> c2, ps1 <> ps2 ) -> ( c1 <> c2, ps1 <> ps2 )

View file

@ -18,8 +18,6 @@ module Math.Bezier.Stroke
where where
-- base -- base
import Prelude
hiding ( unzip )
import Control.Arrow import Control.Arrow
( first, (***) ) ( first, (***) )
import Control.Applicative import Control.Applicative
@ -37,11 +35,15 @@ import Data.Foldable
import Data.Functor.Identity import Data.Functor.Identity
( Identity(..) ) ( Identity(..) )
import Data.List import Data.List
( nub, partition ) ( nub, partition, sort )
import Data.List.NonEmpty import Data.List.NonEmpty
( unzip ) ( NonEmpty )
import qualified Data.List.NonEmpty as NE
( cons, singleton, unzip )
import Data.Maybe import Data.Maybe
( fromMaybe, isJust, listToMaybe, mapMaybe ) ( fromMaybe, isJust, listToMaybe, mapMaybe )
import Data.Semigroup
( sconcat )
import GHC.Exts import GHC.Exts
( newMutVar#, runRW# ( newMutVar#, runRW#
, Proxy#, proxy# , Proxy#, proxy#
@ -211,8 +213,8 @@ data Cusp
= Cusp = Cusp
{ cuspParameters :: !( 2 ) { cuspParameters :: !( 2 )
-- ^ @(t,s)@ parameter values of the cusp -- ^ @(t,s)@ parameter values of the cusp
, cuspPathCoords :: !( 2 ) , cuspPathCoords :: !( D 2 ( 1 ) ( 2 ) )
-- ^ path point coordinates -- ^ path point coordinates and tangent
, cuspStrokeCoords :: !( 2 ) , cuspStrokeCoords :: !( 2 )
-- ^ brush stroke point coordinates -- ^ brush stroke point coordinates
} }
@ -417,14 +419,19 @@ computeStrokeOutline fitParams ptParams toBrushParams brushFn spline@( Spline {
-- No cached fit: compute the fit anew. -- No cached fit: compute the fit anew.
Nothing -> do Nothing -> do
let let
-- Split up the path at the cusps
cusps :: [ Cusp ] cusps :: [ Cusp ]
cusps = outlineDefiniteCusps fwdBwd cusps = outlineDefiniteCusps fwdBwd
++ outlinePotentialCusps fwdBwd ++ outlinePotentialCusps fwdBwd
-- SLD TODO: here!!! intervals :: NonEmpty ( 1, 1 )
intervals = splitInterval ( 1 0, 1 1 )
$ sort [ 1 t | Cusp { cuspParameters = 2 t _ } <- cusps ]
fwdData, bwdData :: ( SplinePts Open, Seq FitPoint ) fwdData, bwdData :: ( SplinePts Open, Seq FitPoint )
( fwdData, bwdData ) = ( fwdData, bwdData ) =
( fitSpline fitParams ( fst . outlineFn fwdBwd ) ( sconcat $ fmap ( fitSpline fitParams ( fst . outlineFn fwdBwd ) ) intervals
, fitSpline fitParams ( snd . outlineFn fwdBwd ) ) , sconcat $ fmap ( fitSpline fitParams ( snd . outlineFn fwdBwd ) ) intervals )
-- TODO: use foldMap1 once that's in base.
`Strats.using` `Strats.using`
( Strats.parTuple2 Strats.rdeepseq Strats.rdeepseq ) ( Strats.parTuple2 Strats.rdeepseq Strats.rdeepseq )
outlineData :: OutlineData outlineData :: OutlineData
@ -564,7 +571,8 @@ outlineFunction ptParams toBrushParams brushFromParams = \ sp0 crv ->
path :: C ( ExtentOrder 'Point ) ( 1 ) ( 2 ) path :: C ( ExtentOrder 'Point ) ( 1 ) ( 2 )
( path, usedParams ) = pathAndUsedParams @Point id ( path, usedParams ) = pathAndUsedParams @Point id
curves :: 1 -> Seq ( 1 -> StrokeDatum Point ) curves :: 1 -- t
-> Seq ( 1 {- s -} -> StrokeDatum Point )
curves = curves =
brushStrokeData @Point @( ExtentOrder 'Point ) @brushParams brushStrokeData @Point @( ExtentOrder 'Point ) @brushParams
path path
@ -574,7 +582,8 @@ outlineFunction ptParams toBrushParams brushFromParams = \ sp0 crv ->
) )
( brushFromParams @Point proxy# id ) ( brushFromParams @Point proxy# id )
curvesI :: 𝕀 1 -> Seq ( 𝕀 1 -> StrokeDatum 'Interval ) curvesI :: 𝕀 1 -- t
-> Seq ( 𝕀 1 {- s -} -> StrokeDatum 'Interval )
curvesI = brushStrokeData @'Interval @( ExtentOrder 'Interval ) @brushParams curvesI = brushStrokeData @'Interval @( ExtentOrder 'Interval ) @brushParams
pathI pathI
( chainRule @( 𝕀 Double ) @( ExtentOrder 'Interval ) ( chainRule @( 𝕀 Double ) @( ExtentOrder 'Interval )
@ -602,7 +611,7 @@ outlineFunction ptParams toBrushParams brushFromParams = \ sp0 crv ->
$ runD ( brushFromParams @Point proxy# id ) $ runD ( brushFromParams @Point proxy# id )
$ toBrushParams params_t $ toBrushParams params_t
--( newtDunno, newtSols ) = intervalNewtonGS InverseMidJacobian 0.0001 curvesI ( newtDunno, newtSols ) = intervalNewtonGS InverseMidJacobian 0.0001 curvesI
in --trace in --trace
-- ( unlines $ -- ( unlines $
@ -617,8 +626,8 @@ outlineFunction ptParams toBrushParams brushFromParams = \ sp0 crv ->
-- ) $ -- ) $
OutlineInfo OutlineInfo
{ outlineFn = fwdBwd { outlineFn = fwdBwd
, outlineDefiniteCusps = [] -- map ( cuspCoords curves ) newtSols , outlineDefiniteCusps = map ( cuspCoords curves ) newtSols
, outlinePotentialCusps = [] -- map ( cuspCoords curves ) newtDunno , outlinePotentialCusps = map ( cuspCoords curves ) newtDunno
} }
{-# INLINEABLE outlineFunction #-} {-# INLINEABLE outlineFunction #-}
@ -640,6 +649,20 @@ lastTangent ( Spline { splineCurves = NoCurves } ) = Nothing
lastTangent ( Spline { splineStart, splineCurves = ClosedCurves Empty lst } ) = Just $ endTangent splineStart splineStart lst lastTangent ( Spline { splineStart, splineCurves = ClosedCurves Empty lst } ) = Just $ endTangent splineStart splineStart lst
lastTangent ( Spline { splineStart, splineCurves = ClosedCurves ( _ :|> prev ) lst } ) = Just $ endTangent splineStart ( openCurveEnd prev ) lst lastTangent ( Spline { splineStart, splineCurves = ClosedCurves ( _ :|> prev ) lst } ) = Just $ endTangent splineStart ( openCurveEnd prev ) lst
-- | Split up the given interval at the list of points provided.
--
-- Assumes the list is sorted.
splitInterval :: Ord a => ( a, a ) -> [ a ] -> NonEmpty ( a, a )
splitInterval ( start, end ) []
= NE.singleton ( start, end )
splitInterval ( start, end ) ( split : splits )
| split >= end
= NE.singleton ( start, end )
| split <= start
= splitInterval ( start, end ) splits
| otherwise
= NE.cons ( start, split ) $ splitInterval ( split, end ) splits
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Compute the join at a point of discontinuity of the tangent vector direction (G1 discontinuity). -- | Compute the join at a point of discontinuity of the tangent vector direction (G1 discontinuity).
@ -668,7 +691,7 @@ joinBetweenOffsets
= let = let
pcs, lastAndRest :: Maybe ( SplinePts Open ) pcs, lastAndRest :: Maybe ( SplinePts Open )
( pcs, lastAndRest ) ( pcs, lastAndRest )
= unzip = NE.unzip
$ ( discardCurveData *** discardCurveData ) $ ( discardCurveData *** discardCurveData )
. splitSplineAt ( i2 - i1 ) . splitSplineAt ( i2 - i1 )
<$> dropCurves i1 openSpline <$> dropCurves i1 openSpline
@ -1049,6 +1072,7 @@ newtype ZipSeq a = ZipSeq { getZipSeq :: Seq a }
instance Applicative ZipSeq where instance Applicative ZipSeq where
pure _ = error "only use Applicative ZipSeq with non-empty Traversable functors" pure _ = error "only use Applicative ZipSeq with non-empty Traversable functors"
liftA2 f ( ZipSeq xs ) ( ZipSeq ys ) = ZipSeq ( Seq.zipWith f xs ys ) liftA2 f ( ZipSeq xs ) ( ZipSeq ys ) = ZipSeq ( Seq.zipWith f xs ys )
{-# INLINE liftA2 #-}
brushStrokeData :: forall i k brushParams arr brushStrokeData :: forall i k brushParams arr
. ( k ~ ExtentOrder i, CurveOrder k, arr ~ C k . ( k ~ ExtentOrder i, CurveOrder k, arr ~ C k
@ -1181,12 +1205,12 @@ cuspCoords :: ( 1 -> Seq ( 1 -> StrokeDatum 'Point ) )
-> Cusp -> Cusp
cuspCoords eqs ( 𝕀 ( 1 t_lo ) ( 1 t_hi ), i, 𝕀 ( 1 s_lo ) ( 1 s_hi ) ) cuspCoords eqs ( 𝕀 ( 1 t_lo ) ( 1 t_hi ), i, 𝕀 ( 1 s_lo ) ( 1 s_hi ) )
| StrokeDatum | StrokeDatum
{ dpath = D21 { _D21_v = path } { dpath
, dstroke = D22 { _D22_v = stroke } } , dstroke = D22 { _D22_v = stroke } }
<- ( eqs ( 1 t_mid ) `Seq.index` i ) ( 1 s_mid ) <- ( eqs ( 1 t_mid ) `Seq.index` i ) ( 1 s_mid )
= Cusp = Cusp
{ cuspParameters = 2 t_mid s_mid { cuspParameters = 2 t_mid s_mid
, cuspPathCoords = path , cuspPathCoords = dpath
, cuspStrokeCoords = stroke , cuspStrokeCoords = stroke
} }
where where

View file

@ -9,7 +9,8 @@ module Math.Module
( Module(..), lerp ( Module(..), lerp
, Inner(..), Cross(..) , Inner(..), Cross(..)
, Interpolatable , Interpolatable
, norm, squaredNorm, quadrance, distance , norm, squaredNorm, normalise
, quadrance, distance
, proj, projC, closestPointOnSegment , proj, projC, closestPointOnSegment
, strictlyParallel, convexCombination , strictlyParallel, convexCombination
@ -73,6 +74,9 @@ norm = sqrt . squaredNorm
squaredNorm :: forall m r. Inner r m => m -> r squaredNorm :: forall m r. Inner r m => m -> r
squaredNorm v = v ^.^ v squaredNorm v = v ^.^ v
normalise :: ( Floating r, Inner r m ) => m -> m
normalise v = recip ( norm v ) *^ v
-- | Squared distance between two points. -- | Squared distance between two points.
quadrance :: forall v r p. ( Inner r v, Torsor v p ) => p -> p -> r quadrance :: forall v r p. ( Inner r v, Torsor v p ) => p -> p -> r
quadrance p1 p2 = squaredNorm ( p1 --> p2 :: v ) quadrance p1 p2 = squaredNorm ( p1 --> p2 :: v )