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