aboutsummaryrefslogtreecommitdiffstats
path: root/draw_linear_bezier_reference.c
blob: 60669461aa35bf9cefa4e83dd8f83d49675fbbe8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifndef TEST

/*
 * The implementation in this fill is not supposed to be fast,
 * it is supposed to be absolutely correct. The implementation
 * shall be as clear and obvious as possible. It is important
 * that everything written to cells is as precise as possible
 * and that the function behavious correctly when outside the
 * raster. This implementation shall be usable to generate an
 * as perfect as possible rasterisation and shall be usable to
 * test the correctness and accuracy of the fast implementation.
 */


void
draw_linear_bezier_reference(RASTER *restrict raster, double x1, double y1, double x2, double y2)
{
	double dx = x2 - x1;
	double dy = y2 - y1;
	int xdir = SIGNUM(dx);
	int ydir = SIGNUM(dy);
	double kx = dx / dy;
	double ky = dy / dx;
	double prevX, prevY, x, y, cx, cy, h2x, h2y;
	double xfloor, yfloor;
	ssize_t cellX, cellY;
	size_t cell;

	prevX = x1;
	prevY = y1;

	for (; prevX != x2 && prevY != y2; prevX = x, prevY = y) {
		/* Find next vertical and next hozitonal grid line */
		x = xdir < 0 ? floor(prevX) : xdir > 0 ? ceil(prevX) : prevX;
		y = ydir < 0 ? floor(prevY) : ydir > 0 ? ceil(prevY) : prevY;
		x = x == prevX ? x + xdir : x;
		y = y == prevY ? y + ydir : y;

		/* Limit to the extend of the drawn line segment */
		if ((xdir < 0 && x < x2) || (xdir > 0 && x > x2))
			x = x2;
		if ((ydir < 0 && y < y2) || (ydir > 0 && y > y2))
			y = y2;

		/* Find next cell edge */
		if (xdir && ydir) {
			cy = y1 + (x - x1) * ky;
			cx = x1 + (y - y1) * kx;
			h2x = (cy - prevY) * (cy - prevY) + (x - prevX) * (x - prevX);
			h2y = (cx - prevX) * (cx - prevX) + (y - prevY) * (y - prevY);
			if (h2x < h2y)
				y = cy;
			else
				x = cx;

			/* Make sure loop will exit when we reach the end */
			if ((xdir < 0 && x < x2) || (xdir > 0 && x > x2))
				x = x2;
			if ((ydir < 0 && y < y2) || (ydir > 0 && y > y2))
				y = y2;
		}

		/* Select cell to draw in */
		xfloor = xdir >= 0 ? floor(prevX) : floor(x);
		yfloor = ydir >= 0 ? floor(prevY) : floor(y);
		cellX = (ssize_t)xfloor;
		cellY = (ssize_t)yfloor;

		/* Do not draw if above or below the raster*/
		if (cellY < 0 || (size_t)cellY >= raster->height)
			continue;

		if (cellX < 0) {
			/* Draw on first column in raster of outside of raster on the left side,
			 * with full horizontal coverage, `.opposite_coverage` one the actual
			 * cell (outside of the raster) will contribute to the first column's
			 * `.cell_coverage` */
			cell = (size_t)cellY * raster->width;
			raster->cells[cell].opposite_coverage += y - prevY;
			raster->cells[cell].cell_coverage += y - prevY;

		} else if ((size_t)cellX >= raster->width) {
			/* If outside of the raster, on the right side, add to the last
			 * columns contributions to the next column, so that the ink level
			 * balances out to zero */
			cell = (size_t)cellY * raster->width + (raster->width - 1);
			raster->cells[cell].opposite_coverage += y - prevY;

		} else {
			/* If inside the raster, just draw the line */
			cell = (size_t)cellY * raster->width + (size_t)cellX;
			raster->cells[cell].opposite_coverage += y - prevY;
			raster->cells[cell].cell_coverage += (y - prevY) * fabs(fmax(ceil(x), ceil(prevX)) - (x + prevX) / 2.0);
		}
	}
}


#else


int
main(void)
{
	return 0; /* TODO add test */
}


#endif