packages feed

hcg-minus 0.12 → 0.14

raw patch · 7 files changed

+685/−624 lines, 7 filesdep −SG

Dependencies removed: SG

Files

Data/CG/Minus.hs view
@@ -1,37 +1,67 @@ -- | CG library (minus). module Data.CG.Minus where -import Control.Applicative-import Data.Complex-import Data.Maybe-import qualified Data.SG as G {- SG -}-import Text.Printf+import Data.Complex {- base -}+import Data.Maybe {- base -}+import Text.Printf {- base -}  -- * Types  -- | Two-dimensional point.-type Pt = G.Point2'--- type Pt a = Pt {pt_x :: a, pt_y :: a}+--+-- Pt are 'Num', pointwise, ie:+--+-- > Pt 1 2 + Pt 3 4 == Pt 4 6+-- > Pt 1 2 * Pt 3 4 == Pt 3 8+-- > negate (Pt 0 1) == Pt 0 (-1)+-- > abs (Pt (-1) 1) == Pt 1 1+-- > signum (Pt (-1/2) (1/2)) == Pt (-1) 1+data Pt a = Pt {pt_x :: a,pt_y :: a} deriving (Eq,Ord,Show) --- | Two-dimensional vector.-type Vc = G.Rel2'--- type Vc a = Vc {vc_x :: a, vc_y :: a}+instance Num a => Num (Pt a) where+    (+) = pt_binop (+)+    (-) = pt_binop (-)+    (*) = pt_binop (*)+    negate = pt_uop negate+    abs = pt_uop abs+    signum = pt_uop signum+    fromInteger n = let n' = fromInteger n in Pt n' n' +-- | Two-dimensional vector.  Vector are 'Num' in the same manner as+-- 'Pt'.+data Vc a = Vc {vc_x :: a,vc_y :: a} deriving (Eq,Ord,Show)++instance Num a => Num (Vc a) where+    (+) = vc_binop (+)+    (-) = vc_binop (-)+    (*) = vc_binop (*)+    negate = vc_uop negate+    abs = vc_uop abs+    signum = vc_uop signum+    fromInteger n = let n' = fromInteger n in Vc n' n'+ -- | Two-dimensional line.-type Ln = G.Line2'--- type Ln a = Ln (Pt a) (Pt a)+data Ln a = Ln {ln_start :: Pt a,ln_end :: Pt a} deriving (Eq,Ord,Show)  -- | Line segments. type Ls a = [Pt a]  -- | Window, given by a /lower left/ 'Pt' and an /extent/ 'Vc'.-type Wn a = (Pt a,Vc a)+data Wn a = Wn {wn_ll :: Pt a,wn_ex :: Vc a} deriving (Eq,Show)  -- | Real number, synonym for 'Double'. type R = Double  -- * R(eal) functions +-- | Epsilon.+epsilon :: Floating n => n+epsilon = 0.000001++-- | Is absolute difference less than 'epsilon'.+(~=) :: (Floating a, Ord a) => a -> a -> Bool+p ~= q = abs (p - q) < epsilon+ -- | Degrees to radians. -- -- > map r_to_radians [-180,-90,0,90,180] == [-pi,-pi/2,0,pi/2,pi]@@ -54,190 +84,118 @@         both n i j x = up n i (down n j x)     in both (r - l) l r --- * Pt functions---- | 'Pt' constructor.------ > pt_xy (pt 0 pi) == (0,pi)-pt :: a -> a -> Pt a-pt = curry G.Point2--- pt = Pt+-- | Sum of squares.+mag_sq :: Num a => a -> a -> a+mag_sq x y = x * x + y * y --- | Variant 'Pt' constructor, ie. 'uncurry' 'pt'.------ > pt_xy (pt' (0,pi)) == (0,pi)-pt' :: (a,a) -> Pt a-pt' = G.Point2---pt' = uncurry pt+-- | 'sqrt' of 'mag_sq'.+mag :: Floating c => c -> c -> c+mag x = sqrt . mag_sq x --- | /x/ field of 'Pt'.------ > pt_x (pt 0 pi) == 0-pt_x :: Pt t -> t-pt_x = G.getX+-- * Pt functions --- | /y/ field of 'Pt'.------ > pt_y (pt 0 pi) == pi-pt_y :: Pt t -> t-pt_y = G.getY+-- | Tuple constructor.+pt' :: (a,a) -> Pt a+pt' (x,y) = Pt x y --- | /x/ and /y/ fields of 'Pt'.------ > pt_xy (pt 0 pi) == (0,pi)-pt_xy :: Pt a -> (a,a)-pt_xy p = (pt_x p,pt_y p)+-- | Tuple accessor.+pt_xy :: Pt t -> (t, t)+pt_xy (Pt x y) = (x,y)  -- | 'Pt' of (0,0). ----- > pt_origin == pt 0 0+-- > pt_origin == Pt 0 0 pt_origin :: Num a => Pt a-pt_origin = pt 0 0---- | Binary operator at 'Pt'. Given the 'Applicative' instance for--- 'Pt' this is a synonym for 'liftA2'.------ > pt_binary_op (+) (pt 1 1) (pt 2 2) == pt 3 3-pt_binary_op_ :: (a -> b -> c) -> Pt a -> Pt b -> Pt c-pt_binary_op_ f p1 p2 = pt (pt_x p1 `f` pt_x p2) (pt_y p1 `f` pt_y p2)---- | Variant applicative definition as 'liftA2'-pt_binary_op :: (a -> b -> c) -> Pt a -> Pt b -> Pt c-pt_binary_op = liftA2---- | Pointwise '+'.------ > pt_add (pt 1 2) (pt 3 4) == pt 4 6-pt_add :: (Num a) => Pt a -> Pt a -> Pt a-pt_add = pt_binary_op (+)---- | Pointwise '+' (applicative definition).------ > pt_add_ (pt 1 2) (pt 3 4) == pt 4 6--- > (liftA2 (+)) (pt 1 2) (pt 3 4) == pt 4 6--- > (pure (+) <*> pt 1 2 <*> pt 3 4) == pt 4 6-pt_add_ :: (Num a) => Pt a -> Pt a -> Pt a-pt_add_ = pt_binary_op_ (+)---- | Pointwise '-'.-pt_sub :: (Num a) => Pt a -> Pt a -> Pt a-pt_sub = pt_binary_op (-)---- | Pointwise '*'.-pt_mul :: (Num a) => Pt a -> Pt a -> Pt a-pt_mul = pt_binary_op (*)---- | Unary operator at 'Pt'.  Given 'Applicative' instance for 'Pt'--- this is a synonym for 'liftA'.------ > pt_unary_op negate (pt 0 1) == pt 0 (-1)--- > pt_unary_op_ negate (pt 0 1) == pt 0 (-1)--- > (liftA negate) (pt 0 1) == pt 0 (-1)--- > (pure negate <*> pt 0 1) == pt 0 (-1)-pt_unary_op_ :: (a -> b) -> Pt a -> Pt b-pt_unary_op_ f p = pt (f (pt_x p)) (f (pt_y p))---- | Variant applicative definition as 'liftA'.-pt_unary_op :: (a -> b) -> Pt a -> Pt b-pt_unary_op = liftA---- | Pointwise 'negate'.------ > pt_negate (pt 0 1) == pt 0 (-1)-pt_negate :: (Num a) => Pt a -> Pt a-pt_negate = pt_unary_op negate+pt_origin = Pt 0 0 --- | Pointwise 'abs'.------ > pt_abs (pt (-1) 1) == pt 1 1-pt_abs :: (Num a) => Pt a -> Pt a-pt_abs = pt_unary_op abs+-- | Unary operator at 'Pt', ie. basis for 'Num' instances.+pt_uop :: (a -> b) -> Pt a -> Pt b+pt_uop f (Pt x y) = Pt (f x) (f y) --- | Pointwise 'signum'.------ > pt_signum (pt (-1/2) (1/2)) == pt (-1) 1-pt_signum :: (Num a) => Pt a -> Pt a-pt_signum = pt_unary_op signum+-- | Binary operator at 'Pt', ie. basis for 'Num' instances.+pt_binop :: (a -> b -> c) -> Pt a -> Pt b -> Pt c+pt_binop f (Pt x1 y1) (Pt x2 y2) = Pt (x1 `f` x2) (y1 `f` y2)  -- | 'Pt' at /(n,n)/. ----- > pt_from_scalar 1 == pt 1 1+-- > pt_from_scalar 1 == Pt 1 1 pt_from_scalar :: (Num a) => a -> Pt a-pt_from_scalar a = pt a a+pt_from_scalar a = Pt a a  -- | Clip /x/ and /y/ to lie in /(0,n)/. ----- > pt_clipu 1 (pt 0.5 1.5) == pt 0.5 1+-- > pt_clipu 1 (Pt 0.5 1.5) == Pt 0.5 1 pt_clipu :: (Ord a,Num a) => a -> Pt a -> Pt a pt_clipu u =     let f n = if n < 0 then 0 else if n > u then u else n-    in pt_unary_op f+    in pt_uop f  -- | Swap /x/ and /y/ coordinates at 'Pt'. ----- > pt_swap (pt 1 2) == pt 2 1+-- > pt_swap (Pt 1 2) == Pt 2 1 pt_swap :: Pt a -> Pt a-pt_swap p = pt (pt_y p) (pt_x p)+pt_swap (Pt x y) = Pt y x  -- | Negate /y/ element of 'Pt'. ----- > pt_negate_y (pt 1 1) == pt 1 (-1)+-- > pt_negate_y (Pt 1 1) == Pt 1 (-1) pt_negate_y :: (Num a) => Pt a -> Pt a-pt_negate_y p = pt (pt_x p) (negate (pt_y p))+pt_negate_y (Pt x y) = Pt x (negate y)  -- | 'Pt' variant of 'r_to_radians'. ----- > pt_to_radians (pt 90 270) == pt (pi/2) (pi*(3/2))+-- > pt_to_radians (Pt 90 270) == Pt (pi/2) (pi*(3/2)) pt_to_radians :: Pt R -> Pt R-pt_to_radians = pt_unary_op r_to_radians+pt_to_radians = pt_uop r_to_radians  -- | Cartesian to polar. ----- > pt_to_polar (pt 0 pi) == pt pi (pi/2)+-- > pt_to_polar (Pt 0 pi) == Pt pi (pi/2) pt_to_polar :: Pt R -> Pt R-pt_to_polar p = pt' (polar (pt_x p :+ pt_y p))+pt_to_polar (Pt x y) =+    let (x',y') = polar (x :+ y)+    in Pt x' y'  -- | Polar to cartesian, inverse of 'pt_to_polar'. ----- > pt_from_polar (pt pi (pi/2)) ~= pt 0 pi+-- > pt_from_polar (Pt pi (pi/2)) ~= Pt 0 pi pt_from_polar :: Pt R -> Pt R-pt_from_polar p =-    let (mg,ph) = pt_xy p-        c = mkPolar mg ph-        x = realPart c-        y = imagPart c-    in pt x y+pt_from_polar (Pt mg ph) =+    let c = mkPolar mg ph+    in Pt (realPart c) (imagPart c)  -- | Scalar 'Pt' '+'. ----- > pt_offset 1 pt_origin == pt 1 1+-- > pt_offset 1 pt_origin == Pt 1 1 pt_offset :: Num a => a -> Pt a -> Pt a-pt_offset = pt_unary_op . (+)+pt_offset = pt_uop . (+) +-- | Scalar 'Pt' '*'.+--+-- > pt_scale 2 (Pt 1 2) == Pt 2 4+pt_scale :: Num a => a -> Pt a -> Pt a+pt_scale = pt_uop . (*)+ -- | Pointwise 'min'. pt_min :: (Ord a) => Pt a -> Pt a -> Pt a-pt_min = pt_binary_op min+pt_min = pt_binop min  -- | Pointwise 'max'. pt_max :: (Ord a) => Pt a -> Pt a -> Pt a-pt_max = pt_binary_op max+pt_max = pt_binop max  -- | Apply function to /x/ and /y/ fields of three 'Pt'. pt_ternary_f :: (a->a->b->b->c->c->d) -> Pt a -> Pt b -> Pt c -> d-pt_ternary_f f p0 p1 p2 =-    let (x0,y0) = pt_xy p0-        (x1,y1) = pt_xy p1-        (x2,y2) = pt_xy p2-    in f x0 y0 x1 y1 x2 y2+pt_ternary_f f (Pt x0 y0) (Pt x1 y1) (Pt x2 y2) = f x0 y0 x1 y1 x2 y2  -- | Given a /(minima,maxima)/ pair, expand so as to include /p/. ----- > pt_minmax (pt 0 0,pt 1 1) (pt (-1) 2) == (pt (-1) 0,pt 1 2)+-- > pt_minmax (Pt 0 0,Pt 1 1) (Pt (-1) 2) == (Pt (-1) 0,Pt 1 2) pt_minmax :: Ord a => (Pt a,Pt a) -> Pt a -> (Pt a,Pt a) pt_minmax (p0,p1) p =     let f x0 y0 x1 y1 x y =-            (pt (min x x0) (min y y0)-            ,pt (max x x1) (max y y1))+            (Pt (min x x0) (min y y0)+            ,Pt (max x x1) (max y y1))     in pt_ternary_f f p0 p1 p  -- | 'Pt' variant of 'constrain'.@@ -246,172 +204,137 @@     let f x0 y0 x1 y1 x y =             let x' = r_constrain (x0,x1) x                 y' = r_constrain (y0,y1) y-            in pt x' y'+            in Pt x' y'     in pt_ternary_f f p0 p1 p  -- | Angle to origin. ----- > pt_angle_o (pt 0 1) == pi / 2+-- > pt_angle_o (Pt 0 1) == pi / 2 pt_angle_o :: Pt R -> R-pt_angle_o p = atan2 (pt_y p) (pt_x p)+pt_angle_o (Pt x y) = atan2 y x  -- | Angle from /p/ to /q/. ----- > pt_angle (pt 0 (-1)) (pt 0 1) == pi/2--- > pt_angle (pt 1 0) (pt 0 1) == pi * 3/4--- > pt_angle (pt 0 1) (pt 0 1) == 0+-- > pt_angle (Pt 0 (-1)) (Pt 0 1) == pi/2+-- > pt_angle (Pt 1 0) (Pt 0 1) == pi * 3/4+-- > pt_angle (Pt 0 1) (Pt 0 1) == 0 pt_angle :: Pt R -> Pt R -> R-pt_angle p q = pt_angle_o (q `pt_sub` p)+pt_angle p q = pt_angle_o (q - p)  -- | Pointwise '+'.+--+-- pt_translate (Pt 0 0) (vc 1 1) == pt 1 1 pt_translate :: (Num a,Eq a) => Pt a -> Vc a -> Pt a-pt_translate = G.plusDir--- pt_translate p v = pt (pt_x p + vc_x v) (pt_y p + vc_y v)--- pt_translate (Pt x y) (Vc dx dy) = Pt (x + dx) (y + dy)---- | Alternate implementation of 'pt_translate'.-pt_translate_ :: Num a => Pt a -> Vc a -> Pt a-pt_translate_ p v =-    let (dx,dy) = vc_xy v-        (x,y) = pt_xy p-    in pt (x+dx) (y+dy)+pt_translate (Pt x y) (Vc dx dy) = Pt (x + dx) (y + dy) --- | 'pt_unary_op' 'fromIntegral'.+-- | 'pt_uop' 'fromIntegral'. pt_from_i :: (Integral i,Num a) => Pt i -> Pt a-pt_from_i = pt_unary_op fromIntegral+pt_from_i = pt_uop fromIntegral +-- | 'mag_sq' of /x/ /y/ of 'Pt'.+pt_mag_sq :: Num a => Pt a -> a+pt_mag_sq (Pt x y) = mag_sq x y++-- | 'mag' of /x/ /y/ of 'Pt'.+pt_mag :: Floating a => Pt a -> a+pt_mag (Pt x y) = mag x y+ -- | Distance from 'Pt' /p/ to 'Pt' /q/. ----- > pt_distance (pt 0 0) (pt 0 1) == 1--- > pt_distance (pt 0 0) (pt 1 1) == sqrt 2+-- > pt_distance (Pt 0 0) (Pt 0 1) == 1+-- > pt_distance (Pt 0 0) (Pt 1 1) == sqrt 2 pt_distance :: (Floating a,Eq a) => Pt a -> Pt a -> a-pt_distance = G.distFrom+pt_distance (Pt x1 y1) (Pt x2 y2) = pt_mag (Pt (x2 - x1) (y2 - y1))  -- | Are /x/ and /y/ of 'Pt' /p/ in range (0,1). ----- > map pt_is_normal [pt 0 0,pt 1 1,pt 2 2] == [True,True,False]+-- > map pt_is_normal [Pt 0 0,Pt 1 1,Pt 2 2] == [True,True,False] pt_is_normal :: (Ord a,Num a) => Pt a -> Bool-pt_is_normal p =-    let (x,y) = pt_xy p-    in x >= 0 && x <= 1 && y >= 0 && y <= 1+pt_is_normal (Pt x y) = x >= 0 && x <= 1 && y >= 0 && y <= 1  -- | Rotate 'Pt' /n/ radians. ----- > pt_rotate pi (pt 1 0) ~= pt (-1) 0+-- > pt_rotate pi (Pt 1 0) ~= Pt (-1) 0 pt_rotate :: Floating a => a -> Pt a -> Pt a-pt_rotate a p =-    let (x,y) = pt_xy p-        s = sin a+pt_rotate a (Pt x y) =+    let s = sin a         c = cos a-    in pt (x * c - y * s) (y * c + x * s)+    in Pt (x * c - y * s) (y * c + x * s)  -- * Vc functions --- | Construct 'Vc'.-vc :: Num a => a -> a -> Vc a-vc = curry G.makeRel2--- vc = Vc---- | Alernate constructor for 'Vc'.------ > (vc 0 0 == vc' (0,0)) == True-vc' :: Num a => (a,a) -> Vc a-vc' = G.makeRel2--- vc' = uncurry vc+-- | Unary operator at 'Vc', ie. basis for 'Num' instances.+vc_uop :: (a -> b) -> Vc a -> Vc b+vc_uop f (Vc x y) = Vc (f x) (f y) --- | 'Vc' /x/ field.-vc_x :: Vc t -> t-vc_x = G.getX+-- | Binary operator at 'Vc', ie. basis for 'Num' instances.+vc_binop :: (a -> b -> c) -> Vc a -> Vc b -> Vc c+vc_binop f (Vc x1 y1) (Vc x2 y2) = Vc (x1 `f` x2) (y1 `f` y2) --- | 'Vc' /y/ field.-vc_y :: Vc t -> t-vc_y = G.getY+-- | 'mag_sq' of 'Vc'.+vc_mag_sq :: Floating c => Vc c -> c+vc_mag_sq (Vc dx dy) = mag_sq dx dy --- | 'Vc' /x/ and /y/ fields.-vc_xy :: Vc a -> (a,a)-vc_xy v = (vc_x v,vc_y v)+-- | 'mag' of 'Vc'.+vc_mag :: Floating c => Vc c -> c+vc_mag (Vc dx dy) = mag dx dy  -- | Multiply 'Vc' pointwise by scalar. ----- > vc_scale 2 (vc 3 4) == vc 6 8+-- > vc_scale 2 (Vc 3 4) == Vc 6 8 vc_scale :: Num a => a -> Vc a -> Vc a-vc_scale = G.scaleRel--- vc_scale n v = vc (vc_x v * n) (vc_y v * n)+vc_scale n (Vc x y) = Vc (x * n) (y * n)  -- | 'Vc' dot product. ----- > vc_dot (vc 1 2) (vc 3 4) == 11+-- > vc_dot (Vc 1 2) (Vc 3 4) == 11 vc_dot :: Num a => Vc a -> Vc a -> a-vc_dot = G.dotProduct--- vc_dot p q = (vc_x p * vc_x q) + (vc_y p * vc_y q)+vc_dot (Vc x y) (Vc x' y') = (x * x') + (y * y')  -- | Scale 'Vc' to have unit magnitude (to within tolerance). ----- > vc_unit (vc 1 1) ~= let x = (sqrt 2) / 2 in vc x x+-- > vc_unit (Vc 1 1) ~= let x = (sqrt 2) / 2 in Vc x x vc_unit :: (Ord a, Floating a) => Vc a -> Vc a-vc_unit = G.unitVector+vc_unit v+    | abs (vc_mag_sq v - 1) < epsilon = v+    | vc_mag_sq v == 0 = v+    | otherwise = let Vc x y = v+                      m = mag x y+                  in Vc (x / m) (y / m)  -- | The angle between two vectors on a plane. The angle is from v1 to -- v2, positive anticlockwise.  The result is in (-pi,pi) vc_angle :: Vc R -> Vc R -> R-vc_angle v1 v2 =-    let (x1,y1) = vc_xy v1-        (x2,y2) = vc_xy v2-        t1 = atan2 y1 x1+vc_angle (Vc x1 y1) (Vc x2 y2) =+    let t1 = atan2 y1 x1         t2 = atan2 y2 x2     in r_constrain (-pi,pi) (t2 - t1)  -- * Line functions --- | 'Ln' constructor.------ > ln_start (ln (pt 0 0) (pt 1 1)) == pt 0 0-ln :: (Num a,Eq a) => Pt a -> Pt a -> Ln a-ln = G.lineTo---- | Variant constructor.------ > ln_start (ln_ (pt 1 1) (pt 0 0)) == pt 1 1-ln_ :: (Num a,Eq a) => Pt a -> Pt a -> Ln a-ln_ p q = G.Line2 p (G.fromPt q p)---- | Variant on 'ln' which takes 'Pt' co-ordinates as duples.+-- | Variant on 'Ln' which takes 'Pt' co-ordinates as duples. ----- > ln' (0,0) (1,1) == ln (pt 0 0) (pt 1 1)+-- > ln' (0,0) (1,1) == Ln (Pt 0 0) (Pt 1 1)+-- > ln_start (Ln (Pt 0 0) (Pt 1 1)) == Pt 0 0+-- > ln_end (Ln (Pt 0 0) (Pt 1 1)) == Pt 1 1 ln' :: (Num a,Eq a) => (a,a) -> (a,a) -> Ln a-ln' (x1,y1) (x2,y2) = ln (pt x1 y1) (pt x2 y2)---- | Initial 'Pt' of 'Ln'.------ > ln_start (ln (pt 0 0) (pt 1 1)) == pt 0 0-ln_start :: (Num a,Eq a) => Ln a -> Pt a-ln_start = G.getLineStart---- | Alternate implementation of 'ln_start' (without 'Num' constraint).-ln_start_ :: Ln a -> Pt a-ln_start_ (G.Line2 p _) = p---- | End 'Pt' of 'Ln'.------ > ln_end (ln (pt 0 0) (pt 1 1)) == pt 1 1-ln_end :: (Num a,Eq a) => Ln a -> Pt a-ln_end = G.getLineEnd---- | Alternate implementation of 'ln_end'.-ln_end_ :: (Num a,Eq a) => Ln a -> Pt a-ln_end_ (G.Line2 p v) = p `pt_translate` v+ln' (x1,y1) (x2,y2) = Ln (Pt x1 y1) (Pt x2 y2)  -- | 'Vc' that 'pt_translate's start 'Pt' to end 'Pt' of 'Ln'. ----- > let l = ln (pt 0 0) (pt 1 1)--- > in ln_start l `pt_translate` ln_vc l == pt 1 1+-- > let l = Ln (Pt 0 0) (Pt 1 1)+-- > in ln_start l `pt_translate` ln_vc l == Pt 1 1 ln_vc :: (Num a,Eq a) => Ln a -> Vc a-ln_vc = G.getLineDir+ln_vc (Ln p q) = let Pt x y = q - p in Vc x y --- | Alternate implementation of 'ln_vc', without 'Num' constraint.-ln_vc_ :: Ln a -> Vc a-ln_vc_ (G.Line2 _ v) = v+-- | 'Pt' UOp at 'Ln'.+ln_uop :: (Pt a -> Pt b) -> Ln a -> Ln b+ln_uop f (Ln l r) = Ln (f l) (f r) +-- | 'pt_scale' at 'Ln'.+ln_scale :: Num b => b -> Ln b -> Ln b+ln_scale m = ln_uop (pt_scale m)+ -- | The angle, in /radians/, anti-clockwise from the /x/-axis. -- -- > ln_angle (ln' (0,0) (0,0)) == 0@@ -419,151 +342,149 @@ -- > ln_angle (ln' (0,0) (0,1)) == pi/2 -- > ln_angle (ln' (0,0) (-1,1)) == pi * 3/4 ln_angle :: Ln R -> R-ln_angle = G.toAngle . ln_vc+ln_angle ln =+    let Vc dx dy = ln_vc ln+    in if dx == 0 && dy == 0 then 0 else atan2 dy dx  -- | Start and end points of 'Ln'. ----- > ln_pt (ln (pt 1 0) (pt 0 0)) == (pt 1 0,pt 0 0)-ln_pt :: (Num a,Eq a) => Ln a -> (Pt a, Pt a)-ln_pt l = (ln_start l,ln_end l)+-- > ln_pt (Ln (Pt 1 0) (Pt 0 0)) == (Pt 1 0,Pt 0 0)+ln_pt :: (Num a,Eq a) => Ln a -> (Pt a,Pt a)+ln_pt (Ln s e) = (s,e)  -- | Variant of 'ln_pt' giving co-ordinates as duples. ----- > ln_pt' (ln (pt 1 0) (pt 0 0)) == ((1,0),(0,0))+-- > ln_pt' (Ln (Pt 1 0) (Pt 0 0)) == ((1,0),(0,0)) ln_pt' :: (Num a,Eq a) => Ln a -> ((a,a),(a,a))-ln_pt' l =-    let (p1,p2) = ln_pt l-    in (pt_xy p1,pt_xy p2)+ln_pt' (Ln (Pt x1 y1) (Pt x2 y2)) = ((x1,y1),(x2,y2))  -- | Midpoint of a 'Ln'. ----- > ln_midpoint (ln (pt 0 0) (pt 2 1)) == pt 1 (1/2)+-- > ln_midpoint (Ln (Pt 0 0) (Pt 2 1)) == Pt 1 (1/2) ln_midpoint :: (Fractional a,Eq a) => Ln a -> Pt a-ln_midpoint l =-    let (p1,p2) = ln_pt l-        x = (pt_x p1 + pt_x p2) / 2-        y = (pt_y p1 + pt_y p2) / 2-    in pt x y+ln_midpoint (Ln (Pt x1 y1) (Pt x2 y2)) =+    let x = (x1 + x2) / 2+        y = (y1 + y2) / 2+    in Pt x y  -- | Variant on 'ln_midpoint'. ----- > cc_midpoint (Just (pt 0 0),Nothing) == pt 0 0--- > cc_midpoint (Nothing,Just (pt 2 1)) == pt 2 1--- > cc_midpoint (Just (pt 0 0),Just (pt 2 1)) == pt 1 (1/2)+-- > cc_midpoint (Just (Pt 0 0),Nothing) == Pt 0 0+-- > cc_midpoint (Nothing,Just (Pt 2 1)) == Pt 2 1+-- > cc_midpoint (Just (Pt 0 0),Just (Pt 2 1)) == Pt 1 (1/2) cc_midpoint :: (Maybe (Pt R), Maybe (Pt R)) -> Pt R cc_midpoint cc =     case cc of-      (Nothing,Nothing) -> pt 0 0+      (Nothing,Nothing) -> Pt 0 0       (Just p,Nothing) -> p       (Nothing, Just q) -> q-      (Just p, Just q) -> ln_midpoint (ln p q)+      (Just p, Just q) -> ln_midpoint (Ln p q)  -- | Magnitude of 'Ln', ie. length of line. ----- > ln_magnitude (ln (pt 0 0) (pt 1 1)) == sqrt 2--- > pt_x (pt_to_polar (pt 1 1)) == sqrt 2+-- > ln_magnitude (Ln (Pt 0 0) (Pt 1 1)) == sqrt 2+-- > pt_x (pt_to_polar (Pt 1 1)) == sqrt 2 ln_magnitude :: Ln R -> R-ln_magnitude = G.mag . ln_vc---- | Variant definition of 'ln_magnitude'.-ln_magnitude_ :: Ln R -> R-ln_magnitude_ l =-    let ((x1,y1),(x2,y2)) = ln_pt' l-        x = x2 - x1-        y = y2 - y1-    in sqrt (x * x + y * y)+ln_magnitude = vc_mag . ln_vc  -- | Order 'Pt' at 'Ln' so that /p/ is to the left of /q/.  If /x/ -- fields are equal, sort on /y/. ----- > ln_sort (ln (pt 1 0) (pt 0 0)) == ln (pt 0 0) (pt 1 0)--- > ln_sort (ln (pt 0 1) (pt 0 0)) == ln (pt 0 0) (pt 0 1)+-- > ln_sort (Ln (Pt 1 0) (Pt 0 0)) == Ln (Pt 0 0) (Pt 1 0)+-- > ln_sort (Ln (Pt 0 1) (Pt 0 0)) == Ln (Pt 0 0) (Pt 0 1) ln_sort :: (Num a,Ord a) => Ln a -> Ln a-ln_sort l =-    let (p,q) = ln_pt l-    in case compare (pt_x p) (pt_x q) of-         LT -> l-         EQ -> if pt_y p <= pt_y q then l else ln q p-         GT -> ln q p+ln_sort ln =+    let Ln p q = ln+        Pt x1 y1 = p+        Pt x2 y2 = q+    in case compare x1 x2 of+         LT -> ln+         EQ -> if y1 <= y2 then ln else Ln q p+         GT -> Ln q p  -- | Adjust 'Ln' to have equal starting 'Pt' but magnitude 'R'. ----- > ln_adjust (sqrt 2) (ln (pt 0 0) (pt 2 2)) == ln (pt 0 0) (pt 1 1)+-- > ln_adjust (sqrt 2) (Ln (Pt 0 0) (Pt 2 2)) == Ln (Pt 0 0) (Pt 1 1) ln_adjust :: (Floating a, Ord a) => a -> Ln a -> Ln a-ln_adjust = G.makeLength+ln_adjust z ln =+    let Ln p _ = ln+        v = vc_scale z (vc_unit (ln_vc ln))+    in Ln p (pt_translate p v)  -- | Extend 'Ln' by 'R', ie. 'ln_adjust' with /n/ added to -- 'ln_magnitude'. ----- > ln_extend (sqrt 2) (ln (pt 0 0) (pt 1 1)) ~= ln (pt 0 0) (pt 2 2)+-- > ln_extend (sqrt 2) (Ln (Pt 0 0) (Pt 1 1)) ~= Ln (Pt 0 0) (Pt 2 2) ln_extend :: R -> Ln R -> Ln R-ln_extend n l = ln (ln_start l) (pt_linear_extension n l)+ln_extend n l = Ln (ln_start l) (pt_linear_extension n l)  -- | Variant definition of 'ln_extend'. ----- > ln_extend_ (sqrt 2) (ln (pt 0 0) (pt 1 1)) == ln (pt 0 0) (pt 2 2)+-- > ln_extend_ (sqrt 2) (Ln (Pt 0 0) (Pt 1 1)) == Ln (Pt 0 0) (Pt 2 2) ln_extend_ :: R -> Ln R -> Ln R ln_extend_ n l = ln_adjust (n + ln_magnitude l) l +-- | Calculate the point that extends a line by length 'n'.+--+-- > pt_linear_extension (sqrt 2) (Ln (Pt 1 1) (Pt 2 2)) ~= Pt 3 3+-- > pt_linear_extension 1 (Ln (Pt 1 1) (Pt 1 2)) ~= Pt 1 3+pt_linear_extension :: R -> Ln R -> Pt R+pt_linear_extension n (Ln p q) =+    let Pt mg ph = pt_to_polar (q - p)+    in pt_from_polar (Pt (mg + n) ph) + p+ -- | Does 'Pt' /p/ lie on 'Ln' (inclusive). ----- > let f = pt_on_line (ln (pt 0 0) (pt 1 1))--- > in map f [pt 0.5 0.5,pt 2 2,pt (-1) (-1),pt 0 0] == [True,False,False,True]+-- > let {f = pt_on_line (Ln (Pt 0 0) (Pt 1 1))+-- >     ;r = [True,False,False,True]}+-- > in map f [Pt 0.5 0.5,Pt 2 2,Pt (-1) (-1),Pt 0 0] == r pt_on_line :: Ln R -> Pt R -> Bool pt_on_line l r =     let (p,q) = ln_pt l-        (i,j) = pt_xy (pt_to_polar (q `pt_sub` p))-        (i',j') = pt_xy (pt_to_polar (r `pt_sub` p))+        Pt i j = pt_to_polar (q - p)+        Pt i' j' = pt_to_polar (r - p)     in r == p || r == q || (j == j' && i' <= i) --- | Variant definition of 'pt_on_line', exclusive of starting point.------ > let f = pt_on_line_ (ln (pt 0 0) (pt 1 1))--- > in map f [pt 0.5 0.5,pt 2 2,pt (-1) (-1),pt 0 0] == [True,False,False,False]-pt_on_line_ :: Ln R -> Pt R -> Bool-pt_on_line_ l p =-    case G.distAlongLine p l of-      Nothing -> False-      Just d -> d >= 0 && d <= 1---- | Calculate the point that extends a line by length 'n'.------ > pt_linear_extension (sqrt 2) (ln (pt 0 0) (pt 1 1)) ~= pt 2 2-pt_linear_extension :: R -> Ln R -> Pt R-pt_linear_extension n l =-    let (p0,p1) = ln_pt l-        (mg,ph) = pt_xy (pt_to_polar (p1 `pt_sub` p0))-    in pt_from_polar (pt (mg+n) ph) `pt_add` p0- -- * Intersection --- | Given /left/ and /right/, is /x/ in range (inclusive).------ > map (in_range 0 1) [-1,0,1,2] == [False,True,True,False]-in_range :: Ord a => a -> a -> a -> Bool-in_range l r x = l <= x && x <= r+ln_intersect :: (Eq t, Fractional t) => Ln t -> Ln t -> Maybe (t,t)+ln_intersect l1 l2 =+    let Ln (Pt x1 y1) _ = l1+        Vc dx1 dy1 = ln_vc l1+        Ln (Pt x2 y2) _ = l2+        Vc dx2 dy2 = ln_vc l2+        a = (dx2 * dy1) - (dx1 * dy2)+        t' = ((dx1 * (y2 - y1)) - (dy1 * (x2 - x1))) / a+        t = ((dx2 * (y1 - y2)) - (dy2 * (x1 - x2))) / (negate a)+    in if a == 0 then Nothing else Just (t,t') +ln_pt_along :: (Eq a, Num a) => a -> Ln a -> Pt a+ln_pt_along z ln =+    let v = vc_scale z (ln_vc ln)+        Ln p _ = ln+    in pt_translate p v+ -- | Do two 'Ln's intersect, and if so at which 'Pt'. ----- > ln_intersection (ln' (0,0) (5,5)) (ln' (5,0) (0,5)) == Just (pt 2.5 2.5)+-- > ln_intersection (ln' (0,0) (5,5)) (ln' (5,0) (0,5)) == Just (Pt 2.5 2.5) -- > ln_intersection (ln' (1,3) (9,3)) (ln' (0,1) (2,1)) == Nothing -- > ln_intersection (ln' (1,5) (6,8)) (ln' (0.5,3) (6,4)) == Nothing -- > ln_intersection (ln' (1,2) (3,6)) (ln' (2,4) (4,8)) == Nothing -- > ln_intersection (ln' (2,3) (7,9)) (ln' (1,2) (5,7)) == Nothing--- > ln_intersection (ln' (0,0) (1,1)) (ln' (0,0) (1,0)) == Just (pt 0 0)+-- > ln_intersection (ln' (0,0) (1,1)) (ln' (0,0) (1,0)) == Just (Pt 0 0) ln_intersection :: (Ord a,Fractional a) => Ln a -> Ln a -> Maybe (Pt a) ln_intersection l0 l1 =-    case G.intersectLines2 l0 l1 of+    case ln_intersect l0 l1 of       Nothing -> Nothing       Just (i,j) -> if i >= 0 && i <= 1 && j >= 0 && j <= 1-                    then Just (G.alongLine i l0)+                    then Just (ln_pt_along i l0)                     else Nothing  -- | Variant definition of 'ln_intersection', using algorithm at -- <http://paulbourke.net/geometry/lineline2d/>. -- -- > ln_intersection_ (ln' (1,2) (3,6)) (ln' (2,4) (4,8)) == Nothing--- > ln_intersection_ (ln' (0,0) (1,1)) (ln' (0,0) (1,0)) == Just (pt 0 0)+-- > ln_intersection_ (ln' (0,0) (1,1)) (ln' (0,0) (1,0)) == Just (Pt 0 0) ln_intersection_ :: (Ord a,Fractional a) => Ln a -> Ln a -> Maybe (Pt a) ln_intersection_ l0 l1 =     let ((x1,y1),(x2,y2)) = ln_pt' l0@@ -574,21 +495,21 @@     in if d == 0        then Nothing        else if ua' == 0 && ub' == 0-            then Just (pt x1 y1)+            then Just (Pt x1 y1)             else let ua = ua' / d                      ub = ub' / d                  in if in_range 0 1 ua && in_range 0 1 ub                     then let x = x1 + ua * (x2 - x1)                              y = y1 + ua * (y2 - y1)-                         in Just (pt x y)+                         in Just (Pt x y)                     else Nothing  -- | Predicate variant of 'ln_intersection'. ----- > ln_intersect (ln' (1,1) (3,8)) (ln' (0.5,2) (4,7)) == True--- > ln_intersect (ln' (3.5,9) (3.5,0.5)) (ln' (3,1) (9,1)) == True-ln_intersect :: (Ord a, Fractional a) => Ln a -> Ln a -> Bool-ln_intersect l = isJust . ln_intersection l+-- > ln_intersect_p (ln' (1,1) (3,8)) (ln' (0.5,2) (4,7)) == True+-- > ln_intersect_p (ln' (3.5,9) (3.5,0.5)) (ln' (3,1) (9,1)) == True+ln_intersect_p :: (Ord a, Fractional a) => Ln a -> Ln a -> Bool+ln_intersect_p l = isJust . ln_intersection l  -- * Line slope @@ -620,18 +541,26 @@ ln_parallel_ :: Ln R -> Ln R -> Bool ln_parallel_ p q = ln_angle (ln_sort p) == ln_angle (ln_sort q) +-- | Are two vectors are in the same direction (to within a small+-- tolerance).+vc_same_direction :: (Ord a, Floating a) => Vc a -> Vc a -> Bool+vc_same_direction v w =+    let Vc dx1 dy1 = vc_unit v+        Vc dx2 dy2 = vc_unit w+    in abs (dx2 - dx1) < epsilon && abs (dy2 - dy1) < epsilon+ -- | Do 'Ln's have same direction (within tolerance). -- -- > ln_same_direction (ln' (0,0) (1,1)) (ln' (0,0) (2,2)) == True -- > ln_same_direction (ln' (0,0) (1,1)) (ln' (2,2) (0,0)) == False ln_same_direction :: (Ord a, Floating a) => Ln a -> Ln a -> Bool-ln_same_direction p q = ln_vc p `G.sameDirection` ln_vc q+ln_same_direction p q = ln_vc p `vc_same_direction` ln_vc q  -- | Are 'Ln's parallel, ie. does 'ln_vc' of each equal 'ln_same_direction'. -- -- > ln_parallel__ (ln' (0,0) (1,1)) (ln' (2,2) (1,1)) == True ln_parallel__ :: Ln R -> Ln R -> Bool-ln_parallel__ p q = ln_vc (ln_sort p) `G.sameDirection` ln_vc (ln_sort q)+ln_parallel__ p q = ln_vc (ln_sort p) `vc_same_direction` ln_vc (ln_sort q)  -- | Is 'Ln' horizontal, ie. is 'ln_slope' zero. --@@ -646,6 +575,18 @@ ln_vertical :: (Fractional a,Eq a) => Ln a -> Bool ln_vertical = (== Nothing) . ln_slope +-- * Ln sets++-- | 'pt_minmax' for set of 'Ln'.+lns_minmax :: [Ln R] -> (Pt R,Pt R)+lns_minmax = ls_minmax . concatMap (\(Ln l r) -> [l,r])++-- | Normalise to (0,m).+lns_normalise :: R -> [Ln R] -> [Ln R]+lns_normalise m l =+    let w = wn_from_extent (lns_minmax l)+    in map (ln_scale m . ln_normalise_w w) l+ -- * L(ine) s(egment) functions  -- | 'Ls' constructor.@@ -654,7 +595,7 @@  -- | Variant 'Ls' constructor from 'Pt' co-ordinates as duples. ls' :: [(a,a)] -> Ls a-ls' = map pt'+ls' = map (uncurry Pt)  -- | Negate /y/ elements. ls_negate_y :: (Num a) => Ls a -> Ls a@@ -667,64 +608,22 @@       [] -> undefined       p:ps -> foldl pt_minmax (p,p) ps --- | Split list at element where predicate /f/ over adjacent elements--- first holds.------ > split_f (\p q -> q - p < 3) [1,2,4,7,11] == ([1,2,4],[7,11])-split_f :: (a -> a -> Bool) -> [a] -> ([a],[a])-split_f f =-    let go i [] = (reverse i,[])-        go i [p] = (reverse (p:i), [])-        go i (p:q:r) =-            if f p q-            then go (p:i) (q:r)-            else (reverse (p:i),q:r)-    in go []---- | Variant on 'split_f' that segments input.------ > segment_f (\p q -> abs (q - p) < 3) [1,3,7,9,15] == [[1,3],[7,9],[15]]-segment_f :: (a -> a -> Bool) -> [a] -> [[a]]-segment_f f xs =-    let (p,q) = split_f f xs-    in if null q-       then [p]-       else p : segment_f f q- -- | Separate 'Ls' at points where the 'Vc' from one element to the -- next exceeds the indicated distance. ----- > map length (ls_separate (vc 2 2) (map pt' [(0,0),(1,1),(3,3)])) == [2,1]+-- > map length (ls_separate (Vc 2 2) (map (uncurry Pt) [(0,0),(1,1),(3,3)])) == [2,1] ls_separate :: (Ord a,Num a) => Vc a -> Ls a -> [Ls a]-ls_separate v =-    let (dx,dy) = vc_xy v-        f p0 p1 = let (x0,y0) = pt_xy p0-                      (x1,y1) = pt_xy p1-                  in abs (x1 - x0) < dx &&-                     abs (y1 - y0) < dy+ls_separate (Vc dx dy) =+    let f (Pt x0 y0) (Pt x1 y1) = abs (x1 - x0) < dx &&+                                  abs (y1 - y0) < dy     in segment_f f --- | Delete elements of a list using a predicate over the--- previous and current elements.-delete_f :: (a -> a -> Bool) -> [a] -> [a]-delete_f f =-    let go [] = []-        go [p] = [p]-        go (p:q:r) =-            if f p q-            then go (p:r)-            else p : go (q:r)-    in go- -- | Delete 'Pt' from 'Ls' so that no two 'Pt' are within a tolerance -- given by 'Vc'. ls_tolerate :: (Ord a,Num a) => Vc a -> Ls a -> Ls a-ls_tolerate v =-    let (x,y) = vc_xy v-        too_close p0 p1 =-            let (x0,y0) = pt_xy p0-                (x1,y1) = pt_xy p1-                dx = abs (x1 - x0)+ls_tolerate (Vc x y) =+    let too_close (Pt x0 y0) (Pt x1 y1) =+            let dx = abs (x1 - x0)                 dy = abs (y1 - y0)             in dx < x && dy < y     in delete_f too_close@@ -736,38 +635,26 @@       Nothing -> id       Just i' -> ls_tolerate i' --- | All adjacent pairs of a list.------ > pairs [1..5] == [(1,2),(2,3),(3,4),(4,5)]-pairs :: [x] -> [(x,x)]-pairs l =-    case l of-      x:y:z -> (x,y) : pairs (y:z)-      _ -> []- -- | Test if point 'Pt' lies inside polygon 'Ls'. ----- > ls_pt_inside (ls' [(0,0),(1,0),(1,1),(0,1)]) (pt 0.5 0.5) == True+-- > ls_pt_inside (ls' [(0,0),(1,0),(1,1),(0,1)]) (Pt 0.5 0.5) == True ls_pt_inside :: Ls R -> Pt R -> Bool-ls_pt_inside s p =-    let (x,y) = pt_xy p-    in case s of-         [] -> undefined-         l0:l -> let xs = pairs ((l0:l)++[l0])-                     f (p1,p2) =-                         let (x1,y1) = pt_xy p1-                             (x2,y2) = pt_xy p2-                         in and [y > min y1 y2-                                ,y <= max y1 y2-                                ,x <= max x1 x2-                                ,y1 /= y2-                                ,x1 == x2 ||-                                 x <= (y-y1)*(x2-x1)/(y2-y1)+x1]-                 in odd (length (filter id (map f xs)))+ls_pt_inside s (Pt x y) =+    case s of+      [] -> undefined+      l0:l -> let xs = pairs ((l0:l)++[l0])+                  f (Pt x1 y1,Pt x2 y2) =+                      and [y > min y1 y2+                          ,y <= max y1 y2+                          ,x <= max x1 x2+                          ,y1 /= y2+                          ,x1 == x2 ||+                           x <= (y-y1)*(x2-x1)/(y2-y1)+x1]+              in odd (length (filter id (map f xs)))  -- | Variant that counts points at vertices as inside. ----- > ls_pt_inside' (ls' [(0,0),(1,0),(1,1),(0,1)]) (pt 0 1) == True+-- > ls_pt_inside' (ls' [(0,0),(1,0),(1,1),(0,1)]) (Pt 0 1) == True ls_pt_inside' :: Ls R -> Pt R -> Bool ls_pt_inside' l p = p `elem` l || ls_pt_inside l p @@ -778,75 +665,64 @@       [] -> True       p:z -> pt_is_normal p && ls_check_normalised z --- * Window+-- | Line co-ordinates as /x/,/y/ list.+--+-- > ls_xy [Pt 0 0,Pt 1 1] == [0,0,1,1]+ls_xy :: Ls a -> [a]+ls_xy = concatMap (\(Pt x y) -> [x,y]) --- | 'Wn' constructor.-wn :: Pt a -> Vc a -> Wn a-wn p v = (p,v)+-- * Window  -- | Variant 'Wn' constructor. wn' :: Num a => (a,a) -> (a,a) -> Wn a-wn' (x,y) (i,j) = (pt x y,vc i j)---- | Lower-left 'Pt' of 'Wn'.-wn_ll :: Wn a -> Pt a-wn_ll (p,_) = p---- | Extent 'Vc' of 'Wn'.-wn_ex :: Wn a -> Vc a-wn_ex (_,v) = v+wn' (x,y) (i,j) = Wn (Pt x y) (Vc i j)  -- | Extract /(x,y)/ and /(dx,dy)/ pairs. ----- > wn_extract (wn (pt 0 0) (vc 1 1)) == ((0,0),(1,1))+-- > wn_extract (Wn (Pt 0 0) (Vc 1 1)) == ((0,0),(1,1)) wn_extract :: Wn a -> ((a,a),(a,a))-wn_extract (p,v) = (pt_xy p,vc_xy v)+wn_extract (Wn (Pt x y) (Vc dx dy)) = ((x,y),(dx,dy))  -- | Show function for window with fixed precision of 'n'. ----- > wn_show 1 (wn (pt 0 0) (vc 1 1)) == "((0.0,0.0),(1.0,1.0))"+-- > wn_show 1 (Wn (Pt 0 0) (Vc 1 1)) == "((0.0,0.0),(1.0,1.0))" wn_show :: Int -> Wn R -> String-wn_show n w =-    let ((x0,y0),(dx,dy)) = wn_extract w-        fs = printf "((%%.%df,%%.%df),(%%.%df,%%.%df))" n n n n+wn_show n (Wn (Pt x0 y0) (Vc dx dy)) =+    let fs = printf "((%%.%df,%%.%df),(%%.%df,%%.%df))" n n n n     in printf fs x0 y0 dx dy  -- | Is 'Pt' within 'Wn' exclusive of edge. ----- > map (pt_in_window (wn' (0,0) (1,1))) [pt' (0.5,0.5),pt' (1,1)] == [True,False]+-- > map (pt_in_window (wn' (0,0) (1,1))) [Pt 0.5 0.5,Pt 1 1] == [True,False] pt_in_window :: (Ord a,Num a) => Wn a -> Pt a -> Bool-pt_in_window w p =-    let ((lx,ly),(dx,dy)) = wn_extract w-        (x,y) = pt_xy p-        (ux,uy) = (lx+dx,ly+dy)+pt_in_window (Wn (Pt lx ly) (Vc dx dy)) (Pt x y) =+    let (ux,uy) = (lx+dx,ly+dy)     in x > lx && x < ux && y > ly && y < uy +-- | 'Wn' from /(lower-left,upper-right)/ extent.+wn_from_extent :: (Num a,Ord a) => (Pt a,Pt a) -> Wn a+wn_from_extent (Pt x0 y0,Pt x1 y1) = Wn (Pt x0 y0) (Vc (x1-x0) (y1-y0))+ -- | 'Wn' containing 'Ls'. -- -- > ls_window (ls' [(0,0),(1,1),(2,0)]) == wn' (0,0) (2,1) ls_window :: (Num a,Ord a) => Ls a -> Wn a-ls_window l =-    let (p0,p1) = ls_minmax l-        (x0,y0) = pt_xy p0-        (x1,y1) = pt_xy p1-    in (pt x0 y0,vc (x1-x0) (y1-y0))+ls_window = wn_from_extent . ls_minmax  -- | A 'Wn' that encompasses both input 'Wn's. wn_join :: (Num a,Ord a) => Wn a -> Wn a -> Wn a-wn_join w0 w1 =-    let ((x0,y0),(dx0,dy0)) = wn_extract w0-        ((x1,y1),(dx1,dy1)) = wn_extract w1-        x = min x0 x1+wn_join (Wn (Pt x0 y0) (Vc dx0 dy0)) (Wn (Pt x1 y1) (Vc dx1 dy1)) =+    let x = min x0 x1         y = min y0 y1         dx = max (x0+dx0) (x1+dx1) - x         dy = max (y0+dy0) (y1+dy1) - y-    in (pt x y,vc dx dy)+    in Wn (Pt x y) (Vc dx dy)  -- | Predictate to determine if two 'Wn's intersect. wn_intersect :: (Num a,Ord a) => Wn a -> Wn a -> Bool-wn_intersect w0 w1 =-    let ((x0,y0),(dx0,dy0)) = wn_extract w0-        ((x1,y1),(dx1,dy1)) = wn_extract w1+wn_intersect w0 w1  =+    let (Wn (Pt x0 y0) (Vc dx0 dy0)) = w0+        (Wn (Pt x1 y1) (Vc dx1 dy1)) = w1     in not (x0 > x1+dx1 || x1 > x0+dx0 || y0 > y1+dy1 || y1 > y0+dy0)  -- | Are all points at 'Ls' within the 'Wn'.@@ -869,19 +745,202 @@                in i : g (dropWhile (not . pt_in_window w) xs')     in filter (not . null) . g --- | Given a 'Wn' for a 'Ls', normalise the 'Ls' to lie within (0,1).+-- | Normalisation function for 'Wn', ie. map 'Pt' to lie within (0,1).+wn_normalise_f :: Wn R -> Pt R -> Pt R+wn_normalise_f (Wn (Pt x0 y0) (Vc dx dy)) (Pt x y) =+    let z = max dx dy+    in Pt ((x - x0) / z) ((y - y0) / z)++-- | Given 'Wn' normalise the 'Ls'. ls_normalise_w :: Wn R -> Ls R -> Ls R-ls_normalise_w w =-    let ((x0,y0),(dx,dy)) = wn_extract w-        z = max dx dy-        f p = let (x,y) = pt_xy p-              in pt ((x - x0) / z) ((y - y0) / z)-    in map f+ls_normalise_w w = map (wn_normalise_f w) --- | Shift lower left 'Pt' of 'Wn' by indicated 'Pt' (ie. 'pt_add').+-- | Given 'Wn' normalise 'Ln'.+ln_normalise_w :: Wn R -> Ln R -> Ln R+ln_normalise_w w (Ln p q) =+    let f = wn_normalise_f w+    in Ln (f p) (f q)++-- | Shift lower left 'Pt' of 'Wn' by indicated 'Pt'. pt_shift_w :: Num a => Pt a -> Wn a -> Wn a-pt_shift_w p (dp,ex) = (p `pt_add` dp,ex)+pt_shift_w p (Wn dp ex) = Wn (p + dp) ex  -- | Negate /y/ field of lower left 'Pt' of 'Wn'. wn_negate_y :: Num a => Wn a -> Wn a-wn_negate_y (p,v) = (pt_negate_y p,v)+wn_negate_y (Wn p v) = Wn (pt_negate_y p) v++-- * Matrix++-- | Transformation matrix data type.+data Matrix n = Matrix n n n n n n deriving (Eq,Show)++-- | Enumeration of 'Matrix' indices.+data Matrix_Index = I0 | I1 | I2++mx_row :: Num n => Matrix n -> Matrix_Index -> (n,n,n)+mx_row (Matrix a b c d e f) i =+    case i of+      I0 -> (a,b,0)+      I1 -> (c,d,0)+      I2 -> (e,f,1)++mx_col :: Num n => Matrix n -> Matrix_Index -> (n,n,n)+mx_col (Matrix a b c d e f) i =+    case i of+      I0 -> (a,c,e)+      I1 -> (b,d,f)+      I2 -> (0,0,1)++mx_multiply :: Num n => Matrix n -> Matrix n -> Matrix n+mx_multiply a b =+    let f i j = let (r1,r2,r3) = mx_row a i+                    (c1,c2,c3) = mx_col b j+                in r1 * c1 + r2 * c2 + r3 * c3+    in Matrix (f I0 I0) (f I0 I1) (f I1 I0) (f I1 I1) (f I2 I0) (f I2 I1)++-- | Pointwise unary operator.+mx_uop :: (n -> n) -> Matrix n -> Matrix n+mx_uop g (Matrix a b c d e f) =+    Matrix (g a) (g b) (g c) (g d) (g e) (g f)++-- | Pointwise binary operator.+mx_binop :: (n -> n -> n) -> Matrix n -> Matrix n -> Matrix n+mx_binop g (Matrix a b c d e f) (Matrix a' b' c' d' e' f') =+    Matrix (g a a') (g b b') (g c c') (g d d') (g e e') (g f f')++instance Num n => Num (Matrix n) where+    (*) = mx_multiply+    (+) = mx_binop (+)+    (-) = mx_binop (-)+    abs = mx_uop abs+    signum = mx_uop signum+    fromInteger n = let n' = fromInteger n+                    in Matrix n' 0 0 n' 0 0++-- | A translation matrix with independent x and y offsets.+mx_translation :: Num n => n -> n -> Matrix n+mx_translation = Matrix 1 0 0 1++-- | A scaling matrix with independent x and y scalars.+mx_scaling :: Num n => n -> n -> Matrix n+mx_scaling x y = Matrix x 0 0 y 0 0++-- | A rotation matrix through the indicated angle (in radians).+mx_rotation :: Floating n => n -> Matrix n+mx_rotation a =+    let c = cos a+        s = sin a+        t = negate s+    in Matrix c s t c 0 0++-- | The identity matrix.+mx_identity :: Num n => Matrix n+mx_identity = Matrix 1 0 0 1 0 0++mx_translate :: Num n => n -> n -> Matrix n -> Matrix n+mx_translate x y m = m * (mx_translation x y)++mx_scale :: Num n => n -> n -> Matrix n -> Matrix n+mx_scale x y m = m * (mx_scaling x y)++mx_rotate :: Floating n => n -> Matrix n -> Matrix n+mx_rotate r m = m * (mx_rotation r)++mx_scalar_multiply :: Num n => n -> Matrix n -> Matrix n+mx_scalar_multiply scalar = mx_uop (* scalar)++mx_adjoint :: Num n => Matrix n -> Matrix n+mx_adjoint (Matrix a b c d x y) =+    Matrix d (-b) (-c) a (c * y - d * x) (b * x - a * y)++mx_invert :: Fractional n => Matrix n -> Matrix n+mx_invert m =+    let Matrix xx yx xy yy _ _ = m+        d = xx*yy - yx*xy+    in mx_scalar_multiply (recip d) (mx_adjoint m)++mx_list :: Matrix n -> [n]+mx_list (Matrix a b c d e f) = [a,b,c,d,e,f]++-- | Apply a transformation matrix to a point.+pt_transform :: Num n => Matrix n -> Pt n -> Pt n+pt_transform (Matrix a b c d e f) (Pt x y) =+    let x' = x * a + y * c + e+        y' = x * b + y * d + f+    in Pt x' y'++-- * Bezier functions.++bezier3 :: Num n => Pt n -> Pt n -> Pt n -> n -> Pt n+bezier3 (Pt x1 y1) (Pt x2 y2) (Pt x3 y3) mu = (Pt x y)+    where a = mu*mu+          b = 1 - mu+          c = b*b+          x = x1*c + 2*x2*b*mu + x3*a+          y = y1*c + 2*y2*b*mu + y3*a++-- | Four-point bezier curve interpolation.  The index /mu/ is+--   in the range zero to one.+bezier4 :: Num n => Pt n -> Pt n -> Pt n -> Pt n -> n -> Pt n+bezier4 (Pt x1 y1) (Pt x2 y2) (Pt x3 y3) (Pt x4 y4) mu =+    let a = 1 - mu+        b = a*a*a+        c = mu*mu*mu+        x = b*x1 + 3*mu*a*a*x2 + 3*mu*mu*a*x3 + c*x4+        y = b*y1 + 3*mu*a*a*y2 + 3*mu*mu*a*y3 + c*y4+    in Pt x y++-- * Ord++-- | Given /left/ and /right/, is /x/ in range (inclusive).+--+-- > map (in_range 0 1) [-1,0,1,2] == [False,True,True,False]+in_range :: Ord a => a -> a -> a -> Bool+in_range l r x = l <= x && x <= r++-- * List++-- | Split list at element where predicate /f/ over adjacent elements+-- first holds.+--+-- > split_f (\p q -> q - p < 3) [1,2,4,7,11] == ([1,2,4],[7,11])+split_f :: (a -> a -> Bool) -> [a] -> ([a],[a])+split_f f =+    let go i [] = (reverse i,[])+        go i [p] = (reverse (p:i), [])+        go i (p:q:r) =+            if f p q+            then go (p:i) (q:r)+            else (reverse (p:i),q:r)+    in go []++-- | Variant on 'split_f' that segments input.+--+-- > segment_f (\p q -> abs (q - p) < 3) [1,3,7,9,15] == [[1,3],[7,9],[15]]+segment_f :: (a -> a -> Bool) -> [a] -> [[a]]+segment_f f xs =+    let (p,q) = split_f f xs+    in if null q+       then [p]+       else p : segment_f f q++-- | Delete elements of a list using a predicate over the+-- previous and current elements.+delete_f :: (a -> a -> Bool) -> [a] -> [a]+delete_f f =+    let go [] = []+        go [p] = [p]+        go (p:q:r) =+            if f p q+            then go (p:r)+            else p : go (q:r)+    in go++-- | All adjacent pairs of a list.+--+-- > pairs [1..5] == [(1,2),(2,3),(3,4),(4,5)]+pairs :: [x] -> [(x,x)]+pairs l =+    case l of+      x:y:z -> (x,y) : pairs (y:z)+      _ -> []
Data/CG/Minus/Arrow.hs view
@@ -6,7 +6,7 @@ -- | Given the arrow body 'Ln' and the arrow length and arrow angle -- (in radians) 'R' calculate the 'Pt' of each arrow tip. ----- > arrow_coord (ln' (0,0) (1,1)) 0.1 (pi/9)+-- > arrow_coord (Ln (Pt 0 0) (Pt 1 1)) 0.1 (pi/9) arrow_coord :: Ln R -> R -> R -> (Pt R,Pt R) arrow_coord l n a =     let ((x0,y0),(x1,y1)) = ln_pt' l@@ -15,4 +15,4 @@         y2 = y1 + n * sin (a' - a)         x3 = x1 + n * cos (a' + a)         y3 = y1 + n * sin (a' + a)-    in (pt x2 y2,pt x3 y3)+    in (Pt x2 y2,Pt x3 y3)
Data/CG/Minus/Bearing.hs view
@@ -12,7 +12,7 @@  -- | Bearing from 'Pt' /p/ to /q/. ----- > let f (x,y) = bearing (pt 0 0) (pt x y)+-- > let f (x,y) = bearing (Pt 0 0) (Pt x y) -- > map f [(0,1),(1,1),(1,0),(1,-1)] == [N,NE,E,SE] -- > map f [(0,-1),(-1,-1),(-1,0),(-1,1)] == [S,SW,W,NW] -- > map f [(1/4,1),(1,1/4),(1,-1/4),(1/4,-1)] == [NNE,ENE,ESE,SSE]@@ -25,7 +25,7 @@  -- | Bearing to nearest eight point compass bearing ----- > let f (x,y) = bearing_8 (pt 0 0) (pt x y)+-- > let f (x,y) = bearing_8 (Pt 0 0) (Pt x y) -- > map f [(1/4,1),(1,1/4),(1,-1/4),(1/4,-1)] == [N,E,E,S] bearing_8 :: Pt R -> Pt R -> Bearing bearing_8 p q =
Data/CG/Minus/Colour.hs view
@@ -2,8 +2,8 @@ module Data.CG.Minus.Colour where  import Data.Colour {- colour -}-import Data.Colour.SRGB-import Data.Colour.Names as N+import qualified Data.Colour.SRGB as S {- colour -}+import qualified Data.Colour.Names as N {- colour -}  -- | Opaque colour. type C = Colour Double@@ -13,12 +13,12 @@  -- | Grey 'Colour'. mk_grey :: (Ord a,Floating a) => a -> Colour a-mk_grey x = sRGB x x x+mk_grey x = S.sRGB x x x  -- | Reduce 'Colour' to grey.  Constants are @0.3@, @0.59@ and @0.11@. to_greyscale :: (Ord a,Floating a) => Colour a -> a to_greyscale c =-    let (RGB r g b) = toSRGB c+    let (S.RGB r g b) = S.toSRGB c     in r * 0.3 + g * 0.59 + b * 0.11  -- | 'mk_grey' '.' 'to_greyscale'.@@ -37,13 +37,13 @@  -- | Tuple to 'C', inverse of 'unC'. toC :: (Double,Double,Double) -> C-toC (r,g,b) = sRGB r g b+toC (r,g,b) = S.sRGB r g b  -- | 'C' to /(red,green,blue)/ tuple. unC :: C -> (Double,Double,Double) unC x =-    let x' = toSRGB x-    in (channelRed x', channelGreen x', channelBlue x')+    let x' = S.toSRGB x+    in (S.channelRed x',S.channelGreen x',S.channelBlue x')  -- | Tuple to 'Ca', inverse of 'unCa'. toCa :: (Double,Double,Double,Double) -> Ca@@ -52,42 +52,45 @@ -- | 'Ca' to /(red,green,blue,alpha)/ tuple unCa :: Ca -> (Double,Double,Double,Double) unCa x =-    let x' = toSRGB (pureColour x)-    in (channelRed x', channelGreen x', channelBlue x', alphaChannel x)+    let x' = S.toSRGB (pureColour x)+    in (S.channelRed x'+       ,S.channelGreen x'+       ,S.channelBlue x'+       ,alphaChannel x)  -- * Constants  -- | Venetian red (@#c80815@). venetianRed :: C-venetianRed = sRGB24read "#c80815"+venetianRed = S.sRGB24read "#c80815"  -- | Swedish azure blue (@#005b99@). swedishAzureBlue :: C-swedishAzureBlue = sRGB24read "#005b99"+swedishAzureBlue = S.sRGB24read "#005b99"  -- | Safety orange (@#ff6600@). safetyOrange :: C-safetyOrange = sRGB24read "#ff6600"+safetyOrange = S.sRGB24read "#ff6600"  -- | Dye magenta (@#ca1f7b@). dyeMagenta :: C-dyeMagenta = sRGB24read "#ca1f7b"+dyeMagenta = S.sRGB24read "#ca1f7b"  -- | Candlelight yellow (@#fcd116@). candlelightYellow :: C-candlelightYellow = sRGB24read "#fcd116"+candlelightYellow = S.sRGB24read "#fcd116"  -- | Subtractive primary cyan (@#00B7EB@). subtractivePrimaryCyan :: C-subtractivePrimaryCyan = sRGB24read "#00B7EB"+subtractivePrimaryCyan = S.sRGB24read "#00B7EB"  -- | Fern green (@#009246@). fernGreen :: C-fernGreen = sRGB24read "#009246"+fernGreen = S.sRGB24read "#009246"  -- | Sepia brown (@#704214@). sepiaBrown :: C-sepiaBrown = sRGB24read "#704214"+sepiaBrown = S.sRGB24read "#704214"  -- | The set of named colours defined in this module. non_svg_colour_set :: [C]@@ -107,150 +110,150 @@ -- alphabetical order). svg_colour_set :: [C] svg_colour_set =-    [aliceblue-    ,antiquewhite-    ,aqua-    ,aquamarine-    ,azure-    ,beige-    ,bisque-    ,black-    ,blanchedalmond-    ,blue-    ,blueviolet-    ,brown-    ,burlywood-    ,cadetblue-    ,chartreuse-    ,chocolate-    ,coral-    ,cornflowerblue-    ,cornsilk-    ,crimson-    ,cyan-    ,darkblue-    ,darkcyan-    ,darkgoldenrod-    ,darkgray-    ,darkgreen-    ,darkgrey-    ,darkkhaki-    ,darkmagenta-    ,darkolivegreen-    ,darkorange-    ,darkorchid-    ,darkred-    ,darksalmon-    ,darkseagreen-    ,darkslateblue-    ,darkslategray-    ,darkslategrey-    ,darkturquoise-    ,darkviolet-    ,deeppink-    ,deepskyblue-    ,dimgray-    ,dimgrey-    ,dodgerblue-    ,firebrick-    ,floralwhite-    ,forestgreen-    ,fuchsia-    ,gainsboro-    ,ghostwhite-    ,gold-    ,goldenrod-    ,gray-    ,grey-    ,green-    ,greenyellow-    ,honeydew-    ,hotpink-    ,indianred-    ,indigo-    ,ivory-    ,khaki-    ,lavender-    ,lavenderblush-    ,lawngreen-    ,lemonchiffon-    ,lightblue-    ,lightcoral-    ,lightcyan-    ,lightgoldenrodyellow-    ,lightgray-    ,lightgreen-    ,lightgrey-    ,lightpink-    ,lightsalmon-    ,lightseagreen-    ,lightskyblue-    ,lightslategray-    ,lightslategrey-    ,lightsteelblue-    ,lightyellow-    ,lime-    ,limegreen-    ,linen-    ,magenta-    ,maroon-    ,mediumaquamarine-    ,mediumblue-    ,mediumorchid-    ,mediumpurple-    ,mediumseagreen-    ,mediumslateblue-    ,mediumspringgreen-    ,mediumturquoise-    ,mediumvioletred-    ,midnightblue-    ,mintcream-    ,mistyrose-    ,moccasin-    ,navajowhite-    ,navy-    ,oldlace-    ,olive-    ,olivedrab-    ,orange-    ,orangered-    ,orchid-    ,palegoldenrod-    ,palegreen-    ,paleturquoise-    ,palevioletred-    ,papayawhip-    ,peachpuff-    ,peru-    ,pink-    ,plum-    ,powderblue-    ,purple-    ,red-    ,rosybrown-    ,royalblue-    ,saddlebrown-    ,salmon-    ,sandybrown-    ,seagreen-    ,seashell-    ,sienna-    ,silver-    ,skyblue-    ,slateblue-    ,slategray-    ,slategrey-    ,snow-    ,springgreen-    ,steelblue+    [N.aliceblue+    ,N.antiquewhite+    ,N.aqua+    ,N.aquamarine+    ,N.azure+    ,N.beige+    ,N.bisque+    ,N.black+    ,N.blanchedalmond+    ,N.blue+    ,N.blueviolet+    ,N.brown+    ,N.burlywood+    ,N.cadetblue+    ,N.chartreuse+    ,N.chocolate+    ,N.coral+    ,N.cornflowerblue+    ,N.cornsilk+    ,N.crimson+    ,N.cyan+    ,N.darkblue+    ,N.darkcyan+    ,N.darkgoldenrod+    ,N.darkgray+    ,N.darkgreen+    ,N.darkgrey+    ,N.darkkhaki+    ,N.darkmagenta+    ,N.darkolivegreen+    ,N.darkorange+    ,N.darkorchid+    ,N.darkred+    ,N.darksalmon+    ,N.darkseagreen+    ,N.darkslateblue+    ,N.darkslategray+    ,N.darkslategrey+    ,N.darkturquoise+    ,N.darkviolet+    ,N.deeppink+    ,N.deepskyblue+    ,N.dimgray+    ,N.dimgrey+    ,N.dodgerblue+    ,N.firebrick+    ,N.floralwhite+    ,N.forestgreen+    ,N.fuchsia+    ,N.gainsboro+    ,N.ghostwhite+    ,N.gold+    ,N.goldenrod+    ,N.gray+    ,N.grey+    ,N.green+    ,N.greenyellow+    ,N.honeydew+    ,N.hotpink+    ,N.indianred+    ,N.indigo+    ,N.ivory+    ,N.khaki+    ,N.lavender+    ,N.lavenderblush+    ,N.lawngreen+    ,N.lemonchiffon+    ,N.lightblue+    ,N.lightcoral+    ,N.lightcyan+    ,N.lightgoldenrodyellow+    ,N.lightgray+    ,N.lightgreen+    ,N.lightgrey+    ,N.lightpink+    ,N.lightsalmon+    ,N.lightseagreen+    ,N.lightskyblue+    ,N.lightslategray+    ,N.lightslategrey+    ,N.lightsteelblue+    ,N.lightyellow+    ,N.lime+    ,N.limegreen+    ,N.linen+    ,N.magenta+    ,N.maroon+    ,N.mediumaquamarine+    ,N.mediumblue+    ,N.mediumorchid+    ,N.mediumpurple+    ,N.mediumseagreen+    ,N.mediumslateblue+    ,N.mediumspringgreen+    ,N.mediumturquoise+    ,N.mediumvioletred+    ,N.midnightblue+    ,N.mintcream+    ,N.mistyrose+    ,N.moccasin+    ,N.navajowhite+    ,N.navy+    ,N.oldlace+    ,N.olive+    ,N.olivedrab+    ,N.orange+    ,N.orangered+    ,N.orchid+    ,N.palegoldenrod+    ,N.palegreen+    ,N.paleturquoise+    ,N.palevioletred+    ,N.papayawhip+    ,N.peachpuff+    ,N.peru+    ,N.pink+    ,N.plum+    ,N.powderblue+    ,N.purple+    ,N.red+    ,N.rosybrown+    ,N.royalblue+    ,N.saddlebrown+    ,N.salmon+    ,N.sandybrown+    ,N.seagreen+    ,N.seashell+    ,N.sienna+    ,N.silver+    ,N.skyblue+    ,N.slateblue+    ,N.slategray+    ,N.slategrey+    ,N.snow+    ,N.springgreen+    ,N.steelblue     ,N.tan-    ,teal-    ,thistle-    ,tomato-    ,turquoise-    ,violet-    ,wheat-    ,white-    ,whitesmoke-    ,yellow-    ,yellowgreen]+    ,N.teal+    ,N.thistle+    ,N.tomato+    ,N.turquoise+    ,N.violet+    ,N.wheat+    ,N.white+    ,N.whitesmoke+    ,N.yellow+    ,N.yellowgreen]
Data/CG/Minus/Colour/Planck.hs view
@@ -1,14 +1,13 @@ -- | Planck radiation equation. module Data.CG.Minus.Colour.Planck where -import Data.CG.Minus (R) import Data.CG.Minus.Colour  -- | Given wavelength (in microns) and temperature (in degrees Kelvin) -- solve Planck's radiation equation. -- -- > planck_rad_eq 0.7 2600 == 8.22656629154115e7-planck_rad_eq :: R -> R -> R+planck_rad_eq :: Floating a => a -> a -> a planck_rad_eq l t =     let k0 = 3.7403e10         k1 = -5.0@@ -28,7 +27,7 @@ -- -- > let h (r,g,b) = let f = floor . (*) 255 in (f r,f g,f b) -- > in map (h . k_to_rgb) [2600,28000] == [(255,95,22),(49,118,254)]-k_to_rgb :: R -> (R,R,R)+k_to_rgb :: (Floating t, Ord t) => t -> (t,t,t) k_to_rgb k =     let r = planck_rad_eq 0.7000 k         g = planck_rad_eq 0.5461 k@@ -37,5 +36,5 @@     in (r * s,g * s,b * s)  -- | 'toC' '.' 'k_to_rgb'.-k_to_colour :: R -> C+k_to_colour :: Double -> C k_to_colour = toC . k_to_rgb
README view
@@ -5,7 +5,7 @@  [hs]: http://haskell.org/ -© [rohan drape][rd], 2009-2012, [gpl]+© [rohan drape][rd], 2009-2013, [gpl]  [rd]: http://rd.slavepianos.org/ [gpl]: http://gnu.org/copyleft/
hcg-minus.cabal view
@@ -1,10 +1,10 @@ name:              hcg-minus-version:           0.12+version:           0.14 synopsis:          haskell cg (minus) description:       cg (minus) library license:           BSD3 category:          Math-copyright:         (c) rohan drape, 2011-2012+copyright:         (c) rohan drape, 2011-2013 author:            Rohan Drape maintainer:        rd@slavepianos.org stability:         Experimental@@ -16,7 +16,7 @@ data-files:        README  library-  build-depends:   base==4.*,colour,SG+  build-depends:   base==4.*,colour   ghc-options:     -Wall -fwarn-tabs   exposed-modules: Data.CG.Minus                    Data.CG.Minus.Arrow