File: animations.ml

package info (click to toggle)
mlpost 0.9-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,844 kB
  • sloc: ml: 21,094; javascript: 4,047; makefile: 430; ansic: 34; lisp: 19; sh: 15
file content (161 lines) | stat: -rw-r--r-- 4,479 bytes parent folder | download | duplicates (2)
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
open Mlpost
module P = Path
module N = Num
open Point

let ( |> ) x f = f x

let red dir distmax distmin =
  let l = dir |> length |> N.minn (N.bp distmax) |> N.maxn (N.bp distmin) in
  scale l (normalize dir)

let little_man ~phead ~plhand ~prhand ~plfoot ~prfoot =
  let downbody = origin in
  let upbody = bpp (0., 1.) in
  let armfac = 0.75 in
  let body = P.pathp [ downbody; upbody ] in
  let dneck = sub phead upbody in
  let neck, head =
    let red = red dneck 0.75 0.25 in
    (add (scale (N.bp (0.5 /. 0.75)) red) upbody, add red upbody)
  in
  let head = P.fullcircle |> P.scale (N.bp 0.5) |> P.shift head in
  let neck = P.pathp [ neck; upbody ] in
  let handbody = scale (N.bp armfac) upbody in
  let pos anchor dir = add anchor (red (sub dir anchor) 1. 0.10) in
  let hand dir =
    let c = P.fullcircle |> P.scale (N.bp 0.1) in
    let p = pos handbody dir in
    (P.shift p c, p)
  in
  let lhand, plhand = hand plhand in
  let rhand, prhand = hand prhand in
  let foot dir =
    let c = P.fullcircle |> P.scale (N.bp 0.1) in
    let p = pos origin dir in
    (P.shift p c, p)
  in
  let lfoot, plfoot = foot plfoot in
  let rfoot, prfoot = foot prfoot in
  let larm = P.pathp [ handbody; plhand ] in
  let rarm = P.pathp [ handbody; prhand ] in
  let lleg = P.pathp [ origin; plfoot ] in
  let rleg = P.pathp [ origin; prfoot ] in
  [ body; head; neck; larm; rarm; lhand; rhand; lfoot; rleg; lleg; rfoot ]

let rot p i = rotate (360. *. i) p

let rot_lit i =
  little_man
    ~phead:(rot (bpp (0., 2.)) i)
    ~plhand:(rot (bpp (-2., 1.)) i)
    ~prhand:(rot (bpp (2., 1.)) i)
    ~plfoot:(rot (bpp (-2., -1.)) i)
    ~prfoot:(rot (bpp (2., -1.)) i)
  |> List.map (P.scale (N.cm 1.))
  |> List.map P.draw |> Command.seq

(*let () = List.iter (fun (s,f) -> Metapost.emit s f)
  ["little_man0", rot_lit 0.;
   "little_man1", rot_lit 0.1;
   "little_man2", rot_lit 0.2;
   "little_man3", rot_lit 0.3;
   "little_man4", rot_lit 0.4;]
*)

let _ = GMain.Main.init ()

let width = ref 400

let height = ref 400

let new_pixmap width height =
  let drawable = GDraw.pixmap ~width ~height () in
  drawable#set_foreground `WHITE;
  drawable#rectangle ~x:0 ~y:0 ~width ~height ~filled:true ();
  drawable

let pm = ref (new_pixmap !width !height)

let need_update = ref true

let init_time = Unix.gettimeofday ()

let fps =
  let nb = ref 0 in
  let time = ref (Unix.time ()) in
  fun () ->
    let time2 = Unix.time () in
    if time2 -. !time > 1. then (
      Format.printf "fps : %i@." !nb;
      nb := 0;
      time := time2 )
    else incr nb

let paint () =
  let cr = Cairo_lablgtk.create !pm#pixmap in
  !pm#rectangle ~x:0 ~y:0 ~width:!width ~height:!height ~filled:true ();
  let w, h = (float_of_int !width, float_of_int !height) in
  let i = Unix.gettimeofday () -. init_time in
  let fig = rot_lit i in
  let fig = Picture.shift (ptp (w /. 2., h /. 2.)) fig in
  fps ();
  Cairost.emit_cairo cr (w, h) fig

let refresh da =
  need_update := true;
  GtkBase.Widget.queue_draw da#as_widget

let grow_pixmap () =
  pm := new_pixmap !width !height;
  need_update := true

(* no need to queue a redraw here, an expose
   event should follow the configure, right ? *)

let config_cb ev =
  let w = GdkEvent.Configure.width ev in
  let h = GdkEvent.Configure.height ev in
  let has_grown = w > !width || h > !height in
  width := w;
  height := h;
  if has_grown then grow_pixmap ();
  true

let expose da x y width height =
  let gwin = da#misc#window in
  let d = new GDraw.drawable gwin in
  d#put_pixmap ~x ~y ~xsrc:x ~ysrc:y ~width ~height !pm#pixmap

let expose_cb da ev =
  let area = GdkEvent.Expose.area ev in
  let module GR = Gdk.Rectangle in
  if !need_update then paint ();
  expose da (GR.x area) (GR.y area) (GR.width area) (GR.height area);
  refresh da;
  true

let button_ev da ev =
  match GdkEvent.get_type ev with
  | `BUTTON_RELEASE ->
      refresh da;
      true
  | _ -> false

let init packing =
  let da = GMisc.drawing_area ~width:!width ~height:!height ~packing () in
  da#misc#set_can_focus true;
  da#event#add [ `KEY_PRESS; `BUTTON_MOTION; `BUTTON_PRESS; `BUTTON_RELEASE ];
  ignore (da#event#connect#expose (expose_cb da));
  ignore (da#event#connect#configure config_cb);
  ignore (da#event#connect#button_release (button_ev da))

let main =
  let w =
    GWindow.window ~title:"Cairo spline demo" ~allow_grow:true
      ~allow_shrink:true ()
  in
  ignore (w#connect#destroy GMain.quit);
  init w#add;
  w#show ();
  GMain.main ()