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) 2017, Blender Foundation
19 * This is a new part of Blender
21 * Contributor(s): Antonio Vazquez
23 * ***** END GPL LICENSE BLOCK *****
25 * Operators for creating new Grease Pencil primitives (boxes, circles, ...)
28 /** \file blender/editors/gpencil/gpencil_primitive.c
39 #include "MEM_guardedalloc.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_utildefines.h"
46 #include "BLT_translation.h"
50 #include "DNA_brush_types.h"
51 #include "DNA_gpencil_types.h"
52 #include "DNA_meshdata_types.h"
53 #include "DNA_object_types.h"
54 #include "DNA_scene_types.h"
55 #include "DNA_screen_types.h"
56 #include "DNA_space_types.h"
57 #include "DNA_view3d_types.h"
59 #include "BKE_brush.h"
60 #include "BKE_colortools.h"
61 #include "BKE_context.h"
62 #include "BKE_deform.h"
63 #include "BKE_global.h"
64 #include "BKE_gpencil.h"
66 #include "BKE_material.h"
67 #include "BKE_paint.h"
68 #include "BKE_report.h"
70 #include "UI_interface.h"
71 #include "UI_resources.h"
76 #include "RNA_access.h"
77 #include "RNA_define.h"
78 #include "RNA_enum_types.h"
80 #include "ED_gpencil.h"
81 #include "ED_object.h"
82 #include "ED_screen.h"
83 #include "ED_view3d.h"
84 #include "ED_space_api.h"
86 #include "DEG_depsgraph.h"
87 #include "DEG_depsgraph_query.h"
89 #include "gpencil_intern.h"
97 #define IN_CURVE_EDIT 2
100 #define SELECT_NONE 0
101 #define SELECT_START 1
106 #define BIG_SIZE_CTL 15
107 #define MID_SIZE_CTL 10
108 #define SMALL_SIZE_CTL 8
114 /* ************************************************ */
115 /* Core/Shared Utilities */
117 /* clear the session buffers (call this before AND after a paint operation) */
118 static void gp_session_validatebuffer(tGPDprimitive *p)
120 bGPdata *gpd = p->gpd;
122 /* clear memory of buffer (or allocate it if starting a new session) */
123 if (gpd->runtime.sbuffer) {
124 memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX);
127 gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
131 gpd->runtime.sbuffer_size = 0;
134 gpd->runtime.sbuffer_sflag = 0;
135 gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE;
137 if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE))
138 gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC;
141 static void gp_init_colors(tGPDprimitive *p)
143 bGPdata *gpd = p->gpd;
144 Brush *brush = p->brush;
147 MaterialGPencilStyle *gp_style = NULL;
149 /* use brush material */
150 ma = BKE_gpencil_get_material_from_brush(brush);
152 /* if no brush defaults, get material and color info */
153 if ((ma == NULL) || (ma->gp_style == NULL)) {
154 BKE_gpencil_material_ensure(p->bmain, p->ob);
156 /* assign always the first material to the brush */
157 p->mat = give_current_material(p->ob, 1);
158 brush->gpencil_settings->material = p->mat;
164 /* check if the material is already on object material slots and add it if missing */
165 if (BKE_gpencil_get_material_index(p->ob, p->mat) == 0) {
166 BKE_object_material_slot_add(p->bmain, p->ob);
167 assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_USERPREF);
170 /* assign color information to temp data */
171 gp_style = p->mat->gp_style;
175 copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
176 copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
177 /* add some alpha to make easy the filling without hide strokes */
178 if (gpd->runtime.sfill[3] > 0.8f) {
179 gpd->runtime.sfill[3] = 0.8f;
182 gpd->runtime.mode = (short)gp_style->mode;
183 gpd->runtime.bstroke_style = gp_style->stroke_style;
184 gpd->runtime.bfill_style = gp_style->fill_style;
188 /* Helper to square a primitive */
189 static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, const float y)
193 if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
195 tgpi->end[1] = tgpi->origin[1] + x;
197 tgpi->end[0] = tgpi->origin[0] + y;
201 tgpi->end[1] = tgpi->origin[1] - x;
203 tgpi->end[0] = tgpi->origin[0] - y;
207 /* Helper to rotate point around origin */
208 static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle)
212 sub_v2_v2v2(pt, p, origin);
213 rotate_v2_v2fl(r, pt, angle);
214 add_v2_v2v2(v, r, origin);
217 /* Helper to rotate line around line centre */
218 static void gp_primitive_rotate_line(float va[2], float vb[2], const float a[2], const float b[2], const float angle)
221 mid_v2_v2v2(midpoint, a, b);
222 gp_rotate_v2_v2v2fl(va, a, midpoint, angle);
223 gp_rotate_v2_v2v2fl(vb, b, midpoint, angle);
226 /* Helper to update cps */
227 static void gp_primitive_update_cps(tGPDprimitive *tgpi)
230 mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
231 copy_v2_v2(tgpi->cp1, tgpi->midpoint);
232 copy_v2_v2(tgpi->cp2, tgpi->cp1);
234 else if (tgpi->type == GP_STROKE_CURVE) {
235 mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
236 copy_v2_v2(tgpi->cp1, tgpi->midpoint);
237 copy_v2_v2(tgpi->cp2, tgpi->cp1);
239 else if (tgpi->type == GP_STROKE_ARC) {
241 gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
244 gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
249 /* Poll callback for primitive operators */
250 static bool gpencil_primitive_add_poll(bContext *C)
253 ScrArea *sa = CTX_wm_area(C);
254 if (sa && sa->spacetype != SPACE_VIEW3D) {
258 /* need data to create primitive */
259 bGPdata *gpd = CTX_data_gpencil_data(C);
264 /* only in edit and paint modes
265 * - paint as it's the "drawing/creation mode"
266 * - edit as this is more of an atomic editing operation
267 * (similar to copy/paste), and also for consistency
269 if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) {
270 CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes");
274 /* don't allow operator to function if the active layer is locked/hidden
275 * (BUT, if there isn't an active layer, we are free to add new layer when the time comes)
277 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
278 if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
279 CTX_wm_operator_poll_msg_set(C, "Primitives cannot be added as active layer is locked or hidden");
286 /* Allocate memory to stroke, adds MAX_EDGES on every call */
287 static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi)
289 tgpi->point_count += (tgpi->type == GP_STROKE_BOX) ? (MAX_EDGES * 4 + 1) : (MAX_EDGES + 1);
290 bGPDstroke *gpsf = tgpi->gpf->strokes.first;
291 gpsf->points = MEM_reallocN(gpsf->points, sizeof(bGPDspoint) * tgpi->point_count);
292 if (gpsf->dvert != NULL)
293 gpsf->dvert = MEM_reallocN(gpsf->dvert, sizeof(MDeformVert) * tgpi->point_count);
294 tgpi->points = MEM_reallocN(tgpi->points, sizeof(tGPspoint) * tgpi->point_count);
297 /* ****************** Primitive Interactive *********************** */
299 /* Helper: Create internal strokes primitives data */
300 static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
302 ToolSettings *ts = CTX_data_tool_settings(C);
303 Depsgraph *depsgraph = CTX_data_depsgraph(C);
304 int cfra_eval = (int)DEG_get_ctime(depsgraph);
306 bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
308 /* if brush doesn't exist, create a new one */
309 Paint *paint = &ts->gp_paint->paint;
310 /* if not exist, create a new one */
311 if (paint->brush == NULL) {
312 /* create new brushes */
313 BKE_brush_gpencil_presets(C);
315 tgpi->brush = paint->brush;
317 /* if layer doesn't exist, create a new one */
319 gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
323 /* create a new temporary frame */
324 tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe");
325 tgpi->gpf->framenum = tgpi->cframe = cfra_eval;
327 /* create new temp stroke */
328 bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke");
329 gps->thickness = 2.0f;
330 gps->inittime = 0.0f;
332 /* enable recalculation flag by default */
333 gps->flag |= GP_STROKE_RECALC_CACHES;
334 gps->flag &= ~GP_STROKE_SELECT;
335 /* the polygon must be closed, so enabled cyclic */
336 if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
337 gps->flag |= GP_STROKE_CYCLIC;
340 gps->flag |= GP_STROKE_3DSPACE;
342 gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat) - 1;
344 /* allocate memory for storage points, but keep empty */
346 gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
347 /* initialize triangle memory to dummy data */
348 gps->tot_triangles = 0;
349 gps->triangles = NULL;
350 gps->flag |= GP_STROKE_RECALC_CACHES;
353 BLI_addtail(&tgpi->gpf->strokes, gps);
355 /* allocate memory for storage points */
356 gpencil_primitive_allocate_memory(tgpi);
358 /* Random generator, only init once. */
359 uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
360 tgpi->rng = BLI_rng_new(rng_seed);
363 /* add new segment to curve */
364 static void gpencil_primitive_add_segment(tGPDprimitive *tgpi)
366 if (tgpi->tot_stored_edges > 0) {
367 tgpi->tot_stored_edges += (tgpi->tot_edges - 1);
370 tgpi->tot_stored_edges += tgpi->tot_edges;
372 gpencil_primitive_allocate_memory(tgpi);
375 /* Helper: set control point */
376 static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
378 bGPDcontrolpoint *cp_points = tgpi->gpd->runtime.cp_points;
380 if (tgpi->gpd->runtime.tot_cp_points < MAX_CP) {
382 bGPDcontrolpoint *cp = &cp_points[tgpi->gpd->runtime.tot_cp_points];
383 copy_v2_v2(&cp->x, p);
384 copy_v4_v4(cp->color, color);
387 tgpi->gpd->runtime.tot_cp_points += 1;
391 /* Helper: Draw status message while the user is running the operator */
392 static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi)
394 Scene *scene = tgpi->scene;
395 char status_str[UI_MAX_DRAW_STR];
396 char msg_str[UI_MAX_DRAW_STR];
398 if (tgpi->type == GP_STROKE_LINE) {
399 BLI_strncpy(msg_str, IFACE_("Line: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to align, Alt to center"), UI_MAX_DRAW_STR);
401 else if (tgpi->type == GP_STROKE_BOX) {
402 BLI_strncpy(msg_str, IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
404 else if (tgpi->type == GP_STROKE_CIRCLE) {
405 BLI_strncpy(msg_str, IFACE_("Circle: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
407 else if (tgpi->type == GP_STROKE_ARC) {
408 BLI_strncpy(msg_str, IFACE_("Arc: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, M: Flip"), UI_MAX_DRAW_STR);
410 else if (tgpi->type == GP_STROKE_CURVE) {
411 BLI_strncpy(msg_str, IFACE_("Curve: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, E: extrude"), UI_MAX_DRAW_STR);
414 if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) {
415 if (hasNumInput(&tgpi->num)) {
416 char str_offs[NUM_STR_REP_LEN];
418 outputNumInput(&tgpi->num, str_offs, &scene->unit);
419 BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs);
422 if (tgpi->flag == IN_PROGRESS) {
424 status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, tgpi->tot_edges,
425 (int)tgpi->start[0], (int)tgpi->start[1], (int)tgpi->end[0], (int)tgpi->end[1]);
429 status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, tgpi->tot_edges,
430 (int)tgpi->end[0], (int)tgpi->end[1]);
435 if (tgpi->flag == IN_PROGRESS) {
437 status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, tgpi->tot_edges,
438 (int)tgpi->start[0], (int)tgpi->start[1], (int)tgpi->end[0], (int)tgpi->end[1]);
442 status_str, sizeof(status_str), "%s: (%d, %d)", msg_str,
443 (int)tgpi->end[0], (int)tgpi->end[1]);
446 ED_workspace_status_text(C, status_str);
449 /* create a rectangle */
450 static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
454 coords[0][0] = tgpi->start[0];
455 coords[0][1] = tgpi->start[1];
456 coords[1][0] = tgpi->end[0];
457 coords[1][1] = tgpi->start[1];
458 coords[2][0] = tgpi->end[0];
459 coords[2][1] = tgpi->end[1];
460 coords[3][0] = tgpi->start[0];
461 coords[3][1] = tgpi->end[1];
462 coords[4][0] = tgpi->start[0];
463 coords[4][1] = tgpi->start[1];
465 const float step = 1.0f / (float)(tgpi->tot_edges);
466 int i = tgpi->tot_stored_edges;
468 for (int j = 0; j < 4; j++) {
470 for (int k = 0; k < tgpi->tot_edges; k++) {
471 tGPspoint *p2d = &points2D[i];
472 interp_v2_v2v2(&p2d->x, coords[j], coords[j + 1], a);
478 mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
480 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
481 gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
482 if (tgpi->tot_stored_edges) {
483 UI_GetThemeColor4fv(TH_REDALERT, color);
484 gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
487 gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
489 UI_GetThemeColor4fv(TH_REDALERT, color);
490 gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
494 static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D)
496 if (tgpi->tot_edges == 2) {
497 int i = tgpi->tot_stored_edges;
499 points2D[i].x = tgpi->start[0];
500 points2D[i].y = tgpi->start[1];
502 points2D[i + 1].x = tgpi->end[0];
503 points2D[i + 1].y = tgpi->end[1];
506 const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
507 const float step = 1.0f / (float)(tgpi->tot_edges - 1);
508 float a = tgpi->tot_stored_edges ? step : 0.0f;
510 for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
511 tGPspoint *p2d = &points2D[i];
512 interp_v2_v2v2(&p2d->x, tgpi->start, tgpi->end, a);
517 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
518 gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
519 if (tgpi->tot_stored_edges) {
520 UI_GetThemeColor4fv(TH_REDALERT, color);
521 gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
524 gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
529 static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
531 const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
532 const float step = M_PI_2 / (float)(tgpi->tot_edges - 1);
538 float a = tgpi->tot_stored_edges ? step : 0.0f;
540 mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
541 copy_v2_v2(start, tgpi->start);
542 copy_v2_v2(end, tgpi->end);
543 copy_v2_v2(cp1, tgpi->cp1);
544 copy_v2_v2(midpoint, tgpi->midpoint);
546 corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
547 corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
549 for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
550 tGPspoint *p2d = &points2D[i];
551 p2d->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
552 p2d->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
556 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
557 gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
558 if (tgpi->tot_stored_edges) {
559 UI_GetThemeColor4fv(TH_REDALERT, color);
560 gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
563 gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
565 UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
566 gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
569 /* create a bezier */
570 static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
572 const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
573 const float step = 1.0f / (float)(tgpi->tot_edges - 1);
578 float a = tgpi->tot_stored_edges ? step : 0.0f;
580 copy_v2_v2(bcp1, tgpi->start);
581 copy_v2_v2(bcp2, tgpi->cp1);
582 copy_v2_v2(bcp3, tgpi->cp2);
583 copy_v2_v2(bcp4, tgpi->end);
585 for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
586 tGPspoint *p2d = &points2D[i];
587 interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a);
591 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
592 gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
593 if (tgpi->tot_stored_edges) {
594 UI_GetThemeColor4fv(TH_REDALERT, color);
595 gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
598 gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
600 UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
601 gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
602 gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
605 /* create a circle */
606 static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
608 const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
609 const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges);
614 center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f);
615 center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f);
616 radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f));
617 radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f));
619 for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
620 tGPspoint *p2d = &points2D[i];
621 p2d->x = (center[0] + cosf(a) * radius[0]);
622 p2d->y = (center[1] + sinf(a) * radius[1]);
626 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
627 gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
628 gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
629 UI_GetThemeColor4fv(TH_REDALERT, color);
630 gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
633 /* Helper: Update shape of the stroke */
634 static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
636 ToolSettings *ts = tgpi->scene->toolsettings;
637 bGPdata *gpd = tgpi->gpd;
638 Brush *brush = tgpi->brush;
639 bGPDstroke *gps = tgpi->gpf->strokes.first;
640 GP_Sculpt_Settings *gset = &ts->gp_sculpt;
641 int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
642 char *align_flag = &ts->gpencil_v3d_align;
643 bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
645 if (tgpi->type == GP_STROKE_BOX)
646 gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges);
648 gps->totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
650 if (tgpi->tot_stored_edges)
653 tgpi->gpd->runtime.tot_cp_points = 0;
655 /* compute screen-space coordinates for points */
656 tGPspoint *points2D = tgpi->points;
658 switch (tgpi->type) {
660 gp_primitive_rectangle(tgpi, points2D);
663 gp_primitive_line(tgpi, points2D);
665 case GP_STROKE_CIRCLE:
666 gp_primitive_circle(tgpi, points2D);
669 gp_primitive_arc(tgpi, points2D);
671 case GP_STROKE_CURVE:
672 gp_primitive_bezier(tgpi, points2D);
677 /* convert screen-coordinates to 3D coordinates */
678 gp_session_validatebuffer(tgpi);
679 gp_init_colors(tgpi);
680 if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
681 curvemapping_initialize(ts->gp_sculpt.cur_primitive);
683 if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
684 curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter);
686 if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
687 curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength);
690 /* get an array of depths, far depths are blended */
691 float *depth_arr = NULL;
694 int mval_i[2], mval_prev[2] = { 0 };
695 bool interp_depth = false;
696 bool found_depth = false;
698 /* need to restore the original projection settings before packing up */
699 view3d_region_operator_needs_opengl(tgpi->win, tgpi->ar);
700 ED_view3d_autodist_init(
701 tgpi->depsgraph, tgpi->ar, tgpi->v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
703 depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
704 tGPspoint *ptc = &points2D[0];
705 for (i = 0; i < gps->totpoints; i++, ptc++) {
706 round_v2i_v2fl(mval_i, &ptc->x);
707 if ((ED_view3d_autodist_depth(
708 tgpi->ar, mval_i, depth_margin, depth_arr + i) == 0) &&
709 (i && (ED_view3d_autodist_depth_seg(
710 tgpi->ar, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0)))
717 copy_v2_v2_int(mval_prev, mval_i);
721 for (i = 0; i < gps->totpoints; i++) {
722 depth_arr[i] = 0.9999f;
726 /* if all depth are too high disable */
727 bool valid_depth = false;
728 for (i = 0; i < gps->totpoints; i++) {
729 if (depth_arr[i] < 0.9999f) {
735 MEM_SAFE_FREE(depth_arr);
739 if ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) ||
740 (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST))
745 /* find first valid contact point */
746 for (i = 0; i < gps->totpoints; i++) {
747 if (depth_arr[i] != FLT_MAX)
752 /* find last valid contact point */
753 if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST) {
754 last_valid = first_valid;
757 for (i = gps->totpoints - 1; i >= 0; i--) {
758 if (depth_arr[i] != FLT_MAX)
764 /* invalidate any other point, to interpolate between
765 * first and last contact in an imaginary line between them */
766 for (i = 0; i < gps->totpoints; i++) {
767 if ((i != first_valid) && (i != last_valid)) {
768 depth_arr[i] = FLT_MAX;
775 interp_sparse_array(depth_arr, gps->totpoints, FLT_MAX);
781 /* load stroke points and sbuffer */
782 for (int i = 0; i < gps->totpoints; i++) {
783 bGPDspoint *pt = &gps->points[i];
784 tGPspoint *p2d = &points2D[i];
786 /* set rnd value for reuse */
787 if (p2d->rnd_dirty != true) {
788 p2d->rnd[0] = BLI_rng_get_float(tgpi->rng);
789 p2d->rnd[1] = BLI_rng_get_float(tgpi->rng);
790 p2d->rnd_dirty = true;
793 /* Copy points to buffer */
794 tGPspoint *tpt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size);
796 /* Store original points */
798 copy_v2_v2(tmp_xyp, &p2d->x);
801 float curve_pressure = 1.0;
802 float pressure = 1.0;
803 float strength = brush->gpencil_settings->draw_strength;
805 /* normalize value to evaluate curve */
806 if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
807 float value = (float)i / (gps->totpoints - 1);
808 curve_pressure = curvemapping_evaluateF(gset->cur_primitive, 0, value);
809 pressure = curve_pressure;
812 /* apply jitter to position */
813 if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
814 (brush->gpencil_settings->draw_jitter > 0.0f))
818 if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
819 jitter = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, curve_pressure);
820 jitter *= brush->gpencil_settings->draw_sensitivity;
823 jitter = brush->gpencil_settings->draw_jitter;
826 /* exponential value */
827 const float exfactor = SQUARE(brush->gpencil_settings->draw_jitter + 2.0f);
828 const float fac = p2d->rnd[0] * exfactor * jitter;
829 if (p2d->rnd[0] > 0.5f) {
830 add_v2_fl(&p2d->x, -fac);
833 add_v2_fl(&p2d->x, fac);
837 /* apply randomness to pressure */
838 if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
839 (brush->gpencil_settings->draw_random_press > 0.0f))
841 if (p2d->rnd[0] > 0.5f) {
842 pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[0];
845 pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[0];
850 if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
851 float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, curve_pressure);
852 strength *= curvef * brush->gpencil_settings->draw_sensitivity;
853 strength *= brush->gpencil_settings->draw_strength;
856 CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
858 /* apply randomness to color strength */
859 if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
860 (brush->gpencil_settings->draw_random_strength > 0.0f))
862 if (p2d->rnd[1] > 0.5f) {
863 strength -= strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1];
866 strength += strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1];
868 CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
871 copy_v2_v2(&tpt->x, &p2d->x);
873 CLAMP_MIN(pressure, 0.1f);
875 tpt->pressure = pressure;
876 tpt->strength = strength;
877 tpt->time = p2d->time;
879 tpt->uv_rot = p2d->uv_rot;
881 gpd->runtime.sbuffer_size++;
883 /* add small offset to keep stroke over the surface */
884 if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) {
885 depth_arr[i] *= (1.0f - gpd->zdepth_offset);
888 /* convert screen-coordinates to 3D coordinates */
889 gp_stroke_convertcoords_tpoint(
890 tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl,
891 p2d, depth_arr ? depth_arr + i : NULL,
894 pt->pressure = pressure;
895 pt->strength = strength;
900 if (gps->dvert != NULL) {
901 MDeformVert *dvert = &gps->dvert[i];
902 dvert->totweight = 0;
906 /* Restore original points */
907 copy_v2_v2(&p2d->x, tmp_xyp);
910 /* store cps and convert coords */
911 if (tgpi->gpd->runtime.tot_cp_points > 0) {
912 bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points;
913 for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) {
914 bGPDcontrolpoint *cp = &cps[i];
915 gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, (tGPspoint *)cp, NULL, &cp->x);
919 /* reproject to plane */
922 ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl,
923 ts->gpencil_v3d_align, origin);
924 ED_gp_project_stroke_to_plane(
925 tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
928 /* if parented change position relative to parent object */
929 for (int i = 0; i < gps->totpoints; i++) {
930 bGPDspoint *pt = &gps->points[i];
931 gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt);
934 /* force fill recalc */
935 gps->flag |= GP_STROKE_RECALC_CACHES;
937 MEM_SAFE_FREE(depth_arr);
939 DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
940 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
941 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
944 /* Update screen and stroke */
945 static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi)
947 /* update indicator in header */
948 gpencil_primitive_status_indicators(C, tgpi);
950 tgpi->type = RNA_enum_get(op->ptr, "type");
951 tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
952 /* update points position */
953 gp_primitive_update_strokes(C, tgpi);
956 static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
958 copy_v2fl_v2i(tgpi->mval, event->mval);
959 copy_v2_v2(tgpi->origin, tgpi->mval);
960 copy_v2_v2(tgpi->start, tgpi->mval);
961 copy_v2_v2(tgpi->end, tgpi->mval);
962 copy_v2_v2(tgpi->cp1, tgpi->mval);
963 copy_v2_v2(tgpi->cp2, tgpi->mval);
966 /* Exit and free memory */
967 static void gpencil_primitive_exit(bContext *C, wmOperator *op)
969 tGPDprimitive *tgpi = op->customdata;
970 bGPdata *gpd = tgpi->gpd;
972 /* don't assume that operator data exists at all */
974 /* clear status message area */
975 ED_workspace_status_text(C, NULL);
977 MEM_SAFE_FREE(tgpi->points);
978 tgpi->gpd->runtime.tot_cp_points = 0;
979 MEM_SAFE_FREE(tgpi->gpd->runtime.cp_points);
980 /* finally, free memory used by temp data */
981 BKE_gpencil_free_strokes(tgpi->gpf);
982 MEM_SAFE_FREE(tgpi->gpf);
984 /* free random seed */
985 if (tgpi->rng != NULL) {
986 BLI_rng_free(tgpi->rng);
992 /* free stroke buffer */
993 if ((gpd != NULL) && (gpd->runtime.sbuffer)) {
994 MEM_SAFE_FREE(gpd->runtime.sbuffer);
995 gpd->runtime.sbuffer = NULL;
998 gpd->runtime.sbuffer_size = 0;
999 gpd->runtime.sbuffer_sflag = 0;
1002 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
1003 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
1006 op->customdata = NULL;
1009 /* Init new temporary primitive data */
1010 static void gpencil_primitive_init(bContext *C, wmOperator *op)
1012 ToolSettings *ts = CTX_data_tool_settings(C);
1013 bGPdata *gpd = CTX_data_gpencil_data(C);
1014 Main *bmain = CTX_data_main(C);
1015 Scene *scene = CTX_data_scene(C);
1016 Depsgraph *depsgraph = CTX_data_depsgraph(C);
1017 int cfra_eval = (int)DEG_get_ctime(depsgraph);
1019 /* create temporary operator data */
1020 tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data");
1021 op->customdata = tgpi;
1023 tgpi->points = MEM_callocN(sizeof(tGPspoint), "gp primitive points2D");
1025 /* set current scene and window info */
1026 tgpi->bmain = CTX_data_main(C);
1027 tgpi->scene = scene;
1028 tgpi->ob = CTX_data_active_object(C);
1029 tgpi->sa = CTX_wm_area(C);
1030 tgpi->ar = CTX_wm_region(C);
1031 tgpi->rv3d = tgpi->ar->regiondata;
1032 tgpi->v3d = tgpi->sa->spacedata.first;
1033 tgpi->depsgraph = CTX_data_depsgraph(C);
1034 tgpi->win = CTX_wm_window(C);
1036 /* save original type */
1037 tgpi->orign_type = RNA_enum_get(op->ptr, "type");
1039 /* set current frame number */
1040 tgpi->cframe = cfra_eval;
1042 /* set GP datablock */
1044 /* region where paint was originated */
1045 tgpi->gpd->runtime.ar = tgpi->ar;
1047 /* control points */
1048 tgpi->gpd->runtime.cp_points = MEM_callocN(sizeof(bGPDcontrolpoint) * MAX_CP, "gp primitive cpoint");
1049 tgpi->gpd->runtime.tot_cp_points = 0;
1052 tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob);
1054 /* set parameters */
1055 tgpi->type = RNA_enum_get(op->ptr, "type");
1057 if (ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_CURVE)) {
1061 tgpi->curve = false;
1064 /* set default edge count */
1065 switch (tgpi->type) {
1066 case GP_STROKE_LINE:
1068 RNA_int_set(op->ptr, "edges", 8);
1073 RNA_int_set(op->ptr, "edges", 8);
1076 case GP_STROKE_CIRCLE:
1078 RNA_int_set(op->ptr, "edges", 96);
1083 RNA_int_set(op->ptr, "edges", 64);
1088 tgpi->tot_stored_edges = 0;
1089 tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
1091 tgpi->lock_axis = ts->gp_sculpt.lock_axis;
1093 /* set temp layer, frame and stroke */
1094 gp_primitive_set_initdata(C, tgpi);
1097 /* Invoke handler: Initialize the operator */
1098 static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1100 wmWindow *win = CTX_wm_window(C);
1101 bGPdata *gpd = CTX_data_gpencil_data(C);
1102 tGPDprimitive *tgpi = NULL;
1104 /* initialize operator runtime data */
1105 gpencil_primitive_init(C, op);
1106 tgpi = op->customdata;
1108 const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1110 tgpi->flag = IN_PROGRESS;
1111 gpencil_primitive_interaction_begin(tgpi, event);
1114 /* if in tools region, wait till we get to the main (3d-space)
1115 * region before allowing drawing to take place.
1117 op->flag |= OP_IS_MODAL_CURSOR_REGION;
1119 /* set cursor to indicate modal */
1120 WM_cursor_modal_set(win, BC_CROSSCURSOR);
1122 /* update sindicator in header */
1123 gpencil_primitive_status_indicators(C, tgpi);
1124 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1125 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
1127 /* add a modal handler for this operator */
1128 WM_event_add_modal_handler(C, op);
1130 return OPERATOR_RUNNING_MODAL;
1133 /* Helper to complete a primitive */
1134 static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWindow *win, tGPDprimitive *tgpi)
1139 ToolSettings *ts = tgpi->scene->toolsettings;
1141 const int def_nr = tgpi->ob->actdef - 1;
1142 const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr);
1144 /* return to normal cursor and header status */
1145 ED_workspace_status_text(C, NULL);
1146 WM_cursor_modal_restore(win);
1148 /* insert keyframes as required... */
1149 short add_frame_mode;
1150 if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
1151 add_frame_mode = GP_GETFRAME_ADD_COPY;
1154 add_frame_mode = GP_GETFRAME_ADD_NEW;
1157 gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode);
1159 /* prepare stroke to get transferred */
1160 gps = tgpi->gpf->strokes.first;
1162 gps->thickness = tgpi->brush->size;
1163 gps->flag |= GP_STROKE_RECALC_CACHES;
1164 gps->tot_triangles = 0;
1167 /* transfer stroke from temporary buffer to the actual frame */
1168 if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) {
1169 BLI_movelisttolist_reverse(&gpf->strokes, &tgpi->gpf->strokes);
1172 BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes);
1174 BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes));
1176 /* add weights if required */
1177 if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
1178 BKE_gpencil_dvert_ensure(gps);
1179 for (int i = 0; i < gps->totpoints; i++) {
1180 MDeformVert *ve = &gps->dvert[i];
1181 MDeformWeight *dw = defvert_verify_index(ve, def_nr);
1183 dw->weight = ts->vgroup_weight;
1188 DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE);
1189 DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1191 /* clean up temp data */
1192 gpencil_primitive_exit(C, op);
1195 /* edit event handling */
1196 static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi)
1198 /* calculate nearest point then set cursor */
1199 int move = MOVE_NONE;
1200 float a = len_v2v2(tgpi->mval, tgpi->start);
1201 float b = len_v2v2(tgpi->mval, tgpi->end);
1203 float c = len_v2v2(tgpi->mval, tgpi->cp1);
1204 float d = len_v2v2(tgpi->mval, tgpi->cp2);
1206 if (tgpi->flag == IN_CURVE_EDIT) {
1207 if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) {
1209 WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1211 else if (tgpi->curve) {
1213 WM_cursor_modal_set(win, BC_HANDCURSOR);
1216 WM_cursor_modal_set(win, BC_CROSSCURSOR);
1219 else if (tgpi->flag == IN_PROGRESS) {
1220 WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1223 switch (event->type) {
1226 if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) {
1227 if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) {
1228 copy_v2_v2(tgpi->start, tgpi->mval);
1230 else if (tgpi->sel_cp == SELECT_END) {
1231 copy_v2_v2(tgpi->end, tgpi->mval);
1233 else if (tgpi->sel_cp == SELECT_CP1 || (tgpi->sel_cp == SELECT_CP2 && tgpi->type != GP_STROKE_CURVE)) {
1234 float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
1235 float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
1239 copy_v2_v2(tgpi->cp2, tgpi->cp1);
1241 else if (tgpi->sel_cp == SELECT_CP2) {
1242 float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
1243 float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
1247 copy_v2_v2(tgpi->cp1, tgpi->cp2);
1250 gpencil_primitive_update(C, op, tgpi);
1256 if ((event->val == KM_PRESS)) {
1257 /* find nearest cp based on stroke end points */
1258 if (move == MOVE_ENDS)
1259 tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END;
1260 else if (move == MOVE_CP)
1261 tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2;
1263 tgpi->sel_cp = SELECT_NONE;
1266 else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
1267 /* set control points and enter edit mode */
1268 tgpi->flag = IN_CURVE_EDIT;
1269 gp_primitive_update_cps(tgpi);
1270 gpencil_primitive_update(C, op, tgpi);
1273 tgpi->sel_cp = SELECT_NONE;
1279 if ((event->val == KM_PRESS) &&
1281 (tgpi->orign_type == GP_STROKE_ARC))
1284 gp_primitive_update_cps(tgpi);
1285 gpencil_primitive_update(C, op, tgpi);
1291 if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
1292 tgpi->flag = IN_PROGRESS;
1293 WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1294 gpencil_primitive_add_segment(tgpi);
1295 copy_v2_v2(tgpi->start, tgpi->end);
1296 copy_v2_v2(tgpi->origin, tgpi->start);
1297 gp_primitive_update_cps(tgpi);
1305 static void gpencil_primitive_move(tGPDprimitive *tgpi, bool reset)
1311 sub_v2_v2(move, tgpi->move);
1312 zero_v2(tgpi->move);
1315 sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
1316 add_v2_v2(tgpi->move, move);
1319 bGPDstroke *gps = tgpi->gpf->strokes.first;
1320 tGPspoint *points2D = tgpi->points;
1322 for (int i = 0; i < gps->totpoints; i++) {
1323 tGPspoint *p2d = &points2D[i];
1324 add_v2_v2(&p2d->x, move);
1327 add_v2_v2(tgpi->start, move);
1328 add_v2_v2(tgpi->end, move);
1329 add_v2_v2(tgpi->cp1, move);
1330 add_v2_v2(tgpi->cp2, move);
1331 add_v2_v2(tgpi->origin, move);
1334 /* Modal handler: Events handling during interactive part */
1335 static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
1337 tGPDprimitive *tgpi = op->customdata;
1338 wmWindow *win = CTX_wm_window(C);
1339 const bool has_numinput = hasNumInput(&tgpi->num);
1341 copy_v2fl_v2i(tgpi->mval, event->mval);
1343 if (tgpi->flag == IN_MOVE) {
1345 switch (event->type) {
1347 gpencil_primitive_move(tgpi, false);
1348 gpencil_primitive_update(C, op, tgpi);
1352 zero_v2(tgpi->move);
1353 tgpi->flag = IN_CURVE_EDIT;
1356 if (event->val == KM_RELEASE) {
1357 tgpi->flag = IN_CURVE_EDIT;
1358 gpencil_primitive_move(tgpi, true);
1359 gpencil_primitive_update(C, op, tgpi);
1363 copy_v2_v2(tgpi->mvalo, tgpi->mval);
1364 return OPERATOR_RUNNING_MODAL;
1366 else if (tgpi->flag != IDLE) {
1367 gpencil_primitive_edit_event_handling(C, op, win, event, tgpi);
1370 switch (event->type) {
1373 if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) {
1374 /* start drawing primitive */
1375 /* TODO: Ignore if not in main region yet */
1376 tgpi->flag = IN_PROGRESS;
1377 gpencil_primitive_interaction_begin(tgpi, event);
1379 else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) {
1380 tgpi->flag = IN_CURVE_EDIT;
1382 else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
1383 /* set control points and enter edit mode */
1384 tgpi->flag = IN_CURVE_EDIT;
1385 gp_primitive_update_cps(tgpi);
1386 gpencil_primitive_update(C, op, tgpi);
1388 else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && (tgpi->type != GP_STROKE_CURVE)) {
1389 /* stop drawing primitive */
1391 gpencil_primitive_interaction_end(C, op, win, tgpi);
1393 return OPERATOR_FINISHED;
1396 if (G.debug & G_DEBUG) {
1397 printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag);
1402 case SPACEKEY: /* confirm */
1406 gpencil_primitive_interaction_end(C, op, win, tgpi);
1408 return OPERATOR_FINISHED;
1410 case RIGHTMOUSE: /* cancel */
1413 /* return to normal cursor and header status */
1414 ED_workspace_status_text(C, NULL);
1415 WM_cursor_modal_restore(win);
1417 /* clean up temp data */
1418 gpencil_primitive_exit(C, op);
1421 return OPERATOR_CANCELLED;
1426 if ((event->val != KM_RELEASE)) {
1427 tgpi->tot_edges = tgpi->tot_edges + 1;
1428 CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1429 RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1432 gpencil_primitive_update(C, op, tgpi);
1437 case WHEELDOWNMOUSE:
1439 if ((event->val != KM_RELEASE)) {
1440 tgpi->tot_edges = tgpi->tot_edges - 1;
1441 CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1442 RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1445 gpencil_primitive_update(C, op, tgpi);
1449 case GKEY: /* grab mode */
1451 if ((event->val == KM_PRESS)) {
1452 tgpi->flag = IN_MOVE;
1453 WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1457 case CKEY: /* curve mode */
1459 if ((event->val == KM_PRESS) &&
1460 (tgpi->orign_type == GP_STROKE_CURVE))
1462 switch (tgpi->type) {
1463 case GP_STROKE_CURVE:
1464 tgpi->type = GP_STROKE_ARC;
1468 tgpi->type = GP_STROKE_CURVE;
1472 RNA_enum_set(op->ptr, "type", tgpi->type);
1473 gp_primitive_update_cps(tgpi);
1474 gpencil_primitive_update(C, op, tgpi);
1480 if (tgpi->flag == IN_CURVE_EDIT) {
1481 tgpi->flag = IN_PROGRESS;
1482 WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1483 gp_primitive_update_cps(tgpi);
1484 gpencil_primitive_update(C, op, tgpi);
1488 case MOUSEMOVE: /* calculate new position */
1490 if (tgpi->flag == IN_CURVE_EDIT) {
1493 /* only handle mousemove if not doing numinput */
1494 if (has_numinput == false) {
1495 /* update position of mouse */
1496 copy_v2_v2(tgpi->end, tgpi->mval);
1497 copy_v2_v2(tgpi->start, tgpi->origin);
1498 if (tgpi->flag == IDLE) {
1499 copy_v2_v2(tgpi->origin, tgpi->mval);
1501 /* Keep square if shift key */
1503 float x = tgpi->end[0] - tgpi->origin[0];
1504 float y = tgpi->end[1] - tgpi->origin[1];
1505 if (tgpi->type == GP_STROKE_LINE || tgpi->curve) {
1506 float angle = fabsf(atan2f(y, x));
1507 if (angle < 0.4f || angle > (M_PI - 0.4f)) {
1508 tgpi->end[1] = tgpi->origin[1];
1510 else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) {
1511 tgpi->end[0] = tgpi->origin[0];
1514 gpencil_primitive_to_square(tgpi, x, y);
1518 gpencil_primitive_to_square(tgpi, x, y);
1521 /* Center primitive if alt key */
1523 tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
1524 tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
1526 gp_primitive_update_cps(tgpi);
1528 gpencil_primitive_update(C, op, tgpi);
1534 if (tgpi->flag != IN_CURVE_EDIT && (event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
1537 /* Grab data from numeric input, and store this new value (the user see an int) */
1538 value = tgpi->tot_edges;
1539 applyNumInput(&tgpi->num, &value);
1540 tgpi->tot_edges = value;
1542 CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1543 RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1546 gpencil_primitive_update(C, op, tgpi);
1551 /* unhandled event - allow to pass through */
1552 return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
1557 copy_v2_v2(tgpi->mvalo, tgpi->mval);
1558 /* still running... */
1559 return OPERATOR_RUNNING_MODAL;
1562 /* Cancel handler */
1563 static void gpencil_primitive_cancel(bContext *C, wmOperator *op)
1565 /* this is just a wrapper around exit() */
1566 gpencil_primitive_exit(C, op);
1569 void GPENCIL_OT_primitive(wmOperatorType *ot)
1571 static EnumPropertyItem primitive_type[] = {
1572 {GP_STROKE_BOX, "BOX", 0, "Box", ""},
1573 {GP_STROKE_LINE, "LINE", 0, "Line", ""},
1574 {GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""},
1575 {GP_STROKE_ARC, "ARC", 0, "Arc", ""},
1576 {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""},
1577 {0, NULL, 0, NULL, NULL}
1581 ot->name = "Grease Pencil Shapes";
1582 ot->idname = "GPENCIL_OT_primitive";
1583 ot->description = "Create predefined grease pencil stroke shapes";
1586 ot->invoke = gpencil_primitive_invoke;
1587 ot->modal = gpencil_primitive_modal;
1588 ot->cancel = gpencil_primitive_cancel;
1589 ot->poll = gpencil_primitive_add_poll;
1592 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1597 prop = RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
1598 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1600 RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape");
1602 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1603 RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);