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