Skip to content

Commit 6295467

Browse files
committed
Add Convex Hull in C# (TheRenegadeCoder#4873)
1 parent 58545ba commit 6295467

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

archive/c/c-sharp/ConvexHull.cs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
public record Point(int X, int Y) : IComparable<Point>
6+
{
7+
public int CompareTo(Point? other)
8+
=> other is null ? 1 : X != other.X ? X.CompareTo(other.X) : Y.CompareTo(other.Y);
9+
10+
public override string ToString() => $"({X}, {Y})";
11+
12+
public static bool operator <(Point left, Point right) => left.CompareTo(right) < 0;
13+
public static bool operator >(Point left, Point right) => left.CompareTo(right) > 0;
14+
}
15+
16+
public static class ConvexHull
17+
{
18+
private static void ShowUsage()
19+
{
20+
Console.Error.WriteLine("Usage: please provide at least 3 x and y coordinates as separate lists (e.g. \"100, 440, 210\")");
21+
}
22+
23+
private static List<int> ParseIntegerList(string input)
24+
{
25+
if (string.IsNullOrWhiteSpace(input))
26+
{
27+
ShowUsage();
28+
Environment.Exit(1);
29+
}
30+
31+
var list = input
32+
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
33+
.Select(part => int.TryParse(part, out var val)
34+
? val
35+
: throw new ArgumentException($"Invalid integer value: '{part}'"))
36+
.ToList();
37+
38+
if (list.Count < 3)
39+
{
40+
ShowUsage();
41+
Environment.Exit(1);
42+
}
43+
44+
return list;
45+
}
46+
47+
48+
/// <summary>
49+
/// Calculates the cross product of vectors OA and OB.
50+
/// Positive result means counter-clockwise turn.
51+
/// Negative result means clockwise turn.
52+
/// Zero means points are colinear.
53+
/// </summary>
54+
private static long Cross(Point o, Point a, Point b)
55+
{
56+
var (ox, oy) = (o.X, o.Y);
57+
var (ax, ay) = (a.X, a.Y);
58+
var (bx, by) = (b.X, b.Y);
59+
return (long)(ax - ox) * (by - oy) - (long)(ay - oy) * (bx - ox);
60+
}
61+
62+
63+
/// <summary>
64+
/// Constructs the convex hull using the Jarvis March algorithm.
65+
/// </summary>
66+
private static List<Point> BuildHull(List<Point> points)
67+
{
68+
int n = points.Count;
69+
if (n < 3) return [.. points];
70+
71+
int startIndex = 0;
72+
for (int i = 1; i < n; i++)
73+
{
74+
if (points[i] < points[startIndex])
75+
startIndex = i;
76+
}
77+
78+
var hull = new List<Point>();
79+
int currentIndex = startIndex;
80+
81+
do
82+
{
83+
hull.Add(points[currentIndex]);
84+
int candidateIndex = (currentIndex + 1) % n;
85+
86+
for (int i = 0; i < n; i++)
87+
{
88+
if (Cross(points[currentIndex], points[i], points[candidateIndex]) > 0)
89+
candidateIndex = i;
90+
}
91+
92+
currentIndex = candidateIndex;
93+
94+
} while (currentIndex != startIndex);
95+
96+
return hull;
97+
}
98+
99+
public static int Main(string[] args)
100+
{
101+
if (args.Length != 2)
102+
{
103+
ShowUsage();
104+
return 1;
105+
}
106+
107+
try
108+
{
109+
var xCoords = ParseIntegerList(args[0]);
110+
var yCoords = ParseIntegerList(args[1]);
111+
if (xCoords.Count != yCoords.Count)
112+
{
113+
ShowUsage();
114+
return 1;
115+
}
116+
117+
if (xCoords.Count < 3)
118+
{
119+
ShowUsage();
120+
return 1;
121+
}
122+
123+
var points = xCoords.Zip(yCoords, (x, y) => new Point(x, y)).ToList();
124+
125+
BuildHull(points).ForEach(Console.WriteLine);
126+
127+
return 0;
128+
}
129+
catch
130+
{
131+
ShowUsage();
132+
return 1;
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)