aboutsummaryrefslogtreecommitdiffstats
path: root/draw_linear_bezier_reference.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--draw_linear_bezier_reference.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/draw_linear_bezier_reference.c b/draw_linear_bezier_reference.c
new file mode 100644
index 0000000..6066946
--- /dev/null
+++ b/draw_linear_bezier_reference.c
@@ -0,0 +1,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