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