diff options
Diffstat (limited to 'source/blender/bmesh/tools/bmesh_bevel.c')
-rw-r--r-- | source/blender/bmesh/tools/bmesh_bevel.c | 303 |
1 files changed, 244 insertions, 59 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index b9c9aa3aec8..9497466723a 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -59,6 +59,8 @@ #define BEVEL_SMALL_ANG DEG2RADF(10.0f) /** Difference in dot products that corresponds to 10 degree difference between vectors. */ #define BEVEL_SMALL_ANG_DOT 1 - cosf(BEVEL_SMALL_ANG) +/** Difference in dot products that corresponds to 2.0 degree difference between vectors. */ +#define BEVEL_EPSILON_ANG_DOT 1 - cosf(BEVEL_EPSILON_ANG) #define BEVEL_MAX_ADJUST_PCT 10.0f #define BEVEL_MAX_AUTO_ADJUST_PCT 300.0f #define BEVEL_MATCH_SPEC_WEIGHT 0.2 @@ -301,14 +303,14 @@ typedef enum { } FKind; /** Helper for keeping track of angle kind. */ -enum { +typedef enum AngleKind { /** Angle less than 180 degrees. */ ANGLE_SMALLER = -1, /** 180 degree angle. */ ANGLE_STRAIGHT = 0, /** Angle greater than 180 degrees. */ ANGLE_LARGER = 1, -}; +} AngleKind; /** Bevel parameters and state. */ typedef struct BevelParams { @@ -432,6 +434,18 @@ static bool nearly_parallel(const float d1[3], const float d2[3]) return (fabsf(ang) < BEVEL_EPSILON_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG); } +/** + * \return True if d1 and d2 are parallel or nearly parallel. + */ +static bool nearly_parallel_normalized(const float d1[3], const float d2[3]) +{ + BLI_ASSERT_UNIT_V3(d1); + BLI_ASSERT_UNIT_V3(d2); + + const float direction_dot = dot_v3v3(d1, d2); + return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT); +} + /* Make a new BoundVert of the given kind, inserting it at the end of the circular linked * list with entry point bv->boundstart, and return it. */ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3]) @@ -1087,7 +1101,7 @@ static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_ } /* Return whether the angle is less than, equal to, or larger than 180 degrees. */ -static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v) +static AngleKind edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v) { BMVert *v1 = BM_edge_other_vert(e1->e, v); BMVert *v2 = BM_edge_other_vert(e2->e, v); @@ -1096,6 +1110,12 @@ static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v) sub_v3_v3v3(dir2, v->co, v2->co); normalize_v3(dir1); normalize_v3(dir2); + + /* First check for in-line edges using a simpler test. */ + if (nearly_parallel_normalized(dir1, dir2)) { + return ANGLE_STRAIGHT; + } + /* Angles are in [0,pi]. Need to compare cross product with normal to see if they are reflex. */ float cross[3]; cross_v3_v3v3(cross, dir1, dir2); @@ -1110,11 +1130,8 @@ static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v) else { no = v->no; } - float dot = dot_v3v3(cross, no); - if (fabsf(dot) < BEVEL_EPSILON_BIG) { - return ANGLE_STRAIGHT; - } - if (dot < 0.0f) { + + if (dot_v3v3(cross, no) < 0.0f) { return ANGLE_LARGER; } return ANGLE_SMALLER; @@ -1149,6 +1166,113 @@ static bool point_between_edges( return (ang11 - ang1co > -BEVEL_EPSILON_ANG); } +/* Is the angle swept from e1 to e2, CCW when viewed from the normal side of f, + * not a reflex angle or a straight angle? Assume e1 and e2 share a vert. */ +static bool edge_edge_angle_less_than_180(const BMEdge *e1, const BMEdge *e2, const BMFace *f) +{ + float dir1[3], dir2[3], cross[3]; + BLI_assert(f != NULL); + BMVert *v, *v1, *v2; + if (e1->v1 == e2->v1) { + v = e1->v1; + v1 = e1->v2; + v2 = e2->v2; + } + else if (e1->v1 == e2->v2) { + v = e1->v1; + v1 = e1->v2; + v2 = e2->v1; + } + else if (e1->v2 == e2->v1) { + v = e1->v2; + v1 = e1->v1; + v2 = e2->v2; + } + else if (e1->v2 == e2->v2) { + v = e1->v2; + v1 = e1->v1; + v2 = e2->v1; + } + else { + BLI_assert(false); + return false; + } + sub_v3_v3v3(dir1, v1->co, v->co); + sub_v3_v3v3(dir2, v2->co, v->co); + cross_v3_v3v3(cross, dir1, dir2); + return dot_v3v3(cross, f->no) > 0.0f; +} + +/* When the offset_type is BEVEL_AMT_PERCENT or BEVEL_AMT_ABSOLUTE, fill in the coordinates + * of the lines whose intersection defines the boundary point between e1 and e2 with common + * vert v, as defined in the parameters of offset_meet. + */ +static void offset_meet_lines_percent_or_absolute(BevelParams *bp, + EdgeHalf *e1, + EdgeHalf *e2, + BMVert *v, + float r_l1a[3], + float r_l1b[3], + float r_l2a[3], + float r_l2b[3]) +{ + /* Get points the specified distance along each leg. + * Note: not all BevVerts and EdgeHalfs have been made yet, so we have + * to find required edges by moving around faces and use fake EdgeHalfs for + * some of the edges. If there aren't faces to move around, we have to give up. + * The legs we need are: + * e0 : the next edge around e1->fnext (==f1) after e1. + * e3 : the prev edge around e2->fprev (==f2) before e2. + * e4 : the previous edge around f1 before e1 (may be e2). + * e5 : the next edge around f2 after e2 (may be e1). + */ + BMVert *v1, *v2; + EdgeHalf e0, e3, e4, e5; + BMFace *f1, *f2; + float d0, d3, d4, d5; + v1 = BM_edge_other_vert(e1->e, v); + v2 = BM_edge_other_vert(e2->e, v); + f1 = e1->fnext; + f2 = e2->fprev; + bool no_offsets = f1 == NULL || f2 == NULL; + if (!no_offsets) { + BMLoop *l = BM_face_vert_share_loop(f1, v1); + e0.e = l->e; + l = BM_face_vert_share_loop(f2, v2); + e3.e = l->prev->e; + l = BM_face_vert_share_loop(f1, v); + e4.e = l->prev->e; + l = BM_face_vert_share_loop(f2, v); + e5.e = l->e; + /* All the legs must be visible from their opposite legs. */ + no_offsets = !edge_edge_angle_less_than_180(e0.e, e1->e, f1) || + !edge_edge_angle_less_than_180(e1->e, e4.e, f1) || + !edge_edge_angle_less_than_180(e2->e, e3.e, f2) || + !edge_edge_angle_less_than_180(e5.e, e2->e, f1); + if (!no_offsets) { + if (bp->offset_type == BEVEL_AMT_ABSOLUTE) { + d0 = d3 = d4 = d5 = bp->offset; + } + else { + d0 = bp->offset * BM_edge_calc_length(e0.e) / 100.0f; + d3 = bp->offset * BM_edge_calc_length(e3.e) / 100.0f; + d4 = bp->offset * BM_edge_calc_length(e4.e) / 100.0f; + d5 = bp->offset * BM_edge_calc_length(e5.e) / 100.0f; + } + slide_dist(&e4, v, d4, r_l1a); + slide_dist(&e0, v1, d0, r_l1b); + slide_dist(&e5, v, d5, r_l2a); + slide_dist(&e3, v2, d3, r_l2b); + } + } + if (no_offsets) { + copy_v3_v3(r_l1a, v->co); + copy_v3_v3(r_l1b, v1->co); + copy_v3_v3(r_l2a, v->co); + copy_v3_v3(r_l2b, v2->co); + } +} + /** * Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco. * e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of @@ -1167,7 +1291,8 @@ static bool point_between_edges( * \param e_in_plane: If we need to drop from the calculated offset lines to one of the faces, * we don't want to drop onto the 'in plane' face, so if this is not null skip this edge's faces. */ -static void offset_meet(EdgeHalf *e1, +static void offset_meet(BevelParams *bp, + EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, @@ -1209,7 +1334,21 @@ static void offset_meet(EdgeHalf *e1, copy_v3_v3(norm_v, f->no); } else { - copy_v3_v3(norm_v, v->no); + /* Get average of face norms of faces between e and e2. */ + int fcount = 0; + zero_v3(norm_v); + for (EdgeHalf *eloop = e1; eloop != e2; eloop = eloop->next) { + if (eloop->fnext != NULL) { + add_v3_v3(norm_v, eloop->fnext->no); + fcount++; + } + } + if (fcount == 0) { + copy_v3_v3(norm_v, v->no); + } + else { + mul_v3_fl(norm_v, 1.0f / fcount); + } } add_v3_v3(dir1, dir2); cross_v3_v3v3(norm_perp1, dir1, norm_v); @@ -1273,14 +1412,19 @@ static void offset_meet(EdgeHalf *e1, normalize_v3(norm_perp1); normalize_v3(norm_perp2); - /* Get points that are offset distances from each line, then another point on each line. */ float off1a[3], off1b[3], off2a[3], off2b[3]; - copy_v3_v3(off1a, v->co); - madd_v3_v3fl(off1a, norm_perp1, e1->offset_r); - add_v3_v3v3(off1b, off1a, dir1); - copy_v3_v3(off2a, v->co); - madd_v3_v3fl(off2a, norm_perp2, e2->offset_l); - add_v3_v3v3(off2b, off2a, dir2); + if (ELEM(bp->offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE)) { + offset_meet_lines_percent_or_absolute(bp, e1, e2, v, off1a, off1b, off2a, off2b); + } + else { + /* Get points that are offset distances from each line, then another point on each line. */ + copy_v3_v3(off1a, v->co); + madd_v3_v3fl(off1a, norm_perp1, e1->offset_r); + add_v3_v3v3(off1b, off1a, dir1); + copy_v3_v3(off2a, v->co); + madd_v3_v3fl(off2a, norm_perp2, e2->offset_l); + add_v3_v3v3(off2b, off2a, dir2); + } /* Intersect the offset lines. */ float isect2[3]; @@ -1407,9 +1551,16 @@ static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *em * in-between edge emid. Viewed from the vertex normal side, the CCW order of these edges is e1, * emid, e2. Return true if we placed meetco as compromise between where two edges met. If we did, * put the ratio of sines of angles in *r_sinratio too. + * However, if the bp->offset_type is BEVEL_AMT_PERCENT or BEVEL_AMT_ABSOLUTE, we just slide + * along emid by the specified amount. */ -static bool offset_on_edge_between( - EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v, float meetco[3], float *r_sinratio) +static bool offset_on_edge_between(BevelParams *bp, + EdgeHalf *e1, + EdgeHalf *e2, + EdgeHalf *emid, + BMVert *v, + float meetco[3], + float *r_sinratio) { bool retval = false; @@ -1419,6 +1570,22 @@ static bool offset_on_edge_between( float meet1[3], meet2[3]; bool ok1 = offset_meet_edge(e1, emid, v, meet1, &ang1); bool ok2 = offset_meet_edge(emid, e2, v, meet2, &ang2); + if (ELEM(bp->offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE)) { + BMVert *v2 = BM_edge_other_vert(emid->e, v); + if (bp->offset_type == BEVEL_AMT_PERCENT) { + interp_v3_v3v3(meetco, v->co, v2->co, bp->offset / 100.0f); + } + else { + float dir[3]; + sub_v3_v3v3(dir, v2->co, v->co); + normalize_v3(dir); + madd_v3_v3v3fl(meetco, v->co, dir, bp->offset); + } + if (r_sinratio) { + *r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1); + } + return true; + } if (ok1 && ok2) { mid_v3_v3v3(meetco, meet1, meet2); if (r_sinratio) { @@ -2005,7 +2172,7 @@ static void snap_to_superellipsoid(float co[3], const float super_r, bool midlin float x = a; float y = b; float z = c; - if (r == PRO_SQUARE_R || r == PRO_SQUARE_IN_R) { + if (ELEM(r, PRO_SQUARE_R, PRO_SQUARE_IN_R)) { /* Will only be called for 2d profile. */ BLI_assert(fabsf(z) < BEVEL_EPSILON); z = 0.0f; @@ -2288,7 +2455,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) BMFace *f; BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { FKind fkind = get_face_kind(bp, f); - if (fkind == F_ORIG || fkind == F_RECON) { + if (ELEM(fkind, F_ORIG, F_RECON)) { continue; } BMIter liter; @@ -2554,7 +2721,9 @@ static void build_boundary_terminal_edge(BevelParams *bp, EdgeHalf *e = efirst; float co[3]; if (bv->edgecount == 2) { - /* Only 2 edges in, so terminate the edge with an artificial vertex on the unbeveled edge. */ + /* Only 2 edges in, so terminate the edge with an artificial vertex on the unbeveled edge. + * If the offset type is BEVEL_AMT_PERCENT or BEVEL_AMT_ABSOLUTE, what to do is a bit + * undefined (there aren't two "legs"), so just let the code do what it does. */ const float *no = e->fprev ? e->fprev->no : (e->fnext ? e->fnext->no : NULL); offset_in_plane(e, no, true, co); if (construct) { @@ -2592,7 +2761,13 @@ static void build_boundary_terminal_edge(BevelParams *bp, * edge to make a poly or adj mesh, because e->prev has offset 0, offset_meet will put co on * that edge. */ /* TODO: should do something else if angle between e and e->prev > 180 */ - offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL); + bool leg_slide = bp->offset_type == BEVEL_AMT_PERCENT || bp->offset_type == BEVEL_AMT_ABSOLUTE; + if (leg_slide) { + slide_dist(e->prev, bv->v, e->offset_l, co); + } + else { + offset_meet(bp, e->prev, e, bv->v, e->fprev, false, co, NULL); + } if (construct) { BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co); bndv->efirst = e->prev; @@ -2604,7 +2779,12 @@ static void build_boundary_terminal_edge(BevelParams *bp, adjust_bound_vert(e->leftv, co); } e = e->next; - offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL); + if (leg_slide) { + slide_dist(e, bv->v, e->prev->offset_r, co); + } + else { + offset_meet(bp, e->prev, e, bv->v, e->fprev, false, co, NULL); + } if (construct) { BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co); bndv->efirst = e->prev; @@ -2823,27 +3003,27 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) float r, co[3]; if (in_plane == 0 && not_in_plane == 0) { - offset_meet(e, e2, bv->v, e->fnext, false, co, NULL); + offset_meet(bp, e, e2, bv->v, e->fnext, false, co, NULL); } else if (not_in_plane > 0) { if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) { - if (offset_on_edge_between(e, e2, enip, bv->v, co, &r)) { + if (offset_on_edge_between(bp, e, e2, enip, bv->v, co, &r)) { eon = enip; } } else { - offset_meet(e, e2, bv->v, NULL, true, co, eip); + offset_meet(bp, e, e2, bv->v, NULL, true, co, eip); } } else { /* n_in_plane > 0 and n_not_in_plane == 0. */ if (bp->loop_slide && in_plane == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) { - if (offset_on_edge_between(e, e2, eip, bv->v, co, &r)) { + if (offset_on_edge_between(bp, e, e2, eip, bv->v, co, &r)) { eon = eip; } } else { - offset_meet(e, e2, bv->v, e->fnext, true, co, eip); + offset_meet(bp, e, e2, bv->v, e->fnext, true, co, eip); } } @@ -2861,7 +3041,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) for (EdgeHalf *e3 = e->next; e3 != e2; e3 = e3->next) { e3->leftv = e3->rightv = v; } - int ang_kind = edges_angle_kind(e, e2, bv->v); + AngleKind ang_kind = edges_angle_kind(e, e2, bv->v); /* Are we doing special mitering? * There can only be one outer reflex angle, so only one outer miter, @@ -2939,7 +3119,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } else { /* construct == false. */ - int ang_kind = edges_angle_kind(e, e2, bv->v); + AngleKind ang_kind = edges_angle_kind(e, e2, bv->v); if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) || (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) { if (ang_kind == ANGLE_LARGER) { @@ -4402,13 +4582,13 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co); plane_from_point_normal_v3(plane, co, edir); - float va0[3], vb0[3], vmid0[3]; - closest_to_plane_v3(va0, plane, pro->start); - closest_to_plane_v3(vb0, plane, pro->end); - closest_to_plane_v3(vmid0, plane, pro->middle); + float start_plane[3], end_plane[3], middle_plane[3]; + closest_to_plane_v3(start_plane, plane, pro->start); + closest_to_plane_v3(end_plane, plane, pro->end); + closest_to_plane_v3(middle_plane, plane, pro->middle); float m[4][4], minv[4][4]; - if (make_unit_square_map(va0, vmid0, vb0, m) && invert_m4_m4(minv, m)) { + if (make_unit_square_map(start_plane, middle_plane, end_plane, m) && invert_m4_m4(minv, m)) { /* Transform co and project it onto superellipse. */ float p[3]; mul_v3_m4v3(p, minv, co); @@ -4419,9 +4599,9 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) copy_v3_v3(co, snap); } else { - /* Planar case: just snap to line va0--vb0. */ + /* Planar case: just snap to line start_plane--end_plane. */ float p[3]; - closest_to_line_segment_v3(p, co, va0, vb0); + closest_to_line_segment_v3(p, co, start_plane, end_plane); copy_v3_v3(co, p); } } @@ -4453,7 +4633,7 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) if (bp->profile_type == BEVEL_PROFILE_CUSTOM) { /* Find both profile vertices that correspond to this point. */ float *profile_point_pipe1, *profile_point_pipe2, f; - if (i == ipipe1 || i == ipipe2) { + if (ELEM(i, ipipe1, ipipe2)) { if (n_bndv == 3 && i == ipipe1) { /* This part of the vmesh is the triangular corner between the two pipe profiles. */ int ring = max_ii(j, k); @@ -4762,7 +4942,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv) copy_v3_v3(bndco, bndv->nv.co); EdgeHalf *e1 = bndv->efirst; EdgeHalf *e2 = bndv->elast; - int ang_kind = ANGLE_STRAIGHT; + AngleKind ang_kind = ANGLE_STRAIGHT; if (e1 && e2) { ang_kind = edges_angle_kind(e1, e2, bv->v); } @@ -6040,27 +6220,21 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) break; } case BEVEL_AMT_PERCENT: { - /* Offset needs to meet adjacent edges at percentage of their lengths. */ - BMVert *v1 = BM_edge_other_vert(e->prev->e, v); - BMVert *v2 = BM_edge_other_vert(e->e, v); - float z = sinf(angle_v3v3v3(v1->co, v->co, v2->co)); - e->offset_l_spec = BM_edge_calc_length(e->prev->e) * bp->offset * z / 100.0f; - v1 = BM_edge_other_vert(e->e, v); - v2 = BM_edge_other_vert(e->next->e, v); - z = sinf(angle_v3v3v3(v1->co, v->co, v2->co)); - e->offset_r_spec = BM_edge_calc_length(e->next->e) * bp->offset * z / 100.0f; + /* Offset needs to meet adjacent edges at percentage of their lengths. + * Since the width isn't constant, we don't store a width at all, but + * rather the distance along the adjacent edge that we need to go + * at this end of the edge. + */ + + e->offset_l_spec = BM_edge_calc_length(e->prev->e) * bp->offset / 100.0f; + e->offset_r_spec = BM_edge_calc_length(e->next->e) * bp->offset / 100.0f; + break; } case BEVEL_AMT_ABSOLUTE: { /* Like Percent, but the amount gives the absolute distance along adjacent edges. */ - BMVert *v1 = BM_edge_other_vert(e->prev->e, v); - BMVert *v2 = BM_edge_other_vert(e->e, v); - float z = sinf(angle_v3v3v3(v1->co, v->co, v2->co)); - e->offset_l_spec = bp->offset * z; - v1 = BM_edge_other_vert(e->e, v); - v2 = BM_edge_other_vert(e->next->e, v); - z = sinf(angle_v3v3v3(v1->co, v->co, v2->co)); - e->offset_r_spec = bp->offset * z; + e->offset_l_spec = bp->offset; + e->offset_r_spec = bp->offset; break; } default: { @@ -6069,7 +6243,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) break; } } - if (bp->offset_type != BEVEL_AMT_PERCENT && bp->offset_type != BEVEL_AMT_ABSOLUTE) { + if (!ELEM(bp->offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE)) { e->offset_r_spec = e->offset_l_spec; } if (bp->use_weights) { @@ -6611,7 +6785,7 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) BMIter iter; BMLoop *l; BM_ITER_ELEM (l, &iter, r_f, BM_LOOPS_OF_FACE) { - if (l->v == verts[0] || l->v == verts[2]) { + if (ELEM(l->v, verts[0], verts[2])) { BM_elem_flag_enable(l, BM_ELEM_LONG_TAG); } } @@ -7072,6 +7246,17 @@ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb) EdgeHalf *ec; BMVert *vd; float kc; + if (ELEM(bp->offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE)) { + if (ea->is_bev && ebother != NULL && ebother->prev->is_bev) { + if (bp->offset_type == BEVEL_AMT_PERCENT) { + return 50.0f; + } + /* This is only right sometimes. The exact answer is very hard to calculate. */ + float blen = BM_edge_calc_length(eb->e); + return bp->offset > blen / 2.0f ? blen / 2.0f : blen; + } + return no_collide_offset; + } if (ebother != NULL) { ec = ebother->prev; /* Note: this is in direction c --> d. */ vc = bvc->v; |