diff options
author | Howard Trickey <howard.trickey@gmail.com> | 2014-01-28 22:18:43 +0400 |
---|---|---|
committer | Howard Trickey <howard.trickey@gmail.com> | 2014-01-28 22:20:42 +0400 |
commit | 39202a53b559debb5ce1413c08f131173515c3cd (patch) | |
tree | 4d4ad4315f4fe63e9401ab745cc7c81b53c5fba7 | |
parent | 84f95875401f0c9cd815d842d9c6f4526dc8f939 (diff) |
Bevel: fix glitch in "pipe" case with square profile.
The "pipe" case -- where two beveled edges are in line
and there is at least one more beveled edge -- needed better
handling when profile parameter = 1.0 (square outward).
-rw-r--r-- | source/blender/bmesh/tools/bmesh_bevel.c | 235 |
1 files changed, 148 insertions, 87 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 39e43dbb296..460e3f4a59f 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -1240,10 +1240,11 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv) } } -/* Snap a direction co to a superellipsoid with parameter super_r */ -static void snap_to_superellipsoid(float co[3], const float super_r) +/* Snap a direction co to a superellipsoid with parameter super_r. + * For square profiles, midline says whether or not to snap to both planes. */ +static void snap_to_superellipsoid(float co[3], const float super_r, bool midline) { - float a, b, c, x, y, z, r, rinv; + float a, b, c, x, y, z, r, rinv, dx, dy; r = super_r; if (r == PRO_CIRCLE_R) { @@ -1254,64 +1255,62 @@ static void snap_to_superellipsoid(float co[3], const float super_r) x = a = max_ff(0.0f, co[0]); y = b = max_ff(0.0f, co[1]); z = c = max_ff(0.0f, co[2]); - if (r <= 0.0f) - r = 0.1f; - rinv = 1.0f / r; - if (a == 0.0f) { - if (b == 0.0f) { - x = 0.0f; - y = 0.0f; - z = powf(c, rinv); + if (r == PRO_SQUARE_R || r == PRO_SQUARE_IN_R) { + /* will only be called for 2d profile */ + BLI_assert(fabsf(z) < BEVEL_EPSILON); + z = 0.0f; + x = min_ff(1.0f, x); + y = min_ff(1.0f, y); + if (r == PRO_SQUARE_R) { + /* snap to closer of x==1 and y==1 lines, or maybe both */ + dx = 1.0f - x; + dy = 1.0f - y; + if (dx < dy) { + x = 1.0f; + y = midline ? 1.0f : y; + } + else { + y = 1.0f; + x = midline ? 1.0f : x; + } } else { - x = 0.0f; - y = powf(1.0f / (1.0f + powf(c / b, r)), rinv); - z = c * y / b; + /* snap to closer of x==0 and y==0 lines, or maybe both */ + if (x < y) { + x = 0.0f; + y = midline ? 0.0f : y; + } + else { + y = 0.0f; + x = midline ? 0.0f : x; + } } } else { - x = powf(1.0f / (1.0f + powf(b / a, r) + powf(c / a, r)), rinv); - y = b * x / a; - z = c * x / a; + rinv = 1.0f / r; + if (a == 0.0f) { + if (b == 0.0f) { + x = 0.0f; + y = 0.0f; + z = powf(c, rinv); + } + else { + x = 0.0f; + y = powf(1.0f / (1.0f + powf(c / b, r)), rinv); + z = c * y / b; + } + } + else { + x = powf(1.0f / (1.0f + powf(b / a, r) + powf(c / a, r)), rinv); + y = b * x / a; + z = c * x / a; + } } co[0] = x; co[1] = y; co[2] = z; } -static void snap_to_profile(BoundVert *bndv, EdgeHalf *e, float co[3]) -{ - float va[3], vb[3], edir[3], va0[3], vb0[3], vmid0[3]; - float plane[4], m[4][4], minv[4][4], p[3], snap[3]; - - copy_v3_v3(va, bndv->nv.co); - copy_v3_v3(vb, bndv->next->nv.co); - - sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co); - - plane_from_point_normal_v3(plane, co, edir); - closest_to_plane_v3(va0, plane, va); - closest_to_plane_v3(vb0, plane, vb); - closest_to_plane_v3(vmid0, plane, bndv->profile.midco); - if (make_unit_square_map(va0, vmid0, vb0, m)) { - /* Transform co and project it onto superellipse */ - if (!invert_m4_m4(minv, m)) { - /* shouldn't happen */ - BLI_assert(!"failed inverse during profile snap"); - return; - } - mul_v3_m4v3(p, minv, co); - snap_to_superellipsoid(p, bndv->profile.super_r); - mul_v3_m4v3(snap, m, p); - copy_v3_v3(co, snap); - } - else { - /* planar case: just snap to line va--vb */ - closest_to_line_segment_v3(p, co, va, vb); - copy_v3_v3(co, p); - } -} - /* Set the any_seam property for a BevVert and all its BoundVerts */ static void set_bound_vert_seams(BevVert *bv) { @@ -1640,33 +1639,52 @@ static void adjust_offsets(BevelParams *bp) } /* Do the edges at bv form a "pipe"? - * Current definition: at least three beveled edges, - * two in line, and sharing a face. */ -static EdgeHalf *pipe_test(BevVert *bv) -{ - EdgeHalf *e1, *e2, *epipe; + * Current definition: 3 or 4 beveled edges, 2 in line with each other, + * with other edges on opposite sides of the pipe if there are 4. + * Also, the vertex boundary should have 3 or 4 vertices in it, + * and all of the faces involved should be parallel to the pipe edges. + * Return the boundary vert whose ebev is one of the pipe edges, and + * whose next boundary vert has a beveled, non-pipe edge. */ +static BoundVert *pipe_test(BevVert *bv) +{ + EdgeHalf *e, *epipe; + VMesh *vm; + BoundVert *v1, *v2, *v3; + float dir1[3], dir3[3]; + + vm = bv->vmesh; + if (vm->count < 3 || vm->count > 4 || bv->selcount < 3 || bv->selcount > 4) + return NULL; + /* find v1, v2, v3 all with beveled edges, where v1 and v3 have collinear edges */ epipe = NULL; - if (bv->selcount > 2) { - for (e1 = &bv->edges[0]; epipe == NULL && e1 != &bv->edges[bv->edgecount]; e1++) { - if (e1->is_bev) { - for (e2 = &bv->edges[0]; e2 != &bv->edges[bv->edgecount]; e2++) { - if (e1 != e2 && e2->is_bev) { - if ((e1->fnext == e2->fprev) || (e1->fprev == e2->fnext)) { - float dir1[3], dir2[3]; - sub_v3_v3v3(dir1, bv->v->co, BM_edge_other_vert(e1->e, bv->v)->co); - sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, bv->v)->co, bv->v->co); - if (angle_v3v3(dir1, dir2) < 100.0f * BEVEL_EPSILON) { - epipe = e1; - break; - } - } - } - } + v1 = vm->boundstart; + do { + v2 = v1->next; + v3 = v2->next; + if (v1->ebev && v2->ebev && v3->ebev) { + sub_v3_v3v3(dir1, bv->v->co, BM_edge_other_vert(v1->ebev->e, bv->v)->co); + sub_v3_v3v3(dir3, BM_edge_other_vert(v3->ebev->e, bv->v)->co, bv->v->co); + normalize_v3(dir1); + normalize_v3(dir3); + if (angle_v3v3(dir1, dir3) < 100.0f * BEVEL_EPSILON) { + epipe = v1->ebev; + break; } } + } while ((v1 = v1->next) != vm->boundstart); + + if (!epipe) + return NULL; + + /* check face planes: all should have normals perpendicular to epipe */ + for (e = &bv->edges[0]; e != &bv->edges[bv->edgecount]; e++) { + if (e->fnext) { + if (dot_v3v3(dir1, e->fnext->no) > BEVEL_EPSILON) + return FALSE; + } } - return epipe; + return v1; } static VMesh *new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *bounds) @@ -2214,7 +2232,7 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp) for (i = 0; i < 3; i++) { for (j = 0; j <= ns2; j++) { for (k = 0; k <= nseg; k++) { - snap_to_superellipsoid(mesh_vert(vm1, i, j, k)->co, r); + snap_to_superellipsoid(mesh_vert(vm1, i, j, k)->co, r, false); } } } @@ -2344,11 +2362,55 @@ static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv) return vm1; } -static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, EdgeHalf *epipe) +/* Snap co to the closest point on the profile for vpipe projected onto the plane + * containing co with normal in the direction of edge vpipe->ebev. + * For the square profiles, need to decide whether to snap to just one plane + * or to the midpoint of the profile; do so if midline is true. */ +static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) +{ + float va[3], vb[3], edir[3], va0[3], vb0[3], vmid0[3]; + float plane[4], m[4][4], minv[4][4], p[3], snap[3]; + Profile *pro = &vpipe->profile; + EdgeHalf *e = vpipe->ebev; + + copy_v3_v3(va, pro->coa); + copy_v3_v3(vb, pro->cob); + + sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co); + + plane_from_point_normal_v3(plane, co, edir); + closest_to_plane_v3(va0, plane, va); + closest_to_plane_v3(vb0, plane, vb); + closest_to_plane_v3(vmid0, plane, pro->midco); + if (make_unit_square_map(va0, vmid0, vb0, m)) { + /* Transform co and project it onto superellipse */ + if (!invert_m4_m4(minv, m)) { + /* shouldn't happen */ + BLI_assert(!"failed inverse during pipe profile snap"); + return; + } + mul_v3_m4v3(p, minv, co); + snap_to_superellipsoid(p, pro->super_r, midline); + mul_v3_m4v3(snap, m, p); + copy_v3_v3(co, snap); + } + else { + /* planar case: just snap to line va0--vb0 */ + closest_to_line_segment_v3(p, co, va0, vb0); + copy_v3_v3(co, p); + } +} + +/* See pipe_test for conditions that make 'pipe'; vpipe is the return value from that. + * We want to make an ADJ mesh but then snap the vertices to the profile in a plane + * perpendicular to the pipes. + * A tricky case is for the 'square' profiles and an even nseg: we want certain vertices + * to snap to the midline on the pipe, not just to one plane or the other. */ +static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) { - int i, j, k, n, ns, ns2; + int i, j, k, n, ns, ns2, ipipe1, ipipe2; VMesh *vm; - BoundVert *bndv; + bool even, midline; vm = adj_vmesh(bp, bv); @@ -2356,18 +2418,17 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, EdgeHalf *epipe) n = bv->vmesh->count; ns = bv->vmesh->seg; ns2 = ns / 2; - bndv = vm->boundstart; - for (i = 0; i < n; i++) { - if (bndv->ebev == epipe) - break; - bndv = bndv->next; - } + even = (ns % 2) == 0; + ipipe1 = vpipe->index; + ipipe2 = vpipe->next->next->index; for (i = 0; i < n; i++) { for (j = 1; j <= ns2; j++) { for (k = 0; k <= ns2; k++) { if (!is_canon(vm, i, j, k)) continue; - snap_to_profile(bndv, epipe, mesh_vert(vm, i, j, k)->co); + midline = even && k == ns2 && + ((i == 0 && j == ns2) || (i == ipipe1 || i == ipipe2)); + snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co); } } } @@ -2386,7 +2447,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) BoundVert *v; BMVert *bmv1, *bmv2, *bmv3, *bmv4; BMFace *f, *f2, *f23; - EdgeHalf *epipe; + BoundVert *vpipe; n = bv->vmesh->count; ns = bv->vmesh->seg; @@ -2394,10 +2455,10 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) odd = ns % 2; BLI_assert(n >= 3 && ns > 1); - epipe = pipe_test(bv); + vpipe = pipe_test(bv); - if (epipe) - vm1 = pipe_adj_vmesh(bp, bv, epipe); + if (vpipe) + vm1 = pipe_adj_vmesh(bp, bv, vpipe); else if (tri_corner_test(bp, bv)) vm1 = tri_corner_adj_vmesh(bp, bv); else |