summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inc/cad/solve.h50
-rw-r--r--src/main.c601
-rw-r--r--src/solve.c519
3 files changed, 639 insertions, 531 deletions
diff --git a/inc/cad/solve.h b/inc/cad/solve.h
new file mode 100644
index 0000000..cb2c4ac
--- /dev/null
+++ b/inc/cad/solve.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "cad/construction.h"
+
+#define SEARCH_DEPTH 16
+
+enum component_type {
+ COM_POINT,
+ COM_LINE,
+};
+
+struct component {
+ enum component_type type;
+
+ struct element *e;
+
+ bool show_when_placed;
+
+ double min;
+ double max;
+ bool min_max_init;
+ bool drawn;
+};
+
+extern char *constraint_type_name[];
+enum constraint_type {
+ CT_POINT_POINT_DISTANCE,
+ CT_POINT_LINE_DISTANCE,
+ CT_LINE_LINE_ANGLE,
+};
+
+struct path_step {
+ uint64_t i;
+ bool direction;
+};
+
+struct constraint {
+ enum constraint_type type;
+ double v;
+
+ struct component *c1;
+ struct component *c2;
+
+ uint64_t order;
+ struct path_step path[SEARCH_DEPTH];
+ bool forward;
+ bool used;
+};
+
+bool solve_constraints(struct constraint *constraints, size_t constraints_num, struct drawing *drawing);
diff --git a/src/main.c b/src/main.c
index aa85fa2..b6c4b0d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,12 +6,15 @@
#include <string.h>
#include "cad/construction.h"
+#include "cad/solve.h"
#define TEXT_OFFSET 0.4
#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)
+
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);
@@ -135,13 +138,27 @@ void plot_arc_between_style(struct point c, struct point p1, struct point p2, en
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 0 0 %f %f\" fill=\"none\" />\n", style_str, p2.pos[0], -p2.pos[1], r, r, p1.pos[0], -p1.pos[1]);
+ 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) {
@@ -563,24 +580,6 @@ void create_drawing(struct drawing* drawing, struct element **line_start, struct
create_rounded_3line(drawing, round_rad, *line_start, line_corner, *line_end, line_ctr, line_bend_start, line_bend_end);
}
-enum component_type {
- COM_POINT,
- COM_LINE,
-};
-
-struct component {
- enum component_type type;
-
- struct element *e;
-
- bool show_when_placed;
-
- double min;
- double max;
- bool min_max_init;
- bool drawn;
-};
-
void extend_line_to(struct component* c, struct point *p) {
assert(c->type == COM_LINE);
@@ -595,87 +594,6 @@ void extend_line_to(struct component* c, struct point *p) {
}
}
-enum constraint_type {
- CT_POINT_POINT_DISTANCE,
- CT_POINT_LINE_DISTANCE,
- CT_LINE_LINE_ANGLE,
-};
-
-char *constraint_type_name[] = {
- [CT_POINT_POINT_DISTANCE] = "Point Point Distance",
- [CT_POINT_LINE_DISTANCE] = "Point Line Distance",
- [CT_LINE_LINE_ANGLE] = "Line Line Angle",
-};
-
-#define SEARCH_DEPTH 16
-
-struct path_step {
- uint64_t i;
- bool direction;
-};
-
-struct constraint {
- enum constraint_type type;
- double v;
-
- struct component *c1;
- struct component *c2;
-
- uint64_t order;
- struct path_step path[SEARCH_DEPTH];
- bool forward;
- bool used;
-};
-
-struct frontier {
- struct component *elems[32];
- size_t n;
-};
-
-bool frontier_scan(struct frontier *frontier, struct component *component) {
- for(size_t i = 0; i < sizeof(frontier->elems)/sizeof(frontier->elems[0]); i++) {
- if(frontier->elems[i] == component) return true;
- }
-
- return false;
-}
-
-void add_frontier(struct frontier *frontier, struct component *component) {
- assert(!frontier_scan(frontier, component));
- frontier->elems[frontier->n++] = component;
-}
-
-void build_angle_point_line(struct drawing *drawing, struct component *local_i, struct component *local_j, struct component *oppo_i) {
- assert(local_i->type == COM_LINE);
- assert(local_j->type == COM_POINT);
- struct element *theta = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *d = 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 = !oppo_i->show_when_placed,
- .result.type = ETYPE_LINE,
- .arg1 = local_j->e,
- .arg2 = local_i->e,
- .arg3 = theta,
- });
-
- oppo_i->e = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
- .hidden = !oppo_i->show_when_placed,
- .result.type = ETYPE_LINE,
- .arg1 = l,
- .arg2 = d,
- });
-}
-
struct smooth_line {
struct component l1;
struct component l2;
@@ -708,395 +626,6 @@ struct mid {
struct component p;
};
-struct angle_search_frame {
- size_t constraint_i;
- struct component *head;
- bool dir;
-};
-
-bool find_angle(struct constraint *constraints, size_t constraints_num, struct component *first_component, struct component *needle, struct path_step *path) {
- struct angle_search_frame frames[SEARCH_DEPTH];
- struct angle_search_frame *frame = frames;
-
- frame->constraint_i = 0;
- frame->head = first_component;
-
- bool *checked = calloc(sizeof(bool), constraints_num);
-
- while(frame >= frames) {
- assert(frame < frames + SEARCH_DEPTH);
- assert(frame >= frames);
-
- assert(frame->constraint_i <= constraints_num);
- if(frame->constraint_i == constraints_num) {
-#if 0
- printf("Dead end at: ");
- for(struct angle_search_frame *i = frames; i <= frame; i++) {
- printf("%ld -> ", i->constraint_i);
- }
- printf("\n");
-#endif
- frame--;
- frame->constraint_i++;
- continue;
- }
-
- if(checked[frame->constraint_i]) {
- frame->constraint_i++;
- continue;
- }
-
- struct constraint *constraint = &constraints[frame->constraint_i];
- if(constraint->type != CT_LINE_LINE_ANGLE) {
- frame->constraint_i++;
- continue;
- }
-
- struct component *other;
-
- if(constraint->c1 == frame->head) {
- other = constraint->c2;
- frame->dir = true;
- } else if(constraint->c2 == frame->head) {
- other = constraint->c1;
- frame->dir = false;
- } else {
- frame->constraint_i++;
- continue;
- }
-
- if(other == needle) {
- // printf("Found path %p %p: ", first_component, needle);
- for(struct angle_search_frame *i = frames; i <= frame; i++) {
- path[i - frames].i = i->constraint_i;
- path[i - frames].direction = i->dir;
- // printf("%ld [%d] [%p] -> ", i->constraint_i, i->dir, i->head);
- }
- // printf("\n");
- if(frame < frames + SEARCH_DEPTH-1) {
- path[frame - frames + 1].i = -1;
- }
- return true;
- }
-
- checked[frame->constraint_i] = true;
-
- frame++;
- frame->constraint_i = 0;
- frame->head = other;
- }
-
- return false;
-}
-
-void solve_constraints(struct constraint *constraints, size_t constraints_num, struct drawing *drawing) {
- struct frontier frontier = {};
- uint64_t order = 1;
-
- for(size_t i = 0; i < constraints_num; i++) {
- constraints[i].path[0].i = -1;
- constraints[i].forward = true;
- }
-
- // Step 1 Pick some point point distance constraint as the base
- for(size_t i = 0; i < constraints_num; i++) {
- assert(!constraints[i].used);
-
- if(constraints[i].type != CT_POINT_POINT_DISTANCE) continue;
-
- constraints[i].c1->e = insert_cmd(drawing, (struct command){
- .op = CMD_ORIGIN,
- .hidden = true,
- .result.type = ETYPE_POINT,
- });
-
- struct element *xaxis = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_X,
- .hidden = true,
- .result.type = ETYPE_LINE,
- });
-
- struct element *distance = insert_cmd(drawing, (struct command){
- .op = CMD_VALUE_INPUT,
- .result.type = ETYPE_VALUE,
- });
-
- struct element *c = insert_cmd(drawing, (struct command){
- .op = CMD_CIRCLE_CENTER_RADIUS,
- .hidden = true,
- .result.type = ETYPE_CIRCLE,
- .arg1 = constraints[i].c1->e,
- .arg2 = distance,
- });
-
- constraints[i].c2->e = insert_cmd(drawing, (struct command){
- .op = CMD_POINT_CIRCLE_LINE,
- .hidden = true,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = xaxis,
- });
-
- constraints[i].used = true;
- constraints[i].order = order++;
- add_frontier(&frontier, constraints[i].c1);
- add_frontier(&frontier, constraints[i].c2);
- break;
- }
-
- // Step 2 we iteratively expand the frontier from the selected base
- while(true) {
- for(size_t i = 0; i < constraints_num; i++) {
- if(constraints[i].used) continue;
-
- struct component *oppo_i;
- struct component *local_i;
-
- if(frontier_scan(&frontier, constraints[i].c1)) {
- oppo_i = constraints[i].c2;
- local_i = constraints[i].c1;
- } else if(frontier_scan(&frontier, constraints[i].c2)) {
- oppo_i = constraints[i].c1;
- local_i = constraints[i].c2;
- } else continue;
-
- for(size_t j = i+1; j < constraints_num; j++) {
- if(constraints[j].used) continue;
-
- struct component *oppo_j;
- struct component *local_j;
-
- if(frontier_scan(&frontier, constraints[j].c1)) {
- oppo_j = constraints[j].c2;
- local_j = constraints[j].c1;
- } else if(frontier_scan(&frontier, constraints[j].c2)) {
- oppo_j = constraints[j].c1;
- local_j = constraints[j].c2;
- } else continue;
-
- // One of the constraints can't be an angle one
- if(constraints[i].type == CT_LINE_LINE_ANGLE && constraints[j].type == CT_LINE_LINE_ANGLE) continue;
-
- if(oppo_i != oppo_j) {
- if(constraints[i].type == CT_LINE_LINE_ANGLE) {
- if(!find_angle(constraints, constraints_num, oppo_i, oppo_j, constraints[i].path)) continue;
-
- oppo_i = oppo_j;
- } else if(constraints[j].type == CT_LINE_LINE_ANGLE) {
- if(!find_angle(constraints, constraints_num, oppo_j, oppo_i, constraints[j].path)) continue;
-
- oppo_j = oppo_i;
- } else continue;
- }
-
-
- // printf("Detected %ld %ld\n", i, j);
-
- bool shown = oppo_i->show_when_placed;
-
- if(constraints[i].type == CT_POINT_POINT_DISTANCE
- && local_i->type == COM_POINT
- && constraints[j].type == CT_POINT_POINT_DISTANCE
- && 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,
- });
- } else if(constraints[i].type == CT_POINT_LINE_DISTANCE
- && local_i->type == COM_POINT
- && constraints[j].type == CT_POINT_LINE_DISTANCE
- && local_j->type == COM_POINT) {
- assert(oppo_i->type == COM_LINE);
-
- 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_LINE_CIRCLE_CIRCLE_TANGENT,
- .hidden = !shown,
- .result.type = ETYPE_LINE,
- .arg1 = c1,
- .arg2 = c2,
- });
- } else if(constraints[i].type == CT_POINT_LINE_DISTANCE
- && local_i->type == COM_LINE
- && constraints[j].type == CT_POINT_LINE_DISTANCE
- && local_j->type == COM_LINE) {
- 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 *l1 = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
- .hidden = !shown,
- .result.type = ETYPE_LINE,
- .arg1 = local_i->e,
- .arg2 = d1,
- });
-
- struct element *l2 = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
- .hidden = !shown,
- .result.type = ETYPE_LINE,
- .arg1 = local_j->e,
- .arg2 = d2,
- });
-
- oppo_i->e = insert_cmd(drawing, (struct command){
- .op = CMD_POINT_LINE_LINE,
- .hidden = !shown,
- .result.type = ETYPE_POINT,
- .arg1 = l1,
- .arg2 = l2,
- });
- } else if(constraints[i].type == CT_LINE_LINE_ANGLE
- && local_i->type == COM_LINE
- && constraints[j].type == CT_POINT_LINE_DISTANCE
- && local_j->type == COM_POINT) {
- assert(oppo_i->type == COM_LINE);
-
- constraints[i].forward = constraints[i].c1 == local_i;
- build_angle_point_line(drawing, local_i, local_j, oppo_i);
- } else if(constraints[i].type == CT_POINT_LINE_DISTANCE
- && local_i->type == COM_POINT
- && constraints[j].type == CT_LINE_LINE_ANGLE
- && local_j->type == COM_LINE) {
- assert(oppo_i->type == COM_LINE);
- constraints[j].forward = constraints[j].c1 == local_j;
-
- // @HACK Swap the two constraints to reuse the construction
- // steps. This sucks, and we need to figure out some better
- // way of doing it.
- struct component* tmp = local_i;
- local_i = local_j;
- local_j = tmp;
-
- i ^= j;
- j = j ^ i;
- i ^= j;
-
- build_angle_point_line(drawing, local_i, local_j, oppo_i);
- } else if(constraints[i].type == CT_POINT_LINE_DISTANCE
- && local_i->type == COM_LINE
- && constraints[j].type == CT_POINT_POINT_DISTANCE
- && 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 *l = insert_cmd(drawing, (struct command){
- .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
- .hidden = !shown,
- .result.type = ETYPE_LINE,
- .arg1 = local_i->e,
- .arg2 = d1,
- });
-
- struct element *c = 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_LINE,
- .hidden = !shown,
- .root = constraints[j].c2 == local_j,
- .result.type = ETYPE_POINT,
- .arg1 = c,
- .arg2 = l,
- });
- } else {
- printf("Unknown constraint combination %s and %s\n", constraint_type_name[constraints[i].type], constraint_type_name[constraints[j].type]);
- abort();
- }
-
- add_frontier(&frontier, oppo_i);
- constraints[i].used = true;
- constraints[i].order = order++;
- constraints[j].used = true;
- constraints[j].order = order++;
- goto candidate_found;
- }
- }
- // No candidate found
- break;
-
-candidate_found:
- ;
- }
-
-}
-
int main(int argc, char *argv[]) {
// struct element *line_start;
// struct element *line_bend_start;
@@ -1121,9 +650,9 @@ int main(int argc, char *argv[]) {
struct smooth_line line = {
.l1 = {.type = COM_LINE},
.l2 = {.type = COM_LINE},
- .corner = {.type = COM_POINT},
+ .corner = {.type = COM_POINT, .show_when_placed=false},
.corner_center = {.type = COM_POINT},
- .end = {.type = COM_POINT},
+ .end = {.type = COM_POINT, .show_when_placed=false},
.start = {.type = COM_POINT},
.perp1 = {.type = COM_LINE},
@@ -1135,14 +664,14 @@ int main(int argc, char *argv[]) {
struct box box = {
.corner = {
+ {.type = COM_POINT, .show_when_placed=false},
{.type = COM_POINT},
- {.type = COM_POINT},
- {.type = COM_POINT},
+ {.type = COM_POINT, .show_when_placed=false},
{.type = COM_POINT},
},
.side = {
- {.type = COM_LINE},
- {.type = COM_LINE},
+ {.type = COM_LINE, .show_when_placed=false},
+ {.type = COM_LINE, .show_when_placed=false},
{.type = COM_LINE},
{.type = COM_LINE},
},
@@ -1386,9 +915,9 @@ int main(int argc, char *argv[]) {
{
.type = CT_LINE_LINE_ANGLE,
- .v = M_PI+M_PI/2,
- .c1 = &box.side[0],
- .c2 = &box.side[3],
+ .v = M_PI/2,
+ .c1 = &box.side[3],
+ .c2 = &box.side[0],
},
{
.type = CT_LINE_LINE_ANGLE,
@@ -1398,9 +927,9 @@ int main(int argc, char *argv[]) {
},
{
.type = CT_LINE_LINE_ANGLE,
- .v = M_PI+M_PI/2.9,
- .c1 = &box.side[3],
- .c2 = &box.side[2],
+ .v = M_PI/2,
+ .c1 = &box.side[2],
+ .c2 = &box.side[3],
},
{
.type = CT_LINE_LINE_ANGLE,
@@ -1413,15 +942,15 @@ int main(int argc, char *argv[]) {
.type = CT_POINT_POINT_DISTANCE,
.v = 9.5,
.c1 = &line.corner_end,
- .c2 = &box_enter.p,
- // .c2 = &box.corner[0],
+ // .c2 = &box_enter.p,
+ .c2 = &box.corner[0],
},
{
.type = CT_POINT_POINT_DISTANCE,
.v = 10,
.c1 = &line.corner_start,
- .c2 = &box_enter.p,
- // .c2 = &box.corner[0],
+ // .c2 = &box_enter.p,
+ .c2 = &box.corner[0],
},
{
@@ -1433,8 +962,8 @@ int main(int argc, char *argv[]) {
{
.type = CT_LINE_LINE_ANGLE,
- .v = M_PI/4,
- .c1 = &box.side[0],
+ .v = DEG(-30),
+ .c1 = &box.side[3],
.c2 = &box_enter.l1,
},
{
@@ -1445,7 +974,7 @@ int main(int argc, char *argv[]) {
},
{
.type = CT_LINE_LINE_ANGLE,
- .v = M_PI/4,
+ .v = DEG(30),
.c1 = &box.side[3],
.c2 = &box_enter.l2,
},
@@ -1495,22 +1024,27 @@ int main(int argc, char *argv[]) {
.c2 = &box_enter.p,
},
- // {
- // .type = CT_POINT_POINT_DISTANCE,
- // .v = 10,
- // .c1 = &box_enter.p,
- // .c2 = &box.corner[0],
- // },
{
- .type = CT_POINT_LINE_DISTANCE,
- .v = 0,
- .c1 = &box.corner[0],
- .c2 = &components[3],
+ .type = CT_POINT_POINT_DISTANCE,
+ .v = 5,
+ .c1 = &box_enter.p,
+ .c2 = &box.corner[0],
},
+ // {
+ // .type = CT_POINT_LINE_DISTANCE,
+ // .v = 0,
+ // .c1 = &box.corner[0],
+ // .c2 = &components[3],
+ // },
};
struct drawing drawing = {};
solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
+ solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
+ solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
+ solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
+ solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
+ solve_constraints(constraints, sizeof(constraints)/sizeof(constraints[0]), &drawing);
double *params = malloc(sizeof(double) * (sizeof(constraints)/sizeof(constraints[0])));
for(size_t i = 0; i < sizeof(constraints)/sizeof(constraints[0]); i++) {
@@ -1558,10 +1092,7 @@ int main(int argc, char *argv[]) {
plot_line_between(components[1].e->point, components[5].e->point);
plot_line_between(components[5].e->point, components[0].e->point);
- // plot_line_between(line.corner_start.e->point, box.corner[0].e->point);
- // printf("%f %f %f\n", box.side[0].e->line.norm[0], box.side[0].e->line.norm[1], box.side[0].e->line.C);
// printf("%f %f %f\n", components[3].e->line.norm[0], components[3].e->line.norm[1], components[3].e->line.C);
- // plot_generic(*box.side[0].e);
plot_line_between(box.corner[0].e->point, box.corner[1].e->point);
plot_line_between(box.corner[1].e->point, box.corner[2].e->point);
plot_line_between(box.corner[2].e->point, box.corner[3].e->point);
@@ -1578,7 +1109,7 @@ int main(int argc, char *argv[]) {
// plot_generic(*bend[8].e);
// plot_generic(*box_enter.p.e);
// plot_generic(*box.corner[0].e);
- // plot_generic(*box.side[0].e);
+ // plot_generic(*box.side[1].e);
// plot_generic(*box_enter.l1.e);
// plot_line_style(box.side[0].e->line, LSTYLE_NORMAL);
// plot_line_style(box.side[1].e->line, LSTYLE_NORMAL);
@@ -1590,7 +1121,7 @@ int main(int argc, char *argv[]) {
// plot_generic(*line.corner_center.e);
// plot_angle(components[3].e->line, line.l1.e->line, 90);
// plot_angle(line.l2.e->line, line.perp2.e->line, -90);
-
+
if(true) {
for(size_t i = 0; i < sizeof(constraints)/sizeof(constraints[0]); i++) {
struct constraint *constraint = &constraints[i];
@@ -1624,15 +1155,25 @@ int main(int argc, char *argv[]) {
case CT_LINE_LINE_ANGLE: {
assert(constraint->c1->type == COM_LINE);
assert(constraint->c2->type == COM_LINE);
+
+ 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(constraint->c1->e->line, constraint->c2->e->line, constraint->v, &intersect, &p1, &p2);
+ 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(constraint->c1, &p1);
- extend_line_to(constraint->c2, &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;
@@ -1648,7 +1189,7 @@ int main(int argc, char *argv[]) {
assert(line->type == COM_LINE);
if(constraint->v == 0) {
- extend_line_to(line, &point->e->point);
+ // extend_line_to(line, &point->e->point);
}
} break;
}
@@ -1687,8 +1228,6 @@ int main(int argc, char *argv[]) {
}
}
- printf("Component addr %p", &box.side[1]);
-
// 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);
diff --git a/src/solve.c b/src/solve.c
new file mode 100644
index 0000000..fd0c7cd
--- /dev/null
+++ b/src/solve.c
@@ -0,0 +1,519 @@
+#include "cad/solve.h"
+
+#define SWAP(x, y) do { \
+ typeof(x) tmp = x; \
+ x = y; \
+ y = tmp; \
+ }while(0)
+
+char *constraint_type_name[] = {
+ [CT_POINT_POINT_DISTANCE] = "Point Point Distance",
+ [CT_POINT_LINE_DISTANCE] = "Point Line Distance",
+ [CT_LINE_LINE_ANGLE] = "Line Line Angle",
+};
+
+// @IMPROVE: We should do something better than this. I don't really know what.
+struct frontier {
+ struct component *elems[64];
+ size_t n;
+};
+
+static bool frontier_scan(struct frontier *frontier, struct component *component) {
+ for(size_t i = 0; i < sizeof(frontier->elems)/sizeof(frontier->elems[0]); i++) {
+ if(frontier->elems[i] == component) return true;
+ }
+
+ return false;
+}
+
+static void add_frontier(struct frontier *frontier, struct component *component) {
+ assert(!frontier_scan(frontier, component));
+ frontier->elems[frontier->n++] = component;
+}
+
+static void build_angle_point_line(struct drawing *drawing, struct component *local_i, struct component *local_j, struct component *oppo_i, bool swap) {
+ assert(local_i->e != NULL);
+ assert(local_j->e != NULL);
+ assert(local_i->type == COM_LINE);
+ assert(local_j->type == COM_POINT);
+ struct element *theta = insert_cmd(drawing, (struct command){
+ .op = CMD_VALUE_INPUT,
+ .result.type = ETYPE_VALUE,
+ });
+
+ struct element *d = insert_cmd(drawing, (struct command){
+ .op = CMD_VALUE_INPUT,
+ .result.type = ETYPE_VALUE,
+ });
+
+ if(swap) SWAP(theta, d);
+
+ struct element* l = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_POINT_LINE_ANGLE,
+ .hidden = !oppo_i->show_when_placed,
+ .result.type = ETYPE_LINE,
+ .arg1 = local_j->e,
+ .arg2 = local_i->e,
+ .arg3 = theta,
+ });
+ assert(l != NULL);
+
+ oppo_i->e = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
+ .hidden = !oppo_i->show_when_placed,
+ .result.type = ETYPE_LINE,
+ .arg1 = l,
+ .arg2 = d,
+ });
+}
+
+struct angle_search_frame {
+ size_t constraint_i;
+ struct component *head;
+ bool dir;
+};
+static bool find_angle(struct constraint *constraints, size_t constraints_num, struct component *first_component, struct component *needle, struct path_step *path) {
+ struct angle_search_frame frames[SEARCH_DEPTH];
+ struct angle_search_frame *frame = frames;
+
+ frame->constraint_i = 0;
+ frame->head = first_component;
+
+ bool *checked = calloc(sizeof(bool), constraints_num);
+
+ while(frame >= frames) {
+ assert(frame < frames + SEARCH_DEPTH);
+ assert(frame >= frames);
+
+ assert(frame->constraint_i <= constraints_num);
+ if(frame->constraint_i == constraints_num) {
+#if 0
+ printf("Dead end at: ");
+ for(struct angle_search_frame *i = frames; i <= frame; i++) {
+ printf("%ld -> ", i->constraint_i);
+ }
+ printf("\n");
+#endif
+ frame--;
+ frame->constraint_i++;
+ continue;
+ }
+
+ if(checked[frame->constraint_i]) {
+ frame->constraint_i++;
+ continue;
+ }
+
+ struct constraint *constraint = &constraints[frame->constraint_i];
+ if(constraint->type != CT_LINE_LINE_ANGLE) {
+ frame->constraint_i++;
+ continue;
+ }
+
+ struct component *other;
+
+ if(constraint->c1 == frame->head) {
+ other = constraint->c2;
+ frame->dir = true;
+ } else if(constraint->c2 == frame->head) {
+ other = constraint->c1;
+ frame->dir = false;
+ } else {
+ frame->constraint_i++;
+ continue;
+ }
+
+ if(other == needle) {
+ for(struct angle_search_frame *i = frames; i <= frame; i++) {
+ path[i - frames].i = i->constraint_i;
+ path[i - frames].direction = i->dir;
+ }
+ if(frame < frames + SEARCH_DEPTH-1) {
+ path[frame - frames + 1].i = -1;
+ }
+ return true;
+ }
+
+ checked[frame->constraint_i] = true;
+
+ frame++;
+ frame->constraint_i = 0;
+ frame->head = other;
+ }
+
+ return false;
+}
+
+struct solve_step {
+ size_t i;
+ size_t j;
+ bool i_forward;
+ bool j_forward;
+};
+
+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;
+
+ *c = i;
+ return true;
+ }
+
+ return false;
+}
+
+static size_t build_triangles(struct constraint *constraints, size_t constraints_num, size_t origin, struct solve_step *steps) {
+ struct frontier frontier = {};
+ uint64_t order = 1;
+
+ for(size_t i = 0; i < constraints_num; i++) {
+ if(constraints[i].used) continue;
+
+ constraints[i].path[0].i = -1;
+ constraints[i].forward = true;
+ }
+
+ constraints[origin].used = true;
+ constraints[origin].order = order++;
+ add_frontier(&frontier, constraints[origin].c1);
+ add_frontier(&frontier, constraints[origin].c2);
+
+ size_t steps_i = 0;
+
+ while(true) {
+ for(size_t i = 0; i < constraints_num; i++) {
+ if(constraints[i].used) continue;
+
+ struct component *oppo_i;
+ bool i_forward;
+
+ if(frontier_scan(&frontier, constraints[i].c1)) {
+ oppo_i = constraints[i].c2;
+ i_forward = true;
+ } else if(frontier_scan(&frontier, constraints[i].c2)) {
+ oppo_i = constraints[i].c1;
+ i_forward = false;
+ } else continue;
+
+ for(size_t j = i+1; j < constraints_num; j++) {
+ if(constraints[j].used) continue;
+
+ struct component *oppo_j;
+ bool j_forward;
+
+ if(frontier_scan(&frontier, constraints[j].c1)) {
+ oppo_j = constraints[j].c2;
+ j_forward = true;
+ } else if(frontier_scan(&frontier, constraints[j].c2)) {
+ oppo_j = constraints[j].c1;
+ j_forward = false;
+ } else continue;
+
+ // One of the constraints can't be an angle one
+ if(constraints[i].type == CT_LINE_LINE_ANGLE && constraints[j].type == CT_LINE_LINE_ANGLE) continue;
+
+ if(oppo_i != oppo_j) {
+ if(constraints[i].type == CT_LINE_LINE_ANGLE) {
+ if(!find_angle(constraints, constraints_num, oppo_i, oppo_j, constraints[i].path)) continue;
+
+ oppo_i = oppo_j;
+ } else if(constraints[j].type == CT_LINE_LINE_ANGLE) {
+ if(!find_angle(constraints, constraints_num, oppo_j, oppo_i, constraints[j].path)) continue;
+
+ oppo_j = oppo_i;
+ } else continue;
+ }
+
+ // constraints[i].forward = i_forward;
+ // constraints[j].forward = j_forward;
+
+ add_frontier(&frontier, oppo_i);
+ constraints[i].used = true;
+ constraints[i].order = order++;
+ constraints[j].used = true;
+ constraints[j].order = order++;
+ steps[steps_i].i = i;
+ steps[steps_i].j = j;
+ steps[steps_i].i_forward = i_forward;
+ steps[steps_i].j_forward = j_forward;
+ steps_i++;
+ goto candidate_found;
+ }
+ }
+ // No candidate found
+ break;
+
+candidate_found:
+ ;
+ }
+
+ return steps_i;
+}
+
+bool solve_constraints(struct constraint *constraints, size_t constraints_num, struct drawing *drawing) {
+ // Step 1 Pick some point point distance constraint as the base
+ size_t fix;
+ if(!fix_first(constraints, constraints_num, &fix)) {
+ return false;
+ }
+
+ // Build the root of the drawing
+ constraints[fix].c1->e = insert_cmd(drawing, (struct command){
+ .op = CMD_ORIGIN,
+ .hidden = true,
+ .result.type = ETYPE_POINT,
+ });
+
+ struct element *xaxis = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_X,
+ .hidden = true,
+ .result.type = ETYPE_LINE,
+ });
+
+ struct element *distance = insert_cmd(drawing, (struct command){
+ .op = CMD_VALUE_INPUT,
+ .result.type = ETYPE_VALUE,
+ });
+
+ struct element *c = insert_cmd(drawing, (struct command){
+ .op = CMD_CIRCLE_CENTER_RADIUS,
+ .hidden = true,
+ .result.type = ETYPE_CIRCLE,
+ .arg1 = constraints[fix].c1->e,
+ .arg2 = distance,
+ });
+
+ constraints[fix].c2->e = insert_cmd(drawing, (struct command){
+ .op = CMD_POINT_CIRCLE_LINE,
+ .hidden = true,
+ .result.type = ETYPE_POINT,
+ .arg1 = c,
+ .arg2 = xaxis,
+ });
+
+ struct solve_step *steps = malloc(sizeof(struct solve_step) * constraints_num);
+ size_t steps_num = build_triangles(constraints, constraints_num, fix, steps);
+
+ // printf("Solved in %ld steps\n", steps_num);
+
+ for(struct solve_step *step = steps; step < (steps + steps_num); step++) {
+ struct component *local_i;
+ struct component *oppo_i;
+ if(step->i_forward) {
+ local_i = constraints[step->i].c1;
+ oppo_i = constraints[step->i].c2;
+ } else {
+ local_i = constraints[step->i].c2;
+ oppo_i = constraints[step->i].c1;
+ }
+
+ struct component *local_j;
+ struct component *oppo_j;
+ if(step->j_forward) {
+ local_j = constraints[step->j].c1;
+ oppo_j = constraints[step->j].c2;
+ } else {
+ local_j = constraints[step->j].c2;
+ oppo_j = constraints[step->j].c1;
+ }
+
+ if(oppo_i != oppo_j) {
+ if(constraints[step->i].type == CT_LINE_LINE_ANGLE) {
+ oppo_i = oppo_j;
+ } else if(constraints[step->j].type == CT_LINE_LINE_ANGLE) {
+ // Do nothing, oppo_j isn't used below
+ } else {
+ assert(false);
+ }
+ }
+
+ assert(local_i->e != NULL);
+ assert(local_j->e != NULL);
+
+ bool shown = oppo_i->show_when_placed;
+
+ if(constraints[step->i].type == CT_POINT_POINT_DISTANCE
+ && local_i->type == COM_POINT
+ && constraints[step->j].type == CT_POINT_POINT_DISTANCE
+ && 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,
+ });
+ } else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE
+ && local_i->type == COM_POINT
+ && constraints[step->j].type == CT_POINT_LINE_DISTANCE
+ && local_j->type == COM_POINT) {
+ assert(oppo_i->type == COM_LINE);
+
+ 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_LINE_CIRCLE_CIRCLE_TANGENT,
+ .hidden = !shown,
+ .result.type = ETYPE_LINE,
+ .arg1 = c1,
+ .arg2 = c2,
+ });
+ } else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE
+ && local_i->type == COM_LINE
+ && constraints[step->j].type == CT_POINT_LINE_DISTANCE
+ && local_j->type == COM_LINE) {
+ 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 *l1 = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
+ .hidden = !shown,
+ .result.type = ETYPE_LINE,
+ .arg1 = local_i->e,
+ .arg2 = d1,
+ });
+
+ struct element *l2 = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
+ .hidden = !shown,
+ .result.type = ETYPE_LINE,
+ .arg1 = local_j->e,
+ .arg2 = d2,
+ });
+
+ oppo_i->e = insert_cmd(drawing, (struct command){
+ .op = CMD_POINT_LINE_LINE,
+ .hidden = !shown,
+ .result.type = ETYPE_POINT,
+ .arg1 = l1,
+ .arg2 = l2,
+ });
+ } else if(constraints[step->i].type == CT_LINE_LINE_ANGLE
+ && local_i->type == COM_LINE
+ && constraints[step->j].type == CT_POINT_LINE_DISTANCE
+ && local_j->type == COM_POINT) {
+ assert(oppo_i->type == COM_LINE);
+
+ constraints[step->i].forward = step->i_forward;
+ constraints[step->j].forward = step->j_forward;
+
+ build_angle_point_line(drawing, local_i, local_j, oppo_i, false);
+ } else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE
+ && local_i->type == COM_POINT
+ && constraints[step->j].type == CT_LINE_LINE_ANGLE
+ && local_j->type == COM_LINE) {
+ assert(oppo_i->type == COM_LINE);
+
+ constraints[step->i].forward = step->i_forward;
+ constraints[step->j].forward = step->j_forward;
+
+ build_angle_point_line(drawing, local_j, local_i, oppo_i, true);
+ } else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE
+ && local_i->type == COM_LINE
+ && constraints[step->j].type == CT_POINT_POINT_DISTANCE
+ && 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 *l = insert_cmd(drawing, (struct command){
+ .op = CMD_LINE_LINE_DISTANCE_PARALLEL,
+ .hidden = !shown,
+ .result.type = ETYPE_LINE,
+ .arg1 = local_i->e,
+ .arg2 = d1,
+ });
+
+ struct element *c = 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_LINE,
+ .hidden = !shown,
+ .root = constraints[step->j].c2 == local_j,
+ .result.type = ETYPE_POINT,
+ .arg1 = c,
+ .arg2 = l,
+ });
+ } else {
+ printf("Unknown constraint combination %s and %s\n", constraint_type_name[constraints[step->i].type], constraint_type_name[constraints[step->j].type]);
+ abort();
+ }
+
+ }
+
+ return true;
+}