2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Contributor(s): Campbell Barton
20 * ***** END GPL LICENSE BLOCK *****
23 /** \file blender/editors/space_view3d/view3d_ruler.c
27 /* defines VIEW3D_OT_ruler modal operator */
29 #include "DNA_scene_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_gpencil_types.h"
33 #include "MEM_guardedalloc.h"
36 #include "BLI_blenlib.h"
38 #include "BKE_context.h"
40 #include "BKE_gpencil.h"
47 #include "ED_screen.h"
48 #include "ED_space_api.h"
51 #include "BIF_glutil.h"
53 #include "UI_resources.h"
54 #include "UI_interface.h"
56 #include "view3d_intern.h" /* own include */
59 /* -------------------------------------------------------------------- */
60 /* Snapping (could be own function) */
61 /* NOTE - this is not very nice use of transform snapping */
62 #include "ED_transform.h"
64 #define MVAL_MAX_PX_DIST 12.0f
67 * Convenience function for performing snapping.
70 * \param r_co hit location.
71 * \param r_no hit normal (optional).
72 * \param co_ss Screenspace coordinate.
73 * \param use_depth Snap to the closest element, use when using more then one snap type.
74 * \param use_obedit Use editmode cage.
75 * \param use_vert Snap to verts.
76 * \param use_edge Snap to edges.
77 * \param use_face Snap to faces.
78 * \return Snap success
80 static bool ED_view3d_snap_co(bContext *C, float r_co[3], float r_no[3], const float co_ss[2],
81 bool use_depth, bool use_obedit,
82 bool use_vert, bool use_edge, bool use_face)
84 float dist_px = MVAL_MAX_PX_DIST; /* snap dist */
86 float ray_dist = TRANSFORM_DIST_MAX_RAY;
88 float *r_no_ptr = r_no ? r_no : r_no_dummy;
90 Scene *scene = CTX_data_scene(C);
91 View3D *v3d = CTX_wm_view3d(C);
92 ARegion *ar = CTX_wm_region(C);
93 struct Object *obedit = use_obedit ? CTX_data_edit_object(C) : NULL;
95 BLI_assert(use_vert || use_edge || use_face);
97 /* try snap edge, then face if it fails */
99 ret |= snapObjectsEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_VERTEX,
100 co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL);
102 if (use_edge && (ret == false || use_depth)) {
103 if (use_depth == false) ray_dist = TRANSFORM_DIST_MAX_RAY;
104 ret |= snapObjectsEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_EDGE,
105 co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL);
107 if (use_face && (ret == false || use_depth)) {
108 if (use_depth == false) ray_dist = TRANSFORM_DIST_MAX_RAY;
109 ret |= snapObjectsEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_FACE,
110 co_ss, &dist_px, r_co, r_no_ptr, &ray_dist, SNAP_ALL);
116 static bool ED_view3d_snap_ray(bContext *C, float r_co[3],
117 const float ray_start[3], const float ray_normal[3])
119 float dist_px = MVAL_MAX_PX_DIST; /* snap dist */
121 float ray_dist = TRANSFORM_DIST_MAX_RAY;
124 Scene *scene = CTX_data_scene(C);
125 View3D *v3d = CTX_wm_view3d(C);
126 ARegion *ar = CTX_wm_region(C);
127 struct Object *obedit = CTX_data_edit_object(C);
129 /* try snap edge, then face if it fails */
130 ret = snapObjectsRayEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_FACE,
132 ray_start, ray_normal, &ray_dist,
133 NULL, &dist_px, r_co, r_no_dummy, SNAP_ALL);
140 /* -------------------------------------------------------------------- */
141 /* Ruler Item (we can have many) */
143 RULERITEM_USE_ANGLE = (1 << 0), /* use protractor */
144 RULERITEM_USE_RAYCAST = (1 << 1)
148 RULERITEM_DIRECTION_IN = 0,
149 RULERITEM_DIRECTION_OUT
152 /* keep smaller then selection, since we may want click elsewhere without selecting a ruler */
153 #define RULER_PICK_DIST 12.0f
154 #define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
156 typedef struct RulerItem {
157 struct RulerItem *next, *prev;
159 /* worldspace coords, middle being optional */
163 char co_index; /* 0 -> 2*/
166 int raycast_dir; /* RULER_DIRECTION_* */
170 /* -------------------------------------------------------------------- */
171 /* Ruler Info (one per session) */
174 RULER_STATE_NORMAL = 0,
179 RULER_SNAP_OK = (1 << 0),
182 typedef struct RulerInfo {
188 float drag_start_co[3];
194 void *draw_handle_pixel;
197 /* -------------------------------------------------------------------- */
198 /* local functions */
199 static RulerItem *ruler_item_add(RulerInfo *ruler_info)
201 RulerItem *ruler_item = MEM_callocN(sizeof(RulerItem), "RulerItem");
202 BLI_addtail(&ruler_info->items, ruler_item);
206 static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item)
208 BLI_remlink(&ruler_info->items, ruler_item);
209 MEM_freeN(ruler_item);
212 static RulerItem *ruler_item_active_get(RulerInfo *ruler_info)
214 return BLI_findlink(&ruler_info->items, ruler_info->item_active);
217 static void ruler_item_active_set(RulerInfo *ruler_info, RulerItem *ruler_item)
219 ruler_info->item_active = BLI_findindex(&ruler_info->items, ruler_item);
222 static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
223 char *numstr, size_t numstr_size, int prec)
225 const int do_split = unit->flag & USER_UNIT_OPT_SPLIT;
227 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
228 const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
232 if (unit->system == USER_UNIT_NONE) {
233 BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
236 bUnit_AsString(numstr, numstr_size,
238 prec, unit->system, B_UNIT_ROTATION, do_split, false);
242 const float ruler_len = len_v3v3(ruler_item->co[0],
245 if (unit->system == USER_UNIT_NONE) {
246 BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
249 bUnit_AsString(numstr, numstr_size,
250 (double)(ruler_len * unit->scale_length),
251 prec, unit->system, B_UNIT_LENGTH, do_split, false);
257 static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2],
258 RulerItem **r_ruler_item, int *r_co_index)
260 ARegion *ar = ruler_info->ar;
261 RulerItem *ruler_item;
263 float dist_best = RULER_PICK_DIST_SQ;
264 RulerItem *ruler_item_best = NULL;
265 int co_index_best = -1;
267 for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
272 /* should these be checked? - ok for now not to */
273 for (j = 0; j < 3; j++) {
274 ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
277 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
278 dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
279 dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
280 if (dist < dist_best) {
282 ruler_item_best = ruler_item;
285 float dist_points[3] = {len_squared_v2v2(co_ss[0], mval),
286 len_squared_v2v2(co_ss[1], mval),
287 len_squared_v2v2(co_ss[2], mval)};
288 if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
289 co_index_best = min_axis_v3(dist_points);
298 dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
299 if (dist < dist_best) {
301 ruler_item_best = ruler_item;
304 float dist_points[2] = {len_squared_v2v2(co_ss[0], mval),
305 len_squared_v2v2(co_ss[2], mval)};
306 if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
307 co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
317 if (ruler_item_best) {
318 *r_ruler_item = ruler_item_best;
319 *r_co_index = co_index_best;
323 *r_ruler_item = NULL;
329 #define RULER_ID "RulerData3D"
330 static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
332 Scene *scene = CTX_data_scene(C);
336 RulerItem *ruler_item;
337 const char *ruler_name = RULER_ID;
340 if (scene->gpd == NULL) {
341 scene->gpd = gpencil_data_addnew("GPencil");
344 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
346 gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false);
348 gpl->flag |= GP_LAYER_HIDE;
351 gpf = gpencil_layer_getframe(gpl, CFRA, true);
352 free_gpencil_strokes(gpf);
354 for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
358 /* allocate memory for a new stroke */
359 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
360 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
362 pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
363 for (j = 0; j < 3; j++) {
364 copy_v3_v3(&pt->x, ruler_item->co[j]);
371 pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
372 for (j = 0; j < 3; j += 2) {
373 copy_v3_v3(&pt->x, ruler_item->co[j]);
378 gps->flag = GP_STROKE_3DSPACE;
379 BLI_addtail(&gpf->strokes, gps);
386 static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info)
388 Scene *scene = CTX_data_scene(C);
393 const char *ruler_name = RULER_ID;
394 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
397 gpf = gpencil_layer_getframe(gpl, CFRA, false);
400 for (gps = gpf->strokes.first; gps; gps = gps->next) {
401 bGPDspoint *pt = gps->points;
403 if (gps->totpoints == 3) {
404 RulerItem *ruler_item = ruler_item_add(ruler_info);
405 for (j = 0; j < 3; j++) {
406 copy_v3_v3(ruler_item->co[j], &pt->x);
409 ruler_item->flag |= RULERITEM_USE_ANGLE;
412 else if (gps->totpoints == 2) {
413 RulerItem *ruler_item = ruler_item_add(ruler_info);
414 for (j = 0; j < 3; j += 2) {
415 copy_v3_v3(ruler_item->co[j], &pt->x);
428 /* -------------------------------------------------------------------- */
429 /* local callbacks */
431 static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg)
433 Scene *scene = CTX_data_scene(C);
434 UnitSettings *unit = &scene->unit;
435 RulerItem *ruler_item;
436 RulerInfo *ruler_info = arg;
437 RegionView3D *rv3d = ruler_info->ar->regiondata;
438 // ARegion *ar = ruler_info->ar;
439 const float cap_size = 4.0f;
440 const float bg_margin = 4.0f * U.pixelsize;
441 const float bg_radius = 4.0f * U.pixelsize;
442 const float arc_size = 64.0f * U.pixelsize;
444 const int arc_steps = ARC_STEPS;
446 //unsigned int color_act = 0x666600;
447 unsigned int color_act = 0xffffff;
448 unsigned int color_base = 0x0;
449 unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80};
450 unsigned char color_text[3];
451 unsigned char color_wire[3];
453 /* anti-aliased lines for more consistent appearance */
454 glEnable(GL_LINE_SMOOTH);
456 BLF_enable(blf_mono_font, BLF_ROTATION);
457 BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
458 BLF_rotation(blf_mono_font, 0.0f);
460 UI_GetThemeColor3ubv(TH_TEXT, color_text);
461 UI_GetThemeColor3ubv(TH_WIRE, color_wire);
463 for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) {
464 const bool is_act = (i == ruler_info->item_active);
469 /* should these be checked? - ok for now not to */
470 for (j = 0; j < 3; j++) {
471 ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
476 cpack(is_act ? color_act : color_base);
478 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
479 glBegin(GL_LINE_STRIP);
480 for (j = 0; j < 3; j++) {
481 glVertex2fv(co_ss[j]);
486 glBegin(GL_LINE_STRIP);
487 for (j = 0; j < 3; j++) {
488 glVertex2fv(co_ss[j]);
497 float arc_ss_coords[ARC_STEPS + 1][2];
504 const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
506 len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
507 len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
509 sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
510 sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
514 cross_v3_v3v3(axis, dir_a, dir_b);
515 angle = angle_normalized_v3v3(dir_a, dir_b);
517 axis_angle_to_quat(quat, axis, angle / arc_steps);
519 copy_v3_v3(dir_tmp, dir_a);
521 glColor3ubv(color_wire);
523 for (j = 0; j <= arc_steps; j++) {
524 madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
525 ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP);
526 mul_qt_v3(quat, dir_tmp);
529 glEnableClientState(GL_VERTEX_ARRAY);
530 glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords);
531 glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1);
532 glDisableClientState(GL_VERTEX_ARRAY);
538 float numstr_size[2];
540 const int prec = 2; /* XXX, todo, make optional */
542 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
544 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
546 pos[0] = co_ss[1][0] + (cap_size * 2.0f);
547 pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
550 glColor4ubv(color_back);
551 uiSetRoundBox(UI_CNR_ALL);
552 uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin,
553 pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1],
556 glColor3ubv(color_text);
557 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
558 BLF_rotation(blf_mono_font, 0.0f);
559 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
564 float rot_90_vec_a[2];
565 float rot_90_vec_b[2];
568 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
569 rot_90_vec_a[0] = -dir_ruler[1];
570 rot_90_vec_a[1] = dir_ruler[0];
571 normalize_v2(rot_90_vec_a);
573 sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
574 rot_90_vec_b[0] = -dir_ruler[1];
575 rot_90_vec_b[1] = dir_ruler[0];
576 normalize_v2(rot_90_vec_b);
580 glColor3ubv(color_wire);
584 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
586 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
589 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
591 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
595 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
596 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
597 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
598 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
605 glBegin(GL_LINE_STRIP);
606 for (j = 0; j < 3; j += 2) {
607 glVertex2fv(co_ss[j]);
612 glBegin(GL_LINE_STRIP);
613 for (j = 0; j < 3; j += 2) {
614 glVertex2fv(co_ss[j]);
619 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
624 float numstr_size[2];
625 const int prec = 6; /* XXX, todo, make optional */
628 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
630 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
632 mid_v2_v2v2(pos, co_ss[0], co_ss[2]);
635 pos[0] -= numstr_size[0] / 2.0f;
636 pos[1] -= numstr_size[1] / 2.0f;
639 glColor4ubv(color_back);
640 uiSetRoundBox(UI_CNR_ALL);
641 uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin,
642 pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1],
645 glColor3ubv(color_text);
646 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
647 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
652 float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
655 normalize_v2(rot_90_vec);
658 glColor3ubv(color_wire);
661 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
663 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
666 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
668 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
677 glDisable(GL_LINE_SMOOTH);
679 BLF_disable(blf_mono_font, BLF_ROTATION);
684 if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) {
685 ruler_item = ruler_item_active_get(ruler_info);
687 /* size from drawSnapping */
688 const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
690 ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP);
693 circ(co_ss[0], co_ss[1], size * U.pixelsize);
699 /* free, use for both cancel and finish */
700 static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_info)
702 ED_region_draw_cb_exit(ruler_info->ar->type, ruler_info->draw_handle_pixel);
705 static void view3d_ruler_free(RulerInfo *ruler_info)
707 BLI_freelistN(&ruler_info->items);
708 MEM_freeN(ruler_info);
711 static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3],
714 ED_view3d_win_to_3d_int(ruler_info->ar, r_co, xy, r_co);
717 /* use for mousemove events */
718 static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const int mval[2],
719 const bool do_thickness, const bool do_snap)
721 const float eps_bias = 0.0002f;
722 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
724 ruler_info->snap_flag &= ~RULER_SNAP_OK;
727 float *co = ruler_item->co[ruler_item->co_index];
728 /* restore the initial depth */
729 copy_v3_v3(co, ruler_info->drag_start_co);
730 view3d_ruler_item_project(ruler_info, co, mval);
731 if (do_thickness && ruler_item->co_index != 1) {
732 const float mval_fl[2] = {UNPACK2(mval)};
737 co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0];
739 if (ED_view3d_snap_co(C, co, ray_normal, mval_fl, true, false,
742 negate_v3(ray_normal);
744 madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias);
745 ED_view3d_snap_ray(C, co_other,
746 ray_start, ray_normal);
750 const float mval_fl[2] = {UNPACK2(mval)};
751 View3D *v3d = CTX_wm_view3d(C);
752 bool use_depth = (v3d->drawtype >= OB_SOLID);
753 bool is_hit = ED_view3d_snap_co(C, co, NULL, mval_fl, use_depth, false,
754 true, true, use_depth);
757 ruler_info->snap_flag |= RULER_SNAP_OK;
767 static void view3d_ruler_header_update(ScrArea *sa)
769 const char *text = "Ctrl+LMB: Add, "
772 "Shift+Drag: Thickness, "
773 "Ctrl+C: Copy Value, "
777 ED_area_headerprint(sa, text);
780 /* -------------------------------------------------------------------- */
781 /* Operator callbacks */
783 static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
785 wmWindow *win = CTX_wm_window(C);
786 ScrArea *sa = CTX_wm_area(C);
787 ARegion *ar = CTX_wm_region(C);
788 RulerInfo *ruler_info;
790 ruler_info = MEM_callocN(sizeof(RulerInfo), "RulerInfo");
792 if (view3d_ruler_from_gpencil(C, ruler_info)) {
793 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
796 op->customdata = ruler_info;
798 ruler_info->win = win;
801 ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel,
802 ruler_info, REGION_DRAW_POST_PIXEL);
804 view3d_ruler_header_update(sa);
806 WM_cursor_modal_set(win, BC_CROSSCURSOR);
807 WM_event_add_modal_handler(C, op);
809 return OPERATOR_RUNNING_MODAL;
812 static int view3d_ruler_cancel(bContext *C, wmOperator *op)
814 RulerInfo *ruler_info = op->customdata;
816 view3d_ruler_end(C, ruler_info);
817 view3d_ruler_free(ruler_info);
818 op->customdata = NULL;
820 return OPERATOR_CANCELLED;
823 static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event)
825 bool do_draw = false;
826 int exit_code = OPERATOR_RUNNING_MODAL;
827 RulerInfo *ruler_info = op->customdata;
828 ScrArea *sa = ruler_info->sa;
829 ARegion *ar = ruler_info->ar;
830 RegionView3D *rv3d = ar->regiondata;
832 /* its possible to change spaces while running the operator [#34894] */
833 if (UNLIKELY(ar != CTX_wm_region(C))) {
834 exit_code = OPERATOR_FINISHED;
838 switch (event->type) {
840 if (event->val == KM_RELEASE) {
841 if (ruler_info->state == RULER_STATE_DRAG) {
842 /* rubber-band angle removal */
843 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
844 if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
845 if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
846 ruler_item->flag &= ~RULERITEM_USE_ANGLE;
850 if (ruler_info->snap_flag & RULER_SNAP_OK) {
851 ruler_info->snap_flag &= ~RULER_SNAP_OK;
854 ruler_info->state = RULER_STATE_NORMAL;
858 if (ruler_info->state == RULER_STATE_NORMAL) {
861 /* weak - but user friendly */
862 (ruler_info->items.first == NULL))
864 View3D *v3d = CTX_wm_view3d(C);
865 const bool use_depth = (v3d->drawtype >= OB_SOLID);
867 /* Create new line */
868 RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info);
869 RulerItem *ruler_item;
870 /* check if we want to drag an existing point or add a new one */
871 ruler_info->state = RULER_STATE_DRAG;
873 ruler_item = ruler_item_add(ruler_info);
874 ruler_item_active_set(ruler_info, ruler_item);
877 /* snap the first point added, not essential but handy */
878 ruler_item->co_index = 0;
879 view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true);
880 copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]);
883 /* initial depth either previous ruler, view offset */
884 if (ruler_item_prev) {
885 copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]);
888 negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs);
891 copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co);
892 view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
895 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
896 ruler_item->co_index = 2;
901 float mval_fl[2] = {UNPACK2(event->mval)};
902 RulerItem *ruler_item_pick;
905 /* select and drag */
906 if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
907 if (co_index == -1) {
908 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
909 /* Add Center Point */
910 ruler_item_active_set(ruler_info, ruler_item_pick);
911 ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
912 ruler_item_pick->co_index = 1;
913 ruler_info->state = RULER_STATE_DRAG;
915 /* find the factor */
920 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
921 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
923 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
924 CLAMP(fac, 0.0f, 1.0f);
926 interp_v3_v3v3(ruler_item_pick->co[1],
927 ruler_item_pick->co[0],
928 ruler_item_pick->co[2], fac);
931 /* update the new location */
932 view3d_ruler_item_mousemove(C, ruler_info, event->mval,
933 event->shift != 0, event->ctrl != 0);
938 ruler_item_active_set(ruler_info, ruler_item_pick);
939 ruler_item_pick->co_index = co_index;
940 ruler_info->state = RULER_STATE_DRAG;
942 /* store the initial depth */
943 copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]);
949 exit_code = OPERATOR_PASS_THROUGH;
959 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
963 Scene *scene = CTX_data_scene(C);
964 UnitSettings *unit = &scene->unit;
966 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
967 WM_clipboard_text_set((void *) numstr, false);
975 WM_event_add_mousemove(C);
980 if (ruler_info->state == RULER_STATE_DRAG) {
981 if (view3d_ruler_item_mousemove(C, ruler_info, event->mval,
982 event->shift != 0, event->ctrl != 0))
992 exit_code = OPERATOR_CANCELLED;
997 view3d_ruler_to_gpencil(C, ruler_info);
999 exit_code = OPERATOR_FINISHED;
1004 if (event->val == KM_PRESS) {
1005 if (ruler_info->state == RULER_STATE_NORMAL) {
1006 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
1008 RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next;
1009 ruler_item_remove(ruler_info, ruler_item);
1010 ruler_item_active_set(ruler_info, ruler_item_other);
1018 exit_code = OPERATOR_PASS_THROUGH;
1024 view3d_ruler_header_update(sa);
1026 /* all 3d views draw rulers */
1027 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
1031 if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
1032 WM_cursor_modal_restore(ruler_info->win);
1034 view3d_ruler_end(C, ruler_info);
1035 view3d_ruler_free(ruler_info);
1036 op->customdata = NULL;
1038 ED_area_headerprint(sa, NULL);
1044 void VIEW3D_OT_ruler(wmOperatorType *ot)
1047 ot->name = "Ruler/Protractor";
1048 ot->description = "Interactive ruler";
1049 ot->idname = "VIEW3D_OT_ruler";
1052 ot->invoke = view3d_ruler_invoke;
1053 ot->cancel = view3d_ruler_cancel;
1054 ot->modal = view3d_ruler_modal;
1055 ot->poll = ED_operator_view3d_active;