#include "bezier.h" #include "mathutils.h" #include "../motion.h" #include #include #include #include #include #include BezierPath make_bezier_path(int n, point bezier[n]) { BezierPath path = { .arr = malloc(sizeof(point) * n), .cap = n, .len = n, }; memcpy(path.arr, bezier, sizeof(point) * n); return path; } void free_bezier(BezierPath *path) { free(path->arr); *path = (BezierPath) { .arr = NULL, .cap = 0, .len = 0, .selected = -1, }; } double get_bez_distance(BezierPath *bezier, double res) { double dist = 0; for (int i = 0; i < bezier->len; i += 2) { point prev = bezier->arr[i]; for (float j = 0; j < 1.f; j += res) { point percent_a = lerp_point(bezier->arr[i ], bezier->arr[i + 1], j); point percent_b = lerp_point(bezier->arr[i + 1], bezier->arr[i + 2], j); point curve = lerp_point(percent_a, percent_b, j); dist += get_distance(prev, curve); prev = curve; } } return dist; } bool select_bez_point(int x, int y, BezierPath *path, Field *field) { for (int i = 0; i < path->len; i++) { point node = scale_to_screen( path->arr[i].x, path->arr[i].y, FIELD_RESCALED); rect handle = { node.x - 5, node.y - 5, 10, 10 }; if (within_box((point) { x, y }, handle)) { path->selected = i; return true; } } return false; } void move_point(int x, int y, BezierPath *path, Field *field) { if (path->selected == -1) return; path->arr[path->selected] = scale_to_field(x, y, FIELD_RESCALED); } #define DIST 0.01 #define DIST_SQ (DIST * DIST) /* fuck how do I do this shit? * the issue is traveling a consistent distance along the curve * instead of a percentage of each bezier. * * this is a very slow implementation of `arc length parameterization` * * I don't factor in ramping, which could lead to some problems */ void save_bezier(BezierPath *path, const char *filename, double speed) { if (speed < DIST) { printf("bezier cannot be exported with speed %f\n", speed); return; } FILE *file; fopen_s(&file, filename, "w+"); fprintf(file, "%f,%f,%f,%f,%d\n", 0.f, 0.f, 0.f, 0.f, 0); uint32_t time_ms = 0; for (int i = 0; i < path->len-1; i += 2) { point prev = path->arr[i]; for (float j = 0; j < 1.f; j += 0.001) { point percent_a = lerp_point(path->arr[i ], path->arr[i + 1], j); point percent_b = lerp_point(path->arr[i + 1], path->arr[i + 2], j); point curve = lerp_point(percent_a, percent_b, j); double dist_sq = (prev.x - curve.x) * (prev.x - curve.x) + (prev.y - curve.y) * (prev.y - curve.y); if (dist_sq > DIST_SQ) { // are the component distances correct or should I apply normalization? double dist = sqrt(dist_sq); double dist_x = curve.x - prev.x; double dist_y = curve.y - prev.y; // does this make sense? am i going insane? double time_sec = dist / speed; double left_x = (dist_y / time_sec) / speed; double left_y = (dist_x / time_sec) / speed; time_ms += (uint32_t) (time_sec * 1000); fprintf(file, "%f,%f,%f,%f,%d\n", left_x, left_y, 0.f, 0.f, time_ms); prev = curve; } } } fclose(file); printf("saved bezier path %s\n", filename); }