module Graphics.Implicit.Export.MarchingSquares (getContour) where
import Graphics.Implicit.Definitions
import Control.Parallel.Strategies (using, parList, rdeepseq)
import Debug.Trace
import Data.VectorSpace
both :: (a -> b) -> (a,a) -> (b,b)
both f (x,y) = (f x, f y)
getContour :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline]
getContour p1 p2 d obj =
let
n@(nx,ny) = (fromIntegral . ceiling) `both` ((p2 ^-^ p1) ⋯/ d)
gridPos :: (Int,Int) -> (Int,Int) -> ℝ2
gridPos (nx,ny) (mx,my) = let p = ( fromIntegral mx / fromIntegral nx
, fromIntegral my / fromIntegral ny)
in p1 ^+^ (p2 ^-^ p1) ⋯* p
linesOnGrid :: [[[Polyline]]]
linesOnGrid = [[getSquareLineSegs
(gridPos n (mx,my))
(gridPos n (mx+1,my+1))
obj
| mx <- [0.. nx1] ] | my <- [0..ny1] ]
multilines = (filter polylineNotNull) $ (map reducePolyline) $ orderLinesDC $ linesOnGrid
in
multilines
getContour2 :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline]
getContour2 p1@(x1, y1) p2@(x2, y2) d obj =
let
n@(nx,ny) = (fromIntegral . ceiling) `both` ((p2 ^-^ p1) ⋯/ d)
fromGrid (mx, my) = let p = (mx/nx, my/ny)
in (p1 ^+^ (p2 ^-^ p1) ⋯/ p)
toGrid (x,y) = (floor $ nx*(xx1)/(x2x1), floor $ ny*(yy1)/(y2y1))
valsOnGrid :: [[ℝ]]
valsOnGrid = [[ obj (fromGrid (mx, my)) | mx <- [0.. nx1] ] | my <- [0..ny1] ]
`using` parList rdeepseq
preEvaledObj p = valsOnGrid !! my !! mx where (mx,my) = toGrid p
linesOnGrid :: [[[Polyline]]]
linesOnGrid = [[getSquareLineSegs (fromGrid (mx, my)) (fromGrid (mx+1, my+1)) preEvaledObj
| mx <- [0.. nx1] ] | my <- [0..ny1] ]
multilines = (filter polylineNotNull) $ (map reducePolyline) $ orderLinesDC $ linesOnGrid
in
multilines
getSquareLineSegs :: ℝ2 -> ℝ2 -> Obj2 -> [Polyline]
getSquareLineSegs p1@(x1, y1) p2@(x2, y2) obj =
let
(x,y) = (x1, y1)
x1y1 = obj (x1, y1)
x2y1 = obj (x2, y1)
x1y2 = obj (x1, y2)
x2y2 = obj (x2, y2)
c = obj ((x1+x2)/2, (y1+y2)/2)
dx = x2 x1
dy = y2 y1
midx1 = (x, y + dy*x1y1/(x1y1x1y2))
midx2 = (x + dx, y + dy*x2y1/(x2y1x2y2))
midy1 = (x + dx*x1y1/(x1y1x2y1), y )
midy2 = (x + dx*x1y2/(x1y2x2y2), y + dy)
notPointLine (p1:p2:[]) = p1 /= p2
in filter (notPointLine) $ case (x1y2 <= 0, x2y2 <= 0,
x1y1 <= 0, x2y1 <= 0) of
(True, True,
True, True) -> []
(False, False,
False, False) -> []
(True, True,
False, False) -> [[midx1, midx2]]
(False, False,
True, True) -> [[midx1, midx2]]
(False, True,
False, True) -> [[midy1, midy2]]
(True, False,
True, False) -> [[midy1, midy2]]
(True, False,
False, False) -> [[midx1, midy2]]
(False, True,
True, True) -> [[midx1, midy2]]
(True, True,
False, True) -> [[midx1, midy1]]
(False, False,
True, False) -> [[midx1, midy1]]
(True, True,
True, False) -> [[midx2, midy1]]
(False, False,
False, True) -> [[midx2, midy1]]
(True, False,
True, True) -> [[midx2, midy2]]
(False, True,
False, False) -> [[midx2, midy2]]
(True, False,
False, True) -> if c > 0
then [[midx1, midy2], [midx2, midy1]]
else [[midx1, midy1], [midx2, midy2]]
(False, True,
True, False) -> if c <= 0
then [[midx1, midy2], [midx2, midy1]]
else [[midx1, midy1], [midx2, midy2]]
orderLines :: [Polyline] -> [Polyline]
orderLines [] = []
orderLines (present:remaining) =
let
findNext ((p3:ps):segs) = if p3 == last present then (Just (p3:ps), segs) else
if last ps == last present then (Just (reverse $ p3:ps), segs) else
case findNext segs of (res1,res2) -> (res1,(p3:ps):res2)
findNext [] = (Nothing, [])
in
case findNext remaining of
(Nothing, _) -> present:(orderLines remaining)
(Just match, others) -> orderLines $ (present ++ tail match): others
reducePolyline ((x1,y1):(x2,y2):(x3,y3):others) =
if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):(x3,y3):others) else
if abs ( (y2y1)/(x2x1) (y3y1)/(x3x1) ) < 0.0001
|| ( (x2x1) == 0 && (x3x1) == 0 && (y2y1)*(y3y1) > 0)
then reducePolyline ((x1,y1):(x3,y3):others)
else (x1,y1) : reducePolyline ((x2,y2):(x3,y3):others)
reducePolyline ((x1,y1):(x2,y2):others) =
if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):others) else (x1,y1):(x2,y2):others
reducePolyline l = l
orderLinesDC :: [[[Polyline]]] -> [Polyline]
orderLinesDC segs =
let
halve :: [a] -> ([a], [a])
halve l = splitAt (div (length l) 2) l
splitOrder segs = case (\(x,y) -> (halve x, halve y)) . unzip . map (halve) $ segs of
((a,b),(c,d)) -> orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d
in
if (length segs < 5 || length (head segs) < 5 ) then concat $ concat segs else
case (\(x,y) -> (halve x, halve y)) $ unzip $ map (halve) segs of
((a,b),(c,d)) ->orderLines $
orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d
polylineNotNull (a:l) = not (null l)
polylineNotNull [] = False