fixup curve drag code

This commit is contained in:
sheaf 2024-11-02 10:53:54 +01:00
parent bb237d99c8
commit 7a82470db9
2 changed files with 104 additions and 111 deletions

View file

@ -50,9 +50,8 @@ import Data.List
import Data.List.NonEmpty import Data.List.NonEmpty
( NonEmpty ) ( NonEmpty )
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
( cons, singleton )
import Data.Maybe import Data.Maybe
( fromMaybe, isJust, listToMaybe, mapMaybe ) ( catMaybes, fromMaybe, isJust, listToMaybe, mapMaybe )
import Data.Proxy import Data.Proxy
( Proxy(..) ) ( Proxy(..) )
import Data.Semigroup import Data.Semigroup
@ -82,7 +81,6 @@ import qualified Data.IntMap.Strict as IntMap
import Data.Sequence import Data.Sequence
( Seq(..) ) ( Seq(..) )
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
( empty, index, length, reverse, singleton, zipWith )
-- deepseq -- deepseq
import Control.DeepSeq import Control.DeepSeq
@ -732,8 +730,8 @@ joinWithBrush brush startTgt endTgt = joinBetweenOffsets brush startOffset endOf
joinBetweenOffsets :: forall crvData ptData. HasType ( 2 ) ptData => Spline Closed crvData ptData -> Offset -> Offset -> SplinePts Open joinBetweenOffsets :: forall crvData ptData. HasType ( 2 ) ptData => Spline Closed crvData ptData -> Offset -> Offset -> SplinePts Open
joinBetweenOffsets joinBetweenOffsets
spline spline
( Offset { offsetIndex = i1, offsetParameter = mb_t1 } ) ( Offset { offsetIndex = i1, offsetParameter = fromMaybe 1 -> t1 } )
( Offset { offsetIndex = i2, offsetParameter = mb_t2 } ) ( Offset { offsetIndex = i2, offsetParameter = fromMaybe 0 -> t2 } )
| i2 > i1 | i2 > i1
= let = let
pcs, lastAndRest :: Maybe ( SplinePts Open ) pcs, lastAndRest :: Maybe ( SplinePts Open )
@ -749,13 +747,16 @@ joinBetweenOffsets
, dropFirstPiece =<< pcs , dropFirstPiece =<< pcs
, fst <$> ( splitFirstPiece t2 =<< lastAndRest ) , fst <$> ( splitFirstPiece t2 =<< lastAndRest )
] ]
| i2 == i1 && mb_t2 >= mb_t1 | i2 == i1 && t2 >= t1
= let = let
pcs :: Maybe ( SplinePts Open ) pcs :: Maybe ( SplinePts Open )
pcs = discardCurveData <$> dropCurves i1 openSpline pcs = discardCurveData <$> dropCurves i1 openSpline
in in
maybe empty fst maybe empty fst
( splitFirstPiece t2 . snd =<< ( splitFirstPiece t1 =<< pcs ) ) ( splitFirstPiece ( ( t2 - t1 ) / ( 1 - t1 ) ) . snd =<< ( splitFirstPiece t1 =<< pcs ) )
-- NB: we want to split: 0 -- t1 -- t2 -- 1
-- First split at t1, then split again but after rescaling
-- using t |-> ( t - t1 ) / ( 1 - t1 ).
| otherwise | otherwise
= let = let
start, middle, end :: SplinePts Open start, middle, end :: SplinePts Open
@ -764,21 +765,17 @@ joinBetweenOffsets
$ first ( splitSplineAt i2 ) $ first ( splitSplineAt i2 )
$ splitSplineAt i1 openSpline $ splitSplineAt i1 openSpline
in in
fromMaybe empty $ sconcat $ NE.fromList $ catMaybes $
mconcat [ snd <$> splitFirstPiece t1 start
[ snd <$> splitFirstPiece t1 start , dropFirstPiece start
, dropFirstPiece start , Just middle
, Just middle , fst <$> splitFirstPiece t2 ( if i1 == i2 then start else end )
, fst <$> splitFirstPiece t2 end ]
]
where where
empty :: SplinePts Open empty :: SplinePts Open
empty = Spline { splineStart = 2 0 0, splineCurves = OpenCurves Empty } empty = Spline { splineStart = 2 0 0, splineCurves = OpenCurves Empty }
openSpline :: Spline Open crvData ptData openSpline :: Spline Open crvData ptData
openSpline = adjustSplineType spline openSpline = adjustSplineType spline
t1, t2 :: Double
t1 = fromMaybe 0.5 mb_t1
t2 = fromMaybe 0.5 mb_t2
discardCurveData discardCurveData

View file

@ -23,7 +23,7 @@ module MetaBrush.Action
import Control.Arrow import Control.Arrow
( second ) ( second )
import Control.Monad import Control.Monad
( guard, when ) ( guard, when, unless )
import Control.Monad.ST import Control.Monad.ST
( RealWorld ) ( RealWorld )
import Data.Foldable import Data.Foldable
@ -349,105 +349,101 @@ dragMoveSelect c ( Document { documentContent, documentMetadata } ) =
-> Int -> Int
-> PointData brushParams -> Curve clo' ( CurveData RealWorld ) ( PointData brushParams ) -> PointData brushParams -> Curve clo' ( CurveData RealWorld ) ( PointData brushParams )
-> State ( Maybe DragMoveSelect ) () -> State ( Maybe DragMoveSelect ) ()
dragSelectSplineCurve uniq start i sp0 crv = do dragSelectSplineCurve uniq start i sp0 = \case
LineTo sp1 ( CurveData { curveIndex } ) -> do
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp1
unlessPrevDrag $ do
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
let
t :: Double
p :: 2
( t, p ) =
closestPointOnSegment @( T ( 2 ) )
c
( Segment ( coords sp0 ) ( coords $ fromNextPoint start sp1 ) )
guard ( inSelectionRange p )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
Bezier2To sp1 sp2 ( CurveData { curveIndex } ) -> do
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez2Cp ) ) sp1
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp2
unlessPrevDrag $ do
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
let
bez :: Quadratic.Bezier ( 2 )
bez = Quadratic.Bezier ( coords sp0 ) ( coords sp1 ) ( coords $ fromNextPoint start sp2 )
sq_d :: Double
t :: Double
Min ( Arg sq_d (t, _) ) =
Quadratic.closestPoint @( T ( 2 ) ) bez c
guard ( t > 0 && t < 1 && sq_d * zoomFactor ^ ( 2 :: Int ) < 16 )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
Bezier3To sp1 sp2 sp3 ( CurveData { curveIndex } ) -> do
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez3Cp1 ) ) sp1
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez3Cp2 ) ) sp2
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp3
unlessPrevDrag $ do
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
let
bez :: Cubic.Bezier ( 2 )
bez = Cubic.Bezier ( coords sp0 ) ( coords sp1 ) ( coords sp2 ) ( coords $ fromNextPoint start sp3 )
sq_d :: Double
t :: Double
Min ( Arg sq_d (t, _) ) =
Cubic.closestPoint @( T ( 2 ) ) bez c
guard ( t > 0 && t < 1 && sq_d * zoomFactor ^ ( 2 :: Int ) < 16 )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
unlessPrevDrag :: State ( Maybe DragMoveSelect ) () -> State ( Maybe DragMoveSelect ) ()
unlessPrevDrag action = do
mbPrevDrag <- State.get
unless ( isJust mbPrevDrag ) $
action
unlessPrevPointDrag :: State ( Maybe DragMoveSelect ) () -> State ( Maybe DragMoveSelect ) ()
unlessPrevPointDrag action = do
mbPrevDrag <- State.get mbPrevDrag <- State.get
case mbPrevDrag of case mbPrevDrag of
-- If we've clicked on a point, don't try anything further.
Just ( ClickedOnPoint {} ) -> return () Just ( ClickedOnPoint {} ) -> return ()
_notPointDrag -> do _ -> action
-- If we've already initiated a curve drag, don't try
-- another curve drag. However, we allow overridding a curve drag
-- by a point drag.
let clickedOnCurve = isJust mbPrevDrag
case crv of
LineTo sp1 ( CurveData { curveIndex } ) -> do
-- Check endpoints first.
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp1
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
guard ( not clickedOnCurve )
let
t :: Double
p :: 2
( t, p ) =
closestPointOnSegment @( T ( 2 ) )
c
( Segment ( coords sp0 ) ( coords $ fromNextPoint start sp1 ) )
guard ( inSelectionRange p )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
Bezier2To sp1 sp2 ( CurveData { curveIndex } ) -> do
-- Check endpoints first.
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez2Cp ) ) sp1
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp2
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
guard ( not clickedOnCurve )
let
bez :: Quadratic.Bezier ( 2 )
bez = Quadratic.Bezier ( coords sp0 ) ( coords sp1 ) ( coords $ fromNextPoint start sp2 )
sq_d :: Double
t :: Double
Min ( Arg sq_d (t, _) ) =
Quadratic.closestPoint @( T ( 2 ) ) bez c
guard ( t > 0 && t < 1 && sq_d * zoomFactor ^ ( 2 :: Int ) < 16 )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
Bezier3To sp1 sp2 sp3 ( CurveData { curveIndex } ) -> do
-- Check endpoints first.
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez3Cp1 ) ) sp1
dragSelectSplinePoint uniq ( PointIndex curveIndex ( ControlPoint Bez3Cp2 ) ) sp2
traverse_ ( dragSelectSplinePoint uniq ( PointIndex curveIndex PathPoint ) ) sp3
let
mbCurveDrag :: Maybe DragMoveSelect
mbCurveDrag = do
guard ( not clickedOnCurve )
let
bez :: Cubic.Bezier ( 2 )
bez = Cubic.Bezier ( coords sp0 ) ( coords sp1 ) ( coords sp2 ) ( coords $ fromNextPoint start sp3 )
sq_d :: Double
t :: Double
Min ( Arg sq_d (t, _) ) =
Cubic.closestPoint @( T ( 2 ) ) bez c
guard ( t > 0 && t < 1 && sq_d * zoomFactor ^ ( 2 :: Int ) < 16 )
pure $
ClickedOnCurve
{ dragStrokeUnique = uniq
, dragCurve = curveIndex
, dragCurveIndex = i
, dragCurveParameter = t
}
for_ mbCurveDrag $ State.put . Just
dragSelectSplinePoint :: Unique -> PointIndex -> PointData brushParams -> State ( Maybe DragMoveSelect ) () dragSelectSplinePoint :: Unique -> PointIndex -> PointData brushParams -> State ( Maybe DragMoveSelect ) ()
dragSelectSplinePoint u ptIx pt = dragSelectSplinePoint u ptIx pt =
when ( inSelectionRange ( coords pt ) ) do unlessPrevPointDrag $ when ( inSelectionRange ( coords pt ) ) do
mbPrevDrag <- State.get let
case mbPrevDrag of drag :: DragMoveSelect
Just ( ClickedOnPoint {} ) -> return () drag = ClickedOnPoint
_ -> do { dragPoint = ( u, ptIx )
let , dragPointWasSelected =
drag :: DragMoveSelect elemStrokePoint u ptIx ( selectedPoints documentMetadata )
drag = ClickedOnPoint }
{ dragPoint = ( u, ptIx ) State.put $ Just drag
, dragPointWasSelected =
elemStrokePoint u ptIx ( selectedPoints documentMetadata )
}
State.put $ Just drag
addOrRemove :: SelectionMode addOrRemove :: SelectionMode