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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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): none yet.
25 * ***** END GPL LICENSE BLOCK *****
29 #include "MEM_guardedalloc.h"
31 #include "DNA_brush_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_screen_types.h"
36 #include "RNA_access.h"
38 #include "BKE_context.h"
39 #include "BKE_paint.h"
44 #include "BLI_arithb.h"
46 #include "BIF_glutil.h"
48 #include "ED_screen.h"
49 #include "ED_view3d.h"
51 #include "paint_intern.h"
56 typedef struct PaintStroke {
62 float last_mouse_position[2];
64 /* Set whether any stroke step has yet occured
65 e.g. in sculpt mode, stroke doesn't start until cursor
66 passes over the mesh */
69 StrokeTestStart test_start;
70 StrokeUpdateStep update_step;
74 /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
75 static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
78 float cur_depth, pressure = 1;
80 PaintStroke *stroke = op->customdata;
82 cur_depth = read_cached_depth(&stroke->vc, mouse[0], mouse[1]);
83 view3d_unproject(&stroke->mats, center, mouse[0], mouse[1], cur_depth);
86 if(event->custom == EVT_DATA_TABLET) {
87 wmTabletData *wmtab= event->customdata;
88 if(wmtab->Active != EVT_TABLET_NONE)
89 pressure= wmtab->Pressure;
93 RNA_collection_add(op->ptr, "stroke", &itemptr);
94 RNA_float_set_array(&itemptr, "location", center);
95 RNA_float_set_array(&itemptr, "mouse", mouse);
96 RNA_boolean_set(&itemptr, "flip", event->shift);
97 RNA_float_set(&itemptr, "pressure", pressure);
99 stroke->last_mouse_position[0] = mouse[0];
100 stroke->last_mouse_position[1] = mouse[1];
102 stroke->update_step(C, stroke, &itemptr);
105 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
106 static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
108 output[0] = event->x;
109 output[1] = event->y;
111 if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
112 float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
113 float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
115 /* If the mouse is moving within the radius of the last move,
116 don't update the mouse position. This allows sharp turns. */
117 if(dx*dx + dy*dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius)
120 output[0] = event->x * v + stroke->last_mouse_position[0] * u;
121 output[1] = event->y * v + stroke->last_mouse_position[1] * u;
127 /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
128 static int paint_space_stroke_enabled(Brush *br)
130 return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
133 /* For brushes with stroke spacing enabled, moves mouse in steps
134 towards the final mouse location. */
135 static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
137 PaintStroke *stroke = op->customdata;
140 if(paint_space_stroke_enabled(stroke->brush)) {
141 float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
142 float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
146 /* Normalize the vector between the last stroke dot and the goal */
147 length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
149 if(length > FLT_EPSILON) {
150 scale = stroke->brush->spacing / length;
154 steps = (int)(length / stroke->brush->spacing);
155 for(i = 0; i < steps; ++i, ++cnt) {
158 paint_brush_stroke_add_step(C, op, event, mouse);
166 /**** Public API ****/
168 PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start,
169 StrokeUpdateStep update_step, StrokeDone done)
171 PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
173 stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C)));
174 view3d_set_viewcontext(C, &stroke->vc);
175 view3d_get_transformation(&stroke->vc, stroke->vc.obact, &stroke->mats);
177 stroke->test_start = test_start;
178 stroke->update_step = update_step;
184 int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
186 ARegion *ar = CTX_wm_region(C);
187 PaintStroke *stroke = op->customdata;
190 if(!stroke->stroke_started) {
191 stroke->last_mouse_position[0] = event->x;
192 stroke->last_mouse_position[1] = event->y;
193 stroke->stroke_started = stroke->test_start(C, op, event);
194 ED_region_tag_redraw(ar);
197 if(stroke->stroke_started) {
198 if(paint_smooth_stroke(stroke, mouse, event)) {
199 if(paint_space_stroke_enabled(stroke->brush)) {
200 if(!paint_space_stroke(C, op, event, mouse))
201 ED_region_tag_redraw(ar);
204 paint_brush_stroke_add_step(C, op, event, mouse);
207 ED_region_tag_redraw(ar);
210 /* TODO: fix hardcoded event here */
211 if(event->type == LEFTMOUSE && event->val == 0) {
212 stroke->done(C, stroke);
214 return OPERATOR_FINISHED;
217 return OPERATOR_RUNNING_MODAL;
220 ViewContext *paint_stroke_view_context(PaintStroke *stroke)