Created
September 16, 2020 14:02
Interactive Bézier curves demo in Haskell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE NoMonomorphismRestriction #-} | |
import Control.Monad (when) | |
import Control.Arrow ((***)) | |
import Data.List (findIndex) | |
import Graphics.UI.SDL as SDL hiding (init) | |
import Graphics.UI.SDL.Primitives (circle, line) | |
dim = 400 | |
main = withInit [InitVideo] $ do | |
w <- setVideoMode dim dim 32 [] | |
enableEvent SDLMouseMotion False | |
setCaption "Bézier Curves" "Bézier Curves" | |
loop w [] | |
plot w ps = do | |
fillRect w (Just $ Rect 0 0 dim dim) $ Pixel 0xFF222255 | |
mapM_ (f 2 0xFFFFFFFF . zz) [head ps, last ps] | |
when b $ mapM_ (f 3 0x888888FF . zz) controls | |
when b $ mapM_ (f 2 0xBBBBBBFF . zz) controls | |
where | |
f r c (x,y) = circle w x y r $ Pixel c | |
(b, controls) = (length ps > 2, tail $ init ps) | |
limn w [_] = SDL.flip w | |
limn w ((a,b):(x,y):ps) = do | |
line w a b x y $ Pixel 0xFFFFFFFF | |
limn w $ (x,y) : ps | |
loop w ps = do | |
delay 128 | |
event <- pollEvent | |
case event of | |
KeyUp (Keysym SDLK_ESCAPE _ _) -> return () | |
MouseButtonDown x y _ -> click x y | |
_ -> loop w ps | |
where | |
click x y = let p = rr (x,y) in | |
case findIndex ((10 >) . dist p) ps of | |
Just i -> drag w i ps | |
Nothing -> do | |
let ps' = p : ps | |
plot w ps' >> SDL.flip w | |
when (length ps' > 2) $ render w ps' | |
loop w ps' | |
drag w i ps = do | |
delay 16 | |
(x,y,_) <- getMouseState | |
event <- pollEvent | |
let ps' = swap i ps $ rr (x,y) | |
plot w ps' | |
when (length ps' > 2) $ render w ps' | |
case event of | |
MouseButtonUp x y _ -> loop w ps' | |
_ -> drag w i ps' | |
render w ps = limn w $ map zz curve | |
where | |
curve = map (casteljau ps) [0, 0.001.. 1] | |
casteljau [p] t = p | |
casteljau ps t = casteljau ps' t | |
where | |
ps' = zipWith (g t) ps $ tail ps | |
g t (a,b) (c,d) = (f t a c, f t b d) | |
f t a b = (1 - t) * a + t * b | |
swap 0 ps p = p : tail ps | |
swap i ps p = take i ps ++ p : drop (i+1) ps | |
dist (a,b) (c,d) = sqrt $ (a-c)^2 + (b-d)^2 | |
rr = fromIntegral *** fromIntegral | |
zz = round *** round |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment