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