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