hexmino-0.1.0.0: src/Hex.hs
module Hex where
import Data.Char (toUpper)
import qualified Graphics.Gloss.Data.Picture as Pict
data Hexagon = Hexagon {center :: Pict.Point, radius :: Float} deriving (Show)
-- rendering functions
render :: Hexagon -> Pict.Picture
render hex = Pict.translate x y $ renderCentered hex
where (x, y) = center hex
renderCentered :: Hexagon -> Pict.Picture
renderCentered = hexagonSolid . radius
hexagonSolidPointy :: Float -> Pict.Picture
hexagonSolidPointy = Pict.rotate 30 . hexagonSolid
hexagonSolid :: Float -> Pict.Picture
hexagonSolid = Pict.polygon . hexagonPath
hexagonPath :: Float -> Pict.Path
hexagonPath r = [(-r, 0), (-rx, ry), (rx, ry), (r, 0), (rx, -ry), (-rx, -ry)]
where
rx = r / 2
ry = heightFromRadius r / 2
rectangleBlunt :: Float -> Float -> Pict.Picture
rectangleBlunt w h = Pict.polygon $ rectangleBluntPath w h
rectangleBluntLine :: Float -> Float -> Pict.Picture
rectangleBluntLine w h = Pict.line $ (x:xs) ++ [x]
where (x:xs) = rectangleBluntPath w h
rectangleBluntPath :: Float -> Float -> Pict.Path
rectangleBluntPath w h = [(-sw,hh),(-hw,sh),(-hw,-sh),(-sw,-hh),(sw,-hh),(hw,-sh),(hw,sh),(sw,hh)]
where
c = w / 10
hw = w / 2
hh = h / 2
sw = hw - c
sh = hh - c
-- manupulation functions
moveTo :: Pict.Point -> Hexagon -> Hexagon
moveTo point hex = hex {center = point}
moveBy :: Pict.Point -> Hexagon -> Hexagon
moveBy (x, y) hex = hex {center = (cx+x, cy+y)}
where (cx, cy) = center hex
-- utility functions
hexagonHeight :: Hexagon -> Float
hexagonHeight = heightFromRadius . radius
heightFromRadius :: Float -> Float
heightFromRadius = (* sqrt 3)
contains :: Pict.Point -> Hexagon -> Bool -- approximation, but good enough
contains pos hex
| distanceFromCenter pos hex < hexagonHeight hex / 2 = True
| otherwise = False
distanceFromCenter :: Pict.Point -> Hexagon -> Float
distanceFromCenter pnt Hexagon {center = cnt} = pointsDistance pnt cnt
pointsDistance :: Pict.Point -> Pict.Point -> Float
pointsDistance (x1,y1) (x2,y2) = sqrt $ (x1-x2) ** 2 + (y1-y2) ** 2
-- string (contained in a rectangle) and letter with hexagonal shape
hexagonText :: Float -> Float -> String -> Pict.Picture
hexagonText w h txt = Pict.scale fa 1 . Pict.translate offset 0 $ Pict.pictures letters
where
r = h / 3
spacing = r * 2.5
size = length txt
letters = zipWith (`Pict.translate` 0) [0,spacing..] $ map (hexagonChar r) txt
offset = (-spacing) * fromIntegral (size - 1) / 2
fa = w / (fromIntegral size * r * 3)
hexagonChar :: Float -> Char -> Pict.Picture
hexagonChar r letter = thickLine (r/4) $ case toUpper letter of
'A' -> [sw, w, nw, ne, e, w, e, se]
'B' -> [e, se, sw, nw, ne, e, c]
'C' -> [ne, nw, w, sw, se]
'D' -> [sw, nw, ne, e, se, sw]
'E' -> [ne, nw, w, c, w, sw, se]
'F' -> [ne, nw, w, c, w, sw]
'G' -> [ne, nw, w, sw, se, e, c]
'H' -> [nw, w, sw, w, e, ne, e, se]
'I' -> [nw, ne, n, s, se, sw]
'J' -> [ne, e, se, sw, w]
'K' -> [nw, w, sw, w, ne, w, se]
'L' -> [nw, w, sw, se]
'M' -> [sw, w, nw, c, ne, e, se]
'N' -> [sw, w, nw, se, e, ne]
'O' -> [w, nw, ne, e, se, sw, w]
'P' -> [sw, w, nw, ne, e, w]
'Q' -> [se, sw, w, nw, ne, e, se, c]
'R' -> [sw, w, nw, ne, e, w, c, se]
'S' -> [ne, nw, w, e, se, sw]
'T' -> [s, n, nw, w, nw, ne, e]
'U' -> [nw, w, sw, se, e, ne]
'V' -> [nw, w, s, e, ne]
'W' -> [nw, w, sw, c, se, e, ne]
'X' -> [nw, se, c, ne, sw]
'Y' -> [nw, c, s, c, ne]
'Z' -> [w, nw, ne, sw, se, e]
'0' -> [nw, w, sw, se, e, ne, nw, se]
'1' -> [nw, n, s]
'2' -> [w, nw, ne, e, sw, se]
'3' -> [nw, ne, c, e, se, sw]
'4' -> [nw, w, e, ne, e, se]
'5' -> [ne, nw, c, e, se, sw]
'6' -> [ne, nw, w, e, se, sw, w]
'7' -> [w, nw, ne, sw]
'8' -> [c, nw, ne, c, e, se, sw, w, c]
'9' -> [sw, se, e, ne, nw, w, e]
'>' -> [nw, c, sw]
'<' -> [ne, c, se]
'_' -> [sw, se]
'/' -> [sw, ne]
'?' -> [w, nw, ne, e, c, s]
' ' -> []
_ -> [ne, e, w, sw, ne]
where
[w, nw, ne, e, se, sw] = hexagonPath r
c = (0,0)
ry = heightFromRadius r / 2
n = (0, ry)
s = (0, -ry)
thickLine :: Float -> Pict.Path -> Pict.Picture
thickLine t = Pict.pictures . thickSegments t
thickSegments :: Float -> Pict.Path -> [Pict.Picture]
thickSegments t (a:b:xs) = Pict.polygon (thickSegmentPath t a b) : thickSegments t (b:xs)
thickSegments _ _ = []
thickSegmentPath :: Float -> Pict.Point -> Pict.Point -> Pict.Path
thickSegmentPath t (ax, ay) (bx, by)
| abs dx >= abs dy && dx >= 0 = [(ax, ay+r),(ax-r, ay),(ax, ay-r),(bx, by-r),(bx+r, by),(bx, by+r)]
| abs dx >= abs dy = [(ax, ay+r),(ax+r, ay),(ax, ay-r),(bx, by-r),(bx-r, by),(bx, by+r)]
| dy >= 0 = [(ax-r, ay),(ax, ay-r),(ax+r, ay),(bx+r, by),(bx, by+r),(bx-r, by)]
| otherwise = [(ax-r, ay),(ax, ay+r),(ax+r, ay),(bx+r, by),(bx, by-r),(bx-r, by)]
where
(dx, dy) = (bx - ax, by - ay)
r = t / 2