-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathdraw.ml
255 lines (215 loc) · 7.5 KB
/
draw.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
(* This file is part of the tutorial
http://cairo.forge.ocamlcore.org/tutorial/
*)
let pi_4 = atan 1.
let two_pi = 8. *. pi_4
let draw_path_moveto cr =
Cairo.set_line_width cr 0.1;
Cairo.set_source_rgb cr 0. 0. 0.;
Cairo.move_to cr 0.25 0.25
let draw_path_lineto cr =
draw_path_moveto cr;
Cairo.line_to cr 0.5 0.375;
Cairo.rel_line_to cr 0.25 (-0.125)
let draw_path_arcto cr =
draw_path_lineto cr;
Cairo.arc cr 0.5 0.5 ~r:(0.25 *. sqrt 2.) ~a1:(-. pi_4) ~a2:pi_4
let draw_path_curveto cr =
draw_path_arcto cr;
Cairo.rel_curve_to cr (-0.25) (-0.125) (-0.25) 0.125 (-0.5) 0.
let draw_path_close cr =
draw_path_curveto cr;
Cairo.Path.close cr
let draw_textextents cr =
let text = "joy" in
Cairo.set_font_size cr 0.5;
(* Drawing code goes here *)
Cairo.set_source_rgb cr 0.0 0.0 0.0;
Cairo.select_font_face cr "Georgia" ~weight:Cairo.Bold;
let ux, uy = Cairo.device_to_user_distance cr 1. 1. in
let px = max ux uy in
let fe = Cairo.font_extents cr in
let te = Cairo.text_extents cr text in
let x = 0.5 -. te.Cairo.x_bearing -. te.Cairo.width /. 2.
and y = 0.5 -. fe.Cairo.descent +. fe.Cairo.baseline /. 2. in
(* baseline, descent, ascent, height *)
Cairo.set_line_width cr (4. *. px);
Cairo.set_dash cr [| 9. *. px |];
Cairo.set_source_rgba cr 0. 0.6 0. 0.5;
let horizontal_line y =
Cairo.move_to cr (x +. te.Cairo.x_bearing) y;
Cairo.rel_line_to cr te.Cairo.width 0. in
horizontal_line y;
horizontal_line (y +. fe.Cairo.descent);
horizontal_line (y -. fe.Cairo.ascent);
horizontal_line (y -. fe.Cairo.baseline);
Cairo.stroke cr;
(* extents: width & height (in dashed blue) *)
Cairo.set_source_rgba cr 0. 0. 0.75 0.5;
Cairo.set_line_width cr px;
Cairo.set_dash cr [| 3. *. px |];
Cairo.rectangle cr (x +. te.Cairo.x_bearing)
(y +. te.Cairo.y_bearing) ~w:te.Cairo.width ~h:te.Cairo.height;
Cairo.stroke cr;
(* text *)
Cairo.move_to cr x y;
Cairo.set_source_rgb cr 0. 0. 0.;
Cairo.show_text cr text;
(* bearing (solid blue line) *)
Cairo.set_dash cr [| |];
Cairo.set_line_width cr (2. *. px);
Cairo.set_source_rgba cr 0. 0. 0.75 0.5;
Cairo.move_to cr x y;
Cairo.rel_line_to cr te.Cairo.x_bearing te.Cairo.y_bearing;
Cairo.stroke cr;
(* text's advance (blue dot) *)
Cairo.set_source_rgba cr 0. 0. 0.75 0.5;
Cairo.arc cr (x +. te.Cairo.x_advance) (y +. te.Cairo.y_advance)
~r:(6. *. px) ~a1:0. ~a2:two_pi;
Cairo.fill cr;
(* reference point (x,y) (red dot) *)
Cairo.arc cr x y ~r:(6. *. px) ~a1:0. ~a2:two_pi;
Cairo.set_source_rgba cr 0.75 0. 0. 0.5;
Cairo.fill cr
;;
let draw_setsourcegradient cr =
let radpat = Cairo.Pattern.create_radial ~x0:0.25 ~y0:0.25 ~r0:0.1
~x1:0.5 ~y1:0.5 ~r1:0.5 in
Cairo.Pattern.add_color_stop_rgb radpat 1.0 0.8 0.8;
Cairo.Pattern.add_color_stop_rgb radpat ~ofs:1. 0.9 0.0 0.0;
for i = 1 to 9 do
for j = 1 to 9 do
Cairo.rectangle cr (float i /. 10.0 -. 0.04) (float j /. 10.0 -. 0.04)
~w:0.08 ~h:0.08;
done;
done;
Cairo.set_source cr radpat;
Cairo.fill cr;
let linpat = Cairo.Pattern.create_linear ~x0:0.25 ~y0:0.35
~x1:0.75 ~y1:0.65 in
Cairo.Pattern.add_color_stop_rgba linpat ~ofs:0.00 1. 1. 1. 0.;
Cairo.Pattern.add_color_stop_rgba linpat ~ofs:0.25 0. 1. 0. 0.5;
Cairo.Pattern.add_color_stop_rgba linpat ~ofs:0.50 1. 1. 1. 0.;
Cairo.Pattern.add_color_stop_rgba linpat ~ofs:0.75 0. 0. 1. 0.5;
Cairo.Pattern.add_color_stop_rgba linpat ~ofs:1.00 1. 1. 1. 0.;
Cairo.rectangle cr 0.0 0.0 ~w:1. ~h:1.;
Cairo.set_source cr linpat;
Cairo.fill cr
;;
let get_point = function
| Cairo.MOVE_TO(x,y) -> x,y
| Cairo.LINE_TO(x,y) -> x,y
| Cairo.CURVE_TO(x,y, _,_, _,_) -> x,y
| Cairo.CLOSE_PATH -> failwith "get_point"
let path_diagram cr =
let path = Cairo.Path.to_array(Cairo.Path.copy_flat cr) in
let px, py = Cairo.device_to_user_distance cr 3. 3. in
Cairo.set_line_width cr (max px py);
Cairo.set_source_rgb cr 0. 0.6 0.;
Cairo.stroke cr;
(* Draw markers at the first and the last point of the path, but
only if the path is not closed.
If the last path manipulation was a Cairo.Path.close, then we
can detect this at the end of the path array. The [CLOSE_PATH]
element will be followed by a [MOVE_TO] element (since cairo
1.2.4), so we need to check position [Array.length path - 2].
See the module [Path] for further explanations. *)
let len = Array.length path in
if len <= 1 || path.(len - 2) <> Cairo.CLOSE_PATH then (
(* Get the first point in the path *)
let x, y = get_point path.(0) in
let px, py = Cairo.device_to_user_distance cr 5. 5. in
let px = max px py in
Cairo.arc cr x y ~r:px ~a1:0. ~a2:two_pi;
Cairo.set_source_rgba cr 0.0 0.6 0.0 0.5;
Cairo.fill cr;
let x, y = get_point path.(len - 1) in
Cairo.arc cr x y ~r:px ~a1:0. ~a2:two_pi;
Cairo.set_source_rgba cr 0.0 0.0 0.75 0.5;
Cairo.fill cr;
)
;;
let draw_path_curveto_hints cr =
Cairo.save cr;
let px, py = Cairo.device_to_user_distance cr 3. 3. in
let px = max px py in
Cairo.set_source_rgba cr 0.5 0. 0. 0.5;
Cairo.Path.sub cr;
Cairo.arc cr 0.5 0.625 ~r:px ~a1:0. ~a2:two_pi;
Cairo.fill cr;
Cairo.arc cr 0.5 0.875 ~r:px ~a1:0. ~a2:two_pi;
Cairo.fill cr;
let px, py = Cairo.device_to_user_distance cr 2. 2. in
let px = max px py in
Cairo.set_line_width cr px;
Cairo.set_source_rgba cr 0.5 0. 0. 0.25;
Cairo.move_to cr 0.25 0.75;
Cairo.rel_line_to cr 0.25 0.125;
Cairo.stroke cr;
Cairo.move_to cr 0.75 0.75;
Cairo.rel_line_to cr (-0.25) (-0.125);
Cairo.stroke cr;
Cairo.restore cr
;;
let draw_setsourcergba cr =
Cairo.set_source_rgb cr 0. 0. 0.;
Cairo.move_to cr 0. 0.;
Cairo.line_to cr 1. 1.;
Cairo.move_to cr 1. 0.;
Cairo.line_to cr 0. 1.;
Cairo.set_line_width cr 0.2;
Cairo.stroke cr;
Cairo.rectangle cr 0. 0. ~w:0.5 ~h:0.5;
Cairo.set_source_rgba cr 1. 0. 0. 0.80;
Cairo.fill cr;
Cairo.rectangle cr 0. 0.5 ~w:0.5 ~h:0.5;
Cairo.set_source_rgba cr 0. 1. 0. 0.60;
Cairo.fill cr;
Cairo.rectangle cr 0.5 0. ~w:0.5 ~h:0.5;
Cairo.set_source_rgba cr 0. 0. 1. 0.40;
Cairo.fill cr
;;
let draw_diagram name cr =
(match name with
| "setsourcergba" -> draw_setsourcergba cr
| "setsourcegradient" -> draw_setsourcegradient cr
| "path-moveto" -> draw_path_moveto cr
| "path-lineto" -> draw_path_lineto cr
| "path-arcto" -> draw_path_arcto cr
| "path-curveto" ->
draw_path_curveto_hints cr;
draw_path_curveto cr
| "path-close" -> draw_path_close cr
| "textextents" -> draw_textextents cr
| _ -> assert false
);
if String.sub name 0 5 = "path-" then path_diagram cr
let diagram name =
let width = 120. and height = 120. in
let svg_filename = name ^ ".svg"
and png_filename = name ^ ".png" in
let surf = Cairo.SVG.create svg_filename ~w:width ~h:height in
let cr = Cairo.create surf in
Cairo.scale cr width height;
Cairo.set_line_width cr 0.01;
Cairo.rectangle cr 0. 0. ~w:1. ~h:1.;
Cairo.set_source_rgb cr 1. 1. 1.;
Cairo.fill cr;
draw_diagram name cr;
let ux, uy = Cairo.device_to_user_distance cr 2. 2. in
Cairo.set_line_width cr (max ux uy);
Cairo.set_source_rgb cr 0. 0. 0.;
Cairo.rectangle cr 0. 0. ~w:1. ~h:1.;
Cairo.stroke cr;
(* write output *)
Cairo.PNG.write surf png_filename;
Cairo.Surface.finish surf
let () =
diagram "setsourcergba";
diagram "setsourcegradient";
diagram "path-moveto";
diagram "path-lineto";
diagram "path-arcto";
diagram "path-curveto";
diagram "path-close";
diagram "textextents"