Files

317 lines
7.6 KiB
C
Raw Permalink Normal View History

2024-01-06 13:43:22 -07:00
#include "planner.h"
#include "../gfx/gfx.h"
#include "../include/SDL.h"
#include "math/bezier.h"
#include "math/mathutils.h"
#include "motion.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define SELECTED state.paths.arr[state.paths.selected]
#define STATE_FIELD_RESCALED state.field.w_meters, state.field.h_meters, 0, 0
static PlannerState state;
static BezierPath bezier = {
.arr = NULL,
.cap = 51,
.len = 0,
.selected = -1,
};
const point START_POSITIONS[6] = {
[RED_ONE] = {14.5, 3.6},
[RED_TWO] = {14.5, 5.5},
[RED_THREE] = {14.5, 7.0},
[BLUE_ONE] = {2.0, 3.6},
[BLUE_TWO] = {2.0, 5.5},
[BLUE_THREE] = {2.0, 7.0},
};
static void build_bounding_box(MultiPath *path) {
rect box = {
state.field.startx,
state.field.starty,
state.field.startx,
state.field.starty
};
if (path->type == PATH_MOTION) {
printf("motion.len: %d\n", path->motion.len);
for (int i = 0; i < path->motion.len; i++) {
double x = path->motion.odo_path[i].x;
double y = path->motion.odo_path[i].y;
if (x < box.x) box.x = x;
if (y < box.y) box.y = y;
if (x > box.w) box.w = x;
if (y > box.h) box.h = y;
}
} else {
for (int i = 0; i < path->bezier.len; i += 2) {
point prev = path->bezier.arr[i];
for (float j = 0; j < 1.f; j += RES) {
point percent_a = lerp_point(path->bezier.arr[i],
path->bezier.arr[i + 1], j);
point percent_b = lerp_point(path->bezier.arr[i + 1],
path->bezier.arr[i + 2], j);
point curve = lerp_point(percent_a, percent_b, j);
if (curve.x < box.x) box.x = curve.x;
if (curve.y < box.y) box.y = curve.y;
if (curve.x > box.w) box.w = curve.x;
if (curve.y > box.h) box.h = curve.y;
prev = curve;
}
}
}
point ll = scale_to_screen(box.x, box.y, STATE_FIELD_RESCALED);
point rr = scale_to_screen(box.w - box.x, box.h - box.y, STATE_FIELD_RESCALED);
path->bounding_box = (rect) {ll.x, ll.y, rr.x, rr.y};
}
void init_planner(StartPos start) {
const size_t path_arr_size = sizeof(MotionPath) * 10;
state = (PlannerState) {
.field = {
.startx = START_POSITIONS[start].x, // 8.25,
.starty = START_POSITIONS[start].y, // 4.05,
.w_meters = 16.5,
.h_meters = 8.1,
.xy_speed = 0.8, //4.8,
.rot_speed = 0,
},
.zoom_level = 1,
.x_offset = 0,
.y_offset = 0,
.paths = {
.cap = 4,
.len = 0,
.arr = malloc(path_arr_size),
.selected = 0,
},
.mode = MODE_EDIT,
};
memset(state.paths.arr, 0, path_arr_size);
if (state.paths.arr == NULL) {
printf("could not allocate MotionPaths");
exit(1);
}
const size_t bezier_size = sizeof(point) * bezier.cap;
bezier.arr = malloc(bezier_size);
memset(bezier.arr, 0, bezier_size);
bezier.arr[bezier.len++] = (point) {
state.field.startx,
state.field.starty
};
bezier.arr[bezier.len++] = (point) {
state.field.startx + 1,
state.field.starty
};
bezier.arr[bezier.len++] = (point) {
state.field.startx + 2,
state.field.starty
};
state.paths.arr[state.paths.len++] = WRAP_BEZIER(bezier);
state.paths.arr[state.paths.len++] =
WRAP_MOTION(load_path("bezier.txt", &state.field));
for (int i = 0; i < state.paths.len; i++)
build_bounding_box(&state.paths.arr[i]);
}
static void path_playback(bool start, MotionPath *path) {
static clock_t start_time = 0;
static int index = 1;
if (start) {
start_time = clock();
index = 1;
}
clock_t playback_time = clock() - start_time;
while (index < path->len && path->points[index].time < playback_time) {
index++;
}
if (index == path->len) {
state.mode = MODE_EDIT;
return;
}
double x_min = path->odo_path[index - 1].x;
double x_max = path->odo_path[index ].x;
double y_min = path->odo_path[index - 1].y;
double y_max = path->odo_path[index ].y;
double lerp_time =
((double) (path->points[index].time - path->points[index-1].time)) /
((double) playback_time);
point robot_pos = {
x_min + lerp_time * (x_max - x_min),
y_min + lerp_time * (y_max - y_min),
};
draw_robot(&state.field, robot_pos);
}
static void bezier_playback(bool start, BezierPath *path) {
}
// TODO: use BezierPath & port to bezier module
static void deselect_bez_point(MultiPath *path) {
if (path->type == PATH_MOTION) return;
path->bezier.selected = -1;
build_bounding_box(path);
}
static void select_path(int x, int y) {
point mouse = { x, y };
for (int i = 0; i < state.paths.len; i++) {
if (within_box(mouse, state.paths.arr[i].bounding_box)) {
state.paths.selected = i;
return;
}
}
}
static void draw_correct_path(MultiPath *path, bool bold, bool select) {
if (path->type == PATH_MOTION)
draw_path(&path->motion, &state.field, bold, select);
else
draw_bezier(&path->bezier, &state.field, bold, select);
}
static void draw_paths(int x, int y) {
point mouse = { x, y };
for (int i = 0; i < state.paths.len && i < 2; i++) {
if (state.paths.selected == i) {
draw_correct_path(&state.paths.arr[i], true, true);
continue;
}
if (within_box(mouse, state.paths.arr[i].bounding_box))
draw_correct_path(&state.paths.arr[i], true, false);
else
draw_correct_path(&state.paths.arr[i], false, false);
}
}
void save_correct_format(MultiPath *path) {
if (path->type == PATH_MOTION)
save_path(&path->motion);
else
save_bezier(&path->bezier, "bezier.txt", state.field.xy_speed);
}
void planner_loop(SDL_Event *e) {
int x, y;
SDL_GetMouseState(&x, &y);
while (SDL_PollEvent(e)) {
switch (e->type) {
case SDL_QUIT:
// FIXME: crashing the program to exit is rather ungraceful.
// I should make a cleanup procedure at some point.
exit(0);
break;
case SDL_MOUSEBUTTONDOWN:
if (SELECTED.type == PATH_BEZIER &&
select_bez_point(x, y, &SELECTED.bezier, &state.field)) break;
select_path(x, y);
break;
case SDL_MOUSEBUTTONUP:
deselect_bez_point(&SELECTED);
break;
case SDL_KEYDOWN:
switch(e->key.keysym.scancode) {
case SDL_SCANCODE_P:
if (state.paths.selected >= 0 && SELECTED.type == PATH_MOTION) {
path_playback(true, &SELECTED.motion);
state.mode = MODE_PLAYBACK;
}
break;
case SDL_SCANCODE_A:
if (state.paths.selected >= 0 && SELECTED.type == PATH_BEZIER) {
if (bezier.len >= bezier.cap) break;
for (int i = 0; i < 2; i++) {
SELECTED.bezier.arr[SELECTED.bezier.len++] = (point) {
.x = state.field.startx,
.y = state.field.starty,
};
}
}
break;
case SDL_SCANCODE_S:
save_correct_format(&SELECTED);
break;
case SDL_SCANCODE_D:
printf("(%f, %f)\n", bezier.arr[0].x, bezier.arr[0].y);
break;
default:
break;
}
break;
}
}
draw_field(&state.field);
switch (state.mode) {
case MODE_EDIT:
if (SELECTED.type == PATH_BEZIER)
move_point(x, y, &SELECTED.bezier, &state.field);
draw_paths(x, y);
draw_robot(&state.field, (point) { state.field.startx, state.field.starty });
break;
case MODE_PLAYBACK:
DRAW_PATH_SELECT(&SELECTED.motion, &state.field);
path_playback(false, &SELECTED.motion);
break;
case MODE_RECORD:
DRAW_PATH_SELECT(&SELECTED.motion, &state.field);
draw_robot(&state.field, (point) {
SELECTED.motion.odo_path[SELECTED.motion.len - 1].x,
SELECTED.motion.odo_path[SELECTED.motion.len - 1].y,
});
break;
}
blit_screen();
}