framework for brush differentiation

This commit is contained in:
sheaf 2023-01-08 17:16:14 +01:00
parent 4e5c848883
commit 5bd4e7f4cf
56 changed files with 1319 additions and 1458 deletions

View file

@ -50,6 +50,44 @@ common common
, transformers , transformers
^>= 0.5.6.2 ^>= 0.5.6.2
default-extensions:
BangPatterns
BlockArguments
ConstraintKinds
DataKinds
DeriveAnyClass
DeriveTraversable
DeriveGeneric
DerivingVia
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
FunctionalDependencies
GADTs
GeneralisedNewtypeDeriving
ImplicitParams
InstanceSigs
LambdaCase
LexicalNegation
MagicHash
MultiWayIf
NamedFieldPuns
PatternSynonyms
QuantifiedConstraints
RankNTypes
RecordWildCards
RecursiveDo
ScopedTypeVariables
StandaloneDeriving
StandaloneKindSignatures
TupleSections
TypeApplications
TypeFamilies
TypeOperators
UnboxedTuples
UndecidableInstances
ViewPatterns
ghc-options: ghc-options:
-O1 -O1
-fexpose-all-unfoldings -fexpose-all-unfoldings
@ -132,11 +170,12 @@ library splines
, Math.Bezier.Spline , Math.Bezier.Spline
, Math.Bezier.Stroke , Math.Bezier.Stroke
, Math.Epsilon , Math.Epsilon
, Math.Linear
, Math.Linear.Dual
, Math.Linear.Solve , Math.Linear.Solve
, Math.Module , Math.Module
, Math.Orientation , Math.Orientation
, Math.Roots , Math.Roots
, Math.Vector2D
build-depends: build-depends:
bifunctors bifunctors
@ -170,8 +209,6 @@ library metabrushes
, MetaBrush.Document.History , MetaBrush.Document.History
, MetaBrush.Document.Serialise , MetaBrush.Document.Serialise
, MetaBrush.Document.SubdivideStroke , MetaBrush.Document.SubdivideStroke
, MetaBrush.DSL.Interpolation
, MetaBrush.DSL.Types
, MetaBrush.Records , MetaBrush.Records
, MetaBrush.Serialisable , MetaBrush.Serialisable
, MetaBrush.Unique , MetaBrush.Unique

View file

@ -1,19 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.Action where module MetaBrush.Action where
@ -104,7 +89,7 @@ import Math.Bezier.Stroke
( CachedStroke(..), invalidateCache ) ( CachedStroke(..), invalidateCache )
import Math.Module import Math.Module
( Module((*^)), quadrance ) ( Module((*^)), quadrance )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..) )
import MetaBrush.Context import MetaBrush.Context
( UIElements(..), Variables(..) ( UIElements(..), Variables(..)

View file

@ -1,5 +1,3 @@
{-# LANGUAGE MonoLocalBinds #-}
module MetaBrush.Action where module MetaBrush.Action where
-- base -- base
@ -18,7 +16,7 @@ import Data.Text
( Text ) ( Text )
-- MetaBrush -- MetaBrush
import Math.Vector2D import Math.Linear
( Point2D, Vector2D ) ( Point2D, Vector2D )
import {-# SOURCE #-} MetaBrush.Context import {-# SOURCE #-} MetaBrush.Context
( UIElements, Variables ) ( UIElements, Variables )

View file

@ -1,13 +1,5 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Application module MetaBrush.Application
( runApplication ) ( runApplication )
@ -86,8 +78,8 @@ import Math.Bezier.Spline
( Spline(..), Curves(..), Curve(..), NextPoint(..) ) ( Spline(..), Curves(..), Curve(..), NextPoint(..) )
import Math.Bezier.Stroke import Math.Bezier.Stroke
( invalidateCache ) ( invalidateCache )
import Math.Vector2D import Math.Linear
( Point2D(..) ) ( Point2D(..), (..) )
import MetaBrush.Action import MetaBrush.Action
( ActionOrigin(..) ) ( ActionOrigin(..) )
import qualified MetaBrush.Asset.Brushes as Asset.Brushes import qualified MetaBrush.Asset.Brushes as Asset.Brushes
@ -113,9 +105,6 @@ import MetaBrush.Document.Update
import MetaBrush.Event import MetaBrush.Event
( handleEvents ) ( handleEvents )
import MetaBrush.Records import MetaBrush.Records
( Rec, I(..) )
import qualified MetaBrush.Records as Rec
( empty, insert )
import MetaBrush.Render.Document import MetaBrush.Render.Document
( blankRender, getDocumentRender ) ( blankRender, getDocumentRender )
import MetaBrush.Render.Rulers import MetaBrush.Render.Rulers
@ -184,10 +173,8 @@ runApplication application = do
) )
] ]
where where
mkPoint :: Point2D Double -> Double -> Double -> Double -> PointData ( Rec Asset.Brushes.EllipseBrushFields ) mkPoint :: Point2D Double -> Double -> Double -> Double -> PointData ( Record Asset.Brushes.EllipseBrushFields )
mkPoint pt a b phi = PointData pt Normal mkPoint pt a b phi = PointData pt Normal ( MkR $ 3 a b phi )
( Rec.insert @"a" (I a) $ Rec.insert @"b" (I b) $ Rec.insert @"phi" (I phi) $ Rec.empty )
recomputeStrokesTVar <- STM.newTVarIO @Bool False recomputeStrokesTVar <- STM.newTVarIO @Bool False
documentRenderTVar <- STM.newTVarIO @( ( Int32, Int32 ) -> Cairo.Render () ) ( const $ pure () ) documentRenderTVar <- STM.newTVarIO @( ( Int32, Int32 ) -> Cairo.Render () ) ( const $ pure () )
@ -225,7 +212,7 @@ runApplication application = do
display <- GTK.rootGetDisplay window display <- GTK.rootGetDisplay window
dataPath <- Directory.canonicalizePath =<< Cabal.getDataDir dataPath <- Directory.canonicalizePath =<< Cabal.getDataDir
themePath <- ( Directory.canonicalizePath =<< Cabal.getDataFileName "theme.css" ) themePath <- Directory.canonicalizePath =<< Cabal.getDataFileName "theme.css"
cssProvider <- GTK.cssProviderNew cssProvider <- GTK.cssProviderNew
GTK.cssProviderLoadFromPath cssProvider themePath GTK.cssProviderLoadFromPath cssProvider themePath
GTK.styleContextAddProviderForDisplay display cssProvider 1000 GTK.styleContextAddProviderForDisplay display cssProvider 1000

View file

@ -1,7 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE RecordWildCards #-}
module MetaBrush.Asset.CloseTabButton module MetaBrush.Asset.CloseTabButton
( drawCloseTabButton ) ( drawCloseTabButton )
where where

View file

@ -1,9 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-deprecations #-} {-# OPTIONS_GHC -fno-warn-deprecations #-}

View file

@ -1,5 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
module MetaBrush.Asset.Cursor module MetaBrush.Asset.Cursor
( drawCursor, drawCursorIcon ) ( drawCursor, drawCursorIcon )
where where

View file

@ -1,6 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
module MetaBrush.Asset.InfoBar module MetaBrush.Asset.InfoBar
( drawMagnifier, drawTopLeftCornerRect ) ( drawMagnifier, drawTopLeftCornerRect )
where where

View file

@ -1,5 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
module MetaBrush.Asset.Logo module MetaBrush.Asset.Logo
( drawLogo ) ( drawLogo )
where where

View file

@ -1,6 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
module MetaBrush.Asset.TickBox module MetaBrush.Asset.TickBox
( drawBox, drawTickedBox ) ( drawBox, drawTickedBox )
where where

View file

@ -1,6 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
module MetaBrush.Asset.Tools module MetaBrush.Asset.Tools
( drawBrush, drawBug, drawMeta, drawPath, drawPen ) ( drawBrush, drawBug, drawMeta, drawPath, drawPen )
where where

View file

@ -1,5 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
module MetaBrush.Asset.WindowIcons module MetaBrush.Asset.WindowIcons
( drawMinimise, drawRestoreDown, drawMaximise, drawClose ) ( drawMinimise, drawRestoreDown, drawMaximise, drawClose )
where where

View file

@ -1,6 +1,3 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
module MetaBrush.Context module MetaBrush.Context
( UIElements(..), Variables(..) ( UIElements(..), Variables(..)
, LR(..), Modifier(..), modifierKey , LR(..), Modifier(..), modifierKey
@ -44,7 +41,7 @@ import Data.HashMap.Strict
-- MetaBrush -- MetaBrush
import Math.Bezier.Cubic.Fit import Math.Bezier.Cubic.Fit
( FitParameters ) ( FitParameters )
import Math.Vector2D import Math.Linear
( Point2D ) ( Point2D )
import {-# SOURCE #-} MetaBrush.Action import {-# SOURCE #-} MetaBrush.Action
( ActionName ) ( ActionName )

View file

@ -1,19 +1,4 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module MetaBrush.Document.Selection module MetaBrush.Document.Selection
( SelectionMode(..), selectionMode ( SelectionMode(..), selectionMode
@ -108,9 +93,9 @@ import Math.Bezier.Spline
import Math.Bezier.Stroke import Math.Bezier.Stroke
( CachedStroke(..), invalidateCache ) ( CachedStroke(..), invalidateCache )
import Math.Module import Math.Module
( lerp, squaredNorm, closestPointOnSegment ) ( Module, lerp, squaredNorm, closestPointOnSegment )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..), Segment(..) ) ( Point2D(..), Vector2D(..), Segment(..), T(..) )
import {-# SOURCE #-} MetaBrush.Context import {-# SOURCE #-} MetaBrush.Context
( Modifier(..) ) ( Modifier(..) )
import MetaBrush.Document import MetaBrush.Document
@ -122,8 +107,6 @@ import MetaBrush.Document
) )
import {-# SOURCE #-} MetaBrush.Document.Update import {-# SOURCE #-} MetaBrush.Document.Update
( DocChange(..) ) ( DocChange(..) )
import MetaBrush.DSL.Interpolation
( Interpolatable(Diff) )
import MetaBrush.Unique import MetaBrush.Unique
( Unique ) ( Unique )
import MetaBrush.Util import MetaBrush.Util
@ -753,7 +736,7 @@ dragUpdate _ p ( ClickedOnCurve { dragStrokeUnique, dragSegmentIndex, dragSegmen
where where
updateSpline updateSpline
:: forall clo pointParams :: forall clo pointParams
. ( KnownSplineType clo, Interpolatable pointParams ) . ( KnownSplineType clo, Module Double ( T pointParams ), Torsor ( T pointParams ) pointParams )
=> StrokeSpline clo pointParams => StrokeSpline clo pointParams
-> State ( Maybe Text ) ( StrokeSpline clo pointParams ) -> State ( Maybe Text ) ( StrokeSpline clo pointParams )
updateSpline updateSpline
@ -785,16 +768,16 @@ dragUpdate _ p ( ClickedOnCurve { dragStrokeUnique, dragSegmentIndex, dragSegmen
LineTo ( NextPoint sp1 ) dat -> do LineTo ( NextPoint sp1 ) dat -> do
let let
bez2 :: Quadratic.Bezier ( PointData pointParams ) bez2 :: Quadratic.Bezier ( PointData pointParams )
bez2 = Quadratic.Bezier sp0 ( lerp @( DiffPointData ( Diff pointParams ) ) dragSegmentParameter sp0 sp1 ) sp1 bez2 = Quadratic.Bezier sp0 ( lerp @( DiffPointData ( T pointParams ) ) dragSegmentParameter sp0 sp1 ) sp1
if alternateMode if alternateMode
then quadraticDragCurve dat bez2 then quadraticDragCurve dat bez2
else cubicDragCurve dat ( Cubic.fromQuadratic @( DiffPointData ( Diff pointParams ) ) bez2 ) else cubicDragCurve dat ( Cubic.fromQuadratic @( DiffPointData ( T pointParams ) ) bez2 )
Bezier2To sp1 ( NextPoint sp2 ) dat -> do Bezier2To sp1 ( NextPoint sp2 ) dat -> do
let let
bez2 :: Quadratic.Bezier ( PointData pointParams ) bez2 :: Quadratic.Bezier ( PointData pointParams )
bez2 = Quadratic.Bezier sp0 sp1 sp2 bez2 = Quadratic.Bezier sp0 sp1 sp2
if alternateMode if alternateMode
then cubicDragCurve dat $ Cubic.fromQuadratic @( DiffPointData ( Diff pointParams ) ) bez2 then cubicDragCurve dat $ Cubic.fromQuadratic @( DiffPointData ( T pointParams ) ) bez2
else quadraticDragCurve dat ( Quadratic.Bezier sp0 sp1 sp2 ) else quadraticDragCurve dat ( Quadratic.Bezier sp0 sp1 sp2 )
Bezier3To sp1 sp2 ( NextPoint sp3 ) dat -> do Bezier3To sp1 sp2 ( NextPoint sp3 ) dat -> do
let let
@ -804,7 +787,7 @@ dragUpdate _ p ( ClickedOnCurve { dragStrokeUnique, dragSegmentIndex, dragSegmen
then quadraticDragCurve dat then quadraticDragCurve dat
( Quadratic.Bezier ( Quadratic.Bezier
sp0 sp0
( Cubic.bezier @( DiffPointData ( Diff pointParams ) ) bez3 dragSegmentParameter ) ( Cubic.bezier @( DiffPointData ( T pointParams ) ) bez3 dragSegmentParameter )
sp3 sp3
) )
else cubicDragCurve dat bez3 else cubicDragCurve dat bez3

View file

@ -1,11 +1,4 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Document.Update module MetaBrush.Document.Update
( activeDocument, withActiveDocument ( activeDocument, withActiveDocument

View file

@ -1,9 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Event module MetaBrush.Event
( handleEvents ) ( handleEvents )
where where
@ -29,7 +23,7 @@ import qualified Control.Concurrent.STM.TVar as STM
( readTVarIO ) ( readTVarIO )
-- MetaBrush -- MetaBrush
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..) )
import MetaBrush.Action import MetaBrush.Action
( HandleAction(..) ( HandleAction(..)

View file

@ -1,11 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.GTK.Util module MetaBrush.GTK.Util
( withRGBA, showRGBA ( withRGBA, showRGBA
, widgetAddClasses, widgetAddClass , widgetAddClasses, widgetAddClass

View file

@ -1,22 +1,5 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module MetaBrush.Render.Document module MetaBrush.Render.Document
( getDocumentRender, blankRender ) ( getDocumentRender, blankRender )
@ -87,8 +70,8 @@ import Math.Bezier.Stroke
( CachedStroke(..), invalidateCache ( CachedStroke(..), invalidateCache
, computeStrokeOutline , computeStrokeOutline
) )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..), T(..) )
import MetaBrush.Asset.Colours import MetaBrush.Asset.Colours
( Colours, ColourRecord(..) ) ( Colours, ColourRecord(..) )
import MetaBrush.Brush import MetaBrush.Brush
@ -116,15 +99,7 @@ import MetaBrush.Document.Serialise
( ) -- 'Serialisable' instances ( ) -- 'Serialisable' instances
import MetaBrush.Document.Update import MetaBrush.Document.Update
( DocChange(..) ) ( DocChange(..) )
import MetaBrush.DSL.Interpolation
( Interpolatable, DRec )
import MetaBrush.Records import MetaBrush.Records
( Record, Rec, WithParams(..)
, I(..), (:*:)(..)
, MyIntersection (..), myIntersect
)
import qualified MetaBrush.Records as Rec
( map )
import MetaBrush.UI.ToolBar import MetaBrush.UI.ToolBar
( Mode(..) ) ( Mode(..) )
import MetaBrush.Unique import MetaBrush.Unique
@ -205,9 +180,9 @@ getDocumentRender
, Just finalPoint <- mbFinalPoint , Just finalPoint <- mbFinalPoint
, let , let
previewStroke :: Stroke previewStroke :: Stroke
previewStroke = withAnchorBrushData anchor doc \ mbBrush ( pointData :: Rec pointFields ) -> previewStroke = withAnchorBrushData anchor doc \ mbBrush ( pointData :: Record pointFields ) ->
let let
previewSpline :: Spline Open ( CachedStroke RealWorld ) ( PointData ( Rec pointFields ) ) previewSpline :: Spline Open ( CachedStroke RealWorld ) ( PointData ( Record pointFields ) )
previewSpline = catMaybesSpline ( invalidateCache undefined ) previewSpline = catMaybesSpline ( invalidateCache undefined )
( PointData p0 Normal pointData ) ( PointData p0 Normal pointData )
( do ( do
@ -295,7 +270,7 @@ instance NFData StrokeRenderData where
strokeRenderData :: FitParameters -> Stroke -> Maybe ( ST RealWorld StrokeRenderData ) strokeRenderData :: FitParameters -> Stroke -> Maybe ( ST RealWorld StrokeRenderData )
strokeRenderData fitParams strokeRenderData fitParams
( Stroke ( Stroke
{ strokeSpline = spline :: StrokeSpline clo ( Rec pointFields ) { strokeSpline = spline :: StrokeSpline clo ( Record pointFields )
, strokeBrush = ( strokeBrush :: Maybe ( Brush brushFields ) ) , strokeBrush = ( strokeBrush :: Maybe ( Brush brushFields ) )
, .. , ..
} }
@ -306,32 +281,26 @@ strokeRenderData fitParams
{ defaultParams = brush_defaults { defaultParams = brush_defaults
, withParams = brushFn , withParams = brushFn
} <- fn } <- fn
-> do -> -- This is the key place where we need to perform impedance matching
-- Use the handy 'intersect' function to do a computation -- between the collection of parameters supplied along a strong and
-- using only the relevant fields (which are the intersection -- the collection of parameters expected by the brush.
-- of the parameters along the stroke and the brush parameters). case intersect @pointFields @brushFields of
-- Intersection
-- See also MetaBrush.DSL.Eval.eval for how we interpret brush code { inject
-- to obtain a brush function. , project = toUsedParams :: Record pointFields -> Record usedFields }
case myIntersect @Interpolatable @pointFields brush_defaults of -> do
MyIntersection let embedUsedParams r = inject r brush_defaults
{ myProject = project :: forall f. Record f pointFields -> Record (f :*: I) usedFields
, myInject } -> do -- Compute the outline using the brush function.
let ( outline, fitPts ) <-
toUsedParams :: Rec pointFields -> Rec usedFields computeStrokeOutline @( T ( Record usedFields) ) @clo
toUsedParams given = Rec.map ( \ (x :*: _) -> x ) $ project @I given fitParams ( toUsedParams . brushParams ) ( brushFn . embedUsedParams ) spline
embedUsedParams :: Rec usedFields -> Rec brushFields pure $
embedUsedParams = myInject StrokeWithOutlineRenderData
-- Compute the outline using the brush function. { strokeDataSpline = spline
( outline, fitPts ) <- , strokeOutlineData = ( outline, fitPts )
computeStrokeOutline @( DRec usedFields ) @clo @( Rec usedFields ) , strokeBrushFunction = brushFn . embedUsedParams . toUsedParams
fitParams ( toUsedParams . brushParams ) ( brushFn . embedUsedParams ) spline }
pure $
StrokeWithOutlineRenderData
{ strokeDataSpline = spline
, strokeOutlineData = ( outline, fitPts )
, strokeBrushFunction = brushFn . embedUsedParams . toUsedParams
}
_ -> pure $ _ -> pure $
StrokeRenderData StrokeRenderData
{ strokeDataSpline = spline } { strokeDataSpline = spline }
@ -443,7 +412,7 @@ renderBrushShape cols mbHoverContext zoom brushFn pt =
brushPts :: SplinePts Closed brushPts :: SplinePts Closed
brushPts = brushFn ( brushParams pt ) brushPts = brushFn ( brushParams pt )
mbHoverContext' :: Maybe HoverContext mbHoverContext' :: Maybe HoverContext
mbHoverContext' = Vector2D (-x) (-y) mbHoverContext mbHoverContext' = Vector2D -x -y mbHoverContext
in in
toAll do toAll do
Cairo.save Cairo.save
@ -467,12 +436,12 @@ drawPoint ( Colours {..} ) mbHover zoom PathPoint pt
Cairo.translate x y Cairo.translate x y
Cairo.scale ( 3 / zoom ) ( 3 / zoom ) Cairo.scale ( 3 / zoom ) ( 3 / zoom )
Cairo.moveTo 1 0 Cairo.moveTo 1 0
Cairo.lineTo 0.5 hsqrt3 Cairo.lineTo 0.5 hsqrt3
Cairo.lineTo -0.5 hsqrt3 Cairo.lineTo -0.5 hsqrt3
Cairo.lineTo -1 0 Cairo.lineTo -1 0
Cairo.lineTo -0.5 (-hsqrt3) Cairo.lineTo -0.5 -hsqrt3
Cairo.lineTo 0.5 (-hsqrt3) Cairo.lineTo 0.5 -hsqrt3
Cairo.closePath Cairo.closePath
Cairo.setLineWidth 1.0 Cairo.setLineWidth 1.0

View file

@ -1,12 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Render.Rulers module MetaBrush.Render.Rulers
( renderRuler ) ( renderRuler )
where where
@ -51,7 +42,7 @@ import Control.Lens
( set, over ) ( set, over )
-- MetaBrush -- MetaBrush
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..) )
import MetaBrush.Action import MetaBrush.Action
( ActionOrigin(..) ) ( ActionOrigin(..) )
@ -255,7 +246,7 @@ renderRuler
Cairo.translate tickPosition top Cairo.translate tickPosition top
Cairo.scale ( 1 / zoomFactor ) ( 1 / zoomFactor ) Cairo.scale ( 1 / zoomFactor ) ( 1 / zoomFactor )
Cairo.moveTo 0 0 Cairo.moveTo 0 0
Cairo.lineTo 0 (-tickSize) Cairo.lineTo 0 -tickSize
Cairo.stroke Cairo.stroke
when tickHasLabel do when tickHasLabel do
Cairo.translate 2 -8.5 Cairo.translate 2 -8.5

View file

@ -57,8 +57,8 @@ pprSeconds ( h_name, m_name, s_name ) ( Seconds secs ) = pm <> absolute
where where
pm :: String pm :: String
pm pm
| secs <= (-1) = "-" | secs <= -1 = "-"
| otherwise = "" | otherwise = ""
h, r, m, s :: Int64 h, r, m, s :: Int64
(h,r) = round ( abs secs ) `divMod` 3600 (h,r) = round ( abs secs ) `divMod` 3600
(m,s) = r `divMod` 60 (m,s) = r `divMod` 60

View file

@ -1,11 +1,3 @@
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.UI.Coordinates module MetaBrush.UI.Coordinates
( toViewportCoordinates, closestPoint ) ( toViewportCoordinates, closestPoint )
where where
@ -37,7 +29,7 @@ import Math.Bezier.Spline
) )
import Math.Module import Math.Module
( (*^), squaredNorm, closestPointOnSegment ) ( (*^), squaredNorm, closestPointOnSegment )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..), Segment(..) ) ( Point2D(..), Vector2D(..), Segment(..) )
import MetaBrush.Document import MetaBrush.Document
( Stroke(..), PointData(..) ( Stroke(..), PointData(..)

View file

@ -1,10 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.UI.FileBar module MetaBrush.UI.FileBar
( FileBar(..), FileBarTab(..) ( FileBar(..), FileBarTab(..)

View file

@ -1,10 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.UI.InfoBar module MetaBrush.UI.InfoBar
( InfoBar(..), createInfoBar, updateInfoBar ) ( InfoBar(..), createInfoBar, updateInfoBar )
@ -41,7 +35,7 @@ import qualified Data.Text as Text
( pack ) ( pack )
-- MetaBrush -- MetaBrush
import Math.Vector2D import Math.Linear
( Point2D(..) ) ( Point2D(..) )
import MetaBrush.Asset.Colours import MetaBrush.Asset.Colours
( Colours ) ( Colours )

View file

@ -1,14 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.UI.Menu where module MetaBrush.UI.Menu where

View file

@ -1,4 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module MetaBrush.UI.Panels module MetaBrush.UI.Panels

View file

@ -1,8 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.UI.ToolBar module MetaBrush.UI.ToolBar
( Tool(..), Mode(..) ( Tool(..), Mode(..)

View file

@ -1,7 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module MetaBrush.UI.Viewport module MetaBrush.UI.Viewport
( Viewport(..), ViewportEventControllers(..) ( Viewport(..), ViewportEventControllers(..)

View file

@ -1,6 +1,4 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where module Main where
@ -41,7 +39,7 @@ import Math.Bezier.Spline
( Spline, SplineType(..) ) ( Spline, SplineType(..) )
import Math.Bezier.Stroke import Math.Bezier.Stroke
( CachedStroke(..) ) ( CachedStroke(..) )
import Math.Vector2D import Math.Linear
( Point2D(..) ) ( Point2D(..) )
-- metabrushes -- metabrushes
@ -113,7 +111,7 @@ test = trailToSpline @Diagrams.Line
mk_ellipse a phi = mk_ellipse a phi =
Rec.insert @"a" (I $ 0.5 * a) $ Rec.insert @"b" (I 0.05) $ Rec.insert @"phi" (I phi) $ Rec.empty Rec.insert @"a" (I $ 0.5 * a) $ Rec.insert @"b" (I 0.05) $ Rec.insert @"phi" (I phi) $ Rec.empty
up = arriving $ Linear.V2 0 (-1) up = arriving $ Linear.V2 0 -1
main :: IO () main :: IO ()
main = case test of main = case test of

View file

@ -1,16 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE UnboxedTuples #-}
module MetaBrush.MetaFont.Convert module MetaBrush.MetaFont.Convert
( MetaFontError(..) ( MetaFontError(..)
, SomeSpline(..) , SomeSpline(..)
@ -78,7 +65,7 @@ import Math.Bezier.Stroke
( CachedStroke(..) ) ( CachedStroke(..) )
import Math.Module import Math.Module
( lerp ) ( lerp )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..) )
-- metabrushes -- metabrushes

View file

@ -1,15 +1,11 @@
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module MetaBrush.Asset.Brushes where module MetaBrush.Asset.Brushes where
-- base
import Data.Coerce
( coerce )
-- containers -- containers
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
( fromList ) ( fromList )
@ -26,16 +22,19 @@ import qualified Data.HashMap.Strict as HashMap
-- MetaBrush -- MetaBrush
import Math.Bezier.Spline import Math.Bezier.Spline
import Math.Vector2D import Math.Linear
( Point2D(..), (..), T(..) )
import Math.Linear.Dual
( D, type (~>)(..), Var(var), konst )
import Math.Module
( Module((^+^), (*^)) )
import MetaBrush.Brush import MetaBrush.Brush
( Brush(..), SomeBrush(..) ) ( Brush(..), SomeBrush(..) )
import MetaBrush.Records import MetaBrush.Records
( Rec, WithParams(..), I(..) )
import qualified MetaBrush.Records as Rec
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type CircleBrushFields = '[ '("r", Double) ] type CircleBrushFields = '[ "r" ]
lookupBrush :: Text -> Maybe SomeBrush lookupBrush :: Text -> Maybe SomeBrush
lookupBrush nm = HashMap.lookup nm brushes lookupBrush nm = HashMap.lookup nm brushes
@ -51,8 +50,8 @@ brushes = HashMap.fromList
-- | Root of @(Sqrt[2] (4 + 3 κ) - 16) (2 - 3 κ)^2 - 8 (1 - 3 κ) Sqrt[8 - 24 κ + 12 κ^2 + 8 κ^3 + 3 κ^4]@. -- | 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. -- Used to approximate circles and ellipses with Bézier curves.
c :: Double κ :: Double
c = 0.5519150244935105707435627227925 κ = 0.5519150244935105707435627227925
circleSpline :: (Double -> Double -> ptData) -> Spline 'Closed () ptData circleSpline :: (Double -> Double -> ptData) -> Spline 'Closed () ptData
circleSpline p = circleSpline p =
@ -60,38 +59,71 @@ circleSpline p =
, splineCurves = ClosedCurves crvs lastCrv } , splineCurves = ClosedCurves crvs lastCrv }
where where
crvs = Seq.fromList crvs = Seq.fromList
[ Bezier3To (p 1 c) (p c 1 ) (NextPoint (p 0 1 )) () [ Bezier3To (p 1 κ) (p κ 1) (NextPoint (p 0 1)) ()
, Bezier3To (p (-c) 1) (p (-1) c ) (NextPoint (p (-1) 0 )) () , Bezier3To (p -κ 1) (p -1 κ) (NextPoint (p -1 0)) ()
, Bezier3To (p (-1) (-c)) (p (-c) (-1)) (NextPoint (p 0 (-1))) () , Bezier3To (p -1 -κ) (p -κ -1) (NextPoint (p 0 -1)) ()
] ]
lastCrv = lastCrv =
Bezier3To (p c (-1)) (p 1 (-c)) BackToStart () Bezier3To (p κ -1) (p 1 -κ) BackToStart ()
circle :: Brush CircleBrushFields circle :: Brush CircleBrushFields
circle = BrushData "circle" (WithParams deflts shape) circle = BrushData "circle" (WithParams deflts shape)
where where
deflts :: Rec CircleBrushFields deflts :: Record CircleBrushFields
deflts = Rec.insert @"r" (I 1) Rec.empty deflts = MkR ( 1 1 )
shape :: Rec CircleBrushFields -> SplinePts 'Closed shape :: Record CircleBrushFields -> SplinePts 'Closed
shape params = shape ( MkR ( 1 r ) ) =
let !(I !r) = Rec.lookup @"r" params circleSpline ( \ x y -> Point2D (r * x) (r * y) )
in circleSpline ( \ x y -> Point2D (r * x) (r * y) )
type EllipseBrushFields = '[ '("a", Double), '("b", Double), '("phi", Double) ] type EllipseBrushFields = '[ "a", "b", "phi" ]
ellipse :: Brush EllipseBrushFields ellipse :: Brush EllipseBrushFields
ellipse = BrushData "ellipse" (WithParams deflts shape) ellipse = BrushData "ellipse" (WithParams deflts shape)
where where
deflts :: Rec EllipseBrushFields deflts :: Record EllipseBrushFields
deflts = Rec.insert @"a" (I 1) deflts = MkR ( 3 1 1 0 )
$ Rec.insert @"b" (I 1) shape :: Record EllipseBrushFields -> SplinePts 'Closed
$ Rec.insert @"phi" (I 0) shape ( MkR ( 3 a b phi ) ) =
$ Rec.empty circleSpline ( \ x y -> Point2D (a * x * cos phi - b * y * sin phi)
shape :: Rec EllipseBrushFields -> SplinePts 'Closed (b * y * cos phi + a * x * sin phi) )
shape params =
let --------------------------------------------------------------------------------
!(I !a ) = Rec.lookup @"a" params -- Differentiable brushes.
!(I !b ) = Rec.lookup @"b" params
!(I !phi) = Rec.lookup @"phi" params circleSpline2 :: ( Double -> Double -> D ( 3 ) ptData ) -> D ( 3 ) ( Spline 'Closed () ptData )
in circleSpline ( \ x y -> Point2D (a * x * cos phi - b * y * sin phi) circleSpline2 p = sequenceA $
(b * y * cos phi + a * x * sin phi) ) 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

View file

@ -1,35 +1,24 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints#-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module MetaBrush.Brush module MetaBrush.Brush
( Brush(..), SomeBrush(..) ( Brush(..), SomeBrush(..), BrushFunction
, BrushFunction , PointFields, provePointFields, duplicates
, SomeFieldSType(..), SomeBrushFields(..)
, reflectBrushFieldsNoDups
) )
where where
-- base -- base
import Control.Arrow import Data.Kind
( second ) ( Type, Constraint )
import Data.Proxy import Data.List
( Proxy(..) ) ( nub )
import Data.Typeable
( Typeable )
import GHC.Exts import GHC.Exts
( Proxy#, Any ) ( Proxy#, proxy# )
import Unsafe.Coerce import GHC.TypeLits
( unsafeCoerce ) ( Symbol, someSymbolVal
, SomeSymbol(..)
)
-- deepseq -- deepseq
import Control.DeepSeq import Control.DeepSeq
@ -45,49 +34,34 @@ import Data.Text
import qualified Data.Text as Text import qualified Data.Text as Text
( unpack ) ( unpack )
-- unordered-containers
import qualified Data.HashMap.Strict as HashMap
( fromList )
-- MetaBrush -- MetaBrush
import Math.Bezier.Spline import Math.Bezier.Spline
( SplineType(Closed), SplinePts) ( SplineType(Closed), SplinePts)
import MetaBrush.Serialisable
( Serialisable )
import MetaBrush.DSL.Types
( STypeI, STypesI
, SomeSType(..), proveSomeSTypes
)
import MetaBrush.DSL.Interpolation
( Interpolatable(..) )
import MetaBrush.Records import MetaBrush.Records
( Record(MkR), Rec, AllFields import MetaBrush.Serialisable
, WithParams(..) import Math.Linear
, Dict(..)
, proveRecordDicts
)
import qualified MetaBrush.Records as Rec
( map )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | A brush function: a function from a record of parameters to a closed spline. -- | 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 (SplinePts Closed)
type Brush :: [ Symbol ] -> Type
data Brush brushFields where data Brush brushFields where
BrushData BrushData
:: forall brushFields :: forall brushFields
. ( STypesI brushFields ) . ( KnownSymbols brushFields
=> , Representable ( ( Length brushFields) )
{ brushName :: !Text , Typeable brushFields )
, brushFunction :: BrushFunction brushFields => { brushName :: !Text
} , brushFunction :: BrushFunction brushFields
}
-> Brush brushFields -> Brush brushFields
data SomeBrush where data SomeBrush where
SomeBrush SomeBrush
:: STypesI brushFields :: { someBrush :: !( Brush brushFields ) }
=> { someBrush :: !( Brush brushFields ) }
-> SomeBrush -> SomeBrush
instance Show ( Brush brushFields ) where instance Show ( Brush brushFields ) where
@ -107,62 +81,52 @@ instance Hashable ( Brush brushFields ) where
hashWithSalt salt ( BrushData { brushName } ) = hashWithSalt salt ( BrushData { brushName } ) =
hashWithSalt salt brushName hashWithSalt salt brushName
-------------------------------------------------------------------------------- type PointFields :: [ Symbol ] -> Constraint
-- Instance dictionary passing machinery. class ( KnownSymbols pointFields, Typeable pointFields
, Serialisable ( Record pointFields )
-- | Existential type over an allowed record field type used in brushes, such as Double and Point2D Double. , Show ( Record pointFields )
data SomeFieldSType where , NFData ( Record pointFields )
SomeFieldSType , Interpolatable ( Record pointFields )
:: ( STypeI a, Show a, NFData a, Interpolatable a, Serialisable a ) , Representable ( ( Length pointFields ) )
=> SomeFieldSType
data FieldSType a where
FieldSType
:: ( STypeI a, Show a, NFData a, Interpolatable a, Serialisable a )
=> FieldSType a
-- | Existential type for allowed fields of a brush record.
data SomeBrushFields where
SomeBrushFields
:: forall kvs rec
. ( STypesI kvs
, rec ~ Rec kvs
, Show rec, NFData rec
, Serialisable rec
, AllFields Interpolatable kvs
) )
=> SomeBrushFields => PointFields pointFields where { }
instance ( KnownSymbols pointFields, Typeable pointFields
, Serialisable ( Record pointFields )
, Show ( Record pointFields )
, NFData ( Record pointFields )
, Interpolatable ( Record pointFields )
, Representable ( ( Length pointFields ) )
)
=> PointFields pointFields where { }
instance Show SomeBrushFields where -- | Assumes the input has no duplicates (doesn't check.)
show ( SomeBrushFields @kvs ) = show ( Proxy @kvs ) provePointFields :: [ Text ] -> ( forall pointFields. PointFields pointFields => Proxy# pointFields -> r ) -> r
provePointFields fieldNames k =
case fieldNames of
[]
-> k ( proxy# @'[] )
[ f1 ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
-> k ( proxy# @'[ f1 ] )
[ f1, f2 ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
, SomeSymbol @f2 _ <- someSymbolVal ( Text.unpack f2 )
-> k ( proxy# @'[ f1, f2 ] )
[ f1, f2, f3 ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
, SomeSymbol @f2 _ <- someSymbolVal ( Text.unpack f2 )
, SomeSymbol @f3 _ <- someSymbolVal ( Text.unpack f3 )
-> k ( proxy# @'[ f1, f2, f3 ] )
_ -> error $ "I haven't defined " ++ show ( length fieldNames )
{-# INLINE provePointFields #-}
-- | Reflects a list of brush fields to the type level. duplicates :: [ Text ] -> [ Text ]
-- duplicates = nub . duplicatesAcc [] []
-- Assumes the input list has no duplicate field names, where
-- but they don't have to be sorted. duplicatesAcc :: [ Text ] -> [ Text ] -> [ Text ] -> [ Text ]
reflectBrushFieldsNoDups :: [ ( Text, SomeFieldSType ) ] -> SomeBrushFields duplicatesAcc _ dups [] = dups
reflectBrushFieldsNoDups elts = duplicatesAcc seen dups ( k : kvs )
let | k `elem` seen
mkSomeSType :: SomeFieldSType -> SomeSType = duplicatesAcc seen ( k : dups ) kvs
mkSomeSType (SomeFieldSType @a) = SomeSType @a | otherwise
mkField :: SomeFieldSType -> FieldSType Any = duplicatesAcc ( k : seen ) dups kvs
mkField (SomeFieldSType @a) = unsafeCoerce $ FieldSType @a
in
proveSomeSTypes (map (second mkSomeSType) elts) \ ( _ :: Proxy# kvs ) ->
let
dictsRec :: Record FieldSType kvs
dictsRec = MkR (HashMap.fromList $ map (second mkField) elts)
showDicts :: Record (Dict Show) kvs
showDicts = Rec.map ( \ ( ( FieldSType @a ) ) -> Dict @Show @a ) dictsRec
nfDataDicts :: Record (Dict NFData) kvs
nfDataDicts = Rec.map ( \ ( ( FieldSType @a ) ) -> Dict @NFData @a ) dictsRec
serialisableDicts :: Record (Dict Serialisable) kvs
serialisableDicts = Rec.map ( \ ( ( FieldSType @a ) ) -> Dict @Serialisable @a ) dictsRec
interpolatableDicts :: Record (Dict Interpolatable) kvs
interpolatableDicts = Rec.map ( \ ( ( FieldSType @a ) ) -> Dict @Interpolatable @a ) dictsRec
in
proveRecordDicts @Show showDicts $
proveRecordDicts @NFData nfDataDicts $
proveRecordDicts @Serialisable serialisableDicts $
proveRecordDicts @Interpolatable interpolatableDicts $
SomeBrushFields @kvs

View file

@ -1,101 +0,0 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.DSL.Interpolation
( Interpolatable(..)
, D(..), DRec
)
where
-- base
import Data.Kind
( Type )
import Data.Monoid
( Sum )
import GHC.TypeLits
( Symbol )
-- acts
import Data.Act
( Act(..), Torsor(..) )
-- groups
import Data.Group
( Group(..) )
-- MetaBrush
import Math.Module
( Module(..) )
import Math.Vector2D
( Point2D, Vector2D )
import MetaBrush.Records
( Record, AllFields, I(..) )
import qualified MetaBrush.Records as Rec
( cpure, cmap, czipWith )
--------------------------------------------------------------------------------
class ( Module Double ( Diff a ), Torsor ( Diff a ) a ) => Interpolatable a where
type Diff a = ( d :: Type ) | d -> a
instance ( a ~ Double ) => Interpolatable ( Point2D a ) where
type Diff ( Point2D a ) = Vector2D a
instance Interpolatable Double where
type Diff Double = Sum Double
--------------------------------------------------------------------------------
-- Linear/affine action for records.
type DRec :: [ ( Symbol, Type ) ] -> Type
type DRec = Record D
newtype D a = D { getDiff :: Diff a }
deriving newtype instance Semigroup (Diff a) => Semigroup (D a)
deriving newtype instance Monoid (Diff a) => Monoid (D a)
deriving newtype instance Group (Diff a) => Group (D a)
instance Interpolatable a => Act (D a) (I a) where
act (D d) (I a) = I (act d a)
instance Interpolatable a => Torsor (D a) (I a) where
I a --> I b = D (a --> b)
instance Interpolatable a => Module Double (D a) where
origin = D origin
D a ^+^ D b = D (a ^+^ b)
d *^ D a = D (d *^ a)
instance AllFields Interpolatable kvs
=> Semigroup (Record D kvs) where
(<>) = Rec.czipWith @Interpolatable (<>)
instance AllFields Interpolatable kvs
=> Monoid (Record D kvs) where
mempty = Rec.cpure @Interpolatable mempty
instance AllFields Interpolatable kvs
=> Group (Record D kvs) where
invert = Rec.cmap @Interpolatable invert
instance AllFields Interpolatable kvs
=> Act (Record D kvs) (Record I kvs) where
act = Rec.czipWith @Interpolatable act
instance AllFields Interpolatable kvs
=> Torsor (Record D kvs) (Record I kvs) where
(-->) = Rec.czipWith @Interpolatable (-->)
instance AllFields Interpolatable kvs
=> Module Double (Record D kvs) where
origin = Rec.cpure @Interpolatable origin
(^+^) = Rec.czipWith @Interpolatable (^+^)
d *^ r = Rec.cmap @Interpolatable (d *^) r
instance AllFields Interpolatable kvs
=> Interpolatable (Record I kvs) where
type Diff (Record I kvs) = Record D kvs

View file

@ -1,129 +0,0 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.DSL.Types
( STypeI, STypesI
, SomeSType(..)
, eqTys
, someSTypes, proveSomeSTypes
) where
-- base
import Data.Kind
( Constraint, Type )
import Data.List
( intercalate )
import Data.Proxy
( Proxy(..) )
import Data.Typeable
( Typeable, eqT )
import Data.Type.Equality
( (:~:)(Refl) )
import GHC.Exts
( Proxy#, proxy# )
import GHC.TypeLits
( Symbol, KnownSymbol, SomeSymbol(..)
, symbolVal', sameSymbol, someSymbolVal )
-- text
import Data.Text
( Text )
import qualified Data.Text as Text
( pack, unpack )
-- MetaBrush
import MetaBrush.DSL.Interpolation
( Interpolatable )
--------------------------------------------------------------------------------
-- Types.
class ( Typeable ty, Interpolatable ty ) => STypeI ty where
instance ( Typeable ty, Interpolatable ty ) => STypeI ty where
type STypes :: [ (Symbol, Type) ] -> Type
data STypes kvs where
STyNil :: STypes '[]
STyCons :: ( kv ~ '( k, v ), KnownSymbol k, STypeI v, STypesI kvs ) => STypes ( kv ': kvs )
instance Show ( STypes kvs ) where
show sTypes = "'[" <> intercalate "," ( showSTypes sTypes ) <> "]"
showSTypes :: STypes kvs -> [ String ]
showSTypes STyNil = []
showSTypes sTyCons@STyCons
| ( _ :: STypes ( '( k, v ) ': tail_kvs ) ) <- sTyCons
= ( symbolVal' ( proxy# :: Proxy# k ) <> " := " <> show( Proxy @v ) ) : showSTypes ( sTypesI @tail_kvs )
type STypesI :: [ (Symbol, Type) ] -> Constraint
class STypesI kvs where
sTypesI :: STypes kvs
instance STypesI '[] where
sTypesI = STyNil
-- Warning: this instance is somewhat overly general as it doesn't check for lack of duplicates
instance ( kv ~ '( k, v ), KnownSymbol k, STypeI v, STypesI kvs ) => STypesI ( kv ': kvs ) where
sTypesI = STyCons
eqSTys :: STypes as -> STypes bs -> Maybe ( as :~: bs )
eqSTys STyNil STyNil = Just Refl
eqSTys sTyCons1@STyCons sTyCons2@STyCons
| ( _ :: STypes ( '( l1, v1 ) ': as' ) ) <- sTyCons1
, ( _ :: STypes ( '( l2, v2 ) ': bs' ) ) <- sTyCons2
, Just Refl <- sameSymbol ( Proxy :: Proxy l1 ) ( Proxy :: Proxy l2 )
, Just Refl <- eqT @v1 @v2
, Just Refl <- eqTys @as' @bs'
= Just Refl
eqSTys _ _ = Nothing
eqTys :: forall as bs. ( STypesI as, STypesI bs ) => Maybe ( as :~: bs )
eqTys = eqSTys ( sTypesI @as ) ( sTypesI @bs )
data SomeSType where
SomeSType :: forall a. STypeI a => SomeSType
instance Show SomeSType where
show ( SomeSType @a ) = show ( Proxy @a )
instance Eq SomeSType where
( SomeSType @a ) == ( SomeSType @b ) =
case eqT @a @b of
Just _ -> True
_ -> False
data SomeSTypes where
SomeSTypes :: forall kvs. STypesI kvs => SomeSTypes
someSTypes :: forall kvs. STypesI kvs => [ ( Text, SomeSType ) ]
someSTypes = go ( sTypesI @kvs )
where
go :: forall lvs. STypes lvs -> [ ( Text, SomeSType ) ]
go STyNil = []
go sTyCons@STyCons
| ( _ :: STypes ( '( l, v ) ': lvs' ) ) <- sTyCons
, let
l :: Text
l = Text.pack $ symbolVal' ( proxy# :: Proxy# l )
= ( l, SomeSType @v )
: go ( sTypesI @lvs' )
proveSomeSTypes :: [ ( Text, SomeSType ) ] -> ( forall kvs. STypesI kvs => Proxy# kvs -> r ) -> r
proveSomeSTypes rs f = case go rs of { SomeSTypes @kvs -> f @kvs proxy# }
where
go :: [ ( Text, SomeSType ) ] -> SomeSTypes
go [] = SomeSTypes @'[]
go ( ( s, SomeSType @v ) :rest )
= case go rest of
SomeSTypes @kvs
| SomeSymbol ( _ :: Proxy k ) <- someSymbolVal ( Text.unpack s )
-> SomeSTypes @( '( k, v ) ': kvs )

View file

@ -1,24 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
@ -45,8 +25,12 @@ import Data.Functor.Identity
( Identity(..) ) ( Identity(..) )
import Data.Semigroup import Data.Semigroup
( Arg(..), Min(..), ArgMin ) ( Arg(..), Min(..), ArgMin )
import Data.Typeable
( Typeable )
import GHC.Generics import GHC.Generics
( Generic, Generic1 ) ( Generic, Generic1 )
import GHC.TypeLits
( Symbol )
-- acts -- acts
import Data.Act import Data.Act
@ -103,18 +87,11 @@ import Math.Module
, Inner((^.^)) , Inner((^.^))
, squaredNorm, quadrance , squaredNorm, quadrance
) )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..) )
import MetaBrush.Brush import MetaBrush.Brush
( Brush ) ( Brush, PointFields )
import MetaBrush.Serialisable
( Serialisable(..) )
import MetaBrush.DSL.Types
( STypesI )
import MetaBrush.DSL.Interpolation
( Interpolatable(..) )
import MetaBrush.Records import MetaBrush.Records
( Rec, AllFields )
import MetaBrush.Unique import MetaBrush.Unique
( UniqueSupply, Unique, freshUnique ) ( UniqueSupply, Unique, freshUnique )
@ -189,12 +166,10 @@ type StrokeSpline clo brushParams =
data Stroke where data Stroke where
Stroke Stroke
:: ( KnownSplineType clo :: forall clo pointParams ( pointFields :: [ Symbol ] ) ( brushFields :: [ Symbol ] )
, pointParams ~ Rec pointFields . ( KnownSplineType clo
, STypesI pointFields, STypesI brushFields , pointParams ~ Record pointFields
, Show pointParams, NFData pointParams , PointFields pointFields, Typeable pointFields
, AllFields Interpolatable pointFields
, Serialisable pointParams
) )
=> =>
{ strokeName :: !Text { strokeName :: !Text
@ -216,12 +191,10 @@ instance NFData Stroke where
_strokeSpline _strokeSpline
:: forall f :: forall f
. Functor f . Functor f
=> ( forall clo pointParams pointFields => ( forall clo pointParams ( pointFields :: [ Symbol ] )
. ( KnownSplineType clo . ( KnownSplineType clo
, Show pointParams, NFData pointParams , pointParams ~ Record pointFields
, AllFields Interpolatable pointFields , PointFields pointFields
, pointParams ~ Rec pointFields, STypesI pointFields
, Serialisable pointParams
) )
=> StrokeSpline clo pointParams => StrokeSpline clo pointParams
-> f ( StrokeSpline clo pointParams ) -> f ( StrokeSpline clo pointParams )
@ -231,12 +204,10 @@ _strokeSpline f ( Stroke { strokeSpline = oldStrokeSpline, .. } )
= ( \ newSpline -> Stroke { strokeSpline = newSpline, .. } ) <$> f oldStrokeSpline = ( \ newSpline -> Stroke { strokeSpline = newSpline, .. } ) <$> f oldStrokeSpline
overStrokeSpline overStrokeSpline
:: ( forall clo pointParams pointFields :: ( forall clo pointParams ( pointFields :: [ Symbol ] )
. ( KnownSplineType clo . ( KnownSplineType clo
, Show pointParams, NFData pointParams , pointParams ~ Record pointFields
, AllFields Interpolatable pointFields , PointFields pointFields
, pointParams ~ Rec pointFields, STypesI pointFields
, Serialisable pointParams
) )
=> StrokeSpline clo pointParams => StrokeSpline clo pointParams
-> StrokeSpline clo pointParams -> StrokeSpline clo pointParams
@ -373,7 +344,7 @@ instance Module Double diffBrushParams => Monoid ( DiffPointData diffBrushParams
mempty = DiffPointData mempty origin mempty mempty = DiffPointData mempty origin mempty
instance Module Double diffBrushParams => Group ( DiffPointData diffBrushParams ) where instance Module Double diffBrushParams => Group ( DiffPointData diffBrushParams ) where
invert ( DiffPointData v1 p1 s1 ) = invert ( DiffPointData v1 p1 s1 ) =
DiffPointData ( invert v1 ) ( (-1) *^ p1 ) ( invert s1 ) DiffPointData ( invert v1 ) ( -1 *^ p1 ) ( invert s1 )
instance ( Module Double diffBrushParams, Act diffBrushParams brushParams ) instance ( Module Double diffBrushParams, Act diffBrushParams brushParams )
=> Act ( DiffPointData diffBrushParams ) ( PointData brushParams ) where => Act ( DiffPointData diffBrushParams ) ( PointData brushParams ) where

View file

@ -1,15 +1,4 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module MetaBrush.Document.Draw module MetaBrush.Document.Draw
( DrawAnchor(..), anchorsAreComplementary ( DrawAnchor(..), anchorsAreComplementary
@ -25,6 +14,8 @@ import Data.Functor
( ($>) ) ( ($>) )
import Data.Semigroup import Data.Semigroup
( First(..) ) ( First(..) )
import GHC.TypeLits
( Symbol )
-- acts -- acts
import Data.Act import Data.Act
@ -34,10 +25,6 @@ import Data.Act
import Data.Sequence import Data.Sequence
( Seq(..) ) ( Seq(..) )
-- deepseq
import Control.DeepSeq
( NFData )
-- generic-lens -- generic-lens
import Data.Generics.Product.Fields import Data.Generics.Product.Fields
( field, field' ) ( field, field' )
@ -70,12 +57,12 @@ import Math.Bezier.Spline
) )
import Math.Module import Math.Module
( squaredNorm ) ( squaredNorm )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..), (..) )
import MetaBrush.Assert import MetaBrush.Assert
( assert ) ( assert )
import MetaBrush.Brush import MetaBrush.Brush
( Brush(..) ) ( Brush(..), PointFields )
import MetaBrush.Document import MetaBrush.Document
( Document(..), DocumentContent(..) ( Document(..), DocumentContent(..)
, Stroke(..), StrokeHierarchy(..), StrokeSpline , Stroke(..), StrokeHierarchy(..), StrokeSpline
@ -83,16 +70,7 @@ import MetaBrush.Document
, _selection, _strokeSpline , _selection, _strokeSpline
, coords, overStrokeSpline , coords, overStrokeSpline
) )
import MetaBrush.Serialisable
( Serialisable )
import MetaBrush.DSL.Types
( STypesI )
import MetaBrush.DSL.Interpolation
( Interpolatable )
import MetaBrush.Records import MetaBrush.Records
( Rec, AllFields )
import qualified MetaBrush.Records as Rec
( empty )
import MetaBrush.Unique import MetaBrush.Unique
( Unique, UniqueSupply, freshUnique, uniqueText ) ( Unique, UniqueSupply, freshUnique, uniqueText )
@ -132,9 +110,9 @@ getOrCreateDrawAnchor uniqueSupply c doc@( Document { zoomFactor } ) =
( newDoc, Nothing ) -> do ( newDoc, Nothing ) -> do
uniq <- runReaderT freshUnique uniqueSupply uniq <- runReaderT freshUnique uniqueSupply
let let
newSpline :: StrokeSpline Open ( Rec '[] ) newSpline :: StrokeSpline Open ( Record ( '[] :: [ Symbol ] ) )
newSpline = newSpline =
Spline { splineStart = PointData c Normal Rec.empty Spline { splineStart = PointData c Normal ( MkR 0 )
, splineCurves = OpenCurves Empty , splineCurves = OpenCurves Empty
} }
newStroke :: Stroke newStroke :: Stroke
@ -144,7 +122,7 @@ getOrCreateDrawAnchor uniqueSupply c doc@( Document { zoomFactor } ) =
, strokeVisible = True , strokeVisible = True
, strokeUnique = uniq , strokeUnique = uniq
, strokeSpline = newSpline , strokeSpline = newSpline
, strokeBrush = Nothing :: Maybe ( Brush '[] ) , strokeBrush = Nothing :: Maybe ( Brush ( '[] :: [ Symbol ] ) )
} }
newDoc' :: Document newDoc' :: Document
newDoc' newDoc'
@ -251,14 +229,11 @@ withAnchorBrushData
:: forall r :: forall r
. DrawAnchor . DrawAnchor
-> Document -> Document
-> ( forall pointParams pointFields brushFields -> ( forall pointParams ( pointFields :: [ Symbol ] ) ( brushFields :: [ Symbol ] )
. ( pointParams ~ Rec pointFields . ( pointParams ~ Record pointFields
, STypesI pointFields, STypesI brushFields , PointFields pointFields
, Show pointParams, NFData pointParams
, Serialisable pointParams
, AllFields Interpolatable pointFields
) )
=> Maybe (Brush brushFields) => Maybe ( Brush brushFields )
-> pointParams -> pointParams
-> r -> r
) )
@ -283,4 +258,4 @@ withAnchorBrushData anchor ( Document { documentContent = Content { strokes } }
AnchorAtStart {} -> f strokeBrush ( brushParams ( splineStart strokeSpline ) ) AnchorAtStart {} -> f strokeBrush ( brushParams ( splineStart strokeSpline ) )
AnchorAtEnd {} -> f strokeBrush ( brushParams ( splineEnd strokeSpline ) ) AnchorAtEnd {} -> f strokeBrush ( brushParams ( splineEnd strokeSpline ) )
splineAnchor _ splineAnchor _
= f (Nothing :: Maybe (Brush '[])) Rec.empty = f @_ @'[] @'[] Nothing ( MkR 0 )

View file

@ -1,13 +1,3 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.Document.History module MetaBrush.Document.History
( DocumentHistory(..) ( DocumentHistory(..)
, back, fwd, newHistory, newFutureStep , back, fwd, newHistory, newFutureStep

View file

@ -1,19 +1,5 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.Document.Serialise module MetaBrush.Document.Serialise
( documentToJSON, documentFromJSON ( documentToJSON, documentFromJSON
@ -28,12 +14,12 @@ import qualified Data.Bifunctor as Bifunctor
( first ) ( first )
import Data.Functor.Identity import Data.Functor.Identity
( Identity(..) ) ( Identity(..) )
import Data.Typeable
( eqT )
import Data.Type.Equality
( (:~:)(Refl) )
import Data.Version import Data.Version
( Version(versionBranch) ) ( Version(versionBranch) )
import GHC.Exts
( Proxy# )
import GHC.TypeLits
( Symbol )
import Unsafe.Coerce import Unsafe.Coerce
( unsafeCoerce ) -- Tony Morris special ( unsafeCoerce ) -- Tony Morris special
@ -89,12 +75,12 @@ import qualified Waargonaut.Decode as JSON
import qualified Waargonaut.Decode.Error as JSON import qualified Waargonaut.Decode.Error as JSON
( DecodeError(ParseFailed) ) ( DecodeError(ParseFailed) )
import qualified Waargonaut.Decode as JSON.Decoder import qualified Waargonaut.Decode as JSON.Decoder
( atKey, atKeyOptional, bool, objectAsKeyValues, text ) ( atKey, atKeyOptional, bool, text, list )
import qualified Waargonaut.Encode as JSON import qualified Waargonaut.Encode as JSON
( Encoder ) ( Encoder )
import qualified Waargonaut.Encode as JSON.Encoder import qualified Waargonaut.Encode as JSON.Encoder
( runEncoder ( runEncoder
, atKey', bool, int, keyValueTupleFoldable, list, mapLikeObj, text , atKey', bool, int, list, mapLikeObj, text
) )
import qualified Waargonaut.Encode.Builder as JSON.Builder import qualified Waargonaut.Encode.Builder as JSON.Builder
( waargonautBuilder, bsBuilder ) ( waargonautBuilder, bsBuilder )
@ -118,22 +104,17 @@ import qualified Waargonaut.Types.Whitespace as JSON
-- metabrushes -- metabrushes
import Math.Bezier.Spline import Math.Bezier.Spline
( SplineType(..), SSplineType(..), SplineTypeI(..) ) ( SplineType(..), SSplineType(..), SplineTypeI(..) )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..)) ( Point2D(..), Vector2D(..))
import MetaBrush.Asset.Brushes import MetaBrush.Asset.Brushes
( lookupBrush ) ( lookupBrush )
import MetaBrush.Brush import MetaBrush.Brush
( Brush(..), SomeBrush(..) ( Brush(..), SomeBrush(..), provePointFields, duplicates )
, SomeFieldSType(..), SomeBrushFields(..)
, reflectBrushFieldsNoDups
)
import MetaBrush.Document import MetaBrush.Document
( Document(..), DocumentContent(..), Guide(..) ( Document(..), DocumentContent(..), Guide(..)
, Stroke(..), StrokeHierarchy(..), StrokeSpline , Stroke(..), StrokeHierarchy(..), StrokeSpline
, PointData(..), FocusState(..) , PointData(..), FocusState(..)
) )
import MetaBrush.DSL.Types
( SomeSType(..), someSTypes )
import MetaBrush.Serialisable import MetaBrush.Serialisable
( Serialisable(..) ( Serialisable(..)
, encodeSequence, decodeSequence , encodeSequence, decodeSequence
@ -141,7 +122,7 @@ import MetaBrush.Serialisable
, encodeSpline, decodeSpline , encodeSpline, decodeSpline
) )
import MetaBrush.Records import MetaBrush.Records
( Rec ) ( Record, knownSymbols )
import MetaBrush.Unique import MetaBrush.Unique
( UniqueSupply, freshUnique ) ( UniqueSupply, freshUnique )
@ -203,21 +184,21 @@ loadDocument uniqueSupply fp = do
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
encodePointData encodePointData
:: forall f flds brushParams :: forall f ( flds :: [ Symbol ] ) brushParams
. ( Applicative f . ( Applicative f
, brushParams ~ Rec flds , brushParams ~ Record flds
, Serialisable ( Rec flds ) , Serialisable ( Record flds )
) )
=> JSON.Encoder f ( PointData brushParams ) => JSON.Encoder f ( PointData brushParams )
encodePointData = JSON.Encoder.mapLikeObj \ ( PointData { pointCoords, brushParams } ) -> encodePointData = JSON.Encoder.mapLikeObj \ ( PointData { pointCoords, brushParams } ) ->
JSON.Encoder.atKey' "coords" ( encoder @( Point2D Double ) ) pointCoords JSON.Encoder.atKey' "coords" ( encoder @( Point2D Double ) ) pointCoords
. JSON.Encoder.atKey' "brushParams" ( encoder @( Rec flds ) ) brushParams . JSON.Encoder.atKey' "brushParams" ( encoder @( Record flds ) ) brushParams
decodePointData decodePointData
:: forall m flds brushParams :: forall m ( flds :: [ Symbol ] ) brushParams
. ( Monad m . ( Monad m
, brushParams ~ Rec flds , brushParams ~ Record flds
, Serialisable ( Rec flds ) , Serialisable ( Record flds )
) )
=> JSON.Decoder m ( PointData brushParams ) => JSON.Decoder m ( PointData brushParams )
decodePointData = do decodePointData = do
@ -225,50 +206,22 @@ decodePointData = do
let let
pointState :: FocusState pointState :: FocusState
pointState = Normal pointState = Normal
brushParams <- JSON.Decoder.atKey "brushParams" ( decoder @( Rec flds ) ) brushParams <- JSON.Decoder.atKey "brushParams" ( decoder @( Record flds ) )
pure ( PointData { pointCoords, pointState, brushParams } ) pure ( PointData { pointCoords, pointState, brushParams } )
encodeFields :: Monad f => JSON.Encoder f [ Text ]
encodeFields = JSON.Encoder.list JSON.Encoder.text
encodeSomeSType :: Applicative f => JSON.Encoder f SomeSType decodeFields :: Monad m => JSON.Decoder m [ Text ]
encodeSomeSType = JSON.Encoder.mapLikeObj \ ( SomeSType @ty ) -> decodeFields = do
if fields <- JSON.Decoder.list JSON.Decoder.text
| Just Refl <- eqT @ty @Double case duplicates fields of
-> JSON.Encoder.atKey' "tag" JSON.Encoder.text "double" [] -> pure fields
| otherwise
-> error "SLD TODO" --( JSON.ParseFailed $ "Unsupported record field type (not double)" )
decodeSomeFieldSType :: Monad m => JSON.Decoder m SomeFieldSType
decodeSomeFieldSType = do
tag <- JSON.Decoder.atKey "tag" JSON.Decoder.text
case tag of
"double" -> pure ( SomeFieldSType @Double )
_ -> throwError ( JSON.ParseFailed $ "Unsupported record field type with tag " <> tag )
encodeFieldTypes :: Monad f => JSON.Encoder f [ ( Text, SomeSType ) ]
encodeFieldTypes = JSON.Encoder.keyValueTupleFoldable encodeSomeSType
decodeFieldTypes :: Monad m => JSON.Decoder m SomeBrushFields
decodeFieldTypes = do
fields <- JSON.Decoder.objectAsKeyValues JSON.Decoder.text decodeSomeFieldSType
let
duplicates :: [ Text ]
duplicates = duplicatesAcc [] [] fields
duplicatesAcc :: [ Text ] -> [ Text ] -> [ ( Text, whatever ) ] -> [ Text ]
duplicatesAcc _ dups [] = dups
duplicatesAcc seen dups ( ( k, _ ) : kvs )
| k `elem` seen
= duplicatesAcc seen ( k : dups ) kvs
| otherwise
= duplicatesAcc ( k : seen ) dups kvs
case duplicates of
[] -> pure ( reflectBrushFieldsNoDups fields )
[dup] -> throwError ( JSON.ParseFailed $ "Duplicate field name " <> dup <> " in brush record type" ) [dup] -> throwError ( JSON.ParseFailed $ "Duplicate field name " <> dup <> " in brush record type" )
dups -> throwError ( JSON.ParseFailed $ "Duplicate field names in brush record type:\n" <> Text.unwords dups ) dups -> throwError ( JSON.ParseFailed $ "Duplicate field names in brush record type:\n" <> Text.unwords dups )
encodeBrush :: Applicative f => JSON.Encoder f (Brush brushFields) encodeBrush :: Applicative f => JSON.Encoder f (Brush brushFields)
encodeBrush = JSON.Encoder.mapLikeObj encodeBrush = JSON.Encoder.mapLikeObj
\ ( BrushData { brushName } ) -> \ ( BrushData { brushName } ) ->
@ -287,7 +240,7 @@ encodeStroke = JSON.Encoder.mapLikeObj
\ ( Stroke \ ( Stroke
{ strokeName { strokeName
, strokeVisible , strokeVisible
, strokeSpline = strokeSpline :: StrokeSpline clo ( Rec pointFields ) , strokeSpline = strokeSpline :: StrokeSpline clo ( Record pointFields )
, strokeBrush , strokeBrush
} }
) -> ) ->
@ -298,41 +251,41 @@ encodeStroke = JSON.Encoder.mapLikeObj
SOpen -> False SOpen -> False
mbEncodeBrush :: JSON.MapLikeObj JSON.WS Json -> JSON.MapLikeObj JSON.WS Json mbEncodeBrush :: JSON.MapLikeObj JSON.WS Json -> JSON.MapLikeObj JSON.WS Json
mbEncodeBrush = case strokeBrush of mbEncodeBrush = case strokeBrush of
Nothing -> Nothing -> id
id Just brush -> JSON.Encoder.atKey' "brush" encodeBrush brush
Just brush ->
JSON.Encoder.atKey' "brush" encodeBrush brush
in in
JSON.Encoder.atKey' "name" JSON.Encoder.text strokeName JSON.Encoder.atKey' "name" JSON.Encoder.text strokeName
. JSON.Encoder.atKey' "visible" JSON.Encoder.bool strokeVisible . JSON.Encoder.atKey' "visible" JSON.Encoder.bool strokeVisible
. JSON.Encoder.atKey' "closed" JSON.Encoder.bool closed . JSON.Encoder.atKey' "closed" JSON.Encoder.bool closed
. JSON.Encoder.atKey' "pointFields" encodeFieldTypes ( someSTypes @pointFields ) . JSON.Encoder.atKey' "pointFields" encodeFields ( knownSymbols @pointFields )
. mbEncodeBrush . mbEncodeBrush
. JSON.Encoder.atKey' "spline" ( encodeSpline encodePointData ) strokeSpline . JSON.Encoder.atKey' "spline" ( encodeSpline encodePointData ) strokeSpline
decodeStroke :: MonadIO m => UniqueSupply -> JSON.Decoder m Stroke decodeStroke :: MonadIO m => UniqueSupply -> JSON.Decoder m Stroke
decodeStroke uniqueSupply = do decodeStroke uniqueSupply = do
strokeName <- JSON.Decoder.atKey "name" JSON.Decoder.text
strokeVisible <- JSON.Decoder.atKey "visible" JSON.Decoder.bool
strokeUnique <- lift ( liftIO . STM.atomically $ runReaderT freshUnique uniqueSupply ) strokeUnique <- lift ( liftIO . STM.atomically $ runReaderT freshUnique uniqueSupply )
strokeClosed <- JSON.Decoder.atKey "closed" JSON.Decoder.bool strokeName <- JSON.Decoder.atKey "name" JSON.Decoder.text
SomeBrushFields @pointFields <- JSON.Decoder.atKey "pointFields" decodeFieldTypes strokeVisible <- JSON.Decoder.atKey "visible" JSON.Decoder.bool
mbSomeBrush <- JSON.Decoder.atKeyOptional "brush" decodeBrush strokeClosed <- JSON.Decoder.atKey "closed" JSON.Decoder.bool
if strokeClosed mbSomeBrush <- JSON.Decoder.atKeyOptional "brush" decodeBrush
then do pointFields <- JSON.Decoder.atKey "pointFields" decodeFields
strokeSpline <- JSON.Decoder.atKey "spline" ( decodeSpline @Closed @( PointData ( Rec pointFields ) ) decodePointData ) -- decodeFields ensured there were no duplicate field names.
pure $ case mbSomeBrush of provePointFields pointFields \ ( _ :: Proxy# pointFields ) ->
Nothing -> if strokeClosed
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Nothing :: Maybe ( Brush '[] ) } then do
Just (SomeBrush brush) -> strokeSpline <- JSON.Decoder.atKey "spline" ( decodeSpline @Closed @( PointData ( Record pointFields ) ) decodePointData )
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Just brush } pure $ case mbSomeBrush of
else do Nothing ->
strokeSpline <- JSON.Decoder.atKey "spline" ( decodeSpline @Open @( PointData ( Rec pointFields ) ) decodePointData ) Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Nothing :: Maybe ( Brush '[] ) }
pure $ case mbSomeBrush of Just (SomeBrush brush) ->
Nothing -> Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Just brush }
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Nothing :: Maybe ( Brush '[] ) } else do
Just (SomeBrush brush) -> strokeSpline <- JSON.Decoder.atKey "spline" ( decodeSpline @Open @( PointData ( Record pointFields ) ) decodePointData )
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Just brush } pure $ case mbSomeBrush of
Nothing ->
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Nothing :: Maybe ( Brush '[] ) }
Just (SomeBrush brush) ->
Stroke { strokeName, strokeVisible, strokeUnique, strokeSpline, strokeBrush = Just brush }
encodeStrokeHierarchy :: Monad f => JSON.Encoder f StrokeHierarchy encodeStrokeHierarchy :: Monad f => JSON.Encoder f StrokeHierarchy

View file

@ -1,12 +1,4 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Document.SubdivideStroke module MetaBrush.Document.SubdivideStroke
( subdivide ) ( subdivide )
@ -57,15 +49,15 @@ import Math.Bezier.Stroke
( CachedStroke(..), invalidateCache ) ( CachedStroke(..), invalidateCache )
import Math.Module import Math.Module
( lerp, quadrance, closestPointOnSegment ) ( lerp, quadrance, closestPointOnSegment )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..), Segment(..) ) ( Point2D(..), Vector2D(..), Segment(..), T(..) )
import MetaBrush.Document import MetaBrush.Document
( Document(..), Stroke(..), StrokeHierarchy(..), StrokeSpline ( Document(..), Stroke(..), StrokeHierarchy(..), StrokeSpline
, PointData(..), DiffPointData(..) , PointData(..), DiffPointData(..)
, coords, _strokeSpline , coords, _strokeSpline
) )
import MetaBrush.DSL.Interpolation import MetaBrush.Records
( Interpolatable(Diff) ) ( Interpolatable )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -128,7 +120,7 @@ subdivide c doc@( Document { zoomFactor } ) =
then then
let let
subdiv :: PointData brushParams subdiv :: PointData brushParams
subdiv = lerp @( DiffPointData ( Diff brushParams ) ) t sp0 sp1 subdiv = lerp @( DiffPointData ( T brushParams ) ) t sp0 sp1
in do in do
put ( Just txt ) put ( Just txt )
pure ( LineTo ( NextPoint subdiv ) ( invalidateCache dat ) :<| LineTo ( NextPoint sp1 ) ( invalidateCache dat ) :<| Empty ) pure ( LineTo ( NextPoint subdiv ) ( invalidateCache dat ) :<| LineTo ( NextPoint sp1 ) ( invalidateCache dat ) :<| Empty )
@ -143,7 +135,7 @@ subdivide c doc@( Document { zoomFactor } ) =
Min ( Arg sqDist ( t, _ ) ) Min ( Arg sqDist ( t, _ ) )
= Quadratic.closestPoint @( Vector2D Double ) ( Quadratic.Bezier {..} ) ( invert offset c ) = Quadratic.closestPoint @( Vector2D Double ) ( Quadratic.Bezier {..} ) ( invert offset c )
in if t > 0 && t < 1 && sqDist * zoomFactor ^ ( 2 :: Int ) < 16 in if t > 0 && t < 1 && sqDist * zoomFactor ^ ( 2 :: Int ) < 16
then case Quadratic.subdivide @( DiffPointData ( Diff brushParams ) ) ( Quadratic.Bezier sp0 sp1 sp2 ) t of then case Quadratic.subdivide @( DiffPointData ( T brushParams ) ) ( Quadratic.Bezier sp0 sp1 sp2 ) t of
( Quadratic.Bezier _ q1 subdiv, Quadratic.Bezier _ r1 _ ) -> do ( Quadratic.Bezier _ q1 subdiv, Quadratic.Bezier _ r1 _ ) -> do
let let
bez_start, bez_end :: Curve Open ( CachedStroke RealWorld ) ( PointData brushParams ) bez_start, bez_end :: Curve Open ( CachedStroke RealWorld ) ( PointData brushParams )
@ -162,7 +154,7 @@ subdivide c doc@( Document { zoomFactor } ) =
Min ( Arg sqDist ( t, _ ) ) Min ( Arg sqDist ( t, _ ) )
= Cubic.closestPoint @( Vector2D Double ) ( Cubic.Bezier {..} ) ( invert offset c ) = Cubic.closestPoint @( Vector2D Double ) ( Cubic.Bezier {..} ) ( invert offset c )
in if t > 0 && t < 1 && sqDist * zoomFactor ^ ( 2 :: Int ) < 16 in if t > 0 && t < 1 && sqDist * zoomFactor ^ ( 2 :: Int ) < 16
then case Cubic.subdivide @( DiffPointData ( Diff brushParams ) ) ( Cubic.Bezier sp0 sp1 sp2 sp3 ) t of then case Cubic.subdivide @( DiffPointData ( T brushParams ) ) ( Cubic.Bezier sp0 sp1 sp2 sp3 ) t of
( Cubic.Bezier _ q1 q2 subdiv, Cubic.Bezier _ r1 r2 _ ) -> do ( Cubic.Bezier _ q1 q2 subdiv, Cubic.Bezier _ r1 r2 _ ) -> do
let let
bez_start, bez_end :: Curve Open ( CachedStroke RealWorld ) ( PointData brushParams ) bez_start, bez_end :: Curve Open ( CachedStroke RealWorld ) ( PointData brushParams )

View file

@ -1,78 +1,36 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE PolyKinds #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-} module MetaBrush.Records where
module MetaBrush.Records
( Record(..), Rec, AllFields(..)
, empty, insert, lookup, Lookup
, map, mapM
, mapMWithKey
, zipWith
, cpure, cmap, czipWith
, cpureM, cpureMWithKey
, cmapWithKey
, collapse, foldRec
, proveRecordDicts
, describeRecord
, MyIntersection(..), myIntersect
, WithParams(..)
-- * Functors
, I(..), K(..), (:*:)(..), Dict(..)
)
where
-- base -- base
import Prelude import Data.Functor
hiding ( lookup, map, mapM, zipWith ) ( (<&>) )
import Data.Coerce
( coerce )
import Data.Functor.Const
( Const(..) )
import Data.Kind import Data.Kind
( Type, Constraint ) ( Type, Constraint )
import Data.List import Data.List
( intersperse ) ( findIndex, intersperse )
import Data.Monoid
( Endo(..) )
import Data.Proxy
( Proxy(..) )
import Data.Typeable import Data.Typeable
( Typeable, TypeRep, typeRep ) ( Typeable, eqT )
import GHC.TypeLits import Data.Type.Equality
( Symbol, KnownSymbol, symbolVal' ( (:~:)(Refl) )
, TypeError, ErrorMessage(..)
)
import GHC.Exts import GHC.Exts
( Any, proxy#, withDict ) ( Word(W#), Proxy#, proxy# )
import GHC.Show import GHC.Show
( showCommaSpace ) ( showCommaSpace )
import GHC.TypeLits
( Symbol, KnownSymbol, symbolVal'
, SomeSymbol(..), someSymbolVal
)
import GHC.TypeNats
( Nat, type (+) )
import Unsafe.Coerce import Unsafe.Coerce
( unsafeCoerce ) ( unsafeCoerce )
-- acts
import Data.Act
( Act(..), Torsor(..) )
-- deepseq -- deepseq
import Control.DeepSeq import Control.DeepSeq
( NFData(..) ) ( NFData(..) )
@ -87,249 +45,217 @@ import Data.Text
import qualified Data.Text as Text import qualified Data.Text as Text
( pack, unpack ) ( pack, unpack )
-- unordered-containers -- MetaBrush
import Data.HashMap.Strict import Math.Linear
( HashMap ) import Math.Module
import qualified Data.HashMap.Strict as HashMap
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type Record :: (Type -> Type) -> [(Symbol, Type)] -> Type -- | A convenient constraint synonym for types that support interpolation.
newtype Record f kvs = MkR { recordKeyVals :: HashMap Text (f Any) } type Interpolatable :: Type -> Constraint
class ( Torsor ( T r ) r, Module Double ( T r ) ) => Interpolatable r
instance ( Torsor ( T r ) r, Module Double ( T r ) ) => Interpolatable r
empty :: Record f '[] --------------------------------------------------------------------------------
empty = MkR HashMap.empty
insert :: forall k v kvs f
. KnownSymbol k
=> f v
-> Record f kvs
-> Record f ( '(k,v) ': kvs )
insert v (MkR r) = MkR $ HashMap.insert k v' r
where
k :: Text
k = Text.pack $ symbolVal' @k proxy#
v' :: f Any
v' = unsafeCoerce v
lookup :: forall k kvs f
. KnownSymbol k
=> Record f kvs -> f (Lookup k kvs)
lookup ( MkR r ) = unsafeCoerce ( r HashMap.! k )
where
k :: Text
k = Text.pack $ symbolVal' @k proxy#
type Lookup :: Symbol -> [(Symbol, Type)] -> Type
type Lookup k kvs = LookupIn kvs k kvs
type LookupIn :: [(Symbol, Type)] -> Symbol -> [(Symbol, Type)] -> Type
type family LookupIn orig k kvs where
LookupIn _ k ( '(k, v) ': _ ) = v
LookupIn orig k ( _ ': kvs ) = LookupIn orig k kvs
LookupIn orig k _ = TypeError
( 'Text "Key '" :<>: ShowType k :<>: 'Text "' is not present in row:"
:$$: 'Text " - " :<>: ShowType orig )
type Rec :: [(Symbol, Type)] -> Type
type Rec kvs = Record I kvs
type I :: Type -> Type
newtype I a = I { unI :: a }
deriving newtype ( Semigroup, Monoid, Group, NFData )
type K :: Type -> Type -> Type
newtype K a b = K { unK :: a }
deriving newtype ( Semigroup, Monoid, Group, NFData )
type (:*:) :: (Type -> Type) -> (Type -> Type) -> Type -> Type
data (f :*: g) a = f a :*: g a
type Dict :: (Type -> Constraint) -> Type -> Type
data Dict c a where
Dict :: c a => Dict c a
type AllFields :: (Type -> Constraint) -> [(Symbol, Type)] -> Constraint
class AllFields c kvs where
recordDicts :: Record (Dict c) kvs
instance AllFields c '[] where
recordDicts = MkR HashMap.empty
instance ( c v, KnownSymbol k, AllFields c kvs ) => AllFields c ( '(k, v) ': kvs ) where
recordDicts = case recordDicts @c @kvs of
MkR kvs -> MkR $ HashMap.insert k dict kvs
where
k :: Text
k = Text.pack $ symbolVal' @k proxy#
dict :: Dict c Any
dict = unsafeCoerce ( Dict :: Dict c v )
instance AllFields Show kvs => Show (Record I kvs) where
showsPrec d = aux . collapse . cmapWithKey @Show showField
where
showField :: Show x => Text -> I x -> K ShowS x
showField k (I x) = K $ showString (Text.unpack k) . showString " = " . showsPrec 0 x
aux :: [ShowS] -> ShowS
aux fields = showParen (d >= 11)
$ showString "{"
. foldr (.) id (intersperse showCommaSpace fields)
. showString "}"
-- | A function from a given record type, with provided default values
-- that can be overridden.
type WithParams :: [ Symbol ] -> Type -> Type
data WithParams params a = data WithParams params a =
WithParams WithParams
{ defaultParams :: Rec params { defaultParams :: Record params
, withParams :: Rec params -> a , withParams :: Record params -> a
} }
instance AllFields Semigroup kvs --------------------------------------------------------------------------------
=> Semigroup (Record I kvs) where
(<>) = czipWith @Semigroup (<>)
instance ( AllFields Semigroup kvs -- | A record of 'Double' values.
, AllFields Monoid kvs ) type Record :: [ k ] -> Type
=> Monoid (Record I kvs) where newtype Record ks = MkR { recordKeyVals :: ( Length ks ) }
mempty = cpure @Monoid mempty
instance ( AllFields Semigroup kvs deriving newtype
, AllFields Monoid kvs instance Eq ( ( Length ks ) )
, AllFields Group kvs ) => Eq ( Record ks )
=> Group (Record I kvs) where deriving newtype
invert = cmap @Group ( \ (I g) -> I (invert g) ) instance Ord ( ( Length ks ) )
=> Ord ( Record ks )
deriving newtype
instance NFData ( ( Length ks ) )
=> NFData ( Record ks )
instance AllFields NFData kvs -- | Show a record, using the given type-level field names.
=> NFData ( Record I kvs ) where instance ( KnownSymbols ks, Representable ( ( Length ks ) ) )
rnf (MkR r) = HashMap.foldlWithKey' go () r => Show ( Record ks ) where
where showsPrec p ( MkR r )
dicts :: HashMap Text (Dict NFData Any) = showParen ( p >= 11 )
MkR dicts = recordDicts @NFData @kvs $ showString "{"
go :: () -> Text -> I Any -> () . foldr (.) id ( intersperse showCommaSpace fields )
go !_ k (I a) = . showString "}"
case dicts HashMap.! k of where
Dict -> rnf a fields :: [ ShowS ]
fields =
zip [ 1.. ] ( knownSymbols @ks ) <&> \ ( W# i, fld ) ->
let v = index r ( Fin i )
in showString ( Text.unpack fld ) . showString " = " . showsPrec 0 v
data MyIntersection r1 g r2 c where deriving via ( T ( ( Length ks ) ) )
MyIntersection instance Semigroup ( T ( ( Length ks ) ) )
:: forall i r1 g r2 c => Semigroup ( T ( Record ks ) )
. ( AllFields c i ) deriving via ( T ( ( Length ks ) ) )
=> { myProject :: forall f. Record f r1 -> Record (f :*: g) i instance Monoid ( T ( ( Length ks ) ) )
, myInject :: Record g i -> Record g r2 => Monoid ( T ( Record ks ) )
} deriving via ( T ( ( Length ks ) ) )
-> MyIntersection r1 g r2 c 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 ) )
myIntersect instance ( Act ( T ( ( Length ks ) ) ) ( ( Length ks ) )
:: forall c r1 g r2 , Semigroup ( T ( ( Length ks ) ) ) )
. ( AllFields c r1 ) => Act ( T ( Record ks ) ) ( Record ks ) where
=> Record g r2 T ( MkR g ) MkR a = MkR ( T g a )
-> MyIntersection r1 g r2 c instance ( Torsor ( T ( ( Length ks ) ) ) ( ( Length ks ) )
myIntersect (MkR r2) = , Group ( T ( ( Length ks ) ) ) )
proveRecordDicts @c @Any intersectionDict => Torsor ( T ( Record ks ) ) ( Record ks ) where
( MyIntersection { myProject, myInject } ) MkR g --> MkR a = T $ MkR $ unT $ g --> a
--------------------------------------------------------------------------------
type Length :: [ k ] -> Nat
type family Length xs where
Length '[] = 0
Length ( _ : xs ) = 1 + Length xs
type KnownSymbols :: [ Symbol ] -> Constraint
class KnownSymbols ks where
knownSymbols :: [ Text ]
instance KnownSymbols '[] where
knownSymbols = []
{-# INLINE knownSymbols #-}
instance ( KnownSymbol k, KnownSymbols ks ) => KnownSymbols ( k ': ks ) where
knownSymbols = Text.pack ( symbolVal' @k proxy# ) : knownSymbols @ks
{-# INLINE knownSymbols #-}
--------------------------------------------------------------------------------
-- Intersection of two records.
{-# INLINE intersect #-}
intersect :: forall r1 r2 l1 l2
. ( Typeable r1, Typeable r2
, KnownSymbols r1, KnownSymbols r2
, l1 ~ Length r1, l2 ~ Length r2
, Representable ( l1 ), Representable ( l2 )
, Interpolatable ( Record r1 )
)
=> 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 = const }
| 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 r1r2 -> Record r2 -> Record r2
inject = \ ( MkR r1r2 ) ( MkR r2 ) -> MkR $ injection ( find eqFin r2_idxs ) r1r2 r2
in Intersection { project, inject }
data Intersection r1 r2 where
Intersection
:: forall r1r2 r1 r2
. ( KnownSymbols r1r2, Representable ( ( Length r1r2 ) )
, Interpolatable ( Record r1r2 ) )
=> { project :: Record r1 -> Record r1r2
, inject :: Record r1r2 -> Record r2 -> Record r2
} -> Intersection r1 r2
{-# INLINE doIntersection #-}
doIntersection
:: forall r1 r2 l1 l2 kont
. ( KnownSymbols r1, KnownSymbols r2
, l1 ~ Length r1, l2 ~ Length r2
, Representable ( l1 ), Representable ( l2 )
)
=> ( forall r1r2 l12.
( r1r2 ~ Intersect r1 r2, l12 ~ Length r1r2
, Representable ( l12 ), Interpolatable ( l12 )
, KnownSymbols r1r2, Representable ( ( Length r1r2 ) )
)
=> Proxy# r1r2 -> Vec l12 ( Fin l1 ) -> Vec l12 ( Fin l2 ) -> kont )
-> kont
doIntersection k =
case knownSymbols @r1 `intersectLists` knownSymbols @r2 of
[ ]
| ( _ :: Proxy# r1r2 ) <- proxy# @'[ ]
, Refl <- ( unsafeCoerce Refl :: r1r2 :~: Intersect r1 r2 )
-> k @'[] proxy#
VZ
VZ
[ ( f1, W# r1_i1, W# r2_i1 ) ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
, ( _ :: Proxy# r1r2 ) <- proxy# @'[ f1 ]
, Refl <- ( unsafeCoerce Refl :: r1r2 :~: Intersect r1 r2 )
-> k @r1r2 proxy#
( VS ( Fin r1_i1 ) VZ )
( VS ( Fin r2_i1 ) VZ )
[ ( f1, W# r1_i1, W# r2_i1 )
, ( f2, W# r1_i2, W# r2_i2 ) ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
, SomeSymbol @f2 _ <- someSymbolVal ( Text.unpack f2 )
, ( _ :: Proxy# r1r2 ) <- proxy# @'[ f1, f2 ]
, Refl <- ( unsafeCoerce Refl :: r1r2 :~: Intersect r1 r2 )
-> k @r1r2 proxy#
( VS ( Fin r1_i1 ) $ VS ( Fin r1_i2 ) VZ )
( VS ( Fin r2_i1 ) $ VS ( Fin r2_i2 ) VZ )
[ ( f1, W# r1_i1, W# r2_i1 )
, ( f2, W# r1_i2, W# r2_i2 )
, ( f3, W# r1_i3, W# r2_i3 ) ]
| SomeSymbol @f1 _ <- someSymbolVal ( Text.unpack f1 )
, SomeSymbol @f2 _ <- someSymbolVal ( Text.unpack f2 )
, SomeSymbol @f3 _ <- someSymbolVal ( Text.unpack f3 )
, ( _ :: Proxy# r1r2 ) <- proxy# @'[ f1, f2, f3 ]
, Refl <- ( unsafeCoerce Refl :: r1r2 :~: Intersect r1 r2 )
-> k @r1r2 proxy#
( VS ( Fin r1_i1 ) $ VS ( Fin r1_i2 ) $ VS ( Fin r1_i3 ) VZ )
( VS ( Fin r2_i1 ) $ VS ( Fin r2_i2 ) $ VS ( Fin r2_i3 ) VZ )
other -> error $ "Intersection not defined in dimension " ++ show ( length other )
------
-- Functions for intersection.
intersectLists :: forall k. Eq k => [ k ] -> [ k ] -> [ ( k, Word, Word ) ]
intersectLists = go 1
where where
myProject :: Record f r1 -> Record (f :*: g) Any go :: Word -> [ k ] -> [ k ] -> [ ( k, Word, Word ) ]
myProject (MkR r1) = MkR (HashMap.intersectionWith (:*:) r1 r2) go _ [] _
myInject :: Record g Any -> Record g r2 = []
myInject (MkR i) = MkR (HashMap.union i r2) go i ( k : ks ) r
intersectionDict :: Record (Dict c) Any | Just j <- findIndex ( k == ) r
intersectionDict = = ( k, i, fromIntegral j + 1 ) : go ( i + 1 ) ks r
case recordDicts @c @r1 of | otherwise
MkR d -> MkR (HashMap.intersection d r2) = go ( i + 1 ) ks r
proveRecordDicts :: forall c r x. Record (Dict c) r -> (AllFields c r => x) -> x type Intersect :: [ k ] -> [ k ] -> [ k ]
proveRecordDicts = withDict @(AllFields c r) type family Intersect r1 r2 where
Intersect '[] _ = '[]
Intersect ( k ': ks ) r = DoIntersection k ks r ( Elem k r )
describeRecord :: forall kvs. AllFields Typeable kvs => [ ( Text, TypeRep ) ] type DoIntersection :: k -> [ k ] -> [ k ] -> Bool -> [ k ]
describeRecord = collapse $ cmapWithKey @Typeable describeField (recordDicts @Typeable @kvs) type family DoIntersection k ks r mb_j where
where DoIntersection _ ks r False = Intersect ks r
describeField :: forall a. Text -> Dict Typeable a -> K ( Text, TypeRep ) a DoIntersection k ks r True = k ': Intersect ks r
describeField k Dict = K ( k, typeRep ( Proxy :: Proxy a ) )
------------------------------------------------------------ type Elem :: k -> [ k ] -> Bool
-- Record combinators. type family Elem k ks where
Elem _ '[] = False
map :: ( forall x. f x -> g x ) Elem k ( k ': _ ) = True
-> Record f kvs -> Record g kvs Elem k ( _ ': ks ) = Elem k ks
map f (MkR r) = MkR $ fmap f r
mapM :: Applicative m
=> ( forall x. f x -> m ( g x ) )
-> Record f kvs -> m (Record g kvs)
mapM f (MkR r) =
MkR <$> traverse f r
mapMWithKey :: forall m kvs f g
. Applicative m
=> ( forall x. Text -> f x -> m ( g x ) )
-> Record f kvs -> m (Record g kvs)
mapMWithKey f (MkR r) =
MkR <$> HashMap.traverseWithKey f r
cpure :: forall c kvs f
. AllFields c kvs
=> ( forall x. c x => f x )
-> Record f kvs
cpure f =
MkR $ fmap (\ Dict -> f) (recordKeyVals $ recordDicts @c @kvs)
cmap :: forall c kvs f g
. AllFields c kvs
=> ( forall x. c x => f x -> g x )
-> Record f kvs
-> Record g kvs
cmap f (MkR r) =
MkR $ HashMap.intersectionWith (\ Dict x -> f x) (recordKeyVals $ recordDicts @c @kvs) r
zipWith :: forall kvs f g h
. ( forall x. f x -> g x -> h x )
-> Record f kvs
-> Record g kvs
-> Record h kvs
zipWith f (MkR r1) (MkR r2) =
MkR $ HashMap.intersectionWith (\ x y -> f x y) r1 r2
czipWith :: forall c kvs f g h
. AllFields c kvs
=> ( forall x. c x => f x -> g x -> h x )
-> Record f kvs
-> Record g kvs
-> Record h kvs
czipWith f (MkR r1) (MkR r2) =
MkR $ HashMap.intersectionWith (\ Dict (x :*: y) -> f x y) (recordKeyVals $ recordDicts @c @kvs) pairs
where
pairs :: HashMap Text ((f :*: g) Any)
pairs = HashMap.intersectionWith (\ x y -> x :*: y) r1 r2
cpureM :: forall c m kvs f
. ( Applicative m, AllFields c kvs)
=> ( forall x. c x => m (f x) )
-> m ( Record f kvs )
cpureM f = mapM (\Dict -> f) (recordDicts @c @kvs)
cpureMWithKey :: forall c m kvs f
. ( Applicative m, AllFields c kvs)
=> ( forall x. c x => Text-> m (f x) )
-> m ( Record f kvs )
cpureMWithKey f = mapMWithKey (\k Dict -> f k) (recordDicts @c @kvs)
cmapWithKey :: forall c kvs f g
. AllFields c kvs
=> (forall x. c x => Text -> f x -> g x)
-> Record f kvs
-> Record g kvs
cmapWithKey f = zipWithKey ( \ k Dict x -> f k x ) (recordDicts @c @kvs)
zipWithKey :: forall r f g h
. ( forall x. Text -> f x -> g x -> h x )
-> Record f r -> Record g r -> Record h r
zipWithKey f (MkR a) (MkR b) = MkR $
HashMap.intersectionWithKey f a b
foldRec :: forall y f r. ( forall x . f x -> y -> y ) -> Record f r -> y -> y
foldRec f r = coerce $ mapM g r
where
g :: ( forall x. f x -> Const (Endo y) (I x) )
g x = coerce (f x)
collapse :: Record (K a) r -> [a]
collapse (MkR a) = coerce $ HashMap.elems a

View file

@ -1,18 +1,4 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.Serialisable module MetaBrush.Serialisable
( Serialisable(..) ( Serialisable(..)
@ -31,12 +17,18 @@ import Control.Monad.ST
( RealWorld, stToIO ) ( RealWorld, stToIO )
import Data.Foldable import Data.Foldable
( toList ) ( toList )
import Data.Functor
( (<&>) )
import Data.Functor.Contravariant import Data.Functor.Contravariant
( contramap ) ( contramap )
import Data.Functor.Identity import Data.Functor.Identity
( Identity(..) ) ( Identity(..) )
import Data.STRef import Data.STRef
( newSTRef ) ( newSTRef )
import Data.Traversable
( for )
import GHC.Exts
( Word(W#) )
-- containers -- containers
import Data.Map.Strict import Data.Map.Strict
@ -78,11 +70,7 @@ import qualified Waargonaut.Decode as JSON.Decoder
import qualified Waargonaut.Encode as JSON import qualified Waargonaut.Encode as JSON
( Encoder ) ( Encoder )
import qualified Waargonaut.Encode as JSON.Encoder import qualified Waargonaut.Encode as JSON.Encoder
( runPureEncoder ( atKey', keyValueTupleFoldable, list, mapLikeObj, scientific, text, either )
, atKey', json, keyValueTupleFoldable, list, mapLikeObj, scientific, text, either
)
import Waargonaut.Types.Json
( Json )
-- meta-brushes -- meta-brushes
import Math.Bezier.Spline import Math.Bezier.Spline
@ -91,13 +79,11 @@ import Math.Bezier.Spline
) )
import Math.Bezier.Stroke import Math.Bezier.Stroke
( CachedStroke(..) ) ( CachedStroke(..) )
import MetaBrush.Records import Math.Linear
( Record, Rec, AllFields ( Point2D(..), Vector2D(..), (..)
, I(..), K(..) , Fin(..), Representable(tabulate, index)
, collapse, cmapWithKey, cpureMWithKey
) )
import Math.Vector2D import MetaBrush.Records
( Point2D(..), Vector2D(..) )
import MetaBrush.Unique import MetaBrush.Unique
( Unique ) ( Unique )
@ -121,24 +107,20 @@ instance Serialisable a => Serialisable ( Vector2D a ) where
encoder = JSON.Encoder.mapLikeObj \ ( Vector2D x y ) -> encoder = JSON.Encoder.mapLikeObj \ ( Vector2D x y ) ->
JSON.Encoder.atKey' "x" encoder x JSON.Encoder.atKey' "x" encoder x
. JSON.Encoder.atKey' "y" encoder y . JSON.Encoder.atKey' "y" encoder y
decoder = Vector2D <$> JSON.Decoder.atKey "x" decoder <*> JSON.Decoder.atKey "y" decoder decoder = Vector2D <$> JSON.Decoder.atKey "x" decoder <*> JSON.Decoder.atKey "y" decoder
instance ( KnownSymbols ks, Representable ( ( Length ks ) ) ) => Serialisable ( Record ks ) where
instance Serialisable a => Serialisable (I a) where encoder = contramap encodeFields ( JSON.Encoder.keyValueTupleFoldable ( encoder @Double ) )
encoder = contramap unI encoder
decoder = fmap I decoder
instance ( AllFields Serialisable kvs )
=> Serialisable ( Record I kvs ) where
encoder :: forall f. Monad f => JSON.Encoder f ( Rec kvs )
encoder = contramap encodeFields ( JSON.Encoder.keyValueTupleFoldable JSON.Encoder.json )
where where
encodeFields :: Record I kvs -> [ ( Text, Json ) ] encodeFields :: Record ks -> [ ( Text, Double ) ]
encodeFields = collapse . cmapWithKey @Serialisable keyVal encodeFields ( MkR r ) =
keyVal :: Serialisable x => Text -> I x -> K (Text, Json) x zip [1..] ( knownSymbols @ks ) <&> \ ( W# i, fld ) ->
keyVal k (I x) = K ( k, JSON.Encoder.runPureEncoder encoder x ) ( fld, index r ( Fin i ) )
decoder :: forall m. Monad m => JSON.Decoder m ( Rec kvs ) decoder = fmap decodeFields $ for ( knownSymbols @ks ) \ k -> JSON.Decoder.atKey k ( decoder @Double )
decoder = cpureMWithKey @Serialisable ( \ k -> JSON.Decoder.atKey k decoder ) where
decodeFields :: [ Double ] -> Record ks
decodeFields coords = MkR $ tabulate \ ( Fin i# ) ->
coords !! ( fromIntegral ( W# i# ) - 1 )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View file

@ -1,13 +1,4 @@
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module MetaBrush.Unique module MetaBrush.Unique
( MonadUnique(freshUnique) ( MonadUnique(freshUnique)
@ -79,7 +70,7 @@ newtype Unique = Unique { unique :: Int64 }
deriving newtype ( Eq, Ord, Enum, Storable, NFData ) deriving newtype ( Eq, Ord, Enum, Storable, NFData )
unsafeUnique :: Word32 -> Unique unsafeUnique :: Word32 -> Unique
unsafeUnique i = Unique ( - fromIntegral i - 1 ) unsafeUnique i = Unique ( -(fromIntegral i) - 1 )
uniqueText :: Unique -> Text uniqueText :: Unique -> Text
uniqueText ( Unique i ) uniqueText ( Unique i )

View file

@ -1,11 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module MetaBrush.Util module MetaBrush.Util
( traverseMaybe ( traverseMaybe
, Exists(..) , Exists(..)

View file

@ -1,18 +1,4 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.Bezier.Cubic module Math.Bezier.Cubic
( Bezier(..) ( Bezier(..)
@ -73,8 +59,8 @@ import Math.Module
) )
import Math.Roots import Math.Roots
( realRoots, solveQuadratic ) ( realRoots, solveQuadratic )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..), T(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -91,13 +77,15 @@ data Bezier p
via Generically1 Bezier via Generically1 Bezier
deriving anyclass ( NFData, NFData1 ) deriving anyclass ( NFData, NFData1 )
deriving via Ap Bezier p
instance {-# OVERLAPPING #-} Act v p => Act v ( Bezier p )
deriving via Ap Bezier ( T b )
instance Module r ( T b ) => Module r ( T ( Bezier b ) )
instance Show p => Show (Bezier p) where instance Show p => Show (Bezier p) where
show (Bezier p1 p2 p3 p4) = show (Bezier p1 p2 p3 p4) =
show p1 ++ "--" ++ show p2 ++ "--" ++ show p3 ++ "->" ++ show p4 show p1 ++ "--" ++ show p2 ++ "--" ++ show p3 ++ "->" ++ show p4
deriving via Ap Bezier p
instance {-# OVERLAPPING #-} Act v p => Act v ( Bezier p )
-- | Degree raising: convert a quadratic Bézier curve to a cubic Bézier curve. -- | Degree raising: convert a quadratic Bézier curve to a cubic Bézier curve.
fromQuadratic :: forall v r p. ( Torsor v p, Module r v, Fractional r ) => Quadratic.Bezier p -> Bezier p fromQuadratic :: forall v r p. ( Torsor v p, Module r v, Fractional r ) => Quadratic.Bezier p -> Bezier p
fromQuadratic ( Quadratic.Bezier { p0 = q0, p1 = q1, p2 = q2 } ) = Bezier {..} fromQuadratic ( Quadratic.Bezier { p0 = q0, p1 = q1, p2 = q2 } ) = Bezier {..}
@ -114,7 +102,7 @@ bezier ( Bezier {..} ) t =
( Quadratic.bezier @v ( Quadratic.Bezier p0 p1 p2 ) t ) ( Quadratic.bezier @v ( Quadratic.Bezier p0 p1 p2 ) t )
( Quadratic.bezier @v ( Quadratic.Bezier p1 p2 p3 ) t ) ( Quadratic.bezier @v ( Quadratic.Bezier p1 p2 p3 ) t )
-- | Derivative of cubic Bézier curve. -- | Derivative of a cubic Bézier curve.
bezier' :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> v bezier' :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> v
bezier' ( Bezier {..} ) bezier' ( Bezier {..} )
= ( 3 *^ ) = ( 3 *^ )
@ -149,7 +137,7 @@ squaredCurvature bez t
-- | Signed curvature of a planar cubic Bézier curve. -- | Signed curvature of a planar cubic Bézier curve.
signedCurvature :: forall r. Floating r => Bezier ( Point2D r ) -> r -> r signedCurvature :: forall r. Floating r => Bezier ( Point2D r ) -> r -> r
signedCurvature bez t = ( g' `cross` g'' ) / norm g' signedCurvature bez t = ( g' `cross` g'' ) / norm g' ^ ( 3 :: Int )
where where
g', g'' :: Vector2D r g', g'' :: Vector2D r
g' = bezier' @( Vector2D r ) bez t g' = bezier' @( Vector2D r ) bez t

View file

@ -1,12 +1,3 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Math.Bezier.Cubic.Fit module Math.Bezier.Cubic.Fit
( FitParameters(..), FitPoint(..) ( FitParameters(..), FitPoint(..)
, fitSpline, fitPiece , fitSpline, fitPiece
@ -86,7 +77,7 @@ import Math.Module
) )
import Math.Roots import Math.Roots
( laguerre ) --, eval, derivative ) ( laguerre ) --, eval, derivative )
import Math.Vector2D import Math.Linear
( Mat22(..), Point2D(..), Vector2D(..) ) ( Mat22(..), Point2D(..), Vector2D(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -248,8 +239,8 @@ fitPiece dist_tol t_tol maxIters p tp qs r tr =
let let
-- Convert from Hermite form to Bézier form. -- Convert from Hermite form to Bézier form.
cp1, cp2 :: Point2D Double cp1, cp2 :: Point2D Double
cp1 = ( ( s1 / 3 ) *^ tp ) p cp1 = ( ( s1 / 3 ) *^ tp ) p
cp2 = ( ( (-s2) / 3 ) *^ tr ) r cp2 = ( ( -s2 / 3 ) *^ tr ) r
bez :: Cubic.Bezier ( Point2D Double ) bez :: Cubic.Bezier ( Point2D Double )
bez = Cubic.Bezier p cp1 cp2 r bez = Cubic.Bezier p cp1 cp2 r

View file

@ -1,10 +1,3 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Math.Bezier.Envelope where module Math.Bezier.Envelope where
-- acts -- acts
@ -28,7 +21,7 @@ import qualified Math.Bezier.Quadratic as Quadratic
( Bezier(..), bezier, bezier' ) ( Bezier(..), bezier, bezier' )
import Math.Module import Math.Module
( Module((^+^),(*^)), lerp, cross ) ( Module((^+^),(*^)), lerp, cross )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..), Segment(..) ) ( Point2D(..), Vector2D(..), Segment(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -61,7 +54,7 @@ validRoot r
{- {-
:seti -XNegativeLiterals -XFlexibleInstances -XRebindableSyntax :seti -XNegativeLiterals -XFlexibleInstances -XRebindableSyntax
:m Math.Vector2D Math.Bezier.Envelope :m Math.Linear Math.Bezier.Envelope
import qualified Math.Bezier.Cubic as Cubic import qualified Math.Bezier.Cubic as Cubic
import Prelude hiding ( fromInteger ) import Prelude hiding ( fromInteger )
import AlgebraicPrelude ( fromInteger ) import AlgebraicPrelude ( fromInteger )
@ -515,7 +508,7 @@ envelope31 path
( Segment b10 b11 ) ( Segment b10 b11 )
( Segment b20 b21 ) ( Segment b20 b21 )
( Segment b30 b31 ) ( Segment b30 b31 )
) t0 = [ - a1 / a0 ] ) t0 = [ -a1 / a0 ]
where where
@ -553,7 +546,7 @@ envelope21 path
( Segment b00 b01 ) ( Segment b00 b01 )
( Segment b10 b11 ) ( Segment b10 b11 )
( Segment b20 b21 ) ( Segment b20 b21 )
) t0 = [ - a1 / a0 ] ) t0 = [ -a1 / a0 ]
where where
@ -590,7 +583,7 @@ envelope11 ( Segment p0 p1 )
( Segment ( Segment
( Segment b00 b01 ) ( Segment b00 b01 )
( Segment b10 b11 ) ( Segment b10 b11 )
) t0 = [ - a1 / a0 ] ) t0 = [ -a1 / a0 ]
where where

View file

@ -1,16 +1,4 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.Bezier.Quadratic module Math.Bezier.Quadratic
( Bezier(..) ( Bezier(..)
@ -68,8 +56,8 @@ import Math.Module
) )
import Math.Roots import Math.Roots
( realRoots ) ( realRoots )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) ( Point2D(..), Vector2D(..), T(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -86,18 +74,20 @@ data Bezier p
via Generically1 Bezier via Generically1 Bezier
deriving anyclass ( NFData, NFData1 ) deriving anyclass ( NFData, NFData1 )
deriving via Ap Bezier p
instance {-# OVERLAPPING #-} Act v p => Act v ( Bezier p )
deriving via Ap Bezier ( T b )
instance Module r ( T b ) => Module r ( T ( Bezier b ) )
instance Show p => Show (Bezier p) where instance Show p => Show (Bezier p) where
show (Bezier p1 p2 p3) = show (Bezier p1 p2 p3) =
show p1 ++ "--" ++ show p2 ++ "->" ++ show p3 show p1 ++ "--" ++ show p2 ++ "->" ++ show p3
deriving via Ap Bezier p
instance {-# OVERLAPPING #-} Act v p => Act v ( Bezier p )
-- | Quadratic Bézier curve. -- | Quadratic Bézier curve.
bezier :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> p bezier :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> p
bezier ( Bezier {..} ) t = lerp @v t ( lerp @v t p0 p1 ) ( lerp @v t p1 p2 ) bezier ( Bezier {..} ) t = lerp @v t ( lerp @v t p0 p1 ) ( lerp @v t p1 p2 )
-- | Derivative of quadratic Bézier curve. -- | Derivative of a quadratic Bézier curve.
bezier' :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> v bezier' :: forall v r p. ( Torsor v p, Module r v ) => Bezier p -> r -> v
bezier' ( Bezier {..} ) t = 2 *^ lerp @v t ( p0 --> p1 ) ( p1 --> p2 ) bezier' ( Bezier {..} ) t = 2 *^ lerp @v t ( p0 --> p1 ) ( p1 --> p2 )
@ -126,7 +116,7 @@ squaredCurvature bez t
-- | Signed curvature of a planar quadratic Bézier curve. -- | Signed curvature of a planar quadratic Bézier curve.
signedCurvature :: forall r. Floating r => Bezier ( Point2D r ) -> r -> r signedCurvature :: forall r. Floating r => Bezier ( Point2D r ) -> r -> r
signedCurvature bez t = ( g' `cross` g'' ) / norm g' signedCurvature bez t = ( g' `cross` g'' ) / norm g' ^ ( 3 :: Int )
where where
g', g'' :: Vector2D r g', g'' :: Vector2D r
g' = bezier' @( Vector2D r ) bez t g' = bezier' @( Vector2D r ) bez t

View file

@ -1,26 +1,3 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.Bezier.Spline where module Math.Bezier.Spline where
-- base -- base
@ -43,7 +20,7 @@ import Data.Monoid
import Data.Semigroup import Data.Semigroup
( First(..) ) ( First(..) )
import GHC.Generics import GHC.Generics
( Generic, Generic1 ) ( Generic, Generic1, Generically1(..) )
-- bifunctors -- bifunctors
import qualified Data.Bifunctor.Tannen as Biff import qualified Data.Bifunctor.Tannen as Biff
@ -74,7 +51,7 @@ import Control.Monad.Trans.State.Strict
-- MetaBrush -- MetaBrush
import qualified Math.Bezier.Cubic as Cubic import qualified Math.Bezier.Cubic as Cubic
( Bezier(..) ) ( Bezier(..) )
import Math.Vector2D import Math.Linear
( Point2D ) ( Point2D )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -110,9 +87,13 @@ data family NextPoint ( clo :: SplineType ) ptData
newtype instance NextPoint Open ptData = NextPoint { nextPoint :: ptData } newtype instance NextPoint Open ptData = NextPoint { nextPoint :: ptData }
deriving stock ( Show, Generic, Generic1, Functor, Foldable, Traversable ) deriving stock ( Show, Generic, Generic1, Functor, Foldable, Traversable )
deriving anyclass ( NFData, NFData1 ) deriving anyclass ( NFData, NFData1 )
deriving Applicative
via ( Generically1 ( NextPoint Open ) )
data instance NextPoint Closed ptData = BackToStart data instance NextPoint Closed ptData = BackToStart
deriving stock ( Show, Generic, Generic1, Functor, Foldable, Traversable ) deriving stock ( Show, Generic, Generic1, Functor, Foldable, Traversable )
deriving anyclass ( NFData, NFData1 ) deriving anyclass ( NFData, NFData1 )
deriving Applicative
via ( Generically1 ( NextPoint Closed ) )
fromNextPoint :: forall clo ptData. SplineTypeI clo => ptData -> NextPoint clo ptData -> ptData fromNextPoint :: forall clo ptData. SplineTypeI clo => ptData -> NextPoint clo ptData -> ptData
fromNextPoint pt nxt fromNextPoint pt nxt

View file

@ -1,27 +1,20 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-} {-# OPTIONS_GHC -Wno-type-defaults #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE ViewPatterns #-}
module Math.Bezier.Stroke module Math.Bezier.Stroke
( Offset(..) ( Offset(..)
, CachedStroke(..), discardCache, invalidateCache , CachedStroke(..), discardCache, invalidateCache
, computeStrokeOutline, joinWithBrush , computeStrokeOutline, joinWithBrush
, withTangent , withTangent
-- * Brush stroking
-- $brushes
, brushStroke, envelopeEquation
, linear, bezier2, bezier3
-- , uncurryD
) )
where where
@ -119,8 +112,8 @@ import Math.Orientation
) )
import Math.Roots import Math.Roots
( solveQuadratic ) ( solveQuadratic )
import Math.Vector2D import Math.Linear
( Point2D(..), Vector2D(..) ) import Math.Linear.Dual
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -208,10 +201,10 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
endPt :: ptData endPt :: ptData
endPt = openCurveEnd lastCurve endPt = openCurveEnd lastCurve
startTgtFwd, startTgtBwd, endTgtFwd, endTgtBwd :: Vector2D Double startTgtFwd, startTgtBwd, endTgtFwd, endTgtBwd :: Vector2D Double
startTgtFwd = snd ( firstOutlineFwd 0 ) startTgtFwd = snd ( firstOutlineFwd 0 )
startTgtBwd = (-1) *^ snd ( firstOutlineBwd 1 ) startTgtBwd = -1 *^ snd ( firstOutlineBwd 1 )
endTgtFwd = snd ( lastOutlineFwd 1 ) endTgtFwd = snd ( lastOutlineFwd 1 )
endTgtBwd = (-1) *^ snd ( lastOutlineBwd 0 ) endTgtBwd = -1 *^ snd ( lastOutlineBwd 0 )
startBrush, endBrush :: SplinePts Closed startBrush, endBrush :: SplinePts Closed
startBrush = brushShape spt0 startBrush = brushShape spt0
endBrush = brushShape endPt endBrush = brushShape endPt
@ -223,11 +216,11 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
Empty -> endTangent spt0 spt0 lastCurve Empty -> endTangent spt0 spt0 lastCurve
_ :|> lastPrev -> endTangent spt0 ( openCurveEnd lastPrev ) lastCurve _ :|> lastPrev -> endTangent spt0 ( openCurveEnd lastPrev ) lastCurve
startTestTgt, endTestTgt :: Vector2D Double startTestTgt, endTestTgt :: Vector2D Double
startTestTgt = Vector2D sty (-stx) startTestTgt = Vector2D sty -stx
where where
stx, sty :: Double stx, sty :: Double
Vector2D stx sty = startTgt Vector2D stx sty = startTgt
endTestTgt = Vector2D ety (-etx) endTestTgt = Vector2D ety -etx
where where
etx, ety :: Double etx, ety :: Double
Vector2D etx ety = endTgt Vector2D etx ety = endTgt
@ -270,10 +263,10 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
endTgt = case prevCurves of endTgt = case prevCurves of
Empty -> endTangent spt0 spt0 lastCurve Empty -> endTangent spt0 spt0 lastCurve
_ :|> lastPrev -> endTangent spt0 ( openCurveEnd lastPrev ) lastCurve _ :|> lastPrev -> endTangent spt0 ( openCurveEnd lastPrev ) lastCurve
startTgtFwd = snd ( firstOutlineFwd 0 ) startTgtFwd = snd ( firstOutlineFwd 0 )
startTgtBwd = (-1) *^ snd ( firstOutlineBwd 1 ) startTgtBwd = -1 *^ snd ( firstOutlineBwd 1 )
endTgtFwd = snd ( lastOutlineFwd 1 ) endTgtFwd = snd ( lastOutlineFwd 1 )
endTgtBwd = (-1) *^ snd ( lastOutlineBwd 0 ) endTgtBwd = -1 *^ snd ( lastOutlineBwd 0 )
fwdStartCap, bwdStartCap :: SplinePts Open fwdStartCap, bwdStartCap :: SplinePts Open
TwoSided fwdStartCap bwdStartCap TwoSided fwdStartCap bwdStartCap
= fmap fst . snd . runWriter = fmap fst . snd . runWriter
@ -326,10 +319,10 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
tgt, next_tgt, tgtFwd, next_tgtFwd, tgtBwd, next_tgtBwd :: Vector2D Double tgt, next_tgt, tgtFwd, next_tgtFwd, tgtBwd, next_tgtBwd :: Vector2D Double
tgt = startTangent spt0 ptData curve tgt = startTangent spt0 ptData curve
next_tgt = endTangent spt0 ptData curve next_tgt = endTangent spt0 ptData curve
tgtFwd = snd ( fwd 0 ) tgtFwd = snd ( fwd 0 )
next_tgtFwd = snd ( fwd 1 ) next_tgtFwd = snd ( fwd 1 )
tgtBwd = (-1) *^ snd ( bwd 1 ) tgtBwd = -1 *^ snd ( bwd 1 )
next_tgtBwd = (-1) *^ snd ( bwd 0 ) next_tgtBwd = -1 *^ snd ( bwd 0 )
lift $ tellBrushJoin ( prevTgt, prev_tgtFwd, tgtBwd ) ptData ( tgt, tgtFwd, prev_tgtBwd ) lift $ tellBrushJoin ( prevTgt, prev_tgtFwd, tgtBwd ) ptData ( tgt, tgtFwd, prev_tgtBwd )
lift $ updateCurveData ( curveData curve ) fwd bwd lift $ updateCurveData ( curveData curve ) fwd bwd
put ( next_tgt, next_tgtFwd, next_tgtBwd ) put ( next_tgt, next_tgtFwd, next_tgtBwd )
@ -385,14 +378,14 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
( fwdCond, bwdCond ) ( fwdCond, bwdCond )
| prevTgt `cross` tgt < 0 && prevTgt ^.^ tgt < 0 | prevTgt `cross` tgt < 0 && prevTgt ^.^ tgt < 0
= ( isJust $ between ori prevTgtFwd tgtFwd testTgt1 = ( isJust $ between ori prevTgtFwd tgtFwd testTgt1
, isJust $ between ori prevTgtBwd tgtBwd ( (-1) *^ testTgt1 ) , isJust $ between ori prevTgtBwd tgtBwd ( -1 *^ testTgt1 )
) )
| otherwise | otherwise
= ( not . isJust $ between ori prevTgtFwd tgtFwd testTgt2 = ( not . isJust $ between ori prevTgtFwd tgtFwd testTgt2
, not . isJust $ between ori prevTgtBwd tgtBwd ( (-1) *^ testTgt2 ) , not . isJust $ between ori prevTgtBwd tgtBwd ( -1 *^ testTgt2 )
) )
testTgt1, testTgt2 :: Vector2D Double testTgt1, testTgt2 :: Vector2D Double
testTgt1 = Vector2D (-ty) tx testTgt1 = Vector2D -ty tx
where where
tx, ty :: Double tx, ty :: Double
Vector2D tx ty = tgt ^-^ prevTgt Vector2D tx ty = tgt ^-^ prevTgt
@ -400,7 +393,7 @@ computeStrokeOutline fitParams ptParams brushFn spline@( Spline { splineStart =
| prevTgt ^.^ tgt < 0 | prevTgt ^.^ tgt < 0
= testTgt1 = testTgt1
| otherwise | otherwise
= (-1) *^ ( tgt ^+^ prevTgt ) = -1 *^ ( tgt ^+^ prevTgt )
fwdJoin, bwdJoin :: SplinePts Open fwdJoin, bwdJoin :: SplinePts Open
fwdJoin fwdJoin
| tgtFwd `strictlyParallel` prevTgtFwd | tgtFwd `strictlyParallel` prevTgtFwd
@ -504,14 +497,14 @@ outlineFunctions ptParams brushFn sp0 crv =
| otherwise | otherwise
= offTgt u = offTgt u
bwd t bwd t
= ( off s --offset ( withTangent ( (-1) *^ bwd' s ) ( brush s ) ) • f s = ( off s --offset ( withTangent ( -1 *^ bwd' s ) ( brush s ) ) • f s
, bwd' s , bwd' s
) )
where where
s :: Double s :: Double
s = 1 - t s = 1 - t
off :: Double -> Point2D Double off :: Double -> Point2D Double
off u = offset ( withTangent ( (-1) *^ f' u ) ( brush u ) ) f u off u = offset ( withTangent ( -1 *^ f' u ) ( brush u ) ) f u
offTgt :: Double -> Vector2D Double offTgt :: Double -> Vector2D Double
offTgt u offTgt u
| u < 0.5 | u < 0.5
@ -521,7 +514,7 @@ outlineFunctions ptParams brushFn sp0 crv =
bwd' :: Double -> Vector2D Double bwd' :: Double -> Vector2D Double
bwd' u bwd' u
| squaredNorm ( offTgt u ) < epsilon | squaredNorm ( offTgt u ) < epsilon
= (-1) *^ f' u = -1 *^ f' u
| otherwise | otherwise
= offTgt u = offTgt u
in ( fwd, bwd ) in ( fwd, bwd )
@ -804,3 +797,94 @@ withTangent tgt_wanted spline@( Spline { splineStart } )
, offsetParameter = Just t , offsetParameter = Just t
, offset = MkVector2D $ Cubic.bezier @( Vector2D Double ) bez t , offset = MkVector2D $ Cubic.bezier @( Vector2D Double ) bez t
} }
--------------------------------------------------------------------------------
-- $brushes
--
-- You can compute the envelope equation for a brush stroke by using
-- the functions 'linear', 'bezier2' and 'bezier3' in conjunction with
-- the 'brushStroke' function, e.g.
--
-- > brushStroke ( bezier2 path ) ( uncurryD $ fmap bezier3 brush )
-- | Linear interpolation, as a differentiable function.
linear :: forall b. ( Module Double ( T b ), Torsor ( T b ) b )
=> Segment b -> 1 ~> b
linear ( Segment a b ) = D \ ( 1 t ) ->
D1 ( lerp @( T b ) t a b )
( a --> b )
origin
-- | A quadratic Bézier curve, as a differentiable function.
bezier2 :: forall b. ( Module Double ( T b ), Torsor ( T b ) b )
=> Quadratic.Bezier b -> 1 ~> b
bezier2 bez = D \ ( 1 t ) ->
D1 ( Quadratic.bezier @( T b ) bez t )
( Quadratic.bezier' bez t )
( Quadratic.bezier'' bez )
-- | A cubic Bézier curve, as a differentiable function.
bezier3 :: forall b. ( Module Double ( T b ), Torsor ( T b ) b )
=> Cubic.Bezier b -> 1 ~> b
bezier3 bez = D \ ( 1 t ) ->
D1 ( Cubic.bezier @( T b ) bez t )
( Cubic.bezier' bez t )
( Cubic.bezier'' bez t )
-- | A brush stroke, as described by the equation
--
-- \[ c(t,s) = p(t) + b(t,s) \]
--
-- where:
--
-- - \( p(t) \) is the path that the brush follows, and
-- - \( b(t,s) \) is the brush shape, as it varies along the path.
brushStroke :: 1 ~> 2 -- ^ stroke path \( p(t) \)
-> 2 ~> 2 -- ^ brush \( b(t,s) \)
-> 2 ~> 2
brushStroke ( D f_p ) ( D f_b ) = D \ ( 2 t0 s0 ) ->
let !( D1 p dpdt d2pdt2 )
= f_p ( 1 t0 )
!( D2 b dbdt dbds d2bdt2 d2bdtds d2bds2 )
= f_b ( 2 t0 s0 )
in
D2 ( unT $ T p ^+^ T b )
-- c = p + b
( dpdt ^+^ dbdt ) dbds
-- ∂c/∂t = dp/dt + ∂b/∂t
-- ∂c/∂s = ∂b/∂s
( d2pdt2 ^+^ d2bdt2 ) d2bdtds d2bds2
-- ∂²c/∂t² = d²p/dt² + ∂²b/∂t²
-- ∂²c/∂t∂s = ∂²b/∂t∂s
-- ∂²c/∂s² = ∂²b/∂s²
-- | The envelope equation
--
-- \[ E = \frac{\partial c}{\partial t} \times \frac{\partial c}{\partial s} = 0, ]
--
-- as well as the vector
--
-- \[ \frac{\partial E}{\partial s} \frac{\mathrm{d} c}{\mathrm{d} t} \]
--
-- whose roots correspond to cusps in the envelope.
envelopeEquation :: 2 ~> 2 -> Double -> Double -> (# Double, T ( 2 ) #)
envelopeEquation ( D c ) t s =
case c ( 2 t s ) of
D2 _ dcdt dcds d2cdt2 d2cdtds d2cds2 ->
let dEdt = d2cdt2 `cross2` dcds + dcdt `cross2` d2cdtds
dEds = d2cdtds `cross2` dcds + dcdt `cross2` d2cds2
in (# dcdt `cross2` dcds, dEds *^ dcdt ^-^ dEdt *^ dcds #)
-- Computation of total derivative dc/dt:
--
-- dc/dt = ∂c/∂t + ∂c/∂s ∂s/∂t
-- ∂s/∂t = - ( ∂E / ∂t ) / ( ∂E / ∂s )
--
-- ( ∂E / ∂s ) dc/dt = ( ∂E / ∂s ) ∂c/∂t - ( ∂E / ∂t ) ∂c/∂s.
-- | Cross-product of two 2D vectors.
cross2 :: T ( 2 ) -> T ( 2 ) -> Double
cross2 ( T ( 2 x1 y1 ) ) ( T ( 2 x2 y2 ) )
= x1 * y2 - x2 * y1

View file

@ -1,5 +1,3 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Math.Epsilon module Math.Epsilon
( epsilon, nearZero ) ( epsilon, nearZero )
where where

271
src/splines/Math/Linear.hs Normal file
View file

@ -0,0 +1,271 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE UnliftedNewtypes #-}
module Math.Linear
( -- * Points and vectors
Point2D(..), Vector2D(.., Vector2D), Segment(..), Mat22(..)
-- * Points and vectors (second version)
, (..), T(.., V2, V3)
, Fin(..), eqFin, MFin(..)
, Dim, Representable(..), injection, projection
, Vec(..), (!), find
) where
-- base
import Data.Kind
( Type, Constraint )
import Data.Monoid
( Sum(..) )
import GHC.Exts
( TYPE, RuntimeRep(..)
, Word#, plusWord#, minusWord#, isTrue#, eqWord#
)
import GHC.Generics
( Generic, Generic1, Generically(..), Generically1(..) )
import GHC.TypeNats
( Nat, type (+) )
-- acts
import Data.Act
( Act(()), Torsor((-->)) )
-- deepseq
import Control.DeepSeq
( NFData, NFData1 )
-- groups
import Data.Group
( Group(..) )
-- groups-generic
import Data.Group.Generics
( )
--------------------------------------------------------------------------------
data Point2D a = Point2D !a !a
deriving stock ( Eq, Generic, Generic1, Functor, Foldable, Traversable )
deriving ( Act ( Vector2D a ), Torsor ( Vector2D a ) )
via Vector2D a
deriving Applicative
via Generically1 Point2D
deriving anyclass ( NFData, NFData1 )
instance Show a => Show (Point2D a) where
showsPrec i (Point2D a b) = showsPrec i (a,b)
newtype Vector2D a = MkVector2D { tip :: Point2D a }
deriving stock ( Generic, Generic1, Foldable, Traversable )
deriving newtype ( Eq, Functor, Applicative, NFData, NFData1 )
deriving ( Semigroup, Monoid, Group )
via Generically ( Point2D ( Sum a ) )
-- | One-off datatype used for the 'Show' instance of Vector2D.
data V a = V a a
deriving stock Show
instance Show a => Show (Vector2D a) where
showsPrec i (Vector2D x y) = showsPrec i (V x y)
{-# COMPLETE Vector2D #-}
pattern Vector2D :: a -> a -> Vector2D a
pattern Vector2D x y = MkVector2D ( Point2D x y )
data Mat22 a
= Mat22 !a !a !a !a
deriving stock ( Show, Eq, Generic, Generic1, Functor, Foldable, Traversable )
deriving Applicative
via Generically1 Mat22
deriving anyclass ( NFData, NFData1 )
data Segment p =
Segment
{ segmentStart :: !p
, segmentEnd :: !p
}
deriving stock ( Generic, Generic1, Functor, Foldable, Traversable )
deriving ( Semigroup, Monoid, Group )
via Generically ( Segment p )
deriving Applicative
via Generically1 Segment
deriving anyclass ( NFData, NFData1 )
instance Show p => Show (Segment p) where
show (Segment s e) = show s ++ " -> " ++ show e
--------------------------------------------------------------------------------
-- | Euclidean space \( \mathbb{R}^n \).
type :: Nat -> Type
data family n
data instance 0 = 0
deriving stock ( Show, Eq, Ord, Generic )
deriving anyclass NFData
newtype instance 1 = 1 Double
deriving stock ( Generic )
deriving newtype ( Show, Eq, Ord, NFData )
data instance 2 = 2 {-# UNPACK #-} !Double {-# UNPACK #-} !Double
deriving stock Generic
deriving anyclass NFData
deriving stock ( Show, Eq, Ord )
data instance 3 = 3 {-# UNPACK #-} !Double {-# UNPACK #-} !Double {-# UNPACK #-} !Double
deriving stock Generic
deriving anyclass NFData
deriving stock ( Show, Eq, Ord )
-- | Tangent space to Euclidean space.
type T :: Type -> Type
newtype T e = T { unT :: e }
deriving stock ( Eq, Functor )
deriving newtype ( Show, NFData ) -- newtype Show instance for debugging...
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 )
--instance Show (T ( 2)) where
-- showsPrec i (T (2 x y)) = showsPrec i (V x y)
{-# COMPLETE V3 #-}
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
type Fin :: Nat -> TYPE WordRep
newtype Fin n = Fin Word#
{-# INLINE eqFin #-}
eqFin :: Fin n -> Fin n -> Bool
eqFin ( Fin i ) ( Fin j ) = isTrue# ( i `eqWord#` j )
-- | 0, ..., n
type MFin :: Nat -> TYPE WordRep
newtype MFin n = MFin Word#
type Dim :: k -> Nat
type family Dim v
type instance Dim ( n ) = n
type Representable :: Type -> Constraint
class Representable v where
tabulate :: ( Fin ( Dim v ) -> Double ) -> v
index :: v -> Fin ( Dim v ) -> Double
instance Representable ( 0 ) where
{-# INLINE tabulate #-}
tabulate _ = 0
{-# INLINE index #-}
index _ _ = 0
instance Representable ( 1 ) where
{-# INLINE tabulate #-}
tabulate f = 1 ( f ( Fin 1## ) )
{-# INLINE index #-}
index ( 1 x ) _ = x
instance Representable ( 2 ) where
{-# INLINE tabulate #-}
tabulate f = 2 ( f ( Fin 1## ) ) ( f ( Fin 2## ) )
{-# INLINE index #-}
index ( 2 x y ) = \ case
Fin 1## -> x
_ -> y
instance Representable ( 3 ) where
{-# INLINE tabulate #-}
tabulate f = 3 ( f ( Fin 1## ) ) ( f ( Fin 2## ) ) ( f ( Fin 3## ) )
{-# INLINE index #-}
index ( 3 x y z ) = \ case
Fin 1## -> x
Fin 2## -> y
_ -> z
{-# INLINE projection #-}
projection :: ( Representable u, Representable v )
=> ( Fin ( Dim v ) -> Fin ( Dim u ) )
-> u -> v
projection f = \ u ->
tabulate \ i -> index u ( f i )
{-# INLINE injection #-}
injection :: ( Representable u, Representable v )
=> ( Fin ( Dim v ) -> MFin ( Dim u ) )
-> u -> v -> v
injection f = \ u v ->
tabulate \ i -> case f i of
MFin 0## -> index v i
MFin j -> index u ( Fin j )
type Vec :: Nat -> TYPE WordRep -> Type
data Vec n a where
VZ :: Vec 0 a
VS :: a -> Vec n a -> Vec ( 1 + n ) a
infixl 9 !
(!) :: forall l a. Vec l a -> Fin l -> a
VS a _ ! Fin 1## = a
VS _ a ! Fin i = a ! Fin ( i `minusWord#` 1## )
_ ! _ = error "impossible: Fin 0 is uninhabited"
find :: forall l a. ( a -> a -> Bool ) -> Vec l a -> a -> MFin l
find eq v b = MFin ( go 1## v )
where
go :: Word# -> Vec n a -> Word#
go j ( VS a as )
| a `eq` b
= j
| otherwise
= go ( j `plusWord#` 1## ) as
go _ VZ = 0##

View file

@ -0,0 +1,271 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
module Math.Linear.Dual where
-- base
import Control.Applicative
( liftA2 )
import Data.Kind
( Type, Constraint )
import GHC.Generics
( Generic, Generic1, Generically1(..) )
import GHC.TypeNats
( Nat )
-- MetaBrush
import Math.Module
import Math.Linear
--------------------------------------------------------------------------------
-- | Differentiable mappings between spaces.
infixr 0 ~>
type (~>) :: Type -> Type -> Type
newtype a ~> b = D { runD :: a -> D a b }
deriving stock instance Functor ( D a ) => Functor ( (~>) a )
-- | @D ( n ) v@ is \( \mathbb{R}[x_1, \ldots, x_n]/(x_1, \ldots, x_n)^3 \otimes_\mathbb{R} v \)
type D :: Type -> Type -> Type
data family D u v
newtype instance D ( 0 ) v = D0 { v :: v }
deriving stock ( Show, Eq, Functor, Generic, Generic1 )
deriving newtype ( Num, Fractional, Floating )
deriving Applicative
via Generically1 ( D ( 0 ) )
data instance D ( 1 ) v = D1 { v :: !v, dx :: !( T v ), ddx :: !( T v ) }
deriving stock ( Show, Eq, Functor, Generic, Generic1 )
deriving Applicative
via Generically1 ( D ( 1 ) )
data instance D ( 2 ) v = D2 { v :: !v, dx, dy :: !( T v ), ddx, dxdy, ddy :: !( T v ) }
deriving stock ( Show, Eq, Functor, Generic, Generic1 )
deriving Applicative
via Generically1 ( D ( 2 ) )
data instance D ( 3 ) v = D3 { v :: !v, dx, dy, dz :: !( T v ), ddx, dxdy, ddy, dxdz, dydz, ddz :: !( T v ) }
deriving stock ( Show, Eq, Functor, Generic, Generic1 )
deriving Applicative
via Generically1 ( D ( 3 ) )
instance Num ( D ( 1 ) Double ) where
(+) = liftA2 (+)
(-) = liftA2 (-)
negate = fmap negate
fromInteger = konst . fromInteger
abs = error "no"
signum = error "no"
D1 v1 ( T dx1 ) ( T ddx1 )
* D1 v2 ( T dx2 ) ( T ddx2 )
= D1 ( v1 * v2 )
( T $ dx1 * v2 + v1 * dx2 )
( T $ dx1 * dx2 + v1 * ddx2 + ddx1 * v2 )
instance Num ( D ( 2 ) Double ) where
(+) = liftA2 (+)
(-) = liftA2 (-)
negate = fmap negate
fromInteger = konst . fromInteger
abs = error "no"
signum = error "no"
D2 v1 ( T dx1 ) ( T dy1 ) ( T ddx1 ) ( T dxdy1 ) ( T ddy1 )
* D2 v2 ( T dx2 ) ( T dy2 ) ( T ddx2 ) ( T dxdy2 ) ( T ddy2 )
= D2 ( v1 * v2 )
( T $ dx1 * v2 + v1 * dx2 )
( T $ dy1 * v2 + v1 * dy2 )
( T $ dx1 * dx2 + v1 * ddx2 + ddx1 * v2 )
( T $ dy1 * dx2 + dx1 * dy2 + v1 * dxdy2 + dxdy1 * v2 )
( T $ dy1 * dy2 + v1 * ddy2 + ddy1 * v2 )
instance Num ( D ( 3 ) Double ) where
(+) = liftA2 (+)
(-) = liftA2 (-)
negate = fmap negate
fromInteger = konst . fromInteger
abs = error "no"
signum = error "no"
D3 v1 ( T dx1 ) ( T dy1 ) ( T dz1 ) ( T ddx1 ) ( T dxdy1 ) ( T ddy1 ) ( T dxdz1 ) ( T dydz1 ) ( T ddz1 )
* D3 v2 ( T dx2 ) ( T dy2 ) ( T dz2 ) ( T ddx2 ) ( T dxdy2 ) ( T ddy2 ) ( T dxdz2 ) ( T dydz2 ) ( T ddz2 )
= D3 ( v1 * v2 )
( T $ dx1 * v2 + v1 * dx2 )
( T $ dy1 * v2 + v1 * dy2 )
( T $ dz1 * v2 + v1 * dz2 )
( T $ dx1 * dx2 + ddx2 * v1 + ddx1 * v2 )
( T $ dy1 * dx2 + dx1 * dy2 + v1 * dxdy2 + dxdy1 * v2 )
( T $ dy1 * dy2 + v1 * ddy2 + ddy1 * v2 )
( T $ dz1 * dx2 + dx1 * dz2 + v1 * dxdz2 + dxdz1 * v2 )
( T $ dz1 * dy2 + dy1 * dz2 + v1 * dydz2 + dydz1 * v2 )
( T $ dz1 * dz2 + v1 * ddz2 + ddz1 * v2)
instance Module Double v => Module ( D ( 0 ) Double ) ( D ( 0 ) v ) where
(^+^) = liftA2 (^+^)
(^-^) = liftA2 (^-^)
origin = pure origin
(*^) = liftA2 (*^)
instance Module Double v => Module ( D ( 1 ) Double ) ( D ( 1 ) v ) where
(^+^) = liftA2 (^+^)
(^-^) = liftA2 (^-^)
origin = pure origin
(*^) = liftA2 (*^)
instance Module Double v => Module ( D ( 2 ) Double ) ( D ( 2 ) v ) where
(^+^) = liftA2 (^+^)
(^-^) = liftA2 (^-^)
origin = pure origin
(*^) = liftA2 (*^)
instance Module Double v => Module ( D ( 3 ) Double ) ( D ( 3 ) v ) where
(^+^) = liftA2 (^+^)
(^-^) = liftA2 (^-^)
origin = pure origin
(*^) = liftA2 (*^)
instance Fractional ( D ( 1 ) Double ) where
(/) = error "I haven't yet defined (/) for D ( 1 )"
fromRational = konst . fromRational
instance Floating ( D ( 1 ) Double ) where
pi = konst pi
sin ( D1 v ( T dx ) ( T ddx ) )
= let !s = sin v
!c = cos v
in D1 s ( T $ c * dx ) ( T $ 2 * c * ddx - s * dx * dx )
cos ( D1 v ( T dx ) ( T ddx ) )
= let !s = sin v
!c = cos v
in D1 c ( T $ -s * dx ) ( T $ -2 * s * ddx - c * dx * dx )
instance Fractional ( D ( 2 ) Double ) where
(/) = error "I haven't yet defined (/) for D ( 2 )"
fromRational = konst . fromRational
instance Floating ( D ( 2 ) Double ) where
pi = konst pi
sin ( D2 v ( T dx ) ( T dy ) ( T ddx ) ( T dxdy ) ( T ddy ) )
= let !s = sin v
!c = cos v
in D2 s
( T $ c * dx ) ( T $ c * dy )
( T $ 2 * c * ddx - s * dx * dx )
( T $ 2 * c * dxdy - 2 * s * dx * dy )
( T $ 2 * c * ddy - s * dy * dy )
cos ( D2 v ( T dx ) ( T dy ) ( T ddx ) ( T dxdy ) ( T ddy ) )
= let !s = sin v
!c = cos v
in D2 c
( T $ -s * dx ) ( T $ -s * dy )
( T $ -2 * s * ddx - c * dx * dx )
( T $ -2 * s * dxdy - 2 * c * dx * dy )
( T $ -2 * s * ddy - c * dy * dy )
instance Fractional ( D ( 3 ) Double ) where
(/) = error "I haven't yet defined (/) for D ( 3 )"
fromRational = konst . fromRational
instance Floating ( D ( 3 ) Double ) where
pi = konst 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
!c = cos v
in D3 s
( T $ c * dx ) ( T $ c * dy ) ( T $ c * dz )
( T $ 2 * c * ddx - s * dx * dx )
( T $ 2 * c * dxdy - 2 * s * dx * dy )
( T $ 2 * c * ddy - s * dy * dy )
( T $ 2 * c * dxdz - 2 * s * dx * dz )
( T $ 2 * c * dydz - 2 * s * dy * dz )
( T $ 2 * c * ddz - s * dz * dz )
cos ( D3 v ( T dx ) ( T dy ) ( T dz ) ( T ddx ) ( T dxdy ) ( T ddy ) ( T dxdz ) ( T dydz ) ( T ddz ) )
= let !s = sin v
!c = cos v
in D3 c
( T $ -s * dx ) ( T $ -s * dy ) ( T $ -s * dz )
( T $ -2 * s * ddx - c * dx * dx )
( T $ -2 * s * dxdy - 2 * c * dx * dy )
( T $ -2 * s * ddy - c * dy * dy )
( T $ -2 * s * dxdz - 2 * c * dx * dz )
( T $ -2 * s * dydz - 2 * c * dy * dz )
( T $ -2 * s * ddz - c * dz * dz )
--------------------------------------------------------------------------------
uncurryD :: ( 1 ~> 1 ~> b ) -> ( 2 ~> b )
uncurryD ( D b ) = D \ ( 2 t0 s0 ) ->
let !(D1 ( D b_t0 ) ( T ( D dbdt_t0 ) ) ( T ( D d2bdt2_t0 ) ) ) = b ( 1 t0 )
!(D1 b_t0s0 dbds_t0s0 d2bds2_t0s0 ) = b_t0 ( 1 s0 )
!(D1 dbdt_t0s0 d2bdtds_t0s0 _ ) = dbdt_t0 ( 1 s0 )
!(D1 d2bdt2_t0s0 _ _ ) = d2bdt2_t0 ( 1 s0 )
in D2 b_t0s0 ( T dbdt_t0s0 ) dbds_t0s0 ( T d2bdt2_t0s0 ) d2bdtds_t0s0 d2bds2_t0s0
--------------------------------------------------------------------------------
chainRule :: ( Chain v, Module Double ( T w ) )
=> ( ( 1 ) ~> v )
-> ( v ~> w )
-> ( ( 1 ) ~> w )
chainRule ( D f ) ( D g ) =
D \ x ->
case f x of
df@( D1 { v = f_x } ) ->
chain df ( g f_x )
type Chain :: Type -> Constraint
class Chain v where
chain :: ( Module Double ( T w ) )
=> D ( 1 ) v -> D v w -> D ( 1 ) w
konst :: Module Double ( T w ) => w -> D v w
instance Chain ( 0 ) where
chain _ ( D0 v ) = D1 v origin origin
konst k = D0 k
instance Chain ( 1 ) where
chain ( D1 _ ( T ( 1 x' ) ) ( T ( 1 x'' ) ) ) ( D1 v g_x g_xx )
= D1 v
( x' *^ g_x )
( x'' *^ g_x ^+^ ( x' * x' ) *^ g_xx )
konst k = D1 k origin origin
instance Chain ( 2 ) where
chain ( D1 _ ( T ( 2 x' y' ) ) ( T ( 2 x'' y'' ) ) ) ( D2 v g_x g_y g_xx g_xy g_yy )
= 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 ) )
konst k = D2 k origin origin origin origin origin
instance Chain ( 3 ) where
chain ( D1 _ ( T ( 3 x' y' z' ) ) ( T ( 3 x'' y'' z'' ) ) )
( D3 v g_x g_y g_z g_xx g_xy g_yy g_xz g_yz g_zz )
= 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 )
konst k = D3 k origin origin origin origin origin origin origin origin origin
--------------------------------------------------------------------------------
type Var :: Nat -> Type -> Constraint
class Var n v where
var :: v ~> Double
instance Var 1 ( 1 ) where
var = D \ ( 1 x ) -> D1 x ( T 1 ) ( T 0 )
instance Var 1 ( 2 ) where
var = D \ ( 2 x _ ) -> D2 x ( T 1 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 )
instance Var 2 ( 2 ) where
var = D \ ( 2 _ y ) -> D2 y ( T 0 ) ( T 1 ) ( T 0 ) ( T 0 ) ( T 0 )
instance Var 1 ( 3 ) where
var = D \ ( 3 x _ _ ) -> D3 x ( T 1 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 )
instance Var 2 ( 3 ) where
var = D \ ( 3 _ y _ ) -> D3 y ( T 0 ) ( T 1 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 )
instance Var 3 ( 3 ) where
var = D \ ( 3 _ _ z ) -> D3 z ( T 0 ) ( T 0 ) ( T 1 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 ) ( T 0 )

View file

@ -1,5 +1,3 @@
{-# LANGUAGE DataKinds #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module Math.Linear.Solve module Math.Linear.Solve
@ -17,7 +15,7 @@ import qualified Eigen.Solver.LA as Eigen
( Decomposition(..), solve ) ( Decomposition(..), solve )
-- MetaBrush -- MetaBrush
import Math.Vector2D import Math.Linear
( Vector2D(..), Mat22(..) ) ( Vector2D(..), Mat22(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View file

@ -1,9 +1,4 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.Module module Math.Module
( Module(..), lerp ( Module(..), lerp
@ -38,8 +33,7 @@ import Data.Group
-- MetaBrush -- MetaBrush
import Math.Epsilon import Math.Epsilon
( epsilon ) ( epsilon )
import Math.Vector2D import Math.Linear
( Vector2D(..), Segment(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -58,7 +52,7 @@ class Num r => Module r m | m -> r where
(*^) = flip (^*) (*^) = flip (^*)
(^*) = flip (*^) (^*) = flip (*^)
m ^-^ n = m ^+^ (-1) *^ n m ^-^ n = m ^+^ -1 *^ n
instance ( Applicative f, Module r m ) => Module r ( Ap f m ) where instance ( Applicative f, Module r m ) => Module r ( Ap f m ) where
origin = pure origin origin = pure origin
@ -145,6 +139,29 @@ instance Num a => Inner a ( Vector2D a ) where
( Vector2D x1 y1 ) ^.^ ( Vector2D x2 y2 ) ( Vector2D x1 y1 ) ^.^ ( Vector2D x2 y2 )
= x1 * x2 + y1 * y2 = x1 * x2 + y1 * y2
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 ) )
-- | Cross-product of two 2D vectors. -- | Cross-product of two 2D vectors.
cross :: Num a => Vector2D a -> Vector2D a -> a cross :: Num a => Vector2D a -> Vector2D a -> a
cross ( Vector2D x1 y1 ) ( Vector2D x2 y2 ) cross ( Vector2D x1 y1 ) ( Vector2D x2 y2 )
@ -181,7 +198,7 @@ convexCombination v0 v1 u
let let
t :: r t :: r
t = c0 / c10 t = c0 / c10
guard ( t > - epsilon && t < 1 + epsilon ) guard ( t > -epsilon && t < 1 + epsilon )
guard ( epsilon < u ^.^ ( lerp @( Vector2D r ) t v0 v1 ) ) guard ( epsilon < u ^.^ ( lerp @( Vector2D r ) t v0 v1 ) )
Just $ min 1 ( max 0 t ) Just $ min 1 ( max 0 t )

View file

@ -1,10 +1,4 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Math.Orientation module Math.Orientation
( Orientation(..), reverseOrientation ( Orientation(..), reverseOrientation
@ -43,7 +37,7 @@ import Math.Bezier.Spline
, SplineType(..), KnownSplineType(..), SSplineType(..) , SplineType(..), KnownSplineType(..), SSplineType(..)
, ssplineType , ssplineType
) )
import Math.Vector2D import Math.Linear
( Point2D, Vector2D(..) ) ( Point2D, Vector2D(..) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -127,7 +121,7 @@ splineTangents spline@( Spline { splineStart = sp0, splineCurves = curves } )
-- Returns the proportion of the angle the vector is in between, or @Nothing@ if the query vector -- Returns the proportion of the angle the vector is in between, or @Nothing@ if the query vector
-- is not in between. -- is not in between.
-- --
-- >>> between CCW ( Vector2D 1 0 ) ( Vector2D (-1) 1 ) ( Vector2D 1 1 ) -- >>> between CCW ( Vector2D 1 0 ) ( Vector2D -1 1 ) ( Vector2D 1 1 )
-- Just 0.3333333333333333 -- Just 0.3333333333333333
between between
:: forall r :: forall r
@ -135,7 +129,7 @@ between
=> Orientation => Orientation
-> Vector2D r -- ^ start vector -> Vector2D r -- ^ start vector
-> Vector2D r -- ^ end vector -> Vector2D r -- ^ end vector
-> Vector2D r -- ^ query vector: is in between the start and end vectors w.r.t. the provided orientation? -> Vector2D r -- ^ query vector: is it in between the start and end vectors w.r.t. the provided orientation?
-> Maybe r -> Maybe r
between CCW ( Vector2D x1 y1 ) ( Vector2D x2 y2 ) ( Vector2D a b ) = between CCW ( Vector2D x1 y1 ) ( Vector2D x2 y2 ) ( Vector2D a b ) =
let let

View file

@ -1,12 +1,3 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedWildCards #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Math.Roots where module Math.Roots where
-- base -- base
@ -61,7 +52,7 @@ solveQuadratic a0 a1 a2
then [ 0, 0.5, 1 ] -- convention then [ 0, 0.5, 1 ] -- convention
else [] else []
| nearZero ( a0 * a0 * a2 / ( a1 * a1 ) ) | nearZero ( a0 * a0 * a2 / ( a1 * a1 ) )
= [ - a0 / a1 ] = [ -a0 / a1 ]
| disc < 0 | disc < 0
= [] -- non-real solutions = [] -- non-real solutions
| otherwise | otherwise
@ -69,8 +60,8 @@ solveQuadratic a0 a1 a2
r :: a r :: a
r = r =
if a1 >= 0 if a1 >= 0
then 2 * a0 / ( - a1 - sqrt disc ) then 2 * a0 / ( -a1 - sqrt disc )
else 0.5 * ( - a1 + sqrt disc ) / a2 else 0.5 * ( -a1 + sqrt disc ) / a2
in [ r, -r - a1 / a2 ] in [ r, -r - a1 / a2 ]
where where
disc :: a disc :: a

View file

@ -1,93 +0,0 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.Vector2D
( Point2D(..), Vector2D(.., Vector2D), Mat22(..)
, Segment(..)
)
where
-- base
import Data.Monoid
( Sum(..) )
import GHC.Generics
( Generic, Generic1
, Generically(..), Generically1(..)
)
-- acts
import Data.Act
( Act, Torsor )
-- deepseq
import Control.DeepSeq
( NFData, NFData1 )
-- groups
import Data.Group
( Group(..) )
-- groups-generic
import Data.Group.Generics
( )
--------------------------------------------------------------------------------
data Point2D a = Point2D !a !a
deriving stock ( Eq, Generic, Generic1, Functor, Foldable, Traversable )
deriving ( Act ( Vector2D a ), Torsor ( Vector2D a ) )
via Vector2D a
deriving Applicative
via Generically1 Point2D
deriving anyclass ( NFData, NFData1 )
instance Show a => Show (Point2D a) where
showsPrec i (Point2D a b) = showsPrec i (a,b)
newtype Vector2D a = MkVector2D { tip :: Point2D a }
deriving stock ( Generic, Generic1, Foldable, Traversable )
deriving newtype ( Eq, Functor, Applicative, NFData, NFData1 )
deriving ( Semigroup, Monoid, Group )
via Generically ( Point2D ( Sum a ) )
data V a = V a a
deriving stock Show
instance Show a => Show (Vector2D a) where
showsPrec i (Vector2D x y) = showsPrec i (V x y)
{-# COMPLETE Vector2D #-}
pattern Vector2D :: a -> a -> Vector2D a
pattern Vector2D x y = MkVector2D ( Point2D x y )
data Mat22 a
= Mat22 !a !a !a !a
deriving stock ( Show, Eq, Generic, Generic1, Functor, Foldable, Traversable )
deriving Applicative
via Generically1 Mat22
deriving anyclass ( NFData, NFData1 )
data Segment p =
Segment
{ segmentStart :: !p
, segmentEnd :: !p
}
deriving stock ( Generic, Generic1, Functor, Foldable, Traversable )
deriving ( Semigroup, Monoid, Group )
via Generically ( Segment p )
deriving Applicative
via Generically1 Segment
deriving anyclass ( NFData, NFData1 )
instance Show p => Show (Segment p) where
show (Segment s e) = show s ++ " -> " ++ show e