module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pack, box3sWithin) where
import Prelude (Bool, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), sin, asin, pi, max, sqrt, min, compare, (<=), fst, snd, (<>), head, flip, maximum, minimum, (==))
import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, (⋅))
import Data.List (sort, sortBy, (!!))
import Data.VectorSpace (magnitude, normalized, (^-^), (^+^), (*^))
import Data.AffineSpace (distance)
distFromLineSeg :: ℝ2 -> (ℝ2, ℝ2) -> ℝ
distFromLineSeg p (a,b) = distance p closest
where
ab = b ^-^ a
ap = p ^-^ a
d :: ℝ
d = normalized ab ⋅ ap
closest :: ℝ2
closest
| d < 0 = a
| d > magnitude ab = b
| otherwise = a ^+^ d *^ normalized ab
box3sWithin :: ℝ -> (ℝ3, ℝ3) -> (ℝ3, ℝ3) -> Bool
box3sWithin r ((ax1, ay1, az1),(ax2, ay2, az2)) ((bx1, by1, bz1),(bx2, by2, bz2)) =
let
near (a1, a2) (b1, b2) = not $ (a2 + r < b1) || (b2 + r < a1)
in
(ax1,ax2) `near` (bx1, bx2)
&& (ay1,ay2) `near` (by1, by2)
&& (az1,az2) `near` (bz1, bz2)
rmax ::
ℝ
-> ℝ
-> ℝ
-> ℝ
rmax r x y
| r == 0 = max x y
| otherwise = if abs (x-y) < r
then y - r*sin(pi/4-asin((x-y)/r/sqrt 2)) + r
else max x y
rmin ::
ℝ
-> ℝ
-> ℝ
-> ℝ
rmin r x y = if abs (x-y) < r
then y + r*sin(pi/4+asin((x-y)/r/sqrt 2)) - r
else min x y
rmaximum ::
ℝ
-> [ℝ]
-> ℝ
rmaximum _ [] = 0
rmaximum _ [a] = a
rmaximum r [a,b]
| r == 0 = max a b
| otherwise = rmax r a b
rmaximum r l
| r == 0 = maximum l
| otherwise =
let
tops = sortBy (flip compare) l
in
rmax r (head tops) (tops !! 1)
rminimum ::
ℝ
-> [ℝ]
-> ℝ
rminimum _ [] = 0
rminimum _ [a] = a
rminimum r [a,b]
| r > 0 = rmin r a b
| otherwise = min a b
rminimum r l
| r > 0 =
let
tops = sort l
in
rmin r (head tops) (tops !! 1)
| otherwise = minimum l
pack ::
Box2
-> ℝ
-> [(Box2, a)]
-> ([(ℝ2, a)], [(Box2, a)] )
pack (dx, dy) sep objs = packSome sortedObjs (dx, dy)
where
compareBoxesByY :: Box2 -> Box2 -> Ordering
compareBoxesByY ((_, ay1), (_, ay2)) ((_, by1), (_, by2)) =
compare (abs $ by2-by1) (abs $ ay2-ay1)
sortedObjs = sortBy
(\(boxa, _) (boxb, _) -> compareBoxesByY boxa boxb )
objs
tmap1 :: (t2 -> t) -> (t2, t1) -> (t, t1)
tmap1 f (a,b) = (f a, b)
tmap2 :: (t2 -> t1) -> (t, t2) -> (t, t1)
tmap2 f (a,b) = (a, f b)
packSome :: [(Box2,a)] -> Box2 -> ([(ℝ2,a)], [(Box2,a)])
packSome (presObj@(((x1,y1),(x2,y2)),obj):otherBoxedObjs) box@((bx1, by1), (bx2, by2)) =
if abs (x2 - x1) <= abs (bx2-bx1) && abs (y2 - y1) <= abs (by2-by1)
then
let
row = tmap1 (((bx1-x1,by1-y1), obj):) $
packSome otherBoxedObjs ((bx1+x2-x1+sep, by1), (bx2, by1 + y2-y1))
rowAndUp =
if abs (by2-by1) - abs (y2-y1) > sep
then tmap1 (fst row <> ) $
packSome (snd row) ((bx1, by1 + y2-y1+sep), (bx2, by2))
else row
in
rowAndUp
else
tmap2 (presObj:) $ packSome otherBoxedObjs box
packSome [] _ = ([], [])