Depsgraph: remove EvaluationContext, pass Depsgraph instead.
[blender.git] / source / blender / editors / space_view3d / view3d_manipulator_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_manipulator_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_object.h"
35 #include "BKE_gpencil.h"
36 #include "BKE_unit.h"
37
38 #include "DNA_object_types.h"
39 #include "DNA_gpencil_types.h"
40 #include "DNA_view3d_types.h"
41
42 #include "BIF_gl.h"
43
44 #include "ED_screen.h"
45 #include "ED_transform_snap_object_context.h"
46 #include "ED_view3d.h"
47
48 #include "UI_resources.h"
49 #include "UI_interface.h"
50
51 #include "MEM_guardedalloc.h"
52
53 #include "RNA_access.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "view3d_intern.h"  /* own include */
59
60 #include "GPU_immediate.h"
61 #include "GPU_immediate_util.h"
62 #include "GPU_select.h"
63
64 #include "BLF_api.h"
65
66
67 static const char *view3d_wgt_ruler_id = "VIEW3D_WGT_ruler";
68
69
70 #define MVAL_MAX_PX_DIST 12.0f
71
72 /* -------------------------------------------------------------------- */
73 /* Ruler Item (we can have many) */
74 enum {
75         RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
76         RULERITEM_USE_RAYCAST = (1 << 1)
77 };
78
79 enum {
80         RULERITEM_DIRECTION_IN = 0,
81         RULERITEM_DIRECTION_OUT
82 };
83
84 /* keep smaller then selection, since we may want click elsewhere without selecting a ruler */
85 #define RULER_PICK_DIST 12.0f
86 #define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
87
88 /* not clicking on a point */
89 #define PART_LINE 0xff
90
91 /* -------------------------------------------------------------------- */
92 /* Ruler Info (wmManipulatorGroup customdata) */
93
94 enum {
95         RULER_STATE_NORMAL = 0,
96         RULER_STATE_DRAG
97 };
98
99 enum {
100         RULER_SNAP_OK = (1 << 0),
101 };
102
103 typedef struct RulerInfo {
104         // ListBase items;
105         int      item_active;
106         int flag;
107         int snap_flag;
108         int state;
109
110         struct SnapObjectContext *snap_context;
111
112         /* wm state */
113         wmWindow *win;
114         ScrArea *sa;
115         ARegion *ar;  /* re-assigned every modal update */
116 } RulerInfo;
117
118 /* -------------------------------------------------------------------- */
119 /* Ruler Item (two or three points) */
120
121 typedef struct RulerItem {
122         wmManipulator mpr;
123
124         /* worldspace coords, middle being optional */
125         float co[3][3];
126
127         int   flag;
128         int   raycast_dir;  /* RULER_DIRECTION_* */
129 } RulerItem;
130
131 typedef struct RulerInteraction {
132         /* selected coord */
133         char  co_index; /* 0 -> 2 */
134         float drag_start_co[3];
135         uint inside_region : 1;
136 } RulerInteraction;
137
138 /* -------------------------------------------------------------------- */
139 /** \name Internal Ruler Utilities
140  * \{ */
141
142 static RulerItem *ruler_item_add(wmManipulatorGroup *mgroup)
143 {
144         /* could pass this as an arg */
145         const wmManipulatorType *wt_ruler = WM_manipulatortype_find("VIEW3D_WT_ruler_item", true);
146         RulerItem *ruler_item = (RulerItem *)WM_manipulator_new_ptr(wt_ruler, mgroup, NULL);
147         WM_manipulator_set_flag(&ruler_item->mpr, WM_MANIPULATOR_DRAW_MODAL, true);
148         return ruler_item;
149 }
150
151 static void ruler_item_remove(bContext *C, wmManipulatorGroup *mgroup, RulerItem *ruler_item)
152 {
153         WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, &ruler_item->mpr, C);
154 }
155
156 static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
157                                  char *numstr, size_t numstr_size, int prec)
158 {
159         const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
160
161         if (ruler_item->flag & RULERITEM_USE_ANGLE) {
162                 const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
163                                                        ruler_item->co[1],
164                                                        ruler_item->co[2]);
165
166                 if (unit->system == USER_UNIT_NONE) {
167                         BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
168                 }
169                 else {
170                         bUnit_AsString(numstr, numstr_size,
171                                        (double)ruler_angle,
172                                        prec, unit->system, B_UNIT_ROTATION, do_split, false);
173                 }
174         }
175         else {
176                 const float ruler_len = len_v3v3(ruler_item->co[0],
177                                                  ruler_item->co[2]);
178
179                 if (unit->system == USER_UNIT_NONE) {
180                         BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
181                 }
182                 else {
183                         bUnit_AsString(numstr, numstr_size,
184                                        (double)(ruler_len * unit->scale_length),
185                                        prec, unit->system, B_UNIT_LENGTH, do_split, false);
186                 }
187         }
188 }
189
190 static bool view3d_ruler_pick(
191         wmManipulatorGroup *mgroup, RulerItem *ruler_item, const float mval[2],
192         int *r_co_index)
193 {
194         RulerInfo *ruler_info = mgroup->customdata;
195         ARegion *ar = ruler_info->ar;
196         bool found = false;
197
198         float dist_best = RULER_PICK_DIST_SQ;
199         int co_index_best = -1;
200
201         {
202                 float co_ss[3][2];
203                 float dist;
204                 int j;
205
206                 /* should these be checked? - ok for now not to */
207                 for (j = 0; j < 3; j++) {
208                         ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
209                 }
210
211                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
212                         dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
213                                       dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
214                         if (dist < dist_best) {
215                                 dist_best = dist;
216                                 found = true;
217
218                                 {
219                                         const float dist_points[3] = {
220                                             len_squared_v2v2(co_ss[0], mval),
221                                             len_squared_v2v2(co_ss[1], mval),
222                                             len_squared_v2v2(co_ss[2], mval),
223                                         };
224                                         if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
225                                                 co_index_best = min_axis_v3(dist_points);
226                                         }
227                                         else {
228                                                 co_index_best = -1;
229                                         }
230                                 }
231                         }
232                 }
233                 else {
234                         dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
235                         if (dist < dist_best) {
236                                 dist_best = dist;
237                                 found = true;
238
239                                 {
240                                         const float dist_points[2] = {
241                                             len_squared_v2v2(co_ss[0], mval),
242                                             len_squared_v2v2(co_ss[2], mval),
243                                         };
244                                         if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
245                                                 co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
246                                         }
247                                         else {
248                                                 co_index_best = -1;
249                                         }
250                                 }
251                         }
252                 }
253         }
254
255         *r_co_index = co_index_best;
256         return found;
257 }
258
259 /**
260  * Ensure the 'snap_context' is only cached while dragging,
261  * needed since the user may toggle modes between tool use.
262  */
263 static void ruler_state_set(bContext *C, RulerInfo *ruler_info, int state)
264 {
265         if (state == ruler_info->state) {
266                 return;
267         }
268
269         /* always remove */
270         if (ruler_info->snap_context) {
271                 ED_transform_snap_object_context_destroy(ruler_info->snap_context);
272                 ruler_info->snap_context = NULL;
273         }
274
275         if (state == RULER_STATE_NORMAL) {
276                 /* pass */
277         }
278         else if (state == RULER_STATE_DRAG) {
279                 ruler_info->snap_context = ED_transform_snap_object_context_create_view3d(
280                         CTX_data_main(C), CTX_data_scene(C), 0,
281                         ruler_info->ar, CTX_wm_view3d(C));
282         }
283         else {
284                 BLI_assert(0);
285         }
286
287         ruler_info->state = state;
288 }
289
290 static void view3d_ruler_item_project(
291         RulerInfo *ruler_info, float r_co[3],
292         const int xy[2])
293 {
294         ED_view3d_win_to_3d_int(ruler_info->sa->spacedata.first, ruler_info->ar, r_co, xy, r_co);
295 }
296
297 /* use for mousemove events */
298 static bool view3d_ruler_item_mousemove(
299         RulerInfo *ruler_info, RulerItem *ruler_item, const int mval[2],
300         const bool do_thickness, const bool do_snap)
301 {
302         RulerInteraction *inter = ruler_item->mpr.interaction_data;
303         const float eps_bias = 0.0002f;
304         float dist_px = MVAL_MAX_PX_DIST * U.pixelsize;  /* snap dist */
305
306         ruler_info->snap_flag &= ~RULER_SNAP_OK;
307
308         if (ruler_item) {
309                 float *co = ruler_item->co[inter->co_index];
310                 /* restore the initial depth */
311                 copy_v3_v3(co, inter->drag_start_co);
312                 view3d_ruler_item_project(ruler_info, co, mval);
313                 if (do_thickness && inter->co_index != 1) {
314                         // Scene *scene = CTX_data_scene(C);
315                         // View3D *v3d = ruler_info->sa->spacedata.first;
316                         const float mval_fl[2] = {UNPACK2(mval)};
317                         float ray_normal[3];
318                         float ray_start[3];
319                         float *co_other;
320
321                         co_other = ruler_item->co[inter->co_index == 0 ? 2 : 0];
322
323                         if (ED_transform_snap_object_project_view3d_mixed(
324                                 ruler_info->snap_context,
325                                 SCE_SELECT_FACE,
326                                 &(const struct SnapObjectParams){
327                                     .snap_select = SNAP_ALL,
328                                     .use_object_edit_cage = true,
329                                 },
330                                 mval_fl, &dist_px, true,
331                                 co, ray_normal))
332                         {
333                                 negate_v3(ray_normal);
334                                 /* add some bias */
335                                 madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias);
336                                 ED_transform_snap_object_project_ray(
337                                         ruler_info->snap_context,
338                                         &(const struct SnapObjectParams){
339                                             .snap_select = SNAP_ALL,
340                                             .use_object_edit_cage = true,
341                                         },
342                                         ray_start, ray_normal, NULL,
343                                         co_other, NULL);
344                         }
345                 }
346                 else if (do_snap) {
347                         // Scene *scene = CTX_data_scene(C);
348                         View3D *v3d = ruler_info->sa->spacedata.first;
349                         const float mval_fl[2] = {UNPACK2(mval)};
350                         bool use_depth = (v3d->drawtype >= OB_SOLID);
351
352                         if (ED_transform_snap_object_project_view3d_mixed(
353                                 ruler_info->snap_context,
354                                 (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0),
355                                 &(const struct SnapObjectParams){
356                                     .snap_select = SNAP_ALL,
357                                     .use_object_edit_cage = true,
358                                 },
359                                 mval_fl, &dist_px, use_depth,
360                                 co, NULL))
361                         {
362                                 ruler_info->snap_flag |= RULER_SNAP_OK;
363                         }
364                 }
365                 return true;
366         }
367         else {
368                 return false;
369         }
370 }
371
372
373 /** \} */
374
375 /* -------------------------------------------------------------------- */
376 /** \name Ruler/Grease Pencil Conversion
377  * \{ */
378
379 #define RULER_ID "RulerData3D"
380 static bool view3d_ruler_to_gpencil(bContext *C, wmManipulatorGroup *mgroup)
381 {
382         // RulerInfo *ruler_info = mgroup->customdata;
383         Scene *scene = CTX_data_scene(C);
384         bGPDlayer *gpl;
385         bGPDframe *gpf;
386         bGPDstroke *gps;
387         bGPDpalette *palette;
388         bGPDpalettecolor *palcolor;
389         RulerItem *ruler_item;
390         const char *ruler_name = RULER_ID;
391         bool changed = false;
392
393         if (scene->gpd == NULL) {
394                 scene->gpd = BKE_gpencil_data_addnew("GPencil");
395         }
396
397         gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
398         if (gpl == NULL) {
399                 gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false);
400                 gpl->thickness = 1;
401                 gpl->flag |= GP_LAYER_HIDE;
402         }
403
404         /* try to get active palette or create a new one */
405         palette = BKE_gpencil_palette_getactive(scene->gpd);
406         if (palette == NULL) {
407                 palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true);
408         }
409         /* try to get color with the ruler name or create a new one */
410         palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name);
411         if (palcolor == NULL) {
412                 palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true);
413         }
414
415         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
416         BKE_gpencil_free_strokes(gpf);
417
418         for (ruler_item = mgroup->manipulators.first; ruler_item; ruler_item = (RulerItem *)ruler_item->mpr.next) {
419                 bGPDspoint *pt;
420                 int j;
421
422                 /* allocate memory for a new stroke */
423                 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
424                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
425                         gps->totpoints = 3;
426                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
427                         for (j = 0; j < 3; j++) {
428                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
429                                 pt->pressure = 1.0f;
430                                 pt->strength = 1.0f;
431                                 pt++;
432                         }
433                 }
434                 else {
435                         gps->totpoints = 2;
436                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
437                         for (j = 0; j < 3; j += 2) {
438                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
439                                 pt->pressure = 1.0f;
440                                 pt->strength = 1.0f;
441                                 pt++;
442                         }
443                 }
444                 gps->flag = GP_STROKE_3DSPACE;
445                 gps->thickness = 3;
446                 /* assign color to stroke */
447                 BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname));
448                 gps->palcolor = palcolor;
449                 BLI_addtail(&gpf->strokes, gps);
450                 changed = true;
451         }
452
453         return changed;
454 }
455
456 static bool view3d_ruler_from_gpencil(const bContext *C, wmManipulatorGroup *mgroup)
457 {
458         Scene *scene = CTX_data_scene(C);
459         bool changed = false;
460
461         if (scene->gpd) {
462                 bGPDlayer *gpl;
463                 const char *ruler_name = RULER_ID;
464                 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
465                 if (gpl) {
466                         bGPDframe *gpf;
467                         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false);
468                         if (gpf) {
469                                 bGPDstroke *gps;
470                                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
471                                         bGPDspoint *pt = gps->points;
472                                         int j;
473                                         RulerItem *ruler_item = NULL;
474                                         if (gps->totpoints == 3) {
475                                                 ruler_item = ruler_item_add(mgroup);
476                                                 for (j = 0; j < 3; j++) {
477                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
478                                                         pt++;
479                                                 }
480                                                 ruler_item->flag |= RULERITEM_USE_ANGLE;
481                                                 changed = true;
482                                         }
483                                         else if (gps->totpoints == 2) {
484                                                 ruler_item = ruler_item_add(mgroup);
485                                                 for (j = 0; j < 3; j += 2) {
486                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
487                                                         pt++;
488                                                 }
489                                                 changed = true;
490                                         }
491                                 }
492                         }
493                 }
494         }
495
496         return changed;
497 }
498
499 /** \} */
500
501 /* -------------------------------------------------------------------- */
502 /** \name Ruler Item Manipulator Type
503  * \{ */
504
505 static void manipulator_ruler_draw(const bContext *C, wmManipulator *mpr)
506 {
507         Scene *scene = CTX_data_scene(C);
508         UnitSettings *unit = &scene->unit;
509         RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
510         RulerItem *ruler_item = (RulerItem *)mpr;
511         ARegion *ar = ruler_info->ar;
512         RegionView3D *rv3d = ar->regiondata;
513         const float cap_size = 4.0f;
514         const float bg_margin = 4.0f * U.pixelsize;
515         const float bg_radius = 4.0f * U.pixelsize;
516         const float arc_size = 64.0f * U.pixelsize;
517 #define ARC_STEPS 24
518         const int arc_steps = ARC_STEPS;
519         const float color_act[4] = {1.0f, 1.0f, 1.0f, 1.0f};
520         const float color_base[4] = {0.0f, 0.0f, 0.0f, 1.0f};
521         unsigned char color_text[3];
522         unsigned char color_wire[3];
523         float color_back[4] = {1.0f, 1.0f, 1.0f, 0.5f};
524
525         /* anti-aliased lines for more consistent appearance */
526         glEnable(GL_LINE_SMOOTH);
527
528         BLF_enable(blf_mono_font, BLF_ROTATION);
529         BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
530         BLF_rotation(blf_mono_font, 0.0f);
531
532         UI_GetThemeColor3ubv(TH_TEXT, color_text);
533         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
534
535         const bool is_act = (mpr->flag & WM_MANIPULATOR_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         glEnable(GL_BLEND);
546
547         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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                 glGetFloatv(GL_VIEWPORT, viewport_size);
554                 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
555
556                 immUniform1i("num_colors", 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(GWN_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(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(GWN_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                         glEnable(GL_BLEND);
633
634                         immUniformColor3ubv(color_wire);
635
636                         immBegin(GWN_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                         glDisable(GL_BLEND);
657                 }
658
659                 immUnbindProgram();
660
661                 /* text */
662                 {
663                         char numstr[256];
664                         float numstr_size[2];
665                         float posit[2];
666                         const int prec = 2;  /* XXX, todo, make optional */
667
668                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
669
670                         BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
671
672                         posit[0] = co_ss[1][0] + (cap_size * 2.0f);
673                         posit[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
674
675                         /* draw text (bg) */
676                         UI_draw_roundbox_corner_set(UI_CNR_ALL);
677                         UI_draw_roundbox_aa(
678                                 true,
679                                 posit[0] - bg_margin,                  posit[1] - bg_margin,
680                                 posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
681                                 bg_radius, color_back);
682                         /* draw text */
683                         BLF_color3ubv(blf_mono_font, color_text);
684                         BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
685                         BLF_rotation(blf_mono_font, 0.0f);
686                         BLF_draw(blf_mono_font, numstr, sizeof(numstr));
687                 }
688         }
689         else {
690                 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
691
692                 float viewport_size[4];
693                 glGetFloatv(GL_VIEWPORT, viewport_size);
694                 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
695
696                 immUniform1i("num_colors", 2);  /* "advanced" mode */
697                 const float *col = is_act ? color_act : color_base;
698                 immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
699                 immUniform1f("dash_width", 6.0f);
700
701                 immBegin(GWN_PRIM_LINES, 2);
702
703                 immVertex2fv(shdr_pos, co_ss[0]);
704                 immVertex2fv(shdr_pos, co_ss[2]);
705
706                 immEnd();
707
708                 immUnbindProgram();
709
710                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
711
712                 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
713
714                 /* capping */
715                 {
716                         float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
717                         float cap[2];
718
719                         normalize_v2(rot_90_vec);
720
721                         glEnable(GL_BLEND);
722
723                         immUniformColor3ubv(color_wire);
724
725                         immBegin(GWN_PRIM_LINES, 4);
726
727                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
728                         immVertex2fv(shdr_pos, cap);
729                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
730                         immVertex2fv(shdr_pos, cap);
731
732                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
733                         immVertex2fv(shdr_pos, cap);
734                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
735                         immVertex2fv(shdr_pos, cap);
736
737                         immEnd();
738
739                         glDisable(GL_BLEND);
740                 }
741
742                 immUnbindProgram();
743
744                 /* text */
745                 {
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                         UI_draw_roundbox_corner_set(UI_CNR_ALL);
763                         UI_draw_roundbox_aa(
764                                 true,
765                                 posit[0] - bg_margin,                  posit[1] - bg_margin,
766                                 posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
767                                 bg_radius, color_back);
768                         /* draw text */
769                         BLF_color3ubv(blf_mono_font, color_text);
770                         BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
771                         BLF_draw(blf_mono_font, numstr, sizeof(numstr));
772                 }
773         }
774
775         glDisable(GL_LINE_SMOOTH);
776
777         BLF_disable(blf_mono_font, BLF_ROTATION);
778
779 #undef ARC_STEPS
780
781         /* draw snap */
782         if ((ruler_info->snap_flag & RULER_SNAP_OK) &&
783             (ruler_info->state == RULER_STATE_DRAG) &&
784             (ruler_item->mpr.interaction_data != NULL))
785         {
786                 RulerInteraction *inter = ruler_item->mpr.interaction_data;
787                 /* size from drawSnapping */
788                 const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
789                 float co_ss_snap[3];
790                 ED_view3d_project_float_global(ar, ruler_item->co[inter->co_index], co_ss_snap, V3D_PROJ_TEST_NOP);
791
792                 unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
793
794                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
795                 immUniformColor4fv(color_act);
796
797                 imm_draw_circle_wire_2d(pos, co_ss_snap[0], co_ss_snap[1], size * U.pixelsize, 32);
798
799                 immUnbindProgram();
800         }
801 }
802
803 static int manipulator_ruler_test_select(
804         bContext *UNUSED(C), wmManipulator *mpr, const wmEvent *event)
805 {
806         RulerItem *ruler_item_pick = (RulerItem *)mpr;
807         float mval_fl[2] = {UNPACK2(event->mval)};
808         int co_index;
809
810         /* select and drag */
811         if (view3d_ruler_pick(mpr->parent_mgroup, ruler_item_pick, mval_fl, &co_index)) {
812                 if (co_index == -1) {
813                         if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
814                                 return PART_LINE;
815                         }
816                 }
817                 else {
818                         return co_index;
819                 }
820         }
821         return -1;
822 }
823
824 static int manipulator_ruler_modal(
825         bContext *C, wmManipulator *mpr, const wmEvent *event,
826         eWM_ManipulatorTweak UNUSED(tweak_flag))
827 {
828         bool do_draw = false;
829         int exit_code = OPERATOR_RUNNING_MODAL;
830         RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
831         RulerItem *ruler_item = (RulerItem *)mpr;
832         RulerInteraction *inter = ruler_item->mpr.interaction_data;
833         ARegion *ar = CTX_wm_region(C);
834
835         ruler_info->ar = ar;
836
837         switch (event->type) {
838                 case MOUSEMOVE:
839                 {
840                         if (ruler_info->state == RULER_STATE_DRAG) {
841                                 if (view3d_ruler_item_mousemove(
842                                         ruler_info, ruler_item, event->mval,
843                                         event->shift != 0, event->ctrl != 0))
844                                 {
845                                         do_draw = true;
846                                 }
847                                 inter->inside_region = BLI_rcti_isect_pt_v(&ar->winrct, &event->x);
848                         }
849                         break;
850                 }
851         }
852         if (do_draw) {
853                 ED_region_tag_redraw(ar);
854         }
855         return exit_code;
856 }
857
858 static int manipulator_ruler_invoke(
859         bContext *C, wmManipulator *mpr, const wmEvent *event)
860 {
861         wmManipulatorGroup *mgroup = mpr->parent_mgroup;
862         RulerInfo *ruler_info = mgroup->customdata;
863         RulerItem *ruler_item_pick = (RulerItem *)mpr;
864         RulerInteraction *inter = MEM_callocN(sizeof(RulerInteraction), __func__);
865         mpr->interaction_data = inter;
866
867         ARegion *ar = ruler_info->ar;
868
869         const float mval_fl[2] = {UNPACK2(event->mval)};
870
871         /* select and drag */
872         if (mpr->highlight_part == PART_LINE) {
873                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
874                         /* Add Center Point */
875                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
876                         inter->co_index = 1;
877                         ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
878
879                         /* find the factor */
880                         {
881                                 float co_ss[2][2];
882                                 float fac;
883
884                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
885                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
886
887                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
888                                 CLAMP(fac, 0.0f, 1.0f);
889
890                                 interp_v3_v3v3(ruler_item_pick->co[1],
891                                                ruler_item_pick->co[0],
892                                                ruler_item_pick->co[2], fac);
893                         }
894
895                         /* update the new location */
896                         view3d_ruler_item_mousemove(
897                                 ruler_info, ruler_item_pick, event->mval,
898                                 event->shift != 0, event->ctrl != 0);
899                 }
900         }
901         else {
902                 inter->co_index = mpr->highlight_part;
903                 ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
904
905                 /* store the initial depth */
906                 copy_v3_v3(inter->drag_start_co, ruler_item_pick->co[inter->co_index]);
907         }
908
909         return OPERATOR_RUNNING_MODAL;
910 }
911
912 static void manipulator_ruler_exit(bContext *C, wmManipulator *mpr, const bool cancel)
913 {
914         wmManipulatorGroup *mgroup = mpr->parent_mgroup;
915         RulerInfo *ruler_info = mgroup->customdata;
916
917         if (!cancel) {
918                 if (ruler_info->state == RULER_STATE_DRAG) {
919                         RulerItem *ruler_item = (RulerItem *)mpr;
920                         RulerInteraction *inter = mpr->interaction_data;
921                         /* rubber-band angle removal */
922                         if (!inter->inside_region) {
923                                 if ((inter->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
924                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
925                                 }
926                                 else {
927                                         /* Not ideal, since the ruler isn't a mode and we don't want to override delete key
928                                          * use dragging out of the view for removal. */
929                                         ruler_item_remove(C, mgroup, ruler_item);
930                                         ruler_item = NULL;
931                                         mpr = NULL;
932                                         inter = NULL;
933                                 }
934                         }
935                         if (ruler_info->snap_flag & RULER_SNAP_OK) {
936                                 ruler_info->snap_flag &= ~RULER_SNAP_OK;
937                         }
938                         ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
939                 }
940                 /* We could convert only the current manipulator, for now just re-generate. */
941                 view3d_ruler_to_gpencil(C, mgroup);
942         }
943
944         if (mpr) {
945                 MEM_SAFE_FREE(mpr->interaction_data);
946         }
947
948         ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
949 }
950
951 static int manipulator_ruler_cursor_get(wmManipulator *mpr)
952 {
953         if (mpr->highlight_part == PART_LINE) {
954                 return BC_CROSSCURSOR;
955         }
956         return BC_NSEW_SCROLLCURSOR;
957 }
958
959 void VIEW3D_WT_ruler_item(wmManipulatorType *wt)
960 {
961         /* identifiers */
962         wt->idname = "VIEW3D_WT_ruler_item";
963
964         /* api callbacks */
965         wt->draw = manipulator_ruler_draw;
966         wt->test_select = manipulator_ruler_test_select;
967         wt->modal = manipulator_ruler_modal;
968         wt->invoke = manipulator_ruler_invoke;
969         wt->exit = manipulator_ruler_exit;
970         wt->cursor_get = manipulator_ruler_cursor_get;
971
972         wt->struct_size = sizeof(RulerItem);
973 }
974
975 /** \} */
976
977 /* -------------------------------------------------------------------- */
978 /** \name Ruler Manipulator Group
979  * \{ */
980
981 static bool WIDGETGROUP_ruler_poll(const bContext *C, wmManipulatorGroupType *wgt)
982 {
983         WorkSpace *workspace = CTX_wm_workspace(C);
984         if (!STREQ(wgt->idname, workspace->tool.manipulator_group)) {
985                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
986                 return false;
987         }
988         return true;
989 }
990
991 static void WIDGETGROUP_ruler_setup(const bContext *C, wmManipulatorGroup *mgroup)
992 {
993         RulerInfo *ruler_info = MEM_callocN(sizeof(RulerInfo), __func__);
994
995         if (view3d_ruler_from_gpencil(C, mgroup)) {
996                 /* nop */
997         }
998
999         wmWindow *win = CTX_wm_window(C);
1000         ScrArea *sa = CTX_wm_area(C);
1001         ARegion *ar = CTX_wm_region(C);
1002         ruler_info->win = win;
1003         ruler_info->sa = sa;
1004         ruler_info->ar = ar;
1005
1006         mgroup->customdata = ruler_info;
1007 }
1008
1009 void VIEW3D_WGT_ruler(wmManipulatorGroupType *wgt)
1010 {
1011         wgt->name = "Ruler Widgets";
1012         wgt->idname = view3d_wgt_ruler_id;
1013
1014         wgt->flag |= WM_MANIPULATORGROUPTYPE_SCALE | WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL;
1015
1016         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1017         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1018
1019         wgt->poll = WIDGETGROUP_ruler_poll;
1020         wgt->setup = WIDGETGROUP_ruler_setup;
1021 }
1022
1023 /** \} */
1024
1025 /* -------------------------------------------------------------------- */
1026 /** \name Add Ruler Operator
1027  * \{ */
1028
1029 static int view3d_ruler_poll(bContext *C)
1030 {
1031         WorkSpace *workspace = CTX_wm_workspace(C);
1032         if (!STREQ(view3d_wgt_ruler_id, workspace->tool.manipulator_group) ||
1033             CTX_wm_region_view3d(C) == NULL)
1034         {
1035                 return false;
1036         }
1037         return true;
1038 }
1039
1040 static int view3d_ruler_add_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1041 {
1042         ARegion *ar = CTX_wm_region(C);
1043         View3D *v3d = CTX_wm_view3d(C);
1044         RegionView3D *rv3d = ar->regiondata;
1045
1046         wmManipulatorMap *mmap = ar->manipulator_map;
1047         wmManipulatorGroup *mgroup = WM_manipulatormap_group_find(mmap, view3d_wgt_ruler_id);
1048         const bool use_depth = (v3d->drawtype >= OB_SOLID);
1049
1050         /* Create new line */
1051         RulerItem *ruler_item;
1052         ruler_item = ruler_item_add(mgroup);
1053
1054         /* This is a little weak, but there is no real good way to tweak directly. */
1055         WM_manipulator_highlight_set(mmap, &ruler_item->mpr);
1056         if (WM_operator_name_call(
1057                 C, "MANIPULATORGROUP_OT_manipulator_tweak",
1058                 WM_OP_INVOKE_REGION_WIN, NULL) == OPERATOR_RUNNING_MODAL)
1059         {
1060                 RulerInfo *ruler_info = mgroup->customdata;
1061                 RulerInteraction *inter = ruler_item->mpr.interaction_data;
1062                 if (use_depth) {
1063                         /* snap the first point added, not essential but handy */
1064                         inter->co_index = 0;
1065                         view3d_ruler_item_mousemove(ruler_info, ruler_item, event->mval, false, true);
1066                         copy_v3_v3(inter->drag_start_co, ruler_item->co[inter->co_index]);
1067                 }
1068                 else {
1069                         negate_v3_v3(inter->drag_start_co, rv3d->ofs);
1070                         copy_v3_v3(ruler_item->co[0], inter->drag_start_co);
1071                         view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
1072                 }
1073
1074                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
1075                 ruler_item->mpr.highlight_part = inter->co_index = 2;
1076         }
1077         return OPERATOR_FINISHED;
1078 }
1079
1080 void VIEW3D_OT_ruler_add(wmOperatorType *ot)
1081 {
1082         /* identifiers */
1083         ot->name = "Ruler Add";
1084         ot->idname = "VIEW3D_OT_ruler_add";
1085         ot->description = "";
1086
1087         ot->invoke = view3d_ruler_add_invoke;
1088         ot->poll = view3d_ruler_poll;
1089
1090         /* flags */
1091         ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
1092 }
1093
1094 /** \} */