module Wumpus.Tree.Design
(
design
, rotateAboutRoot
)
where
import Wumpus.Tree.Base
import Wumpus.Tree.ScalingContext
import Wumpus.Core
import Data.List
import Data.Maybe
import Data.Tree
type XPos u = u
type XTree u a = Tree (XPos u, a)
type Delta u = u
data HSpan u = HSpan !u !u
deriving (Eq,Ord,Show)
outsideMerge :: HSpan u -> HSpan u -> HSpan u
outsideMerge (HSpan p _) (HSpan _ q) = HSpan p q
moveSpan :: Num u => Delta u -> HSpan u -> HSpan u
moveSpan d (HSpan p q) = HSpan (p+d) (q+d)
newtype Extent u = Extent { span_list :: [HSpan u] }
deriving (Eq,Show)
extlink :: u -> Extent u -> Extent u
extlink a (Extent as) = Extent $ (HSpan a a) :as
midtop :: Fractional u => u -> Extent u -> XPos u
midtop r (Extent []) = r
midtop _ (Extent (HSpan p q:_)) = p + (0.5*(qp))
mergeMR :: Num u => Delta u -> Extent u -> Extent u -> Extent u
mergeMR dx (Extent xs) (Extent ys) = Extent $ step xs ys
where
step ps [] = ps
step [] qs = map (moveSpan dx) qs
step (p:ps) (q:qs) = outsideMerge p (moveSpan dx q) : step ps qs
mergeML :: Num u => Delta u -> Extent u -> Extent u -> Extent u
mergeML dx (Extent xs) (Extent ys) = Extent $ step xs ys
where
step ps [] = map (moveSpan dx) ps
step [] qs = qs
step (p:ps) (q:qs) = outsideMerge (moveSpan dx p) q : step ps qs
extentZero :: Extent u
extentZero = Extent []
extentOne :: XPos u -> Extent u
extentOne x = Extent [HSpan x x]
moveTree :: Num u => Delta u -> XTree u a -> XTree u a
moveTree dx (Node (x,a) subtrees) = Node ((x+dx),a) subtrees'
where
subtrees' = map (moveTree dx) subtrees
fit :: (Fractional u, Ord u)
=> Extent u -> Extent u -> u
fit a b = go (span_list a) (span_list b) 0.0
where
go (HSpan _ p:ps) (HSpan q _:qs) acc = go ps qs (max acc (p q + 1.0))
go _ _ acc = acc
fitleft :: (Fractional u, Ord u)
=> [(XTree u a,Extent u)] -> ([XTree u a], Extent u)
fitleft [] = ([],extentZero)
fitleft ((l,ext):xs) = (l:ts,ext')
where
(ext',ts) = mapAccumL step ext xs
step aex (t,ex) = let dx = fit aex ex
in (mergeMR dx aex ex, moveTree dx t)
fitright :: (Fractional u, Ord u)
=> [(XTree u a, Extent u)] -> ([XTree u a], Extent u)
fitright = post . foldr fn Nothing
where
post = fromMaybe ([],extentZero)
fn (t,ex) Nothing = Just ([t],ex)
fn (t,ex) (Just (ts,aex)) = Just (t':ts,aex')
where
dx = negate $ fit ex aex
t' = moveTree dx t
aex' = mergeML dx ex aex
designl :: forall a u. (Fractional u, Ord u)
=> Tree a -> (XTree u a, Extent u)
designl (Node a []) = (Node (0.0,a) [], extentOne 0.0)
designl (Node a kids) = (Node (xpos,a) kids', ext1)
where
xs :: [(XTree u a, Extent u)]
xs = map designl kids
kids' :: [XTree u a]
ext0, ext1 :: Extent u
(kids',ext0) = fitleft xs
xpos = midtop 0.0 ext0
ext1 = xpos `extlink` ext0
designr :: forall u a. (Fractional u, Ord u)
=> XPos u -> Tree a -> (XTree u a, Extent u)
designr r (Node a []) = (Node (r,a) [], extentOne r)
designr r (Node a kids) = (Node (xpos,a) kids', ext1)
where
xs :: [(XTree u a, Extent u)]
xs = map (designr r) kids
kids' :: [XTree u a]
ext0, ext1 :: Extent u
(kids',ext0) = fitright xs
xpos = midtop r ext0
ext1 = xpos `extlink` ext0
design :: (Fractional u, Ord u)
=> Point2 u -> ScalingContext u Int u -> Tree a -> CoordTree u a
design ro sctx t = rootOrientate ro $ scaleDesign sctx 0 t3
where
(t1,ext) = designl t
(_, HSpan xmin xmax) = stats ext
width = xmax xmin
(t2,_) = designr width t
t3 = treeZipWith zfn t1 t2
zfn (x0,a) (x1,_) = (mean x0 x1,a)
scaleDesign :: Num uy
=> ScalingContext ux uy u -> uy -> Tree (XPos ux, a) -> CoordTree u a
scaleDesign ctx lvl (Node (xpos,a) kids) = Node (pt,a) kids'
where
pt = scalePt ctx xpos lvl
kids' = map (scaleDesign ctx (lvl1)) kids
rootOrientate :: Num u => Point2 u -> CoordTree u a -> CoordTree u a
rootOrientate (P2 ox oy) (Node (P2 x0 y0, val) kids) =
Node (P2 ox oy, val) $ map (mv (oxx0) (oyy0)) kids
where
mv dx dy (Node (P2 x y, a) ks) = let ks' = map (mv dx dy) ks
in Node (P2 (x+dx) (y+dy), a) ks'
rotateAboutRoot :: (Real u, Floating u)
=> Radian -> CoordTree u a -> CoordTree u a
rotateAboutRoot ang (Node (ogin,val) kids) =
Node (ogin, val) $ map step kids
where
step (Node (p0, a) ks) = Node (rotA p0, a) $ map step ks
rotA = rotateAbout ang ogin
stats :: (Num u, Ord u) => Extent u -> (Int, HSpan u)
stats (Extent []) = (0,HSpan 0 0)
stats (Extent (e:es)) = foldr fn (1,e) es
where
fn s1 (h, acc_span) = (h+1, minmaxMerge s1 acc_span)
mean :: Fractional u => u -> u -> u
mean x y = (x+y) / 2.0
minmaxMerge :: Ord u => HSpan u -> HSpan u -> HSpan u
minmaxMerge (HSpan p q) (HSpan p' q') = HSpan (min p p') (max q q')
treeZipWith :: (a -> b -> c) -> Tree a -> Tree b -> Tree c
treeZipWith f (Node a xs) (Node b ys) = Node (f a b) (step xs ys)
where
step (p:ps) (q:qs) = treeZipWith f p q : step ps qs
step _ _ = []