metabrush/src/metabrushes/MetaBrush/Document.hs

146 lines
4.1 KiB
Haskell
Raw Normal View History

module MetaBrush.Document where
2020-08-04 06:15:06 +00:00
2020-09-01 19:56:59 +00:00
-- base
import GHC.Generics
( Generic )
-- containers
import Data.Map.Strict
( Map )
import qualified Data.Map.Strict as Map
import Data.Set
( Set )
import qualified Data.Set as Set
2020-09-10 16:43:42 +00:00
-- deepseq
import Control.DeepSeq
( NFData(..) )
2020-08-10 14:38:27 +00:00
-- text
import Data.Text
( Text )
2020-11-12 17:34:46 +00:00
-- brush-strokes
import Math.Linear
( (..), T(..) )
2020-08-10 14:38:27 +00:00
-- MetaBrush
import MetaBrush.Layer
( LayerMetadata, emptyHierarchy )
import MetaBrush.Stroke
( StrokeHierarchy, PointIndex )
2020-08-16 22:09:16 +00:00
import MetaBrush.Unique
( Unique )
2020-08-10 14:38:27 +00:00
2020-08-04 06:15:06 +00:00
--------------------------------------------------------------------------------
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
{ documentContent :: !DocumentContent
-- ^ Main document content, which we keep track throughout history.
, documentMetadata :: !DocumentMetadata
-- ^ Metadata about the document, that we don't track throughout history.
2020-09-10 16:43:42 +00:00
}
deriving stock ( Show, Generic )
deriving anyclass NFData
newtype Zoom = Zoom { zoomFactor :: Double }
deriving stock ( Show, Eq, Ord )
deriving newtype NFData
-- | A collection of points, indexed first by the stroke they belong to
-- and then their position in that stroke.
newtype StrokePoints = StrokePoints { strokePoints :: Map Unique ( Set PointIndex ) }
deriving newtype ( Eq, Show, NFData )
-- Invariant: the sets are never empty.
instance Semigroup StrokePoints where
( StrokePoints pts1 ) <> ( StrokePoints pts2 ) =
StrokePoints ( Map.unionWith Set.union pts1 pts2 )
instance Monoid StrokePoints where
mempty = StrokePoints Map.empty
-- | Remove the second set of points from the first.
differenceStrokePoints :: StrokePoints -> StrokePoints -> StrokePoints
differenceStrokePoints ( StrokePoints pts1 ) ( StrokePoints pts2 ) =
StrokePoints $
Map.differenceWith remove pts1 pts2
where
remove :: Set PointIndex -> Set PointIndex -> Maybe ( Set PointIndex )
remove old new =
let new' = old Set.\\ new
in if null new'
then Nothing
else Just new'
noStrokePoints :: StrokePoints -> Bool
noStrokePoints ( StrokePoints pts ) = null pts
elemStrokePoint :: Unique -> PointIndex -> StrokePoints -> Bool
elemStrokePoint u i ( StrokePoints pts ) =
case Map.lookup u pts of
Nothing -> False
Just is -> Set.member i is
-- | Metadata about a document and its content, that we don't track through
-- history.
data DocumentMetadata =
Metadata
{ documentName :: !Text
, documentFilePath :: !( Maybe FilePath )
, viewportCenter :: !( 2 )
, documentZoom :: !Zoom
, documentGuides :: !( Map Unique Guide )
, layerMetadata :: !LayerMetadata
, selectedPoints :: !StrokePoints
2021-04-26 15:18:48 +00:00
}
deriving stock ( Show, Generic )
deriving anyclass NFData
-- | Main content of document (data which we keep track of throughout history).
data DocumentContent
= Content
{ unsavedChanges :: !Bool
-- ^ Whether this current content is unsaved.
, strokeHierarchy :: !StrokeHierarchy
-- ^ Hierarchical structure of layers and groups.
2020-08-13 17:05:19 +00:00
}
2020-11-12 17:34:46 +00:00
deriving stock ( Show, Generic )
deriving anyclass NFData
-- | A guide, i.e. a horizontal or vertical line used for alignment.
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-10 16:43:42 +00:00
deriving stock ( Show, Generic )
deriving anyclass NFData
emptyDocument :: Text -> Document
emptyDocument docName =
Document
{ documentContent = emptyDocumentContent
, documentMetadata = emptyDocumentMetadata docName
}
emptyDocumentContent :: DocumentContent
emptyDocumentContent =
Content
{ strokeHierarchy = emptyHierarchy
, unsavedChanges = False
}
emptyDocumentMetadata :: Text -> DocumentMetadata
emptyDocumentMetadata docName =
Metadata
{ documentName = docName
, documentFilePath = Nothing
, viewportCenter = 2 0 0
, documentZoom = Zoom { zoomFactor = 1 }
, documentGuides = Map.empty
, layerMetadata = mempty
, selectedPoints = mempty
}