feat: implement miter joins

This commit is contained in:
2026-03-14 14:37:03 +05:30
parent e054fc4fe7
commit af6b0c4d30
7 changed files with 183 additions and 112 deletions

View File

@@ -1,5 +1,6 @@
#include "raster.h"
#include "epoxy/gl.h"
#include "glib.h"
#include "primitives.h"
#include "src/core/matrix.h"
#include "src/core/vector.h"
@@ -38,8 +39,8 @@ void vektor_polygon_tessellate(EdgeBuffer* buffer, VektorPolygon* polygon,
void vektor_circle_tessellate(EdgeBuffer* buffer, VektorCircle* circle,
size_t j, double scale) {
double err = 0.000025;
size_t res = PI * sqrt((scale * circle->radius) / (2 * err));
double err = 0.0025;
size_t res = MIN(PI * sqrt((scale * circle->radius) / (2 * err)), 20);
for (size_t i = 0; i < res; i++) {
double theta1 = (2 * PI * i) / res;
double theta2 = (2 * PI * (i + 1)) / res;
@@ -70,25 +71,31 @@ void vektor_rectangle_tessellate(EdgeBuffer* buffer, VektorRectangle* rct,
void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes,
double scale) {
EdgeBuffer edges = {0};
for (size_t i = 0; i < shapes->count; i++) {
EdgeBuffer edges = {0};
VektorPrimitive* p = &shapes->shapes[i].primitive;
VektorStyle style = shapes->shapes[i].style;
M33 transform = shapes->shapes[i].transform;
switch (p->kind) {
case VEKTOR_POLYLINE:
vektor_polyline_tessellate(&edges, p->polyline, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, FALSE);
break;
case VEKTOR_POLYGON:
vektor_polygon_tessellate(&edges, p->polygon, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break;
case VEKTOR_CIRCLE:
vektor_circle_tessellate(&edges, &p->circle, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break;
case VEKTOR_RECTANGLE:
vektor_rectangle_tessellate(&edges, &p->rectangle, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break;
default:
@@ -96,8 +103,6 @@ void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes,
break;
}
}
vektor_edges_to_triangles(vb, &edges, shapes);
}
void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2,
@@ -132,37 +137,91 @@ void vektor_vb_add_quad(VertexBuffer* vb, V2 a, V2 b, VektorColor color) {
vektor_vb_add_triangle(vb, tl, br, tr, color);
}
void vektor_edge_to_triangles(VertexBuffer* vb, Edge e,
VektorShapeBuffer* shape_buffer) {
float dx = e.p2.x - e.p1.x;
float dy = e.p2.y - e.p1.y;
float len = sqrtf(dx * dx + dy * dy);
if (len == 0)
return;
Edge edge_transform(const Edge* e, const M33* t) {
Edge out = *e;
out.p1 = m33_transform(*t, e->p1);
out.p2 = m33_transform(*t, e->p2);
return out;
}
float px =
-dy / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2);
float py =
dx / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2);
V2 v0 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
(V2){e.p1.x + px, e.p1.y + py});
V2 v1 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
(V2){e.p1.x - px, e.p1.y - py});
V2 v2 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
(V2){e.p2.x + px, e.p2.y + py});
V2 v3 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
(V2){e.p2.x - px, e.p2.y - py});
vektor_vb_add_triangle(vb, v0, v1, v2,
shape_buffer->shapes[e.shape_id].style.stroke_color);
vektor_vb_add_triangle(vb, v2, v1, v3,
shape_buffer->shapes[e.shape_id].style.stroke_color);
V2 line_intersection(V2 p, V2 r, V2 q, V2 s) {
float t = vec2_cross(vec2_sub(q, p), s) / vec2_cross(r, s);
return vec2_add(p, vec2_scale(r, t));
}
void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges,
VektorShapeBuffer* shape_buffer) {
M33* transform, VektorStyle style, bool closed) {
if (!edges || edges->count < 1)
return;
float hw = style.stroke_width * 0.5f;
for (size_t i = 0; i < edges->count; i++) {
vektor_edge_to_triangles(vb, edges->edges[i], shape_buffer);
Edge e = edge_transform(&edges->edges[i], transform);
V2 d = vec2_normalize(vec2_sub(e.p2, e.p1));
V2 n = vec2_perp(d);
V2 off = vec2_scale(n, hw);
V2 v0 = vec2_add(e.p1, off);
V2 v1 = vec2_sub(e.p1, off);
V2 v2 = vec2_add(e.p2, off);
V2 v3 = vec2_sub(e.p2, off);
vektor_vb_add_triangle(vb, v0, v1, v2, style.stroke_color);
vektor_vb_add_triangle(vb, v2, v1, v3, style.stroke_color);
}
size_t limit = closed ? edges->count : edges->count - 1;
for (size_t i = 0; i < limit; i++) {
Edge e1 = edge_transform(&edges->edges[i], transform);
Edge e2 =
edge_transform(&edges->edges[(i + 1) % edges->count], transform);
V2 corner = e1.p2;
V2 d1 = vec2_normalize(vec2_sub(e1.p2, e1.p1));
V2 d2 = vec2_normalize(vec2_sub(e2.p2, e2.p1));
V2 n1 = vec2_perp(d1);
V2 n2 = vec2_perp(d2);
V2 off1 = vec2_scale(n1, hw);
V2 off2 = vec2_scale(n2, hw);
V2 v10 = vec2_add(e1.p1, off1);
V2 v11 = vec2_sub(e1.p1, off1);
V2 v12 = vec2_add(e1.p2, off1);
V2 v13 = vec2_sub(e1.p2, off1);
V2 v20 = vec2_add(e2.p1, off2);
V2 v21 = vec2_sub(e2.p1, off2);
V2 v22 = vec2_add(e2.p2, off2);
V2 v23 = vec2_sub(e2.p2, off2);
V2 outer_miter = line_intersection(vec2_add(corner, off1), d1,
vec2_add(corner, off2), d2);
V2 inner_miter = line_intersection(vec2_sub(corner, off1), d1,
vec2_sub(corner, off2), d2);
V2 mo = vec2_sub(outer_miter, corner);
V2 mi = vec2_sub(inner_miter, corner);
V2 vo1 = line_intersection(corner, vec2_normalize(vec2_perp(mo)),
vec2_add(corner, off1), d1);
V2 vo2 = line_intersection(corner,
vec2_negate(vec2_normalize(vec2_perp(mo))),
vec2_add(corner, off2), d2);
V2 vi1 = line_intersection(corner, vec2_normalize(vec2_perp(mi)),
vec2_sub(corner, off1), d1);
V2 vi2 = line_intersection(corner,
vec2_negate(vec2_normalize(vec2_perp(mi))),
vec2_sub(corner, off2), d2);
vektor_vb_add_triangle(vb, vo1, outer_miter, vo2, style.stroke_color);
vektor_vb_add_triangle(vb, vi1, inner_miter, vi2, style.stroke_color);
}
}