{-# LANGUAGE TupleSections #-} module Picture ( module Picture.Data , polygon , polygonZ , polygonCol , poly3 , poly3Col , bezierQuad , arc , arcSolid , thickArc , thickCircle , thickLine , thickLineCol , circleSolid , circleSolidCol , circle , line , lineCol , text , pictures , translate , rotate , scale , color , withAlpha , greyN , red , green , blue , yellow , cyan , magenta , rose , violet , azure , aquamarine , chartreuse , orange , white , black , dim , light , dark , bright , mixColors , zeroZ , setDepth , addDepth , setLayer ) where import Geometry import Geometry.Vector3D --import Geometry.Data import Picture.Data --import Data.List --import Data.Bifunctor --import qualified Data.DList as DL --import Graphics.Rendering.OpenGL (lineWidth, ($=)) import Control.Lens black :: RGBA black = (V4 0 0 0 1) polygon :: [Point2] -> Picture {-# INLINE polygon #-} polygon ps = map (f . zeroZ) $ polyToTris ps where f pos = Verx pos black PolyV 0 polygonZ :: [Point2] -> Float -> Picture {-# INLINE polygonZ #-} polygonZ ps z = map (f . zeroZ) $ polyToTris ps where f pos = Verx pos black (PolyzV z) 0 polygonCol :: [(Point2,RGBA)] -> Picture {-# INLINE polygonCol #-} polygonCol vs = map f $ polyToTris vs where f ((V2 x y),col) = Verx (V3 x y 0) col PolyV 0 poly3 :: [Point3] -> Picture {-# INLINE poly3 #-} poly3 = poly3Col . map (, black) poly3Col :: [(Point3,RGBA)] -> Picture {-# INLINE poly3Col #-} poly3Col vs = map f $ polyToTris vs where f (pos,col) = Verx pos col PolyV 0 -- note that much of work computing the width of the bezier curve is done here bezierQuad :: Color -> Color -> Float -> Float -> Point2 -> Point2 -> Point2 -> Picture bezierQuad cola colc ra rc a b c | a == b && b == c = blank | a == b || b == c = bezierQuad cola colc ra rc a (0.5 *.* (a +.+ c)) c | otherwise = bzhelp [-- ( (0,0) , cola, (0,0), (0,0) ) (aIn, cola, (V2 (fa aIn) (fc aIn)) , (V2 1 0) ) ,(aIn, cola, (V2 (fa aIn) (fc aIn)) , (V2 1 0) ) ,(cIn, colc, (V2 (fa cIn) (fc cIn)) , (V2 0 1) ) ,( aX, cola, (V2 1 0) , (V2 (fa' aX) (fc' aX)) ) ,( cX, colc, (V2 0 1) , (V2 (fa' cX) (fc' cX)) ) ,( bX, colb, (V2 0 0) , (V2 (fa' bX) (fc' bX)) ) ,( bX, colb, (V2 0 0) , (V2 (fa' bX) (fc' bX)) ) ] where colb = mixColors 0.5 0.5 cola colc b2a | isLHS a b c = a -.- b | otherwise = b -.- a aRadVec = 0.5 * ra *.* normalizeV (vNormal b2a) aX = a -.- aRadVec aIn = a +.+ aRadVec b2c | isLHS a b c = b -.- c | otherwise = c -.- b cRadVec = 0.5 * rc *.* normalizeV (vNormal b2c) cX = c -.- cRadVec cIn = c +.+ cRadVec bRadVec = 0.25 * (ra + rc) *.* normalizeV (a +.+ b -.- 2 *.* c) bX = b +.+ bRadVec bIn = b -.- bRadVec fa = extrapolate aX cX bX fc = extrapolate cX aX bX fa' = extrapolate aIn cIn bIn fc' = extrapolate cIn aIn bIn bzhelp :: [(Point2, Point4, Point2, Point2)] -> Picture bzhelp = map f where f ((V2 x y),col,(V2 a b),(V2 c d)) = Verx (V3 x y 0) col (BezV (V4 a b c d)) 0 -- given a one and two zeros of a linear function over x and y, -- determine the function -- so if f(ox,oy) = 1 and f(ax,ay) = f(bx,by) = 0, determines f extrapolate :: Point2 -> Point2 -> Point2 -> Point2 -> Float extrapolate (V2 ox oy) (V2 ax ay) (V2 bx by) (V2 x y) = ( x * ( ay - by ) + y * ( bx - ax ) + (ax * by - bx * ay) ) / ( ox * ( ay - by ) + ax * ( by - oy ) + bx * ( oy - ay ) ) color :: RGBA -> Picture -> Picture {-# INLINE color #-} color c = map $ overCol (const c) translate3 :: Float -> Float -> Point3 -> Point3 {-# INLINE translate3 #-} translate3 a b (V3 x y z) = V3 (x+a) (y+b) z translate :: Float -> Float -> Picture -> Picture --{-# INLINE translate #-} --translate x y = map $ second $ overPos (translate3 x y) translate x y = map $ overPos (translate3 x y) setDepth :: Float -> Picture -> Picture --{-# INLINE setDepth #-} --setDepth d = map $ second $ overPos (\(x,y,_) -> (x,y,d)) setDepth d = map $ overPos (\(V3 x y _) -> V3 x y d) addDepth :: Float -> Picture -> Picture --{-# INLINE addDepth #-} --addDepth d = map $ second $ overPos (\(x,y,z) -> (x,y,z+d)) addDepth d = map $ overPos (\(V3 x y z) -> V3 x y (z+d)) setLayer :: Int -> Picture -> Picture {-# INLINE setLayer #-} setLayer i = map f where f v = v {_vxLayer = i} scale3 :: Float -> Float -> Point3 -> Point3 {-# INLINE scale3 #-} scale3 a b (V3 x y z) = (V3 (x*a) (y*b) (z)) scale :: Float -> Float -> Picture -> Picture --{-# INLINE scale #-} --scale x y = map . second . overPos $ scale3 x y scale x y = map $ overPos $ scale3 x y rotate :: Float -> Picture -> Picture --{-# INLINE rotate #-} rotate a = map $ overPos $ rotate3 a pictures :: [Picture] -> Picture {-# INLINE pictures #-} pictures = concat makeArc :: Float -> Point2 -> [Point2] {-# INLINE makeArc #-} makeArc rad (V2 a b) = map (`rotateV` (V2 0 rad)) angles where angles = [a,a+step.. b] step = pi * 0.2 circleSolid :: Float -> Picture {-# INLINE circleSolid #-} circleSolid = circleSolidCol white white circleSolidCol :: Color -> Color -> Float -> Picture {-# INLINE circleSolidCol #-} circleSolidCol colC colE r = map f [( (V3 (-r) ( r) (0)), colC) ,( (V3 (-r) (-r) (0)), colE) ,( (V3 ( r) (-r) (0)), black) ] where f (pos,col) = Verx pos col EllV 0 circle :: Float -> Picture {-# INLINE circle #-} circle rad = thickArc 0 (2*pi) rad 1 text :: String -> Picture {-# INLINE text #-} text s = map f $ stringToList s where f (pos,col,val) = Verx pos col (TextV val) 0 line :: [Point2] -> Picture {-# INLINE line #-} --line = Line line = flip thickLine 1 lineCol :: [(Point2,RGBA)] -> Picture {-# INLINE lineCol #-} --lineCol = LineCol lineCol = flip thickLineCol 1 thickLine :: [Point2] -> Float -> Picture {-# INLINE thickLine #-} thickLine ps t = pictures $ f ps where f (x:y:ys) | x == y = f (x:ys) | otherwise = polygon [x +.+ n x y, x -.- n x y, y -.- n x y, y +.+ n x y] : f (y:ys) f _ = [] n a b = (t*0.5) *.* errorNormalizeV 42 (vNormal (a -.- b)) thickLineCol :: [(Point2,RGBA)] -> Float -> Picture {-# INLINE thickLineCol #-} thickLineCol ps t = pictures $ f ps where f ((x,c):(y,c'):ys) | x == y = f ((x,c):ys) | otherwise = polygonCol [(x +.+ n x y,c) ,(x -.- n x y,c) ,(y -.- n x y,c') ,(y +.+ n x y,c') ] : f ((y,c'):ys) f _ = [] n a b = (t*0.5) *.* safeNormalizeV (vNormal (a -.- b)) thickCircle :: Float -> Float -> Picture {-# INLINE thickCircle #-} thickCircle = thickArc 0 (2*pi) arcSolid :: Float -- ^ Start angle -> Float -- ^ End angle -> Float -- ^ Radius -> Picture {-# INLINE arcSolid #-} arcSolid startA endA rad = polygon $ (V2 0 0) : makeArc rad (V2 startA endA) arc :: Float -- ^ Start angle -> Float -- ^ End angle -> Float -- ^ Radius -> Picture arc startA endA rad = thickArc startA endA rad 1 {-# INLINE arc #-} thickArc :: Float -> Float -> Float -> Float -> Picture {-# INLINE thickArc #-} thickArc startA endA rad wdth | endA - startA > pi = pictures [ thickArc (startA + pi) endA rad wdth , thickArcHelp startA (startA + pi) r w ] | otherwise = thickArcHelp startA endA r w where r = rad + 0.5 * wdth w = 1 - wdth / r thickArcHelp :: Float -> Float -> Float -> Float -> [Verx] thickArcHelp startA endA rad wdth = map f [( (V3 0 0 0),black,(V3 0 0 wdth)) ,((V3 xa ya 0),black,(V3 1 0 wdth)) ,((V3 xb yb 0),black,(V3 1 1 wdth)) ,( (V3 0 0 0),black,(V3 0 0 wdth)) ,((V3 xb yb 0),black,(V3 1 1 wdth)) ,((V3 xc yc 0),black,(V3 0 1 wdth)) ] where (V2 xa ya) = rotateV startA (V2 rad 0) (V2 xb yb) = rotateV (0.5 * (startA + endA)) (V2 (rad * sqrt 2) (0)) (V2 xc yc) = rotateV endA (V2 rad 0) f (pos,col,val) = Verx pos col (ArcV val) 0 withAlpha :: Float -> RGBA -> RGBA {-# INLINE withAlpha #-} withAlpha a (V4 x y z a') = (V4 x y z (a*a')) red,green,blue,yellow,cyan,magenta,rose,violet,azure,aquamarine,chartreuse,orange,white::Color red = toV4 (1,0,0,1) green = toV4 (0,1,0,1) blue = toV4 (0,0,1,1) yellow = toV4 (1,1,0,1) cyan = toV4 (0,1,1,1) magenta = toV4 (1,0,1,1) rose = toV4 (1,0,0.5,1) violet = toV4 (0.5,0,1,1) azure = toV4 (0,0.5,1,1) aquamarine= toV4 (0,1,0.5,1) chartreuse= toV4 (0.5,1,0,1) orange = toV4 (1,0.5,0,1) white = toV4 (1,1,1,1) mixColors :: Float -> Float -> Color -> Color -> Color mixColors rata ratb (V4 r0 g0 b0 a0) (V4 r2 g2 b2 a2) = let fullrat = rata + ratb normrata = rata / fullrat normratb = ratb / fullrat f x y = sqrt $ normrata * x^(2::Int) + normratb * y^(2::Int) in (V4 (f r0 r2 ) ( f g0 g2 ) ( f b0 b2 ) ( normrata * a0 + normratb * a2)) light :: Color -> Color light (V4 r g b a) = (V4 (r+0.2) (g+0.2) (b+0.2) (a)) dark :: Color -> Color dark (V4 r g b a) = (V4 (r-0.2) (g-0.2) (b-0.2) (a)) dim :: Color -> Color dim (V4 r g b a) = (V4 (r/1.2) (g/1.2) (b/1.2) (a)) bright :: Color -> Color bright (V4 r g b a) = (V4 (r*1.2) (g*1.2) (b*1.2) (a)) greyN :: Float -> Color greyN x = toV4 (x,x,x,1) overPos :: (Point3 -> Point3) -> Verx -> Verx {-# INLINE overPos #-} overPos = over vxPos overCol :: (Point4 -> Point4) -> Verx -> Verx overCol = over vxCol -- no premature optimisation, consider changing to use texture arrays stringToList :: String -> [(Point3,Point4,Point2)] {-# INLINE stringToList #-} --stringToList s = concat $ zipWith charToTuple [0,0.9*dimText ..] s stringToList s = concatMap (uncurry charToTuple) $ zip [0,0.9*dimText ..] s where dimText = 100 charToTuple :: Float -> Char -> [(Point3,Point4,Point2)] {-# INLINE charToTuple #-} charToTuple x c = [((V3 (x-50) (-100) (0)), white,(V2 offset 1)) ,((V3 (x-50) (100) (0)), white,(V2 offset 0)) ,((V3 (x+50) (100) (0)), white,(V2 (offset+1) 0)) ,((V3 (x-50) (-100) (0)), white,(V2 offset 1)) ,((V3 (x+50) (-100) (0)), white,(V2 (offset+1) 1)) ,((V3 (x+50) (100) (0)), white,(V2 (offset+1) 0)) ] where offset = fromIntegral (fromEnum c) - 32