mirror of
https://gitlab.com/sheaf/metabrush.git
synced 2024-11-05 23:03:38 +00:00
fix brush stroke point order for open brush path
* also: add some preset brushes
This commit is contained in:
parent
62bb1451c5
commit
9bde44ed42
|
@ -98,7 +98,8 @@ executable MetaBrush
|
||||||
Main.hs
|
Main.hs
|
||||||
|
|
||||||
other-modules:
|
other-modules:
|
||||||
MetaBrush.Asset.Colours
|
MetaBrush.Asset.Brushes
|
||||||
|
, MetaBrush.Asset.Colours
|
||||||
, MetaBrush.Asset.Cursor
|
, MetaBrush.Asset.Cursor
|
||||||
, MetaBrush.Asset.InfoBar
|
, MetaBrush.Asset.InfoBar
|
||||||
, MetaBrush.Asset.Logo
|
, MetaBrush.Asset.Logo
|
||||||
|
|
66
app/Main.hs
66
app/Main.hs
|
@ -58,6 +58,8 @@ import Math.Bezier.Stroke
|
||||||
( StrokePoint(..) )
|
( StrokePoint(..) )
|
||||||
import Math.Vector2D
|
import Math.Vector2D
|
||||||
( Point2D(..) )
|
( Point2D(..) )
|
||||||
|
import MetaBrush.Asset.Brushes
|
||||||
|
( ellipse, rect )
|
||||||
import MetaBrush.Asset.Colours
|
import MetaBrush.Asset.Colours
|
||||||
( getColours )
|
( getColours )
|
||||||
import MetaBrush.Asset.Logo
|
import MetaBrush.Asset.Logo
|
||||||
|
@ -102,7 +104,7 @@ testDocuments = IntMap.fromList
|
||||||
{ displayName = "Document 1"
|
{ displayName = "Document 1"
|
||||||
, filePath = Nothing
|
, filePath = Nothing
|
||||||
, unsavedChanges = False
|
, unsavedChanges = False
|
||||||
, strokes = [ Stroke ( circle ( PointData Normal ( rect $ BrushPointData Normal ) ) ) "Circle" True ( unsafeUnique 0 )
|
, strokes = [ Stroke ( ellipse 150 100 ( PointData Normal ( rect 30 6 $ BrushPointData Normal ) ) ) "Ellipse" True ( unsafeUnique 0 )
|
||||||
]
|
]
|
||||||
, bounds = AABB ( Point2D 0 0 ) ( Point2D 100 100 )
|
, bounds = AABB ( Point2D 0 0 ) ( Point2D 100 100 )
|
||||||
, viewportCenter = Point2D 50 50
|
, viewportCenter = Point2D 50 50
|
||||||
|
@ -112,62 +114,20 @@ testDocuments = IntMap.fromList
|
||||||
{ displayName = "Document 2"
|
{ displayName = "Document 2"
|
||||||
, filePath = Nothing
|
, filePath = Nothing
|
||||||
, unsavedChanges = True
|
, unsavedChanges = True
|
||||||
, strokes = [ ]
|
, strokes = [ Stroke linePts "Line" True ( unsafeUnique 1 ) ]
|
||||||
, bounds = AABB ( Point2D 0 0 ) ( Point2D 50 50 )
|
, bounds = AABB ( Point2D 0 0 ) ( Point2D 50 50 )
|
||||||
, viewportCenter = Point2D 10 10
|
, viewportCenter = Point2D 0 0
|
||||||
, zoomFactor = 0.25
|
, zoomFactor = 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
circle :: forall a. a -> Seq ( StrokePoint a )
|
|
||||||
circle d = Seq.fromList
|
|
||||||
[ pp ( Point2D 0 1 )
|
|
||||||
, cp ( Point2D a 1 )
|
|
||||||
, cp ( Point2D 1 a )
|
|
||||||
, pp ( Point2D 1 0 )
|
|
||||||
, cp ( Point2D 1 (-a) )
|
|
||||||
, cp ( Point2D a (-1) )
|
|
||||||
, pp ( Point2D 0 (-1) )
|
|
||||||
, cp ( Point2D (-a) (-1) )
|
|
||||||
, cp ( Point2D (-1) (-a) )
|
|
||||||
, pp ( Point2D (-1) 0 )
|
|
||||||
, cp ( Point2D (-1) a )
|
|
||||||
, cp ( Point2D (-a) 1 )
|
|
||||||
, pp ( Point2D 0 1 )
|
|
||||||
]
|
|
||||||
where
|
where
|
||||||
a :: Double
|
linePts :: Seq ( StrokePoint PointData )
|
||||||
a = 0.551915024494
|
linePts = Seq.fromList
|
||||||
pp, cp :: Point2D Double -> StrokePoint a
|
[ PathPoint ( Point2D 0 (-100) ) ( PointData Normal ( ellipse 30 8 $ BrushPointData Normal ) )
|
||||||
pp p = PathPoint ( fmap ( * 100 ) p ) d
|
, ControlPoint ( Point2D 0 ( -30) ) ( PointData Normal ( ellipse 25 6 $ BrushPointData Normal ) )
|
||||||
cp p = ControlPoint ( fmap ( * 100 ) p ) d
|
, ControlPoint ( Point2D 0 ( 30) ) ( PointData Normal ( ellipse 15 6 $ BrushPointData Normal ) )
|
||||||
|
, PathPoint ( Point2D 0 ( 100) ) ( PointData Normal ( ellipse 5 2 $ BrushPointData Normal ) )
|
||||||
razor :: forall a. a -> Seq ( StrokePoint a )
|
]
|
||||||
razor d = Seq.fromList
|
|
||||||
[ pp ( Point2D 30 0 )
|
|
||||||
, cp ( Point2D 30 -6 )
|
|
||||||
, cp ( Point2D -30 -6 )
|
|
||||||
, pp ( Point2D -30 0 )
|
|
||||||
, cp ( Point2D -30 3 )
|
|
||||||
, cp ( Point2D 30 3 )
|
|
||||||
, pp ( Point2D 30 0 )
|
|
||||||
]
|
|
||||||
where
|
|
||||||
pp, cp :: Point2D Double -> StrokePoint a
|
|
||||||
pp p = PathPoint p d
|
|
||||||
cp p = ControlPoint p d
|
|
||||||
|
|
||||||
rect :: forall a. a -> Seq ( StrokePoint a )
|
|
||||||
rect d = Seq.fromList
|
|
||||||
[ pp ( Point2D 20 5 )
|
|
||||||
, pp ( Point2D 20 -5 )
|
|
||||||
, pp ( Point2D -20 -5 )
|
|
||||||
, pp ( Point2D -20 5 )
|
|
||||||
, pp ( Point2D 20 5 )
|
|
||||||
]
|
|
||||||
where
|
|
||||||
pp :: Point2D Double -> StrokePoint a
|
|
||||||
pp p = PathPoint p d
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
70
src/app/MetaBrush/Asset/Brushes.hs
Normal file
70
src/app/MetaBrush/Asset/Brushes.hs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{-# LANGUAGE NegativeLiterals #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
|
module MetaBrush.Asset.Brushes
|
||||||
|
( ellipse, blob, rect )
|
||||||
|
where
|
||||||
|
|
||||||
|
-- containers
|
||||||
|
import Data.Sequence
|
||||||
|
( Seq(..) )
|
||||||
|
import qualified Data.Sequence as Seq
|
||||||
|
( fromList )
|
||||||
|
|
||||||
|
-- MetaBrush
|
||||||
|
import Math.Bezier.Stroke
|
||||||
|
( StrokePoint(..) )
|
||||||
|
import Math.Vector2D
|
||||||
|
( Point2D(..) )
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ellipse :: forall d. Double -> Double -> d -> Seq ( StrokePoint d )
|
||||||
|
ellipse w h d = Seq.fromList
|
||||||
|
[ pp ( Point2D 0 1 )
|
||||||
|
, cp ( Point2D a 1 )
|
||||||
|
, cp ( Point2D 1 a )
|
||||||
|
, pp ( Point2D 1 0 )
|
||||||
|
, cp ( Point2D 1 (-a) )
|
||||||
|
, cp ( Point2D a (-1) )
|
||||||
|
, pp ( Point2D 0 (-1) )
|
||||||
|
, cp ( Point2D (-a) (-1) )
|
||||||
|
, cp ( Point2D (-1) (-a) )
|
||||||
|
, pp ( Point2D (-1) 0 )
|
||||||
|
, cp ( Point2D (-1) a )
|
||||||
|
, cp ( Point2D (-a) 1 )
|
||||||
|
, pp ( Point2D 0 1 )
|
||||||
|
]
|
||||||
|
where
|
||||||
|
a :: Double
|
||||||
|
a = 0.551915024494
|
||||||
|
pp, cp :: Point2D Double -> StrokePoint d
|
||||||
|
pp ( Point2D x y ) = PathPoint ( Point2D ( w * x ) ( h * y ) ) d
|
||||||
|
cp ( Point2D x y ) = ControlPoint ( Point2D ( w * x ) ( h * y ) ) d
|
||||||
|
|
||||||
|
blob :: forall d. Double -> Double -> d -> Seq ( StrokePoint d )
|
||||||
|
blob w h d = Seq.fromList
|
||||||
|
[ pp ( Point2D 1 0 )
|
||||||
|
, cp ( Point2D 1 -1 )
|
||||||
|
, cp ( Point2D -1 -1 )
|
||||||
|
, pp ( Point2D -1 0 )
|
||||||
|
, cp ( Point2D -1 1 )
|
||||||
|
, cp ( Point2D 1 1 )
|
||||||
|
, pp ( Point2D 1 0 )
|
||||||
|
]
|
||||||
|
where
|
||||||
|
pp, cp :: Point2D Double -> StrokePoint d
|
||||||
|
pp ( Point2D x y ) = PathPoint ( Point2D ( w * x ) ( h * y ) ) d
|
||||||
|
cp ( Point2D x y ) = ControlPoint ( Point2D ( w * x ) ( h * y ) ) d
|
||||||
|
|
||||||
|
rect :: forall d. Double -> Double -> d -> Seq ( StrokePoint d )
|
||||||
|
rect w h d = Seq.fromList
|
||||||
|
[ pp ( Point2D 1 1 )
|
||||||
|
, pp ( Point2D 1 -1 )
|
||||||
|
, pp ( Point2D -1 -1 )
|
||||||
|
, pp ( Point2D -1 1 )
|
||||||
|
, pp ( Point2D 1 1 )
|
||||||
|
]
|
||||||
|
where
|
||||||
|
pp :: Point2D Double -> StrokePoint d
|
||||||
|
pp ( Point2D x y ) = PathPoint ( Point2D ( w * x ) ( h * y ) ) d
|
|
@ -115,18 +115,17 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
|
||||||
| isClosed
|
| isClosed
|
||||||
= Right ( fwdPts, bwdPts )
|
= Right ( fwdPts, bwdPts )
|
||||||
| otherwise
|
| otherwise
|
||||||
= Left ( fwdPts <> bwdPts )
|
= Left ( startingCap <> fwdPts <> bwdPts )
|
||||||
where
|
where
|
||||||
|
|
||||||
startOffset, endOffset :: Vector2D Double
|
startOffset, endOffset :: Vector2D Double
|
||||||
tgt_start, tgt_end :: Vector2D Double
|
tgt_start, tgt_end :: Vector2D Double
|
||||||
|
brush_start, brush_end :: Seq ( StrokePoint x )
|
||||||
startOffset = Point2D 0 0 --> coords spt0
|
startOffset = Point2D 0 0 --> coords spt0
|
||||||
tgt_start = coords spt0 --> coords spt1
|
tgt_start = coords spt0 --> coords spt1
|
||||||
( tgt_end, endOffset ) = case allPts of
|
( tgt_end, endOffset, brush_end ) = case allPts of
|
||||||
_ :|> sptnm1 :|> sptn -> ( coords sptnm1 --> coords sptn, Point2D 0 0 --> coords sptn )
|
_ :|> sptnm1 :|> sptn -> ( coords sptnm1 --> coords sptn, Point2D 0 0 --> coords sptn, brushShape sptn )
|
||||||
_ -> error "impossible"
|
_ -> error "impossible"
|
||||||
|
|
||||||
brush_start :: Seq ( StrokePoint x )
|
|
||||||
brush_start = brushShape spt0
|
brush_start = brushShape spt0
|
||||||
|
|
||||||
isClosed :: Bool
|
isClosed :: Bool
|
||||||
|
@ -149,19 +148,6 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
|
||||||
-- Connecting paths at a point of discontinuity of the tangent vector direction (G1 discontinuity).
|
-- Connecting paths at a point of discontinuity of the tangent vector direction (G1 discontinuity).
|
||||||
-- This happens at corners of the brush path (including endpoints of an open brush path, where the tangent flips direction).
|
-- This happens at corners of the brush path (including endpoints of an open brush path, where the tangent flips direction).
|
||||||
joinAndContinue :: Vector2D Double -> StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
|
joinAndContinue :: Vector2D Double -> StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
|
||||||
joinAndContinue _ _ Empty
|
|
||||||
-- Closed curve.
|
|
||||||
| isClosed
|
|
||||||
= if parallel tgt_start tgt_end
|
|
||||||
then ( Empty, Empty )
|
|
||||||
else ( startOffset • joinWithBrush ( withTangent tgt_start brush_start ) ( withTangent tgt_end brush_start ) brush_start
|
|
||||||
, startOffset • joinWithBrush ( withTangent ( (-1) *^ tgt_start ) brush_start ) ( withTangent ( (-1) *^ tgt_end ) brush_start ) brush_start
|
|
||||||
)
|
|
||||||
-- Open curve.
|
|
||||||
| otherwise
|
|
||||||
= ( endOffset • joinWithBrush ( withTangent tgt_end brush_start ) ( withTangent ( (-1) *^ tgt_end ) brush_start ) brush_start
|
|
||||||
, startOffset • joinWithBrush ( withTangent ( (-1) *^ tgt_start ) brush_start ) ( withTangent tgt_start brush_start ) brush_start
|
|
||||||
)
|
|
||||||
joinAndContinue tgt sp0 ( sp1 :<| sps )
|
joinAndContinue tgt sp0 ( sp1 :<| sps )
|
||||||
| tgt' `parallel` tgt
|
| tgt' `parallel` tgt
|
||||||
= go sp0 ( sp1 :<| sps )
|
= go sp0 ( sp1 :<| sps )
|
||||||
|
@ -177,6 +163,24 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
|
||||||
tgt' = coords sp0 --> coords sp1
|
tgt' = coords sp0 --> coords sp1
|
||||||
brush0 :: Seq ( StrokePoint () )
|
brush0 :: Seq ( StrokePoint () )
|
||||||
brush0 = removePointData $ brushShape @x sp0
|
brush0 = removePointData $ brushShape @x sp0
|
||||||
|
joinAndContinue _ _ Empty
|
||||||
|
-- Closed curve.
|
||||||
|
| isClosed
|
||||||
|
= if parallel tgt_start tgt_end
|
||||||
|
then ( Empty, Empty )
|
||||||
|
else ( startOffset • joinWithBrush ( withTangent tgt_start brush_start ) ( withTangent tgt_end brush_start ) brush_start
|
||||||
|
, startOffset • joinWithBrush ( withTangent ( (-1) *^ tgt_start ) brush_start ) ( withTangent ( (-1) *^ tgt_end ) brush_start ) brush_start
|
||||||
|
)
|
||||||
|
-- Open curve.
|
||||||
|
| otherwise
|
||||||
|
= ( endOffset • joinWithBrush ( withTangent tgt_end brush_end ) ( withTangent ( (-1) *^ tgt_end ) brush_end ) brush_end
|
||||||
|
, Empty -- handled separately: see 'startingCap' below
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Final cap for an open curve. Handled separately for correct stroke order.
|
||||||
|
startingCap :: Seq ( StrokePoint () )
|
||||||
|
startingCap
|
||||||
|
= startOffset • joinWithBrush ( withTangent ( (-1) *^ tgt_start ) brush_start ) ( withTangent tgt_start brush_start ) brush_start
|
||||||
|
|
||||||
go :: StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
|
go :: StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
|
||||||
go _ Empty = ( Empty, Empty )
|
go _ Empty = ( Empty, Empty )
|
||||||
|
|
Loading…
Reference in a new issue