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

89
src/core/matrix.c Normal file
View File

@@ -0,0 +1,89 @@
#include "matrix.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) {
return (M33){{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
}
inline M33 m33_translate(double tx, double ty) {
return (M33){{{1, 0, tx}, {0, 1, ty}, {0, 0, 1}}};
}
inline M33 m33_scale(double sx, double sy) {
return (M33){{{sx, 0, 0}, {0, sy, 0}, {0, 0, 1}}};
}
inline M33 m33_rotate(double theta) {
return (M33){
{{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 res;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
res.m[i][j] = m1.m[i][j] + m2.m[i][j];
return res;
}
M33 m33_sub(const M33 m1, const M33 m2) {
M33 res;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
res.m[i][j] = m1.m[i][j] - m2.m[i][j];
return res;
}
M33 m33_mul(const M33 m1, const M33 m2) {
M33 res = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
res.m[i][j] += m1.m[i][k] * m2.m[k][j];
return res;
}
M33 m33_transpose(const M33 m) {
M33 res;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
res.m[i][j] = m.m[j][i];
return res;
}
// 2D affine transform inversion (not general 3x3 inversion)
M33 m33_inverse(const M33 m) {
M33 inv;
double det;
det = m.m[0][0] * m.m[1][1] - m.m[0][1] * m.m[1][0];
if (det == 0.0) {
return m33_identity();
}
double invDet = 1.0 / det;
inv.m[0][0] = m.m[1][1] * invDet;
inv.m[0][1] = -m.m[0][1] * invDet;
inv.m[1][0] = -m.m[1][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[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][1] = 0;
inv.m[2][2] = 1;
return inv;
}
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],
mat.m[1][0] * v.x + mat.m[1][1] * v.y + mat.m[1][2]};
}

25
src/core/matrix.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef MATRIX_H_
#define MATRIX_H_
#include "vector.h"
// Row major 3x3 matricies
typedef struct {
double m[3][3];
} M33;
M33 m33_identity(void);
M33 m33_translate(double tx, double ty);
M33 m33_scale(double sx, double sy);
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_sub(const M33 m1, const M33 m2);
M33 m33_mul(const M33 m1, const M33 m2);
M33 m33_transpose(const M33 m);
M33 m33_inverse(const M33 m);
V2 m33_transform(const M33 mat, const V2 v);
#endif // MATRIX_H_

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_

57
src/core/vector.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef VECTOR_H_
#define VECTOR_H_
#include "math.h"
typedef struct {
double x;
double y;
} 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) {
return (V2){v1.x + v2.x, v1.y + v2.y};
}
static inline V2 vec2_sub(const V2 v1, const V2 v2) {
return (V2){v1.x - v2.x, v1.y - v2.y};
}
static inline V2 vec2_mul(const V2 v1, const V2 v2) {
return (V2){v1.x * v2.x, v1.y * v2.y};
}
static inline V2 vec2_scale(const V2 v, const double k) {
return (V2){v.x * k, v.y * k};
}
static inline double vec2_dot(const V2 v1, const V2 v2) {
return v1.x * v2.x + v1.y * v2.y;
}
static inline double vec2_cross(const V2 a, const V2 b) {
return a.x * b.y - a.y * b.x;
}
static inline double vec2_norm(const V2 v) {
return sqrt(v.x * v.x + v.y * v.y);
}
static inline double vec2_quadrance(const V2 v) {
return (v.x * v.x + v.y * v.y);
}
static inline V2 vec2_normalize(const V2 v) {
return vec2_scale(v, 1 / vec2_norm(v));
}
#endif // VECTOR_H_