mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Fix #148890: Spin tool floating-point error with high step count
Previously, a single-step rotation matrix and applied it repeatedly, causing increasing inaccuracy with higher step counts. Now each step calculates its rotation directly from the original geometry and target angle. Ref !149543
This commit is contained in:
parent
9240e49385
commit
08afa95a78
1 changed files with 22 additions and 6 deletions
|
|
@ -552,7 +552,6 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op)
|
|||
BMOperator dupop, extop;
|
||||
float cent[3], dvec[3];
|
||||
float axis[3];
|
||||
float rmat[3][3];
|
||||
float phi;
|
||||
int steps, do_dupli, a;
|
||||
bool use_dvec;
|
||||
|
|
@ -563,14 +562,13 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op)
|
|||
BMO_slot_vec_get(op->slots_in, "dvec", dvec);
|
||||
use_dvec = !is_zero_v3(dvec);
|
||||
steps = BMO_slot_int_get(op->slots_in, "steps");
|
||||
phi = BMO_slot_float_get(op->slots_in, "angle") / steps;
|
||||
const float angle_total = BMO_slot_float_get(op->slots_in, "angle");
|
||||
phi = angle_total / steps;
|
||||
do_dupli = BMO_slot_bool_get(op->slots_in, "use_duplicate");
|
||||
const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip");
|
||||
/* Caller needs to perform other sanity checks (such as the spin being 360d). */
|
||||
const bool use_merge = BMO_slot_bool_get(op->slots_in, "use_merge") && steps >= 3;
|
||||
|
||||
axis_angle_normalized_to_mat3(rmat, axis, phi);
|
||||
|
||||
BMVert **vtable = nullptr;
|
||||
if (use_merge) {
|
||||
vtable = MEM_malloc_arrayN<BMVert *>(bm->totvert, __func__);
|
||||
|
|
@ -590,8 +588,18 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op)
|
|||
|
||||
BMO_slot_copy(op, slots_in, "geom", op, slots_out, "geom_last.out");
|
||||
for (a = 0; a < steps; a++) {
|
||||
/* Calculate rotation matrix for this step independently to avoid floating-point error
|
||||
* accumulation. For step a (0-indexed), rotate by (a + 1) * phi from the original angle. */
|
||||
float rmat[3][3];
|
||||
{
|
||||
float step_angle = phi * (a + 1);
|
||||
axis_angle_normalized_to_mat3(rmat, axis, step_angle);
|
||||
}
|
||||
|
||||
if (do_dupli) {
|
||||
BMO_op_initf(bm, &dupop, op->flag, "duplicate geom=%S", op, "geom_last.out");
|
||||
/* For duplicate mode, duplicate from original geometry
|
||||
* and rotate by total angle for this step. */
|
||||
BMO_op_initf(bm, &dupop, op->flag, "duplicate geom=%S", op, "geom");
|
||||
BMO_op_exec(bm, &dupop);
|
||||
BMO_op_callf(bm,
|
||||
op->flag,
|
||||
|
|
@ -623,11 +631,19 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op)
|
|||
true);
|
||||
BMO_op_exec(bm, &extop);
|
||||
if ((use_merge && (a == steps - 1)) == false) {
|
||||
/* For extrude mode, we need to rotate the extruded geometry.
|
||||
* The extruded geometry is at the position of the previous step,
|
||||
* so we rotate it by phi to get to the current step position. */
|
||||
const float step_angle_prev = phi * a;
|
||||
const float step_angle_curr = phi * (a + 1);
|
||||
const float rotation_delta = step_angle_curr - step_angle_prev;
|
||||
float rmat_delta[3][3];
|
||||
axis_angle_normalized_to_mat3(rmat_delta, axis, rotation_delta);
|
||||
BMO_op_callf(bm,
|
||||
op->flag,
|
||||
"rotate cent=%v matrix=%m3 space=%s verts=%S",
|
||||
cent,
|
||||
rmat,
|
||||
rmat_delta,
|
||||
op,
|
||||
"space",
|
||||
&extop,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue