[Bf-blender-cvs] [01c1790] master: Make multisegment bevel profiles even for all parameters.

Howard Trickey noreply at git.blender.org
Fri Jan 24 16:09:54 CET 2014


Commit: 01c1790a1105ac84cac2ad2ed90c88a285dfac0a
Author: Howard Trickey
Date:   Fri Jan 24 10:07:24 2014 -0500
https://developer.blender.org/rB01c1790a1105ac84cac2ad2ed90c88a285dfac0a

Make multisegment bevel profiles even for all parameters.

The method for calculating points on the profile for non-circles
and non-lines meant that the segments making up an edge had
uneven widths.
Use a numeric search technique to find superellipse evaluation
points that lead to equal-sized chords across the profile.
Also calculate the actual profile points sooner, so that they
don't have to be recalculated again and again.
This also sets up for a possible later feature of arbitrary
profile shapes, set by user.

===================================================================

M	source/blender/bmesh/tools/bmesh_bevel.c

===================================================================

diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 98e8c79..1071f25 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -98,6 +98,10 @@ typedef struct EdgeHalf {
  * The profile is an arc with control points coa, midco,
  * projected onto a plane (plane_no is normal, plane_co is a point on it)
  * via lines in a given direction (proj_dir).
+ * After the parameters are all set, the actual profile points are calculated
+ * and point in prof_co. We also may need profile points for a higher resolution
+ * number of segments, in order to make the vertex mesh pattern, and that goes
+ * in prof_co_2.
  */
 typedef struct Profile {
 	float super_r;       /* superellipse r parameter */
@@ -107,12 +111,23 @@ typedef struct Profile {
 	float plane_no[3];   /* normal of plane to project to */
 	float plane_co[3];   /* coordinate on plane to project to */
 	float proj_dir[3];   /* direction of projection line */
+	float *prof_co;      /* seg+1 profile coordinates (triples of floats) */
+	float *prof_co_2;    /* like prof_co, but for seg power of 2 >= seg */
 } Profile;
 #define PRO_SQUARE_R 4.0f
 #define PRO_CIRCLE_R 2.0f
 #define PRO_LINE_R 1.0f
 #define PRO_SQUARE_IN_R 0.0f
 
+/* Cache result of expensive calculation of u parameter values to
+ * get even spacing on superellipse for current BevelParams seg
+ * and pro_super_r. */
+typedef struct ProfileSpacing {
+	float *uvals;       /* seg+1 u values */
+	float *uvals_2;     /* seg_2+1 u values, seg_2 = power of 2 >= seg */
+	int seg_2;          /* the seg_2 value */
+} ProfileSpacing;
+
 /* An element in a cyclic boundary of a Vertex Mesh (VMesh) */
 typedef struct BoundVert {
 	struct BoundVert *next, *prev;  /* in CCW order */
@@ -161,6 +176,7 @@ typedef struct BevelParams {
 	 * GHash: (key=(BMVert *), value=(BevVert *)) */
 	GHash    *vert_hash;
 	MemArena *mem_arena;    /* use for all allocs while bevel runs, if we need to free we can switch to mempool */
+	ProfileSpacing pro_spacing; /* parameter values for evenly spaced profiles */
 
 	float offset;           /* blender units to offset each side of a beveled edge */
 	int offset_type;        /* how offset is measured; enum defined in bmesh_operators.h */
@@ -1096,60 +1112,166 @@ static void get_point_on_round_edge(EdgeHalf *e, int k,
 }
 #endif
 
-/* Find the point on given profile at parameter u which goes from 0 to 2 as
- * the profile is moved from pro->coa to pro->cob. */
-static void get_profile_point(const Profile *pro, float u, float r_co[3])
+/* Get the coordinate on the superellipse (exponent r),
+ * at parameter value u.  u goes from u to 2 as the
+ * superellipse moves on the quadrant (0,1) to (1,0). */
+static void superellipse_co(float u, float r, float r_co[2])
 {
-	float co[3], co2[3], p[3], vo[3], angle, r, w;
-	float m[4][4];
-
-	if (u <= 0.0f)
-		copy_v3_v3(co, pro->coa);
-	else if (u >= 2.0f)
-		copy_v3_v3(co, pro->cob);
-	else {
-		r = pro->super_r;
-		if (r == 1.0f || !make_unit_square_map(pro->coa, pro->midco, pro->cob, m)) {
-			interp_v3_v3v3(co, pro->coa, pro->cob, u / 2.0f);
-		}
-		else if (r == PRO_SQUARE_IN_R) {
-			/* square inward concave */
-			zero_v3(p);
-			mul_v3_m4v3(vo, m, p);
-			if (u <= 1.0f)
-				interp_v3_v3v3(co, pro->coa, vo, u);
-			else
-				interp_v3_v3v3(co, vo, pro->cob, u - 1.0f);
+	float t;
+	
+	if (u <= 0.0f) {
+		r_co[0] = 0.0f;
+		r_co[1] = 1.0f;
+ 	}
+	else if (u >= 2.0f) {
+		r_co[0] = 1.0f;
+		r_co[1] = 0.0f;
+	}
+	else if (r == PRO_LINE_R) {
+		t = u / 2.0f;
+		r_co[0] = t;
+		r_co[1] = 1.0f - t;
+		
+	}
+	else if (r == PRO_SQUARE_IN_R) {
+		if (u < 1.0f) {
+			r_co[0] = 0.0f;
+			r_co[1] = 1.0f - u;
 		}
-		else if (r >= PRO_SQUARE_R) {
-			/* square outward convex */
-			if (u <= 1.0f)
-				interp_v3_v3v3(co, pro->coa, pro->midco, u);
-			else
-				interp_v3_v3v3(co, pro->midco, pro->cob, u - 1.0f);
+		else {
+			r_co[0] = u - 1.0f;
+			r_co[1] = 0.0f;
+		}
+	}
+	else if (r == PRO_SQUARE_R) {
+		if (u < 1.0f) {
+			r_co[0] = u;
+			r_co[1] = 1.0f;
 		}
 		else {
-			angle = u * (float)M_PI / 4.0f;  /* angle from y axis */
-			p[0] = sinf(angle);
-			p[1] = cosf(angle);
-			p[2] = 0.0f;
-			if (r != PRO_CIRCLE_R) {
-				w = powf(powf(p[0], r) + powf(p[1], r), -1.0f / r);
-				mul_v2_fl(p, w);
-			}
-			mul_v3_m4v3(co, m, p);
+			r_co[0] = 1.0f;
+			r_co[1] = 2.0f - u;
 		}
+		
 	}
-	/* project co onto final profile plane */
-	if (!is_zero_v3(pro->proj_dir)) {
-		add_v3_v3v3(co2, co, pro->proj_dir);
-		if (!isect_line_plane_v3(r_co, co, co2, pro->plane_co, pro->plane_no)) {
-			/* shouldn't happen */
-			copy_v3_v3(r_co, co);
+	else {
+		t = u * (float)M_PI / 4.0f;  /* angle from y axis */
+		r_co[0] = sinf(t);
+		r_co[1] = cosf(t);
+		if (r != PRO_SQUARE_R) {
+			r_co[0] = pow(r_co[0], 2.0f / r);
+			r_co[1] = pow(r_co[1], 2.0f / r);
 		}
 	}
+}
+
+/* Find the point on given profile at parameter i which goes from 0 to n as
+ * the profile is moved from pro->coa to pro->cob.
+ * We assume that n is either the global seg number or a power of 2 less than
+ * or equal to the power of 2 >= seg.
+ * In the latter case, we subsample the profile for seg_2, which will not necessarily
+ * give equal spaced chords, but is in fact more what is desired by the cubic subdivision
+ * method used to make the vmesh pattern. */
+static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int n, float r_co[3])
+{
+	int d;
+
+	if (bp->seg == 1) {
+		if (i == 0)
+			copy_v3_v3(r_co, pro->coa);
+		else
+			copy_v3_v3(r_co, pro->cob);
+	}
+	
 	else {
-		copy_v3_v3(r_co, co);
+		if (n == bp->seg) {
+			BLI_assert(pro->prof_co != NULL);
+			copy_v3_v3(r_co, pro->prof_co + 3 * i);
+		}
+		else {
+			BLI_assert(is_power_of_2_i(n) && n <= bp->pro_spacing.seg_2);
+			/* set d to spacing in prof_co_2 between subsamples */
+			d = bp->pro_spacing.seg_2 / n;
+			copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * d);
+		}
+	}
+}
+
+/* Calculcate the actual coordinate values for bndv's profile.
+ * This is only needed if bp->seg > 1.
+ * Allocate the space for them if that hasn't been done already.
+ * If bp->seg is not a power of 2, also need to calculate
+ * the coordinate values for the power of 2 >= bp->seg,
+ * because the ADJ_SUBDIV pattern needs power-of-2 boundaries
+ * during construction. */
+static void calculate_profile(BevelParams *bp, BoundVert *bndv)
+{
+	int i, k, ns;
+	float *uvals;
+	float co[3], co2[3], p[3], m[4][4];
+	float *prof_co, *prof_co_k;
+	float r;
+	bool need_2, map_ok;
+	Profile *pro = &bndv->profile;
+
+	if (bp->seg == 1)
+		return;
+
+	need_2 = bp->seg != bp->pro_spacing.seg_2;
+	if (!pro->prof_co) {
+		pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, (bp->seg + 1) * 3 * sizeof(float));
+		if (need_2)
+			pro->prof_co_2 = (float *)BLI_memarena_alloc(bp->mem_arena, (bp->pro_spacing.seg_2 + 1) * 3 *sizeof(float));
+		else
+			pro->prof_co_2 = pro->prof_co;
+	}
+	r = pro->super_r;
+	if (r == PRO_LINE_R)
+		map_ok = false;
+	else
+		map_ok = make_unit_square_map(pro->coa, pro->midco, pro->cob, m);
+	for (i = 0; i < 2; i++) {
+		if (i == 0) {
+			ns = bp->seg;
+			uvals = bp->pro_spacing.uvals;
+			prof_co = pro->prof_co;
+		}
+		else {
+			if (!need_2)
+				break;  /* shares coords with pro->prof_co */
+			ns = bp->pro_spacing.seg_2;
+			uvals = bp->pro_spacing.uvals_2;
+			prof_co = pro->prof_co_2;
+		}
+		BLI_assert((r == PRO_LINE_R || uvals != NULL) && prof_co != NULL);
+		for (k = 0; k <= ns; k++) {
+			if (k == 0)
+				copy_v3_v3(co, pro->coa);
+			else if (k == ns)
+				copy_v3_v3(co, pro->cob);
+			else {
+				if (map_ok) {
+					superellipse_co(uvals[k], r, p);
+					p[2] = 0.0f;
+					mul_v3_m4v3(co, m, p);
+				}
+				else {
+					interp_v3_v3v3(co, pro->coa, pro->cob, (float)k / (float)ns);
+				}
+			}
+			/* project co onto final profile plane */
+			prof_co_k = prof_co + 3 * k;
+			if (!is_zero_v3(pro->proj_dir)) {
+				add_v3_v3v3(co2, co, pro->proj_dir);
+				if (!isect_line_plane_v3(prof_co_k, co, co2, pro->plane_co, pro->plane_no)) {
+					/* shouldn't happen */
+					copy_v3_v3(prof_co_k, co);
+				}
+			}
+			else {
+				copy_v3_v3(prof_co_k, co);
+			}
+		}
 	}
 }
 
@@ -1367,6 +1489,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
 			adjust_bound_vert(e->next->leftv, co);
 		}
 		set_profile_params(bp, vm->boundstart);
+		calculate_profile(bp, vm->boundstart);
 		return;
 	}
 
@@ -1471,6 +1594,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
 	v = vm->boundstart;
 	do {
 		set_profile_params(bp, v);
+		calculate_profile(bp, v);
 	} while ((v = v->next) != vm->boundstart);
 
 	if (bv->selcount == 1 && bv->edgecount == 3) {
@@ -1478,6 +1602,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
 		v = vm->boundstart;
 		BLI_assert(v->ebev != NULL);
 		move_profile_plane(v, v->efirst, v->next->elast);
+		calculate_profile(bp, v);
 	}
 
 	if (construct) {
@@ -2130,7 +2255,7 @@ static void fill_vmesh_fracs(VMesh *vm, float *frac, int i)
 }
 
 /* Like fill_vmesh_fracs but want fractions for profile points of bndv, with ns segments */
-static void fill_profile_fracs(BoundVert *bndv, float *frac, int ns)
+static void fill_profile_fracs(BevelParams *bp, BoundVert *bndv, float *frac, int ns)
 {
 	int k;
 	float co[3], nextco[3];
@@ -2139,7 +2264,7 @@ static void fill_profile_fracs(BoundVert *bndv, float *frac, int ns)
 	frac[0] = 0.0f;
 	copy_v3_v3(co, bndv->nv.co);
 	for (k = 0; k < ns; k++) {
-		get_profile_point(&bndv->profile, 2.0f * (float) (k + 1) / (float) ns, nextco);
+		get_profile_point(bp, &bndv->profile, k + 1, ns, nextco);
 		total += len_v3v3(co, nextco);
 		frac[k + 1] = total;
 		copy_v3_v3(co, nextco);
@@ -2178,7 +2303,7 @@ static int interp_range(const float *frac, int n, const float f, float *r_rest)
 }
 
 /* Interpolate given vmesh to make one with target nseg border vertices on the profiles */
-static VMesh *interp_vmesh(MemArena *mem_arena, VMesh *vm0, int nseg)
+static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm0, int nseg)
 {
 	int n, ns0, nseg2, odd, i, j, k, j0, k0, k0prev;
 	float *prev_frac, *frac, *new_frac, *prev_new_frac;
@@ -2191,7 +2316,7 @@ static VMe

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list