diff --git a/src/Polygon2d.elm b/src/Polygon2d.elm index 13ca1de1..574bad14 100644 --- a/src/Polygon2d.elm +++ b/src/Polygon2d.elm @@ -9,7 +9,7 @@ module Polygon2d exposing ( Polygon2d - , singleLoop, withHoles, convexHull + , singleLoop, withHoles, convexHull, regular , outerLoop, innerLoops, vertices, edges, perimeter, area, centroid, boundingBox , contains , scaleAbout, rotateAround, translateBy, translateIn, mirrorAcross @@ -32,7 +32,7 @@ holes. This module contains a variety of polygon-related functionality, such as # Constructors -@docs singleLoop, withHoles, convexHull +@docs singleLoop, withHoles, convexHull, regular # Properties @@ -261,6 +261,31 @@ convexHull points = singleLoop (lower ++ upper) +{-| Constructs a regular convex polygon with the specified center point, +circumradius and number of sides. The polygon will be oriented so that the +bottom edge is horizontal (assuming a Y-up coordinate system; note that SVG +uses Y-down instead!). +-} +regular : { centerPoint : Point2d units coordinates, circumradius : Quantity Float units, numSides : Int } -> Polygon2d units coordinates +regular { centerPoint, circumradius, numSides } = + let + n = + toFloat numSides + + angle = + Angle.turns 1 |> Quantity.divideBy n + + startAngle = + Angle.degrees -90 |> Quantity.plus (Angle.degrees 180 |> Quantity.divideBy n) + + point index = + Point2d.translateBy (Vector2d.rTheta circumradius (Quantity.plus startAngle (Quantity.multiplyBy index angle))) centerPoint + in + List.range 0 (numSides - 1) + |> List.map (toFloat >> point) + |> singleLoop + + {-| Convert a polygon from one units type to another, by providing a conversion factor given as a rate of change of destination units with respect to source units. diff --git a/tests/Tests/Polygon2d.elm b/tests/Tests/Polygon2d.elm index d8ec8962..305ba0bc 100644 --- a/tests/Tests/Polygon2d.elm +++ b/tests/Tests/Polygon2d.elm @@ -3,13 +3,14 @@ module Tests.Polygon2d exposing , convexHullContainsAllPoints , convexHullIsConvex , rectangleCentroidIsInTheCenter + , regularTest , rotatingAroundCentroidKeepsCentroid , triangulationHasCorrectArea , triangulationHasCorrectNumberOfTriangles , triangulationHasCorrectWeightedCentroid ) -import Area +import Angle import Expect import Fuzz import Geometry.Expect as Expect @@ -26,6 +27,51 @@ import TriangularMesh import Vector2d +{-| Implements the formula for area of a regular polygon. +-} +areaOfRegularNGon : Quantity.Quantity Float units -> Float -> Quantity.Quantity Float (Quantity.Squared units) +areaOfRegularNGon radius sides = + Quantity.squared radius + |> Quantity.multiplyBy sides + |> Quantity.multiplyBy (Angle.sin (Angle.turns (1 / sides))) + |> Quantity.divideBy 2 + + +regularTest : Test +regularTest = + Test.describe "regular" + [ Test.fuzz3 Fuzz.point2d + Fuzz.length + (Fuzz.intRange 3 300) + "A centroid of a regular polygon is in the center" + <| + \center radius sides -> + Polygon2d.regular { centerPoint = center, circumradius = radius, numSides = sides } + |> Polygon2d.centroid + |> Expect.just (Expect.point2d center) + , Test.fuzz3 Fuzz.point2d + Fuzz.length + (Fuzz.intRange 3 300) + "The area matches what we would expect from a regular polygon" + <| + \center radius sides -> + Polygon2d.regular { centerPoint = center, circumradius = radius, numSides = sides } + |> Polygon2d.area + |> Expect.quantity (areaOfRegularNGon radius (toFloat sides)) + , Test.test "sanity check" <| + \() -> + Polygon2d.regular { centerPoint = Point2d.meters 0.5 0.5, circumradius = meters (sqrt 2 / 2), numSides = 4 } + |> Expect.polygon2d + (Polygon2d.singleLoop + [ Point2d.meters 1 0 + , Point2d.meters 1 1 + , Point2d.meters 0 1 + , Point2d.meters 0 0 + ] + ) + ] + + convexHullIsConvex : Test convexHullIsConvex = Test.fuzz (Fuzz.list Fuzz.point2d)