feat: add rasterization primitives

This commit is contained in:
2026-03-04 15:41:20 +05:30
parent e7b99ed918
commit 1c3fc0c4bd
18 changed files with 386 additions and 152 deletions

5
.clang-format Normal file
View File

@@ -0,0 +1,5 @@
BasedOnStyle: LLVM
ColumnLimit: 80
BreakBeforeBraces: Attach
IndentWidth: 2
ContinuationIndentWidth: 4

2
.gitignore vendored
View File

@@ -1,7 +1,9 @@
.DS_Store .DS_Store
.vscode
.idea .idea
*.log *.log
tmp/ tmp/
.cache/ .cache/
build/ build/
.direnv/ .direnv/
*.ppm

View File

@@ -1,19 +0,0 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64",
"compileCommands": [
"${workspaceFolder}/build/compile_commands.json"
]
}
],
"version": 4
}

6
flake.lock generated
View File

@@ -16,11 +16,7 @@
"type" : "github" "type" : "github"
} }
}, },
"root": { "root" : {"inputs" : {"nixpkgs" : "nixpkgs"}}
"inputs": {
"nixpkgs": "nixpkgs"
}
}
}, },
"root" : "root", "root" : "root",
"version" : 7 "version" : 7

View File

@@ -3,30 +3,23 @@
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = outputs = {
{
self, self,
nixpkgs, nixpkgs,
}: } : let system = "x86_64-linux";
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs { inherit system; };
in in {
{
devShells.${system}.default = pkgs.mkShell { devShells.${system}.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs;
gcc [gcc clang -
clang-tools tools lldb
lldb
meson meson ninja pkg -
ninja config
pkg-config
gtk4 gtk4
gdb gdb];
];
shellHook = ""; shellHook = "";
}; };

View File

@@ -14,7 +14,9 @@ gtk = dependency('gtk4', required: true)
src = files( src = files(
'src/main.c', 'src/main.c',
'src/matrix.c', 'src/core/matrix.c',
'src/core/primitives.c',
'src/core/raster.c',
'src/ui/uicontroller.c', 'src/ui/uicontroller.c',
'src/ui/vektorcanvas.c' 'src/ui/vektorcanvas.c'
) )

View File

@@ -1,6 +1,8 @@
#include "matrix.h" #include "matrix.h"
#include <math.h> #include <math.h>
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}}};
} }
@@ -18,6 +20,10 @@ inline M33 m33_rotate(double theta) {
{{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) {
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)

View File

@@ -12,6 +12,7 @@ M33 m33_identity(void);
M33 m33_translate(double tx, double ty); M33 m33_translate(double tx, double ty);
M33 m33_scale(double sx, double sy); M33 m33_scale(double sx, double sy);
M33 m33_rotate(double rad); M33 m33_rotate(double rad);
M33 m33_shear(double theta_x, double theta_y);
M33 m33_add(const M33 m1, const M33 m2); M33 m33_add(const M33 m1, const M33 m2);
M33 m33_sub(const M33 m1, const M33 m2); M33 m33_sub(const M33 m1, const M33 m2);

47
src/core/primitives.c Normal file
View File

@@ -0,0 +1,47 @@
#include "primitives.h"
Polyline *mk_polyline(void) {
Polyline *pl = malloc(sizeof(Polyline));
pl->count = 0;
pl->capacity = 4;
pl->points = malloc(sizeof(V2) * pl->capacity);
return pl;
}
void add_point_polyline(Polyline *pl, V2 point) {
if (pl->count >= pl->capacity) {
pl->capacity *= 2;
pl->points = realloc(pl->points, sizeof(V2) * pl->capacity);
}
pl->points[pl->count++] = point;
}
void free_polyline(Polyline *pl) {
if (!pl)
return;
free(pl->points);
free(pl);
}
Polygon *mk_polygon(void) {
Polygon *pg = malloc(sizeof(Polygon));
pg->count = 0;
pg->capacity = 4;
pg->points = malloc(sizeof(V2) * pg->capacity);
return pg;
}
void add_point_polygon(Polygon *pg, V2 point) {
if (pg->count >= pg->capacity) {
pg->capacity *= 2;
pg->points = realloc(pg->points, sizeof(V2) * pg->capacity);
}
pg->points[pg->count++] = point;
}
void free_polygon(Polygon *pg) {
if (!pg)
return;
free(pg->points);
free(pg);
}

50
src/core/primitives.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef PRIMITIVES_H_
#define PRIMITIVES_H_
#include "stddef.h"
#include "stdlib.h"
#include "vector.h"
typedef struct {
V2 p1;
V2 p2;
} Line;
typedef struct {
V2 *points;
size_t count;
size_t capacity;
} Polyline;
typedef struct {
V2 *points;
size_t count;
size_t capacity;
} Polygon;
typedef struct {
V2 center;
double radius;
} Circle;
typedef enum { LINE, POLYLINE, POLYGON, CIRCLE } PrimitiveKind;
typedef struct {
PrimitiveKind kind;
union {
Line line;
Polyline *polyline;
Polygon *polygon;
Circle circle;
};
} Primitive;
Polyline *mk_polyline(void);
void add_point_polyline(Polyline *pl, V2 point);
void free_polyline(Polyline *pl);
Polygon *mk_polygon(void);
void add_point_polygon(Polygon *pl, V2 point);
void free_polygon(Polygon *pl);
#endif // PRIMITIVES_H_

80
src/core/raster.c Normal file
View File

@@ -0,0 +1,80 @@
#include "raster.h"
#include "primitives.h"
#include "stddef.h"
void add_edge(EdgeBuffer *buffer, Edge edge) {
if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->edges = realloc(buffer->edges, sizeof(Edge) * buffer->capacity);
}
buffer->edges[buffer->count++] = edge;
}
void flatten_line(EdgeBuffer *buffer, Line line) {
add_edge(buffer, (Edge){line.p1, line.p2, 0});
}
void flatten_polyline(EdgeBuffer *buffer, Polyline *line) {
for (size_t i = 0; i + 1 < line->count; i++) {
add_edge(buffer, (Edge){line->points[i], line->points[i + 1], 0});
}
}
void flatten_polygon(EdgeBuffer *buffer, Polygon *pg) {
size_t n = pg->count;
if (n < 3)
return;
for (size_t i = 0; i < n; i++) {
V2 p1 = pg->points[i];
V2 p2 = pg->points[(i + 1) % n];
int winding = (p1.y < p2.y) ? +1 : -1;
add_edge(buffer, (Edge){p1, p2, winding});
}
}
inline Framebuffer mk_framebuffer(unsigned int W, unsigned int H) {
Framebuffer fb = {.width = W, .height = H, .pixels = calloc(W * H * 3, 1)};
return fb;
}
inline void put_pixel(Framebuffer *fb, int x, int y, unsigned char r,
unsigned char g, unsigned char b) {
if ((unsigned)x >= fb->width || (unsigned)y >= fb->height)
return;
int i = (y * fb->width + x) * 3;
fb->pixels[i + 0] = r;
fb->pixels[i + 1] = g;
fb->pixels[i + 2] = b;
}
void draw_line(Framebuffer *fb, V2 a, V2 b, unsigned char r, unsigned char g,
unsigned char bl) {
int x0 = (int)a.x;
int y0 = (int)a.y;
int x1 = (int)b.x;
int y1 = (int)b.y;
int dx = abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1;
int dy = -abs(y1 - y0);
int sy = y0 < y1 ? 1 : -1;
int err = dx + dy;
for (;;) {
put_pixel(fb, x0, y0, r, g, bl);
if (x0 == x1 && y0 == y1)
break;
int e2 = 2 * err;
if (e2 >= dy) {
err += dy;
x0 += sx;
}
if (e2 <= dx) {
err += dx;
y0 += sy;
}
}
}

40
src/core/raster.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef RASTER_H_
#define RASTER_H_
#include "primitives.h"
#include "stddef.h"
#include "vector.h"
typedef struct {
V2 p1;
V2 p2;
int winding;
} Edge;
typedef struct {
Edge *edges;
size_t count;
size_t capacity;
} EdgeBuffer;
void add_edge(EdgeBuffer *edges, Edge edge);
void flatten_line(EdgeBuffer *edges, Line line);
void flatten_polyline(EdgeBuffer *edges, Polyline *line);
void flatten_polygon(EdgeBuffer *buffer, Polygon *line);
typedef struct {
unsigned int width;
unsigned int height;
unsigned char *pixels; // Flat RGB8 array
} Framebuffer;
Framebuffer mk_framebuffer(unsigned int width, unsigned int height);
void put_pixel(Framebuffer *fb, int x, int y, unsigned char r, unsigned char g,
unsigned char b);
void draw_line(Framebuffer *fb, V2 a, V2 b, unsigned char r, unsigned char g,
unsigned char bl);
#endif // RASTER_H_

View File

@@ -8,6 +8,16 @@ typedef struct {
double y; double y;
} V2; } V2;
typedef struct {
double x;
double y;
double z;
} V3;
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 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};
} }

View File

@@ -2,17 +2,18 @@
#include "stdio.h" #include "stdio.h"
#include "stdlib.h" #include "stdlib.h"
#include "./core/raster.h"
#include "./ui/uicontroller.h" #include "./ui/uicontroller.h"
#include "./ui/vektorcanvas.h" #include "./ui/vektorcanvas.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*)malloc(sizeof(VektorWidgetState)); VektorWidgetState *widget_state =
(VektorWidgetState *)malloc(sizeof(VektorWidgetState));
vektor_uictrl_init(app, widget_state); vektor_uictrl_init(app, widget_state);
VektorCanvas *canvas = (VektorCanvas *)malloc(sizeof(VektorCanvas)); VektorCanvas *canvas = (VektorCanvas *)malloc(sizeof(VektorCanvas));
@@ -21,12 +22,38 @@ static void activate(GtkApplication *app, gpointer user_data) {
vektor_canvas_fill(canvas, &red); vektor_canvas_fill(canvas, &red);
vektor_canvas_update(canvas); vektor_canvas_update(canvas);
g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map), widget_state); g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map),
widget_state);
gtk_window_present(widget_state->window); gtk_window_present(widget_state->window);
} }
void write_ppm(const char *path, const Framebuffer *fb) {
FILE *f = fopen(path, "wb");
if (!f)
abort();
fprintf(f, "P6\n%d %d\n255\n", fb->width, fb->height);
fwrite(fb->pixels, 1, fb->width * fb->height * 3, f);
fclose(f);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
Framebuffer fb = mk_framebuffer(256, 256);
EdgeBuffer edges = {0};
Polygon pg = *mk_polygon();
add_point_polygon(&pg, (V2){50, 50});
add_point_polygon(&pg, (V2){200, 80});
add_point_polygon(&pg, (V2){120, 200});
flatten_polygon(&edges, &pg);
for (size_t i = 0; i < edges.count; i++) {
draw_line(&fb, edges.edges[i].p1, edges.edges[i].p2, 255, 255, 255);
}
write_ppm("out.ppm", &fb);
GtkApplication *app; GtkApplication *app;
int status; int status;

View File

@@ -13,8 +13,10 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) {
} }
stateOut->window = GTK_WINDOW(gtk_builder_get_object(builder, "main_window")); stateOut->window = GTK_WINDOW(gtk_builder_get_object(builder, "main_window"));
stateOut->workspacePaned = GTK_PANED(gtk_builder_get_object(builder, "workspace_paned")); stateOut->workspacePaned =
stateOut->workspaceCanvas = GTK_PICTURE(gtk_builder_get_object(builder, "workspace")); GTK_PANED(gtk_builder_get_object(builder, "workspace_paned"));
stateOut->workspaceCanvas =
GTK_PICTURE(gtk_builder_get_object(builder, "workspace"));
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");

View File

@@ -1,7 +1,7 @@
#include "gtk/gtk.h" #include "gtk/gtk.h"
#include "vektorcanvas.h"
#include "uicontroller.h" #include "uicontroller.h"
#include "vektorcanvas.h"
#define VKTR_CANVAS_WIDTH 400 #define VKTR_CANVAS_WIDTH 400
#define VKTR_CANVAS_HEIGHT 400 #define VKTR_CANVAS_HEIGHT 400
@@ -13,33 +13,30 @@ void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut) {
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 = g_bytes_new(canvasOut->canvasPixels, VKTR_CANVAS_SIZE); canvasOut->canvasPixelBytes =
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_WIDTH, VKTR_CANVAS_HEIGHT, GDK_MEMORY_R8G8B8A8,
VKTR_CANVAS_HEIGHT, canvasOut->canvasPixelBytes, VKTR_CANVAS_WIDTH * 4);
GDK_MEMORY_R8G8B8A8,
canvasOut->canvasPixelBytes,
VKTR_CANVAS_WIDTH * 4
);
gtk_picture_set_paintable(canvasOut->canvasWidget, GDK_PAINTABLE(canvasOut->canvasTexture)); gtk_picture_set_paintable(canvasOut->canvasWidget,
GDK_PAINTABLE(canvasOut->canvasTexture));
// g_object_unref(bytes); // g_object_unref(bytes);
} }
void vektor_canvas_update(VektorCanvas *canvas) { void vektor_canvas_update(VektorCanvas *canvas) {
g_bytes_unref(canvas->canvasPixelBytes); g_bytes_unref(canvas->canvasPixelBytes);
canvas->canvasPixelBytes = g_bytes_new(canvas->canvasPixels, VKTR_CANVAS_SIZE); canvas->canvasPixelBytes =
g_bytes_new(canvas->canvasPixels, VKTR_CANVAS_SIZE);
g_object_unref(canvas->canvasTexture); g_object_unref(canvas->canvasTexture);
canvas->canvasTexture = gdk_memory_texture_new( canvas->canvasTexture =
canvas->width, canvas->height, gdk_memory_texture_new(canvas->width, canvas->height, GDK_MEMORY_R8G8B8A8,
GDK_MEMORY_R8G8B8A8, canvas->canvasPixelBytes, canvas->width * 4);
canvas->canvasPixelBytes,
canvas->width * 4
);
gtk_picture_set_paintable(canvas->canvasWidget, GDK_PAINTABLE(canvas->canvasTexture)); gtk_picture_set_paintable(canvas->canvasWidget,
GDK_PAINTABLE(canvas->canvasTexture));
} }
void vektor_canvas_fill(VektorCanvas *canvas, VektorCanvasColor *color) { void vektor_canvas_fill(VektorCanvas *canvas, VektorCanvasColor *color) {
@@ -55,11 +52,6 @@ void vektor_canvas_fill(VektorCanvas* canvas, VektorCanvasColor* color) {
} }
VektorCanvasColor vektor_color_new(guchar cr, guchar cg, guchar cb, guchar ca) { VektorCanvasColor vektor_color_new(guchar cr, guchar cg, guchar cb, guchar ca) {
VektorCanvasColor c = { VektorCanvasColor c = {.r = cr, .g = cg, .b = cb, .a = ca};
.r = cr,
.g = cg,
.b = cb,
.a = ca
};
return c; return c;
} }