Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created 'MonotonePolygon' #95

Merged
merged 37 commits into from
Jan 18, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4d49f79
Created 'MonotonePolygon'
1ndy Jan 10, 2021
08db24a
Skeleton for the implementation of monotone polygons
lemmih Jan 10, 2021
be3107f
Add MonotonePolygon to exposed modules.
lemmih Jan 10, 2021
9757fff
Add 'randomMonotoneAny' variant.
lemmih Jan 10, 2021
5511fc0
Implement 'isMonotone'. Will be used in testing.
lemmih Jan 10, 2021
f437e48
Rename 'randomMonotoneAny' to 'randomMonotoneDirected'
lemmih Jan 10, 2021
416352e
defined randomeMonotone
1ndy Jan 12, 2021
af6202b
Implemented randomMonotone
1ndy Jan 12, 2021
c07e9d8
Implemented randomMonotoneDirected
1ndy Jan 12, 2021
8bfde07
cleaned up module definition and imports
1ndy Jan 12, 2021
2927ed0
Resolved name conflicts for intersect, minimumBy, MaximumBy, replicate
1ndy Jan 12, 2021
a9da834
Corrected type signuature for linearInterpolation function
1ndy Jan 12, 2021
b3173e9
imported Data.Geometry.Polygon.Extreme
1ndy Jan 13, 2021
5245fcb
Corrected typo in import
1ndy Jan 13, 2021
4f55098
Trying to convert list to Vector 2 Rational
1ndy Jan 13, 2021
bc9d3f3
refactored randomMonotone
1ndy Jan 15, 2021
f6d3e09
fixed conflicting definitions of rightHalf
1ndy Jan 15, 2021
9cefd40
changed replicate to replicateM
1ndy Jan 15, 2021
6cd58af
Merge branch 'master' of https://github.com/noinia/hgeometry into mon…
1ndy Jan 15, 2021
51567cc
Successful compile
1ndy Jan 16, 2021
d63a7c4
Changed ccw to ccw'
1ndy Jan 16, 2021
fedd832
Merge branch 'master' of github.com:noinia/hgeometry into monotone-po…
lemmih Jan 16, 2021
d4cecfb
Basic visualization for random monotone polygons.
lemmih Jan 16, 2021
e16c1a6
Fix bug in `isMonotone`.
lemmih Jan 16, 2021
af9fe61
Update monotone animation.
lemmih Jan 16, 2021
3fef36c
Update hie.yaml.
lemmih Jan 16, 2021
7810aa2
Clean up imports.
lemmih Jan 16, 2021
ba1fc9d
Enumerate imports.
lemmih Jan 16, 2021
0255ecc
Refactored to include generalized polygon generation function
1ndy Jan 17, 2021
02ee9c0
Changed to
1ndy Jan 17, 2021
caa9706
Changed randomMonotoneFrom to monotoneFrom
1ndy Jan 17, 2021
72c213b
Merge branch 'master' of github.com:noinia/hgeometry into monotone-po…
lemmih Jan 17, 2021
5e1ecd0
Generalize types and use new Random instances.
lemmih Jan 17, 2021
9dd6fb1
Rename monotone module. Add two QC tests.
lemmih Jan 18, 2021
4523d6b
Update random_monotone showcase.
lemmih Jan 18, 2021
3b6445c
Add algo complexity and comments.
lemmih Jan 18, 2021
890d677
Write module header.
lemmih Jan 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hgeometry/hgeometry.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ library
Algorithms.Geometry.SSSP
Algorithms.Geometry.SSSP.Naive

Algorithms.Geometry.MonotonePolygon.MonotonePolygon

-- * Embedded Planar Graphs
Data.PlaneGraph
Expand Down
126 changes: 126 additions & 0 deletions hgeometry/src/Algorithms/Geometry/MonotonePolygon/MonotonePolygon.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
module Algorithms.Geometry.MonotonePolygon.MonotonePolygon
( isMonotone
, randomMonotone
, randomMonotoneDirected
) where

import Control.Monad.Random
import Data.Ext
import qualified Data.Foldable as F
import Data.Geometry.Line (Line (..))
import Data.Geometry.LineSegment
import Data.Geometry.Point
import Data.Geometry.Polygon.Core
import Data.Geometry.Vector
import Data.Geometry.Polygon.Extremes
import Data.Geometry.Point.Orientation.Degenerate


import Data.Intersection
import Data.CircularSeq
import Data.List
import Data.Ratio

import Data.Vinyl
import Data.Vinyl.CoRec

-- | \( O(n \log n) \)
-- A polygon is monotone if a straight line in a given direction
-- cannot have more than two intersections.
isMonotone :: (Fractional r, Ord r) => Vector 2 r -> SimplePolygon p r -> Bool
-- Check for each vertex that the number of intersections with the
-- line starting at the vertex and going out in the given direction
-- intersects with the edges of the polygon no more than 2 times.
isMonotone direction p = all isMonotoneAt (map _core $ toPoints p)
where
isMonotoneAt pt =
sum (map (intersectionsThrough pt) (F.toList $ outerBoundaryEdges p)) <= 2
intersectionsThrough pt edge =
match (Data.Intersection.intersect edge line) $
H (\NoIntersection -> 0)
:& H (\Point{} -> 1)
-- This happens when an edge is parallel with the given direction.
-- I think it's correct to count it as a single intersection.
:& H (\LineSegment{} -> 1)
:& RNil
where
line = Line pt direction

{- Algorithm overview:

1. Create N `Point 2 Rational` (N >= 3)
2. Create a random `Vector 2 Rational`
3. Find the extremes (min and max) of the points when sorted in the direction of the vector.
We already have code for this. See `maximumBy (cmpExtreme vector)` and
`minimumBy (cmpExtreme vector)`.
4. Take out the two extremal points from the set.
5. Partition the remaining points according to whether they're on the left side or right side
of the imaginary line between the two extremal points.
6. Sort the two partitioned sets, one in the direction of the vector and one in the opposite
direction.
7. Connect the points, starting from the minimal extreme point, going through the set of points
that are increasing in the direction of the vector, then to the maximal point, and finally
down through the points that are decreasing in the direction of the vector.
-}
-- | \( O(n \log n) \)
randomMonotone :: RandomGen g => Int -> Vector 2 Rational -> Rand g (SimplePolygon () Rational)
randomMonotone nVertices direction = do
-- 1, skip 2 in this function bc `direction` is given
points <- replicateM nVertices createRandomPoint
-- 3
let
specialPoints = map (\x -> x :+ ()) points
min = Data.List.minimumBy (cmpExtreme direction) specialPoints
max = Data.List.maximumBy (cmpExtreme direction) specialPoints
-- 4
pointsWithoutExtremes = filter (\x -> x /= min && x /= max) specialPoints
-- 5, 6
(leftHalfUnsorted,rightHalfUnsorted) = Data.List.partition (toTheLeft min max) pointsWithoutExtremes
leftHalf = sortBy (cmpExtreme direction) leftHalfUnsorted
rightHalf = reverse (sortBy (cmpExtreme direction) rightHalfUnsorted)
return (fromPoints ([min] ++ leftHalf ++ [max] ++ rightHalf))

-- Pick a random vector and then call 'randomMonotone'.
-- | \( O(n \log n) \)
randomMonotoneDirected :: RandomGen g => Int -> Rand g (SimplePolygon () Rational)
randomMonotoneDirected nVertices = do
points <- replicateM nVertices createRandomPoint
direction <- generateRandomVector2
let
specialPoints = map (\x -> x :+ ()) points
min = Data.List.minimumBy (cmpExtreme direction) specialPoints
max = Data.List.maximumBy (cmpExtreme direction) specialPoints
-- 4
pointsWithoutExtremes = filter (\x -> x /= min && x /= max) specialPoints
-- 5, 6
(leftHalfUnsorted,rightHalfUnsorted) = Data.List.partition (toTheLeft min max) pointsWithoutExtremes
leftHalf = sortBy (cmpExtreme direction) leftHalfUnsorted
rightHalf = reverse (sortBy (cmpExtreme direction) rightHalfUnsorted)
return (fromPoints ([min] ++ leftHalf ++ [max] ++ rightHalf))

-------------------------------------------------------------------------------------------------
-- helper functions

granularity :: Integer
granularity = 10000000

toTheLeft :: Point 2 Rational :+ () -> Point 2 Rational :+ () -> Point 2 Rational :+ () -> Bool
toTheLeft min max x = Data.Geometry.Point.Orientation.Degenerate.ccw a b c == CCW
where
a = _core min
b = _core max
c = _core x
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ccw' helper function calls _core for you.

ccw' :: (Ord r, Num r) => Point 2 r :+ a -> Point 2 r :+ b -> Point 2 r :+ c -> CCW


createRandomPoint :: RandomGen g => Rand g (Point 2 Rational)
createRandomPoint = do
x <- liftRand $ randomR (0, granularity)
y <- liftRand $ randomR (0, granularity)
pure $ Point2 (x % granularity) (y % granularity)


generateRandomVector2 :: RandomGen g => Rand g (Vector 2 Rational)
generateRandomVector2 = do
a <- liftRand $ randomR(0, granularity)
b <- liftRand $ randomR(0, granularity)
pure $ Vector2 (a % granularity) (b % granularity)