aboutsummaryrefslogblamecommitdiffstats
path: root/rtgrpblib_draw_linear_bezier.c
blob: a2af58f86bdc4b6248f24836f47699b4bc75fd30 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                                                 
 




                                                                     




                              
 


                     











                                                                           
                               





                                             
                             


                                             
                             
         
                                

                                             

                                       

                                             
                             











                                                        

                                                                  


                                 
                                                                                                




                                            
                                                                     

                       
                             


                                            
                                                                     

                       
                             



                                            
                                                                                                   

                       
                             


                                            
                                                                                                   

                       
                             



                                                  
                  

                                                                  
                                                                   
                
                                                                                           
         

                 





     



                         
                             
           























                                                                           

                 
 

                                                             



                                               

                                                                
 
                 
 
                                   

                                                                                                      





                                                  









                                                                                    










                                                


          









                                                    
                                       



                                   


                                                         
                                                         
                                                         




                                                         
                                                         
                                                         
                                                         

                                                         




                        



      
/* 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