/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifndef TEST
void
rtgrpblib_draw_linear_bezier(RASTER *restrict raster, double x1, double y1, double x2, double y2)
{
double w, h, dx, dy, x, y;
if (raster->draftness <= 0) {
draw_linear_bezier_reference(raster, x1, y1, x2, y2);
return;
}
#define CALC_SLOPE()\
do {\
dx = x2 - x1;\
dy = y2 - y1;\
} while (0)
CALC_SLOPE();
if (!dy) {
/* For horizontal lines, it is enough that we have the
* corners mapped onto the raster, which we must have
* since a glyph cannot just contain a line, but outlines:
* in `fill`, the line will not make a contribution because
* `.opposite_coverage` for each cell will be zero, and
* consequentially, `.cell_coverage` will also be zero */
return;
}
/* We cut of everything above and below the raster,
* as these do not contribute to the result */
h = (double)raster->height;
if (y1 <= 0 && y2 <= 0)
return;
if (y1 >= h && y2 >= h)
return;
if (y1 < 0 && y2 >= 0) {
x1 = x1 + (0 - y1) * dx / dy;
y1 = 0;
CALC_SLOPE();
} else if (y2 < 0 && y1 >= 0) {
x2 = x1 + (0 - y1) * dx / dy;
y2 = 0;
CALC_SLOPE();
}
if (y1 <= h && y2 > h) {
x2 = x1 + (h - y1) * dx / dy;
y2 = h;
CALC_SLOPE();
} else if (y2 <= h && y1 > h) {
x1 = x1 + (h - y1) * dx / dy;
y1 = h;
CALC_SLOPE();
}
/* Dealing with the left and the right section
* is not as simple as `fill` assumes that the
* sum of each line's `.opposite_coverage` is 0.
* Therefore must must identify the parts of the
* line that are left of the raster, right of
* the raster, and inside the raster. The parts
* that are outside the raster are move to the
* edge, or rather their show on the edges are
* used. */
w = (double)raster->width;
if (x1 < 0 && x2 < 0) {
draw_vertical_line(raster, 0, y1, y2, SIGNUM(dy));
return;
}
if (x1 >= w && x2 >= w) {
draw_vertical_line_opposite_only(raster, raster->width - 1, y1, y2, SIGNUM(dy));
return;
}
if (x1 < 0 && x2 >= 0) {
y = y1 + (0 - x1) * dy / dx;
x = 0;
draw_vertical_line(raster, 0, y1, y, SIGNUM(y - y1));
x1 = x;
y1 = y;
CALC_SLOPE();
} else if (x2 < 0 && x1 >= 0) {
y = y1 + (0 - x1) * dy / dx;
x = 0;
draw_vertical_line(raster, 0, y, y2, SIGNUM(y2 - y));
x2 = x;
y2 = y;
CALC_SLOPE();
}
if (x1 < w && x2 >= w) {
y = y1 + (w - x1) * dy / dx;
x = w;
draw_vertical_line_opposite_only(raster, raster->width - 1, y, y2, SIGNUM(y2 - y));
x2 = x;
y2 = y;
CALC_SLOPE();
} else if (x2 < w && x1 >= w) {
y = y1 + (w - x1) * dy / dx;
x = w;
draw_vertical_line_opposite_only(raster, raster->width - 1, y1, y, SIGNUM(y - y1));
x1 = x;
y1 = y;
CALC_SLOPE();
}
/* Now we can finally draw the part of the
* line that is inside the raster */
if (!dx) {
/* Optimisation for vertical lines. It also serves
* to illustrate how the algorithm is designed. */
draw_vertical_line(raster, x1, y1, y2, SIGNUM(dy));
} else {
draw_diagonal_line(raster, x1, y1, x2, y2, dx, dy, SIGNUM(dx), SIGNUM(dy));
}
#undef CALC_SLOPE
}
#else
static RASTER *raster;
static RASTER *refraster;
#ifdef ROWWISE_RESET_INKLEVEL
static void
draw_right_edge_shadow(double x1, double y1, double x2, double y2)
{
if (raster->draftness <= 0)
return;
if (x1 >= 10.0 && x2 >= 10.0) {
draw_linear_bezier_reference(raster, x1, y1, x2, y2);
} else if (x1 >= 10.0) {
y2 = y2 + (10.0 - x2) * (y2 - y1) / (x2 - x1);
x2 = 10.0;
draw_linear_bezier_reference(raster, x1, y1, x2, y2);
} else if (x2 >= 10.0) {
y1 = y1 + (10.0 - x1) * (y2 - y1) / (x2 - x1);
x1 = 10.0;
draw_linear_bezier_reference(raster, x1, y1, x2, y2);
}
}
#endif
static void
proper_check_draw_linear_bezier(double x1, double y1, double x2, double y2)
{
size_t i;
alarm(5);
rtgrpblib_draw_linear_bezier(raster, x1, y1, x2, y2);
#ifdef ROWWISE_RESET_INKLEVEL
draw_right_edge_shadow(x1, y1, x2, y2);
#endif
alarm(5);
draw_linear_bezier_reference(refraster, x1, y1, x2, y2);
alarm(0);
for (i = 0; i < 100; i++) {
ASSERT(eq(raster->cells[i].cell_coverage, refraster->cells[i].cell_coverage));
ASSERT(eq(raster->cells[i].opposite_coverage, refraster->cells[i].opposite_coverage));
}
rtgrpblib_reset_raster(raster, 10, 10);
rtgrpblib_reset_raster(refraster, 10, 10);
}
static void
check_draw_linear_bezier(double x1, double y1, double x2, double y2)
{
proper_check_draw_linear_bezier(floor(x1), floor(y1), floor(x2), floor(y2));
proper_check_draw_linear_bezier(floor(x1), y1, floor(x2), y2);
proper_check_draw_linear_bezier(x1, floor(y1), x2, floor(y2));
proper_check_draw_linear_bezier(x1, y1, x2, y2);
}
static double
frand(double a, double b)
{
int i = rand();
double f = (double)i / (double)RAND_MAX;
double max = fmax(a, b);
double min = fmin(a, b);
return fma(f, max - min, min);
}
int
main(void)
{
double x1, x2;
double y1, y2;
size_t i;
raster = rtgrpblib_create_raster(10, 10);
ASSERT(raster);
refraster = rtgrpblib_create_raster(10, 10);
ASSERT(refraster);
srand((unsigned)(uintptr_t)raster);
for (i = 0; i < 50000UL; i++) {
x1 = frand(-5, 15);
y1 = frand(-5, 15);
x2 = frand(-5, 15);
y2 = frand(-5, 15);
check_draw_linear_bezier(x1, y1, x2, y2);
check_draw_linear_bezier(x1, x1, x2, x2);
check_draw_linear_bezier(x1, y1, x2, y1);
check_draw_linear_bezier(x1, x2, x2, x1);
check_draw_linear_bezier(x1, y1, x1, y2);
check_draw_linear_bezier(x1, x1, x1, x1);
raster->draftness = -1;
check_draw_linear_bezier(x1, y1, x2, y2);
check_draw_linear_bezier(x1, x1, x2, x2);
check_draw_linear_bezier(x1, y1, x2, y1);
check_draw_linear_bezier(x1, x2, x2, x1);
check_draw_linear_bezier(x1, y1, x1, y2);
check_draw_linear_bezier(x1, x1, x1, x1);
raster->draftness = 0.5;
}
free(raster);
free(refraster);
return 0;
}
#endif