mirror of
https://github.com/reanimate/reanimate.git
synced 2026-09-13 09:02:54 +00:00
Morphology (#112)
* Move hmatrix dependency out of reanimate's core. * Move chiphunk to a separate package.
This commit is contained in:
parent
d1b0a50e51
commit
0d4a5801de
31 changed files with 1408 additions and 1117 deletions
|
|
@ -11,10 +11,6 @@ jobs:
|
|||
BUILD: stack
|
||||
STACK_YAML: stack.yaml
|
||||
ARGS: --pedantic
|
||||
stack-no-hmatrix:
|
||||
BUILD: stack
|
||||
STACK_YAML: stack.yaml
|
||||
ARGS: --flag reanimate:disable-hmatrix --flag reanimate:-test
|
||||
stack-lts-15:
|
||||
BUILD: stack
|
||||
STACK_YAML: stack-lts-15.yaml
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ jobs:
|
|||
displayName: Cache stack root
|
||||
- bash: |
|
||||
set -o xtrace
|
||||
$CONDA/condabin/conda.bat install --yes --quiet -c msys2 m2w64-gcc-libgfortran m2w64-openblas
|
||||
# $CONDA/condabin/conda.bat install --yes --quiet -c msys2 m2w64-gcc-libgfortran m2w64-openblas
|
||||
|
||||
cp $CONDA/Library/mingw-w64/bin/libgfortran-3.dll $CONDA/Library/mingw-w64/bin/libgfortran.dll
|
||||
# cp $CONDA/Library/mingw-w64/bin/libgfortran-3.dll $CONDA/Library/mingw-w64/bin/libgfortran.dll
|
||||
|
||||
curl -sSkL http://www.stackage.org/stack/windows-x86_64 -o /usr/bin/stack.zip
|
||||
unzip -o /usr/bin/stack.zip -d /usr/bin/
|
||||
|
|
@ -44,13 +44,16 @@ jobs:
|
|||
displayName: 'Setup'
|
||||
- bash: |
|
||||
set -o xtrace
|
||||
stack --install-ghc build $ARGS --no-keep-going --fast --only-dependencies --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
stack --install-ghc test $ARGS --no-keep-going --fast --flag reanimate:test --only-dependencies --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
#stack --install-ghc build $ARGS --no-keep-going --fast --only-dependencies --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
stack --install-ghc build $ARGS --no-keep-going --fast --only-dependencies
|
||||
#stack --install-ghc test $ARGS --no-keep-going --fast --flag reanimate:test --only-dependencies --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
stack --install-ghc test $ARGS --no-keep-going --fast --flag reanimate:test --only-dependencies
|
||||
displayName: 'Install dependencies'
|
||||
- bash: |
|
||||
set -o xtrace
|
||||
# stack ./examples/counter.hs check
|
||||
stack test --flag reanimate:test --test-arguments="-j2" --fast --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
# stack test --flag reanimate:test --test-arguments="-j2" --fast --flag reanimate:enable-hmatrix --flag hmatrix:openblas --extra-lib-dirs=$CONDA/Library/mingw-w64/bin
|
||||
stack test --flag reanimate:test --test-arguments="-j2" --fast
|
||||
displayName: 'Build & Test'
|
||||
- bash: |
|
||||
# stack haddock --no-haddock-deps
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main (main) where
|
||||
|
||||
import Chiphunk.Low
|
||||
import Control.Monad
|
||||
import Graphics.SvgTree (Tree)
|
||||
import Linear.V2
|
||||
import Reanimate
|
||||
import Reanimate.Chiphunk
|
||||
import Reanimate.PolyShape
|
||||
import System.IO.Unsafe
|
||||
|
||||
test :: Animation
|
||||
test = unsafePerformIO $ do
|
||||
bodyStore <- newBodyStore
|
||||
let gravity = Vect 0 (-1)
|
||||
|
||||
-- Create an empty space.
|
||||
space <- spaceNew
|
||||
spaceCollisionSlop space $= (screenWidth/2560)
|
||||
spaceGravity space $= gravity
|
||||
|
||||
-- Add a static line segment shape for the ground.
|
||||
-- We'll make it slightly tilted so the ball will roll off.
|
||||
-- We attach it to a static body to tell Chipmunk it shouldn't be movable.
|
||||
static <- get $ spaceStaticBody space
|
||||
ground <- segmentShapeNew static
|
||||
(Vect (-screenWidth/2) 0)
|
||||
(Vect (screenWidth/2) (-screenHeight/2)) 0
|
||||
-- ground <- polyShapeNewRaw static
|
||||
-- [ Vect (-screenWidth/2) (screenHeight/2)
|
||||
-- , Vect (screenWidth/2) (-screenHeight/2)
|
||||
-- , Vect (-screenWidth/2) (-screenHeight/2)
|
||||
-- , Vect (-screenWidth/2) (screenHeight/2) ] 0
|
||||
shapeFriction ground $= 1
|
||||
spaceAddShape space ground
|
||||
|
||||
|
||||
|
||||
-- Now let's make a ball that falls onto the line and rolls off.
|
||||
-- First we need to make a cpBody to hold the physical properties of the object.
|
||||
-- These include the mass, position, velocity, angle, etc. of the object.
|
||||
-- Then we attach collision shapes to the Body to give it a size and shape.
|
||||
|
||||
let toVect (V2 x y) = Vect x y
|
||||
|
||||
let svg = center $ scale 2 $ latex "\\LaTeX"
|
||||
poly = svgToPolyShapes svg
|
||||
vectGroup = plDecompose poly
|
||||
--mkCircle (Num radius)
|
||||
-- vects = svgToVects svg
|
||||
-- vects' = fst $ convexHull vects 0
|
||||
-- svg' =
|
||||
-- withFillOpacity 0 $ withStrokeColor "white" $
|
||||
-- withStrokeWidth 0.01 $
|
||||
-- renderPolyShapes (map plFromPolygon vectGroup)
|
||||
--
|
||||
-- ballBody <- polyShapesToBody space poly
|
||||
-- bodyPosition ballBody $= Vect (-screenWidth/4) (screenHeight/2)
|
||||
--
|
||||
-- addToBodyStore bodyStore ballBody $
|
||||
-- withFillColor "white" $
|
||||
-- mkGroup
|
||||
-- [ svg' ]
|
||||
|
||||
-- let splitPolys = vectGroup
|
||||
forM_ vectGroup $ \polygon -> do
|
||||
bd <- polygonsToBody space [map toVect polygon]
|
||||
bodyPosition bd $= Vect 0 (screenHeight/3)
|
||||
addToBodyStore bodyStore bd $
|
||||
renderPolyShape $ plFromPolygon polygon
|
||||
-- withFillColor "white" $
|
||||
-- mkGroup
|
||||
-- [ --withStrokeWidth (Num 0.005) $
|
||||
-- -- withStrokeWidth (Num 0.00) $
|
||||
-- -- withStrokeColor "white" $
|
||||
-- -- withFillOpacity 1 $
|
||||
-- renderPolyShape $ plFromPolygon polygon
|
||||
-- ]
|
||||
|
||||
ani <- simulate space bodyStore 60 60 10
|
||||
spaceFreeRecursive space
|
||||
return ani
|
||||
|
||||
-- data LineCommand
|
||||
-- = LineMove RPoint
|
||||
-- -- | LineDraw RPoint
|
||||
-- | LineBezier [RPoint]
|
||||
-- | LineEnd
|
||||
|
||||
-- vectsToSVG :: [Vect] -> Tree
|
||||
-- vectsToSVG (Vect x y:rest) =
|
||||
-- mkPath $
|
||||
-- MoveTo OriginAbsolute [V2 x y] :
|
||||
-- [ LineTo OriginAbsolute [V2 a b] | Vect a b <- rest ] ++
|
||||
-- [ EndPath ]
|
||||
-- where
|
||||
-- mkPath cmds = PathTree $ defaultSvg & pathDefinition .~ cmds
|
||||
|
||||
-- polygonsToSVG :: [[Vect]] -> Tree
|
||||
-- polygonsToSVG = merge . mkGroup . map vectsToSVG
|
||||
-- where
|
||||
-- merge svg = PathTree $ defaultSvg & pathDefinition .~ extractPath svg
|
||||
|
||||
-- svgToVects :: Tree -> [Vect]
|
||||
-- svgToVects svg = map worker (lineToPoints 200 cmds)
|
||||
-- where
|
||||
-- worker (V2 x y) = Vect x y
|
||||
-- cmds = toLineCommands $ wibble $ extractPath svg
|
||||
-- wibble xs = takeWhile (/=EndPath) xs ++ [EndPath]
|
||||
|
||||
chunkPolyshapes :: Tree -> Tree
|
||||
chunkPolyshapes t =
|
||||
withStrokeColor "white" $
|
||||
withStrokeWidth 0.01 $
|
||||
withFillColor "white" $ t
|
||||
|
||||
reorient :: Tree -> Tree
|
||||
reorient = id -- scale 4 . translate 0 (-0.9)
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate $ bg `parA` mapA reorient (line `parA` mapA chunkPolyshapes test)
|
||||
where
|
||||
bg = animate $ const $ mkBackground "black"
|
||||
line = animate $ const $ withStrokeColor "white" $
|
||||
withStrokeWidth 0.01 $
|
||||
mkLine (-screenWidth/2, 0)
|
||||
(screenWidth/2, -screenHeight/2)
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module Main (main) where
|
||||
|
||||
import Chiphunk.Low
|
||||
import Control.Monad
|
||||
import Graphics.SvgTree (Tree)
|
||||
import Linear.V2
|
||||
import Reanimate.Chiphunk
|
||||
import Reanimate
|
||||
import Reanimate.PolyShape
|
||||
import System.IO.Unsafe
|
||||
|
||||
test :: Animation
|
||||
test = unsafePerformIO $ do
|
||||
bodyStore <- newBodyStore
|
||||
let gravity = Vect 0 (-1)
|
||||
|
||||
-- Create an empty space.
|
||||
space <- spaceNew
|
||||
-- spaceCollisionSlop space $= (screenWidth/2560)
|
||||
spaceGravity space $= gravity
|
||||
|
||||
-- Add a static line segment shape for the ground.
|
||||
-- We'll make it slightly tilted so the ball will roll off.
|
||||
-- We attach it to a static body to tell Chipmunk it shouldn't be movable.
|
||||
static <- get $ spaceStaticBody space
|
||||
ground <- segmentShapeNew static
|
||||
(Vect (-screenWidth/2) (screenHeight/2))
|
||||
(Vect (screenWidth/2) (-screenHeight/2)) 0
|
||||
shapeFriction ground $= 1
|
||||
spaceAddShape space ground
|
||||
|
||||
-- Now let's make a ball that falls onto the line and rolls off.
|
||||
-- First we need to make a cpBody to hold the physical properties of the object.
|
||||
-- These include the mass, position, velocity, angle, etc. of the object.
|
||||
-- Then we attach collision shapes to the Body to give it a size and shape.
|
||||
|
||||
let toVect (V2 x y) = Vect x y
|
||||
|
||||
let svg = center $ scale 2 $ latex "$\\Phi$"
|
||||
poly = svgToPolyShapes svg
|
||||
vectGroup = plDecompose' 1 poly
|
||||
--mkCircle (Num radius)
|
||||
-- vects = svgToVects svg
|
||||
-- vects' = fst $ convexHull vects 0
|
||||
-- svg' =
|
||||
-- withFillOpacity 0 $ withStrokeColor "white" $
|
||||
-- withStrokeWidth 0.01 $
|
||||
-- renderPolyShapes (map plFromPolygon vectGroup)
|
||||
--
|
||||
-- ballBody <- polyShapesToBody space poly
|
||||
-- bodyPosition ballBody $= Vect (-screenWidth/4) (screenHeight/2)
|
||||
--
|
||||
-- addToBodyStore bodyStore ballBody $
|
||||
-- withFillColor "white" $
|
||||
-- mkGroup
|
||||
-- [ svg' ]
|
||||
|
||||
-- let splitPolys = vectGroup
|
||||
forM_ vectGroup $ \polygon -> do
|
||||
bd <- polygonsToBody space [map toVect polygon]
|
||||
bodyPosition bd $= Vect 0 (screenHeight/3)
|
||||
addToBodyStore bodyStore bd $
|
||||
renderPolyShape $ plFromPolygon polygon
|
||||
-- withFillColor "white" $
|
||||
-- mkGroup
|
||||
-- [ --withStrokeWidth (Num 0.005) $
|
||||
-- -- withStrokeWidth (Num 0.00) $
|
||||
-- -- withStrokeColor "white" $
|
||||
-- -- withFillOpacity 1 $
|
||||
-- renderPolyShape $ plFromPolygon polygon
|
||||
-- ]
|
||||
|
||||
ani <- simulate space bodyStore 60 10 10
|
||||
spaceFreeRecursive space
|
||||
return ani
|
||||
|
||||
-- data LineCommand
|
||||
-- = LineMove RPoint
|
||||
-- -- | LineDraw RPoint
|
||||
-- | LineBezier [RPoint]
|
||||
-- | LineEnd
|
||||
|
||||
-- vectsToSVG :: [Vect] -> Tree
|
||||
-- vectsToSVG (Vect x y:rest) =
|
||||
-- mkPath $
|
||||
-- MoveTo OriginAbsolute [V2 x y] :
|
||||
-- [ LineTo OriginAbsolute [V2 a b] | Vect a b <- rest ] ++
|
||||
-- [ EndPath ]
|
||||
-- where
|
||||
-- mkPath cmds = PathTree $ defaultSvg & pathDefinition .~ cmds
|
||||
|
||||
-- polygonsToSVG :: [[Vect]] -> Tree
|
||||
-- polygonsToSVG = merge . mkGroup . map vectsToSVG
|
||||
-- where
|
||||
-- merge svg = PathTree $ defaultSvg & pathDefinition .~ extractPath svg
|
||||
|
||||
-- svgToVects :: Tree -> [Vect]
|
||||
-- svgToVects svg = map worker (lineToPoints 200 cmds)
|
||||
-- where
|
||||
-- worker (V2 x y) = Vect x y
|
||||
-- cmds = toLineCommands $ wibble $ extractPath svg
|
||||
-- wibble xs = takeWhile (/=EndPath) xs ++ [EndPath]
|
||||
|
||||
chunkPolyshapes :: Tree -> Tree
|
||||
chunkPolyshapes t =
|
||||
mkGroup
|
||||
[
|
||||
-- withFillColor "white" $
|
||||
-- withFillOpacity 1 $
|
||||
-- withStrokeWidth (Num 0.01) $
|
||||
-- withStrokeColor "blue" $
|
||||
-- mkGroup $ map renderPolyShape $
|
||||
-- map mergePolyShapeHoles $
|
||||
-- plGroupShapes $
|
||||
-- -- unionPolyShapes $
|
||||
-- traceUnion $
|
||||
-- svgToPolyShapes t
|
||||
withStrokeWidth 0.001 $
|
||||
withStrokeColor "red" $
|
||||
withFillOpacity 0 $
|
||||
mkGroup $ map renderPolyShape $
|
||||
svgToPolyShapes t
|
||||
-- withStrokeWidth 0.001 $
|
||||
-- withStrokeColor "red" $
|
||||
-- mkGroup $ map renderPolyShape [pl12,pl13]
|
||||
]
|
||||
|
||||
reorient :: Tree -> Tree
|
||||
reorient = scale 6 . translate 0 (-0.9)
|
||||
|
||||
-- pl1 = PolyShape {unPolyShape = ClosedPath [(Point 9.076152025049407e-2 0.8793158714390639,JoinLine),(Point 0.48865783997158957 0.5875446050720555,JoinLine),(Point 0.6291445682322502 0.560956966929786,JoinLine),(Point 0.684414274089106 0.6254399758525935,JoinLine),(Point 8.549729225029659e-2 0.9226054512958702,JoinLine)]}
|
||||
-- pl2 = PolyShape {unPolyShape = ClosedPath [(Point 0.3174796674934045 (-0.17830385091116296),JoinLine),(Point 0.3930521201476444 (-0.22384314349780887),JoinLine),(Point 0.42400439387727545 (-0.17026635397131717),JoinLine),(Point 0.367180526197663 (-0.13743818486413273),JoinLine)]}
|
||||
-- pl3 = PolyShape {unPolyShape = ClosedPath [(Point 3.818529643334073e-2 4.994390978944008e-2,JoinLine),(Point 3.5509598929490127e-3 3.0061273354143103e-2,JoinLine),(Point 2.6643683455205014e-2 (-1.3314171733262437e-2),JoinLine)]}
|
||||
-- pl4 = PolyShape {unPolyShape = ClosedPath [(Point 8.041123459068245e-2 (-4.592522065745819e-2),JoinLine),(Point 0.29675416515553454 (-0.17235110729572378),JoinLine),(Point 0.34590008699238584 (-0.13081971694800998),JoinLine),(Point 0.22737727571852562 4.513304505856075e-2,JoinLine),(Point 0.10498823211784264 (-3.373201779345303e-3),JoinLine)]}
|
||||
-- pl5 = PolyShape {unPolyShape = ClosedPath [(Point 4.1547772506933034e-2 0.10908491253751296,JoinLine),(Point 5.7556283592893165e-2 (-2.2936657573235042e-2),JoinLine),(Point 0.18645395693905423 2.887370173550552e-2,JoinLine),(Point 0.24063435970720265 0.12675372820097613,JoinLine),(Point 0.19807899284556882 0.17285980812856805,JoinLine),(Point 8.45668830191807e-2 0.18680141998908184,JoinLine)]}
|
||||
-- pl6 = PolyShape {unPolyShape = ClosedPath [(Point (-0.2574090456009813) 0.22352645168090446,JoinLine),(Point (-0.27139525022076605) 0.16073053060077958,JoinLine),(Point (-9.444562231853837e-2) 4.994785063168361e-2,JoinLine),(Point (-4.012849544398306e-2) 6.733655689772311e-2,JoinLine),(Point (-4.6550067413171026e-2) 0.20017002713448606,JoinLine)]}
|
||||
-- pl7 = PolyShape {unPolyShape = ClosedPath [(Point (-0.3714373753139388) 0.20874389306710772,JoinLine),(Point (-0.29452803703157854) 0.1655072120141602,JoinLine),(Point (-0.2835217605861194) 0.22889335799870397,JoinLine),(Point (-0.34012040364470725) 0.2621083279509192,JoinLine)]}
|
||||
-- pl8 = PolyShape {unPolyShape = ClosedPath [(Point 0.1712163207191154 0.2149766965620984,JoinLine),(Point 0.19616659305280632 0.1905738263277067,JoinLine),(Point 0.6315796735654418 0.5328570813993471,JoinLine),(Point 0.49895896036053317 0.5862908698518825,JoinLine)]}
|
||||
-- pl9 = PolyShape {unPolyShape = ClosedPath [(Point 0.10739670009207455 0.19390054402859663,JoinLine),(Point 0.16776933204268935 0.17681618959615353,JoinLine),(Point 0.19166035517967667 0.1970425575035583,JoinLine),(Point 0.16584071117957827 0.2205236594561943,JoinLine)]}
|
||||
-- pl10 = PolyShape {unPolyShape = ClosedPath [(Point (-0.3669271639958152) 1.1382616686769857,JoinLine),(Point (-0.30169633405804164) 1.1454435521784956,JoinLine),(Point (-0.2858326377769962) 1.2078015601462844,JoinLine),(Point (-0.3736986541543818) 1.199765022618315,JoinLine)]}
|
||||
-- pl11 = PolyShape {unPolyShape = ClosedPath [(Point (-3.0061173114158422e-2) 1.1779482589541495,JoinLine),(Point 5.4907529076574785e-3 1.2315286122957358,JoinLine),(Point (-3.4177530164954735e-2) 1.2269151735259667,JoinLine)]}
|
||||
-- pl12 = PolyShape {unPolyShape = ClosedPath
|
||||
-- [(Point (-0.301730083226429*10) 1.1457103198223786,JoinLine)
|
||||
-- ,(Point (-0.10299641326632633*10) 1.0714677466302471,JoinLine)
|
||||
-- ,(Point (-3.162369257155477e-2*10) 1.182092647608354,JoinLine)
|
||||
-- ,(Point (-3.636877035247056e-2*10) 1.2310026399913667,JoinLine)
|
||||
-- ,(Point (-0.28589249688644447*10) 1.208074964264014,JoinLine)]}
|
||||
-- pl13 = PolyShape {unPolyShape = ClosedPath
|
||||
-- [(Point (-9.39646610882567e-2*10) 0.96018603098516,JoinLine)
|
||||
-- ,(Point (-6.465077411411442e-2*10) 0.9514465910530718,JoinLine)
|
||||
-- ,(Point (0.14134955357291654*10) 1.2461928296570437,JoinLine)
|
||||
-- ,(Point (8.29425965486209e-3*10) 1.2330682613363688,JoinLine)
|
||||
-- ,(Point (-0.10287959610529117*10) 1.0717052650151784,JoinLine)]}
|
||||
-- pl14 = PolyShape {unPolyShape = ClosedPath [(Point (-0.6410657779553459) 0.5634659418454123,JoinLine),(Point (-0.641345175626591) 0.555247219163176,JoinLine),(Point (-0.4420444191709594) 0.5513358399243028,JoinLine),(Point (-0.10594072095664082) 0.9023816092645105,JoinLine),(Point (-7.577913627887163e-2) 0.9355404742795719,JoinLine),(Point (-0.10373629906690308) 0.9479533236181756,JoinLine)]}
|
||||
-- pl15 = PolyShape {unPolyShape = ClosedPath [(Point (-3.523485505752828e-2) 0.1998408875682416,JoinLine),(Point 1.2546982880291841e-2 0.15208851089779252,JoinLine),(Point 0.13018455413761065 0.208301106923033,JoinLine),(Point 8.433578523127425e-2 0.9418697065712005,JoinLine),(Point (-7.884702019521485e-2) 0.932428903501462,JoinLine),(Point (-8.084970167352624e-2) 0.9296667902794287,JoinLine)]}
|
||||
-- pl16 = PolyShape {unPolyShape = ClosedPath [(Point (-7.878197627394322e-2) 0.9319709148544943,JoinLine),(Point 8.438182768423004e-2 0.941734614325483,JoinLine),(Point 8.518191684165782e-2 0.9853357574063363,JoinLine),(Point 8.15663333974107e-2 0.9853939259545145,JoinLine),(Point (-4.936159754845773e-2) 0.9727171460725276,JoinLine)]}
|
||||
-- pl17 = PolyShape {unPolyShape = ClosedPath [(Point (-4.99911351869741e-2) 0.9726181699897635,JoinLine),(Point 8.094809532371453e-2 0.9851776987711782,JoinLine),(Point 7.384845354621125e-2 1.0968271981504014,JoinLine),(Point 4.400324523877765e-2 1.1025518925313644,JoinLine)]}
|
||||
-- pl18 = PolyShape {unPolyShape = ClosedPath [(Point 4.8482888984171635e-2 1.1105768787999895,JoinLine),(Point 7.512899062784217e-2 1.0959654499901599,JoinLine),(Point 0.2822087302816073 1.1420633097553252,JoinLine),(Point 0.27513287205836434 1.2060076125068004,JoinLine),(Point 0.20204313118994668 1.2231219927110881,JoinLine)]}
|
||||
-- pl19 = PolyShape {unPolyShape = ClosedPath [(Point 0.2748784478150325 1.2078902086770202,JoinLine),(Point 0.2820822061918643 1.1439601884266248,JoinLine),(Point 0.3457225261391361 1.127943000720636,JoinLine),(Point 0.36082444597621127 1.187946730956635,JoinLine)]}
|
||||
-- pl20 = PolyShape {unPolyShape = ClosedPath [(Point (-0.6864531908080556) 0.5032767356555317,JoinLine),(Point (-9.929294916146593e-2) 0.20829491627795255,JoinLine),(Point (-8.483230342755331e-2) 0.23523598173633797,JoinLine),(Point (-0.49217693932265094) 0.547917843618004,JoinLine)]}
|
||||
-- pl21 = PolyShape {unPolyShape = ClosedPath [(Point (-9.274301086931341e-2) 0.2153671046631685,JoinLine),(Point (-4.68781647762479e-2) 0.2005165600411279,JoinLine),(Point (-4.790067311086227e-2) 0.23865704773924823,JoinLine),(Point (-7.795810209993242e-2) 0.2330461435659677,JoinLine)]}
|
||||
|
||||
-- unionTest = parsed
|
||||
-- where
|
||||
-- svg = center $ scale 2 $ latex "I"
|
||||
-- poly = svgToPolyShapes svg
|
||||
-- vectGroup = plDecompose' 1 poly
|
||||
-- rendered = mkGroup $ take 100 $ map (renderPolyShape . plFromPolygon) vectGroup
|
||||
-- parsed = svgToPolyShapes rendered
|
||||
--
|
||||
-- myDecompose tol =
|
||||
-- concatMap decomposePolygon .
|
||||
-- map (plPolygonify tol) .
|
||||
-- map mergePolyShapeHoles .
|
||||
-- plGroupShapes .
|
||||
-- unionPolyShapes
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate $ bg `parA` mapA reorient (line `parA` mapA chunkPolyshapes test)
|
||||
where
|
||||
bg = animate $ const $ mkBackground "black"
|
||||
line = animate $ const $ withStrokeColor "white" $
|
||||
withStrokeWidth 0.01 $
|
||||
mkLine (-screenWidth/2, screenHeight/2)
|
||||
(screenWidth/2, -screenHeight/2)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
0<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(0, 4.5)"><g transform="rotate(0)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
1<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(0, 3.5)"><g transform="rotate(0)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
2<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(0, 0.623148)"><g transform="rotate(0)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
3<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(0.544434, -1.371641)"><g transform="rotate(-32.404054)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
4<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(1.709402, -1.699288)"><g transform="rotate(-101.741451)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
5<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(3.264515, -2.136663)"><g transform="rotate(-194.299838)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
6<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(5.09458, -2.651369)"><g transform="rotate(-303.223039)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
7<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(7.340372, -3.282998)"><g transform="rotate(-436.88976)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
8<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(9.775043, -4.315897)"><g transform="rotate(-581.798313)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
9<svg viewBox="-8 -4.5 16 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none"><g stroke-width="0.05" transform="scale(1, -1)"><g><g><g stroke-width="0" fill="#000000" transform="translate(-8, -4.5)" fill-opacity="1"><rect width="16" height="9" /></g><line stroke-width="0.01" stroke="#FFFFFF" x1="-8" x2="8" y2="-4.5" /></g><g><g transform="translate(12.329251, -7.234268)"><g transform="rotate(-733.821586)"><g fill="#FFFFFF"><circle r="1" /><line stroke="#000000" y2="1" /></g></g></g></g></g></g></svg>
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module Main (main) where
|
||||
|
||||
import Chiphunk.Low
|
||||
import Reanimate.Chiphunk
|
||||
import Reanimate
|
||||
import System.IO.Unsafe
|
||||
|
||||
|
||||
test :: Animation
|
||||
test = unsafePerformIO $ do
|
||||
bodyStore <- newBodyStore
|
||||
let gravity = Vect 0 (-10)
|
||||
|
||||
-- Create an empty space.
|
||||
space <- spaceNew
|
||||
spaceGravity space $= gravity
|
||||
|
||||
-- Add a static line segment shape for the ground.
|
||||
-- We'll make it slightly tilted so the ball will roll off.
|
||||
-- We attach it to a static body to tell Chipmunk it shouldn't be movable.
|
||||
static <- get $ spaceStaticBody space
|
||||
ground <- segmentShapeNew static
|
||||
(Vect (-screenWidth/2) 0)
|
||||
(Vect (screenWidth/2) (-screenHeight/2)) 0
|
||||
shapeFriction ground $= 1
|
||||
spaceAddShape space ground
|
||||
|
||||
-- Now let's make a ball that falls onto the line and rolls off.
|
||||
-- First we need to make a cpBody to hold the physical properties of the object.
|
||||
-- These include the mass, position, velocity, angle, etc. of the object.
|
||||
-- Then we attach collision shapes to the Body to give it a size and shape.
|
||||
|
||||
let radius = 1
|
||||
let mass = 1
|
||||
|
||||
-- The moment of inertia is like mass for rotation
|
||||
-- Use the momentFor* functions to help you approximate it.
|
||||
let moment = momentForCircle mass 0 radius (Vect 0 0)
|
||||
|
||||
-- The spaceAdd* functions return the thing that you are adding.
|
||||
ballBody <- bodyNew mass moment
|
||||
spaceAddBody space ballBody
|
||||
bodyPosition ballBody $= Vect 0 (screenHeight/2)
|
||||
|
||||
-- Now we create the collision shape for the ball.
|
||||
-- You can create multiple collision shapes that point to the same body.
|
||||
-- They will all be attached to the body and move around to follow it.
|
||||
ballShape <- circleShapeNew ballBody radius (Vect 0 0)
|
||||
spaceAddShape space ballShape
|
||||
shapeFriction ballShape $= 0.7
|
||||
|
||||
addToBodyStore bodyStore ballBody $
|
||||
withFillColor "white" $
|
||||
mkGroup
|
||||
[ mkCircle radius
|
||||
, withStrokeColor "black" $
|
||||
mkLine (0, 0) (0, radius) ]
|
||||
|
||||
ani <- simulate space bodyStore 60 3 4
|
||||
|
||||
shapeFree ballShape
|
||||
bodyFree ballBody
|
||||
shapeFree ground
|
||||
spaceFree space
|
||||
return ani
|
||||
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate $ bg `parA` line `parA` test
|
||||
where
|
||||
bg = animate $ const $ mkBackground "black"
|
||||
line = animate $ const $ withStrokeColor "white" $
|
||||
withStrokeWidth 0.01 $
|
||||
mkLine (-screenWidth/2, 0)
|
||||
(screenWidth/2, -screenHeight/2)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module Main (main) where
|
||||
|
||||
import Chiphunk.Low
|
||||
import Codec.Picture (PixelRGBA8 (..))
|
||||
import Control.Lens
|
||||
import Control.Monad (forM_)
|
||||
import Linear.V2 (V2 (..))
|
||||
import Reanimate
|
||||
import Reanimate.Chiphunk
|
||||
import Reanimate.PolyShape
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
shatter :: Animation
|
||||
shatter = unsafePerformIO $ do
|
||||
bodyStore <- newBodyStore
|
||||
let gravity = Vect 0 (-1) -- Gravity points down (negative 1 y/s^2)
|
||||
|
||||
space <- spaceNew
|
||||
spaceGravity space $= gravity
|
||||
|
||||
static <- get $ spaceStaticBody space
|
||||
ground <- segmentShapeNew static
|
||||
(Vect (-screenWidth/2) (-screenHeight/2))
|
||||
(Vect (screenWidth/2) (-screenHeight/2)) 0
|
||||
shapeFriction ground $= 1
|
||||
spaceAddShape space ground
|
||||
|
||||
let toVect (V2 x y) = Vect x y
|
||||
vectGroup = plDecompose $ svgToPolyShapes $ center $ scale 4 $
|
||||
latex "$F=ma$"
|
||||
|
||||
forM_ vectGroup $ \polygon -> do
|
||||
bd <- polygonsToBody space [map toVect polygon]
|
||||
bodyPosition bd $= Vect 0 (screenHeight/3)
|
||||
addToBodyStore bodyStore bd $
|
||||
renderPolyShape $ plFromPolygon polygon
|
||||
|
||||
ani <- simulate space bodyStore fps stepsPerFrame shatterT
|
||||
spaceFreeRecursive space
|
||||
return $ mapA pp ani
|
||||
where
|
||||
shatterT = 10
|
||||
fps = 60
|
||||
stepsPerFrame = 10
|
||||
pp = withStrokeWidth 0.01 . withStrokeColor "black" . withFillColor "black"
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate $ parA bg $ sceneAnimation $ do
|
||||
play $ shatter
|
||||
play $ shatter
|
||||
& reverseA
|
||||
& setDuration 5
|
||||
& signalA (powerS 2)
|
||||
where
|
||||
bg = animate $ const $ mkBackgroundPixel (PixelRGBA8 252 252 252 0xFF)
|
||||
|
|
@ -19,8 +19,8 @@ nav:
|
|||
- Reanimate: index.md
|
||||
- Core concepts: introduction.md
|
||||
- Gluing together animations: glue_tut.md
|
||||
# - Morphology (incomplete): morphology.md
|
||||
- Voice control: voice.md
|
||||
- Morphology (incomplete): morphology.md
|
||||
# - Voice control: voice.md
|
||||
# - How to:
|
||||
# - Use fonts (incomplete): fonts.md
|
||||
- Showcase:
|
||||
|
|
|
|||
|
|
@ -1,31 +1,37 @@
|
|||
{-# LANGUAGE MultiWayIf #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
module Reanimate.Math.Compatible where
|
||||
|
||||
import Data.Hashable
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Ord
|
||||
import qualified Data.Vector as V
|
||||
import Linear.V2
|
||||
import Linear.Vector
|
||||
import Reanimate.Animation
|
||||
import Reanimate.Debug
|
||||
import Reanimate.Math.Common
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Debug
|
||||
import Reanimate.Math.Render
|
||||
import Reanimate.Svg
|
||||
import Reanimate.Animation
|
||||
|
||||
-- import Debug.Trace
|
||||
-- import GHC.Stack
|
||||
|
||||
truncateP :: V2 Rational -> V2 Rational
|
||||
truncateP = fmap (realToFrac . (realToFrac :: Rational -> Double))
|
||||
truncateP :: forall a. (Fractional a, Real a) => V2 a -> V2 a
|
||||
truncateP = fmap (fromDouble . toDouble)
|
||||
where
|
||||
toDouble :: a -> Double
|
||||
toDouble = fromRational . toRational
|
||||
fromDouble :: Double -> a
|
||||
fromDouble = fromRational . toRational
|
||||
|
||||
mkSteinerPoints :: V2 Rational -> V2 Rational -> Int -> [V2 Rational]
|
||||
mkSteinerPoints a b s_ = [ lerp (i / (s + 1)) b a | i <- [1 .. s] ]
|
||||
where s = fromIntegral s_
|
||||
mkSteinerPoints :: (Fractional a) => V2 a -> V2 a -> Int -> [V2 a]
|
||||
mkSteinerPoints a b s = [ lerp (fromIntegral i / (fromIntegral s + 1)) b a | i <- [1 .. s] ]
|
||||
|
||||
-- 0..i,j..n-1
|
||||
-- i..j
|
||||
split1Link :: Polygon -> Int -> Int -> Int -> (Polygon, Polygon)
|
||||
split1Link :: (Real a, Fractional a) => APolygon a -> Int -> Int -> Int -> (APolygon a, APolygon a)
|
||||
split1Link p i j s | j < i = split1Link p j i s
|
||||
split1Link p i j s =
|
||||
(mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
||||
|
|
@ -35,7 +41,7 @@ split1Link p i j s =
|
|||
left = map (pAccess p) [0 .. i] ++ sp ++ map (pAccess p) [j .. n - 1]
|
||||
right = map (pAccess p) [i .. j] ++ reverse sp
|
||||
|
||||
steiner2Link :: Polygon -> Int -> Int -> V2 Rational
|
||||
steiner2Link :: (Real a, Fractional a) => APolygon a -> Int -> Int -> V2 a
|
||||
steiner2Link p i j | j < i = steiner2Link p j i
|
||||
steiner2Link p i j
|
||||
| isNeighbour
|
||||
|
|
@ -46,57 +52,94 @@ steiner2Link p i j
|
|||
= error
|
||||
$ "steiner2Link: Cannot construct 2-link chain between points: "
|
||||
++ show (i, j, pParent p i j, pParent p i (pParent p i j))
|
||||
| oneBendBetween p i j
|
||||
= let p1 = lerp 0.5 (pAccess p i) (pAccess p j)
|
||||
p2 = case p1 - pAccess p i of
|
||||
V2 x y -> p1 + V2 (-y) x -- rotate 90 degrees.
|
||||
intersects =
|
||||
sortOn (approxDist p1)
|
||||
$ [ u
|
||||
| n <- [0 .. pSize p - 1]
|
||||
, n < i || n >= j
|
||||
, let edge = (pAccess p n, pAccess p $ pNext p n)
|
||||
, u <- case rayIntersect (p1,p2) edge of
|
||||
Nothing -> []
|
||||
Just u -> [u]
|
||||
, isBetween u edge
|
||||
]
|
||||
in lerp 0.5 p1 (intersects !! 0)
|
||||
| otherwise
|
||||
= truncateP $ lerp 0.5 (fst vect) (intersects !! 0)
|
||||
= let pI = ssspVisibility $ pSetOffset p i
|
||||
pJ = ssspVisibility $ pSetOffset p j
|
||||
in truncateP $ pCentroid $ pOverlap pI pJ
|
||||
-- | otherwise
|
||||
-- = truncateP $ lerp 0.5 windowCommon (intersects !! 0)
|
||||
where
|
||||
distToV = approxDist (fst vect)
|
||||
-- trigger = True -- i == 4 && j == 8
|
||||
-- info = mkGroup
|
||||
-- [ scale 2 $ mkGroup
|
||||
-- [ mkGroup [withFillColor "grey" $ polygonShape p, polygonNumDots p]
|
||||
-- , let V2 x1 y1 = realToFrac <$> windowCommon
|
||||
-- in translate x1 y1 $ withFillColor "red" $ mkCircle 0.08
|
||||
-- , let V2 x1 y1 = realToFrac <$> iWindow
|
||||
-- in translate x1 y1 $ withFillColor "blue" $ mkCircle 0.04
|
||||
-- , let V2 x1 y1 = realToFrac <$> jWindow
|
||||
-- in translate x1 y1 $ withFillColor "green" $ mkCircle 0.04
|
||||
-- ]
|
||||
-- , translate 5 0 $ withFillColor "white" $ latex $ T.pack $ show (i, j) ]
|
||||
-- distToV = approxDist windowCommon
|
||||
isNeighbour = i == pNext p j || i == pPrev p j
|
||||
isParent = pParent p i j == i
|
||||
isGrandparent = pParent p i (pParent p i j) == i
|
||||
isStraightLine =
|
||||
direction (pAccess p j) (pAccess p $ pParent p i j) (pAccess p i) == 0
|
||||
intersects =
|
||||
sortOn distToV
|
||||
$ snd vect
|
||||
: [ u
|
||||
| n <- [0 .. pSize p - 1]
|
||||
, let edge = (pAccess p n, pAccess p $ pNext p n)
|
||||
, u <- case rayIntersect vect edge of
|
||||
Nothing -> []
|
||||
Just u -> [u]
|
||||
, isBetween u edge
|
||||
, u /= fst vect
|
||||
, isForward vect u
|
||||
]
|
||||
iP = pAdjustOffset p i
|
||||
jP = pAdjustOffset p j
|
||||
vect
|
||||
| isStraightLine
|
||||
= let p1 = lerp 0.5 (pAccess p i) (pAccess p j)
|
||||
p2 = case p1 - pAccess p i of
|
||||
V2 x y -> p1 + V2 (-y) x -- rotate 90 degrees.
|
||||
in (p1, p2)
|
||||
| otherwise
|
||||
= fromMaybe
|
||||
(error $ "No window overlap: " ++ show
|
||||
(isStraightLine, isGrandparent, oneBendBetween p i j)
|
||||
)
|
||||
$ listToMaybe
|
||||
$ [ (p1, p1 + (p2 - p1) + (p3 - p1))
|
||||
| (a, b) <- ssspWindows iP
|
||||
, (c, d) <- ssspWindows jP
|
||||
, (p1, p2, p3) <- if
|
||||
| a == c -> pure (a, b, d)
|
||||
| a == d -> pure (a, b, c)
|
||||
| b == c -> pure (b, a, d)
|
||||
| b == d -> pure (b, a, c)
|
||||
| otherwise -> []
|
||||
]
|
||||
isForward (a, b) v = not (isBetween a (b, v))
|
||||
-- intersects =
|
||||
-- sortOn distToV
|
||||
-- $ snd windowDirection
|
||||
-- : [ u
|
||||
-- | n <- [0 .. pSize p - 1]
|
||||
-- , let edge = (pAccess p n, pAccess p $ pNext p n)
|
||||
-- , u <- case rayIntersect windowDirection edge of
|
||||
-- Nothing -> []
|
||||
-- Just u -> [u]
|
||||
-- , isBetween u edge
|
||||
-- , u /= windowCommon
|
||||
-- , isForward windowDirection u
|
||||
-- ] ++
|
||||
-- [ u
|
||||
-- | not isStraightLine
|
||||
-- , ray <- [(pAccess p i, jWindow), (pAccess p j, iWindow)]
|
||||
-- , u <- maybeToList $ rayIntersect windowDirection ray ]
|
||||
-- iP = pAdjustOffset p i
|
||||
-- jP = pAdjustOffset p j
|
||||
-- windowDirection = (windowCommon, windowOpposite)
|
||||
-- (windowCommon, iWindow, jWindow, windowOpposite)
|
||||
-- | isStraightLine
|
||||
-- = let p1 = lerp 0.5 (pAccess p i) (pAccess p j)
|
||||
-- p2 = case p1 - pAccess p i of
|
||||
-- V2 x y -> p1 + V2 (-y) x -- rotate 90 degrees.
|
||||
-- in (p1, undefined, undefined, p2)
|
||||
-- | otherwise
|
||||
-- = fromMaybe
|
||||
-- (error $ "No window overlap: " ++ show
|
||||
-- (isStraightLine, isGrandparent, oneBendBetween p i j)
|
||||
-- )
|
||||
-- $ listToMaybe
|
||||
-- $ [ (p1, p2, p3, p2+p3-p1)
|
||||
-- | (a, b) <- ssspWindows iP
|
||||
-- , (c, d) <- ssspWindows jP
|
||||
-- , (p1, p2, p3) <- if
|
||||
-- | a == c -> pure (a, b, d)
|
||||
-- | a == d -> pure (a, b, c)
|
||||
-- | b == c -> pure (b, a, d)
|
||||
-- | b == d -> pure (b, a, c)
|
||||
-- | otherwise -> []
|
||||
-- ]
|
||||
-- isForward (a, b) v = not (isBetween a (b, v))
|
||||
|
||||
-- 0..i,s,j..n-1
|
||||
-- i..j,s
|
||||
split2Link :: Polygon -> Int -> Int -> (Polygon, Polygon)
|
||||
split2Link :: (Real a, Fractional a) => APolygon a -> Int -> Int -> (APolygon a, APolygon a)
|
||||
split2Link p i j | j < i = split2Link p j i
|
||||
split2Link p i j = (mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
||||
where
|
||||
|
|
@ -107,7 +150,7 @@ split2Link p i j = (mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
|||
|
||||
data Link = OneLink | TwoLink
|
||||
|
||||
splitNLink :: Polygon -> Int -> [(Link, Int)] -> (Polygon, Polygon)
|
||||
splitNLink :: (Real a, Fractional a) => APolygon a -> Int -> [(Link, Int)] -> (APolygon a, APolygon a)
|
||||
splitNLink p i js = (mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
||||
where
|
||||
n = pSize p
|
||||
|
|
@ -117,7 +160,7 @@ splitNLink p i js = (mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
|||
steiners = splitNLinks p i js
|
||||
-- 0, [(TwoLink,2),(OneLink,3)]
|
||||
-- 0, [(OneLink,5),(TwoLink,3)]
|
||||
splitNLinks :: Polygon -> Int -> [(Link, Int)] -> [V2 Rational]
|
||||
splitNLinks :: (Real a, Fractional a) => APolygon a -> Int -> [(Link, Int)] -> [V2 a]
|
||||
splitNLinks _p _i [] = []
|
||||
splitNLinks p i [(TwoLink, j )] = [steiner2Link p i j]
|
||||
splitNLinks _p _i [(OneLink, _j)] = []
|
||||
|
|
@ -154,7 +197,7 @@ splitNLinks p i ((OneLink, j) : (TwoLink, j') : xs) =
|
|||
s' : s : splitNLinks p'' sIdx' ((OneLink, j') : xs)
|
||||
splitNLinks _p _i _ = error "splitNLinks: invalid input"
|
||||
|
||||
selectContains :: Polygon -> Polygon -> V2 Rational -> Polygon
|
||||
selectContains :: Eq a => APolygon a -> APolygon a -> V2 a -> APolygon a
|
||||
selectContains p1 p2 elt | V.elem elt (polygonPoints p1) = p1
|
||||
| V.elem elt (polygonPoints p2) = p2
|
||||
| otherwise = error "elt not member of either polygons"
|
||||
|
|
@ -218,7 +261,7 @@ data MeshPair = MeshPair Points Points Edges
|
|||
-- (a,b,1)
|
||||
-- (a,b,2)
|
||||
-- floyd
|
||||
compatiblyTriangulateP :: Polygon -> Polygon -> [(Polygon, Polygon)]
|
||||
compatiblyTriangulateP :: (Real a, Fractional a) => APolygon a -> APolygon a -> [(APolygon a, APolygon a)]
|
||||
compatiblyTriangulateP a b
|
||||
| pSize a /= pSize b = error "polygon size mismatch"
|
||||
| otherwise = traceSVG (showStep a b) $ compatiblyTriangulateP'
|
||||
|
|
@ -226,39 +269,57 @@ compatiblyTriangulateP a b
|
|||
(pSetOffset a 0)
|
||||
(pSetOffset b 0)
|
||||
|
||||
showStep :: Polygon -> Polygon -> SVG
|
||||
showStep a b = mkGroup
|
||||
[ translate (-3) 0
|
||||
$ mkGroup [withFillColor "grey" $ polygonShape a, polygonNumDots a]
|
||||
, translate 3 0
|
||||
$ mkGroup [withFillColor "grey" $ polygonShape b, polygonNumDots b]
|
||||
]
|
||||
showStep :: APolygon a -> APolygon a -> SVG
|
||||
showStep _a _b = mkGroup []
|
||||
-- [ translate (-3) 0 $ scale 2
|
||||
-- $ mkGroup [withFillColor "grey" $ polygonShape a, polygonNumDots a]
|
||||
-- , translate 3 0 $ scale 2
|
||||
-- $ mkGroup [withFillColor "grey" $ polygonShape b, polygonNumDots b]
|
||||
-- ]
|
||||
|
||||
compatiblyTriangulateP' :: Polygon -> Polygon -> Polygon -> [(Polygon, Polygon)]
|
||||
showSolution :: APolygon a -> APolygon a -> (Int, Int) -> SVG
|
||||
showSolution _a _b (_nodeL, _nodeR) = mkGroup []
|
||||
-- [ showStep a b
|
||||
-- -- , translate (-3) 0 $ scale 2 $
|
||||
-- -- translate xA yA $ withFillColor "red" $
|
||||
-- -- mkCircle 0.05
|
||||
-- , withFillColor "white" $ latex $ T.pack $ show (nodeL, nodeR)
|
||||
-- ]
|
||||
where
|
||||
-- V2 xA yA = realToFrac <$> steiner2Link a nodeL nodeR
|
||||
-- V2 xB yB = steiner2Link b nodeL nodeR
|
||||
|
||||
compatiblyTriangulateP' :: (Real a, Fractional a) => APolygon a -> APolygon a -> APolygon a -> [(APolygon a, APolygon a)]
|
||||
compatiblyTriangulateP' aOrigin a b
|
||||
-- | trace (show (pSize a, pSize b)) False = undefined
|
||||
| n == 3 = traceSVG (showStep a b) $ {- trace ("Done") $ -} [(a, b)]
|
||||
| otherwise =
|
||||
-- trace ("PolygonA " ++ show a) $
|
||||
-- trace ("PolygonB " ++ show b) $
|
||||
-- trace ("pSize: " ++ show (pSize a)) $
|
||||
-- trace ("aOneLink: " ++ show aOneLink) $
|
||||
-- trace ("bOneLink: " ++ show bOneLink) $
|
||||
-- trace ("aTwoLink: " ++ show aTwoLink) $
|
||||
-- trace ("bTwoLink: " ++ show bTwoLink) $
|
||||
-- trace ("bestOneLink: " ++ show bestOneLink) $
|
||||
-- trace ("bestTwoLink: " ++ show bestTwoLink) $
|
||||
traceSVG (showStep a b) $ case bestOneLink of
|
||||
Nothing -> case bestTwoLink of
|
||||
Nothing -> error $ "no 2-links"
|
||||
Just (nodeL, nodeR) ->
|
||||
{-trace
|
||||
(show
|
||||
("two link" :: String, toOriginIndex nodeL, toOriginIndex nodeR)
|
||||
)
|
||||
$ -}
|
||||
let (aL, aR) = if (nodeL, nodeR) `elem` aOneLink
|
||||
Just (nodeL, nodeR) -> --trace ("Creating steiner node: " ++ show (nodeL, nodeR)) $
|
||||
let isTwolink =
|
||||
(nodeL, nodeR) `notElem` aOneLink ||
|
||||
(nodeL, nodeR) `notElem` bOneLink
|
||||
(aL, aR) = if (nodeL, nodeR) `elem` aOneLink
|
||||
then split1Link a nodeL nodeR 1
|
||||
else split2Link a nodeL nodeR
|
||||
(bL, bR) = if (nodeL, nodeR) `elem` bOneLink
|
||||
then split1Link b nodeL nodeR 1
|
||||
else split2Link b nodeL nodeR
|
||||
in compatiblyTriangulateP' aOrigin aL bL
|
||||
in (if isTwolink then traceSVG (showSolution a b (nodeL, nodeR)) else id) $
|
||||
(compatiblyTriangulateP' aOrigin aL bL
|
||||
++ compatiblyTriangulateP' aOrigin aR bR
|
||||
)
|
||||
Just (nodeL, nodeR) ->
|
||||
{-trace
|
||||
(show ("one link" :: String, toOriginIndex nodeL, toOriginIndex nodeR)
|
||||
|
|
@ -285,22 +346,21 @@ compatiblyTriangulateP' aOrigin a b
|
|||
bOneLink = polygonOneLinks b
|
||||
aTwoLink = polygonTwoLinks a
|
||||
bTwoLink = polygonTwoLinks b
|
||||
nodeDist (i, j) = min (j - i) (n - j + i)
|
||||
nodeDist (i, j) = (min (j - i) (n - j + i), hash (i,j))
|
||||
|
||||
oneBendBetween :: Polygon -> Int -> Int -> Bool
|
||||
oneBendBetween _p _a _b = False
|
||||
-- oneBendBetween p a b =
|
||||
-- direction
|
||||
-- (pAccess p b)
|
||||
-- (pAccess p (pParent p a b))
|
||||
-- (pAccess p $ obstructedBy b) == 0 &&
|
||||
-- (pParent p a b) /= obstructedBy b
|
||||
-- where
|
||||
oneBendBetween :: (Real a, Fractional a) => APolygon a -> Int -> Int -> Bool
|
||||
-- oneBendBetween _p _a _b = False
|
||||
oneBendBetween p a b =
|
||||
abs (direction
|
||||
(pAccess p a)
|
||||
(pAccess p (pParent p a b))
|
||||
(pAccess p b)) < epsilon
|
||||
where
|
||||
-- obstructedBy n =
|
||||
-- case pParent p a n of
|
||||
-- i -> if i == a then n else obstructedBy i
|
||||
|
||||
polygonTwoLinks :: Polygon -> [(Int, Int)]
|
||||
polygonTwoLinks :: (Real a, Fractional a) => APolygon a -> [(Int, Int)]
|
||||
polygonTwoLinks p =
|
||||
[ (i, j)
|
||||
| i <- [0 .. n - 1]
|
||||
|
|
@ -315,7 +375,7 @@ polygonTwoLinks p =
|
|||
]
|
||||
where n = pSize p
|
||||
|
||||
polygonOneLinks :: Polygon -> [(Int, Int)]
|
||||
polygonOneLinks :: APolygon a -> [(Int, Int)]
|
||||
polygonOneLinks p =
|
||||
[ (i, j)
|
||||
| i <- [0 .. pSize p - 1]
|
||||
195
reanimate-morph/LeastDifference.hs
Normal file
195
reanimate-morph/LeastDifference.hs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Reanimate.Morph.LeastDifference where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
import Data.Ord
|
||||
import Data.Maybe
|
||||
import Linear.V2
|
||||
import qualified Data.Vector as V
|
||||
import Reanimate.Math.Common
|
||||
import qualified Reanimate.Math.Compatible as Compat
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Morph.Common
|
||||
|
||||
import Debug.Trace
|
||||
|
||||
leastDifference :: PointCorrespondence
|
||||
leastDifference = undefined
|
||||
|
||||
pCutsTop :: (Real a, Fractional a) => [(V2 a, V2 a)] -> APolygon a -> [(APolygon a, APolygon a, [Topology], [Topology], (V2 a, V2 a))]
|
||||
pCutsTop edges p =
|
||||
[ (l, r, topL, topR, edge)
|
||||
| (l, r) <- pCuts p
|
||||
, let edge = (pAccess l 0, pAccess r 1)
|
||||
topL = pTopology (edge : edges) l
|
||||
topR = pTopology (edge : edges) r
|
||||
]
|
||||
|
||||
{-# INLINE pCompatibleCut #-}
|
||||
pCompatibleCut :: (Real a, Fractional a) => [(Edge a,Edge a)] -> APolygon a -> APolygon a -> Maybe ((APolygon a, APolygon a),(APolygon a, APolygon a), [(Edge a,Edge a)])
|
||||
pCompatibleCut edges p1 p2 = listToMaybe $ sortOn pairSizeDifference $ do
|
||||
(p1l, p1r, aTopL, aTopR, aEdge) <- pCutsTop (map fst edges) p1
|
||||
-- let aEdge = (pAccess p1l 0, pAccess p1r 1)
|
||||
-- aTopL = pTopology (aEdge : map fst edges) p1l
|
||||
-- aTopR = pTopology (aEdge : map fst edges) p1r
|
||||
(p2l, p2r, bTopL, bTopR, bEdge) <- pCutsTop (map snd edges) p2
|
||||
-- let bEdge = (pAccess p2r 0, pAccess p2r 1)
|
||||
-- bTopL = pTopology (bEdge : map snd edges) p2l
|
||||
-- bTopR = pTopology (bEdge : map snd edges) p2r
|
||||
let newEdges = (aEdge,bEdge) : edges
|
||||
-- guard $ pTopology (map fst newEdges) p1l == pTopology (map snd newEdges) p2l
|
||||
-- guard $ pTopology (map fst newEdges) p1r == pTopology (map snd newEdges) p2r
|
||||
guard $ aTopL == bTopL
|
||||
guard $ aTopR == bTopR
|
||||
return ((p1l, p1r), (p2l, p2r), newEdges)
|
||||
|
||||
sizeDifference :: (Fractional a) => (APolygon a, APolygon a) -> a
|
||||
sizeDifference (a,b) = abs (pArea a - pArea b)
|
||||
|
||||
pairSizeDifference :: (Fractional a, Ord a) => ((APolygon a, APolygon a),(APolygon a, APolygon a),b) -> a
|
||||
pairSizeDifference (a,b,_) = max (sizeDifference a) (sizeDifference b)
|
||||
|
||||
pEdges :: APolygon a -> [Edge a]
|
||||
pEdges p = [ (pAccess p i, pAccess p $ i+1) | i <- [0 .. pSize p-1]]
|
||||
|
||||
type Edge a = (V2 a, V2 a)
|
||||
|
||||
data Topology = MutableSegment | ImmutableSegment Int
|
||||
deriving (Eq, Show)
|
||||
|
||||
{- INLINE pTopology -}
|
||||
pTopology :: Eq a => [Edge a] -> APolygon a -> [Topology]
|
||||
pTopology immutableEdges p = worker
|
||||
[ maybe MutableSegment ImmutableSegment isImmutable
|
||||
| i <- [0 .. pSize p-1]
|
||||
, let isImmutable =
|
||||
((pAccess p i, pAccess p $ i+1) `elemIndex` immutableEdges) <|>
|
||||
((pAccess p $ i+1, pAccess p i) `elemIndex` immutableEdges)
|
||||
]
|
||||
where
|
||||
worker (MutableSegment:MutableSegment:xs) = worker (MutableSegment:xs)
|
||||
worker (x:xs) = x : worker xs
|
||||
worker [] = []
|
||||
|
||||
triangulate :: Polygon -> Polygon -> [(Polygon,Polygon)]
|
||||
triangulate a b =
|
||||
[ (castPolygon l, castPolygon r)
|
||||
| (l,r) <- triangulate_ (castPolygon a) (castPolygon b)
|
||||
]
|
||||
|
||||
triangulate_ :: FPolygon -> FPolygon -> [(FPolygon,FPolygon)]
|
||||
triangulate_ a b
|
||||
| distSquared (pCentroid al) (pCentroid bl) +
|
||||
distSquared (pCentroid ar) (pCentroid br) <
|
||||
distSquared (pCentroid al) (pCentroid br) +
|
||||
distSquared (pCentroid ar) (pCentroid bl)
|
||||
-- = [(al,bl), (ar,br)]
|
||||
= triangulate' edges al bl ++ triangulate' edges ar br
|
||||
| otherwise
|
||||
-- = [(al,br), (ar,bl)]
|
||||
= triangulate' edges al br ++ triangulate' edges ar bl
|
||||
where
|
||||
edges = [(aEdge,bEdge)]
|
||||
aEdge = (pAccess ar 0, pAccess ar 1)
|
||||
bEdge = (pAccess br 0, pAccess br 1)
|
||||
(al, ar) = pCutEqual a
|
||||
(bl, br) = pCutEqual b
|
||||
|
||||
{-# SPECIALIZE triangulate' :: [(Edge Double,Edge Double)] -> FPolygon -> FPolygon -> [(FPolygon,FPolygon)] #-}
|
||||
triangulate' :: (Real a, Fractional a) => [(Edge a,Edge a)] -> APolygon a -> APolygon a -> [(APolygon a,APolygon a)]
|
||||
triangulate' edges a b
|
||||
| pSize a == 3 || pSize b == 3 = giveUp
|
||||
-- | trace (show (pSize a, pSize b)) False = undefined
|
||||
| Just ((al,ar),(bl,br), newEdges) <- pCompatibleCut edges a b
|
||||
= -- traceSVG (helper "topology") $
|
||||
triangulate' newEdges al bl ++ triangulate' newEdges ar br
|
||||
-- = [(al,bl), (ar,br)]
|
||||
| otherwise = giveUp
|
||||
where
|
||||
giveUp = [(a,b)]
|
||||
-- let aNewEdges = max 0 (pSize b - pSize a)
|
||||
-- bNewEdges = max 0 (pSize a - pSize b)
|
||||
-- a' = pAddPointsRestricted (map fst edges) aNewEdges a
|
||||
-- b' = pAddPointsRestricted (map snd edges) bNewEdges b
|
||||
-- -- (a'', b'') = closestLinearCorrespondenceA a' b'
|
||||
-- in [(a, b)]
|
||||
--Compat.compatiblyTriangulateP a'' b''
|
||||
|
||||
polygonLengths :: Eq a => [APolygon a] -> APolygon a -> [(Int, Int, Int)]
|
||||
polygonLengths polys origin' = worker 0
|
||||
where
|
||||
Just firstIdx = V.elemIndex (pAccess (head polys) 0) (polygonPoints origin')
|
||||
origin = pSetOffset origin' firstIdx
|
||||
polyLength a b
|
||||
| V.elem (pAccess a 1) (polygonPoints b)
|
||||
= 1 + polyLength (pAdjustOffset a 1) b
|
||||
| otherwise
|
||||
= 0
|
||||
findPoly idx a b = fromMaybe (error $ "Missing: " ++ show idx) $ listToMaybe
|
||||
[ (n, p, (i+polygonOffset p) `mod` pSize p)
|
||||
| (n, p) <- zip [0..] polys
|
||||
, let pts = polygonPoints p
|
||||
, Just i <- [V.elemIndex a pts]
|
||||
, V.elem b pts
|
||||
]
|
||||
worker n | n == pSize origin = []
|
||||
worker n =
|
||||
let (polyIdx, poly, polyKey) = findPoly n (pAccess origin n) (pAccess origin $ n+1)
|
||||
originHere = pAdjustOffset origin n
|
||||
len = polyLength originHere poly
|
||||
in (polyIdx, polyKey, len) : worker (n+len)
|
||||
|
||||
|
||||
alignPolygons :: (Fractional a, Real a, Eq a) =>
|
||||
[(APolygon a, APolygon a)] -> APolygon a -> APolygon a -> [(APolygon a, APolygon a)]
|
||||
alignPolygons polys originL originR =
|
||||
zip
|
||||
(worker 0 (map fst polys) diffsL)
|
||||
(worker 0 (map snd polys) diffsR)
|
||||
where
|
||||
lensL = polygonLengths (map fst polys) originL
|
||||
lensR = polygonLengths (map snd polys) originR
|
||||
diffsL = sortOn sortKey $ zipWith mkDiff lensL lensR
|
||||
diffsR = sortOn sortKey $ zipWith mkDiff lensR lensL
|
||||
worker n (y:ys) ((i,k,l,d):xs) | n < i
|
||||
= y : worker (n+1) ys ((i,k,l,d):xs)
|
||||
worker n (y:ys) ((_i,_k,_l,0):xs)
|
||||
= worker n (y:ys) xs
|
||||
worker n (y:ys) ((_i,k,l,d):xs)
|
||||
= let y' = pAddPointsBetween (k,l) d y
|
||||
in worker n (y':ys) xs
|
||||
worker _ ys [] = ys
|
||||
worker _ _ _ = error "bad worker input"
|
||||
mkDiff (li, lk, ll) (_ri, _rk, rl) =
|
||||
(li, lk, ll, max 0 (rl-ll))
|
||||
sortKey (i, k, _l, _d) = (i,Down k)
|
||||
|
||||
compatTriagPairs :: (Real a, Fractional a) => [(APolygon a, APolygon a)] -> [(APolygon a, APolygon a)]
|
||||
compatTriagPairs = concatMap (uncurry Compat.compatiblyTriangulateP)
|
||||
|
||||
circumference :: (Real a, Fractional a) => [APolygon a] -> APolygon a -> APolygon a
|
||||
circumference polys origin' = mkPolygon $ V.fromList $ worker 0
|
||||
where
|
||||
Just firstIdx = V.elemIndex (pAccess (head polys) 0) (polygonPoints origin')
|
||||
origin = pSetOffset origin' firstIdx
|
||||
fromTo i j p
|
||||
| i < j = [i .. j-1]
|
||||
| otherwise = [i .. pSize p-1] ++ [0..j-1]
|
||||
check [] = error "no results"
|
||||
check [x] = x
|
||||
check _ = error "multiple results"
|
||||
findPoints n a b = check -- fromMaybe (error $ "Missing: " ++ show n) $ listToMaybe
|
||||
[ map (pAccess p) (fromTo i j p)
|
||||
| p <- map (flip pSetOffset 0) polys
|
||||
, let pts = polygonPoints p
|
||||
, Just i <- [V.elemIndex a pts]
|
||||
, Just j <- [V.elemIndex b pts]
|
||||
, if n == 2 then trace ("Found: " ++ show (i,j)) True else True
|
||||
]
|
||||
worker n | n == pSize origin = []
|
||||
worker n =
|
||||
let a = pAccess origin n
|
||||
b = pAccess origin (n+1)
|
||||
in findPoints n a b ++ worker (n+1)
|
||||
|
|
@ -2,21 +2,16 @@ module Reanimate.Math.Render where
|
|||
|
||||
import Codec.Picture.Types
|
||||
import Control.Monad
|
||||
-- import Data.List
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Vector as V
|
||||
import Linear.V2
|
||||
import Reanimate.Animation
|
||||
import Reanimate.ColorMap
|
||||
-- import Reanimate.Constants
|
||||
import Reanimate.LaTeX
|
||||
-- import Reanimate.Math.Compatible
|
||||
-- import Reanimate.Math.EarClip
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Math.Common
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Math.SSSP
|
||||
import Reanimate.Math.Triangulate
|
||||
-- import Reanimate.Math.Visibility
|
||||
import Reanimate.Scene
|
||||
import Reanimate.Svg
|
||||
|
||||
|
|
@ -37,18 +37,6 @@ Source-Repository head
|
|||
Type: git
|
||||
Location: git://github.com/lemmih/reanimate.git
|
||||
|
||||
-- If both 'enable-hmatrix' and 'disable-hmatrix' are false, then use
|
||||
-- hmatrix on Macos and Linux but not Windows.
|
||||
Flag enable-hmatrix
|
||||
Description: Enable hmatrix dependency on Windows (requires openblas)
|
||||
Default: False
|
||||
Manual: True
|
||||
|
||||
Flag disable-hmatrix
|
||||
Description: Force disable hmatrix dependency
|
||||
Default: False
|
||||
Manual: True
|
||||
|
||||
Flag test
|
||||
Description: Enable testing (requires stack)
|
||||
Default: False
|
||||
|
|
@ -68,7 +56,6 @@ library
|
|||
Reanimate.Svg.Constructors
|
||||
Reanimate.Svg.BoundingBox
|
||||
Reanimate.Svg.LineCommand
|
||||
-- Reanimate.Diagrams
|
||||
Reanimate.Transform
|
||||
Reanimate.Misc
|
||||
Reanimate.Math.Common
|
||||
|
|
@ -78,13 +65,9 @@ library
|
|||
Reanimate.Math.Visibility
|
||||
Reanimate.Math.SSSP
|
||||
Reanimate.Math.Balloon
|
||||
Reanimate.Math.Compatible
|
||||
Reanimate.Math.Render
|
||||
Reanimate.Morph.Common
|
||||
Reanimate.Morph.Linear
|
||||
Reanimate.Morph.Rotational
|
||||
Reanimate.Morph.LeastWork
|
||||
Reanimate.Morph.LineBend
|
||||
Reanimate.Morph.Cache
|
||||
Reanimate.Raster
|
||||
Reanimate.ColorComponents
|
||||
|
|
@ -102,7 +85,6 @@ library
|
|||
Reanimate.Builtin.Slide
|
||||
Reanimate.Constants
|
||||
Reanimate.Parameters
|
||||
Reanimate.Chiphunk
|
||||
Reanimate.PolyShape
|
||||
Reanimate.GeoProjection
|
||||
Reanimate.Builtin.Documentation
|
||||
|
|
@ -118,23 +100,48 @@ library
|
|||
Reanimate.Driver.Server
|
||||
Reanimate.Driver.Compile
|
||||
Paths_reanimate
|
||||
build-depends: base >=4.10 && <5,
|
||||
time, text, filepath, process, directory,
|
||||
containers, reanimate-svg >= 0.10.0.0, xml, bytestring, lens, linear, mtl, matrix,
|
||||
JuicyPixels >= 3.3.3, attoparsec, parallel,
|
||||
cubicbezier, websockets >= 0.12.7.0,
|
||||
hashable, fsnotify, open-browser, random, random-shuffle, base64-bytestring,
|
||||
vector >= 0.12.0.0, colour, cassava, ansi-terminal, temporary,
|
||||
optparse-applicative, chiphunk >= 0.1.2.1,
|
||||
geojson, aeson >= 1.3.0.0, earcut >= 0.1.0.4,
|
||||
split, array, cereal, vector-space,
|
||||
neat-interpolation >= 0.3, ghcid, filelock
|
||||
if (flag(enable-hmatrix) || !os(windows)) && !flag(disable-hmatrix) {
|
||||
build-depends: hmatrix >= 0.20.0.0
|
||||
exposed-modules: Reanimate.Morph.Rigid
|
||||
Reanimate.Math.Smooth
|
||||
Reanimate.Math.DCEL
|
||||
}
|
||||
build-depends:
|
||||
base >=4.10 && <5,
|
||||
JuicyPixels >=3.3.3,
|
||||
aeson >=1.3.0.0,
|
||||
ansi-terminal >=0.8.0.4,
|
||||
array >=0.5.2.0,
|
||||
attoparsec >=0.13.2.0,
|
||||
base64-bytestring >=1.0.0.1,
|
||||
bytestring >=0.10.8.0,
|
||||
cassava >=0.5.1.0,
|
||||
cereal >=0.5.5.0,
|
||||
colour >=2.3.4,
|
||||
containers >=0.5.11.0,
|
||||
cubicbezier >=0.6.0.5,
|
||||
directory >=1.3.1.0,
|
||||
earcut >=0.1.0.4,
|
||||
filelock >=0.1.1.2,
|
||||
filepath >=1.4.2,
|
||||
fsnotify >=0.3.0.1,
|
||||
geojson >=3.0.4,
|
||||
ghcid >=0.7,
|
||||
hashable >=1.2.7.0,
|
||||
lens >=4.16.1,
|
||||
linear >=1.20.8,
|
||||
matrix >=0.3.6.1,
|
||||
mtl >=2.2.2,
|
||||
neat-interpolation >=0.3,
|
||||
open-browser >=0.2.1.0,
|
||||
optparse-applicative >=0.14.2.0,
|
||||
parallel >=3.2.1.0,
|
||||
process >=1.6.3.0,
|
||||
random >=1.1,
|
||||
random-shuffle >=0.0.4,
|
||||
reanimate-svg >=0.10.0.0,
|
||||
split >=0.2.3.3,
|
||||
temporary >=1.3,
|
||||
text >=1.2.3.0,
|
||||
time >=1.8.0.0,
|
||||
vector >=0.12.0.0,
|
||||
vector-space >=0.13,
|
||||
websockets >=0.12.7.0,
|
||||
xml >=1.3.14
|
||||
ghc-options: -Wall -fno-ignore-asserts
|
||||
|
||||
test-suite spec
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
module Reanimate.Chiphunk
|
||||
( simulate
|
||||
, BodyStore
|
||||
, newBodyStore
|
||||
, addToBodyStore
|
||||
, spaceFreeRecursive
|
||||
, polyShapesToBody
|
||||
, polygonsToBody
|
||||
) where
|
||||
|
||||
import Chiphunk.Low
|
||||
import Control.Monad
|
||||
import Data.IORef
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Vector as V
|
||||
import qualified Data.Vector.Mutable as V
|
||||
import Foreign.Ptr
|
||||
import Graphics.SvgTree (Tree)
|
||||
import Linear.V2 (V2(..))
|
||||
import Reanimate.Animation
|
||||
import Reanimate.PolyShape
|
||||
import Reanimate.Svg.Constructors
|
||||
|
||||
type BodyStore = IORef (Map WordPtr Tree)
|
||||
|
||||
newBodyStore :: IO BodyStore
|
||||
newBodyStore = newIORef Map.empty
|
||||
|
||||
addToBodyStore :: BodyStore -> Body -> Tree -> IO ()
|
||||
addToBodyStore store body svg = do
|
||||
key <- atomicModifyIORef' store $ \m ->
|
||||
case Map.maxViewWithKey m of
|
||||
Nothing -> (Map.singleton 1 svg, 1)
|
||||
Just ((maxKey,_),_) ->
|
||||
(Map.insert (maxKey+1) svg m, maxKey+1)
|
||||
bodyUserData body $= wordPtrToPtr key
|
||||
|
||||
renderBodyStore :: Space -> BodyStore -> IO Tree
|
||||
renderBodyStore space store = do
|
||||
m <- readIORef store
|
||||
lst <- newIORef []
|
||||
spaceEachBody space (\body _dat -> do
|
||||
key <- get (bodyUserData body)
|
||||
case Map.lookup (ptrToWordPtr key) m of
|
||||
Nothing -> putStrLn "Body doesn't have an associated SVG"
|
||||
Just svg -> do
|
||||
Vect posX posY <- get $ bodyPosition body
|
||||
angle <- get $ bodyAngle body
|
||||
let bodySvg =
|
||||
translate posX posY $
|
||||
rotate (angle/pi*180)
|
||||
svg
|
||||
modifyIORef lst (bodySvg:)
|
||||
) nullPtr
|
||||
result <- readIORef lst
|
||||
return $ mkGroup result
|
||||
|
||||
|
||||
simulate :: Space -> BodyStore -> Double -> Int -> Double -> IO Animation
|
||||
simulate space store fps stepsPerFrame dur = do
|
||||
let timeStep = 1/(fps*fromIntegral stepsPerFrame)
|
||||
frames = round (dur * fps)
|
||||
v <- V.new frames
|
||||
forM_ [0..frames-1] $ \nth -> do
|
||||
svg <- renderBodyStore space store
|
||||
V.write v nth svg
|
||||
replicateM_ stepsPerFrame $ spaceStep space timeStep
|
||||
frozen <- V.unsafeFreeze v
|
||||
return $ mkAnimation dur $ \t ->
|
||||
let key = round (t * fromIntegral (frames-1))
|
||||
in frozen V.! key
|
||||
|
||||
polyShapesToBody :: Space -> [PolyShape] -> IO Body
|
||||
polyShapesToBody space poly =
|
||||
polygonsToBody space (map (map toVect) $ plDecompose poly)
|
||||
where
|
||||
toVect (V2 x y) = Vect x y
|
||||
|
||||
polygonsToBody :: Space -> [[Vect]] -> IO Body
|
||||
polygonsToBody space polygons = do
|
||||
plBody <- bodyNew 0 0
|
||||
spaceAddBody space plBody
|
||||
|
||||
forM_ polygons $ \vects -> do
|
||||
polyShape <- polyShapeNewRaw plBody vects 0.00
|
||||
shapeDensity polyShape $= 1
|
||||
spaceAddShape space polyShape
|
||||
shapeFriction polyShape $= 0.7
|
||||
shapeElasticity polyShape $= 0.5
|
||||
return plBody
|
||||
|
||||
spaceFreeRecursive :: Space -> IO ()
|
||||
spaceFreeRecursive space = do
|
||||
spaceEachBody space (\body _ -> bodyFree body) nullPtr
|
||||
spaceEachShape space (\shape _ -> shapeFree shape) nullPtr
|
||||
spaceFree space
|
||||
|
|
@ -13,12 +13,13 @@ module Reanimate.Math.Common
|
|||
, area2X -- :: Fractional a => V2 a -> V2 a -> V2 a -> a
|
||||
, epsilon -- :: Fractional a => a
|
||||
, epsEq -- :: (Ord a, Fractional a) => a -> a -> Bool
|
||||
, isLeftTurn -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isLeftTurnOrLinear -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isRightTurn -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isRightTurnOrLinear -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, direction -- :: Fractional a => V2 a -> V2 a -> V2 a -> a
|
||||
, isLeftTurn -- :: (Num a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isLeftTurnOrLinear -- :: (Num a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isRightTurn -- :: (Num a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, isRightTurnOrLinear -- :: (Num a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
, direction -- :: Num a => V2 a -> V2 a -> V2 a -> a
|
||||
, isInside -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> V2 a -> Bool
|
||||
, isInsideStrict -- :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> V2 a -> Bool
|
||||
, barycentricCoords -- :: Fractional a => V2 a -> V2 a -> V2 a -> V2 a -> (a, a, a)
|
||||
, rayIntersect -- :: (Fractional a, Ord a) => (V2 a,V2 a) -> (V2 a,V2 a) -> Maybe (V2 a)
|
||||
, isBetween -- :: (Ord a, Fractional a) => V2 a -> (V2 a, V2 a) -> Bool
|
||||
|
|
@ -77,21 +78,26 @@ epsilon = 1e-13
|
|||
epsEq :: (Ord a, Fractional a) => a -> a -> Bool
|
||||
epsEq a b = abs (a-b) < epsilon
|
||||
|
||||
compareEpsZero :: (Ord a, Fractional a) => a -> Ordering
|
||||
compareEpsZero val
|
||||
| abs val < epsilon = EQ
|
||||
| otherwise = compare val 0
|
||||
|
||||
{-# INLINE isLeftTurn #-}
|
||||
-- Left turn.
|
||||
isLeftTurn :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
isLeftTurn p1 p2 p3 =
|
||||
case compare (direction p1 p2 p3) 0 of
|
||||
case compareEpsZero (direction p1 p2 p3) of
|
||||
LT -> True
|
||||
EQ -> False -- colnear
|
||||
EQ -> False -- colinear
|
||||
GT -> False
|
||||
|
||||
{-# INLINE isLeftTurnOrLinear #-}
|
||||
isLeftTurnOrLinear :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
isLeftTurnOrLinear p1 p2 p3 =
|
||||
case compare (direction p1 p2 p3) 0 of
|
||||
case compareEpsZero (direction p1 p2 p3) of
|
||||
LT -> True
|
||||
EQ -> True -- colnear
|
||||
EQ -> True -- colinear
|
||||
GT -> False
|
||||
|
||||
{-# INLINE isRightTurn #-}
|
||||
|
|
@ -103,15 +109,22 @@ isRightTurnOrLinear :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> Bool
|
|||
isRightTurnOrLinear a b c = not (isLeftTurn a b c)
|
||||
|
||||
{-# INLINE direction #-}
|
||||
direction :: Fractional a => V2 a -> V2 a -> V2 a -> a
|
||||
direction :: Num a => V2 a -> V2 a -> V2 a -> a
|
||||
direction p1 p2 p3 = crossZ (p3-p1) (p2-p1)
|
||||
|
||||
{-# INLINE isInside #-}
|
||||
isInside :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> V2 a -> Bool
|
||||
isInside a b c d =
|
||||
s >= 0 && s <= 1 && t >= 0 && t <= 1
|
||||
s >= 0 && s <= 1 && t >= 0 && t <= 1 && i >= 0 && i <= 1
|
||||
where
|
||||
(s, t, _) = barycentricCoords a b c d
|
||||
(s, t, i) = barycentricCoords a b c d
|
||||
|
||||
{-# INLINE isInsideStrict #-}
|
||||
isInsideStrict :: (Fractional a, Ord a) => V2 a -> V2 a -> V2 a -> V2 a -> Bool
|
||||
isInsideStrict a b c d =
|
||||
s > 0 && s < 1 && t > 0 && t < 1 && i > 0 && i < 1
|
||||
where
|
||||
(s, t, i) = barycentricCoords a b c d
|
||||
|
||||
{-# INLINE barycentricCoords #-}
|
||||
barycentricCoords :: Fractional a => V2 a -> V2 a -> V2 a -> V2 a -> (a, a, a)
|
||||
|
|
@ -153,7 +166,7 @@ lineIntersect a b =
|
|||
|
||||
-- circleIntersect :: (Ord a, Fractional a) => (V2 a, V2 a) -> (V2 a, V2 a) -> [V2 a]
|
||||
|
||||
distSquared :: (Fractional a) => V2 a -> V2 a -> a
|
||||
distSquared :: (Num a) => V2 a -> V2 a -> a
|
||||
distSquared a b = quadrance (a ^-^ b)
|
||||
|
||||
approxDist :: (Real a, Fractional a) => V2 a -> V2 a -> a
|
||||
|
|
|
|||
|
|
@ -15,15 +15,14 @@ import Data.Maybe
|
|||
import qualified Data.Set as S
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Vector as V
|
||||
-- import Debug.Trace
|
||||
import Linear.Metric
|
||||
import Linear.V2
|
||||
import Linear.Vector (lerp, (^/))
|
||||
import Reanimate
|
||||
import Reanimate.Math.Common (isInside, lineIntersect, triangleAngles)
|
||||
import Reanimate.Math.Polygon
|
||||
import qualified Reanimate.Morph.Rigid as Rigid
|
||||
import Reanimate.Math.Common (isInsideStrict, lineIntersect,
|
||||
triangleAngles)
|
||||
import Text.Printf
|
||||
-- import Debug.Trace
|
||||
|
||||
type VertexId = Int
|
||||
type EdgeId = Int
|
||||
|
|
@ -179,8 +178,8 @@ emptyMesh = Mesh 1 0 M.empty M.empty M.empty
|
|||
|
||||
polygonsMesh :: Eq a => [a] -> [[a]] -> MeshM a ()
|
||||
polygonsMesh outer trigs = do
|
||||
polygonMeshOuter (reverse outer)
|
||||
modify $ meshFaces .~ M.empty
|
||||
polygonMeshOuter (reverse outer)
|
||||
forM_ trigs $ \pts -> do
|
||||
innerFace <- createFace
|
||||
vIds <- mapM getVertex pts
|
||||
|
|
@ -552,26 +551,43 @@ meshSmoothPosition :: Mesh (V2 Double) -> Mesh (V2 Double)
|
|||
meshSmoothPosition = execState worker
|
||||
where
|
||||
worker = gets steinerNodes >>= mapM_ smoothVertex
|
||||
-- worker = mapM_ smoothVertex [221]
|
||||
|
||||
smoothVertex :: VertexId -> MeshM (V2 Double) ()
|
||||
smoothVertex steiner = do
|
||||
self <- _vertexPosition <$> requireVertex steiner
|
||||
es <- gets (outgoingEdges steiner)
|
||||
vs <- mapM (requireVertex . _edgeVertex) es
|
||||
let ps = V.fromList (sortEdges self (map _vertexPosition vs))
|
||||
let ps = V.fromList (map _vertexPosition vs')
|
||||
vs' = sortVertices self vs
|
||||
angleBased = angleSmooth self ps
|
||||
laplacian = sum ps ^/ (fromIntegral $ length ps) -- laplacian
|
||||
-- trace ("self: " ++ show self) $ return ()
|
||||
-- trace ("angleBased: " ++ show angleBased) $ return ()
|
||||
-- trace ("Edges: " ++ show (map _vertexId vs')) $ return ()
|
||||
-- trace ("Valid angleBased: " ++ show (isValidLocation self ps angleBased)) $ return ()
|
||||
-- trace ("Valid laplacian: " ++ show (isValidLocation self ps laplacian)) $ return ()
|
||||
-- let checks = [ isInsideStrict self a b angleBased
|
||||
-- | i <- [0 .. length ps - 1]
|
||||
-- , let a = ps V.! i
|
||||
-- b = ps V.! mod (i + 1) (length ps)
|
||||
-- ]
|
||||
-- trace ("Checks: " ++ show checks) $ return ()
|
||||
if isValidLocation self ps angleBased
|
||||
then modifyVertex (vertexPosition .~ angleBased) steiner
|
||||
else if isValidLocation self ps laplacian
|
||||
then modifyVertex (vertexPosition .~ laplacian) steiner
|
||||
else return ()
|
||||
where
|
||||
sortEdges :: V2 Double -> [V2 Double] -> [V2 Double]
|
||||
sortEdges = sortOn . dir
|
||||
-- sortEdges :: V2 Double -> [V2 Double] -> [V2 Double]
|
||||
-- sortEdges = sortOn . dir
|
||||
sortVertices :: V2 Double -> [Vertex (V2 Double)] -> [Vertex (V2 Double)]
|
||||
sortVertices self = sortOn (dirV self)
|
||||
-- Direction from south of 'a', to 'a', to 'b'.
|
||||
dir :: V2 Double -> V2 Double -> Double
|
||||
dir a b = (atan2 (crossZ (V2 0 1) (b - a)) (dot (V2 0 1) (b - a)))
|
||||
dirV :: V2 Double -> Vertex (V2 Double) -> Double
|
||||
dirV a v = dir a (_vertexPosition v)
|
||||
|
||||
|
||||
angleSmooth :: V2 Double -> V.Vector (V2 Double) -> V2 Double
|
||||
|
|
@ -604,7 +620,7 @@ angleSmooth origin js = V.sum (V.generate n nth) ^/ V.sum (V.generate n factor)
|
|||
isValidLocation :: V2 Double -> V.Vector (V2 Double) -> V2 Double -> Bool
|
||||
isValidLocation origin edges newLoc =
|
||||
or
|
||||
[ isInside origin a b newLoc
|
||||
[ isInsideStrict origin a b newLoc
|
||||
| i <- [0 .. length edges - 1]
|
||||
, let a = edges V.! i
|
||||
b = edges V.! mod (i + 1) (length edges)
|
||||
|
|
@ -617,6 +633,11 @@ isValidLocation origin edges newLoc =
|
|||
dir :: V2 Double -> V2 Double -> Double
|
||||
dir a b = (atan2 (crossZ (V2 0 1) (b - a)) (dot (V2 0 1) (b - a)))
|
||||
|
||||
isCCW :: (Ord a, Num a) => V2 a -> V2 a -> V2 a -> Bool
|
||||
isCCW a b c = sum [fn a b, fn b c, fn c a] < 0
|
||||
where
|
||||
fn (V2 x1 y1) (V2 x2 y2) = (x2-x1)*(y2+y1)
|
||||
|
||||
minAngle :: V2 Double -> V.Vector (V2 Double) -> Double
|
||||
minAngle origin edges = minimum $ concat
|
||||
[ [a1, a2, a3]
|
||||
|
|
@ -703,7 +724,7 @@ delaunayFlip' eid m =
|
|||
((f1',f2'), mAfter) = runState (flipEdge edge) m
|
||||
afterAng = min (faceMinAngle f1' mAfter) (faceMinAngle f2' mAfter)
|
||||
in
|
||||
if (afterAng < beforeAng) || f1' == f1
|
||||
if (afterAng <= beforeAng) || f1' == f1
|
||||
then Nothing
|
||||
else Just mAfter
|
||||
|
||||
|
|
@ -861,31 +882,3 @@ renderMeshStats mesh = mkGroup
|
|||
meanAngle = L.sort angs !! (length angs `div` 2)
|
||||
avgAngle = sum angs / (fromIntegral $ length angs)
|
||||
maxAngle = maximum angs
|
||||
|
||||
toRigidMesh :: Mesh (V2 Double) -> Mesh (V2 Double) -> Rigid.Mesh
|
||||
toRigidMesh meshA meshB = Rigid.Mesh
|
||||
{ meshPointsA = pointsA
|
||||
, meshPointsB = pointsB
|
||||
, meshOutline = outline
|
||||
, meshTriangles = trigs }
|
||||
where
|
||||
pointsA = V.fromList $ map _vertexPosition $ M.elems (meshA^.meshVertices)
|
||||
pointsB = V.fromList $ map _vertexPosition $ M.elems (meshB^.meshVertices)
|
||||
outline = V.fromList
|
||||
[ fromJust (V.elemIndex v pointsA)
|
||||
| eid <- faceEdges (meshA^.meshOuterFace) meshA
|
||||
, let edge = meshGetEdge eid meshA
|
||||
v = _vertexPosition (meshGetVertex (edge^.edgeVertex) meshA)
|
||||
]
|
||||
trigs = V.fromList
|
||||
[ (aIdx, bIdx, cIdx)
|
||||
| fid <- M.keys (meshA^.meshFaces)
|
||||
, fid /= (meshA^.meshOuterFace)
|
||||
, let edges = map (`meshGetEdge` meshA) (faceEdges fid meshA)
|
||||
vs = map (`meshGetVertex` meshA) $ map _edgeVertex edges
|
||||
ps = map _vertexPosition vs
|
||||
[aIdx, bIdx, cIdx] = map (fromJust . (`V.elemIndex` pointsA)) ps
|
||||
p = mkPolygon (V.fromList $ map (fmap realToFrac) ps)
|
||||
, pIsSimple p || error "invalid polygon"
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{-# LANGUAGE BangPatterns #-}
|
||||
module Reanimate.Math.Polygon
|
||||
( APolygon(..)
|
||||
, Polygon
|
||||
|
|
@ -5,6 +6,7 @@ module Reanimate.Math.Polygon
|
|||
, P
|
||||
, mkPolygon -- :: (Fractional a, Ord a) => V.Vector (V2 a) -> APolygon a
|
||||
, mkPolygonFromRing -- :: (Fractional a, Ord a) => Ring a -> APolygon a
|
||||
, castPolygon -- :: (Real a, Fractional b, Ord a) => APolygon a -> APolygon b
|
||||
, pParent -- :: Polygon -> Int -> Int -> Int
|
||||
, pSetOffset -- :: APolygon a -> Int -> APolygon a
|
||||
, pAdjustOffset -- :: APolygon a -> Int -> APolygon a
|
||||
|
|
@ -33,7 +35,10 @@ module Reanimate.Math.Polygon
|
|||
, pCircumference -- :: (Real a, Fractional a) => APolygon a -> a
|
||||
, pCircumference' -- :: (Real a, Fractional a) => APolygon a -> Double
|
||||
, pAddPoints -- :: Int -> Polygon -> Polygon
|
||||
, pAddPointsRestricted -- :: [Int] -> Int -> Polygon -> Polygon
|
||||
, pAddPointsBetween -- :: (Fractional a, Ord a, Real a) => (Int, Int) -> Int -> APolygon a -> APolygon a
|
||||
, pRayIntersect -- :: Polygon -> (Int, Int) -> (Int,Int) -> Maybe (V2 Rational)
|
||||
, pOverlap -- :: Polygon -> Polygon -> Polygon
|
||||
, pCuts -- :: Polygon -> [(Polygon,Polygon)]
|
||||
, pCutEqual -- :: Polygon -> (Polygon, Polygon)
|
||||
-- * Triangulation
|
||||
|
|
@ -119,14 +124,14 @@ instance Show a => Show (APolygon a) where
|
|||
instance Hashable a => Hashable (APolygon a) where
|
||||
hashWithSalt s p = V.foldl' hashWithSalt s (polygonPoints p)
|
||||
|
||||
instance (Real a, Fractional a, Ord a, Serialize a) => Serialize (APolygon a) where
|
||||
instance (Real a, Fractional a, Serialize a) => Serialize (APolygon a) where
|
||||
put = put . V.toList . polygonPoints
|
||||
get = mkPolygon . V.fromList <$> get
|
||||
|
||||
pRing :: APolygon a -> Ring a
|
||||
pRing = ringPack . polygonPoints
|
||||
|
||||
mkPolygon :: (Real a, Fractional a, Ord a) => V.Vector (V2 a) -> APolygon a
|
||||
mkPolygon :: (Real a, Fractional a) => V.Vector (V2 a) -> APolygon a
|
||||
mkPolygon points = Polygon
|
||||
{ polygonPoints = points
|
||||
, polygonOffset = 0
|
||||
|
|
@ -139,6 +144,9 @@ mkPolygon points = Polygon
|
|||
trig = earCut ring
|
||||
-- earClip ring
|
||||
|
||||
castPolygon :: (Real a, Real b, Fractional b) => APolygon a -> APolygon b
|
||||
castPolygon = mkPolygon . V.map (fmap realToFrac) . polygonPoints
|
||||
|
||||
mkPolygonFromRing :: (Real a, Fractional a, Ord a) => Ring a -> APolygon a
|
||||
mkPolygonFromRing = mkPolygon . ringUnpack
|
||||
|
||||
|
|
@ -146,7 +154,7 @@ pUnsafeMap :: (Ring a -> Ring a) -> APolygon a -> APolygon a
|
|||
pUnsafeMap fn p = p{ polygonPoints = ringUnpack (fn (pRing p)) }
|
||||
|
||||
-- pParent p i j = shortest-path parent from j to i
|
||||
pParent :: Polygon -> Int -> Int -> Int
|
||||
pParent :: APolygon a -> Int -> Int -> Int
|
||||
pParent p i j =
|
||||
(sTree V.! mod (j + polygonOffset p) n - polygonOffset p) `mod` n
|
||||
where
|
||||
|
|
@ -164,6 +172,7 @@ pAdjustOffset :: APolygon a -> Int -> APolygon a
|
|||
pAdjustOffset p offset =
|
||||
p { polygonOffset = (polygonOffset p + offset) `mod` pSize p }
|
||||
|
||||
{-# INLINE pSize #-}
|
||||
pSize :: APolygon a -> Int
|
||||
pSize = length . polygonPoints
|
||||
|
||||
|
|
@ -316,9 +325,10 @@ pIsInside p point = or
|
|||
-- findSmallest = minimumBy (comparing area2X)
|
||||
-- shareEdge p1 p2 =
|
||||
|
||||
|
||||
{-# INLINE pAccess #-}
|
||||
pAccess :: APolygon a -> Int -> V2 a
|
||||
pAccess p i = polygonPoints p V.! ((polygonOffset p + i) `mod` pSize p)
|
||||
pAccess p i = -- polygonPoints p V.! ((polygonOffset p + i) `mod` pSize p)
|
||||
polygonPoints p `V.unsafeIndex` ((polygonOffset p + i) `mod` pSize p)
|
||||
|
||||
triangle :: Polygon
|
||||
triangle = mkPolygon $ V.fromList [V2 1 1, V2 0 0, V2 2 0]
|
||||
|
|
@ -503,7 +513,7 @@ pDeoverlap p = mkPolygon arr
|
|||
in lerp 0.99999 this prev
|
||||
else pAccess p n
|
||||
|
||||
pCycles :: Polygon -> [Polygon]
|
||||
pCycles :: APolygon a -> [APolygon a]
|
||||
pCycles p = map (pAdjustOffset p) [0 .. pSize p-1]
|
||||
|
||||
pCycle :: (Real a, Fractional a, Ord a) => APolygon a -> Double -> APolygon a
|
||||
|
|
@ -526,7 +536,7 @@ pCycle p t = mkPolygon $ worker 0 0
|
|||
len = pCircumference' p
|
||||
limit = t * len
|
||||
|
||||
pCentroid :: Polygon -> V2 Rational
|
||||
pCentroid :: Fractional a => APolygon a -> V2 a
|
||||
pCentroid p = V2 cx cy
|
||||
where
|
||||
a = pArea p
|
||||
|
|
@ -535,13 +545,29 @@ pCentroid p = V2 cx cy
|
|||
fnX (V2 x y) (V2 x' y') = (x+x')*(x*y' - x'*y)
|
||||
fnY (V2 x y) (V2 x' y') = (y+y')*(x*y' - x'*y)
|
||||
|
||||
pMapEdges :: (V2 Rational -> V2 Rational -> a) -> Polygon -> V.Vector a
|
||||
pMapEdges fn p = V.generate (pSize p) $ \i ->
|
||||
fn (pAccess p i) (pAccess p $ i+1)
|
||||
{-# INLINE pMapEdges #-}
|
||||
pMapEdges :: (V2 a -> V2 a -> b) -> APolygon a -> V.Vector b
|
||||
pMapEdges fn p = V.generate n $ \i ->
|
||||
if i == n-1
|
||||
then fn (arr `V.unsafeIndex` i) (arr `V.unsafeIndex` 0)
|
||||
else fn (arr `V.unsafeIndex` i) (arr `V.unsafeIndex` (i+1))
|
||||
where
|
||||
n = pSize p
|
||||
arr = polygonPoints p
|
||||
|
||||
pArea :: Polygon -> Rational
|
||||
{-# SPECIALIZE pArea :: APolygon Double -> Double #-}
|
||||
{-# SPECIALIZE pArea :: APolygon Rational -> Rational #-}
|
||||
pArea :: (Fractional a) => APolygon a -> a
|
||||
pArea p =
|
||||
0.5 * V.sum (pMapEdges (\(V2 x y) (V2 x' y') -> x*y' - x'*y) p)
|
||||
-- 0.5 * V.sum (pMapEdges (\(V2 x y) (V2 x' y') -> x*y' - x'*y) p)
|
||||
0.5 * worker 0 0
|
||||
where
|
||||
fn (V2 x y) (V2 x' y') = x*y' - x'*y
|
||||
arr = polygonPoints p
|
||||
worker !acc i
|
||||
| i == pSize p - 1 = acc + fn (arr `V.unsafeIndex` i) (arr `V.unsafeIndex` 0)
|
||||
| otherwise =
|
||||
worker (acc + fn (arr `V.unsafeIndex` i) (arr `V.unsafeIndex` (i+1))) (i+1)
|
||||
|
||||
pCircumference :: (Real a, Fractional a) => APolygon a -> a
|
||||
pCircumference p = sum
|
||||
|
|
@ -555,7 +581,7 @@ pCircumference' p = sum
|
|||
|
||||
|
||||
-- Add points by splitting the longest lines in half repeatedly.
|
||||
pAddPoints :: Int -> Polygon -> Polygon
|
||||
pAddPoints :: (Ord a, Real a, Fractional a) => Int -> APolygon a -> APolygon a
|
||||
pAddPoints n p | n <= 0 = p
|
||||
pAddPoints n p = pAddPoints (n-1) $
|
||||
mkPolygon $ V.fromList $ concatMap worker [0 .. pSize p-1]
|
||||
|
|
@ -572,6 +598,45 @@ pAddPoints n p = pAddPoints (n-1) $
|
|||
distSquared (pAccess p a) (pAccess p $ a+1) `compare`
|
||||
distSquared (pAccess p b) (pAccess p $ b+1)
|
||||
|
||||
pAddPointsRestricted :: (Fractional a, Ord a, Real a) => [(V2 a, V2 a)] -> Int -> APolygon a -> APolygon a
|
||||
pAddPointsRestricted _immutableEdges n p | n <= 0 = p
|
||||
pAddPointsRestricted immutableEdges n p = pAddPointsRestricted immutableEdges (n-1) $
|
||||
mkPolygon $ V.fromList $ concatMap worker [0 .. pSize p-1]
|
||||
where
|
||||
isImmutable idx =
|
||||
(pAccess p idx, pAccess p $ idx+1) `elem` immutableEdges ||
|
||||
(pAccess p $ idx+1, pAccess p idx) `elem` immutableEdges
|
||||
worker idx
|
||||
| idx == longestEdge && not (isImmutable idx) =
|
||||
let start = pAccess p idx
|
||||
end = pAccess p $ idx+1
|
||||
middle = lerp 0.5 end start
|
||||
in [start, middle]
|
||||
| otherwise = [pAccess p idx]
|
||||
longestEdge = maximumBy cmpLength [0 .. pSize p-1]
|
||||
cmpLength a _ | isImmutable a = LT
|
||||
cmpLength _ b | isImmutable b = GT
|
||||
cmpLength a b =
|
||||
distSquared (pAccess p a) (pAccess p $ a+1) `compare`
|
||||
distSquared (pAccess p b) (pAccess p $ b+1)
|
||||
|
||||
pAddPointsBetween :: (Fractional a, Ord a, Real a) => (Int, Int) -> Int -> APolygon a -> APolygon a
|
||||
pAddPointsBetween _ n p | n <= 0 = p
|
||||
pAddPointsBetween (i,l) n p = pAddPointsBetween (i,l+1) (n-1) $
|
||||
mkPolygon $ V.fromList $ concatMap worker [0 .. pSize p-1]
|
||||
where
|
||||
worker idx
|
||||
| idx == longestEdge =
|
||||
let start = pAccess p idx
|
||||
end = pAccess p $ idx+1
|
||||
middle = lerp 0.5 end start
|
||||
in [start, middle]
|
||||
| otherwise = [pAccess p idx]
|
||||
longestEdge = maximumBy cmpLength [i .. i+l-1]
|
||||
cmpLength a b =
|
||||
distSquared (pAccess p a) (pAccess p $ a+1) `compare`
|
||||
distSquared (pAccess p b) (pAccess p $ b+1)
|
||||
|
||||
-- addPoints :: Int -> Polygon -> Polygon
|
||||
-- addPoints n p = mkPolygon $ V.fromList $ worker n 0 (map (pAccess p) [0..s])
|
||||
-- where
|
||||
|
|
@ -603,11 +668,11 @@ pIsCCW p = V.sum (pMapEdges fn p) < 0
|
|||
fn (V2 x1 y1) (V2 x2 y2) = (x2-x1)*(y2+y1)
|
||||
|
||||
{-# INLINE pRayIntersect #-}
|
||||
pRayIntersect :: Polygon -> (Int, Int) -> (Int,Int) -> Maybe (V2 Rational)
|
||||
pRayIntersect :: (Fractional a, Ord a) => APolygon a -> (Int, Int) -> (Int,Int) -> Maybe (V2 a)
|
||||
pRayIntersect p (a,b) (c,d) =
|
||||
rayIntersect (pAccess p a, pAccess p b) (pAccess p c, pAccess p d)
|
||||
|
||||
pCuts :: Polygon -> [(Polygon,Polygon)]
|
||||
pCuts :: (Real a, Fractional a) => APolygon a -> [(APolygon a,APolygon a)]
|
||||
pCuts p =
|
||||
[ pCutAt (pAdjustOffset p i) (j-i)
|
||||
| i <- [0 .. pSize p-1 ]
|
||||
|
|
@ -615,26 +680,41 @@ pCuts p =
|
|||
, (j+1) `mod` pSize p /= i
|
||||
, pParent p i j == i ]
|
||||
|
||||
pCutEqual :: Polygon -> (Polygon, Polygon)
|
||||
pCutEqual :: (Real a, Fractional a) => APolygon a -> (APolygon a, APolygon a)
|
||||
pCutEqual p =
|
||||
fromMaybe (p,p) $ listToMaybe $ sortOn f $ pCuts p
|
||||
where
|
||||
f (a,b) = abs (pArea a - pArea b)
|
||||
|
||||
-- FIXME: This should be more efficient
|
||||
pCutAt :: Polygon -> Int -> (Polygon, Polygon)
|
||||
pCutAt :: (Real a, Fractional a) => APolygon a -> Int -> (APolygon a, APolygon a)
|
||||
pCutAt p i = (mkPolygon $ V.fromList left, mkPolygon $ V.fromList right)
|
||||
where
|
||||
n = pSize p
|
||||
left = map (pAccess p) [0 .. i]
|
||||
right = map (pAccess p) (0:[i..n-1])
|
||||
|
||||
|
||||
pOverlap :: (Fractional a, Ord a, Real a) => APolygon a -> APolygon a -> APolygon a
|
||||
pOverlap a b = mkPolygon $ V.fromList $ clearDups $ concatMap edgeIntersect [0 .. pSize a-1]
|
||||
where
|
||||
clearDups (x:y:xs)
|
||||
| x == y = clearDups (y:xs)
|
||||
| otherwise = x : clearDups (y:xs)
|
||||
clearDups xs = xs
|
||||
edgeIntersect edge =
|
||||
sortOn (distSquared (pAccess a edge)) $ catMaybes
|
||||
[ lineIntersect (aP, aP') (bP, bP')
|
||||
| i <- [0 .. pSize b-1]
|
||||
, let aP = pAccess a edge
|
||||
aP' = pAccess a (edge+1)
|
||||
bP = pAccess b i
|
||||
bP' = pAccess b (i+1)
|
||||
]
|
||||
|
||||
---------------------------------------------------------
|
||||
-- SSSP visibility and SSSP windows
|
||||
|
||||
ssspVisibility :: Polygon -> Polygon
|
||||
ssspVisibility :: (Real a, Fractional a) => APolygon a -> APolygon a
|
||||
ssspVisibility p = mkPolygon $
|
||||
V.fromList $ clearDups $ go [0 .. pSize p-1] -- ([root..pSize p-1] ++ [0 .. root-1])
|
||||
where
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import Linear.V2
|
|||
import Reanimate.Animation
|
||||
import Reanimate.ColorComponents
|
||||
import Reanimate.Ease
|
||||
import Reanimate.Math.Polygon (Polygon, mkPolygon, pAddPoints,
|
||||
import Reanimate.Math.Polygon (Polygon, APolygon, mkPolygon, pAddPoints,
|
||||
pCentroid, pCutEqual, pSize,
|
||||
polygonPoints)
|
||||
import Reanimate.PolyShape
|
||||
|
|
@ -55,6 +55,7 @@ data Morph = Morph
|
|||
, morphObjectCorrespondence :: ObjectCorrespondence
|
||||
}
|
||||
|
||||
{-# INLINE morph #-}
|
||||
morph :: Morph -> SVG -> SVG -> Double -> SVG
|
||||
morph Morph{..} src dst = \t ->
|
||||
case t of
|
||||
|
|
@ -77,7 +78,7 @@ morph Morph{..} src dst = \t ->
|
|||
, let arranged = morphPointCorrespondence srcPoly' dstPoly'
|
||||
]
|
||||
|
||||
normalizePolygons :: Polygon -> Polygon -> (Polygon, Polygon)
|
||||
normalizePolygons :: (Real a, Fractional a) => APolygon a -> APolygon a -> (APolygon a, APolygon a)
|
||||
normalizePolygons src dst =
|
||||
(pAddPoints (max 0 $ dstN-srcN) src
|
||||
,pAddPoints (max 0 $ srcN-dstN) dst)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ module Reanimate.Morph.Linear
|
|||
( linear, rawLinear
|
||||
, linearCorrespondence
|
||||
, closestLinearCorrespondence
|
||||
, closestLinearCorrespondenceA
|
||||
, linearTrajectory
|
||||
) where
|
||||
|
||||
|
|
@ -32,7 +33,10 @@ linearCorrespondence :: PointCorrespondence
|
|||
linearCorrespondence = normalizePolygons
|
||||
|
||||
closestLinearCorrespondence :: PointCorrespondence
|
||||
closestLinearCorrespondence src' dst' =
|
||||
closestLinearCorrespondence = closestLinearCorrespondenceA
|
||||
|
||||
closestLinearCorrespondenceA :: (Real a, Fractional a) => APolygon a -> APolygon a -> (APolygon a, APolygon a)
|
||||
closestLinearCorrespondenceA src' dst' =
|
||||
(src, worker dst (score dst) options)
|
||||
where
|
||||
(src, dst) = normalizePolygons src' dst'
|
||||
|
|
@ -44,7 +48,8 @@ closestLinearCorrespondence src' dst' =
|
|||
else worker bestP bestPScore xs
|
||||
options = pCycles dst
|
||||
score p = sum
|
||||
[ approxDist (pAccess src n) (pAccess p n)
|
||||
[ -- approxDist (pAccess src n) (pAccess p n)
|
||||
distSquared (pAccess src n) (pAccess p n)
|
||||
| n <- [0 .. pSize src-1] ]
|
||||
|
||||
linearTrajectory :: Trajectory
|
||||
|
|
|
|||
|
|
@ -66,20 +66,27 @@ on the shear.
|
|||
-}
|
||||
module Reanimate.Morph.Rigid where
|
||||
|
||||
import Control.Lens
|
||||
import Data.Foldable (toList)
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
import Data.Vector (Vector)
|
||||
import qualified Data.Vector as V
|
||||
import Linear.Quaternion
|
||||
import Linear.Vector
|
||||
import Linear.V2
|
||||
import Linear.V3
|
||||
import Linear.Vector
|
||||
import qualified Numeric.LinearAlgebra as Matrix
|
||||
import Numeric.LinearAlgebra.HMatrix (GMatrix, Matrix,
|
||||
toLists, (!), (><))
|
||||
import Numeric.LinearAlgebra.HMatrix (GMatrix, Matrix, toLists, (!),
|
||||
(><))
|
||||
import Reanimate.Animation
|
||||
import Reanimate.Math.Compatible (compatiblyTriangulateP)
|
||||
import qualified Reanimate.Math.DCEL as DCEL
|
||||
import Reanimate.Math.Polygon (mkPolygon, pCopy,
|
||||
polygonPoints)
|
||||
import Reanimate.Morph.Common
|
||||
import Reanimate.Svg
|
||||
|
||||
|
||||
type P = V2 Double
|
||||
type Trig = (P,P,P)
|
||||
type RelTrig = (Int,Int,Int)
|
||||
|
|
@ -252,7 +259,7 @@ interpolate Prep{..} t = V.fromList $
|
|||
False
|
||||
1e-9
|
||||
1e-9
|
||||
1000
|
||||
10000
|
||||
prepUToB
|
||||
b
|
||||
(Matrix.fromList $ concat [ [x,y] | V2 x y <- V.toList target ])
|
||||
|
|
@ -279,3 +286,61 @@ interpolate Prep{..} t = V.fromList $
|
|||
, let a = computeA_RSt r s (1-t)
|
||||
, j <- [0..3]
|
||||
]
|
||||
|
||||
toRigidMesh :: DCEL.Mesh (V2 Double) -> DCEL.Mesh (V2 Double) -> Mesh
|
||||
toRigidMesh meshA meshB = Mesh
|
||||
{ meshPointsA = pointsA
|
||||
, meshPointsB = pointsB
|
||||
, meshOutline = outline
|
||||
, meshTriangles = trigs }
|
||||
where
|
||||
pointsA = V.fromList $ map DCEL._vertexPosition $ M.elems (meshA^.DCEL.meshVertices)
|
||||
pointsB = V.fromList $ map DCEL._vertexPosition $ M.elems (meshB^.DCEL.meshVertices)
|
||||
outline = V.fromList
|
||||
[ fromJust (V.elemIndex v pointsA)
|
||||
| eid <- DCEL.faceEdges (meshA^.DCEL.meshOuterFace) meshA
|
||||
, let edge = DCEL.meshGetEdge eid meshA
|
||||
v = DCEL._vertexPosition (DCEL.meshGetVertex (edge^.DCEL.edgeVertex) meshA)
|
||||
]
|
||||
trigs = V.fromList
|
||||
[ (aIdx, bIdx, cIdx)
|
||||
| fid <- M.keys (meshA^.DCEL.meshFaces)
|
||||
, fid /= (meshA^.DCEL.meshOuterFace)
|
||||
, let edges = map (`DCEL.meshGetEdge` meshA) (DCEL.faceEdges fid meshA)
|
||||
vs = map (`DCEL.meshGetVertex` meshA) $ map DCEL._edgeVertex edges
|
||||
ps = map DCEL._vertexPosition vs
|
||||
[aIdx, bIdx, cIdx] = map (fromJust . (`V.elemIndex` pointsA)) ps
|
||||
-- p = mkPolygon (V.fromList $ map (fmap realToFrac) ps)
|
||||
-- , pIsSimple p || error "invalid polygon"
|
||||
]
|
||||
|
||||
|
||||
{-# INLINE rigidMorph #-}
|
||||
rigidMorph :: Trajectory
|
||||
rigidMorph (p1', p2') = \t ->
|
||||
let points = V.map (fmap realToFrac) $ interpolate optPrep t
|
||||
in mkPolygon (V.map (\i -> points V.! i) (meshOutline optMesh))
|
||||
where
|
||||
p1 = pCopy p1'
|
||||
p2 = pCopy p2'
|
||||
(p1s,p2s) = unzip (compatiblyTriangulateP p1 p2)
|
||||
m2 = DCEL.buildMesh $ DCEL.polygonsMesh
|
||||
(map (fmap realToFrac) $ V.toList $ polygonPoints p2)
|
||||
(map (map (fmap realToFrac) . V.toList . polygonPoints) p2s)
|
||||
|
||||
m1 = DCEL.buildMesh $ DCEL.polygonsMesh
|
||||
(map (fmap realToFrac) $ V.toList $ polygonPoints p1)
|
||||
(map (map (fmap realToFrac) . V.toList . polygonPoints) p1s)
|
||||
|
||||
optPrep = prepare optMesh
|
||||
optMesh = toRigidMesh m1final m2final
|
||||
pipeline1 = last . take 20 . iterate
|
||||
(uncurry DCEL.delaunayFlip .
|
||||
uncurry DCEL.splitInternalEdges .
|
||||
(\(a,b) -> (DCEL.meshSmoothPosition a, DCEL.meshSmoothPosition b)))
|
||||
(m1final, m2final) = last $ take 20 $ iterate
|
||||
(pipeline1 .
|
||||
uncurry DCEL.splitLongestEdge .
|
||||
pipeline1
|
||||
) (m1,m2)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,19 +6,12 @@ packages:
|
|||
- .
|
||||
|
||||
extra-deps:
|
||||
- reanimate-svg-0.10.0.0
|
||||
- chiphunk-0.1.2.1
|
||||
- cubicbezier-0.6.0.6@sha256:2191ff47144d9a13a2784651a33d340cd31be1926a6c188925143103eb3c8db3
|
||||
- fast-math-1.0.2@sha256:91181eb836e54413cc5a841e797c42b2264954e893ea530b6fc4da0dccf6a8b7
|
||||
- matrices-0.5.0@sha256:b2761813f6a61c84224559619cc60a16a858ac671c8436bbac8ec89e85473058
|
||||
- earcut-0.1.0.4@sha256:d5118b3eecf24d130263d81fb30f1ff56b1db43036582bfd1d8cc9ba3adae8be,1010
|
||||
- geojson-4.0.1@sha256:276de5cb2aa3e07179a8d42c184b1a4e52d2c8d23cf2eb989ecfa6adbe726227
|
||||
- hmatrix-0.20.0.0@sha256:d79a9218e314f1a2344457c3851bd1d2536518ecb5f1a2fcd81daa45e46cd025,4870
|
||||
- JuicyPixels-3.3.5@sha256:5c67dc066d67f5045221a8202f701f4c77eab8220f44414c00fadcdc3e4f872b,3911
|
||||
- websockets-0.12.7.1@sha256:4363a5ca794af094971266a1397807ac55d5b3124645060fb78c64ac4c88a1f1,7863
|
||||
- clock-0.8@sha256:b4ae207e2d3761450060a0d0feb873269233898039c76fceef9cc1a544067767,4113
|
||||
- JuicyPixels-3.3.5@sha256:5c67dc066d67f5045221a8202f701f4c77eab8220f44414c00fadcdc3e4f872b,3911
|
||||
- th-printf-0.7@sha256:c6b2c08e62a32a327b29b1a3739939f0c85b4079ff2ae6d79fac639f25ee48b1,2297
|
||||
- earcut-0.1.0.4@sha256:d5118b3eecf24d130263d81fb30f1ff56b1db43036582bfd1d8cc9ba3adae8be,1010
|
||||
- reanimate-svg-0.10.0.0
|
||||
- tasty-rerun-1.1.17@sha256:d4a3ccb0f63f499f36edc71b33c0f91c850eddb22dd92b928aa33b8459f3734a,1373
|
||||
- tasty-1.3.1@sha256:01e35c97f7ee5ccbc28f21debea02a38cd010d53b4c3087f5677c5d06617a507,2520
|
||||
- optparse-applicative-0.15.1.0@sha256:29ff6146aabf54d46c4c8788e8d1eadaea27c94f6d360c690c5f6c93dac4b07e,4810
|
||||
- ansi-terminal-0.10.3@sha256:e2fbcef5f980dc234c7ad8e2fa433b0e8109132c9e643bc40ea5608cd5697797,3226
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ p2 = pCopy p2'
|
|||
-- normalizePolygons
|
||||
closestLinearCorrespondence
|
||||
-- leastWork defaultStretchCosts defaultBendCosts
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.01 $ scale 4 $ latex "S")
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.01 $ scale 4 $ latex "C")
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.5 $ scale 6 $ latex "X")
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.5 $ scale 6 $ latex "S")
|
||||
|
||||
(p1s,p2s) = unzip (compatiblyTriangulateP p1 p2)
|
||||
|
||||
|
|
@ -65,6 +65,10 @@ m1 = buildMesh $ polygonsMesh
|
|||
(map (fmap realToFrac) $ V.toList $ polygonPoints p1)
|
||||
(map (map (fmap realToFrac) . V.toList . polygonPoints) p1s)
|
||||
|
||||
doSmooth (a,b) = (meshSmoothPosition a, meshSmoothPosition b)
|
||||
step0 = (m1,m2)
|
||||
step1 = uncurry delaunayFlip step0
|
||||
step2 = doSmooth step1
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate $ sceneAnimation $ do
|
||||
|
|
@ -76,7 +80,7 @@ main = reanimate $ sceneAnimation $ do
|
|||
-- DCEL.renderMesh m
|
||||
s <- newVar 1
|
||||
mVar <- newVar (m1, m2)
|
||||
let V2 centerX centerY = V2 0 0 -- realToFrac <$> pAccess p2 33
|
||||
let V2 centerX centerY = V2 0 0 -- V2 (-0.2) (-2)-- realToFrac <$> pAccess p2 33
|
||||
adjustZ 2 $ newSprite_ $ do
|
||||
~(m1, m2) <- unVar mVar
|
||||
pure $ mkGroup
|
||||
|
|
@ -91,27 +95,33 @@ main = reanimate $ sceneAnimation $ do
|
|||
[ mkGroup []
|
||||
, DCEL.renderMeshColored m1
|
||||
, DCEL.renderMesh (0.05/sc) m1
|
||||
-- , renderMeshEdges m
|
||||
-- , DCEL.renderMeshSimple (0.10/sc) m
|
||||
-- , renderMeshEdges m1
|
||||
, withFillColor "grey" $ DCEL.renderMeshSimple (0.15/sc) m1
|
||||
]
|
||||
,translate 2 0 $ withStrokeWidth (defaultStrokeWidth*1) $ lowerTransformations $ scale sc $
|
||||
translate (negate centerX) (negate centerY) $ mkGroup
|
||||
[ mkGroup []
|
||||
, DCEL.renderMeshColored m2
|
||||
, DCEL.renderMesh (0.05/sc) m2
|
||||
-- , renderMeshEdges m
|
||||
-- , DCEL.renderMeshSimple (0.10/sc) m
|
||||
-- , renderMeshEdges m2
|
||||
-- , DCEL.renderMeshSimple (0.10/sc) m2
|
||||
, withFillColor "grey" $ DCEL.renderMeshSimple (0.15/sc) m2
|
||||
]]
|
||||
writeVar s 2
|
||||
wait (1/60)
|
||||
let pipeline1 = last . take 20 . iterate
|
||||
(uncurry delaunayFlip .
|
||||
uncurry splitInternalEdges .
|
||||
(\(a,b) -> (meshSmoothPosition a, meshSmoothPosition b)))
|
||||
stages = take 30 $ iterate
|
||||
(pipeline1 .
|
||||
uncurry splitLongestEdge .
|
||||
pipeline1
|
||||
-- wait (1/60)
|
||||
let pipeline1 = last . take 20 . iterate (
|
||||
uncurry delaunayFlip .
|
||||
-- uncurry splitInternalEdges .
|
||||
doSmooth .
|
||||
id
|
||||
)
|
||||
stages = take 30 $ iterate (
|
||||
pipeline1 .
|
||||
-- uncurry splitInternalEdges .
|
||||
-- pipeline1 .
|
||||
-- uncurry splitLongestEdge .
|
||||
--pipeline1
|
||||
id
|
||||
) (m1,m2)
|
||||
let pipeline = do
|
||||
modifyVar mVar (uncurry delaunayFlip)
|
||||
|
|
|
|||
286
videos/morph/do_morph.hs
Normal file
286
videos/morph/do_morph.hs
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
module Main where
|
||||
|
||||
import Codec.Picture.Types
|
||||
import Control.Lens
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple
|
||||
import qualified Data.Vector as V
|
||||
import Linear.V2
|
||||
import Linear.Vector
|
||||
import qualified Numeric.LinearAlgebra as Matrix
|
||||
import Numeric.LinearAlgebra.HMatrix (Matrix, linearSolve, toLists,
|
||||
(><))
|
||||
import Reanimate
|
||||
import Reanimate.Math.Common (barycentricCoords, isBetween,
|
||||
rayIntersect)
|
||||
|
||||
type Points = V.Vector (V2 Double)
|
||||
type Edges = [(Int, Int, Int)]
|
||||
data Mesh = Mesh { meshPoints :: Points, meshEdges :: Edges }
|
||||
data MeshPair = MeshPair Points Points Edges
|
||||
-- The points in a RelMesh are:
|
||||
-- relMeshStatic ++ x where Ax = B
|
||||
data RelMesh = RelMesh
|
||||
{ relMeshStatic :: Points
|
||||
, relMeshEdges :: Edges
|
||||
, relMeshA :: Matrix Double
|
||||
, relMeshB :: Matrix Double
|
||||
}
|
||||
data RelMeshPair = RelMeshPair Points Edges (Matrix Double) (Matrix Double) (Matrix Double) (Matrix Double)
|
||||
-- Linear interpolation on RelMesh gives smooth morph.
|
||||
-- solveMesh :: RelMesh -> Mesh
|
||||
-- mkRelative :: Mesh -> RelMesh
|
||||
-- mkRelativePair :: MeshPair -> RelMeshPair
|
||||
-- triangulate :: Polygon -> Polygon -> MeshPair ?
|
||||
-- embed :: MeshPair -> MeshPair
|
||||
-- compatible :: Mesh -> Mesh -> Maybe MeshPair
|
||||
-- linearInterpolate :: MeshPair -> Double -> Mesh
|
||||
-- convexInterpolate :: RelMeshPair -> Double -> RelMesh
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate morphAnimation
|
||||
|
||||
morphAnimation :: Animation
|
||||
morphAnimation = playThenReverseA $ pauseAround 1 1 $ addStatic (mkBackground "black") $
|
||||
signalA (curveS 2) $ mkAnimation 5 $ \t -> lowerTransformations $ scale 3 $ pathify $ center $
|
||||
mkGroup
|
||||
[ translate (-1) 0 $ drawTrig (linearInterpolate meshPair t)
|
||||
, translate 1 0 $ drawTrig $ solveMesh $ convexInterpolate relPair t
|
||||
]
|
||||
where
|
||||
relPair = mkRelativePair meshPair
|
||||
meshPair = fromJust $ compatible example1 example2
|
||||
|
||||
mkLineP :: P -> P -> SVG
|
||||
mkLineP (V2 x1 y1) (V2 x2 y2) = mkLine (x1,y1) (x2,y2)
|
||||
|
||||
-- FIXME: Check that the triangles are all anticlockwise.
|
||||
-- FIXME: Check that the edges connect all the points.
|
||||
-- FIXME: Check that the edgse leave no gaps.
|
||||
compatible :: Mesh -> Mesh -> Maybe MeshPair
|
||||
compatible a b =
|
||||
if meshEdges a == meshEdges b && V.length (meshPoints a) == V.length (meshPoints b)
|
||||
then Just $ MeshPair (meshPoints a) (meshPoints b) (meshEdges a)
|
||||
else Nothing
|
||||
|
||||
linearInterpolate :: MeshPair -> Double -> Mesh
|
||||
linearInterpolate (MeshPair aP bP edges) t = Mesh
|
||||
{ meshPoints = V.zipWith (lerp (1-t)) aP bP
|
||||
, meshEdges = edges }
|
||||
|
||||
example1 :: Mesh
|
||||
example1 = Mesh points edges
|
||||
where
|
||||
points = V.fromList
|
||||
[ V2 1 0
|
||||
, V2 (-1/2) (sqrt 3 / 2)
|
||||
, V2 (-1/2) (-sqrt 3 / 2)
|
||||
, 3 * points V.! 0 ^/ 4
|
||||
, 3 * points V.! 1 ^/ 4
|
||||
, 3 * points V.! 2 ^/ 4
|
||||
, points V.! 0 ^/ 2
|
||||
, points V.! 1 ^/ 2
|
||||
, points V.! 2 ^/ 2
|
||||
]
|
||||
edges =
|
||||
[ (1,5,4), (1,2,5), (2,6,5), (2,3,6), (3,4,6), (3,1,4)
|
||||
, (4,8,7), (4,5,8), (5,9,8), (5,6,9), (6,7,9), (6,4,7), (7,8,9)]
|
||||
|
||||
example2 :: Mesh
|
||||
example2 = Mesh points edges
|
||||
where
|
||||
points = V.fromList
|
||||
[ V2 1 0
|
||||
, V2 (-1/2) (sqrt 3 / 2)
|
||||
, V2 (-1/2) (-sqrt 3 / 2)
|
||||
, 3 * points V.! 2 ^/ 4
|
||||
, 3 * points V.! 0 ^/ 4
|
||||
, 3 * points V.! 1 ^/ 4
|
||||
, points V.! 1 ^/ 2
|
||||
, points V.! 2 ^/ 2
|
||||
, points V.! 0 ^/ 2
|
||||
]
|
||||
edges = meshEdges example1
|
||||
|
||||
drawTrig (Mesh points gs) = withStrokeColor "grey" $ withFillColor "white" $
|
||||
withStrokeWidth (defaultStrokeWidth/2) $ mkGroup
|
||||
[ mkGroup
|
||||
[ mkGroup
|
||||
[ mkLine (ax, ay) (bx, by)
|
||||
, mkLine (bx, by) (cx, cy)
|
||||
, mkLine (cx, cy) (ax, ay)
|
||||
]
|
||||
| (a, b, c) <- gs
|
||||
, let V2 ax ay = points V.! (a-1)
|
||||
V2 bx by = points V.! (b-1)
|
||||
V2 cx cy = points V.! (c-1)
|
||||
]
|
||||
, mkGroup $ concat
|
||||
[ [ colored v $ translate ax ay $ mkCircle circleRadius
|
||||
, withStrokeWidth 0 $
|
||||
withStrokeColor "white" $ withFillColor "black" $ mkGroup
|
||||
[ translate ax ay $ ppNum v ]
|
||||
]
|
||||
| v <- nub $ concat [ [a,b,c] | (a,b,c) <- gs]
|
||||
, let V2 ax ay = points V.! (v-1)
|
||||
]]
|
||||
where
|
||||
colored n =
|
||||
let c = promotePixel $ turbo (fromIntegral n / fromIntegral (length gs-1))
|
||||
in withStrokeColorPixel c . withFillColorPixel c
|
||||
ppNum n = cachedNumbers !! n
|
||||
--scaleToHeight (circR*1.5) $ center $ latex $ T.pack $ "\\texttt{" ++ show n ++ "}"
|
||||
|
||||
cachedNumbers =
|
||||
[ scaleToHeight (circleRadius*1.5) $ center $ latex $ T.pack $ "\\texttt{" ++ show n ++ "}"
|
||||
| n <- [0 .. ] ]
|
||||
|
||||
circleRadius :: Double
|
||||
circleRadius = 0.05
|
||||
|
||||
-- Anticlockwise. No duplicate vertices. length >= 3
|
||||
type Polygon = [V2 Double]
|
||||
type P = V2 Double
|
||||
|
||||
-- T = (U, G)
|
||||
-- G = [Polygon]
|
||||
-- U = nub $ concat G
|
||||
|
||||
findStarNeighbours :: Eq a => [(a,a,a)] -> a -> [(a, a)]
|
||||
findStarNeighbours allTrig self =
|
||||
[ (b,c)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == a
|
||||
] ++
|
||||
[ (c,a)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == b
|
||||
] ++
|
||||
[ (a,b)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == c
|
||||
]
|
||||
|
||||
isInterior :: Eq a => [(a, a)] -> Bool
|
||||
isInterior = isJust . getExteriorPoly
|
||||
|
||||
getExteriorPoly :: Eq a => [(a, a)] -> Maybe [a]
|
||||
getExteriorPoly [] = Nothing
|
||||
getExteriorPoly ((a,b):rest) = worker [a] a b rest
|
||||
where
|
||||
worker acc start this [] = do
|
||||
guard (start == this)
|
||||
return (reverse acc)
|
||||
worker acc start this xs =
|
||||
case lookup this xs of
|
||||
Just next -> worker (this:acc) start next (delete (this,next) xs)
|
||||
Nothing ->
|
||||
case lookup this (map swap xs) of
|
||||
Just next -> worker (this:acc) start next (delete (next, this) xs)
|
||||
Nothing -> Nothing
|
||||
|
||||
convexInterpolate :: RelMeshPair -> Double -> RelMesh
|
||||
convexInterpolate (RelMeshPair static edges leftM leftB rightM rightB) t =
|
||||
RelMesh
|
||||
{ relMeshStatic = static
|
||||
, relMeshEdges = edges
|
||||
, relMeshA = Matrix.scale (1-t) leftM +
|
||||
Matrix.scale t rightM
|
||||
, relMeshB = Matrix.scale (1-t) leftB +
|
||||
Matrix.scale t rightB
|
||||
}
|
||||
|
||||
solveMesh :: RelMesh -> Mesh
|
||||
solveMesh (RelMesh static edges m b) =
|
||||
case linearSolve m b of
|
||||
Nothing -> error "Failed to solve mesh"
|
||||
Just ret ->
|
||||
Mesh (static <> V.fromList (worker (toLists ret))) edges
|
||||
where
|
||||
worker [] = []
|
||||
worker ([x]:[y]:rest) = V2 x y : worker rest
|
||||
worker _ = error "invalid result"
|
||||
|
||||
mkRelative :: Mesh -> RelMesh
|
||||
mkRelative (Mesh points edges) = RelMesh (V.fromList exteriorPoints) edges mM bM
|
||||
where
|
||||
mM = (s><s) (concat m)
|
||||
bM = (s><1) b
|
||||
(s,exterior, (m, b)) = toParameters points edges
|
||||
exteriorPoints =
|
||||
[ points V.! (i-1)
|
||||
| i <- exterior
|
||||
]
|
||||
|
||||
mkRelativePair :: MeshPair -> RelMeshPair
|
||||
mkRelativePair (MeshPair p1 p2 edges) =
|
||||
let RelMesh static _ leftM leftB = mkRelative (Mesh p1 edges)
|
||||
RelMesh _ _ rightM rightB = mkRelative (Mesh p2 edges)
|
||||
in RelMeshPair static edges leftM leftB rightM rightB
|
||||
|
||||
toParameters points groups = (length interior*2,exterior,unzip $ concat
|
||||
[ let lst = [(if i == j then -1 else t)
|
||||
| j <- interior
|
||||
, let t = fromMaybe 0 $ lookup (i,j) lam_ij_cache
|
||||
]
|
||||
pos = negate $ sum
|
||||
[ pj ^* t
|
||||
| j <- exterior
|
||||
, let t = fromMaybe 0 $ lookup (i,j) lam_ij_cache
|
||||
pj = points V.! (j-1)
|
||||
]
|
||||
in [ (dupX lst, pos ^. _x)
|
||||
, (dupY lst, pos ^. _y)]
|
||||
| i <- interior ])
|
||||
where
|
||||
lam_ij_cache = lam_ij points groups
|
||||
dupX [] = []
|
||||
dupX (x:xs) = x:0:dupX xs
|
||||
dupY [] = []
|
||||
dupY (x:xs) = 0:x:dupY xs
|
||||
(interior, exterior) =
|
||||
partition (isInterior . findStarNeighbours groups) [1 .. length points]
|
||||
|
||||
lam_ij points groups =
|
||||
[ ((i, j), t)
|
||||
| i <- [1..V.length points]
|
||||
, (j, t) <- lam_j points groups i
|
||||
]
|
||||
lam_j points groups p =
|
||||
[ (nP, sum [ t | (j,k,t) <- mu, j == nP ] / fromIntegral (length nPoints))
|
||||
| let n = findStarNeighbours groups p
|
||||
nPoints = fromMaybe [] $ getExteriorPoly n
|
||||
mu = calcMu points groups p
|
||||
, nP <- nPoints ]
|
||||
calcMu points groups p = concat
|
||||
[ [ (nP, nP, t1)
|
||||
, (a, nP, t2)
|
||||
, (b, nP, t3) ]
|
||||
-- (nP, a, b)
|
||||
| {-p <- [1..length points]-}
|
||||
let selfVert = points V.! (p-1)
|
||||
n = findStarNeighbours groups p
|
||||
nPoints :: [Int]
|
||||
nPoints = fromMaybe [] $ getExteriorPoly n
|
||||
, (i, nP) <- zip [1 .. ] nPoints
|
||||
, let vert = points V.! (nP-1)
|
||||
, let line = (vert, selfVert)
|
||||
, let (a,b,aP,bP) = head $
|
||||
[ (a,b,aP,bP)
|
||||
| (a,b) <- n
|
||||
, let aP = points V.! (a-1)
|
||||
bP = points V.! (b-1)
|
||||
segment = (points V.! (a-1), points V.! (b-1))
|
||||
, case rayIntersect line segment of
|
||||
Nothing -> False
|
||||
Just u -> isBetween u segment
|
||||
, a /= nP
|
||||
, b /= nP ]
|
||||
-- , b == (nPoints ++ nPoints) !! i
|
||||
, let (t1,t2,t3) = barycentricCoords vert aP bP selfVert
|
||||
]
|
||||
127
videos/morph/least-difference.hs
Normal file
127
videos/morph/least-difference.hs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main where
|
||||
|
||||
import Codec.Picture.Types
|
||||
import Control.Lens ()
|
||||
import Control.Monad
|
||||
import Data.Function
|
||||
import Data.List
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import Data.Maybe
|
||||
import Data.Ratio
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple
|
||||
import qualified Data.Vector as V
|
||||
import Debug.Trace
|
||||
import Linear.Matrix hiding (trace)
|
||||
import Linear.Metric
|
||||
import Linear.V2
|
||||
import Linear.V3
|
||||
import Linear.Vector
|
||||
import Numeric.LinearAlgebra hiding (polar, scale, (<>))
|
||||
import qualified Numeric.LinearAlgebra as Matrix
|
||||
import Numeric.LinearAlgebra.HMatrix hiding (polar, scale, (<>))
|
||||
import Reanimate
|
||||
import Reanimate.Animation
|
||||
import Reanimate.Math.Balloon
|
||||
import Reanimate.Math.Common
|
||||
import Reanimate.Math.Triangulate
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Math.EarClip
|
||||
import Reanimate.Math.SSSP
|
||||
import Reanimate.Math.Render
|
||||
import Reanimate.Math.Visibility
|
||||
import Reanimate.Math.Compatible
|
||||
import Reanimate.Morph.Common
|
||||
import Reanimate.Morph.Linear
|
||||
import Reanimate.Morph.LeastDifference
|
||||
import Reanimate.PolyShape (svgToPolygons)
|
||||
import Reanimate.Debug
|
||||
import qualified Reanimate.Math.Compatible as Compat
|
||||
|
||||
extraPoints = 0
|
||||
|
||||
-- p1 = pScale 2 $ pAtCenter $ pAddPoints extraPoints (pSetOffset shape13 0)
|
||||
-- p1 = pSetOffset (addPoints 2 shape13) 0
|
||||
-- p2 = pScale 0.5 $ centerPolygon shape20
|
||||
-- p1 = centerPolygon shape2
|
||||
-- p2 = pScale 2 $ pAtCenter $ pAddPoints extraPoints (pSetOffset shape14 0 )
|
||||
|
||||
-- p1 = pAddPoints extraPoints $ (pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "S")
|
||||
-- p2 = pAddPoints extraPoints $ (pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "C")
|
||||
p1 = pAddPoints extraPoints $ (pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "X")
|
||||
p2 = pAddPoints extraPoints $ (pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "I")
|
||||
|
||||
p1_ = castPolygon p1
|
||||
p2_ = castPolygon p2
|
||||
polys = triangulate_ p1_ p2_
|
||||
p1_circ = circumference (map fst polys') p1_
|
||||
p2_circ = circumference (map snd polys') p2_
|
||||
|
||||
polys' = alignPolygons polys p1_ p2_
|
||||
polys'' = compatTriagPairs polys'
|
||||
|
||||
-- (p1_circ, p2_circ) = closestLinearCorrespondence p1_circ' p2_circ'
|
||||
|
||||
-- (p1s, p2s) = unzip $ triangulate p1 p2
|
||||
|
||||
main :: IO ()
|
||||
-- main = reanimate $ playTraces $ seq (last $ triangulate [] p1 p2) ()
|
||||
main = reanimate $ sceneAnimation $ do
|
||||
bg <- newSpriteSVG $ mkBackground "black"
|
||||
spriteZ bg (-1)
|
||||
|
||||
-- let leastDiffTrig = triangulate p1 p2
|
||||
let lst = polys'
|
||||
-- let lst = take 10 $ uncurry Compat.compatiblyTriangulateP (polys'!!3) -- polys''
|
||||
-- let lst = polys''
|
||||
forM_ (zip [0..] lst {-([polys''!!2]++p_snd)-}) $ \(n,(l, r)) -> do
|
||||
let c = promotePixel $ turbo (n/fromIntegral (length lst-1))
|
||||
newSpriteSVG_ $
|
||||
translate (-2) 0 $ withFillColorPixel c $ mkGroup
|
||||
[ polygonShape (castPolygon l)
|
||||
-- , polygonNumDots (castPolygon l)
|
||||
]
|
||||
newSpriteSVG_ $
|
||||
translate (2) 0 $ withFillColorPixel c $ mkGroup
|
||||
[ polygonShape (castPolygon r)
|
||||
-- , polygonNumDots (castPolygon r)
|
||||
]
|
||||
nums <- newSpriteSVG $ mkGroup
|
||||
[ translate (-2) 0 $ polygonNumDots (castPolygon l)
|
||||
, translate (2) 0 $ polygonNumDots (castPolygon r)
|
||||
]
|
||||
wait (1/60)
|
||||
destroySprite nums
|
||||
|
||||
-- fork $ play $ staticFrame (1/60) $ mkGroup
|
||||
-- [ translate (2) 0 $ mkGroup
|
||||
-- [ withFillColor "grey" $ polygonShape p2_circ
|
||||
-- , polygonNumDots p2_circ
|
||||
-- ]
|
||||
-- , translate (6) 0 $ mkGroup
|
||||
-- [ withFillColor "grey" $ polygonShape p2
|
||||
-- , polygonNumDots p2
|
||||
-- ]
|
||||
-- ]
|
||||
-- play $ staticFrame (1/60) $ mkGroup
|
||||
-- [ translate (-2) 0 $ mkGroup
|
||||
-- [ withFillColor "grey" $ polygonShape p1_circ
|
||||
-- , polygonNumDots p1_circ
|
||||
-- ]
|
||||
-- , translate (-6) 0 $ mkGroup
|
||||
-- [ withFillColor "grey" $ polygonShape p1
|
||||
-- , polygonNumDots p1
|
||||
-- ]
|
||||
-- ]
|
||||
|
||||
|
||||
return ()
|
||||
|
||||
|
||||
showP p = mkGroup
|
||||
[ withFillColor "grey" $ polygonShape p
|
||||
, polygonNumDots p ]
|
||||
|
|
@ -1,286 +1,3 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
module Main where
|
||||
|
||||
import Codec.Picture.Types
|
||||
import Control.Lens
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple
|
||||
import qualified Data.Vector as V
|
||||
import Linear.V2
|
||||
import Linear.Vector
|
||||
import qualified Numeric.LinearAlgebra as Matrix
|
||||
import Numeric.LinearAlgebra.HMatrix (Matrix, linearSolve, toLists,
|
||||
(><))
|
||||
import Reanimate
|
||||
import Reanimate.Math.Common (barycentricCoords, isBetween,
|
||||
rayIntersect)
|
||||
|
||||
type Points = V.Vector (V2 Double)
|
||||
type Edges = [(Int, Int, Int)]
|
||||
data Mesh = Mesh { meshPoints :: Points, meshEdges :: Edges }
|
||||
data MeshPair = MeshPair Points Points Edges
|
||||
-- The points in a RelMesh are:
|
||||
-- relMeshStatic ++ x where Ax = B
|
||||
data RelMesh = RelMesh
|
||||
{ relMeshStatic :: Points
|
||||
, relMeshEdges :: Edges
|
||||
, relMeshA :: Matrix Double
|
||||
, relMeshB :: Matrix Double
|
||||
}
|
||||
data RelMeshPair = RelMeshPair Points Edges (Matrix Double) (Matrix Double) (Matrix Double) (Matrix Double)
|
||||
-- Linear interpolation on RelMesh gives smooth morph.
|
||||
-- solveMesh :: RelMesh -> Mesh
|
||||
-- mkRelative :: Mesh -> RelMesh
|
||||
-- mkRelativePair :: MeshPair -> RelMeshPair
|
||||
-- triangulate :: Polygon -> Polygon -> MeshPair ?
|
||||
-- embed :: MeshPair -> MeshPair
|
||||
-- compatible :: Mesh -> Mesh -> Maybe MeshPair
|
||||
-- linearInterpolate :: MeshPair -> Double -> Mesh
|
||||
-- convexInterpolate :: RelMeshPair -> Double -> RelMesh
|
||||
|
||||
main :: IO ()
|
||||
main = reanimate morphAnimation
|
||||
|
||||
morphAnimation :: Animation
|
||||
morphAnimation = playThenReverseA $ pauseAround 1 1 $ addStatic (mkBackground "black") $
|
||||
signalA (curveS 2) $ mkAnimation 5 $ \t -> lowerTransformations $ scale 3 $ pathify $ center $
|
||||
mkGroup
|
||||
[ translate (-1) 0 $ drawTrig (linearInterpolate meshPair t)
|
||||
, translate 1 0 $ drawTrig $ solveMesh $ convexInterpolate relPair t
|
||||
]
|
||||
where
|
||||
relPair = mkRelativePair meshPair
|
||||
meshPair = fromJust $ compatible example1 example2
|
||||
|
||||
mkLineP :: P -> P -> SVG
|
||||
mkLineP (V2 x1 y1) (V2 x2 y2) = mkLine (x1,y1) (x2,y2)
|
||||
|
||||
-- FIXME: Check that the triangles are all anticlockwise.
|
||||
-- FIXME: Check that the edges connect all the points.
|
||||
-- FIXME: Check that the edgse leave no gaps.
|
||||
compatible :: Mesh -> Mesh -> Maybe MeshPair
|
||||
compatible a b =
|
||||
if meshEdges a == meshEdges b && V.length (meshPoints a) == V.length (meshPoints b)
|
||||
then Just $ MeshPair (meshPoints a) (meshPoints b) (meshEdges a)
|
||||
else Nothing
|
||||
|
||||
linearInterpolate :: MeshPair -> Double -> Mesh
|
||||
linearInterpolate (MeshPair aP bP edges) t = Mesh
|
||||
{ meshPoints = V.zipWith (lerp (1-t)) aP bP
|
||||
, meshEdges = edges }
|
||||
|
||||
example1 :: Mesh
|
||||
example1 = Mesh points edges
|
||||
where
|
||||
points = V.fromList
|
||||
[ V2 1 0
|
||||
, V2 (-1/2) (sqrt 3 / 2)
|
||||
, V2 (-1/2) (-sqrt 3 / 2)
|
||||
, 3 * points V.! 0 ^/ 4
|
||||
, 3 * points V.! 1 ^/ 4
|
||||
, 3 * points V.! 2 ^/ 4
|
||||
, points V.! 0 ^/ 2
|
||||
, points V.! 1 ^/ 2
|
||||
, points V.! 2 ^/ 2
|
||||
]
|
||||
edges =
|
||||
[ (1,5,4), (1,2,5), (2,6,5), (2,3,6), (3,4,6), (3,1,4)
|
||||
, (4,8,7), (4,5,8), (5,9,8), (5,6,9), (6,7,9), (6,4,7), (7,8,9)]
|
||||
|
||||
example2 :: Mesh
|
||||
example2 = Mesh points edges
|
||||
where
|
||||
points = V.fromList
|
||||
[ V2 1 0
|
||||
, V2 (-1/2) (sqrt 3 / 2)
|
||||
, V2 (-1/2) (-sqrt 3 / 2)
|
||||
, 3 * points V.! 2 ^/ 4
|
||||
, 3 * points V.! 0 ^/ 4
|
||||
, 3 * points V.! 1 ^/ 4
|
||||
, points V.! 1 ^/ 2
|
||||
, points V.! 2 ^/ 2
|
||||
, points V.! 0 ^/ 2
|
||||
]
|
||||
edges = meshEdges example1
|
||||
|
||||
drawTrig (Mesh points gs) = withStrokeColor "grey" $ withFillColor "white" $
|
||||
withStrokeWidth (defaultStrokeWidth/2) $ mkGroup
|
||||
[ mkGroup
|
||||
[ mkGroup
|
||||
[ mkLine (ax, ay) (bx, by)
|
||||
, mkLine (bx, by) (cx, cy)
|
||||
, mkLine (cx, cy) (ax, ay)
|
||||
]
|
||||
| (a, b, c) <- gs
|
||||
, let V2 ax ay = points V.! (a-1)
|
||||
V2 bx by = points V.! (b-1)
|
||||
V2 cx cy = points V.! (c-1)
|
||||
]
|
||||
, mkGroup $ concat
|
||||
[ [ colored v $ translate ax ay $ mkCircle circleRadius
|
||||
, withStrokeWidth 0 $
|
||||
withStrokeColor "white" $ withFillColor "black" $ mkGroup
|
||||
[ translate ax ay $ ppNum v ]
|
||||
]
|
||||
| v <- nub $ concat [ [a,b,c] | (a,b,c) <- gs]
|
||||
, let V2 ax ay = points V.! (v-1)
|
||||
]]
|
||||
where
|
||||
colored n =
|
||||
let c = promotePixel $ turbo (fromIntegral n / fromIntegral (length gs-1))
|
||||
in withStrokeColorPixel c . withFillColorPixel c
|
||||
ppNum n = cachedNumbers !! n
|
||||
--scaleToHeight (circR*1.5) $ center $ latex $ T.pack $ "\\texttt{" ++ show n ++ "}"
|
||||
|
||||
cachedNumbers =
|
||||
[ scaleToHeight (circleRadius*1.5) $ center $ latex $ T.pack $ "\\texttt{" ++ show n ++ "}"
|
||||
| n <- [0 .. ] ]
|
||||
|
||||
circleRadius :: Double
|
||||
circleRadius = 0.05
|
||||
|
||||
-- Anticlockwise. No duplicate vertices. length >= 3
|
||||
type Polygon = [V2 Double]
|
||||
type P = V2 Double
|
||||
|
||||
-- T = (U, G)
|
||||
-- G = [Polygon]
|
||||
-- U = nub $ concat G
|
||||
|
||||
findStarNeighbours :: Eq a => [(a,a,a)] -> a -> [(a, a)]
|
||||
findStarNeighbours allTrig self =
|
||||
[ (b,c)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == a
|
||||
] ++
|
||||
[ (c,a)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == b
|
||||
] ++
|
||||
[ (a,b)
|
||||
| (a,b,c) <- allTrig
|
||||
, self == c
|
||||
]
|
||||
|
||||
isInterior :: Eq a => [(a, a)] -> Bool
|
||||
isInterior = isJust . getExteriorPoly
|
||||
|
||||
getExteriorPoly :: Eq a => [(a, a)] -> Maybe [a]
|
||||
getExteriorPoly [] = Nothing
|
||||
getExteriorPoly ((a,b):rest) = worker [a] a b rest
|
||||
where
|
||||
worker acc start this [] = do
|
||||
guard (start == this)
|
||||
return (reverse acc)
|
||||
worker acc start this xs =
|
||||
case lookup this xs of
|
||||
Just next -> worker (this:acc) start next (delete (this,next) xs)
|
||||
Nothing ->
|
||||
case lookup this (map swap xs) of
|
||||
Just next -> worker (this:acc) start next (delete (next, this) xs)
|
||||
Nothing -> Nothing
|
||||
|
||||
convexInterpolate :: RelMeshPair -> Double -> RelMesh
|
||||
convexInterpolate (RelMeshPair static edges leftM leftB rightM rightB) t =
|
||||
RelMesh
|
||||
{ relMeshStatic = static
|
||||
, relMeshEdges = edges
|
||||
, relMeshA = Matrix.scale (1-t) leftM +
|
||||
Matrix.scale t rightM
|
||||
, relMeshB = Matrix.scale (1-t) leftB +
|
||||
Matrix.scale t rightB
|
||||
}
|
||||
|
||||
solveMesh :: RelMesh -> Mesh
|
||||
solveMesh (RelMesh static edges m b) =
|
||||
case linearSolve m b of
|
||||
Nothing -> error "Failed to solve mesh"
|
||||
Just ret ->
|
||||
Mesh (static <> V.fromList (worker (toLists ret))) edges
|
||||
where
|
||||
worker [] = []
|
||||
worker ([x]:[y]:rest) = V2 x y : worker rest
|
||||
worker _ = error "invalid result"
|
||||
|
||||
mkRelative :: Mesh -> RelMesh
|
||||
mkRelative (Mesh points edges) = RelMesh (V.fromList exteriorPoints) edges mM bM
|
||||
where
|
||||
mM = (s><s) (concat m)
|
||||
bM = (s><1) b
|
||||
(s,exterior, (m, b)) = toParameters points edges
|
||||
exteriorPoints =
|
||||
[ points V.! (i-1)
|
||||
| i <- exterior
|
||||
]
|
||||
|
||||
mkRelativePair :: MeshPair -> RelMeshPair
|
||||
mkRelativePair (MeshPair p1 p2 edges) =
|
||||
let RelMesh static _ leftM leftB = mkRelative (Mesh p1 edges)
|
||||
RelMesh _ _ rightM rightB = mkRelative (Mesh p2 edges)
|
||||
in RelMeshPair static edges leftM leftB rightM rightB
|
||||
|
||||
toParameters points groups = (length interior*2,exterior,unzip $ concat
|
||||
[ let lst = [(if i == j then -1 else t)
|
||||
| j <- interior
|
||||
, let t = fromMaybe 0 $ lookup (i,j) lam_ij_cache
|
||||
]
|
||||
pos = negate $ sum
|
||||
[ pj ^* t
|
||||
| j <- exterior
|
||||
, let t = fromMaybe 0 $ lookup (i,j) lam_ij_cache
|
||||
pj = points V.! (j-1)
|
||||
]
|
||||
in [ (dupX lst, pos ^. _x)
|
||||
, (dupY lst, pos ^. _y)]
|
||||
| i <- interior ])
|
||||
where
|
||||
lam_ij_cache = lam_ij points groups
|
||||
dupX [] = []
|
||||
dupX (x:xs) = x:0:dupX xs
|
||||
dupY [] = []
|
||||
dupY (x:xs) = 0:x:dupY xs
|
||||
(interior, exterior) =
|
||||
partition (isInterior . findStarNeighbours groups) [1 .. length points]
|
||||
|
||||
lam_ij points groups =
|
||||
[ ((i, j), t)
|
||||
| i <- [1..V.length points]
|
||||
, (j, t) <- lam_j points groups i
|
||||
]
|
||||
lam_j points groups p =
|
||||
[ (nP, sum [ t | (j,k,t) <- mu, j == nP ] / fromIntegral (length nPoints))
|
||||
| let n = findStarNeighbours groups p
|
||||
nPoints = fromMaybe [] $ getExteriorPoly n
|
||||
mu = calcMu points groups p
|
||||
, nP <- nPoints ]
|
||||
calcMu points groups p = concat
|
||||
[ [ (nP, nP, t1)
|
||||
, (a, nP, t2)
|
||||
, (b, nP, t3) ]
|
||||
-- (nP, a, b)
|
||||
| {-p <- [1..length points]-}
|
||||
let selfVert = points V.! (p-1)
|
||||
n = findStarNeighbours groups p
|
||||
nPoints :: [Int]
|
||||
nPoints = fromMaybe [] $ getExteriorPoly n
|
||||
, (i, nP) <- zip [1 .. ] nPoints
|
||||
, let vert = points V.! (nP-1)
|
||||
, let line = (vert, selfVert)
|
||||
, let (a,b,aP,bP) = head $
|
||||
[ (a,b,aP,bP)
|
||||
| (a,b) <- n
|
||||
, let aP = points V.! (a-1)
|
||||
bP = points V.! (b-1)
|
||||
segment = (points V.! (a-1), points V.! (b-1))
|
||||
, case rayIntersect line segment of
|
||||
Nothing -> False
|
||||
Just u -> isBetween u segment
|
||||
, a /= nP
|
||||
, b /= nP ]
|
||||
-- , b == (nPoints ++ nPoints) !! i
|
||||
, let (t1,t2,t3) = barycentricCoords vert aP bP selfVert
|
||||
]
|
||||
main = return ()
|
||||
|
|
|
|||
309
videos/morph/rigid.hs
Normal file
309
videos/morph/rigid.hs
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#!/usr/bin/env stack
|
||||
-- stack runghc --package reanimate
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main where
|
||||
|
||||
import Codec.Picture.Types
|
||||
import Control.Exception
|
||||
import Control.Lens ()
|
||||
import Control.Monad
|
||||
import Data.Function
|
||||
import Data.List
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import Data.Maybe
|
||||
import Data.Ratio
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple
|
||||
import qualified Data.Vector as V
|
||||
import Debug.Trace
|
||||
import Linear.Matrix hiding (trace)
|
||||
import Linear.Metric
|
||||
import Linear.V2
|
||||
import Linear.V3
|
||||
import Linear.Vector
|
||||
import Numeric.LinearAlgebra hiding (polar, scale, (<>))
|
||||
import qualified Numeric.LinearAlgebra as Matrix
|
||||
import Numeric.LinearAlgebra.HMatrix hiding (polar, scale, (<>))
|
||||
import Reanimate
|
||||
import Reanimate.Builtin.Documentation
|
||||
import Reanimate.Debug
|
||||
import Reanimate.Math.Balloon
|
||||
import Reanimate.Math.Common
|
||||
import Reanimate.Math.Compatible (compatiblyTriangulateP)
|
||||
import qualified Reanimate.Math.DCEL as DCEL
|
||||
import Reanimate.Math.EarClip
|
||||
import Reanimate.Math.Polygon
|
||||
import Reanimate.Math.Render
|
||||
import Reanimate.Math.Smooth
|
||||
import Reanimate.Math.SSSP
|
||||
import Reanimate.Math.Visibility
|
||||
import Reanimate.Morph.Common
|
||||
import Reanimate.Morph.LeastDifference
|
||||
import Reanimate.Morph.LeastWork
|
||||
import Reanimate.Morph.Linear
|
||||
import Reanimate.Morph.Rigid
|
||||
import Reanimate.PolyShape (svgToPolygons)
|
||||
import Text.Printf
|
||||
|
||||
-- p1 = centerPolygon $ shape2
|
||||
--p1 = pScale 3.5 $ pAtCenter $ pAddPoints (0+2) (pSetOffset shape13 0)
|
||||
-- p1 = setOffset (addPoints 2 shape13) 0
|
||||
-- p2 = scalePolygon 0.5 $ centerPolygon shape20
|
||||
-- p1 = centerPolygon shape2
|
||||
--p2 = pScale 3.5 $ pAtCenter $ pAddPoints 0 (pSetOffset shape14 0)
|
||||
-- p2 = setOffset shape14 0
|
||||
p1 = pCopy $ pSetOffset (pCopy p1') 0
|
||||
p2 = pCopy $ pSetOffset (pCopy p2') 0
|
||||
(p1', p2') =
|
||||
-- normalizePolygons
|
||||
-- closestLinearCorrespondence
|
||||
(,)
|
||||
-- leastWork zeroStretchCosts defaultBendCosts
|
||||
-- leastWork defaultStretchCosts defaultBendCosts
|
||||
-- (pAtCenter $ unsafeSVGToPolygon 0.01 $ scale 6 $ latex "S")
|
||||
-- (pAtCenter $ unsafeSVGToPolygon 0.01 $ scale 6 $ latex "C")
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "X")
|
||||
(pAtCenter $ unsafeSVGToPolygon 0.1 $ scale 6 $ latex "I")
|
||||
|
||||
p1_ = castPolygon p1
|
||||
p2_ = castPolygon p2
|
||||
polys = triangulate_ p1_ p2_
|
||||
p1_circ = castPolygon (circumference (map fst polys') p1_)
|
||||
p2_circ = castPolygon (circumference (map snd polys') p2_)
|
||||
|
||||
polys' = alignPolygons polys p1_ p2_
|
||||
(p1s, p2s) = unzip $ compatTriagPairs [ (castPolygon a, castPolygon b) | (a,b) <- polys' ]
|
||||
|
||||
-- (p1s,p2s) = unzip (compatiblyTriangulateP p1 p2)
|
||||
|
||||
m2 = DCEL.buildMesh $ DCEL.polygonsMesh
|
||||
(map (fmap realToFrac) $ V.toList $ polygonPoints p2_circ)
|
||||
(map (map (fmap realToFrac) . V.toList . polygonPoints) p2s)
|
||||
|
||||
m1 = DCEL.buildMesh $ DCEL.polygonsMesh
|
||||
(map (fmap realToFrac) $ V.toList $ polygonPoints p1_circ)
|
||||
(map (map (fmap realToFrac) . V.toList . polygonPoints) p1s)
|
||||
|
||||
pipeline1 = last . take 20 . iterate
|
||||
( uncurry DCEL.delaunayFlip .
|
||||
uncurry DCEL.splitInternalEdges .
|
||||
(\(a,b) -> (DCEL.meshSmoothPosition a, DCEL.meshSmoothPosition b)))
|
||||
(m1final, m2final) = last $ take 30 $ iterate
|
||||
(pipeline1 .
|
||||
uncurry DCEL.splitLongestEdge .
|
||||
pipeline1
|
||||
) (m1,m2)
|
||||
|
||||
-- (m1good, m2good) = last $ take 9 $ iterate
|
||||
-- (pipeline1 .
|
||||
-- -- uncurry DCEL.splitLongestEdge .
|
||||
-- pipeline1
|
||||
-- ) (m1,m2)
|
||||
|
||||
main :: IO ()
|
||||
-- main = do
|
||||
-- evaluate (last $ take 900 $ compatiblyTriangulateP p1 p2)
|
||||
-- return ()
|
||||
-- main = reanimate $ playTraces $ length $ take 29 p1s
|
||||
-- main = reanimate $ playThenReverseA $ pauseAround 0.5 0.5 $ sceneAnimation $ do
|
||||
|
||||
main = reanimate $ sceneAnimation $ do
|
||||
newSpriteSVG_ $ mkBackgroundPixel rtfdBackgroundColor
|
||||
-- play
|
||||
-- $ playTraces
|
||||
-- $ traceA (mapA (withFillOpacity 0 . withStrokeColor "white") drawCircle)
|
||||
-- $ traceA (mapA (withFillOpacity 0 . withStrokeColor "white") drawBox)
|
||||
-- $ traceSVG ((withFillOpacity 0 . withStrokeColor "white") $ mkCircle 2)
|
||||
-- $ traceSVG ((withFillOpacity 0 . withStrokeColor "white") $ mkRect 2 2)
|
||||
-- $ 20
|
||||
-- play $ playTraces $ last $ take 35 $ compatiblyTriangulateP p1 p2
|
||||
-- fork $ newSpriteA $ drawCompatible p1 p2
|
||||
-- newSpriteSVG_ $ translate (5) 0 $ lowerTransformations $ scale 2 $
|
||||
-- -- DCEL.renderMesh 0.02 m1final
|
||||
-- DCEL.renderMeshColored m1final
|
||||
-- renderMeshPair optMesh
|
||||
-- newSpriteSVG_ $ translate (-5) 0 $ lowerTransformations $ scale 2 $
|
||||
-- DCEL.renderMesh 0.02 m1good
|
||||
|
||||
-- newSpriteSVG_ $
|
||||
-- translate (-3) 0 $ scale 1 $ mkGroup
|
||||
-- [ mkGroup []
|
||||
-- , withFillColor "grey" $ polygonShape p1
|
||||
-- , withFillColor "grey" $ polygonNumDots p1
|
||||
-- ]
|
||||
-- newSpriteSVG_ $
|
||||
-- translate (3) 0 $ scale 1 $ mkGroup
|
||||
-- [ mkGroup []
|
||||
-- , withFillColor "grey" $ polygonShape p2
|
||||
-- , withFillColor "grey" $ polygonNumDots p2
|
||||
-- ]
|
||||
-- wait 1
|
||||
-- fork $ forM_ p1s $ \p1Piece -> do
|
||||
-- newSpriteSVG_ $
|
||||
-- translate (-3) 0 $ scale 1 $ mkGroup
|
||||
-- [ mkGroup []
|
||||
-- , withFillColor "white" $ polygonShape p1Piece
|
||||
-- -- , withFillColor "grey" $ polygonNumDots p1Piece
|
||||
-- ]
|
||||
-- wait (1/60)
|
||||
-- -- wait 1
|
||||
-- fork $ forM_ p2s $ \p1Piece -> do
|
||||
-- newSpriteSVG_ $
|
||||
-- translate (3) 0 $ scale 1 $ mkGroup
|
||||
-- [ mkGroup []
|
||||
-- , withFillColor "white" $ polygonShape p1Piece
|
||||
-- -- , withFillColor "grey" $ polygonNumDots p1Piece
|
||||
-- ]
|
||||
-- wait (1/60)
|
||||
-- -- wait 1
|
||||
|
||||
-- fork $ play $ mkAnimation 3 $ \t ->
|
||||
-- let points = interpolate prep t
|
||||
-- in translate (-3) 0 $ scale 1 $ mkGroup
|
||||
-- [ mkGroup []
|
||||
-- , drawTrigs points (meshTriangles myMesh)
|
||||
-- -- , DCEL.renderMeshColored m1
|
||||
-- ]
|
||||
fork $ play $ mkAnimation 3 $ \t ->
|
||||
let points = interpolate optPrep t
|
||||
in translate (3) 0 $ lowerTransformations $ scale 1 $ mkGroup
|
||||
[ mkGroup []
|
||||
, drawTrigs points (meshTriangles optMesh)
|
||||
-- , DCEL.renderMeshColored m1final
|
||||
-- , withGroupOpacity 0.5 $ DCEL.renderMesh 0.02 m1final
|
||||
-- , withGroupOpacity 0.5 $ withFillOpacity 0 $ renderMeshPair optMesh
|
||||
-- , let V2 x y = V.head points in
|
||||
-- translate x y $ withFillColor "green" $
|
||||
-- mkCircle 0.03
|
||||
]
|
||||
-- , scale 2 $ polygonNumDots p1
|
||||
-- play $ pauseAtEnd 1 $ mkAnimation 3 $ \t ->
|
||||
-- let points = interpolate bestPrep t
|
||||
-- in translate (3) 0 $ mkGroup [drawTrigs points (meshTriangles bestMesh)
|
||||
-- -- , scale 2 $ polygonNumDots p2
|
||||
-- ]
|
||||
-- play $ pauseAtEnd 1 $ mkAnimation 3 $ \t ->
|
||||
-- let points = interpolate prepRev t in
|
||||
-- mkGroup
|
||||
-- [ drawTrigs points (meshTriangles myMeshRev)
|
||||
-- -- , polygonNumDots shape2
|
||||
-- ]
|
||||
-- forM_ (myMesh : smoothMesh myMesh) $ \newMesh -> do
|
||||
-- let (minAngA, minAngB) = meshMinAngle newMesh
|
||||
-- txt <- newSpriteSVG $ withFillColor "white" $
|
||||
-- mkGroup
|
||||
-- [translate (5) 2 $ center $
|
||||
-- latex $ T.pack $ printf "Min: %.1f" (minAngA/pi*180)
|
||||
-- ,translate (5) 1 $ center $
|
||||
-- latex $ T.pack $ printf "Min: %.1f" (minAngB/pi*180) ]
|
||||
-- s <- newSpriteSVG $ renderAMesh newMesh
|
||||
-- wait (recip 60)
|
||||
-- destroySprite s
|
||||
-- destroySprite txt
|
||||
-- newSpriteSVG $ translate (-3) 0 $ scale 2 $ renderMesh p1 (map fst trigs)
|
||||
-- newSpriteSVG $ translate 3 0 $ scale 2 $ renderMesh p2 (map snd trigs)
|
||||
-- wait 1
|
||||
where
|
||||
prep = prepare myMesh
|
||||
optPrep = prepare optMesh
|
||||
-- bestPrep = prepare bestMesh
|
||||
myMesh = mkMesh p1 p2
|
||||
optMesh = toRigidMesh m1final m2final
|
||||
-- optMesh = DCEL.toRigidMesh m1good m2good
|
||||
-- bestMesh = last $ smoothMesh myMesh
|
||||
prepRev = prepare myMeshRev
|
||||
myMeshRev = reverseMesh myMesh
|
||||
|
||||
|
||||
|
||||
reverseMesh :: Mesh -> Mesh
|
||||
reverseMesh mesh =
|
||||
mesh { meshPointsA = meshPointsB mesh, meshPointsB = meshPointsA mesh }
|
||||
|
||||
mkMesh :: Polygon -> Polygon -> Mesh
|
||||
mkMesh a b = Mesh
|
||||
{ meshPointsA = V.map (fmap realToFrac) pointsA
|
||||
, meshPointsB = V.map (fmap realToFrac) pointsB
|
||||
, meshOutline = V.map (fromJust . flip V.elemIndex pointsA) (polygonPoints a)
|
||||
, meshTriangles = relTrigs
|
||||
}
|
||||
where
|
||||
pointsA =
|
||||
V.fromList $ nub $ V.toList $ V.concat [ polygonPoints a | (a, b) <- trigs ]
|
||||
pointsB =
|
||||
V.fromList $ nub $ V.toList $ V.concat [ polygonPoints b | (a, b) <- trigs ]
|
||||
trigs = compatiblyTriangulateP a b
|
||||
mkRel arr p =
|
||||
( (fromJust $ V.elemIndex (pAccess p 0) arr)
|
||||
, (fromJust $ V.elemIndex (pAccess p 1) arr)
|
||||
, (fromJust $ V.elemIndex (pAccess p 2) arr)
|
||||
)
|
||||
relTrigs = V.fromList [ (mkRel pointsA a) | (a, b) <- trigs ]
|
||||
-- data Mesh = Mesh (Vector P) (Vector (RelTrig, RelTrig))
|
||||
|
||||
testMesh2 :: Mesh
|
||||
testMesh2 = mkMesh (pTranslate (V2 0 0) shape2) shape20
|
||||
|
||||
testPrep = prepare testMesh2
|
||||
|
||||
drawTrigs :: V.Vector (V2 Double) -> V.Vector RelTrig -> SVG
|
||||
drawTrigs points trigs = mkGroup
|
||||
[ mkGroup
|
||||
[ withFillOpacity 1
|
||||
$ withStrokeWidth (defaultStrokeWidth * 0)
|
||||
$ withStrokeColor "grey"
|
||||
$ withFillColor "black"
|
||||
$ drawPolygon
|
||||
$ map (points V.!) [a, b, c]
|
||||
| (a, b, c) <- V.toList trigs
|
||||
]
|
||||
-- , a == 0
|
||||
-- , mkGroup
|
||||
-- [ withFillColor "red" $ mkGroup
|
||||
-- [ drawPoint (points V.! a), drawPoint (points V.! b), drawPoint (points V.! c)]
|
||||
-- | (a,b,c) <- V.toList trigs
|
||||
-- -- , a == 0
|
||||
-- ]
|
||||
]
|
||||
|
||||
drawTrigsLines :: V.Vector (V2 Double) -> V.Vector RelTrig -> SVG
|
||||
drawTrigsLines points trigs = withFillOpacity 0 $ withStrokeColor "black" $ mkGroup
|
||||
[ mkLinePathClosed
|
||||
[ (aPx, aPy)
|
||||
, (bPx, bPy)
|
||||
, (cPx, cPy)]
|
||||
| (a,b,c) <- V.toList trigs
|
||||
-- , a==2 || b == 2 || c == 2
|
||||
, let V2 aPx aPy = points V.! a
|
||||
V2 bPx bPy = points V.! b
|
||||
V2 cPx cPy = points V.! c
|
||||
]
|
||||
|
||||
drawPoint :: V2 Double -> SVG
|
||||
drawPoint (V2 x y) = translate x y $ mkCircle 0.1
|
||||
|
||||
drawPolygon :: [V2 Double] -> SVG
|
||||
drawPolygon lst = mkLinePathClosed [ (x, y) | V2 x y <- lst ]
|
||||
|
||||
drawCompatible :: Polygon -> Polygon -> Animation
|
||||
drawCompatible a b = sceneAnimation $ do
|
||||
let left = -6
|
||||
right = 4
|
||||
newSpriteSVG $ translate left 0 $ mkGroup
|
||||
[ withFillColor "grey" $ polygonShape a
|
||||
, withFillColor "grey" $ polygonNumDots a
|
||||
]
|
||||
newSpriteSVG $ translate right 0 $ mkGroup
|
||||
[ withFillColor "grey" $ polygonShape b
|
||||
, withFillColor "grey" $ polygonNumDots b
|
||||
]
|
||||
return ()
|
||||
-- let compat = compatiblyTriangulateP a b
|
||||
-- forM_ compat $ \(l, r) -> do
|
||||
-- fork $ play $ staticFrame 1 $
|
||||
-- translate left 0 $ withStrokeColor "white" $ withStrokeWidth (defaultStrokeWidth*0.2) $
|
||||
-- withFillOpacity 0 $ polygonShape l
|
||||
-- fork $ play $ staticFrame 1 $
|
||||
-- translate right 0 $ withStrokeColor "white" $ withStrokeWidth (defaultStrokeWidth*0.2) $
|
||||
-- withFillOpacity 0 $ polygonShape r
|
||||
Loading…
Reference in a new issue