Tool System: per space/mode tool support
[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), CTX_data_depsgraph(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                                     .use_occlusion_test = true,
330                                 },
331                                 mval_fl, &dist_px,
332                                 co, ray_normal))
333                         {
334                                 negate_v3(ray_normal);
335                                 /* add some bias */
336                                 madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias);
337                                 ED_transform_snap_object_project_ray(
338                                         ruler_info->snap_context,
339                                         &(const struct SnapObjectParams){
340                                             .snap_select = SNAP_ALL,
341                                             .use_object_edit_cage = true,
342                                         },
343                                         ray_start, ray_normal, NULL,
344                                         co_other, NULL);
345                         }
346                 }
347                 else if (do_snap) {
348                         // Scene *scene = CTX_data_scene(C);
349                         View3D *v3d = ruler_info->sa->spacedata.first;
350                         const float mval_fl[2] = {UNPACK2(mval)};
351                         bool use_depth = (v3d->drawtype >= OB_SOLID);
352
353                         if (ED_transform_snap_object_project_view3d_mixed(
354                                 ruler_info->snap_context,
355                                 (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0),
356                                 &(const struct SnapObjectParams){
357                                     .snap_select = SNAP_ALL,
358                                     .use_object_edit_cage = true,
359                                     .use_occlusion_test = use_depth,
360                                 },
361                                 mval_fl, &dist_px,
362                                 co, NULL))
363                         {
364                                 ruler_info->snap_flag |= RULER_SNAP_OK;
365                         }
366                 }
367                 return true;
368         }
369         else {
370                 return false;
371         }
372 }
373
374
375 /** \} */
376
377 /* -------------------------------------------------------------------- */
378 /** \name Ruler/Grease Pencil Conversion
379  * \{ */
380
381 #define RULER_ID "RulerData3D"
382 static bool view3d_ruler_to_gpencil(bContext *C, wmManipulatorGroup *mgroup)
383 {
384         // RulerInfo *ruler_info = mgroup->customdata;
385         Scene *scene = CTX_data_scene(C);
386         bGPDlayer *gpl;
387         bGPDframe *gpf;
388         bGPDstroke *gps;
389         bGPDpalette *palette;
390         bGPDpalettecolor *palcolor;
391         RulerItem *ruler_item;
392         const char *ruler_name = RULER_ID;
393         bool changed = false;
394
395         if (scene->gpd == NULL) {
396                 scene->gpd = BKE_gpencil_data_addnew("GPencil");
397         }
398
399         gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
400         if (gpl == NULL) {
401                 gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false);
402                 gpl->thickness = 1;
403                 gpl->flag |= GP_LAYER_HIDE;
404         }
405
406         /* try to get active palette or create a new one */
407         palette = BKE_gpencil_palette_getactive(scene->gpd);
408         if (palette == NULL) {
409                 palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true);
410         }
411         /* try to get color with the ruler name or create a new one */
412         palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name);
413         if (palcolor == NULL) {
414                 palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true);
415         }
416
417         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
418         BKE_gpencil_free_strokes(gpf);
419
420         for (ruler_item = mgroup->manipulators.first; ruler_item; ruler_item = (RulerItem *)ruler_item->mpr.next) {
421                 bGPDspoint *pt;
422                 int j;
423
424                 /* allocate memory for a new stroke */
425                 gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
426                 if (ruler_item->flag & RULERITEM_USE_ANGLE) {
427                         gps->totpoints = 3;
428                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
429                         for (j = 0; j < 3; j++) {
430                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
431                                 pt->pressure = 1.0f;
432                                 pt->strength = 1.0f;
433                                 pt++;
434                         }
435                 }
436                 else {
437                         gps->totpoints = 2;
438                         pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
439                         for (j = 0; j < 3; j += 2) {
440                                 copy_v3_v3(&pt->x, ruler_item->co[j]);
441                                 pt->pressure = 1.0f;
442                                 pt->strength = 1.0f;
443                                 pt++;
444                         }
445                 }
446                 gps->flag = GP_STROKE_3DSPACE;
447                 gps->thickness = 3;
448                 /* assign color to stroke */
449                 BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname));
450                 gps->palcolor = palcolor;
451                 BLI_addtail(&gpf->strokes, gps);
452                 changed = true;
453         }
454
455         return changed;
456 }
457
458 static bool view3d_ruler_from_gpencil(const bContext *C, wmManipulatorGroup *mgroup)
459 {
460         Scene *scene = CTX_data_scene(C);
461         bool changed = false;
462
463         if (scene->gpd) {
464                 bGPDlayer *gpl;
465                 const char *ruler_name = RULER_ID;
466                 gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
467                 if (gpl) {
468                         bGPDframe *gpf;
469                         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false);
470                         if (gpf) {
471                                 bGPDstroke *gps;
472                                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
473                                         bGPDspoint *pt = gps->points;
474                                         int j;
475                                         RulerItem *ruler_item = NULL;
476                                         if (gps->totpoints == 3) {
477                                                 ruler_item = ruler_item_add(mgroup);
478                                                 for (j = 0; j < 3; j++) {
479                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
480                                                         pt++;
481                                                 }
482                                                 ruler_item->flag |= RULERITEM_USE_ANGLE;
483                                                 changed = true;
484                                         }
485                                         else if (gps->totpoints == 2) {
486                                                 ruler_item = ruler_item_add(mgroup);
487                                                 for (j = 0; j < 3; j += 2) {
488                                                         copy_v3_v3(ruler_item->co[j], &pt->x);
489                                                         pt++;
490                                                 }
491                                                 changed = true;
492                                         }
493                                 }
494                         }
495                 }
496         }
497
498         return changed;
499 }
500
501 /** \} */
502
503 /* -------------------------------------------------------------------- */
504 /** \name Ruler Item Manipulator Type
505  * \{ */
506
507 static void manipulator_ruler_draw(const bContext *C, wmManipulator *mpr)
508 {
509         Scene *scene = CTX_data_scene(C);
510         UnitSettings *unit = &scene->unit;
511         RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
512         RulerItem *ruler_item = (RulerItem *)mpr;
513         ARegion *ar = ruler_info->ar;
514         RegionView3D *rv3d = ar->regiondata;
515         const float cap_size = 4.0f;
516         const float bg_margin = 4.0f * U.pixelsize;
517         const float bg_radius = 4.0f * U.pixelsize;
518         const float arc_size = 64.0f * U.pixelsize;
519 #define ARC_STEPS 24
520         const int arc_steps = ARC_STEPS;
521         const float color_act[4] = {1.0f, 1.0f, 1.0f, 1.0f};
522         const float color_base[4] = {0.0f, 0.0f, 0.0f, 1.0f};
523         unsigned char color_text[3];
524         unsigned char color_wire[3];
525         float color_back[4] = {1.0f, 1.0f, 1.0f, 0.5f};
526
527         /* anti-aliased lines for more consistent appearance */
528         glEnable(GL_LINE_SMOOTH);
529
530         BLF_enable(blf_mono_font, BLF_ROTATION);
531         BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
532         BLF_rotation(blf_mono_font, 0.0f);
533
534         UI_GetThemeColor3ubv(TH_TEXT, color_text);
535         UI_GetThemeColor3ubv(TH_WIRE, color_wire);
536
537         const bool is_act = (mpr->flag & WM_MANIPULATOR_DRAW_HOVER);
538         float dir_ruler[2];
539         float co_ss[3][2];
540         int j;
541
542         /* should these be checked? - ok for now not to */
543         for (j = 0; j < 3; j++) {
544                 ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
545         }
546
547         glEnable(GL_BLEND);
548
549         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
550
551         if (ruler_item->flag & RULERITEM_USE_ANGLE) {
552                 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
553
554                 float viewport_size[4];
555                 glGetFloatv(GL_VIEWPORT, viewport_size);
556                 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
557
558                 immUniform1i("num_colors", 2);  /* "advanced" mode */
559                 const float *col = is_act ? color_act : color_base;
560                 immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
561                 immUniform1f("dash_width", 6.0f);
562
563                 immBegin(GWN_PRIM_LINE_STRIP, 3);
564
565                 immVertex2fv(shdr_pos, co_ss[0]);
566                 immVertex2fv(shdr_pos, co_ss[1]);
567                 immVertex2fv(shdr_pos, co_ss[2]);
568
569                 immEnd();
570
571                 immUnbindProgram();
572
573                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
574
575                 /* arc */
576                 {
577                         float dir_tmp[3];
578                         float co_tmp[3];
579                         float arc_ss_coord[2];
580
581                         float dir_a[3];
582                         float dir_b[3];
583                         float quat[4];
584                         float axis[3];
585                         float angle;
586                         const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
587                                                 min_fff(arc_size,
588                                                         len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
589                                                         len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
590
591                         sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
592                         sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
593                         normalize_v3(dir_a);
594                         normalize_v3(dir_b);
595
596                         cross_v3_v3v3(axis, dir_a, dir_b);
597                         angle = angle_normalized_v3v3(dir_a, dir_b);
598
599                         axis_angle_to_quat(quat, axis, angle / arc_steps);
600
601                         copy_v3_v3(dir_tmp, dir_a);
602
603                         immUniformColor3ubv(color_wire);
604
605                         immBegin(GWN_PRIM_LINE_STRIP, arc_steps + 1);
606
607                         for (j = 0; j <= arc_steps; j++) {
608                                 madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
609                                 ED_view3d_project_float_global(ar, co_tmp, arc_ss_coord, V3D_PROJ_TEST_NOP);
610                                 mul_qt_v3(quat, dir_tmp);
611
612                                 immVertex2fv(shdr_pos, arc_ss_coord);
613                         }
614
615                         immEnd();
616                 }
617
618                 /* capping */
619                 {
620                         float rot_90_vec_a[2];
621                         float rot_90_vec_b[2];
622                         float cap[2];
623
624                         sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
625                         rot_90_vec_a[0] = -dir_ruler[1];
626                         rot_90_vec_a[1] =  dir_ruler[0];
627                         normalize_v2(rot_90_vec_a);
628
629                         sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
630                         rot_90_vec_b[0] = -dir_ruler[1];
631                         rot_90_vec_b[1] =  dir_ruler[0];
632                         normalize_v2(rot_90_vec_b);
633
634                         glEnable(GL_BLEND);
635
636                         immUniformColor3ubv(color_wire);
637
638                         immBegin(GWN_PRIM_LINES, 8);
639
640                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
641                         immVertex2fv(shdr_pos, cap);
642                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
643                         immVertex2fv(shdr_pos, cap);
644
645                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
646                         immVertex2fv(shdr_pos, cap);
647                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
648                         immVertex2fv(shdr_pos, cap);
649
650                         /* angle vertex */
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                         immVertex2f(shdr_pos, co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
654                         immVertex2f(shdr_pos, co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
655
656                         immEnd();
657
658                         glDisable(GL_BLEND);
659                 }
660
661                 immUnbindProgram();
662
663                 /* text */
664                 {
665                         char numstr[256];
666                         float numstr_size[2];
667                         float posit[2];
668                         const int prec = 2;  /* XXX, todo, make optional */
669
670                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
671
672                         BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
673
674                         posit[0] = co_ss[1][0] + (cap_size * 2.0f);
675                         posit[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
676
677                         /* draw text (bg) */
678                         UI_draw_roundbox_corner_set(UI_CNR_ALL);
679                         UI_draw_roundbox_aa(
680                                 true,
681                                 posit[0] - bg_margin,                  posit[1] - bg_margin,
682                                 posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
683                                 bg_radius, color_back);
684                         /* draw text */
685                         BLF_color3ubv(blf_mono_font, color_text);
686                         BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
687                         BLF_rotation(blf_mono_font, 0.0f);
688                         BLF_draw(blf_mono_font, numstr, sizeof(numstr));
689                 }
690         }
691         else {
692                 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
693
694                 float viewport_size[4];
695                 glGetFloatv(GL_VIEWPORT, viewport_size);
696                 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
697
698                 immUniform1i("num_colors", 2);  /* "advanced" mode */
699                 const float *col = is_act ? color_act : color_base;
700                 immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
701                 immUniform1f("dash_width", 6.0f);
702
703                 immBegin(GWN_PRIM_LINES, 2);
704
705                 immVertex2fv(shdr_pos, co_ss[0]);
706                 immVertex2fv(shdr_pos, co_ss[2]);
707
708                 immEnd();
709
710                 immUnbindProgram();
711
712                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
713
714                 sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
715
716                 /* capping */
717                 {
718                         float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
719                         float cap[2];
720
721                         normalize_v2(rot_90_vec);
722
723                         glEnable(GL_BLEND);
724
725                         immUniformColor3ubv(color_wire);
726
727                         immBegin(GWN_PRIM_LINES, 4);
728
729                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
730                         immVertex2fv(shdr_pos, cap);
731                         madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
732                         immVertex2fv(shdr_pos, cap);
733
734                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
735                         immVertex2fv(shdr_pos, cap);
736                         madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
737                         immVertex2fv(shdr_pos, cap);
738
739                         immEnd();
740
741                         glDisable(GL_BLEND);
742                 }
743
744                 immUnbindProgram();
745
746                 /* text */
747                 {
748                         char numstr[256];
749                         float numstr_size[2];
750                         const int prec = 6;  /* XXX, todo, make optional */
751                         float posit[2];
752
753                         ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
754
755                         BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
756
757                         mid_v2_v2v2(posit, co_ss[0], co_ss[2]);
758
759                         /* center text */
760                         posit[0] -= numstr_size[0] / 2.0f;
761                         posit[1] -= numstr_size[1] / 2.0f;
762
763                         /* draw text (bg) */
764                         UI_draw_roundbox_corner_set(UI_CNR_ALL);
765                         UI_draw_roundbox_aa(
766                                 true,
767                                 posit[0] - bg_margin,                  posit[1] - bg_margin,
768                                 posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
769                                 bg_radius, color_back);
770                         /* draw text */
771                         BLF_color3ubv(blf_mono_font, color_text);
772                         BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
773                         BLF_draw(blf_mono_font, numstr, sizeof(numstr));
774                 }
775         }
776
777         glDisable(GL_LINE_SMOOTH);
778
779         BLF_disable(blf_mono_font, BLF_ROTATION);
780
781 #undef ARC_STEPS
782
783         /* draw snap */
784         if ((ruler_info->snap_flag & RULER_SNAP_OK) &&
785             (ruler_info->state == RULER_STATE_DRAG) &&
786             (ruler_item->mpr.interaction_data != NULL))
787         {
788                 RulerInteraction *inter = ruler_item->mpr.interaction_data;
789                 /* size from drawSnapping */
790                 const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
791                 float co_ss_snap[3];
792                 ED_view3d_project_float_global(ar, ruler_item->co[inter->co_index], co_ss_snap, V3D_PROJ_TEST_NOP);
793
794                 unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
795
796                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
797                 immUniformColor4fv(color_act);
798
799                 imm_draw_circle_wire_2d(pos, co_ss_snap[0], co_ss_snap[1], size * U.pixelsize, 32);
800
801                 immUnbindProgram();
802         }
803 }
804
805 static int manipulator_ruler_test_select(
806         bContext *UNUSED(C), wmManipulator *mpr, const wmEvent *event)
807 {
808         RulerItem *ruler_item_pick = (RulerItem *)mpr;
809         float mval_fl[2] = {UNPACK2(event->mval)};
810         int co_index;
811
812         /* select and drag */
813         if (view3d_ruler_pick(mpr->parent_mgroup, ruler_item_pick, mval_fl, &co_index)) {
814                 if (co_index == -1) {
815                         if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
816                                 return PART_LINE;
817                         }
818                 }
819                 else {
820                         return co_index;
821                 }
822         }
823         return -1;
824 }
825
826 static int manipulator_ruler_modal(
827         bContext *C, wmManipulator *mpr, const wmEvent *event,
828         eWM_ManipulatorTweak UNUSED(tweak_flag))
829 {
830         bool do_draw = false;
831         int exit_code = OPERATOR_RUNNING_MODAL;
832         RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
833         RulerItem *ruler_item = (RulerItem *)mpr;
834         RulerInteraction *inter = ruler_item->mpr.interaction_data;
835         ARegion *ar = CTX_wm_region(C);
836
837         ruler_info->ar = ar;
838
839         switch (event->type) {
840                 case MOUSEMOVE:
841                 {
842                         if (ruler_info->state == RULER_STATE_DRAG) {
843                                 if (view3d_ruler_item_mousemove(
844                                         ruler_info, ruler_item, event->mval,
845                                         event->shift != 0, event->ctrl != 0))
846                                 {
847                                         do_draw = true;
848                                 }
849                                 inter->inside_region = BLI_rcti_isect_pt_v(&ar->winrct, &event->x);
850                         }
851                         break;
852                 }
853         }
854         if (do_draw) {
855                 ED_region_tag_redraw(ar);
856         }
857         return exit_code;
858 }
859
860 static int manipulator_ruler_invoke(
861         bContext *C, wmManipulator *mpr, const wmEvent *event)
862 {
863         wmManipulatorGroup *mgroup = mpr->parent_mgroup;
864         RulerInfo *ruler_info = mgroup->customdata;
865         RulerItem *ruler_item_pick = (RulerItem *)mpr;
866         RulerInteraction *inter = MEM_callocN(sizeof(RulerInteraction), __func__);
867         mpr->interaction_data = inter;
868
869         ARegion *ar = ruler_info->ar;
870
871         const float mval_fl[2] = {UNPACK2(event->mval)};
872
873         /* select and drag */
874         if (mpr->highlight_part == PART_LINE) {
875                 if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
876                         /* Add Center Point */
877                         ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
878                         inter->co_index = 1;
879                         ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
880
881                         /* find the factor */
882                         {
883                                 float co_ss[2][2];
884                                 float fac;
885
886                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
887                                 ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
888
889                                 fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
890                                 CLAMP(fac, 0.0f, 1.0f);
891
892                                 interp_v3_v3v3(ruler_item_pick->co[1],
893                                                ruler_item_pick->co[0],
894                                                ruler_item_pick->co[2], fac);
895                         }
896
897                         /* update the new location */
898                         view3d_ruler_item_mousemove(
899                                 ruler_info, ruler_item_pick, event->mval,
900                                 event->shift != 0, event->ctrl != 0);
901                 }
902         }
903         else {
904                 inter->co_index = mpr->highlight_part;
905                 ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
906
907                 /* store the initial depth */
908                 copy_v3_v3(inter->drag_start_co, ruler_item_pick->co[inter->co_index]);
909         }
910
911         return OPERATOR_RUNNING_MODAL;
912 }
913
914 static void manipulator_ruler_exit(bContext *C, wmManipulator *mpr, const bool cancel)
915 {
916         wmManipulatorGroup *mgroup = mpr->parent_mgroup;
917         RulerInfo *ruler_info = mgroup->customdata;
918
919         if (!cancel) {
920                 if (ruler_info->state == RULER_STATE_DRAG) {
921                         RulerItem *ruler_item = (RulerItem *)mpr;
922                         RulerInteraction *inter = mpr->interaction_data;
923                         /* rubber-band angle removal */
924                         if (!inter->inside_region) {
925                                 if ((inter->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
926                                         ruler_item->flag &= ~RULERITEM_USE_ANGLE;
927                                 }
928                                 else {
929                                         /* Not ideal, since the ruler isn't a mode and we don't want to override delete key
930                                          * use dragging out of the view for removal. */
931                                         ruler_item_remove(C, mgroup, ruler_item);
932                                         ruler_item = NULL;
933                                         mpr = NULL;
934                                         inter = NULL;
935                                 }
936                         }
937                         if (ruler_info->snap_flag & RULER_SNAP_OK) {
938                                 ruler_info->snap_flag &= ~RULER_SNAP_OK;
939                         }
940                         ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
941                 }
942                 /* We could convert only the current manipulator, for now just re-generate. */
943                 view3d_ruler_to_gpencil(C, mgroup);
944         }
945
946         if (mpr) {
947                 MEM_SAFE_FREE(mpr->interaction_data);
948         }
949
950         ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
951 }
952
953 static int manipulator_ruler_cursor_get(wmManipulator *mpr)
954 {
955         if (mpr->highlight_part == PART_LINE) {
956                 return BC_CROSSCURSOR;
957         }
958         return BC_NSEW_SCROLLCURSOR;
959 }
960
961 void VIEW3D_WT_ruler_item(wmManipulatorType *wt)
962 {
963         /* identifiers */
964         wt->idname = "VIEW3D_WT_ruler_item";
965
966         /* api callbacks */
967         wt->draw = manipulator_ruler_draw;
968         wt->test_select = manipulator_ruler_test_select;
969         wt->modal = manipulator_ruler_modal;
970         wt->invoke = manipulator_ruler_invoke;
971         wt->exit = manipulator_ruler_exit;
972         wt->cursor_get = manipulator_ruler_cursor_get;
973
974         wt->struct_size = sizeof(RulerItem);
975 }
976
977 /** \} */
978
979 /* -------------------------------------------------------------------- */
980 /** \name Ruler Manipulator Group
981  * \{ */
982
983 static bool WIDGETGROUP_ruler_poll(const bContext *C, wmManipulatorGroupType *wgt)
984 {
985         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
986         if ((tref_rt == NULL) ||
987             !STREQ(wgt->idname, tref_rt->manipulator_group))
988         {
989                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
990                 return false;
991         }
992         return true;
993 }
994
995 static void WIDGETGROUP_ruler_setup(const bContext *C, wmManipulatorGroup *mgroup)
996 {
997         RulerInfo *ruler_info = MEM_callocN(sizeof(RulerInfo), __func__);
998
999         if (view3d_ruler_from_gpencil(C, mgroup)) {
1000                 /* nop */
1001         }
1002
1003         wmWindow *win = CTX_wm_window(C);
1004         ScrArea *sa = CTX_wm_area(C);
1005         ARegion *ar = CTX_wm_region(C);
1006         ruler_info->win = win;
1007         ruler_info->sa = sa;
1008         ruler_info->ar = ar;
1009
1010         mgroup->customdata = ruler_info;
1011 }
1012
1013 void VIEW3D_WGT_ruler(wmManipulatorGroupType *wgt)
1014 {
1015         wgt->name = "Ruler Widgets";
1016         wgt->idname = view3d_wgt_ruler_id;
1017
1018         wgt->flag |= WM_MANIPULATORGROUPTYPE_SCALE | WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL;
1019
1020         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1021         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1022
1023         wgt->poll = WIDGETGROUP_ruler_poll;
1024         wgt->setup = WIDGETGROUP_ruler_setup;
1025 }
1026
1027 /** \} */
1028
1029 /* -------------------------------------------------------------------- */
1030 /** \name Add Ruler Operator
1031  * \{ */
1032
1033 static int view3d_ruler_poll(bContext *C)
1034 {
1035         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1036         if ((tref_rt == NULL) ||
1037             !STREQ(view3d_wgt_ruler_id, tref_rt->manipulator_group) ||
1038             CTX_wm_region_view3d(C) == NULL)
1039         {
1040                 return false;
1041         }
1042         return true;
1043 }
1044
1045 static int view3d_ruler_add_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1046 {
1047         ARegion *ar = CTX_wm_region(C);
1048         View3D *v3d = CTX_wm_view3d(C);
1049         RegionView3D *rv3d = ar->regiondata;
1050
1051         wmManipulatorMap *mmap = ar->manipulator_map;
1052         wmManipulatorGroup *mgroup = WM_manipulatormap_group_find(mmap, view3d_wgt_ruler_id);
1053         const bool use_depth = (v3d->drawtype >= OB_SOLID);
1054
1055         /* Create new line */
1056         RulerItem *ruler_item;
1057         ruler_item = ruler_item_add(mgroup);
1058
1059         /* This is a little weak, but there is no real good way to tweak directly. */
1060         WM_manipulator_highlight_set(mmap, &ruler_item->mpr);
1061         if (WM_operator_name_call(
1062                 C, "MANIPULATORGROUP_OT_manipulator_tweak",
1063                 WM_OP_INVOKE_REGION_WIN, NULL) == OPERATOR_RUNNING_MODAL)
1064         {
1065                 RulerInfo *ruler_info = mgroup->customdata;
1066                 RulerInteraction *inter = ruler_item->mpr.interaction_data;
1067                 if (use_depth) {
1068                         /* snap the first point added, not essential but handy */
1069                         inter->co_index = 0;
1070                         view3d_ruler_item_mousemove(ruler_info, ruler_item, event->mval, false, true);
1071                         copy_v3_v3(inter->drag_start_co, ruler_item->co[inter->co_index]);
1072                 }
1073                 else {
1074                         negate_v3_v3(inter->drag_start_co, rv3d->ofs);
1075                         copy_v3_v3(ruler_item->co[0], inter->drag_start_co);
1076                         view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
1077                 }
1078
1079                 copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
1080                 ruler_item->mpr.highlight_part = inter->co_index = 2;
1081         }
1082         return OPERATOR_FINISHED;
1083 }
1084
1085 void VIEW3D_OT_ruler_add(wmOperatorType *ot)
1086 {
1087         /* identifiers */
1088         ot->name = "Ruler Add";
1089         ot->idname = "VIEW3D_OT_ruler_add";
1090         ot->description = "";
1091
1092         ot->invoke = view3d_ruler_add_invoke;
1093         ot->poll = view3d_ruler_poll;
1094
1095         /* flags */
1096         ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
1097 }
1098
1099 /** \} */