Merge branch 'master' into blender2.8
[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_meshdata_types.h"
30 #include "DNA_scene_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_gpencil_types.h"
33 #include "DNA_brush_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_math.h"
38 #include "BLI_blenlib.h"
39
40 #include "BLT_translation.h"
41
42 #include "BKE_context.h"
43 #include "BKE_gpencil.h"
44 #include "BKE_main.h"
45 #include "BKE_material.h"
46 #include "BKE_unit.h"
47
48 #include "BIF_gl.h"
49
50 #include "GPU_immediate.h"
51 #include "GPU_immediate_util.h"
52 #include "GPU_state.h"
53
54 #include "WM_api.h"
55 #include "WM_types.h"
56
57 #include "ED_gpencil.h"
58 #include "ED_screen.h"
59 #include "ED_view3d.h"
60 #include "ED_transform_snap_object_context.h"
61 #include "ED_space_api.h"
62
63 #include "BLF_api.h"
64 #include "BIF_glutil.h"
65
66 #include "UI_resources.h"
67 #include "UI_interface.h"
68
69 #include "view3d_intern.h"  /* own include */
70
71 #define MVAL_MAX_PX_DIST 12.0f
72
73
74 /* -------------------------------------------------------------------- */
75 /* Ruler Item (we can have many) */
76 enum {
77         RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
78         RULERITEM_USE_RAYCAST = (1 << 1)
79 };
80
81 enum {
82         RULERITEM_DIRECTION_IN = 0,
83         RULERITEM_DIRECTION_OUT
84 };
85
86 /* keep smaller then selection, since we may want click elsewhere without selecting a ruler */
87 #define RULER_PICK_DIST 12.0f
88 #define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
89
90 typedef struct RulerItem {
91         struct RulerItem *next, *prev;
92
93         /* worldspace coords, middle being optional */
94         float co[3][3];
95
96         /* selected coord */
97         char  co_index; /* 0 -> 2*/
98
99         int   flag;
100         int   raycast_dir;  /* RULER_DIRECTION_* */
101 } RulerItem;
102
103
104 /* -------------------------------------------------------------------- */
105 /* Ruler Info (one per session) */
106
107 enum {
108         RULER_STATE_NORMAL = 0,
109         RULER_STATE_DRAG
110 };
111
112 enum {
113         RULER_SNAP_OK = (1 << 0),
114 };
115
116 typedef struct RulerInfo {
117         ListBase items;
118         int      item_active;
119         int flag;
120         int snap_flag;
121         int state;
122         float drag_start_co[3];
123
124         struct SnapObjectContext *snap_context;
125
126         /* wm state */
127         wmWindow *win;
128         ScrArea *sa;
129         void *draw_handle_pixel;
130         ARegion *ar;  /* re-assigned every modal update */
131 } RulerInfo;
132
133 /* -------------------------------------------------------------------- */
134 /* local functions */
135 static RulerItem *ruler_item_add(RulerInfo *ruler_info)
136 {
137         RulerItem *ruler_item = MEM_callocN(sizeof(RulerItem), "RulerItem");
138         BLI_addtail(&ruler_info->items, ruler_item);
139         return ruler_item;
140 }
141
142 static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item)
143 {
144         BLI_remlink(&ruler_info->items, ruler_item);
145
146         MEM_freeN(ruler_item);
147 }
148
149 static RulerItem *ruler_item_active_get(RulerInfo *ruler_info)
150 {
151         return BLI_findlink(&ruler_info->items, ruler_info->item_active);
152 }
153
154 static void ruler_item_active_set(RulerInfo *ruler_info, RulerItem *ruler_item)
155 {
156         ruler_info->item_active = BLI_findindex(&ruler_info->items, ruler_item);
157 }
158
159 static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
160                                  char *numstr, size_t numstr_size, int prec)
161 {
162         const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
163
164         if (ruler_item->flag & RULERITEM_USE_ANGLE) {
165                 const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
166                                                        ruler_item->co[1],
167                                                        ruler_item->co[2]);
168
169                 if (unit->system == USER_UNIT_NONE) {
170                         BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
171                 }
172                 else {
173                         bUnit_AsString(numstr, numstr_size,
174                                        (double)ruler_angle,
175                                        prec, unit->system, B_UNIT_ROTATION, do_split, false);
176                 }
177         }
178         else {
179                 const float ruler_len = len_v3v3(ruler_item->co[0],
180                                                  ruler_item->co[2]);
181
182                 if (unit->system == USER_UNIT_NONE) {
183                         BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
184                 }
185                 else {
186                         bUnit_AsString(numstr, numstr_size,
187                                        (double)(ruler_len * unit->scale_length),
188                                        prec, unit->system, B_UNIT_LENGTH, do_split, false);
189                 }
190         }
191
192 }
193
194 static bool view3d_ruler_pick(RulerInfo *ruler_info, const float mval[2],
195                               RulerItem **r_ruler_item, int *r_co_index)
196 {
197         ARegion *ar = ruler_info->ar;
198         RulerItem *ruler_item;
199
200         float dist_best = RULER_PICK_DIST_SQ;
201         RulerItem *ruler_item_best = NULL;
202         int co_index_best = -1;
203
204         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
205                 float co_ss[3][2];
206                 float dist;
207                 int j;
208
209                 /* should these be checked? - ok for now not to */
210                 for (j = 0; j < 3; j++) {
211                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
212                 }
213
214                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
215                         dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
216                                       dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
217                         if (dist < dist_best) {
218                                 dist_best = dist;
219                                 ruler_item_best = ruler_item;
220
221                                 {
222                                         const float dist_points[3] = {
223                                             len_squared_v2v2(co_ss[0], mval),
224                                             len_squared_v2v2(co_ss[1], mval),
225                                             len_squared_v2v2(co_ss[2], mval),
226                                         };
227                                         if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
228                                                 co_index_best = min_axis_v3(dist_points);
229                                         }
230                                         else {
231                                                 co_index_best = -1;
232                                         }
233                                 }
234                         }
235                 }
236                 else {
237                         dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
238                         if (dist < dist_best) {
239                                 dist_best = dist;
240                                 ruler_item_best = ruler_item;
241
242                                 {
243                                         const float dist_points[2] = {
244                                             len_squared_v2v2(co_ss[0], mval),
245                                             len_squared_v2v2(co_ss[2], mval),
246                                         };
247                                         if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
248                                                 co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
249                                         }
250                                         else {
251                                                 co_index_best = -1;
252                                         }
253                                 }
254                         }
255                 }
256         }
257
258         if (ruler_item_best) {
259                 *r_ruler_item = ruler_item_best;
260                 *r_co_index = co_index_best;
261                 return true;
262         }
263         else {
264                 *r_ruler_item = NULL;
265                 *r_co_index = -1;
266                 return false;
267         }
268 }
269
270 /**
271  * Ensure the 'snap_context' is only cached while dragging,
272  * needed since the user may toggle modes between tool use.
273  */
274 static void ruler_state_set(bContext *C, RulerInfo *ruler_info, int state)
275 {
276         Main *bmain = CTX_data_main(C);
277         if (state == ruler_info->state) {
278                 return;
279         }
280
281         /* always remove */
282         if (ruler_info->snap_context) {
283                 ED_transform_snap_object_context_destroy(ruler_info->snap_context);
284                 ruler_info->snap_context = NULL;
285         }
286
287         if (state == RULER_STATE_NORMAL) {
288                 /* pass */
289         }
290         else if (state == RULER_STATE_DRAG) {
291                 ruler_info->snap_context = ED_transform_snap_object_context_create_view3d(
292                         bmain, CTX_data_scene(C), CTX_data_depsgraph(C), 0,
293                         ruler_info->ar, CTX_wm_view3d(C));
294         }
295         else {
296                 BLI_assert(0);
297         }
298
299         ruler_info->state = state;
300 }
301
302 #define RULER_ID "RulerData3D"
303 static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
304 {
305         Main *bmain = CTX_data_main(C);
306         Scene *scene = CTX_data_scene(C);
307
308         bGPDlayer *gpl;
309         bGPDframe *gpf;
310         bGPDstroke *gps;
311         RulerItem *ruler_item;
312         const char *ruler_name = RULER_ID;
313         bool changed = false;
314
315         /* FIXME: This needs to be reviewed. Should it keep being done like this? */
316         if (scene->gpd == NULL) {
317                 scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations");
318         }
319         bGPdata *gpd = scene->gpd;
320
321         gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info));
322         if (gpl == NULL) {
323                 gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false);
324                 copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
325                 gpl->thickness = 1;
326                 gpl->flag |= GP_LAYER_HIDE;
327         }
328
329         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW);
330         BKE_gpencil_free_strokes(gpf);
331
332         for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
333                 bGPDspoint *pt;
334                 int j;
335
336                 /* allocate memory for a new stroke */
337                 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
338                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
339                         gps->totpoints = 3;
340                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
341                         for (j = 0; j < 3; j++) {
342                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
343                                 pt->pressure = 1.0f;
344                                 pt->strength = 1.0f;
345                                 pt++;
346                         }
347                 }
348                 else {
349                         gps->totpoints = 2;
350                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
351                         for (j = 0; j < 3; j += 2) {
352                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
353                                 pt->pressure = 1.0f;
354                                 pt->strength = 1.0f;
355                                 pt++;
356                         }
357                 }
358                 gps->flag = GP_STROKE_3DSPACE;
359                 gps->thickness = 3;
360
361                 BLI_addtail(&gpf->strokes, gps);
362                 changed = true;
363         }
364
365         return changed;
366 }
367
368 static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info)
369 {
370         Scene *scene = CTX_data_scene(C);
371         bool changed = false;
372
373         if (scene->gpd) {
374                 bGPDlayer *gpl;
375                 const char *ruler_name = RULER_ID;
376                 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
377                 if (gpl) {
378                         bGPDframe *gpf;
379                         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
380                         if (gpf) {
381                                 bGPDstroke *gps;
382                                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
383                                         bGPDspoint *pt = gps->points;
384                                         int j;
385                                         if (gps->totpoints == 3) {
386                                                 RulerItem *ruler_item = ruler_item_add(ruler_info);
387                                                 for (j = 0; j < 3; j++) {
388                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
389                                                         pt++;
390                                                 }
391                                                 ruler_item->flag |= RULERITEM_USE_ANGLE;
392                                                 changed = true;
393                                         }
394                                         else if (gps->totpoints == 2) {
395                                                 RulerItem *ruler_item = ruler_item_add(ruler_info);
396                                                 for (j = 0; j < 3; j += 2) {
397                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
398                                                         pt++;
399                                                 }
400                                                 changed = true;
401                                         }
402                                 }
403                         }
404                 }
405         }
406
407         return changed;
408 }
409
410 /* -------------------------------------------------------------------- */
411 /* local callbacks */
412
413 static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg)
414 {
415         Scene *scene = CTX_data_scene(C);
416         UnitSettings *unit = &scene->unit;
417         RulerItem *ruler_item;
418         RulerInfo *ruler_info = arg;
419         RegionView3D *rv3d = ar->regiondata;
420 //      ARegion *ar = ruler_info->ar;
421         const float cap_size = 4.0f;
422         const float bg_margin = 4.0f * U.pixelsize;
423         const float bg_radius = 4.0f * U.pixelsize;
424         const float arc_size = 64.0f * U.pixelsize;
425 #define ARC_STEPS 24
426         const int arc_steps = ARC_STEPS;
427         int i;
428         const float color_act[4] = {1.0f, 1.0f, 1.0f, 1.0f};
429         const float color_base[4] = {0.0f, 0.0f, 0.0f, 1.0f};
430         unsigned char color_text[3];
431         unsigned char color_wire[3];
432         float color_back[4] = {1.0f, 1.0f, 1.0f, 0.5f};
433
434         /* anti-aliased lines for more consistent appearance */
435         GPU_line_smooth(true);
436
437         BLF_enable(blf_mono_font, BLF_ROTATION);
438         BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
439         BLF_rotation(blf_mono_font, 0.0f);
440
441         UI_GetThemeColor3ubv(TH_TEXT, color_text);
442         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
443
444         for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) {
445                 const bool is_act = (i == ruler_info->item_active);
446                 float dir_ruler[2];
447                 float co_ss[3][2];
448                 int j;
449
450                 /* should these be checked? - ok for now not to */
451                 for (j = 0; j < 3; j++) {
452                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
453                 }
454
455                 GPU_blend(true);
456
457                 const uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
458
459                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
460                         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
461
462                         float viewport_size[4];
463                         GPU_viewport_size_get_f(viewport_size);
464                         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
465
466                         immUniform1i("colors_len", 2);  /* "advanced" mode */
467                         const float *col = is_act ? color_act : color_base;
468                         immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
469                         immUniform1f("dash_width", 6.0f);
470
471                         immBegin(GPU_PRIM_LINE_STRIP, 3);
472
473                         immVertex2fv(shdr_pos, co_ss[0]);
474                         immVertex2fv(shdr_pos, co_ss[1]);
475                         immVertex2fv(shdr_pos, co_ss[2]);
476
477                         immEnd();
478
479                         immUnbindProgram();
480
481                         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
482
483                         /* arc */
484                         {
485                                 float dir_tmp[3];
486                                 float co_tmp[3];
487                                 float arc_ss_coord[2];
488
489                                 float dir_a[3];
490                                 float dir_b[3];
491                                 float quat[4];
492                                 float axis[3];
493                                 float angle;
494                                 const float px_scale = (ED_view3d_pixel_size_no_ui_scale(rv3d, ruler_item->co[1]) *
495                                                         min_fff(arc_size,
496                                                                 len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
497                                                                 len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
498
499                                 sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
500                                 sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
501                                 normalize_v3(dir_a);
502                                 normalize_v3(dir_b);
503
504                                 cross_v3_v3v3(axis, dir_a, dir_b);
505                                 angle = angle_normalized_v3v3(dir_a, dir_b);
506
507                                 axis_angle_to_quat(quat, axis, angle / arc_steps);
508
509                                 copy_v3_v3(dir_tmp, dir_a);
510
511                                 immUniformColor3ubv(color_wire);
512
513                                 immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1);
514
515                                 for (j = 0; j <= arc_steps; j++) {
516                                         madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
517                                         ED_view3d_project_float_global(ar, co_tmp, arc_ss_coord, V3D_PROJ_TEST_NOP);
518                                         mul_qt_v3(quat, dir_tmp);
519
520                                         immVertex2fv(shdr_pos, arc_ss_coord);
521                                 }
522
523                                 immEnd();
524                         }
525
526                         /* capping */
527                         {
528                                 float rot_90_vec_a[2];
529                                 float rot_90_vec_b[2];
530                                 float cap[2];
531
532                                 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
533                                 rot_90_vec_a[0] = -dir_ruler[1];
534                                 rot_90_vec_a[1] =  dir_ruler[0];
535                                 normalize_v2(rot_90_vec_a);
536
537                                 sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
538                                 rot_90_vec_b[0] = -dir_ruler[1];
539                                 rot_90_vec_b[1] =  dir_ruler[0];
540                                 normalize_v2(rot_90_vec_b);
541
542                                 GPU_blend(true);
543
544                                 immUniformColor3ubv(color_wire);
545
546                                 immBegin(GPU_PRIM_LINES, 8);
547
548                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
549                                 immVertex2fv(shdr_pos, cap);
550                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
551                                 immVertex2fv(shdr_pos, cap);
552
553                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
554                                 immVertex2fv(shdr_pos, cap);
555                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
556                                 immVertex2fv(shdr_pos, cap);
557
558                                 /* angle vertex */
559                                 immVertex2f(shdr_pos, co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
560                                 immVertex2f(shdr_pos, co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
561                                 immVertex2f(shdr_pos, co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
562                                 immVertex2f(shdr_pos, co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
563
564                                 immEnd();
565
566                                 GPU_blend(false);
567                         }
568
569                         immUnbindProgram();
570
571                         /* text */
572                         {
573                                 char numstr[256];
574                                 float numstr_size[2];
575                                 float posit[2];
576                                 const int prec = 2;  /* XXX, todo, make optional */
577
578                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
579
580                                 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
581
582                                 posit[0] = co_ss[1][0] + (cap_size * 2.0f);
583                                 posit[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
584
585                                 /* draw text (bg) */
586                                 UI_draw_roundbox_corner_set(UI_CNR_ALL);
587                                 UI_draw_roundbox_aa(true,
588                                         posit[0] - bg_margin,                  posit[1] - bg_margin,
589                                         posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
590                                         bg_radius, color_back);
591                                 /* draw text */
592                                 BLF_color3ubv(blf_mono_font, color_text);
593                                 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
594                                 BLF_rotation(blf_mono_font, 0.0f);
595                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
596                         }
597                 }
598                 else {
599                         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
600
601                         float viewport_size[4];
602                         GPU_viewport_size_get_f(viewport_size);
603                         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
604
605                         immUniform1i("colors_len", 2);  /* "advanced" mode */
606                         const float *col = is_act ? color_act : color_base;
607                         immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
608                         immUniform1f("dash_width", 6.0f);
609
610                         immBegin(GPU_PRIM_LINES, 2);
611
612                         immVertex2fv(shdr_pos, co_ss[0]);
613                         immVertex2fv(shdr_pos, co_ss[2]);
614
615                         immEnd();
616
617                         immUnbindProgram();
618
619                         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
620
621                         sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
622
623                         /* capping */
624                         {
625                                 float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
626                                 float cap[2];
627
628                                 normalize_v2(rot_90_vec);
629
630                                 GPU_blend(true);
631
632                                 immUniformColor3ubv(color_wire);
633
634                                 immBegin(GPU_PRIM_LINES, 4);
635
636                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
637                                 immVertex2fv(shdr_pos, cap);
638                                 madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
639                                 immVertex2fv(shdr_pos, cap);
640
641                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
642                                 immVertex2fv(shdr_pos, cap);
643                                 madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
644                                 immVertex2fv(shdr_pos, cap);
645
646                                 immEnd();
647
648                                 GPU_blend(false);
649                         }
650
651                         immUnbindProgram();
652
653                         /* text */
654                         {
655                                 char numstr[256];
656                                 float numstr_size[2];
657                                 const int prec = 6;  /* XXX, todo, make optional */
658                                 float posit[2];
659
660                                 ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
661
662                                 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
663
664                                 mid_v2_v2v2(posit, co_ss[0], co_ss[2]);
665
666                                 /* center text */
667                                 posit[0] -= numstr_size[0] / 2.0f;
668                                 posit[1] -= numstr_size[1] / 2.0f;
669
670                                 /* draw text (bg) */
671                                 UI_draw_roundbox_corner_set(UI_CNR_ALL);
672                                 UI_draw_roundbox_aa(true,
673                                            posit[0] - bg_margin,                  posit[1] - bg_margin,
674                                            posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
675                                            bg_radius, color_back);
676                                 /* draw text */
677                                 BLF_color3ubv(blf_mono_font, color_text);
678                                 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
679                                 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
680                         }
681                 }
682         }
683
684         GPU_line_smooth(false);
685
686         BLF_disable(blf_mono_font, BLF_ROTATION);
687
688 #undef ARC_STEPS
689
690         /* draw snap */
691         if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) {
692                 ruler_item = ruler_item_active_get(ruler_info);
693                 if (ruler_item) {
694                         /* size from drawSnapping */
695                         const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
696                         float co_ss[3];
697                         ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP);
698
699                         uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
700
701                         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
702                         immUniformColor4fv(color_act);
703
704                         imm_draw_circle_wire_2d(pos, co_ss[0], co_ss[1], size * U.pixelsize, 32);
705
706                         immUnbindProgram();
707                 }
708         }
709
710 }
711
712 /* free, use for both cancel and finish */
713 static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_info)
714 {
715         ED_region_draw_cb_exit(ruler_info->ar->type, ruler_info->draw_handle_pixel);
716 }
717
718 static void view3d_ruler_free(RulerInfo *ruler_info)
719 {
720         BLI_freelistN(&ruler_info->items);
721
722         if (ruler_info->snap_context) {
723                 ED_transform_snap_object_context_destroy(ruler_info->snap_context);
724         }
725
726         MEM_freeN(ruler_info);
727 }
728
729 static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3],
730                                       const int xy[2])
731 {
732         ED_view3d_win_to_3d_int(ruler_info->sa->spacedata.first, ruler_info->ar, r_co, xy, r_co);
733 }
734
735 /* use for mousemove events */
736 static bool view3d_ruler_item_mousemove(
737         RulerInfo *ruler_info, const int mval[2],
738         const bool do_thickness, const bool do_snap)
739 {
740         const float eps_bias = 0.0002f;
741         float dist_px = MVAL_MAX_PX_DIST * U.pixelsize;  /* snap dist */
742         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
743
744         ruler_info->snap_flag &= ~RULER_SNAP_OK;
745
746         if (ruler_item) {
747                 float *co = ruler_item->co[ruler_item->co_index];
748                 /* restore the initial depth */
749                 copy_v3_v3(co, ruler_info->drag_start_co);
750                 view3d_ruler_item_project(ruler_info, co, mval);
751                 if (do_thickness && ruler_item->co_index != 1) {
752                         // Scene *scene = CTX_data_scene(C);
753                         // View3D *v3d = ruler_info->sa->spacedata.first;
754                         const float mval_fl[2] = {UNPACK2(mval)};
755                         float ray_normal[3];
756                         float ray_start[3];
757                         float *co_other;
758
759                         co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0];
760
761                         if (ED_transform_snap_object_project_view3d(
762                                 ruler_info->snap_context,
763                                 SCE_SNAP_MODE_FACE,
764                                 &(const struct SnapObjectParams){
765                                     .snap_select = SNAP_ALL,
766                                     .use_object_edit_cage = true,
767                                 },
768                                 mval_fl, &dist_px,
769                                 co, ray_normal))
770                         {
771                                 negate_v3(ray_normal);
772                                 /* add some bias */
773                                 madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias);
774                                 ED_transform_snap_object_project_ray(
775                                         ruler_info->snap_context,
776                                         &(const struct SnapObjectParams){
777                                             .snap_select = SNAP_ALL,
778                                             .use_object_edit_cage = true,
779                                         },
780                                         ray_start, ray_normal, NULL,
781                                         co_other, NULL);
782                         }
783                 }
784                 else if (do_snap) {
785                         const float mval_fl[2] = {UNPACK2(mval)};
786
787                         if (ED_transform_snap_object_project_view3d(
788                                 ruler_info->snap_context,
789                                 (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
790                                 &(const struct SnapObjectParams){
791                                     .snap_select = SNAP_ALL,
792                                     .use_object_edit_cage = true,
793                                     .use_occlusion_test = true,
794                                 },
795                                 mval_fl, &dist_px,
796                                 co, NULL))
797                         {
798                                 ruler_info->snap_flag |= RULER_SNAP_OK;
799                         }
800                 }
801                 return true;
802         }
803         else {
804                 return false;
805         }
806 }
807
808 static void view3d_ruler_header_update(bContext *C)
809 {
810         const char *text = IFACE_("Ctrl+LMB: Add, "
811                                   "Del: Remove, "
812                                   "Ctrl+Drag: Snap, "
813                                   "Shift+Drag: Thickness, "
814                                   "Ctrl+C: Copy Value, "
815                                   "Enter: Store,  "
816                                   "Esc: Cancel");
817
818         ED_workspace_status_text(C, text);
819 }
820
821 /* -------------------------------------------------------------------- */
822 /* Operator callbacks */
823
824 static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
825 {
826         wmWindow *win = CTX_wm_window(C);
827         ScrArea *sa = CTX_wm_area(C);
828         ARegion *ar = CTX_wm_region(C);
829         RulerInfo *ruler_info;
830
831         ruler_info = MEM_callocN(sizeof(RulerInfo), "RulerInfo");
832
833         if (view3d_ruler_from_gpencil(C, ruler_info)) {
834                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
835         }
836
837         op->customdata = ruler_info;
838
839         ruler_info->win = win;
840         ruler_info->sa = sa;
841         ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel,
842                                                                    ruler_info, REGION_DRAW_POST_PIXEL);
843
844         view3d_ruler_header_update(C);
845
846         op->flag |= OP_IS_MODAL_CURSOR_REGION;
847
848         WM_cursor_modal_set(win, BC_CROSSCURSOR);
849         WM_event_add_modal_handler(C, op);
850
851         return OPERATOR_RUNNING_MODAL;
852 }
853
854 static void view3d_ruler_cancel(bContext *C, wmOperator *op)
855 {
856         RulerInfo *ruler_info = op->customdata;
857
858         view3d_ruler_end(C, ruler_info);
859         view3d_ruler_free(ruler_info);
860         op->customdata = NULL;
861 }
862
863 static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event)
864 {
865         bool do_draw = false;
866         int exit_code = OPERATOR_RUNNING_MODAL;
867         RulerInfo *ruler_info = op->customdata;
868         ScrArea *sa = CTX_wm_area(C);
869         ARegion *ar = CTX_wm_region(C);
870         RegionView3D *rv3d = ar->regiondata;
871
872         /* its possible to change spaces while running the operator [#34894] */
873         if (UNLIKELY(sa != ruler_info->sa)) {
874                 exit_code = OPERATOR_FINISHED;
875                 goto exit;
876         }
877
878         ruler_info->ar = ar;
879
880         switch (event->type) {
881                 case LEFTMOUSE:
882                         if (event->val == KM_RELEASE) {
883                                 if (ruler_info->state == RULER_STATE_DRAG) {
884                                         /* rubber-band angle removal */
885                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
886                                         if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
887                                                 if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
888                                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
889                                                         do_draw = true;
890                                                 }
891                                         }
892                                         if (ruler_info->snap_flag & RULER_SNAP_OK) {
893                                                 ruler_info->snap_flag &= ~RULER_SNAP_OK;
894                                                 do_draw = true;
895                                         }
896                                         ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
897                                 }
898                         }
899                         else {
900                                 if (ruler_info->state == RULER_STATE_NORMAL) {
901
902                                         if (event->ctrl ||
903                                             /* weak - but user friendly */
904                                             BLI_listbase_is_empty(&ruler_info->items))
905                                         {
906                                                 View3D *v3d = CTX_wm_view3d(C);
907                                                 const bool use_depth = (v3d->shading.type >= OB_SOLID);
908
909                                                 /* Create new line */
910                                                 RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info);
911                                                 RulerItem *ruler_item;
912                                                 /* check if we want to drag an existing point or add a new one */
913                                                 ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
914
915                                                 ruler_item = ruler_item_add(ruler_info);
916                                                 ruler_item_active_set(ruler_info, ruler_item);
917
918                                                 if (use_depth) {
919                                                         /* snap the first point added, not essential but handy */
920                                                         ruler_item->co_index = 0;
921                                                         view3d_ruler_item_mousemove(ruler_info, event->mval, false, true);
922                                                         copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]);
923                                                 }
924                                                 else {
925                                                         /* initial depth either previous ruler, view offset */
926                                                         if (ruler_item_prev) {
927                                                                 copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]);
928                                                         }
929                                                         else {
930                                                                 negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs);
931                                                         }
932
933                                                         copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co);
934                                                         view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
935                                                 }
936
937                                                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
938                                                 ruler_item->co_index = 2;
939
940                                                 do_draw = true;
941                                         }
942                                         else {
943                                                 float mval_fl[2] = {UNPACK2(event->mval)};
944                                                 RulerItem *ruler_item_pick;
945                                                 int co_index;
946
947                                                 /* select and drag */
948                                                 if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
949                                                         if (co_index == -1) {
950                                                                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
951                                                                         /* Add Center Point */
952                                                                         ruler_item_active_set(ruler_info, ruler_item_pick);
953                                                                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
954                                                                         ruler_item_pick->co_index = 1;
955                                                                         ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
956
957                                                                         /* find the factor */
958                                                                         {
959                                                                                 float co_ss[2][2];
960                                                                                 float fac;
961
962                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
963                                                                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
964
965                                                                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
966                                                                                 CLAMP(fac, 0.0f, 1.0f);
967
968                                                                                 interp_v3_v3v3(ruler_item_pick->co[1],
969                                                                                                ruler_item_pick->co[0],
970                                                                                                ruler_item_pick->co[2], fac);
971                                                                         }
972
973                                                                         /* update the new location */
974                                                                         view3d_ruler_item_mousemove(ruler_info, event->mval,
975                                                                                                     event->shift != 0, event->ctrl != 0);
976                                                                         do_draw = true;
977                                                                 }
978                                                         }
979                                                         else {
980                                                                 ruler_item_active_set(ruler_info, ruler_item_pick);
981                                                                 ruler_item_pick->co_index = co_index;
982                                                                 ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
983
984                                                                 /* store the initial depth */
985                                                                 copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]);
986
987                                                                 do_draw = true;
988                                                         }
989                                                 }
990                                                 else {
991                                                         exit_code = OPERATOR_PASS_THROUGH;
992                                                 }
993
994                                         }
995                                 }
996                         }
997                         break;
998                 case CKEY:
999                 {
1000                         if (event->ctrl) {
1001                                 RulerItem *ruler_item = ruler_item_active_get(ruler_info);
1002                                 if (ruler_item) {
1003                                         const int prec = 8;
1004                                         char numstr[256];
1005                                         Scene *scene = CTX_data_scene(C);
1006                                         UnitSettings *unit = &scene->unit;
1007
1008                                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
1009                                         WM_clipboard_text_set((void *) numstr, false);
1010                                 }
1011                         }
1012                         break;
1013                 }
1014                 case RIGHTCTRLKEY:
1015                 case LEFTCTRLKEY:
1016                 {
1017                         WM_event_add_mousemove(C);
1018                         break;
1019                 }
1020                 case MOUSEMOVE:
1021                 {
1022                         if (ruler_info->state == RULER_STATE_DRAG) {
1023                                 if (view3d_ruler_item_mousemove(ruler_info, event->mval,
1024                                                                 event->shift != 0, event->ctrl != 0))
1025                                 {
1026                                         do_draw = true;
1027                                 }
1028                         }
1029                         break;
1030                 }
1031                 case ESCKEY:
1032                 {
1033                         do_draw = true;
1034                         exit_code = OPERATOR_CANCELLED;
1035                         break;
1036                 }
1037                 case RETKEY:
1038                 {
1039                         /* Enter may be used to invoke from search. */
1040                         if (event->val == KM_PRESS) {
1041                                 view3d_ruler_to_gpencil(C, ruler_info);
1042                                 do_draw = true;
1043                                 exit_code = OPERATOR_FINISHED;
1044                         }
1045                         break;
1046                 }
1047                 case DELKEY:
1048                 {
1049                         if (event->val == KM_PRESS) {
1050                                 if (ruler_info->state == RULER_STATE_NORMAL) {
1051                                         RulerItem *ruler_item = ruler_item_active_get(ruler_info);
1052                                         if (ruler_item) {
1053                                                 RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next;
1054                                                 ruler_item_remove(ruler_info, ruler_item);
1055                                                 ruler_item_active_set(ruler_info, ruler_item_other);
1056                                                 do_draw = true;
1057                                         }
1058                                 }
1059                         }
1060                         break;
1061                 }
1062                 default:
1063                         exit_code = OPERATOR_PASS_THROUGH;
1064                         break;
1065
1066         }
1067
1068         if (ruler_info->state == RULER_STATE_DRAG) {
1069                 op->flag &= ~OP_IS_MODAL_CURSOR_REGION;
1070         }
1071         else {
1072                 op->flag |= OP_IS_MODAL_CURSOR_REGION;
1073         }
1074
1075         if (do_draw) {
1076                 view3d_ruler_header_update(C);
1077
1078                 /* all 3d views draw rulers */
1079                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
1080         }
1081
1082 exit:
1083         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
1084                 WM_cursor_modal_restore(ruler_info->win);
1085
1086                 view3d_ruler_end(C, ruler_info);
1087                 view3d_ruler_free(ruler_info);
1088                 op->customdata = NULL;
1089
1090                 ED_workspace_status_text(C, NULL);
1091         }
1092
1093         return exit_code;
1094 }
1095
1096 void VIEW3D_OT_ruler(wmOperatorType *ot)
1097 {
1098         /* identifiers */
1099         ot->name = "Ruler/Protractor";
1100         ot->description = "Interactive ruler";
1101         ot->idname = "VIEW3D_OT_ruler";
1102
1103         /* api callbacks */
1104         ot->invoke = view3d_ruler_invoke;
1105         ot->cancel = view3d_ruler_cancel;
1106         ot->modal = view3d_ruler_modal;
1107         ot->poll = ED_operator_view3d_active;
1108
1109         /* flags */
1110         ot->flag = 0;
1111 }