-
Notifications
You must be signed in to change notification settings - Fork 41
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
Changes from 20 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
4d49f79
Created 'MonotonePolygon'
1ndy 08db24a
Skeleton for the implementation of monotone polygons
lemmih be3107f
Add MonotonePolygon to exposed modules.
lemmih 9757fff
Add 'randomMonotoneAny' variant.
lemmih 5511fc0
Implement 'isMonotone'. Will be used in testing.
lemmih f437e48
Rename 'randomMonotoneAny' to 'randomMonotoneDirected'
lemmih 416352e
defined randomeMonotone
1ndy af6202b
Implemented randomMonotone
1ndy c07e9d8
Implemented randomMonotoneDirected
1ndy 8bfde07
cleaned up module definition and imports
1ndy 2927ed0
Resolved name conflicts for intersect, minimumBy, MaximumBy, replicate
1ndy a9da834
Corrected type signuature for linearInterpolation function
1ndy b3173e9
imported Data.Geometry.Polygon.Extreme
1ndy 5245fcb
Corrected typo in import
1ndy 4f55098
Trying to convert list to Vector 2 Rational
1ndy bc9d3f3
refactored randomMonotone
1ndy f6d3e09
fixed conflicting definitions of rightHalf
1ndy 9cefd40
changed replicate to replicateM
1ndy 6cd58af
Merge branch 'master' of https://github.com/noinia/hgeometry into mon…
1ndy 51567cc
Successful compile
1ndy d63a7c4
Changed ccw to ccw'
1ndy fedd832
Merge branch 'master' of github.com:noinia/hgeometry into monotone-po…
lemmih d4cecfb
Basic visualization for random monotone polygons.
lemmih e16c1a6
Fix bug in `isMonotone`.
lemmih af9fe61
Update monotone animation.
lemmih 3fef36c
Update hie.yaml.
lemmih 7810aa2
Clean up imports.
lemmih ba1fc9d
Enumerate imports.
lemmih 0255ecc
Refactored to include generalized polygon generation function
1ndy 02ee9c0
Changed to
1ndy caa9706
Changed randomMonotoneFrom to monotoneFrom
1ndy 72c213b
Merge branch 'master' of github.com:noinia/hgeometry into monotone-po…
lemmih 5e1ecd0
Generalize types and use new Random instances.
lemmih 9dd6fb1
Rename monotone module. Add two QC tests.
lemmih 4523d6b
Update random_monotone showcase.
lemmih 3b6445c
Add algo complexity and comments.
lemmih 890d677
Write module header.
lemmih File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
hgeometry/src/Algorithms/Geometry/MonotonePolygon/MonotonePolygon.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
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) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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