import Data.List
import Text.Regex
import System.Random
import Data.Ord
type Point = (Float,Float)
type Color = (Int,Int,Int)
type Polygon = [Point]
type Person = [Int]
type Link = [Point]
type Placement = [(Point,Person)]
type EnergyFunction a = a -> Int
type TemperatureFunction = Int -> Int -> Float
type TransitionProbabilityFunction = Int -> Int -> Float -> Float
type MotionFunction a = StdGen -> a -> (StdGen,a)
main = do
putStr "Hello World! Let's have a picnic! \n"
people_text <- readFile "people.txt"
let people :: [Person]
people = read people_text
putStr "Number of people coming: "
print (length people)
let writePoint :: Point -> String
writePoint (x,y) = (show x)++","++(show y)++" "
let writePolygon :: (Color,Polygon) -> String
writePolygon ((r,g,b),p) = ""
let writePolygons :: [(Color,Polygon)] -> String
writePolygons p = ""
let colorize :: Color -> [Polygon] -> [(Color,Polygon)]
colorize = zip.repeat
let rainbow@[red,green,blue,yellow,purple,teal] = map colorize [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(255,0,255),(0,255,255)]
writeFile "tut0.svg" $ writePolygons (blue [[(100,100),(200,100),(200,200),(100,200)],[(200,200),(300,200),(300,300),(200,300)]])
let readPoint :: String -> Point
readPoint s | Just [x,y] <- matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s = (read x,read y)
let readPolygon :: String -> Polygon
readPolygon = (map readPoint).(splitRegex $ mkRegex " L ")
let readPolygons :: String -> [Polygon]
readPolygons = (map readPolygon).tail.(splitRegex $ mkRegex " [Polygon]
triangulate (a:b:c:xs) = [a,b,c]:triangulate (a:c:xs)
triangulate _ = []
let triangles = concatMap triangulate park
writeFile "tut2.svg" $ writePolygons (purple triangles)
let clipTriangle :: (Point -> Point -> Point) -> [Point] -> [Point] -> [Polygon]
clipTriangle i [] [a,b,c] = []
clipTriangle i [a] [b,c] = [[a,i a b,i a c]]
clipTriangle i [a,b] [c] = [[a,i a c,b],[b,i a c,i b c]]
clipTriangle i [a,b,c] [] = [[a,b,c]]
let slice :: (Point -> Bool) -> (Point -> Point -> Point) -> [Polygon] -> ([Polygon],[Polygon])
slice f i t = (clip f,clip $ not.f)
where clip g = concatMap ((uncurry $ clipTriangle i).(partition g)) t
let sliceX :: Float -> [Polygon] -> ([Polygon],[Polygon])
sliceX x = slice ((x >).fst) interpolateX
where interpolateX (x1,y1) (x2,y2) = (x,y1+(y2-y1)*(x-x1)/(x2-x1))
let sliceY :: Float -> [Polygon] -> ([Polygon],[Polygon])
sliceY y = slice ((y >).snd) interpolateY
where interpolateY (x1,y1) (x2,y2) = (x1+(x2-x1)*(y-y1)/(y2-y1),y)
let (left_side,right_side) = sliceX 200 triangles
writeFile "tut3.svg" $ writePolygons $ (red left_side) ++ (blue right_side)
let boundingRect :: [Polygon] -> (Float,Float,Float,Float)
boundingRect p = (minimum xs,minimum ys,maximum xs,maximum ys)
where xs = map fst $ concat p
ys = map snd $ concat p
let halveTriangles :: Int -> [Polygon] -> ([Polygon],[Polygon])
halveTriangles n p = let (l,t,r,b) = boundingRect p
f = fromIntegral n
h = fromIntegral $ div n 2
in if r-l > b-t
then sliceX ((r*h+l*(f-h))/f) p
else sliceY ((b*h+t*(f-h))/f) p
let distance :: Point -> Point -> Float
distance p1 p2 = sqrt (deltax*deltax+deltay*deltay)
where deltax = (fst p1)-(fst p2)
deltay = (snd p1)-(snd p2)
let area :: Polygon -> Float
area [a,b,c] = let x = distance a b
y = distance b c
z = distance c a
s = (x+y+z)/2
in sqrt (s*(s-x)*(s-y)*(s-z))
let allocatePeople :: Int -> [Polygon] -> [[Polygon]]
allocatePeople 0 t = []
allocatePeople 1 t = [t]
allocatePeople n t = let (t1,t2) = halveTriangles n t
a1 = sum $ map area t1
a2 = sum $ map area t2
f = round $ (fromIntegral n)*a1/(a1+a2)
in (allocatePeople f t1)++(allocatePeople (n-f) t2)
let lots = allocatePeople (length people) triangles
writeFile "tut4.svg" $ writePolygons $ concat $ zipWith ($) (cycle rainbow) lots
let findLotCenter :: [Polygon] -> Point
findLotCenter p = let (l,t,r,b) = boundingRect p
m@(x,y) = ((r+l)/2,(b+t)/2)
(lh,rh) = sliceX x p
(th,bh) = sliceY y $ lh ++ rh
centerOrder p1 p2 = compare (distance p1 m) (distance p2 m)
in minimumBy (comparing $ distance m) $ concat $ th ++ bh
let makeDot :: Point -> Polygon
makeDot (x,y) = [(x-2,y-2),(x+2,y-2),(x+2,y+2),(x-2,y+2)]
let centers = map findLotCenter lots
let spots = blue $ map makeDot centers
writeFile "tut5.svg" $ writePolygons $ (green park) ++ spots
let shortestLinks :: Int -> [Link] -> [Link]
shortestLinks n = (take n).(sortBy $ comparing linkLength)
where linkLength [a,b] = distance a b
let sittingNeighbors :: Int -> [Point] -> [Link]
sittingNeighbors n p = nub $ shortestLinks (n * (length p)) [[a,b] | a <- p, b <- p, a /= b]
let sitting = sittingNeighbors 4 centers
writeFile "tut6.svg" $ writePolygons $ (green park) ++ spots ++ (red sitting)
let walkingNeighbors :: Int -> [Point] -> [Link]
walkingNeighbors n l = nub $ concatMap myNeighbors l
where myNeighbors :: Point -> [Link]
myNeighbors p = shortestLinks n [sort [p,c] | c <- l, p /= c]
let walking = walkingNeighbors 4 centers
writeFile "tut7.svg" $ writePolygons $ (green park) ++ spots ++ (red walking)
let starting_placement = zip centers people
let mismatches :: Person -> Person -> Int
mismatches a b = length $ filter (uncurry (/=)) $ zip a b
let similarityColor :: Person -> Person -> Color
similarityColor p1 p2 = let m = mismatches p1 p2
h = div (length p1) 2
d = 30 * (abs (h - m))
b = max 0 (255-d)
o = min d 255
in if m < h
then (0,o,b)
else (o,0,b)
let findPerson :: Placement -> Point -> Person
findPerson a p | Just (_,e) <- find ((== p).fst) a = e
let similarityLine :: Placement -> Link -> (Color,Polygon)
similarityLine l [p1,p2] = (similarityColor (findPerson l p1) (findPerson l p2),[p1,p2])
writeFile "tut8.svg" $ writePolygons $ map (similarityLine starting_placement) sitting
let picnicEnergy :: [Link] -> EnergyFunction Placement
picnicEnergy l a = sum $ map linkEnergy l
where linkEnergy :: Link -> Int
linkEnergy [p1,p2] = mismatches (findPerson a p1) (findPerson a p2)
let picnicMotion :: [Link] -> MotionFunction Placement
picnicMotion l r a = let (n,r2) = randomR (0,(length l)-1) r
[p1,p2] = l!!n
in (r2,(p1,findPerson a p2):(p2,findPerson a p1):(filter (not.((flip elem) [p1,p2]).fst) a))
let picnicTemperature :: TemperatureFunction
picnicTemperature m c = 50.0 * (exp (0.0 - (5.0 * ((fromIntegral c) / (fromIntegral m)))))
let picnicTransitionalProbability :: TransitionProbabilityFunction
picnicTransitionalProbability e1 e2 t = exp ((fromIntegral (e1 - e2)) / t)
let annealing_time = 500
putStr "starting energy: "
print $ picnicEnergy sitting starting_placement
putStr "starting temperature: "
print $ picnicTemperature annealing_time annealing_time
let anneal_tick :: MotionFunction a -> TransitionProbabilityFunction -> EnergyFunction a -> Float -> (StdGen,a) -> (StdGen,a)
anneal_tick mf tpf ef t (r,p) = let (r2,p2) = mf r p
(n ,r3) = random r2
in (r3,
if n < tpf (ef p) (ef p2) t
then p2
else p)
let anneal :: EnergyFunction a -> MotionFunction a -> TransitionProbabilityFunction -> TemperatureFunction -> Int -> StdGen -> a -> a
anneal ef mf tpf tf m r s = snd $ foldl' (flip (anneal_tick mf tpf ef)) (r,s) (map (tf m) [0..m])
random_generator <- getStdGen
putStr "starting annealing... "
putStr "number of annealing steps: "
print annealing_time
let ideal_placement = anneal
(picnicEnergy sitting)
(picnicMotion walking)
picnicTransitionalProbability
picnicTemperature
annealing_time
random_generator
starting_placement
writeFile "tut9.svg" $ writePolygons $ map (similarityLine ideal_placement) sitting
putStr "Done!\nfinal energy: "
print $ picnicEnergy sitting ideal_placement
putStr "final temperature: "
print $ picnicTemperature 0 annealing_time