packages feed

htdp-image 1.0.0.0 → 1.1.0.0

raw patch · 13 files changed

+841/−836 lines, 13 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Graphics.Combinator: above :: Image -> Image -> Image
- Graphics.Combinator: aboveAlign :: Alignment -> Image -> Image -> Image
- Graphics.Combinator: aboves :: [Image] -> Image
- Graphics.Combinator: abovesAlign :: Alignment -> [Image] -> Image
- Graphics.Combinator: beside :: Image -> Image -> Image
- Graphics.Combinator: besideAlign :: Alignment -> Image -> Image -> Image
- Graphics.Combinator: besides :: [Image] -> Image
- Graphics.Combinator: besidesAlign :: Alignment -> [Image] -> Image
- Graphics.Combinator: data Alignment
- Graphics.Combinator: high :: Alignment
- Graphics.Combinator: low :: Alignment
- Graphics.Combinator: mid :: Alignment
- Graphics.Combinator: overlay :: Image -> Image -> Image
- Graphics.Combinator: overlayAlign :: Alignment -> Alignment -> Image -> Image -> Image
- Graphics.Combinator: overlayAlignOffset :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: overlayOffset :: Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: overlayXY :: Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: placeImage :: Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: placeImageAlign :: Image -> Float -> Float -> Alignment -> Alignment -> Image -> Image
- Graphics.Combinator: placeImages :: [Image] -> [(Float, Float)] -> Image -> Image
- Graphics.Combinator: placeImagesAlign :: [Image] -> [(Float, Float)] -> Alignment -> Alignment -> Image -> Image
- Graphics.Combinator: underlay :: Image -> Image -> Image
- Graphics.Combinator: underlayAlign :: Alignment -> Alignment -> Image -> Image -> Image
- Graphics.Combinator: underlayAlignOffset :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: underlayOffset :: Image -> Float -> Float -> Image -> Image
- Graphics.Combinator: underlayXY :: Image -> Float -> Float -> Image -> Image
- Graphics.Data.Image: Image :: Float -> Float -> [(Picture, Point)] -> Image
- Graphics.Data.Image: [height] :: Image -> Float
- Graphics.Data.Image: [shapes] :: Image -> [(Picture, Point)]
- Graphics.Data.Image: [width] :: Image -> Float
- Graphics.Data.Image: data Image
- Graphics.Data.Image: instance GHC.Classes.Eq Graphics.Data.Image.Image
- Graphics.Data.Image: rotate :: Float -> Image -> Image
- Graphics.Shape: addLine :: Image -> Float -> Float -> Float -> Float -> Color -> Image
- Graphics.Shape: circle :: Float -> Mode -> Color -> Image
- Graphics.Shape: data Mode
- Graphics.Shape: ellipse :: Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: emptyImage :: Image
- Graphics.Shape: instance GHC.Classes.Eq Graphics.Shape.Mode
- Graphics.Shape: isoscelesTriangle :: Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: line :: Float -> Float -> Color -> Image
- Graphics.Shape: outline :: Mode
- Graphics.Shape: rectangle :: Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: rhombus :: Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: rightTriangle :: Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: solid :: Mode
- Graphics.Shape: square :: Float -> Mode -> Color -> Image
- Graphics.Shape: star :: Float -> Mode -> Color -> Image
- Graphics.Shape: triangle :: Float -> Mode -> Color -> Image
- Graphics.Shape: triangleAAS :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleASA :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleASS :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleSAA :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleSAS :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleSSA :: Float -> Float -> Float -> Mode -> Color -> Image
- Graphics.Shape: triangleSSS :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Combinator: above :: Image -> Image -> Image
+ Graphics.Htdp.Combinator: aboveAlign :: Alignment -> Image -> Image -> Image
+ Graphics.Htdp.Combinator: aboves :: [Image] -> Image
+ Graphics.Htdp.Combinator: abovesAlign :: Alignment -> [Image] -> Image
+ Graphics.Htdp.Combinator: beside :: Image -> Image -> Image
+ Graphics.Htdp.Combinator: besideAlign :: Alignment -> Image -> Image -> Image
+ Graphics.Htdp.Combinator: besides :: [Image] -> Image
+ Graphics.Htdp.Combinator: besidesAlign :: Alignment -> [Image] -> Image
+ Graphics.Htdp.Combinator: data Alignment
+ Graphics.Htdp.Combinator: high :: Alignment
+ Graphics.Htdp.Combinator: low :: Alignment
+ Graphics.Htdp.Combinator: mid :: Alignment
+ Graphics.Htdp.Combinator: overlay :: Image -> Image -> Image
+ Graphics.Htdp.Combinator: overlayAlign :: Alignment -> Alignment -> Image -> Image -> Image
+ Graphics.Htdp.Combinator: overlayAlignOffset :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: overlayOffset :: Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: overlayXY :: Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: placeImage :: Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: placeImageAlign :: Image -> Float -> Float -> Alignment -> Alignment -> Image -> Image
+ Graphics.Htdp.Combinator: placeImages :: [Image] -> [(Float, Float)] -> Image -> Image
+ Graphics.Htdp.Combinator: placeImagesAlign :: [Image] -> [(Float, Float)] -> Alignment -> Alignment -> Image -> Image
+ Graphics.Htdp.Combinator: underlay :: Image -> Image -> Image
+ Graphics.Htdp.Combinator: underlayAlign :: Alignment -> Alignment -> Image -> Image -> Image
+ Graphics.Htdp.Combinator: underlayAlignOffset :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: underlayOffset :: Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Combinator: underlayXY :: Image -> Float -> Float -> Image -> Image
+ Graphics.Htdp.Data.Image: Image :: Float -> Float -> [(Picture, Point)] -> Image
+ Graphics.Htdp.Data.Image: [height] :: Image -> Float
+ Graphics.Htdp.Data.Image: [shapes] :: Image -> [(Picture, Point)]
+ Graphics.Htdp.Data.Image: [width] :: Image -> Float
+ Graphics.Htdp.Data.Image: data Image
+ Graphics.Htdp.Data.Image: instance GHC.Classes.Eq Graphics.Htdp.Data.Image.Image
+ Graphics.Htdp.Data.Image: rotate :: Float -> Image -> Image
+ Graphics.Htdp.Shape: addLine :: Image -> Float -> Float -> Float -> Float -> Color -> Image
+ Graphics.Htdp.Shape: circle :: Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: data Mode
+ Graphics.Htdp.Shape: ellipse :: Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: emptyImage :: Image
+ Graphics.Htdp.Shape: instance GHC.Classes.Eq Graphics.Htdp.Shape.Mode
+ Graphics.Htdp.Shape: isoscelesTriangle :: Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: line :: Float -> Float -> Color -> Image
+ Graphics.Htdp.Shape: outline :: Mode
+ Graphics.Htdp.Shape: rectangle :: Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: rhombus :: Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: rightTriangle :: Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: solid :: Mode
+ Graphics.Htdp.Shape: square :: Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: star :: Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangle :: Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleAAS :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleASA :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleASS :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleSAA :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleSAS :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleSSA :: Float -> Float -> Float -> Mode -> Color -> Image
+ Graphics.Htdp.Shape: triangleSSS :: Float -> Float -> Float -> Mode -> Color -> Image

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for htdp-image +## 1.1.0.0 -- 2019-07-25++* Add "Htdp" to submodules++ ## 1.0.0.0 -- 2019-07-21  * Remove emptyScene.
htdp-image.cabal view
@@ -1,5 +1,5 @@ name:                  htdp-image-version:               1.0.0.0+version:               1.1.0.0 license:               BSD3 license-file:          LICENSE author:                Turab Jafri@@ -25,10 +25,10 @@  library   exposed-modules:     Graphics.Htdp,-                       Graphics.Data.Image,-                       Graphics.Shape,-                       Graphics.Combinator-  other-modules:       Graphics.Util.Arithmetic+                       Graphics.Htdp.Combinator,+                       Graphics.Htdp.Data.Image,+                       Graphics.Htdp.Shape+  other-modules:       Graphics.Htdp.Util.Arithmetic   build-depends:       AC-Angle ^>=1.0,                        base ^>=4.12,                        gloss ^>=1.13.0
− src/Graphics/Combinator.hs
@@ -1,315 +0,0 @@-{-# LANGUAGE MultiWayIf, RecordWildCards #-}---- | Utility to combine images.--module Graphics.Combinator-  ( Alignment-  , high-  , low-  , mid-  , overlay-  , overlayAlign-  , overlayOffset-  , overlayAlignOffset-  , overlayXY-  , underlay-  , underlayAlign-  , underlayOffset-  , underlayXY-  , underlayAlignOffset-  , beside-  , besides-  , besideAlign-  , besidesAlign-  , above-  , aboves-  , aboveAlign-  , abovesAlign-  , placeImage-  , placeImages-  , placeImageAlign-  , placeImagesAlign-  )-where--import           Graphics.Data.Image-import           Graphics.Util.Arithmetic-import           Prelude                 hiding ( Left-                                                , Right-                                                )---- | Alignment position-data Alignment = Low | Mid | High---- | Position to align at.---   On x-axis, low means left, high means right.---   On y-axis, low means bottom, high means top.-low, mid, high :: Alignment-low = Low-mid = Mid-high = High---- Function to determine the alignment shift in above/beside-imageOffset :: (Image -> Float) -> Alignment -> Image -> Image -> Float-imageOffset dim al i1 i2 = case al of-  Low  -> lowAlign-  Mid  -> 0-  High -> negate lowAlign-  where lowAlign = (dim i2 - dim i1) / 2---- | Constructs an image by placing @i1@ on top of @i2@, aligned along---   the center.-above-  :: Image -- ^ @i1@-  -> Image -- ^ @i2@-  -> Image-above = aboveAlign mid---- | Constructs an image by placing all images in a vertical row, aligned---   along the center such that the first image in @is@ is at the top.-aboves-  :: [Image] -- ^ @is@-  -> Image-aboves = foldr1 above---- | Constructs an image by placing @i1@ on top of @i2@, aligned as---   specified by @al@.-aboveAlign-  :: Alignment -- ^ @al@-  -> Image     -- ^ @i1@-  -> Image     -- ^ @i2@-  -> Image-aboveAlign a i1 i2 = placeImage i2-                                (offset + width i1 / 2)-                                (height i1 + height i2 / 2)-                                i1-  where offset = imageOffset width a i1 i2---- | Constructs an image by placing all images in a veritcal row, aligned---   as specified by @al@ such that the first image in @is@ is at the top.-abovesAlign-  :: Alignment -- ^ @al@-  -> [Image]   -- ^ @is@-  -> Image-abovesAlign a = foldr1 $ aboveAlign a---- | Constructs an image by placing @i1@ on the left of @i2@, aligned along---   the center.-beside-  :: Image -- ^ @i1@-  -> Image -- ^ @i2@-  -> Image-beside = besideAlign mid---- | Constructs an image by placing all images in a horizontal row, aligned---   along the center such that the first image in @is@ is on the left.-besides-  :: [Image] -- ^ @is@-  -> Image-besides = foldr1 beside---- | Constructs an image by placing @i1@ on the left of @i2@, aligned as---   specified by @al@.-besideAlign-  :: Alignment -- ^ @al@-  -> Image     -- ^ @i1@-  -> Image     -- ^ @i2@-  -> Image-besideAlign a i1 i2 = placeImage i2-                                 (width i1 + width i2 / 2)-                                 (negate offset + height i1 / 2)-                                 i1-  where offset = imageOffset height a i1 i2---- | Constructs an image by placing all images in a horizontal row, aligned---   as specified by @al@ such that the first image in @is@ is on the left.-besidesAlign-  :: Alignment -- ^ @al@-  -> [Image]   -- ^ @is@-  -> Image-besidesAlign a = foldr1 $ besideAlign a---- | Places @i1@ on top of @i2@ with @i1@'s center at position @(x, y)@.---   Unlike 2htdp/image's place-image, placeImage increases the binding box---   so that both images fit in it, instead of cropping parts of @i1@ that---   lay outside of @i2@'s bounds.-placeImage-  :: Image -- ^ @i1@-  -> Float -- ^ @x@-  -> Float -- ^ @y@-  -> Image -- ^ @i2@-  -> Image-placeImage i1 x y = placeImageAlign i1 x y mid mid---- | Places each @i@ in @is@ onto @i2@ using `placeImage`, using the coordinates---   @(x, y)@ in @ps@.-placeImages-  :: [Image]          -- ^ @is@-  -> [(Float, Float)] -- ^ @ps@-  -> Image            -- ^ @i2@-  -> Image-placeImages is ps base =-  foldr (\(i1, (x, y)) i2 -> placeImage i1 x y i2) base $ zip is ps---- | Like `placeImage`, but anchors @i1@ on @i2@ by the alignment specified---   by @xAl@ and @yAl@.-placeImageAlign-  :: Image     -- ^ @i1@-  -> Float     -- ^ @x@-  -> Float     -- ^ @y@-  -> Alignment -- ^ @xAl@-  -> Alignment -- ^ @yAL@-  -> Image     -- ^ @i2@-  -> Image-placeImageAlign i1 x y xAl yAl i2 = Image { width  = newW-                                          , height = newH-                                          , shapes = newShapes-                                          }- where-  -- width and height of new image-  newW = max (width i2) $ max (width i1) $ if incWCase-    then (width i1 / 2) + (abs newX) + (width i2 / 2)-    else 0-  newH = max (height i2) $ max (height i1) $ if incHCase-    then (height i1 / 2) + (abs newY) + (height i2 / 2)-    else 0-  -- whether i1's width/height lay outside of i2's width/height-  incWCase = (abs newX) + (width i1 / 2) > (width i2 / 2)-  incHCase = (abs newY) + (height i1 / 2) > (height i2 / 2)-  -- x/y position converted from screen coord to cartesian coord-  newX =-    convert 0 (negate $ width i2 / 2) (width i2) (width i2 / 2) x + xOffset-  newY =-    convert 0 (height i2 / 2) (height i2) (negate $ height i2 / 2) y + yOffset-  --x/y offset based on the given alignment-  xOffset = shiftImage width xAl-  yOffset = shiftImage height yAl-  shiftImage dim a = case a of-    Low  -> dim i1 / 2-    Mid  -> 0-    High -> negate $ dim i1 / 2-  -- if i1 covers i2, don't move i1 at all, but shift i2-  -- if i1 is within i2, don't move i2 at all, but shift i1-  -- else, move both-  newShapes-    | newW == width i1 && newH == height i1-    = [ (p, (ox - newX, oy - newY)) | (p, (ox, oy)) <- shapes i2 ] ++ shapes i1-    | newW == width i2 && newH == height i2-    = shapes i2 ++ [ (p, (ox + newX, oy + newY)) | (p, (ox, oy)) <- shapes i1 ]-    | otherwise-    = [ (p, (x2 + x2Shift, y2 + y2Shift)) | (p, (x2, y2)) <- shapes i2 ]-      ++ [ (p, (x1 + x1Shift, y1 + y1Shift)) | (p, (x1, y1)) <- shapes i1 ]-  -- shift magnitudes-  x2Shift = xDir * ((newW - width i2) / 2)-  y2Shift = yDir * ((newH - height i2) / 2)-  x1Shift = (negate xDir) * ((newW - width i1) / 2)-  y1Shift = (negate yDir) * ((newH - height i1) / 2)-  -- direction based on i1's location relative to i2-  xDir    = if-    | newX > 0  -> -1-    | newX < 0  -> 1-    | otherwise -> 0-  yDir = if-    | newY > 0  -> -1-    | newY < 0  -> 1-    | otherwise -> 0----- | Like `placeImages`, but anchors @is@ on @i2@ by the alignment specified---   by @xAl@ and @yAl@.-placeImagesAlign-  :: [Image]          -- ^ @is@-  -> [(Float, Float)] -- ^ @ps@-  -> Alignment        -- ^ @xAl@-  -> Alignment        -- ^ @yAl@-  -> Image            -- ^ @i2@-  -> Image-placeImagesAlign is ps xAl yAl b =-  foldr (\(i1, (x, y)) i2 -> placeImageAlign i1 x y xAl yAl i2) b $ zip is ps---- | Places @i1@ on the center of @i2@.-overlay-  :: Image -- ^ @i1@-  -> Image -- ^ @i2@-  -> Image-overlay i1 i2 = placeImage i1 (width i2 / 2) (height i2 / 2) i2---- | Places @i1@ on top of @i2@ and uses @xAl@ and @yAl@ for alignment.-overlayAlign-  :: Alignment -- ^ @xAl@-  -> Alignment -- ^ @yal@-  -> Image     -- ^ @i1@-  -> Image     -- ^ @i2@-  -> Image-overlayAlign xAl yAl i1 = overlayAlignOffset xAl yAl i1 0 0---- | Places @i1@ on top of @i2@ and moves @i2@ by @x@ pixels to the right,---   and @y@ pixels down.-overlayOffset-  :: Image -- ^ @i1@-  -> Float -- ^ @x@-  -> Float -- ^ @y@-  -> Image -- ^ @i2@-  -> Image-overlayOffset = overlayAlignOffset mid mid---- | Places @i1@ on top of @i2@ by lining them on their top left corners,---   then @i2@ is shifted to the right by @x@ pixels and down by @y@ pixels.-overlayXY-  :: Image -- ^ @i1@-  -> Float -- ^ @x@-  -> Float -- ^ @y@-  -> Image -- ^ @i2@-  -> Image-overlayXY i1 x y i2 = placeImage i1 (width i1 / 2 - x) (height i1 / 2 - y) i2---- | Combination of `overlayAlign` and `overlayOffset`.-overlayAlignOffset-  :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image-overlayAlignOffset xAl yAl i1 x y i2 = placeImageAlign i1-                                                       shiftedX-                                                       shiftedY-                                                       mid-                                                       mid-                                                       i2- where-  newX  = (width i2 / 2 - x)-  newY  = (height i2 / 2 - y)-  wDiff = (width i2 - width i1) / 2-  hDiff = (height i2 - height i1) / 2-  shiftedX =-    newX-      + (case xAl of-          Low  -> (-wDiff)-          Mid  -> 0-          High -> wDiff-        )-  shiftedY =-    newY-      + (case yAl of-          Low  -> hDiff-          Mid  -> 0-          High -> (-hDiff)-        )---- | Same of `overlay`, but with image arguments flipped.-underlay :: Image -> Image -> Image-underlay = flip overlay---- | Same of `overlayAlign`, but with image arguments flipped.-underlayAlign :: Alignment -> Alignment -> Image -> Image -> Image-underlayAlign xAl yAl i1 i2 = overlayAlign xAl yAl i2 i1---- | Same of `overlayOffset`, but with image arguments flipped.-underlayOffset :: Image -> Float -> Float -> Image -> Image-underlayOffset i1 x y i2 = overlayOffset i2 (negate x) (negate y) i1---- | Same of `overlayAlignOffset`, but with image arguments flipped.-underlayAlignOffset-  :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image-underlayAlignOffset xAl yAl i1 x y i2 =-  overlayAlignOffset xAl yAl i2 (negate x) (negate y) i1---- | Same of `overlayXY`, but with image arguments flipped.-underlayXY :: Image -> Float -> Float -> Image -> Image-underlayXY i1 x y i2 = overlayXY i2 (negate x) (negate y) i1
− src/Graphics/Data/Image.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | Image related utilities.-module Graphics.Data.Image-  ( Image(..)-  , rotate-  )-where--import           Data.Angle-import qualified Graphics.Gloss                as G---- | A 2D Image.-data Image = Image {width :: Float ,                 -- ^ Width of image-                    height :: Float ,                -- ^ Height of image-                    shapes :: [(G.Picture, G.Point)] -- ^ Collection of all Gloss Pictures that create the Image.-                   } deriving Eq---- | Rotates @i@ by @deg@ degrees in a counter-clockwise direction.---   Unlike 2htdp/image's rotate function, this function is not smart enough---   to reduce the rotated image's binding box to fit the actual image.---   Instead, it just creates a new binding box so that @i@'s binding box---   fits in it.-rotate-  :: Float -- ^ @deg@-  -> Image -- ^ @i@-  -> Image-rotate deg Image {..} = Image newW newH-  $ map (\(p, c) -> (G.rotate (negate deg) p, rotateC c)) shapes- where-  newW =-    width-      * (abs . sine . Degrees $ 90 - deg)-      + height-      * (abs . sine . Degrees $ deg)-  newH =-    width-      * (abs . sine . Degrees $ deg)-      + height-      * (abs . sine . Degrees $ 90 - deg)-  rotateC :: (Float, Float) -> (Float, Float)-  rotateC (x, y) =-    ( x * (cosine . Degrees $ deg) + y * (negate . sine . Degrees $ deg)-    , x * (sine . Degrees $ deg) + y * (cosine . Degrees $ deg)-    )-
src/Graphics/Htdp.hs view
@@ -17,9 +17,9 @@ module Graphics.Htdp   ( module Graphics.Gloss.Data.Color     -- * Image constructors-  , module Graphics.Shape+  , module Graphics.Htdp.Shape     -- * Image combinators-  , module Graphics.Combinator+  , module Graphics.Htdp.Combinator     -- * Image   , Image   , width@@ -32,9 +32,9 @@  import qualified Graphics.Gloss                as G import           Graphics.Gloss.Data.Color-import           Graphics.Combinator-import           Graphics.Data.Image-import           Graphics.Shape+import           Graphics.Htdp.Combinator+import           Graphics.Htdp.Data.Image+import           Graphics.Htdp.Shape  -- | Function to draw an image in a new window with same dimensions as the given image. drawImage :: Image -> IO ()
+ src/Graphics/Htdp/Combinator.hs view
@@ -0,0 +1,315 @@+{-# LANGUAGE MultiWayIf, RecordWildCards #-}++-- | Utility to combine images.++module Graphics.Htdp.Combinator+  ( Alignment+  , high+  , low+  , mid+  , overlay+  , overlayAlign+  , overlayOffset+  , overlayAlignOffset+  , overlayXY+  , underlay+  , underlayAlign+  , underlayOffset+  , underlayXY+  , underlayAlignOffset+  , beside+  , besides+  , besideAlign+  , besidesAlign+  , above+  , aboves+  , aboveAlign+  , abovesAlign+  , placeImage+  , placeImages+  , placeImageAlign+  , placeImagesAlign+  )+where++import           Graphics.Htdp.Data.Image+import           Graphics.Htdp.Util.Arithmetic+import           Prelude                 hiding ( Left+                                                , Right+                                                )++-- | Alignment position+data Alignment = Low | Mid | High++-- | Position to align at.+--   On x-axis, low means left, high means right.+--   On y-axis, low means bottom, high means top.+low, mid, high :: Alignment+low = Low+mid = Mid+high = High++-- Function to determine the alignment shift in above/beside+imageOffset :: (Image -> Float) -> Alignment -> Image -> Image -> Float+imageOffset dim al i1 i2 = case al of+  Low  -> lowAlign+  Mid  -> 0+  High -> negate lowAlign+  where lowAlign = (dim i2 - dim i1) / 2++-- | Constructs an image by placing @i1@ on top of @i2@, aligned along+--   the center.+above+  :: Image -- ^ @i1@+  -> Image -- ^ @i2@+  -> Image+above = aboveAlign mid++-- | Constructs an image by placing all images in a vertical row, aligned+--   along the center such that the first image in @is@ is at the top.+aboves+  :: [Image] -- ^ @is@+  -> Image+aboves = foldr1 above++-- | Constructs an image by placing @i1@ on top of @i2@, aligned as+--   specified by @al@.+aboveAlign+  :: Alignment -- ^ @al@+  -> Image     -- ^ @i1@+  -> Image     -- ^ @i2@+  -> Image+aboveAlign a i1 i2 = placeImage i2+                                (offset + width i1 / 2)+                                (height i1 + height i2 / 2)+                                i1+  where offset = imageOffset width a i1 i2++-- | Constructs an image by placing all images in a veritcal row, aligned+--   as specified by @al@ such that the first image in @is@ is at the top.+abovesAlign+  :: Alignment -- ^ @al@+  -> [Image]   -- ^ @is@+  -> Image+abovesAlign a = foldr1 $ aboveAlign a++-- | Constructs an image by placing @i1@ on the left of @i2@, aligned along+--   the center.+beside+  :: Image -- ^ @i1@+  -> Image -- ^ @i2@+  -> Image+beside = besideAlign mid++-- | Constructs an image by placing all images in a horizontal row, aligned+--   along the center such that the first image in @is@ is on the left.+besides+  :: [Image] -- ^ @is@+  -> Image+besides = foldr1 beside++-- | Constructs an image by placing @i1@ on the left of @i2@, aligned as+--   specified by @al@.+besideAlign+  :: Alignment -- ^ @al@+  -> Image     -- ^ @i1@+  -> Image     -- ^ @i2@+  -> Image+besideAlign a i1 i2 = placeImage i2+                                 (width i1 + width i2 / 2)+                                 (negate offset + height i1 / 2)+                                 i1+  where offset = imageOffset height a i1 i2++-- | Constructs an image by placing all images in a horizontal row, aligned+--   as specified by @al@ such that the first image in @is@ is on the left.+besidesAlign+  :: Alignment -- ^ @al@+  -> [Image]   -- ^ @is@+  -> Image+besidesAlign a = foldr1 $ besideAlign a++-- | Places @i1@ on top of @i2@ with @i1@'s center at position @(x, y)@.+--   Unlike 2htdp/image's place-image, placeImage increases the binding box+--   so that both images fit in it, instead of cropping parts of @i1@ that+--   lay outside of @i2@'s bounds.+placeImage+  :: Image -- ^ @i1@+  -> Float -- ^ @x@+  -> Float -- ^ @y@+  -> Image -- ^ @i2@+  -> Image+placeImage i1 x y = placeImageAlign i1 x y mid mid++-- | Places each @i@ in @is@ onto @i2@ using `placeImage`, using the coordinates+--   @(x, y)@ in @ps@.+placeImages+  :: [Image]          -- ^ @is@+  -> [(Float, Float)] -- ^ @ps@+  -> Image            -- ^ @i2@+  -> Image+placeImages is ps base =+  foldr (\(i1, (x, y)) i2 -> placeImage i1 x y i2) base $ zip is ps++-- | Like `placeImage`, but anchors @i1@ on @i2@ by the alignment specified+--   by @xAl@ and @yAl@.+placeImageAlign+  :: Image     -- ^ @i1@+  -> Float     -- ^ @x@+  -> Float     -- ^ @y@+  -> Alignment -- ^ @xAl@+  -> Alignment -- ^ @yAL@+  -> Image     -- ^ @i2@+  -> Image+placeImageAlign i1 x y xAl yAl i2 = Image { width  = newW+                                          , height = newH+                                          , shapes = newShapes+                                          }+ where+  -- width and height of new image+  newW = max (width i2) $ max (width i1) $ if incWCase+    then (width i1 / 2) + (abs newX) + (width i2 / 2)+    else 0+  newH = max (height i2) $ max (height i1) $ if incHCase+    then (height i1 / 2) + (abs newY) + (height i2 / 2)+    else 0+  -- whether i1's width/height lay outside of i2's width/height+  incWCase = (abs newX) + (width i1 / 2) > (width i2 / 2)+  incHCase = (abs newY) + (height i1 / 2) > (height i2 / 2)+  -- x/y position converted from screen coord to cartesian coord+  newX =+    convert 0 (negate $ width i2 / 2) (width i2) (width i2 / 2) x + xOffset+  newY =+    convert 0 (height i2 / 2) (height i2) (negate $ height i2 / 2) y + yOffset+  --x/y offset based on the given alignment+  xOffset = shiftImage width xAl+  yOffset = shiftImage height yAl+  shiftImage dim a = case a of+    Low  -> dim i1 / 2+    Mid  -> 0+    High -> negate $ dim i1 / 2+  -- if i1 covers i2, don't move i1 at all, but shift i2+  -- if i1 is within i2, don't move i2 at all, but shift i1+  -- else, move both+  newShapes+    | newW == width i1 && newH == height i1+    = [ (p, (ox - newX, oy - newY)) | (p, (ox, oy)) <- shapes i2 ] ++ shapes i1+    | newW == width i2 && newH == height i2+    = shapes i2 ++ [ (p, (ox + newX, oy + newY)) | (p, (ox, oy)) <- shapes i1 ]+    | otherwise+    = [ (p, (x2 + x2Shift, y2 + y2Shift)) | (p, (x2, y2)) <- shapes i2 ]+      ++ [ (p, (x1 + x1Shift, y1 + y1Shift)) | (p, (x1, y1)) <- shapes i1 ]+  -- shift magnitudes+  x2Shift = xDir * ((newW - width i2) / 2)+  y2Shift = yDir * ((newH - height i2) / 2)+  x1Shift = (negate xDir) * ((newW - width i1) / 2)+  y1Shift = (negate yDir) * ((newH - height i1) / 2)+  -- direction based on i1's location relative to i2+  xDir    = if+    | newX > 0  -> -1+    | newX < 0  -> 1+    | otherwise -> 0+  yDir = if+    | newY > 0  -> -1+    | newY < 0  -> 1+    | otherwise -> 0+++-- | Like `placeImages`, but anchors @is@ on @i2@ by the alignment specified+--   by @xAl@ and @yAl@.+placeImagesAlign+  :: [Image]          -- ^ @is@+  -> [(Float, Float)] -- ^ @ps@+  -> Alignment        -- ^ @xAl@+  -> Alignment        -- ^ @yAl@+  -> Image            -- ^ @i2@+  -> Image+placeImagesAlign is ps xAl yAl b =+  foldr (\(i1, (x, y)) i2 -> placeImageAlign i1 x y xAl yAl i2) b $ zip is ps++-- | Places @i1@ on the center of @i2@.+overlay+  :: Image -- ^ @i1@+  -> Image -- ^ @i2@+  -> Image+overlay i1 i2 = placeImage i1 (width i2 / 2) (height i2 / 2) i2++-- | Places @i1@ on top of @i2@ and uses @xAl@ and @yAl@ for alignment.+overlayAlign+  :: Alignment -- ^ @xAl@+  -> Alignment -- ^ @yal@+  -> Image     -- ^ @i1@+  -> Image     -- ^ @i2@+  -> Image+overlayAlign xAl yAl i1 = overlayAlignOffset xAl yAl i1 0 0++-- | Places @i1@ on top of @i2@ and moves @i2@ by @x@ pixels to the right,+--   and @y@ pixels down.+overlayOffset+  :: Image -- ^ @i1@+  -> Float -- ^ @x@+  -> Float -- ^ @y@+  -> Image -- ^ @i2@+  -> Image+overlayOffset = overlayAlignOffset mid mid++-- | Places @i1@ on top of @i2@ by lining them on their top left corners,+--   then @i2@ is shifted to the right by @x@ pixels and down by @y@ pixels.+overlayXY+  :: Image -- ^ @i1@+  -> Float -- ^ @x@+  -> Float -- ^ @y@+  -> Image -- ^ @i2@+  -> Image+overlayXY i1 x y i2 = placeImage i1 (width i1 / 2 - x) (height i1 / 2 - y) i2++-- | Combination of `overlayAlign` and `overlayOffset`.+overlayAlignOffset+  :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image+overlayAlignOffset xAl yAl i1 x y i2 = placeImageAlign i1+                                                       shiftedX+                                                       shiftedY+                                                       mid+                                                       mid+                                                       i2+ where+  newX  = (width i2 / 2 - x)+  newY  = (height i2 / 2 - y)+  wDiff = (width i2 - width i1) / 2+  hDiff = (height i2 - height i1) / 2+  shiftedX =+    newX+      + (case xAl of+          Low  -> (-wDiff)+          Mid  -> 0+          High -> wDiff+        )+  shiftedY =+    newY+      + (case yAl of+          Low  -> hDiff+          Mid  -> 0+          High -> (-hDiff)+        )++-- | Same of `overlay`, but with image arguments flipped.+underlay :: Image -> Image -> Image+underlay = flip overlay++-- | Same of `overlayAlign`, but with image arguments flipped.+underlayAlign :: Alignment -> Alignment -> Image -> Image -> Image+underlayAlign xAl yAl i1 i2 = overlayAlign xAl yAl i2 i1++-- | Same of `overlayOffset`, but with image arguments flipped.+underlayOffset :: Image -> Float -> Float -> Image -> Image+underlayOffset i1 x y i2 = overlayOffset i2 (negate x) (negate y) i1++-- | Same of `overlayAlignOffset`, but with image arguments flipped.+underlayAlignOffset+  :: Alignment -> Alignment -> Image -> Float -> Float -> Image -> Image+underlayAlignOffset xAl yAl i1 x y i2 =+  overlayAlignOffset xAl yAl i2 (negate x) (negate y) i1++-- | Same of `overlayXY`, but with image arguments flipped.+underlayXY :: Image -> Float -> Float -> Image -> Image+underlayXY i1 x y i2 = overlayXY i2 (negate x) (negate y) i1
+ src/Graphics/Htdp/Data/Image.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE RecordWildCards #-}++-- | Image related utilities.+module Graphics.Htdp.Data.Image+  ( Image(..)+  , rotate+  )+where++import           Data.Angle+import qualified Graphics.Gloss                as G++-- | A 2D Image.+data Image = Image {width :: Float ,                 -- ^ Width of image+                    height :: Float ,                -- ^ Height of image+                    shapes :: [(G.Picture, G.Point)] -- ^ Collection of all Gloss Pictures that create the Image.+                   } deriving Eq++-- | Rotates @i@ by @deg@ degrees in a counter-clockwise direction.+--   Unlike 2htdp/image's rotate function, this function is not smart enough+--   to reduce the rotated image's binding box to fit the actual image.+--   Instead, it just creates a new binding box so that @i@'s binding box+--   fits in it.+rotate+  :: Float -- ^ @deg@+  -> Image -- ^ @i@+  -> Image+rotate deg Image {..} = Image newW newH+  $ map (\(p, c) -> (G.rotate (negate deg) p, rotateC c)) shapes+ where+  newW =+    width+      * (abs . sine . Degrees $ 90 - deg)+      + height+      * (abs . sine . Degrees $ deg)+  newH =+    width+      * (abs . sine . Degrees $ deg)+      + height+      * (abs . sine . Degrees $ 90 - deg)+  rotateC :: (Float, Float) -> (Float, Float)+  rotateC (x, y) =+    ( x * (cosine . Degrees $ deg) + y * (negate . sine . Degrees $ deg)+    , x * (sine . Degrees $ deg) + y * (cosine . Degrees $ deg)+    )+
+ src/Graphics/Htdp/Shape.hs view
@@ -0,0 +1,409 @@+{-# LANGUAGE MultiWayIf #-}++-- Sorted as they are in 2htdp/image+-- | Image constructors+module Graphics.Htdp.Shape+  ( Mode+  , solid+  , outline+  , circle+  , ellipse+  , line+  , addLine+  , emptyImage+  , triangle+  , rightTriangle+  , isoscelesTriangle+  -- *** The following image from 2htdp/image documentation is useful for the following family of functions.+  -- |   <<diagrams/triangleDiagram.png>>+  , triangleSSS+  , triangleASS+  , triangleSAS+  , triangleSSA+  , triangleAAS+  , triangleASA+  , triangleSAA+  , square+  , rectangle+  , rhombus+  , star+  )+where++import           Data.Angle+import           Data.Fixed+import           Data.List+import           Graphics.Htdp.Combinator+import           Graphics.Htdp.Data.Image+import qualified Graphics.Gloss                as G+import           Graphics.Gloss.Data.Color+import           Graphics.Htdp.Util.Arithmetic++-- | Drawing mode.+data Mode = Solid | Outline deriving Eq++-- | Type of drawing mode.+solid, outline :: Mode+solid = Solid+outline = Outline++-- Initial point for all images+origin :: G.Point+origin = (0, 0)++-- | Adds a line to the given image @i@ of color @c@, starting from point @(x1, y1)@+--   and going to point @(x2, y2)@. If the line crosses the given image's binding box,+--   then new image dimesions are changed to accommodate the line.+addLine+  :: Image -- ^ @i@+  -> Float -- ^ @x1@+  -> Float -- ^ @y1@+  -> Float -- ^ @x2@+  -> Float -- ^ @y2@+  -> Color+  -> Image+addLine i x1 y1 x2 y2 c =+  placeImage (line (x1 - x2) (y1 - y2) c) ((x1 + x2) / 2) ((y1 + y2) / 2) i++-- | Constructs a circle of radius @r@, drawing mode @m@ and color @c@.+circle+  :: Float -- ^ @r@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+circle r mode c = Image { width  = r * 2+                        , height = r * 2+                        , shapes = [(G.color c $ circleKind r, origin)]+                        }+ where+  circleKind = case mode of+    Solid   -> G.circleSolid+    Outline -> G.circle++-- | Constructs an ellipse of width @w@, height @h@, mode @m@, and color @c@.+ellipse+  :: Float -- ^ @w@+  -> Float -- ^ @h@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+ellipse w h m c = Image { width  = w+                        , height = h+                        , shapes = [(circleToEllipse, origin)]+                        }+ where -- This took me longer than it should have+  circleToEllipse = G.scale (w / (2 * radius)) (h / (2 * radius)) circPic+  circPic         = fst . head . shapes $ circle radius m c+  radius          = (w + h) / 4++-- | Constructs an image of width and height @0@.+emptyImage :: Image+emptyImage = Image 0 0 []++-- | Constructs a triangle of two equal-length sides, of length @l@, where the+--   angle between those sides is @a@, mode is @m@ and color is @c@. If the angle+--   is less than @180@, then the triangle will point up, else it will point down.+isoscelesTriangle+  :: Float -- ^ @l@+  -> Float -- ^ @a@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+isoscelesTriangle sl deg m c = Image+  { width  = newW+  , height = newH+  , shapes = [(G.color c triangleShape, origin)]+  }+ where+  newW   = (2 * (sl ** 2) * (1 - (cosine . Degrees $ deg))) ** (1 / 2)+  newH   = computeRightSide sl (newW / 2)+  topDir = if mod' deg 360 < 180 then 1 else -1+  tShape =+    [ (negate newW / 2, negate topDir * (newH / 2))+    , (0              , topDir * newH / 2)+    , (newW / 2       , negate topDir * (newH / 2))+    ]+  triangleShape = case m of+    Solid   -> G.polygon tShape+    Outline -> G.line ((newW / 2, negate topDir * (newH / 2)) : tShape)++-- | Constructs an image of a line segment of color @c@ that connects the points+--   @(0,0)@ to @(x1, y1)@.+line+  :: Float -- ^ @x1@+  -> Float -- ^ @y1@+  -> Color -- ^ @c@+  -> Image+line x y c = Image { width  = abs x+                   , height = abs y+                   , shapes = [(G.color c $ G.Line lineShape, origin)]+                   }+  -- We want the line centered on the origin.+  -- For that, we will need to move the given points+  -- (I had to take a day off to get this)+  where lineShape = [(x / 2, negate y / 2), (negate x / 2, y / 2)]++-- | Constructs a rectangle of width @w@, height @h@, mode @m@, and color @c@.+rectangle+  :: Float -- ^ @w@+  -> Float -- ^ @h@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+rectangle w h mode c = Image { width  = w+                             , height = h+                             , shapes = [(G.color c $ rectShape w h, origin)]+                             }+ where+  rectShape = case mode of+    Solid   -> G.rectangleSolid+    Outline -> G.rectangleWire++-- | Constructs a four sided polygon with all equal sides of length @l@, where the+--   top and bottom pair of angles is @a@, and the left and right are @180 - a@.+--   As usual, mode is @m@ and color is @c@.+rhombus+  :: Float -- ^ @l@+  -> Float -- ^ @a@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+rhombus sideLength angle m c = Image { width  = base+                                     , height = opp+                                     , shapes = [(G.color c rShape, origin)]+                                     }+ where+  -- It's the law of Cosine bb+  base = (2 * (sideLength ** 2) * (1 - (cosine . Degrees $ angle))) ** (1 / 2)+  opp  = 2 * computeRightSide sideLength (base / 2)+  rhombusShape =+    [(negate base / 2, 0), (0, opp / 2), (base / 2, 0), (0, negate opp / 2)]+  rShape = case m of+    Solid   -> G.polygon rhombusShape+    Outline -> G.line ((0, negate opp / 2) : rhombusShape)++-- | Constructs a right triangle with base length @b@, perpendicular length+--   @p@, mode @m@, and color @c@.+rightTriangle+  :: Float -- ^ @b@+  -> Float -- ^ @p@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+rightTriangle b p m c = Image { width  = b+                              , height = p+                              , shapes = [(G.color c triangleShape, origin)]+                              }+ where+  tShape =+    [(b / 2, negate p / 2), (negate b / 2, p / 2), (negate b / 2, negate p / 2)]+  triangleShape = case m of+    Solid   -> G.polygon tShape+    Outline -> G.line ((negate b / 2, negate p / 2) : tShape)++-- | Constructs a square of side @s@, mode @m@, and color @c@.+square+  :: Float -- ^ @s@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+square w = rectangle w w++-- | Constructs a star with five points of mode @m@ and color @c@. The argument+--   @l@ determines the side length of the internal pentagon.+--   Currently, a solid star is glitchy since it is a non-convex polygon+--   and openGL (the underlying graphics library) doesn't draw them correctly.+--   This will be corrected in future versions.+star+  :: Float -- ^ @l@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+star side m c = Image { width  = w+                      , height = h+                      , shapes = [(G.color c sShape, origin)]+                      }+ where+    -- Pentagon is 108degs apart+  w          = (2 * triHyp) + side+  h          = (1.539 * side) + triPerp + bottomPerp+  triHyp     = (sine . Degrees $ 72) * (side / (sine . Degrees $ 36))+  triPerp    = computeRightSide triHyp (side / 2)+  bottomPerp = computeRightSide triHyp (bottom / 2)+  bottom     = (2 * (triHyp ** 2) * (1 - (cosine . Degrees $ 108))) ** (1 / 2)+  sShape     = case m of+    Solid ->+      G.polygon+        $ concat+        . transpose+        $ [[bLeftSt, bRightSt, rightSt, topSt, leftSt], pentPoints]+    Outline -> G.line (rightSt : starPoints) -- Some hack to fix solid+  starPoints = [bLeftSt, topSt, bRightSt, leftSt, rightSt]+  topSt      = (0, h / 2)+  leftSt     = (negate (w / 2), (h / 2) - triPerp)+  rightSt    = (negate . fst $ leftSt, snd leftSt)+  bLeftSt    = (negate (bottom / 2), negate (h / 2))+  bRightSt   = (negate . fst $ bLeftSt, snd bLeftSt)+  pentPoints = [bottomP, bRightP, rightP, leftP, bLeftP]+  bottomP    = (0, negate $ h / 2 - bottomPerp)+  leftP      = (negate $ side / 2, snd leftSt)+  rightP     = (negate . fst $ leftP, snd leftP)+  bLeftP     = (negate midPerp, negate $ h / 2 - (bottomPerp + midPerp))+  bRightP    = (negate . fst $ bLeftP, snd bLeftP)+  midPerp    = computeRightSide side $ (1.618 * side) / 2++-- | Constructs an upward-pointing equilateral triangle with length @l@,+--   mode @m@, and color @c@.+triangle+  :: Float -- ^ @l@+  -> Mode  -- ^ @m@+  -> Color -- ^ @c@+  -> Image+triangle sideLength = isoscelesTriangle sideLength 60++-- | Constructs a triangle of mode @m@, color @color@, angle @A@, angle @B@, and+--   length @c@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleAAS+  :: Float -- ^ @A@+  -> Float -- ^ @B@+  -> Float -- ^ @c@+  -> Mode  -- ^ @m@+  -> Color -- ^ @color@+  -> Image+triangleAAS degr degl t = triangleSSS+  (t * (sine . Degrees $ degr) / (sine . Degrees $ 180 - (degl + degr)))+  (t * (sine . Degrees $ degl) / (sine . Degrees $ 180 - (degl + degr)))+  t++-- | Constructs a triangle of mode @m@, color @color@, angle @A@, angle @C@, and+--   length @b@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleASA+  :: Float -- ^ @A@+  -> Float -- ^ @C@+  -> Float -- ^ @b@+  -> Mode  -- ^ @m@+  -> Color -- ^ @color@+  -> Image+triangleASA degl l degt = triangleSSS+  (l * (sine . Degrees $ degl) / (sine . Degrees $ 180 - (degt + degl)))+  l+  (l * (sine . Degrees $ degt) / (sine . Degrees $ 180 - (degt + degl)))++-- | Constructs a triangle of mode @m@, color @color@, angle @B@, angle @C@, and+--   length @a@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleSAA+  :: Float -- ^ @B@+  -> Float -- ^ @C@+  -> Float -- ^ @a@+  -> Mode  -- ^ @m@+  -> Color -- ^ @color@+  -> Image+triangleSAA r degr degt = triangleSSS+  r+  (r * (sine . Degrees $ degr) / (sine . Degrees $ 180 - (degt + degr)))+  (r * (sine . Degrees $ degt) / (sine . Degrees $ 180 - (degt + degr)))++-- | Constructs a triangle of mode @m@, color @color@, angle @A@, and lengths+--   @b@ and @c@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleASS+  :: Float -- ^ @A@+  -> Float -- ^ @b@+  -> Float -- ^ @c@+  -> Mode  -- ^ @mode@+  -> Color -- ^ @color@+  -> Image+triangleASS deg l t = triangleSSS+  (((l ** 2) + (t ** 2) - 2 * t * l * (cosine . Degrees $ deg)) ** (1 / 2))+  l+  t++-- | Constructs a triangle of mode @m@, color @color@, angle @B@, and lengths+--   @a@ and @c@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleSAS+  :: Float -- ^ @B@+  -> Float -- ^ @a@+  -> Float -- ^ @c@+  -> Mode  -- ^ @mode@+  -> Color -- ^ @color@+  -> Image+triangleSAS r deg t = triangleSSS+  r+  (((r ** 2) + (t ** 2) - 2 * t * r * (cosine . Degrees $ deg)) ** (1 / 2))+  t++-- | Constructs a triangle of mode @m@, color @color@, angle @C@, and lengths+--   @a@ and @c@. The variables refer to the diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleSSA+  :: Float -- ^ @C@+  -> Float -- ^ @a@+  -> Float -- ^ @c@+  -> Mode  -- ^ @mode@+  -> Color -- ^ @color@+  -> Image+triangleSSA r l deg = triangleSSS+  r+  l+  (((r ** 2) + (l ** 2) - 2 * l * r * (cosine . Degrees $ deg)) ** (1 / 2))++-- | Constructs a triangle of side @a@, @b@, and @c@. The variables refer to the+--   diagram above.+--   If it's not possible to construct the triangle with the given arguments,+--   an empty image is returned.+triangleSSS+  :: Float -- ^ @a@+  -> Float -- ^ @b@+  -> Float -- ^ @c@+  -> Mode  -- ^ @m@+  -> Color -- ^ @color@+  -> Image+triangleSSS r l t m c =+  if (round . distance (bottX, negate newH / 2) $ (-t / 2, newH / 2) :: Integer)+     == (round l)+     && (round . distance (bottX, negate newH / 2) $ (t / 2, newH / 2) :: Integer+        )+     == round r+  then+    Image { width  = newW+          , height = newH+          , shapes = [(G.color c triangleShape, origin)]+          }+  else+    emptyImage++ where+  angleL =+    arccosine $ (l ** 2 - r ** 2 - t ** 2) / (-2 * r * t) :: Degrees Float+  angleR =+    arccosine $ (r ** 2 - l ** 2 - t ** 2) / (-2 * l * t) :: Degrees Float+  newH = (if angleR < angleL then l else r) * (sine $ min angleR angleL)+  newW = if+    | angleR < angleL && angleL > Degrees 90+    -> l * (sine $ Degrees 90 - min angleR angleL)+    | angleL < angleR && angleR > Degrees 90+    -> r * (sine $ Degrees 90 - min angleR angleL)+    | otherwise+    -> t+  bottW = computeRightSide (max l r) newH+  bottX = if l > r then bottW - (t / 2) else negate $ bottW - (t / 2)+  converter =+    convert (min bottX $ -t / 2) (-newW / 2) (max bottX $ t / 2) (newW / 2)+  tShape =+    [ (converter $ negate t / 2, newH / 2)+    , (converter bottX         , negate newH / 2)+    , (converter $ t / 2       , newH / 2)+    ]+  triangleShape = case m of+    Solid   -> G.polygon tShape+    Outline -> G.line ((converter $ t / 2, newH / 2) : tShape)
+ src/Graphics/Htdp/Util/Arithmetic.hs view
@@ -0,0 +1,54 @@+module Graphics.Htdp.Util.Arithmetic+  ( computeHypotenuse+  , computeRightSide+  , convert+  , distance+  , heron+  )+where++computeHypotenuse :: Float -> Float -> Float+computeHypotenuse a b = (a ** 2 + b ** 2) ** (1 / 2)++computeRightSide :: Float -> Float -> Float+computeRightSide h a = (h ** 2 - a ** 2) ** (1 / 2)++convert :: Float -> Float -> Float -> Float -> Float -> Float+convert a1 a2 b1 b2 c1 = (((c1 - a1) * (b2 - a2)) / (b1 - a1)) + a2++{-   y_a1  = 0x + ba                    y_a2 = 0x + bb+       a1  =      ba                      a2 =      bb+------------------------------------------------------+     y_b1  = m  + ba                    y_b2 = M  + bb+   b1 - ba = m                       b2 - bb = M+   b1 - a1 = m                       b2 - a2 = M+------------------------------------------------------+      y_c1 = mx + ba                    y_c2 = Mx + bb+ y_c1 - ba = mx                    y_c2 - bb = Mx+   c1 - a1 = (b1 - a1)x              c2 - a2 = (b2 - a2)x+                                     c2 - a2+                                     ------- = x+                                     b2 - a2++            c1 - a1 = (b1 - a1)(c2 - a2)+                      -----------------+                          (b2 - a2)++          (c1 - a1)(b2 - a2)+          ------------------ = c2 - a2+               (b1 - a1)+++          c2 = (c1 - a1)(b2 - a2) + a2+               ------------------+                    (b1 - a1)++Thanks RRose+-}++distance :: (Float, Float) -> (Float, Float) -> Float+distance (x1, y1) (x2, y2) = ((y2 - y1) ** 2 + (x2 - x1) ** 2) ** (1 / 2)++heron :: Float -> Float -> Float -> Float+heron a b c = (p * (p - a) * (p - b) * (p - c)) ** (1 / 2)+  where p = (a + b + c) / 2
− src/Graphics/Shape.hs
@@ -1,409 +0,0 @@-{-# LANGUAGE MultiWayIf #-}---- Sorted as they are in 2htdp/image--- | Image constructors-module Graphics.Shape-  ( Mode-  , solid-  , outline-  , circle-  , ellipse-  , line-  , addLine-  , emptyImage-  , triangle-  , rightTriangle-  , isoscelesTriangle-  -- *** The following image from 2htdp/image documentation is useful for the following family of functions.-  -- |   <<diagrams/triangleDiagram.png>>-  , triangleSSS-  , triangleASS-  , triangleSAS-  , triangleSSA-  , triangleAAS-  , triangleASA-  , triangleSAA-  , square-  , rectangle-  , rhombus-  , star-  )-where--import           Data.Angle-import           Data.Fixed-import           Data.List-import           Graphics.Combinator-import           Graphics.Data.Image-import qualified Graphics.Gloss                as G-import           Graphics.Gloss.Data.Color-import           Graphics.Util.Arithmetic---- | Drawing mode.-data Mode = Solid | Outline deriving Eq---- | Type of drawing mode.-solid, outline :: Mode-solid = Solid-outline = Outline---- Initial point for all images-origin :: G.Point-origin = (0, 0)---- | Adds a line to the given image @i@ of color @c@, starting from point @(x1, y1)@---   and going to point @(x2, y2)@. If the line crosses the given image's binding box,---   then new image dimesions are changed to accommodate the line.-addLine-  :: Image -- ^ @i@-  -> Float -- ^ @x1@-  -> Float -- ^ @y1@-  -> Float -- ^ @x2@-  -> Float -- ^ @y2@-  -> Color-  -> Image-addLine i x1 y1 x2 y2 c =-  placeImage (line (x1 - x2) (y1 - y2) c) ((x1 + x2) / 2) ((y1 + y2) / 2) i---- | Constructs a circle of radius @r@, drawing mode @m@ and color @c@.-circle-  :: Float -- ^ @r@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-circle r mode c = Image { width  = r * 2-                        , height = r * 2-                        , shapes = [(G.color c $ circleKind r, origin)]-                        }- where-  circleKind = case mode of-    Solid   -> G.circleSolid-    Outline -> G.circle---- | Constructs an ellipse of width @w@, height @h@, mode @m@, and color @c@.-ellipse-  :: Float -- ^ @w@-  -> Float -- ^ @h@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-ellipse w h m c = Image { width  = w-                        , height = h-                        , shapes = [(circleToEllipse, origin)]-                        }- where -- This took me longer than it should have-  circleToEllipse = G.scale (w / (2 * radius)) (h / (2 * radius)) circPic-  circPic         = fst . head . shapes $ circle radius m c-  radius          = (w + h) / 4---- | Constructs an image of width and height @0@.-emptyImage :: Image-emptyImage = Image 0 0 []---- | Constructs a triangle of two equal-length sides, of length @l@, where the---   angle between those sides is @a@, mode is @m@ and color is @c@. If the angle---   is less than @180@, then the triangle will point up, else it will point down.-isoscelesTriangle-  :: Float -- ^ @l@-  -> Float -- ^ @a@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-isoscelesTriangle sl deg m c = Image-  { width  = newW-  , height = newH-  , shapes = [(G.color c triangleShape, origin)]-  }- where-  newW   = (2 * (sl ** 2) * (1 - (cosine . Degrees $ deg))) ** (1 / 2)-  newH   = computeRightSide sl (newW / 2)-  topDir = if mod' deg 360 < 180 then 1 else -1-  tShape =-    [ (negate newW / 2, negate topDir * (newH / 2))-    , (0              , topDir * newH / 2)-    , (newW / 2       , negate topDir * (newH / 2))-    ]-  triangleShape = case m of-    Solid   -> G.polygon tShape-    Outline -> G.line ((newW / 2, negate topDir * (newH / 2)) : tShape)---- | Constructs an image of a line segment of color @c@ that connects the points---   @(0,0)@ to @(x1, y1)@.-line-  :: Float -- ^ @x1@-  -> Float -- ^ @y1@-  -> Color -- ^ @c@-  -> Image-line x y c = Image { width  = abs x-                   , height = abs y-                   , shapes = [(G.color c $ G.Line lineShape, origin)]-                   }-  -- We want the line centered on the origin.-  -- For that, we will need to move the given points-  -- (I had to take a day off to get this)-  where lineShape = [(x / 2, negate y / 2), (negate x / 2, y / 2)]---- | Constructs a rectangle of width @w@, height @h@, mode @m@, and color @c@.-rectangle-  :: Float -- ^ @w@-  -> Float -- ^ @h@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-rectangle w h mode c = Image { width  = w-                             , height = h-                             , shapes = [(G.color c $ rectShape w h, origin)]-                             }- where-  rectShape = case mode of-    Solid   -> G.rectangleSolid-    Outline -> G.rectangleWire---- | Constructs a four sided polygon with all equal sides of length @l@, where the---   top and bottom pair of angles is @a@, and the left and right are @180 - a@.---   As usual, mode is @m@ and color is @c@.-rhombus-  :: Float -- ^ @l@-  -> Float -- ^ @a@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-rhombus sideLength angle m c = Image { width  = base-                                     , height = opp-                                     , shapes = [(G.color c rShape, origin)]-                                     }- where-  -- It's the law of Cosine bb-  base = (2 * (sideLength ** 2) * (1 - (cosine . Degrees $ angle))) ** (1 / 2)-  opp  = 2 * computeRightSide sideLength (base / 2)-  rhombusShape =-    [(negate base / 2, 0), (0, opp / 2), (base / 2, 0), (0, negate opp / 2)]-  rShape = case m of-    Solid   -> G.polygon rhombusShape-    Outline -> G.line ((0, negate opp / 2) : rhombusShape)---- | Constructs a right triangle with base length @b@, perpendicular length---   @p@, mode @m@, and color @c@.-rightTriangle-  :: Float -- ^ @b@-  -> Float -- ^ @p@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-rightTriangle b p m c = Image { width  = b-                              , height = p-                              , shapes = [(G.color c triangleShape, origin)]-                              }- where-  tShape =-    [(b / 2, negate p / 2), (negate b / 2, p / 2), (negate b / 2, negate p / 2)]-  triangleShape = case m of-    Solid   -> G.polygon tShape-    Outline -> G.line ((negate b / 2, negate p / 2) : tShape)---- | Constructs a square of side @s@, mode @m@, and color @c@.-square-  :: Float -- ^ @s@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-square w = rectangle w w---- | Constructs a star with five points of mode @m@ and color @c@. The argument---   @l@ determines the side length of the internal pentagon.---   Currently, a solid star is glitchy since it is a non-convex polygon---   and openGL (the underlying graphics library) doesn't draw them correctly.---   This will be corrected in future versions.-star-  :: Float -- ^ @l@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-star side m c = Image { width  = w-                      , height = h-                      , shapes = [(G.color c sShape, origin)]-                      }- where-    -- Pentagon is 108degs apart-  w          = (2 * triHyp) + side-  h          = (1.539 * side) + triPerp + bottomPerp-  triHyp     = (sine . Degrees $ 72) * (side / (sine . Degrees $ 36))-  triPerp    = computeRightSide triHyp (side / 2)-  bottomPerp = computeRightSide triHyp (bottom / 2)-  bottom     = (2 * (triHyp ** 2) * (1 - (cosine . Degrees $ 108))) ** (1 / 2)-  sShape     = case m of-    Solid ->-      G.polygon-        $ concat-        . transpose-        $ [[bLeftSt, bRightSt, rightSt, topSt, leftSt], pentPoints]-    Outline -> G.line (rightSt : starPoints) -- Some hack to fix solid-  starPoints = [bLeftSt, topSt, bRightSt, leftSt, rightSt]-  topSt      = (0, h / 2)-  leftSt     = (negate (w / 2), (h / 2) - triPerp)-  rightSt    = (negate . fst $ leftSt, snd leftSt)-  bLeftSt    = (negate (bottom / 2), negate (h / 2))-  bRightSt   = (negate . fst $ bLeftSt, snd bLeftSt)-  pentPoints = [bottomP, bRightP, rightP, leftP, bLeftP]-  bottomP    = (0, negate $ h / 2 - bottomPerp)-  leftP      = (negate $ side / 2, snd leftSt)-  rightP     = (negate . fst $ leftP, snd leftP)-  bLeftP     = (negate midPerp, negate $ h / 2 - (bottomPerp + midPerp))-  bRightP    = (negate . fst $ bLeftP, snd bLeftP)-  midPerp    = computeRightSide side $ (1.618 * side) / 2---- | Constructs an upward-pointing equilateral triangle with length @l@,---   mode @m@, and color @c@.-triangle-  :: Float -- ^ @l@-  -> Mode  -- ^ @m@-  -> Color -- ^ @c@-  -> Image-triangle sideLength = isoscelesTriangle sideLength 60---- | Constructs a triangle of mode @m@, color @color@, angle @A@, angle @B@, and---   length @c@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleAAS-  :: Float -- ^ @A@-  -> Float -- ^ @B@-  -> Float -- ^ @c@-  -> Mode  -- ^ @m@-  -> Color -- ^ @color@-  -> Image-triangleAAS degr degl t = triangleSSS-  (t * (sine . Degrees $ degr) / (sine . Degrees $ 180 - (degl + degr)))-  (t * (sine . Degrees $ degl) / (sine . Degrees $ 180 - (degl + degr)))-  t---- | Constructs a triangle of mode @m@, color @color@, angle @A@, angle @C@, and---   length @b@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleASA-  :: Float -- ^ @A@-  -> Float -- ^ @C@-  -> Float -- ^ @b@-  -> Mode  -- ^ @m@-  -> Color -- ^ @color@-  -> Image-triangleASA degl l degt = triangleSSS-  (l * (sine . Degrees $ degl) / (sine . Degrees $ 180 - (degt + degl)))-  l-  (l * (sine . Degrees $ degt) / (sine . Degrees $ 180 - (degt + degl)))---- | Constructs a triangle of mode @m@, color @color@, angle @B@, angle @C@, and---   length @a@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleSAA-  :: Float -- ^ @B@-  -> Float -- ^ @C@-  -> Float -- ^ @a@-  -> Mode  -- ^ @m@-  -> Color -- ^ @color@-  -> Image-triangleSAA r degr degt = triangleSSS-  r-  (r * (sine . Degrees $ degr) / (sine . Degrees $ 180 - (degt + degr)))-  (r * (sine . Degrees $ degt) / (sine . Degrees $ 180 - (degt + degr)))---- | Constructs a triangle of mode @m@, color @color@, angle @A@, and lengths---   @b@ and @c@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleASS-  :: Float -- ^ @A@-  -> Float -- ^ @b@-  -> Float -- ^ @c@-  -> Mode  -- ^ @mode@-  -> Color -- ^ @color@-  -> Image-triangleASS deg l t = triangleSSS-  (((l ** 2) + (t ** 2) - 2 * t * l * (cosine . Degrees $ deg)) ** (1 / 2))-  l-  t---- | Constructs a triangle of mode @m@, color @color@, angle @B@, and lengths---   @a@ and @c@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleSAS-  :: Float -- ^ @B@-  -> Float -- ^ @a@-  -> Float -- ^ @c@-  -> Mode  -- ^ @mode@-  -> Color -- ^ @color@-  -> Image-triangleSAS r deg t = triangleSSS-  r-  (((r ** 2) + (t ** 2) - 2 * t * r * (cosine . Degrees $ deg)) ** (1 / 2))-  t---- | Constructs a triangle of mode @m@, color @color@, angle @C@, and lengths---   @a@ and @c@. The variables refer to the diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleSSA-  :: Float -- ^ @C@-  -> Float -- ^ @a@-  -> Float -- ^ @c@-  -> Mode  -- ^ @mode@-  -> Color -- ^ @color@-  -> Image-triangleSSA r l deg = triangleSSS-  r-  l-  (((r ** 2) + (l ** 2) - 2 * l * r * (cosine . Degrees $ deg)) ** (1 / 2))---- | Constructs a triangle of side @a@, @b@, and @c@. The variables refer to the---   diagram above.---   If it's not possible to construct the triangle with the given arguments,---   an empty image is returned.-triangleSSS-  :: Float -- ^ @a@-  -> Float -- ^ @b@-  -> Float -- ^ @c@-  -> Mode  -- ^ @m@-  -> Color -- ^ @color@-  -> Image-triangleSSS r l t m c =-  if (round . distance (bottX, negate newH / 2) $ (-t / 2, newH / 2) :: Integer)-     == (round l)-     && (round . distance (bottX, negate newH / 2) $ (t / 2, newH / 2) :: Integer-        )-     == round r-  then-    Image { width  = newW-          , height = newH-          , shapes = [(G.color c triangleShape, origin)]-          }-  else-    emptyImage-- where-  angleL =-    arccosine $ (l ** 2 - r ** 2 - t ** 2) / (-2 * r * t) :: Degrees Float-  angleR =-    arccosine $ (r ** 2 - l ** 2 - t ** 2) / (-2 * l * t) :: Degrees Float-  newH = (if angleR < angleL then l else r) * (sine $ min angleR angleL)-  newW = if-    | angleR < angleL && angleL > Degrees 90-    -> l * (sine $ Degrees 90 - min angleR angleL)-    | angleL < angleR && angleR > Degrees 90-    -> r * (sine $ Degrees 90 - min angleR angleL)-    | otherwise-    -> t-  bottW = computeRightSide (max l r) newH-  bottX = if l > r then bottW - (t / 2) else negate $ bottW - (t / 2)-  converter =-    convert (min bottX $ -t / 2) (-newW / 2) (max bottX $ t / 2) (newW / 2)-  tShape =-    [ (converter $ negate t / 2, newH / 2)-    , (converter bottX         , negate newH / 2)-    , (converter $ t / 2       , newH / 2)-    ]-  triangleShape = case m of-    Solid   -> G.polygon tShape-    Outline -> G.line ((converter $ t / 2, newH / 2) : tShape)
− src/Graphics/Util/Arithmetic.hs
@@ -1,54 +0,0 @@-module Graphics.Util.Arithmetic-  ( computeHypotenuse-  , computeRightSide-  , convert-  , distance-  , heron-  )-where--computeHypotenuse :: Float -> Float -> Float-computeHypotenuse a b = (a ** 2 + b ** 2) ** (1 / 2)--computeRightSide :: Float -> Float -> Float-computeRightSide h a = (h ** 2 - a ** 2) ** (1 / 2)--convert :: Float -> Float -> Float -> Float -> Float -> Float-convert a1 a2 b1 b2 c1 = (((c1 - a1) * (b2 - a2)) / (b1 - a1)) + a2--{-   y_a1  = 0x + ba                    y_a2 = 0x + bb-       a1  =      ba                      a2 =      bb--------------------------------------------------------     y_b1  = m  + ba                    y_b2 = M  + bb-   b1 - ba = m                       b2 - bb = M-   b1 - a1 = m                       b2 - a2 = M--------------------------------------------------------      y_c1 = mx + ba                    y_c2 = Mx + bb- y_c1 - ba = mx                    y_c2 - bb = Mx-   c1 - a1 = (b1 - a1)x              c2 - a2 = (b2 - a2)x-                                     c2 - a2-                                     ------- = x-                                     b2 - a2--            c1 - a1 = (b1 - a1)(c2 - a2)-                      ------------------                          (b2 - a2)--          (c1 - a1)(b2 - a2)-          ------------------ = c2 - a2-               (b1 - a1)---          c2 = (c1 - a1)(b2 - a2) + a2-               -------------------                    (b1 - a1)--Thanks RRose--}--distance :: (Float, Float) -> (Float, Float) -> Float-distance (x1, y1) (x2, y2) = ((y2 - y1) ** 2 + (x2 - x1) ** 2) ** (1 / 2)--heron :: Float -> Float -> Float -> Float-heron a b c = (p * (p - a) * (p - b) * (p - c)) ** (1 / 2)-  where p = (a + b + c) / 2
tests/CombinatorTest.hs view
@@ -3,7 +3,7 @@   ) where -import           Graphics.Data.Image+import           Graphics.Htdp.Data.Image import           Graphics.Htdp import           Graphics.Gloss                 ( Picture ) import           Graphics.Gloss.Data.Picture    ( blank )
tests/ShapeTest.hs view
@@ -3,7 +3,7 @@   ) where -import           Graphics.Data.Image+import           Graphics.Htdp.Data.Image import           Graphics.Htdp import qualified Graphics.Gloss                as G import           Test.HUnit