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,1284 @@
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
+
20
+ #include <cstdlib>
21
+ #include <cstdio>
22
+ #include <sstream>
23
+ #include <limits>
24
+ #include <math.h>
25
+ #include <cairo/cairo-svg.h>
26
+ #include <cairo/cairo-pdf.h>
27
+ #include <stdexcept>
28
+ #include "figure.hh"
29
+
30
+ /* TODO
31
+ * graduation 0, 0.5. 1
32
+ */
33
+
34
+ #define P1788FIGURECOMMAND_CHECK_TYPE(type, expected, valuetype) \
35
+ do { \
36
+ if (((int)type & expected) == 0 || ((int)type & ~expected) != 0) { \
37
+ fprintf(stderr, "badd Command(%d) for command constructor with valuetype value\n", (int)type); \
38
+ exit(-1); \
39
+ } \
40
+ } while (0) \
41
+
42
+ typedef std::string stringclass;
43
+ typedef std::vector<P1788Figure::Box> boxvectorclass;
44
+
45
+
46
+ #if 0
47
+ static const char* cmd_type_to_str(P1788Figure::Command::command_type_t t)
48
+ {
49
+ const char *r = "ERROR";
50
+ switch (t) {
51
+ case P1788Figure::Command::NONE:
52
+ r = "NONE"; break;
53
+ case P1788Figure::Command::STROKE:
54
+ r = "STROKE bool"; break;
55
+ case P1788Figure::Command::FILL:
56
+ r = "FILL bool"; break;
57
+ case P1788Figure::Command::STROKE_WIDTH:
58
+ r = "STROKE_WIDTH float"; break;
59
+ case P1788Figure::Command::STROKE_COLOR:
60
+ r = "STROKE_COLOR unsigned int"; break;
61
+ case P1788Figure::Command::FILL_COLOR:
62
+ r = "FILL_COLOR unsigned int"; break;
63
+ case P1788Figure::Command::DRAW_BOXES:
64
+ r = "DRAW_BOXES std::vector<Box>"; break;
65
+ default:
66
+ break;
67
+ }
68
+ return r;
69
+ }
70
+ #endif
71
+
72
+
73
+ P1788Figure::Command::Command(P1788Figure::Command::command_type_t type, bool v) :
74
+ m_type(type),
75
+ m_bool(v)
76
+ {
77
+ P1788FIGURECOMMAND_CHECK_TYPE(type, 0x03, bool);
78
+ }
79
+
80
+
81
+ P1788Figure::Command::Command(P1788Figure::Command::command_type_t type, float v) :
82
+ m_type(type),
83
+ m_float(v)
84
+ {
85
+ P1788FIGURECOMMAND_CHECK_TYPE(type, 0x04, float);
86
+ }
87
+
88
+
89
+ P1788Figure::Command::Command(P1788Figure::Command::command_type_t type, unsigned int c) :
90
+ m_type(type),
91
+ m_uint(c)
92
+ {
93
+ P1788FIGURECOMMAND_CHECK_TYPE(type, 0x18, unsigned int);
94
+ }
95
+
96
+
97
+ P1788Figure::Command::Command(P1788Figure::Command::command_type_t type, std::vector<Box>&& v) :
98
+ m_type(type),
99
+ m_boxes(std::move(v))
100
+ {
101
+ P1788FIGURECOMMAND_CHECK_TYPE(type, 0x20, std::vector<Box>);
102
+ }
103
+
104
+
105
+ P1788Figure::Command::Command(P1788Figure::Command::command_type_t type, const Box& b) :
106
+ m_type(type),
107
+ m_boxes()
108
+ {
109
+ P1788FIGURECOMMAND_CHECK_TYPE(type, 0x20, Box);
110
+ m_boxes.emplace_back(b);
111
+ }
112
+
113
+
114
+ P1788Figure::Command::Command(P1788Figure::Command&& c) :
115
+ m_type(c.m_type)
116
+ {
117
+ // printf("Command(Command&&) %s\n", cmd_type_to_str(m_type));
118
+ switch (m_type) {
119
+ case STROKE:
120
+ case FILL:
121
+ m_bool = c.m_bool;
122
+ break;
123
+ case STROKE_WIDTH:
124
+ m_float = c.m_float;
125
+ break;
126
+ case STROKE_COLOR:
127
+ case FILL_COLOR:
128
+ m_uint = c.m_uint;
129
+ break;
130
+ case DRAW_BOXES:
131
+ new (&m_boxes) std::vector<Box>(std::move(c.m_boxes));
132
+ break;
133
+ default:
134
+ break;
135
+ }
136
+ }
137
+
138
+
139
+ P1788Figure::Command::~Command()
140
+ {
141
+ // printf("~Command(%s)\n", cmd_type_to_str(m_type));
142
+ if (m_type == DRAW_BOXES)
143
+ m_boxes.~boxvectorclass();
144
+ }
145
+
146
+
147
+ void P1788Figure::Command::push_box(const Box& b)
148
+ {
149
+ if (m_type == DRAW_BOXES)
150
+ m_boxes.emplace_back(b);
151
+ }
152
+
153
+
154
+ static inline void P1788Figure_check_cairo_status(cairo_status_t s)
155
+ {
156
+ if (s == CAIRO_STATUS_SUCCESS)
157
+ return;
158
+ throw std::runtime_error(cairo_status_to_string(s));
159
+ }
160
+
161
+
162
+ static void P1788Figure_print_boxes(P1788Figure::State& s, const std::vector<P1788Figure::Box>& boxes)
163
+ {
164
+ const size_t l = boxes.size();
165
+ P1788Figure::Box b;
166
+ for (size_t i = 0; i < l; i++) {
167
+ s.real_box_to_screen_box(boxes[i], b);
168
+ // printf("[%g, %g], [%g, %g] => [%g, %g], [%g, %g]\n",
169
+ // Interval::inf(boxes[i].x), Interval::sup(boxes[i].x),
170
+ // Interval::inf(boxes[i].y), Interval::sup(boxes[i].y),
171
+ // Interval::inf(b.x), Interval::sup(b.x),
172
+ // Interval::inf(b.y), Interval::sup(b.y));
173
+ if (b.is_empty())
174
+ continue;
175
+ if (s.fill) {
176
+ cairo_rectangle(s.cr,
177
+ Interval::inf(b.x),
178
+ Interval::inf(b.y),
179
+ Interval::wid(b.x)+1,
180
+ Interval::wid(b.y)+1);
181
+ s.set_source_color(s.fill_color);
182
+ cairo_fill(s.cr);
183
+ }
184
+ if (s.stroke) {
185
+ s.set_source_color(s.stroke_color);
186
+ double wx = Interval::wid(b.x);
187
+ double wy = Interval::wid(b.y);
188
+ if (((int)round(s.stroke_width) & 1) == 1) {
189
+ if (wx < 1.0 || wy < 1.0) {
190
+ cairo_rectangle(s.cr, Interval::inf(b.x), Interval::inf(b.y), 1.0, 1.0);
191
+ cairo_fill(s.cr);
192
+ }
193
+ else {
194
+ cairo_rectangle(s.cr, Interval::inf(b.x)+0.5, Interval::inf(b.y)+0.5, wx, wy);
195
+ cairo_set_line_width(s.cr, s.stroke_width);
196
+ cairo_stroke(s.cr);
197
+ }
198
+ }
199
+ else {
200
+ cairo_rectangle(s.cr, Interval::inf(b.x), Interval::inf(b.y), wx+1.0, wy+1.0);
201
+ cairo_set_line_width(s.cr, s.stroke_width);
202
+ cairo_stroke(s.cr);
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+
209
+ void P1788Figure::Command::exec(State& s)
210
+ {
211
+ cairo_set_line_cap(s.cr, CAIRO_LINE_CAP_SQUARE);
212
+ cairo_set_line_join(s.cr, CAIRO_LINE_JOIN_MITER);
213
+ switch (m_type) {
214
+ case P1788Figure::Command::STROKE:
215
+ // printf("STROKE: %d\n", m_bool);
216
+ s.stroke = m_bool;
217
+ break;
218
+ case P1788Figure::Command::FILL:
219
+ // printf("FILL: %d\n", m_bool);
220
+ s.fill = m_bool;
221
+ break;
222
+ case P1788Figure::Command::STROKE_WIDTH:
223
+ // printf("STROKE_WIDTHT: %f\n", m_float);
224
+ s.stroke_width = m_float;
225
+ break;
226
+ case P1788Figure::Command::STROKE_COLOR:
227
+ // printf("STROKE_COLOR: 0x%08x\n", m_uint);
228
+ s.stroke_color = m_uint;
229
+ break;
230
+ case P1788Figure::Command::FILL_COLOR:
231
+ // printf("FILL_COLOR: 0x%08x\n", m_uint);
232
+ s.fill_color = m_uint;
233
+ break;
234
+ case P1788Figure::Command::DRAW_BOXES:
235
+ // printf("DRAW_BOXES (%lu)\n", m_boxes.size());
236
+ P1788Figure_print_boxes(s, m_boxes);
237
+ break;
238
+ default:
239
+ // printf("ERROR BAD COMMAND TYPE\n");
240
+ break;
241
+ }
242
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
243
+ }
244
+
245
+
246
+ size_t P1788Figure::Command::allocated_size() const
247
+ {
248
+ size_t r = sizeof(P1788Figure::Command);
249
+ if (m_type == P1788Figure::Command::DRAW_BOXES)
250
+ r += m_boxes.capacity()*sizeof(Box);
251
+ return r;
252
+ }
253
+
254
+
255
+ static cairo_status_t P1788Figure_write_func(void *closure, const unsigned char *data, unsigned int length)
256
+ {
257
+ std::string& str = *(std::string*)closure;
258
+ str.append((const char*)data, length);
259
+ return CAIRO_STATUS_SUCCESS;
260
+ }
261
+
262
+
263
+ #ifdef CAIRO_HAS_IMAGE_SURFACE
264
+ void P1788Figure::Figure::write_png(const std::string& file_name)
265
+ {
266
+ State s(this);
267
+ cairo_surface_t *imgsurf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, s.figwidth, s.figheight);
268
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
269
+ s.cr = cairo_create(imgsurf);
270
+ exec_commands(s);
271
+ s.decorate();
272
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
273
+ P1788Figure_check_cairo_status(cairo_surface_write_to_png(imgsurf, file_name.c_str()));
274
+ cairo_surface_destroy(imgsurf);
275
+ }
276
+
277
+ std::string P1788Figure::Figure::to_png()
278
+ {
279
+ std::string str;
280
+ State s(this);
281
+ cairo_surface_t *imgsurf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, s.figwidth, s.figheight);
282
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
283
+ s.cr = cairo_create(imgsurf);
284
+ exec_commands(s);
285
+ s.decorate();
286
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
287
+ P1788Figure_check_cairo_status(cairo_surface_write_to_png_stream(imgsurf, P1788Figure_write_func, &str));
288
+ cairo_surface_destroy(imgsurf);
289
+ return str;
290
+ }
291
+ #endif
292
+
293
+
294
+ #ifdef CAIRO_HAS_SVG_SURFACE
295
+ void P1788Figure::Figure::write_svg(const std::string& file_name)
296
+ {
297
+ State s(this);
298
+ cairo_surface_t *imgsurf = cairo_svg_surface_create(file_name.c_str(), s.figwidth, s.figheight);
299
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
300
+ cairo_svg_surface_set_document_unit(imgsurf, CAIRO_SVG_UNIT_PX);
301
+ s.cr = cairo_create(imgsurf);
302
+ exec_commands(s);
303
+ s.decorate();
304
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
305
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
306
+ cairo_surface_destroy(imgsurf);
307
+ }
308
+
309
+ std::string P1788Figure::Figure::to_svg()
310
+ {
311
+ std::string str;
312
+ State s(this);
313
+ cairo_surface_t *imgsurf = cairo_svg_surface_create_for_stream(
314
+ P1788Figure_write_func, &str, s.figwidth, s.figheight);
315
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
316
+ cairo_svg_surface_set_document_unit(imgsurf, CAIRO_SVG_UNIT_PX);
317
+ s.cr = cairo_create(imgsurf);
318
+ exec_commands(s);
319
+ s.decorate();
320
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
321
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
322
+ cairo_surface_destroy(imgsurf);
323
+ return str;
324
+ }
325
+ #endif
326
+
327
+
328
+ #ifdef CAIRO_HAS_PDF_SURFACE
329
+ void P1788Figure::Figure::write_pdf(const std::string& file_name)
330
+ {
331
+ State s(this);
332
+ cairo_surface_t *imgsurf = cairo_pdf_surface_create(file_name.c_str(), s.figwidth, s.figheight);
333
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
334
+ if (!m_title.empty())
335
+ cairo_pdf_surface_set_metadata(imgsurf, CAIRO_PDF_METADATA_TITLE, m_title.c_str());
336
+ cairo_pdf_surface_set_metadata(imgsurf, CAIRO_PDF_METADATA_CREATOR, "Created with P1788 (https://gitlab.ensta-bretagne.fr/bollenth/p1788)");
337
+ s.cr = cairo_create(imgsurf);
338
+ exec_commands(s);
339
+ s.decorate();
340
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
341
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
342
+ cairo_surface_destroy(imgsurf);
343
+ }
344
+
345
+ std::string P1788Figure::Figure::to_pdf()
346
+ {
347
+ std::string str;
348
+ State s(this);
349
+ cairo_surface_t *imgsurf = cairo_pdf_surface_create_for_stream(
350
+ P1788Figure_write_func, &str, s.figwidth, s.figheight);
351
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
352
+ if (!m_title.empty())
353
+ cairo_pdf_surface_set_metadata(imgsurf, CAIRO_PDF_METADATA_TITLE, m_title.c_str());
354
+ cairo_pdf_surface_set_metadata(imgsurf, CAIRO_PDF_METADATA_CREATOR, "Created with P1788 (https://gitlab.ensta-bretagne.fr/bollenth/p1788)");
355
+ s.cr = cairo_create(imgsurf);
356
+ exec_commands(s);
357
+ s.decorate();
358
+ P1788Figure_check_cairo_status(cairo_status(s.cr));
359
+ P1788Figure_check_cairo_status(cairo_surface_status(imgsurf));
360
+ cairo_surface_destroy(imgsurf);
361
+ return str;
362
+ }
363
+ #endif
364
+
365
+
366
+ P1788Figure::Figure::Figure() :
367
+ m_width(0),
368
+ m_height(0),
369
+ m_xlim(),
370
+ m_ylim(),
371
+ m_xlabel(),
372
+ m_ylabel(),
373
+ m_title(),
374
+ m_bold_title(false),
375
+ m_do_print_graduation(true),
376
+ m_graduation_font("Sans"),
377
+ m_graduation_font_size(11.0f),
378
+ m_graduation_color(0x000000ffU),
379
+ m_graduation_stroke_width(1.0f),
380
+ m_label_font_size(0.0f),
381
+ m_title_font_size(0.0f),
382
+ m_do_stroke(true),
383
+ m_stroke_color(0x000000ffU),
384
+ m_stroke_width(1.0f),
385
+ m_do_fill(true),
386
+ m_fill_color(0x808080ffU),
387
+ // m_background_color(0xffffffffU),
388
+ m_background_color(0xffffff00U),
389
+ m_commands()
390
+ {
391
+ m_commands.reserve(10);
392
+ }
393
+
394
+
395
+ void P1788Figure::Figure::reset()
396
+ {
397
+ m_width = 0;
398
+ m_height = 0;
399
+ m_xlim = Interval();
400
+ m_ylim = Interval();
401
+ m_xlabel.clear();
402
+ m_ylabel.clear();
403
+ m_title.clear();
404
+ m_bold_title = false;
405
+ m_do_print_graduation = true;
406
+ m_graduation_font = "Sans";
407
+ m_graduation_color = 0x000000ffU;
408
+ m_graduation_stroke_width = 1.0f;
409
+ m_graduation_font_size = 11.0f;
410
+ m_label_font_size = 0.0f;
411
+ m_title_font_size = 0.0f;
412
+ m_do_stroke = true;
413
+ m_stroke_color = 0x000000ffU;
414
+ m_stroke_width = 1.0f;
415
+ m_do_fill = true;
416
+ m_fill_color = 0x808080ffU;
417
+ m_background_color = 0xffffff00U;
418
+ m_commands.clear();
419
+ }
420
+
421
+
422
+ // P1788Figure::Figure::~Figure()
423
+ // {
424
+ // const size_t l = m_commands.size();
425
+ // for (size_t i = 0; i < l; i++)
426
+ // delete m_commands[i];
427
+ //
428
+ // }
429
+
430
+
431
+ void P1788Figure::Figure::exec_commands(State& s)
432
+ {
433
+ if ((m_background_color & 0xff) != 0x00) {
434
+ s.set_source_color(m_background_color);
435
+ cairo_paint(s.cr);
436
+ }
437
+ const size_t l = m_commands.size();
438
+ for (size_t i = 0; i < l; i++)
439
+ m_commands[i].exec(s);
440
+ }
441
+
442
+
443
+ void P1788Figure::Figure::cmd_stroke(bool b)
444
+ {
445
+ if (b != m_do_stroke) {
446
+ m_do_stroke = b;
447
+ m_commands.emplace_back(Command::STROKE, b);
448
+ // if (b) {
449
+ // if (!m_stroke_color.empty())
450
+ // cmd_stroke_color(m_stroke_color);
451
+ // if (m_stroke_width > 0.0f)
452
+ // cmd_stroke_width(m_width);
453
+ // }
454
+ }
455
+ }
456
+
457
+
458
+ void P1788Figure::Figure::cmd_stroke_color(unsigned int c)
459
+ {
460
+ if (c != m_stroke_color) {
461
+ m_stroke_color = c;
462
+ m_commands.emplace_back(Command::STROKE_COLOR, c);
463
+ }
464
+ }
465
+
466
+
467
+ void P1788Figure::Figure::cmd_stroke_width(float sw)
468
+ {
469
+ if (sw <= 0.0f) return;
470
+ if (sw != m_stroke_width) {
471
+ m_stroke_width = sw;
472
+ m_commands.emplace_back(Command::STROKE_WIDTH, sw);
473
+ }
474
+ }
475
+
476
+
477
+ void P1788Figure::Figure::cmd_fill(bool b)
478
+ {
479
+ if (b != m_do_fill) {
480
+ m_do_fill = b;
481
+ m_commands.emplace_back(Command::FILL, b);
482
+ }
483
+ }
484
+
485
+
486
+ void P1788Figure::Figure::cmd_fill_color(unsigned int c)
487
+ {
488
+ if (c != m_fill_color) {
489
+ m_fill_color = c;
490
+ m_commands.emplace_back(Command::FILL_COLOR, c);
491
+ }
492
+ }
493
+
494
+
495
+ void P1788Figure::Figure::cmd_draw_box(const IntervalVector& iv)
496
+ {
497
+ if (iv.size() < 2 || Interval::is_empty(iv[0]) || Interval::is_empty(iv[1])) return;
498
+ Box b(iv[0], iv[1]);
499
+ if (m_commands.size() > 0 && m_commands.back().is_a_draw_box_command())
500
+ m_commands.back().push_box(b);
501
+ else
502
+ m_commands.emplace_back(Command::DRAW_BOXES, b);
503
+ }
504
+
505
+
506
+ void P1788Figure::Figure::cmd_draw_boxes(const std::vector<IntervalVector*>& bv)
507
+ {
508
+ const size_t vsize = bv.size();
509
+ if (vsize < 1) return;
510
+ size_t i;
511
+ std::vector<Box> v;
512
+ v.reserve(vsize);
513
+ for (i = 0; i < vsize; i++) {
514
+ const IntervalVector& e = *bv[i];
515
+ if (e.size() < 2 || Interval::is_empty(e[0]) || Interval::is_empty(e[1])) continue;
516
+ v.emplace_back(e[0], e[1]);
517
+ }
518
+ m_commands.emplace_back(Command::DRAW_BOXES, std::move(v));
519
+ }
520
+
521
+
522
+ void P1788Figure::Figure::cmd_draw_boxes(std::vector<Box>&& bv)
523
+ {
524
+ if (bv.size() < 1) return;
525
+ m_commands.emplace_back(Command::DRAW_BOXES, std::move(bv));
526
+ }
527
+
528
+
529
+ void P1788Figure::Figure::set_title(const std::string& s)
530
+ {
531
+ m_title = s;
532
+ }
533
+
534
+
535
+ void P1788Figure::Figure::set_background_color(unsigned int c)
536
+ {
537
+ m_background_color = c;
538
+ }
539
+
540
+
541
+ void P1788Figure::Figure::set_title_bold(bool b)
542
+ {
543
+ m_bold_title = b;
544
+ }
545
+
546
+
547
+ void P1788Figure::Figure::set_width(int width)
548
+ {
549
+ if (width <= 0)
550
+ m_width = 0;
551
+ else
552
+ m_width = std::max(std::min((unsigned int)width, 3840U), 10U);
553
+ }
554
+
555
+
556
+ void P1788Figure::Figure::set_height(int height)
557
+ {
558
+ if (height <= 0)
559
+ m_height = 0;
560
+ else
561
+ m_height = std::max(std::min((unsigned int)height, 3840U), 10U);
562
+ }
563
+
564
+
565
+ void P1788Figure::Figure::set_xlabel(const std::string& l)
566
+ {
567
+ m_xlabel = l;
568
+ }
569
+
570
+
571
+ void P1788Figure::Figure::set_ylabel(const std::string& l)
572
+ {
573
+ m_ylabel = l;
574
+ }
575
+
576
+
577
+ void P1788Figure::Figure::set_xlimit(const Interval& l)
578
+ {
579
+ if (Interval::is_common_interval(l))
580
+ m_xlim = l;
581
+ else
582
+ m_xlim = Interval();
583
+ }
584
+
585
+
586
+ void P1788Figure::Figure::set_ylimit(const Interval& l)
587
+ {
588
+ if (Interval::is_common_interval(l))
589
+ m_ylim = l;
590
+ else
591
+ m_ylim = Interval();
592
+ }
593
+
594
+
595
+ void P1788Figure::Figure::set_print_graduations(bool b)
596
+ {
597
+ m_do_print_graduation = b;
598
+ }
599
+
600
+
601
+ void P1788Figure::Figure::set_graduation_font(const std::string& s)
602
+ {
603
+ if (s.empty())
604
+ m_graduation_font = std::string("Sans");
605
+ else
606
+ m_graduation_font = s;
607
+ }
608
+
609
+
610
+ void P1788Figure::Figure::set_graduation_font_size(float s)
611
+ {
612
+ if (s > 0.0)
613
+ m_graduation_font_size = s;
614
+ else
615
+ m_graduation_font_size = 11.0f;
616
+ }
617
+
618
+
619
+ void P1788Figure::Figure::set_label_font_size(float s)
620
+ {
621
+ m_label_font_size = std::max(0.0f, std::min(128.0f, s));
622
+ }
623
+
624
+
625
+
626
+ void P1788Figure::Figure::set_title_font_size(float s)
627
+ {
628
+ m_title_font_size = std::max(0.0f, std::min(128.0f, s));
629
+ }
630
+
631
+
632
+ double P1788Figure::Figure::label_font_size() const
633
+ {
634
+ if (m_label_font_size <= 0.0f)
635
+ return 1.0909090909090908 * m_graduation_font_size;
636
+ return m_label_font_size;
637
+ }
638
+
639
+
640
+ double P1788Figure::Figure::title_font_size() const
641
+ {
642
+ if (m_title_font_size <= 0.0f)
643
+ return 1.2272727272727273 * m_graduation_font_size;
644
+ return m_title_font_size;
645
+ }
646
+
647
+
648
+ void P1788Figure::Figure::set_graduation_color(unsigned int c)
649
+ {
650
+ m_graduation_color = c;
651
+ }
652
+
653
+
654
+ void P1788Figure::Figure::set_graduation_stroke_width(float s)
655
+ {
656
+ if (s > 0.0)
657
+ m_graduation_stroke_width = s;
658
+ }
659
+
660
+
661
+ size_t P1788Figure::Figure::allocated_size() const
662
+ {
663
+ size_t r = sizeof(Figure);
664
+ r += m_xlabel.capacity();
665
+ r += m_ylabel.capacity();
666
+ r += m_title.capacity();
667
+ r += m_graduation_font.capacity();
668
+ for (auto c = m_commands.cbegin(); c < m_commands.cend(); ++c)
669
+ r += c->allocated_size();
670
+ return r;
671
+ }
672
+
673
+
674
+ void P1788Figure::Figure::find_unbounded_limits(Box& limits) const
675
+ {
676
+ double x_min = std::numeric_limits<double>::quiet_NaN();
677
+ double x_max = std::numeric_limits<double>::quiet_NaN();
678
+ double y_min = std::numeric_limits<double>::quiet_NaN();
679
+ double y_max = std::numeric_limits<double>::quiet_NaN();
680
+
681
+ for (auto cmd = m_commands.cbegin(); cmd < m_commands.cend(); ++cmd) {
682
+ if (!cmd->is_a_draw_box_command())
683
+ continue;
684
+ const std::vector<Box>& boxes = cmd->boxes();
685
+ for (auto b = boxes.cbegin(); b < boxes.cend(); ++b) {
686
+ if (Interval::is_empty(b->x) || Interval::is_empty(b->y))
687
+ continue;
688
+ x_min = fmin(x_min, Interval::inf(b->x));
689
+ x_max = fmax(x_max, Interval::sup(b->x));
690
+ y_min = fmin(y_min, Interval::inf(b->y));
691
+ y_max = fmax(y_max, Interval::sup(b->y));
692
+ }
693
+ }
694
+
695
+ limits.x = Interval(x_min, x_max);
696
+ limits.y = Interval(y_min, y_max);
697
+ }
698
+
699
+
700
+ void P1788Figure::Figure::find_limits(Box& limits, Box& nice_limits) const
701
+ {
702
+ double x_finitemin = std::numeric_limits<double>::quiet_NaN();
703
+ double x_finitemax = std::numeric_limits<double>::quiet_NaN();
704
+ double y_finitemin = std::numeric_limits<double>::quiet_NaN();
705
+ double y_finitemax = std::numeric_limits<double>::quiet_NaN();
706
+
707
+ for (auto cmd = m_commands.cbegin(); cmd < m_commands.cend(); ++cmd) {
708
+ if (!cmd->is_a_draw_box_command())
709
+ continue;
710
+ const std::vector<Box>& boxes = cmd->boxes();
711
+ for (auto b = boxes.cbegin(); b < boxes.cend(); ++b) {
712
+ if (Interval::is_empty(b->x) || Interval::is_empty(b->y))
713
+ continue;
714
+ if (std::isfinite(Interval::inf(b->x)))
715
+ x_finitemin = fmin(x_finitemin, Interval::inf(b->x));
716
+ if (std::isfinite(Interval::sup(b->x)))
717
+ x_finitemax = fmax(x_finitemax, Interval::sup(b->x));
718
+ if (std::isfinite(Interval::inf(b->y)))
719
+ y_finitemin = fmin(y_finitemin, Interval::inf(b->y));
720
+ if (std::isfinite(Interval::sup(b->y)))
721
+ y_finitemax = fmax(y_finitemax, Interval::sup(b->y));
722
+ }
723
+ }
724
+
725
+ if (std::isfinite(x_finitemin) && std::isfinite(x_finitemax))
726
+ limits.x = Interval(x_finitemin, x_finitemax);
727
+ else if (std::isfinite(x_finitemin))
728
+ limits.x = Interval(x_finitemin, x_finitemin + 1.0);
729
+ else if (std::isfinite(x_finitemax))
730
+ limits.x = Interval(x_finitemax - 1.0, x_finitemax);
731
+ else
732
+ limits.x = Interval(-1.0, 1.0);
733
+
734
+ if (std::isfinite(y_finitemin) && std::isfinite(y_finitemax))
735
+ limits.y = Interval(y_finitemin, y_finitemax);
736
+ else if (std::isfinite(y_finitemin))
737
+ limits.y = Interval(y_finitemin, y_finitemin + 1.0);
738
+ else if (std::isfinite(y_finitemax))
739
+ limits.y = Interval(y_finitemax - 1.0, y_finitemax);
740
+ else
741
+ limits.y = Interval(-1.0, 1.0);
742
+
743
+ TickAxis tk(limits.x, 10);
744
+ nice_limits.x = Interval(tk.nice_min(), tk.nice_max());
745
+ tk.set_min_max(limits.y);
746
+ nice_limits.y = Interval(tk.nice_min(), tk.nice_max());
747
+ }
748
+
749
+
750
+ P1788Figure::State::State(Figure* figure) :
751
+ cr(NULL),
752
+ figure(figure),
753
+ width(0), height(0),
754
+ m_Sx(1.0), m_Sy(1.0), m_Tx(0.0), m_Ty(0.0),
755
+ stroke(true),
756
+ stroke_width(1.0f),
757
+ stroke_color(0x000000ffU),
758
+ fill(true),
759
+ fill_color(0x808080ffU),
760
+ frame_box(),
761
+ figwidth(0), figheight(0),
762
+ top_offset(0), bot_offset(0), left_offset(0), right_offset(0),
763
+ xtickaxis(NULL), ytickaxis(NULL),
764
+ x_grad(), y_grad(),
765
+ xlabel(figure->xlabel()),
766
+ ylabel(figure->ylabel()),
767
+ title(figure->title()),
768
+ max_x_grad_height(0.0f), max_y_grad_width(0.0f), char_height(0.0f), char_width(0.0f)
769
+ {
770
+ Box limits, nice_limits;
771
+ bool x_is_bad = !Interval::is_common_interval(figure->xlim()) || Interval::is_singleton(figure->xlim());
772
+ bool y_is_bad = !Interval::is_common_interval(figure->ylim()) || Interval::is_singleton(figure->ylim());
773
+ if (x_is_bad || y_is_bad) {
774
+ figure->find_limits(limits, nice_limits);
775
+ if (x_is_bad)
776
+ limits.x = nice_limits.x;
777
+ else
778
+ limits.x = figure->xlim();
779
+ if (y_is_bad)
780
+ limits.y = nice_limits.y;
781
+ else
782
+ limits.y = figure->ylim();
783
+ }
784
+ else {
785
+ limits.x = figure->xlim();
786
+ limits.y = figure->ylim();
787
+ }
788
+
789
+ double aratio = Interval::wid(limits.x) / Interval::wid(limits.y);
790
+ // printf("==================\nxl: %g\nxr: %g\nyt: %g, yb: %g\nwidth: %d\nheight: %d\nratio: %g\n================\n",
791
+ // Interval::inf(limits.x), Interval::sup(limits.x), Interval::inf(limits.y), Interval::sup(limits.y),
792
+ // figure->width(), figure->height(), aratio);
793
+ if (figure->width() < 1 && figure->height() < 1) {
794
+ width = 512;
795
+ height = (int)round((double)width / aratio) & ~1;
796
+ }
797
+ else if (figure->width() < 1) {
798
+ height = figure->height();
799
+ width = (int)round(aratio*(double)height) & ~1;
800
+ }
801
+ else if (figure->height() < 1) {
802
+ width = figure->width();
803
+ height = (int)round((double)width / aratio) & ~1;
804
+ }
805
+ else {
806
+ width = figure->width();
807
+ height = figure->height();
808
+ }
809
+ width = std::max(std::min((unsigned int)width, 3840U), 10U);
810
+ height = std::max(std::min((unsigned int)height, 3840U), 10U);
811
+ // printf("width: %u\nheight: %u\n", width, height);
812
+
813
+ if (figure->print_graduations()) {
814
+ xtickaxis = new TickAxis(limits.x, (int)round((double)width / 70.0));
815
+ ytickaxis = new TickAxis(limits.y, (int)round((double)height / 70.0));
816
+ calculate_spacings();
817
+ frame_box = Box(Interval((double)left_offset, (double)(left_offset+width-1)), Interval((double)top_offset, (double)(top_offset+height-1)));
818
+ figwidth = left_offset + width + right_offset;
819
+ figheight = top_offset + height + bot_offset;
820
+ }
821
+ else {
822
+ frame_box = Box(Interval(0.0, (double)(width-1)), Interval(0.0, (double)(height-1)));
823
+ figwidth = width;
824
+ figheight = height;
825
+ }
826
+ double rl = Interval::inf(limits.x);
827
+ double rr = Interval::sup(limits.x);
828
+ double rb = Interval::inf(limits.y);
829
+ double rt = Interval::sup(limits.y);
830
+ double sl = Interval::inf(frame_box.x);
831
+ double sr = Interval::sup(frame_box.x);
832
+ double sb = Interval::sup(frame_box.y);
833
+ double st = Interval::inf(frame_box.y);
834
+ m_Sx = (sl-sr)/(rl-rr);
835
+ m_Sy = (sb-st)/(rb-rt);
836
+ m_Tx = (rl*sr-rr*sl)/(rl-rr);
837
+ m_Ty = (rb*st-rt*sb)/(rb-rt);
838
+
839
+ stroke = true;
840
+ stroke_width = 1.0f;
841
+ stroke_color = 0x000000ffU;
842
+ fill = true;
843
+ fill_color = 0x808080ffU;
844
+ }
845
+
846
+
847
+ P1788Figure::State::~State()
848
+ {
849
+ if (cr)
850
+ cairo_destroy(cr);
851
+ if (xtickaxis)
852
+ delete xtickaxis;
853
+ if (ytickaxis)
854
+ delete ytickaxis;
855
+ }
856
+
857
+
858
+ void P1788Figure::State::set_font_options(cairo_t *ctx)
859
+ {
860
+ cairo_select_font_face(ctx, figure->graduation_font().c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
861
+ cairo_set_font_size(ctx, figure->graduation_font_size());
862
+ if ((figure->graduation_color() & 0xff) != 0xff) {
863
+ #if 1
864
+ cairo_font_options_t *font_options = cairo_font_options_create();
865
+ cairo_font_options_set_antialias(font_options, CAIRO_ANTIALIAS_SUBPIXEL); // DEFAULT GRAY SUBPIXEL
866
+ cairo_font_options_set_hint_style(font_options, CAIRO_HINT_STYLE_MEDIUM); // SLIGHT MEDIUM FULL
867
+ cairo_font_options_set_hint_metrics(font_options, CAIRO_HINT_METRICS_ON); // ON OFF
868
+ cairo_set_font_options(ctx, font_options);
869
+ cairo_font_options_destroy(font_options);
870
+ #else
871
+ cairo_font_options_t *font_options = cairo_font_options_create();
872
+ cairo_font_options_set_antialias(font_options, CAIRO_ANTIALIAS_GRAY);//DEFAULT SUBPIXEL);
873
+ //cairo_font_options_set_subpixel_order(font_options, CAIRO_SUBPIXEL_ORDER_RGB); //DEFAULT);
874
+ cairo_font_options_set_hint_style(font_options, CAIRO_HINT_STYLE_MEDIUM);//SLIGHT MEDIUM FULL);
875
+ cairo_font_options_set_hint_metrics(font_options, CAIRO_HINT_METRICS_ON); //ON OFF);
876
+ cairo_set_font_options(ctx, font_options);
877
+ cairo_font_options_destroy(font_options);
878
+ #endif
879
+ }
880
+ }
881
+
882
+
883
+ void P1788Figure::State::calculate_spacings()
884
+ {
885
+ if (!figure->print_graduations()) return;
886
+
887
+ /* Create a temporary surface and context to get font extents */
888
+ cairo_surface_t *tmpsurf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
889
+ cairo_t * tmpcr = cairo_create(tmpsurf);
890
+ cairo_surface_destroy(tmpsurf);
891
+
892
+ set_font_options(tmpcr);
893
+
894
+ double max_x_grad_width = 0.0;
895
+ max_x_grad_height = 0.0f;
896
+ max_y_grad_width = 0.0f;
897
+ cairo_text_extents_t te;
898
+
899
+ cairo_text_extents(tmpcr, "bdfghjklpqtbHM", &te);
900
+ char_height = ceil(te.height);
901
+ cairo_text_extents(tmpcr, "6", &te);
902
+ char_width = ceil((te.width + char_height)/2.0);
903
+
904
+ x_grad = xtickaxis->get_coordinate_strings();
905
+ for (auto s = x_grad.begin(); s < x_grad.end(); ++s) {
906
+ cairo_text_extents(tmpcr, s->str.c_str(), &te);
907
+ s->width = te.width;
908
+ s->height = te.height;
909
+ s->x_bearing = te.x_bearing;
910
+ s->y_bearing = te.y_bearing;
911
+ max_x_grad_height = std::max(max_x_grad_height, (float)te.height);
912
+ max_x_grad_width = std::max(max_x_grad_width, te.width);
913
+ }
914
+
915
+ y_grad = ytickaxis->get_coordinate_strings();
916
+ for (auto s = y_grad.begin(); s < y_grad.end(); ++s) {
917
+ cairo_text_extents(tmpcr, s->str.c_str(), &te);
918
+ s->width = te.width;
919
+ s->height = te.height;
920
+ s->x_bearing = te.x_bearing;
921
+ s->y_bearing = te.y_bearing;
922
+ // printf("- \"%s\" -> %f\n", s->str.c_str(), s->width);
923
+ max_y_grad_width = std::max(max_y_grad_width, (float)te.width);
924
+ }
925
+
926
+ if (!xlabel.empty()) {
927
+ cairo_set_font_size(tmpcr, figure->label_font_size());
928
+ cairo_text_extents(tmpcr, xlabel.str.c_str(), &te);
929
+ xlabel.width = te.width;
930
+ xlabel.height = te.height;
931
+ xlabel.x_bearing = te.x_bearing;
932
+ xlabel.y_bearing = te.y_bearing;
933
+ }
934
+
935
+ if (!ylabel.empty()) {
936
+ cairo_set_font_size(tmpcr, figure->label_font_size());
937
+ cairo_text_extents(tmpcr, ylabel.str.c_str(), &te);
938
+ ylabel.width = te.width;
939
+ ylabel.height = te.height;
940
+ ylabel.x_bearing = te.x_bearing;
941
+ ylabel.y_bearing = te.y_bearing;
942
+ }
943
+
944
+ if (!title.empty()) {
945
+ cairo_set_font_size(tmpcr, figure->title_font_size());
946
+ if (figure->title_bold())
947
+ cairo_select_font_face(tmpcr, figure->graduation_font().c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
948
+ cairo_text_extents(tmpcr, title.str.c_str(), &te);
949
+ title.width = te.width;
950
+ title.height = te.height;
951
+ title.x_bearing = te.x_bearing;
952
+ title.y_bearing = te.y_bearing;
953
+ }
954
+
955
+ double bot_offset_d;
956
+ if (xlabel.empty())
957
+ bot_offset_d = char_height + max_x_grad_height + char_height;
958
+ else
959
+ bot_offset_d = char_height + max_x_grad_height + 3.0*xlabel.height;
960
+
961
+ double left_offset_d;
962
+ if (ylabel.empty())
963
+ left_offset_d = char_width + max_y_grad_width + char_height;
964
+ else
965
+ left_offset_d = char_width + max_y_grad_width + 3.0*ylabel.height;
966
+
967
+ double top_offset_d;
968
+ if (title.empty())
969
+ top_offset_d = char_height;
970
+ else
971
+ top_offset_d = 3.0*title.height;
972
+
973
+ double right_offset_d = std::max((double)char_height, char_width + max_x_grad_width/2.0);
974
+
975
+ bot_offset = ceilf(bot_offset_d);
976
+ top_offset = ceilf(top_offset_d);
977
+ right_offset = ceilf(right_offset_d);
978
+ left_offset = ceilf(left_offset_d);
979
+
980
+ // printf("---------------------------\n");
981
+ // printf("char_height: %f\n", char_height);
982
+ // printf("char_width: %f\n", char_width);
983
+ // printf("mx_x_height: %f\n", max_x_grad_height);
984
+ // printf("mx_y_width: %f\n", max_y_grad_width);
985
+ // printf("---------------------------\n");
986
+
987
+ cairo_destroy(tmpcr);
988
+ }
989
+
990
+
991
+ void P1788Figure::State::real_box_to_screen_box(const Box& rb, Box& sb) const
992
+ {
993
+ Interval sx(m_Sx, m_Sx), tx(m_Tx, m_Tx), sy(m_Sy, m_Sy), ty(m_Ty, m_Ty);
994
+ sb.x = Interval::intersection(rb.x * sx + tx, frame_box.x);
995
+ sb.y = Interval::intersection(rb.y * sy + ty, frame_box.y);
996
+ sb.x = Interval(round(Interval::inf(sb.x)), round(Interval::sup(sb.x)));
997
+ sb.y = Interval(round(Interval::inf(sb.y)), round(Interval::sup(sb.y)));
998
+ }
999
+
1000
+
1001
+ void P1788Figure::State::set_source_color(unsigned int c)
1002
+ {
1003
+ if (!cr) return;
1004
+ double r = (double)((c >> 24) & 0xff) / 255.0;
1005
+ double g = (double)((c >> 16) & 0xff) / 255.0;
1006
+ double b = (double)((c >> 8) & 0xff) / 255.0;
1007
+ double a = (double)( c & 0xff) / 255.0;
1008
+ if (a == 1.0)
1009
+ cairo_set_source_rgb(cr, r, g, b);
1010
+ else
1011
+ cairo_set_source_rgba(cr, r, g, b, a);
1012
+ }
1013
+
1014
+
1015
+ void P1788Figure::State::decorate()
1016
+ {
1017
+ if (!figure->print_graduations() || !cr) return;
1018
+
1019
+ /* Draw frame */
1020
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
1021
+ set_source_color(figure->graduation_color());
1022
+ cairo_set_line_width(cr, figure->graduation_stroke_width());
1023
+ if (((int)round(figure->graduation_stroke_width()) & 1) == 1)
1024
+ cairo_rectangle(cr, Interval::inf(frame_box.x)+0.5, Interval::inf(frame_box.y)+0.5, Interval::wid(frame_box.x), Interval::wid(frame_box.y));
1025
+ else
1026
+ cairo_rectangle(cr, Interval::inf(frame_box.x), Interval::inf(frame_box.y), Interval::wid(frame_box.x)+1, Interval::wid(frame_box.y)+1);
1027
+ cairo_stroke(cr);
1028
+
1029
+ set_font_options(cr);
1030
+
1031
+ cairo_set_line_width(cr, 1.0);
1032
+
1033
+ /* X axis */
1034
+ for (auto s = x_grad.cbegin(); s < x_grad.cend(); ++s) {
1035
+ double x = round(s->value * m_Sx + m_Tx);
1036
+ if (x < Interval::inf(frame_box.x) || x > Interval::sup(frame_box.x))
1037
+ continue;
1038
+ cairo_move_to(cr, x+0.5, Interval::sup(frame_box.y)+0.5);
1039
+ cairo_rel_line_to(cr, 0.0, -ceil(char_width*0.7));
1040
+ cairo_stroke(cr);
1041
+ cairo_move_to(cr, x+0.5, Interval::inf(frame_box.y)+0.5);
1042
+ cairo_rel_line_to(cr, 0.0, ceil(char_width*0.7));
1043
+ cairo_stroke(cr);
1044
+
1045
+ cairo_move_to(cr,
1046
+ round(x - s->width/2.0 - s->x_bearing + 0.5),
1047
+ round(Interval::sup(frame_box.y) + char_height + max_x_grad_height));
1048
+ cairo_show_text(cr, s->str.c_str());
1049
+ }
1050
+
1051
+ /* Y axis */
1052
+ for (auto s = y_grad.cbegin(); s < y_grad.cend(); ++s) {
1053
+ double y = round(s->value * m_Sy + m_Ty);
1054
+ if (y < Interval::inf(frame_box.y) || y > Interval::sup(frame_box.y))
1055
+ continue;
1056
+ cairo_move_to(cr, Interval::inf(frame_box.x)+0.5, y+0.5);
1057
+ cairo_rel_line_to(cr, ceil(char_width*0.7), 0);
1058
+ cairo_stroke(cr);
1059
+ cairo_move_to(cr, Interval::sup(frame_box.x)+0.5, y+0.5);
1060
+ cairo_rel_line_to(cr, -ceil(char_width*0.7), 0);
1061
+ cairo_stroke(cr);
1062
+
1063
+ cairo_move_to(cr,
1064
+ round(Interval::inf(frame_box.x) - char_width - s->width - s->x_bearing),
1065
+ round(y - s->y_bearing - s->height/2.0 + 0.5));
1066
+ cairo_show_text(cr, s->str.c_str());
1067
+ }
1068
+
1069
+ cairo_set_font_size(cr, figure->label_font_size());
1070
+
1071
+ /* X label */
1072
+ if (!xlabel.empty()) {
1073
+ cairo_move_to(cr,
1074
+ round(Interval::inf(frame_box.x) + Interval::wid(frame_box.x)/2.0 - xlabel.width/2 - xlabel.x_bearing),
1075
+ round(Interval::sup(frame_box.y) + char_height + max_x_grad_height + 2.0*xlabel.height));
1076
+ cairo_show_text(cr, xlabel.str.c_str());
1077
+ }
1078
+
1079
+ /* Y label */
1080
+ if (!ylabel.empty()) {
1081
+ cairo_save(cr);
1082
+ cairo_rotate(cr, -M_PI/2.0);
1083
+
1084
+ cairo_move_to(cr,
1085
+ round(-Interval::inf(frame_box.y) - ylabel.width/2.0 - ylabel.x_bearing - Interval::wid(frame_box.y)/2.0),
1086
+ round(Interval::inf(frame_box.x) - char_width - max_y_grad_width - ylabel.height));
1087
+ cairo_show_text(cr, ylabel.str.c_str());
1088
+ cairo_restore(cr);
1089
+ }
1090
+
1091
+ /* Title */
1092
+ if (!title.empty()) {
1093
+ cairo_set_font_size(cr, figure->title_font_size());
1094
+ if (figure->title_bold())
1095
+ cairo_select_font_face(cr, figure->graduation_font().c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
1096
+ cairo_move_to(cr,
1097
+ round(Interval::inf(frame_box.x) + Interval::wid(frame_box.x)/2.0 - title.width/2 - title.x_bearing),
1098
+ round(title.height - title.y_bearing));
1099
+ cairo_show_text(cr, title.str.c_str());
1100
+ }
1101
+ }
1102
+
1103
+
1104
+ P1788Figure::TickAxis::TickAxis(const Interval& interval, int nb_ticks) :
1105
+ m_interval(Interval::is_common_interval(interval) ? interval : Interval(0.0, 1.0)),
1106
+ m_nice_min(0.0),
1107
+ m_nice_max(0.0),
1108
+ m_tick_spacing(0.0),
1109
+ m_nb_ticks(std::max(2, nb_ticks))
1110
+ {
1111
+ update();
1112
+ }
1113
+
1114
+
1115
+ static double P1788Figure_round_double(double x, int nbits)
1116
+ {
1117
+ /* https://stackoverflow.com/questions/41583249/how-to-round-a-double-float-to-binary-precision */
1118
+ int c = 1 | (1 << nbits);
1119
+ double t = c * x;
1120
+ return x - t + t;
1121
+ }
1122
+
1123
+
1124
+ std::vector<double> P1788Figure::TickAxis::get_coordinates() const
1125
+ {
1126
+ std::vector<double> r;
1127
+ double i = 0.0;
1128
+ double d;
1129
+ const double mx = Interval::sup(m_interval) + Interval::wid(m_interval)/10000.0f;
1130
+
1131
+ while (1) {
1132
+ d = m_nice_min + i*m_tick_spacing;
1133
+ if (d > mx)
1134
+ break;
1135
+ else
1136
+ r.push_back(std::abs(d) <= 1e-15 ? 0.0 : d);
1137
+ i = i + 1.0;
1138
+ }
1139
+
1140
+ return r;
1141
+ }
1142
+
1143
+
1144
+ void P1788Figure::TickAxis::update()
1145
+ {
1146
+ const double rng = Interval::wid(m_interval);
1147
+ const double minimum = rng / (double)m_nb_ticks;
1148
+ const double magnitude = exp10(floor(log10(minimum)));
1149
+ const double residual = minimum / magnitude;
1150
+
1151
+ if (residual > 5.0)
1152
+ m_tick_spacing = 10.0 * magnitude;
1153
+ else if (residual > 2.0)
1154
+ m_tick_spacing = 5.0 * magnitude;
1155
+ else if (residual > 1.0)
1156
+ m_tick_spacing = 2.0 * magnitude;
1157
+ else
1158
+ m_tick_spacing = 1.0 * magnitude;
1159
+
1160
+ const int nbits = 1;
1161
+ // printf("--------------------------------------\n");
1162
+ // printf("min: %.16f\n", Interval::inf(m_interval));
1163
+ // printf("m_tick_spacing: %.16f\n", m_tick_spacing);
1164
+ // printf("(min/m_tick_spacing): %.16f => %.16f\n", Interval::inf(m_interval)/m_tick_spacing,
1165
+ // P1788Figure_round_double(Interval::inf(m_interval)/m_tick_spacing, nbits));
1166
+ // printf("floor(min/m_tick_spacing): %.16f => %.16f\n", floor(Interval::inf(m_interval)/m_tick_spacing),
1167
+ // floor(P1788Figure_round_double(Interval::inf(m_interval)/m_tick_spacing, nbits)));
1168
+ // printf("floor(min/m_tick_spacing)*m_tick_spacing: %.16f => %.16f\n", floor(Interval::inf(m_interval)/m_tick_spacing)*m_tick_spacing,
1169
+ // floor(P1788Figure_round_double(Interval::inf(m_interval)/m_tick_spacing, nbits))*m_tick_spacing);
1170
+ // printf("--\n");
1171
+ // printf("max: %.16f\n", Interval::sup(m_interval));
1172
+ // printf("m_tick_spacing: %.16f\n", m_tick_spacing);
1173
+ // printf("(max/m_tick_spacing): %.16f => %.16f\n", Interval::sup(m_interval)/m_tick_spacing,
1174
+ // P1788Figure_round_double(Interval::sup(m_interval)/m_tick_spacing, nbits));
1175
+ // printf("ceil(max/m_tick_spacing): %.16f => %.16f\n", ceil(Interval::sup(m_interval)/m_tick_spacing),
1176
+ // ceil(P1788Figure_round_double(Interval::sup(m_interval)/m_tick_spacing, nbits)));
1177
+ // printf("ceil(max/m_tick_spacing)*m_tick_spacing: %.16f => %.16f\n", ceil(Interval::sup(m_interval)/m_tick_spacing)*m_tick_spacing,
1178
+ // ceil(P1788Figure_round_double(Interval::sup(m_interval)/m_tick_spacing, nbits))*m_tick_spacing);
1179
+ // printf("--------------------------------------\n");
1180
+ m_nice_min = P1788Figure_round_double(floor(P1788Figure_round_double(Interval::inf(m_interval) / m_tick_spacing, nbits)) * m_tick_spacing, nbits);
1181
+ m_nice_max = P1788Figure_round_double( ceil(P1788Figure_round_double(Interval::sup(m_interval) / m_tick_spacing, nbits)) * m_tick_spacing, nbits);
1182
+ }
1183
+
1184
+
1185
+ #if 0
1186
+ static bool P1788Figure_all_doubles_are_integer(const std::vector<double>& v)
1187
+ {
1188
+ bool r = true;
1189
+ const size_t l = v.size();
1190
+ for (size_t i = 0; i < l; i++) {
1191
+ if ((double)((int)(v[i])) != v[i]) {
1192
+ r = false;
1193
+ break;
1194
+ }
1195
+ }
1196
+ return r;
1197
+ }
1198
+
1199
+
1200
+ static std::string P1788Figure_double_to_string(double v, bool rm_point)
1201
+ {
1202
+ std::string s = std::to_string(v);
1203
+ int l = s.size();
1204
+ int nl = l;
1205
+ if (rm_point) {
1206
+ while (nl > 1 && s[nl-1] == '0')
1207
+ nl--;
1208
+ if (nl > 1 && s[nl-1] == '.')
1209
+ nl--;
1210
+ }
1211
+ else {
1212
+ while (nl > 2 && s[nl-1] == '0' && s[nl-2] != '.')
1213
+ nl--;
1214
+ }
1215
+ if (nl != l)
1216
+ s.resize(nl);
1217
+ #else
1218
+ static P1788Figure::StringWithExtent P1788Figure_double_to_string(double v)
1219
+ {
1220
+ std::ostringstream streamObj;
1221
+ streamObj << v;
1222
+ P1788Figure::StringWithExtent s(streamObj.str());
1223
+ #endif
1224
+ return s;
1225
+ }
1226
+
1227
+
1228
+ static bool P1788Figure_one_is_not_integer(const std::vector<P1788Figure::StringWithExtent>& v)
1229
+ {
1230
+ const size_t l = v.size();
1231
+ for (size_t i = 0; i < l; i++) {
1232
+ const std::string& str = v[i].str;
1233
+ const size_t sl = str.length();
1234
+ for (size_t j = 0; j < sl; j++) {
1235
+ if ((str[j] < '0' || str[j] > '9') && str[j] != '-')
1236
+ return true;
1237
+ }
1238
+ }
1239
+ return false;
1240
+ }
1241
+
1242
+
1243
+ static bool P1788Figure_string_is_an_integer_string(const std::string& str)
1244
+ {
1245
+ const size_t sl = str.length();
1246
+ if (sl < 1)
1247
+ return false;
1248
+ for (size_t j = 0; j < sl; j++) {
1249
+ if ((str[j] < '0' || str[j] > '9') && str[j] != '-')
1250
+ return false;
1251
+ }
1252
+ return true;
1253
+ }
1254
+
1255
+
1256
+ static void P1788Figure_add_dot_zero_to_integer_strings(std::vector<P1788Figure::StringWithExtent>& v)
1257
+ {
1258
+ const size_t l = v.size();
1259
+ for (size_t i = 0; i < l; i++) {
1260
+ if (P1788Figure_string_is_an_integer_string(v[i].str))
1261
+ v[i].str += ".0";
1262
+ }
1263
+ }
1264
+
1265
+
1266
+ std::vector<P1788Figure::StringWithExtent> P1788Figure::TickAxis::get_coordinate_strings() const
1267
+ {
1268
+ std::vector<StringWithExtent> strs;
1269
+ std::vector<double> coords(get_coordinates());
1270
+ // bool rm_point = P1788Figure_all_doubles_are_integer(coords);
1271
+ const size_t l = coords.size();
1272
+ for (size_t i = 0; i < l; i++) {
1273
+ strs.emplace_back(P1788Figure_double_to_string(coords[i]));
1274
+ strs.back().value = coords[i];
1275
+ //printf("%s\n", strs[i].str.c_str());
1276
+ }
1277
+
1278
+ if (P1788Figure_one_is_not_integer(strs))
1279
+ P1788Figure_add_dot_zero_to_integer_strings(strs);
1280
+
1281
+ return strs;
1282
+ }
1283
+
1284
+