Knife tool: generalize angle snapping code

Replace hard-coded snap angles with function that allows arbitrary snapping increments.

Currently no user visible change.
This commit is contained in:
Campbell Barton 2015-10-16 03:57:31 +11:00
parent 99142ec7e0
commit 14de9a5982
1 changed files with 84 additions and 123 deletions

View File

@ -228,14 +228,8 @@ typedef struct KnifeTool_OpData {
/* use to check if we're currently dragging an angle snapped line */
bool is_angle_snapping;
enum {
ANGLE_FREE,
ANGLE_0,
ANGLE_45,
ANGLE_90,
ANGLE_135
} angle_snapping;
bool angle_snapping;
float angle;
const float (*cagecos)[3];
} KnifeTool_OpData;
@ -285,7 +279,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
WM_MODALKEY(KNF_MODAL_ADD_CUT), WM_MODALKEY(KNF_MODAL_ADD_CUT_CLOSED), WM_MODALKEY(KNF_MODAL_NEW_CUT),
WM_MODALKEY(KNF_MODAL_MIDPOINT_ON), WM_bool_as_string(kcd->snap_midpoints),
WM_MODALKEY(KNF_MODEL_IGNORE_SNAP_ON), WM_bool_as_string(kcd->ignore_edge_snapping),
WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), WM_bool_as_string(kcd->is_angle_snapping),
WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), WM_bool_as_string(kcd->angle_snapping),
WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), WM_bool_as_string(kcd->cut_through),
WM_MODALKEY(KNF_MODAL_PANNING));
@ -943,98 +937,66 @@ static void knife_finish_cut(KnifeTool_OpData *kcd)
static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
{
bglMats mats;
double u[3], u1[2], u2[2], v1[3], v2[3], dx, dy;
double wminx, wminy, wmaxx, wmaxy;
float v1[3], v2[3];
float planes[4][4];
/* make u the window coords of prevcage */
view3d_get_transformation(kcd->ar, kcd->vc.rv3d, kcd->ob, &mats);
gluProject(kcd->prev.cage[0], kcd->prev.cage[1], kcd->prev.cage[2],
mats.modelview, mats.projection, mats.viewport,
&u[0], &u[1], &u[2]);
planes_from_projmat(
(float (*)[4])kcd->projmat,
planes[2], planes[0], planes[3], planes[1], NULL, NULL);
/* make u1, u2 the points on window going through u at snap angle */
wminx = kcd->ar->winrct.xmin;
wmaxx = kcd->ar->winrct.xmin + kcd->ar->winx;
wminy = kcd->ar->winrct.ymin;
wmaxy = kcd->ar->winrct.ymin + kcd->ar->winy;
/* ray-cast all planes */
{
float ray_dir[3];
float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
float lambda_best[2] = {-FLT_MAX, FLT_MAX};
int i;
switch (kcd->angle_snapping) {
case ANGLE_0:
u1[0] = wminx;
u2[0] = wmaxx;
u1[1] = u2[1] = u[1];
break;
case ANGLE_90:
u1[0] = u2[0] = u[0];
u1[1] = wminy;
u2[1] = wmaxy;
break;
case ANGLE_45:
/* clip against left or bottom */
dx = u[0] - wminx;
dy = u[1] - wminy;
if (dy > dx) {
u1[0] = wminx;
u1[1] = u[1] - dx;
/* we (sometimes) need the lines to be at the same depth before projecting */
#if 0
sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
#else
{
float curr_cage_adjust[3];
float co_depth[3];
copy_v3_v3(co_depth, kcd->prev.cage);
mul_m4_v3(kcd->ob->obmat, co_depth);
ED_view3d_win_to_3d(kcd->ar, co_depth, kcd->curr.mval, curr_cage_adjust);
mul_m4_v3(kcd->ob->imat, curr_cage_adjust);
sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
}
#endif
for (i = 0; i < 4; i++) {
float ray_hit[3];
float lambda_test;
if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
if (lambda_test < 0.0f) {
if (lambda_test > lambda_best[0]) {
copy_v3_v3(ray_hit_best[0], ray_hit);
lambda_best[0] = lambda_test;
}
}
else {
if (lambda_test < lambda_best[1]) {
copy_v3_v3(ray_hit_best[1], ray_hit);
lambda_best[1] = lambda_test;
}
}
}
else {
u1[0] = u[0] - dy;
u1[1] = wminy;
}
/* clip against right or top */
dx = wmaxx - u[0];
dy = wmaxy - u[1];
if (dy > dx) {
u2[0] = wmaxx;
u2[1] = u[1] + dx;
}
else {
u2[0] = u[0] + dy;
u2[1] = wmaxy;
}
break;
case ANGLE_135:
/* clip against right or bottom */
dx = wmaxx - u[0];
dy = u[1] - wminy;
if (dy > dx) {
u1[0] = wmaxx;
u1[1] = u[1] - dx;
}
else {
u1[0] = u[0] + dy;
u1[1] = wminy;
}
/* clip against left or top */
dx = u[0] - wminx;
dy = wmaxy - u[1];
if (dy > dx) {
u2[0] = wminx;
u2[1] = u[1] + dx;
}
else {
u2[0] = u[0] - dy;
u2[1] = wmaxy;
}
break;
default:
return;
}
copy_v3_v3(v1, ray_hit_best[0]);
copy_v3_v3(v2, ray_hit_best[1]);
}
/* unproject u1 and u2 back into object space */
gluUnProject(u1[0], u1[1], 0.0,
mats.modelview, mats.projection, mats.viewport,
&v1[0], &v1[1], &v1[2]);
gluUnProject(u2[0], u2[1], 0.0,
mats.modelview, mats.projection, mats.viewport,
&v2[0], &v2[1], &v2[2]);
UI_ThemeColor(TH_TRANSFORM);
glLineWidth(2.0);
glBegin(GL_LINES);
glVertex3dv(v1);
glVertex3dv(v2);
glVertex3fv(v1);
glVertex3fv(v2);
glEnd();
}
@ -1067,7 +1029,7 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
glMultMatrixf(kcd->ob->obmat);
if (kcd->mode == MODE_DRAGGING) {
if (kcd->angle_snapping != ANGLE_FREE)
if (kcd->is_angle_snapping)
knifetool_draw_angle_snapping(kcd);
glColor3ubv(kcd->colors.line);
@ -2141,42 +2103,41 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
return NULL;
}
/**
* Snaps a 2d vector to an angle, relative to \a v_ref.
*/
static float snap_v2_angle(float r[2], const float v[2], const float v_ref[2], float angle_snap)
{
float m2[2][2];
float v_unit[2];
float angle, angle_delta;
BLI_ASSERT_UNIT_V2(v_ref);
normalize_v2_v2(v_unit, v);
angle = angle_signed_v2v2(v_unit, v_ref);
angle_delta = (round(angle / angle_snap) * angle_snap) - angle;
rotate_m2(m2, angle_delta);
mul_v2_m2v2(r, m2, v);
return angle + angle_delta;
}
/* update both kcd->curr.mval and kcd->mval to snap to required angle */
static bool knife_snap_angle(KnifeTool_OpData *kcd)
{
float dx, dy;
float w, abs_tan;
const float dvec_ref[2] = {0.0f, 1.0f};
float dvec[2], dvec_snap[2];
float snap_step = DEG2RADF(45);
dx = kcd->curr.mval[0] - kcd->prev.mval[0];
dy = kcd->curr.mval[1] - kcd->prev.mval[1];
if (dx == 0.0f && dy == 0.0f)
sub_v2_v2v2(dvec, kcd->curr.mval, kcd->prev.mval);
if (is_zero_v2(dvec)) {
return false;
if (dx == 0.0f) {
kcd->angle_snapping = ANGLE_90;
kcd->curr.mval[0] = kcd->prev.mval[0];
}
w = dy / dx;
abs_tan = fabsf(w);
if (abs_tan <= 0.4142f) { /* tan(22.5 degrees) = 0.4142 */
kcd->angle_snapping = ANGLE_0;
kcd->curr.mval[1] = kcd->prev.mval[1];
}
else if (abs_tan < 2.4142f) { /* tan(67.5 degrees) = 2.4142 */
if (w > 0) {
kcd->angle_snapping = ANGLE_45;
kcd->curr.mval[1] = kcd->prev.mval[1] + dx;
}
else {
kcd->angle_snapping = ANGLE_135;
kcd->curr.mval[1] = kcd->prev.mval[1] - dx;
}
}
else {
kcd->angle_snapping = ANGLE_90;
kcd->curr.mval[0] = kcd->prev.mval[0];
}
kcd->angle = snap_v2_angle(dvec_snap, dvec, dvec_ref, snap_step);
add_v2_v2v2(kcd->curr.mval, kcd->prev.mval, dvec_snap);
copy_v2_v2(kcd->mval, kcd->curr.mval);
@ -2192,7 +2153,7 @@ static int knife_update_active(KnifeTool_OpData *kcd)
/* view matrix may have changed, reproject */
knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
if (kcd->angle_snapping != ANGLE_FREE && kcd->mode == MODE_DRAGGING) {
if (kcd->angle_snapping && (kcd->mode == MODE_DRAGGING)) {
kcd->is_angle_snapping = knife_snap_angle(kcd);
}
else {