p1788 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1249 @@
1
+ /* Copyright (C) 2022 Théotime Bollengier <theotime.bollengier@ensta-bretagne.fr>
2
+ *
3
+ * This file is part of P1788. <https://gitlab.ensta-bretagne.fr/bollenth/p1788>
4
+ *
5
+ * P1788 is free software: you can redistribute it and/or modify it
6
+ * under the terms of the GNU General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License,
8
+ * or (at your option) any later version.
9
+ *
10
+ * P1788 is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ * See the GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with P1788. If not, see <https://www.gnu.org/licenses/>. 
17
+ */
18
+
19
+ /* TODO
20
+ * wrapper:
21
+ * - commands
22
+ * - color
23
+ * reset figure
24
+ * do not repeat commands, ex: stroke on then stroke of
25
+ */
26
+
27
+ #include <ruby.h>
28
+ #include "p1788.hh"
29
+ #include "figure.hh"
30
+
31
+
32
+ VALUE c_Figure;
33
+
34
+
35
+ static ID id_width;
36
+ static ID id_height;
37
+ static ID id_xrange;
38
+ static ID id_yrange;
39
+ static ID id_with_decorations;
40
+ static ID id_title;
41
+ static ID id_xlabel;
42
+ static ID id_ylabel;
43
+ static ID id_background_color;
44
+ static ID id_decoration_color;
45
+ static ID id_decoration_font;
46
+ static ID id_decoration_font_size;
47
+ static ID id_decoration_line_width;
48
+ static ID id_label_font_size;
49
+ static ID id_title_font_size;
50
+ static ID id_title_in_bold;
51
+
52
+
53
+ static void p1788_figure_free(void *p)
54
+ {
55
+ P1788Figure::Figure *f = (P1788Figure::Figure*)p;
56
+ delete f;
57
+ }
58
+
59
+
60
+ static size_t p1788_figure_size(const void* p)
61
+ {
62
+ P1788Figure::Figure *f = (P1788Figure::Figure*)p;
63
+ return f->allocated_size();
64
+ }
65
+
66
+
67
+ static const rb_data_type_t p1788_figure_type = {
68
+ .wrap_struct_name = "P1788::Figure",
69
+ .function = {
70
+ .dmark = NULL,
71
+ .dfree = p1788_figure_free,
72
+ .dsize = p1788_figure_size,
73
+ },
74
+ .parent = NULL,
75
+ .data = NULL,
76
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
77
+ };
78
+
79
+
80
+ static VALUE p1788_figure_alloc(VALUE klass)
81
+ {
82
+ P1788Figure::Figure *f = new P1788Figure::Figure();
83
+ return TypedData_Wrap_Struct(klass, &p1788_figure_type, f);
84
+ }
85
+
86
+
87
+ static inline P1788Figure::Figure& p1788_figure_rb2ref(VALUE obj) {
88
+ P1788Figure::Figure *f;
89
+ TypedData_Get_Struct(obj, P1788Figure::Figure, &p1788_figure_type, f);
90
+ return *f;
91
+ }
92
+
93
+
94
+ static const char *color_regexp = "^((\\#(\\h{8}))|(((\\#(\\h{6}))|([krgybmcwtKRGYBMCWT]))(\\.\\d+)?))$";
95
+ // match[] 12 3 456 7 8 9
96
+
97
+ /* Tango colors */
98
+ #define P1788_FIGURE_COLOR_k 0x00000000
99
+ #define P1788_FIGURE_COLOR_r 0xCC000000
100
+ #define P1788_FIGURE_COLOR_g 0x4E9A0600
101
+ #define P1788_FIGURE_COLOR_y 0xC4A00000
102
+ #define P1788_FIGURE_COLOR_b 0x3465A400
103
+ #define P1788_FIGURE_COLOR_m 0x75507B00
104
+ #define P1788_FIGURE_COLOR_c 0x06989A00
105
+ #define P1788_FIGURE_COLOR_w 0xD3D7CF00
106
+ #define P1788_FIGURE_COLOR_K 0x55575300
107
+ #define P1788_FIGURE_COLOR_R 0xEF292900
108
+ #define P1788_FIGURE_COLOR_G 0x8AE23400
109
+ #define P1788_FIGURE_COLOR_Y 0xFCE94F00
110
+ #define P1788_FIGURE_COLOR_B 0x739FCF00
111
+ #define P1788_FIGURE_COLOR_M 0xAD7FA800
112
+ #define P1788_FIGURE_COLOR_C 0x34E2E200
113
+ #define P1788_FIGURE_COLOR_W 0xFFFFFF00
114
+
115
+
116
+ static unsigned int p1788_figure_parse_color(VALUE c)
117
+ {
118
+ if (RB_TYPE_P(c, T_FIXNUM) | RB_TYPE_P(c, T_BIGNUM))
119
+ return NUM2UINT(c);
120
+ if (!RB_TYPE_P(c, T_STRING))
121
+ rb_raise(rb_eTypeError, "expecting a string or an integer, not a %" PRIsVALUE,
122
+ rb_class_name(rb_class_of(c)));
123
+ unsigned int col = 0;
124
+ VALUE cr = rb_str_new_static(color_regexp, 62);
125
+ VALUE regexp = rb_class_new_instance(1, &cr, rb_cRegexp);
126
+ VALUE match = rb_funcallv(c, id_match, 1, &regexp);
127
+ if (NIL_P(match))
128
+ rb_raise(rb_eRuntimeError, "cannot parse \"%" PRIsVALUE "\" as a color", c);
129
+ VALUE m;
130
+
131
+ /* Full hexa */
132
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(3));
133
+ if (m != Qnil)
134
+ col = NUM2UINT(rb_funcall(m, id_to_i, 1, INT2NUM(16)));
135
+ else {
136
+ /* Alpha */
137
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(9));
138
+ if (m != Qnil)
139
+ col |= std::min(255, std::max(0, (int)round(255.0*NUM2DBL(rb_funcall(m, id_to_f, 0)))));
140
+ else
141
+ col |= 0x000000ff;
142
+ /* Color hexa */
143
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(7));
144
+ if (m != Qnil)
145
+ col |= NUM2UINT(rb_funcall(m, id_to_i, 1, INT2NUM(16))) << 8;
146
+ /* Color leter */
147
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(8));
148
+ if (m != Qnil) {
149
+ switch (*StringValuePtr(m)) {
150
+ case 'k': col |= P1788_FIGURE_COLOR_k; break;
151
+ case 'r': col |= P1788_FIGURE_COLOR_r; break;
152
+ case 'g': col |= P1788_FIGURE_COLOR_g; break;
153
+ case 'y': col |= P1788_FIGURE_COLOR_y; break;
154
+ case 'b': col |= P1788_FIGURE_COLOR_b; break;
155
+ case 'm': col |= P1788_FIGURE_COLOR_m; break;
156
+ case 'c': col |= P1788_FIGURE_COLOR_c; break;
157
+ case 'w': col |= P1788_FIGURE_COLOR_w; break;
158
+ case 'K': col |= P1788_FIGURE_COLOR_K; break;
159
+ case 'R': col |= P1788_FIGURE_COLOR_R; break;
160
+ case 'G': col |= P1788_FIGURE_COLOR_G; break;
161
+ case 'Y': col |= P1788_FIGURE_COLOR_Y; break;
162
+ case 'B': col |= P1788_FIGURE_COLOR_B; break;
163
+ case 'M': col |= P1788_FIGURE_COLOR_M; break;
164
+ case 'C': col |= P1788_FIGURE_COLOR_C; break;
165
+ case 'W': col |= P1788_FIGURE_COLOR_W; break;
166
+ default:
167
+ col &= 0xffffff00;
168
+ }
169
+ }
170
+ }
171
+ return col;
172
+ }
173
+
174
+
175
+ static inline VALUE p1788_cppstring_to_rbstring_utf8(const std::string& cppstring) {
176
+ return rb_str_freeze(rb_utf8_str_new(cppstring.c_str(), cppstring.size()));
177
+ }
178
+
179
+
180
+ static std::string p1788_rbstring_to_cppstring(VALUE rbstring) {
181
+ if (!RB_TYPE_P(rbstring, T_STRING))
182
+ rb_raise(rb_eTypeError, "expecting a string, not a %" PRIsVALUE, rb_class_name(rb_class_of(rbstring)));
183
+ return std::string(StringValuePtr(rbstring), RSTRING_LEN(rbstring));
184
+ }
185
+
186
+
187
+ /* Return the number of bytes allocated for the figure.
188
+ * @!visibility private
189
+ * @return [Integer]
190
+ */
191
+ static VALUE p1788_figure_allocated_size(VALUE self)
192
+ {
193
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
194
+ return SIZET2NUM(p1788_figure_size(&f));
195
+ }
196
+
197
+
198
+ /* The title to be drawn on top of the figure.
199
+ * Set the title to _nil_ to remove the title.
200
+ * @return [String, nil]
201
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :title; end; end
202
+ */
203
+ static VALUE p1788_figure_get_title(VALUE self)
204
+ {
205
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
206
+ const std::string& t = f.title();
207
+ if (t.empty())
208
+ return Qnil;
209
+ return p1788_cppstring_to_rbstring_utf8(t);
210
+ }
211
+
212
+
213
+ static VALUE p1788_figure_set_title(VALUE self, VALUE str)
214
+ {
215
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
216
+ if (NIL_P(str))
217
+ f.set_title("");
218
+ else
219
+ f.set_title(p1788_rbstring_to_cppstring(rb_funcallv(str, id_to_s, 0, NULL)));
220
+ return self;
221
+ }
222
+
223
+
224
+ /* The label to be drawn below the horizontal axis.
225
+ * Set the label to _nil_ to remove it.
226
+ * @return [String, nil]
227
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :xlabel; end; end
228
+ */
229
+ static VALUE p1788_figure_get_xlabel(VALUE self)
230
+ {
231
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
232
+ const std::string& t = f.xlabel();
233
+ if (t.empty())
234
+ return Qnil;
235
+ return p1788_cppstring_to_rbstring_utf8(t);
236
+ }
237
+
238
+
239
+ static VALUE p1788_figure_set_xlabel(VALUE self, VALUE str)
240
+ {
241
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
242
+ if (NIL_P(str))
243
+ f.set_xlabel("");
244
+ else
245
+ f.set_xlabel(p1788_rbstring_to_cppstring(rb_funcallv(str, id_to_s, 0, NULL)));
246
+ return self;
247
+ }
248
+
249
+
250
+ /* The label to be drawn on the left of the vertical axis.
251
+ * Set the label to _nil_ to remove it.
252
+ * @return [String, nil]
253
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :ylabel; end; end
254
+ */
255
+ static VALUE p1788_figure_get_ylabel(VALUE self)
256
+ {
257
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
258
+ const std::string& t = f.ylabel();
259
+ if (t.empty())
260
+ return Qnil;
261
+ return p1788_cppstring_to_rbstring_utf8(t);
262
+ }
263
+
264
+
265
+ static VALUE p1788_figure_set_ylabel(VALUE self, VALUE str)
266
+ {
267
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
268
+ if (NIL_P(str))
269
+ f.set_ylabel("");
270
+ else
271
+ f.set_ylabel(p1788_rbstring_to_cppstring(rb_funcallv(str, id_to_s, 0, NULL)));
272
+ return self;
273
+ }
274
+
275
+
276
+ /* The font used to display the title, labels and graduations.
277
+ * Set the font to _nil_ to reset it to its default ("Sans").
278
+ * @return [String, nil]
279
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :decoration_font; end; end
280
+ */
281
+ static VALUE p1788_figure_get_decoration_font(VALUE self)
282
+ {
283
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
284
+ const std::string& t = f.graduation_font();
285
+ if (t.empty())
286
+ return Qnil;
287
+ return p1788_cppstring_to_rbstring_utf8(t);
288
+ }
289
+
290
+
291
+ static VALUE p1788_figure_set_decoration_font(VALUE self, VALUE str)
292
+ {
293
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
294
+ if (NIL_P(str))
295
+ f.set_graduation_font("");
296
+ else
297
+ f.set_graduation_font(p1788_rbstring_to_cppstring(rb_funcallv(str, id_to_s, 0, NULL)));
298
+ return self;
299
+ }
300
+
301
+
302
+ /* Size of the font used to display the title, labels and graduations.
303
+ * @return [Float]
304
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :decoration_font_size; end; end
305
+ */
306
+ static VALUE p1788_figure_get_decoration_font_size(VALUE self)
307
+ {
308
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
309
+ return DBL2NUM(f.graduation_font_size());
310
+ }
311
+
312
+
313
+ static VALUE p1788_figure_set_decoration_font_size(VALUE self, VALUE s)
314
+ {
315
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
316
+ if (NIL_P(s))
317
+ f.set_graduation_font_size(0.0);
318
+ else
319
+ f.set_graduation_font_size((float)NUM2DBL(rb_funcallv(s, id_to_f, 0, NULL)));
320
+ return self;
321
+ }
322
+
323
+
324
+ /* Size of the font used to display the title.
325
+ *
326
+ * If set to _nil_, the title will automatically be drawn at 1.23 times the decoration font size.
327
+ * @return [Float, nil]
328
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :title_font_size; end; end
329
+ */
330
+ static VALUE p1788_figure_get_title_font_size(VALUE self)
331
+ {
332
+ double s = p1788_figure_rb2ref(self).title_font_size_as_set();
333
+ if (s <= 0.0)
334
+ return Qnil;
335
+ return DBL2NUM(s);
336
+ }
337
+
338
+
339
+ static VALUE p1788_figure_set_title_font_size(VALUE self, VALUE s)
340
+ {
341
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
342
+ if (NIL_P(s))
343
+ f.set_title_font_size(0.0);
344
+ else
345
+ f.set_title_font_size((float)NUM2DBL(rb_funcallv(s, id_to_f, 0, NULL)));
346
+ return self;
347
+ }
348
+
349
+
350
+ /* Size of the font used to display the labels.
351
+ *
352
+ * If set to _nil_, the labels will automatically be drawn at 1.1 times the decoration font size.
353
+ * @return [Float, nil]
354
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :label_font_size; end; end
355
+ */
356
+ static VALUE p1788_figure_get_label_font_size(VALUE self)
357
+ {
358
+ double s = p1788_figure_rb2ref(self).label_font_size_as_set();
359
+ if (s <= 0.0)
360
+ return Qnil;
361
+ return DBL2NUM(s);
362
+ }
363
+
364
+
365
+ static VALUE p1788_figure_set_label_font_size(VALUE self, VALUE s)
366
+ {
367
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
368
+ if (NIL_P(s))
369
+ f.set_label_font_size(0.0);
370
+ else
371
+ f.set_label_font_size((float)NUM2DBL(rb_funcallv(s, id_to_f, 0, NULL)));
372
+ return self;
373
+ }
374
+
375
+
376
+ /* Line width for drawing the frame around the figure.
377
+ * @return [Float]
378
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :decoration_line_width; end; end
379
+ */
380
+ static VALUE p1788_figure_get_decoration_line_width(VALUE self)
381
+ {
382
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
383
+ return DBL2NUM(f.graduation_stroke_width());
384
+ }
385
+
386
+
387
+ static VALUE p1788_figure_set_decoration_line_width(VALUE self, VALUE w)
388
+ {
389
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
390
+ f.set_graduation_stroke_width((float)NUM2DBL(rb_funcallv(w, id_to_f, 0, NULL)));
391
+ return self;
392
+ }
393
+
394
+
395
+ /* Color of the decoration (frame, graduations, title and axis labels).
396
+ *
397
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
398
+ * for the red, green, blue and alpha channels.
399
+ *
400
+ * This attribute can also be set to a string, like `"#cc000080"`, `"#cc0000.5"`,`"r.5"` or `"W"`.
401
+ * @return [Integer]
402
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :decoration_color; end; end
403
+ */
404
+ static VALUE p1788_figure_get_decoration_color(VALUE self)
405
+ {
406
+ return UINT2NUM(p1788_figure_rb2ref(self).graduation_color());
407
+ }
408
+
409
+ static VALUE p1788_figure_set_decoration_color(VALUE self, VALUE c)
410
+ {
411
+ unsigned int n = p1788_figure_parse_color(c);
412
+ p1788_figure_rb2ref(self).set_graduation_color(n);
413
+ return self;
414
+ }
415
+
416
+
417
+ /* Background color of the figure.
418
+ *
419
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
420
+ * for the red, green, blue and alpha channels.
421
+ *
422
+ * This attribute can also be set to a string, like `"#cc000080"`, `"#cc0000.5"`,`"r.5"` or `"W"`.
423
+ * @return [Integer]
424
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :background_color; end; end
425
+ */
426
+ static VALUE p1788_figure_get_background_color(VALUE self)
427
+ {
428
+ return UINT2NUM(p1788_figure_rb2ref(self).background_color());
429
+ }
430
+
431
+ static VALUE p1788_figure_set_background_color(VALUE self, VALUE c)
432
+ {
433
+ unsigned int n = p1788_figure_parse_color(c);
434
+ p1788_figure_rb2ref(self).set_background_color(n);
435
+ return self;
436
+ }
437
+
438
+
439
+ /* The width of the output image, in pixels.
440
+ * Set the width to _nil_ or 0 to get automatic sizing.
441
+ * @return [Integer, nil]
442
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :width; end; end
443
+ */
444
+ static VALUE p1788_figure_get_width(VALUE self)
445
+ {
446
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
447
+ int i = f.width();
448
+ if (i < 1)
449
+ return Qnil;
450
+ return INT2NUM(i);
451
+ }
452
+
453
+ static VALUE p1788_figure_set_width(VALUE self, VALUE w)
454
+ {
455
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
456
+ if (NIL_P(w)) {
457
+ f.set_width(0);
458
+ return self;
459
+ }
460
+ int i = NUM2INT(rb_funcallv(w, id_to_i, 0, NULL));
461
+ if (i <= 0)
462
+ f.set_width(0);
463
+ else
464
+ f.set_width(i);
465
+ return self;
466
+ }
467
+
468
+
469
+ /* The height of the output image, in pixels.
470
+ * Set the height to _nil_ or 0 to get automatic sizing.
471
+ * @return [Integer, nil]
472
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :height; end; end
473
+ */
474
+ static VALUE p1788_figure_get_height(VALUE self)
475
+ {
476
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
477
+ int i = f.height();
478
+ if (i < 1)
479
+ return Qnil;
480
+ return INT2NUM(i);
481
+ }
482
+
483
+ static VALUE p1788_figure_set_height(VALUE self, VALUE h)
484
+ {
485
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
486
+ if (NIL_P(h)) {
487
+ f.set_height(0);
488
+ return self;
489
+ }
490
+ int i = NUM2INT(rb_funcallv(h, id_to_i, 0, NULL));
491
+ if (i <= 0)
492
+ f.set_height(0);
493
+ else
494
+ f.set_height(i);
495
+ return self;
496
+ }
497
+
498
+
499
+ /* The horizontal range that will be displayed.
500
+ * Set _xrange_ to _nil_ or an empty interval to get automatic range adjustment.
501
+ * @return [Interval, nil]
502
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :xrange; end; end
503
+ */
504
+ static VALUE p1788_figure_get_xrange(VALUE self)
505
+ {
506
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
507
+ const Interval& r = f.xlim();
508
+ if (!Interval::is_common_interval(r))
509
+ return Qnil;
510
+ VALUE res;
511
+ P1788_NEW_RB_INTERVAL(res, r);
512
+ return res;
513
+ }
514
+
515
+ static VALUE p1788_figure_set_xrange(VALUE self, VALUE rng)
516
+ {
517
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
518
+ if (NIL_P(rng))
519
+ f.set_xlimit(Interval());
520
+ else
521
+ f.set_xlimit(p1788_rbobj2interval(rng));
522
+ return self;
523
+ }
524
+
525
+
526
+ /* The vertical range that will be displayed.
527
+ * Set _yrange_ to _nil_ or an empty interval to get automatic range adjustment.
528
+ * @return [Interval, nil]
529
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :yrange; end; end
530
+ */
531
+ static VALUE p1788_figure_get_yrange(VALUE self)
532
+ {
533
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
534
+ const Interval& r = f.ylim();
535
+ if (!Interval::is_common_interval(r))
536
+ return Qnil;
537
+ VALUE res;
538
+ P1788_NEW_RB_INTERVAL(res, r);
539
+ return res;
540
+ }
541
+
542
+ static VALUE p1788_figure_set_yrange(VALUE self, VALUE rng)
543
+ {
544
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
545
+ if (NIL_P(rng))
546
+ f.set_ylimit(Interval());
547
+ else
548
+ f.set_ylimit(p1788_rbobj2interval(rng));
549
+ return self;
550
+ }
551
+
552
+
553
+ /* Set the horizontal and vertical range that will be displayed.
554
+ *
555
+ * This is the same as calling both {xrange=} and {yrange=}.
556
+ * Set to _nil_ to reset ranges and get automatic display range according to the content.
557
+ * @param box [IntervalVector, nil] an interval vector with at least two elements, or nil
558
+ * @return [self]
559
+ */
560
+ static VALUE p1788_figure_set_range(VALUE self, VALUE box)
561
+ {
562
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
563
+ if (NIL_P(box)) {
564
+ f.set_xlimit(Interval());
565
+ f.set_ylimit(Interval());
566
+ return self;
567
+ }
568
+ if (!is_an_interval_vector(box))
569
+ rb_raise(rb_eTypeError, "expecting a %" PRIsVALUE ", not a %" PRIsVALUE,
570
+ rb_class_name(c_IntervalVector), rb_class_name(rb_class_of(box)));
571
+ const IntervalVector& r = p1788_vector_rb2ref(box);
572
+ if (r.size() < 2)
573
+ rb_raise(rb_eRuntimeError, "the interval vector must have at least two elements (for the x an y axis)");
574
+ f.set_xlimit(r[0]);
575
+ f.set_ylimit(r[1]);
576
+ return self;
577
+ }
578
+
579
+
580
+ /* Draw decorations around the figure?.
581
+ *
582
+ * If set to _false_, no frame, ticks, title nor labels will be drawn around the figure,
583
+ * even if they are set. The output image size will be _width_ x _height_.
584
+ *
585
+ * If set to _true_, a frame and graduations will be drawn around the figure.
586
+ * If they are set, title and labels will also be drawn.
587
+ * The output image size will be larger than _width_ x _height_.
588
+ * @return [Boolean]
589
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :with_decorations; end; end
590
+ */
591
+ static VALUE p1788_figure_get_with_decorations(VALUE self)
592
+ {
593
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
594
+ return f.print_graduations() ? Qtrue : Qfalse;
595
+ }
596
+
597
+
598
+ static VALUE p1788_figure_set_with_decorations(VALUE self, VALUE b)
599
+ {
600
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
601
+ f.set_print_graduations((NIL_P(b) || b == Qfalse) ? false : true);
602
+ return self;
603
+ }
604
+
605
+
606
+ /* Draw the title in a bold font?
607
+ *
608
+ * If {with_decorations} is set to _true_ and
609
+ * {title} is not _nil_ nor empty,
610
+ * the title will be drawn in bold or normal weight according
611
+ * to this attribute.
612
+ * @return [Boolean]
613
+ * @!parse [ruby] module P1788; class Figure; attr_accessor :title_in_bold; end; end
614
+ */
615
+ static VALUE p1788_figure_get_title_in_bold(VALUE self)
616
+ {
617
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
618
+ return f.title_bold() ? Qtrue : Qfalse;
619
+ }
620
+
621
+
622
+ static VALUE p1788_figure_set_title_in_bold(VALUE self, VALUE b)
623
+ {
624
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
625
+ f.set_title_bold((NIL_P(b) || b == Qfalse) ? false : true);
626
+ return self;
627
+ }
628
+
629
+
630
+ /* Returns the union of all the boxes that have ben sent to the figure for drwawing.
631
+ * @return [IntervalVector] a two interval vector, for _x_ and _y_.
632
+ */
633
+ static VALUE p1788_figure_content_range(VALUE self)
634
+ {
635
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
636
+ P1788Figure::Box b;
637
+ f.find_unbounded_limits(b);
638
+
639
+ IntervalVector *v = new IntervalVector(2);
640
+ (*v)[0] = b.x;
641
+ (*v)[1] = b.y;
642
+ return p1788_vector_alloc_from_pointer(v);
643
+ }
644
+
645
+
646
+ /* Returns a range that would be nice to display, according to the content of the figure.
647
+ * @return [IntervalVector] a two interval vector, for _x_ and _y_.
648
+ */
649
+ static VALUE p1788_figure_nice_range(VALUE self)
650
+ {
651
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
652
+ P1788Figure::Box b, c;
653
+ f.find_limits(c, b);
654
+
655
+ IntervalVector *v = new IntervalVector(2);
656
+ (*v)[0] = b.x;
657
+ (*v)[1] = b.y;
658
+ return p1788_vector_alloc_from_pointer(v);
659
+ }
660
+
661
+
662
+ #ifdef CAIRO_HAS_PNG_FUNCTIONS
663
+ /* Generate the figure and write the output image in PNG format to file _file_name_.
664
+ * @param file_name [String] name of the file to write the image to.
665
+ * @return [self]
666
+ */
667
+ static VALUE p1788_figure_write_png(VALUE self, VALUE file_name)
668
+ {
669
+ file_name = rb_funcallv(file_name, id_to_s, 0, NULL);
670
+ if (RSTRING_LEN(file_name) < 1)
671
+ rb_raise(rb_eRuntimeError, "file name is empty");
672
+ file_name = rb_funcallv(rb_cFile, rb_intern("absolute_path"), 1, &file_name);
673
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
674
+ std::string cppfname = p1788_rbstring_to_cppstring(file_name);
675
+ try {
676
+ f.write_png(cppfname);
677
+ } catch (const std::exception &e) {
678
+ rb_raise(rb_eRuntimeError, "%s", e.what());
679
+ }
680
+ return self;
681
+ }
682
+
683
+ /* Generate the figure and return its content as a string in the PNG format.
684
+ * @return [String]
685
+ */
686
+ static VALUE p1788_figure_to_png(VALUE self)
687
+ {
688
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
689
+ std::string s;
690
+ try {
691
+ s = std::move(f.to_png());
692
+ } catch (const std::exception &e) {
693
+ rb_raise(rb_eRuntimeError, "%s", e.what());
694
+ }
695
+ return rb_usascii_str_new(s.c_str(), s.size());
696
+ }
697
+ #endif
698
+
699
+
700
+ #ifdef CAIRO_HAS_SVG_SURFACE
701
+ /* Generate the figure and write the output image in SVG format to file _file_name_.
702
+ * @param file_name [String] name of the file to write the image to.
703
+ * @return [self]
704
+ */
705
+ static VALUE p1788_figure_write_svg(VALUE self, VALUE file_name)
706
+ {
707
+ file_name = rb_funcallv(file_name, id_to_s, 0, NULL);
708
+ if (RSTRING_LEN(file_name) < 1)
709
+ rb_raise(rb_eRuntimeError, "file name is empty");
710
+ file_name = rb_funcallv(rb_cFile, rb_intern("absolute_path"), 1, &file_name);
711
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
712
+ std::string cppfname = p1788_rbstring_to_cppstring(file_name);
713
+ try {
714
+ f.write_svg(cppfname);
715
+ } catch (const std::exception &e) {
716
+ rb_raise(rb_eRuntimeError, "%s", e.what());
717
+ }
718
+ return self;
719
+ }
720
+
721
+ /* Generate the figure and return its content as a string in the SVG format.
722
+ * @return [String]
723
+ */
724
+ static VALUE p1788_figure_to_svg(VALUE self)
725
+ {
726
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
727
+ std::string s;
728
+ try {
729
+ s = std::move(f.to_svg());
730
+ } catch (const std::exception &e) {
731
+ rb_raise(rb_eRuntimeError, "%s", e.what());
732
+ }
733
+ return rb_usascii_str_new(s.c_str(), s.size());
734
+ }
735
+ #endif
736
+
737
+
738
+ #ifdef CAIRO_HAS_PDF_SURFACE
739
+ /* Generate the figure and write the output image in PDF format to file _file_name_.
740
+ * @param file_name [String] name of the file to write the image to.
741
+ * @return [self]
742
+ */
743
+ static VALUE p1788_figure_write_pdf(VALUE self, VALUE file_name)
744
+ {
745
+ file_name = rb_funcallv(file_name, id_to_s, 0, NULL);
746
+ if (RSTRING_LEN(file_name) < 1)
747
+ rb_raise(rb_eRuntimeError, "file name is empty");
748
+ file_name = rb_funcallv(rb_cFile, rb_intern("absolute_path"), 1, &file_name);
749
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
750
+ std::string cppfname = p1788_rbstring_to_cppstring(file_name);
751
+ try {
752
+ f.write_pdf(cppfname);
753
+ } catch (const std::exception &e) {
754
+ rb_raise(rb_eRuntimeError, "%s", e.what());
755
+ }
756
+ return self;
757
+ }
758
+
759
+ /* Generate the figure and return its content as a string in the PDF format.
760
+ * @return [String]
761
+ */
762
+ static VALUE p1788_figure_to_pdf(VALUE self)
763
+ {
764
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
765
+ std::string s;
766
+ try {
767
+ s = std::move(f.to_pdf());
768
+ } catch (const std::exception &e) {
769
+ rb_raise(rb_eRuntimeError, "%s", e.what());
770
+ }
771
+ return rb_usascii_str_new(s.c_str(), s.size());
772
+ }
773
+ #endif
774
+
775
+
776
+ /* Tell the figure to draw new objects (boxes) with a border.
777
+ * @see stroke_off
778
+ * @see stroke_width=
779
+ * @see stroke_color=
780
+ * @see stroke?
781
+ * @return [self]
782
+ */
783
+ static VALUE p1788_figure_stroke_on(VALUE self)
784
+ {
785
+ p1788_figure_rb2ref(self).cmd_stroke(true);
786
+ return self;
787
+ }
788
+
789
+
790
+ /* Tell the figure to draw new objects (boxes) without border.
791
+ * @see stroke_on
792
+ * @see stroke?
793
+ * @return [self]
794
+ */
795
+ static VALUE p1788_figure_stroke_off(VALUE self)
796
+ {
797
+ p1788_figure_rb2ref(self).cmd_stroke(false);
798
+ return self;
799
+ }
800
+
801
+
802
+ /* Returns _true_ if new objects (boxes) will be drawned with a border.
803
+ * @see stroke_on
804
+ * @see stroke_off
805
+ * @return [Boolean]
806
+ */
807
+ static VALUE p1788_figure_get_stroke_onoff(VALUE self)
808
+ {
809
+ return p1788_figure_rb2ref(self).do_stroke() ? Qtrue : Qfalse;
810
+ }
811
+
812
+
813
+ /* Tell the figure to fill new objects (boxes) with the fill color.
814
+ * @see fill_off
815
+ * @see fill_color=
816
+ * @see fill?
817
+ * @return [self]
818
+ */
819
+ static VALUE p1788_figure_fill_on(VALUE self)
820
+ {
821
+ p1788_figure_rb2ref(self).cmd_fill(true);
822
+ return self;
823
+ }
824
+
825
+
826
+ /* Tell the figure not to fill new objects (boxes).
827
+ * @see fill_on
828
+ * @see fill?
829
+ * @return [self]
830
+ */
831
+ static VALUE p1788_figure_fill_off(VALUE self)
832
+ {
833
+ p1788_figure_rb2ref(self).cmd_fill(false);
834
+ return self;
835
+ }
836
+
837
+
838
+ /* Returns _true_ if new objects (boxes) will be filled.
839
+ * @see fill_on
840
+ * @see fill_off
841
+ * @return [Boolean]
842
+ */
843
+ static VALUE p1788_figure_get_fill_onoff(VALUE self)
844
+ {
845
+ return p1788_figure_rb2ref(self).do_fill() ? Qtrue : Qfalse;
846
+ }
847
+
848
+
849
+ /* Set the line width of the new objects (boxes) that will be drawned.
850
+ * @see stroke_on
851
+ * @see stroke_color=
852
+ * @return [self]
853
+ */
854
+ static VALUE p1788_figure_set_stroke_width(VALUE self, VALUE w)
855
+ {
856
+ p1788_figure_rb2ref(self).cmd_stroke_width(NUM2DBL(w));
857
+ return self;
858
+ }
859
+
860
+
861
+ /* Returns the current line width used to draw the border of objects (boxes).
862
+ * @see stroke_width=
863
+ * @return [Float]
864
+ */
865
+ static VALUE p1788_figure_get_stroke_width(VALUE self)
866
+ {
867
+ return DBL2NUM(p1788_figure_rb2ref(self).stroke_width());
868
+ }
869
+
870
+
871
+ /* Set the border color of the new objects (boxes) that will be drawned.
872
+ *
873
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
874
+ * for the red, green, blue and alpha channels.
875
+ *
876
+ * The color can also be set to a string, like `"#cc000080"`, `"#cc0000.5"`,`"r.5"` or `"W"`.
877
+ * @param c [Integer, String]
878
+ * @see stroke_on
879
+ * @see stroke_width=
880
+ * @return [self]
881
+ */
882
+ static VALUE p1788_figure_set_stroke_color(VALUE self, VALUE c)
883
+ {
884
+ unsigned int n = p1788_figure_parse_color(c);
885
+ p1788_figure_rb2ref(self).cmd_stroke_color(n);
886
+ return self;
887
+ }
888
+
889
+
890
+ /* Returns the current color used to draw the border of objects (boxes).
891
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
892
+ * for the red, green, blue and alpha channels.
893
+ * @see stroke_color=
894
+ * @return [Integer]
895
+ */
896
+ static VALUE p1788_figure_get_stroke_color(VALUE self)
897
+ {
898
+ return UINT2NUM(p1788_figure_rb2ref(self).stroke_color());
899
+ }
900
+
901
+
902
+ /* Set the fill color of the new objects (boxes) that will be drawned.
903
+ *
904
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
905
+ * for the red, green, blue and alpha channels.
906
+ *
907
+ * The color can also be set to a string, like `"#cc000080"`, `"#cc0000.5"`,`"r.5"` or `"W"`.
908
+ * @param c [Integer, String]
909
+ * @see fill_on
910
+ * @return [self]
911
+ */
912
+ static VALUE p1788_figure_set_fill_color(VALUE self, VALUE c)
913
+ {
914
+ unsigned int n = p1788_figure_parse_color(c);
915
+ p1788_figure_rb2ref(self).cmd_fill_color(n);
916
+ return self;
917
+ }
918
+
919
+
920
+ /* Returns the current color used to fill objects (boxes).
921
+ * The color is encoded into a 32-bit integer that can be split in hexadecimal as: 0xRRGGBBAA,
922
+ * for the red, green, blue and alpha channels.
923
+ * @see fill_color=
924
+ * @return [Integer]
925
+ */
926
+ static VALUE p1788_figure_get_fill_color(VALUE self)
927
+ {
928
+ return UINT2NUM(p1788_figure_rb2ref(self).fill_color());
929
+ }
930
+
931
+
932
+ static const char *format_regexp = "^((_|([^\\[\\]]+))?(\\[(_|([^\\[\\]]+))?\\])?)$";
933
+ // match[] 12 3 4 5 6
934
+
935
+
936
+ static void p1788_figure_parse_format(P1788Figure::Figure& f, VALUE str)
937
+ {
938
+ if (!RB_TYPE_P(str, T_STRING))
939
+ rb_raise(rb_eTypeError, "expecting a string as format, not a %" PRIsVALUE,
940
+ rb_class_name(rb_class_of(str)));
941
+ VALUE cr = rb_str_new_static(format_regexp, 41);
942
+ VALUE regexp = rb_class_new_instance(1, &cr, rb_cRegexp);
943
+ VALUE match = rb_funcallv(str, id_match, 1, &regexp);
944
+ if (NIL_P(match))
945
+ rb_raise(rb_eRuntimeError, "cannot parse \"%" PRIsVALUE "\" as a format", str);
946
+ VALUE m;
947
+
948
+ /* Stroke */
949
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(2));
950
+ if (NIL_P(m)) // no stroke
951
+ f.cmd_stroke(false);
952
+ else {
953
+ f.cmd_stroke(true);
954
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(3));
955
+ if (m != Qnil)
956
+ f.cmd_stroke_color(p1788_figure_parse_color(m));
957
+ }
958
+
959
+ /* Fill */
960
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(5));
961
+ if (NIL_P(m)) // no fill
962
+ f.cmd_fill(false);
963
+ else {
964
+ f.cmd_fill(true);
965
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(6));
966
+ if (m != Qnil)
967
+ f.cmd_fill_color(p1788_figure_parse_color(m));
968
+ }
969
+ }
970
+
971
+
972
+ /* Draw a single box.
973
+ * @return [self]
974
+ * @overload draw_box(box, format=nil)
975
+ * @param box [IntervalVector] an interval vector with at least two elements (for the _x_ and _y_ axis)
976
+ * @param format [String] an optional string in the form `"border_color[fill_color]"`
977
+ */
978
+ static VALUE p1788_figure_draw_box(int argc, VALUE *argv, VALUE self)
979
+ {
980
+ VALUE rbox, rformat;
981
+ rb_scan_args(argc, argv, "11", &rbox, &rformat);
982
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
983
+ if (rformat != Qnil)
984
+ p1788_figure_parse_format(f, rformat);
985
+ f.cmd_draw_box(p1788_vector_rb2ref(rbox));
986
+ return self;
987
+ }
988
+
989
+
990
+ /* Draw multiple boxes at once.
991
+ * @return [self]
992
+ * @overload draw_boxes(array_of_boxes, format=nil)
993
+ * @param array_of_boxes [Array<IntervalVector>] an array of interval vectors each with at least two elements (for the _x_ and _y_ axis)
994
+ * @param format [String] an optional string in the form `"border_color[fill_color]"`
995
+ */
996
+ static VALUE p1788_figure_draw_boxes(int argc, VALUE *argv, VALUE self)
997
+ {
998
+ VALUE rboxes, rformat;
999
+ rb_scan_args(argc, argv, "11", &rboxes, &rformat);
1000
+ P1788Figure::Figure& f = p1788_figure_rb2ref(self);
1001
+ if (!RB_TYPE_P(rboxes, T_ARRAY))
1002
+ rb_raise(rb_eTypeError, "expecting an array of %" PRIsVALUE ", not a %" PRIsVALUE,
1003
+ rb_class_name(c_IntervalVector), rb_class_name(rb_class_of(rboxes)));
1004
+ if (rformat != Qnil)
1005
+ p1788_figure_parse_format(f, rformat);
1006
+ const size_t l = rb_array_len(rboxes);
1007
+ if (l < 1) return self;
1008
+ std::vector<P1788Figure::Box> bv;
1009
+ bv.reserve(l);
1010
+ for (size_t i = 0; i < l; i++) {
1011
+ VALUE e = rb_ary_entry(rboxes, i);
1012
+ if (!is_an_interval_vector(e))
1013
+ rb_raise(rb_eTypeError, "expecting interval vectors in the array, not %" PRIsVALUE,
1014
+ rb_class_name(rb_class_of(e)));
1015
+ const IntervalVector& iv = p1788_vector_rb2ref(e);
1016
+ if (!(iv.size() < 2 || Interval::is_empty(iv[0]) || Interval::is_empty(iv[1])))
1017
+ bv.emplace_back(iv[0], iv[1]);
1018
+ }
1019
+ if (bv.size() > 0)
1020
+ f.cmd_draw_boxes(std::move(bv));
1021
+ return self;
1022
+ }
1023
+
1024
+
1025
+ /* Reset the figure to its default, clearing all boxes, labels, etc.
1026
+ * After a reset, the figure state is the same than after `Figure.new()`.
1027
+ * @return [self]
1028
+ */
1029
+ static VALUE p1788_figure_reset(VALUE self)
1030
+ {
1031
+ p1788_figure_rb2ref(self).reset();
1032
+ return self;
1033
+ }
1034
+
1035
+
1036
+ static const char *extension_regexp = "\\.((png|PNG)|(svg|SVG)|(pdf|PDF))$";
1037
+ // match[] 12 3 4
1038
+
1039
+
1040
+ /* Generate the figure and write the output image to file _file_name_.
1041
+ * The output format (either _png_, _svg_ or _pdf_) is deduced from the extension of the file.
1042
+ * @return [self]
1043
+ * @param file_name [String] name of the file to write the image to, ending with '.png', '.svg' or '.pdf'.
1044
+ */
1045
+ static VALUE p1788_figure_write(VALUE self, VALUE file_name)
1046
+ {
1047
+ file_name = rb_funcallv(file_name, id_to_s, 0, NULL);
1048
+ if (RSTRING_LEN(file_name) < 1)
1049
+ rb_raise(rb_eRuntimeError, "file name is empty");
1050
+ VALUE cr = rb_str_new_static(extension_regexp, 34);
1051
+ VALUE regexp = rb_class_new_instance(1, &cr, rb_cRegexp);
1052
+ VALUE match = rb_funcallv(file_name, id_match, 1, &regexp);
1053
+ if (NIL_P(match))
1054
+ rb_raise(rb_eRuntimeError, "cannot deduce format from file name \"%" PRIsVALUE "\"", file_name);
1055
+ VALUE m;
1056
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(2));
1057
+ if (m != Qnil) {
1058
+ #ifdef CAIRO_HAS_PNG_FUNCTIONS
1059
+ return p1788_figure_write_png(self, file_name);
1060
+ #else
1061
+ rb_raise(rb_eRuntimeError, "no support for PNG, sorry");
1062
+ #endif
1063
+ }
1064
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(3));
1065
+ if (m != Qnil) {
1066
+ #ifdef CAIRO_HAS_SVG_SURFACE
1067
+ return p1788_figure_write_svg(self, file_name);
1068
+ #else
1069
+ rb_raise(rb_eRuntimeError, "no support for SVG, sorry");
1070
+ #endif
1071
+ }
1072
+ m = rb_funcall(match, id_brackets, 1, INT2NUM(4));
1073
+ if (m != Qnil) {
1074
+ #ifdef CAIRO_HAS_PDF_SURFACE
1075
+ return p1788_figure_write_pdf(self, file_name);
1076
+ #else
1077
+ rb_raise(rb_eRuntimeError, "no support for PDF, sorry");
1078
+ #endif
1079
+ }
1080
+ return self;
1081
+ }
1082
+
1083
+
1084
+ /* Returns a new {Figure}
1085
+ * @return [Figure]
1086
+ * @overload new(width: nil, height: nil, xrange: nil, yrange: nil, with_decorations: true, title: nil, xlabel: nil, ylabel: nil, background_color: 't', decoration_color: 'k', decoration_font: 'Sans', decoration_font_size: 11, decoration_line_width: 1, label_font_size: nil, title_font_size: nil, title_in_bold: false)
1087
+ * @param width [Integer, nil] Width of the frame of the output image. See {width=}.
1088
+ * @param height [Integer, nil] Height of the frame of the output image. See {height=}.
1089
+ * @param xrange [Interval, nil] The horizontal range that will be displayed. See {xrange=}.
1090
+ * @param yrange [Interval, nil] The vertical range that will be displayed. See {yrange=}.
1091
+ * @param with_decorations [Boolean, nil] Draw frame, graduations, labels and title around the figure? See {with_decorations=}.
1092
+ * @param title [String, nil] The title of the figure. See {title=}.
1093
+ * @param xlabel [String, nil] The label for the horizontal axis. See {xlabel=}.
1094
+ * @param ylabel [String, nil] The label for the vertical axis. See {ylabel=}.
1095
+ * @param background_color [Integer, String, nil] Background color of the figure. Default is transparent. See {background_color=}.
1096
+ * @param decoration_color [Integer, String, nil] Color of the frame, labels and title. Defaults to black. See {decoration_color=}.
1097
+ * @param decoration_font [Integer, String, nil] Font for labels, title and graduations. Defaults to "Sans". See {decoration_font=}.
1098
+ * @param decoration_font_size [Float, nil] Font size for the graduations. Defaults to 11. See {decoration_font_size=}.
1099
+ * @param decoration_line_width [Float, nil] Line width of the frame of the figure. Defaults to 1. See {decoration_line_width=}.
1100
+ * @param label_font_size [Float, nil] Font size for the labels. Defaults to 1.1 times _decoration_font_size_. See {label_font_size=}.
1101
+ * @param title_font_size [Float, nil] Font size for the title. Defaults to 1.23 times _decoration_font_size_. See {title_font_size=}.
1102
+ * @param title_in_bold [Boolean] Draw the title in bold or not?. Defaults to _false_. See {title_in_bold=}.
1103
+ */
1104
+ static VALUE p1788_figure_initialize(int argc, VALUE *argv, VALUE self)
1105
+ {
1106
+ const ID kwkeys[] = {
1107
+ id_width,
1108
+ id_height,
1109
+ id_xrange,
1110
+ id_yrange,
1111
+ id_with_decorations,
1112
+ id_title,
1113
+ id_xlabel,
1114
+ id_ylabel,
1115
+ id_background_color,
1116
+ id_decoration_color,
1117
+ id_decoration_font,
1118
+ id_decoration_font_size,
1119
+ id_decoration_line_width,
1120
+ id_label_font_size,
1121
+ id_title_font_size,
1122
+ id_title_in_bold
1123
+ };
1124
+ VALUE kwvalues[] = {Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef, Qundef};
1125
+ VALUE kw;
1126
+
1127
+ rb_scan_args(argc, argv, ":", &kw);
1128
+ if (NIL_P(kw))
1129
+ return self;
1130
+
1131
+ rb_get_kwargs(kw, kwkeys, 0, 16, kwvalues);
1132
+
1133
+ if (kwvalues[0] != Qundef) p1788_figure_set_width(self, kwvalues[0]);
1134
+ if (kwvalues[1] != Qundef) p1788_figure_set_height(self, kwvalues[1]);
1135
+ if (kwvalues[2] != Qundef) p1788_figure_set_xrange(self, kwvalues[2]);
1136
+ if (kwvalues[3] != Qundef) p1788_figure_set_yrange(self, kwvalues[3]);
1137
+ if (kwvalues[4] != Qundef) p1788_figure_set_with_decorations(self, kwvalues[4]);
1138
+ if (kwvalues[5] != Qundef) p1788_figure_set_title(self, kwvalues[5]);
1139
+ if (kwvalues[6] != Qundef) p1788_figure_set_xlabel(self, kwvalues[6]);
1140
+ if (kwvalues[7] != Qundef) p1788_figure_set_ylabel(self, kwvalues[7]);
1141
+ if (kwvalues[8] != Qundef) p1788_figure_set_background_color(self, kwvalues[8]);
1142
+ if (kwvalues[9] != Qundef) p1788_figure_set_decoration_color(self, kwvalues[9]);
1143
+ if (kwvalues[10] != Qundef) p1788_figure_set_decoration_font(self, kwvalues[10]);
1144
+ if (kwvalues[11] != Qundef) p1788_figure_set_decoration_font_size(self, kwvalues[11]);
1145
+ if (kwvalues[12] != Qundef) p1788_figure_set_decoration_line_width(self, kwvalues[12]);
1146
+ if (kwvalues[13] != Qundef) p1788_figure_set_label_font_size(self, kwvalues[13]);
1147
+ if (kwvalues[14] != Qundef) p1788_figure_set_title_font_size(self, kwvalues[14]);
1148
+ if (kwvalues[15] != Qundef) p1788_figure_set_title_in_bold(self, kwvalues[15]);
1149
+
1150
+ return self;
1151
+ }
1152
+
1153
+
1154
+ void Init_p1788_figure()
1155
+ {
1156
+ id_width = rb_intern("width");
1157
+ id_height = rb_intern("height");
1158
+ id_xrange = rb_intern("xrange");
1159
+ id_yrange = rb_intern("yrange");
1160
+ id_with_decorations = rb_intern("with_decorations");
1161
+ id_title = rb_intern("title");
1162
+ id_xlabel = rb_intern("xlabel");
1163
+ id_ylabel = rb_intern("ylabel");
1164
+ id_background_color = rb_intern("background_color");
1165
+ id_decoration_color = rb_intern("decoration_color");
1166
+ id_decoration_font = rb_intern("decoration_font");
1167
+ id_decoration_font_size = rb_intern("decoration_font_size");
1168
+ id_decoration_line_width = rb_intern("decoration_line_width");
1169
+ id_label_font_size = rb_intern("label_font_size");
1170
+ id_title_font_size = rb_intern("title_font_size");
1171
+ id_title_in_bold = rb_intern("title_in_bold");
1172
+
1173
+ c_Figure = rb_define_class_under(m_P1788, "Figure", rb_cObject);
1174
+ rb_define_alloc_func(c_Figure, p1788_figure_alloc);
1175
+ rb_undef_method(rb_class_of(c_Figure), "allocate");
1176
+ rb_define_method(c_Figure, "initialize", p1788_figure_initialize, -1);
1177
+
1178
+ /* Attributes */
1179
+ rb_define_method(c_Figure, "with_decorations", p1788_figure_get_with_decorations, 0);
1180
+ rb_define_method(c_Figure, "with_decorations=", p1788_figure_set_with_decorations, 1);
1181
+ rb_define_method(c_Figure, "decoration_font", p1788_figure_get_decoration_font, 0);
1182
+ rb_define_method(c_Figure, "decoration_font=", p1788_figure_set_decoration_font, 1);
1183
+ rb_define_method(c_Figure, "decoration_font_size", p1788_figure_get_decoration_font_size, 0);
1184
+ rb_define_method(c_Figure, "decoration_font_size=", p1788_figure_set_decoration_font_size, 1);
1185
+ rb_define_method(c_Figure, "title_font_size", p1788_figure_get_title_font_size, 0);
1186
+ rb_define_method(c_Figure, "title_font_size=", p1788_figure_set_title_font_size, 1);
1187
+ rb_define_method(c_Figure, "label_font_size", p1788_figure_get_label_font_size, 0);
1188
+ rb_define_method(c_Figure, "label_font_size=", p1788_figure_set_label_font_size, 1);
1189
+ rb_define_method(c_Figure, "decoration_line_width", p1788_figure_get_decoration_line_width, 0);
1190
+ rb_define_method(c_Figure, "decoration_line_width=", p1788_figure_set_decoration_line_width, 1);
1191
+ rb_define_method(c_Figure, "decoration_color", p1788_figure_get_decoration_color, 0);
1192
+ rb_define_method(c_Figure, "decoration_color=", p1788_figure_set_decoration_color, 1);
1193
+ rb_define_method(c_Figure, "background_color", p1788_figure_get_background_color, 0);
1194
+ rb_define_method(c_Figure, "background_color=", p1788_figure_set_background_color, 1);
1195
+ rb_define_method(c_Figure, "title_in_bold", p1788_figure_get_title_in_bold, 0);
1196
+ rb_define_method(c_Figure, "title_in_bold=", p1788_figure_set_title_in_bold, 1);
1197
+ rb_define_method(c_Figure, "title", p1788_figure_get_title, 0);
1198
+ rb_define_method(c_Figure, "title=", p1788_figure_set_title, 1);
1199
+ rb_define_method(c_Figure, "xlabel", p1788_figure_get_xlabel, 0);
1200
+ rb_define_method(c_Figure, "xlabel=", p1788_figure_set_xlabel, 1);
1201
+ rb_define_method(c_Figure, "ylabel", p1788_figure_get_ylabel, 0);
1202
+ rb_define_method(c_Figure, "ylabel=", p1788_figure_set_ylabel, 1);
1203
+ rb_define_method(c_Figure, "width", p1788_figure_get_width, 0);
1204
+ rb_define_method(c_Figure, "width=", p1788_figure_set_width, 1);
1205
+ rb_define_method(c_Figure, "height", p1788_figure_get_height, 0);
1206
+ rb_define_method(c_Figure, "height=", p1788_figure_set_height, 1);
1207
+ rb_define_method(c_Figure, "xrange", p1788_figure_get_xrange, 0);
1208
+ rb_define_method(c_Figure, "xrange=", p1788_figure_set_xrange, 1);
1209
+ rb_define_method(c_Figure, "yrange", p1788_figure_get_yrange, 0);
1210
+ rb_define_method(c_Figure, "yrange=", p1788_figure_set_yrange, 1);
1211
+
1212
+ /* Statefull commands */
1213
+ rb_define_method(c_Figure, "stroke_on", p1788_figure_stroke_on, 0);
1214
+ rb_define_method(c_Figure, "stroke_off", p1788_figure_stroke_off, 0);
1215
+ rb_define_method(c_Figure, "stroke?", p1788_figure_get_stroke_onoff, 0);
1216
+ rb_define_method(c_Figure, "stroke_width=", p1788_figure_set_stroke_width, 1);
1217
+ rb_define_method(c_Figure, "stroke_width", p1788_figure_get_stroke_width, 0);
1218
+ rb_define_method(c_Figure, "stroke_color=", p1788_figure_set_stroke_color, 1);
1219
+ rb_define_method(c_Figure, "stroke_color", p1788_figure_get_stroke_color, 0);
1220
+ rb_define_method(c_Figure, "fill_on", p1788_figure_fill_on, 0);
1221
+ rb_define_method(c_Figure, "fill_off", p1788_figure_fill_off, 0);
1222
+ rb_define_method(c_Figure, "fill?", p1788_figure_get_fill_onoff, 0);
1223
+ rb_define_method(c_Figure, "fill_color=", p1788_figure_set_fill_color, 1);
1224
+ rb_define_method(c_Figure, "fill_color", p1788_figure_get_fill_color, 0);
1225
+ rb_define_method(c_Figure, "draw_box", p1788_figure_draw_box, -1);
1226
+ rb_define_method(c_Figure, "draw_boxes", p1788_figure_draw_boxes, -1);
1227
+ rb_define_method(c_Figure, "reset!", p1788_figure_reset, 0);
1228
+
1229
+ /* Figure generation and output */
1230
+ rb_define_method(c_Figure, "allocated_size", p1788_figure_allocated_size, 0);
1231
+ rb_define_method(c_Figure, "content_range", p1788_figure_content_range, 0);
1232
+ rb_define_method(c_Figure, "nice_range", p1788_figure_nice_range, 0);
1233
+ rb_define_method(c_Figure, "write", p1788_figure_write, 1);
1234
+ rb_define_method(c_Figure, "set_range", p1788_figure_set_range, 1);
1235
+ #ifdef CAIRO_HAS_PNG_FUNCTIONS
1236
+ rb_define_method(c_Figure, "write_png", p1788_figure_write_png, 1);
1237
+ rb_define_method(c_Figure, "to_png", p1788_figure_to_png, 0);
1238
+ #endif
1239
+ #ifdef CAIRO_HAS_SVG_SURFACE
1240
+ rb_define_method(c_Figure, "write_svg", p1788_figure_write_svg, 1);
1241
+ rb_define_method(c_Figure, "to_svg", p1788_figure_to_svg, 0);
1242
+ #endif
1243
+ #ifdef CAIRO_HAS_PDF_SURFACE
1244
+ rb_define_method(c_Figure, "write_pdf", p1788_figure_write_pdf, 1);
1245
+ rb_define_method(c_Figure, "to_pdf", p1788_figure_to_pdf, 0);
1246
+ #endif
1247
+ }
1248
+
1249
+