p1788 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+