open System;; open System.Windows.Forms;; open Microsoft.FSharp.Collections;; type Point = { x :double; y :double; } with static member ( = ) a b = a.x = b.x && a.y = b.y; static member ( <> ) a b = not(a = b); end;; type Edge = { orig:Point; dest:Point; } with static member ( = ) a b = a.orig = b.orig && a.dest = b.dest; static member ( <> ) a b = not(a = b); end;; let sort point_list = List.sort( fun p1 p2 -> match Pervasives.compare p1.y p2.y with | 0 -> Pervasives.compare p1.x p2.x; | _ as v -> v; ) point_list;; // Input: three points P0, P1, and P2 // Return: >0 for P2 left of the line through P0 and P1 // =0 for P2 on the line // <0 for P2 right of the line let testSide p0 p1 p2 = (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);; let isLeft p0 p1 p2 = (testSide p0 p1 p2) > 0.;; let isRight p0 p1 p2 = (testSide p0 p1 p2) < 0.;; let rec _jarvis cmp max_point (hull_list:Point list) (input_list:Point list) = match input_list with | p::tl -> let last = List.hd hull_list in if last = max_point then hull_list else ( let best = List.fold_left(fun p1 p2 -> if (cmp last p1 p2) then p2 else p1; ) p input_list in _jarvis cmp max_point (best::hull_list) tl ) | [] -> hull_list;; let jarvis point_list = if List.length point_list < 3 then failwith "3 points are required for a polygon"; let point_list = sort point_list in let miny = point_list |> List.hd in let maxy = point_list |> List.rev |> List.hd in let left = _jarvis (isLeft) maxy [miny] point_list in let right = _jarvis (isRight) maxy [miny] point_list in right @ left |> List.rev;; let points_to_edges pl = let p1::p2::tl = pl in List.fold_left(fun (el:Edge list) p -> let e = List.hd el in {orig = e.dest; dest = p}::el) [{orig = p1; dest = p2}] pl;; let generate_plist count = let r = new Random(DateTime.Now.GetHashCode()) in let rand_float max = Convert.ToDouble(r.Next max) + r.NextDouble() in [for i in 1 .. count -> {x = rand_float 600; y = rand_float 600}] |> sort;; type ConvexHullControl = class inherit UserControl as base val mutable point_list: Point list; new() as this = { point_list = []; } then this.init(); member this.init() = base.BackColor <- Drawing.Color.White; base.SetStyle (ControlStyles.UserPaint, true); base.SetStyle (ControlStyles.DoubleBuffer, true); base.SetStyle (ControlStyles.AllPaintingInWmPaint, true); base.Click.Add(fun e -> this.reset()); this.reset(); member this.reset() = this.point_list <- generate_plist 30 |> sort; this.Invalidate(); override this.OnPaint e = let g = e.Graphics in let graph_elist p l = List.iter(fun e -> let x1 = Convert.ToInt32(e.orig.x) in let y1 = Convert.ToInt32(e.orig.y) in let x2 = Convert.ToInt32(e.dest.x) in let y2 = Convert.ToInt32(e.dest.y) in g.DrawLine(p, x1, y1, x2, y2); ) l in let miny = this.point_list |> List.hd in let maxy = this.point_list |> List.rev |> List.hd in let left = _jarvis (isLeft) maxy [miny] this.point_list in let right = _jarvis (isRight) maxy [miny] this.point_list in let left = left |> List.rev in let right = right |> List.rev in left |> points_to_edges |> graph_elist Drawing.Pens.Green; right |> points_to_edges |> graph_elist Drawing.Pens.Blue; List.iter(fun p -> let x = Convert.ToInt32(p.x) in let y = Convert.ToInt32(p.y) in g.DrawEllipse(Drawing.Pens.Red, x - 1, y - 1, 2, 2); ) this.point_list; end;; [] do let size = new Drawing.Size(700, 700) in let mw = new Form() in let cc = new ConvexHullControl() in mw.Controls.Add(cc); cc.Dock <- DockStyle.Fill; cc.Size <- size; mw.ClientSize <- size; mw.MaximumSize <- mw.Size; mw.MinimumSize <- mw.Size; mw.Text <- "Convex Hull"; mw.Show(); Application.Run(mw);;