summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2019-05-17 17:55:23 -0400
committerJoe Crayne <joe@jerkface.net>2019-05-17 17:55:23 -0400
commit19b7e91dd12f9d22e4a2991341c1622ec83968cb (patch)
tree227537f18dc6b25b2bad1ca2023c39442cbda87a
parent330649949aea845e0472d0d99fbd30fb00bd6183 (diff)
Enable higher-precision rotation.
-rw-r--r--MeshSketch.hs141
1 files changed, 81 insertions, 60 deletions
diff --git a/MeshSketch.hs b/MeshSketch.hs
index 8f38863..273c3dc 100644
--- a/MeshSketch.hs
+++ b/MeshSketch.hs
@@ -1,7 +1,8 @@
1{-# LANGUAGE OverloadedLabels #-} 1{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE OverloadedLabels #-}
2{-# LANGUAGE OverloadedStrings #-} 4{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE LambdaCase #-} 5{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE RecordWildCards #-}
5module MeshSketch where 6module MeshSketch where
6 7
7import Codec.Picture as Juicy 8import Codec.Picture as Juicy
@@ -16,6 +17,7 @@ import Data.Text (Text)
16import Data.Map.Strict (Map) 17import Data.Map.Strict (Map)
17import qualified Data.Map.Strict as Map 18import qualified Data.Map.Strict as Map
18import qualified Data.Vector as V 19import qualified Data.Vector as V
20import qualified Data.Vector.Generic as G (init)
19import Foreign.Marshal.Array 21import Foreign.Marshal.Array
20import Foreign.Storable 22import Foreign.Storable
21import GI.Gdk 23import GI.Gdk
@@ -24,7 +26,7 @@ import GI.GLib.Constants
24import GI.Gtk hiding (IsWindow,windowFullscreen,windowUnfullscreen) 26import GI.Gtk hiding (IsWindow,windowFullscreen,windowUnfullscreen)
25import LambdaCube.GL as LC 27import LambdaCube.GL as LC
26import LambdaCube.GL.Mesh as LC 28import LambdaCube.GL.Mesh as LC
27import Numeric.LinearAlgebra hiding ((<>)) 29import Numeric.LinearAlgebra as Math hiding ((<>))
28import System.Environment 30import System.Environment
29import System.IO 31import System.IO
30import System.IO.Error 32import System.IO.Error
@@ -86,16 +88,17 @@ data State = State
86 88
87data Camera = Camera 89data Camera = Camera
88 { camHeightAngle :: Float 90 { camHeightAngle :: Float
89 , camTarget :: Vector Float 91 , camTarget :: Vector Float -- 3-vector
90 , camDirection :: Vector Float 92 , camDirection :: Vector Float -- 3-vector
91 , camDistance :: Float 93 , camDistance :: Float
92 , camWidth :: Float 94 , camWidth :: Float
93 , camHeight :: Float 95 , camHeight :: Float
94 , camUp :: Vector Float 96 , camUp :: Vector Float -- 3-vector
95 , camWorldToScreen :: Maybe (Matrix Float) 97 , camWorldToScreen :: Maybe (Matrix Float) -- 4×4
96 , camScreenToWorld :: Maybe (Matrix Float) 98 , camScreenToWorld :: Maybe (Matrix Float) -- 4×4
97 } 99 }
98 100
101camPos :: Camera -> Vector Float
99camPos c = camTarget c - scale (camDistance c) (camDirection c) 102camPos c = camTarget c - scale (camDistance c) (camDirection c)
100 103
101initCamera :: Camera 104initCamera :: Camera
@@ -122,6 +125,13 @@ viewProjection c
122 pos = camTarget c - scale (camDistance c) (camDirection c) 125 pos = camTarget c - scale (camDistance c) (camDirection c)
123 proj = perspective 0.1 100 (camHeightAngle c) (camWidth c / camHeight c) 126 proj = perspective 0.1 100 (camHeightAngle c) (camWidth c / camHeight c)
124 127
128realToFracVector :: ( Real a
129 , Fractional b
130 , Storable a
131 , Storable b
132 ) => Vector a -> Vector b
133realToFracVector v = Math.fromList $ map realToFrac $ Math.toList v
134
125realToFracMatrix :: (Real a, Fractional t, Element t, Element a) => Matrix a -> Matrix t 135realToFracMatrix :: (Real a, Fractional t, Element t, Element a) => Matrix a -> Matrix t
126realToFracMatrix m = fromLists $ map realToFrac <$> toLists m 136realToFracMatrix m = fromLists $ map realToFrac <$> toLists m
127 137
@@ -337,12 +347,12 @@ onRender w realized gl = do
337 347
338onResize :: GLArea -> Realized -> Int32 -> Int32 -> IO () 348onResize :: GLArea -> Realized -> Int32 -> Int32 -> IO ()
339onResize glarea realized w h = do 349onResize glarea realized w h = do
340 let storage = stStorage realized
341 -- Plenty of options here. I went with the last one. 350 -- Plenty of options here. I went with the last one.
342 -- 1. gLContextGetWindow :: HasCallStack => GLContext -> IO (Maybe Window) 351 -- 1. gLContextGetWindow :: HasCallStack => GLContext -> IO (Maybe Window)
343 -- 2. getGLContextWindow :: GLContext -> IO (Maybe Window) 352 -- 2. getGLContextWindow :: GLContext -> IO (Maybe Window)
344 -- 3. widgetGetWindow :: HasCallStack => GLArea -> IO (Maybe Window) 353 -- 3. widgetGetWindow :: HasCallStack => GLArea -> IO (Maybe Window)
345 widgetGetWindow glarea >>= mapM_ (\win -> do 354 mwin <- widgetGetWindow glarea
355 forM_ mwin $ \win -> do
346 (wd,ht) <- do wd <- windowGetWidth win 356 (wd,ht) <- do wd <- windowGetWidth win
347 ht <- windowGetHeight win 357 ht <- windowGetHeight win
348 return (fromIntegral wd,fromIntegral ht) 358 return (fromIntegral wd,fromIntegral ht)
@@ -352,11 +362,19 @@ onResize glarea realized w h = do
352 , camWorldToScreen = Nothing 362 , camWorldToScreen = Nothing
353 , camScreenToWorld = Nothing 363 , camScreenToWorld = Nothing
354 } 364 }
355 LC.setScreenSize (stStorage realized) wd ht) 365 LC.setScreenSize (stStorage realized) wd ht
356 366
357-- This computes a point in world coordinates on the view screen if 367-- This computes a point in world coordinates on the view screen if
358-- we assume the camera is located at the origin. 368-- we assume the camera is located at the origin.
359computeDirection :: Camera -> Double -> Double -> Vector Float 369computeDirection :: Camera -> Double -> Double -> Vector Float
370computeDirection cam h k | Just pv <- camScreenToWorld cam =
371 let d0 = fromList [ 2 * realToFrac h/camWidth cam - 1
372 , 1 - 2 * realToFrac k/camHeight cam
373 , 1
374 , 1
375 ] :: Vector Float
376 d1 = pv #> d0
377 in scale (1 /(d1!3) ) $ G.init d1
360computeDirection cam h k = 378computeDirection cam h k =
361 let d̂ = camDirection cam -- forward 379 let d̂ = camDirection cam -- forward
362 û = camUp cam -- upward 380 û = camUp cam -- upward
@@ -366,7 +384,11 @@ computeDirection cam h k =
366 xd = (camHeight cam / 2) / tan (camHeightAngle cam / 2) 384 xd = (camHeight cam / 2) / tan (camHeightAngle cam / 2)
367 in scale xr r̂ + scale xu û + scale xd d̂ 385 in scale xr r̂ + scale xu û + scale xd d̂
368 386
369rotate :: Float -> Vector Float -> Matrix Float 387rotate :: ( Floating a
388 , Math.Container Vector a
389 , Indexable (Vector a) a
390 , Normed (Vector a)
391 ) => a -> Vector a -> Matrix a
370rotate cosθ u = (3><3) 392rotate cosθ u = (3><3)
371 [ cosθ + ux² mcosθ , (uy.uy)mcosθ - uz sinθ , (ux.uz)mcosθ + uy sinθ 393 [ cosθ + ux² mcosθ , (uy.uy)mcosθ - uz sinθ , (ux.uz)mcosθ + uy sinθ
372 , (uy.ux)mcosθ + uz sinθ , cosθ + uy² mcosθ , (uy.uz)mcosθ - ux sinθ 394 , (uy.ux)mcosθ + uz sinθ , cosθ + uy² mcosθ , (uy.uz)mcosθ - ux sinθ
@@ -383,19 +405,27 @@ rotate cosθ u = (3><3)
383 uy² = uy . uy 405 uy² = uy . uy
384 uz² = uz . uz 406 uz² = uz . uz
385 407
408updateCameraRotation :: IsWidget a => a -> State -> Double -> Double -> IO ()
386updateCameraRotation w st h k = do 409updateCameraRotation w st h k = do
387 m <- readIORef (stDragFrom st) 410 m <- readIORef (stDragFrom st)
388 forM_ m $ \(df,cam) -> do 411 forM_ m $ \(df0,cam) -> do
389 let d̂ = camDirection cam -- forward 412 let d̂ = camDirection cam -- forward
390 û = camUp cam -- upward 413 û = camUp cam -- upward
391 r̂ = d̂ `cross` û -- rightward 414 -- r̂ = d̂ `cross` û -- rightward
392 -- fr = df `dot` r̂ 415#if 0
393 -- fu = df `dot` û 416 -- This turned out to be pointless.
394 -- fd = df `dot` d̂ 417 promote :: Vector Float -> Vector Double
395 dt = computeDirection cam h k 418 promote = realToFracVector
396 -- tr = dt `dot` r̂ 419 demote :: Vector Double -> Vector Float
397 -- tu = dt `dot` û 420 demote = realToFracVector
398 -- td = dt `dot` d̂ 421#else
422 promote = id
423 demote = id
424 {-# INLINE promote #-}
425 {-# INLINE demote #-}
426#endif
427 df = promote df0
428 dt = promote $ computeDirection cam h k
399 cosθ = dot df dt / realToFrac (norm_2 df) / realToFrac (norm_2 dt) 429 cosθ = dot df dt / realToFrac (norm_2 df) / realToFrac (norm_2 dt)
400 axis0 = df `cross` dt 430 axis0 = df `cross` dt
401 small x = abs x < 0.00001 431 small x = abs x < 0.00001
@@ -404,8 +434,8 @@ updateCameraRotation w st h k = do
404 then fromList [0,1,0] 434 then fromList [0,1,0]
405 else axis0 435 else axis0
406 cam' = cam 436 cam' = cam
407 { camDirection = d̂ <# rotate cosθ axis 437 { camDirection = demote $ promote d̂ <# rotate cosθ axis
408 , camUp = û <# rotate cosθ axis 438 , camUp = demote $ promote û <# rotate cosθ axis
409 , camWorldToScreen = Nothing 439 , camWorldToScreen = Nothing
410 , camScreenToWorld = Nothing 440 , camScreenToWorld = Nothing
411 } 441 }
@@ -414,6 +444,7 @@ updateCameraRotation w st h k = do
414 forM_ mwin $ \win -> 444 forM_ mwin $ \win ->
415 windowInvalidateRect win Nothing False 445 windowInvalidateRect win Nothing False
416 446
447sanitizeCamera :: State -> IO ()
417sanitizeCamera st = do 448sanitizeCamera st = do
418 modifyIORef (stCamera st) $ \cam -> 449 modifyIORef (stCamera st) $ \cam ->
419 let d = camDirection cam 450 let d = camDirection cam
@@ -435,10 +466,29 @@ sanitizeCamera st = do
435 , camScreenToWorld = Nothing 466 , camScreenToWorld = Nothing
436 } 467 }
437 468
469worldCoordinates :: State -> Double -> Double -> IO (Vector Float)
470worldCoordinates st h k = do
471 pv <- atomicModifyIORef' (stCamera st) projectionView
472 cam <- readIORef (stCamera st)
473 let d0 = fromList [ 2 * realToFrac h/camWidth cam - 1
474 , 1 - 2 * realToFrac k/camHeight cam
475 , 1
476 , 1
477 ] :: Vector Float
478 d1 = pv #> d0
479 return $ scale (1 /(d1!3) ) d1
480
481pushRing :: IsWidget w => w -> State -> Double -> Double -> IO (Vector Float)
482pushRing w st h k = do
483 d <- worldCoordinates st h k
484 Just win <- getWidgetWindow w
485 pushBack (stRingBuffer st) (d!0) (d!1) (d!2)
486 windowInvalidateRect win Nothing False
487 return d
488
438 489
439onEvent :: IsWidget w => w -> Realized -> Event -> IO Bool 490onEvent :: IsWidget w => w -> Realized -> Event -> IO Bool
440onEvent w realized ev = do 491onEvent w realized ev = do
441 let st = stState realized
442 msrc <- eventGetSourceDevice ev 492 msrc <- eventGetSourceDevice ev
443 inputSource <- forM msrc $ \src -> do 493 inputSource <- forM msrc $ \src -> do
444 src <- get src #inputSource 494 src <- get src #inputSource
@@ -446,7 +496,7 @@ onEvent w realized ev = do
446 etype <- get ev #type 496 etype <- get ev #type
447 -- putStrLn $ "onEvent! " ++ show (etype,inputSource) 497 -- putStrLn $ "onEvent! " ++ show (etype,inputSource)
448 let put x = putStrLn (show inputSource ++ " " ++ show x) 498 let put x = putStrLn (show inputSource ++ " " ++ show x)
449 let st = stState realized 499 st = stState realized
450 case etype of 500 case etype of
451 501
452 EventTypeMotionNotify -> do 502 EventTypeMotionNotify -> do
@@ -457,19 +507,7 @@ onEvent w realized ev = do
457 mev <- get ev #motion 507 mev <- get ev #motion
458 h <- get mev #x 508 h <- get mev #x
459 k <- get mev #y 509 k <- get mev #y
460 -- let d = camPos cam + computeDirection cam h k 510 d <- pushRing w st h k
461 pv <- atomicModifyIORef' (stCamera st) projectionView
462 cam <- readIORef (stCamera st)
463 let d0 = fromList [ 2 * realToFrac h/camWidth cam - 1
464 , 1 - 2 * realToFrac k/camHeight cam
465 , 1
466 , 1
467 ] :: Vector Float
468 d1 = pv #> d0
469 d = scale (1 /(d1!3) ) d1
470 pushBack (stRingBuffer st) (d!0) (d!1) (d!2)
471 Just win <- getWidgetWindow w
472 windowInvalidateRect win Nothing False
473 put (etype,(h,k),d) 511 put (etype,(h,k),d)
474 _ -> do 512 _ -> do
475 mev <- get ev #motion 513 mev <- get ev #motion
@@ -486,16 +524,7 @@ onEvent w realized ev = do
486 bev <- get ev #button 524 bev <- get ev #button
487 h <- get bev #x 525 h <- get bev #x
488 k <- get bev #y 526 k <- get bev #y
489 pv <- atomicModifyIORef' (stCamera st) projectionView 527 d <- pushRing w st h k
490 cam <- readIORef (stCamera st)
491 let d0 = fromList [ 2 * realToFrac h/camWidth cam - 1
492 , 1 - 2 * realToFrac k/camHeight cam
493 , 1
494 , 1
495 ] :: Vector Float
496 d1 = pv #> d0
497 d = scale (1 /(d1!3) ) d1
498 pushBack (stRingBuffer st) (d!0) (d!1) (d!2)
499 Just win <- getWidgetWindow w 528 Just win <- getWidgetWindow w
500 windowInvalidateRect win Nothing False 529 windowInvalidateRect win Nothing False
501 put (etype,(h,k),d) 530 put (etype,(h,k),d)
@@ -503,8 +532,9 @@ onEvent w realized ev = do
503 bev <- get ev #button 532 bev <- get ev #button
504 h <- get bev #x 533 h <- get bev #x
505 k <- get bev #y 534 k <- get bev #y
535 d <- G.init <$> worldCoordinates st h k
506 cam <- readIORef (stCamera st) 536 cam <- readIORef (stCamera st)
507 let d = computeDirection cam h k 537 -- let d = computeDirection cam h k
508 writeIORef (stDragFrom st) $ Just (d,cam) 538 writeIORef (stDragFrom st) $ Just (d,cam)
509 put (etype,(h,k),d) 539 put (etype,(h,k),d)
510 return () 540 return ()
@@ -516,16 +546,7 @@ onEvent w realized ev = do
516 bev <- get ev #button 546 bev <- get ev #button
517 h <- get bev #x 547 h <- get bev #x
518 k <- get bev #y 548 k <- get bev #y
519 pv <- atomicModifyIORef' (stCamera st) projectionView 549 d <- pushRing w st h k
520 cam <- readIORef (stCamera st)
521 let d0 = fromList [ 2 * realToFrac h/camWidth cam - 1
522 , 1 - 2 * realToFrac k/camHeight cam
523 , 1
524 , 1
525 ] :: Vector Float
526 d1 = pv #> d0
527 d = scale (1 /(d1!3) ) d1
528 pushBack (stRingBuffer st) (d!0) (d!1) (d!2)
529 Just win <- getWidgetWindow w 550 Just win <- getWidgetWindow w
530 windowInvalidateRect win Nothing False 551 windowInvalidateRect win Nothing False
531 _ -> do 552 _ -> do