curves-1.1.0: Graphics/Curves/Geometry.hs
{-# LANGUAGE MultiWayIf #-}
{-| Combinators for drawing various geometric figures.
-}
module Graphics.Curves.Geometry where
import Graphics.Curves.Math
import Graphics.Curves.Image
import Graphics.Curves.Curve
import Graphics.Curves.Attribute
import Graphics.Curves.Text
-- | Draw an axis-aligned rectangle with the given opposite corners.
rectangle :: Point -> Point -> Image
rectangle p q = poly [p, Vec (getX q) (getY p), q, Vec (getX p) (getY q)]
type Length = Scalar
type Angle = Scalar
-- | Draw a triangle with the given side lengths. The corner opposite the third
-- side is located at the origin and the first side is drawn along the
-- x-axis.
triangle :: Length -> Length -> Length -> Image
triangle a b c
| a + b < c ||
b + c < a ||
c + a < b = error $ unwords ["not a triangle:", show a, show b, show c]
| otherwise = triangleA a b $ acos $ (a^2 + b^2 - c^2) / (2 * a * b)
-- | Draw a triangle given by two side lengths and an angle. The corner of the
-- angle is drawn at the origin and the first side along the x-axis.
triangleA :: Length -> Length -> Angle -> Image
triangleA a b α = poly [0, Vec a 0, rotate α $ Vec b 0]
-- | Draw a triangle given by two angles and the length of the side shared by
-- the angles. The first angle is drawn at the origin and the side along the
-- x-axis.
triangleAA :: Length -> Angle -> Angle -> Image
triangleAA a α β = triangleA a (a * sin β / sin (α + β)) α
-- | Draw an n-sided regular polygon centered at the origin and one corner at
-- 'unitY'.
regularPoly :: Int -> Image
regularPoly n | n < 3 = error "regularPoly: n < 3"
regularPoly n = poly [ rotate (2 * pi * fromIntegral i / fromIntegral n) unitY | i <- [0..n - 1] ]
-- | Draw an angle arc for the counter clockwise angle BAC.
angleArc :: Scalar -- ^ Radius of the arc in pixels
-> Point -- ^ A
-> Point -- ^ B
-> Point -- ^ C
-> Image
angleArc w p0 p1 p2 = curve' 0 1 f g
where
f _ = (p0, p1, p2)
g t (p0, p1, p2) = p0 + diag w * rotate (t * α) (norm (p1 - p0))
where
α = angle (p1 - p0) (p2 - p0)
-- | Draw an angle arc labelled by the given string.
labelledAngle :: String -> Vec -- ^ Position of the text relative to the angle
-> Point -> Point -> Point -> Image
labelledAngle s d p0 p1 p2 =
angleArc 20 p0 p1 p2
<> freezeImageSize p0 (label (p0 + d * norm (v1 + v2)) 14 s)
where
v1 = norm $ p1 - p0
v2 = norm $ p2 - p0
-- | Draw a line segment with an arrow head.
arrow :: Point -> Point -> Image
arrow from to = curve' 0 2 f g <> line from to
where
f = const $ Seg from to
g t (Seg from to) =
if | t <= 1 -> interpolate fin1 to t
| otherwise -> interpolate to fin2 (t - 1)
where
v = norm (from - to)
fin1 = to + 20 * rotate (pi/6) v
fin2 = to + 20 * rotate (-pi/6) v