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).
This commit is contained in:
Howard Trickey
2014-01-28 13:18:43 -05:00
parent 84f9587540
commit 39202a53b5

View File

@@ -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)
* 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 *e1, *e2, *epipe;
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])
{
int i, j, k, n, ns, ns2;
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, 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