diff options
| author | Jesper Jensen <jesper@jnsn.dev> | 2026-07-24 20:19:48 +0200 |
|---|---|---|
| committer | Jesper Jensen <jesper@jnsn.dev> | 2026-07-24 20:19:48 +0200 |
| commit | 2667aa2806877d6fdd31b6ad34b0440197c14e1f (patch) | |
| tree | 2c7fc9ded8170e3794c505c3ff50f610dd538c3e /src | |
| parent | 1af2dfbde432af9f13e8fba09b7ca29f5de71863 (diff) | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/construction.c | 20 | ||||
| -rw-r--r-- | src/debug.c | 2 | ||||
| -rw-r--r-- | src/main.c | 177 | ||||
| -rw-r--r-- | src/solve.c | 141 | ||||
| -rw-r--r-- | src/svg.c | 158 |
5 files changed, 260 insertions, 238 deletions
diff --git a/src/construction.c b/src/construction.c index f175f79..f2a473a 100644 --- a/src/construction.c +++ b/src/construction.c @@ -35,6 +35,12 @@ bool circle_line_intersect(struct circle circle, struct line line, uint8_t root, return true; } +void normalize_line(struct line *line) { + double mag = glm_vec2_norm(line->norm); + glm_vec2_scale(line->norm, 1.0f/mag, line->norm); + line->C /= mag; +} + void line_through_points(struct point p1, struct point p2, struct line* l) { l->norm[0] = p1.pos[1] - p2.pos[1]; l->norm[1] = p2.pos[0] - p1.pos[0]; @@ -141,7 +147,9 @@ static void transform_part(struct command *cmd, const struct command *last_cmd, } void dump_program(struct drawing *drawing, double inputs[]) { + size_t i = 0; for(struct command *current = drawing->root; current != NULL; current = current->next) { + fprintf(stderr, "%ld ", i++); switch(current->op) { case CMD_VALUE_INPUT: { fprintf(stderr, "%p -> LIT(%d, %f) = %f\n", ¤t->result, current->dir, inputs[current->index], current->result.value); @@ -171,7 +179,7 @@ void dump_program(struct drawing *drawing, double inputs[]) { fprintf(stderr, "%p -> CCT(%p, %p) = (%fx + %fy + %f = 0)\n", ¤t->result, current->arg1, current->arg2, current->result.line.norm[0], current->result.line.norm[1], current->result.line.C); }break; case CMD_POINT_CIRCLE_LINE: { - fprintf(stderr, "%p -> PCL(%p, %p) = <%f, %f>\n", ¤t->result, current->arg1, current->arg2, current->result.point.pos[0], current->result.point.pos[1]); + fprintf(stderr, "%p -> PCL(%p, %p, %d) = <%f, %f>\n", ¤t->result, current->arg1, current->arg2, current->root, current->result.point.pos[0], current->result.point.pos[1]); }break; case CMD_POINT_CIRCLE_CIRCLE: { fprintf(stderr, "%p -> PCC(%p, %p) = <%f, %f>\n", ¤t->result, current->arg1, current->arg2, current->result.point.pos[0], current->result.point.pos[1]); @@ -201,7 +209,11 @@ void dump_program(struct drawing *drawing, double inputs[]) { void place_points(struct drawing *drawing, double inputs[]) { struct command *outer_object = drawing->root; + uint32_t i = 0; for(struct command *current = drawing->root; current != NULL; current = current->next) { + // if(i++ > 36) break; + current->placed = true; + switch(current->op) { case CMD_VALUE_INPUT: { assert(current->result.type == ETYPE_VALUE); @@ -244,7 +256,7 @@ void place_points(struct drawing *drawing, double inputs[]) { double value = current->arg3->value; value = current->root == 0 ? value : -value; glm_vec2_rotate(current->arg2->line.norm, value, current->result.line.norm); - // printf("%f %f\n", current->result.line.norm[0], current->result.line.norm[1]); + fprintf(stderr, "%f %f\n", current->result.line.norm[0], current->result.line.norm[1]); vec2 offset = {-current->arg1->point.pos[0], -current->arg1->point.pos[1]}; current->result.line.C = glm_vec2_dot(current->result.line.norm, offset); @@ -366,16 +378,14 @@ void place_points(struct drawing *drawing, double inputs[]) { double dot = glm_vec2_dot(current->arg2->line.norm, current->arg1->point.pos); current->result.value = (dot + current->arg2->line.C) / glm_vec2_norm(current->arg2->line.norm); - fprintf(stderr, "Measured Distance %f\n", current->result.value); }break; case CMD_MEASURE_LINE_LINE_ANGLE: { assert(current->arg1->type == ETYPE_LINE); assert(current->arg2->type == ETYPE_LINE); assert(current->result.type == ETYPE_VALUE); - double theta = atan2(current->arg1->line.norm[1], current->arg1->line.norm[0]) - atan2(current->arg2->line.norm[1], current->arg2->line.norm[0]); + double theta = atan2(current->arg2->line.norm[1], current->arg2->line.norm[0]) - atan2(current->arg1->line.norm[1], current->arg1->line.norm[0]); current->result.value = theta; - fprintf(stderr, "Measured Angle %f\n", current->result.value); }break; } // printf("%fx + %fy + %f = 0\n", cmd[2].line.norm[0], cmd[2].line.norm[1], cmd[2].line.C); diff --git a/src/debug.c b/src/debug.c index 97f0b3c..0a9767a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,4 +1,6 @@ #include "cad/debug.h" +#include "cglm/types.h" +#include "cglm/cglm.h" static const char *component_type_name[] = { [COM_POINT] = "point", @@ -1,17 +1,6 @@ #include "cad.h" #include "cad/debug.h" -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS -#include "cimgui/cimgui.h" -#include "cimgui/cimgui_impl.h" -#include <SDL2/SDL.h> - -#include <GL/gl.h> -#include <GL/glu.h> - -#define igGetIO igGetIO_Nil -SDL_Window *window = NULL; - struct smooth_line { struct component l1; struct component l2; @@ -157,12 +146,13 @@ void draw_from_constraints(struct constraints *c, struct topology *t, struct can draw_topology(cv, t); draw_constraints(cv, c); + draw_elements(cv, &drawing); end_drawing(cv); free_drawing(&drawing); } -int main2(int argc, char *argv[]) { +int main(int argc, char *argv[]) { struct constraints constraints = {}; struct topology topo = {}; @@ -293,166 +283,3 @@ int main2(int argc, char *argv[]) { free_topology(&topo); return 0; } - -int main(int argc, char* argv[]) { - - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - SDL_Log("failed to init: %s", SDL_GetError()); - return -1; - } - - // GL 3.0 + GLSL 130 - const char* glsl_version = "#version 130"; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - - // and prepare OpenGL stuff - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_DisplayMode current; - SDL_GetCurrentDisplayMode(0, ¤t); - float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(720 * main_scale), window_flags); - - if (window == NULL) { - SDL_Log("Failed to create window: %s", SDL_GetError()); - return -1; - } - - SDL_GLContext gl_context = SDL_GL_CreateContext(window); - SDL_GL_SetSwapInterval(1); // enable vsync - - - // check opengl version sdl uses - SDL_Log("opengl version: %s", (char*)glGetString(GL_VERSION)); - - // setup imgui - igCreateContext(NULL); - - //set docking - ImGuiIO* ioptr = igGetIO(); - ioptr->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - ioptr->ConfigFlags |= ImGuiConfigFlags_DockingEnable; - - // Setup scaling - ImGuiStyle* style = igGetStyle(); - ImGuiStyle_ScaleAllSizes(style, main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) - style->FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose) - ioptr->ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now. - ioptr->ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes. - - - ImGui_ImplSDL2_InitForOpenGL(window, gl_context); - ImGui_ImplOpenGL3_Init(glsl_version); - - igStyleColorsDark(NULL); - //ImFontAtlas_AddFontDefault(io.Fonts, NULL); - - - bool showDemoWindow = true; - bool showAnotherWindow = false; - ImVec4 clearColor; - clearColor.x = 0.45f; - clearColor.y = 0.55f; - clearColor.z = 0.60f; - clearColor.w = 1.00f; - - bool quit = false; - while (!quit) - { - SDL_Event e; - - // we need to call SDL_PollEvent to let window rendered, otherwise - // no window will be shown - while (SDL_PollEvent(&e) != 0) - { - ImGui_ImplSDL2_ProcessEvent(&e); - if (e.type == SDL_QUIT) - quit = true; - if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE && e.window.windowID == SDL_GetWindowID(window)) - quit = true; - } - - // start imgui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - igNewFrame(); - - if (showDemoWindow) - igShowDemoWindow(&showDemoWindow); - - // show a simple window that we created ourselves. - { - static float f = 0.0f; - static int counter = 0; - - igBegin("Hello, world!", NULL, 0); - igText("This is some useful text"); - igCheckbox("Demo window", &showDemoWindow); - igCheckbox("Another window", &showAnotherWindow); - - igSliderFloat("Float", &f, 0.0f, 1.0f, "%.3f", 0); - igColorEdit3("clear color", (float*)&clearColor, 0); - - ImVec2 buttonSize; - buttonSize.x = 0; - buttonSize.y = 0; - if (igButton("Button", buttonSize)) - counter++; - igSameLine(0.0f, -1.0f); - igText("counter = %d", counter); - - igText("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / igGetIO()->Framerate, igGetIO()->Framerate); - igEnd(); - } - - if (showAnotherWindow) - { - igBegin("imgui Another Window", &showAnotherWindow, 0); - igText("Hello from imgui"); - ImVec2 buttonSize; - buttonSize.x = 0; buttonSize.y = 0; - if (igButton("Close me", buttonSize)) - { - showAnotherWindow = false; - } - igEnd(); - } - - // render - igRender(); - SDL_GL_MakeCurrent(window, gl_context); - glViewport(0, 0, (int)ioptr->DisplaySize.x, (int)ioptr->DisplaySize.y); - glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); - if (ioptr->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); - SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); - igUpdatePlatformWindows(); - igRenderPlatformWindowsDefault(NULL,NULL); - SDL_GL_MakeCurrent(backup_current_window, backup_current_context); - } - SDL_GL_SwapWindow(window); - } - - // clean up - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - igDestroyContext(NULL); - - SDL_GL_DeleteContext(gl_context); - if (window != NULL) - { - SDL_DestroyWindow(window); - window = NULL; - } - SDL_Quit(); - - return 0; -} diff --git a/src/solve.c b/src/solve.c index 0eab611..67a7bff 100644 --- a/src/solve.c +++ b/src/solve.c @@ -143,6 +143,7 @@ static bool fix_first(struct constraint *constraints, size_t constraints_num, si if(constraints[i].c1->ein != NULL) continue; if(constraints[i].c2->ein != NULL) continue; + fprintf(stderr, "Fix %ld\n", i); *c = i; return true; } @@ -553,7 +554,7 @@ static size_t build_triangles(struct constraints *constraints_in, struct compone if(possibly_angle_c == NULL) continue; - fprintf(stderr, "Assembly %p and %p, %d %d %s %s\n", ein_a, ein_b, f1_c, f2_c, constraint_type_name[not_angle_c->type], constraint_type_name[possibly_angle_c->type]); + fprintf(stderr, "%ld %ld Assembly %p and %p, %d %d %s %s\n", i, j, ein_a, ein_b, f1_c, f2_c, constraint_type_name[not_angle_c->type], constraint_type_name[possibly_angle_c->type]); // We've fixed the subcomponents, so we have to fix // the whole thing @@ -640,6 +641,49 @@ nomatch: return steps_i; } +static void point_from_line_and_distance(struct drawing *drawing, struct constraint *constraints, size_t c1, size_t c2, struct element *line, struct element *point, int root, struct element **result) { + assert(line->type == ETYPE_LINE); + assert(point->type == ETYPE_POINT); + + struct element *d1 = insert_cmd(drawing, (struct command){ + .op = CMD_VALUE_INPUT, + .index = c1, + .dir = constraints[c1].forward, + .result.type = ETYPE_VALUE, + }); + struct element *d2 = insert_cmd(drawing, (struct command){ + .op = CMD_VALUE_INPUT, + .index = c2, + .dir = constraints[c2].forward, + .result.type = ETYPE_VALUE, + }); + + struct element *l = insert_cmd(drawing, (struct command){ + .op = CMD_LINE_LINE_DISTANCE_PARALLEL, + .hidden = false, + .result.type = ETYPE_LINE, + .arg1 = line, + .arg2 = d1, + }); + + struct element *c = insert_cmd(drawing, (struct command){ + .op = CMD_CIRCLE_CENTER_RADIUS, + .hidden = false, + .result.type = ETYPE_CIRCLE, + .arg1 = point, + .arg2 = d2, + }); + + *result = insert_cmd(drawing, (struct command){ + .op = CMD_POINT_CIRCLE_LINE, + .hidden = false, + .root = root, + .result.type = ETYPE_POINT, + .arg1 = c, + .arg2 = l, + }); +} + static void draw_point_from_2_distance(struct drawing *drawing, struct constraint *constraints, size_t i, size_t j, struct component *local_i, struct component *local_j, struct element **e, bool shown) { struct element *d1 = insert_cmd(drawing, (struct command){ .op = CMD_VALUE_INPUT, @@ -722,10 +766,33 @@ static void draw_for_subassembly(struct constraint *constraints, size_t fix, str // @HACK Let's only solve for an explicit angle for now assert(constraints[step->j].type == CT_LINE_LINE_ANGLE); - // The point we share with the subassembly struct element *p = NULL; - draw_point_from_2_distance(drawing, constraints, step->i, step->k, local_i, local_k, &p, false); - assert(p != NULL); + if(constraints[step->i].type == CT_POINT_POINT_DISTANCE + && local_i->type == COM_POINT + && constraints[step->k].type == CT_POINT_POINT_DISTANCE + && local_k->type == COM_POINT) { + + draw_point_from_2_distance(drawing, constraints, step->i, step->k, local_i, local_k, &p, false); + assert(p != NULL); + } else if(constraints[step->i].type == CT_POINT_LINE_DISTANCE + && local_i->type == COM_LINE + && constraints[step->k].type == CT_POINT_POINT_DISTANCE + && local_k->type == COM_POINT) { + assert(oppo_i->type == COM_POINT); + + point_from_line_and_distance(drawing, constraints, step->i, step->k, local_i->e, local_k->e, constraints[step->k].c2 == local_k, &p); + } else if(constraints[step->i].type == CT_POINT_POINT_DISTANCE + && local_i->type == COM_POINT + && constraints[step->k].type == CT_POINT_LINE_DISTANCE + && local_k->type == COM_LINE) { + assert(oppo_i->type == COM_POINT); + + point_from_line_and_distance(drawing, constraints, step->k, step->i, local_k->e, local_i->e, constraints[step->i].c2 == local_i, &p); + } else { + abort(); + } + + // The point we share with the subassembly // The line that matches the angle struct element *theta = insert_cmd(drawing, (struct command){ @@ -755,49 +822,6 @@ static void draw_for_subassembly(struct constraint *constraints, size_t fix, str }); } -void point_from_line_and_distance(struct drawing *drawing, struct constraint *constraints, size_t c1, size_t c2, struct element *line, struct element *point, int root, struct element **result) { - assert(line->type == ETYPE_LINE); - assert(point->type == ETYPE_POINT); - - struct element *d1 = insert_cmd(drawing, (struct command){ - .op = CMD_VALUE_INPUT, - .index = c1, - .dir = constraints[c1].forward, - .result.type = ETYPE_VALUE, - }); - struct element *d2 = insert_cmd(drawing, (struct command){ - .op = CMD_VALUE_INPUT, - .index = c2, - .dir = constraints[c2].forward, - .result.type = ETYPE_VALUE, - }); - - struct element *l = insert_cmd(drawing, (struct command){ - .op = CMD_LINE_LINE_DISTANCE_PARALLEL, - .hidden = false, - .result.type = ETYPE_LINE, - .arg1 = line, - .arg2 = d1, - }); - - struct element *c = insert_cmd(drawing, (struct command){ - .op = CMD_CIRCLE_CENTER_RADIUS, - .hidden = false, - .result.type = ETYPE_CIRCLE, - .arg1 = point, - .arg2 = d2, - }); - - *result = insert_cmd(drawing, (struct command){ - .op = CMD_POINT_CIRCLE_LINE, - .hidden = false, - .root = root, - .result.type = ETYPE_POINT, - .arg1 = c, - .arg2 = l, - }); -} - #define expand_constraint(NAME, IDX) \ struct constraint *NAME = &constraints[IDX]; \ struct component *NAME##_local; \ @@ -853,7 +877,7 @@ static void draw_for_double_subassembly(struct constraint *constraints, size_t f // The basic idea here is to create some new points for ax_oppo and bx_oppo // which we can then use to place a circle or line struct element *ax_oppo_locally = {}; - point_from_line_and_distance(drawing, constraints, step->j, step->i, a2_local->e, a1_local->e, constraints[step->j].c2 == a2_local, &ax_oppo_locally); + point_from_line_and_distance(drawing, constraints, step->j, step->i, a2_local->e, a1_local->e, constraints[step->j].c2 != a2_local, &ax_oppo_locally); struct element *bx_oppo_locally = {}; build_angle_point_line2(drawing, constraints, step->x, step->k, b2_local->e, b1_local->e, true, &bx_oppo_locally); @@ -1175,7 +1199,7 @@ static void draw_solution(struct constraint *constraints, size_t fix, struct sol // @COPYPASTA: Taken from above but with params swapped assert(oppo_i->type == COM_POINT); - point_from_line_and_distance(drawing, constraints, step->j, step->i, local_j->e, local_i->e, constraints[step->j].c2 == local_i, &oppo_i->e); + point_from_line_and_distance(drawing, constraints, step->j, step->i, local_j->e, local_i->e, constraints[step->i].c2 == local_i, &oppo_i->e); } else { CRASH("Unknown constraint combination %s and %s\n", constraint_type_name[constraints[step->i].type], constraint_type_name[constraints[step->j].type]); } @@ -1185,13 +1209,20 @@ static void draw_solution(struct constraint *constraints, size_t fix, struct sol assembly->last_command = drawing->tail; } -int unt64_t_compar(const void *a, const void *b) { +int uint64_t_compar(const void *a, const void *b) { uint64_t x = *(const uint64_t*)a; uint64_t y = *(const uint64_t*)b; return (x > y) - (x < y); } +int id_ptr_compar(const void *a, const void *b) { + const struct component *x = *(const struct component**)a; + const struct component *y = *(const struct component**)b; + + return (x->id > y->id) - (x->id < y->id); +} + bool solve_constraints(struct constraints *constraints, struct drawing *drawing, struct subassembly *assemblies, size_t *assemblies_num) { *assemblies_num = 0; // Replace all the aliased points with the point they point to @@ -1210,11 +1241,13 @@ bool solve_constraints(struct constraints *constraints, struct drawing *drawing, size_t component_num = 0; for(size_t i = 0; i < constraints->length; i++) { + constraints->elements[i].c1->id = constraints->elements[i].c1->id != 0 ? constraints->elements[i].c1->id : i+1; components[component_num++] = constraints->elements[i].c1; + constraints->elements[i].c2->id = constraints->elements[i].c2->id != 0 ? constraints->elements[i].c2->id : i+1; components[component_num++] = constraints->elements[i].c2; } - qsort(components, component_num, sizeof(struct component*), unt64_t_compar); + qsort(components, component_num, sizeof(struct component*), uint64_t_compar); size_t dest_num = 1; for(size_t i = 1; i < component_num; i++) { @@ -1222,6 +1255,8 @@ bool solve_constraints(struct constraints *constraints, struct drawing *drawing, components[dest_num++] = components[i]; } + qsort(components, dest_num, sizeof(struct component*), id_ptr_compar); + // Pick some point point distance constraint as the base size_t fix; while(fix_first(constraints->elements, constraints->length, &fix)) { @@ -1232,12 +1267,12 @@ bool solve_constraints(struct constraints *constraints, struct drawing *drawing, assemblies[*assemblies_num].steps_num = build_triangles(constraints, components, dest_num, fix, *assemblies_num+1, &assemblies[*assemblies_num]); assemblies[*assemblies_num].fix = fix; - // printf("Assembly %ld\n", *assemblies_num); + // printf("Assembly %ld %ld\n", *assemblies_num, fix); // for(size_t i = 0; i < assemblies[*assemblies_num].articulation_num; i++) { // printf(" Articulation %p\n", assemblies[*assemblies_num].articulation[i]); // } - // printf("Solved in %ld steps\n", steps_num); + // printf("Solved in %ld steps\n", assemblies[*assemblies_num].steps_num); draw_solution(constraints->elements, fix, assemblies[*assemblies_num].steps, assemblies[*assemblies_num].steps_num, drawing, &assemblies[*assemblies_num]); for(size_t i = 0; i < assemblies[*assemblies_num].articulation_num; i++) { @@ -2,8 +2,32 @@ #define SIGNOF(x) ((typeof(x))((x)>0) - ((x)<0)) -void plot_line_between_style(struct canvas *canvas, struct point p1, struct point p2, enum LineStyle style) { +static void end_coordinate(struct canvas *canvas) { + assert(canvas->state == CANVAS_COORDINATE); + fprintf(canvas->f, "</g>\n"); + canvas->state = CANVAS_DRAWING; +} + +static void begin_coordinate(struct canvas *canvas) { + if(canvas->state == CANVAS_COORDINATE) { + end_coordinate(canvas); + } + assert(canvas->state == CANVAS_DRAWING); + fprintf(canvas->f, "<g transform=\"translate(%lf, %lf)\">\n", canvas->next_offset[0], canvas->next_offset[1]); + // canvas->next_offset[0] += 7.5; + // canvas->next_offset[1] += 7.5; + canvas->state = CANVAS_COORDINATE; +} + +static bool point_valid(struct point p) { + return !isnan(p.pos[0]) && !isnan(p.pos[1]); +} + +void plot_line_between_style(struct canvas *canvas, struct point p1, struct point p2, enum LineStyle style) { + assert(canvas->state == CANVAS_COORDINATE); + + if(!point_valid(p1) || !point_valid(p2)) return; char *style_str; switch(style) { @@ -29,7 +53,9 @@ void plot_line_between(struct canvas *canvas, struct point p1, struct point p2) } void plot_arc_between_style(struct canvas *canvas, struct point c, struct point p1, struct point p2, enum LineStyle style) { - assert(canvas->state == CANVAS_DRAWING); + assert(canvas->state == CANVAS_COORDINATE); + + if(!point_valid(p1) || !point_valid(p2) || !point_valid(c)) return; char *style_str; switch(style) { @@ -72,14 +98,35 @@ void plot_arc_between(struct canvas *canvas, struct point c, struct point p1, st plot_arc_between_style(canvas, c, p1, p2, LSTYLE_NORMAL); } +void plot_point(struct canvas *canvas, struct point p1, enum LineStyle style) { + assert(canvas->state == CANVAS_COORDINATE); + + char *style_str = ""; + switch(style) { + case LSTYLE_NORMAL: + break; + case LSTYLE_CONSTRUCTION: + style_str = "fill=\"blue\""; + break; + case LSTYLE_INDICATOR: + break; + case LSTYLE_INDICATOR_INLINE: + break; + } + + fprintf(canvas->f, "<circle %s r=\".3\" cx=\"%f\" cy=\"%f\" />\n", style_str, p1.pos[0], -p1.pos[1]); +} + void plot_text(struct canvas *canvas, struct point p, double angle, char* str) { - assert(canvas->state == CANVAS_DRAWING); + assert(canvas->state == CANVAS_COORDINATE); + + if(!point_valid(p)) return; fprintf(canvas->f, "<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 canvas *canvas, struct line l1, struct line l2, double theta, struct point *intersect, struct point *p1, struct point *p2) { - assert(canvas->state == CANVAS_DRAWING); + assert(canvas->state == CANVAS_COORDINATE); line_line_intersect(l1, l2, intersect); @@ -148,7 +195,7 @@ void extend_line_to(struct component* c, struct point *p) { } static void plot_distance_indicator(struct canvas *canvas, struct point p1, struct point p2, double distance, bool offset) { - assert(canvas->state == CANVAS_DRAWING); + assert(canvas->state == CANVAS_COORDINATE); vec2 dir; glm_vec2_sub(p2.pos, p1.pos, dir); @@ -226,8 +273,101 @@ void line_distance_to_point(struct line l, double d, struct point *p) { glm_vec2_muladds(perp, d, p->pos); } +static bool clip_line_box(vec2 norm, float c, vec2 min, vec2 max, vec2 out[2]) { + vec2 p; + vec2 d = { -norm[1], norm[0] }; + + if (fabsf(norm[0]) > fabsf(norm[1])) { + p[0] = -c / norm[0]; + p[1] = 0.0f; + } else { + p[0] = 0.0f; + p[1] = -c / norm[1]; + } + + float t0 = -INFINITY; + float t1 = INFINITY; + + for (int i = 0; i < 2; i++) { + if (fabsf(d[i]) < DBL_EPSILON * 1e1) { + if (p[i] < min[i] || p[i] > max[i]) + return false; + } else { + float lo = (min[i] - p[i]) / d[i]; + float hi = (max[i] - p[i]) / d[i]; + + if (lo > hi) { + float tmp = lo; + lo = hi; + hi = tmp; + } + + t0 = fmaxf(t0, lo); + t1 = fminf(t1, hi); + } + } + + if (t0 > t1) return false; + + glm_vec2_copy(p, out[0]); + glm_vec2_copy(p, out[1]); + + glm_vec2_muladds(d, t0, out[0]); + glm_vec2_muladds(d, t1, out[1]); + + return true; +} + +void draw_elements(struct canvas *canvas, struct drawing *drawing) { + assert(canvas->state == CANVAS_DRAWING); + + for(struct command *current = drawing->root; current != NULL && current != drawing->error; current = current->next) { + // if(current->hidden) continue; + if(!current->placed) continue; + + if(current->op == CMD_ORIGIN) { + begin_coordinate(canvas); + } + + switch(current->result.type) { + case ETYPE_POINT: { + plot_point(canvas, current->result.point, LSTYLE_CONSTRUCTION); + }break; + case ETYPE_CIRCLE: { + }break; + case ETYPE_LINE: { + vec2 p[2] = {}; + struct line *line = ¤t->result.line; + normalize_line(line); + clip_line_box(current->result.line.norm, current->result.line.C, (vec2){-50, -50}, (vec2){50, 50}, p); + + struct point p1 = {}; + glm_vec2_copy(p[0], p1.pos); + struct point p2 = {}; + glm_vec2_copy(p[1], p2.pos); + + plot_line_between_style(canvas, p1, p2, LSTYLE_CONSTRUCTION); + + { + struct point closestPointOnLine = {}; + glm_vec2_muladds(line->norm, -line->C, closestPointOnLine.pos); + struct point p2 = {}; + glm_vec2_copy(closestPointOnLine.pos, p2.pos); + glm_vec2_muladds(line->norm, 2, p2.pos); + plot_line_between_style(canvas, closestPointOnLine, p2, LSTYLE_NORMAL); + } + }break; + case ETYPE_VALUE: { + }break; + } + } + + if(canvas->state == CANVAS_COORDINATE) end_coordinate(canvas); +} + void draw_constraints(struct canvas *canvas, struct constraints *constraints) { assert(canvas->state == CANVAS_DRAWING); + begin_coordinate(canvas); for(size_t i = 0; i < constraints->length; i++) { struct constraint *constraint = &constraints->elements[i]; @@ -332,10 +472,12 @@ void draw_constraints(struct canvas *canvas, struct constraints *constraints) { case CT_END: abort(); } } + end_coordinate(canvas); } void draw_topology(struct canvas *canvas, struct topology *topo) { assert(canvas->state == CANVAS_DRAWING); + begin_coordinate(canvas); struct component *head = NULL; for(size_t i = 0; i < topo->length; i++) { @@ -363,6 +505,8 @@ void draw_topology(struct canvas *canvas, struct topology *topo) { abort(); } } + + end_coordinate(canvas); } void begin_drawing(struct canvas *canvas) { @@ -380,9 +524,13 @@ void begin_drawing(struct canvas *canvas) { fprintf(canvas->f, "<line x1=\"-1000\" y1=\"0\" x2=\"1000\" y2=\"0\" stroke=\"black\" stroke-width=\"0.1\" stroke-opacity=\"0.4\" />\n"); fprintf(canvas->f, "<line y1=\"-1000\" x1=\"0\" y2=\"1000\" x2=\"0\" stroke=\"black\" stroke-width=\"0.1\" stroke-opacity=\"0.4\" />\n"); canvas->state = CANVAS_DRAWING; + glm_vec2_zero(canvas->next_offset); } void end_drawing(struct canvas *canvas) { + if(canvas->state == CANVAS_COORDINATE) { + end_coordinate(canvas); + } assert(canvas->state == CANVAS_DRAWING); fprintf(canvas->f, "</svg>\n"); |
