66f9524bb86b1918065fa17666ab1e7169d2a1d1
[blender.git] / source / blender / editors / space_view3d / view3d_ruler.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/space_view3d/view3d_ruler.c
24  *  \ingroup spview3d
25  */
26
27 /* defines VIEW3D_OT_ruler modal operator */
28
29 #include "DNA_scene_types.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_math.h"
34 #include "BLI_blenlib.h"
35
36 #include "BKE_context.h"
37 #include "BKE_unit.h"
38
39 #include "BIF_gl.h"
40
41 #include "WM_api.h"
42 #include "WM_types.h"
43
44 #include "ED_screen.h"
45 #include "ED_space_api.h"
46
47 #include "BLF_api.h"
48 #include "BIF_glutil.h"
49
50 #include "UI_resources.h"
51 #include "UI_interface.h"
52
53 #include "view3d_intern.h"  /* own include */
54
55 /* -------------------------------------------------------------------- */
56 /* Ruler Item (we can have many) */
57 enum {
58         RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
59         RULERITEM_USE_RAYCAST = (1 << 1)
60 };
61
62 enum {
63         RULERITEM_DIRECTION_IN = 0,
64         RULERITEM_DIRECTION_OUT
65 };
66
67 #define RULER_PICK_DIST 75.0f
68 #define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
69
70 typedef struct RulerItem {
71         struct RulerItem *next, *prev;
72
73         /* worldspace coords, middle being optional */
74         float co[3][3];
75
76         /* selected coord */
77         char  co_index; /* 0 -> 2*/
78
79         int   flag;
80         int   raycast_dir;  /* RULER_DIRECTION_* */
81 } RulerItem;
82
83 enum {
84         RULER_STATE_NORMAL = 0,
85         RULER_STATE_DRAG
86 };
87
88
89 /* -------------------------------------------------------------------- */
90 /* Ruler Info (one per session) */
91
92 typedef struct RulerInfo {
93         ListBase items;
94         int      item_active;
95         int flag;
96         int snap_flag;
97         int state;
98
99         /* --- */
100         ARegion *ar;
101         void *draw_handle_pixel;
102 } RulerInfo;
103
104 /* -------------------------------------------------------------------- */
105 /* local functions */
106 static RulerItem *ruler_item_add(RulerInfo *ruler_info)
107 {
108         RulerItem *ruler_item = MEM_callocN(sizeof(RulerItem), "RulerItem");
109         BLI_addtail(&ruler_info->items, ruler_item);
110         return ruler_item;
111 }
112
113 #if 0
114 static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item)
115 {
116         BLI_remlink(&ruler_info->items, ruler_item);
117         MEM_freeN(ruler_item);
118 }
119 #endif
120
121 static RulerItem *ruler_item_active_get(RulerInfo *ruler_info)
122 {
123         return BLI_findlink(&ruler_info->items, ruler_info->item_active);
124 }
125
126 static void ruler_item_active_set(RulerInfo *ruler_info, RulerItem *ruler_item)
127 {
128         ruler_info->item_active = BLI_findindex(&ruler_info->items, ruler_item);
129 }
130
131 static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2],
132                               RulerItem **r_ruler_item, int *r_co_index)
133 {
134         ARegion *ar = ruler_info->ar;
135         RulerItem *ruler_item;
136
137         float dist_best = RULER_PICK_DIST_SQ;
138         RulerItem *ruler_item_best = NULL;
139         int co_index_best = -1;
140
141         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
142                 float co_ss[3][2];
143                 float dist;
144                 int j;
145
146                 /* should these be checked? - ok for now not to */
147                 for (j = 0; j < 3; j++) {
148                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
149                 }
150
151                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
152                         dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
153                                       dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
154                         if (dist < dist_best) {
155                                 dist_best = dist;
156                                 ruler_item_best = ruler_item;
157
158                                 {
159                                         float dist_points[3] = {len_squared_v2v2(co_ss[0], mval),
160                                                                 len_squared_v2v2(co_ss[1], mval),
161                                                                 len_squared_v2v2(co_ss[2], mval)};
162                                         if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
163                                                 co_index_best = min_axis_v3(dist_points);
164                                         }
165                                         else {
166                                                 co_index_best = -1;
167                                         }
168                                 }
169                         }
170                 }
171                 else {
172                         dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
173                         if (dist < dist_best) {
174                                 dist_best = dist;
175                                 ruler_item_best = ruler_item;
176
177                                 {
178                                         float dist_points[2] = {len_squared_v2v2(co_ss[0], mval),
179                                                                 len_squared_v2v2(co_ss[2], mval)};
180                                         if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
181                                                 co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
182                                         }
183                                         else {
184                                                 co_index_best = -1;
185                                         }
186                                 }
187                         }
188                 }
189         }
190
191         if (ruler_item_best) {
192                 *r_ruler_item = ruler_item_best;
193                 *r_co_index = co_index_best;
194                 return true;
195         }
196         else {
197                 *r_ruler_item = NULL;
198                 *r_co_index = -1;
199                 return false;
200         }
201 }
202
203 /* -------------------------------------------------------------------- */
204 /* local callbacks */
205
206 static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg)
207 {
208         Scene *scene = CTX_data_scene(C);
209         UnitSettings *unit = &scene->unit;
210         const int do_split = unit->flag & USER_UNIT_OPT_SPLIT;
211         RulerItem *ruler_item;
212         RulerInfo *ruler_info = arg;
213         RegionView3D *rv3d = ruler_info->ar->regiondata;
214 //      ARegion *ar = ruler_info->ar;
215         const float cap_size = 4.0f;
216         const float bg_margin = 4.0f * U.pixelsize;
217         const float bg_radius = 4.0f * U.pixelsize;
218         const float arc_size = 64.0f * U.pixelsize;
219 #define ARC_STEPS 24
220         const int arc_steps = ARC_STEPS;
221         int i;
222         //unsigned int color_act = 0x666600;
223         unsigned int color_act = 0xffffff;
224         unsigned int color_base = 0x0;
225         unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80};
226         unsigned char color_text[3];
227         unsigned char color_wire[3];
228
229         /* anti-aliased lines for more consistent appearance */
230         glEnable(GL_LINE_SMOOTH);
231
232         BLF_enable(blf_mono_font, BLF_ROTATION);
233         BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
234
235         UI_GetThemeColor3ubv(TH_TEXT, color_text);
236         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
237
238         for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) {
239                 const bool is_act = (i == ruler_info->item_active);
240                 float dir_ruler[2];
241                 float co_ss[3][2];
242                 int j;
243
244                 /* should these be checked? - ok for now not to */
245                 for (j = 0; j < 3; j++) {
246                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
247                 }
248
249                 glEnable(GL_BLEND);
250
251                 cpack(is_act ? color_act : color_base);
252
253                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
254                         const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
255                                                                ruler_item->co[1],
256                                                                ruler_item->co[2]);
257                         glBegin(GL_LINE_STRIP);
258                         for (j = 0; j < 3; j++) {
259                                 glVertex2fv(co_ss[j]);
260                         }
261                         glEnd();
262                         cpack(0xaaaaaa);
263                         setlinestyle(3);
264                         glBegin(GL_LINE_STRIP);
265                         for (j = 0; j < 3; j++) {
266                                 glVertex2fv(co_ss[j]);
267                         }
268                         glEnd();
269                         setlinestyle(0);
270
271                         /* arc */
272                         {
273                                 float dir_tmp[3];
274                                 float co_tmp[3];
275                                 float arc_ss_coords[ARC_STEPS + 1][2];
276
277                                 float dir_a[3];
278                                 float dir_b[3];
279                                 float quat[4];
280                                 float axis[3];
281                                 float angle;
282                                 int j;
283                                 const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
284                                                         min_fff(arc_size,
285                                                                 len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
286                                                                 len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
287
288                                 sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
289                                 sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
290                                 normalize_v3(dir_a);
291                                 normalize_v3(dir_b);
292
293                                 cross_v3_v3v3(axis, dir_a, dir_b);
294                                 angle = angle_normalized_v3v3(dir_a, dir_b);
295
296                                 axis_angle_to_quat(quat, axis, angle / arc_steps);
297
298                                 copy_v3_v3(dir_tmp, dir_a);
299
300                                 glColor3ubv(color_wire);
301
302                                 for(j = 0; j <= arc_steps; j++) {
303                                         madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
304                                         ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP);
305                                         mul_qt_v3(quat, dir_tmp);
306                                 }
307
308                                 glEnableClientState(GL_VERTEX_ARRAY);
309                                 glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords);
310                                 glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1);
311                                 glDisableClientState(GL_VERTEX_ARRAY);
312                         }
313
314                         /* text */
315                         {
316                                 char numstr[256];
317                                 float numstr_size[2];
318                                 float pos[2];
319                                 const int prec = 2;  /* XXX, todo, make optional */
320
321                                 if (unit->system == USER_UNIT_NONE) {
322                                         BLI_snprintf(numstr, sizeof(numstr), "%.*f°", prec, RAD2DEGF(ruler_angle));
323                                 }
324                                 else {
325                                         bUnit_AsString(numstr, sizeof(numstr),
326                                                        (double)ruler_angle,
327                                                        prec, unit->system, B_UNIT_ROTATION, do_split, false);
328                                 }
329                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
330
331                                 pos[0] = co_ss[1][0] + (cap_size * 2.0f);
332                                 pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
333
334                                 /* draw text (bg) */
335                                 glColor4ubv(color_back);
336                                 uiSetRoundBox(UI_CNR_ALL);
337                                 uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin,
338                                            pos[0] + numstr_size[0] + bg_margin, pos[1] + numstr_size[1] + bg_margin,
339                                            bg_radius);
340                                 /* draw text */
341                                 glColor3ubv(color_text);
342                                 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
343                                 BLF_rotation(blf_mono_font, 0.0f);
344                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
345                         }
346
347                         /* capping */
348                         {
349                                 float rot_90_vec_a[2];
350                                 float rot_90_vec_b[2];
351                                 float cap[2];
352
353                                 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
354                                 rot_90_vec_a[0] = -dir_ruler[1];
355                                 rot_90_vec_a[1] =  dir_ruler[0];
356                                 normalize_v2(rot_90_vec_a);
357
358                                 sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
359                                 rot_90_vec_b[0] = -dir_ruler[1];
360                                 rot_90_vec_b[1] =  dir_ruler[0];
361                                 normalize_v2(rot_90_vec_b);
362
363                                 glEnable(GL_BLEND);
364
365                                 glColor3ubv(color_wire);
366
367                                 glBegin(GL_LINES);
368
369                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
370                                 glVertex2fv(cap);
371                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
372                                 glVertex2fv(cap);
373
374                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
375                                 glVertex2fv(cap);
376                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
377                                 glVertex2fv(cap);
378
379                                 /* angle vertex */
380                                 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
381                                 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
382                                 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
383                                 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
384                                 glEnd();
385
386                                 glDisable(GL_BLEND);
387                         }
388                 }
389                 else {
390                         const float ruler_len = len_v3v3(ruler_item->co[0], ruler_item->co[2]);
391                         glBegin(GL_LINE_STRIP);
392                         for (j = 0; j < 3; j += 2) {
393                                 glVertex2fv(co_ss[j]);
394                         }
395                         glEnd();
396                         cpack(0xaaaaaa);
397                         setlinestyle(3);
398                         glBegin(GL_LINE_STRIP);
399                         for (j = 0; j < 3; j += 2) {
400                                 glVertex2fv(co_ss[j]);
401                         }
402                         glEnd();
403                         setlinestyle(0);
404
405                         sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
406
407                         /* text */
408                         {
409                                 char numstr[256];
410                                 float numstr_size[2];
411                                 const int prec = 6;  /* XXX, todo, make optional */
412                                 const float dir_default_x[2] = {1, 0};
413                                 float pos[2];
414                                 float numstr_angle;
415                                 bool flip_text;
416
417
418                                 /* angle for text */
419                                 numstr_angle = angle_signed_v2v2(dir_ruler, dir_default_x);
420
421                                 /* keep text upright */
422                                 if (numstr_angle >= (float)(M_PI / 2.0)) {
423                                         numstr_angle -= (float)M_PI;
424                                         flip_text = true;
425                                 }
426                                 else if (numstr_angle <= -(float)(M_PI / 2.0)) {
427                                         numstr_angle += (float)M_PI;
428                                         flip_text = true;
429                                 }
430                                 else {
431                                         flip_text = false;
432                                 }
433
434                                 if (unit->system == USER_UNIT_NONE) {
435                                         BLI_snprintf(numstr, sizeof(numstr), "%.*f", prec, ruler_len);
436                                 }
437                                 else {
438                                         bUnit_AsString(numstr, sizeof(numstr),
439                                                        (double)(ruler_len * unit->scale_length),
440                                                        prec, unit->system, B_UNIT_LENGTH, do_split, false);
441                                 }
442                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
443
444                                 mid_v2_v2v2(pos, co_ss[0], co_ss[2]);
445
446                                 /* center text */
447                                 normalize_v2(dir_ruler);
448                                 madd_v2_v2fl(pos, dir_ruler, numstr_size[0] / (flip_text ? 2.0f : -2.0f));
449
450                                 /* draw text (bg) */
451                                 glTranslatef(pos[0], pos[1], 0.0f);
452                                 glRotatef(RAD2DEGF(numstr_angle), 0.0f, 0.0f, 1.0f);
453                                 glColor4ubv(color_back);
454                                 uiSetRoundBox(UI_CNR_ALL);
455                                 uiRoundBox(-bg_margin, -bg_margin,
456                                            numstr_size[0] + bg_margin, numstr_size[1] + bg_margin,
457                                            bg_radius);
458                                 glRotatef(-RAD2DEGF(numstr_angle), 0.0f, 0.0f, 1.0f);
459                                 glTranslatef(-pos[0], -pos[1], 0.0f);
460                                 /* draw text */
461                                 glColor3ubv(color_text);
462                                 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
463                                 BLF_rotation(blf_mono_font, numstr_angle);
464                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
465                         }
466
467                         /* capping */
468                         {
469                                 float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
470                                 float cap[2];
471
472                                 normalize_v2(rot_90_vec);
473
474                                 glEnable(GL_BLEND);
475                                 glColor3ubv(color_wire);
476
477                                 glBegin(GL_LINES);
478                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
479                                 glVertex2fv(cap);
480                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
481                                 glVertex2fv(cap);
482
483                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
484                                 glVertex2fv(cap);
485                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
486                                 glVertex2fv(cap);
487                                 glEnd();
488
489                                 glDisable(GL_BLEND);
490                         }
491                 }
492         }
493
494         glDisable(GL_LINE_SMOOTH);
495
496         BLF_disable(blf_mono_font, BLF_ROTATION);
497
498 #undef ARC_STEPS
499 }
500
501 /* free, use for both cancel and finish */
502 static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_info)
503 {
504         ED_region_draw_cb_exit(ruler_info->ar->type, ruler_info->draw_handle_pixel);
505 }
506
507 static void view3d_ruler_free(RulerInfo *ruler_info)
508 {
509         BLI_freelistN(&ruler_info->items);
510         MEM_freeN(ruler_info);
511 }
512
513 static void view3d_ruler_item_project(bContext *C, RulerInfo *UNUSED(ruler_info), float r_co[3],
514                                       const int xy[2])
515 {
516         ED_view3d_cursor3d_position(C, r_co, xy);
517 }
518
519 /* use for mousemove events */
520 static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const wmEvent *event)
521 {
522         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
523
524         if (ruler_item) {
525                 view3d_ruler_item_project(C, ruler_info, ruler_item->co[ruler_item->co_index], event->mval);
526                 return true;
527         }
528         else {
529                 return false;
530         }
531 }
532
533 /* -------------------------------------------------------------------- */
534 /* Operator callbacks */
535
536 static int view3d_ruler_invoke(bContext *C, wmOperator *op, wmEvent *event)
537 {
538         ARegion *ar = CTX_wm_region(C);
539 //      RegionView3D *rv3d = CTX_wm_region_view3d(C);
540         RulerInfo *ruler_info;
541         (void)event;
542
543         ruler_info = MEM_callocN(sizeof(RulerInfo), "RulerInfo");
544
545         op->customdata = ruler_info;
546
547         ruler_info->ar = ar;
548         ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel, ruler_info, REGION_DRAW_POST_PIXEL);
549
550         WM_event_add_modal_handler(C, op);
551
552         return OPERATOR_RUNNING_MODAL;
553 }
554
555 static int view3d_ruler_cancel(bContext *C, wmOperator *op)
556 {
557         RulerInfo *ruler_info = op->customdata;
558
559         view3d_ruler_end(C, ruler_info);
560         view3d_ruler_free(ruler_info);
561         op->customdata = NULL;
562
563         return OPERATOR_CANCELLED;
564 }
565
566 static int view3d_ruler_modal(bContext *C, wmOperator *op, wmEvent *event)
567 {
568         bool do_draw = false;
569         int exit_code = OPERATOR_RUNNING_MODAL;
570         RulerInfo *ruler_info = op->customdata;
571         ARegion *ar = ruler_info->ar;
572         RegionView3D *rv3d = ar->regiondata;
573
574         (void)C;
575
576         switch (event->type) {
577                 case LEFTMOUSE:
578                         if (event->val == KM_RELEASE) {
579                                 if (ruler_info->state == RULER_STATE_DRAG) {
580                                         /* rubber-band angle removal */
581                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
582                                         if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
583                                                 if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
584                                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
585                                                         do_draw = true;
586                                                 }
587                                         }
588                                         ruler_info->state = RULER_STATE_NORMAL;
589                                 }
590                         }
591                         else {
592                                 if (ruler_info->state == RULER_STATE_NORMAL) {
593
594                                         if (event->ctrl) {
595                                                 /* Create new line */
596                                                 RulerItem *ruler_item;
597                                                 /* check if we want to drag an existing point or add a new one */
598                                                 ruler_info->state = RULER_STATE_DRAG;
599
600                                                 ruler_item = ruler_item_add(ruler_info);
601                                                 ruler_item_active_set(ruler_info, ruler_item);
602                                                 ruler_item->co_index = 2;
603
604                                                 negate_v3_v3(ruler_item->co[0], rv3d->ofs);
605                                                 view3d_ruler_item_project(C, ruler_info, ruler_item->co[0], event->mval);
606                                                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
607
608                                                 do_draw = true;
609                                         }
610                                         else {
611                                                 float mval_fl[2] = {UNPACK2(event->mval)};
612                                                 RulerItem *ruler_item_pick;
613                                                 int co_index;
614
615                                                 /* select and drag */
616                                                 if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
617                                                         if (co_index == -1) {
618                                                                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
619                                                                         /* Add Center Point */
620                                                                         ruler_item_active_set(ruler_info, ruler_item_pick);
621                                                                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
622                                                                         ruler_item_pick->co_index = 1;
623                                                                         ruler_info->state = RULER_STATE_DRAG;
624
625                                                                         /* find the factor */
626                                                                         {
627                                                                                 float co_ss[2][2];
628                                                                                 float fac;
629
630                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
631                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
632
633                                                                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
634                                                                                 CLAMP(fac, 0.0f, 1.0f);
635
636                                                                                 interp_v3_v3v3(ruler_item_pick->co[1],
637                                                                                                ruler_item_pick->co[0],
638                                                                                                ruler_item_pick->co[2], fac);
639                                                                         }
640
641                                                                         /* update the new location */
642                                                                         view3d_ruler_item_mousemove(C, ruler_info, event);
643                                                                         do_draw = true;
644                                                                 }
645                                                         }
646                                                         else {
647                                                                 ruler_item_active_set(ruler_info, ruler_item_pick);
648                                                                 ruler_item_pick->co_index = co_index;
649                                                                 ruler_info->state = RULER_STATE_DRAG;
650                                                                 do_draw = true;
651                                                         }
652                                                 }
653                                                 else {
654                                                         exit_code = OPERATOR_PASS_THROUGH;
655                                                 }
656
657                                         }
658                                 }
659                         }
660                         break;
661                 case MOUSEMOVE:
662                 {
663                         if (ruler_info->state == RULER_STATE_DRAG) {
664                                 if (view3d_ruler_item_mousemove(C, ruler_info, event)) {
665                                         do_draw = true;
666                                 }
667                         }
668                         break;
669                 }
670
671                 case ESCKEY:
672                 {
673                         do_draw = true;
674                         exit_code = OPERATOR_CANCELLED;
675                         break;
676                 }
677                 default:
678                         exit_code = OPERATOR_PASS_THROUGH;
679                         break;
680
681         }
682
683         if (do_draw) {
684                 ED_region_tag_redraw(ar);
685         }
686
687         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
688                 view3d_ruler_end(C, ruler_info);
689                 view3d_ruler_free(ruler_info);
690                 op->customdata = NULL;
691         }
692
693         return exit_code;
694 }
695
696 void VIEW3D_OT_ruler(wmOperatorType *ot)
697 {
698         /* identifiers */
699         ot->name = "3D Ruler";
700         ot->description = "Interactive ruler";
701         ot->idname = "VIEW3D_OT_ruler";
702
703         /* api callbacks */
704         ot->invoke = view3d_ruler_invoke;
705         ot->cancel = view3d_ruler_cancel;
706         ot->modal = view3d_ruler_modal;
707         ot->poll = ED_operator_view3d_active;
708
709         /* flags */
710         ot->flag = OPTYPE_BLOCKING;
711 }