8e2ca2e2b3ab510dd36f5e92724f51e4d2e80a0e
[blender.git] / source / blender / editors / interface / interface_eyedropper_depth.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  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/editors/interface/interface_eyedropper_depth.c
25  *  \ingroup edinterface
26  *
27  * This file defines an eyedropper for picking 3D depth value (primary use is depth-of-field).
28  *
29  * Defines:
30  * - #UI_OT_eyedropper_depth
31  */
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_space_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_view3d_types.h"
39
40 #include "BLI_string.h"
41 #include "BLI_math_vector.h"
42
43 #include "BKE_context.h"
44 #include "BKE_main.h"
45 #include "BKE_screen.h"
46 #include "BKE_unit.h"
47
48 #include "RNA_access.h"
49
50 #include "UI_interface.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "ED_screen.h"
56 #include "ED_space_api.h"
57 #include "ED_view3d.h"
58
59 #include "interface_intern.h"
60 #include "interface_eyedropper_intern.h"
61
62 /**
63  * \note #DepthDropper is only internal name to avoid confusion with other kinds of eye-droppers.
64  */
65 typedef struct DepthDropper {
66         PointerRNA ptr;
67         PropertyRNA *prop;
68
69         float init_depth; /* for resetting on cancel */
70
71         bool  accum_start; /* has mouse been presed */
72         float accum_depth;
73         int   accum_tot;
74
75         ARegionType *art;
76         void *draw_handle_pixel;
77         char name[200];
78 } DepthDropper;
79
80
81 static void depthdropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
82 {
83         DepthDropper *ddr = arg;
84         eyedropper_draw_cursor_text(C, ar, ddr->name);
85 }
86
87
88 static int depthdropper_init(bContext *C, wmOperator *op)
89 {
90         DepthDropper *ddr;
91         int index_dummy;
92
93         SpaceType *st;
94         ARegionType *art;
95
96         st = BKE_spacetype_from_id(SPACE_VIEW3D);
97         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
98
99         op->customdata = ddr = MEM_callocN(sizeof(DepthDropper), "DepthDropper");
100
101         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
102
103         /* fallback to the active camera's dof */
104         if (ddr->prop == NULL) {
105                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
106                 if (rv3d && rv3d->persp == RV3D_CAMOB) {
107                         View3D *v3d = CTX_wm_view3d(C);
108                         if (v3d->camera && v3d->camera->data && !ID_IS_LINKED(v3d->camera->data)) {
109                                 RNA_id_pointer_create(v3d->camera->data, &ddr->ptr);
110                                 ddr->prop = RNA_struct_find_property(&ddr->ptr, "dof_distance");
111                         }
112                 }
113         }
114
115         if ((ddr->ptr.data == NULL) ||
116             (ddr->prop == NULL) ||
117             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
118             (RNA_property_type(ddr->prop) != PROP_FLOAT))
119         {
120                 return false;
121         }
122
123         ddr->art = art;
124         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
125         ddr->init_depth = RNA_property_float_get(&ddr->ptr, ddr->prop);
126
127         return true;
128 }
129
130 static void depthdropper_exit(bContext *C, wmOperator *op)
131 {
132         WM_cursor_modal_restore(CTX_wm_window(C));
133
134         if (op->customdata) {
135                 DepthDropper *ddr = (DepthDropper *)op->customdata;
136
137                 if (ddr->art) {
138                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
139                 }
140
141                 MEM_freeN(op->customdata);
142
143                 op->customdata = NULL;
144         }
145 }
146
147 /* *** depthdropper id helper functions *** */
148 /**
149  * \brief get the ID from the screen.
150  *
151  */
152 static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth)
153 {
154         /* we could use some clever */
155         Main *bmain = CTX_data_main(C);
156         wmWindow *win = CTX_wm_window(C);
157         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my);
158         Scene *scene = win->screen->scene;
159         UnitSettings *unit = &scene->unit;
160         const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
161
162         ScrArea *area_prev = CTX_wm_area(C);
163         ARegion *ar_prev = CTX_wm_region(C);
164
165         ddr->name[0] = '\0';
166
167         if (sa) {
168                 if (sa->spacetype == SPACE_VIEW3D) {
169                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
170                         if (ar) {
171                                 View3D *v3d = sa->spacedata.first;
172                                 RegionView3D *rv3d = ar->regiondata;
173                                 /* weak, we could pass in some reference point */
174                                 const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3];
175                                 const int mval[2] = {
176                                     mx - ar->winrct.xmin,
177                                     my - ar->winrct.ymin};
178                                 float co[3];
179
180                                 CTX_wm_area_set(C, sa);
181                                 CTX_wm_region_set(C, ar);
182
183                                 /* grr, always draw else we leave stale text */
184                                 ED_region_tag_redraw(ar);
185
186                                 view3d_operator_needs_opengl(C);
187
188                                 if (ED_view3d_autodist(bmain, scene, ar, v3d, mval, co, true, NULL)) {
189                                         const float mval_center_fl[2] = {
190                                             (float)ar->winx / 2,
191                                             (float)ar->winy / 2};
192                                         float co_align[3];
193
194                                         /* quick way to get view-center aligned point */
195                                         ED_view3d_win_to_3d(v3d, ar, co, mval_center_fl, co_align);
196
197                                         *r_depth = len_v3v3(view_co, co_align);
198
199                                         bUnit_AsString(ddr->name, sizeof(ddr->name),
200                                                        (double)*r_depth,
201                                                        4, unit->system, B_UNIT_LENGTH, do_split, false);
202                                 }
203                                 else {
204                                         BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name));
205                                 }
206                         }
207                 }
208         }
209
210         CTX_wm_area_set(C, area_prev);
211         CTX_wm_region_set(C, ar_prev);
212 }
213
214 /* sets the sample depth RGB, maintaining A */
215 static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth)
216 {
217         RNA_property_float_set(&ddr->ptr, ddr->prop, depth);
218         RNA_property_update(C, &ddr->ptr, ddr->prop);
219 }
220
221 /* set sample from accumulated values */
222 static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr)
223 {
224         float depth = ddr->accum_depth;
225         if (ddr->accum_tot) {
226                 depth /= (float)ddr->accum_tot;
227         }
228         depthdropper_depth_set(C, ddr, depth);
229 }
230
231 /* single point sample & set */
232 static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, int mx, int my)
233 {
234         float depth = -1.0f;
235         if (depth != -1.0f) {
236                 depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
237                 depthdropper_depth_set(C, ddr, depth);
238         }
239 }
240
241 static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int mx, int my)
242 {
243         float depth = -1.0f;
244         depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
245         if (depth != -1.0f) {
246                 ddr->accum_depth += depth;
247                 ddr->accum_tot++;
248         }
249 }
250
251 static void depthdropper_cancel(bContext *C, wmOperator *op)
252 {
253         DepthDropper *ddr = op->customdata;
254         depthdropper_depth_set(C, ddr, ddr->init_depth);
255         depthdropper_exit(C, op);
256 }
257
258 /* main modal status check */
259 static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
260 {
261         DepthDropper *ddr = (DepthDropper *)op->customdata;
262
263         /* handle modal keymap */
264         if (event->type == EVT_MODAL_MAP) {
265                 switch (event->val) {
266                         case EYE_MODAL_CANCEL:
267                                 depthdropper_cancel(C, op);
268                                 return OPERATOR_CANCELLED;
269                         case EYE_MODAL_SAMPLE_CONFIRM:
270                                 if (ddr->accum_tot == 0) {
271                                         depthdropper_depth_sample(C, ddr, event->x, event->y);
272                                 }
273                                 else {
274                                         depthdropper_depth_set_accum(C, ddr);
275                                 }
276                                 depthdropper_exit(C, op);
277                                 return OPERATOR_FINISHED;
278                         case EYE_MODAL_SAMPLE_BEGIN:
279                                 /* enable accum and make first sample */
280                                 ddr->accum_start = true;
281                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
282                                 break;
283                         case EYE_MODAL_SAMPLE_RESET:
284                                 ddr->accum_tot = 0;
285                                 ddr->accum_depth = 0.0f;
286                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
287                                 depthdropper_depth_set_accum(C, ddr);
288                                 break;
289                 }
290         }
291         else if (event->type == MOUSEMOVE) {
292                 if (ddr->accum_start) {
293                         /* button is pressed so keep sampling */
294                         depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
295                         depthdropper_depth_set_accum(C, ddr);
296                 }
297         }
298
299         return OPERATOR_RUNNING_MODAL;
300 }
301
302 /* Modal Operator init */
303 static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
304 {
305         /* init */
306         if (depthdropper_init(C, op)) {
307                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
308
309                 /* add temp handler */
310                 WM_event_add_modal_handler(C, op);
311
312                 return OPERATOR_RUNNING_MODAL;
313         }
314         else {
315                 depthdropper_exit(C, op);
316                 return OPERATOR_CANCELLED;
317         }
318 }
319
320 /* Repeat operator */
321 static int depthdropper_exec(bContext *C, wmOperator *op)
322 {
323         /* init */
324         if (depthdropper_init(C, op)) {
325                 /* cleanup */
326                 depthdropper_exit(C, op);
327
328                 return OPERATOR_FINISHED;
329         }
330         else {
331                 return OPERATOR_CANCELLED;
332         }
333 }
334
335 static int depthdropper_poll(bContext *C)
336 {
337         PointerRNA ptr;
338         PropertyRNA *prop;
339         int index_dummy;
340         uiBut *but;
341
342         /* check if there's an active button taking depth value */
343         if ((CTX_wm_window(C) != NULL) &&
344             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
345             (but->type == UI_BTYPE_NUM) &&
346             (prop != NULL))
347         {
348                 if ((RNA_property_type(prop) == PROP_FLOAT) &&
349                     (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) &&
350                     (RNA_property_array_check(prop) == false))
351                 {
352                         return 1;
353                 }
354         }
355         else {
356                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
357                 if (rv3d && rv3d->persp == RV3D_CAMOB) {
358                         View3D *v3d = CTX_wm_view3d(C);
359                         if (v3d->camera && v3d->camera->data && !ID_IS_LINKED(v3d->camera->data)) {
360                                 return 1;
361                         }
362                 }
363         }
364
365         return 0;
366 }
367
368 void UI_OT_eyedropper_depth(wmOperatorType *ot)
369 {
370         /* identifiers */
371         ot->name = "Eyedropper Depth";
372         ot->idname = "UI_OT_eyedropper_depth";
373         ot->description = "Sample depth from the 3D view";
374
375         /* api callbacks */
376         ot->invoke = depthdropper_invoke;
377         ot->modal = depthdropper_modal;
378         ot->cancel = depthdropper_cancel;
379         ot->exec = depthdropper_exec;
380         ot->poll = depthdropper_poll;
381
382         /* flags */
383         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
384
385         /* properties */
386 }