2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2009 by Nicholas Bishop
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Jason Wilkins, Tom Musgrove.
25 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/editors/sculpt_paint/paint_stroke.c
34 #include "MEM_guardedalloc.h"
37 #include "BLI_utildefines.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_brush_types.h"
43 #include "RNA_access.h"
45 #include "BKE_context.h"
46 #include "BKE_paint.h"
47 #include "BKE_brush.h"
53 #include "BIF_glutil.h"
55 #include "ED_screen.h"
56 #include "ED_view3d.h"
58 #include "paint_intern.h"
63 typedef struct PaintSample {
66 /* TODO: other input properties, e.g. tablet pressure */
69 typedef struct PaintStroke {
71 void *smooth_stroke_cursor;
79 /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
80 * to smooth the stroke */
81 PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
85 float last_mouse_position[2];
87 /* Set whether any stroke step has yet occurred
88 * e.g. in sculpt mode, stroke doesn't start until cursor
89 * passes over the mesh */
91 /* event that started stroke, for modal() return */
94 StrokeGetLocation get_location;
95 StrokeTestStart test_start;
96 StrokeUpdateStep update_step;
101 static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata)
103 Paint *paint = paint_get_active_from_context(C);
104 Brush *brush = paint_brush(paint);
105 PaintStroke *stroke = customdata;
107 glColor4ubv(paint->paint_cursor_col);
108 glEnable(GL_LINE_SMOOTH);
111 if (stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) {
112 ARegion *ar = CTX_wm_region(C);
113 sdrawline(x, y, (int)stroke->last_mouse_position[0] - ar->winrct.xmin,
114 (int)stroke->last_mouse_position[1] - ar->winrct.ymin);
118 glDisable(GL_LINE_SMOOTH);
121 /* if this is a tablet event, return tablet pressure and set *pen_flip
122 * to 1 if the eraser tool is being used, 0 otherwise */
123 static float event_tablet_data(wmEvent *event, int *pen_flip)
128 if (event->custom == EVT_DATA_TABLET) {
129 wmTabletData *wmtab = event->customdata;
131 erasor = (wmtab->Active == EVT_TABLET_ERASER);
132 pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
136 (*pen_flip) = erasor;
141 /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
142 static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2])
144 Scene *scene = CTX_data_scene(C);
145 Paint *paint = paint_get_active_from_context(C);
146 Brush *brush = paint_brush(paint);
147 PaintStroke *stroke = op->customdata;
154 /* see if tablet affects event */
155 pressure = event_tablet_data(event, &pen_flip);
157 /* TODO: as sculpt and other paint modes are unified, this
158 * separation will go away */
159 if (stroke->vc.obact->sculpt) {
162 BKE_brush_jitter_pos(scene, brush, mouse_in, mouse);
164 /* XXX: meh, this is round about because
165 * BKE_brush_jitter_pos isn't written in the best way to
167 if (brush->flag & BRUSH_JITTER_PRESSURE) {
168 sub_v2_v2v2(delta, mouse, mouse_in);
169 mul_v2_fl(delta, pressure);
170 add_v2_v2v2(mouse, mouse_in, delta);
174 copy_v2_v2(mouse, mouse_in);
177 /* TODO: can remove the if statement once all modes have this */
178 if (stroke->get_location)
179 stroke->get_location(C, location, mouse);
184 RNA_collection_add(op->ptr, "stroke", &itemptr);
186 RNA_float_set_array(&itemptr, "location", location);
187 RNA_float_set_array(&itemptr, "mouse", mouse);
188 RNA_boolean_set(&itemptr, "pen_flip", pen_flip);
189 RNA_float_set(&itemptr, "pressure", pressure);
191 stroke->last_mouse_position[0] = mouse[0];
192 stroke->last_mouse_position[1] = mouse[1];
194 stroke->update_step(C, stroke, &itemptr);
197 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
198 static int paint_smooth_stroke(PaintStroke *stroke, float output[2],
199 const PaintSample *sample)
201 output[0] = sample->mouse[0];
202 output[1] = sample->mouse[1];
204 if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) &&
205 !ELEM4(stroke->brush->sculpt_tool,
209 SCULPT_TOOL_SNAKE_HOOK) &&
210 !(stroke->brush->flag & BRUSH_ANCHORED) &&
211 !(stroke->brush->flag & BRUSH_RESTORE_MESH))
213 float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u;
214 float dx = stroke->last_mouse_position[0] - sample->mouse[0];
215 float dy = stroke->last_mouse_position[1] - sample->mouse[1];
217 /* If the mouse is moving within the radius of the last move,
218 * don't update the mouse position. This allows sharp turns. */
219 if (dx * dx + dy * dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius)
222 output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u;
223 output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u;
229 /* For brushes with stroke spacing enabled, moves mouse in steps
230 * towards the final mouse location. */
231 static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
233 PaintStroke *stroke = op->customdata;
236 if (paint_space_stroke_enabled(stroke->brush)) {
241 copy_v2_v2(mouse, stroke->last_mouse_position);
242 sub_v2_v2v2(vec, final_mouse, mouse);
244 length = len_v2(vec);
246 if (length > FLT_EPSILON) {
247 const Scene *scene = CTX_data_scene(C);
250 float pressure = 1.0f;
252 /* XXX mysterious :) what has 'use size' do with this here... if you don't check for it, pressure fails */
253 if (BKE_brush_use_size_pressure(scene, stroke->brush))
254 pressure = event_tablet_data(event, NULL);
256 if (pressure > FLT_EPSILON) {
257 /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
258 * causing very high step sizes, hanging blender [#32381] */
259 const float size_clamp = maxf(1.0f, BKE_brush_size_get(scene, stroke->brush) * pressure);
260 scale = (size_clamp * stroke->brush->spacing / 50.0f) / length;
261 if (scale > FLT_EPSILON) {
262 mul_v2_fl(vec, scale);
264 steps = (int)(1.0f / scale);
266 for (i = 0; i < steps; ++i, ++cnt) {
267 add_v2_v2(mouse, vec);
268 paint_brush_stroke_add_step(C, op, event, mouse);
278 /**** Public API ****/
280 PaintStroke *paint_stroke_new(bContext *C,
281 StrokeGetLocation get_location,
282 StrokeTestStart test_start,
283 StrokeUpdateStep update_step,
284 StrokeDone done, int event_type)
286 PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
288 stroke->brush = paint_brush(paint_get_active_from_context(C));
289 view3d_set_viewcontext(C, &stroke->vc);
290 view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats);
292 stroke->get_location = get_location;
293 stroke->test_start = test_start;
294 stroke->update_step = update_step;
296 stroke->event_type = event_type; /* for modal, return event */
301 void paint_stroke_data_free(struct wmOperator *op)
303 MEM_freeN(op->customdata);
304 op->customdata = NULL;
307 static void stroke_done(struct bContext *C, struct wmOperator *op)
309 struct PaintStroke *stroke = op->customdata;
311 if (stroke->stroke_started && stroke->done)
312 stroke->done(C, stroke);
315 WM_event_remove_timer(
321 if (stroke->smooth_stroke_cursor)
322 WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor);
324 paint_stroke_data_free(op);
327 /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
328 int paint_space_stroke_enabled(Brush *br)
330 return (br->flag & BRUSH_SPACE) &&
331 !(br->flag & BRUSH_ANCHORED) &&
332 !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK);
335 #define PAINT_STROKE_MODAL_CANCEL 1
337 /* called in paint_ops.c, on each regeneration of keymaps */
338 struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf)
340 static struct EnumPropertyItem modal_items[] = {
341 {PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0,
343 "Cancel and undo a stroke in progress"},
348 static const char *name = "Paint Stroke Modal";
350 struct wmKeyMap *keymap = WM_modalkeymap_get(keyconf, name);
352 /* this function is called for each spacetype, only needs to add map once */
354 keymap = WM_modalkeymap_add(keyconf, name, modal_items);
356 /* items for modal map */
357 WM_modalkeymap_add_item(
358 keymap, ESCKEY, KM_PRESS, KM_ANY, 0, PAINT_STROKE_MODAL_CANCEL);
364 static void paint_stroke_add_sample(const Paint *paint,
368 PaintSample *sample = &stroke->samples[stroke->cur_sample];
369 int max_samples = MIN2(PAINT_MAX_INPUT_SAMPLES,
370 MAX2(paint->num_input_samples, 1));
372 sample->mouse[0] = x;
373 sample->mouse[1] = y;
375 stroke->cur_sample++;
376 if (stroke->cur_sample >= max_samples)
377 stroke->cur_sample = 0;
378 if (stroke->num_samples < max_samples)
379 stroke->num_samples++;
382 static void paint_stroke_sample_average(const PaintStroke *stroke,
383 PaintSample *average)
387 memset(average, 0, sizeof(*average));
389 BLI_assert(stroke->num_samples > 0);
391 for (i = 0; i < stroke->num_samples; i++)
392 add_v2_v2(average->mouse, stroke->samples[i].mouse);
394 mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
396 /*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/
399 int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
401 Paint *p = paint_get_active_from_context(C);
402 PaintStroke *stroke = op->customdata;
403 PaintSample sample_average;
407 paint_stroke_add_sample(p, stroke, event->x, event->y);
408 paint_stroke_sample_average(stroke, &sample_average);
410 // let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
411 // this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
412 // since the 2D deltas are zero -- code in this file needs to be updated to use the
413 // post-NDOF_MOTION MOUSEMOVE
414 if (event->type == NDOF_MOTION)
415 return OPERATOR_PASS_THROUGH;
417 if (!stroke->stroke_started) {
418 copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
419 stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
421 if (stroke->stroke_started) {
422 stroke->smooth_stroke_cursor =
423 WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke);
425 if (stroke->brush->flag & BRUSH_AIRBRUSH)
426 stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate);
430 //ED_region_tag_redraw(ar);
434 if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) {
435 if (op->type->cancel)
436 return op->type->cancel(C, op);
438 return paint_stroke_cancel(C, op);
441 if (event->type == stroke->event_type && event->val == KM_RELEASE) {
443 return OPERATOR_FINISHED;
446 (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) ||
447 (event->type == TIMER && (event->customdata == stroke->timer)) )
449 if (stroke->stroke_started) {
450 if (paint_smooth_stroke(stroke, mouse, &sample_average)) {
451 if (paint_space_stroke_enabled(stroke->brush)) {
452 if (!paint_space_stroke(C, op, event, mouse)) {
453 //ED_region_tag_redraw(ar);
457 paint_brush_stroke_add_step(C, op, event, mouse);
461 ; //ED_region_tag_redraw(ar);
466 /* we want the stroke to have the first daub at the start location
467 * instead of waiting till we have moved the space distance */
469 stroke->stroke_started &&
470 paint_space_stroke_enabled(stroke->brush) &&
471 !(stroke->brush->flag & BRUSH_ANCHORED) &&
472 !(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
474 paint_brush_stroke_add_step(C, op, event, mouse);
477 return OPERATOR_RUNNING_MODAL;
480 int paint_stroke_exec(bContext *C, wmOperator *op)
482 PaintStroke *stroke = op->customdata;
484 /* only when executed for the first time */
485 if (stroke->stroke_started == 0) {
486 /* XXX stroke->last_mouse_position is unset, this may cause problems */
487 stroke->test_start(C, op, NULL);
488 stroke->stroke_started = 1;
491 RNA_BEGIN (op->ptr, itemptr, "stroke")
493 stroke->update_step(C, stroke, &itemptr);
499 return OPERATOR_FINISHED;
502 int paint_stroke_cancel(bContext *C, wmOperator *op)
505 return OPERATOR_CANCELLED;
508 ViewContext *paint_stroke_view_context(PaintStroke *stroke)
513 void *paint_stroke_mode_data(struct PaintStroke *stroke)
515 return stroke->mode_data;
518 void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
520 stroke->mode_data = mode_data;
523 int paint_poll(bContext *C)
525 Paint *p = paint_get_active_from_context(C);
526 Object *ob = CTX_data_active_object(C);
528 return p && ob && paint_brush(p) &&
529 CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
530 CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;