Fix bevel profile=1 problems, see T39132, T38458, T40278, T51010,

T53783.

Before, profile=1 ("square outside") only worked well in a few cases
(some "pipes", cube corners). This makes it work well pretty much
everywhere.
This commit is contained in:
Howard Trickey 2018-02-05 14:21:43 -05:00
parent f9fcda533b
commit bd9ed0228b
1 changed files with 172 additions and 2 deletions

View File

@ -2170,7 +2170,6 @@ static void adjust_offsets(BevelParams *bp)
v->visited = true;
if (vnext->visited) {
if (vnext != vchainstart) {
printf("WHOOPS, adjusting offsets, expected cycle!\n");
break;
}
adjust_the_cycle_or_chain(vchainstart, true);
@ -3187,6 +3186,173 @@ static void build_square_in_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv, VMesh
}
}
/* copy whichever of a and b is closer to v into r */
static void closer_v3_v3v3v3(float r[3], float a[3], float b[3], float v[3])
{
if (len_squared_v3v3(a, v) <= len_squared_v3v3(b, v))
copy_v3_v3(r, a);
else
copy_v3_v3(r, b);
}
/* Special case of VMesh when profile == 1 and there are 3 or more beveled edges.
* We want the effect of parallel offset lines (n/2 of them) on each side of the center, for even n.
* Wherever they intersect with each other between two successive beveled edges, those intersections
* are part of the vmesh rings.
* We have to move the boundary edges too -- the usual method is to make one profile plane between
* successive BoundVerts, but for the effect we want here, there will be two planes, one on each side
* of the original edge.
*/
static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
{
int n, ns, ns2, odd, i, j, k, ikind, im1, clstride;
float bndco[3], dir1[3], dir2[3], co1[3], co2[3], meet1[3], meet2[3], v1co[3], v2co[3];
float *on_edge_cur, *on_edge_prev, *p;
float ns2inv, finalfrac, ang;
BoundVert *bndv;
EdgeHalf *e1, *e2;
VMesh *vm;
float *centerline;
n = bv->vmesh->count;
ns = bv->vmesh->seg;
ns2 = ns / 2;
odd = ns % 2;
ns2inv = 1.0f / (float) ns2;
vm = new_adj_vmesh(bp->mem_arena, n, ns, bv->vmesh->boundstart);
clstride = 3 * (ns2 + 1);
centerline = MEM_mallocN(clstride * n * sizeof(float), "bevel");
/* find on_edge, place on bndv[i]'s elast where offset line would meet,
* averaging with position where next sector's offset line would meet */
bndv = vm->boundstart;
for (i = 0; i < n; i++) {
copy_v3_v3(bndco, bndv->nv.co);
e1 = bndv->efirst;
e2 = bndv->elast;
sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
add_v3_v3v3(co1, bndco, dir1);
add_v3_v3v3(co2, bndco, dir2);
/* intersect e1 with line through bndv parallel to e2 to get v1co */
ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, bndco, co2, meet1, meet2);
if (ikind == 0) {
/* Placeholder: this should get eliminated by min dist test with adjacent edge */
mid_v3_v3v3(v1co, e1->e->v1->co, e1->e->v2->co);
}
else {
/* if the lines are skew (ikind == 2), want meet1 which is on e1 */
copy_v3_v3(v1co, meet1);
}
/* intersect e2 with line through bndv parallel to e1 to get v2co */
ikind = isect_line_line_v3(e2->e->v1->co, e2->e->v2->co, bndco, co1, meet1, meet2);
if (ikind == 0) {
mid_v3_v3v3(v2co, e2->e->v1->co, e2->e->v2->co);
}
else {
copy_v3_v3(v2co, meet1);
}
/* want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration */
on_edge_cur = centerline + clstride * i;
on_edge_prev = centerline + clstride * ((i == 0) ? n - 1 : i - 1);
if (i == 0) {
copy_v3_v3(on_edge_cur, v2co);
copy_v3_v3(on_edge_prev, v1co);
}
else if (i == n - 1) {
closer_v3_v3v3v3(on_edge_cur, on_edge_cur, v2co, bv->v->co);
closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
}
else {
copy_v3_v3(on_edge_cur, v2co);
closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
}
bndv = bndv->next;
}
/* fill in rest of centerlines by interpolation */
copy_v3_v3(co2, bv->v->co);
bndv = vm->boundstart;
for (i = 0; i < n; i++) {
if (odd) {
ang = 0.5f * angle_v3v3v3(bndv->nv.co, co1, bndv->next->nv.co);
if (ang > BEVEL_SMALL_ANG) {
/* finalfrac is length along arms of isoceles triangle with top angle 2*ang
* such that the base of the triangle is 1.
* This is used in interpolation along centerline in odd case.
* To avoid too big a drop from bv, cap finalfrac a 0.8 arbitrarily */
finalfrac = 0.5f / sin(ang);
if (finalfrac > 0.8f)
finalfrac = 0.8f;
}
else {
finalfrac = 0.8f;
}
ns2inv = 1.0f / (ns2 + finalfrac);
}
p = centerline + clstride * i;
copy_v3_v3(co1, p);
p += 3;
for (j = 1; j <= ns2; j++) {
interp_v3_v3v3(p, co1, co2, j * ns2inv);
p += 3;
}
bndv = bndv->next;
}
/* coords of edges and mid or near-mid line */
bndv = vm->boundstart;
for (i = 0; i < n; i++) {
copy_v3_v3(co1, bndv->nv.co);
copy_v3_v3(co2, centerline + clstride * (i == 0 ? n - 1 : i - 1));
for (j = 0; j < ns2 + odd; j++) {
interp_v3_v3v3(mesh_vert(vm, i, j, 0)->co, co1, co2, j * ns2inv);
}
copy_v3_v3(co2, centerline + clstride * i);
for (k = 1; k <= ns2; k++) {
interp_v3_v3v3(mesh_vert(vm, i, 0, k)->co, co1, co2, k * ns2inv);
}
bndv = bndv->next;
}
if (!odd)
copy_v3_v3(mesh_vert(vm, 0, ns2, ns2)->co, bv->v->co);
vmesh_copy_equiv_verts(vm);
/* fill in interior points by interpolation from edges to centerlines */
bndv = vm->boundstart;
for (i = 0; i < n; i++) {
im1 = (i == 0) ? n - 1 : i - 1;
for (j = 1; j < ns2 + odd; j++) {
for (k = 1; k <= ns2; k++) {
ikind = isect_line_line_v3(
mesh_vert(vm, i, 0, k)->co, centerline + clstride * im1 + 3 * k,
mesh_vert(vm, i, j, 0)->co, centerline + clstride * i + 3 * j,
meet1, meet2);
if (ikind == 0) {
/* how can this happen? fall back on interpolation in one direction if it does */
interp_v3_v3v3(mesh_vert(vm, i, j, k)->co,
mesh_vert(vm, i, 0, k)->co, centerline + clstride * im1 + 3 * k, j * ns2inv);
}
else if (ikind == 1) {
copy_v3_v3(mesh_vert(vm, i, j, k)->co, meet1);
}
else {
mid_v3_v3v3(mesh_vert(vm, i, j, k)->co, meet1, meet2);
}
}
}
bndv = bndv->next;
}
vmesh_copy_equiv_verts(vm);
MEM_freeN(centerline);
return vm;
}
/*
* Given that the boundary is built and the boundary BMVerts have been made,
* calculate the positions of the interior mesh points for the M_ADJ pattern,
@ -3209,9 +3375,13 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
odd = ns % 2;
BLI_assert(n >= 3 && ns > 1);
vpipe = pipe_test(bv);
if (vpipe) {
if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd) {
vm1 = square_out_adj_vmesh(bp, bv);
}
else if (vpipe) {
vm1 = pipe_adj_vmesh(bp, bv, vpipe);
}
else if (tri_corner_test(bp, bv)) {