static struct point lerp_points(struct point points[2], double factor) { struct point delta; delta.x = points[1].x - points[0].x; delta.y = points[1].y - points[0].y; delta.x *= factor; delta.y *= factor; return (struct point){.x = points[0].x + delta.x, .y = points[0].y + delta.y}; } static struct point bezier_point(struct point *points, int count, double factor) { struct point retval = points[0]; if (count < 2) return retval; if (count == 2) return lerp_points(points, factor); struct point newpoints[count - 1]; for (int i = 0; i < count - 1; i++) newpoints[i] = lerp_points(points + i, factor); return bezier_point(newpoints, count - 1, factor); } void plot_bezier(struct point *points, int count, colour_t *buff, uint32_t width, uint32_t height, colour_t colour) { struct point last = points[0]; for (double factor = 0; factor < 1; factor += step) { struct point next = bezier_point(points, count, factor); uint32_t x = next.x, y = next.y; if (x > width) x = width; if (y > height) y = height; buff[x + width * y] = colour; } }
static void vd_flatten(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) { #ifdef DEBUG double flat = 0.5; double d2x0 = (p0x - 2 * p1x + p2x), d2y0 = (p0y - 2 * p1y + p2y); double d2x1 = (p1x - 2 * p2x + p3x), d2y1 = (p1y - 2 * p2y + p3y); double d2norm0 = hypot(d2x0, d2y0); double d2norm1 = hypot(d2x1, d2y1); double D = max(d2norm0, d2norm1); /* This is half of maximum norm of 2nd derivative of the curve by parameter t. */ int NN = (int)ceil(sqrt(D * 3 / 4 / flat)); /* Number of output segments. */ int i; int N = max(NN, 1); /* safety (if the curve degenerates to line) */ double e = 0.5 / N; for (i = 0; i < N; i++) { double t = (double)i / N + e; double px = bezier_point(p0x, p1x, p2x, p3x, t); double py = bezier_point(p0y, p1y, p2y, p3y, t); vd_lineto(px, py); } vd_lineto(p3x, p3y); #endif }
void bezier_do_action(VALUE points, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { float u = 0.0; action_struct cur; float x1, y1, x2, y2; int first_x, first_y; int format; int num_point_pairs; bool closed = false; VALUE offset_val; int draw_offset_x, draw_offset_y; /* defaults to 200 (1 / 0.005) samples per curve */ float step_size = 0.005; draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload); /* calculate offset */ offset_val = get_image_local(tex->image, DRAW_OFFSET); draw_offset_x = NUM2INT(get_from_array(offset_val, 0)); draw_offset_y = NUM2INT(get_from_array(offset_val, 1)); if(is_a_hash(hash_arg)) { /* if the polyline is 'closed' make the last point the first */ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) { /* so that our additional point is not persistent */ points = rb_obj_dup(points); closed = true; } /* number of points to sample */ if(RTEST(get_from_hash(hash_arg, "sample_size"))) { VALUE c = get_from_hash(hash_arg, "sample_size"); Check_Type(c, T_FIXNUM); step_size = 1.0 / (float)FIX2INT(c); } } if(is_a_point(get_from_array(points, 0))) { format = POINT_FORMAT; if(closed) rb_ary_push(points, get_from_array(points, 0)); num_point_pairs = RARRAY_LEN(points); } else { format = SIMPLE_FORMAT; /* ensure points are given in pairs */ if(RARRAY_LEN(points) % 2) rb_raise(rb_eArgError, "bezier needs an even number of points. got %d\n", (int)RARRAY_LEN(points)); if(closed) { rb_ary_push(points, get_from_array(points, 0)); rb_ary_push(points, get_from_array(points, 1)); } num_point_pairs = RARRAY_LEN(points) / 2; } if(num_point_pairs > 17) rb_raise(rb_eArgError, "too many points for bezier curve. 17 points is current maximum. got %d\n", num_point_pairs); /* get the first point */ bezier_point(points, 0, &x1, &y1, num_point_pairs, format, draw_offset_x, draw_offset_y); /* save it so we can link up with last point properly if the curve is 'closed' */ first_x = x1; first_y = y1; while(u <= 1) { bezier_point(points, u, &x2, &y2, num_point_pairs, format, draw_offset_x, draw_offset_y); line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload); /* update drawing rectangle */ update_bounds(payload, x1, y1, x2, y2); x1 = x2; y1 = y2; u += step_size; } /* sometimes beziers dont close properly, so we'll ensure it's closed */ if(closed) line_do_action(x2, y2, first_x, first_y, tex, hash_arg, no_sync, false, payload); draw_epilogue(&cur, tex, primary); }