1 /* $Id: draw.c,v 1.34 2016/03/04 00:25:52 kristaps Exp $ */
2 /*
3 * Copyright (c) 2014, 2015 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include "compat.h"
18
19 #include <assert.h>
20 #include <cairo.h>
21 #include <float.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "kplot.h"
28 #include "extern.h"
29
30 static double dimx = 0.0;
31 static double dimy = 0.0;
32 static double offsx = 0.0;
33 static double offsy = 0.0;
34
35
36 /*
37 * Simple function to check that the double-precision values in the
38 * kpair are valid: normal (or 0.0) values.
39 */
40 static inline int
kpair_vrfy(const struct kpair * data)41 kpair_vrfy(const struct kpair *data)
42 {
43
44 if (0.0 != data->x && ! isnormal(data->x))
45 return(0);
46 if (0.0 != data->y && ! isnormal(data->y))
47 return(0);
48 return(1);
49 }
50
51 /*
52 * Set the pair "kp" to the value at position "pos", which depends upon
53 * the smoothing type (if stipulated).
54 * The value "kp" SHOULD NOT be cleared between invocations, as some
55 * data streams (e.g., KSMOOTH_CDF) accumulate it.
56 */
57 static void
kpair_set(const struct kplotdat * d,size_t pos,struct kpair * kp)58 kpair_set(const struct kplotdat *d, size_t pos, struct kpair *kp)
59 {
60 size_t j, sz, samps;
61 ssize_t start;
62
63 switch (d->smthtype) {
64 case (KSMOOTH_CDF):
65 kp->x = d->datas[0]->pairs[pos].x;
66 kp->y += d->datas[0]->pairs[pos].y / d->sum;
67 break;
68 case (KSMOOTH_PMF):
69 kp->x = d->datas[0]->pairs[pos].x;
70 kp->y = d->datas[0]->pairs[pos].y / d->sum;
71 break;
72 case (KSMOOTH_MOVAVG):
73 *kp = d->datas[0]->pairs[pos];
74 samps = d->smth.movsamples / 2;
75 start = pos - samps;
76 sz = pos + samps;
77 if (start < 0 || sz >= d->datas[0]->pairsz)
78 break;
79 for (kp->y = 0.0, j = start; j <= sz; j++) {
80 if ( ! kpair_vrfy(&d->datas[0]->pairs[j]))
81 break;
82 kp->y += d->datas[0]->pairs[j].y;
83 }
84 kp->y /= (double)d->smth.movsamples;
85 if (j <= sz)
86 *kp = d->datas[0]->pairs[pos];
87 break;
88 default:
89 *kp = d->datas[0]->pairs[pos];
90 break;
91 }
92 }
93
94 /*
95 * Accumulate extrema where we add and subtract the second data source
96 * from the first, e.g., in a graph with mean and standard deviation.
97 */
98 static void
kdata_extrema_yerr(struct kplotdat * d,struct kplotctx * ctx)99 kdata_extrema_yerr(struct kplotdat *d, struct kplotctx *ctx)
100 {
101 size_t i, sz;
102 struct kpair *p, *err;
103
104 assert(d->datasz > 1);
105 p = d->datas[0]->pairs;
106 err = d->datas[1]->pairs;
107
108 /* Truncate to the smaller of the two pair lengths. */
109 sz = d->datas[0]->pairsz < d->datas[1]->pairsz ?
110 d->datas[0]->pairsz : d->datas[1]->pairsz;
111
112 for (i = 0; i < sz; i++) {
113 /* Both must be valid. */
114 if ( ! (kpair_vrfy(&p[i]) && kpair_vrfy(&err[i])))
115 continue;
116
117 /*
118 * Since the error can be negative, check in both
119 * directions from the basis point.
120 */
121 if (p[i].x < ctx->minv.x)
122 ctx->minv.x = p[i].x;
123 if (p[i].x > ctx->maxv.x)
124 ctx->maxv.x = p[i].x;
125 if (p[i].y - err[i].y < ctx->minv.y)
126 ctx->minv.y = p[i].y - err[i].y;
127 if (p[i].y + err[i].y < ctx->minv.y)
128 ctx->minv.y = p[i].y + err[i].y;
129 if (p[i].y - err[i].y > ctx->maxv.y)
130 ctx->maxv.y = p[i].y - err[i].y;
131 if (p[i].y + err[i].y > ctx->maxv.y)
132 ctx->maxv.y = p[i].y + err[i].y;
133 }
134 }
135
136 /*
137 * Accumulate extrema of a single data source.
138 */
139 static void
kdata_extrema_single(struct kplotdat * d,struct kplotctx * ctx)140 kdata_extrema_single(struct kplotdat *d, struct kplotctx *ctx)
141 {
142 size_t i;
143 double max;
144 struct kpair kp;
145
146 max = -DBL_MAX;
147 d->sum = 0.0;
148 memset(&kp, 0, sizeof(struct kpair));
149 for (i = 0; i < d->datas[0]->pairsz; i++) {
150 if ( ! kpair_vrfy(&d->datas[0]->pairs[i]))
151 continue;
152 kpair_set(d, i, &kp);
153 if (KSMOOTH_CDF == d->smthtype)
154 d->sum += d->datas[0]->pairs[i].y;
155 if (KSMOOTH_PMF == d->smthtype) {
156 d->sum += d->datas[0]->pairs[i].y;
157 if (d->datas[0]->pairs[i].y > max)
158 max = d->datas[0]->pairs[i].y;
159 }
160 if (kp.x < ctx->minv.x)
161 ctx->minv.x = kp.x;
162 if (kp.x > ctx->maxv.x)
163 ctx->maxv.x = kp.x;
164 switch (d->smthtype) {
165 case (KSMOOTH_CDF):
166 case (KSMOOTH_PMF):
167 break;
168 default:
169 if (kp.y < ctx->minv.y)
170 ctx->minv.y = kp.y;
171 if (kp.y > ctx->maxv.y)
172 ctx->maxv.y = kp.y;
173 break;
174 }
175 }
176 if (KSMOOTH_CDF == d->smthtype) {
177 if (0.0 < ctx->minv.y)
178 ctx->minv.y = 0.0;
179 if (1.0 > ctx->maxv.y)
180 ctx->maxv.y = 1.0;
181 } else if (KSMOOTH_PMF == d->smthtype) {
182 if (0.0 < ctx->minv.y)
183 ctx->minv.y = 0.0;
184 if (max / d->sum > ctx->maxv.y)
185 ctx->maxv.y = max / d->sum;
186 }
187 }
188
189
190 /*
191 * Adjust a plot point to be within the graphing space.
192 * The graphing space is the same for all data sources in the plot, so
193 * we simply take the point and adjust it.
194 * NOTE: this might fall outside of the drawable area.
195 * That's ok: we'll discard it (if points) or clip it (lines).
196 */
197 static inline void
kpoint_to_real(const struct kpair * data,struct kpair * real,const struct kpair * minv,const struct kpair * maxv,double w,double h)198 kpoint_to_real(const struct kpair *data, struct kpair *real,
199 const struct kpair *minv, const struct kpair *maxv,
200 double w, double h)
201 {
202
203 real->x = maxv->x == minv->x ? 0.0 :
204 w * (data->x - minv->x) / (maxv->x - minv->x);
205 real->y = maxv->y == minv->y ? h :
206 h - h * (data->y - minv->y) / (maxv->y - minv->y);
207 }
208
209 /*
210 * Verify that a given point is real (in terms of floating-point) and if
211 * so, convert it to the plot space.
212 */
213 static int
kplotctx_point_to_real(const struct kpair * data,struct kpair * real,const struct kplotctx * ctx)214 kplotctx_point_to_real(const struct kpair *data,
215 struct kpair *real, const struct kplotctx *ctx)
216 {
217
218 if ( ! kpair_vrfy(data))
219 return(0);
220 kpoint_to_real(data, real,
221 &ctx->minv, &ctx->maxv, ctx->w, ctx->h);
222 return(1);
223 }
224
225 /*
226 * Draw a circle (arc) to the plot IFF it happens to fall within the
227 * boundaries we set with the plot, otherwise do nothing.
228 */
229 static void
kplot_arc(const struct kpair * kp,const struct kplotpoint * p,struct kplotctx * ctx)230 kplot_arc(const struct kpair *kp,
231 const struct kplotpoint *p, struct kplotctx *ctx)
232 {
233 struct kpair pair;
234
235 if (kp->x < ctx->minv.x || kp->x > ctx->maxv.x)
236 return;
237 if (kp->y < ctx->minv.y || kp->y > ctx->maxv.y)
238 return;
239 if (0 == kplotctx_point_to_real(kp, &pair, ctx))
240 return;
241 cairo_arc(ctx->cr, pair.x, pair.y, p->radius, 0, 2 * M_PI);
242 cairo_stroke(ctx->cr);
243 }
244
245 static void
kplot_mark(const struct kpair * kp,const struct kplotpoint * p,struct kplotctx * ctx)246 kplot_mark(const struct kpair *kp,
247 const struct kplotpoint *p, struct kplotctx *ctx)
248 {
249 struct kpair pair;
250
251 if (kp->x < ctx->minv.x || kp->x > ctx->maxv.x)
252 return;
253 if (kp->y < ctx->minv.y || kp->y > ctx->maxv.y)
254 return;
255 if (0 == kplotctx_point_to_real(kp, &pair, ctx))
256 return;
257 cairo_move_to (ctx->cr, pair.x - p->radius, pair.y - p->radius);
258 cairo_line_to (ctx->cr, pair.x + p->radius, pair.y + p->radius);
259 cairo_move_to (ctx->cr, pair.x - p->radius, pair.y + p->radius);
260 cairo_line_to (ctx->cr, pair.x + p->radius, pair.y - p->radius);
261 cairo_stroke (ctx->cr);
262 }
263
264 /*
265 * When drawing points, arrange the drawing space.
266 * It's the responsibility of kplot_arc() to avoid points that would be
267 * drawn outside of this range.
268 * You must call cairo_restore(ctx->cr) to symmetrise.
269 */
270 static void
ksubwin_points(struct kplotctx * ctx)271 ksubwin_points(struct kplotctx *ctx)
272 {
273
274 cairo_save(ctx->cr);
275 cairo_translate(ctx->cr, ctx->offs.x, ctx->offs.y);
276 }
277
278 /*
279 * When drawing lines (or bars), create a subwindow large enough for
280 * lines within the context dimensions.
281 * Lines drawn outside of the subwindow will be clipped.
282 * You must call cairo_restore(ctx->cr) to symmetrise.
283 */
284 static void
ksubwin_lines(struct kplotctx * ctx,const struct kdatacfg * dat)285 ksubwin_lines(struct kplotctx *ctx, const struct kdatacfg *dat)
286 {
287 double width;
288
289 width = dat->line.sz / 2.0;
290 cairo_save(ctx->cr);
291 cairo_translate(ctx->cr,
292 ctx->offs.x - width,
293 ctx->offs.y - width);
294 cairo_rectangle(ctx->cr, 0, 0,
295 ctx->dims.x + width * 2,
296 ctx->dims.y + width * 2);
297 cairo_clip(ctx->cr);
298 cairo_translate(ctx->cr, width, width);
299 }
300
301 static size_t
kplotctx_draw_yerrline_start(struct kplotctx * ctx,const struct kplotdat * d,size_t * end)302 kplotctx_draw_yerrline_start(struct kplotctx *ctx,
303 const struct kplotdat *d, size_t *end)
304 {
305 size_t start;
306
307 /* Overlap between both point sets. */
308 *end = d->datas[0]->pairsz < d->datas[1]->pairsz ?
309 d->datas[0]->pairsz : d->datas[1]->pairsz;
310
311 /* Skip past bad points to get to initial. */
312 for (start = 0; start < *end; start++)
313 if (kpair_vrfy(&d->datas[0]->pairs[start]) &&
314 kpair_vrfy(&d->datas[1]->pairs[start]))
315 return(start);
316
317 return(*end);
318 }
319
320 static void
kplotctx_draw_yerrline_basepoints(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)321 kplotctx_draw_yerrline_basepoints(struct kplotctx *ctx,
322 size_t start, size_t end, const struct kplotdat *d)
323 {
324 size_t i;
325
326 ksubwin_points(ctx);
327 kplotctx_point_init(ctx, &d->cfgs[0].point);
328 for (i = start; i < end; i++) {
329 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
330 kpair_vrfy(&d->datas[1]->pairs[i])))
331 continue;
332 kplot_arc(&d->datas[0]->pairs[i],
333 &d->cfgs[0].point, ctx);
334 }
335 cairo_restore(ctx->cr);
336 }
337
338 static void
kplotctx_draw_yerrline_basemarks(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)339 kplotctx_draw_yerrline_basemarks(struct kplotctx *ctx,
340 size_t start, size_t end, const struct kplotdat *d)
341 {
342 size_t i;
343
344 ksubwin_points(ctx);
345 kplotctx_point_init(ctx, &d->cfgs[0].point);
346 for (i = start; i < end; i++) {
347 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
348 kpair_vrfy(&d->datas[1]->pairs[i])))
349 continue;
350 kplot_mark(&d->datas[0]->pairs[i],
351 &d->cfgs[0].point, ctx);
352 }
353 cairo_restore(ctx->cr);
354 }
355
356 static void
kplotctx_draw_yerrline_pairbars(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)357 kplotctx_draw_yerrline_pairbars(struct kplotctx *ctx,
358 size_t start, size_t end, const struct kplotdat *d)
359 {
360 size_t i;
361 struct kpair bot, top, pair;
362 int rc;
363
364 ksubwin_lines(ctx, &d->cfgs[1]);
365 kplotctx_line_init(ctx, &d->cfgs[1].line);
366 for (i = start; i < end; i++) {
367 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
368 kpair_vrfy(&d->datas[1]->pairs[i])))
369 continue;
370
371 bot.x = top.x = d->datas[0]->pairs[i].x;
372 bot.y = d->datas[0]->pairs[i].y -
373 d->datas[1]->pairs[i].y;
374 top.y = d->datas[0]->pairs[i].y +
375 d->datas[1]->pairs[i].y;
376
377 rc = kplotctx_point_to_real(&bot, &pair, ctx);
378 assert(0 != rc);
379 cairo_move_to(ctx->cr, pair.x, pair.y);
380
381 rc = kplotctx_point_to_real(&top, &pair, ctx);
382 assert(0 != rc);
383 cairo_line_to(ctx->cr, pair.x, pair.y);
384 }
385 cairo_stroke(ctx->cr);
386 cairo_restore(ctx->cr);
387 }
388
389 static void
kplotctx_draw_yerrline_pairpoints(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)390 kplotctx_draw_yerrline_pairpoints(struct kplotctx *ctx,
391 size_t start, size_t end, const struct kplotdat *d)
392 {
393 size_t i;
394 struct kpair orig;
395
396 ksubwin_points(ctx);
397 kplotctx_point_init(ctx, &d->cfgs[1].point);
398 for (i = start; i < end; i++) {
399 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
400 kpair_vrfy(&d->datas[1]->pairs[i])))
401 continue;
402 orig.x = d->datas[0]->pairs[i].x;
403 orig.y = d->datas[0]->pairs[i].y +
404 d->datas[1]->pairs[i].y;
405 kplot_arc(&orig, &d->cfgs[1].point, ctx);
406 }
407
408 kplotctx_point_init(ctx, &d->cfgs[1].point);
409 for (i = start; i < end; i++) {
410 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
411 kpair_vrfy(&d->datas[1]->pairs[i])))
412 continue;
413 orig.x = d->datas[0]->pairs[i].x;
414 orig.y = d->datas[0]->pairs[i].y -
415 d->datas[1]->pairs[i].y;
416 kplot_arc(&orig, &d->cfgs[1].point, ctx);
417 }
418
419 cairo_restore(ctx->cr);
420 }
421
422 static void
kplotctx_draw_yerrline_pairmarks(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)423 kplotctx_draw_yerrline_pairmarks(struct kplotctx *ctx,
424 size_t start, size_t end, const struct kplotdat *d)
425 {
426 size_t i;
427 struct kpair orig;
428
429 ksubwin_points(ctx);
430 kplotctx_point_init(ctx, &d->cfgs[1].point);
431 for (i = start; i < end; i++) {
432 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
433 kpair_vrfy(&d->datas[1]->pairs[i])))
434 continue;
435 orig.x = d->datas[0]->pairs[i].x;
436 orig.y = d->datas[0]->pairs[i].y +
437 d->datas[1]->pairs[i].y;
438 kplot_mark(&orig, &d->cfgs[1].point, ctx);
439 }
440
441 kplotctx_point_init(ctx, &d->cfgs[1].point);
442 for (i = start; i < end; i++) {
443 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
444 kpair_vrfy(&d->datas[1]->pairs[i])))
445 continue;
446 orig.x = d->datas[0]->pairs[i].x;
447 orig.y = d->datas[0]->pairs[i].y -
448 d->datas[1]->pairs[i].y;
449 kplot_mark(&orig, &d->cfgs[1].point, ctx);
450 }
451
452 cairo_restore(ctx->cr);
453 }
454
455 static void
kplotctx_draw_yerrline_baselines(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)456 kplotctx_draw_yerrline_baselines(struct kplotctx *ctx,
457 size_t start, size_t end, const struct kplotdat *d)
458 {
459 size_t i;
460 struct kpair pair;
461 int rc;
462
463 assert(d->datasz > 1);
464 ksubwin_lines(ctx, &d->cfgs[0]);
465 kplotctx_line_init(ctx, &d->cfgs[0].line);
466 rc = kplotctx_point_to_real
467 (&d->datas[0]->pairs[start], &pair, ctx);
468 assert(0 != rc);
469 cairo_move_to(ctx->cr, pair.x, pair.y);
470 for (i = start; i < end; i++) {
471 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
472 kpair_vrfy(&d->datas[1]->pairs[i])))
473 continue;
474 rc = kplotctx_point_to_real
475 (&d->datas[0]->pairs[i], &pair, ctx);
476 assert(0 != rc);
477 cairo_line_to(ctx->cr, pair.x, pair.y);
478 }
479 cairo_stroke(ctx->cr);
480 cairo_restore(ctx->cr);
481 }
482
483 static void
kplotctx_draw_yerrline_pairlines(struct kplotctx * ctx,size_t start,size_t end,const struct kplotdat * d)484 kplotctx_draw_yerrline_pairlines(struct kplotctx *ctx,
485 size_t start, size_t end, const struct kplotdat *d)
486 {
487 struct kpair orig, pair;
488 size_t i;
489 int rc;
490
491 ksubwin_lines(ctx, &d->cfgs[1]);
492 kplotctx_line_init(ctx, &d->cfgs[1].line);
493 orig.x = d->datas[0]->pairs[start].x;
494 orig.y = d->datas[0]->pairs[start].y +
495 d->datas[1]->pairs[start].y;
496 rc = kplotctx_point_to_real(&orig, &pair, ctx);
497 assert(0 != rc);
498 cairo_move_to(ctx->cr, pair.x, pair.y);
499 for (i = start; i < end; i++) {
500 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
501 kpair_vrfy(&d->datas[1]->pairs[i])))
502 continue;
503 orig.x = d->datas[0]->pairs[i].x;
504 orig.y = d->datas[0]->pairs[i].y +
505 d->datas[1]->pairs[i].y;
506 rc = kplotctx_point_to_real(&orig, &pair, ctx);
507 assert(0 != rc);
508 cairo_line_to(ctx->cr, pair.x, pair.y);
509 }
510 cairo_stroke(ctx->cr);
511
512 kplotctx_line_init(ctx, &d->cfgs[1].line);
513 orig.x = d->datas[0]->pairs[start].x;
514 orig.y = d->datas[0]->pairs[start].y -
515 d->datas[1]->pairs[start].y;
516 kplotctx_point_to_real(&orig, &pair, ctx);
517 cairo_move_to(ctx->cr, pair.x, pair.y);
518 for (i = start; i < end; i++) {
519 if ( ! (kpair_vrfy(&d->datas[0]->pairs[i]) &&
520 kpair_vrfy(&d->datas[1]->pairs[i])))
521 continue;
522 orig.x = d->datas[0]->pairs[i].x;
523 orig.y = d->datas[0]->pairs[i].y -
524 d->datas[1]->pairs[i].y;
525 rc = kplotctx_point_to_real(&orig, &pair, ctx);
526 assert(0 != rc);
527 cairo_line_to(ctx->cr, pair.x, pair.y);
528 }
529 cairo_stroke(ctx->cr);
530 cairo_restore(ctx->cr);
531 }
532
533 static void
kplotctx_draw_lines(struct kplotctx * ctx,const struct kplotdat * d)534 kplotctx_draw_lines(struct kplotctx *ctx, const struct kplotdat *d)
535 {
536 size_t i;
537 struct kpair kp, pair;
538 int rc;
539
540 ksubwin_lines(ctx, &d->cfgs[0]);
541 memset(&kp, 0, sizeof(struct kpair));
542 for (i = 0; i < d->datas[0]->pairsz; i++) {
543 kpair_set(d, i, &kp);
544 if (kplotctx_point_to_real(&kp, &pair, ctx))
545 break;
546 }
547
548 if (i == d->datas[0]->pairsz)
549 goto out;
550
551 kplotctx_line_init(ctx, &d->cfgs[0].line);
552 cairo_move_to(ctx->cr, pair.x, pair.y);
553 memset(&kp, 0, sizeof(struct kpair));
554 for ( ; i < d->datas[0]->pairsz; i++) {
555 if ( ! kpair_vrfy(&d->datas[0]->pairs[i]))
556 continue;
557 kpair_set(d, i, &kp);
558 rc = kplotctx_point_to_real(&kp, &pair, ctx);
559 if ( ! rc)
560 continue;
561 cairo_line_to(ctx->cr, pair.x, pair.y);
562 }
563 cairo_stroke(ctx->cr);
564 out:
565 cairo_restore(ctx->cr);
566 }
567
568 static void
kplotctx_draw_points(struct kplotctx * ctx,const struct kplotdat * d)569 kplotctx_draw_points(struct kplotctx *ctx, const struct kplotdat *d)
570 {
571 size_t i;
572 struct kpair kp;
573
574 ksubwin_points(ctx);
575 memset(&kp, 0, sizeof(struct kpair));
576 kplotctx_point_init(ctx, &d->cfgs[0].point);
577 for (i = 0; i < d->datas[0]->pairsz; i++) {
578 if ( ! kpair_vrfy(&d->datas[0]->pairs[i]))
579 continue;
580 kpair_set(d, i, &kp);
581 kplot_arc(&kp, &d->cfgs[0].point, ctx);
582 }
583 cairo_restore(ctx->cr);
584 }
585
586 static void
kplotctx_draw_marks(struct kplotctx * ctx,const struct kplotdat * d)587 kplotctx_draw_marks(struct kplotctx *ctx, const struct kplotdat *d)
588 {
589 size_t i;
590 struct kpair kp;
591
592 ksubwin_points(ctx);
593 memset(&kp, 0, sizeof(struct kpair));
594 kplotctx_point_init(ctx, &d->cfgs[0].point);
595 for (i = 0; i < d->datas[0]->pairsz; i++) {
596 if ( ! kpair_vrfy(&d->datas[0]->pairs[i]))
597 continue;
598 kpair_set(d, i, &kp);
599 kplot_mark(&kp, &d->cfgs[0].point, ctx);
600 }
601 cairo_restore(ctx->cr);
602 }
603
604 void
kplotfont_defaults(struct kplotfont * font)605 kplotfont_defaults(struct kplotfont *font)
606 {
607
608 memset(font, 0, sizeof(struct kplotfont));
609
610 /* Point 12 size serif font. */
611 font->family = "serif";
612 font->sz = 12.0;
613 font->slant = CAIRO_FONT_SLANT_NORMAL;
614 font->weight = CAIRO_FONT_WEIGHT_NORMAL;
615 }
616
617 void
kplotcfg_defaults(struct kplotcfg * cfg)618 kplotcfg_defaults(struct kplotcfg *cfg)
619 {
620
621 memset(cfg, 0, sizeof(struct kplotcfg));
622
623 /* Five left and bottom grey tic labels. */
624 kplotfont_defaults(&cfg->ticlabelfont);
625 cfg->ticlabel = TICLABEL_LEFT | TICLABEL_BOTTOM;
626 cfg->xticlabelpad = cfg->yticlabelpad = 15.0;
627 cfg->xtics = cfg->ytics = 5;
628
629 /* A bit of margin. */
630 cfg->margin = MARGIN_ALL;
631 cfg->marginsz = 15.0;
632
633 /* Innie tics, grey. */
634 cfg->tic = TIC_LEFT_IN | TIC_BOTTOM_IN;
635 cfg->ticline.len = 5.0;
636 cfg->ticline.sz = 1.0;
637
638 /* Grid line: dotted, grey. */
639 cfg->grid = GRID_ALL;
640 cfg->gridline.sz = 1.0;
641 cfg->gridline.dashes[0] = 1.0;
642 cfg->gridline.dashes[1] = 4.0;
643 cfg->gridline.dashesz = 2;
644
645 /* Border line: solid, grey. */
646 cfg->border = BORDER_LEFT | BORDER_BOTTOM;
647 cfg->borderline.sz = 1.0;
648
649 /* Black axis labels. */
650 kplotfont_defaults(&cfg->axislabelfont);
651 cfg->xaxislabelpad = cfg->yaxislabelpad = 15.0;
652 }
653
654 void
kplot_draw(struct kplot * p,double w,double h,cairo_t * cr)655 kplot_draw(struct kplot *p, double w, double h, cairo_t *cr)
656 {
657 size_t i, start, end;
658 struct kplotctx ctx;
659 struct kplotdat *d;
660 struct kplotccfg defs[7];
661
662 memset(&ctx, 0, sizeof(struct kplotctx));
663
664 ctx.w = w;
665 ctx.h = h;
666 ctx.cr = cr;
667 ctx.minv.x = ctx.minv.y = DBL_MAX;
668 ctx.maxv.x = ctx.maxv.y = -DBL_MAX;
669 ctx.cfg = p->cfg;
670
671 if (KPLOTCTYPE_DEFAULT == ctx.cfg.borderline.clr.type) {
672 ctx.cfg.borderline.clr.type = KPLOTCTYPE_RGBA;
673 ctx.cfg.borderline.clr.rgba[0] = 0.0;
674 ctx.cfg.borderline.clr.rgba[1] = 0.0;
675 ctx.cfg.borderline.clr.rgba[2] = 0.0;
676 ctx.cfg.borderline.clr.rgba[3] = 1.0;
677 }
678
679 if (KPLOTCTYPE_DEFAULT == ctx.cfg.axislabelfont.clr.type) {
680 ctx.cfg.axislabelfont.clr.type = KPLOTCTYPE_RGBA;
681 ctx.cfg.axislabelfont.clr.rgba[0] = 0.5;
682 ctx.cfg.axislabelfont.clr.rgba[1] = 0.5;
683 ctx.cfg.axislabelfont.clr.rgba[2] = 0.5;
684 ctx.cfg.axislabelfont.clr.rgba[3] = 1.0;
685 }
686
687 if (KPLOTCTYPE_DEFAULT == ctx.cfg.ticline.clr.type) {
688 ctx.cfg.ticline.clr.type = KPLOTCTYPE_RGBA;
689 ctx.cfg.ticline.clr.rgba[0] = 0.0;
690 ctx.cfg.ticline.clr.rgba[1] = 0.0;
691 ctx.cfg.ticline.clr.rgba[2] = 0.0;
692 ctx.cfg.ticline.clr.rgba[3] = 1.0;
693 }
694
695 if (KPLOTCTYPE_DEFAULT == ctx.cfg.gridline.clr.type) {
696 ctx.cfg.gridline.clr.type = KPLOTCTYPE_RGBA;
697 ctx.cfg.gridline.clr.rgba[0] = 0.5;
698 ctx.cfg.gridline.clr.rgba[1] = 0.5;
699 ctx.cfg.gridline.clr.rgba[2] = 0.5;
700 ctx.cfg.gridline.clr.rgba[3] = 1.0;
701 }
702
703 if (KPLOTCTYPE_DEFAULT == ctx.cfg.ticlabelfont.clr.type) {
704 ctx.cfg.ticlabelfont.clr.type = KPLOTCTYPE_RGBA;
705 ctx.cfg.ticlabelfont.clr.rgba[0] = 0.5;
706 ctx.cfg.ticlabelfont.clr.rgba[1] = 0.5;
707 ctx.cfg.ticlabelfont.clr.rgba[2] = 0.5;
708 ctx.cfg.ticlabelfont.clr.rgba[3] = 1.0;
709 }
710
711 if (0 == ctx.cfg.clrsz) {
712 ctx.cfg.clrs = defs;
713 ctx.cfg.clrsz = 7;
714 for (i = 0; i < ctx.cfg.clrsz; i++) {
715 ctx.cfg.clrs[i].type = KPLOTCTYPE_RGBA;
716 ctx.cfg.clrs[i].rgba[3] = 1.0;
717 }
718 ctx.cfg.clrs[0].rgba[0] = 0x94 / 255.0;
719 ctx.cfg.clrs[0].rgba[1] = 0x04 / 255.0;
720 ctx.cfg.clrs[0].rgba[2] = 0xd3 / 255.0;
721 ctx.cfg.clrs[1].rgba[0] = 0x00 / 255.0;
722 ctx.cfg.clrs[1].rgba[1] = 0x9e / 255.0;
723 ctx.cfg.clrs[1].rgba[2] = 0x73 / 255.0;
724 ctx.cfg.clrs[2].rgba[0] = 0x56 / 255.0;
725 ctx.cfg.clrs[2].rgba[1] = 0xb4 / 255.0;
726 ctx.cfg.clrs[2].rgba[2] = 0xe9 / 255.0;
727 ctx.cfg.clrs[3].rgba[0] = 0xe6 / 255.0;
728 ctx.cfg.clrs[3].rgba[1] = 0x9f / 255.0;
729 ctx.cfg.clrs[3].rgba[2] = 0x00 / 255.0;
730 ctx.cfg.clrs[4].rgba[0] = 0xf0 / 255.0;
731 ctx.cfg.clrs[4].rgba[1] = 0xe4 / 255.0;
732 ctx.cfg.clrs[4].rgba[2] = 0x42 / 255.0;
733 ctx.cfg.clrs[5].rgba[0] = 0x00 / 255.0;
734 ctx.cfg.clrs[5].rgba[1] = 0x72 / 255.0;
735 ctx.cfg.clrs[5].rgba[2] = 0xb2 / 255.0;
736 ctx.cfg.clrs[6].rgba[0] = 0xe5 / 255.0;
737 ctx.cfg.clrs[6].rgba[1] = 0x1e / 255.0;
738 ctx.cfg.clrs[6].rgba[2] = 0x10 / 255.0;
739 }
740
741 for (i = 0; i < p->datasz; i++) {
742 d = &p->datas[i];
743 switch (d->stype) {
744 case (KPLOTS_YERRORBAR):
745 case (KPLOTS_YERRORLINE):
746 kdata_extrema_yerr(d, &ctx);
747 break;
748 case (KPLOTS_SINGLE):
749 kdata_extrema_single(d, &ctx);
750 break;
751 }
752 }
753
754 if (EXTREMA_XMIN & ctx.cfg.extrema)
755 ctx.minv.x = ctx.cfg.extrema_xmin;
756 if (EXTREMA_YMIN & ctx.cfg.extrema)
757 ctx.minv.y = ctx.cfg.extrema_ymin;
758 if (EXTREMA_XMAX & ctx.cfg.extrema)
759 ctx.maxv.x = ctx.cfg.extrema_xmax;
760 if (EXTREMA_YMAX & ctx.cfg.extrema)
761 ctx.maxv.y = ctx.cfg.extrema_ymax;
762
763 if (ctx.minv.x > ctx.maxv.x)
764 ctx.minv.x = ctx.maxv.x = 0.0;
765 if (ctx.minv.y > ctx.maxv.y)
766 ctx.minv.y = ctx.maxv.y = 0.0;
767
768 kplotctx_margin_init(&ctx);
769 kplotctx_label_init(&ctx);
770 kplotctx_grid_init(&ctx);
771 kplotctx_border_init(&ctx);
772 kplotctx_tic_init(&ctx);
773
774 dimy = ctx.h = ctx.dims.y;
775 dimx = ctx.w = ctx.dims.x;
776 offsx = ctx.offs.x;
777 offsy = ctx.offs.y;
778
779 for (i = 0; i < p->datasz; i++) {
780 d = &p->datas[i];
781 switch (d->stype) {
782 case (KPLOTS_SINGLE):
783 switch (d->types[0]) {
784 case (KPLOT_POINTS):
785 kplotctx_draw_points(&ctx, d);
786 break;
787 case (KPLOT_MARKS):
788 kplotctx_draw_marks(&ctx, d);
789 break;
790 case (KPLOT_LINES):
791 kplotctx_draw_lines(&ctx, d);
792 break;
793 case (KPLOT_LINESPOINTS):
794 kplotctx_draw_points(&ctx, d);
795 kplotctx_draw_lines(&ctx, d);
796 break;
797 case (KPLOT_LINESMARKS):
798 kplotctx_draw_marks(&ctx, d);
799 kplotctx_draw_lines(&ctx, d);
800 break;
801 default:
802 abort();
803 break;
804 }
805 break;
806 case (KPLOTS_YERRORBAR):
807 case (KPLOTS_YERRORLINE):
808 start = kplotctx_draw_yerrline_start
809 (&ctx, d, &end);
810 if (start == end)
811 break;
812 assert(d->datasz > 1);
813 switch (d->types[0]) {
814 case (KPLOT_POINTS):
815 kplotctx_draw_yerrline_basepoints
816 (&ctx, start, end, d);
817 break;
818 case (KPLOT_MARKS):
819 kplotctx_draw_yerrline_basemarks
820 (&ctx, start, end, d);
821 break;
822 case (KPLOT_LINES):
823 kplotctx_draw_yerrline_baselines
824 (&ctx, start, end, d);
825 break;
826 case (KPLOT_LINESPOINTS):
827 kplotctx_draw_yerrline_basepoints
828 (&ctx, start, end, d);
829 kplotctx_draw_yerrline_baselines
830 (&ctx, start, end, d);
831 break;
832 case (KPLOT_LINESMARKS):
833 kplotctx_draw_yerrline_basemarks
834 (&ctx, start, end, d);
835 kplotctx_draw_yerrline_baselines
836 (&ctx, start, end, d);
837 break;
838 default:
839 abort();
840 break;
841 }
842 switch (p->datas[i].types[1]) {
843 case (KPLOT_POINTS):
844 kplotctx_draw_yerrline_pairpoints
845 (&ctx, start, end, d);
846 break;
847 case (KPLOT_MARKS):
848 kplotctx_draw_yerrline_pairmarks
849 (&ctx, start, end, d);
850 break;
851 case (KPLOT_LINES):
852 kplotctx_draw_yerrline_pairlines
853 (&ctx, start, end, d);
854 break;
855 case (KPLOT_LINESPOINTS):
856 kplotctx_draw_yerrline_pairpoints
857 (&ctx, start, end, d);
858 kplotctx_draw_yerrline_pairlines
859 (&ctx, start, end, d);
860 break;
861 case (KPLOT_LINESMARKS):
862 kplotctx_draw_yerrline_pairmarks
863 (&ctx, start, end, d);
864 kplotctx_draw_yerrline_pairlines
865 (&ctx, start, end, d);
866 break;
867 default:
868 abort();
869 break;
870 }
871 if (KPLOTS_YERRORBAR == d->stype)
872 kplotctx_draw_yerrline_pairbars
873 (&ctx, start, end, d);
874 break;
875 default:
876 break;
877 }
878 }
879 }
880
881 int
kplotcfg_default_palette(struct kplotccfg ** pp,size_t * szp)882 kplotcfg_default_palette(struct kplotccfg **pp, size_t *szp)
883 {
884 size_t i;
885
886 *szp = 7;
887 if (NULL == (*pp = calloc(*szp, sizeof(struct kplotccfg))))
888 return(0);
889
890 for (i = 0; i < *szp; i++) {
891 (*pp)[i].type = KPLOTCTYPE_RGBA;
892 (*pp)[i].rgba[3] = 1.0;
893 }
894
895 (*pp)[0].rgba[0] = 0x94 / 255.0;
896 (*pp)[0].rgba[1] = 0x04 / 255.0;
897 (*pp)[0].rgba[2] = 0xd3 / 255.0;
898 (*pp)[1].rgba[0] = 0x00 / 255.0;
899 (*pp)[1].rgba[1] = 0x9e / 255.0;
900 (*pp)[1].rgba[2] = 0x73 / 255.0;
901 (*pp)[2].rgba[0] = 0x56 / 255.0;
902 (*pp)[2].rgba[1] = 0xb4 / 255.0;
903 (*pp)[2].rgba[2] = 0xe9 / 255.0;
904 (*pp)[3].rgba[0] = 0xe6 / 255.0;
905 (*pp)[3].rgba[1] = 0x9f / 255.0;
906 (*pp)[3].rgba[2] = 0x00 / 255.0;
907 (*pp)[4].rgba[0] = 0xf0 / 255.0;
908 (*pp)[4].rgba[1] = 0xe4 / 255.0;
909 (*pp)[4].rgba[2] = 0x42 / 255.0;
910 (*pp)[5].rgba[0] = 0x00 / 255.0;
911 (*pp)[5].rgba[1] = 0x72 / 255.0;
912 (*pp)[5].rgba[2] = 0xb2 / 255.0;
913 (*pp)[6].rgba[0] = 0xe5 / 255.0;
914 (*pp)[6].rgba[1] = 0x1e / 255.0;
915 (*pp)[6].rgba[2] = 0x10 / 255.0;
916
917 return(1);
918 }
919
920 double
get_dimx()921 get_dimx()
922 {
923 return dimx;
924 }
925
926 double
get_dimy()927 get_dimy()
928 {
929 return dimy;
930 }
931
932 double
get_offsx()933 get_offsx()
934 {
935 return offsx;
936 }
937
938 double
get_offsy()939 get_offsy()
940 {
941 return offsy;
942 }
943