2023-01-09 01:54:42 +00:00
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
{-# LANGUAGE UndecidableInstances #-}
|
2021-02-24 21:45:08 +00:00
|
|
|
|
|
2020-08-15 17:11:52 +00:00
|
|
|
|
module MetaBrush.Document
|
2020-09-08 23:23:25 +00:00
|
|
|
|
( AABB(..), mkAABB
|
2020-09-10 16:43:42 +00:00
|
|
|
|
, Document(..), DocumentContent(..)
|
|
|
|
|
, emptyDocument
|
2021-04-26 15:18:48 +00:00
|
|
|
|
, Stroke(..), StrokeHierarchy(..), visibleStrokes
|
|
|
|
|
, StrokeSpline, _strokeSpline, overStrokeSpline
|
2020-11-12 17:34:46 +00:00
|
|
|
|
, PointData(..), BrushPointData(..), DiffPointData(..)
|
2020-09-08 23:23:25 +00:00
|
|
|
|
, FocusState(..), Hoverable(..), HoverContext(..)
|
2022-02-11 21:05:13 +00:00
|
|
|
|
, Guide(..), Ruler(..)
|
2020-11-12 17:34:46 +00:00
|
|
|
|
, _selection, _coords, coords
|
2020-09-08 23:23:25 +00:00
|
|
|
|
, addGuide, selectedGuide
|
2020-08-15 17:11:52 +00:00
|
|
|
|
)
|
|
|
|
|
where
|
2020-08-04 06:15:06 +00:00
|
|
|
|
|
2020-09-01 19:56:59 +00:00
|
|
|
|
-- base
|
2021-02-24 21:45:08 +00:00
|
|
|
|
import Control.Monad.ST
|
|
|
|
|
( RealWorld )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
import Data.Coerce
|
|
|
|
|
( coerce )
|
|
|
|
|
import Data.Functor.Identity
|
|
|
|
|
( Identity(..) )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
import Data.Semigroup
|
|
|
|
|
( Arg(..), Min(..), ArgMin )
|
2023-01-08 16:16:14 +00:00
|
|
|
|
import Data.Typeable
|
|
|
|
|
( Typeable )
|
2020-09-01 19:56:59 +00:00
|
|
|
|
import GHC.Generics
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( Generic, Generic1 )
|
2023-01-08 16:16:14 +00:00
|
|
|
|
import GHC.TypeLits
|
|
|
|
|
( Symbol )
|
2020-09-01 19:56:59 +00:00
|
|
|
|
|
2020-09-08 23:23:25 +00:00
|
|
|
|
-- acts
|
|
|
|
|
import Data.Act
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( Act(..), Torsor(..) )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
|
2020-08-10 22:07:09 +00:00
|
|
|
|
-- containers
|
2020-09-05 22:35:00 +00:00
|
|
|
|
import Data.Map.Strict
|
|
|
|
|
( Map )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
import qualified Data.Map.Strict as Map
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( empty, insert )
|
2021-04-26 12:34:34 +00:00
|
|
|
|
import Data.Sequence
|
2021-04-26 15:18:48 +00:00
|
|
|
|
( Seq(..) )
|
2021-04-26 12:34:34 +00:00
|
|
|
|
import qualified Data.Sequence as Seq
|
2021-04-26 15:18:48 +00:00
|
|
|
|
( empty, singleton )
|
2020-08-10 22:07:09 +00:00
|
|
|
|
|
2020-09-10 16:43:42 +00:00
|
|
|
|
-- deepseq
|
|
|
|
|
import Control.DeepSeq
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( NFData(..), NFData1, deepseq )
|
2020-09-10 16:43:42 +00:00
|
|
|
|
|
2020-08-19 21:34:43 +00:00
|
|
|
|
-- generic-lens
|
|
|
|
|
import Data.Generics.Product.Fields
|
|
|
|
|
( field' )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
|
|
|
|
|
-- groups
|
|
|
|
|
import Data.Group
|
|
|
|
|
( Group(..) )
|
2020-08-19 21:34:43 +00:00
|
|
|
|
|
|
|
|
|
-- lens
|
|
|
|
|
import Control.Lens
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( Lens'
|
|
|
|
|
, set, view, over
|
|
|
|
|
)
|
2020-08-19 21:34:43 +00:00
|
|
|
|
|
2020-09-08 23:23:25 +00:00
|
|
|
|
-- stm
|
|
|
|
|
import Control.Concurrent.STM
|
|
|
|
|
( STM )
|
|
|
|
|
|
2020-08-10 14:38:27 +00:00
|
|
|
|
-- text
|
|
|
|
|
import Data.Text
|
|
|
|
|
( Text )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
|
|
|
|
|
-- transformers
|
|
|
|
|
import Control.Monad.Trans.Reader
|
|
|
|
|
( ReaderT, runReaderT )
|
2020-08-10 14:38:27 +00:00
|
|
|
|
|
|
|
|
|
-- MetaBrush
|
2020-11-12 17:34:46 +00:00
|
|
|
|
import Math.Bezier.Spline
|
2020-11-14 22:32:23 +00:00
|
|
|
|
( Spline(..), KnownSplineType )
|
2020-08-19 21:34:43 +00:00
|
|
|
|
import Math.Bezier.Stroke
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( CachedStroke )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
import Math.Module
|
2020-11-12 17:34:46 +00:00
|
|
|
|
( Module
|
|
|
|
|
( origin, (^+^), (^-^), (*^) )
|
|
|
|
|
, Inner((^.^))
|
|
|
|
|
, squaredNorm, quadrance
|
|
|
|
|
)
|
2023-01-08 16:16:14 +00:00
|
|
|
|
import Math.Linear
|
2023-01-09 03:27:08 +00:00
|
|
|
|
( ℝ(..), T(..) )
|
2020-11-14 22:32:23 +00:00
|
|
|
|
import MetaBrush.Brush
|
2024-04-20 16:28:41 +00:00
|
|
|
|
( NamedBrush, PointFields )
|
2022-02-11 21:05:13 +00:00
|
|
|
|
import MetaBrush.Records
|
2020-08-16 22:09:16 +00:00
|
|
|
|
import MetaBrush.Unique
|
2020-09-08 23:23:25 +00:00
|
|
|
|
( UniqueSupply, Unique, freshUnique )
|
2020-08-10 14:38:27 +00:00
|
|
|
|
|
2020-08-04 06:15:06 +00:00
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
2020-08-10 14:38:27 +00:00
|
|
|
|
data AABB
|
|
|
|
|
= AABB
|
2023-01-09 03:27:08 +00:00
|
|
|
|
{ topLeft, botRight :: !( ℝ 2 ) }
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
2020-08-10 14:38:27 +00:00
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
mkAABB :: ℝ 2 -> ℝ 2 -> AABB
|
|
|
|
|
mkAABB ( ℝ2 x1 y1 ) ( ℝ2 x2 y2 ) = AABB ( ℝ2 xmin ymin ) ( ℝ2 xmax ymax )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
where
|
|
|
|
|
( xmin, xmax )
|
|
|
|
|
| x1 > x2 = ( x2, x1 )
|
|
|
|
|
| otherwise = ( x1, x2 )
|
|
|
|
|
( ymin, ymax )
|
|
|
|
|
| y1 > y2 = ( y2, y1 )
|
|
|
|
|
| otherwise = ( y1, y2 )
|
|
|
|
|
|
2020-09-10 16:43:42 +00:00
|
|
|
|
-- | Document, together with some extra metadata.
|
2020-08-04 06:15:06 +00:00
|
|
|
|
data Document
|
|
|
|
|
= Document
|
2020-09-10 16:43:42 +00:00
|
|
|
|
{ displayName :: !Text
|
|
|
|
|
, mbFilePath :: !( Maybe FilePath )
|
2023-01-09 03:27:08 +00:00
|
|
|
|
, viewportCenter :: !( ℝ 2 )
|
2020-09-10 16:43:42 +00:00
|
|
|
|
, zoomFactor :: !Double
|
|
|
|
|
, documentUnique :: Unique
|
|
|
|
|
, documentContent :: !DocumentContent
|
|
|
|
|
}
|
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
|
|
|
|
|
2020-11-12 17:34:46 +00:00
|
|
|
|
-- | Main content of document (data which we kept track of throughout history).
|
2020-09-10 16:43:42 +00:00
|
|
|
|
data DocumentContent
|
|
|
|
|
= Content
|
|
|
|
|
{ unsavedChanges :: !Bool
|
|
|
|
|
, latestChange :: !Text
|
|
|
|
|
, guides :: !( Map Unique Guide )
|
2021-04-26 15:18:48 +00:00
|
|
|
|
, strokes :: !( Seq StrokeHierarchy )
|
2020-09-10 16:43:42 +00:00
|
|
|
|
}
|
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
2020-08-10 14:38:27 +00:00
|
|
|
|
|
2021-04-26 15:18:48 +00:00
|
|
|
|
-- | Hierarchy for groups of strokes.
|
|
|
|
|
data StrokeHierarchy
|
2022-02-11 21:05:13 +00:00
|
|
|
|
= StrokeGroup
|
2021-04-26 15:18:48 +00:00
|
|
|
|
{ groupName :: !Text
|
|
|
|
|
, groupVisible :: !Bool
|
|
|
|
|
, groupContents :: !( Seq StrokeHierarchy )
|
|
|
|
|
}
|
|
|
|
|
| StrokeLeaf
|
|
|
|
|
{ strokeLeaf :: !Stroke }
|
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
|
|
|
|
|
|
|
|
|
visibleStrokes :: StrokeHierarchy -> Seq Stroke
|
|
|
|
|
visibleStrokes ( StrokeGroup { groupVisible, groupContents } )
|
|
|
|
|
| groupVisible
|
|
|
|
|
= foldMap visibleStrokes groupContents
|
|
|
|
|
| otherwise
|
|
|
|
|
= Empty
|
|
|
|
|
visibleStrokes ( StrokeLeaf { strokeLeaf } )
|
|
|
|
|
| strokeVisible strokeLeaf
|
|
|
|
|
= Seq.singleton strokeLeaf
|
|
|
|
|
| otherwise
|
|
|
|
|
= Empty
|
|
|
|
|
|
2021-02-25 00:08:13 +00:00
|
|
|
|
type StrokeSpline clo brushParams =
|
|
|
|
|
Spline clo ( CachedStroke RealWorld ) ( PointData brushParams )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
|
|
|
|
|
data Stroke where
|
|
|
|
|
Stroke
|
2023-01-08 16:16:14 +00:00
|
|
|
|
:: forall clo pointParams ( pointFields :: [ Symbol ] ) ( brushFields :: [ Symbol ] )
|
|
|
|
|
. ( KnownSplineType clo
|
|
|
|
|
, pointParams ~ Record pointFields
|
|
|
|
|
, PointFields pointFields, Typeable pointFields
|
2020-11-12 17:34:46 +00:00
|
|
|
|
)
|
|
|
|
|
=>
|
2020-11-14 22:32:23 +00:00
|
|
|
|
{ strokeName :: !Text
|
|
|
|
|
, strokeVisible :: !Bool
|
|
|
|
|
, strokeUnique :: Unique
|
2024-04-20 16:28:41 +00:00
|
|
|
|
, strokeBrush :: !( Maybe ( NamedBrush brushFields ) )
|
2020-11-14 22:32:23 +00:00
|
|
|
|
, strokeSpline :: !( StrokeSpline clo pointParams )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
}
|
|
|
|
|
-> Stroke
|
|
|
|
|
deriving stock instance Show Stroke
|
|
|
|
|
instance NFData Stroke where
|
2021-04-25 23:17:27 +00:00
|
|
|
|
rnf ( Stroke { strokeName, strokeVisible, strokeUnique, strokeBrush, strokeSpline } )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
= deepseq strokeSpline
|
2021-04-25 23:17:27 +00:00
|
|
|
|
. deepseq strokeBrush
|
2020-11-12 17:34:46 +00:00
|
|
|
|
. deepseq strokeUnique
|
|
|
|
|
. deepseq strokeVisible
|
|
|
|
|
$ rnf strokeName
|
|
|
|
|
|
|
|
|
|
_strokeSpline
|
|
|
|
|
:: forall f
|
|
|
|
|
. Functor f
|
2023-01-08 16:16:14 +00:00
|
|
|
|
=> ( forall clo pointParams ( pointFields :: [ Symbol ] )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
. ( KnownSplineType clo
|
2023-01-08 16:16:14 +00:00
|
|
|
|
, pointParams ~ Record pointFields
|
|
|
|
|
, PointFields pointFields
|
2020-11-12 17:34:46 +00:00
|
|
|
|
)
|
|
|
|
|
=> StrokeSpline clo pointParams
|
|
|
|
|
-> f ( StrokeSpline clo pointParams )
|
|
|
|
|
)
|
|
|
|
|
-> Stroke -> f Stroke
|
|
|
|
|
_strokeSpline f ( Stroke { strokeSpline = oldStrokeSpline, .. } )
|
|
|
|
|
= ( \ newSpline -> Stroke { strokeSpline = newSpline, .. } ) <$> f oldStrokeSpline
|
|
|
|
|
|
|
|
|
|
overStrokeSpline
|
2023-01-08 16:16:14 +00:00
|
|
|
|
:: ( forall clo pointParams ( pointFields :: [ Symbol ] )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
. ( KnownSplineType clo
|
2023-01-08 16:16:14 +00:00
|
|
|
|
, pointParams ~ Record pointFields
|
|
|
|
|
, PointFields pointFields
|
2020-11-12 17:34:46 +00:00
|
|
|
|
)
|
|
|
|
|
=> StrokeSpline clo pointParams
|
|
|
|
|
-> StrokeSpline clo pointParams
|
|
|
|
|
)
|
|
|
|
|
-> Stroke -> Stroke
|
|
|
|
|
overStrokeSpline f = coerce ( _strokeSpline @Identity ( coerce . f ) )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data PointData params
|
2020-08-19 21:34:43 +00:00
|
|
|
|
= PointData
|
2023-01-09 03:27:08 +00:00
|
|
|
|
{ pointCoords :: !( ℝ 2 )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
, pointState :: FocusState
|
|
|
|
|
, brushParams :: !params
|
2020-08-13 17:05:19 +00:00
|
|
|
|
}
|
2020-08-15 17:11:52 +00:00
|
|
|
|
deriving stock ( Show, Generic )
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving anyclass NFData
|
2020-08-10 22:07:09 +00:00
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
instance Act (T ( ℝ 2 )) (PointData params) where
|
2022-02-13 16:30:54 +00:00
|
|
|
|
v • ( dat@( PointData { pointCoords = p } ) ) =
|
|
|
|
|
dat { pointCoords = v • p }
|
|
|
|
|
|
2020-08-20 01:57:26 +00:00
|
|
|
|
data BrushPointData
|
|
|
|
|
= BrushPointData
|
|
|
|
|
{ brushPointState :: FocusState }
|
|
|
|
|
deriving stock ( Show, Generic )
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving anyclass NFData
|
2020-08-20 01:57:26 +00:00
|
|
|
|
|
2020-08-13 17:05:19 +00:00
|
|
|
|
data FocusState
|
|
|
|
|
= Normal
|
|
|
|
|
| Hover
|
|
|
|
|
| Selected
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving stock ( Show, Eq, Generic )
|
|
|
|
|
deriving anyclass NFData
|
2020-08-10 22:07:09 +00:00
|
|
|
|
|
2020-09-08 23:23:25 +00:00
|
|
|
|
instance Semigroup FocusState where
|
|
|
|
|
Selected <> _ = Selected
|
|
|
|
|
Normal <> s = s
|
|
|
|
|
_ <> Selected = Selected
|
|
|
|
|
s <> Normal = s
|
|
|
|
|
_ <> _ = Hover
|
|
|
|
|
instance Monoid FocusState where
|
|
|
|
|
mempty = Normal
|
|
|
|
|
|
2020-09-01 19:56:59 +00:00
|
|
|
|
emptyDocument :: Text -> Unique -> Document
|
|
|
|
|
emptyDocument docName unique =
|
|
|
|
|
Document
|
2020-09-10 16:43:42 +00:00
|
|
|
|
{ displayName = docName
|
|
|
|
|
, mbFilePath = Nothing
|
2023-01-09 03:27:08 +00:00
|
|
|
|
, viewportCenter = ℝ2 0 0
|
2020-09-10 16:43:42 +00:00
|
|
|
|
, zoomFactor = 1
|
|
|
|
|
, documentUnique = unique
|
|
|
|
|
, documentContent =
|
|
|
|
|
Content
|
|
|
|
|
{ unsavedChanges = False
|
|
|
|
|
, latestChange = "New document"
|
2021-04-26 12:34:34 +00:00
|
|
|
|
, strokes = Seq.empty
|
2020-09-10 16:43:42 +00:00
|
|
|
|
, guides = Map.empty
|
|
|
|
|
}
|
2020-09-01 19:56:59 +00:00
|
|
|
|
}
|
2020-09-08 23:23:25 +00:00
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
data HoverContext
|
2023-01-09 03:27:08 +00:00
|
|
|
|
= MouseHover !( ℝ 2 )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
| RectangleHover !AABB
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
2020-09-08 23:23:25 +00:00
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
instance Act ( T ( ℝ 2 ) ) HoverContext where
|
2020-09-08 23:23:25 +00:00
|
|
|
|
v • MouseHover p = MouseHover ( v • p )
|
|
|
|
|
v • RectangleHover ( AABB p1 p2 ) = RectangleHover ( AABB ( v • p1 ) ( v • p2 ) )
|
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
instance Act ( T ( ℝ 2 ) ) ( Maybe HoverContext ) where
|
2020-09-08 23:23:25 +00:00
|
|
|
|
(•) v = fmap ( v • )
|
|
|
|
|
|
|
|
|
|
class Hoverable a where
|
|
|
|
|
hovered :: Maybe HoverContext -> Double -> a -> FocusState
|
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
instance Hoverable ( ℝ 2 ) where
|
2020-09-08 23:23:25 +00:00
|
|
|
|
hovered Nothing _ _ = Normal
|
|
|
|
|
hovered ( Just ( MouseHover p ) ) zoom q
|
2023-01-09 03:27:08 +00:00
|
|
|
|
| quadrance @( T ( ℝ 2 ) ) p q * zoom ^ ( 2 :: Int ) < 16
|
2020-09-08 23:23:25 +00:00
|
|
|
|
= Hover
|
|
|
|
|
| otherwise
|
|
|
|
|
= Normal
|
2023-01-09 03:27:08 +00:00
|
|
|
|
hovered ( Just ( RectangleHover ( AABB ( ℝ2 x1 y1 ) ( ℝ2 x2 y2 ) ) ) ) _ ( ℝ2 x y )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
| x >= x1 && x <= x2 && y >= y1 && y <= y2
|
|
|
|
|
= Hover
|
|
|
|
|
| otherwise
|
|
|
|
|
= Normal
|
|
|
|
|
|
2020-11-12 17:34:46 +00:00
|
|
|
|
class HasSelection pt where
|
|
|
|
|
_selection :: Lens' pt FocusState
|
|
|
|
|
instance HasSelection ( PointData brushParams ) where
|
|
|
|
|
_selection = field' @"pointState"
|
|
|
|
|
instance HasSelection BrushPointData where
|
|
|
|
|
_selection = field' @"brushPointState"
|
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
_coords :: Lens' ( PointData brushParams ) ( ℝ 2 )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
_coords = field' @"pointCoords"
|
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
coords :: PointData brushParams -> ℝ 2
|
2020-11-12 17:34:46 +00:00
|
|
|
|
coords = view _coords
|
|
|
|
|
|
|
|
|
|
data FocusDifference
|
|
|
|
|
= DifferentFocus
|
|
|
|
|
| SameFocus
|
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
|
|
|
|
|
|
|
|
|
instance Semigroup FocusDifference where
|
|
|
|
|
SameFocus <> SameFocus = SameFocus
|
|
|
|
|
_ <> _ = DifferentFocus
|
|
|
|
|
|
|
|
|
|
instance Monoid FocusDifference where
|
|
|
|
|
mempty = SameFocus
|
|
|
|
|
|
|
|
|
|
instance Group FocusDifference where
|
|
|
|
|
invert = id
|
|
|
|
|
|
|
|
|
|
data DiffPointData diffBrushParams
|
|
|
|
|
= DiffPointData
|
2023-01-09 03:27:08 +00:00
|
|
|
|
{ diffVector :: !( T ( ℝ 2 ) )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
, diffParams :: !diffBrushParams
|
|
|
|
|
, diffState :: !FocusDifference
|
|
|
|
|
}
|
|
|
|
|
deriving stock ( Show, Generic, Generic1, Functor, Foldable, Traversable )
|
|
|
|
|
deriving anyclass ( NFData, NFData1 )
|
|
|
|
|
|
|
|
|
|
instance Module Double diffBrushParams => Semigroup ( DiffPointData diffBrushParams ) where
|
|
|
|
|
DiffPointData v1 p1 s1 <> DiffPointData v2 p2 s2 =
|
|
|
|
|
DiffPointData ( v1 <> v2 ) ( p1 ^+^ p2 ) ( s1 <> s2 )
|
|
|
|
|
instance Module Double diffBrushParams => Monoid ( DiffPointData diffBrushParams ) where
|
|
|
|
|
mempty = DiffPointData mempty origin mempty
|
|
|
|
|
instance Module Double diffBrushParams => Group ( DiffPointData diffBrushParams ) where
|
|
|
|
|
invert ( DiffPointData v1 p1 s1 ) =
|
2023-01-08 16:16:14 +00:00
|
|
|
|
DiffPointData ( invert v1 ) ( -1 *^ p1 ) ( invert s1 )
|
2020-11-12 17:34:46 +00:00
|
|
|
|
|
|
|
|
|
instance ( Module Double diffBrushParams, Act diffBrushParams brushParams )
|
|
|
|
|
=> Act ( DiffPointData diffBrushParams ) ( PointData brushParams ) where
|
|
|
|
|
(•) ( DiffPointData { diffVector = dp, diffParams = db, diffState = focusDiff } )
|
|
|
|
|
= over _coords ( dp • )
|
|
|
|
|
. over ( field' @"brushParams" ) ( db • )
|
|
|
|
|
. ( case focusDiff of { SameFocus -> id; DifferentFocus -> set ( field' @"pointState" ) Normal } )
|
|
|
|
|
instance ( Module Double diffBrushParams, Torsor diffBrushParams brushParams )
|
|
|
|
|
=> Torsor ( DiffPointData diffBrushParams ) ( PointData brushParams ) where
|
|
|
|
|
( PointData { pointCoords = p1, brushParams = b1, pointState = s1 } ) <-- ( PointData { pointCoords = p2, brushParams = b2, pointState = s2 } ) =
|
|
|
|
|
DiffPointData
|
|
|
|
|
{ diffVector = p1 <-- p2
|
|
|
|
|
, diffParams = b1 <-- b2
|
|
|
|
|
, diffState = if s1 == s2 then SameFocus else DifferentFocus
|
|
|
|
|
}
|
|
|
|
|
instance Module Double brushParams => Module Double ( DiffPointData brushParams ) where
|
|
|
|
|
origin = mempty
|
|
|
|
|
(^+^) = (<>)
|
|
|
|
|
x ^-^ y = x <> invert y
|
|
|
|
|
d *^ DiffPointData v1 p1 s1 = DiffPointData ( d *^ v1 ) ( d *^ p1 ) s1
|
|
|
|
|
|
|
|
|
|
|
2020-09-08 23:23:25 +00:00
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
-- Guides.
|
|
|
|
|
|
|
|
|
|
data Guide
|
|
|
|
|
= Guide
|
2023-01-09 03:27:08 +00:00
|
|
|
|
{ guidePoint :: !( ℝ 2 ) -- ^ point on the guide line
|
|
|
|
|
, guideNormal :: !( T ( ℝ 2 ) ) -- ^ /normalised/ normal vector of the guide
|
2020-09-08 23:23:25 +00:00
|
|
|
|
, guideFocus :: !FocusState
|
|
|
|
|
, guideUnique :: Unique
|
|
|
|
|
}
|
2020-09-10 16:43:42 +00:00
|
|
|
|
deriving stock ( Show, Generic )
|
|
|
|
|
deriving anyclass NFData
|
2020-09-08 23:23:25 +00:00
|
|
|
|
|
2022-02-11 21:05:13 +00:00
|
|
|
|
data Ruler
|
|
|
|
|
= RulerCorner
|
|
|
|
|
| LeftRuler
|
|
|
|
|
| TopRuler
|
|
|
|
|
deriving stock Show
|
|
|
|
|
|
2020-09-08 23:23:25 +00:00
|
|
|
|
-- | Try to select a guide at the given document coordinates.
|
2023-01-09 03:27:08 +00:00
|
|
|
|
selectedGuide :: ℝ 2 -> Document -> Maybe Guide
|
2020-09-10 16:43:42 +00:00
|
|
|
|
selectedGuide c ( Document { zoomFactor, documentContent = Content { guides } } ) =
|
2020-09-08 23:23:25 +00:00
|
|
|
|
\case { Min ( Arg _ g ) -> g } <$> foldMap ( selectGuide_maybe c zoomFactor ) guides
|
|
|
|
|
|
2023-01-09 03:27:08 +00:00
|
|
|
|
selectGuide_maybe :: ℝ 2 -> Double -> Guide -> Maybe ( ArgMin Double Guide )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
selectGuide_maybe c zoom guide@( Guide { guidePoint = p, guideNormal = n } )
|
|
|
|
|
| sqDist * zoom ^ ( 2 :: Int ) < 4
|
|
|
|
|
= Just ( Min ( Arg sqDist guide ) )
|
|
|
|
|
| otherwise
|
|
|
|
|
= Nothing
|
|
|
|
|
where
|
|
|
|
|
t :: Double
|
|
|
|
|
t = ( c --> p ) ^.^ n
|
|
|
|
|
sqDist :: Double
|
|
|
|
|
sqDist = t ^ ( 2 :: Int ) / squaredNorm n
|
|
|
|
|
|
|
|
|
|
-- | Add new guide after a mouse drag from a ruler area.
|
2023-01-09 03:27:08 +00:00
|
|
|
|
addGuide :: UniqueSupply -> Ruler -> ℝ 2 -> Document -> STM Document
|
2020-11-12 17:34:46 +00:00
|
|
|
|
addGuide uniqueSupply ruler p doc = ( `runReaderT` uniqueSupply ) $ ( field' @"documentContent" . field' @"guides" ) insertNewGuides doc
|
2020-09-08 23:23:25 +00:00
|
|
|
|
where
|
2020-11-12 17:34:46 +00:00
|
|
|
|
insertNewGuides :: Map Unique Guide -> ReaderT UniqueSupply STM ( Map Unique Guide )
|
2020-09-08 23:23:25 +00:00
|
|
|
|
insertNewGuides gs = case ruler of
|
|
|
|
|
RulerCorner
|
|
|
|
|
-> do
|
2020-11-12 17:34:46 +00:00
|
|
|
|
uniq1 <- freshUnique
|
|
|
|
|
uniq2 <- freshUnique
|
2020-09-08 23:23:25 +00:00
|
|
|
|
let
|
|
|
|
|
guide1, guide2 :: Guide
|
2023-01-09 03:27:08 +00:00
|
|
|
|
guide1 = Guide { guidePoint = p, guideNormal = V2 0 1, guideFocus = Normal, guideUnique = uniq1 }
|
|
|
|
|
guide2 = Guide { guidePoint = p, guideNormal = V2 1 0, guideFocus = Normal, guideUnique = uniq2 }
|
2020-09-08 23:23:25 +00:00
|
|
|
|
pure ( Map.insert uniq2 guide2 . Map.insert uniq1 guide1 $ gs )
|
|
|
|
|
TopRuler
|
|
|
|
|
-> do
|
2020-11-12 17:34:46 +00:00
|
|
|
|
uniq1 <- freshUnique
|
2020-09-08 23:23:25 +00:00
|
|
|
|
let
|
|
|
|
|
guide1 :: Guide
|
2023-01-09 03:27:08 +00:00
|
|
|
|
guide1 = Guide { guidePoint = p, guideNormal = V2 0 1, guideFocus = Normal, guideUnique = uniq1 }
|
2020-09-08 23:23:25 +00:00
|
|
|
|
pure ( Map.insert uniq1 guide1 gs )
|
|
|
|
|
LeftRuler
|
|
|
|
|
-> do
|
2020-11-12 17:34:46 +00:00
|
|
|
|
uniq2 <- freshUnique
|
2020-09-08 23:23:25 +00:00
|
|
|
|
let
|
|
|
|
|
guide2 :: Guide
|
2023-01-09 03:27:08 +00:00
|
|
|
|
guide2 = Guide { guidePoint = p, guideNormal = V2 1 0, guideFocus = Normal, guideUnique = uniq2 }
|
2020-09-08 23:23:25 +00:00
|
|
|
|
pure ( Map.insert uniq2 guide2 gs )
|
|
|
|
|
|
|
|
|
|
instance Hoverable Guide where
|
|
|
|
|
hovered ( Just ( MouseHover c ) ) zoom guide
|
|
|
|
|
| Just _ <- selectGuide_maybe c zoom guide
|
|
|
|
|
= Hover
|
|
|
|
|
| otherwise
|
|
|
|
|
= Normal
|
|
|
|
|
hovered _ _ _ = Normal
|