/* Project screenspace coordinates to 3D-space * NOTE: We include this as a utility function, since the standard method * involves quite a few steps, which are invariably always the same * for all GPencil operations. So, it's nicer to just centralise these. * WARNING: Assumes that it is getting called in a 3D view only */ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3]) { View3D *v3d = gsc->sa->spacedata.first; RegionView3D *rv3d = gsc->ar->regiondata; float *rvec = ED_view3d_cursor3d_get(scene, v3d); float ref[3] = {rvec[0], rvec[1], rvec[2]}; float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); float mval_f[2], mval_prj[2]; float dvec[3]; copy_v2_v2(mval_f, screen_co); if (ED_view3d_project_float_global(gsc->ar, ref, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { sub_v2_v2v2(mval_f, mval_prj, mval_f); ED_view3d_win_to_delta(gsc->ar, mval_f, dvec, zfac); sub_v3_v3v3(r_out, rvec, dvec); return true; } else { zero_v3(r_out); return false; } }
/** * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) * * Just like gp_point_to_xy(), except the resulting coordinates are floats not ints. * Use this version to solve "stair-step" artifacts which may arise when roundtripping the calculations. * * \param r_x: [out] The screen-space x-coordinate of the point * \param r_y: [out] The screen-space y-coordinate of the point * * \warning This assumes that the caller has already checked whether the stroke in question can be drawn */ void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, float *r_x, float *r_y) { ARegion *ar = gsc->ar; View2D *v2d = gsc->v2d; rctf *subrect = gsc->subrect; float xyval[2]; /* sanity checks */ BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); if (gps->flag & GP_STROKE_3DSPACE) { if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { *r_x = xyval[0]; *r_y = xyval[1]; } else { *r_x = 0.0f; *r_y = 0.0f; } } else if (gps->flag & GP_STROKE_2DSPACE) { float vec[3] = {pt->x, pt->y, 0.0f}; int t_x, t_y; mul_m4_v3(gsc->mat, vec); UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { /* XXX: Or should we just always use the values as-is? */ *r_x = 0.0f; *r_y = 0.0f; } else { *r_x = (float)t_x; *r_y = (float)t_y; } } else { if (subrect == NULL) { /* normal 3D view (or view space) */ *r_x = (pt->x / 100.0f * ar->winx); *r_y = (pt->y / 100.0f * ar->winy); } else { /* camera view, use subrect */ *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; } } }
static void ApplySnapTranslation(TransInfo *t, float vec[3]) { float point[3]; getSnapPoint(t, point); if (t->spacetype == SPACE_NODE) { char border = t->tsnap.snapNodeBorder; if (border & (NODE_LEFT | NODE_RIGHT)) vec[0] = point[0] - t->tsnap.snapTarget[0]; if (border & (NODE_BOTTOM | NODE_TOP)) vec[1] = point[1] - t->tsnap.snapTarget[1]; } else { if (t->spacetype == SPACE_VIEW3D) { if (t->options & CTX_PAINT_CURVE) { if (ED_view3d_project_float_global(t->ar, point, point, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { zero_v3(point); /* no good answer here... */ } } } sub_v3_v3v3(vec, point, t->tsnap.snapTarget); } }
eRedrawFlag updateSelectedSnapPoint(TransInfo *t) { eRedrawFlag status = TREDRAW_NOTHING; if (t->tsnap.status & MULTI_POINTS) { TransSnapPoint *p, *closest_p = NULL; float dist_min_sq = TRANSFORM_SNAP_MAX_PX; const float mval_fl[2] = {t->mval[0], t->mval[1]}; float screen_loc[2]; for (p = t->tsnap.points.first; p; p = p->next) { float dist_sq; if (ED_view3d_project_float_global(t->ar, p->co, screen_loc, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { continue; } dist_sq = len_squared_v2v2(mval_fl, screen_loc); if (dist_sq < dist_min_sq) { closest_p = p; dist_min_sq = dist_sq; } } if (closest_p) { if (t->tsnap.selectedPoint != closest_p) { status = TREDRAW_HARD; } t->tsnap.selectedPoint = closest_p; } } return status; }
static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) { bool do_draw = false; int exit_code = OPERATOR_RUNNING_MODAL; RulerInfo *ruler_info = op->customdata; ScrArea *sa = ruler_info->sa; ARegion *ar = ruler_info->ar; RegionView3D *rv3d = ar->regiondata; /* its possible to change spaces while running the operator [#34894] */ if (UNLIKELY(ar != CTX_wm_region(C))) { exit_code = OPERATOR_FINISHED; goto exit; } switch (event->type) { case LEFTMOUSE: if (event->val == KM_RELEASE) { if (ruler_info->state == RULER_STATE_DRAG) { /* rubber-band angle removal */ RulerItem *ruler_item = ruler_item_active_get(ruler_info); if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) { if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) { ruler_item->flag &= ~RULERITEM_USE_ANGLE; do_draw = true; } } if (ruler_info->snap_flag & RULER_SNAP_OK) { ruler_info->snap_flag &= ~RULER_SNAP_OK; do_draw = true; } ruler_info->state = RULER_STATE_NORMAL; } } else { if (ruler_info->state == RULER_STATE_NORMAL) { if (event->ctrl || /* weak - but user friendly */ BLI_listbase_is_empty(&ruler_info->items)) { View3D *v3d = CTX_wm_view3d(C); const bool use_depth = (v3d->drawtype >= OB_SOLID); /* Create new line */ RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info); RulerItem *ruler_item; /* check if we want to drag an existing point or add a new one */ ruler_info->state = RULER_STATE_DRAG; ruler_item = ruler_item_add(ruler_info); ruler_item_active_set(ruler_info, ruler_item); if (use_depth) { /* snap the first point added, not essential but handy */ ruler_item->co_index = 0; view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true); copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]); } else { /* initial depth either previous ruler, view offset */ if (ruler_item_prev) { copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]); } else { negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs); } copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co); view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval); } copy_v3_v3(ruler_item->co[2], ruler_item->co[0]); ruler_item->co_index = 2; do_draw = true; } else { float mval_fl[2] = {UNPACK2(event->mval)}; RulerItem *ruler_item_pick; int co_index; /* select and drag */ if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) { if (co_index == -1) { if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) { /* Add Center Point */ ruler_item_active_set(ruler_info, ruler_item_pick); ruler_item_pick->flag |= RULERITEM_USE_ANGLE; ruler_item_pick->co_index = 1; ruler_info->state = RULER_STATE_DRAG; /* find the factor */ { float co_ss[2][2]; float fac; ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP); ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP); fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]); CLAMP(fac, 0.0f, 1.0f); interp_v3_v3v3(ruler_item_pick->co[1], ruler_item_pick->co[0], ruler_item_pick->co[2], fac); } /* update the new location */ view3d_ruler_item_mousemove(C, ruler_info, event->mval, event->shift != 0, event->ctrl != 0); do_draw = true; } } else { ruler_item_active_set(ruler_info, ruler_item_pick); ruler_item_pick->co_index = co_index; ruler_info->state = RULER_STATE_DRAG; /* store the initial depth */ copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]); do_draw = true; } } else { exit_code = OPERATOR_PASS_THROUGH; } } } } break; case CKEY: { if (event->ctrl) { RulerItem *ruler_item = ruler_item_active_get(ruler_info); if (ruler_item) { const int prec = 8; char numstr[256]; Scene *scene = CTX_data_scene(C); UnitSettings *unit = &scene->unit; ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); WM_clipboard_text_set((void *) numstr, false); } } break; } case RIGHTCTRLKEY: case LEFTCTRLKEY: { WM_event_add_mousemove(C); break; } case MOUSEMOVE: { if (ruler_info->state == RULER_STATE_DRAG) { if (view3d_ruler_item_mousemove(C, ruler_info, event->mval, event->shift != 0, event->ctrl != 0)) { do_draw = true; } } break; } case ESCKEY: { do_draw = true; exit_code = OPERATOR_CANCELLED; break; } case RETKEY: { view3d_ruler_to_gpencil(C, ruler_info); do_draw = true; exit_code = OPERATOR_FINISHED; break; } case DELKEY: { if (event->val == KM_PRESS) { if (ruler_info->state == RULER_STATE_NORMAL) { RulerItem *ruler_item = ruler_item_active_get(ruler_info); if (ruler_item) { RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next; ruler_item_remove(ruler_info, ruler_item); ruler_item_active_set(ruler_info, ruler_item_other); do_draw = true; } } } break; } default: exit_code = OPERATOR_PASS_THROUGH; break; } if (do_draw) { view3d_ruler_header_update(sa); /* all 3d views draw rulers */ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); } exit: if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { WM_cursor_modal_restore(ruler_info->win); view3d_ruler_end(C, ruler_info); view3d_ruler_free(ruler_info); op->customdata = NULL; ED_area_headerprint(sa, NULL); } return exit_code; }
static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg) { Scene *scene = CTX_data_scene(C); UnitSettings *unit = &scene->unit; RulerItem *ruler_item; RulerInfo *ruler_info = arg; RegionView3D *rv3d = ruler_info->ar->regiondata; // ARegion *ar = ruler_info->ar; const float cap_size = 4.0f; const float bg_margin = 4.0f * U.pixelsize; const float bg_radius = 4.0f * U.pixelsize; const float arc_size = 64.0f * U.pixelsize; #define ARC_STEPS 24 const int arc_steps = ARC_STEPS; int i; //unsigned int color_act = 0x666600; unsigned int color_act = 0xffffff; unsigned int color_base = 0x0; unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80}; unsigned char color_text[3]; unsigned char color_wire[3]; /* anti-aliased lines for more consistent appearance */ glEnable(GL_LINE_SMOOTH); BLF_enable(blf_mono_font, BLF_ROTATION); BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi); BLF_rotation(blf_mono_font, 0.0f); UI_GetThemeColor3ubv(TH_TEXT, color_text); UI_GetThemeColor3ubv(TH_WIRE, color_wire); for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) { const bool is_act = (i == ruler_info->item_active); float dir_ruler[2]; float co_ss[3][2]; int j; /* should these be checked? - ok for now not to */ for (j = 0; j < 3; j++) { ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP); } glEnable(GL_BLEND); cpack(is_act ? color_act : color_base); if (ruler_item->flag & RULERITEM_USE_ANGLE) { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); /* arc */ { float dir_tmp[3]; float co_tmp[3]; float arc_ss_coords[ARC_STEPS + 1][2]; float dir_a[3]; float dir_b[3]; float quat[4]; float axis[3]; float angle; const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) * min_fff(arc_size, len_v2v2(co_ss[0], co_ss[1]) / 2.0f, len_v2v2(co_ss[2], co_ss[1]) / 2.0f)); sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]); sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]); normalize_v3(dir_a); normalize_v3(dir_b); cross_v3_v3v3(axis, dir_a, dir_b); angle = angle_normalized_v3v3(dir_a, dir_b); axis_angle_to_quat(quat, axis, angle / arc_steps); copy_v3_v3(dir_tmp, dir_a); glColor3ubv(color_wire); for (j = 0; j <= arc_steps; j++) { madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale); ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP); mul_qt_v3(quat, dir_tmp); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords); glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1); glDisableClientState(GL_VERTEX_ARRAY); } /* text */ { char numstr[256]; float numstr_size[2]; float pos[2]; const int prec = 2; /* XXX, todo, make optional */ ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); pos[0] = co_ss[1][0] + (cap_size * 2.0f); pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f); /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_rotation(blf_mono_font, 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec_a[2]; float rot_90_vec_b[2]; float cap[2]; sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]); rot_90_vec_a[0] = -dir_ruler[1]; rot_90_vec_a[1] = dir_ruler[0]; normalize_v2(rot_90_vec_a); sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]); rot_90_vec_b[0] = -dir_ruler[1]; rot_90_vec_b[1] = dir_ruler[0]; normalize_v2(rot_90_vec_b); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size); glVertex2fv(cap); /* angle vertex */ glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size); glEnd(); glDisable(GL_BLEND); } } else { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]); /* text */ { char numstr[256]; float numstr_size[2]; const int prec = 6; /* XXX, todo, make optional */ float pos[2]; ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); mid_v2_v2v2(pos, co_ss[0], co_ss[2]); /* center text */ pos[0] -= numstr_size[0] / 2.0f; pos[1] -= numstr_size[1] / 2.0f; /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]}; float cap[2]; normalize_v2(rot_90_vec); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size); glVertex2fv(cap); glEnd(); glDisable(GL_BLEND); } } } glDisable(GL_LINE_SMOOTH); BLF_disable(blf_mono_font, BLF_ROTATION); #undef ARC_STEPS /* draw snap */ if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) { ruler_item = ruler_item_active_get(ruler_info); if (ruler_item) { /* size from drawSnapping */ const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); float co_ss[3]; ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP); cpack(color_act); circ(co_ss[0], co_ss[1], size * U.pixelsize); } } }
static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2], RulerItem **r_ruler_item, int *r_co_index) { ARegion *ar = ruler_info->ar; RulerItem *ruler_item; float dist_best = RULER_PICK_DIST_SQ; RulerItem *ruler_item_best = NULL; int co_index_best = -1; for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) { float co_ss[3][2]; float dist; int j; /* should these be checked? - ok for now not to */ for (j = 0; j < 3; j++) { ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP); } if (ruler_item->flag & RULERITEM_USE_ANGLE) { dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]), dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2])); if (dist < dist_best) { dist_best = dist; ruler_item_best = ruler_item; { const float dist_points[3] = { len_squared_v2v2(co_ss[0], mval), len_squared_v2v2(co_ss[1], mval), len_squared_v2v2(co_ss[2], mval), }; if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) { co_index_best = min_axis_v3(dist_points); } else { co_index_best = -1; } } } } else { dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]); if (dist < dist_best) { dist_best = dist; ruler_item_best = ruler_item; { const float dist_points[2] = { len_squared_v2v2(co_ss[0], mval), len_squared_v2v2(co_ss[2], mval), }; if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) { co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2; } else { co_index_best = -1; } } } } } if (ruler_item_best) { *r_ruler_item = ruler_item_best; *r_co_index = co_index_best; return true; } else { *r_ruler_item = NULL; *r_co_index = -1; return false; } }
void applyProject(TransInfo *t) { /* XXX FLICKER IN OBJECT MODE */ if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) { TransData *td = t->data; float tvec[3]; float imat[4][4]; int i; if (t->flag & (T_EDIT | T_POSE)) { Object *ob = t->obedit ? t->obedit : t->poseobj; invert_m4_m4(imat, ob->obmat); } for (i = 0; i < t->total; i++, td++) { float iloc[3], loc[3], no[3]; float mval_fl[2]; float dist_px = TRANSFORM_DIST_MAX_PX; if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) continue; copy_v3_v3(iloc, td->loc); if (t->flag & (T_EDIT | T_POSE)) { Object *ob = t->obedit ? t->obedit : t->poseobj; mul_m4_v3(ob->obmat, iloc); } else if (t->flag & T_OBJECT) { /* TODO(sergey): Ideally force update is not needed here. */ td->ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME; BKE_object_handle_update(G.main->eval_ctx, t->scene, td->ob); copy_v3_v3(iloc, td->ob->obmat[3]); } if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { if (snapObjectsTransform( t, mval_fl, &dist_px, loc, no)) { // if (t->flag & (T_EDIT|T_POSE)) { // mul_m4_v3(imat, loc); // } sub_v3_v3v3(tvec, loc, iloc); mul_m3_v3(td->smtx, tvec); add_v3_v3(td->loc, tvec); if (t->tsnap.align && (t->flag & T_OBJECT)) { /* handle alignment as well */ const float *original_normal; float mat[3][3]; /* In pose mode, we want to align normals with Y axis of bones... */ original_normal = td->axismtx[2]; rotation_between_vecs_to_mat3(mat, original_normal, no); transform_data_ext_rotate(td, mat, true); /* TODO support constraints for rotation too? see ElementRotation */ } } } //XXX constraintTransLim(t, td); } } }