From acfa01613f6e7fbeace88a15df61252156d39cf1 Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Thu, 20 Nov 2025 22:31:53 +0100 Subject: Separate everything out --- src/svg.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 src/svg.c (limited to 'src/svg.c') diff --git a/src/svg.c b/src/svg.c new file mode 100644 index 0000000..e84f17a --- /dev/null +++ b/src/svg.c @@ -0,0 +1,312 @@ +#include "cad/svg.h" + +#define SIGNOF(x) ((typeof(x))((x)>0) - ((x)<0)) + +void plot_line_between_style(struct point p1, struct point p2, enum LineStyle style) { + char *style_str; + switch(style) { + case LSTYLE_NORMAL: + style_str = "stroke=\"black\" stroke-width=\"0.2\""; + break; + case LSTYLE_CONSTRUCTION: + style_str = "stroke=\"blue\" stroke-width=\"0.1\" stroke-dasharray=\"0.7,0.2\" stroke-opacity=\"0.3\""; + break; + case LSTYLE_INDICATOR: + style_str = "stroke=\"blue\" stroke-width=\"0.1\" stroke-opacity=\"0.3\""; + break; + } + + printf("\n", style_str, p1.pos[0], -p1.pos[1], p2.pos[0], -p2.pos[1]); +} + +void plot_line_between(struct point p1, struct point p2) { + plot_line_between_style(p1, p2, LSTYLE_NORMAL); +} + +void plot_arc_between_style(struct point c, struct point p1, struct point p2, enum LineStyle style) { + char *style_str; + switch(style) { + case LSTYLE_NORMAL: + style_str = "stroke=\"black\" stroke-width=\"0.2\""; + break; + case LSTYLE_CONSTRUCTION: + abort(); + break; + case LSTYLE_INDICATOR: + style_str = "stroke=\"blue\" stroke-width=\"0.1\" stroke-opacity=\"0.3\""; + break; + } + + bool above_pi; + { + vec2 p1l; + glm_vec2_sub(p1.pos, c.pos, p1l); + glm_vec2_normalize(p1l); + vec2 p2l; + glm_vec2_sub(p2.pos, c.pos, p2l); + glm_vec2_normalize(p2l); + + glm_vec2_copy((vec2){-p2l[1], p2l[0]}, p2l); + + above_pi = glm_vec2_dot(p1l, p2l) < 0; + } + + // @COML: There's something missing here about which side of the arc we + // want. I think we can figure that out from the relation of the center and + // the points + vec2 t; + glm_vec2_sub(c.pos, p1.pos, t); + double r = glm_vec2_norm(t); + printf("\n", style_str, p2.pos[0], -p2.pos[1], r, r, above_pi, p1.pos[0], -p1.pos[1]); +} + +void plot_arc_between(struct point c, struct point p1, struct point p2) { + plot_arc_between_style(c, p1, p2, LSTYLE_NORMAL); +} + +void plot_text(struct point p, double angle, char* str) { + printf("%s\n", p.pos[0], -p.pos[1], angle * (180.0/M_PI), str); +} + +void plot_angle(struct line l1, struct line l2, double theta, struct point *intersect, struct point *p1, struct point *p2) { + line_line_intersect(l1, l2, intersect); + + struct circle c = { .radius = 1.3 }; + glm_vec2_copy(intersect->pos, c.center); + + circle_line_intersect(c, l1, 0, p1); + circle_line_intersect(c, l2, 0, p2); + + plot_arc_between_style(*intersect, *p2, *p1, LSTYLE_INDICATOR); + + vec2 l1v; + vec2 l2v; + glm_vec2_normalize_to(l1.norm, l1v); + glm_vec2_normalize_to(l2.norm, l2v); + + // Label + glm_vec2_negate(l2v); + + vec2 x; + glm_vec2_add(l1v, l2v, x); + glm_vec2_normalize(x); + + struct point label_point; + glm_vec2_copy(intersect->pos, label_point.pos); + glm_vec2_muladds(x, c.radius + TEXT_OFFSET, label_point.pos); + + double dot = x[0]; + double det = x[1]; + + double angle = atan2(det, dot); + + char buf[512]; + snprintf(buf, sizeof(buf), "%.1f°", theta); + plot_text(label_point, angle - M_PI/2, buf); + + // plot_line_style(l1, LSTYLE_CONSTRUCTION); + // plot_line_style(l2, LSTYLE_CONSTRUCTION); +} + +double project_point_to_line_distance(struct point p, struct line l) { + vec2 offset = {-p.pos[0], -p.pos[1]}; + double c = glm_vec2_dot(l.norm, offset); + + double det = (l.norm[0] * p.pos[1]) - (l.norm[1] * p.pos[0]); + det = -det; + + // @HACK There's a rounding error here that can cause d1 to end up + // negative. Just take the abolute value of it to get around that. + double d1 = fabs(glm_vec2_norm2(p.pos) - pow(c, 2)/glm_vec2_norm2(l.norm)); + assert(d1 >= 0.0); + double m1 = sqrt(d1 / glm_vec2_norm2(l.norm)); + + return SIGNOF(det) * m1; +} + +void extend_line_to(struct component* c, struct point *p) { + assert(c->type == COM_LINE); + + double val = project_point_to_line_distance(*p, c->e->line); + if(!c->min_max_init) { + c->min_max_init = true; + c->min = val; + c->max = val; + } else { + c->min = fmin(c->min, val - 0.1); + c->max = fmax(c->max, val + 0.1); + } +} + +static void plot_distance_indicator(struct point p1, struct point p2, double distance) { + vec2 dir; + glm_vec2_sub(p2.pos, p1.pos, dir); + glm_vec2_normalize(dir); + vec2 norm = {-dir[1], dir[0]}; + + struct point start; + struct point end; + { + glm_vec2_add(p1.pos, norm, start.pos); + glm_vec2_add(p2.pos, norm, end.pos); + plot_line_between_style(start, end, LSTYLE_INDICATOR); + } + + // The little wings to highlight the ends + vec2 tip = {.3, .3}; + glm_vec2_mul(norm, tip, tip); + { + struct point p1; + struct point p2; + glm_vec2_add(start.pos, tip, p1.pos); + glm_vec2_sub(start.pos, tip, p2.pos); + plot_line_between_style(p1, p2, LSTYLE_INDICATOR); + } + { + struct point p1; + struct point p2; + glm_vec2_add(end.pos, tip, p1.pos); + glm_vec2_sub(end.pos, tip, p2.pos); + plot_line_between_style(p1, p2, LSTYLE_INDICATOR); + } + + // The text + { + struct point p; + glm_vec2_lerp(start.pos, end.pos, 0.5, p.pos); + + glm_vec2_muladds(norm, TEXT_OFFSET, p.pos); + + double angle = atan2(dir[1], dir[0]); + + // Flip upside down labels + if(angle > M_PI/2) { + glm_vec2_muladds(norm, 0.1, p.pos); + angle -= M_PI; + } + if(angle < -M_PI/2) { + glm_vec2_muladds(norm, 0.1, p.pos); + angle += M_PI; + } + + assert(angle >= -M_PI); + assert(angle <= M_PI); + + char buf[512]; + snprintf(buf, sizeof(buf), "%.1f u", distance); + plot_text(p, angle, buf); + } +} + +void line_distance_to_point(struct line l, double d, struct point *p) { + glm_vec2_zero(p->pos); + glm_vec2_muladds(l.norm, l.C, p->pos); + glm_vec2_divs(p->pos, glm_vec2_norm2(l.norm), p->pos); + glm_vec2_negate(p->pos); + + vec2 perp = {l.norm[1], -l.norm[0]}; + glm_vec2_muladds(perp, d, p->pos); +} + +void draw_constraints(struct constraints *constraints) { + for(size_t i = 0; i < constraints->length; i++) { + struct constraint *constraint = &constraints->elements[i]; + if(!constraint->used) continue; + + switch(constraint->type) { + case CT_POINT_POINT_DISTANCE: { + assert(constraint->c1->type == COM_POINT); + assert(constraint->c2->type == COM_POINT); + if(constraint->v == 0.0) continue; + + plot_distance_indicator(constraint->c1->e->point, constraint->c2->e->point, constraint->v); + } break; + case CT_LINE_LINE_ANGLE: { + assert(constraint->c1->type == COM_LINE); + assert(constraint->c2->type == COM_LINE); + + // Check if it's more likely a "parallel" constraint + if(fabs(sin(constraint->v)) < 0.1) continue; + + struct component *l1 = constraint->c1; + struct component *l2 = constraint->c2; + // Negative angles are counterclockwise, we have to swap + // the arguments to get proper rendering. + if(constraint->v < 0) { + l1 = constraint->c2; + l2 = constraint->c1; + } + + struct point p1; + struct point p2; + struct point intersect; + plot_angle(l1->e->line, l2->e->line, constraint->v, &intersect, &p1, &p2); + + extend_line_to(constraint->c1, &intersect); + extend_line_to(constraint->c2, &intersect); + extend_line_to(l1, &p1); + extend_line_to(l2, &p2); + } break; + case CT_POINT_LINE_DISTANCE: { + struct component *line; + struct component *point; + if(constraint->c1->type == COM_LINE) { + line = constraint->c1; + point = constraint->c2; + } else { + line = constraint->c2; + point = constraint->c1; + } + assert(point->type == COM_POINT); + assert(line->type == COM_LINE); + + if(constraint->v > 0) { + double dist = project_point_to_line_distance(point->e->point, line->e->line); + struct point closest; + line_distance_to_point(line->e->line, dist, &closest); + + plot_distance_indicator(point->e->point, closest, constraint->v); + } + + extend_line_to(line, &point->e->point); + } break; + case CT_END: abort(); + } + } + + for(size_t i = 0; i < constraints->length; i++) { + struct constraint *constraint = &constraints->elements[i]; + if(!constraint->used) continue; + + switch(constraint->type) { + case CT_POINT_POINT_DISTANCE: { + } break; + case CT_LINE_LINE_ANGLE: { + assert(constraint->c1->type == COM_LINE); + assert(constraint->c2->type == COM_LINE); + + struct point p1; + struct point p2; + if(!constraint->c1->drawn) { + line_distance_to_point(constraint->c1->e->line, constraint->c1->min, &p1); + line_distance_to_point(constraint->c1->e->line, constraint->c1->max, &p2); + plot_line_between_style(p1, p2, LSTYLE_CONSTRUCTION); + constraint->c1->drawn = true; + } + + if(!constraint->c2->drawn) { + line_distance_to_point(constraint->c2->e->line, constraint->c2->min, &p1); + line_distance_to_point(constraint->c2->e->line, constraint->c2->max, &p2); + plot_line_between_style(p1, p2, LSTYLE_CONSTRUCTION); + constraint->c2->drawn = true; + } + } break; + case CT_POINT_LINE_DISTANCE: + break; + case CT_END: abort(); + } + } +} + +void draw_topology(struct topology *topo) { +} -- cgit v1.2.3