wraparound (empty) → 0.0.1
raw patch · 5 files changed
+323/−0 lines, 5 filesdep +basesetup-changed
Dependencies added: base
Files
- Data/WrapAround.hs +194/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- wrap-around-example.hs +35/−0
- wraparound.cabal +62/−0
+ Data/WrapAround.hs view
@@ -0,0 +1,194 @@+{-|+ WrapAround is a convenience module which helps you perform calculations with+ points that are supposed to exist on a 2-dimensional, finite, unbounded plane.+ (Or infinite, bounded plane, depending on who you ask.) On such a plane, space+ wraps around so that an object travelling vertically or horizontally eventually+ comes back to the place where it started. This allows you to move objects+ around on a seamless map. For example, in some video games when an object+ crosses the bottom of the screen it reappears at the top.++ WrapAround represents the points and handles the common calculations properly+ so you don't have to bother with the messy math and edge cases. This is done+ with two data structures: a 'WrapMap', which stores information about the size+ of the plane, and a 'WrapPoint', which stores information about the location of+ the point.++ A WrapPoint is represented internally as a pair of angles, like in a torus.+ The WrapMap and WrapPoint structures are kept separate because some WrapPoint+ calculations can be performed without a WrapMap context. Functions typically+ only need a WrapMap when a WrapPoint must be converted to actual x, y+ coordinates or vice versa. You can perform calculations mixing WrapPoints that+ were generated with different WrapMaps, but this generally yields meaningless+ results.++ When you need the actual x, y coordinates, use the 'toCoords' conversion+ function.++ If you are grateful for this software, I gladly accept donations!++ <https://frigidcode.com/donate/>+-}+module Data.WrapAround ( WrapMap()+ , wrapmap+ , WrapPoint()+ , wrappoint+ , addPoints+ , addPoints'+ , distance+ , subtractPoints+ , toCoords+ , vectorRelation+ -- , windowView+ -- , WrapWindow(..)+ ) where++-- |Contains the contextual information necessary to convert a WrapPoint to+-- coordinates and vice versa.+data WrapMap = WrapMap { radiusr :: Double -- radius from tube center+ , radiusR :: Double -- radius from torus center+ }+ deriving (Show)++-- |Generates a 'WrapMap'.+wrapmap :: Double -- ^ Width+ -> Double -- ^ Height+ -> WrapMap+wrapmap width height = WrapMap { radiusR = height / (2 * pi)+ , radiusr = width / (2 * pi)+ }++-- |A representation of a point location that allows for wrapping in the+-- vertical or horizontal direction.+data WrapPoint = WrapPoint { angler :: Double -- radians around tube center+ , angleR :: Double -- radians around torus center+ }+ deriving (Show)++-- |Generates a 'WrapPoint'.+wrappoint :: WrapMap -- ^ Corresponding WrapMap structure+ -> (Double, Double) -- ^ x, y coordinates+ -> WrapPoint+wrappoint wmap (x, y)+ = let angleR' = fixAngle (y / radiusR wmap) in + let angler' = fixAngle (x / radiusr wmap) in+ WrapPoint { angleR = angleR', angler = angler' }++-- |Converts a WrapPoint to x, y coordinates. Generally you will only will only+-- want to use this function for informational purposes, for example, to print+-- out the x, y coordinates or to feed the coordinates to a graphics display+-- function. If you convert a WrapPoint to x, y coordinates so that you can+-- perform calculations with the coordinates, you must handle the wrapping math+-- yourself and you are doing the work the module is supposed to do for you.+toCoords :: WrapMap -- ^ Corresponding WrapMap structure+ -> WrapPoint -- ^ WrapPoint to be converted+ -> (Double, Double)+toCoords WrapMap { radiusR = mRadiusR, radiusr = mRadiusr }+ WrapPoint { angleR = pAngleR, angler = pAngler }+ = (pAngler * mRadiusr, pAngleR * mRadiusR)++-- |Adds two WrapPoints together (vector style).+addPoints :: WrapPoint -- ^ The first WrapPoint in the operation+ -> WrapPoint -- ^ The WrapPoint to be added to the first WrapPoint+ -> WrapPoint+addPoints wp1 wp2+ = let angleR' = fixAngle (angleR wp1 + angleR wp2) in+ let angler' = fixAngle (angler wp1 + angler wp2) in+ WrapPoint { angleR = angleR', angler = angler' }++-- |Adds a WrapPoint and a pair of x, y coordinates (vector style).+addPoints' :: WrapMap -- ^ The corresponding WrapMap structure+ -> WrapPoint -- ^ The WrapPoint in the operation+ -> (Double, Double) -- ^ The x, y coordinates to be added to the WrapPoint+ -> WrapPoint+addPoints' wmap wp1 (x, y)+ = let wp2 = wrappoint wmap (x, y) in+ let angleR' = fixAngle (angleR wp1 + angleR wp2) in+ let angler' = fixAngle (angler wp1 + angler wp2) in+ WrapPoint { angleR = angleR', angler = angler' }++-- |Subtracts a WrapPoint from a WrapPoint (vector style).+subtractPoints :: WrapPoint -- ^ The first WrapPoint in the operation+ -> WrapPoint -- ^ The WrapPoint to be subtracted from the first WrapPoint+ -> WrapPoint+subtractPoints wp1 wp2+ = let angleR' = fixAngle (angleR wp1 - angleR wp2) in+ let angler' = fixAngle (angler wp1 - angler wp2) in+ WrapPoint { angleR = angleR', angler = angler' }++-- distance :: WrapMap -> WrapPoint -> WrapPoint -> Double+-- distance WrapMap { radiusR = mRadiusR, radiusr = mRadiusr }+-- WrapPoint { angleR = p1angleR, angler = p1angler }+-- WrapPoint { angleR = p2angleR, angler = p2angler }+-- = let dXa = abs (p2angler - p1angler) in+-- let dYa = abs (p2angleR - p1angleR) in+-- let dXb = if p1angler < p2angler+-- then abs ((p1angler + 2 * pi) - p2angler)+-- else abs ((p1angler - 2 * pi) - p2angler) in+-- let dYb = if p1angleR < p2angleR+-- then abs ((p1angleR + 2 * pi) - p2angleR)+-- else abs ((p1angleR - 2 * pi) - p2angleR) in+-- let dX = min dXa dXb in+-- let dY = min dYa dYb in+-- let dX' = dX * mRadiusr in+-- let dY' = dY * mRadiusR in+-- sqrt (dX'**2 + dY'**2)+++-- |Finds the distance between two WrapPoints.+distance :: WrapMap -- ^ The corresponding WrapMap structure+ -> WrapPoint -- ^ The first WrapPoint+ -> WrapPoint -- ^ The second WrapPoint+ -> Double+distance wmap wp1 wp2+ = let (dX, dY) = vectorRelation wmap wp1 wp2 in+ sqrt (dX**2 + dY**2)++fixAngle :: Double -> Double+fixAngle radians+ = let q = radians / (2 * pi) in+ let angle = radians - fromIntegral (truncate q) * (2 * pi) in+ if angle < 0 then 2 * pi + angle+ else angle++-- |Returns the relationship between two WrapPoints as a pair of x, y+-- coordinates (a vector).+vectorRelation :: WrapMap -- ^ The corresponding WrapMap structure+ -> WrapPoint -- ^ The first WrapPoint+ -> WrapPoint -- ^ The second WrapPoint+ -> (Double, Double)+vectorRelation WrapMap { radiusR = mRadiusR, radiusr = mRadiusr }+ WrapPoint { angleR = p1angleR, angler = p1angler }+ WrapPoint { angleR = p2angleR, angler = p2angler }+ = let dXa = abs (p2angler - p1angler) in+ let dYa = abs (p2angleR - p1angleR) in+ let dXb = if p1angler < p2angler+ then abs ((p1angler + 2 * pi) - p2angler)+ else abs ((p1angler - 2 * pi) - p2angler) in+ let dYb = if p1angleR < p2angleR+ then abs ((p1angleR + 2 * pi) - p2angleR)+ else abs ((p1angleR - 2 * pi) - p2angleR) in+ let dX = min dXa dXb in+ let dY = min dYa dYb in+ let dX' = dX * mRadiusr in+ let dY' = dY * mRadiusR in+ (dX', dY')++-- Maybe add these someday after I find them useful++-- data WrapWindow = WrapWindow { tlCorner :: (Double, Double)+-- , width :: Double+-- , height :: Double+-- , wrapMap :: WrapMap+-- }++-- windowView :: WrapWindow -> WrapPoint -> WrapPoint -> (Double, Double)+-- windowView window centerpoint point+-- = let cornerPoint = addPoints'+-- (wrapMap window)+-- centerpoint+-- ((-width window) / 2.0, height window / 2.0) in+-- let (vx, vy) = vectorRelation (wrapMap window) cornerPoint point in+-- let vx' = vx in+-- let vy' = (-vy) in+-- (vx' + fst (tlCorner window), vy' + snd (tlCorner window))+
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2012, Christopher Howard++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Christopher Howard nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ wrap-around-example.hs view
@@ -0,0 +1,35 @@+import Graphics.Gloss.Interface.Pure.Simulate+import GHC.Float+import Data.WrapAround++dispmode = InWindow "WrapAround module test" (1024, 768) (0, 0)++main = simulate dispmode (dim green) 10 model dispmodel stepfunc++data Model = Model { particles :: [(WrapPoint, (Double, Double))]+ , wrapMap :: WrapMap }+ deriving (Show)++wmap = wrapmap 200 300++model = Model { particles = [ (wrappoint wmap (10, 10), (10, 0))+ , (wrappoint wmap (30, 30), ((-5), (-5)))+ ]+ , Main.wrapMap = wmap+ }++dispmodel m = let ps = do (wp, _) <- particles m+ let (px, py) = toCoords (Main.wrapMap m) wp in+ [ Polygon [ (double2Float px, double2Float py)+ , (double2Float px + 10, double2Float py)+ , (double2Float px + 10, double2Float py + 10)+ , (double2Float px, double2Float py + 10)+ ] ] in+ Translate (-100.0) (-150.0) (Color green (Pictures ps))++stepfunc _ _ m = let map = Main.wrapMap m in+ let nparticles = do (wp, (x, y)) <- particles m+ [ (addPoints' map wp (x, y), (x, y)) ] in+ m { particles = nparticles }++
+ wraparound.cabal view
@@ -0,0 +1,62 @@+Name: wraparound++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.0.1++-- A short (one-line) description of the package.+Synopsis: Convenient handling of points on a seamless 2-dimensional plane++-- A longer description of the package.+Description:+ WrapAround helps you perform calculations involving points on a finite,+ unbounded plane, in which objects that move across one edge of the map+ appear on the other. Add points, calculate distance, and so forth+ without worrying about the edge cases and frustrating math mistakes.++-- URL for the project homepage or repository.+Homepage: http://frigidcode.com++-- The license under which the package is released.+License: BSD3++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Christopher Howard++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: christopher.howard@frigidcode.com++-- A copyright notice.+-- Copyright: ++Category: Data++Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+Extra-source-files: wrap-around-example.hs++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.10++Library+ -- Modules exported by the library.+ Exposed-modules: Data.WrapAround+ + -- Packages needed in order to build this package.+ Build-depends: base >= 4 && < 5+ + Default-language: Haskell2010++ -- Modules not exported by this package.+ -- Other-modules: + + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+ -- Build-tools: +