[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