Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
circle.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
open Math type t = {center: Point.t; radius: distance} let make (center : Point.t) (radius : distance) : t = if radius >= 0. then {center; radius} else invalid_arg "Circle.make:radius should be positive or zero" let center {center; _} = center let radius {radius; _} = radius let translate (dx : float) (dy : float) (c : t) : t = {c with center= Point.translate dx dy c.center} let reflection p (c : t) = {c with center= Point.reflection p c.center} let rotate (c : t) p f = {c with center= Point.rotate c.center p f} let rotate_angle (c : t) p f = {c with center= Point.rotate_angle c.center p f} let contains (c : t) p = Point.sq_distance c.center p <= c.radius *. c.radius let area ({radius; _} : t) = pi *. radius *. radius let perimeter ({radius; _} : t) = 2. *. pi *. radius let proj_x (c : t) = let open Point in (c.center.x -. c.radius, c.center.x +. c.radius) let proj_y (c : t) = let open Point in (c.center.y -. c.radius, c.center.y +. c.radius) let intersects (c1 : t) (c2 : t) = Point.sq_distance c1.center c2.center < (c1.radius +. c2.radius) ** 2. (** line_intersection takes a circle and line and returns the list of the intersection points. (can be [], [a] or [a,b] *) let intersect_line (c : t) (l : Line.t) = let cx = c.center.Point.x and cy = c.center.Point.y in let open Line in match l with | X x -> let a = x -. cx in Math.solve 1. 0. ((a *. a) -. (c.radius *. c.radius)) |> List.map (fun y -> Point.make x y |> Point.translate 0. cy) | _ -> (* we go to origin *) let l_2 = translate (-.cx) (-.cy) l in let a, b = match l_2 with Y (a, b) -> (a, b) | _ -> assert false in (* we solve the equation at the origin for x*) (* x² + y² = r² and y = ax+b => x² + (ax + b)² = r² => x² + a²x² + 2abx + b² = r² => (a²+1)x² + 2abx + b²-r² = 0 *) Math.solve ((a *. a) +. 1.) (2. *. a *. b) ((b *. b) -. (c.radius *. c.radius)) (* we calculate the associated y*) (* and translate back the result to the first coordinates*) |> List.map (fun x -> Point.make x ((a *. x) +. b) |> Point.translate cx cy) let segment_intersection c (s : Segment.t) = let a, b = s in let ab = Vector.of_points a b in let dab2 = Point.sq_distance a b in Segment.to_line s |> intersect_line c |> List.filter (fun p -> let dp = Vector.dot_product ab (Vector.of_points a p) in 0. <= dp && dp <= dab2) let tangent {center; _} p = Line.perpendicular_of_line (Line.of_points center p) p let intersection ({center= c1; radius= r1} as c : t) ({center= c2; radius= r2} : t) = let c1_c2 = Line.of_points c1 c2 in let v = Vector.of_points c1 c2 in let d = Vector.magnitude_sq v in let v' = Vector.scal_mult (0.5 *. ((r1 *. r1) -. (r2 *. r2)) /. d) v in let p = Vector.move_to v' (Point.center c1 c2) in let l = Line.perpendicular_of_line c1_c2 p in intersect_line c l let circumscribed p1 p2 p3 = let b1 = Line.point_bissection p1 p2 and b2 = Line.point_bissection p2 p3 in let center = Line.intersection b1 b2 in let radius = Point.distance p2 center in make center radius let incircle p1 p2 p3 = let a = Point.distance p1 p2 and b = Point.distance p2 p3 and c = Point.distance p3 p1 in let center = Point.barycenter [(p1, b); (p2, c); (p3, a)] in let radius = let s = 0.5 *. (a +. b +. c) in 2. *. sqrt (s *. (s -. a) *. (s -. b) *. (s -. c)) /. (a +. b +. c) in make center radius let of_diameter p1 p2 = let c = Point.center p1 p2 in let radius = Point.distance p1 c in make c radius let bounding (pts : Point.t list) : t = let of_two x y pt = try [((x, pt), y); ((y, pt), x)] |> List.map (fun ((a, b), c) -> (([a; b], of_diameter a b), c)) |> List.find (fun ((_, circle), inner) -> contains circle inner) |> fst with Not_found -> ([x; y; pt], circumscribed x y pt) in let of_three x y z pt = let found = [((x, y), z); ((x, z), y); ((y, z), x)] |> List.map (fun ((a, b), c) -> (of_two a b pt, c)) |> List.filter (fun ((_, c), inner) -> contains c inner) |> List.map fst in List.fold_left (fun (e1, c1) (e2, c2) -> if radius c1 < radius c2 then (e1, c1) else (e2, c2)) (List.hd found) (List.tl found) in let update set pt = match set with | [x] -> ([x; pt], of_diameter x pt) | [x; y] -> of_two x y pt | [x; y; z] -> of_three x y z pt | _ -> assert false in let rec mindisk l circle set pts = match pts with | [] -> circle | h :: tl when contains circle h || List.mem h set -> mindisk l circle set tl | h :: _ -> let new_set, new_circle = update set h in List.filter (fun e -> List.mem e new_set |> not) l |> mindisk l new_circle new_set in match pts with | [] -> invalid_arg "can't build a bounding circle with an empty list" | h :: tl -> mindisk pts (make h 0.) [h] tl let random_point st (c : t) : Point.t = let theta = Random.State.float st Math.pi *. 2. and r = Random.State.float st (c.radius *. c.radius) |> sqrt in let x = r *. cos theta and y = r *. sin theta in Point.(make (c.center.x +. x) (c.center.y +. y)) let random_point_perimeter st (c : t) : Point.t = let theta = Random.State.float st Math.pi *. 2. and r = c.radius in let x = r *. cos theta and y = r *. sin theta in Point.(make (c.center.x +. x) (c.center.y +. y)) let print fmt (c : t) = Format.fprintf fmt "center:%a, radius=%f" Point.print c.center c.radius