use the correct tangent vector for stroke joins

This commit is contained in:
sheaf 2020-08-29 16:34:51 +02:00
parent 3916fe2656
commit d4561ad549
2 changed files with 37 additions and 22 deletions

View file

@ -102,7 +102,7 @@ testDocuments = IntMap.fromList
{ displayName = "Document 1"
, filePath = Nothing
, unsavedChanges = False
, strokes = [ Stroke ( circle ( PointData Normal ( razor $ BrushPointData Normal ) ) ) "Circle" True ( unsafeUnique 0 )
, strokes = [ Stroke ( circle ( PointData Normal ( rect $ BrushPointData Normal ) ) ) "Circle" True ( unsafeUnique 0 )
]
, bounds = AABB ( Point2D 0 0 ) ( Point2D 100 100 )
, viewportCenter = Point2D 50 50
@ -157,6 +157,18 @@ razor d = Seq.fromList
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
--------------------------------------------------------------------------------
main :: IO ()

View file

@ -137,7 +137,7 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
_ -> False
fwd, bwd :: Seq ( StrokePoint () )
( fwd, bwd ) = go tgt_start spt0 ( spt1 :<| spts )
( fwd, bwd ) = go spt0 ( spt1 :<| spts )
(<~>)
:: ( Monoid a, Monoid b )
@ -149,6 +149,7 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
-- 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).
joinAndContinue :: Vector2D Double -> StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
--joinAndContinue tgt sp sps = go sp sps
joinAndContinue _ _ Empty
-- Closed curve.
| isClosed
@ -164,52 +165,52 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
)
joinAndContinue tgt sp0 ( sp1 :<| sps )
| tgt' `parallel` tgt
= go tgt sp0 ( sp1 :<| sps )
= go sp0 ( sp1 :<| sps )
| let
ptOffset :: Vector2D Double
ptOffset = Point2D 0 0 --> coords sp0
= ( ptOffset joinWithBrush ( withTangent tgt brush0 ) ( withTangent tgt' brush0 ) brush0
, ptOffset joinWithBrush ( withTangent ( (-1) *^ tgt' ) brush0 ) ( withTangent ( (-1) *^ tgt ) brush0 ) brush0
)
<~> go tgt sp0 ( sp1 :<| sps )
<~> go sp0 ( sp1 :<| sps )
where
tgt' :: Vector2D Double
tgt' = coords sp0 --> coords sp1
brush0 :: Seq ( StrokePoint () )
brush0 = removePointData $ brushShape @x sp0
go :: Vector2D Double -> StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
go _ _ Empty = ( Empty, Empty )
go :: StrokePoint d -> Seq ( StrokePoint d ) -> ( Seq ( StrokePoint () ), Seq ( StrokePoint () ) )
go _ Empty = ( Empty, Empty )
-- Line.
go tgt0 sp0 ( sp1 :<| sps )
go sp0 ( sp1 :<| sps )
| PathPoint {} <- sp1
, let
p0, p1, fop0, fop1, bop0, bop1 :: Point2D Double
p0 = coords sp0
p1 = coords sp1
fop0 = offset ( withTangent tgt0 ( brushShape @x sp0 ) ) p0
fop1 = offset ( withTangent tgt0 ( brushShape @x sp1 ) ) p1
bop0 = offset ( withTangent ( (-1) *^ tgt0 ) ( brushShape @x sp0 ) ) p0
bop1 = offset ( withTangent ( (-1) *^ tgt0 ) ( brushShape @x sp1 ) ) p1
tgt1 :: Vector2D Double
tgt1 = p0 --> p1
fop0 = offset ( withTangent tgt ( brushShape @x sp0 ) ) p0
fop1 = offset ( withTangent tgt ( brushShape @x sp1 ) ) p1
bop0 = offset ( withTangent ( (-1) *^ tgt ) ( brushShape @x sp0 ) ) p0
bop1 = offset ( withTangent ( (-1) *^ tgt ) ( brushShape @x sp1 ) ) p1
tgt :: Vector2D Double
tgt = p0 --> p1
brush :: Double -> Seq ( StrokePoint () )
brush t = lerpBrush t ( brushShape @x sp0 ) ( brushShape @x sp1 )
fwdPts, bwdPts :: [ Point2D Double ]
fwdPts
= [ ( offset $ withTangent tgt0 $ brush t )
= [ ( offset $ withTangent tgt $ brush t )
( lerp @( Vector2D Double ) t p0 p1 )
| t <- [0.1,0.2..0.9]
]
bwdPts
= [ ( offset $ withTangent ( (-1) *^ tgt0 ) $ brush t )
= [ ( offset $ withTangent ( (-1) *^ tgt ) $ brush t )
( lerp @( Vector2D Double ) t p0 p1 )
| t <- [0.9,0.8..0.1]
]
= ( fitCurve fop0 tgt0 fwdPts fop1 tgt0, fitCurve bop1 ( (-1) *^ tgt1 ) bwdPts bop0 ( (-1) *^ tgt0 ) )
<~> joinAndContinue tgt1 sp1 sps
= ( fitCurve fop0 tgt fwdPts fop1 tgt, fitCurve bop1 ( (-1) *^ tgt ) bwdPts bop0 ( (-1) *^ tgt ) )
<~> joinAndContinue tgt sp1 sps
-- Quadratic Bézier curve.
go tgt0 sp0 ( sp1 :<| sp2 :<| sps )
go sp0 ( sp1 :<| sp2 :<| sps )
| ControlPoint {} <- sp1
, PathPoint {} <- sp2
, let
@ -221,7 +222,8 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
fop2 = offset ( withTangent tgt2 ( brushShape @x sp2 ) ) p2
bop0 = offset ( withTangent ( (-1) *^ tgt0 ) ( brushShape @x sp0 ) ) p0
bop2 = offset ( withTangent ( (-1) *^ tgt2 ) ( brushShape @x sp2 ) ) p2
tgt2 :: Vector2D Double
tgt0, tgt2 :: Vector2D Double
tgt0 = p0 --> p1
tgt2 = p1 --> p2
bez :: Quadratic.Bezier ( Point2D Double )
bez = Quadratic.Bezier { .. }
@ -242,7 +244,7 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
= ( fitCurve fop0 tgt0 fwdPts fop2 tgt2, fitCurve bop2 ( (-1) *^ tgt2 ) bwdPts bop0 ( (-1) *^ tgt0 ) )
<~> joinAndContinue tgt2 sp2 sps
-- Cubic Bézier curve.
go tgt0 sp0 ( sp1 :<| sp2 :<| sp3 :<| sps )
go sp0 ( sp1 :<| sp2 :<| sp3 :<| sps )
| ControlPoint {} <- sp1
, ControlPoint {} <- sp2
, PathPoint {} <- sp3
@ -256,7 +258,8 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
fop3 = offset ( withTangent tgt3 ( brushShape @x sp3 ) ) p3
bop0 = offset ( withTangent ( (-1) *^ tgt0 ) ( brushShape @x sp0 ) ) p0
bop3 = offset ( withTangent ( (-1) *^ tgt3 ) ( brushShape @x sp3 ) ) p3
tgt3 :: Vector2D Double
tgt0, tgt3 :: Vector2D Double
tgt0 = p0 --> p1
tgt3 = p2 --> p3
bez :: Cubic.Bezier ( Point2D Double )
bez = Cubic.Bezier { .. }
@ -276,7 +279,7 @@ stroke allPts@( spt0 :<| spt1 :<| spts )
]
= ( fitCurve fop0 tgt0 fwdPts fop3 tgt3, fitCurve bop3 ( (-1) *^ tgt3 ) bwdPts bop0 ( (-1) *^ tgt0 ) )
<~> joinAndContinue tgt3 sp3 sps
go _ p0 ps = error $ "stroke: unrecognised stroke type\n" <> show ( p0 :<| ps )
go p0 ps = error $ "stroke: unrecognised stroke type\n" <> show ( p0 :<| ps )
-----------------------------------
-- Various utility functions