diff --git a/hiddenwire.c b/hiddenwire.c index cc3eda5..507fc80 100644 --- a/hiddenwire.c +++ b/hiddenwire.c @@ -58,186 +58,26 @@ struct _seg_t { }; -#if 0 -typedef struct face face_t; -typedef struct poly poly_t; - -struct face -{ - float sides[3]; - face_t * next[3]; - int next_edge[3]; - int coplanar[3]; - int used; -}; - -// once this triangle has been used, it will be placed -// in a polygon group and fixed in a position relative to that group -struct poly -{ - int start_edge; - int printed; - - // local coordinates of the triangle vertices - float a; - float x2; - float y2; - float rot; - - // absolute coordintes of the triangle vertices - float p[3][2]; - - // todo: make this const and add backtracking - face_t * face; - poly_t * next[3]; - - poly_t * work_next; -}; - - -/* Compare two edges in two triangles. - * - * note that if the windings are all the same, the edges will - * compare in the opposite order (for example, the edge from 0 to 1 - * compares to the edge from 2 to 1 in the other triangle). - */ -static int -edge_eq2( - const stl_face_t * const t0, - const stl_face_t * const t1, - int e0, - int e1 -) -{ - const v3_t * const v00 = &t0->p[e0]; - const v3_t * const v01 = &t0->p[(e0+1) % 3]; - - const v3_t * const v10 = &t1->p[e1]; - const v3_t * const v11 = &t1->p[(e1+1) % 3]; - - if (v3_eq(v00, v11) && v3_eq(v01, v10)) - return 1; - - return 0; -} -#endif - void svg_line( const char * color, const float * p1, const float * p2, - int dash + float thick ) { - if (!dash) - { - printf("\n", - p1[0], - p1[1], - p2[0], - p2[1], - color - ); - return; - } - - // dashed line, split in the middle - const float dx = p2[0] - p1[0]; - const float dy = p2[1] - p1[1]; - - const float h1[] = { - p1[0] + dx*0.45, - p1[1] + dy*0.45, - }; - const float h2[] = { - p1[0] + dx*0.55, - p1[1] + dy*0.55, - }; - - - svg_line(color, p1, h1, 0); - svg_line(color, h2, p2, 0); + printf("\n", + p1[0], + p1[1], + p2[0], + p2[1], + color, + thick + ); } -#if 0 -void -rotate( - float * p, - const float * origin, - float a, - float x, - float y -) -{ - p[0] = cos(a) * x - sin(a) * y + origin[0]; - p[1] = sin(a) * x + cos(a) * y + origin[1]; -} - - -/* Rotate and translate a triangle */ -void -poly_position( - poly_t * const g, - const poly_t * const g_src, - float rot, - float trans_x, - float trans_y -) -{ - const face_t * const f = g->face; - const int start_edge = g->start_edge; - - float a = f->sides[(start_edge + 0) % 3]; - float c = f->sides[(start_edge + 1) % 3]; - float b = f->sides[(start_edge + 2) % 3]; - float x2 = (a*a + b*b - c*c) / (2*a); - float y2 = sqrt(b*b - x2*x2); - - // translate by trans_x/trans_y in the original ref frame - // to get the origin point - float origin[2]; - rotate(origin, g_src->p[0], g_src->rot, trans_x, trans_y); - - g->rot = g_src->rot + rot; - g->a = a; - g->x2 = x2; - g->y2 = y2; - -//fprintf(stderr, "%p %d %f %f %f %f => %f %f %f\n", f, start_edge, g->rot*180/M_PI, a, b, c, x2, y2, rot); - rotate(g->p[0], origin, g->rot, 0, 0); - rotate(g->p[1], origin, g->rot, a, 0); - rotate(g->p[2], origin, g->rot, x2, y2); -} - - -static void -enqueue( - poly_t * g, - poly_t * const new_g, - int at_head -) -{ - if (at_head) - { - new_g->work_next = g->work_next; - g->work_next = new_g; - return; - } - - // go to the end of the line - while (g->work_next) - g = g->work_next; - g->work_next = new_g; -} - - -static poly_t * poly_root; -static float poly_min[2], poly_max[2]; -#endif - static inline int v2_eq( const float p0[], @@ -363,407 +203,6 @@ if(0) fprintf(stderr, "collision: %.0f,%.0f,%.0f->%.0f,%.0f,%.0f %.0f,%.0f,%.0f- } -#if 0 -/** Check to see if two triangles overlap */ -int -overlap_poly( - const poly_t * const g1, - const poly_t * const g2 -) -{ - if (intersect(g1->p[0], g1->p[1], g2->p[0], g2->p[1])) - return 1; - if (intersect(g1->p[0], g1->p[1], g2->p[1], g2->p[2])) - return 1; - if (intersect(g1->p[0], g1->p[1], g2->p[2], g2->p[0])) - return 1; - - if (intersect(g1->p[1], g1->p[2], g2->p[0], g2->p[1])) - return 1; - if (intersect(g1->p[1], g1->p[2], g2->p[1], g2->p[2])) - return 1; - if (intersect(g1->p[1], g1->p[2], g2->p[2], g2->p[0])) - return 1; - - if (intersect(g1->p[2], g1->p[0], g2->p[0], g2->p[1])) - return 1; - if (intersect(g1->p[2], g1->p[0], g2->p[1], g2->p[2])) - return 1; - if (intersect(g1->p[2], g1->p[0], g2->p[2], g2->p[0])) - return 1; - - return 0; -} - - -/** Check to see if any triangles overlap */ -int -overlap_check( - const poly_t * g, - const poly_t * const new_g -) -{ - // special case -- if the root is the same as the one that we - // are checking, then it does not overlap - if (g == new_g) - return 0; - - while (g) - { - if (overlap_poly(g, new_g)) - return 1; - g = g->work_next; - } - - return 0; -} - - -/** recursively try to fix up the triangles. - * - * returns the maximum number of triangles added - */ -int -poly_build( - poly_t * const g -) -{ - face_t * const f = g->face; - const int start_edge = g->start_edge; - f->used = 1; - - // update the group's bounding box - for (int i = 0 ; i < 3 ; i++) - { - const float px = g->p[i][0]; - const float py = g->p[i][1]; - - if (px < poly_min[0]) poly_min[0] = px; - if (px > poly_max[0]) poly_max[0] = px; - - if (py < poly_min[1]) poly_min[1] = py; - if (py > poly_max[1]) poly_max[1] = py; - } - - - if (debug) fprintf(stderr, "%p: adding to poly\n", f); - - for(int pass = 0 ; pass < 2 ; pass++) - { - // for each edge, find the triangle that matches - for (int i = 0 ; i < 3 ; i++) - { - const int edge = (i + start_edge) % 3; - face_t * const f2 = f->next[edge]; - assert(f2 != NULL); - if (f2->used) - continue; - if (pass == 0 && f->coplanar[edge] == 0) - continue; - - // create a group that translates and rotates - // such that it lines up with this edge - float trans_x, trans_y, rotate; - if (i == 0) - { - trans_x = g->a; - trans_y = 0; - rotate = M_PI; - } else - if (i == 1) - { - trans_x = g->x2; - trans_y = g->y2; - rotate = -atan2(g->y2, g->a - g->x2); - } else - if (i == 2) - { - trans_x = 0; - trans_y = 0; - rotate = atan2(g->y2, g->x2); - } else { - errx(EXIT_FAILURE, "edge %d invalid?\n", i); - } - - // position this one translated and rotated - poly_t * const g2 = calloc(1, sizeof(*g2)); - g2->face = f2; - g2->start_edge = f->next_edge[edge]; - - poly_position( - g2, - g, - rotate, - trans_x, - trans_y - ); - - if (overlap_check(poly_root, g2)) - { - free(g2); - continue; - } - - // no overlap, add it to the current group - g->next[i] = g2; - g2->next[0] = g; - f2->used = 1; - - // if g2 is a coplanar triangle, process it now rather than - // defering the work. - if (f->coplanar[edge] == 0) - enqueue(g, g2, 1); - else - enqueue(g, g2, 0); - } - } - - return 0; -} - - -void -svg_text( - float x, - float y, - float angle, - const char * fmt, - ... -) -{ - - printf("", - x, - y, - angle - ); - - printf(""); - - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - - printf("\n"); -} - -void -poly_print( - poly_t * const g -) -{ - const face_t * const f = g->face; - const int start_edge = g->start_edge; - - g->printed = 1; - - // draw this triangle; - // if the edge is an outside, which means that the group - // has no next element, draw a cut line. If there is an - // adjacent neighbor and it is not coplanar, draw a score line -printf("\n", - f, - g->start_edge, g->rot * 180/M_PI, - f->sides[0], - f->next[0], - f->sides[1], - f->next[1], - f->sides[2], - f->next[2] -); - - int cut_lines = 0; - const uintptr_t a1 = (0x7FFFF & (uintptr_t) f) >> 3; - - for (int i = 0 ; i < 3 ; i++) - { - const int edge = (start_edge + i) % 3; - poly_t * const next = g->next[i]; - - if (!next) - { - // draw a cut line - const float * const p1 = g->p[i]; - const float * const p2 = g->p[(i+1) % 3]; - const float cx = (p2[0] + p1[0]) / 2; - const float cy = (p2[1] + p1[1]) / 2; - const float dx = (p2[0] - p1[0]); - const float dy = (p2[1] - p1[1]); - const float angle = atan2(dy, dx) * 180 / M_PI; - - svg_line("#FF0000", p1, p2, 0); - cut_lines++; - - // use the lower address as the label - if (draw_labels) - { - uintptr_t a2 = (0x7FFFF & (uintptr_t) f->next[edge]) >> 3; - if (a2 > a1) - a2 = a1; - svg_text(cx, cy, angle, "%04x", a2); - } - - continue; - } - - if (next->printed) - continue; - - if (f->coplanar[edge] < 0) - { - // draw a mountain score line since they are not coplanar - svg_line("#00FF00", g->p[i], g->p[(i+1) % 3], 1); - } else - if (f->coplanar[edge] > 0) - { - // draw a valley score line since they are not coplanar - svg_line("#00FF00", g->p[i], g->p[(i+1) % 3], 0); - } else { - // draw a shadow line since they are coplanar - //svg_line("#F0F0F0", g->p[i], g->p[(i+1) % 3]); - } - } - -/* - // only draw labels if requested and if there are any cut-edges - // on this polygon. - const float tx = (g->p[0][0] + g->p[1][0] + g->p[2][0]) / 3.0; - const float ty = (g->p[0][1] + g->p[1][1] + g->p[2][1]) / 3.0; - if (draw_labels && cut_lines > 0) - svg_text(tx, ty, 0, "%04x", - (0x7FFFF & (uintptr_t) f) >> 3); -*/ - -printf("\n"); - - for (int i = 0 ; i < 3 ; i++) - { - poly_t * const next = g->next[i]; - if (!next || next->printed) - continue; - - poly_print(next); - } -} - - -/* Returns the 0 for coplanar, negative for mountain, positive for valley. - * (approximates the angle between two triangles that share one edge). - */ -int -coplanar_check( - const stl_face_t * const f1, - const stl_face_t * const f2 -) -{ - // find the four distinct points - v3_t x1 = f1->p[0]; - v3_t x2 = f1->p[1]; - v3_t x3 = f1->p[2]; - v3_t x4; - - for (int i = 0 ; i < 3 ; i++) - { - x4 = f2->p[i]; - if (v3_eq(&x1, &x4)) - continue; - if (v3_eq(&x2, &x4)) - continue; - if (v3_eq(&x3, &x4)) - continue; - break; - } - - // (x3-x1) . ((x2-x1) X (x4-x3)) == 0 - v3_t dx31 = v3_sub(x3, x1); - v3_t dx21 = v3_sub(x2, x1); - v3_t dx43 = v3_sub(x4, x3); - v3_t cross = v3_cross(dx21, dx43); - float dot = v3_dot(dx31, cross); - - int check = -EPS < dot && dot < +EPS; - if (debug) fprintf( stderr, "%p %p %s: %f\n", f1, f2, check ? "yes" : "no", dot); - return (int) dot; -} - - -/** Translate a list of STL triangles into a connected graph of faces. - * - * If there are any triangles that do not have three connected edges, - * the first error will be reported and NULL will be returned. - */ -face_t * -stl2faces( - const stl_face_t * const stl_faces, - const int num_triangles -) -{ - face_t * const faces = calloc(num_triangles, sizeof(*faces)); - - // convert the stl triangles into faces - for (int i = 0 ; i < num_triangles ; i++) - { - const stl_face_t * const stl = &stl_faces[i]; - face_t * const f = &faces[i]; - - f->sides[0] = v3_len(&stl->p[0], &stl->p[1]); - f->sides[1] = v3_len(&stl->p[1], &stl->p[2]); - f->sides[2] = v3_len(&stl->p[2], &stl->p[0]); - if (debug) fprintf(stderr, "%p %f %f %f\n", - f, f->sides[0], f->sides[1], f->sides[2]); - } - - // look to see if there is a matching point - // in the faces that we've already built - for (int i = 0 ; i < num_triangles ; i++) - { - const stl_face_t * const stl = &stl_faces[i]; - face_t * const f = &faces[i]; - - for (int j = 0 ; j < num_triangles ; j++) - { - if (i == j) - continue; - - const stl_face_t * const stl2 = &stl_faces[j]; - face_t * const f2 = &faces[j]; - - for (int edge = 0 ; edge < 3 ; edge++) - { - if (f->next[edge]) - continue; - - for (int edge2 = 0 ; edge2 < 3 ; edge2++) - { - if (f2->next[edge2]) - continue; - - if (!edge_eq2(stl, stl2, edge, edge2)) - continue; - - f->next[edge] = f2; - f->next_edge[edge] = edge2; - f2->next[edge2] = f; - f2->next_edge[edge2] = edge; - - f->coplanar[edge] = - f2->coplanar[edge2] = coplanar_check(stl, stl2); - } - } - } - - // all three edges should be matched - if (f->next[0] && f->next[1] && f->next[2]) - continue; - fprintf(stderr, "%d missing edges?\n", i); - free(faces); - return NULL; - } - - return faces; -} -#endif int @@ -787,9 +226,9 @@ tri_inside( // compute the barycentric coordinates of p in triangle t const float a = (p1y - p2y)*(p0x - p2x) + (p2x - p1x)*(p0y - p2y); //fprintf(stderr, "a=%f\n", a); - if (-EPS < a && a < EPS) + if (a < EPS) { - // triangle is too small + // triangle is too small or has negative area return 0; } @@ -1057,23 +496,37 @@ find_z( } - /* -int -tri_line_intersect( - const tri_t * zlist, - const tri_t * t + * Find the Z point of an XY coordinate in a triangle. + * + * p can be written as a combination of t01 and t02, + * p - t0 = a * (t1 - t0) + b * (t2 - t0) + * setting t0 to 0, this becomes: + * p = a * t1 + b * t2 + * which is two equations with two unknowns + */ +float +tri_find_z( + const tri_t * const t, + const v3_t * const p ) { - for( const tri_t * t2 = zlist ; t2 ; t2 = t2->next ) - { - if (t2 == t) - continue; - for(int j = 0 ; j < 3 ; j++) - { - const v3_t * const p0 = &t->p[j].p; - const v3_t * const p1 = &t->p[(j+1) % 3].p; -*/ + const float t1x = t->p[1].p[0] - t->p[0].p[0]; + const float t1y = t->p[1].p[1] - t->p[0].p[1]; + const float t1z = t->p[1].p[2] - t->p[0].p[2]; + const float t2x = t->p[2].p[0] - t->p[0].p[0]; + const float t2y = t->p[2].p[1] - t->p[0].p[1]; + const float t2z = t->p[2].p[2] - t->p[0].p[2]; + const float px = p->p[0] - t->p[0].p[0]; + const float py = p->p[1] - t->p[0].p[1]; + + const float a = (px * t2y - py * t2x) / (t1x * t2y - t2x * t1y); + const float b = (px * t1y - py * t1x) / (t2x * t1y - t1x * t2y); + + const float z = t->p[0].p[2] + a * t1z + b * t2z; + + return z; +} /* @@ -1166,9 +619,17 @@ fprintf(stderr, "%d: processing segment ", recursive++); seg_print(s); int inside0 = tri_inside(t, &s->p[0], &bary[0]); int inside1 = tri_inside(t, &s->p[1], &bary[1]); - // if both are inside we discard this segment + // if both are inside and the triangle is behind + // this segment, then we discard this segment if (inside0 && inside1) { + float p0_tri_z = tri_find_z(t, &s->p[0]); + if (s->p[0].p[2] <= p0_tri_z) + continue; +fprintf(stderr, "z=%.3f ", p0_tri_z); +seg_print(s); +tri_print(t); + //svg_line("#0000FF", s->p[0].p, s->p[1].p, 0); //svg_line("#00FF00", t->p[0].p, t->p[1].p, 0); //svg_line("#00FF00", t->p[1].p, t->p[2].p, 0); @@ -1180,6 +641,10 @@ seg_print(s); fprintf(stderr, "bary0 %f,%f,%f\n", bary[0].p[0], bary[0].p[1], bary[0].p[2]); fprintf(stderr, "bary1 %f,%f,%f\n", bary[1].p[0], bary[1].p[1], bary[1].p[2]); } + //svg_line("#00FF00", s->p[0].p, s->p[1].p, 10); + //svg_line("#0000FF", t->p[0].p, t->p[1].p, 2); + //svg_line("#0000FF", t->p[1].p, t->p[2].p, 2); + //svg_line("#0000FF", t->p[2].p, t->p[0].p, 2); recursive--; return; } @@ -1228,7 +693,14 @@ fprintf(stderr, "bary1 %f,%f,%f\n", bary[1].p[0], bary[1].p[1], bary[1].p[2]); //fprintf(stderr, "split %d %d inter %d\n", inside0 , inside1, intersections); if (intersections == 3) { + // this likely means that the triangle is very, very + // small, so let's just throw away this line segment +/* fprintf(stderr, "uh, three intersections?\n"); +seg_print(s); +tri_print(t); +svg_line("#00FF00", s->p[0].p, s->p[1].p, 10); +*/ recursive--; return; } @@ -1316,20 +788,24 @@ seg_print(news); if (is[0].p[2] <= it[0].p[2]) continue; +/* // due to floating point issues, one of these might // be closer to the edge. re-check the barycentric // coordinates for "close enough" inside0 = bary[0].p[0] > -EPS && bary[0].p[1] > -EPS && bary[0].p[2] > -EPS; inside1 = bary[1].p[0] > -EPS && bary[1].p[1] > -EPS && bary[1].p[2] > -EPS; +*/ // segment is behind the triangle, so it needs to be // cut into pieces - if (v2_eq(s->p[0].p, is[0].p, 0.1) - || v2_eq(s->p[1].p, is[0].p, 0.1)) +/* + if (v2_eq(s->p[0].p, is[0].p, 0.01) + || v2_eq(s->p[1].p, is[0].p, 0.01)) { // we're touching on one side, ignore it continue; } else +*/ if (inside0) { // shorten it on the 0 side @@ -1344,6 +820,18 @@ seg_print(news); //fprintf(stderr, "short seg 1: "); seg_print(s); continue; } else { + // both outside, but an intersection? + // split at that point and hope for the best + seg_t * const news = seg_new(s->p[0], is[0]); + news->src[0] = s->src[0]; + news->src[1] = s->src[1]; + s->p[0] = is[0]; + tri_seg_intersect(zlist->next, news, slist_visible); +//fprintf(stderr, "%d -----\n", --recursive); + + // continue splitting our current segment + continue; +/* fprintf(stderr, "**** uh, both outside but one intersection? %.3f,%.3f\n", is[0].p[0], is[0].p[1] @@ -1352,8 +840,9 @@ seg_print(s); tri_print(t); fprintf(stderr, "bary0 %f,%f,%f\n", bary[0].p[0], bary[0].p[1], bary[0].p[2]); fprintf(stderr, "bary1 %f,%f,%f\n", bary[1].p[0], bary[1].p[1], bary[1].p[2]); - //svg_line("#00FF00", s->p[0].p, s->p[1].p, 0); + svg_line("#00FF00", s->p[0].p, s->p[1].p, 10); continue; +*/ } } } @@ -1399,7 +888,7 @@ int main( int backface = 1; int coplanar = 1; int hidden = 1; - float coplanar_eps = 0.0001; + float coplanar_eps = 0.001; if(debug) { @@ -1533,7 +1022,7 @@ tri_print(t); // display all of the visible segments for(seg_t * s = slist_visible ; s ; s = s->next) { - svg_line("#FF0000", s->p[0].p, s->p[1].p, 0); + svg_line("#FF0000", s->p[0].p, s->p[1].p, 1); } if (debug)