summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inc/cad/solve.h18
-rw-r--r--inc/cad/svg.h19
-rw-r--r--inc/cad/topology.h50
-rw-r--r--inc/cad/util.h19
-rw-r--r--src/construction.c2
-rw-r--r--src/main.c810
-rw-r--r--src/solve.c160
-rw-r--r--src/svg.c312
-rw-r--r--src/topology.c39
9 files changed, 609 insertions, 820 deletions
diff --git a/inc/cad/solve.h b/inc/cad/solve.h
index 4eae272..85b902b 100644
--- a/inc/cad/solve.h
+++ b/inc/cad/solve.h
@@ -22,6 +22,20 @@ struct component {
bool drawn;
};
+struct alias {
+ struct component *alias;
+ struct component *target;
+};
+
+struct constraints {
+ struct constraint *elements;
+ size_t length;
+ size_t capacity;
+
+ struct alias aliases[16];
+ size_t aliases_num;
+};
+
extern char *constraint_type_name[];
enum constraint_type {
CT_POINT_POINT_DISTANCE,
@@ -49,4 +63,6 @@ struct constraint {
bool used;
};
-bool solve_constraints(struct constraint *constraints, size_t constraints_num, struct drawing *drawing);
+void alias_point(struct constraints *c, struct component *alias, struct component *target);
+void add_constraint(struct constraints *c, struct constraint *new);
+bool solve_constraints(struct constraints *constraints, struct drawing *drawing);
diff --git a/inc/cad/svg.h b/inc/cad/svg.h
new file mode 100644
index 0000000..68634b8
--- /dev/null
+++ b/inc/cad/svg.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "cad/solve.h"
+#include "cad/topology.h"
+
+enum LineStyle {
+ LSTYLE_NORMAL,
+ LSTYLE_CONSTRUCTION,
+ LSTYLE_INDICATOR,
+};
+#define TEXT_OFFSET 0.4
+
+void plot_line_between(struct point p1, struct point p2);
+void plot_arc_between_style(struct point c, struct point p1, struct point p2, enum LineStyle style);
+void plot_arc_between(struct point c, struct point p1, struct point p2);
+void plot_text(struct point p, double angle, char* str);
+
+void draw_constraints(struct constraints *constraints);
+void draw_topology(struct topology *topo);
diff --git a/inc/cad/topology.h b/inc/cad/topology.h
new file mode 100644
index 0000000..b3879b2
--- /dev/null
+++ b/inc/cad/topology.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <stddef.h>
+
+#define TOPO_MOVETO(c) \
+ { .cmd = {TOPO_MOVETO} }, \
+ { .arg = {c} } \
+
+#define TOPO_LINETO(c) \
+ { .cmd = {TOPO_LINETO} }, \
+ { .arg = {c} } \
+
+#define TOPO_ARCTO(center, end) \
+ { .cmd = {TOPO_ARCTO} }, \
+ { .arg = {center} }, \
+ { .arg = {end} } \
+
+#define TOPO_END() \
+ { .cmd = {TOPO_END} } \
+
+enum topology_op {
+ TOPO_MOVETO,
+ TOPO_LINETO,
+ TOPO_ARCTO,
+
+ TOPO_END,
+};
+
+struct topology_cmd {
+ enum topology_op op;
+};
+
+struct topology_arg {
+ struct component *c;
+};
+
+struct topology_elem {
+ union {
+ struct topology_cmd cmd;
+ struct topology_arg arg;
+ };
+};
+
+struct topology {
+ struct topology_elem *elements;
+ size_t length;
+ size_t capacity;
+};
+
+void add_fragment(struct topology *topo, struct topology_elem *new);
diff --git a/inc/cad/util.h b/inc/cad/util.h
new file mode 100644
index 0000000..440d29b
--- /dev/null
+++ b/inc/cad/util.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+static void resize_buffer(void **buffer, size_t *capacity, size_t elems, size_t elemSize) {
+ assert(elems > 0);
+ uint64_t newpower = (sizeof(elems) * CHAR_BIT) - __builtin_clzl(elems-1);
+ elems = 1 << newpower;
+
+ if(elems != *capacity) {
+ *capacity = elems;
+ *buffer = realloc(*buffer, *capacity * elemSize);
+ }
+}
+
diff --git a/src/construction.c b/src/construction.c
index 918ceef..6e3cd6e 100644
--- a/src/construction.c
+++ b/src/construction.c
@@ -155,6 +155,8 @@ void execute_drawing(struct drawing *drawing, double inputs[]) {
circle_line_intersect(current->arg1->circle, current->arg2->line, current->root, &current->result.point);
}break;
case CMD_POINT_CIRCLE_CIRCLE: {
+ assert(current->arg1->type == ETYPE_CIRCLE);
+ assert(current->arg2->type == ETYPE_CIRCLE);
assert(current->result.type == ETYPE_POINT);
vec2 between_centers;
glm_vec2_sub(current->arg1->circle.center, current->arg2->circle.center, between_centers);
diff --git a/src/main.c b/src/main.c
index 31756ab..8579ddf 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,105 +8,12 @@
#include "cad/construction.h"
#include "cad/solve.h"
-
-#define TEXT_OFFSET 0.4
+#include "cad/svg.h"
#define SETSIGN(b, v) ((v) * ((2 * (b)) - 1))
-#define SIGNOF(x) ((typeof(x))((x)>0) - ((x)<0))
#define DEG(x) ((x) * M_PI / 180.0)
-enum topology_op {
- TOPO_MOVETO,
- TOPO_LINETO,
- TOPO_ARCTO,
-
- TOPO_END,
-};
-
-struct topology_cmd {
- enum topology_op op;
-};
-
-struct topology_arg {
- struct component *c;
-};
-
-struct topology_elem {
- union {
- struct topology_cmd cmd;
- struct topology_arg arg;
- };
-};
-
-#define MOVETO(c) \
- { .cmd = {TOPO_MOVETO} }, \
- { .arg = {c} } \
-
-#define LINETO(c) \
- { .cmd = {TOPO_LINETO} }, \
- { .arg = {c} } \
-
-#define ARCTO(center, end) \
- { .cmd = {TOPO_ARCTO} }, \
- { .arg = {center} }, \
- { .arg = {end} } \
-
-#define END() \
- { .cmd = {TOPO_END} } \
-
-struct topology {
- struct topology_elem *elements;
- size_t length;
- size_t capacity;
-};
-
-static void resize_buffer(void **buffer, size_t *capacity, size_t elems, size_t elemSize) {
- assert(elems > 0);
- uint64_t newpower = (sizeof(elems) * CHAR_BIT) - __builtin_clzl(elems-1);
- elems = 1 << newpower;
-
- if(elems != *capacity) {
- *capacity = elems;
- *buffer = realloc(*buffer, *capacity * elemSize);
- }
-}
-
-static void topo_resize(struct topology *topo, uint64_t newcapacity) {
- resize_buffer((void**)&topo->elements, &topo->capacity, newcapacity, sizeof(struct topology_elem));
-}
-
-size_t frag_len(struct topology_elem *elems) {
- struct topology_elem *cur = elems;
- while(cur->cmd.op != TOPO_END) {
- switch(cur->cmd.op) {
- case TOPO_MOVETO:
- cur += 2;
- break;
- case TOPO_LINETO:
- cur += 2;
- break;
- case TOPO_ARCTO:
- cur += 3;
- break;
- case TOPO_END:
- abort();
- }
- }
-
- return cur - elems;
-}
-
-void add_topo_fragment(struct topology *topo, struct topology_elem *new) {
- size_t new_num = frag_len(new);
- if(new_num + topo->length > topo->capacity) {
- topo_resize(topo, new_num + topo->length);
- }
-
- memcpy(topo->elements + topo->length, new, new_num * sizeof(struct topology_elem));
- topo->length += new_num;
-}
-
#define PP_DISTANCE(C1, C2, D) \
{ \
.type = CT_POINT_POINT_DISTANCE, \
@@ -139,86 +46,10 @@ void add_topo_fragment(struct topology *topo, struct topology_elem *new) {
.type = CT_END, \
}
-struct constraints {
- struct constraint *elements;
- size_t length;
- size_t capacity;
-};
-
-void add_constraint(struct constraints *c, struct constraint *new) {
- struct constraint *end = new;
- while(end->type != CT_END) end++;
- size_t new_num = end - new;
-
- if(c->length + new_num > c->capacity) {
- resize_buffer((void**)&c->elements, &c->capacity, c->length + new_num, sizeof(struct constraint));
- }
-
- memcpy(c->elements + c->length, new, new_num * sizeof(struct constraint));
- c->length += new_num;
-}
-
-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 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 plot_point(struct point p) {
printf("<circle cx=\"%f\" cy=\"%f\" r=\".4\" fill=\"black\" />\n", p.pos[0], -p.pos[1]);
}
-enum LineStyle {
- LSTYLE_NORMAL,
- LSTYLE_CONSTRUCTION,
- LSTYLE_INDICATOR,
-};
-
-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("<line %s x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\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_text(struct point p, double angle, char* str) {
- printf("<text text-anchor=\"middle\" dominant-baseline=\"central\" transform=\"translate(%f, %f) scale(1, -1) rotate(%f) scale(1, -1)\" font-size=\"0.75\">%s</text>\n", p.pos[0], -p.pos[1], angle * (180.0/M_PI), str);
-}
-
void plot_line_style(struct line l, enum LineStyle style) {
char *style_str;
switch(style) {
@@ -267,47 +98,6 @@ void plot_line(struct line l) {
plot_line_style(l, 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("<path %s d=\"M %f %f A %f %f 0 %d 0 %f %f\" fill=\"none\" />\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_circle(struct circle c) {
printf("<circle cx=\"%f\" cy=\"%f\" r=\"%f\" fill=\"none\" stroke=\"black\" stroke-width=\".1\" />\n", c.center[0], -c.center[1], c.radius);
}
@@ -328,415 +118,6 @@ void plot_generic(struct element e) {
}
}
-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 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);
-}
-
-struct element *create_perp(struct drawing *cmds, struct element *l, struct element *p, bool hide) {
- static struct element radius = {
- .type = ETYPE_VALUE,
- .value = 5, // Just an arbitrary number
- };
-
- struct element *c1 = insert_cmd(cmds, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .result.type = ETYPE_CIRCLE,
- .hidden = true,
- .arg1 = p,
- .arg2 = &radius,
- });
- struct element *p1 = insert_cmd(cmds, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .result.type = ETYPE_POINT,
- .hidden = true,
- .root = 0,
- .arg1 = c1,
- .arg2 = l,
- });
- struct element *p2 = insert_cmd(cmds, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .result.type = ETYPE_POINT,
- .hidden = true,
- .root = 1,
- .arg1 = c1,
- .arg2 = l,
- });
-
- struct element *c2 = insert_cmd(cmds, (struct command){
- .op = CMD_CIRCLE_CENTER_POINT,
- .result.type = ETYPE_CIRCLE,
- .hidden = true,
- .arg1 = p2,
- .arg2 = p1,
- });
- struct element *c3 = insert_cmd(cmds, (struct command){
- .op = CMD_CIRCLE_CENTER_POINT,
- .result.type = ETYPE_CIRCLE,
- .hidden = true,
- .arg1 = p1,
- .arg2 = p2,
- });
-
- struct element *perp1 = insert_cmd(cmds, (struct command){
- .op = CMD_POINT_CIRCLE_CIRCLE,
- .result.type = ETYPE_POINT,
- .hidden = true,
- .arg1 = c2,
- .arg2 = c3,
- });
- struct element *perp2 = insert_cmd(cmds, (struct command){
- .op = CMD_POINT_CIRCLE_CIRCLE,
- .result.type = ETYPE_POINT,
- .hidden = true,
- .root = 1,
- .arg1 = c2,
- .arg2 = c3,
- });
-
- return insert_cmd(cmds, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .result.type = ETYPE_LINE,
- .hidden = hide,
- .arg1 = perp1,
- .arg2 = perp2,
- });
-}
-
-void create_rounded_3line(struct drawing *next, struct element *r, struct element *p1, struct element *p2, struct element *p3, struct element **center, struct element **start, struct element **end) {
- {
- struct element *par1;
- struct element *par2;
- {
- struct element *l = insert_cmd(next, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = p1,
- .arg2 = p2,
- });
-
- struct element *perp = create_perp(next, l, p2, true);
-
- struct element *c = insert_cmd(next, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = p2,
- .arg2 = r,
- });
-
- struct element *p = insert_cmd(next, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = perp,
- });
-
- par1 = create_perp(next, perp, p, true);
- }
-
- {
- struct element *l = insert_cmd(next, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = p2,
- .arg2 = p3,
- });
-
- struct element *perp = create_perp(next, l, p2, true);
-
- struct element *c = insert_cmd(next, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = p2,
- .arg2 = r,
- });
-
- struct element *p = insert_cmd(next, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = perp,
- });
-
- par2 = create_perp(next, perp, p, true);
- }
-
- struct element *pc = insert_cmd(next, (struct command){
- .op = CMD_POINT_LINE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = par1,
- .arg2 = par2,
- });
- *center = pc;
-
- struct element *c = insert_cmd(next, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = pc,
- .arg2 = r,
- });
-
- {
- struct element *l = insert_cmd(next, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = p1,
- .arg2 = p2,
- });
-
- struct element *p = insert_cmd(next, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = l,
- });
- *start = p;
- }
-
- {
- struct element *l = insert_cmd(next, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = p2,
- .arg2 = p3,
- });
-
- struct element *p = insert_cmd(next, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = l,
- });
- *end = p;
- }
-
- }
-}
-
-void create_drawing(struct drawing* drawing, struct element **line_start, struct element **line_bend_start, struct element **line_ctr, struct element **line_bend_end, struct element **line_end) {
- *line_start = insert_cmd(drawing, (struct command){
- .op = CMD_ORIGIN,
- .hidden = true,
- .result.type = ETYPE_POINT,
- });
-
- struct element *element_spacing = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *line_corner;
- {
- struct element *xaxis = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_X,
- .hidden = true,
- .result.type = ETYPE_LINE,
- });
-
- struct element *root_angle = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *l = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_POINT_LINE_ANGLE,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = *line_start,
- .arg2 = xaxis,
- .arg3 = root_angle,
- });
-
- struct element *c = insert_cmd(drawing, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = *line_start,
- .arg2 = element_spacing,
- });
-
- struct element *p = insert_cmd(drawing, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = l,
- });
- line_corner = p;
- }
-
- {
- struct element *li = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_POINT_POINT,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = *line_start,
- .arg2 = line_corner,
- });
-
- struct element *bend_angle = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .hidden = true,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *l = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_POINT_LINE_ANGLE,
- .hidden = true,
- .result.type = ETYPE_LINE,
- .arg1 = line_corner,
- .arg2 = li,
- .arg3 = bend_angle,
- });
-
- struct element *c = insert_cmd(drawing, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = line_corner,
- .arg2 = element_spacing,
- });
-
- struct element *p = insert_cmd(drawing, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = l,
- });
- *line_end = p;
- }
-
-
- struct element *round_rad = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .hidden = true,
- .result.type = ETYPE_VALUE,
- });
- create_rounded_3line(drawing, round_rad, *line_start, line_corner, *line_end, line_ctr, line_bend_start, line_bend_end);
-}
-
-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);
- }
-}
-
struct smooth_line {
struct component l1;
struct component l2;
@@ -817,16 +198,6 @@ struct mid init_mid() {
}
int main(int argc, char *argv[]) {
- // struct element *line_start;
- // struct element *line_bend_start;
- // struct element *line_ctr;
- // struct element *line_bend_end;
- // struct element *line_end;
-
- // struct drawing drawing = {};
- // create_drawing(&drawing, &line_start, &line_bend_start, &line_ctr, &line_bend_end, &line_end);
- // execute_drawing(&drawing, (double[]){8, M_PI * 0.5, M_PI * 0.4, .3});
-
struct constraints constraints = {0};
struct topology topo = {0};
@@ -840,14 +211,14 @@ int main(int argc, char *argv[]) {
{.type = COM_POINT},
};
- add_topo_fragment(&topo, (struct topology_elem[]){
- MOVETO(&components[1]),
- LINETO(&components[5]),
- LINETO(&components[0]),
- LINETO(&components[2]),
- LINETO(&components[1]),
+ add_fragment(&topo, (struct topology_elem[]){
+ TOPO_MOVETO(&components[1]),
+ TOPO_LINETO(&components[5]),
+ TOPO_LINETO(&components[0]),
+ TOPO_LINETO(&components[2]),
+ TOPO_LINETO(&components[1]),
- END(),
+ TOPO_END(),
});
add_constraint(&constraints, (struct constraint[]){
@@ -869,18 +240,21 @@ int main(int argc, char *argv[]) {
struct smooth_line line = init_smooth_line();
- add_topo_fragment(&topo, (struct topology_elem[]){
- MOVETO(&components[5]),
- LINETO(&line.corner_start),
- ARCTO(&line.corner_center, &line.corner_end),
- LINETO(&line.end),
+ add_fragment(&topo, (struct topology_elem[]){
+ TOPO_MOVETO(&line.start),
+ TOPO_LINETO(&line.corner_start),
+ TOPO_ARCTO(&line.corner_center, &line.corner_end),
+ TOPO_LINETO(&line.end),
- END(),
+ TOPO_END(),
});
+ alias_point(&constraints, &line.start, &components[5]);
+
add_constraint(&constraints, (struct constraint[]){
LL_ANGLE(&components[3], &line.l1, DEG(90)),
- POINT_ON_LINE(&components[5], &line.l1),
+
+ POINT_ON_LINE(&line.start, &line.l1),
LL_ANGLE(&line.l2, &line.l1, DEG(90)),
POINT_ON_LINE(&line.corner, &line.l1),
@@ -888,7 +262,7 @@ int main(int argc, char *argv[]) {
POINT_ON_LINE(&line.end, &line.l2),
PL_DISTANCE(&line.end, &line.l1, 10),
- PL_DISTANCE(&line.end, &components[3], 12),
+ PL_DISTANCE(&line.end, &components[4], 5),
PL_DISTANCE(&line.corner_center, &line.l1, 2),
PL_DISTANCE(&line.corner_center, &line.l2, -2),
@@ -907,14 +281,14 @@ int main(int argc, char *argv[]) {
});
struct box box = init_box();
- add_topo_fragment(&topo, (struct topology_elem[]){
- MOVETO(&box.corner[0]),
- LINETO(&box.corner[1]),
- LINETO(&box.corner[2]),
- LINETO(&box.corner[3]),
- LINETO(&box.corner[0]),
-
- END(),
+ add_fragment(&topo, (struct topology_elem[]){
+ TOPO_MOVETO(&box.corner[0]),
+ TOPO_LINETO(&box.corner[1]),
+ TOPO_LINETO(&box.corner[2]),
+ TOPO_LINETO(&box.corner[3]),
+ TOPO_LINETO(&box.corner[0]),
+
+ TOPO_END(),
});
add_constraint(&constraints, (struct constraint[]){
@@ -964,11 +338,13 @@ int main(int argc, char *argv[]) {
});
struct drawing drawing = {};
- solve_constraints(constraints.elements, constraints.length, &drawing);
+ solve_constraints(&constraints, &drawing);
+ // line.start.e = components[5].e;
double *params = malloc(sizeof(double) * (constraints.length));
for(size_t i = 0; i < constraints.length; i++) {
if(!constraints.elements[i].used) continue;
+ if(constraints.elements[i].order == 0) continue;
params[constraints.elements[i].order-1] = SETSIGN(constraints.elements[i].forward, constraints.elements[i].v);
@@ -1035,131 +411,7 @@ int main(int argc, char *argv[]) {
// printf("%f %f %f\n", components[3].e->line.norm[0], components[3].e->line.norm[1], components[3].e->line.C);
- if(true) {
- 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 intersect;
- line_line_intersect(constraint->c1->e->line, constraint->c2->e->line, &intersect);
- } break;
- case CT_POINT_LINE_DISTANCE:
- 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: {
- assert(constraint->c1->type == COM_POINT);
- assert(constraint->c2->type == COM_POINT);
- 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();
- }
- }
- }
-
- // plot_line_between(components[0].e->point, components[3].e->point);
- // plot_line_between(components[3].e->point, components[1].e->point);
- // plot_line_between(line_bend_end->point, line_end->point);
- // plot_arc_between(line_ctr->point, line_bend_start->point, line_bend_end->point);
- // plot_line_between(detector_right->point, decomposer_left->point);
- // plot_line_between(decomposer_right->point, solver_left->point);
- // plot_line_between(solver_right->point, solutions_left->point);
+ draw_constraints(&constraints);
printf("</svg>\n");
}
diff --git a/src/solve.c b/src/solve.c
index 1abc757..5c0016c 100644
--- a/src/solve.c
+++ b/src/solve.c
@@ -1,5 +1,8 @@
#include "cad/solve.h"
+#include "cad/util.h"
+#include <string.h>
+
#define SWAP(x, y) do { \
typeof(x) tmp = x; \
x = y; \
@@ -12,6 +15,31 @@ char *constraint_type_name[] = {
[CT_LINE_LINE_ANGLE] = "Line Line Angle",
};
+void alias_point(struct constraints *c, struct component *alias, struct component *target) {
+ assert(alias->type == COM_POINT);
+ assert(target->type == COM_POINT);
+
+ assert(c->aliases_num < 16);
+
+ struct alias *new = &c->aliases[c->aliases_num++];
+ new->alias = alias;
+ new->target = target;
+}
+
+void add_constraint(struct constraints *c, struct constraint *new) {
+ struct constraint *end = new;
+ while(end->type != CT_END) end++;
+ size_t new_num = end - new;
+
+ if(c->length + new_num > c->capacity) {
+ resize_buffer((void**)&c->elements, &c->capacity, c->length + new_num, sizeof(struct constraint));
+ }
+
+ memcpy(c->elements + c->length, new, new_num * sizeof(struct constraint));
+ c->length += new_num;
+}
+
+
// @IMPROVE: We should do something better than this. I don't really know what.
struct frontier {
struct component *elems[64];
@@ -28,6 +56,7 @@ static bool frontier_scan(struct frontier *frontier, struct component *component
static void add_frontier(struct frontier *frontier, struct component *component) {
assert(!frontier_scan(frontier, component));
+ assert(frontier->n < 64);
frontier->elems[frontier->n++] = component;
}
@@ -152,10 +181,10 @@ struct solve_step {
};
static bool fix_first(struct constraint *constraints, size_t constraints_num, size_t *c) {
-
for(size_t i = 0; i < constraints_num; i++) {
if(constraints[i].used) continue;
if(constraints[i].type != CT_POINT_POINT_DISTANCE) continue;
+ if(constraints[i].v == 0.0) continue;
*c = i;
return true;
@@ -186,6 +215,34 @@ static size_t build_triangles(struct constraint *constraints, size_t constraints
for(size_t i = 0; i < constraints_num; i++) {
if(constraints[i].used) continue;
+ if(constraints[i].type != CT_POINT_POINT_DISTANCE) continue;
+ if(constraints[i].v != 0.0) continue;
+
+ struct component *oppo;
+ bool forward;
+
+ if(frontier_scan(&frontier, constraints[i].c1)) {
+ oppo = constraints[i].c2;
+ forward = true;
+ } else if(frontier_scan(&frontier, constraints[i].c2)) {
+ oppo = constraints[i].c1;
+ forward = false;
+ } else continue;
+
+ add_frontier(&frontier, oppo);
+ constraints[i].order = 0;
+ constraints[i].used = true;
+
+ steps[steps_i].i = i;
+ steps[steps_i].j = i;
+ steps[steps_i].i_forward = forward;
+ steps[steps_i].j_forward = forward;
+ steps_i++;
+ }
+
+ for(size_t i = 0; i < constraints_num; i++) {
+ if(constraints[i].used) continue;
+
struct component *oppo_i;
bool i_forward;
@@ -331,39 +388,44 @@ static void draw_solution(struct constraint *constraints, size_t fix, struct sol
&& local_j->type == COM_POINT) {
assert(oppo_i->type == COM_POINT);
- struct element *d1 = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *d2 = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *c1 = insert_cmd(drawing, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = !shown,
- .result.type = ETYPE_CIRCLE,
- .arg1 = local_i->e,
- .arg2 = d1,
- });
-
- struct element *c2 = insert_cmd(drawing, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = !shown,
- .result.type = ETYPE_CIRCLE,
- .arg1 = local_j->e,
- .arg2 = d2,
- });
-
- oppo_i->e = insert_cmd(drawing, (struct command){
- .op = CMD_POINT_CIRCLE_CIRCLE,
- .hidden = !shown,
- .result.type = ETYPE_POINT,
- .arg1 = c1,
- .arg2 = c2,
- });
+ if(step->i == step->j) {
+ assert(constraints[step->i].v == 0);
+ oppo_i->e = local_i->e;
+ } else {
+ struct element *d1 = insert_cmd(drawing, (struct command){
+ .op = CMD_VALUE_INPUT,
+ .result.type = ETYPE_VALUE,
+ });
+
+ struct element *d2 = insert_cmd(drawing, (struct command){
+ .op = CMD_VALUE_INPUT,
+ .result.type = ETYPE_VALUE,
+ });
+
+ struct element *c1 = insert_cmd(drawing, (struct command){
+ .op = CMD_CIRCLE_CENTER_RADIUS,
+ .hidden = !shown,
+ .result.type = ETYPE_CIRCLE,
+ .arg1 = local_i->e,
+ .arg2 = d1,
+ });
+
+ struct element *c2 = insert_cmd(drawing, (struct command){
+ .op = CMD_CIRCLE_CENTER_RADIUS,
+ .hidden = !shown,
+ .result.type = ETYPE_CIRCLE,
+ .arg1 = local_j->e,
+ .arg2 = d2,
+ });
+
+ oppo_i->e = insert_cmd(drawing, (struct command){
+ .op = CMD_POINT_CIRCLE_CIRCLE,
+ .hidden = !shown,
+ .result.type = ETYPE_POINT,
+ .arg1 = c1,
+ .arg2 = c2,
+ });
+ }
} else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE
&& local_i->type == COM_POINT
&& constraints[step->j].type == CT_POINT_LINE_DISTANCE
@@ -508,20 +570,38 @@ static void draw_solution(struct constraint *constraints, size_t fix, struct sol
}
-bool solve_constraints(struct constraint *constraints, size_t constraints_num, struct drawing *drawing) {
- // Step 1 Pick some point point distance constraint as the base
+bool solve_constraints(struct constraints *constraints, struct drawing *drawing) {
+ // Replace all the aliased points with the point they point to
+ for(size_t i = 0; i < constraints->aliases_num; i++) {
+ struct alias *alias = &constraints->aliases[i];
+
+ for(size_t j = 0; j < constraints->length; j++) {
+ struct constraint *it = &constraints->elements[j];
+ if(it->c1 == alias->alias) it->c1 = alias->target;
+ if(it->c2 == alias->alias) it->c2 = alias->target;
+ }
+ }
+
+ // Pick some point point distance constraint as the base
size_t fix;
- if(!fix_first(constraints, constraints_num, &fix)) {
+ if(!fix_first(constraints->elements, constraints->length, &fix)) {
return false;
}
// Build triangles on that root
- struct solve_step *steps = malloc(sizeof(struct solve_step) * constraints_num);
- size_t steps_num = build_triangles(constraints, constraints_num, fix, steps);
+ struct solve_step *steps = malloc(sizeof(struct solve_step) * constraints->length);
+ size_t steps_num = build_triangles(constraints->elements, constraints->length, fix, steps);
// printf("Solved in %ld steps\n", steps_num);
- draw_solution(constraints, fix, steps, steps_num, drawing);
+ draw_solution(constraints->elements, fix, steps, steps_num, drawing);
+
+ // Fill out the aliased points out with the values from their targets
+ for(size_t i = 0; i < constraints->aliases_num; i++) {
+ struct alias *alias = &constraints->aliases[i];
+
+ memcpy(alias->alias, alias->target, sizeof(struct component));
+ }
return true;
}
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("<line %s x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\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("<path %s d=\"M %f %f A %f %f 0 %d 0 %f %f\" fill=\"none\" />\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("<text text-anchor=\"middle\" dominant-baseline=\"central\" transform=\"translate(%f, %f) scale(1, -1) rotate(%f) scale(1, -1)\" font-size=\"0.75\">%s</text>\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) {
+}
diff --git a/src/topology.c b/src/topology.c
new file mode 100644
index 0000000..10fc53f
--- /dev/null
+++ b/src/topology.c
@@ -0,0 +1,39 @@
+#include "cad/topology.h"
+#include "cad/util.h"
+
+#include <string.h>
+
+static void topo_resize(struct topology *topo, uint64_t newcapacity) {
+ resize_buffer((void**)&topo->elements, &topo->capacity, newcapacity, sizeof(struct topology_elem));
+}
+
+size_t frag_len(struct topology_elem *elems) {
+ struct topology_elem *cur = elems;
+ while(cur->cmd.op != TOPO_END) {
+ switch(cur->cmd.op) {
+ case TOPO_MOVETO:
+ cur += 2;
+ break;
+ case TOPO_LINETO:
+ cur += 2;
+ break;
+ case TOPO_ARCTO:
+ cur += 3;
+ break;
+ case TOPO_END:
+ abort();
+ }
+ }
+
+ return cur - elems;
+}
+
+void add_fragment(struct topology *topo, struct topology_elem *new) {
+ size_t new_num = frag_len(new);
+ if(new_num + topo->length > topo->capacity) {
+ topo_resize(topo, new_num + topo->length);
+ }
+
+ memcpy(topo->elements + topo->length, new, new_num * sizeof(struct topology_elem));
+ topo->length += new_num;
+}