3021932400ee885fa06bfe35ab145648e4ec2168
[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_gpencil_types.h"
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_math.h"
35 #include "BLI_blenlib.h"
36
37 #include "BKE_context.h"
38 #include "BKE_unit.h"
39 #include "BKE_gpencil.h"
40
41 #include "BIF_gl.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_screen.h"
47 #include "ED_space_api.h"
48
49 #include "BLF_api.h"
50 #include "BIF_glutil.h"
51
52 #include "UI_resources.h"
53 #include "UI_interface.h"
54
55 #include "view3d_intern.h"  /* own include */
56
57
58 /* -------------------------------------------------------------------- */
59 /* Snapping (could be own function) */
60 /* NOTE - this is not very nice use of transform snapping */
61 #include "ED_transform.h"
62 #include "../transform/transform.h"
63
64 static bool ED_view3d_snap_co(bContext *C, float r_co[3], const float co_ss[2],
65                               bool use_vert, bool use_edge, bool use_face)
66 {
67         TransInfo t = {0};
68         int dist = 12;  /* snap dist */
69         float r_no_dummy[3];
70         bool ret = false;
71         char  backup_snap_mode;
72         Base *backup_baseact;
73
74         t.scene  = CTX_data_scene(C);
75         t.view   = CTX_wm_view3d(C);
76         t.ar     = CTX_wm_region(C);
77         t.obedit = CTX_data_edit_object(C);
78
79         backup_snap_mode = t.scene->toolsettings->snap_mode;
80         backup_baseact = t.scene->basact;
81         t.scene->basact = NULL;
82
83         /* try snap edge, then face if it fails */
84         if (use_vert) {
85                 t.scene->toolsettings->snap_mode = SCE_SNAP_MODE_VERTEX;
86                 ret = snapObjectsTransform(&t, co_ss, &dist, r_co, r_no_dummy, SNAP_ALL);
87         }
88         if (use_edge && (ret == false)) {
89                 t.scene->toolsettings->snap_mode = SCE_SNAP_MODE_EDGE;
90                 ret = snapObjectsTransform(&t, co_ss, &dist, r_co, r_no_dummy, SNAP_ALL);
91         }
92         if (use_face && (ret == false)) {
93                 t.scene->toolsettings->snap_mode = SCE_SNAP_MODE_FACE;
94                 ret = snapObjectsTransform(&t, co_ss, &dist, r_co, r_no_dummy, SNAP_ALL);
95         }
96
97         t.scene->toolsettings->snap_mode = backup_snap_mode;
98         t.scene->basact = backup_baseact;
99
100         return ret;
101 }
102 /* done snapping */
103
104
105 /* -------------------------------------------------------------------- */
106 /* Ruler Item (we can have many) */
107 enum {
108         RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
109         RULERITEM_USE_RAYCAST = (1 << 1)
110 };
111
112 enum {
113         RULERITEM_DIRECTION_IN = 0,
114         RULERITEM_DIRECTION_OUT
115 };
116
117 #define RULER_PICK_DIST 75.0f
118 #define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
119
120 typedef struct RulerItem {
121         struct RulerItem *next, *prev;
122
123         /* worldspace coords, middle being optional */
124         float co[3][3];
125
126         /* selected coord */
127         char  co_index; /* 0 -> 2*/
128
129         int   flag;
130         int   raycast_dir;  /* RULER_DIRECTION_* */
131 } RulerItem;
132
133 enum {
134         RULER_STATE_NORMAL = 0,
135         RULER_STATE_DRAG
136 };
137
138
139 /* -------------------------------------------------------------------- */
140 /* Ruler Info (one per session) */
141
142 typedef struct RulerInfo {
143         ListBase items;
144         int      item_active;
145         int flag;
146         int snap_flag;
147         int state;
148
149         /* --- */
150         ARegion *ar;
151         void *draw_handle_pixel;
152 } RulerInfo;
153
154 /* -------------------------------------------------------------------- */
155 /* local functions */
156 static RulerItem *ruler_item_add(RulerInfo *ruler_info)
157 {
158         RulerItem *ruler_item = MEM_callocN(sizeof(RulerItem), "RulerItem");
159         BLI_addtail(&ruler_info->items, ruler_item);
160         return ruler_item;
161 }
162
163 static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item)
164 {
165         BLI_remlink(&ruler_info->items, ruler_item);
166         MEM_freeN(ruler_item);
167 }
168
169 static RulerItem *ruler_item_active_get(RulerInfo *ruler_info)
170 {
171         return BLI_findlink(&ruler_info->items, ruler_info->item_active);
172 }
173
174 static void ruler_item_active_set(RulerInfo *ruler_info, RulerItem *ruler_item)
175 {
176         ruler_info->item_active = BLI_findindex(&ruler_info->items, ruler_item);
177 }
178
179 static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
180                                  char *numstr, size_t numstr_size, int prec)
181 {
182         const int do_split = unit->flag & USER_UNIT_OPT_SPLIT;
183
184         if (ruler_item->flag & RULERITEM_USE_ANGLE) {
185                 const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
186                                                        ruler_item->co[1],
187                                                        ruler_item->co[2]);
188
189                 if (unit->system == USER_UNIT_NONE) {
190                         BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
191                 }
192                 else {
193                         bUnit_AsString(numstr, numstr_size,
194                                        (double)ruler_angle,
195                                        prec, unit->system, B_UNIT_ROTATION, do_split, false);
196                 }
197         }
198         else {
199                 const float ruler_len = len_v3v3(ruler_item->co[0],
200                                                  ruler_item->co[2]);
201
202                 if (unit->system == USER_UNIT_NONE) {
203                         BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
204                 }
205                 else {
206                         bUnit_AsString(numstr, numstr_size,
207                                        (double)(ruler_len * unit->scale_length),
208                                        prec, unit->system, B_UNIT_LENGTH, do_split, false);
209                 }
210         }
211
212 }
213
214 static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2],
215                               RulerItem **r_ruler_item, int *r_co_index)
216 {
217         ARegion *ar = ruler_info->ar;
218         RulerItem *ruler_item;
219
220         float dist_best = RULER_PICK_DIST_SQ;
221         RulerItem *ruler_item_best = NULL;
222         int co_index_best = -1;
223
224         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
225                 float co_ss[3][2];
226                 float dist;
227                 int j;
228
229                 /* should these be checked? - ok for now not to */
230                 for (j = 0; j < 3; j++) {
231                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
232                 }
233
234                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
235                         dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
236                                       dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
237                         if (dist < dist_best) {
238                                 dist_best = dist;
239                                 ruler_item_best = ruler_item;
240
241                                 {
242                                         float dist_points[3] = {len_squared_v2v2(co_ss[0], mval),
243                                                                 len_squared_v2v2(co_ss[1], mval),
244                                                                 len_squared_v2v2(co_ss[2], mval)};
245                                         if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
246                                                 co_index_best = min_axis_v3(dist_points);
247                                         }
248                                         else {
249                                                 co_index_best = -1;
250                                         }
251                                 }
252                         }
253                 }
254                 else {
255                         dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
256                         if (dist < dist_best) {
257                                 dist_best = dist;
258                                 ruler_item_best = ruler_item;
259
260                                 {
261                                         float dist_points[2] = {len_squared_v2v2(co_ss[0], mval),
262                                                                 len_squared_v2v2(co_ss[2], mval)};
263                                         if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
264                                                 co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
265                                         }
266                                         else {
267                                                 co_index_best = -1;
268                                         }
269                                 }
270                         }
271                 }
272         }
273
274         if (ruler_item_best) {
275                 *r_ruler_item = ruler_item_best;
276                 *r_co_index = co_index_best;
277                 return true;
278         }
279         else {
280                 *r_ruler_item = NULL;
281                 *r_co_index = -1;
282                 return false;
283         }
284 }
285
286 #define RULER_ID "RulerData3D"
287 static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
288 {
289         Scene *scene = CTX_data_scene(C);
290         bGPDlayer *gpl;
291         bGPDframe *gpf;
292         bGPDstroke *gps;
293         RulerItem *ruler_item;
294         const char *ruler_name = RULER_ID;
295         bool change = false;
296
297         if (scene->gpd == NULL) {
298                 scene->gpd = gpencil_data_addnew("GPencil");
299         }
300
301         gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
302         if (gpl == NULL) {
303                 gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false);
304                 gpl->thickness = 1;
305                 gpl->flag |= GP_LAYER_HIDE;
306         }
307
308         gpf = gpencil_layer_getframe(gpl, CFRA, true);
309         free_gpencil_strokes(gpf);
310
311         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
312                 bGPDspoint *pt;
313                 int j;
314
315                 /* allocate memory for a new stroke */
316                 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
317                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
318                         gps->totpoints = 3;
319                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
320                         for (j = 0; j < 3; j++) {
321                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
322                                 pt->pressure = 1.0f;
323                                 pt++;
324                         }
325                 }
326                 else {
327                         gps->totpoints = 2;
328                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
329                         for (j = 0; j < 3; j += 2) {
330                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
331                                 pt->pressure = 1.0f;
332                                 pt++;
333                         }
334                 }
335                 gps->flag = GP_STROKE_3DSPACE;
336                 BLI_addtail(&gpf->strokes, gps);
337                 change = true;
338         }
339
340         return change;
341 }
342
343 static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info)
344 {
345         Scene *scene = CTX_data_scene(C);
346         bool change = false;
347
348         if (scene->gpd) {
349                 bGPDlayer *gpl;
350                 const char *ruler_name = RULER_ID;
351                 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
352                 if (gpl) {
353                         bGPDframe *gpf;
354                         gpf = gpencil_layer_getframe(gpl, CFRA, false);
355                         if (gpf) {
356                                 bGPDstroke *gps;
357                                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
358                                         bGPDspoint *pt = gps->points;
359                                         int j;
360                                         if (gps->totpoints == 3) {
361                                                 RulerItem *ruler_item = ruler_item_add(ruler_info);
362                                                 for (j = 0; j < 3; j++) {
363                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
364                                                         pt++;
365                                                 }
366                                                 ruler_item->flag |= RULERITEM_USE_ANGLE;
367                                                 change = true;
368                                         }
369                                         else if (gps->totpoints == 2) {
370                                                 RulerItem *ruler_item = ruler_item_add(ruler_info);
371                                                 for (j = 0; j < 3; j++) {
372                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
373                                                         pt++;
374                                                 }
375                                                 change = true;
376                                         }
377                                 }
378                         }
379                 }
380         }
381
382         return change;
383 }
384
385 /* -------------------------------------------------------------------- */
386 /* local callbacks */
387
388 static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg)
389 {
390         Scene *scene = CTX_data_scene(C);
391         UnitSettings *unit = &scene->unit;
392         RulerItem *ruler_item;
393         RulerInfo *ruler_info = arg;
394         RegionView3D *rv3d = ruler_info->ar->regiondata;
395 //      ARegion *ar = ruler_info->ar;
396         const float cap_size = 4.0f;
397         const float bg_margin = 4.0f * U.pixelsize;
398         const float bg_radius = 4.0f * U.pixelsize;
399         const float arc_size = 64.0f * U.pixelsize;
400 #define ARC_STEPS 24
401         const int arc_steps = ARC_STEPS;
402         int i;
403         //unsigned int color_act = 0x666600;
404         unsigned int color_act = 0xffffff;
405         unsigned int color_base = 0x0;
406         unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80};
407         unsigned char color_text[3];
408         unsigned char color_wire[3];
409
410         /* anti-aliased lines for more consistent appearance */
411         glEnable(GL_LINE_SMOOTH);
412
413         BLF_enable(blf_mono_font, BLF_ROTATION);
414         BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
415
416         UI_GetThemeColor3ubv(TH_TEXT, color_text);
417         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
418
419         for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) {
420                 const bool is_act = (i == ruler_info->item_active);
421                 float dir_ruler[2];
422                 float co_ss[3][2];
423                 int j;
424
425                 /* should these be checked? - ok for now not to */
426                 for (j = 0; j < 3; j++) {
427                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
428                 }
429
430                 glEnable(GL_BLEND);
431
432                 cpack(is_act ? color_act : color_base);
433
434                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
435                         glBegin(GL_LINE_STRIP);
436                         for (j = 0; j < 3; j++) {
437                                 glVertex2fv(co_ss[j]);
438                         }
439                         glEnd();
440                         cpack(0xaaaaaa);
441                         setlinestyle(3);
442                         glBegin(GL_LINE_STRIP);
443                         for (j = 0; j < 3; j++) {
444                                 glVertex2fv(co_ss[j]);
445                         }
446                         glEnd();
447                         setlinestyle(0);
448
449                         /* arc */
450                         {
451                                 float dir_tmp[3];
452                                 float co_tmp[3];
453                                 float arc_ss_coords[ARC_STEPS + 1][2];
454
455                                 float dir_a[3];
456                                 float dir_b[3];
457                                 float quat[4];
458                                 float axis[3];
459                                 float angle;
460                                 int j;
461                                 const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
462                                                         min_fff(arc_size,
463                                                                 len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
464                                                                 len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
465
466                                 sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
467                                 sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
468                                 normalize_v3(dir_a);
469                                 normalize_v3(dir_b);
470
471                                 cross_v3_v3v3(axis, dir_a, dir_b);
472                                 angle = angle_normalized_v3v3(dir_a, dir_b);
473
474                                 axis_angle_to_quat(quat, axis, angle / arc_steps);
475
476                                 copy_v3_v3(dir_tmp, dir_a);
477
478                                 glColor3ubv(color_wire);
479
480                                 for (j = 0; j <= arc_steps; j++) {
481                                         madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
482                                         ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP);
483                                         mul_qt_v3(quat, dir_tmp);
484                                 }
485
486                                 glEnableClientState(GL_VERTEX_ARRAY);
487                                 glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords);
488                                 glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1);
489                                 glDisableClientState(GL_VERTEX_ARRAY);
490                         }
491
492                         /* text */
493                         {
494                                 char numstr[256];
495                                 float numstr_size[2];
496                                 float pos[2];
497                                 const int prec = 2;  /* XXX, todo, make optional */
498
499                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
500
501                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
502
503                                 pos[0] = co_ss[1][0] + (cap_size * 2.0f);
504                                 pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
505
506                                 /* draw text (bg) */
507                                 glColor4ubv(color_back);
508                                 uiSetRoundBox(UI_CNR_ALL);
509                                 uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin,
510                                            pos[0] + numstr_size[0] + bg_margin, pos[1] + numstr_size[1] + bg_margin,
511                                            bg_radius);
512                                 /* draw text */
513                                 glColor3ubv(color_text);
514                                 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
515                                 BLF_rotation(blf_mono_font, 0.0f);
516                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
517                         }
518
519                         /* capping */
520                         {
521                                 float rot_90_vec_a[2];
522                                 float rot_90_vec_b[2];
523                                 float cap[2];
524
525                                 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
526                                 rot_90_vec_a[0] = -dir_ruler[1];
527                                 rot_90_vec_a[1] =  dir_ruler[0];
528                                 normalize_v2(rot_90_vec_a);
529
530                                 sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
531                                 rot_90_vec_b[0] = -dir_ruler[1];
532                                 rot_90_vec_b[1] =  dir_ruler[0];
533                                 normalize_v2(rot_90_vec_b);
534
535                                 glEnable(GL_BLEND);
536
537                                 glColor3ubv(color_wire);
538
539                                 glBegin(GL_LINES);
540
541                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
542                                 glVertex2fv(cap);
543                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
544                                 glVertex2fv(cap);
545
546                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
547                                 glVertex2fv(cap);
548                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
549                                 glVertex2fv(cap);
550
551                                 /* angle vertex */
552                                 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
553                                 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
554                                 glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
555                                 glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
556                                 glEnd();
557
558                                 glDisable(GL_BLEND);
559                         }
560                 }
561                 else {
562                         glBegin(GL_LINE_STRIP);
563                         for (j = 0; j < 3; j += 2) {
564                                 glVertex2fv(co_ss[j]);
565                         }
566                         glEnd();
567                         cpack(0xaaaaaa);
568                         setlinestyle(3);
569                         glBegin(GL_LINE_STRIP);
570                         for (j = 0; j < 3; j += 2) {
571                                 glVertex2fv(co_ss[j]);
572                         }
573                         glEnd();
574                         setlinestyle(0);
575
576                         sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
577
578                         /* text */
579                         {
580                                 char numstr[256];
581                                 float numstr_size[2];
582                                 const int prec = 6;  /* XXX, todo, make optional */
583                                 const float dir_default_x[2] = {1, 0};
584                                 float pos[2];
585                                 float numstr_angle;
586                                 bool flip_text;
587
588
589                                 /* angle for text */
590                                 numstr_angle = angle_signed_v2v2(dir_ruler, dir_default_x);
591
592                                 /* keep text upright */
593                                 if (numstr_angle >= (float)(M_PI / 2.0)) {
594                                         numstr_angle -= (float)M_PI;
595                                         flip_text = true;
596                                 }
597                                 else if (numstr_angle <= -(float)(M_PI / 2.0)) {
598                                         numstr_angle += (float)M_PI;
599                                         flip_text = true;
600                                 }
601                                 else {
602                                         flip_text = false;
603                                 }
604
605                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
606
607                                 BLF_width_and_height(blf_mono_font, numstr, &numstr_size[0], &numstr_size[1]);
608
609                                 mid_v2_v2v2(pos, co_ss[0], co_ss[2]);
610
611                                 /* center text */
612                                 normalize_v2(dir_ruler);
613                                 madd_v2_v2fl(pos, dir_ruler, numstr_size[0] / (flip_text ? 2.0f : -2.0f));
614
615                                 /* draw text (bg) */
616                                 glTranslatef(pos[0], pos[1], 0.0f);
617                                 glRotatef(RAD2DEGF(numstr_angle), 0.0f, 0.0f, 1.0f);
618                                 glColor4ubv(color_back);
619                                 uiSetRoundBox(UI_CNR_ALL);
620                                 uiRoundBox(-bg_margin, -bg_margin,
621                                            numstr_size[0] + bg_margin, numstr_size[1] + bg_margin,
622                                            bg_radius);
623                                 glRotatef(-RAD2DEGF(numstr_angle), 0.0f, 0.0f, 1.0f);
624                                 glTranslatef(-pos[0], -pos[1], 0.0f);
625                                 /* draw text */
626                                 glColor3ubv(color_text);
627                                 BLF_position(blf_mono_font, pos[0], pos[1], 0.0f);
628                                 BLF_rotation(blf_mono_font, numstr_angle);
629                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
630                         }
631
632                         /* capping */
633                         {
634                                 float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
635                                 float cap[2];
636
637                                 normalize_v2(rot_90_vec);
638
639                                 glEnable(GL_BLEND);
640                                 glColor3ubv(color_wire);
641
642                                 glBegin(GL_LINES);
643                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
644                                 glVertex2fv(cap);
645                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
646                                 glVertex2fv(cap);
647
648                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
649                                 glVertex2fv(cap);
650                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
651                                 glVertex2fv(cap);
652                                 glEnd();
653
654                                 glDisable(GL_BLEND);
655                         }
656                 }
657         }
658
659         glDisable(GL_LINE_SMOOTH);
660
661         BLF_disable(blf_mono_font, BLF_ROTATION);
662
663 #undef ARC_STEPS
664 }
665
666 /* free, use for both cancel and finish */
667 static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_info)
668 {
669         ED_region_draw_cb_exit(ruler_info->ar->type, ruler_info->draw_handle_pixel);
670 }
671
672 static void view3d_ruler_free(RulerInfo *ruler_info)
673 {
674         BLI_freelistN(&ruler_info->items);
675         MEM_freeN(ruler_info);
676 }
677
678 static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3],
679                                       const int xy[2])
680 {
681         view3d_get_view_aligned_coordinate(ruler_info->ar, r_co, xy, true);
682 }
683
684 /* use for mousemove events */
685 static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const int mval[2], const bool do_snap)
686 {
687         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
688
689         if (ruler_item) {
690                 float *co = ruler_item->co[ruler_item->co_index];
691                 view3d_ruler_item_project(ruler_info, co, mval);
692                 if (do_snap) {
693                         const float mval_fl[2] = {UNPACK2(mval)};
694                         ED_view3d_snap_co(C, co, mval_fl, true, true, true);
695                 }
696                 return true;
697         }
698         else {
699                 return false;
700         }
701 }
702
703 static void view3d_ruler_header_update(ScrArea *sa)
704 {
705         const char *text = "Ctrl+LMB: Add, "
706                            "Del: Remove, "
707                            "Ctrl+Drag: Snap, "
708                            "Ctrl+C: Copy Value, "
709                            "Enter: Store,  "
710                            "Esc: Cancel";
711
712         ED_area_headerprint(sa, text);
713 }
714
715 /* -------------------------------------------------------------------- */
716 /* Operator callbacks */
717
718 static int view3d_ruler_invoke(bContext *C, wmOperator *op, wmEvent *event)
719 {
720         ScrArea *sa = CTX_wm_area(C);
721         ARegion *ar = CTX_wm_region(C);
722 //      RegionView3D *rv3d = CTX_wm_region_view3d(C);
723         RulerInfo *ruler_info;
724         (void)event;
725
726         ruler_info = MEM_callocN(sizeof(RulerInfo), "RulerInfo");
727
728         if (view3d_ruler_from_gpencil(C, ruler_info)) {
729                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
730         }
731
732         op->customdata = ruler_info;
733
734         ruler_info->ar = ar;
735         ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel, ruler_info, REGION_DRAW_POST_PIXEL);
736
737         view3d_ruler_header_update(sa);
738
739         WM_event_add_modal_handler(C, op);
740
741         return OPERATOR_RUNNING_MODAL;
742 }
743
744 static int view3d_ruler_cancel(bContext *C, wmOperator *op)
745 {
746         RulerInfo *ruler_info = op->customdata;
747
748         view3d_ruler_end(C, ruler_info);
749         view3d_ruler_free(ruler_info);
750         op->customdata = NULL;
751
752         return OPERATOR_CANCELLED;
753 }
754
755 static int view3d_ruler_modal(bContext *C, wmOperator *op, wmEvent *event)
756 {
757         bool do_draw = false;
758         int exit_code = OPERATOR_RUNNING_MODAL;
759         RulerInfo *ruler_info = op->customdata;
760         ARegion *ar = ruler_info->ar;
761         RegionView3D *rv3d = ar->regiondata;
762
763         (void)C;
764
765         switch (event->type) {
766                 case LEFTMOUSE:
767                         if (event->val == KM_RELEASE) {
768                                 if (ruler_info->state == RULER_STATE_DRAG) {
769                                         /* rubber-band angle removal */
770                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
771                                         if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
772                                                 if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
773                                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
774                                                         do_draw = true;
775                                                 }
776                                         }
777                                         ruler_info->state = RULER_STATE_NORMAL;
778                                 }
779                         }
780                         else {
781                                 if (ruler_info->state == RULER_STATE_NORMAL) {
782
783                                         if (event->ctrl ||
784                                             /* weak - but user friendly */
785                                             (ruler_info->items.first == NULL))
786                                         {
787                                                 /* Create new line */
788                                                 RulerItem *ruler_item;
789                                                 /* check if we want to drag an existing point or add a new one */
790                                                 ruler_info->state = RULER_STATE_DRAG;
791
792                                                 ruler_item = ruler_item_add(ruler_info);
793                                                 ruler_item_active_set(ruler_info, ruler_item);
794
795                                                 negate_v3_v3(ruler_item->co[0], rv3d->ofs);
796                                                 view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
797                                                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
798
799                                                 /* snap the first point added, not essential but handy */
800                                                 {
801                                                         ruler_item->co_index = 0;
802                                                         view3d_ruler_item_mousemove(C, ruler_info, event->mval, true);
803                                                 }
804
805                                                 ruler_item->co_index = 2;
806
807                                                 do_draw = true;
808                                         }
809                                         else {
810                                                 float mval_fl[2] = {UNPACK2(event->mval)};
811                                                 RulerItem *ruler_item_pick;
812                                                 int co_index;
813
814                                                 /* select and drag */
815                                                 if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
816                                                         if (co_index == -1) {
817                                                                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
818                                                                         /* Add Center Point */
819                                                                         ruler_item_active_set(ruler_info, ruler_item_pick);
820                                                                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
821                                                                         ruler_item_pick->co_index = 1;
822                                                                         ruler_info->state = RULER_STATE_DRAG;
823
824                                                                         /* find the factor */
825                                                                         {
826                                                                                 float co_ss[2][2];
827                                                                                 float fac;
828
829                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
830                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
831
832                                                                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
833                                                                                 CLAMP(fac, 0.0f, 1.0f);
834
835                                                                                 interp_v3_v3v3(ruler_item_pick->co[1],
836                                                                                                ruler_item_pick->co[0],
837                                                                                                ruler_item_pick->co[2], fac);
838                                                                         }
839
840                                                                         /* update the new location */
841                                                                         view3d_ruler_item_mousemove(C, ruler_info, event->mval, event->ctrl != 0);
842                                                                         do_draw = true;
843                                                                 }
844                                                         }
845                                                         else {
846                                                                 ruler_item_active_set(ruler_info, ruler_item_pick);
847                                                                 ruler_item_pick->co_index = co_index;
848                                                                 ruler_info->state = RULER_STATE_DRAG;
849                                                                 do_draw = true;
850                                                         }
851                                                 }
852                                                 else {
853                                                         exit_code = OPERATOR_PASS_THROUGH;
854                                                 }
855
856                                         }
857                                 }
858                         }
859                         break;
860                 case CKEY:
861                 {
862                         if (event->ctrl) {
863                                 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
864                                 if (ruler_item) {
865                                         const int prec = 8;
866                                         char numstr[256];
867                                         Scene *scene = CTX_data_scene(C);
868                                         UnitSettings *unit = &scene->unit;
869
870                                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
871                                         WM_clipboard_text_set((void *) numstr, false);
872                                 }
873                         }
874                 }
875                 case RIGHTCTRLKEY:
876                 case LEFTCTRLKEY:
877                 {
878                         WM_event_add_mousemove(C);
879                         break;
880                 }
881                 case MOUSEMOVE:
882                 {
883                         if (ruler_info->state == RULER_STATE_DRAG) {
884                                 if (view3d_ruler_item_mousemove(C, ruler_info, event->mval, event->ctrl != 0)) {
885                                         do_draw = true;
886                                 }
887                         }
888                         break;
889                 }
890                 case ESCKEY:
891                 {
892                         do_draw = true;
893                         exit_code = OPERATOR_CANCELLED;
894                         break;
895                 }
896                 case RETKEY:
897                 {
898                         view3d_ruler_to_gpencil(C, ruler_info);
899                         do_draw = true;
900                         exit_code = OPERATOR_FINISHED;
901                         break;
902                 }
903                 case DELKEY:
904                 {
905                         if (event->val == KM_PRESS) {
906                                 if (ruler_info->state == RULER_STATE_NORMAL) {
907                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
908                                         if (ruler_item) {
909                                                 ruler_item_remove(ruler_info, ruler_item);
910                                                 ruler_info->item_active = -1;
911                                                 do_draw = true;
912                                         }
913                                 }
914                         }
915                         break;
916                 }
917                 default:
918                         exit_code = OPERATOR_PASS_THROUGH;
919                         break;
920
921         }
922
923         if (do_draw) {
924                 ScrArea *sa = CTX_wm_area(C);
925
926                 view3d_ruler_header_update(sa);
927
928                 /* all 3d views draw rulers */
929                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
930         }
931
932         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
933                 ScrArea *sa = CTX_wm_area(C);
934
935                 view3d_ruler_end(C, ruler_info);
936                 view3d_ruler_free(ruler_info);
937                 op->customdata = NULL;
938
939                 ED_area_headerprint(sa, NULL);
940         }
941
942         return exit_code;
943 }
944
945 void VIEW3D_OT_ruler(wmOperatorType *ot)
946 {
947         /* identifiers */
948         ot->name = "3D Ruler & Protractor";
949         ot->description = "Interactive ruler";
950         ot->idname = "VIEW3D_OT_ruler";
951
952         /* api callbacks */
953         ot->invoke = view3d_ruler_invoke;
954         ot->cancel = view3d_ruler_cancel;
955         ot->modal = view3d_ruler_modal;
956         ot->poll = ED_operator_view3d_active;
957
958         /* flags */
959         ot->flag = 0;
960 }