Fix bug in how librt knob adapters handled edit_flag.

edit_flag identifies the exact primitive operation, while
edit_mode identifies the interaction class.

rt_knob_edit_rot/tran/sca() incorrectly replaced a
primitive-specific flag with the generic operation before
dispatching.

Fixed in src/librt/edit.cpp:811:

- Preserve primitive-specific edit_flag when edit_mode matches.
- Fall back to generic operations otherwise.
- Covers translation, rotation, and scale paths.

The MGED faceplate and constrained-axis handlers already
preserve the flag correctly; they route through these adapters.

Added regressions in src/librt/tests/edit/arb8.cpp:312 for:

- ARB5 point 5 direct editing.
- ARB5 point 5 knob dragging.
- ARB face rotation through the knob path.
This commit is contained in:
Clifford Yapp committed 2026-08-24 16:27:01 +00:00
1 parent f0a946cf1d
commit be97d86e4e
3 files changed
+111 -3

No files matched your search

+6
View File
@@ -473,6 +473,12 @@ rt_edit_knob_cmd_process(
char origin, int incr_flag, void *u_data
);
/* Knob operations preserve a primitive-specific edit_flag when edit_mode
* already matches the requested interaction class. This allows interactive
* drags to continue operations such as moving an ARB vertex or rotating an
* ARB face; generic RT_PARAMS_EDIT_* flags are selected only when the current
* edit is not in the corresponding class. */
RT_EXPORT extern void
rt_knob_edit_rot(struct rt_edit *s,
char coords,
+16 -3
View File
@@ -808,6 +808,19 @@ rt_edit_knob_cmd_process(
return BRLCAD_ERROR;
}
/*
* Knob input describes an interaction class, but the active edit flag may
* identify a primitive-specific operation in that class. Keep that exact
* operation selected when the caller has already supplied a matching
* edit_mode; otherwise fall back to the generic operation.
*/
static void
rt_edit_set_knob_edflag(struct rt_edit *s, int edit_mode)
{
if (s->edit_mode != edit_mode)
rt_edit_set_edflag(s, edit_mode);
}
void
rt_knob_edit_rot(struct rt_edit *s,
char coords,
@@ -853,7 +866,7 @@ rt_knob_edit_rot(struct rt_edit *s,
int save_edflag = s->edit_flag;
int save_mode = s->edit_mode;
rt_edit_set_edflag(s, RT_PARAMS_EDIT_ROT);
rt_edit_set_knob_edflag(s, RT_PARAMS_EDIT_ROT);
rt_edit_process(s);
@@ -952,7 +965,7 @@ rt_knob_edit_tran(struct rt_edit *s,
int save_edflag = s->edit_flag;
int save_mode = s->edit_mode;
rt_edit_set_edflag(s, RT_PARAMS_EDIT_TRANS);
rt_edit_set_knob_edflag(s, RT_PARAMS_EDIT_TRANS);
VADD2(s->e_para, delta, s->curr_e_axes_pos);
s->e_inpara = 3;
@@ -996,7 +1009,7 @@ rt_knob_edit_sca(struct rt_edit *s, int matrix_edit)
int save_edflag = s->edit_flag;
int save_mode = s->edit_mode;
rt_edit_set_edflag(s, RT_PARAMS_EDIT_SCALE);
rt_edit_set_knob_edflag(s, RT_PARAMS_EDIT_SCALE);
rt_edit_process(s);
+89
View File
@@ -124,6 +124,30 @@ arb8_reset(struct rt_edit *s, struct rt_arb_internal *arb, struct rt_arb8_edit *
s->mv_context = 1;
}
static void
arb5_reset(struct rt_edit *s, struct rt_arb_internal *arb, struct rt_arb8_edit *a)
{
VSET(arb->pt[0], 0, 0, 0);
VSET(arb->pt[1], 1, 0, 0);
VSET(arb->pt[2], 1, 1, 0);
VSET(arb->pt[3], 0, 1, 0);
VSET(arb->pt[4], 0.5, 0.5, 1);
VMOVE(arb->pt[5], arb->pt[4]);
VMOVE(arb->pt[6], arb->pt[4]);
VMOVE(arb->pt[7], arb->pt[4]);
a->newedge = 0;
a->edit_menu = 0;
VSETALL(s->e_keypoint, 0.0);
MAT_IDN(s->acc_rot_sol);
MAT_IDN(s->incr_change);
s->acc_sc_sol = 1.0;
s->e_inpara = 0;
s->es_scale = 0.0;
s->mv_context = 1;
}
int
main(int argc, char *argv[])
@@ -285,6 +309,52 @@ main(int argc, char *argv[])
bu_exit(1, "ERROR: RT_PARAMS_EDIT_ROT(xy) failed\n");
bu_log("RT_PARAMS_EDIT_ROT(xy) SUCCESS: rotation applied via knob path\n");
/* ================================================================
* PTARB ARB5 point 5: both direct parameters and knob translation must
* retain the primitive-specific edit flag. Point 5 is stored at pt[4]
* and duplicated in pt[5..7].
* ================================================================*/
arb5_reset(s, arb, a);
a->edit_menu = 8; /* symbolic ARB5 point-5 menu argument */
rt_edit_set_edflag(s, PTARB);
s->edit_mode = RT_PARAMS_EDIT_TRANS;
s->e_inpara = 3;
VSET(s->e_para, 0.75, 0.75, 1.25);
s->mv_context = 0;
rt_edit_process(s);
{
point_t expected = {0.75, 0.75, 1.25};
point_t expected_base = {0, 0, 0};
if (!VNEAR_EQUAL(arb->pt[4], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[5], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[6], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[7], expected, VUNITIZE_TOL))
bu_exit(1, "ERROR: PTARB ARB5 point 5 parameter edit failed\n");
if (!VNEAR_EQUAL(arb->pt[0], expected_base, VUNITIZE_TOL))
bu_exit(1, "ERROR: PTARB ARB5 point 5 moved the base\n");
bu_log("PTARB ARB5 point 5 parameter edit SUCCESS\n");
}
arb5_reset(s, arb, a);
a->edit_menu = 8;
rt_edit_set_edflag(s, PTARB);
s->edit_mode = RT_PARAMS_EDIT_TRANS;
s->mv_context = 0;
VMOVE(s->curr_e_axes_pos, arb->pt[4]);
vect_t point_delta = {0.1, 0.2, 0.3};
rt_knob_edit_tran(s, 'm', 0, point_delta);
{
point_t expected = {0.6, 0.7, 1.3};
point_t expected_base = {0, 0, 0};
if (!VNEAR_EQUAL(arb->pt[4], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[5], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[6], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[7], expected, VUNITIZE_TOL) ||
!VNEAR_EQUAL(arb->pt[0], expected_base, VUNITIZE_TOL))
bu_exit(1, "ERROR: PTARB ARB5 point 5 knob edit failed\n");
bu_log("PTARB ARB5 point 5 knob edit SUCCESS\n");
}
/* ================================================================
* RT_MATRIX_EDIT_ROT: absolute matrix rotation from keyboard angles
@@ -465,6 +535,25 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
a->es_peqn[a->edit_menu][0], a->es_peqn[a->edit_menu][1],
a->es_peqn[a->edit_menu][2], a->es_peqn[a->edit_menu][W]);
/* The same face operation must survive the interactive rotation knob
* adapter, which supplies an incremental matrix rather than parameters.
*/
arb8_reset(s, arb, a);
a->edit_menu = 4;
s->e_inpara = 1;
s->e_para[0] = 1.0;
rt_edit_set_edflag(s, ECMD_ARB_SETUP_ROTFACE);
rt_edit_process(s);
plane_t knob_orig_peqn;
HMOVE(knob_orig_peqn, a->es_peqn[a->edit_menu]);
mat_t knob_rot;
MAT_IDN(knob_rot);
bn_mat_angles(knob_rot, 45.0, 0.0, 0.0);
rt_knob_edit_rot(s, 'm', 'm', 0, knob_rot);
if (VEQUAL(a->es_peqn[a->edit_menu], knob_orig_peqn))
bu_exit(1, "ERROR: ECMD_ARB_ROTATE_FACE knob edit did not rotate face\n");
bu_log("ECMD_ARB_ROTATE_FACE knob edit SUCCESS\n");
rt_edit_destroy(s);
db_close(dbip);
return 0;