mirror of
https://gitlab.com/sheaf/metabrush.git
synced 2024-11-05 23:03:38 +00:00
264 lines
9.3 KiB
Haskell
264 lines
9.3 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
||
{-# LANGUAGE ScopedTypeVariables #-}
|
||
{-# LANGUAGE UndecidableInstances #-}
|
||
|
||
module MetaBrush.Serialisable
|
||
( Serialisable(..)
|
||
, encodeSequence, decodeSequence
|
||
, encodeCurve, decodeCurve
|
||
, encodeCurves, decodeCurves
|
||
, encodeSpline, decodeSpline
|
||
)
|
||
where
|
||
|
||
-- base
|
||
|
||
import Data.Foldable
|
||
( toList )
|
||
import Data.Functor
|
||
( (<&>) )
|
||
import Data.Functor.Contravariant
|
||
( contramap )
|
||
import Data.Functor.Identity
|
||
( Identity(..) )
|
||
import Data.IORef
|
||
( newIORef, atomicModifyIORef' )
|
||
import Data.Traversable
|
||
( for )
|
||
|
||
-- containers
|
||
import Data.Sequence
|
||
( Seq )
|
||
import qualified Data.Sequence as Seq
|
||
( fromList )
|
||
|
||
-- scientific
|
||
import qualified Data.Scientific as Scientific
|
||
( fromFloatDigits, toRealFloat )
|
||
|
||
-- text
|
||
import Data.Text
|
||
( Text )
|
||
|
||
-- transformers
|
||
import Control.Monad.IO.Class
|
||
( MonadIO(liftIO) )
|
||
import Control.Monad.Trans.Class
|
||
( MonadTrans(lift) )
|
||
|
||
-- waargonaut
|
||
import qualified Waargonaut.Decode as JSON
|
||
( Decoder )
|
||
import qualified Waargonaut.Decode as JSON.Decoder
|
||
import qualified Waargonaut.Encode as JSON
|
||
( Encoder )
|
||
import qualified Waargonaut.Encode as JSON.Encoder
|
||
|
||
-- meta-brushes
|
||
import Math.Bezier.Spline
|
||
( Spline(..), SplineType(..), SSplineType(..), SplineTypeI(..)
|
||
, Curves(..), Curve(..), NextPoint(..)
|
||
)
|
||
import Math.Linear
|
||
( ℝ(..), T(..)
|
||
, Fin(..), Representable(tabulate, index)
|
||
)
|
||
import MetaBrush.Records
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
class Serialisable a where
|
||
encoder :: Monad f => JSON.Encoder f a
|
||
decoder :: Monad m => JSON.Decoder m a
|
||
|
||
instance Serialisable Double where
|
||
encoder = contramap Scientific.fromFloatDigits JSON.Encoder.scientific
|
||
decoder = fmap Scientific.toRealFloat JSON.Decoder.scientific
|
||
|
||
instance Serialisable ( ℝ 2 ) where
|
||
encoder = JSON.Encoder.mapLikeObj \ ( ℝ2 x y ) ->
|
||
JSON.Encoder.atKey' "x" encoder x
|
||
. JSON.Encoder.atKey' "y" encoder y
|
||
decoder = ℝ2 <$> JSON.Decoder.atKey "x" decoder
|
||
<*> JSON.Decoder.atKey "y" decoder
|
||
instance Serialisable ( T ( ℝ 2 ) ) where
|
||
encoder = JSON.Encoder.mapLikeObj \ ( V2 x y ) ->
|
||
JSON.Encoder.atKey' "x" encoder x
|
||
. JSON.Encoder.atKey' "y" encoder y
|
||
decoder = V2 <$> JSON.Decoder.atKey "x" decoder
|
||
<*> JSON.Decoder.atKey "y" decoder
|
||
|
||
instance ( KnownSymbols ks, Representable Double ( ℝ ( Length ks ) ) )
|
||
=> Serialisable ( Record ks ) where
|
||
encoder = contramap encodeFields ( JSON.Encoder.keyValueTupleFoldable ( encoder @Double ) )
|
||
where
|
||
encodeFields :: Record ks -> [ ( Text, Double ) ]
|
||
encodeFields ( MkR r ) =
|
||
zip [1..] ( knownSymbols @ks ) <&> \ ( i, fld ) ->
|
||
( fld, index r ( Fin i ) )
|
||
|
||
decoder = fmap decodeFields $ for ( knownSymbols @ks ) \ k -> JSON.Decoder.atKey k ( decoder @Double )
|
||
where
|
||
decodeFields :: [ Double ] -> Record ks
|
||
decodeFields coords = MkR $ tabulate \ ( Fin i ) ->
|
||
coords !! ( fromIntegral i - 1 )
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
encodeSequence :: Applicative f => JSON.Encoder f a -> JSON.Encoder f ( Seq a )
|
||
encodeSequence enc = contramap toList ( JSON.Encoder.list enc )
|
||
|
||
decodeSequence :: Monad m => JSON.Decoder m a -> JSON.Decoder m ( Seq a )
|
||
decodeSequence dec = Seq.fromList <$> JSON.Decoder.list dec
|
||
|
||
{-
|
||
encodeMat22 :: Applicative f => JSON.Encoder' a -> JSON.Encoder f ( Mat22 a )
|
||
encodeMat22 enc = JSON.Encoder.mapLikeObj \ ( Mat22 m00 m01 m10 m11 ) ->
|
||
JSON.Encoder.atKey' "m00" enc m00
|
||
. JSON.Encoder.atKey' "m01" enc m01
|
||
. JSON.Encoder.atKey' "m10" enc m10
|
||
. JSON.Encoder.atKey' "m11" enc m11
|
||
|
||
decodeMat22 :: Monad m => JSON.Decoder m a -> JSON.Decoder m ( Mat22 a )
|
||
decodeMat22 dec =
|
||
Mat22 <$> JSON.Decoder.atKey "m00" dec
|
||
<*> JSON.Decoder.atKey "m01" dec
|
||
<*> JSON.Decoder.atKey "m10" dec
|
||
<*> JSON.Decoder.atKey "m11" dec
|
||
|
||
|
||
|
||
encodeAABB :: Applicative f => JSON.Encoder f AABB
|
||
encodeAABB = JSON.Encoder.mapLikeObj \ ( AABB { topLeft, botRight } ) ->
|
||
JSON.Encoder.atKey' "topLeft" enc topLeft
|
||
. JSON.Encoder.atKey' "botRight" enc botRight
|
||
where
|
||
enc :: JSON.Encoder' ( Point2D Double )
|
||
enc = encodePoint2D encodeDouble
|
||
|
||
decodeAABB :: forall m. Monad m => JSON.Decoder m AABB
|
||
decodeAABB = do
|
||
topLeft <- JSON.Decoder.atKey "topLeft" dec
|
||
botRight <- JSON.Decoder.atKey "botRight" dec
|
||
pure ( AABB { topLeft, botRight } )
|
||
where
|
||
dec :: JSON.Decoder m ( Point2D Double )
|
||
dec = decodePoint2D decodeDouble
|
||
-}
|
||
|
||
|
||
encodeCurve
|
||
:: forall clo crvData ptData f
|
||
. ( SplineTypeI clo, Applicative f )
|
||
=> JSON.Encoder Identity ptData
|
||
-> JSON.Encoder f ( Curve clo crvData ptData )
|
||
encodeCurve encodePtData = case ssplineType @clo of
|
||
SOpen -> JSON.Encoder.mapLikeObj \case
|
||
LineTo ( NextPoint p1 ) _ ->
|
||
JSON.Encoder.atKey' "p1" encodePtData p1
|
||
Bezier2To p1 ( NextPoint p2 ) _ ->
|
||
JSON.Encoder.atKey' "p1" encodePtData p1
|
||
. JSON.Encoder.atKey' "p2" encodePtData p2
|
||
Bezier3To p1 p2 ( NextPoint p3 ) _ ->
|
||
JSON.Encoder.atKey' "p1" encodePtData p1
|
||
. JSON.Encoder.atKey' "p2" encodePtData p2
|
||
. JSON.Encoder.atKey' "p3" encodePtData p3
|
||
SClosed -> JSON.Encoder.mapLikeObj \case
|
||
LineTo BackToStart _ -> id
|
||
Bezier2To p1 BackToStart _ ->
|
||
JSON.Encoder.atKey' "p1" encodePtData p1
|
||
Bezier3To p1 p2 BackToStart _ ->
|
||
JSON.Encoder.atKey' "p1" encodePtData p1
|
||
. JSON.Encoder.atKey' "p2" encodePtData p2
|
||
|
||
decodeCurve
|
||
:: forall clo ptData crvData m
|
||
. ( SplineTypeI clo, MonadIO m )
|
||
=> JSON.Decoder m ptData
|
||
-> JSON.Decoder m crvData
|
||
-> JSON.Decoder m ( Curve clo crvData ptData )
|
||
decodeCurve decodePtData decodeCrvData = do
|
||
crv <- decodeCrvData
|
||
case ssplineType @clo of
|
||
SOpen -> do
|
||
p1 <- JSON.Decoder.atKey "p1" decodePtData
|
||
mb_p2 <- JSON.Decoder.atKeyOptional "p2" decodePtData
|
||
case mb_p2 of
|
||
Nothing ->
|
||
pure ( LineTo ( NextPoint p1 ) crv)
|
||
Just p2 -> do
|
||
mb_p3 <- JSON.Decoder.atKeyOptional "p3" decodePtData
|
||
case mb_p3 of
|
||
Nothing -> pure ( Bezier2To p1 ( NextPoint p2 ) crv )
|
||
Just p3 -> pure ( Bezier3To p1 p2 ( NextPoint p3 ) crv )
|
||
SClosed -> do
|
||
mb_p1 <- JSON.Decoder.atKeyOptional "p1" decodePtData
|
||
case mb_p1 of
|
||
Nothing ->
|
||
pure ( LineTo BackToStart crv )
|
||
Just p1 -> do
|
||
mb_p2 <- JSON.Decoder.atKeyOptional "p2" decodePtData
|
||
case mb_p2 of
|
||
Nothing -> pure ( Bezier2To p1 BackToStart crv )
|
||
Just p2 -> pure ( Bezier3To p1 p2 BackToStart crv )
|
||
|
||
encodeCurves
|
||
:: forall clo crvData ptData f
|
||
. ( SplineTypeI clo, Applicative f )
|
||
=> JSON.Encoder Identity ptData
|
||
-> JSON.Encoder f ( Curves clo crvData ptData )
|
||
encodeCurves encodePtData = case ssplineType @clo of
|
||
SOpen -> contramap ( openCurves ) ( encodeSequence $ encodeCurve @Open encodePtData )
|
||
SClosed -> contramap ( \case { NoCurves -> Left (); ClosedCurves prevs lst -> Right ( prevs, lst ) } ) ( JSON.Encoder.either encodeL encodeR )
|
||
where
|
||
encodeL :: JSON.Encoder f ()
|
||
encodeL = contramap ( const "NoCurves" ) JSON.Encoder.text
|
||
encodeR :: JSON.Encoder f ( Seq ( Curve Open crvData ptData ), Curve Closed crvData ptData )
|
||
encodeR = JSON.Encoder.mapLikeObj \ ( openCurves, closedCurve ) ->
|
||
JSON.Encoder.atKey' "prevOpenCurves" ( encodeSequence $ encodeCurve @Open encodePtData ) openCurves
|
||
. JSON.Encoder.atKey' "lastClosedCurve" ( encodeCurve @Closed encodePtData ) closedCurve
|
||
|
||
decodeCurves
|
||
:: forall clo ptData crvData m
|
||
. ( SplineTypeI clo, MonadIO m )
|
||
=> JSON.Decoder m ptData
|
||
-> JSON.Decoder m crvData
|
||
-> JSON.Decoder m ( Curves clo crvData ptData )
|
||
decodeCurves decodePtData decodeCrvData = case ssplineType @clo of
|
||
SOpen -> OpenCurves <$> decodeSequence ( decodeCurve @Open decodePtData decodeCrvData )
|
||
SClosed -> do
|
||
mbNoCurves <- JSON.Decoder.atKeyOptional "NoCurves" ( JSON.Decoder.text )
|
||
case mbNoCurves of
|
||
Just _ -> pure NoCurves
|
||
Nothing -> do
|
||
prevCurves <- JSON.Decoder.atKey "prevOpenCurves" ( decodeSequence $ decodeCurve @Open decodePtData decodeCrvData )
|
||
lastCurve <- JSON.Decoder.atKey "lastClosedCurve" ( decodeCurve @Closed decodePtData decodeCrvData )
|
||
pure ( ClosedCurves prevCurves lastCurve )
|
||
|
||
|
||
|
||
encodeSpline
|
||
:: forall clo crvData ptData f
|
||
. ( SplineTypeI clo, Applicative f )
|
||
=> JSON.Encoder Identity ptData
|
||
-> JSON.Encoder f ( Spline clo crvData ptData )
|
||
encodeSpline encodePtData = JSON.Encoder.mapLikeObj \ ( Spline { splineStart, splineCurves } ) ->
|
||
JSON.Encoder.atKey' "splineStart" encodePtData splineStart
|
||
. JSON.Encoder.atKey' "splineCurves" ( encodeCurves @clo encodePtData ) splineCurves
|
||
|
||
decodeSpline
|
||
:: forall clo ptData crvData m
|
||
. ( SplineTypeI clo, MonadIO m )
|
||
=> JSON.Decoder m ptData
|
||
-> ( Integer -> m crvData )
|
||
-> JSON.Decoder m ( Spline clo crvData ptData )
|
||
decodeSpline decodePtData newCurve = do
|
||
ref <- lift $ liftIO $ newIORef 0
|
||
let newCrvData :: m crvData
|
||
newCrvData = do
|
||
i <- liftIO $ atomicModifyIORef' ref ( \ o -> ( o + 1, o ) )
|
||
newCurve i
|
||
splineStart <- JSON.Decoder.atKey "splineStart" decodePtData
|
||
splineCurves <- JSON.Decoder.atKey "splineCurves" ( decodeCurves @clo decodePtData ( lift newCrvData ) )
|
||
pure ( Spline { splineStart, splineCurves } )
|