diff --git a/MetaBrush.cabal b/MetaBrush.cabal index 8a46e24..6304b85 100644 --- a/MetaBrush.cabal +++ b/MetaBrush.cabal @@ -50,6 +50,8 @@ common common ^>= 0.3.1.0 , primitive ^>= 0.7.1.0 + , rounded-hw + ^>= 0.3 , transformers ^>= 0.5.6.2 @@ -195,8 +197,6 @@ library splines ^>= 3.2.2.0 , prim-instances ^>= 0.2 - , rounded-hw - ^>= 0.3 , vector >= 0.12.1.2 && < 0.14 diff --git a/src/app/MetaBrush/Render/Document.hs b/src/app/MetaBrush/Render/Document.hs index 8f98239..b826c14 100644 --- a/src/app/MetaBrush/Render/Document.hs +++ b/src/app/MetaBrush/Render/Document.hs @@ -19,6 +19,8 @@ import Data.Functor.Compose ( Compose(..) ) import Data.Int ( Int32 ) +import GHC.Exts + ( proxy# ) import GHC.Generics ( Generic, Generic1, Generically1(..) ) @@ -72,13 +74,13 @@ import Math.Bezier.Stroke , computeStrokeOutline ) import Math.Linear - ( ℝ(..), T(..) ) + ( ℝ(..), T(..), Extent(Point) ) import Math.Linear.Dual ( fun ) import MetaBrush.Asset.Colours ( Colours, ColourRecord(..) ) import MetaBrush.Brush - ( Brush(..) ) + ( Brush(..), WithParams(..) ) import MetaBrush.Context ( Modifier(..) , HoldAction(..), PartialPath(..) @@ -285,7 +287,7 @@ strokeRenderData fitParams , withParams = brushFn } <- fn -> -- This is the key place where we need to perform impedance matching - -- between the collection of parameters supplied along a strong and + -- between the collection of parameters supplied along a stroke and -- the collection of parameters expected by the brush. case intersect @pointFields @brushFields of Intersection @@ -303,7 +305,9 @@ strokeRenderData fitParams StrokeWithOutlineRenderData { strokeDataSpline = spline , strokeOutlineData = ( outline, fitPts ) - , strokeBrushFunction = fun brushFn . fun embedUsedParams . toUsedParams + , strokeBrushFunction = fun ( brushFn @Point proxy# id ) + . embedUsedParams + . toUsedParams } _ -> pure $ StrokeRenderData diff --git a/src/convert/MetaBrush/MetaFont/Convert.hs b/src/convert/MetaBrush/MetaFont/Convert.hs index e6e3292..4365aef 100644 --- a/src/convert/MetaBrush/MetaFont/Convert.hs +++ b/src/convert/MetaBrush/MetaFont/Convert.hs @@ -98,7 +98,7 @@ type LocatedTrail' l = Diagrams.Located (Diagrams.Trail' l Linear.V2 Double) data SomeSpline ptData where SomeSpline :: KnownSplineType clo => Spline clo (CachedStroke RealWorld) ptData -> SomeSpline ptData -parseMetaFontPath :: Interpolatable ptParams => Text -> [ptParams] -> Either MetaFontError (SomeSpline (PointData ptParams)) +parseMetaFontPath :: Interpolatable Double ptParams => Text -> [ptParams] -> Either MetaFontError (SomeSpline (PointData ptParams)) parseMetaFontPath pathText ptParams = do locTrail <- Bi.first PathParseError $ MetaFont.fromString @LocatedTrail pathText let @@ -110,7 +110,7 @@ parseMetaFontPath pathText ptParams = do SomeSpline <$> trailToSpline @Diagrams.Loop ( Diagrams.Loc { Diagrams.loc = loc, Diagrams.unLoc = trail' } ) ptParams trailToSpline :: forall l ptParams - . Interpolatable ptParams + . Interpolatable Double ptParams => LocatedTrail' l -> [ptParams] -> Either MetaFontError (Spline (Openness l) (CachedStroke RealWorld) (PointData ptParams)) @@ -167,7 +167,7 @@ trailToSpline (Diagrams.Loc { Diagrams.loc = Linear.P ( Linear.V2 sx sy ), Diagr go _ (_:_) [] = Left TooFewBrushParams segmentToCurve :: forall c ptParams - . Interpolatable ptParams + . Interpolatable Double ptParams => PointData ptParams -- ^ start point -> ptParams -- ^ parameters at end of curve -> Diagrams.Segment c Linear.V2 Double diff --git a/src/metabrushes/MetaBrush/Asset/Brushes.hs b/src/metabrushes/MetaBrush/Asset/Brushes.hs index 81ffcd3..886755a 100644 --- a/src/metabrushes/MetaBrush/Asset/Brushes.hs +++ b/src/metabrushes/MetaBrush/Asset/Brushes.hs @@ -1,8 +1,13 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module MetaBrush.Asset.Brushes where +-- base +import GHC.Exts + ( Proxy# ) + -- containers import qualified Data.Sequence as Seq ( fromList ) @@ -20,14 +25,14 @@ import qualified Data.HashMap.Strict as HashMap -- MetaBrush import Math.Bezier.Spline import Math.Linear - ( ℝ(..), Fin(..) ) import Math.Linear.Dual - ( D, type (~>)(..), var, konst ) + ( D, type (~>)(..), Differentiable, Diffy(konst), var ) import Math.Module ( Module((^+^), (*^)) ) import MetaBrush.Brush - ( Brush(..), SomeBrush(..) ) + ( Brush(..), SomeBrush(..), WithParams(..) ) import MetaBrush.Records + ( Record(MkR) ) -------------------------------------------------------------------------------- @@ -65,51 +70,62 @@ ellipse = BrushData "ellipse" ( WithParams deflts ellipseBrush ) -------------------------------------------------------------------------------- -- Differentiable brushes. -circleSpline :: Applicative ( D u ) => ( Double -> Double -> D u v ) -> D u ( Spline 'Closed () v ) +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)) () + [ 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 () + Bezier3To ( p κ -1 ) ( p 1 -κ ) BackToStart () -circleBrush :: Record CircleBrushFields ~> Spline 'Closed () ( ℝ 2 ) -circleBrush = +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 ( Record CircleBrushFields ) Double + let r :: D ( I i ( Record CircleBrushFields ) ) ( I i Double ) r = runD ( var ( Fin 1## ) ) params - mkPt :: Double -> Double -> D ( Record CircleBrushFields ) ( ℝ 2 ) + 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 @( Record CircleBrushFields ) mkPt + in circleSpline @i @( Record CircleBrushFields ) @( ℝ 2 ) mkPt where - e_x, e_y :: D ( Record CircleBrushFields ) ( ℝ 2 ) - e_x = pure $ ℝ2 1 0 - e_y = pure $ ℝ2 0 1 + 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 @Double @( Record CircleBrushFields ) + kon = konst @( I i Double ) @( I i ( Record CircleBrushFields ) ) . mkI -ellipseBrush :: Record EllipseBrushFields ~> Spline 'Closed () ( ℝ 2 ) -ellipseBrush = +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 ( Record EllipseBrushFields ) Double + 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 ( Record EllipseBrushFields ) ( ℝ 2 ) + 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 @( Record EllipseBrushFields ) mkPt + in circleSpline @i @( Record EllipseBrushFields ) @( ℝ 2 ) mkPt where - e_x, e_y :: D ( Record EllipseBrushFields ) ( ℝ 2 ) - e_x = pure $ ℝ2 1 0 - e_y = pure $ ℝ2 0 1 + 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 @Double @( Record EllipseBrushFields ) + kon = konst @( I i Double ) @( I i ( Record EllipseBrushFields ) ) . mkI diff --git a/src/metabrushes/MetaBrush/Brush.hs b/src/metabrushes/MetaBrush/Brush.hs index 16391e2..229d7c5 100644 --- a/src/metabrushes/MetaBrush/Brush.hs +++ b/src/metabrushes/MetaBrush/Brush.hs @@ -4,7 +4,8 @@ {-# LANGUAGE UndecidableInstances #-} module MetaBrush.Brush - ( Brush(..), SomeBrush(..), BrushFunction + ( WithParams(..) + , Brush(..), SomeBrush(..), BrushFunction , PointFields, provePointFields, duplicates ) where @@ -39,32 +40,45 @@ import qualified Data.Text as Text -- MetaBrush import Math.Linear - ( ℝ, Representable ) + ( ℝ, type I, Extent(Point, Interval) ) import Math.Linear.Dual - ( Diffy ) -import Math.Module - ( Interpolatable ) + ( type (~>), Differentiable ) import Math.Bezier.Spline - ( SplineType(Closed), SplinePts) + ( SplineType(Closed), Spline ) import MetaBrush.Records - ( KnownSymbols, Length, Record, WithParams ) + ( KnownSymbols, Length, Record ) import MetaBrush.Serialisable ( Serialisable ) -------------------------------------------------------------------------------- +-- | A differentiable function from a given record type, +-- with provided default values that can be overridden. +type WithParams :: [ Symbol ] -> ( Type -> Type ) -> Type +data WithParams params f = + WithParams + { defaultParams :: Record params + , withParams :: forall i + . ( Differentiable i ( Record params ) ) + => Proxy# i + -> ( forall a. a -> I i a ) + -> I i ( Record params ) ~> f ( I i ( ℝ 2 ) ) + } + +-------------------------------------------------------------------------------- + -- | A brush function: a function from a record of parameters to a closed spline. type BrushFunction :: [ Symbol ] -> Type -type BrushFunction brushFields = WithParams brushFields ( SplinePts Closed ) +type BrushFunction brushFields = WithParams brushFields ( Spline Closed () ) type Brush :: [ Symbol ] -> Type data Brush brushFields where BrushData :: forall brushFields - . ( KnownSymbols brushFields - , Representable Double ( ℝ ( Length brushFields) ) - , Diffy Double ( ℝ ( Length brushFields) ) - , Typeable brushFields ) + . ( KnownSymbols brushFields, Typeable brushFields + , Differentiable Point ( ℝ ( Length brushFields ) ) + , Differentiable Interval ( ℝ ( Length brushFields ) ) + ) => { brushName :: !Text , brushFunction :: BrushFunction brushFields } @@ -97,16 +111,16 @@ class ( KnownSymbols pointFields, Typeable pointFields , Serialisable ( Record pointFields ) , Show ( Record pointFields ) , NFData ( Record pointFields ) - , Interpolatable ( Record pointFields ) - , Representable Double ( ℝ ( Length pointFields ) ) + , Differentiable Point ( ℝ ( Length pointFields ) ) + , Differentiable Interval ( ℝ ( Length pointFields ) ) ) => PointFields pointFields where { } instance ( KnownSymbols pointFields, Typeable pointFields , Serialisable ( Record pointFields ) , Show ( Record pointFields ) , NFData ( Record pointFields ) - , Interpolatable ( Record pointFields ) - , Representable Double ( ℝ ( Length pointFields ) ) + , Differentiable Point ( ℝ ( Length pointFields ) ) + , Differentiable Interval ( ℝ ( Length pointFields ) ) ) => PointFields pointFields where { } diff --git a/src/metabrushes/MetaBrush/Document/SubdivideStroke.hs b/src/metabrushes/MetaBrush/Document/SubdivideStroke.hs index fdf5016..8975d00 100644 --- a/src/metabrushes/MetaBrush/Document/SubdivideStroke.hs +++ b/src/metabrushes/MetaBrush/Document/SubdivideStroke.hs @@ -85,7 +85,7 @@ subdivide c doc@( Document { zoomFactor } ) = where updateSpline :: forall clo brushParams - . ( KnownSplineType clo, Interpolatable brushParams ) + . ( KnownSplineType clo, Interpolatable Double brushParams ) => StrokeSpline clo brushParams -> State ( Maybe Text ) ( StrokeSpline clo brushParams ) updateSpline spline@( Spline { splineStart } ) | not strokeVisible diff --git a/src/metabrushes/MetaBrush/Records.hs b/src/metabrushes/MetaBrush/Records.hs index 1708c9e..f5cadcc 100644 --- a/src/metabrushes/MetaBrush/Records.hs +++ b/src/metabrushes/MetaBrush/Records.hs @@ -41,6 +41,12 @@ import Control.DeepSeq import Data.Group ( Group(..) ) +-- rounded-hw +import Numeric.Rounded.Hardware + ( Rounded(..) ) +import Numeric.Rounded.Hardware.Interval.NonEmpty + ( Interval(..) ) + -- text import Data.Text ( Text ) @@ -50,18 +56,9 @@ import qualified Data.Text as Text -- MetaBrush import Math.Linear import Math.Linear.Dual + ( type (~>)(..), D, Diffy, Differentiable ) import Math.Module - --------------------------------------------------------------------------------- - --- | A function from a given record type, with provided default values --- that can be overridden. -type WithParams :: [ Symbol ] -> Type -> Type -data WithParams params a = - WithParams - { defaultParams :: Record params - , withParams :: Record params ~> a - } + ( Module ) -------------------------------------------------------------------------------- @@ -107,6 +104,19 @@ deriving via ( T ( ℝ ( Length ks ) ) ) instance Module Double ( T ( ℝ ( Length ks ) ) ) => Module Double ( T ( Record ks ) ) +deriving via ( T ( 𝕀ℝ ( Length ks ) ) ) + instance Semigroup ( T ( 𝕀ℝ ( Length ks ) ) ) + => Semigroup ( T ( 𝕀 ( Record ks ) ) ) +deriving via ( T ( 𝕀ℝ ( Length ks ) ) ) + instance Monoid ( T ( 𝕀ℝ ( Length ks ) ) ) + => Monoid ( T ( 𝕀 ( Record ks ) ) ) +deriving via ( T ( 𝕀ℝ ( Length ks ) ) ) + instance Group ( T ( 𝕀ℝ ( Length ks ) ) ) + => Group ( T ( 𝕀 ( Record ks ) ) ) +deriving via ( T ( 𝕀ℝ ( Length ks ) ) ) + instance Module ( 𝕀 Double ) ( T ( 𝕀ℝ ( Length ks ) ) ) + => Module ( 𝕀 Double ) ( T ( 𝕀 ( Record ks ) ) ) + instance ( Act ( T ( ℝ ( Length ks ) ) ) ( ℝ ( Length ks ) ) , Semigroup ( T ( ℝ ( Length ks ) ) ) ) => Act ( T ( Record ks ) ) ( Record ks ) where @@ -116,12 +126,34 @@ instance ( Torsor ( T ( ℝ ( Length ks ) ) ) ( ℝ ( Length ks ) ) => Torsor ( T ( Record ks ) ) ( Record ks ) where MkR g --> MkR a = T $ MkR $ unT $ g --> a +instance ( Act ( T ( 𝕀ℝ ( Length ks ) ) ) ( 𝕀ℝ ( Length ks ) ) + , Semigroup ( T ( 𝕀ℝ ( Length ks ) ) ) ) + => Act ( T ( 𝕀 ( Record ks ) ) ) ( 𝕀 ( Record ks ) ) where + T ( I ( Rounded ( MkR g_lo ) ) ( Rounded ( MkR g_hi ) ) ) + • I ( Rounded ( MkR a_lo ) ) ( Rounded ( MkR a_hi ) ) + = case T ( I ( Rounded g_lo ) ( Rounded g_hi ) ) • I ( Rounded a_lo ) ( Rounded a_hi ) of + I ( Rounded b_lo ) ( Rounded b_hi ) -> + I ( Rounded ( MkR b_lo ) ) ( Rounded ( MkR b_hi ) ) +instance ( Torsor ( T ( 𝕀ℝ ( Length ks ) ) ) ( 𝕀ℝ ( Length ks ) ) + , Group ( T ( 𝕀ℝ ( Length ks ) ) ) ) + => Torsor ( T ( 𝕀 ( Record ks ) ) ) ( 𝕀 ( Record ks ) ) where + I ( Rounded ( MkR a_lo ) ) ( Rounded ( MkR a_hi ) ) + --> I ( Rounded ( MkR b_lo ) ) ( Rounded ( MkR b_hi ) ) + = case I ( Rounded a_lo ) ( Rounded a_hi ) --> I ( Rounded b_lo ) ( Rounded b_hi ) of + T ( I ( Rounded c_lo ) ( Rounded c_hi ) ) -> + T ( I ( Rounded ( MkR c_lo ) ) ( Rounded ( MkR c_hi ) ) ) + deriving newtype instance Representable r ( ℝ ( Length ks ) ) => Representable r ( Record ks ) type instance D ( Record ks ) = D ( ℝ ( Length ks ) ) -deriving newtype instance Diffy Double ( ℝ ( Length ks ) ) => Diffy Double ( Record ks ) +deriving newtype instance Diffy Double ( ℝ ( Length ks ) ) + => Diffy Double ( Record ks ) + +deriving via 𝕀ℝ ( Length ks ) + instance Diffy ( 𝕀 Double ) ( 𝕀ℝ ( Length ks ) ) + => Diffy ( 𝕀 Double ) ( 𝕀 ( Record ks ) ) -------------------------------------------------------------------------------- @@ -148,23 +180,24 @@ intersect :: forall r1 r2 l1 l2 . ( Typeable r1, Typeable r2 , KnownSymbols r1, KnownSymbols r2 , l1 ~ Length r1, l2 ~ Length r2 - , Representable Double ( ℝ l1 ), Representable Double ( ℝ l2 ) - , Interpolatable ( Record r1 ), Diffy Double ( ℝ l2 ) + , Representable Double ( ℝ l1 ) + , Differentiable 'Point ( ℝ l2 ) + , Differentiable 'Interval ( ℝ l2 ) ) => Intersection r1 r2 intersect -- Shortcut when the two rows are equal. | Just Refl <- eqT @r1 @r2 , Refl <- ( unsafeCoerce Refl :: r1 :~: Intersect r1 r2 ) - = Intersection { project = id, inject = \ _ -> linear id } + = Intersection { project = id, inject = \ _ -> id } | otherwise = doIntersection @r1 @r2 \ ( _ :: Proxy# r1r2 ) r1_idxs r2_idxs -> let project :: Record r1 -> Record r1r2 project = \ ( MkR r1 ) -> MkR $ projection ( (!) r1_idxs ) r1 - inject :: Record r2 -> Record r1r2 ~> Record r2 - inject = \ ( MkR r2 ) -> linear \ ( MkR r1r2 ) -> MkR $ injection ( find eqFin r2_idxs ) r1r2 r2 + inject :: Record r2 -> Record r1r2 -> Record r2 + inject = \ ( MkR r2 ) -> \ ( MkR r1r2 ) -> MkR $ injection ( find eqFin r2_idxs ) r1r2 r2 in Intersection { project, inject } data Intersection r1 r2 where @@ -172,12 +205,15 @@ data Intersection r1 r2 where :: forall r1r2 r1 r2 l12 . ( l12 ~ Length r1r2 , KnownSymbols r1r2 - , Representable Double ( ℝ l12 ) - , Diffy Double ( ℝ l12 ) - , Interpolatable ( Record r1r2 ) ) + , Differentiable 'Point ( ℝ l12 ) + , Differentiable 'Interval ( ℝ l12 ) + ) => { project :: Record r1 -> Record r1r2 - , inject :: Record r2 -> Record r1r2 ~> Record r2 + -- ^ project out fields present in both rows + -- (linear non-decreasing mapping) + , inject :: Record r2 -> Record r1r2 -> Record r2 -- ^ overrides the components of the first record with the second + -- (linear non-decreasing mapping in its second argument) } -> Intersection r1 r2 {-# INLINE doIntersection #-} @@ -185,13 +221,13 @@ doIntersection :: forall r1 r2 l1 l2 kont . ( KnownSymbols r1, KnownSymbols r2 , l1 ~ Length r1, l2 ~ Length r2 - , Representable Double ( ℝ l1 ), Representable Double ( ℝ l2 ) + , Representable Double ( ℝ l1 ) + , Representable Double ( ℝ l2 ) ) => ( forall r1r2 l12. - ( r1r2 ~ Intersect r1 r2, l12 ~ Length r1r2 - , Representable Double ( ℝ l12 ), Diffy Double ( ℝ l12 ) - , Interpolatable ( ℝ l12 ) - , KnownSymbols r1r2, Representable Double ( ℝ ( Length r1r2 ) ) + ( r1r2 ~ Intersect r1 r2, KnownSymbols r1r2, l12 ~ Length r1r2 + , Differentiable 'Point ( ℝ l12 ) + , Differentiable 'Interval ( ℝ l12 ) ) => Proxy# r1r2 -> Vec l12 ( Fin l1 ) -> Vec l12 ( Fin l2 ) -> kont ) -> kont diff --git a/src/splines/Math/Bezier/Stroke.hs b/src/splines/Math/Bezier/Stroke.hs index c4f75c2..f8c0277 100644 --- a/src/splines/Math/Bezier/Stroke.hs +++ b/src/splines/Math/Bezier/Stroke.hs @@ -1,4 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} @@ -36,14 +37,14 @@ import Data.Foldable ( for_ ) import Data.Functor.Identity ( Identity(..) ) -import Data.Kind - ( Type ) import Data.List.NonEmpty ( unzip ) import Data.Maybe ( fromMaybe, isJust, listToMaybe, mapMaybe ) import GHC.Exts - ( newMutVar#, runRW# ) + ( newMutVar#, runRW# + , Proxy#, proxy# + ) import GHC.STRef ( STRef(..), readSTRef, writeSTRef ) import GHC.Generics @@ -182,18 +183,30 @@ type OutlineFn = ℝ 1 -> ( ( ℝ 2, T ( ℝ 2 ) ), ( ℝ 2, T ( ℝ 2 ) ) ) computeStrokeOutline :: forall ( clo :: SplineType ) usedParams brushParams crvData ptData s . ( KnownSplineType clo - , Interpolatable usedParams - , Diffy Double usedParams, Diffy Double brushParams , HasType ( ℝ 2 ) ptData , HasType ( CachedStroke s ) crvData , NFData ptData, NFData crvData + + -- Differentiability. + , Differentiable 'Point brushParams + , Differentiable 'Interval brushParams + , Interpolatable Double usedParams + , Interpolatable ( 𝕀 Double ) ( 𝕀 usedParams ) + , Diffy Double usedParams + , Diffy ( 𝕀 Double ) ( 𝕀 usedParams ) + -- Debugging. , Show ptData, Show brushParams + ) => FitParameters -> ( ptData -> usedParams ) - -> ( usedParams ~> brushParams ) - -> ( brushParams ~> SplinePts Closed ) + -> ( usedParams -> brushParams ) -- ^ assumed to be linear and non-decreasing + -> ( forall i. Differentiable i brushParams + => Proxy# i + -> ( forall a. a -> I i a ) + -> I i brushParams ~> Spline Closed () ( I i ( ℝ 2 ) ) + ) -> Spline clo crvData ptData -> ST s ( Either ( SplinePts Closed ) ( SplinePts Closed, SplinePts Closed ) @@ -303,7 +316,7 @@ computeStrokeOutline fitParams ptParams toBrushParams brushFn spline@( Spline { outlineFunction ptParams toBrushParams brushFn p0 crv :<| go ( openCurveEnd crv ) crvs brushShape :: ptData -> SplinePts Closed - brushShape pt = fun brushFn $ fun toBrushParams $ ptParams pt + brushShape pt = fun ( brushFn @Point proxy# id ) $ toBrushParams $ ptParams pt updateSpline :: ( T ( ℝ 2 ), T ( ℝ 2 ), T ( ℝ 2 ) ) -> ST s OutlineData updateSpline ( lastTgt, lastTgtFwd, lastTgtBwd ) @@ -415,36 +428,71 @@ computeStrokeOutline fitParams ptParams toBrushParams brushFn spline@( Spline { -- | Computes the forward and backward stroke outline functions for a single curve. outlineFunction :: forall usedParams brushParams crvData ptData - . ( Interpolatable usedParams - , Diffy Double usedParams, Diffy Double brushParams - , HasType ( ℝ 2 ) ptData + . ( HasType ( ℝ 2 ) ptData + + -- Differentiability. + , Differentiable 'Point brushParams + , Differentiable 'Interval brushParams + , Interpolatable Double usedParams + , Interpolatable ( 𝕀 Double ) ( 𝕀 usedParams ) + , Diffy Double usedParams + , Diffy ( 𝕀 Double ) ( 𝕀 usedParams ) + -- Debugging. , Show ptData, Show brushParams ) => ( ptData -> usedParams ) - -> ( usedParams ~> brushParams ) - -> ( brushParams ~> SplinePts Closed ) + -> ( usedParams -> brushParams ) -- ^ assumed to be linear and non-decreasing + -> ( forall i. Differentiable i brushParams + => Proxy# i + -> ( forall a. a -> I i a ) + -> I i brushParams ~> Spline Closed () ( I i ( ℝ 2 ) ) + ) -> ptData -> Curve Open crvData ptData -> OutlineFn outlineFunction ptParams toBrushParams brushFromParams sp0 crv = let - usedParams :: ℝ 1 ~> usedParams - path :: ℝ 1 ~> ℝ 2 - ( path, usedParams ) = + pathAndUsedParams :: forall i + . ( D ( I i ( ℝ 1 ) ) ~ D ( ℝ 1 ) + , Coercible ( I i ( ℝ 1 ) ) ( I i Double ) + , Module ( I i Double ) ( T ( I i ( ℝ 2 ) ) ) + , Torsor ( T ( I i ( ℝ 2 ) ) ) ( I i ( ℝ 2 ) ) + , Module ( I i Double ) ( T ( I i usedParams ) ) + , Torsor ( T ( I i usedParams ) ) ( I i usedParams ) + ) + => ( forall a. a -> I i a ) + -> ( I i ( ℝ 1 ) ~> I i ( ℝ 2 ), I i ( ℝ 1 ) ~> I i usedParams ) + pathAndUsedParams toI = case crv of LineTo { curveEnd = NextPoint sp1 } | let seg = Segment sp0 sp1 - -> ( line @Point ( fmap coords seg ) - , line @Point ( fmap ptParams seg ) ) + -> ( line @i ( fmap ( toI . coords ) seg ) + , line @i ( fmap ( toI . ptParams ) seg ) ) Bezier2To { controlPoint = sp1, curveEnd = NextPoint sp2 } | let bez2 = Quadratic.Bezier sp0 sp1 sp2 - -> ( bezier2 @Point ( fmap coords bez2 ) - , bezier2 @Point ( fmap ptParams bez2 ) ) + -> ( bezier2 @i ( fmap ( toI . coords ) bez2 ) + , bezier2 @i ( fmap ( toI . ptParams ) bez2 ) ) Bezier3To { controlPoint1 = sp1, controlPoint2 = sp2, curveEnd = NextPoint sp3 } | let bez3 = Cubic.Bezier sp0 sp1 sp2 sp3 - -> ( bezier3 @Point ( fmap coords bez3 ) - , bezier3 @Point ( fmap ptParams bez3 ) ) + -> ( bezier3 @i ( fmap ( toI . coords ) bez3 ) + , bezier3 @i ( fmap ( toI . ptParams ) bez3 ) ) + + usedParams :: ℝ 1 ~> usedParams + path :: ℝ 1 ~> ℝ 2 + ( path, usedParams ) = pathAndUsedParams @Point id + +{- + curvesI :: 𝕀ℝ 1 -> Seq ( 𝕀ℝ 1 -> StrokeDatum 'Interval ) + curvesI = brushStrokeData @'Interval @brushParams + pathI + ( usedParamsI `chainRule` linear ( nonDecreasing toBrushParams ) ) + ( brushFromParams @'Interval proxy# singleton ) + + usedParamsI :: 𝕀ℝ 1 ~> 𝕀 usedParams + pathI :: 𝕀ℝ 1 ~> 𝕀ℝ 2 + ( pathI, usedParamsI ) = pathAndUsedParams @'Interval singleton +-} fwdBwd :: OutlineFn fwdBwd t @@ -456,8 +504,8 @@ outlineFunction ptParams toBrushParams brushFromParams sp0 crv = curves :: Seq ( ℝ 1 -> StrokeDatum Point ) curves = brushStrokeData @Point @brushParams path - ( usedParams `chainRule` toBrushParams ) - brushFromParams + ( usedParams `chainRule` linear toBrushParams ) + ( brushFromParams @Point proxy# id ) t fwdOffset = withTangent path'_t brush_t @@ -466,8 +514,8 @@ outlineFunction ptParams toBrushParams brushFromParams sp0 crv = D1 path_t path'_t _ = runD path t D1 params_t _ _ = runD usedParams t brush_t = value @Double @brushParams - $ runD brushFromParams - $ fun toBrushParams params_t + $ runD ( brushFromParams @Point proxy# id ) + $ toBrushParams params_t in fwdBwd @@ -1075,14 +1123,5 @@ data StrokeDatum i } -deriving stock instance Show ( StrokeDatum Point ) -deriving stock instance Show ( StrokeDatum Interval ) - - --- Handling points and intervals uniformly. -data Extent = Point | Interval - -type I :: Extent -> Type -> Type -type family I i a where - I Point a = a - I Interval a = 𝕀 a +deriving stock instance Show ( StrokeDatum 'Point ) +deriving stock instance Show ( StrokeDatum 'Interval ) diff --git a/src/splines/Math/Linear.hs b/src/splines/Math/Linear.hs index 77848c0..c0de5f8 100644 --- a/src/splines/Math/Linear.hs +++ b/src/splines/Math/Linear.hs @@ -1,7 +1,8 @@ -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE UnliftedNewtypes #-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE UnliftedNewtypes #-} module Math.Linear ( -- * Points and vectors @@ -10,14 +11,18 @@ module Math.Linear -- * Points and vectors (second version) , ℝ(..), T(.., V2, V3) , Fin(..), eqFin, MFin(..) - , Dim, Representable(..), injection, projection + , Dim, Representable(..), ApRep(..) + , injection, projection , Vec(..), (!), find -- * Intervals - , 𝕀, 𝕀ℝ + , 𝕀, 𝕀ℝ, singleton, nonDecreasing + , Extent(..), I ) where -- base +import Data.Coerce + ( Coercible, coerce ) import Data.Kind ( Type, Constraint ) import Data.Monoid @@ -48,8 +53,12 @@ import Data.Group.Generics ( ) -- rounded-hw +import Numeric.Rounded.Hardware + ( Rounded(..) ) import Numeric.Rounded.Hardware.Interval.NonEmpty - ( Interval ) + ( Interval(..) ) +import qualified Numeric.Rounded.Hardware.Interval.NonEmpty as Interval + ( sup, inf ) -------------------------------------------------------------------------------- @@ -90,11 +99,36 @@ data instance ℝ 3 = ℝ3 {-# UNPACK #-} !Double {-# UNPACK #-} !Double {-# deriving anyclass NFData deriving stock ( Show, Eq, Ord ) +deriving via ApRep ( Sum Double ) ( ℝ n ) + instance Representable Double ( ℝ n ) => Semigroup ( T ( ℝ n ) ) +deriving via ApRep ( Sum Double ) ( ℝ n ) + instance Representable Double ( ℝ n ) => Monoid ( T ( ℝ n ) ) +deriving via ApRep ( Sum Double ) ( ℝ n ) + instance Representable Double ( ℝ n ) => Group ( T ( ℝ n ) ) +deriving via ApRep Double ( ℝ n ) + instance Representable Double ( ℝ n ) => Act ( T ( ℝ n ) ) ( ℝ n ) +deriving via ApRep Double ( ℝ n ) + instance Representable Double ( ℝ n ) => Torsor ( T ( ℝ n ) ) ( ℝ n ) + -- | Tangent space to Euclidean space. type T :: Type -> Type newtype T e = T { unT :: e } deriving stock ( Eq, Functor, Foldable, Traversable ) - deriving newtype NFData -- newtype Show instance for debugging... + deriving newtype NFData + +instance Semigroup ( T Double ) where + (<>) = coerce ( (+) @Double ) +instance Monoid ( T Double ) where + mempty = T 0 + +instance Group ( T Double ) where + invert ( T x ) = T ( negate x ) + +instance Act ( T Double ) Double where + T u • v = u + v +instance Torsor ( T Double ) Double where + a --> b = T ( b - a ) + instance {-# OVERLAPPING #-} Show ( ℝ n ) => Show ( T ( ℝ n ) ) where show ( T p ) = "V" ++ drop 1 ( show p ) @@ -104,14 +138,6 @@ instance Applicative T where pure = T T f <*> T a = T ( f a ) -instance Semigroup ( T ( ℝ 0 ) ) where { _ <> _ = T ℝ0 } -instance Monoid ( T ( ℝ 0 ) ) where { mempty = T ℝ0 } -instance Group ( T ( ℝ 0 ) ) where { invert _ = T ℝ0 } - -deriving via Sum Double instance Semigroup ( T ( ℝ 1 ) ) -deriving via Sum Double instance Monoid ( T ( ℝ 1 ) ) -deriving via Sum Double instance Group ( T ( ℝ 1 ) ) - {-# COMPLETE V2 #-} pattern V2 :: Double -> Double -> T ( ℝ 2 ) pattern V2 x y = T ( ℝ2 x y ) @@ -122,42 +148,6 @@ pattern V2 x y = T ( ℝ2 x y ) pattern V3 :: Double -> Double -> Double -> T ( ℝ 3 ) pattern V3 x y z = T ( ℝ3 x y z ) -instance Semigroup ( T ( ℝ 2 ) ) where - T ( ℝ2 x1 y1 ) <> T ( ℝ2 x2 y2 ) = - T ( ℝ2 ( x1 + x2 ) ( y1 + y2 ) ) -instance Monoid ( T ( ℝ 2 ) ) where - mempty = T ( ℝ2 0 0 ) -instance Group ( T ( ℝ 2 ) ) where - invert ( T ( ℝ2 x y ) ) = T ( ℝ2 ( negate x ) ( negate y ) ) - -instance Semigroup ( T ( ℝ 3 ) ) where - T ( ℝ3 x1 y1 z1 ) <> T ( ℝ3 x2 y2 z2 ) = - T ( ℝ3 ( x1 + x2 ) ( y1 + y2 ) ( z1 + z2 ) ) -instance Monoid ( T ( ℝ 3 ) ) where - mempty = T ( ℝ3 0 0 0 ) -instance Group ( T ( ℝ 3 ) ) where - invert ( T ( ℝ3 x y z ) ) = T ( ℝ3 ( negate x ) ( negate y ) ( negate z ) ) - -instance Act ( T ( ℝ 0 ) ) ( ℝ 0 ) where - _ • _ = ℝ0 -instance Torsor ( T ( ℝ 0 ) ) ( ℝ 0 ) where - _ --> _ = T ℝ0 - -instance Act ( T ( ℝ 1 ) ) ( ℝ 1 ) where - T ( ℝ1 t ) • ℝ1 a = ℝ1 ( a + t ) -instance Torsor ( T ( ℝ 1 ) ) ( ℝ 1 ) where - ℝ1 a --> ℝ1 b = T ( ℝ1 ( b - a ) ) - -instance Act ( T ( ℝ 2 ) ) ( ℝ 2 ) where - T ( ℝ2 u v ) • ℝ2 x y = ℝ2 ( x + u ) ( y + v ) -instance Torsor ( T ( ℝ 2 ) ) ( ℝ 2 ) where - ℝ2 a1 b1 --> ℝ2 a2 b2 = T ( ℝ2 ( a2 - a1 ) ( b2 - b1 ) ) - -instance Act ( T ( ℝ 3 ) ) ( ℝ 3 ) where - T ( ℝ3 u v w ) • ℝ3 x y z = ℝ3 ( x + u ) ( y + v ) ( z + w) -instance Torsor ( T ( ℝ 3 ) ) ( ℝ 3 ) where - ℝ3 a1 b1 c1 --> ℝ3 a2 b2 c2 = T ( ℝ3 ( a2 - a1 ) ( b2 - b1 ) ( c2 - c1 ) ) - -------------------------------------------------------------------------------- -- | 1, ..., n @@ -250,8 +240,88 @@ find eq v b = MFin ( go 1## v ) = go ( j `plusWord#` 1## ) as go _ VZ = 0## +-------------------------------------------------------------------------------- +-- Instances in terms of representable. + +-- | A newtype to hang off instances for representable functors. +newtype ApRep r u = ApRep { unApRep :: u } + +instance ( Representable r u, Coercible r m, Semigroup m ) => Semigroup ( ApRep m u ) where + ApRep a <> ApRep b = ApRep $ tabulate @r @u \ i -> + coerce $ (<>) @m ( coerce ( index @r @u a i ) ) ( coerce ( index @r @u b i ) ) + {-# INLINE (<>) #-} +instance ( Representable r u, Coercible r m, Monoid m ) => Monoid ( ApRep m u ) where + mempty = ApRep $ tabulate @r @u \ _ -> coerce $ mempty @m + {-# INLINE mempty #-} +instance ( Representable r u, Coercible r m, Group m ) => Group ( ApRep m u ) where + invert ( ApRep a ) = ApRep $ tabulate @r @u \ i -> + coerce $ invert @m $ coerce ( index @r @u a i ) + {-# INLINE invert #-} +instance ( Act ( T r ) r , Semigroup ( T u ), Representable r u ) => Act ( T u ) ( ApRep r u ) where + T g • ApRep a = ApRep $ tabulate @r @u \ i -> + coerce $ (•) @(T r) @r ( coerce $ index @r @u g i ) ( coerce ( index @r @u a i ) ) + {-# INLINE (•) #-} +instance ( Torsor ( T r ) r , Group ( T u ), Representable r u ) => Torsor ( T u ) ( ApRep r u ) where + ApRep a --> ApRep b = T $ tabulate @r @u \ i -> + coerce $ (-->) @(T r) @r ( coerce $ index @r @u a i ) ( coerce ( index @r @u b i ) ) + {-# INLINE (-->) #-} + -------------------------------------------------------------------------------- -- Intervals. type 𝕀 = Interval type 𝕀ℝ n = 𝕀 ( ℝ n ) + +-- Handling points and intervals uniformly. +data Extent = Point | Interval + +type I :: Extent -> Type -> Type +type family I i a where + I 'Point a = a + I 'Interval a = 𝕀 a + +singleton :: a -> 𝕀 a +singleton a = I ( Rounded a ) ( Rounded a ) + +-- | Turn a non-decreasing function into a function on intervals. +nonDecreasing :: ( a -> b ) -> 𝕀 a -> 𝕀 b +nonDecreasing f ( I ( Rounded lo ) ( Rounded hi ) ) = + I ( Rounded $ f lo ) ( Rounded $ f hi ) + +type instance Dim ( 𝕀 u ) = Dim u + +instance Representable r u => Representable ( 𝕀 r ) ( 𝕀 u ) where + tabulate f = + let !lo = tabulate @r @u ( \ i -> getRounded $ Interval.inf ( f i ) ) + !hi = tabulate @r @u ( \ i -> getRounded $ Interval.sup ( f i ) ) + in I ( Rounded lo ) ( Rounded hi ) + {-# INLINE tabulate #-} + index ( I ( Rounded lo ) ( Rounded hi ) ) i = + let !lo_i = index @r @u lo i + !hi_i = index @r @u hi i + in I ( Rounded lo_i ) ( Rounded hi_i ) + {-# INLINE index #-} + +deriving via ApRep ( Sum ( 𝕀 Double ) ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Semigroup ( T ( 𝕀ℝ n ) ) +deriving via ApRep ( Sum ( 𝕀 Double ) ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Monoid ( T ( 𝕀ℝ n ) ) +deriving via ApRep ( Sum ( 𝕀 Double ) ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Group ( T ( 𝕀ℝ n ) ) + +deriving via Sum ( 𝕀 Double ) + instance Semigroup ( T ( 𝕀 Double ) ) +deriving via Sum ( 𝕀 Double ) + instance Monoid ( T ( 𝕀 Double ) ) +deriving via Sum ( 𝕀 Double ) + instance Group ( T ( 𝕀 Double ) ) + +instance Act ( T ( 𝕀 Double ) ) ( 𝕀 Double ) where + T g • a = coerce ( Sum g • a ) +instance Torsor ( T ( 𝕀 Double ) ) ( 𝕀 Double ) where + a --> b = T $ getSum ( a --> b ) + +deriving via ApRep ( 𝕀 Double ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Act ( T ( 𝕀ℝ n ) ) ( 𝕀ℝ n ) +deriving via ApRep ( 𝕀 Double ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Torsor ( T ( 𝕀ℝ n ) ) ( 𝕀ℝ n ) diff --git a/src/splines/Math/Linear/Dual.hs b/src/splines/Math/Linear/Dual.hs index fdf8ab7..18d5c9b 100644 --- a/src/splines/Math/Linear/Dual.hs +++ b/src/splines/Math/Linear/Dual.hs @@ -16,8 +16,19 @@ import Data.Kind import GHC.Generics ( Generic, Generic1(..), Generically1(..) ) +-- acts +import Data.Act + ( Torsor ) + +-- rounded-hw +import Numeric.Rounded.Hardware + ( Rounded(..) ) +import Numeric.Rounded.Hardware.Interval.NonEmpty + ( Interval(..) ) + -- MetaBrush import Math.Module + ( Module(..) ) import Math.Linear -------------------------------------------------------------------------------- @@ -66,7 +77,7 @@ data Dℝ3 v = D3 { v :: !v, dx, dy, dz :: !( T v ), ddx, dxdy, ddy, dxdz, dydz, via Generically1 Dℝ3 deriving stock instance ( Show v, Show ( T v ) ) => Show ( Dℝ3 v ) -instance Num ( Dℝ1 Double ) where +instance ( Module s ( T r ), Num r ) => Num ( Dℝ1 r ) where (+) = liftA2 (+) (-) = liftA2 (-) negate = fmap negate @@ -81,7 +92,7 @@ instance Num ( Dℝ1 Double ) where ( T $ dx1 * v2 + v1 * dx2 ) ( T $ dx1 * dx2 + v1 * ddx2 + ddx1 * v2 ) -instance Num ( Dℝ2 Double ) where +instance ( Num r, Module s ( T r ) ) => Num ( Dℝ2 r ) where (+) = liftA2 (+) (-) = liftA2 (-) negate = fmap negate @@ -100,7 +111,7 @@ instance Num ( Dℝ2 Double ) where ( T $ dy1 * dy2 + v1 * ddy2 + ddy1 * v2 ) -instance Num ( Dℝ3 Double ) where +instance ( Module s ( T r ) , Num r ) => Num ( Dℝ3 r ) where (+) = liftA2 (+) (-) = liftA2 (-) negate = fmap negate @@ -123,34 +134,34 @@ instance Num ( Dℝ3 Double ) where ( T $ dz1 * dz2 + v1 * ddz2 + ddz1 * v2) -instance Module Double ( T v ) => Module ( Dℝ0 Double ) ( Dℝ0 v ) where - (^+^) = liftA2 ( coerce $ (^+^) @Double @( T v ) ) - (^-^) = liftA2 ( coerce $ (^-^) @Double @( T v ) ) - origin = pure ( coerce $ origin @Double @( T v ) ) - (*^) = liftA2 ( coerce $ (*^) @Double @( T v ) ) +instance ( Num ( Dℝ0 r ), Module r ( T v ) ) => Module ( Dℝ0 r ) ( Dℝ0 v ) where + (^+^) = liftA2 ( coerce $ (^+^) @r @( T v ) ) + (^-^) = liftA2 ( coerce $ (^-^) @r @( T v ) ) + origin = pure ( coerce $ origin @r @( T v ) ) + (*^) = liftA2 ( coerce $ (*^) @r @( T v ) ) -instance Module Double ( T v ) => Module ( Dℝ1 Double ) ( Dℝ1 v ) where - (^+^) = liftA2 ( coerce $ (^+^) @Double @( T v ) ) - (^-^) = liftA2 ( coerce $ (^-^) @Double @( T v ) ) - origin = pure ( coerce $ origin @Double @( T v ) ) - (*^) = liftA2 ( coerce $ (*^) @Double @( T v ) ) +instance ( Num ( Dℝ1 r ), Module r ( T v ) ) => Module ( Dℝ1 r ) ( Dℝ1 v ) where + (^+^) = liftA2 ( coerce $ (^+^) @r @( T v ) ) + (^-^) = liftA2 ( coerce $ (^-^) @r @( T v ) ) + origin = pure ( coerce $ origin @r @( T v ) ) + (*^) = liftA2 ( coerce $ (*^) @r @( T v ) ) -instance Module Double ( T v ) => Module ( Dℝ2 Double ) ( Dℝ2 v ) where - (^+^) = liftA2 ( coerce $ (^+^) @Double @( T v ) ) - (^-^) = liftA2 ( coerce $ (^-^) @Double @( T v ) ) - origin = pure ( coerce $ origin @Double @( T v ) ) - (*^) = liftA2 ( coerce $ (*^) @Double @( T v ) ) +instance ( Num ( Dℝ2 r ), Module r ( T v ) ) => Module ( Dℝ2 r ) ( Dℝ2 v ) where + (^+^) = liftA2 ( coerce $ (^+^) @r @( T v ) ) + (^-^) = liftA2 ( coerce $ (^-^) @r @( T v ) ) + origin = pure ( coerce $ origin @r @( T v ) ) + (*^) = liftA2 ( coerce $ (*^) @r @( T v ) ) -instance Module Double ( T v ) => Module ( Dℝ3 Double ) ( Dℝ3 v ) where - (^+^) = liftA2 ( coerce $ (^+^) @Double @( T v ) ) - (^-^) = liftA2 ( coerce $ (^-^) @Double @( T v ) ) - origin = pure ( coerce $ origin @Double @( T v ) ) - (*^) = liftA2 ( coerce $ (*^) @Double @( T v ) ) +instance ( Num ( Dℝ3 r ), Module r ( T v ) ) => Module ( Dℝ3 r ) ( Dℝ3 v ) where + (^+^) = liftA2 ( coerce $ (^+^) @r @( T v ) ) + (^-^) = liftA2 ( coerce $ (^-^) @r @( T v ) ) + origin = pure ( coerce $ origin @r @( T v ) ) + (*^) = liftA2 ( coerce $ (*^) @r @( T v ) ) -instance Fractional ( Dℝ1 Double ) where +instance ( Module s ( T r ), Fractional r ) => Fractional ( Dℝ1 r ) where (/) = error "I haven't yet defined (/) for Dℝ1" fromRational = konst @Double @( ℝ 1 ) . fromRational -instance Floating ( Dℝ1 Double ) where +instance ( Module s ( T r ), Floating r ) => Floating ( Dℝ1 r ) where pi = konst @Double @( ℝ 1 ) pi sin ( D1 v ( T dx ) ( T ddx ) ) = let !s = sin v @@ -162,10 +173,10 @@ instance Floating ( Dℝ1 Double ) where !c = cos v in D1 c ( T $ -s * dx ) ( T $ -2 * s * ddx - c * dx * dx ) -instance Fractional ( Dℝ2 Double ) where +instance ( Module s ( T r ), Fractional r ) => Fractional ( Dℝ2 r ) where (/) = error "I haven't yet defined (/) for Dℝ2" fromRational = konst @Double @( ℝ 2 ) . fromRational -instance Floating ( Dℝ2 Double ) where +instance ( Module s ( T r ), Floating r ) => Floating ( Dℝ2 r ) where pi = konst @Double @( ℝ 2 ) pi sin ( D2 v ( T dx ) ( T dy ) ( T ddx ) ( T dxdy ) ( T ddy ) ) = let !s = sin v @@ -185,10 +196,10 @@ instance Floating ( Dℝ2 Double ) where ( T $ -2 * s * dxdy - 2 * c * dx * dy ) ( T $ -2 * s * ddy - c * dy * dy ) -instance Fractional ( Dℝ3 Double ) where +instance ( Module s ( T r ), Fractional r ) => Fractional ( Dℝ3 r ) where (/) = error "I haven't yet defined (/) for Dℝ3" fromRational = konst @Double @( ℝ 3 ) . fromRational -instance Floating ( Dℝ3 Double ) where +instance ( Module s ( T r ), Floating r ) => Floating ( Dℝ3 r ) where pi = konst @Double @( ℝ 3 ) pi sin ( D3 v ( T dx ) ( T dy ) ( T dz ) ( T ddx ) ( T dxdy ) ( T ddy ) ( T dxdz ) ( T dydz ) ( T ddz ) ) = let !s = sin v @@ -229,12 +240,12 @@ uncurryD ( D1 ( D b_t0 ) ( T ( D dbdt_t0 ) ) ( T ( D d2bdt2_t0 ) ) ) s0 = type Diffy :: Type -> Type -> Constraint class ( Traversable ( D v ), Module r ( T v ) ) => Diffy r v where chain :: ( Module r ( T w ) ) => D ( ℝ 1 ) v -> D v w -> D ( ℝ 1 ) w - konst :: Module r ( T w ) => w -> D v w + konst :: Module s ( T w ) => w -> D v w value :: D v w -> w - linear :: Module r ( T w ) => ( v -> w ) -> ( v ~> w ) + linear :: Module s ( T w ) => ( v -> w ) -> ( v ~> w ) -chainRule :: ( Diffy r v, Module r ( T w ) ) - => ( ( ℝ 1 ) ~> v ) -> ( v ~> w ) -> ( ( ℝ 1 ) ~> w ) +chainRule :: ( Diffy r v, Module r ( T w ), D u ~ D ( ℝ 1 ) ) + => ( u ~> v ) -> ( v ~> w ) -> ( u ~> w ) chainRule ( D df ) ( D dg ) = D \ x -> case df x of @@ -310,3 +321,110 @@ instance Diffy Double ( ℝ 3 ) where ( T $ f ( ℝ3 1 0 0 ) ) ( T $ f ( ℝ3 0 1 0 ) ) ( T $ f ( ℝ3 0 0 1 ) ) origin origin origin origin origin origin {-# INLINE linear #-} + +-------------------------------------------------------------------------------- + +-- TODO: avoid copying over code... + +instance Diffy ( 𝕀 Double ) ( 𝕀ℝ 0 ) where + chain _ ( D0 w ) = D1 w origin origin + {-# INLINE chain #-} + konst k = D0 k + {-# INLINE konst #-} + value ( D0 w ) = w + {-# INLINE value #-} + linear f = D \ _ -> D0 ( f $ singleton ℝ0 ) + {-# INLINE linear #-} + +instance Diffy ( 𝕀 Double ) ( 𝕀ℝ 1 ) where + chain ( D1 _ ( T ( I ( Rounded ( ℝ1 x'_lo ) ) ( Rounded ( ℝ1 x'_hi ) ) ) ) + ( T ( I ( Rounded ( ℝ1 x''_lo ) ) ( Rounded ( ℝ1 x''_hi ) ) ) ) ) + ( D1 v g_x g_xx ) + = let + !x' = I ( Rounded x'_lo ) ( Rounded x'_hi ) + !x'' = I ( Rounded x''_lo ) ( Rounded x''_hi ) + in D1 v + ( x' *^ g_x ) + ( x'' *^ g_x ^+^ ( x' * x' ) *^ g_xx ) + {-# INLINE chain #-} + konst k = D1 k origin origin + {-# INLINE konst #-} + value ( D1 { v } ) = v + {-# INLINE value #-} + linear f = D \ u -> D1 ( f u ) ( T $ f u ) origin + {-# INLINE linear #-} + +instance Diffy ( 𝕀 Double ) ( 𝕀ℝ 2 ) where + chain ( D1 _ ( T ( I ( Rounded ( ℝ2 x'_lo y'_lo ) ) ( Rounded ( ℝ2 x'_hi y'_hi ) ) ) ) + ( T ( I ( Rounded ( ℝ2 x''_lo y''_lo ) ) ( Rounded ( ℝ2 x''_hi y''_hi ) ) ) ) ) + ( D2 v g_x g_y g_xx g_xy g_yy ) + = let + !x' = I ( Rounded x'_lo ) ( Rounded x'_hi ) + !y' = I ( Rounded y'_lo ) ( Rounded y'_hi ) + !x'' = I ( Rounded x''_lo ) ( Rounded x''_hi ) + !y'' = I ( Rounded y''_lo ) ( Rounded y''_hi ) + in D1 v + ( x' *^ g_x ^+^ y' *^ g_y ) + ( x'' *^ g_x ^+^ y'' *^ g_y + ^+^ ( x' * x' ) *^ g_xx ^+^ ( y' * y' ) *^ g_yy + ^+^ 2 *^ ( ( x' * y' ) *^ g_xy ) ) + {-# INLINE chain #-} + konst k = D2 k origin origin origin origin origin + {-# INLINE konst #-} + value ( D2 { v } ) = v + {-# INLINE value #-} + linear f = D \ u -> + D2 ( f u ) + ( T $ f ( singleton $ ℝ2 1 0 ) ) ( T $ f ( singleton $ ℝ2 0 1 ) ) + origin origin origin + {-# INLINE linear #-} + +instance Diffy ( 𝕀 Double ) ( 𝕀ℝ 3 ) where + chain ( D1 _ ( T ( I ( Rounded ( ℝ3 x'_lo y'_lo z'_lo ) ) ( Rounded ( ℝ3 x'_hi y'_hi z'_hi ) ) ) ) + ( T ( I ( Rounded ( ℝ3 x''_lo y''_lo z''_lo ) ) ( Rounded ( ℝ3 x''_hi y''_hi z''_hi ) ) ) ) ) + ( D3 v g_x g_y g_z g_xx g_xy g_yy g_xz g_yz g_zz ) + = let + !x' = I ( Rounded x'_lo ) ( Rounded x'_hi ) + !y' = I ( Rounded y'_lo ) ( Rounded y'_hi ) + !z' = I ( Rounded z'_lo ) ( Rounded z'_hi ) + !x'' = I ( Rounded x''_lo ) ( Rounded x''_hi ) + !y'' = I ( Rounded y''_lo ) ( Rounded y''_hi ) + !z'' = I ( Rounded z''_lo ) ( Rounded z''_hi ) + in D1 v + ( x' *^ g_x ^+^ y' *^ g_y ^+^ z' *^ g_z ) + ( x'' *^ g_x ^+^ y'' *^ g_y ^+^ z'' *^ g_z + ^+^ ( x' * x' ) *^ g_xx ^+^ ( y' * y' ) *^ g_yy ^+^ ( z' * z' ) *^ g_zz + ^+^ 2 *^ ( ( x' * y' ) *^ g_xy ) ^+^ ( x' * z' ) *^ g_xz ^+^ ( y' * z' ) *^ g_yz ) + {-# INLINE chain #-} + konst k = D3 k origin origin origin origin origin origin origin origin origin + {-# INLINE konst #-} + value ( D3 { v } ) = v + {-# INLINE value #-} + linear f = D \ u -> + D3 ( f u ) + ( T $ f ( singleton $ ℝ3 1 0 0 ) ) ( T $ f ( singleton $ ℝ3 0 1 0 ) ) ( T $ f ( singleton $ ℝ3 0 0 1 ) ) + origin origin origin origin origin origin + {-# INLINE linear #-} + +-------------------------------------------------------------------------------- + +type Differentiable :: Extent -> Type -> Constraint +class + ( Diffy ( I i Double ) ( I i u ) + , Module ( I i Double ) ( T ( I i Double ) ) + , Torsor ( T ( I i u ) ) ( I i u ) + , Module ( D ( I i u ) ( I i Double ) ) ( D ( I i u ) ( I i ( ℝ 2 ) ) ) + , Representable ( I i Double ) ( I i u ) + , Floating ( D ( I i u ) ( I i Double ) ) + , Applicative ( D ( I i u ) ) + ) => Differentiable i u + +instance + ( Diffy ( I i Double ) ( I i u ) + , Module ( I i Double ) ( T ( I i Double ) ) + , Torsor ( T ( I i u ) ) ( I i u ) + , Module ( D ( I i u ) ( I i Double ) ) ( D ( I i u ) ( I i ( ℝ 2 ) ) ) + , Representable ( I i Double ) ( I i u ) + , Floating ( D ( I i u ) ( I i Double ) ) + , Applicative ( D ( I i u ) ) + ) => Differentiable i u diff --git a/src/splines/Math/Module.hs b/src/splines/Math/Module.hs index 61a1937..7570d34 100644 --- a/src/splines/Math/Module.hs +++ b/src/splines/Math/Module.hs @@ -9,7 +9,6 @@ module Math.Module , norm, squaredNorm, quadrance, distance , proj, projC, closestPointOnSegment , strictlyParallel, convexCombination - , 𝕀 ) where @@ -18,6 +17,8 @@ import Control.Applicative ( liftA2 ) import Control.Monad ( guard ) +import Data.Coerce + ( Coercible, coerce ) import Data.Kind ( Type, Constraint ) import Data.Monoid @@ -36,8 +37,6 @@ import Numeric.Rounded.Hardware ( Rounded(..) ) import Numeric.Rounded.Hardware.Interval.NonEmpty ( Interval(..) ) -import Numeric.Rounded.Hardware.Class - ( intervalAdd, intervalSub, intervalMul ) -- MetaBrush import Math.Epsilon @@ -124,47 +123,42 @@ closestPointOnSegment c ( Segment p0 p1 ) -------------------------------------------------------------------------------- -- | A convenient constraint synonym for types that support interpolation. -type Interpolatable :: Type -> Constraint -class ( Torsor ( T u ) u, Module Double ( T u ) ) => Interpolatable u -instance ( Torsor ( T u ) u, Module Double ( T u ) ) => Interpolatable u +type Interpolatable :: Type -> Type -> Constraint +class ( Torsor ( T u ) u, Module r ( T u ) ) => Interpolatable r u +instance ( Torsor ( T u ) u, Module r ( T u ) ) => Interpolatable r u -------------------------------------------------------------------------------- +instance ( Representable r u, Coercible r m, Module r m ) => Module r ( ApRep m u ) where + origin = ApRep $ tabulate @r @u \ _ -> coerce $ origin @r @m + {-# INLINE origin #-} + ApRep a ^+^ ApRep b = ApRep $ tabulate @r @u \ i -> + coerce $ (^+^) @r @m ( coerce ( index @r @u a i ) ) ( coerce ( index @r @u b i ) ) + {-# INLINE (^+^) #-} + ApRep a ^-^ ApRep b = ApRep $ tabulate @r @u \ i -> + coerce $ (^-^) @r @m ( coerce ( index @r @u a i ) ) ( coerce ( index @r @u b i ) ) + {-# INLINE (^-^) #-} + k *^ ApRep a = ApRep $ tabulate @r @u \ i -> + coerce $ (*^) @r @m k ( coerce ( index @r @u a i ) ) + {-# INLINE (*^) #-} + +deriving via ( ApRep ( Sum Double ) ( ℝ n ) ) + instance Representable Double ( ℝ n ) => Module Double ( T ( ℝ n ) ) + instance Num a => Module a ( Sum a ) where origin = Sum 0 (^+^) = (<>) - ( Sum x ) ^-^ ( Sum y ) = Sum ( x - y ) + Sum x ^-^ Sum y = Sum ( x - y ) - c *^ ( Sum x ) = Sum ( c * x ) - ( Sum x ) ^* c = Sum ( x * c ) + c *^ Sum x = Sum ( c * x ) + Sum x ^* c = Sum ( x * c ) instance Num a => Inner a ( Sum a ) where Sum a ^.^ Sum b = a * b -instance Module Double ( T ( ℝ 0 ) ) where - origin = T ℝ0 - _ ^+^ _ = T ℝ0 - _ ^-^ _ = T ℝ0 - _ *^ _ = T ℝ0 - deriving via Sum Double instance Module Double ( T Double ) -deriving via Sum Double instance Module Double ( T ( ℝ 1 ) ) - -instance Module Double ( T ( ℝ 2 ) ) where - origin = mempty - (^+^) = (<>) - T ( ℝ2 x1 y1 ) ^-^ T ( ℝ2 x2 y2 ) = - T ( ℝ2 ( x1 - x2 ) ( y1 - y2 ) ) - k *^ ( T ( ℝ2 a b ) ) = T ( ℝ2 ( k * a ) ( k * b ) ) - -instance Module Double ( T ( ℝ 3 ) ) where - origin = mempty - (^+^) = (<>) - T ( ℝ3 x1 y1 z1 ) ^-^ T ( ℝ3 x2 y2 z2 ) = - T ( ℝ3 ( x1 - x2 ) ( y1 - y2 ) ( z1 - z2 ) ) - k *^ ( T ( ℝ3 a b c ) ) = T ( ℝ3 ( k * a ) ( k * b ) ( k * c ) ) instance Inner Double ( T ( ℝ 2 ) ) where V2 x1 y1 ^.^ V2 x2 y2 = x1 * x2 + y1 * y2 @@ -213,35 +207,21 @@ convexCombination v0 v1 u -------------------------------------------------------------------------------- -- Interval arithmetic using rounded-hw library. -instance Module ( 𝕀 Double ) ( T ( 𝕀ℝ 2 ) ) where - origin = T ( I ( Rounded ( ℝ2 0 0 ) ) ( Rounded ( ℝ2 0 0 ) ) ) - T ( I ( Rounded ( ℝ2 x1_lo y1_lo ) ) ( Rounded ( ℝ2 x1_hi y1_hi ) ) ) ^+^ - T ( I ( Rounded ( ℝ2 x2_lo y2_lo ) ) ( Rounded ( ℝ2 x2_hi y2_hi ) ) ) - = let !( Rounded x_lo, Rounded x_hi ) = intervalAdd ( Rounded x1_lo ) ( Rounded x1_hi ) ( Rounded x2_lo ) ( Rounded x2_hi ) - !( Rounded y_lo, Rounded y_hi ) = intervalAdd ( Rounded y1_lo ) ( Rounded y1_hi ) ( Rounded y2_lo ) ( Rounded y2_hi ) - in T ( I ( Rounded ( ℝ2 x_lo y_lo ) ) ( Rounded ( ℝ2 x_hi y_hi ) ) ) - T ( I ( Rounded ( ℝ2 x1_lo y1_lo ) ) ( Rounded ( ℝ2 x1_hi y1_hi ) ) ) ^-^ - T ( I ( Rounded ( ℝ2 x2_lo y2_lo ) ) ( Rounded ( ℝ2 x2_hi y2_hi ) ) ) - = let !( Rounded x_lo, Rounded x_hi ) = intervalSub ( Rounded x1_lo ) ( Rounded x1_hi ) ( Rounded x2_lo ) ( Rounded x2_hi ) - !( Rounded y_lo, Rounded y_hi ) = intervalSub ( Rounded y1_lo ) ( Rounded y1_hi ) ( Rounded y2_lo ) ( Rounded y2_hi ) - in T ( I ( Rounded ( ℝ2 x_lo y_lo ) ) ( Rounded ( ℝ2 x_hi y_hi ) ) ) - I ( Rounded k_lo ) ( Rounded k_hi ) *^ T ( I ( Rounded ( ℝ2 x1_lo y1_lo ) ) ( Rounded ( ℝ2 x1_hi y1_hi ) ) ) - = let !( Rounded x_lo, Rounded x_hi ) = intervalMul ( Rounded k_lo ) ( Rounded k_hi ) ( Rounded x1_lo ) ( Rounded x1_hi ) - !( Rounded y_lo, Rounded y_hi ) = intervalMul ( Rounded k_lo ) ( Rounded k_hi ) ( Rounded y1_lo ) ( Rounded y1_hi ) - in T ( I ( Rounded ( ℝ2 x_lo y_lo ) ) ( Rounded ( ℝ2 x_hi y_hi ) ) ) +deriving via Sum ( 𝕀 Double ) instance Module ( 𝕀 Double ) ( T ( 𝕀 Double ) ) + +deriving via ApRep ( Sum ( 𝕀 Double ) ) ( 𝕀ℝ n ) + instance Representable ( 𝕀 Double ) ( 𝕀ℝ n ) => Module ( 𝕀 Double ) ( T ( 𝕀ℝ n ) ) instance Inner ( 𝕀 Double ) ( T ( 𝕀ℝ 2 ) ) where T ( I ( Rounded ( ℝ2 x1_lo y1_lo ) ) ( Rounded ( ℝ2 x1_hi y1_hi ) ) ) ^.^ T ( I ( Rounded ( ℝ2 x2_lo y2_lo ) ) ( Rounded ( ℝ2 x2_hi y2_hi ) ) ) - = let !( x_lo, x_hi ) = intervalMul ( Rounded x1_lo ) ( Rounded x1_hi ) ( Rounded x2_lo ) ( Rounded x2_hi ) - !( y_lo, y_hi ) = intervalMul ( Rounded y1_lo ) ( Rounded y1_hi ) ( Rounded y2_lo ) ( Rounded y2_hi ) - !( z_lo, z_hi ) = intervalAdd x_lo x_hi y_lo y_hi - in I z_lo z_hi + = let !x1x2 = I ( Rounded x1_lo ) ( Rounded x1_hi ) * I ( Rounded x2_lo ) ( Rounded x2_hi ) + !y1y2 = I ( Rounded y1_lo ) ( Rounded y1_hi ) * I ( Rounded y2_lo ) ( Rounded y2_hi ) + in x1x2 + y1y2 instance Cross ( 𝕀 Double ) ( T ( 𝕀ℝ 2 ) ) where T ( I ( Rounded ( ℝ2 x1_lo y1_lo ) ) ( Rounded ( ℝ2 x1_hi y1_hi ) ) ) `cross` T ( I ( Rounded ( ℝ2 x2_lo y2_lo ) ) ( Rounded ( ℝ2 x2_hi y2_hi ) ) ) - = let !( x_lo, x_hi ) = intervalMul ( Rounded x1_lo ) ( Rounded x1_hi ) ( Rounded y2_lo ) ( Rounded y2_hi ) - !( y_lo, y_hi ) = intervalMul ( Rounded x2_lo ) ( Rounded x2_hi ) ( Rounded y1_lo ) ( Rounded y1_hi ) - !( z_lo, z_hi ) = intervalSub x_lo x_hi y_lo y_hi - in I z_lo z_hi + = let !x1y2 = I ( Rounded x1_lo ) ( Rounded x1_hi ) * I ( Rounded y2_lo ) ( Rounded y2_hi ) + !y2x1 = I ( Rounded x2_lo ) ( Rounded x2_hi ) * I ( Rounded y1_lo ) ( Rounded y1_hi ) + in x1y2 - y2x1