|
| 1 | +function Show-Usage() { |
| 2 | + Write-Host 'Usage: please provide at least 3 x and y coordinates as separate lists (e.g. "100, 440, 210")' |
| 3 | + Exit 1 |
| 4 | +} |
| 5 | + |
| 6 | +function Parse-IntList([string]$Str) { |
| 7 | + @($Str.Split(",") | ForEach-Object { [int]::Parse($_) }) |
| 8 | +} |
| 9 | + |
| 10 | +class Point { |
| 11 | + [int]$X |
| 12 | + [int]$Y |
| 13 | + |
| 14 | + Point([int]$x, [int]$y) { |
| 15 | + $this.X = $x |
| 16 | + $this.Y = $y |
| 17 | + } |
| 18 | + |
| 19 | + [string] ToString() { |
| 20 | + return "($($this.X), $($this.Y))" |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +function Get-Points([int[]]$X, [int[]]$Y) { |
| 25 | + @(0..($X.Length - 1) | ForEach-Object { [Point]::new($X[$_], $Y[$_]) }) |
| 26 | +} |
| 27 | + |
| 28 | +# Find Convex Hull using Jarvis' algorithm |
| 29 | +# Source: https://www.geeksforgeeks.org/convex-hull-using-jarvis-algorithm-or-wrapping/ |
| 30 | +function Invoke-ConvexHull([Point[]]$Points) { |
| 31 | + $n = $Points.Length |
| 32 | + |
| 33 | + # The first point is the leftmost point with the highest y-coord in the event of a tie |
| 34 | + $l = Find-LeftmostPoint $Points |
| 35 | + |
| 36 | + # Repeat until wrapped around to first hull point |
| 37 | + $p = $l |
| 38 | + do { |
| 39 | + # Output convex hull point |
| 40 | + $Points[$p] |
| 41 | + |
| 42 | + $q = ($p + 1) % $n |
| 43 | + for ($j = 0; $j -lt $n; $j++) { |
| 44 | + # If point j is more counter-clockwise, then update end point (q) |
| 45 | + if ((Get-Orientation $Points[$p] $Points[$j] $Points[$q]) -lt 0) { |
| 46 | + $q = $j |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + $p = $q |
| 51 | + } while ($p -ne $l) |
| 52 | +} |
| 53 | + |
| 54 | +function Find-LeftmostPoint([Point[]]$Points) { |
| 55 | + $leftmostPoint = [Point]::new([int]::MaxValue, [int]::MinValue) |
| 56 | + $leftmostIndex = 0 |
| 57 | + for ($i = 0; $i -lt $Points.Length; $i++) { |
| 58 | + # In the event of a tie, pick the point with the greater y-coord |
| 59 | + if ($Points[$i].X -lt $leftmostPoint.X -or |
| 60 | + ($Points[$i].X -eq $leftmostPoint.X -and $Points[$i].Y -gt $leftmostPoint.Y) |
| 61 | + ) { |
| 62 | + $leftmostPoint = $Points[$i] |
| 63 | + $leftmostIndex = $i |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + $leftmostIndex |
| 68 | +} |
| 69 | + |
| 70 | +# Get orientation of three points |
| 71 | +# |
| 72 | +# 0 = points are in a line |
| 73 | +# > 0 = points are clockwise |
| 74 | +# < 0 = points are counter-clockwise |
| 75 | +function Get-Orientation([Point]$P, [Point]$Q, [Point]$R) { |
| 76 | + ($Q.Y - $P.Y) * ($R.X - $Q.X) - ($Q.X - $P.X) * ($R.Y - $Q.Y) |
| 77 | +} |
| 78 | + |
| 79 | +if ($args.Length -lt 2 -or -not $args[0] -or -not $args[1]) { |
| 80 | + Show-Usage |
| 81 | +} |
| 82 | + |
| 83 | +try { |
| 84 | + $x = Parse-IntList $args[0] |
| 85 | + $y = Parse-IntList $args[1] |
| 86 | + if ($x.Length -ne $y.Length -or $x.Length -lt 3) { |
| 87 | + Show-Usage |
| 88 | + } |
| 89 | +} catch { |
| 90 | + Show-Usage |
| 91 | +} |
| 92 | + |
| 93 | +# Combine values into set of points |
| 94 | +$points = Get-Points $X $Y |
| 95 | + |
| 96 | +# Get convex hull of points and show them |
| 97 | +$hullPoints = Invoke-ConvexHull $points |
| 98 | +Write-Output ($hullPoints -join "`n") |
0 commit comments