|
| 1 | +open OcamlCanvas.V1 |
| 2 | + |
| 3 | +let option_iter f o = match o with None -> () | Some e -> f e |
| 4 | + |
| 5 | +let sw = 800 |
| 6 | +let sh = 800 |
| 7 | + |
| 8 | +let base_y = 300. |
| 9 | +let p1 : Point.t = (200., base_y) |
| 10 | +let p2 : Point.t = (600., base_y) |
| 11 | + |
| 12 | +let oeuil_y = base_y -. 200. |
| 13 | + |
| 14 | +let o1 = (300., oeuil_y) |
| 15 | +let o2 = (500., oeuil_y) |
| 16 | + |
| 17 | +let saucisse_width = 30. |
| 18 | +let saucisse_color = Color.orange |
| 19 | + |
| 20 | +let mid = Point.barycenter 1. p1 1. p2 |
| 21 | + |
| 22 | +let p3 : Point.t ref = ref (fst mid, 300.) |
| 23 | + |
| 24 | +let () = |
| 25 | + Backend.init () |
| 26 | + |
| 27 | +let c = |
| 28 | + Canvas.createOnscreen ~title:"Saucisse" |
| 29 | + ~pos:(0, 0) ~size:(sw, sh) () |
| 30 | + |
| 31 | +let rot (x, y) = (-.y, x) |
| 32 | + |
| 33 | +let (++) (x1, y1) (x2, y2) = x1 +. x2, y1 +. y2 |
| 34 | +let (--) (x1, y1) (x2, y2) = x1 -. x2, y1 -. y2 |
| 35 | +let normalize (x, y) = |
| 36 | + let norm = sqrt (x *. x +. y *. y) in |
| 37 | + x /. norm, y /. norm |
| 38 | +let dir p1 p2 = |
| 39 | + normalize (p2 -- p1) |
| 40 | +let scale v (x, y) = |
| 41 | + v *. x, v *. y |
| 42 | + |
| 43 | +let disc ?color ?gradient center radius = |
| 44 | + option_iter (Canvas.setFillColor c) color; |
| 45 | + option_iter (Canvas.setFillGradient c) gradient; |
| 46 | + Canvas.clearPath c; |
| 47 | + Canvas.arc c ~center |
| 48 | + ~radius ~theta1:0.0 ~theta2:(2.0 *. Const.pi) ~ccw:false; |
| 49 | + Canvas.fill c ~nonzero:false |
| 50 | + |
| 51 | +let circle ~color center radius = |
| 52 | + Canvas.setStrokeColor c color; |
| 53 | + Canvas.clearPath c; |
| 54 | + Canvas.arc c ~center |
| 55 | + ~radius ~theta1:0.0 ~theta2:(2.0 *. Const.pi) ~ccw:false; |
| 56 | + Canvas.stroke c |
| 57 | + |
| 58 | +let point ?(color=Color.red) center = |
| 59 | + disc ~color center 5.0 |
| 60 | + |
| 61 | +let taille_oeuil = 30. |
| 62 | +let taille_pupille = 10. |
| 63 | +let cursor_z = 100. |
| 64 | + |
| 65 | +let gray n = Color.of_rgb n n n |
| 66 | + |
| 67 | +let sourcil pos dir ratio = |
| 68 | + let w = 50. in |
| 69 | + let p0 = pos ++ (-.w, 0.) in |
| 70 | + let p2 = pos ++ (w, 0.) in |
| 71 | + let dy = |
| 72 | + ratio *. 20. |
| 73 | + in |
| 74 | + let p0, p2 = |
| 75 | + if dir then |
| 76 | + p0, p2 ++ (0., dy) |
| 77 | + else |
| 78 | + p0 ++ (0., dy), p2 |
| 79 | + in |
| 80 | + let ratio = if dir then ratio else 1. -. ratio in |
| 81 | + let b = Point.barycenter ratio p0 (1. -. ratio) p2 in |
| 82 | + let p1 = b ++ (0., -.20.) in |
| 83 | + let p3 = b ++ (0., -.5.) in |
| 84 | + let pa = Path.create () in |
| 85 | + Path.moveTo pa p0; |
| 86 | + Path.lineTo pa p1; |
| 87 | + Path.lineTo pa p2; |
| 88 | + Path.lineTo pa p3; |
| 89 | + Path.close pa; |
| 90 | + Canvas.setFillColor c Color.black; |
| 91 | + Canvas.setStrokeColor c Color.black; |
| 92 | + Canvas.fillPath c pa ~nonzero:false; |
| 93 | + Canvas.strokePath c pa; |
| 94 | + () |
| 95 | + |
| 96 | +let oeuil pos dir is_left sourcil_ratio = |
| 97 | + let g = |
| 98 | + Gradient.createRadial |
| 99 | + ~center1:(pos ++ (-.15., 10.)) |
| 100 | + ~rad1:1. |
| 101 | + ~center2:pos |
| 102 | + ~rad2:30. |
| 103 | + in |
| 104 | + Gradient.addColorStop g Color.white 0.; |
| 105 | + Gradient.addColorStop g (gray 235) 0.2; |
| 106 | + Gradient.addColorStop g (gray 230) 0.85; |
| 107 | + Gradient.addColorStop g (gray 100) 1.; |
| 108 | + disc ~gradient:g pos taille_oeuil; |
| 109 | + circle ~color:Color.black pos taille_oeuil; |
| 110 | + let pup = |
| 111 | + let (x, y) = dir -- pos in |
| 112 | + let ax = atan2 cursor_z x in |
| 113 | + let ay = atan2 cursor_z y in |
| 114 | + let x = cos ax in |
| 115 | + let y = cos ay in |
| 116 | + let o = (x, y) in |
| 117 | + scale 20. o ++ pos |
| 118 | + in |
| 119 | + disc ~color:Color.black pup taille_pupille; |
| 120 | + sourcil (pos -- (0., 50.)) is_left sourcil_ratio |
| 121 | + |
| 122 | +let controls a b = |
| 123 | + let v = scale saucisse_width (dir a b) in |
| 124 | + let p1 = b ++ rot v in |
| 125 | + let p2 = b ++ v ++ rot v in |
| 126 | + let p3 = b ++ v in |
| 127 | + let p4 = b ++ v -- rot v in |
| 128 | + let p5 = b -- rot v in |
| 129 | + v, p1, p2, p3, p4, p5 |
| 130 | + |
| 131 | +let bout path a b = |
| 132 | + let _v, _p1, p2, p3, p4, p5 = controls a b in |
| 133 | + Path.arcTo path ~p1:p2 ~p2:p3 ~radius:saucisse_width; |
| 134 | + Path.arcTo path ~p1:p4 ~p2:p5 ~radius:saucisse_width; |
| 135 | + () |
| 136 | + |
| 137 | +let bouboule_radius = 10. |
| 138 | + |
| 139 | +let bouboule v p = |
| 140 | + let v = normalize v in |
| 141 | + let center = p ++ (scale (saucisse_width +. bouboule_radius) v) in |
| 142 | + disc ~color:saucisse_color center bouboule_radius; |
| 143 | + circle ~color:Color.black center bouboule_radius |
| 144 | + |
| 145 | +let tri v p = |
| 146 | + let v = normalize v in |
| 147 | + let center = p ++ (scale (saucisse_width +. bouboule_radius) v) in |
| 148 | + let size_bout = 30. in |
| 149 | + let p1 = center ++ (scale size_bout v) ++ (scale 15. (rot v)) in |
| 150 | + let p2 = center ++ (scale size_bout v) -- (scale 15. (rot v)) in |
| 151 | + let pa = Path.create () in |
| 152 | + Path.moveTo pa center; |
| 153 | + Path.lineTo pa p1; |
| 154 | + Path.lineTo pa p2; |
| 155 | + Path.close pa; |
| 156 | + Canvas.setFillColor c saucisse_color; |
| 157 | + Canvas.fillPath c pa ~nonzero:false; |
| 158 | + Canvas.setStrokeColor c Color.black; |
| 159 | + Canvas.strokePath c pa; |
| 160 | + |
| 161 | + bouboule v p; |
| 162 | + () |
| 163 | + |
| 164 | +module F = struct |
| 165 | + type 'a t = { |
| 166 | + c : 'a list; |
| 167 | + s : int; |
| 168 | + } |
| 169 | + |
| 170 | + let make v s = { c = [v]; s } |
| 171 | + |
| 172 | + let push v s = |
| 173 | + let l = List.length s.c in |
| 174 | + let c = |
| 175 | + if l >= s.s then |
| 176 | + List.rev (List.tl (List.rev s.c)) |
| 177 | + else |
| 178 | + s.c |
| 179 | + in |
| 180 | + { s with c = v :: c } |
| 181 | + |
| 182 | + let mean s = |
| 183 | + List.fold_left (fun acc v -> acc +. v) 0. s.c /. (float s.s) |
| 184 | +end |
| 185 | + |
| 186 | +let time () = |
| 187 | + (Int64.to_float @@ Backend.getCurrentTimestamp ()) /. 1_000_000. |
| 188 | + |
| 189 | +let last_frame_time = ref (time ()) |
| 190 | +let frame_time : float F.t ref = ref (F.make 100. 30) |
| 191 | +let push_time t = |
| 192 | + let dt = t -. !last_frame_time in |
| 193 | + last_frame_time := t; |
| 194 | + frame_time := F.push dt !frame_time |
| 195 | +let fps () = 1. /. F.mean !frame_time |
| 196 | + |
| 197 | +let draw () = |
| 198 | + |
| 199 | + let t = time () in |
| 200 | + |
| 201 | + let truc = 20. in |
| 202 | + |
| 203 | + let p1 = p1 ++ (truc *. 2.1 *. sin (3.1 *. t) , truc *. 2. *. cos (4. *. t)) in |
| 204 | + let p2 = p2 ++ (truc *. 2.1 *. sin (5.1 *. t) , truc *. 2.3 *. cos (4.2 *. t)) in |
| 205 | + |
| 206 | + let p3 = !p3 in |
| 207 | + let p3_oeuil = p3 in |
| 208 | + let p3 = |
| 209 | + let o = 40. in |
| 210 | + let x = max (fst p1 +. o) (min (fst p2 -. o) (fst p3)) in |
| 211 | + (x, snd p3) |
| 212 | + in |
| 213 | + Canvas.setFillColor c saucisse_color; |
| 214 | + let path = Path.create () in |
| 215 | + let v1, x, _, _, _, a = controls p3 p1 in |
| 216 | + Path.moveTo path x; |
| 217 | + bout path p3 p1; |
| 218 | + let v2, b, _, _, _, a2 = controls p3 p2 in |
| 219 | + let v = (0., saucisse_width) in |
| 220 | + |
| 221 | + let ratio = 0.8 +. 0.3 *. sin (2. *. t) in |
| 222 | + |
| 223 | + let pos a cont = |
| 224 | + a ++ (scale ratio (cont -- a)) |
| 225 | + in |
| 226 | + |
| 227 | + let d = p3 ++ v in |
| 228 | + Path.bezierCurveTo path |
| 229 | + ~cp1:(pos a d) |
| 230 | + ~cp2:(pos b d) |
| 231 | + ~p:b; |
| 232 | + |
| 233 | + bout path p3 p2; |
| 234 | + |
| 235 | + let u = p3 -- v in |
| 236 | + Path.bezierCurveTo path |
| 237 | + ~cp1:(pos a2 u) |
| 238 | + ~cp2:(pos x u) |
| 239 | + ~p:x; |
| 240 | + Path.close path; |
| 241 | + Canvas.setFillColor c saucisse_color; |
| 242 | + Canvas.fillPath c path ~nonzero:false; |
| 243 | + Canvas.setStrokeColor c Color.black; |
| 244 | + Canvas.strokePath c path; |
| 245 | + |
| 246 | + tri v1 p1; |
| 247 | + tri v2 p2; |
| 248 | + |
| 249 | + let sourcil_ratio = |
| 250 | + let r = |
| 251 | + let dy = snd p3 -. (base_y +. 200.) in |
| 252 | + -. (dy /. 400.) |
| 253 | + in |
| 254 | + min 0.95 (max 0.05 r) |
| 255 | + in |
| 256 | + |
| 257 | + oeuil o1 p3_oeuil true sourcil_ratio; |
| 258 | + oeuil o2 p3_oeuil false sourcil_ratio; |
| 259 | + |
| 260 | + push_time (time ()); |
| 261 | + |
| 262 | + let fps_s = Printf.sprintf "%0.2f" (fps ()) in |
| 263 | + Canvas.setFillColor c Color.black; |
| 264 | + Canvas.fillText c fps_s (10., 10.); |
| 265 | + |
| 266 | + Canvas.show c |
| 267 | + |
| 268 | +let () = |
| 269 | + draw () |
| 270 | + |
| 271 | +let e_move = |
| 272 | + React.E.map (fun { Event.canvas = _; timestamp = _; data = (x, y) } -> |
| 273 | + p3 := (float_of_int x, float_of_int y) |
| 274 | + ) Event.mouse_move |
| 275 | + |
| 276 | +let e1 = |
| 277 | + React.E.map (fun { Event.canvas = _; timestamp = _; data = () } -> |
| 278 | + Backend.stop () |
| 279 | + ) Event.close |
| 280 | + |
| 281 | +let e2 = |
| 282 | + React.E.map (fun { Event.canvas = _; timestamp = _; |
| 283 | + data = { Event.key; char = _; flags = _ }; _ } -> |
| 284 | + if key = KeyEscape then |
| 285 | + Backend.stop () |
| 286 | + ) Event.key_down |
| 287 | + |
| 288 | +let e3 = |
| 289 | + React.E.map (fun { Event.canvas = _; timestamp = _; |
| 290 | + data = { Event.position = (x, y); _ } } -> |
| 291 | + point (float_of_int x, float_of_int y); |
| 292 | + ) Event.button_down |
| 293 | + |
| 294 | +let frames = ref 0L |
| 295 | + |
| 296 | +let e_frame = |
| 297 | + React.E.map (fun { Event.canvas = _; timestamp = _; _ } -> |
| 298 | + Canvas.setFillColor c Color.white; |
| 299 | + Canvas.fillRect c ~pos:(0.0, 0.0) ~size:(float_of_int sw, float_of_int sh); |
| 300 | + |
| 301 | + draw (); |
| 302 | + frames := Int64.add !frames Int64.one |
| 303 | + ) Event.frame |
| 304 | + |
| 305 | +let () = |
| 306 | + if Array.length Sys.argv >= 2 && Sys.argv.(1) = "bench" then |
| 307 | + for _ = 0 to 1_000 do |
| 308 | + draw (); |
| 309 | + done |
| 310 | + else |
| 311 | + Backend.run (fun () -> |
| 312 | + ignore (Sys.opaque_identity e_frame); |
| 313 | + ignore (Sys.opaque_identity e_move); |
| 314 | + ignore (Sys.opaque_identity (e1, e2)); |
| 315 | + ignore (Sys.opaque_identity (e3)); |
| 316 | + Printf.printf "\nDisplayed %Ld frames. Goodbye !\n" !frames) |
| 317 | + |
0 commit comments