feat: add stroke thickness

This commit is contained in:
2026-03-05 00:51:20 +05:30
parent 8ac783e6e0
commit eefd95e4d2
15 changed files with 379 additions and 356 deletions

View File

@@ -1,42 +1,61 @@
#include "./applicationstate.h" #include "./applicationstate.h"
#include "glib.h"
#include "src/core/primitives.h" #include "src/core/primitives.h"
#include "src/core/raster.h" #include "src/core/raster.h"
#include "src/ui/vektorcanvas.h" #include "src/ui/vektorcanvas.h"
typedef struct button_tool_set_data { typedef struct button_tool_set_data {
VektorAppState* state; VektorAppState *state;
VektorAppTool tool; VektorAppTool tool;
} button_tool_set_data; } button_tool_set_data;
static void appstate_set_tool(GtkButton* button, gpointer user_data) { static void appstate_set_tool(GtkButton *button, gpointer user_data) {
button_tool_set_data* data = (button_tool_set_data*)user_data; button_tool_set_data *data = (button_tool_set_data *)user_data;
data->state->selectedTool = data->tool; data->state->selectedTool = data->tool;
// setting tool also resets selected primitive // setting tool also resets selected primitive
data->state->selectedPrimitive = NULL; data->state->selectedPrimitive = NULL;
} }
static void canvas_onclick(GtkGestureClick* gesture, int n_press, double x, double y, gpointer user_data) { static void canvas_onclick(GtkGestureClick *gesture, int n_press, double x,
vektor_appstate_canvas_click(user_data, x, y); double y, gpointer user_data) {
VektorAppState *state = user_data;
GtkWidget *widget =
gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
int widget_w = gtk_widget_get_width(widget);
int widget_h = gtk_widget_get_height(widget);
int canvas_w = state->canvas->width;
int canvas_h = state->canvas->height;
double sx = canvas_w / (double)widget_w;
double sy = canvas_h / (double)widget_h;
g_debug("<%f , %f>", x * sx, y * sy);
vektor_appstate_canvas_click(state, x * sx, y * sy);
} }
void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) { void vektor_appstate_canvas_click(VektorAppState *state, double x, double y) {
V2 pos = (V2){x,y}; V2 pos = (V2){x, y};
begin_click_dispatch: begin_click_dispatch:
if(state->selectedTool == VektorLineTool) { if (state->selectedTool == VektorLineTool) {
// create new polyline primitive if none is selected // create new polyline primitive if none is selected
if(state->selectedPrimitive == NULL) { if (state->selectedPrimitive == NULL) {
VektorPolyline* line = vektor_polyline_new(); VektorPolyline *line = vektor_polyline_new();
VektorPrimitive linePrimitive = (VektorPrimitive){ VektorPrimitive linePrimitive =
.kind = VEKTOR_POLYLINE, .polyline = line (VektorPrimitive){.kind = VEKTOR_POLYLINE, .polyline = line};
}; vektor_primitivebuffer_add_primitive(state->primitiveBuffer,
vektor_primitivebuffer_add_primitive(state->primitiveBuffer, linePrimitive); linePrimitive);
state->selectedPrimitive = state->selectedPrimitive =
&(state->primitiveBuffer->primitives[state->primitiveBuffer->count - 1]); &(state->primitiveBuffer
->primitives[state->primitiveBuffer->count - 1]);
} else if (state->selectedPrimitive->kind != VEKTOR_POLYLINE) { } else if (state->selectedPrimitive->kind != VEKTOR_POLYLINE) {
// selecting a tool resets the selection, so this condition // selecting a tool resets the selection, so this condition
// should not happen // should not happen
@@ -44,8 +63,7 @@ void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) {
state->selectedPrimitive = NULL; state->selectedPrimitive = NULL;
goto begin_click_dispatch; // retry goto begin_click_dispatch; // retry
} }
vektor_polyline_add_point(state->selectedPrimitive->polyline, pos); vektor_polyline_add_point(state->selectedPrimitive->polyline, pos);
} }
@@ -54,8 +72,8 @@ void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) {
vektor_canvas_update(state->canvas); vektor_canvas_update(state->canvas);
} }
void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { void vektor_appstate_new(VektorWidgetState *wstate, VektorAppState *stateOut) {
button_tool_set_data* data_linetool = malloc(sizeof(button_tool_set_data)); button_tool_set_data *data_linetool = malloc(sizeof(button_tool_set_data));
data_linetool->state = stateOut; data_linetool->state = stateOut;
data_linetool->tool = VektorLineTool; data_linetool->tool = VektorLineTool;
@@ -68,18 +86,13 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {
vektor_canvas_init(wstate, stateOut->canvas); vektor_canvas_init(wstate, stateOut->canvas);
// link all the buttons // link all the buttons
g_signal_connect( g_signal_connect(G_OBJECT(wstate->workspaceButtonLinetool), "clicked",
G_OBJECT(wstate->workspaceButtonLinetool), G_CALLBACK(appstate_set_tool), data_linetool);
"clicked", G_CALLBACK(appstate_set_tool), data_linetool);
// Add click gesture to canvas // Add click gesture to canvas
GtkGesture* canvasClickGesture = gtk_gesture_click_new(); GtkGesture *canvasClickGesture = gtk_gesture_click_new();
g_signal_connect( g_signal_connect(G_OBJECT(canvasClickGesture), "pressed",
G_OBJECT(canvasClickGesture), G_CALLBACK(canvas_onclick), stateOut);
"pressed", G_CALLBACK(canvas_onclick), stateOut gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas),
); GTK_EVENT_CONTROLLER(canvasClickGesture));
gtk_widget_add_controller(
GTK_WIDGET(wstate->workspaceCanvas),
GTK_EVENT_CONTROLLER(canvasClickGesture)
);
} }

View File

@@ -1,29 +1,27 @@
#ifndef VKTR_APPSTATE_H #ifndef VKTR_APPSTATE_H
#define VKTR_APPSTATE_H #define VKTR_APPSTATE_H
#include "../ui/uicontroller.h"
#include "../core/primitives.h" #include "../core/primitives.h"
#include "src/core/raster.h" #include "../ui/uicontroller.h"
#include "../ui/vektorcanvas.h" #include "../ui/vektorcanvas.h"
#include "src/core/raster.h"
typedef enum VektorAppTool { typedef enum VektorAppTool { VektorLineTool } VektorAppTool;
VektorLineTool
} VektorAppTool;
typedef struct VektorAppState { typedef struct VektorAppState {
VektorAppTool selectedTool; VektorAppTool selectedTool;
VektorPrimitive* selectedPrimitive; VektorPrimitive *selectedPrimitive;
// Logic space // Logic space
VektorPrimitiveBuffer* primitiveBuffer; VektorPrimitiveBuffer *primitiveBuffer;
// Pixel space // Pixel space
VektorFramebuffer* frameBuffer; VektorFramebuffer *frameBuffer;
// View space // View space
VektorCanvas* canvas; VektorCanvas *canvas;
} VektorAppState; } VektorAppState;
void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut); void vektor_appstate_new(VektorWidgetState *wstate, VektorAppState *stateOut);
void vektor_appstate_canvas_click(VektorAppState* state, double x, double y); void vektor_appstate_canvas_click(VektorAppState *state, double x, double y);
#endif #endif

View File

@@ -4,86 +4,86 @@
inline M33 m33_zero(void) { return (M33){{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; } inline M33 m33_zero(void) { return (M33){{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; }
inline M33 m33_identity(void) { inline M33 m33_identity(void) {
return (M33){{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}; return (M33){{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
} }
inline M33 m33_translate(double tx, double ty) { inline M33 m33_translate(double tx, double ty) {
return (M33){{{1, 0, tx}, {0, 1, ty}, {0, 0, 1}}}; return (M33){{{1, 0, tx}, {0, 1, ty}, {0, 0, 1}}};
} }
inline M33 m33_scale(double sx, double sy) { inline M33 m33_scale(double sx, double sy) {
return (M33){{{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}}}; return (M33){{{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}}};
} }
inline M33 m33_rotate(double theta) { inline M33 m33_rotate(double theta) {
return (M33){ return (M33){
{{cos(theta), -sin(theta), 0}, {sin(theta), cos(theta), 0}, {0, 0, 1}}}; {{cos(theta), -sin(theta), 0}, {sin(theta), cos(theta), 0}, {0, 0, 1}}};
} }
inline M33 m33_shear(double theta_x, double theta_y) { inline M33 m33_shear(double theta_x, double theta_y) {
return (M33){{{1, tan(theta_x), 0}, {tan(theta_y), 1, 0}, {0, 0, 0}}}; return (M33){{{1, tan(theta_x), 0}, {tan(theta_y), 1, 0}, {0, 0, 0}}};
} }
M33 m33_add(const M33 m1, const M33 m2) { M33 m33_add(const M33 m1, const M33 m2) {
M33 res; M33 res;
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
res.m[i][j] = m1.m[i][j] + m2.m[i][j]; res.m[i][j] = m1.m[i][j] + m2.m[i][j];
return res; return res;
} }
M33 m33_sub(const M33 m1, const M33 m2) { M33 m33_sub(const M33 m1, const M33 m2) {
M33 res; M33 res;
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
res.m[i][j] = m1.m[i][j] - m2.m[i][j]; res.m[i][j] = m1.m[i][j] - m2.m[i][j];
return res; return res;
} }
M33 m33_mul(const M33 m1, const M33 m2) { M33 m33_mul(const M33 m1, const M33 m2) {
M33 res = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; M33 res = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k) for (int k = 0; k < 3; ++k)
res.m[i][j] += m1.m[i][k] * m2.m[k][j]; res.m[i][j] += m1.m[i][k] * m2.m[k][j];
return res; return res;
} }
M33 m33_transpose(const M33 m) { M33 m33_transpose(const M33 m) {
M33 res; M33 res;
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
res.m[i][j] = m.m[j][i]; res.m[i][j] = m.m[j][i];
return res; return res;
} }
// 2D affine transform inversion (not general 3x3 inversion) // 2D affine transform inversion (not general 3x3 inversion)
M33 m33_inverse(const M33 m) { M33 m33_inverse(const M33 m) {
M33 inv; M33 inv;
double det; double det;
det = m.m[0][0] * m.m[1][1] - m.m[0][1] * m.m[1][0]; det = m.m[0][0] * m.m[1][1] - m.m[0][1] * m.m[1][0];
if (det == 0.0) { if (det == 0.0) {
return m33_identity(); return m33_identity();
} }
double invDet = 1.0 / det; double invDet = 1.0 / det;
inv.m[0][0] = m.m[1][1] * invDet; inv.m[0][0] = m.m[1][1] * invDet;
inv.m[0][1] = -m.m[0][1] * invDet; inv.m[0][1] = -m.m[0][1] * invDet;
inv.m[1][0] = -m.m[1][0] * invDet; inv.m[1][0] = -m.m[1][0] * invDet;
inv.m[1][1] = m.m[0][0] * invDet; inv.m[1][1] = m.m[0][0] * invDet;
inv.m[0][2] = -(inv.m[0][0] * m.m[0][2] + inv.m[0][1] * m.m[1][2]); inv.m[0][2] = -(inv.m[0][0] * m.m[0][2] + inv.m[0][1] * m.m[1][2]);
inv.m[1][2] = -(inv.m[1][0] * m.m[0][2] + inv.m[1][1] * m.m[1][2]); inv.m[1][2] = -(inv.m[1][0] * m.m[0][2] + inv.m[1][1] * m.m[1][2]);
inv.m[2][0] = 0; inv.m[2][0] = 0;
inv.m[2][1] = 0; inv.m[2][1] = 0;
inv.m[2][2] = 1; inv.m[2][2] = 1;
return inv; return inv;
} }
V2 m33_transform(const M33 mat, const V2 v) { V2 m33_transform(const M33 mat, const V2 v) {
return (V2){mat.m[0][0] * v.x + mat.m[0][1] * v.y + mat.m[0][2], return (V2){mat.m[0][0] * v.x + mat.m[0][1] * v.y + mat.m[0][2],
mat.m[1][0] * v.x + mat.m[1][1] * v.y + mat.m[1][2]}; mat.m[1][0] * v.x + mat.m[1][1] * v.y + mat.m[1][2]};
} }

View File

@@ -5,7 +5,7 @@
// Row major 3x3 matricies // Row major 3x3 matricies
typedef struct { typedef struct {
double m[3][3]; double m[3][3];
} M33; } M33;
M33 m33_identity(void); M33 m33_identity(void);

View File

@@ -1,57 +1,57 @@
#include "primitives.h" #include "primitives.h"
VektorPolyline *vektor_polyline_new(void) { VektorPolyline *vektor_polyline_new(void) {
VektorPolyline *pl = malloc(sizeof(VektorPolyline)); VektorPolyline *pl = malloc(sizeof(VektorPolyline));
pl->count = 0; pl->count = 0;
pl->capacity = 4; pl->capacity = 4;
pl->points = malloc(sizeof(V2) * pl->capacity); pl->points = malloc(sizeof(V2) * pl->capacity);
return pl; return pl;
} }
void vektor_polyline_add_point(VektorPolyline *pl, V2 point) { void vektor_polyline_add_point(VektorPolyline *pl, V2 point) {
if (pl->count >= pl->capacity) { if (pl->count >= pl->capacity) {
pl->capacity *= 2; pl->capacity *= 2;
pl->points = realloc(pl->points, sizeof(V2) * pl->capacity); pl->points = realloc(pl->points, sizeof(V2) * pl->capacity);
} }
pl->points[pl->count++] = point; pl->points[pl->count++] = point;
} }
void vektor_polyline_free(VektorPolyline *pl) { void vektor_polyline_free(VektorPolyline *pl) {
if (!pl) if (!pl)
return; return;
free(pl->points); free(pl->points);
free(pl); free(pl);
} }
VektorPolygon *vektor_polygon_new(void) { VektorPolygon *vektor_polygon_new(void) {
VektorPolygon *pg = malloc(sizeof(VektorPolygon)); VektorPolygon *pg = malloc(sizeof(VektorPolygon));
pg->count = 0; pg->count = 0;
pg->capacity = 4; pg->capacity = 4;
pg->points = malloc(sizeof(V2) * pg->capacity); pg->points = malloc(sizeof(V2) * pg->capacity);
return pg; return pg;
} }
void vektor_polygon_add_point(VektorPolygon *pg, V2 point) { void vektor_polygon_add_point(VektorPolygon *pg, V2 point) {
if (pg->count >= pg->capacity) { if (pg->count >= pg->capacity) {
pg->capacity *= 2; pg->capacity *= 2;
pg->points = realloc(pg->points, sizeof(V2) * pg->capacity); pg->points = realloc(pg->points, sizeof(V2) * pg->capacity);
} }
pg->points[pg->count++] = point; pg->points[pg->count++] = point;
} }
void vektor_polygon_free(VektorPolygon *pg) { void vektor_polygon_free(VektorPolygon *pg) {
if (!pg) if (!pg)
return; return;
free(pg->points); free(pg->points);
free(pg); free(pg);
} }
void vektor_primitivebuffer_add_primitive(VektorPrimitiveBuffer *buffer, void vektor_primitivebuffer_add_primitive(VektorPrimitiveBuffer *buffer,
VektorPrimitive prim) { VektorPrimitive prim) {
if (buffer->count >= buffer->capacity) { if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4; buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->primitives = buffer->primitives = realloc(
realloc(buffer->primitives, sizeof(VektorPrimitive) * buffer->capacity); buffer->primitives, sizeof(VektorPrimitive) * buffer->capacity);
} }
buffer->primitives[buffer->count++] = prim; buffer->primitives[buffer->count++] = prim;
} }

View File

@@ -6,42 +6,42 @@
#include "vector.h" #include "vector.h"
typedef struct { typedef struct {
V2 p1; V2 p1;
V2 p2; V2 p2;
} VektorLine; } VektorLine;
typedef struct { typedef struct {
V2 *points; V2 *points;
size_t count; size_t count;
size_t capacity; size_t capacity;
} VektorPolyline; } VektorPolyline;
typedef struct { typedef struct {
V2 *points; V2 *points;
size_t count; size_t count;
size_t capacity; size_t capacity;
} VektorPolygon; } VektorPolygon;
typedef struct { typedef struct {
V2 center; V2 center;
double radius; double radius;
} VektorCircle; } VektorCircle;
typedef enum { typedef enum {
VEKTOR_LINE, VEKTOR_LINE,
VEKTOR_POLYLINE, VEKTOR_POLYLINE,
VEKTOR_POLYGON, VEKTOR_POLYGON,
VEKTOR_CIRCLE VEKTOR_CIRCLE
} VektorPrimitiveKind; } VektorPrimitiveKind;
typedef struct { typedef struct {
VektorPrimitiveKind kind; VektorPrimitiveKind kind;
union { union {
VektorLine line; VektorLine line;
VektorPolyline *polyline; VektorPolyline *polyline;
VektorPolygon *polygon; VektorPolygon *polygon;
VektorCircle circle; VektorCircle circle;
}; };
} VektorPrimitive; } VektorPrimitive;
VektorPolyline *vektor_polyline_new(void); VektorPolyline *vektor_polyline_new(void);
@@ -53,9 +53,9 @@ void vektor_polygon_add_point(VektorPolygon *pl, V2 point);
void vektor_polygon_free(VektorPolygon *pl); void vektor_polygon_free(VektorPolygon *pl);
typedef struct { typedef struct {
VektorPrimitive *primitives; VektorPrimitive *primitives;
size_t count; size_t count;
size_t capacity; size_t capacity;
} VektorPrimitiveBuffer; } VektorPrimitiveBuffer;
void vektor_primitivebuffer_add_primitive(VektorPrimitiveBuffer *edges, void vektor_primitivebuffer_add_primitive(VektorPrimitiveBuffer *edges,

View File

@@ -4,112 +4,123 @@
#include <stddef.h> #include <stddef.h>
void vektor_edgebuffer_add_edge(EdgeBuffer *buffer, Edge edge) { void vektor_edgebuffer_add_edge(EdgeBuffer *buffer, Edge edge) {
if (buffer->count >= buffer->capacity) { if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4; buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->edges = realloc(buffer->edges, sizeof(Edge) * buffer->capacity); buffer->edges = realloc(buffer->edges, sizeof(Edge) * buffer->capacity);
} }
buffer->edges[buffer->count++] = edge; buffer->edges[buffer->count++] = edge;
} }
void vektor_line_flatten(EdgeBuffer *buffer, VektorLine line) { void vektor_line_flatten(EdgeBuffer *buffer, VektorLine line) {
vektor_edgebuffer_add_edge(buffer, (Edge){line.p1, line.p2, 0}); vektor_edgebuffer_add_edge(buffer, (Edge){line.p1, line.p2, 0});
} }
void vektor_polyline_flatten(EdgeBuffer *buffer, VektorPolyline *line) { void vektor_polyline_flatten(EdgeBuffer *buffer, VektorPolyline *line) {
for (size_t i = 0; i + 1 < line->count; i++) { for (size_t i = 0; i + 1 < line->count; i++) {
vektor_edgebuffer_add_edge(buffer, vektor_edgebuffer_add_edge(
(Edge){line->points[i], line->points[i + 1], 0}); buffer, (Edge){line->points[i], line->points[i + 1], 0});
} }
} }
void vektor_polygon_flatten(EdgeBuffer *buffer, VektorPolygon *pg) { void vektor_polygon_flatten(EdgeBuffer *buffer, VektorPolygon *pg) {
size_t n = pg->count; size_t n = pg->count;
if (n < 3) if (n < 3)
return; return;
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
V2 p1 = pg->points[i]; V2 p1 = pg->points[i];
V2 p2 = pg->points[(i + 1) % n]; V2 p2 = pg->points[(i + 1) % n];
int winding = (p1.y < p2.y) ? +1 : -1; int winding = (p1.y < p2.y) ? +1 : -1;
vektor_edgebuffer_add_edge(buffer, (Edge){p1, p2, winding}); vektor_edgebuffer_add_edge(buffer, (Edge){p1, p2, winding});
} }
} }
inline VektorFramebuffer vektor_framebuffer_new(unsigned int W, inline VektorFramebuffer vektor_framebuffer_new(unsigned int W,
unsigned int H) { unsigned int H) {
VektorFramebuffer fb = { VektorFramebuffer fb = {
.width = W, .height = H, .pixels = calloc(W * H * 4, 1)}; .width = W, .height = H, .pixels = calloc(W * H * 4, 1)};
return fb; return fb;
} }
inline void vektor_framebuffer_putpixel(VektorFramebuffer *fb, int x, int y, inline void vektor_framebuffer_putpixel(VektorFramebuffer *fb, int x, int y,
VektorColor color) { VektorColor color) {
if ((unsigned)x >= fb->width || (unsigned)y >= fb->height) if ((unsigned)x >= fb->width || (unsigned)y >= fb->height)
return; return;
int i = (y * fb->width + x) * 4; int i = (y * fb->width + x) * 4;
fb->pixels[i + 0] = color.r; fb->pixels[i + 0] = color.r;
fb->pixels[i + 1] = color.g; fb->pixels[i + 1] = color.g;
fb->pixels[i + 2] = color.b; fb->pixels[i + 2] = color.b;
fb->pixels[i + 3] = color.a; fb->pixels[i + 3] = color.a;
}
void draw_filled_circle(VektorFramebuffer *fb, int cx, int cy, int r,
VektorColor color) {
for (int y = -r; y <= r; y++) {
int dx = (int)sqrt(r * r - y * y);
for (int x = -dx; x <= dx; x++) {
vektor_framebuffer_putpixel(fb, cx + x, cy + y, color);
}
}
} }
void vektor_framebuffer_drawline(VektorFramebuffer *fb, V2 a, V2 b, void vektor_framebuffer_drawline(VektorFramebuffer *fb, V2 a, V2 b,
VektorColor color) { VektorColor color, double thickness) {
int x0 = (int)a.x; int x0 = (int)a.x;
int y0 = (int)a.y; int y0 = (int)a.y;
int x1 = (int)b.x; int x1 = (int)b.x;
int y1 = (int)b.y; int y1 = (int)b.y;
int dx = abs(x1 - x0); int dx = abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1; int sx = x0 < x1 ? 1 : -1;
int dy = -abs(y1 - y0); int dy = -abs(y1 - y0);
int sy = y0 < y1 ? 1 : -1; int sy = y0 < y1 ? 1 : -1;
int err = dx + dy; int err = dx + dy;
for (;;) { for (;;) {
vektor_framebuffer_putpixel(fb, x0, y0, color); draw_filled_circle(fb, x0, y0, thickness / 2, color);
if (x0 == x1 && y0 == y1) if (x0 == x1 && y0 == y1)
break; break;
int e2 = 2 * err; int e2 = 2 * err;
if (e2 >= dy) { if (e2 >= dy) {
err += dy; err += dy;
x0 += sx; x0 += sx;
}
if (e2 <= dx) {
err += dx;
y0 += sy;
}
} }
if (e2 <= dx) {
err += dx;
y0 += sy;
}
}
} }
void vektor_framebuffer_rasterize(VektorFramebuffer *fb, VektorPrimitiveBuffer *prims) { void vektor_framebuffer_rasterize(VektorFramebuffer *fb,
EdgeBuffer edges = {0}; VektorPrimitiveBuffer *prims) {
for (size_t i = 0; i < prims->count; i++) { EdgeBuffer edges = {0};
VektorPrimitive *p = &prims->primitives[i]; for (size_t i = 0; i < prims->count; i++) {
VektorPrimitive *p = &prims->primitives[i];
switch (p->kind) { switch (p->kind) {
case VEKTOR_LINE: case VEKTOR_LINE:
vektor_line_flatten(&edges, p->line); vektor_line_flatten(&edges, p->line);
break; break;
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
vektor_polyline_flatten(&edges, p->polyline); vektor_polyline_flatten(&edges, p->polyline);
break; break;
case VEKTOR_POLYGON: case VEKTOR_POLYGON:
vektor_polygon_flatten(&edges, p->polygon); vektor_polygon_flatten(&edges, p->polygon);
break; break;
default: default:
// TODO fill in all primitives // TODO fill in all primitives
break; break;
}
} }
}
for (size_t i = 0; i < edges.count; i++) { for (size_t i = 0; i < edges.count; i++) {
vektor_framebuffer_drawline(fb, edges.edges[i].p1, edges.edges[i].p2, vektor_framebuffer_drawline(fb, edges.edges[i].p1, edges.edges[i].p2,
vektor_color_solid(255, 0, 255)); vektor_color_solid(255, 0, 255), 4);
} }
} }

View File

@@ -8,15 +8,15 @@
#include "vector.h" #include "vector.h"
typedef struct { typedef struct {
V2 p1; V2 p1;
V2 p2; V2 p2;
int winding; int winding;
} Edge; } Edge;
typedef struct { typedef struct {
Edge *edges; Edge *edges;
size_t count; size_t count;
size_t capacity; size_t capacity;
} EdgeBuffer; } EdgeBuffer;
void vektor_edgebuffer_add_edge(EdgeBuffer *edges, Edge edge); void vektor_edgebuffer_add_edge(EdgeBuffer *edges, Edge edge);
@@ -26,9 +26,9 @@ void vektor_polyline_flatten(EdgeBuffer *edges, VektorPolyline *line);
void vektor_polygon_flatten(EdgeBuffer *buffer, VektorPolygon *line); void vektor_polygon_flatten(EdgeBuffer *buffer, VektorPolygon *line);
typedef struct { typedef struct {
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
unsigned char *pixels; // Flat RGBA8 array unsigned char *pixels; // Flat RGBA8 array
} VektorFramebuffer; } VektorFramebuffer;
VektorFramebuffer vektor_framebuffer_new(unsigned int width, VektorFramebuffer vektor_framebuffer_new(unsigned int width,
@@ -38,8 +38,9 @@ void vektor_framebuffer_putpixel(VektorFramebuffer *fb, int x, int y,
VektorColor color); VektorColor color);
void vektor_framebuffer_drawline(VektorFramebuffer *fb, V2 a, V2 b, void vektor_framebuffer_drawline(VektorFramebuffer *fb, V2 a, V2 b,
VektorColor color); VektorColor color, double thickness);
void vektor_framebuffer_rasterize(VektorFramebuffer *fb, VektorPrimitiveBuffer *primitives); void vektor_framebuffer_rasterize(VektorFramebuffer *fb,
VektorPrimitiveBuffer *primitives);
#endif // RASTER_H_ #endif // RASTER_H_

View File

@@ -4,14 +4,14 @@
#include "math.h" #include "math.h"
typedef struct { typedef struct {
double x; double x;
double y; double y;
} V2; } V2;
typedef struct { typedef struct {
double x; double x;
double y; double y;
double z; double z;
} V3; } V3;
static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; } static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; }
@@ -19,39 +19,39 @@ static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; }
static inline V3 vec2_point(const V2 v) { return (V3){v.x, v.y, 1}; } static inline V3 vec2_point(const V2 v) { return (V3){v.x, v.y, 1}; }
static inline V2 vec2_add(const V2 v1, const V2 v2) { static inline V2 vec2_add(const V2 v1, const V2 v2) {
return (V2){v1.x + v2.x, v1.y + v2.y}; return (V2){v1.x + v2.x, v1.y + v2.y};
} }
static inline V2 vec2_sub(const V2 v1, const V2 v2) { static inline V2 vec2_sub(const V2 v1, const V2 v2) {
return (V2){v1.x - v2.x, v1.y - v2.y}; return (V2){v1.x - v2.x, v1.y - v2.y};
} }
static inline V2 vec2_mul(const V2 v1, const V2 v2) { static inline V2 vec2_mul(const V2 v1, const V2 v2) {
return (V2){v1.x * v2.x, v1.y * v2.y}; return (V2){v1.x * v2.x, v1.y * v2.y};
} }
static inline V2 vec2_scale(const V2 v, const double k) { static inline V2 vec2_scale(const V2 v, const double k) {
return (V2){v.x * k, v.y * k}; return (V2){v.x * k, v.y * k};
} }
static inline double vec2_dot(const V2 v1, const V2 v2) { static inline double vec2_dot(const V2 v1, const V2 v2) {
return v1.x * v2.x + v1.y * v2.y; return v1.x * v2.x + v1.y * v2.y;
} }
static inline double vec2_cross(const V2 a, const V2 b) { static inline double vec2_cross(const V2 a, const V2 b) {
return a.x * b.y - a.y * b.x; return a.x * b.y - a.y * b.x;
} }
static inline double vec2_norm(const V2 v) { static inline double vec2_norm(const V2 v) {
return sqrt(v.x * v.x + v.y * v.y); return sqrt(v.x * v.x + v.y * v.y);
} }
static inline double vec2_quadrance(const V2 v) { static inline double vec2_quadrance(const V2 v) {
return (v.x * v.x + v.y * v.y); return (v.x * v.x + v.y * v.y);
} }
static inline V2 vec2_normalize(const V2 v) { static inline V2 vec2_normalize(const V2 v) {
return vec2_scale(v, 1 / vec2_norm(v)); return vec2_scale(v, 1 / vec2_norm(v));
} }
#endif // VECTOR_H_ #endif // VECTOR_H_

View File

@@ -4,41 +4,40 @@
#include "stdio.h" #include "stdio.h"
#include "stdlib.h" #include "stdlib.h"
#include "./application/applicationstate.h"
#include "./core/raster.h" #include "./core/raster.h"
#include "./ui/uicontroller.h" #include "./ui/uicontroller.h"
#include "./ui/vektorcanvas.h" #include "./ui/vektorcanvas.h"
#include "./util/color.h" #include "./util/color.h"
#include "./application/applicationstate.h"
static void on_map(GtkWidget *window, gpointer user_data) { static void on_map(GtkWidget *window, gpointer user_data) {
vektor_uictrl_map((VektorWidgetState *)user_data); vektor_uictrl_map((VektorWidgetState *)user_data);
} }
static void activate(GtkApplication *app, gpointer user_data) { static void activate(GtkApplication *app, gpointer user_data) {
VektorWidgetState *widget_state = VektorWidgetState *widget_state =
(VektorWidgetState *)malloc(sizeof(VektorWidgetState)); (VektorWidgetState *)malloc(sizeof(VektorWidgetState));
vektor_uictrl_init(app, widget_state); vektor_uictrl_init(app, widget_state);
VektorAppState* app_state = VektorAppState *app_state =
(VektorAppState *)malloc(sizeof(VektorAppState)); (VektorAppState *)malloc(sizeof(VektorAppState));
vektor_appstate_new(widget_state, app_state); vektor_appstate_new(widget_state, app_state);
g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map),
widget_state);
g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map), gtk_window_present(widget_state->window);
widget_state);
gtk_window_present(widget_state->window);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int status; int status;
app = gtk_application_new("dev.frox.vektor", G_APPLICATION_DEFAULT_FLAGS); app = gtk_application_new("dev.frox.vektor", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv); status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app); g_object_unref(app);
return status; return status;
} }

View File

@@ -5,43 +5,43 @@
#include "gtk/gtkcssprovider.h" #include "gtk/gtkcssprovider.h"
void vektor_uictrl_init(GtkApplication *app, VektorWidgetState *stateOut) { void vektor_uictrl_init(GtkApplication *app, VektorWidgetState *stateOut) {
GtkBuilder *builder = gtk_builder_new(); GtkBuilder *builder = gtk_builder_new();
GError *error = NULL; GError *error = NULL;
// TODO: .ui files as resources instead of sketchy relative paths // TODO: .ui files as resources instead of sketchy relative paths
if (!gtk_builder_add_from_file(builder, "./ui/main.ui", &error)) { if (!gtk_builder_add_from_file(builder, "./ui/main.ui", &error)) {
g_error("Fatal: %s", error->message); g_error("Fatal: %s", error->message);
} }
// Load css // Load css
GtkCssProvider* provider = gtk_css_provider_new(); GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_path(provider, "./ui/main.css"); gtk_css_provider_load_from_path(provider, "./ui/main.css");
gtk_style_context_add_provider_for_display(gdk_display_get_default(), gtk_style_context_add_provider_for_display(
GTK_STYLE_PROVIDER(provider), gdk_display_get_default(), GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
);
// populate state // populate state
stateOut->window = GTK_WINDOW(gtk_builder_get_object(builder, "main_window")); stateOut->window =
stateOut->workspacePaned = GTK_WINDOW(gtk_builder_get_object(builder, "main_window"));
GTK_PANED(gtk_builder_get_object(builder, "workspace_paned")); stateOut->workspacePaned =
stateOut->workspaceCanvas = GTK_PANED(gtk_builder_get_object(builder, "workspace_paned"));
GTK_PICTURE(gtk_builder_get_object(builder, "workspace")); stateOut->workspaceCanvas =
stateOut->workspaceButtonLinetool = GTK_PICTURE(gtk_builder_get_object(builder, "workspace"));
GTK_BUTTON(gtk_builder_get_object(builder, "button_linetool")); stateOut->workspaceButtonLinetool =
GTK_BUTTON(gtk_builder_get_object(builder, "button_linetool"));
// Set window properties // Set window properties
gtk_window_set_application(stateOut->window, app); gtk_window_set_application(stateOut->window, app);
gtk_window_set_title(stateOut->window, "Vektor"); gtk_window_set_title(stateOut->window, "Vektor");
gtk_window_set_default_size(stateOut->window, 800, 600); gtk_window_set_default_size(stateOut->window, 800, 600);
g_object_unref(builder); g_object_unref(builder);
} }
void vektor_uictrl_map(VektorWidgetState *state) { void vektor_uictrl_map(VektorWidgetState *state) {
// set the workspace divider to 7:3 ratio // set the workspace divider to 7:3 ratio
int window_width = gtk_widget_get_width(GTK_WIDGET(state->window)); int window_width = gtk_widget_get_width(GTK_WIDGET(state->window));
g_print("%i", window_width); g_print("%i", window_width);
gtk_paned_set_position(state->workspacePaned, 800 * .7); gtk_paned_set_position(state->workspacePaned, 800 * .7);
} }

View File

@@ -8,13 +8,13 @@ Global application widget state, holding references to
all the widgets used in internal logic of the program all the widgets used in internal logic of the program
*/ */
typedef struct VektorWidgetState { typedef struct VektorWidgetState {
GtkWindow *window; GtkWindow *window;
GtkPaned *workspacePaned; GtkPaned *workspacePaned;
GtkPicture *workspaceCanvas; GtkPicture *workspaceCanvas;
GtkButton* workspaceButtonLinetool; GtkButton *workspaceButtonLinetool;
// GtkWidget* Workspace // GtkWidget* Workspace
} VektorWidgetState; } VektorWidgetState;
void vektor_uictrl_init(GtkApplication *app, VektorWidgetState *stateOut); void vektor_uictrl_init(GtkApplication *app, VektorWidgetState *stateOut);

View File

@@ -9,59 +9,60 @@
#define VKTR_CANVAS_SIZE (VKTR_CANVAS_WIDTH * VKTR_CANVAS_HEIGHT * 4) #define VKTR_CANVAS_SIZE (VKTR_CANVAS_WIDTH * VKTR_CANVAS_HEIGHT * 4)
void vektor_canvas_init(VektorWidgetState *state, VektorCanvas *canvasOut) { void vektor_canvas_init(VektorWidgetState *state, VektorCanvas *canvasOut) {
canvasOut->canvasWidget = state->workspaceCanvas; canvasOut->canvasWidget = state->workspaceCanvas;
canvasOut->width = VKTR_CANVAS_WIDTH; canvasOut->width = VKTR_CANVAS_WIDTH;
canvasOut->height = VKTR_CANVAS_HEIGHT; canvasOut->height = VKTR_CANVAS_HEIGHT;
canvasOut->canvasPixels = g_malloc0(VKTR_CANVAS_SIZE); canvasOut->canvasPixels = g_malloc0(VKTR_CANVAS_SIZE);
canvasOut->canvasPixelBytes = canvasOut->canvasPixelBytes =
g_bytes_new(canvasOut->canvasPixels, VKTR_CANVAS_SIZE); g_bytes_new(canvasOut->canvasPixels, VKTR_CANVAS_SIZE);
canvasOut->canvasTexture = gdk_memory_texture_new( canvasOut->canvasTexture = gdk_memory_texture_new(
VKTR_CANVAS_WIDTH, VKTR_CANVAS_HEIGHT, GDK_MEMORY_R8G8B8A8, VKTR_CANVAS_WIDTH, VKTR_CANVAS_HEIGHT, GDK_MEMORY_R8G8B8A8,
canvasOut->canvasPixelBytes, VKTR_CANVAS_WIDTH * 4); canvasOut->canvasPixelBytes, VKTR_CANVAS_WIDTH * 4);
gtk_picture_set_paintable(canvasOut->canvasWidget, gtk_picture_set_paintable(canvasOut->canvasWidget,
GDK_PAINTABLE(canvasOut->canvasTexture)); GDK_PAINTABLE(canvasOut->canvasTexture));
gtk_picture_set_content_fit(GTK_PICTURE(canvasOut->canvasWidget),
// g_object_unref(bytes); GTK_CONTENT_FIT_CONTAIN);
// g_object_unref(bytes);
} }
/* Generate new texture based on canvasPixels*/ /* Generate new texture based on canvasPixels*/
void vektor_canvas_update(VektorCanvas *canvas) { void vektor_canvas_update(VektorCanvas *canvas) {
g_bytes_unref(canvas->canvasPixelBytes); g_bytes_unref(canvas->canvasPixelBytes);
canvas->canvasPixelBytes = canvas->canvasPixelBytes =
g_bytes_new(canvas->canvasPixels, VKTR_CANVAS_SIZE); g_bytes_new(canvas->canvasPixels, VKTR_CANVAS_SIZE);
g_object_unref(canvas->canvasTexture); g_object_unref(canvas->canvasTexture);
canvas->canvasTexture = canvas->canvasTexture = gdk_memory_texture_new(
gdk_memory_texture_new(canvas->width, canvas->height, GDK_MEMORY_R8G8B8A8, canvas->width, canvas->height, GDK_MEMORY_R8G8B8A8,
canvas->canvasPixelBytes, canvas->width * 4); canvas->canvasPixelBytes, canvas->width * 4);
gtk_picture_set_paintable(canvas->canvasWidget, gtk_picture_set_paintable(canvas->canvasWidget,
GDK_PAINTABLE(canvas->canvasTexture)); GDK_PAINTABLE(canvas->canvasTexture));
} }
void vektor_canvas_fill(VektorCanvas *canvas, VektorColor color) { void vektor_canvas_fill(VektorCanvas *canvas, VektorColor color) {
for (int x = 0; x < VKTR_CANVAS_WIDTH; x++) { for (int x = 0; x < VKTR_CANVAS_WIDTH; x++) {
for (int y = 0; y < VKTR_CANVAS_HEIGHT; y++) { for (int y = 0; y < VKTR_CANVAS_HEIGHT; y++) {
int i = (y * VKTR_CANVAS_WIDTH + x) * 4; int i = (y * VKTR_CANVAS_WIDTH + x) * 4;
canvas->canvasPixels[i + 0] = color.r; canvas->canvasPixels[i + 0] = color.r;
canvas->canvasPixels[i + 1] = color.g; canvas->canvasPixels[i + 1] = color.g;
canvas->canvasPixels[i + 2] = color.b; canvas->canvasPixels[i + 2] = color.b;
canvas->canvasPixels[i + 3] = color.a; canvas->canvasPixels[i + 3] = color.a;
}
} }
}
} }
void vektor_canvas_drawfrom(VektorFramebuffer *fb, VektorCanvas *target) { void vektor_canvas_drawfrom(VektorFramebuffer *fb, VektorCanvas *target) {
for (int x = 0; x < fb->width; x++) { for (int x = 0; x < fb->width; x++) {
for (int y = 0; y < fb->height; y++) { for (int y = 0; y < fb->height; y++) {
int i = (y * fb->width + x) * 4; int i = (y * fb->width + x) * 4;
target->canvasPixels[i + 0] = (guchar)fb->pixels[i + 0]; target->canvasPixels[i + 0] = (guchar)fb->pixels[i + 0];
target->canvasPixels[i + 1] = (guchar)fb->pixels[i + 1]; target->canvasPixels[i + 1] = (guchar)fb->pixels[i + 1];
target->canvasPixels[i + 2] = (guchar)fb->pixels[i + 2]; target->canvasPixels[i + 2] = (guchar)fb->pixels[i + 2];
target->canvasPixels[i + 3] = (guchar)fb->pixels[i + 3]; target->canvasPixels[i + 3] = (guchar)fb->pixels[i + 3];
}
} }
}
} }

View File

@@ -6,15 +6,15 @@
#include "uicontroller.h" #include "uicontroller.h"
typedef struct VektorCanvas { typedef struct VektorCanvas {
GtkPicture *canvasWidget; GtkPicture *canvasWidget;
// texture related stuff // texture related stuff
guchar *canvasPixels; guchar *canvasPixels;
GdkTexture *canvasTexture; GdkTexture *canvasTexture;
GBytes *canvasPixelBytes; GBytes *canvasPixelBytes;
int width; int width;
int height; int height;
} VektorCanvas; } VektorCanvas;
void vektor_canvas_init(VektorWidgetState *state, VektorCanvas *canvasOut); void vektor_canvas_init(VektorWidgetState *state, VektorCanvas *canvasOut);

View File

@@ -2,22 +2,22 @@
#define COLOR_H_ #define COLOR_H_
typedef struct VektorColor { typedef struct VektorColor {
unsigned char r; unsigned char r;
unsigned char g; unsigned char g;
unsigned char b; unsigned char b;
unsigned char a; unsigned char a;
} VektorColor; } VektorColor;
static VektorColor vektor_color_blank = (VektorColor){0, 0, 0, 0}; static VektorColor vektor_color_blank = (VektorColor){0, 0, 0, 0};
static inline VektorColor vektor_color_new(unsigned char r, unsigned char g, static inline VektorColor vektor_color_new(unsigned char r, unsigned char g,
unsigned char b, unsigned char a) { unsigned char b, unsigned char a) {
return (VektorColor){r, g, b, a}; return (VektorColor){r, g, b, a};
} }
static inline VektorColor vektor_color_solid(unsigned char r, unsigned char g, static inline VektorColor vektor_color_solid(unsigned char r, unsigned char g,
unsigned char b) { unsigned char b) {
return (VektorColor){r, g, b, 255}; return (VektorColor){r, g, b, 255};
} }
#endif // COLOR_H_ #endif // COLOR_H_