Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / sculpt_paint / paint_curve.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/sculpt_paint/paint_curve.c
22  *  \ingroup edsculpt
23  */
24
25 #include <string.h>
26 #include <limits.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "DNA_brush_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_view3d_types.h"
35
36 #include "BLI_math_vector.h"
37
38 #include "BKE_context.h"
39 #include "BKE_main.h"
40 #include "BKE_paint.h"
41
42 #include "DEG_depsgraph.h"
43
44 #include "ED_view3d.h"
45 #include "ED_paint.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "UI_view2d.h"
54
55 #include "paint_intern.h"
56
57 #define PAINT_CURVE_SELECT_THRESHOLD 40.0f
58 #define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT)
59
60 int paint_curve_poll(bContext *C)
61 {
62         Object *ob = CTX_data_active_object(C);
63         Paint *p;
64         RegionView3D *rv3d = CTX_wm_region_view3d(C);
65         SpaceImage *sima;
66
67         if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0)))
68                 return false;
69
70         sima = CTX_wm_space_image(C);
71
72         if (sima && sima->mode != SI_MODE_PAINT)
73                 return false;
74
75         p = BKE_paint_get_active_from_context(C);
76
77         if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) {
78                 return true;
79         }
80
81         return false;
82 }
83
84 #define SEL_F1 (1 << 0)
85 #define SEL_F2 (1 << 1)
86 #define SEL_F3 (1 << 2)
87
88 /* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */
89 static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point)
90 {
91         PaintCurvePoint *pcp, *closest = NULL;
92         int i;
93         float dist, closest_dist = FLT_MAX;
94
95         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
96                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]);
97                 if (dist < threshold) {
98                         if (dist < closest_dist) {
99                                 closest = pcp;
100                                 closest_dist = dist;
101                                 if (point)
102                                         *point = SEL_F1;
103                         }
104                 }
105                 if (!ignore_pivot) {
106                         dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]);
107                         if (dist < threshold) {
108                                 if (dist < closest_dist) {
109                                         closest = pcp;
110                                         closest_dist = dist;
111                                         if (point)
112                                                 *point = SEL_F2;
113                                 }
114                         }
115                 }
116                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]);
117                 if (dist < threshold) {
118                         if (dist < closest_dist) {
119                                 closest = pcp;
120                                 closest_dist = dist;
121                                 if (point)
122                                         *point = SEL_F3;
123                         }
124                 }
125         }
126
127         return closest;
128 }
129
130 static int paintcurve_point_co_index(char sel)
131 {
132         char i = 0;
133         while (sel != 1) {
134                 sel >>= 1;
135                 i++;
136         }
137         return i;
138 }
139
140 static char paintcurve_point_side_index(const BezTriple *bezt, const bool is_first, const char fallback)
141 {
142         /* when matching, guess based on endpoint side */
143         if (BEZT_ISSEL_ANY(bezt)) {
144                 if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) {
145                         return is_first ? SEL_F1 : SEL_F3;
146                 }
147                 else if (bezt->f1 & SELECT) {
148                         return SEL_F1;
149                 }
150                 else if (bezt->f3  & SELECT) {
151                         return SEL_F3;
152                 }
153                 else {
154                         return fallback;
155                 }
156         }
157         else {
158                 return 0;
159         }
160 }
161
162 /******************* Operators *********************************/
163
164 static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op))
165 {
166         Paint *p = BKE_paint_get_active_from_context(C);
167         Main *bmain = CTX_data_main(C);
168
169         if (p && p->brush) {
170                 p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve");
171         }
172
173         return OPERATOR_FINISHED;
174 }
175
176 void PAINTCURVE_OT_new(wmOperatorType *ot)
177 {
178         /* identifiers */
179         ot->name = "Add New Paint Curve";
180         ot->description = "Add new paint curve";
181         ot->idname = "PAINTCURVE_OT_new";
182
183         /* api callbacks */
184         ot->exec = paintcurve_new_exec;
185         ot->poll = paint_curve_poll;
186
187         /* flags */
188         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
189 }
190
191 static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
192 {
193         Paint *p = BKE_paint_get_active_from_context(C);
194         Brush *br = p->brush;
195         Main *bmain = CTX_data_main(C);
196         PaintCurve *pc;
197         PaintCurvePoint *pcp;
198         wmWindow *window = CTX_wm_window(C);
199         ARegion *ar = CTX_wm_region(C);
200         float vec[3] = {loc[0], loc[1], 0.0};
201         int add_index;
202         int i;
203
204         pc = br->paint_curve;
205         if (!pc) {
206                 br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
207         }
208
209         ED_paintcurve_undo_push_begin(op->type->name);
210
211         pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
212         add_index = pc->add_index;
213
214         if (pc->points) {
215                 if (add_index > 0)
216                         memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint));
217                 if (add_index < pc->tot_points)
218                         memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint));
219
220                 MEM_freeN(pc->points);
221         }
222         pc->points = pcp;
223         pc->tot_points++;
224
225         /* initialize new point */
226         memset(&pcp[add_index], 0, sizeof(PaintCurvePoint));
227         copy_v3_v3(pcp[add_index].bez.vec[0], vec);
228         copy_v3_v3(pcp[add_index].bez.vec[1], vec);
229         copy_v3_v3(pcp[add_index].bez.vec[2], vec);
230
231         /* last step, clear selection from all bezier handles expect the next */
232         for (i = 0; i < pc->tot_points; i++) {
233                 pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0;
234         }
235
236         BKE_paint_curve_clamp_endpoint_add_index(pc, add_index);
237
238         if (pc->add_index != 0) {
239                 pcp[add_index].bez.f3 = SELECT;
240                 pcp[add_index].bez.h2 = HD_ALIGN;
241         }
242         else {
243                 pcp[add_index].bez.f1 = SELECT;
244                 pcp[add_index].bez.h1 = HD_ALIGN;
245         }
246
247         ED_paintcurve_undo_push_end();
248
249         WM_paint_cursor_tag_redraw(window, ar);
250 }
251
252
253 static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
254 {
255         int loc[2] = {event->mval[0], event->mval[1]};
256         paintcurve_point_add(C, op, loc);
257         RNA_int_set_array(op->ptr, "location", loc);
258         return OPERATOR_FINISHED;
259 }
260
261 static int paintcurve_add_point_exec(bContext *C, wmOperator *op)
262 {
263         int loc[2];
264
265         if (RNA_struct_property_is_set(op->ptr, "location")) {
266                 RNA_int_get_array(op->ptr, "location", loc);
267                 paintcurve_point_add(C, op, loc);
268                 return OPERATOR_FINISHED;
269         }
270
271         return OPERATOR_CANCELLED;
272 }
273
274 void PAINTCURVE_OT_add_point(wmOperatorType *ot)
275 {
276         /* identifiers */
277         ot->name = "Add New Paint Curve Point";
278         ot->description = ot->name;
279         ot->idname = "PAINTCURVE_OT_add_point";
280
281         /* api callbacks */
282         ot->invoke = paintcurve_add_point_invoke;
283         ot->exec = paintcurve_add_point_exec;
284         ot->poll = paint_curve_poll;
285
286         /* flags */
287         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
288
289         /* properties */
290         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
291                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
292 }
293
294 static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
295 {
296         Paint *p = BKE_paint_get_active_from_context(C);
297         Brush *br = p->brush;
298         PaintCurve *pc;
299         PaintCurvePoint *pcp;
300         wmWindow *window = CTX_wm_window(C);
301         ARegion *ar = CTX_wm_region(C);
302         int i;
303         int tot_del = 0;
304         pc = br->paint_curve;
305
306         if (!pc || pc->tot_points == 0) {
307                 return OPERATOR_CANCELLED;
308         }
309
310         ED_paintcurve_undo_push_begin(op->type->name);
311
312 #define DELETE_TAG 2
313
314         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
315                 if (BEZT_ISSEL_ANY(&pcp->bez)) {
316                         pcp->bez.f2 |= DELETE_TAG;
317                         tot_del++;
318                 }
319         }
320
321         if (tot_del > 0) {
322                 int j = 0;
323                 int new_tot = pc->tot_points - tot_del;
324                 PaintCurvePoint *points_new = NULL;
325                 if (new_tot > 0)
326                         points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint");
327
328                 for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
329                         if (!(pcp->bez.f2 & DELETE_TAG)) {
330                                 points_new[j] = pc->points[i];
331
332                                 if ((i + 1) == pc->add_index) {
333                                         BKE_paint_curve_clamp_endpoint_add_index(pc, j);
334                                 }
335                                 j++;
336                         }
337                         else if ((i + 1) == pc->add_index) {
338                                 /* prefer previous point */
339                                 pc->add_index = j;
340                         }
341                 }
342                 MEM_freeN(pc->points);
343
344                 pc->points = points_new;
345                 pc->tot_points = new_tot;
346         }
347
348 #undef DELETE_TAG
349
350         ED_paintcurve_undo_push_end();
351
352         WM_paint_cursor_tag_redraw(window, ar);
353
354         return OPERATOR_FINISHED;
355 }
356
357
358 void PAINTCURVE_OT_delete_point(wmOperatorType *ot)
359 {
360         /* identifiers */
361         ot->name = "Remove Paint Curve Point";
362         ot->description = ot->name;
363         ot->idname = "PAINTCURVE_OT_delete_point";
364
365         /* api callbacks */
366         ot->exec = paintcurve_delete_point_exec;
367         ot->poll = paint_curve_poll;
368
369         /* flags */
370         ot->flag = OPTYPE_UNDO;
371 }
372
373
374 static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend)
375 {
376         wmWindow *window = CTX_wm_window(C);
377         ARegion *ar = CTX_wm_region(C);
378         Paint *p = BKE_paint_get_active_from_context(C);
379         Brush *br = p->brush;
380         PaintCurve *pc;
381         int i;
382         const float loc_fl[2] = {UNPACK2(loc)};
383
384         pc = br->paint_curve;
385
386         if (!pc)
387                 return false;
388
389         ED_paintcurve_undo_push_begin(op->type->name);
390
391         if (toggle) {
392                 PaintCurvePoint *pcp;
393                 char select = 0;
394                 bool selected = false;
395
396                 pcp = pc->points;
397
398                 for (i = 0; i < pc->tot_points; i++) {
399                         if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) {
400                                 selected = true;
401                                 break;
402                         }
403                 }
404
405                 if (!selected) {
406                         select = SELECT;
407                 }
408
409                 for (i = 0; i < pc->tot_points; i++) {
410                         pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select;
411                 }
412         }
413         else {
414                 PaintCurvePoint *pcp;
415                 char selflag;
416
417                 pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag);
418
419                 if (pcp) {
420                         BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points);
421
422                         if (selflag == SEL_F2) {
423                                 if (extend)
424                                         pcp->bez.f2 ^= SELECT;
425                                 else
426                                         pcp->bez.f2 |= SELECT;
427                         }
428                         else if (selflag == SEL_F1) {
429                                 if (extend)
430                                         pcp->bez.f1 ^= SELECT;
431                                 else
432                                         pcp->bez.f1 |= SELECT;
433                         }
434                         else if (selflag == SEL_F3) {
435                                 if (extend)
436                                         pcp->bez.f3 ^= SELECT;
437                                 else
438                                         pcp->bez.f3 |= SELECT;
439                         }
440                 }
441
442                 /* clear selection for unselected points if not extending and if a point has been selected */
443                 if (!extend && pcp) {
444                         for (i = 0; i < pc->tot_points; i++) {
445                                 pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0;
446
447                                 if ((pc->points + i) == pcp) {
448                                         char index = paintcurve_point_co_index(selflag);
449                                         PAINT_CURVE_POINT_SELECT(pcp, index);
450                                 }
451                         }
452                 }
453
454                 if (!pcp) {
455                         ED_paintcurve_undo_push_end();
456                         return false;
457                 }
458         }
459
460         ED_paintcurve_undo_push_end();
461
462         WM_paint_cursor_tag_redraw(window, ar);
463
464         return true;
465 }
466
467
468 static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
469 {
470         int loc[2] = {UNPACK2(event->mval)};
471         bool toggle = RNA_boolean_get(op->ptr, "toggle");
472         bool extend = RNA_boolean_get(op->ptr, "extend");
473         if (paintcurve_point_select(C, op, loc, toggle, extend)) {
474                 RNA_int_set_array(op->ptr, "location", loc);
475                 return OPERATOR_FINISHED;
476         }
477         else {
478                 return OPERATOR_CANCELLED;
479         }
480 }
481
482 static int paintcurve_select_point_exec(bContext *C, wmOperator *op)
483 {
484         int loc[2];
485
486         if (RNA_struct_property_is_set(op->ptr, "location")) {
487                 bool toggle = RNA_boolean_get(op->ptr, "toggle");
488                 bool extend = RNA_boolean_get(op->ptr, "extend");
489                 RNA_int_get_array(op->ptr, "location", loc);
490                 if (paintcurve_point_select(C, op, loc, toggle, extend))
491                         return OPERATOR_FINISHED;
492         }
493
494         return OPERATOR_CANCELLED;
495 }
496
497 void PAINTCURVE_OT_select(wmOperatorType *ot)
498 {
499         PropertyRNA *prop;
500
501         /* identifiers */
502         ot->name = "Select Paint Curve Point";
503         ot->description = "Select a paint curve point";
504         ot->idname = "PAINTCURVE_OT_select";
505
506         /* api callbacks */
507         ot->invoke = paintcurve_select_point_invoke;
508         ot->exec = paintcurve_select_point_exec;
509         ot->poll = paint_curve_poll;
510
511         /* flags */
512         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
513
514         /* properties */
515         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
516                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
517         prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "(De)select all");
518         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
519         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
520         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
521 }
522
523 typedef struct PointSlideData {
524         PaintCurvePoint *pcp;
525         char select;
526         int initial_loc[2];
527         float point_initial_loc[3][2];
528         int event;
529         bool align;
530 } PointSlideData;
531
532 static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event)
533 {
534         Paint *p = BKE_paint_get_active_from_context(C);
535         const float loc_fl[2] = {UNPACK2(event->mval)};
536         char select;
537         int i;
538         bool do_select = RNA_boolean_get(op->ptr, "select");
539         bool align = RNA_boolean_get(op->ptr, "align");
540         Brush *br = p->brush;
541         PaintCurve *pc = br->paint_curve;
542         PaintCurvePoint *pcp;
543
544         if (!pc)
545                 return OPERATOR_PASS_THROUGH;
546
547         if (do_select) {
548                 pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select);
549         }
550         else {
551                 pcp = NULL;
552                 /* just find first selected point */
553                 for (i = 0; i < pc->tot_points; i++) {
554                         if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) {
555                                 pcp = &pc->points[i];
556                                 break;
557                         }
558                 }
559         }
560
561
562         if (pcp) {
563                 ARegion *ar = CTX_wm_region(C);
564                 wmWindow *window = CTX_wm_window(C);
565                 PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData");
566                 copy_v2_v2_int(psd->initial_loc, event->mval);
567                 psd->event = event->type;
568                 psd->pcp = pcp;
569                 psd->select = paintcurve_point_co_index(select);
570                 for (i = 0; i < 3; i++) {
571                         copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]);
572                 }
573                 psd->align = align;
574                 op->customdata = psd;
575
576                 /* first, clear all selection from points */
577                 for (i = 0; i < pc->tot_points; i++)
578                         pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
579
580                 /* only select the active point */
581                 PAINT_CURVE_POINT_SELECT(pcp, psd->select);
582                 BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points);
583
584                 WM_event_add_modal_handler(C, op);
585                 WM_paint_cursor_tag_redraw(window, ar);
586                 return OPERATOR_RUNNING_MODAL;
587         }
588
589         return OPERATOR_PASS_THROUGH;
590 }
591
592 static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
593 {
594         PointSlideData *psd = op->customdata;
595
596         if (event->type == psd->event && event->val == KM_RELEASE) {
597                 MEM_freeN(psd);
598                 ED_paintcurve_undo_push_begin(op->type->name);
599                 ED_paintcurve_undo_push_end();
600                 return OPERATOR_FINISHED;
601         }
602
603         switch (event->type) {
604                 case MOUSEMOVE:
605                 {
606                         ARegion *ar = CTX_wm_region(C);
607                         wmWindow *window = CTX_wm_window(C);
608                         float diff[2] = {event->mval[0] - psd->initial_loc[0],
609                                          event->mval[1] - psd->initial_loc[1]};
610                         if (psd->select == 1) {
611                                 int i;
612                                 for (i = 0; i < 3; i++)
613                                         add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]);
614                         }
615                         else {
616                                 add_v2_v2(diff, psd->point_initial_loc[psd->select]);
617                                 copy_v2_v2(psd->pcp->bez.vec[psd->select], diff);
618
619                                 if (psd->align) {
620                                         char opposite = (psd->select == 0) ? 2 : 0;
621                                         sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]);
622                                         add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff);
623                                 }
624                         }
625                         WM_paint_cursor_tag_redraw(window, ar);
626                         break;
627                 }
628                 default:
629                         break;
630         }
631
632         return OPERATOR_RUNNING_MODAL;
633 }
634
635
636 void PAINTCURVE_OT_slide(wmOperatorType *ot)
637 {
638         /* identifiers */
639         ot->name = "Slide Paint Curve Point";
640         ot->description = "Select and slide paint curve point";
641         ot->idname = "PAINTCURVE_OT_slide";
642
643         /* api callbacks */
644         ot->invoke = paintcurve_slide_invoke;
645         ot->modal = paintcurve_slide_modal;
646         ot->poll = paint_curve_poll;
647
648         /* flags */
649         ot->flag = OPTYPE_UNDO;
650
651         /* properties */
652         RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform");
653         RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform");
654 }
655
656 static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op))
657 {
658         ePaintMode mode = BKE_paintmode_get_active_from_context(C);
659         const char *name;
660
661         switch (mode) {
662                 case ePaintTexture2D:
663                 case ePaintTextureProjective:
664                         name = "PAINT_OT_image_paint";
665                         break;
666                 case ePaintWeight:
667                         name = "PAINT_OT_weight_paint";
668                         break;
669                 case ePaintVertex:
670                         name = "PAINT_OT_vertex_paint";
671                         break;
672                 case ePaintSculpt:
673                         name = "SCULPT_OT_brush_stroke";
674                         break;
675                 default:
676                         return OPERATOR_PASS_THROUGH;
677         }
678
679         return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL);
680 }
681
682 void PAINTCURVE_OT_draw(wmOperatorType *ot)
683 {
684         /* identifiers */
685         ot->name = "Draw Curve";
686         ot->description = "Draw curve";
687         ot->idname = "PAINTCURVE_OT_draw";
688
689         /* api callbacks */
690         ot->exec = paintcurve_draw_exec;
691         ot->poll = paint_curve_poll;
692
693         /* flags */
694         ot->flag = OPTYPE_UNDO;
695 }
696
697 static int paintcurve_cursor_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
698 {
699         ePaintMode mode = BKE_paintmode_get_active_from_context(C);
700
701         switch (mode) {
702                 case ePaintTexture2D:
703                 {
704                         ARegion *ar = CTX_wm_region(C);
705                         SpaceImage *sima = CTX_wm_space_image(C);
706                         float location[2];
707
708                         if (!sima)
709                                 return OPERATOR_CANCELLED;
710
711                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
712                         copy_v2_v2(sima->cursor, location);
713                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
714                         break;
715                 }
716                 default:
717                         ED_view3d_cursor3d_update(C, event->mval);
718                         break;
719         }
720
721         return OPERATOR_FINISHED;
722 }
723
724 void PAINTCURVE_OT_cursor(wmOperatorType *ot)
725 {
726         /* identifiers */
727         ot->name = "Place Cursor";
728         ot->description = "Place cursor";
729         ot->idname = "PAINTCURVE_OT_cursor";
730
731         /* api callbacks */
732         ot->invoke = paintcurve_cursor_invoke;
733         ot->poll = paint_curve_poll;
734
735         /* flags */
736         ot->flag = 0;
737 }