initial
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
6
compile_flags.txt
Normal file
6
compile_flags.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
-xc
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-c23-extensions
|
||||
-I/usr/lib/plan9/include
|
||||
-DIN_EDITOR
|
||||
14
mkfile
Normal file
14
mkfile
Normal file
@@ -0,0 +1,14 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
src=`{ls src/*.c >[2] /dev/null}
|
||||
hdr=`{ls src/*.h >[2] /dev/null}
|
||||
obj=${src:src/%.c=build/obj/%.$O}
|
||||
|
||||
build/drawer: $obj
|
||||
$LD -o $target $prereq
|
||||
|
||||
build/obj/%.$O: src/%.c $hdr mkfile structure
|
||||
$CC $CFLAGS -o $target src/$stem.c
|
||||
|
||||
structure:V:
|
||||
mkdir -p build/obj
|
||||
598
src/main.c
Normal file
598
src/main.c
Normal file
@@ -0,0 +1,598 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <draw.h>
|
||||
#include <event.h>
|
||||
#include <cursor.h>
|
||||
#include "str.h"
|
||||
|
||||
#define SELECT_COLOR DGreen
|
||||
|
||||
typedef struct Command Command;
|
||||
struct Command
|
||||
{
|
||||
const char *name;
|
||||
union
|
||||
{
|
||||
void (*no_args)(void);
|
||||
void (*many_args)(uintptr nargs, const char **args);
|
||||
void (*str_arg)(const char *arg);
|
||||
} fn;
|
||||
enum
|
||||
{
|
||||
CMD_NO_ARGS,
|
||||
CMD_STR_ARG
|
||||
} mode;
|
||||
};
|
||||
|
||||
typedef struct Draw_Point Draw_Point;
|
||||
struct Draw_Point
|
||||
{
|
||||
uintptr rc;
|
||||
float x, y;
|
||||
};
|
||||
|
||||
typedef struct Draw_Line Draw_Line;
|
||||
struct Draw_Line
|
||||
{
|
||||
uintptr point_a, point_b;
|
||||
u8int active;
|
||||
u8int selected;
|
||||
};
|
||||
|
||||
typedef struct Canvas Canvas;
|
||||
struct Canvas
|
||||
{
|
||||
struct
|
||||
{
|
||||
Draw_Point *contents;
|
||||
uintptr capacity;
|
||||
} points;
|
||||
struct
|
||||
{
|
||||
Draw_Line *contents;
|
||||
uintptr capacity;
|
||||
} lines;
|
||||
struct
|
||||
{
|
||||
uintptr *ids;
|
||||
uintptr capacity;
|
||||
uintptr length;
|
||||
} selection;
|
||||
};
|
||||
|
||||
typedef struct State State;
|
||||
struct State
|
||||
{
|
||||
Str_Buf command;
|
||||
Str_Buf bottom_line;
|
||||
Canvas *active_canvas;
|
||||
Image *select_texture;
|
||||
struct
|
||||
{
|
||||
Point a, b;
|
||||
} select_window;
|
||||
struct
|
||||
{
|
||||
float x, y;
|
||||
} origin;
|
||||
float scale;
|
||||
float pixel_size;
|
||||
enum
|
||||
{
|
||||
ST_DRAW,
|
||||
ST_COMMAND,
|
||||
} state;
|
||||
u8int stopping;
|
||||
u8int selecting;
|
||||
};
|
||||
|
||||
static void calc_pixel_size(void);
|
||||
static Canvas *canvas_new(void);
|
||||
static void canvas_delete(Canvas *canvas);
|
||||
static void canvas_more_points(Canvas *canvas, uintptr n);
|
||||
static void canvas_mode_lines(Canvas *canvas, uintptr n);
|
||||
static uintptr canvas_add_line(Canvas *canvas, float x0, float y0, float x1, float y1);
|
||||
static void canvas_select(Canvas *canvas, float min_x, float min_y, float max_x, float max_y);
|
||||
static void canvas_desel(Canvas *canvas);
|
||||
static void canvas_del_lines(Canvas *canvas);
|
||||
static Draw_Line *canvas_get_line(Canvas *canvas, uintptr id);
|
||||
static Draw_Point *canvas_get_point(Canvas *canvas, uintptr id);
|
||||
static Point draw_point_screen_pos(Draw_Point p);
|
||||
static void canvas_draw(const Canvas *canvas);
|
||||
static void draw_bottom_line(void);
|
||||
static void run_cmd(void);
|
||||
static void cmd_exit(void);
|
||||
static void cmd_scale(const char *arg);
|
||||
static void cmd_del(void);
|
||||
static void cmd_join(void);
|
||||
|
||||
#define CMD_NO_ARGS(NAME, FN) { .name = (NAME), .fn = { .no_args = (FN) }, .mode = CMD_NO_ARGS }
|
||||
#define CMD_STR_ARG(NAME, FN) { .name = (NAME), .fn = { .str_arg = (FN) }, .mode = CMD_STR_ARG }
|
||||
static const Command cmds[] = {
|
||||
CMD_NO_ARGS("exit", cmd_exit),
|
||||
CMD_STR_ARG("scale", cmd_scale),
|
||||
CMD_NO_ARGS("del", cmd_del),
|
||||
CMD_NO_ARGS("join", cmd_join),
|
||||
};
|
||||
|
||||
static const Point nil_point = { 0, 0 };
|
||||
static const Rectangle unit_rect = {
|
||||
.min = { 0, 0 },
|
||||
.max = { 1, 1 }
|
||||
};
|
||||
static State state = {
|
||||
.origin = { 0.f, 0.f },
|
||||
.scale = 1.f
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
if(initdraw(nil, nil, "drawer") == -1)
|
||||
{
|
||||
fprint(2, "initdraw: %r\n");
|
||||
exits("initdraw");
|
||||
}
|
||||
einit(Emouse | Ekeyboard);
|
||||
|
||||
state.active_canvas = canvas_new();
|
||||
state.select_texture = allocimage(display, unit_rect, RGBA32, 1, SELECT_COLOR);
|
||||
calc_pixel_size();
|
||||
|
||||
uintptr current_line = 0;
|
||||
Point prev_point = nil_point;
|
||||
enum
|
||||
{
|
||||
DRAW_IDLE,
|
||||
DRAW_DRAG,
|
||||
DRAW_ADD,
|
||||
DRAW_SEL,
|
||||
DRAW_MOVE
|
||||
} draw_mode = DRAW_IDLE;
|
||||
|
||||
while(!state.stopping)
|
||||
{
|
||||
Event e;
|
||||
switch(event(&e))
|
||||
{
|
||||
case Emouse:
|
||||
{
|
||||
float x = (e.mouse.xy.x - (float)(screen->r.min.x + screen->r.max.x) / 2.f) * state.pixel_size + state.origin.x;
|
||||
float y = (e.mouse.xy.y - (float)(screen->r.min.y + screen->r.max.y) / 2.f) * state.pixel_size + state.origin.y;
|
||||
if(state.state == ST_DRAW)
|
||||
{
|
||||
static char buf[128];
|
||||
sprint(buf, "x / y: %f / %f", x, y);
|
||||
str_buf_clear(&state.bottom_line);
|
||||
str_buf_append_cstr(&state.bottom_line, buf);
|
||||
}
|
||||
if(e.mouse.buttons == 1)
|
||||
{
|
||||
if(draw_mode == DRAW_ADD)
|
||||
{
|
||||
Draw_Point *p1 = canvas_get_point(state.active_canvas, canvas_get_line(state.active_canvas, current_line)->point_b);
|
||||
p1->x = x;
|
||||
p1->y = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_line = canvas_add_line(state.active_canvas, x, y, x, y);
|
||||
draw_mode = DRAW_ADD;
|
||||
}
|
||||
}
|
||||
else if(e.mouse.buttons == 2)
|
||||
{
|
||||
if(draw_mode == DRAW_DRAG)
|
||||
{
|
||||
state.origin.x -= (e.mouse.xy.x - prev_point.x) * state.pixel_size;
|
||||
state.origin.y -= (e.mouse.xy.y - prev_point.y) * state.pixel_size;
|
||||
}
|
||||
draw_mode = DRAW_DRAG;
|
||||
prev_point = e.mouse.xy;
|
||||
}
|
||||
else if(e.mouse.buttons == 4)
|
||||
{
|
||||
state.select_window.b = e.mouse.xy;
|
||||
if(draw_mode != DRAW_SEL)
|
||||
{
|
||||
state.select_window.a = e.mouse.xy;
|
||||
canvas_desel(state.active_canvas);
|
||||
draw_mode = DRAW_SEL;
|
||||
state.selecting = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(draw_mode == DRAW_SEL)
|
||||
{
|
||||
state.selecting = 0;
|
||||
float x1 = (state.select_window.a.x - (float)(screen->r.min.x + screen->r.max.x) / 2.f) * state.pixel_size + state.origin.x;
|
||||
float y1 = (state.select_window.a.y - (float)(screen->r.min.y + screen->r.max.y) / 2.f) * state.pixel_size + state.origin.y;
|
||||
float mix_x = x < x1 ? x : x1;
|
||||
float min_y = y < y1 ? y : y1;
|
||||
float max_x = x > x1 ? x : x1;
|
||||
float max_y = y > y1 ? y : y1;
|
||||
canvas_select(state.active_canvas, mix_x, min_y, max_x, max_y);
|
||||
}
|
||||
draw_mode = DRAW_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Ekeyboard:
|
||||
switch(state.state)
|
||||
{
|
||||
case ST_DRAW:
|
||||
switch(e.kbdc)
|
||||
{
|
||||
case ':':
|
||||
state.state = ST_COMMAND;
|
||||
str_buf_clear(&state.command);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ST_COMMAND:
|
||||
if(e.kbdc == '\n')
|
||||
{
|
||||
run_cmd();
|
||||
state.state = ST_DRAW;
|
||||
}
|
||||
else if(e.kbdc == 8) // backspace
|
||||
{
|
||||
if(state.command.length > 0)
|
||||
state.command.length--;
|
||||
else
|
||||
state.state = ST_DRAW;
|
||||
}
|
||||
else if(isprint(e.kbdc))
|
||||
str_buf_push_char(&state.command, e.kbdc);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
canvas_draw(state.active_canvas);
|
||||
draw_bottom_line();
|
||||
|
||||
// draw selection if needed
|
||||
if(state.selecting)
|
||||
{
|
||||
Point c = { state.select_window.a.x, state.select_window.b.y };
|
||||
Point d = { state.select_window.b.x, state.select_window.a.y };
|
||||
line(screen, state.select_window.a, c, Endsquare, Endsquare, 0, state.select_texture, nil_point);
|
||||
line(screen, state.select_window.a, d, Endsquare, Endsquare, 0, state.select_texture, nil_point);
|
||||
line(screen, state.select_window.b, c, Endsquare, Endsquare, 0, state.select_texture, nil_point);
|
||||
line(screen, state.select_window.b, d, Endsquare, Endsquare, 0, state.select_texture, nil_point);
|
||||
}
|
||||
}
|
||||
|
||||
freeimage(state.select_texture);
|
||||
canvas_delete(state.active_canvas);
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
void eresized(int new)
|
||||
{
|
||||
if(new)
|
||||
getwindow(display, Refbackup);
|
||||
calc_pixel_size();
|
||||
canvas_draw(state.active_canvas);
|
||||
draw_bottom_line();
|
||||
}
|
||||
|
||||
static void calc_pixel_size(void)
|
||||
{
|
||||
state.pixel_size = state.scale / ((float)(screen->r.max.y - screen->r.min.y) / 2.f);
|
||||
}
|
||||
|
||||
static Canvas *canvas_new(void)
|
||||
{
|
||||
Canvas *res = malloc(sizeof(Canvas));
|
||||
memset(res, 0, sizeof(Canvas));
|
||||
return res;
|
||||
}
|
||||
|
||||
static void canvas_delete(Canvas *canvas)
|
||||
{
|
||||
if(canvas->lines.contents)
|
||||
free(canvas->lines.contents);
|
||||
if(canvas->points.contents)
|
||||
free(canvas->points.contents);
|
||||
if(canvas->selection.ids)
|
||||
free(canvas->selection.ids);
|
||||
free(canvas);
|
||||
}
|
||||
|
||||
static void canvas_more_points(Canvas *canvas, uintptr n)
|
||||
{
|
||||
uintptr new_capacity = canvas->points.capacity ? canvas->points.capacity : 1;
|
||||
while(new_capacity < canvas->points.capacity + n)
|
||||
new_capacity *= 2;
|
||||
Draw_Point *new_points = malloc(sizeof(Draw_Point) * new_capacity);
|
||||
memcpy(new_points, canvas->points.contents, sizeof(Draw_Point) * canvas->points.capacity);
|
||||
for(uintptr i = canvas->points.capacity; i < new_capacity; i++)
|
||||
new_points[i].rc = 0;
|
||||
if(canvas->points.contents)
|
||||
free(canvas->points.contents);
|
||||
canvas->points.contents = new_points;
|
||||
canvas->points.capacity = new_capacity;
|
||||
}
|
||||
|
||||
static void canvas_mode_lines(Canvas *canvas, uintptr n)
|
||||
{
|
||||
uintptr new_capacity = canvas->lines.capacity ? canvas->lines.capacity : 1;
|
||||
while(new_capacity < canvas->lines.capacity + n)
|
||||
new_capacity *= 2;
|
||||
Draw_Line *new_lines = malloc(sizeof(Draw_Line) * new_capacity);
|
||||
memcpy(new_lines, canvas->lines.contents, sizeof(Draw_Line) * canvas->lines.capacity);
|
||||
for(uintptr i = canvas->lines.capacity; i < new_capacity; i++)
|
||||
new_lines[i].active = 0;
|
||||
if(canvas->lines.contents)
|
||||
free(canvas->lines.contents);
|
||||
canvas->lines.contents = new_lines;
|
||||
canvas->lines.capacity = new_capacity;
|
||||
}
|
||||
|
||||
static uintptr canvas_add_line(Canvas *canvas, float x0, float y0, float x1, float y1)
|
||||
{
|
||||
// create points
|
||||
uintptr point_a, point_b;
|
||||
uintptr pos = 0;
|
||||
while(pos < canvas->points.capacity && canvas->points.contents[pos].rc > 0)
|
||||
pos++;
|
||||
point_a = pos;
|
||||
if(pos == canvas->points.capacity)
|
||||
{
|
||||
canvas_more_points(canvas, 2);
|
||||
point_b = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos++;
|
||||
while(pos < canvas->points.capacity && canvas->points.contents[pos].rc > 0)
|
||||
pos++;
|
||||
if(pos == canvas->points.capacity)
|
||||
canvas_more_points(canvas, 1);
|
||||
point_b = pos;
|
||||
}
|
||||
|
||||
Draw_Point a = { 1, x0, y0 };
|
||||
Draw_Point b = { 1, x1, y1 };
|
||||
canvas->points.contents[point_a] = a;
|
||||
canvas->points.contents[point_b] = b;
|
||||
|
||||
uintptr line = 0;
|
||||
while(line < canvas->lines.capacity && canvas->lines.contents[line].active)
|
||||
line++;
|
||||
if(line == canvas->lines.capacity)
|
||||
canvas_mode_lines(canvas, 1);
|
||||
Draw_Line l = { point_a, point_b, 1, 0 };
|
||||
canvas->lines.contents[line] = l;
|
||||
return line;
|
||||
}
|
||||
|
||||
static void canvas_select(Canvas *canvas, float min_x, float min_y, float max_x, float max_y)
|
||||
{
|
||||
for(uintptr i = 0; i < canvas->lines.capacity; i++)
|
||||
{
|
||||
Draw_Line *line_ptr = &canvas->lines.contents[i];
|
||||
if(line_ptr->active)
|
||||
{
|
||||
Draw_Point *a = &canvas->points.contents[line_ptr->point_a];
|
||||
Draw_Point *b = &canvas->points.contents[line_ptr->point_b];
|
||||
if((min_x < a->x && a->x < max_x && min_y < a->y && a->y < max_y) || (min_x < b->x && b->x < max_x && min_y < b->y && b->y < max_y))
|
||||
{
|
||||
line_ptr->selected = 1;
|
||||
|
||||
// add selection
|
||||
if(canvas->selection.length == canvas->selection.capacity)
|
||||
{
|
||||
uintptr new_capacity = canvas->selection.capacity ? canvas->selection.capacity * 2 : 1;
|
||||
uintptr *new_ids = malloc(sizeof(uintptr) * new_capacity);
|
||||
memcpy(new_ids, canvas->selection.ids, sizeof(uintptr) * canvas->selection.length);
|
||||
if(canvas->selection.ids)
|
||||
free(canvas->selection.ids);
|
||||
canvas->selection.ids = new_ids;
|
||||
canvas->selection.capacity = new_capacity;
|
||||
}
|
||||
canvas->selection.ids[canvas->selection.length++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void canvas_desel(Canvas *canvas)
|
||||
{
|
||||
for(uintptr i = 0; i < canvas->selection.length; i++)
|
||||
canvas->lines.contents[canvas->selection.ids[i]].selected = 0;
|
||||
canvas->selection.length = 0;
|
||||
}
|
||||
|
||||
static void canvas_del_lines(Canvas *canvas)
|
||||
{
|
||||
for(uintptr i = 0; i < canvas->selection.length; i++)
|
||||
{
|
||||
Draw_Line *l = &canvas->lines.contents[canvas->selection.ids[i]];
|
||||
canvas->points.contents[l->point_a].rc--;
|
||||
canvas->points.contents[l->point_b].rc--;
|
||||
l->active = 0;
|
||||
}
|
||||
canvas->selection.length = 0;
|
||||
}
|
||||
|
||||
static Draw_Line *canvas_get_line(Canvas *canvas, uintptr id)
|
||||
{
|
||||
Draw_Line *res = &canvas->lines.contents[id];
|
||||
return res->active ? res : nil;
|
||||
}
|
||||
|
||||
static Draw_Point *canvas_get_point(Canvas *canvas, uintptr id)
|
||||
{
|
||||
Draw_Point *res = &canvas->points.contents[id];
|
||||
return res->rc > 0 ? res : nil;
|
||||
}
|
||||
|
||||
static Point draw_point_screen_pos(Draw_Point p)
|
||||
{
|
||||
int x = (screen->r.min.x + screen->r.max.x) / 2 + (intptr)((p.x - state.origin.x) / state.pixel_size);
|
||||
int y = (screen->r.min.y + screen->r.max.y) / 2 + (intptr)((p.y - state.origin.y) / state.pixel_size);
|
||||
Point res = { x, y };
|
||||
return res;
|
||||
}
|
||||
|
||||
static void canvas_draw(const Canvas *canvas)
|
||||
{
|
||||
draw(screen, screen->r, display->white, display->opaque, nil_point);
|
||||
for(uintptr l = 0; l < canvas->lines.capacity; l++)
|
||||
{
|
||||
Draw_Line *line_ptr = &canvas->lines.contents[l];
|
||||
if(line_ptr->active)
|
||||
{
|
||||
Point a = draw_point_screen_pos(canvas->points.contents[line_ptr->point_a]);
|
||||
Point b = draw_point_screen_pos(canvas->points.contents[line_ptr->point_b]);
|
||||
line(screen, a, b, Endsquare, Endsquare, 0, line_ptr->selected ? state.select_texture : display->black, nil_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_bottom_line(void)
|
||||
{
|
||||
Rectangle line_rect = {
|
||||
.min = { screen->r.min.x, screen->r.max.y - font->height },
|
||||
.max = { screen->r.max.x, screen->r.max.y }
|
||||
};
|
||||
// screen->clipr = line_rect;
|
||||
draw(screen, line_rect, display->white, display->opaque, nil_point);
|
||||
|
||||
Point pos = { screen->r.min.x, screen->r.max.y - font->height };
|
||||
if(state.state == ST_COMMAND)
|
||||
{
|
||||
pos = string(screen, pos, display->black, nil_point, font, ":");
|
||||
pos = string(screen, pos, display->black, nil_point, font, str_buf_to_cstr(&state.command));
|
||||
|
||||
Point cur_top = { pos.x + 2, screen->r.max.y - font->height };
|
||||
Point cur_bot = { cur_top.x, screen->r.max.y };
|
||||
line(screen, cur_top, cur_bot, Enddisc, Enddisc, 0, display->black, nil_point);
|
||||
}
|
||||
else
|
||||
string(screen, pos, display->black, nil_point, font, str_buf_to_cstr(&state.bottom_line));
|
||||
}
|
||||
|
||||
static void run_cmd(void)
|
||||
{
|
||||
uintptr cmd_start = 0;
|
||||
while(cmd_start < state.command.length && isspace(state.command.contents[cmd_start]))
|
||||
cmd_start++;
|
||||
if(cmd_start == state.command.length)
|
||||
return;
|
||||
uintptr cmd_end = cmd_start;
|
||||
while(cmd_end < state.command.length && !isspace(state.command.contents[cmd_end]))
|
||||
cmd_end++;
|
||||
|
||||
char *cmd = malloc(cmd_end - cmd_start + 1);
|
||||
memcpy(cmd, state.command.contents + cmd_start, cmd_end - cmd_start);
|
||||
cmd[cmd_end - cmd_start] = 0;
|
||||
|
||||
for(uintptr i = 0; i < nelem(cmds); i++)
|
||||
{
|
||||
if(!strcmp(cmds[i].name, cmd))
|
||||
{
|
||||
switch(cmds[i].mode)
|
||||
{
|
||||
case CMD_NO_ARGS:
|
||||
cmds[i].fn.no_args();
|
||||
break;
|
||||
|
||||
case CMD_STR_ARG:
|
||||
cmds[i].fn.str_arg(state.command.length > cmd_end ? str_buf_to_cstr(&state.command) + cmd_end + 1 : 0);
|
||||
break;
|
||||
}
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
str_buf_clear(&state.bottom_line);
|
||||
str_buf_append_cstr(&state.bottom_line, "Command wasn't found");
|
||||
|
||||
found:
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void cmd_exit(void)
|
||||
{
|
||||
state.stopping = 1;
|
||||
}
|
||||
|
||||
static void cmd_scale(const char *arg)
|
||||
{
|
||||
if(arg)
|
||||
{
|
||||
if((state.scale = atof(arg)) == 0.f)
|
||||
state.scale = 1.f;
|
||||
}
|
||||
else
|
||||
state.scale = 1.;
|
||||
calc_pixel_size();
|
||||
}
|
||||
|
||||
static void cmd_del(void)
|
||||
{
|
||||
canvas_del_lines(state.active_canvas);
|
||||
}
|
||||
|
||||
static void cmd_join(void)
|
||||
{
|
||||
if(state.active_canvas->selection.length != 2)
|
||||
{
|
||||
str_buf_clear(&state.bottom_line);
|
||||
str_buf_append_cstr(&state.bottom_line, "join works with 2 lines");
|
||||
return;
|
||||
}
|
||||
|
||||
Draw_Line *a = canvas_get_line(state.active_canvas, state.active_canvas->selection.ids[0]);
|
||||
Draw_Line *b = canvas_get_line(state.active_canvas, state.active_canvas->selection.ids[1]);
|
||||
|
||||
Draw_Point *pairs[4][2] = {
|
||||
{ canvas_get_point(state.active_canvas, a->point_a), canvas_get_point(state.active_canvas, b->point_a) },
|
||||
{ canvas_get_point(state.active_canvas, a->point_a), canvas_get_point(state.active_canvas, b->point_b) },
|
||||
{ canvas_get_point(state.active_canvas, a->point_b), canvas_get_point(state.active_canvas, b->point_a) },
|
||||
{ canvas_get_point(state.active_canvas, a->point_b), canvas_get_point(state.active_canvas, b->point_b) },
|
||||
};
|
||||
|
||||
float min_dist = -1.f;
|
||||
uintptr best_pair = 0;
|
||||
for(uintptr i = 0; i < 4; i++)
|
||||
{
|
||||
if(pairs[i][0] == pairs[i][1])
|
||||
{
|
||||
str_buf_clear(&state.bottom_line);
|
||||
str_buf_append_cstr(&state.bottom_line, "Those lines are already connected");
|
||||
return;
|
||||
}
|
||||
float dx = pairs[i][0]->x - pairs[i][1]->x;
|
||||
float dy = pairs[i][0]->y - pairs[i][1]->y;
|
||||
float dist = sqrt(dx * dx + dy * dy);
|
||||
if(min_dist < 0.f || dist < min_dist)
|
||||
{
|
||||
min_dist = dist;
|
||||
best_pair = i;
|
||||
}
|
||||
}
|
||||
|
||||
float x = (pairs[best_pair][0]->x + pairs[best_pair][1]->x) / 2.f;
|
||||
float y = (pairs[best_pair][0]->y + pairs[best_pair][1]->y) / 2.f;
|
||||
uintptr join_point_id = best_pair & 2 ? a->point_b : a->point_a;
|
||||
if(best_pair & 1)
|
||||
{
|
||||
canvas_get_point(state.active_canvas, b->point_b)->rc--;
|
||||
b->point_b = join_point_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas_get_point(state.active_canvas, b->point_a)->rc--;
|
||||
b->point_a = join_point_id;
|
||||
}
|
||||
Draw_Point *join_point = canvas_get_point(state.active_canvas, join_point_id);
|
||||
join_point->rc++;
|
||||
join_point->x = x;
|
||||
join_point->y = y;
|
||||
}
|
||||
46
src/str.c
Normal file
46
src/str.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include "str.h"
|
||||
|
||||
void str_buf_clear(Str_Buf *buf)
|
||||
{
|
||||
buf->length = 0;
|
||||
}
|
||||
|
||||
void str_buf_delete(Str_Buf *buf)
|
||||
{
|
||||
if(buf->contents)
|
||||
{
|
||||
free(buf->contents);
|
||||
buf->contents = 0;
|
||||
}
|
||||
buf->length = buf->capacity = 0;
|
||||
}
|
||||
|
||||
void str_buf_push_char(Str_Buf *buf, char ch)
|
||||
{
|
||||
if(buf->length == buf->capacity)
|
||||
{
|
||||
uintptr new_capacity = buf->capacity ? buf->capacity * 2 : 8;
|
||||
char *new_contents = malloc(new_capacity);
|
||||
memcpy(new_contents, buf->contents, buf->length);
|
||||
if(buf->contents)
|
||||
free(buf->contents);
|
||||
buf->contents = new_contents;
|
||||
buf->capacity = new_capacity;
|
||||
}
|
||||
buf->contents[buf->length++] = ch;
|
||||
}
|
||||
|
||||
void str_buf_append_cstr(Str_Buf *buf, const char *str)
|
||||
{
|
||||
while(*str)
|
||||
str_buf_push_char(buf, *(str++));
|
||||
}
|
||||
|
||||
char *str_buf_to_cstr(Str_Buf *buf)
|
||||
{
|
||||
str_buf_push_char(buf, 0);
|
||||
buf->length--;
|
||||
return buf->contents;
|
||||
}
|
||||
22
src/str.h
Normal file
22
src/str.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef STR_H
|
||||
#define STR_H
|
||||
|
||||
#ifdef IN_EDITOR
|
||||
#include <u.h>
|
||||
#endif
|
||||
|
||||
typedef struct Str_Buf Str_Buf;
|
||||
struct Str_Buf
|
||||
{
|
||||
char *contents;
|
||||
uintptr length;
|
||||
uintptr capacity;
|
||||
};
|
||||
|
||||
void str_buf_clear(Str_Buf *buf);
|
||||
void str_buf_delete(Str_Buf *buf);
|
||||
void str_buf_push_char(Str_Buf *buf, char ch);
|
||||
void str_buf_append_cstr(Str_Buf *buf, const char *str);
|
||||
char *str_buf_to_cstr(Str_Buf *buf);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user