From 8438e1604767ae5210432d5e920069f767d60343 Mon Sep 17 00:00:00 2001 From: Jaro Date: Sun, 19 Feb 2023 10:37:17 +0100 Subject: [PATCH] Add module for working with rectangle coordinates. --- balkon.cabal | 2 + src/Data/Text/ParagraphLayout/Rect.hs | 67 ++++++++++++++++++++++ test/Data/Text/ParagraphLayout/RectSpec.hs | 29 ++++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/Data/Text/ParagraphLayout/Rect.hs create mode 100644 test/Data/Text/ParagraphLayout/RectSpec.hs diff --git a/balkon.cabal b/balkon.cabal index 7e91dfc..d9e211a 100644 --- a/balkon.cabal +++ b/balkon.cabal @@ -97,6 +97,7 @@ library -- Modules exported by the library. exposed-modules: Data.Text.ParagraphLayout, + Data.Text.ParagraphLayout.Rect, Data.Text.ParagraphLayout.Run, Data.Text.ParagraphLayout.Span @@ -126,6 +127,7 @@ test-suite balkon-test other-modules: Data.Text.ParagraphLayoutSpec, Data.Text.ParagraphLayout.FontLoader, + Data.Text.ParagraphLayout.RectSpec, Data.Text.ParagraphLayout.RunSpec, Data.Text.ParagraphLayout.SpanData diff --git a/src/Data/Text/ParagraphLayout/Rect.hs b/src/Data/Text/ParagraphLayout/Rect.hs new file mode 100644 index 0000000..035835d --- /dev/null +++ b/src/Data/Text/ParagraphLayout/Rect.hs @@ -0,0 +1,67 @@ +-- | Representation of an axis-aligned rectangle on a 2D plane, with one of its +-- corners being a designated origin point. +module Data.Text.ParagraphLayout.Rect + (Rect(..) + ,height + ,union + ,width + ,x_max + ,x_min + ,x_terminus + ,y_max + ,y_min + ,y_terminus + ) +where + +data Rect a = Rect { x_origin :: a, y_origin :: a, x_size :: a, y_size :: a } + deriving (Eq, Read, Show) + +-- | Absolute difference between the X coordinates of the rectangle's sides. +width :: Num a => Rect a -> a +width r = abs $ x_size r + +-- | Absolute difference between the X coordinates of the rectangle's sides. +height :: Num a => Rect a -> a +height r = abs $ y_size r + +-- | X coordinate of the corner opposite of the origin. +x_terminus :: Num a => Rect a -> a +x_terminus r = x_origin r + x_size r + +-- | Y coordinate of the corner opposite of the origin. +y_terminus :: Num a => Rect a -> a +y_terminus r = y_origin r + y_size r + +-- | The smaller of the two X coordinates of the rectangle's edges. +x_min :: (Num a, Ord a) => Rect a -> a +x_min r = x_origin r `min` x_terminus r + +-- | The smaller of the two Y coordinates of the rectangle's edges. +y_min :: (Num a, Ord a) => Rect a -> a +y_min r = y_origin r `min` y_terminus r + +-- | The larger of the two X coordinates of the rectangle's edges. +x_max :: (Num a, Ord a) => Rect a -> a +x_max r = x_origin r `max` x_terminus r + +-- | The larger of the two Y coordinates of the rectangle's edges. +y_max :: (Num a, Ord a) => Rect a -> a +y_max r = y_origin r `max` y_terminus r + +-- | Calculate the smallest rectangle that completely contains the given two +-- rectangles. +-- +-- The origin of the resulting rectangle will be the corner with the lowest +-- X and Y coordinates, regardless of the origin of the input rectangles. +union :: (Num a, Ord a) => Rect a -> Rect a -> Rect a +union a b = Rect x1 y1 dx dy where + x1 = x_min a `min` x_min b + y1 = y_min a `min` y_min b + x2 = x_max a `max` x_max b + y2 = y_max a `max` y_max b + dx = x2 - x1 + dy = y2 - y1 + +instance (Num a, Ord a) => Semigroup (Rect a) where + (<>) = union diff --git a/test/Data/Text/ParagraphLayout/RectSpec.hs b/test/Data/Text/ParagraphLayout/RectSpec.hs new file mode 100644 index 0000000..8d06cf6 --- /dev/null +++ b/test/Data/Text/ParagraphLayout/RectSpec.hs @@ -0,0 +1,29 @@ +module Data.Text.ParagraphLayout.RectSpec (spec) where + +import Data.Int (Int32) + +import Test.Hspec +import Data.Text.ParagraphLayout.Rect + +positiveRect :: Rect Int32 +positiveRect = Rect 50 60 10 10 + +negativeRect :: Rect Int32 +negativeRect = Rect 80 90 (-15) (-15) + +spec :: Spec +spec = do + describe "union of two rects" $ do + let r = union positiveRect negativeRect + it "has origin at 50,60" $ + (x_origin r, y_origin r) `shouldBe` (50, 60) + it "has minimum coordinates at at 50,60" $ + (x_min r, y_min r) `shouldBe` (50, 60) + it "has terminus at 80,90" $ + (x_terminus r, y_terminus r) `shouldBe` (80, 90) + it "has maximum coordinates at 80,90" $ + (x_max r, y_max r) `shouldBe` (80, 90) + it "has size 30,30" $ + (x_size r, y_size r) `shouldBe` (30, 30) + it "has absolute size 30,30" $ + (width r, height r) `shouldBe` (30, 30) -- 2.30.2