ruler tweak: don't, snap to edit-object, because it didnt work well when modifiers...
[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 #include "DNA_object_types.h"
31 #include "DNA_gpencil_types.h"
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_math.h"
36 #include "BLI_blenlib.h"
37
38 #include "BKE_context.h"
39 #include "BKE_unit.h"
40 #include "BKE_gpencil.h"
41
42 #include "BIF_gl.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "ED_screen.h"
48 #include "ED_space_api.h"
49
50 #include "BLF_api.h"
51 #include "BIF_glutil.h"
52
53 #include "UI_resources.h"
54 #include "UI_interface.h"
55
56 #include "view3d_intern.h"  /* own include */
57
58
59 /* -------------------------------------------------------------------- */
60 /* Snapping (could be own function) */
61 /* NOTE - this is not very nice use of transform snapping */
62 #include "ED_transform.h"
63
64 #define MVAL_MAX_PX_DIST 12.0f
65
66 /**
67  * Convenience function for performing snapping.
68  *
69  * \param C  Context.
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
79  */
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)
83 {
84         float dist_px = MVAL_MAX_PX_DIST;  /* snap dist */
85         float r_no_dummy[3];
86         float ray_dist = TRANSFORM_DIST_MAX_RAY;
87         bool ret = false;
88         float *r_no_ptr = r_no ? r_no : r_no_dummy;
89
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;
94
95         BLI_assert(use_vert || use_edge || use_face);
96
97         /* try snap edge, then face if it fails */
98         if (use_vert) {
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);
101         }
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);
106         }
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);
111         }
112
113         return ret;
114 }
115
116 static bool ED_view3d_snap_ray(bContext *C, float r_co[3],
117                                const float ray_start[3], const float ray_normal[3])
118 {
119         float dist_px = MVAL_MAX_PX_DIST;  /* snap dist */
120         float r_no_dummy[3];
121         float ray_dist = TRANSFORM_DIST_MAX_RAY;
122         bool ret;
123
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);
128
129         /* try snap edge, then face if it fails */
130         ret = snapObjectsRayEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_FACE,
131                                NULL, NULL,
132                                ray_start, ray_normal, &ray_dist,
133                                NULL, &dist_px, r_co, r_no_dummy, SNAP_ALL);
134
135         return ret;
136 }
137 /* done snapping */
138
139
140 /* -------------------------------------------------------------------- */
141 /* Ruler Item (we can have many) */
142 enum {
143         RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
144         RULERITEM_USE_RAYCAST = (1 << 1)
145 };
146
147 enum {
148         RULERITEM_DIRECTION_IN = 0,
149         RULERITEM_DIRECTION_OUT
150 };
151
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)
155
156 typedef struct RulerItem {
157         struct RulerItem *next, *prev;
158
159         /* worldspace coords, middle being optional */
160         float co[3][3];
161
162         /* selected coord */
163         char  co_index; /* 0 -> 2*/
164
165         int   flag;
166         int   raycast_dir;  /* RULER_DIRECTION_* */
167 } RulerItem;
168
169
170 /* -------------------------------------------------------------------- */
171 /* Ruler Info (one per session) */
172
173 enum {
174         RULER_STATE_NORMAL = 0,
175         RULER_STATE_DRAG
176 };
177
178 enum {
179         RULER_SNAP_OK = (1 << 0),
180 };
181
182 typedef struct RulerInfo {
183         ListBase items;
184         int      item_active;
185         int flag;
186         int snap_flag;
187         int state;
188         float drag_start_co[3];
189
190         /* wm state */
191         wmWindow *win;
192         ScrArea *sa;
193         ARegion *ar;
194         void *draw_handle_pixel;
195 } RulerInfo;
196
197 /* -------------------------------------------------------------------- */
198 /* local functions */
199 static RulerItem *ruler_item_add(RulerInfo *ruler_info)
200 {
201         RulerItem *ruler_item = MEM_callocN(sizeof(RulerItem), "RulerItem");
202         BLI_addtail(&ruler_info->items, ruler_item);
203         return ruler_item;
204 }
205
206 static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item)
207 {
208         BLI_remlink(&ruler_info->items, ruler_item);
209         MEM_freeN(ruler_item);
210 }
211
212 static RulerItem *ruler_item_active_get(RulerInfo *ruler_info)
213 {
214         return BLI_findlink(&ruler_info->items, ruler_info->item_active);
215 }
216
217 static void ruler_item_active_set(RulerInfo *ruler_info, RulerItem *ruler_item)
218 {
219         ruler_info->item_active = BLI_findindex(&ruler_info->items, ruler_item);
220 }
221
222 static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
223                                  char *numstr, size_t numstr_size, int prec)
224 {
225         const int do_split = unit->flag & USER_UNIT_OPT_SPLIT;
226
227         if (ruler_item->flag & RULERITEM_USE_ANGLE) {
228                 const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
229                                                        ruler_item->co[1],
230                                                        ruler_item->co[2]);
231
232                 if (unit->system == USER_UNIT_NONE) {
233                         BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
234                 }
235                 else {
236                         bUnit_AsString(numstr, numstr_size,
237                                        (double)ruler_angle,
238                                        prec, unit->system, B_UNIT_ROTATION, do_split, false);
239                 }
240         }
241         else {
242                 const float ruler_len = len_v3v3(ruler_item->co[0],
243                                                  ruler_item->co[2]);
244
245                 if (unit->system == USER_UNIT_NONE) {
246                         BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
247                 }
248                 else {
249                         bUnit_AsString(numstr, numstr_size,
250                                        (double)(ruler_len * unit->scale_length),
251                                        prec, unit->system, B_UNIT_LENGTH, do_split, false);
252                 }
253         }
254
255 }
256
257 static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2],
258                               RulerItem **r_ruler_item, int *r_co_index)
259 {
260         ARegion *ar = ruler_info->ar;
261         RulerItem *ruler_item;
262
263         float dist_best = RULER_PICK_DIST_SQ;
264         RulerItem *ruler_item_best = NULL;
265         int co_index_best = -1;
266
267         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
268                 float co_ss[3][2];
269                 float dist;
270                 int j;
271
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);
275                 }
276
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) {
281                                 dist_best = dist;
282                                 ruler_item_best = ruler_item;
283
284                                 {
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);
290                                         }
291                                         else {
292                                                 co_index_best = -1;
293                                         }
294                                 }
295                         }
296                 }
297                 else {
298                         dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
299                         if (dist < dist_best) {
300                                 dist_best = dist;
301                                 ruler_item_best = ruler_item;
302
303                                 {
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;
308                                         }
309                                         else {
310                                                 co_index_best = -1;
311                                         }
312                                 }
313                         }
314                 }
315         }
316
317         if (ruler_item_best) {
318                 *r_ruler_item = ruler_item_best;
319                 *r_co_index = co_index_best;
320                 return true;
321         }
322         else {
323                 *r_ruler_item = NULL;
324                 *r_co_index = -1;
325                 return false;
326         }
327 }
328
329 #define RULER_ID "RulerData3D"
330 static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
331 {
332         Scene *scene = CTX_data_scene(C);
333         bGPDlayer *gpl;
334         bGPDframe *gpf;
335         bGPDstroke *gps;
336         RulerItem *ruler_item;
337         const char *ruler_name = RULER_ID;
338         bool change = false;
339
340         if (scene->gpd == NULL) {
341                 scene->gpd = gpencil_data_addnew("GPencil");
342         }
343
344         gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
345         if (gpl == NULL) {
346                 gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false);
347                 gpl->thickness = 1;
348                 gpl->flag |= GP_LAYER_HIDE;
349         }
350
351         gpf = gpencil_layer_getframe(gpl, CFRA, true);
352         free_gpencil_strokes(gpf);
353
354         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
355                 bGPDspoint *pt;
356                 int j;
357
358                 /* allocate memory for a new stroke */
359                 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
360                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
361                         gps->totpoints = 3;
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]);
365                                 pt->pressure = 1.0f;
366                                 pt++;
367                         }
368                 }
369                 else {
370                         gps->totpoints = 2;
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]);
374                                 pt->pressure = 1.0f;
375                                 pt++;
376                         }
377                 }
378                 gps->flag = GP_STROKE_3DSPACE;
379                 BLI_addtail(&gpf->strokes, gps);
380                 change = true;
381         }
382
383         return change;
384 }
385
386 static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info)
387 {
388         Scene *scene = CTX_data_scene(C);
389         bool change = false;
390
391         if (scene->gpd) {
392                 bGPDlayer *gpl;
393                 const char *ruler_name = RULER_ID;
394                 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
395                 if (gpl) {
396                         bGPDframe *gpf;
397                         gpf = gpencil_layer_getframe(gpl, CFRA, false);
398                         if (gpf) {
399                                 bGPDstroke *gps;
400                                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
401                                         bGPDspoint *pt = gps->points;
402                                         int j;
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);
407                                                         pt++;
408                                                 }
409                                                 ruler_item->flag |= RULERITEM_USE_ANGLE;
410                                                 change = true;
411                                         }
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);
416                                                         pt++;
417                                                 }
418                                                 change = true;
419                                         }
420                                 }
421                         }
422                 }
423         }
424
425         return change;
426 }
427
428 /* -------------------------------------------------------------------- */
429 /* local callbacks */
430
431 static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg)
432 {
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;
443 #define ARC_STEPS 24
444         const int arc_steps = ARC_STEPS;
445         int i;
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];
452
453         /* anti-aliased lines for more consistent appearance */
454         glEnable(GL_LINE_SMOOTH);
455
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);
459
460         UI_GetThemeColor3ubv(TH_TEXT, color_text);
461         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
462
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);
465                 float dir_ruler[2];
466                 float co_ss[3][2];
467                 int j;
468
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);
472                 }
473
474                 glEnable(GL_BLEND);
475
476                 cpack(is_act ? color_act : color_base);
477
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]);
482                         }
483                         glEnd();
484                         cpack(0xaaaaaa);
485                         setlinestyle(3);
486                         glBegin(GL_LINE_STRIP);
487                         for (j = 0; j < 3; j++) {
488                                 glVertex2fv(co_ss[j]);
489                         }
490                         glEnd();
491                         setlinestyle(0);
492
493                         /* arc */
494                         {
495                                 float dir_tmp[3];
496                                 float co_tmp[3];
497                                 float arc_ss_coords[ARC_STEPS + 1][2];
498
499                                 float dir_a[3];
500                                 float dir_b[3];
501                                 float quat[4];
502                                 float axis[3];
503                                 float angle;
504                                 const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
505                                                         min_fff(arc_size,
506                                                                 len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
507                                                                 len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
508
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]);
511                                 normalize_v3(dir_a);
512                                 normalize_v3(dir_b);
513
514                                 cross_v3_v3v3(axis, dir_a, dir_b);
515                                 angle = angle_normalized_v3v3(dir_a, dir_b);
516
517                                 axis_angle_to_quat(quat, axis, angle / arc_steps);
518
519                                 copy_v3_v3(dir_tmp, dir_a);
520
521                                 glColor3ubv(color_wire);
522
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);
527                                 }
528
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);
533                         }
534
535                         /* text */
536                         {
537                                 char numstr[256];
538                                 float numstr_size[2];
539                                 float pos[2];
540                                 const int prec = 2;  /* XXX, todo, make optional */
541
542                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
543
544                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
545
546                                 pos[0] = co_ss[1][0] + (cap_size * 2.0f);
547                                 pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
548
549                                 /* draw text (bg) */
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],
554                                            bg_radius);
555                                 /* draw text */
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));
560                         }
561
562                         /* capping */
563                         {
564                                 float rot_90_vec_a[2];
565                                 float rot_90_vec_b[2];
566                                 float cap[2];
567
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);
572
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);
577
578                                 glEnable(GL_BLEND);
579
580                                 glColor3ubv(color_wire);
581
582                                 glBegin(GL_LINES);
583
584                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
585                                 glVertex2fv(cap);
586                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
587                                 glVertex2fv(cap);
588
589                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
590                                 glVertex2fv(cap);
591                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
592                                 glVertex2fv(cap);
593
594                                 /* angle vertex */
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);
599                                 glEnd();
600
601                                 glDisable(GL_BLEND);
602                         }
603                 }
604                 else {
605                         glBegin(GL_LINE_STRIP);
606                         for (j = 0; j < 3; j += 2) {
607                                 glVertex2fv(co_ss[j]);
608                         }
609                         glEnd();
610                         cpack(0xaaaaaa);
611                         setlinestyle(3);
612                         glBegin(GL_LINE_STRIP);
613                         for (j = 0; j < 3; j += 2) {
614                                 glVertex2fv(co_ss[j]);
615                         }
616                         glEnd();
617                         setlinestyle(0);
618
619                         sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
620
621                         /* text */
622                         {
623                                 char numstr[256];
624                                 float numstr_size[2];
625                                 const int prec = 6;  /* XXX, todo, make optional */
626                                 float pos[2];
627
628                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
629
630                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
631
632                                 mid_v2_v2v2(pos, co_ss[0], co_ss[2]);
633
634                                 /* center text */
635                                 pos[0] -= numstr_size[0] / 2.0f;
636                                 pos[1] -= numstr_size[1] / 2.0f;
637
638                                 /* draw text (bg) */
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],
643                                            bg_radius);
644                                 /* draw text */
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));
648                         }
649
650                         /* capping */
651                         {
652                                 float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
653                                 float cap[2];
654
655                                 normalize_v2(rot_90_vec);
656
657                                 glEnable(GL_BLEND);
658                                 glColor3ubv(color_wire);
659
660                                 glBegin(GL_LINES);
661                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
662                                 glVertex2fv(cap);
663                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
664                                 glVertex2fv(cap);
665
666                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
667                                 glVertex2fv(cap);
668                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
669                                 glVertex2fv(cap);
670                                 glEnd();
671
672                                 glDisable(GL_BLEND);
673                         }
674                 }
675         }
676
677         glDisable(GL_LINE_SMOOTH);
678
679         BLF_disable(blf_mono_font, BLF_ROTATION);
680
681 #undef ARC_STEPS
682
683         /* draw snap */
684         if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) {
685                 ruler_item = ruler_item_active_get(ruler_info);
686                 if (ruler_item) {
687                         /* size from drawSnapping */
688                         const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
689                         float co_ss[3];
690                         ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP);
691
692                         cpack(color_act);
693                         circ(co_ss[0], co_ss[1], size * U.pixelsize);
694                 }
695         }
696
697 }
698
699 /* free, use for both cancel and finish */
700 static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_info)
701 {
702         ED_region_draw_cb_exit(ruler_info->ar->type, ruler_info->draw_handle_pixel);
703 }
704
705 static void view3d_ruler_free(RulerInfo *ruler_info)
706 {
707         BLI_freelistN(&ruler_info->items);
708         MEM_freeN(ruler_info);
709 }
710
711 static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3],
712                                       const int xy[2])
713 {
714         ED_view3d_win_to_3d_int(ruler_info->ar, r_co, xy, r_co);
715 }
716
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)
720 {
721         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
722
723         ruler_info->snap_flag &= ~RULER_SNAP_OK;
724
725         if (ruler_item) {
726                 float *co = ruler_item->co[ruler_item->co_index];
727                 /* restore the initial depth */
728                 copy_v3_v3(co, ruler_info->drag_start_co);
729                 view3d_ruler_item_project(ruler_info, co, mval);
730                 if (do_thickness && ruler_item->co_index != 1) {
731                         const float mval_fl[2] = {UNPACK2(mval)};
732                         float ray_normal[3];
733                         float ray_start[3];
734                         float *co_other;
735
736                         co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0];
737
738                         if (ED_view3d_snap_co(C, co, ray_normal, mval_fl, true, false,
739                                               false, false, true))
740                         {
741                                 negate_v3(ray_normal);
742                                 /* add some bias */
743                                 madd_v3_v3v3fl(ray_start, co, ray_normal, 0.0001f);
744                                 ED_view3d_snap_ray(C, co_other,
745                                                    ray_start, ray_normal);
746                         }
747                 }
748                 else if (do_snap) {
749                         const float mval_fl[2] = {UNPACK2(mval)};
750                         View3D *v3d = CTX_wm_view3d(C);
751                         bool use_depth = (v3d->drawtype >= OB_SOLID);
752                         bool is_hit = ED_view3d_snap_co(C, co, NULL, mval_fl, use_depth, false,
753                                                         true, true, use_depth);
754
755                         if (is_hit) {
756                                 ruler_info->snap_flag |= RULER_SNAP_OK;
757                         }
758                 }
759                 return true;
760         }
761         else {
762                 return false;
763         }
764 }
765
766 static void view3d_ruler_header_update(ScrArea *sa)
767 {
768         const char *text = "Ctrl+LMB: Add, "
769                            "Del: Remove, "
770                            "Ctrl+Drag: Snap, "
771                            "Shift+Drag: Thickness, "
772                            "Ctrl+C: Copy Value, "
773                            "Enter: Store,  "
774                            "Esc: Cancel";
775
776         ED_area_headerprint(sa, text);
777 }
778
779 /* -------------------------------------------------------------------- */
780 /* Operator callbacks */
781
782 static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
783 {
784         wmWindow *win = CTX_wm_window(C);
785         ScrArea *sa = CTX_wm_area(C);
786         ARegion *ar = CTX_wm_region(C);
787         RulerInfo *ruler_info;
788
789         ruler_info = MEM_callocN(sizeof(RulerInfo), "RulerInfo");
790
791         if (view3d_ruler_from_gpencil(C, ruler_info)) {
792                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
793         }
794
795         op->customdata = ruler_info;
796
797         ruler_info->win = win;
798         ruler_info->sa = sa;
799         ruler_info->ar = ar;
800         ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel,
801                                                                    ruler_info, REGION_DRAW_POST_PIXEL);
802
803         view3d_ruler_header_update(sa);
804
805         WM_cursor_modal(win, BC_CROSSCURSOR);
806         WM_event_add_modal_handler(C, op);
807
808         return OPERATOR_RUNNING_MODAL;
809 }
810
811 static int view3d_ruler_cancel(bContext *C, wmOperator *op)
812 {
813         RulerInfo *ruler_info = op->customdata;
814
815         view3d_ruler_end(C, ruler_info);
816         view3d_ruler_free(ruler_info);
817         op->customdata = NULL;
818
819         return OPERATOR_CANCELLED;
820 }
821
822 static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event)
823 {
824         bool do_draw = false;
825         int exit_code = OPERATOR_RUNNING_MODAL;
826         RulerInfo *ruler_info = op->customdata;
827         ScrArea *sa = ruler_info->sa;
828         ARegion *ar = ruler_info->ar;
829         RegionView3D *rv3d = ar->regiondata;
830
831         /* its possible to change  spaces while running the operator [#34894] */
832         if (UNLIKELY(ar != CTX_wm_region(C))) {
833                 exit_code = OPERATOR_FINISHED;
834                 goto exit;
835         }
836
837         switch (event->type) {
838                 case LEFTMOUSE:
839                         if (event->val == KM_RELEASE) {
840                                 if (ruler_info->state == RULER_STATE_DRAG) {
841                                         /* rubber-band angle removal */
842                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
843                                         if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
844                                                 if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
845                                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
846                                                         do_draw = true;
847                                                 }
848                                         }
849                                         if (ruler_info->snap_flag & RULER_SNAP_OK) {
850                                                 ruler_info->snap_flag &= ~RULER_SNAP_OK;
851                                                 do_draw = true;
852                                         }
853                                         ruler_info->state = RULER_STATE_NORMAL;
854                                 }
855                         }
856                         else {
857                                 if (ruler_info->state == RULER_STATE_NORMAL) {
858
859                                         if (event->ctrl ||
860                                             /* weak - but user friendly */
861                                             (ruler_info->items.first == NULL))
862                                         {
863                                                 View3D *v3d = CTX_wm_view3d(C);
864                                                 const bool use_depth = (v3d->drawtype >= OB_SOLID);
865
866                                                 /* Create new line */
867                                                 RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info);
868                                                 RulerItem *ruler_item;
869                                                 /* check if we want to drag an existing point or add a new one */
870                                                 ruler_info->state = RULER_STATE_DRAG;
871
872                                                 ruler_item = ruler_item_add(ruler_info);
873                                                 ruler_item_active_set(ruler_info, ruler_item);
874
875                                                 if (use_depth) {
876                                                         /* snap the first point added, not essential but handy */
877                                                         ruler_item->co_index = 0;
878                                                         view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true);
879                                                         copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]);
880                                                 }
881                                                 else {
882                                                         /* initial depth either previous ruler, view offset */
883                                                         if (ruler_item_prev) {
884                                                                 copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]);
885                                                         }
886                                                         else {
887                                                                 negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs);
888                                                         }
889
890                                                         copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co);
891                                                         view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
892                                                 }
893
894                                                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
895                                                 ruler_item->co_index = 2;
896
897                                                 do_draw = true;
898                                         }
899                                         else {
900                                                 float mval_fl[2] = {UNPACK2(event->mval)};
901                                                 RulerItem *ruler_item_pick;
902                                                 int co_index;
903
904                                                 /* select and drag */
905                                                 if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
906                                                         if (co_index == -1) {
907                                                                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
908                                                                         /* Add Center Point */
909                                                                         ruler_item_active_set(ruler_info, ruler_item_pick);
910                                                                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
911                                                                         ruler_item_pick->co_index = 1;
912                                                                         ruler_info->state = RULER_STATE_DRAG;
913
914                                                                         /* find the factor */
915                                                                         {
916                                                                                 float co_ss[2][2];
917                                                                                 float fac;
918
919                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
920                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
921
922                                                                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
923                                                                                 CLAMP(fac, 0.0f, 1.0f);
924
925                                                                                 interp_v3_v3v3(ruler_item_pick->co[1],
926                                                                                                ruler_item_pick->co[0],
927                                                                                                ruler_item_pick->co[2], fac);
928                                                                         }
929
930                                                                         /* update the new location */
931                                                                         view3d_ruler_item_mousemove(C, ruler_info, event->mval,
932                                                                                                     event->shift != 0, event->ctrl != 0);
933                                                                         do_draw = true;
934                                                                 }
935                                                         }
936                                                         else {
937                                                                 ruler_item_active_set(ruler_info, ruler_item_pick);
938                                                                 ruler_item_pick->co_index = co_index;
939                                                                 ruler_info->state = RULER_STATE_DRAG;
940
941                                                                 /* store the initial depth */
942                                                                 copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]);
943
944                                                                 do_draw = true;
945                                                         }
946                                                 }
947                                                 else {
948                                                         exit_code = OPERATOR_PASS_THROUGH;
949                                                 }
950
951                                         }
952                                 }
953                         }
954                         break;
955                 case CKEY:
956                 {
957                         if (event->ctrl) {
958                                 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
959                                 if (ruler_item) {
960                                         const int prec = 8;
961                                         char numstr[256];
962                                         Scene *scene = CTX_data_scene(C);
963                                         UnitSettings *unit = &scene->unit;
964
965                                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
966                                         WM_clipboard_text_set((void *) numstr, false);
967                                 }
968                         }
969                 }
970                 case RIGHTCTRLKEY:
971                 case LEFTCTRLKEY:
972                 {
973                         WM_event_add_mousemove(C);
974                         break;
975                 }
976                 case MOUSEMOVE:
977                 {
978                         if (ruler_info->state == RULER_STATE_DRAG) {
979                                 if (view3d_ruler_item_mousemove(C, ruler_info, event->mval,
980                                                                 event->shift != 0, event->ctrl != 0))
981                                 {
982                                         do_draw = true;
983                                 }
984                         }
985                         break;
986                 }
987                 case ESCKEY:
988                 {
989                         do_draw = true;
990                         exit_code = OPERATOR_CANCELLED;
991                         break;
992                 }
993                 case RETKEY:
994                 {
995                         view3d_ruler_to_gpencil(C, ruler_info);
996                         do_draw = true;
997                         exit_code = OPERATOR_FINISHED;
998                         break;
999                 }
1000                 case DELKEY:
1001                 {
1002                         if (event->val == KM_PRESS) {
1003                                 if (ruler_info->state == RULER_STATE_NORMAL) {
1004                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
1005                                         if (ruler_item) {
1006                                                 RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next;
1007                                                 ruler_item_remove(ruler_info, ruler_item);
1008                                                 ruler_item_active_set(ruler_info, ruler_item_other);
1009                                                 do_draw = true;
1010                                         }
1011                                 }
1012                         }
1013                         break;
1014                 }
1015                 default:
1016                         exit_code = OPERATOR_PASS_THROUGH;
1017                         break;
1018
1019         }
1020
1021         if (do_draw) {
1022                 view3d_ruler_header_update(sa);
1023
1024                 /* all 3d views draw rulers */
1025                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
1026         }
1027
1028 exit:
1029         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
1030                 WM_cursor_restore(ruler_info->win);
1031
1032                 view3d_ruler_end(C, ruler_info);
1033                 view3d_ruler_free(ruler_info);
1034                 op->customdata = NULL;
1035
1036                 ED_area_headerprint(sa, NULL);
1037         }
1038
1039         return exit_code;
1040 }
1041
1042 void VIEW3D_OT_ruler(wmOperatorType *ot)
1043 {
1044         /* identifiers */
1045         ot->name = "Ruler/Protractor";
1046         ot->description = "Interactive ruler";
1047         ot->idname = "VIEW3D_OT_ruler";
1048
1049         /* api callbacks */
1050         ot->invoke = view3d_ruler_invoke;
1051         ot->cancel = view3d_ruler_cancel;
1052         ot->modal = view3d_ruler_modal;
1053         ot->poll = ED_operator_view3d_active;
1054
1055         /* flags */
1056         ot->flag = 0;
1057 }