diff options
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_primitive.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_primitive.c | 1083 |
1 files changed, 891 insertions, 192 deletions
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 029db23499e..0e4f9558842 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -41,9 +41,12 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLT_translation.h" +#include "PIL_time.h" + #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_meshdata_types.h" @@ -54,6 +57,7 @@ #include "DNA_view3d_types.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_global.h" @@ -64,6 +68,7 @@ #include "BKE_report.h" #include "UI_interface.h" +#include "UI_resources.h" #include "WM_api.h" #include "WM_types.h" @@ -85,13 +90,162 @@ #define MIN_EDGES 2 #define MAX_EDGES 128 +#define MAX_CP 128 #define IDLE 0 #define IN_PROGRESS 1 +#define IN_CURVE_EDIT 2 +#define IN_MOVE 3 + +#define SELECT_NONE 0 +#define SELECT_START 1 +#define SELECT_CP1 2 +#define SELECT_CP2 3 +#define SELECT_END 4 + +#define BIG_SIZE_CTL 15 +#define MID_SIZE_CTL 10 +#define SMALL_SIZE_CTL 8 + +#define MOVE_NONE 0 +#define MOVE_ENDS 1 +#define MOVE_CP 2 /* ************************************************ */ /* Core/Shared Utilities */ +/* clear the session buffers (call this before AND after a paint operation) */ +static void gp_session_validatebuffer(tGPDprimitive *p) +{ + bGPdata *gpd = p->gpd; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->runtime.sbuffer) { + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + } + else { + gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + } + + /* reset indices */ + gpd->runtime.sbuffer_size = 0; + + /* reset flags */ + gpd->runtime.sbuffer_sflag = 0; + gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + + if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) + gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC; +} + +static void gp_init_colors(tGPDprimitive *p) +{ + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; + + /* use brush material */ + ma = BKE_gpencil_get_material_from_brush(brush); + + /* if no brush defaults, get material and color info */ + if ((ma == NULL) || (ma->gp_style == NULL)) { + BKE_gpencil_material_ensure(p->bmain, p->ob); + + /* assign always the first material to the brush */ + p->mat = give_current_material(p->ob, 1); + brush->gpencil_settings->material = p->mat; + } + else { + p->mat = ma; + } + + /* check if the material is already on object material slots and add it if missing */ + if (BKE_gpencil_get_material_index(p->ob, p->mat) == 0) { + BKE_object_material_slot_add(p->bmain, p->ob); + assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + /* assign color information to temp data */ + gp_style = p->mat->gp_style; + if (gp_style) { + + /* set colors */ + copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); + copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba); + /* add some alpha to make easy the filling without hide strokes */ + if (gpd->runtime.sfill[3] > 0.8f) { + gpd->runtime.sfill[3] = 0.8f; + } + + gpd->runtime.mode = (short)gp_style->mode; + gpd->runtime.bstroke_style = gp_style->stroke_style; + gpd->runtime.bfill_style = gp_style->fill_style; + } +} + +/* Helper to square a primitive */ +static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, const float y) +{ + float w = fabsf(x); + float h = fabsf(y); + if ((x > 0 && y > 0) || (x < 0 && y < 0)) { + if (w > h) + tgpi->end[1] = tgpi->origin[1] + x; + else + tgpi->end[0] = tgpi->origin[0] + y; + } + else { + if (w > h) + tgpi->end[1] = tgpi->origin[1] - x; + else + tgpi->end[0] = tgpi->origin[0] - y; + } +} + +/* Helper to rotate point around origin */ +static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle) +{ + float pt[2]; + float r[2]; + sub_v2_v2v2(pt, p, origin); + rotate_v2_v2fl(r, pt, angle); + add_v2_v2v2(v, r, origin); +} + +/* Helper to rotate line around line centre */ +static void gp_primitive_rotate_line(float va[2], float vb[2], const float a[2], const float b[2], const float angle) +{ + float midpoint[2]; + mid_v2_v2v2(midpoint, a, b); + gp_rotate_v2_v2v2fl(va, a, midpoint, angle); + gp_rotate_v2_v2v2fl(vb, b, midpoint, angle); +} + +/* Helper to update cps */ +static void gp_primitive_update_cps(tGPDprimitive *tgpi) +{ + if (!tgpi->curve) { + mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); + copy_v2_v2(tgpi->cp1, tgpi->midpoint); + copy_v2_v2(tgpi->cp2, tgpi->cp1); + } + else if (tgpi->type == GP_STROKE_CURVE) { + mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); + copy_v2_v2(tgpi->cp1, tgpi->midpoint); + copy_v2_v2(tgpi->cp2, tgpi->cp1); + } + else if (tgpi->type == GP_STROKE_ARC) { + if (tgpi->flip) { + gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2); + } + else { + gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2); + } + } +} + /* Poll callback for primitive operators */ static bool gpencil_primitive_add_poll(bContext *C) { @@ -129,6 +283,15 @@ static bool gpencil_primitive_add_poll(bContext *C) return 1; } +/* Allocate memory to stroke, adds MAX_EDGES on every call */ +static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi) { + tgpi->point_count += (tgpi->type == GP_STROKE_BOX) ? (MAX_EDGES * 4 + 1) : (MAX_EDGES + 1); + bGPDstroke *gpsf = tgpi->gpf->strokes.first; + gpsf->points = MEM_reallocN(gpsf->points, sizeof(bGPDspoint) * tgpi->point_count); + if (gpsf->dvert != NULL) + gpsf->dvert = MEM_reallocN(gpsf->dvert, sizeof(MDeformVert) * tgpi->point_count); + tgpi->points = MEM_reallocN(tgpi->points, sizeof(tGPspoint) * tgpi->point_count); +} /* ****************** Primitive Interactive *********************** */ @@ -167,13 +330,11 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* enable recalculation flag by default */ gps->flag |= GP_STROKE_RECALC_CACHES; + gps->flag &= ~GP_STROKE_SELECT; /* the polygon must be closed, so enabled cyclic */ - if (tgpi->type != GP_STROKE_LINE && tgpi->type != GP_STROKE_ARC) { + if (ELEM(tgpi->type,GP_STROKE_BOX ,GP_STROKE_CIRCLE)) gps->flag |= GP_STROKE_CYCLIC; - } - else { - gps->flag &= ~GP_STROKE_CYCLIC; - } + gps->flag |= GP_STROKE_3DSPACE; gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat) - 1; @@ -188,19 +349,38 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* add to strokes */ BLI_addtail(&tgpi->gpf->strokes, gps); -} -/* ----------------------- */ -/* Drawing Callbacks */ + /* allocate memory for storage points */ + gpencil_primitive_allocate_memory(tgpi); + + /* Random generator, only init once. */ + tgpi->rng = BLI_rng_new((uint)0); +} -/* Drawing callback for modal operator in 3d mode */ -static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) +/* add new segment to curve */ +static void gpencil_primitive_add_segment(tGPDprimitive *tgpi) { - tGPDprimitive *tgpi = (tGPDprimitive *)arg; - ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW); + if(tgpi->tot_stored_edges > 0) + tgpi->tot_stored_edges += (tgpi->tot_edges - 1); + else + tgpi->tot_stored_edges += tgpi->tot_edges; + gpencil_primitive_allocate_memory(tgpi); } -/* ----------------------- */ +/* Helper: set control point */ +static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size) +{ + bGPDcontrolpoint *cp_points = tgpi->gpd->runtime.cp_points; + + if (tgpi->gpd->runtime.tot_cp_points < MAX_CP) { + CLAMP(size, 5, 20); + bGPDcontrolpoint *cp = &cp_points[tgpi->gpd->runtime.tot_cp_points]; + copy_v2_v2(&cp->x, p); + copy_v4_v4(cp->color, color); + cp->size = size; + tgpi->gpd->runtime.tot_cp_points += 1; + } +} /* Helper: Draw status message while the user is running the operator */ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi) @@ -209,20 +389,23 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi char status_str[UI_MAX_DRAW_STR]; char msg_str[UI_MAX_DRAW_STR]; - if (tgpi->type == GP_STROKE_BOX) { - BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square, Alt to center"), UI_MAX_DRAW_STR); + if (tgpi->type == GP_STROKE_LINE) { + 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); + } + else if (tgpi->type == GP_STROKE_BOX) { + 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); } - else if (tgpi->type == GP_STROKE_LINE) { - BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Alt to center"), UI_MAX_DRAW_STR); + else if (tgpi->type == GP_STROKE_CIRCLE) { + 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); } else if (tgpi->type == GP_STROKE_ARC) { - BLI_strncpy(msg_str, IFACE_("Arc: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, F to flip, C to Close"), UI_MAX_DRAW_STR); + 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); } - else { - BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR); + else if (tgpi->type == GP_STROKE_CURVE) { + 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); } - if (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC) { + if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) { if (hasNumInput(&tgpi->num)) { char str_offs[NUM_STR_REP_LEN]; @@ -233,114 +416,208 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi if (tgpi->flag == IN_PROGRESS) { BLI_snprintf( status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges, - tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]); } else { BLI_snprintf( status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges, - tgpi->bottom[0], tgpi->bottom[1]); + tgpi->end[0], tgpi->end[1]); } } } else { if (tgpi->flag == IN_PROGRESS) { BLI_snprintf( - status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str, - tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges, + tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]); } else { BLI_snprintf( status_str, sizeof(status_str), "%s: (%d, %d)", msg_str, - tgpi->bottom[0], tgpi->bottom[1]); + tgpi->end[0], tgpi->end[1]); } } ED_workspace_status_text(C, status_str); } -/* ----------------------- */ - /* create a rectangle */ static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D) { - BLI_assert(tgpi->tot_edges == 4); - - points2D[0].x = (float)tgpi->top[0]; - points2D[0].y = (float)tgpi->top[1]; - - points2D[1].x = (float)tgpi->bottom[0]; - points2D[1].y = (float)tgpi->top[1]; - - points2D[2].x = (float)tgpi->bottom[0]; - points2D[2].y = (float)tgpi->bottom[1]; + float coords[5][2]; + + coords[0][0] = tgpi->start[0]; + coords[0][1] = tgpi->start[1]; + coords[1][0] = tgpi->end[0]; + coords[1][1] = tgpi->start[1]; + coords[2][0] = tgpi->end[0]; + coords[2][1] = tgpi->end[1]; + coords[3][0] = tgpi->start[0]; + coords[3][1] = tgpi->end[1]; + coords[4][0] = tgpi->start[0]; + coords[4][1] = tgpi->start[1]; + + const float step = 1.0f / (float)(tgpi->tot_edges); + int i = tgpi->tot_stored_edges; + + for (int j = 0; j < 4; j++) { + float a = 0.0f; + for (int k = 0; k < tgpi->tot_edges; k++) { + tGPspoint *p2d = &points2D[i]; + interp_v2_v2v2(&p2d->x, coords[j], coords[j + 1], a); + a += step; + i++; + } + } - points2D[3].x = (float)tgpi->top[0]; - points2D[3].y = (float)tgpi->bottom[1]; + mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); + float color[4]; + UI_GetThemeColor4fv(TH_ACTIVE_VERT, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + if (tgpi->tot_stored_edges) { + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + } + else + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL); } /* create a line */ static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D) { - BLI_assert(tgpi->tot_edges == 2); + if (tgpi->tot_edges == 2) { + int i = tgpi->tot_stored_edges; - points2D[0].x = (float)tgpi->top[0]; - points2D[0].y = (float)tgpi->top[1]; + points2D[i].x = tgpi->start[0]; + points2D[i].y = tgpi->start[1]; - points2D[1].x = (float)tgpi->bottom[0]; - points2D[1].y = (float)tgpi->bottom[1]; + points2D[i + 1].x = tgpi->end[0]; + points2D[i + 1].y = tgpi->end[1]; + } + else { + const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); + const float step = 1.0f / (float)(tgpi->tot_edges - 1); + float a = tgpi->tot_stored_edges ? step : 0.0f; + + for (int i = tgpi->tot_stored_edges; i < totpoints; i++) { + tGPspoint *p2d = &points2D[i]; + interp_v2_v2v2(&p2d->x, tgpi->start, tgpi->end, a); + a += step; + } + } + float color[4]; + UI_GetThemeColor4fv(TH_ACTIVE_VERT, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + if (tgpi->tot_stored_edges) { + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + } + else + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); } /* create an arc */ static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D) { - const int totpoints = tgpi->tot_edges; - const float step = M_PI_2 / (float)(totpoints - 1); - float length[2]; - int start[2]; - int end[2]; - float a = 0.0f; - - start[0] = tgpi->top[0]; - start[1] = tgpi->top[1]; - end[0] = tgpi->bottom[0]; - end[1] = tgpi->bottom[1]; - - if (tgpi->flip) { - SWAP(int, end[0], start[0]); - SWAP(int, end[1], start[1]); + const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); + const float step = M_PI_2 / (float)(tgpi->tot_edges - 1); + float start[2]; + float end[2]; + float cp1[2]; + float corner[2]; + float midpoint[2]; + float a = tgpi->tot_stored_edges ? step : 0.0f; + + mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); + copy_v2_v2(start, tgpi->start); + copy_v2_v2(end, tgpi->end); + copy_v2_v2(cp1, tgpi->cp1); + copy_v2_v2(midpoint, tgpi->midpoint); + + corner[0] = midpoint[0] - (cp1[0] - midpoint[0]); + corner[1] = midpoint[1] - (cp1[1] - midpoint[1]); + + for (int i = tgpi->tot_stored_edges; i < totpoints; i++) { + tGPspoint *p2d = &points2D[i]; + p2d->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a); + p2d->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a); + a += step; } + float color[4]; + UI_GetThemeColor4fv(TH_ACTIVE_VERT, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + if (tgpi->tot_stored_edges) { + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + } + else + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color); + gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f); +} - length[0] = end[0] - start[0]; - length[1] = end[1] - start[1]; - - for (int i = 0; i < totpoints; i++) { +/* create a bezier */ +static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); + const float step = 1.0f / (float)(tgpi->tot_edges - 1); + float bcp1[2]; + float bcp2[2]; + float bcp3[2]; + float bcp4[2]; + float a = tgpi->tot_stored_edges ? step : 0.0f; + + copy_v2_v2(bcp1, tgpi->start); + copy_v2_v2(bcp2, tgpi->cp1); + copy_v2_v2(bcp3, tgpi->cp2); + copy_v2_v2(bcp4, tgpi->end); + + for (int i = tgpi->tot_stored_edges; i < totpoints; i++) { tGPspoint *p2d = &points2D[i]; - p2d->x = (start[0] + sinf(a) * length[0]); - p2d->y = (end[1] - cosf(a) * length[1]); + interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a); a += step; } + float color[4]; + UI_GetThemeColor4fv(TH_ACTIVE_VERT, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + if (tgpi->tot_stored_edges) { + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + } + else + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color); + gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f); + gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f); } /* create a circle */ static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D) { - const int totpoints = tgpi->tot_edges; - const float step = (2.0f * M_PI) / (float)(totpoints); + const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); + const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges); float center[2]; float radius[2]; float a = 0.0f; - /* TODO: Use math-lib functions for these? */ - center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f); - center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f); - radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f)); - radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f)); + center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f); + center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f); + radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f)); + radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f)); - for (int i = 0; i < totpoints; i++) { + for (int i = tgpi->tot_stored_edges; i < totpoints; i++) { tGPspoint *p2d = &points2D[i]; p2d->x = (center[0] + cosf(a) * radius[0]); p2d->y = (center[1] + sinf(a) * radius[1]); a += step; } + float color[4]; + UI_GetThemeColor4fv(TH_ACTIVE_VERT, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL); } /* Helper: Update shape of the stroke */ @@ -348,18 +625,26 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) { ToolSettings *ts = tgpi->scene->toolsettings; bGPdata *gpd = tgpi->gpd; + Brush *brush = tgpi->brush; bGPDstroke *gps = tgpi->gpf->strokes.first; + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; + char *align_flag = &ts->gpencil_v3d_align; + bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); - /* realloc points to new size */ - /* TODO: only do this if the size has changed? */ - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges); - if (gps->dvert != NULL) { - gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges); - } - gps->totpoints = tgpi->tot_edges; + if (tgpi->type == GP_STROKE_BOX) + gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges); + else + gps->totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); + + if (tgpi->tot_stored_edges) + gps->totpoints--; + + tgpi->gpd->runtime.tot_cp_points = 0; /* compute screen-space coordinates for points */ - tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D"); + tGPspoint *points2D = tgpi->points; + switch (tgpi->type) { case GP_STROKE_BOX: gp_primitive_rectangle(tgpi, points2D); @@ -372,46 +657,254 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) break; case GP_STROKE_ARC: gp_primitive_arc(tgpi, points2D); - if (tgpi->cyclic) - gps->flag |= GP_STROKE_CYCLIC; - else - gps->flag &= ~GP_STROKE_CYCLIC; break; + case GP_STROKE_CURVE: + gp_primitive_bezier(tgpi, points2D); default: break; - } + } /* convert screen-coordinates to 3D coordinates */ + gp_session_validatebuffer(tgpi); + gp_init_colors(tgpi); + if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { + curvemapping_initialize(ts->gp_sculpt.cur_primitive); + } + if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { + curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter); + } + if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength); + } + + /* get an array of depths, far depths are blended */ + float *depth_arr = NULL; + if (is_depth) { + int i; + int mval_i[2], mval_prev[2] = { 0 }; + bool interp_depth = false; + bool found_depth = false; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(tgpi->win, tgpi->ar); + ED_view3d_autodist_init(tgpi->depsgraph, tgpi->ar, tgpi->v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + + depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); + tGPspoint *ptc = &points2D[0]; + for (i = 0; i < gps->totpoints; i++, ptc++) { + round_v2i_v2fl(mval_i, &ptc->x); + if ((ED_view3d_autodist_depth(tgpi->ar, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg(tgpi->ar, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) + { + interp_depth = true; + } + else { + found_depth = true; + } + copy_v2_v2_int(mval_prev, mval_i); + } + + if (!found_depth) { + for (i = 0; i < gps->totpoints; i++) { + depth_arr[i] = 0.9999f; + } + } + else { + /* if all depth are too high disable */ + bool valid_depth = false; + for (i = 0; i < gps->totpoints; i++) { + if (depth_arr[i] < 0.9999f) { + valid_depth = true; + break; + } + } + if (!valid_depth) { + MEM_SAFE_FREE(depth_arr); + is_depth = false; + } + else { + if ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) || + (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST)) + { + int first_valid = 0; + int last_valid = 0; + + /* find first valid contact point */ + for (i = 0; i < gps->totpoints; i++) { + if (depth_arr[i] != FLT_MAX) + break; + } + first_valid = i; + + /* find last valid contact point */ + if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST) { + last_valid = first_valid; + } + else { + for (i = gps->totpoints - 1; i >= 0; i--) { + if (depth_arr[i] != FLT_MAX) + break; + } + last_valid = i; + } + + /* invalidate any other point, to interpolate between + * first and last contact in an imaginary line between them */ + for (i = 0; i < gps->totpoints; i++) { + if ((i != first_valid) && (i != last_valid)) { + depth_arr[i] = FLT_MAX; + } + } + interp_depth = true; + } + + if (interp_depth) { + interp_sparse_array(depth_arr, gps->totpoints, FLT_MAX); + } + } + } + } + + /* load stroke points and sbuffer */ for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; tGPspoint *p2d = &points2D[i]; + /* Copy points to buffer */ + tGPspoint *tpt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); + + /* Store original points */ + float tmp_xyp[2]; + copy_v2_v2(tmp_xyp, &p2d->x); + + /* calc pressure */ + float curve_pressure = 1.0; + float pressure = 1.0; + float strength = brush->gpencil_settings->draw_strength; + + /* normalize value to evaluate curve */ + if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { + float value = (float)i / (gps->totpoints - 1); + curve_pressure = curvemapping_evaluateF(gset->cur_primitive, 0, value); + pressure = curve_pressure; + } + + /* apply jitter to position */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_jitter > 0.0f)) + { + float jitter; + + if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { + jitter = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, curve_pressure); + jitter *= brush->gpencil_settings->draw_sensitivity; + } + else { + jitter = brush->gpencil_settings->draw_jitter; + } + + const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */ + const float rnd = BLI_rng_get_float(tgpi->rng); + const float fac = rnd * exfactor * jitter; + if (rnd > 0.5f) { + add_v2_fl(&p2d->x, -fac); + } + else { + add_v2_fl(&p2d->x, fac); + } + } + + /* apply randomness to pressure */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_press > 0.0f)) + { + float rnd = BLI_rng_get_float(tgpi->rng); + if (rnd > 0.5f) { + pressure -= brush->gpencil_settings->draw_random_press * rnd; + } + else { + pressure += brush->gpencil_settings->draw_random_press * rnd; + } + } + + /* color strength */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, curve_pressure); + strength *= curvef * brush->gpencil_settings->draw_sensitivity; + strength *= brush->gpencil_settings->draw_strength; + } + + CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* apply randomness to color strength */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_strength > 0.0f)) + { + const float rnd = BLI_rng_get_float(tgpi->rng); + if (rnd > 0.5f) { + strength -= strength * brush->gpencil_settings->draw_random_strength * rnd; + } + else { + strength += strength * brush->gpencil_settings->draw_random_strength * rnd; + } + CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + copy_v2_v2(&tpt->x, &p2d->x); + + CLAMP_MIN(pressure, 0.1f); + + tpt->pressure = pressure; + tpt->strength = strength; + tpt->time = p2d->time; + tpt->uv_fac = 1.0f; + tpt->uv_rot = p2d->uv_rot; + + gpd->runtime.sbuffer_size++; + + /* add small offset to keep stroke over the surface */ + if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) { + depth_arr[i] *= (1.0f - gpd->zdepth_offset); + } + /* convert screen-coordinates to 3D coordinates */ - gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x); + gp_stroke_convertcoords_tpoint( + tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, + p2d, depth_arr ? depth_arr + i : NULL, + &pt->x); - pt->pressure = 1.0f; - pt->strength = tgpi->brush->gpencil_settings->draw_strength; + pt->pressure = pressure; + pt->strength = strength; pt->time = 0.0f; + pt->flag = 0; + pt->uv_fac = 1.0f; if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; dvert->totweight = 0; dvert->dw = NULL; } + + /* Restore original points */ + copy_v2_v2(&p2d->x, tmp_xyp); } - /* if axis locked, reproject to plane locked */ - if (tgpi->lock_axis > GP_LOCKAXIS_VIEW) { - bGPDspoint *tpt = gps->points; + /* store cps and convert coords */ + if (tgpi->gpd->runtime.tot_cp_points > 0) { + bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points; + for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) { + bGPDcontrolpoint *cp = &cps[i]; + gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, (tGPspoint *)cp, NULL, &cp->x); + } + } + + /* reproject to plane */ + if (!is_depth) { float origin[3]; ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); - - for (int i = 0; i < gps->totpoints; i++, tpt++) { - ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin, - ts->gp_sculpt.lock_axis - 1, - tpt); - } + ED_gp_project_stroke_to_plane( + tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1); } /* if parented change position relative to parent object */ @@ -423,8 +916,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* force fill recalc */ gps->flag |= GP_STROKE_RECALC_CACHES; - /* free temp data */ - MEM_SAFE_FREE(points2D); + MEM_SAFE_FREE(depth_arr); DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -443,18 +935,14 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive gp_primitive_update_strokes(C, tgpi); } -/* ----------------------- */ - static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { - tgpi->origin[0] = event->mval[0]; - tgpi->origin[1] = event->mval[1]; - - tgpi->top[0] = event->mval[0]; - tgpi->top[1] = event->mval[1]; - - tgpi->bottom[0] = event->mval[0]; - tgpi->bottom[1] = event->mval[1]; + copy_v2fl_v2i(tgpi->mval, event->mval); + copy_v2_v2(tgpi->origin, tgpi->mval); + copy_v2_v2(tgpi->start, tgpi->mval); + copy_v2_v2(tgpi->end, tgpi->mval); + copy_v2_v2(tgpi->cp1, tgpi->mval); + copy_v2_v2(tgpi->cp2, tgpi->mval); } /* Exit and free memory */ @@ -465,20 +953,35 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op) /* don't assume that operator data exists at all */ if (tgpi) { - /* remove drawing handler */ - if (tgpi->draw_handle_3d) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); - } - /* clear status message area */ ED_workspace_status_text(C, NULL); + MEM_SAFE_FREE(tgpi->points); + tgpi->gpd->runtime.tot_cp_points = 0; + MEM_SAFE_FREE(tgpi->gpd->runtime.cp_points); /* finally, free memory used by temp data */ BKE_gpencil_free_strokes(tgpi->gpf); - MEM_freeN(tgpi->gpf); + MEM_SAFE_FREE(tgpi->gpf); + + /* free random seed */ + if (tgpi->rng != NULL) { + BLI_rng_free(tgpi->rng); + } + MEM_freeN(tgpi); } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + /* free stroke buffer */ + if ((gpd != NULL) && (gpd->runtime.sbuffer)) { + MEM_SAFE_FREE(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; + + /* clear flags */ + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; + } + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* clear pointer */ @@ -499,7 +1002,10 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data"); op->customdata = tgpi; + tgpi->points = MEM_callocN(sizeof(tGPspoint), "gp primitive points2D"); + /* set current scene and window info */ + tgpi->bmain = CTX_data_main(C); tgpi->scene = scene; tgpi->ob = CTX_data_active_object(C); tgpi->sa = CTX_wm_area(C); @@ -509,11 +1015,20 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) tgpi->depsgraph = CTX_data_depsgraph(C); tgpi->win = CTX_wm_window(C); + /* save original type */ + tgpi->orign_type = RNA_enum_get(op->ptr, "type"); + /* set current frame number */ tgpi->cframe = cfra_eval; /* set GP datablock */ tgpi->gpd = gpd; + /* region where paint was originated */ + tgpi->gpd->runtime.ar = tgpi->ar; + + /* control points */ + tgpi->gpd->runtime.cp_points = MEM_callocN(sizeof(bGPDcontrolpoint) * MAX_CP, "gp primitive cpoint"); + tgpi->gpd->runtime.tot_cp_points = 0; /* getcolor info */ tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob); @@ -521,31 +1036,44 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* set parameters */ tgpi->type = RNA_enum_get(op->ptr, "type"); + if(ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_CURVE)) + tgpi->curve = true; + else + tgpi->curve = false; + /* set default edge count */ - if (tgpi->type == GP_STROKE_CIRCLE) { - RNA_int_set(op->ptr, "edges", 64); - } - else if (tgpi->type == GP_STROKE_ARC) { - RNA_int_set(op->ptr, "edges", 32); - } - else if (tgpi->type == GP_STROKE_BOX) { - RNA_int_set(op->ptr, "edges", 4); - } - else { /* LINE */ - RNA_int_set(op->ptr, "edges", 2); + switch (tgpi->type) { + case GP_STROKE_LINE: + { + RNA_int_set(op->ptr, "edges", 8); + break; + } + case GP_STROKE_BOX: + { + RNA_int_set(op->ptr, "edges", 8); + break; + } + case GP_STROKE_CIRCLE: + { + RNA_int_set(op->ptr, "edges", 96); + break; + } + default: + { + RNA_int_set(op->ptr, "edges", 64); + break; + } } + tgpi->tot_stored_edges = 0; tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); tgpi->flag = IDLE; - tgpi->lock_axis = ts->gp_sculpt.lock_axis; /* set temp layer, frame and stroke */ gp_primitive_set_initdata(C, tgpi); } -/* ----------------------- */ - /* Invoke handler: Initialize the operator */ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event) { @@ -568,9 +1096,6 @@ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent * */ op->flag |= OP_IS_MODAL_CURSOR_REGION; - /* Enable custom drawing handlers */ - tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW); - /* set cursor to indicate modal */ WM_cursor_modal_set(win, BC_CROSSCURSOR); @@ -624,7 +1149,6 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin if (dw) { dw->weight = ts->vgroup_weight; } - } } @@ -635,25 +1159,136 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin gpencil_primitive_exit(C, op); } -/* Helper to square a primitive */ -static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const int x, const int y) +/* edit event handling */ +static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi) { - int w = abs(x); - int h = abs(y); - if ((x > 0 && y > 0) || (x < 0 && y < 0)) { - if (w > h) - tgpi->bottom[1] = tgpi->origin[1] + x; - else - tgpi->bottom[0] = tgpi->origin[0] + y; + /* calculate nearest point then set cursor */ + int move = MOVE_NONE; + float a = len_v2v2(tgpi->mval, tgpi->start); + float b = len_v2v2(tgpi->mval, tgpi->end); + + float c = len_v2v2(tgpi->mval, tgpi->cp1); + float d = len_v2v2(tgpi->mval, tgpi->cp2); + + if (tgpi->flag == IN_CURVE_EDIT) { + if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) { + move = MOVE_ENDS; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + } + else if(tgpi->curve) { + move = MOVE_CP; + WM_cursor_modal_set(win, BC_HANDCURSOR); + } + else { + WM_cursor_modal_set(win, BC_CROSSCURSOR); + } } - else { - if (w > h) - tgpi->bottom[1] = tgpi->origin[1] - x; - else - tgpi->bottom[0] = tgpi->origin[0] - y; + else if (tgpi->flag == IN_PROGRESS) { + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + } + + switch (event->type) { + case MOUSEMOVE: + { + if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) { + if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) { + copy_v2_v2(tgpi->start, tgpi->mval); + } + else if (tgpi->sel_cp == SELECT_END) { + copy_v2_v2(tgpi->end, tgpi->mval); + } + else if (tgpi->sel_cp == SELECT_CP1 || (tgpi->sel_cp == SELECT_CP2 && tgpi->type != GP_STROKE_CURVE)) { + float dx = (tgpi->mval[0] - tgpi->mvalo[0]); + float dy = (tgpi->mval[1] - tgpi->mvalo[1]); + tgpi->cp1[0] += dx; + tgpi->cp1[1] += dy; + if (event->shift) + copy_v2_v2(tgpi->cp2, tgpi->cp1); + } + else if (tgpi->sel_cp == SELECT_CP2) { + float dx = (tgpi->mval[0] - tgpi->mvalo[0]); + float dy = (tgpi->mval[1] - tgpi->mvalo[1]); + tgpi->cp2[0] += dx; + tgpi->cp2[1] += dy; + if (event->shift) + copy_v2_v2(tgpi->cp1, tgpi->cp2); + } + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case LEFTMOUSE: + { + if ((event->val == KM_PRESS)) { + /* find nearest cp based on stroke end points */ + if (move == MOVE_ENDS) + tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END; + else if (move == MOVE_CP) + tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2; + else + tgpi->sel_cp = SELECT_NONE; + break; + } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + /* set control points and enter edit mode */ + tgpi->flag = IN_CURVE_EDIT; + gp_primitive_update_cps(tgpi); + gpencil_primitive_update(C, op, tgpi); + } + else { + tgpi->sel_cp = SELECT_NONE; + } + break; + } + case MKEY: + { + if ((event->val == KM_PRESS) && + (tgpi->curve) && + (tgpi->orign_type == GP_STROKE_ARC)) + { + tgpi->flip ^= 1; + gp_primitive_update_cps(tgpi); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case EKEY: + { + if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { + tgpi->flag = IN_PROGRESS; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + gpencil_primitive_add_segment(tgpi); + copy_v2_v2(tgpi->start, tgpi->end); + copy_v2_v2(tgpi->origin, tgpi->start); + gp_primitive_update_cps(tgpi); + } + break; + } } } +/* move */ +static void gpencil_primitive_move(tGPDprimitive *tgpi) +{ + float move[2]; + sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); + + bGPDstroke *gps = tgpi->gpf->strokes.first; + tGPspoint *points2D = tgpi->points; + + for (int i = 0; i < gps->totpoints; i++) { + tGPspoint *p2d = &points2D[i]; + add_v2_v2(&p2d->x, move); + } + + add_v2_v2(tgpi->start, move); + add_v2_v2(tgpi->end, move); + add_v2_v2(tgpi->cp1, move); + add_v2_v2(tgpi->cp2, move); + add_v2_v2(tgpi->origin, move); +} + /* Modal handler: Events handling during interactive part */ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -661,15 +1296,47 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e wmWindow *win = CTX_wm_window(C); const bool has_numinput = hasNumInput(&tgpi->num); + copy_v2fl_v2i(tgpi->mval, event->mval); + + if (tgpi->flag == IN_MOVE) { + + switch (event->type) { + case MOUSEMOVE: + gpencil_primitive_move(tgpi); + gpencil_primitive_update(C, op, tgpi); + break; + case ESCKEY: + case RIGHTMOUSE: + case LEFTMOUSE: + tgpi->flag = IN_CURVE_EDIT; + break; + } + copy_v2_v2(tgpi->mvalo, tgpi->mval); + return OPERATOR_RUNNING_MODAL; + } + else if (tgpi->flag != IDLE) { + gpencil_primitive_edit_event_handling(C, op, win, event, tgpi); + } + switch (event->type) { case LEFTMOUSE: + { if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) { /* start drawing primitive */ /* TODO: Ignore if not in main region yet */ tgpi->flag = IN_PROGRESS; gpencil_primitive_interaction_begin(tgpi, event); } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) { + tgpi->flag = IN_CURVE_EDIT; + } else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + /* set control points and enter edit mode */ + tgpi->flag = IN_CURVE_EDIT; + gp_primitive_update_cps(tgpi); + gpencil_primitive_update(C, op, tgpi); + } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && (tgpi->type != GP_STROKE_CURVE)) { /* stop drawing primitive */ tgpi->flag = IDLE; gpencil_primitive_interaction_end(C, op, win, tgpi); @@ -682,16 +1349,26 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } } break; - case RETKEY: /* confirm */ + } + case SPACEKEY: /* confirm */ + case RETKEY: { tgpi->flag = IDLE; gpencil_primitive_interaction_end(C, op, win, tgpi); /* done! */ return OPERATOR_FINISHED; } - case ESCKEY: /* cancel */ case RIGHTMOUSE: { + if (tgpi->flag == IN_CURVE_EDIT || (tgpi->flag == IN_PROGRESS && tgpi->tot_stored_edges > 0)) { + tgpi->flag = IDLE; + gpencil_primitive_interaction_end(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + } + case ESCKEY: + { /* return to normal cursor and header status */ ED_workspace_status_text(C, NULL); WM_cursor_modal_restore(win); @@ -702,76 +1379,95 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e /* canceled! */ return OPERATOR_CANCELLED; } - case CKEY: + case PADPLUSKEY: + case WHEELUPMOUSE: { - if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) { - tgpi->cyclic ^= 1; + if ((event->val != KM_RELEASE)) { + tgpi->tot_edges = tgpi->tot_edges + 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); /* update screen */ gpencil_primitive_update(C, op, tgpi); } break; } - case FKEY: + case PADMINUS: + case WHEELDOWNMOUSE: { - if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) { - tgpi->flip ^= 1; + if ((event->val != KM_RELEASE)) { + tgpi->tot_edges = tgpi->tot_edges - 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); /* update screen */ gpencil_primitive_update(C, op, tgpi); } break; } - case PADPLUSKEY: - case WHEELUPMOUSE: + case GKEY: /* grab mode */ { - if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) { - tgpi->tot_edges = tgpi->tot_edges + 1; - CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); - RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + if ((event->val == KM_PRESS)) { + tgpi->flag = IN_MOVE; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + } + break; + } + case CKEY: /* curve mode */ + { + if ((event->val == KM_PRESS) && + (tgpi->orign_type == GP_STROKE_CURVE)) + { + switch (tgpi->type) { + case GP_STROKE_CURVE: + tgpi->type = GP_STROKE_ARC; + break; + default: + case GP_STROKE_ARC: + tgpi->type = GP_STROKE_CURVE; + break; + } - /* update screen */ + RNA_enum_set(op->ptr, "type", tgpi->type); + gp_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); } break; } - case PADMINUS: - case WHEELDOWNMOUSE: + case TABKEY: { - if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) { - tgpi->tot_edges = tgpi->tot_edges - 1; - CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); - RNA_int_set(op->ptr, "edges", tgpi->tot_edges); - - /* update screen */ + if (tgpi->flag == IN_CURVE_EDIT) { + tgpi->flag = IN_PROGRESS; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + gp_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); } break; } case MOUSEMOVE: /* calculate new position */ { + if (tgpi->flag == IN_CURVE_EDIT) { + break; + } /* only handle mousemove if not doing numinput */ if (has_numinput == false) { /* update position of mouse */ - tgpi->bottom[0] = event->mval[0]; - tgpi->bottom[1] = event->mval[1]; - tgpi->top[0] = tgpi->origin[0]; - tgpi->top[1] = tgpi->origin[1]; + copy_v2_v2(tgpi->end, tgpi->mval); + copy_v2_v2(tgpi->start, tgpi->origin); if (tgpi->flag == IDLE) { - tgpi->origin[0] = event->mval[0]; - tgpi->origin[1] = event->mval[1]; + copy_v2_v2(tgpi->origin, tgpi->mval); } /* Keep square if shift key */ if (event->shift) { - int x = tgpi->bottom[0] - tgpi->origin[0]; - int y = tgpi->bottom[1] - tgpi->origin[1]; - if (tgpi->type == GP_STROKE_LINE) { - float angle = fabsf(atan2f((float)y, (float)x)); - if (angle < 0.4f || angle > (M_PI - 0.4f)) { - tgpi->bottom[1] = tgpi->origin[1]; + float x = tgpi->end[0] - tgpi->origin[0]; + float y = tgpi->end[1] - tgpi->origin[1]; + if (tgpi->type == GP_STROKE_LINE || tgpi->curve) { + float angle = fabsf(atan2f(y, x)); + if (angle < 0.4f || angle >(M_PI - 0.4f)) { + tgpi->end[1] = tgpi->origin[1]; } else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) { - tgpi->bottom[0] = tgpi->origin[0]; + tgpi->end[0] = tgpi->origin[0]; } else { gpencil_primitive_to_square(tgpi, x, y); @@ -783,9 +1479,10 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } /* Center primitive if alt key */ if (event->alt) { - tgpi->top[0] = tgpi->origin[0] - (tgpi->bottom[0] - tgpi->origin[0]); - tgpi->top[1] = tgpi->origin[1] - (tgpi->bottom[1] - tgpi->origin[1]); + tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]); + tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]); } + gp_primitive_update_cps(tgpi); /* update screen */ gpencil_primitive_update(C, op, tgpi); } @@ -793,7 +1490,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } default: { - if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + if (tgpi->flag != IN_CURVE_EDIT && (event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { float value; /* Grab data from numeric input, and store this new value (the user see an int) */ @@ -816,6 +1513,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } } + copy_v2_v2(tgpi->mvalo, tgpi->mval); /* still running... */ return OPERATOR_RUNNING_MODAL; } @@ -834,6 +1532,7 @@ void GPENCIL_OT_primitive(wmOperatorType *ot) {GP_STROKE_LINE, "LINE", 0, "Line", ""}, {GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""}, {GP_STROKE_ARC, "ARC", 0, "Arc", ""}, + {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""}, {0, NULL, 0, NULL, NULL} }; @@ -854,11 +1553,11 @@ void GPENCIL_OT_primitive(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES); + prop = RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape"); prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } - -/* *************************************************************** */ |