diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/editors/gpencil | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'source/blender/editors/gpencil')
23 files changed, 26590 insertions, 26665 deletions
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index fa91ffd1e72..5573c88c710 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -16,58 +16,58 @@ # ***** END GPL LICENSE BLOCK ***** set(INC - ../include - ../../blenfont - ../../blenkernel - ../../blenlib - ../../blentranslation - ../../depsgraph - ../../imbuf - ../../gpu - ../../makesdna - ../../makesrna - ../../windowmanager - ../../../../intern/guardedalloc - ../../../../intern/glew-mx + ../include + ../../blenfont + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../imbuf + ../../gpu + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${GLEW_INCLUDE_PATH} ) set(SRC - annotate_draw.c - annotate_paint.c - drawgpencil.c - editaction_gpencil.c - gpencil_add_monkey.c - gpencil_add_stroke.c - gpencil_armature.c - gpencil_brush.c - gpencil_convert.c - gpencil_data.c - gpencil_edit.c - gpencil_fill.c - gpencil_interpolate.c - gpencil_merge.c - gpencil_ops.c - gpencil_ops_versioning.c - gpencil_paint.c - gpencil_primitive.c - gpencil_select.c - gpencil_undo.c - gpencil_utils.c + annotate_draw.c + annotate_paint.c + drawgpencil.c + editaction_gpencil.c + gpencil_add_monkey.c + gpencil_add_stroke.c + gpencil_armature.c + gpencil_brush.c + gpencil_convert.c + gpencil_data.c + gpencil_edit.c + gpencil_fill.c + gpencil_interpolate.c + gpencil_merge.c + gpencil_ops.c + gpencil_ops_versioning.c + gpencil_paint.c + gpencil_primitive.c + gpencil_select.c + gpencil_undo.c + gpencil_utils.c - gpencil_intern.h + gpencil_intern.h ) set(LIB - bf_blenkernel - bf_blenlib + bf_blenkernel + bf_blenlib ) if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) endif() add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 2a7f9a715ad..a7fa51350ba 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -73,418 +72,436 @@ /* ----- General Defines ------ */ /* flags for sflag */ typedef enum eDrawStrokeFlags { - /** don't draw status info */ - GP_DRAWDATA_NOSTATUS = (1 << 0), - /** only draw 3d-strokes */ - GP_DRAWDATA_ONLY3D = (1 << 1), - /** only draw 'canvas' strokes */ - GP_DRAWDATA_ONLYV2D = (1 << 2), - /** only draw 'image' strokes */ - GP_DRAWDATA_ONLYI2D = (1 << 3), - /** special hack for drawing strokes in Image Editor (weird coordinates) */ - GP_DRAWDATA_IEDITHACK = (1 << 4), - /** don't draw xray in 3D view (which is default) */ - GP_DRAWDATA_NO_XRAY = (1 << 5), - /** no onionskins should be drawn (for animation playback) */ - GP_DRAWDATA_NO_ONIONS = (1 << 6), + /** don't draw status info */ + GP_DRAWDATA_NOSTATUS = (1 << 0), + /** only draw 3d-strokes */ + GP_DRAWDATA_ONLY3D = (1 << 1), + /** only draw 'canvas' strokes */ + GP_DRAWDATA_ONLYV2D = (1 << 2), + /** only draw 'image' strokes */ + GP_DRAWDATA_ONLYI2D = (1 << 3), + /** special hack for drawing strokes in Image Editor (weird coordinates) */ + GP_DRAWDATA_IEDITHACK = (1 << 4), + /** don't draw xray in 3D view (which is default) */ + GP_DRAWDATA_NO_XRAY = (1 << 5), + /** no onionskins should be drawn (for animation playback) */ + GP_DRAWDATA_NO_ONIONS = (1 << 6), } eDrawStrokeFlags; - /* ----- Tool Buffer Drawing ------ */ /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ -static void gp_draw_stroke_buffer( - const tGPspoint *points, int totpoints, short thickness, - short dflag, short sflag, float ink[4]) +static void gp_draw_stroke_buffer(const tGPspoint *points, + int totpoints, + short thickness, + short dflag, + short sflag, + float ink[4]) { - int draw_points = 0; - - /* error checking */ - if ((points == NULL) || (totpoints <= 0)) - return; - - /* check if buffer can be drawn */ - if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D)) - return; - - if (sflag & GP_STROKE_ERASER) { - /* don't draw stroke at all! */ - return; - } - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - const tGPspoint *pt = points; - - if (totpoints == 1) { - /* if drawing a single point, draw it larger */ - GPU_point_size((float)(thickness + 2) * points->pressure); - immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - immUniformColor3fvAlpha(ink, ink[3]); - immBegin(GPU_PRIM_POINTS, 1); - immVertex2fv(pos, &pt->x); - } - else { - float oldpressure = points[0].pressure; - - /* draw stroke curve */ - GPU_line_width(max_ff(oldpressure * thickness, 1.0)); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor3fvAlpha(ink, ink[3]); - - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints); - - for (int i = 0; i < totpoints; i++, pt++) { - /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, - * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) - */ - if (fabsf(pt->pressure - oldpressure) > 0.2f) { - /* need to have 2 points to avoid immEnd assert error */ - if (draw_points < 2) { - immVertex2fv(pos, &(pt - 1)->x); - } - - immEnd(); - draw_points = 0; - - GPU_line_width(max_ff(pt->pressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1); - - /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) { - immVertex2fv(pos, &(pt - 1)->x); - draw_points++; - } - - oldpressure = pt->pressure; /* reset our threshold */ - } - - /* now the point we want */ - immVertex2fv(pos, &pt->x); - draw_points++; - } - /* need to have 2 points to avoid immEnd assert error */ - if (draw_points < 2) { - immVertex2fv(pos, &(pt - 1)->x); - } - } - - immEnd(); - immUnbindProgram(); + int draw_points = 0; + + /* error checking */ + if ((points == NULL) || (totpoints <= 0)) + return; + + /* check if buffer can be drawn */ + if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D)) + return; + + if (sflag & GP_STROKE_ERASER) { + /* don't draw stroke at all! */ + return; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + const tGPspoint *pt = points; + + if (totpoints == 1) { + /* if drawing a single point, draw it larger */ + GPU_point_size((float)(thickness + 2) * points->pressure); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2fv(pos, &pt->x); + } + else { + float oldpressure = points[0].pressure; + + /* draw stroke curve */ + GPU_line_width(max_ff(oldpressure * thickness, 1.0)); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints); + + for (int i = 0; i < totpoints; i++, pt++) { + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, + * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) + */ + if (fabsf(pt->pressure - oldpressure) > 0.2f) { + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2fv(pos, &(pt - 1)->x); + } + + immEnd(); + draw_points = 0; + + GPU_line_width(max_ff(pt->pressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + immVertex2fv(pos, &(pt - 1)->x); + draw_points++; + } + + oldpressure = pt->pressure; /* reset our threshold */ + } + + /* now the point we want */ + immVertex2fv(pos, &pt->x); + draw_points++; + } + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2fv(pos, &(pt - 1)->x); + } + } + + immEnd(); + immUnbindProgram(); } /* --------- 2D Stroke Drawing Helpers --------- */ /* change in parameter list */ -static void gp_calc_2d_stroke_fxy(const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +static void gp_calc_2d_stroke_fxy( + const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) { - if (sflag & GP_STROKE_2DSPACE) { - r_co[0] = pt[0]; - r_co[1] = pt[1]; - } - else if (sflag & GP_STROKE_2DIMAGE) { - const float x = (float)((pt[0] * winx) + offsx); - const float y = (float)((pt[1] * winy) + offsy); - - r_co[0] = x; - r_co[1] = y; - } - else { - const float x = (float)(pt[0] / 100 * winx) + offsx; - const float y = (float)(pt[1] / 100 * winy) + offsy; - - r_co[0] = x; - r_co[1] = y; - } + if (sflag & GP_STROKE_2DSPACE) { + r_co[0] = pt[0]; + r_co[1] = pt[1]; + } + else if (sflag & GP_STROKE_2DIMAGE) { + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + + r_co[0] = x; + r_co[1] = y; + } + else { + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + + r_co[0] = x; + r_co[1] = y; + } } /* ----- Existing Strokes Drawing (3D and Point) ------ */ /* draw a given stroke - just a single dot (only one point) */ -static void gp_draw_stroke_point( - const bGPDspoint *points, short thickness, short UNUSED(dflag), short sflag, - int offsx, int offsy, int winx, int winy, const float ink[4]) +static void gp_draw_stroke_point(const bGPDspoint *points, + short thickness, + short UNUSED(dflag), + short sflag, + int offsx, + int offsy, + int winx, + int winy, + const float ink[4]) { - const bGPDspoint *pt = points; + const bGPDspoint *pt = points; - /* get final position using parent matrix */ - float fpt[3]; - copy_v3_v3(fpt, &pt->x); + /* get final position using parent matrix */ + float fpt[3]; + copy_v3_v3(fpt, &pt->x); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - if (sflag & GP_STROKE_3DSPACE) { - immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - } - else { - immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + if (sflag & GP_STROKE_3DSPACE) { + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + } + else { + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - /* get 2D coordinates of point */ - float co[3] = { 0.0f }; - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); - copy_v3_v3(fpt, co); - } + /* get 2D coordinates of point */ + float co[3] = {0.0f}; + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); + copy_v3_v3(fpt, co); + } - /* set color */ - immUniformColor3fvAlpha(ink, ink[3]); + /* set color */ + immUniformColor3fvAlpha(ink, ink[3]); - /* set point thickness (since there's only one of these) */ - immUniform1f("size", (float)(thickness + 2) * pt->pressure); + /* set point thickness (since there's only one of these) */ + immUniform1f("size", (float)(thickness + 2) * pt->pressure); - immBegin(GPU_PRIM_POINTS, 1); - immVertex3fv(pos, fpt); - immEnd(); + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, fpt); + immEnd(); - immUnbindProgram(); + immUnbindProgram(); } /* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d( - const bGPDspoint *points, int totpoints, short thickness, - short UNUSED(sflag), const float ink[4], bool cyclic) +static void gp_draw_stroke_3d(const bGPDspoint *points, + int totpoints, + short thickness, + short UNUSED(sflag), + const float ink[4], + bool cyclic) { - float curpressure = points[0].pressure; - float cyclic_fpt[3]; - int draw_points = 0; - - /* if cyclic needs one vertex more */ - int cyclic_add = 0; - if (cyclic) { - cyclic_add++; - } - - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3fvAlpha(ink, ink[3]); - - /* draw stroke curve */ - GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); - const bGPDspoint *pt = points; - for (int i = 0; i < totpoints; i++, pt++) { - /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, - * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) - * Note: we want more visible levels of pressures when thickness is bigger. - */ - if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { - /* if the pressure changes before get at least 2 vertices, - * need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - immVertex3fv(pos, &pt2->x); - } - immEnd(); - draw_points = 0; - - curpressure = pt->pressure; - GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); - - /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) { - const bGPDspoint *pt2 = pt - 1; - immVertex3fv(pos, &pt2->x); - draw_points++; - } - } - - /* now the point we want */ - immVertex3fv(pos, &pt->x); - draw_points++; - - if (cyclic && i == 0) { - /* save first point to use in cyclic */ - copy_v3_v3(cyclic_fpt, &pt->x); - } - } - - if (cyclic) { - /* draw line to first point to complete the cycle */ - immVertex3fv(pos, cyclic_fpt); - draw_points++; - } - - /* if less of two points, need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - immVertex3fv(pos, &pt2->x); - } - - immEnd(); - immUnbindProgram(); + float curpressure = points[0].pressure; + float cyclic_fpt[3]; + int draw_points = 0; + + /* if cyclic needs one vertex more */ + int cyclic_add = 0; + if (cyclic) { + cyclic_add++; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + /* draw stroke curve */ + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + for (int i = 0; i < totpoints; i++, pt++) { + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, + * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) + * Note: we want more visible levels of pressures when thickness is bigger. + */ + if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { + /* if the pressure changes before get at least 2 vertices, + * need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + immEnd(); + draw_points = 0; + + curpressure = pt->pressure; + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + draw_points++; + } + } + + /* now the point we want */ + immVertex3fv(pos, &pt->x); + draw_points++; + + if (cyclic && i == 0) { + /* save first point to use in cyclic */ + copy_v3_v3(cyclic_fpt, &pt->x); + } + } + + if (cyclic) { + /* draw line to first point to complete the cycle */ + immVertex3fv(pos, cyclic_fpt); + draw_points++; + } + + /* if less of two points, need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + + immEnd(); + immUnbindProgram(); } /* ----- Fancy 2D-Stroke Drawing ------ */ /* draw a given stroke in 2d */ -static void gp_draw_stroke_2d( - const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - int offsx, int offsy, int winx, int winy, const float ink[4]) +static void gp_draw_stroke_2d(const bGPDspoint *points, + int totpoints, + short thickness_s, + short dflag, + short sflag, + int offsx, + int offsy, + int winx, + int winy, + const float ink[4]) { - /* otherwise thickness is twice that of the 3D view */ - float thickness = (float)thickness_s * 0.5f; - - /* strokes in Image Editor need a scale factor, since units there are not pixels! */ - float scalefac = 1.0f; - if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { - scalefac = 0.001f; - } - - /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection - * edges rotated to minimize shrinking artifacts, and rounded endcaps - */ - { - const bGPDspoint *pt1, *pt2; - float s0[2], s1[2]; /* segment 'center' points */ - float pm[2]; /* normal from previous segment. */ - int i; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor3fvAlpha(ink, ink[3]); - immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); - - /* get x and y coordinates from first point */ - gp_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0); - - for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { - float t0[2], t1[2]; /* tessellated coordinates */ - float m1[2], m2[2]; /* gradient and normal */ - float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ - float pthick; /* thickness at segment point */ - - /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ - gp_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1); - - /* calculate gradient and normal - 'angle'=(ny/nx) */ - m1[1] = s1[1] - s0[1]; - m1[0] = s1[0] - s0[0]; - normalize_v2(m1); - m2[1] = -m1[0]; - m2[0] = m1[1]; - - /* always use pressure from first point here */ - pthick = (pt1->pressure * thickness * scalefac); - - /* if the first segment, start of segment is segment's normal */ - if (i == 0) { - /* draw start cap first - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s0[0] - (m1[0] * pthick * 0.75f); - sc[1] = s0[1] - (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* First two points of cap. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - - /* calculate points for start of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; - - /* Last two points of start cap (and first two points of first segment). */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - } - /* if not the first segment, use bisector of angle between segments */ - else { - float mb[2]; /* bisector normal */ - float athick, dfac; /* actual thickness, difference between thicknesses */ - - /* calculate gradient of bisector (as average of normals) */ - mb[0] = (pm[0] + m2[0]) / 2; - mb[1] = (pm[1] + m2[1]) / 2; - normalize_v2(mb); - - /* calculate gradient to apply - * - as basis, use just pthick * bisector gradient - * - if cross-section not as thick as it should be, add extra padding to fix it - */ - mt[0] = mb[0] * pthick; - mt[1] = mb[1] * pthick; - athick = len_v2(mt); - dfac = pthick - (athick * 2); - - if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { - mt[0] += (mb[0] * dfac); - mt[1] += (mb[1] * dfac); - } - - /* calculate points for start of segment */ - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; - - /* Last two points of previous segment, and first two points of current segment. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - } - - /* if last segment, also draw end of segment (defined as segment's normal) */ - if (i == totpoints - 2) { - /* for once, we use second point's pressure (otherwise it won't be drawn) */ - pthick = (pt2->pressure * thickness * scalefac); - - /* calculate points for end of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s1[0] - mt[0]; - t0[1] = s1[1] - mt[1]; - t1[0] = s1[0] + mt[0]; - t1[1] = s1[1] + mt[1]; - - /* Last two points of last segment (and first two points of end cap). */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - - /* draw end cap as last step - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s1[0] + (m1[0] * pthick * 0.75f); - sc[1] = s1[1] + (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* Last two points of end cap. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - } - - /* store computed point2 coordinates as point1 ones of next segment. */ - copy_v2_v2(s0, s1); - /* store stroke's 'natural' normal for next stroke to use */ - copy_v2_v2(pm, m2); - } - - immEnd(); - immUnbindProgram(); - } + /* otherwise thickness is twice that of the 3D view */ + float thickness = (float)thickness_s * 0.5f; + + /* strokes in Image Editor need a scale factor, since units there are not pixels! */ + float scalefac = 1.0f; + if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { + scalefac = 0.001f; + } + + /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection + * edges rotated to minimize shrinking artifacts, and rounded endcaps + */ + { + const bGPDspoint *pt1, *pt2; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ + int i; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); + + /* get x and y coordinates from first point */ + gp_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0); + + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { + float t0[2], t1[2]; /* tessellated coordinates */ + float m1[2], m2[2]; /* gradient and normal */ + float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ + float pthick; /* thickness at segment point */ + + /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ + gp_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1); + + /* calculate gradient and normal - 'angle'=(ny/nx) */ + m1[1] = s1[1] - s0[1]; + m1[0] = s1[0] - s0[0]; + normalize_v2(m1); + m2[1] = -m1[0]; + m2[0] = m1[1]; + + /* always use pressure from first point here */ + pthick = (pt1->pressure * thickness * scalefac); + + /* if the first segment, start of segment is segment's normal */ + if (i == 0) { + /* draw start cap first + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s0[0] - (m1[0] * pthick * 0.75f); + sc[1] = s0[1] - (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* First two points of cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* calculate points for start of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of start cap (and first two points of first segment). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + /* if not the first segment, use bisector of angle between segments */ + else { + float mb[2]; /* bisector normal */ + float athick, dfac; /* actual thickness, difference between thicknesses */ + + /* calculate gradient of bisector (as average of normals) */ + mb[0] = (pm[0] + m2[0]) / 2; + mb[1] = (pm[1] + m2[1]) / 2; + normalize_v2(mb); + + /* calculate gradient to apply + * - as basis, use just pthick * bisector gradient + * - if cross-section not as thick as it should be, add extra padding to fix it + */ + mt[0] = mb[0] * pthick; + mt[1] = mb[1] * pthick; + athick = len_v2(mt); + dfac = pthick - (athick * 2); + + if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { + mt[0] += (mb[0] * dfac); + mt[1] += (mb[1] * dfac); + } + + /* calculate points for start of segment */ + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of previous segment, and first two points of current segment. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* if last segment, also draw end of segment (defined as segment's normal) */ + if (i == totpoints - 2) { + /* for once, we use second point's pressure (otherwise it won't be drawn) */ + pthick = (pt2->pressure * thickness * scalefac); + + /* calculate points for end of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s1[0] - mt[0]; + t0[1] = s1[1] - mt[1]; + t1[0] = s1[0] + mt[0]; + t1[1] = s1[1] + mt[1]; + + /* Last two points of last segment (and first two points of end cap). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* draw end cap as last step + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s1[0] + (m1[0] * pthick * 0.75f); + sc[1] = s1[1] + (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* Last two points of end cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); + /* store stroke's 'natural' normal for next stroke to use */ + copy_v2_v2(pm, m2); + } + + immEnd(); + immUnbindProgram(); + } } /* ----- Strokes Drawing ------ */ @@ -492,484 +509,507 @@ static void gp_draw_stroke_2d( /* Helper for doing all the checks on whether a stroke can be drawn */ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) { - /* skip stroke if it isn't in the right display space for this drawing context */ - /* 1) 3D Strokes */ - if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) - return false; - - /* 2) Screen Space 2D Strokes */ - if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) - return false; - - /* 3) Image Space (2D) */ - if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) - return false; - - /* skip stroke if it doesn't have any valid data */ - if ((gps->points == NULL) || (gps->totpoints < 1)) - return false; - - /* stroke can be drawn */ - return true; + /* skip stroke if it isn't in the right display space for this drawing context */ + /* 1) 3D Strokes */ + if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) + return false; + + /* 2) Screen Space 2D Strokes */ + if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) + return false; + + /* 3) Image Space (2D) */ + if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) + return false; + + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1)) + return false; + + /* stroke can be drawn */ + return true; } /* draw a set of strokes */ -static void gp_draw_strokes( - bGPdata *UNUSED(gpd), bGPDlayer *UNUSED(gpl), const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int dflag, short lthick, const float color[4]) +static void gp_draw_strokes(bGPdata *UNUSED(gpd), + bGPDlayer *UNUSED(gpl), + const bGPDframe *gpf, + int offsx, + int offsy, + int winx, + int winy, + int dflag, + short lthick, + const float color[4]) { - GPU_enable_program_point_size(); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) { - continue; - } - - /* check which stroke-drawer to use */ - if (dflag & GP_DRAWDATA_ONLY3D) { - const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); - int mask_orig = 0; - - if (no_xray) { - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); - glDepthMask(0); - GPU_depth_test(true); - - /* first arg is normally rv3d->dist, but this isn't - * available here and seems to work quite well without */ - bglPolygonOffset(1.0f, 1.0f); - } - - /* 3D Lines - OpenGL primitives-based */ - if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); - } - else { - gp_draw_stroke_3d( - gps->points, gps->totpoints, lthick, gps->flag, - color, gps->flag & GP_STROKE_CYCLIC); - } - - if (no_xray) { - glDepthMask(mask_orig); - GPU_depth_test(false); - - bglPolygonOffset(0.0, 0.0); - } - } - else { - /* 2D Strokes... */ - if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); - } - else { - gp_draw_stroke_2d( - gps->points, gps->totpoints, lthick, dflag, gps->flag, - offsx, offsy, winx, winy, color); - } - } - } - - GPU_disable_program_point_size(); + GPU_enable_program_point_size(); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) { + continue; + } + + /* check which stroke-drawer to use */ + if (dflag & GP_DRAWDATA_ONLY3D) { + const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); + int mask_orig = 0; + + if (no_xray) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + + /* 3D Lines - OpenGL primitives-based */ + if (gps->totpoints == 1) { + gp_draw_stroke_point( + gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_3d( + gps->points, gps->totpoints, lthick, gps->flag, color, gps->flag & GP_STROKE_CYCLIC); + } + + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); + } + } + else { + /* 2D Strokes... */ + if (gps->totpoints == 1) { + gp_draw_stroke_point( + gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_2d(gps->points, + gps->totpoints, + lthick, + dflag, + gps->flag, + offsx, + offsy, + winx, + winy, + color); + } + } + } + + GPU_disable_program_point_size(); } /* Draw selected verts for strokes being edited */ -static void gp_draw_strokes_edit( - bGPdata *gpd, bGPDlayer *gpl, const bGPDframe *gpf, - int offsx, int offsy, int winx, int winy, - short dflag, short UNUSED(lflag), float alpha) +static void gp_draw_strokes_edit(bGPdata *gpd, + bGPDlayer *gpl, + const bGPDframe *gpf, + int offsx, + int offsy, + int winx, + int winy, + short dflag, + short UNUSED(lflag), + float alpha) { - /* if alpha 0 do not draw */ - if (alpha == 0.0f) - return; - - const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; - int mask_orig = 0; - - /* set up depth masks... */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); - glDepthMask(0); - GPU_depth_test(true); - - /* first arg is normally rv3d->dist, but this isn't - * available here and seems to work quite well without */ - bglPolygonOffset(1.0f, 1.0f); - } - } - - GPU_enable_program_point_size(); - - /* draw stroke verts */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) - continue; - - /* Optimisation: only draw points for selected strokes - * We assume that selected points can only occur in - * strokes that are selected too. - */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - /* Get size of verts: - * - The selected state needs to be larger than the unselected state so that - * they stand out more. - * - We use the theme setting for size of the unselected verts - */ - float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); - float vsize; - if ((int)bsize > 8) { - vsize = 10.0f; - bsize = 8.0f; - } - else { - vsize = bsize + 2; - } - - float selectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); - selectColor[3] = alpha; - - GPUVertFormat *format = immVertexFormat(); - uint pos; /* specified later */ - uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - if (gps->flag & GP_STROKE_3DSPACE) { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); - } - else { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); - } - - immBegin(GPU_PRIM_POINTS, gps->totpoints); - - /* Draw start and end point differently if enabled stroke direction hint */ - bool show_direction_hint = (gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1); - - /* Draw all the stroke points (selected or not) */ - bGPDspoint *pt = gps->points; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* size and color first */ - if (show_direction_hint && i == 0) { - /* start point in green bigger */ - immAttr3f(color, 0.0f, 1.0f, 0.0f); - immAttr1f(size, vsize + 4); - } - else if (show_direction_hint && (i == gps->totpoints - 1)) { - /* end point in red smaller */ - immAttr3f(color, 1.0f, 0.0f, 0.0f); - immAttr1f(size, vsize + 1); - } - else if (pt->flag & GP_SPOINT_SELECT) { - immAttr3fv(color, selectColor); - immAttr1f(size, vsize); - } - else { - immAttr3fv(color, gpl->color); - immAttr1f(size, bsize); - } - - /* then position */ - if (gps->flag & GP_STROKE_3DSPACE) { - immVertex3fv(pos, &pt->x); - } - else { - float co[2]; - gp_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); - immVertex2fv(pos, co); - } - } - - immEnd(); - immUnbindProgram(); - } - - GPU_disable_program_point_size(); - - /* clear depth mask */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glDepthMask(mask_orig); - GPU_depth_test(false); - - bglPolygonOffset(0.0, 0.0); + /* if alpha 0 do not draw */ + if (alpha == 0.0f) + return; + + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; + int mask_orig = 0; + + /* set up depth masks... */ + if (dflag & GP_DRAWDATA_ONLY3D) { + if (no_xray) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + } + + GPU_enable_program_point_size(); + + /* draw stroke verts */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) + continue; + + /* Optimisation: only draw points for selected strokes + * We assume that selected points can only occur in + * strokes that are selected too. + */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + /* Get size of verts: + * - The selected state needs to be larger than the unselected state so that + * they stand out more. + * - We use the theme setting for size of the unselected verts + */ + float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); + float vsize; + if ((int)bsize > 8) { + vsize = 10.0f; + bsize = 8.0f; + } + else { + vsize = bsize + 2; + } + + float selectColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); + selectColor[3] = alpha; + + GPUVertFormat *format = immVertexFormat(); + uint pos; /* specified later */ + uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (gps->flag & GP_STROKE_3DSPACE) { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); + } + else { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); + } + + immBegin(GPU_PRIM_POINTS, gps->totpoints); + + /* Draw start and end point differently if enabled stroke direction hint */ + bool show_direction_hint = (gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1); + + /* Draw all the stroke points (selected or not) */ + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { + /* size and color first */ + if (show_direction_hint && i == 0) { + /* start point in green bigger */ + immAttr3f(color, 0.0f, 1.0f, 0.0f); + immAttr1f(size, vsize + 4); + } + else if (show_direction_hint && (i == gps->totpoints - 1)) { + /* end point in red smaller */ + immAttr3f(color, 1.0f, 0.0f, 0.0f); + immAttr1f(size, vsize + 1); + } + else if (pt->flag & GP_SPOINT_SELECT) { + immAttr3fv(color, selectColor); + immAttr1f(size, vsize); + } + else { + immAttr3fv(color, gpl->color); + immAttr1f(size, bsize); + } + + /* then position */ + if (gps->flag & GP_STROKE_3DSPACE) { + immVertex3fv(pos, &pt->x); + } + else { + float co[2]; + gp_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + } + } + + immEnd(); + immUnbindProgram(); + } + + GPU_disable_program_point_size(); + + /* clear depth mask */ + if (dflag & GP_DRAWDATA_ONLY3D) { + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); #if 0 - glDisable(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(0, 0); + glDisable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(0, 0); #endif - } - } + } + } } /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ -static void gp_draw_onionskins( - bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag) +static void gp_draw_onionskins(bGPdata *gpd, + bGPDlayer *gpl, + bGPDframe *gpf, + int offsx, + int offsy, + int winx, + int winy, + int UNUSED(cfra), + int dflag) { - const float alpha = 1.0f; - float color[4]; - - /* 1) Draw Previous Frames First */ - copy_v3_v3(color, gpl->gcolor_prev); - - if (gpl->gstep > 0) { - bGPDframe *gf; - float fac; - - /* draw previous frames first */ - for (gf = gpf->prev; gf; gf = gf->prev) { - /* check if frame is drawable */ - if ((gpf->framenum - gf->framenum) <= gpl->gstep) { - /* alpha decreases with distance from curframe index */ - fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); - color[3] = alpha * fac * 0.66f; - gp_draw_strokes( - gpd, gpl, gf, offsx, offsy, winx, winy, dflag, - gpl->thickness, color); - } - else - break; - } - } - else if (gpl->gstep == 0) { - /* draw the strokes for the ghost frames (at half of the alpha set by user) */ - if (gpf->prev) { - color[3] = (alpha / 7); - gp_draw_strokes( - gpd, gpl, gpf->prev, offsx, offsy, winx, winy, dflag, - gpl->thickness, color); - } - } - else { - /* don't draw - disabled */ - } - - - /* 2) Now draw next frames */ - copy_v3_v3(color, gpl->gcolor_next); - - if (gpl->gstep_next > 0) { - bGPDframe *gf; - float fac; - - /* now draw next frames */ - for (gf = gpf->next; gf; gf = gf->next) { - /* check if frame is drawable */ - if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) { - /* alpha decreases with distance from curframe index */ - fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); - color[3] = alpha * fac * 0.66f; - gp_draw_strokes( - gpd, gpl, gf, offsx, offsy, winx, winy, dflag, - gpl->thickness, color); - } - else - break; - } - } - else if (gpl->gstep_next == 0) { - /* draw the strokes for the ghost frames (at half of the alpha set by user) */ - if (gpf->next) { - color[3] = (alpha / 4); - gp_draw_strokes( - gpd, gpl, gpf->next, offsx, offsy, winx, winy, dflag, - gpl->thickness, color); - } - } - else { - /* don't draw - disabled */ - } - + const float alpha = 1.0f; + float color[4]; + + /* 1) Draw Previous Frames First */ + copy_v3_v3(color, gpl->gcolor_prev); + + if (gpl->gstep > 0) { + bGPDframe *gf; + float fac; + + /* draw previous frames first */ + for (gf = gpf->prev; gf; gf = gf->prev) { + /* check if frame is drawable */ + if ((gpf->framenum - gf->framenum) <= gpl->gstep) { + /* alpha decreases with distance from curframe index */ + fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); + color[3] = alpha * fac * 0.66f; + gp_draw_strokes(gpd, gpl, gf, offsx, offsy, winx, winy, dflag, gpl->thickness, color); + } + else + break; + } + } + else if (gpl->gstep == 0) { + /* draw the strokes for the ghost frames (at half of the alpha set by user) */ + if (gpf->prev) { + color[3] = (alpha / 7); + gp_draw_strokes(gpd, gpl, gpf->prev, offsx, offsy, winx, winy, dflag, gpl->thickness, color); + } + } + else { + /* don't draw - disabled */ + } + + /* 2) Now draw next frames */ + copy_v3_v3(color, gpl->gcolor_next); + + if (gpl->gstep_next > 0) { + bGPDframe *gf; + float fac; + + /* now draw next frames */ + for (gf = gpf->next; gf; gf = gf->next) { + /* check if frame is drawable */ + if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) { + /* alpha decreases with distance from curframe index */ + fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); + color[3] = alpha * fac * 0.66f; + gp_draw_strokes(gpd, gpl, gf, offsx, offsy, winx, winy, dflag, gpl->thickness, color); + } + else + break; + } + } + else if (gpl->gstep_next == 0) { + /* draw the strokes for the ghost frames (at half of the alpha set by user) */ + if (gpf->next) { + color[3] = (alpha / 4); + gp_draw_strokes(gpd, gpl, gpf->next, offsx, offsy, winx, winy, dflag, gpl->thickness, color); + } + } + else { + /* don't draw - disabled */ + } } /* loop over gpencil data layers, drawing them */ static void gp_draw_data_layers( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, - int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) { - float ink[4]; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* verify never thickness is less than 1 */ - CLAMP_MIN(gpl->thickness, 1.0f); - short lthick = gpl->thickness; - - /* apply layer opacity */ - copy_v3_v3(ink, gpl->color); - ink[3] = gpl->opacity; - - /* don't draw layer if hidden */ - if (gpl->flag & GP_LAYER_HIDE) - continue; - - /* get frame to draw */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV); - if (gpf == NULL) - continue; - - /* set basic stroke thickness */ - GPU_line_width(lthick); - - /* Add layer drawing settings to the set of "draw flags" - * NOTE: If the setting doesn't apply, it *must* be cleared, - * as dflag's carry over from the previous layer - */ - - /* xray... */ - SET_FLAG_FROM_TEST(dflag, gpl->flag & GP_LAYER_NO_XRAY, GP_DRAWDATA_NO_XRAY); - - /* Draw 'onionskins' (frame left + right) */ - if (gpl->onion_flag & GP_LAYER_ONIONSKIN) { - gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag); - } - - /* draw the strokes already in active frame */ - gp_draw_strokes(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, lthick, ink); - - /* Draw verts of selected strokes - * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering - * - locked layers can't be edited, so there's no point showing these verts - * as they will have no bearings on what gets edited - * - only show when in editmode, since operators shouldn't work otherwise - * (NOTE: doing it this way means that the toggling editmode shows visible change immediately) - */ - /* XXX: perhaps we don't want to show these when users are drawing... */ - if ((G.f & G_FLAG_RENDER_VIEWPORT) == 0 && - (gpl->flag & GP_LAYER_LOCKED) == 0 && - (gpd->flag & GP_DATA_STROKE_EDITMODE)) - { - gp_draw_strokes_edit(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, alpha); - } - - /* Check if may need to draw the active stroke cache, only if this layer is the active layer - * that is being edited. (Stroke buffer is currently stored in gp-data) - */ - if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) && - (gpf->flag & GP_FRAME_PAINT)) - { - /* Buffer stroke needs to be drawn with a different linestyle - * to help differentiate them from normal strokes. - * - * It should also be noted that sbuffer contains temporary point types - * i.e. tGPspoints NOT bGPDspoints - */ - gp_draw_stroke_buffer( - gpd->runtime.sbuffer, - gpd->runtime.sbuffer_size, lthick, - dflag, gpd->runtime.sbuffer_sflag, ink); - } - } + float ink[4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* verify never thickness is less than 1 */ + CLAMP_MIN(gpl->thickness, 1.0f); + short lthick = gpl->thickness; + + /* apply layer opacity */ + copy_v3_v3(ink, gpl->color); + ink[3] = gpl->opacity; + + /* don't draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV); + if (gpf == NULL) + continue; + + /* set basic stroke thickness */ + GPU_line_width(lthick); + + /* Add layer drawing settings to the set of "draw flags" + * NOTE: If the setting doesn't apply, it *must* be cleared, + * as dflag's carry over from the previous layer + */ + + /* xray... */ + SET_FLAG_FROM_TEST(dflag, gpl->flag & GP_LAYER_NO_XRAY, GP_DRAWDATA_NO_XRAY); + + /* Draw 'onionskins' (frame left + right) */ + if (gpl->onion_flag & GP_LAYER_ONIONSKIN) { + gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag); + } + + /* draw the strokes already in active frame */ + gp_draw_strokes(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, lthick, ink); + + /* Draw verts of selected strokes + * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering + * - locked layers can't be edited, so there's no point showing these verts + * as they will have no bearings on what gets edited + * - only show when in editmode, since operators shouldn't work otherwise + * (NOTE: doing it this way means that the toggling editmode shows visible change immediately) + */ + /* XXX: perhaps we don't want to show these when users are drawing... */ + if ((G.f & G_FLAG_RENDER_VIEWPORT) == 0 && (gpl->flag & GP_LAYER_LOCKED) == 0 && + (gpd->flag & GP_DATA_STROKE_EDITMODE)) { + gp_draw_strokes_edit(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, alpha); + } + + /* Check if may need to draw the active stroke cache, only if this layer is the active layer + * that is being edited. (Stroke buffer is currently stored in gp-data) + */ + if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) && + (gpf->flag & GP_FRAME_PAINT)) { + /* Buffer stroke needs to be drawn with a different linestyle + * to help differentiate them from normal strokes. + * + * It should also be noted that sbuffer contains temporary point types + * i.e. tGPspoints NOT bGPDspoints + */ + gp_draw_stroke_buffer(gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, + lthick, + dflag, + gpd->runtime.sbuffer_sflag, + ink); + } + } } /* draw a short status message in the top-right corner */ static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) { - rcti rect; + rcti rect; - /* Cannot draw any status text when drawing OpenGL Renders */ - if (G.f & G_FLAG_RENDER_VIEWPORT) - return; + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_FLAG_RENDER_VIEWPORT) + return; - /* Get bounds of region - Necessary to avoid problems with region overlap */ - ED_region_visible_rect(ar, &rect); + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); - /* for now, this should only be used to indicate when we are in stroke editmode */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - const char *printable = IFACE_("GPencil Stroke Editing"); - float printable_size[2]; + /* for now, this should only be used to indicate when we are in stroke editmode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + const char *printable = IFACE_("GPencil Stroke Editing"); + float printable_size[2]; - int font_id = BLF_default(); + int font_id = BLF_default(); - BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + BLF_width_and_height( + font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect.ymax - U.widget_unit); + int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + int yco = (rect.ymax - U.widget_unit); - /* text label */ - UI_FontThemeColor(font_id, TH_TEXT_HI); + /* text label */ + UI_FontThemeColor(font_id, TH_TEXT_HI); #ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #else - BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #endif - /* grease pencil icon... */ - // XXX: is this too intrusive? - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + /* grease pencil icon... */ + // XXX: is this too intrusive? + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); - xco -= U.widget_unit; - yco -= (int)printable_size[1] / 2; + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; - UI_icon_draw(xco, yco, ICON_GREASEPENCIL); + UI_icon_draw(xco, yco, ICON_GREASEPENCIL); - GPU_blend(false); - } + GPU_blend(false); + } } /* draw grease-pencil datablock */ static void gp_draw_data( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, - int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) { - /* turn on smooth lines (i.e. anti-aliasing) */ - GPU_line_smooth(true); + /* turn on smooth lines (i.e. anti-aliasing) */ + GPU_line_smooth(true); - /* turn on alpha-blending */ - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + /* turn on alpha-blending */ + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); - /* draw! */ - gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + /* draw! */ + gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); - /* turn off alpha blending, then smooth lines */ - GPU_blend(false); // alpha blending - GPU_line_smooth(false); // smooth lines + /* turn off alpha blending, then smooth lines */ + GPU_blend(false); // alpha blending + GPU_line_smooth(false); // smooth lines } /* if we have strokes for scenes (3d view)/clips (movie clip editor) * and objects/tracks, multiple data blocks have to be drawn */ -static void gp_draw_data_all( - Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, - int cfra, int dflag, const char spacetype) +static void gp_draw_data_all(Scene *scene, + bGPdata *gpd, + int offsx, + int offsy, + int winx, + int winy, + int cfra, + int dflag, + const char spacetype) { - bGPdata *gpd_source = NULL; - float alpha = 1.0f; - - if (scene) { - if (spacetype == SPACE_VIEW3D) { - gpd_source = (scene->gpd ? scene->gpd : NULL); - } - else if (spacetype == SPACE_CLIP && scene->clip) { - /* currently drawing only gpencil data from either clip or track, - * but not both - XXX fix logic behind */ - gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); - } - - if (gpd_source) { - gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); - } - } - - /* scene/clip data has already been drawn, only object/track data is drawn here - * if gpd_source == gpd, we don't have any object/track data and we can skip */ - if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { - gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); - } + bGPdata *gpd_source = NULL; + float alpha = 1.0f; + + if (scene) { + if (spacetype == SPACE_VIEW3D) { + gpd_source = (scene->gpd ? scene->gpd : NULL); + } + else if (spacetype == SPACE_CLIP && scene->clip) { + /* currently drawing only gpencil data from either clip or track, + * but not both - XXX fix logic behind */ + gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); + } + + if (gpd_source) { + gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); + } + } + + /* scene/clip data has already been drawn, only object/track data is drawn here + * if gpd_source == gpd, we don't have any object/track data and we can skip */ + if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { + gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + } } /* ----- Grease Pencil Sketches Drawing API ------ */ @@ -983,66 +1023,67 @@ static void gp_draw_data_all( /* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ void ED_annotation_draw_2dimage(const bContext *C) { - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - - int offsx, offsy, sizex, sizey; - int dflag = GP_DRAWDATA_NOSTATUS; - - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX - if (gpd == NULL) return; - - /* calculate rect */ - switch (sa->spacetype) { - case SPACE_IMAGE: /* image */ - case SPACE_CLIP: /* clip */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); - - dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; - break; - } - case SPACE_SEQ: /* sequence */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated - * and everything moved to standard View2d - */ - dflag |= GP_DRAWDATA_ONLYV2D; - break; - } - default: /* for spacetype not yet handled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - dflag |= GP_DRAWDATA_ONLYI2D; - break; - } - - if (ED_screen_animation_playing(wm)) { - /* Don't show onion-skins during animation playback/scrub (i.e. it obscures the poses) - * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes). */ - dflag |= GP_DRAWDATA_NO_ONIONS; - } - - /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + + int offsx, offsy, sizex, sizey; + int dflag = GP_DRAWDATA_NOSTATUS; + + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) + return; + + /* calculate rect */ + switch (sa->spacetype) { + case SPACE_IMAGE: /* image */ + case SPACE_CLIP: /* clip */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); + + dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; + break; + } + case SPACE_SEQ: /* sequence */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated + * and everything moved to standard View2d + */ + dflag |= GP_DRAWDATA_ONLYV2D; + break; + } + default: /* for spacetype not yet handled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + dflag |= GP_DRAWDATA_ONLYI2D; + break; + } + + if (ED_screen_animation_playing(wm)) { + /* Don't show onion-skins during animation playback/scrub (i.e. it obscures the poses) + * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes). */ + dflag |= GP_DRAWDATA_NO_ONIONS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); } /* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly @@ -1050,95 +1091,96 @@ void ED_annotation_draw_2dimage(const bContext *C) * second time with onlyv2d=false for screen-aligned strokes */ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) { - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - int dflag = 0; - - /* check that we have grease-pencil stuff to draw */ - if (sa == NULL) return; - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX - if (gpd == NULL) return; - - /* special hack for Image Editor */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) - dflag |= GP_DRAWDATA_IEDITHACK; - - /* draw it! */ - if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); - if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS; - - gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); - - /* draw status text (if in screen/pixel-space) */ - if (!onlyv2d) { - gp_draw_status_text(gpd, ar); - } + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + int dflag = 0; + + /* check that we have grease-pencil stuff to draw */ + if (sa == NULL) + return; + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) + return; + + /* special hack for Image Editor */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) + dflag |= GP_DRAWDATA_IEDITHACK; + + /* draw it! */ + if (onlyv2d) + dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); + if (ED_screen_animation_playing(wm)) + dflag |= GP_DRAWDATA_NO_ONIONS; + + gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (!onlyv2d) { + gp_draw_status_text(gpd, ar); + } } - /* draw annotations sketches to specified 3d-view assuming that matrices are already set correctly * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, * second time with only3d=false for screen-aligned strokes */ void ED_annotation_draw_view3d( - Scene *scene, struct Depsgraph *depsgraph, - View3D *v3d, ARegion *ar, - bool only3d) + Scene *scene, struct Depsgraph *depsgraph, View3D *v3d, ARegion *ar, bool only3d) { - int dflag = 0; - RegionView3D *rv3d = ar->regiondata; - int offsx, offsy, winx, winy; - - /* check that we have grease-pencil stuff to draw */ - /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */ - bGPdata *gpd = scene->gpd; - if (gpd == NULL) return; - - /* when rendering to the offscreen buffer we don't want to - * deal with the camera border, otherwise map the coords to the camera border. */ - if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_FLAG_RENDER_VIEWPORT)) { - rctf rectf; - ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ - - offsx = round_fl_to_int(rectf.xmin); - offsy = round_fl_to_int(rectf.ymin); - winx = round_fl_to_int(rectf.xmax - rectf.xmin); - winy = round_fl_to_int(rectf.ymax - rectf.ymin); - } - else { - offsx = 0; - offsy = 0; - winx = ar->winx; - winy = ar->winy; - } - - /* set flags */ - if (only3d) { - /* 3D strokes/3D space: - * - only 3D space points - * - don't status text either (as it's the wrong space) - */ - dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); - } - - if (v3d->flag2 & V3D_HIDE_OVERLAYS) { - /* don't draw status text when "only render" flag is set */ - dflag |= GP_DRAWDATA_NOSTATUS; - } - - /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + int dflag = 0; + RegionView3D *rv3d = ar->regiondata; + int offsx, offsy, winx, winy; + + /* check that we have grease-pencil stuff to draw */ + /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */ + bGPdata *gpd = scene->gpd; + if (gpd == NULL) + return; + + /* when rendering to the offscreen buffer we don't want to + * deal with the camera border, otherwise map the coords to the camera border. */ + if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_FLAG_RENDER_VIEWPORT)) { + rctf rectf; + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ + + offsx = round_fl_to_int(rectf.xmin); + offsy = round_fl_to_int(rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); + } + else { + offsx = 0; + offsy = 0; + winx = ar->winx; + winy = ar->winy; + } + + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_HIDE_OVERLAYS) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); } void ED_annotation_draw_ex( - Scene *scene, bGPdata *gpd, - int winx, int winy, const int cfra, const char spacetype) + Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) { - int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; + int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; - gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); + gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); } /* ************************************************** */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index bfbf319b1c6..4933f081790 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <stddef.h> #include <stdlib.h> @@ -61,7 +60,6 @@ #include "ED_view3d.h" #include "ED_clip.h" - #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_state.h" @@ -81,132 +79,131 @@ /* values for tGPsdata->status */ typedef enum eGPencil_PaintStatus { - GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ - GP_STATUS_PAINTING, /* a stroke is in progress */ - GP_STATUS_ERROR, /* something wasn't correctly set up */ - GP_STATUS_DONE, /* painting done */ - GP_STATUS_CAPTURE /* capture event, but cancel */ + GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ + GP_STATUS_PAINTING, /* a stroke is in progress */ + GP_STATUS_ERROR, /* something wasn't correctly set up */ + GP_STATUS_DONE, /* painting done */ + GP_STATUS_CAPTURE /* capture event, but cancel */ } eGPencil_PaintStatus; /* Return flags for adding points to stroke buffer */ typedef enum eGP_StrokeAdd_Result { - GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ - GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ - GP_STROKEADD_NORMAL, /* point was successfully added */ - GP_STROKEADD_FULL, /* cannot add any more points to buffer */ + GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ + GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ + GP_STROKEADD_NORMAL, /* point was successfully added */ + GP_STROKEADD_FULL, /* cannot add any more points to buffer */ } eGP_StrokeAdd_Result; /* Runtime flags */ typedef enum eGPencil_PaintFlags { - GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ - GP_PAINTFLAG_STROKEADDED = (1 << 1), - GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), - GP_PAINTFLAG_SELECTMASK = (1 << 3), + GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ + GP_PAINTFLAG_STROKEADDED = (1 << 1), + GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), + GP_PAINTFLAG_SELECTMASK = (1 << 3), } eGPencil_PaintFlags; - /* Temporary 'Stroke' Operation data * "p" = op->customdata */ typedef struct tGPsdata { - Main *bmain; - /** current scene from context. */ - Scene *scene; - struct Depsgraph *depsgraph; - - /** window where painting originated. */ - wmWindow *win; - /** area where painting originated. */ - ScrArea *sa; - /** region where painting originated. */ - ARegion *ar; - /** needed for GP_STROKE_2DSPACE. */ - View2D *v2d; - /** for using the camera rect within the 3d view. */ - rctf *subrect; - rctf subrect_data; - - /** settings to pass to gp_points_to_xy(). */ - GP_SpaceConversion gsc; - - /** pointer to owner of gp-datablock. */ - PointerRNA ownerPtr; - /** gp-datablock layer comes from. */ - bGPdata *gpd; - /** layer we're working on. */ - bGPDlayer *gpl; - /** frame we're working on. */ - bGPDframe *gpf; - - /** projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ - char *align_flag; - - /** current status of painting. */ - eGPencil_PaintStatus status; - /** mode for painting. */ - eGPencil_PaintModes paintmode; - /** flags that can get set during runtime (eGPencil_PaintFlags) */ - eGPencil_PaintFlags flags; - - /** radius of influence for eraser. */ - short radius; - - /** current mouse-position. */ - float mval[2]; - /** previous recorded mouse-position. */ - float mvalo[2]; - - /** current stylus pressure. */ - float pressure; - /** previous stylus pressure. */ - float opressure; - - /* These need to be doubles, as (at least under unix) they are in seconds since epoch, - * float (and its 7 digits precision) is definitively not enough here! - * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. - */ - /** Used when converting to path. */ - double inittime; - /** Used when converting to path. */ - double curtime; - /** Used when converting to path. */ - double ocurtime; - - /** Inverted transformation matrix applying when converting coords from screen-space - * to region space. */ - float imat[4][4]; - float mat[4][4]; - - /** custom color - hack for enforcing a particular color for track/mask editing. */ - float custom_color[4]; - - /** radial cursor data for drawing eraser. */ - void *erasercursor; - - /** 1: line horizontal, 2: line vertical, other: not defined, second element position. */ - short straight[2]; - - /** key used for invoking the operator. */ - short keymodifier; + Main *bmain; + /** current scene from context. */ + Scene *scene; + struct Depsgraph *depsgraph; + + /** window where painting originated. */ + wmWindow *win; + /** area where painting originated. */ + ScrArea *sa; + /** region where painting originated. */ + ARegion *ar; + /** needed for GP_STROKE_2DSPACE. */ + View2D *v2d; + /** for using the camera rect within the 3d view. */ + rctf *subrect; + rctf subrect_data; + + /** settings to pass to gp_points_to_xy(). */ + GP_SpaceConversion gsc; + + /** pointer to owner of gp-datablock. */ + PointerRNA ownerPtr; + /** gp-datablock layer comes from. */ + bGPdata *gpd; + /** layer we're working on. */ + bGPDlayer *gpl; + /** frame we're working on. */ + bGPDframe *gpf; + + /** projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ + char *align_flag; + + /** current status of painting. */ + eGPencil_PaintStatus status; + /** mode for painting. */ + eGPencil_PaintModes paintmode; + /** flags that can get set during runtime (eGPencil_PaintFlags) */ + eGPencil_PaintFlags flags; + + /** radius of influence for eraser. */ + short radius; + + /** current mouse-position. */ + float mval[2]; + /** previous recorded mouse-position. */ + float mvalo[2]; + + /** current stylus pressure. */ + float pressure; + /** previous stylus pressure. */ + float opressure; + + /* These need to be doubles, as (at least under unix) they are in seconds since epoch, + * float (and its 7 digits precision) is definitively not enough here! + * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. + */ + /** Used when converting to path. */ + double inittime; + /** Used when converting to path. */ + double curtime; + /** Used when converting to path. */ + double ocurtime; + + /** Inverted transformation matrix applying when converting coords from screen-space + * to region space. */ + float imat[4][4]; + float mat[4][4]; + + /** custom color - hack for enforcing a particular color for track/mask editing. */ + float custom_color[4]; + + /** radial cursor data for drawing eraser. */ + void *erasercursor; + + /** 1: line horizontal, 2: line vertical, other: not defined, second element position. */ + short straight[2]; + + /** key used for invoking the operator. */ + short keymodifier; } tGPsdata; /* ------ */ /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTEN_PX (U.gp_manhattendist) /* minimum length of new segment before new point can be added */ -#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) +#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) static bool gp_stroke_added_check(tGPsdata *p) { - return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); + return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); } static void gp_stroke_added_enable(tGPsdata *p) { - BLI_assert(p->gpf->strokes.last != NULL); - p->flags |= GP_PAINTFLAG_STROKEADDED; + BLI_assert(p->gpf->strokes.last != NULL); + p->flags |= GP_PAINTFLAG_STROKEADDED; } /* ------ */ @@ -220,43 +217,42 @@ static void gp_session_validatebuffer(tGPsdata *p); /* check if context is suitable for drawing */ static bool gpencil_draw_poll(bContext *C) { - /* if is inside grease pencil draw mode cannot use annotations */ - Object *obact = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - if ((sa) && (sa->spacetype == SPACE_VIEW3D)) { - if ((obact) && (obact->type == OB_GPENCIL) && - (obact->mode == OB_MODE_PAINT_GPENCIL)) - { - CTX_wm_operator_poll_msg_set(C, "Annotation cannot be used in grease pencil draw mode"); - return false; - } - } - - if (ED_operator_regionactive(C)) { - /* check if current context can support GPencil data */ - if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { - /* check if Grease Pencil isn't already running */ - if (ED_gpencil_session_active() == 0) - return true; - else - CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active"); - } - else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); - } - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not set"); - } - - return false; + /* if is inside grease pencil draw mode cannot use annotations */ + Object *obact = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + if ((sa) && (sa->spacetype == SPACE_VIEW3D)) { + if ((obact) && (obact->type == OB_GPENCIL) && (obact->mode == OB_MODE_PAINT_GPENCIL)) { + CTX_wm_operator_poll_msg_set(C, "Annotation cannot be used in grease pencil draw mode"); + return false; + } + } + + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() == 0) + return true; + else + CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active"); + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return false; } /* check if projecting strokes into 3d-geometry in the 3D-View */ static bool gpencil_project_check(tGPsdata *p) { - bGPdata *gpd = p->gpd; - return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); + bGPdata *gpd = p->gpd; + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && + (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); } /* ******************************************* */ @@ -267,10 +263,10 @@ static bool gpencil_project_check(tGPsdata *p) /* get the reference point for stroke-point conversions */ static void gp_get_3d_reference(tGPsdata *p, float vec[3]) { - const float *fp = p->scene->cursor.location; + const float *fp = p->scene->cursor.location; - /* use 3D-cursor */ - copy_v3_v3(vec, fp); + /* use 3D-cursor */ + copy_v3_v3(vec, fp); } /* Stroke Editing ---------------------------- */ @@ -278,91 +274,92 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3]) /* check if the current mouse position is suitable for adding a new point */ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2]) { - int dx = (int)fabsf(mval[0] - pmval[0]); - int dy = (int)fabsf(mval[1] - pmval[1]); - - /* if buffer is empty, just let this go through (i.e. so that dots will work) */ - if (p->gpd->runtime.sbuffer_size == 0) - return true; - - /* check if mouse moved at least certain distance on both axes (best case) - * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand - */ - else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) - return true; - - /* check if the distance since the last point is significant enough - * - prevents points being added too densely - * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though - */ - else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) - return true; - - /* mouse 'didn't move' */ - else - return false; + int dx = (int)fabsf(mval[0] - pmval[0]); + int dy = (int)fabsf(mval[1] - pmval[1]); + + /* if buffer is empty, just let this go through (i.e. so that dots will work) */ + if (p->gpd->runtime.sbuffer_size == 0) + return true; + + /* check if mouse moved at least certain distance on both axes (best case) + * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand + */ + else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) + return true; + + /* check if the distance since the last point is significant enough + * - prevents points being added too densely + * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though + */ + else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) + return true; + + /* mouse 'didn't move' */ + else + return false; } /* convert screen-coordinates to buffer-coordinates */ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], float *depth) { - bGPdata *gpd = p->gpd; - - /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ - if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { - int mval_i[2]; - round_v2i_v2fl(mval_i, mval); - if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval_i, out, 0, depth))) { - /* projecting onto 3D-Geometry - * - nothing more needs to be done here, since view_autodist_simple() has already done it - */ - } - else { - float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; - - /* Current method just converts each point in screen-coordinates to - * 3D-coordinates using the 3D-cursor as reference. In general, this - * works OK, but it could of course be improved. - * - * TODO: - * - investigate using nearest point(s) on a previous stroke as - * reference point instead or as offset, for easier stroke matching - */ - - gp_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); - - if (ED_view3d_project_float_global(p->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - float mval_f[2]; - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->ar, mval_f, dvec, zfac); - sub_v3_v3v3(out, rvec, dvec); - } - else { - zero_v3(out); - } - } - } - - /* 2d - on 'canvas' (assume that p->v2d is set) */ - else if ((gpd->runtime.sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { - UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); - mul_v3_m4v3(out, p->imat, out); - } - - /* 2d - relative to screen (viewport area) */ - else { - if (p->subrect == NULL) { /* normal 3D view */ - out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; - out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; - } - else { /* camera view, use subrect */ - out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; - out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; - } - } + bGPdata *gpd = p->gpd; + + /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { + int mval_i[2]; + round_v2i_v2fl(mval_i, mval); + if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval_i, out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ + } + else { + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. In general, this + * works OK, but it could of course be improved. + * + * TODO: + * - investigate using nearest point(s) on a previous stroke as + * reference point instead or as offset, for easier stroke matching + */ + + gp_get_3d_reference(p, rvec); + zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(p->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + float mval_f[2]; + sub_v2_v2v2(mval_f, mval_prj, mval); + ED_view3d_win_to_delta(p->ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } + } + } + + /* 2d - on 'canvas' (assume that p->v2d is set) */ + else if ((gpd->runtime.sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { + UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); + mul_v3_m4v3(out, p->imat, out); + } + + /* 2d - relative to screen (viewport area) */ + else { + if (p->subrect == NULL) { /* normal 3D view */ + out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; + out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; + } + else { /* camera view, use subrect */ + out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; + out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; + } + } } /* Apply smooth to buffer while drawing @@ -376,188 +373,187 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[ */ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) { - bGPdata *gpd = p->gpd; - short num_points = gpd->runtime.sbuffer_size; - - /* Do nothing if not enough points to smooth out */ - if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { - return; - } - - tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - float steps = 4.0f; - if (idx < 4) { - steps--; - } - - tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; - tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; - tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; - tGPspoint *ptd = &points[idx - 1]; - - float sco[2] = { 0.0f }; - float a[2], b[2], c[2], d[2]; - const float average_fac = 1.0f / steps; - - /* Compute smoothed coordinate by taking the ones nearby */ - if (pta) { - copy_v2_v2(a, &pta->x); - madd_v2_v2fl(sco, a, average_fac); - } - if (ptb) { - copy_v2_v2(b, &ptb->x); - madd_v2_v2fl(sco, b, average_fac); - } - if (ptc) { - copy_v2_v2(c, &ptc->x); - madd_v2_v2fl(sco, c, average_fac); - } - if (ptd) { - copy_v2_v2(d, &ptd->x); - madd_v2_v2fl(sco, d, average_fac); - } - - /* Based on influence factor, blend between original and optimal smoothed coordinate */ - interp_v2_v2v2(c, c, sco, inf); - copy_v2_v2(&ptc->x, c); + bGPdata *gpd = p->gpd; + short num_points = gpd->runtime.sbuffer_size; + + /* Do nothing if not enough points to smooth out */ + if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + float steps = 4.0f; + if (idx < 4) { + steps--; + } + + tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; + tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; + tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; + tGPspoint *ptd = &points[idx - 1]; + + float sco[2] = {0.0f}; + float a[2], b[2], c[2], d[2]; + const float average_fac = 1.0f / steps; + + /* Compute smoothed coordinate by taking the ones nearby */ + if (pta) { + copy_v2_v2(a, &pta->x); + madd_v2_v2fl(sco, a, average_fac); + } + if (ptb) { + copy_v2_v2(b, &ptb->x); + madd_v2_v2fl(sco, b, average_fac); + } + if (ptc) { + copy_v2_v2(c, &ptc->x); + madd_v2_v2fl(sco, c, average_fac); + } + if (ptd) { + copy_v2_v2(d, &ptd->x); + madd_v2_v2fl(sco, d, average_fac); + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v2_v2v2(c, c, sco, inf); + copy_v2_v2(&ptc->x, c); } /* add current stroke-point to buffer (returns whether point was successfully added) */ -static short gp_stroke_addpoint( - tGPsdata *p, const float mval[2], float pressure, double curtime) +static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime) { - bGPdata *gpd = p->gpd; - tGPspoint *pt; - ToolSettings *ts = p->scene->toolsettings; - - /* check painting mode */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { - /* straight lines only - i.e. only store start and end point in buffer */ - if (gpd->runtime.sbuffer_size == 0) { - /* first point in buffer (start point) */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* increment buffer size */ - gpd->runtime.sbuffer_size++; - } - else { - /* just reset the endpoint to the latest value - * - assume that pointers for this are always valid... - */ - pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ - gpd->runtime.sbuffer_size = 2; - } - - /* can keep carrying on this way :) */ - return GP_STROKEADD_NORMAL; - } - else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ - /* check if still room in buffer */ - if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_OVERFLOW; - - /* get pointer to destination point */ - pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - pt->pressure = pressure; - /* unused for annotations, but initialise for easier conversions to GP Object */ - pt->strength = 1.0f; - - /* point time */ - pt->time = (float)(curtime - p->inittime); - - /* increment counters */ - gpd->runtime.sbuffer_size++; - /* smooth while drawing previous points with a reduction factor for previous */ - for (int s = 0; s < 3; s++) { - gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_size - s); - } - - /* check if another operation can still occur */ - if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_FULL; - else - return GP_STROKEADD_NORMAL; - } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* if there's stroke for this poly line session add (or replace last) point - * to stroke. This allows to draw lines more interactively (see new segment - * during mouse slide, e.g.) - */ - if (gp_stroke_added_check(p)) { - bGPDstroke *gps = p->gpf->strokes.last; - bGPDspoint *pts; - - /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ - if (gpd->runtime.sbuffer_size == 0) { - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - gps->totpoints++; - } - - pts = &gps->points[gps->totpoints - 1]; - - /* special case for poly lines: normally, - * depth is needed only when creating new stroke from buffer, - * but poly lines are converting to stroke instantly, - * so initialize depth buffer before converting coordinates - */ - if (gpencil_project_check(p)) { - View3D *v3d = p->sa->spacedata.first; - - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init( - p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); - } - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - - /* copy pressure and time */ - pts->pressure = pt->pressure; - pts->strength = pt->strength; - pts->time = pt->time; - - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - } - - /* increment counters */ - if (gpd->runtime.sbuffer_size == 0) - gpd->runtime.sbuffer_size++; - - return GP_STROKEADD_NORMAL; - } - - /* return invalid state for now... */ - return GP_STROKEADD_INVALID; + bGPdata *gpd = p->gpd; + tGPspoint *pt; + ToolSettings *ts = p->scene->toolsettings; + + /* check painting mode */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only - i.e. only store start and end point in buffer */ + if (gpd->runtime.sbuffer_size == 0) { + /* first point in buffer (start point) */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* increment buffer size */ + gpd->runtime.sbuffer_size++; + } + else { + /* just reset the endpoint to the latest value + * - assume that pointers for this are always valid... + */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ + gpd->runtime.sbuffer_size = 2; + } + + /* can keep carrying on this way :) */ + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ + /* check if still room in buffer */ + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_OVERFLOW; + + /* get pointer to destination point */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + pt->pressure = pressure; + /* unused for annotations, but initialise for easier conversions to GP Object */ + pt->strength = 1.0f; + + /* point time */ + pt->time = (float)(curtime - p->inittime); + + /* increment counters */ + gpd->runtime.sbuffer_size++; + /* smooth while drawing previous points with a reduction factor for previous */ + for (int s = 0; s < 3; s++) { + gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_size - s); + } + + /* check if another operation can still occur */ + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_FULL; + else + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* get pointer to destination point */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* if there's stroke for this poly line session add (or replace last) point + * to stroke. This allows to draw lines more interactively (see new segment + * during mouse slide, e.g.) + */ + if (gp_stroke_added_check(p)) { + bGPDstroke *gps = p->gpf->strokes.last; + bGPDspoint *pts; + + /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ + if (gpd->runtime.sbuffer_size == 0) { + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + } + + pts = &gps->points[gps->totpoints - 1]; + + /* special case for poly lines: normally, + * depth is needed only when creating new stroke from buffer, + * but poly lines are converting to stroke instantly, + * so initialize depth buffer before converting coordinates + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); + + /* copy pressure and time */ + pts->pressure = pt->pressure; + pts->strength = pt->strength; + pts->time = pt->time; + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + } + + /* increment counters */ + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + return GP_STROKEADD_NORMAL; + } + + /* return invalid state for now... */ + return GP_STROKEADD_INVALID; } /* simplify a stroke (in buffer) before storing it @@ -566,266 +562,270 @@ static short gp_stroke_addpoint( */ static void gp_stroke_simplify(tGPsdata *p) { - bGPdata *gpd = p->gpd; - tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; - short num_points = gpd->runtime.sbuffer_size; - short flag = gpd->runtime.sbuffer_sflag; - short i, j; - - /* only simplify if simplification is enabled, and we're not doing a straight line */ - if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)) - return; - - /* don't simplify if less than 4 points in buffer */ - if ((num_points <= 4) || (old_points == NULL)) - return; - - /* clear buffer (but don't free mem yet) so that we can write to it - * - firstly set sbuffer to NULL, so a new one is allocated - * - secondly, reset flag after, as it gets cleared auto - */ - gpd->runtime.sbuffer = NULL; - gp_session_validatebuffer(p); - gpd->runtime.sbuffer_sflag = flag; + bGPdata *gpd = p->gpd; + tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; + short num_points = gpd->runtime.sbuffer_size; + short flag = gpd->runtime.sbuffer_sflag; + short i, j; + + /* only simplify if simplification is enabled, and we're not doing a straight line */ + if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)) + return; + + /* don't simplify if less than 4 points in buffer */ + if ((num_points <= 4) || (old_points == NULL)) + return; + + /* clear buffer (but don't free mem yet) so that we can write to it + * - firstly set sbuffer to NULL, so a new one is allocated + * - secondly, reset flag after, as it gets cleared auto + */ + gpd->runtime.sbuffer = NULL; + gp_session_validatebuffer(p); + gpd->runtime.sbuffer_sflag = flag; /* macro used in loop to get position of new point * - used due to the mixture of datatypes in use here */ #define GP_SIMPLIFY_AVPOINT(offs, sfac) \ - { \ - co[0] += (float)(old_points[offs].x * sfac); \ - co[1] += (float)(old_points[offs].y * sfac); \ - pressure += old_points[offs].pressure * sfac; \ - time += old_points[offs].time * sfac; \ - } (void)0 - - /* XXX Here too, do not lose start and end points! */ - gp_stroke_addpoint(p, &old_points->x, old_points->pressure, p->inittime + (double)old_points->time); - for (i = 0, j = 0; i < num_points; i++) { - if (i - j == 3) { - float co[2], pressure, time; - float mco[2]; - - /* initialize values */ - co[0] = 0.0f; - co[1] = 0.0f; - pressure = 0.0f; - time = 0.0f; - - /* using macro, calculate new point */ - GP_SIMPLIFY_AVPOINT(j, -0.25f); - GP_SIMPLIFY_AVPOINT(j + 1, 0.75f); - GP_SIMPLIFY_AVPOINT(j + 2, 0.75f); - GP_SIMPLIFY_AVPOINT(j + 3, -0.25f); - - /* set values for adding */ - mco[0] = co[0]; - mco[1] = co[1]; - - /* ignore return values on this... assume to be ok for now */ - gp_stroke_addpoint(p, mco, pressure, p->inittime + (double)time); - - j += 2; - } - } - gp_stroke_addpoint( - p, &old_points[num_points - 1].x, old_points[num_points - 1].pressure, - p->inittime + (double)old_points[num_points - 1].time); - - /* free old buffer */ - MEM_freeN(old_points); + { \ + co[0] += (float)(old_points[offs].x * sfac); \ + co[1] += (float)(old_points[offs].y * sfac); \ + pressure += old_points[offs].pressure * sfac; \ + time += old_points[offs].time * sfac; \ + } \ + (void)0 + + /* XXX Here too, do not lose start and end points! */ + gp_stroke_addpoint( + p, &old_points->x, old_points->pressure, p->inittime + (double)old_points->time); + for (i = 0, j = 0; i < num_points; i++) { + if (i - j == 3) { + float co[2], pressure, time; + float mco[2]; + + /* initialize values */ + co[0] = 0.0f; + co[1] = 0.0f; + pressure = 0.0f; + time = 0.0f; + + /* using macro, calculate new point */ + GP_SIMPLIFY_AVPOINT(j, -0.25f); + GP_SIMPLIFY_AVPOINT(j + 1, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 2, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 3, -0.25f); + + /* set values for adding */ + mco[0] = co[0]; + mco[1] = co[1]; + + /* ignore return values on this... assume to be ok for now */ + gp_stroke_addpoint(p, mco, pressure, p->inittime + (double)time); + + j += 2; + } + } + gp_stroke_addpoint(p, + &old_points[num_points - 1].x, + old_points[num_points - 1].pressure, + p->inittime + (double)old_points[num_points - 1].time); + + /* free old buffer */ + MEM_freeN(old_points); } /* make a new stroke from the buffer data */ static void gp_stroke_newfrombuffer(tGPsdata *p) { - bGPdata *gpd = p->gpd; - bGPDlayer *gpl = p->gpl; - bGPDstroke *gps; - bGPDspoint *pt; - tGPspoint *ptc; - ToolSettings *ts = p->scene->toolsettings; - - int i, totelem; - /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ - int depth_margin = (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; - - /* get total number of points to allocate space for - * - drawing straight-lines only requires the endpoints - */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) - totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; - else - totelem = gpd->runtime.sbuffer_size; - - /* exit with error if no valid points from this stroke */ - if (totelem == 0) { - if (G.debug & G_DEBUG) - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); - return; - } - - /* special case for poly line -- for already added stroke during session - * coordinates are getting added to stroke immediately to allow more - * interactive behavior - */ - if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - if (gp_stroke_added_check(p)) { - return; - } - } - - /* allocate memory for a new stroke */ - gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - - /* copy appropriate settings for stroke */ - gps->totpoints = totelem; - gps->thickness = gpl->thickness; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; - gps->flag = gpd->runtime.sbuffer_sflag; - gps->inittime = p->inittime; - - /* enable recalculation flag by default (only used if hq fill) */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* allocate enough memory for a continuous array for storage points */ - gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); - gps->tot_triangles = 0; - - /* set pointer to first non-initialized point */ - pt = gps->points + (gps->totpoints - totelem); - - /* copy points from the buffer to the stroke */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { - /* straight lines only -> only endpoints */ - { - /* first point */ - ptc = gpd->runtime.sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - - pt++; - } - - if (totelem == 2) { - /* last point if applicable */ - ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - } - } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* first point */ - ptc = gpd->runtime.sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - pt->time = ptc->time; - } - else { - float *depth_arr = NULL; - - /* get an array of depths, far depths are blended */ - if (gpencil_project_check(p)) { - int mval_i[2], mval_prev[2] = { 0 }; - int interp_depth = 0; - int found_depth = 0; - - depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); - - for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { - round_v2i_v2fl(mval_i, &ptc->x); - - if ((ED_view3d_autodist_depth(p->ar, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg(p->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 == false) { - /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ - for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) - depth_arr[i] = 0.9999f; - } - else { - if (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) { - /* remove all info between the valid endpoints */ - int first_valid = 0; - int last_valid = 0; - - for (i = 0; i < gpd->runtime.sbuffer_size; i++) { - if (depth_arr[i] != FLT_MAX) - break; - } - first_valid = i; - - for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { - if (depth_arr[i] != FLT_MAX) - break; - } - last_valid = i; - - /* invalidate non-endpoints, so only blend between first and last */ - for (i = first_valid + 1; i < last_valid; i++) - depth_arr[i] = FLT_MAX; - - interp_depth = true; - } - - if (interp_depth) { - interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); - } - } - } - - - pt = gps->points; - - /* convert all points (normal behavior) */ - for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); - - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - } - - if (depth_arr) - MEM_freeN(depth_arr); - } - - /* add stroke to frame */ - BLI_addtail(&p->gpf->strokes, gps); - gp_stroke_added_enable(p); + bGPdata *gpd = p->gpd; + bGPDlayer *gpl = p->gpl; + bGPDstroke *gps; + bGPDspoint *pt; + tGPspoint *ptc; + ToolSettings *ts = p->scene->toolsettings; + + int i, totelem; + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; + + /* get total number of points to allocate space for + * - drawing straight-lines only requires the endpoints + */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; + else + totelem = gpd->runtime.sbuffer_size; + + /* exit with error if no valid points from this stroke */ + if (totelem == 0) { + if (G.debug & G_DEBUG) + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", + gpd->runtime.sbuffer_size); + return; + } + + /* special case for poly line -- for already added stroke during session + * coordinates are getting added to stroke immediately to allow more + * interactive behavior + */ + if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + if (gp_stroke_added_check(p)) { + return; + } + } + + /* allocate memory for a new stroke */ + gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + + /* copy appropriate settings for stroke */ + gps->totpoints = totelem; + gps->thickness = gpl->thickness; + gps->gradient_f = 1.0f; + gps->gradient_s[0] = 1.0f; + gps->gradient_s[1] = 1.0f; + gps->flag = gpd->runtime.sbuffer_sflag; + gps->inittime = p->inittime; + + /* enable recalculation flag by default (only used if hq fill) */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* allocate enough memory for a continuous array for storage points */ + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->tot_triangles = 0; + + /* set pointer to first non-initialized point */ + pt = gps->points + (gps->totpoints - totelem); + + /* copy points from the buffer to the stroke */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only -> only endpoints */ + { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + + pt++; + } + + if (totelem == 2) { + /* last point if applicable */ + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + } + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + pt->time = ptc->time; + } + else { + float *depth_arr = NULL; + + /* get an array of depths, far depths are blended */ + if (gpencil_project_check(p)) { + int mval_i[2], mval_prev[2] = {0}; + int interp_depth = 0; + int found_depth = 0; + + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); + + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { + round_v2i_v2fl(mval_i, &ptc->x); + + if ((ED_view3d_autodist_depth(p->ar, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg( + p->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 == false) { + /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) + depth_arr[i] = 0.9999f; + } + else { + if (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) { + /* remove all info between the valid endpoints */ + int first_valid = 0; + int last_valid = 0; + + for (i = 0; i < gpd->runtime.sbuffer_size; i++) { + if (depth_arr[i] != FLT_MAX) + break; + } + first_valid = i; + + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { + if (depth_arr[i] != FLT_MAX) + break; + } + last_valid = i; + + /* invalidate non-endpoints, so only blend between first and last */ + for (i = first_valid + 1; i < last_valid; i++) + depth_arr[i] = FLT_MAX; + + interp_depth = true; + } + + if (interp_depth) { + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); + } + } + } + + pt = gps->points; + + /* convert all points (normal behavior) */ + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; + i++, ptc++, pt++) { + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + } + + if (depth_arr) + MEM_freeN(depth_arr); + } + + /* add stroke to frame */ + BLI_addtail(&p->gpf->strokes, gps); + gp_stroke_added_enable(p); } /* --- 'Eraser' for 'Paint' Tool ------ */ @@ -835,186 +835,186 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) */ static void gp_free_stroke(bGPDframe *gpf, bGPDstroke *gps) { - if (gps->points) { - MEM_freeN(gps->points); - } + if (gps->points) { + MEM_freeN(gps->points); + } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } - if (gps->triangles) { - MEM_freeN(gps->triangles); - } + if (gps->triangles) { + MEM_freeN(gps->triangles); + } - BLI_freelinkN(&gpf->strokes, gps); + BLI_freelinkN(&gpf->strokes, gps); } - /* which which point is infront (result should only be used for comparison) */ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) { - if (rv3d->is_persp) { - return ED_view3d_calc_zfac(rv3d, co, NULL); - } - else { - return -dot_v3v3(rv3d->viewinv[2], co); - } + if (rv3d->is_persp) { + return ED_view3d_calc_zfac(rv3d, co, NULL); + } + else { + return -dot_v3v3(rv3d->viewinv[2], co); + } } /* only erase stroke points that are visible (3d view) */ -static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) +static bool gp_stroke_eraser_is_occluded(tGPsdata *p, + const bGPDspoint *pt, + const int x, + const int y) { - if ((p->sa->spacetype == SPACE_VIEW3D) && - (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) - { - RegionView3D *rv3d = p->ar->regiondata; - const int mval_i[2] = {x, y}; - float mval_3d[3]; - - if (ED_view3d_autodist_simple(p->ar, mval_i, mval_3d, 0, NULL)) { - const float depth_mval = view3d_point_depth(rv3d, mval_3d); - const float depth_pt = view3d_point_depth(rv3d, &pt->x); - - if (depth_pt > depth_mval) { - return true; - } - } - } - return false; + if ((p->sa->spacetype == SPACE_VIEW3D) && (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { + RegionView3D *rv3d = p->ar->regiondata; + const int mval_i[2] = {x, y}; + float mval_3d[3]; + + if (ED_view3d_autodist_simple(p->ar, mval_i, mval_3d, 0, NULL)) { + const float depth_mval = view3d_point_depth(rv3d, mval_3d); + const float depth_pt = view3d_point_depth(rv3d, &pt->x); + + if (depth_pt > depth_mval) { + return true; + } + } + } + return false; } /* eraser tool - evaluation per stroke */ /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ -static void gp_stroke_eraser_dostroke( - tGPsdata *p, - bGPDframe *gpf, bGPDstroke *gps, - const float mval[2], const float mvalo[2], - const int radius, const rcti *rect) +static void gp_stroke_eraser_dostroke(tGPsdata *p, + bGPDframe *gpf, + bGPDstroke *gps, + const float mval[2], + const float mvalo[2], + const int radius, + const rcti *rect) { - bGPDspoint *pt1, *pt2; - int pc1[2] = {0}; - int pc2[2] = {0}; - int i; - int mval_i[2]; - round_v2i_v2fl(mval_i, mval); - - if (gps->totpoints == 0) { - /* just free stroke */ - gp_free_stroke(gpf, gps); - } - else if (gps->totpoints == 1) { - /* only process if it hasn't been masked out... */ - if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); - - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - if (len_v2v2_int(mval_i, pc1) <= radius) { - /* free stroke */ - gp_free_stroke(gpf, gps); - } - } - } - } - else { - /* Perform culling? */ - bool do_cull = false; - - /* Clear Tags - * - * Note: It's better this way, as we are sure that - * we don't miss anything, though things will be - * slightly slower as a result - */ - for (i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - pt->flag &= ~GP_SPOINT_TAG; - } - - /* First Pass: Loop over the points in the stroke - * 1) Thin out parts of the stroke under the brush - * 2) Tag "too thin" parts for removal (in second pass) - */ - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* get points to work with */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* only process if it hasn't been masked out... */ - if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) - continue; - - gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); - - /* Check that point segment of the boundbox of the eraser stroke */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) - { - /* Check if point segment of stroke had anything to do with - * eraser region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { - if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || - (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) - { - /* Edge is affected - Check individual points now */ - if (len_v2v2_int(mval_i, pc1) <= radius) { - pt1->flag |= GP_SPOINT_TAG; - } - if (len_v2v2_int(mval_i, pc2) <= radius) { - pt2->flag |= GP_SPOINT_TAG; - } - do_cull = true; - } - } - } - } - - /* Second Pass: Remove any points that are tagged */ - if (do_cull) { - gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0); - } - } + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + int mval_i[2]; + round_v2i_v2fl(mval_i, mval); + + if (gps->totpoints == 0) { + /* just free stroke */ + gp_free_stroke(gpf, gps); + } + else if (gps->totpoints == 1) { + /* only process if it hasn't been masked out... */ + if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { + gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); + + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(gpf, gps); + } + } + } + } + else { + /* Perform culling? */ + bool do_cull = false; + + /* Clear Tags + * + * Note: It's better this way, as we are sure that + * we don't miss anything, though things will be + * slightly slower as a result + */ + for (i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->flag &= ~GP_SPOINT_TAG; + } + + /* First Pass: Loop over the points in the stroke + * 1) Thin out parts of the stroke under the brush + * 2) Tag "too thin" parts for removal (in second pass) + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the eraser stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * eraser region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || + (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) { + /* Edge is affected - Check individual points now */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + pt1->flag |= GP_SPOINT_TAG; + } + if (len_v2v2_int(mval_i, pc2) <= radius) { + pt2->flag |= GP_SPOINT_TAG; + } + do_cull = true; + } + } + } + } + + /* Second Pass: Remove any points that are tagged */ + if (do_cull) { + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0); + } + } } /* erase strokes which fall under the eraser strokes */ static void gp_stroke_doeraser(tGPsdata *p) { - bGPDframe *gpf = p->gpf; - bGPDstroke *gps, *gpn; - rcti rect; - - /* rect is rectangle of eraser */ - rect.xmin = p->mval[0] - p->radius; - rect.ymin = p->mval[1] - p->radius; - rect.xmax = p->mval[0] + p->radius; - rect.ymax = p->mval[1] + p->radius; - - if (p->sa->spacetype == SPACE_VIEW3D) { - if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { - View3D *v3d = p->sa->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); - } - } - - /* loop over strokes of active layer only (session init already took care of ensuring validity), - * checking segments for intersections to remove - */ - for (gps = gpf->strokes.first; gps; gps = gpn) { - gpn = gps->next; - /* Not all strokes in the datablock may be valid in the current editor/context - * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) - */ - if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { - gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->mvalo, p->radius, &rect); - } - } + bGPDframe *gpf = p->gpf; + bGPDstroke *gps, *gpn; + rcti rect; + + /* rect is rectangle of eraser */ + rect.xmin = p->mval[0] - p->radius; + rect.ymin = p->mval[1] - p->radius; + rect.xmax = p->mval[0] + p->radius; + rect.ymax = p->mval[1] + p->radius; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { + View3D *v3d = p->sa->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); + } + } + + /* loop over strokes of active layer only (session init already took care of ensuring validity), + * checking segments for intersections to remove + */ + for (gps = gpf->strokes.first; gps; gps = gpn) { + gpn = gps->next; + /* Not all strokes in the datablock may be valid in the current editor/context + * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) + */ + if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { + gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->mvalo, p->radius, &rect); + } + } } /* ******************************************* */ @@ -1023,462 +1023,456 @@ static void gp_stroke_doeraser(tGPsdata *p) /* clear the session buffers (call this before AND after a paint operation) */ static void gp_session_validatebuffer(tGPsdata *p) { - bGPdata *gpd = p->gpd; - - /* clear memory of buffer (or allocate it if starting a new session) */ - if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - reset sbuffer\n"); */ - memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); - } - else { - /* printf("\t\tGP - allocate sbuffer\n"); */ - 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; - - /* reset inittime */ - p->inittime = 0.0; + bGPdata *gpd = p->gpd; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - reset sbuffer\n"); */ + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + } + else { + /* printf("\t\tGP - allocate sbuffer\n"); */ + 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; + + /* reset inittime */ + p->inittime = 0.0; } /* (re)init new painting data */ static bool gp_session_initdata(bContext *C, tGPsdata *p) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = NULL; - ScrArea *curarea = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - ToolSettings *ts = CTX_data_tool_settings(C); - - /* make sure the active view (at the starting time) is a 3d-view */ - if (curarea == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: No active view for painting\n"); - return 0; - } - - /* pass on current scene and window */ - p->bmain = CTX_data_main(C); - p->scene = CTX_data_scene(C); - p->depsgraph = CTX_data_depsgraph(C); - p->win = CTX_wm_window(C); - - unit_m4(p->imat); - unit_m4(p->mat); - - switch (curarea->spacetype) { - /* supported views first */ - case SPACE_VIEW3D: - { - /* View3D *v3d = curarea->spacedata.first; */ - /* RegionView3D *rv3d = ar->regiondata; */ - - /* set current area - * - must verify that region data is 3D-view (and not something else) - */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ - p->sa = curarea; - p->ar = ar; - p->align_flag = &ts->annotate_v3d_align; - - if (ar->regiondata == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); - return 0; - } - break; - } - case SPACE_NODE: - { - /* SpaceNode *snode = curarea->spacedata.first; */ - - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - break; - } - case SPACE_SEQ: - { - SpaceSeq *sseq = curarea->spacedata.first; - - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_seq_align; - - /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); - return 0; - } - break; - } - case SPACE_IMAGE: - { - /* SpaceImage *sima = curarea->spacedata.first; */ - - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_ima_align; - break; - } - case SPACE_CLIP: - { - SpaceClip *sc = curarea->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip == NULL) { - p->status = GP_STATUS_ERROR; - return false; - } - - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - - invert_m4_m4(p->imat, sc->unistabmat); - - /* custom color for new layer */ - p->custom_color[0] = 1.0f; - p->custom_color[1] = 0.0f; - p->custom_color[2] = 0.5f; - p->custom_color[3] = 0.9f; - - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - int framenr = ED_space_clip_get_clip_frame_number(sc); - MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; - - if (marker) { - p->imat[3][0] -= marker->pos[0]; - p->imat[3][1] -= marker->pos[1]; - } - else { - p->status = GP_STATUS_ERROR; - return false; - } - } - - invert_m4_m4(p->mat, p->imat); - copy_m4_m4(p->gsc.mat, p->mat); - break; - } - /* unsupported views */ - default: - { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Annotations are not supported in this editor\n"); - return 0; - } - } - - /* get gp-data */ - gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); - if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Current context doesn't allow for any Annotation data\n"); - return 0; - } - else { - /* if no existing GPencil block exists, add one */ - if (*gpd_ptr == NULL) { - bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); - *gpd_ptr = gpd; - - /* mark datablock as being used for annotations */ - gpd->flag |= GP_DATA_ANNOTATIONS; - } - p->gpd = *gpd_ptr; - } - - if (ED_gpencil_session_active() == 0) { - /* initialize undo stack, - * also, existing undo stack would make buffer drawn - */ - gpencil_undo_init(p->gpd); - } - - /* clear out buffer (stored in gp-data), in case something contaminated it */ - gp_session_validatebuffer(p); - - return 1; + Main *bmain = CTX_data_main(C); + bGPdata **gpd_ptr = NULL; + ScrArea *curarea = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + /* make sure the active view (at the starting time) is a 3d-view */ + if (curarea == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No active view for painting\n"); + return 0; + } + + /* pass on current scene and window */ + p->bmain = CTX_data_main(C); + p->scene = CTX_data_scene(C); + p->depsgraph = CTX_data_depsgraph(C); + p->win = CTX_wm_window(C); + + unit_m4(p->imat); + unit_m4(p->mat); + + switch (curarea->spacetype) { + /* supported views first */ + case SPACE_VIEW3D: { + /* View3D *v3d = curarea->spacedata.first; */ + /* RegionView3D *rv3d = ar->regiondata; */ + + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->sa = curarea; + p->ar = ar; + p->align_flag = &ts->annotate_v3d_align; + + if (ar->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf( + "Error: 3D-View active region doesn't have any region data, so cannot be " + "drawable\n"); + return 0; + } + break; + } + case SPACE_NODE: { + /* SpaceNode *snode = curarea->spacedata.first; */ + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + break; + } + case SPACE_SEQ: { + SpaceSeq *sseq = curarea->spacedata.first; + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_seq_align; + + /* check that gpencil data is allowed to be drawn */ + if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); + return 0; + } + break; + } + case SPACE_IMAGE: { + /* SpaceImage *sima = curarea->spacedata.first; */ + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_ima_align; + break; + } + case SPACE_CLIP: { + SpaceClip *sc = curarea->spacedata.first; + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (clip == NULL) { + p->status = GP_STATUS_ERROR; + return false; + } + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + + invert_m4_m4(p->imat, sc->unistabmat); + + /* custom color for new layer */ + p->custom_color[0] = 1.0f; + p->custom_color[1] = 0.0f; + p->custom_color[2] = 0.5f; + p->custom_color[3] = 0.9f; + + if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + int framenr = ED_space_clip_get_clip_frame_number(sc); + MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); + MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; + + if (marker) { + p->imat[3][0] -= marker->pos[0]; + p->imat[3][1] -= marker->pos[1]; + } + else { + p->status = GP_STATUS_ERROR; + return false; + } + } + + invert_m4_m4(p->mat, p->imat); + copy_m4_m4(p->gsc.mat, p->mat); + break; + } + /* unsupported views */ + default: { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Annotations are not supported in this editor\n"); + return 0; + } + } + + /* get gp-data */ + gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); + if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Current context doesn't allow for any Annotation data\n"); + return 0; + } + else { + /* if no existing GPencil block exists, add one */ + if (*gpd_ptr == NULL) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); + *gpd_ptr = gpd; + + /* mark datablock as being used for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; + } + p->gpd = *gpd_ptr; + } + + if (ED_gpencil_session_active() == 0) { + /* initialize undo stack, + * also, existing undo stack would make buffer drawn + */ + gpencil_undo_init(p->gpd); + } + + /* clear out buffer (stored in gp-data), in case something contaminated it */ + gp_session_validatebuffer(p); + + return 1; } /* init new painting session */ static tGPsdata *gp_session_initpaint(bContext *C) { - tGPsdata *p = NULL; - - /* create new context data */ - p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); - - /* Try to initialise context data - * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) - */ - if (gp_session_initdata(C, p) == 0) { - /* Invalid state - Exit - * NOTE: It should be safe to just free the data, since failing context checks should - * only happen when no data has been allocated. - */ - MEM_freeN(p); - return NULL; - } - - /* Radius for eraser circle is defined in userprefs */ - /* NOTE: we do this here, so that if we exit immediately, - * erase size won't get lost - */ - p->radius = U.gp_eraser; - - /* return context data for running paint operator */ - return p; + tGPsdata *p = NULL; + + /* create new context data */ + p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); + + /* Try to initialise context data + * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) + */ + if (gp_session_initdata(C, p) == 0) { + /* Invalid state - Exit + * NOTE: It should be safe to just free the data, since failing context checks should + * only happen when no data has been allocated. + */ + MEM_freeN(p); + return NULL; + } + + /* Radius for eraser circle is defined in userprefs */ + /* NOTE: we do this here, so that if we exit immediately, + * erase size won't get lost + */ + p->radius = U.gp_eraser; + + /* return context data for running paint operator */ + return p; } /* cleanup after a painting session */ static void gp_session_cleanup(tGPsdata *p) { - bGPdata *gpd = (p) ? p->gpd : NULL; - - /* error checking */ - if (gpd == NULL) - return; - - /* free stroke buffer */ - if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - free sbuffer\n"); */ - MEM_freeN(gpd->runtime.sbuffer); - gpd->runtime.sbuffer = NULL; - } - - /* clear flags */ - gpd->runtime.sbuffer_size = 0; - gpd->runtime.sbuffer_sflag = 0; - p->inittime = 0.0; + bGPdata *gpd = (p) ? p->gpd : NULL; + + /* error checking */ + if (gpd == NULL) + return; + + /* free stroke buffer */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - free sbuffer\n"); */ + MEM_freeN(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; + } + + /* clear flags */ + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; + p->inittime = 0.0; } static void gp_session_free(tGPsdata *p) { - MEM_freeN(p); + MEM_freeN(p); } - /* init new stroke */ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) { - Scene *scene = p->scene; - ToolSettings *ts = scene->toolsettings; - - /* get active layer (or add a new one if non-existent) */ - p->gpl = BKE_gpencil_layer_getactive(p->gpd); - if (p->gpl == NULL) { - /* tag for annotations */ - p->gpd->flag |= GP_DATA_ANNOTATIONS; - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); - - if (p->custom_color[3]) - copy_v3_v3(p->gpl->color, p->custom_color); - } - if (p->gpl->flag & GP_LAYER_LOCKED) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Cannot paint on locked layer\n"); - return; - } - - /* get active frame (add a new one if not matching frame) */ - if (paintmode == GP_PAINTMODE_ERASER) { - /* Eraser mode: - * 1) Only allow erasing on the active layer (unlike for 3d-art Grease Pencil), - * since we won't be exposing layer locking in the UI - * 2) Ensure that p->gpf refers to the frame used for the active layer - * (to avoid problems with other tools which expect it to exist) - */ - bool has_layer_to_erase = false; - - if (gpencil_layer_is_editable(p->gpl)) { - /* Ensure that there's stuff to erase here (not including selection mask below)... */ - if (p->gpl->actframe && p->gpl->actframe->strokes.first) { - has_layer_to_erase = true; - } - } - - /* Ensure active frame is set correctly... */ - p->gpf = p->gpl->actframe; - - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - - if (has_layer_to_erase == false) { - p->status = GP_STATUS_CAPTURE; - //if (G.debug & G_DEBUG) - printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); - return; - } - } - else { - /* Drawing Modes - Add a new frame if needed on the active layer */ - short add_frame_mode = GP_GETFRAME_ADD_NEW; - - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) - add_frame_mode = GP_GETFRAME_ADD_COPY; - else - add_frame_mode = GP_GETFRAME_ADD_NEW; - - p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); - - if (p->gpf == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: No frame created (gpencil_paint_init)\n"); - return; - } - else { - p->gpf->flag |= GP_FRAME_PAINT; - } - } - - /* set 'eraser' for this stroke if using eraser */ - p->paintmode = paintmode; - if (p->paintmode == GP_PAINTMODE_ERASER) { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; - - /* check if we should respect depth while erasing */ - if (p->sa->spacetype == SPACE_VIEW3D) { - if (p->gpl->flag & GP_LAYER_NO_XRAY) { - p->flags |= GP_PAINTFLAG_V3D_ERASER_DEPTH; - } - } - } - else { - /* disable eraser flags - so that we can switch modes during a session */ - p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; - - if (p->sa->spacetype == SPACE_VIEW3D) { - if (p->gpl->flag & GP_LAYER_NO_XRAY) { - p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; - } - } - } - - /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ - p->flags |= GP_PAINTFLAG_FIRSTRUN; - - - /* when drawing in the camera view, in 2D space, set the subrect */ - p->subrect = NULL; - if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { - if (p->sa->spacetype == SPACE_VIEW3D) { - View3D *v3d = p->sa->spacedata.first; - RegionView3D *rv3d = p->ar->regiondata; - - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - /* no shift */ - ED_view3d_calc_camera_border(p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); - p->subrect = &p->subrect_data; - } - } - } - - /* init stroke point space-conversion settings... */ - p->gsc.gpd = p->gpd; - p->gsc.gpl = p->gpl; - - p->gsc.sa = p->sa; - p->gsc.ar = p->ar; - p->gsc.v2d = p->v2d; - - p->gsc.subrect_data = p->subrect_data; - p->gsc.subrect = p->subrect; - - copy_m4_m4(p->gsc.mat, p->mat); - - - /* check if points will need to be made in view-aligned space */ - if (*p->align_flag & GP_PROJECT_VIEWSPACE) { - switch (p->sa->spacetype) { - case SPACE_VIEW3D: - { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - case SPACE_NODE: - case SPACE_SEQ: - case SPACE_IMAGE: - case SPACE_CLIP: - { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; - break; - } - } - } + Scene *scene = p->scene; + ToolSettings *ts = scene->toolsettings; + + /* get active layer (or add a new one if non-existent) */ + p->gpl = BKE_gpencil_layer_getactive(p->gpd); + if (p->gpl == NULL) { + /* tag for annotations */ + p->gpd->flag |= GP_DATA_ANNOTATIONS; + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + + if (p->custom_color[3]) + copy_v3_v3(p->gpl->color, p->custom_color); + } + if (p->gpl->flag & GP_LAYER_LOCKED) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Cannot paint on locked layer\n"); + return; + } + + /* get active frame (add a new one if not matching frame) */ + if (paintmode == GP_PAINTMODE_ERASER) { + /* Eraser mode: + * 1) Only allow erasing on the active layer (unlike for 3d-art Grease Pencil), + * since we won't be exposing layer locking in the UI + * 2) Ensure that p->gpf refers to the frame used for the active layer + * (to avoid problems with other tools which expect it to exist) + */ + bool has_layer_to_erase = false; + + if (gpencil_layer_is_editable(p->gpl)) { + /* Ensure that there's stuff to erase here (not including selection mask below)... */ + if (p->gpl->actframe && p->gpl->actframe->strokes.first) { + has_layer_to_erase = true; + } + } + + /* Ensure active frame is set correctly... */ + p->gpf = p->gpl->actframe; + + /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on + * (though this is only available in editmode) + */ + if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { + if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { + p->flags |= GP_PAINTFLAG_SELECTMASK; + } + } + + if (has_layer_to_erase == false) { + p->status = GP_STATUS_CAPTURE; + //if (G.debug & G_DEBUG) + printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); + return; + } + } + else { + /* Drawing Modes - Add a new frame if needed on the active layer */ + short add_frame_mode = GP_GETFRAME_ADD_NEW; + + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) + add_frame_mode = GP_GETFRAME_ADD_COPY; + else + add_frame_mode = GP_GETFRAME_ADD_NEW; + + p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + + if (p->gpf == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No frame created (gpencil_paint_init)\n"); + return; + } + else { + p->gpf->flag |= GP_FRAME_PAINT; + } + } + + /* set 'eraser' for this stroke if using eraser */ + p->paintmode = paintmode; + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; + + /* check if we should respect depth while erasing */ + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags |= GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + else { + /* disable eraser flags - so that we can switch modes during a session */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ + p->flags |= GP_PAINTFLAG_FIRSTRUN; + + /* when drawing in the camera view, in 2D space, set the subrect */ + p->subrect = NULL; + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + if (p->sa->spacetype == SPACE_VIEW3D) { + View3D *v3d = p->sa->spacedata.first; + RegionView3D *rv3d = p->ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + /* no shift */ + ED_view3d_calc_camera_border( + p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); + p->subrect = &p->subrect_data; + } + } + } + + /* init stroke point space-conversion settings... */ + p->gsc.gpd = p->gpd; + p->gsc.gpl = p->gpl; + + p->gsc.sa = p->sa; + p->gsc.ar = p->ar; + p->gsc.v2d = p->v2d; + + p->gsc.subrect_data = p->subrect_data; + p->gsc.subrect = p->subrect; + + copy_m4_m4(p->gsc.mat, p->mat); + + /* check if points will need to be made in view-aligned space */ + if (*p->align_flag & GP_PROJECT_VIEWSPACE) { + switch (p->sa->spacetype) { + case SPACE_VIEW3D: { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + break; + } + case SPACE_NODE: + case SPACE_SEQ: + case SPACE_IMAGE: + case SPACE_CLIP: { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + } + } } /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ static void gp_paint_strokeend(tGPsdata *p) { - ToolSettings *ts = p->scene->toolsettings; - /* for surface sketching, need to set the right OpenGL context stuff so that - * the conversions will project the values correctly... - */ - if (gpencil_project_check(p)) { - View3D *v3d = p->sa->spacedata.first; - - /* need to restore the original projection settings before packing up */ - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); - } - - /* check if doing eraser or not */ - if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { - /* simplify stroke before transferring? */ - gp_stroke_simplify(p); - - /* transfer stroke to frame */ - gp_stroke_newfrombuffer(p); - } - - /* clean up buffer now */ - gp_session_validatebuffer(p); + ToolSettings *ts = p->scene->toolsettings; + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* check if doing eraser or not */ + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { + /* simplify stroke before transferring? */ + gp_stroke_simplify(p); + + /* transfer stroke to frame */ + gp_stroke_newfrombuffer(p); + } + + /* clean up buffer now */ + gp_session_validatebuffer(p); } /* finish off stroke painting operation */ static void gp_paint_cleanup(tGPsdata *p) { - /* p->gpd==NULL happens when stroke failed to initialize, - * for example when GP is hidden in current space (sergey) - */ - if (p->gpd) { - /* finish off a stroke */ - gp_paint_strokeend(p); - } - - /* "unlock" frame */ - if (p->gpf) - p->gpf->flag &= ~GP_FRAME_PAINT; + /* p->gpd==NULL happens when stroke failed to initialize, + * for example when GP is hidden in current space (sergey) + */ + if (p->gpd) { + /* finish off a stroke */ + gp_paint_strokeend(p); + } + + /* "unlock" frame */ + if (p->gpf) + p->gpf->flag &= ~GP_FRAME_PAINT; } /* ------------------------------- */ @@ -1486,216 +1480,227 @@ static void gp_paint_cleanup(tGPsdata *p) /* Helper callback for drawing the cursor itself */ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) { - tGPsdata *p = (tGPsdata *)p_ptr; + tGPsdata *p = (tGPsdata *)p_ptr; - if (p->paintmode == GP_PAINTMODE_ERASER) { - GPUVertFormat *format = immVertexFormat(); - const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + if (p->paintmode == GP_PAINTMODE_ERASER) { + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_line_smooth(true); + GPU_blend(true); + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - immUniformColor4ub(255, 100, 100, 20); - imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); - immUnbindProgram(); + immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniform1f("dash_width", 12.0f); - immUniform1f("dash_factor", 0.5f); + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); - imm_draw_circle_wire_2d( - shdr_pos, x, y, p->radius, - /* XXX Dashed shader gives bad results with sets of small segments currently, - * temp hack around the issue. :( */ - max_ii(8, p->radius / 2)); /* was fixed 40 */ + imm_draw_circle_wire_2d( + shdr_pos, + x, + y, + p->radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, p->radius / 2)); /* was fixed 40 */ - immUnbindProgram(); + immUnbindProgram(); - GPU_blend(false); - GPU_line_smooth(false); - } + GPU_blend(false); + GPU_line_smooth(false); + } } /* Turn brush cursor in 3D view on/off */ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) { - if (p->erasercursor && !enable) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); - p->erasercursor = NULL; - } - else if (enable && !p->erasercursor) { - /* enable cursor */ - p->erasercursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - NULL, /* XXX */ - gpencil_draw_eraser, p); - } + if (p->erasercursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); + p->erasercursor = NULL; + } + else if (enable && !p->erasercursor) { + /* enable cursor */ + p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + NULL, /* XXX */ + gpencil_draw_eraser, + p); + } } /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + return (wmtab->Active == EVT_TABLET_ERASER); + } - return false; + return false; } /* ------------------------------- */ static void gpencil_draw_exit(bContext *C, wmOperator *op) { - tGPsdata *p = op->customdata; - - /* restore cursor to indicate end of drawing */ - WM_cursor_modal_restore(CTX_wm_window(C)); - - /* don't assume that operator data exists at all */ - if (p) { - /* check size of buffer before cleanup, to determine if anything happened here */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - /* turn off radial brush cursor */ - gpencil_draw_toggle_eraser_cursor(C, p, false); - } - - /* always store the new eraser size to be used again next time - * NOTE: Do this even when not in eraser mode, as eraser may - * have been toggled at some point. - */ - U.gp_eraser = p->radius; - - /* clear undo stack */ - gpencil_undo_finish(); - - /* cleanup */ - gp_paint_cleanup(p); - gp_session_cleanup(p); - gp_session_free(p); - p = NULL; - } - - op->customdata = NULL; + tGPsdata *p = op->customdata; + + /* restore cursor to indicate end of drawing */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + /* don't assume that operator data exists at all */ + if (p) { + /* check size of buffer before cleanup, to determine if anything happened here */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* turn off radial brush cursor */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + + /* always store the new eraser size to be used again next time + * NOTE: Do this even when not in eraser mode, as eraser may + * have been toggled at some point. + */ + U.gp_eraser = p->radius; + + /* clear undo stack */ + gpencil_undo_finish(); + + /* cleanup */ + gp_paint_cleanup(p); + gp_session_cleanup(p); + gp_session_free(p); + p = NULL; + } + + op->customdata = NULL; } static void gpencil_draw_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_draw_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_draw_exit(C, op); } /* ------------------------------- */ - static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) { - tGPsdata *p; - eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); - - /* check context */ - p = op->customdata = gp_session_initpaint(C); - if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { - /* something wasn't set correctly in context */ - gpencil_draw_exit(C, op); - return 0; - } - - /* init painting data */ - gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); - if (p->status == GP_STATUS_ERROR) { - gpencil_draw_exit(C, op); - return 0; - } - - if (event != NULL) { - p->keymodifier = event->keymodifier; - } - else { - p->keymodifier = -1; - } - - /* everything is now setup ok */ - return 1; + tGPsdata *p; + eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + + /* check context */ + p = op->customdata = gp_session_initpaint(C); + if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { + /* something wasn't set correctly in context */ + gpencil_draw_exit(C, op); + return 0; + } + + /* init painting data */ + gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); + if (p->status == GP_STATUS_ERROR) { + gpencil_draw_exit(C, op); + return 0; + } + + if (event != NULL) { + p->keymodifier = event->keymodifier; + } + else { + p->keymodifier = -1; + } + + /* everything is now setup ok */ + return 1; } - /* ------------------------------- */ /* ensure that the correct cursor icon is set */ static void gpencil_draw_cursor_set(tGPsdata *p) { - if (p->paintmode == GP_PAINTMODE_ERASER) - WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ - else - WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); + if (p->paintmode == GP_PAINTMODE_ERASER) + WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + else + WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); } /* update UI indicators of status, including cursor and header prints */ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) { - /* header prints */ - switch (p->status) { - case GP_STATUS_PAINTING: - switch (p->paintmode) { - case GP_PAINTMODE_DRAW_POLY: - /* Provide usage tips, since this is modal, and unintuitive without hints */ - ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " - "ESC/Enter to end (or click outside this area)")); - break; - default: - /* Do nothing - the others are self explanatory, exit quickly once the mouse is released - * Showing any text would just be annoying as it would flicker. - */ - break; - } - break; - - case GP_STATUS_IDLING: - /* print status info */ - switch (p->paintmode) { - case GP_PAINTMODE_ERASER: - ED_workspace_status_text(C, IFACE_("Annotation Eraser: Hold and drag LMB or RMB to erase | " - "ESC/Enter to end (or click outside this area)")); - break; - case GP_PAINTMODE_DRAW_STRAIGHT: - ED_workspace_status_text(C, IFACE_("Annotation Line Draw: Hold and drag LMB to draw | " - "ESC/Enter to end (or click outside this area)")); - break; - case GP_PAINTMODE_DRAW: - ED_workspace_status_text(C, IFACE_("Annotation Freehand Draw: Hold and drag LMB to draw | " - "E/ESC/Enter to end (or click outside this area)")); - break; - case GP_PAINTMODE_DRAW_POLY: - ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " - "ESC/Enter to end (or click outside this area)")); - break; - - default: /* unhandled future cases */ - ED_workspace_status_text(C, IFACE_("Annotation Session: ESC/Enter to end (or click outside this area)")); - break; - } - break; - - case GP_STATUS_ERROR: - case GP_STATUS_DONE: - case GP_STATUS_CAPTURE: - /* clear status string */ - ED_workspace_status_text(C, NULL); - break; - } + /* header prints */ + switch (p->status) { + case GP_STATUS_PAINTING: + switch (p->paintmode) { + case GP_PAINTMODE_DRAW_POLY: + /* Provide usage tips, since this is modal, and unintuitive without hints */ + ED_workspace_status_text( + C, + IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + default: + /* Do nothing - the others are self explanatory, exit quickly once the mouse is released + * Showing any text would just be annoying as it would flicker. + */ + break; + } + break; + + case GP_STATUS_IDLING: + /* print status info */ + switch (p->paintmode) { + case GP_PAINTMODE_ERASER: + ED_workspace_status_text(C, + IFACE_("Annotation Eraser: Hold and drag LMB or RMB to erase | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_STRAIGHT: + ED_workspace_status_text(C, + IFACE_("Annotation Line Draw: Hold and drag LMB to draw | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW: + ED_workspace_status_text(C, + IFACE_("Annotation Freehand Draw: Hold and drag LMB to draw | " + "E/ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_POLY: + ED_workspace_status_text( + C, + IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + + default: /* unhandled future cases */ + ED_workspace_status_text( + C, IFACE_("Annotation Session: ESC/Enter to end (or click outside this area)")); + break; + } + break; + + case GP_STATUS_ERROR: + case GP_STATUS_DONE: + case GP_STATUS_CAPTURE: + /* clear status string */ + ED_workspace_status_text(C, NULL); + break; + } } /* ------------------------------- */ @@ -1703,169 +1708,170 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* create a new stroke point at the point indicated by the painting context */ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) { - /* handle drawing/erasing -> test for erasing first */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - /* do 'live' erasing now */ - gp_stroke_doeraser(p); - - /* store used values */ - p->mvalo[0] = p->mval[0]; - p->mvalo[1] = p->mval[1]; - p->opressure = p->pressure; - } - /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ - else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { - /* try to add point */ - short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); - - /* handle errors while adding point */ - if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { - /* finish off old stroke */ - gp_paint_strokeend(p); - /* And start a new one!!! Else, projection errors! */ - gp_paint_initstroke(p, p->paintmode, depsgraph); - - /* start a new stroke, starting from previous point */ - /* XXX Must manually reset inittime... */ - /* XXX We only need to reuse previous point if overflow! */ - if (ok == GP_STROKEADD_OVERFLOW) { - p->inittime = p->ocurtime; - gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); - } - else { - p->inittime = p->curtime; - } - gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); - } - else if (ok == GP_STROKEADD_INVALID) { - /* the painting operation cannot continue... */ - BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); - p->status = GP_STATUS_ERROR; - - if (G.debug & G_DEBUG) - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - return; - } - - /* store used values */ - p->mvalo[0] = p->mval[0]; - p->mvalo[1] = p->mval[1]; - p->opressure = p->pressure; - p->ocurtime = p->curtime; - } + /* handle drawing/erasing -> test for erasing first */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* do 'live' erasing now */ + gp_stroke_doeraser(p); + + /* store used values */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + } + /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ + else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { + /* try to add point */ + short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + + /* handle errors while adding point */ + if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { + /* finish off old stroke */ + gp_paint_strokeend(p); + /* And start a new one!!! Else, projection errors! */ + gp_paint_initstroke(p, p->paintmode, depsgraph); + + /* start a new stroke, starting from previous point */ + /* XXX Must manually reset inittime... */ + /* XXX We only need to reuse previous point if overflow! */ + if (ok == GP_STROKEADD_OVERFLOW) { + p->inittime = p->ocurtime; + gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); + } + else { + p->inittime = p->curtime; + } + gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + } + else if (ok == GP_STROKEADD_INVALID) { + /* the painting operation cannot continue... */ + BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); + p->status = GP_STATUS_ERROR; + + if (G.debug & G_DEBUG) + printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); + return; + } + + /* store used values */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } } /* handle draw event */ -static void annotation_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) +static void annotation_draw_apply_event( + wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) { - tGPsdata *p = op->customdata; - PointerRNA itemptr; - float mousef[2]; - int tablet = 0; - - /* convert from window-space to area-space mouse coordinates - * add any x,y override position for fake events - */ - p->mval[0] = (float)event->mval[0] - x; - p->mval[1] = (float)event->mval[1] - y; - - /* verify key status for straight lines */ - if ((event->ctrl > 0) || (event->alt > 0)) { - if (p->straight[0] == 0) { - int dx = abs((int)(p->mval[0] - p->mvalo[0])); - int dy = abs((int)(p->mval[1] - p->mvalo[1])); - if ((dx > 0) || (dy > 0)) { - /* check mouse direction to replace the other coordinate with previous values */ - if (dx >= dy) { - /* horizontal */ - p->straight[0] = 1; - p->straight[1] = p->mval[1]; /* save y */ - } - else { - /* vertical */ - p->straight[0] = 2; - p->straight[1] = p->mval[0]; /* save x */ - } - } - } - } - else { - p->straight[0] = 0; - } - - p->curtime = PIL_check_seconds_timer(); - - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; - - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } - } - } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } - - /* special exception for start of strokes (i.e. maybe for just a dot) */ - if (p->flags & GP_PAINTFLAG_FIRSTRUN) { - p->flags &= ~GP_PAINTFLAG_FIRSTRUN; - - p->mvalo[0] = p->mval[0]; - p->mvalo[1] = p->mval[1]; - p->opressure = p->pressure; - p->inittime = p->ocurtime = p->curtime; - p->straight[0] = 0; - p->straight[1] = 0; - - /* special exception here for too high pressure values on first touch in - * windows for some tablets, then we just skip first touch... - */ - if (tablet && (p->pressure >= 0.99f)) - return; - } - - /* check if alt key is pressed and limit to straight lines */ - if ((p->paintmode != GP_PAINTMODE_ERASER) && (p->straight[0] != 0)) { - if (p->straight[0] == 1) { - /* horizontal */ - p->mval[1] = p->straight[1]; /* replace y */ - } - else { - /* vertical */ - p->mval[0] = p->straight[1]; /* replace x */ - } - } - - /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - mousef[0] = p->mval[0]; - mousef[1] = p->mval[1]; - RNA_float_set_array(&itemptr, "mouse", mousef); - RNA_float_set(&itemptr, "pressure", p->pressure); - RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); - - RNA_float_set(&itemptr, "time", p->curtime - p->inittime); - - /* apply the current latest drawing point */ - gpencil_draw_apply(op, p, depsgraph); - - /* force refresh */ - /* just active area for now, since doing whole screen is too slow */ - ED_region_tag_redraw(p->ar); + tGPsdata *p = op->customdata; + PointerRNA itemptr; + float mousef[2]; + int tablet = 0; + + /* convert from window-space to area-space mouse coordinates + * add any x,y override position for fake events + */ + p->mval[0] = (float)event->mval[0] - x; + p->mval[1] = (float)event->mval[1] - y; + + /* verify key status for straight lines */ + if ((event->ctrl > 0) || (event->alt > 0)) { + if (p->straight[0] == 0) { + int dx = abs((int)(p->mval[0] - p->mvalo[0])); + int dy = abs((int)(p->mval[1] - p->mvalo[1])); + if ((dx > 0) || (dy > 0)) { + /* check mouse direction to replace the other coordinate with previous values */ + if (dx >= dy) { + /* horizontal */ + p->straight[0] = 1; + p->straight[1] = p->mval[1]; /* save y */ + } + else { + /* vertical */ + p->straight[0] = 2; + p->straight[1] = p->mval[0]; /* save x */ + } + } + } + } + else { + p->straight[0] = 0; + } + + p->curtime = PIL_check_seconds_timer(); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + p->pressure = wmtab->Pressure; + + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; + } + } + } + else { + /* No tablet data -> No pressure info is available */ + p->pressure = 1.0f; + } + + /* special exception for start of strokes (i.e. maybe for just a dot) */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->inittime = p->ocurtime = p->curtime; + p->straight[0] = 0; + p->straight[1] = 0; + + /* special exception here for too high pressure values on first touch in + * windows for some tablets, then we just skip first touch... + */ + if (tablet && (p->pressure >= 0.99f)) + return; + } + + /* check if alt key is pressed and limit to straight lines */ + if ((p->paintmode != GP_PAINTMODE_ERASER) && (p->straight[0] != 0)) { + if (p->straight[0] == 1) { + /* horizontal */ + p->mval[1] = p->straight[1]; /* replace y */ + } + else { + /* vertical */ + p->mval[0] = p->straight[1]; /* replace x */ + } + } + + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + mousef[0] = p->mval[0]; + mousef[1] = p->mval[1]; + RNA_float_set_array(&itemptr, "mouse", mousef); + RNA_float_set(&itemptr, "pressure", p->pressure); + RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); + + RNA_float_set(&itemptr, "time", p->curtime - p->inittime); + + /* apply the current latest drawing point */ + gpencil_draw_apply(op, p, depsgraph); + + /* force refresh */ + /* just active area for now, since doing whole screen is too slow */ + ED_region_tag_redraw(p->ar); } /* ------------------------------- */ @@ -1873,74 +1879,74 @@ static void annotation_draw_apply_event(wmOperator *op, const wmEvent *event, De /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ static int gpencil_draw_exec(bContext *C, wmOperator *op) { - tGPsdata *p = NULL; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - /* printf("GPencil - Starting Re-Drawing\n"); */ - - /* try to initialize context data needed while drawing */ - if (!gpencil_draw_init(C, op, NULL)) { - if (op->customdata) MEM_freeN(op->customdata); - /* printf("\tGP - no valid data\n"); */ - return OPERATOR_CANCELLED; - } - else - p = op->customdata; - - /* printf("\tGP - Start redrawing stroke\n"); */ - - /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), - * setting the relevant values in context at each step, then applying - */ - RNA_BEGIN (op->ptr, itemptr, "stroke") - { - float mousef[2]; - - /* printf("\t\tGP - stroke elem\n"); */ - - /* get relevant data for this point from stroke */ - RNA_float_get_array(&itemptr, "mouse", mousef); - p->mval[0] = (int)mousef[0]; - p->mval[1] = (int)mousef[1]; - p->pressure = RNA_float_get(&itemptr, "pressure"); - p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; - - if (RNA_boolean_get(&itemptr, "is_start")) { - /* if first-run flag isn't set already (i.e. not true first stroke), - * then we must terminate the previous one first before continuing - */ - if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { - /* TODO: both of these ops can set error-status, but we probably don't need to worry */ - gp_paint_strokeend(p); - gp_paint_initstroke(p, p->paintmode, depsgraph); - } - } - - /* if first run, set previous data too */ - if (p->flags & GP_PAINTFLAG_FIRSTRUN) { - p->flags &= ~GP_PAINTFLAG_FIRSTRUN; - - p->mvalo[0] = p->mval[0]; - p->mvalo[1] = p->mval[1]; - p->opressure = p->pressure; - p->ocurtime = p->curtime; - } - - /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(op, p, depsgraph); - } - RNA_END; - - /* printf("\tGP - done\n"); */ - - /* cleanup */ - gpencil_draw_exit(C, op); - - /* refreshes */ - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* done */ - return OPERATOR_FINISHED; + tGPsdata *p = NULL; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* printf("GPencil - Starting Re-Drawing\n"); */ + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, NULL)) { + if (op->customdata) + MEM_freeN(op->customdata); + /* printf("\tGP - no valid data\n"); */ + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* printf("\tGP - Start redrawing stroke\n"); */ + + /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), + * setting the relevant values in context at each step, then applying + */ + RNA_BEGIN (op->ptr, itemptr, "stroke") { + float mousef[2]; + + /* printf("\t\tGP - stroke elem\n"); */ + + /* get relevant data for this point from stroke */ + RNA_float_get_array(&itemptr, "mouse", mousef); + p->mval[0] = (int)mousef[0]; + p->mval[1] = (int)mousef[1]; + p->pressure = RNA_float_get(&itemptr, "pressure"); + p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; + + if (RNA_boolean_get(&itemptr, "is_start")) { + /* if first-run flag isn't set already (i.e. not true first stroke), + * then we must terminate the previous one first before continuing + */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + /* TODO: both of these ops can set error-status, but we probably don't need to worry */ + gp_paint_strokeend(p); + gp_paint_initstroke(p, p->paintmode, depsgraph); + } + } + + /* if first run, set previous data too */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } + + /* apply this data as necessary now (as per usual) */ + gpencil_draw_apply(op, p, depsgraph); + } + RNA_END; + + /* printf("\tGP - done\n"); */ + + /* cleanup */ + gpencil_draw_exit(C, op); + + /* refreshes */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* done */ + return OPERATOR_FINISHED; } /* ------------------------------- */ @@ -1948,521 +1954,540 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) /* start of interactive drawing part of operator */ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *ob = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); - tGPsdata *p = NULL; - - /* support for tablets eraser pen */ - if (gpencil_is_tablet_eraser_active(event)) { - RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); - } - - /* if try to do annotations with a gp object selected, first - * unselect the object to avoid conflicts. - * The solution is not perfect but we can keep running the annotations while - * found a better solution. - */ - if (sa && sa->spacetype == SPACE_VIEW3D) { - if ((ob != NULL) && (ob->type == OB_GPENCIL)) { - ob->mode = OB_MODE_OBJECT; - bGPdata *gpd = (bGPdata *)ob->data; - ED_gpencil_setup_modes(C, gpd, 0); - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - ViewLayer *view_layer = CTX_data_view_layer(C); - BKE_view_layer_base_deselect_all(view_layer); - view_layer->basact = NULL; - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - } - } - - if (G.debug & G_DEBUG) - printf("GPencil - Starting Drawing\n"); - - /* try to initialize context data needed while drawing */ - if (!gpencil_draw_init(C, op, event)) { - if (op->customdata) - MEM_freeN(op->customdata); - if (G.debug & G_DEBUG) - printf("\tGP - no valid data\n"); - return OPERATOR_CANCELLED; - } - else - p = op->customdata; - - /* if empty erase capture and finish */ - if (p->status == GP_STATUS_CAPTURE) { - gpencil_draw_exit(C, op); - - BKE_report(op->reports, RPT_ERROR, "Nothing to erase"); - return OPERATOR_FINISHED; - } - - /* TODO: set any additional settings that we can take from the events? - * TODO? if tablet is erasing, force eraser to be on? */ - - /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ - - /* if eraser is on, draw radial aid */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - gpencil_draw_toggle_eraser_cursor(C, p, true); - } - /* set cursor - * NOTE: This may change later (i.e. intentionally via brush toggle, - * or unintentionally if the user scrolls outside the area)... - */ - gpencil_draw_cursor_set(p); - - /* only start drawing immediately if we're allowed to do so... */ - if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { - /* hotkey invoked - start drawing */ - /* printf("\tGP - set first spot\n"); */ - p->status = GP_STATUS_PAINTING; - - /* handle the initial drawing - i.e. for just doing a simple dot */ - annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); - op->flag |= OP_IS_MODAL_CURSOR_REGION; - } - else { - /* toolbar invoked - don't start drawing yet... */ - /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ - op->flag |= OP_IS_MODAL_CURSOR_REGION; - } - - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - /* add a modal handler for this operator, so that we can then draw continuous strokes */ - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); + tGPsdata *p = NULL; + + /* support for tablets eraser pen */ + if (gpencil_is_tablet_eraser_active(event)) { + RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); + } + + /* if try to do annotations with a gp object selected, first + * unselect the object to avoid conflicts. + * The solution is not perfect but we can keep running the annotations while + * found a better solution. + */ + if (sa && sa->spacetype == SPACE_VIEW3D) { + if ((ob != NULL) && (ob->type == OB_GPENCIL)) { + ob->mode = OB_MODE_OBJECT; + bGPdata *gpd = (bGPdata *)ob->data; + ED_gpencil_setup_modes(C, gpd, 0); + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_base_deselect_all(view_layer); + view_layer->basact = NULL; + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + } + } + + if (G.debug & G_DEBUG) + printf("GPencil - Starting Drawing\n"); + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, event)) { + if (op->customdata) + MEM_freeN(op->customdata); + if (G.debug & G_DEBUG) + printf("\tGP - no valid data\n"); + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* if empty erase capture and finish */ + if (p->status == GP_STATUS_CAPTURE) { + gpencil_draw_exit(C, op); + + BKE_report(op->reports, RPT_ERROR, "Nothing to erase"); + return OPERATOR_FINISHED; + } + + /* TODO: set any additional settings that we can take from the events? + * TODO? if tablet is erasing, force eraser to be on? */ + + /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ + + /* if eraser is on, draw radial aid */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + gpencil_draw_toggle_eraser_cursor(C, p, true); + } + /* set cursor + * NOTE: This may change later (i.e. intentionally via brush toggle, + * or unintentionally if the user scrolls outside the area)... + */ + gpencil_draw_cursor_set(p); + + /* only start drawing immediately if we're allowed to do so... */ + if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { + /* hotkey invoked - start drawing */ + /* printf("\tGP - set first spot\n"); */ + p->status = GP_STATUS_PAINTING; + + /* handle the initial drawing - i.e. for just doing a simple dot */ + annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + else { + /* toolbar invoked - don't start drawing yet... */ + /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* add a modal handler for this operator, so that we can then draw continuous strokes */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) { - bScreen *sc = CTX_wm_screen(C); - return (BLI_findindex(&sc->areabase, sa_test) != -1); + bScreen *sc = CTX_wm_screen(C); + return (BLI_findindex(&sc->areabase, sa_test) != -1); } static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) { - tGPsdata *p = op->customdata; + tGPsdata *p = op->customdata; - /* we must check that we're still within the area that we're set up to work from - * otherwise we could crash (see bug #20586) - */ - if (CTX_wm_area(C) != p->sa) { - printf("\t\t\tGP - wrong area execution abort!\n"); - p->status = GP_STATUS_ERROR; - } + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + printf("\t\t\tGP - wrong area execution abort!\n"); + p->status = GP_STATUS_ERROR; + } - /* printf("\t\tGP - start stroke\n"); */ + /* printf("\t\tGP - start stroke\n"); */ - /* we may need to set up paint env again if we're resuming */ - /* XXX: watch it with the paintmode! in future, - * it'd be nice to allow changing paint-mode when in sketching-sessions */ + /* we may need to set up paint env again if we're resuming */ + /* XXX: watch it with the paintmode! in future, + * it'd be nice to allow changing paint-mode when in sketching-sessions */ - if (gp_session_initdata(C, p)) - gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); + if (gp_session_initdata(C, p)) + gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); - if (p->status != GP_STATUS_ERROR) { - p->status = GP_STATUS_PAINTING; - op->flag &= ~OP_IS_MODAL_CURSOR_REGION; - } + if (p->status != GP_STATUS_ERROR) { + p->status = GP_STATUS_PAINTING; + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } - return op->customdata; + return op->customdata; } static void gpencil_stroke_end(wmOperator *op) { - tGPsdata *p = op->customdata; + tGPsdata *p = op->customdata; - gp_paint_cleanup(p); + gp_paint_cleanup(p); - gpencil_undo_push(p->gpd); + gpencil_undo_push(p->gpd); - gp_session_cleanup(p); + gp_session_cleanup(p); - p->status = GP_STATUS_IDLING; - op->flag |= OP_IS_MODAL_CURSOR_REGION; + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; - p->gpd = NULL; - p->gpl = NULL; - p->gpf = NULL; + p->gpd = NULL; + p->gpl = NULL; + p->gpf = NULL; } /* add events for missing mouse movements when the artist draw very fast */ -static void annotation_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +static void annotation_add_missing_events(bContext *C, + wmOperator *op, + const wmEvent *event, + tGPsdata *p) { - float pt[2], a[2], b[2]; - float factor = 10.0f; - - copy_v2_v2(a, p->mvalo); - b[0] = (float)event->mval[0] + 1.0f; - b[1] = (float)event->mval[1] + 1.0f; - - /* get distance in pixels */ - float dist = len_v2v2(a, b); - - /* for very small distances, add a half way point */ - if (dist <= 2.0f) { - interp_v2_v2v2(pt, a, b, 0.5f); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), pt[0], pt[1]); - } - else if (dist >= factor) { - int slices = 2 + (int)((dist - 1.0) / factor); - float n = 1.0f / slices; - for (int i = 1; i < slices; i++) { - interp_v2_v2v2(pt, a, b, n * i); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - annotation_draw_apply_event( - op, event, CTX_data_depsgraph(C), - pt[0], pt[1]); - } - } + float pt[2], a[2], b[2]; + float factor = 10.0f; + + copy_v2_v2(a, p->mvalo); + b[0] = (float)event->mval[0] + 1.0f; + b[1] = (float)event->mval[1] + 1.0f; + + /* get distance in pixels */ + float dist = len_v2v2(a, b); + + /* for very small distances, add a half way point */ + if (dist <= 2.0f) { + interp_v2_v2v2(pt, a, b, 0.5f); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + } + else if (dist >= factor) { + int slices = 2 + (int)((dist - 1.0) / factor); + float n = 1.0f / slices; + for (int i = 1; i < slices; i++) { + interp_v2_v2v2(pt, a, b, n * i); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + } + } } /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGPsdata *p = op->customdata; - /* default exit state - pass through to support MMB view nav, etc. */ - int estate = OPERATOR_PASS_THROUGH; - - /* if (event->type == NDOF_MOTION) - * return OPERATOR_PASS_THROUGH; - * ------------------------------- - * [mce] Not quite what I was looking - * for, but a good start! GP continues to - * draw on the screen while the 3D mouse - * moves the viewpoint. Problem is that - * the stroke is converted to 3D only after - * it is finished. This approach should work - * better in tools that immediately apply - * in 3D space. - */ - - if (p->status == GP_STATUS_IDLING) { - ARegion *ar = CTX_wm_region(C); - p->ar = ar; - } - - /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ - if (ISKEYBOARD(event->type)) { - if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { - /* allow some keys: - * - for frame changing [#33412] - * - for undo (during sketching sessions) - */ - } - else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { - /* allow numpad keys so that camera/view manipulations can still take place - * - PAD0 in particular is really important for Grease Pencil drawing, - * as animators may be working "to camera", so having this working - * is essential for ensuring that they can quickly return to that view - */ - } - else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { - /* Add Blank Frame - * - Since this operator is non-modal, we can just call it here, and keep going... - * - This operator is especially useful when animating - */ - WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); - estate = OPERATOR_RUNNING_MODAL; - } - else { - estate = OPERATOR_RUNNING_MODAL; - } - } - - //printf("\tGP - handle modal event...\n"); - - /* exit painting mode (and/or end current stroke) - * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] - */ - if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { - /* exit() ends the current stroke before cleaning up */ - /* printf("\t\tGP - end of paint op + end of stroke\n"); */ - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - - /* toggle painting mode upon mouse-button movement - * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) - * - RIGHTMOUSE = polyline (hotkey) / eraser (all) - * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) - * also making sure we have a valid event value, to not exit too early - */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { - /* if painting, end stroke */ - if (p->status == GP_STATUS_PAINTING) { - int sketch = 0; - - /* basically, this should be mouse-button up = end stroke - * BUT, polyline drawing is an exception -- all knots should be added during one session - */ - sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); - - if (sketch) { - /* end stroke only, and then wait to resume painting soon */ - /* printf("\t\tGP - end stroke only\n"); */ - gpencil_stroke_end(op); - - /* If eraser mode is on, turn it off after the stroke finishes - * NOTE: This just makes it nicer to work with drawing sessions - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - p->paintmode = RNA_enum_get(op->ptr, "mode"); - - /* if the original mode was *still* eraser, - * we'll let it say for now, since this gives - * users an opportunity to have visual feedback - * when adjusting eraser size - */ - if (p->paintmode != GP_PAINTMODE_ERASER) { - /* turn off cursor... - * NOTE: this should be enough for now - * Just hiding this makes it seem like - * you can paint again... - */ - gpencil_draw_toggle_eraser_cursor(C, p, false); - } - } - - /* we've just entered idling state, so this event was processed (but no others yet) */ - estate = OPERATOR_RUNNING_MODAL; - - /* stroke could be smoothed, send notifier to refresh screen */ - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - } - else { - /* printf("\t\tGP - end of stroke + op\n"); */ - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - } - else if (event->val == KM_PRESS) { - bool in_bounds = false; - - /* Check if we're outside the bounds of the active region - * NOTE: An exception here is that if launched from the toolbar, - * whatever region we're now in should become the new region - */ - if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { - /* Change to whatever region is now under the mouse */ - ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); - - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", - current_region, p->ar, event->x, event->y, - p->sa->totrct.xmin, p->sa->totrct.ymin, p->sa->totrct.xmax, p->sa->totrct.ymax); - } - - if (current_region) { - /* Assume that since we found the cursor in here, it is in bounds - * and that this should be the region that we begin drawing in - */ - p->ar = current_region; - in_bounds = true; - } - else { - /* Out of bounds, or invalid in some other way */ - p->status = GP_STATUS_ERROR; - estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } - } - else if (p->ar) { - rcti region_rect; - - /* Perform bounds check using */ - ED_region_visible_rect(p->ar, ®ion_rect); - in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); - } - else { - /* No region */ - p->status = GP_STATUS_ERROR; - estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) - printf("%s: No active region found in GP Paint session data\n", __func__); - } - - if (in_bounds) { - /* Switch paintmode (temporarily if need be) based on which button was used - * NOTE: This is to make it more convenient to erase strokes when using drawing sessions - */ - if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { - /* turn on eraser */ - p->paintmode = GP_PAINTMODE_ERASER; - } - else if (event->type == LEFTMOUSE) { - /* restore drawmode to default */ - p->paintmode = RNA_enum_get(op->ptr, "mode"); - } - - gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); - - /* not painting, so start stroke (this should be mouse-button down) */ - p = gpencil_stroke_begin(C, op); - - if (p->status == GP_STATUS_ERROR) { - estate = OPERATOR_CANCELLED; - } - } - else if (p->status != GP_STATUS_ERROR) { - /* User clicked outside bounds of window while idling, so exit paintmode - * NOTE: Don't enter this case if an error occurred while finding the - * region (as above) - */ - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - } - else if (event->val == KM_RELEASE) { - p->status = GP_STATUS_IDLING; - op->flag |= OP_IS_MODAL_CURSOR_REGION; - } - } - - /* handle mode-specific events */ - if (p->status == GP_STATUS_PAINTING) { - /* handle painting mouse-movements? */ - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { - /* handle drawing event */ - if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { - annotation_add_missing_events(C, op, event, p); - } - - annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); - - /* finish painting operation if anything went wrong just now */ - if (p->status == GP_STATUS_ERROR) { - printf("\t\t\t\tGP - add error done!\n"); - estate = OPERATOR_CANCELLED; - } - else { - /* event handled, so just tag as running modal */ - /* printf("\t\t\t\tGP - add point handled!\n"); */ - estate = OPERATOR_RUNNING_MODAL; - } - } - /* eraser size */ - else if ((p->paintmode == GP_PAINTMODE_ERASER) && - ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) - { - /* just resize the brush (local version) - * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys - */ - /* printf("\t\tGP - resize eraser\n"); */ - switch (event->type) { - case WHEELDOWNMOUSE: /* larger */ - case PADPLUSKEY: - p->radius += 5; - break; - - case WHEELUPMOUSE: /* smaller */ - case PADMINUS: - p->radius -= 5; - - if (p->radius <= 0) - p->radius = 1; - break; - } - - /* force refresh */ - /* just active area for now, since doing whole screen is too slow */ - ED_region_tag_redraw(p->ar); - - /* event handled, so just tag as running modal */ - estate = OPERATOR_RUNNING_MODAL; - } - /* there shouldn't be any other events, but just in case there are, let's swallow them - * (i.e. to prevent problems with undo) - */ - else { - /* swallow event to save ourselves trouble */ - estate = OPERATOR_RUNNING_MODAL; - } - } - - /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ - if (0 == gpencil_area_exists(C, p->sa)) - estate = OPERATOR_CANCELLED; - else { - /* update status indicators - cursor, header, etc. */ - gpencil_draw_status_indicators(C, p); - /* cursor may have changed outside our control - T44084 */ - gpencil_draw_cursor_set(p); - } - - /* process last operations before exiting */ - switch (estate) { - case OPERATOR_FINISHED: - /* one last flush before we're done */ - gpencil_draw_exit(C, op); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - break; - - case OPERATOR_CANCELLED: - gpencil_draw_exit(C, op); - break; - - case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: - /* event doesn't need to be handled */ + tGPsdata *p = op->customdata; + /* default exit state - pass through to support MMB view nav, etc. */ + int estate = OPERATOR_PASS_THROUGH; + + /* if (event->type == NDOF_MOTION) + * return OPERATOR_PASS_THROUGH; + * ------------------------------- + * [mce] Not quite what I was looking + * for, but a good start! GP continues to + * draw on the screen while the 3D mouse + * moves the viewpoint. Problem is that + * the stroke is converted to 3D only after + * it is finished. This approach should work + * better in tools that immediately apply + * in 3D space. + */ + + if (p->status == GP_STATUS_IDLING) { + ARegion *ar = CTX_wm_region(C); + p->ar = ar; + } + + /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ + if (ISKEYBOARD(event->type)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { + /* allow some keys: + * - for frame changing [#33412] + * - for undo (during sketching sessions) + */ + } + else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { + /* allow numpad keys so that camera/view manipulations can still take place + * - PAD0 in particular is really important for Grease Pencil drawing, + * as animators may be working "to camera", so having this working + * is essential for ensuring that they can quickly return to that view + */ + } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } + else { + estate = OPERATOR_RUNNING_MODAL; + } + } + + //printf("\tGP - handle modal event...\n"); + + /* exit painting mode (and/or end current stroke) + * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] + */ + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { + /* exit() ends the current stroke before cleaning up */ + /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + + /* toggle painting mode upon mouse-button movement + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) + * - RIGHTMOUSE = polyline (hotkey) / eraser (all) + * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) + * also making sure we have a valid event value, to not exit too early + */ + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { + /* if painting, end stroke */ + if (p->status == GP_STATUS_PAINTING) { + int sketch = 0; + + /* basically, this should be mouse-button up = end stroke + * BUT, polyline drawing is an exception -- all knots should be added during one session + */ + sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); + + if (sketch) { + /* end stroke only, and then wait to resume painting soon */ + /* printf("\t\tGP - end stroke only\n"); */ + gpencil_stroke_end(op); + + /* If eraser mode is on, turn it off after the stroke finishes + * NOTE: This just makes it nicer to work with drawing sessions + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->paintmode = RNA_enum_get(op->ptr, "mode"); + + /* if the original mode was *still* eraser, + * we'll let it say for now, since this gives + * users an opportunity to have visual feedback + * when adjusting eraser size + */ + if (p->paintmode != GP_PAINTMODE_ERASER) { + /* turn off cursor... + * NOTE: this should be enough for now + * Just hiding this makes it seem like + * you can paint again... + */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + } + + /* we've just entered idling state, so this event was processed (but no others yet) */ + estate = OPERATOR_RUNNING_MODAL; + + /* stroke could be smoothed, send notifier to refresh screen */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + } + else { + /* printf("\t\tGP - end of stroke + op\n"); */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_PRESS) { + bool in_bounds = false; + + /* Check if we're outside the bounds of the active region + * NOTE: An exception here is that if launched from the toolbar, + * whatever region we're now in should become the new region + */ + if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { + /* Change to whatever region is now under the mouse */ + ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); + + if (G.debug & G_DEBUG) { + printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", + current_region, + p->ar, + event->x, + event->y, + p->sa->totrct.xmin, + p->sa->totrct.ymin, + p->sa->totrct.xmax, + p->sa->totrct.ymax); + } + + if (current_region) { + /* Assume that since we found the cursor in here, it is in bounds + * and that this should be the region that we begin drawing in + */ + p->ar = current_region; + in_bounds = true; + } + else { + /* Out of bounds, or invalid in some other way */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); + } + } + else if (p->ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(p->ar, ®ion_rect); + in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + } + else { + /* No region */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: No active region found in GP Paint session data\n", __func__); + } + + if (in_bounds) { + /* Switch paintmode (temporarily if need be) based on which button was used + * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + */ + if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { + /* turn on eraser */ + p->paintmode = GP_PAINTMODE_ERASER; + } + else if (event->type == LEFTMOUSE) { + /* restore drawmode to default */ + p->paintmode = RNA_enum_get(op->ptr, "mode"); + } + + gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); + + /* not painting, so start stroke (this should be mouse-button down) */ + p = gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { + estate = OPERATOR_CANCELLED; + } + } + else if (p->status != GP_STATUS_ERROR) { + /* User clicked outside bounds of window while idling, so exit paintmode + * NOTE: Don't enter this case if an error occurred while finding the + * region (as above) + */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_RELEASE) { + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + } + + /* handle mode-specific events */ + if (p->status == GP_STATUS_PAINTING) { + /* handle painting mouse-movements? */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { + /* handle drawing event */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + annotation_add_missing_events(C, op, event, p); + } + + annotation_draw_apply_event(op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); + + /* finish painting operation if anything went wrong just now */ + if (p->status == GP_STATUS_ERROR) { + printf("\t\t\t\tGP - add error done!\n"); + estate = OPERATOR_CANCELLED; + } + else { + /* event handled, so just tag as running modal */ + /* printf("\t\t\t\tGP - add point handled!\n"); */ + estate = OPERATOR_RUNNING_MODAL; + } + } + /* eraser size */ + else if ((p->paintmode == GP_PAINTMODE_ERASER) && + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) { + /* just resize the brush (local version) + * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys + */ + /* printf("\t\tGP - resize eraser\n"); */ + switch (event->type) { + case WHEELDOWNMOUSE: /* larger */ + case PADPLUSKEY: + p->radius += 5; + break; + + case WHEELUPMOUSE: /* smaller */ + case PADMINUS: + p->radius -= 5; + + if (p->radius <= 0) + p->radius = 1; + break; + } + + /* force refresh */ + /* just active area for now, since doing whole screen is too slow */ + ED_region_tag_redraw(p->ar); + + /* event handled, so just tag as running modal */ + estate = OPERATOR_RUNNING_MODAL; + } + /* there shouldn't be any other events, but just in case there are, let's swallow them + * (i.e. to prevent problems with undo) + */ + else { + /* swallow event to save ourselves trouble */ + estate = OPERATOR_RUNNING_MODAL; + } + } + + /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + if (0 == gpencil_area_exists(C, p->sa)) + estate = OPERATOR_CANCELLED; + else { + /* update status indicators - cursor, header, etc. */ + gpencil_draw_status_indicators(C, p); + /* cursor may have changed outside our control - T44084 */ + gpencil_draw_cursor_set(p); + } + + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + /* one last flush before we're done */ + gpencil_draw_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_draw_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + /* event doesn't need to be handled */ #if 0 - printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", - event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE); + printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", + event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE); #endif - break; - } + break; + } - /* return status code */ - return estate; + /* return status code */ + return estate; } /* ------------------------------- */ static const EnumPropertyItem prop_gpencil_drawmodes[] = { - {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, - {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", "Draw straight line segment(s)"}, - {GP_PAINTMODE_DRAW_POLY, "DRAW_POLY", 0, "Draw Poly Line", "Click to place endpoints of straight line segments (connected)"}, - {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Annotation strokes"}, - {0, NULL, 0, NULL, NULL}, + {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, + {GP_PAINTMODE_DRAW_STRAIGHT, + "DRAW_STRAIGHT", + 0, + "Draw Straight Lines", + "Draw straight line segment(s)"}, + {GP_PAINTMODE_DRAW_POLY, + "DRAW_POLY", + 0, + "Draw Poly Line", + "Click to place endpoints of straight line segments (connected)"}, + {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Annotation strokes"}, + {0, NULL, 0, NULL, NULL}, }; void GPENCIL_OT_annotate(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Annotation Draw"; - ot->idname = "GPENCIL_OT_annotate"; - ot->description = "Make annotations on the active data"; - - /* api callbacks */ - ot->exec = gpencil_draw_exec; - ot->invoke = gpencil_draw_invoke; - ot->modal = gpencil_draw_modal; - ot->cancel = gpencil_draw_cancel; - ot->poll = gpencil_draw_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* settings for drawing */ - ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); - - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* NOTE: wait for input is enabled by default, - * so that all UI code can work properly without needing users to know about this */ - prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Annotation Draw"; + ot->idname = "GPENCIL_OT_annotate"; + ot->description = "Make annotations on the active data"; + + /* api callbacks */ + ot->exec = gpencil_draw_exec; + ot->invoke = gpencil_draw_invoke; + ot->modal = gpencil_draw_modal; + ot->cancel = gpencil_draw_cancel; + ot->poll = gpencil_draw_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* settings for drawing */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* NOTE: wait for input is enabled by default, + * so that all UI code can work properly without needing users to know about this */ + prop = RNA_def_boolean(ot->srna, + "wait_for_input", + true, + "Wait for Input", + "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 711962400ff..24bdbcc4cba 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -84,31 +83,29 @@ /* ----- General Defines ------ */ /* flags for sflag */ typedef enum eDrawStrokeFlags { - /** don't draw status info */ - GP_DRAWDATA_NOSTATUS = (1 << 0), - /** only draw 3d-strokes */ - GP_DRAWDATA_ONLY3D = (1 << 1), - /** only draw 'canvas' strokes */ - GP_DRAWDATA_ONLYV2D = (1 << 2), - /** only draw 'image' strokes */ - GP_DRAWDATA_ONLYI2D = (1 << 3), - /** special hack for drawing strokes in Image Editor (weird coordinates) */ - GP_DRAWDATA_IEDITHACK = (1 << 4), - /** don't draw xray in 3D view (which is default) */ - GP_DRAWDATA_NO_XRAY = (1 << 5), - /** no onionskins should be drawn (for animation playback) */ - GP_DRAWDATA_NO_ONIONS = (1 << 6), - /** draw strokes as "volumetric" circular billboards */ - GP_DRAWDATA_VOLUMETRIC = (1 << 7), - /** fill insides/bounded-regions of strokes */ - GP_DRAWDATA_FILL = (1 << 8), + /** don't draw status info */ + GP_DRAWDATA_NOSTATUS = (1 << 0), + /** only draw 3d-strokes */ + GP_DRAWDATA_ONLY3D = (1 << 1), + /** only draw 'canvas' strokes */ + GP_DRAWDATA_ONLYV2D = (1 << 2), + /** only draw 'image' strokes */ + GP_DRAWDATA_ONLYI2D = (1 << 3), + /** special hack for drawing strokes in Image Editor (weird coordinates) */ + GP_DRAWDATA_IEDITHACK = (1 << 4), + /** don't draw xray in 3D view (which is default) */ + GP_DRAWDATA_NO_XRAY = (1 << 5), + /** no onionskins should be drawn (for animation playback) */ + GP_DRAWDATA_NO_ONIONS = (1 << 6), + /** draw strokes as "volumetric" circular billboards */ + GP_DRAWDATA_VOLUMETRIC = (1 << 7), + /** fill insides/bounded-regions of strokes */ + GP_DRAWDATA_FILL = (1 << 8), } eDrawStrokeFlags; - - /* thickness above which we should use special drawing */ #if 0 -#define GP_DRAWTHICKNESS_SPECIAL 3 +# define GP_DRAWTHICKNESS_SPECIAL 3 #endif /* conversion utility (float --> normalized unsigned byte) */ @@ -119,644 +116,697 @@ typedef enum eDrawStrokeFlags { static void gp_set_point_uniform_color(const bGPDspoint *pt, const float ink[4]) { - float alpha = ink[3] * pt->strength; - CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); - immUniformColor3fvAlpha(ink, alpha); + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + immUniformColor3fvAlpha(ink, alpha); } static void gp_set_point_varying_color(const bGPDspoint *pt, const float ink[4], uint attr_id) { - float alpha = ink[3] * pt->strength; - CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); - immAttr4ub(attr_id, F2UB(ink[0]), F2UB(ink[1]), F2UB(ink[2]), F2UB(alpha)); + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + immAttr4ub(attr_id, F2UB(ink[0]), F2UB(ink[1]), F2UB(ink[2]), F2UB(alpha)); } /* --------- 2D Stroke Drawing Helpers --------- */ /* change in parameter list */ -static void gp_calc_2d_stroke_fxy(const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +static void gp_calc_2d_stroke_fxy( + const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) { - if (sflag & GP_STROKE_2DSPACE) { - r_co[0] = pt[0]; - r_co[1] = pt[1]; - } - else if (sflag & GP_STROKE_2DIMAGE) { - const float x = (float)((pt[0] * winx) + offsx); - const float y = (float)((pt[1] * winy) + offsy); - - r_co[0] = x; - r_co[1] = y; - } - else { - const float x = (float)(pt[0] / 100 * winx) + offsx; - const float y = (float)(pt[1] / 100 * winy) + offsy; - - r_co[0] = x; - r_co[1] = y; - } + if (sflag & GP_STROKE_2DSPACE) { + r_co[0] = pt[0]; + r_co[1] = pt[1]; + } + else if (sflag & GP_STROKE_2DIMAGE) { + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + + r_co[0] = x; + r_co[1] = y; + } + else { + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + + r_co[0] = x; + r_co[1] = y; + } } /* ----------- Volumetric Strokes --------------- */ /* draw a 2D strokes in "volumetric" style */ -static void gp_draw_stroke_volumetric_2d( - const bGPDspoint *points, int totpoints, short thickness, - short UNUSED(dflag), short sflag, - int offsx, int offsy, int winx, int winy, - const float diff_mat[4][4], const float ink[4]) +static void gp_draw_stroke_volumetric_2d(const bGPDspoint *points, + int totpoints, + short thickness, + short UNUSED(dflag), + short sflag, + int offsx, + int offsy, + int winx, + int winy, + const float diff_mat[4][4], + const float ink[4]) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); - GPU_enable_program_point_size(); - immBegin(GPU_PRIM_POINTS, totpoints); - - const bGPDspoint *pt = points; - for (int i = 0; i < totpoints; i++, pt++) { - /* transform position to 2D */ - float co[2]; - float fpt[3]; - - mul_v3_m4v3(fpt, diff_mat, &pt->x); - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); - - gp_set_point_varying_color(pt, ink, color); - immAttr1f(size, pt->pressure * thickness); /* TODO: scale based on view transform */ - immVertex2f(pos, co[0], co[1]); - } - - immEnd(); - immUnbindProgram(); - GPU_disable_program_point_size(); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); + GPU_enable_program_point_size(); + immBegin(GPU_PRIM_POINTS, totpoints); + + const bGPDspoint *pt = points; + for (int i = 0; i < totpoints; i++, pt++) { + /* transform position to 2D */ + float co[2]; + float fpt[3]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); + + gp_set_point_varying_color(pt, ink, color); + immAttr1f(size, pt->pressure * thickness); /* TODO: scale based on view transform */ + immVertex2f(pos, co[0], co[1]); + } + + immEnd(); + immUnbindProgram(); + GPU_disable_program_point_size(); } /* draw a 3D stroke in "volumetric" style */ -static void gp_draw_stroke_volumetric_3d( - const bGPDspoint *points, int totpoints, short thickness, - const float ink[4]) +static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points, + int totpoints, + short thickness, + const float ink[4]) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); - GPU_enable_program_point_size(); - immBegin(GPU_PRIM_POINTS, totpoints); - - const bGPDspoint *pt = points; - for (int i = 0; i < totpoints && pt; i++, pt++) { - gp_set_point_varying_color(pt, ink, color); - /* TODO: scale based on view transform */ - immAttr1f(size, pt->pressure * thickness); - /* we can adjust size in vertex shader based on view/projection! */ - immVertex3fv(pos, &pt->x); - } - - immEnd(); - immUnbindProgram(); - GPU_disable_program_point_size(); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); + GPU_enable_program_point_size(); + immBegin(GPU_PRIM_POINTS, totpoints); + + const bGPDspoint *pt = points; + for (int i = 0; i < totpoints && pt; i++, pt++) { + gp_set_point_varying_color(pt, ink, color); + /* TODO: scale based on view transform */ + immAttr1f(size, pt->pressure * thickness); + /* we can adjust size in vertex shader based on view/projection! */ + immVertex3fv(pos, &pt->x); + } + + immEnd(); + immUnbindProgram(); + GPU_disable_program_point_size(); } - /* --------------- Stroke Fills ----------------- */ /* calc bounding box in 2d using flat projection data */ -static void gp_calc_2d_bounding_box(const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand) +static void gp_calc_2d_bounding_box( + const float (*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand) { - copy_v2_v2(minv, points2d[0]); - copy_v2_v2(maxv, points2d[0]); - - for (int i = 1; i < totpoints; i++) { - /* min */ - if (points2d[i][0] < minv[0]) { - minv[0] = points2d[i][0]; - } - if (points2d[i][1] < minv[1]) { - minv[1] = points2d[i][1]; - } - /* max */ - if (points2d[i][0] > maxv[0]) { - maxv[0] = points2d[i][0]; - } - if (points2d[i][1] > maxv[1]) { - maxv[1] = points2d[i][1]; - } - } - /* If not expanded, use a perfect square */ - if (expand == false) { - if (maxv[0] > maxv[1]) { - maxv[1] = maxv[0]; - } - else { - maxv[0] = maxv[1]; - } - } + copy_v2_v2(minv, points2d[0]); + copy_v2_v2(maxv, points2d[0]); + + for (int i = 1; i < totpoints; i++) { + /* min */ + if (points2d[i][0] < minv[0]) { + minv[0] = points2d[i][0]; + } + if (points2d[i][1] < minv[1]) { + minv[1] = points2d[i][1]; + } + /* max */ + if (points2d[i][0] > maxv[0]) { + maxv[0] = points2d[i][0]; + } + if (points2d[i][1] > maxv[1]) { + maxv[1] = points2d[i][1]; + } + } + /* If not expanded, use a perfect square */ + if (expand == false) { + if (maxv[0] > maxv[1]) { + maxv[1] = maxv[0]; + } + else { + maxv[0] = maxv[1]; + } + } } /* calc texture coordinates using flat projected points */ -static void gp_calc_stroke_text_coordinates(const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], float(*r_uv)[2]) +static void gp_calc_stroke_text_coordinates( + const float (*points2d)[2], int totpoints, float minv[2], float maxv[2], float (*r_uv)[2]) { - float d[2]; - d[0] = maxv[0] - minv[0]; - d[1] = maxv[1] - minv[1]; - for (int i = 0; i < totpoints; i++) { - r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0]; - r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1]; - } + float d[2]; + d[0] = maxv[0] - minv[0]; + d[1] = maxv[1] - minv[1]; + for (int i = 0; i < totpoints; i++) { + r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0]; + r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1]; + } } /* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ static void gp_triangulate_stroke_fill(bGPDstroke *gps) { - BLI_assert(gps->totpoints >= 3); - - /* allocate memory for temporary areas */ - gps->tot_triangles = gps->totpoints - 2; - uint (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); - float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); - float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data"); - - int direction = 0; - - /* convert to 2d and triangulate */ - BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); - BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles); - - /* calc texture coordinates automatically */ - float minv[2]; - float maxv[2]; - /* first needs bounding box data */ - gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false); - /* calc uv data */ - gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv); - - /* Number of triangles */ - gps->tot_triangles = gps->totpoints - 2; - /* save triangulation data in stroke cache */ - if (gps->tot_triangles > 0) { - if (gps->triangles == NULL) { - gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, "GP Stroke triangulation"); - } - else { - gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); - } - - for (int i = 0; i < gps->tot_triangles; i++) { - bGPDtriangle *stroke_triangle = &gps->triangles[i]; - memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3])); - /* copy texture coordinates */ - copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]); - copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]); - copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]); - } - } - else { - /* No triangles needed - Free anything allocated previously */ - if (gps->triangles) - MEM_freeN(gps->triangles); - - gps->triangles = NULL; - } - - /* disable recalculation flag */ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; - } - - /* clear memory */ - MEM_SAFE_FREE(tmp_triangles); - MEM_SAFE_FREE(points2d); - MEM_SAFE_FREE(uv); + BLI_assert(gps->totpoints >= 3); + + /* allocate memory for temporary areas */ + gps->tot_triangles = gps->totpoints - 2; + uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, + "GP Stroke temp triangulation"); + float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, + "GP Stroke temp 2d points"); + float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data"); + + int direction = 0; + + /* convert to 2d and triangulate */ + BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); + BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles); + + /* calc texture coordinates automatically */ + float minv[2]; + float maxv[2]; + /* first needs bounding box data */ + gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false); + /* calc uv data */ + gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv); + + /* Number of triangles */ + gps->tot_triangles = gps->totpoints - 2; + /* save triangulation data in stroke cache */ + if (gps->tot_triangles > 0) { + if (gps->triangles == NULL) { + gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, + "GP Stroke triangulation"); + } + else { + gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); + } + + for (int i = 0; i < gps->tot_triangles; i++) { + bGPDtriangle *stroke_triangle = &gps->triangles[i]; + memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3])); + /* copy texture coordinates */ + copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]); + copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]); + copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]); + } + } + else { + /* No triangles needed - Free anything allocated previously */ + if (gps->triangles) + MEM_freeN(gps->triangles); + + gps->triangles = NULL; + } + + /* disable recalculation flag */ + if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { + gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; + } + + /* clear memory */ + MEM_SAFE_FREE(tmp_triangles); + MEM_SAFE_FREE(points2d); + MEM_SAFE_FREE(uv); } /* add a new fill point and texture coordinates to vertex buffer */ -static void gp_add_filldata_tobuffer( - const bGPDspoint *pt, const float uv[2], uint pos, uint texcoord, short flag, - int offsx, int offsy, int winx, int winy, const float diff_mat[4][4]) +static void gp_add_filldata_tobuffer(const bGPDspoint *pt, + const float uv[2], + uint pos, + uint texcoord, + short flag, + int offsx, + int offsy, + int winx, + int winy, + const float diff_mat[4][4]) { - float fpt[3]; - float co[2]; - - mul_v3_m4v3(fpt, diff_mat, &pt->x); - /* if 2d, need conversion */ - if (!(flag & GP_STROKE_3DSPACE)) { - gp_calc_2d_stroke_fxy(fpt, flag, offsx, offsy, winx, winy, co); - copy_v2_v2(fpt, co); - fpt[2] = 0.0f; /* 2d always is z=0.0f */ - } - - immAttr2f(texcoord, uv[0], uv[1]); /* texture coordinates */ - immVertex3fv(pos, fpt); /* position */ + float fpt[3]; + float co[2]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* if 2d, need conversion */ + if (!(flag & GP_STROKE_3DSPACE)) { + gp_calc_2d_stroke_fxy(fpt, flag, offsx, offsy, winx, winy, co); + copy_v2_v2(fpt, co); + fpt[2] = 0.0f; /* 2d always is z=0.0f */ + } + + immAttr2f(texcoord, uv[0], uv[1]); /* texture coordinates */ + immVertex3fv(pos, fpt); /* position */ } #if 0 /* GPXX disabled, not used in annotations */ /* assign image texture for filling stroke */ static int gp_set_filling_texture(Image *image, short flag) { - ImBuf *ibuf; - uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D]; - int error = GL_NO_ERROR; - ImageUser iuser = { NULL }; - void *lock; - - iuser.ok = true; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(image, ibuf, NULL); - return (int)GL_INVALID_OPERATION; - } - - GPU_create_gl_tex( - bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, - false, false, image); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (flag & GP_STYLE_COLOR_TEX_CLAMP) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - BKE_image_release_ibuf(image, ibuf, NULL); - - return error; + ImBuf *ibuf; + uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D]; + int error = GL_NO_ERROR; + ImageUser iuser = { NULL }; + void *lock; + + iuser.ok = true; + + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf == NULL || ibuf->rect == NULL) { + BKE_image_release_ibuf(image, ibuf, NULL); + return (int)GL_INVALID_OPERATION; + } + + GPU_create_gl_tex( + bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, + false, false, image); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (flag & GP_STYLE_COLOR_TEX_CLAMP) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + BKE_image_release_ibuf(image, ibuf, NULL); + + return error; } #endif /* draw fills for shapes */ -static void gp_draw_stroke_fill( - bGPdata *gpd, bGPDstroke *gps, - int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float color[4]) +static void gp_draw_stroke_fill(bGPdata *gpd, + bGPDstroke *gps, + int offsx, + int offsy, + int winx, + int winy, + const float diff_mat[4][4], + const float color[4]) { - BLI_assert(gps->totpoints >= 3); - Material *ma = gpd->mat[gps->mat_nr]; - MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; - - /* Calculate triangles cache for filling area (must be done only after changes) */ - if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { - gp_triangulate_stroke_fill(gps); - } - BLI_assert(gps->tot_triangles >= 1); - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_GPENCIL_FILL); - - immUniformColor4fv(color); - immUniform4fv("color2", gp_style->mix_rgba); - immUniform1i("fill_type", gp_style->fill_style); - immUniform1f("mix_factor", gp_style->mix_factor); - - immUniform1f("gradient_angle", gp_style->gradient_angle); - immUniform1f("gradient_radius", gp_style->gradient_radius); - immUniform1f("pattern_gridsize", gp_style->pattern_gridsize); - immUniform2fv("gradient_scale", gp_style->gradient_scale); - immUniform2fv("gradient_shift", gp_style->gradient_shift); - - immUniform1f("texture_angle", gp_style->texture_angle); - immUniform2fv("texture_scale", gp_style->texture_scale); - immUniform2fv("texture_offset", gp_style->texture_offset); - immUniform1f("texture_opacity", gp_style->texture_opacity); - immUniform1i("t_mix", (gp_style->flag & GP_STYLE_COLOR_TEX_MIX) != 0); - immUniform1i("t_flip", (gp_style->flag & GP_STYLE_COLOR_FLIP_FILL) != 0); + BLI_assert(gps->totpoints >= 3); + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; + + /* Calculate triangles cache for filling area (must be done only after changes) */ + if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) || + (gps->triangles == NULL)) { + gp_triangulate_stroke_fill(gps); + } + BLI_assert(gps->tot_triangles >= 1); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_GPENCIL_FILL); + + immUniformColor4fv(color); + immUniform4fv("color2", gp_style->mix_rgba); + immUniform1i("fill_type", gp_style->fill_style); + immUniform1f("mix_factor", gp_style->mix_factor); + + immUniform1f("gradient_angle", gp_style->gradient_angle); + immUniform1f("gradient_radius", gp_style->gradient_radius); + immUniform1f("pattern_gridsize", gp_style->pattern_gridsize); + immUniform2fv("gradient_scale", gp_style->gradient_scale); + immUniform2fv("gradient_shift", gp_style->gradient_shift); + + immUniform1f("texture_angle", gp_style->texture_angle); + immUniform2fv("texture_scale", gp_style->texture_scale); + immUniform2fv("texture_offset", gp_style->texture_offset); + immUniform1f("texture_opacity", gp_style->texture_opacity); + immUniform1i("t_mix", (gp_style->flag & GP_STYLE_COLOR_TEX_MIX) != 0); + immUniform1i("t_flip", (gp_style->flag & GP_STYLE_COLOR_FLIP_FILL) != 0); #if 0 /* GPXX disabled, not used in annotations */ - /* image texture */ - if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) || (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) { - gp_set_filling_texture(gp_style->ima, gp_style->flag); - } + /* image texture */ + if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) || (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) { + gp_set_filling_texture(gp_style->ima, gp_style->flag); + } #endif - /* Draw all triangles for filling the polygon (cache must be calculated before) */ - immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3); - /* TODO: use batch instead of immediate mode, to share vertices */ - - const bGPDtriangle *stroke_triangle = gps->triangles; - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - gp_add_filldata_tobuffer( - &gps->points[stroke_triangle->verts[j]], stroke_triangle->uv[j], - pos, texcoord, gps->flag, - offsx, offsy, winx, winy, diff_mat); - } - } - - immEnd(); - immUnbindProgram(); + /* Draw all triangles for filling the polygon (cache must be calculated before) */ + immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3); + /* TODO: use batch instead of immediate mode, to share vertices */ + + const bGPDtriangle *stroke_triangle = gps->triangles; + for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { + for (int j = 0; j < 3; j++) { + gp_add_filldata_tobuffer(&gps->points[stroke_triangle->verts[j]], + stroke_triangle->uv[j], + pos, + texcoord, + gps->flag, + offsx, + offsy, + winx, + winy, + diff_mat); + } + } + + immEnd(); + immUnbindProgram(); } /* ----- Existing Strokes Drawing (3D and Point) ------ */ /* draw a given stroke - just a single dot (only one point) */ -static void gp_draw_stroke_point( - const bGPDspoint *points, short thickness, short UNUSED(dflag), short sflag, - int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) +static void gp_draw_stroke_point(const bGPDspoint *points, + short thickness, + short UNUSED(dflag), + short sflag, + int offsx, + int offsy, + int winx, + int winy, + const float diff_mat[4][4], + const float ink[4]) { - const bGPDspoint *pt = points; + const bGPDspoint *pt = points; - /* get final position using parent matrix */ - float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* get final position using parent matrix */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - if (sflag & GP_STROKE_3DSPACE) { - immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - } - else { - immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + if (sflag & GP_STROKE_3DSPACE) { + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + } + else { + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - /* get 2D coordinates of point */ - float co[3] = { 0.0f }; - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); - copy_v3_v3(fpt, co); - } + /* get 2D coordinates of point */ + float co[3] = {0.0f}; + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); + copy_v3_v3(fpt, co); + } - gp_set_point_uniform_color(pt, ink); - /* set point thickness (since there's only one of these) */ - immUniform1f("size", (float)(thickness + 2) * pt->pressure); + gp_set_point_uniform_color(pt, ink); + /* set point thickness (since there's only one of these) */ + immUniform1f("size", (float)(thickness + 2) * pt->pressure); - immBegin(GPU_PRIM_POINTS, 1); - immVertex3fv(pos, fpt); - immEnd(); + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, fpt); + immEnd(); - immUnbindProgram(); + immUnbindProgram(); } /* draw a given stroke in 3d (i.e. in 3d-space) */ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic) { - bGPDspoint *points = tgpw->gps->points; - int totpoints = tgpw->gps->totpoints; - - const float viewport[2] = { (float)tgpw->winx, (float)tgpw->winy }; - float curpressure = points[0].pressure; - float fpt[3]; - - /* if cyclic needs more vertex */ - int cyclic_add = (cyclic) ? 1 : 0; - - GPUVertFormat *format = immVertexFormat(); - const struct { - uint pos, color, thickness; - } attr_id = { - .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT), - .color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT), - .thickness = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT), - }; - - immBindBuiltinProgram(GPU_SHADER_GPENCIL_STROKE); - immUniform2fv("Viewport", viewport); - immUniform1f("pixsize", tgpw->rv3d->pixsize); - float obj_scale = tgpw->ob ? (tgpw->ob->scale[0] + tgpw->ob->scale[1] + tgpw->ob->scale[2]) / 3.0f : 1.0f; - - immUniform1f("objscale", obj_scale); - int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); - immUniform1i("keep_size", keep_size); - immUniform1f("pixfactor", tgpw->gpd->pixfactor); - /* xray mode always to 3D space to avoid wrong zdepth calculation (T60051) */ - immUniform1i("xraymode", GP_XRAY_3DSPACE); - immUniform1i("caps_start", (int)tgpw->gps->caps[0]); - immUniform1i("caps_end", (int)tgpw->gps->caps[1]); - immUniform1i("fill_stroke", (int)tgpw->is_fill_stroke); - - /* draw stroke curve */ - GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2); - const bGPDspoint *pt = points; - - for (int i = 0; i < totpoints; i++, pt++) { - /* first point for adjacency (not drawn) */ - if (i == 0) { - gp_set_point_varying_color(points, ink, attr_id.color); - - if ((cyclic) && (totpoints > 2)) { - immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x); - } - else { - immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); - } - immVertex3fv(attr_id.pos, fpt); - } - /* set point */ - gp_set_point_varying_color(pt, ink, attr_id.color); - immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x); - immVertex3fv(attr_id.pos, fpt); - } - - if (cyclic && totpoints > 2) { - /* draw line to first point to complete the cycle */ - immAttr1f(attr_id.thickness, max_ff(points->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x); - immVertex3fv(attr_id.pos, fpt); - - /* now add adjacency point (not drawn) */ - immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); - immVertex3fv(attr_id.pos, fpt); - } - /* last adjacency point (not drawn) */ - else { - gp_set_point_varying_color(points + totpoints - 2, ink, attr_id.color); - immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f)); - mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x); - immVertex3fv(attr_id.pos, fpt); - } - - immEnd(); - immUnbindProgram(); + bGPDspoint *points = tgpw->gps->points; + int totpoints = tgpw->gps->totpoints; + + const float viewport[2] = {(float)tgpw->winx, (float)tgpw->winy}; + float curpressure = points[0].pressure; + float fpt[3]; + + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; + + GPUVertFormat *format = immVertexFormat(); + const struct { + uint pos, color, thickness; + } attr_id = { + .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT), + .color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT), + .thickness = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT), + }; + + immBindBuiltinProgram(GPU_SHADER_GPENCIL_STROKE); + immUniform2fv("Viewport", viewport); + immUniform1f("pixsize", tgpw->rv3d->pixsize); + float obj_scale = tgpw->ob ? + (tgpw->ob->scale[0] + tgpw->ob->scale[1] + tgpw->ob->scale[2]) / 3.0f : + 1.0f; + + immUniform1f("objscale", obj_scale); + int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); + immUniform1i("keep_size", keep_size); + immUniform1f("pixfactor", tgpw->gpd->pixfactor); + /* xray mode always to 3D space to avoid wrong zdepth calculation (T60051) */ + immUniform1i("xraymode", GP_XRAY_3DSPACE); + immUniform1i("caps_start", (int)tgpw->gps->caps[0]); + immUniform1i("caps_end", (int)tgpw->gps->caps[1]); + immUniform1i("fill_stroke", (int)tgpw->is_fill_stroke); + + /* draw stroke curve */ + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2); + const bGPDspoint *pt = points; + + for (int i = 0; i < totpoints; i++, pt++) { + /* first point for adjacency (not drawn) */ + if (i == 0) { + gp_set_point_varying_color(points, ink, attr_id.color); + + if ((cyclic) && (totpoints > 2)) { + immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x); + } + else { + immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); + } + immVertex3fv(attr_id.pos, fpt); + } + /* set point */ + gp_set_point_varying_color(pt, ink, attr_id.color); + immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x); + immVertex3fv(attr_id.pos, fpt); + } + + if (cyclic && totpoints > 2) { + /* draw line to first point to complete the cycle */ + immAttr1f(attr_id.thickness, max_ff(points->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x); + immVertex3fv(attr_id.pos, fpt); + + /* now add adjacency point (not drawn) */ + immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); + immVertex3fv(attr_id.pos, fpt); + } + /* last adjacency point (not drawn) */ + else { + gp_set_point_varying_color(points + totpoints - 2, ink, attr_id.color); + immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x); + immVertex3fv(attr_id.pos, fpt); + } + + immEnd(); + immUnbindProgram(); } /* ----- Fancy 2D-Stroke Drawing ------ */ /* draw a given stroke in 2d */ -static void gp_draw_stroke_2d( - const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) +static void gp_draw_stroke_2d(const bGPDspoint *points, + int totpoints, + short thickness_s, + short dflag, + short sflag, + bool UNUSED(debug), + int offsx, + int offsy, + int winx, + int winy, + const float diff_mat[4][4], + const float ink[4]) { - /* otherwise thickness is twice that of the 3D view */ - float thickness = (float)thickness_s * 0.5f; - - /* strokes in Image Editor need a scale factor, since units there are not pixels! */ - float scalefac = 1.0f; - if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { - scalefac = 0.001f; - } - - /* TODO: fancy++ with the magic of shaders */ - - /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection - * edges rotated to minimize shrinking artifacts, and rounded endcaps - */ - { - const bGPDspoint *pt1, *pt2; - float s0[2], s1[2]; /* segment 'center' points */ - float pm[2]; /* normal from previous segment. */ - int i; - float fpt[3]; - - GPUVertFormat *format = immVertexFormat(); - const struct { - uint pos, color; - } attr_id = { - .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT), - .color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT), - }; - - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); - - /* get x and y coordinates from first point */ - mul_v3_m4v3(fpt, diff_mat, &points->x); - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); - - for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { - float t0[2], t1[2]; /* tessellated coordinates */ - float m1[2], m2[2]; /* gradient and normal */ - float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ - float pthick; /* thickness at segment point */ - - /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); - - /* calculate gradient and normal - 'angle'=(ny/nx) */ - m1[1] = s1[1] - s0[1]; - m1[0] = s1[0] - s0[0]; - normalize_v2(m1); - m2[1] = -m1[0]; - m2[0] = m1[1]; - - /* always use pressure from first point here */ - pthick = (pt1->pressure * thickness * scalefac); - - /* color of point */ - gp_set_point_varying_color(pt1, ink, attr_id.color); - - /* if the first segment, start of segment is segment's normal */ - if (i == 0) { - /* draw start cap first - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s0[0] - (m1[0] * pthick * 0.75f); - sc[1] = s0[1] - (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* First two points of cap. */ - immVertex2fv(attr_id.pos, t0); - immVertex2fv(attr_id.pos, t1); - - /* calculate points for start of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; - - /* Last two points of start cap (and first two points of first segment). */ - immVertex2fv(attr_id.pos, t0); - immVertex2fv(attr_id.pos, t1); - } - /* if not the first segment, use bisector of angle between segments */ - else { - float mb[2]; /* bisector normal */ - float athick, dfac; /* actual thickness, difference between thicknesses */ - - /* calculate gradient of bisector (as average of normals) */ - mb[0] = (pm[0] + m2[0]) / 2; - mb[1] = (pm[1] + m2[1]) / 2; - normalize_v2(mb); - - /* calculate gradient to apply - * - as basis, use just pthick * bisector gradient - * - if cross-section not as thick as it should be, add extra padding to fix it - */ - mt[0] = mb[0] * pthick; - mt[1] = mb[1] * pthick; - athick = len_v2(mt); - dfac = pthick - (athick * 2); - - if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { - mt[0] += (mb[0] * dfac); - mt[1] += (mb[1] * dfac); - } - - /* calculate points for start of segment */ - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; - - /* Last two points of previous segment, and first two points of current segment. */ - immVertex2fv(attr_id.pos, t0); - immVertex2fv(attr_id.pos, t1); - } - - /* if last segment, also draw end of segment (defined as segment's normal) */ - if (i == totpoints - 2) { - /* for once, we use second point's pressure (otherwise it won't be drawn) */ - pthick = (pt2->pressure * thickness * scalefac); - - /* color of point */ - gp_set_point_varying_color(pt2, ink, attr_id.color); - - /* calculate points for end of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s1[0] - mt[0]; - t0[1] = s1[1] - mt[1]; - t1[0] = s1[0] + mt[0]; - t1[1] = s1[1] + mt[1]; - - /* Last two points of last segment (and first two points of end cap). */ - immVertex2fv(attr_id.pos, t0); - immVertex2fv(attr_id.pos, t1); - - /* draw end cap as last step - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s1[0] + (m1[0] * pthick * 0.75f); - sc[1] = s1[1] + (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* Last two points of end cap. */ - immVertex2fv(attr_id.pos, t0); - immVertex2fv(attr_id.pos, t1); - } - - /* store computed point2 coordinates as point1 ones of next segment. */ - copy_v2_v2(s0, s1); - /* store stroke's 'natural' normal for next stroke to use */ - copy_v2_v2(pm, m2); - } - - immEnd(); - immUnbindProgram(); - } + /* otherwise thickness is twice that of the 3D view */ + float thickness = (float)thickness_s * 0.5f; + + /* strokes in Image Editor need a scale factor, since units there are not pixels! */ + float scalefac = 1.0f; + if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { + scalefac = 0.001f; + } + + /* TODO: fancy++ with the magic of shaders */ + + /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection + * edges rotated to minimize shrinking artifacts, and rounded endcaps + */ + { + const bGPDspoint *pt1, *pt2; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ + int i; + float fpt[3]; + + GPUVertFormat *format = immVertexFormat(); + const struct { + uint pos, color; + } attr_id = { + .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT), + .color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT), + }; + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); + + /* get x and y coordinates from first point */ + mul_v3_m4v3(fpt, diff_mat, &points->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { + float t0[2], t1[2]; /* tessellated coordinates */ + float m1[2], m2[2]; /* gradient and normal */ + float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ + float pthick; /* thickness at segment point */ + + /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); + + /* calculate gradient and normal - 'angle'=(ny/nx) */ + m1[1] = s1[1] - s0[1]; + m1[0] = s1[0] - s0[0]; + normalize_v2(m1); + m2[1] = -m1[0]; + m2[0] = m1[1]; + + /* always use pressure from first point here */ + pthick = (pt1->pressure * thickness * scalefac); + + /* color of point */ + gp_set_point_varying_color(pt1, ink, attr_id.color); + + /* if the first segment, start of segment is segment's normal */ + if (i == 0) { + /* draw start cap first + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s0[0] - (m1[0] * pthick * 0.75f); + sc[1] = s0[1] - (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* First two points of cap. */ + immVertex2fv(attr_id.pos, t0); + immVertex2fv(attr_id.pos, t1); + + /* calculate points for start of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of start cap (and first two points of first segment). */ + immVertex2fv(attr_id.pos, t0); + immVertex2fv(attr_id.pos, t1); + } + /* if not the first segment, use bisector of angle between segments */ + else { + float mb[2]; /* bisector normal */ + float athick, dfac; /* actual thickness, difference between thicknesses */ + + /* calculate gradient of bisector (as average of normals) */ + mb[0] = (pm[0] + m2[0]) / 2; + mb[1] = (pm[1] + m2[1]) / 2; + normalize_v2(mb); + + /* calculate gradient to apply + * - as basis, use just pthick * bisector gradient + * - if cross-section not as thick as it should be, add extra padding to fix it + */ + mt[0] = mb[0] * pthick; + mt[1] = mb[1] * pthick; + athick = len_v2(mt); + dfac = pthick - (athick * 2); + + if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { + mt[0] += (mb[0] * dfac); + mt[1] += (mb[1] * dfac); + } + + /* calculate points for start of segment */ + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of previous segment, and first two points of current segment. */ + immVertex2fv(attr_id.pos, t0); + immVertex2fv(attr_id.pos, t1); + } + + /* if last segment, also draw end of segment (defined as segment's normal) */ + if (i == totpoints - 2) { + /* for once, we use second point's pressure (otherwise it won't be drawn) */ + pthick = (pt2->pressure * thickness * scalefac); + + /* color of point */ + gp_set_point_varying_color(pt2, ink, attr_id.color); + + /* calculate points for end of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s1[0] - mt[0]; + t0[1] = s1[1] - mt[1]; + t1[0] = s1[0] + mt[0]; + t1[1] = s1[1] + mt[1]; + + /* Last two points of last segment (and first two points of end cap). */ + immVertex2fv(attr_id.pos, t0); + immVertex2fv(attr_id.pos, t1); + + /* draw end cap as last step + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s1[0] + (m1[0] * pthick * 0.75f); + sc[1] = s1[1] + (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* Last two points of end cap. */ + immVertex2fv(attr_id.pos, t0); + immVertex2fv(attr_id.pos, t1); + } + + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); + /* store stroke's 'natural' normal for next stroke to use */ + copy_v2_v2(pm, m2); + } + + immEnd(); + immUnbindProgram(); + } } /* ----- Strokes Drawing ------ */ @@ -764,335 +814,371 @@ static void gp_draw_stroke_2d( /* Helper for doing all the checks on whether a stroke can be drawn */ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) { - /* skip stroke if it isn't in the right display space for this drawing context */ - /* 1) 3D Strokes */ - if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) - return false; - - /* 2) Screen Space 2D Strokes */ - if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) - return false; - - /* 3) Image Space (2D) */ - if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) - return false; - if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) - return false; - - /* skip stroke if it doesn't have any valid data */ - if ((gps->points == NULL) || (gps->totpoints < 1)) - return false; - - /* stroke can be drawn */ - return true; + /* skip stroke if it isn't in the right display space for this drawing context */ + /* 1) 3D Strokes */ + if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) + return false; + + /* 2) Screen Space 2D Strokes */ + if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) + return false; + + /* 3) Image Space (2D) */ + if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) + return false; + + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1)) + return false; + + /* stroke can be drawn */ + return true; } /* draw a set of strokes */ static void gp_draw_strokes(tGPDdraw *tgpw) { - float tcolor[4]; - float tfill[4]; - short sthickness; - float ink[4]; - - GPU_enable_program_point_size(); - - for (bGPDstroke *gps = tgpw->t_gpf->strokes.first; gps; gps = gps->next) { - /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, tgpw->dflag) == false) { - continue; - } - /* check if the color is visible */ - Material *ma = tgpw->gpd->mat[gps->mat_nr]; - MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; - - if ((gp_style == NULL) || - (gp_style->flag & GP_STYLE_COLOR_HIDE) || - /* if onion and ghost flag do not draw*/ - (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) - { - continue; - } - - /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */ - if ((tgpw->disable_fill == 1) && - (gp_style->fill_rgba[3] > 0.0f) && - ((gps->flag & GP_STROKE_NOFILL) == 0)) - { - continue; - } - - /* calculate thickness */ - sthickness = gps->thickness + tgpw->lthick; - - if (tgpw->is_fill_stroke) { - sthickness = (short)max_ii(1, sthickness / 2); - } - - if (sthickness <= 0) { - continue; - } - - /* check which stroke-drawer to use */ - if (tgpw->dflag & GP_DRAWDATA_ONLY3D) { - const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); - int mask_orig = 0; - - if (no_xray) { - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); - glDepthMask(0); - GPU_depth_test(true); - - /* first arg is normally rv3d->dist, but this isn't - * available here and seems to work quite well without */ - bglPolygonOffset(1.0f, 1.0f); - } - - /* 3D Fill */ - //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - if ((gps->totpoints >= 3) && (tgpw->disable_fill != 1)) { - /* set color using material, tint color and opacity */ - interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); - tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; - if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { - const float *color; - if (!tgpw->onion) { - color = tfill; - } - else { - if (tgpw->custonion) { - color = tgpw->tintcolor; - } - else { - ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); - color = tfill; - } - } - gp_draw_stroke_fill( - tgpw->gpd, gps, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); - } - } - - /* 3D Stroke */ - /* set color using material tint color and opacity */ - if (!tgpw->onion) { - interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); - tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; - copy_v4_v4(ink, tcolor); - } - else { - if (tgpw->custonion) { - copy_v4_v4(ink, tgpw->tintcolor); - } - else { - ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); - copy_v4_v4(ink, tcolor); - } - } - if (gp_style->mode == GP_STYLE_MODE_DOTS) { - /* volumetric stroke drawing */ - if (tgpw->disable_fill != 1) { - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); - } - } - else { - /* 3D Lines - OpenGL primitives-based */ - if (gps->totpoints == 1) { - if (tgpw->disable_fill != 1) { - gp_draw_stroke_point( - gps->points, sthickness, tgpw->dflag, gps->flag, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, - tgpw->diff_mat, ink); - } - } - else { - tgpw->gps = gps; - gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC); - } - } - if (no_xray) { - glDepthMask(mask_orig); - GPU_depth_test(false); - - bglPolygonOffset(0.0, 0.0); - } - } - else { - /* 2D - Fill */ - if (gps->totpoints >= 3) { - /* set color using material, tint color and opacity */ - interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); - tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; - if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { - const float *color; - if (!tgpw->onion) { - color = tfill; - } - else { - if (tgpw->custonion) { - color = tgpw->tintcolor; - } - else { - ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); - color = tfill; - } - } - gp_draw_stroke_fill( - tgpw->gpd, gps, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); - } - } - - /* 2D Strokes... */ - /* set color using material, tint color and opacity */ - if (!tgpw->onion) { - interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); - tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; - copy_v4_v4(ink, tcolor); - } - else { - if (tgpw->custonion) { - copy_v4_v4(ink, tgpw->tintcolor); - } - else { - ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); - copy_v4_v4(ink, tcolor); - } - } - if (gp_style->mode == GP_STYLE_MODE_DOTS) { - /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d( - gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); - } - else { - /* normal 2D strokes */ - if (gps->totpoints == 1) { - gp_draw_stroke_point( - gps->points, sthickness, tgpw->dflag, gps->flag, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, - tgpw->diff_mat, ink); - } - else { - gp_draw_stroke_2d( - gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, false, - tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); - } - } - } - } - - GPU_disable_program_point_size(); + float tcolor[4]; + float tfill[4]; + short sthickness; + float ink[4]; + + GPU_enable_program_point_size(); + + for (bGPDstroke *gps = tgpw->t_gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, tgpw->dflag) == false) { + continue; + } + /* check if the color is visible */ + Material *ma = tgpw->gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; + + if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE) || + /* if onion and ghost flag do not draw*/ + (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) { + continue; + } + + /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */ + if ((tgpw->disable_fill == 1) && (gp_style->fill_rgba[3] > 0.0f) && + ((gps->flag & GP_STROKE_NOFILL) == 0)) { + continue; + } + + /* calculate thickness */ + sthickness = gps->thickness + tgpw->lthick; + + if (tgpw->is_fill_stroke) { + sthickness = (short)max_ii(1, sthickness / 2); + } + + if (sthickness <= 0) { + continue; + } + + /* check which stroke-drawer to use */ + if (tgpw->dflag & GP_DRAWDATA_ONLY3D) { + const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); + int mask_orig = 0; + + if (no_xray) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + + /* 3D Fill */ + //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { + if ((gps->totpoints >= 3) && (tgpw->disable_fill != 1)) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { + const float *color; + if (!tgpw->onion) { + color = tfill; + } + else { + if (tgpw->custonion) { + color = tgpw->tintcolor; + } + else { + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); + color = tfill; + } + } + gp_draw_stroke_fill(tgpw->gpd, + gps, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + color); + } + } + + /* 3D Stroke */ + /* set color using material tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); + copy_v4_v4(ink, tcolor); + } + } + if (gp_style->mode == GP_STYLE_MODE_DOTS) { + /* volumetric stroke drawing */ + if (tgpw->disable_fill != 1) { + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); + } + } + else { + /* 3D Lines - OpenGL primitives-based */ + if (gps->totpoints == 1) { + if (tgpw->disable_fill != 1) { + gp_draw_stroke_point(gps->points, + sthickness, + tgpw->dflag, + gps->flag, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + ink); + } + } + else { + tgpw->gps = gps; + gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC); + } + } + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); + } + } + else { + /* 2D - Fill */ + if (gps->totpoints >= 3) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { + const float *color; + if (!tgpw->onion) { + color = tfill; + } + else { + if (tgpw->custonion) { + color = tgpw->tintcolor; + } + else { + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); + color = tfill; + } + } + gp_draw_stroke_fill(tgpw->gpd, + gps, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + color); + } + } + + /* 2D Strokes... */ + /* set color using material, tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); + copy_v4_v4(ink, tcolor); + } + } + if (gp_style->mode == GP_STYLE_MODE_DOTS) { + /* blob/disk-based "volumetric" drawing */ + gp_draw_stroke_volumetric_2d(gps->points, + gps->totpoints, + sthickness, + tgpw->dflag, + gps->flag, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + ink); + } + else { + /* normal 2D strokes */ + if (gps->totpoints == 1) { + gp_draw_stroke_point(gps->points, + sthickness, + tgpw->dflag, + gps->flag, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + ink); + } + else { + gp_draw_stroke_2d(gps->points, + gps->totpoints, + sthickness, + tgpw->dflag, + gps->flag, + false, + tgpw->offsx, + tgpw->offsy, + tgpw->winx, + tgpw->winy, + tgpw->diff_mat, + ink); + } + } + } + } + + GPU_disable_program_point_size(); } /* ----- General Drawing ------ */ - /* draw interpolate strokes (used only while operator is running) */ void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type) { - tGPDdraw tgpw; - ARegion *ar = CTX_wm_region(C); - RegionView3D *rv3d = ar->regiondata; - tGPDinterpolate_layer *tgpil; - Object *obact = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - float color[4]; - - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); - color[3] = 0.6f; - int dflag = 0; - /* if 3d stuff, enable flags */ - if (type == REGION_DRAW_POST_VIEW) { - dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); - } - - tgpw.rv3d = rv3d; - tgpw.depsgraph = depsgraph; - tgpw.ob = obact; - tgpw.gpd = tgpi->gpd; - tgpw.offsx = 0; - tgpw.offsy = 0; - tgpw.winx = tgpi->ar->winx; - tgpw.winy = tgpi->ar->winy; - tgpw.dflag = dflag; - - /* turn on alpha-blending */ - GPU_blend(true); - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - /* calculate parent position */ - ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat); - if (tgpil->interFrame) { - tgpw.gpl = tgpil->gpl; - tgpw.gpf = tgpil->interFrame; - tgpw.t_gpf = tgpil->interFrame; - - tgpw.lthick = tgpil->gpl->line_change; - tgpw.opacity = 1.0; - copy_v4_v4(tgpw.tintcolor, color); - tgpw.onion = true; - tgpw.custonion = true; - - gp_draw_strokes(&tgpw); - } - } - GPU_blend(false); + tGPDdraw tgpw; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + tGPDinterpolate_layer *tgpil; + Object *obact = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + float color[4]; + + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); + color[3] = 0.6f; + int dflag = 0; + /* if 3d stuff, enable flags */ + if (type == REGION_DRAW_POST_VIEW) { + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + tgpw.rv3d = rv3d; + tgpw.depsgraph = depsgraph; + tgpw.ob = obact; + tgpw.gpd = tgpi->gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpi->ar->winx; + tgpw.winy = tgpi->ar->winy; + tgpw.dflag = dflag; + + /* turn on alpha-blending */ + GPU_blend(true); + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + /* calculate parent position */ + ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat); + if (tgpil->interFrame) { + tgpw.gpl = tgpil->gpl; + tgpw.gpf = tgpil->interFrame; + tgpw.t_gpf = tgpil->interFrame; + + tgpw.lthick = tgpil->gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, color); + tgpw.onion = true; + tgpw.custonion = true; + + gp_draw_strokes(&tgpw); + } + } + GPU_blend(false); } /* wrapper to draw strokes for filling operator */ void ED_gp_draw_fill(tGPDdraw *tgpw) { - gp_draw_strokes(tgpw); + gp_draw_strokes(tgpw); } /* draw a short status message in the top-right corner */ static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar) { - rcti rect; + rcti rect; - /* Cannot draw any status text when drawing OpenGL Renders */ - if (G.f & G_FLAG_RENDER_VIEWPORT) - return; + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_FLAG_RENDER_VIEWPORT) + return; - /* Get bounds of region - Necessary to avoid problems with region overlap */ - ED_region_visible_rect(ar, &rect); + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); - /* for now, this should only be used to indicate when we are in stroke editmode */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - const char *printable = IFACE_("GPencil Stroke Editing"); - float printable_size[2]; + /* for now, this should only be used to indicate when we are in stroke editmode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + const char *printable = IFACE_("GPencil Stroke Editing"); + float printable_size[2]; - int font_id = BLF_default(); + int font_id = BLF_default(); - BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + BLF_width_and_height( + font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect.ymax - U.widget_unit); + int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + int yco = (rect.ymax - U.widget_unit); - /* text label */ - UI_FontThemeColor(font_id, TH_TEXT_HI); + /* text label */ + UI_FontThemeColor(font_id, TH_TEXT_HI); #ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #else - BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #endif - /* grease pencil icon... */ - // XXX: is this too intrusive? - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + /* grease pencil icon... */ + // XXX: is this too intrusive? + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); - xco -= U.widget_unit; - yco -= (int)printable_size[1] / 2; + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; - UI_icon_draw(xco, yco, ICON_GREASEPENCIL); + UI_icon_draw(xco, yco, ICON_GREASEPENCIL); - GPU_blend(false); - } + GPU_blend(false); + } } diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 8ada5a05618..4724dc068b6 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -59,21 +58,21 @@ /* Loops over the gp-frames for a gp-layer, and applies the given callback */ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *)) { - bGPDframe *gpf; + bGPDframe *gpf; - /* error checker */ - if (gpl == NULL) - return false; + /* error checker */ + if (gpl == NULL) + return false; - /* do loop */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* execute callback */ - if (gpf_cb(gpf, scene)) - return true; - } + /* do loop */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* execute callback */ + if (gpf_cb(gpf, scene)) + return true; + } - /* nothing to return */ - return false; + /* nothing to return */ + return false; } /* ****************************************** */ @@ -82,24 +81,24 @@ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPD /* make a listing all the gp-frames in a layer as cfraelems */ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) { - bGPDframe *gpf; - CfraElem *ce; + bGPDframe *gpf; + CfraElem *ce; - /* error checking */ - if (ELEM(NULL, gpl, elems)) - return; + /* error checking */ + if (ELEM(NULL, gpl, elems)) + return; - /* loop through gp-frames, adding */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) { - ce = MEM_callocN(sizeof(CfraElem), "CfraElem"); + /* loop through gp-frames, adding */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) { + ce = MEM_callocN(sizeof(CfraElem), "CfraElem"); - ce->cfra = (float)gpf->framenum; - ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0; + ce->cfra = (float)gpf->framenum; + ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0; - BLI_addtail(elems, ce); - } - } + BLI_addtail(elems, ce); + } + } } /* ***************************************** */ @@ -108,125 +107,128 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) /* check if one of the frames in this layer is selected */ bool ED_gplayer_frame_select_check(bGPDlayer *gpl) { - bGPDframe *gpf; + bGPDframe *gpf; - /* error checking */ - if (gpl == NULL) - return false; + /* error checking */ + if (gpl == NULL) + return false; - /* stop at the first one found */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) - return true; - } + /* stop at the first one found */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) + return true; + } - /* not found */ - return false; + /* not found */ + return false; } /* helper function - select gp-frame based on SELECT_* mode */ static void gpframe_select(bGPDframe *gpf, short select_mode) { - if (gpf == NULL) - return; - - switch (select_mode) { - case SELECT_ADD: - gpf->flag |= GP_FRAME_SELECT; - break; - case SELECT_SUBTRACT: - gpf->flag &= ~GP_FRAME_SELECT; - break; - case SELECT_INVERT: - gpf->flag ^= GP_FRAME_SELECT; - break; - } + if (gpf == NULL) + return; + + switch (select_mode) { + case SELECT_ADD: + gpf->flag |= GP_FRAME_SELECT; + break; + case SELECT_SUBTRACT: + gpf->flag &= ~GP_FRAME_SELECT; + break; + case SELECT_INVERT: + gpf->flag ^= GP_FRAME_SELECT; + break; + } } /* set all/none/invert select (like above, but with SELECT_* modes) */ void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode) { - bGPDframe *gpf; + bGPDframe *gpf; - /* error checking */ - if (gpl == NULL) - return; + /* error checking */ + if (gpl == NULL) + return; - /* handle according to mode */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - gpframe_select(gpf, select_mode); - } + /* handle according to mode */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + gpframe_select(gpf, select_mode); + } } /* set all/none/invert select */ void ED_gplayer_frame_select_set(bGPDlayer *gpl, short mode) { - /* error checking */ - if (gpl == NULL) - return; + /* error checking */ + if (gpl == NULL) + return; - /* now call the standard function */ - ED_gpencil_select_frames(gpl, mode); + /* now call the standard function */ + ED_gpencil_select_frames(gpl, mode); } /* select the frame in this layer that occurs on this frame (there should only be one at most) */ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode) { - bGPDframe *gpf; + bGPDframe *gpf; - if (gpl == NULL) - return; + if (gpl == NULL) + return; - gpf = BKE_gpencil_layer_find_frame(gpl, selx); + gpf = BKE_gpencil_layer_find_frame(gpl, selx); - if (gpf) { - gpframe_select(gpf, select_mode); - } + if (gpf) { + gpframe_select(gpf, select_mode); + } } /* select the frames in this layer that occur within the bounds specified */ void ED_gplayer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode) { - bGPDframe *gpf; + bGPDframe *gpf; - if (gpl == NULL) - return; + if (gpl == NULL) + return; - /* only select those frames which are in bounds */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (IN_RANGE(gpf->framenum, min, max)) - gpframe_select(gpf, select_mode); - } + /* only select those frames which are in bounds */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (IN_RANGE(gpf->framenum, min, max)) + gpframe_select(gpf, select_mode); + } } /* select the frames in this layer that occur within the lasso/circle region specified */ -void ED_gplayer_frames_select_region(KeyframeEditData *ked, bGPDlayer *gpl, short tool, short select_mode) +void ED_gplayer_frames_select_region(KeyframeEditData *ked, + bGPDlayer *gpl, + short tool, + short select_mode) { - bGPDframe *gpf; - - if (gpl == NULL) - return; - - /* only select frames which are within the region */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* construct a dummy point coordinate to do this testing with */ - float pt[2] = {0}; - - pt[0] = gpf->framenum; - pt[1] = ked->channel_y; - - /* check the necessary regions */ - if (tool == BEZT_OK_CHANNEL_LASSO) { - /* Lasso */ - if (keyframe_region_lasso_test(ked->data, pt)) - gpframe_select(gpf, select_mode); - } - else if (tool == BEZT_OK_CHANNEL_CIRCLE) { - /* Circle */ - if (keyframe_region_circle_test(ked->data, pt)) - gpframe_select(gpf, select_mode); - } - } + bGPDframe *gpf; + + if (gpl == NULL) + return; + + /* only select frames which are within the region */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* construct a dummy point coordinate to do this testing with */ + float pt[2] = {0}; + + pt[0] = gpf->framenum; + pt[1] = ked->channel_y; + + /* check the necessary regions */ + if (tool == BEZT_OK_CHANNEL_LASSO) { + /* Lasso */ + if (keyframe_region_lasso_test(ked->data, pt)) + gpframe_select(gpf, select_mode); + } + else if (tool == BEZT_OK_CHANNEL_CIRCLE) { + /* Circle */ + if (keyframe_region_circle_test(ked->data, pt)) + gpframe_select(gpf, select_mode); + } + } } /* ***************************************** */ @@ -235,50 +237,50 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked, bGPDlayer *gpl, shor /* Delete selected frames */ bool ED_gplayer_frames_delete(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; - bool changed = false; + bGPDframe *gpf, *gpfn; + bool changed = false; - /* error checking */ - if (gpl == NULL) - return false; + /* error checking */ + if (gpl == NULL) + return false; - /* check for frames to delete */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; + /* check for frames to delete */ + for (gpf = gpl->frames.first; gpf; gpf = gpfn) { + gpfn = gpf->next; - if (gpf->flag & GP_FRAME_SELECT) { - BKE_gpencil_layer_delframe(gpl, gpf); - changed = true; - } - } + if (gpf->flag & GP_FRAME_SELECT) { + BKE_gpencil_layer_delframe(gpl, gpf); + changed = true; + } + } - return changed; + return changed; } /* Duplicate selected frames from given gp-layer */ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; + bGPDframe *gpf, *gpfn; - /* error checking */ - if (gpl == NULL) - return; + /* error checking */ + if (gpl == NULL) + return; - /* duplicate selected frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; + /* duplicate selected frames */ + for (gpf = gpl->frames.first; gpf; gpf = gpfn) { + gpfn = gpf->next; - /* duplicate this frame */ - if (gpf->flag & GP_FRAME_SELECT) { - bGPDframe *gpfd; + /* duplicate this frame */ + if (gpf->flag & GP_FRAME_SELECT) { + bGPDframe *gpfd; - /* duplicate frame, and deselect self */ - gpfd = BKE_gpencil_frame_duplicate(gpf); - gpf->flag &= ~GP_FRAME_SELECT; + /* duplicate frame, and deselect self */ + gpfd = BKE_gpencil_frame_duplicate(gpf); + gpf->flag &= ~GP_FRAME_SELECT; - BLI_insertlinkafter(&gpl->frames, gpf, gpfd); - } - } + BLI_insertlinkafter(&gpl->frames, gpf, gpfd); + } + } } /* Set keyframe type for selected frames from given gp-layer @@ -286,19 +288,18 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) */ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type) { - bGPDframe *gpf; + bGPDframe *gpf; - if (gpl == NULL) - return; + if (gpl == NULL) + return; - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) { - gpf->key_type = type; - } - } + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + gpf->key_type = type; + } + } } - /* -------------------------------------- */ /* Copy and Paste Tools */ /* - The copy/paste buffer currently stores a set of GP_Layers, with temporary @@ -311,23 +312,21 @@ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type) /* globals for copy/paste data (like for other copy/paste buffers) */ static ListBase gp_anim_copybuf = {NULL, NULL}; -static int gp_anim_copy_firstframe = 999999999; -static int gp_anim_copy_lastframe = -999999999; -static int gp_anim_copy_cfra = 0; - +static int gp_anim_copy_firstframe = 999999999; +static int gp_anim_copy_lastframe = -999999999; +static int gp_anim_copy_cfra = 0; /* This function frees any MEM_calloc'ed copy/paste buffer data */ void ED_gpencil_anim_copybuf_free(void) { - BKE_gpencil_free_layers(&gp_anim_copybuf); - BLI_listbase_clear(&gp_anim_copybuf); + BKE_gpencil_free_layers(&gp_anim_copybuf); + BLI_listbase_clear(&gp_anim_copybuf); - gp_anim_copy_firstframe = 999999999; - gp_anim_copy_lastframe = -999999999; - gp_anim_copy_cfra = 0; + gp_anim_copy_firstframe = 999999999; + gp_anim_copy_lastframe = -999999999; + gp_anim_copy_cfra = 0; } - /* This function adds data to the copy/paste buffer, freeing existing data first * Only the selected GP-layers get their selected keyframes copied. * @@ -335,180 +334,177 @@ void ED_gpencil_anim_copybuf_free(void) */ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - Scene *scene = ac->scene; - - - /* clear buffer first */ - ED_gpencil_anim_copybuf_free(); - - /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* assume that each of these is a GP layer */ - for (ale = anim_data.first; ale; ale = ale->next) { - ListBase copied_frames = {NULL, NULL}; - bGPDlayer *gpl = (bGPDlayer *)ale->data; - bGPDframe *gpf; - - /* loop over frames, and copy only selected frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* if frame is selected, make duplicate it and its strokes */ - if (gpf->flag & GP_FRAME_SELECT) { - /* make a copy of this frame */ - bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf); - BLI_addtail(&copied_frames, new_frame); - - /* extend extents for keyframes encountered */ - if (gpf->framenum < gp_anim_copy_firstframe) - gp_anim_copy_firstframe = gpf->framenum; - if (gpf->framenum > gp_anim_copy_lastframe) - gp_anim_copy_lastframe = gpf->framenum; - } - } - - /* create a new layer in buffer if there were keyframes here */ - if (BLI_listbase_is_empty(&copied_frames) == false) { - bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer"); - BLI_addtail(&gp_anim_copybuf, new_layer); - - /* move over copied frames */ - BLI_movelisttolist(&new_layer->frames, &copied_frames); - BLI_assert(copied_frames.first == NULL); - - /* make a copy of the layer's name - for name-based matching later... */ - BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info)); - } - } - - /* in case 'relative' paste method is used */ - gp_anim_copy_cfra = CFRA; - - /* clean up */ - ANIM_animdata_freelist(&anim_data); - - /* check if anything ended up in the buffer */ - if (ELEM(NULL, gp_anim_copybuf.first, gp_anim_copybuf.last)) { - BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); - return false; - } - - /* report success */ - return true; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene = ac->scene; + + /* clear buffer first */ + ED_gpencil_anim_copybuf_free(); + + /* filter data */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* assume that each of these is a GP layer */ + for (ale = anim_data.first; ale; ale = ale->next) { + ListBase copied_frames = {NULL, NULL}; + bGPDlayer *gpl = (bGPDlayer *)ale->data; + bGPDframe *gpf; + + /* loop over frames, and copy only selected frames */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* if frame is selected, make duplicate it and its strokes */ + if (gpf->flag & GP_FRAME_SELECT) { + /* make a copy of this frame */ + bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf); + BLI_addtail(&copied_frames, new_frame); + + /* extend extents for keyframes encountered */ + if (gpf->framenum < gp_anim_copy_firstframe) + gp_anim_copy_firstframe = gpf->framenum; + if (gpf->framenum > gp_anim_copy_lastframe) + gp_anim_copy_lastframe = gpf->framenum; + } + } + + /* create a new layer in buffer if there were keyframes here */ + if (BLI_listbase_is_empty(&copied_frames) == false) { + bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer"); + BLI_addtail(&gp_anim_copybuf, new_layer); + + /* move over copied frames */ + BLI_movelisttolist(&new_layer->frames, &copied_frames); + BLI_assert(copied_frames.first == NULL); + + /* make a copy of the layer's name - for name-based matching later... */ + BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info)); + } + } + + /* in case 'relative' paste method is used */ + gp_anim_copy_cfra = CFRA; + + /* clean up */ + ANIM_animdata_freelist(&anim_data); + + /* check if anything ended up in the buffer */ + if (ELEM(NULL, gp_anim_copybuf.first, gp_anim_copybuf.last)) { + BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); + return false; + } + + /* report success */ + return true; } - /* Pastes keyframes from buffer, and reports success */ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - Scene *scene = ac->scene; - bool no_name = false; - int offset = 0; - - /* check if buffer is empty */ - if (BLI_listbase_is_empty(&gp_anim_copybuf)) { - BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste"); - return false; - } - - /* check if single channel in buffer (disregard names if so) */ - if (gp_anim_copybuf.first == gp_anim_copybuf.last) { - no_name = true; - } - - /* methods of offset (eKeyPasteOffset) */ - switch (offset_mode) { - case KEYFRAME_PASTE_OFFSET_CFRA_START: - offset = (CFRA - gp_anim_copy_firstframe); - break; - case KEYFRAME_PASTE_OFFSET_CFRA_END: - offset = (CFRA - gp_anim_copy_lastframe); - break; - case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: - offset = (CFRA - gp_anim_copy_cfra); - break; - case KEYFRAME_PASTE_OFFSET_NONE: - offset = 0; - break; - } - - - /* filter data */ - // TODO: try doing it with selection, then without selection imits - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* from selected channels */ - for (ale = anim_data.first; ale; ale = ale->next) { - bGPDlayer *gpld = (bGPDlayer *)ale->data; - bGPDlayer *gpls = NULL; - bGPDframe *gpfs, *gpf; - - - /* find suitable layer from buffer to use to paste from */ - for (gpls = gp_anim_copybuf.first; gpls; gpls = gpls->next) { - /* check if layer name matches */ - if ((no_name) || STREQ(gpls->info, gpld->info)) { - break; - } - } - - /* this situation might occur! */ - if (gpls == NULL) - continue; - - /* add frames from buffer */ - for (gpfs = gpls->frames.first; gpfs; gpfs = gpfs->next) { - /* temporarily apply offset to buffer-frame while copying */ - gpfs->framenum += offset; - - /* get frame to copy data into (if no frame returned, then just ignore) */ - gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW); - if (gpf) { - bGPDstroke *gps, *gpsn; - - /* This should be the right frame... as it may be a pre-existing frame, - * must make sure that only compatible stroke types get copied over - * - We cannot just add a duplicate frame, as that would cause errors - * - For now, we don't check if the types will be compatible since we - * don't have enough info to do so. Instead, we simply just paste, - * if it works, it will show up. - */ - for (gps = gpfs->strokes.first; gps; gps = gps->next) { - /* make a copy of stroke, then of its points array */ - gpsn = MEM_dupallocN(gps); - gpsn->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - gpsn->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, gpsn); - } - /* duplicate triangle information */ - gpsn->triangles = MEM_dupallocN(gps->triangles); - /* append stroke to frame */ - BLI_addtail(&gpf->strokes, gpsn); - } - - /* if no strokes (i.e. new frame) added, free gpf */ - if (BLI_listbase_is_empty(&gpf->strokes)) - BKE_gpencil_layer_delframe(gpld, gpf); - } - - /* unapply offset from buffer-frame */ - gpfs->framenum -= offset; - } - } - - /* clean up */ - ANIM_animdata_freelist(&anim_data); - return true; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene = ac->scene; + bool no_name = false; + int offset = 0; + + /* check if buffer is empty */ + if (BLI_listbase_is_empty(&gp_anim_copybuf)) { + BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste"); + return false; + } + + /* check if single channel in buffer (disregard names if so) */ + if (gp_anim_copybuf.first == gp_anim_copybuf.last) { + no_name = true; + } + + /* methods of offset (eKeyPasteOffset) */ + switch (offset_mode) { + case KEYFRAME_PASTE_OFFSET_CFRA_START: + offset = (CFRA - gp_anim_copy_firstframe); + break; + case KEYFRAME_PASTE_OFFSET_CFRA_END: + offset = (CFRA - gp_anim_copy_lastframe); + break; + case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: + offset = (CFRA - gp_anim_copy_cfra); + break; + case KEYFRAME_PASTE_OFFSET_NONE: + offset = 0; + break; + } + + /* filter data */ + // TODO: try doing it with selection, then without selection imits + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* from selected channels */ + for (ale = anim_data.first; ale; ale = ale->next) { + bGPDlayer *gpld = (bGPDlayer *)ale->data; + bGPDlayer *gpls = NULL; + bGPDframe *gpfs, *gpf; + + /* find suitable layer from buffer to use to paste from */ + for (gpls = gp_anim_copybuf.first; gpls; gpls = gpls->next) { + /* check if layer name matches */ + if ((no_name) || STREQ(gpls->info, gpld->info)) { + break; + } + } + + /* this situation might occur! */ + if (gpls == NULL) + continue; + + /* add frames from buffer */ + for (gpfs = gpls->frames.first; gpfs; gpfs = gpfs->next) { + /* temporarily apply offset to buffer-frame while copying */ + gpfs->framenum += offset; + + /* get frame to copy data into (if no frame returned, then just ignore) */ + gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW); + if (gpf) { + bGPDstroke *gps, *gpsn; + + /* This should be the right frame... as it may be a pre-existing frame, + * must make sure that only compatible stroke types get copied over + * - We cannot just add a duplicate frame, as that would cause errors + * - For now, we don't check if the types will be compatible since we + * don't have enough info to do so. Instead, we simply just paste, + * if it works, it will show up. + */ + for (gps = gpfs->strokes.first; gps; gps = gps->next) { + /* make a copy of stroke, then of its points array */ + gpsn = MEM_dupallocN(gps); + gpsn->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsn->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsn); + } + /* duplicate triangle information */ + gpsn->triangles = MEM_dupallocN(gps->triangles); + /* append stroke to frame */ + BLI_addtail(&gpf->strokes, gpsn); + } + + /* if no strokes (i.e. new frame) added, free gpf */ + if (BLI_listbase_is_empty(&gpf->strokes)) + BKE_gpencil_layer_delframe(gpld, gpf); + } + + /* unapply offset from buffer-frame */ + gpfs->framenum -= offset; + } + } + + /* clean up */ + ANIM_animdata_freelist(&anim_data); + return true; } /* -------------------------------------- */ @@ -517,53 +513,54 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) static short snap_gpf_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene)) { #if 0 /* note: gpf->framenum is already an int! */ - if (gpf->flag & GP_FRAME_SELECT) - gpf->framenum = (int)(floor(gpf->framenum + 0.5)); + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum = (int)(floor(gpf->framenum + 0.5)); #endif - return 0; + return 0; } static short snap_gpf_nearestsec(bGPDframe *gpf, Scene *scene) { - float secf = (float)FPS; - if (gpf->flag & GP_FRAME_SELECT) - gpf->framenum = (int)(floorf(gpf->framenum / secf + 0.5f) * secf); - return 0; + float secf = (float)FPS; + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum = (int)(floorf(gpf->framenum / secf + 0.5f) * secf); + return 0; } static short snap_gpf_cframe(bGPDframe *gpf, Scene *scene) { - if (gpf->flag & GP_FRAME_SELECT) - gpf->framenum = (int)CFRA; - return 0; + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum = (int)CFRA; + return 0; } static short snap_gpf_nearmarker(bGPDframe *gpf, Scene *scene) { - if (gpf->flag & GP_FRAME_SELECT) - gpf->framenum = (int)ED_markers_find_nearest_marker_time(&scene->markers, (float)gpf->framenum); - return 0; + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum = (int)ED_markers_find_nearest_marker_time(&scene->markers, + (float)gpf->framenum); + return 0; } /* snap selected frames to ... */ void ED_gplayer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode) { - switch (mode) { - case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */ - ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearest); - break; - case SNAP_KEYS_CURFRAME: /* snap to current frame */ - ED_gplayer_frames_looper(gpl, scene, snap_gpf_cframe); - break; - case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */ - ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker); - break; - case SNAP_KEYS_NEARSEC: /* snap to nearest second */ - ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec); - break; - default: /* just in case */ - break; - } + switch (mode) { + case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */ + ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearest); + break; + case SNAP_KEYS_CURFRAME: /* snap to current frame */ + ED_gplayer_frames_looper(gpl, scene, snap_gpf_cframe); + break; + case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */ + ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker); + break; + case SNAP_KEYS_NEARSEC: /* snap to nearest second */ + ED_gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec); + break; + default: /* just in case */ + break; + } } /* -------------------------------------- */ @@ -571,105 +568,104 @@ void ED_gplayer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode) static short mirror_gpf_cframe(bGPDframe *gpf, Scene *scene) { - int diff; + int diff; - if (gpf->flag & GP_FRAME_SELECT) { - diff = CFRA - gpf->framenum; - gpf->framenum = CFRA + diff; - } + if (gpf->flag & GP_FRAME_SELECT) { + diff = CFRA - gpf->framenum; + gpf->framenum = CFRA + diff; + } - return 0; + return 0; } static short mirror_gpf_yaxis(bGPDframe *gpf, Scene *UNUSED(scene)) { - int diff; + int diff; - if (gpf->flag & GP_FRAME_SELECT) { - diff = -gpf->framenum; - gpf->framenum = diff; - } + if (gpf->flag & GP_FRAME_SELECT) { + diff = -gpf->framenum; + gpf->framenum = diff; + } - return 0; + return 0; } static short mirror_gpf_xaxis(bGPDframe *gpf, Scene *UNUSED(scene)) { - int diff; + int diff; - /* NOTE: since we can't really do this, we just do the same as for yaxis... */ - if (gpf->flag & GP_FRAME_SELECT) { - diff = -gpf->framenum; - gpf->framenum = diff; - } + /* NOTE: since we can't really do this, we just do the same as for yaxis... */ + if (gpf->flag & GP_FRAME_SELECT) { + diff = -gpf->framenum; + gpf->framenum = diff; + } - return 0; + return 0; } static short mirror_gpf_marker(bGPDframe *gpf, Scene *scene) { - static TimeMarker *marker; - static short initialized = 0; - int diff; - - /* In order for this mirror function to work without - * any extra arguments being added, we use the case - * of bezt==NULL to denote that we should find the - * marker to mirror over. The static pointer is safe - * to use this way, as it will be set to null after - * each cycle in which this is called. - */ - - if (gpf) { - /* mirroring time */ - if ((gpf->flag & GP_FRAME_SELECT) && (marker)) { - diff = (marker->frame - gpf->framenum); - gpf->framenum = (marker->frame + diff); - } - } - else { - /* initialization time */ - if (initialized) { - /* reset everything for safety */ - marker = NULL; - initialized = 0; - } - else { - /* try to find a marker */ - marker = ED_markers_get_first_selected(&scene->markers); - if (marker) { - initialized = 1; - } - } - } - - return 0; + static TimeMarker *marker; + static short initialized = 0; + int diff; + + /* In order for this mirror function to work without + * any extra arguments being added, we use the case + * of bezt==NULL to denote that we should find the + * marker to mirror over. The static pointer is safe + * to use this way, as it will be set to null after + * each cycle in which this is called. + */ + + if (gpf) { + /* mirroring time */ + if ((gpf->flag & GP_FRAME_SELECT) && (marker)) { + diff = (marker->frame - gpf->framenum); + gpf->framenum = (marker->frame + diff); + } + } + else { + /* initialization time */ + if (initialized) { + /* reset everything for safety */ + marker = NULL; + initialized = 0; + } + else { + /* try to find a marker */ + marker = ED_markers_get_first_selected(&scene->markers); + if (marker) { + initialized = 1; + } + } + } + + return 0; } - /* mirror selected gp-frames on... */ // TODO: mirror over a specific time void ED_gplayer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode) { - switch (mode) { - case MIRROR_KEYS_CURFRAME: /* mirror over current frame */ - ED_gplayer_frames_looper(gpl, scene, mirror_gpf_cframe); - break; - case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */ - ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis); - break; - case MIRROR_KEYS_XAXIS: /* mirror over value 0 */ - ED_gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis); - break; - case MIRROR_KEYS_MARKER: /* mirror over marker */ - mirror_gpf_marker(NULL, NULL); - ED_gplayer_frames_looper(gpl, scene, mirror_gpf_marker); - mirror_gpf_marker(NULL, NULL); - break; - default: /* just in case */ - ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis); - break; - } + switch (mode) { + case MIRROR_KEYS_CURFRAME: /* mirror over current frame */ + ED_gplayer_frames_looper(gpl, scene, mirror_gpf_cframe); + break; + case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */ + ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis); + break; + case MIRROR_KEYS_XAXIS: /* mirror over value 0 */ + ED_gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis); + break; + case MIRROR_KEYS_MARKER: /* mirror over marker */ + mirror_gpf_marker(NULL, NULL); + ED_gplayer_frames_looper(gpl, scene, mirror_gpf_marker); + mirror_gpf_marker(NULL, NULL); + break; + default: /* just in case */ + ED_gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis); + break; + } } /* ***************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index de6ecfefb2f..4d62d169bf8 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -42,1386 +42,784 @@ /* Definition of the most important info from a color */ typedef struct ColorTemplate { - const char *name; - float line[4]; - float fill[4]; + const char *name; + float line[4]; + float fill[4]; } ColorTemplate; /* Add color an ensure duplications (matched by name) */ static int gpencil_monkey_color( - Main *bmain, Object *ob, const ColorTemplate *pct, bool stroke, bool fill) + Main *bmain, Object *ob, const ColorTemplate *pct, bool stroke, bool fill) { - short *totcol = give_totcolp(ob); - Material *ma = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - if (STREQ(ma->id.name, pct->name)) { - return i; - } - } - - int idx; - - /* create a new one */ - ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); - - copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); - copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); - - if (!stroke) { - ma->gp_style->flag &= ~GP_STYLE_STROKE_SHOW; - } - - if (!fill) { - ma->gp_style->flag &= ~GP_STYLE_FILL_SHOW; - } - else { - ma->gp_style->flag |= GP_STYLE_FILL_SHOW; - } - - return idx; + short *totcol = give_totcolp(ob); + Material *ma = NULL; + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + if (STREQ(ma->id.name, pct->name)) { + return i; + } + } + + int idx; + + /* create a new one */ + ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + + if (!stroke) { + ma->gp_style->flag &= ~GP_STYLE_STROKE_SHOW; + } + + if (!fill) { + ma->gp_style->flag &= ~GP_STYLE_FILL_SHOW; + } + else { + ma->gp_style->flag |= GP_STYLE_FILL_SHOW; + } + + return idx; } /* ***************************************************************** */ /* Monkey Geometry */ static const float data0[270 * GP_PRIM_DATABUF_SIZE] = { - -0.4911f, 0.0f, -0.1781f, 0.267f, 0.362f, - -0.5168f, 0.0f, -0.1806f, 0.31f, 0.407f, - -0.5361f, 0.0f, -0.1817f, 0.38f, 0.439f, - -0.5618f, 0.0f, -0.1829f, 0.433f, 0.458f, - -0.5892f, 0.0f, -0.1827f, 0.471f, 0.5f, - -0.6193f, 0.0f, -0.1814f, 0.496f, 0.516f, - -0.6499f, 0.0f, -0.1782f, 0.511f, 0.519f, - -0.6808f, 0.0f, -0.1729f, 0.521f, 0.53f, - -0.7107f, 0.0f, -0.1651f, 0.527f, 0.533f, - -0.7404f, 0.0f, -0.1555f, 0.531f, 0.534f, - -0.7698f, 0.0f, -0.1447f, 0.534f, 0.535f, - -0.7993f, 0.0f, -0.1332f, 0.535f, 0.535f, - -0.8289f, 0.0f, -0.1209f, 0.536f, 0.537f, - -0.8586f, 0.0f, -0.109f, 0.536f, 0.537f, - -0.8871f, 0.0f, -0.0973f, 0.536f, 0.537f, - -0.9125f, 0.0f, -0.0838f, 0.535f, 0.535f, - -0.9353f, 0.0f, -0.0688f, 0.534f, 0.534f, - -0.9561f, 0.0f, -0.0525f, 0.534f, 0.534f, - -0.9752f, 0.0f, -0.0346f, 0.533f, 0.534f, - -0.9944f, 0.0f, -0.016f, 0.533f, 0.534f, - -1.0148f, 0.0f, 0.0028f, 0.532f, 0.532f, - -1.0348f, 0.0f, 0.0215f, 0.531f, 0.531f, - -1.05f, 0.0f, 0.0407f, 0.531f, 0.531f, - -1.0639f, 0.0f, 0.0613f, 0.532f, 0.532f, - -1.0752f, 0.0f, 0.0838f, 0.535f, 0.535f, - -1.0848f, 0.0f, 0.1082f, 0.54f, 0.54f, - -1.0936f, 0.0f, 0.1346f, 0.542f, 0.542f, - -1.1024f, 0.0f, 0.1639f, 0.543f, 0.543f, - -1.1102f, 0.0f, 0.1953f, 0.543f, 0.543f, - -1.1128f, 0.0f, 0.2277f, 0.546f, 0.546f, - -1.1091f, 0.0f, 0.2579f, 0.549f, 0.549f, - -1.1023f, 0.0f, 0.2849f, 0.549f, 0.549f, - -1.0934f, 0.0f, 0.3086f, 0.549f, 0.549f, - -1.0831f, 0.0f, 0.3285f, 0.549f, 0.549f, - -1.0724f, 0.0f, 0.3451f, 0.551f, 0.551f, - -1.0607f, 0.0f, 0.3594f, 0.553f, 0.553f, - -1.0474f, 0.0f, 0.3713f, 0.554f, 0.554f, - -1.031f, 0.0f, 0.3804f, 0.554f, 0.554f, - -1.0108f, 0.0f, 0.3874f, 0.555f, 0.555f, - -0.9862f, 0.0f, 0.3922f, 0.556f, 0.556f, - -0.9568f, 0.0f, 0.3941f, 0.557f, 0.557f, - -0.9243f, 0.0f, 0.3934f, 0.557f, 0.557f, - -0.8897f, 0.0f, 0.3861f, 0.557f, 0.557f, - -0.8556f, 0.0f, 0.3754f, 0.557f, 0.557f, - -0.8237f, 0.0f, 0.3608f, 0.558f, 0.557f, - -0.7982f, 0.0f, 0.344f, 0.558f, 0.558f, - -0.7786f, 0.0f, 0.329f, 0.557f, 0.559f, - -0.7633f, 0.0f, 0.3183f, 0.556f, 0.559f, - -0.7498f, 0.0f, 0.3135f, 0.554f, 0.559f, - -0.7374f, 0.0f, 0.3134f, 0.552f, 0.548f, - -0.7261f, 0.0f, 0.3179f, 0.551f, 0.546f, - -0.7146f, 0.0f, 0.3262f, 0.55f, 0.547f, - -0.703f, 0.0f, 0.3395f, 0.549f, 0.547f, - -0.692f, 0.0f, 0.3576f, 0.549f, 0.548f, - -0.6831f, 0.0f, 0.3806f, 0.549f, 0.548f, - -0.6748f, 0.0f, 0.4052f, 0.55f, 0.549f, - -0.6648f, 0.0f, 0.4305f, 0.552f, 0.548f, - -0.6527f, 0.0f, 0.4549f, 0.556f, 0.549f, - -0.6375f, 0.0f, 0.4783f, 0.563f, 0.549f, - -0.6195f, 0.0f, 0.5021f, 0.572f, 0.558f, - -0.5985f, 0.0f, 0.5256f, 0.582f, 0.587f, - -0.5775f, 0.0f, 0.5488f, 0.591f, 0.595f, - -0.556f, 0.0f, 0.5715f, 0.597f, 0.598f, - -0.5339f, 0.0f, 0.593f, 0.602f, 0.603f, - -0.5119f, 0.0f, 0.613f, 0.605f, 0.606f, - -0.4905f, 0.0f, 0.6312f, 0.607f, 0.607f, - -0.4697f, 0.0f, 0.6474f, 0.609f, 0.607f, - -0.4499f, 0.0f, 0.6613f, 0.612f, 0.611f, - -0.4306f, 0.0f, 0.6734f, 0.616f, 0.615f, - -0.4116f, 0.0f, 0.6845f, 0.619f, 0.621f, - -0.3918f, 0.0f, 0.6954f, 0.623f, 0.623f, - -0.3709f, 0.0f, 0.7059f, 0.626f, 0.626f, - -0.3486f, 0.0f, 0.7157f, 0.63f, 0.63f, - -0.3251f, 0.0f, 0.7249f, 0.637f, 0.632f, - -0.3006f, 0.0f, 0.7333f, 0.646f, 0.644f, - -0.2755f, 0.0f, 0.7414f, 0.654f, 0.657f, - -0.25f, 0.0f, 0.7489f, 0.659f, 0.661f, - -0.2242f, 0.0f, 0.7562f, 0.664f, 0.664f, - -0.1979f, 0.0f, 0.7631f, 0.667f, 0.668f, - -0.171f, 0.0f, 0.7695f, 0.671f, 0.671f, - -0.1434f, 0.0f, 0.7752f, 0.674f, 0.674f, - -0.1151f, 0.0f, 0.7801f, 0.677f, 0.678f, - -0.0861f, 0.0f, 0.7841f, 0.678f, 0.68f, - -0.0563f, 0.0f, 0.7869f, 0.68f, 0.68f, - -0.026f, 0.0f, 0.7889f, 0.68f, 0.68f, - 0.0049f, 0.0f, 0.7899f, 0.681f, 0.681f, - 0.0362f, 0.0f, 0.7898f, 0.682f, 0.681f, - 0.0679f, 0.0f, 0.7881f, 0.683f, 0.683f, - 0.0996f, 0.0f, 0.7853f, 0.685f, 0.683f, - 0.1313f, 0.0f, 0.7812f, 0.687f, 0.685f, - 0.1632f, 0.0f, 0.7756f, 0.69f, 0.686f, - 0.1953f, 0.0f, 0.7687f, 0.693f, 0.694f, - 0.2277f, 0.0f, 0.7608f, 0.697f, 0.697f, - 0.2606f, 0.0f, 0.7513f, 0.7f, 0.7f, - 0.2934f, 0.0f, 0.7404f, 0.704f, 0.704f, - 0.3258f, 0.0f, 0.7276f, 0.707f, 0.71f, - 0.357f, 0.0f, 0.7135f, 0.709f, 0.711f, - 0.387f, 0.0f, 0.6983f, 0.711f, 0.713f, - 0.4157f, 0.0f, 0.6819f, 0.712f, 0.714f, - 0.444f, 0.0f, 0.6645f, 0.714f, 0.714f, - 0.4719f, 0.0f, 0.6459f, 0.715f, 0.715f, - 0.4994f, 0.0f, 0.6261f, 0.715f, 0.716f, - 0.526f, 0.0f, 0.6046f, 0.716f, 0.716f, - 0.552f, 0.0f, 0.5816f, 0.717f, 0.717f, - 0.577f, 0.0f, 0.5575f, 0.718f, 0.717f, - 0.6008f, 0.0f, 0.5328f, 0.718f, 0.718f, - 0.6231f, 0.0f, 0.5077f, 0.718f, 0.718f, - 0.6423f, 0.0f, 0.4829f, 0.719f, 0.718f, - 0.658f, 0.0f, 0.4617f, 0.719f, 0.719f, - 0.6713f, 0.0f, 0.4432f, 0.719f, 0.719f, - 0.6828f, 0.0f, 0.4266f, 0.719f, 0.719f, - 0.6928f, 0.0f, 0.4118f, 0.719f, 0.719f, - 0.7016f, 0.0f, 0.3987f, 0.718f, 0.717f, - 0.7094f, 0.0f, 0.3871f, 0.717f, 0.717f, - 0.7165f, 0.0f, 0.3769f, 0.717f, 0.717f, - 0.7233f, 0.0f, 0.3679f, 0.718f, 0.718f, - 0.7301f, 0.0f, 0.3598f, 0.717f, 0.719f, - 0.7373f, 0.0f, 0.3524f, 0.715f, 0.719f, - 0.7454f, 0.0f, 0.3458f, 0.713f, 0.709f, - 0.7545f, 0.0f, 0.3398f, 0.718f, 0.704f, - 0.7651f, 0.0f, 0.3351f, 0.732f, 0.705f, - 0.777f, 0.0f, 0.3317f, 0.753f, 0.713f, - 0.7909f, 0.0f, 0.3311f, 0.774f, 0.813f, - 0.8068f, 0.0f, 0.334f, 0.791f, 0.815f, - 0.8246f, 0.0f, 0.3398f, 0.802f, 0.815f, - 0.8438f, 0.0f, 0.3486f, 0.809f, 0.816f, - 0.8651f, 0.0f, 0.3575f, 0.812f, 0.816f, - 0.8893f, 0.0f, 0.3665f, 0.814f, 0.816f, - 0.9166f, 0.0f, 0.374f, 0.814f, 0.817f, - 0.9459f, 0.0f, 0.3791f, 0.812f, 0.817f, - 0.9751f, 0.0f, 0.3811f, 0.81f, 0.815f, - 1.0029f, 0.0f, 0.38f, 0.806f, 0.807f, - 1.0288f, 0.0f, 0.3754f, 0.8f, 0.801f, - 1.052f, 0.0f, 0.3673f, 0.794f, 0.8f, - 1.0722f, 0.0f, 0.3556f, 0.788f, 0.781f, - 1.0888f, 0.0f, 0.3403f, 0.783f, 0.78f, - 1.1027f, 0.0f, 0.322f, 0.781f, 0.778f, - 1.1133f, 0.0f, 0.301f, 0.779f, 0.777f, - 1.1215f, 0.0f, 0.278f, 0.778f, 0.777f, - 1.1269f, 0.0f, 0.2534f, 0.777f, 0.777f, - 1.1296f, 0.0f, 0.2284f, 0.777f, 0.778f, - 1.1292f, 0.0f, 0.2031f, 0.776f, 0.776f, - 1.1254f, 0.0f, 0.1778f, 0.775f, 0.776f, - 1.1178f, 0.0f, 0.153f, 0.774f, 0.774f, - 1.1076f, 0.0f, 0.1299f, 0.774f, 0.772f, - 1.0955f, 0.0f, 0.1079f, 0.773f, 0.773f, - 1.0817f, 0.0f, 0.087f, 0.772f, 0.773f, - 1.0668f, 0.0f, 0.0677f, 0.771f, 0.772f, - 1.0508f, 0.0f, 0.0491f, 0.77f, 0.772f, - 1.0339f, 0.0f, 0.0313f, 0.768f, 0.766f, - 1.0157f, 0.0f, 0.0144f, 0.767f, 0.765f, - 0.9969f, 0.0f, -0.0015f, 0.766f, 0.765f, - 0.9784f, 0.0f, -0.017f, 0.765f, 0.766f, - 0.96f, 0.0f, -0.0321f, 0.764f, 0.765f, - 0.9413f, 0.0f, -0.0468f, 0.761f, 0.765f, - 0.9216f, 0.0f, -0.0611f, 0.756f, 0.761f, - 0.9009f, 0.0f, -0.0751f, 0.751f, 0.751f, - 0.8787f, 0.0f, -0.0893f, 0.745f, 0.744f, - 0.8556f, 0.0f, -0.1027f, 0.739f, 0.738f, - 0.8312f, 0.0f, -0.1152f, 0.733f, 0.731f, - 0.8058f, 0.0f, -0.1268f, 0.728f, 0.727f, - 0.7788f, 0.0f, -0.1372f, 0.723f, 0.723f, - 0.7505f, 0.0f, -0.1467f, 0.718f, 0.717f, - 0.7214f, 0.0f, -0.1549f, 0.713f, 0.708f, - 0.6929f, 0.0f, -0.1617f, 0.709f, 0.706f, - 0.6652f, 0.0f, -0.1665f, 0.704f, 0.705f, - 0.6388f, 0.0f, -0.1691f, 0.7f, 0.704f, - 0.6131f, 0.0f, -0.1701f, 0.695f, 0.698f, - 0.5883f, 0.0f, -0.1699f, 0.691f, 0.691f, - 0.5644f, 0.0f, -0.1691f, 0.686f, 0.685f, - 0.5416f, 0.0f, -0.1683f, 0.681f, 0.683f, - 0.5195f, 0.0f, -0.168f, 0.676f, 0.676f, - 0.4975f, 0.0f, -0.1687f, 0.671f, 0.67f, - 0.4754f, 0.0f, -0.1705f, 0.666f, 0.663f, - 0.4527f, 0.0f, -0.1741f, 0.663f, 0.66f, - 0.4293f, 0.0f, -0.1797f, 0.661f, 0.659f, - 0.4054f, 0.0f, -0.1881f, 0.66f, 0.659f, - 0.3813f, 0.0f, -0.1992f, 0.659f, 0.657f, - 0.3585f, 0.0f, -0.212f, 0.658f, 0.659f, - 0.3368f, 0.0f, -0.2266f, 0.658f, 0.659f, - 0.3174f, 0.0f, -0.2426f, 0.658f, 0.659f, - 0.2996f, 0.0f, -0.2594f, 0.657f, 0.657f, - 0.284f, 0.0f, -0.2768f, 0.656f, 0.658f, - 0.2702f, 0.0f, -0.2946f, 0.653f, 0.657f, - 0.2585f, 0.0f, -0.3127f, 0.646f, 0.656f, - 0.25f, 0.0f, -0.3308f, 0.637f, 0.642f, - 0.2447f, 0.0f, -0.3489f, 0.628f, 0.609f, - 0.2418f, 0.0f, -0.3672f, 0.62f, 0.608f, - 0.2412f, 0.0f, -0.386f, 0.614f, 0.607f, - 0.2425f, 0.0f, -0.4051f, 0.61f, 0.606f, - 0.2456f, 0.0f, -0.4246f, 0.608f, 0.604f, - 0.2509f, 0.0f, -0.4447f, 0.607f, 0.606f, - 0.2576f, 0.0f, -0.4652f, 0.606f, 0.607f, - 0.2666f, 0.0f, -0.4867f, 0.605f, 0.607f, - 0.2766f, 0.0f, -0.5091f, 0.603f, 0.607f, - 0.2871f, 0.0f, -0.5326f, 0.598f, 0.606f, - 0.2973f, 0.0f, -0.5569f, 0.591f, 0.602f, - 0.306f, 0.0f, -0.5826f, 0.583f, 0.585f, - 0.3131f, 0.0f, -0.61f, 0.574f, 0.576f, - 0.3197f, 0.0f, -0.6384f, 0.564f, 0.564f, - 0.326f, 0.0f, -0.6681f, 0.555f, 0.549f, - 0.3315f, 0.0f, -0.6984f, 0.547f, 0.543f, - 0.336f, 0.0f, -0.7291f, 0.541f, 0.541f, - 0.3391f, 0.0f, -0.7593f, 0.536f, 0.538f, - 0.3399f, 0.0f, -0.7884f, 0.532f, 0.528f, - 0.3382f, 0.0f, -0.8158f, 0.529f, 0.528f, - 0.334f, 0.0f, -0.8417f, 0.525f, 0.529f, - 0.3273f, 0.0f, -0.8657f, 0.521f, 0.528f, - 0.3185f, 0.0f, -0.8881f, 0.516f, 0.515f, - 0.3073f, 0.0f, -0.9088f, 0.51f, 0.514f, - 0.2941f, 0.0f, -0.9278f, 0.505f, 0.507f, - 0.2786f, 0.0f, -0.9449f, 0.499f, 0.494f, - 0.261f, 0.0f, -0.96f, 0.495f, 0.49f, - 0.2413f, 0.0f, -0.9733f, 0.493f, 0.491f, - 0.2193f, 0.0f, -0.9845f, 0.491f, 0.489f, - 0.1953f, 0.0f, -0.9935f, 0.491f, 0.491f, - 0.1693f, 0.0f, -1.0004f, 0.491f, 0.492f, - 0.1421f, 0.0f, -1.0051f, 0.492f, 0.492f, - 0.1136f, 0.0f, -1.0072f, 0.492f, 0.492f, - 0.0842f, 0.0f, -1.0073f, 0.492f, 0.492f, - 0.0548f, 0.0f, -1.0059f, 0.493f, 0.494f, - 0.0258f, 0.0f, -1.0037f, 0.493f, 0.494f, - -0.0027f, 0.0f, -1.0003f, 0.493f, 0.493f, - -0.0309f, 0.0f, -0.9959f, 0.492f, 0.492f, - -0.0584f, 0.0f, -0.9904f, 0.491f, 0.492f, - -0.0858f, 0.0f, -0.9848f, 0.491f, 0.491f, - -0.1127f, 0.0f, -0.9783f, 0.49f, 0.49f, - -0.1386f, 0.0f, -0.9703f, 0.49f, 0.49f, - -0.1649f, 0.0f, -0.9604f, 0.489f, 0.489f, - -0.191f, 0.0f, -0.9479f, 0.489f, 0.489f, - -0.2165f, 0.0f, -0.9345f, 0.489f, 0.49f, - -0.2414f, 0.0f, -0.9205f, 0.489f, 0.489f, - -0.2654f, 0.0f, -0.9055f, 0.489f, 0.489f, - -0.2877f, 0.0f, -0.8898f, 0.49f, 0.49f, - -0.3076f, 0.0f, -0.8723f, 0.49f, 0.489f, - -0.324f, 0.0f, -0.8532f, 0.491f, 0.489f, - -0.3367f, 0.0f, -0.8316f, 0.492f, 0.489f, - -0.3451f, 0.0f, -0.8077f, 0.494f, 0.488f, - -0.3505f, 0.0f, -0.7829f, 0.497f, 0.49f, - -0.3531f, 0.0f, -0.7584f, 0.501f, 0.497f, - -0.3528f, 0.0f, -0.7349f, 0.505f, 0.504f, - -0.3503f, 0.0f, -0.7115f, 0.51f, 0.51f, - -0.346f, 0.0f, -0.688f, 0.515f, 0.515f, - -0.3411f, 0.0f, -0.6643f, 0.52f, 0.522f, - -0.3361f, 0.0f, -0.6403f, 0.525f, 0.528f, - -0.3304f, 0.0f, -0.6164f, 0.53f, 0.532f, - -0.3244f, 0.0f, -0.5925f, 0.535f, 0.535f, - -0.318f, 0.0f, -0.5687f, 0.539f, 0.54f, - -0.3124f, 0.0f, -0.5441f, 0.542f, 0.545f, - -0.3051f, 0.0f, -0.5191f, 0.546f, 0.549f, - -0.2959f, 0.0f, -0.4917f, 0.548f, 0.549f, - -0.2882f, 0.0f, -0.4639f, 0.55f, 0.552f, - -0.2814f, 0.0f, -0.4363f, 0.551f, 0.552f, - -0.2759f, 0.0f, -0.4084f, 0.552f, 0.553f, - -0.2707f, 0.0f, -0.3827f, 0.553f, 0.553f, - -0.2703f, 0.0f, -0.3586f, 0.554f, 0.553f, - -0.2772f, 0.0f, -0.3375f, 0.554f, 0.554f, - -0.2871f, 0.0f, -0.3178f, 0.555f, 0.555f, - -0.2995f, 0.0f, -0.2996f, 0.556f, 0.555f, - -0.3145f, 0.0f, -0.283f, 0.556f, 0.557f, - -0.332f, 0.0f, -0.2672f, 0.557f, 0.557f, - -0.3488f, 0.0f, -0.2531f, 0.558f, 0.558f, - -0.3639f, 0.0f, -0.2407f, 0.558f, 0.558f, - -0.3778f, 0.0f, -0.2292f, 0.558f, 0.558f, - -0.3909f, 0.0f, -0.2191f, 0.559f, 0.559f, - -0.4032f, 0.0f, -0.2102f, 0.559f, 0.558f, - -0.4146f, 0.0f, -0.2027f, 0.559f, 0.559f, - -0.426f, 0.0f, -0.1968f, 0.558f, 0.558f, - -0.4348f, 0.0f, -0.1931f, 0.558f, 0.558f, - -0.4479f, 0.0f, -0.1886f, 0.555f, 0.559f, + -0.4911f, 0.0f, -0.1781f, 0.267f, 0.362f, -0.5168f, 0.0f, -0.1806f, 0.31f, 0.407f, + -0.5361f, 0.0f, -0.1817f, 0.38f, 0.439f, -0.5618f, 0.0f, -0.1829f, 0.433f, 0.458f, + -0.5892f, 0.0f, -0.1827f, 0.471f, 0.5f, -0.6193f, 0.0f, -0.1814f, 0.496f, 0.516f, + -0.6499f, 0.0f, -0.1782f, 0.511f, 0.519f, -0.6808f, 0.0f, -0.1729f, 0.521f, 0.53f, + -0.7107f, 0.0f, -0.1651f, 0.527f, 0.533f, -0.7404f, 0.0f, -0.1555f, 0.531f, 0.534f, + -0.7698f, 0.0f, -0.1447f, 0.534f, 0.535f, -0.7993f, 0.0f, -0.1332f, 0.535f, 0.535f, + -0.8289f, 0.0f, -0.1209f, 0.536f, 0.537f, -0.8586f, 0.0f, -0.109f, 0.536f, 0.537f, + -0.8871f, 0.0f, -0.0973f, 0.536f, 0.537f, -0.9125f, 0.0f, -0.0838f, 0.535f, 0.535f, + -0.9353f, 0.0f, -0.0688f, 0.534f, 0.534f, -0.9561f, 0.0f, -0.0525f, 0.534f, 0.534f, + -0.9752f, 0.0f, -0.0346f, 0.533f, 0.534f, -0.9944f, 0.0f, -0.016f, 0.533f, 0.534f, + -1.0148f, 0.0f, 0.0028f, 0.532f, 0.532f, -1.0348f, 0.0f, 0.0215f, 0.531f, 0.531f, + -1.05f, 0.0f, 0.0407f, 0.531f, 0.531f, -1.0639f, 0.0f, 0.0613f, 0.532f, 0.532f, + -1.0752f, 0.0f, 0.0838f, 0.535f, 0.535f, -1.0848f, 0.0f, 0.1082f, 0.54f, 0.54f, + -1.0936f, 0.0f, 0.1346f, 0.542f, 0.542f, -1.1024f, 0.0f, 0.1639f, 0.543f, 0.543f, + -1.1102f, 0.0f, 0.1953f, 0.543f, 0.543f, -1.1128f, 0.0f, 0.2277f, 0.546f, 0.546f, + -1.1091f, 0.0f, 0.2579f, 0.549f, 0.549f, -1.1023f, 0.0f, 0.2849f, 0.549f, 0.549f, + -1.0934f, 0.0f, 0.3086f, 0.549f, 0.549f, -1.0831f, 0.0f, 0.3285f, 0.549f, 0.549f, + -1.0724f, 0.0f, 0.3451f, 0.551f, 0.551f, -1.0607f, 0.0f, 0.3594f, 0.553f, 0.553f, + -1.0474f, 0.0f, 0.3713f, 0.554f, 0.554f, -1.031f, 0.0f, 0.3804f, 0.554f, 0.554f, + -1.0108f, 0.0f, 0.3874f, 0.555f, 0.555f, -0.9862f, 0.0f, 0.3922f, 0.556f, 0.556f, + -0.9568f, 0.0f, 0.3941f, 0.557f, 0.557f, -0.9243f, 0.0f, 0.3934f, 0.557f, 0.557f, + -0.8897f, 0.0f, 0.3861f, 0.557f, 0.557f, -0.8556f, 0.0f, 0.3754f, 0.557f, 0.557f, + -0.8237f, 0.0f, 0.3608f, 0.558f, 0.557f, -0.7982f, 0.0f, 0.344f, 0.558f, 0.558f, + -0.7786f, 0.0f, 0.329f, 0.557f, 0.559f, -0.7633f, 0.0f, 0.3183f, 0.556f, 0.559f, + -0.7498f, 0.0f, 0.3135f, 0.554f, 0.559f, -0.7374f, 0.0f, 0.3134f, 0.552f, 0.548f, + -0.7261f, 0.0f, 0.3179f, 0.551f, 0.546f, -0.7146f, 0.0f, 0.3262f, 0.55f, 0.547f, + -0.703f, 0.0f, 0.3395f, 0.549f, 0.547f, -0.692f, 0.0f, 0.3576f, 0.549f, 0.548f, + -0.6831f, 0.0f, 0.3806f, 0.549f, 0.548f, -0.6748f, 0.0f, 0.4052f, 0.55f, 0.549f, + -0.6648f, 0.0f, 0.4305f, 0.552f, 0.548f, -0.6527f, 0.0f, 0.4549f, 0.556f, 0.549f, + -0.6375f, 0.0f, 0.4783f, 0.563f, 0.549f, -0.6195f, 0.0f, 0.5021f, 0.572f, 0.558f, + -0.5985f, 0.0f, 0.5256f, 0.582f, 0.587f, -0.5775f, 0.0f, 0.5488f, 0.591f, 0.595f, + -0.556f, 0.0f, 0.5715f, 0.597f, 0.598f, -0.5339f, 0.0f, 0.593f, 0.602f, 0.603f, + -0.5119f, 0.0f, 0.613f, 0.605f, 0.606f, -0.4905f, 0.0f, 0.6312f, 0.607f, 0.607f, + -0.4697f, 0.0f, 0.6474f, 0.609f, 0.607f, -0.4499f, 0.0f, 0.6613f, 0.612f, 0.611f, + -0.4306f, 0.0f, 0.6734f, 0.616f, 0.615f, -0.4116f, 0.0f, 0.6845f, 0.619f, 0.621f, + -0.3918f, 0.0f, 0.6954f, 0.623f, 0.623f, -0.3709f, 0.0f, 0.7059f, 0.626f, 0.626f, + -0.3486f, 0.0f, 0.7157f, 0.63f, 0.63f, -0.3251f, 0.0f, 0.7249f, 0.637f, 0.632f, + -0.3006f, 0.0f, 0.7333f, 0.646f, 0.644f, -0.2755f, 0.0f, 0.7414f, 0.654f, 0.657f, + -0.25f, 0.0f, 0.7489f, 0.659f, 0.661f, -0.2242f, 0.0f, 0.7562f, 0.664f, 0.664f, + -0.1979f, 0.0f, 0.7631f, 0.667f, 0.668f, -0.171f, 0.0f, 0.7695f, 0.671f, 0.671f, + -0.1434f, 0.0f, 0.7752f, 0.674f, 0.674f, -0.1151f, 0.0f, 0.7801f, 0.677f, 0.678f, + -0.0861f, 0.0f, 0.7841f, 0.678f, 0.68f, -0.0563f, 0.0f, 0.7869f, 0.68f, 0.68f, + -0.026f, 0.0f, 0.7889f, 0.68f, 0.68f, 0.0049f, 0.0f, 0.7899f, 0.681f, 0.681f, + 0.0362f, 0.0f, 0.7898f, 0.682f, 0.681f, 0.0679f, 0.0f, 0.7881f, 0.683f, 0.683f, + 0.0996f, 0.0f, 0.7853f, 0.685f, 0.683f, 0.1313f, 0.0f, 0.7812f, 0.687f, 0.685f, + 0.1632f, 0.0f, 0.7756f, 0.69f, 0.686f, 0.1953f, 0.0f, 0.7687f, 0.693f, 0.694f, + 0.2277f, 0.0f, 0.7608f, 0.697f, 0.697f, 0.2606f, 0.0f, 0.7513f, 0.7f, 0.7f, + 0.2934f, 0.0f, 0.7404f, 0.704f, 0.704f, 0.3258f, 0.0f, 0.7276f, 0.707f, 0.71f, + 0.357f, 0.0f, 0.7135f, 0.709f, 0.711f, 0.387f, 0.0f, 0.6983f, 0.711f, 0.713f, + 0.4157f, 0.0f, 0.6819f, 0.712f, 0.714f, 0.444f, 0.0f, 0.6645f, 0.714f, 0.714f, + 0.4719f, 0.0f, 0.6459f, 0.715f, 0.715f, 0.4994f, 0.0f, 0.6261f, 0.715f, 0.716f, + 0.526f, 0.0f, 0.6046f, 0.716f, 0.716f, 0.552f, 0.0f, 0.5816f, 0.717f, 0.717f, + 0.577f, 0.0f, 0.5575f, 0.718f, 0.717f, 0.6008f, 0.0f, 0.5328f, 0.718f, 0.718f, + 0.6231f, 0.0f, 0.5077f, 0.718f, 0.718f, 0.6423f, 0.0f, 0.4829f, 0.719f, 0.718f, + 0.658f, 0.0f, 0.4617f, 0.719f, 0.719f, 0.6713f, 0.0f, 0.4432f, 0.719f, 0.719f, + 0.6828f, 0.0f, 0.4266f, 0.719f, 0.719f, 0.6928f, 0.0f, 0.4118f, 0.719f, 0.719f, + 0.7016f, 0.0f, 0.3987f, 0.718f, 0.717f, 0.7094f, 0.0f, 0.3871f, 0.717f, 0.717f, + 0.7165f, 0.0f, 0.3769f, 0.717f, 0.717f, 0.7233f, 0.0f, 0.3679f, 0.718f, 0.718f, + 0.7301f, 0.0f, 0.3598f, 0.717f, 0.719f, 0.7373f, 0.0f, 0.3524f, 0.715f, 0.719f, + 0.7454f, 0.0f, 0.3458f, 0.713f, 0.709f, 0.7545f, 0.0f, 0.3398f, 0.718f, 0.704f, + 0.7651f, 0.0f, 0.3351f, 0.732f, 0.705f, 0.777f, 0.0f, 0.3317f, 0.753f, 0.713f, + 0.7909f, 0.0f, 0.3311f, 0.774f, 0.813f, 0.8068f, 0.0f, 0.334f, 0.791f, 0.815f, + 0.8246f, 0.0f, 0.3398f, 0.802f, 0.815f, 0.8438f, 0.0f, 0.3486f, 0.809f, 0.816f, + 0.8651f, 0.0f, 0.3575f, 0.812f, 0.816f, 0.8893f, 0.0f, 0.3665f, 0.814f, 0.816f, + 0.9166f, 0.0f, 0.374f, 0.814f, 0.817f, 0.9459f, 0.0f, 0.3791f, 0.812f, 0.817f, + 0.9751f, 0.0f, 0.3811f, 0.81f, 0.815f, 1.0029f, 0.0f, 0.38f, 0.806f, 0.807f, + 1.0288f, 0.0f, 0.3754f, 0.8f, 0.801f, 1.052f, 0.0f, 0.3673f, 0.794f, 0.8f, + 1.0722f, 0.0f, 0.3556f, 0.788f, 0.781f, 1.0888f, 0.0f, 0.3403f, 0.783f, 0.78f, + 1.1027f, 0.0f, 0.322f, 0.781f, 0.778f, 1.1133f, 0.0f, 0.301f, 0.779f, 0.777f, + 1.1215f, 0.0f, 0.278f, 0.778f, 0.777f, 1.1269f, 0.0f, 0.2534f, 0.777f, 0.777f, + 1.1296f, 0.0f, 0.2284f, 0.777f, 0.778f, 1.1292f, 0.0f, 0.2031f, 0.776f, 0.776f, + 1.1254f, 0.0f, 0.1778f, 0.775f, 0.776f, 1.1178f, 0.0f, 0.153f, 0.774f, 0.774f, + 1.1076f, 0.0f, 0.1299f, 0.774f, 0.772f, 1.0955f, 0.0f, 0.1079f, 0.773f, 0.773f, + 1.0817f, 0.0f, 0.087f, 0.772f, 0.773f, 1.0668f, 0.0f, 0.0677f, 0.771f, 0.772f, + 1.0508f, 0.0f, 0.0491f, 0.77f, 0.772f, 1.0339f, 0.0f, 0.0313f, 0.768f, 0.766f, + 1.0157f, 0.0f, 0.0144f, 0.767f, 0.765f, 0.9969f, 0.0f, -0.0015f, 0.766f, 0.765f, + 0.9784f, 0.0f, -0.017f, 0.765f, 0.766f, 0.96f, 0.0f, -0.0321f, 0.764f, 0.765f, + 0.9413f, 0.0f, -0.0468f, 0.761f, 0.765f, 0.9216f, 0.0f, -0.0611f, 0.756f, 0.761f, + 0.9009f, 0.0f, -0.0751f, 0.751f, 0.751f, 0.8787f, 0.0f, -0.0893f, 0.745f, 0.744f, + 0.8556f, 0.0f, -0.1027f, 0.739f, 0.738f, 0.8312f, 0.0f, -0.1152f, 0.733f, 0.731f, + 0.8058f, 0.0f, -0.1268f, 0.728f, 0.727f, 0.7788f, 0.0f, -0.1372f, 0.723f, 0.723f, + 0.7505f, 0.0f, -0.1467f, 0.718f, 0.717f, 0.7214f, 0.0f, -0.1549f, 0.713f, 0.708f, + 0.6929f, 0.0f, -0.1617f, 0.709f, 0.706f, 0.6652f, 0.0f, -0.1665f, 0.704f, 0.705f, + 0.6388f, 0.0f, -0.1691f, 0.7f, 0.704f, 0.6131f, 0.0f, -0.1701f, 0.695f, 0.698f, + 0.5883f, 0.0f, -0.1699f, 0.691f, 0.691f, 0.5644f, 0.0f, -0.1691f, 0.686f, 0.685f, + 0.5416f, 0.0f, -0.1683f, 0.681f, 0.683f, 0.5195f, 0.0f, -0.168f, 0.676f, 0.676f, + 0.4975f, 0.0f, -0.1687f, 0.671f, 0.67f, 0.4754f, 0.0f, -0.1705f, 0.666f, 0.663f, + 0.4527f, 0.0f, -0.1741f, 0.663f, 0.66f, 0.4293f, 0.0f, -0.1797f, 0.661f, 0.659f, + 0.4054f, 0.0f, -0.1881f, 0.66f, 0.659f, 0.3813f, 0.0f, -0.1992f, 0.659f, 0.657f, + 0.3585f, 0.0f, -0.212f, 0.658f, 0.659f, 0.3368f, 0.0f, -0.2266f, 0.658f, 0.659f, + 0.3174f, 0.0f, -0.2426f, 0.658f, 0.659f, 0.2996f, 0.0f, -0.2594f, 0.657f, 0.657f, + 0.284f, 0.0f, -0.2768f, 0.656f, 0.658f, 0.2702f, 0.0f, -0.2946f, 0.653f, 0.657f, + 0.2585f, 0.0f, -0.3127f, 0.646f, 0.656f, 0.25f, 0.0f, -0.3308f, 0.637f, 0.642f, + 0.2447f, 0.0f, -0.3489f, 0.628f, 0.609f, 0.2418f, 0.0f, -0.3672f, 0.62f, 0.608f, + 0.2412f, 0.0f, -0.386f, 0.614f, 0.607f, 0.2425f, 0.0f, -0.4051f, 0.61f, 0.606f, + 0.2456f, 0.0f, -0.4246f, 0.608f, 0.604f, 0.2509f, 0.0f, -0.4447f, 0.607f, 0.606f, + 0.2576f, 0.0f, -0.4652f, 0.606f, 0.607f, 0.2666f, 0.0f, -0.4867f, 0.605f, 0.607f, + 0.2766f, 0.0f, -0.5091f, 0.603f, 0.607f, 0.2871f, 0.0f, -0.5326f, 0.598f, 0.606f, + 0.2973f, 0.0f, -0.5569f, 0.591f, 0.602f, 0.306f, 0.0f, -0.5826f, 0.583f, 0.585f, + 0.3131f, 0.0f, -0.61f, 0.574f, 0.576f, 0.3197f, 0.0f, -0.6384f, 0.564f, 0.564f, + 0.326f, 0.0f, -0.6681f, 0.555f, 0.549f, 0.3315f, 0.0f, -0.6984f, 0.547f, 0.543f, + 0.336f, 0.0f, -0.7291f, 0.541f, 0.541f, 0.3391f, 0.0f, -0.7593f, 0.536f, 0.538f, + 0.3399f, 0.0f, -0.7884f, 0.532f, 0.528f, 0.3382f, 0.0f, -0.8158f, 0.529f, 0.528f, + 0.334f, 0.0f, -0.8417f, 0.525f, 0.529f, 0.3273f, 0.0f, -0.8657f, 0.521f, 0.528f, + 0.3185f, 0.0f, -0.8881f, 0.516f, 0.515f, 0.3073f, 0.0f, -0.9088f, 0.51f, 0.514f, + 0.2941f, 0.0f, -0.9278f, 0.505f, 0.507f, 0.2786f, 0.0f, -0.9449f, 0.499f, 0.494f, + 0.261f, 0.0f, -0.96f, 0.495f, 0.49f, 0.2413f, 0.0f, -0.9733f, 0.493f, 0.491f, + 0.2193f, 0.0f, -0.9845f, 0.491f, 0.489f, 0.1953f, 0.0f, -0.9935f, 0.491f, 0.491f, + 0.1693f, 0.0f, -1.0004f, 0.491f, 0.492f, 0.1421f, 0.0f, -1.0051f, 0.492f, 0.492f, + 0.1136f, 0.0f, -1.0072f, 0.492f, 0.492f, 0.0842f, 0.0f, -1.0073f, 0.492f, 0.492f, + 0.0548f, 0.0f, -1.0059f, 0.493f, 0.494f, 0.0258f, 0.0f, -1.0037f, 0.493f, 0.494f, + -0.0027f, 0.0f, -1.0003f, 0.493f, 0.493f, -0.0309f, 0.0f, -0.9959f, 0.492f, 0.492f, + -0.0584f, 0.0f, -0.9904f, 0.491f, 0.492f, -0.0858f, 0.0f, -0.9848f, 0.491f, 0.491f, + -0.1127f, 0.0f, -0.9783f, 0.49f, 0.49f, -0.1386f, 0.0f, -0.9703f, 0.49f, 0.49f, + -0.1649f, 0.0f, -0.9604f, 0.489f, 0.489f, -0.191f, 0.0f, -0.9479f, 0.489f, 0.489f, + -0.2165f, 0.0f, -0.9345f, 0.489f, 0.49f, -0.2414f, 0.0f, -0.9205f, 0.489f, 0.489f, + -0.2654f, 0.0f, -0.9055f, 0.489f, 0.489f, -0.2877f, 0.0f, -0.8898f, 0.49f, 0.49f, + -0.3076f, 0.0f, -0.8723f, 0.49f, 0.489f, -0.324f, 0.0f, -0.8532f, 0.491f, 0.489f, + -0.3367f, 0.0f, -0.8316f, 0.492f, 0.489f, -0.3451f, 0.0f, -0.8077f, 0.494f, 0.488f, + -0.3505f, 0.0f, -0.7829f, 0.497f, 0.49f, -0.3531f, 0.0f, -0.7584f, 0.501f, 0.497f, + -0.3528f, 0.0f, -0.7349f, 0.505f, 0.504f, -0.3503f, 0.0f, -0.7115f, 0.51f, 0.51f, + -0.346f, 0.0f, -0.688f, 0.515f, 0.515f, -0.3411f, 0.0f, -0.6643f, 0.52f, 0.522f, + -0.3361f, 0.0f, -0.6403f, 0.525f, 0.528f, -0.3304f, 0.0f, -0.6164f, 0.53f, 0.532f, + -0.3244f, 0.0f, -0.5925f, 0.535f, 0.535f, -0.318f, 0.0f, -0.5687f, 0.539f, 0.54f, + -0.3124f, 0.0f, -0.5441f, 0.542f, 0.545f, -0.3051f, 0.0f, -0.5191f, 0.546f, 0.549f, + -0.2959f, 0.0f, -0.4917f, 0.548f, 0.549f, -0.2882f, 0.0f, -0.4639f, 0.55f, 0.552f, + -0.2814f, 0.0f, -0.4363f, 0.551f, 0.552f, -0.2759f, 0.0f, -0.4084f, 0.552f, 0.553f, + -0.2707f, 0.0f, -0.3827f, 0.553f, 0.553f, -0.2703f, 0.0f, -0.3586f, 0.554f, 0.553f, + -0.2772f, 0.0f, -0.3375f, 0.554f, 0.554f, -0.2871f, 0.0f, -0.3178f, 0.555f, 0.555f, + -0.2995f, 0.0f, -0.2996f, 0.556f, 0.555f, -0.3145f, 0.0f, -0.283f, 0.556f, 0.557f, + -0.332f, 0.0f, -0.2672f, 0.557f, 0.557f, -0.3488f, 0.0f, -0.2531f, 0.558f, 0.558f, + -0.3639f, 0.0f, -0.2407f, 0.558f, 0.558f, -0.3778f, 0.0f, -0.2292f, 0.558f, 0.558f, + -0.3909f, 0.0f, -0.2191f, 0.559f, 0.559f, -0.4032f, 0.0f, -0.2102f, 0.559f, 0.558f, + -0.4146f, 0.0f, -0.2027f, 0.559f, 0.559f, -0.426f, 0.0f, -0.1968f, 0.558f, 0.558f, + -0.4348f, 0.0f, -0.1931f, 0.558f, 0.558f, -0.4479f, 0.0f, -0.1886f, 0.555f, 0.559f, }; static const float data1[33 * GP_PRIM_DATABUF_SIZE] = { - 0.5292f, 0.0f, 0.1742f, 0.1f, 1.0f, - 0.5291f, 0.0f, 0.1621f, 0.2199f, 1.0f, - 0.5274f, 0.0f, 0.1386f, 0.4615f, 1.0f, - 0.5239f, 0.0f, 0.116f, 0.6019f, 1.0f, - 0.5185f, 0.0f, 0.0945f, 0.6981f, 1.0f, - 0.5115f, 0.0f, 0.0741f, 0.7689f, 1.0f, - 0.503f, 0.0f, 0.0548f, 0.8236f, 1.0f, - 0.4931f, 0.0f, 0.0368f, 0.866f, 1.0f, - 0.482f, 0.0f, 0.02f, 0.9003f, 1.0f, - 0.4697f, 0.0f, 0.0046f, 0.9272f, 1.0f, - 0.4565f, 0.0f, -0.0094f, 0.9485f, 1.0f, - 0.4424f, 0.0f, -0.0219f, 0.9653f, 1.0f, - 0.4275f, 0.0f, -0.033f, 0.9781f, 1.0f, - 0.4121f, 0.0f, -0.0424f, 0.9876f, 1.0f, - 0.3961f, 0.0f, -0.0501f, 0.9942f, 1.0f, - 0.3799f, 0.0f, -0.0562f, 0.9983f, 1.0f, - 0.3634f, 0.0f, -0.0605f, 0.9997f, 1.0f, - 0.3468f, 0.0f, -0.0629f, 0.999f, 1.0f, - 0.3303f, 0.0f, -0.0634f, 0.9963f, 1.0f, - 0.3139f, 0.0f, -0.062f, 0.9912f, 1.0f, - 0.2979f, 0.0f, -0.0585f, 0.9834f, 1.0f, - 0.2823f, 0.0f, -0.0529f, 0.9724f, 1.0f, - 0.2672f, 0.0f, -0.0452f, 0.9576f, 1.0f, - 0.2529f, 0.0f, -0.0352f, 0.9385f, 1.0f, - 0.2393f, 0.0f, -0.023f, 0.9143f, 1.0f, - 0.2267f, 0.0f, -0.0085f, 0.8841f, 1.0f, - 0.2153f, 0.0f, 0.0085f, 0.8461f, 1.0f, - 0.205f, 0.0f, 0.0279f, 0.7979f, 1.0f, - 0.196f, 0.0f, 0.0499f, 0.7359f, 1.0f, - 0.1886f, 0.0f, 0.0745f, 0.6541f, 1.0f, - 0.1827f, 0.0f, 0.1017f, 0.5396f, 1.0f, - 0.1786f, 0.0f, 0.1316f, 0.36f, 1.0f, - 0.1763f, 0.0f, 0.1643f, 0.1f, 1.0f, + 0.5292f, 0.0f, 0.1742f, 0.1f, 1.0f, 0.5291f, 0.0f, 0.1621f, 0.2199f, 1.0f, + 0.5274f, 0.0f, 0.1386f, 0.4615f, 1.0f, 0.5239f, 0.0f, 0.116f, 0.6019f, 1.0f, + 0.5185f, 0.0f, 0.0945f, 0.6981f, 1.0f, 0.5115f, 0.0f, 0.0741f, 0.7689f, 1.0f, + 0.503f, 0.0f, 0.0548f, 0.8236f, 1.0f, 0.4931f, 0.0f, 0.0368f, 0.866f, 1.0f, + 0.482f, 0.0f, 0.02f, 0.9003f, 1.0f, 0.4697f, 0.0f, 0.0046f, 0.9272f, 1.0f, + 0.4565f, 0.0f, -0.0094f, 0.9485f, 1.0f, 0.4424f, 0.0f, -0.0219f, 0.9653f, 1.0f, + 0.4275f, 0.0f, -0.033f, 0.9781f, 1.0f, 0.4121f, 0.0f, -0.0424f, 0.9876f, 1.0f, + 0.3961f, 0.0f, -0.0501f, 0.9942f, 1.0f, 0.3799f, 0.0f, -0.0562f, 0.9983f, 1.0f, + 0.3634f, 0.0f, -0.0605f, 0.9997f, 1.0f, 0.3468f, 0.0f, -0.0629f, 0.999f, 1.0f, + 0.3303f, 0.0f, -0.0634f, 0.9963f, 1.0f, 0.3139f, 0.0f, -0.062f, 0.9912f, 1.0f, + 0.2979f, 0.0f, -0.0585f, 0.9834f, 1.0f, 0.2823f, 0.0f, -0.0529f, 0.9724f, 1.0f, + 0.2672f, 0.0f, -0.0452f, 0.9576f, 1.0f, 0.2529f, 0.0f, -0.0352f, 0.9385f, 1.0f, + 0.2393f, 0.0f, -0.023f, 0.9143f, 1.0f, 0.2267f, 0.0f, -0.0085f, 0.8841f, 1.0f, + 0.2153f, 0.0f, 0.0085f, 0.8461f, 1.0f, 0.205f, 0.0f, 0.0279f, 0.7979f, 1.0f, + 0.196f, 0.0f, 0.0499f, 0.7359f, 1.0f, 0.1886f, 0.0f, 0.0745f, 0.6541f, 1.0f, + 0.1827f, 0.0f, 0.1017f, 0.5396f, 1.0f, 0.1786f, 0.0f, 0.1316f, 0.36f, 1.0f, + 0.1763f, 0.0f, 0.1643f, 0.1f, 1.0f, }; static const float data2[18 * GP_PRIM_DATABUF_SIZE] = { - -0.0844f, 0.0f, -0.301f, 0.1f, 1.0f, - -0.0825f, 0.0f, -0.3034f, 0.2199f, 1.0f, - -0.0751f, 0.0f, -0.3128f, 0.6019f, 1.0f, - -0.0677f, 0.0f, -0.3216f, 0.7689f, 1.0f, - -0.06f, 0.0f, -0.3298f, 0.866f, 1.0f, - -0.0522f, 0.0f, -0.3372f, 0.9272f, 1.0f, - -0.044f, 0.0f, -0.3437f, 0.9653f, 1.0f, - -0.0354f, 0.0f, -0.3491f, 0.9876f, 1.0f, - -0.0264f, 0.0f, -0.3535f, 0.9983f, 1.0f, - -0.0168f, 0.0f, -0.3566f, 0.999f, 1.0f, - -0.0065f, 0.0f, -0.3583f, 0.9912f, 1.0f, - 0.0045f, 0.0f, -0.3585f, 0.9724f, 1.0f, - 0.0163f, 0.0f, -0.3571f, 0.9385f, 1.0f, - 0.029f, 0.0f, -0.354f, 0.8841f, 1.0f, - 0.0427f, 0.0f, -0.3491f, 0.7979f, 1.0f, - 0.0574f, 0.0f, -0.3421f, 0.6541f, 1.0f, - 0.0732f, 0.0f, -0.3331f, 0.36f, 1.0f, - 0.0816f, 0.0f, -0.3278f, 0.1f, 1.0f, + -0.0844f, 0.0f, -0.301f, 0.1f, 1.0f, -0.0825f, 0.0f, -0.3034f, 0.2199f, 1.0f, + -0.0751f, 0.0f, -0.3128f, 0.6019f, 1.0f, -0.0677f, 0.0f, -0.3216f, 0.7689f, 1.0f, + -0.06f, 0.0f, -0.3298f, 0.866f, 1.0f, -0.0522f, 0.0f, -0.3372f, 0.9272f, 1.0f, + -0.044f, 0.0f, -0.3437f, 0.9653f, 1.0f, -0.0354f, 0.0f, -0.3491f, 0.9876f, 1.0f, + -0.0264f, 0.0f, -0.3535f, 0.9983f, 1.0f, -0.0168f, 0.0f, -0.3566f, 0.999f, 1.0f, + -0.0065f, 0.0f, -0.3583f, 0.9912f, 1.0f, 0.0045f, 0.0f, -0.3585f, 0.9724f, 1.0f, + 0.0163f, 0.0f, -0.3571f, 0.9385f, 1.0f, 0.029f, 0.0f, -0.354f, 0.8841f, 1.0f, + 0.0427f, 0.0f, -0.3491f, 0.7979f, 1.0f, 0.0574f, 0.0f, -0.3421f, 0.6541f, 1.0f, + 0.0732f, 0.0f, -0.3331f, 0.36f, 1.0f, 0.0816f, 0.0f, -0.3278f, 0.1f, 1.0f, }; static const float data3[64 * GP_PRIM_DATABUF_SIZE] = { - -0.6551f, 0.0f, 0.4448f, 0.1f, 1.0f, - -0.6353f, 0.0f, 0.4689f, 0.2199f, 1.0f, - -0.6211f, 0.0f, 0.4845f, 0.36f, 1.0f, - -0.6033f, 0.0f, 0.5034f, 0.4615f, 1.0f, - -0.5856f, 0.0f, 0.5211f, 0.5396f, 1.0f, - -0.5672f, 0.0f, 0.5387f, 0.6019f, 1.0f, - -0.5485f, 0.0f, 0.5555f, 0.6541f, 1.0f, - -0.5295f, 0.0f, 0.5718f, 0.6981f, 1.0f, - -0.5103f, 0.0f, 0.5875f, 0.7359f, 1.0f, - -0.4909f, 0.0f, 0.6028f, 0.7689f, 1.0f, - -0.4712f, 0.0f, 0.6174f, 0.7979f, 1.0f, - -0.4512f, 0.0f, 0.6313f, 0.8236f, 1.0f, - -0.4307f, 0.0f, 0.6444f, 0.8461f, 1.0f, - -0.4099f, 0.0f, 0.6568f, 0.866f, 1.0f, - -0.3884f, 0.0f, 0.6684f, 0.8841f, 1.0f, - -0.3665f, 0.0f, 0.6793f, 0.9003f, 1.0f, - -0.3439f, 0.0f, 0.6893f, 0.9143f, 1.0f, - -0.3207f, 0.0f, 0.6984f, 0.9272f, 1.0f, - -0.2971f, 0.0f, 0.7069f, 0.9385f, 1.0f, - -0.2731f, 0.0f, 0.7148f, 0.9485f, 1.0f, - -0.249f, 0.0f, 0.7222f, 0.9576f, 1.0f, - -0.2247f, 0.0f, 0.7292f, 0.9653f, 1.0f, - -0.2003f, 0.0f, 0.7356f, 0.9724f, 1.0f, - -0.1759f, 0.0f, 0.7416f, 0.9781f, 1.0f, - -0.1515f, 0.0f, 0.7471f, 0.9834f, 1.0f, - -0.1272f, 0.0f, 0.7518f, 0.9876f, 1.0f, - -0.1028f, 0.0f, 0.7556f, 0.9912f, 1.0f, - -0.0785f, 0.0f, 0.7586f, 0.9942f, 1.0f, - -0.0543f, 0.0f, 0.7607f, 0.9963f, 1.0f, - -0.0302f, 0.0f, 0.7621f, 0.9983f, 1.0f, - -0.0062f, 0.0f, 0.7627f, 0.999f, 1.0f, - 0.0177f, 0.0f, 0.7625f, 0.9997f, 1.0f, - 0.0415f, 0.0f, 0.7616f, 0.9997f, 1.0f, - 0.0652f, 0.0f, 0.7602f, 0.999f, 1.0f, - 0.0887f, 0.0f, 0.7583f, 0.9983f, 1.0f, - 0.1122f, 0.0f, 0.7559f, 0.9963f, 1.0f, - 0.1355f, 0.0f, 0.7529f, 0.9942f, 1.0f, - 0.1585f, 0.0f, 0.7493f, 0.9912f, 1.0f, - 0.1814f, 0.0f, 0.7451f, 0.9876f, 1.0f, - 0.2041f, 0.0f, 0.7404f, 0.9834f, 1.0f, - 0.2266f, 0.0f, 0.7352f, 0.9781f, 1.0f, - 0.2488f, 0.0f, 0.729f, 0.9724f, 1.0f, - 0.2706f, 0.0f, 0.7216f, 0.9653f, 1.0f, - 0.2921f, 0.0f, 0.7131f, 0.9576f, 1.0f, - 0.3135f, 0.0f, 0.7041f, 0.9485f, 1.0f, - 0.3348f, 0.0f, 0.6945f, 0.9385f, 1.0f, - 0.3559f, 0.0f, 0.6845f, 0.9272f, 1.0f, - 0.3769f, 0.0f, 0.6739f, 0.9143f, 1.0f, - 0.3978f, 0.0f, 0.6628f, 0.9003f, 1.0f, - 0.4185f, 0.0f, 0.651f, 0.8841f, 1.0f, - 0.439f, 0.0f, 0.6383f, 0.866f, 1.0f, - 0.4594f, 0.0f, 0.6249f, 0.8461f, 1.0f, - 0.4795f, 0.0f, 0.6106f, 0.8236f, 1.0f, - 0.4995f, 0.0f, 0.5956f, 0.7979f, 1.0f, - 0.5193f, 0.0f, 0.5801f, 0.7689f, 1.0f, - 0.539f, 0.0f, 0.5642f, 0.7359f, 1.0f, - 0.5586f, 0.0f, 0.5479f, 0.6981f, 1.0f, - 0.578f, 0.0f, 0.5312f, 0.6541f, 1.0f, - 0.597f, 0.0f, 0.5141f, 0.6019f, 1.0f, - 0.6153f, 0.0f, 0.4966f, 0.5396f, 1.0f, - 0.6324f, 0.0f, 0.4797f, 0.4615f, 1.0f, - 0.6498f, 0.0f, 0.462f, 0.36f, 1.0f, - 0.6638f, 0.0f, 0.4477f, 0.2199f, 1.0f, - 0.6843f, 0.0f, 0.4263f, 0.1f, 1.0f, + -0.6551f, 0.0f, 0.4448f, 0.1f, 1.0f, -0.6353f, 0.0f, 0.4689f, 0.2199f, 1.0f, + -0.6211f, 0.0f, 0.4845f, 0.36f, 1.0f, -0.6033f, 0.0f, 0.5034f, 0.4615f, 1.0f, + -0.5856f, 0.0f, 0.5211f, 0.5396f, 1.0f, -0.5672f, 0.0f, 0.5387f, 0.6019f, 1.0f, + -0.5485f, 0.0f, 0.5555f, 0.6541f, 1.0f, -0.5295f, 0.0f, 0.5718f, 0.6981f, 1.0f, + -0.5103f, 0.0f, 0.5875f, 0.7359f, 1.0f, -0.4909f, 0.0f, 0.6028f, 0.7689f, 1.0f, + -0.4712f, 0.0f, 0.6174f, 0.7979f, 1.0f, -0.4512f, 0.0f, 0.6313f, 0.8236f, 1.0f, + -0.4307f, 0.0f, 0.6444f, 0.8461f, 1.0f, -0.4099f, 0.0f, 0.6568f, 0.866f, 1.0f, + -0.3884f, 0.0f, 0.6684f, 0.8841f, 1.0f, -0.3665f, 0.0f, 0.6793f, 0.9003f, 1.0f, + -0.3439f, 0.0f, 0.6893f, 0.9143f, 1.0f, -0.3207f, 0.0f, 0.6984f, 0.9272f, 1.0f, + -0.2971f, 0.0f, 0.7069f, 0.9385f, 1.0f, -0.2731f, 0.0f, 0.7148f, 0.9485f, 1.0f, + -0.249f, 0.0f, 0.7222f, 0.9576f, 1.0f, -0.2247f, 0.0f, 0.7292f, 0.9653f, 1.0f, + -0.2003f, 0.0f, 0.7356f, 0.9724f, 1.0f, -0.1759f, 0.0f, 0.7416f, 0.9781f, 1.0f, + -0.1515f, 0.0f, 0.7471f, 0.9834f, 1.0f, -0.1272f, 0.0f, 0.7518f, 0.9876f, 1.0f, + -0.1028f, 0.0f, 0.7556f, 0.9912f, 1.0f, -0.0785f, 0.0f, 0.7586f, 0.9942f, 1.0f, + -0.0543f, 0.0f, 0.7607f, 0.9963f, 1.0f, -0.0302f, 0.0f, 0.7621f, 0.9983f, 1.0f, + -0.0062f, 0.0f, 0.7627f, 0.999f, 1.0f, 0.0177f, 0.0f, 0.7625f, 0.9997f, 1.0f, + 0.0415f, 0.0f, 0.7616f, 0.9997f, 1.0f, 0.0652f, 0.0f, 0.7602f, 0.999f, 1.0f, + 0.0887f, 0.0f, 0.7583f, 0.9983f, 1.0f, 0.1122f, 0.0f, 0.7559f, 0.9963f, 1.0f, + 0.1355f, 0.0f, 0.7529f, 0.9942f, 1.0f, 0.1585f, 0.0f, 0.7493f, 0.9912f, 1.0f, + 0.1814f, 0.0f, 0.7451f, 0.9876f, 1.0f, 0.2041f, 0.0f, 0.7404f, 0.9834f, 1.0f, + 0.2266f, 0.0f, 0.7352f, 0.9781f, 1.0f, 0.2488f, 0.0f, 0.729f, 0.9724f, 1.0f, + 0.2706f, 0.0f, 0.7216f, 0.9653f, 1.0f, 0.2921f, 0.0f, 0.7131f, 0.9576f, 1.0f, + 0.3135f, 0.0f, 0.7041f, 0.9485f, 1.0f, 0.3348f, 0.0f, 0.6945f, 0.9385f, 1.0f, + 0.3559f, 0.0f, 0.6845f, 0.9272f, 1.0f, 0.3769f, 0.0f, 0.6739f, 0.9143f, 1.0f, + 0.3978f, 0.0f, 0.6628f, 0.9003f, 1.0f, 0.4185f, 0.0f, 0.651f, 0.8841f, 1.0f, + 0.439f, 0.0f, 0.6383f, 0.866f, 1.0f, 0.4594f, 0.0f, 0.6249f, 0.8461f, 1.0f, + 0.4795f, 0.0f, 0.6106f, 0.8236f, 1.0f, 0.4995f, 0.0f, 0.5956f, 0.7979f, 1.0f, + 0.5193f, 0.0f, 0.5801f, 0.7689f, 1.0f, 0.539f, 0.0f, 0.5642f, 0.7359f, 1.0f, + 0.5586f, 0.0f, 0.5479f, 0.6981f, 1.0f, 0.578f, 0.0f, 0.5312f, 0.6541f, 1.0f, + 0.597f, 0.0f, 0.5141f, 0.6019f, 1.0f, 0.6153f, 0.0f, 0.4966f, 0.5396f, 1.0f, + 0.6324f, 0.0f, 0.4797f, 0.4615f, 1.0f, 0.6498f, 0.0f, 0.462f, 0.36f, 1.0f, + 0.6638f, 0.0f, 0.4477f, 0.2199f, 1.0f, 0.6843f, 0.0f, 0.4263f, 0.1f, 1.0f, }; static const float data4[33 * GP_PRIM_DATABUF_SIZE] = { - -0.7765f, 0.0f, 0.3255f, 0.1f, 1.0f, - -0.7908f, 0.0f, 0.3335f, 0.2199f, 1.0f, - -0.8048f, 0.0f, 0.3402f, 0.4615f, 1.0f, - -0.8225f, 0.0f, 0.3467f, 0.6019f, 1.0f, - -0.8426f, 0.0f, 0.3529f, 0.6981f, 1.0f, - -0.8651f, 0.0f, 0.3587f, 0.7689f, 1.0f, - -0.8878f, 0.0f, 0.3633f, 0.8236f, 1.0f, - -0.9105f, 0.0f, 0.367f, 0.866f, 1.0f, - -0.9323f, 0.0f, 0.3693f, 0.9003f, 1.0f, - -0.9529f, 0.0f, 0.3701f, 0.9272f, 1.0f, - -0.972f, 0.0f, 0.3695f, 0.9485f, 1.0f, - -0.9896f, 0.0f, 0.3675f, 0.9653f, 1.0f, - -1.0057f, 0.0f, 0.364f, 0.9781f, 1.0f, - -1.0203f, 0.0f, 0.3592f, 0.9876f, 1.0f, - -1.0333f, 0.0f, 0.3528f, 0.9942f, 1.0f, - -1.0447f, 0.0f, 0.3452f, 0.9983f, 1.0f, - -1.0546f, 0.0f, 0.3362f, 0.9997f, 1.0f, - -1.0633f, 0.0f, 0.3261f, 0.999f, 1.0f, - -1.0708f, 0.0f, 0.315f, 0.9963f, 1.0f, - -1.0767f, 0.0f, 0.3027f, 0.9912f, 1.0f, - -1.0818f, 0.0f, 0.2894f, 0.9834f, 1.0f, - -1.0861f, 0.0f, 0.2752f, 0.9724f, 1.0f, - -1.0897f, 0.0f, 0.2604f, 0.9576f, 1.0f, - -1.0922f, 0.0f, 0.2446f, 0.9385f, 1.0f, - -1.0938f, 0.0f, 0.2277f, 0.9143f, 1.0f, - -1.0944f, 0.0f, 0.2098f, 0.8841f, 1.0f, - -1.0939f, 0.0f, 0.191f, 0.8461f, 1.0f, - -1.0924f, 0.0f, 0.1714f, 0.7979f, 1.0f, - -1.0897f, 0.0f, 0.1511f, 0.7359f, 1.0f, - -1.0855f, 0.0f, 0.1303f, 0.6541f, 1.0f, - -1.0798f, 0.0f, 0.1095f, 0.5396f, 1.0f, - -1.0723f, 0.0f, 0.089f, 0.36f, 1.0f, - -1.0642f, 0.0f, 0.0702f, 0.1f, 1.0f, + -0.7765f, 0.0f, 0.3255f, 0.1f, 1.0f, -0.7908f, 0.0f, 0.3335f, 0.2199f, 1.0f, + -0.8048f, 0.0f, 0.3402f, 0.4615f, 1.0f, -0.8225f, 0.0f, 0.3467f, 0.6019f, 1.0f, + -0.8426f, 0.0f, 0.3529f, 0.6981f, 1.0f, -0.8651f, 0.0f, 0.3587f, 0.7689f, 1.0f, + -0.8878f, 0.0f, 0.3633f, 0.8236f, 1.0f, -0.9105f, 0.0f, 0.367f, 0.866f, 1.0f, + -0.9323f, 0.0f, 0.3693f, 0.9003f, 1.0f, -0.9529f, 0.0f, 0.3701f, 0.9272f, 1.0f, + -0.972f, 0.0f, 0.3695f, 0.9485f, 1.0f, -0.9896f, 0.0f, 0.3675f, 0.9653f, 1.0f, + -1.0057f, 0.0f, 0.364f, 0.9781f, 1.0f, -1.0203f, 0.0f, 0.3592f, 0.9876f, 1.0f, + -1.0333f, 0.0f, 0.3528f, 0.9942f, 1.0f, -1.0447f, 0.0f, 0.3452f, 0.9983f, 1.0f, + -1.0546f, 0.0f, 0.3362f, 0.9997f, 1.0f, -1.0633f, 0.0f, 0.3261f, 0.999f, 1.0f, + -1.0708f, 0.0f, 0.315f, 0.9963f, 1.0f, -1.0767f, 0.0f, 0.3027f, 0.9912f, 1.0f, + -1.0818f, 0.0f, 0.2894f, 0.9834f, 1.0f, -1.0861f, 0.0f, 0.2752f, 0.9724f, 1.0f, + -1.0897f, 0.0f, 0.2604f, 0.9576f, 1.0f, -1.0922f, 0.0f, 0.2446f, 0.9385f, 1.0f, + -1.0938f, 0.0f, 0.2277f, 0.9143f, 1.0f, -1.0944f, 0.0f, 0.2098f, 0.8841f, 1.0f, + -1.0939f, 0.0f, 0.191f, 0.8461f, 1.0f, -1.0924f, 0.0f, 0.1714f, 0.7979f, 1.0f, + -1.0897f, 0.0f, 0.1511f, 0.7359f, 1.0f, -1.0855f, 0.0f, 0.1303f, 0.6541f, 1.0f, + -1.0798f, 0.0f, 0.1095f, 0.5396f, 1.0f, -1.0723f, 0.0f, 0.089f, 0.36f, 1.0f, + -1.0642f, 0.0f, 0.0702f, 0.1f, 1.0f, }; static const float data5[64 * GP_PRIM_DATABUF_SIZE] = { - 0.8135f, 0.0f, 0.3341f, 0.1f, 1.0f, - 0.8191f, 0.0f, 0.3376f, 0.2199f, 1.0f, - 0.8246f, 0.0f, 0.3408f, 0.36f, 1.0f, - 0.8304f, 0.0f, 0.3438f, 0.4615f, 1.0f, - 0.8368f, 0.0f, 0.3465f, 0.5396f, 1.0f, - 0.843f, 0.0f, 0.3491f, 0.6019f, 1.0f, - 0.8494f, 0.0f, 0.3515f, 0.6541f, 1.0f, - 0.8558f, 0.0f, 0.3536f, 0.6981f, 1.0f, - 0.8623f, 0.0f, 0.3557f, 0.7359f, 1.0f, - 0.8688f, 0.0f, 0.3575f, 0.7689f, 1.0f, - 0.8752f, 0.0f, 0.3593f, 0.7979f, 1.0f, - 0.8813f, 0.0f, 0.3609f, 0.8236f, 1.0f, - 0.8872f, 0.0f, 0.3625f, 0.8461f, 1.0f, - 0.8929f, 0.0f, 0.364f, 0.866f, 1.0f, - 0.8984f, 0.0f, 0.3653f, 0.8841f, 1.0f, - 0.9039f, 0.0f, 0.3665f, 0.9003f, 1.0f, - 0.9093f, 0.0f, 0.3675f, 0.9143f, 1.0f, - 0.9146f, 0.0f, 0.3684f, 0.9272f, 1.0f, - 0.9199f, 0.0f, 0.3692f, 0.9385f, 1.0f, - 0.9253f, 0.0f, 0.3698f, 0.9485f, 1.0f, - 0.9305f, 0.0f, 0.3703f, 0.9576f, 1.0f, - 0.9358f, 0.0f, 0.3706f, 0.9653f, 1.0f, - 0.941f, 0.0f, 0.3709f, 0.9724f, 1.0f, - 0.9462f, 0.0f, 0.371f, 0.9781f, 1.0f, - 0.9514f, 0.0f, 0.371f, 0.9834f, 1.0f, - 0.9566f, 0.0f, 0.3708f, 0.9876f, 1.0f, - 0.9617f, 0.0f, 0.3706f, 0.9912f, 1.0f, - 0.9668f, 0.0f, 0.3702f, 0.9942f, 1.0f, - 0.9718f, 0.0f, 0.3697f, 0.9963f, 1.0f, - 0.9768f, 0.0f, 0.3692f, 0.9983f, 1.0f, - 0.9818f, 0.0f, 0.3685f, 0.999f, 1.0f, - 0.9867f, 0.0f, 0.3677f, 0.9997f, 1.0f, - 0.9916f, 0.0f, 0.3667f, 0.9997f, 1.0f, - 0.9964f, 0.0f, 0.3657f, 0.999f, 1.0f, - 1.0012f, 0.0f, 0.3646f, 0.9983f, 1.0f, - 1.006f, 0.0f, 0.3634f, 0.9963f, 1.0f, - 1.0107f, 0.0f, 0.3621f, 0.9942f, 1.0f, - 1.0154f, 0.0f, 0.3607f, 0.9912f, 1.0f, - 1.02f, 0.0f, 0.3593f, 0.9876f, 1.0f, - 1.0245f, 0.0f, 0.3577f, 0.9834f, 1.0f, - 1.029f, 0.0f, 0.3561f, 0.9781f, 1.0f, - 1.0335f, 0.0f, 0.3543f, 0.9724f, 1.0f, - 1.0379f, 0.0f, 0.3525f, 0.9653f, 1.0f, - 1.0422f, 0.0f, 0.3507f, 0.9576f, 1.0f, - 1.0465f, 0.0f, 0.3487f, 0.9485f, 1.0f, - 1.0507f, 0.0f, 0.3468f, 0.9385f, 1.0f, - 1.0549f, 0.0f, 0.3447f, 0.9272f, 1.0f, - 1.0591f, 0.0f, 0.3427f, 0.9143f, 1.0f, - 1.0633f, 0.0f, 0.3404f, 0.9003f, 1.0f, - 1.0675f, 0.0f, 0.338f, 0.8841f, 1.0f, - 1.0717f, 0.0f, 0.3351f, 0.866f, 1.0f, - 1.0761f, 0.0f, 0.3318f, 0.8461f, 1.0f, - 1.0805f, 0.0f, 0.3279f, 0.8236f, 1.0f, - 1.0849f, 0.0f, 0.3235f, 0.7979f, 1.0f, - 1.0893f, 0.0f, 0.3186f, 0.7689f, 1.0f, - 1.0936f, 0.0f, 0.3134f, 0.7359f, 1.0f, - 1.0979f, 0.0f, 0.3076f, 0.6981f, 1.0f, - 1.102f, 0.0f, 0.3017f, 0.6541f, 1.0f, - 1.106f, 0.0f, 0.2953f, 0.6019f, 1.0f, - 1.1097f, 0.0f, 0.289f, 0.5396f, 1.0f, - 1.1132f, 0.0f, 0.2826f, 0.4615f, 1.0f, - 1.1164f, 0.0f, 0.2766f, 0.36f, 1.0f, - 1.1193f, 0.0f, 0.2708f, 0.2199f, 1.0f, - 1.1221f, 0.0f, 0.2652f, 0.1f, 1.0f, + 0.8135f, 0.0f, 0.3341f, 0.1f, 1.0f, 0.8191f, 0.0f, 0.3376f, 0.2199f, 1.0f, + 0.8246f, 0.0f, 0.3408f, 0.36f, 1.0f, 0.8304f, 0.0f, 0.3438f, 0.4615f, 1.0f, + 0.8368f, 0.0f, 0.3465f, 0.5396f, 1.0f, 0.843f, 0.0f, 0.3491f, 0.6019f, 1.0f, + 0.8494f, 0.0f, 0.3515f, 0.6541f, 1.0f, 0.8558f, 0.0f, 0.3536f, 0.6981f, 1.0f, + 0.8623f, 0.0f, 0.3557f, 0.7359f, 1.0f, 0.8688f, 0.0f, 0.3575f, 0.7689f, 1.0f, + 0.8752f, 0.0f, 0.3593f, 0.7979f, 1.0f, 0.8813f, 0.0f, 0.3609f, 0.8236f, 1.0f, + 0.8872f, 0.0f, 0.3625f, 0.8461f, 1.0f, 0.8929f, 0.0f, 0.364f, 0.866f, 1.0f, + 0.8984f, 0.0f, 0.3653f, 0.8841f, 1.0f, 0.9039f, 0.0f, 0.3665f, 0.9003f, 1.0f, + 0.9093f, 0.0f, 0.3675f, 0.9143f, 1.0f, 0.9146f, 0.0f, 0.3684f, 0.9272f, 1.0f, + 0.9199f, 0.0f, 0.3692f, 0.9385f, 1.0f, 0.9253f, 0.0f, 0.3698f, 0.9485f, 1.0f, + 0.9305f, 0.0f, 0.3703f, 0.9576f, 1.0f, 0.9358f, 0.0f, 0.3706f, 0.9653f, 1.0f, + 0.941f, 0.0f, 0.3709f, 0.9724f, 1.0f, 0.9462f, 0.0f, 0.371f, 0.9781f, 1.0f, + 0.9514f, 0.0f, 0.371f, 0.9834f, 1.0f, 0.9566f, 0.0f, 0.3708f, 0.9876f, 1.0f, + 0.9617f, 0.0f, 0.3706f, 0.9912f, 1.0f, 0.9668f, 0.0f, 0.3702f, 0.9942f, 1.0f, + 0.9718f, 0.0f, 0.3697f, 0.9963f, 1.0f, 0.9768f, 0.0f, 0.3692f, 0.9983f, 1.0f, + 0.9818f, 0.0f, 0.3685f, 0.999f, 1.0f, 0.9867f, 0.0f, 0.3677f, 0.9997f, 1.0f, + 0.9916f, 0.0f, 0.3667f, 0.9997f, 1.0f, 0.9964f, 0.0f, 0.3657f, 0.999f, 1.0f, + 1.0012f, 0.0f, 0.3646f, 0.9983f, 1.0f, 1.006f, 0.0f, 0.3634f, 0.9963f, 1.0f, + 1.0107f, 0.0f, 0.3621f, 0.9942f, 1.0f, 1.0154f, 0.0f, 0.3607f, 0.9912f, 1.0f, + 1.02f, 0.0f, 0.3593f, 0.9876f, 1.0f, 1.0245f, 0.0f, 0.3577f, 0.9834f, 1.0f, + 1.029f, 0.0f, 0.3561f, 0.9781f, 1.0f, 1.0335f, 0.0f, 0.3543f, 0.9724f, 1.0f, + 1.0379f, 0.0f, 0.3525f, 0.9653f, 1.0f, 1.0422f, 0.0f, 0.3507f, 0.9576f, 1.0f, + 1.0465f, 0.0f, 0.3487f, 0.9485f, 1.0f, 1.0507f, 0.0f, 0.3468f, 0.9385f, 1.0f, + 1.0549f, 0.0f, 0.3447f, 0.9272f, 1.0f, 1.0591f, 0.0f, 0.3427f, 0.9143f, 1.0f, + 1.0633f, 0.0f, 0.3404f, 0.9003f, 1.0f, 1.0675f, 0.0f, 0.338f, 0.8841f, 1.0f, + 1.0717f, 0.0f, 0.3351f, 0.866f, 1.0f, 1.0761f, 0.0f, 0.3318f, 0.8461f, 1.0f, + 1.0805f, 0.0f, 0.3279f, 0.8236f, 1.0f, 1.0849f, 0.0f, 0.3235f, 0.7979f, 1.0f, + 1.0893f, 0.0f, 0.3186f, 0.7689f, 1.0f, 1.0936f, 0.0f, 0.3134f, 0.7359f, 1.0f, + 1.0979f, 0.0f, 0.3076f, 0.6981f, 1.0f, 1.102f, 0.0f, 0.3017f, 0.6541f, 1.0f, + 1.106f, 0.0f, 0.2953f, 0.6019f, 1.0f, 1.1097f, 0.0f, 0.289f, 0.5396f, 1.0f, + 1.1132f, 0.0f, 0.2826f, 0.4615f, 1.0f, 1.1164f, 0.0f, 0.2766f, 0.36f, 1.0f, + 1.1193f, 0.0f, 0.2708f, 0.2199f, 1.0f, 1.1221f, 0.0f, 0.2652f, 0.1f, 1.0f, }; static const float data6[33 * GP_PRIM_DATABUF_SIZE] = { - -0.2677f, 0.0f, -0.3496f, 0.1f, 1.0f, - -0.2658f, 0.0f, -0.3919f, 0.2199f, 1.0f, - -0.2657f, 0.0f, -0.4295f, 0.4615f, 1.0f, - -0.2691f, 0.0f, -0.4621f, 0.6019f, 1.0f, - -0.275f, 0.0f, -0.4949f, 0.6981f, 1.0f, - -0.2828f, 0.0f, -0.527f, 0.7689f, 1.0f, - -0.2911f, 0.0f, -0.5578f, 0.8236f, 1.0f, - -0.2971f, 0.0f, -0.5884f, 0.866f, 1.0f, - -0.303f, 0.0f, -0.6181f, 0.9003f, 1.0f, - -0.3089f, 0.0f, -0.6467f, 0.9272f, 1.0f, - -0.3148f, 0.0f, -0.6738f, 0.9485f, 1.0f, - -0.3196f, 0.0f, -0.7f, 0.9653f, 1.0f, - -0.323f, 0.0f, -0.7253f, 0.9781f, 1.0f, - -0.3234f, 0.0f, -0.7496f, 0.9876f, 1.0f, - -0.3219f, 0.0f, -0.7728f, 0.9942f, 1.0f, - -0.3184f, 0.0f, -0.7949f, 0.9983f, 1.0f, - -0.3103f, 0.0f, -0.8138f, 0.9997f, 1.0f, - -0.3004f, 0.0f, -0.8307f, 0.999f, 1.0f, - -0.2892f, 0.0f, -0.8464f, 0.9963f, 1.0f, - -0.2766f, 0.0f, -0.8611f, 0.9912f, 1.0f, - -0.2618f, 0.0f, -0.8735f, 0.9834f, 1.0f, - -0.2469f, 0.0f, -0.8853f, 0.9724f, 1.0f, - -0.2315f, 0.0f, -0.8964f, 0.9576f, 1.0f, - -0.2158f, 0.0f, -0.9068f, 0.9385f, 1.0f, - -0.1997f, 0.0f, -0.9167f, 0.9143f, 1.0f, - -0.1833f, 0.0f, -0.9262f, 0.8841f, 1.0f, - -0.1663f, 0.0f, -0.9355f, 0.8461f, 1.0f, - -0.1481f, 0.0f, -0.9445f, 0.7979f, 1.0f, - -0.1282f, 0.0f, -0.9534f, 0.7359f, 1.0f, - -0.1077f, 0.0f, -0.9625f, 0.6541f, 1.0f, - -0.0854f, 0.0f, -0.9718f, 0.5396f, 1.0f, - -0.0616f, 0.0f, -0.9813f, 0.36f, 1.0f, - -0.0248f, 0.0f, -0.992f, 0.1f, 1.0f, + -0.2677f, 0.0f, -0.3496f, 0.1f, 1.0f, -0.2658f, 0.0f, -0.3919f, 0.2199f, 1.0f, + -0.2657f, 0.0f, -0.4295f, 0.4615f, 1.0f, -0.2691f, 0.0f, -0.4621f, 0.6019f, 1.0f, + -0.275f, 0.0f, -0.4949f, 0.6981f, 1.0f, -0.2828f, 0.0f, -0.527f, 0.7689f, 1.0f, + -0.2911f, 0.0f, -0.5578f, 0.8236f, 1.0f, -0.2971f, 0.0f, -0.5884f, 0.866f, 1.0f, + -0.303f, 0.0f, -0.6181f, 0.9003f, 1.0f, -0.3089f, 0.0f, -0.6467f, 0.9272f, 1.0f, + -0.3148f, 0.0f, -0.6738f, 0.9485f, 1.0f, -0.3196f, 0.0f, -0.7f, 0.9653f, 1.0f, + -0.323f, 0.0f, -0.7253f, 0.9781f, 1.0f, -0.3234f, 0.0f, -0.7496f, 0.9876f, 1.0f, + -0.3219f, 0.0f, -0.7728f, 0.9942f, 1.0f, -0.3184f, 0.0f, -0.7949f, 0.9983f, 1.0f, + -0.3103f, 0.0f, -0.8138f, 0.9997f, 1.0f, -0.3004f, 0.0f, -0.8307f, 0.999f, 1.0f, + -0.2892f, 0.0f, -0.8464f, 0.9963f, 1.0f, -0.2766f, 0.0f, -0.8611f, 0.9912f, 1.0f, + -0.2618f, 0.0f, -0.8735f, 0.9834f, 1.0f, -0.2469f, 0.0f, -0.8853f, 0.9724f, 1.0f, + -0.2315f, 0.0f, -0.8964f, 0.9576f, 1.0f, -0.2158f, 0.0f, -0.9068f, 0.9385f, 1.0f, + -0.1997f, 0.0f, -0.9167f, 0.9143f, 1.0f, -0.1833f, 0.0f, -0.9262f, 0.8841f, 1.0f, + -0.1663f, 0.0f, -0.9355f, 0.8461f, 1.0f, -0.1481f, 0.0f, -0.9445f, 0.7979f, 1.0f, + -0.1282f, 0.0f, -0.9534f, 0.7359f, 1.0f, -0.1077f, 0.0f, -0.9625f, 0.6541f, 1.0f, + -0.0854f, 0.0f, -0.9718f, 0.5396f, 1.0f, -0.0616f, 0.0f, -0.9813f, 0.36f, 1.0f, + -0.0248f, 0.0f, -0.992f, 0.1f, 1.0f, }; static const float data7[18 * GP_PRIM_DATABUF_SIZE] = { - -0.0618f, 0.0f, -0.1922f, 0.1f, 1.0f, - -0.0703f, 0.0f, -0.2021f, 0.2199f, 1.0f, - -0.0758f, 0.0f, -0.2103f, 0.6019f, 1.0f, - -0.0803f, 0.0f, -0.2206f, 0.7689f, 1.0f, - -0.083f, 0.0f, -0.2307f, 0.866f, 1.0f, - -0.0851f, 0.0f, -0.2405f, 0.9272f, 1.0f, - -0.0865f, 0.0f, -0.2499f, 0.9653f, 1.0f, - -0.0872f, 0.0f, -0.2588f, 0.9876f, 1.0f, - -0.0869f, 0.0f, -0.2673f, 0.9983f, 1.0f, - -0.0858f, 0.0f, -0.2754f, 0.999f, 1.0f, - -0.0831f, 0.0f, -0.2829f, 0.9912f, 1.0f, - -0.0791f, 0.0f, -0.2898f, 0.9724f, 1.0f, - -0.074f, 0.0f, -0.2966f, 0.9385f, 1.0f, - -0.0674f, 0.0f, -0.303f, 0.8841f, 1.0f, - -0.0591f, 0.0f, -0.3084f, 0.7979f, 1.0f, - -0.0465f, 0.0f, -0.3134f, 0.6541f, 1.0f, - -0.0331f, 0.0f, -0.3165f, 0.36f, 1.0f, - -0.015f, 0.0f, -0.318f, 0.1f, 1.0f, + -0.0618f, 0.0f, -0.1922f, 0.1f, 1.0f, -0.0703f, 0.0f, -0.2021f, 0.2199f, 1.0f, + -0.0758f, 0.0f, -0.2103f, 0.6019f, 1.0f, -0.0803f, 0.0f, -0.2206f, 0.7689f, 1.0f, + -0.083f, 0.0f, -0.2307f, 0.866f, 1.0f, -0.0851f, 0.0f, -0.2405f, 0.9272f, 1.0f, + -0.0865f, 0.0f, -0.2499f, 0.9653f, 1.0f, -0.0872f, 0.0f, -0.2588f, 0.9876f, 1.0f, + -0.0869f, 0.0f, -0.2673f, 0.9983f, 1.0f, -0.0858f, 0.0f, -0.2754f, 0.999f, 1.0f, + -0.0831f, 0.0f, -0.2829f, 0.9912f, 1.0f, -0.0791f, 0.0f, -0.2898f, 0.9724f, 1.0f, + -0.074f, 0.0f, -0.2966f, 0.9385f, 1.0f, -0.0674f, 0.0f, -0.303f, 0.8841f, 1.0f, + -0.0591f, 0.0f, -0.3084f, 0.7979f, 1.0f, -0.0465f, 0.0f, -0.3134f, 0.6541f, 1.0f, + -0.0331f, 0.0f, -0.3165f, 0.36f, 1.0f, -0.015f, 0.0f, -0.318f, 0.1f, 1.0f, }; static const float data8[49 * GP_PRIM_DATABUF_SIZE] = { - 0.5311f, 0.0f, 0.1661f, 1.0f, 1.0f, - 0.5307f, 0.0f, 0.1794f, 1.0f, 1.0f, - 0.5277f, 0.0f, 0.2057f, 1.0f, 1.0f, - 0.5218f, 0.0f, 0.2314f, 1.0f, 1.0f, - 0.513f, 0.0f, 0.256f, 1.0f, 1.0f, - 0.5014f, 0.0f, 0.279f, 1.0f, 1.0f, - 0.4874f, 0.0f, 0.3001f, 1.0f, 1.0f, - 0.4711f, 0.0f, 0.3189f, 1.0f, 1.0f, - 0.4529f, 0.0f, 0.3351f, 1.0f, 1.0f, - 0.4329f, 0.0f, 0.3483f, 1.0f, 1.0f, - 0.4117f, 0.0f, 0.3585f, 1.0f, 1.0f, - 0.3894f, 0.0f, 0.3654f, 1.0f, 1.0f, - 0.3666f, 0.0f, 0.3689f, 1.0f, 1.0f, - 0.3435f, 0.0f, 0.3689f, 1.0f, 1.0f, - 0.3207f, 0.0f, 0.3654f, 1.0f, 1.0f, - 0.2985f, 0.0f, 0.3585f, 1.0f, 1.0f, - 0.2772f, 0.0f, 0.3483f, 1.0f, 1.0f, - 0.2573f, 0.0f, 0.3351f, 1.0f, 1.0f, - 0.239f, 0.0f, 0.3189f, 1.0f, 1.0f, - 0.2227f, 0.0f, 0.3001f, 1.0f, 1.0f, - 0.2087f, 0.0f, 0.279f, 1.0f, 1.0f, - 0.1972f, 0.0f, 0.256f, 1.0f, 1.0f, - 0.1884f, 0.0f, 0.2314f, 1.0f, 1.0f, - 0.1824f, 0.0f, 0.2057f, 1.0f, 1.0f, - 0.1794f, 0.0f, 0.1794f, 1.0f, 1.0f, - 0.1794f, 0.0f, 0.1528f, 1.0f, 1.0f, - 0.1824f, 0.0f, 0.1264f, 1.0f, 1.0f, - 0.1884f, 0.0f, 0.1007f, 1.0f, 1.0f, - 0.1972f, 0.0f, 0.0762f, 1.0f, 1.0f, - 0.2087f, 0.0f, 0.0531f, 1.0f, 1.0f, - 0.2227f, 0.0f, 0.032f, 1.0f, 1.0f, - 0.239f, 0.0f, 0.0132f, 1.0f, 1.0f, - 0.2573f, 0.0f, -0.0029f, 1.0f, 1.0f, - 0.2772f, 0.0f, -0.0162f, 1.0f, 1.0f, - 0.2985f, 0.0f, -0.0264f, 1.0f, 1.0f, - 0.3207f, 0.0f, -0.0333f, 1.0f, 1.0f, - 0.3435f, 0.0f, -0.0368f, 1.0f, 1.0f, - 0.3666f, 0.0f, -0.0368f, 1.0f, 1.0f, - 0.3894f, 0.0f, -0.0333f, 1.0f, 1.0f, - 0.4117f, 0.0f, -0.0264f, 1.0f, 1.0f, - 0.4329f, 0.0f, -0.0162f, 1.0f, 1.0f, - 0.4529f, 0.0f, -0.0029f, 1.0f, 1.0f, - 0.4711f, 0.0f, 0.0132f, 1.0f, 1.0f, - 0.4874f, 0.0f, 0.032f, 1.0f, 1.0f, - 0.5014f, 0.0f, 0.0531f, 1.0f, 1.0f, - 0.513f, 0.0f, 0.0762f, 1.0f, 1.0f, - 0.5218f, 0.0f, 0.1007f, 1.0f, 1.0f, - 0.5277f, 0.0f, 0.1264f, 1.0f, 1.0f, - 0.5307f, 0.0f, 0.1528f, 1.0f, 1.0f, + 0.5311f, 0.0f, 0.1661f, 1.0f, 1.0f, 0.5307f, 0.0f, 0.1794f, 1.0f, 1.0f, + 0.5277f, 0.0f, 0.2057f, 1.0f, 1.0f, 0.5218f, 0.0f, 0.2314f, 1.0f, 1.0f, + 0.513f, 0.0f, 0.256f, 1.0f, 1.0f, 0.5014f, 0.0f, 0.279f, 1.0f, 1.0f, + 0.4874f, 0.0f, 0.3001f, 1.0f, 1.0f, 0.4711f, 0.0f, 0.3189f, 1.0f, 1.0f, + 0.4529f, 0.0f, 0.3351f, 1.0f, 1.0f, 0.4329f, 0.0f, 0.3483f, 1.0f, 1.0f, + 0.4117f, 0.0f, 0.3585f, 1.0f, 1.0f, 0.3894f, 0.0f, 0.3654f, 1.0f, 1.0f, + 0.3666f, 0.0f, 0.3689f, 1.0f, 1.0f, 0.3435f, 0.0f, 0.3689f, 1.0f, 1.0f, + 0.3207f, 0.0f, 0.3654f, 1.0f, 1.0f, 0.2985f, 0.0f, 0.3585f, 1.0f, 1.0f, + 0.2772f, 0.0f, 0.3483f, 1.0f, 1.0f, 0.2573f, 0.0f, 0.3351f, 1.0f, 1.0f, + 0.239f, 0.0f, 0.3189f, 1.0f, 1.0f, 0.2227f, 0.0f, 0.3001f, 1.0f, 1.0f, + 0.2087f, 0.0f, 0.279f, 1.0f, 1.0f, 0.1972f, 0.0f, 0.256f, 1.0f, 1.0f, + 0.1884f, 0.0f, 0.2314f, 1.0f, 1.0f, 0.1824f, 0.0f, 0.2057f, 1.0f, 1.0f, + 0.1794f, 0.0f, 0.1794f, 1.0f, 1.0f, 0.1794f, 0.0f, 0.1528f, 1.0f, 1.0f, + 0.1824f, 0.0f, 0.1264f, 1.0f, 1.0f, 0.1884f, 0.0f, 0.1007f, 1.0f, 1.0f, + 0.1972f, 0.0f, 0.0762f, 1.0f, 1.0f, 0.2087f, 0.0f, 0.0531f, 1.0f, 1.0f, + 0.2227f, 0.0f, 0.032f, 1.0f, 1.0f, 0.239f, 0.0f, 0.0132f, 1.0f, 1.0f, + 0.2573f, 0.0f, -0.0029f, 1.0f, 1.0f, 0.2772f, 0.0f, -0.0162f, 1.0f, 1.0f, + 0.2985f, 0.0f, -0.0264f, 1.0f, 1.0f, 0.3207f, 0.0f, -0.0333f, 1.0f, 1.0f, + 0.3435f, 0.0f, -0.0368f, 1.0f, 1.0f, 0.3666f, 0.0f, -0.0368f, 1.0f, 1.0f, + 0.3894f, 0.0f, -0.0333f, 1.0f, 1.0f, 0.4117f, 0.0f, -0.0264f, 1.0f, 1.0f, + 0.4329f, 0.0f, -0.0162f, 1.0f, 1.0f, 0.4529f, 0.0f, -0.0029f, 1.0f, 1.0f, + 0.4711f, 0.0f, 0.0132f, 1.0f, 1.0f, 0.4874f, 0.0f, 0.032f, 1.0f, 1.0f, + 0.5014f, 0.0f, 0.0531f, 1.0f, 1.0f, 0.513f, 0.0f, 0.0762f, 1.0f, 1.0f, + 0.5218f, 0.0f, 0.1007f, 1.0f, 1.0f, 0.5277f, 0.0f, 0.1264f, 1.0f, 1.0f, + 0.5307f, 0.0f, 0.1528f, 1.0f, 1.0f, }; static const float data9[33 * GP_PRIM_DATABUF_SIZE] = { - -0.5271f, 0.0f, 0.1742f, 0.1f, 1.0f, - -0.527f, 0.0f, 0.1621f, 0.2199f, 1.0f, - -0.5253f, 0.0f, 0.1386f, 0.4615f, 1.0f, - -0.5217f, 0.0f, 0.116f, 0.6019f, 1.0f, - -0.5164f, 0.0f, 0.0945f, 0.6981f, 1.0f, - -0.5094f, 0.0f, 0.0741f, 0.7689f, 1.0f, - -0.5009f, 0.0f, 0.0548f, 0.8236f, 1.0f, - -0.491f, 0.0f, 0.0368f, 0.866f, 1.0f, - -0.4799f, 0.0f, 0.02f, 0.9003f, 1.0f, - -0.4676f, 0.0f, 0.0046f, 0.9272f, 1.0f, - -0.4544f, 0.0f, -0.0094f, 0.9485f, 1.0f, - -0.4402f, 0.0f, -0.0219f, 0.9653f, 1.0f, - -0.4254f, 0.0f, -0.033f, 0.9781f, 1.0f, - -0.4099f, 0.0f, -0.0424f, 0.9876f, 1.0f, - -0.394f, 0.0f, -0.0501f, 0.9942f, 1.0f, - -0.3777f, 0.0f, -0.0562f, 0.9983f, 1.0f, - -0.3612f, 0.0f, -0.0605f, 0.9997f, 1.0f, - -0.3447f, 0.0f, -0.0629f, 0.999f, 1.0f, - -0.3281f, 0.0f, -0.0634f, 0.9963f, 1.0f, - -0.3118f, 0.0f, -0.062f, 0.9912f, 1.0f, - -0.2957f, 0.0f, -0.0585f, 0.9834f, 1.0f, - -0.2801f, 0.0f, -0.0529f, 0.9724f, 1.0f, - -0.2651f, 0.0f, -0.0452f, 0.9576f, 1.0f, - -0.2507f, 0.0f, -0.0352f, 0.9385f, 1.0f, - -0.2372f, 0.0f, -0.023f, 0.9143f, 1.0f, - -0.2246f, 0.0f, -0.0085f, 0.8841f, 1.0f, - -0.2131f, 0.0f, 0.0085f, 0.8461f, 1.0f, - -0.2028f, 0.0f, 0.0279f, 0.7979f, 1.0f, - -0.1939f, 0.0f, 0.0499f, 0.7359f, 1.0f, - -0.1864f, 0.0f, 0.0745f, 0.6541f, 1.0f, - -0.1806f, 0.0f, 0.1017f, 0.5396f, 1.0f, - -0.1765f, 0.0f, 0.1316f, 0.36f, 1.0f, - -0.1742f, 0.0f, 0.1643f, 0.1f, 1.0f, + -0.5271f, 0.0f, 0.1742f, 0.1f, 1.0f, -0.527f, 0.0f, 0.1621f, 0.2199f, 1.0f, + -0.5253f, 0.0f, 0.1386f, 0.4615f, 1.0f, -0.5217f, 0.0f, 0.116f, 0.6019f, 1.0f, + -0.5164f, 0.0f, 0.0945f, 0.6981f, 1.0f, -0.5094f, 0.0f, 0.0741f, 0.7689f, 1.0f, + -0.5009f, 0.0f, 0.0548f, 0.8236f, 1.0f, -0.491f, 0.0f, 0.0368f, 0.866f, 1.0f, + -0.4799f, 0.0f, 0.02f, 0.9003f, 1.0f, -0.4676f, 0.0f, 0.0046f, 0.9272f, 1.0f, + -0.4544f, 0.0f, -0.0094f, 0.9485f, 1.0f, -0.4402f, 0.0f, -0.0219f, 0.9653f, 1.0f, + -0.4254f, 0.0f, -0.033f, 0.9781f, 1.0f, -0.4099f, 0.0f, -0.0424f, 0.9876f, 1.0f, + -0.394f, 0.0f, -0.0501f, 0.9942f, 1.0f, -0.3777f, 0.0f, -0.0562f, 0.9983f, 1.0f, + -0.3612f, 0.0f, -0.0605f, 0.9997f, 1.0f, -0.3447f, 0.0f, -0.0629f, 0.999f, 1.0f, + -0.3281f, 0.0f, -0.0634f, 0.9963f, 1.0f, -0.3118f, 0.0f, -0.062f, 0.9912f, 1.0f, + -0.2957f, 0.0f, -0.0585f, 0.9834f, 1.0f, -0.2801f, 0.0f, -0.0529f, 0.9724f, 1.0f, + -0.2651f, 0.0f, -0.0452f, 0.9576f, 1.0f, -0.2507f, 0.0f, -0.0352f, 0.9385f, 1.0f, + -0.2372f, 0.0f, -0.023f, 0.9143f, 1.0f, -0.2246f, 0.0f, -0.0085f, 0.8841f, 1.0f, + -0.2131f, 0.0f, 0.0085f, 0.8461f, 1.0f, -0.2028f, 0.0f, 0.0279f, 0.7979f, 1.0f, + -0.1939f, 0.0f, 0.0499f, 0.7359f, 1.0f, -0.1864f, 0.0f, 0.0745f, 0.6541f, 1.0f, + -0.1806f, 0.0f, 0.1017f, 0.5396f, 1.0f, -0.1765f, 0.0f, 0.1316f, 0.36f, 1.0f, + -0.1742f, 0.0f, 0.1643f, 0.1f, 1.0f, }; static const float data10[49 * GP_PRIM_DATABUF_SIZE] = { - -0.174f, 0.0f, 0.1661f, 1.0f, 1.0f, - -0.1744f, 0.0f, 0.1794f, 1.0f, 1.0f, - -0.1774f, 0.0f, 0.2057f, 1.0f, 1.0f, - -0.1833f, 0.0f, 0.2314f, 1.0f, 1.0f, - -0.1922f, 0.0f, 0.256f, 1.0f, 1.0f, - -0.2037f, 0.0f, 0.279f, 1.0f, 1.0f, - -0.2177f, 0.0f, 0.3001f, 1.0f, 1.0f, - -0.234f, 0.0f, 0.3189f, 1.0f, 1.0f, - -0.2522f, 0.0f, 0.3351f, 1.0f, 1.0f, - -0.2722f, 0.0f, 0.3483f, 1.0f, 1.0f, - -0.2935f, 0.0f, 0.3585f, 1.0f, 1.0f, - -0.3157f, 0.0f, 0.3654f, 1.0f, 1.0f, - -0.3385f, 0.0f, 0.3689f, 1.0f, 1.0f, - -0.3616f, 0.0f, 0.3689f, 1.0f, 1.0f, - -0.3844f, 0.0f, 0.3654f, 1.0f, 1.0f, - -0.4066f, 0.0f, 0.3585f, 1.0f, 1.0f, - -0.4279f, 0.0f, 0.3483f, 1.0f, 1.0f, - -0.4479f, 0.0f, 0.3351f, 1.0f, 1.0f, - -0.4661f, 0.0f, 0.3189f, 1.0f, 1.0f, - -0.4824f, 0.0f, 0.3001f, 1.0f, 1.0f, - -0.4964f, 0.0f, 0.279f, 1.0f, 1.0f, - -0.508f, 0.0f, 0.256f, 1.0f, 1.0f, - -0.5168f, 0.0f, 0.2314f, 1.0f, 1.0f, - -0.5227f, 0.0f, 0.2057f, 1.0f, 1.0f, - -0.5257f, 0.0f, 0.1794f, 1.0f, 1.0f, - -0.5257f, 0.0f, 0.1528f, 1.0f, 1.0f, - -0.5227f, 0.0f, 0.1264f, 1.0f, 1.0f, - -0.5168f, 0.0f, 0.1007f, 1.0f, 1.0f, - -0.508f, 0.0f, 0.0762f, 1.0f, 1.0f, - -0.4964f, 0.0f, 0.0531f, 1.0f, 1.0f, - -0.4824f, 0.0f, 0.032f, 1.0f, 1.0f, - -0.4661f, 0.0f, 0.0132f, 1.0f, 1.0f, - -0.4479f, 0.0f, -0.0029f, 1.0f, 1.0f, - -0.4279f, 0.0f, -0.0162f, 1.0f, 1.0f, - -0.4066f, 0.0f, -0.0264f, 1.0f, 1.0f, - -0.3844f, 0.0f, -0.0333f, 1.0f, 1.0f, - -0.3616f, 0.0f, -0.0368f, 1.0f, 1.0f, - -0.3385f, 0.0f, -0.0368f, 1.0f, 1.0f, - -0.3157f, 0.0f, -0.0333f, 1.0f, 1.0f, - -0.2935f, 0.0f, -0.0264f, 1.0f, 1.0f, - -0.2722f, 0.0f, -0.0162f, 1.0f, 1.0f, - -0.2522f, 0.0f, -0.0029f, 1.0f, 1.0f, - -0.234f, 0.0f, 0.0132f, 1.0f, 1.0f, - -0.2177f, 0.0f, 0.032f, 1.0f, 1.0f, - -0.2037f, 0.0f, 0.0531f, 1.0f, 1.0f, - -0.1922f, 0.0f, 0.0762f, 1.0f, 1.0f, - -0.1833f, 0.0f, 0.1007f, 1.0f, 1.0f, - -0.1774f, 0.0f, 0.1264f, 1.0f, 1.0f, - -0.1744f, 0.0f, 0.1528f, 1.0f, 1.0f, + -0.174f, 0.0f, 0.1661f, 1.0f, 1.0f, -0.1744f, 0.0f, 0.1794f, 1.0f, 1.0f, + -0.1774f, 0.0f, 0.2057f, 1.0f, 1.0f, -0.1833f, 0.0f, 0.2314f, 1.0f, 1.0f, + -0.1922f, 0.0f, 0.256f, 1.0f, 1.0f, -0.2037f, 0.0f, 0.279f, 1.0f, 1.0f, + -0.2177f, 0.0f, 0.3001f, 1.0f, 1.0f, -0.234f, 0.0f, 0.3189f, 1.0f, 1.0f, + -0.2522f, 0.0f, 0.3351f, 1.0f, 1.0f, -0.2722f, 0.0f, 0.3483f, 1.0f, 1.0f, + -0.2935f, 0.0f, 0.3585f, 1.0f, 1.0f, -0.3157f, 0.0f, 0.3654f, 1.0f, 1.0f, + -0.3385f, 0.0f, 0.3689f, 1.0f, 1.0f, -0.3616f, 0.0f, 0.3689f, 1.0f, 1.0f, + -0.3844f, 0.0f, 0.3654f, 1.0f, 1.0f, -0.4066f, 0.0f, 0.3585f, 1.0f, 1.0f, + -0.4279f, 0.0f, 0.3483f, 1.0f, 1.0f, -0.4479f, 0.0f, 0.3351f, 1.0f, 1.0f, + -0.4661f, 0.0f, 0.3189f, 1.0f, 1.0f, -0.4824f, 0.0f, 0.3001f, 1.0f, 1.0f, + -0.4964f, 0.0f, 0.279f, 1.0f, 1.0f, -0.508f, 0.0f, 0.256f, 1.0f, 1.0f, + -0.5168f, 0.0f, 0.2314f, 1.0f, 1.0f, -0.5227f, 0.0f, 0.2057f, 1.0f, 1.0f, + -0.5257f, 0.0f, 0.1794f, 1.0f, 1.0f, -0.5257f, 0.0f, 0.1528f, 1.0f, 1.0f, + -0.5227f, 0.0f, 0.1264f, 1.0f, 1.0f, -0.5168f, 0.0f, 0.1007f, 1.0f, 1.0f, + -0.508f, 0.0f, 0.0762f, 1.0f, 1.0f, -0.4964f, 0.0f, 0.0531f, 1.0f, 1.0f, + -0.4824f, 0.0f, 0.032f, 1.0f, 1.0f, -0.4661f, 0.0f, 0.0132f, 1.0f, 1.0f, + -0.4479f, 0.0f, -0.0029f, 1.0f, 1.0f, -0.4279f, 0.0f, -0.0162f, 1.0f, 1.0f, + -0.4066f, 0.0f, -0.0264f, 1.0f, 1.0f, -0.3844f, 0.0f, -0.0333f, 1.0f, 1.0f, + -0.3616f, 0.0f, -0.0368f, 1.0f, 1.0f, -0.3385f, 0.0f, -0.0368f, 1.0f, 1.0f, + -0.3157f, 0.0f, -0.0333f, 1.0f, 1.0f, -0.2935f, 0.0f, -0.0264f, 1.0f, 1.0f, + -0.2722f, 0.0f, -0.0162f, 1.0f, 1.0f, -0.2522f, 0.0f, -0.0029f, 1.0f, 1.0f, + -0.234f, 0.0f, 0.0132f, 1.0f, 1.0f, -0.2177f, 0.0f, 0.032f, 1.0f, 1.0f, + -0.2037f, 0.0f, 0.0531f, 1.0f, 1.0f, -0.1922f, 0.0f, 0.0762f, 1.0f, 1.0f, + -0.1833f, 0.0f, 0.1007f, 1.0f, 1.0f, -0.1774f, 0.0f, 0.1264f, 1.0f, 1.0f, + -0.1744f, 0.0f, 0.1528f, 1.0f, 1.0f, }; static const float data11[18 * GP_PRIM_DATABUF_SIZE] = { - 0.963f, 0.0f, 0.1753f, 0.1f, 1.0f, - 0.9555f, 0.0f, 0.1761f, 0.2199f, 1.0f, - 0.9367f, 0.0f, 0.1758f, 0.6019f, 1.0f, - 0.9202f, 0.0f, 0.1741f, 0.7689f, 1.0f, - 0.9036f, 0.0f, 0.1714f, 0.866f, 1.0f, - 0.8885f, 0.0f, 0.1668f, 0.9272f, 1.0f, - 0.8746f, 0.0f, 0.1607f, 0.9653f, 1.0f, - 0.8621f, 0.0f, 0.1531f, 0.9876f, 1.0f, - 0.8503f, 0.0f, 0.1447f, 0.9983f, 1.0f, - 0.8389f, 0.0f, 0.1352f, 0.999f, 1.0f, - 0.8279f, 0.0f, 0.1244f, 0.9912f, 1.0f, - 0.8174f, 0.0f, 0.1125f, 0.9724f, 1.0f, - 0.8079f, 0.0f, 0.099f, 0.9385f, 1.0f, - 0.7999f, 0.0f, 0.0839f, 0.8841f, 1.0f, - 0.7935f, 0.0f, 0.0669f, 0.7979f, 1.0f, - 0.7892f, 0.0f, 0.0488f, 0.6541f, 1.0f, - 0.787f, 0.0f, 0.0305f, 0.36f, 1.0f, - 0.7847f, 0.0f, 0.0139f, 0.1f, 1.0f, + 0.963f, 0.0f, 0.1753f, 0.1f, 1.0f, 0.9555f, 0.0f, 0.1761f, 0.2199f, 1.0f, + 0.9367f, 0.0f, 0.1758f, 0.6019f, 1.0f, 0.9202f, 0.0f, 0.1741f, 0.7689f, 1.0f, + 0.9036f, 0.0f, 0.1714f, 0.866f, 1.0f, 0.8885f, 0.0f, 0.1668f, 0.9272f, 1.0f, + 0.8746f, 0.0f, 0.1607f, 0.9653f, 1.0f, 0.8621f, 0.0f, 0.1531f, 0.9876f, 1.0f, + 0.8503f, 0.0f, 0.1447f, 0.9983f, 1.0f, 0.8389f, 0.0f, 0.1352f, 0.999f, 1.0f, + 0.8279f, 0.0f, 0.1244f, 0.9912f, 1.0f, 0.8174f, 0.0f, 0.1125f, 0.9724f, 1.0f, + 0.8079f, 0.0f, 0.099f, 0.9385f, 1.0f, 0.7999f, 0.0f, 0.0839f, 0.8841f, 1.0f, + 0.7935f, 0.0f, 0.0669f, 0.7979f, 1.0f, 0.7892f, 0.0f, 0.0488f, 0.6541f, 1.0f, + 0.787f, 0.0f, 0.0305f, 0.36f, 1.0f, 0.7847f, 0.0f, 0.0139f, 0.1f, 1.0f, }; static const float data12[18 * GP_PRIM_DATABUF_SIZE] = { - -1.0227f, 0.0f, 0.1753f, 0.1f, 1.0f, - -1.0153f, 0.0f, 0.1761f, 0.2199f, 1.0f, - -0.9964f, 0.0f, 0.1758f, 0.6019f, 1.0f, - -0.9799f, 0.0f, 0.1741f, 0.7689f, 1.0f, - -0.9634f, 0.0f, 0.1714f, 0.866f, 1.0f, - -0.9483f, 0.0f, 0.1668f, 0.9272f, 1.0f, - -0.9344f, 0.0f, 0.1607f, 0.9653f, 1.0f, - -0.9219f, 0.0f, 0.1531f, 0.9876f, 1.0f, - -0.9101f, 0.0f, 0.1447f, 0.9983f, 1.0f, - -0.8986f, 0.0f, 0.1352f, 0.999f, 1.0f, - -0.8876f, 0.0f, 0.1244f, 0.9912f, 1.0f, - -0.8772f, 0.0f, 0.1125f, 0.9724f, 1.0f, - -0.8677f, 0.0f, 0.099f, 0.9385f, 1.0f, - -0.8597f, 0.0f, 0.0839f, 0.8841f, 1.0f, - -0.8533f, 0.0f, 0.0669f, 0.7979f, 1.0f, - -0.849f, 0.0f, 0.0488f, 0.6541f, 1.0f, - -0.8467f, 0.0f, 0.0305f, 0.36f, 1.0f, - -0.8444f, 0.0f, 0.0139f, 0.1f, 1.0f, + -1.0227f, 0.0f, 0.1753f, 0.1f, 1.0f, -1.0153f, 0.0f, 0.1761f, 0.2199f, 1.0f, + -0.9964f, 0.0f, 0.1758f, 0.6019f, 1.0f, -0.9799f, 0.0f, 0.1741f, 0.7689f, 1.0f, + -0.9634f, 0.0f, 0.1714f, 0.866f, 1.0f, -0.9483f, 0.0f, 0.1668f, 0.9272f, 1.0f, + -0.9344f, 0.0f, 0.1607f, 0.9653f, 1.0f, -0.9219f, 0.0f, 0.1531f, 0.9876f, 1.0f, + -0.9101f, 0.0f, 0.1447f, 0.9983f, 1.0f, -0.8986f, 0.0f, 0.1352f, 0.999f, 1.0f, + -0.8876f, 0.0f, 0.1244f, 0.9912f, 1.0f, -0.8772f, 0.0f, 0.1125f, 0.9724f, 1.0f, + -0.8677f, 0.0f, 0.099f, 0.9385f, 1.0f, -0.8597f, 0.0f, 0.0839f, 0.8841f, 1.0f, + -0.8533f, 0.0f, 0.0669f, 0.7979f, 1.0f, -0.849f, 0.0f, 0.0488f, 0.6541f, 1.0f, + -0.8467f, 0.0f, 0.0305f, 0.36f, 1.0f, -0.8444f, 0.0f, 0.0139f, 0.1f, 1.0f, }; static const float data13[33 * GP_PRIM_DATABUF_SIZE] = { - -0.6794f, 0.0f, 0.3908f, 0.1f, 1.0f, - -0.6711f, 0.0f, 0.4112f, 0.2199f, 1.0f, - -0.6513f, 0.0f, 0.4509f, 0.4615f, 1.0f, - -0.6276f, 0.0f, 0.489f, 0.6019f, 1.0f, - -0.6001f, 0.0f, 0.5253f, 0.6981f, 1.0f, - -0.5692f, 0.0f, 0.5598f, 0.7689f, 1.0f, - -0.535f, 0.0f, 0.5924f, 0.8236f, 1.0f, - -0.4979f, 0.0f, 0.6228f, 0.866f, 1.0f, - -0.4579f, 0.0f, 0.651f, 0.9003f, 1.0f, - -0.4155f, 0.0f, 0.677f, 0.9272f, 1.0f, - -0.3707f, 0.0f, 0.7005f, 0.9485f, 1.0f, - -0.3239f, 0.0f, 0.7215f, 0.9653f, 1.0f, - -0.2753f, 0.0f, 0.7399f, 0.9781f, 1.0f, - -0.2251f, 0.0f, 0.7555f, 0.9876f, 1.0f, - -0.1736f, 0.0f, 0.7683f, 0.9942f, 1.0f, - -0.121f, 0.0f, 0.778f, 0.9983f, 1.0f, - -0.0675f, 0.0f, 0.7847f, 0.9997f, 1.0f, - -0.0134f, 0.0f, 0.7882f, 0.999f, 1.0f, - 0.0411f, 0.0f, 0.7884f, 0.9963f, 1.0f, - 0.0957f, 0.0f, 0.7851f, 0.9912f, 1.0f, - 0.1503f, 0.0f, 0.7783f, 0.9834f, 1.0f, - 0.2045f, 0.0f, 0.7678f, 0.9724f, 1.0f, - 0.2581f, 0.0f, 0.7536f, 0.9576f, 1.0f, - 0.311f, 0.0f, 0.7355f, 0.9385f, 1.0f, - 0.3628f, 0.0f, 0.7134f, 0.9143f, 1.0f, - 0.4133f, 0.0f, 0.6873f, 0.8841f, 1.0f, - 0.4622f, 0.0f, 0.6569f, 0.8461f, 1.0f, - 0.5095f, 0.0f, 0.6221f, 0.7979f, 1.0f, - 0.5547f, 0.0f, 0.583f, 0.7359f, 1.0f, - 0.5977f, 0.0f, 0.5393f, 0.6541f, 1.0f, - 0.6382f, 0.0f, 0.4909f, 0.5396f, 1.0f, - 0.676f, 0.0f, 0.4377f, 0.36f, 1.0f, - 0.7109f, 0.0f, 0.3797f, 0.1f, 1.0f, + -0.6794f, 0.0f, 0.3908f, 0.1f, 1.0f, -0.6711f, 0.0f, 0.4112f, 0.2199f, 1.0f, + -0.6513f, 0.0f, 0.4509f, 0.4615f, 1.0f, -0.6276f, 0.0f, 0.489f, 0.6019f, 1.0f, + -0.6001f, 0.0f, 0.5253f, 0.6981f, 1.0f, -0.5692f, 0.0f, 0.5598f, 0.7689f, 1.0f, + -0.535f, 0.0f, 0.5924f, 0.8236f, 1.0f, -0.4979f, 0.0f, 0.6228f, 0.866f, 1.0f, + -0.4579f, 0.0f, 0.651f, 0.9003f, 1.0f, -0.4155f, 0.0f, 0.677f, 0.9272f, 1.0f, + -0.3707f, 0.0f, 0.7005f, 0.9485f, 1.0f, -0.3239f, 0.0f, 0.7215f, 0.9653f, 1.0f, + -0.2753f, 0.0f, 0.7399f, 0.9781f, 1.0f, -0.2251f, 0.0f, 0.7555f, 0.9876f, 1.0f, + -0.1736f, 0.0f, 0.7683f, 0.9942f, 1.0f, -0.121f, 0.0f, 0.778f, 0.9983f, 1.0f, + -0.0675f, 0.0f, 0.7847f, 0.9997f, 1.0f, -0.0134f, 0.0f, 0.7882f, 0.999f, 1.0f, + 0.0411f, 0.0f, 0.7884f, 0.9963f, 1.0f, 0.0957f, 0.0f, 0.7851f, 0.9912f, 1.0f, + 0.1503f, 0.0f, 0.7783f, 0.9834f, 1.0f, 0.2045f, 0.0f, 0.7678f, 0.9724f, 1.0f, + 0.2581f, 0.0f, 0.7536f, 0.9576f, 1.0f, 0.311f, 0.0f, 0.7355f, 0.9385f, 1.0f, + 0.3628f, 0.0f, 0.7134f, 0.9143f, 1.0f, 0.4133f, 0.0f, 0.6873f, 0.8841f, 1.0f, + 0.4622f, 0.0f, 0.6569f, 0.8461f, 1.0f, 0.5095f, 0.0f, 0.6221f, 0.7979f, 1.0f, + 0.5547f, 0.0f, 0.583f, 0.7359f, 1.0f, 0.5977f, 0.0f, 0.5393f, 0.6541f, 1.0f, + 0.6382f, 0.0f, 0.4909f, 0.5396f, 1.0f, 0.676f, 0.0f, 0.4377f, 0.36f, 1.0f, + 0.7109f, 0.0f, 0.3797f, 0.1f, 1.0f, }; static const float data14[33 * GP_PRIM_DATABUF_SIZE] = { - -0.7544f, 0.0f, 0.1799f, 0.1f, 1.0f, - -0.7495f, 0.0f, 0.162f, 0.2199f, 1.0f, - -0.7392f, 0.0f, 0.1283f, 0.4615f, 1.0f, - -0.7281f, 0.0f, 0.0975f, 0.6019f, 1.0f, - -0.7161f, 0.0f, 0.0693f, 0.6981f, 1.0f, - -0.7033f, 0.0f, 0.0435f, 0.7689f, 1.0f, - -0.6898f, 0.0f, 0.02f, 0.8236f, 1.0f, - -0.6757f, 0.0f, -0.0014f, 0.866f, 1.0f, - -0.6609f, 0.0f, -0.0208f, 0.9003f, 1.0f, - -0.6455f, 0.0f, -0.0386f, 0.9272f, 1.0f, - -0.6297f, 0.0f, -0.0547f, 0.9485f, 1.0f, - -0.6133f, 0.0f, -0.0695f, 0.9653f, 1.0f, - -0.5966f, 0.0f, -0.083f, 0.9781f, 1.0f, - -0.5795f, 0.0f, -0.0955f, 0.9876f, 1.0f, - -0.5621f, 0.0f, -0.1071f, 0.9942f, 1.0f, - -0.5444f, 0.0f, -0.118f, 0.9983f, 1.0f, - -0.5265f, 0.0f, -0.1284f, 0.9997f, 1.0f, - -0.5084f, 0.0f, -0.1384f, 0.999f, 1.0f, - -0.4902f, 0.0f, -0.1483f, 0.9963f, 1.0f, - -0.4719f, 0.0f, -0.1582f, 0.9912f, 1.0f, - -0.4537f, 0.0f, -0.1682f, 0.9834f, 1.0f, - -0.4355f, 0.0f, -0.1787f, 0.9724f, 1.0f, - -0.4173f, 0.0f, -0.1896f, 0.9576f, 1.0f, - -0.3993f, 0.0f, -0.2013f, 0.9385f, 1.0f, - -0.3815f, 0.0f, -0.2138f, 0.9143f, 1.0f, - -0.364f, 0.0f, -0.2274f, 0.8841f, 1.0f, - -0.3467f, 0.0f, -0.2422f, 0.8461f, 1.0f, - -0.3298f, 0.0f, -0.2584f, 0.7979f, 1.0f, - -0.3133f, 0.0f, -0.2762f, 0.7359f, 1.0f, - -0.2972f, 0.0f, -0.2958f, 0.6541f, 1.0f, - -0.2816f, 0.0f, -0.3173f, 0.5396f, 1.0f, - -0.2665f, 0.0f, -0.3409f, 0.36f, 1.0f, - -0.2521f, 0.0f, -0.3668f, 0.1f, 1.0f, + -0.7544f, 0.0f, 0.1799f, 0.1f, 1.0f, -0.7495f, 0.0f, 0.162f, 0.2199f, 1.0f, + -0.7392f, 0.0f, 0.1283f, 0.4615f, 1.0f, -0.7281f, 0.0f, 0.0975f, 0.6019f, 1.0f, + -0.7161f, 0.0f, 0.0693f, 0.6981f, 1.0f, -0.7033f, 0.0f, 0.0435f, 0.7689f, 1.0f, + -0.6898f, 0.0f, 0.02f, 0.8236f, 1.0f, -0.6757f, 0.0f, -0.0014f, 0.866f, 1.0f, + -0.6609f, 0.0f, -0.0208f, 0.9003f, 1.0f, -0.6455f, 0.0f, -0.0386f, 0.9272f, 1.0f, + -0.6297f, 0.0f, -0.0547f, 0.9485f, 1.0f, -0.6133f, 0.0f, -0.0695f, 0.9653f, 1.0f, + -0.5966f, 0.0f, -0.083f, 0.9781f, 1.0f, -0.5795f, 0.0f, -0.0955f, 0.9876f, 1.0f, + -0.5621f, 0.0f, -0.1071f, 0.9942f, 1.0f, -0.5444f, 0.0f, -0.118f, 0.9983f, 1.0f, + -0.5265f, 0.0f, -0.1284f, 0.9997f, 1.0f, -0.5084f, 0.0f, -0.1384f, 0.999f, 1.0f, + -0.4902f, 0.0f, -0.1483f, 0.9963f, 1.0f, -0.4719f, 0.0f, -0.1582f, 0.9912f, 1.0f, + -0.4537f, 0.0f, -0.1682f, 0.9834f, 1.0f, -0.4355f, 0.0f, -0.1787f, 0.9724f, 1.0f, + -0.4173f, 0.0f, -0.1896f, 0.9576f, 1.0f, -0.3993f, 0.0f, -0.2013f, 0.9385f, 1.0f, + -0.3815f, 0.0f, -0.2138f, 0.9143f, 1.0f, -0.364f, 0.0f, -0.2274f, 0.8841f, 1.0f, + -0.3467f, 0.0f, -0.2422f, 0.8461f, 1.0f, -0.3298f, 0.0f, -0.2584f, 0.7979f, 1.0f, + -0.3133f, 0.0f, -0.2762f, 0.7359f, 1.0f, -0.2972f, 0.0f, -0.2958f, 0.6541f, 1.0f, + -0.2816f, 0.0f, -0.3173f, 0.5396f, 1.0f, -0.2665f, 0.0f, -0.3409f, 0.36f, 1.0f, + -0.2521f, 0.0f, -0.3668f, 0.1f, 1.0f, }; static const float data15[65 * GP_PRIM_DATABUF_SIZE] = { - -0.2854f, 0.0f, -0.4528f, 0.1f, 1.0f, - -0.2866f, 0.0f, -0.4623f, 0.1288f, 1.0f, - -0.2899f, 0.0f, -0.4814f, 0.2962f, 1.0f, - -0.2943f, 0.0f, -0.5008f, 0.4147f, 1.0f, - -0.2995f, 0.0f, -0.5203f, 0.5028f, 1.0f, - -0.3054f, 0.0f, -0.54f, 0.5723f, 1.0f, - -0.3117f, 0.0f, -0.5598f, 0.6291f, 1.0f, - -0.3182f, 0.0f, -0.5797f, 0.6768f, 1.0f, - -0.3249f, 0.0f, -0.5996f, 0.7177f, 1.0f, - -0.3314f, 0.0f, -0.6196f, 0.753f, 1.0f, - -0.3376f, 0.0f, -0.6395f, 0.7838f, 1.0f, - -0.3432f, 0.0f, -0.6594f, 0.8109f, 1.0f, - -0.3482f, 0.0f, -0.6792f, 0.8349f, 1.0f, - -0.3523f, 0.0f, -0.6989f, 0.8564f, 1.0f, - -0.3552f, 0.0f, -0.7185f, 0.8756f, 1.0f, - -0.3569f, 0.0f, -0.7379f, 0.8922f, 1.0f, - -0.357f, 0.0f, -0.7571f, 0.9074f, 1.0f, - -0.3555f, 0.0f, -0.7761f, 0.9211f, 1.0f, - -0.3522f, 0.0f, -0.7948f, 0.9329f, 1.0f, - -0.3467f, 0.0f, -0.8132f, 0.944f, 1.0f, - -0.339f, 0.0f, -0.8313f, 0.9531f, 1.0f, - -0.3289f, 0.0f, -0.849f, 0.9617f, 1.0f, - -0.316f, 0.0f, -0.8663f, 0.9688f, 1.0f, - -0.3004f, 0.0f, -0.8831f, 0.9755f, 1.0f, - -0.2817f, 0.0f, -0.8996f, 0.9808f, 1.0f, - -0.2598f, 0.0f, -0.9155f, 0.9858f, 1.0f, - -0.2344f, 0.0f, -0.9309f, 0.9894f, 1.0f, - -0.2051f, 0.0f, -0.9457f, 0.993f, 1.0f, - -0.1716f, 0.0f, -0.9599f, 0.9952f, 1.0f, - -0.1341f, 0.0f, -0.9733f, 0.9973f, 1.0f, - -0.0928f, 0.0f, -0.9857f, 0.9987f, 1.0f, - -0.05f, 0.0f, -0.9962f, 0.9993f, 1.0f, - -0.0087f, 0.0f, -1.0041f, 1.0f, 1.0f, - 0.0287f, 0.0f, -1.0087f, 0.9993f, 1.0f, - 0.062f, 0.0f, -1.0104f, 0.9987f, 1.0f, - 0.0924f, 0.0f, -1.0102f, 0.9973f, 1.0f, - 0.1205f, 0.0f, -1.0086f, 0.9952f, 1.0f, - 0.1465f, 0.0f, -1.0057f, 0.993f, 1.0f, - 0.1706f, 0.0f, -1.0017f, 0.9894f, 1.0f, - 0.1928f, 0.0f, -0.9964f, 0.9858f, 1.0f, - 0.2132f, 0.0f, -0.99f, 0.9808f, 1.0f, - 0.2318f, 0.0f, -0.9824f, 0.9755f, 1.0f, - 0.2487f, 0.0f, -0.9738f, 0.9688f, 1.0f, - 0.2641f, 0.0f, -0.9641f, 0.9617f, 1.0f, - 0.2778f, 0.0f, -0.9533f, 0.9531f, 1.0f, - 0.2901f, 0.0f, -0.9415f, 0.944f, 1.0f, - 0.3009f, 0.0f, -0.9287f, 0.9329f, 1.0f, - 0.3103f, 0.0f, -0.9148f, 0.9211f, 1.0f, - 0.3183f, 0.0f, -0.8999f, 0.9074f, 1.0f, - 0.325f, 0.0f, -0.8841f, 0.8922f, 1.0f, - 0.3304f, 0.0f, -0.8672f, 0.8756f, 1.0f, - 0.3345f, 0.0f, -0.8493f, 0.8564f, 1.0f, - 0.3374f, 0.0f, -0.8305f, 0.8349f, 1.0f, - 0.3391f, 0.0f, -0.8107f, 0.8109f, 1.0f, - 0.3397f, 0.0f, -0.7899f, 0.7838f, 1.0f, - 0.3392f, 0.0f, -0.7682f, 0.753f, 1.0f, - 0.3377f, 0.0f, -0.7456f, 0.7177f, 1.0f, - 0.3352f, 0.0f, -0.7221f, 0.6768f, 1.0f, - 0.3317f, 0.0f, -0.6976f, 0.6291f, 1.0f, - 0.3273f, 0.0f, -0.6722f, 0.5723f, 1.0f, - 0.322f, 0.0f, -0.646f, 0.5028f, 1.0f, - 0.316f, 0.0f, -0.6188f, 0.4147f, 1.0f, - 0.3091f, 0.0f, -0.5908f, 0.2962f, 1.0f, - 0.3015f, 0.0f, -0.5619f, 0.1288f, 1.0f, - 0.2974f, 0.0f, -0.5472f, 0.1f, 1.0f, + -0.2854f, 0.0f, -0.4528f, 0.1f, 1.0f, -0.2866f, 0.0f, -0.4623f, 0.1288f, 1.0f, + -0.2899f, 0.0f, -0.4814f, 0.2962f, 1.0f, -0.2943f, 0.0f, -0.5008f, 0.4147f, 1.0f, + -0.2995f, 0.0f, -0.5203f, 0.5028f, 1.0f, -0.3054f, 0.0f, -0.54f, 0.5723f, 1.0f, + -0.3117f, 0.0f, -0.5598f, 0.6291f, 1.0f, -0.3182f, 0.0f, -0.5797f, 0.6768f, 1.0f, + -0.3249f, 0.0f, -0.5996f, 0.7177f, 1.0f, -0.3314f, 0.0f, -0.6196f, 0.753f, 1.0f, + -0.3376f, 0.0f, -0.6395f, 0.7838f, 1.0f, -0.3432f, 0.0f, -0.6594f, 0.8109f, 1.0f, + -0.3482f, 0.0f, -0.6792f, 0.8349f, 1.0f, -0.3523f, 0.0f, -0.6989f, 0.8564f, 1.0f, + -0.3552f, 0.0f, -0.7185f, 0.8756f, 1.0f, -0.3569f, 0.0f, -0.7379f, 0.8922f, 1.0f, + -0.357f, 0.0f, -0.7571f, 0.9074f, 1.0f, -0.3555f, 0.0f, -0.7761f, 0.9211f, 1.0f, + -0.3522f, 0.0f, -0.7948f, 0.9329f, 1.0f, -0.3467f, 0.0f, -0.8132f, 0.944f, 1.0f, + -0.339f, 0.0f, -0.8313f, 0.9531f, 1.0f, -0.3289f, 0.0f, -0.849f, 0.9617f, 1.0f, + -0.316f, 0.0f, -0.8663f, 0.9688f, 1.0f, -0.3004f, 0.0f, -0.8831f, 0.9755f, 1.0f, + -0.2817f, 0.0f, -0.8996f, 0.9808f, 1.0f, -0.2598f, 0.0f, -0.9155f, 0.9858f, 1.0f, + -0.2344f, 0.0f, -0.9309f, 0.9894f, 1.0f, -0.2051f, 0.0f, -0.9457f, 0.993f, 1.0f, + -0.1716f, 0.0f, -0.9599f, 0.9952f, 1.0f, -0.1341f, 0.0f, -0.9733f, 0.9973f, 1.0f, + -0.0928f, 0.0f, -0.9857f, 0.9987f, 1.0f, -0.05f, 0.0f, -0.9962f, 0.9993f, 1.0f, + -0.0087f, 0.0f, -1.0041f, 1.0f, 1.0f, 0.0287f, 0.0f, -1.0087f, 0.9993f, 1.0f, + 0.062f, 0.0f, -1.0104f, 0.9987f, 1.0f, 0.0924f, 0.0f, -1.0102f, 0.9973f, 1.0f, + 0.1205f, 0.0f, -1.0086f, 0.9952f, 1.0f, 0.1465f, 0.0f, -1.0057f, 0.993f, 1.0f, + 0.1706f, 0.0f, -1.0017f, 0.9894f, 1.0f, 0.1928f, 0.0f, -0.9964f, 0.9858f, 1.0f, + 0.2132f, 0.0f, -0.99f, 0.9808f, 1.0f, 0.2318f, 0.0f, -0.9824f, 0.9755f, 1.0f, + 0.2487f, 0.0f, -0.9738f, 0.9688f, 1.0f, 0.2641f, 0.0f, -0.9641f, 0.9617f, 1.0f, + 0.2778f, 0.0f, -0.9533f, 0.9531f, 1.0f, 0.2901f, 0.0f, -0.9415f, 0.944f, 1.0f, + 0.3009f, 0.0f, -0.9287f, 0.9329f, 1.0f, 0.3103f, 0.0f, -0.9148f, 0.9211f, 1.0f, + 0.3183f, 0.0f, -0.8999f, 0.9074f, 1.0f, 0.325f, 0.0f, -0.8841f, 0.8922f, 1.0f, + 0.3304f, 0.0f, -0.8672f, 0.8756f, 1.0f, 0.3345f, 0.0f, -0.8493f, 0.8564f, 1.0f, + 0.3374f, 0.0f, -0.8305f, 0.8349f, 1.0f, 0.3391f, 0.0f, -0.8107f, 0.8109f, 1.0f, + 0.3397f, 0.0f, -0.7899f, 0.7838f, 1.0f, 0.3392f, 0.0f, -0.7682f, 0.753f, 1.0f, + 0.3377f, 0.0f, -0.7456f, 0.7177f, 1.0f, 0.3352f, 0.0f, -0.7221f, 0.6768f, 1.0f, + 0.3317f, 0.0f, -0.6976f, 0.6291f, 1.0f, 0.3273f, 0.0f, -0.6722f, 0.5723f, 1.0f, + 0.322f, 0.0f, -0.646f, 0.5028f, 1.0f, 0.316f, 0.0f, -0.6188f, 0.4147f, 1.0f, + 0.3091f, 0.0f, -0.5908f, 0.2962f, 1.0f, 0.3015f, 0.0f, -0.5619f, 0.1288f, 1.0f, + 0.2974f, 0.0f, -0.5472f, 0.1f, 1.0f, }; static const float data16[34 * GP_PRIM_DATABUF_SIZE] = { - -0.4408f, 0.0f, 0.5073f, 0.1f, 1.0f, - -0.4312f, 0.0f, 0.5132f, 0.1288f, 1.0f, - -0.3945f, 0.0f, 0.5319f, 0.4147f, 1.0f, - -0.3605f, 0.0f, 0.5438f, 0.5723f, 1.0f, - -0.3288f, 0.0f, 0.5496f, 0.6768f, 1.0f, - -0.2992f, 0.0f, 0.5501f, 0.753f, 1.0f, - -0.2712f, 0.0f, 0.546f, 0.8109f, 1.0f, - -0.2446f, 0.0f, 0.5381f, 0.8564f, 1.0f, - -0.2191f, 0.0f, 0.5272f, 0.8922f, 1.0f, - -0.1943f, 0.0f, 0.5139f, 0.9211f, 1.0f, - -0.1699f, 0.0f, 0.4992f, 0.944f, 1.0f, - -0.1456f, 0.0f, 0.4837f, 0.9617f, 1.0f, - -0.1211f, 0.0f, 0.4682f, 0.9755f, 1.0f, - -0.096f, 0.0f, 0.4535f, 0.9858f, 1.0f, - -0.07f, 0.0f, 0.4404f, 0.993f, 1.0f, - -0.0428f, 0.0f, 0.4301f, 0.9973f, 1.0f, - -0.016f, 0.0f, 0.4235f, 0.9993f, 1.0f, - 0.0076f, 0.0f, 0.4216f, 0.9993f, 1.0f, - 0.0283f, 0.0f, 0.4237f, 0.9973f, 1.0f, - 0.0477f, 0.0f, 0.4291f, 0.993f, 1.0f, - 0.067f, 0.0f, 0.4369f, 0.9858f, 1.0f, - 0.0866f, 0.0f, 0.4465f, 0.9755f, 1.0f, - 0.1068f, 0.0f, 0.4576f, 0.9617f, 1.0f, - 0.1278f, 0.0f, 0.4695f, 0.944f, 1.0f, - 0.1498f, 0.0f, 0.4819f, 0.9211f, 1.0f, - 0.173f, 0.0f, 0.4941f, 0.8922f, 1.0f, - 0.1977f, 0.0f, 0.5056f, 0.8564f, 1.0f, - 0.2241f, 0.0f, 0.5159f, 0.8109f, 1.0f, - 0.2523f, 0.0f, 0.5244f, 0.753f, 1.0f, - 0.2825f, 0.0f, 0.5307f, 0.6768f, 1.0f, - 0.315f, 0.0f, 0.5342f, 0.5723f, 1.0f, - 0.3499f, 0.0f, 0.5345f, 0.4147f, 1.0f, - 0.3875f, 0.0f, 0.5311f, 0.1288f, 1.0f, - 0.3973f, 0.0f, 0.5295f, 0.1f, 1.0f, + -0.4408f, 0.0f, 0.5073f, 0.1f, 1.0f, -0.4312f, 0.0f, 0.5132f, 0.1288f, 1.0f, + -0.3945f, 0.0f, 0.5319f, 0.4147f, 1.0f, -0.3605f, 0.0f, 0.5438f, 0.5723f, 1.0f, + -0.3288f, 0.0f, 0.5496f, 0.6768f, 1.0f, -0.2992f, 0.0f, 0.5501f, 0.753f, 1.0f, + -0.2712f, 0.0f, 0.546f, 0.8109f, 1.0f, -0.2446f, 0.0f, 0.5381f, 0.8564f, 1.0f, + -0.2191f, 0.0f, 0.5272f, 0.8922f, 1.0f, -0.1943f, 0.0f, 0.5139f, 0.9211f, 1.0f, + -0.1699f, 0.0f, 0.4992f, 0.944f, 1.0f, -0.1456f, 0.0f, 0.4837f, 0.9617f, 1.0f, + -0.1211f, 0.0f, 0.4682f, 0.9755f, 1.0f, -0.096f, 0.0f, 0.4535f, 0.9858f, 1.0f, + -0.07f, 0.0f, 0.4404f, 0.993f, 1.0f, -0.0428f, 0.0f, 0.4301f, 0.9973f, 1.0f, + -0.016f, 0.0f, 0.4235f, 0.9993f, 1.0f, 0.0076f, 0.0f, 0.4216f, 0.9993f, 1.0f, + 0.0283f, 0.0f, 0.4237f, 0.9973f, 1.0f, 0.0477f, 0.0f, 0.4291f, 0.993f, 1.0f, + 0.067f, 0.0f, 0.4369f, 0.9858f, 1.0f, 0.0866f, 0.0f, 0.4465f, 0.9755f, 1.0f, + 0.1068f, 0.0f, 0.4576f, 0.9617f, 1.0f, 0.1278f, 0.0f, 0.4695f, 0.944f, 1.0f, + 0.1498f, 0.0f, 0.4819f, 0.9211f, 1.0f, 0.173f, 0.0f, 0.4941f, 0.8922f, 1.0f, + 0.1977f, 0.0f, 0.5056f, 0.8564f, 1.0f, 0.2241f, 0.0f, 0.5159f, 0.8109f, 1.0f, + 0.2523f, 0.0f, 0.5244f, 0.753f, 1.0f, 0.2825f, 0.0f, 0.5307f, 0.6768f, 1.0f, + 0.315f, 0.0f, 0.5342f, 0.5723f, 1.0f, 0.3499f, 0.0f, 0.5345f, 0.4147f, 1.0f, + 0.3875f, 0.0f, 0.5311f, 0.1288f, 1.0f, 0.3973f, 0.0f, 0.5295f, 0.1f, 1.0f, }; static const float data17[33 * GP_PRIM_DATABUF_SIZE] = { - 0.724f, 0.0f, 0.1799f, 0.1f, 1.0f, - 0.7192f, 0.0f, 0.162f, 0.2199f, 1.0f, - 0.7089f, 0.0f, 0.1283f, 0.4615f, 1.0f, - 0.6977f, 0.0f, 0.0975f, 0.6019f, 1.0f, - 0.6858f, 0.0f, 0.0693f, 0.6981f, 1.0f, - 0.673f, 0.0f, 0.0435f, 0.7689f, 1.0f, - 0.6595f, 0.0f, 0.02f, 0.8236f, 1.0f, - 0.6453f, 0.0f, -0.0014f, 0.866f, 1.0f, - 0.6306f, 0.0f, -0.0208f, 0.9003f, 1.0f, - 0.6152f, 0.0f, -0.0386f, 0.9272f, 1.0f, - 0.5994f, 0.0f, -0.0547f, 0.9485f, 1.0f, - 0.583f, 0.0f, -0.0695f, 0.9653f, 1.0f, - 0.5663f, 0.0f, -0.083f, 0.9781f, 1.0f, - 0.5492f, 0.0f, -0.0955f, 0.9876f, 1.0f, - 0.5317f, 0.0f, -0.1071f, 0.9942f, 1.0f, - 0.514f, 0.0f, -0.118f, 0.9983f, 1.0f, - 0.4961f, 0.0f, -0.1284f, 0.9997f, 1.0f, - 0.4781f, 0.0f, -0.1384f, 0.999f, 1.0f, - 0.4599f, 0.0f, -0.1483f, 0.9963f, 1.0f, - 0.4416f, 0.0f, -0.1582f, 0.9912f, 1.0f, - 0.4234f, 0.0f, -0.1682f, 0.9834f, 1.0f, - 0.4051f, 0.0f, -0.1787f, 0.9724f, 1.0f, - 0.387f, 0.0f, -0.1896f, 0.9576f, 1.0f, - 0.369f, 0.0f, -0.2013f, 0.9385f, 1.0f, - 0.3512f, 0.0f, -0.2138f, 0.9143f, 1.0f, - 0.3337f, 0.0f, -0.2274f, 0.8841f, 1.0f, - 0.3164f, 0.0f, -0.2422f, 0.8461f, 1.0f, - 0.2995f, 0.0f, -0.2584f, 0.7979f, 1.0f, - 0.2829f, 0.0f, -0.2762f, 0.7359f, 1.0f, - 0.2669f, 0.0f, -0.2958f, 0.6541f, 1.0f, - 0.2513f, 0.0f, -0.3173f, 0.5396f, 1.0f, - 0.2362f, 0.0f, -0.3409f, 0.36f, 1.0f, - 0.2218f, 0.0f, -0.3668f, 0.1f, 1.0f, + 0.724f, 0.0f, 0.1799f, 0.1f, 1.0f, 0.7192f, 0.0f, 0.162f, 0.2199f, 1.0f, + 0.7089f, 0.0f, 0.1283f, 0.4615f, 1.0f, 0.6977f, 0.0f, 0.0975f, 0.6019f, 1.0f, + 0.6858f, 0.0f, 0.0693f, 0.6981f, 1.0f, 0.673f, 0.0f, 0.0435f, 0.7689f, 1.0f, + 0.6595f, 0.0f, 0.02f, 0.8236f, 1.0f, 0.6453f, 0.0f, -0.0014f, 0.866f, 1.0f, + 0.6306f, 0.0f, -0.0208f, 0.9003f, 1.0f, 0.6152f, 0.0f, -0.0386f, 0.9272f, 1.0f, + 0.5994f, 0.0f, -0.0547f, 0.9485f, 1.0f, 0.583f, 0.0f, -0.0695f, 0.9653f, 1.0f, + 0.5663f, 0.0f, -0.083f, 0.9781f, 1.0f, 0.5492f, 0.0f, -0.0955f, 0.9876f, 1.0f, + 0.5317f, 0.0f, -0.1071f, 0.9942f, 1.0f, 0.514f, 0.0f, -0.118f, 0.9983f, 1.0f, + 0.4961f, 0.0f, -0.1284f, 0.9997f, 1.0f, 0.4781f, 0.0f, -0.1384f, 0.999f, 1.0f, + 0.4599f, 0.0f, -0.1483f, 0.9963f, 1.0f, 0.4416f, 0.0f, -0.1582f, 0.9912f, 1.0f, + 0.4234f, 0.0f, -0.1682f, 0.9834f, 1.0f, 0.4051f, 0.0f, -0.1787f, 0.9724f, 1.0f, + 0.387f, 0.0f, -0.1896f, 0.9576f, 1.0f, 0.369f, 0.0f, -0.2013f, 0.9385f, 1.0f, + 0.3512f, 0.0f, -0.2138f, 0.9143f, 1.0f, 0.3337f, 0.0f, -0.2274f, 0.8841f, 1.0f, + 0.3164f, 0.0f, -0.2422f, 0.8461f, 1.0f, 0.2995f, 0.0f, -0.2584f, 0.7979f, 1.0f, + 0.2829f, 0.0f, -0.2762f, 0.7359f, 1.0f, 0.2669f, 0.0f, -0.2958f, 0.6541f, 1.0f, + 0.2513f, 0.0f, -0.3173f, 0.5396f, 1.0f, 0.2362f, 0.0f, -0.3409f, 0.36f, 1.0f, + 0.2218f, 0.0f, -0.3668f, 0.1f, 1.0f, }; static const float data18[33 * GP_PRIM_DATABUF_SIZE] = { - -0.1119f, 0.0f, -0.7868f, 0.1f, 1.0f, - -0.1087f, 0.0f, -0.7905f, 0.2199f, 1.0f, - -0.1022f, 0.0f, -0.7974f, 0.4615f, 1.0f, - -0.0955f, 0.0f, -0.8038f, 0.6019f, 1.0f, - -0.0886f, 0.0f, -0.8097f, 0.6981f, 1.0f, - -0.0816f, 0.0f, -0.8152f, 0.7689f, 1.0f, - -0.0744f, 0.0f, -0.8201f, 0.8236f, 1.0f, - -0.0671f, 0.0f, -0.8246f, 0.866f, 1.0f, - -0.0597f, 0.0f, -0.8286f, 0.9003f, 1.0f, - -0.0522f, 0.0f, -0.8321f, 0.9272f, 1.0f, - -0.0446f, 0.0f, -0.8351f, 0.9485f, 1.0f, - -0.0369f, 0.0f, -0.8376f, 0.9653f, 1.0f, - -0.0292f, 0.0f, -0.8397f, 0.9781f, 1.0f, - -0.0214f, 0.0f, -0.8413f, 0.9876f, 1.0f, - -0.0136f, 0.0f, -0.8424f, 0.9942f, 1.0f, - -0.0057f, 0.0f, -0.843f, 0.9983f, 1.0f, - 0.0022f, 0.0f, -0.8431f, 0.9997f, 1.0f, - 0.01f, 0.0f, -0.8428f, 0.999f, 1.0f, - 0.0178f, 0.0f, -0.8419f, 0.9963f, 1.0f, - 0.0257f, 0.0f, -0.8407f, 0.9912f, 1.0f, - 0.0334f, 0.0f, -0.8389f, 0.9834f, 1.0f, - 0.0411f, 0.0f, -0.8366f, 0.9724f, 1.0f, - 0.0488f, 0.0f, -0.8339f, 0.9576f, 1.0f, - 0.0564f, 0.0f, -0.8308f, 0.9385f, 1.0f, - 0.0638f, 0.0f, -0.8271f, 0.9143f, 1.0f, - 0.0712f, 0.0f, -0.823f, 0.8841f, 1.0f, - 0.0785f, 0.0f, -0.8184f, 0.8461f, 1.0f, - 0.0856f, 0.0f, -0.8134f, 0.7979f, 1.0f, - 0.0925f, 0.0f, -0.8079f, 0.7359f, 1.0f, - 0.0994f, 0.0f, -0.8019f, 0.6541f, 1.0f, - 0.106f, 0.0f, -0.7954f, 0.5396f, 1.0f, - 0.1125f, 0.0f, -0.7885f, 0.36f, 1.0f, - 0.1187f, 0.0f, -0.7812f, 0.1f, 1.0f, + -0.1119f, 0.0f, -0.7868f, 0.1f, 1.0f, -0.1087f, 0.0f, -0.7905f, 0.2199f, 1.0f, + -0.1022f, 0.0f, -0.7974f, 0.4615f, 1.0f, -0.0955f, 0.0f, -0.8038f, 0.6019f, 1.0f, + -0.0886f, 0.0f, -0.8097f, 0.6981f, 1.0f, -0.0816f, 0.0f, -0.8152f, 0.7689f, 1.0f, + -0.0744f, 0.0f, -0.8201f, 0.8236f, 1.0f, -0.0671f, 0.0f, -0.8246f, 0.866f, 1.0f, + -0.0597f, 0.0f, -0.8286f, 0.9003f, 1.0f, -0.0522f, 0.0f, -0.8321f, 0.9272f, 1.0f, + -0.0446f, 0.0f, -0.8351f, 0.9485f, 1.0f, -0.0369f, 0.0f, -0.8376f, 0.9653f, 1.0f, + -0.0292f, 0.0f, -0.8397f, 0.9781f, 1.0f, -0.0214f, 0.0f, -0.8413f, 0.9876f, 1.0f, + -0.0136f, 0.0f, -0.8424f, 0.9942f, 1.0f, -0.0057f, 0.0f, -0.843f, 0.9983f, 1.0f, + 0.0022f, 0.0f, -0.8431f, 0.9997f, 1.0f, 0.01f, 0.0f, -0.8428f, 0.999f, 1.0f, + 0.0178f, 0.0f, -0.8419f, 0.9963f, 1.0f, 0.0257f, 0.0f, -0.8407f, 0.9912f, 1.0f, + 0.0334f, 0.0f, -0.8389f, 0.9834f, 1.0f, 0.0411f, 0.0f, -0.8366f, 0.9724f, 1.0f, + 0.0488f, 0.0f, -0.8339f, 0.9576f, 1.0f, 0.0564f, 0.0f, -0.8308f, 0.9385f, 1.0f, + 0.0638f, 0.0f, -0.8271f, 0.9143f, 1.0f, 0.0712f, 0.0f, -0.823f, 0.8841f, 1.0f, + 0.0785f, 0.0f, -0.8184f, 0.8461f, 1.0f, 0.0856f, 0.0f, -0.8134f, 0.7979f, 1.0f, + 0.0925f, 0.0f, -0.8079f, 0.7359f, 1.0f, 0.0994f, 0.0f, -0.8019f, 0.6541f, 1.0f, + 0.106f, 0.0f, -0.7954f, 0.5396f, 1.0f, 0.1125f, 0.0f, -0.7885f, 0.36f, 1.0f, + 0.1187f, 0.0f, -0.7812f, 0.1f, 1.0f, }; static const float data19[34 * GP_PRIM_DATABUF_SIZE] = { - -0.1119f, 0.0f, -0.2655f, 0.1f, 1.0f, - -0.1101f, 0.0f, -0.2605f, 0.1288f, 1.0f, - -0.1032f, 0.0f, -0.2424f, 0.4147f, 1.0f, - -0.0965f, 0.0f, -0.2276f, 0.5723f, 1.0f, - -0.0901f, 0.0f, -0.2158f, 0.6768f, 1.0f, - -0.0837f, 0.0f, -0.2069f, 0.753f, 1.0f, - -0.0775f, 0.0f, -0.2006f, 0.8109f, 1.0f, - -0.0712f, 0.0f, -0.1967f, 0.8564f, 1.0f, - -0.065f, 0.0f, -0.1949f, 0.8922f, 1.0f, - -0.0587f, 0.0f, -0.195f, 0.9211f, 1.0f, - -0.0522f, 0.0f, -0.1968f, 0.944f, 1.0f, - -0.0455f, 0.0f, -0.2001f, 0.9617f, 1.0f, - -0.0386f, 0.0f, -0.2046f, 0.9755f, 1.0f, - -0.0315f, 0.0f, -0.21f, 0.9858f, 1.0f, - -0.0239f, 0.0f, -0.2163f, 0.993f, 1.0f, - -0.016f, 0.0f, -0.223f, 0.9973f, 1.0f, - -0.0075f, 0.0f, -0.2301f, 0.9993f, 1.0f, - -0.0002f, 0.0f, -0.2342f, 0.9993f, 1.0f, - 0.0054f, 0.0f, -0.234f, 0.9973f, 1.0f, - 0.0106f, 0.0f, -0.2324f, 0.993f, 1.0f, - 0.0155f, 0.0f, -0.2297f, 0.9858f, 1.0f, - 0.0202f, 0.0f, -0.2261f, 0.9755f, 1.0f, - 0.0249f, 0.0f, -0.2219f, 0.9617f, 1.0f, - 0.0296f, 0.0f, -0.2174f, 0.944f, 1.0f, - 0.0344f, 0.0f, -0.2128f, 0.9211f, 1.0f, - 0.0394f, 0.0f, -0.2084f, 0.8922f, 1.0f, - 0.0448f, 0.0f, -0.2044f, 0.8564f, 1.0f, - 0.0506f, 0.0f, -0.2012f, 0.8109f, 1.0f, - 0.057f, 0.0f, -0.1988f, 0.753f, 1.0f, - 0.064f, 0.0f, -0.1977f, 0.6768f, 1.0f, - 0.0717f, 0.0f, -0.198f, 0.5723f, 1.0f, - 0.0804f, 0.0f, -0.2001f, 0.4147f, 1.0f, - 0.0899f, 0.0f, -0.2042f, 0.1288f, 1.0f, - 0.0925f, 0.0f, -0.2055f, 0.1f, 1.0f, + -0.1119f, 0.0f, -0.2655f, 0.1f, 1.0f, -0.1101f, 0.0f, -0.2605f, 0.1288f, 1.0f, + -0.1032f, 0.0f, -0.2424f, 0.4147f, 1.0f, -0.0965f, 0.0f, -0.2276f, 0.5723f, 1.0f, + -0.0901f, 0.0f, -0.2158f, 0.6768f, 1.0f, -0.0837f, 0.0f, -0.2069f, 0.753f, 1.0f, + -0.0775f, 0.0f, -0.2006f, 0.8109f, 1.0f, -0.0712f, 0.0f, -0.1967f, 0.8564f, 1.0f, + -0.065f, 0.0f, -0.1949f, 0.8922f, 1.0f, -0.0587f, 0.0f, -0.195f, 0.9211f, 1.0f, + -0.0522f, 0.0f, -0.1968f, 0.944f, 1.0f, -0.0455f, 0.0f, -0.2001f, 0.9617f, 1.0f, + -0.0386f, 0.0f, -0.2046f, 0.9755f, 1.0f, -0.0315f, 0.0f, -0.21f, 0.9858f, 1.0f, + -0.0239f, 0.0f, -0.2163f, 0.993f, 1.0f, -0.016f, 0.0f, -0.223f, 0.9973f, 1.0f, + -0.0075f, 0.0f, -0.2301f, 0.9993f, 1.0f, -0.0002f, 0.0f, -0.2342f, 0.9993f, 1.0f, + 0.0054f, 0.0f, -0.234f, 0.9973f, 1.0f, 0.0106f, 0.0f, -0.2324f, 0.993f, 1.0f, + 0.0155f, 0.0f, -0.2297f, 0.9858f, 1.0f, 0.0202f, 0.0f, -0.2261f, 0.9755f, 1.0f, + 0.0249f, 0.0f, -0.2219f, 0.9617f, 1.0f, 0.0296f, 0.0f, -0.2174f, 0.944f, 1.0f, + 0.0344f, 0.0f, -0.2128f, 0.9211f, 1.0f, 0.0394f, 0.0f, -0.2084f, 0.8922f, 1.0f, + 0.0448f, 0.0f, -0.2044f, 0.8564f, 1.0f, 0.0506f, 0.0f, -0.2012f, 0.8109f, 1.0f, + 0.057f, 0.0f, -0.1988f, 0.753f, 1.0f, 0.064f, 0.0f, -0.1977f, 0.6768f, 1.0f, + 0.0717f, 0.0f, -0.198f, 0.5723f, 1.0f, 0.0804f, 0.0f, -0.2001f, 0.4147f, 1.0f, + 0.0899f, 0.0f, -0.2042f, 0.1288f, 1.0f, 0.0925f, 0.0f, -0.2055f, 0.1f, 1.0f, }; static const float data20[33 * GP_PRIM_DATABUF_SIZE] = { - 0.7993f, 0.0f, 0.3242f, 0.1f, 1.0f, - 0.8169f, 0.0f, 0.3354f, 0.2199f, 1.0f, - 0.8512f, 0.0f, 0.3542f, 0.4615f, 1.0f, - 0.884f, 0.0f, 0.3686f, 0.6019f, 1.0f, - 0.9153f, 0.0f, 0.3787f, 0.6981f, 1.0f, - 0.945f, 0.0f, 0.3848f, 0.7689f, 1.0f, - 0.9729f, 0.0f, 0.3871f, 0.8236f, 1.0f, - 0.9989f, 0.0f, 0.3857f, 0.866f, 1.0f, - 1.0229f, 0.0f, 0.3811f, 0.9003f, 1.0f, - 1.0447f, 0.0f, 0.3732f, 0.9272f, 1.0f, - 1.0643f, 0.0f, 0.3624f, 0.9485f, 1.0f, - 1.0814f, 0.0f, 0.349f, 0.9653f, 1.0f, - 1.096f, 0.0f, 0.333f, 0.9781f, 1.0f, - 1.108f, 0.0f, 0.3147f, 0.9876f, 1.0f, - 1.1172f, 0.0f, 0.2944f, 0.9942f, 1.0f, - 1.1235f, 0.0f, 0.2723f, 0.9983f, 1.0f, - 1.1267f, 0.0f, 0.2485f, 0.9997f, 1.0f, - 1.1269f, 0.0f, 0.2233f, 0.999f, 1.0f, - 1.1237f, 0.0f, 0.197f, 0.9963f, 1.0f, - 1.1172f, 0.0f, 0.1697f, 0.9912f, 1.0f, - 1.1071f, 0.0f, 0.1417f, 0.9834f, 1.0f, - 1.0934f, 0.0f, 0.1131f, 0.9724f, 1.0f, - 1.0759f, 0.0f, 0.0842f, 0.9576f, 1.0f, - 1.0545f, 0.0f, 0.0553f, 0.9385f, 1.0f, - 1.0291f, 0.0f, 0.0265f, 0.9143f, 1.0f, - 0.9995f, 0.0f, -0.0019f, 0.8841f, 1.0f, - 0.9657f, 0.0f, -0.0298f, 0.8461f, 1.0f, - 0.9275f, 0.0f, -0.0569f, 0.7979f, 1.0f, - 0.8847f, 0.0f, -0.0829f, 0.7359f, 1.0f, - 0.8373f, 0.0f, -0.1077f, 0.6541f, 1.0f, - 0.7852f, 0.0f, -0.1311f, 0.5396f, 1.0f, - 0.7281f, 0.0f, -0.1528f, 0.36f, 1.0f, - 0.6661f, 0.0f, -0.1725f, 0.1f, 1.0f, + 0.7993f, 0.0f, 0.3242f, 0.1f, 1.0f, 0.8169f, 0.0f, 0.3354f, 0.2199f, 1.0f, + 0.8512f, 0.0f, 0.3542f, 0.4615f, 1.0f, 0.884f, 0.0f, 0.3686f, 0.6019f, 1.0f, + 0.9153f, 0.0f, 0.3787f, 0.6981f, 1.0f, 0.945f, 0.0f, 0.3848f, 0.7689f, 1.0f, + 0.9729f, 0.0f, 0.3871f, 0.8236f, 1.0f, 0.9989f, 0.0f, 0.3857f, 0.866f, 1.0f, + 1.0229f, 0.0f, 0.3811f, 0.9003f, 1.0f, 1.0447f, 0.0f, 0.3732f, 0.9272f, 1.0f, + 1.0643f, 0.0f, 0.3624f, 0.9485f, 1.0f, 1.0814f, 0.0f, 0.349f, 0.9653f, 1.0f, + 1.096f, 0.0f, 0.333f, 0.9781f, 1.0f, 1.108f, 0.0f, 0.3147f, 0.9876f, 1.0f, + 1.1172f, 0.0f, 0.2944f, 0.9942f, 1.0f, 1.1235f, 0.0f, 0.2723f, 0.9983f, 1.0f, + 1.1267f, 0.0f, 0.2485f, 0.9997f, 1.0f, 1.1269f, 0.0f, 0.2233f, 0.999f, 1.0f, + 1.1237f, 0.0f, 0.197f, 0.9963f, 1.0f, 1.1172f, 0.0f, 0.1697f, 0.9912f, 1.0f, + 1.1071f, 0.0f, 0.1417f, 0.9834f, 1.0f, 1.0934f, 0.0f, 0.1131f, 0.9724f, 1.0f, + 1.0759f, 0.0f, 0.0842f, 0.9576f, 1.0f, 1.0545f, 0.0f, 0.0553f, 0.9385f, 1.0f, + 1.0291f, 0.0f, 0.0265f, 0.9143f, 1.0f, 0.9995f, 0.0f, -0.0019f, 0.8841f, 1.0f, + 0.9657f, 0.0f, -0.0298f, 0.8461f, 1.0f, 0.9275f, 0.0f, -0.0569f, 0.7979f, 1.0f, + 0.8847f, 0.0f, -0.0829f, 0.7359f, 1.0f, 0.8373f, 0.0f, -0.1077f, 0.6541f, 1.0f, + 0.7852f, 0.0f, -0.1311f, 0.5396f, 1.0f, 0.7281f, 0.0f, -0.1528f, 0.36f, 1.0f, + 0.6661f, 0.0f, -0.1725f, 0.1f, 1.0f, }; static const float data21[64 * GP_PRIM_DATABUF_SIZE] = { - -0.7428f, 0.0f, 0.2977f, 0.1f, 1.0f, - -0.7608f, 0.0f, 0.3138f, 0.2199f, 1.0f, - -0.7786f, 0.0f, 0.3284f, 0.36f, 1.0f, - -0.7962f, 0.0f, 0.3414f, 0.4615f, 1.0f, - -0.8135f, 0.0f, 0.3531f, 0.5396f, 1.0f, - -0.8306f, 0.0f, 0.3633f, 0.6019f, 1.0f, - -0.8473f, 0.0f, 0.3722f, 0.6541f, 1.0f, - -0.8637f, 0.0f, 0.3798f, 0.6981f, 1.0f, - -0.8798f, 0.0f, 0.386f, 0.7359f, 1.0f, - -0.8956f, 0.0f, 0.3911f, 0.7689f, 1.0f, - -0.9109f, 0.0f, 0.3949f, 0.7979f, 1.0f, - -0.9259f, 0.0f, 0.3975f, 0.8236f, 1.0f, - -0.9405f, 0.0f, 0.3989f, 0.8461f, 1.0f, - -0.9546f, 0.0f, 0.3993f, 0.866f, 1.0f, - -0.9682f, 0.0f, 0.3986f, 0.8841f, 1.0f, - -0.9814f, 0.0f, 0.3969f, 0.9003f, 1.0f, - -0.994f, 0.0f, 0.3941f, 0.9143f, 1.0f, - -1.0062f, 0.0f, 0.3904f, 0.9272f, 1.0f, - -1.0178f, 0.0f, 0.3858f, 0.9385f, 1.0f, - -1.0288f, 0.0f, 0.3803f, 0.9485f, 1.0f, - -1.0393f, 0.0f, 0.3739f, 0.9576f, 1.0f, - -1.0492f, 0.0f, 0.3668f, 0.9653f, 1.0f, - -1.0584f, 0.0f, 0.3588f, 0.9724f, 1.0f, - -1.067f, 0.0f, 0.3501f, 0.9781f, 1.0f, - -1.075f, 0.0f, 0.3407f, 0.9834f, 1.0f, - -1.0822f, 0.0f, 0.3307f, 0.9876f, 1.0f, - -1.0888f, 0.0f, 0.32f, 0.9912f, 1.0f, - -1.0946f, 0.0f, 0.3087f, 0.9942f, 1.0f, - -1.0997f, 0.0f, 0.2969f, 0.9963f, 1.0f, - -1.104f, 0.0f, 0.2845f, 0.9983f, 1.0f, - -1.1075f, 0.0f, 0.2717f, 0.999f, 1.0f, - -1.1103f, 0.0f, 0.2584f, 0.9997f, 1.0f, - -1.1122f, 0.0f, 0.2447f, 0.9997f, 1.0f, - -1.1132f, 0.0f, 0.2306f, 0.999f, 1.0f, - -1.1134f, 0.0f, 0.2162f, 0.9983f, 1.0f, - -1.1128f, 0.0f, 0.2015f, 0.9963f, 1.0f, - -1.1112f, 0.0f, 0.1865f, 0.9942f, 1.0f, - -1.1086f, 0.0f, 0.1713f, 0.9912f, 1.0f, - -1.1052f, 0.0f, 0.1559f, 0.9876f, 1.0f, - -1.1007f, 0.0f, 0.1404f, 0.9834f, 1.0f, - -1.0953f, 0.0f, 0.1247f, 0.9781f, 1.0f, - -1.0889f, 0.0f, 0.109f, 0.9724f, 1.0f, - -1.0814f, 0.0f, 0.0932f, 0.9653f, 1.0f, - -1.0729f, 0.0f, 0.0774f, 0.9576f, 1.0f, - -1.0633f, 0.0f, 0.0617f, 0.9485f, 1.0f, - -1.0527f, 0.0f, 0.046f, 0.9385f, 1.0f, - -1.0409f, 0.0f, 0.0304f, 0.9272f, 1.0f, - -1.028f, 0.0f, 0.015f, 0.9143f, 1.0f, - -1.0139f, 0.0f, -0.0003f, 0.9003f, 1.0f, - -0.9987f, 0.0f, -0.0154f, 0.8841f, 1.0f, - -0.9823f, 0.0f, -0.0302f, 0.866f, 1.0f, - -0.9646f, 0.0f, -0.0447f, 0.8461f, 1.0f, - -0.9458f, 0.0f, -0.0589f, 0.8236f, 1.0f, - -0.9257f, 0.0f, -0.0727f, 0.7979f, 1.0f, - -0.9043f, 0.0f, -0.0862f, 0.7689f, 1.0f, - -0.8816f, 0.0f, -0.0992f, 0.7359f, 1.0f, - -0.8576f, 0.0f, -0.1117f, 0.6981f, 1.0f, - -0.8323f, 0.0f, -0.1237f, 0.6541f, 1.0f, - -0.8056f, 0.0f, -0.1352f, 0.6019f, 1.0f, - -0.7775f, 0.0f, -0.1461f, 0.5396f, 1.0f, - -0.7481f, 0.0f, -0.1564f, 0.4615f, 1.0f, - -0.7172f, 0.0f, -0.166f, 0.36f, 1.0f, - -0.6849f, 0.0f, -0.175f, 0.2199f, 1.0f, - -0.6512f, 0.0f, -0.1832f, 0.1f, 1.0f, + -0.7428f, 0.0f, 0.2977f, 0.1f, 1.0f, -0.7608f, 0.0f, 0.3138f, 0.2199f, 1.0f, + -0.7786f, 0.0f, 0.3284f, 0.36f, 1.0f, -0.7962f, 0.0f, 0.3414f, 0.4615f, 1.0f, + -0.8135f, 0.0f, 0.3531f, 0.5396f, 1.0f, -0.8306f, 0.0f, 0.3633f, 0.6019f, 1.0f, + -0.8473f, 0.0f, 0.3722f, 0.6541f, 1.0f, -0.8637f, 0.0f, 0.3798f, 0.6981f, 1.0f, + -0.8798f, 0.0f, 0.386f, 0.7359f, 1.0f, -0.8956f, 0.0f, 0.3911f, 0.7689f, 1.0f, + -0.9109f, 0.0f, 0.3949f, 0.7979f, 1.0f, -0.9259f, 0.0f, 0.3975f, 0.8236f, 1.0f, + -0.9405f, 0.0f, 0.3989f, 0.8461f, 1.0f, -0.9546f, 0.0f, 0.3993f, 0.866f, 1.0f, + -0.9682f, 0.0f, 0.3986f, 0.8841f, 1.0f, -0.9814f, 0.0f, 0.3969f, 0.9003f, 1.0f, + -0.994f, 0.0f, 0.3941f, 0.9143f, 1.0f, -1.0062f, 0.0f, 0.3904f, 0.9272f, 1.0f, + -1.0178f, 0.0f, 0.3858f, 0.9385f, 1.0f, -1.0288f, 0.0f, 0.3803f, 0.9485f, 1.0f, + -1.0393f, 0.0f, 0.3739f, 0.9576f, 1.0f, -1.0492f, 0.0f, 0.3668f, 0.9653f, 1.0f, + -1.0584f, 0.0f, 0.3588f, 0.9724f, 1.0f, -1.067f, 0.0f, 0.3501f, 0.9781f, 1.0f, + -1.075f, 0.0f, 0.3407f, 0.9834f, 1.0f, -1.0822f, 0.0f, 0.3307f, 0.9876f, 1.0f, + -1.0888f, 0.0f, 0.32f, 0.9912f, 1.0f, -1.0946f, 0.0f, 0.3087f, 0.9942f, 1.0f, + -1.0997f, 0.0f, 0.2969f, 0.9963f, 1.0f, -1.104f, 0.0f, 0.2845f, 0.9983f, 1.0f, + -1.1075f, 0.0f, 0.2717f, 0.999f, 1.0f, -1.1103f, 0.0f, 0.2584f, 0.9997f, 1.0f, + -1.1122f, 0.0f, 0.2447f, 0.9997f, 1.0f, -1.1132f, 0.0f, 0.2306f, 0.999f, 1.0f, + -1.1134f, 0.0f, 0.2162f, 0.9983f, 1.0f, -1.1128f, 0.0f, 0.2015f, 0.9963f, 1.0f, + -1.1112f, 0.0f, 0.1865f, 0.9942f, 1.0f, -1.1086f, 0.0f, 0.1713f, 0.9912f, 1.0f, + -1.1052f, 0.0f, 0.1559f, 0.9876f, 1.0f, -1.1007f, 0.0f, 0.1404f, 0.9834f, 1.0f, + -1.0953f, 0.0f, 0.1247f, 0.9781f, 1.0f, -1.0889f, 0.0f, 0.109f, 0.9724f, 1.0f, + -1.0814f, 0.0f, 0.0932f, 0.9653f, 1.0f, -1.0729f, 0.0f, 0.0774f, 0.9576f, 1.0f, + -1.0633f, 0.0f, 0.0617f, 0.9485f, 1.0f, -1.0527f, 0.0f, 0.046f, 0.9385f, 1.0f, + -1.0409f, 0.0f, 0.0304f, 0.9272f, 1.0f, -1.028f, 0.0f, 0.015f, 0.9143f, 1.0f, + -1.0139f, 0.0f, -0.0003f, 0.9003f, 1.0f, -0.9987f, 0.0f, -0.0154f, 0.8841f, 1.0f, + -0.9823f, 0.0f, -0.0302f, 0.866f, 1.0f, -0.9646f, 0.0f, -0.0447f, 0.8461f, 1.0f, + -0.9458f, 0.0f, -0.0589f, 0.8236f, 1.0f, -0.9257f, 0.0f, -0.0727f, 0.7979f, 1.0f, + -0.9043f, 0.0f, -0.0862f, 0.7689f, 1.0f, -0.8816f, 0.0f, -0.0992f, 0.7359f, 1.0f, + -0.8576f, 0.0f, -0.1117f, 0.6981f, 1.0f, -0.8323f, 0.0f, -0.1237f, 0.6541f, 1.0f, + -0.8056f, 0.0f, -0.1352f, 0.6019f, 1.0f, -0.7775f, 0.0f, -0.1461f, 0.5396f, 1.0f, + -0.7481f, 0.0f, -0.1564f, 0.4615f, 1.0f, -0.7172f, 0.0f, -0.166f, 0.36f, 1.0f, + -0.6849f, 0.0f, -0.175f, 0.2199f, 1.0f, -0.6512f, 0.0f, -0.1832f, 0.1f, 1.0f, }; static const float data22[26 * GP_PRIM_DATABUF_SIZE] = { - 0.2721f, 0.0f, 0.2084f, 1.0f, 1.0f, - 0.2721f, 0.0f, 0.2112f, 1.0f, 1.0f, - 0.2739f, 0.0f, 0.2223f, 1.0f, 1.0f, - 0.2778f, 0.0f, 0.2324f, 1.0f, 1.0f, - 0.2836f, 0.0f, 0.2409f, 1.0f, 1.0f, - 0.291f, 0.0f, 0.2471f, 1.0f, 1.0f, - 0.2994f, 0.0f, 0.2507f, 1.0f, 1.0f, - 0.3082f, 0.0f, 0.2515f, 1.0f, 1.0f, - 0.3169f, 0.0f, 0.2493f, 1.0f, 1.0f, - 0.3248f, 0.0f, 0.2443f, 1.0f, 1.0f, - 0.3315f, 0.0f, 0.2369f, 1.0f, 1.0f, - 0.3364f, 0.0f, 0.2275f, 1.0f, 1.0f, - 0.3392f, 0.0f, 0.2168f, 1.0f, 1.0f, - 0.3398f, 0.0f, 0.2056f, 1.0f, 1.0f, - 0.3381f, 0.0f, 0.1945f, 1.0f, 1.0f, - 0.3342f, 0.0f, 0.1844f, 1.0f, 1.0f, - 0.3283f, 0.0f, 0.176f, 1.0f, 1.0f, - 0.321f, 0.0f, 0.1697f, 1.0f, 1.0f, - 0.3126f, 0.0f, 0.1661f, 1.0f, 1.0f, - 0.3038f, 0.0f, 0.1653f, 1.0f, 1.0f, - 0.2951f, 0.0f, 0.1675f, 1.0f, 1.0f, - 0.2871f, 0.0f, 0.1725f, 1.0f, 1.0f, - 0.2805f, 0.0f, 0.1799f, 1.0f, 1.0f, - 0.2756f, 0.0f, 0.1893f, 1.0f, 1.0f, - 0.2727f, 0.0f, 0.2f, 1.0f, 1.0f, - 0.2721f, 0.0f, 0.2056f, 1.0f, 1.0f, + 0.2721f, 0.0f, 0.2084f, 1.0f, 1.0f, 0.2721f, 0.0f, 0.2112f, 1.0f, 1.0f, + 0.2739f, 0.0f, 0.2223f, 1.0f, 1.0f, 0.2778f, 0.0f, 0.2324f, 1.0f, 1.0f, + 0.2836f, 0.0f, 0.2409f, 1.0f, 1.0f, 0.291f, 0.0f, 0.2471f, 1.0f, 1.0f, + 0.2994f, 0.0f, 0.2507f, 1.0f, 1.0f, 0.3082f, 0.0f, 0.2515f, 1.0f, 1.0f, + 0.3169f, 0.0f, 0.2493f, 1.0f, 1.0f, 0.3248f, 0.0f, 0.2443f, 1.0f, 1.0f, + 0.3315f, 0.0f, 0.2369f, 1.0f, 1.0f, 0.3364f, 0.0f, 0.2275f, 1.0f, 1.0f, + 0.3392f, 0.0f, 0.2168f, 1.0f, 1.0f, 0.3398f, 0.0f, 0.2056f, 1.0f, 1.0f, + 0.3381f, 0.0f, 0.1945f, 1.0f, 1.0f, 0.3342f, 0.0f, 0.1844f, 1.0f, 1.0f, + 0.3283f, 0.0f, 0.176f, 1.0f, 1.0f, 0.321f, 0.0f, 0.1697f, 1.0f, 1.0f, + 0.3126f, 0.0f, 0.1661f, 1.0f, 1.0f, 0.3038f, 0.0f, 0.1653f, 1.0f, 1.0f, + 0.2951f, 0.0f, 0.1675f, 1.0f, 1.0f, 0.2871f, 0.0f, 0.1725f, 1.0f, 1.0f, + 0.2805f, 0.0f, 0.1799f, 1.0f, 1.0f, 0.2756f, 0.0f, 0.1893f, 1.0f, 1.0f, + 0.2727f, 0.0f, 0.2f, 1.0f, 1.0f, 0.2721f, 0.0f, 0.2056f, 1.0f, 1.0f, }; static const float data23[26 * GP_PRIM_DATABUF_SIZE] = { - -0.3545f, 0.0f, 0.2084f, 1.0f, 1.0f, - -0.3544f, 0.0f, 0.2112f, 1.0f, 1.0f, - -0.3527f, 0.0f, 0.2223f, 1.0f, 1.0f, - -0.3488f, 0.0f, 0.2324f, 1.0f, 1.0f, - -0.343f, 0.0f, 0.2409f, 1.0f, 1.0f, - -0.3356f, 0.0f, 0.2471f, 1.0f, 1.0f, - -0.3272f, 0.0f, 0.2507f, 1.0f, 1.0f, - -0.3184f, 0.0f, 0.2515f, 1.0f, 1.0f, - -0.3097f, 0.0f, 0.2493f, 1.0f, 1.0f, - -0.3018f, 0.0f, 0.2443f, 1.0f, 1.0f, - -0.2951f, 0.0f, 0.2369f, 1.0f, 1.0f, - -0.2902f, 0.0f, 0.2275f, 1.0f, 1.0f, - -0.2874f, 0.0f, 0.2168f, 1.0f, 1.0f, - -0.2868f, 0.0f, 0.2056f, 1.0f, 1.0f, - -0.2885f, 0.0f, 0.1945f, 1.0f, 1.0f, - -0.2924f, 0.0f, 0.1844f, 1.0f, 1.0f, - -0.2983f, 0.0f, 0.176f, 1.0f, 1.0f, - -0.3056f, 0.0f, 0.1697f, 1.0f, 1.0f, - -0.314f, 0.0f, 0.1661f, 1.0f, 1.0f, - -0.3228f, 0.0f, 0.1653f, 1.0f, 1.0f, - -0.3315f, 0.0f, 0.1675f, 1.0f, 1.0f, - -0.3394f, 0.0f, 0.1725f, 1.0f, 1.0f, - -0.3461f, 0.0f, 0.1799f, 1.0f, 1.0f, - -0.351f, 0.0f, 0.1893f, 1.0f, 1.0f, - -0.3539f, 0.0f, 0.2f, 1.0f, 1.0f, - -0.3544f, 0.0f, 0.2056f, 1.0f, 1.0f, + -0.3545f, 0.0f, 0.2084f, 1.0f, 1.0f, -0.3544f, 0.0f, 0.2112f, 1.0f, 1.0f, + -0.3527f, 0.0f, 0.2223f, 1.0f, 1.0f, -0.3488f, 0.0f, 0.2324f, 1.0f, 1.0f, + -0.343f, 0.0f, 0.2409f, 1.0f, 1.0f, -0.3356f, 0.0f, 0.2471f, 1.0f, 1.0f, + -0.3272f, 0.0f, 0.2507f, 1.0f, 1.0f, -0.3184f, 0.0f, 0.2515f, 1.0f, 1.0f, + -0.3097f, 0.0f, 0.2493f, 1.0f, 1.0f, -0.3018f, 0.0f, 0.2443f, 1.0f, 1.0f, + -0.2951f, 0.0f, 0.2369f, 1.0f, 1.0f, -0.2902f, 0.0f, 0.2275f, 1.0f, 1.0f, + -0.2874f, 0.0f, 0.2168f, 1.0f, 1.0f, -0.2868f, 0.0f, 0.2056f, 1.0f, 1.0f, + -0.2885f, 0.0f, 0.1945f, 1.0f, 1.0f, -0.2924f, 0.0f, 0.1844f, 1.0f, 1.0f, + -0.2983f, 0.0f, 0.176f, 1.0f, 1.0f, -0.3056f, 0.0f, 0.1697f, 1.0f, 1.0f, + -0.314f, 0.0f, 0.1661f, 1.0f, 1.0f, -0.3228f, 0.0f, 0.1653f, 1.0f, 1.0f, + -0.3315f, 0.0f, 0.1675f, 1.0f, 1.0f, -0.3394f, 0.0f, 0.1725f, 1.0f, 1.0f, + -0.3461f, 0.0f, 0.1799f, 1.0f, 1.0f, -0.351f, 0.0f, 0.1893f, 1.0f, 1.0f, + -0.3539f, 0.0f, 0.2f, 1.0f, 1.0f, -0.3544f, 0.0f, 0.2056f, 1.0f, 1.0f, }; static const float data24[33 * GP_PRIM_DATABUF_SIZE] = { - 0.1904f, 0.0f, 0.0983f, 0.1f, 1.0f, - 0.1997f, 0.0f, 0.0774f, 0.2199f, 1.0f, - 0.2234f, 0.0f, 0.0407f, 0.4615f, 1.0f, - 0.2477f, 0.0f, 0.0158f, 0.6019f, 1.0f, - 0.2767f, 0.0f, -0.0047f, 0.6981f, 1.0f, - 0.3061f, 0.0f, -0.0179f, 0.7689f, 1.0f, - 0.3346f, 0.0f, -0.0247f, 0.8236f, 1.0f, - 0.362f, 0.0f, -0.0262f, 0.866f, 1.0f, - 0.3881f, 0.0f, -0.0231f, 0.9003f, 1.0f, - 0.4128f, 0.0f, -0.0159f, 0.9272f, 1.0f, - 0.436f, 0.0f, -0.0049f, 0.9485f, 1.0f, - 0.4577f, 0.0f, 0.0099f, 0.9653f, 1.0f, - 0.4778f, 0.0f, 0.0289f, 0.9781f, 1.0f, - 0.4959f, 0.0f, 0.0524f, 0.9876f, 1.0f, - 0.5114f, 0.0f, 0.0806f, 0.9942f, 1.0f, - 0.5229f, 0.0f, 0.1134f, 0.9983f, 1.0f, - 0.5291f, 0.0f, 0.1498f, 0.9997f, 1.0f, - 0.5289f, 0.0f, 0.1884f, 0.999f, 1.0f, - 0.5219f, 0.0f, 0.2267f, 0.9963f, 1.0f, - 0.5087f, 0.0f, 0.2616f, 0.9912f, 1.0f, - 0.4907f, 0.0f, 0.2916f, 0.9834f, 1.0f, - 0.4697f, 0.0f, 0.3159f, 0.9724f, 1.0f, - 0.4465f, 0.0f, 0.3349f, 0.9576f, 1.0f, - 0.422f, 0.0f, 0.3489f, 0.9385f, 1.0f, - 0.3964f, 0.0f, 0.3582f, 0.9143f, 1.0f, - 0.3705f, 0.0f, 0.3628f, 0.8841f, 1.0f, - 0.3436f, 0.0f, 0.363f, 0.8461f, 1.0f, - 0.3166f, 0.0f, 0.3586f, 0.7979f, 1.0f, - 0.2897f, 0.0f, 0.3491f, 0.7359f, 1.0f, - 0.2635f, 0.0f, 0.3344f, 0.6541f, 1.0f, - 0.2382f, 0.0f, 0.3137f, 0.5396f, 1.0f, - 0.2162f, 0.0f, 0.2882f, 0.36f, 1.0f, - 0.1949f, 0.0f, 0.2516f, 0.1f, 1.0f, + 0.1904f, 0.0f, 0.0983f, 0.1f, 1.0f, 0.1997f, 0.0f, 0.0774f, 0.2199f, 1.0f, + 0.2234f, 0.0f, 0.0407f, 0.4615f, 1.0f, 0.2477f, 0.0f, 0.0158f, 0.6019f, 1.0f, + 0.2767f, 0.0f, -0.0047f, 0.6981f, 1.0f, 0.3061f, 0.0f, -0.0179f, 0.7689f, 1.0f, + 0.3346f, 0.0f, -0.0247f, 0.8236f, 1.0f, 0.362f, 0.0f, -0.0262f, 0.866f, 1.0f, + 0.3881f, 0.0f, -0.0231f, 0.9003f, 1.0f, 0.4128f, 0.0f, -0.0159f, 0.9272f, 1.0f, + 0.436f, 0.0f, -0.0049f, 0.9485f, 1.0f, 0.4577f, 0.0f, 0.0099f, 0.9653f, 1.0f, + 0.4778f, 0.0f, 0.0289f, 0.9781f, 1.0f, 0.4959f, 0.0f, 0.0524f, 0.9876f, 1.0f, + 0.5114f, 0.0f, 0.0806f, 0.9942f, 1.0f, 0.5229f, 0.0f, 0.1134f, 0.9983f, 1.0f, + 0.5291f, 0.0f, 0.1498f, 0.9997f, 1.0f, 0.5289f, 0.0f, 0.1884f, 0.999f, 1.0f, + 0.5219f, 0.0f, 0.2267f, 0.9963f, 1.0f, 0.5087f, 0.0f, 0.2616f, 0.9912f, 1.0f, + 0.4907f, 0.0f, 0.2916f, 0.9834f, 1.0f, 0.4697f, 0.0f, 0.3159f, 0.9724f, 1.0f, + 0.4465f, 0.0f, 0.3349f, 0.9576f, 1.0f, 0.422f, 0.0f, 0.3489f, 0.9385f, 1.0f, + 0.3964f, 0.0f, 0.3582f, 0.9143f, 1.0f, 0.3705f, 0.0f, 0.3628f, 0.8841f, 1.0f, + 0.3436f, 0.0f, 0.363f, 0.8461f, 1.0f, 0.3166f, 0.0f, 0.3586f, 0.7979f, 1.0f, + 0.2897f, 0.0f, 0.3491f, 0.7359f, 1.0f, 0.2635f, 0.0f, 0.3344f, 0.6541f, 1.0f, + 0.2382f, 0.0f, 0.3137f, 0.5396f, 1.0f, 0.2162f, 0.0f, 0.2882f, 0.36f, 1.0f, + 0.1949f, 0.0f, 0.2516f, 0.1f, 1.0f, }; static const float data25[18 * GP_PRIM_DATABUF_SIZE] = { - -1.0361f, 0.0f, 0.1745f, 0.1f, 1.0f, - -1.0319f, 0.0f, 0.177f, 0.2199f, 1.0f, - -1.0153f, 0.0f, 0.1852f, 0.6019f, 1.0f, - -0.999f, 0.0f, 0.1907f, 0.7689f, 1.0f, - -0.983f, 0.0f, 0.1936f, 0.866f, 1.0f, - -0.9673f, 0.0f, 0.1941f, 0.9272f, 1.0f, - -0.9522f, 0.0f, 0.1923f, 0.9653f, 1.0f, - -0.9376f, 0.0f, 0.1885f, 0.9876f, 1.0f, - -0.9236f, 0.0f, 0.1827f, 0.9983f, 1.0f, - -0.9103f, 0.0f, 0.1752f, 0.999f, 1.0f, - -0.8978f, 0.0f, 0.1662f, 0.9912f, 1.0f, - -0.8862f, 0.0f, 0.1558f, 0.9724f, 1.0f, - -0.8756f, 0.0f, 0.1441f, 0.9385f, 1.0f, - -0.866f, 0.0f, 0.1314f, 0.8841f, 1.0f, - -0.8575f, 0.0f, 0.1179f, 0.7979f, 1.0f, - -0.8501f, 0.0f, 0.1036f, 0.6541f, 1.0f, - -0.8441f, 0.0f, 0.0889f, 0.36f, 1.0f, - -0.8416f, 0.0f, 0.0813f, 0.1f, 1.0f, + -1.0361f, 0.0f, 0.1745f, 0.1f, 1.0f, -1.0319f, 0.0f, 0.177f, 0.2199f, 1.0f, + -1.0153f, 0.0f, 0.1852f, 0.6019f, 1.0f, -0.999f, 0.0f, 0.1907f, 0.7689f, 1.0f, + -0.983f, 0.0f, 0.1936f, 0.866f, 1.0f, -0.9673f, 0.0f, 0.1941f, 0.9272f, 1.0f, + -0.9522f, 0.0f, 0.1923f, 0.9653f, 1.0f, -0.9376f, 0.0f, 0.1885f, 0.9876f, 1.0f, + -0.9236f, 0.0f, 0.1827f, 0.9983f, 1.0f, -0.9103f, 0.0f, 0.1752f, 0.999f, 1.0f, + -0.8978f, 0.0f, 0.1662f, 0.9912f, 1.0f, -0.8862f, 0.0f, 0.1558f, 0.9724f, 1.0f, + -0.8756f, 0.0f, 0.1441f, 0.9385f, 1.0f, -0.866f, 0.0f, 0.1314f, 0.8841f, 1.0f, + -0.8575f, 0.0f, 0.1179f, 0.7979f, 1.0f, -0.8501f, 0.0f, 0.1036f, 0.6541f, 1.0f, + -0.8441f, 0.0f, 0.0889f, 0.36f, 1.0f, -0.8416f, 0.0f, 0.0813f, 0.1f, 1.0f, }; static const float data26[18 * GP_PRIM_DATABUF_SIZE] = { - 0.9747f, 0.0f, 0.1745f, 0.1f, 1.0f, - 0.9706f, 0.0f, 0.177f, 0.2199f, 1.0f, - 0.954f, 0.0f, 0.1852f, 0.6019f, 1.0f, - 0.9377f, 0.0f, 0.1907f, 0.7689f, 1.0f, - 0.9216f, 0.0f, 0.1936f, 0.866f, 1.0f, - 0.906f, 0.0f, 0.1941f, 0.9272f, 1.0f, - 0.8908f, 0.0f, 0.1923f, 0.9653f, 1.0f, - 0.8762f, 0.0f, 0.1885f, 0.9876f, 1.0f, - 0.8622f, 0.0f, 0.1827f, 0.9983f, 1.0f, - 0.849f, 0.0f, 0.1752f, 0.999f, 1.0f, - 0.8365f, 0.0f, 0.1662f, 0.9912f, 1.0f, - 0.8249f, 0.0f, 0.1558f, 0.9724f, 1.0f, - 0.8143f, 0.0f, 0.1441f, 0.9385f, 1.0f, - 0.8046f, 0.0f, 0.1314f, 0.8841f, 1.0f, - 0.7961f, 0.0f, 0.1179f, 0.7979f, 1.0f, - 0.7888f, 0.0f, 0.1036f, 0.6541f, 1.0f, - 0.7828f, 0.0f, 0.0889f, 0.36f, 1.0f, - 0.7802f, 0.0f, 0.0813f, 0.1f, 1.0f, + 0.9747f, 0.0f, 0.1745f, 0.1f, 1.0f, 0.9706f, 0.0f, 0.177f, 0.2199f, 1.0f, + 0.954f, 0.0f, 0.1852f, 0.6019f, 1.0f, 0.9377f, 0.0f, 0.1907f, 0.7689f, 1.0f, + 0.9216f, 0.0f, 0.1936f, 0.866f, 1.0f, 0.906f, 0.0f, 0.1941f, 0.9272f, 1.0f, + 0.8908f, 0.0f, 0.1923f, 0.9653f, 1.0f, 0.8762f, 0.0f, 0.1885f, 0.9876f, 1.0f, + 0.8622f, 0.0f, 0.1827f, 0.9983f, 1.0f, 0.849f, 0.0f, 0.1752f, 0.999f, 1.0f, + 0.8365f, 0.0f, 0.1662f, 0.9912f, 1.0f, 0.8249f, 0.0f, 0.1558f, 0.9724f, 1.0f, + 0.8143f, 0.0f, 0.1441f, 0.9385f, 1.0f, 0.8046f, 0.0f, 0.1314f, 0.8841f, 1.0f, + 0.7961f, 0.0f, 0.1179f, 0.7979f, 1.0f, 0.7888f, 0.0f, 0.1036f, 0.6541f, 1.0f, + 0.7828f, 0.0f, 0.0889f, 0.36f, 1.0f, 0.7802f, 0.0f, 0.0813f, 0.1f, 1.0f, }; static const float data27[33 * GP_PRIM_DATABUF_SIZE] = { - -0.186f, 0.0f, 0.0983f, 0.1f, 1.0f, - -0.1954f, 0.0f, 0.0774f, 0.2199f, 1.0f, - -0.2191f, 0.0f, 0.0407f, 0.4615f, 1.0f, - -0.2434f, 0.0f, 0.0158f, 0.6019f, 1.0f, - -0.2723f, 0.0f, -0.0047f, 0.6981f, 1.0f, - -0.3017f, 0.0f, -0.0179f, 0.7689f, 1.0f, - -0.3302f, 0.0f, -0.0247f, 0.8236f, 1.0f, - -0.3576f, 0.0f, -0.0262f, 0.866f, 1.0f, - -0.3837f, 0.0f, -0.0231f, 0.9003f, 1.0f, - -0.4085f, 0.0f, -0.0159f, 0.9272f, 1.0f, - -0.4317f, 0.0f, -0.0049f, 0.9485f, 1.0f, - -0.4533f, 0.0f, 0.0099f, 0.9653f, 1.0f, - -0.4734f, 0.0f, 0.0289f, 0.9781f, 1.0f, - -0.4916f, 0.0f, 0.0524f, 0.9876f, 1.0f, - -0.507f, 0.0f, 0.0806f, 0.9942f, 1.0f, - -0.5185f, 0.0f, 0.1134f, 0.9983f, 1.0f, - -0.5247f, 0.0f, 0.1498f, 0.9997f, 1.0f, - -0.5246f, 0.0f, 0.1884f, 0.999f, 1.0f, - -0.5175f, 0.0f, 0.2267f, 0.9963f, 1.0f, - -0.5043f, 0.0f, 0.2616f, 0.9912f, 1.0f, - -0.4864f, 0.0f, 0.2916f, 0.9834f, 1.0f, - -0.4653f, 0.0f, 0.3159f, 0.9724f, 1.0f, - -0.4422f, 0.0f, 0.3349f, 0.9576f, 1.0f, - -0.4177f, 0.0f, 0.3489f, 0.9385f, 1.0f, - -0.3921f, 0.0f, 0.3582f, 0.9143f, 1.0f, - -0.3661f, 0.0f, 0.3628f, 0.8841f, 1.0f, - -0.3392f, 0.0f, 0.363f, 0.8461f, 1.0f, - -0.3123f, 0.0f, 0.3586f, 0.7979f, 1.0f, - -0.2853f, 0.0f, 0.3491f, 0.7359f, 1.0f, - -0.2591f, 0.0f, 0.3344f, 0.6541f, 1.0f, - -0.2339f, 0.0f, 0.3137f, 0.5396f, 1.0f, - -0.2118f, 0.0f, 0.2882f, 0.36f, 1.0f, - -0.1906f, 0.0f, 0.2516f, 0.1f, 1.0f, + -0.186f, 0.0f, 0.0983f, 0.1f, 1.0f, -0.1954f, 0.0f, 0.0774f, 0.2199f, 1.0f, + -0.2191f, 0.0f, 0.0407f, 0.4615f, 1.0f, -0.2434f, 0.0f, 0.0158f, 0.6019f, 1.0f, + -0.2723f, 0.0f, -0.0047f, 0.6981f, 1.0f, -0.3017f, 0.0f, -0.0179f, 0.7689f, 1.0f, + -0.3302f, 0.0f, -0.0247f, 0.8236f, 1.0f, -0.3576f, 0.0f, -0.0262f, 0.866f, 1.0f, + -0.3837f, 0.0f, -0.0231f, 0.9003f, 1.0f, -0.4085f, 0.0f, -0.0159f, 0.9272f, 1.0f, + -0.4317f, 0.0f, -0.0049f, 0.9485f, 1.0f, -0.4533f, 0.0f, 0.0099f, 0.9653f, 1.0f, + -0.4734f, 0.0f, 0.0289f, 0.9781f, 1.0f, -0.4916f, 0.0f, 0.0524f, 0.9876f, 1.0f, + -0.507f, 0.0f, 0.0806f, 0.9942f, 1.0f, -0.5185f, 0.0f, 0.1134f, 0.9983f, 1.0f, + -0.5247f, 0.0f, 0.1498f, 0.9997f, 1.0f, -0.5246f, 0.0f, 0.1884f, 0.999f, 1.0f, + -0.5175f, 0.0f, 0.2267f, 0.9963f, 1.0f, -0.5043f, 0.0f, 0.2616f, 0.9912f, 1.0f, + -0.4864f, 0.0f, 0.2916f, 0.9834f, 1.0f, -0.4653f, 0.0f, 0.3159f, 0.9724f, 1.0f, + -0.4422f, 0.0f, 0.3349f, 0.9576f, 1.0f, -0.4177f, 0.0f, 0.3489f, 0.9385f, 1.0f, + -0.3921f, 0.0f, 0.3582f, 0.9143f, 1.0f, -0.3661f, 0.0f, 0.3628f, 0.8841f, 1.0f, + -0.3392f, 0.0f, 0.363f, 0.8461f, 1.0f, -0.3123f, 0.0f, 0.3586f, 0.7979f, 1.0f, + -0.2853f, 0.0f, 0.3491f, 0.7359f, 1.0f, -0.2591f, 0.0f, 0.3344f, 0.6541f, 1.0f, + -0.2339f, 0.0f, 0.3137f, 0.5396f, 1.0f, -0.2118f, 0.0f, 0.2882f, 0.36f, 1.0f, + -0.1906f, 0.0f, 0.2516f, 0.1f, 1.0f, }; - /* ***************************************************************** */ /* Monkey Color Data */ static const ColorTemplate gp_monkey_pct_black = { - "Black", - {0.0f, 0.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_monkey_pct_skin = { - "Skin", - {0.733f, 0.569f, 0.361f, 1.0f}, - {0.745f, 0.502f, 0.278f, 1.0f}, + "Skin", + {0.733f, 0.569f, 0.361f, 1.0f}, + {0.745f, 0.502f, 0.278f, 1.0f}, }; static const ColorTemplate gp_monkey_pct_skin_light = { - "Skin_Light", - {0.914f, 0.827f, 0.635f, 1.0f}, - {0.913f, 0.828f, 0.637f, 0.0f}, + "Skin_Light", + {0.914f, 0.827f, 0.635f, 1.0f}, + {0.913f, 0.828f, 0.637f, 0.0f}, }; static const ColorTemplate gp_monkey_pct_skin_shadow = { - "Skin_Shadow", - {0.322f, 0.29f, 0.224f, 0.5f}, - {0.32f, 0.29f, 0.223f, 0.3f}, + "Skin_Shadow", + {0.322f, 0.29f, 0.224f, 0.5f}, + {0.32f, 0.29f, 0.223f, 0.3f}, }; static const ColorTemplate gp_monkey_pct_eyes = { - "Eyes", - {0.553f, 0.39f, 0.266f, 0.0f}, - {0.847f, 0.723f, 0.599f, 1.0f}, + "Eyes", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.847f, 0.723f, 0.599f, 1.0f}, }; static const ColorTemplate gp_monkey_pct_pupils = { - "Pupils", - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}, + "Pupils", + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}, }; /* ***************************************************************** */ @@ -1430,122 +828,121 @@ static const ColorTemplate gp_monkey_pct_pupils = { /* add a 2D Suzanne (original model created by Matias Mendiola) */ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPdata *gpd = (bGPdata *)ob->data; - bGPDstroke *gps; + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + bGPDstroke *gps; - /* create colors */ - int color_Black = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_black, true, false); - int color_Skin = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin, false, true); - int color_Skin_Light = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_light, true, false); - int color_Skin_Shadow = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_shadow, true, false); - int color_Eyes = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_eyes, false, true); - int color_Pupils = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_pupils, false, true); + /* create colors */ + int color_Black = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_black, true, false); + int color_Skin = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin, false, true); + int color_Skin_Light = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_light, true, false); + int color_Skin_Shadow = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_shadow, true, false); + int color_Eyes = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_eyes, false, true); + int color_Pupils = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_pupils, false, true); - /* set first color as active */ - ob->actcol = color_Black + 1; + /* set first color as active */ + ob->actcol = color_Black + 1; - /* layers */ - /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ - bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false); - bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + /* layers */ + /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ + bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); - /* frames */ - /* NOTE: No need to check for existing, as this will take care of it for us */ - bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, cfra_eval); - bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, cfra_eval); + /* frames */ + /* NOTE: No need to check for existing, as this will take care of it for us */ + bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, cfra_eval); + bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, cfra_eval); - /* generate strokes */ - gps = BKE_gpencil_add_stroke(frameFills, color_Skin, 270, 75); - BKE_gpencil_stroke_add_points(gps, data0, 270, mat); + /* generate strokes */ + gps = BKE_gpencil_add_stroke(frameFills, color_Skin, 270, 75); + BKE_gpencil_stroke_add_points(gps, data0, 270, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); - BKE_gpencil_stroke_add_points(gps, data1, 33, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); + BKE_gpencil_stroke_add_points(gps, data1, 33, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 60); - BKE_gpencil_stroke_add_points(gps, data2, 18, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 60); + BKE_gpencil_stroke_add_points(gps, data2, 18, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); - BKE_gpencil_stroke_add_points(gps, data3, 64, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); + BKE_gpencil_stroke_add_points(gps, data3, 64, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); - BKE_gpencil_stroke_add_points(gps, data4, 33, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); + BKE_gpencil_stroke_add_points(gps, data4, 33, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); - BKE_gpencil_stroke_add_points(gps, data5, 64, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); + BKE_gpencil_stroke_add_points(gps, data5, 64, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); - BKE_gpencil_stroke_add_points(gps, data6, 33, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); + BKE_gpencil_stroke_add_points(gps, data6, 33, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 18, 40); - BKE_gpencil_stroke_add_points(gps, data7, 18, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 18, 40); + BKE_gpencil_stroke_add_points(gps, data7, 18, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); - BKE_gpencil_stroke_add_points(gps, data8, 49, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); + BKE_gpencil_stroke_add_points(gps, data8, 49, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); - BKE_gpencil_stroke_add_points(gps, data9, 33, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); + BKE_gpencil_stroke_add_points(gps, data9, 33, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); - BKE_gpencil_stroke_add_points(gps, data10, 49, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); + BKE_gpencil_stroke_add_points(gps, data10, 49, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); - BKE_gpencil_stroke_add_points(gps, data11, 18, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); + BKE_gpencil_stroke_add_points(gps, data11, 18, mat); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); - BKE_gpencil_stroke_add_points(gps, data12, 18, mat); + gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); + BKE_gpencil_stroke_add_points(gps, data12, 18, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data13, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data13, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data14, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data14, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 65, 60); - BKE_gpencil_stroke_add_points(gps, data15, 65, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 65, 60); + BKE_gpencil_stroke_add_points(gps, data15, 65, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 60); - BKE_gpencil_stroke_add_points(gps, data16, 34, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 60); + BKE_gpencil_stroke_add_points(gps, data16, 34, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data17, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data17, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 40); - BKE_gpencil_stroke_add_points(gps, data18, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 40); + BKE_gpencil_stroke_add_points(gps, data18, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 40); - BKE_gpencil_stroke_add_points(gps, data19, 34, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 40); + BKE_gpencil_stroke_add_points(gps, data19, 34, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data20, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data20, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 64, 60); - BKE_gpencil_stroke_add_points(gps, data21, 64, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 64, 60); + BKE_gpencil_stroke_add_points(gps, data21, 64, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); - BKE_gpencil_stroke_add_points(gps, data22, 26, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); + BKE_gpencil_stroke_add_points(gps, data22, 26, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); - BKE_gpencil_stroke_add_points(gps, data23, 26, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); + BKE_gpencil_stroke_add_points(gps, data23, 26, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data24, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data24, 33, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); - BKE_gpencil_stroke_add_points(gps, data25, 18, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); + BKE_gpencil_stroke_add_points(gps, data25, 18, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); - BKE_gpencil_stroke_add_points(gps, data26, 18, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); + BKE_gpencil_stroke_add_points(gps, data26, 18, mat); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); - BKE_gpencil_stroke_add_points(gps, data27, 33, mat); + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + BKE_gpencil_stroke_add_points(gps, data27, 33, mat); - /* update depsgraph */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; } - /* ***************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index 1107819a09a..d6e068926dd 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -42,170 +42,169 @@ /* Definition of the most important info from a color */ typedef struct ColorTemplate { - const char *name; - float line[4]; - float fill[4]; + const char *name; + float line[4]; + float fill[4]; } ColorTemplate; /* Add color an ensure duplications (matched by name) */ -static int gp_stroke_material( - Main *bmain, Object *ob, const ColorTemplate *pct, const bool fill) +static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct, const bool fill) { - short *totcol = give_totcolp(ob); - Material *ma = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - if (STREQ(ma->id.name, pct->name)) { - return i; - } - } + short *totcol = give_totcolp(ob); + Material *ma = NULL; + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + if (STREQ(ma->id.name, pct->name)) { + return i; + } + } - int idx; + int idx; - /* create a new one */ - ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); + /* create a new one */ + ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); - copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); - copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); - if (fill) { - ma->gp_style->flag |= GP_STYLE_FILL_SHOW; - } + if (fill) { + ma->gp_style->flag |= GP_STYLE_FILL_SHOW; + } - return idx; + return idx; } /* ***************************************************************** */ /* Stroke Geometry */ static const float data0[175 * GP_PRIM_DATABUF_SIZE] = { - -1.281f, 0.0f, -0.315f, 0.038f, 1.0f, -1.269f, 0.0f, -0.302f, 0.069f, 1.0f, - -1.261f, 0.0f, -0.293f, 0.089f, 1.0f, -1.251f, 0.0f, -0.282f, 0.112f, 1.0f, - -1.241f, 0.0f, -0.271f, 0.134f, 1.0f, -1.23f, 0.0f, -0.259f, 0.155f, 1.0f, - -1.219f, 0.0f, -0.247f, 0.175f, 1.0f, -1.208f, 0.0f, -0.234f, 0.194f, 1.0f, - -1.196f, 0.0f, -0.221f, 0.211f, 1.0f, -1.184f, 0.0f, -0.208f, 0.227f, 1.0f, - -1.172f, 0.0f, -0.194f, 0.242f, 1.0f, -1.159f, 0.0f, -0.18f, 0.256f, 1.0f, - -1.147f, 0.0f, -0.165f, 0.268f, 1.0f, -1.134f, 0.0f, -0.151f, 0.28f, 1.0f, - -1.121f, 0.0f, -0.136f, 0.29f, 1.0f, -1.108f, 0.0f, -0.121f, 0.299f, 1.0f, - -1.094f, 0.0f, -0.106f, 0.307f, 1.0f, -1.08f, 0.0f, -0.091f, 0.315f, 1.0f, - -1.066f, 0.0f, -0.076f, 0.322f, 1.0f, -1.052f, 0.0f, -0.061f, 0.329f, 1.0f, - -1.037f, 0.0f, -0.047f, 0.335f, 1.0f, -1.022f, 0.0f, -0.032f, 0.341f, 1.0f, - -1.007f, 0.0f, -0.017f, 0.346f, 1.0f, -0.991f, 0.0f, -0.003f, 0.351f, 1.0f, - -0.975f, 0.0f, 0.012f, 0.355f, 1.0f, -0.959f, 0.0f, 0.027f, 0.36f, 1.0f, - -0.942f, 0.0f, 0.041f, 0.364f, 1.0f, -0.926f, 0.0f, 0.056f, 0.368f, 1.0f, - -0.909f, 0.0f, 0.071f, 0.371f, 1.0f, -0.893f, 0.0f, 0.086f, 0.373f, 1.0f, - -0.876f, 0.0f, 0.1f, 0.376f, 1.0f, -0.859f, 0.0f, 0.115f, 0.377f, 1.0f, - -0.842f, 0.0f, 0.129f, 0.378f, 1.0f, -0.824f, 0.0f, 0.144f, 0.379f, 1.0f, - -0.807f, 0.0f, 0.158f, 0.379f, 1.0f, -0.79f, 0.0f, 0.172f, 0.379f, 1.0f, - -0.773f, 0.0f, 0.186f, 0.38f, 1.0f, -0.755f, 0.0f, 0.199f, 0.38f, 1.0f, - -0.738f, 0.0f, 0.212f, 0.381f, 1.0f, -0.721f, 0.0f, 0.224f, 0.382f, 1.0f, - -0.703f, 0.0f, 0.236f, 0.384f, 1.0f, -0.686f, 0.0f, 0.248f, 0.386f, 1.0f, - -0.67f, 0.0f, 0.26f, 0.388f, 1.0f, -0.653f, 0.0f, 0.27f, 0.39f, 1.0f, - -0.637f, 0.0f, 0.28f, 0.393f, 1.0f, -0.621f, 0.0f, 0.29f, 0.396f, 1.0f, - -0.605f, 0.0f, 0.298f, 0.399f, 1.0f, -0.589f, 0.0f, 0.306f, 0.403f, 1.0f, - -0.574f, 0.0f, 0.313f, 0.407f, 1.0f, -0.559f, 0.0f, 0.319f, 0.411f, 1.0f, - -0.544f, 0.0f, 0.325f, 0.415f, 1.0f, -0.53f, 0.0f, 0.331f, 0.42f, 1.0f, - -0.516f, 0.0f, 0.336f, 0.425f, 1.0f, -0.503f, 0.0f, 0.34f, 0.431f, 1.0f, - -0.489f, 0.0f, 0.344f, 0.437f, 1.0f, -0.477f, 0.0f, 0.347f, 0.443f, 1.0f, - -0.464f, 0.0f, 0.35f, 0.45f, 1.0f, -0.452f, 0.0f, 0.352f, 0.457f, 1.0f, - -0.44f, 0.0f, 0.354f, 0.464f, 1.0f, -0.429f, 0.0f, 0.355f, 0.471f, 1.0f, - -0.418f, 0.0f, 0.355f, 0.479f, 1.0f, -0.407f, 0.0f, 0.355f, 0.487f, 1.0f, - -0.397f, 0.0f, 0.354f, 0.495f, 1.0f, -0.387f, 0.0f, 0.353f, 0.503f, 1.0f, - -0.378f, 0.0f, 0.351f, 0.512f, 1.0f, -0.368f, 0.0f, 0.348f, 0.52f, 1.0f, - -0.36f, 0.0f, 0.344f, 0.528f, 1.0f, -0.351f, 0.0f, 0.34f, 0.537f, 1.0f, - -0.344f, 0.0f, 0.336f, 0.545f, 1.0f, -0.336f, 0.0f, 0.33f, 0.553f, 1.0f, - -0.329f, 0.0f, 0.324f, 0.562f, 1.0f, -0.322f, 0.0f, 0.318f, 0.57f, 1.0f, - -0.316f, 0.0f, 0.31f, 0.579f, 1.0f, -0.311f, 0.0f, 0.303f, 0.588f, 1.0f, - -0.306f, 0.0f, 0.294f, 0.597f, 1.0f, -0.301f, 0.0f, 0.285f, 0.606f, 1.0f, - -0.297f, 0.0f, 0.275f, 0.615f, 1.0f, -0.293f, 0.0f, 0.264f, 0.625f, 1.0f, - -0.29f, 0.0f, 0.253f, 0.635f, 1.0f, -0.288f, 0.0f, 0.241f, 0.644f, 1.0f, - -0.286f, 0.0f, 0.229f, 0.654f, 1.0f, -0.285f, 0.0f, 0.216f, 0.664f, 1.0f, - -0.284f, 0.0f, 0.202f, 0.675f, 1.0f, -0.283f, 0.0f, 0.188f, 0.685f, 1.0f, - -0.283f, 0.0f, 0.173f, 0.696f, 1.0f, -0.284f, 0.0f, 0.158f, 0.707f, 1.0f, - -0.285f, 0.0f, 0.142f, 0.718f, 1.0f, -0.286f, 0.0f, 0.125f, 0.729f, 1.0f, - -0.288f, 0.0f, 0.108f, 0.74f, 1.0f, -0.29f, 0.0f, 0.091f, 0.751f, 1.0f, - -0.293f, 0.0f, 0.073f, 0.761f, 1.0f, -0.295f, 0.0f, 0.054f, 0.772f, 1.0f, - -0.298f, 0.0f, 0.035f, 0.782f, 1.0f, -0.302f, 0.0f, 0.016f, 0.793f, 1.0f, - -0.305f, 0.0f, -0.004f, 0.804f, 1.0f, -0.309f, 0.0f, -0.024f, 0.815f, 1.0f, - -0.313f, 0.0f, -0.044f, 0.828f, 1.0f, -0.317f, 0.0f, -0.065f, 0.843f, 1.0f, - -0.321f, 0.0f, -0.085f, 0.86f, 1.0f, -0.326f, 0.0f, -0.106f, 0.879f, 1.0f, - -0.33f, 0.0f, -0.127f, 0.897f, 1.0f, -0.335f, 0.0f, -0.148f, 0.915f, 1.0f, - -0.339f, 0.0f, -0.168f, 0.932f, 1.0f, -0.344f, 0.0f, -0.189f, 0.947f, 1.0f, - -0.348f, 0.0f, -0.21f, 0.962f, 1.0f, -0.353f, 0.0f, -0.23f, 0.974f, 1.0f, - -0.357f, 0.0f, -0.25f, 0.985f, 1.0f, -0.361f, 0.0f, -0.27f, 0.995f, 1.0f, - -0.365f, 0.0f, -0.29f, 1.004f, 1.0f, -0.369f, 0.0f, -0.309f, 1.011f, 1.0f, - -0.372f, 0.0f, -0.328f, 1.018f, 1.0f, -0.375f, 0.0f, -0.347f, 1.024f, 1.0f, - -0.377f, 0.0f, -0.365f, 1.029f, 1.0f, -0.379f, 0.0f, -0.383f, 1.033f, 1.0f, - -0.38f, 0.0f, -0.4f, 1.036f, 1.0f, -0.38f, 0.0f, -0.417f, 1.037f, 1.0f, - -0.38f, 0.0f, -0.434f, 1.037f, 1.0f, -0.379f, 0.0f, -0.449f, 1.035f, 1.0f, - -0.377f, 0.0f, -0.464f, 1.032f, 1.0f, -0.374f, 0.0f, -0.478f, 1.029f, 1.0f, - -0.371f, 0.0f, -0.491f, 1.026f, 1.0f, -0.366f, 0.0f, -0.503f, 1.023f, 1.0f, - -0.361f, 0.0f, -0.513f, 1.021f, 1.0f, -0.354f, 0.0f, -0.523f, 1.019f, 1.0f, - -0.347f, 0.0f, -0.531f, 1.017f, 1.0f, -0.339f, 0.0f, -0.538f, 1.016f, 1.0f, - -0.33f, 0.0f, -0.543f, 1.016f, 1.0f, -0.32f, 0.0f, -0.547f, 1.016f, 1.0f, - -0.31f, 0.0f, -0.549f, 1.016f, 1.0f, -0.298f, 0.0f, -0.55f, 1.017f, 1.0f, - -0.286f, 0.0f, -0.55f, 1.017f, 1.0f, -0.274f, 0.0f, -0.548f, 1.018f, 1.0f, - -0.261f, 0.0f, -0.544f, 1.017f, 1.0f, -0.247f, 0.0f, -0.539f, 1.017f, 1.0f, - -0.232f, 0.0f, -0.533f, 1.016f, 1.0f, -0.218f, 0.0f, -0.525f, 1.015f, 1.0f, - -0.202f, 0.0f, -0.515f, 1.013f, 1.0f, -0.186f, 0.0f, -0.503f, 1.009f, 1.0f, - -0.169f, 0.0f, -0.49f, 1.005f, 1.0f, -0.151f, 0.0f, -0.475f, 0.998f, 1.0f, - -0.132f, 0.0f, -0.458f, 0.99f, 1.0f, -0.112f, 0.0f, -0.44f, 0.98f, 1.0f, - -0.091f, 0.0f, -0.42f, 0.968f, 1.0f, -0.069f, 0.0f, -0.398f, 0.955f, 1.0f, - -0.045f, 0.0f, -0.375f, 0.939f, 1.0f, -0.021f, 0.0f, -0.35f, 0.923f, 1.0f, - 0.005f, 0.0f, -0.324f, 0.908f, 1.0f, 0.031f, 0.0f, -0.297f, 0.895f, 1.0f, - 0.06f, 0.0f, -0.268f, 0.882f, 1.0f, 0.089f, 0.0f, -0.238f, 0.87f, 1.0f, - 0.12f, 0.0f, -0.207f, 0.858f, 1.0f, 0.153f, 0.0f, -0.175f, 0.844f, 1.0f, - 0.187f, 0.0f, -0.14f, 0.828f, 1.0f, 0.224f, 0.0f, -0.104f, 0.81f, 1.0f, - 0.262f, 0.0f, -0.067f, 0.79f, 1.0f, 0.302f, 0.0f, -0.027f, 0.769f, 1.0f, - 0.344f, 0.0f, 0.014f, 0.747f, 1.0f, 0.388f, 0.0f, 0.056f, 0.724f, 1.0f, - 0.434f, 0.0f, 0.1f, 0.7f, 1.0f, 0.483f, 0.0f, 0.145f, 0.676f, 1.0f, - 0.533f, 0.0f, 0.191f, 0.651f, 1.0f, 0.585f, 0.0f, 0.238f, 0.625f, 1.0f, - 0.637f, 0.0f, 0.284f, 0.599f, 1.0f, 0.69f, 0.0f, 0.33f, 0.573f, 1.0f, - 0.746f, 0.0f, 0.376f, 0.546f, 1.0f, 0.802f, 0.0f, 0.421f, 0.516f, 1.0f, - 0.859f, 0.0f, 0.464f, 0.483f, 1.0f, 0.915f, 0.0f, 0.506f, 0.446f, 1.0f, - 0.97f, 0.0f, 0.545f, 0.407f, 1.0f, 1.023f, 0.0f, 0.581f, 0.365f, 1.0f, - 1.075f, 0.0f, 0.614f, 0.322f, 1.0f, 1.122f, 0.0f, 0.643f, 0.28f, 1.0f, - 1.169f, 0.0f, 0.671f, 0.236f, 1.0f, 1.207f, 0.0f, 0.693f, 0.202f, 1.0f, - 1.264f, 0.0f, 0.725f, 0.155f, 1.0f, + -1.281f, 0.0f, -0.315f, 0.038f, 1.0f, -1.269f, 0.0f, -0.302f, 0.069f, 1.0f, + -1.261f, 0.0f, -0.293f, 0.089f, 1.0f, -1.251f, 0.0f, -0.282f, 0.112f, 1.0f, + -1.241f, 0.0f, -0.271f, 0.134f, 1.0f, -1.23f, 0.0f, -0.259f, 0.155f, 1.0f, + -1.219f, 0.0f, -0.247f, 0.175f, 1.0f, -1.208f, 0.0f, -0.234f, 0.194f, 1.0f, + -1.196f, 0.0f, -0.221f, 0.211f, 1.0f, -1.184f, 0.0f, -0.208f, 0.227f, 1.0f, + -1.172f, 0.0f, -0.194f, 0.242f, 1.0f, -1.159f, 0.0f, -0.18f, 0.256f, 1.0f, + -1.147f, 0.0f, -0.165f, 0.268f, 1.0f, -1.134f, 0.0f, -0.151f, 0.28f, 1.0f, + -1.121f, 0.0f, -0.136f, 0.29f, 1.0f, -1.108f, 0.0f, -0.121f, 0.299f, 1.0f, + -1.094f, 0.0f, -0.106f, 0.307f, 1.0f, -1.08f, 0.0f, -0.091f, 0.315f, 1.0f, + -1.066f, 0.0f, -0.076f, 0.322f, 1.0f, -1.052f, 0.0f, -0.061f, 0.329f, 1.0f, + -1.037f, 0.0f, -0.047f, 0.335f, 1.0f, -1.022f, 0.0f, -0.032f, 0.341f, 1.0f, + -1.007f, 0.0f, -0.017f, 0.346f, 1.0f, -0.991f, 0.0f, -0.003f, 0.351f, 1.0f, + -0.975f, 0.0f, 0.012f, 0.355f, 1.0f, -0.959f, 0.0f, 0.027f, 0.36f, 1.0f, + -0.942f, 0.0f, 0.041f, 0.364f, 1.0f, -0.926f, 0.0f, 0.056f, 0.368f, 1.0f, + -0.909f, 0.0f, 0.071f, 0.371f, 1.0f, -0.893f, 0.0f, 0.086f, 0.373f, 1.0f, + -0.876f, 0.0f, 0.1f, 0.376f, 1.0f, -0.859f, 0.0f, 0.115f, 0.377f, 1.0f, + -0.842f, 0.0f, 0.129f, 0.378f, 1.0f, -0.824f, 0.0f, 0.144f, 0.379f, 1.0f, + -0.807f, 0.0f, 0.158f, 0.379f, 1.0f, -0.79f, 0.0f, 0.172f, 0.379f, 1.0f, + -0.773f, 0.0f, 0.186f, 0.38f, 1.0f, -0.755f, 0.0f, 0.199f, 0.38f, 1.0f, + -0.738f, 0.0f, 0.212f, 0.381f, 1.0f, -0.721f, 0.0f, 0.224f, 0.382f, 1.0f, + -0.703f, 0.0f, 0.236f, 0.384f, 1.0f, -0.686f, 0.0f, 0.248f, 0.386f, 1.0f, + -0.67f, 0.0f, 0.26f, 0.388f, 1.0f, -0.653f, 0.0f, 0.27f, 0.39f, 1.0f, + -0.637f, 0.0f, 0.28f, 0.393f, 1.0f, -0.621f, 0.0f, 0.29f, 0.396f, 1.0f, + -0.605f, 0.0f, 0.298f, 0.399f, 1.0f, -0.589f, 0.0f, 0.306f, 0.403f, 1.0f, + -0.574f, 0.0f, 0.313f, 0.407f, 1.0f, -0.559f, 0.0f, 0.319f, 0.411f, 1.0f, + -0.544f, 0.0f, 0.325f, 0.415f, 1.0f, -0.53f, 0.0f, 0.331f, 0.42f, 1.0f, + -0.516f, 0.0f, 0.336f, 0.425f, 1.0f, -0.503f, 0.0f, 0.34f, 0.431f, 1.0f, + -0.489f, 0.0f, 0.344f, 0.437f, 1.0f, -0.477f, 0.0f, 0.347f, 0.443f, 1.0f, + -0.464f, 0.0f, 0.35f, 0.45f, 1.0f, -0.452f, 0.0f, 0.352f, 0.457f, 1.0f, + -0.44f, 0.0f, 0.354f, 0.464f, 1.0f, -0.429f, 0.0f, 0.355f, 0.471f, 1.0f, + -0.418f, 0.0f, 0.355f, 0.479f, 1.0f, -0.407f, 0.0f, 0.355f, 0.487f, 1.0f, + -0.397f, 0.0f, 0.354f, 0.495f, 1.0f, -0.387f, 0.0f, 0.353f, 0.503f, 1.0f, + -0.378f, 0.0f, 0.351f, 0.512f, 1.0f, -0.368f, 0.0f, 0.348f, 0.52f, 1.0f, + -0.36f, 0.0f, 0.344f, 0.528f, 1.0f, -0.351f, 0.0f, 0.34f, 0.537f, 1.0f, + -0.344f, 0.0f, 0.336f, 0.545f, 1.0f, -0.336f, 0.0f, 0.33f, 0.553f, 1.0f, + -0.329f, 0.0f, 0.324f, 0.562f, 1.0f, -0.322f, 0.0f, 0.318f, 0.57f, 1.0f, + -0.316f, 0.0f, 0.31f, 0.579f, 1.0f, -0.311f, 0.0f, 0.303f, 0.588f, 1.0f, + -0.306f, 0.0f, 0.294f, 0.597f, 1.0f, -0.301f, 0.0f, 0.285f, 0.606f, 1.0f, + -0.297f, 0.0f, 0.275f, 0.615f, 1.0f, -0.293f, 0.0f, 0.264f, 0.625f, 1.0f, + -0.29f, 0.0f, 0.253f, 0.635f, 1.0f, -0.288f, 0.0f, 0.241f, 0.644f, 1.0f, + -0.286f, 0.0f, 0.229f, 0.654f, 1.0f, -0.285f, 0.0f, 0.216f, 0.664f, 1.0f, + -0.284f, 0.0f, 0.202f, 0.675f, 1.0f, -0.283f, 0.0f, 0.188f, 0.685f, 1.0f, + -0.283f, 0.0f, 0.173f, 0.696f, 1.0f, -0.284f, 0.0f, 0.158f, 0.707f, 1.0f, + -0.285f, 0.0f, 0.142f, 0.718f, 1.0f, -0.286f, 0.0f, 0.125f, 0.729f, 1.0f, + -0.288f, 0.0f, 0.108f, 0.74f, 1.0f, -0.29f, 0.0f, 0.091f, 0.751f, 1.0f, + -0.293f, 0.0f, 0.073f, 0.761f, 1.0f, -0.295f, 0.0f, 0.054f, 0.772f, 1.0f, + -0.298f, 0.0f, 0.035f, 0.782f, 1.0f, -0.302f, 0.0f, 0.016f, 0.793f, 1.0f, + -0.305f, 0.0f, -0.004f, 0.804f, 1.0f, -0.309f, 0.0f, -0.024f, 0.815f, 1.0f, + -0.313f, 0.0f, -0.044f, 0.828f, 1.0f, -0.317f, 0.0f, -0.065f, 0.843f, 1.0f, + -0.321f, 0.0f, -0.085f, 0.86f, 1.0f, -0.326f, 0.0f, -0.106f, 0.879f, 1.0f, + -0.33f, 0.0f, -0.127f, 0.897f, 1.0f, -0.335f, 0.0f, -0.148f, 0.915f, 1.0f, + -0.339f, 0.0f, -0.168f, 0.932f, 1.0f, -0.344f, 0.0f, -0.189f, 0.947f, 1.0f, + -0.348f, 0.0f, -0.21f, 0.962f, 1.0f, -0.353f, 0.0f, -0.23f, 0.974f, 1.0f, + -0.357f, 0.0f, -0.25f, 0.985f, 1.0f, -0.361f, 0.0f, -0.27f, 0.995f, 1.0f, + -0.365f, 0.0f, -0.29f, 1.004f, 1.0f, -0.369f, 0.0f, -0.309f, 1.011f, 1.0f, + -0.372f, 0.0f, -0.328f, 1.018f, 1.0f, -0.375f, 0.0f, -0.347f, 1.024f, 1.0f, + -0.377f, 0.0f, -0.365f, 1.029f, 1.0f, -0.379f, 0.0f, -0.383f, 1.033f, 1.0f, + -0.38f, 0.0f, -0.4f, 1.036f, 1.0f, -0.38f, 0.0f, -0.417f, 1.037f, 1.0f, + -0.38f, 0.0f, -0.434f, 1.037f, 1.0f, -0.379f, 0.0f, -0.449f, 1.035f, 1.0f, + -0.377f, 0.0f, -0.464f, 1.032f, 1.0f, -0.374f, 0.0f, -0.478f, 1.029f, 1.0f, + -0.371f, 0.0f, -0.491f, 1.026f, 1.0f, -0.366f, 0.0f, -0.503f, 1.023f, 1.0f, + -0.361f, 0.0f, -0.513f, 1.021f, 1.0f, -0.354f, 0.0f, -0.523f, 1.019f, 1.0f, + -0.347f, 0.0f, -0.531f, 1.017f, 1.0f, -0.339f, 0.0f, -0.538f, 1.016f, 1.0f, + -0.33f, 0.0f, -0.543f, 1.016f, 1.0f, -0.32f, 0.0f, -0.547f, 1.016f, 1.0f, + -0.31f, 0.0f, -0.549f, 1.016f, 1.0f, -0.298f, 0.0f, -0.55f, 1.017f, 1.0f, + -0.286f, 0.0f, -0.55f, 1.017f, 1.0f, -0.274f, 0.0f, -0.548f, 1.018f, 1.0f, + -0.261f, 0.0f, -0.544f, 1.017f, 1.0f, -0.247f, 0.0f, -0.539f, 1.017f, 1.0f, + -0.232f, 0.0f, -0.533f, 1.016f, 1.0f, -0.218f, 0.0f, -0.525f, 1.015f, 1.0f, + -0.202f, 0.0f, -0.515f, 1.013f, 1.0f, -0.186f, 0.0f, -0.503f, 1.009f, 1.0f, + -0.169f, 0.0f, -0.49f, 1.005f, 1.0f, -0.151f, 0.0f, -0.475f, 0.998f, 1.0f, + -0.132f, 0.0f, -0.458f, 0.99f, 1.0f, -0.112f, 0.0f, -0.44f, 0.98f, 1.0f, + -0.091f, 0.0f, -0.42f, 0.968f, 1.0f, -0.069f, 0.0f, -0.398f, 0.955f, 1.0f, + -0.045f, 0.0f, -0.375f, 0.939f, 1.0f, -0.021f, 0.0f, -0.35f, 0.923f, 1.0f, + 0.005f, 0.0f, -0.324f, 0.908f, 1.0f, 0.031f, 0.0f, -0.297f, 0.895f, 1.0f, + 0.06f, 0.0f, -0.268f, 0.882f, 1.0f, 0.089f, 0.0f, -0.238f, 0.87f, 1.0f, + 0.12f, 0.0f, -0.207f, 0.858f, 1.0f, 0.153f, 0.0f, -0.175f, 0.844f, 1.0f, + 0.187f, 0.0f, -0.14f, 0.828f, 1.0f, 0.224f, 0.0f, -0.104f, 0.81f, 1.0f, + 0.262f, 0.0f, -0.067f, 0.79f, 1.0f, 0.302f, 0.0f, -0.027f, 0.769f, 1.0f, + 0.344f, 0.0f, 0.014f, 0.747f, 1.0f, 0.388f, 0.0f, 0.056f, 0.724f, 1.0f, + 0.434f, 0.0f, 0.1f, 0.7f, 1.0f, 0.483f, 0.0f, 0.145f, 0.676f, 1.0f, + 0.533f, 0.0f, 0.191f, 0.651f, 1.0f, 0.585f, 0.0f, 0.238f, 0.625f, 1.0f, + 0.637f, 0.0f, 0.284f, 0.599f, 1.0f, 0.69f, 0.0f, 0.33f, 0.573f, 1.0f, + 0.746f, 0.0f, 0.376f, 0.546f, 1.0f, 0.802f, 0.0f, 0.421f, 0.516f, 1.0f, + 0.859f, 0.0f, 0.464f, 0.483f, 1.0f, 0.915f, 0.0f, 0.506f, 0.446f, 1.0f, + 0.97f, 0.0f, 0.545f, 0.407f, 1.0f, 1.023f, 0.0f, 0.581f, 0.365f, 1.0f, + 1.075f, 0.0f, 0.614f, 0.322f, 1.0f, 1.122f, 0.0f, 0.643f, 0.28f, 1.0f, + 1.169f, 0.0f, 0.671f, 0.236f, 1.0f, 1.207f, 0.0f, 0.693f, 0.202f, 1.0f, + 1.264f, 0.0f, 0.725f, 0.155f, 1.0f, }; /* ***************************************************************** */ /* Color Data */ static const ColorTemplate gp_stroke_material_black = { - "Black", - {0.0f, 0.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_white = { - "White", - {1.0f, 1.0f, 1.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "White", + {1.0f, 1.0f, 1.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_red = { - "Red", - {1.0f, 0.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "Red", + {1.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_green = { - "Green", - {0.0f, 1.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "Green", + {0.0f, 1.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_blue = { - "Blue", - {0.0f, 0.0f, 1.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, + "Blue", + {0.0f, 0.0f, 1.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_grey = { - "Grey", - {0.358f, 0.358f, 0.358f, 1.0f}, - {0.5f, 0.5f, 0.5f, 1.0f}, + "Grey", + {0.358f, 0.358f, 0.358f, 1.0f}, + {0.5f, 0.5f, 0.5f, 1.0f}, }; /* ***************************************************************** */ @@ -214,37 +213,37 @@ static const ColorTemplate gp_stroke_material_grey = { /* add a Simple stroke with colors (original design created by Daniel M. Lara and Matias Mendiola) */ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPdata *gpd = (bGPdata *)ob->data; - bGPDstroke *gps; - - /* create colors */ - int color_black = gp_stroke_material(bmain, ob, &gp_stroke_material_black, false); - gp_stroke_material(bmain, ob, &gp_stroke_material_white, false); - gp_stroke_material(bmain, ob, &gp_stroke_material_red, false); - gp_stroke_material(bmain, ob, &gp_stroke_material_green, false); - gp_stroke_material(bmain, ob, &gp_stroke_material_blue, false); - gp_stroke_material(bmain, ob, &gp_stroke_material_grey, true); - - /* set first color as active and in brushes */ - ob->actcol = color_black + 1; - - /* layers */ - bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); - - /* frames */ - bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, cfra_eval); - bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, cfra_eval); - UNUSED_VARS(frame_color); - - /* generate stroke */ - gps = BKE_gpencil_add_stroke(frame_lines, color_black, 175, 75); - BKE_gpencil_stroke_add_points(gps, data0, 175, mat); - - /* update depsgraph */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + bGPDstroke *gps; + + /* create colors */ + int color_black = gp_stroke_material(bmain, ob, &gp_stroke_material_black, false); + gp_stroke_material(bmain, ob, &gp_stroke_material_white, false); + gp_stroke_material(bmain, ob, &gp_stroke_material_red, false); + gp_stroke_material(bmain, ob, &gp_stroke_material_green, false); + gp_stroke_material(bmain, ob, &gp_stroke_material_blue, false); + gp_stroke_material(bmain, ob, &gp_stroke_material_grey, true); + + /* set first color as active and in brushes */ + ob->actcol = color_black + 1; + + /* layers */ + bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + + /* frames */ + bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, cfra_eval); + bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, cfra_eval); + UNUSED_VARS(frame_color); + + /* generate stroke */ + gps = BKE_gpencil_add_stroke(frame_lines, color_black, 175, 75); + BKE_gpencil_stroke_add_points(gps, data0, 175, mat); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; } diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index 8fff6b207ba..d99a630a45c 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -69,609 +68,625 @@ #include "gpencil_intern.h" enum { - GP_ARMATURE_NAME = 0, - GP_ARMATURE_AUTO = 1, + GP_ARMATURE_NAME = 0, + GP_ARMATURE_AUTO = 1, }; #define DEFAULT_RATIO 0.10f #define DEFAULT_DECAY 0.8f -static int gpencil_bone_looper( - Object *ob, Bone *bone, void *data, - int(*bone_func)(Object *, Bone *, void *)) +static int gpencil_bone_looper(Object *ob, + Bone *bone, + void *data, + int (*bone_func)(Object *, Bone *, void *)) { - /* We want to apply the function bone_func to every bone - * in an armature -- feed bone_looper the first bone and - * a pointer to the bone_func and watch it go!. The int count - * can be useful for counting bones with a certain property - * (e.g. skinnable) - */ - int count = 0; - - if (bone) { - /* only do bone_func if the bone is non null */ - count += bone_func(ob, bone, data); - - /* try to execute bone_func for the first child */ - count += gpencil_bone_looper(ob, bone->childbase.first, data, bone_func); - - /* try to execute bone_func for the next bone at this - * depth of the recursion. - */ - count += gpencil_bone_looper(ob, bone->next, data, bone_func); - } - - return count; + /* We want to apply the function bone_func to every bone + * in an armature -- feed bone_looper the first bone and + * a pointer to the bone_func and watch it go!. The int count + * can be useful for counting bones with a certain property + * (e.g. skinnable) + */ + int count = 0; + + if (bone) { + /* only do bone_func if the bone is non null */ + count += bone_func(ob, bone, data); + + /* try to execute bone_func for the first child */ + count += gpencil_bone_looper(ob, bone->childbase.first, data, bone_func); + + /* try to execute bone_func for the next bone at this + * depth of the recursion. + */ + count += gpencil_bone_looper(ob, bone->next, data, bone_func); + } + + return count; } static int gpencil_bone_skinnable_cb(Object *UNUSED(ob), Bone *bone, void *datap) { - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) It returns 1 if the bone is skinnable. - * If we loop over all bones with this - * function, we can count the number of - * skinnable bones. - * b) If the pointer data is non null, - * it is treated like a handle to a - * bone pointer -- the bone pointer - * is set to point at this bone, and - * the pointer the handle points to - * is incremented to point to the - * next member of an array of pointers - * to bones. This way we can loop using - * this function to construct an array of - * pointers to bones that point to all - * skinnable bones. - */ - Bone ***hbone; - int a, segments; - struct { Object *armob; void *list; int heat;} *data = datap; - - if (!(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && - BKE_pose_channel_find_name(data->armob->pose, bone->name)) - { - segments = bone->segments; - } - else { - segments = 1; - } - - if (data->list != NULL) { - hbone = (Bone ***)&data->list; - - for (a = 0; a < segments; a++) { - **hbone = bone; - (*hbone)++; - } - } - return segments; - } - } - return 0; + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) It returns 1 if the bone is skinnable. + * If we loop over all bones with this + * function, we can count the number of + * skinnable bones. + * b) If the pointer data is non null, + * it is treated like a handle to a + * bone pointer -- the bone pointer + * is set to point at this bone, and + * the pointer the handle points to + * is incremented to point to the + * next member of an array of pointers + * to bones. This way we can loop using + * this function to construct an array of + * pointers to bones that point to all + * skinnable bones. + */ + Bone ***hbone; + int a, segments; + struct { + Object *armob; + void *list; + int heat; + } *data = datap; + + if (!(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && + BKE_pose_channel_find_name(data->armob->pose, bone->name)) { + segments = bone->segments; + } + else { + segments = 1; + } + + if (data->list != NULL) { + hbone = (Bone ***)&data->list; + + for (a = 0; a < segments; a++) { + **hbone = bone; + (*hbone)++; + } + } + return segments; + } + } + return 0; } static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) { - /* This group creates a vertex group to ob that has the - * same name as bone (provided the bone is skinnable). - * If such a vertex group already exist the routine exits. - */ - if (!(bone->flag & BONE_NO_DEFORM)) { - if (!defgroup_find_name(ob, bone->name)) { - BKE_object_defgroup_add_name(ob, bone->name); - return 1; - } - } - return 0; + /* This group creates a vertex group to ob that has the + * same name as bone (provided the bone is skinnable). + * If such a vertex group already exist the routine exits. + */ + if (!(bone->flag & BONE_NO_DEFORM)) { + if (!defgroup_find_name(ob, bone->name)) { + BKE_object_defgroup_add_name(ob, bone->name); + return 1; + } + } + return 0; } static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) { - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) If the bone is skinnable, it creates - * a vertex group for ob that has - * the name of the skinnable bone - * (if one doesn't exist already). - * b) If the pointer data is non null, - * it is treated like a handle to a - * bDeformGroup pointer -- the - * bDeformGroup pointer is set to point - * to the deform group with the bone's - * name, and the pointer the handle - * points to is incremented to point to the - * next member of an array of pointers - * to bDeformGroups. This way we can loop using - * this function to construct an array of - * pointers to bDeformGroups, all with names - * of skinnable bones. - */ - bDeformGroup ***hgroup, *defgroup = NULL; - int a, segments; - struct { Object *armob; void *list; int heat; } *data = datap; - bArmature *arm = data->armob->data; - - if (!(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && - BKE_pose_channel_find_name(data->armob->pose, bone->name)) - { - segments = bone->segments; - } - else { - segments = 1; - } - - if (arm->layer & bone->layer) { - if (!(defgroup = defgroup_find_name(ob, bone->name))) { - defgroup = BKE_object_defgroup_add_name(ob, bone->name); - } - else if (defgroup->flag & DG_LOCK_WEIGHT) { - /* In case vgroup already exists and is locked, do not modify it here. See T43814. */ - defgroup = NULL; - } - } - - if (data->list != NULL) { - hgroup = (bDeformGroup ***)&data->list; - - for (a = 0; a < segments; a++) { - **hgroup = defgroup; - (*hgroup)++; - } - } - return segments; - } - } - return 0; + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) If the bone is skinnable, it creates + * a vertex group for ob that has + * the name of the skinnable bone + * (if one doesn't exist already). + * b) If the pointer data is non null, + * it is treated like a handle to a + * bDeformGroup pointer -- the + * bDeformGroup pointer is set to point + * to the deform group with the bone's + * name, and the pointer the handle + * points to is incremented to point to the + * next member of an array of pointers + * to bDeformGroups. This way we can loop using + * this function to construct an array of + * pointers to bDeformGroups, all with names + * of skinnable bones. + */ + bDeformGroup ***hgroup, *defgroup = NULL; + int a, segments; + struct { + Object *armob; + void *list; + int heat; + } *data = datap; + bArmature *arm = data->armob->data; + + if (!(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && + BKE_pose_channel_find_name(data->armob->pose, bone->name)) { + segments = bone->segments; + } + else { + segments = 1; + } + + if (arm->layer & bone->layer) { + if (!(defgroup = defgroup_find_name(ob, bone->name))) { + defgroup = BKE_object_defgroup_add_name(ob, bone->name); + } + else if (defgroup->flag & DG_LOCK_WEIGHT) { + /* In case vgroup already exists and is locked, do not modify it here. See T43814. */ + defgroup = NULL; + } + } + + if (data->list != NULL) { + hgroup = (bDeformGroup ***)&data->list; + + for (a = 0; a < segments; a++) { + **hgroup = defgroup; + (*hgroup)++; + } + } + return segments; + } + } + return 0; } /* get weight value depending of distance and decay value */ static float get_weight(float dist, float decay_rad, float dif_rad) { - float weight = 1.0f; - if (dist < decay_rad) { - weight = 1.0f; - } - else { - weight = interpf(0.0f, 0.9f, (dist - decay_rad) / dif_rad); - } - - return weight; + float weight = 1.0f; + if (dist < decay_rad) { + weight = 1.0f; + } + else { + weight = interpf(0.0f, 0.9f, (dist - decay_rad) / dif_rad); + } + + return weight; } /* This functions implements the automatic computation of vertex group weights */ static void gpencil_add_verts_to_dgroups( - const bContext *C, Object *ob, Object *ob_arm, const float ratio, const float decay) + const bContext *C, Object *ob, Object *ob_arm, const float ratio, const float decay) { - bArmature *arm = ob_arm->data; - Bone **bonelist, *bone; - bDeformGroup **dgrouplist; - bPoseChannel *pchan; - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL; - float(*root)[3], (*tip)[3], (*verts)[3]; - float *radsqr; - int *selected; - float weight; - int numbones, i, j, segments = 0; - struct { Object *armob; void *list; int heat; } looper_data; - - looper_data.armob = ob_arm; - looper_data.heat = true; - looper_data.list = NULL; - - /* count the number of skinnable bones */ - numbones = gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb); - - if (numbones == 0) - return; - - /* create an array of pointer to bones that are skinnable - * and fill it with all of the skinnable bones */ - bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); - looper_data.list = bonelist; - gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb); - - /* create an array of pointers to the deform groups that - * correspond to the skinnable bones (creating them - * as necessary. */ - dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); - - looper_data.list = dgrouplist; - gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); - - /* create an array of root and tip positions transformed into - * global coords */ - root = MEM_callocN(numbones * sizeof(float) * 3, "root"); - tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); - selected = MEM_callocN(numbones * sizeof(int), "selected"); - radsqr = MEM_callocN(numbones * sizeof(float), "radsqr"); - - for (j = 0; j < numbones; j++) { - bone = bonelist[j]; - - /* handle bbone */ - if (segments == 0) { - segments = 1; - bbone = NULL; - - if ((ob_arm->pose) && - (pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name))) - { - if (bone->segments > 1) { - segments = bone->segments; - BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array); - bbone = bbone_array; - } - } - } - - segments--; - - /* compute root and tip */ - if (bbone) { - mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); - if ((segments + 1) < bone->segments) { - mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); - } - else { - copy_v3_v3(tip[j], bone->arm_tail); - } - } - else { - copy_v3_v3(root[j], bone->arm_head); - copy_v3_v3(tip[j], bone->arm_tail); - } - - mul_m4_v3(ob_arm->obmat, root[j]); - mul_m4_v3(ob_arm->obmat, tip[j]); - - selected[j] = 1; - - /* calculate radius squared */ - radsqr[j] = len_squared_v3v3(root[j], tip[j]) * ratio; - } - - /* loop all strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *init_gpf = gpl->actframe; - bGPDspoint *pt = NULL; - - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || - ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) - { - - if (gpf == NULL) - continue; - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - BKE_gpencil_dvert_ensure(gps); - - /* create verts array */ - verts = MEM_callocN(gps->totpoints * sizeof(*verts), __func__); - - /* transform stroke points to global space */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - copy_v3_v3(verts[i], &pt->x); - mul_m4_v3(ob->obmat, verts[i]); - } - - /* loop groups and assign weight */ - for (j = 0; j < numbones; j++) { - int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]); - if (def_nr < 0) { - continue; - } - - float decay_rad = radsqr[j] - (radsqr[j] * decay); - float dif_rad = radsqr[j] - decay_rad; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - MDeformVert *dvert = &gps->dvert[i]; - float dist = dist_squared_to_line_segment_v3(verts[i], root[j], tip[j]); - if (dist > radsqr[j]) { - /* if not in cylinder, check if inside extreme spheres */ - weight = 0.0f; - dist = len_squared_v3v3(root[j], verts[i]); - if (dist < radsqr[j]) { - weight = get_weight(dist, decay_rad, dif_rad); - } - else { - dist = len_squared_v3v3(tip[j], verts[i]); - if (dist < radsqr[j]) { - weight = get_weight(dist, decay_rad, dif_rad); - } - } - } - else { - /* inside bone cylinder */ - weight = get_weight(dist, decay_rad, dif_rad); - } - - /* assign weight */ - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = weight; - } - } - } - MEM_SAFE_FREE(verts); - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - - /* free the memory allocated */ - MEM_SAFE_FREE(bonelist); - MEM_SAFE_FREE(dgrouplist); - MEM_SAFE_FREE(root); - MEM_SAFE_FREE(tip); - MEM_SAFE_FREE(radsqr); - MEM_SAFE_FREE(selected); + bArmature *arm = ob_arm->data; + Bone **bonelist, *bone; + bDeformGroup **dgrouplist; + bPoseChannel *pchan; + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL; + float(*root)[3], (*tip)[3], (*verts)[3]; + float *radsqr; + int *selected; + float weight; + int numbones, i, j, segments = 0; + struct { + Object *armob; + void *list; + int heat; + } looper_data; + + looper_data.armob = ob_arm; + looper_data.heat = true; + looper_data.list = NULL; + + /* count the number of skinnable bones */ + numbones = gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb); + + if (numbones == 0) + return; + + /* create an array of pointer to bones that are skinnable + * and fill it with all of the skinnable bones */ + bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); + looper_data.list = bonelist; + gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb); + + /* create an array of pointers to the deform groups that + * correspond to the skinnable bones (creating them + * as necessary. */ + dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); + + looper_data.list = dgrouplist; + gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); + + /* create an array of root and tip positions transformed into + * global coords */ + root = MEM_callocN(numbones * sizeof(float) * 3, "root"); + tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); + selected = MEM_callocN(numbones * sizeof(int), "selected"); + radsqr = MEM_callocN(numbones * sizeof(float), "radsqr"); + + for (j = 0; j < numbones; j++) { + bone = bonelist[j]; + + /* handle bbone */ + if (segments == 0) { + segments = 1; + bbone = NULL; + + if ((ob_arm->pose) && (pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name))) { + if (bone->segments > 1) { + segments = bone->segments; + BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array); + bbone = bbone_array; + } + } + } + + segments--; + + /* compute root and tip */ + if (bbone) { + mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); + if ((segments + 1) < bone->segments) { + mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); + } + else { + copy_v3_v3(tip[j], bone->arm_tail); + } + } + else { + copy_v3_v3(root[j], bone->arm_head); + copy_v3_v3(tip[j], bone->arm_tail); + } + + mul_m4_v3(ob_arm->obmat, root[j]); + mul_m4_v3(ob_arm->obmat, tip[j]); + + selected[j] = 1; + + /* calculate radius squared */ + radsqr[j] = len_squared_v3v3(root[j], tip[j]) * ratio; + } + + /* loop all strokes */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *init_gpf = gpl->actframe; + bGPDspoint *pt = NULL; + + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + BKE_gpencil_dvert_ensure(gps); + + /* create verts array */ + verts = MEM_callocN(gps->totpoints * sizeof(*verts), __func__); + + /* transform stroke points to global space */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + copy_v3_v3(verts[i], &pt->x); + mul_m4_v3(ob->obmat, verts[i]); + } + + /* loop groups and assign weight */ + for (j = 0; j < numbones; j++) { + int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]); + if (def_nr < 0) { + continue; + } + + float decay_rad = radsqr[j] - (radsqr[j] * decay); + float dif_rad = radsqr[j] - decay_rad; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + MDeformVert *dvert = &gps->dvert[i]; + float dist = dist_squared_to_line_segment_v3(verts[i], root[j], tip[j]); + if (dist > radsqr[j]) { + /* if not in cylinder, check if inside extreme spheres */ + weight = 0.0f; + dist = len_squared_v3v3(root[j], verts[i]); + if (dist < radsqr[j]) { + weight = get_weight(dist, decay_rad, dif_rad); + } + else { + dist = len_squared_v3v3(tip[j], verts[i]); + if (dist < radsqr[j]) { + weight = get_weight(dist, decay_rad, dif_rad); + } + } + } + else { + /* inside bone cylinder */ + weight = get_weight(dist, decay_rad, dif_rad); + } + + /* assign weight */ + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = weight; + } + } + } + MEM_SAFE_FREE(verts); + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + + /* free the memory allocated */ + MEM_SAFE_FREE(bonelist); + MEM_SAFE_FREE(dgrouplist); + MEM_SAFE_FREE(root); + MEM_SAFE_FREE(tip); + MEM_SAFE_FREE(radsqr); + MEM_SAFE_FREE(selected); } -static void gpencil_object_vgroup_calc_from_armature( - const bContext *C, - Object *ob, Object *ob_arm, - const int mode, const float ratio, const float decay) +static void gpencil_object_vgroup_calc_from_armature(const bContext *C, + Object *ob, + Object *ob_arm, + const int mode, + const float ratio, + const float decay) { - /* Lets try to create some vertex groups - * based on the bones of the parent armature. - */ - bArmature *arm = ob_arm->data; - - /* always create groups */ - const int defbase_tot = BLI_listbase_count(&ob->defbase); - int defbase_add; - /* Traverse the bone list, trying to create empty vertex - * groups corresponding to the bone. - */ - defbase_add = gpencil_bone_looper( - ob, arm->bonebase.first, NULL, - vgroup_add_unique_bone_cb); - - if (defbase_add) { - /* its possible there are DWeight's outside the range of the current - * objects deform groups, in this case the new groups wont be empty */ - ED_vgroup_data_clamp_range(ob->data, defbase_tot); - } - - if (mode == GP_ARMATURE_AUTO) { - /* Traverse the bone list, trying to fill vertex groups - * with the corresponding vertice weights for which the - * bone is closest. - */ - gpencil_add_verts_to_dgroups( - C, ob, ob_arm, - ratio, decay); - } + /* Lets try to create some vertex groups + * based on the bones of the parent armature. + */ + bArmature *arm = ob_arm->data; + + /* always create groups */ + const int defbase_tot = BLI_listbase_count(&ob->defbase); + int defbase_add; + /* Traverse the bone list, trying to create empty vertex + * groups corresponding to the bone. + */ + defbase_add = gpencil_bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); + + if (defbase_add) { + /* its possible there are DWeight's outside the range of the current + * objects deform groups, in this case the new groups wont be empty */ + ED_vgroup_data_clamp_range(ob->data, defbase_tot); + } + + if (mode == GP_ARMATURE_AUTO) { + /* Traverse the bone list, trying to fill vertex groups + * with the corresponding vertice weights for which the + * bone is closest. + */ + gpencil_add_verts_to_dgroups(C, ob, ob_arm, ratio, decay); + } } bool ED_gpencil_add_armature_weights( - const bContext *C, ReportList *reports, - Object *ob, Object *ob_arm, int mode) + const bContext *C, ReportList *reports, Object *ob, Object *ob_arm, int mode) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - if (ob == NULL) { - return false; - } - - /* if no armature modifier, add a new one */ - GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob, eGpencilModifierType_Armature); - if (md == NULL) { - md = ED_object_gpencil_modifier_add( - reports, bmain, scene, - ob, "Armature", eGpencilModifierType_Armature); - if (md == NULL) { - BKE_report(reports, RPT_ERROR, - "Unable to add a new Armature modifier to object"); - return false; - } - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - - /* verify armature */ - ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md; - if (mmd->object == NULL) { - mmd->object = ob_arm; - } - else { - if (ob_arm != mmd->object) { - BKE_report(reports, RPT_ERROR, - "The existing Armature modifier is already using a different Armature object"); - return false; - } - } - - /* add weights */ - gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, DEFAULT_RATIO, DEFAULT_DECAY); - - return true; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + if (ob == NULL) { + return false; + } + + /* if no armature modifier, add a new one */ + GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob, eGpencilModifierType_Armature); + if (md == NULL) { + md = ED_object_gpencil_modifier_add( + reports, bmain, scene, ob, "Armature", eGpencilModifierType_Armature); + if (md == NULL) { + BKE_report(reports, RPT_ERROR, "Unable to add a new Armature modifier to object"); + return false; + } + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + + /* verify armature */ + ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md; + if (mmd->object == NULL) { + mmd->object = ob_arm; + } + else { + if (ob_arm != mmd->object) { + BKE_report(reports, + RPT_ERROR, + "The existing Armature modifier is already using a different Armature object"); + return false; + } + } + + /* add weights */ + gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, DEFAULT_RATIO, DEFAULT_DECAY); + + return true; } /* ***************** Generate armature weights ************************** */ static bool gpencil_generate_weights_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - if (ob == NULL) { - return false; - } + if (ob == NULL) { + return false; + } - if (ob->type != OB_GPENCIL) { - return false; - } + if (ob->type != OB_GPENCIL) { + return false; + } - ViewLayer *view_layer = CTX_data_view_layer(C); - bGPdata *gpd = (bGPdata *)ob->data; + ViewLayer *view_layer = CTX_data_view_layer(C); + bGPdata *gpd = (bGPdata *)ob->data; - if (BLI_listbase_count(&gpd->layers) == 0) { - return false; - } + if (BLI_listbase_count(&gpd->layers) == 0) { + return false; + } - /* need some armature in the view layer */ - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if (base->object->type == OB_ARMATURE) { - return true; - } - } + /* need some armature in the view layer */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->object->type == OB_ARMATURE) { + return true; + } + } - return false; + return false; } static int gpencil_generate_weights_exec(bContext *C, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = CTX_data_active_object(C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - bGPdata *gpd = (bGPdata *)ob->data; - Object *ob_arm = NULL; - - const int mode = RNA_enum_get(op->ptr, "mode"); - const float ratio = RNA_float_get(op->ptr, "ratio"); - const float decay = RNA_float_get(op->ptr, "decay"); - - /* sanity checks */ - if (ELEM(NULL, ob, gpd)) - return OPERATOR_CANCELLED; - - /* get armature */ - const int arm_idx = RNA_enum_get(op->ptr, "armature"); - if (arm_idx > 0) { - Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1); - ob_arm = base->object; - } - else { - /* get armature from modifier */ - GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob_eval, eGpencilModifierType_Armature); - if (md == NULL) { - BKE_report(op->reports, RPT_ERROR, - "The grease pencil object need an Armature modifier"); - return OPERATOR_CANCELLED; - } - - ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md; - if (mmd->object == NULL) { - BKE_report(op->reports, RPT_ERROR, - "Armature modifier is not valid or wrong defined"); - return OPERATOR_CANCELLED; - } - - ob_arm = mmd->object; - } - - if (ob_arm == NULL) { - BKE_report(op->reports, RPT_ERROR, - "No Armature object in the view layer"); - return OPERATOR_CANCELLED; - } - - gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, ratio, decay); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + bGPdata *gpd = (bGPdata *)ob->data; + Object *ob_arm = NULL; + + const int mode = RNA_enum_get(op->ptr, "mode"); + const float ratio = RNA_float_get(op->ptr, "ratio"); + const float decay = RNA_float_get(op->ptr, "decay"); + + /* sanity checks */ + if (ELEM(NULL, ob, gpd)) + return OPERATOR_CANCELLED; + + /* get armature */ + const int arm_idx = RNA_enum_get(op->ptr, "armature"); + if (arm_idx > 0) { + Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1); + ob_arm = base->object; + } + else { + /* get armature from modifier */ + GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob_eval, + eGpencilModifierType_Armature); + if (md == NULL) { + BKE_report(op->reports, RPT_ERROR, "The grease pencil object need an Armature modifier"); + return OPERATOR_CANCELLED; + } + + ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md; + if (mmd->object == NULL) { + BKE_report(op->reports, RPT_ERROR, "Armature modifier is not valid or wrong defined"); + return OPERATOR_CANCELLED; + } + + ob_arm = mmd->object; + } + + if (ob_arm == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Armature object in the view layer"); + return OPERATOR_CANCELLED; + } + + gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, ratio, decay); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } /* Dynamically populate an enum of Armatures */ -static const EnumPropertyItem *gpencil_armatures_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { - ViewLayer *view_layer = CTX_data_view_layer(C); - EnumPropertyItem *item = NULL, item_tmp = { 0 }; - int totitem = 0; - int i = 0; - - if (C == NULL) { - return DummyRNA_DEFAULT_items; - } - - /* add default */ - item_tmp.identifier = "DEFAULT"; - item_tmp.name = "Default"; - item_tmp.value = 0; - RNA_enum_item_add(&item, &totitem, &item_tmp); - i++; - - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - Object *ob = base->object; - if (ob->type == OB_ARMATURE) { - item_tmp.identifier = item_tmp.name = ob->id.name + 2; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - i++; - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; + ViewLayer *view_layer = CTX_data_view_layer(C); + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (C == NULL) { + return DummyRNA_DEFAULT_items; + } + + /* add default */ + item_tmp.identifier = "DEFAULT"; + item_tmp.name = "Default"; + item_tmp.value = 0; + RNA_enum_item_add(&item, &totitem, &item_tmp); + i++; + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if (ob->type == OB_ARMATURE) { + item_tmp.identifier = item_tmp.name = ob->id.name + 2; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + i++; + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } void GPENCIL_OT_generate_weights(wmOperatorType *ot) { - static const EnumPropertyItem mode_type[] = { - {GP_ARMATURE_NAME, "NAME", 0, "Empty Groups", ""}, - {GP_ARMATURE_AUTO, "AUTO", 0, "Automatic Weights", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Generate Automatic Weights"; - ot->idname = "GPENCIL_OT_generate_weights"; - ot->description = "Generate automatic weights for armatures (requires armature modifier)"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gpencil_generate_weights_exec; - ot->poll = gpencil_generate_weights_poll; - - ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, 0, "Mode", ""); - - prop = RNA_def_enum(ot->srna, "armature", DummyRNA_DEFAULT_items, 0, "Armature", "Armature to use"); - RNA_def_enum_funcs(prop, gpencil_armatures_enum_itemf); - - RNA_def_float( - ot->srna, "ratio", DEFAULT_RATIO, 0.0f, 2.0f, "Ratio", - "Ratio between bone length and influence radius", 0.001f, 1.0f); - - RNA_def_float( - ot->srna, "decay", DEFAULT_DECAY, 0.0f, 1.0f, "Decay", - "Factor to reduce influence depending of distance to bone axis", 0.0f, 1.0f); + static const EnumPropertyItem mode_type[] = { + {GP_ARMATURE_NAME, "NAME", 0, "Empty Groups", ""}, + {GP_ARMATURE_AUTO, "AUTO", 0, "Automatic Weights", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Generate Automatic Weights"; + ot->idname = "GPENCIL_OT_generate_weights"; + ot->description = "Generate automatic weights for armatures (requires armature modifier)"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gpencil_generate_weights_exec; + ot->poll = gpencil_generate_weights_poll; + + ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, 0, "Mode", ""); + + prop = RNA_def_enum( + ot->srna, "armature", DummyRNA_DEFAULT_items, 0, "Armature", "Armature to use"); + RNA_def_enum_funcs(prop, gpencil_armatures_enum_itemf); + + RNA_def_float(ot->srna, + "ratio", + DEFAULT_RATIO, + 0.0f, + 2.0f, + "Ratio", + "Ratio between bone length and influence radius", + 0.001f, + 1.0f); + + RNA_def_float(ot->srna, + "decay", + DEFAULT_DECAY, + 0.0f, + 1.0f, + "Decay", + "Factor to reduce influence depending of distance to bone axis", + 0.0f, + 1.0f); } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 8f64ec1c868..ef0ebd06d07 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -86,145 +85,133 @@ /* Context for brush operators */ typedef struct tGP_BrushEditData { - /* Current editor/region/etc. */ - /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ - Depsgraph *depsgraph; - Scene *scene; - Object *object; - - ScrArea *sa; - ARegion *ar; - - /* Current GPencil datablock */ - bGPdata *gpd; + /* Current editor/region/etc. */ + /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ + Depsgraph *depsgraph; + Scene *scene; + Object *object; - /* Brush Settings */ - GP_Sculpt_Settings *settings; - GP_Sculpt_Data *gp_brush; - GP_Sculpt_Data *gp_brush_old; + ScrArea *sa; + ARegion *ar; - eGP_Sculpt_Types brush_type; - eGP_Sculpt_Types brush_type_old; - eGP_Sculpt_Flag flag; + /* Current GPencil datablock */ + bGPdata *gpd; - /* Space Conversion Data */ - GP_SpaceConversion gsc; + /* Brush Settings */ + GP_Sculpt_Settings *settings; + GP_Sculpt_Data *gp_brush; + GP_Sculpt_Data *gp_brush_old; + eGP_Sculpt_Types brush_type; + eGP_Sculpt_Types brush_type_old; + eGP_Sculpt_Flag flag; - /* Is the brush currently painting? */ - bool is_painting; - bool is_weight_mode; + /* Space Conversion Data */ + GP_SpaceConversion gsc; - /* Start of new sculpt stroke */ - bool first; + /* Is the brush currently painting? */ + bool is_painting; + bool is_weight_mode; - /* Is multiframe editing enabled, and are we using falloff for that? */ - bool is_multiframe; - bool use_multiframe_falloff; + /* Start of new sculpt stroke */ + bool first; - /* Current frame */ - int cfra; + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + /* Current frame */ + int cfra; - /* Brush Runtime Data: */ - /* - position and pressure - * - the *_prev variants are the previous values - */ - float mval[2], mval_prev[2]; - float pressure, pressure_prev; + /* Brush Runtime Data: */ + /* - position and pressure + * - the *_prev variants are the previous values + */ + float mval[2], mval_prev[2]; + float pressure, pressure_prev; - /* - effect vector (e.g. 2D/3D translation for grab brush) */ - float dvec[3]; + /* - effect vector (e.g. 2D/3D translation for grab brush) */ + float dvec[3]; - /* - multiframe falloff factor */ - float mf_falloff; + /* - multiframe falloff factor */ + float mf_falloff; - /* active vertex group */ - int vrgroup; + /* active vertex group */ + int vrgroup; + /* brush geometry (bounding box) */ + rcti brush_rect; - /* brush geometry (bounding box) */ - rcti brush_rect; + /* Custom data for certain brushes */ + /* - map from bGPDstroke's to structs containing custom data about those strokes */ + GHash *stroke_customdata; + /* - general customdata */ + void *customdata; - /* Custom data for certain brushes */ - /* - map from bGPDstroke's to structs containing custom data about those strokes */ - GHash *stroke_customdata; - /* - general customdata */ - void *customdata; + /* Timer for in-place accumulation of brush effect */ + wmTimer *timer; + bool timerTick; /* is this event from a timer */ - - /* Timer for in-place accumulation of brush effect */ - wmTimer *timer; - bool timerTick; /* is this event from a timer */ - - RNG *rng; + RNG *rng; } tGP_BrushEditData; - /* Callback for performing some brush operation on a single point */ typedef bool (*GP_BrushApplyCb)( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]); + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]); /* ************************************************ */ /* Utility Functions */ /* apply lock axis reset */ -static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3]) +static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, + bGPDspoint *pt, + const float save_pt[3]) { - if (gso->sa->spacetype != SPACE_VIEW3D) { - return; - } - - const ToolSettings *ts = gso->scene->toolsettings; - const View3DCursor *cursor = &gso->scene->cursor; - const int axis = ts->gp_sculpt.lock_axis; - - /* lock axis control */ - switch (axis) { - case GP_LOCKAXIS_X: - { - pt->x = save_pt[0]; - break; - } - case GP_LOCKAXIS_Y: - { - pt->y = save_pt[1]; - break; - } - case GP_LOCKAXIS_Z: - { - pt->z = save_pt[2]; - break; - } - case GP_LOCKAXIS_CURSOR: - { - /* compute a plane with cursor normal and position of the point - before do the sculpt */ - const float scale[3] = { 1.0f, 1.0f, 1.0f }; - float plane_normal[3] = { 0.0f, 0.0f, 1.0f }; - float plane[4]; - float mat[4][4]; - float r_close[3]; - - loc_eul_size_to_mat4(mat, - cursor->location, - cursor->rotation_euler, - scale); - - mul_mat3_m4_v3(mat, plane_normal); - plane_from_point_normal_v3(plane, save_pt, plane_normal); - - /* find closest point to the plane with the new position */ - closest_to_plane_v3(r_close, plane, &pt->x); - copy_v3_v3(&pt->x, r_close); - break; - } - default: - { - break; - } - } + if (gso->sa->spacetype != SPACE_VIEW3D) { + return; + } + + const ToolSettings *ts = gso->scene->toolsettings; + const View3DCursor *cursor = &gso->scene->cursor; + const int axis = ts->gp_sculpt.lock_axis; + + /* lock axis control */ + switch (axis) { + case GP_LOCKAXIS_X: { + pt->x = save_pt[0]; + break; + } + case GP_LOCKAXIS_Y: { + pt->y = save_pt[1]; + break; + } + case GP_LOCKAXIS_Z: { + pt->z = save_pt[2]; + break; + } + case GP_LOCKAXIS_CURSOR: { + /* compute a plane with cursor normal and position of the point + before do the sculpt */ + const float scale[3] = {1.0f, 1.0f, 1.0f}; + float plane_normal[3] = {0.0f, 0.0f, 1.0f}; + float plane[4]; + float mat[4][4]; + float r_close[3]; + + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); + + mul_mat3_m4_v3(mat, plane_normal); + plane_from_point_normal_v3(plane, save_pt, plane_normal); + + /* find closest point to the plane with the new position */ + closest_to_plane_v3(r_close, plane, &pt->x); + copy_v3_v3(&pt->x, r_close); + break; + } + default: { + break; + } + } } /* Context ---------------------------------------- */ @@ -232,22 +219,22 @@ static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, c /* Get the sculpting settings */ static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene) { - return &scene->toolsettings->gp_sculpt; + return &scene->toolsettings->gp_sculpt; } /* Get the active brush */ static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) { - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - GP_Sculpt_Data *gp_brush = NULL; - if (is_weight_mode) { - gp_brush = &gset->brush[gset->weighttype]; - } - else { - gp_brush = &gset->brush[gset->brushtype]; - } - - return gp_brush; + GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; + GP_Sculpt_Data *gp_brush = NULL; + if (is_weight_mode) { + gp_brush = &gset->brush[gset->weighttype]; + } + else { + gp_brush = &gset->brush[gset->brushtype]; + } + + return gp_brush; } /* Brush Operations ------------------------------- */ @@ -255,56 +242,56 @@ static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) /* Invert behavior of brush? */ static bool gp_brush_invert_check(tGP_BrushEditData *gso) { - /* The basic setting is the brush's setting (from the panel) */ - bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0); - - /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ - if (gso->flag & GP_SCULPT_FLAG_INVERT) { - invert ^= true; - } - - /* set temporary status */ - if (invert) { - gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT; - } - else { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - } - - return invert; + /* The basic setting is the brush's setting (from the panel) */ + bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0); + + /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ + if (gso->flag & GP_SCULPT_FLAG_INVERT) { + invert ^= true; + } + + /* set temporary status */ + if (invert) { + gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT; + } + else { + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + } + + return invert; } /* Compute strength of effect */ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2]) { - GP_Sculpt_Data *gp_brush = gso->gp_brush; + GP_Sculpt_Data *gp_brush = gso->gp_brush; - /* basic strength factor from brush settings */ - float influence = gp_brush->strength; + /* basic strength factor from brush settings */ + float influence = gp_brush->strength; - /* use pressure? */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) { - influence *= gso->pressure; - } + /* use pressure? */ + if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) { + influence *= gso->pressure; + } - /* distance fading */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - int mval_i[2]; - round_v2i_v2fl(mval_i, gso->mval); - float distance = (float)len_v2v2_int(mval_i, co); - float fac; + /* distance fading */ + if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, co); + float fac; - CLAMP(distance, 0.0f, (float)radius); - fac = 1.0f - (distance / (float)radius); + CLAMP(distance, 0.0f, (float)radius); + fac = 1.0f - (distance / (float)radius); - influence *= fac; - } + influence *= fac; + } - /* apply multiframe falloff */ - influence *= gso->mf_falloff; + /* apply multiframe falloff */ + influence *= gso->mf_falloff; - /* return influence */ - return influence; + /* return influence */ + return influence; } /* ************************************************ */ @@ -318,38 +305,34 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c /* A simple (but slower + inaccurate) smooth-brush implementation to test the algorithm for stroke smoothing */ static bool gp_brush_smooth_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - // GP_Sculpt_Data *gp_brush = gso->brush; - float inf = gp_brush_influence_calc(gso, radius, co); - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | - GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | - GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) - { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* perform smoothing */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - BKE_gpencil_smooth_stroke(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - return true; + // GP_Sculpt_Data *gp_brush = gso->brush; + float inf = gp_brush_influence_calc(gso, radius, co); + /* need one flag enabled by default */ + if ((gso->settings->flag & + (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | + GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { + gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; + } + + /* perform smoothing */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { + BKE_gpencil_smooth_stroke(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { + BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { + BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + return true; } /* ----------------------------------------------- */ @@ -357,77 +340,73 @@ static bool gp_brush_smooth_apply( /* Make lines thicker or thinner by the specified amounts */ static bool gp_brush_thickness_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength by 10, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; - - /* apply */ - // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff - if (gp_brush_invert_check(gso)) { - /* make line thinner - reduce stroke pressure */ - pt->pressure -= inf; - } - else { - /* make line thicker - increase stroke pressure */ - pt->pressure += inf; - } - - /* Pressure should stay within [0.0, 1.0] - * However, it is nice for volumetric strokes to be able to exceed - * the upper end of this range. Therefore, we don't actually clamp - * down on the upper end. - */ - if (pt->pressure < 0.0f) - pt->pressure = 0.0f; - - return true; + bGPDspoint *pt = gps->points + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* apply */ + // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->pressure -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->pressure += inf; + } + + /* Pressure should stay within [0.0, 1.0] + * However, it is nice for volumetric strokes to be able to exceed + * the upper end of this range. Therefore, we don't actually clamp + * down on the upper end. + */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; } - /* ----------------------------------------------- */ /* Color Strength Brush */ /* Make color more or less transparent by the specified amounts */ static bool gp_brush_strength_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 20.0f; - - /* apply */ - if (gp_brush_invert_check(gso)) { - /* make line more transparent - reduce alpha factor */ - pt->strength -= inf; - } - else { - /* make line more opaque - increase stroke strength */ - pt->strength += inf; - } - /* smooth the strength */ - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - - /* Strength should stay within [0.0, 1.0] */ - CLAMP(pt->strength, 0.0f, 1.0f); - - return true; + bGPDspoint *pt = gps->points + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 20.0f; + + /* apply */ + if (gp_brush_invert_check(gso)) { + /* make line more transparent - reduce alpha factor */ + pt->strength -= inf; + } + else { + /* make line more opaque - increase stroke strength */ + pt->strength += inf; + } + /* smooth the strength */ + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); + + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + return true; } - /* ----------------------------------------------- */ /* Grab Brush */ @@ -438,145 +417,145 @@ static bool gp_brush_strength_apply( * the brush region. */ typedef struct tGPSB_Grab_StrokeData { - /* array of indices to corresponding points in the stroke */ - int *points; - /* array of influence weights for each of the included points */ - float *weights; - - /* capacity of the arrays */ - int capacity; - /* actual number of items currently stored */ - int size; + /* array of indices to corresponding points in the stroke */ + int *points; + /* array of influence weights for each of the included points */ + float *weights; + + /* capacity of the arrays */ + int capacity; + /* actual number of items currently stored */ + int size; } tGPSB_Grab_StrokeData; /* initialise custom data for handling this stroke */ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) { - tGPSB_Grab_StrokeData *data = NULL; + tGPSB_Grab_StrokeData *data = NULL; - BLI_assert(gps->totpoints > 0); + BLI_assert(gps->totpoints > 0); - /* Check if there are buffers already (from a prior run) */ - if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { - /* Ensure that the caches are empty - * - Since we reuse these between different strokes, we don't - * want the previous invocation's data polluting the arrays - */ - data = BLI_ghash_lookup(gso->stroke_customdata, gps); - BLI_assert(data != NULL); + /* Check if there are buffers already (from a prior run) */ + if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { + /* Ensure that the caches are empty + * - Since we reuse these between different strokes, we don't + * want the previous invocation's data polluting the arrays + */ + data = BLI_ghash_lookup(gso->stroke_customdata, gps); + BLI_assert(data != NULL); - data->size = 0; /* minimum requirement - so that we can repopulate again */ + data->size = 0; /* minimum requirement - so that we can repopulate again */ - memset(data->points, 0, sizeof(int) * data->capacity); - memset(data->weights, 0, sizeof(float) * data->capacity); - } - else { - /* Create new instance */ - data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); + memset(data->points, 0, sizeof(int) * data->capacity); + memset(data->weights, 0, sizeof(float) * data->capacity); + } + else { + /* Create new instance */ + data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); - data->capacity = gps->totpoints; - data->size = 0; + data->capacity = gps->totpoints; + data->size = 0; - data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); - data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); + data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); + data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); - /* hook up to the cache */ - BLI_ghash_insert(gso->stroke_customdata, gps, data); - } + /* hook up to the cache */ + BLI_ghash_insert(gso->stroke_customdata, gps, data); + } } /* store references to stroke points in the initial stage */ static bool gp_brush_grab_store_points( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); - float inf = gp_brush_influence_calc(gso, radius, co); + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + float inf = gp_brush_influence_calc(gso, radius, co); - BLI_assert(data != NULL); - BLI_assert(data->size < data->capacity); + BLI_assert(data != NULL); + BLI_assert(data->size < data->capacity); - /* insert this point into the set of affected points */ - data->points[data->size] = pt_index; - data->weights[data->size] = inf; - data->size++; + /* insert this point into the set of affected points */ + data->points[data->size] = pt_index; + data->weights[data->size] = inf; + data->size++; - /* done */ - return true; + /* done */ + return true; } /* Compute effect vector for grab brush */ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) { - /* Convert mouse-movements to movement vector */ - // TODO: incorporate pressure into this? - // XXX: screen-space strokes in 3D space will suffer! - if (gso->sa->spacetype == SPACE_VIEW3D) { - RegionView3D *rv3d = gso->ar->regiondata; - float *rvec = gso->scene->cursor.location; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - - /* convert from 2D screenspace to 3D... */ - mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac); - } - else { - /* 2D - just copy */ - // XXX: view2d? - gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - gso->dvec[2] = 0.0f; /* unused */ - } + /* Convert mouse-movements to movement vector */ + // TODO: incorporate pressure into this? + // XXX: screen-space strokes in 3D space will suffer! + if (gso->sa->spacetype == SPACE_VIEW3D) { + RegionView3D *rv3d = gso->ar->regiondata; + float *rvec = gso->scene->cursor.location; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + + /* convert from 2D screenspace to 3D... */ + mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac); + } + else { + /* 2D - just copy */ + // XXX: view2d? + gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + gso->dvec[2] = 0.0f; /* unused */ + } } /* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached( - tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4]) +static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, + bGPDstroke *gps, + float diff_mat[4][4]) { - tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); - int i; - - /* Apply dvec to all of the stored points */ - for (i = 0; i < data->size; i++) { - bGPDspoint *pt = &gps->points[data->points[i]]; - float delta[3] = {0.0f}; - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, data->weights[i]); - - float fpt[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - /* apply transformation */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); - /* apply */ - add_v3_v3v3(&pt->x, fpt, delta); - /* undo transformation to the init parent position */ - float inverse_diff_mat[4][4]; - invert_m4_m4(inverse_diff_mat, diff_mat); - mul_m4_v3(inverse_diff_mat, &pt->x); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + int i; + + /* Apply dvec to all of the stored points */ + for (i = 0; i < data->size; i++) { + bGPDspoint *pt = &gps->points[data->points[i]]; + float delta[3] = {0.0f}; + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + + float fpt[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3v3(&pt->x, fpt, delta); + /* undo transformation to the init parent position */ + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; } /* free customdata used for handling this stroke */ static void gp_brush_grab_stroke_free(void *ptr) { - tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; + tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; - /* free arrays */ - MEM_freeN(data->points); - MEM_freeN(data->weights); + /* free arrays */ + MEM_freeN(data->points); + MEM_freeN(data->weights); - /* ... and this item itself, since it was also allocated */ - MEM_freeN(data); + /* ... and this item itself, since it was also allocated */ + MEM_freeN(data); } /* ----------------------------------------------- */ @@ -584,29 +563,28 @@ static void gp_brush_grab_stroke_free(void *ptr) /* NOTE: Depends on gp_brush_grab_calc_dvec() */ static bool gp_brush_push_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); - float inf = gp_brush_influence_calc(gso, radius, co); - float delta[3] = {0.0f}; + float inf = gp_brush_influence_calc(gso, radius, co); + float delta[3] = {0.0f}; - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, inf); + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, inf); - /* apply */ - add_v3_v3(&pt->x, delta); + /* apply */ + add_v3_v3(&pt->x, delta); - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->flag |= GP_STROKE_RECALC_GEOMETRY; - /* done */ - return true; + /* done */ + return true; } /* ----------------------------------------------- */ @@ -615,84 +593,83 @@ static bool gp_brush_push_apply( /* Compute reference midpoint for the brush - this is what we'll be moving towards */ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) { - if (gso->sa->spacetype == SPACE_VIEW3D) { - /* Convert mouse position to 3D space - * See: gpencil_paint.c :: gp_stroke_convertcoords() - */ - RegionView3D *rv3d = gso->ar->regiondata; - const float *rvec = gso->scene->cursor.location; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - copy_v2_v2(mval_f, gso->mval); - float mval_prj[2]; - float dvec[3]; - - - if (ED_view3d_project_float_global(gso->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gso->ar, mval_f, dvec, zfac); - sub_v3_v3v3(gso->dvec, rvec, dvec); - } - else { - zero_v3(gso->dvec); - } - } - else { - /* Just 2D coordinates */ - // XXX: fix View2D offsets later - gso->dvec[0] = (float)gso->mval[0]; - gso->dvec[1] = (float)gso->mval[1]; - gso->dvec[2] = 0.0f; - } + if (gso->sa->spacetype == SPACE_VIEW3D) { + /* Convert mouse position to 3D space + * See: gpencil_paint.c :: gp_stroke_convertcoords() + */ + RegionView3D *rv3d = gso->ar->regiondata; + const float *rvec = gso->scene->cursor.location; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + copy_v2_v2(mval_f, gso->mval); + float mval_prj[2]; + float dvec[3]; + + if (ED_view3d_project_float_global(gso->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(gso->ar, mval_f, dvec, zfac); + sub_v3_v3v3(gso->dvec, rvec, dvec); + } + else { + zero_v3(gso->dvec); + } + } + else { + /* Just 2D coordinates */ + // XXX: fix View2D offsets later + gso->dvec[0] = (float)gso->mval[0]; + gso->dvec[1] = (float)gso->mval[1]; + gso->dvec[2] = 0.0f; + } } /* Shrink distance between midpoint and this point... */ static bool gp_brush_pinch_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float fac, inf; - float vec[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Scale down standard influence value to get it more manageable... - * - No damping = Unmanageable at > 0.5 strength - * - Div 10 = Not enough effect - * - Div 5 = Happy medium... (by trial and error) - */ - inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; - - /* 1) Make this point relative to the cursor/midpoint (dvec) */ - sub_v3_v3v3(vec, &pt->x, gso->dvec); - - /* 2) Shrink the distance by pulling the point towards the midpoint - * (0.0 = at midpoint, 1 = at edge of brush region) - * OR - * Increase the distance (if inverting the brush action!) - */ - if (gp_brush_invert_check(gso)) { - /* Inflate (inverse) */ - fac = 1.0f + (inf * inf); /* squared to temper the effect... */ - } - else { - /* Shrink (default) */ - fac = 1.0f - (inf * inf); /* squared to temper the effect... */ - } - mul_v3_fl(vec, fac); - - /* 3) Translate back to original space, with the shrinkage applied */ - add_v3_v3v3(&pt->x, gso->dvec, vec); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + bGPDspoint *pt = gps->points + pt_index; + float fac, inf; + float vec[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Scale down standard influence value to get it more manageable... + * - No damping = Unmanageable at > 0.5 strength + * - Div 10 = Not enough effect + * - Div 5 = Happy medium... (by trial and error) + */ + inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; + + /* 1) Make this point relative to the cursor/midpoint (dvec) */ + sub_v3_v3v3(vec, &pt->x, gso->dvec); + + /* 2) Shrink the distance by pulling the point towards the midpoint + * (0.0 = at midpoint, 1 = at edge of brush region) + * OR + * Increase the distance (if inverting the brush action!) + */ + if (gp_brush_invert_check(gso)) { + /* Inflate (inverse) */ + fac = 1.0f + (inf * inf); /* squared to temper the effect... */ + } + else { + /* Shrink (default) */ + fac = 1.0f - (inf * inf); /* squared to temper the effect... */ + } + mul_v3_fl(vec, fac); + + /* 3) Translate back to original space, with the shrinkage applied */ + add_v3_v3v3(&pt->x, gso->dvec, vec); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* done */ + return true; } /* ----------------------------------------------- */ @@ -703,268 +680,259 @@ static bool gp_brush_pinch_apply( */ static bool gp_brush_twist_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float angle, inf; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Angle to rotate by */ - inf = gp_brush_influence_calc(gso, radius, co); - angle = DEG2RADF(1.0f) * inf; - - if (gp_brush_invert_check(gso)) { - /* invert angle that we rotate by */ - angle *= -1; - } - - /* Rotate in 2D or 3D space? */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* Perform rotation in 3D space... */ - RegionView3D *rv3d = gso->ar->regiondata; - float rmat[3][3]; - float axis[3]; - float vec[3]; - - /* Compute rotation matrix - rotate around view vector by angle */ - negate_v3_v3(axis, rv3d->persinv[2]); - normalize_v3(axis); - - axis_angle_normalized_to_mat3(rmat, axis, angle); - - /* Rotate point (no matrix-space transforms needed, as GP points are in world space) */ - sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center - * (center is stored in dvec) */ - mul_m3_v3(rmat, vec); - add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - else { - const float axis[3] = {0.0f, 0.0f, 1.0f}; - float vec[3] = {0.0f}; - float rmat[3][3]; - - /* Express position of point relative to cursor, ready to rotate */ - // XXX: There is still some offset here, but it's close to working as expected... - vec[0] = (float)(co[0] - gso->mval[0]); - vec[1] = (float)(co[1] - gso->mval[1]); - - /* rotate point */ - axis_angle_normalized_to_mat3(rmat, axis, angle); - mul_m3_v3(rmat, vec); - - /* Convert back to screen-coordinates */ - vec[0] += (float)gso->mval[0]; - vec[1] += (float)gso->mval[1]; - - /* Map from screen-coordinates to final coordinate space */ - if (gps->flag & GP_STROKE_2DSPACE) { - View2D *v2d = gso->gsc.v2d; - UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); - } - else { - // XXX - copy_v2_v2(&pt->x, vec); - } - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + bGPDspoint *pt = gps->points + pt_index; + float angle, inf; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Angle to rotate by */ + inf = gp_brush_influence_calc(gso, radius, co); + angle = DEG2RADF(1.0f) * inf; + + if (gp_brush_invert_check(gso)) { + /* invert angle that we rotate by */ + angle *= -1; + } + + /* Rotate in 2D or 3D space? */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* Perform rotation in 3D space... */ + RegionView3D *rv3d = gso->ar->regiondata; + float rmat[3][3]; + float axis[3]; + float vec[3]; + + /* Compute rotation matrix - rotate around view vector by angle */ + negate_v3_v3(axis, rv3d->persinv[2]); + normalize_v3(axis); + + axis_angle_normalized_to_mat3(rmat, axis, angle); + + /* Rotate point (no matrix-space transforms needed, as GP points are in world space) */ + sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center + * (center is stored in dvec) */ + mul_m3_v3(rmat, vec); + add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + else { + const float axis[3] = {0.0f, 0.0f, 1.0f}; + float vec[3] = {0.0f}; + float rmat[3][3]; + + /* Express position of point relative to cursor, ready to rotate */ + // XXX: There is still some offset here, but it's close to working as expected... + vec[0] = (float)(co[0] - gso->mval[0]); + vec[1] = (float)(co[1] - gso->mval[1]); + + /* rotate point */ + axis_angle_normalized_to_mat3(rmat, axis, angle); + mul_m3_v3(rmat, vec); + + /* Convert back to screen-coordinates */ + vec[0] += (float)gso->mval[0]; + vec[1] += (float)gso->mval[1]; + + /* Map from screen-coordinates to final coordinate space */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d = gso->gsc.v2d; + UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); + } + else { + // XXX + copy_v2_v2(&pt->x, vec); + } + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* done */ + return true; } - /* ----------------------------------------------- */ /* Randomize Brush */ /* Apply some random jitter to the point */ static bool gp_brush_randomize_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Amount of jitter to apply depends on the distance of the point to the cursor, - * as well as the strength of the brush - */ - const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; - const float fac = BLI_rng_get_float(gso->rng) * inf; - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | - GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | - GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) - { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* apply random to position */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * view-space as needed. - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - /* scale the displacement by the random displacement, and apply */ - if (BLI_rng_get_float(gso->rng) > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - - //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->ar->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - } - else { - /* ERROR */ - BLI_assert(!"3D stroke being sculpted in non-3D view"); - } - } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); - } - } - /* apply random to strength */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->strength += fac; - } - else { - pt->strength -= fac; - } - CLAMP_MIN(pt->strength, 0.0f); - CLAMP_MAX(pt->strength, 1.0f); - } - /* apply random to thickness (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->pressure += fac; - } - else { - pt->pressure -= fac; - } - /* only limit lower value */ - CLAMP_MIN(pt->pressure, 0.0f); - } - /* apply random to UV (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->uv_rot += fac; - } - else { - pt->uv_rot -= fac; - } - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Amount of jitter to apply depends on the distance of the point to the cursor, + * as well as the strength of the brush + */ + const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; + const float fac = BLI_rng_get_float(gso->rng) * inf; + /* need one flag enabled by default */ + if ((gso->settings->flag & + (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | + GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { + gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; + } + + /* apply random to position */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * view-space as needed. + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_rng_get_float(gso->rng) > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + if (gso->sa->spacetype == SPACE_VIEW3D) { + bool flip; + RegionView3D *rv3d = gso->ar->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + } + else { + /* ERROR */ + BLI_assert(!"3D stroke being sculpted in non-3D view"); + } + } + else { + /* 2D: As-is */ + // XXX: v2d scaling/offset? + float nco[2]; + nco[0] = (float)co[0] + svec[0]; + nco[1] = (float)co[1] + svec[1]; + + copy_v2_v2(&pt->x, nco); + } + } + /* apply random to strength */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); + } + /* apply random to thickness (use pressure) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* apply random to UV (use pressure) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->uv_rot += fac; + } + else { + pt->uv_rot -= fac; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* done */ + return true; } /* Weight Paint Brush */ /* Change weight paint for vertex groups */ static bool gp_brush_weight_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - const int radius, const int co[2]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - /* create dvert */ - BKE_gpencil_dvert_ensure(gps); - - bGPDspoint *pt = gps->points + pt_index; - MDeformVert *dvert = gps->dvert + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength by 10, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; - - /* need a vertex group */ - if (gso->vrgroup == -1) { - if (gso->object) { - BKE_object_defgroup_add(gso->object); - gso->vrgroup = 0; - } - } - else { - bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); - if (defgroup->flag & DG_LOCK_WEIGHT) { - return false; - } - } - /* get current weight */ - MDeformWeight *dw = defvert_verify_index(dvert, gso->vrgroup); - float curweight = dw ? dw->weight : 0.0f; - - if (gp_brush_invert_check(gso)) { - /* reduce weight */ - curweight -= inf; - } - else { - /* increase weight */ - curweight += inf; - } - - /* verify target weight */ - CLAMP_MAX(curweight, gso->gp_brush->weight); - - CLAMP(curweight, 0.0f, 1.0f); - if (dw) { - dw->weight = curweight; - } - - /* weight should stay within [0.0, 1.0] */ - if (pt->pressure < 0.0f) - pt->pressure = 0.0f; - - return true; + /* create dvert */ + BKE_gpencil_dvert_ensure(gps); + + bGPDspoint *pt = gps->points + pt_index; + MDeformVert *dvert = gps->dvert + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* need a vertex group */ + if (gso->vrgroup == -1) { + if (gso->object) { + BKE_object_defgroup_add(gso->object); + gso->vrgroup = 0; + } + } + else { + bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); + if (defgroup->flag & DG_LOCK_WEIGHT) { + return false; + } + } + /* get current weight */ + MDeformWeight *dw = defvert_verify_index(dvert, gso->vrgroup); + float curweight = dw ? dw->weight : 0.0f; + + if (gp_brush_invert_check(gso)) { + /* reduce weight */ + curweight -= inf; + } + else { + /* increase weight */ + curweight += inf; + } + + /* verify target weight */ + CLAMP_MAX(curweight, gso->gp_brush->weight); + + CLAMP(curweight, 0.0f, 1.0f); + if (dw) { + dw->weight = curweight; + } + + /* weight should stay within [0.0, 1.0] */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; } - - /* ************************************************ */ /* Non Callback-Based Brushes */ @@ -980,217 +948,218 @@ static bool gp_brush_weight_apply( /* Custom state data for clone brush */ typedef struct tGPSB_CloneBrushData { - /* midpoint of the strokes on the clipboard */ - float buffer_midpoint[3]; + /* midpoint of the strokes on the clipboard */ + float buffer_midpoint[3]; - /* number of strokes in the paste buffer (and/or to be created each time) */ - size_t totitems; + /* number of strokes in the paste buffer (and/or to be created each time) */ + size_t totitems; - /* for "stamp" mode, the currently pasted brushes */ - bGPDstroke **new_strokes; + /* for "stamp" mode, the currently pasted brushes */ + bGPDstroke **new_strokes; - /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */ - GHash *new_colors; + /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */ + GHash *new_colors; } tGPSB_CloneBrushData; /* Initialise "clone" brush data */ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data; - bGPDstroke *gps; - - /* init custom data */ - gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); - - /* compute midpoint of strokes on clipboard */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - const float dfac = 1.0f / ((float)gps->totpoints); - float mid[3] = {0.0f}; - - bGPDspoint *pt; - int i; - - /* compute midpoint of this stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float co[3]; - - mul_v3_v3fl(co, &pt->x, dfac); - add_v3_v3(mid, co); - } - - /* combine this stroke's data with the main data */ - add_v3_v3(data->buffer_midpoint, mid); - data->totitems++; - } - } - - /* Divide the midpoint by the number of strokes, to finish averaging it */ - if (data->totitems > 1) { - mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); - } - - /* Create a buffer for storing the current strokes */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, "cloned strokes ptr array"); - } - - /* Init colormap for mapping between the pasted stroke's source color (names) - * and the final colours that will be used here instead. - */ - data->new_colors = gp_copybuf_validate_colormap(C); + tGPSB_CloneBrushData *data; + bGPDstroke *gps; + + /* init custom data */ + gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); + + /* compute midpoint of strokes on clipboard */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + const float dfac = 1.0f / ((float)gps->totpoints); + float mid[3] = {0.0f}; + + bGPDspoint *pt; + int i; + + /* compute midpoint of this stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float co[3]; + + mul_v3_v3fl(co, &pt->x, dfac); + add_v3_v3(mid, co); + } + + /* combine this stroke's data with the main data */ + add_v3_v3(data->buffer_midpoint, mid); + data->totitems++; + } + } + + /* Divide the midpoint by the number of strokes, to finish averaging it */ + if (data->totitems > 1) { + mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); + } + + /* Create a buffer for storing the current strokes */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, + "cloned strokes ptr array"); + } + + /* Init colormap for mapping between the pasted stroke's source color (names) + * and the final colours that will be used here instead. + */ + data->new_colors = gp_copybuf_validate_colormap(C); } /* Free custom data used for "clone" brush */ static void gp_brush_clone_free(tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - - /* free strokes array */ - if (data->new_strokes) { - MEM_freeN(data->new_strokes); - data->new_strokes = NULL; - } - - /* free copybuf colormap */ - if (data->new_colors) { - BLI_ghash_free(data->new_colors, NULL, NULL); - data->new_colors = NULL; - } - - /* free the customdata itself */ - MEM_freeN(data); - gso->customdata = NULL; + tGPSB_CloneBrushData *data = gso->customdata; + + /* free strokes array */ + if (data->new_strokes) { + MEM_freeN(data->new_strokes); + data->new_strokes = NULL; + } + + /* free copybuf colormap */ + if (data->new_colors) { + BLI_ghash_free(data->new_colors, NULL, NULL); + data->new_colors = NULL; + } + + /* free the customdata itself */ + MEM_freeN(data); + gso->customdata = NULL; } /* Create new copies of the strokes on the clipboard */ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - - Object *ob = CTX_data_active_object(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - bGPDstroke *gps; - - float delta[3]; - size_t strokes_added = 0; - - /* Compute amount to offset the points by */ - /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ - - gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ - sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); - - /* Copy each stroke into the layer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDstroke *new_stroke; - bGPDspoint *pt; - int i; - - /* Make a new stroke */ - new_stroke = MEM_dupallocN(gps); - - new_stroke->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); - } - new_stroke->triangles = MEM_dupallocN(gps->triangles); - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); - - /* Fix color references */ - Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); - gps->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); - if (!ma || gps->mat_nr) { - gps->mat_nr = 0; - } - /* Adjust all the stroke's points, so that the strokes - * get pasted relative to where the cursor is now - */ - for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { - /* assume that the delta can just be applied, and then everything works */ - add_v3_v3(&pt->x, delta); - } - - /* Store ref for later */ - if ((data->new_strokes) && (strokes_added < data->totitems)) { - data->new_strokes[strokes_added] = new_stroke; - strokes_added++; - } - } - } + tGPSB_CloneBrushData *data = gso->customdata; + + Object *ob = CTX_data_active_object(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + bGPDstroke *gps; + + float delta[3]; + size_t strokes_added = 0; + + /* Compute amount to offset the points by */ + /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ + + gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ + sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); + + /* Copy each stroke into the layer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + bGPDstroke *new_stroke; + bGPDspoint *pt; + int i; + + /* Make a new stroke */ + new_stroke = MEM_dupallocN(gps); + + new_stroke->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); + } + new_stroke->triangles = MEM_dupallocN(gps->triangles); + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + + /* Fix color references */ + Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); + gps->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); + if (!ma || gps->mat_nr) { + gps->mat_nr = 0; + } + /* Adjust all the stroke's points, so that the strokes + * get pasted relative to where the cursor is now + */ + for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { + /* assume that the delta can just be applied, and then everything works */ + add_v3_v3(&pt->x, delta); + } + + /* Store ref for later */ + if ((data->new_strokes) && (strokes_added < data->totitems)) { + data->new_strokes[strokes_added] = new_stroke; + strokes_added++; + } + } + } } /* Move newly-added strokes around - "Stamp" mode of the Clone brush */ static void gp_brush_clone_adjust(tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - size_t snum; - - /* Compute the amount of movement to apply (overwrites dvec) */ - gp_brush_grab_calc_dvec(gso); - - /* For each of the stored strokes, apply the offset to each point */ - /* NOTE: Again this assumes that in the 3D view, we only have 3d space and not screenspace strokes... */ - for (snum = 0; snum < data->totitems; snum++) { - bGPDstroke *gps = data->new_strokes[snum]; - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - /* "Smudge" Effect when falloff is enabled */ - float delta[3] = {0.0f}; - int sco[2] = {0}; - float influence; - - /* compute influence on point */ - gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); - influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco); - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, influence); - - /* apply */ - add_v3_v3(&pt->x, delta); - } - else { - /* Just apply the offset - All points move perfectly in sync with the cursor */ - add_v3_v3(&pt->x, gso->dvec); - } - } - } + tGPSB_CloneBrushData *data = gso->customdata; + size_t snum; + + /* Compute the amount of movement to apply (overwrites dvec) */ + gp_brush_grab_calc_dvec(gso); + + /* For each of the stored strokes, apply the offset to each point */ + /* NOTE: Again this assumes that in the 3D view, we only have 3d space and not screenspace strokes... */ + for (snum = 0; snum < data->totitems; snum++) { + bGPDstroke *gps = data->new_strokes[snum]; + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { + /* "Smudge" Effect when falloff is enabled */ + float delta[3] = {0.0f}; + int sco[2] = {0}; + float influence; + + /* compute influence on point */ + gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); + influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco); + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, influence); + + /* apply */ + add_v3_v3(&pt->x, delta); + } + else { + /* Just apply the offset - All points move perfectly in sync with the cursor */ + add_v3_v3(&pt->x, gso->dvec); + } + } + } } /* Entrypoint for applying "clone" brush */ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) { - /* Which "mode" are we operating in? */ - if (gso->first) { - /* Create initial clones */ - gp_brush_clone_add(C, gso); - } - else { - /* Stamp or Continuous Mode */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - /* Stamp - Proceed to translate the newly added strokes */ - gp_brush_clone_adjust(gso); - } - else { - /* Continuous - Just keep pasting everytime we move */ - /* TODO: The spacing of repeat should be controlled using a "stepsize" or similar property? */ - gp_brush_clone_add(C, gso); - } - } - - return true; + /* Which "mode" are we operating in? */ + if (gso->first) { + /* Create initial clones */ + gp_brush_clone_add(C, gso); + } + else { + /* Stamp or Continuous Mode */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + /* Stamp - Proceed to translate the newly added strokes */ + gp_brush_clone_adjust(gso); + } + else { + /* Continuous - Just keep pasting everytime we move */ + /* TODO: The spacing of repeat should be controlled using a "stepsize" or similar property? */ + gp_brush_clone_add(C, gso); + } + } + + return true; } /* ************************************************ */ @@ -1198,18 +1167,19 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) { - const char *brush_name = NULL; - char str[UI_MAX_DRAW_STR] = ""; + const char *brush_name = NULL; + char str[UI_MAX_DRAW_STR] = ""; - RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); + RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); - BLI_snprintf(str, sizeof(str), - IFACE_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" - " | Ctrl to Invert Action | Wheel Up/Down for Size " - " | Shift-Wheel Up/Down for Strength"), - (brush_name) ? brush_name : "<?>"); + BLI_snprintf(str, + sizeof(str), + IFACE_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" + " | Ctrl to Invert Action | Wheel Up/Down for Size " + " | Shift-Wheel Up/Down for Strength"), + (brush_name) ? brush_name : "<?>"); - ED_workspace_status_text(C, str); + ED_workspace_status_text(C, str); } /* ************************************************ */ @@ -1219,586 +1189,576 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) static bool gpsculpt_brush_init(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); - const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL; - /* set the brush using the tool */ + const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL; + /* set the brush using the tool */ #if 0 - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype; + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype; #endif - tGP_BrushEditData *gso; - - /* setup operator data */ - gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); - op->customdata = gso; - - gso->depsgraph = CTX_data_depsgraph(C); - /* store state */ - gso->settings = gpsculpt_get_settings(scene); - gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode); - gso->is_weight_mode = is_weight_mode; - - if (is_weight_mode) { - gso->brush_type = gso->settings->weighttype; - } - else { - gso->brush_type = gso->settings->brushtype; - } - - /* Random generator, only init once. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= POINTER_AS_UINT(gso); - gso->rng = BLI_rng_new(rng_seed); - - gso->is_painting = false; - gso->first = true; - - gso->gpd = ED_gpencil_data_get_active(C); - gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ - - /* some brushes cannot use pressure for radius */ - if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS; - } - - gso->scene = scene; - gso->object = ob; - if (ob) { - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { - gso->vrgroup = -1; - } - } - else { - gso->vrgroup = - 1; - } - - gso->sa = CTX_wm_area(C); - gso->ar = CTX_wm_region(C); - - /* multiframe settings */ - gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); - gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; - - /* Init multi-edit falloff curve data before doing anything, - * so we won't have to do it again later. */ - if (gso->is_multiframe) { - curvemapping_initialize(ts->gp_sculpt.cur_falloff); - } - - /* initialise custom data for brushes */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_CLONE: - { - bGPDstroke *gps; - bool found = false; - - /* check that there are some usable strokes in the buffer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - found = true; - break; - } - } - - if (found == false) { - /* STOP HERE! Nothing to paste! */ - BKE_report(op->reports, RPT_ERROR, - "Copy some strokes to the clipboard before using the Clone brush to paste copies of them"); - - MEM_freeN(gso); - op->customdata = NULL; - return false; - } - else { - /* initialise customdata */ - gp_brush_clone_init(C, gso); - } - break; - } - - case GP_SCULPT_TYPE_GRAB: - { - /* initialise the cache needed for this brush */ - gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); - break; - } - - /* Others - No customdata needed */ - default: - break; - } - - - /* setup space conversions */ - gp_point_conversion_init(C, &gso->gsc); - - /* update header */ - gpsculpt_brush_header_set(C, gso); - - /* setup cursor drawing */ - //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - return true; + tGP_BrushEditData *gso; + + /* setup operator data */ + gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); + op->customdata = gso; + + gso->depsgraph = CTX_data_depsgraph(C); + /* store state */ + gso->settings = gpsculpt_get_settings(scene); + gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode); + gso->is_weight_mode = is_weight_mode; + + if (is_weight_mode) { + gso->brush_type = gso->settings->weighttype; + } + else { + gso->brush_type = gso->settings->brushtype; + } + + /* Random generator, only init once. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= POINTER_AS_UINT(gso); + gso->rng = BLI_rng_new(rng_seed); + + gso->is_painting = false; + gso->first = true; + + gso->gpd = ED_gpencil_data_get_active(C); + gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ + + /* some brushes cannot use pressure for radius */ + if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) { + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS; + } + + gso->scene = scene; + gso->object = ob; + if (ob) { + gso->vrgroup = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = -1; + } + } + else { + gso->vrgroup = -1; + } + + gso->sa = CTX_wm_area(C); + gso->ar = CTX_wm_region(C); + + /* multiframe settings */ + gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + + /* Init multi-edit falloff curve data before doing anything, + * so we won't have to do it again later. */ + if (gso->is_multiframe) { + curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + + /* initialise custom data for brushes */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_CLONE: { + bGPDstroke *gps; + bool found = false; + + /* check that there are some usable strokes in the buffer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + found = true; + break; + } + } + + if (found == false) { + /* STOP HERE! Nothing to paste! */ + BKE_report(op->reports, + RPT_ERROR, + "Copy some strokes to the clipboard before using the Clone brush to paste " + "copies of them"); + + MEM_freeN(gso); + op->customdata = NULL; + return false; + } + else { + /* initialise customdata */ + gp_brush_clone_init(C, gso); + } + break; + } + + case GP_SCULPT_TYPE_GRAB: { + /* initialise the cache needed for this brush */ + gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); + break; + } + + /* Others - No customdata needed */ + default: + break; + } + + /* setup space conversions */ + gp_point_conversion_init(C, &gso->gsc); + + /* update header */ + gpsculpt_brush_header_set(C, gso); + + /* setup cursor drawing */ + //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + return true; } static void gpsculpt_brush_exit(bContext *C, wmOperator *op) { - tGP_BrushEditData *gso = op->customdata; - wmWindow *win = CTX_wm_window(C); - - /* free brush-specific data */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: - { - /* Free per-stroke customdata - * - Keys don't need to be freed, as those are the strokes - * - Values assigned to those keys do, as they are custom structs - */ - BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); - break; - } - - case GP_SCULPT_TYPE_CLONE: - { - /* Free customdata */ - gp_brush_clone_free(gso); - break; - } - - default: - break; - } - - /* unregister timer (only used for realtime) */ - if (gso->timer) { - WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); - } - - if (gso->rng != NULL) { - BLI_rng_free(gso->rng); - } - - /* disable cursor and headerprints */ - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - - /* disable temp invert flag */ - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - - /* free operator data */ - MEM_freeN(gso); - op->customdata = NULL; + tGP_BrushEditData *gso = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* free brush-specific data */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_GRAB: { + /* Free per-stroke customdata + * - Keys don't need to be freed, as those are the strokes + * - Values assigned to those keys do, as they are custom structs + */ + BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); + break; + } + + case GP_SCULPT_TYPE_CLONE: { + /* Free customdata */ + gp_brush_clone_free(gso); + break; + } + + default: + break; + } + + /* unregister timer (only used for realtime) */ + if (gso->timer) { + WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); + } + + if (gso->rng != NULL) { + BLI_rng_free(gso->rng); + } + + /* disable cursor and headerprints */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + + /* disable temp invert flag */ + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + + /* free operator data */ + MEM_freeN(gso); + op->customdata = NULL; } /* poll callback for stroke sculpting operator(s) */ static bool gpsculpt_brush_poll(bContext *C) { - /* NOTE: this is a bit slower, but is the most accurate... */ - return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } /* Init Sculpt Stroke ---------------------------------- */ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) { - bGPdata *gpd = gso->gpd; - - bGPDlayer *gpl; - int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); - - /* only try to add a new frame if this is the first stroke, or the frame has changed */ - if ((gpd == NULL) || (cfra_eval == gso->cfra)) - return; - - /* go through each layer, and ensure that we've got a valid frame to use */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - - /* Make a new frame to work on if the layer's frame and the current scene frame don't match up - * - This is useful when animating as it saves that "uh-oh" moment when you realize you've - * spent too much time editing the wrong frame... - */ - // XXX: should this be allowed when framelock is enabled? - if (gpf->framenum != cfra_eval) { - BKE_gpencil_frame_addcopy(gpl, cfra_eval); - } - } - } - - /* save off new current frame, so that next update works fine */ - gso->cfra = cfra_eval; + bGPdata *gpd = gso->gpd; + + bGPDlayer *gpl; + int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); + + /* only try to add a new frame if this is the first stroke, or the frame has changed */ + if ((gpd == NULL) || (cfra_eval == gso->cfra)) + return; + + /* go through each layer, and ensure that we've got a valid frame to use */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + * - This is useful when animating as it saves that "uh-oh" moment when you realize you've + * spent too much time editing the wrong frame... + */ + // XXX: should this be allowed when framelock is enabled? + if (gpf->framenum != cfra_eval) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + } + } + + /* save off new current frame, so that next update works fine */ + gso->cfra = cfra_eval; } /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke( - tGP_BrushEditData *gso, bGPDstroke *gps, - float diff_mat[4][4], GP_BrushApplyCb apply) +static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, + bGPDstroke *gps, + float diff_mat[4][4], + GP_BrushApplyCb apply) { - GP_SpaceConversion *gsc = &gso->gsc; - rcti *rect = &gso->brush_rect; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? gso->gp_brush->size * gso->pressure : gso->gp_brush->size; - - bGPDspoint *pt1, *pt2; - int pc1[2] = {0}; - int pc2[2] = {0}; - int i; - bool include_last = false; - bool changed = false; - - if (gps->totpoints == 1) { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); - - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - int mval_i[2]; - round_v2i_v2fl(mval_i, gso->mval); - if (len_v2v2_int(mval_i, pc1) <= radius) { - /* apply operation to this point */ - changed = apply(gso, gps, 0, radius, pc1); - } - } - } - else { - /* Loop over the points in the stroke, checking for intersections - * - an intersection means that we touched the stroke - */ - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* Get points to work with */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* Skip if neither one is selected (and we are only allowed to edit/consider selected points) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { - include_last = false; - continue; - } - } - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - - gp_point_to_parent_space(pt2, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); - - /* Check that point segment of the boundbox of the selection stroke */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) - { - /* Check if point segment of stroke had anything to do with - * brush region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle(gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { - /* Apply operation to these points */ - bool ok = false; - - /* To each point individually... */ - ok = apply(gso, gps, i, radius, pc1); - - /* Only do the second point if this is the last segment, - * and it is unlikely that the point will get handled - * otherwise. - * - * NOTE: There is a small risk here that the second point wasn't really - * actually in-range. In that case, it only got in because - * the line linking the points was! - */ - if (i + 1 == gps->totpoints - 1) { - ok |= apply(gso, gps, i + 1, radius, pc2); - include_last = false; - } - else { - include_last = true; - } - - changed |= ok; - } - else if (include_last) { - /* This case is for cases where for whatever reason the second vert (1st here) doesn't get included - * because the whole edge isn't in bounds, but it would've qualified since it did with the - * previous step (but wasn't added then, to avoid double-ups) - */ - changed |= apply(gso, gps, i, radius, pc1); - include_last = false; - } - } - } - } - - return changed; + GP_SpaceConversion *gsc = &gso->gsc; + rcti *rect = &gso->brush_rect; + GP_Sculpt_Data *gp_brush = gso->gp_brush; + const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? + gso->gp_brush->size * gso->pressure : + gso->gp_brush->size; + + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + bool include_last = false; + bool changed = false; + + if (gps->totpoints == 1) { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* apply operation to this point */ + changed = apply(gso, gps, 0, radius, pc1); + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Skip if neither one is selected (and we are only allowed to edit/consider selected points) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { + if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { + include_last = false; + continue; + } + } + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the selection stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * brush region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle( + gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + /* Apply operation to these points */ + bool ok = false; + + /* To each point individually... */ + ok = apply(gso, gps, i, radius, pc1); + + /* Only do the second point if this is the last segment, + * and it is unlikely that the point will get handled + * otherwise. + * + * NOTE: There is a small risk here that the second point wasn't really + * actually in-range. In that case, it only got in because + * the line linking the points was! + */ + if (i + 1 == gps->totpoints - 1) { + ok |= apply(gso, gps, i + 1, radius, pc2); + include_last = false; + } + else { + include_last = true; + } + + changed |= ok; + } + else if (include_last) { + /* This case is for cases where for whatever reason the second vert (1st here) doesn't get included + * because the whole edge isn't in bounds, but it would've qualified since it did with the + * previous step (but wasn't added then, to avoid double-ups) + */ + changed |= apply(gso, gps, i, radius, pc1); + include_last = false; + } + } + } + } + + return changed; } /* Apply sculpt brushes to strokes in the given frame */ static bool gpsculpt_brush_do_frame( - bContext *C, tGP_BrushEditData *gso, - bGPDlayer *gpl, bGPDframe *gpf, - float diff_mat[4][4]) + bContext *C, tGP_BrushEditData *gso, bGPDlayer *gpl, bGPDframe *gpf, float diff_mat[4][4]) { - bool changed = false; - Object *ob = CTX_data_active_object(C); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - - switch (gso->brush_type) { - case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); - break; - } - - case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); - break; - } - - case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); - break; - } - - case GP_SCULPT_TYPE_GRAB: /* Grab points */ - { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps); - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); - } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps, diff_mat); - changed |= true; - } - break; - } - - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); - break; - } - - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); - break; - } - - case GP_SCULPT_TYPE_WEIGHT: /* Adjust vertex group weight */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply); - break; - } - - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - } - } - - return changed; + bool changed = false; + Object *ob = CTX_data_active_object(C); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + switch (gso->brush_type) { + case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); + break; + } + + case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); + break; + } + + case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); + break; + } + + case GP_SCULPT_TYPE_GRAB: /* Grab points */ + { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps); + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps, diff_mat); + changed |= true; + } + break; + } + + case GP_SCULPT_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); + break; + } + + case GP_SCULPT_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); + break; + } + + case GP_SCULPT_TYPE_WEIGHT: /* Adjust vertex group weight */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply); + break; + } + + default: + printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); + break; + } + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + } + } + + return changed; } /* Perform two-pass brushes which modify the existing strokes */ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = gso->object; - bGPdata *gpd = gso->gpd; - bool changed = false; - - /* Calculate brush-specific data which applies equally to all points */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: /* Grab points */ - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - /* calculate amount of displacement to apply */ - gp_brush_grab_calc_dvec(gso); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - /* calculate midpoint of the brush (in data space) */ - gp_brush_calc_midpoint(gso); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ - { - /* compute the displacement vector for the cursor (in data space) */ - gp_brush_grab_calc_dvec(gso); - break; - } - - default: - break; - } - - - /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - /* If no active frame, don't do anything... */ - if (gpl->actframe == NULL) { - continue; - } - - /* calculate difference matrix */ - float diff_mat[4][4]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - /* Active Frame or MultiFrame? */ - if (gso->is_multiframe) { - /* init multiframe falloff options */ - int f_init = 0; - int f_end = 0; - - if (gso->use_multiframe_falloff) { - BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); - } - - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* Always do active frame; Otherwise, only include selected frames */ - if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { - /* compute multiframe falloff factor */ - if (gso->use_multiframe_falloff) { - /* Faloff depends on distance to active frame (relative to the overall frame range) */ - gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( - gpf, gpl->actframe->framenum, - f_init, f_end, - ts->gp_sculpt.cur_falloff); - } - else { - /* No falloff */ - gso->mf_falloff = 1.0f; - } - - /* affect strokes in this frame */ - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); - } - } - } - else { - /* Apply to active frame's strokes */ - gso->mf_falloff = 1.0f; - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); - } - } - CTX_DATA_END; - - return changed; + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = gso->object; + bGPdata *gpd = gso->gpd; + bool changed = false; + + /* Calculate brush-specific data which applies equally to all points */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_GRAB: /* Grab points */ + case GP_SCULPT_TYPE_PUSH: /* Push points */ + { + /* calculate amount of displacement to apply */ + gp_brush_grab_calc_dvec(gso); + break; + } + + case GP_SCULPT_TYPE_PINCH: /* Pinch points */ + case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ + { + /* calculate midpoint of the brush (in data space) */ + gp_brush_calc_midpoint(gso); + break; + } + + case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ + { + /* compute the displacement vector for the cursor (in data space) */ + gp_brush_grab_calc_dvec(gso); + break; + } + + default: + break; + } + + /* Find visible strokes, and perform operations on those if hit */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + /* If no active frame, don't do anything... */ + if (gpl->actframe == NULL) { + continue; + } + + /* calculate difference matrix */ + float diff_mat[4][4]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (gso->use_multiframe_falloff) { + BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + } + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + if (gso->use_multiframe_falloff) { + /* Faloff depends on distance to active frame (relative to the overall frame range) */ + gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + else { + /* No falloff */ + gso->mf_falloff = 1.0f; + } + + /* affect strokes in this frame */ + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); + } + } + } + else { + /* Apply to active frame's strokes */ + gso->mf_falloff = 1.0f; + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } + } + CTX_DATA_END; + + return changed; } /* Calculate settings for applying brush */ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) { - tGP_BrushEditData *gso = op->customdata; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = ( - (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? - gso->gp_brush->size * gso->pressure : gso->gp_brush->size); - float mousef[2]; - int mouse[2]; - bool changed = false; - - /* Get latest mouse coordinates */ - RNA_float_get_array(itemptr, "mouse", mousef); - gso->mval[0] = mouse[0] = (int)(mousef[0]); - gso->mval[1] = mouse[1] = (int)(mousef[1]); - - gso->pressure = RNA_float_get(itemptr, "pressure"); - - if (RNA_boolean_get(itemptr, "pen_flip")) - gso->flag |= GP_SCULPT_FLAG_INVERT; - else - gso->flag &= ~GP_SCULPT_FLAG_INVERT; - - - /* Store coordinates as reference, if operator just started running */ - if (gso->first) { - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - } - - /* Update brush_rect, so that it represents the bounding rectangle of brush */ - gso->brush_rect.xmin = mouse[0] - radius; - gso->brush_rect.ymin = mouse[1] - radius; - gso->brush_rect.xmax = mouse[0] + radius; - gso->brush_rect.ymax = mouse[1] + radius; - - - /* Apply brush */ - if (gso->brush_type == GP_SCULPT_TYPE_CLONE) { - changed = gpsculpt_brush_apply_clone(C, gso); - } - else { - changed = gpsculpt_brush_apply_standard(C, gso); - } - - - /* Updates */ - if (changed) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - - /* Store values for next step */ - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - gso->first = false; + tGP_BrushEditData *gso = op->customdata; + GP_Sculpt_Data *gp_brush = gso->gp_brush; + const int radius = ((gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? + gso->gp_brush->size * gso->pressure : + gso->gp_brush->size); + float mousef[2]; + int mouse[2]; + bool changed = false; + + /* Get latest mouse coordinates */ + RNA_float_get_array(itemptr, "mouse", mousef); + gso->mval[0] = mouse[0] = (int)(mousef[0]); + gso->mval[1] = mouse[1] = (int)(mousef[1]); + + gso->pressure = RNA_float_get(itemptr, "pressure"); + + if (RNA_boolean_get(itemptr, "pen_flip")) + gso->flag |= GP_SCULPT_FLAG_INVERT; + else + gso->flag &= ~GP_SCULPT_FLAG_INVERT; + + /* Store coordinates as reference, if operator just started running */ + if (gso->first) { + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + } + + /* Update brush_rect, so that it represents the bounding rectangle of brush */ + gso->brush_rect.xmin = mouse[0] - radius; + gso->brush_rect.ymin = mouse[1] - radius; + gso->brush_rect.xmax = mouse[0] + radius; + gso->brush_rect.ymax = mouse[1] + radius; + + /* Apply brush */ + if (gso->brush_type == GP_SCULPT_TYPE_CLONE) { + changed = gpsculpt_brush_apply_clone(C, gso); + } + else { + changed = gpsculpt_brush_apply_standard(C, gso); + } + + /* Updates */ + if (changed) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Store values for next step */ + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + gso->first = false; } /* Running --------------------------------------------- */ @@ -1806,370 +1766,380 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt /* helper - a record stroke, and apply paint event */ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = op->customdata; - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - PointerRNA itemptr; - float mouse[2]; - int tablet = 0; - - mouse[0] = event->mval[0] + 1; - mouse[1] = event->mval[1] + 1; - - /* fill in stroke */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); - RNA_boolean_set(&itemptr, "is_start", gso->first); - - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - float pressure = wmtab->Pressure; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - - /* special exception here for too high pressure values on first touch in - * windows for some tablets: clamp the values to be sane - */ - if (tablet && (pressure >= 0.99f)) { - pressure = 1.0f; - } - RNA_float_set(&itemptr, "pressure", pressure); - } - else { - RNA_float_set(&itemptr, "pressure", 1.0f); - } - - if (!gso->is_weight_mode) { - if (event->shift) { - gso->gp_brush_old = gso->gp_brush; - gso->brush_type_old = gso->brush_type; - - gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; - gso->brush_type = GP_SCULPT_TYPE_SMOOTH; - } - else { - if (gso->gp_brush_old != NULL) { - gso->gp_brush = gso->gp_brush_old; - gso->brush_type = gso->brush_type_old; - } - } - } - - /* apply */ - gpsculpt_brush_apply(C, op, &itemptr); + tGP_BrushEditData *gso = op->customdata; + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + PointerRNA itemptr; + float mouse[2]; + int tablet = 0; + + mouse[0] = event->mval[0] + 1; + mouse[1] = event->mval[1] + 1; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "is_start", gso->first); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + float pressure = wmtab->Pressure; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + + /* special exception here for too high pressure values on first touch in + * windows for some tablets: clamp the values to be sane + */ + if (tablet && (pressure >= 0.99f)) { + pressure = 1.0f; + } + RNA_float_set(&itemptr, "pressure", pressure); + } + else { + RNA_float_set(&itemptr, "pressure", 1.0f); + } + + if (!gso->is_weight_mode) { + if (event->shift) { + gso->gp_brush_old = gso->gp_brush; + gso->brush_type_old = gso->brush_type; + + gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; + gso->brush_type = GP_SCULPT_TYPE_SMOOTH; + } + else { + if (gso->gp_brush_old != NULL) { + gso->gp_brush = gso->gp_brush_old; + gso->brush_type = gso->brush_type_old; + } + } + } + + /* apply */ + gpsculpt_brush_apply(C, op, &itemptr); } /* reapply */ static int gpsculpt_brush_exec(bContext *C, wmOperator *op) { - if (!gpsculpt_brush_init(C, op)) - return OPERATOR_CANCELLED; + if (!gpsculpt_brush_init(C, op)) + return OPERATOR_CANCELLED; - RNA_BEGIN(op->ptr, itemptr, "stroke") - { - gpsculpt_brush_apply(C, op, &itemptr); - } - RNA_END; + RNA_BEGIN (op->ptr, itemptr, "stroke") { + gpsculpt_brush_apply(C, op, &itemptr); + } + RNA_END; - gpsculpt_brush_exit(C, op); + gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } - /* start modal painting */ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = NULL; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; - bool needs_timer = false; - float brush_rate = 0.0f; - - /* the operator cannot work while play animation */ - if (is_playing) { - BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); - - return OPERATOR_CANCELLED; - } - - /* init painting data */ - if (!gpsculpt_brush_init(C, op)) - return OPERATOR_CANCELLED; - - gso = op->customdata; - - /* initialise type-specific data (used for the entire session) */ - switch (gso->brush_type) { - /* Brushes requiring timer... */ - case GP_SCULPT_TYPE_THICKNESS: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_STRENGTH: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_PINCH: - brush_rate = 0.001f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_TWIST: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - default: - break; - } - - /* register timer for increasing influence by hovering over an area */ - if (needs_timer) { - gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); - } - - /* register modal handler */ - WM_event_add_modal_handler(C, op); - - /* start drawing immediately? */ - if (is_modal == false) { - ARegion *ar = CTX_wm_region(C); - - /* ensure that we'll have a new frame to draw on */ - gpsculpt_brush_init_stroke(gso); - - /* apply first dab... */ - gso->is_painting = true; - gpsculpt_brush_apply_event(C, op, event); - - /* redraw view with feedback */ - ED_region_tag_redraw(ar); - } - - return OPERATOR_RUNNING_MODAL; + tGP_BrushEditData *gso = NULL; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; + bool needs_timer = false; + float brush_rate = 0.0f; + + /* the operator cannot work while play animation */ + if (is_playing) { + BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); + + return OPERATOR_CANCELLED; + } + + /* init painting data */ + if (!gpsculpt_brush_init(C, op)) + return OPERATOR_CANCELLED; + + gso = op->customdata; + + /* initialise type-specific data (used for the entire session) */ + switch (gso->brush_type) { + /* Brushes requiring timer... */ + case GP_SCULPT_TYPE_THICKNESS: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_STRENGTH: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_PINCH: + brush_rate = 0.001f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_TWIST: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + default: + break; + } + + /* register timer for increasing influence by hovering over an area */ + if (needs_timer) { + gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); + } + + /* register modal handler */ + WM_event_add_modal_handler(C, op); + + /* start drawing immediately? */ + if (is_modal == false) { + ARegion *ar = CTX_wm_region(C); + + /* ensure that we'll have a new frame to draw on */ + gpsculpt_brush_init_stroke(gso); + + /* apply first dab... */ + gso->is_painting = true; + gpsculpt_brush_apply_event(C, op, event); + + /* redraw view with feedback */ + ED_region_tag_redraw(ar); + } + + return OPERATOR_RUNNING_MODAL; } /* painting - handle events */ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = op->customdata; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - bool redraw_region = false; - bool redraw_toolsettings = false; - - /* The operator can be in 2 states: Painting and Idling */ - if (gso->is_painting) { - /* Painting */ - switch (event->type) { - /* Mouse Move = Apply somewhere else */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - /* apply brush effect at new position */ - gpsculpt_brush_apply_event(C, op, event); - - /* force redraw, so that the cursor will at least be valid */ - redraw_region = true; - break; - - /* Timer Tick - Only if this was our own timer */ - case TIMER: - if (event->customdata == gso->timer) { - gso->timerTick = true; - gpsculpt_brush_apply_event(C, op, event); - gso->timerTick = false; - } - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Painting mbut release = Stop painting (back to idle) */ - case LEFTMOUSE: - //BLI_assert(event->val == KM_RELEASE); - if (is_modal) { - /* go back to idling... */ - gso->is_painting = false; - } - else { - /* end sculpt session, since we're not modal */ - gso->is_painting = false; - - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - break; - - /* Abort painting if any of the usual things are tried */ - case MIDDLEMOUSE: - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - } - else { - /* Idling */ - BLI_assert(is_modal == true); - - switch (event->type) { - /* Painting mbut press = Start painting (switch to painting state) */ - case LEFTMOUSE: - /* do initial "click" apply */ - gso->is_painting = true; - gso->first = true; - - gpsculpt_brush_init_stroke(gso); - gpsculpt_brush_apply_event(C, op, event); - break; - - /* Exit modal operator, based on the "standard" ops */ - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - - /* MMB is often used for view manipulations */ - case MIDDLEMOUSE: - return OPERATOR_PASS_THROUGH; - - /* Mouse movements should update the brush cursor - Just redraw the active region */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - redraw_region = true; - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Change Frame - Allowed */ - case LEFTARROWKEY: - case RIGHTARROWKEY: - case UPARROWKEY: - case DOWNARROWKEY: - return OPERATOR_PASS_THROUGH; - - /* Camera/View Gizmo's - Allowed */ - /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ - case PAD0: case PAD1: case PAD2: case PAD3: case PAD4: - case PAD5: case PAD6: case PAD7: case PAD8: case PAD9: - return OPERATOR_PASS_THROUGH; - - /* Unhandled event */ - default: - break; - } - } - - /* Redraw region? */ - if (redraw_region) { - ARegion *ar = CTX_wm_region(C); - ED_region_tag_redraw(ar); - } - - /* Redraw toolsettings (brush settings)? */ - if (redraw_toolsettings) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - } - - return OPERATOR_RUNNING_MODAL; + tGP_BrushEditData *gso = op->customdata; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + bool redraw_region = false; + bool redraw_toolsettings = false; + + /* The operator can be in 2 states: Painting and Idling */ + if (gso->is_painting) { + /* Painting */ + switch (event->type) { + /* Mouse Move = Apply somewhere else */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + /* apply brush effect at new position */ + gpsculpt_brush_apply_event(C, op, event); + + /* force redraw, so that the cursor will at least be valid */ + redraw_region = true; + break; + + /* Timer Tick - Only if this was our own timer */ + case TIMER: + if (event->customdata == gso->timer) { + gso->timerTick = true; + gpsculpt_brush_apply_event(C, op, event); + gso->timerTick = false; + } + break; + + /* Adjust brush settings */ + /* FIXME: Step increments and modifier keys are hardcoded here! */ + case WHEELUPMOUSE: + case PADPLUSKEY: + if (event->shift) { + /* increase strength */ + gso->gp_brush->strength += 0.05f; + CLAMP_MAX(gso->gp_brush->strength, 1.0f); + } + else { + /* increase brush size */ + gso->gp_brush->size += 3; + CLAMP_MAX(gso->gp_brush->size, 300); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + case WHEELDOWNMOUSE: + case PADMINUS: + if (event->shift) { + /* decrease strength */ + gso->gp_brush->strength -= 0.05f; + CLAMP_MIN(gso->gp_brush->strength, 0.0f); + } + else { + /* decrease brush size */ + gso->gp_brush->size -= 3; + CLAMP_MIN(gso->gp_brush->size, 1); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + /* Painting mbut release = Stop painting (back to idle) */ + case LEFTMOUSE: + //BLI_assert(event->val == KM_RELEASE); + if (is_modal) { + /* go back to idling... */ + gso->is_painting = false; + } + else { + /* end sculpt session, since we're not modal */ + gso->is_painting = false; + + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + break; + + /* Abort painting if any of the usual things are tried */ + case MIDDLEMOUSE: + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + } + else { + /* Idling */ + BLI_assert(is_modal == true); + + switch (event->type) { + /* Painting mbut press = Start painting (switch to painting state) */ + case LEFTMOUSE: + /* do initial "click" apply */ + gso->is_painting = true; + gso->first = true; + + gpsculpt_brush_init_stroke(gso); + gpsculpt_brush_apply_event(C, op, event); + break; + + /* Exit modal operator, based on the "standard" ops */ + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + + /* MMB is often used for view manipulations */ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Mouse movements should update the brush cursor - Just redraw the active region */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + redraw_region = true; + break; + + /* Adjust brush settings */ + /* FIXME: Step increments and modifier keys are hardcoded here! */ + case WHEELUPMOUSE: + case PADPLUSKEY: + if (event->shift) { + /* increase strength */ + gso->gp_brush->strength += 0.05f; + CLAMP_MAX(gso->gp_brush->strength, 1.0f); + } + else { + /* increase brush size */ + gso->gp_brush->size += 3; + CLAMP_MAX(gso->gp_brush->size, 300); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + case WHEELDOWNMOUSE: + case PADMINUS: + if (event->shift) { + /* decrease strength */ + gso->gp_brush->strength -= 0.05f; + CLAMP_MIN(gso->gp_brush->strength, 0.0f); + } + else { + /* decrease brush size */ + gso->gp_brush->size -= 3; + CLAMP_MIN(gso->gp_brush->size, 1); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + /* Change Frame - Allowed */ + case LEFTARROWKEY: + case RIGHTARROWKEY: + case UPARROWKEY: + case DOWNARROWKEY: + return OPERATOR_PASS_THROUGH; + + /* Camera/View Gizmo's - Allowed */ + /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + return OPERATOR_PASS_THROUGH; + + /* Unhandled event */ + default: + break; + } + } + + /* Redraw region? */ + if (redraw_region) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + + /* Redraw toolsettings (brush settings)? */ + if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + + return OPERATOR_RUNNING_MODAL; } void GPENCIL_OT_sculpt_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Stroke Sculpt"; - ot->idname = "GPENCIL_OT_sculpt_paint"; - ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX - - /* api callbacks */ - ot->exec = gpsculpt_brush_exec; - ot->invoke = gpsculpt_brush_invoke; - ot->modal = gpsculpt_brush_modal; - ot->cancel = gpsculpt_brush_exit; - ot->poll = gpsculpt_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", - "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* identifiers */ + ot->name = "Stroke Sculpt"; + ot->idname = "GPENCIL_OT_sculpt_paint"; + ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX + + /* api callbacks */ + ot->exec = gpsculpt_brush_exec; + ot->invoke = gpsculpt_brush_invoke; + ot->modal = gpsculpt_brush_modal; + ot->cancel = gpsculpt_brush_exit; + ot->poll = gpsculpt_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, + "wait_for_input", + true, + "Wait for Input", + "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index af2c68d6c68..c1c797b15be 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -88,50 +87,62 @@ /* defines for possible modes */ enum { - GP_STROKECONVERT_PATH = 1, - GP_STROKECONVERT_CURVE, - GP_STROKECONVERT_POLY, + GP_STROKECONVERT_PATH = 1, + GP_STROKECONVERT_CURVE, + GP_STROKECONVERT_POLY, }; /* Defines for possible timing modes */ enum { - GP_STROKECONVERT_TIMING_NONE = 1, - GP_STROKECONVERT_TIMING_LINEAR = 2, - GP_STROKECONVERT_TIMING_FULL = 3, - GP_STROKECONVERT_TIMING_CUSTOMGAP = 4, + GP_STROKECONVERT_TIMING_NONE = 1, + GP_STROKECONVERT_TIMING_LINEAR = 2, + GP_STROKECONVERT_TIMING_FULL = 3, + GP_STROKECONVERT_TIMING_CUSTOMGAP = 4, }; /* RNA enum define */ static const EnumPropertyItem prop_gpencil_convertmodes[] = { - {GP_STROKECONVERT_PATH, "PATH", ICON_CURVE_PATH, "Path", "Animation path"}, - {GP_STROKECONVERT_CURVE, "CURVE", ICON_CURVE_BEZCURVE, "Bezier Curve", "Smooth Bezier curve"}, - {GP_STROKECONVERT_POLY, "POLY", ICON_MESH_DATA, "Polygon Curve", "Bezier curve with straight-line segments (vector handles)"}, - {0, NULL, 0, NULL, NULL}, + {GP_STROKECONVERT_PATH, "PATH", ICON_CURVE_PATH, "Path", "Animation path"}, + {GP_STROKECONVERT_CURVE, "CURVE", ICON_CURVE_BEZCURVE, "Bezier Curve", "Smooth Bezier curve"}, + {GP_STROKECONVERT_POLY, + "POLY", + ICON_MESH_DATA, + "Polygon Curve", + "Bezier curve with straight-line segments (vector handles)"}, + {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = { - {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, - {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, - {0, NULL, 0, NULL, NULL}, + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, + {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem prop_gpencil_convert_timingmodes[] = { - {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, - {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, - {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original timing, gaps included"}, - {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps", - "Use the original timing, but with custom gap lengths (in frames)"}, - {0, NULL, 0, NULL, NULL}, + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, + {GP_STROKECONVERT_TIMING_FULL, + "FULL", + 0, + "Original", + "Use the original timing, gaps included"}, + {GP_STROKECONVERT_TIMING_CUSTOMGAP, + "CUSTOMGAP", + 0, + "Custom Gaps", + "Use the original timing, but with custom gap lengths (in frames)"}, + {0, NULL, 0, NULL, NULL}, }; -static const EnumPropertyItem *rna_GPConvert_mode_items( - bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) +static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), + PointerRNA *ptr, + PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) { - if (RNA_boolean_get(ptr, "use_timing_data")) { - return prop_gpencil_convert_timingmodes; - } - return prop_gpencil_convert_timingmodes_restricted; + if (RNA_boolean_get(ptr, "use_timing_data")) { + return prop_gpencil_convert_timingmodes; + } + return prop_gpencil_convert_timingmodes_restricted; } /* --- */ @@ -139,81 +150,84 @@ static const EnumPropertyItem *rna_GPConvert_mode_items( /* convert the coordinates from the given stroke point into 3d-coordinates * - assumes that the active space is the 3D-View */ -static void gp_strokepoint_convertcoords( - bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, - float p3d[3], const rctf *subrect) +static void gp_strokepoint_convertcoords(bContext *C, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDstroke *gps, + bGPDspoint *source_pt, + float p3d[3], + const rctf *subrect) { - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = CTX_data_active_object(C); - bGPDspoint mypt, *pt; - - float diff_mat[4][4]; - pt = &mypt; - - /* apply parent transform */ - float fpt[3]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - mul_v3_m4v3(fpt, diff_mat, &source_pt->x); - copy_v3_v3(&pt->x, fpt); - - - if (gps->flag & GP_STROKE_3DSPACE) { - /* directly use 3d-coordinates */ - copy_v3_v3(p3d, &pt->x); - } - else { - const float *fp = scene->cursor.location; - float mvalf[2]; - - /* get screen coordinate */ - if (gps->flag & GP_STROKE_2DSPACE) { - View2D *v2d = &ar->v2d; - UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]); - } - else { - if (subrect) { - mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; - mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; - } - else { - mvalf[0] = (float)pt->x / 100.0f * ar->winx; - mvalf[1] = (float)pt->y / 100.0f * ar->winy; - } - } - - ED_view3d_win_to_3d(v3d, ar, fp, mvalf, p3d); - } + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + bGPDspoint mypt, *pt; + + float diff_mat[4][4]; + pt = &mypt; + + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); + + if (gps->flag & GP_STROKE_3DSPACE) { + /* directly use 3d-coordinates */ + copy_v3_v3(p3d, &pt->x); + } + else { + const float *fp = scene->cursor.location; + float mvalf[2]; + + /* get screen coordinate */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d = &ar->v2d; + UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]); + } + else { + if (subrect) { + mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + else { + mvalf[0] = (float)pt->x / 100.0f * ar->winx; + mvalf[1] = (float)pt->y / 100.0f * ar->winy; + } + } + + ED_view3d_win_to_3d(v3d, ar, fp, mvalf, p3d); + } } /* --- */ /* temp struct for gp_stroke_path_animation() */ typedef struct tGpTimingData { - /* Data set from operator settings */ - int mode; - int frame_range; /* Number of frames evaluated for path animation */ - int start_frame, end_frame; - bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */ - float gap_duration, gap_randomness; /* To be used with CustomGap mode*/ - int seed; - - /* Data set from points, used to compute final timing FCurve */ - int num_points, cur_point; - - /* Distances */ - float *dists; - float tot_dist; - - /* Times */ - float *times; /* Note: Gap times will be negative! */ - float tot_time, gap_tot_time; - double inittime; - - /* Only used during creation of dists & times lists. */ - float offset_time; + /* Data set from operator settings */ + int mode; + int frame_range; /* Number of frames evaluated for path animation */ + int start_frame, end_frame; + bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */ + float gap_duration, gap_randomness; /* To be used with CustomGap mode*/ + int seed; + + /* Data set from points, used to compute final timing FCurve */ + int num_points, cur_point; + + /* Distances */ + float *dists; + float tot_dist; + + /* Times */ + float *times; /* Note: Gap times will be negative! */ + float tot_time, gap_tot_time; + double inittime; + + /* Only used during creation of dists & times lists. */ + float offset_time; } tGpTimingData; /* Init point buffers for timing data. @@ -221,59 +235,61 @@ typedef struct tGpTimingData { */ static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr) { - float *tmp; - - BLI_assert(nbr > gtd->num_points); - - /* distances */ - tmp = gtd->dists; - gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); - if (tmp) { - memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); - MEM_freeN(tmp); - } - - /* times */ - tmp = gtd->times; - gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); - if (tmp) { - memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); - MEM_freeN(tmp); - } - - gtd->num_points = nbr; + float *tmp; + + BLI_assert(nbr > gtd->num_points); + + /* distances */ + tmp = gtd->dists; + gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); + if (tmp) { + memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); + MEM_freeN(tmp); + } + + /* times */ + tmp = gtd->times; + gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); + if (tmp) { + memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); + MEM_freeN(tmp); + } + + gtd->num_points = nbr; } /* add stroke point to timing buffers */ -static void gp_timing_data_add_point( - tGpTimingData *gtd, const double stroke_inittime, const float time, - const float delta_dist) +static void gp_timing_data_add_point(tGpTimingData *gtd, + const double stroke_inittime, + const float time, + const float delta_dist) { - float delta_time = 0.0f; - const int cur_point = gtd->cur_point; - - if (!cur_point) { - /* Special case, first point, if time is not 0.0f we have to compensate! */ - gtd->offset_time = -time; - gtd->times[cur_point] = 0.0f; - } - else if (time < 0.0f) { - /* This is a gap, negative value! */ - gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); - delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1]; - - gtd->gap_tot_time += delta_time; - } - else { - gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); - delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]); - } - - gtd->tot_time += delta_time; - gtd->tot_dist += delta_dist; - gtd->dists[cur_point] = gtd->tot_dist; - - gtd->cur_point++; + float delta_time = 0.0f; + const int cur_point = gtd->cur_point; + + if (!cur_point) { + /* Special case, first point, if time is not 0.0f we have to compensate! */ + gtd->offset_time = -time; + gtd->times[cur_point] = 0.0f; + } + else if (time < 0.0f) { + /* This is a gap, negative value! */ + gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + + gtd->offset_time); + delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1]; + + gtd->gap_tot_time += delta_time; + } + else { + gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); + delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]); + } + + gtd->tot_time += delta_time; + gtd->tot_dist += delta_dist; + gtd->dists[cur_point] = gtd->tot_dist; + + gtd->cur_point++; } /* In frames! Binary search for FCurve keys have a threshold of 0.01, so we can't set @@ -282,271 +298,301 @@ static void gp_timing_data_add_point( #define MIN_TIME_DELTA 0.02f /* Loop over next points to find the end of the stroke, and compute */ -static int gp_find_end_of_stroke_idx( - tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps, - int *nbr_done_gaps, const float tot_gaps_time, const float delta_time, - float *next_delta_time) +static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, + RNG *rng, + const int idx, + const int nbr_gaps, + int *nbr_done_gaps, + const float tot_gaps_time, + const float delta_time, + float *next_delta_time) { - int j; - - for (j = idx + 1; j < gtd->num_points; j++) { - if (gtd->times[j] < 0) { - gtd->times[j] = -gtd->times[j]; - if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - /* In this mode, gap time between this stroke and the next should be 0 currently... - * So we have to compute its final duration! - */ - if (gtd->gap_randomness > 0.0f) { - /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, - * and which sum to exactly tot_gaps_time... - */ - int rem_gaps = nbr_gaps - (*nbr_done_gaps); - if (rem_gaps < 2) { - /* Last gap, just give remaining time! */ - *next_delta_time = tot_gaps_time; - } - else { - float delta, min, max; - - /* This code ensures that if the first gaps have been shorter than average gap_duration, - * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa! - */ - delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); - - /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ - min = -gtd->gap_randomness - delta; - CLAMP(min, -gtd->gap_randomness, 0.0f); - - /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */ - max = gtd->gap_randomness - delta; - CLAMP(max, 0.0f, gtd->gap_randomness); - *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min; - } - } - else { - *next_delta_time += gtd->gap_duration; - } - } - (*nbr_done_gaps)++; - break; - } - } - - return j - 1; + int j; + + for (j = idx + 1; j < gtd->num_points; j++) { + if (gtd->times[j] < 0) { + gtd->times[j] = -gtd->times[j]; + if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + /* In this mode, gap time between this stroke and the next should be 0 currently... + * So we have to compute its final duration! + */ + if (gtd->gap_randomness > 0.0f) { + /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, + * and which sum to exactly tot_gaps_time... + */ + int rem_gaps = nbr_gaps - (*nbr_done_gaps); + if (rem_gaps < 2) { + /* Last gap, just give remaining time! */ + *next_delta_time = tot_gaps_time; + } + else { + float delta, min, max; + + /* This code ensures that if the first gaps have been shorter than average gap_duration, + * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa! + */ + delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); + + /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ + min = -gtd->gap_randomness - delta; + CLAMP(min, -gtd->gap_randomness, 0.0f); + + /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */ + max = gtd->gap_randomness - delta; + CLAMP(max, 0.0f, gtd->gap_randomness); + *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min; + } + } + else { + *next_delta_time += gtd->gap_duration; + } + } + (*nbr_done_gaps)++; + break; + } + } + + return j - 1; } -static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, int *nbr_gaps, float *tot_gaps_time) +static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, + RNG *rng, + int *nbr_gaps, + float *tot_gaps_time) { - int i; - float delta_time = 0.0f; - - for (i = 0; i < gtd->num_points; i++) { - if (gtd->times[i] < 0 && i) { - (*nbr_gaps)++; - gtd->times[i] = -gtd->times[i] - delta_time; - delta_time += gtd->times[i] - gtd->times[i - 1]; - gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */ - } - else { - gtd->times[i] -= delta_time; - } - } - gtd->tot_time -= delta_time; - - *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; - gtd->tot_time += *tot_gaps_time; - if (G.debug & G_DEBUG) { - printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps); - } - if (gtd->gap_randomness > 0.0f) { - BLI_rng_srandom(rng, gtd->seed); - } + int i; + float delta_time = 0.0f; + + for (i = 0; i < gtd->num_points; i++) { + if (gtd->times[i] < 0 && i) { + (*nbr_gaps)++; + gtd->times[i] = -gtd->times[i] - delta_time; + delta_time += gtd->times[i] - gtd->times[i - 1]; + gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */ + } + else { + gtd->times[i] -= delta_time; + } + } + gtd->tot_time -= delta_time; + + *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; + gtd->tot_time += *tot_gaps_time; + if (G.debug & G_DEBUG) { + printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps); + } + if (gtd->gap_randomness > 0.0f) { + BLI_rng_srandom(rng, gtd->seed); + } } -static void gp_stroke_path_animation_add_keyframes( - Depsgraph *depsgraph, ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, - Curve *cu, tGpTimingData *gtd, RNG *rng, const float time_range, - const int nbr_gaps, const float tot_gaps_time) +static void gp_stroke_path_animation_add_keyframes(Depsgraph *depsgraph, + ReportList *reports, + PointerRNA ptr, + PropertyRNA *prop, + FCurve *fcu, + Curve *cu, + tGpTimingData *gtd, + RNG *rng, + const float time_range, + const int nbr_gaps, + const float tot_gaps_time) { - /* Use actual recorded timing! */ - const float time_start = (float)gtd->start_frame; - - float last_valid_time = 0.0f; - int end_stroke_idx = -1, start_stroke_idx = 0; - float end_stroke_time = 0.0f; - - /* CustomGaps specific */ - float delta_time = 0.0f, next_delta_time = 0.0f; - int nbr_done_gaps = 0; - - int i; - float cfra; - - /* This is a bit tricky, as: - * - We can't add arbitrarily close points on FCurve (in time). - * - We *must* have all "caps" points of all strokes in FCurve, as much as possible! - */ - for (i = 0; i < gtd->num_points; i++) { - /* If new stroke... */ - if (i > end_stroke_idx) { - start_stroke_idx = i; - delta_time = next_delta_time; - /* find end of that new stroke */ - end_stroke_idx = gp_find_end_of_stroke_idx( - gtd, rng, i, nbr_gaps, &nbr_done_gaps, - tot_gaps_time, delta_time, &next_delta_time); - /* This one should *never* be negative! */ - end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); - } - - /* Simple proportional stuff... */ - cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen; - cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range); - - /* And now, the checks about timing... */ - if (i == start_stroke_idx) { - /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and - * that the end point of the stroke is far enough! - * In case it is not, we keep the end point... - * Note that with CustomGaps mode, this is here we set the actual gap timing! - */ - if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) { - if ((cfra - last_valid_time) < MIN_TIME_DELTA) { - cfra = last_valid_time + MIN_TIME_DELTA; - } - insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); - last_valid_time = cfra; - } - else if (G.debug & G_DEBUG) { - printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); - } - } - else if (i == end_stroke_idx) { - /* Always try to insert end point of a curve (should be safe enough, anyway...) */ - if ((cfra - last_valid_time) < MIN_TIME_DELTA) { - cfra = last_valid_time + MIN_TIME_DELTA; - } - insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); - last_valid_time = cfra; - } - else { - /* Else ("middle" point), we only insert it if it's far enough from last keyframe, - * and also far enough from (not yet added!) end_stroke keyframe! - */ - if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) { - insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_BREAKDOWN, NULL, INSERTKEY_FAST); - last_valid_time = cfra; - } - else if (G.debug & G_DEBUG) { - printf("\t Skipping \"middle\" point %d, too close from last added point or end point %d\n", - i, end_stroke_idx); - } - } - } + /* Use actual recorded timing! */ + const float time_start = (float)gtd->start_frame; + + float last_valid_time = 0.0f; + int end_stroke_idx = -1, start_stroke_idx = 0; + float end_stroke_time = 0.0f; + + /* CustomGaps specific */ + float delta_time = 0.0f, next_delta_time = 0.0f; + int nbr_done_gaps = 0; + + int i; + float cfra; + + /* This is a bit tricky, as: + * - We can't add arbitrarily close points on FCurve (in time). + * - We *must* have all "caps" points of all strokes in FCurve, as much as possible! + */ + for (i = 0; i < gtd->num_points; i++) { + /* If new stroke... */ + if (i > end_stroke_idx) { + start_stroke_idx = i; + delta_time = next_delta_time; + /* find end of that new stroke */ + end_stroke_idx = gp_find_end_of_stroke_idx( + gtd, rng, i, nbr_gaps, &nbr_done_gaps, tot_gaps_time, delta_time, &next_delta_time); + /* This one should *never* be negative! */ + end_stroke_time = time_start + + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); + } + + /* Simple proportional stuff... */ + cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen; + cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range); + + /* And now, the checks about timing... */ + if (i == start_stroke_idx) { + /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and + * that the end point of the stroke is far enough! + * In case it is not, we keep the end point... + * Note that with CustomGaps mode, this is here we set the actual gap timing! + */ + if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) { + if ((cfra - last_valid_time) < MIN_TIME_DELTA) { + cfra = last_valid_time + MIN_TIME_DELTA; + } + insert_keyframe_direct( + depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); + last_valid_time = cfra; + } + else if (G.debug & G_DEBUG) { + printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); + } + } + else if (i == end_stroke_idx) { + /* Always try to insert end point of a curve (should be safe enough, anyway...) */ + if ((cfra - last_valid_time) < MIN_TIME_DELTA) { + cfra = last_valid_time + MIN_TIME_DELTA; + } + insert_keyframe_direct( + depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); + last_valid_time = cfra; + } + else { + /* Else ("middle" point), we only insert it if it's far enough from last keyframe, + * and also far enough from (not yet added!) end_stroke keyframe! + */ + if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) { + insert_keyframe_direct(depsgraph, + reports, + ptr, + prop, + fcu, + cfra, + BEZT_KEYTYPE_BREAKDOWN, + NULL, + INSERTKEY_FAST); + last_valid_time = cfra; + } + else if (G.debug & G_DEBUG) { + printf( + "\t Skipping \"middle\" point %d, too close from last added point or end point %d\n", + i, + end_stroke_idx); + } + } + } } -static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd) +static void gp_stroke_path_animation(bContext *C, + ReportList *reports, + Curve *cu, + tGpTimingData *gtd) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - bAction *act; - FCurve *fcu; - PointerRNA ptr; - PropertyRNA *prop = NULL; - int nbr_gaps = 0, i; - - if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) - return; - - /* gap_duration and gap_randomness are in frames, but we need seconds!!! */ - gtd->gap_duration = FRA2TIME(gtd->gap_duration); - gtd->gap_randomness = FRA2TIME(gtd->gap_randomness); - - /* Enable path! */ - cu->flag |= CU_PATH; - cu->pathlen = gtd->frame_range; - - /* Get RNA pointer to read/write path time values */ - RNA_id_pointer_create((ID *)cu, &ptr); - prop = RNA_struct_find_property(&ptr, "eval_time"); - - /* Ensure we have an F-Curve to add keyframes to */ - act = verify_adt_action(bmain, (ID *)cu, true); - fcu = verify_fcurve(bmain, act, NULL, &ptr, "eval_time", 0, true); - - if (G.debug & G_DEBUG) { - printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - } - - if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { - float cfra; - - /* Linear extrapolation! */ - fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; - - cu->ctime = 0.0f; - cfra = (float)gtd->start_frame; - insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); - - cu->ctime = cu->pathlen; - if (gtd->realtime) { - cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ - } - else { - cfra = (float)gtd->end_frame; - } - insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); - } - else { - /* Use actual recorded timing! */ - RNG *rng = BLI_rng_new(0); - float time_range; - - /* CustomGaps specific */ - float tot_gaps_time = 0.0f; - - /* Pre-process gaps, in case we don't want to keep their original timing */ - if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); - } - - if (gtd->realtime) { - time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ - } - else { - time_range = (float)(gtd->end_frame - gtd->start_frame); - } - - if (G.debug & G_DEBUG) { - printf("GP Stroke Path Conversion: Starting keying!\n"); - } - - gp_stroke_path_animation_add_keyframes( - depsgraph, reports, ptr, prop, fcu, cu, gtd, rng, time_range, - nbr_gaps, tot_gaps_time); - - BLI_rng_free(rng); - } - - /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ - calchandles_fcurve(fcu); - - if (G.debug & G_DEBUG) { - printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - printf("\n\n"); - } - - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - - /* send updates */ - DEG_id_tag_update(&cu->id, 0); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bAction *act; + FCurve *fcu; + PointerRNA ptr; + PropertyRNA *prop = NULL; + int nbr_gaps = 0, i; + + if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) + return; + + /* gap_duration and gap_randomness are in frames, but we need seconds!!! */ + gtd->gap_duration = FRA2TIME(gtd->gap_duration); + gtd->gap_randomness = FRA2TIME(gtd->gap_randomness); + + /* Enable path! */ + cu->flag |= CU_PATH; + cu->pathlen = gtd->frame_range; + + /* Get RNA pointer to read/write path time values */ + RNA_id_pointer_create((ID *)cu, &ptr); + prop = RNA_struct_find_property(&ptr, "eval_time"); + + /* Ensure we have an F-Curve to add keyframes to */ + act = verify_adt_action(bmain, (ID *)cu, true); + fcu = verify_fcurve(bmain, act, NULL, &ptr, "eval_time", 0, true); + + if (G.debug & G_DEBUG) { + printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); + for (i = 0; i < gtd->num_points; i++) { + printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); + } + } + + if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { + float cfra; + + /* Linear extrapolation! */ + fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; + + cu->ctime = 0.0f; + cfra = (float)gtd->start_frame; + insert_keyframe_direct( + depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); + + cu->ctime = cu->pathlen; + if (gtd->realtime) { + cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ + } + else { + cfra = (float)gtd->end_frame; + } + insert_keyframe_direct( + depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST); + } + else { + /* Use actual recorded timing! */ + RNG *rng = BLI_rng_new(0); + float time_range; + + /* CustomGaps specific */ + float tot_gaps_time = 0.0f; + + /* Pre-process gaps, in case we don't want to keep their original timing */ + if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); + } + + if (gtd->realtime) { + time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ + } + else { + time_range = (float)(gtd->end_frame - gtd->start_frame); + } + + if (G.debug & G_DEBUG) { + printf("GP Stroke Path Conversion: Starting keying!\n"); + } + + gp_stroke_path_animation_add_keyframes( + depsgraph, reports, ptr, prop, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time); + + BLI_rng_free(rng); + } + + /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ + calchandles_fcurve(fcu); + + if (G.debug & G_DEBUG) { + printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); + for (i = 0; i < gtd->num_points; i++) { + printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); + } + printf("\n\n"); + } + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + /* send updates */ + DEG_id_tag_update(&cu->id, 0); } #undef MIN_TIME_DELTA @@ -558,486 +604,559 @@ static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu /* convert stroke to 3d path */ /* helper */ -static void gp_stroke_to_path_add_point( - tGpTimingData *gtd, BPoint *bp, const float p[3], const float prev_p[3], - const bool do_gtd, const double inittime, const float time, - const float width, const float rad_fac, float minmax_weights[2]) +static void gp_stroke_to_path_add_point(tGpTimingData *gtd, + BPoint *bp, + const float p[3], + const float prev_p[3], + const bool do_gtd, + const double inittime, + const float time, + const float width, + const float rad_fac, + float minmax_weights[2]) { - copy_v3_v3(bp->vec, p); - bp->vec[3] = 1.0f; - - /* set settings */ - bp->f1 = SELECT; - bp->radius = width * rad_fac; - bp->weight = width; - CLAMP(bp->weight, 0.0f, 1.0f); - if (bp->weight < minmax_weights[0]) { - minmax_weights[0] = bp->weight; - } - else if (bp->weight > minmax_weights[1]) { - minmax_weights[1] = bp->weight; - } - - /* Update timing data */ - if (do_gtd) { - gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); - } + copy_v3_v3(bp->vec, p); + bp->vec[3] = 1.0f; + + /* set settings */ + bp->f1 = SELECT; + bp->radius = width * rad_fac; + bp->weight = width; + CLAMP(bp->weight, 0.0f, 1.0f); + if (bp->weight < minmax_weights[0]) { + minmax_weights[0] = bp->weight; + } + else if (bp->weight > minmax_weights[1]) { + minmax_weights[1] = bp->weight; + } + + /* Update timing data */ + if (do_gtd) { + gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); + } } -static void gp_stroke_to_path( - bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, - float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, - const bool add_end_point, tGpTimingData *gtd) +static void gp_stroke_to_path(bContext *C, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDstroke *gps, + Curve *cu, + rctf *subrect, + Nurb **curnu, + float minmax_weights[2], + const float rad_fac, + bool stitch, + const bool add_start_point, + const bool add_end_point, + tGpTimingData *gtd) { - bGPDspoint *pt; - Nurb *nu = (curnu) ? *curnu : NULL; - BPoint *bp, *prev_bp = NULL; - const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); - const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); - int i, old_nbp = 0; - - /* create new 'nurb' or extend current one within the curve */ - if (nu) { - old_nbp = nu->pntsu; - - /* If stitch, the first point of this stroke is already present in current nu. - * Else, we have to add two additional points to make the zero-radius link between strokes. - */ - BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points); - } - else { - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); - - nu->pntsu = gps->totpoints + add_start_end_points; - nu->pntsv = 1; - nu->orderu = 2; /* point-to-point! */ - nu->type = CU_NURBS; - nu->flagu = CU_NURB_ENDPOINT; - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - nu->knotsu = NULL; - - nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints"); - - stitch = false; /* Security! */ - } - - if (do_gtd) { - gp_timing_data_set_nbr(gtd, nu->pntsu); - } - - /* If needed, make the link between both strokes with two zero-radius additional points */ - /* About "zero-radius" point interpolations: - * - If we have at least two points in current curve (most common case), we linearly extrapolate - * the last segment to get the first point (p1) position and timing. - * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point - * with the first point of the current stroke. - * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated - * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... - */ - if (curnu && !stitch && old_nbp) { - float p1[3], p2[3], p[3], next_p[3]; - float dt1 = 0.0f, dt2 = 0.0f; - - BLI_assert(gps->prev != NULL); - - prev_bp = NULL; - if ((old_nbp > 1) && (gps->prev->totpoints > 1)) { - /* Only use last curve segment if previous stroke was not a single-point one! */ - prev_bp = &nu->bp[old_nbp - 2]; - } - bp = &nu->bp[old_nbp - 1]; - - /* First point */ - gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); - if (prev_bp) { - interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); - if (do_gtd) { - const int idx = gps->prev->totpoints - 1; - dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC); - if (do_gtd) { - dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); - } - } - bp++; - gp_stroke_to_path_add_point( - gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1, - 0.0f, rad_fac, minmax_weights); - - /* Second point */ - /* Note dt2 is always negative, which marks the gap. */ - if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); - interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); - } - } - bp++; - gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); - - old_nbp += 2; - } - else if (add_start_point) { - float p[3], next_p[3]; - float dt = 0.0f; - - gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); - if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); - interp_v3_v3v3(p, p, next_p, -GAP_DFAC); - if (do_gtd) { - dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - p[0] -= GAP_DFAC; /* Rather arbitrary... */ - dt = -GAP_DFAC; /* Rather arbitrary too! */ - } - bp = &nu->bp[old_nbp]; - /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value) - * would not work (it would be *before* gtd->inittime, which is not supported currently). - */ - gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); - - old_nbp++; - } - - if (old_nbp) { - prev_bp = &nu->bp[old_nbp - 1]; - } - - /* add points */ - for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp]; - i < gps->totpoints; - i++, pt++, bp++) - { - float p[3]; - float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; - - /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, p, subrect); - - gp_stroke_to_path_add_point( - gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, - width, rad_fac, minmax_weights); - - prev_bp = bp; - } - - if (add_end_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC); - if (do_gtd) { - const int idx = gps->totpoints - 1; - dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, prev_bp->vec); - p[0] += GAP_DFAC; /* Rather arbitrary... */ - dt = GAP_DFAC; /* Rather arbitrary too! */ - } - /* Note bp has already been incremented in main loop above, so it points to the right place. */ - gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); - } - - /* add nurb to curve */ - if (!curnu || !*curnu) { - BLI_addtail(&cu->nurb, nu); - } - if (curnu) { - *curnu = nu; - } - - BKE_nurb_knot_calc_u(nu); + bGPDspoint *pt; + Nurb *nu = (curnu) ? *curnu : NULL; + BPoint *bp, *prev_bp = NULL; + const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); + const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); + int i, old_nbp = 0; + + /* create new 'nurb' or extend current one within the curve */ + if (nu) { + old_nbp = nu->pntsu; + + /* If stitch, the first point of this stroke is already present in current nu. + * Else, we have to add two additional points to make the zero-radius link between strokes. + */ + BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points); + } + else { + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); + + nu->pntsu = gps->totpoints + add_start_end_points; + nu->pntsv = 1; + nu->orderu = 2; /* point-to-point! */ + nu->type = CU_NURBS; + nu->flagu = CU_NURB_ENDPOINT; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + nu->knotsu = NULL; + + nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints"); + + stitch = false; /* Security! */ + } + + if (do_gtd) { + gp_timing_data_set_nbr(gtd, nu->pntsu); + } + + /* If needed, make the link between both strokes with two zero-radius additional points */ + /* About "zero-radius" point interpolations: + * - If we have at least two points in current curve (most common case), we linearly extrapolate + * the last segment to get the first point (p1) position and timing. + * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point + * with the first point of the current stroke. + * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated + * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... + */ + if (curnu && !stitch && old_nbp) { + float p1[3], p2[3], p[3], next_p[3]; + float dt1 = 0.0f, dt2 = 0.0f; + + BLI_assert(gps->prev != NULL); + + prev_bp = NULL; + if ((old_nbp > 1) && (gps->prev->totpoints > 1)) { + /* Only use last curve segment if previous stroke was not a single-point one! */ + prev_bp = &nu->bp[old_nbp - 2]; + } + bp = &nu->bp[old_nbp - 1]; + + /* First point */ + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); + if (prev_bp) { + interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); + if (do_gtd) { + const int idx = gps->prev->totpoints - 1; + dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC); + if (do_gtd) { + dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); + } + } + bp++; + gp_stroke_to_path_add_point(gtd, + bp, + p1, + (bp - 1)->vec, + do_gtd, + gps->prev->inittime, + dt1, + 0.0f, + rad_fac, + minmax_weights); + + /* Second point */ + /* Note dt2 is always negative, which marks the gap. */ + if (gps->totpoints > 1) { + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); + interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); + } + } + bp++; + gp_stroke_to_path_add_point( + gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); + + old_nbp += 2; + } + else if (add_start_point) { + float p[3], next_p[3]; + float dt = 0.0f; + + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); + if (gps->totpoints > 1) { + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); + interp_v3_v3v3(p, p, next_p, -GAP_DFAC); + if (do_gtd) { + dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + p[0] -= GAP_DFAC; /* Rather arbitrary... */ + dt = -GAP_DFAC; /* Rather arbitrary too! */ + } + bp = &nu->bp[old_nbp]; + /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value) + * would not work (it would be *before* gtd->inittime, which is not supported currently). + */ + gp_stroke_to_path_add_point( + gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); + + old_nbp++; + } + + if (old_nbp) { + prev_bp = &nu->bp[old_nbp - 1]; + } + + /* add points */ + for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp]; + i < gps->totpoints; + i++, pt++, bp++) { + float p[3]; + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; + + /* get coordinates to add at */ + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, p, subrect); + + gp_stroke_to_path_add_point(gtd, + bp, + p, + (prev_bp) ? prev_bp->vec : p, + do_gtd, + gps->inittime, + pt->time, + width, + rad_fac, + minmax_weights); + + prev_bp = bp; + } + + if (add_end_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC); + if (do_gtd) { + const int idx = gps->totpoints - 1; + dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, prev_bp->vec); + p[0] += GAP_DFAC; /* Rather arbitrary... */ + dt = GAP_DFAC; /* Rather arbitrary too! */ + } + /* Note bp has already been incremented in main loop above, so it points to the right place. */ + gp_stroke_to_path_add_point( + gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); + } + + /* add nurb to curve */ + if (!curnu || !*curnu) { + BLI_addtail(&cu->nurb, nu); + } + if (curnu) { + *curnu = nu; + } + + BKE_nurb_knot_calc_u(nu); } /* convert stroke to 3d bezier */ /* helper */ -static void gp_stroke_to_bezier_add_point( - tGpTimingData *gtd, BezTriple *bezt, - const float p[3], const float h1[3], const float h2[3], const float prev_p[3], - const bool do_gtd, const double inittime, const float time, - const float width, const float rad_fac, float minmax_weights[2]) +static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, + BezTriple *bezt, + const float p[3], + const float h1[3], + const float h2[3], + const float prev_p[3], + const bool do_gtd, + const double inittime, + const float time, + const float width, + const float rad_fac, + float minmax_weights[2]) { - copy_v3_v3(bezt->vec[0], h1); - copy_v3_v3(bezt->vec[1], p); - copy_v3_v3(bezt->vec[2], h2); - - /* set settings */ - bezt->h1 = bezt->h2 = HD_FREE; - bezt->f1 = bezt->f2 = bezt->f3 = SELECT; - bezt->radius = width * rad_fac; - bezt->weight = width; - CLAMP(bezt->weight, 0.0f, 1.0f); - if (bezt->weight < minmax_weights[0]) { - minmax_weights[0] = bezt->weight; - } - else if (bezt->weight > minmax_weights[1]) { - minmax_weights[1] = bezt->weight; - } - - /* Update timing data */ - if (do_gtd) { - gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); - } + copy_v3_v3(bezt->vec[0], h1); + copy_v3_v3(bezt->vec[1], p); + copy_v3_v3(bezt->vec[2], h2); + + /* set settings */ + bezt->h1 = bezt->h2 = HD_FREE; + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + bezt->radius = width * rad_fac; + bezt->weight = width; + CLAMP(bezt->weight, 0.0f, 1.0f); + if (bezt->weight < minmax_weights[0]) { + minmax_weights[0] = bezt->weight; + } + else if (bezt->weight > minmax_weights[1]) { + minmax_weights[1] = bezt->weight; + } + + /* Update timing data */ + if (do_gtd) { + gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); + } } -static void gp_stroke_to_bezier( - bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, - float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, - const bool add_end_point, tGpTimingData *gtd) +static void gp_stroke_to_bezier(bContext *C, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDstroke *gps, + Curve *cu, + rctf *subrect, + Nurb **curnu, + float minmax_weights[2], + const float rad_fac, + bool stitch, + const bool add_start_point, + const bool add_end_point, + tGpTimingData *gtd) { - bGPDspoint *pt; - Nurb *nu = (curnu) ? *curnu : NULL; - BezTriple *bezt, *prev_bezt = NULL; - int i, tot, old_nbezt = 0; - const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); - float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3]; - const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); - - /* create new 'nurb' or extend current one within the curve */ - if (nu) { - old_nbezt = nu->pntsu; - /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke, - * so no need to add it. - * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes. - */ - BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points); - } - else { - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); - - nu->pntsu = gps->totpoints + add_start_end_points; - nu->resolu = 12; - nu->resolv = 12; - nu->type = CU_BEZIER; - nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts"); - - stitch = false; /* Security! */ - } - - if (do_gtd) { - gp_timing_data_set_nbr(gtd, nu->pntsu); - } - - tot = gps->totpoints; - - /* get initial coordinates */ - pt = gps->points; - if (tot) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); - if (tot > 1) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); - } - if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); - } - } - - /* If needed, make the link between both strokes with two zero-radius additional points */ - if (curnu && old_nbezt) { - BLI_assert(gps->prev != NULL); - - /* Update last point's second handle */ - if (stitch) { - bezt = &nu->bezt[old_nbezt - 1]; - interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC); - copy_v3_v3(bezt->vec[2], h2); - pt++; - } - - /* Create "link points" */ - /* About "zero-radius" point interpolations: - * - If we have at least two points in current curve (most common case), we linearly extrapolate - * the last segment to get the first point (p1) position and timing. - * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point - * with the first point of the current stroke. - * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated - * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... - */ - else { - float p1[3], p2[3]; - float dt1 = 0.0f, dt2 = 0.0f; - - prev_bezt = NULL; - if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) { - /* Only use last curve segment if previous stroke was not a single-point one! */ - prev_bezt = &nu->bezt[old_nbezt - 2]; - } - bezt = &nu->bezt[old_nbezt - 1]; - - /* First point */ - if (prev_bezt) { - interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC); - if (do_gtd) { - const int idx = gps->prev->totpoints - 1; - dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC); - if (do_gtd) { - dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); - } - } - - /* Second point */ - /* Note dt2 is always negative, which marks the gap. */ - if (tot > 1) { - interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); - } - } - - /* Second handle of last point of previous stroke. */ - interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC); - copy_v3_v3(bezt->vec[2], h2); - - /* First point */ - interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC); - bezt++; - gp_stroke_to_bezier_add_point( - gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1, - 0.0f, rad_fac, minmax_weights); - - /* Second point */ - interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC); - bezt++; - gp_stroke_to_bezier_add_point( - gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, - 0.0f, rad_fac, minmax_weights); - - old_nbezt += 2; - copy_v3_v3(p3d_prev, p2); - } - } - else if (add_start_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC); - if (do_gtd) { - dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, p3d_cur); - p[0] -= GAP_DFAC; /* Rather arbitrary... */ - dt = -GAP_DFAC; /* Rather arbitrary too! */ - } - interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC); - bezt = &nu->bezt[old_nbezt]; - gp_stroke_to_bezier_add_point( - gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, - 0.0f, rad_fac, minmax_weights); - - old_nbezt++; - copy_v3_v3(p3d_prev, p); - } - - if (old_nbezt) { - prev_bezt = &nu->bezt[old_nbezt - 1]; - } - - /* add points */ - for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; - - if (i || old_nbezt) { - interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); - } - else { - interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC); - } - - if (i < tot - 1) { - interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC); - } - else { - interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC); - } - - gp_stroke_to_bezier_add_point( - gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur, - do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); - - /* shift coord vects */ - copy_v3_v3(p3d_prev, p3d_cur); - copy_v3_v3(p3d_cur, p3d_next); - - if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); - } - - prev_bezt = bezt; - } - - if (add_end_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC); - if (do_gtd) { - const int idx = gps->totpoints - 1; - dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, prev_bezt->vec[1]); - p[0] += GAP_DFAC; /* Rather arbitrary... */ - dt = GAP_DFAC; /* Rather arbitrary too! */ - } - - /* Second handle of last point of this stroke. */ - interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC); - copy_v3_v3(prev_bezt->vec[2], h2); - - /* The end point */ - interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC); - /* Note bezt has already been incremented in main loop above, so it points to the right place. */ - gp_stroke_to_bezier_add_point( - gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt, - 0.0f, rad_fac, minmax_weights); - } - - /* must calculate handles or else we crash */ - BKE_nurb_handles_calc(nu); - - if (!curnu || !*curnu) { - BLI_addtail(&cu->nurb, nu); - } - if (curnu) { - *curnu = nu; - } + bGPDspoint *pt; + Nurb *nu = (curnu) ? *curnu : NULL; + BezTriple *bezt, *prev_bezt = NULL; + int i, tot, old_nbezt = 0; + const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); + float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3]; + const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); + + /* create new 'nurb' or extend current one within the curve */ + if (nu) { + old_nbezt = nu->pntsu; + /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke, + * so no need to add it. + * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes. + */ + BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points); + } + else { + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); + + nu->pntsu = gps->totpoints + add_start_end_points; + nu->resolu = 12; + nu->resolv = 12; + nu->type = CU_BEZIER; + nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts"); + + stitch = false; /* Security! */ + } + + if (do_gtd) { + gp_timing_data_set_nbr(gtd, nu->pntsu); + } + + tot = gps->totpoints; + + /* get initial coordinates */ + pt = gps->points; + if (tot) { + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + if (tot > 1) { + gp_strokepoint_convertcoords( + C, gpd, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + } + if (stitch && tot > 2) { + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); + } + } + + /* If needed, make the link between both strokes with two zero-radius additional points */ + if (curnu && old_nbezt) { + BLI_assert(gps->prev != NULL); + + /* Update last point's second handle */ + if (stitch) { + bezt = &nu->bezt[old_nbezt - 1]; + interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC); + copy_v3_v3(bezt->vec[2], h2); + pt++; + } + + /* Create "link points" */ + /* About "zero-radius" point interpolations: + * - If we have at least two points in current curve (most common case), we linearly extrapolate + * the last segment to get the first point (p1) position and timing. + * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point + * with the first point of the current stroke. + * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated + * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... + */ + else { + float p1[3], p2[3]; + float dt1 = 0.0f, dt2 = 0.0f; + + prev_bezt = NULL; + if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) { + /* Only use last curve segment if previous stroke was not a single-point one! */ + prev_bezt = &nu->bezt[old_nbezt - 2]; + } + bezt = &nu->bezt[old_nbezt - 1]; + + /* First point */ + if (prev_bezt) { + interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC); + if (do_gtd) { + const int idx = gps->prev->totpoints - 1; + dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC); + if (do_gtd) { + dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); + } + } + + /* Second point */ + /* Note dt2 is always negative, which marks the gap. */ + if (tot > 1) { + interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); + } + } + + /* Second handle of last point of previous stroke. */ + interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC); + copy_v3_v3(bezt->vec[2], h2); + + /* First point */ + interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC); + bezt++; + gp_stroke_to_bezier_add_point(gtd, + bezt, + p1, + h1, + h2, + (bezt - 1)->vec[1], + do_gtd, + gps->prev->inittime, + dt1, + 0.0f, + rad_fac, + minmax_weights); + + /* Second point */ + interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC); + bezt++; + gp_stroke_to_bezier_add_point( + gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); + + old_nbezt += 2; + copy_v3_v3(p3d_prev, p2); + } + } + else if (add_start_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC); + if (do_gtd) { + dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, p3d_cur); + p[0] -= GAP_DFAC; /* Rather arbitrary... */ + dt = -GAP_DFAC; /* Rather arbitrary too! */ + } + interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC); + bezt = &nu->bezt[old_nbezt]; + gp_stroke_to_bezier_add_point( + gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); + + old_nbezt++; + copy_v3_v3(p3d_prev, p); + } + + if (old_nbezt) { + prev_bezt = &nu->bezt[old_nbezt - 1]; + } + + /* add points */ + for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; + + if (i || old_nbezt) { + interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); + } + else { + interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC); + } + + if (i < tot - 1) { + interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC); + } + else { + interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC); + } + + gp_stroke_to_bezier_add_point(gtd, + bezt, + p3d_cur, + h1, + h2, + prev_bezt ? prev_bezt->vec[1] : p3d_cur, + do_gtd, + gps->inittime, + pt->time, + width, + rad_fac, + minmax_weights); + + /* shift coord vects */ + copy_v3_v3(p3d_prev, p3d_cur); + copy_v3_v3(p3d_cur, p3d_next); + + if (i + 2 < tot) { + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); + } + + prev_bezt = bezt; + } + + if (add_end_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC); + if (do_gtd) { + const int idx = gps->totpoints - 1; + dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, prev_bezt->vec[1]); + p[0] += GAP_DFAC; /* Rather arbitrary... */ + dt = GAP_DFAC; /* Rather arbitrary too! */ + } + + /* Second handle of last point of this stroke. */ + interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC); + copy_v3_v3(prev_bezt->vec[2], h2); + + /* The end point */ + interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC); + /* Note bezt has already been incremented in main loop above, so it points to the right place. */ + gp_stroke_to_bezier_add_point(gtd, + bezt, + p, + h1, + h2, + prev_bezt->vec[1], + do_gtd, + gps->inittime, + dt, + 0.0f, + rad_fac, + minmax_weights); + } + + /* must calculate handles or else we crash */ + BKE_nurb_handles_calc(nu); + + if (!curnu || !*curnu) { + BLI_addtail(&cu->nurb, nu); + } + if (curnu) { + *curnu = nu; + } } #undef GAP_DFAC @@ -1046,200 +1165,226 @@ static void gp_stroke_to_bezier( static void gp_stroke_finalize_curve_endpoints(Curve *cu) { - /* start */ - Nurb *nu = cu->nurb.first; - int i = 0; - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - if (bezt) { - bezt[i].weight = bezt[i].radius = 0.0f; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - if (bp) { - bp[i].weight = bp[i].radius = 0.0f; - } - } - - /* end */ - nu = cu->nurb.last; - i = nu->pntsu - 1; - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - if (bezt) { - bezt[i].weight = bezt[i].radius = 0.0f; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - if (bp) { - bp[i].weight = bp[i].radius = 0.0f; - } - } + /* start */ + Nurb *nu = cu->nurb.first; + int i = 0; + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + if (bezt) { + bezt[i].weight = bezt[i].radius = 0.0f; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + if (bp) { + bp[i].weight = bp[i].radius = 0.0f; + } + } + + /* end */ + nu = cu->nurb.last; + i = nu->pntsu - 1; + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + if (bezt) { + bezt[i].weight = bezt[i].radius = 0.0f; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + if (bp) { + bp[i].weight = bp[i].radius = 0.0f; + } + } } static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2]) { - Nurb *nu; - const float delta = minmax_weights[0]; - float fac; - int i; - - /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */ - if (IS_EQF(delta, minmax_weights[1])) - fac = 1.0f; - else - fac = 1.0f / (minmax_weights[1] - delta); - - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - for (i = 0; i < nu->pntsu; i++, bezt++) { - bezt->weight = (bezt->weight - delta) * fac; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - for (i = 0; i < nu->pntsu; i++, bp++) { - bp->weight = (bp->weight - delta) * fac; - } - } - } + Nurb *nu; + const float delta = minmax_weights[0]; + float fac; + int i; + + /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */ + if (IS_EQF(delta, minmax_weights[1])) + fac = 1.0f; + else + fac = 1.0f / (minmax_weights[1] - delta); + + for (nu = cu->nurb.first; nu; nu = nu->next) { + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + for (i = 0; i < nu->pntsu; i++, bezt++) { + bezt->weight = (bezt->weight - delta) * fac; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + for (i = 0; i < nu->pntsu; i++, bp++) { + bp->weight = (bp->weight - delta) * fac; + } + } + } } static int gp_camera_view_subrect(bContext *C, rctf *subrect) { - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - - if (v3d) { - RegionView3D *rv3d = ar->regiondata; - - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, subrect, true); - return 1; - } - } - - return 0; + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + + if (v3d) { + RegionView3D *rv3d = ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, subrect, true); + return 1; + } + } + + return 0; } /* convert a given grease-pencil layer to a 3d-curve representation * (using current view if appropriate) */ -static void gp_layer_to_curve( - bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode, - const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd) +static void gp_layer_to_curve(bContext *C, + ReportList *reports, + bGPdata *gpd, + bGPDlayer *gpl, + const int mode, + const bool norm_weights, + const float rad_fac, + const bool link_strokes, + tGpTimingData *gtd) { - struct Main *bmain = CTX_data_main(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Collection *collection = CTX_data_collection(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - bGPDstroke *gps, *prev_gps = NULL; - Object *ob; - Curve *cu; - Nurb *nu = NULL; - Base *base_new = NULL; - float minmax_weights[2] = {1.0f, 0.0f}; - - /* camera framing */ - rctf subrect, *subrect_ptr = NULL; - - /* error checking */ - if (ELEM(NULL, gpd, gpl, gpf)) - return; - - /* only convert if there are any strokes on this layer's frame to convert */ - if (BLI_listbase_is_empty(&gpf->strokes)) - return; - - /* initialize camera framing */ - if (gp_camera_view_subrect(C, &subrect)) { - subrect_ptr = &subrect; - } - - /* init the curve object (remove rotation and get curve data from it) - * - must clear transforms set on object, as those skew our results - */ - ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); - cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); - BKE_collection_object_add(bmain, collection, ob); - base_new = BKE_view_layer_base_find(view_layer, ob); - DEG_relations_tag_update(bmain); /* added object */ - - cu->flag |= CU_3D; - - gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime; - - /* add points to curve */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - const bool add_start_point = (link_strokes && !(prev_gps)); - const bool add_end_point = (link_strokes && !(gps->next)); - - /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, - * and stitch them to previous one. */ - bool stitch = false; - if (prev_gps) { - bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1]; - bGPDspoint *pt2 = &gps->points[0]; - - if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) { - stitch = true; - } - } - - /* Decide whether we connect this stroke to previous one */ - if (!(stitch || link_strokes)) { - nu = NULL; - } - - switch (mode) { - case GP_STROKECONVERT_PATH: - gp_stroke_to_path( - C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, - add_start_point, add_end_point, gtd); - break; - case GP_STROKECONVERT_CURVE: - case GP_STROKECONVERT_POLY: /* convert after */ - gp_stroke_to_bezier( - C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, - add_start_point, add_end_point, gtd); - break; - default: - BLI_assert(!"invalid mode"); - break; - } - prev_gps = gps; - } - - /* If link_strokes, be sure first and last points have a zero weight/size! */ - if (link_strokes) { - gp_stroke_finalize_curve_endpoints(cu); - } - - /* Update curve's weights, if needed */ - if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) { - gp_stroke_norm_curve_weights(cu, minmax_weights); - } - - /* Create the path animation, if needed */ - gp_stroke_path_animation(C, reports, cu, gtd); - - if (mode == GP_STROKECONVERT_POLY) { - for (nu = cu->nurb.first; nu; nu = nu->next) { - BKE_nurb_type_convert(nu, CU_POLY, false); - } - } - - /* set the layer and select */ - base_new->flag |= SELECT; - BKE_scene_object_base_flag_sync_from_base(base_new); + struct Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Collection *collection = CTX_data_collection(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + bGPDstroke *gps, *prev_gps = NULL; + Object *ob; + Curve *cu; + Nurb *nu = NULL; + Base *base_new = NULL; + float minmax_weights[2] = {1.0f, 0.0f}; + + /* camera framing */ + rctf subrect, *subrect_ptr = NULL; + + /* error checking */ + if (ELEM(NULL, gpd, gpl, gpf)) + return; + + /* only convert if there are any strokes on this layer's frame to convert */ + if (BLI_listbase_is_empty(&gpf->strokes)) + return; + + /* initialize camera framing */ + if (gp_camera_view_subrect(C, &subrect)) { + subrect_ptr = &subrect; + } + + /* init the curve object (remove rotation and get curve data from it) + * - must clear transforms set on object, as those skew our results + */ + ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); + cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); + BKE_collection_object_add(bmain, collection, ob); + base_new = BKE_view_layer_base_find(view_layer, ob); + DEG_relations_tag_update(bmain); /* added object */ + + cu->flag |= CU_3D; + + gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime; + + /* add points to curve */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + const bool add_start_point = (link_strokes && !(prev_gps)); + const bool add_end_point = (link_strokes && !(gps->next)); + + /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, + * and stitch them to previous one. */ + bool stitch = false; + if (prev_gps) { + bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1]; + bGPDspoint *pt2 = &gps->points[0]; + + if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) { + stitch = true; + } + } + + /* Decide whether we connect this stroke to previous one */ + if (!(stitch || link_strokes)) { + nu = NULL; + } + + switch (mode) { + case GP_STROKECONVERT_PATH: + gp_stroke_to_path(C, + gpd, + gpl, + gps, + cu, + subrect_ptr, + &nu, + minmax_weights, + rad_fac, + stitch, + add_start_point, + add_end_point, + gtd); + break; + case GP_STROKECONVERT_CURVE: + case GP_STROKECONVERT_POLY: /* convert after */ + gp_stroke_to_bezier(C, + gpd, + gpl, + gps, + cu, + subrect_ptr, + &nu, + minmax_weights, + rad_fac, + stitch, + add_start_point, + add_end_point, + gtd); + break; + default: + BLI_assert(!"invalid mode"); + break; + } + prev_gps = gps; + } + + /* If link_strokes, be sure first and last points have a zero weight/size! */ + if (link_strokes) { + gp_stroke_finalize_curve_endpoints(cu); + } + + /* Update curve's weights, if needed */ + if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) { + gp_stroke_norm_curve_weights(cu, minmax_weights); + } + + /* Create the path animation, if needed */ + gp_stroke_path_animation(C, reports, cu, gtd); + + if (mode == GP_STROKECONVERT_POLY) { + for (nu = cu->nurb.first; nu; nu = nu->next) { + BKE_nurb_type_convert(nu, CU_POLY, false); + } + } + + /* set the layer and select */ + base_new->flag |= SELECT; + BKE_scene_object_base_flag_sync_from_base(base_new); } /* --- */ @@ -1249,279 +1394,348 @@ static void gp_layer_to_curve( */ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDframe *gpf = NULL; - bGPDstroke *gps = NULL; - bGPDspoint *pt; - double base_time, cur_time, prev_time = -1.0; - int i; - bool valid = true; - - if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV)) || !(gps = gpf->strokes.first)) - return false; - - do { - base_time = cur_time = gps->inittime; - if (cur_time <= prev_time) { - valid = false; - break; - } - - prev_time = cur_time; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - cur_time = base_time + (double)pt->time; - /* First point of a stroke should have the same time as stroke's inittime, - * so it's the only case where equality is allowed! - */ - if ((i && cur_time <= prev_time) || (cur_time < prev_time)) { - valid = false; - break; - } - prev_time = cur_time; - } - - if (!valid) { - break; - } - } while ((gps = gps->next)); - - if (op) { - RNA_boolean_set(op->ptr, "use_timing_data", valid); - } - return valid; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = NULL; + bGPDstroke *gps = NULL; + bGPDspoint *pt; + double base_time, cur_time, prev_time = -1.0; + int i; + bool valid = true; + + if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV)) || + !(gps = gpf->strokes.first)) + return false; + + do { + base_time = cur_time = gps->inittime; + if (cur_time <= prev_time) { + valid = false; + break; + } + + prev_time = cur_time; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + cur_time = base_time + (double)pt->time; + /* First point of a stroke should have the same time as stroke's inittime, + * so it's the only case where equality is allowed! + */ + if ((i && cur_time <= prev_time) || (cur_time < prev_time)) { + valid = false; + break; + } + prev_time = cur_time; + } + + if (!valid) { + break; + } + } while ((gps = gps->next)); + + if (op) { + RNA_boolean_set(op->ptr, "use_timing_data", valid); + } + return valid; } /* Check end_frame is always > start frame! */ -static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UNUSED(scene), struct PointerRNA *ptr) +static void gp_convert_set_end_frame(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) { - int start_frame = RNA_int_get(ptr, "start_frame"); - int end_frame = RNA_int_get(ptr, "end_frame"); + int start_frame = RNA_int_get(ptr, "start_frame"); + int end_frame = RNA_int_get(ptr, "end_frame"); - if (end_frame <= start_frame) { - RNA_int_set(ptr, "end_frame", start_frame + 1); - } + if (end_frame <= start_frame) { + RNA_int_set(ptr, "end_frame", start_frame + 1); + } } static bool gp_convert_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = NULL; - bGPDframe *gpf = NULL; - ScrArea *sa = CTX_wm_area(C); - - /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), - * and if we are not in edit mode! - */ - return ((sa && sa->spacetype == SPACE_VIEW3D) && - (gpl = BKE_gpencil_layer_getactive(gpd)) && - (gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV)) && - (gpf->strokes.first) && - (!GPENCIL_ANY_EDIT_MODE(gpd))); + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = NULL; + bGPDframe *gpf = NULL; + ScrArea *sa = CTX_wm_area(C); + + /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), + * and if we are not in edit mode! + */ + return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_getactive(gpd)) && + (gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV)) && + (gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd))); } static int gp_convert_layer_exec(bContext *C, wmOperator *op) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - Scene *scene = CTX_data_scene(C); - const int mode = RNA_enum_get(op->ptr, "type"); - const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); - const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier"); - const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes"); - bool valid_timing; - tGpTimingData gtd; - - /* check if there's data to work with */ - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on"); - return OPERATOR_CANCELLED; - } - - if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) { - BKE_report(op->reports, RPT_WARNING, - "Current Grease Pencil strokes have no valid timing data, most timing options will be hidden!"); - } - valid_timing = RNA_property_boolean_get(op->ptr, prop); - - gtd.mode = RNA_enum_get(op->ptr, "timing_mode"); - /* Check for illegal timing mode! */ - if (!valid_timing && !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) { - gtd.mode = GP_STROKECONVERT_TIMING_LINEAR; - RNA_enum_set(op->ptr, "timing_mode", gtd.mode); - } - if (!link_strokes) { - gtd.mode = GP_STROKECONVERT_TIMING_NONE; - } - - /* grab all relevant settings */ - gtd.frame_range = RNA_int_get(op->ptr, "frame_range"); - gtd.start_frame = RNA_int_get(op->ptr, "start_frame"); - gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false; - gtd.end_frame = RNA_int_get(op->ptr, "end_frame"); - gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration"); - gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness"); - gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration); - gtd.seed = RNA_int_get(op->ptr, "seed"); - gtd.num_points = gtd.cur_point = 0; - gtd.dists = gtd.times = NULL; - gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f; - gtd.inittime = 0.0; - gtd.offset_time = 0.0f; - - /* perform conversion */ - gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); - - /* free temp memory */ - if (gtd.dists) { - MEM_freeN(gtd.dists); - gtd.dists = NULL; - } - if (gtd.times) { - MEM_freeN(gtd.times); - gtd.times = NULL; - } - - /* notifiers */ - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - /* done */ - return OPERATOR_FINISHED; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + Scene *scene = CTX_data_scene(C); + const int mode = RNA_enum_get(op->ptr, "type"); + const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); + const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier"); + const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes"); + bool valid_timing; + tGpTimingData gtd; + + /* check if there's data to work with */ + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on"); + return OPERATOR_CANCELLED; + } + + if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) { + BKE_report(op->reports, + RPT_WARNING, + "Current Grease Pencil strokes have no valid timing data, most timing options will " + "be hidden!"); + } + valid_timing = RNA_property_boolean_get(op->ptr, prop); + + gtd.mode = RNA_enum_get(op->ptr, "timing_mode"); + /* Check for illegal timing mode! */ + if (!valid_timing && + !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) { + gtd.mode = GP_STROKECONVERT_TIMING_LINEAR; + RNA_enum_set(op->ptr, "timing_mode", gtd.mode); + } + if (!link_strokes) { + gtd.mode = GP_STROKECONVERT_TIMING_NONE; + } + + /* grab all relevant settings */ + gtd.frame_range = RNA_int_get(op->ptr, "frame_range"); + gtd.start_frame = RNA_int_get(op->ptr, "start_frame"); + gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false; + gtd.end_frame = RNA_int_get(op->ptr, "end_frame"); + gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration"); + gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness"); + gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration); + gtd.seed = RNA_int_get(op->ptr, "seed"); + gtd.num_points = gtd.cur_point = 0; + gtd.dists = gtd.times = NULL; + gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f; + gtd.inittime = 0.0; + gtd.offset_time = 0.0f; + + /* perform conversion */ + gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); + + /* free temp memory */ + if (gtd.dists) { + MEM_freeN(gtd.dists); + gtd.dists = NULL; + } + if (gtd.times) { + MEM_freeN(gtd.times); + gtd.times = NULL; + } + + /* notifiers */ + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* done */ + return OPERATOR_FINISHED; } -static bool gp_convert_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) +static bool gp_convert_poll_property(const bContext *UNUSED(C), + wmOperator *op, + const PropertyRNA *prop) { - PointerRNA *ptr = op->ptr; - const char *prop_id = RNA_property_identifier(prop); - const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes"); - int timing_mode = RNA_enum_get(ptr, "timing_mode"); - bool realtime = RNA_boolean_get(ptr, "use_realtime"); - float gap_duration = RNA_float_get(ptr, "gap_duration"); - float gap_randomness = RNA_float_get(ptr, "gap_randomness"); - const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data"); - - /* Always show those props */ - if (STREQ(prop_id, "type") || - STREQ(prop_id, "use_normalize_weights") || - STREQ(prop_id, "radius_multiplier") || - STREQ(prop_id, "use_link_strokes")) - { - return true; - } - - /* Never show this prop */ - if (STREQ(prop_id, "use_timing_data")) - return false; - - if (link_strokes) { - /* Only show when link_stroke is true */ - if (STREQ(prop_id, "timing_mode")) - return true; - - if (timing_mode != GP_STROKECONVERT_TIMING_NONE) { - /* Only show when link_stroke is true and stroke timing is enabled */ - if (STREQ(prop_id, "frame_range") || - STREQ(prop_id, "start_frame")) - { - return true; - } - - /* Only show if we have valid timing data! */ - if (valid_timing && STREQ(prop_id, "use_realtime")) - return true; - - /* Only show if realtime or valid_timing is false! */ - if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame")) - return true; - - if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - /* Only show for custom gaps! */ - if (STREQ(prop_id, "gap_duration")) - return true; - - /* Only show randomness for non-null custom gaps! */ - if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f)) - return true; - - /* Only show seed for randomize action! */ - if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) - return true; - } - } - } - - /* Else, hidden! */ - return false; + PointerRNA *ptr = op->ptr; + const char *prop_id = RNA_property_identifier(prop); + const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes"); + int timing_mode = RNA_enum_get(ptr, "timing_mode"); + bool realtime = RNA_boolean_get(ptr, "use_realtime"); + float gap_duration = RNA_float_get(ptr, "gap_duration"); + float gap_randomness = RNA_float_get(ptr, "gap_randomness"); + const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data"); + + /* Always show those props */ + if (STREQ(prop_id, "type") || STREQ(prop_id, "use_normalize_weights") || + STREQ(prop_id, "radius_multiplier") || STREQ(prop_id, "use_link_strokes")) { + return true; + } + + /* Never show this prop */ + if (STREQ(prop_id, "use_timing_data")) + return false; + + if (link_strokes) { + /* Only show when link_stroke is true */ + if (STREQ(prop_id, "timing_mode")) + return true; + + if (timing_mode != GP_STROKECONVERT_TIMING_NONE) { + /* Only show when link_stroke is true and stroke timing is enabled */ + if (STREQ(prop_id, "frame_range") || STREQ(prop_id, "start_frame")) { + return true; + } + + /* Only show if we have valid timing data! */ + if (valid_timing && STREQ(prop_id, "use_realtime")) + return true; + + /* Only show if realtime or valid_timing is false! */ + if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame")) + return true; + + if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + /* Only show for custom gaps! */ + if (STREQ(prop_id, "gap_duration")) + return true; + + /* Only show randomness for non-null custom gaps! */ + if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f)) + return true; + + /* Only show seed for randomize action! */ + if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) + return true; + } + } + } + + /* Else, hidden! */ + return false; } void GPENCIL_OT_convert(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Convert Grease Pencil"; - ot->idname = "GPENCIL_OT_convert"; - ot->description = "Convert the active Grease Pencil layer to a new Curve Object"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_convert_layer_exec; - ot->poll = gp_convert_poll; - ot->poll_property = gp_convert_poll_property; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to"); - - RNA_def_boolean(ot->srna, "use_normalize_weights", true, "Normalize Weight", - "Normalize weight (set from stroke width)"); - RNA_def_float(ot->srna, "radius_multiplier", 1.0f, 0.0f, 1000.0f, "Radius Fac", - "Multiplier for the points' radii (set from stroke width)", 0.0f, 10.0f); - RNA_def_boolean(ot->srna, "use_link_strokes", false, "Link Strokes", - "Whether to link strokes with zero-radius sections of curves"); - - prop = RNA_def_enum(ot->srna, "timing_mode", prop_gpencil_convert_timingmodes, GP_STROKECONVERT_TIMING_FULL, - "Timing Mode", "How to use timing data stored in strokes"); - RNA_def_enum_funcs(prop, rna_GPConvert_mode_items); - - RNA_def_int(ot->srna, "frame_range", 100, 1, 10000, "Frame Range", - "The duration of evaluation of the path control curve", 1, 1000); - RNA_def_int(ot->srna, "start_frame", 1, 1, 100000, "Start Frame", - "The start frame of the path control curve", 1, 100000); - RNA_def_boolean(ot->srna, "use_realtime", false, "Realtime", - "Whether the path control curve reproduces the drawing in realtime, starting from Start Frame"); - prop = RNA_def_int(ot->srna, "end_frame", 250, 1, 100000, "End Frame", - "The end frame of the path control curve (if Realtime is not set)", 1, 100000); - RNA_def_property_update_runtime(prop, gp_convert_set_end_frame); - - RNA_def_float(ot->srna, "gap_duration", 0.0f, 0.0f, 10000.0f, "Gap Duration", - "Custom Gap mode: (Average) length of gaps, in frames " - "(Note: Realtime value, will be scaled if Realtime is not set)", 0.0f, 1000.0f); - RNA_def_float(ot->srna, "gap_randomness", 0.0f, 0.0f, 10000.0f, "Gap Randomness", - "Custom Gap mode: Number of frames that gap lengths can vary", 0.0f, 1000.0f); - RNA_def_int(ot->srna, "seed", 0, 0, 1000, "Random Seed", - "Custom Gap mode: Random generator seed", 0, 100); - - /* Note: Internal use, this one will always be hidden by UI code... */ - prop = RNA_def_boolean(ot->srna, "use_timing_data", false, "Has Valid Timing", - "Whether the converted Grease Pencil layer has valid timing data (internal use)"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Convert Grease Pencil"; + ot->idname = "GPENCIL_OT_convert"; + ot->description = "Convert the active Grease Pencil layer to a new Curve Object"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_convert_layer_exec; + ot->poll = gp_convert_poll; + ot->poll_property = gp_convert_poll_property; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum( + ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to"); + + RNA_def_boolean(ot->srna, + "use_normalize_weights", + true, + "Normalize Weight", + "Normalize weight (set from stroke width)"); + RNA_def_float(ot->srna, + "radius_multiplier", + 1.0f, + 0.0f, + 1000.0f, + "Radius Fac", + "Multiplier for the points' radii (set from stroke width)", + 0.0f, + 10.0f); + RNA_def_boolean(ot->srna, + "use_link_strokes", + false, + "Link Strokes", + "Whether to link strokes with zero-radius sections of curves"); + + prop = RNA_def_enum(ot->srna, + "timing_mode", + prop_gpencil_convert_timingmodes, + GP_STROKECONVERT_TIMING_FULL, + "Timing Mode", + "How to use timing data stored in strokes"); + RNA_def_enum_funcs(prop, rna_GPConvert_mode_items); + + RNA_def_int(ot->srna, + "frame_range", + 100, + 1, + 10000, + "Frame Range", + "The duration of evaluation of the path control curve", + 1, + 1000); + RNA_def_int(ot->srna, + "start_frame", + 1, + 1, + 100000, + "Start Frame", + "The start frame of the path control curve", + 1, + 100000); + RNA_def_boolean(ot->srna, + "use_realtime", + false, + "Realtime", + "Whether the path control curve reproduces the drawing in realtime, starting " + "from Start Frame"); + prop = RNA_def_int(ot->srna, + "end_frame", + 250, + 1, + 100000, + "End Frame", + "The end frame of the path control curve (if Realtime is not set)", + 1, + 100000); + RNA_def_property_update_runtime(prop, gp_convert_set_end_frame); + + RNA_def_float(ot->srna, + "gap_duration", + 0.0f, + 0.0f, + 10000.0f, + "Gap Duration", + "Custom Gap mode: (Average) length of gaps, in frames " + "(Note: Realtime value, will be scaled if Realtime is not set)", + 0.0f, + 1000.0f); + RNA_def_float(ot->srna, + "gap_randomness", + 0.0f, + 0.0f, + 10000.0f, + "Gap Randomness", + "Custom Gap mode: Number of frames that gap lengths can vary", + 0.0f, + 1000.0f); + RNA_def_int(ot->srna, + "seed", + 0, + 0, + 1000, + "Random Seed", + "Custom Gap mode: Random generator seed", + 0, + 100); + + /* Note: Internal use, this one will always be hidden by UI code... */ + prop = RNA_def_boolean( + ot->srna, + "use_timing_data", + false, + "Has Valid Timing", + "Whether the converted Grease Pencil layer has valid timing data (internal use)"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index a49f6a0d414..c91c543e746 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -91,81 +90,81 @@ /* ******************* Add New Data ************************ */ static bool gp_data_add_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); - if (obact && obact->type == OB_GPENCIL) { - if (obact->mode != OB_MODE_OBJECT) { - return false; - } - } + Object *obact = CTX_data_active_object(C); + if (obact && obact->type == OB_GPENCIL) { + if (obact->mode != OB_MODE_OBJECT) { + return false; + } + } - /* the base line we have is that we have somewhere to add Grease Pencil data */ - return ED_gpencil_data_get_pointers(C, NULL) != NULL; + /* the base line we have is that we have somewhere to add Grease Pencil data */ + return ED_gpencil_data_get_pointers(C, NULL) != NULL; } /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { - PointerRNA gpd_owner = {{NULL}}; - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); - bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - else { - /* decrement user count and add new datablock */ - /* TODO: if a datablock exists, - * we should make a copy of it instead of starting fresh (as in other areas) */ - Main *bmain = CTX_data_main(C); + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + else { + /* decrement user count and add new datablock */ + /* TODO: if a datablock exists, + * we should make a copy of it instead of starting fresh (as in other areas) */ + Main *bmain = CTX_data_main(C); - /* decrement user count of old GP datablock */ - if (*gpd_ptr) { - bGPdata *gpd = (*gpd_ptr); - id_us_min(&gpd->id); - } + /* decrement user count of old GP datablock */ + if (*gpd_ptr) { + bGPdata *gpd = (*gpd_ptr); + id_us_min(&gpd->id); + } - /* add new datablock, with a single layer ready to use (so users don't have to perform an extra step) */ - if (is_annotation) { - bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); - *gpd_ptr = gpd; + /* add new datablock, with a single layer ready to use (so users don't have to perform an extra step) */ + if (is_annotation) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + *gpd_ptr = gpd; - /* tag for annotations */ - gpd->flag |= GP_DATA_ANNOTATIONS; + /* tag for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; - /* add new layer (i.e. a "note") */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); - } - else { - /* GP Object Case - This shouldn't happen! */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + /* add new layer (i.e. a "note") */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + } + else { + /* GP Object Case - This shouldn't happen! */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - /* add default sets of colors and brushes */ - Object *ob = CTX_data_active_object(C); - ED_gpencil_add_defaults(C, ob); + /* add default sets of colors and brushes */ + Object *ob = CTX_data_active_object(C); + ED_gpencil_add_defaults(C, ob); - /* add new layer */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); - } - } + /* add new layer */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } + } - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_data_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Grease Pencil Add New"; - ot->idname = "GPENCIL_OT_data_add"; - ot->description = "Add new Grease Pencil data-block"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Grease Pencil Add New"; + ot->idname = "GPENCIL_OT_data_add"; + ot->description = "Add new Grease Pencil data-block"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_data_add_exec; - ot->poll = gp_data_add_poll; + /* callbacks */ + ot->exec = gp_data_add_exec; + ot->poll = gp_data_add_poll; } /* ******************* Unlink Data ************************ */ @@ -173,57 +172,55 @@ void GPENCIL_OT_data_add(wmOperatorType *ot) /* poll callback for adding data/layers - special */ static bool gp_data_unlink_poll(bContext *C) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - /* only unlink annotation datablocks */ - if ((gpd_ptr != NULL) && (*gpd_ptr != NULL)) { - bGPdata *gpd = (*gpd_ptr); - if ((gpd->flag & GP_DATA_ANNOTATIONS) == 0) { - return false; - } - } - /* if we have access to some active data, make sure there's a datablock before enabling this */ - return (gpd_ptr && *gpd_ptr); + /* only unlink annotation datablocks */ + if ((gpd_ptr != NULL) && (*gpd_ptr != NULL)) { + bGPdata *gpd = (*gpd_ptr); + if ((gpd->flag & GP_DATA_ANNOTATIONS) == 0) { + return false; + } + } + /* if we have access to some active data, make sure there's a datablock before enabling this */ + return (gpd_ptr && *gpd_ptr); } - /* unlink datablock - wrapper around API */ static int gp_data_unlink_exec(bContext *C, wmOperator *op) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - else { - /* just unlink datablock now, decreasing its user count */ - bGPdata *gpd = (*gpd_ptr); + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + else { + /* just unlink datablock now, decreasing its user count */ + bGPdata *gpd = (*gpd_ptr); - id_us_min(&gpd->id); - *gpd_ptr = NULL; - } + id_us_min(&gpd->id); + *gpd_ptr = NULL; + } - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_data_unlink(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Annotation Unlink"; - ot->idname = "GPENCIL_OT_data_unlink"; - ot->description = "Unlink active Annotation data-block"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Annotation Unlink"; + ot->idname = "GPENCIL_OT_data_unlink"; + ot->description = "Unlink active Annotation data-block"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_data_unlink_exec; - ot->poll = gp_data_unlink_poll; + /* callbacks */ + ot->exec = gp_data_unlink_exec; + ot->poll = gp_data_unlink_poll; } - /* ************************************************ */ /* Layer Operators */ @@ -232,618 +229,636 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - PointerRNA gpd_owner = {{NULL}}; - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); - bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); - - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - - if (*gpd_ptr == NULL) { - Main *bmain = CTX_data_main(C); - if (is_annotation) { - /* Annotations */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); - - /* mark as annotation */ - (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; - } - else { - /* GP Object */ - /* NOTE: This shouldn't actually happen in practice */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* add default sets of colors and brushes */ - Object *ob = CTX_data_active_object(C); - ED_gpencil_add_defaults(C, ob); - } - } - - /* add new layer now */ - if (is_annotation) { - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); - } - else { - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); - } - - /* notifiers */ - bGPdata *gpd = *gpd_ptr; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + + if (*gpd_ptr == NULL) { + Main *bmain = CTX_data_main(C); + if (is_annotation) { + /* Annotations */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + + /* mark as annotation */ + (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; + } + else { + /* GP Object */ + /* NOTE: This shouldn't actually happen in practice */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + + /* add default sets of colors and brushes */ + Object *ob = CTX_data_active_object(C); + ED_gpencil_add_defaults(C, ob); + } + } + + /* add new layer now */ + if (is_annotation) { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + } + else { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } + + /* notifiers */ + bGPdata *gpd = *gpd_ptr; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add New Layer"; - ot->idname = "GPENCIL_OT_layer_add"; - ot->description = "Add new layer or note for the active data-block"; + /* identifiers */ + ot->name = "Add New Layer"; + ot->idname = "GPENCIL_OT_layer_add"; + ot->description = "Add new layer or note for the active data-block"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_layer_add_exec; - ot->poll = gp_add_poll; + /* callbacks */ + ot->exec = gp_layer_add_exec; + ot->poll = gp_add_poll; } /* ******************* Remove Active Layer ************************* */ static int gp_layer_remove_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; - if (gpl->flag & GP_LAYER_LOCKED) { - BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers"); - return OPERATOR_CANCELLED; - } + if (gpl->flag & GP_LAYER_LOCKED) { + BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers"); + return OPERATOR_CANCELLED; + } - /* make the layer before this the new active layer - * - use the one after if this is the first - * - if this is the only layer, this naturally becomes NULL - */ - if (gpl->prev) - BKE_gpencil_layer_setactive(gpd, gpl->prev); - else - BKE_gpencil_layer_setactive(gpd, gpl->next); + /* make the layer before this the new active layer + * - use the one after if this is the first + * - if this is the only layer, this naturally becomes NULL + */ + if (gpl->prev) + BKE_gpencil_layer_setactive(gpd, gpl->prev); + else + BKE_gpencil_layer_setactive(gpd, gpl->next); - /* delete the layer now... */ - BKE_gpencil_layer_delete(gpd, gpl); + /* delete the layer now... */ + BKE_gpencil_layer_delete(gpd, gpl); - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_remove(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Layer"; - ot->idname = "GPENCIL_OT_layer_remove"; - ot->description = "Remove active Grease Pencil layer"; + /* identifiers */ + ot->name = "Remove Layer"; + ot->idname = "GPENCIL_OT_layer_remove"; + ot->description = "Remove active Grease Pencil layer"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_layer_remove_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_layer_remove_exec; + ot->poll = gp_active_layer_poll; } /* ******************* Move Layer Up/Down ************************** */ enum { - GP_LAYER_MOVE_UP = -1, - GP_LAYER_MOVE_DOWN = 1, + GP_LAYER_MOVE_UP = -1, + GP_LAYER_MOVE_DOWN = 1, }; static int gp_layer_move_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - const int direction = RNA_enum_get(op->ptr, "type") * -1; + const int direction = RNA_enum_get(op->ptr, "type") * -1; - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; - BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ - if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } + BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ + if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_move(wmOperatorType *ot) { - static const EnumPropertyItem slot_move[] = { - {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, - {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, - }; + static const EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; - /* identifiers */ - ot->name = "Move Grease Pencil Layer"; - ot->idname = "GPENCIL_OT_layer_move"; - ot->description = "Move the active Grease Pencil layer up/down in the list"; + /* identifiers */ + ot->name = "Move Grease Pencil Layer"; + ot->idname = "GPENCIL_OT_layer_move"; + ot->description = "Move the active Grease Pencil layer up/down in the list"; - /* api callbacks */ - ot->exec = gp_layer_move_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gp_layer_move_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } /* ********************* Duplicate Layer ************************** */ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - bGPDlayer *new_layer; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *new_layer; - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; - /* make copy of layer, and add it immediately after the existing layer */ - new_layer = BKE_gpencil_layer_duplicate(gpl); - BLI_insertlinkafter(&gpd->layers, gpl, new_layer); + /* make copy of layer, and add it immediately after the existing layer */ + new_layer = BKE_gpencil_layer_duplicate(gpl); + BLI_insertlinkafter(&gpd->layers, gpl, new_layer); - /* ensure new layer has a unique name, and is now the active layer */ - BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); - BKE_gpencil_layer_setactive(gpd, new_layer); + /* ensure new layer has a unique name, and is now the active layer */ + BLI_uniquename(&gpd->layers, + new_layer, + DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(new_layer->info)); + BKE_gpencil_layer_setactive(gpd, new_layer); - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Duplicate Layer"; - ot->idname = "GPENCIL_OT_layer_duplicate"; - ot->description = "Make a copy of the active Grease Pencil layer"; + /* identifiers */ + ot->name = "Duplicate Layer"; + ot->idname = "GPENCIL_OT_layer_duplicate"; + ot->description = "Make a copy of the active Grease Pencil layer"; - /* callbacks */ - ot->exec = gp_layer_copy_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_layer_copy_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************* Duplicate Layer in a new object ************************** */ enum { - GP_LAYER_COPY_OBJECT_ALL_FRAME = 0, - GP_LAYER_COPY_OBJECT_ACT_FRAME = 1, + GP_LAYER_COPY_OBJECT_ALL_FRAME = 0, + GP_LAYER_COPY_OBJECT_ACT_FRAME = 1, }; static bool gp_layer_duplicate_object_poll(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) - return false; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) + return false; - bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - if (gpl == NULL) - return false; + if (gpl == NULL) + return false; - /* check there are more grease pencil objects */ - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if ((base->object != ob) && (base->object->type == OB_GPENCIL)) - return true; - } + /* check there are more grease pencil objects */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if ((base->object != ob) && (base->object->type == OB_GPENCIL)) + return true; + } - return false; + return false; } static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "object", name); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "object", name); - if (name[0] == '\0') { - return OPERATOR_CANCELLED; - } + if (name[0] == '\0') { + return OPERATOR_CANCELLED; + } - Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name); + Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name); - int mode = RNA_enum_get(op->ptr, "mode"); + int mode = RNA_enum_get(op->ptr, "mode"); - Object *ob_src = CTX_data_active_object(C); - bGPdata *gpd_src = (bGPdata *)ob_src->data; - bGPDlayer *gpl_src = BKE_gpencil_layer_getactive(gpd_src); + Object *ob_src = CTX_data_active_object(C); + bGPdata *gpd_src = (bGPdata *)ob_src->data; + bGPDlayer *gpl_src = BKE_gpencil_layer_getactive(gpd_src); - /* sanity checks */ - if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { - return OPERATOR_CANCELLED; - } - /* cannot copy itself and check destination type */ - if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { - return OPERATOR_CANCELLED; - } + /* sanity checks */ + if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { + return OPERATOR_CANCELLED; + } + /* cannot copy itself and check destination type */ + if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { + return OPERATOR_CANCELLED; + } - bGPdata *gpd_dst = (bGPdata *)ob_dst->data; + bGPdata *gpd_dst = (bGPdata *)ob_dst->data; - /* make copy of layer */ - bGPDlayer *gpl_dst = MEM_dupallocN(gpl_src); - gpl_dst->prev = gpl_dst->next = NULL; - BLI_addtail(&gpd_dst->layers, gpl_dst); - BLI_uniquename(&gpd_dst->layers, gpl_dst, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_dst->info)); + /* make copy of layer */ + bGPDlayer *gpl_dst = MEM_dupallocN(gpl_src); + gpl_dst->prev = gpl_dst->next = NULL; + BLI_addtail(&gpd_dst->layers, gpl_dst); + BLI_uniquename(&gpd_dst->layers, + gpl_dst, + DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(gpl_dst->info)); - /* copy frames */ - BLI_listbase_clear(&gpl_dst->frames); - for (bGPDframe *gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { + /* copy frames */ + BLI_listbase_clear(&gpl_dst->frames); + for (bGPDframe *gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { - if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { - continue; - } + if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + continue; + } - /* make a copy of source frame */ - bGPDframe *gpf_dst = MEM_dupallocN(gpf_src); - gpf_dst->prev = gpf_dst->next = NULL; - BLI_addtail(&gpl_dst->frames, gpf_dst); + /* make a copy of source frame */ + bGPDframe *gpf_dst = MEM_dupallocN(gpf_src); + gpf_dst->prev = gpf_dst->next = NULL; + BLI_addtail(&gpl_dst->frames, gpf_dst); - /* copy strokes */ - BLI_listbase_clear(&gpf_dst->strokes); - for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src); - /* check if material is in destination object, - * otherwise add the slot with the material - */ - Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1); - int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + /* check if material is in destination object, + * otherwise add the slot with the material + */ + Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1); + int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); - /* reasign the stroke material to the right slot in destination object */ - gps_dst->mat_nr = idx; + /* reasign the stroke material to the right slot in destination object */ + gps_dst->mat_nr = idx; - /* add new stroke to frame */ - BLI_addtail(&gpf_dst->strokes, gps_dst); - } - } + /* add new stroke to frame */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } - /* notifiers */ - DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) { - static const EnumPropertyItem copy_mode[] = { - {GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""}, - {GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""}, - {0, NULL, 0, NULL, NULL}, - }; + static const EnumPropertyItem copy_mode[] = { + {GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""}, + {GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""}, + {0, NULL, 0, NULL, NULL}, + }; - /* identifiers */ - ot->name = "Duplicate Layer to new Object"; - ot->idname = "GPENCIL_OT_layer_duplicate_object"; - ot->description = "Make a copy of the active Grease Pencil layer to new object"; + /* identifiers */ + ot->name = "Duplicate Layer to new Object"; + ot->idname = "GPENCIL_OT_layer_duplicate_object"; + ot->description = "Make a copy of the active Grease Pencil layer to new object"; - /* callbacks */ - ot->exec = gp_layer_duplicate_object_exec; - ot->poll = gp_layer_duplicate_object_poll; + /* callbacks */ + ot->exec = gp_layer_duplicate_object_exec; + ot->poll = gp_layer_duplicate_object_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_string(ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object"); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->prop = RNA_def_string( + ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); - RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); + RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); } /* ********************* Duplicate Frame ************************** */ enum { - GP_FRAME_DUP_ACTIVE = 0, - GP_FRAME_DUP_ALL = 1, + GP_FRAME_DUP_ACTIVE = 0, + GP_FRAME_DUP_ALL = 1, }; static int gp_frame_duplicate_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); - int mode = RNA_enum_get(op->ptr, "mode"); + int mode = RNA_enum_get(op->ptr, "mode"); - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; - if (mode == 0) { - BKE_gpencil_frame_addcopy(gpl, cfra_eval); - } - else { - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if ((gpl->flag & GP_LAYER_LOCKED) == 0) { - BKE_gpencil_frame_addcopy(gpl, cfra_eval); - } - } + if (mode == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + else { + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if ((gpl->flag & GP_LAYER_LOCKED) == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + } + } + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_frame_duplicate(wmOperatorType *ot) { - static const EnumPropertyItem duplicate_mode[] = { - {GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only"}, - {GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers"}, - {0, NULL, 0, NULL, NULL}, - }; + static const EnumPropertyItem duplicate_mode[] = { + {GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only"}, + {GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers"}, + {0, NULL, 0, NULL, NULL}, + }; - /* identifiers */ - ot->name = "Duplicate Frame"; - ot->idname = "GPENCIL_OT_frame_duplicate"; - ot->description = "Make a copy of the active Grease Pencil Frame"; + /* identifiers */ + ot->name = "Duplicate Frame"; + ot->idname = "GPENCIL_OT_frame_duplicate"; + ot->description = "Make a copy of the active Grease Pencil Frame"; - /* callbacks */ - ot->exec = gp_frame_duplicate_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_frame_duplicate_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); } /* ********************* Clean Fill Boundaries on Frame ************************** */ enum { - GP_FRAME_CLEAN_FILL_ACTIVE = 0, - GP_FRAME_CLEAN_FILL_ALL = 1, + GP_FRAME_CLEAN_FILL_ACTIVE = 0, + GP_FRAME_CLEAN_FILL_ALL = 1, }; static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) { - bool changed = false; - bGPdata *gpd = ED_gpencil_data_get_active(C); - int mode = RNA_enum_get(op->ptr, "mode"); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (mode == GP_FRAME_CLEAN_FILL_ALL) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) - continue; - - /* simply delete strokes which are no fill */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - /* free stroke */ - if (gps->flag & GP_STROKE_NOFILL) { - /* free stroke memory arrays, then stroke itself */ - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - MEM_SAFE_FREE(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); - - changed = true; - } - } - } - } - } - CTX_DATA_END; - - /* notifiers */ - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - - return OPERATOR_FINISHED; + bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + int mode = RNA_enum_get(op->ptr, "mode"); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (mode == GP_FRAME_CLEAN_FILL_ALL) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + /* simply delete strokes which are no fill */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke */ + if (gps->flag & GP_STROKE_NOFILL) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot) { - static const EnumPropertyItem duplicate_mode[] = { - {GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only"}, - {GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers"}, - {0, NULL, 0, NULL, NULL}, - }; + static const EnumPropertyItem duplicate_mode[] = { + {GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only"}, + {GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers"}, + {0, NULL, 0, NULL, NULL}, + }; - /* identifiers */ - ot->name = "Clean Fill Boundaries"; - ot->idname = "GPENCIL_OT_frame_clean_fill"; - ot->description = "Remove 'no fill' boundary strokes"; + /* identifiers */ + ot->name = "Clean Fill Boundaries"; + ot->idname = "GPENCIL_OT_frame_clean_fill"; + ot->description = "Remove 'no fill' boundary strokes"; - /* callbacks */ - ot->exec = gp_frame_clean_fill_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_frame_clean_fill_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); } /* ********************* Clean Loose Boundaries on Frame ************************** */ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op) { - bool changed = false; - bGPdata *gpd = ED_gpencil_data_get_active(C); - int limit = RNA_int_get(op->ptr, "limit"); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - bGPDstroke *gps = NULL; - bGPDstroke *gpsn = NULL; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - /* simply delete strokes which are no loose */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - /* free stroke */ - if (gps->totpoints <= limit) { - /* free stroke memory arrays, then stroke itself */ - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - MEM_SAFE_FREE(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); - - changed = true; - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; - - /* notifiers */ - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - - return OPERATOR_FINISHED; + bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + int limit = RNA_int_get(op->ptr, "limit"); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + bGPDstroke *gps = NULL; + bGPDstroke *gpsn = NULL; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + /* simply delete strokes which are no loose */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke */ + if (gps->totpoints <= limit) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clean Loose points"; - ot->idname = "GPENCIL_OT_frame_clean_loose"; - ot->description = "Remove loose points"; + /* identifiers */ + ot->name = "Clean Loose points"; + ot->idname = "GPENCIL_OT_frame_clean_loose"; + ot->description = "Remove loose points"; - /* callbacks */ - ot->exec = gp_frame_clean_loose_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_frame_clean_loose_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int(ot->srna, "limit", 1, 1, INT_MAX, "Limit", "Number of points to consider stroke as loose", 1, INT_MAX); + RNA_def_int(ot->srna, + "limit", + 1, + 1, + INT_MAX, + "Limit", + "Number of points to consider stroke as loose", + 1, + INT_MAX); } /* *********************** Hide Layers ******************************** */ static int gp_hide_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); - bool unselected = RNA_boolean_get(op->ptr, "unselected"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); + bool unselected = RNA_boolean_get(op->ptr, "unselected"); - /* sanity checks */ - if (ELEM(NULL, gpd, layer)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd, layer)) + return OPERATOR_CANCELLED; - if (unselected) { - bGPDlayer *gpl; + if (unselected) { + bGPDlayer *gpl; - /* hide unselected */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl != layer) { - gpl->flag |= GP_LAYER_HIDE; - } - } - } - else { - /* hide selected/active */ - layer->flag |= GP_LAYER_HIDE; - } + /* hide unselected */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl != layer) { + gpl->flag |= GP_LAYER_HIDE; + } + } + } + else { + /* hide selected/active */ + layer->flag |= GP_LAYER_HIDE; + } - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_hide(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Hide Layer(s)"; - ot->idname = "GPENCIL_OT_hide"; - ot->description = "Hide selected/unselected Grease Pencil layers"; + /* identifiers */ + ot->name = "Hide Layer(s)"; + ot->idname = "GPENCIL_OT_hide"; + ot->description = "Hide selected/unselected Grease Pencil layers"; - /* callbacks */ - ot->exec = gp_hide_exec; - ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */ + /* callbacks */ + ot->exec = gp_hide_exec; + ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */ - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers"); + /* props */ + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers"); } /* ********************** Show All Layers ***************************** */ @@ -851,368 +866,375 @@ void GPENCIL_OT_hide(wmOperatorType *ot) /* poll callback for showing layers */ static bool gp_reveal_poll(bContext *C) { - return ED_gpencil_data_get_active(C) != NULL; + return ED_gpencil_data_get_active(C) != NULL; } static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select) { - bGPDstroke *gps; - for (gps = frame->strokes.first; gps; gps = gps->next) { + bGPDstroke *gps; + for (gps = frame->strokes.first; gps; gps = gps->next) { - /* only deselect strokes that are valid in this view */ - if (ED_gpencil_stroke_can_use(C, gps)) { + /* only deselect strokes that are valid in this view */ + if (ED_gpencil_stroke_can_use(C, gps)) { - /* (de)select points */ - int i; - bGPDspoint *pt; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - SET_FLAG_FROM_TEST(pt->flag, select, GP_SPOINT_SELECT); - } + /* (de)select points */ + int i; + bGPDspoint *pt; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + SET_FLAG_FROM_TEST(pt->flag, select, GP_SPOINT_SELECT); + } - /* (de)select stroke */ - SET_FLAG_FROM_TEST(gps->flag, select, GP_STROKE_SELECT); - } - } + /* (de)select stroke */ + SET_FLAG_FROM_TEST(gps->flag, select, GP_STROKE_SELECT); + } + } } static int gp_reveal_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; - const bool select = RNA_boolean_get(op->ptr, "select"); - - /* sanity checks */ - if (gpd == NULL) - return OPERATOR_CANCELLED; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - - if (gpl->flag & GP_LAYER_HIDE) { - gpl->flag &= ~GP_LAYER_HIDE; - - /* select or deselect if requested, only on hidden layers */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (select) { - /* select all strokes on active frame only (same as select all operator) */ - if (gpl->actframe) { - gp_reveal_select_frame(C, gpl->actframe, true); - } - } - else { - /* deselect strokes on all frames (same as deselect all operator) */ - bGPDframe *gpf; - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - gp_reveal_select_frame(C, gpf, false); - } - } - } - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; + const bool select = RNA_boolean_get(op->ptr, "select"); + + /* sanity checks */ + if (gpd == NULL) + return OPERATOR_CANCELLED; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + + if (gpl->flag & GP_LAYER_HIDE) { + gpl->flag &= ~GP_LAYER_HIDE; + + /* select or deselect if requested, only on hidden layers */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + if (select) { + /* select all strokes on active frame only (same as select all operator) */ + if (gpl->actframe) { + gp_reveal_select_frame(C, gpl->actframe, true); + } + } + else { + /* deselect strokes on all frames (same as deselect all operator) */ + bGPDframe *gpf; + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + gp_reveal_select_frame(C, gpf, false); + } + } + } + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_reveal(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Show All Layers"; - ot->idname = "GPENCIL_OT_reveal"; - ot->description = "Show all Grease Pencil layers"; + /* identifiers */ + ot->name = "Show All Layers"; + ot->idname = "GPENCIL_OT_reveal"; + ot->description = "Show all Grease Pencil layers"; - /* callbacks */ - ot->exec = gp_reveal_exec; - ot->poll = gp_reveal_poll; + /* callbacks */ + ot->exec = gp_reveal_exec; + ot->poll = gp_reveal_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + /* props */ + RNA_def_boolean(ot->srna, "select", true, "Select", ""); } /* ***************** Lock/Unlock All Layers ************************ */ static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; - /* sanity checks */ - if (gpd == NULL) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (gpd == NULL) + return OPERATOR_CANCELLED; - /* make all layers non-editable */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - gpl->flag |= GP_LAYER_LOCKED; - } + /* make all layers non-editable */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->flag |= GP_LAYER_LOCKED; + } - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_lock_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Lock All Layers"; - ot->idname = "GPENCIL_OT_lock_all"; - ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified"; + /* identifiers */ + ot->name = "Lock All Layers"; + ot->idname = "GPENCIL_OT_lock_all"; + ot->description = + "Lock all Grease Pencil layers to prevent them from being accidentally modified"; - /* callbacks */ - ot->exec = gp_lock_all_exec; - ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ + /* callbacks */ + ot->exec = gp_lock_all_exec; + ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* -------------------------- */ static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; - /* sanity checks */ - if (gpd == NULL) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (gpd == NULL) + return OPERATOR_CANCELLED; - /* make all layers editable again */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - gpl->flag &= ~GP_LAYER_LOCKED; - } + /* make all layers editable again */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->flag &= ~GP_LAYER_LOCKED; + } - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_unlock_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Unlock All Layers"; - ot->idname = "GPENCIL_OT_unlock_all"; - ot->description = "Unlock all Grease Pencil layers so that they can be edited"; + /* identifiers */ + ot->name = "Unlock All Layers"; + ot->idname = "GPENCIL_OT_unlock_all"; + ot->description = "Unlock all Grease Pencil layers so that they can be edited"; - /* callbacks */ - ot->exec = gp_unlock_all_exec; - ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ + /* callbacks */ + ot->exec = gp_unlock_all_exec; + ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************** Isolate Layer **************************** */ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); - bGPDlayer *gpl; - int flags = GP_LAYER_LOCKED; - bool isolate = false; - - if (RNA_boolean_get(op->ptr, "affect_visibility")) - flags |= GP_LAYER_HIDE; - - if (ELEM(NULL, gpd, layer)) { - BKE_report(op->reports, RPT_ERROR, "No active layer to isolate"); - return OPERATOR_CANCELLED; - } - - /* Test whether to isolate or clear all flags */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* Skip if this is the active layer */ - if (gpl == layer) - continue; - - /* If the flags aren't set, that means that the layer is - * not alone, so we have some layers to isolate still - */ - if ((gpl->flag & flags) == 0) { - isolate = true; - break; - } - } - - /* Set/Clear flags as appropriate */ - /* TODO: Include onionskinning on this list? */ - if (isolate) { - /* Set flags on all "other" layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl == layer) - continue; - else - gpl->flag |= flags; - } - } - else { - /* Clear flags - Restore everything else */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - gpl->flag &= ~flags; - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl; + int flags = GP_LAYER_LOCKED; + bool isolate = false; + + if (RNA_boolean_get(op->ptr, "affect_visibility")) + flags |= GP_LAYER_HIDE; + + if (ELEM(NULL, gpd, layer)) { + BKE_report(op->reports, RPT_ERROR, "No active layer to isolate"); + return OPERATOR_CANCELLED; + } + + /* Test whether to isolate or clear all flags */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* Skip if this is the active layer */ + if (gpl == layer) + continue; + + /* If the flags aren't set, that means that the layer is + * not alone, so we have some layers to isolate still + */ + if ((gpl->flag & flags) == 0) { + isolate = true; + break; + } + } + + /* Set/Clear flags as appropriate */ + /* TODO: Include onionskinning on this list? */ + if (isolate) { + /* Set flags on all "other" layers */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl == layer) + continue; + else + gpl->flag |= flags; + } + } + else { + /* Clear flags - Restore everything else */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->flag &= ~flags; + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_isolate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Isolate Layer"; - ot->idname = "GPENCIL_OT_layer_isolate"; - ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible"; + /* identifiers */ + ot->name = "Isolate Layer"; + ot->idname = "GPENCIL_OT_layer_isolate"; + ot->description = + "Toggle whether the active layer is the only one that can be edited and/or visible"; - /* callbacks */ - ot->exec = gp_isolate_layer_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_isolate_layer_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", - "In addition to toggling the editability, also affect the visibility"); + /* properties */ + RNA_def_boolean(ot->srna, + "affect_visibility", + false, + "Affect Visibility", + "In addition to toggling the editability, also affect the visibility"); } /* ********************** Merge Layer with the next layer **************************** */ static int gp_merge_layer_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl_next = BKE_gpencil_layer_getactive(gpd); - bGPDlayer *gpl_current = gpl_next->prev; - - if (ELEM(NULL, gpd, gpl_current, gpl_next)) { - BKE_report(op->reports, RPT_ERROR, "No layers to merge"); - return OPERATOR_CANCELLED; - } - - /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ - GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); - for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { - BLI_ghash_insert(gh_frames_cur, POINTER_FROM_INT(gpf->framenum), gpf); - } - - /* read all frames from next layer and add any missing in current layer */ - for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { - /* try to find frame in current layer */ - bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, POINTER_FROM_INT(gpf->framenum)); - if (!frame) { - bGPDframe *actframe = BKE_gpencil_layer_getframe(gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); - frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); - /* duplicate strokes of current active frame */ - if (actframe) { - BKE_gpencil_frame_copy_strokes(actframe, frame); - } - } - /* add to tail all strokes */ - BLI_movelisttolist(&frame->strokes, &gpf->strokes); - } - - /* Now delete next layer */ - BKE_gpencil_layer_delete(gpd, gpl_next); - BLI_ghash_free(gh_frames_cur, NULL, NULL); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl_next = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_current = gpl_next->prev; + + if (ELEM(NULL, gpd, gpl_current, gpl_next)) { + BKE_report(op->reports, RPT_ERROR, "No layers to merge"); + return OPERATOR_CANCELLED; + } + + /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ + GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); + for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { + BLI_ghash_insert(gh_frames_cur, POINTER_FROM_INT(gpf->framenum), gpf); + } + + /* read all frames from next layer and add any missing in current layer */ + for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { + /* try to find frame in current layer */ + bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, POINTER_FROM_INT(gpf->framenum)); + if (!frame) { + bGPDframe *actframe = BKE_gpencil_layer_getframe( + gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); + frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); + /* duplicate strokes of current active frame */ + if (actframe) { + BKE_gpencil_frame_copy_strokes(actframe, frame); + } + } + /* add to tail all strokes */ + BLI_movelisttolist(&frame->strokes, &gpf->strokes); + } + + /* Now delete next layer */ + BKE_gpencil_layer_delete(gpd, gpl_next); + BLI_ghash_free(gh_frames_cur, NULL, NULL); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_merge(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Merge Down"; - ot->idname = "GPENCIL_OT_layer_merge"; - ot->description = "Merge the current layer with the layer below"; + /* identifiers */ + ot->name = "Merge Down"; + ot->idname = "GPENCIL_OT_layer_merge"; + ot->description = "Merge the current layer with the layer below"; - /* callbacks */ - ot->exec = gp_merge_layer_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_merge_layer_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************** Change Layer ***************************** */ static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) { - uiPopupMenu *pup; - uiLayout *layout; + uiPopupMenu *pup; + uiLayout *layout; - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer"); - UI_popup_menu_end(C, pup); + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer"); + UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; + return OPERATOR_INTERFACE; } static int gp_layer_change_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = NULL; - int layer_num = RNA_enum_get(op->ptr, "layer"); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = NULL; + int layer_num = RNA_enum_get(op->ptr, "layer"); - /* Get layer or create new one */ - if (layer_num == -1) { - /* Create layer */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); - } - else { - /* Try to get layer */ - gpl = BLI_findlink(&gpd->layers, layer_num); + /* Get layer or create new one */ + if (layer_num == -1) { + /* Create layer */ + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + } + else { + /* Try to get layer */ + gpl = BLI_findlink(&gpd->layers, layer_num); - if (gpl == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num); - return OPERATOR_CANCELLED; - } - } + if (gpl == NULL) { + BKE_reportf( + op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num); + return OPERATOR_CANCELLED; + } + } - /* Set active layer */ - BKE_gpencil_layer_setactive(gpd, gpl); + /* Set active layer */ + BKE_gpencil_layer_setactive(gpd, gpl); - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_layer_change(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Change Layer"; - ot->idname = "GPENCIL_OT_layer_change"; - ot->description = "Change active Grease Pencil layer"; + /* identifiers */ + ot->name = "Change Layer"; + ot->idname = "GPENCIL_OT_layer_change"; + ot->description = "Change active Grease Pencil layer"; - /* callbacks */ - ot->invoke = gp_layer_change_invoke; - ot->exec = gp_layer_change_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->invoke = gp_layer_change_invoke; + ot->exec = gp_layer_change_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* gp layer to use (dynamic enum) */ - ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", ""); - RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); + /* gp layer to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); } /* ************************************************ */ @@ -1220,309 +1242,305 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot) /* ******************* Arrange Stroke Up/Down in drawing order ************************** */ enum { - GP_STROKE_MOVE_UP = -1, - GP_STROKE_MOVE_DOWN = 1, - GP_STROKE_MOVE_TOP = 2, - GP_STROKE_MOVE_BOTTOM = 3, + GP_STROKE_MOVE_UP = -1, + GP_STROKE_MOVE_DOWN = 1, + GP_STROKE_MOVE_TOP = 2, + GP_STROKE_MOVE_BOTTOM = 3, }; static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - bGPDstroke *gps; - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl, gpl->actframe)) { - return OPERATOR_CANCELLED; - } - - const int direction = RNA_enum_get(op->ptr, "direction"); - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* temp listbase to store selected strokes by layer */ - ListBase selected = { NULL }; - bGPDframe *gpf = gpl->actframe; - if (gpl->flag & GP_LAYER_LOCKED) { - continue; - } - - if (gpf == NULL) { - continue; - } - bool gpf_lock = false; - /* verify if any selected stroke is in the extreme of the stack and select to move */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - /* some stroke is already at front*/ - if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { - if (gps == gpf->strokes.last) { - gpf_lock = true; - continue; - } - } - /* some stroke is already at botom */ - if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { - if (gps == gpf->strokes.first) { - gpf_lock = true; - continue; - } - } - /* add to list (if not locked) */ - if (!gpf_lock) { - BLI_addtail(&selected, BLI_genericNodeN(gps)); - } - } - } - /* Now do the movement of the stroke */ - if (!gpf_lock) { - switch (direction) { - /* Bring to Front */ - case GP_STROKE_MOVE_TOP: - for (LinkData *link = selected.first; link; link = link->next) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&gpf->strokes, gps); - } - break; - /* Bring Forward */ - case GP_STROKE_MOVE_UP: - for (LinkData *link = selected.last; link; link = link->prev) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, 1); - } - break; - /* Send Backward */ - case GP_STROKE_MOVE_DOWN: - for (LinkData *link = selected.first; link; link = link->next) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, -1); - } - break; - /* Send to Back */ - case GP_STROKE_MOVE_BOTTOM: - for (LinkData *link = selected.last; link; link = link->prev) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); - BLI_addhead(&gpf->strokes, gps); - } - break; - default: - BLI_assert(0); - break; - } - } - BLI_freelistN(&selected); - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps; + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "direction"); + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* temp listbase to store selected strokes by layer */ + ListBase selected = {NULL}; + bGPDframe *gpf = gpl->actframe; + if (gpl->flag & GP_LAYER_LOCKED) { + continue; + } + + if (gpf == NULL) { + continue; + } + bool gpf_lock = false; + /* verify if any selected stroke is in the extreme of the stack and select to move */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + /* some stroke is already at front*/ + if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { + if (gps == gpf->strokes.last) { + gpf_lock = true; + continue; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + gpf_lock = true; + continue; + } + } + /* add to list (if not locked) */ + if (!gpf_lock) { + BLI_addtail(&selected, BLI_genericNodeN(gps)); + } + } + } + /* Now do the movement of the stroke */ + if (!gpf_lock) { + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_addtail(&gpf->strokes, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, 1); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, -1); + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_addhead(&gpf->strokes, gps); + } + break; + default: + BLI_assert(0); + break; + } + } + BLI_freelistN(&selected); + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) { - static const EnumPropertyItem slot_move[] = { - {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, - {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, - {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, - {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, - {0, NULL, 0, NULL, NULL } - }; + static const EnumPropertyItem slot_move[] = { + {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, + {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, + {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, + {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, + {0, NULL, 0, NULL, NULL}}; - /* identifiers */ - ot->name = "Arrange Stroke"; - ot->idname = "GPENCIL_OT_stroke_arrange"; - ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; + /* identifiers */ + ot->name = "Arrange Stroke"; + ot->idname = "GPENCIL_OT_stroke_arrange"; + ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; - /* callbacks */ - ot->exec = gp_stroke_arrange_exec; - ot->poll = gp_active_layer_poll; + /* callbacks */ + ot->exec = gp_stroke_arrange_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); } /* ******************* Move Stroke to new color ************************** */ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Material *ma = NULL; - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "material", name); - - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - if (name[0] == '\0') { - ma = give_current_material(ob, ob->actcol); - } - else { - ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); - if (ma == NULL) { - return OPERATOR_CANCELLED; - } - } - /* try to find slot */ - int idx = BKE_gpencil_object_material_get_index(ob, ma); - if (idx < 0) { - return OPERATOR_CANCELLED; - } - - /* sanity checks */ - if (ELEM(NULL, gpd)) { - return OPERATOR_CANCELLED; - } - - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (ELEM(NULL, ma)) { - return OPERATOR_CANCELLED; - } - - /* loop all strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) - continue; - - /* assign new color */ - gps->mat_nr = idx; - } - } - } - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Material *ma = NULL; + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "material", name); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + if (name[0] == '\0') { + ma = give_current_material(ob, ob->actcol); + } + else { + ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); + if (ma == NULL) { + return OPERATOR_CANCELLED; + } + } + /* try to find slot */ + int idx = BKE_gpencil_object_material_get_index(ob, ma); + if (idx < 0) { + return OPERATOR_CANCELLED; + } + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + if (ELEM(NULL, ma)) { + return OPERATOR_CANCELLED; + } + + /* loop all strokes */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) + continue; + + /* assign new color */ + gps->mat_nr = idx; + } + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Change Stroke Color"; - ot->idname = "GPENCIL_OT_stroke_change_color"; - ot->description = "Move selected strokes to active material"; - - /* callbacks */ - ot->exec = gp_stroke_change_color_exec; - ot->poll = gp_active_layer_poll; + /* identifiers */ + ot->name = "Change Stroke Color"; + ot->idname = "GPENCIL_OT_stroke_change_color"; + ot->description = "Move selected strokes to active material"; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* callbacks */ + ot->exec = gp_stroke_change_color_exec; + ot->poll = gp_active_layer_poll; - RNA_def_string(ot->srna, "material", NULL, MAX_ID_NAME - 2, "Material", "Name of the material"); + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_string(ot->srna, "material", NULL, MAX_ID_NAME - 2, "Material", "Name of the material"); } /* ******************* Lock color of non selected Strokes colors ************************** */ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - short *totcol = give_totcolp(ob); + short *totcol = give_totcolp(ob); - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; - /* first lock all colors */ - for (short i = 0; i < *totcol; i++) { - Material *tmp_ma = give_current_material(ob, i + 1); - tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; - DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); - } + /* first lock all colors */ + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = give_current_material(ob, i + 1); + tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; + DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); + } - /* loop all selected strokes and unlock any color */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* unlock color */ - Material *tmp_ma = give_current_material(ob, gps->mat_nr + 1); + /* loop all selected strokes and unlock any color */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* unlock color */ + Material *tmp_ma = give_current_material(ob, gps->mat_nr + 1); - tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; - DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); - } - } - } - } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + } + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Lock Unused Colors"; - ot->idname = "GPENCIL_OT_stroke_lock_color"; - ot->description = "Lock any color not used in any selected stroke"; + /* identifiers */ + ot->name = "Lock Unused Colors"; + ot->idname = "GPENCIL_OT_stroke_lock_color"; + ot->description = "Lock any color not used in any selected stroke"; - /* api callbacks */ - ot->exec = gp_stroke_lock_color_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gp_stroke_lock_color_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ************************************************ */ @@ -1531,1111 +1549,1111 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) /* ******************* Brush create presets ************************** */ static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) { - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(C); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Create Preset Brushes"; - ot->idname = "GPENCIL_OT_brush_presets_create"; - ot->description = "Create a set of predefined Grease Pencil drawing brushes"; + /* identifiers */ + ot->name = "Create Preset Brushes"; + ot->idname = "GPENCIL_OT_brush_presets_create"; + ot->description = "Create a set of predefined Grease Pencil drawing brushes"; - /* api callbacks */ - ot->exec = gp_brush_presets_create_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* api callbacks */ + ot->exec = gp_brush_presets_create_exec; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /*********************** Vertex Groups ***********************************/ static bool gpencil_vertex_group_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { - if (ELEM(ob->mode, - OB_MODE_EDIT_GPENCIL, - OB_MODE_SCULPT_GPENCIL)) - { - return true; - } - } - } + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ELEM(ob->mode, OB_MODE_EDIT_GPENCIL, OB_MODE_SCULPT_GPENCIL)) { + return true; + } + } + } - return false; + return false; } static bool gpencil_vertex_group_weight_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { - if (ob->mode == OB_MODE_WEIGHT_GPENCIL) { - return true; - } - } - } + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ob->mode == OB_MODE_WEIGHT_GPENCIL) { + return true; + } + } + } - return false; + return false; } static int gpencil_vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) { - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); - /* sanity checks */ - if (ELEM(NULL, ts, ob, ob->data)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) + return OPERATOR_CANCELLED; - ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); + ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_assign(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Assign to Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_assign"; - ot->description = "Assign the selected vertices to the active vertex group"; + /* identifiers */ + ot->name = "Assign to Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_assign"; + ot->description = "Assign the selected vertices to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_poll; - ot->exec = gpencil_vertex_group_assign_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_assign_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* remove point from vertex group */ static int gpencil_vertex_group_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - /* sanity checks */ - if (ELEM(NULL, ob, ob->data)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; - ED_gpencil_vgroup_remove(C, ob); + ED_gpencil_vgroup_remove(C, ob); - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_remove_from(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove from Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_remove_from"; - ot->description = "Remove the selected vertices from active or all vertex group(s)"; - - /* api callbacks */ - ot->poll = gpencil_vertex_group_poll; - ot->exec = gpencil_vertex_group_remove_from_exec; + /* identifiers */ + ot->name = "Remove from Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_remove_from"; + ot->description = "Remove the selected vertices from active or all vertex group(s)"; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_remove_from_exec; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int gpencil_vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - /* sanity checks */ - if (ELEM(NULL, ob, ob->data)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; - ED_gpencil_vgroup_select(C, ob); + ED_gpencil_vgroup_select(C, ob); - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_select(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_select"; - ot->description = "Select all the vertices assigned to the active vertex group"; + /* identifiers */ + ot->name = "Select Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_select"; + ot->description = "Select all the vertices assigned to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_poll; - ot->exec = gpencil_vertex_group_select_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_select_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int gpencil_vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - /* sanity checks */ - if (ELEM(NULL, ob, ob->data)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; - ED_gpencil_vgroup_deselect(C, ob); + ED_gpencil_vgroup_deselect(C, ob); - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_deselect(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Deselect Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_deselect"; - ot->description = "Deselect all selected vertices assigned to the active vertex group"; + /* identifiers */ + ot->name = "Deselect Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_deselect"; + ot->description = "Deselect all selected vertices assigned to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_poll; - ot->exec = gpencil_vertex_group_deselect_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_deselect_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* invert */ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) { - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); - - /* sanity checks */ - if (ELEM(NULL, ts, ob, ob->data)) { - return OPERATOR_CANCELLED; - } - - MDeformVert *dvert; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); - if (defgroup == NULL) { - return OPERATOR_CANCELLED; - } - if (defgroup->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - for (int i = 0; i < gps->totpoints; i++) { - dvert = &gps->dvert[i]; - MDeformWeight *dw = defvert_find_index(dvert, def_nr); - if (dw == NULL) { - defvert_add_index_notest(dvert, def_nr, 1.0f); - } - else if (dw->weight == 1.0f) { - defvert_remove_group(dvert, dw); - } - else { - dw->weight = 1.0f - dw->weight; - } - } - } - CTX_DATA_END; - - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - - return OPERATOR_FINISHED; + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) { + return OPERATOR_CANCELLED; + } + + MDeformVert *dvert; + const int def_nr = ob->actdef - 1; + bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + if (defgroup == NULL) { + return OPERATOR_CANCELLED; + } + if (defgroup->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + MDeformWeight *dw = defvert_find_index(dvert, def_nr); + if (dw == NULL) { + defvert_add_index_notest(dvert, def_nr, 1.0f); + } + else if (dw->weight == 1.0f) { + defvert_remove_group(dvert, dw); + } + else { + dw->weight = 1.0f - dw->weight; + } + } + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_invert(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Invert Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_invert"; - ot->description = "Invert weights to the active vertex group"; + /* identifiers */ + ot->name = "Invert Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_invert"; + ot->description = "Invert weights to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_weight_poll; - ot->exec = gpencil_vertex_group_invert_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_invert_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* smooth */ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) { - const float fac = RNA_float_get(op->ptr, "factor"); - const int repeat = RNA_int_get(op->ptr, "repeat"); - - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); - - /* sanity checks */ - if (ELEM(NULL, ts, ob, ob->data)) { - return OPERATOR_CANCELLED; - } - - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); - if (defgroup == NULL) { - return OPERATOR_CANCELLED; - } - if (defgroup->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); - return OPERATOR_CANCELLED; - } - - bGPDspoint *pta, *ptb, *ptc; - MDeformVert *dverta, *dvertb; - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->dvert == NULL) { - continue; - } - - for (int s = 0; s < repeat; s++) { - for (int i = 0; i < gps->totpoints; i++) { - /* previous point */ - if (i > 0) { - pta = &gps->points[i - 1]; - dverta = &gps->dvert[i - 1]; - } - else { - pta = &gps->points[i]; - dverta = &gps->dvert[i]; - } - /* current */ - ptb = &gps->points[i]; - dvertb = &gps->dvert[i]; - /* next point */ - if (i + 1 < gps->totpoints) { - ptc = &gps->points[i + 1]; - } - else { - ptc = &gps->points[i]; - } - - float wa = defvert_find_weight(dverta, def_nr); - float wb = defvert_find_weight(dvertb, def_nr); - - /* the optimal value is the corresponding to the interpolation of the weight - * at the distance of point b - */ - const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); - const float optimal = interpf(wa, wb, opfac); - /* Based on influence factor, blend between original and optimal */ - MDeformWeight *dw = defvert_verify_index(dvertb, def_nr); - if (dw) { - dw->weight = interpf(wb, optimal, fac); - CLAMP(dw->weight, 0.0, 1.0f); - } - } - } - } - CTX_DATA_END; - - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - - return OPERATOR_FINISHED; + const float fac = RNA_float_get(op->ptr, "factor"); + const int repeat = RNA_int_get(op->ptr, "repeat"); + + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) { + return OPERATOR_CANCELLED; + } + + const int def_nr = ob->actdef - 1; + bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + if (defgroup == NULL) { + return OPERATOR_CANCELLED; + } + if (defgroup->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); + return OPERATOR_CANCELLED; + } + + bGPDspoint *pta, *ptb, *ptc; + MDeformVert *dverta, *dvertb; + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->dvert == NULL) { + continue; + } + + for (int s = 0; s < repeat; s++) { + for (int i = 0; i < gps->totpoints; i++) { + /* previous point */ + if (i > 0) { + pta = &gps->points[i - 1]; + dverta = &gps->dvert[i - 1]; + } + else { + pta = &gps->points[i]; + dverta = &gps->dvert[i]; + } + /* current */ + ptb = &gps->points[i]; + dvertb = &gps->dvert[i]; + /* next point */ + if (i + 1 < gps->totpoints) { + ptc = &gps->points[i + 1]; + } + else { + ptc = &gps->points[i]; + } + + float wa = defvert_find_weight(dverta, def_nr); + float wb = defvert_find_weight(dvertb, def_nr); + + /* the optimal value is the corresponding to the interpolation of the weight + * at the distance of point b + */ + const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = interpf(wa, wb, opfac); + /* Based on influence factor, blend between original and optimal */ + MDeformWeight *dw = defvert_verify_index(dvertb, def_nr); + if (dw) { + dw->weight = interpf(wb, optimal, fac); + CLAMP(dw->weight, 0.0, 1.0f); + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_smooth(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Smooth Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_smooth"; - ot->description = "Smooth weights to the active vertex group"; + /* identifiers */ + ot->name = "Smooth Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_smooth"; + ot->description = "Smooth weights to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_weight_poll; - ot->exec = gpencil_vertex_group_smooth_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_smooth_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); - RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); } /* normalize */ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) { - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); - - /* sanity checks */ - if (ELEM(NULL, ts, ob, ob->data)) { - return OPERATOR_CANCELLED; - } - - MDeformVert *dvert = NULL; - MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); - if (defgroup == NULL) { - return OPERATOR_CANCELLED; - } - if (defgroup->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* look for max value */ - float maxvalue = 0.0f; - for (int i = 0; i < gps->totpoints; i++) { - dvert = &gps->dvert[i]; - dw = defvert_find_index(dvert, def_nr); - if ((dw != NULL) && (dw->weight > maxvalue)) { - maxvalue = dw->weight; - } - } - - /* normalize weights */ - if (maxvalue > 0.0f) { - for (int i = 0; i < gps->totpoints; i++) { - dvert = &gps->dvert[i]; - dw = defvert_find_index(dvert, def_nr); - if (dw != NULL) { - dw->weight = dw->weight / maxvalue; - } - } - } - } - CTX_DATA_END; - - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - - return OPERATOR_FINISHED; + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) { + return OPERATOR_CANCELLED; + } + + MDeformVert *dvert = NULL; + MDeformWeight *dw = NULL; + const int def_nr = ob->actdef - 1; + bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + if (defgroup == NULL) { + return OPERATOR_CANCELLED; + } + if (defgroup->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_ERROR, "Current Vertex Group is locked"); + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* look for max value */ + float maxvalue = 0.0f; + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + dw = defvert_find_index(dvert, def_nr); + if ((dw != NULL) && (dw->weight > maxvalue)) { + maxvalue = dw->weight; + } + } + + /* normalize weights */ + if (maxvalue > 0.0f) { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + dw = defvert_find_index(dvert, def_nr); + if (dw != NULL) { + dw->weight = dw->weight / maxvalue; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_normalize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Normalize Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_normalize"; - ot->description = "Normalize weights to the active vertex group"; + /* identifiers */ + ot->name = "Normalize Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_normalize"; + ot->description = "Normalize weights to the active vertex group"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_weight_poll; - ot->exec = gpencil_vertex_group_normalize_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_normalize_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* normalize all */ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) { - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); - bool lock_active = RNA_boolean_get(op->ptr, "lock_active"); - - /* sanity checks */ - if (ELEM(NULL, ts, ob, ob->data)) { - return OPERATOR_CANCELLED; - } - - bDeformGroup *defgroup = NULL; - MDeformVert *dvert = NULL; - MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - const int defbase_tot = BLI_listbase_count(&ob->defbase); - if (defbase_tot == 0) { - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* verify the strokes has something to change */ - if (gps->totpoints == 0) { - continue; - } - /* look for tot value */ - float *tot_values = MEM_callocN(gps->totpoints * sizeof(float), __func__); - - for (int i = 0; i < gps->totpoints; i++) { - dvert = &gps->dvert[i]; - for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); - /* skip NULL or locked groups */ - if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { - continue; - } - - /* skip current */ - if ((lock_active) && (v == def_nr)) { - continue; - } - - dw = defvert_find_index(dvert, v); - if (dw != NULL) { - tot_values[i] += dw->weight; - } - } - } - - /* normalize weights */ - for (int i = 0; i < gps->totpoints; i++) { - if (tot_values[i] == 0.0f) { - continue; - } - - dvert = &gps->dvert[i]; - for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); - /* skip NULL or locked groups */ - if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { - continue; - } - - /* skip current */ - if ((lock_active) && (v == def_nr)) { - continue; - } - - dw = defvert_find_index(dvert, v); - if (dw != NULL) { - dw->weight = dw->weight / tot_values[i]; - } - } - } - - /* free temp array */ - MEM_SAFE_FREE(tot_values); - } - CTX_DATA_END; - - /* notifiers */ - bGPdata *gpd = ob->data; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); - - return OPERATOR_FINISHED; + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + bool lock_active = RNA_boolean_get(op->ptr, "lock_active"); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) { + return OPERATOR_CANCELLED; + } + + bDeformGroup *defgroup = NULL; + MDeformVert *dvert = NULL; + MDeformWeight *dw = NULL; + const int def_nr = ob->actdef - 1; + const int defbase_tot = BLI_listbase_count(&ob->defbase); + if (defbase_tot == 0) { + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* verify the strokes has something to change */ + if (gps->totpoints == 0) { + continue; + } + /* look for tot value */ + float *tot_values = MEM_callocN(gps->totpoints * sizeof(float), __func__); + + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + for (int v = 0; v < defbase_tot; v++) { + defgroup = BLI_findlink(&ob->defbase, v); + /* skip NULL or locked groups */ + if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { + continue; + } + + /* skip current */ + if ((lock_active) && (v == def_nr)) { + continue; + } + + dw = defvert_find_index(dvert, v); + if (dw != NULL) { + tot_values[i] += dw->weight; + } + } + } + + /* normalize weights */ + for (int i = 0; i < gps->totpoints; i++) { + if (tot_values[i] == 0.0f) { + continue; + } + + dvert = &gps->dvert[i]; + for (int v = 0; v < defbase_tot; v++) { + defgroup = BLI_findlink(&ob->defbase, v); + /* skip NULL or locked groups */ + if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { + continue; + } + + /* skip current */ + if ((lock_active) && (v == def_nr)) { + continue; + } + + dw = defvert_find_index(dvert, v); + if (dw != NULL) { + dw->weight = dw->weight / tot_values[i]; + } + } + } + + /* free temp array */ + MEM_SAFE_FREE(tot_values); + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_vertex_group_normalize_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Normalize All Vertex Group"; - ot->idname = "GPENCIL_OT_vertex_group_normalize_all"; - ot->description = "Normalize all weights of all vertex groups, " - "so that for each vertex, the sum of all weights is 1.0"; + /* identifiers */ + ot->name = "Normalize All Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_normalize_all"; + ot->description = + "Normalize all weights of all vertex groups, " + "so that for each vertex, the sum of all weights is 1.0"; - /* api callbacks */ - ot->poll = gpencil_vertex_group_weight_poll; - ot->exec = gpencil_vertex_group_normalize_all_exec; + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_normalize_all_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "lock_active", true, "Lock Active", - "Keep the values of the active group while normalizing others"); + /* props */ + RNA_def_boolean(ot->srna, + "lock_active", + true, + "Lock Active", + "Keep the values of the active group while normalizing others"); } /****************************** Join ***********************************/ /* userdata for joined_gpencil_fix_animdata_cb() */ typedef struct tJoinGPencil_AdtFixData { - bGPdata *src_gpd; - bGPdata *tar_gpd; + bGPdata *src_gpd; + bGPdata *tar_gpd; - GHash *names_map; + GHash *names_map; } tJoinGPencil_AdtFixData; /* Callback to pass to BKE_fcurves_main_cb() for RNA Paths attached to each F-Curve used in the AnimData */ static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) { - tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; - ID *src_id = &afd->src_gpd->id; - ID *dst_id = &afd->tar_gpd->id; - - GHashIterator gh_iter; - - /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ - if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { - GHASH_ITER(gh_iter, afd->names_map) { - const char *old_name = BLI_ghashIterator_getKey(&gh_iter); - const char *new_name = BLI_ghashIterator_getValue(&gh_iter); - - /* only remap if changed; - * this still means there will be some waste if there aren't many drivers/keys */ - if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { - fcu->rna_path = BKE_animsys_fix_rna_path_rename( - id, fcu->rna_path, "layers", - old_name, new_name, 0, 0, false); - - /* we don't want to apply a second remapping on this F-Curve now, - * so stop trying to fix names names - */ - break; - } - } - } - - /* Fix driver targets */ - if (fcu->driver) { - /* Fix driver references to invalid ID's */ - for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { - /* only change the used targets, since the others will need fixing manually anyway */ - DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) - { - /* change the ID's used... */ - if (dtar->id == src_id) { - dtar->id = dst_id; - - /* also check on the subtarget... - * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own - * little twists so that we know that it isn't going to clobber the wrong data - */ - if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { - GHASH_ITER(gh_iter, afd->names_map) { - const char *old_name = BLI_ghashIterator_getKey(&gh_iter); - const char *new_name = BLI_ghashIterator_getValue(&gh_iter); - - /* only remap if changed */ - if (!STREQ(old_name, new_name)) { - if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { - /* Fix up path */ - dtar->rna_path = BKE_animsys_fix_rna_path_rename( - id, dtar->rna_path, "layers", - old_name, new_name, 0, 0, false); - break; /* no need to try any more names for layer path */ - } - } - } - } - } - } - DRIVER_TARGETS_LOOPER_END; - } - } + tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; + ID *src_id = &afd->src_gpd->id; + ID *dst_id = &afd->tar_gpd->id; + + GHashIterator gh_iter; + + /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ + if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { + GHASH_ITER (gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed; + * this still means there will be some waste if there aren't many drivers/keys */ + if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { + fcu->rna_path = BKE_animsys_fix_rna_path_rename( + id, fcu->rna_path, "layers", old_name, new_name, 0, 0, false); + + /* we don't want to apply a second remapping on this F-Curve now, + * so stop trying to fix names names + */ + break; + } + } + } + + /* Fix driver targets */ + if (fcu->driver) { + /* Fix driver references to invalid ID's */ + for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { + /* only change the used targets, since the others will need fixing manually anyway */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + /* change the ID's used... */ + if (dtar->id == src_id) { + dtar->id = dst_id; + + /* also check on the subtarget... + * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data + */ + if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { + GHASH_ITER (gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed */ + if (!STREQ(old_name, new_name)) { + if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { + /* Fix up path */ + dtar->rna_path = BKE_animsys_fix_rna_path_rename( + id, dtar->rna_path, "layers", old_name, new_name, 0, 0, false); + break; /* no need to try any more names for layer path */ + } + } + } + } + } + } + DRIVER_TARGETS_LOOPER_END; + } + } } /* join objects called from OBJECT_OT_join */ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob_active = CTX_data_active_object(C); - bGPdata *gpd_dst = NULL; - bool ok = false; - - /* Ensure we're in right mode and that the active object is correct */ - if (!ob_active || ob_active->type != OB_GPENCIL) - return OPERATOR_CANCELLED; - - bGPdata *gpd = (bGPdata *)ob_active->data; - if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - /* Ensure all rotations are applied before */ - // XXX: Why don't we apply them here instead of warning? - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob_iter->type == OB_GPENCIL) { - if ((ob_iter->rot[0] != 0) || - (ob_iter->rot[1] != 0) || - (ob_iter->rot[2] != 0)) - { - BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); - return OPERATOR_CANCELLED; - } - } - } - CTX_DATA_END; - - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob_iter == ob_active) { - ok = true; - break; - } - } - CTX_DATA_END; - - /* that way the active object is always selected */ - if (ok == false) { - BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); - return OPERATOR_CANCELLED; - } - - gpd_dst = ob_active->data; - Object *ob_dst = ob_active; - - /* loop and join all data */ - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if ((ob_iter->type == OB_GPENCIL) && (ob_iter != ob_active)) { - /* we assume that each datablock is not already used in active object */ - if (ob_active->data != ob_iter->data) { - Object *ob_src = ob_iter; - bGPdata *gpd_src = ob_iter->data; - - /* Apply all GP modifiers before */ - for (GpencilModifierData *md = ob_iter->greasepencil_modifiers.first; md; md = md->next) { - const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); - if (mti->bakeModifier) { - mti->bakeModifier(bmain, depsgraph, md, ob_iter); - } - } - - /* copy vertex groups to the base one's */ - int old_idx = 0; - for (bDeformGroup *dg = ob_iter->defbase.first; dg; dg = dg->next) { - bDeformGroup *vgroup = MEM_dupallocN(dg); - int idx = BLI_listbase_count(&ob_active->defbase); - defgroup_unique_name(vgroup, ob_active); - BLI_addtail(&ob_active->defbase, vgroup); - /* update vertex groups in strokes in original data */ - for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { - for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - MDeformVert *dvert; - int i; - for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { - if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { - dvert->dw->def_nr = idx; - } - } - } - } - } - old_idx++; - } - if (ob_active->defbase.first && ob_active->actdef == 0) { - ob_active->actdef = 1; - } - - /* add missing materials reading source materials and checking in destination object */ - short *totcol = give_totcolp(ob_src); - - for (short i = 0; i < *totcol; i++) { - Material *tmp_ma = give_current_material(ob_src, i + 1); - BKE_gpencil_object_material_ensure(bmain, ob_dst, tmp_ma); - } - - /* duplicate bGPDlayers */ - tJoinGPencil_AdtFixData afd = {0}; - afd.src_gpd = gpd_src; - afd.tar_gpd = gpd_dst; - afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); - - float imat[3][3], bmat[3][3]; - float offset_global[3]; - float offset_local[3]; - - sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]); - copy_m3_m4(bmat, ob_active->obmat); - invert_m3_m3(imat, bmat); - mul_m3_v3(imat, offset_global); - mul_v3_m3v3(offset_local, imat, offset_global); - - - for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { - bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; - - /* recalculate all stroke points */ - ED_gpencil_parent_location(depsgraph, ob_iter, gpd_src, gpl_src, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); - - Material *ma_src = NULL; - for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - - /* reasign material. Look old material and try to find in dst */ - ma_src = give_current_material(ob_src, gps->mat_nr + 1); - gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); - - bGPDspoint *pt; - int i; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float mpt[3]; - mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); - sub_v3_v3(mpt, offset_local); - mul_v3_m4v3(&pt->x, diff_mat, mpt); - } - } - } - - /* be sure name is unique in new object */ - BLI_uniquename(&gpd_dst->layers, gpl_new, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_new->info)); - BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); - - /* add to destination datablock */ - BLI_addtail(&gpd_dst->layers, gpl_new); - } - - /* Fix all the animation data */ - BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); - BLI_ghash_free(afd.names_map, MEM_freeN, NULL); - - /* Only copy over animdata now, after all the remapping has been done, - * so that we don't have to worry about ambiguities re which datablock - * a layer came from! - */ - if (ob_iter->adt) { - if (ob_active->adt == NULL) { - /* no animdata, so just use a copy of the whole thing */ - ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0); - } - else { - /* merge in data - we'll fix the drivers manually */ - BKE_animdata_merge_copy(bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false); - } - } - - if (gpd_src->adt) { - if (gpd_dst->adt == NULL) { - /* no animdata, so just use a copy of the whole thing */ - gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, 0); - } - else { - /* merge in data - we'll fix the drivers manually */ - BKE_animdata_merge_copy(bmain, &gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); - } - } - DEG_id_tag_update(&gpd_src->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - } - - /* Free the old object */ - ED_object_base_free_and_unlink(bmain, scene, ob_iter); - } - } - CTX_DATA_END; - - DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_relations_tag_update(bmain); /* because we removed object(s) */ - - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob_active = CTX_data_active_object(C); + bGPdata *gpd_dst = NULL; + bool ok = false; + + /* Ensure we're in right mode and that the active object is correct */ + if (!ob_active || ob_active->type != OB_GPENCIL) + return OPERATOR_CANCELLED; + + bGPdata *gpd = (bGPdata *)ob_active->data; + if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* Ensure all rotations are applied before */ + // XXX: Why don't we apply them here instead of warning? + CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { + if (ob_iter->type == OB_GPENCIL) { + if ((ob_iter->rot[0] != 0) || (ob_iter->rot[1] != 0) || (ob_iter->rot[2] != 0)) { + BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); + return OPERATOR_CANCELLED; + } + } + } + CTX_DATA_END; + + CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { + if (ob_iter == ob_active) { + ok = true; + break; + } + } + CTX_DATA_END; + + /* that way the active object is always selected */ + if (ok == false) { + BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); + return OPERATOR_CANCELLED; + } + + gpd_dst = ob_active->data; + Object *ob_dst = ob_active; + + /* loop and join all data */ + CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { + if ((ob_iter->type == OB_GPENCIL) && (ob_iter != ob_active)) { + /* we assume that each datablock is not already used in active object */ + if (ob_active->data != ob_iter->data) { + Object *ob_src = ob_iter; + bGPdata *gpd_src = ob_iter->data; + + /* Apply all GP modifiers before */ + for (GpencilModifierData *md = ob_iter->greasepencil_modifiers.first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + if (mti->bakeModifier) { + mti->bakeModifier(bmain, depsgraph, md, ob_iter); + } + } + + /* copy vertex groups to the base one's */ + int old_idx = 0; + for (bDeformGroup *dg = ob_iter->defbase.first; dg; dg = dg->next) { + bDeformGroup *vgroup = MEM_dupallocN(dg); + int idx = BLI_listbase_count(&ob_active->defbase); + defgroup_unique_name(vgroup, ob_active); + BLI_addtail(&ob_active->defbase, vgroup); + /* update vertex groups in strokes in original data */ + for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { + for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + MDeformVert *dvert; + int i; + for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { + if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { + dvert->dw->def_nr = idx; + } + } + } + } + } + old_idx++; + } + if (ob_active->defbase.first && ob_active->actdef == 0) { + ob_active->actdef = 1; + } + + /* add missing materials reading source materials and checking in destination object */ + short *totcol = give_totcolp(ob_src); + + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = give_current_material(ob_src, i + 1); + BKE_gpencil_object_material_ensure(bmain, ob_dst, tmp_ma); + } + + /* duplicate bGPDlayers */ + tJoinGPencil_AdtFixData afd = {0}; + afd.src_gpd = gpd_src; + afd.tar_gpd = gpd_dst; + afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); + + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + + sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]); + copy_m3_m4(bmat, ob_active->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { + bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all stroke points */ + ED_gpencil_parent_location(depsgraph, ob_iter, gpd_src, gpl_src, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + Material *ma_src = NULL; + for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + + /* reasign material. Look old material and try to find in dst */ + ma_src = give_current_material(ob_src, gps->mat_nr + 1); + gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); + } + } + } + + /* be sure name is unique in new object */ + BLI_uniquename(&gpd_dst->layers, + gpl_new, + DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(gpl_new->info)); + BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); + + /* add to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl_new); + } + + /* Fix all the animation data */ + BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); + BLI_ghash_free(afd.names_map, MEM_freeN, NULL); + + /* Only copy over animdata now, after all the remapping has been done, + * so that we don't have to worry about ambiguities re which datablock + * a layer came from! + */ + if (ob_iter->adt) { + if (ob_active->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy( + bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (gpd_src->adt) { + if (gpd_dst->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, 0); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy( + bmain, &gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + DEG_id_tag_update(&gpd_src->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + } + + /* Free the old object */ + ED_object_base_free_and_unlink(bmain, scene, ob_iter); + } + } + CTX_DATA_END; + + DEG_id_tag_update(&gpd_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); /* because we removed object(s) */ + + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; } /* Color Handle operator */ static bool gpencil_active_color_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - if (ob && ob->data && (ob->type == OB_GPENCIL)) { - short *totcolp = give_totcolp(ob); - return *totcolp > 0; - } - return false; + Object *ob = CTX_data_active_object(C); + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + short *totcolp = give_totcolp(ob); + return *totcolp > 0; + } + return false; } - /* ******************* Lock and hide any color non used in current layer ************************** */ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - MaterialGPencilStyle *gp_style = NULL; - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* first lock and hide all colors */ - Material *ma = NULL; - short *totcol = give_totcolp(ob); - if (totcol == 0) - return OPERATOR_CANCELLED; - - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - gp_style->flag |= GP_STYLE_COLOR_LOCKED; - gp_style->flag |= GP_STYLE_COLOR_HIDE; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } - - /* loop all selected strokes and unlock any color used in active layer */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - ma = give_current_material(ob, gps->mat_nr + 1); - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - - gp_style = ma->gp_style; - /* unlock/unhide color if not unlocked before */ - if (gp_style != NULL) { - gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; - gp_style->flag &= ~GP_STYLE_COLOR_HIDE; - } - } - } - } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = NULL; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* first lock and hide all colors */ + Material *ma = NULL; + short *totcol = give_totcolp(ob); + if (totcol == 0) + return OPERATOR_CANCELLED; + + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; + gp_style->flag |= GP_STYLE_COLOR_HIDE; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } + + /* loop all selected strokes and unlock any color used in active layer */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && + (gpl->flag & GP_LAYER_ACTIVE)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + ma = give_current_material(ob, gps->mat_nr + 1); + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + + gp_style = ma->gp_style; + /* unlock/unhide color if not unlocked before */ + if (gp_style != NULL) { + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; + } + } + } + } + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_lock_layer(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Disable Unused Layer Colors"; - ot->idname = "GPENCIL_OT_lock_layer"; - ot->description = "Lock and hide any color not used in any layer"; + /* identifiers */ + ot->name = "Disable Unused Layer Colors"; + ot->idname = "GPENCIL_OT_lock_layer"; + ot->description = "Lock and hide any color not used in any layer"; - /* api callbacks */ - ot->exec = gpencil_lock_layer_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gpencil_lock_layer_exec; + ot->poll = gp_active_layer_poll; } /* ********************** Isolate gpencil_ color **************************** */ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - Material *active_ma = give_current_material(ob, ob->actcol); - MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); - MaterialGPencilStyle *gp_style; - - int flags = GP_STYLE_COLOR_LOCKED; - bool isolate = false; - - if (RNA_boolean_get(op->ptr, "affect_visibility")) - flags |= GP_STYLE_COLOR_HIDE; - - if (ELEM(NULL, gpd, active_color)) { - BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); - return OPERATOR_CANCELLED; - } - - /* Test whether to isolate or clear all flags */ - Material *ma = NULL; - short *totcol = give_totcolp(ob); - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - /* Skip if this is the active one */ - if (ma == active_ma) - continue; - - /* If the flags aren't set, that means that the color is - * not alone, so we have some colors to isolate still - */ - gp_style = ma->gp_style; - if ((gp_style->flag & flags) == 0) { - isolate = true; - break; - } - } - - /* Set/Clear flags as appropriate */ - if (isolate) { - /* Set flags on all "other" colors */ - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - if (gp_style == active_color) - continue; - else - gp_style->flag |= flags; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } - } - else { - /* Clear flags - Restore everything else */ - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - gp_style->flag &= ~flags; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + Material *active_ma = give_current_material(ob, ob->actcol); + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); + MaterialGPencilStyle *gp_style; + + int flags = GP_STYLE_COLOR_LOCKED; + bool isolate = false; + + if (RNA_boolean_get(op->ptr, "affect_visibility")) + flags |= GP_STYLE_COLOR_HIDE; + + if (ELEM(NULL, gpd, active_color)) { + BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); + return OPERATOR_CANCELLED; + } + + /* Test whether to isolate or clear all flags */ + Material *ma = NULL; + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + /* Skip if this is the active one */ + if (ma == active_ma) + continue; + + /* If the flags aren't set, that means that the color is + * not alone, so we have some colors to isolate still + */ + gp_style = ma->gp_style; + if ((gp_style->flag & flags) == 0) { + isolate = true; + break; + } + } + + /* Set/Clear flags as appropriate */ + if (isolate) { + /* Set flags on all "other" colors */ + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + if (gp_style == active_color) + continue; + else + gp_style->flag |= flags; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } + } + else { + /* Clear flags - Restore everything else */ + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + gp_style->flag &= ~flags; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_color_isolate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Isolate Color"; - ot->idname = "GPENCIL_OT_color_isolate"; - ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; + /* identifiers */ + ot->name = "Isolate Color"; + ot->idname = "GPENCIL_OT_color_isolate"; + ot->description = + "Toggle whether the active color is the only one that is editable and/or visible"; - /* callbacks */ - ot->exec = gpencil_color_isolate_exec; - ot->poll = gpencil_active_color_poll; + /* callbacks */ + ot->exec = gpencil_color_isolate_exec; + ot->poll = gpencil_active_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " - "the editability, also affect the visibility"); + /* properties */ + RNA_def_boolean(ot->srna, + "affect_visibility", + false, + "Affect Visibility", + "In addition to toggling " + "the editability, also affect the visibility"); } /* *********************** Hide colors ******************************** */ static int gpencil_color_hide_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); - bool unselected = RNA_boolean_get(op->ptr, "unselected"); + bool unselected = RNA_boolean_get(op->ptr, "unselected"); - Material *ma = NULL; - short *totcol = give_totcolp(ob); - if (totcol == 0) - return OPERATOR_CANCELLED; + Material *ma = NULL; + short *totcol = give_totcolp(ob); + if (totcol == 0) + return OPERATOR_CANCELLED; - if (unselected) { - /* hide unselected */ - MaterialGPencilStyle *color = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - color = ma->gp_style; - if (active_color != color) { - color->flag |= GP_STYLE_COLOR_HIDE; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } - } - } - else { - /* hide selected/active */ - active_color->flag |= GP_STYLE_COLOR_HIDE; - } + if (unselected) { + /* hide unselected */ + MaterialGPencilStyle *color = NULL; + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + color = ma->gp_style; + if (active_color != color) { + color->flag |= GP_STYLE_COLOR_HIDE; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + else { + /* hide selected/active */ + active_color->flag |= GP_STYLE_COLOR_HIDE; + } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_color_hide(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Hide Color(s)"; - ot->idname = "GPENCIL_OT_color_hide"; - ot->description = "Hide selected/unselected Grease Pencil colors"; + /* identifiers */ + ot->name = "Hide Color(s)"; + ot->idname = "GPENCIL_OT_color_hide"; + ot->description = "Hide selected/unselected Grease Pencil colors"; - /* callbacks */ - ot->exec = gpencil_color_hide_exec; - ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ + /* callbacks */ + ot->exec = gpencil_color_hide_exec; + ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); + /* props */ + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); } /* ********************** Show All Colors ***************************** */ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - Material *ma = NULL; - short *totcol = give_totcolp(ob); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + Material *ma = NULL; + short *totcol = give_totcolp(ob); - if (totcol == 0) - return OPERATOR_CANCELLED; + if (totcol == 0) + return OPERATOR_CANCELLED; - /* make all colors visible */ - MaterialGPencilStyle *gp_style = NULL; + /* make all colors visible */ + MaterialGPencilStyle *gp_style = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - gp_style->flag &= ~GP_STYLE_COLOR_HIDE; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_color_reveal(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Show All Colors"; - ot->idname = "GPENCIL_OT_color_reveal"; - ot->description = "Unhide all hidden Grease Pencil colors"; + /* identifiers */ + ot->name = "Show All Colors"; + ot->idname = "GPENCIL_OT_color_reveal"; + ot->description = "Unhide all hidden Grease Pencil colors"; - /* callbacks */ - ot->exec = gpencil_color_reveal_exec; - ot->poll = gpencil_active_color_poll; + /* callbacks */ + ot->exec = gpencil_color_reveal_exec; + ot->poll = gpencil_active_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ***************** Lock/Unlock All colors ************************ */ @@ -2643,189 +2661,187 @@ void GPENCIL_OT_color_reveal(wmOperatorType *ot) static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - Material *ma = NULL; - short *totcol = give_totcolp(ob); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + Material *ma = NULL; + short *totcol = give_totcolp(ob); - if (totcol == 0) - return OPERATOR_CANCELLED; + if (totcol == 0) + return OPERATOR_CANCELLED; - /* make all layers non-editable */ - MaterialGPencilStyle *gp_style = NULL; + /* make all layers non-editable */ + MaterialGPencilStyle *gp_style = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - gp_style->flag |= GP_STYLE_COLOR_LOCKED; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_color_lock_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Lock All Colors"; - ot->idname = "GPENCIL_OT_color_lock_all"; - ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; + /* identifiers */ + ot->name = "Lock All Colors"; + ot->idname = "GPENCIL_OT_color_lock_all"; + ot->description = + "Lock all Grease Pencil colors to prevent them from being accidentally modified"; - /* callbacks */ - ot->exec = gpencil_color_lock_all_exec; - ot->poll = gpencil_active_color_poll; + /* callbacks */ + ot->exec = gpencil_color_lock_all_exec; + ot->poll = gpencil_active_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* -------------------------- */ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - Material *ma = NULL; - short *totcol = give_totcolp(ob); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + Material *ma = NULL; + short *totcol = give_totcolp(ob); - if (totcol == 0) - return OPERATOR_CANCELLED; + if (totcol == 0) + return OPERATOR_CANCELLED; - /* make all layers editable again*/ - MaterialGPencilStyle *gp_style = NULL; + /* make all layers editable again*/ + MaterialGPencilStyle *gp_style = NULL; - for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); - gp_style = ma->gp_style; - gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; - DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); - } + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); + } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_color_unlock_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Unlock All Colors"; - ot->idname = "GPENCIL_OT_color_unlock_all"; - ot->description = "Unlock all Grease Pencil colors so that they can be edited"; + /* identifiers */ + ot->name = "Unlock All Colors"; + ot->idname = "GPENCIL_OT_color_unlock_all"; + ot->description = "Unlock all Grease Pencil colors so that they can be edited"; - /* callbacks */ - ot->exec = gpencil_color_unlock_all_exec; - ot->poll = gpencil_active_color_poll; + /* callbacks */ + ot->exec = gpencil_color_unlock_all_exec; + ot->poll = gpencil_active_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - /* ***************** Select all strokes using color ************************ */ static int gpencil_color_select_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const bool deselected = RNA_boolean_get(op->ptr, "deselect"); - - /* sanity checks */ - if (ELEM(NULL, gpd, gp_style)) - return OPERATOR_CANCELLED; - - /* read all strokes and select*/ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - - /* verify something to do */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) - continue; - - /* select */ - if (ob->actcol == gps->mat_nr + 1) { - bGPDspoint *pt; - int i; - - if (!deselected) { - gps->flag |= GP_STROKE_SELECT; - } - else { - gps->flag &= ~GP_STROKE_SELECT; - } - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (!deselected) { - pt->flag |= GP_SPOINT_SELECT; - } - else { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - } - } - } - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - - } - } - CTX_DATA_END; - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool deselected = RNA_boolean_get(op->ptr, "deselect"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gp_style)) + return OPERATOR_CANCELLED; + + /* read all strokes and select*/ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + /* verify something to do */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) + continue; + + /* select */ + if (ob->actcol == gps->mat_nr + 1) { + bGPDspoint *pt; + int i; + + if (!deselected) { + gps->flag |= GP_STROKE_SELECT; + } + else { + gps->flag &= ~GP_STROKE_SELECT; + } + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (!deselected) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_color_select(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Color"; - ot->idname = "GPENCIL_OT_color_select"; - ot->description = "Select all Grease Pencil strokes using current color"; + /* identifiers */ + ot->name = "Select Color"; + ot->idname = "GPENCIL_OT_color_select"; + ot->description = "Select all Grease Pencil strokes using current color"; - /* callbacks */ - ot->exec = gpencil_color_select_exec; - ot->poll = gpencil_active_color_poll; + /* callbacks */ + ot->exec = gpencil_color_select_exec; + ot->poll = gpencil_active_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - ot->prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Unselect strokes"); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* props */ + ot->prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Unselect strokes"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e1eaedd435c..40e1e483f41 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -86,452 +86,455 @@ #include "gpencil_intern.h" - /* ************************************************ */ - /* Stroke Edit Mode Management */ +/* ************************************************ */ +/* Stroke Edit Mode Management */ /* poll callback for all stroke editing operators */ static bool gp_stroke_edit_poll(bContext *C) { - /* edit only supported with grease pencil objects */ - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - /* NOTE: this is a bit slower, but is the most accurate... */ - return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; + /* edit only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } /* poll callback to verify edit mode in 3D view only */ static bool gp_strokes_edit3d_poll(bContext *C) { - /* edit only supported with grease pencil objects */ - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - - /* 2 Requirements: - * - 1) Editable GP data - * - 2) 3D View only - */ - return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); + /* edit only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); } static bool gpencil_editmode_toggle_poll(bContext *C) { - /* edit only supported with grease pencil objects */ - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - /* if using gpencil object, use this gpd */ - if (ob->type == OB_GPENCIL) { - return ob->data != NULL; - } - - return ED_gpencil_data_get_active(C) != NULL; + /* edit only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + /* if using gpencil object, use this gpd */ + if (ob->type == OB_GPENCIL) { + return ob->data != NULL; + } + + return ED_gpencil_data_get_active(C) != NULL; } static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op) { - const int back = RNA_boolean_get(op->ptr, "back"); - - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bool is_object = false; - short mode; - /* if using a gpencil object, use this datablock */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - gpd = ob->data; - is_object = true; - } - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active GP data"); - return OPERATOR_CANCELLED; - } - - /* Just toggle editmode flag... */ - gpd->flag ^= GP_DATA_STROKE_EDITMODE; - /* recalculate parent matrix */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - ED_gpencil_reset_layers_parent(depsgraph, ob, gpd); - } - /* set mode */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - mode = OB_MODE_EDIT_GPENCIL; - } - else { - mode = OB_MODE_OBJECT; - } - - if (is_object) { - /* try to back previous mode */ - if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) { - mode = ob->restore_mode; - } - ob->restore_mode = ob->mode; - ob->mode = mode; - } - - /* setup other modes */ - ED_gpencil_setup_modes(C, gpd, mode); - /* set cache as dirty */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); - WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - - if (is_object) { - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - } - if (G.background == false) { - WM_toolsystem_update_from_context_view3d(C); - } - - return OPERATOR_FINISHED; + const int back = RNA_boolean_get(op->ptr, "back"); + + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active GP data"); + return OPERATOR_CANCELLED; + } + + /* Just toggle editmode flag... */ + gpd->flag ^= GP_DATA_STROKE_EDITMODE; + /* recalculate parent matrix */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + ED_gpencil_reset_layers_parent(depsgraph, ob, gpd); + } + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + mode = OB_MODE_EDIT_GPENCIL; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + if (is_object) { + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + } + if (G.background == false) { + WM_toolsystem_update_from_context_view3d(C); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Strokes Edit Mode Toggle"; - ot->idname = "GPENCIL_OT_editmode_toggle"; - ot->description = "Enter/Exit edit mode for Grease Pencil strokes"; + /* identifiers */ + ot->name = "Strokes Edit Mode Toggle"; + ot->idname = "GPENCIL_OT_editmode_toggle"; + ot->description = "Enter/Exit edit mode for Grease Pencil strokes"; - /* callbacks */ - ot->exec = gpencil_editmode_toggle_exec; - ot->poll = gpencil_editmode_toggle_poll; + /* callbacks */ + ot->exec = gpencil_editmode_toggle_exec; + ot->poll = gpencil_editmode_toggle_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - /* properties */ - prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_boolean( + ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* set select mode */ static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const int mode = RNA_int_get(op->ptr, "mode"); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const int mode = RNA_int_get(op->ptr, "mode"); - /* Just set mode */ - ts->gpencil_selectmode = mode; + /* Just set mode */ + ts->gpencil_selectmode = mode; - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Select Mode Toggle"; - ot->idname = "GPENCIL_OT_selectmode_toggle"; - ot->description = "Set selection mode for Grease Pencil strokes"; + /* identifiers */ + ot->name = "Select Mode Toggle"; + ot->idname = "GPENCIL_OT_selectmode_toggle"; + ot->description = "Set selection mode for Grease Pencil strokes"; - /* callbacks */ - ot->exec = gpencil_selectmode_toggle_exec; - ot->poll = gp_strokes_edit3d_poll; + /* callbacks */ + ot->exec = gpencil_selectmode_toggle_exec; + ot->poll = gp_strokes_edit3d_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - /* properties */ - prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* Stroke Paint Mode Management */ static bool gpencil_paintmode_toggle_poll(bContext *C) { - /* if using gpencil object, use this gpd */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - return ob->data != NULL; - } - return ED_gpencil_data_get_active(C) != NULL; + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; } static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) { - const bool back = RNA_boolean_get(op->ptr, "back"); - - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - ToolSettings *ts = CTX_data_tool_settings(C); - - bool is_object = false; - short mode; - /* if using a gpencil object, use this datablock */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - gpd = ob->data; - is_object = true; - } - - if (gpd == NULL) - return OPERATOR_CANCELLED; - - /* Just toggle paintmode flag... */ - gpd->flag ^= GP_DATA_STROKE_PAINTMODE; - /* set mode */ - if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { - mode = OB_MODE_PAINT_GPENCIL; - } - else { - mode = OB_MODE_OBJECT; - } - - if (is_object) { - /* try to back previous mode */ - if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) { - mode = ob->restore_mode; - } - ob->restore_mode = ob->mode; - ob->mode = mode; - } - - if (mode == OB_MODE_PAINT_GPENCIL) { - /* be sure we have brushes */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - Paint *paint = &ts->gp_paint->paint; - /* if not exist, create a new one */ - if (paint->brush == NULL) { - BKE_brush_gpencil_presets(C); - } - BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); - } - - /* setup other modes */ - ED_gpencil_setup_modes(C, gpd, mode); - /* set cache as dirty */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - - if (is_object) { - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - } - if (G.background == false) { - WM_toolsystem_update_from_context_view3d(C); - } - - return OPERATOR_FINISHED; + const bool back = RNA_boolean_get(op->ptr, "back"); + + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle paintmode flag... */ + gpd->flag ^= GP_DATA_STROKE_PAINTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + mode = OB_MODE_PAINT_GPENCIL; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + if (mode == OB_MODE_PAINT_GPENCIL) { + /* be sure we have brushes */ + BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + Paint *paint = &ts->gp_paint->paint; + /* if not exist, create a new one */ + if (paint->brush == NULL) { + BKE_brush_gpencil_presets(C); + } + BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + if (is_object) { + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + } + if (G.background == false) { + WM_toolsystem_update_from_context_view3d(C); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Strokes Paint Mode Toggle"; - ot->idname = "GPENCIL_OT_paintmode_toggle"; - ot->description = "Enter/Exit paint mode for Grease Pencil strokes"; + /* identifiers */ + ot->name = "Strokes Paint Mode Toggle"; + ot->idname = "GPENCIL_OT_paintmode_toggle"; + ot->description = "Enter/Exit paint mode for Grease Pencil strokes"; - /* callbacks */ - ot->exec = gpencil_paintmode_toggle_exec; - ot->poll = gpencil_paintmode_toggle_poll; + /* callbacks */ + ot->exec = gpencil_paintmode_toggle_exec; + ot->poll = gpencil_paintmode_toggle_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - /* properties */ - prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_boolean( + ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* Stroke Sculpt Mode Management */ static bool gpencil_sculptmode_toggle_poll(bContext *C) { - /* if using gpencil object, use this gpd */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - return ob->data != NULL; - } - return ED_gpencil_data_get_active(C) != NULL; + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; } static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) { - const bool back = RNA_boolean_get(op->ptr, "back"); - - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bool is_object = false; - short mode; - /* if using a gpencil object, use this datablock */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - gpd = ob->data; - is_object = true; - } - - if (gpd == NULL) - return OPERATOR_CANCELLED; - - /* Just toggle sculptmode flag... */ - gpd->flag ^= GP_DATA_STROKE_SCULPTMODE; - /* set mode */ - if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) { - mode = OB_MODE_SCULPT_GPENCIL; - } - else { - mode = OB_MODE_OBJECT; - } - - if (is_object) { - /* try to back previous mode */ - if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) { - mode = ob->restore_mode; - } - ob->restore_mode = ob->mode; - ob->mode = mode; - } - - /* setup other modes */ - ED_gpencil_setup_modes(C, gpd, mode); - /* set cache as dirty */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - - if (is_object) { - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - } - if (G.background == false) { - WM_toolsystem_update_from_context_view3d(C); - } - - return OPERATOR_FINISHED; + const bool back = RNA_boolean_get(op->ptr, "back"); + + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle sculptmode flag... */ + gpd->flag ^= GP_DATA_STROKE_SCULPTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) { + mode = OB_MODE_SCULPT_GPENCIL; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + if (is_object) { + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + } + if (G.background == false) { + WM_toolsystem_update_from_context_view3d(C); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Strokes Sculpt Mode Toggle"; - ot->idname = "GPENCIL_OT_sculptmode_toggle"; - ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes"; + /* identifiers */ + ot->name = "Strokes Sculpt Mode Toggle"; + ot->idname = "GPENCIL_OT_sculptmode_toggle"; + ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes"; - /* callbacks */ - ot->exec = gpencil_sculptmode_toggle_exec; - ot->poll = gpencil_sculptmode_toggle_poll; + /* callbacks */ + ot->exec = gpencil_sculptmode_toggle_exec; + ot->poll = gpencil_sculptmode_toggle_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - /* properties */ - prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_boolean( + ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* Stroke Weight Paint Mode Management */ static bool gpencil_weightmode_toggle_poll(bContext *C) { - /* if using gpencil object, use this gpd */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - return ob->data != NULL; - } - return ED_gpencil_data_get_active(C) != NULL; + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; } static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) { - const bool back = RNA_boolean_get(op->ptr, "back"); - - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bool is_object = false; - short mode; - /* if using a gpencil object, use this datablock */ - Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - gpd = ob->data; - is_object = true; - } - - if (gpd == NULL) - return OPERATOR_CANCELLED; - - /* Just toggle weightmode flag... */ - gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE; - /* set mode */ - if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) { - mode = OB_MODE_WEIGHT_GPENCIL; - } - else { - mode = OB_MODE_OBJECT; - } - - if (is_object) { - /* try to back previous mode */ - if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) { - mode = ob->restore_mode; - } - ob->restore_mode = ob->mode; - ob->mode = mode; - } - - /* setup other modes */ - ED_gpencil_setup_modes(C, gpd, mode); - /* set cache as dirty */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - - if (is_object) { - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - } - if (G.background == false) { - WM_toolsystem_update_from_context_view3d(C); - } - - return OPERATOR_FINISHED; + const bool back = RNA_boolean_get(op->ptr, "back"); + + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle weightmode flag... */ + gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) { + mode = OB_MODE_WEIGHT_GPENCIL; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + if (is_object) { + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + } + if (G.background == false) { + WM_toolsystem_update_from_context_view3d(C); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Strokes Weight Mode Toggle"; - ot->idname = "GPENCIL_OT_weightmode_toggle"; - ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes"; + /* identifiers */ + ot->name = "Strokes Weight Mode Toggle"; + ot->idname = "GPENCIL_OT_weightmode_toggle"; + ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes"; - /* callbacks */ - ot->exec = gpencil_weightmode_toggle_exec; - ot->poll = gpencil_weightmode_toggle_poll; + /* callbacks */ + ot->exec = gpencil_weightmode_toggle_exec; + ot->poll = gpencil_weightmode_toggle_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - /* properties */ - prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_boolean( + ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ************************************************ */ @@ -541,440 +544,437 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { - View3D *v3d = CTX_wm_view3d(C); - if (v3d == NULL) - return OPERATOR_CANCELLED; - - /* Just toggle alpha... */ - if (v3d->vertex_opacity > 0.0f) { - v3d->vertex_opacity = 0.0f; - } - else { - v3d->vertex_opacity = 1.0f; - } - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); - WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - - return OPERATOR_FINISHED; + View3D *v3d = CTX_wm_view3d(C); + if (v3d == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle alpha... */ + if (v3d->vertex_opacity > 0.0f) { + v3d->vertex_opacity = 0.0f; + } + else { + v3d->vertex_opacity = 1.0f; + } + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Hide Selected"; - ot->idname = "GPENCIL_OT_selection_opacity_toggle"; - ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor"; + /* identifiers */ + ot->name = "Hide Selected"; + ot->idname = "GPENCIL_OT_selection_opacity_toggle"; + ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor"; - /* callbacks */ - ot->exec = gpencil_hideselect_toggle_exec; - ot->poll = gp_stroke_edit_poll; + /* callbacks */ + ot->exec = gpencil_hideselect_toggle_exec; + ot->poll = gp_stroke_edit_poll; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } /* ************** Duplicate Selected Strokes **************** */ /* Make copies of selected point segments in a selected stroke */ -static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) +static void gp_duplicate_points(const bGPDstroke *gps, + ListBase *new_strokes, + const char *layername) { - bGPDspoint *pt; - int i; - - int start_idx = -1; - - - /* Step through the original stroke's points: - * - We accumulate selected points (from start_idx to current index) - * and then convert that to a new stroke - */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - /* searching for start, are waiting for end? */ - if (start_idx == -1) { - /* is this the first selected point for a new island? */ - if (pt->flag & GP_SPOINT_SELECT) { - start_idx = i; - } - } - else { - size_t len = 0; - - /* is this the end of current island yet? - * 1) Point i-1 was the last one that was selected - * 2) Point i is the last in the array - */ - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - len = i - start_idx; - } - else if (i == gps->totpoints - 1) { - len = i - start_idx + 1; - } - //printf("copying from %d to %d = %d\n", start_idx, i, len); - - /* make copies of the relevant data */ - if (len) { - bGPDstroke *gpsd; - - /* make a stupid copy first of the entire stroke (to get the flags too) */ - gpsd = MEM_dupallocN(gps); - - /* saves original layer name */ - BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); - - /* initialize triangle memory - will be calculated on next redraw */ - gpsd->triangles = NULL; - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->tot_triangles = 0; - - /* now, make a new points array, and copy of the relevant parts */ - gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); - memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); - gpsd->totpoints = len; - - if (gps->dvert != NULL) { - gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy"); - memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len); - - /* Copy weights */ - int e = start_idx; - for (int j = 0; j < gpsd->totpoints; j++) { - MDeformVert *dvert_dst = &gps->dvert[e]; - MDeformVert *dvert_src = &gps->dvert[j]; - dvert_dst->dw = MEM_dupallocN(dvert_src->dw); - e++; - } - } - - /* add to temp buffer */ - gpsd->next = gpsd->prev = NULL; - BLI_addtail(new_strokes, gpsd); - - /* cleanup + reset for next */ - start_idx = -1; - } - } - } + bGPDspoint *pt; + int i; + + int start_idx = -1; + + /* Step through the original stroke's points: + * - We accumulate selected points (from start_idx to current index) + * and then convert that to a new stroke + */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + /* searching for start, are waiting for end? */ + if (start_idx == -1) { + /* is this the first selected point for a new island? */ + if (pt->flag & GP_SPOINT_SELECT) { + start_idx = i; + } + } + else { + size_t len = 0; + + /* is this the end of current island yet? + * 1) Point i-1 was the last one that was selected + * 2) Point i is the last in the array + */ + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + len = i - start_idx; + } + else if (i == gps->totpoints - 1) { + len = i - start_idx + 1; + } + //printf("copying from %d to %d = %d\n", start_idx, i, len); + + /* make copies of the relevant data */ + if (len) { + bGPDstroke *gpsd; + + /* make a stupid copy first of the entire stroke (to get the flags too) */ + gpsd = MEM_dupallocN(gps); + + /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); + + /* initialize triangle memory - will be calculated on next redraw */ + gpsd->triangles = NULL; + gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; + gpsd->tot_triangles = 0; + + /* now, make a new points array, and copy of the relevant parts */ + gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); + memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); + gpsd->totpoints = len; + + if (gps->dvert != NULL) { + gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy"); + memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len); + + /* Copy weights */ + int e = start_idx; + for (int j = 0; j < gpsd->totpoints; j++) { + MDeformVert *dvert_dst = &gps->dvert[e]; + MDeformVert *dvert_src = &gps->dvert[j]; + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + e++; + } + } + + /* add to temp buffer */ + gpsd->next = gpsd->prev = NULL; + BLI_addtail(new_strokes, gpsd); + + /* cleanup + reset for next */ + start_idx = -1; + } + } + } } static int gp_duplicate_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* for each visible (and editable) layer's selected strokes, - * copy the strokes into a temporary buffer, then append - * once all done - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - ListBase new_strokes = {NULL, NULL}; - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps; - - if (gpf == NULL) - continue; - - /* make copies of selected strokes, and deselect these once we're done */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - - if (gps->flag & GP_STROKE_SELECT) { - if (gps->totpoints == 1) { - /* Special Case: If there's just a single point in this stroke... */ - bGPDstroke *gpsd; - - /* make direct copies of the stroke and its points */ - gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); - gpsd->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - gpsd->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, gpsd); - } - - /* triangle information - will be calculated on next redraw */ - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->triangles = NULL; - - /* add to temp buffer */ - gpsd->next = gpsd->prev = NULL; - BLI_addtail(&new_strokes, gpsd); - } - else { - /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &new_strokes, gpl->info); - } - - /* deselect original stroke, or else the originals get moved too - * (when using the copy + move macro) - */ - gps->flag &= ~GP_STROKE_SELECT; - } - } - - /* add all new strokes in temp buffer to the frame (preventing double-copies) */ - BLI_movelisttolist(&gpf->strokes, &new_strokes); - BLI_assert(new_strokes.first == NULL); - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + + /* for each visible (and editable) layer's selected strokes, + * copy the strokes into a temporary buffer, then append + * once all done + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + ListBase new_strokes = {NULL, NULL}; + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + + if (gpf == NULL) + continue; + + /* make copies of selected strokes, and deselect these once we're done */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + if (gps->flag & GP_STROKE_SELECT) { + if (gps->totpoints == 1) { + /* Special Case: If there's just a single point in this stroke... */ + bGPDstroke *gpsd; + + /* make direct copies of the stroke and its points */ + gpsd = MEM_dupallocN(gps); + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); + gpsd->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsd->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsd); + } + + /* triangle information - will be calculated on next redraw */ + gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; + gpsd->triangles = NULL; + + /* add to temp buffer */ + gpsd->next = gpsd->prev = NULL; + BLI_addtail(&new_strokes, gpsd); + } + else { + /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ + gp_duplicate_points(gps, &new_strokes, gpl->info); + } + + /* deselect original stroke, or else the originals get moved too + * (when using the copy + move macro) + */ + gps->flag &= ~GP_STROKE_SELECT; + } + } + + /* add all new strokes in temp buffer to the frame (preventing double-copies) */ + BLI_movelisttolist(&gpf->strokes, &new_strokes); + BLI_assert(new_strokes.first == NULL); + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_duplicate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Duplicate Strokes"; - ot->idname = "GPENCIL_OT_duplicate"; - ot->description = "Duplicate the selected Grease Pencil strokes"; + /* identifiers */ + ot->name = "Duplicate Strokes"; + ot->idname = "GPENCIL_OT_duplicate"; + ot->description = "Duplicate the selected Grease Pencil strokes"; - /* callbacks */ - ot->exec = gp_duplicate_exec; - ot->poll = gp_stroke_edit_poll; + /* callbacks */ + ot->exec = gp_duplicate_exec; + ot->poll = gp_stroke_edit_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ************** Extrude Selected Strokes **************** */ /* helper to copy a point to temp area */ -static void copy_move_point( - bGPDstroke *gps, - bGPDspoint *temp_points, - MDeformVert *temp_dverts, - int from_idx, int to_idx, const bool copy) +static void copy_move_point(bGPDstroke *gps, + bGPDspoint *temp_points, + MDeformVert *temp_dverts, + int from_idx, + int to_idx, + const bool copy) { - bGPDspoint *pt = &temp_points[from_idx]; - bGPDspoint *pt_final = &gps->points[to_idx]; - - copy_v3_v3(&pt_final->x, &pt->x); - pt_final->pressure = pt->pressure; - pt_final->strength = pt->strength; - pt_final->time = pt->time; - pt_final->flag = pt->flag; - pt_final->uv_fac = pt->uv_fac; - pt_final->uv_rot = pt->uv_rot; - - if (gps->dvert != NULL) { - MDeformVert *dvert = &temp_dverts[from_idx]; - MDeformVert *dvert_final = &gps->dvert[to_idx]; - - dvert_final->totweight = dvert->totweight; - /* if copy, duplicate memory, otherwise move only the pointer */ - if (copy) { - dvert_final->dw = MEM_dupallocN(dvert->dw); - } - else { - dvert_final->dw = dvert->dw; - } - } + bGPDspoint *pt = &temp_points[from_idx]; + bGPDspoint *pt_final = &gps->points[to_idx]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &temp_dverts[from_idx]; + MDeformVert *dvert_final = &gps->dvert[to_idx]; + + dvert_final->totweight = dvert->totweight; + /* if copy, duplicate memory, otherwise move only the pointer */ + if (copy) { + dvert_final->dw = MEM_dupallocN(dvert->dw); + } + else { + dvert_final->dw = dvert->dw; + } + } } static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) { - bGPDspoint *temp_points = NULL; - MDeformVert *temp_dverts = NULL; - bGPDspoint *pt = NULL; - const bGPDspoint *pt_start = &gps->points[0]; - const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1]; - const bool do_first = (pt_start->flag & GP_SPOINT_SELECT); - const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last)); - const bool do_stroke = (do_first || do_last); - - /* review points in the middle of stroke to create new strokes */ - for (int i = 0; i < gps->totpoints; i++) { - /* skip first and last point */ - if ((i == 0) || (i == gps->totpoints - 1)) { - continue; - } - - pt = &gps->points[i]; - if (pt->flag == GP_SPOINT_SELECT) { - /* duplicate original stroke data */ - bGPDstroke *gps_new = MEM_dupallocN(gps); - gps_new->prev = gps_new->next = NULL; - - /* add new points array */ - gps_new->totpoints = 1; - gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__); - gps_new->dvert = NULL; - - if (gps->dvert != NULL) { - gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__); - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps_new->triangles = NULL; - gps_new->tot_triangles = 0; - BLI_insertlinkafter(&gpf->strokes, gps, gps_new); - - /* copy selected point data to new stroke */ - copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true); - - /* deselect orinal point */ - pt->flag &= ~GP_SPOINT_SELECT; - } - } - - /* review first and last point to reuse same stroke */ - int i2 = 0; - int totnewpoints, oldtotpoints; - /* if first or last, reuse stroke and resize */ - if ((do_first) || (do_last)) { - totnewpoints = gps->totpoints; - if (do_first) { - totnewpoints++; - } - if (do_last) { - totnewpoints++; - } - - /* duplicate points in a temp area */ - temp_points = MEM_dupallocN(gps->points); - oldtotpoints = gps->totpoints; - if (gps->dvert != NULL) { - temp_dverts = MEM_dupallocN(gps->dvert); - } - - /* if first point, need move all one position */ - if (do_first) { - i2 = 1; - } - - /* resize the points arrays */ - gps->totpoints = totnewpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); - } - - /* move points to new position */ - for (int i = 0; i < oldtotpoints; i++) { - copy_move_point(gps, temp_points, temp_dverts, i, i2, false); - i2++; - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* if first point, add new point at the begining */ - if (do_first) { - copy_move_point(gps, temp_points, temp_dverts, 0, 0, true); - /* deselect old */ - pt = &gps->points[1]; - pt->flag &= ~GP_SPOINT_SELECT; - /* select new */ - pt = &gps->points[0]; - pt->flag |= GP_SPOINT_SELECT; - } - - /* if last point, add new point at the end */ - if (do_last) { - copy_move_point( - gps, temp_points, temp_dverts, - oldtotpoints - 1, gps->totpoints - 1, true); - - /* deselect old */ - pt = &gps->points[gps->totpoints - 2]; - pt->flag &= ~GP_SPOINT_SELECT; - /* select new */ - pt = &gps->points[gps->totpoints - 1]; - pt->flag |= GP_SPOINT_SELECT; - } - - MEM_SAFE_FREE(temp_points); - MEM_SAFE_FREE(temp_dverts); - } - - /* if the stroke is not reused, deselect */ - if (!do_stroke) { - gps->flag &= ~GP_STROKE_SELECT; - } + bGPDspoint *temp_points = NULL; + MDeformVert *temp_dverts = NULL; + bGPDspoint *pt = NULL; + const bGPDspoint *pt_start = &gps->points[0]; + const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1]; + const bool do_first = (pt_start->flag & GP_SPOINT_SELECT); + const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last)); + const bool do_stroke = (do_first || do_last); + + /* review points in the middle of stroke to create new strokes */ + for (int i = 0; i < gps->totpoints; i++) { + /* skip first and last point */ + if ((i == 0) || (i == gps->totpoints - 1)) { + continue; + } + + pt = &gps->points[i]; + if (pt->flag == GP_SPOINT_SELECT) { + /* duplicate original stroke data */ + bGPDstroke *gps_new = MEM_dupallocN(gps); + gps_new->prev = gps_new->next = NULL; + + /* add new points array */ + gps_new->totpoints = 1; + gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__); + gps_new->dvert = NULL; + + if (gps->dvert != NULL) { + gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps_new->triangles = NULL; + gps_new->tot_triangles = 0; + BLI_insertlinkafter(&gpf->strokes, gps, gps_new); + + /* copy selected point data to new stroke */ + copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true); + + /* deselect orinal point */ + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + /* review first and last point to reuse same stroke */ + int i2 = 0; + int totnewpoints, oldtotpoints; + /* if first or last, reuse stroke and resize */ + if ((do_first) || (do_last)) { + totnewpoints = gps->totpoints; + if (do_first) { + totnewpoints++; + } + if (do_last) { + totnewpoints++; + } + + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + if (gps->dvert != NULL) { + temp_dverts = MEM_dupallocN(gps->dvert); + } + + /* if first point, need move all one position */ + if (do_first) { + i2 = 1; + } + + /* resize the points arrays */ + gps->totpoints = totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + + /* move points to new position */ + for (int i = 0; i < oldtotpoints; i++) { + copy_move_point(gps, temp_points, temp_dverts, i, i2, false); + i2++; + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* if first point, add new point at the begining */ + if (do_first) { + copy_move_point(gps, temp_points, temp_dverts, 0, 0, true); + /* deselect old */ + pt = &gps->points[1]; + pt->flag &= ~GP_SPOINT_SELECT; + /* select new */ + pt = &gps->points[0]; + pt->flag |= GP_SPOINT_SELECT; + } + + /* if last point, add new point at the end */ + if (do_last) { + copy_move_point(gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true); + + /* deselect old */ + pt = &gps->points[gps->totpoints - 2]; + pt->flag &= ~GP_SPOINT_SELECT; + /* select new */ + pt = &gps->points[gps->totpoints - 1]; + pt->flag |= GP_SPOINT_SELECT; + } + + MEM_SAFE_FREE(temp_points); + MEM_SAFE_FREE(temp_dverts); + } + + /* if the stroke is not reused, deselect */ + if (!do_stroke) { + gps->flag &= ~GP_STROKE_SELECT; + } } static int gp_extrude_exec(bContext *C, wmOperator *op) { - Object *obact = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)obact->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - bGPDstroke *gps = NULL; - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - if (gps->flag & GP_STROKE_SELECT) { - gpencil_add_move_points(gpf, gps); - } - } - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Object *obact = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)obact->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bGPDstroke *gps = NULL; + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + gpencil_add_move_points(gpf, gps); + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_extrude(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Extrude Stroke Points"; - ot->idname = "GPENCIL_OT_extrude"; - ot->description = "Extrude the selected Grease Pencil points"; + /* identifiers */ + ot->name = "Extrude Stroke Points"; + ot->idname = "GPENCIL_OT_extrude"; + ot->description = "Extrude the selected Grease Pencil points"; - /* callbacks */ - ot->exec = gp_extrude_exec; - ot->poll = gp_stroke_edit_poll; + /* callbacks */ + ot->exec = gp_extrude_exec; + ot->poll = gp_stroke_edit_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - /* ******************* Copy/Paste Strokes ************************* */ /* Grease Pencil stroke data copy/paste buffer: * - The copy operation collects all segments of selected strokes, @@ -984,8 +984,8 @@ void GPENCIL_OT_extrude(wmOperatorType *ot) * from several different layers into a single layer. */ - /* list of bGPDstroke instances */ - /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ +/* list of bGPDstroke instances */ +/* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ ListBase gp_strokes_copypastebuf = {NULL, NULL}; /* Hash for hanging on to all the colors used by strokes in the buffer @@ -997,69 +997,69 @@ static GHash *gp_strokes_copypastebuf_colors = NULL; static GHash *gp_strokes_copypastebuf_colors_material_to_name_create(Main *bmain) { - GHash *ma_to_name = BLI_ghash_ptr_new(__func__); + GHash *ma_to_name = BLI_ghash_ptr_new(__func__); - for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) { - char *name = BKE_id_to_unique_string_key(&ma->id); - BLI_ghash_insert(ma_to_name, ma, name); - } + for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) { + char *name = BKE_id_to_unique_string_key(&ma->id); + BLI_ghash_insert(ma_to_name, ma, name); + } - return ma_to_name; + return ma_to_name; } static void gp_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name) { - BLI_ghash_free(ma_to_name, NULL, MEM_freeN); + BLI_ghash_free(ma_to_name, NULL, MEM_freeN); } static GHash *gp_strokes_copypastebuf_colors_name_to_material_create(Main *bmain) { - GHash *name_to_ma = BLI_ghash_str_new(__func__); + GHash *name_to_ma = BLI_ghash_str_new(__func__); - for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) { - char *name = BKE_id_to_unique_string_key(&ma->id); - BLI_ghash_insert(name_to_ma, name, ma); - } + for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) { + char *name = BKE_id_to_unique_string_key(&ma->id); + BLI_ghash_insert(name_to_ma, name, ma); + } - return name_to_ma; + return name_to_ma; } static void gp_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma) { - BLI_ghash_free(name_to_ma, MEM_freeN, NULL); + BLI_ghash_free(name_to_ma, MEM_freeN, NULL); } /* Free copy/paste buffer data */ void ED_gpencil_strokes_copybuf_free(void) { - bGPDstroke *gps, *gpsn; - - /* Free the colors buffer - * NOTE: This is done before the strokes so that the ptrs are still safe - */ - if (gp_strokes_copypastebuf_colors) { - BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); - gp_strokes_copypastebuf_colors = NULL; - } - - /* Free the stroke buffer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { - gpsn = gps->next; - - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - - MEM_SAFE_FREE(gps->triangles); - - BLI_freelinkN(&gp_strokes_copypastebuf, gps); - } - - gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; + bGPDstroke *gps, *gpsn; + + /* Free the colors buffer + * NOTE: This is done before the strokes so that the ptrs are still safe + */ + if (gp_strokes_copypastebuf_colors) { + BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); + gp_strokes_copypastebuf_colors = NULL; + } + + /* Free the stroke buffer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { + gpsn = gps->next; + + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + MEM_SAFE_FREE(gps->triangles); + + BLI_freelinkN(&gp_strokes_copypastebuf, gps); + } + + gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; } /* Ensure that destination datablock has all the colours the pasted strokes need @@ -1067,30 +1067,30 @@ void ED_gpencil_strokes_copybuf_free(void) */ GHash *gp_copybuf_validate_colormap(bContext *C) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors"); - GHashIterator gh_iter; + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors"); + GHashIterator gh_iter; - /* For each color, check if exist and add if not */ - GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain); + /* For each color, check if exist and add if not */ + GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain); - GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { - int *key = BLI_ghashIterator_getKey(&gh_iter); - char *ma_name = BLI_ghashIterator_getValue(&gh_iter); - Material *ma = BLI_ghash_lookup(name_to_ma, ma_name); + GHASH_ITER (gh_iter, gp_strokes_copypastebuf_colors) { + int *key = BLI_ghashIterator_getKey(&gh_iter); + char *ma_name = BLI_ghashIterator_getValue(&gh_iter); + Material *ma = BLI_ghash_lookup(name_to_ma, ma_name); - BKE_gpencil_object_material_ensure(bmain, ob, ma); + BKE_gpencil_object_material_ensure(bmain, ob, ma); - /* Store this mapping (for use later when pasting) */ - if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) { - BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma); - } - } + /* Store this mapping (for use later when pasting) */ + if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) { + BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma); + } + } - gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma); + gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma); - return new_colors; + return new_colors; } /* --------------------- */ @@ -1098,111 +1098,111 @@ GHash *gp_copybuf_validate_colormap(bContext *C) static int gp_strokes_copy_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* clear the buffer first */ - ED_gpencil_strokes_copybuf_free(); - - /* for each visible (and editable) layer's selected strokes, - * copy the strokes into a temporary buffer, then append - * once all done - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps; - - if (gpf == NULL) - continue; - - /* make copies of selected strokes, and deselect these once we're done */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - if (gps->flag & GP_STROKE_SELECT) { - if (gps->totpoints == 1) { - /* Special Case: If there's just a single point in this stroke... */ - bGPDstroke *gpsd; - - /* make direct copies of the stroke and its points */ - gpsd = MEM_dupallocN(gps); - /* saves original layer name */ - BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); - gpsd->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - gpsd->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, gpsd); - } - - /* triangles cache - will be recalculated on next redraw */ - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->tot_triangles = 0; - gpsd->triangles = NULL; - - /* add to temp buffer */ - gpsd->next = gpsd->prev = NULL; - BLI_addtail(&gp_strokes_copypastebuf, gpsd); - } - else { - /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info); - } - } - } - } - CTX_DATA_END; - - /* Build up hash of material colors used in these strokes */ - if (gp_strokes_copypastebuf.first) { - gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors"); - GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain); - for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - char **ma_name_val; - if (!BLI_ghash_ensure_p(gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) { - Material *ma = give_current_material(ob, gps->mat_nr + 1); - char *ma_name = BLI_ghash_lookup(ma_to_name, ma); - *ma_name_val = MEM_dupallocN(ma_name); - } - } - } - gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name); - } - - /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX? - - /* done */ - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + + /* clear the buffer first */ + ED_gpencil_strokes_copybuf_free(); + + /* for each visible (and editable) layer's selected strokes, + * copy the strokes into a temporary buffer, then append + * once all done + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + + if (gpf == NULL) + continue; + + /* make copies of selected strokes, and deselect these once we're done */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + if (gps->totpoints == 1) { + /* Special Case: If there's just a single point in this stroke... */ + bGPDstroke *gpsd; + + /* make direct copies of the stroke and its points */ + gpsd = MEM_dupallocN(gps); + /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); + gpsd->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsd->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsd); + } + + /* triangles cache - will be recalculated on next redraw */ + gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; + gpsd->tot_triangles = 0; + gpsd->triangles = NULL; + + /* add to temp buffer */ + gpsd->next = gpsd->prev = NULL; + BLI_addtail(&gp_strokes_copypastebuf, gpsd); + } + else { + /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ + gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info); + } + } + } + } + CTX_DATA_END; + + /* Build up hash of material colors used in these strokes */ + if (gp_strokes_copypastebuf.first) { + gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors"); + GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain); + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + char **ma_name_val; + if (!BLI_ghash_ensure_p( + gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) { + Material *ma = give_current_material(ob, gps->mat_nr + 1); + char *ma_name = BLI_ghash_lookup(ma_to_name, ma); + *ma_name_val = MEM_dupallocN(ma_name); + } + } + } + gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name); + } + + /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX? + + /* done */ + return OPERATOR_FINISHED; } void GPENCIL_OT_copy(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Copy Strokes"; - ot->idname = "GPENCIL_OT_copy"; - ot->description = "Copy selected Grease Pencil points and strokes"; + /* identifiers */ + ot->name = "Copy Strokes"; + ot->idname = "GPENCIL_OT_copy"; + ot->description = "Copy selected Grease Pencil points and strokes"; - /* callbacks */ - ot->exec = gp_strokes_copy_exec; - ot->poll = gp_stroke_edit_poll; + /* callbacks */ + ot->exec = gp_strokes_copy_exec; + ot->poll = gp_stroke_edit_poll; - /* flags */ - //ot->flag = OPTYPE_REGISTER; + /* flags */ + //ot->flag = OPTYPE_REGISTER; } /* --------------------- */ @@ -1210,299 +1210,302 @@ void GPENCIL_OT_copy(wmOperatorType *ot) static bool gp_strokes_paste_poll(bContext *C) { - /* 1) Must have GP datablock to paste to - * - We don't need to have an active layer though, as that can easily get added - * - If the active layer is locked, we can't paste there, but that should prompt a warning instead - * 2) Copy buffer must at least have something (though it may be the wrong sort...) - */ - return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); + /* 1) Must have GP datablock to paste to + * - We don't need to have an active layer though, as that can easily get added + * - If the active layer is locked, we can't paste there, but that should prompt a warning instead + * 2) Copy buffer must at least have something (though it may be the wrong sort...) + */ + return (ED_gpencil_data_get_active(C) != NULL) && + (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } typedef enum eGP_PasteMode { - GP_COPY_ONLY = -1, - GP_COPY_MERGE = 1, + GP_COPY_ONLY = -1, + GP_COPY_MERGE = 1, } eGP_PasteMode; static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPDframe *gpf; - - eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); - GHash *new_colors; - - /* check for various error conditions */ - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) { - BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again"); - return OPERATOR_CANCELLED; - } - else if (gpl == NULL) { - /* no active layer - let's just create one */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); - } - else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { - BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); - return OPERATOR_CANCELLED; - } - else { - /* Check that some of the strokes in the buffer can be used */ - bGPDstroke *gps; - bool ok = false; - - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - ok = true; - break; - } - } - - if (ok == false) { - /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes), - * but should be enough to give users a good idea of what's going on - */ - if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) - BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View"); - else - BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors"); - - return OPERATOR_CANCELLED; - } - } - - /* Deselect all strokes first */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - - gps->flag &= ~GP_STROKE_SELECT; - } - CTX_DATA_END; - - /* Ensure that all the necessary colors exist */ - new_colors = gp_copybuf_validate_colormap(C); - - /* Copy over the strokes from the buffer (and adjust the colors) */ - for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - /* Need to verify if layer exists */ - if (type != GP_COPY_MERGE) { - gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info)); - if (gpl == NULL) { - /* no layer - use active (only if layer deleted before paste) */ - gpl = CTX_data_active_gpencil_layer(C); - } - } - - /* Ensure we have a frame to draw into - * NOTE: Since this is an op which creates strokes, - * we are obliged to add a new frame if one - * doesn't exist already - */ - gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - if (gpf) { - /* Create new stroke */ - bGPDstroke *new_stroke = MEM_dupallocN(gps); - new_stroke->runtime.tmp_layerinfo[0] = '\0'; - - new_stroke->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); - } - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - new_stroke->triangles = NULL; - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); - - /* Remap material */ - Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); - new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); - BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */ - } - } - } - - /* free temp data */ - BLI_ghash_free(new_colors, NULL, NULL); - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDframe *gpf; + + eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); + GHash *new_colors; + + /* check for various error conditions */ + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) { + BKE_report(op->reports, + RPT_ERROR, + "No strokes to paste, select and copy some points before trying again"); + return OPERATOR_CANCELLED; + } + else if (gpl == NULL) { + /* no active layer - let's just create one */ + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + } + else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { + BKE_report( + op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); + return OPERATOR_CANCELLED; + } + else { + /* Check that some of the strokes in the buffer can be used */ + bGPDstroke *gps; + bool ok = false; + + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + ok = true; + break; + } + } + + if (ok == false) { + /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes), + * but should be enough to give users a good idea of what's going on + */ + if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) + BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View"); + else + BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors"); + + return OPERATOR_CANCELLED; + } + } + + /* Deselect all strokes first */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + gps->flag &= ~GP_STROKE_SELECT; + } + CTX_DATA_END; + + /* Ensure that all the necessary colors exist */ + new_colors = gp_copybuf_validate_colormap(C); + + /* Copy over the strokes from the buffer (and adjust the colors) */ + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + /* Need to verify if layer exists */ + if (type != GP_COPY_MERGE) { + gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info)); + if (gpl == NULL) { + /* no layer - use active (only if layer deleted before paste) */ + gpl = CTX_data_active_gpencil_layer(C); + } + } + + /* Ensure we have a frame to draw into + * NOTE: Since this is an op which creates strokes, + * we are obliged to add a new frame if one + * doesn't exist already + */ + gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + if (gpf) { + /* Create new stroke */ + bGPDstroke *new_stroke = MEM_dupallocN(gps); + new_stroke->runtime.tmp_layerinfo[0] = '\0'; + + new_stroke->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); + } + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + new_stroke->triangles = NULL; + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + + /* Remap material */ + Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); + new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); + BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */ + } + } + } + + /* free temp data */ + BLI_ghash_free(new_colors, NULL, NULL); + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_paste(wmOperatorType *ot) { - static const EnumPropertyItem copy_type[] = { - {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, - {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Paste Strokes"; - ot->idname = "GPENCIL_OT_paste"; - ot->description = "Paste previously copied strokes or copy and merge in active layer"; - - /* callbacks */ - ot->exec = gp_strokes_paste_exec; - ot->poll = gp_strokes_paste_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); + static const EnumPropertyItem copy_type[] = { + {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, + {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Paste Strokes"; + ot->idname = "GPENCIL_OT_paste"; + ot->description = "Paste previously copied strokes or copy and merge in active layer"; + + /* callbacks */ + ot->exec = gp_strokes_paste_exec; + ot->poll = gp_strokes_paste_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } /* ******************* Move To Layer ****************************** */ static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) { - uiPopupMenu *pup; - uiLayout *layout; + uiPopupMenu *pup; + uiLayout *layout; - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer"); - UI_popup_menu_end(C, pup); + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer"); + UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; + return OPERATOR_INTERFACE; } // FIXME: allow moving partial strokes static int gp_move_to_layer_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = CTX_data_gpencil_data(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPDlayer *target_layer = NULL; - ListBase strokes = {NULL, NULL}; - int layer_num = RNA_enum_get(op->ptr, "layer"); - const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); - - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ - if (use_autolock) { - gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; - } - - /* Get layer or create new one */ - if (layer_num == -1) { - /* Create layer */ - target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); - } - else { - /* Try to get layer */ - target_layer = BLI_findlink(&gpd->layers, layer_num); - - if (target_layer == NULL) { - /* back autolock status */ - if (use_autolock) { - gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; - } - BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num); - return OPERATOR_CANCELLED; - } - } - - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) - continue; - - /* make copies of selected strokes, and deselect these once we're done */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); - } - } - - /* if new layer and autolock, lock old layer */ - if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; - } - } - CTX_DATA_END; - - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - - /* back autolock status */ - if (use_autolock) { - gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; - } - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = CTX_data_gpencil_data(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDlayer *target_layer = NULL; + ListBase strokes = {NULL, NULL}; + int layer_num = RNA_enum_get(op->ptr, "layer"); + const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + + /* if autolock enabled, disabled now */ + if (use_autolock) { + gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; + } + + /* Get layer or create new one */ + if (layer_num == -1) { + /* Create layer */ + target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + } + else { + /* Try to get layer */ + target_layer = BLI_findlink(&gpd->layers, layer_num); + + if (target_layer == NULL) { + /* back autolock status */ + if (use_autolock) { + gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; + } + BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num); + return OPERATOR_CANCELLED; + } + } + + /* Extract all strokes to move to this layer + * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes + * getting repeatedly moved + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps, *gpsn; + + /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ + if ((gpl == target_layer) || (gpf == NULL)) + continue; + + /* make copies of selected strokes, and deselect these once we're done */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + + /* if new layer and autolock, lock old layer */ + if ((layer_num == -1) && (use_autolock)) { + gpl->flag |= GP_LAYER_LOCKED; + } + } + CTX_DATA_END; + + /* Paste them all in one go */ + if (strokes.first) { + bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW); + + BLI_movelisttolist(&gpf->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } + + /* back autolock status */ + if (use_autolock) { + gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; + } + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_move_to_layer(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Move Strokes to Layer"; - ot->idname = "GPENCIL_OT_move_to_layer"; - ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too? - - /* callbacks */ - ot->invoke = gp_move_to_layer_invoke; - ot->exec = gp_move_to_layer_exec; - ot->poll = gp_stroke_edit_poll; // XXX? - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* gp layer to use (dynamic enum) */ - ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", ""); - RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); + /* identifiers */ + ot->name = "Move Strokes to Layer"; + ot->idname = "GPENCIL_OT_move_to_layer"; + ot->description = + "Move selected strokes to another layer"; // XXX: allow moving individual points too? + + /* callbacks */ + ot->invoke = gp_move_to_layer_invoke; + ot->exec = gp_move_to_layer_exec; + ot->poll = gp_stroke_edit_poll; // XXX? + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp layer to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); } /* ********************* Add Blank Frame *************************** */ @@ -1510,221 +1513,224 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) /* Basically the same as the drawing op */ static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) { - if (ED_operator_regionactive(C)) { - /* check if current context can support GPencil data */ - if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { - return 1; - } - else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); - } - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not set"); - } - - return 0; + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + return 1; + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return 0; } static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); - - const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); - - /* Initialise datablock and an active layer if nothing exists yet */ - if (ELEM(NULL, gpd, active_gpl)) { - /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */ - WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL); - } - - /* Go through each layer, adding a frame after the active one - * and/or shunting all the others out of the way - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - if ((all_layers == false) && (gpl != active_gpl)) { - continue; - } - - /* 1) Check for an existing frame on the current frame */ - bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval); - if (gpf) { - /* Shunt all frames after (and including) the existing one later by 1-frame */ - for (; gpf; gpf = gpf->next) { - gpf->framenum += 1; - } - } - - /* 2) Now add a new frame, with nothing in it */ - gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); + + const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); + + /* Initialise datablock and an active layer if nothing exists yet */ + if (ELEM(NULL, gpd, active_gpl)) { + /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */ + WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL); + } + + /* Go through each layer, adding a frame after the active one + * and/or shunting all the others out of the way + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + if ((all_layers == false) && (gpl != active_gpl)) { + continue; + } + + /* 1) Check for an existing frame on the current frame */ + bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval); + if (gpf) { + /* Shunt all frames after (and including) the existing one later by 1-frame */ + for (; gpf; gpf = gpf->next) { + gpf->framenum += 1; + } + } + + /* 2) Now add a new frame, with nothing in it */ + gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Insert Blank Frame"; - ot->idname = "GPENCIL_OT_blank_frame_add"; - ot->description = "Insert a blank frame on the current frame " - "(all subsequently existing frames, if any, are shifted right by one frame)"; - - /* callbacks */ - ot->exec = gp_blank_frame_add_exec; - ot->poll = gp_add_poll; - - /* properties */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active"); + /* identifiers */ + ot->name = "Insert Blank Frame"; + ot->idname = "GPENCIL_OT_blank_frame_add"; + ot->description = + "Insert a blank frame on the current frame " + "(all subsequently existing frames, if any, are shifted right by one frame)"; + + /* callbacks */ + ot->exec = gp_blank_frame_add_exec; + ot->poll = gp_add_poll; + + /* properties */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_boolean(ot->srna, + "all_layers", + false, + "All Layers", + "Create blank frame in all layers, not only active"); } /* ******************* Delete Active Frame ************************ */ static bool gp_actframe_delete_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - /* only if there's an active layer with an active frame */ - return (gpl && gpl->actframe); + /* only if there's an active layer with an active frame */ + return (gpl && gpl->actframe); } /* delete active frame - wrapper around API calls */ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No grease pencil data"); - return OPERATOR_CANCELLED; - } - if (ELEM(NULL, gpl, gpf)) { - BKE_report(op->reports, RPT_ERROR, "No active frame to delete"); - return OPERATOR_CANCELLED; - } + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No grease pencil data"); + return OPERATOR_CANCELLED; + } + if (ELEM(NULL, gpl, gpf)) { + BKE_report(op->reports, RPT_ERROR, "No active frame to delete"); + return OPERATOR_CANCELLED; + } - /* delete it... */ - BKE_gpencil_layer_delframe(gpl, gpf); + /* delete it... */ + BKE_gpencil_layer_delframe(gpl, gpf); - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_active_frame_delete(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Delete Active Frame"; - ot->idname = "GPENCIL_OT_active_frame_delete"; - ot->description = "Delete the active frame for the active Grease Pencil Layer"; + /* identifiers */ + ot->name = "Delete Active Frame"; + ot->idname = "GPENCIL_OT_active_frame_delete"; + ot->description = "Delete the active frame for the active Grease Pencil Layer"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_actframe_delete_exec; - ot->poll = gp_actframe_delete_poll; + /* callbacks */ + ot->exec = gp_actframe_delete_exec; + ot->poll = gp_actframe_delete_poll; } /* **************** Delete All Active Frames ****************** */ static bool gp_actframe_delete_all_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); - /* 1) There must be grease pencil data - * 2) Hopefully some of the layers have stuff we can use - */ - return (gpd && gpd->layers.first); + /* 1) There must be grease pencil data + * 2) Hopefully some of the layers have stuff we can use + */ + return (gpd && gpd->layers.first); } static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bool success = false; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - - if (gpf == NULL) - continue; - - /* delete it... */ - BKE_gpencil_layer_delframe(gpl, gpf); - - /* we successfully modified something */ - success = true; - } - CTX_DATA_END; - - /* updates */ - if (success) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; - } - else { - BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete"); - return OPERATOR_CANCELLED; - } + bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bool success = false; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + /* try to get the "active" frame - but only if it actually occurs on this frame */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + + if (gpf == NULL) + continue; + + /* delete it... */ + BKE_gpencil_layer_delframe(gpl, gpf); + + /* we successfully modified something */ + success = true; + } + CTX_DATA_END; + + /* updates */ + if (success) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete"); + return OPERATOR_CANCELLED; + } } void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Delete All Active Frames"; - ot->idname = "GPENCIL_OT_active_frames_delete_all"; - ot->description = "Delete the active frame(s) of all editable Grease Pencil layers"; + /* identifiers */ + ot->name = "Delete All Active Frames"; + ot->idname = "GPENCIL_OT_active_frames_delete_all"; + ot->description = "Delete the active frame(s) of all editable Grease Pencil layers"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_actframe_delete_all_exec; - ot->poll = gp_actframe_delete_all_poll; + /* callbacks */ + ot->exec = gp_actframe_delete_all_exec; + ot->poll = gp_actframe_delete_all_poll; } /* ******************* Delete Operator ************************ */ typedef enum eGP_DeleteMode { - /* delete selected stroke points */ - GP_DELETEOP_POINTS = 0, - /* delete selected strokes */ - GP_DELETEOP_STROKES = 1, - /* delete active frame */ - GP_DELETEOP_FRAME = 2, + /* delete selected stroke points */ + GP_DELETEOP_POINTS = 0, + /* delete selected strokes */ + GP_DELETEOP_STROKES = 1, + /* delete active frame */ + GP_DELETEOP_FRAME = 2, } eGP_DeleteMode; typedef enum eGP_DissolveMode { - /* dissolve all selected points */ - GP_DISSOLVE_POINTS = 0, - /* dissolve between selected points */ - GP_DISSOLVE_BETWEEN = 1, - /* dissolve unselected points */ - GP_DISSOLVE_UNSELECT = 2, + /* dissolve all selected points */ + GP_DISSOLVE_POINTS = 0, + /* dissolve between selected points */ + GP_DISSOLVE_BETWEEN = 1, + /* dissolve unselected points */ + GP_DISSOLVE_UNSELECT = 2, } eGP_DissolveMode; /* ----------------------------------- */ @@ -1732,61 +1738,60 @@ typedef enum eGP_DissolveMode { /* Delete selected strokes */ static int gp_delete_selected_strokes(bContext *C) { - bool changed = false; - bGPdata *gpd = ED_gpencil_data_get_active(C); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) - continue; - - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - /* free stroke if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* free stroke memory arrays, then stroke itself */ - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - MEM_SAFE_FREE(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); - - changed = true; - } - } - } - } - } - CTX_DATA_END; - - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } /* ----------------------------------- */ @@ -1794,242 +1799,242 @@ static int gp_delete_selected_strokes(bContext *C) /* Delete selected points but keep the stroke */ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - bool changed = false; - int first = 0; - int last = 0; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) - continue; - - /* simply delete points from selected strokes - * NOTE: we may still have to remove the stroke if it ends up having no points! - */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) - continue; - - /* the stroke must have at least one point selected for any operator */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - MDeformVert *dvert = NULL; - int i; - - int tot = gps->totpoints; /* number of points in new buffer */ - - /* first pass: count points to remove */ - switch (mode) { - case GP_DISSOLVE_POINTS: - /* Count how many points are selected (i.e. how many to remove) */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* selected point - one of the points to remove */ - tot--; - } - } - break; - case GP_DISSOLVE_BETWEEN: - /* need to find first and last point selected */ - first = -1; - last = 0; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - if (first < 0) { - first = i; - } - last = i; - } - } - /* count unselected points in the range */ - for (i = first, pt = gps->points + first; i < last; i++, pt++) { - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - tot--; - } - } - break; - case GP_DISSOLVE_UNSELECT: - /* count number of unselected points */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - tot--; - } - } - break; - default: - return false; - break; - } - - /* if no points are left, we simply delete the entire stroke */ - if (tot <= 0) { - /* remove the entire stroke */ - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - if (gps->triangles) { - MEM_freeN(gps->triangles); - } - BLI_freelinkN(&gpf->strokes, gps); - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - else { - /* just copy all points to keep into a smaller buffer */ - bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); - bGPDspoint *npt = new_points; - - MDeformVert *new_dvert = NULL; - MDeformVert *ndvert = NULL; - - if (gps->dvert != NULL) { - new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); - ndvert = new_dvert; - } - - switch (mode) { - case GP_DISSOLVE_POINTS: - (gps->dvert != NULL) ? dvert = gps->dvert : NULL; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - *npt = *pt; - npt++; - - if (gps->dvert != NULL) { - *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); - ndvert++; - dvert++; - } - } - } - break; - case GP_DISSOLVE_BETWEEN: - /* copy first segment */ - (gps->dvert != NULL) ? dvert = gps->dvert : NULL; - for (i = 0, pt = gps->points; i < first; i++, pt++) { - *npt = *pt; - npt++; - - if (gps->dvert != NULL) { - *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); - ndvert++; - dvert++; - } - } - /* copy segment (selected points) */ - (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL; - for (i = first, pt = gps->points + first; i < last; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - *npt = *pt; - npt++; - - if (gps->dvert != NULL) { - *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); - ndvert++; - dvert++; - } - } - } - /* copy last segment */ - (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL; - for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) { - *npt = *pt; - npt++; - - if (gps->dvert != NULL) { - *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); - ndvert++; - dvert++; - } - } - - break; - case GP_DISSOLVE_UNSELECT: - /* copy any selected point */ - (gps->dvert != NULL) ? dvert = gps->dvert : NULL; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - *npt = *pt; - npt++; - - if (gps->dvert != NULL) { - *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); - ndvert++; - dvert++; - } - } - } - break; - } - - /* free the old buffer */ - if (gps->points) { - MEM_freeN(gps->points); - } - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - - /* save the new buffer */ - gps->points = new_points; - gps->dvert = new_dvert; - gps->totpoints = tot; - - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - - /* deselect the stroke, since none of its selected points will still be selected */ - gps->flag &= ~GP_STROKE_SELECT; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - - changed = true; - } - } - } - } - } - CTX_DATA_END; - - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bool changed = false; + int first = 0; + int last = 0; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + /* simply delete points from selected strokes + * NOTE: we may still have to remove the stroke if it ends up having no points! + */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) + continue; + + /* the stroke must have at least one point selected for any operator */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + MDeformVert *dvert = NULL; + int i; + + int tot = gps->totpoints; /* number of points in new buffer */ + + /* first pass: count points to remove */ + switch (mode) { + case GP_DISSOLVE_POINTS: + /* Count how many points are selected (i.e. how many to remove) */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* selected point - one of the points to remove */ + tot--; + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* need to find first and last point selected */ + first = -1; + last = 0; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (first < 0) { + first = i; + } + last = i; + } + } + /* count unselected points in the range */ + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + case GP_DISSOLVE_UNSELECT: + /* count number of unselected points */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + default: + return false; + break; + } + + /* if no points are left, we simply delete the entire stroke */ + if (tot <= 0) { + /* remove the entire stroke */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) { + MEM_freeN(gps->triangles); + } + BLI_freelinkN(&gpf->strokes, gps); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + else { + /* just copy all points to keep into a smaller buffer */ + bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, + "new gp stroke points copy"); + bGPDspoint *npt = new_points; + + MDeformVert *new_dvert = NULL; + MDeformVert *ndvert = NULL; + + if (gps->dvert != NULL) { + new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); + ndvert = new_dvert; + } + + switch (mode) { + case GP_DISSOLVE_POINTS: + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* copy first segment */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < first; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + /* copy segment (selected points) */ + (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL; + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + /* copy last segment */ + (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL; + for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + + break; + case GP_DISSOLVE_UNSELECT: + /* copy any selected point */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + } + + /* free the old buffer */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + /* save the new buffer */ + gps->points = new_points; + gps->dvert = new_dvert; + gps->totpoints = tot; + + /* triangles cache needs to be recalculated */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + + /* deselect the stroke, since none of its selected points will still be selected */ + gps->flag &= ~GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } /* ----------------------------------- */ @@ -2039,94 +2044,93 @@ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) * gp_stroke_delete_tagged_points() */ typedef struct tGPDeleteIsland { - int start_idx; - int end_idx; + int start_idx; + int end_idx; } tGPDeleteIsland; static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last) { - bGPDspoint *pt = NULL; - bGPDspoint *pt_final = NULL; - const int totpoints = gps_first->totpoints + gps_last->totpoints; - - /* create new stroke */ - bGPDstroke *join_stroke = MEM_dupallocN(gps_first); - - join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); - join_stroke->totpoints = totpoints; - join_stroke->flag &= ~GP_STROKE_CYCLIC; - - /* copy points (last before) */ - int e1 = 0; - int e2 = 0; - float delta = 0.0f; - - for (int i = 0; i < totpoints; i++) { - pt_final = &join_stroke->points[i]; - if (i < gps_last->totpoints) { - pt = &gps_last->points[e1]; - e1++; - } - else { - pt = &gps_first->points[e2]; - e2++; - } - - /* copy current point */ - copy_v3_v3(&pt_final->x, &pt->x); - pt_final->pressure = pt->pressure; - pt_final->strength = pt->strength; - pt_final->time = delta; - pt_final->flag = pt->flag; - - /* retiming with fixed time interval (we cannot determine real time) */ - delta += 0.01f; - } - - /* Copy over vertex weight data (if available) */ - if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) { - join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__); - MDeformVert *dvert_src = NULL; - MDeformVert *dvert_dst = NULL; - - /* Copy weights (last before)*/ - e1 = 0; - e2 = 0; - for (int i = 0; i < totpoints; i++) { - dvert_dst = &join_stroke->dvert[i]; - dvert_src = NULL; - if (i < gps_last->totpoints) { - if (gps_last->dvert) { - dvert_src = &gps_last->dvert[e1]; - e1++; - } - } - else { - if (gps_first->dvert) { - dvert_src = &gps_first->dvert[e2]; - e2++; - } - } - - if ((dvert_src) && (dvert_src->dw)) { - dvert_dst->dw = MEM_dupallocN(dvert_src->dw); - } - } - } - - /* add new stroke at head */ - BLI_addhead(&gpf->strokes, join_stroke); - - /* remove first stroke */ - BLI_remlink(&gpf->strokes, gps_first); - BKE_gpencil_free_stroke(gps_first); - - /* remove last stroke */ - BLI_remlink(&gpf->strokes, gps_last); - BKE_gpencil_free_stroke(gps_last); + bGPDspoint *pt = NULL; + bGPDspoint *pt_final = NULL; + const int totpoints = gps_first->totpoints + gps_last->totpoints; + + /* create new stroke */ + bGPDstroke *join_stroke = MEM_dupallocN(gps_first); + + join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); + join_stroke->totpoints = totpoints; + join_stroke->flag &= ~GP_STROKE_CYCLIC; + + /* copy points (last before) */ + int e1 = 0; + int e2 = 0; + float delta = 0.0f; + + for (int i = 0; i < totpoints; i++) { + pt_final = &join_stroke->points[i]; + if (i < gps_last->totpoints) { + pt = &gps_last->points[e1]; + e1++; + } + else { + pt = &gps_first->points[e2]; + e2++; + } + + /* copy current point */ + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = delta; + pt_final->flag = pt->flag; + + /* retiming with fixed time interval (we cannot determine real time) */ + delta += 0.01f; + } + + /* Copy over vertex weight data (if available) */ + if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) { + join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__); + MDeformVert *dvert_src = NULL; + MDeformVert *dvert_dst = NULL; + + /* Copy weights (last before)*/ + e1 = 0; + e2 = 0; + for (int i = 0; i < totpoints; i++) { + dvert_dst = &join_stroke->dvert[i]; + dvert_src = NULL; + if (i < gps_last->totpoints) { + if (gps_last->dvert) { + dvert_src = &gps_last->dvert[e1]; + e1++; + } + } + else { + if (gps_first->dvert) { + dvert_src = &gps_first->dvert[e2]; + e2++; + } + } + + if ((dvert_src) && (dvert_src->dw)) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + } + } + + /* add new stroke at head */ + BLI_addhead(&gpf->strokes, join_stroke); + + /* remove first stroke */ + BLI_remlink(&gpf->strokes, gps_first); + BKE_gpencil_free_stroke(gps_first); + + /* remove last stroke */ + BLI_remlink(&gpf->strokes, gps_last); + BKE_gpencil_free_stroke(gps_last); } - /* Split the given stroke into several new strokes, partitioning * it based on whether the stroke points have a particular flag * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always) @@ -2141,290 +2145,315 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst * 2) Each island gets converted to a new stroke * If the number of points is <= limit, the stroke is deleted */ -void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, - int tag_flags, bool select, int limit) +void gp_stroke_delete_tagged_points(bGPDframe *gpf, + bGPDstroke *gps, + bGPDstroke *next_stroke, + int tag_flags, + bool select, + int limit) { - tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); - bool in_island = false; - int num_islands = 0; - - bGPDstroke *gps_first = NULL; - const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC); - - /* First Pass: Identify start/end of islands */ - bGPDspoint *pt = gps->points; - for (int i = 0; i < gps->totpoints; i++, pt++) { - if (pt->flag & tag_flags) { - /* selected - stop accumulating to island */ - in_island = false; - } - else { - /* unselected - start of a new island? */ - int idx; - - if (in_island) { - /* extend existing island */ - idx = num_islands - 1; - islands[idx].end_idx = i; - } - else { - /* start of new island */ - in_island = true; - num_islands++; - - idx = num_islands - 1; - islands[idx].start_idx = islands[idx].end_idx = i; - } - } - } - - /* Watch out for special case where No islands = All points selected = Delete Stroke only */ - if (num_islands) { - /* there are islands, so create a series of new strokes, adding them before the "next" stroke */ - int idx; - bGPDstroke *new_stroke = NULL; - - /* Create each new stroke... */ - for (idx = 0; idx < num_islands; idx++) { - tGPDeleteIsland *island = &islands[idx]; - new_stroke = MEM_dupallocN(gps); - - /* if cyclic and first stroke, save to join later */ - if ((is_cyclic) && (gps_first == NULL)) { - gps_first = new_stroke; - } - - /* initialize triangle memory - to be calculated on next redraw */ - new_stroke->triangles = NULL; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - new_stroke->flag &= ~GP_STROKE_CYCLIC; - new_stroke->tot_triangles = 0; - - /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ - new_stroke->totpoints = island->end_idx - island->start_idx + 1; - - /* Copy over the relevant point data */ - new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); - memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); - - /* Copy over vertex weight data (if available) */ - if (gps->dvert != NULL) { - /* Copy over the relevant vertex-weight points */ - new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight"); - memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints); - - /* Copy weights */ - int e = island->start_idx; - for (int i = 0; i < new_stroke->totpoints; i++) { - MDeformVert *dvert_src = &gps->dvert[e]; - MDeformVert *dvert_dst = &new_stroke->dvert[i]; - if (dvert_src->dw) { - dvert_dst->dw = MEM_dupallocN(dvert_src->dw); - } - e++; - } - } - /* Each island corresponds to a new stroke. We must adjust the - * timings of these new strokes: - * - * Each point's timing data is a delta from stroke's inittime, so as we erase some points from - * the start of the stroke, we have to offset this inittime and all remaining points' delta values. - * This way we get a new stroke with exactly the same timing as if user had started drawing from - * the first non-removed point... - */ - { - bGPDspoint *pts; - float delta = gps->points[island->start_idx].time; - int j; - - new_stroke->inittime += (double)delta; - - pts = new_stroke->points; - for (j = 0; j < new_stroke->totpoints; j++, pts++) { - pts->time -= delta; - /* set flag for select again later */ - if (select == true) { - pts->flag &= ~GP_SPOINT_SELECT; - pts->flag |= GP_SPOINT_TAG; - } - } - } - - /* Add new stroke to the frame or delete if below limit */ - if ((limit > 0) && (new_stroke->totpoints <= limit)) { - BKE_gpencil_free_stroke(new_stroke); - } - else { - if (next_stroke) { - BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke); - } - else { - BLI_addtail(&gpf->strokes, new_stroke); - } - } - } - /* if cyclic, need to join last stroke with first stroke */ - if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) { - gp_stroke_join_islands(gpf, gps_first, new_stroke); - } - - } - - /* free islands */ - MEM_freeN(islands); - - /* Delete the old stroke */ - BLI_remlink(&gpf->strokes, gps); - BKE_gpencil_free_stroke(gps); + tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, + "gp_point_islands"); + bool in_island = false; + int num_islands = 0; + + bGPDstroke *gps_first = NULL; + const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC); + + /* First Pass: Identify start/end of islands */ + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { + if (pt->flag & tag_flags) { + /* selected - stop accumulating to island */ + in_island = false; + } + else { + /* unselected - start of a new island? */ + int idx; + + if (in_island) { + /* extend existing island */ + idx = num_islands - 1; + islands[idx].end_idx = i; + } + else { + /* start of new island */ + in_island = true; + num_islands++; + + idx = num_islands - 1; + islands[idx].start_idx = islands[idx].end_idx = i; + } + } + } + + /* Watch out for special case where No islands = All points selected = Delete Stroke only */ + if (num_islands) { + /* there are islands, so create a series of new strokes, adding them before the "next" stroke */ + int idx; + bGPDstroke *new_stroke = NULL; + + /* Create each new stroke... */ + for (idx = 0; idx < num_islands; idx++) { + tGPDeleteIsland *island = &islands[idx]; + new_stroke = MEM_dupallocN(gps); + + /* if cyclic and first stroke, save to join later */ + if ((is_cyclic) && (gps_first == NULL)) { + gps_first = new_stroke; + } + + /* initialize triangle memory - to be calculated on next redraw */ + new_stroke->triangles = NULL; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + new_stroke->flag &= ~GP_STROKE_CYCLIC; + new_stroke->tot_triangles = 0; + + /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ + new_stroke->totpoints = island->end_idx - island->start_idx + 1; + + /* Copy over the relevant point data */ + new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, + "gp delete stroke fragment"); + memcpy(new_stroke->points, + gps->points + island->start_idx, + sizeof(bGPDspoint) * new_stroke->totpoints); + + /* Copy over vertex weight data (if available) */ + if (gps->dvert != NULL) { + /* Copy over the relevant vertex-weight points */ + new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, + "gp delete stroke fragment weight"); + memcpy(new_stroke->dvert, + gps->dvert + island->start_idx, + sizeof(MDeformVert) * new_stroke->totpoints); + + /* Copy weights */ + int e = island->start_idx; + for (int i = 0; i < new_stroke->totpoints; i++) { + MDeformVert *dvert_src = &gps->dvert[e]; + MDeformVert *dvert_dst = &new_stroke->dvert[i]; + if (dvert_src->dw) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + e++; + } + } + /* Each island corresponds to a new stroke. We must adjust the + * timings of these new strokes: + * + * Each point's timing data is a delta from stroke's inittime, so as we erase some points from + * the start of the stroke, we have to offset this inittime and all remaining points' delta values. + * This way we get a new stroke with exactly the same timing as if user had started drawing from + * the first non-removed point... + */ + { + bGPDspoint *pts; + float delta = gps->points[island->start_idx].time; + int j; + + new_stroke->inittime += (double)delta; + + pts = new_stroke->points; + for (j = 0; j < new_stroke->totpoints; j++, pts++) { + pts->time -= delta; + /* set flag for select again later */ + if (select == true) { + pts->flag &= ~GP_SPOINT_SELECT; + pts->flag |= GP_SPOINT_TAG; + } + } + } + + /* Add new stroke to the frame or delete if below limit */ + if ((limit > 0) && (new_stroke->totpoints <= limit)) { + BKE_gpencil_free_stroke(new_stroke); + } + else { + if (next_stroke) { + BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke); + } + else { + BLI_addtail(&gpf->strokes, new_stroke); + } + } + } + /* if cyclic, need to join last stroke with first stroke */ + if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) { + gp_stroke_join_islands(gpf, gps_first, new_stroke); + } + } + + /* free islands */ + MEM_freeN(islands); + + /* Delete the old stroke */ + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); } /* Split selected strokes into segments, splitting on selected points */ static int gp_delete_selected_points(bContext *C) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - bool changed = false; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) - continue; - - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) - continue; - - - if (gps->flag & GP_STROKE_SELECT) { - /* deselect old stroke, since it will be used as template for the new strokes */ - gps->flag &= ~GP_STROKE_SELECT; - - /* delete unwanted points by splitting stroke into several smaller ones */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); - - changed = true; - } - } - } - } - } - CTX_DATA_END; - - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bool changed = false; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + /* deselect old stroke, since it will be used as template for the new strokes */ + gps->flag &= ~GP_STROKE_SELECT; + + /* delete unwanted points by splitting stroke into several smaller ones */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } /* simple wrapper to external call */ int gp_delete_selected_point_wrap(bContext *C) { - return gp_delete_selected_points(C); + return gp_delete_selected_points(C); } /* ----------------------------------- */ static int gp_delete_exec(bContext *C, wmOperator *op) { - eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type"); - int result = OPERATOR_CANCELLED; + eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type"); + int result = OPERATOR_CANCELLED; - switch (mode) { - case GP_DELETEOP_STROKES: /* selected strokes */ - result = gp_delete_selected_strokes(C); - break; + switch (mode) { + case GP_DELETEOP_STROKES: /* selected strokes */ + result = gp_delete_selected_strokes(C); + break; - case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */ - result = gp_delete_selected_points(C); - break; + case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */ + result = gp_delete_selected_points(C); + break; - case GP_DELETEOP_FRAME: /* active frame */ - result = gp_actframe_delete_exec(C, op); - break; - } + case GP_DELETEOP_FRAME: /* active frame */ + result = gp_actframe_delete_exec(C, op); + break; + } - return result; + return result; } void GPENCIL_OT_delete(wmOperatorType *ot) { - static const EnumPropertyItem prop_gpencil_delete_types[] = { - {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"}, - {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"}, - {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Delete"; - ot->idname = "GPENCIL_OT_delete"; - ot->description = "Delete selected Grease Pencil strokes, vertices, or frames"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_delete_exec; - ot->poll = gp_stroke_edit_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - - /* props */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data"); + static const EnumPropertyItem prop_gpencil_delete_types[] = { + {GP_DELETEOP_POINTS, + "POINTS", + 0, + "Points", + "Delete selected points and split strokes into segments"}, + {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"}, + {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Delete"; + ot->idname = "GPENCIL_OT_delete"; + ot->description = "Delete selected Grease Pencil strokes, vertices, or frames"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_delete_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, + "type", + prop_gpencil_delete_types, + 0, + "Type", + "Method used for deleting Grease Pencil data"); } static int gp_dissolve_exec(bContext *C, wmOperator *op) { - eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); + eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); - return gp_dissolve_selected_points(C, mode); + return gp_dissolve_selected_points(C, mode); } void GPENCIL_OT_dissolve(wmOperatorType *ot) { - static EnumPropertyItem prop_gpencil_dissolve_types[] = { - {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"}, - {GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points"}, - {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Dissolve"; - ot->idname = "GPENCIL_OT_dissolve"; - ot->description = "Delete selected points without splitting strokes"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_dissolve_exec; - ot->poll = gp_stroke_edit_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - - /* props */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0, - "Type", "Method used for dissolving Stroke points"); + static EnumPropertyItem prop_gpencil_dissolve_types[] = { + {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"}, + {GP_DISSOLVE_BETWEEN, + "BETWEEN", + 0, + "Dissolve Between", + "Dissolve points between selected points"}, + {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Dissolve"; + ot->idname = "GPENCIL_OT_dissolve"; + ot->description = "Delete selected points without splitting strokes"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_dissolve_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, + "type", + prop_gpencil_dissolve_types, + 0, + "Type", + "Method used for dissolving Stroke points"); } /* ****************** Snapping - Strokes <-> Cursor ************************ */ @@ -2435,370 +2464,369 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot) */ static bool gp_snap_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - ScrArea *sa = CTX_wm_area(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + ScrArea *sa = CTX_wm_area(C); - return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); + return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); } /* --------------------------------- */ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - RegionView3D *rv3d = CTX_wm_region_data(C); - View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = CTX_data_active_object(C); - const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL); - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - float diff_mat[4][4]; - - /* calculate difference matrix object */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - bGPDspoint *pt; - int i; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) - continue; - - // TODO: if entire stroke is selected, offset entire stroke by same amount? - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - /* only if point is selected */ - if (pt->flag & GP_SPOINT_SELECT) { - /* apply parent transformations */ - float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - - fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); - fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); - fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); - - /* return data */ - copy_v3_v3(&pt->x, fpt); - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); - } - } - } - } - } - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + RegionView3D *rv3d = CTX_wm_region_data(C); + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL); + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + + /* calculate difference matrix object */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) + continue; + + // TODO: if entire stroke is selected, offset entire stroke by same amount? + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + /* only if point is selected */ + if (pt->flag & GP_SPOINT_SELECT) { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); + fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); + fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); + + /* return data */ + copy_v3_v3(&pt->x, fpt); + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + } + } + } + } + } + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_snap_to_grid(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Snap Selection to Grid"; - ot->idname = "GPENCIL_OT_snap_to_grid"; - ot->description = "Snap selected points to the nearest grid points"; + /* identifiers */ + ot->name = "Snap Selection to Grid"; + ot->idname = "GPENCIL_OT_snap_to_grid"; + ot->description = "Snap selected points to the nearest grid points"; - /* callbacks */ - ot->exec = gp_snap_to_grid; - ot->poll = gp_snap_poll; + /* callbacks */ + ot->exec = gp_snap_to_grid; + ot->poll = gp_snap_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ------------------------------- */ static int gp_snap_to_cursor(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = CTX_data_active_object(C); - - const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); - const float *cursor_global = scene->cursor.location; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - float diff_mat[4][4]; - - /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - bGPDspoint *pt; - int i; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) - continue; - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - if (use_offset) { - float offset[3]; - - /* compute offset from first point of stroke to cursor */ - /* TODO: Allow using midpoint instead? */ - sub_v3_v3v3(offset, cursor_global, &gps->points->x); - - /* apply offset to all points in the stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - add_v3_v3(&pt->x, offset); - } - } - else { - /* affect each selected point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - copy_v3_v3(&pt->x, cursor_global); - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); - } - } - } - } - - } - } - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + + const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); + const float *cursor_global = scene->cursor.location; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) + continue; + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + if (use_offset) { + float offset[3]; + + /* compute offset from first point of stroke to cursor */ + /* TODO: Allow using midpoint instead? */ + sub_v3_v3v3(offset, cursor_global, &gps->points->x); + + /* apply offset to all points in the stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(&pt->x, offset); + } + } + else { + /* affect each selected point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(&pt->x, cursor_global); + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + } + } + } + } + } + } + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Snap Selection to Cursor"; - ot->idname = "GPENCIL_OT_snap_to_cursor"; - ot->description = "Snap selected points/strokes to the cursor"; - - /* callbacks */ - ot->exec = gp_snap_to_cursor; - ot->poll = gp_snap_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - ot->prop = RNA_def_boolean( - ot->srna, "use_offset", true, "With Offset", - "Offset the entire stroke instead of selected points only"); + /* identifiers */ + ot->name = "Snap Selection to Cursor"; + ot->idname = "GPENCIL_OT_snap_to_cursor"; + ot->description = "Snap selected points/strokes to the cursor"; + + /* callbacks */ + ot->exec = gp_snap_to_cursor; + ot->poll = gp_snap_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_boolean(ot->srna, + "use_offset", + true, + "With Offset", + "Offset the entire stroke instead of selected points only"); } /* ------------------------------- */ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = CTX_data_active_object(C); - - float *cursor = scene->cursor.location; - float centroid[3] = {0.0f}; - float min[3], max[3]; - size_t count = 0; - - INIT_MINMAX(min, max); - - /* calculate midpoints from selected points */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - float diff_mat[4][4]; - - /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - bGPDspoint *pt; - int i; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) - continue; - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* apply parent transformations */ - float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - - add_v3_v3(centroid, fpt); - minmax_v3v3_v3(min, max, fpt); - - count++; - } - } - - } - } - } - - if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) { - mul_v3_fl(centroid, 1.0f / (float)count); - copy_v3_v3(cursor, centroid); - } - else { - mid_v3_v3v3(cursor, min, max); - } - - - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + + float *cursor = scene->cursor.location; + float centroid[3] = {0.0f}; + float min[3], max[3]; + size_t count = 0; + + INIT_MINMAX(min, max); + + /* calculate midpoints from selected points */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + float diff_mat[4][4]; + + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) + continue; + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + add_v3_v3(centroid, fpt); + minmax_v3v3_v3(min, max, fpt); + + count++; + } + } + } + } + } + + if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) { + mul_v3_fl(centroid, 1.0f / (float)count); + copy_v3_v3(cursor, centroid); + } + else { + mid_v3_v3v3(cursor, min, max); + } + + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + + return OPERATOR_FINISHED; } void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Snap Cursor to Selected Points"; - ot->idname = "GPENCIL_OT_snap_cursor_to_selected"; - ot->description = "Snap cursor to center of selected points"; + /* identifiers */ + ot->name = "Snap Cursor to Selected Points"; + ot->idname = "GPENCIL_OT_snap_cursor_to_selected"; + ot->description = "Snap cursor to center of selected points"; - /* callbacks */ - ot->exec = gp_snap_cursor_to_sel; - ot->poll = gp_snap_poll; + /* callbacks */ + ot->exec = gp_snap_cursor_to_sel; + ot->poll = gp_snap_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************* Apply layer thickness change to strokes ************************** */ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl, gpl->frames.first)) - return OPERATOR_CANCELLED; - - /* loop all strokes */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* Apply thickness */ - if ((gps->thickness == 0) && (gpl->line_change == 0)) { - gps->thickness = gpl->thickness; - } - else { - gps->thickness = gps->thickness + gpl->line_change; - } - } - } - /* clear value */ - gpl->thickness = 0.0f; - gpl->line_change = 0; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->frames.first)) + return OPERATOR_CANCELLED; + + /* loop all strokes */ + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* Apply thickness */ + if ((gps->thickness == 0) && (gpl->line_change == 0)) { + gps->thickness = gpl->thickness; + } + else { + gps->thickness = gps->thickness + gpl->line_change; + } + } + } + /* clear value */ + gpl->thickness = 0.0f; + gpl->line_change = 0; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Apply Stroke Thickness"; - ot->idname = "GPENCIL_OT_stroke_apply_thickness"; - ot->description = "Apply the thickness change of the layer to its strokes"; - - /* api callbacks */ - ot->exec = gp_stroke_apply_thickness_exec; - ot->poll = gp_active_layer_poll; + /* identifiers */ + ot->name = "Apply Stroke Thickness"; + ot->idname = "GPENCIL_OT_stroke_apply_thickness"; + ot->description = "Apply the thickness change of the layer to its strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_apply_thickness_exec; + ot->poll = gp_active_layer_poll; } /* ******************* Close Strokes ************************** */ enum { - GP_STROKE_CYCLIC_CLOSE = 1, - GP_STROKE_CYCLIC_OPEN = 2, - GP_STROKE_CYCLIC_TOGGLE = 3, + GP_STROKE_CYCLIC_CLOSE = 1, + GP_STROKE_CYCLIC_OPEN = 2, + GP_STROKE_CYCLIC_TOGGLE = 3, }; static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - - const int type = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* loop all selected strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - if (gpl->actframe == NULL) - continue; - - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); - - /* skip strokes that are not selected or invalid for current view */ - if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) - continue; - /* skip hidden or locked colors */ - if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) - continue; - - switch (type) { - case GP_STROKE_CYCLIC_CLOSE: - /* Close all (enable) */ - gps->flag |= GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_OPEN: - /* Open all (disable) */ - gps->flag &= ~GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_TOGGLE: - /* Just toggle flag... */ - gps->flag ^= GP_STROKE_CYCLIC; - break; - default: - BLI_assert(0); - break; - } - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + const int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* loop all selected strokes */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + if (gpl->actframe == NULL) + continue; + + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* skip hidden or locked colors */ + if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || + (gp_style->flag & GP_STYLE_COLOR_LOCKED)) + continue; + + switch (type) { + case GP_STROKE_CYCLIC_CLOSE: + /* Close all (enable) */ + gps->flag |= GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_OPEN: + /* Open all (disable) */ + gps->flag &= ~GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_TOGGLE: + /* Just toggle flag... */ + gps->flag ^= GP_STROKE_CYCLIC; + break; + default: + BLI_assert(0); + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } /** @@ -2807,101 +2835,92 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) */ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) { - static const EnumPropertyItem cyclic_type[] = { - {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, - {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, - {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Set Cyclical State"; - ot->idname = "GPENCIL_OT_stroke_cyclical_set"; - ot->description = "Close or open the selected stroke adding an edge from last to first point"; - - /* api callbacks */ - ot->exec = gp_stroke_cyclical_set_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); + static const EnumPropertyItem cyclic_type[] = { + {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, + {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, + {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Set Cyclical State"; + ot->idname = "GPENCIL_OT_stroke_cyclical_set"; + ot->description = "Close or open the selected stroke adding an edge from last to first point"; + + /* api callbacks */ + ot->exec = gp_stroke_cyclical_set_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); } /* ******************* Flat Stroke Caps ************************** */ enum { - GP_STROKE_CAPS_TOGGLE_BOTH = 0, - GP_STROKE_CAPS_TOGGLE_START = 1, - GP_STROKE_CAPS_TOGGLE_END = 2, - GP_STROKE_CAPS_TOGGLE_DEFAULT = 3, + GP_STROKE_CAPS_TOGGLE_BOTH = 0, + GP_STROKE_CAPS_TOGGLE_START = 1, + GP_STROKE_CAPS_TOGGLE_END = 2, + GP_STROKE_CAPS_TOGGLE_DEFAULT = 3, }; static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - - const int type = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* loop all selected strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - if (gpl->actframe == NULL) - continue; - - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); - - /* skip strokes that are not selected or invalid for current view */ - if (((gps->flag & GP_STROKE_SELECT) == 0) || - (ED_gpencil_stroke_can_use(C, gps) == false)) - { - continue; - } - /* skip hidden or locked colors */ - if (!gp_style || - (gp_style->flag & GP_STYLE_COLOR_HIDE) || - (gp_style->flag & GP_STYLE_COLOR_LOCKED)) - { - continue; - } - - if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || - (type == GP_STROKE_CAPS_TOGGLE_START)) - { - ++gps->caps[0]; - if (gps->caps[0] >= GP_STROKE_CAP_MAX) { - gps->caps[0] = GP_STROKE_CAP_ROUND; - } - } - if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || - (type == GP_STROKE_CAPS_TOGGLE_END)) - { - ++gps->caps[1]; - if (gps->caps[1] >= GP_STROKE_CAP_MAX) { - gps->caps[1] = GP_STROKE_CAP_ROUND; - } - } - if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) { - gps->caps[0] = GP_STROKE_CAP_ROUND; - gps->caps[1] = GP_STROKE_CAP_ROUND; - } - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + const int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* loop all selected strokes */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + if (gpl->actframe == NULL) + continue; + + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) { + continue; + } + /* skip hidden or locked colors */ + if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || + (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + continue; + } + + if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || (type == GP_STROKE_CAPS_TOGGLE_START)) { + ++gps->caps[0]; + if (gps->caps[0] >= GP_STROKE_CAP_MAX) { + gps->caps[0] = GP_STROKE_CAP_ROUND; + } + } + if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || (type == GP_STROKE_CAPS_TOGGLE_END)) { + ++gps->caps[1]; + if (gps->caps[1] >= GP_STROKE_CAP_MAX) { + gps->caps[1] = GP_STROKE_CAP_ROUND; + } + } + if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) { + gps->caps[0] = GP_STROKE_CAP_ROUND; + gps->caps[1] = GP_STROKE_CAP_ROUND; + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } /** @@ -2909,28 +2928,28 @@ static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op) */ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot) { - static const EnumPropertyItem toggle_type[] = { - {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""}, - {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""}, - {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""}, - {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Set Caps Mode"; - ot->idname = "GPENCIL_OT_stroke_caps_set"; - ot->description = "Change Stroke caps mode (rounded or flat)"; - - /* api callbacks */ - ot->exec = gp_stroke_caps_set_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", ""); + static const EnumPropertyItem toggle_type[] = { + {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""}, + {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""}, + {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""}, + {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Set Caps Mode"; + ot->idname = "GPENCIL_OT_stroke_caps_set"; + ot->description = "Change Stroke caps mode (rounded or flat)"; + + /* api callbacks */ + ot->exec = gp_stroke_caps_set_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", ""); } /* ******************* Stroke join ************************** */ @@ -2938,1523 +2957,1524 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot) /* Helper: flip stroke */ static void gpencil_flip_stroke(bGPDstroke *gps) { - int end = gps->totpoints - 1; - - for (int i = 0; i < gps->totpoints / 2; i++) { - bGPDspoint *point, *point2; - bGPDspoint pt; - - /* save first point */ - point = &gps->points[i]; - pt.x = point->x; - pt.y = point->y; - pt.z = point->z; - pt.flag = point->flag; - pt.pressure = point->pressure; - pt.strength = point->strength; - pt.time = point->time; - - /* replace first point with last point */ - point2 = &gps->points[end]; - point->x = point2->x; - point->y = point2->y; - point->z = point2->z; - point->flag = point2->flag; - point->pressure = point2->pressure; - point->strength = point2->strength; - point->time = point2->time; - - /* replace last point with first saved before */ - point = &gps->points[end]; - point->x = pt.x; - point->y = pt.y; - point->z = pt.z; - point->flag = pt.flag; - point->pressure = pt.pressure; - point->strength = pt.strength; - point->time = pt.time; - - end--; - } + int end = gps->totpoints - 1; + + for (int i = 0; i < gps->totpoints / 2; i++) { + bGPDspoint *point, *point2; + bGPDspoint pt; + + /* save first point */ + point = &gps->points[i]; + pt.x = point->x; + pt.y = point->y; + pt.z = point->z; + pt.flag = point->flag; + pt.pressure = point->pressure; + pt.strength = point->strength; + pt.time = point->time; + + /* replace first point with last point */ + point2 = &gps->points[end]; + point->x = point2->x; + point->y = point2->y; + point->z = point2->z; + point->flag = point2->flag; + point->pressure = point2->pressure; + point->strength = point2->strength; + point->time = point2->time; + + /* replace last point with first saved before */ + point = &gps->points[end]; + point->x = pt.x; + point->y = pt.y; + point->z = pt.z; + point->flag = pt.flag; + point->pressure = pt.pressure; + point->strength = pt.strength; + point->time = pt.time; + + end--; + } } /* Helper: copy point between strokes */ -static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, int idx, float delta[3], - float pressure, float strength, float deltatime) +static void gpencil_stroke_copy_point(bGPDstroke *gps, + bGPDspoint *point, + int idx, + float delta[3], + float pressure, + float strength, + float deltatime) { - bGPDspoint *newpoint; - - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - if (gps->dvert != NULL) { - gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); - } - gps->totpoints++; - newpoint = &gps->points[gps->totpoints - 1]; - - newpoint->x = point->x * delta[0]; - newpoint->y = point->y * delta[1]; - newpoint->z = point->z * delta[2]; - newpoint->flag = point->flag; - newpoint->pressure = pressure; - newpoint->strength = strength; - newpoint->time = point->time + deltatime; - - if (gps->dvert != NULL) { - MDeformVert *dvert = &gps->dvert[idx]; - MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1]; - - newdvert->totweight = dvert->totweight; - newdvert->dw = MEM_dupallocN(dvert->dw); - } + bGPDspoint *newpoint; + + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + if (gps->dvert != NULL) { + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); + } + gps->totpoints++; + newpoint = &gps->points[gps->totpoints - 1]; + + newpoint->x = point->x * delta[0]; + newpoint->y = point->y * delta[1]; + newpoint->z = point->z * delta[2]; + newpoint->flag = point->flag; + newpoint->pressure = pressure; + newpoint->strength = strength; + newpoint->time = point->time + deltatime; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &gps->dvert[idx]; + MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1]; + + newdvert->totweight = dvert->totweight; + newdvert->dw = MEM_dupallocN(dvert->dw); + } } /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ -static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) +static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, + bGPDstroke *gps_b, + const bool leave_gaps) { - bGPDspoint point; - bGPDspoint *pt; - int i; - float delta[3] = {1.0f, 1.0f, 1.0f}; - float deltatime = 0.0f; - - /* sanity checks */ - if (ELEM(NULL, gps_a, gps_b)) - return; - - if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) - return; - - /* define start and end points of each stroke */ - float sa[3], sb[3], ea[3], eb[3]; - pt = &gps_a->points[0]; - copy_v3_v3(sa, &pt->x); - - pt = &gps_a->points[gps_a->totpoints - 1]; - copy_v3_v3(ea, &pt->x); - - pt = &gps_b->points[0]; - copy_v3_v3(sb, &pt->x); - - pt = &gps_b->points[gps_b->totpoints - 1]; - copy_v3_v3(eb, &pt->x); - - /* review if need flip stroke B */ - float ea_sb = len_squared_v3v3(ea, sb); - float ea_eb = len_squared_v3v3(ea, eb); - /* flip if distance to end point is shorter */ - if (ea_eb < ea_sb) { - gpencil_flip_stroke(gps_b); - } - - /* don't visibly link the first and last points? */ - if (leave_gaps) { - /* 1st: add one tail point to start invisible area */ - point = gps_a->points[gps_a->totpoints - 1]; - deltatime = point.time; - gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f); - - /* 2nd: add one head point to finish invisible area */ - point = gps_b->points[0]; - gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime); - } - - /* 3rd: add all points */ - for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { - /* check if still room in buffer */ - if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { - gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime); - } - } + bGPDspoint point; + bGPDspoint *pt; + int i; + float delta[3] = {1.0f, 1.0f, 1.0f}; + float deltatime = 0.0f; + + /* sanity checks */ + if (ELEM(NULL, gps_a, gps_b)) + return; + + if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) + return; + + /* define start and end points of each stroke */ + float sa[3], sb[3], ea[3], eb[3]; + pt = &gps_a->points[0]; + copy_v3_v3(sa, &pt->x); + + pt = &gps_a->points[gps_a->totpoints - 1]; + copy_v3_v3(ea, &pt->x); + + pt = &gps_b->points[0]; + copy_v3_v3(sb, &pt->x); + + pt = &gps_b->points[gps_b->totpoints - 1]; + copy_v3_v3(eb, &pt->x); + + /* review if need flip stroke B */ + float ea_sb = len_squared_v3v3(ea, sb); + float ea_eb = len_squared_v3v3(ea, eb); + /* flip if distance to end point is shorter */ + if (ea_eb < ea_sb) { + gpencil_flip_stroke(gps_b); + } + + /* don't visibly link the first and last points? */ + if (leave_gaps) { + /* 1st: add one tail point to start invisible area */ + point = gps_a->points[gps_a->totpoints - 1]; + deltatime = point.time; + gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f); + + /* 2nd: add one head point to finish invisible area */ + point = gps_b->points[0]; + gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime); + } + + /* 3rd: add all points */ + for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { + /* check if still room in buffer */ + if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { + gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime); + } + } } static int gp_stroke_join_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); - bGPDstroke *gps, *gpsn; - Object *ob = CTX_data_active_object(C); - - bGPDframe *gpf_a = NULL; - bGPDstroke *stroke_a = NULL; - bGPDstroke *stroke_b = NULL; - bGPDstroke *new_stroke = NULL; - - const int type = RNA_enum_get(op->ptr, "type"); - const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - if (activegpl->flag & GP_LAYER_LOCKED) - return OPERATOR_CANCELLED; - - BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); - - - /* read all selected strokes */ - bool first = false; - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - - /* to join strokes, cyclic must be disabled */ - gps->flag &= ~GP_STROKE_CYCLIC; - - /* saves first frame and stroke */ - if (!first) { - first = true; - gpf_a = gpf; - stroke_a = gps; - } - else { - stroke_b = gps; - - /* create a new stroke if was not created before (only created if something to join) */ - if (new_stroke == NULL) { - new_stroke = MEM_dupallocN(stroke_a); - new_stroke->points = MEM_dupallocN(stroke_a->points); - if (stroke_a->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(stroke_a->dvert); - BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke); - } - new_stroke->triangles = NULL; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* if new, set current color */ - if (type == GP_STROKE_JOINCOPY) { - new_stroke->mat_nr = stroke_a->mat_nr; - } - } - - /* join new_stroke and stroke B. New stroke will contain all the previous data */ - gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); - - /* if join only, delete old strokes */ - if (type == GP_STROKE_JOIN) { - if (stroke_a) { - BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); - BLI_remlink(&gpf->strokes, stroke_a); - BKE_gpencil_free_stroke(stroke_a); - stroke_a = NULL; - } - if (stroke_b) { - BLI_remlink(&gpf->strokes, stroke_b); - BKE_gpencil_free_stroke(stroke_b); - stroke_b = NULL; - } - } - } - } - } - } - CTX_DATA_END; - - /* add new stroke if was not added before */ - if (type == GP_STROKE_JOINCOPY) { - if (new_stroke) { - /* Add a new frame if needed */ - if (activegpl->actframe == NULL) - activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); - - BLI_addtail(&activegpl->actframe->strokes, new_stroke); - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps, *gpsn; + Object *ob = CTX_data_active_object(C); + + bGPDframe *gpf_a = NULL; + bGPDstroke *stroke_a = NULL; + bGPDstroke *stroke_b = NULL; + bGPDstroke *new_stroke = NULL; + + const int type = RNA_enum_get(op->ptr, "type"); + const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + if (activegpl->flag & GP_LAYER_LOCKED) + return OPERATOR_CANCELLED; + + BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); + + /* read all selected strokes */ + bool first = false; + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + /* to join strokes, cyclic must be disabled */ + gps->flag &= ~GP_STROKE_CYCLIC; + + /* saves first frame and stroke */ + if (!first) { + first = true; + gpf_a = gpf; + stroke_a = gps; + } + else { + stroke_b = gps; + + /* create a new stroke if was not created before (only created if something to join) */ + if (new_stroke == NULL) { + new_stroke = MEM_dupallocN(stroke_a); + new_stroke->points = MEM_dupallocN(stroke_a->points); + if (stroke_a->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(stroke_a->dvert); + BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke); + } + new_stroke->triangles = NULL; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* if new, set current color */ + if (type == GP_STROKE_JOINCOPY) { + new_stroke->mat_nr = stroke_a->mat_nr; + } + } + + /* join new_stroke and stroke B. New stroke will contain all the previous data */ + gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); + + /* if join only, delete old strokes */ + if (type == GP_STROKE_JOIN) { + if (stroke_a) { + BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); + BLI_remlink(&gpf->strokes, stroke_a); + BKE_gpencil_free_stroke(stroke_a); + stroke_a = NULL; + } + if (stroke_b) { + BLI_remlink(&gpf->strokes, stroke_b); + BKE_gpencil_free_stroke(stroke_b); + stroke_b = NULL; + } + } + } + } + } + } + CTX_DATA_END; + + /* add new stroke if was not added before */ + if (type == GP_STROKE_JOINCOPY) { + if (new_stroke) { + /* Add a new frame if needed */ + if (activegpl->actframe == NULL) + activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); + + BLI_addtail(&activegpl->actframe->strokes, new_stroke); + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_join(wmOperatorType *ot) { - static const EnumPropertyItem join_type[] = { - {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, - {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Join Strokes"; - ot->idname = "GPENCIL_OT_stroke_join"; - ot->description = "Join selected strokes (optionally as new stroke)"; - - /* api callbacks */ - ot->exec = gp_stroke_join_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); - RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them"); + static const EnumPropertyItem join_type[] = { + {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, + {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Join Strokes"; + ot->idname = "GPENCIL_OT_stroke_join"; + ot->description = "Join selected strokes (optionally as new stroke)"; + + /* api callbacks */ + ot->exec = gp_stroke_join_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); + RNA_def_boolean(ot->srna, + "leave_gaps", + false, + "Leave Gaps", + "Leave gaps between joined strokes instead of linking them"); } /* ******************* Stroke flip ************************** */ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* read all selected strokes */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) - continue; - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - - /* flip stroke */ - gpencil_flip_stroke(gps); - } - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* read all selected strokes */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + /* flip stroke */ + gpencil_flip_stroke(gps); + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_flip(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Stroke"; - ot->idname = "GPENCIL_OT_stroke_flip"; - ot->description = "Change direction of the points of the selected strokes"; + /* identifiers */ + ot->name = "Flip Stroke"; + ot->idname = "GPENCIL_OT_stroke_flip"; + ot->description = "Change direction of the points of the selected strokes"; - /* api callbacks */ - ot->exec = gp_stroke_flip_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gp_stroke_flip_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ***************** Reproject Strokes ********************** */ typedef enum eGP_ReprojectModes { - /* Axis */ - GP_REPROJECT_FRONT = 0, - GP_REPROJECT_SIDE, - GP_REPROJECT_TOP, - /* On same plane, parallel to viewplane */ - GP_REPROJECT_VIEW, - /* Reprojected on to the scene geometry */ - GP_REPROJECT_SURFACE, - /* Reprojected on 3D cursor orientation */ - GP_REPROJECT_CURSOR, + /* Axis */ + GP_REPROJECT_FRONT = 0, + GP_REPROJECT_SIDE, + GP_REPROJECT_TOP, + /* On same plane, parallel to viewplane */ + GP_REPROJECT_VIEW, + /* Reprojected on to the scene geometry */ + GP_REPROJECT_SURFACE, + /* Reprojected on 3D cursor orientation */ + GP_REPROJECT_CURSOR, } eGP_ReprojectModes; static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = CTX_data_active_object(C); - ARegion *ar = CTX_wm_region(C); - RegionView3D *rv3d = ar->regiondata; - - GP_SpaceConversion gsc = {NULL}; - eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); - - float origin[3]; - - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); - - /* init autodist for geometry projection */ - if (mode == GP_REPROJECT_SURFACE) { - view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); - ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0); - } - - // TODO: For deforming geometry workflow, create new frames? - - /* Go through each editable + selected stroke, adjusting each of its points one by one... */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - float inverse_diff_mat[4][4]; - - /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ - /* TODO: add this bit to the iteration macro? */ - invert_m4_m4(inverse_diff_mat, gpstroke_iter.diff_mat); - - /* Adjust each point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float xy[2]; - - /* 3D to Screenspace */ - /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace - * coordinates, resulting in lost precision, which in turn causes stairstepping - * artifacts in the final points. - */ - bGPDspoint pt2; - gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); - gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); - - /* Project stroke in one axis */ - if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, - GP_REPROJECT_TOP, GP_REPROJECT_CURSOR)) - { - if (mode != GP_REPROJECT_CURSOR) { - ED_gp_get_drawing_reference( - scene, ob, gpl, - ts->gpencil_v3d_align, origin); - } - else { - copy_v3_v3(origin, scene->cursor.location); - } - - int axis = 0; - switch (mode) { - case GP_REPROJECT_FRONT: - { - axis = 1; - break; - } - case GP_REPROJECT_SIDE: - { - axis = 0; - break; - } - case GP_REPROJECT_TOP: - { - axis = 2; - break; - } - case GP_REPROJECT_CURSOR: - { - axis = 3; - break; - } - default: - { - axis = 1; - break; - } - } - - ED_gp_project_point_to_plane( - scene, ob, rv3d, origin, - axis, &pt2); - - copy_v3_v3(&pt->x, &pt2.x); - - /* apply parent again */ - gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); - } - /* Project screenspace back to 3D space (from current perspective) - * so that all points have been treated the same way - */ - else if (mode == GP_REPROJECT_VIEW) { - /* Planar - All on same plane parallel to the viewplane */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); - } - else { - /* Geometry - Snap to surfaces of visible geometry */ - /* XXX: There will be precision loss (possible stairstep artifacts) - * from this conversion to satisfy the API's */ - const int screen_co[2] = {(int)xy[0], (int)xy[1]}; - - int depth_margin = 0; // XXX: 4 for strokes, 0 for normal - float depth; - - /* XXX: The proper procedure computes the depths into an array, - * to have smooth transitions when all else fails... */ - if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) { - ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth); - } - else { - /* Default to planar */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); - } - } - - /* Unapply parent corrections */ - if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) { - mul_m4_v3(inverse_diff_mat, &pt->x); - } - } - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + GP_SpaceConversion gsc = {NULL}; + eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); + + float origin[3]; + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* init autodist for geometry projection */ + if (mode == GP_REPROJECT_SURFACE) { + view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); + ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0); + } + + // TODO: For deforming geometry workflow, create new frames? + + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + float inverse_diff_mat[4][4]; + + /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ + /* TODO: add this bit to the iteration macro? */ + invert_m4_m4(inverse_diff_mat, gpstroke_iter.diff_mat); + + /* Adjust each point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float xy[2]; + + /* 3D to Screenspace */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace + * coordinates, resulting in lost precision, which in turn causes stairstepping + * artifacts in the final points. + */ + bGPDspoint pt2; + gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + + /* Project stroke in one axis */ + if (ELEM(mode, + GP_REPROJECT_FRONT, + GP_REPROJECT_SIDE, + GP_REPROJECT_TOP, + GP_REPROJECT_CURSOR)) { + if (mode != GP_REPROJECT_CURSOR) { + ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); + } + else { + copy_v3_v3(origin, scene->cursor.location); + } + + int axis = 0; + switch (mode) { + case GP_REPROJECT_FRONT: { + axis = 1; + break; + } + case GP_REPROJECT_SIDE: { + axis = 0; + break; + } + case GP_REPROJECT_TOP: { + axis = 2; + break; + } + case GP_REPROJECT_CURSOR: { + axis = 3; + break; + } + default: { + axis = 1; + break; + } + } + + ED_gp_project_point_to_plane(scene, ob, rv3d, origin, axis, &pt2); + + copy_v3_v3(&pt->x, &pt2.x); + + /* apply parent again */ + gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); + } + /* Project screenspace back to 3D space (from current perspective) + * so that all points have been treated the same way + */ + else if (mode == GP_REPROJECT_VIEW) { + /* Planar - All on same plane parallel to the viewplane */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + else { + /* Geometry - Snap to surfaces of visible geometry */ + /* XXX: There will be precision loss (possible stairstep artifacts) + * from this conversion to satisfy the API's */ + const int screen_co[2] = {(int)xy[0], (int)xy[1]}; + + int depth_margin = 0; // XXX: 4 for strokes, 0 for normal + float depth; + + /* XXX: The proper procedure computes the depths into an array, + * to have smooth transitions when all else fails... */ + if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) { + ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth); + } + else { + /* Default to planar */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + } + + /* Unapply parent corrections */ + if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) { + mul_m4_v3(inverse_diff_mat, &pt->x); + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_reproject(wmOperatorType *ot) { - static const EnumPropertyItem reproject_type[] = { - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", - "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", - "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", - "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, "VIEW", 0, "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface", - "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"}, - {GP_REPROJECT_CURSOR, "CURSOR", 0, "Cursor", - "Reproject the strokes using the orienation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Reproject Strokes"; - ot->idname = "GPENCIL_OT_reproject"; - ot->description = "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " - "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " - "or for matching deforming geometry)"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_strokes_reproject_exec; - ot->poll = gp_strokes_edit3d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); + static const EnumPropertyItem reproject_type[] = { + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_SURFACE, + "SURFACE", + 0, + "Surface", + "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orienation of 3D cursor"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Reproject Strokes"; + ot->idname = "GPENCIL_OT_reproject"; + ot->description = + "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " + "or for matching deforming geometry)"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_strokes_reproject_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + ot->prop = RNA_def_enum( + ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); } /* ******************* Stroke subdivide ************************** */ /* helper to smooth */ static void gp_smooth_stroke(bContext *C, wmOperator *op) { - const int repeat = RNA_int_get(op->ptr, "repeat"); - float factor = RNA_float_get(op->ptr, "factor"); - const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); - const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position"); - const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness"); - const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength"); - const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv"); - - if (factor == 0.0f) { - return; - } - - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - if (gps->flag & GP_STROKE_SELECT) { - for (int r = 0; r < repeat; r++) { - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) { - continue; - } - - /* perform smoothing */ - if (smooth_position) { - BKE_gpencil_smooth_stroke(gps, i, factor); - } - if (smooth_strength) { - BKE_gpencil_smooth_stroke_strength(gps, i, factor); - } - if (smooth_thickness) { - /* thickness need to repeat process several times */ - for (int r2 = 0; r2 < r * 10; r2++) { - BKE_gpencil_smooth_stroke_thickness(gps, i, factor); - } - } - if (smooth_uv) { - BKE_gpencil_smooth_stroke_uv(gps, i, factor); - } - } - } - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); + const int repeat = RNA_int_get(op->ptr, "repeat"); + float factor = RNA_float_get(op->ptr, "factor"); + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position"); + const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness"); + const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength"); + const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv"); + + if (factor == 0.0f) { + return; + } + + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + for (int r = 0; r < repeat; r++) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) { + continue; + } + + /* perform smoothing */ + if (smooth_position) { + BKE_gpencil_smooth_stroke(gps, i, factor); + } + if (smooth_strength) { + BKE_gpencil_smooth_stroke_strength(gps, i, factor); + } + if (smooth_thickness) { + /* thickness need to repeat process several times */ + for (int r2 = 0; r2 < r * 10; r2++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, factor); + } + } + if (smooth_uv) { + BKE_gpencil_smooth_stroke_uv(gps, i, factor); + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); } /* helper: Count how many points need to be inserted */ static int gp_count_subdivision_cuts(bGPDstroke *gps) { - bGPDspoint *pt; - int i; - int totnewpoints = 0; - for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - if (i + 1 < gps->totpoints) { - if (gps->points[i + 1].flag & GP_SPOINT_SELECT) { - totnewpoints++; - } - } - } - } - - return totnewpoints; + bGPDspoint *pt; + int i; + int totnewpoints = 0; + for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (i + 1 < gps->totpoints) { + if (gps->points[i + 1].flag & GP_SPOINT_SELECT) { + totnewpoints++; + } + } + } + } + + return totnewpoints; } static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDspoint *temp_points; - const int cuts = RNA_int_get(op->ptr, "number_cuts"); - - int totnewpoints, oldtotpoints; - int i2; - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* Go through each editable + selected stroke */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - if (gps->flag & GP_STROKE_SELECT) { - /* loop as many times as cuts */ - for (int s = 0; s < cuts; s++) { - totnewpoints = gp_count_subdivision_cuts(gps); - if (totnewpoints == 0) { - continue; - } - /* duplicate points in a temp area */ - temp_points = MEM_dupallocN(gps->points); - oldtotpoints = gps->totpoints; - - MDeformVert *temp_dverts = NULL; - MDeformVert *dvert_final = NULL; - MDeformVert *dvert = NULL; - MDeformVert *dvert_next = NULL; - if (gps->dvert != NULL) { - temp_dverts = MEM_dupallocN(gps->dvert); - } - - /* resize the points arrays */ - gps->totpoints += totnewpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* loop and interpolate */ - i2 = 0; - for (int i = 0; i < oldtotpoints; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *pt_final = &gps->points[i2]; - - /* copy current point */ - copy_v3_v3(&pt_final->x, &pt->x); - pt_final->pressure = pt->pressure; - pt_final->strength = pt->strength; - pt_final->time = pt->time; - pt_final->flag = pt->flag; - - if (gps->dvert != NULL) { - dvert = &temp_dverts[i]; - dvert_final = &gps->dvert[i2]; - dvert_final->totweight = dvert->totweight; - dvert_final->dw = dvert->dw; - } - i2++; - - /* if next point is selected add a half way point */ - if (pt->flag & GP_SPOINT_SELECT) { - if (i + 1 < oldtotpoints) { - if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { - pt_final = &gps->points[i2]; - if (gps->dvert != NULL) { - dvert_final = &gps->dvert[i2]; - } - /* Interpolate all values */ - bGPDspoint *next = &temp_points[i + 1]; - interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); - pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); - pt_final->strength = interpf(pt->strength, next->strength, 0.5f); - CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt_final->time = interpf(pt->time, next->time, 0.5f); - pt_final->flag |= GP_SPOINT_SELECT; - - /* interpolate weights */ - if (gps->dvert != NULL) { - dvert = &temp_dverts[i]; - dvert_next = &temp_dverts[i + 1]; - dvert_final = &gps->dvert[i2]; - - dvert_final->totweight = dvert->totweight; - dvert_final->dw = MEM_dupallocN(dvert->dw); - - /* interpolate weight values */ - for (int d = 0; d < dvert->totweight; d++) { - MDeformWeight *dw_a = &dvert->dw[d]; - if (dvert_next->totweight > d) { - MDeformWeight *dw_b = &dvert_next->dw[d]; - MDeformWeight *dw_final = &dvert_final->dw[d]; - dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f); - } - } - } - - i2++; - } - } - } - } - /* free temp memory */ - MEM_SAFE_FREE(temp_points); - MEM_SAFE_FREE(temp_dverts); - } - - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* smooth stroke */ - gp_smooth_stroke(C, op); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDspoint *temp_points; + const int cuts = RNA_int_get(op->ptr, "number_cuts"); + + int totnewpoints, oldtotpoints; + int i2; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + /* loop as many times as cuts */ + for (int s = 0; s < cuts; s++) { + totnewpoints = gp_count_subdivision_cuts(gps); + if (totnewpoints == 0) { + continue; + } + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + MDeformVert *temp_dverts = NULL; + MDeformVert *dvert_final = NULL; + MDeformVert *dvert = NULL; + MDeformVert *dvert_next = NULL; + if (gps->dvert != NULL) { + temp_dverts = MEM_dupallocN(gps->dvert); + } + + /* resize the points arrays */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* loop and interpolate */ + i2 = 0; + for (int i = 0; i < oldtotpoints; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + + /* copy current point */ + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + + if (gps->dvert != NULL) { + dvert = &temp_dverts[i]; + dvert_final = &gps->dvert[i2]; + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + } + i2++; + + /* if next point is selected add a half way point */ + if (pt->flag & GP_SPOINT_SELECT) { + if (i + 1 < oldtotpoints) { + if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { + pt_final = &gps->points[i2]; + if (gps->dvert != NULL) { + dvert_final = &gps->dvert[i2]; + } + /* Interpolate all values */ + bGPDspoint *next = &temp_points[i + 1]; + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + pt_final->flag |= GP_SPOINT_SELECT; + + /* interpolate weights */ + if (gps->dvert != NULL) { + dvert = &temp_dverts[i]; + dvert_next = &temp_dverts[i + 1]; + dvert_final = &gps->dvert[i2]; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = MEM_dupallocN(dvert->dw); + + /* interpolate weight values */ + for (int d = 0; d < dvert->totweight; d++) { + MDeformWeight *dw_a = &dvert->dw[d]; + if (dvert_next->totweight > d) { + MDeformWeight *dw_b = &dvert_next->dw[d]; + MDeformWeight *dw_final = &dvert_final->dw[d]; + dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f); + } + } + } + + i2++; + } + } + } + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); + MEM_SAFE_FREE(temp_dverts); + } + + /* triangles cache needs to be recalculated */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* smooth stroke */ + gp_smooth_stroke(C, op); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Subdivide Stroke"; - ot->idname = "GPENCIL_OT_stroke_subdivide"; - ot->description = "Subdivide between continuous selected points of the stroke adding a point half way between them"; - - /* api callbacks */ - ot->exec = gp_stroke_subdivide_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); - /* avoid re-using last var because it can cause _very_ high value and annoy users */ - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - /* Smooth parameters */ - RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f); - prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_boolean( - ot->srna, "only_selected", true, "Selected Points", - "Smooth only selected points in the stroke"); - RNA_def_boolean(ot->srna, "smooth_position", true, "Position", ""); - RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", ""); - RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", ""); - RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", ""); - + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Subdivide Stroke"; + ot->idname = "GPENCIL_OT_stroke_subdivide"; + ot->description = + "Subdivide between continuous selected points of the stroke adding a point half way between " + "them"; + + /* api callbacks */ + ot->exec = gp_stroke_subdivide_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); + /* avoid re-using last var because it can cause _very_ high value and annoy users */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + /* Smooth parameters */ + RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f); + prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_boolean(ot->srna, + "only_selected", + true, + "Selected Points", + "Smooth only selected points in the stroke"); + RNA_def_boolean(ot->srna, "smooth_position", true, "Position", ""); + RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", ""); + RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", ""); + RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", ""); } /* ** simplify stroke *** */ static int gp_stroke_simplify_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - float factor = RNA_float_get(op->ptr, "factor"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* Go through each editable + selected stroke */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - if (gps->flag & GP_STROKE_SELECT) { - /* simplify stroke using Ramer-Douglas-Peucker algorithm */ - BKE_gpencil_simplify_stroke(gps, factor); - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + float factor = RNA_float_get(op->ptr, "factor"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + /* simplify stroke using Ramer-Douglas-Peucker algorithm */ + BKE_gpencil_simplify_stroke(gps, factor); + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_simplify(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Simplify Stroke"; - ot->idname = "GPENCIL_OT_stroke_simplify"; - ot->description = "Simplify selected stroked reducing number of points"; + /* identifiers */ + ot->name = "Simplify Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify"; + ot->description = "Simplify selected stroked reducing number of points"; - /* api callbacks */ - ot->exec = gp_stroke_simplify_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gp_stroke_simplify_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); - /* avoid re-using last var */ - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ** simplify stroke using fixed algorithm *** */ static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - int steps = RNA_int_get(op->ptr, "step"); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* Go through each editable + selected stroke */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - if (gps->flag & GP_STROKE_SELECT) { - for (int i = 0; i < steps; i++) { - BKE_gpencil_simplify_fixed(gps); - } - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + int steps = RNA_int_get(op->ptr, "step"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < steps; i++) { + BKE_gpencil_simplify_fixed(gps); + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Simplify Fixed Stroke"; - ot->idname = "GPENCIL_OT_stroke_simplify_fixed"; - ot->description = "Simplify selected stroked reducing number of points using fixed algorithm"; + PropertyRNA *prop; - /* api callbacks */ - ot->exec = gp_stroke_simplify_fixed_exec; - ot->poll = gp_active_layer_poll; + /* identifiers */ + ot->name = "Simplify Fixed Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify_fixed"; + ot->description = "Simplify selected stroked reducing number of points using fixed algorithm"; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* api callbacks */ + ot->exec = gp_stroke_simplify_fixed_exec; + ot->poll = gp_active_layer_poll; - /* properties */ - prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10); + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* avoid re-using last var */ - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* properties */ + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************* Stroke trim ************************** */ static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; - - /* Go through each editable + selected stroke */ - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_trim_stroke(gps); - } - } - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + BKE_gpencil_trim_stroke(gps); + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_trim(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Trim Stroke"; - ot->idname = "GPENCIL_OT_stroke_trim"; - ot->description = "Trim selected stroke to first loop or intersection"; + /* identifiers */ + ot->name = "Trim Stroke"; + ot->idname = "GPENCIL_OT_stroke_trim"; + ot->description = "Trim selected stroke to first loop or intersection"; - /* api callbacks */ - ot->exec = gp_stroke_trim_exec; - ot->poll = gp_active_layer_poll; + /* api callbacks */ + ot->exec = gp_stroke_trim_exec; + ot->poll = gp_active_layer_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ***************** Separate Strokes ********************** */ typedef enum eGP_SeparateModes { - /* Points */ - GP_SEPARATE_POINT = 0, - /* Selected Strokes */ - GP_SEPARATE_STROKE, - /* Current Layer */ - GP_SEPARATE_LAYER, + /* Points */ + GP_SEPARATE_POINT = 0, + /* Selected Strokes */ + GP_SEPARATE_STROKE, + /* Current Layer */ + GP_SEPARATE_LAYER, } eGP_SeparateModes; static int gp_stroke_separate_exec(bContext *C, wmOperator *op) { - Base *base_new; - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base_old = CTX_data_active_base(C); - bGPdata *gpd_src = ED_gpencil_data_get_active(C); - Object *ob = CTX_data_active_object(C); - - Object *ob_dst = NULL; - bGPdata *gpd_dst = NULL; - bGPDlayer *gpl_dst = NULL; - bGPDframe *gpf_dst = NULL; - bGPDspoint *pt; - Material *ma = NULL; - int i, idx; - - eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); - - /* sanity checks */ - if (ELEM(NULL, gpd_src)) { - return OPERATOR_CANCELLED; - } - - if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_count(&gpd_src->layers) == 1)) { - BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only"); - return OPERATOR_CANCELLED; - } - - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); - - /* create a new object */ - base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0); - ob_dst = base_new->object; - ob_dst->mode = OB_MODE_OBJECT; - /* create new grease pencil datablock */ - gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2); - ob_dst->data = (bGPdata *)gpd_dst; - - /* loop old datablock and separate parts */ - if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - gpl_dst = NULL; - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) { - continue; - } - - gpf_dst = NULL; - - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - /* separate selected strokes */ - if (gps->flag & GP_STROKE_SELECT) { - /* add layer if not created before */ - if (gpl_dst == NULL) { - gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); - } - - /* add frame if not created before */ - if (gpf_dst == NULL) { - gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); - } - - /* add duplicate materials */ - ma = give_current_material(ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */ - idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); - - /* selected points mode */ - if (mode == GP_SEPARATE_POINT) { - /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); - - /* reasign material */ - gps_dst->mat_nr = idx; - - /* link to destination frame */ - BLI_addtail(&gpf_dst->strokes, gps_dst); - - /* Invert selection status of all points in destination stroke */ - for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { - pt->flag ^= GP_SPOINT_SELECT; - } - - /* delete selected points from destination stroke */ - gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0); - - /* delete selected points from origin stroke */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); - } - /* selected strokes mode */ - else if (mode == GP_SEPARATE_STROKE) { - /* deselect old stroke */ - gps->flag &= ~GP_STROKE_SELECT; - /* unlink from source frame */ - BLI_remlink(&gpf->strokes, gps); - gps->prev = gps->next = NULL; - /* relink to destination frame */ - BLI_addtail(&gpf_dst->strokes, gps); - /* reasign material */ - gps->mat_nr = idx; - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; - } - else if (mode == GP_SEPARATE_LAYER) { - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - if (gpl) { - /* try to set a new active layer in source datablock */ - if (gpl->prev) { - BKE_gpencil_layer_setactive(gpd_src, gpl->prev); - } - else if (gpl->next) { - BKE_gpencil_layer_setactive(gpd_src, gpl->next); - } - /* unlink from source datablock */ - BLI_remlink(&gpd_src->layers, gpl); - gpl->prev = gpl->next = NULL; - /* relink to destination datablock */ - BLI_addtail(&gpd_dst->layers, gpl); - - /* add duplicate materials */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - ma = give_current_material(ob, gps->mat_nr + 1); - gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); - } - } - } - } - - DEG_id_tag_update(&gpd_src->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Base *base_new; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Base *base_old = CTX_data_active_base(C); + bGPdata *gpd_src = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + Object *ob_dst = NULL; + bGPdata *gpd_dst = NULL; + bGPDlayer *gpl_dst = NULL; + bGPDframe *gpf_dst = NULL; + bGPDspoint *pt; + Material *ma = NULL; + int i, idx; + + eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); + + /* sanity checks */ + if (ELEM(NULL, gpd_src)) { + return OPERATOR_CANCELLED; + } + + if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_count(&gpd_src->layers) == 1)) { + BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only"); + return OPERATOR_CANCELLED; + } + + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); + + /* create a new object */ + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0); + ob_dst = base_new->object; + ob_dst->mode = OB_MODE_OBJECT; + /* create new grease pencil datablock */ + gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2); + ob_dst->data = (bGPdata *)gpd_dst; + + /* loop old datablock and separate parts */ + if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + gpl_dst = NULL; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + gpf_dst = NULL; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + /* separate selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* add layer if not created before */ + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + } + + /* add frame if not created before */ + if (gpf_dst == NULL) { + gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); + } + + /* add duplicate materials */ + ma = give_current_material( + ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */ + idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); + + /* selected points mode */ + if (mode == GP_SEPARATE_POINT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* reasign material */ + gps_dst->mat_nr = idx; + + /* link to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + + /* Invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + } + /* selected strokes mode */ + else if (mode == GP_SEPARATE_STROKE) { + /* deselect old stroke */ + gps->flag &= ~GP_STROKE_SELECT; + /* unlink from source frame */ + BLI_remlink(&gpf->strokes, gps); + gps->prev = gps->next = NULL; + /* relink to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps); + /* reasign material */ + gps->mat_nr = idx; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + } + else if (mode == GP_SEPARATE_LAYER) { + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if (gpl) { + /* try to set a new active layer in source datablock */ + if (gpl->prev) { + BKE_gpencil_layer_setactive(gpd_src, gpl->prev); + } + else if (gpl->next) { + BKE_gpencil_layer_setactive(gpd_src, gpl->next); + } + /* unlink from source datablock */ + BLI_remlink(&gpd_src->layers, gpl); + gpl->prev = gpl->next = NULL; + /* relink to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl); + + /* add duplicate materials */ + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + ma = give_current_material(ob, gps->mat_nr + 1); + gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); + } + } + } + } + + DEG_id_tag_update(&gpd_src->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_separate(wmOperatorType *ot) { - static const EnumPropertyItem separate_type[] = { - {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"}, - {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"}, - {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Separate Strokes"; - ot->idname = "GPENCIL_OT_stroke_separate"; - ot->description = "Separate the selected strokes or layer in a new grease pencil object"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_stroke_separate_exec; - ot->poll = gp_strokes_edit3d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", ""); + static const EnumPropertyItem separate_type[] = { + {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"}, + {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"}, + {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Separate Strokes"; + ot->idname = "GPENCIL_OT_stroke_separate"; + ot->description = "Separate the selected strokes or layer in a new grease pencil object"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_stroke_separate_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", ""); } /* ***************** Split Strokes ********************** */ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDspoint *pt; - int i; - - /* sanity checks */ - if (ELEM(NULL, gpd)) { - return OPERATOR_CANCELLED; - } - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - /* loop strokes and split parts */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; - - if (gpf == NULL) { - continue; - } - - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - /* split selected strokes */ - if (gps->flag & GP_STROKE_SELECT) { - /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); - - /* link to same frame */ - BLI_addtail(&gpf->strokes, gps_dst); - - /* invert selection status of all points in destination stroke */ - for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { - pt->flag ^= GP_SPOINT_SELECT; - } - - /* delete selected points from destination stroke */ - gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0); - - /* delete selected points from origin stroke */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); - } - } - /* select again tagged points */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - bGPDspoint *ptn = gps->points; - for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) { - if (ptn->flag & GP_SPOINT_TAG) { - ptn->flag |= GP_SPOINT_SELECT; - ptn->flag &= ~GP_SPOINT_TAG; - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDspoint *pt; + int i; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + /* loop strokes and split parts */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + /* split selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* link to same frame */ + BLI_addtail(&gpf->strokes, gps_dst); + + /* invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + } + } + /* select again tagged points */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *ptn = gps->points; + for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) { + if (ptn->flag & GP_SPOINT_TAG) { + ptn->flag |= GP_SPOINT_SELECT; + ptn->flag &= ~GP_SPOINT_TAG; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_split(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Split Strokes"; - ot->idname = "GPENCIL_OT_stroke_split"; - ot->description = "Split selected points as new stroke on same frame"; + /* identifiers */ + ot->name = "Split Strokes"; + ot->idname = "GPENCIL_OT_stroke_split"; + ot->description = "Split selected points as new stroke on same frame"; - /* callbacks */ - ot->exec = gp_stroke_split_exec; - ot->poll = gp_strokes_edit3d_poll; + /* callbacks */ + ot->exec = gp_stroke_split_exec; + ot->poll = gp_strokes_edit3d_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int gp_stroke_smooth_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); - /* sanity checks */ - if (ELEM(NULL, gpd)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; - gp_smooth_stroke(C, op); + gp_smooth_stroke(C, op); - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_smooth(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Smooth Stroke"; - ot->idname = "GPENCIL_OT_stroke_smooth"; - ot->description = "Smooth selected strokes"; - - /* api callbacks */ - ot->exec = gp_stroke_smooth_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f); - RNA_def_boolean( - ot->srna, "only_selected", true, "Selected Points", - "Smooth only selected points in the stroke"); - RNA_def_boolean(ot->srna, "smooth_position", true, "Position", ""); - RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", ""); - RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", ""); - RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", ""); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Smooth Stroke"; + ot->idname = "GPENCIL_OT_stroke_smooth"; + ot->description = "Smooth selected strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_smooth_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f); + RNA_def_boolean(ot->srna, + "only_selected", + true, + "Selected Points", + "Smooth only selected points in the stroke"); + RNA_def_boolean(ot->srna, "smooth_position", true, "Position", ""); + RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", ""); + RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", ""); + RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", ""); } /* smart stroke cutter for trimming stroke ends */ struct GP_SelectLassoUserData { - rcti rect; - const int(*mcords)[2]; - int mcords_len; + rcti rect; + const int (*mcords)[2]; + int mcords_len; }; -static bool gpencil_test_lasso( - bGPDstroke *gps, bGPDspoint *pt, - const GP_SpaceConversion *gsc, const float diff_mat[4][4], - void *user_data) +static bool gpencil_test_lasso(bGPDstroke *gps, + bGPDspoint *pt, + const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + void *user_data) { - const struct GP_SelectLassoUserData *data = user_data; - bGPDspoint pt2; - int x0, y0; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); - /* test if in lasso */ - return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && - BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + const struct GP_SelectLassoUserData *data = user_data; + bGPDspoint pt2; + int x0, y0; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); + /* test if in lasso */ + return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && + BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); } -typedef bool(*GPencilTestFn)( - bGPDstroke *gps, bGPDspoint *pt, - const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data); +typedef bool (*GPencilTestFn)(bGPDstroke *gps, + bGPDspoint *pt, + const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + void *user_data); static void gpencil_cutter_dissolve(bGPDlayer *hit_layer, bGPDstroke *hit_stroke) { - bGPDspoint *pt = NULL; - bGPDspoint *pt1 = NULL; - int i; - - bGPDstroke *gpsn = hit_stroke->next; - - int totselect = 0; - for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - totselect++; - } - } - - /* if all points selected delete or only 2 points and 1 selected */ - if (((totselect == 1) && (hit_stroke->totpoints == 2)) || - (hit_stroke->totpoints == totselect)) - { - BLI_remlink(&hit_layer->actframe->strokes, hit_stroke); - BKE_gpencil_free_stroke(hit_stroke); - hit_stroke = NULL; - } - - /* if very small distance delete */ - if ((hit_stroke) && (hit_stroke->totpoints == 2)) { - pt = &hit_stroke->points[0]; - pt1 = &hit_stroke->points[1]; - if (len_v3v3(&pt->x, &pt1->x) < 0.001f) { - BLI_remlink(&hit_layer->actframe->strokes, hit_stroke); - BKE_gpencil_free_stroke(hit_stroke); - hit_stroke = NULL; - } - } - - if (hit_stroke) { - /* tag and dissolve (untag new points) */ - for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - pt->flag &= ~GP_SPOINT_SELECT; - pt->flag |= GP_SPOINT_TAG; - } - else if (pt->flag & GP_SPOINT_TAG) { - pt->flag &= ~GP_SPOINT_TAG; - } - } - gp_stroke_delete_tagged_points( - hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1); - } + bGPDspoint *pt = NULL; + bGPDspoint *pt1 = NULL; + int i; + + bGPDstroke *gpsn = hit_stroke->next; + + int totselect = 0; + for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + totselect++; + } + } + + /* if all points selected delete or only 2 points and 1 selected */ + if (((totselect == 1) && (hit_stroke->totpoints == 2)) || (hit_stroke->totpoints == totselect)) { + BLI_remlink(&hit_layer->actframe->strokes, hit_stroke); + BKE_gpencil_free_stroke(hit_stroke); + hit_stroke = NULL; + } + + /* if very small distance delete */ + if ((hit_stroke) && (hit_stroke->totpoints == 2)) { + pt = &hit_stroke->points[0]; + pt1 = &hit_stroke->points[1]; + if (len_v3v3(&pt->x, &pt1->x) < 0.001f) { + BLI_remlink(&hit_layer->actframe->strokes, hit_stroke); + BKE_gpencil_free_stroke(hit_stroke); + hit_stroke = NULL; + } + } + + if (hit_stroke) { + /* tag and dissolve (untag new points) */ + for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + pt->flag &= ~GP_SPOINT_SELECT; + pt->flag |= GP_SPOINT_TAG; + } + else if (pt->flag & GP_SPOINT_TAG) { + pt->flag &= ~GP_SPOINT_TAG; + } + } + gp_stroke_delete_tagged_points(hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1); + } } -static int gpencil_cutter_lasso_select( - bContext *C, wmOperator *op, - GPencilTestFn is_inside_fn, void *user_data) +static int gpencil_cutter_lasso_select(bContext *C, + wmOperator *op, + GPencilTestFn is_inside_fn, + void *user_data) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - ScrArea *sa = CTX_wm_area(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const float scale = ts->gp_sculpt.isect_threshold; - - bGPDspoint *pt; - int i; - GP_SpaceConversion gsc = { NULL }; - - bool changed = false; - - /* sanity checks */ - if (sa == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active area"); - return OPERATOR_CANCELLED; - } - - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); - - /* deselect all strokes first */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - - gps->flag &= ~GP_STROKE_SELECT; - } - CTX_DATA_END; - - /* select points */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - int tot_inside = 0; - const int oldtot = gps->totpoints; - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) { - continue; - } - /* convert point coords to screenspace */ - const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); - if (is_inside) { - tot_inside++; - changed = true; - pt->flag |= GP_SPOINT_SELECT; - gps->flag |= GP_STROKE_SELECT; - float r_hita[3], r_hitb[3]; - if (gps->totpoints > 1) { - ED_gpencil_select_stroke_segment( - gpl, gps, pt, true, true, scale, r_hita, r_hitb); - } - /* avoid infinite loops */ - if (gps->totpoints > oldtot) { - break; - } - } - } - /* if mark all points inside lasso set to remove all stroke */ - if ((tot_inside == oldtot) || - ((tot_inside == 1) && (oldtot == 2))) - { - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - pt->flag |= GP_SPOINT_SELECT; - } - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* dissolve selected points */ - bGPDstroke *gpsn; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - if (gps->flag & GP_STROKE_SELECT) { - gpencil_cutter_dissolve(gpl, gps); - } - } - } - - /* updates */ - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - } - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + ScrArea *sa = CTX_wm_area(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const float scale = ts->gp_sculpt.isect_threshold; + + bGPDspoint *pt; + int i; + GP_SpaceConversion gsc = {NULL}; + + bool changed = false; + + /* sanity checks */ + if (sa == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active area"); + return OPERATOR_CANCELLED; + } + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* deselect all strokes first */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + gps->flag &= ~GP_STROKE_SELECT; + } + CTX_DATA_END; + + /* select points */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + int tot_inside = 0; + const int oldtot = gps->totpoints; + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) { + continue; + } + /* convert point coords to screenspace */ + const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); + if (is_inside) { + tot_inside++; + changed = true; + pt->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + float r_hita[3], r_hitb[3]; + if (gps->totpoints > 1) { + ED_gpencil_select_stroke_segment(gpl, gps, pt, true, true, scale, r_hita, r_hitb); + } + /* avoid infinite loops */ + if (gps->totpoints > oldtot) { + break; + } + } + } + /* if mark all points inside lasso set to remove all stroke */ + if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) { + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + pt->flag |= GP_SPOINT_SELECT; + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* dissolve selected points */ + bGPDstroke *gpsn; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + continue; + } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->flag & GP_STROKE_SELECT) { + gpencil_cutter_dissolve(gpl, gps); + } + } + } + + /* updates */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; } static bool gpencil_cutter_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); - if (GPENCIL_PAINT_MODE(gpd)) { - if (gpd->layers.first) - return true; - } + if (GPENCIL_PAINT_MODE(gpd)) { + if (gpd->layers.first) + return true; + } - return false; + return false; } static int gpencil_cutter_exec(bContext *C, wmOperator *op) { - ScrArea *sa = CTX_wm_area(C); - /* sanity checks */ - if (sa == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active area"); - return OPERATOR_CANCELLED; - } + ScrArea *sa = CTX_wm_area(C); + /* sanity checks */ + if (sa == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active area"); + return OPERATOR_CANCELLED; + } - struct GP_SelectLassoUserData data = { 0 }; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + struct GP_SelectLassoUserData data = {0}; + data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); - /* Sanity check. */ - if (data.mcords == NULL) { - return OPERATOR_PASS_THROUGH; - } + /* Sanity check. */ + if (data.mcords == NULL) { + return OPERATOR_PASS_THROUGH; + } - /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + /* Compute boundbox of lasso (for faster testing later). */ + BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); - gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data); + gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcords); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_cutter(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Stroke Cutter"; - ot->description = "Select section and cut"; - ot->idname = "GPENCIL_OT_stroke_cutter"; - - /* callbacks */ - ot->invoke = WM_gesture_lasso_invoke; - ot->modal = WM_gesture_lasso_modal; - ot->exec = gpencil_cutter_exec; - ot->poll = gpencil_cutter_poll; - ot->cancel = WM_gesture_lasso_cancel; - - /* flag */ - ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - WM_operator_properties_gesture_lasso(ot); + /* identifiers */ + ot->name = "Stroke Cutter"; + ot->description = "Select section and cut"; + ot->idname = "GPENCIL_OT_stroke_cutter"; + + /* callbacks */ + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = gpencil_cutter_exec; + ot->poll = gpencil_cutter_poll; + ot->cancel = WM_gesture_lasso_cancel; + + /* flag */ + ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + WM_operator_properties_gesture_lasso(ot); } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 76d15557055..8d3137ed3db 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -79,373 +79,393 @@ #define LEAK_HORZ 0 #define LEAK_VERT 1 - - /* Temporary fill operation data (op->customdata) */ +/* Temporary fill operation data (op->customdata) */ typedef struct tGPDfill { - bContext *C; - struct Main *bmain; - struct Depsgraph *depsgraph; - /** window where painting originated */ - struct wmWindow *win; - /** current scene from context */ - struct Scene *scene; - /** current active gp object */ - struct Object *ob; - /** area where painting originated */ - struct ScrArea *sa; - /** region where painting originated */ - struct RegionView3D *rv3d; - /** view3 where painting originated */ - struct View3D *v3d; - /** region where painting originated */ - struct ARegion *ar; - /** current GP datablock */ - struct bGPdata *gpd; - /** current material */ - struct Material *mat; - /** layer */ - struct bGPDlayer *gpl; - /** frame */ - struct bGPDframe *gpf; - - /** flags */ - short flag; - /** avoid too fast events */ - short oldkey; - /** send to back stroke */ - bool on_back; - - /** mouse fill center position */ - int center[2]; - /** windows width */ - int sizex; - /** window height */ - int sizey; - /** lock to viewport axis */ - int lock_axis; - - /** number of pixel to consider the leak is too small (x 2) */ - short fill_leak; - /** factor for transparency */ - float fill_threshold; - /** number of simplify steps */ - int fill_simplylvl; - /** boundary limits drawing mode */ - int fill_draw_mode; - /* scaling factor */ - short fill_factor; - - /** number of elements currently in cache */ - short sbuffer_size; - /** temporary points */ - void *sbuffer; - /** depth array for reproject */ - float *depth_arr; - - /** temp image */ - Image *ima; - /** temp points data */ - BLI_Stack *stack; - /** handle for drawing strokes while operator is running 3d stuff */ - void *draw_handle_3d; - - /* tmp size x */ - int bwinx; - /* tmp size y */ - int bwiny; - rcti brect; + bContext *C; + struct Main *bmain; + struct Depsgraph *depsgraph; + /** window where painting originated */ + struct wmWindow *win; + /** current scene from context */ + struct Scene *scene; + /** current active gp object */ + struct Object *ob; + /** area where painting originated */ + struct ScrArea *sa; + /** region where painting originated */ + struct RegionView3D *rv3d; + /** view3 where painting originated */ + struct View3D *v3d; + /** region where painting originated */ + struct ARegion *ar; + /** current GP datablock */ + struct bGPdata *gpd; + /** current material */ + struct Material *mat; + /** layer */ + struct bGPDlayer *gpl; + /** frame */ + struct bGPDframe *gpf; + + /** flags */ + short flag; + /** avoid too fast events */ + short oldkey; + /** send to back stroke */ + bool on_back; + + /** mouse fill center position */ + int center[2]; + /** windows width */ + int sizex; + /** window height */ + int sizey; + /** lock to viewport axis */ + int lock_axis; + + /** number of pixel to consider the leak is too small (x 2) */ + short fill_leak; + /** factor for transparency */ + float fill_threshold; + /** number of simplify steps */ + int fill_simplylvl; + /** boundary limits drawing mode */ + int fill_draw_mode; + /* scaling factor */ + short fill_factor; + + /** number of elements currently in cache */ + short sbuffer_size; + /** temporary points */ + void *sbuffer; + /** depth array for reproject */ + float *depth_arr; + + /** temp image */ + Image *ima; + /** temp points data */ + BLI_Stack *stack; + /** handle for drawing strokes while operator is running 3d stuff */ + void *draw_handle_3d; + + /* tmp size x */ + int bwinx; + /* tmp size y */ + int bwiny; + rcti brect; } tGPDfill; - /* draw a given stroke using same thickness and color for all points */ -static void gp_draw_basic_stroke( - tGPDfill *tgpf, bGPDstroke *gps, const float diff_mat[4][4], - const bool cyclic, const float ink[4], const int flag, const float thershold) +static void gp_draw_basic_stroke(tGPDfill *tgpf, + bGPDstroke *gps, + const float diff_mat[4][4], + const bool cyclic, + const float ink[4], + const int flag, + const float thershold) { - bGPDspoint *points = gps->points; - - Material *ma = tgpf->mat; - MaterialGPencilStyle *gp_style = ma->gp_style; - - int totpoints = gps->totpoints; - float fpt[3]; - float col[4]; - - copy_v4_v4(col, ink); - - /* if cyclic needs more vertex */ - int cyclic_add = (cyclic) ? 1 : 0; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); - - /* draw stroke curve */ - GPU_line_width(1.0f); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); - const bGPDspoint *pt = points; - - for (int i = 0; i < totpoints; i++, pt++) { - - if (flag & GP_BRUSH_FILL_HIDE) { - float alpha = gp_style->stroke_rgba[3] * pt->strength; - CLAMP(alpha, 0.0f, 1.0f); - col[3] = alpha <= thershold ? 0.0f : 1.0f; - } - else { - col[3] = 1.0f; - } - /* set point */ - immAttr4fv(color, col); - mul_v3_m4v3(fpt, diff_mat, &pt->x); - immVertex3fv(pos, fpt); - } - - if (cyclic && totpoints > 2) { - /* draw line to first point to complete the cycle */ - immAttr4fv(color, col); - mul_v3_m4v3(fpt, diff_mat, &points->x); - immVertex3fv(pos, fpt); - } - - immEnd(); - immUnbindProgram(); + bGPDspoint *points = gps->points; + + Material *ma = tgpf->mat; + MaterialGPencilStyle *gp_style = ma->gp_style; + + int totpoints = gps->totpoints; + float fpt[3]; + float col[4]; + + copy_v4_v4(col, ink); + + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); + + /* draw stroke curve */ + GPU_line_width(1.0f); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + + for (int i = 0; i < totpoints; i++, pt++) { + + if (flag & GP_BRUSH_FILL_HIDE) { + float alpha = gp_style->stroke_rgba[3] * pt->strength; + CLAMP(alpha, 0.0f, 1.0f); + col[3] = alpha <= thershold ? 0.0f : 1.0f; + } + else { + col[3] = 1.0f; + } + /* set point */ + immAttr4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + immVertex3fv(pos, fpt); + } + + if (cyclic && totpoints > 2) { + /* draw line to first point to complete the cycle */ + immAttr4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &points->x); + immVertex3fv(pos, fpt); + } + + immEnd(); + immUnbindProgram(); } /* loop all layers */ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4]) { - /* duplicated: etempFlags */ - enum { - GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */ - GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */ - }; - - Object *ob = tgpf->ob; - bGPdata *gpd = tgpf->gpd; - int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); - - tGPDdraw tgpw; - tgpw.rv3d = tgpf->rv3d; - tgpw.depsgraph = tgpf->depsgraph; - tgpw.ob = ob; - tgpw.gpd = gpd; - tgpw.offsx = 0; - tgpw.offsy = 0; - tgpw.winx = tgpf->ar->winx; - tgpw.winy = tgpf->ar->winy; - tgpw.dflag = 0; - tgpw.disable_fill = 1; - tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS); - - GPU_blend(true); - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* calculate parent position */ - ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat); - - /* do not draw layer if hidden */ - if (gpl->flag & GP_LAYER_HIDE) - continue; - - /* if active layer and no keyframe, create a new one */ - if (gpl == tgpf->gpl) { - if ((gpl->actframe == NULL) || (gpl->actframe->framenum != cfra_eval)) { - BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - } - } - - /* get frame to draw */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - if (gpf == NULL) - continue; - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* check if stroke can be drawn */ - if ((gps->points == NULL) || (gps->totpoints < 2)) { - continue; - } - /* check if the color is visible */ - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); - if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) { - continue; - } - - tgpw.gps = gps; - tgpw.gpl = gpl; - tgpw.gpf = gpf; - tgpw.t_gpf = gpf; - - /* reduce thickness to avoid gaps */ - tgpw.is_fill_stroke = (tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ? false : true ; - tgpw.lthick = gpl->line_change; - tgpw.opacity = 1.0; - copy_v4_v4(tgpw.tintcolor, ink); - tgpw.onion = true; - tgpw.custonion = true; - - bool textured_stroke = (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE); - - /* normal strokes */ - if (((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) || - (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) && - !textured_stroke) - { - ED_gp_draw_fill(&tgpw); - } - - /* 3D Lines with basic shapes and invisible lines */ - if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) || - (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH) || - textured_stroke) - { - gp_draw_basic_stroke( - tgpf, gps, tgpw.diff_mat, gps->flag & GP_STROKE_CYCLIC, ink, - tgpf->flag, tgpf->fill_threshold); - } - } - } - - GPU_blend(false); + /* duplicated: etempFlags */ + enum { + GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */ + GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */ + }; + + Object *ob = tgpf->ob; + bGPdata *gpd = tgpf->gpd; + int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + tGPDdraw tgpw; + tgpw.rv3d = tgpf->rv3d; + tgpw.depsgraph = tgpf->depsgraph; + tgpw.ob = ob; + tgpw.gpd = gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpf->ar->winx; + tgpw.winy = tgpf->ar->winy; + tgpw.dflag = 0; + tgpw.disable_fill = 1; + tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS); + + GPU_blend(true); + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* calculate parent position */ + ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat); + + /* do not draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* if active layer and no keyframe, create a new one */ + if (gpl == tgpf->gpl) { + if ((gpl->actframe == NULL) || (gpl->actframe->framenum != cfra_eval)) { + BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + } + } + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if ((gps->points == NULL) || (gps->totpoints < 2)) { + continue; + } + /* check if the color is visible */ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) { + continue; + } + + tgpw.gps = gps; + tgpw.gpl = gpl; + tgpw.gpf = gpf; + tgpw.t_gpf = gpf; + + /* reduce thickness to avoid gaps */ + tgpw.is_fill_stroke = (tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ? false : true; + tgpw.lthick = gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, ink); + tgpw.onion = true; + tgpw.custonion = true; + + bool textured_stroke = (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE); + + /* normal strokes */ + if (((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) && + !textured_stroke) { + ED_gp_draw_fill(&tgpw); + } + + /* 3D Lines with basic shapes and invisible lines */ + if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH) || textured_stroke) { + gp_draw_basic_stroke(tgpf, + gps, + tgpw.diff_mat, + gps->flag & GP_STROKE_CYCLIC, + ink, + tgpf->flag, + tgpf->fill_threshold); + } + } + } + + GPU_blend(false); } /* draw strokes in offscreen buffer */ static bool gp_render_offscreen(tGPDfill *tgpf) { - bool is_ortho = false; - float winmat[4][4]; - - if (!tgpf->gpd) { - return false; - } - - /* set temporary new size */ - tgpf->bwinx = tgpf->ar->winx; - tgpf->bwiny = tgpf->ar->winy; - tgpf->brect = tgpf->ar->winrct; - - /* resize ar */ - tgpf->ar->winrct.xmin = 0; - tgpf->ar->winrct.ymin = 0; - tgpf->ar->winrct.xmax = (int)tgpf->ar->winx * tgpf->fill_factor; - tgpf->ar->winrct.ymax = (int)tgpf->ar->winy * tgpf->fill_factor; - tgpf->ar->winx = (short)abs(tgpf->ar->winrct.xmax - tgpf->ar->winrct.xmin); - tgpf->ar->winy = (short)abs(tgpf->ar->winrct.ymax - tgpf->ar->winrct.ymin); - - /* save new size */ - tgpf->sizex = (int)tgpf->ar->winx; - tgpf->sizey = (int)tgpf->ar->winy; - - /* adjust center */ - float center[2]; - center[0] = (float)tgpf->center[0] * ((float)tgpf->ar->winx / (float)tgpf->bwinx); - center[1] = (float)tgpf->center[1] * ((float)tgpf->ar->winy / (float)tgpf->bwiny); - round_v2i_v2fl(tgpf->center, center); - - char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, 0, true, false, err_out); - if (offscreen == NULL) { - printf("GPencil - Fill - Unable to create fill buffer\n"); - return false; - } - - GPU_offscreen_bind(offscreen, true); - uint flag = IB_rect | IB_rectfloat; - ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag); - - rctf viewplane; - float clip_start, clip_end; - - is_ortho = ED_view3d_viewplane_get( - tgpf->depsgraph, tgpf->v3d, tgpf->rv3d, tgpf->sizex, tgpf->sizey, - &viewplane, &clip_start, &clip_end, NULL); - if (is_ortho) { - orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clip_end, clip_end); - } - else { - perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clip_start, clip_end); - } - - GPU_matrix_push_projection(); - GPU_matrix_identity_set(); - GPU_matrix_push(); - GPU_matrix_identity_set(); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - ED_view3d_update_viewmat( - tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->ar, - NULL, winmat, NULL); - /* set for opengl */ - GPU_matrix_projection_set(tgpf->rv3d->winmat); - GPU_matrix_set(tgpf->rv3d->viewmat); - - /* draw strokes */ - float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; - gp_draw_datablock(tgpf, ink); - - GPU_matrix_pop_projection(); - GPU_matrix_pop(); - - /* create a image to see result of template */ - if (ibuf->rect_float) { - GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); - } - else if (ibuf->rect) { - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); - } - if (ibuf->rect_float && ibuf->rect) { - IMB_rect_from_float(ibuf); - } - - tgpf->ima = BKE_image_add_from_imbuf(tgpf->bmain, ibuf, "GP_fill"); - tgpf->ima->id.tag |= LIB_TAG_DOIT; - - BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); - - /* switch back to window-system-provided framebuffer */ - GPU_offscreen_unbind(offscreen, true); - GPU_offscreen_free(offscreen); - - return true; + bool is_ortho = false; + float winmat[4][4]; + + if (!tgpf->gpd) { + return false; + } + + /* set temporary new size */ + tgpf->bwinx = tgpf->ar->winx; + tgpf->bwiny = tgpf->ar->winy; + tgpf->brect = tgpf->ar->winrct; + + /* resize ar */ + tgpf->ar->winrct.xmin = 0; + tgpf->ar->winrct.ymin = 0; + tgpf->ar->winrct.xmax = (int)tgpf->ar->winx * tgpf->fill_factor; + tgpf->ar->winrct.ymax = (int)tgpf->ar->winy * tgpf->fill_factor; + tgpf->ar->winx = (short)abs(tgpf->ar->winrct.xmax - tgpf->ar->winrct.xmin); + tgpf->ar->winy = (short)abs(tgpf->ar->winrct.ymax - tgpf->ar->winrct.ymin); + + /* save new size */ + tgpf->sizex = (int)tgpf->ar->winx; + tgpf->sizey = (int)tgpf->ar->winy; + + /* adjust center */ + float center[2]; + center[0] = (float)tgpf->center[0] * ((float)tgpf->ar->winx / (float)tgpf->bwinx); + center[1] = (float)tgpf->center[1] * ((float)tgpf->ar->winy / (float)tgpf->bwiny); + round_v2i_v2fl(tgpf->center, center); + + char err_out[256] = "unknown"; + GPUOffScreen *offscreen = GPU_offscreen_create( + tgpf->sizex, tgpf->sizey, 0, true, false, err_out); + if (offscreen == NULL) { + printf("GPencil - Fill - Unable to create fill buffer\n"); + return false; + } + + GPU_offscreen_bind(offscreen, true); + uint flag = IB_rect | IB_rectfloat; + ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag); + + rctf viewplane; + float clip_start, clip_end; + + is_ortho = ED_view3d_viewplane_get(tgpf->depsgraph, + tgpf->v3d, + tgpf->rv3d, + tgpf->sizex, + tgpf->sizey, + &viewplane, + &clip_start, + &clip_end, + NULL); + if (is_ortho) { + orthographic_m4(winmat, + viewplane.xmin, + viewplane.xmax, + viewplane.ymin, + viewplane.ymax, + -clip_end, + clip_end); + } + else { + perspective_m4(winmat, + viewplane.xmin, + viewplane.xmax, + viewplane.ymin, + viewplane.ymax, + clip_start, + clip_end); + } + + GPU_matrix_push_projection(); + GPU_matrix_identity_set(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ED_view3d_update_viewmat(tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->ar, NULL, winmat, NULL); + /* set for opengl */ + GPU_matrix_projection_set(tgpf->rv3d->winmat); + GPU_matrix_set(tgpf->rv3d->viewmat); + + /* draw strokes */ + float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + gp_draw_datablock(tgpf, ink); + + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + /* create a image to see result of template */ + if (ibuf->rect_float) { + GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + } + else if (ibuf->rect) { + GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + } + if (ibuf->rect_float && ibuf->rect) { + IMB_rect_from_float(ibuf); + } + + tgpf->ima = BKE_image_add_from_imbuf(tgpf->bmain, ibuf, "GP_fill"); + tgpf->ima->id.tag |= LIB_TAG_DOIT; + + BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); + + /* switch back to window-system-provided framebuffer */ + GPU_offscreen_unbind(offscreen, true); + GPU_offscreen_free(offscreen); + + return true; } /* return pixel data (rgba) at index */ static void get_pixel(const ImBuf *ibuf, const int idx, float r_col[4]) { - if (ibuf->rect_float) { - const float *frgba = &ibuf->rect_float[idx * 4]; - copy_v4_v4(r_col, frgba); - } - else { - /* XXX: This case probably doesn't happen, as we only write to the float buffer, - * but we get compiler warnings about uninitialised vars otherwise - */ - BLI_assert(!"gpencil_fill.c - get_pixel() non-float case is used!"); - zero_v4(r_col); - } + if (ibuf->rect_float) { + const float *frgba = &ibuf->rect_float[idx * 4]; + copy_v4_v4(r_col, frgba); + } + else { + /* XXX: This case probably doesn't happen, as we only write to the float buffer, + * but we get compiler warnings about uninitialised vars otherwise + */ + BLI_assert(!"gpencil_fill.c - get_pixel() non-float case is used!"); + zero_v4(r_col); + } } /* set pixel data (rgba) at index */ static void set_pixel(ImBuf *ibuf, int idx, const float col[4]) { - //BLI_assert(idx <= ibuf->x * ibuf->y); - if (ibuf->rect) { - uint *rrect = &ibuf->rect[idx]; - uchar ccol[4]; - - rgba_float_to_uchar(ccol, col); - *rrect = *((uint *)ccol); - } - - if (ibuf->rect_float) { - float *rrectf = &ibuf->rect_float[idx * 4]; - copy_v4_v4(rrectf, col); - } + //BLI_assert(idx <= ibuf->x * ibuf->y); + if (ibuf->rect) { + uint *rrect = &ibuf->rect[idx]; + uchar ccol[4]; + + rgba_float_to_uchar(ccol, col); + *rrect = *((uint *)ccol); + } + + if (ibuf->rect_float) { + float *rrectf = &ibuf->rect_float[idx * 4]; + copy_v4_v4(rrectf, col); + } } /* check if the size of the leak is narrow to determine if the stroke is closed @@ -460,96 +480,96 @@ static void set_pixel(ImBuf *ibuf, int idx, const float col[4]) */ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type) { - float rgba[4]; - int i; - int pt; - bool t_a = false; - bool t_b = false; - - /* Horizontal leak (check vertical pixels) - * X - * X - * xB7 - * X - * X - */ - if (type == LEAK_HORZ) { - /* pixels on top */ - for (i = 1; i <= limit; i++) { - pt = index + (ibuf->x * i); - if (pt <= maxpixel) { - get_pixel(ibuf, pt, rgba); - if (rgba[0] == 1.0f) { - t_a = true; - break; - } - } - else { - /* edge of image*/ - t_a = true; - break; - } - } - /* pixels on bottom */ - for (i = 1; i <= limit; i++) { - pt = index - (ibuf->x * i); - if (pt >= 0) { - get_pixel(ibuf, pt, rgba); - if (rgba[0] == 1.0f) { - t_b = true; - break; - } - } - else { - /* edge of image*/ - t_b = true; - break; - } - } - } - - /* Vertical leak (check horizontal pixels) - * - * XXXxB7XX - */ - if (type == LEAK_VERT) { - /* get pixel range of the row */ - int row = index / ibuf->x; - int lowpix = row * ibuf->x; - int higpix = lowpix + ibuf->x - 1; - - /* pixels to right */ - for (i = 0; i < limit; i++) { - pt = index - (limit - i); - if (pt >= lowpix) { - get_pixel(ibuf, pt, rgba); - if (rgba[0] == 1.0f) { - t_a = true; - break; - } - } - else { - t_a = true; /* edge of image*/ - break; - } - } - /* pixels to left */ - for (i = 0; i < limit; i++) { - pt = index + (limit - i); - if (pt <= higpix) { - get_pixel(ibuf, pt, rgba); - if (rgba[0] == 1.0f) { - t_b = true; - break; - } - } - else { - t_b = true; /* edge of image */ - break; - } - } - } - return (bool)(t_a && t_b); + float rgba[4]; + int i; + int pt; + bool t_a = false; + bool t_b = false; + + /* Horizontal leak (check vertical pixels) + * X + * X + * xB7 + * X + * X + */ + if (type == LEAK_HORZ) { + /* pixels on top */ + for (i = 1; i <= limit; i++) { + pt = index + (ibuf->x * i); + if (pt <= maxpixel) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + /* edge of image*/ + t_a = true; + break; + } + } + /* pixels on bottom */ + for (i = 1; i <= limit; i++) { + pt = index - (ibuf->x * i); + if (pt >= 0) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + /* edge of image*/ + t_b = true; + break; + } + } + } + + /* Vertical leak (check horizontal pixels) + * + * XXXxB7XX + */ + if (type == LEAK_VERT) { + /* get pixel range of the row */ + int row = index / ibuf->x; + int lowpix = row * ibuf->x; + int higpix = lowpix + ibuf->x - 1; + + /* pixels to right */ + for (i = 0; i < limit; i++) { + pt = index - (limit - i); + if (pt >= lowpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + t_a = true; /* edge of image*/ + break; + } + } + /* pixels to left */ + for (i = 0; i < limit; i++) { + pt = index + (limit - i); + if (pt <= higpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + t_b = true; /* edge of image */ + break; + } + } + } + return (bool)(t_a && t_b); } /* Boundary fill inside strokes @@ -560,122 +580,122 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index */ static void gpencil_boundaryfill_area(tGPDfill *tgpf) { - ImBuf *ibuf; - float rgba[4]; - void *lock; - const float fill_col[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; - ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); - const int maxpixel = (ibuf->x * ibuf->y) - 1; - - BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); - - /* calculate index of the seed point using the position of the mouse */ - int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0]; - if ((index >= 0) && (index <= maxpixel)) { - BLI_stack_push(stack, &index); - } - - /* the fill use a stack to save the pixel list instead of the common recursive - * 4-contact point method. - * The problem with recursive calls is that for big fill areas, we can get max limit - * of recursive calls and STACK_OVERFLOW error. - * - * The 4-contact point analyze the pixels to the left, right, bottom and top - * ----------- - * | X | - * | XoX | - * | X | - * ----------- - */ - while (!BLI_stack_is_empty(stack)) { - int v; - - BLI_stack_pop(stack, &v); - - get_pixel(ibuf, v, rgba); - - if (true) { /* Was: 'rgba' */ - /* check if no border(red) or already filled color(green) */ - if ((rgba[0] != 1.0f) && (rgba[1] != 1.0f)) { - /* fill current pixel with green */ - set_pixel(ibuf, v, fill_col); - - /* add contact pixels */ - /* pixel left */ - if (v - 1 >= 0) { - index = v - 1; - if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { - BLI_stack_push(stack, &index); - } - } - /* pixel right */ - if (v + 1 <= maxpixel) { - index = v + 1; - if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { - BLI_stack_push(stack, &index); - } - } - /* pixel top */ - if (v + ibuf->x <= maxpixel) { - index = v + ibuf->x; - if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { - BLI_stack_push(stack, &index); - } - } - /* pixel bottom */ - if (v - ibuf->x >= 0) { - index = v - ibuf->x; - if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { - BLI_stack_push(stack, &index); - } - } - } - } - } - - /* release ibuf */ - if (ibuf) { - BKE_image_release_ibuf(tgpf->ima, ibuf, lock); - } - - tgpf->ima->id.tag |= LIB_TAG_DOIT; - /* free temp stack data */ - BLI_stack_free(stack); + ImBuf *ibuf; + float rgba[4]; + void *lock; + const float fill_col[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + const int maxpixel = (ibuf->x * ibuf->y) - 1; + + BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); + + /* calculate index of the seed point using the position of the mouse */ + int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0]; + if ((index >= 0) && (index <= maxpixel)) { + BLI_stack_push(stack, &index); + } + + /* the fill use a stack to save the pixel list instead of the common recursive + * 4-contact point method. + * The problem with recursive calls is that for big fill areas, we can get max limit + * of recursive calls and STACK_OVERFLOW error. + * + * The 4-contact point analyze the pixels to the left, right, bottom and top + * ----------- + * | X | + * | XoX | + * | X | + * ----------- + */ + while (!BLI_stack_is_empty(stack)) { + int v; + + BLI_stack_pop(stack, &v); + + get_pixel(ibuf, v, rgba); + + if (true) { /* Was: 'rgba' */ + /* check if no border(red) or already filled color(green) */ + if ((rgba[0] != 1.0f) && (rgba[1] != 1.0f)) { + /* fill current pixel with green */ + set_pixel(ibuf, v, fill_col); + + /* add contact pixels */ + /* pixel left */ + if (v - 1 >= 0) { + index = v - 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel right */ + if (v + 1 <= maxpixel) { + index = v + 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel top */ + if (v + ibuf->x <= maxpixel) { + index = v + ibuf->x; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + /* pixel bottom */ + if (v - ibuf->x >= 0) { + index = v - ibuf->x; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + } + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; + /* free temp stack data */ + BLI_stack_free(stack); } /* clean external border of image to avoid infinite loops */ static void gpencil_clean_borders(tGPDfill *tgpf) { - ImBuf *ibuf; - void *lock; - const float fill_col[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); - int idx; - int pixel = 0; - - /* horizontal lines */ - for (idx = 0; idx < ibuf->x; idx++) { - /* bottom line */ - set_pixel(ibuf, idx, fill_col); - /* top line */ - pixel = idx + (ibuf->x * (ibuf->y - 1)); - set_pixel(ibuf, pixel, fill_col); - } - /* vertical lines */ - for (idx = 0; idx < ibuf->y; idx++) { - /* left line */ - set_pixel(ibuf, ibuf->x * idx, fill_col); - /* right line */ - pixel = ibuf->x * idx + (ibuf->x - 1); - set_pixel(ibuf, pixel, fill_col); - } - - /* release ibuf */ - if (ibuf) { - BKE_image_release_ibuf(tgpf->ima, ibuf, lock); - } - - tgpf->ima->id.tag |= LIB_TAG_DOIT; + ImBuf *ibuf; + void *lock; + const float fill_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int idx; + int pixel = 0; + + /* horizontal lines */ + for (idx = 0; idx < ibuf->x; idx++) { + /* bottom line */ + set_pixel(ibuf, idx, fill_col); + /* top line */ + pixel = idx + (ibuf->x * (ibuf->y - 1)); + set_pixel(ibuf, pixel, fill_col); + } + /* vertical lines */ + for (idx = 0; idx < ibuf->y; idx++) { + /* left line */ + set_pixel(ibuf, ibuf->x * idx, fill_col); + /* right line */ + pixel = ibuf->x * idx + (ibuf->x - 1); + set_pixel(ibuf, pixel, fill_col); + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; } /* Naive dilate @@ -691,96 +711,96 @@ static void gpencil_clean_borders(tGPDfill *tgpf) */ static void dilate(ImBuf *ibuf) { - BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); - const float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; - const int maxpixel = (ibuf->x * ibuf->y) - 1; - /* detect pixels and expand into red areas */ - for (int v = maxpixel; v != 0; v--) { - float color[4]; - int index; - int tp = 0; - int bm = 0; - int lt = 0; - int rt = 0; - get_pixel(ibuf, v, color); - if (color[1] == 1.0f) { - /* pixel left */ - if (v - 1 >= 0) { - index = v - 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - lt = index; - } - } - /* pixel right */ - if (v + 1 <= maxpixel) { - index = v + 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - rt = index; - } - } - /* pixel top */ - if (v + ibuf->x <= maxpixel) { - index = v + ibuf->x; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - tp = index; - } - } - /* pixel bottom */ - if (v - ibuf->x >= 0) { - index = v - ibuf->x; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - bm = index; - } - } - /* pixel top-left */ - if (tp && lt) { - index = tp - 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - } - } - /* pixel top-right */ - if (tp && rt) { - index = tp + 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - } - } - /* pixel bottom-left */ - if (bm && lt) { - index = bm - 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - } - } - /* pixel bottom-right */ - if (bm && rt) { - index = bm + 1; - get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { - BLI_stack_push(stack, &index); - } - } - } - } - /* set dilated pixels */ - while (!BLI_stack_is_empty(stack)) { - int v; - BLI_stack_pop(stack, &v); - set_pixel(ibuf, v, green); - } - BLI_stack_free(stack); + BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); + const float green[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + const int maxpixel = (ibuf->x * ibuf->y) - 1; + /* detect pixels and expand into red areas */ + for (int v = maxpixel; v != 0; v--) { + float color[4]; + int index; + int tp = 0; + int bm = 0; + int lt = 0; + int rt = 0; + get_pixel(ibuf, v, color); + if (color[1] == 1.0f) { + /* pixel left */ + if (v - 1 >= 0) { + index = v - 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + lt = index; + } + } + /* pixel right */ + if (v + 1 <= maxpixel) { + index = v + 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + rt = index; + } + } + /* pixel top */ + if (v + ibuf->x <= maxpixel) { + index = v + ibuf->x; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + tp = index; + } + } + /* pixel bottom */ + if (v - ibuf->x >= 0) { + index = v - ibuf->x; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + bm = index; + } + } + /* pixel top-left */ + if (tp && lt) { + index = tp - 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + } + } + /* pixel top-right */ + if (tp && rt) { + index = tp + 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + } + } + /* pixel bottom-left */ + if (bm && lt) { + index = bm - 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + } + } + /* pixel bottom-right */ + if (bm && rt) { + index = bm + 1; + get_pixel(ibuf, index, color); + if (color[0] == 1.0f) { + BLI_stack_push(stack, &index); + } + } + } + } + /* set dilated pixels */ + while (!BLI_stack_is_empty(stack)) { + int v; + BLI_stack_pop(stack, &v); + set_pixel(ibuf, v, green); + } + BLI_stack_free(stack); } /* Get the outline points of a shape using Moore Neighborhood algorithm @@ -790,331 +810,325 @@ static void dilate(ImBuf *ibuf) */ static void gpencil_get_outline_points(tGPDfill *tgpf) { - ImBuf *ibuf; - float rgba[4]; - void *lock; - int v[2]; - int boundary_co[2]; - int start_co[2]; - int backtracked_co[2]; - int current_check_co[2]; - int prev_check_co[2]; - int backtracked_offset[1][2] = {{0, 0}}; - // bool boundary_found = false; - bool start_found = false; - const int NEIGHBOR_COUNT = 8; - - const int offset[8][2] = { - {-1, -1}, - {0, -1}, - {1, -1}, - {1, 0}, - {1, 1}, - {0, 1}, - {-1, 1}, - {-1, 0}, - }; - - tgpf->stack = BLI_stack_new(sizeof(int[2]), __func__); - - ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); - int imagesize = ibuf->x * ibuf->y; - - /* dilate */ - dilate(ibuf); - - /* find the initial point to start outline analysis */ - for (int idx = imagesize - 1; idx != 0; idx--) { - get_pixel(ibuf, idx, rgba); - if (rgba[1] == 1.0f) { - boundary_co[0] = idx % ibuf->x; - boundary_co[1] = idx / ibuf->x; - copy_v2_v2_int(start_co, boundary_co); - backtracked_co[0] = (idx - 1) % ibuf->x; - backtracked_co[1] = (idx - 1) / ibuf->x; - backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; - backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; - copy_v2_v2_int(prev_check_co, start_co); - - BLI_stack_push(tgpf->stack, &boundary_co); - start_found = true; - break; - } - } - - while (start_found) { - int cur_back_offset = -1; - for (int i = 0; i < NEIGHBOR_COUNT; i++) { - if (backtracked_offset[0][0] == offset[i][0] && - backtracked_offset[0][1] == offset[i][1]) - { - /* Finding the bracktracked pixel offset index */ - cur_back_offset = i; - break; - } - } - - int loop = 0; - while (loop < (NEIGHBOR_COUNT - 1) && cur_back_offset != -1) { - int offset_idx = (cur_back_offset + 1) % NEIGHBOR_COUNT; - current_check_co[0] = boundary_co[0] + offset[offset_idx][0]; - current_check_co[1] = boundary_co[1] + offset[offset_idx][1]; - - int image_idx = ibuf->x * current_check_co[1] + current_check_co[0]; - get_pixel(ibuf, image_idx, rgba); - - /* find next boundary pixel */ - if (rgba[1] == 1.0f) { - copy_v2_v2_int(boundary_co, current_check_co); - copy_v2_v2_int(backtracked_co, prev_check_co); - backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; - backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; - - BLI_stack_push(tgpf->stack, &boundary_co); - - break; - } - copy_v2_v2_int(prev_check_co, current_check_co); - cur_back_offset++; - loop++; - } - /* current pixel is equal to starting pixel */ - if (boundary_co[0] == start_co[0] && - boundary_co[1] == start_co[1]) - { - BLI_stack_pop(tgpf->stack, &v); - // boundary_found = true; - break; - } - } - - /* release ibuf */ - if (ibuf) { - BKE_image_release_ibuf(tgpf->ima, ibuf, lock); - } + ImBuf *ibuf; + float rgba[4]; + void *lock; + int v[2]; + int boundary_co[2]; + int start_co[2]; + int backtracked_co[2]; + int current_check_co[2]; + int prev_check_co[2]; + int backtracked_offset[1][2] = {{0, 0}}; + // bool boundary_found = false; + bool start_found = false; + const int NEIGHBOR_COUNT = 8; + + const int offset[8][2] = { + {-1, -1}, + {0, -1}, + {1, -1}, + {1, 0}, + {1, 1}, + {0, 1}, + {-1, 1}, + {-1, 0}, + }; + + tgpf->stack = BLI_stack_new(sizeof(int[2]), __func__); + + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int imagesize = ibuf->x * ibuf->y; + + /* dilate */ + dilate(ibuf); + + /* find the initial point to start outline analysis */ + for (int idx = imagesize - 1; idx != 0; idx--) { + get_pixel(ibuf, idx, rgba); + if (rgba[1] == 1.0f) { + boundary_co[0] = idx % ibuf->x; + boundary_co[1] = idx / ibuf->x; + copy_v2_v2_int(start_co, boundary_co); + backtracked_co[0] = (idx - 1) % ibuf->x; + backtracked_co[1] = (idx - 1) / ibuf->x; + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + copy_v2_v2_int(prev_check_co, start_co); + + BLI_stack_push(tgpf->stack, &boundary_co); + start_found = true; + break; + } + } + + while (start_found) { + int cur_back_offset = -1; + for (int i = 0; i < NEIGHBOR_COUNT; i++) { + if (backtracked_offset[0][0] == offset[i][0] && backtracked_offset[0][1] == offset[i][1]) { + /* Finding the bracktracked pixel offset index */ + cur_back_offset = i; + break; + } + } + + int loop = 0; + while (loop < (NEIGHBOR_COUNT - 1) && cur_back_offset != -1) { + int offset_idx = (cur_back_offset + 1) % NEIGHBOR_COUNT; + current_check_co[0] = boundary_co[0] + offset[offset_idx][0]; + current_check_co[1] = boundary_co[1] + offset[offset_idx][1]; + + int image_idx = ibuf->x * current_check_co[1] + current_check_co[0]; + get_pixel(ibuf, image_idx, rgba); + + /* find next boundary pixel */ + if (rgba[1] == 1.0f) { + copy_v2_v2_int(boundary_co, current_check_co); + copy_v2_v2_int(backtracked_co, prev_check_co); + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + + BLI_stack_push(tgpf->stack, &boundary_co); + + break; + } + copy_v2_v2_int(prev_check_co, current_check_co); + cur_back_offset++; + loop++; + } + /* current pixel is equal to starting pixel */ + if (boundary_co[0] == start_co[0] && boundary_co[1] == start_co[1]) { + BLI_stack_pop(tgpf->stack, &v); + // boundary_found = true; + break; + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } } /* get z-depth array to reproject on surface */ static void gpencil_get_depth_array(tGPDfill *tgpf) { - tGPspoint *ptc; - ToolSettings *ts = tgpf->scene->toolsettings; - int totpoints = tgpf->sbuffer_size; - int i = 0; - - if (totpoints == 0) { - return; - } - - /* for surface sketching, need to set the right OpenGL context stuff so that - * the conversions will project the values correctly... - */ - if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) { - /* need to restore the original projection settings before packing up */ - view3d_region_operator_needs_opengl(tgpf->win, tgpf->ar); - ED_view3d_autodist_init(tgpf->depsgraph, tgpf->ar, tgpf->v3d, 0); - - /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ - int depth_margin = 0; - - /* get an array of depths, far depths are blended */ - int mval_prev[2] = { 0 }; - int interp_depth = 0; - int found_depth = 0; - - tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); - - for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { - - int mval_i[2]; - round_v2i_v2fl(mval_i, &ptc->x); - - if ((ED_view3d_autodist_depth( - tgpf->ar, mval_i, depth_margin, tgpf->depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - tgpf->ar, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) - { - interp_depth = true; - } - else { - found_depth = true; - } - - copy_v2_v2_int(mval_prev, mval_i); - } - - if (found_depth == false) { - /* eeh... not much we can do.. :/, ignore depth in this case */ - for (i = totpoints - 1; i >= 0; i--) - tgpf->depth_arr[i] = 0.9999f; - } - else { - if (interp_depth) { - interp_sparse_array(tgpf->depth_arr, totpoints, FLT_MAX); - } - } - } + tGPspoint *ptc; + ToolSettings *ts = tgpf->scene->toolsettings; + int totpoints = tgpf->sbuffer_size; + int i = 0; + + if (totpoints == 0) { + return; + } + + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) { + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(tgpf->win, tgpf->ar); + ED_view3d_autodist_init(tgpf->depsgraph, tgpf->ar, tgpf->v3d, 0); + + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = 0; + + /* get an array of depths, far depths are blended */ + int mval_prev[2] = {0}; + int interp_depth = 0; + int found_depth = 0; + + tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); + + for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { + + int mval_i[2]; + round_v2i_v2fl(mval_i, &ptc->x); + + if ((ED_view3d_autodist_depth(tgpf->ar, mval_i, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg( + tgpf->ar, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { + interp_depth = true; + } + else { + found_depth = true; + } + + copy_v2_v2_int(mval_prev, mval_i); + } + + if (found_depth == false) { + /* eeh... not much we can do.. :/, ignore depth in this case */ + for (i = totpoints - 1; i >= 0; i--) + tgpf->depth_arr[i] = 0.9999f; + } + else { + if (interp_depth) { + interp_sparse_array(tgpf->depth_arr, totpoints, FLT_MAX); + } + } + } } /* create array of points using stack as source */ static void gpencil_points_from_stack(tGPDfill *tgpf) { - tGPspoint *point2D; - int totpoints = BLI_stack_count(tgpf->stack); - if (totpoints == 0) { - return; - } - - tgpf->sbuffer_size = (short)totpoints; - tgpf->sbuffer = MEM_callocN(sizeof(tGPspoint) * totpoints, __func__); - - point2D = tgpf->sbuffer; - while (!BLI_stack_is_empty(tgpf->stack)) { - int v[2]; - BLI_stack_pop(tgpf->stack, &v); - copy_v2fl_v2i(&point2D->x, v); - /* shift points to center of pixel */ - add_v2_fl(&point2D->x, 0.5f); - point2D->pressure = 1.0f; - point2D->strength = 1.0f; - point2D->time = 0.0f; - point2D++; - } + tGPspoint *point2D; + int totpoints = BLI_stack_count(tgpf->stack); + if (totpoints == 0) { + return; + } + + tgpf->sbuffer_size = (short)totpoints; + tgpf->sbuffer = MEM_callocN(sizeof(tGPspoint) * totpoints, __func__); + + point2D = tgpf->sbuffer; + while (!BLI_stack_is_empty(tgpf->stack)) { + int v[2]; + BLI_stack_pop(tgpf->stack, &v); + copy_v2fl_v2i(&point2D->x, v); + /* shift points to center of pixel */ + add_v2_fl(&point2D->x, 0.5f); + point2D->pressure = 1.0f; + point2D->strength = 1.0f; + point2D->time = 0.0f; + point2D++; + } } /* create a grease pencil stroke using points in buffer */ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) { - const int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); - - ToolSettings *ts = tgpf->scene->toolsettings; - const char *align_flag = &ts->gpencil_v3d_align; - const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); - const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && - (tgpf->rv3d->persp == RV3D_CAMOB) && (!is_depth); - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - if (brush == NULL) { - return; - } - - bGPDspoint *pt; - MDeformVert *dvert = NULL; - tGPspoint *point2D; - - if (tgpf->sbuffer_size == 0) { - return; - } - - /* get frame or create a new one */ - tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - - /* create new stroke */ - bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); - gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); - gps->inittime = 0.0f; - - /* the polygon must be closed, so enabled cyclic */ - gps->flag |= GP_STROKE_CYCLIC; - gps->flag |= GP_STROKE_3DSPACE; - - gps->mat_nr = BKE_gpencil_object_material_ensure(tgpf->bmain, tgpf->ob, tgpf->mat); - - /* allocate memory for storage points */ - gps->totpoints = tgpf->sbuffer_size; - gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_size, "gp_stroke_points"); - - /* initialize triangle memory to dummy data */ - gps->tot_triangles = 0; - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* add stroke to frame */ - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)) { - BLI_addhead(&tgpf->gpf->strokes, gps); - } - else { - BLI_addtail(&tgpf->gpf->strokes, gps); - } - - /* add points */ - pt = gps->points; - point2D = (tGPspoint *)tgpf->sbuffer; - - const int def_nr = tgpf->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpf->ob->defbase, def_nr); - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - dvert = gps->dvert; - } - - for (int i = 0; i < tgpf->sbuffer_size && point2D; i++, point2D++, pt++) { - /* convert screen-coordinates to 3D coordinates */ - gp_stroke_convertcoords_tpoint( - tgpf->scene, tgpf->ar, tgpf->ob, - tgpf->gpl, point2D, - tgpf->depth_arr ? tgpf->depth_arr + i : NULL, - &pt->x); - - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = 0.0f; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - - dvert++; - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - dvert++; - } - } - } - - /* smooth stroke */ - float reduce = 0.0f; - float smoothfac = 1.0f; - for (int r = 0; r < 1; r++) { - for (int i = 0; i < gps->totpoints; i++) { - BKE_gpencil_smooth_stroke(gps, i, smoothfac - reduce); - } - reduce += 0.25f; // reduce the factor - } - - /* if axis locked, reproject to plane locked */ - if ((tgpf->lock_axis > GP_LOCKAXIS_VIEW) && ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) { - float origin[3]; - ED_gp_get_drawing_reference( - tgpf->scene, tgpf->ob, tgpf->gpl, - ts->gpencil_v3d_align, origin); - ED_gp_project_stroke_to_plane( - tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, - tgpf->lock_axis - 1); - } - - /* if parented change position relative to parent object */ - for (int a = 0; a < tgpf->sbuffer_size; a++) { - pt = &gps->points[a]; - gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt); - } - - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(tgpf->C, tgpf->gpl, gps); - } - - /* simplify stroke */ - for (int b = 0; b < tgpf->fill_simplylvl; b++) { - BKE_gpencil_simplify_fixed(gps); - } + const int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + ToolSettings *ts = tgpf->scene->toolsettings; + const char *align_flag = &ts->gpencil_v3d_align; + const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); + const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && + (tgpf->rv3d->persp == RV3D_CAMOB) && (!is_depth); + Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + if (brush == NULL) { + return; + } + + bGPDspoint *pt; + MDeformVert *dvert = NULL; + tGPspoint *point2D; + + if (tgpf->sbuffer_size == 0) { + return; + } + + /* get frame or create a new one */ + tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + + /* create new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); + gps->thickness = brush->size; + gps->gradient_f = brush->gpencil_settings->gradient_f; + copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->inittime = 0.0f; + + /* the polygon must be closed, so enabled cyclic */ + gps->flag |= GP_STROKE_CYCLIC; + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_gpencil_object_material_ensure(tgpf->bmain, tgpf->ob, tgpf->mat); + + /* allocate memory for storage points */ + gps->totpoints = tgpf->sbuffer_size; + gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_size, "gp_stroke_points"); + + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* add stroke to frame */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)) { + BLI_addhead(&tgpf->gpf->strokes, gps); + } + else { + BLI_addtail(&tgpf->gpf->strokes, gps); + } + + /* add points */ + pt = gps->points; + point2D = (tGPspoint *)tgpf->sbuffer; + + const int def_nr = tgpf->ob->actdef - 1; + const bool have_weight = (bool)BLI_findlink(&tgpf->ob->defbase, def_nr); + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + dvert = gps->dvert; + } + + for (int i = 0; i < tgpf->sbuffer_size && point2D; i++, point2D++, pt++) { + /* convert screen-coordinates to 3D coordinates */ + gp_stroke_convertcoords_tpoint(tgpf->scene, + tgpf->ar, + tgpf->ob, + tgpf->gpl, + point2D, + tgpf->depth_arr ? tgpf->depth_arr + i : NULL, + &pt->x); + + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = 0.0f; + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + + dvert++; + } + else { + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + dvert++; + } + } + } + + /* smooth stroke */ + float reduce = 0.0f; + float smoothfac = 1.0f; + for (int r = 0; r < 1; r++) { + for (int i = 0; i < gps->totpoints; i++) { + BKE_gpencil_smooth_stroke(gps, i, smoothfac - reduce); + } + reduce += 0.25f; // reduce the factor + } + + /* if axis locked, reproject to plane locked */ + if ((tgpf->lock_axis > GP_LOCKAXIS_VIEW) && + ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) { + float origin[3]; + ED_gp_get_drawing_reference(tgpf->scene, tgpf->ob, tgpf->gpl, ts->gpencil_v3d_align, origin); + ED_gp_project_stroke_to_plane( + tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, tgpf->lock_axis - 1); + } + + /* if parented change position relative to parent object */ + for (int a = 0; a < tgpf->sbuffer_size; a++) { + pt = &gps->points[a]; + gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt); + } + + /* if camera view, reproject flat to view to avoid perspective effect */ + if (is_camera) { + ED_gpencil_project_stroke_to_view(tgpf->C, tgpf->gpl, gps); + } + + /* simplify stroke */ + for (int b = 0; b < tgpf->fill_simplylvl; b++) { + BKE_gpencil_simplify_fixed(gps); + } } /* ----------------------- */ @@ -1122,357 +1136,356 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) /* Helper: Draw status message while the user is running the operator */ static void gpencil_fill_status_indicators(bContext *C, tGPDfill *UNUSED(tgpf)) { - const char *status_str = IFACE_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back"); - ED_workspace_status_text(C, status_str); + const char *status_str = IFACE_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back"); + ED_workspace_status_text(C, status_str); } /* draw boundary lines to see fill limits */ static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgpf) { - if (!tgpf->gpd) { - return; - } - const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; - gp_draw_datablock(tgpf, ink); + if (!tgpf->gpd) { + return; + } + const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + gp_draw_datablock(tgpf, ink); } /* Drawing callback for modal operator in 3d mode */ static void gpencil_fill_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) { - tGPDfill *tgpf = (tGPDfill *)arg; - /* draw only in the region that originated operator. This is required for multiwindow */ - ARegion *ar = CTX_wm_region(C); - if (ar != tgpf->ar) { - return; - } - - gpencil_draw_boundary_lines(C, tgpf); + tGPDfill *tgpf = (tGPDfill *)arg; + /* draw only in the region that originated operator. This is required for multiwindow */ + ARegion *ar = CTX_wm_region(C); + if (ar != tgpf->ar) { + return; + } + + gpencil_draw_boundary_lines(C, tgpf); } /* check if context is suitable for filling */ static bool gpencil_fill_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); - - if (ED_operator_regionactive(C)) { - ScrArea *sa = CTX_wm_area(C); - if (sa->spacetype == SPACE_VIEW3D) { - if ((obact == NULL) || - (obact->type != OB_GPENCIL) || - (obact->mode != OB_MODE_PAINT_GPENCIL)) - { - return false; - } - - return true; - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator"); - return false; - } - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not set"); - return false; - } + Object *obact = CTX_data_active_object(C); + + if (ED_operator_regionactive(C)) { + ScrArea *sa = CTX_wm_area(C); + if (sa->spacetype == SPACE_VIEW3D) { + if ((obact == NULL) || (obact->type != OB_GPENCIL) || + (obact->mode != OB_MODE_PAINT_GPENCIL)) { + return false; + } + + return true; + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator"); + return false; + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return false; + } } /* Allocate memory and initialize values */ static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op)) { - tGPDfill *tgpf = MEM_callocN(sizeof(tGPDfill), "GPencil Fill Data"); - - /* define initial values */ - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - Main *bmain = CTX_data_main(C); - - /* set current scene and window info */ - tgpf->C = C; - tgpf->bmain = CTX_data_main(C); - tgpf->scene = CTX_data_scene(C); - tgpf->ob = CTX_data_active_object(C); - tgpf->sa = CTX_wm_area(C); - tgpf->ar = CTX_wm_region(C); - tgpf->rv3d = tgpf->ar->regiondata; - tgpf->v3d = tgpf->sa->spacedata.first; - tgpf->depsgraph = CTX_data_depsgraph(C); - tgpf->win = CTX_wm_window(C); - - /* set GP datablock */ - tgpf->gpd = gpd; - tgpf->gpl = BKE_gpencil_layer_getactive(gpd); - if (tgpf->gpl == NULL) { - tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); - } - tgpf->lock_axis = ts->gp_sculpt.lock_axis; - - tgpf->oldkey = -1; - tgpf->sbuffer_size = 0; - tgpf->sbuffer = NULL; - tgpf->depth_arr = NULL; - - /* save filling parameters */ - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - tgpf->flag = brush->gpencil_settings->flag; - tgpf->fill_leak = brush->gpencil_settings->fill_leak; - tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; - tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; - tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; - tgpf->fill_factor = (short)max_ii(1, min_ii((int)brush->gpencil_settings->fill_factor, 8)); - - int totcol = tgpf->ob->totcol; - - /* get color info */ - Material *ma = BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, tgpf->ob, brush); - - tgpf->mat = ma; - - /* check whether the material was newly added */ - if (totcol != tgpf->ob->totcol) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); - } - - /* init undo */ - gpencil_undo_init(tgpf->gpd); - - /* return context data for running operator */ - return tgpf; + tGPDfill *tgpf = MEM_callocN(sizeof(tGPDfill), "GPencil Fill Data"); + + /* define initial values */ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + + /* set current scene and window info */ + tgpf->C = C; + tgpf->bmain = CTX_data_main(C); + tgpf->scene = CTX_data_scene(C); + tgpf->ob = CTX_data_active_object(C); + tgpf->sa = CTX_wm_area(C); + tgpf->ar = CTX_wm_region(C); + tgpf->rv3d = tgpf->ar->regiondata; + tgpf->v3d = tgpf->sa->spacedata.first; + tgpf->depsgraph = CTX_data_depsgraph(C); + tgpf->win = CTX_wm_window(C); + + /* set GP datablock */ + tgpf->gpd = gpd; + tgpf->gpl = BKE_gpencil_layer_getactive(gpd); + if (tgpf->gpl == NULL) { + tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); + } + tgpf->lock_axis = ts->gp_sculpt.lock_axis; + + tgpf->oldkey = -1; + tgpf->sbuffer_size = 0; + tgpf->sbuffer = NULL; + tgpf->depth_arr = NULL; + + /* save filling parameters */ + Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + tgpf->flag = brush->gpencil_settings->flag; + tgpf->fill_leak = brush->gpencil_settings->fill_leak; + tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; + tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; + tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; + tgpf->fill_factor = (short)max_ii(1, min_ii((int)brush->gpencil_settings->fill_factor, 8)); + + int totcol = tgpf->ob->totcol; + + /* get color info */ + Material *ma = BKE_gpencil_object_material_ensure_from_active_input_brush( + bmain, tgpf->ob, brush); + + tgpf->mat = ma; + + /* check whether the material was newly added */ + if (totcol != tgpf->ob->totcol) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); + } + + /* init undo */ + gpencil_undo_init(tgpf->gpd); + + /* return context data for running operator */ + return tgpf; } /* end operator */ static void gpencil_fill_exit(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - - /* clear undo stack */ - gpencil_undo_finish(); - - /* restore cursor to indicate end of fill */ - WM_cursor_modal_restore(CTX_wm_window(C)); - - tGPDfill *tgpf = op->customdata; - - /* don't assume that operator data exists at all */ - if (tgpf) { - /* clear status message area */ - ED_workspace_status_text(C, NULL); - - MEM_SAFE_FREE(tgpf->sbuffer); - MEM_SAFE_FREE(tgpf->depth_arr); - - /* remove drawing handler */ - if (tgpf->draw_handle_3d) { - ED_region_draw_cb_exit(tgpf->ar->type, tgpf->draw_handle_3d); - } - - /* delete temp image */ - if (tgpf->ima) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (ima == tgpf->ima) { - BLI_remlink(&bmain->images, ima); - BKE_image_free(tgpf->ima); - MEM_SAFE_FREE(tgpf->ima); - break; - } - } - } - - /* finally, free memory used by temp data */ - MEM_freeN(tgpf); - } - - /* clear pointer */ - op->customdata = NULL; - - /* drawing batch cache is dirty now */ - if ((ob) && (ob->type == OB_GPENCIL) && (ob->data)) { - bGPdata *gpd2 = ob->data; - DEG_id_tag_update(&gpd2->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - gpd2->flag |= GP_DATA_CACHE_IS_DIRTY; - } - - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + + /* clear undo stack */ + gpencil_undo_finish(); + + /* restore cursor to indicate end of fill */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + tGPDfill *tgpf = op->customdata; + + /* don't assume that operator data exists at all */ + if (tgpf) { + /* clear status message area */ + ED_workspace_status_text(C, NULL); + + MEM_SAFE_FREE(tgpf->sbuffer); + MEM_SAFE_FREE(tgpf->depth_arr); + + /* remove drawing handler */ + if (tgpf->draw_handle_3d) { + ED_region_draw_cb_exit(tgpf->ar->type, tgpf->draw_handle_3d); + } + + /* delete temp image */ + if (tgpf->ima) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + if (ima == tgpf->ima) { + BLI_remlink(&bmain->images, ima); + BKE_image_free(tgpf->ima); + MEM_SAFE_FREE(tgpf->ima); + break; + } + } + } + + /* finally, free memory used by temp data */ + MEM_freeN(tgpf); + } + + /* clear pointer */ + op->customdata = NULL; + + /* drawing batch cache is dirty now */ + if ((ob) && (ob->type == OB_GPENCIL) && (ob->data)) { + bGPdata *gpd2 = ob->data; + DEG_id_tag_update(&gpd2->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd2->flag |= GP_DATA_CACHE_IS_DIRTY; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } static void gpencil_fill_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_fill_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_fill_exit(C, op); } /* Init: Allocate memory and set init values */ static int gpencil_fill_init(bContext *C, wmOperator *op) { - tGPDfill *tgpf; - /* cannot paint in locked layer */ - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - if ((gpl) && (gpl->flag & GP_LAYER_LOCKED)) { - return 0; - } - - /* check context */ - tgpf = op->customdata = gp_session_init_fill(C, op); - if (tgpf == NULL) { - /* something wasn't set correctly in context */ - gpencil_fill_exit(C, op); - return 0; - } - - /* everything is now setup ok */ - return 1; + tGPDfill *tgpf; + /* cannot paint in locked layer */ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + if ((gpl) && (gpl->flag & GP_LAYER_LOCKED)) { + return 0; + } + + /* check context */ + tgpf = op->customdata = gp_session_init_fill(C, op); + if (tgpf == NULL) { + /* something wasn't set correctly in context */ + gpencil_fill_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; } /* start of interactive part of operator */ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - tGPDfill *tgpf = NULL; + tGPDfill *tgpf = NULL; - /* try to initialize context data needed */ - if (!gpencil_fill_init(C, op)) { - gpencil_fill_exit(C, op); - if (op->customdata) - MEM_freeN(op->customdata); - return OPERATOR_CANCELLED; - } - else { - tgpf = op->customdata; - } + /* try to initialize context data needed */ + if (!gpencil_fill_init(C, op)) { + gpencil_fill_exit(C, op); + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpf = op->customdata; + } - /* Enable custom drawing handlers to show help lines */ - if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) { - tgpf->draw_handle_3d = ED_region_draw_cb_activate(tgpf->ar->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); - } + /* Enable custom drawing handlers to show help lines */ + if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) { + tgpf->draw_handle_3d = ED_region_draw_cb_activate( + tgpf->ar->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); + } - WM_cursor_modal_set(CTX_wm_window(C), BC_PAINTBRUSHCURSOR); + WM_cursor_modal_set(CTX_wm_window(C), BC_PAINTBRUSHCURSOR); - gpencil_fill_status_indicators(C, tgpf); + gpencil_fill_status_indicators(C, tgpf); - DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - /* add a modal handler for this operator*/ - WM_event_add_modal_handler(C, op); + /* add a modal handler for this operator*/ + WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } /* events handling during interactive part of operator */ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGPDfill *tgpf = op->customdata; - - int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ - - switch (event->type) { - case ESCKEY: - case RIGHTMOUSE: - estate = OPERATOR_CANCELLED; - break; - case LEFTMOUSE: - tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); - /* first time the event is not enabled to show help lines */ - if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) { - ARegion *ar = BKE_area_find_region_xy(CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); - if (ar) { - bool in_bounds = false; - - /* Perform bounds check */ - in_bounds = BLI_rcti_isect_pt(&ar->winrct, event->x, event->y); - - if ((in_bounds) && (ar->regiontype == RGN_TYPE_WINDOW)) { - /* TODO GPXX: Verify the mouse click is right for any window size */ - tgpf->center[0] = event->mval[0]; - tgpf->center[1] = event->mval[1]; - - /* render screen to temp image */ - if ( gp_render_offscreen(tgpf) ) { - - /* apply boundary fill */ - gpencil_boundaryfill_area(tgpf); - - /* clean borders to avoid infinite loops */ - gpencil_clean_borders(tgpf); - - /* analyze outline */ - gpencil_get_outline_points(tgpf); - - /* create array of points from stack */ - gpencil_points_from_stack(tgpf); - - /* create z-depth array for reproject */ - gpencil_get_depth_array(tgpf); - - /* create stroke and reproject */ - gpencil_stroke_from_buffer(tgpf); - - } - - /* restore size */ - tgpf->ar->winx = (short)tgpf->bwinx; - tgpf->ar->winy = (short)tgpf->bwiny; - tgpf->ar->winrct = tgpf->brect; - - /* free temp stack data */ - if (tgpf->stack) { - BLI_stack_free(tgpf->stack); - } - - /* push undo data */ - gpencil_undo_push(tgpf->gpd); - - estate = OPERATOR_FINISHED; - } - else { - estate = OPERATOR_CANCELLED; - } - } - else { - estate = OPERATOR_CANCELLED; - } - } - tgpf->oldkey = event->type; - break; - } - /* process last operations before exiting */ - switch (estate) { - case OPERATOR_FINISHED: - gpencil_fill_exit(C, op); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - break; - - case OPERATOR_CANCELLED: - gpencil_fill_exit(C, op); - break; - - case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: - break; - } - - /* return status code */ - return estate; + tGPDfill *tgpf = op->customdata; + + int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ + + switch (event->type) { + case ESCKEY: + case RIGHTMOUSE: + estate = OPERATOR_CANCELLED; + break; + case LEFTMOUSE: + tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); + /* first time the event is not enabled to show help lines */ + if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) { + ARegion *ar = BKE_area_find_region_xy(CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); + if (ar) { + bool in_bounds = false; + + /* Perform bounds check */ + in_bounds = BLI_rcti_isect_pt(&ar->winrct, event->x, event->y); + + if ((in_bounds) && (ar->regiontype == RGN_TYPE_WINDOW)) { + /* TODO GPXX: Verify the mouse click is right for any window size */ + tgpf->center[0] = event->mval[0]; + tgpf->center[1] = event->mval[1]; + + /* render screen to temp image */ + if (gp_render_offscreen(tgpf)) { + + /* apply boundary fill */ + gpencil_boundaryfill_area(tgpf); + + /* clean borders to avoid infinite loops */ + gpencil_clean_borders(tgpf); + + /* analyze outline */ + gpencil_get_outline_points(tgpf); + + /* create array of points from stack */ + gpencil_points_from_stack(tgpf); + + /* create z-depth array for reproject */ + gpencil_get_depth_array(tgpf); + + /* create stroke and reproject */ + gpencil_stroke_from_buffer(tgpf); + } + + /* restore size */ + tgpf->ar->winx = (short)tgpf->bwinx; + tgpf->ar->winy = (short)tgpf->bwiny; + tgpf->ar->winrct = tgpf->brect; + + /* free temp stack data */ + if (tgpf->stack) { + BLI_stack_free(tgpf->stack); + } + + /* push undo data */ + gpencil_undo_push(tgpf->gpd); + + estate = OPERATOR_FINISHED; + } + else { + estate = OPERATOR_CANCELLED; + } + } + else { + estate = OPERATOR_CANCELLED; + } + } + tgpf->oldkey = event->type; + break; + } + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + gpencil_fill_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_fill_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + break; + } + + /* return status code */ + return estate; } void GPENCIL_OT_fill(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Grease Pencil Fill"; - ot->idname = "GPENCIL_OT_fill"; - ot->description = "Fill with color the shape formed by strokes"; + /* identifiers */ + ot->name = "Grease Pencil Fill"; + ot->idname = "GPENCIL_OT_fill"; + ot->description = "Fill with color the shape formed by strokes"; - /* api callbacks */ - ot->invoke = gpencil_fill_invoke; - ot->modal = gpencil_fill_modal; - ot->poll = gpencil_fill_poll; - ot->cancel = gpencil_fill_cancel; + /* api callbacks */ + ot->invoke = gpencil_fill_invoke; + ot->modal = gpencil_fill_modal; + ot->poll = gpencil_fill_poll; + ot->cancel = gpencil_fill_cancel; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - prop = RNA_def_boolean(ot->srna, "on_back", false, "Draw On Back", "Send new stroke to Back"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "on_back", false, "Draw On Back", "Send new stroke to Back"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 3742ca92551..c8d2547ad51 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -24,12 +24,10 @@ #ifndef __GPENCIL_INTERN_H__ #define __GPENCIL_INTERN_H__ - #include "DNA_vec_types.h" #include "ED_numinput.h" - /* internal exports only */ struct Material; struct bGPDspoint; @@ -53,7 +51,6 @@ struct EnumPropertyItem; struct PointerRNA; struct PropertyRNA; - /* ***************************************************** */ /* Modal Operator Geometry Preview * @@ -73,172 +70,171 @@ struct PropertyRNA; /* Temporary draw data (no draw manager mode) */ typedef struct tGPDdraw { - struct RegionView3D *rv3d; /* region to draw */ - struct Depsgraph *depsgraph; /* depsgraph */ - struct Object *ob; /* GP object */ - struct bGPdata *gpd; /* current GP datablock */ - struct bGPDlayer *gpl; /* layer */ - struct bGPDframe *gpf; /* frame */ - struct bGPDframe *t_gpf; /* temporal frame */ - struct bGPDstroke *gps; /* stroke */ - int disable_fill; /* disable fill */ - int offsx; /* windows offset x */ - int offsy; /* windows offset y */ - int winx; /* windows width */ - int winy; /* windows height */ - int dflag; /* flags datablock */ - short lthick; /* layer thickness */ - float opacity; /* opacity */ - float tintcolor[4]; /* tint color */ - bool onion; /* onion flag */ - bool custonion; /* use custom onion colors */ - bool is_fill_stroke; /* use fill tool */ - float diff_mat[4][4]; /* matrix */ + struct RegionView3D *rv3d; /* region to draw */ + struct Depsgraph *depsgraph; /* depsgraph */ + struct Object *ob; /* GP object */ + struct bGPdata *gpd; /* current GP datablock */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + struct bGPDframe *t_gpf; /* temporal frame */ + struct bGPDstroke *gps; /* stroke */ + int disable_fill; /* disable fill */ + int offsx; /* windows offset x */ + int offsy; /* windows offset y */ + int winx; /* windows width */ + int winy; /* windows height */ + int dflag; /* flags datablock */ + short lthick; /* layer thickness */ + float opacity; /* opacity */ + float tintcolor[4]; /* tint color */ + bool onion; /* onion flag */ + bool custonion; /* use custom onion colors */ + bool is_fill_stroke; /* use fill tool */ + float diff_mat[4][4]; /* matrix */ } tGPDdraw; - /* Temporary interpolate operation data */ typedef struct tGPDinterpolate_layer { - struct tGPDinterpolate_layer *next, *prev; - - /** layer */ - struct bGPDlayer *gpl; - /** frame before current frame (interpolate-from) */ - struct bGPDframe *prevFrame; - /** frame after current frame (interpolate-to) */ - struct bGPDframe *nextFrame; - /** interpolated frame */ - struct bGPDframe *interFrame; - /** interpolate factor */ - float factor; + struct tGPDinterpolate_layer *next, *prev; + + /** layer */ + struct bGPDlayer *gpl; + /** frame before current frame (interpolate-from) */ + struct bGPDframe *prevFrame; + /** frame after current frame (interpolate-to) */ + struct bGPDframe *nextFrame; + /** interpolated frame */ + struct bGPDframe *interFrame; + /** interpolate factor */ + float factor; } tGPDinterpolate_layer; typedef struct tGPDinterpolate { - /** current scene from context */ - struct Scene *scene; - /** area where painting originated */ - struct ScrArea *sa; - /** region where painting originated */ - struct ARegion *ar; - /** current GP datablock */ - struct bGPdata *gpd; - /** current material */ - struct Material *mat; - - /** current frame number */ - int cframe; - /** (tGPDinterpolate_layer) layers to be interpolated */ - ListBase ilayers; - /** value for determining the displacement influence */ - float shift; - /** initial interpolation factor for active layer */ - float init_factor; - /** shift low limit (-100%) */ - float low_limit; - /** shift upper limit (200%) */ - float high_limit; - /** flag from toolsettings */ - int flag; - - NumInput num; /* numeric input */ - /** handle for drawing strokes while operator is running 3d stuff */ - void *draw_handle_3d; - /** handle for drawing strokes while operator is running screen stuff */ - void *draw_handle_screen; + /** current scene from context */ + struct Scene *scene; + /** area where painting originated */ + struct ScrArea *sa; + /** region where painting originated */ + struct ARegion *ar; + /** current GP datablock */ + struct bGPdata *gpd; + /** current material */ + struct Material *mat; + + /** current frame number */ + int cframe; + /** (tGPDinterpolate_layer) layers to be interpolated */ + ListBase ilayers; + /** value for determining the displacement influence */ + float shift; + /** initial interpolation factor for active layer */ + float init_factor; + /** shift low limit (-100%) */ + float low_limit; + /** shift upper limit (200%) */ + float high_limit; + /** flag from toolsettings */ + int flag; + + NumInput num; /* numeric input */ + /** handle for drawing strokes while operator is running 3d stuff */ + void *draw_handle_3d; + /** handle for drawing strokes while operator is running screen stuff */ + void *draw_handle_screen; } tGPDinterpolate; - /* Temporary primitive operation data */ typedef struct tGPDprimitive { - /** main database pointer */ - struct Main *bmain; - struct Depsgraph *depsgraph; - /** window where painting originated */ - struct wmWindow *win; - /** current scene from context */ - struct Scene *scene; - /** current active gp object */ - struct Object *ob; - /** area where painting originated */ - struct ScrArea *sa; - /** region where painting originated */ - struct RegionView3D *rv3d; - /** view3d where painting originated */ - struct View3D *v3d; - /** region where painting originated */ - struct ARegion *ar; - /** current GP datablock */ - struct bGPdata *gpd; - /** current material */ - struct Material *mat; - /** current brush */ - struct Brush *brush; - - /** current frame number */ - int cframe; - /** layer */ - struct bGPDlayer *gpl; - /** frame */ - struct bGPDframe *gpf; - /** type of primitive */ - int type; - /** original type of primitive */ - int orign_type; - /** type of primitive is a curve */ - bool curve; - /** brush size */ - int brush_size; - /** brush strength */ - float brush_strength; - /** flip option */ - short flip; - /** array of data-points for stroke */ - tGPspoint *points; - /** number of edges allocated */ - int point_count; - /** stored number of polygon edges */ - int tot_stored_edges; - /** number of polygon edges */ - int tot_edges; - /** move distance */ - float move[2]; - /** initial box corner */ - float origin[2]; - /** first box corner */ - float start[2]; - /** last box corner */ - float end[2]; - /** midpoint box corner */ - float midpoint[2]; - /** first control point */ - float cp1[2]; - /** second control point */ - float cp2[2]; - /** flag to determine control point is selected */ - int sel_cp; - /** flag to determine operations in progress */ - int flag; - /** recorded mouse-position */ - float mval[2]; - /** previous recorded mouse-position */ - float mvalo[2]; - - /** lock to viewport axis */ - int lock_axis; - struct RNG *rng; - - /** numeric input */ - NumInput num; - - /** size in pixels for uv calculation */ - float totpixlen; + /** main database pointer */ + struct Main *bmain; + struct Depsgraph *depsgraph; + /** window where painting originated */ + struct wmWindow *win; + /** current scene from context */ + struct Scene *scene; + /** current active gp object */ + struct Object *ob; + /** area where painting originated */ + struct ScrArea *sa; + /** region where painting originated */ + struct RegionView3D *rv3d; + /** view3d where painting originated */ + struct View3D *v3d; + /** region where painting originated */ + struct ARegion *ar; + /** current GP datablock */ + struct bGPdata *gpd; + /** current material */ + struct Material *mat; + /** current brush */ + struct Brush *brush; + + /** current frame number */ + int cframe; + /** layer */ + struct bGPDlayer *gpl; + /** frame */ + struct bGPDframe *gpf; + /** type of primitive */ + int type; + /** original type of primitive */ + int orign_type; + /** type of primitive is a curve */ + bool curve; + /** brush size */ + int brush_size; + /** brush strength */ + float brush_strength; + /** flip option */ + short flip; + /** array of data-points for stroke */ + tGPspoint *points; + /** number of edges allocated */ + int point_count; + /** stored number of polygon edges */ + int tot_stored_edges; + /** number of polygon edges */ + int tot_edges; + /** move distance */ + float move[2]; + /** initial box corner */ + float origin[2]; + /** first box corner */ + float start[2]; + /** last box corner */ + float end[2]; + /** midpoint box corner */ + float midpoint[2]; + /** first control point */ + float cp1[2]; + /** second control point */ + float cp2[2]; + /** flag to determine control point is selected */ + int sel_cp; + /** flag to determine operations in progress */ + int flag; + /** recorded mouse-position */ + float mval[2]; + /** previous recorded mouse-position */ + float mvalo[2]; + + /** lock to viewport axis */ + int lock_axis; + struct RNG *rng; + + /** numeric input */ + NumInput num; + + /** size in pixels for uv calculation */ + float totpixlen; } tGPDprimitive; - /* Modal Operator Drawing Callbacks ------------------------ */ -void ED_gp_draw_interpolation(const struct bContext *C, struct tGPDinterpolate *tgpi, const int type); +void ED_gp_draw_interpolation(const struct bContext *C, + struct tGPDinterpolate *tgpi, + const int type); void ED_gp_draw_fill(struct tGPDdraw *tgpw); /* ***************************************************** */ @@ -248,55 +244,74 @@ void ED_gp_draw_fill(struct tGPDdraw *tgpw); /* gpencil_utils.c */ typedef struct GP_SpaceConversion { - struct Scene *scene; - struct Object *ob; - struct bGPdata *gpd; - struct bGPDlayer *gpl; + struct Scene *scene; + struct Object *ob; + struct bGPdata *gpd; + struct bGPDlayer *gpl; - struct ScrArea *sa; - struct ARegion *ar; - struct View2D *v2d; + struct ScrArea *sa; + struct ARegion *ar; + struct View2D *v2d; - rctf *subrect; /* for using the camera rect within the 3d view */ - rctf subrect_data; + rctf *subrect; /* for using the camera rect within the 3d view */ + rctf subrect_data; - float mat[4][4]; /* transform matrix on the strokes (introduced in [b770964]) */ + float mat[4][4]; /* transform matrix on the strokes (introduced in [b770964]) */ } GP_SpaceConversion; bool gp_stroke_inside_circle( - const float mval[2], const float UNUSED(mvalo[2]), - int rad, int x0, int y0, int x1, int y1); + const float mval[2], const float UNUSED(mvalo[2]), int rad, int x0, int y0, int x1, int y1); void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc); -void gp_point_to_xy( - const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt, - int *r_x, int *r_y); +void gp_point_to_xy(const GP_SpaceConversion *gsc, + const struct bGPDstroke *gps, + const struct bGPDspoint *pt, + int *r_x, + int *r_y); -void gp_point_to_xy_fl( - const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, - float *r_x, float *r_y); +void gp_point_to_xy_fl(const GP_SpaceConversion *gsc, + const bGPDstroke *gps, + const bGPDspoint *pt, + float *r_x, + float *r_y); void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt); /** * Change points position relative to parent object */ -void gp_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps); +void gp_apply_parent(struct Depsgraph *depsgraph, + struct Object *obact, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDstroke *gps); /** * Change point position relative to parent object */ -void gp_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt); - -void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, const short flag, const float pt[3], float xy[2]); - -bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]); +void gp_apply_parent_point(struct Depsgraph *depsgraph, + struct Object *obact, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDspoint *pt); + +void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, + const short flag, + const float pt[3], + float xy[2]); + +bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, + struct Scene *scene, + const float screen_co[2], + float r_out[3]); /* helper to convert 2d to 3d */ -void gp_stroke_convertcoords_tpoint( - struct Scene *scene, struct ARegion *ar, - struct Object *ob, - bGPDlayer *gpl, const struct tGPspoint *point2D, - float *depth, float out[3]); +void gp_stroke_convertcoords_tpoint(struct Scene *scene, + struct ARegion *ar, + struct Object *ob, + bGPDlayer *gpl, + const struct tGPspoint *point2D, + float *depth, + float out[3]); /* Poll Callbacks ------------------------------------ */ /* gpencil_utils.c */ @@ -316,9 +331,12 @@ struct GHash *gp_copybuf_validate_colormap(struct bContext *C); /* Stroke Editing ------------------------------------ */ -void gp_stroke_delete_tagged_points( - bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, - int tag_flags, bool select, int limit); +void gp_stroke_delete_tagged_points(bGPDframe *gpf, + bGPDstroke *gps, + bGPDstroke *next_stroke, + int tag_flags, + bool select, + int limit); int gp_delete_selected_point_wrap(bContext *C); void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide); @@ -326,12 +344,14 @@ void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, struct RNG *rng); /* Layers Enums -------------------------------------- */ -const struct EnumPropertyItem *ED_gpencil_layers_enum_itemf( - struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, - bool *r_free); -const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( - struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, - bool *r_free); +const struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + bool *r_free); +const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -340,7 +360,6 @@ const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( void GPENCIL_OT_annotate(struct wmOperatorType *ot); - /* drawing ---------- */ void GPENCIL_OT_draw(struct wmOperatorType *ot); @@ -352,15 +371,15 @@ void GPENCIL_OT_guide_rotate(struct wmOperatorType *ot); /* Paint Modes for operator */ typedef enum eGPencil_PaintModes { - GP_PAINTMODE_DRAW = 0, - GP_PAINTMODE_ERASER, - GP_PAINTMODE_DRAW_STRAIGHT, - GP_PAINTMODE_DRAW_POLY, - GP_PAINTMODE_SET_CP, + GP_PAINTMODE_DRAW = 0, + GP_PAINTMODE_ERASER, + GP_PAINTMODE_DRAW_STRAIGHT, + GP_PAINTMODE_DRAW_POLY, + GP_PAINTMODE_SET_CP, } eGPencil_PaintModes; /* maximum sizes of gp-session buffer */ -#define GP_STROKE_BUFFER_MAX 5000 +#define GP_STROKE_BUFFER_MAX 5000 /* stroke editing ----- */ @@ -436,21 +455,21 @@ void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); enum { - GP_STROKE_JOIN = -1, - GP_STROKE_JOINCOPY = 1, + GP_STROKE_JOIN = -1, + GP_STROKE_JOINCOPY = 1, }; enum { - GP_STROKE_BOX = -1, - GP_STROKE_LINE = 1, - GP_STROKE_CIRCLE = 2, - GP_STROKE_ARC = 3, - GP_STROKE_CURVE = 4, + GP_STROKE_BOX = -1, + GP_STROKE_LINE = 1, + GP_STROKE_CIRCLE = 2, + GP_STROKE_ARC = 3, + GP_STROKE_CURVE = 4, }; enum { - GP_MERGE_STROKE = -1, - GP_MERGE_POINT = 1, + GP_MERGE_STROKE = -1, + GP_MERGE_POINT = 1, }; void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); @@ -520,20 +539,20 @@ void GPENCIL_OT_generate_weights(struct wmOperatorType *ot); /* XXX - TODO: replace this with the modern bAnimListElem... */ /* This struct defines a structure used for quick access */ typedef struct bActListElem { - struct bActListElem *next, *prev; + struct bActListElem *next, *prev; - void *data; /* source data this elem represents */ - int type; /* one of the ACTTYPE_* values */ - int flag; /* copy of elem's flags for quick access */ - int index; /* copy of adrcode where applicable */ + void *data; /* source data this elem represents */ + int type; /* one of the ACTTYPE_* values */ + int flag; /* copy of elem's flags for quick access */ + int index; /* copy of adrcode where applicable */ - void *key_data; /* motion data - ipo or ipo-curve */ - short datatype; /* type of motion data to expect */ + void *key_data; /* motion data - ipo or ipo-curve */ + short datatype; /* type of motion data to expect */ - struct bActionGroup *grp; /* action group that owns the channel */ + struct bActionGroup *grp; /* action group that owns the channel */ - void *owner; /* will either be an action channel or fake ipo-channel (for keys) */ - short ownertype; /* type of owner */ + void *owner; /* will either be an action channel or fake ipo-channel (for keys) */ + short ownertype; /* type of owner */ } bActListElem; /* ****************************************************** */ @@ -541,29 +560,29 @@ typedef struct bActListElem { /* filtering flags - under what circumstances should a channel be added */ typedef enum ACTFILTER_FLAGS { - ACTFILTER_VISIBLE = (1 << 0), /* should channels be visible */ - ACTFILTER_SEL = (1 << 1), /* should channels be selected */ - ACTFILTER_FOREDIT = (1 << 2), /* does editable status matter */ - ACTFILTER_CHANNELS = (1 << 3), /* do we only care that it is a channel */ - ACTFILTER_IPOKEYS = (1 << 4), /* only channels referencing ipo's */ - ACTFILTER_ONLYICU = (1 << 5), /* only reference ipo-curves */ - ACTFILTER_FORDRAWING = (1 << 6), /* make list for interface drawing */ - ACTFILTER_ACTGROUPED = (1 << 7), /* belongs to the active group */ + ACTFILTER_VISIBLE = (1 << 0), /* should channels be visible */ + ACTFILTER_SEL = (1 << 1), /* should channels be selected */ + ACTFILTER_FOREDIT = (1 << 2), /* does editable status matter */ + ACTFILTER_CHANNELS = (1 << 3), /* do we only care that it is a channel */ + ACTFILTER_IPOKEYS = (1 << 4), /* only channels referencing ipo's */ + ACTFILTER_ONLYICU = (1 << 5), /* only reference ipo-curves */ + ACTFILTER_FORDRAWING = (1 << 6), /* make list for interface drawing */ + ACTFILTER_ACTGROUPED = (1 << 7), /* belongs to the active group */ } ACTFILTER_FLAGS; /* Action Editor - Main Data types */ typedef enum ACTCONT_TYPES { - ACTCONT_NONE = 0, - ACTCONT_ACTION, - ACTCONT_SHAPEKEY, - ACTCONT_GPENCIL, + ACTCONT_NONE = 0, + ACTCONT_ACTION, + ACTCONT_SHAPEKEY, + ACTCONT_GPENCIL, } ACTCONT_TYPES; /* ****************************************************** */ /* Stroke Iteration Utilities */ struct GP_EditableStrokes_Iter { - float diff_mat[4][4]; + float diff_mat[4][4]; }; /** @@ -576,42 +595,42 @@ struct GP_EditableStrokes_Iter { * \param gps: The identifier to use for current stroke being processed. * Choose a suitable value to avoid name clashes. */ -#define GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \ -{ \ - struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \ - Depsgraph *depsgraph_ = CTX_data_depsgraph(C); \ - Object *obact_ = CTX_data_active_object(C); \ - bGPdata *gpd_ = CTX_data_gpencil_data(C); \ - const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) \ - { \ - bGPDframe *init_gpf_ = gpl->actframe; \ - if (is_multiedit_) { \ - init_gpf_ = gpl->frames.first; \ - } \ - for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ - if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ - /* loop over strokes */ \ - for (bGPDstroke *gps = gpf_->strokes.first; gps; gps = gps->next) { \ - /* skip strokes that are invalid for current view */ \ - if (ED_gpencil_stroke_can_use(C, gps) == false) \ - continue; \ - /* check if the color is editable */ \ - if (ED_gpencil_stroke_color_use(obact_, gpl, gps) == false) \ - continue; \ - /* ... Do Stuff With Strokes ... */ - -#define GP_EDITABLE_STROKES_END(gpstroke_iter) \ - } \ - } \ - if (!is_multiedit_) { \ - break; \ - } \ - } \ - } \ - CTX_DATA_END; \ -} (void)0 +#define GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \ + { \ + struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \ + Depsgraph *depsgraph_ = CTX_data_depsgraph(C); \ + Object *obact_ = CTX_data_active_object(C); \ + bGPdata *gpd_ = CTX_data_gpencil_data(C); \ + const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { \ + bGPDframe *init_gpf_ = gpl->actframe; \ + if (is_multiedit_) { \ + init_gpf_ = gpl->frames.first; \ + } \ + for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ + if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ + ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ + /* loop over strokes */ \ + for (bGPDstroke *gps = gpf_->strokes.first; gps; gps = gps->next) { \ + /* skip strokes that are invalid for current view */ \ + if (ED_gpencil_stroke_can_use(C, gps) == false) \ + continue; \ + /* check if the color is editable */ \ + if (ED_gpencil_stroke_color_use(obact_, gpl, gps) == false) \ + continue; \ + /* ... Do Stuff With Strokes ... */ + +#define GP_EDITABLE_STROKES_END(gpstroke_iter) \ + } \ + } \ + if (!is_multiedit_) { \ + break; \ + } \ + } \ + } \ + CTX_DATA_END; \ + } \ + (void)0 /* ****************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 7be157b5d84..5c9b9416330 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -82,46 +81,48 @@ /* Poll callback for interpolation operators */ static bool gpencil_view3d_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - /* only 3D view */ - ScrArea *sa = CTX_wm_area(C); - if (sa && sa->spacetype != SPACE_VIEW3D) { - return 0; - } + /* only 3D view */ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return 0; + } - /* need data to interpolate */ - if (ELEM(NULL, gpd, gpl)) { - return 0; - } + /* need data to interpolate */ + if (ELEM(NULL, gpd, gpl)) { + return 0; + } - return 1; + return 1; } /* Perform interpolation */ -static void gp_interpolate_update_points( - const bGPDstroke *gps_from, const bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) +static void gp_interpolate_update_points(const bGPDstroke *gps_from, + const bGPDstroke *gps_to, + bGPDstroke *new_stroke, + float factor) { - /* update points */ - for (int i = 0; i < new_stroke->totpoints; i++) { - const bGPDspoint *prev = &gps_from->points[i]; - const bGPDspoint *next = &gps_to->points[i]; - bGPDspoint *pt = &new_stroke->points[i]; - - /* Interpolate all values */ - interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); - pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); - pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - - /* GPXX interpolate dverts */ + /* update points */ + for (int i = 0; i < new_stroke->totpoints; i++) { + const bGPDspoint *prev = &gps_from->points[i]; + const bGPDspoint *next = &gps_to->points[i]; + bGPDspoint *pt = &new_stroke->points[i]; + + /* Interpolate all values */ + interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); + pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); + pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* GPXX interpolate dverts */ #if 0 - MDeformVert *dvert = &new_stroke->dvert[i]; - dvert->totweight = 0; - dvert->dw = NULL; + MDeformVert *dvert = &new_stroke->dvert[i]; + dvert->totweight = 0; + dvert->dw = NULL; #endif - } + } } /* ****************** Interpolate Interactive *********************** */ @@ -129,212 +130,223 @@ static void gp_interpolate_update_points( /* Helper: Update all strokes interpolated */ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) { - bGPdata *gpd = tgpi->gpd; - tGPDinterpolate_layer *tgpil; - const float shift = tgpi->shift; - - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - bGPDstroke *new_stroke; - const float factor = tgpil->factor + shift; - - for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { - bGPDstroke *gps_from, *gps_to; - int stroke_idx; - - if (new_stroke->totpoints == 0) { - continue; - } - - /* get strokes to interpolate */ - stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); - - gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx); - gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx); - - /* update points position */ - if ((gps_from) && (gps_to)) { - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - } - } - } - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + bGPdata *gpd = tgpi->gpd; + tGPDinterpolate_layer *tgpil; + const float shift = tgpi->shift; + + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + bGPDstroke *new_stroke; + const float factor = tgpil->factor + shift; + + for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; + new_stroke = new_stroke->next) { + bGPDstroke *gps_from, *gps_to; + int stroke_idx; + + if (new_stroke->totpoints == 0) { + continue; + } + + /* get strokes to interpolate */ + stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); + + gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx); + + /* update points position */ + if ((gps_from) && (gps_to)) { + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + } + } + } + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } /* Helper: Verify valid strokes for interpolation */ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) { - Object *ob = CTX_data_active_object(C); - ToolSettings *ts = CTX_data_tool_settings(C); - eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; - - /* get layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* all layers or only active */ - if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* read strokes */ - for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *gps_to; - int fFrame; - - /* only selected */ - if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - continue; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { - continue; - } - - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); - gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); - if (gps_to == NULL) { - continue; - } - - return true; - } - } - return false; + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; + + /* get layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* read strokes */ + for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; + gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + /* only selected */ + if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && + ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { + continue; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); + gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + + return true; + } + } + return false; } /* Helper: Create internal strokes interpolated */ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) { - bGPdata *gpd = tgpi->gpd; - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *actframe = active_gpl->actframe; - Object *ob = CTX_data_active_object(C); - - /* save initial factor for active layer to define shift limits */ - tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / (actframe->next->framenum - actframe->framenum + 1); - - /* limits are 100% below 0 and 100% over the 100% */ - tgpi->low_limit = -1.0f - tgpi->init_factor; - tgpi->high_limit = 2.0f - tgpi->init_factor; - - /* set layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - tGPDinterpolate_layer *tgpil; - - /* all layers or only active */ - if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* create temp data for each layer */ - tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); - - tgpil->gpl = gpl; - tgpil->prevFrame = gpl->actframe; - tgpil->nextFrame = gpl->actframe->next; - - BLI_addtail(&tgpi->ilayers, tgpil); - - /* create a new temporary frame */ - tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); - tgpil->interFrame->framenum = tgpi->cframe; - - /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ - tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); - - /* create new strokes data with interpolated points reading original stroke */ - for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *gps_to; - int fFrame; - - bGPDstroke *new_stroke = NULL; - bool valid = true; - - - /* only selected */ - if ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - valid = false; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - valid = false; - } - - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { - valid = false; - } - - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from); - gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame); - if (gps_to == NULL) { - valid = false; - } - - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); - - if (valid) { - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); - } - else { - /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ - new_stroke->totpoints = 0; - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert)); - } - new_stroke->tot_triangles = 0; - new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - - /* add to strokes */ - BLI_addtail(&tgpil->interFrame->strokes, new_stroke); - } - } + bGPdata *gpd = tgpi->gpd; + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + Object *ob = CTX_data_active_object(C); + + /* save initial factor for active layer to define shift limits */ + tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / + (actframe->next->framenum - actframe->framenum + 1); + + /* limits are 100% below 0 and 100% over the 100% */ + tgpi->low_limit = -1.0f - tgpi->init_factor; + tgpi->high_limit = 2.0f - tgpi->init_factor; + + /* set layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + tGPDinterpolate_layer *tgpil; + + /* all layers or only active */ + if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* create temp data for each layer */ + tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); + + tgpil->gpl = gpl; + tgpil->prevFrame = gpl->actframe; + tgpil->nextFrame = gpl->actframe->next; + + BLI_addtail(&tgpi->ilayers, tgpil); + + /* create a new temporary frame */ + tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); + tgpil->interFrame->framenum = tgpi->cframe; + + /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ + tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / + (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); + + /* create new strokes data with interpolated points reading original stroke */ + for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; + gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + bGPDstroke *new_stroke = NULL; + bool valid = true; + + /* only selected */ + if ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && + ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + valid = false; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + valid = false; + } + + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { + valid = false; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame); + if (gps_to == NULL) { + valid = false; + } + + /* create new stroke */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + + if (valid) { + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, + sizeof(*new_stroke->points) * gps_to->totpoints); + if (new_stroke->dvert != NULL) { + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, + sizeof(*new_stroke->dvert) * gps_to->totpoints); + } + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); + } + else { + /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ + new_stroke->totpoints = 0; + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); + if (new_stroke->dvert != NULL) { + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert)); + } + new_stroke->tot_triangles = 0; + new_stroke->triangles = MEM_recallocN(new_stroke->triangles, + sizeof(*new_stroke->triangles)); + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + + /* add to strokes */ + BLI_addtail(&tgpil->interFrame->strokes, new_stroke); + } + } } /* ----------------------- */ /* Drawing Callbacks */ /* Drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *C, ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_screen(const struct bContext *C, + ARegion *UNUSED(ar), + void *arg) { - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); } /* Drawing callback for modal operator in 3d mode */ static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) { - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); } /* ----------------------- */ @@ -344,52 +356,57 @@ static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), */ static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) { - float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; - float mpos = event->x - tgpi->ar->winrct.xmin; - - if (mpos >= mid) { - tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; - } - else { - tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); - } - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); + float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; + float mpos = event->x - tgpi->ar->winrct.xmin; + + if (mpos >= mid) { + tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; + } + else { + tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); + } + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); } /* Helper: Draw status message while the user is running the operator */ static void gpencil_interpolate_status_indicators(bContext *C, tGPDinterpolate *p) { - Scene *scene = p->scene; - char status_str[UI_MAX_DRAW_STR]; - char msg_str[UI_MAX_DRAW_STR]; - - BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: "), UI_MAX_DRAW_STR); - - if (hasNumInput(&p->num)) { - char str_offs[NUM_STR_REP_LEN]; - - outputNumInput(&p->num, str_offs, &scene->unit); - BLI_snprintf(status_str, sizeof(status_str), "%s%s", msg_str, str_offs); - } - else { - BLI_snprintf(status_str, sizeof(status_str), "%s%d %%", msg_str, (int)((p->init_factor + p->shift) * 100.0f)); - } - - ED_area_status_text(p->sa, status_str); - ED_workspace_status_text(C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor")); + Scene *scene = p->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: "), UI_MAX_DRAW_STR); + + if (hasNumInput(&p->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&p->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s%s", msg_str, str_offs); + } + else { + BLI_snprintf(status_str, + sizeof(status_str), + "%s%d %%", + msg_str, + (int)((p->init_factor + p->shift) * 100.0f)); + } + + ED_area_status_text(p->sa, status_str); + ED_workspace_status_text( + C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor")); } /* Update screen and stroke */ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) { - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(C, tgpi); - /* apply... */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* update points position */ - gp_interpolate_update_strokes(C, tgpi); + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(C, tgpi); + /* apply... */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* update points position */ + gp_interpolate_update_strokes(C, tgpi); } /* ----------------------- */ @@ -397,93 +414,93 @@ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpol /* Exit and free memory */ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi = op->customdata; - tGPDinterpolate_layer *tgpil; - bGPdata *gpd = tgpi->gpd; - - /* don't assume that operator data exists at all */ - if (tgpi) { - /* remove drawing handler */ - if (tgpi->draw_handle_screen) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); - } - if (tgpi->draw_handle_3d) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); - } - - /* clear status message area */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - - /* finally, free memory used by temp data */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - BKE_gpencil_free_strokes(tgpil->interFrame); - MEM_freeN(tgpil->interFrame); - } - - BLI_freelistN(&tgpi->ilayers); - MEM_freeN(tgpi); - } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* clear pointer */ - op->customdata = NULL; + tGPDinterpolate *tgpi = op->customdata; + tGPDinterpolate_layer *tgpil; + bGPdata *gpd = tgpi->gpd; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_screen) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); + } + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + + /* clear status message area */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + + /* finally, free memory used by temp data */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + BKE_gpencil_free_strokes(tgpil->interFrame); + MEM_freeN(tgpil->interFrame); + } + + BLI_freelistN(&tgpi->ilayers); + MEM_freeN(tgpi); + } + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; } /* Init new temporary interpolation data */ static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) { - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); - /* set current scene and window */ - tgpi->scene = CTX_data_scene(C); - tgpi->sa = CTX_wm_area(C); - tgpi->ar = CTX_wm_region(C); - tgpi->flag = ts->gp_interpolate.flag; + /* set current scene and window */ + tgpi->scene = CTX_data_scene(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->flag = ts->gp_interpolate.flag; - /* set current frame number */ - tgpi->cframe = tgpi->scene->r.cfra; + /* set current frame number */ + tgpi->cframe = tgpi->scene->r.cfra; - /* set GP datablock */ - tgpi->gpd = gpd; + /* set GP datablock */ + tgpi->gpd = gpd; - /* set interpolation weight */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* set layers */ - gp_interpolate_set_points(C, tgpi); + /* set interpolation weight */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* set layers */ + gp_interpolate_set_points(C, tgpi); - return 1; + return 1; } /* Allocate memory and initialize values */ static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); + tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); - /* define initial values */ - gp_interpolate_set_init_values(C, op, tgpi); + /* define initial values */ + gp_interpolate_set_init_values(C, op, tgpi); - /* return context data for running operator */ - return tgpi; + /* return context data for running operator */ + return tgpi; } /* Init interpolation: Allocate memory and set init values */ static int gpencil_interpolate_init(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi; - - /* check context */ - tgpi = op->customdata = gp_session_init_interpolation(C, op); - if (tgpi == NULL) { - /* something wasn't set correctly in context */ - gpencil_interpolate_exit(C, op); - return 0; - } - - /* everything is now setup ok */ - return 1; + tGPDinterpolate *tgpi; + + /* check context */ + tgpi = op->customdata = gp_session_init_interpolation(C, op); + if (tgpi == NULL) { + /* something wasn't set correctly in context */ + gpencil_interpolate_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; } /* ----------------------- */ @@ -491,219 +508,230 @@ static int gpencil_interpolate_init(bContext *C, wmOperator *op) /* Invoke handler: Initialize the operator */ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - wmWindow *win = CTX_wm_window(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPDframe *actframe = gpl->actframe; - tGPDinterpolate *tgpi = NULL; - - /* cannot interpolate if not between 2 frames */ - if (ELEM(NULL, actframe, actframe->next)) { - BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); - return OPERATOR_CANCELLED; - } - - /* cannot interpolate in extremes */ - if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { - BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); - return OPERATOR_CANCELLED; - } - - /* need editable strokes */ - if (!gp_interpolate_check_todo(C, gpd)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes"); - return OPERATOR_CANCELLED; - } - - /* try to initialize context data needed */ - if (!gpencil_interpolate_init(C, op)) { - if (op->customdata) - MEM_freeN(op->customdata); - return OPERATOR_CANCELLED; - } - else { - tgpi = op->customdata; - } - - /* Enable custom drawing handlers - * It needs 2 handlers because strokes can in 3d space and screen space - * and each handler use different coord system - */ - tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); - tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); - - /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); - - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(C, tgpi); - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* add a modal handler for this operator */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; + wmWindow *win = CTX_wm_window(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDframe *actframe = gpl->actframe; + tGPDinterpolate *tgpi = NULL; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report( + op->reports, + RPT_ERROR, + "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + + /* cannot interpolate in extremes */ + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, + RPT_ERROR, + "Cannot interpolate as current frame already has existing grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* need editable strokes */ + if (!gp_interpolate_check_todo(C, gpd)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes"); + return OPERATOR_CANCELLED; + } + + /* try to initialize context data needed */ + if (!gpencil_interpolate_init(C, op)) { + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpi = op->customdata; + } + + /* Enable custom drawing handlers + * It needs 2 handlers because strokes can in 3d space and screen space + * and each handler use different coord system + */ + tgpi->draw_handle_screen = ED_region_draw_cb_activate( + tgpi->ar->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); + tgpi->draw_handle_3d = ED_region_draw_cb_activate( + tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; } /* Modal handler: Events handling during interactive part */ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGPDinterpolate *tgpi = op->customdata; - wmWindow *win = CTX_wm_window(C); - bGPDframe *gpf_dst; - bGPDstroke *gps_src, *gps_dst; - tGPDinterpolate_layer *tgpil; - const bool has_numinput = hasNumInput(&tgpi->num); - - switch (event->type) { - case LEFTMOUSE: /* confirm */ - case RETKEY: - { - /* return to normal cursor and header status */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* insert keyframes as required... */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); - gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; - - /* copy strokes */ - BLI_listbase_clear(&gpf_dst->strokes); - for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { - if (gps_src->totpoints == 0) { - continue; - } - - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - if (gps_src->dvert != NULL) { - gps_dst->dvert = MEM_dupallocN(gps_src->dvert); - BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); - } - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; - BLI_addtail(&gpf_dst->strokes, gps_dst); - } - } - - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* done! */ - return OPERATOR_FINISHED; - } - - case ESCKEY: /* cancel */ - case RIGHTMOUSE: - { - /* return to normal cursor and header status */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* canceled! */ - return OPERATOR_CANCELLED; - } - - case WHEELUPMOUSE: - { - tgpi->shift = tgpi->shift + 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case WHEELDOWNMOUSE: - { - tgpi->shift = tgpi->shift - 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case MOUSEMOVE: /* calculate new position */ - { - /* only handle mousemove if not doing numinput */ - if (has_numinput == false) { - /* update shift based on position of mouse */ - gpencil_mouse_update_shift(tgpi, op, event); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - } - break; - } - default: - { - if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { - const float factor = tgpi->init_factor; - float value; - - /* Grab shift from numeric input, and store this new value (the user see an int) */ - value = (factor + tgpi->shift) * 100.0f; - applyNumInput(&tgpi->num, &value); - tgpi->shift = value / 100.0f; - - /* recalculate the shift to get the right value in the frame scale */ - tgpi->shift = tgpi->shift - factor; - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - - break; - } - else { - /* unhandled event - allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; - } - } - } - - /* still running... */ - return OPERATOR_RUNNING_MODAL; + tGPDinterpolate *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + bGPDframe *gpf_dst; + bGPDstroke *gps_src, *gps_dst; + tGPDinterpolate_layer *tgpil; + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: /* confirm */ + case RETKEY: { + /* return to normal cursor and header status */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; + + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { + if (gps_src->totpoints == 0) { + continue; + } + + /* make copy of source stroke, then adjust pointer to points too */ + gps_dst = MEM_dupallocN(gps_src); + gps_dst->points = MEM_dupallocN(gps_src->points); + if (gps_src->dvert != NULL) { + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); + } + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); + gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: { + /* return to normal cursor and header status */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + case WHEELUPMOUSE: { + tgpi->shift = tgpi->shift + 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case WHEELDOWNMOUSE: { + tgpi->shift = tgpi->shift - 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update shift based on position of mouse */ + gpencil_mouse_update_shift(tgpi, op, event); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + } + break; + } + default: { + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + const float factor = tgpi->init_factor; + float value; + + /* Grab shift from numeric input, and store this new value (the user see an int) */ + value = (factor + tgpi->shift) * 100.0f; + applyNumInput(&tgpi->num, &value); + tgpi->shift = value / 100.0f; + + /* recalculate the shift to get the right value in the frame scale */ + tgpi->shift = tgpi->shift - factor; + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; } /* Cancel handler */ static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_interpolate_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_interpolate_exit(C, op); } void GPENCIL_OT_interpolate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Grease Pencil Interpolation"; - ot->idname = "GPENCIL_OT_interpolate"; - ot->description = "Interpolate grease pencil strokes between frames"; - - /* callbacks */ - ot->invoke = gpencil_interpolate_invoke; - ot->modal = gpencil_interpolate_modal; - ot->cancel = gpencil_interpolate_cancel; - ot->poll = gpencil_view3d_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Bias factor for which frame has more influence on the interpolated strokes", -0.9f, 0.9f); + /* identifiers */ + ot->name = "Grease Pencil Interpolation"; + ot->idname = "GPENCIL_OT_interpolate"; + ot->description = "Interpolate grease pencil strokes between frames"; + + /* callbacks */ + ot->invoke = gpencil_interpolate_invoke; + ot->modal = gpencil_interpolate_modal; + ot->cancel = gpencil_interpolate_cancel; + ot->poll = gpencil_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_float_percentage( + ot->srna, + "shift", + 0.0f, + -1.0f, + 1.0f, + "Shift", + "Bias factor for which frame has more influence on the interpolated strokes", + -0.9f, + 0.9f); } /* ****************** Interpolate Sequence *********************** */ @@ -711,464 +739,473 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot) /* Helper: Perform easing equation calculations for GP interpolation operator */ static float gp_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time) { - const float begin = 0.0f; - const float change = 1.0f; - const float duration = 1.0f; - - const float back = ipo_settings->back; - const float amplitude = ipo_settings->amplitude; - const float period = ipo_settings->period; - - eBezTriple_Easing easing = ipo_settings->easing; - float result = time; - - switch (ipo_settings->type) { - case GP_IPO_BACK: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_back_ease_in(time, begin, change, duration, back); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_back_ease_out(time, begin, change, duration, back); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_back_ease_out(time, begin, change, duration, back); - break; - } - break; - - case GP_IPO_BOUNCE: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_bounce_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - } - break; - - case GP_IPO_CIRC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_circ_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_circ_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_CUBIC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_cubic_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_ELASTIC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - } - break; - - case GP_IPO_EXPO: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_expo_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_expo_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUAD: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quad_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quad_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUART: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quart_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quart_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUINT: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quint_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quint_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_SINE: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_sine_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_sine_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - } - break; - - default: - printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); - break; - } - - return result; + const float begin = 0.0f; + const float change = 1.0f; + const float duration = 1.0f; + + const float back = ipo_settings->back; + const float amplitude = ipo_settings->amplitude; + const float period = ipo_settings->period; + + eBezTriple_Easing easing = ipo_settings->easing; + float result = time; + + switch (ipo_settings->type) { + case GP_IPO_BACK: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_back_ease_in(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + } + break; + + case GP_IPO_BOUNCE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_bounce_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CIRC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_circ_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_circ_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CUBIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_cubic_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_ELASTIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_elastic_ease_in_out( + time, begin, change, duration, amplitude, period); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + } + break; + + case GP_IPO_EXPO: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_expo_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_expo_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUAD: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quad_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quad_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUART: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quart_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quart_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUINT: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quint_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quint_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_SINE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_sine_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_sine_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + } + break; + + default: + printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); + break; + } + + return result; } static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *actframe = active_gpl->actframe; - - Object *ob = CTX_data_active_object(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; - eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; - - /* cannot interpolate if not between 2 frames */ - if (ELEM(NULL, actframe, actframe->next)) { - BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); - return OPERATOR_CANCELLED; - } - /* cannot interpolate in extremes */ - if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { - BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); - return OPERATOR_CANCELLED; - } - - /* loop all layer to check if need interpolation */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *prevFrame, *nextFrame; - bGPDstroke *gps_from, *gps_to; - int cframe, fFrame; - - /* all layers or only active */ - if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* store extremes */ - prevFrame = gpl->actframe; - nextFrame = gpl->actframe->next; - - /* Loop over intermediary frames and create the interpolation */ - for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { - bGPDframe *interFrame = NULL; - float factor; - - /* get interpolation factor */ - factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); - - if (ipo_settings->type == GP_IPO_CURVEMAP) { - /* custom curvemap */ - if (ipo_settings->custom_ipo) { - factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); - } - else { - BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); - } - } - else if (ipo_settings->type >= GP_IPO_BACK) { - /* easing equation... */ - factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); - } - - /* create new strokes data with interpolated points reading original stroke */ - for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *new_stroke = NULL; - - /* only selected */ - if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - continue; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { - continue; - } - - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&prevFrame->strokes, gps_from); - gps_to = BLI_findlink(&nextFrame->strokes, fFrame); - if (gps_to == NULL) { - continue; - } - - /* create a new frame if needed */ - if (interFrame == NULL) { - interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); - interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; - } - - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); - - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - /* free weights of removed points */ - if (gps_from->dvert != NULL) { - BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, gps_from->totpoints - gps_to->totpoints); - } - - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - - /* add to strokes */ - BLI_addtail(&interFrame->strokes, new_stroke); - } - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; + eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report( + op->reports, + RPT_ERROR, + "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + /* cannot interpolate in extremes */ + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, + RPT_ERROR, + "Cannot interpolate as current frame already has existing grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* loop all layer to check if need interpolation */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *prevFrame, *nextFrame; + bGPDstroke *gps_from, *gps_to; + int cframe, fFrame; + + /* all layers or only active */ + if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* store extremes */ + prevFrame = gpl->actframe; + nextFrame = gpl->actframe->next; + + /* Loop over intermediary frames and create the interpolation */ + for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { + bGPDframe *interFrame = NULL; + float factor; + + /* get interpolation factor */ + factor = (float)(cframe - prevFrame->framenum) / + (nextFrame->framenum - prevFrame->framenum + 1); + + if (ipo_settings->type == GP_IPO_CURVEMAP) { + /* custom curvemap */ + if (ipo_settings->custom_ipo) { + factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); + } + else { + BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); + } + } + else if (ipo_settings->type >= GP_IPO_BACK) { + /* easing equation... */ + factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); + } + + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *new_stroke = NULL; + + /* only selected */ + if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && + ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { + continue; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&nextFrame->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + + /* create a new frame if needed */ + if (interFrame == NULL) { + interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); + interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; + } + + /* create new stroke */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + /* free weights of removed points */ + if (gps_from->dvert != NULL) { + BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, + gps_from->totpoints - gps_to->totpoints); + } + + new_stroke->points = MEM_recallocN(new_stroke->points, + sizeof(*new_stroke->points) * gps_to->totpoints); + + if (new_stroke->dvert != NULL) { + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, + sizeof(*new_stroke->dvert) * gps_to->totpoints); + } + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + + /* add to strokes */ + BLI_addtail(&interFrame->strokes, new_stroke); + } + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Interpolate Sequence"; - ot->idname = "GPENCIL_OT_interpolate_sequence"; - ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; + /* identifiers */ + ot->name = "Interpolate Sequence"; + ot->idname = "GPENCIL_OT_interpolate_sequence"; + ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; - /* api callbacks */ - ot->exec = gpencil_interpolate_seq_exec; - ot->poll = gpencil_view3d_poll; + /* api callbacks */ + ot->exec = gpencil_interpolate_seq_exec; + ot->poll = gpencil_view3d_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************** Remove Breakdowns ************************ */ static bool gpencil_interpolate_reverse_poll(bContext *C) { - if (!gpencil_view3d_poll(C)) { - return 0; - } + if (!gpencil_view3d_poll(C)) { + return 0; + } - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - /* need to be on a breakdown frame */ - if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) { - CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown"); - return 0; - } + /* need to be on a breakdown frame */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) { + CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown"); + return 0; + } - return 1; + return 1; } static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - /* Go through each layer, deleting the breakdowns around the current frame, - * but only if there is a keyframe nearby to stop at - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *start_key = NULL; - bGPDframe *end_key = NULL; - bGPDframe *gpf, *gpfn; - - /* Only continue if we're currently on a breakdown keyframe */ - if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) - continue; - - /* Search left for "start_key" (i.e. the first breakdown to remove) */ - gpf = gpl->actframe; - while (gpf) { - if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { - /* A breakdown... keep going left */ - start_key = gpf; - gpf = gpf->prev; - } - else { - /* Not a breakdown (may be a key, or an extreme, - * or something else that wasn't generated)... stop */ - break; - } - } - - /* Search right for "end_key" (i.e. the last breakdown to remove) */ - gpf = gpl->actframe; - while (gpf) { - if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { - /* A breakdown... keep going right */ - end_key = gpf; - gpf = gpf->next; - } - else { - /* Not a breakdown... stop */ - break; - } - } - - /* Did we find anything? */ - /* NOTE: We should only proceed if there's something before/after these extents... - * Otherwise, there's just an extent of breakdowns with no keys to interpolate between - */ - if ((start_key && end_key) && - ELEM(NULL, start_key->prev, end_key->next) == false) - { - /* Set actframe to the key before start_key, since the keys have been removed now */ - gpl->actframe = start_key->prev; - - /* Free each frame we're removing (except the last one) */ - for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { - gpfn = gpf->next; - - /* free strokes and their associated memory */ - BKE_gpencil_free_strokes(gpf); - BLI_freelinkN(&gpl->frames, gpf); - } - - /* Now free the last one... */ - BKE_gpencil_free_strokes(end_key); - BLI_freelinkN(&gpl->frames, end_key); - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* Go through each layer, deleting the breakdowns around the current frame, + * but only if there is a keyframe nearby to stop at + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *start_key = NULL; + bGPDframe *end_key = NULL; + bGPDframe *gpf, *gpfn; + + /* Only continue if we're currently on a breakdown keyframe */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) + continue; + + /* Search left for "start_key" (i.e. the first breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going left */ + start_key = gpf; + gpf = gpf->prev; + } + else { + /* Not a breakdown (may be a key, or an extreme, + * or something else that wasn't generated)... stop */ + break; + } + } + + /* Search right for "end_key" (i.e. the last breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going right */ + end_key = gpf; + gpf = gpf->next; + } + else { + /* Not a breakdown... stop */ + break; + } + } + + /* Did we find anything? */ + /* NOTE: We should only proceed if there's something before/after these extents... + * Otherwise, there's just an extent of breakdowns with no keys to interpolate between + */ + if ((start_key && end_key) && ELEM(NULL, start_key->prev, end_key->next) == false) { + /* Set actframe to the key before start_key, since the keys have been removed now */ + gpl->actframe = start_key->prev; + + /* Free each frame we're removing (except the last one) */ + for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { + gpfn = gpf->next; + + /* free strokes and their associated memory */ + BKE_gpencil_free_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + } + + /* Now free the last one... */ + BKE_gpencil_free_strokes(end_key); + BLI_freelinkN(&gpl->frames, end_key); + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Breakdowns"; - ot->idname = "GPENCIL_OT_interpolate_reverse"; - ot->description = "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; - - /* callbacks */ - ot->exec = gpencil_interpolate_reverse_exec; - ot->poll = gpencil_interpolate_reverse_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Remove Breakdowns"; + ot->idname = "GPENCIL_OT_interpolate_reverse"; + ot->description = + "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; + + /* callbacks */ + ot->exec = gpencil_interpolate_reverse_exec; + ot->poll = gpencil_interpolate_reverse_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* *************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 10bf45a9e2f..48c761919e5 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -54,144 +54,144 @@ #include "gpencil_intern.h" typedef struct tGPencilPointCache { - float factor; /* value to sort */ - bGPDstroke *gps; - float x, y, z; - float pressure; - float strength; + float factor; /* value to sort */ + bGPDstroke *gps; + float x, y, z; + float pressure; + float strength; } tGPencilPointCache; /* helper function to sort points */ static int gpencil_sort_points(const void *a1, const void *a2) { - const tGPencilPointCache *ps1 = a1, *ps2 = a2; + const tGPencilPointCache *ps1 = a1, *ps2 = a2; - if (ps1->factor < ps2->factor) return -1; - else if (ps1->factor > ps2->factor) return 1; + if (ps1->factor < ps2->factor) + return -1; + else if (ps1->factor > ps2->factor) + return 1; - return 0; + return 0; } -static void gpencil_insert_points_to_stroke( - bGPDstroke *gps, tGPencilPointCache *points_array, int totpoints) +static void gpencil_insert_points_to_stroke(bGPDstroke *gps, + tGPencilPointCache *points_array, + int totpoints) { - tGPencilPointCache *point_elem = NULL; - - for (int i = 0; i < totpoints; i++) { - point_elem = &points_array[i]; - bGPDspoint *pt_dst = &gps->points[i]; - - copy_v3_v3(&pt_dst->x, &point_elem->x); - pt_dst->pressure = point_elem->pressure; - pt_dst->strength = point_elem->strength; - pt_dst->uv_fac = 1.0f; - pt_dst->uv_rot = 0; - pt_dst->flag |= GP_SPOINT_SELECT; - } - + tGPencilPointCache *point_elem = NULL; + + for (int i = 0; i < totpoints; i++) { + point_elem = &points_array[i]; + bGPDspoint *pt_dst = &gps->points[i]; + + copy_v3_v3(&pt_dst->x, &point_elem->x); + pt_dst->pressure = point_elem->pressure; + pt_dst->strength = point_elem->strength; + pt_dst->uv_fac = 1.0f; + pt_dst->uv_rot = 0; + pt_dst->flag |= GP_SPOINT_SELECT; + } } static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpoints) { - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = CTX_data_active_object(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - const bool back = RNA_boolean_get(op->ptr, "back"); - const bool additive = RNA_boolean_get(op->ptr, "additive"); - const bool cyclic = RNA_boolean_get(op->ptr, "cyclic"); - - Paint *paint = &ts->gp_paint->paint; - /* if not exist, create a new one */ - if (paint->brush == NULL) { - /* create new brushes */ - BKE_brush_gpencil_presets(C); - } - Brush *brush = paint->brush; - - /* frame */ - short add_frame_mode; - if (additive) { - add_frame_mode = GP_GETFRAME_ADD_COPY; - } - else { - add_frame_mode = GP_GETFRAME_ADD_NEW; - } - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, add_frame_mode); - - /* stroke */ - bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - gps->totpoints = totpoints; - gps->inittime = 0.0f; - gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); - gps->flag |= GP_STROKE_SELECT; - gps->flag |= GP_STROKE_3DSPACE; - gps->mat_nr = ob->actcol - 1; - - /* allocate memory for points */ - gps->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, "gp_stroke_points"); - /* initialize triangle memory to dummy data */ - gps->tot_triangles = 0; - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - if (cyclic) { - gps->flag |= GP_STROKE_CYCLIC; - } - - /* add new stroke to frame */ - if (back) { - BLI_addhead(&gpf->strokes, gps); - } - else { - BLI_addtail(&gpf->strokes, gps); - } - - return gps; + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + const bool back = RNA_boolean_get(op->ptr, "back"); + const bool additive = RNA_boolean_get(op->ptr, "additive"); + const bool cyclic = RNA_boolean_get(op->ptr, "cyclic"); + + Paint *paint = &ts->gp_paint->paint; + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + Brush *brush = paint->brush; + + /* frame */ + short add_frame_mode; + if (additive) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, add_frame_mode); + + /* stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + gps->totpoints = totpoints; + gps->inittime = 0.0f; + gps->thickness = brush->size; + gps->gradient_f = brush->gpencil_settings->gradient_f; + copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->flag |= GP_STROKE_SELECT; + gps->flag |= GP_STROKE_3DSPACE; + gps->mat_nr = ob->actcol - 1; + + /* allocate memory for points */ + gps->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, "gp_stroke_points"); + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + if (cyclic) { + gps->flag |= GP_STROKE_CYCLIC; + } + + /* add new stroke to frame */ + if (back) { + BLI_addhead(&gpf->strokes, gps); + } + else { + BLI_addtail(&gpf->strokes, gps); + } + + return gps; } static void gpencil_get_elements_len(bContext *C, int *totstrokes, int *totpoints) { - bGPDspoint *pt; - int i; - - /* count number of strokes and selected points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - *totstrokes += 1; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - *totpoints += 1; - } - } - } - } - CTX_DATA_END; + bGPDspoint *pt; + int i; + + /* count number of strokes and selected points */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + *totstrokes += 1; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *totpoints += 1; + } + } + } + } + CTX_DATA_END; } static void gpencil_dissolve_points(bContext *C) { - bGPDstroke *gps, *gpsn; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false, 0); - } - } - CTX_DATA_END; + bGPDstroke *gps, *gpsn; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + continue; + } + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false, 0); + } + } + CTX_DATA_END; } /* Calc a factor of each selected point and fill an array with all the data. @@ -201,367 +201,368 @@ static void gpencil_dissolve_points(bContext *C) * * All the data is saved to be sorted and used later. */ -static void gpencil_calc_points_factor( - bContext *C, const int mode, int totpoints, - const bool clear_point, const bool clear_stroke, - tGPencilPointCache *src_array) +static void gpencil_calc_points_factor(bContext *C, + const int mode, + int totpoints, + const bool clear_point, + const bool clear_stroke, + tGPencilPointCache *src_array) { - bGPDspoint *pt; - int i; - int idx = 0; - - /* create selected point array an fill it */ - bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * totpoints, __func__); - bGPDspoint *pt_array = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (gps->flag & GP_STROKE_SELECT) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (clear_stroke) { - pt->flag |= GP_SPOINT_TAG; - } - else { - pt->flag &= ~GP_SPOINT_TAG; - } - - if (pt->flag & GP_SPOINT_SELECT) { - bGPDspoint *pt2 = &pt_array[idx]; - copy_v3_v3(&pt2->x, &pt->x); - pt2->pressure = pt->pressure; - pt2->strength = pt->strength; - pt->flag &= ~GP_SPOINT_SELECT; - if (clear_point) { - pt->flag |= GP_SPOINT_TAG; - } - - /* save stroke */ - gps_array[idx] = gps; - - idx++; - } - } - gps->flag &= ~GP_STROKE_SELECT; - } - } - } - CTX_DATA_END; - - /* project in 2d plane */ - int direction = 0; - float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, "GP Stroke temp 2d points"); - BKE_gpencil_stroke_2d_flat(pt_array, totpoints, points2d, &direction); - - /* calc center */ - float center[2] = { 0.0f, 0.0f }; - for (i = 0; i < totpoints; i++) { - center[0] += points2d[i][0]; - center[1] += points2d[i][1]; - } - mul_v2_fl(center, 1.0f / totpoints); - - /* calc angle and distance to center for each point */ - const float axis[2] = { 1.0f, 0.0f }; - float v1[3]; - for (i = 0; i < totpoints; i++) { - float ln = len_v2v2(center, points2d[i]); - sub_v2_v2v2(v1, points2d[i], center); - float angle = angle_signed_v2v2(axis, v1); - if (angle < 0.0f) { - angle = fabsf(angle); - } - else { - angle = (M_PI * 2.0) - angle; - } - tGPencilPointCache *sort_pt = &src_array[i]; - bGPDspoint *pt2 = &pt_array[i]; - - copy_v3_v3(&sort_pt->x, &pt2->x); - sort_pt->pressure = pt2->pressure; - sort_pt->strength = pt2->strength; - - sort_pt->gps = gps_array[i]; - - if (mode == GP_MERGE_STROKE) { - sort_pt->factor = angle; - } - else { - sort_pt->factor = (angle * 100000.0f) + ln; - } - } - MEM_SAFE_FREE(points2d); - MEM_SAFE_FREE(gps_array); - MEM_SAFE_FREE(pt_array); + bGPDspoint *pt; + int i; + int idx = 0; + + /* create selected point array an fill it */ + bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * totpoints, __func__); + bGPDspoint *pt_array = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + continue; + } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->flag & GP_STROKE_SELECT) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (clear_stroke) { + pt->flag |= GP_SPOINT_TAG; + } + else { + pt->flag &= ~GP_SPOINT_TAG; + } + + if (pt->flag & GP_SPOINT_SELECT) { + bGPDspoint *pt2 = &pt_array[idx]; + copy_v3_v3(&pt2->x, &pt->x); + pt2->pressure = pt->pressure; + pt2->strength = pt->strength; + pt->flag &= ~GP_SPOINT_SELECT; + if (clear_point) { + pt->flag |= GP_SPOINT_TAG; + } + + /* save stroke */ + gps_array[idx] = gps; + + idx++; + } + } + gps->flag &= ~GP_STROKE_SELECT; + } + } + } + CTX_DATA_END; + + /* project in 2d plane */ + int direction = 0; + float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, "GP Stroke temp 2d points"); + BKE_gpencil_stroke_2d_flat(pt_array, totpoints, points2d, &direction); + + /* calc center */ + float center[2] = {0.0f, 0.0f}; + for (i = 0; i < totpoints; i++) { + center[0] += points2d[i][0]; + center[1] += points2d[i][1]; + } + mul_v2_fl(center, 1.0f / totpoints); + + /* calc angle and distance to center for each point */ + const float axis[2] = {1.0f, 0.0f}; + float v1[3]; + for (i = 0; i < totpoints; i++) { + float ln = len_v2v2(center, points2d[i]); + sub_v2_v2v2(v1, points2d[i], center); + float angle = angle_signed_v2v2(axis, v1); + if (angle < 0.0f) { + angle = fabsf(angle); + } + else { + angle = (M_PI * 2.0) - angle; + } + tGPencilPointCache *sort_pt = &src_array[i]; + bGPDspoint *pt2 = &pt_array[i]; + + copy_v3_v3(&sort_pt->x, &pt2->x); + sort_pt->pressure = pt2->pressure; + sort_pt->strength = pt2->strength; + + sort_pt->gps = gps_array[i]; + + if (mode == GP_MERGE_STROKE) { + sort_pt->factor = angle; + } + else { + sort_pt->factor = (angle * 100000.0f) + ln; + } + } + MEM_SAFE_FREE(points2d); + MEM_SAFE_FREE(gps_array); + MEM_SAFE_FREE(pt_array); } /* insert a group of points in destination array */ -static int gpencil_insert_to_array( - tGPencilPointCache *src_array, tGPencilPointCache *dst_array, int totpoints, - bGPDstroke *gps_filter, bool reverse, int last) +static int gpencil_insert_to_array(tGPencilPointCache *src_array, + tGPencilPointCache *dst_array, + int totpoints, + bGPDstroke *gps_filter, + bool reverse, + int last) { - tGPencilPointCache *src_elem = NULL; - tGPencilPointCache *dst_elem = NULL; - int idx = 0; - - for (int i = 0; i < totpoints; i++) { - if (!reverse) { - idx = i; - } - else { - idx = totpoints - i - 1; - } - src_elem = &src_array[idx]; - /* check if all points or only a stroke */ - if ((gps_filter != NULL) && (gps_filter != src_elem->gps)) { - continue; - } - - dst_elem = &dst_array[last]; - last++; - - copy_v3_v3(&dst_elem->x, &src_elem->x); - dst_elem->gps = src_elem->gps; - dst_elem->pressure = src_elem->pressure; - dst_elem->strength = src_elem->strength; - dst_elem->factor = src_elem->factor; - } - - return last; + tGPencilPointCache *src_elem = NULL; + tGPencilPointCache *dst_elem = NULL; + int idx = 0; + + for (int i = 0; i < totpoints; i++) { + if (!reverse) { + idx = i; + } + else { + idx = totpoints - i - 1; + } + src_elem = &src_array[idx]; + /* check if all points or only a stroke */ + if ((gps_filter != NULL) && (gps_filter != src_elem->gps)) { + continue; + } + + dst_elem = &dst_array[last]; + last++; + + copy_v3_v3(&dst_elem->x, &src_elem->x); + dst_elem->gps = src_elem->gps; + dst_elem->pressure = src_elem->pressure; + dst_elem->strength = src_elem->strength; + dst_elem->factor = src_elem->factor; + } + + return last; } /* get first and last point location */ static void gpencil_get_extremes( - tGPencilPointCache *src_array, int totpoints, - bGPDstroke *gps_filter, float *start, float *end) + tGPencilPointCache *src_array, int totpoints, bGPDstroke *gps_filter, float *start, float *end) { - tGPencilPointCache *array_pt = NULL; - int i; - - /* find first point */ - for (i = 0; i < totpoints; i++) { - array_pt = &src_array[i]; - if (gps_filter == array_pt->gps) { - copy_v3_v3(start, &array_pt->x); - break; - } - } - /* find last point */ - for (i = totpoints - 1; i >= 0; i--) { - array_pt = &src_array[i]; - if (gps_filter == array_pt->gps) { - copy_v3_v3(end, &array_pt->x); - break; - } - } + tGPencilPointCache *array_pt = NULL; + int i; + + /* find first point */ + for (i = 0; i < totpoints; i++) { + array_pt = &src_array[i]; + if (gps_filter == array_pt->gps) { + copy_v3_v3(start, &array_pt->x); + break; + } + } + /* find last point */ + for (i = totpoints - 1; i >= 0; i--) { + array_pt = &src_array[i]; + if (gps_filter == array_pt->gps) { + copy_v3_v3(end, &array_pt->x); + break; + } + } } -static int gpencil_analyze_strokes( - tGPencilPointCache *src_array, int totstrokes, int totpoints, - tGPencilPointCache *dst_array) +static int gpencil_analyze_strokes(tGPencilPointCache *src_array, + int totstrokes, + int totpoints, + tGPencilPointCache *dst_array) { - int i; - int last = 0; - GHash *all_strokes = BLI_ghash_ptr_new(__func__); - /* add first stroke to array */ - tGPencilPointCache *sort_pt = &src_array[0]; - bGPDstroke *gps = sort_pt->gps; - last = gpencil_insert_to_array(src_array, dst_array, totpoints, gps, false, last); - float start[3]; - float end[3]; - float end_prv[3]; - gpencil_get_extremes(src_array, totpoints, gps, start, end); - copy_v3_v3(end_prv, end); - BLI_ghash_insert(all_strokes, sort_pt->gps, sort_pt->gps); - - /* look for near stroke */ - bool loop = (bool)(totstrokes > 1); - while (loop) { - bGPDstroke *gps_next = NULL; - GHash *strokes = BLI_ghash_ptr_new(__func__); - float dist_start = 0.0f; - float dist_end = 0.0f; - float dist = FLT_MAX; - bool reverse = false; - - for (i = 0; i < totpoints; i++) { - sort_pt = &src_array[i]; - /* avoid dups */ - if (BLI_ghash_haskey(all_strokes, sort_pt->gps)) { - continue; - } - if (!BLI_ghash_haskey(strokes, sort_pt->gps)) { - gpencil_get_extremes(src_array, totpoints, sort_pt->gps, start, end); - /* distances to previous end */ - dist_start = len_v3v3(end_prv, start); - dist_end = len_v3v3(end_prv, end); - - if (dist > dist_start) { - gps_next = sort_pt->gps; - dist = dist_start; - reverse = false; - } - if (dist > dist_end) { - gps_next = sort_pt->gps; - dist = dist_end; - reverse = true; - } - BLI_ghash_insert(strokes, sort_pt->gps, sort_pt->gps); - } - } - BLI_ghash_free(strokes, NULL, NULL); - - /* add the stroke to array */ - if (gps->next != NULL) { - BLI_ghash_insert(all_strokes, gps_next, gps_next); - last = gpencil_insert_to_array(src_array, dst_array, totpoints, gps_next, reverse, last); - /* replace last end */ - sort_pt = &dst_array[last - 1]; - copy_v3_v3(end_prv, &sort_pt->x); - } - - /* loop exit */ - if (last >= totpoints) { - loop = false; - } - } - - BLI_ghash_free(all_strokes, NULL, NULL); - return last; + int i; + int last = 0; + GHash *all_strokes = BLI_ghash_ptr_new(__func__); + /* add first stroke to array */ + tGPencilPointCache *sort_pt = &src_array[0]; + bGPDstroke *gps = sort_pt->gps; + last = gpencil_insert_to_array(src_array, dst_array, totpoints, gps, false, last); + float start[3]; + float end[3]; + float end_prv[3]; + gpencil_get_extremes(src_array, totpoints, gps, start, end); + copy_v3_v3(end_prv, end); + BLI_ghash_insert(all_strokes, sort_pt->gps, sort_pt->gps); + + /* look for near stroke */ + bool loop = (bool)(totstrokes > 1); + while (loop) { + bGPDstroke *gps_next = NULL; + GHash *strokes = BLI_ghash_ptr_new(__func__); + float dist_start = 0.0f; + float dist_end = 0.0f; + float dist = FLT_MAX; + bool reverse = false; + + for (i = 0; i < totpoints; i++) { + sort_pt = &src_array[i]; + /* avoid dups */ + if (BLI_ghash_haskey(all_strokes, sort_pt->gps)) { + continue; + } + if (!BLI_ghash_haskey(strokes, sort_pt->gps)) { + gpencil_get_extremes(src_array, totpoints, sort_pt->gps, start, end); + /* distances to previous end */ + dist_start = len_v3v3(end_prv, start); + dist_end = len_v3v3(end_prv, end); + + if (dist > dist_start) { + gps_next = sort_pt->gps; + dist = dist_start; + reverse = false; + } + if (dist > dist_end) { + gps_next = sort_pt->gps; + dist = dist_end; + reverse = true; + } + BLI_ghash_insert(strokes, sort_pt->gps, sort_pt->gps); + } + } + BLI_ghash_free(strokes, NULL, NULL); + + /* add the stroke to array */ + if (gps->next != NULL) { + BLI_ghash_insert(all_strokes, gps_next, gps_next); + last = gpencil_insert_to_array(src_array, dst_array, totpoints, gps_next, reverse, last); + /* replace last end */ + sort_pt = &dst_array[last - 1]; + copy_v3_v3(end_prv, &sort_pt->x); + } + + /* loop exit */ + if (last >= totpoints) { + loop = false; + } + } + + BLI_ghash_free(all_strokes, NULL, NULL); + return last; } static bool gp_strokes_merge_poll(bContext *C) { - /* only supported with grease pencil objects */ - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - /* check material */ - Material *ma = NULL; - ma = give_current_material(ob, ob->actcol); - if ((ma == NULL) || (ma->gp_style == NULL)) { - return false; - } - - /* check hidden or locked materials */ - MaterialGPencilStyle *gp_style = ma->gp_style; - if ((gp_style->flag & GP_STYLE_COLOR_HIDE) || - (gp_style->flag & GP_STYLE_COLOR_LOCKED)) - { - return false; - } - - /* check layer */ - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - if ((gpl == NULL) || - (gpl->flag & GP_LAYER_LOCKED) || - (gpl->flag & GP_LAYER_HIDE)) - { - return false; - } - - /* NOTE: this is a bit slower, but is the most accurate... */ - return (CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0) && ED_operator_view3d_active(C); + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + /* check material */ + Material *ma = NULL; + ma = give_current_material(ob, ob->actcol); + if ((ma == NULL) || (ma->gp_style == NULL)) { + return false; + } + + /* check hidden or locked materials */ + MaterialGPencilStyle *gp_style = ma->gp_style; + if ((gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + return false; + } + + /* check layer */ + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if ((gpl == NULL) || (gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE)) { + return false; + } + + /* NOTE: this is a bit slower, but is the most accurate... */ + return (CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0) && ED_operator_view3d_active(C); } static int gp_stroke_merge_exec(bContext *C, wmOperator *op) { - const int mode = RNA_enum_get(op->ptr, "mode"); - const bool clear_point = RNA_boolean_get(op->ptr, "clear_point"); - const bool clear_stroke = RNA_boolean_get(op->ptr, "clear_stroke"); - - Object *ob = CTX_data_active_object(C); - /* sanity checks */ - if (!ob || ob->type != OB_GPENCIL) { - return OPERATOR_CANCELLED; - } - - bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - if (gpl == NULL) { - return OPERATOR_CANCELLED; - } - - int totstrokes = 0; - int totpoints = 0; - - /* count number of strokes and selected points */ - gpencil_get_elements_len(C, &totstrokes, &totpoints); - - if (totpoints == 0) { - return OPERATOR_CANCELLED; - } - - /* calc factor of each point and fill an array with all data */ - tGPencilPointCache *sorted_array = NULL; - tGPencilPointCache *original_array = MEM_callocN(sizeof(tGPencilPointCache) * totpoints, __func__); - gpencil_calc_points_factor(C, mode, totpoints, clear_point, clear_stroke, original_array); - - /* for strokes analyze strokes and load sorted array */ - if (mode == GP_MERGE_STROKE) { - sorted_array = MEM_callocN(sizeof(tGPencilPointCache) * totpoints, __func__); - totpoints = gpencil_analyze_strokes(original_array, totstrokes, totpoints, sorted_array); - } - else { - /* make a copy to sort */ - sorted_array = MEM_dupallocN(original_array); - /* sort by factor around center */ - qsort(sorted_array, totpoints, sizeof(tGPencilPointCache), gpencil_sort_points); - } - - /* prepare the new stroke */ - bGPDstroke *gps = gpencil_prepare_stroke(C, op, totpoints); - - /* copy original points to final stroke */ - gpencil_insert_points_to_stroke(gps, sorted_array, totpoints); - - /* dissolve all tagged points */ - if ((clear_point) || (clear_stroke)) { - gpencil_dissolve_points(C); - } - - /* free memory */ - MEM_SAFE_FREE(original_array); - MEM_SAFE_FREE(sorted_array); - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + const int mode = RNA_enum_get(op->ptr, "mode"); + const bool clear_point = RNA_boolean_get(op->ptr, "clear_point"); + const bool clear_stroke = RNA_boolean_get(op->ptr, "clear_stroke"); + + Object *ob = CTX_data_active_object(C); + /* sanity checks */ + if (!ob || ob->type != OB_GPENCIL) { + return OPERATOR_CANCELLED; + } + + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if (gpl == NULL) { + return OPERATOR_CANCELLED; + } + + int totstrokes = 0; + int totpoints = 0; + + /* count number of strokes and selected points */ + gpencil_get_elements_len(C, &totstrokes, &totpoints); + + if (totpoints == 0) { + return OPERATOR_CANCELLED; + } + + /* calc factor of each point and fill an array with all data */ + tGPencilPointCache *sorted_array = NULL; + tGPencilPointCache *original_array = MEM_callocN(sizeof(tGPencilPointCache) * totpoints, + __func__); + gpencil_calc_points_factor(C, mode, totpoints, clear_point, clear_stroke, original_array); + + /* for strokes analyze strokes and load sorted array */ + if (mode == GP_MERGE_STROKE) { + sorted_array = MEM_callocN(sizeof(tGPencilPointCache) * totpoints, __func__); + totpoints = gpencil_analyze_strokes(original_array, totstrokes, totpoints, sorted_array); + } + else { + /* make a copy to sort */ + sorted_array = MEM_dupallocN(original_array); + /* sort by factor around center */ + qsort(sorted_array, totpoints, sizeof(tGPencilPointCache), gpencil_sort_points); + } + + /* prepare the new stroke */ + bGPDstroke *gps = gpencil_prepare_stroke(C, op, totpoints); + + /* copy original points to final stroke */ + gpencil_insert_points_to_stroke(gps, sorted_array, totpoints); + + /* dissolve all tagged points */ + if ((clear_point) || (clear_stroke)) { + gpencil_dissolve_points(C); + } + + /* free memory */ + MEM_SAFE_FREE(original_array); + MEM_SAFE_FREE(sorted_array); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_stroke_merge(wmOperatorType *ot) { - static const EnumPropertyItem mode_type[] = { - {GP_MERGE_STROKE, "STROKE", 0, "Stroke", ""}, - {GP_MERGE_POINT, "POINT", 0, "Point", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Merge Strokes"; - ot->idname = "GPENCIL_OT_stroke_merge"; - ot->description = "Create a new stroke with the selected stroke points"; - - /* api callbacks */ - ot->exec = gp_stroke_merge_exec; - ot->poll = gp_strokes_merge_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, GP_MERGE_STROKE, "Mode", ""); - RNA_def_boolean(ot->srna, "back", 0, "Draw on Back", "Draw new stroke below all previous strokes"); - RNA_def_boolean(ot->srna, "additive", 0, "Additive Drawing", "Add to previous drawing"); - RNA_def_boolean(ot->srna, "cyclic", 0, "Cyclic", "Close new stroke"); - RNA_def_boolean(ot->srna, "clear_point", 0, "Dissolve Points", "Dissolve old selected points"); - RNA_def_boolean(ot->srna, "clear_stroke", 0, "Delete Strokes", "Delete old selected strokes"); + static const EnumPropertyItem mode_type[] = { + {GP_MERGE_STROKE, "STROKE", 0, "Stroke", ""}, + {GP_MERGE_POINT, "POINT", 0, "Point", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Merge Strokes"; + ot->idname = "GPENCIL_OT_stroke_merge"; + ot->description = "Create a new stroke with the selected stroke points"; + + /* api callbacks */ + ot->exec = gp_stroke_merge_exec; + ot->poll = gp_strokes_merge_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, GP_MERGE_STROKE, "Mode", ""); + RNA_def_boolean( + ot->srna, "back", 0, "Draw on Back", "Draw new stroke below all previous strokes"); + RNA_def_boolean(ot->srna, "additive", 0, "Additive Drawing", "Add to previous drawing"); + RNA_def_boolean(ot->srna, "cyclic", 0, "Cyclic", "Close new stroke"); + RNA_def_boolean(ot->srna, "clear_point", 0, "Dissolve Points", "Dissolve old selected points"); + RNA_def_boolean(ot->srna, "clear_stroke", 0, "Delete Strokes", "Delete old selected strokes"); } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 779094c21ea..fce8fa79217 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdlib.h> #include <stddef.h> #include <stdio.h> @@ -58,7 +57,7 @@ /* Generic Drawing Keymap - Annotations */ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) { - WM_keymap_ensure(keyconf, "Grease Pencil", 0, 0); + WM_keymap_ensure(keyconf, "Grease Pencil", 0, 0); } /* ==================== */ @@ -66,317 +65,316 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) /* Poll callback for stroke editing mode */ static bool gp_stroke_editmode_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + bGPdata *gpd = CTX_data_gpencil_data(C); + return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)); } /* Poll callback for stroke painting mode */ static bool gp_stroke_paintmode_poll(bContext *C) { - /* TODO: limit this to mode, but review 2D editors */ - bGPdata *gpd = CTX_data_gpencil_data(C); - return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE)); + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE)); } static bool gp_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_tool) { - /* TODO: limit this to mode, but review 2D editors */ - bGPdata *gpd = CTX_data_gpencil_data(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - return ((gpd) && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && - (brush && brush->gpencil_settings) && - WM_toolsystem_active_tool_is_brush(C) && - (brush->gpencil_tool == gpencil_tool)); + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + return ((gpd) && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush && brush->gpencil_settings) && + WM_toolsystem_active_tool_is_brush(C) && (brush->gpencil_tool == gpencil_tool)); } /* Poll callback for stroke painting (draw brush) */ static bool gp_stroke_paintmode_draw_poll(bContext *C) { - return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW); + return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW); } /* Poll callback for stroke painting (erase brush) */ static bool gp_stroke_paintmode_erase_poll(bContext *C) { - return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_ERASE); + return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_ERASE); } /* Poll callback for stroke painting (fill) */ static bool gp_stroke_paintmode_fill_poll(bContext *C) { - return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL); + return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL); } /* Poll callback for stroke sculpting mode */ static bool gp_stroke_sculptmode_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - Object *ob = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - - /* if not gpencil object and not view3d, need sculpt keys if edit mode */ - if (sa->spacetype != SPACE_VIEW3D) { - return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); - } - else { - /* weight paint is a submode of sculpt */ - if ((ob) && (ob->type == OB_GPENCIL)) { - return GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd); - } - } - - return 0; + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + + /* if not gpencil object and not view3d, need sculpt keys if edit mode */ + if (sa->spacetype != SPACE_VIEW3D) { + return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + } + else { + /* weight paint is a submode of sculpt */ + if ((ob) && (ob->type == OB_GPENCIL)) { + return GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd); + } + } + + return 0; } /* Poll callback for stroke weight paint mode */ static bool gp_stroke_weightmode_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - Object *ob = CTX_data_active_object(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); - if ((ob) && (ob->type == OB_GPENCIL)) { - return (gpd && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)); - } + if ((ob) && (ob->type == OB_GPENCIL)) { + return (gpd && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)); + } - return 0; + return 0; } /* Stroke Editing Keymap - Only when editmode is enabled */ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) { - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); - /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ - keymap->poll = gp_stroke_editmode_poll; + /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ + keymap->poll = gp_stroke_editmode_poll; } /* keys for draw with a drawing brush (no fill) */ static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf) { - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); - keymap->poll = gp_stroke_paintmode_draw_poll; + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); + keymap->poll = gp_stroke_paintmode_draw_poll; } /* keys for draw with a eraser brush (erase) */ static void ed_keymap_gpencil_painting_erase(wmKeyConfig *keyconf) { - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); - keymap->poll = gp_stroke_paintmode_erase_poll; + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); + keymap->poll = gp_stroke_paintmode_erase_poll; } /* keys for draw with a fill brush */ static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf) { - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); - keymap->poll = gp_stroke_paintmode_fill_poll; + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); + keymap->poll = gp_stroke_paintmode_fill_poll; } /* Stroke Painting Keymap - Only when paintmode is enabled */ static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf) { - /* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */ - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0); - keymap->poll = gp_stroke_paintmode_poll; + /* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0); + keymap->poll = gp_stroke_paintmode_poll; } /* Stroke Sculpting Keymap - Only when sculptmode is enabled */ static void ed_keymap_gpencil_sculpting(wmKeyConfig *keyconf) { - /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); - keymap->poll = gp_stroke_sculptmode_poll; + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); + keymap->poll = gp_stroke_sculptmode_poll; } /* Stroke Weight Paint Keymap - Only when weight is enabled */ static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf) { - /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ - wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0); - keymap->poll = gp_stroke_weightmode_poll; + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0); + keymap->poll = gp_stroke_weightmode_poll; } /* ==================== */ void ED_keymap_gpencil(wmKeyConfig *keyconf) { - ed_keymap_gpencil_general(keyconf); - ed_keymap_gpencil_editing(keyconf); - ed_keymap_gpencil_painting(keyconf); - ed_keymap_gpencil_painting_draw(keyconf); - ed_keymap_gpencil_painting_erase(keyconf); - ed_keymap_gpencil_painting_fill(keyconf); - ed_keymap_gpencil_sculpting(keyconf); - ed_keymap_gpencil_weightpainting(keyconf); + ed_keymap_gpencil_general(keyconf); + ed_keymap_gpencil_editing(keyconf); + ed_keymap_gpencil_painting(keyconf); + ed_keymap_gpencil_painting_draw(keyconf); + ed_keymap_gpencil_painting_erase(keyconf); + ed_keymap_gpencil_painting_fill(keyconf); + ed_keymap_gpencil_sculpting(keyconf); + ed_keymap_gpencil_weightpainting(keyconf); } /* ****************************************** */ void ED_operatortypes_gpencil(void) { - /* Annotations -------------------- */ - - WM_operatortype_append(GPENCIL_OT_annotate); - - /* Drawing ----------------------- */ - - WM_operatortype_append(GPENCIL_OT_draw); - WM_operatortype_append(GPENCIL_OT_fill); - - /* Guides ----------------------- */ - - WM_operatortype_append(GPENCIL_OT_guide_rotate); - - /* Editing (Strokes) ------------ */ - - WM_operatortype_append(GPENCIL_OT_editmode_toggle); - WM_operatortype_append(GPENCIL_OT_selectmode_toggle); - WM_operatortype_append(GPENCIL_OT_paintmode_toggle); - WM_operatortype_append(GPENCIL_OT_sculptmode_toggle); - WM_operatortype_append(GPENCIL_OT_weightmode_toggle); - WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); - - WM_operatortype_append(GPENCIL_OT_select); - WM_operatortype_append(GPENCIL_OT_select_all); - WM_operatortype_append(GPENCIL_OT_select_circle); - WM_operatortype_append(GPENCIL_OT_select_box); - WM_operatortype_append(GPENCIL_OT_select_lasso); - - WM_operatortype_append(GPENCIL_OT_select_linked); - WM_operatortype_append(GPENCIL_OT_select_grouped); - WM_operatortype_append(GPENCIL_OT_select_more); - WM_operatortype_append(GPENCIL_OT_select_less); - WM_operatortype_append(GPENCIL_OT_select_first); - WM_operatortype_append(GPENCIL_OT_select_last); - WM_operatortype_append(GPENCIL_OT_select_alternate); - - WM_operatortype_append(GPENCIL_OT_duplicate); - WM_operatortype_append(GPENCIL_OT_delete); - WM_operatortype_append(GPENCIL_OT_dissolve); - WM_operatortype_append(GPENCIL_OT_copy); - WM_operatortype_append(GPENCIL_OT_paste); - WM_operatortype_append(GPENCIL_OT_extrude); - - WM_operatortype_append(GPENCIL_OT_move_to_layer); - WM_operatortype_append(GPENCIL_OT_layer_change); - - WM_operatortype_append(GPENCIL_OT_snap_to_grid); - WM_operatortype_append(GPENCIL_OT_snap_to_cursor); - WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); - - WM_operatortype_append(GPENCIL_OT_reproject); - - WM_operatortype_append(GPENCIL_OT_sculpt_paint); - - /* Editing (Buttons) ------------ */ - - WM_operatortype_append(GPENCIL_OT_data_add); - WM_operatortype_append(GPENCIL_OT_data_unlink); - - WM_operatortype_append(GPENCIL_OT_layer_add); - WM_operatortype_append(GPENCIL_OT_layer_remove); - WM_operatortype_append(GPENCIL_OT_layer_move); - WM_operatortype_append(GPENCIL_OT_layer_duplicate); - WM_operatortype_append(GPENCIL_OT_layer_duplicate_object); - - WM_operatortype_append(GPENCIL_OT_hide); - WM_operatortype_append(GPENCIL_OT_reveal); - WM_operatortype_append(GPENCIL_OT_lock_all); - WM_operatortype_append(GPENCIL_OT_unlock_all); - WM_operatortype_append(GPENCIL_OT_layer_isolate); - WM_operatortype_append(GPENCIL_OT_layer_merge); - - WM_operatortype_append(GPENCIL_OT_blank_frame_add); - - WM_operatortype_append(GPENCIL_OT_active_frame_delete); - WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); - WM_operatortype_append(GPENCIL_OT_frame_duplicate); - WM_operatortype_append(GPENCIL_OT_frame_clean_fill); - WM_operatortype_append(GPENCIL_OT_frame_clean_loose); - - WM_operatortype_append(GPENCIL_OT_convert); - - WM_operatortype_append(GPENCIL_OT_stroke_arrange); - WM_operatortype_append(GPENCIL_OT_stroke_change_color); - WM_operatortype_append(GPENCIL_OT_stroke_lock_color); - WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); - WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); - WM_operatortype_append(GPENCIL_OT_stroke_caps_set); - WM_operatortype_append(GPENCIL_OT_stroke_join); - WM_operatortype_append(GPENCIL_OT_stroke_flip); - WM_operatortype_append(GPENCIL_OT_stroke_subdivide); - WM_operatortype_append(GPENCIL_OT_stroke_simplify); - WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed); - WM_operatortype_append(GPENCIL_OT_stroke_separate); - WM_operatortype_append(GPENCIL_OT_stroke_split); - WM_operatortype_append(GPENCIL_OT_stroke_smooth); - WM_operatortype_append(GPENCIL_OT_stroke_merge); - WM_operatortype_append(GPENCIL_OT_stroke_cutter); - WM_operatortype_append(GPENCIL_OT_stroke_trim); - - WM_operatortype_append(GPENCIL_OT_brush_presets_create); - - /* vertex groups */ - WM_operatortype_append(GPENCIL_OT_vertex_group_assign); - WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from); - WM_operatortype_append(GPENCIL_OT_vertex_group_select); - WM_operatortype_append(GPENCIL_OT_vertex_group_deselect); - WM_operatortype_append(GPENCIL_OT_vertex_group_invert); - WM_operatortype_append(GPENCIL_OT_vertex_group_smooth); - WM_operatortype_append(GPENCIL_OT_vertex_group_normalize); - WM_operatortype_append(GPENCIL_OT_vertex_group_normalize_all); - - /* color handle */ - WM_operatortype_append(GPENCIL_OT_lock_layer); - WM_operatortype_append(GPENCIL_OT_color_isolate); - WM_operatortype_append(GPENCIL_OT_color_hide); - WM_operatortype_append(GPENCIL_OT_color_reveal); - WM_operatortype_append(GPENCIL_OT_color_lock_all); - WM_operatortype_append(GPENCIL_OT_color_unlock_all); - WM_operatortype_append(GPENCIL_OT_color_select); - - /* Editing (Time) --------------- */ - - /* Interpolation */ - WM_operatortype_append(GPENCIL_OT_interpolate); - WM_operatortype_append(GPENCIL_OT_interpolate_sequence); - WM_operatortype_append(GPENCIL_OT_interpolate_reverse); - - /* Primitives */ - WM_operatortype_append(GPENCIL_OT_primitive); - - /* convert old 2.7 files to 2.8 */ - WM_operatortype_append(GPENCIL_OT_convert_old_files); - - /* armatures */ - WM_operatortype_append(GPENCIL_OT_generate_weights); + /* Annotations -------------------- */ + + WM_operatortype_append(GPENCIL_OT_annotate); + + /* Drawing ----------------------- */ + + WM_operatortype_append(GPENCIL_OT_draw); + WM_operatortype_append(GPENCIL_OT_fill); + + /* Guides ----------------------- */ + + WM_operatortype_append(GPENCIL_OT_guide_rotate); + + /* Editing (Strokes) ------------ */ + + WM_operatortype_append(GPENCIL_OT_editmode_toggle); + WM_operatortype_append(GPENCIL_OT_selectmode_toggle); + WM_operatortype_append(GPENCIL_OT_paintmode_toggle); + WM_operatortype_append(GPENCIL_OT_sculptmode_toggle); + WM_operatortype_append(GPENCIL_OT_weightmode_toggle); + WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); + + WM_operatortype_append(GPENCIL_OT_select); + WM_operatortype_append(GPENCIL_OT_select_all); + WM_operatortype_append(GPENCIL_OT_select_circle); + WM_operatortype_append(GPENCIL_OT_select_box); + WM_operatortype_append(GPENCIL_OT_select_lasso); + + WM_operatortype_append(GPENCIL_OT_select_linked); + WM_operatortype_append(GPENCIL_OT_select_grouped); + WM_operatortype_append(GPENCIL_OT_select_more); + WM_operatortype_append(GPENCIL_OT_select_less); + WM_operatortype_append(GPENCIL_OT_select_first); + WM_operatortype_append(GPENCIL_OT_select_last); + WM_operatortype_append(GPENCIL_OT_select_alternate); + + WM_operatortype_append(GPENCIL_OT_duplicate); + WM_operatortype_append(GPENCIL_OT_delete); + WM_operatortype_append(GPENCIL_OT_dissolve); + WM_operatortype_append(GPENCIL_OT_copy); + WM_operatortype_append(GPENCIL_OT_paste); + WM_operatortype_append(GPENCIL_OT_extrude); + + WM_operatortype_append(GPENCIL_OT_move_to_layer); + WM_operatortype_append(GPENCIL_OT_layer_change); + + WM_operatortype_append(GPENCIL_OT_snap_to_grid); + WM_operatortype_append(GPENCIL_OT_snap_to_cursor); + WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); + + WM_operatortype_append(GPENCIL_OT_reproject); + + WM_operatortype_append(GPENCIL_OT_sculpt_paint); + + /* Editing (Buttons) ------------ */ + + WM_operatortype_append(GPENCIL_OT_data_add); + WM_operatortype_append(GPENCIL_OT_data_unlink); + + WM_operatortype_append(GPENCIL_OT_layer_add); + WM_operatortype_append(GPENCIL_OT_layer_remove); + WM_operatortype_append(GPENCIL_OT_layer_move); + WM_operatortype_append(GPENCIL_OT_layer_duplicate); + WM_operatortype_append(GPENCIL_OT_layer_duplicate_object); + + WM_operatortype_append(GPENCIL_OT_hide); + WM_operatortype_append(GPENCIL_OT_reveal); + WM_operatortype_append(GPENCIL_OT_lock_all); + WM_operatortype_append(GPENCIL_OT_unlock_all); + WM_operatortype_append(GPENCIL_OT_layer_isolate); + WM_operatortype_append(GPENCIL_OT_layer_merge); + + WM_operatortype_append(GPENCIL_OT_blank_frame_add); + + WM_operatortype_append(GPENCIL_OT_active_frame_delete); + WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); + WM_operatortype_append(GPENCIL_OT_frame_duplicate); + WM_operatortype_append(GPENCIL_OT_frame_clean_fill); + WM_operatortype_append(GPENCIL_OT_frame_clean_loose); + + WM_operatortype_append(GPENCIL_OT_convert); + + WM_operatortype_append(GPENCIL_OT_stroke_arrange); + WM_operatortype_append(GPENCIL_OT_stroke_change_color); + WM_operatortype_append(GPENCIL_OT_stroke_lock_color); + WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); + WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); + WM_operatortype_append(GPENCIL_OT_stroke_caps_set); + WM_operatortype_append(GPENCIL_OT_stroke_join); + WM_operatortype_append(GPENCIL_OT_stroke_flip); + WM_operatortype_append(GPENCIL_OT_stroke_subdivide); + WM_operatortype_append(GPENCIL_OT_stroke_simplify); + WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed); + WM_operatortype_append(GPENCIL_OT_stroke_separate); + WM_operatortype_append(GPENCIL_OT_stroke_split); + WM_operatortype_append(GPENCIL_OT_stroke_smooth); + WM_operatortype_append(GPENCIL_OT_stroke_merge); + WM_operatortype_append(GPENCIL_OT_stroke_cutter); + WM_operatortype_append(GPENCIL_OT_stroke_trim); + + WM_operatortype_append(GPENCIL_OT_brush_presets_create); + + /* vertex groups */ + WM_operatortype_append(GPENCIL_OT_vertex_group_assign); + WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from); + WM_operatortype_append(GPENCIL_OT_vertex_group_select); + WM_operatortype_append(GPENCIL_OT_vertex_group_deselect); + WM_operatortype_append(GPENCIL_OT_vertex_group_invert); + WM_operatortype_append(GPENCIL_OT_vertex_group_smooth); + WM_operatortype_append(GPENCIL_OT_vertex_group_normalize); + WM_operatortype_append(GPENCIL_OT_vertex_group_normalize_all); + + /* color handle */ + WM_operatortype_append(GPENCIL_OT_lock_layer); + WM_operatortype_append(GPENCIL_OT_color_isolate); + WM_operatortype_append(GPENCIL_OT_color_hide); + WM_operatortype_append(GPENCIL_OT_color_reveal); + WM_operatortype_append(GPENCIL_OT_color_lock_all); + WM_operatortype_append(GPENCIL_OT_color_unlock_all); + WM_operatortype_append(GPENCIL_OT_color_select); + + /* Editing (Time) --------------- */ + + /* Interpolation */ + WM_operatortype_append(GPENCIL_OT_interpolate); + WM_operatortype_append(GPENCIL_OT_interpolate_sequence); + WM_operatortype_append(GPENCIL_OT_interpolate_reverse); + + /* Primitives */ + WM_operatortype_append(GPENCIL_OT_primitive); + + /* convert old 2.7 files to 2.8 */ + WM_operatortype_append(GPENCIL_OT_convert_old_files); + + /* armatures */ + WM_operatortype_append(GPENCIL_OT_generate_weights); } void ED_operatormacros_gpencil(void) { - wmOperatorType *ot; - wmOperatorTypeMacro *otmacro; - - /* Duplicate + Move = Interactively place newly duplicated strokes */ - ot = WM_operatortype_append_macro( - "GPENCIL_OT_duplicate_move", "Duplicate Strokes", - "Make copies of the selected Grease Pencil strokes and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); - WM_operatortype_macro_define(ot, "GPENCIL_OT_duplicate"); - otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); - RNA_enum_set(otmacro->ptr, "proportional", 0); - RNA_boolean_set(otmacro->ptr, "mirror", false); - - /* Extrude + Move = Interactively add new points */ - ot = WM_operatortype_append_macro( - "GPENCIL_OT_extrude_move", "Extrude Stroke Points", - "Extrude selected points and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); - WM_operatortype_macro_define(ot, "GPENCIL_OT_extrude"); - otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); - RNA_enum_set(otmacro->ptr, "proportional", 0); - RNA_boolean_set(otmacro->ptr, "mirror", false); + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + /* Duplicate + Move = Interactively place newly duplicated strokes */ + ot = WM_operatortype_append_macro( + "GPENCIL_OT_duplicate_move", + "Duplicate Strokes", + "Make copies of the selected Grease Pencil strokes and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "GPENCIL_OT_duplicate"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); + RNA_enum_set(otmacro->ptr, "proportional", 0); + RNA_boolean_set(otmacro->ptr, "mirror", false); + + /* Extrude + Move = Interactively add new points */ + ot = WM_operatortype_append_macro("GPENCIL_OT_extrude_move", + "Extrude Stroke Points", + "Extrude selected points and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "GPENCIL_OT_extrude"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); + RNA_enum_set(otmacro->ptr, "proportional", 0); + RNA_boolean_set(otmacro->ptr, "mirror", false); } /* ****************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c index 9fdb5a9b174..af49587f9ad 100644 --- a/source/blender/editors/gpencil/gpencil_ops_versioning.c +++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c @@ -22,7 +22,7 @@ * \ingroup edgpencil */ - /* allow to use deprecated functionality */ +/* allow to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW #include <stdio.h> @@ -56,162 +56,161 @@ #include "gpencil_intern.h" - /* Free all of a gp-colors */ +/* Free all of a gp-colors */ static void free_gpencil_colors(bGPDpalette *palette) { - /* error checking */ - if (palette == NULL) { - return; - } + /* error checking */ + if (palette == NULL) { + return; + } - /* free colors */ - BLI_freelistN(&palette->colors); + /* free colors */ + BLI_freelistN(&palette->colors); } /* Free all of the gp-palettes and colors */ static void free_palettes(ListBase *list) { - bGPDpalette *palette_next; - - /* error checking */ - if (list == NULL) { - return; - } - - /* delete palettes */ - for (bGPDpalette *palette = list->first; palette; palette = palette_next) { - palette_next = palette->next; - /* free palette colors */ - free_gpencil_colors(palette); - - MEM_freeN(palette); - } - BLI_listbase_clear(list); + bGPDpalette *palette_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete palettes */ + for (bGPDpalette *palette = list->first; palette; palette = palette_next) { + palette_next = palette->next; + /* free palette colors */ + free_gpencil_colors(palette); + + MEM_freeN(palette); + } + BLI_listbase_clear(list); } /* ***************** Convert old 2.7 files to 2.8 ************************ */ static bool gpencil_convert_old_files_poll(bContext *C) { - Scene *scene = CTX_data_scene(C); + Scene *scene = CTX_data_scene(C); - return (int) (scene->gpd != NULL); + return (int)(scene->gpd != NULL); } static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool is_annotation = RNA_boolean_get(op->ptr, "annotation"); - bGPdata *gpd = scene->gpd; - - /* Convert grease pencil scene datablock to GP object */ - if ((!is_annotation) && (view_layer != NULL)) { - Object *ob; - ob = BKE_object_add_for_data(bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); - zero_v3(ob->loc); - - /* convert grease pencil palettes (version >= 2.78) to materials and weights */ - for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { - for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - - /* create material slot */ - Material *ma = BKE_gpencil_object_material_new(bmain, ob, palcolor->info, NULL); - - /* copy color settings */ - MaterialGPencilStyle *gp_style = ma->gp_style; - copy_v4_v4(gp_style->stroke_rgba, palcolor->color); - copy_v4_v4(gp_style->fill_rgba, palcolor->fill); - - /* set basic settings */ - gp_style->pattern_gridsize = 0.1f; - gp_style->gradient_radius = 0.5f; - ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f); - ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f); - ARRAY_SET_ITEMS(gp_style->texture_scale, 1.0f, 1.0f); - gp_style->texture_opacity = 1.0f; - gp_style->texture_pixsize = 100.0f; - - gp_style->flag |= GP_STYLE_STROKE_SHOW; - gp_style->flag |= GP_STYLE_FILL_SHOW; - - /* fix strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if ((gps->colorname[0] != '\0') && - (STREQ(gps->colorname, palcolor->info))) - { - gps->mat_nr = ob->totcol - 1; - gps->colorname[0] = '\0'; - /* weights array */ - gps->dvert = NULL; - } - } - } - } - } - } - - /* free palettes */ - free_palettes(&gpd->palettes); - - /* disable all GP modes */ - ED_gpencil_setup_modes(C, gpd, 0); - - /* set cache as dirty */ - BKE_gpencil_batch_cache_dirty_tag(ob->data); - - scene->gpd = NULL; - } - - if (is_annotation) { - for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { - for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - /* fix layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* unlock/unhide layer */ - gpl->flag &= ~GP_LAYER_LOCKED; - gpl->flag &= ~GP_LAYER_HIDE; - /* set opacity to 1 */ - gpl->opacity = 1.0f; - /* disable tint */ - gpl->tintcolor[3] = 0.0f; - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if ((gps->colorname[0] != '\0') && - (STREQ(gps->colorname, palcolor->info))) - { - /* copy color settings */ - copy_v4_v4(gpl->color, palcolor->color); - } - } - } - } - } - } - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool is_annotation = RNA_boolean_get(op->ptr, "annotation"); + bGPdata *gpd = scene->gpd; + + /* Convert grease pencil scene datablock to GP object */ + if ((!is_annotation) && (view_layer != NULL)) { + Object *ob; + ob = BKE_object_add_for_data( + bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); + zero_v3(ob->loc); + + /* convert grease pencil palettes (version >= 2.78) to materials and weights */ + for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; + palcolor = palcolor->next) { + + /* create material slot */ + Material *ma = BKE_gpencil_object_material_new(bmain, ob, palcolor->info, NULL); + + /* copy color settings */ + MaterialGPencilStyle *gp_style = ma->gp_style; + copy_v4_v4(gp_style->stroke_rgba, palcolor->color); + copy_v4_v4(gp_style->fill_rgba, palcolor->fill); + + /* set basic settings */ + gp_style->pattern_gridsize = 0.1f; + gp_style->gradient_radius = 0.5f; + ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f); + ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f); + ARRAY_SET_ITEMS(gp_style->texture_scale, 1.0f, 1.0f); + gp_style->texture_opacity = 1.0f; + gp_style->texture_pixsize = 100.0f; + + gp_style->flag |= GP_STYLE_STROKE_SHOW; + gp_style->flag |= GP_STYLE_FILL_SHOW; + + /* fix strokes */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { + gps->mat_nr = ob->totcol - 1; + gps->colorname[0] = '\0'; + /* weights array */ + gps->dvert = NULL; + } + } + } + } + } + } + + /* free palettes */ + free_palettes(&gpd->palettes); + + /* disable all GP modes */ + ED_gpencil_setup_modes(C, gpd, 0); + + /* set cache as dirty */ + BKE_gpencil_batch_cache_dirty_tag(ob->data); + + scene->gpd = NULL; + } + + if (is_annotation) { + for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; + palcolor = palcolor->next) { + /* fix layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* unlock/unhide layer */ + gpl->flag &= ~GP_LAYER_LOCKED; + gpl->flag &= ~GP_LAYER_HIDE; + /* set opacity to 1 */ + gpl->opacity = 1.0f; + /* disable tint */ + gpl->tintcolor[3] = 0.0f; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { + /* copy color settings */ + copy_v4_v4(gpl->color, palcolor->color); + } + } + } + } + } + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_convert_old_files(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Convert Grease Pencil"; - ot->idname = "GPENCIL_OT_convert_old_files"; - ot->description = "Convert 2.7x grease pencil files to 2.80"; + /* identifiers */ + ot->name = "Convert Grease Pencil"; + ot->idname = "GPENCIL_OT_convert_old_files"; + ot->description = "Convert 2.7x grease pencil files to 2.80"; - /* callbacks */ - ot->exec = gpencil_convert_old_files_exec; - ot->poll = gpencil_convert_old_files_poll; + /* callbacks */ + ot->exec = gpencil_convert_old_files_exec; + ot->poll = gpencil_convert_old_files_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - ot->prop = RNA_def_boolean(ot->srna, "annotation", 0, "Annotation", "Convert to Annotations"); + /* props */ + ot->prop = RNA_def_boolean(ot->srna, "annotation", 0, "Annotation", "Convert to Annotations"); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 8ac4d602736..bfdfe64ed54 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -69,7 +69,6 @@ #include "ED_view3d.h" #include "ED_clip.h" - #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_state.h" @@ -85,153 +84,153 @@ #include "gpencil_intern.h" - /* ******************************************* */ - /* 'Globals' and Defines */ +/* ******************************************* */ +/* 'Globals' and Defines */ - /* values for tGPsdata->status */ +/* values for tGPsdata->status */ typedef enum eGPencil_PaintStatus { - GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ - GP_STATUS_PAINTING, /* a stroke is in progress */ - GP_STATUS_ERROR, /* something wasn't correctly set up */ - GP_STATUS_DONE, /* painting done */ + GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ + GP_STATUS_PAINTING, /* a stroke is in progress */ + GP_STATUS_ERROR, /* something wasn't correctly set up */ + GP_STATUS_DONE, /* painting done */ } eGPencil_PaintStatus; /* Return flags for adding points to stroke buffer */ typedef enum eGP_StrokeAdd_Result { - GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ - GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ - GP_STROKEADD_NORMAL, /* point was successfully added */ - GP_STROKEADD_FULL, /* cannot add any more points to buffer */ + GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ + GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ + GP_STROKEADD_NORMAL, /* point was successfully added */ + GP_STROKEADD_FULL, /* cannot add any more points to buffer */ } eGP_StrokeAdd_Result; /* Runtime flags */ typedef enum eGPencil_PaintFlags { - GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ - GP_PAINTFLAG_STROKEADDED = (1 << 1), - GP_PAINTFLAG_SELECTMASK = (1 << 3), - GP_PAINTFLAG_HARD_ERASER = (1 << 4), - GP_PAINTFLAG_STROKE_ERASER = (1 << 5), - GP_PAINTFLAG_REQ_VECTOR = (1 << 6), + GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ + GP_PAINTFLAG_STROKEADDED = (1 << 1), + GP_PAINTFLAG_SELECTMASK = (1 << 3), + GP_PAINTFLAG_HARD_ERASER = (1 << 4), + GP_PAINTFLAG_STROKE_ERASER = (1 << 5), + GP_PAINTFLAG_REQ_VECTOR = (1 << 6), } eGPencil_PaintFlags; /* Temporary 'Stroke' Operation data * "p" = op->customdata */ typedef struct tGPsdata { - bContext *C; - - /** main database pointer. */ - Main *bmain; - /** current scene from context. */ - Scene *scene; - struct Depsgraph *depsgraph; - - /** current object. */ - Object *ob; - /** window where painting originated. */ - wmWindow *win; - /** area where painting originated. */ - ScrArea *sa; - /** region where painting originated. */ - ARegion *ar; - /** needed for GP_STROKE_2DSPACE. */ - View2D *v2d; - /** for using the camera rect within the 3d view. */ - rctf *subrect; - rctf subrect_data; - - /** settings to pass to gp_points_to_xy(). */ - GP_SpaceConversion gsc; - - /** pointer to owner of gp-datablock. */ - PointerRNA ownerPtr; - /** gp-datablock layer comes from. */ - bGPdata *gpd; - /** layer we're working on. */ - bGPDlayer *gpl; - /** frame we're working on. */ - bGPDframe *gpf; - - /** projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ - char *align_flag; - - /** current status of painting. */ - eGPencil_PaintStatus status; - /** mode for painting. */ - eGPencil_PaintModes paintmode; - /** flags that can get set during runtime (eGPencil_PaintFlags) */ - eGPencil_PaintFlags flags; - - /** radius of influence for eraser. */ - short radius; - - /** current mouse-position. */ - float mval[2]; - /** previous recorded mouse-position. */ - float mvalo[2]; - /** initial recorded mouse-position */ - float mvali[2]; - - /** current stylus pressure. */ - float pressure; - /** previous stylus pressure. */ - float opressure; - - /* These need to be doubles, as (at least under unix) they are in seconds since epoch, - * float (and its 7 digits precision) is definitively not enough here! - * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. - */ - /** Used when converting to path. */ - double inittime; - /** Used when converting to path. */ - double curtime; - /** Used when converting to path. */ - double ocurtime; - - /** Inverted transformation matrix applying when converting coords from screen-space - * to region space. */ - float imat[4][4]; - float mat[4][4]; - - /** custom color - hack for enforcing a particular color for track/mask editing. */ - float custom_color[4]; - - /** radial cursor data for drawing eraser. */ - void *erasercursor; - - /* mat settings are only used for 3D view */ - /** current material */ - Material *material; - /** current drawing brush */ - Brush *brush; - /** default eraser brush */ - Brush *eraser; - - /** 1: line horizontal, 2: line vertical, other: not defined */ - short straight; - /** lock drawing to one axis */ - int lock_axis; - /** the stroke is no fill mode */ - bool disable_fill; - - RNG *rng; - - /** key used for invoking the operator */ - short keymodifier; - /** shift modifier flag */ - short shift; - /** size in pixels for uv calculation */ - float totpixlen; - - /* guide */ - /** guide spacing */ - float guide_spacing; - /** half guide spacing */ - float half_spacing; - /** origin */ - float origin[2]; - - ReportList *reports; + bContext *C; + + /** main database pointer. */ + Main *bmain; + /** current scene from context. */ + Scene *scene; + struct Depsgraph *depsgraph; + + /** current object. */ + Object *ob; + /** window where painting originated. */ + wmWindow *win; + /** area where painting originated. */ + ScrArea *sa; + /** region where painting originated. */ + ARegion *ar; + /** needed for GP_STROKE_2DSPACE. */ + View2D *v2d; + /** for using the camera rect within the 3d view. */ + rctf *subrect; + rctf subrect_data; + + /** settings to pass to gp_points_to_xy(). */ + GP_SpaceConversion gsc; + + /** pointer to owner of gp-datablock. */ + PointerRNA ownerPtr; + /** gp-datablock layer comes from. */ + bGPdata *gpd; + /** layer we're working on. */ + bGPDlayer *gpl; + /** frame we're working on. */ + bGPDframe *gpf; + + /** projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ + char *align_flag; + + /** current status of painting. */ + eGPencil_PaintStatus status; + /** mode for painting. */ + eGPencil_PaintModes paintmode; + /** flags that can get set during runtime (eGPencil_PaintFlags) */ + eGPencil_PaintFlags flags; + + /** radius of influence for eraser. */ + short radius; + + /** current mouse-position. */ + float mval[2]; + /** previous recorded mouse-position. */ + float mvalo[2]; + /** initial recorded mouse-position */ + float mvali[2]; + + /** current stylus pressure. */ + float pressure; + /** previous stylus pressure. */ + float opressure; + + /* These need to be doubles, as (at least under unix) they are in seconds since epoch, + * float (and its 7 digits precision) is definitively not enough here! + * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. + */ + /** Used when converting to path. */ + double inittime; + /** Used when converting to path. */ + double curtime; + /** Used when converting to path. */ + double ocurtime; + + /** Inverted transformation matrix applying when converting coords from screen-space + * to region space. */ + float imat[4][4]; + float mat[4][4]; + + /** custom color - hack for enforcing a particular color for track/mask editing. */ + float custom_color[4]; + + /** radial cursor data for drawing eraser. */ + void *erasercursor; + + /* mat settings are only used for 3D view */ + /** current material */ + Material *material; + /** current drawing brush */ + Brush *brush; + /** default eraser brush */ + Brush *eraser; + + /** 1: line horizontal, 2: line vertical, other: not defined */ + short straight; + /** lock drawing to one axis */ + int lock_axis; + /** the stroke is no fill mode */ + bool disable_fill; + + RNG *rng; + + /** key used for invoking the operator */ + short keymodifier; + /** shift modifier flag */ + short shift; + /** size in pixels for uv calculation */ + float totpixlen; + + /* guide */ + /** guide spacing */ + float guide_spacing; + /** half guide spacing */ + float half_spacing; + /** origin */ + float origin[2]; + + ReportList *reports; } tGPsdata; /* ------ */ @@ -241,30 +240,30 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTEN_PX (U.gp_manhattendist) /* minimum length of new segment before new point can be added */ -#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) +#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) static void gp_update_cache(bGPdata *gpd) { - if (gpd) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } + if (gpd) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } } static bool gp_stroke_added_check(tGPsdata *p) { - return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); + return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); } static void gp_stroke_added_enable(tGPsdata *p) { - BLI_assert(p->gpf->strokes.last != NULL); - p->flags |= GP_PAINTFLAG_STROKEADDED; + BLI_assert(p->gpf->strokes.last != NULL); + p->flags |= GP_PAINTFLAG_STROKEADDED; - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); } /* ------ */ @@ -278,49 +277,50 @@ static void gp_session_validatebuffer(tGPsdata *p); /* check if context is suitable for drawing */ static bool gpencil_draw_poll(bContext *C) { - if (ED_operator_regionactive(C)) { - ScrArea *sa = CTX_wm_area(C); - /* 3D Viewport */ - if (sa->spacetype != SPACE_VIEW3D) { - return false; - } - - /* check if Grease Pencil isn't already running */ - if (ED_gpencil_session_active() != 0) { - CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); - return false; - } - - /* only grease pencil object type */ - Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (ob->type != OB_GPENCIL)) { - return false; - } - - bGPdata *gpd = (bGPdata *)ob->data; - if (!GPENCIL_PAINT_MODE(gpd)) { - return false; - } - - ToolSettings *ts = CTX_data_scene(C)->toolsettings; - if (!ts->gp_paint->paint.brush) { - CTX_wm_operator_poll_msg_set(C, "Grease Pencil has no active paint tool"); - return false; - } - - return true; - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not set"); - return false; - } + if (ED_operator_regionactive(C)) { + ScrArea *sa = CTX_wm_area(C); + /* 3D Viewport */ + if (sa->spacetype != SPACE_VIEW3D) { + return false; + } + + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() != 0) { + CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + return false; + } + + /* only grease pencil object type */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + bGPdata *gpd = (bGPdata *)ob->data; + if (!GPENCIL_PAINT_MODE(gpd)) { + return false; + } + + ToolSettings *ts = CTX_data_scene(C)->toolsettings; + if (!ts->gp_paint->paint.brush) { + CTX_wm_operator_poll_msg_set(C, "Grease Pencil has no active paint tool"); + return false; + } + + return true; + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return false; + } } /* check if projecting strokes into 3d-geometry in the 3D-View */ static bool gpencil_project_check(tGPsdata *p) { - bGPdata *gpd = p->gpd; - return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); + bGPdata *gpd = p->gpd; + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && + (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); } /* ******************************************* */ @@ -331,220 +331,219 @@ static bool gpencil_project_check(tGPsdata *p) /* get the reference point for stroke-point conversions */ static void gp_get_3d_reference(tGPsdata *p, float vec[3]) { - Object *ob = NULL; - if (p->ownerPtr.type == &RNA_Object) { - ob = (Object *)p->ownerPtr.data; - } - ED_gp_get_drawing_reference(p->scene, ob, p->gpl, *p->align_flag, vec); + Object *ob = NULL; + if (p->ownerPtr.type == &RNA_Object) { + ob = (Object *)p->ownerPtr.data; + } + ED_gp_get_drawing_reference(p->scene, ob, p->gpl, *p->align_flag, vec); } /* Stroke Editing ---------------------------- */ /* check if the current mouse position is suitable for adding a new point */ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2]) { - Brush *brush = p->brush; - int dx = (int)fabsf(mval[0] - mvalo[0]); - int dy = (int)fabsf(mval[1] - mvalo[1]); - brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; - - /* if buffer is empty, just let this go through (i.e. so that dots will work) */ - if (p->gpd->runtime.sbuffer_size == 0) { - return true; - } - /* if lazy mouse, check minimum distance */ - else if (GPENCIL_LAZY_MODE(brush, p->shift)) { - brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; - if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) { - return true; - } - else { - /* If the mouse is moving within the radius of the last move, - * don't update the mouse position. This allows sharp turns. */ - copy_v2_v2(p->mval, p->mvalo); - return false; - } - } - /* check if mouse moved at least certain distance on both axes (best case) - * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand - */ - else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) - return true; - - /* check if the distance since the last point is significant enough - * - prevents points being added too densely - * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though - */ - else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) - return true; - - /* mouse 'didn't move' */ - else - return false; + Brush *brush = p->brush; + int dx = (int)fabsf(mval[0] - mvalo[0]); + int dy = (int)fabsf(mval[1] - mvalo[1]); + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; + + /* if buffer is empty, just let this go through (i.e. so that dots will work) */ + if (p->gpd->runtime.sbuffer_size == 0) { + return true; + } + /* if lazy mouse, check minimum distance */ + else if (GPENCIL_LAZY_MODE(brush, p->shift)) { + brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; + if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) { + return true; + } + else { + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + copy_v2_v2(p->mval, p->mvalo); + return false; + } + } + /* check if mouse moved at least certain distance on both axes (best case) + * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand + */ + else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) + return true; + + /* check if the distance since the last point is significant enough + * - prevents points being added too densely + * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though + */ + else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) + return true; + + /* mouse 'didn't move' */ + else + return false; } /* reproject stroke to plane locked to axis in 3d cursor location */ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) { - bGPdata *gpd = p->gpd; - Object *obact = (Object *)p->ownerPtr.data; - - float origin[3]; - RegionView3D *rv3d = p->ar->regiondata; - - /* verify the stroke mode is CURSOR 3d space mode */ - if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { - return; - } - if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { - return; - } - if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { - return; - } - - /* get drawing origin */ - gp_get_3d_reference(p, origin); - ED_gp_project_stroke_to_plane( - p->scene, obact, rv3d, gps, - origin, p->lock_axis - 1); + bGPdata *gpd = p->gpd; + Object *obact = (Object *)p->ownerPtr.data; + + float origin[3]; + RegionView3D *rv3d = p->ar->regiondata; + + /* verify the stroke mode is CURSOR 3d space mode */ + if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { + return; + } + + /* get drawing origin */ + gp_get_3d_reference(p, origin); + ED_gp_project_stroke_to_plane(p->scene, obact, rv3d, gps, origin, p->lock_axis - 1); } /* convert screen-coordinates to buffer-coordinates */ /* XXX this method needs a total overhaul! */ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], float *depth) { - bGPdata *gpd = p->gpd; - - /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ - if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { - - /* add small offset to keep stroke over the surface */ - if ((depth) && - (gpd->zdepth_offset > 0.0f) && - (*p->align_flag & GP_PROJECT_DEPTH_VIEW)) - { - *depth *= (1.0f - gpd->zdepth_offset); - } - - int mval_i[2]; - round_v2i_v2fl(mval_i, mval); - - if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval_i, out, 0, depth))) { - /* projecting onto 3D-Geometry - * - nothing more needs to be done here, since view_autodist_simple() has already done it - */ - - /* verify valid zdepth, if it's wrong, the default drawing mode is used - * and the function doesn't return now */ - if ((depth == NULL) || (*depth <= 1.0f)) { - return; - } - } - - float mval_prj[2]; - float rvec[3], dvec[3]; - float mval_f[2]; - float zfac; - - /* Current method just converts each point in screen-coordinates to - * 3D-coordinates using the 3D-cursor as reference. In general, this - * works OK, but it could of course be improved. */ - - gp_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); - - if (ED_view3d_project_float_global(p->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->ar, mval_f, dvec, zfac); - sub_v3_v3v3(out, rvec, dvec); - } - else { - zero_v3(out); - } - } + bGPdata *gpd = p->gpd; + + /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { + + /* add small offset to keep stroke over the surface */ + if ((depth) && (gpd->zdepth_offset > 0.0f) && (*p->align_flag & GP_PROJECT_DEPTH_VIEW)) { + *depth *= (1.0f - gpd->zdepth_offset); + } + + int mval_i[2]; + round_v2i_v2fl(mval_i, mval); + + if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval_i, out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ + + /* verify valid zdepth, if it's wrong, the default drawing mode is used + * and the function doesn't return now */ + if ((depth == NULL) || (*depth <= 1.0f)) { + return; + } + } + + float mval_prj[2]; + float rvec[3], dvec[3]; + float mval_f[2]; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. In general, this + * works OK, but it could of course be improved. */ + + gp_get_3d_reference(p, rvec); + zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(p->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval); + ED_view3d_win_to_delta(p->ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } + } } /* apply jitter to stroke */ -static void gp_brush_jitter( - bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2], - const float pressure, float r_mval[2], RNG *rng) +static void gp_brush_jitter(bGPdata *gpd, + Brush *brush, + tGPspoint *pt, + const float mval[2], + const float pressure, + float r_mval[2], + RNG *rng) { - float tmp_pressure = pressure; - if (brush->gpencil_settings->draw_jitter > 0.0f) { - float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); - tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - } - /* exponential value */ - const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); - const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure; - /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ - float mvec[2], svec[2]; - /* mouse movement in ints -> floats */ - if (gpd->runtime.sbuffer_size > 1) { - mvec[0] = (mval[0] - (pt - 1)->x); - mvec[1] = (mval[1] - (pt - 1)->y); - normalize_v2(mvec); - } - else { - mvec[0] = 0.0f; - mvec[1] = 0.0f; - } - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - /* scale the displacement by the random, and apply */ - if (BLI_rng_get_float(rng) > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - - r_mval[0] = mval[0] + svec[0]; - r_mval[1] = mval[1] + svec[1]; - + float tmp_pressure = pressure; + if (brush->gpencil_settings->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); + tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; + } + /* exponential value */ + const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * + (brush->gpencil_settings->draw_jitter + 2.0f); + const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure; + /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ + float mvec[2], svec[2]; + /* mouse movement in ints -> floats */ + if (gpd->runtime.sbuffer_size > 1) { + mvec[0] = (mval[0] - (pt - 1)->x); + mvec[1] = (mval[1] - (pt - 1)->y); + normalize_v2(mvec); + } + else { + mvec[0] = 0.0f; + mvec[1] = 0.0f; + } + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + /* scale the displacement by the random, and apply */ + if (BLI_rng_get_float(rng) > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + r_mval[0] = mval[0] + svec[0]; + r_mval[1] = mval[1] + svec[1]; } /* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2]) { - float mvec[2]; - float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */ - float fac; - float mpressure; - - /* default angle of brush in radians */ - float angle = brush->gpencil_settings->draw_angle; - /* angle vector of the brush with full thickness */ - float v0[2] = { cos(angle), sin(angle) }; - - /* Apply to first point (only if there are 2 points because before no data to do it ) */ - if (gpd->runtime.sbuffer_size == 1) { - mvec[0] = (mval[0] - (pt - 1)->x); - mvec[1] = (mval[1] - (pt - 1)->y); - normalize_v2(mvec); - - /* uses > 1.0f to get a smooth transition in first point */ - fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ - (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac); - - CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); - } - - /* apply from second point */ - if (gpd->runtime.sbuffer_size >= 1) { - mvec[0] = (mval[0] - (pt - 1)->x); - mvec[1] = (mval[1] - (pt - 1)->y); - normalize_v2(mvec); - - fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ - /* interpolate with previous point for smoother transitions */ - mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); - pt->pressure = mpressure; - - CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); - } - + float mvec[2]; + float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */ + float fac; + float mpressure; + + /* default angle of brush in radians */ + float angle = brush->gpencil_settings->draw_angle; + /* angle vector of the brush with full thickness */ + float v0[2] = {cos(angle), sin(angle)}; + + /* Apply to first point (only if there are 2 points because before no data to do it ) */ + if (gpd->runtime.sbuffer_size == 1) { + mvec[0] = (mval[0] - (pt - 1)->x); + mvec[1] = (mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + /* uses > 1.0f to get a smooth transition in first point */ + fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac); + + CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + + /* apply from second point */ + if (gpd->runtime.sbuffer_size >= 1) { + mvec[0] = (mval[0] - (pt - 1)->x); + mvec[1] = (mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + /* interpolate with previous point for smoother transitions */ + mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); + pt->pressure = mpressure; + + CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } } /* Apply smooth to buffer while drawing @@ -558,708 +557,712 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa */ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) { - bGPdata *gpd = p->gpd; - short num_points = gpd->runtime.sbuffer_size; - - /* Do nothing if not enough points to smooth out */ - if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { - return; - } - - tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - float steps = 4.0f; - if (idx < 4) { - steps--; - } - - tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; - tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; - tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; - tGPspoint *ptd = &points[idx - 1]; - - float sco[2] = { 0.0f }; - float a[2], b[2], c[2], d[2]; - const float average_fac = 1.0f / steps; - - /* Compute smoothed coordinate by taking the ones nearby */ - if (pta) { - copy_v2_v2(a, &pta->x); - madd_v2_v2fl(sco, a, average_fac); - } - if (ptb) { - copy_v2_v2(b, &ptb->x); - madd_v2_v2fl(sco, b, average_fac); - } - if (ptc) { - copy_v2_v2(c, &ptc->x); - madd_v2_v2fl(sco, c, average_fac); - } - if (ptd) { - copy_v2_v2(d, &ptd->x); - madd_v2_v2fl(sco, d, average_fac); - } - - /* Based on influence factor, blend between original and optimal smoothed coordinate */ - interp_v2_v2v2(c, c, sco, inf); - copy_v2_v2(&ptc->x, c); + bGPdata *gpd = p->gpd; + short num_points = gpd->runtime.sbuffer_size; + + /* Do nothing if not enough points to smooth out */ + if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + float steps = 4.0f; + if (idx < 4) { + steps--; + } + + tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; + tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; + tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; + tGPspoint *ptd = &points[idx - 1]; + + float sco[2] = {0.0f}; + float a[2], b[2], c[2], d[2]; + const float average_fac = 1.0f / steps; + + /* Compute smoothed coordinate by taking the ones nearby */ + if (pta) { + copy_v2_v2(a, &pta->x); + madd_v2_v2fl(sco, a, average_fac); + } + if (ptb) { + copy_v2_v2(b, &ptb->x); + madd_v2_v2fl(sco, b, average_fac); + } + if (ptc) { + copy_v2_v2(c, &ptc->x); + madd_v2_v2fl(sco, c, average_fac); + } + if (ptd) { + copy_v2_v2(d, &ptd->x); + madd_v2_v2fl(sco, d, average_fac); + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v2_v2v2(c, c, sco, inf); + copy_v2_v2(&ptc->x, c); } /* add current stroke-point to buffer (returns whether point was successfully added) */ -static short gp_stroke_addpoint( - tGPsdata *p, const float mval[2], float pressure, double curtime) +static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime) { - bGPdata *gpd = p->gpd; - Brush *brush = p->brush; - tGPspoint *pt; - ToolSettings *ts = p->scene->toolsettings; - Object *obact = (Object *)p->ownerPtr.data; - Depsgraph *depsgraph = p->depsgraph; - RegionView3D *rv3d = p->ar->regiondata; - View3D *v3d = p->sa->spacedata.first; - MaterialGPencilStyle *gp_style = p->material->gp_style; - const int def_nr = obact->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); - - /* check painting mode */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { - /* straight lines only - i.e. only store start and end point in buffer */ - if (gpd->runtime.sbuffer_size == 0) { - /* first point in buffer (start point) */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* increment buffer size */ - gpd->runtime.sbuffer_size++; - } - else { - /* just reset the endpoint to the latest value - * - assume that pointers for this are always valid... - */ - pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ - gpd->runtime.sbuffer_size = 2; - } - - /* can keep carrying on this way :) */ - return GP_STROKEADD_NORMAL; - } - else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ - /* check if still room in buffer */ - if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_OVERFLOW; - - /* get pointer to destination point */ - pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); - - /* store settings */ - /* pressure */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { - float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); - pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; - } - else { - pt->pressure = 1.0f; - } - - /* Apply jitter to position */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_jitter > 0.0f)) - { - float r_mval[2]; - const float jitpress = (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) ? pressure : 1.0f; - gp_brush_jitter(gpd, brush, pt, mval, jitpress, r_mval, p->rng); - copy_v2_v2(&pt->x, r_mval); - } - else { - copy_v2_v2(&pt->x, mval); - } - /* apply randomness to pressure */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_press > 0.0f)) - { - float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); - float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); - } - else { - pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); - } - CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); - } - - /* apply randomness to uv texture rotation */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->uv_random > 0.0f)) { - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random; - } - else { - pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; - } - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - else { - pt->uv_rot = 0.0f; - } - - /* apply angle of stroke to brush size */ - if (brush->gpencil_settings->draw_angle_factor != 0.0f) { - gp_brush_angle(gpd, brush, pt, mval); - } - - /* color strength */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { - float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, pressure); - float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - - pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; - } - else { - pt->strength = brush->gpencil_settings->draw_strength; - } - CLAMP(pt->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)) - { - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); - } - else { - pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); - } - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - } - - /* point time */ - pt->time = (float)(curtime - p->inittime); - - /* point uv (only 3d view) */ - if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_size > 0)) { - float pixsize = gp_style->texture_pixsize / 1000000.0f; - tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; - bGPDspoint spt, spt2; - - /* get origin to reproject point */ - float origin[3]; - gp_get_3d_reference(p, origin); - /* reproject current */ - ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt); - ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt); - - /* reproject previous */ - ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2); - ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2); - p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; - pt->uv_fac = p->totpixlen; - if ((gp_style) && (gp_style->sima)) { - pt->uv_fac /= gp_style->sima->gen_x; - } - } - else { - p->totpixlen = 0.0f; - pt->uv_fac = 0.0f; - } - - /* increment counters */ - gpd->runtime.sbuffer_size++; - - /* smooth while drawing previous points with a reduction factor for previous */ - if (brush->gpencil_settings->active_smooth > 0.0f) { - for (int s = 0; s < 3; s++) { - gp_smooth_buffer(p, brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_size - s); - } - } - - /* check if another operation can still occur */ - if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_FULL; - else - return GP_STROKEADD_NORMAL; - } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - - /* enable special flag for drawing engine */ - gpd->flag |= GP_DATA_STROKE_POLYGON; - - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* if there's stroke for this poly line session add (or replace last) point - * to stroke. This allows to draw lines more interactively (see new segment - * during mouse slide, e.g.) - */ - if (gp_stroke_added_check(p)) { - bGPDstroke *gps = p->gpf->strokes.last; - bGPDspoint *pts; - MDeformVert *dvert = NULL; - - /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ - if (gpd->runtime.sbuffer_size == 0) { - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - if (gps->dvert != NULL) { - gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); - } - gps->totpoints++; - } - - pts = &gps->points[gps->totpoints - 1]; - if (gps->dvert != NULL) { - dvert = &gps->dvert[gps->totpoints - 1]; - } - /* special case for poly lines: normally, - * depth is needed only when creating new stroke from buffer, - * but poly lines are converting to stroke instantly, - * so initialize depth buffer before converting coordinates - */ - if (gpencil_project_check(p)) { - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init( - p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); - } - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts); - /* copy pressure and time */ - pts->pressure = pt->pressure; - pts->strength = pt->strength; - pts->time = pt->time; - pts->uv_fac = pt->uv_fac; - pts->uv_rot = pt->uv_rot; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - } - } - - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); - } - - /* increment counters */ - if (gpd->runtime.sbuffer_size == 0) - gpd->runtime.sbuffer_size++; - - return GP_STROKEADD_NORMAL; - } - - /* return invalid state for now... */ - return GP_STROKEADD_INVALID; + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + tGPspoint *pt; + ToolSettings *ts = p->scene->toolsettings; + Object *obact = (Object *)p->ownerPtr.data; + Depsgraph *depsgraph = p->depsgraph; + RegionView3D *rv3d = p->ar->regiondata; + View3D *v3d = p->sa->spacedata.first; + MaterialGPencilStyle *gp_style = p->material->gp_style; + const int def_nr = obact->actdef - 1; + const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); + + /* check painting mode */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only - i.e. only store start and end point in buffer */ + if (gpd->runtime.sbuffer_size == 0) { + /* first point in buffer (start point) */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* increment buffer size */ + gpd->runtime.sbuffer_size++; + } + else { + /* just reset the endpoint to the latest value + * - assume that pointers for this are always valid... + */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ + gpd->runtime.sbuffer_size = 2; + } + + /* can keep carrying on this way :) */ + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ + /* check if still room in buffer */ + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_OVERFLOW; + + /* get pointer to destination point */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); + + /* store settings */ + /* pressure */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF( + brush->gpencil_settings->curve_sensitivity, 0, pressure); + pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; + } + else { + pt->pressure = 1.0f; + } + + /* Apply jitter to position */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_jitter > 0.0f)) { + float r_mval[2]; + const float jitpress = (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) ? + pressure : + 1.0f; + gp_brush_jitter(gpd, brush, pt, mval, jitpress, r_mval, p->rng); + copy_v2_v2(&pt->x, r_mval); + } + else { + copy_v2_v2(&pt->x, mval); + } + /* apply randomness to pressure */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_press > 0.0f)) { + float curvef = curvemapping_evaluateF( + brush->gpencil_settings->curve_sensitivity, 0, pressure); + float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; + if (BLI_rng_get_float(p->rng) > 0.5f) { + pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * + BLI_rng_get_float(p->rng); + } + else { + pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * + BLI_rng_get_float(p->rng); + } + CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* apply randomness to uv texture rotation */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->uv_random > 0.0f)) { + if (BLI_rng_get_float(p->rng) > 0.5f) { + pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random; + } + else { + pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } + else { + pt->uv_rot = 0.0f; + } + + /* apply angle of stroke to brush size */ + if (brush->gpencil_settings->draw_angle_factor != 0.0f) { + gp_brush_angle(gpd, brush, pt, mval); + } + + /* color strength */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, pressure); + float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; + + pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; + } + else { + pt->strength = brush->gpencil_settings->draw_strength; + } + CLAMP(pt->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)) { + if (BLI_rng_get_float(p->rng) > 0.5f) { + pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * + BLI_rng_get_float(p->rng); + } + else { + pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * + BLI_rng_get_float(p->rng); + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* point time */ + pt->time = (float)(curtime - p->inittime); + + /* point uv (only 3d view) */ + if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_size > 0)) { + float pixsize = gp_style->texture_pixsize / 1000000.0f; + tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + bGPDspoint spt, spt2; + + /* get origin to reproject point */ + float origin[3]; + gp_get_3d_reference(p, origin); + /* reproject current */ + ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt); + ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt); + + /* reproject previous */ + ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2); + ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2); + p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + pt->uv_fac = p->totpixlen; + if ((gp_style) && (gp_style->sima)) { + pt->uv_fac /= gp_style->sima->gen_x; + } + } + else { + p->totpixlen = 0.0f; + pt->uv_fac = 0.0f; + } + + /* increment counters */ + gpd->runtime.sbuffer_size++; + + /* smooth while drawing previous points with a reduction factor for previous */ + if (brush->gpencil_settings->active_smooth > 0.0f) { + for (int s = 0; s < 3; s++) { + gp_smooth_buffer(p, + brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f), + gpd->runtime.sbuffer_size - s); + } + } + + /* check if another operation can still occur */ + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_FULL; + else + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + + /* enable special flag for drawing engine */ + gpd->flag |= GP_DATA_STROKE_POLYGON; + + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + /* get pointer to destination point */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2(&pt->x, mval); + /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* if there's stroke for this poly line session add (or replace last) point + * to stroke. This allows to draw lines more interactively (see new segment + * during mouse slide, e.g.) + */ + if (gp_stroke_added_check(p)) { + bGPDstroke *gps = p->gpf->strokes.last; + bGPDspoint *pts; + MDeformVert *dvert = NULL; + + /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ + if (gpd->runtime.sbuffer_size == 0) { + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + if (gps->dvert != NULL) { + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); + } + gps->totpoints++; + } + + pts = &gps->points[gps->totpoints - 1]; + if (gps->dvert != NULL) { + dvert = &gps->dvert[gps->totpoints - 1]; + } + /* special case for poly lines: normally, + * depth is needed only when creating new stroke from buffer, + * but poly lines are converting to stroke instantly, + * so initialize depth buffer before converting coordinates + */ + if (gpencil_project_check(p)) { + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + /* if parented change position relative to parent object */ + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts); + /* copy pressure and time */ + pts->pressure = pt->pressure; + pts->strength = pt->strength; + pts->time = pt->time; + pts->uv_fac = pt->uv_fac; + pts->uv_rot = pt->uv_rot; + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + } + else { + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + } + } + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); + } + + /* increment counters */ + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + return GP_STROKEADD_NORMAL; + } + + /* return invalid state for now... */ + return GP_STROKEADD_INVALID; } /* make a new stroke from the buffer data */ static void gp_stroke_newfrombuffer(tGPsdata *p) { - bGPdata *gpd = p->gpd; - bGPDlayer *gpl = p->gpl; - bGPDstroke *gps; - bGPDspoint *pt; - tGPspoint *ptc; - MDeformVert *dvert = NULL; - Brush *brush = p->brush; - ToolSettings *ts = p->scene->toolsettings; - Depsgraph *depsgraph = p->depsgraph; - Object *obact = (Object *)p->ownerPtr.data; - RegionView3D *rv3d = p->ar->regiondata; - const int def_nr = obact->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); - const char *align_flag = &ts->gpencil_v3d_align; - const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); - const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && - (rv3d->persp == RV3D_CAMOB) && (!is_depth); - int i, totelem; - - /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ - int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; - - /* get total number of points to allocate space for - * - drawing straight-lines only requires the endpoints - */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) - totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; - else - totelem = gpd->runtime.sbuffer_size; - - /* exit with error if no valid points from this stroke */ - if (totelem == 0) { - if (G.debug & G_DEBUG) - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); - return; - } - - /* special case for poly line -- for already added stroke during session - * coordinates are getting added to stroke immediately to allow more - * interactive behavior - */ - if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* be sure to hide any lazy cursor */ - ED_gpencil_toggle_brush_cursor(p->C, true, NULL); - - if (gp_stroke_added_check(p)) { - return; - } - } - - /* allocate memory for a new stroke */ - gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - - /* copy appropriate settings for stroke */ - gps->totpoints = totelem; - gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); - gps->flag = gpd->runtime.sbuffer_sflag; - gps->inittime = p->inittime; - - /* enable recalculation flag by default (only used if hq fill) */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* allocate enough memory for a continuous array for storage points */ - const int subdivide = brush->gpencil_settings->draw_subdivide; - - gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); - - /* initialize triangle memory to dummy data */ - gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); - /* set pointer to first non-initialized point */ - pt = gps->points + (gps->totpoints - totelem); - if (gps->dvert != NULL) { - dvert = gps->dvert + (gps->totpoints - totelem); - } - - /* copy points from the buffer to the stroke */ - if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { - /* straight lines only -> only endpoints */ - { - /* first point */ - ptc = gpd->runtime.sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - pt++; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - dvert++; - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - dvert++; - } - } - } - - if (totelem == 2) { - /* last point if applicable */ - ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - } - } - } - - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - pt = gps->points; - for (i = 0; i < gps->totpoints; i++, pt++) { - /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); - } - - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); - } - } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* first point */ - ptc = gpd->runtime.sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); - } - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - } - } - - } - else { - float *depth_arr = NULL; - - /* get an array of depths, far depths are blended */ - if (gpencil_project_check(p)) { - int mval_i[2], mval_prev[2] = { 0 }; - int interp_depth = 0; - int found_depth = 0; - - depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); - - for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { - - round_v2i_v2fl(mval_i, &ptc->x); - - if ((ED_view3d_autodist_depth(p->ar, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg(p->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 == false) { - /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ - for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) - depth_arr[i] = 0.9999f; - } - 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 < gpd->runtime.sbuffer_size; 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 = gpd->runtime.sbuffer_size - 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 < gpd->runtime.sbuffer_size; i++) { - if ((i != first_valid) && (i != last_valid)) { - depth_arr[i] = FLT_MAX; - } - } - interp_depth = true; - } - - if (interp_depth) { - interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); - } - } - } - - pt = gps->points; - dvert = gps->dvert; - - /* convert all points (normal behavior) */ - for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); - - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - pt->uv_fac = ptc->uv_fac; - pt->uv_rot = ptc->uv_rot; - - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - dvert++; - } - } - - /* subdivide and smooth the stroke */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { - gp_subdivide_stroke(gps, subdivide); - } - /* apply randomness to stroke */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_sub > 0.0f)) - { - gp_randomize_stroke(gps, brush, p->rng); - } - - /* smooth stroke after subdiv - only if there's something to do - * for each iteration, the factor is reduced to get a better smoothing without changing too much - * the original stroke - */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && - (brush->gpencil_settings->draw_smoothfac > 0.0f)) - { - float reduce = 0.0f; - for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { - for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); - BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); - } - reduce += 0.25f; // reduce the factor - } - } - /* smooth thickness */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && - (brush->gpencil_settings->thick_smoothfac > 0.0f)) - { - for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) { - for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); - } - } - } - - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - /* change position relative to parent object */ - gp_apply_parent(depsgraph, obact, gpd, gpl, gps); - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); - } - - if (depth_arr) - MEM_freeN(depth_arr); - } - - /* Save material index */ - gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush); - - /* calculate UVs along the stroke */ - ED_gpencil_calc_stroke_uv(obact, gps); - - /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head - * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist - * when drawing the background - */ - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { - BLI_addhead(&p->gpf->strokes, gps); - } - else { - BLI_addtail(&p->gpf->strokes, gps); - } - /* add weights */ - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - for (i = 0; i < gps->totpoints; i++) { - MDeformVert *ve = &gps->dvert[i]; - MDeformWeight *dw = defvert_verify_index(ve, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - } - - /* post process stroke */ - if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && - p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) - { - BKE_gpencil_trim_stroke(gps); - } - - gp_stroke_added_enable(p); + bGPdata *gpd = p->gpd; + bGPDlayer *gpl = p->gpl; + bGPDstroke *gps; + bGPDspoint *pt; + tGPspoint *ptc; + MDeformVert *dvert = NULL; + Brush *brush = p->brush; + ToolSettings *ts = p->scene->toolsettings; + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; + RegionView3D *rv3d = p->ar->regiondata; + const int def_nr = obact->actdef - 1; + const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); + const char *align_flag = &ts->gpencil_v3d_align; + const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); + const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && (rv3d->persp == RV3D_CAMOB) && + (!is_depth); + int i, totelem; + + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; + + /* get total number of points to allocate space for + * - drawing straight-lines only requires the endpoints + */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; + else + totelem = gpd->runtime.sbuffer_size; + + /* exit with error if no valid points from this stroke */ + if (totelem == 0) { + if (G.debug & G_DEBUG) + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", + gpd->runtime.sbuffer_size); + return; + } + + /* special case for poly line -- for already added stroke during session + * coordinates are getting added to stroke immediately to allow more + * interactive behavior + */ + if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* be sure to hide any lazy cursor */ + ED_gpencil_toggle_brush_cursor(p->C, true, NULL); + + if (gp_stroke_added_check(p)) { + return; + } + } + + /* allocate memory for a new stroke */ + gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + + /* copy appropriate settings for stroke */ + gps->totpoints = totelem; + gps->thickness = brush->size; + gps->gradient_f = brush->gpencil_settings->gradient_f; + copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->flag = gpd->runtime.sbuffer_sflag; + gps->inittime = p->inittime; + + /* enable recalculation flag by default (only used if hq fill) */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* allocate enough memory for a continuous array for storage points */ + const int subdivide = brush->gpencil_settings->draw_subdivide; + + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); + /* set pointer to first non-initialized point */ + pt = gps->points + (gps->totpoints - totelem); + if (gps->dvert != NULL) { + dvert = gps->dvert + (gps->totpoints - totelem); + } + + /* copy points from the buffer to the stroke */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only -> only endpoints */ + { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + pt++; + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + dvert++; + } + else { + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + dvert++; + } + } + } + + if (totelem == 2) { + /* last point if applicable */ + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + } + else { + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + } + } + } + + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + pt = gps->points; + for (i = 0; i < gps->totpoints; i++, pt++) { + /* if parented change position relative to parent object */ + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + } + + /* if camera view, reproject flat to view to avoid perspective effect */ + if (is_camera) { + ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); + } + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + /* if parented change position relative to parent object */ + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + /* if camera view, reproject flat to view to avoid perspective effect */ + if (is_camera) { + ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); + } + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + } + else { + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + } + } + } + else { + float *depth_arr = NULL; + + /* get an array of depths, far depths are blended */ + if (gpencil_project_check(p)) { + int mval_i[2], mval_prev[2] = {0}; + int interp_depth = 0; + int found_depth = 0; + + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); + + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { + + round_v2i_v2fl(mval_i, &ptc->x); + + if ((ED_view3d_autodist_depth(p->ar, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg( + p->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 == false) { + /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) + depth_arr[i] = 0.9999f; + } + 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 < gpd->runtime.sbuffer_size; 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 = gpd->runtime.sbuffer_size - 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 < gpd->runtime.sbuffer_size; i++) { + if ((i != first_valid) && (i != last_valid)) { + depth_arr[i] = FLT_MAX; + } + } + interp_depth = true; + } + + if (interp_depth) { + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); + } + } + } + + pt = gps->points; + dvert = gps->dvert; + + /* convert all points (normal behavior) */ + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; + i++, ptc++, pt++) { + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + pt->uv_fac = ptc->uv_fac; + pt->uv_rot = ptc->uv_rot; + + if (dvert != NULL) { + dvert->totweight = 0; + dvert->dw = NULL; + dvert++; + } + } + + /* subdivide and smooth the stroke */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { + gp_subdivide_stroke(gps, subdivide); + } + /* apply randomness to stroke */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_sub > 0.0f)) { + gp_randomize_stroke(gps, brush, p->rng); + } + + /* smooth stroke after subdiv - only if there's something to do + * for each iteration, the factor is reduced to get a better smoothing without changing too much + * the original stroke + */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + (brush->gpencil_settings->draw_smoothfac > 0.0f)) { + float reduce = 0.0f; + for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { + for (i = 0; i < gps->totpoints - 1; i++) { + BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); + } + reduce += 0.25f; // reduce the factor + } + } + /* smooth thickness */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + (brush->gpencil_settings->thick_smoothfac > 0.0f)) { + for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) { + for (i = 0; i < gps->totpoints - 1; i++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); + } + } + } + + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + /* change position relative to parent object */ + gp_apply_parent(depsgraph, obact, gpd, gpl, gps); + /* if camera view, reproject flat to view to avoid perspective effect */ + if (is_camera) { + ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); + } + + if (depth_arr) + MEM_freeN(depth_arr); + } + + /* Save material index */ + gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush); + + /* calculate UVs along the stroke */ + ED_gpencil_calc_stroke_uv(obact, gps); + + /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head + * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist + * when drawing the background + */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && + (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { + BLI_addhead(&p->gpf->strokes, gps); + } + else { + BLI_addtail(&p->gpf->strokes, gps); + } + /* add weights */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + for (i = 0; i < gps->totpoints; i++) { + MDeformVert *ve = &gps->dvert[i]; + MDeformWeight *dw = defvert_verify_index(ve, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + } + } + + /* post process stroke */ + if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) { + BKE_gpencil_trim_stroke(gps); + } + + gp_stroke_added_enable(p); } /* --- 'Eraser' for 'Paint' Tool ------ */ @@ -1267,97 +1270,102 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* which which point is infront (result should only be used for comparison) */ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) { - if (rv3d->is_persp) { - return ED_view3d_calc_zfac(rv3d, co, NULL); - } - else { - return -dot_v3v3(rv3d->viewinv[2], co); - } + if (rv3d->is_persp) { + return ED_view3d_calc_zfac(rv3d, co, NULL); + } + else { + return -dot_v3v3(rv3d->viewinv[2], co); + } } /* only erase stroke points that are visible */ -static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) +static bool gp_stroke_eraser_is_occluded(tGPsdata *p, + const bGPDspoint *pt, + const int x, + const int y) { - Object *obact = (Object *)p->ownerPtr.data; - Brush *brush = p->brush; - Brush *eraser = p->eraser; - BrushGpencilSettings *gp_settings = NULL; - - if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { - gp_settings = brush->gpencil_settings; - } - else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) { - gp_settings = eraser->gpencil_settings; - } - - if ((gp_settings != NULL) && (p->sa->spacetype == SPACE_VIEW3D) && - (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) - { - RegionView3D *rv3d = p->ar->regiondata; - bGPDlayer *gpl = p->gpl; - - const int mval_i[2] = {x, y}; - float mval_3d[3]; - float fpt[3]; - - float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat); - - if (ED_view3d_autodist_simple(p->ar, mval_i, mval_3d, 0, NULL)) { - const float depth_mval = view3d_point_depth(rv3d, mval_3d); - - mul_v3_m4v3(fpt, diff_mat, &pt->x); - const float depth_pt = view3d_point_depth(rv3d, fpt); - - if (depth_pt > depth_mval) { - return true; - } - } - } - return false; + Object *obact = (Object *)p->ownerPtr.data; + Brush *brush = p->brush; + Brush *eraser = p->eraser; + BrushGpencilSettings *gp_settings = NULL; + + if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { + gp_settings = brush->gpencil_settings; + } + else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) { + gp_settings = eraser->gpencil_settings; + } + + if ((gp_settings != NULL) && (p->sa->spacetype == SPACE_VIEW3D) && + (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + RegionView3D *rv3d = p->ar->regiondata; + bGPDlayer *gpl = p->gpl; + + const int mval_i[2] = {x, y}; + float mval_3d[3]; + float fpt[3]; + + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ + ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat); + + if (ED_view3d_autodist_simple(p->ar, mval_i, mval_3d, 0, NULL)) { + const float depth_mval = view3d_point_depth(rv3d, mval_3d); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + const float depth_pt = view3d_point_depth(rv3d, fpt); + + if (depth_pt > depth_mval) { + return true; + } + } + } + return false; } /* apply a falloff effect to brush strength, based on distance */ -static float gp_stroke_eraser_calc_influence(tGPsdata *p, const float mval[2], const int radius, const int co[2]) +static float gp_stroke_eraser_calc_influence(tGPsdata *p, + const float mval[2], + const int radius, + const int co[2]) { - Brush *brush = p->brush; - /* Linear Falloff... */ - int mval_i[2]; - round_v2i_v2fl(mval_i, mval); - float distance = (float)len_v2v2_int(mval_i, co); - float fac; - - CLAMP(distance, 0.0f, (float)radius); - fac = 1.0f - (distance / (float)radius); - - /* apply strength factor */ - fac *= brush->gpencil_settings->draw_strength; - - /* Control this further using pen pressure */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { - fac *= p->pressure; - } - /* Return influence factor computed here */ - return fac; + Brush *brush = p->brush; + /* Linear Falloff... */ + int mval_i[2]; + round_v2i_v2fl(mval_i, mval); + float distance = (float)len_v2v2_int(mval_i, co); + float fac; + + CLAMP(distance, 0.0f, (float)radius); + fac = 1.0f - (distance / (float)radius); + + /* apply strength factor */ + fac *= brush->gpencil_settings->draw_strength; + + /* Control this further using pen pressure */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + fac *= p->pressure; + } + /* Return influence factor computed here */ + return fac; } /* helper to free a stroke */ static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) { - if (gps->points) { - MEM_freeN(gps->points); - } - - if (gps->dvert) { - BKE_gpencil_free_stroke_weights(gps); - MEM_freeN(gps->dvert); - } - - if (gps->triangles) - MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); - gp_update_cache(gpd); + if (gps->points) { + MEM_freeN(gps->points); + } + + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + if (gps->triangles) + MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + gp_update_cache(gpd); } /** @@ -1367,322 +1375,324 @@ static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) */ static void gp_stroke_soft_refine(bGPDstroke *gps) { - bGPDspoint *pt = NULL; - bGPDspoint *pt2 = NULL; - int i; - - /* check if enough points*/ - if (gps->totpoints < 3) { - return; - } - - /* loop all points to untag any point that next is not tagged */ - pt = gps->points; - for (i = 1; i < gps->totpoints - 1; i++, pt++) { - if (pt->flag & GP_SPOINT_TAG) { - pt2 = &gps->points[i + 1]; - if ((pt2->flag & GP_SPOINT_TAG) == 0) { - pt->flag &= ~GP_SPOINT_TAG; - } - } - } - - /* loop reverse all points to untag any point that previous is not tagged */ - pt = &gps->points[gps->totpoints - 1]; - for (i = gps->totpoints - 1; i > 0; i--, pt--) { - if (pt->flag & GP_SPOINT_TAG) { - pt2 = &gps->points[i - 1]; - if ((pt2->flag & GP_SPOINT_TAG) == 0) { - pt->flag &= ~GP_SPOINT_TAG; - } - } - } + bGPDspoint *pt = NULL; + bGPDspoint *pt2 = NULL; + int i; + + /* check if enough points*/ + if (gps->totpoints < 3) { + return; + } + + /* loop all points to untag any point that next is not tagged */ + pt = gps->points; + for (i = 1; i < gps->totpoints - 1; i++, pt++) { + if (pt->flag & GP_SPOINT_TAG) { + pt2 = &gps->points[i + 1]; + if ((pt2->flag & GP_SPOINT_TAG) == 0) { + pt->flag &= ~GP_SPOINT_TAG; + } + } + } + + /* loop reverse all points to untag any point that previous is not tagged */ + pt = &gps->points[gps->totpoints - 1]; + for (i = gps->totpoints - 1; i > 0; i--, pt--) { + if (pt->flag & GP_SPOINT_TAG) { + pt2 = &gps->points[i - 1]; + if ((pt2->flag & GP_SPOINT_TAG) == 0) { + pt->flag &= ~GP_SPOINT_TAG; + } + } + } } /* eraser tool - evaluation per stroke */ /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ static void gp_stroke_eraser_dostroke(tGPsdata *p, - bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, - const float mval[2], const float mvalo[2], - const int radius, const rcti *rect) + bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + const float mval[2], + const float mvalo[2], + const int radius, + const rcti *rect) { - Depsgraph *depsgraph = p->depsgraph; - Object *obact = (Object *)p->ownerPtr.data; - Brush *eraser = p->eraser; - bGPDspoint *pt0, *pt1, *pt2; - int pc0[2] = {0}; - int pc1[2] = {0}; - int pc2[2] = {0}; - int i; - float diff_mat[4][4]; - int mval_i[2]; - round_v2i_v2fl(mval_i, mval); - - /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); - - if (gps->totpoints == 0) { - /* just free stroke */ - gp_free_stroke(p->gpd, gpf, gps); - } - else if (gps->totpoints == 1) { - /* only process if it hasn't been masked out... */ - if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - if (len_v2v2_int(mval_i, pc1) <= radius) { - /* free stroke */ - gp_free_stroke(p->gpd, gpf, gps); - } - } - } - } - else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) || (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_STROKE)) { - for (i = 0; (i + 1) < gps->totpoints; i++) { - - /* only process if it hasn't been masked out... */ - if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) - continue; - - /* get points to work with */ - pt1 = gps->points + i; - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); - - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - if (len_v2v2_int(mval_i, pc1) <= radius) { - /* free stroke */ - gp_free_stroke(p->gpd, gpf, gps); - return; - } - } - } - } - else { - /* Pressure threshold at which stroke should be culled */ - const float cull_thresh = 0.005f; - - /* Amount to decrease the pressure of each point with each stroke */ - const float strength = 0.1f; - - /* Perform culling? */ - bool do_cull = false; - - /* Clear Tags - * - * Note: It's better this way, as we are sure that - * we don't miss anything, though things will be - * slightly slower as a result - */ - for (i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - pt->flag &= ~GP_SPOINT_TAG; - } - - /* First Pass: Loop over the points in the stroke - * 1) Thin out parts of the stroke under the brush - * 2) Tag "too thin" parts for removal (in second pass) - */ - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* get points to work with */ - pt0 = i > 0 ? gps->points + i - 1 : NULL; - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* only process if it hasn't been masked out... */ - if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) - continue; - - bGPDspoint npt; - if (pt0) { - gp_point_to_parent_space(pt0, diff_mat, &npt); - gp_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]); - } - else { - /* avoid null values */ - copy_v2_v2_int(pc0, pc1); - } - - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); - - gp_point_to_parent_space(pt2, diff_mat, &npt); - gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); - - /* Check that point segment of the boundbox of the eraser stroke */ - if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) - { - /* Check if point segment of stroke had anything to do with - * eraser region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle(mval, mvalo, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { - if ((gp_stroke_eraser_is_occluded(p, pt0, pc0[0], pc0[1]) == false) || - (gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || - (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) - { - /* Point is affected: */ - /* Adjust thickness - * - Influence of eraser falls off with distance from the middle of the eraser - * - Second point gets less influence, as it might get hit again in the next segment - */ - - /* Adjust strength if the eraser is soft */ - if (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_SOFT) { - float f_strength = eraser->gpencil_settings->era_strength_f / 100.0f; - float f_thickness = eraser->gpencil_settings->era_thickness_f / 100.0f; - float influence = 0.0f; - - if (pt0) { - influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc0); - pt0->strength -= influence * strength * f_strength * 0.5f; - CLAMP_MIN(pt0->strength, 0.0f); - pt0->pressure -= influence * strength * f_thickness * 0.5f; - } - - influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc1); - pt1->strength -= influence * strength * f_strength; - CLAMP_MIN(pt1->strength, 0.0f); - pt1->pressure -= influence * strength * f_thickness; - - influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc2); - pt2->strength -= influence * strength * f_strength * 0.5f; - CLAMP_MIN(pt2->strength, 0.0f); - pt2->pressure -= influence * strength * f_thickness * 0.5f; - - /* if invisible, delete point */ - if ((pt0) && - ((pt0->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || - (pt0->pressure < cull_thresh))) - { - pt0->flag |= GP_SPOINT_TAG; - do_cull = true; - } - if ((pt1->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || (pt1->pressure < cull_thresh)) { - pt1->flag |= GP_SPOINT_TAG; - do_cull = true; - } - if ((pt2->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || (pt2->pressure < cull_thresh)) { - pt2->flag |= GP_SPOINT_TAG; - do_cull = true; - } - } - else { - pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength; - pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength * 0.5f; - } - - /* 2) Tag any point with overly low influence for removal in the next pass */ - if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || - (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) - { - pt1->flag |= GP_SPOINT_TAG; - do_cull = true; - } - if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || - (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) - { - pt2->flag |= GP_SPOINT_TAG; - do_cull = true; - } - } - } - } - } - - /* Second Pass: Remove any points that are tagged */ - if (do_cull) { - /* if soft eraser, must analyze points to be sure the stroke ends - * don't get rounded */ - if (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_SOFT) { - gp_stroke_soft_refine(gps); - } - - gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0); - } - gp_update_cache(p->gpd); - } + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; + Brush *eraser = p->eraser; + bGPDspoint *pt0, *pt1, *pt2; + int pc0[2] = {0}; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + float diff_mat[4][4]; + int mval_i[2]; + round_v2i_v2fl(mval_i, mval); + + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); + + if (gps->totpoints == 0) { + /* just free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + else if (gps->totpoints == 1) { + /* only process if it hasn't been masked out... */ + if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + } + } + } + else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_STROKE)) { + for (i = 0; (i + 1) < gps->totpoints; i++) { + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + /* get points to work with */ + pt1 = gps->points + i; + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + return; + } + } + } + } + else { + /* Pressure threshold at which stroke should be culled */ + const float cull_thresh = 0.005f; + + /* Amount to decrease the pressure of each point with each stroke */ + const float strength = 0.1f; + + /* Perform culling? */ + bool do_cull = false; + + /* Clear Tags + * + * Note: It's better this way, as we are sure that + * we don't miss anything, though things will be + * slightly slower as a result + */ + for (i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->flag &= ~GP_SPOINT_TAG; + } + + /* First Pass: Loop over the points in the stroke + * 1) Thin out parts of the stroke under the brush + * 2) Tag "too thin" parts for removal (in second pass) + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* get points to work with */ + pt0 = i > 0 ? gps->points + i - 1 : NULL; + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + bGPDspoint npt; + if (pt0) { + gp_point_to_parent_space(pt0, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]); + } + else { + /* avoid null values */ + copy_v2_v2_int(pc0, pc1); + } + + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the eraser stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * eraser region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle(mval, mvalo, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { + if ((gp_stroke_eraser_is_occluded(p, pt0, pc0[0], pc0[1]) == false) || + (gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || + (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) { + /* Point is affected: */ + /* Adjust thickness + * - Influence of eraser falls off with distance from the middle of the eraser + * - Second point gets less influence, as it might get hit again in the next segment + */ + + /* Adjust strength if the eraser is soft */ + if (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_SOFT) { + float f_strength = eraser->gpencil_settings->era_strength_f / 100.0f; + float f_thickness = eraser->gpencil_settings->era_thickness_f / 100.0f; + float influence = 0.0f; + + if (pt0) { + influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc0); + pt0->strength -= influence * strength * f_strength * 0.5f; + CLAMP_MIN(pt0->strength, 0.0f); + pt0->pressure -= influence * strength * f_thickness * 0.5f; + } + + influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc1); + pt1->strength -= influence * strength * f_strength; + CLAMP_MIN(pt1->strength, 0.0f); + pt1->pressure -= influence * strength * f_thickness; + + influence = gp_stroke_eraser_calc_influence(p, mval, radius, pc2); + pt2->strength -= influence * strength * f_strength * 0.5f; + CLAMP_MIN(pt2->strength, 0.0f); + pt2->pressure -= influence * strength * f_thickness * 0.5f; + + /* if invisible, delete point */ + if ((pt0) && ((pt0->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || + (pt0->pressure < cull_thresh))) { + pt0->flag |= GP_SPOINT_TAG; + do_cull = true; + } + if ((pt1->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || + (pt1->pressure < cull_thresh)) { + pt1->flag |= GP_SPOINT_TAG; + do_cull = true; + } + if ((pt2->strength <= GPENCIL_ALPHA_OPACITY_THRESH) || + (pt2->pressure < cull_thresh)) { + pt2->flag |= GP_SPOINT_TAG; + do_cull = true; + } + } + else { + pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength; + pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength * + 0.5f; + } + + /* 2) Tag any point with overly low influence for removal in the next pass */ + if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) { + pt1->flag |= GP_SPOINT_TAG; + do_cull = true; + } + if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) { + pt2->flag |= GP_SPOINT_TAG; + do_cull = true; + } + } + } + } + } + + /* Second Pass: Remove any points that are tagged */ + if (do_cull) { + /* if soft eraser, must analyze points to be sure the stroke ends + * don't get rounded */ + if (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_SOFT) { + gp_stroke_soft_refine(gps); + } + + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0); + } + gp_update_cache(p->gpd); + } } /* erase strokes which fall under the eraser strokes */ static void gp_stroke_doeraser(tGPsdata *p) { - bGPDlayer *gpl; - bGPDstroke *gps, *gpn; - rcti rect; - Brush *brush = p->brush; - Brush *eraser = p->eraser; - bool use_pressure = false; - float press = 1.0f; - BrushGpencilSettings *gp_settings = NULL; - - /* detect if use pressure in eraser */ - if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { - use_pressure = (bool)(brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE); - gp_settings = brush->gpencil_settings; - } - else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) { - use_pressure = (bool)(eraser->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE); - gp_settings = eraser->gpencil_settings; - } - if (use_pressure) { - press = p->pressure; - CLAMP(press, 0.01f, 1.0f); - } - /* rect is rectangle of eraser */ - const int calc_radius = (int)p->radius * press; - rect.xmin = p->mval[0] - calc_radius; - rect.ymin = p->mval[1] - calc_radius; - rect.xmax = p->mval[0] + calc_radius; - rect.ymax = p->mval[1] + calc_radius; - - if (p->sa->spacetype == SPACE_VIEW3D) { - if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { - View3D *v3d = p->sa->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); - } - } - - /* loop over all layers too, since while it's easy to restrict editing to - * only a subset of layers, it is harder to perform the same erase operation - * on multiple layers... - */ - for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *gpf = gpl->actframe; - - /* only affect layer if it's editable (and visible) */ - if (gpencil_layer_is_editable(gpl) == false) { - continue; - } - else if (gpf == NULL) { - continue; - } - - /* loop over strokes, checking segments for intersections */ - for (gps = gpf->strokes.first; gps; gps = gpn) { - gpn = gps->next; - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { - continue; - } - /* Not all strokes in the datablock may be valid in the current editor/context - * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) - */ - if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { - gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, calc_radius, &rect); - } - } - } + bGPDlayer *gpl; + bGPDstroke *gps, *gpn; + rcti rect; + Brush *brush = p->brush; + Brush *eraser = p->eraser; + bool use_pressure = false; + float press = 1.0f; + BrushGpencilSettings *gp_settings = NULL; + + /* detect if use pressure in eraser */ + if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { + use_pressure = (bool)(brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE); + gp_settings = brush->gpencil_settings; + } + else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) { + use_pressure = (bool)(eraser->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE); + gp_settings = eraser->gpencil_settings; + } + if (use_pressure) { + press = p->pressure; + CLAMP(press, 0.01f, 1.0f); + } + /* rect is rectangle of eraser */ + const int calc_radius = (int)p->radius * press; + rect.xmin = p->mval[0] - calc_radius; + rect.ymin = p->mval[1] - calc_radius; + rect.xmax = p->mval[0] + calc_radius; + rect.ymax = p->mval[1] + calc_radius; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + View3D *v3d = p->sa->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); + } + } + + /* loop over all layers too, since while it's easy to restrict editing to + * only a subset of layers, it is harder to perform the same erase operation + * on multiple layers... + */ + for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf = gpl->actframe; + + /* only affect layer if it's editable (and visible) */ + if (gpencil_layer_is_editable(gpl) == false) { + continue; + } + else if (gpf == NULL) { + continue; + } + + /* loop over strokes, checking segments for intersections */ + for (gps = gpf->strokes.first; gps; gps = gpn) { + gpn = gps->next; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { + continue; + } + /* Not all strokes in the datablock may be valid in the current editor/context + * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) + */ + if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { + gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, calc_radius, &rect); + } + } + } } /* ******************************************* */ @@ -1691,563 +1701,558 @@ static void gp_stroke_doeraser(tGPsdata *p) /* clear the session buffers (call this before AND after a paint operation) */ static void gp_session_validatebuffer(tGPsdata *p) { - bGPdata *gpd = p->gpd; - Brush *brush = p->brush; - - /* clear memory of buffer (or allocate it if starting a new session) */ - if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - reset sbuffer\n"); */ - memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); - } - else { - /* printf("\t\tGP - allocate sbuffer\n"); */ - 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; - - /* reset region */ - gpd->runtime.ar = NULL; - - /* reset inittime */ - p->inittime = 0.0; - - /* reset lazy */ - if (brush) { - brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; - } + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - reset sbuffer\n"); */ + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + } + else { + /* printf("\t\tGP - allocate sbuffer\n"); */ + 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; + + /* reset region */ + gpd->runtime.ar = NULL; + + /* reset inittime */ + p->inittime = 0.0; + + /* reset lazy */ + if (brush) { + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; + } } /* helper to get default eraser and create one if no eraser brush */ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) { - Brush *brush_dft = NULL; - Paint *paint = &ts->gp_paint->paint; - Brush *brush_old = paint->brush; - for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { - if ((brush->ob_mode == OB_MODE_PAINT_GPENCIL) && - (brush->gpencil_tool == GPAINT_TOOL_ERASE)) - { - /* save first eraser to use later if no default */ - if (brush_dft == NULL) { - brush_dft = brush; - } - /* found default */ - if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { - return brush; - } - } - } - /* if no default, but exist eraser brush, return this and set as default */ - if (brush_dft) { - brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - return brush_dft; - } - /* create a new soft eraser brush */ - else { - brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); - brush_dft->size = 30.0f; - brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); - brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; - brush_dft->gpencil_tool = GPAINT_TOOL_ERASE; - brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - - /* reset current brush */ - BKE_paint_brush_set(paint, brush_old); - - return brush_dft; - } + Brush *brush_dft = NULL; + Paint *paint = &ts->gp_paint->paint; + Brush *brush_old = paint->brush; + for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { + if ((brush->ob_mode == OB_MODE_PAINT_GPENCIL) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { + /* save first eraser to use later if no default */ + if (brush_dft == NULL) { + brush_dft = brush; + } + /* found default */ + if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { + return brush; + } + } + } + /* if no default, but exist eraser brush, return this and set as default */ + if (brush_dft) { + brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; + return brush_dft; + } + /* create a new soft eraser brush */ + else { + brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); + brush_dft->size = 30.0f; + brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush_dft->gpencil_tool = GPAINT_TOOL_ERASE; + brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + + /* reset current brush */ + BKE_paint_brush_set(paint, brush_old); + + return brush_dft; + } } /* helper to set default eraser and disable others */ static void gp_set_default_eraser(Main *bmain, Brush *brush_dft) { - if (brush_dft == NULL) { - return; - } - - for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { - if ((brush->gpencil_settings) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - if (brush == brush_dft) { - brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - } - else if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { - brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER; - } - } - } + if (brush_dft == NULL) { + return; + } + + for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { + if ((brush->gpencil_settings) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { + if (brush == brush_dft) { + brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; + } + else if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { + brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER; + } + } + } } /* initialize a drawing brush */ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - - Paint *paint = &ts->gp_paint->paint; - bool changed = false; - /* if not exist, create a new one */ - if (paint->brush == NULL) { - /* create new brushes */ - BKE_brush_gpencil_presets(C); - changed = true; - } - /* be sure curves are initializated */ - curvemapping_initialize(paint->brush->gpencil_settings->curve_sensitivity); - curvemapping_initialize(paint->brush->gpencil_settings->curve_strength); - curvemapping_initialize(paint->brush->gpencil_settings->curve_jitter); - - /* assign to temp tGPsdata */ - p->brush = paint->brush; - if (paint->brush->gpencil_tool != GPAINT_TOOL_ERASE) { - p->eraser = gp_get_default_eraser(p->bmain, ts); - } - else { - p->eraser = paint->brush; - } - /* set new eraser as default */ - gp_set_default_eraser(p->bmain, p->eraser); - - /* use radius of eraser */ - p->radius = (short)p->eraser->size; - - /* GPXX: Need this update to synchronize brush with draw manager. - * Maybe this update can be removed when the new tool system - * will be in place, but while, we need this to keep drawing working. - */ - if (changed) { - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - } + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + Paint *paint = &ts->gp_paint->paint; + bool changed = false; + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + changed = true; + } + /* be sure curves are initializated */ + curvemapping_initialize(paint->brush->gpencil_settings->curve_sensitivity); + curvemapping_initialize(paint->brush->gpencil_settings->curve_strength); + curvemapping_initialize(paint->brush->gpencil_settings->curve_jitter); + + /* assign to temp tGPsdata */ + p->brush = paint->brush; + if (paint->brush->gpencil_tool != GPAINT_TOOL_ERASE) { + p->eraser = gp_get_default_eraser(p->bmain, ts); + } + else { + p->eraser = paint->brush; + } + /* set new eraser as default */ + gp_set_default_eraser(p->bmain, p->eraser); + + /* use radius of eraser */ + p->radius = (short)p->eraser->size; + + /* GPXX: Need this update to synchronize brush with draw manager. + * Maybe this update can be removed when the new tool system + * will be in place, but while, we need this to keep drawing working. + */ + if (changed) { + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + } } - /* initialize a paint brush and a default color if not exist */ static void gp_init_colors(tGPsdata *p) { - bGPdata *gpd = p->gpd; - Brush *brush = p->brush; - - MaterialGPencilStyle *gp_style = NULL; - - /* use brush material */ - p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); - - /* assign color information to temp tGPsdata */ - gp_style = p->material->gp_style; - if (gp_style) { - - /* set colors */ - if (gp_style->flag & GP_STYLE_STROKE_SHOW) { - copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); - } - else { - /* if no stroke, use fill */ - copy_v4_v4(gpd->runtime.scolor, gp_style->fill_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; - } + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + + MaterialGPencilStyle *gp_style = NULL; + + /* use brush material */ + p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); + + /* assign color information to temp tGPsdata */ + gp_style = p->material->gp_style; + if (gp_style) { + + /* set colors */ + if (gp_style->flag & GP_STYLE_STROKE_SHOW) { + copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); + } + else { + /* if no stroke, use fill */ + copy_v4_v4(gpd->runtime.scolor, gp_style->fill_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; + } } /* (re)init new painting data */ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = NULL; - ScrArea *curarea = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Object *obact = CTX_data_active_object(C); - - /* make sure the active view (at the starting time) is a 3d-view */ - if (curarea == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: No active view for painting\n"); - return 0; - } - - /* pass on current scene and window */ - p->C = C; - p->bmain = CTX_data_main(C); - p->scene = CTX_data_scene(C); - p->depsgraph = CTX_data_depsgraph(C); - p->win = CTX_wm_window(C); - p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill"); - - unit_m4(p->imat); - unit_m4(p->mat); - - switch (curarea->spacetype) { - /* supported views first */ - case SPACE_VIEW3D: - { - /* View3D *v3d = curarea->spacedata.first; */ - /* RegionView3D *rv3d = ar->regiondata; */ - - /* set current area - * - must verify that region data is 3D-view (and not something else) - */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ - p->sa = curarea; - p->ar = ar; - p->align_flag = &ts->gpencil_v3d_align; - - if (ar->regiondata == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); - return 0; - } - - if ((!obact) || (obact->type != OB_GPENCIL)) { - View3D *v3d = p->sa->spacedata.first; - /* if active object doesn't exist or isn't a GP Object, create one */ - const float *cur = p->scene->cursor.location; - - ushort local_view_bits = 0; - if (v3d->localvd) { - local_view_bits = v3d->local_view_uuid; - } - /* create new default object */ - obact = ED_gpencil_add_object(C, p->scene, cur, local_view_bits); - } - /* assign object after all checks to be sure we have one active */ - p->ob = obact; - - break; - } - - /* unsupported views */ - default: - { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Active view not appropriate for Grease Pencil drawing\n"); - return 0; - } - } - - /* get gp-data */ - gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); - if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Current context doesn't allow for any Grease Pencil data\n"); - return 0; - } - else { - /* if no existing GPencil block exists, add one */ - if (*gpd_ptr == NULL) - *gpd_ptr = BKE_gpencil_data_addnew(bmain, "GPencil"); - p->gpd = *gpd_ptr; - } - - /* clear out buffer (stored in gp-data), in case something contaminated it */ - gp_session_validatebuffer(p); - - /* set brush and create a new one if null */ - gp_init_drawing_brush(C, p); - - /* setup active color */ - if (curarea->spacetype == SPACE_VIEW3D) { - /* region where paint was originated */ - p->gpd->runtime.ar = CTX_wm_region(C); - - /* NOTE: This is only done for 3D view, as Materials aren't used for - * annotations in 2D editors - */ - int totcol = p->ob->totcol; - - gp_init_colors(p); - - /* check whether the material was newly added */ - if (totcol != p->ob->totcol) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); - } - } - - /* lock axis (in some modes, disable) */ - if (((*p->align_flag & GP_PROJECT_DEPTH_VIEW) == 0) && - ((*p->align_flag & GP_PROJECT_DEPTH_STROKE) == 0)) - { - p->lock_axis = ts->gp_sculpt.lock_axis; - } - else { - p->lock_axis = 0; - } - - return 1; + Main *bmain = CTX_data_main(C); + bGPdata **gpd_ptr = NULL; + ScrArea *curarea = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *obact = CTX_data_active_object(C); + + /* make sure the active view (at the starting time) is a 3d-view */ + if (curarea == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No active view for painting\n"); + return 0; + } + + /* pass on current scene and window */ + p->C = C; + p->bmain = CTX_data_main(C); + p->scene = CTX_data_scene(C); + p->depsgraph = CTX_data_depsgraph(C); + p->win = CTX_wm_window(C); + p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill"); + + unit_m4(p->imat); + unit_m4(p->mat); + + switch (curarea->spacetype) { + /* supported views first */ + case SPACE_VIEW3D: { + /* View3D *v3d = curarea->spacedata.first; */ + /* RegionView3D *rv3d = ar->regiondata; */ + + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->sa = curarea; + p->ar = ar; + p->align_flag = &ts->gpencil_v3d_align; + + if (ar->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf( + "Error: 3D-View active region doesn't have any region data, so cannot be " + "drawable\n"); + return 0; + } + + if ((!obact) || (obact->type != OB_GPENCIL)) { + View3D *v3d = p->sa->spacedata.first; + /* if active object doesn't exist or isn't a GP Object, create one */ + const float *cur = p->scene->cursor.location; + + ushort local_view_bits = 0; + if (v3d->localvd) { + local_view_bits = v3d->local_view_uuid; + } + /* create new default object */ + obact = ED_gpencil_add_object(C, p->scene, cur, local_view_bits); + } + /* assign object after all checks to be sure we have one active */ + p->ob = obact; + + break; + } + + /* unsupported views */ + default: { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Active view not appropriate for Grease Pencil drawing\n"); + return 0; + } + } + + /* get gp-data */ + gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); + if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Current context doesn't allow for any Grease Pencil data\n"); + return 0; + } + else { + /* if no existing GPencil block exists, add one */ + if (*gpd_ptr == NULL) + *gpd_ptr = BKE_gpencil_data_addnew(bmain, "GPencil"); + p->gpd = *gpd_ptr; + } + + /* clear out buffer (stored in gp-data), in case something contaminated it */ + gp_session_validatebuffer(p); + + /* set brush and create a new one if null */ + gp_init_drawing_brush(C, p); + + /* setup active color */ + if (curarea->spacetype == SPACE_VIEW3D) { + /* region where paint was originated */ + p->gpd->runtime.ar = CTX_wm_region(C); + + /* NOTE: This is only done for 3D view, as Materials aren't used for + * annotations in 2D editors + */ + int totcol = p->ob->totcol; + + gp_init_colors(p); + + /* check whether the material was newly added */ + if (totcol != p->ob->totcol) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); + } + } + + /* lock axis (in some modes, disable) */ + if (((*p->align_flag & GP_PROJECT_DEPTH_VIEW) == 0) && + ((*p->align_flag & GP_PROJECT_DEPTH_STROKE) == 0)) { + p->lock_axis = ts->gp_sculpt.lock_axis; + } + else { + p->lock_axis = 0; + } + + return 1; } /* init new painting session */ static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op) { - tGPsdata *p = NULL; - - /* Create new context data */ - p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); - - /* Try to initialize context data - * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) - */ - if (gp_session_initdata(C, op, p) == 0) { - /* Invalid state - Exit - * NOTE: It should be safe to just free the data, since failing context checks should - * only happen when no data has been allocated. - */ - MEM_freeN(p); - return NULL; - } - - /* Random generator, only init once. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= POINTER_AS_UINT(p); - p->rng = BLI_rng_new(rng_seed); - - /* return context data for running paint operator */ - return p; + tGPsdata *p = NULL; + + /* Create new context data */ + p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); + + /* Try to initialize context data + * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) + */ + if (gp_session_initdata(C, op, p) == 0) { + /* Invalid state - Exit + * NOTE: It should be safe to just free the data, since failing context checks should + * only happen when no data has been allocated. + */ + MEM_freeN(p); + return NULL; + } + + /* Random generator, only init once. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= POINTER_AS_UINT(p); + p->rng = BLI_rng_new(rng_seed); + + /* return context data for running paint operator */ + return p; } /* cleanup after a painting session */ static void gp_session_cleanup(tGPsdata *p) { - bGPdata *gpd = (p) ? p->gpd : NULL; - - /* error checking */ - if (gpd == NULL) - return; - - /* free stroke buffer */ - if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - free sbuffer\n"); */ - MEM_SAFE_FREE(gpd->runtime.sbuffer); - gpd->runtime.sbuffer = NULL; - } - - /* clear flags */ - gpd->runtime.sbuffer_size = 0; - gpd->runtime.sbuffer_sflag = 0; - p->inittime = 0.0; + bGPdata *gpd = (p) ? p->gpd : NULL; + + /* error checking */ + if (gpd == NULL) + return; + + /* free stroke buffer */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - free sbuffer\n"); */ + MEM_SAFE_FREE(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; + } + + /* clear flags */ + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; + p->inittime = 0.0; } static void gp_session_free(tGPsdata *p) { - if (p->rng != NULL) { - BLI_rng_free(p->rng); - } + if (p->rng != NULL) { + BLI_rng_free(p->rng); + } - MEM_freeN(p); + MEM_freeN(p); } /* init new stroke */ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) { - Scene *scene = p->scene; - ToolSettings *ts = scene->toolsettings; - int cfra_eval = (int)DEG_get_ctime(p->depsgraph); - - /* get active layer (or add a new one if non-existent) */ - p->gpl = BKE_gpencil_layer_getactive(p->gpd); - if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); - - if (p->custom_color[3]) { - copy_v3_v3(p->gpl->color, p->custom_color); - } - } - if ((paintmode != GP_PAINTMODE_ERASER) && - (p->gpl->flag & GP_LAYER_LOCKED)) - { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: Cannot paint on locked layer\n"); - return; - } - - /* get active frame (add a new one if not matching frame) */ - if (paintmode == GP_PAINTMODE_ERASER) { - /* Eraser mode: - * 1) Add new frames to all frames that we might touch, - * 2) Ensure that p->gpf refers to the frame used for the active layer - * (to avoid problems with other tools which expect it to exist) - */ - bool has_layer_to_erase = false; - - for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { - /* Skip if layer not editable */ - if (gpencil_layer_is_editable(gpl) == false) - continue; - - /* Add a new frame if needed (and based off the active frame, - * as we need some existing strokes to erase) - * - * Note: We don't add a new frame if there's nothing there now, so - * -> If there are no frames at all, don't add one - * -> If there are no strokes in that frame, don't add a new empty frame - */ - if (gpl->actframe && gpl->actframe->strokes.first) { - gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_COPY); - has_layer_to_erase = true; - } - - /* XXX: we omit GP_FRAME_PAINT here for now, - * as it is only really useful for doing - * paintbuffer drawing - */ - } - - /* Ensure this gets set... */ - p->gpf = p->gpl->actframe; - - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - - if (has_layer_to_erase == false) { - p->status = GP_STATUS_ERROR; - return; - } - } - else { - /* Drawing Modes - Add a new frame if needed on the active layer */ - short add_frame_mode; - - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) - add_frame_mode = GP_GETFRAME_ADD_COPY; - else - add_frame_mode = GP_GETFRAME_ADD_NEW; - - p->gpf = BKE_gpencil_layer_getframe(p->gpl, cfra_eval, add_frame_mode); - - if (p->gpf == NULL) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: No frame created (gpencil_paint_init)\n"); - return; - } - else { - p->gpf->flag |= GP_FRAME_PAINT; - } - } - - /* set 'eraser' for this stroke if using eraser */ - p->paintmode = paintmode; - if (p->paintmode == GP_PAINTMODE_ERASER) { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; - } - else { - /* disable eraser flags - so that we can switch modes during a session */ - p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; - } - - /* set special fill stroke mode */ - if (p->disable_fill == true) { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_NOFILL; - /* replace stroke color with fill color */ - copy_v4_v4(p->gpd->runtime.scolor, p->gpd->runtime.sfill); - } - - /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ - p->flags |= GP_PAINTFLAG_FIRSTRUN; - - /* when drawing in the camera view, in 2D space, set the subrect */ - p->subrect = NULL; - if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { - if (p->sa->spacetype == SPACE_VIEW3D) { - View3D *v3d = p->sa->spacedata.first; - RegionView3D *rv3d = p->ar->regiondata; - - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - /* no shift */ - ED_view3d_calc_camera_border(p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); - p->subrect = &p->subrect_data; - } - } - } - - /* init stroke point space-conversion settings... */ - p->gsc.gpd = p->gpd; - p->gsc.gpl = p->gpl; - - p->gsc.sa = p->sa; - p->gsc.ar = p->ar; - p->gsc.v2d = p->v2d; - - p->gsc.subrect_data = p->subrect_data; - p->gsc.subrect = p->subrect; - - copy_m4_m4(p->gsc.mat, p->mat); - - - /* check if points will need to be made in view-aligned space */ - if (*p->align_flag & GP_PROJECT_VIEWSPACE) { - switch (p->sa->spacetype) { - case SPACE_VIEW3D: - { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - } - } + Scene *scene = p->scene; + ToolSettings *ts = scene->toolsettings; + int cfra_eval = (int)DEG_get_ctime(p->depsgraph); + + /* get active layer (or add a new one if non-existent) */ + p->gpl = BKE_gpencil_layer_getactive(p->gpd); + if (p->gpl == NULL) { + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); + + if (p->custom_color[3]) { + copy_v3_v3(p->gpl->color, p->custom_color); + } + } + if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Cannot paint on locked layer\n"); + return; + } + + /* get active frame (add a new one if not matching frame) */ + if (paintmode == GP_PAINTMODE_ERASER) { + /* Eraser mode: + * 1) Add new frames to all frames that we might touch, + * 2) Ensure that p->gpf refers to the frame used for the active layer + * (to avoid problems with other tools which expect it to exist) + */ + bool has_layer_to_erase = false; + + for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + /* Skip if layer not editable */ + if (gpencil_layer_is_editable(gpl) == false) + continue; + + /* Add a new frame if needed (and based off the active frame, + * as we need some existing strokes to erase) + * + * Note: We don't add a new frame if there's nothing there now, so + * -> If there are no frames at all, don't add one + * -> If there are no strokes in that frame, don't add a new empty frame + */ + if (gpl->actframe && gpl->actframe->strokes.first) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_COPY); + has_layer_to_erase = true; + } + + /* XXX: we omit GP_FRAME_PAINT here for now, + * as it is only really useful for doing + * paintbuffer drawing + */ + } + + /* Ensure this gets set... */ + p->gpf = p->gpl->actframe; + + /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on + * (though this is only available in editmode) + */ + if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { + if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { + p->flags |= GP_PAINTFLAG_SELECTMASK; + } + } + + if (has_layer_to_erase == false) { + p->status = GP_STATUS_ERROR; + return; + } + } + else { + /* Drawing Modes - Add a new frame if needed on the active layer */ + short add_frame_mode; + + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) + add_frame_mode = GP_GETFRAME_ADD_COPY; + else + add_frame_mode = GP_GETFRAME_ADD_NEW; + + p->gpf = BKE_gpencil_layer_getframe(p->gpl, cfra_eval, add_frame_mode); + + if (p->gpf == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No frame created (gpencil_paint_init)\n"); + return; + } + else { + p->gpf->flag |= GP_FRAME_PAINT; + } + } + + /* set 'eraser' for this stroke if using eraser */ + p->paintmode = paintmode; + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; + } + else { + /* disable eraser flags - so that we can switch modes during a session */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; + } + + /* set special fill stroke mode */ + if (p->disable_fill == true) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_NOFILL; + /* replace stroke color with fill color */ + copy_v4_v4(p->gpd->runtime.scolor, p->gpd->runtime.sfill); + } + + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ + p->flags |= GP_PAINTFLAG_FIRSTRUN; + + /* when drawing in the camera view, in 2D space, set the subrect */ + p->subrect = NULL; + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + if (p->sa->spacetype == SPACE_VIEW3D) { + View3D *v3d = p->sa->spacedata.first; + RegionView3D *rv3d = p->ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + /* no shift */ + ED_view3d_calc_camera_border( + p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); + p->subrect = &p->subrect_data; + } + } + } + + /* init stroke point space-conversion settings... */ + p->gsc.gpd = p->gpd; + p->gsc.gpl = p->gpl; + + p->gsc.sa = p->sa; + p->gsc.ar = p->ar; + p->gsc.v2d = p->v2d; + + p->gsc.subrect_data = p->subrect_data; + p->gsc.subrect = p->subrect; + + copy_m4_m4(p->gsc.mat, p->mat); + + /* check if points will need to be made in view-aligned space */ + if (*p->align_flag & GP_PROJECT_VIEWSPACE) { + switch (p->sa->spacetype) { + case SPACE_VIEW3D: { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + break; + } + } + } } /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ static void gp_paint_strokeend(tGPsdata *p) { - ToolSettings *ts = p->scene->toolsettings; - /* for surface sketching, need to set the right OpenGL context stuff so that - * the conversions will project the values correctly... - */ - if (gpencil_project_check(p)) { - View3D *v3d = p->sa->spacedata.first; - - /* need to restore the original projection settings before packing up */ - view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); - } - - /* check if doing eraser or not */ - if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { - /* transfer stroke to frame */ - gp_stroke_newfrombuffer(p); - } - - /* clean up buffer now */ - gp_session_validatebuffer(p); + ToolSettings *ts = p->scene->toolsettings; + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* check if doing eraser or not */ + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { + /* transfer stroke to frame */ + gp_stroke_newfrombuffer(p); + } + + /* clean up buffer now */ + gp_session_validatebuffer(p); } /* finish off stroke painting operation */ static void gp_paint_cleanup(tGPsdata *p) { - /* p->gpd==NULL happens when stroke failed to initialize, - * for example when GP is hidden in current space (sergey) - */ - if (p->gpd) { - /* finish off a stroke */ - gp_paint_strokeend(p); - } - - /* "unlock" frame */ - if (p->gpf) - p->gpf->flag &= ~GP_FRAME_PAINT; + /* p->gpd==NULL happens when stroke failed to initialize, + * for example when GP is hidden in current space (sergey) + */ + if (p->gpd) { + /* finish off a stroke */ + gp_paint_strokeend(p); + } + + /* "unlock" frame */ + if (p->gpf) + p->gpf->flag &= ~GP_FRAME_PAINT; } /* ------------------------------- */ @@ -2255,265 +2260,271 @@ static void gp_paint_cleanup(tGPsdata *p) /* Helper callback for drawing the cursor itself */ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) { - tGPsdata *p = (tGPsdata *)p_ptr; + tGPsdata *p = (tGPsdata *)p_ptr; - if (p->paintmode == GP_PAINTMODE_ERASER) { - GPUVertFormat *format = immVertexFormat(); - const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + if (p->paintmode == GP_PAINTMODE_ERASER) { + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_line_smooth(true); + GPU_blend(true); + GPU_blend_set_func_separate( + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - immUniformColor4ub(255, 100, 100, 20); - imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); - immUnbindProgram(); + immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniform1f("dash_width", 12.0f); - immUniform1f("dash_factor", 0.5f); + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); - imm_draw_circle_wire_2d( - shdr_pos, x, y, p->radius, - /* XXX Dashed shader gives bad results with sets of small segments currently, - * temp hack around the issue. :( */ - max_ii(8, p->radius / 2)); /* was fixed 40 */ + imm_draw_circle_wire_2d( + shdr_pos, + x, + y, + p->radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, p->radius / 2)); /* was fixed 40 */ - immUnbindProgram(); + immUnbindProgram(); - GPU_blend(false); - GPU_line_smooth(false); - } + GPU_blend(false); + GPU_line_smooth(false); + } } /* Turn brush cursor in 3D view on/off */ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) { - if (p->erasercursor && !enable) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); - p->erasercursor = NULL; - } - else if (enable && !p->erasercursor) { - ED_gpencil_toggle_brush_cursor(p->C, false, NULL); - /* enable cursor */ - p->erasercursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - NULL, /* XXX */ - gpencil_draw_eraser, p); - } + if (p->erasercursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); + p->erasercursor = NULL; + } + else if (enable && !p->erasercursor) { + ED_gpencil_toggle_brush_cursor(p->C, false, NULL); + /* enable cursor */ + p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + NULL, /* XXX */ + gpencil_draw_eraser, + p); + } } /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + return (wmtab->Active == EVT_TABLET_ERASER); + } - return false; + return false; } /* ------------------------------- */ static void gpencil_draw_exit(bContext *C, wmOperator *op) { - tGPsdata *p = op->customdata; - - /* don't assume that operator data exists at all */ - if (p) { - /* check size of buffer before cleanup, to determine if anything happened here */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - /* turn off radial brush cursor */ - gpencil_draw_toggle_eraser_cursor(C, p, false); - } - - /* always store the new eraser size to be used again next time - * NOTE: Do this even when not in eraser mode, as eraser may - * have been toggled at some point. - */ - if (p->eraser) { - p->eraser->size = p->radius; - } - - /* restore cursor to indicate end of drawing */ - if (p->sa->spacetype != SPACE_VIEW3D) { - WM_cursor_modal_restore(CTX_wm_window(C)); - } - else { - /* or restore paint if 3D view */ - if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { - WM_cursor_modal_set(p->win, CURSOR_STD); - } - - /* drawing batch cache is dirty now */ - bGPdata *gpd = CTX_data_gpencil_data(C); - if (gpd) { - gpd->flag &= ~GP_DATA_STROKE_POLYGON; - gp_update_cache(gpd); - } - } - - /* clear undo stack */ - gpencil_undo_finish(); - - /* cleanup */ - WM_cursor_modal_set(p->win, CURSOR_STD); - - gp_paint_cleanup(p); - gp_session_cleanup(p); - ED_gpencil_toggle_brush_cursor(C, true, NULL); - - /* finally, free the temp data */ - gp_session_free(p); - p = NULL; - } - - op->customdata = NULL; + tGPsdata *p = op->customdata; + + /* don't assume that operator data exists at all */ + if (p) { + /* check size of buffer before cleanup, to determine if anything happened here */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* turn off radial brush cursor */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + + /* always store the new eraser size to be used again next time + * NOTE: Do this even when not in eraser mode, as eraser may + * have been toggled at some point. + */ + if (p->eraser) { + p->eraser->size = p->radius; + } + + /* restore cursor to indicate end of drawing */ + if (p->sa->spacetype != SPACE_VIEW3D) { + WM_cursor_modal_restore(CTX_wm_window(C)); + } + else { + /* or restore paint if 3D view */ + if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { + WM_cursor_modal_set(p->win, CURSOR_STD); + } + + /* drawing batch cache is dirty now */ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (gpd) { + gpd->flag &= ~GP_DATA_STROKE_POLYGON; + gp_update_cache(gpd); + } + } + + /* clear undo stack */ + gpencil_undo_finish(); + + /* cleanup */ + WM_cursor_modal_set(p->win, CURSOR_STD); + + gp_paint_cleanup(p); + gp_session_cleanup(p); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + + /* finally, free the temp data */ + gp_session_free(p); + p = NULL; + } + + op->customdata = NULL; } static void gpencil_draw_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_draw_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_draw_exit(C, op); } /* ------------------------------- */ - static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) { - tGPsdata *p; - eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); - ToolSettings *ts = CTX_data_tool_settings(C); - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - - /* if mode is draw and the brush is eraser, cancel */ - if (paintmode != GP_PAINTMODE_ERASER) { - if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - return 0; - } - } - - /* check context */ - p = op->customdata = gp_session_initpaint(C, op); - if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { - /* something wasn't set correctly in context */ - gpencil_draw_exit(C, op); - return 0; - } - - /* init painting data */ - gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); - if (p->status == GP_STATUS_ERROR) { - gpencil_draw_exit(C, op); - return 0; - } - - if (event != NULL) { - p->keymodifier = event->keymodifier; - } - else { - p->keymodifier = -1; - } - - p->reports = op->reports; - - /* everything is now setup ok */ - return 1; + tGPsdata *p; + eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + + /* if mode is draw and the brush is eraser, cancel */ + if (paintmode != GP_PAINTMODE_ERASER) { + if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { + return 0; + } + } + + /* check context */ + p = op->customdata = gp_session_initpaint(C, op); + if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { + /* something wasn't set correctly in context */ + gpencil_draw_exit(C, op); + return 0; + } + + /* init painting data */ + gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); + if (p->status == GP_STATUS_ERROR) { + gpencil_draw_exit(C, op); + return 0; + } + + if (event != NULL) { + p->keymodifier = event->keymodifier; + } + else { + p->keymodifier = -1; + } + + p->reports = op->reports; + + /* everything is now setup ok */ + return 1; } - /* ------------------------------- */ /* ensure that the correct cursor icon is set */ static void gpencil_draw_cursor_set(tGPsdata *p) { - Brush *brush = p->brush; - if ((p->paintmode == GP_PAINTMODE_ERASER) || - (brush->gpencil_tool == GPAINT_TOOL_ERASE)) - { - WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ - } - else { - WM_cursor_modal_set(p->win, CURSOR_NONE); - } + Brush *brush = p->brush; + if ((p->paintmode == GP_PAINTMODE_ERASER) || (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { + WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + } + else { + WM_cursor_modal_set(p->win, CURSOR_NONE); + } } /* update UI indicators of status, including cursor and header prints */ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) { - /* header prints */ - switch (p->status) { - case GP_STATUS_IDLING: - { - /* print status info */ - switch (p->paintmode) { - case GP_PAINTMODE_ERASER: - { - ED_workspace_status_text(C, IFACE_("Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | " - "ESC/Enter to end (or click outside this area)")); - break; - } - case GP_PAINTMODE_DRAW_STRAIGHT: - { - ED_workspace_status_text(C, IFACE_("Grease Pencil Line Session: Hold and drag LMB to draw | " - "ESC/Enter to end (or click outside this area)")); - break; - } - case GP_PAINTMODE_SET_CP: - { - ED_workspace_status_text(C, IFACE_("Grease Pencil Guides: LMB click and release to place reference point | " - "Esc/RMB to cancel")); - break; - } - case GP_PAINTMODE_DRAW: - { - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - if (guide->use_guide) { - ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "M key to flip guide | O key to move reference point")); - } - else { - ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); - } - break; - } - case GP_PAINTMODE_DRAW_POLY: - { - ED_workspace_status_text(C, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " - "Release Shift/ESC/Enter to end (or click outside this area)")); - break; - } - default: /* unhandled future cases */ - { - ED_workspace_status_text(C, IFACE_("Grease Pencil Session: ESC/Enter to end (or click outside this area)")); - break; - } - } - break; - } - case GP_STATUS_ERROR: - case GP_STATUS_DONE: - { - /* clear status string */ - ED_workspace_status_text(C, NULL); - break; - } - case GP_STATUS_PAINTING: - break; - } + /* header prints */ + switch (p->status) { + case GP_STATUS_IDLING: { + /* print status info */ + switch (p->paintmode) { + case GP_PAINTMODE_ERASER: { + ED_workspace_status_text( + C, + IFACE_("Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | " + "ESC/Enter to end (or click outside this area)")); + break; + } + case GP_PAINTMODE_DRAW_STRAIGHT: { + ED_workspace_status_text( + C, + IFACE_("Grease Pencil Line Session: Hold and drag LMB to draw | " + "ESC/Enter to end (or click outside this area)")); + break; + } + case GP_PAINTMODE_SET_CP: { + ED_workspace_status_text( + C, + IFACE_("Grease Pencil Guides: LMB click and release to place reference point | " + "Esc/RMB to cancel")); + break; + } + case GP_PAINTMODE_DRAW: { + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + if (guide->use_guide) { + ED_workspace_status_text( + C, + IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " + "M key to flip guide | O key to move reference point")); + } + else { + ED_workspace_status_text( + C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); + } + break; + } + case GP_PAINTMODE_DRAW_POLY: { + ED_workspace_status_text( + C, + IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " + "Release Shift/ESC/Enter to end (or click outside this area)")); + break; + } + default: /* unhandled future cases */ + { + ED_workspace_status_text( + C, IFACE_("Grease Pencil Session: ESC/Enter to end (or click outside this area)")); + break; + } + } + break; + } + case GP_STATUS_ERROR: + case GP_STATUS_DONE: { + /* clear status string */ + ED_workspace_status_text(C, NULL); + break; + } + case GP_STATUS_PAINTING: + break; + } } /* ------------------------------- */ @@ -2521,401 +2532,399 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* create a new stroke point at the point indicated by the painting context */ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) { - bGPdata *gpd = p->gpd; - tGPspoint *pt = NULL; - - /* handle drawing/erasing -> test for erasing first */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - /* do 'live' erasing now */ - gp_stroke_doeraser(p); - - /* store used values */ - copy_v2_v2(p->mvalo, p->mval); - p->opressure = p->pressure; - } - /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ - else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { - - /* if lazy mouse, interpolate the last and current mouse positions */ - if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { - float now_mouse[2]; - float last_mouse[2]; - copy_v2_v2(now_mouse, p->mval); - copy_v2_v2(last_mouse, p->mvalo); - interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); - copy_v2_v2(p->mval, now_mouse); - } - - /* try to add point */ - short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); - - /* handle errors while adding point */ - if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { - /* finish off old stroke */ - gp_paint_strokeend(p); - /* And start a new one!!! Else, projection errors! */ - gp_paint_initstroke(p, p->paintmode, depsgraph); - - /* start a new stroke, starting from previous point */ - /* XXX Must manually reset inittime... */ - /* XXX We only need to reuse previous point if overflow! */ - if (ok == GP_STROKEADD_OVERFLOW) { - p->inittime = p->ocurtime; - gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); - } - else { - p->inittime = p->curtime; - } - gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); - } - else if (ok == GP_STROKEADD_INVALID) { - /* the painting operation cannot continue... */ - BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); - p->status = GP_STATUS_ERROR; - - if (G.debug & G_DEBUG) - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - return; - } - - /* store used values */ - copy_v2_v2(p->mvalo, p->mval); - p->opressure = p->pressure; - p->ocurtime = p->curtime; - - pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; - if (p->paintmode != GP_PAINTMODE_ERASER) { - ED_gpencil_toggle_brush_cursor(C, true, &pt->x); - } - } - else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && - (gpd->runtime.sbuffer_size > 0)) - { - pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; - if (p->paintmode != GP_PAINTMODE_ERASER) { - ED_gpencil_toggle_brush_cursor(C, true, &pt->x); - } - } + bGPdata *gpd = p->gpd; + tGPspoint *pt = NULL; + + /* handle drawing/erasing -> test for erasing first */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* do 'live' erasing now */ + gp_stroke_doeraser(p); + + /* store used values */ + copy_v2_v2(p->mvalo, p->mval); + p->opressure = p->pressure; + } + /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ + else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { + + /* if lazy mouse, interpolate the last and current mouse positions */ + if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { + float now_mouse[2]; + float last_mouse[2]; + copy_v2_v2(now_mouse, p->mval); + copy_v2_v2(last_mouse, p->mvalo); + interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); + copy_v2_v2(p->mval, now_mouse); + } + + /* try to add point */ + short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + + /* handle errors while adding point */ + if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { + /* finish off old stroke */ + gp_paint_strokeend(p); + /* And start a new one!!! Else, projection errors! */ + gp_paint_initstroke(p, p->paintmode, depsgraph); + + /* start a new stroke, starting from previous point */ + /* XXX Must manually reset inittime... */ + /* XXX We only need to reuse previous point if overflow! */ + if (ok == GP_STROKEADD_OVERFLOW) { + p->inittime = p->ocurtime; + gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); + } + else { + p->inittime = p->curtime; + } + gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + } + else if (ok == GP_STROKEADD_INVALID) { + /* the painting operation cannot continue... */ + BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); + p->status = GP_STATUS_ERROR; + + if (G.debug & G_DEBUG) + printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); + return; + } + + /* store used values */ + copy_v2_v2(p->mvalo, p->mval); + p->opressure = p->pressure; + p->ocurtime = p->curtime; + + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } + } + else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && + (gpd->runtime.sbuffer_size > 0)) { + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } + } } /* 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) +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); + 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 snap value to grid */ static float gp_snap_to_grid_fl(float v, const float offset, const float spacing) { - if (spacing > 0.0f) { - return roundf(v / spacing) * spacing + fmodf(offset, spacing); - } - else { - return v; - } + if (spacing > 0.0f) { + return roundf(v / spacing) * spacing + fmodf(offset, spacing); + } + else { + return v; + } } -static void UNUSED_FUNCTION(gp_snap_to_grid_v2)(float v[2], const float offset[2], const float spacing) +static void UNUSED_FUNCTION(gp_snap_to_grid_v2)(float v[2], + const float offset[2], + const float spacing) { - v[0] = gp_snap_to_grid_fl(v[0], offset[0], spacing); - v[1] = gp_snap_to_grid_fl(v[1], offset[1], spacing); + v[0] = gp_snap_to_grid_fl(v[0], offset[0], spacing); + v[1] = gp_snap_to_grid_fl(v[1], offset[1], spacing); } /* get reference point - screen coords to buffer coords */ static void gp_origin_set(wmOperator *op, const int mval[2]) { - tGPsdata *p = op->customdata; - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - float origin[2]; - float point[3]; - copy_v2fl_v2i(origin, mval); - gp_stroke_convertcoords(p, origin, point, NULL); - if (guide->reference_point == GP_GUIDE_REF_CUSTOM) { - copy_v3_v3(guide->location, point); - } - else if (guide->reference_point == GP_GUIDE_REF_CURSOR) { - copy_v3_v3(p->scene->cursor.location, point); - } + tGPsdata *p = op->customdata; + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + float origin[2]; + float point[3]; + copy_v2fl_v2i(origin, mval); + gp_stroke_convertcoords(p, origin, point, NULL); + if (guide->reference_point == GP_GUIDE_REF_CUSTOM) { + copy_v3_v3(guide->location, point); + } + else if (guide->reference_point == GP_GUIDE_REF_CURSOR) { + copy_v3_v3(p->scene->cursor.location, point); + } } /* get reference point - buffer coords to screen coords */ static void gp_origin_get(tGPsdata *p, float origin[2]) { - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - float location[3]; - if (guide->reference_point == GP_GUIDE_REF_CUSTOM) { - copy_v3_v3(location, guide->location); - } - else if (guide->reference_point == GP_GUIDE_REF_OBJECT && - guide->reference_object != NULL) - { - copy_v3_v3(location, guide->reference_object->loc); - } - else { - copy_v3_v3(location, p->scene->cursor.location); - } - GP_SpaceConversion *gsc = &p->gsc; - gp_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin); + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + float location[3]; + if (guide->reference_point == GP_GUIDE_REF_CUSTOM) { + copy_v3_v3(location, guide->location); + } + else if (guide->reference_point == GP_GUIDE_REF_OBJECT && guide->reference_object != NULL) { + copy_v3_v3(location, guide->reference_object->loc); + } + else { + copy_v3_v3(location, p->scene->cursor.location); + } + GP_SpaceConversion *gsc = &p->gsc; + gp_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin); } /* handle draw event */ -static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) +static void gpencil_draw_apply_event( + bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) { - tGPsdata *p = op->customdata; - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - PointerRNA itemptr; - float mousef[2]; - int tablet = 0; - - /* convert from window-space to area-space mouse coordinates - * add any x,y override position for fake events - */ - p->mval[0] = (float)event->mval[0] - x; - p->mval[1] = (float)event->mval[1] - y; - p->shift = event->shift; - - /* verify direction for straight lines */ - if ((guide->use_guide) || ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { - if (p->straight == 0) { - int dx = (int)fabsf(p->mval[0] - p->mvali[0]); - int dy = (int)fabsf(p->mval[1] - p->mvali[1]); - if ((dx > 0) || (dy > 0)) { - /* store mouse direction */ - if (dx > dy) { - p->straight = STROKE_HORIZONTAL; - } - else if (dx < dy) { - p->straight = STROKE_VERTICAL; - } - } - } - } - - p->curtime = PIL_check_seconds_timer(); - - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; - - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } - } - } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } - - /* special eraser modes */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if (event->shift > 0) { - p->flags |= GP_PAINTFLAG_HARD_ERASER; - } - else { - p->flags &= ~GP_PAINTFLAG_HARD_ERASER; - } - if (event->alt > 0) { - p->flags |= GP_PAINTFLAG_STROKE_ERASER; - } - else { - p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; - } - } - - /* special exception for start of strokes (i.e. maybe for just a dot) */ - if (p->flags & GP_PAINTFLAG_FIRSTRUN) { - p->flags &= ~GP_PAINTFLAG_FIRSTRUN; - - /* set values */ - copy_v2_v2(p->mvalo, p->mval); - p->opressure = p->pressure; - p->inittime = p->ocurtime = p->curtime; - p->straight = 0; - - /* save initial mouse */ - copy_v2_v2(p->mvali, p->mval); - - /* calculate once and store snapping distance and origin */ - RegionView3D * rv3d = p->ar->regiondata; - float scale = 1.0f; - if (rv3d->is_persp) { - float vec[3]; - gp_get_3d_reference(p, vec); - mul_m4_v3(rv3d->persmat, vec); - scale = vec[2] * rv3d->pixsize; - } - else { - scale = rv3d->pixsize; - } - p->guide_spacing = guide->spacing / scale; - p->half_spacing = p->guide_spacing * 0.5f; - gp_origin_get(p, p->origin); - - /* special exception here for too high pressure values on first touch in - * windows for some tablets, then we just skip first touch... - */ - if (tablet && (p->pressure >= 0.99f)) { - return; - } - - /* special exception for grid snapping - * it requires direction which needs at least two points - */ - if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) && - guide->use_guide && - guide->use_snapping && - (guide->type == GP_GUIDE_GRID)) - { - p->flags |= GP_PAINTFLAG_REQ_VECTOR; - } - } - - /* wait for vector then add initial point */ - if (p->flags & GP_PAINTFLAG_REQ_VECTOR) { - if (p->straight == 0) { - return; - } - - p->flags &= ~GP_PAINTFLAG_REQ_VECTOR; - - /* create fake events */ - float tmp[2]; - float pt[2]; - copy_v2_v2(tmp, p->mval); - sub_v2_v2v2(pt, p->mval, p->mvali); - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); - if (len_v2v2(p->mval, p->mvalo)) { - sub_v2_v2v2(pt, p->mval, p->mvalo); - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); - } - copy_v2_v2(p->mval, tmp); - } - - /* check if stroke is straight or guided */ - if ((p->paintmode != GP_PAINTMODE_ERASER) && - ((p->straight) || (guide->use_guide))) - { - /* guided stroke */ - if (guide->use_guide) { - switch (guide->type) { - default: - case GP_GUIDE_CIRCULAR: - { - float distance; - distance = len_v2v2(p->mvali, p->origin); - - if (guide->use_snapping && (guide->spacing > 0.0f)) { - distance = gp_snap_to_grid_fl(distance, 0.0f, p->guide_spacing); - } - - dist_ensure_v2_v2fl(p->mval, p->origin, distance); - break; - } - case GP_GUIDE_RADIAL: - { - if (guide->use_snapping && (guide->angle_snap > 0.0f)) { - float point[2]; - float xy[2]; - float angle; - float half_angle = guide->angle_snap * 0.5f; - sub_v2_v2v2(xy, p->mvali, p->origin); - angle = atan2f(xy[1], xy[0]); - angle += (M_PI * 2.0f); - angle = fmodf(angle + half_angle, guide->angle_snap); - angle -= half_angle; - gp_rotate_v2_v2v2fl(point, p->mvali, p->origin, -angle); - closest_to_line_v2(p->mval, p->mval, point, p->origin); - } - else { - closest_to_line_v2(p->mval, p->mval, p->mvali, p->origin); - } - break; - } - case GP_GUIDE_PARALLEL: - { - float point[2]; - float unit[2]; - copy_v2_v2(unit, p->mvali); - unit[0] += 1.0f; /* start from horizontal */ - gp_rotate_v2_v2v2fl(point, unit, p->mvali, guide->angle); - closest_to_line_v2(p->mval, p->mval, p->mvali, point); - - if (guide->use_snapping && (guide->spacing > 0.0f)) { - gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, -guide->angle); - p->mval[1] = gp_snap_to_grid_fl(p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing); - gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, guide->angle); - } - break; - } - case GP_GUIDE_GRID: - { - if (guide->use_snapping && (guide->spacing > 0.0f)) { - float point[2]; - float unit[2]; - float angle; - copy_v2_v2(unit, p->mvali); - unit[0] += 1.0f; /* start from horizontal */ - angle = (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f; - gp_rotate_v2_v2v2fl(point, unit, p->mvali, angle); - closest_to_line_v2(p->mval, p->mval, p->mvali, point); - - if (p->straight == STROKE_HORIZONTAL) { - p->mval[1] = gp_snap_to_grid_fl(p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing); - } - else { - p->mval[0] = gp_snap_to_grid_fl(p->mval[0] - p->half_spacing, p->origin[0], p->guide_spacing); - } - } - else if (p->straight == STROKE_HORIZONTAL) { - p->mval[1] = p->mvali[1]; /* replace y */ - } - else { - p->mval[0] = p->mvali[0]; /* replace x */ - } - break; - } - } - } - else if (p->straight == STROKE_HORIZONTAL) { - p->mval[1] = p->mvali[1]; /* replace y */ - } - else { - p->mval[0] = p->mvali[0]; /* replace x */ - } - } - - /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - mousef[0] = p->mval[0]; - mousef[1] = p->mval[1]; - RNA_float_set_array(&itemptr, "mouse", mousef); - RNA_float_set(&itemptr, "pressure", p->pressure); - RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); - - RNA_float_set(&itemptr, "time", p->curtime - p->inittime); - - /* apply the current latest drawing point */ - gpencil_draw_apply(C, op, p, depsgraph); - - /* force refresh */ - /* just active area for now, since doing whole screen is too slow */ - ED_region_tag_redraw(p->ar); + tGPsdata *p = op->customdata; + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + PointerRNA itemptr; + float mousef[2]; + int tablet = 0; + + /* convert from window-space to area-space mouse coordinates + * add any x,y override position for fake events + */ + p->mval[0] = (float)event->mval[0] - x; + p->mval[1] = (float)event->mval[1] - y; + p->shift = event->shift; + + /* verify direction for straight lines */ + if ((guide->use_guide) || + ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { + if (p->straight == 0) { + int dx = (int)fabsf(p->mval[0] - p->mvali[0]); + int dy = (int)fabsf(p->mval[1] - p->mvali[1]); + if ((dx > 0) || (dy > 0)) { + /* store mouse direction */ + if (dx > dy) { + p->straight = STROKE_HORIZONTAL; + } + else if (dx < dy) { + p->straight = STROKE_VERTICAL; + } + } + } + } + + p->curtime = PIL_check_seconds_timer(); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + p->pressure = wmtab->Pressure; + + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; + } + } + } + else { + /* No tablet data -> No pressure info is available */ + p->pressure = 1.0f; + } + + /* special eraser modes */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if (event->shift > 0) { + p->flags |= GP_PAINTFLAG_HARD_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_HARD_ERASER; + } + if (event->alt > 0) { + p->flags |= GP_PAINTFLAG_STROKE_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; + } + } + + /* special exception for start of strokes (i.e. maybe for just a dot) */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + /* set values */ + copy_v2_v2(p->mvalo, p->mval); + p->opressure = p->pressure; + p->inittime = p->ocurtime = p->curtime; + p->straight = 0; + + /* save initial mouse */ + copy_v2_v2(p->mvali, p->mval); + + /* calculate once and store snapping distance and origin */ + RegionView3D *rv3d = p->ar->regiondata; + float scale = 1.0f; + if (rv3d->is_persp) { + float vec[3]; + gp_get_3d_reference(p, vec); + mul_m4_v3(rv3d->persmat, vec); + scale = vec[2] * rv3d->pixsize; + } + else { + scale = rv3d->pixsize; + } + p->guide_spacing = guide->spacing / scale; + p->half_spacing = p->guide_spacing * 0.5f; + gp_origin_get(p, p->origin); + + /* special exception here for too high pressure values on first touch in + * windows for some tablets, then we just skip first touch... + */ + if (tablet && (p->pressure >= 0.99f)) { + return; + } + + /* special exception for grid snapping + * it requires direction which needs at least two points + */ + if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) && guide->use_guide && + guide->use_snapping && (guide->type == GP_GUIDE_GRID)) { + p->flags |= GP_PAINTFLAG_REQ_VECTOR; + } + } + + /* wait for vector then add initial point */ + if (p->flags & GP_PAINTFLAG_REQ_VECTOR) { + if (p->straight == 0) { + return; + } + + p->flags &= ~GP_PAINTFLAG_REQ_VECTOR; + + /* create fake events */ + float tmp[2]; + float pt[2]; + copy_v2_v2(tmp, p->mval); + sub_v2_v2v2(pt, p->mval, p->mvali); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + if (len_v2v2(p->mval, p->mvalo)) { + sub_v2_v2v2(pt, p->mval, p->mvalo); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + } + copy_v2_v2(p->mval, tmp); + } + + /* check if stroke is straight or guided */ + if ((p->paintmode != GP_PAINTMODE_ERASER) && ((p->straight) || (guide->use_guide))) { + /* guided stroke */ + if (guide->use_guide) { + switch (guide->type) { + default: + case GP_GUIDE_CIRCULAR: { + float distance; + distance = len_v2v2(p->mvali, p->origin); + + if (guide->use_snapping && (guide->spacing > 0.0f)) { + distance = gp_snap_to_grid_fl(distance, 0.0f, p->guide_spacing); + } + + dist_ensure_v2_v2fl(p->mval, p->origin, distance); + break; + } + case GP_GUIDE_RADIAL: { + if (guide->use_snapping && (guide->angle_snap > 0.0f)) { + float point[2]; + float xy[2]; + float angle; + float half_angle = guide->angle_snap * 0.5f; + sub_v2_v2v2(xy, p->mvali, p->origin); + angle = atan2f(xy[1], xy[0]); + angle += (M_PI * 2.0f); + angle = fmodf(angle + half_angle, guide->angle_snap); + angle -= half_angle; + gp_rotate_v2_v2v2fl(point, p->mvali, p->origin, -angle); + closest_to_line_v2(p->mval, p->mval, point, p->origin); + } + else { + closest_to_line_v2(p->mval, p->mval, p->mvali, p->origin); + } + break; + } + case GP_GUIDE_PARALLEL: { + float point[2]; + float unit[2]; + copy_v2_v2(unit, p->mvali); + unit[0] += 1.0f; /* start from horizontal */ + gp_rotate_v2_v2v2fl(point, unit, p->mvali, guide->angle); + closest_to_line_v2(p->mval, p->mval, p->mvali, point); + + if (guide->use_snapping && (guide->spacing > 0.0f)) { + gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, -guide->angle); + p->mval[1] = gp_snap_to_grid_fl( + p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing); + gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, guide->angle); + } + break; + } + case GP_GUIDE_GRID: { + if (guide->use_snapping && (guide->spacing > 0.0f)) { + float point[2]; + float unit[2]; + float angle; + copy_v2_v2(unit, p->mvali); + unit[0] += 1.0f; /* start from horizontal */ + angle = (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f; + gp_rotate_v2_v2v2fl(point, unit, p->mvali, angle); + closest_to_line_v2(p->mval, p->mval, p->mvali, point); + + if (p->straight == STROKE_HORIZONTAL) { + p->mval[1] = gp_snap_to_grid_fl( + p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing); + } + else { + p->mval[0] = gp_snap_to_grid_fl( + p->mval[0] - p->half_spacing, p->origin[0], p->guide_spacing); + } + } + else if (p->straight == STROKE_HORIZONTAL) { + p->mval[1] = p->mvali[1]; /* replace y */ + } + else { + p->mval[0] = p->mvali[0]; /* replace x */ + } + break; + } + } + } + else if (p->straight == STROKE_HORIZONTAL) { + p->mval[1] = p->mvali[1]; /* replace y */ + } + else { + p->mval[0] = p->mvali[0]; /* replace x */ + } + } + + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + mousef[0] = p->mval[0]; + mousef[1] = p->mval[1]; + RNA_float_set_array(&itemptr, "mouse", mousef); + RNA_float_set(&itemptr, "pressure", p->pressure); + RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); + + RNA_float_set(&itemptr, "time", p->curtime - p->inittime); + + /* apply the current latest drawing point */ + gpencil_draw_apply(C, op, p, depsgraph); + + /* force refresh */ + /* just active area for now, since doing whole screen is too slow */ + ED_region_tag_redraw(p->ar); } /* ------------------------------- */ @@ -2923,910 +2932,945 @@ static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ static int gpencil_draw_exec(bContext *C, wmOperator *op) { - tGPsdata *p = NULL; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - /* printf("GPencil - Starting Re-Drawing\n"); */ - - /* try to initialize context data needed while drawing */ - if (!gpencil_draw_init(C, op, NULL)) { - MEM_SAFE_FREE(op->customdata); - /* printf("\tGP - no valid data\n"); */ - return OPERATOR_CANCELLED; - } - else - p = op->customdata; - - /* printf("\tGP - Start redrawing stroke\n"); */ - - /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), - * setting the relevant values in context at each step, then applying - */ - RNA_BEGIN(op->ptr, itemptr, "stroke") - { - float mousef[2]; - - /* printf("\t\tGP - stroke elem\n"); */ - - /* get relevant data for this point from stroke */ - RNA_float_get_array(&itemptr, "mouse", mousef); - p->mval[0] = mousef[0]; - p->mval[1] = mousef[1]; - p->pressure = RNA_float_get(&itemptr, "pressure"); - p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; - - if (RNA_boolean_get(&itemptr, "is_start")) { - /* if first-run flag isn't set already (i.e. not true first stroke), - * then we must terminate the previous one first before continuing - */ - if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { - /* TODO: both of these ops can set error-status, but we probably don't need to worry */ - gp_paint_strokeend(p); - gp_paint_initstroke(p, p->paintmode, depsgraph); - } - } - - /* if first run, set previous data too */ - if (p->flags & GP_PAINTFLAG_FIRSTRUN) { - p->flags &= ~GP_PAINTFLAG_FIRSTRUN; - - p->mvalo[0] = p->mval[0]; - p->mvalo[1] = p->mval[1]; - p->opressure = p->pressure; - p->ocurtime = p->curtime; - } - - /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(C, op, p, depsgraph); - } - RNA_END; - - /* printf("\tGP - done\n"); */ - - /* cleanup */ - gpencil_draw_exit(C, op); - - /* refreshes */ - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* done */ - return OPERATOR_FINISHED; + tGPsdata *p = NULL; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* printf("GPencil - Starting Re-Drawing\n"); */ + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, NULL)) { + MEM_SAFE_FREE(op->customdata); + /* printf("\tGP - no valid data\n"); */ + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* printf("\tGP - Start redrawing stroke\n"); */ + + /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), + * setting the relevant values in context at each step, then applying + */ + RNA_BEGIN (op->ptr, itemptr, "stroke") { + float mousef[2]; + + /* printf("\t\tGP - stroke elem\n"); */ + + /* get relevant data for this point from stroke */ + RNA_float_get_array(&itemptr, "mouse", mousef); + p->mval[0] = mousef[0]; + p->mval[1] = mousef[1]; + p->pressure = RNA_float_get(&itemptr, "pressure"); + p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; + + if (RNA_boolean_get(&itemptr, "is_start")) { + /* if first-run flag isn't set already (i.e. not true first stroke), + * then we must terminate the previous one first before continuing + */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + /* TODO: both of these ops can set error-status, but we probably don't need to worry */ + gp_paint_strokeend(p); + gp_paint_initstroke(p, p->paintmode, depsgraph); + } + } + + /* if first run, set previous data too */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } + + /* apply this data as necessary now (as per usual) */ + gpencil_draw_apply(C, op, p, depsgraph); + } + RNA_END; + + /* printf("\tGP - done\n"); */ + + /* cleanup */ + gpencil_draw_exit(C, op); + + /* refreshes */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* done */ + return OPERATOR_FINISHED; } /* ------------------------------- */ /* handle events for guides */ -static void gpencil_guide_event_handling(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +static void gpencil_guide_event_handling(bContext *C, + wmOperator *op, + const wmEvent *event, + tGPsdata *p) { - bool add_notifier = false; - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - - /* Enter or exit set center point mode */ - if ((event->type == OKEY) && (event->val == KM_RELEASE)) { - if (p->paintmode == GP_PAINTMODE_DRAW && guide->reference_point != GP_GUIDE_REF_OBJECT) { - add_notifier = true; - p->paintmode = GP_PAINTMODE_SET_CP; - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - } - /* Freehand mode, turn off speed guide */ - else if ((event->type == VKEY) && (event->val == KM_RELEASE)) { - guide->use_guide = false; - add_notifier = true; - } - /* Alternate or flip direction */ - else if ((event->type == MKEY) && (event->val == KM_RELEASE)) { - if (guide->type == GP_GUIDE_CIRCULAR) { - add_notifier = true; - guide->type = GP_GUIDE_RADIAL; - } - else if (guide->type == GP_GUIDE_RADIAL) { - add_notifier = true; - guide->type = GP_GUIDE_CIRCULAR; - } - else if (guide->type == GP_GUIDE_PARALLEL) { - add_notifier = true; - guide->angle += M_PI_2; - guide->angle = angle_compat_rad(guide->angle, M_PI); - } - else { - add_notifier = false; - } - } - /* Line guides */ - else if ((event->type == LKEY) && (event->val == KM_RELEASE)) { - add_notifier = true; - guide->use_guide = true; - if (event->ctrl) { - guide->angle = 0.0f; - guide->type = GP_GUIDE_PARALLEL; - } - else if (event->alt) { - guide->type = GP_GUIDE_PARALLEL; - guide->angle = RNA_float_get(op->ptr, "guide_last_angle"); - } - else { - guide->type = GP_GUIDE_PARALLEL; - } - } - /* Point guide */ - else if ((event->type == CKEY) && (event->val == KM_RELEASE)) { - add_notifier = true; - if (!guide->use_guide) { - guide->use_guide = true; - guide->type = GP_GUIDE_CIRCULAR; - } - else if (guide->type == GP_GUIDE_CIRCULAR) { - guide->type = GP_GUIDE_RADIAL; - } - else if (guide->type == GP_GUIDE_RADIAL) { - guide->type = GP_GUIDE_CIRCULAR; - } - else { - guide->type = GP_GUIDE_CIRCULAR; - } - } - /* Change line angle */ - else if (ELEM(event->type, JKEY, KKEY) && (event->val == KM_RELEASE)) { - add_notifier = true; - float angle = guide->angle; - float adjust = (float)M_PI / 180.0f; - if (event->alt) - adjust *= 45.0f; - else if (!event->shift) - adjust *= 15.0f; - angle += (event->type == JKEY) ? adjust : -adjust; - angle = angle_compat_rad(angle, M_PI); - guide->angle = angle; - } - - if (add_notifier) { - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS | NC_GPENCIL | NA_EDITED, NULL); - } + bool add_notifier = false; + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + + /* Enter or exit set center point mode */ + if ((event->type == OKEY) && (event->val == KM_RELEASE)) { + if (p->paintmode == GP_PAINTMODE_DRAW && guide->reference_point != GP_GUIDE_REF_OBJECT) { + add_notifier = true; + p->paintmode = GP_PAINTMODE_SET_CP; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + } + /* Freehand mode, turn off speed guide */ + else if ((event->type == VKEY) && (event->val == KM_RELEASE)) { + guide->use_guide = false; + add_notifier = true; + } + /* Alternate or flip direction */ + else if ((event->type == MKEY) && (event->val == KM_RELEASE)) { + if (guide->type == GP_GUIDE_CIRCULAR) { + add_notifier = true; + guide->type = GP_GUIDE_RADIAL; + } + else if (guide->type == GP_GUIDE_RADIAL) { + add_notifier = true; + guide->type = GP_GUIDE_CIRCULAR; + } + else if (guide->type == GP_GUIDE_PARALLEL) { + add_notifier = true; + guide->angle += M_PI_2; + guide->angle = angle_compat_rad(guide->angle, M_PI); + } + else { + add_notifier = false; + } + } + /* Line guides */ + else if ((event->type == LKEY) && (event->val == KM_RELEASE)) { + add_notifier = true; + guide->use_guide = true; + if (event->ctrl) { + guide->angle = 0.0f; + guide->type = GP_GUIDE_PARALLEL; + } + else if (event->alt) { + guide->type = GP_GUIDE_PARALLEL; + guide->angle = RNA_float_get(op->ptr, "guide_last_angle"); + } + else { + guide->type = GP_GUIDE_PARALLEL; + } + } + /* Point guide */ + else if ((event->type == CKEY) && (event->val == KM_RELEASE)) { + add_notifier = true; + if (!guide->use_guide) { + guide->use_guide = true; + guide->type = GP_GUIDE_CIRCULAR; + } + else if (guide->type == GP_GUIDE_CIRCULAR) { + guide->type = GP_GUIDE_RADIAL; + } + else if (guide->type == GP_GUIDE_RADIAL) { + guide->type = GP_GUIDE_CIRCULAR; + } + else { + guide->type = GP_GUIDE_CIRCULAR; + } + } + /* Change line angle */ + else if (ELEM(event->type, JKEY, KKEY) && (event->val == KM_RELEASE)) { + add_notifier = true; + float angle = guide->angle; + float adjust = (float)M_PI / 180.0f; + if (event->alt) + adjust *= 45.0f; + else if (!event->shift) + adjust *= 15.0f; + angle += (event->type == JKEY) ? adjust : -adjust; + angle = angle_compat_rad(angle, M_PI); + guide->angle = angle; + } + + if (add_notifier) { + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS | NC_GPENCIL | NA_EDITED, NULL); + } } /* start of interactive drawing part of operator */ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tGPsdata *p = NULL; - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - - if (G.debug & G_DEBUG) - printf("GPencil - Starting Drawing\n"); - - /* support for tablets eraser pen */ - if (gpencil_is_tablet_eraser_active(event)) { - RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); - } - - /* do not draw in locked or invisible layers */ - eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); - if (paintmode != GP_PAINTMODE_ERASER) { - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - if ((gpl) && ((gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE))) { - BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hide"); - return OPERATOR_CANCELLED; - } - } - else { - /* don't erase empty frames */ - bool has_layer_to_erase = false; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* Skip if layer not editable */ - if (gpencil_layer_is_editable(gpl)) { - if (gpl->actframe && gpl->actframe->strokes.first) { - has_layer_to_erase = true; - break; - } - } - } - if (!has_layer_to_erase) { - BKE_report(op->reports, RPT_ERROR, "Nothing to erase or all layers locked"); - return OPERATOR_FINISHED; - } - } - - /* try to initialize context data needed while drawing */ - if (!gpencil_draw_init(C, op, event)) { - if (op->customdata) - MEM_freeN(op->customdata); - if (G.debug & G_DEBUG) - printf("\tGP - no valid data\n"); - return OPERATOR_CANCELLED; - } - else - p = op->customdata; - - /* TODO: set any additional settings that we can take from the events? - * TODO? if tablet is erasing, force eraser to be on? */ - - /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ - - /* if eraser is on, draw radial aid */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - gpencil_draw_toggle_eraser_cursor(C, p, true); - } - else { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - /* set cursor - * NOTE: This may change later (i.e. intentionally via brush toggle, - * or unintentionally if the user scrolls outside the area)... - */ - gpencil_draw_cursor_set(p); - - /* only start drawing immediately if we're allowed to do so... */ - if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { - /* hotkey invoked - start drawing */ - /* printf("\tGP - set first spot\n"); */ - p->status = GP_STATUS_PAINTING; - - /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); - op->flag |= OP_IS_MODAL_CURSOR_REGION; - } - else { - /* toolbar invoked - don't start drawing yet... */ - /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ - op->flag |= OP_IS_MODAL_CURSOR_REGION; - } - - /* enable paint mode */ - /* handle speed guide events before drawing inside view3d */ - if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP)) { - gpencil_guide_event_handling(C, op, event, p); - } - - if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { - /* FIXME: use the mode switching operator, this misses notifiers, messages. */ - /* Just set paintmode flag... */ - p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; - /* disable other GP modes */ - p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; - p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; - p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - /* set workspace mode */ - ob->restore_mode = ob->mode; - ob->mode = OB_MODE_PAINT_GPENCIL; - /* redraw mode on screen */ - WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); - } - - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* add a modal handler for this operator, so that we can then draw continuous strokes */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; + tGPsdata *p = NULL; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + if (G.debug & G_DEBUG) + printf("GPencil - Starting Drawing\n"); + + /* support for tablets eraser pen */ + if (gpencil_is_tablet_eraser_active(event)) { + RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); + } + + /* do not draw in locked or invisible layers */ + eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + if (paintmode != GP_PAINTMODE_ERASER) { + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if ((gpl) && ((gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE))) { + BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hide"); + return OPERATOR_CANCELLED; + } + } + else { + /* don't erase empty frames */ + bool has_layer_to_erase = false; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* Skip if layer not editable */ + if (gpencil_layer_is_editable(gpl)) { + if (gpl->actframe && gpl->actframe->strokes.first) { + has_layer_to_erase = true; + break; + } + } + } + if (!has_layer_to_erase) { + BKE_report(op->reports, RPT_ERROR, "Nothing to erase or all layers locked"); + return OPERATOR_FINISHED; + } + } + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, event)) { + if (op->customdata) + MEM_freeN(op->customdata); + if (G.debug & G_DEBUG) + printf("\tGP - no valid data\n"); + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* TODO: set any additional settings that we can take from the events? + * TODO? if tablet is erasing, force eraser to be on? */ + + /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ + + /* if eraser is on, draw radial aid */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + gpencil_draw_toggle_eraser_cursor(C, p, true); + } + else { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + /* set cursor + * NOTE: This may change later (i.e. intentionally via brush toggle, + * or unintentionally if the user scrolls outside the area)... + */ + gpencil_draw_cursor_set(p); + + /* only start drawing immediately if we're allowed to do so... */ + if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { + /* hotkey invoked - start drawing */ + /* printf("\tGP - set first spot\n"); */ + p->status = GP_STATUS_PAINTING; + + /* handle the initial drawing - i.e. for just doing a simple dot */ + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + else { + /* toolbar invoked - don't start drawing yet... */ + /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + + /* enable paint mode */ + /* handle speed guide events before drawing inside view3d */ + if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP)) { + gpencil_guide_event_handling(C, op, event, p); + } + + if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { + /* FIXME: use the mode switching operator, this misses notifiers, messages. */ + /* Just set paintmode flag... */ + p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; + /* disable other GP modes */ + p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + /* set workspace mode */ + ob->restore_mode = ob->mode; + ob->mode = OB_MODE_PAINT_GPENCIL; + /* redraw mode on screen */ + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator, so that we can then draw continuous strokes */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; } /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) { - bScreen *sc = CTX_wm_screen(C); - return (BLI_findindex(&sc->areabase, sa_test) != -1); + bScreen *sc = CTX_wm_screen(C); + return (BLI_findindex(&sc->areabase, sa_test) != -1); } static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) { - tGPsdata *p = op->customdata; + tGPsdata *p = op->customdata; - /* we must check that we're still within the area that we're set up to work from - * otherwise we could crash (see bug #20586) - */ - if (CTX_wm_area(C) != p->sa) { - printf("\t\t\tGP - wrong area execution abort!\n"); - p->status = GP_STATUS_ERROR; - } + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + printf("\t\t\tGP - wrong area execution abort!\n"); + p->status = GP_STATUS_ERROR; + } - /* printf("\t\tGP - start stroke\n"); */ + /* printf("\t\tGP - start stroke\n"); */ - /* we may need to set up paint env again if we're resuming */ - /* XXX: watch it with the paintmode! in future, - * it'd be nice to allow changing paint-mode when in sketching-sessions */ + /* we may need to set up paint env again if we're resuming */ + /* XXX: watch it with the paintmode! in future, + * it'd be nice to allow changing paint-mode when in sketching-sessions */ - if (gp_session_initdata(C, op, p)) - gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); + if (gp_session_initdata(C, op, p)) + gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); - if (p->status != GP_STATUS_ERROR) { - p->status = GP_STATUS_PAINTING; - op->flag &= ~OP_IS_MODAL_CURSOR_REGION; - } + if (p->status != GP_STATUS_ERROR) { + p->status = GP_STATUS_PAINTING; + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } - return op->customdata; + return op->customdata; } static void gpencil_stroke_end(wmOperator *op) { - tGPsdata *p = op->customdata; + tGPsdata *p = op->customdata; - gp_paint_cleanup(p); + gp_paint_cleanup(p); - gpencil_undo_push(p->gpd); + gpencil_undo_push(p->gpd); - gp_session_cleanup(p); + gp_session_cleanup(p); - p->status = GP_STATUS_IDLING; - op->flag |= OP_IS_MODAL_CURSOR_REGION; + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; - p->gpd = NULL; - p->gpl = NULL; - p->gpf = NULL; + p->gpd = NULL; + p->gpl = NULL; + p->gpf = NULL; } /* Move last stroke in the listbase to the head to be drawn below all previous strokes in the layer */ static void gpencil_move_last_stroke_to_back(bContext *C) { - /* move last stroke (the polygon) to head of the listbase stroke to draw on back of all previous strokes */ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl, gpl->actframe)) { - return; - } - - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps = gpf->strokes.last; - if (ELEM(NULL, gps)) { - return; - } - - BLI_remlink(&gpf->strokes, gps); - BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); + /* move last stroke (the polygon) to head of the listbase stroke to draw on back of all previous strokes */ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps = gpf->strokes.last; + if (ELEM(NULL, gps)) { + return; + } + + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); } /* add events for missing mouse movements when the artist draw very fast */ -static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +static void gpencil_add_missing_events(bContext *C, + wmOperator *op, + const wmEvent *event, + tGPsdata *p) { - Brush *brush = p->brush; - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - int input_samples = brush->gpencil_settings->input_samples; - - /* ensure sampling when using circular guide */ - if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) { - input_samples = GP_MAX_INPUT_SAMPLES; - } - - if (input_samples == 0) { - return; - } - - RegionView3D *rv3d = p->ar->regiondata; - float defaultpixsize = rv3d->pixsize * 1000.0f; - int samples = (GP_MAX_INPUT_SAMPLES - input_samples + 1); - float thickness = (float)brush->size; - - float pt[2], a[2], b[2]; - float vec[3]; - float scale = 1.0f; - - /* get pixel scale */ - gp_get_3d_reference(p, vec); - mul_m4_v3(rv3d->persmat, vec); - if (rv3d->is_persp) { - scale = vec[2] * defaultpixsize; - } - else { - scale = defaultpixsize; - } - - /* The thickness of the brush is reduced of thickness to get overlap dots */ - float dot_factor = 0.50f; - if (samples < 2) { - dot_factor = 0.05f; - } - else if (samples < 4) { - dot_factor = 0.10f; - } - else if (samples < 7) { - dot_factor = 0.3f; - } - else if (samples < 10) { - dot_factor = 0.4f; - } - float factor = ((thickness * dot_factor) / scale) * samples; - - copy_v2_v2(a, p->mvalo); - b[0] = (float)event->mval[0] + 1.0f; - b[1] = (float)event->mval[1] + 1.0f; - - /* get distance in pixels */ - float dist = len_v2v2(a, b); - - /* for very small distances, add a half way point */ - if (dist <= 2.0f) { - interp_v2_v2v2(pt, a, b, 0.5f); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); - } - else if (dist >= factor) { - int slices = 2 + (int)((dist - 1.0) / factor); - float n = 1.0f / slices; - for (int i = 1; i < slices; i++) { - interp_v2_v2v2(pt, a, b, n * i); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - gpencil_draw_apply_event( - C, op, event, CTX_data_depsgraph(C), - pt[0], pt[1]); - } - } + Brush *brush = p->brush; + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + int input_samples = brush->gpencil_settings->input_samples; + + /* ensure sampling when using circular guide */ + if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) { + input_samples = GP_MAX_INPUT_SAMPLES; + } + + if (input_samples == 0) { + return; + } + + RegionView3D *rv3d = p->ar->regiondata; + float defaultpixsize = rv3d->pixsize * 1000.0f; + int samples = (GP_MAX_INPUT_SAMPLES - input_samples + 1); + float thickness = (float)brush->size; + + float pt[2], a[2], b[2]; + float vec[3]; + float scale = 1.0f; + + /* get pixel scale */ + gp_get_3d_reference(p, vec); + mul_m4_v3(rv3d->persmat, vec); + if (rv3d->is_persp) { + scale = vec[2] * defaultpixsize; + } + else { + scale = defaultpixsize; + } + + /* The thickness of the brush is reduced of thickness to get overlap dots */ + float dot_factor = 0.50f; + if (samples < 2) { + dot_factor = 0.05f; + } + else if (samples < 4) { + dot_factor = 0.10f; + } + else if (samples < 7) { + dot_factor = 0.3f; + } + else if (samples < 10) { + dot_factor = 0.4f; + } + float factor = ((thickness * dot_factor) / scale) * samples; + + copy_v2_v2(a, p->mvalo); + b[0] = (float)event->mval[0] + 1.0f; + b[1] = (float)event->mval[1] + 1.0f; + + /* get distance in pixels */ + float dist = len_v2v2(a, b); + + /* for very small distances, add a half way point */ + if (dist <= 2.0f) { + interp_v2_v2v2(pt, a, b, 0.5f); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + } + else if (dist >= factor) { + int slices = 2 + (int)((dist - 1.0) / factor); + float n = 1.0f / slices; + for (int i = 1; i < slices; i++) { + interp_v2_v2v2(pt, a, b, n * i); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]); + } + } } /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGPsdata *p = op->customdata; - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - /* default exit state - pass through to support MMB view nav, etc. */ - int estate = OPERATOR_PASS_THROUGH; - - /* if (event->type == NDOF_MOTION) - * return OPERATOR_PASS_THROUGH; - * ------------------------------- - * [mce] Not quite what I was looking - * for, but a good start! GP continues to - * draw on the screen while the 3D mouse - * moves the viewpoint. Problem is that - * the stroke is converted to 3D only after - * it is finished. This approach should work - * better in tools that immediately apply - * in 3D space. - */ - - if (p->status == GP_STATUS_IDLING) { - ARegion *ar = CTX_wm_region(C); - p->ar = ar; - } - - /* special mode for editing control points */ - if (p->paintmode == GP_PAINTMODE_SET_CP) { - wmWindow *win = p->win; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); - bool drawmode = false; - - switch (event->type) { - /* cancel */ - case ESCKEY: - case RIGHTMOUSE: - { - if (ELEM(event->val, KM_RELEASE)) { - drawmode = true; - } - break; - } - /* set */ - case LEFTMOUSE: - { - if (ELEM(event->val, KM_RELEASE)) { - gp_origin_set(op, event->mval); - drawmode = true; - } - break; - } - } - if (drawmode) { - p->status = GP_STATUS_IDLING; - p->paintmode = GP_PAINTMODE_DRAW; - ED_gpencil_toggle_brush_cursor(C, true, NULL); - DEG_id_tag_update(&p->scene->id, ID_RECALC_COPY_ON_WRITE); - } - else { - return OPERATOR_RUNNING_MODAL; - } - } - - /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ - if (ISKEYBOARD(event->type)) { - if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { - /* allow some keys: - * - for frame changing [#33412] - * - for undo (during sketching sessions) - */ - } - else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { - /* allow numpad keys so that camera/view manipulations can still take place - * - PAD0 in particular is really important for Grease Pencil drawing, - * as animators may be working "to camera", so having this working - * is essential for ensuring that they can quickly return to that view - */ - } - else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { - /* Add Blank Frame - * - Since this operator is non-modal, we can just call it here, and keep going... - * - This operator is especially useful when animating - */ - WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); - estate = OPERATOR_RUNNING_MODAL; - } - else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) { - gpencil_guide_event_handling(C, op, event, p); - estate = OPERATOR_RUNNING_MODAL; - } - else { - estate = OPERATOR_RUNNING_MODAL; - } - } - - //printf("\tGP - handle modal event...\n"); - - /* exit painting mode (and/or end current stroke) - * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] - */ - /* if polyline and release shift must cancel */ - if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || - ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) - { - /* exit() ends the current stroke before cleaning up */ - /* printf("\t\tGP - end of paint op + end of stroke\n"); */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } - - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - - /* toggle painting mode upon mouse-button movement - * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) - * - RIGHTMOUSE = polyline (hotkey) / eraser (all) - * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) - * also making sure we have a valid event value, to not exit too early - */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { - /* if painting, end stroke */ - if (p->status == GP_STATUS_PAINTING) { - int sketch = 0; - - /* basically, this should be mouse-button up = end stroke - * BUT, polyline drawing is an exception -- all knots should be added during one session - */ - sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); - - if (sketch) { - /* end stroke only, and then wait to resume painting soon */ - /* printf("\t\tGP - end stroke only\n"); */ - gpencil_stroke_end(op); - - /* If eraser mode is on, turn it off after the stroke finishes - * NOTE: This just makes it nicer to work with drawing sessions - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - p->paintmode = RNA_enum_get(op->ptr, "mode"); - - /* if the original mode was *still* eraser, - * we'll let it say for now, since this gives - * users an opportunity to have visual feedback - * when adjusting eraser size - */ - if (p->paintmode != GP_PAINTMODE_ERASER) { - /* turn off cursor... - * NOTE: this should be enough for now - * Just hiding this makes it seem like - * you can paint again... - */ - gpencil_draw_toggle_eraser_cursor(C, p, false); - } - } - - /* we've just entered idling state, so this event was processed (but no others yet) */ - estate = OPERATOR_RUNNING_MODAL; - - /* stroke could be smoothed, send notifier to refresh screen */ - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - } - else { - /* printf("\t\tGP - end of stroke + op\n"); */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); - - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - } - else if (event->val == KM_PRESS) { - bool in_bounds = false; - - /* Check if we're outside the bounds of the active region - * NOTE: An exception here is that if launched from the toolbar, - * whatever region we're now in should become the new region - */ - if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { - /* Change to whatever region is now under the mouse */ - ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); - - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", - current_region, p->ar, event->x, event->y, - p->sa->totrct.xmin, p->sa->totrct.ymin, p->sa->totrct.xmax, p->sa->totrct.ymax); - } - - if (current_region) { - /* Assume that since we found the cursor in here, it is in bounds - * and that this should be the region that we begin drawing in - */ - p->ar = current_region; - in_bounds = true; - } - else { - /* Out of bounds, or invalid in some other way */ - p->status = GP_STATUS_ERROR; - estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } - } - else if (p->ar) { - rcti region_rect; - - /* Perform bounds check using */ - ED_region_visible_rect(p->ar, ®ion_rect); - in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); - } - else { - /* No region */ - p->status = GP_STATUS_ERROR; - estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) - printf("%s: No active region found in GP Paint session data\n", __func__); - } - - if (in_bounds) { - /* Switch paintmode (temporarily if need be) based on which button was used - * NOTE: This is to make it more convenient to erase strokes when using drawing sessions - */ - if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { - /* turn on eraser */ - p->paintmode = GP_PAINTMODE_ERASER; - } - else if (event->type == LEFTMOUSE) { - /* restore drawmode to default */ - p->paintmode = RNA_enum_get(op->ptr, "mode"); - } - - gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); - - /* not painting, so start stroke (this should be mouse-button down) */ - p = gpencil_stroke_begin(C, op); - - if (p->status == GP_STATUS_ERROR) { - estate = OPERATOR_CANCELLED; - } - } - else if (p->status != GP_STATUS_ERROR) { - /* User clicked outside bounds of window while idling, so exit paintmode - * NOTE: Don't enter this case if an error occurred while finding the - * region (as above) - */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } - } - else if (event->val == KM_RELEASE) { - p->status = GP_STATUS_IDLING; - op->flag |= OP_IS_MODAL_CURSOR_REGION; - ED_region_tag_redraw(p->ar); - } - } - - /* handle mode-specific events */ - if (p->status == GP_STATUS_PAINTING) { - /* handle painting mouse-movements? */ - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { - /* handle drawing event */ - /* printf("\t\tGP - add point\n"); */ - - if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) || (guide->use_guide)) { - gpencil_add_missing_events(C, op, event, p); - } - - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); - - /* finish painting operation if anything went wrong just now */ - if (p->status == GP_STATUS_ERROR) { - printf("\t\t\t\tGP - add error done!\n"); - estate = OPERATOR_CANCELLED; - } - else { - /* event handled, so just tag as running modal */ - /* printf("\t\t\t\tGP - add point handled!\n"); */ - estate = OPERATOR_RUNNING_MODAL; - } - } - /* eraser size */ - else if ((p->paintmode == GP_PAINTMODE_ERASER) && - ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) - { - /* just resize the brush (local version) - * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys - */ - /* printf("\t\tGP - resize eraser\n"); */ - switch (event->type) { - case WHEELDOWNMOUSE: /* larger */ - case PADPLUSKEY: - p->radius += 5; - break; - - case WHEELUPMOUSE: /* smaller */ - case PADMINUS: - p->radius -= 5; - - if (p->radius <= 0) - p->radius = 1; - break; - } - - /* force refresh */ - /* just active area for now, since doing whole screen is too slow */ - ED_region_tag_redraw(p->ar); - - /* event handled, so just tag as running modal */ - estate = OPERATOR_RUNNING_MODAL; - } - /* there shouldn't be any other events, but just in case there are, let's swallow them - * (i.e. to prevent problems with undo) - */ - else { - /* swallow event to save ourselves trouble */ - estate = OPERATOR_RUNNING_MODAL; - } - } - - /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ - if (0 == gpencil_area_exists(C, p->sa)) - estate = OPERATOR_CANCELLED; - else { - /* update status indicators - cursor, header, etc. */ - gpencil_draw_status_indicators(C, p); - gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ - } - - /* process last operations before exiting */ - switch (estate) { - case OPERATOR_FINISHED: - /* store stroke angle for parallel guide */ - if ((p->straight == 0) || (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR))) { - float xy[2]; - sub_v2_v2v2(xy, p->mval, p->mvali); - float angle = atan2f(xy[1], xy[0]); - RNA_float_set(op->ptr, "guide_last_angle", angle); - } - /* one last flush before we're done */ - gpencil_draw_exit(C, op); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - break; - - case OPERATOR_CANCELLED: - gpencil_draw_exit(C, op); - break; - - case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: - /* event doesn't need to be handled */ + tGPsdata *p = op->customdata; + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + /* default exit state - pass through to support MMB view nav, etc. */ + int estate = OPERATOR_PASS_THROUGH; + + /* if (event->type == NDOF_MOTION) + * return OPERATOR_PASS_THROUGH; + * ------------------------------- + * [mce] Not quite what I was looking + * for, but a good start! GP continues to + * draw on the screen while the 3D mouse + * moves the viewpoint. Problem is that + * the stroke is converted to 3D only after + * it is finished. This approach should work + * better in tools that immediately apply + * in 3D space. + */ + + if (p->status == GP_STATUS_IDLING) { + ARegion *ar = CTX_wm_region(C); + p->ar = ar; + } + + /* special mode for editing control points */ + if (p->paintmode == GP_PAINTMODE_SET_CP) { + wmWindow *win = p->win; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + bool drawmode = false; + + switch (event->type) { + /* cancel */ + case ESCKEY: + case RIGHTMOUSE: { + if (ELEM(event->val, KM_RELEASE)) { + drawmode = true; + } + break; + } + /* set */ + case LEFTMOUSE: { + if (ELEM(event->val, KM_RELEASE)) { + gp_origin_set(op, event->mval); + drawmode = true; + } + break; + } + } + if (drawmode) { + p->status = GP_STATUS_IDLING; + p->paintmode = GP_PAINTMODE_DRAW; + ED_gpencil_toggle_brush_cursor(C, true, NULL); + DEG_id_tag_update(&p->scene->id, ID_RECALC_COPY_ON_WRITE); + } + else { + return OPERATOR_RUNNING_MODAL; + } + } + + /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ + if (ISKEYBOARD(event->type)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { + /* allow some keys: + * - for frame changing [#33412] + * - for undo (during sketching sessions) + */ + } + else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { + /* allow numpad keys so that camera/view manipulations can still take place + * - PAD0 in particular is really important for Grease Pencil drawing, + * as animators may be working "to camera", so having this working + * is essential for ensuring that they can quickly return to that view + */ + } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } + else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) { + gpencil_guide_event_handling(C, op, event, p); + estate = OPERATOR_RUNNING_MODAL; + } + else { + estate = OPERATOR_RUNNING_MODAL; + } + } + + //printf("\tGP - handle modal event...\n"); + + /* exit painting mode (and/or end current stroke) + * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] + */ + /* if polyline and release shift must cancel */ + if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || + ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) { + /* exit() ends the current stroke before cleaning up */ + /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && + (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } + + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + + /* toggle painting mode upon mouse-button movement + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) + * - RIGHTMOUSE = polyline (hotkey) / eraser (all) + * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) + * also making sure we have a valid event value, to not exit too early + */ + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { + /* if painting, end stroke */ + if (p->status == GP_STATUS_PAINTING) { + int sketch = 0; + + /* basically, this should be mouse-button up = end stroke + * BUT, polyline drawing is an exception -- all knots should be added during one session + */ + sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); + + if (sketch) { + /* end stroke only, and then wait to resume painting soon */ + /* printf("\t\tGP - end stroke only\n"); */ + gpencil_stroke_end(op); + + /* If eraser mode is on, turn it off after the stroke finishes + * NOTE: This just makes it nicer to work with drawing sessions + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->paintmode = RNA_enum_get(op->ptr, "mode"); + + /* if the original mode was *still* eraser, + * we'll let it say for now, since this gives + * users an opportunity to have visual feedback + * when adjusting eraser size + */ + if (p->paintmode != GP_PAINTMODE_ERASER) { + /* turn off cursor... + * NOTE: this should be enough for now + * Just hiding this makes it seem like + * you can paint again... + */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + } + + /* we've just entered idling state, so this event was processed (but no others yet) */ + estate = OPERATOR_RUNNING_MODAL; + + /* stroke could be smoothed, send notifier to refresh screen */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + } + else { + /* printf("\t\tGP - end of stroke + op\n"); */ + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && + (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); + + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_PRESS) { + bool in_bounds = false; + + /* Check if we're outside the bounds of the active region + * NOTE: An exception here is that if launched from the toolbar, + * whatever region we're now in should become the new region + */ + if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { + /* Change to whatever region is now under the mouse */ + ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); + + if (G.debug & G_DEBUG) { + printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", + current_region, + p->ar, + event->x, + event->y, + p->sa->totrct.xmin, + p->sa->totrct.ymin, + p->sa->totrct.xmax, + p->sa->totrct.ymax); + } + + if (current_region) { + /* Assume that since we found the cursor in here, it is in bounds + * and that this should be the region that we begin drawing in + */ + p->ar = current_region; + in_bounds = true; + } + else { + /* Out of bounds, or invalid in some other way */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); + } + } + else if (p->ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(p->ar, ®ion_rect); + in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + } + else { + /* No region */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: No active region found in GP Paint session data\n", __func__); + } + + if (in_bounds) { + /* Switch paintmode (temporarily if need be) based on which button was used + * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + */ + if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { + /* turn on eraser */ + p->paintmode = GP_PAINTMODE_ERASER; + } + else if (event->type == LEFTMOUSE) { + /* restore drawmode to default */ + p->paintmode = RNA_enum_get(op->ptr, "mode"); + } + + gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); + + /* not painting, so start stroke (this should be mouse-button down) */ + p = gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { + estate = OPERATOR_CANCELLED; + } + } + else if (p->status != GP_STATUS_ERROR) { + /* User clicked outside bounds of window while idling, so exit paintmode + * NOTE: Don't enter this case if an error occurred while finding the + * region (as above) + */ + /* if drawing polygon and enable on back, must move stroke */ + if (ts) { + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && + (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } + } + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_RELEASE) { + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + ED_region_tag_redraw(p->ar); + } + } + + /* handle mode-specific events */ + if (p->status == GP_STATUS_PAINTING) { + /* handle painting mouse-movements? */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { + /* handle drawing event */ + /* printf("\t\tGP - add point\n"); */ + + if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) || (guide->use_guide)) { + gpencil_add_missing_events(C, op, event, p); + } + + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f); + + /* finish painting operation if anything went wrong just now */ + if (p->status == GP_STATUS_ERROR) { + printf("\t\t\t\tGP - add error done!\n"); + estate = OPERATOR_CANCELLED; + } + else { + /* event handled, so just tag as running modal */ + /* printf("\t\t\t\tGP - add point handled!\n"); */ + estate = OPERATOR_RUNNING_MODAL; + } + } + /* eraser size */ + else if ((p->paintmode == GP_PAINTMODE_ERASER) && + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) { + /* just resize the brush (local version) + * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys + */ + /* printf("\t\tGP - resize eraser\n"); */ + switch (event->type) { + case WHEELDOWNMOUSE: /* larger */ + case PADPLUSKEY: + p->radius += 5; + break; + + case WHEELUPMOUSE: /* smaller */ + case PADMINUS: + p->radius -= 5; + + if (p->radius <= 0) + p->radius = 1; + break; + } + + /* force refresh */ + /* just active area for now, since doing whole screen is too slow */ + ED_region_tag_redraw(p->ar); + + /* event handled, so just tag as running modal */ + estate = OPERATOR_RUNNING_MODAL; + } + /* there shouldn't be any other events, but just in case there are, let's swallow them + * (i.e. to prevent problems with undo) + */ + else { + /* swallow event to save ourselves trouble */ + estate = OPERATOR_RUNNING_MODAL; + } + } + + /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + if (0 == gpencil_area_exists(C, p->sa)) + estate = OPERATOR_CANCELLED; + else { + /* update status indicators - cursor, header, etc. */ + gpencil_draw_status_indicators(C, p); + gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ + } + + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + /* store stroke angle for parallel guide */ + if ((p->straight == 0) || (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR))) { + float xy[2]; + sub_v2_v2v2(xy, p->mval, p->mvali); + float angle = atan2f(xy[1], xy[0]); + RNA_float_set(op->ptr, "guide_last_angle", angle); + } + /* one last flush before we're done */ + gpencil_draw_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_draw_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + /* event doesn't need to be handled */ #if 0 - printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", - event->type, event->type == MIDDLEMOUSE, event->type == MOUSEMOVE); + printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", + event->type, event->type == MIDDLEMOUSE, event->type == MOUSEMOVE); #endif - break; - } + break; + } - /* return status code */ - return estate; + /* return status code */ + return estate; } /* ------------------------------- */ static const EnumPropertyItem prop_gpencil_drawmodes[] = { - {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, - {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", "Draw straight line segment(s)"}, - {GP_PAINTMODE_DRAW_POLY, "DRAW_POLY", 0, "Draw Poly Line", "Click to place endpoints of straight line segments (connected)"}, - {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Grease Pencil strokes"}, - {0, NULL, 0, NULL, NULL}, + {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, + {GP_PAINTMODE_DRAW_STRAIGHT, + "DRAW_STRAIGHT", + 0, + "Draw Straight Lines", + "Draw straight line segment(s)"}, + {GP_PAINTMODE_DRAW_POLY, + "DRAW_POLY", + 0, + "Draw Poly Line", + "Click to place endpoints of straight line segments (connected)"}, + {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Grease Pencil strokes"}, + {0, NULL, 0, NULL, NULL}, }; void GPENCIL_OT_draw(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Grease Pencil Draw"; - ot->idname = "GPENCIL_OT_draw"; - ot->description = "Draw a new stroke in the active Grease Pencil Object"; - - /* api callbacks */ - ot->exec = gpencil_draw_exec; - ot->invoke = gpencil_draw_invoke; - ot->modal = gpencil_draw_modal; - ot->cancel = gpencil_draw_cancel; - ot->poll = gpencil_draw_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* settings for drawing */ - ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); - - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* NOTE: wait for input is enabled by default, - * so that all UI code can work properly without needing users to know about this */ - prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "disable_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - /* guides */ - prop = RNA_def_float(ot->srna, "guide_last_angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Speed guide angle", -10000.0f, 10000.0f); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Grease Pencil Draw"; + ot->idname = "GPENCIL_OT_draw"; + ot->description = "Draw a new stroke in the active Grease Pencil Object"; + + /* api callbacks */ + ot->exec = gpencil_draw_exec; + ot->invoke = gpencil_draw_invoke; + ot->modal = gpencil_draw_modal; + ot->cancel = gpencil_draw_cancel; + ot->poll = gpencil_draw_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* settings for drawing */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* NOTE: wait for input is enabled by default, + * so that all UI code can work properly without needing users to know about this */ + prop = RNA_def_boolean(ot->srna, + "wait_for_input", + true, + "Wait for Input", + "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, + "disable_fill", + false, + "No Fill Areas", + "Disable fill to use stroke as fill boundary"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + /* guides */ + prop = RNA_def_float(ot->srna, + "guide_last_angle", + 0.0f, + -10000.0f, + 10000.0f, + "Angle", + "Speed guide angle", + -10000.0f, + 10000.0f); } /* additional OPs */ static int gpencil_guide_rotate(bContext *C, wmOperator *op) { - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Guide *guide = &ts->gp_sculpt.guide; - float angle = RNA_float_get(op->ptr, "angle"); - bool increment = RNA_boolean_get(op->ptr, "increment"); - if (increment) { - float oldangle = guide->angle; - oldangle += angle; - guide->angle = angle_compat_rad(oldangle, M_PI); - } - else { - guide->angle = angle_compat_rad(angle, M_PI); - } - - return OPERATOR_FINISHED; + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Sculpt_Guide *guide = &ts->gp_sculpt.guide; + float angle = RNA_float_get(op->ptr, "angle"); + bool increment = RNA_boolean_get(op->ptr, "increment"); + if (increment) { + float oldangle = guide->angle; + oldangle += angle; + guide->angle = angle_compat_rad(oldangle, M_PI); + } + else { + guide->angle = angle_compat_rad(angle, M_PI); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_guide_rotate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Rotate Guide Angle"; - ot->idname = "GPENCIL_OT_guide_rotate"; - ot->description = "Rotate guide angle"; + /* identifiers */ + ot->name = "Rotate Guide Angle"; + ot->idname = "GPENCIL_OT_guide_rotate"; + ot->description = "Rotate guide angle"; - /* api callbacks */ - ot->exec = gpencil_guide_rotate; + /* api callbacks */ + ot->exec = gpencil_guide_rotate; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - PropertyRNA *prop; + PropertyRNA *prop; - prop = RNA_def_boolean(ot->srna, "increment", true, "Increment", "Increment angle"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_float(ot->srna, "angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Guide angle", -10000.0f, 10000.0f); - RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "increment", true, "Increment", "Increment angle"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_float( + ot->srna, "angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Guide angle", -10000.0f, 10000.0f); + RNA_def_property_flag(prop, PROP_HIDDEN); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 374dbbf4d9c..f178d232376 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -97,193 +97,201 @@ #define SELECT_CP2 3 #define SELECT_END 4 -#define BIG_SIZE_CTL 15 -#define MID_SIZE_CTL 10 -#define SMALL_SIZE_CTL 8 +#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 */ +/* ************************************************ */ +/* 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; + 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; - - MaterialGPencilStyle *gp_style = NULL; - - /* use brush material */ - p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); - - /* assign color information to temp data */ - gp_style = p->mat->gp_style; - if (gp_style) { - - /* set colors */ - if (gp_style->flag & GP_STYLE_STROKE_SHOW) { - copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); - } - else { - /* if no stroke, use fill */ - copy_v4_v4(gpd->runtime.scolor, gp_style->fill_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; - } + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + + MaterialGPencilStyle *gp_style = NULL; + + /* use brush material */ + p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); + + /* assign color information to temp data */ + gp_style = p->mat->gp_style; + if (gp_style) { + + /* set colors */ + if (gp_style->flag & GP_STYLE_STROKE_SHOW) { + copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); + } + else { + /* if no stroke, use fill */ + copy_v4_v4(gpd->runtime.scolor, gp_style->fill_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; - } + 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) +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); + 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) +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); + 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); - } - } + 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); + } + } } /* Helper to reflect point */ -static void UNUSED_FUNCTION(gp_reflect_point_v2_v2v2v2)( - float va[2], const float p[2], const float a[2], const float b[2]) +static void UNUSED_FUNCTION(gp_reflect_point_v2_v2v2v2)(float va[2], + const float p[2], + const float a[2], + const float b[2]) { - float point[2]; - closest_to_line_v2(point, p, a, b); - va[0] = point[0] - (p[0] - point[0]); - va[1] = point[1] - (p[1] - point[1]); + float point[2]; + closest_to_line_v2(point, p, a, b); + va[0] = point[0] - (p[0] - point[0]); + va[1] = point[1] - (p[1] - point[1]); } - /* Poll callback for primitive operators */ +/* Poll callback for primitive operators */ static bool gpencil_primitive_add_poll(bContext *C) { - /* only 3D view */ - ScrArea *sa = CTX_wm_area(C); - if (sa && sa->spacetype != SPACE_VIEW3D) { - return 0; - } - - /* need data to create primitive */ - bGPdata *gpd = CTX_data_gpencil_data(C); - if (gpd == NULL) { - return 0; - } - - /* only in edit and paint modes - * - paint as it's the "drawing/creation mode" - * - edit as this is more of an atomic editing operation - * (similar to copy/paste), and also for consistency - */ - if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) { - CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes"); - return 0; - } - - /* don't allow operator to function if the active layer is locked/hidden - * (BUT, if there isn't an active layer, we are free to add new layer when the time comes) - */ - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) { - CTX_wm_operator_poll_msg_set(C, "Primitives cannot be added as active layer is locked or hidden"); - return 0; - } - - return 1; + /* only 3D view */ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return 0; + } + + /* need data to create primitive */ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (gpd == NULL) { + return 0; + } + + /* only in edit and paint modes + * - paint as it's the "drawing/creation mode" + * - edit as this is more of an atomic editing operation + * (similar to copy/paste), and also for consistency + */ + if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) { + CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes"); + return 0; + } + + /* don't allow operator to function if the active layer is locked/hidden + * (BUT, if there isn't an active layer, we are free to add new layer when the time comes) + */ + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) { + CTX_wm_operator_poll_msg_set(C, + "Primitives cannot be added as active layer is locked or hidden"); + return 0; + } + + 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); + 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 *********************** */ @@ -291,1510 +299,1511 @@ static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi) /* Helper: Create internal strokes primitives data */ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) { - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - - /* if brush doesn't exist, create a new one */ - Paint *paint = &ts->gp_paint->paint; - /* if not exist, create a new one */ - if (paint->brush == NULL) { - /* create new brushes */ - BKE_brush_gpencil_presets(C); - } - tgpi->brush = paint->brush; - - /* if layer doesn't exist, create a new one */ - if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); - } - tgpi->gpl = gpl; - - /* create a new temporary frame */ - tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe"); - tgpi->gpf->framenum = tgpi->cframe = cfra_eval; - - /* create new temp stroke */ - bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke"); - gps->thickness = 2.0f; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; - gps->inittime = 0.0f; - - /* enable recalculation flag by default */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->flag &= ~GP_STROKE_SELECT; - /* the polygon must be closed, so enabled cyclic */ - if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { - gps->flag |= GP_STROKE_CYCLIC; - } - - gps->flag |= GP_STROKE_3DSPACE; - - gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat); - - /* allocate memory for storage points, but keep empty */ - gps->totpoints = 0; - gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points"); - /* initialize triangle memory to dummy data */ - gps->tot_triangles = 0; - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* add to strokes */ - BLI_addtail(&tgpi->gpf->strokes, gps); - - /* allocate memory for storage points */ - gpencil_primitive_allocate_memory(tgpi); - - /* Random generator, only init once. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - tgpi->rng = BLI_rng_new(rng_seed); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + + /* if brush doesn't exist, create a new one */ + Paint *paint = &ts->gp_paint->paint; + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + tgpi->brush = paint->brush; + + /* if layer doesn't exist, create a new one */ + if (gpl == NULL) { + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + } + tgpi->gpl = gpl; + + /* create a new temporary frame */ + tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe"); + tgpi->gpf->framenum = tgpi->cframe = cfra_eval; + + /* create new temp stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke"); + gps->thickness = 2.0f; + gps->gradient_f = 1.0f; + gps->gradient_s[0] = 1.0f; + gps->gradient_s[1] = 1.0f; + gps->inittime = 0.0f; + + /* enable recalculation flag by default */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->flag &= ~GP_STROKE_SELECT; + /* the polygon must be closed, so enabled cyclic */ + if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { + gps->flag |= GP_STROKE_CYCLIC; + } + + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat); + + /* allocate memory for storage points, but keep empty */ + gps->totpoints = 0; + gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points"); + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* add to strokes */ + BLI_addtail(&tgpi->gpf->strokes, gps); + + /* allocate memory for storage points */ + gpencil_primitive_allocate_memory(tgpi); + + /* Random generator, only init once. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + tgpi->rng = BLI_rng_new(rng_seed); } /* add new segment to curve */ static void gpencil_primitive_add_segment(tGPDprimitive *tgpi) { - 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); + 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) { - if (tgpi->flag == IN_PROGRESS) { - return; - } - - 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); - color[3] = 0.8f; - cp->size = size; - tgpi->gpd->runtime.tot_cp_points += 1; - } + if (tgpi->flag == IN_PROGRESS) { + return; + } + + 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); + color[3] = 0.8f; + 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) { - Scene *scene = tgpi->scene; - char status_str[UI_MAX_DRAW_STR]; - char msg_str[UI_MAX_DRAW_STR]; - - if (tgpi->type == GP_STROKE_LINE) { - BLI_strncpy( - msg_str, - IFACE_("Line: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- to adjust subdivision number, Shift to align, Alt to center, E: extrude"), - UI_MAX_DRAW_STR); - } - else if (tgpi->type == GP_STROKE_BOX) { - BLI_strncpy( - msg_str, - IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- to adjust subdivision number, Shift to square, Alt to center"), - UI_MAX_DRAW_STR); - } - else if (tgpi->type == GP_STROKE_CIRCLE) { - BLI_strncpy( - msg_str, - IFACE_("Circle: ESC to cancel, Enter/MMB 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 to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, M: Flip, E: extrude"), - UI_MAX_DRAW_STR); - } - else if (tgpi->type == GP_STROKE_CURVE) { - BLI_strncpy( - msg_str, - IFACE_("Curve: ESC to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, E: extrude"), - UI_MAX_DRAW_STR); - } - - 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]; - - outputNumInput(&tgpi->num, str_offs, &scene->unit); - BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); - } - else { - if (tgpi->flag == IN_PROGRESS) { - BLI_snprintf( - status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, tgpi->tot_edges, - (int)tgpi->start[0], (int)tgpi->start[1], (int)tgpi->end[0], (int)tgpi->end[1]); - } - else { - BLI_snprintf( - status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, tgpi->tot_edges, - (int)tgpi->end[0], (int)tgpi->end[1]); - } - } - } - else { - if (tgpi->flag == IN_PROGRESS) { - BLI_snprintf( - status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, tgpi->tot_edges, - (int)tgpi->start[0], (int)tgpi->start[1], (int)tgpi->end[0], (int)tgpi->end[1]); - } - else { - BLI_snprintf( - status_str, sizeof(status_str), "%s: (%d, %d)", msg_str, - (int)tgpi->end[0], (int)tgpi->end[1]); - } - } - ED_workspace_status_text(C, status_str); + Scene *scene = tgpi->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + if (tgpi->type == GP_STROKE_LINE) { + BLI_strncpy(msg_str, + IFACE_("Line: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- to " + "adjust subdivision number, Shift to align, Alt to center, E: extrude"), + UI_MAX_DRAW_STR); + } + else if (tgpi->type == GP_STROKE_BOX) { + BLI_strncpy(msg_str, + IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- " + "to adjust subdivision number, Shift to square, Alt to center"), + UI_MAX_DRAW_STR); + } + else if (tgpi->type == GP_STROKE_CIRCLE) { + BLI_strncpy(msg_str, + IFACE_("Circle: ESC to cancel, Enter/MMB 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 to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge number, " + "Shift to square, Alt to center, M: Flip, E: extrude"), + UI_MAX_DRAW_STR); + } + else if (tgpi->type == GP_STROKE_CURVE) { + BLI_strncpy(msg_str, + IFACE_("Curve: ESC to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge " + "number, Shift to square, Alt to center, E: extrude"), + UI_MAX_DRAW_STR); + } + + 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]; + + outputNumInput(&tgpi->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf(status_str, + sizeof(status_str), + "%s: %d (%d, %d) (%d, %d)", + msg_str, + tgpi->tot_edges, + (int)tgpi->start[0], + (int)tgpi->start[1], + (int)tgpi->end[0], + (int)tgpi->end[1]); + } + else { + BLI_snprintf(status_str, + sizeof(status_str), + "%s: %d (%d, %d)", + msg_str, + tgpi->tot_edges, + (int)tgpi->end[0], + (int)tgpi->end[1]); + } + } + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf(status_str, + sizeof(status_str), + "%s: %d (%d, %d) (%d, %d)", + msg_str, + tgpi->tot_edges, + (int)tgpi->start[0], + (int)tgpi->start[1], + (int)tgpi->end[0], + (int)tgpi->end[1]); + } + else { + BLI_snprintf(status_str, + sizeof(status_str), + "%s: (%d, %d)", + msg_str, + (int)tgpi->end[0], + (int)tgpi->end[1]); + } + } + ED_workspace_status_text(C, status_str); } /* create a rectangle */ static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D) { - 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++; - } - } - - mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); - float color[4]; - UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, 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); + 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++; + } + } + + mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end); + float color[4]; + UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, 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) { - 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_GIZMO_PRIMARY, 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); - } + 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_GIZMO_PRIMARY, 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 + 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_GIZMO_PRIMARY, 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_GIZMO_SECONDARY, color); - gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f); + 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_GIZMO_PRIMARY, 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_GIZMO_SECONDARY, color); + gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f); } /* 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]; - interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a); - a += step; - } - float color[4]; - UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, 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_GIZMO_SECONDARY, 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); + 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]; + interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a); + a += step; + } + float color[4]; + UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, 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_GIZMO_SECONDARY, 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 + 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; - - 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 = 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_GIZMO_PRIMARY, 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); + 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; + + 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 = 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_GIZMO_PRIMARY, 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 */ 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; - const char *align_flag = &ts->gpencil_v3d_align; - bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); - const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && - (tgpi->rv3d->persp == RV3D_CAMOB) && (!is_depth); - - 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 = tgpi->points; - - if (tgpi->tot_edges > 1) { - switch (tgpi->type) { - case GP_STROKE_BOX: - gp_primitive_rectangle(tgpi, points2D); - break; - case GP_STROKE_LINE: - gp_primitive_line(tgpi, points2D); - break; - case GP_STROKE_CIRCLE: - gp_primitive_circle(tgpi, points2D); - break; - case GP_STROKE_ARC: - gp_primitive_arc(tgpi, points2D); - 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]; - - /* set rnd value for reuse */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (p2d->rnd_dirty != true)) { - p2d->rnd[0] = BLI_rng_get_float(tgpi->rng); - p2d->rnd[1] = BLI_rng_get_float(tgpi->rng); - p2d->rnd[2] = BLI_rng_get_float(tgpi->rng); - p2d->rnd_dirty = true; - } - - /* 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; - } - - /* exponential value */ - const float exfactor = SQUARE(brush->gpencil_settings->draw_jitter + 2.0f); - const float fac = p2d->rnd[0] * exfactor * jitter; - - /* vector */ - float mvec[2], svec[2]; - if (i > 0) { - mvec[0] = (p2d->x - (p2d - 1)->x); - mvec[1] = (p2d->y - (p2d - 1)->y); - normalize_v2(mvec); - } - else { - zero_v2(mvec); - } - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - if (p2d->rnd[1] > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - add_v2_v2(&p2d->x, svec); - } - - /* apply randomness to pressure */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_press > 0.0f)) - { - if (p2d->rnd[0] > 0.5f) { - pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[1]; - } - else { - pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[2]; - } - } - - /* 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)) - { - if (p2d->rnd[2] > 0.5f) { - strength -= strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[0]; - } - else { - strength += strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1]; - } - 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; - - /* point uv */ - if (gpd->runtime.sbuffer_size > 0) { - MaterialGPencilStyle *gp_style = tgpi->mat->gp_style; - const float pixsize = gp_style->texture_pixsize / 1000000.0f; - tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; - bGPDspoint spt, spt2; - - /* get origin to reproject point */ - float origin[3]; - ED_gp_get_drawing_reference( - tgpi->scene, tgpi->ob, tgpi->gpl, - ts->gpencil_v3d_align, origin); - /* reproject current */ - ED_gpencil_tpoint_to_point(tgpi->ar, origin, tpt, &spt); - ED_gp_project_point_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, - origin, tgpi->lock_axis - 1, &spt); - - /* reproject previous */ - ED_gpencil_tpoint_to_point(tgpi->ar, origin, tptb, &spt2); - ED_gp_project_point_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, - origin, tgpi->lock_axis - 1, &spt2); - tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; - tpt->uv_fac = tgpi->totpixlen; - if ((gp_style) && (gp_style->sima)) { - tpt->uv_fac /= gp_style->sima->gen_x; - } - } - else { - tgpi->totpixlen = 0.0f; - tpt->uv_fac = 0.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, depth_arr ? depth_arr + i : NULL, - &pt->x); - - pt->pressure = pressure; - pt->strength = strength; - pt->time = 0.0f; - pt->flag = 0; - pt->uv_fac = tpt->uv_fac; - - 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); - } - - /* 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); - ED_gp_project_stroke_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, gps, - origin, ts->gp_sculpt.lock_axis - 1); - } - - /* if parented change position relative to parent object */ - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt); - } - - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(C, tgpi->gpl, gps); - } - - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - 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); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + 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; + const char *align_flag = &ts->gpencil_v3d_align; + bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); + const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && + (tgpi->rv3d->persp == RV3D_CAMOB) && (!is_depth); + + 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 = tgpi->points; + + if (tgpi->tot_edges > 1) { + switch (tgpi->type) { + case GP_STROKE_BOX: + gp_primitive_rectangle(tgpi, points2D); + break; + case GP_STROKE_LINE: + gp_primitive_line(tgpi, points2D); + break; + case GP_STROKE_CIRCLE: + gp_primitive_circle(tgpi, points2D); + break; + case GP_STROKE_ARC: + gp_primitive_arc(tgpi, points2D); + 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]; + + /* set rnd value for reuse */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (p2d->rnd_dirty != true)) { + p2d->rnd[0] = BLI_rng_get_float(tgpi->rng); + p2d->rnd[1] = BLI_rng_get_float(tgpi->rng); + p2d->rnd[2] = BLI_rng_get_float(tgpi->rng); + p2d->rnd_dirty = true; + } + + /* 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; + } + + /* exponential value */ + const float exfactor = SQUARE(brush->gpencil_settings->draw_jitter + 2.0f); + const float fac = p2d->rnd[0] * exfactor * jitter; + + /* vector */ + float mvec[2], svec[2]; + if (i > 0) { + mvec[0] = (p2d->x - (p2d - 1)->x); + mvec[1] = (p2d->y - (p2d - 1)->y); + normalize_v2(mvec); + } + else { + zero_v2(mvec); + } + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + if (p2d->rnd[1] > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + add_v2_v2(&p2d->x, svec); + } + + /* apply randomness to pressure */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_press > 0.0f)) { + if (p2d->rnd[0] > 0.5f) { + pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[1]; + } + else { + pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[2]; + } + } + + /* 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)) { + if (p2d->rnd[2] > 0.5f) { + strength -= strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[0]; + } + else { + strength += strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1]; + } + 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; + + /* point uv */ + if (gpd->runtime.sbuffer_size > 0) { + MaterialGPencilStyle *gp_style = tgpi->mat->gp_style; + const float pixsize = gp_style->texture_pixsize / 1000000.0f; + tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + bGPDspoint spt, spt2; + + /* get origin to reproject point */ + float origin[3]; + ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); + /* reproject current */ + ED_gpencil_tpoint_to_point(tgpi->ar, origin, tpt, &spt); + ED_gp_project_point_to_plane( + tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt); + + /* reproject previous */ + ED_gpencil_tpoint_to_point(tgpi->ar, origin, tptb, &spt2); + ED_gp_project_point_to_plane( + tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2); + tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + tpt->uv_fac = tgpi->totpixlen; + if ((gp_style) && (gp_style->sima)) { + tpt->uv_fac /= gp_style->sima->gen_x; + } + } + else { + tgpi->totpixlen = 0.0f; + tpt->uv_fac = 0.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, depth_arr ? depth_arr + i : NULL, &pt->x); + + pt->pressure = pressure; + pt->strength = strength; + pt->time = 0.0f; + pt->flag = 0; + pt->uv_fac = tpt->uv_fac; + + 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); + } + + /* 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); + ED_gp_project_stroke_to_plane( + tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1); + } + + /* if parented change position relative to parent object */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt); + } + + /* if camera view, reproject flat to view to avoid perspective effect */ + if (is_camera) { + ED_gpencil_project_stroke_to_view(C, tgpi->gpl, gps); + } + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + 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); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } /* Update screen and stroke */ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi) { - /* update indicator in header */ - gpencil_primitive_status_indicators(C, tgpi); - /* apply... */ - tgpi->type = RNA_enum_get(op->ptr, "type"); - tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); - /* update points position */ - gp_primitive_update_strokes(C, tgpi); + /* update indicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + /* apply... */ + tgpi->type = RNA_enum_get(op->ptr, "type"); + tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); + /* update points position */ + gp_primitive_update_strokes(C, tgpi); } static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { - 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); + 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 */ static void gpencil_primitive_exit(bContext *C, wmOperator *op) { - tGPDprimitive *tgpi = op->customdata; - bGPdata *gpd = tgpi->gpd; - - /* don't assume that operator data exists at all */ - if (tgpi) { - /* 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_SAFE_FREE(tgpi->gpf); - - /* free random seed */ - if (tgpi->rng != NULL) { - BLI_rng_free(tgpi->rng); - } - - MEM_freeN(tgpi); - } - - /* 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 */ - op->customdata = NULL; + tGPDprimitive *tgpi = op->customdata; + bGPdata *gpd = tgpi->gpd; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* 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_SAFE_FREE(tgpi->gpf); + + /* free random seed */ + if (tgpi->rng != NULL) { + BLI_rng_free(tgpi->rng); + } + + MEM_freeN(tgpi); + } + + /* 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 */ + op->customdata = NULL; } /* Init new temporary primitive data */ static void gpencil_primitive_init(bContext *C, wmOperator *op) { - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - /* create temporary operator data */ - 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); - tgpi->ar = CTX_wm_region(C); - tgpi->rv3d = tgpi->ar->regiondata; - tgpi->v3d = tgpi->sa->spacedata.first; - 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_object_material_ensure_from_active_input_toolsettings(bmain, tgpi->ob, ts); - - /* 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 */ - 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); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + /* create temporary operator data */ + 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); + tgpi->ar = CTX_wm_region(C); + tgpi->rv3d = tgpi->ar->regiondata; + tgpi->v3d = tgpi->sa->spacedata.first; + 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_object_material_ensure_from_active_input_toolsettings( + bmain, tgpi->ob, ts); + + /* 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 */ + 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) { - wmWindow *win = CTX_wm_window(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - tGPDprimitive *tgpi = NULL; + wmWindow *win = CTX_wm_window(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + tGPDprimitive *tgpi = NULL; - /* initialize operator runtime data */ - gpencil_primitive_init(C, op); - tgpi = op->customdata; + /* initialize operator runtime data */ + gpencil_primitive_init(C, op); + tgpi = op->customdata; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - if (!is_modal) { - tgpi->flag = IN_PROGRESS; - gpencil_primitive_interaction_begin(tgpi, event); - } + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + if (!is_modal) { + tgpi->flag = IN_PROGRESS; + gpencil_primitive_interaction_begin(tgpi, event); + } - /* if in tools region, wait till we get to the main (3d-space) - * region before allowing drawing to take place. - */ - op->flag |= OP_IS_MODAL_CURSOR_REGION; + /* if in tools region, wait till we get to the main (3d-space) + * region before allowing drawing to take place. + */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; - /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_CROSSCURSOR); + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_CROSSCURSOR); - /* update sindicator in header */ - gpencil_primitive_status_indicators(C, tgpi); - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* update sindicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - /* add a modal handler for this operator */ - WM_event_add_modal_handler(C, op); + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } /* Helper to complete a primitive */ -static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWindow *win, tGPDprimitive *tgpi) +static void gpencil_primitive_interaction_end(bContext *C, + wmOperator *op, + wmWindow *win, + tGPDprimitive *tgpi) { - bGPDframe *gpf; - bGPDstroke *gps; - - ToolSettings *ts = tgpi->scene->toolsettings; - Brush *brush = tgpi->brush; - - const int def_nr = tgpi->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr); - - /* return to normal cursor and header status */ - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* insert keyframes as required... */ - short add_frame_mode; - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - add_frame_mode = GP_GETFRAME_ADD_COPY; - } - else { - add_frame_mode = GP_GETFRAME_ADD_NEW; - } - - gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode); - - /* prepare stroke to get transferred */ - gps = tgpi->gpf->strokes.first; - if (gps) { - gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - - /* calculate UVs along the stroke */ - ED_gpencil_calc_stroke_uv(tgpi->ob, gps); - } - - /* transfer stroke from temporary buffer to the actual frame */ - if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) { - BLI_movelisttolist_reverse(&gpf->strokes, &tgpi->gpf->strokes); - } - else { - BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes); - } - BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes)); - - /* add weights if required */ - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - for (int i = 0; i < gps->totpoints; i++) { - MDeformVert *ve = &gps->dvert[i]; - MDeformWeight *dw = defvert_verify_index(ve, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - } - - DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - /* clean up temp data */ - gpencil_primitive_exit(C, op); + bGPDframe *gpf; + bGPDstroke *gps; + + ToolSettings *ts = tgpi->scene->toolsettings; + Brush *brush = tgpi->brush; + + const int def_nr = tgpi->ob->actdef - 1; + const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr); + + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + short add_frame_mode; + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } + + gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode); + + /* prepare stroke to get transferred */ + gps = tgpi->gpf->strokes.first; + if (gps) { + gps->thickness = brush->size; + gps->gradient_f = brush->gpencil_settings->gradient_f; + copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + + /* calculate UVs along the stroke */ + ED_gpencil_calc_stroke_uv(tgpi->ob, gps); + } + + /* transfer stroke from temporary buffer to the actual frame */ + if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) { + BLI_movelisttolist_reverse(&gpf->strokes, &tgpi->gpf->strokes); + } + else { + BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes); + } + BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes)); + + /* add weights if required */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { + BKE_gpencil_dvert_ensure(gps); + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *ve = &gps->dvert[i]; + MDeformWeight *dw = defvert_verify_index(ve, def_nr); + if (dw) { + dw->weight = ts->vgroup_weight; + } + } + } + + DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); } /* edit event handling */ -static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi) +static void gpencil_primitive_edit_event_handling( + bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi) { - /* 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 (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) && - (ELEM(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; - } - } + /* 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 (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) && (ELEM(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; + } + } } /* brush strength */ static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset) { - Brush *brush = tgpi->brush; - if (brush) { - if (reset) { - brush->gpencil_settings->draw_strength = tgpi->brush_strength; - tgpi->brush_strength = 0.0f; - } - else { - if (tgpi->brush_strength == 0.0f) { - tgpi->brush_strength = brush->gpencil_settings->draw_strength; - } - float move[2]; - sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); - float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f; - brush->gpencil_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); - } - - /* limit low limit because below 0.2f the stroke is invisible */ - CLAMP(brush->gpencil_settings->draw_strength, 0.2f, 1.0f); - } + Brush *brush = tgpi->brush; + if (brush) { + if (reset) { + brush->gpencil_settings->draw_strength = tgpi->brush_strength; + tgpi->brush_strength = 0.0f; + } + else { + if (tgpi->brush_strength == 0.0f) { + tgpi->brush_strength = brush->gpencil_settings->draw_strength; + } + float move[2]; + sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); + float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f; + brush->gpencil_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); + } + + /* limit low limit because below 0.2f the stroke is invisible */ + CLAMP(brush->gpencil_settings->draw_strength, 0.2f, 1.0f); + } } /* brush size */ static void gpencil_primitive_size(tGPDprimitive *tgpi, bool reset) { - Brush *brush = tgpi->brush; - if (brush) { - if (reset) { - brush->size = tgpi->brush_size; - tgpi->brush_size = 0; - } - else { - if (tgpi->brush_size == 0) { - tgpi->brush_size = brush->size; - } - float move[2]; - sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); - int adjust = (move[1] > 0.0f) ? 1 : -1; - brush->size += adjust * (int)fabsf(len_manhattan_v2(move)); - } - CLAMP_MIN(brush->size, 1); - } + Brush *brush = tgpi->brush; + if (brush) { + if (reset) { + brush->size = tgpi->brush_size; + tgpi->brush_size = 0; + } + else { + if (tgpi->brush_size == 0) { + tgpi->brush_size = brush->size; + } + float move[2]; + sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); + int adjust = (move[1] > 0.0f) ? 1 : -1; + brush->size += adjust * (int)fabsf(len_manhattan_v2(move)); + } + CLAMP_MIN(brush->size, 1); + } } /* move */ static void gpencil_primitive_move(tGPDprimitive *tgpi, bool reset) { - float move[2]; - zero_v2(move); - - if (reset) { - sub_v2_v2(move, tgpi->move); - zero_v2(tgpi->move); - } - else { - sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); - add_v2_v2(tgpi->move, move); - } - - 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); + float move[2]; + zero_v2(move); + + if (reset) { + sub_v2_v2(move, tgpi->move); + zero_v2(tgpi->move); + } + else { + sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); + add_v2_v2(tgpi->move, move); + } + + 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) { - tGPDprimitive *tgpi = op->customdata; - 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, false); - gpencil_primitive_update(C, op, tgpi); - break; - } - case ESCKEY: - case LEFTMOUSE: - { - zero_v2(tgpi->move); - tgpi->flag = IN_CURVE_EDIT; - break; - } - case RIGHTMOUSE: - { - if (event->val == KM_RELEASE) { - tgpi->flag = IN_CURVE_EDIT; - gpencil_primitive_move(tgpi, true); - gpencil_primitive_update(C, op, tgpi); - } - break; - } - } - copy_v2_v2(tgpi->mvalo, tgpi->mval); - return OPERATOR_RUNNING_MODAL; - } - else if (tgpi->flag == IN_BRUSH_SIZE) { - switch (event->type) { - case MOUSEMOVE: - gpencil_primitive_size(tgpi, false); - gpencil_primitive_update(C, op, tgpi); - break; - case ESCKEY: - case MIDDLEMOUSE: - case LEFTMOUSE: - tgpi->brush_size = 0; - tgpi->flag = IN_CURVE_EDIT; - break; - case RIGHTMOUSE: - if (event->val == KM_RELEASE) { - tgpi->flag = IN_CURVE_EDIT; - gpencil_primitive_size(tgpi, true); - gpencil_primitive_update(C, op, tgpi); - } - break; - } - copy_v2_v2(tgpi->mvalo, tgpi->mval); - return OPERATOR_RUNNING_MODAL; - } - else if (tgpi->flag == IN_BRUSH_STRENGTH) { - switch (event->type) { - case MOUSEMOVE: - gpencil_primitive_strength(tgpi, false); - gpencil_primitive_update(C, op, tgpi); - break; - case ESCKEY: - case MIDDLEMOUSE: - case LEFTMOUSE: - tgpi->brush_strength = 0.0f; - tgpi->flag = IN_CURVE_EDIT; - break; - case RIGHTMOUSE: - if (event->val == KM_RELEASE) { - tgpi->flag = IN_CURVE_EDIT; - gpencil_primitive_strength(tgpi, true); - gpencil_primitive_update(C, op, tgpi); - } - 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); - /* done! */ - return OPERATOR_FINISHED; - } - else { - if (G.debug & G_DEBUG) { - printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); - } - } - break; - } - case SPACEKEY: /* confirm */ - case MIDDLEMOUSE: - case RETKEY: - { - tgpi->flag = IDLE; - gpencil_primitive_interaction_end(C, op, win, tgpi); - /* done! */ - return OPERATOR_FINISHED; - } - case RIGHTMOUSE: - { - /* exception to cancel current stroke when we have previous strokes in buffer */ - if (tgpi->tot_stored_edges > 0) { - tgpi->flag = IDLE; - tgpi->tot_edges = 0; - gp_primitive_update_strokes(C, tgpi); - gpencil_primitive_interaction_end(C, op, win, tgpi); - /* done! */ - return OPERATOR_FINISHED; - } - ATTR_FALLTHROUGH; - } - case ESCKEY: - { - /* return to normal cursor and header status */ - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* clean up temp data */ - gpencil_primitive_exit(C, op); - - /* canceled! */ - return OPERATOR_CANCELLED; - } - case PADPLUSKEY: - case WHEELUPMOUSE: - { - 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 PADMINUS: - case WHEELDOWNMOUSE: - { - 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 GKEY: /* grab mode */ - { - if ((event->val == KM_PRESS)) { - tgpi->flag = IN_MOVE; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); - } - break; - } - case FKEY: /* brush thickness/ brush strength */ - { - if ((event->val == KM_PRESS)) { - if (event->shift) { - tgpi->flag = IN_BRUSH_STRENGTH; - } - else { - tgpi->flag = IN_BRUSH_SIZE; - } - WM_cursor_modal_set(win, BC_NS_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; - } - - RNA_enum_set(op->ptr, "type", tgpi->type); - gp_primitive_update_cps(tgpi); - gpencil_primitive_update(C, op, tgpi); - } - break; - } - case TABKEY: - { - 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 */ - copy_v2_v2(tgpi->end, tgpi->mval); - copy_v2_v2(tgpi->start, tgpi->origin); - if (tgpi->flag == IDLE) { - copy_v2_v2(tgpi->origin, tgpi->mval); - } - /* Keep square if shift key */ - if (event->shift) { - 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->end[0] = tgpi->origin[0]; - } - else { - gpencil_primitive_to_square(tgpi, x, y); - } - } - else { - gpencil_primitive_to_square(tgpi, x, y); - } - } - /* Center primitive if alt key */ - if (event->alt) { - 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); - } - break; - } - default: - { - 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) */ - value = tgpi->tot_edges; - applyNumInput(&tgpi->num, &value); - tgpi->tot_edges = value; - - 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; - } - else { - /* unhandled event - allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; - } - } - } - - copy_v2_v2(tgpi->mvalo, tgpi->mval); - /* still running... */ - return OPERATOR_RUNNING_MODAL; + tGPDprimitive *tgpi = op->customdata; + 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, false); + gpencil_primitive_update(C, op, tgpi); + break; + } + case ESCKEY: + case LEFTMOUSE: { + zero_v2(tgpi->move); + tgpi->flag = IN_CURVE_EDIT; + break; + } + case RIGHTMOUSE: { + if (event->val == KM_RELEASE) { + tgpi->flag = IN_CURVE_EDIT; + gpencil_primitive_move(tgpi, true); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + } + copy_v2_v2(tgpi->mvalo, tgpi->mval); + return OPERATOR_RUNNING_MODAL; + } + else if (tgpi->flag == IN_BRUSH_SIZE) { + switch (event->type) { + case MOUSEMOVE: + gpencil_primitive_size(tgpi, false); + gpencil_primitive_update(C, op, tgpi); + break; + case ESCKEY: + case MIDDLEMOUSE: + case LEFTMOUSE: + tgpi->brush_size = 0; + tgpi->flag = IN_CURVE_EDIT; + break; + case RIGHTMOUSE: + if (event->val == KM_RELEASE) { + tgpi->flag = IN_CURVE_EDIT; + gpencil_primitive_size(tgpi, true); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + copy_v2_v2(tgpi->mvalo, tgpi->mval); + return OPERATOR_RUNNING_MODAL; + } + else if (tgpi->flag == IN_BRUSH_STRENGTH) { + switch (event->type) { + case MOUSEMOVE: + gpencil_primitive_strength(tgpi, false); + gpencil_primitive_update(C, op, tgpi); + break; + case ESCKEY: + case MIDDLEMOUSE: + case LEFTMOUSE: + tgpi->brush_strength = 0.0f; + tgpi->flag = IN_CURVE_EDIT; + break; + case RIGHTMOUSE: + if (event->val == KM_RELEASE) { + tgpi->flag = IN_CURVE_EDIT; + gpencil_primitive_strength(tgpi, true); + gpencil_primitive_update(C, op, tgpi); + } + 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); + /* done! */ + return OPERATOR_FINISHED; + } + else { + if (G.debug & G_DEBUG) { + printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); + } + } + break; + } + case SPACEKEY: /* confirm */ + case MIDDLEMOUSE: + case RETKEY: { + tgpi->flag = IDLE; + gpencil_primitive_interaction_end(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + case RIGHTMOUSE: { + /* exception to cancel current stroke when we have previous strokes in buffer */ + if (tgpi->tot_stored_edges > 0) { + tgpi->flag = IDLE; + tgpi->tot_edges = 0; + gp_primitive_update_strokes(C, tgpi); + gpencil_primitive_interaction_end(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + ATTR_FALLTHROUGH; + } + case ESCKEY: { + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + case PADPLUSKEY: + case WHEELUPMOUSE: { + 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 PADMINUS: + case WHEELDOWNMOUSE: { + 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 GKEY: /* grab mode */ + { + if ((event->val == KM_PRESS)) { + tgpi->flag = IN_MOVE; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + } + break; + } + case FKEY: /* brush thickness/ brush strength */ + { + if ((event->val == KM_PRESS)) { + if (event->shift) { + tgpi->flag = IN_BRUSH_STRENGTH; + } + else { + tgpi->flag = IN_BRUSH_SIZE; + } + WM_cursor_modal_set(win, BC_NS_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; + } + + RNA_enum_set(op->ptr, "type", tgpi->type); + gp_primitive_update_cps(tgpi); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case TABKEY: { + 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 */ + copy_v2_v2(tgpi->end, tgpi->mval); + copy_v2_v2(tgpi->start, tgpi->origin); + if (tgpi->flag == IDLE) { + copy_v2_v2(tgpi->origin, tgpi->mval); + } + /* Keep square if shift key */ + if (event->shift) { + 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->end[0] = tgpi->origin[0]; + } + else { + gpencil_primitive_to_square(tgpi, x, y); + } + } + else { + gpencil_primitive_to_square(tgpi, x, y); + } + } + /* Center primitive if alt key */ + if (event->alt) { + 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); + } + break; + } + default: { + 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) */ + value = tgpi->tot_edges; + applyNumInput(&tgpi->num, &value); + tgpi->tot_edges = value; + + 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; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + copy_v2_v2(tgpi->mvalo, tgpi->mval); + /* still running... */ + return OPERATOR_RUNNING_MODAL; } /* Cancel handler */ static void gpencil_primitive_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_primitive_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_primitive_exit(C, op); } void GPENCIL_OT_primitive(wmOperatorType *ot) { - static EnumPropertyItem primitive_type[] = { - {GP_STROKE_BOX, "BOX", 0, "Box", ""}, - {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}, - }; - - /* identifiers */ - ot->name = "Grease Pencil Shapes"; - ot->idname = "GPENCIL_OT_primitive"; - ot->description = "Create predefined grease pencil stroke shapes"; - - /* callbacks */ - ot->invoke = gpencil_primitive_invoke; - ot->modal = gpencil_primitive_modal; - ot->cancel = gpencil_primitive_cancel; - ot->poll = gpencil_primitive_add_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - PropertyRNA *prop; - - 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); + static EnumPropertyItem primitive_type[] = { + {GP_STROKE_BOX, "BOX", 0, "Box", ""}, + {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}, + }; + + /* identifiers */ + ot->name = "Grease Pencil Shapes"; + ot->idname = "GPENCIL_OT_primitive"; + ot->description = "Create predefined grease pencil stroke shapes"; + + /* callbacks */ + ot->invoke = gpencil_primitive_invoke; + ot->modal = gpencil_primitive_modal; + ot->cancel = gpencil_primitive_cancel; + ot->poll = gpencil_primitive_add_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + + 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); } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 99bbf10cdc1..ef7a198111b 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -69,16 +69,16 @@ static bool gpencil_select_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); - /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ - if (GPENCIL_ANY_MODE(gpd)) { - /* TODO: include a check for visible strokes? */ - if (gpd->layers.first) - return true; - } + /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ + if (GPENCIL_ANY_MODE(gpd)) { + /* TODO: include a check for visible strokes? */ + if (gpd->layers.first) + return true; + } - return false; + return false; } /** \} */ @@ -89,47 +89,47 @@ static bool gpencil_select_poll(bContext *C) static int gpencil_select_all_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - int action = RNA_enum_get(op->ptr, "action"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + int action = RNA_enum_get(op->ptr, "action"); - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } - ED_gpencil_select_toggle_all(C, action); + ED_gpencil_select_toggle_all(C, action); - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "(De)select All Strokes"; - ot->idname = "GPENCIL_OT_select_all"; - ot->description = "Change selection of all Grease Pencil strokes currently visible"; + /* identifiers */ + ot->name = "(De)select All Strokes"; + ot->idname = "GPENCIL_OT_select_all"; + ot->description = "Change selection of all Grease Pencil strokes currently visible"; - /* callbacks */ - ot->exec = gpencil_select_all_exec; - ot->poll = gpencil_select_poll; + /* callbacks */ + ot->exec = gpencil_select_all_exec; + ot->poll = gpencil_select_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_select_all(ot); + WM_operator_properties_select_all(ot); } /** \} */ @@ -140,56 +140,55 @@ void GPENCIL_OT_select_all(wmOperatorType *ot) static int gpencil_select_linked_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - /* select all points in selected strokes */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag |= GP_SPOINT_SELECT; - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* select all points in selected strokes */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_linked(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Linked"; - ot->idname = "GPENCIL_OT_select_linked"; - ot->description = "Select all points in same strokes as already selected points"; + /* identifiers */ + ot->name = "Select Linked"; + ot->idname = "GPENCIL_OT_select_linked"; + ot->description = "Select all points in same strokes as already selected points"; - /* callbacks */ - ot->exec = gpencil_select_linked_exec; - ot->poll = gpencil_select_poll; + /* callbacks */ + ot->exec = gpencil_select_linked_exec; + ot->poll = gpencil_select_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -200,80 +199,83 @@ void GPENCIL_OT_select_linked(wmOperatorType *ot) static int gpencil_select_alternate_exec(bContext *C, wmOperator *op) { - const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); - bGPdata *gpd = ED_gpencil_data_get_active(C); - - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - /* select all points in selected strokes */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) { - bGPDspoint *pt; - int row = 0; - int start = 0; - if (unselect_ends) { - start = 1; - } - - for (int i = start; i < gps->totpoints; i++) { - pt = &gps->points[i]; - if ((row % 2) == 0) { - pt->flag |= GP_SPOINT_SELECT; - } - else { - pt->flag &= ~GP_SPOINT_SELECT; - } - row++; - } - - /* unselect start and end points */ - if (unselect_ends) { - pt = &gps->points[0]; - pt->flag &= ~GP_SPOINT_SELECT; - - pt = &gps->points[gps->totpoints - 1]; - pt->flag &= ~GP_SPOINT_SELECT; - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* select all points in selected strokes */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) { + bGPDspoint *pt; + int row = 0; + int start = 0; + if (unselect_ends) { + start = 1; + } + + for (int i = start; i < gps->totpoints; i++) { + pt = &gps->points[i]; + if ((row % 2) == 0) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + row++; + } + + /* unselect start and end points */ + if (unselect_ends) { + pt = &gps->points[0]; + pt->flag &= ~GP_SPOINT_SELECT; + + pt = &gps->points[gps->totpoints - 1]; + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_alternate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Alternated"; - ot->idname = "GPENCIL_OT_select_alternate"; - ot->description = "Select alternative points in same strokes as already selected points"; - - /* callbacks */ - ot->exec = gpencil_select_alternate_exec; - ot->poll = gpencil_select_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "unselect_ends", true, "Unselect Ends", "Do not select the first and last point of the stroke"); + /* identifiers */ + ot->name = "Alternated"; + ot->idname = "GPENCIL_OT_select_alternate"; + ot->description = "Select alternative points in same strokes as already selected points"; + + /* callbacks */ + ot->exec = gpencil_select_alternate_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "unselect_ends", + true, + "Unselect Ends", + "Do not select the first and last point of the stroke"); } /** \} */ @@ -283,14 +285,14 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot) * \{ */ typedef enum eGP_SelectGrouped { - /* Select strokes in the same layer */ - GP_SEL_SAME_LAYER = 0, + /* Select strokes in the same layer */ + GP_SEL_SAME_LAYER = 0, - /* Select strokes with the same color */ - GP_SEL_SAME_MATERIAL = 1, + /* Select strokes with the same color */ + GP_SEL_SAME_MATERIAL = 1, - /* TODO: All with same prefix - Useful for isolating all layers for a particular character for instance */ - /* TODO: All with same appearance - colour/opacity/volumetric/fills ? */ + /* TODO: All with same prefix - Useful for isolating all layers for a particular character for instance */ + /* TODO: All with same appearance - colour/opacity/volumetric/fills ? */ } eGP_SelectGrouped; /* ----------------------------------- */ @@ -298,146 +300,143 @@ typedef enum eGP_SelectGrouped { /* On each visible layer, check for selected strokes - if found, select all others */ static void gp_select_same_layer(bContext *C) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - bGPDstroke *gps; - bool found = false; - - if (gpf == NULL) - continue; - - /* Search for a selected stroke */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - if (gps->flag & GP_STROKE_SELECT) { - found = true; - break; - } - } - } - - /* Select all if found */ - if (found) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag |= GP_SPOINT_SELECT; - } - - gps->flag |= GP_STROKE_SELECT; - } - } - } - } - CTX_DATA_END; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + bGPDstroke *gps; + bool found = false; + + if (gpf == NULL) + continue; + + /* Search for a selected stroke */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + if (gps->flag & GP_STROKE_SELECT) { + found = true; + break; + } + } + } + + /* Select all if found */ + if (found) { + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + + gps->flag |= GP_STROKE_SELECT; + } + } + } + } + CTX_DATA_END; } /* Select all strokes with same colors as selected ones */ static void gp_select_same_material(bContext *C) { - /* First, build set containing all the colors of selected strokes */ - GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - /* add instead of insert here, otherwise the uniqueness check gets skipped, - * and we get many duplicate entries... - */ - BLI_gset_add(selected_colors, &gps->mat_nr); - } - } - CTX_DATA_END; - - /* Second, select any visible stroke that uses these colors */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) { - /* select this stroke */ - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag |= GP_SPOINT_SELECT; - } - - gps->flag |= GP_STROKE_SELECT; - } - } - CTX_DATA_END; - - /* free memomy */ - if (selected_colors != NULL) { - BLI_gset_free(selected_colors, NULL); - } + /* First, build set containing all the colors of selected strokes */ + GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + /* add instead of insert here, otherwise the uniqueness check gets skipped, + * and we get many duplicate entries... + */ + BLI_gset_add(selected_colors, &gps->mat_nr); + } + } + CTX_DATA_END; + + /* Second, select any visible stroke that uses these colors */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) { + /* select this stroke */ + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + + gps->flag |= GP_STROKE_SELECT; + } + } + CTX_DATA_END; + + /* free memomy */ + if (selected_colors != NULL) { + BLI_gset_free(selected_colors, NULL); + } } - /* ----------------------------------- */ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) { - eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type"); - bGPdata *gpd = ED_gpencil_data_get_active(C); - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - switch (mode) { - case GP_SEL_SAME_LAYER: - gp_select_same_layer(C); - break; - case GP_SEL_SAME_MATERIAL: - gp_select_same_material(C); - break; - - default: - BLI_assert(!"unhandled select grouped gpencil mode"); - break; - } - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + switch (mode) { + case GP_SEL_SAME_LAYER: + gp_select_same_layer(C); + break; + case GP_SEL_SAME_MATERIAL: + gp_select_same_material(C); + break; + + default: + BLI_assert(!"unhandled select grouped gpencil mode"); + break; + } + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_grouped(wmOperatorType *ot) { - static const EnumPropertyItem prop_select_grouped_types[] = { - {GP_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, - {GP_SEL_SAME_MATERIAL, "MATERIAL", 0, "Material", "Shared materials"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Grouped"; - ot->idname = "GPENCIL_OT_select_grouped"; - ot->description = "Select all strokes with similar characteristics"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gpencil_select_grouped_exec; - ot->poll = gpencil_select_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, GP_SEL_SAME_LAYER, "Type", ""); + static const EnumPropertyItem prop_select_grouped_types[] = { + {GP_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, + {GP_SEL_SAME_MATERIAL, "MATERIAL", 0, "Material", "Shared materials"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Select Grouped"; + ot->idname = "GPENCIL_OT_select_grouped"; + ot->description = "Select all strokes with similar characteristics"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gpencil_select_grouped_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "type", prop_select_grouped_types, GP_SEL_SAME_LAYER, "Type", ""); } /** \} */ @@ -448,71 +447,77 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) static int gpencil_select_first_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* skip stroke if we're only manipulating selected strokes */ - if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { - continue; - } - - /* select first point */ - BLI_assert(gps->totpoints >= 1); - - gps->points->flag |= GP_SPOINT_SELECT; - gps->flag |= GP_STROKE_SELECT; - - /* deselect rest? */ - if ((extend == false) && (gps->totpoints > 1)) { - /* start from index 1, to skip the first point that we'd just selected... */ - bGPDspoint *pt = &gps->points[1]; - int i = 1; - - for (; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* skip stroke if we're only manipulating selected strokes */ + if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { + continue; + } + + /* select first point */ + BLI_assert(gps->totpoints >= 1); + + gps->points->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + + /* deselect rest? */ + if ((extend == false) && (gps->totpoints > 1)) { + /* start from index 1, to skip the first point that we'd just selected... */ + bGPDspoint *pt = &gps->points[1]; + int i = 1; + + for (; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_first(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select First"; - ot->idname = "GPENCIL_OT_select_first"; - ot->description = "Select first point in Grease Pencil strokes"; - - /* callbacks */ - ot->exec = gpencil_select_first_exec; - ot->poll = gpencil_select_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "only_selected_strokes", false, "Selected Strokes Only", - "Only select the first point of strokes that already have points selected"); - - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting all other selected points"); + /* identifiers */ + ot->name = "Select First"; + ot->idname = "GPENCIL_OT_select_first"; + ot->description = "Select first point in Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_select_first_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "only_selected_strokes", + false, + "Selected Strokes Only", + "Only select the first point of strokes that already have points selected"); + + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting all other selected points"); } /** \} */ @@ -523,71 +528,77 @@ void GPENCIL_OT_select_first(wmOperatorType *ot) static int gpencil_select_last_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* skip stroke if we're only manipulating selected strokes */ - if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { - continue; - } - - /* select last point */ - BLI_assert(gps->totpoints >= 1); - - gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT; - gps->flag |= GP_STROKE_SELECT; - - /* deselect rest? */ - if ((extend == false) && (gps->totpoints > 1)) { - /* don't include the last point... */ - bGPDspoint *pt = gps->points; - int i = 1; - - for (; i < gps->totpoints - 1; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* skip stroke if we're only manipulating selected strokes */ + if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { + continue; + } + + /* select last point */ + BLI_assert(gps->totpoints >= 1); + + gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + + /* deselect rest? */ + if ((extend == false) && (gps->totpoints > 1)) { + /* don't include the last point... */ + bGPDspoint *pt = gps->points; + int i = 1; + + for (; i < gps->totpoints - 1; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_last(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Last"; - ot->idname = "GPENCIL_OT_select_last"; - ot->description = "Select last point in Grease Pencil strokes"; - - /* callbacks */ - ot->exec = gpencil_select_last_exec; - ot->poll = gpencil_select_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "only_selected_strokes", false, "Selected Strokes Only", - "Only select the last point of strokes that already have points selected"); - - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting all other selected points"); + /* identifiers */ + ot->name = "Select Last"; + ot->idname = "GPENCIL_OT_select_last"; + ot->description = "Select last point in Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_select_last_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "only_selected_strokes", + false, + "Selected Strokes Only", + "Only select the last point of strokes that already have points selected"); + + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting all other selected points"); } /** \} */ @@ -598,81 +609,80 @@ void GPENCIL_OT_select_last(wmOperatorType *ot) static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - bool prev_sel; - - /* First Pass: Go in forward order, expanding selection if previous was selected (pre changes)... - * - This pass covers the "after" edges of selection islands - */ - prev_sel = false; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* selected point - just set flag for next point */ - prev_sel = true; - } - else { - /* unselected point - expand selection if previous was selected... */ - if (prev_sel) { - pt->flag |= GP_SPOINT_SELECT; - } - prev_sel = false; - } - } - - /* Second Pass: Go in reverse order, doing the same as before (except in opposite order) - * - This pass covers the "before" edges of selection islands - */ - prev_sel = false; - for (pt -= 1; i > 0; i--, pt--) { - if (pt->flag & GP_SPOINT_SELECT) { - prev_sel = true; - } - else { - /* unselected point - expand selection if previous was selected... */ - if (prev_sel) { - pt->flag |= GP_SPOINT_SELECT; - } - prev_sel = false; - } - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + bool prev_sel; + + /* First Pass: Go in forward order, expanding selection if previous was selected (pre changes)... + * - This pass covers the "after" edges of selection islands + */ + prev_sel = false; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* selected point - just set flag for next point */ + prev_sel = true; + } + else { + /* unselected point - expand selection if previous was selected... */ + if (prev_sel) { + pt->flag |= GP_SPOINT_SELECT; + } + prev_sel = false; + } + } + + /* Second Pass: Go in reverse order, doing the same as before (except in opposite order) + * - This pass covers the "before" edges of selection islands + */ + prev_sel = false; + for (pt -= 1; i > 0; i--, pt--) { + if (pt->flag & GP_SPOINT_SELECT) { + prev_sel = true; + } + else { + /* unselected point - expand selection if previous was selected... */ + if (prev_sel) { + pt->flag |= GP_SPOINT_SELECT; + } + prev_sel = false; + } + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_more(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select More"; - ot->idname = "GPENCIL_OT_select_more"; - ot->description = "Grow sets of selected Grease Pencil points"; + /* identifiers */ + ot->name = "Select More"; + ot->idname = "GPENCIL_OT_select_more"; + ot->description = "Grow sets of selected Grease Pencil points"; - /* callbacks */ - ot->exec = gpencil_select_more_exec; - ot->poll = gpencil_select_poll; + /* callbacks */ + ot->exec = gpencil_select_more_exec; + ot->poll = gpencil_select_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -683,82 +693,81 @@ void GPENCIL_OT_select_more(wmOperatorType *ot) static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - bool prev_sel; - - /* First Pass: Go in forward order, shrinking selection if previous was not selected (pre changes)... - * - This pass covers the "after" edges of selection islands - */ - prev_sel = false; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* shrink if previous wasn't selected */ - if (prev_sel == false) { - pt->flag &= ~GP_SPOINT_SELECT; - } - prev_sel = true; - } - else { - /* mark previous as being unselected - and hence, is trigger for shrinking */ - prev_sel = false; - } - } - - /* Second Pass: Go in reverse order, doing the same as before (except in opposite order) - * - This pass covers the "before" edges of selection islands - */ - prev_sel = false; - for (pt -= 1; i > 0; i--, pt--) { - if (pt->flag & GP_SPOINT_SELECT) { - /* shrink if previous wasn't selected */ - if (prev_sel == false) { - pt->flag &= ~GP_SPOINT_SELECT; - } - prev_sel = true; - } - else { - /* mark previous as being unselected - and hence, is trigger for shrinking */ - prev_sel = false; - } - } - } - } - CTX_DATA_END; - - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + bool prev_sel; + + /* First Pass: Go in forward order, shrinking selection if previous was not selected (pre changes)... + * - This pass covers the "after" edges of selection islands + */ + prev_sel = false; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* shrink if previous wasn't selected */ + if (prev_sel == false) { + pt->flag &= ~GP_SPOINT_SELECT; + } + prev_sel = true; + } + else { + /* mark previous as being unselected - and hence, is trigger for shrinking */ + prev_sel = false; + } + } + + /* Second Pass: Go in reverse order, doing the same as before (except in opposite order) + * - This pass covers the "before" edges of selection islands + */ + prev_sel = false; + for (pt -= 1; i > 0; i--, pt--) { + if (pt->flag & GP_SPOINT_SELECT) { + /* shrink if previous wasn't selected */ + if (prev_sel == false) { + pt->flag &= ~GP_SPOINT_SELECT; + } + prev_sel = true; + } + else { + /* mark previous as being unselected - and hence, is trigger for shrinking */ + prev_sel = false; + } + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + return OPERATOR_FINISHED; } void GPENCIL_OT_select_less(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Less"; - ot->idname = "GPENCIL_OT_select_less"; - ot->description = "Shrink sets of selected Grease Pencil points"; + /* identifiers */ + ot->name = "Select Less"; + ot->idname = "GPENCIL_OT_select_less"; + ot->description = "Shrink sets of selected Grease Pencil points"; - /* callbacks */ - ot->exec = gpencil_select_less_exec; - ot->poll = gpencil_select_poll; + /* callbacks */ + ot->exec = gpencil_select_less_exec; + ot->poll = gpencil_select_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -771,216 +780,213 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) /* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke() * It would be great to de-duplicate the logic here sometime, but that can wait... */ -static bool gp_stroke_do_circle_sel( - bGPDlayer *gpl, - bGPDstroke *gps, GP_SpaceConversion *gsc, - const int mx, const int my, const int radius, - const bool select, rcti *rect, float diff_mat[4][4], const int selectmode, - const float scale) +static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, + bGPDstroke *gps, + GP_SpaceConversion *gsc, + const int mx, + const int my, + const int radius, + const bool select, + rcti *rect, + float diff_mat[4][4], + const int selectmode, + const float scale) { - bGPDspoint *pt1 = NULL; - bGPDspoint *pt2 = NULL; - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - int i; - bool changed = false; - - if (gps->totpoints == 1) { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); - - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { - /* only check if point is inside */ - if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) { - /* change selection */ - if (select) { - gps->points->flag |= GP_SPOINT_SELECT; - gps->flag |= GP_STROKE_SELECT; - } - else { - gps->points->flag &= ~GP_SPOINT_SELECT; - gps->flag &= ~GP_STROKE_SELECT; - } - - return true; - } - } - } - else { - /* Loop over the points in the stroke, checking for intersections - * - an intersection means that we touched the stroke - */ - bool hit = false; - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* get points to work with */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &x0, &y0); - - gp_point_to_parent_space(pt2, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &x1, &y1); - - /* check that point segment of the boundbox of the selection stroke */ - if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || - ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) - { - float mval[2] = {(float)mx, (float)my}; - float mvalo[2] = {(float)mx, (float)my}; /* dummy - this isn't used... */ - - /* check if point segment of stroke had anything to do with - * eraser region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle(mval, mvalo, radius, x0, y0, x1, y1)) { - /* change selection of stroke, and then of both points - * (as the last point otherwise wouldn't get selected - * as we only do n-1 loops through). - */ - hit = true; - if (select) { - pt1->flag |= GP_SPOINT_SELECT; - pt2->flag |= GP_SPOINT_SELECT; - - changed = true; - } - else { - pt1->flag &= ~GP_SPOINT_SELECT; - pt2->flag &= ~GP_SPOINT_SELECT; - - changed = true; - } - } - } - /* if stroke mode, don't check more points */ - if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { - break; - } - } - - /* if stroke mode expand selection */ - if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { - for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) { - if (select) { - pt1->flag |= GP_SPOINT_SELECT; - } - else { - pt1->flag &= ~GP_SPOINT_SELECT; - } - } - } - - /* expand selection to segment */ - if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select)) { - float r_hita[3], r_hitb[3]; - bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT); - ED_gpencil_select_stroke_segment( - gpl, gps, pt1, hit_select, false, scale, r_hita, r_hitb); - } - - /* Ensure that stroke selection is in sync with its points */ - BKE_gpencil_stroke_sync_selection(gps); - } - - return changed; + bGPDspoint *pt1 = NULL; + bGPDspoint *pt2 = NULL; + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + int i; + bool changed = false; + + if (gps->totpoints == 1) { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); + + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { + /* only check if point is inside */ + if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) { + /* change selection */ + if (select) { + gps->points->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + else { + gps->points->flag &= ~GP_SPOINT_SELECT; + gps->flag &= ~GP_STROKE_SELECT; + } + + return true; + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + bool hit = false; + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x0, &y0); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x1, &y1); + + /* check that point segment of the boundbox of the selection stroke */ + if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || + ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) { + float mval[2] = {(float)mx, (float)my}; + float mvalo[2] = {(float)mx, (float)my}; /* dummy - this isn't used... */ + + /* check if point segment of stroke had anything to do with + * eraser region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle(mval, mvalo, radius, x0, y0, x1, y1)) { + /* change selection of stroke, and then of both points + * (as the last point otherwise wouldn't get selected + * as we only do n-1 loops through). + */ + hit = true; + if (select) { + pt1->flag |= GP_SPOINT_SELECT; + pt2->flag |= GP_SPOINT_SELECT; + + changed = true; + } + else { + pt1->flag &= ~GP_SPOINT_SELECT; + pt2->flag &= ~GP_SPOINT_SELECT; + + changed = true; + } + } + } + /* if stroke mode, don't check more points */ + if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { + break; + } + } + + /* if stroke mode expand selection */ + if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { + for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) { + if (select) { + pt1->flag |= GP_SPOINT_SELECT; + } + else { + pt1->flag &= ~GP_SPOINT_SELECT; + } + } + } + + /* expand selection to segment */ + if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select)) { + float r_hita[3], r_hitb[3]; + bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT); + ED_gpencil_select_stroke_segment(gpl, gps, pt1, hit_select, false, scale, r_hita, r_hitb); + } + + /* Ensure that stroke selection is in sync with its points */ + BKE_gpencil_stroke_sync_selection(gps); + } + + return changed; } - static int gpencil_circle_select_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const int selectmode = ts->gpencil_selectmode; - const float scale = ts->gp_sculpt.isect_threshold; - - /* if not edit/sculpt mode, the event is catched but not processed */ - if (GPENCIL_NONE_EDIT_MODE(gpd)) { - return OPERATOR_CANCELLED; - } - - ScrArea *sa = CTX_wm_area(C); - - const int mx = RNA_int_get(op->ptr, "x"); - const int my = RNA_int_get(op->ptr, "y"); - const int radius = RNA_int_get(op->ptr, "radius"); - - GP_SpaceConversion gsc = {NULL}; - /* for bounding rect around circle (for quicky intersection testing) */ - rcti rect = {0}; - - bool changed = false; - - - /* sanity checks */ - if (sa == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active area"); - return OPERATOR_CANCELLED; - } - - const eSelectOp sel_op = ED_select_op_modal( - RNA_enum_get(op->ptr, "mode"), WM_gesture_is_modal_first(op->customdata)); - const bool select = (sel_op != SEL_OP_SUB); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_gpencil_select_toggle_all(C, SEL_DESELECT); - changed = true; - } - - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); - - - /* rect is rectangle of selection circle */ - rect.xmin = mx - radius; - rect.ymin = my - radius; - rect.xmax = mx + radius; - rect.ymax = my + radius; - - - /* find visible strokes, and select if hit */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - changed |= gp_stroke_do_circle_sel( - gpl, gps, &gsc, mx, my, radius, select, &rect, - gpstroke_iter.diff_mat, selectmode, scale); - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* updates */ - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - } - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const int selectmode = ts->gpencil_selectmode; + const float scale = ts->gp_sculpt.isect_threshold; + + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + ScrArea *sa = CTX_wm_area(C); + + const int mx = RNA_int_get(op->ptr, "x"); + const int my = RNA_int_get(op->ptr, "y"); + const int radius = RNA_int_get(op->ptr, "radius"); + + GP_SpaceConversion gsc = {NULL}; + /* for bounding rect around circle (for quicky intersection testing) */ + rcti rect = {0}; + + bool changed = false; + + /* sanity checks */ + if (sa == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active area"); + return OPERATOR_CANCELLED; + } + + const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), + WM_gesture_is_modal_first(op->customdata)); + const bool select = (sel_op != SEL_OP_SUB); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + ED_gpencil_select_toggle_all(C, SEL_DESELECT); + changed = true; + } + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* rect is rectangle of selection circle */ + rect.xmin = mx - radius; + rect.ymin = my - radius; + rect.xmax = mx + radius; + rect.ymax = my + radius; + + /* find visible strokes, and select if hit */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + changed |= gp_stroke_do_circle_sel( + gpl, gps, &gsc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode, scale); + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* updates */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; } void GPENCIL_OT_select_circle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Circle Select"; - ot->description = "Select Grease Pencil strokes using brush selection"; - ot->idname = "GPENCIL_OT_select_circle"; - - /* callbacks */ - ot->invoke = WM_gesture_circle_invoke; - ot->modal = WM_gesture_circle_modal; - ot->exec = gpencil_circle_select_exec; - ot->poll = gpencil_select_poll; - ot->cancel = WM_gesture_circle_cancel; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - WM_operator_properties_gesture_circle(ot); - WM_operator_properties_select_operation_simple(ot); + /* identifiers */ + ot->name = "Circle Select"; + ot->description = "Select Grease Pencil strokes using brush selection"; + ot->idname = "GPENCIL_OT_select_circle"; + + /* callbacks */ + ot->invoke = WM_gesture_circle_invoke; + ot->modal = WM_gesture_circle_modal; + ot->exec = gpencil_circle_select_exec; + ot->poll = gpencil_select_poll; + ot->cancel = WM_gesture_circle_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + WM_operator_properties_gesture_circle(ot); + WM_operator_properties_select_operation_simple(ot); } /** \} */ @@ -992,135 +998,132 @@ void GPENCIL_OT_select_circle(wmOperatorType *ot) * * \{ */ -typedef bool (*GPencilTestFn)( - bGPDstroke *gps, bGPDspoint *pt, - const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data); +typedef bool (*GPencilTestFn)(bGPDstroke *gps, + bGPDspoint *pt, + const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + void *user_data); -static int gpencil_generic_select_exec( - bContext *C, wmOperator *op, - GPencilTestFn is_inside_fn, void *user_data) +static int gpencil_generic_select_exec(bContext *C, + wmOperator *op, + GPencilTestFn is_inside_fn, + void *user_data) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - ToolSettings *ts = CTX_data_tool_settings(C); - ScrArea *sa = CTX_wm_area(C); - const bool strokemode = ( - (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && - ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); - const bool segmentmode = ( - (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) && - ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - const float scale = ts->gp_sculpt.isect_threshold; - - - GP_SpaceConversion gsc = {NULL}; - - bool changed = false; - - /* sanity checks */ - if (sa == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active area"); - return OPERATOR_CANCELLED; - } - - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); - - /* deselect all strokes first? */ - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - - gps->flag &= ~GP_STROKE_SELECT; - } - CTX_DATA_END; - } - - /* select/deselect points */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - - bGPDspoint *pt; - int i; - bool hit = false; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - /* convert point coords to screenspace */ - const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); - - if (strokemode == false) { - const bool is_select = (pt->flag & GP_SPOINT_SELECT) != 0; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - SET_FLAG_FROM_TEST(pt->flag, sel_op_result, GP_SPOINT_SELECT); - changed = true; - - /* expand selection to segment */ - if ((sel_op_result != -1) && (segmentmode)) { - bool hit_select = (bool)(pt->flag & GP_SPOINT_SELECT); - float r_hita[3], r_hitb[3]; - ED_gpencil_select_stroke_segment( - gpl, gps, pt, hit_select, false, scale, r_hita, r_hitb); - } - - } - } - else { - if (is_inside) { - hit = true; - break; - } - } - } - - /* if stroke mode expand selection */ - if (strokemode) { - const bool is_select = BKE_gpencil_stroke_select_check(gps); - const bool is_inside = hit; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (sel_op_result) { - pt->flag |= GP_SPOINT_SELECT; - } - else { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - changed = true; - } - } - - /* Ensure that stroke selection is in sync with its points */ - BKE_gpencil_stroke_sync_selection(gps); - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* if paint mode,delete selected points */ - if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { - gp_delete_selected_point_wrap(C); - changed = true; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - - /* updates */ - if (changed) { - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - } - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + ScrArea *sa = CTX_wm_area(C); + const bool strokemode = ((ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && + ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); + const bool segmentmode = ((ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) && + ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); + const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + const float scale = ts->gp_sculpt.isect_threshold; + + GP_SpaceConversion gsc = {NULL}; + + bool changed = false; + + /* sanity checks */ + if (sa == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active area"); + return OPERATOR_CANCELLED; + } + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* deselect all strokes first? */ + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + gps->flag &= ~GP_STROKE_SELECT; + } + CTX_DATA_END; + } + + /* select/deselect points */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + + bGPDspoint *pt; + int i; + bool hit = false; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + /* convert point coords to screenspace */ + const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); + + if (strokemode == false) { + const bool is_select = (pt->flag & GP_SPOINT_SELECT) != 0; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(pt->flag, sel_op_result, GP_SPOINT_SELECT); + changed = true; + + /* expand selection to segment */ + if ((sel_op_result != -1) && (segmentmode)) { + bool hit_select = (bool)(pt->flag & GP_SPOINT_SELECT); + float r_hita[3], r_hitb[3]; + ED_gpencil_select_stroke_segment( + gpl, gps, pt, hit_select, false, scale, r_hita, r_hitb); + } + } + } + else { + if (is_inside) { + hit = true; + break; + } + } + } + + /* if stroke mode expand selection */ + if (strokemode) { + const bool is_select = BKE_gpencil_stroke_select_check(gps); + const bool is_inside = hit; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (sel_op_result) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + changed = true; + } + } + + /* Ensure that stroke selection is in sync with its points */ + BKE_gpencil_stroke_sync_selection(gps); + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* if paint mode,delete selected points */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + gp_delete_selected_point_wrap(C); + changed = true; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + + /* updates */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; } /** \} */ @@ -1130,52 +1133,51 @@ static int gpencil_generic_select_exec( * \{ */ struct GP_SelectBoxUserData { - rcti rect; + rcti rect; }; -static bool gpencil_test_box( - bGPDstroke *gps, bGPDspoint *pt, - const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data) +static bool gpencil_test_box(bGPDstroke *gps, + bGPDspoint *pt, + const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + void *user_data) { - const struct GP_SelectBoxUserData *data = user_data; - bGPDspoint pt2; - int x0, y0; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); - return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && - BLI_rcti_isect_pt(&data->rect, x0, y0)); + const struct GP_SelectBoxUserData *data = user_data; + bGPDspoint pt2; + int x0, y0; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); + return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0)); } static int gpencil_box_select_exec(bContext *C, wmOperator *op) { - struct GP_SelectBoxUserData data = {0}; - WM_operator_properties_border_to_rcti(op, &data.rect); - return gpencil_generic_select_exec( - C, op, - gpencil_test_box, &data); + struct GP_SelectBoxUserData data = {0}; + WM_operator_properties_border_to_rcti(op, &data.rect); + return gpencil_generic_select_exec(C, op, gpencil_test_box, &data); } void GPENCIL_OT_select_box(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Box Select"; - ot->description = "Select Grease Pencil strokes within a rectangular region"; - ot->idname = "GPENCIL_OT_select_box"; + /* identifiers */ + ot->name = "Box Select"; + ot->description = "Select Grease Pencil strokes within a rectangular region"; + ot->idname = "GPENCIL_OT_select_box"; - /* callbacks */ - ot->invoke = WM_gesture_box_invoke; - ot->exec = gpencil_box_select_exec; - ot->modal = WM_gesture_box_modal; - ot->cancel = WM_gesture_box_cancel; + /* callbacks */ + ot->invoke = WM_gesture_box_invoke; + ot->exec = gpencil_box_select_exec; + ot->modal = WM_gesture_box_modal; + ot->cancel = WM_gesture_box_cancel; - ot->poll = gpencil_select_poll; + ot->poll = gpencil_select_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - /* properties */ - WM_operator_properties_gesture_box(ot); - WM_operator_properties_select_operation(ot); + /* properties */ + WM_operator_properties_gesture_box(ot); + WM_operator_properties_select_operation(ot); } /** \} */ @@ -1185,67 +1187,65 @@ void GPENCIL_OT_select_box(wmOperatorType *ot) * \{ */ struct GP_SelectLassoUserData { - rcti rect; - const int (*mcords)[2]; - int mcords_len; + rcti rect; + const int (*mcords)[2]; + int mcords_len; }; -static bool gpencil_test_lasso( - bGPDstroke *gps, bGPDspoint *pt, - const GP_SpaceConversion *gsc, const float diff_mat[4][4], - void *user_data) +static bool gpencil_test_lasso(bGPDstroke *gps, + bGPDspoint *pt, + const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + void *user_data) { - const struct GP_SelectLassoUserData *data = user_data; - bGPDspoint pt2; - int x0, y0; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); - /* test if in lasso boundbox + within the lasso noose */ - return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && - BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + const struct GP_SelectLassoUserData *data = user_data; + bGPDspoint pt2; + int x0, y0; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); + /* test if in lasso boundbox + within the lasso noose */ + return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && + BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); } static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) { - struct GP_SelectLassoUserData data = {0}; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + struct GP_SelectLassoUserData data = {0}; + data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); - /* Sanity check. */ - if (data.mcords == NULL) { - return OPERATOR_PASS_THROUGH; - } + /* Sanity check. */ + if (data.mcords == NULL) { + return OPERATOR_PASS_THROUGH; + } - /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + /* Compute boundbox of lasso (for faster testing later). */ + BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); - int ret = gpencil_generic_select_exec( - C, op, - gpencil_test_lasso, &data); + int ret = gpencil_generic_select_exec(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcords); - return ret; + return ret; } void GPENCIL_OT_select_lasso(wmOperatorType *ot) { - ot->name = "Lasso Select Strokes"; - ot->description = "Select Grease Pencil strokes using lasso selection"; - ot->idname = "GPENCIL_OT_select_lasso"; - - ot->invoke = WM_gesture_lasso_invoke; - ot->modal = WM_gesture_lasso_modal; - ot->exec = gpencil_lasso_select_exec; - ot->poll = gpencil_select_poll; - ot->cancel = WM_gesture_lasso_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - WM_operator_properties_select_operation(ot); - WM_operator_properties_gesture_lasso(ot); + ot->name = "Lasso Select Strokes"; + ot->description = "Select Grease Pencil strokes using lasso selection"; + ot->idname = "GPENCIL_OT_select_lasso"; + + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = gpencil_lasso_select_exec; + ot->poll = gpencil_select_poll; + ot->cancel = WM_gesture_lasso_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + WM_operator_properties_select_operation(ot); + WM_operator_properties_gesture_lasso(ot); } /** \} */ @@ -1257,215 +1257,225 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot) /* helper to deselect all selected strokes/points */ static void deselect_all_selected(bContext *C) { - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* deselect stroke and its points if selected */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - /* deselect points */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - - /* deselect stroke itself too */ - gps->flag &= ~GP_STROKE_SELECT; - } - } - CTX_DATA_END; + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* deselect stroke and its points if selected */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + /* deselect points */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + /* deselect stroke itself too */ + gps->flag &= ~GP_STROKE_SELECT; + } + } + CTX_DATA_END; } static int gpencil_select_exec(bContext *C, wmOperator *op) { - ScrArea *sa = CTX_wm_area(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const float scale = ts->gp_sculpt.isect_threshold; - - /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */ - const float radius = 0.50f * U.widget_unit; - const int radius_squared = (int)(radius * radius); - - bool extend = RNA_boolean_get(op->ptr, "extend"); - bool deselect = RNA_boolean_get(op->ptr, "deselect"); - bool toggle = RNA_boolean_get(op->ptr, "toggle"); - bool whole = RNA_boolean_get(op->ptr, "entire_strokes"); - - int mval[2] = {0}; - - GP_SpaceConversion gsc = {NULL}; - - bGPDlayer *hit_layer = NULL; - bGPDstroke *hit_stroke = NULL; - bGPDspoint *hit_point = NULL; - int hit_distance = radius_squared; - - /* sanity checks */ - if (sa == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active area"); - return OPERATOR_CANCELLED; - } - - /* if select mode is stroke, use whole stroke */ - if (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) { - whole = true; - } - - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); - - /* get mouse location */ - RNA_int_get_array(op->ptr, "location", mval); - - /* First Pass: Find stroke point which gets hit */ - /* XXX: maybe we should go from the top of the stack down instead... */ - GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) - { - bGPDspoint *pt; - int i; - - /* firstly, check for hit-point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - int xy[2]; - - bGPDspoint pt2; - gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); - gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); - - /* do boundbox check first */ - if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { - const int pt_distance = len_manhattan_v2v2_int(mval, xy); - - /* check if point is inside */ - if (pt_distance <= radius_squared) { - /* only use this point if it is a better match than the current hit - T44685 */ - if (pt_distance < hit_distance) { - hit_layer = gpl; - hit_stroke = gps; - hit_point = pt; - hit_distance = pt_distance; - } - } - } - } - } - GP_EDITABLE_STROKES_END(gpstroke_iter); - - /* Abort if nothing hit... */ - if (ELEM(NULL, hit_stroke, hit_point)) { - - /* since left mouse select change, deselect all if click outside any hit */ - deselect_all_selected(C); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - - return OPERATOR_CANCELLED; - } - - /* adjust selection behavior - for toggle option */ - if (toggle) { - deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0; - } - - /* If not extending selection, deselect everything else */ - if (extend == false) { - deselect_all_selected(C); - } - - /* Perform selection operations... */ - if (whole) { - bGPDspoint *pt; - int i; - - /* entire stroke's points */ - for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { - if (deselect == false) - pt->flag |= GP_SPOINT_SELECT; - else - pt->flag &= ~GP_SPOINT_SELECT; - } - - /* stroke too... */ - if (deselect == false) - hit_stroke->flag |= GP_STROKE_SELECT; - else - hit_stroke->flag &= ~GP_STROKE_SELECT; - } - else { - /* just the point (and the stroke) */ - if (deselect == false) { - /* we're adding selection, so selection must be true */ - hit_point->flag |= GP_SPOINT_SELECT; - hit_stroke->flag |= GP_STROKE_SELECT; - - /* expand selection to segment */ - if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) { - float r_hita[3], r_hitb[3]; - bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT); - ED_gpencil_select_stroke_segment( - hit_layer, hit_stroke, hit_point, hit_select, - false, scale, r_hita, r_hitb); - } - } - else { - /* deselect point */ - hit_point->flag &= ~GP_SPOINT_SELECT; - - /* ensure that stroke is selected correctly */ - BKE_gpencil_stroke_sync_selection(hit_stroke); - } - } - - /* updates */ - if (hit_point != NULL) { - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); - - /* copy on write tag is needed, or else no refresh happens */ - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - } - - return OPERATOR_FINISHED; + ScrArea *sa = CTX_wm_area(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const float scale = ts->gp_sculpt.isect_threshold; + + /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */ + const float radius = 0.50f * U.widget_unit; + const int radius_squared = (int)(radius * radius); + + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool whole = RNA_boolean_get(op->ptr, "entire_strokes"); + + int mval[2] = {0}; + + GP_SpaceConversion gsc = {NULL}; + + bGPDlayer *hit_layer = NULL; + bGPDstroke *hit_stroke = NULL; + bGPDspoint *hit_point = NULL; + int hit_distance = radius_squared; + + /* sanity checks */ + if (sa == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active area"); + return OPERATOR_CANCELLED; + } + + /* if select mode is stroke, use whole stroke */ + if (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) { + whole = true; + } + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* get mouse location */ + RNA_int_get_array(op->ptr, "location", mval); + + /* First Pass: Find stroke point which gets hit */ + /* XXX: maybe we should go from the top of the stack down instead... */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + bGPDspoint *pt; + int i; + + /* firstly, check for hit-point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + int xy[2]; + + bGPDspoint pt2; + gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); + + /* do boundbox check first */ + if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { + const int pt_distance = len_manhattan_v2v2_int(mval, xy); + + /* check if point is inside */ + if (pt_distance <= radius_squared) { + /* only use this point if it is a better match than the current hit - T44685 */ + if (pt_distance < hit_distance) { + hit_layer = gpl; + hit_stroke = gps; + hit_point = pt; + hit_distance = pt_distance; + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* Abort if nothing hit... */ + if (ELEM(NULL, hit_stroke, hit_point)) { + + /* since left mouse select change, deselect all if click outside any hit */ + deselect_all_selected(C); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + + return OPERATOR_CANCELLED; + } + + /* adjust selection behavior - for toggle option */ + if (toggle) { + deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0; + } + + /* If not extending selection, deselect everything else */ + if (extend == false) { + deselect_all_selected(C); + } + + /* Perform selection operations... */ + if (whole) { + bGPDspoint *pt; + int i; + + /* entire stroke's points */ + for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) { + if (deselect == false) + pt->flag |= GP_SPOINT_SELECT; + else + pt->flag &= ~GP_SPOINT_SELECT; + } + + /* stroke too... */ + if (deselect == false) + hit_stroke->flag |= GP_STROKE_SELECT; + else + hit_stroke->flag &= ~GP_STROKE_SELECT; + } + else { + /* just the point (and the stroke) */ + if (deselect == false) { + /* we're adding selection, so selection must be true */ + hit_point->flag |= GP_SPOINT_SELECT; + hit_stroke->flag |= GP_STROKE_SELECT; + + /* expand selection to segment */ + if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) { + float r_hita[3], r_hitb[3]; + bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT); + ED_gpencil_select_stroke_segment( + hit_layer, hit_stroke, hit_point, hit_select, false, scale, r_hita, r_hitb); + } + } + else { + /* deselect point */ + hit_point->flag &= ~GP_SPOINT_SELECT; + + /* ensure that stroke is selected correctly */ + BKE_gpencil_stroke_sync_selection(hit_stroke); + } + } + + /* updates */ + if (hit_point != NULL) { + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; } static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - RNA_int_set_array(op->ptr, "location", event->mval); - return gpencil_select_exec(C, op); + RNA_int_set_array(op->ptr, "location", event->mval); + return gpencil_select_exec(C, op); } void GPENCIL_OT_select(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select"; - ot->description = "Select Grease Pencil strokes and/or stroke points"; - ot->idname = "GPENCIL_OT_select"; - - /* callbacks */ - ot->invoke = gpencil_select_invoke; - ot->exec = gpencil_select_exec; - ot->poll = gpencil_select_poll; - - /* flag */ - ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; - - /* properties */ - WM_operator_properties_mouse_select(ot); - - prop = RNA_def_boolean(ot->srna, "entire_strokes", false, "Entire Strokes", "Select entire strokes instead of just the nearest stroke vertex"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, INT_MIN, INT_MAX, "Location", "Mouse location", INT_MIN, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select"; + ot->description = "Select Grease Pencil strokes and/or stroke points"; + ot->idname = "GPENCIL_OT_select"; + + /* callbacks */ + ot->invoke = gpencil_select_invoke; + ot->exec = gpencil_select_exec; + ot->poll = gpencil_select_poll; + + /* flag */ + ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + + /* properties */ + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_boolean(ot->srna, + "entire_strokes", + false, + "Entire Strokes", + "Select entire strokes instead of just the nearest stroke vertex"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + INT_MIN, + INT_MAX, + "Location", + "Mouse location", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /** \} */ diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 7ed794a0a32..d91977bc0da 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -21,7 +21,6 @@ * \ingroup edgpencil */ - #include <stdlib.h> #include <string.h> @@ -48,10 +47,10 @@ #include "gpencil_intern.h" typedef struct bGPundonode { - struct bGPundonode *next, *prev; + struct bGPundonode *next, *prev; - char name[BKE_UNDO_STR_MAX]; - struct bGPdata *gpd; + char name[BKE_UNDO_STR_MAX]; + struct bGPdata *gpd; } bGPundonode; static ListBase undo_nodes = {NULL, NULL}; @@ -59,140 +58,140 @@ static bGPundonode *cur_node = NULL; int ED_gpencil_session_active(void) { - return (BLI_listbase_is_empty(&undo_nodes) == false); + return (BLI_listbase_is_empty(&undo_nodes) == false); } int ED_undo_gpencil_step(bContext *C, int step, const char *name) { - bGPdata **gpd_ptr = NULL, *new_gpd = NULL; - - gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - - if (step == 1) { /* undo */ - //printf("\t\tGP - undo step\n"); - if (cur_node->prev) { - if (!name || STREQ(cur_node->name, name)) { - cur_node = cur_node->prev; - new_gpd = cur_node->gpd; - } - } - } - else if (step == -1) { - //printf("\t\tGP - redo step\n"); - if (cur_node->next) { - if (!name || STREQ(cur_node->name, name)) { - cur_node = cur_node->next; - new_gpd = cur_node->gpd; - } - } - } - - if (new_gpd) { - if (gpd_ptr) { - if (*gpd_ptr) { - bGPdata *gpd = *gpd_ptr; - bGPDlayer *gpl, *gpld; - - BKE_gpencil_free_layers(&gpd->layers); - - /* copy layers */ - BLI_listbase_clear(&gpd->layers); - - for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) { - /* make a copy of source layer and its data */ - gpld = BKE_gpencil_layer_duplicate(gpl); - BLI_addtail(&gpd->layers, gpld); - } - } - } - /* drawing batch cache is dirty now */ - DEG_id_tag_update(&new_gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - new_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } - - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata **gpd_ptr = NULL, *new_gpd = NULL; + + gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + if (step == 1) { /* undo */ + //printf("\t\tGP - undo step\n"); + if (cur_node->prev) { + if (!name || STREQ(cur_node->name, name)) { + cur_node = cur_node->prev; + new_gpd = cur_node->gpd; + } + } + } + else if (step == -1) { + //printf("\t\tGP - redo step\n"); + if (cur_node->next) { + if (!name || STREQ(cur_node->name, name)) { + cur_node = cur_node->next; + new_gpd = cur_node->gpd; + } + } + } + + if (new_gpd) { + if (gpd_ptr) { + if (*gpd_ptr) { + bGPdata *gpd = *gpd_ptr; + bGPDlayer *gpl, *gpld; + + BKE_gpencil_free_layers(&gpd->layers); + + /* copy layers */ + BLI_listbase_clear(&gpd->layers); + + for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) { + /* make a copy of source layer and its data */ + gpld = BKE_gpencil_layer_duplicate(gpl); + BLI_addtail(&gpd->layers, gpld); + } + } + } + /* drawing batch cache is dirty now */ + DEG_id_tag_update(&new_gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + new_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void gpencil_undo_init(bGPdata *gpd) { - gpencil_undo_push(gpd); + gpencil_undo_push(gpd); } static void gpencil_undo_free_node(bGPundonode *undo_node) { - /* HACK: animdata wasn't duplicated, so it shouldn't be freed here, - * or else the real copy will segfault when accessed - */ - undo_node->gpd->adt = NULL; + /* HACK: animdata wasn't duplicated, so it shouldn't be freed here, + * or else the real copy will segfault when accessed + */ + undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd, false); - MEM_freeN(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); + MEM_freeN(undo_node->gpd); } void gpencil_undo_push(bGPdata *gpd) { - bGPundonode *undo_node; + bGPundonode *undo_node; - //printf("\t\tGP - undo push\n"); + //printf("\t\tGP - undo push\n"); - if (cur_node) { - /* remove all un-done nodes from stack */ - undo_node = cur_node->next; + if (cur_node) { + /* remove all un-done nodes from stack */ + undo_node = cur_node->next; - while (undo_node) { - bGPundonode *next_node = undo_node->next; + while (undo_node) { + bGPundonode *next_node = undo_node->next; - gpencil_undo_free_node(undo_node); - BLI_freelinkN(&undo_nodes, undo_node); + gpencil_undo_free_node(undo_node); + BLI_freelinkN(&undo_nodes, undo_node); - undo_node = next_node; - } - } + undo_node = next_node; + } + } - /* limit number of undo steps to the maximum undo steps - * - to prevent running out of memory during **really** - * long drawing sessions (triggering swapping) - */ - /* TODO: Undo-memory constraint is not respected yet, but can be added if we have any need for it */ - if (U.undosteps && !BLI_listbase_is_empty(&undo_nodes)) { - /* remove anything older than n-steps before cur_node */ - int steps = 0; + /* limit number of undo steps to the maximum undo steps + * - to prevent running out of memory during **really** + * long drawing sessions (triggering swapping) + */ + /* TODO: Undo-memory constraint is not respected yet, but can be added if we have any need for it */ + if (U.undosteps && !BLI_listbase_is_empty(&undo_nodes)) { + /* remove anything older than n-steps before cur_node */ + int steps = 0; - undo_node = (cur_node) ? cur_node : undo_nodes.last; - while (undo_node) { - bGPundonode *prev_node = undo_node->prev; + undo_node = (cur_node) ? cur_node : undo_nodes.last; + while (undo_node) { + bGPundonode *prev_node = undo_node->prev; - if (steps >= U.undosteps) { - gpencil_undo_free_node(undo_node); - BLI_freelinkN(&undo_nodes, undo_node); - } + if (steps >= U.undosteps) { + gpencil_undo_free_node(undo_node); + BLI_freelinkN(&undo_nodes, undo_node); + } - steps++; - undo_node = prev_node; - } - } + steps++; + undo_node = prev_node; + } + } - /* create new undo node */ - undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); - undo_node->gpd = BKE_gpencil_data_duplicate(NULL, gpd, true); + /* create new undo node */ + undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); + undo_node->gpd = BKE_gpencil_data_duplicate(NULL, gpd, true); - cur_node = undo_node; + cur_node = undo_node; - BLI_addtail(&undo_nodes, undo_node); + BLI_addtail(&undo_nodes, undo_node); } void gpencil_undo_finish(void) { - bGPundonode *undo_node = undo_nodes.first; + bGPundonode *undo_node = undo_nodes.first; - while (undo_node) { - gpencil_undo_free_node(undo_node); - undo_node = undo_node->next; - } + while (undo_node) { + gpencil_undo_free_node(undo_node); + undo_node = undo_node->next; + } - BLI_freelistN(&undo_nodes); + BLI_freelistN(&undo_nodes); - cur_node = NULL; + cur_node = NULL; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index f582b52713d..858415061b1 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -88,120 +88,129 @@ /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, * when context info is not available. */ -bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob, PointerRNA *r_ptr) +bGPdata **ED_gpencil_data_get_pointers_direct( + ID *screen_id, ScrArea *sa, Scene *scene, Object *ob, PointerRNA *r_ptr) { - /* if there's an active area, check if the particular editor may - * have defined any special Grease Pencil context for editing... - */ - if (sa) { - SpaceLink *sl = sa->spacedata.first; - - switch (sa->spacetype) { - /* XXX: Should we reduce reliance on context.gpencil_data for these cases? */ - case SPACE_PROPERTIES: /* properties */ - case SPACE_INFO: /* header info (needed after workspaces merge) */ - { - if (ob && (ob->type == OB_GPENCIL)) { - /* GP Object */ - if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); - return (bGPdata **)&ob->data; - } - else { - return NULL; - } - - break; - } - - case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ - case SPACE_VIEW3D: /* 3D-View */ - { - if (ob && (ob->type == OB_GPENCIL)) { - /* GP Object */ - if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); - return (bGPdata **)&ob->data; - } - else { - /* Annotations */ - /* XXX: */ - if (r_ptr) RNA_id_pointer_create(&scene->id, r_ptr); - return &scene->gpd; - } - - break; - } - case SPACE_NODE: /* Nodes Editor */ - { - SpaceNode *snode = (SpaceNode *)sl; - - /* return the GP data for the active node block/node */ - if (snode && snode->nodetree) { - /* for now, as long as there's an active node tree, - * default to using that in the Nodes Editor */ - if (r_ptr) RNA_id_pointer_create(&snode->nodetree->id, r_ptr); - return &snode->nodetree->gpd; - } - - /* even when there is no node-tree, don't allow this to flow to scene */ - return NULL; - } - case SPACE_SEQ: /* Sequencer */ - { - SpaceSeq *sseq = (SpaceSeq *)sl; - - /* for now, Grease Pencil data is associated with the space (actually preview region only) */ - /* XXX our convention for everything else is to link to data though... */ - if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); - return &sseq->gpd; - } - case SPACE_IMAGE: /* Image/UV Editor */ - { - SpaceImage *sima = (SpaceImage *)sl; - - /* for now, Grease Pencil data is associated with the space... */ - /* XXX our convention for everything else is to link to data though... */ - if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); - return &sima->gpd; - } - case SPACE_CLIP: /* Nodes Editor */ - { - SpaceClip *sc = (SpaceClip *)sl; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip) { - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - - if (!track) - return NULL; - - if (r_ptr) RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, r_ptr); - return &track->gpd; - } - else { - if (r_ptr) RNA_id_pointer_create(&clip->id, r_ptr); - return &clip->gpd; - } - } - break; - } - default: /* unsupported space */ - return NULL; - } - } - - return NULL; + /* if there's an active area, check if the particular editor may + * have defined any special Grease Pencil context for editing... + */ + if (sa) { + SpaceLink *sl = sa->spacedata.first; + + switch (sa->spacetype) { + /* XXX: Should we reduce reliance on context.gpencil_data for these cases? */ + case SPACE_PROPERTIES: /* properties */ + case SPACE_INFO: /* header info (needed after workspaces merge) */ + { + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) + RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; + } + else { + return NULL; + } + + break; + } + + case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ + case SPACE_VIEW3D: /* 3D-View */ + { + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) + RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; + } + else { + /* Annotations */ + /* XXX: */ + if (r_ptr) + RNA_id_pointer_create(&scene->id, r_ptr); + return &scene->gpd; + } + + break; + } + case SPACE_NODE: /* Nodes Editor */ + { + SpaceNode *snode = (SpaceNode *)sl; + + /* return the GP data for the active node block/node */ + if (snode && snode->nodetree) { + /* for now, as long as there's an active node tree, + * default to using that in the Nodes Editor */ + if (r_ptr) + RNA_id_pointer_create(&snode->nodetree->id, r_ptr); + return &snode->nodetree->gpd; + } + + /* even when there is no node-tree, don't allow this to flow to scene */ + return NULL; + } + case SPACE_SEQ: /* Sequencer */ + { + SpaceSeq *sseq = (SpaceSeq *)sl; + + /* for now, Grease Pencil data is associated with the space (actually preview region only) */ + /* XXX our convention for everything else is to link to data though... */ + if (r_ptr) + RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); + return &sseq->gpd; + } + case SPACE_IMAGE: /* Image/UV Editor */ + { + SpaceImage *sima = (SpaceImage *)sl; + + /* for now, Grease Pencil data is associated with the space... */ + /* XXX our convention for everything else is to link to data though... */ + if (r_ptr) + RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); + return &sima->gpd; + } + case SPACE_CLIP: /* Nodes Editor */ + { + SpaceClip *sc = (SpaceClip *)sl; + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (clip) { + if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); + + if (!track) + return NULL; + + if (r_ptr) + RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, r_ptr); + return &track->gpd; + } + else { + if (r_ptr) + RNA_id_pointer_create(&clip->id, r_ptr); + return &clip->gpd; + } + } + break; + } + default: /* unsupported space */ + return NULL; + } + } + + return NULL; } /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr) { - ID *screen_id = (ID *)CTX_wm_screen(C); - Scene *scene = CTX_data_scene(C); - ScrArea *sa = CTX_wm_area(C); - Object *ob = CTX_data_active_object(C); + ID *screen_id = (ID *)CTX_wm_screen(C); + Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); - return ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, r_ptr); + return ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, r_ptr); } /* -------------------------------------------------------- */ @@ -209,8 +218,8 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr) /* Get the active Grease Pencil datablock, when context is not available */ bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, NULL); - return (gpd_ptr) ? *(gpd_ptr) : NULL; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, NULL); + return (gpd_ptr) ? *(gpd_ptr) : NULL; } /** @@ -220,8 +229,8 @@ bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *sc */ bGPdata *ED_gpencil_data_get_active(const bContext *C) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - return (gpd_ptr) ? *(gpd_ptr) : NULL; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + return (gpd_ptr) ? *(gpd_ptr) : NULL; } /** @@ -234,16 +243,16 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C) */ bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) { - ID *screen_id = (ID *)CTX_wm_screen(C); - ScrArea *sa = CTX_wm_area(C); + ID *screen_id = (ID *)CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); - const Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob = CTX_data_active_object(C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + const Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - /* if (ob && ob->type == OB_GPENCIL) BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); */ - return ED_gpencil_data_get_active_direct(screen_id, sa, scene_eval, ob_eval); + /* if (ob && ob->type == OB_GPENCIL) BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); */ + return ED_gpencil_data_get_active_direct(screen_id, sa, scene_eval, ob_eval); } /* -------------------------------------------------------- */ @@ -254,10 +263,10 @@ bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) */ bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr) { - /* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data. - * Otherwise, the GP datablock is being used for annotations (i.e. everywhere else) - */ - return ((owner_ptr) && (owner_ptr->type != &RNA_Object)); + /* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data. + * Otherwise, the GP datablock is being used for annotations (i.e. everywhere else) + */ + return ((owner_ptr) && (owner_ptr->type != &RNA_Object)); } /* ******************************************************** */ @@ -266,21 +275,21 @@ bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr) /* Check whether there's an active GP keyframe on the current frame */ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) { - if (ob && ob->data && (ob->type == OB_GPENCIL)) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); - if (gpl) { - if (gpl->actframe) { - // XXX: assumes that frame has been fetched already - return (gpl->actframe->framenum == cfra); - } - else { - /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ - } - } - } - - return false; + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); + if (gpl) { + if (gpl->actframe) { + // XXX: assumes that frame has been fetched already + return (gpl->actframe->framenum == cfra); + } + else { + /* XXX: disabled as could be too much of a penalty */ + /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ + } + } + } + + return false; } /* ******************************************************** */ @@ -289,30 +298,30 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) /* poll callback for adding data/layers - special */ bool gp_add_poll(bContext *C) { - /* the base line we have is that we have somewhere to add Grease Pencil data */ - return ED_gpencil_data_get_pointers(C, NULL) != NULL; + /* the base line we have is that we have somewhere to add Grease Pencil data */ + return ED_gpencil_data_get_pointers(C, NULL) != NULL; } /* poll callback for checking if there is an active layer */ bool gp_active_layer_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - return (gpl != NULL); + return (gpl != NULL); } /* poll callback for checking if there is an active brush */ bool gp_active_brush_poll(bContext *C) { - ToolSettings *ts = CTX_data_tool_settings(C); - Paint *paint = &ts->gp_paint->paint; - if (paint) { - return (paint->brush != NULL); - } - else { - return false; - } + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = &ts->gp_paint->paint; + if (paint) { + return (paint->brush != NULL); + } + else { + return false; + } } /* ******************************************************** */ @@ -320,88 +329,91 @@ bool gp_active_brush_poll(bContext *C) /* NOTE: These include an option to create a new layer and use that... */ /* Just existing layers */ -const EnumPropertyItem *ED_gpencil_layers_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +const EnumPropertyItem *ED_gpencil_layers_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl; - EnumPropertyItem *item = NULL, item_tmp = {0}; - int totitem = 0; - int i = 0; - - if (ELEM(NULL, C, gpd)) { - return DummyRNA_DEFAULT_items; - } - - /* Existing layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next, i++) { - item_tmp.identifier = gpl->info; - item_tmp.name = gpl->info; - item_tmp.value = i; - - if (gpl->flag & GP_LAYER_ACTIVE) - item_tmp.icon = ICON_GREASEPENCIL; - else - item_tmp.icon = ICON_NONE; - - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl; + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing layers */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next, i++) { + item_tmp.identifier = gpl->info; + item_tmp.name = gpl->info; + item_tmp.value = i; + + if (gpl->flag & GP_LAYER_ACTIVE) + item_tmp.icon = ICON_GREASEPENCIL; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } /* Existing + Option to add/use new layer */ -const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl; - EnumPropertyItem *item = NULL, item_tmp = {0}; - int totitem = 0; - int i = 0; - - if (ELEM(NULL, C, gpd)) { - return DummyRNA_DEFAULT_items; - } - - /* Create new layer */ - /* TODO: have some way of specifying that we don't want this? */ - { - /* "New Layer" entry */ - item_tmp.identifier = "__CREATE__"; - item_tmp.name = "New Layer"; - item_tmp.value = -1; - item_tmp.icon = ICON_ADD; - RNA_enum_item_add(&item, &totitem, &item_tmp); - - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); - } - const int tot = BLI_listbase_count(&gpd->layers); - /* Existing layers */ - for (gpl = gpd->layers.last, i = 0; gpl; gpl = gpl->prev, i++) { - item_tmp.identifier = gpl->info; - item_tmp.name = gpl->info; - item_tmp.value = tot - i - 1; - - if (gpl->flag & GP_LAYER_ACTIVE) - item_tmp.icon = ICON_GREASEPENCIL; - else - item_tmp.icon = ICON_NONE; - - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl; + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_DEFAULT_items; + } + + /* Create new layer */ + /* TODO: have some way of specifying that we don't want this? */ + { + /* "New Layer" entry */ + item_tmp.identifier = "__CREATE__"; + item_tmp.name = "New Layer"; + item_tmp.value = -1; + item_tmp.icon = ICON_ADD; + RNA_enum_item_add(&item, &totitem, &item_tmp); + + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); + } + const int tot = BLI_listbase_count(&gpd->layers); + /* Existing layers */ + for (gpl = gpd->layers.last, i = 0; gpl; gpl = gpl->prev, i++) { + item_tmp.identifier = gpl->info; + item_tmp.name = gpl->info; + item_tmp.value = tot - i - 1; + + if (gpl->flag & GP_LAYER_ACTIVE) + item_tmp.icon = ICON_GREASEPENCIL; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } - /* ******************************************************** */ /* Brush Tool Core */ @@ -416,19 +428,18 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( * \param x1, y1: The screen-space x and y coordinates of the end of the stroke segment */ bool gp_stroke_inside_circle( - const float mval[2], const float UNUSED(mvalo[2]), - int rad, int x0, int y0, int x1, int y1) + const float mval[2], const float UNUSED(mvalo[2]), int rad, int x0, int y0, int x1, int y1) { - /* simple within-radius check for now */ - const float screen_co_a[2] = {x0, y0}; - const float screen_co_b[2] = {x1, y1}; + /* simple within-radius check for now */ + const float screen_co_a[2] = {x0, y0}; + const float screen_co_b[2] = {x1, y1}; - if (edge_inside_circle(mval, rad, screen_co_a, screen_co_b)) { - return true; - } + if (edge_inside_circle(mval, rad, screen_co_a, screen_co_b)) { + return true; + } - /* not inside */ - return false; + /* not inside */ + return false; } /* ******************************************************** */ @@ -438,50 +449,50 @@ bool gp_stroke_inside_circle( /* TODO: do we need additional flags for screenspace vs dataspace? */ bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) { - /* sanity check */ - if (ELEM(NULL, sa, gps)) - return false; - - /* filter stroke types by flags + spacetype */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D strokes - only in 3D view */ - return ((sa->spacetype == SPACE_VIEW3D) || (sa->spacetype == SPACE_PROPERTIES)); - } - else if (gps->flag & GP_STROKE_2DIMAGE) { - /* Special "image" strokes - only in Image Editor */ - return (sa->spacetype == SPACE_IMAGE); - } - else if (gps->flag & GP_STROKE_2DSPACE) { - /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ - return (sa->spacetype != SPACE_VIEW3D); - } - else { - /* view aligned - anything goes */ - return true; - } + /* sanity check */ + if (ELEM(NULL, sa, gps)) + return false; + + /* filter stroke types by flags + spacetype */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D strokes - only in 3D view */ + return ((sa->spacetype == SPACE_VIEW3D) || (sa->spacetype == SPACE_PROPERTIES)); + } + else if (gps->flag & GP_STROKE_2DIMAGE) { + /* Special "image" strokes - only in Image Editor */ + return (sa->spacetype == SPACE_IMAGE); + } + else if (gps->flag & GP_STROKE_2DSPACE) { + /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ + return (sa->spacetype != SPACE_VIEW3D); + } + else { + /* view aligned - anything goes */ + return true; + } } /* Check whether given stroke can be edited in the current context */ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) { - ScrArea *sa = CTX_wm_area(C); - return ED_gpencil_stroke_can_use_direct(sa, gps); + ScrArea *sa = CTX_wm_area(C); + return ED_gpencil_stroke_can_use_direct(sa, gps); } /* Check whether given stroke can be edited for the current color */ bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps) { - /* check if the color is editable */ - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + /* check if the color is editable */ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); - if (gp_style != NULL) { - if (gp_style->flag & GP_STYLE_COLOR_HIDE) - return false; - if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) - return false; - } + if (gp_style != NULL) { + if (gp_style->flag & GP_STYLE_COLOR_HIDE) + return false; + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) + return false; + } - return true; + return true; } /* ******************************************************** */ @@ -494,41 +505,42 @@ bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstr */ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) { - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - - /* zero out the storage (just in case) */ - memset(r_gsc, 0, sizeof(GP_SpaceConversion)); - unit_m4(r_gsc->mat); - - /* store settings */ - r_gsc->scene = CTX_data_scene(C); - r_gsc->ob = CTX_data_active_object(C); - - r_gsc->sa = sa; - r_gsc->ar = ar; - r_gsc->v2d = &ar->v2d; - - /* init region-specific stuff */ - if (sa->spacetype == SPACE_VIEW3D) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); - struct Depsgraph *depsgraph = CTX_data_depsgraph(C); - View3D *v3d = (View3D *)CTX_wm_space_data(C); - RegionView3D *rv3d = ar->regiondata; - - /* init 3d depth buffers */ - view3d_operator_needs_opengl(C); - - view3d_region_operator_needs_opengl(win, ar); - ED_view3d_autodist_init(depsgraph, ar, v3d, 0); - - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - ED_view3d_calc_camera_border(scene, CTX_data_depsgraph(C), ar, v3d, rv3d, &r_gsc->subrect_data, true); - r_gsc->subrect = &r_gsc->subrect_data; - } - } + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + + /* zero out the storage (just in case) */ + memset(r_gsc, 0, sizeof(GP_SpaceConversion)); + unit_m4(r_gsc->mat); + + /* store settings */ + r_gsc->scene = CTX_data_scene(C); + r_gsc->ob = CTX_data_active_object(C); + + r_gsc->sa = sa; + r_gsc->ar = ar; + r_gsc->v2d = &ar->v2d; + + /* init region-specific stuff */ + if (sa->spacetype == SPACE_VIEW3D) { + wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); + View3D *v3d = (View3D *)CTX_wm_space_data(C); + RegionView3D *rv3d = ar->regiondata; + + /* init 3d depth buffers */ + view3d_operator_needs_opengl(C); + + view3d_region_operator_needs_opengl(win, ar); + ED_view3d_autodist_init(depsgraph, ar, v3d, 0); + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + ED_view3d_calc_camera_border( + scene, CTX_data_depsgraph(C), ar, v3d, rv3d, &r_gsc->subrect_data, true); + r_gsc->subrect = &r_gsc->subrect_data; + } + } } /** @@ -540,50 +552,52 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) */ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt) { - float fpt[3]; + float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - copy_v3_v3(&r_pt->x, fpt); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + copy_v3_v3(&r_pt->x, fpt); } /** * Change position relative to parent object */ -void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) +void gp_apply_parent( + Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) { - bGPDspoint *pt; - int i; - - /* undo matrix */ - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; - float fpt[3]; - - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); - - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); - copy_v3_v3(&pt->x, fpt); - } + bGPDspoint *pt; + int i; + + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); + } } /** * Change point position relative to parent object */ -void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt) +void gp_apply_parent_point( + Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt) { - /* undo matrix */ - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; - float fpt[3]; + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); - mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); - copy_v3_v3(&pt->x, fpt); + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); } /** @@ -595,46 +609,44 @@ void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bG * \warning This assumes that the caller has already checked whether the stroke in question can be drawn. */ void gp_point_to_xy( - const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, - int *r_x, int *r_y) + const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y) { - const ARegion *ar = gsc->ar; - const View2D *v2d = gsc->v2d; - const rctf *subrect = gsc->subrect; - int xyval[2]; - - /* sanity checks */ - BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); - BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); - - - if (gps->flag & GP_STROKE_3DSPACE) { - if (ED_view3d_project_int_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - *r_x = xyval[0]; - *r_y = xyval[1]; - } - else { - *r_x = V2D_IS_CLIPPED; - *r_y = V2D_IS_CLIPPED; - } - } - else if (gps->flag & GP_STROKE_2DSPACE) { - float vec[3] = {pt->x, pt->y, 0.0f}; - mul_m4_v3(gsc->mat, vec); - UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y); - } - else { - if (subrect == NULL) { - /* normal 3D view (or view space) */ - *r_x = (int)(pt->x / 100 * ar->winx); - *r_y = (int)(pt->y / 100 * ar->winy); - } - else { - /* camera view, use subrect */ - *r_x = (int)((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin; - *r_y = (int)((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin; - } - } + const ARegion *ar = gsc->ar; + const View2D *v2d = gsc->v2d; + const rctf *subrect = gsc->subrect; + int xyval[2]; + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + if (gps->flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_int_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + *r_x = xyval[0]; + *r_y = xyval[1]; + } + else { + *r_x = V2D_IS_CLIPPED; + *r_y = V2D_IS_CLIPPED; + } + } + else if (gps->flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt->x, pt->y, 0.0f}; + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y); + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + *r_x = (int)(pt->x / 100 * ar->winx); + *r_y = (int)(pt->y / 100 * ar->winy); + } + else { + /* camera view, use subrect */ + *r_x = (int)((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin; + *r_y = (int)((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } } /** @@ -648,117 +660,119 @@ void gp_point_to_xy( * * \warning This assumes that the caller has already checked whether the stroke in question can be drawn. */ -void gp_point_to_xy_fl( - const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, - float *r_x, float *r_y) +void gp_point_to_xy_fl(const GP_SpaceConversion *gsc, + const bGPDstroke *gps, + const bGPDspoint *pt, + float *r_x, + float *r_y) { - const ARegion *ar = gsc->ar; - const View2D *v2d = gsc->v2d; - const rctf *subrect = gsc->subrect; - float xyval[2]; - - /* sanity checks */ - BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); - BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); - - - if (gps->flag & GP_STROKE_3DSPACE) { - if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - *r_x = xyval[0]; - *r_y = xyval[1]; - } - else { - *r_x = 0.0f; - *r_y = 0.0f; - } - } - else if (gps->flag & GP_STROKE_2DSPACE) { - float vec[3] = {pt->x, pt->y, 0.0f}; - int t_x, t_y; - - mul_m4_v3(gsc->mat, vec); - UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); - - if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { - /* XXX: Or should we just always use the values as-is? */ - *r_x = 0.0f; - *r_y = 0.0f; - } - else { - *r_x = (float)t_x; - *r_y = (float)t_y; - } - } - else { - if (subrect == NULL) { - /* normal 3D view (or view space) */ - *r_x = (pt->x / 100.0f * ar->winx); - *r_y = (pt->y / 100.0f * ar->winy); - } - else { - /* camera view, use subrect */ - *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; - *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; - } - } + const ARegion *ar = gsc->ar; + const View2D *v2d = gsc->v2d; + const rctf *subrect = gsc->subrect; + float xyval[2]; + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + if (gps->flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + *r_x = xyval[0]; + *r_y = xyval[1]; + } + else { + *r_x = 0.0f; + *r_y = 0.0f; + } + } + else if (gps->flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt->x, pt->y, 0.0f}; + int t_x, t_y; + + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); + + if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { + /* XXX: Or should we just always use the values as-is? */ + *r_x = 0.0f; + *r_y = 0.0f; + } + else { + *r_x = (float)t_x; + *r_y = (float)t_y; + } + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + *r_x = (pt->x / 100.0f * ar->winx); + *r_y = (pt->y / 100.0f * ar->winy); + } + else { + /* camera view, use subrect */ + *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } } - /** * generic based on gp_point_to_xy_fl */ -void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, const short flag, const float pt[3], float xy[2]) +void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, + const short flag, + const float pt[3], + float xy[2]) { - const ARegion *ar = gsc->ar; - const View2D *v2d = gsc->v2d; - const rctf *subrect = gsc->subrect; - float xyval[2]; - - /* sanity checks */ - BLI_assert((gsc->sa->spacetype == SPACE_VIEW3D)); - - if (flag & GP_STROKE_3DSPACE) { - if (ED_view3d_project_float_global(ar, pt, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - xy[0] = xyval[0]; - xy[1] = xyval[1]; - } - else { - xy[0] = 0.0f; - xy[1] = 0.0f; - } - } - else if (flag & GP_STROKE_2DSPACE) { - float vec[3] = { pt[0], pt[1], 0.0f }; - int t_x, t_y; - - mul_m4_v3(gsc->mat, vec); - UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); - - if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { - /* XXX: Or should we just always use the values as-is? */ - xy[0] = 0.0f; - xy[1] = 0.0f; - } - else { - xy[0] = (float)t_x; - xy[1] = (float)t_y; - } - } - else { - if (subrect == NULL) { - /* normal 3D view (or view space) */ - xy[0] = (pt[0] / 100.0f * ar->winx); - xy[1] = (pt[1] / 100.0f * ar->winy); - } - else { - /* camera view, use subrect */ - xy[0] = ((pt[0] / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; - xy[1] = ((pt[1] / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; - } - } + const ARegion *ar = gsc->ar; + const View2D *v2d = gsc->v2d; + const rctf *subrect = gsc->subrect; + float xyval[2]; + + /* sanity checks */ + BLI_assert((gsc->sa->spacetype == SPACE_VIEW3D)); + + if (flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_float_global(ar, pt, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + xy[0] = xyval[0]; + xy[1] = xyval[1]; + } + else { + xy[0] = 0.0f; + xy[1] = 0.0f; + } + } + else if (flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt[0], pt[1], 0.0f}; + int t_x, t_y; + + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); + + if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { + /* XXX: Or should we just always use the values as-is? */ + xy[0] = 0.0f; + xy[1] = 0.0f; + } + else { + xy[0] = (float)t_x; + xy[1] = (float)t_y; + } + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + xy[0] = (pt[0] / 100.0f * ar->winx); + xy[1] = (pt[1] / 100.0f * ar->winy); + } + else { + /* camera view, use subrect */ + xy[0] = ((pt[0] / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + xy[1] = ((pt[1] / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } } - /** * Project screenspace coordinates to 3D-space * @@ -774,34 +788,37 @@ void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, const short flag, const fl * * \warning Assumes that it is getting called in a 3D view only. */ -bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3]) +bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, + Scene *scene, + const float screen_co[2], + float r_out[3]) { - const RegionView3D *rv3d = gsc->ar->regiondata; - float rvec[3]; + const RegionView3D *rv3d = gsc->ar->regiondata; + float rvec[3]; - ED_gp_get_drawing_reference( - scene, gsc->ob, gsc->gpl, - scene->toolsettings->gpencil_v3d_align, rvec); + ED_gp_get_drawing_reference( + scene, gsc->ob, gsc->gpl, scene->toolsettings->gpencil_v3d_align, rvec); - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - float mval_f[2], mval_prj[2]; - float dvec[3]; + float mval_f[2], mval_prj[2]; + float dvec[3]; - copy_v2_v2(mval_f, screen_co); + copy_v2_v2(mval_f, screen_co); - if (ED_view3d_project_float_global(gsc->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gsc->ar, mval_f, dvec, zfac); - sub_v3_v3v3(r_out, rvec, dvec); + if (ED_view3d_project_float_global(gsc->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(gsc->ar, mval_f, dvec, zfac); + sub_v3_v3v3(r_out, rvec, dvec); - return true; - } - else { - zero_v3(r_out); + return true; + } + else { + zero_v3(r_out); - return false; - } + return false; + } } /** @@ -812,43 +829,45 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, Scene *scene, const float * \param depth: Depth array (via #ED_view3d_autodist_depth()). * \param[out] r_out: The resulting 2D point data. */ -void gp_stroke_convertcoords_tpoint( - Scene *scene, ARegion *ar, - Object *ob, bGPDlayer *gpl, - const tGPspoint *point2D, float *depth, - float r_out[3]) +void gp_stroke_convertcoords_tpoint(Scene *scene, + ARegion *ar, + Object *ob, + bGPDlayer *gpl, + const tGPspoint *point2D, + float *depth, + float r_out[3]) { - ToolSettings *ts = scene->toolsettings; - - int mval_i[2]; - round_v2i_v2fl(mval_i, &point2D->x); - - if ((depth != NULL) && (ED_view3d_autodist_simple(ar, mval_i, r_out, 0, depth))) { - /* projecting onto 3D-Geometry - * - nothing more needs to be done here, since view_autodist_simple() has already done it - */ - } - else { - float mval_f[2] = {point2D->x, point2D->y}; - float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; - - /* Current method just converts each point in screen-coordinates to - * 3D-coordinates using the 3D-cursor as reference. - */ - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, rvec); - zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); - - if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); - sub_v3_v3v3(r_out, rvec, dvec); - } - else { - zero_v3(r_out); - } - } + ToolSettings *ts = scene->toolsettings; + + int mval_i[2]; + round_v2i_v2fl(mval_i, &point2D->x); + + if ((depth != NULL) && (ED_view3d_autodist_simple(ar, mval_i, r_out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ + } + else { + float mval_f[2] = {point2D->x, point2D->y}; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. + */ + ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, rvec); + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); + sub_v3_v3v3(r_out, rvec, dvec); + } + else { + zero_v3(r_out); + } + } } /** @@ -856,209 +875,206 @@ void gp_stroke_convertcoords_tpoint( * \param[out] r_vec : Reference point found */ void ED_gp_get_drawing_reference( - const Scene *scene, const Object *ob, bGPDlayer *UNUSED(gpl), - char align_flag, float r_vec[3]) + const Scene *scene, const Object *ob, bGPDlayer *UNUSED(gpl), char align_flag, float r_vec[3]) { - const float *fp = scene->cursor.location; - - /* if using a gpencil object at cursor mode, can use the location of the object */ - if (align_flag & GP_PROJECT_VIEWSPACE) { - if (ob && (ob->type == OB_GPENCIL)) { - /* fallback (no strokes) - use cursor or object location */ - if (align_flag & GP_PROJECT_CURSOR) { - /* use 3D-cursor */ - copy_v3_v3(r_vec, fp); - } - else { - /* use object location */ - copy_v3_v3(r_vec, ob->obmat[3]); - } - } - } - else { - /* use 3D-cursor */ - copy_v3_v3(r_vec, fp); - } + const float *fp = scene->cursor.location; + + /* if using a gpencil object at cursor mode, can use the location of the object */ + if (align_flag & GP_PROJECT_VIEWSPACE) { + if (ob && (ob->type == OB_GPENCIL)) { + /* fallback (no strokes) - use cursor or object location */ + if (align_flag & GP_PROJECT_CURSOR) { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); + } + else { + /* use object location */ + copy_v3_v3(r_vec, ob->obmat[3]); + } + } + } + else { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); + } } void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *gps) { - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - GP_SpaceConversion gsc = { NULL }; + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + GP_SpaceConversion gsc = {NULL}; - bGPDspoint *pt; - int i; - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; + bGPDspoint *pt; + int i; + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; - /* init space conversion stuff */ - gp_point_conversion_init(C, &gsc); + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); + ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); - /* Adjust each point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float xy[2]; + /* Adjust each point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float xy[2]; - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); - /* Planar - All on same plane parallel to the viewplane */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + /* Planar - All on same plane parallel to the viewplane */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); - /* Unapply parent corrections */ - mul_m4_v3(inverse_diff_mat, &pt->x); - } + /* Unapply parent corrections */ + mul_m4_v3(inverse_diff_mat, &pt->x); + } } /** * Reproject all points of the stroke to a plane locked to axis to avoid stroke offset */ -void ED_gp_project_stroke_to_plane( - const Scene *scene, const Object *ob, - const RegionView3D *rv3d, bGPDstroke *gps, - const float origin[3], const int axis) +void ED_gp_project_stroke_to_plane(const Scene *scene, + const Object *ob, + const RegionView3D *rv3d, + bGPDstroke *gps, + const float origin[3], + const int axis) { - const ToolSettings *ts = scene->toolsettings; - const View3DCursor *cursor = &scene->cursor; - float plane_normal[3]; - float vn[3]; - - float ray[3]; - float rpoint[3]; - - /* normal vector for a plane locked to axis */ - zero_v3(plane_normal); - if (axis < 0) { - /* if the axis is not locked, need a vector to the view direction - * in order to get the right size of the stroke. - */ - ED_view3d_global_to_vector(rv3d, origin, plane_normal); - } - else if (axis < 3) { - plane_normal[axis] = 1.0f; - /* if object, apply object rotation */ - if (ob && (ob->type == OB_GPENCIL)) { - float mat[4][4]; - copy_m4_m4(mat, ob->obmat); - - /* move origin to cursor */ - if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { - copy_v3_v3(mat[3], cursor->location); - } - - mul_mat3_m4_v3(mat, plane_normal); - } - } - else { - float scale[3] = { 1.0f, 1.0f, 1.0f }; - plane_normal[2] = 1.0f; - float mat[4][4]; - loc_eul_size_to_mat4(mat, - cursor->location, - cursor->rotation_euler, - scale); - - /* move origin to object */ - if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { - copy_v3_v3(mat[3], ob->obmat[3]); - } - - mul_mat3_m4_v3(mat, plane_normal); - } - - /* Reproject the points in the plane */ - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - - /* get a vector from the point with the current view direction of the viewport */ - ED_view3d_global_to_vector(rv3d, &pt->x, vn); - - /* calculate line extreme point to create a ray that cross the plane */ - mul_v3_fl(vn, -50.0f); - add_v3_v3v3(ray, &pt->x, vn); - - /* if the line never intersect, the point is not changed */ - if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { - copy_v3_v3(&pt->x, rpoint); - } - } + const ToolSettings *ts = scene->toolsettings; + const View3DCursor *cursor = &scene->cursor; + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else if (axis < 3) { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + float mat[4][4]; + copy_m4_m4(mat, ob->obmat); + + /* move origin to cursor */ + if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { + copy_v3_v3(mat[3], cursor->location); + } + + mul_mat3_m4_v3(mat, plane_normal); + } + } + else { + float scale[3] = {1.0f, 1.0f, 1.0f}; + plane_normal[2] = 1.0f; + float mat[4][4]; + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); + + /* move origin to object */ + if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { + copy_v3_v3(mat[3], ob->obmat[3]); + } + + mul_mat3_m4_v3(mat, plane_normal); + } + + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extreme point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } + } } /** * Reproject given point to a plane locked to axis to avoid stroke offset * \param[in,out] pt: Point to affect */ -void ED_gp_project_point_to_plane( - const Scene *scene, const Object *ob, - const RegionView3D *rv3d, const float origin[3], - const int axis, bGPDspoint *pt) +void ED_gp_project_point_to_plane(const Scene *scene, + const Object *ob, + const RegionView3D *rv3d, + const float origin[3], + const int axis, + bGPDspoint *pt) { - const ToolSettings *ts = scene->toolsettings; - const View3DCursor *cursor = &scene->cursor; - float plane_normal[3]; - float vn[3]; - - float ray[3]; - float rpoint[3]; - - /* normal vector for a plane locked to axis */ - zero_v3(plane_normal); - if (axis < 0) { - /* if the axis is not locked, need a vector to the view direction - * in order to get the right size of the stroke. - */ - ED_view3d_global_to_vector(rv3d, origin, plane_normal); - } - else if (axis < 3) { - plane_normal[axis] = 1.0f; - /* if object, apply object rotation */ - if (ob && (ob->type == OB_GPENCIL)) { - float mat[4][4]; - copy_m4_m4(mat, ob->obmat); - - /* move origin to cursor */ - if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { - copy_v3_v3(mat[3], cursor->location); - } - - mul_mat3_m4_v3(mat, plane_normal); - } - } - else { - float scale[3] = { 1.0f, 1.0f, 1.0f }; - plane_normal[2] = 1.0f; - float mat[4][4]; - loc_eul_size_to_mat4(mat, - cursor->location, - cursor->rotation_euler, - scale); - - /* move origin to object */ - if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { - copy_v3_v3(mat[3], ob->obmat[3]); - } - - mul_mat3_m4_v3(mat, plane_normal); - } - - /* Reproject the points in the plane */ - /* get a vector from the point with the current view direction of the viewport */ - ED_view3d_global_to_vector(rv3d, &pt->x, vn); - - /* calculate line extrem point to create a ray that cross the plane */ - mul_v3_fl(vn, -50.0f); - add_v3_v3v3(ray, &pt->x, vn); - - /* if the line never intersect, the point is not changed */ - if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { - copy_v3_v3(&pt->x, rpoint); - } + const ToolSettings *ts = scene->toolsettings; + const View3DCursor *cursor = &scene->cursor; + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else if (axis < 3) { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + float mat[4][4]; + copy_m4_m4(mat, ob->obmat); + + /* move origin to cursor */ + if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { + copy_v3_v3(mat[3], cursor->location); + } + + mul_mat3_m4_v3(mat, plane_normal); + } + } + else { + float scale[3] = {1.0f, 1.0f, 1.0f}; + plane_normal[2] = 1.0f; + float mat[4][4]; + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); + + /* move origin to object */ + if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { + copy_v3_v3(mat[3], ob->obmat[3]); + } + + mul_mat3_m4_v3(mat, plane_normal); + } + + /* Reproject the points in the plane */ + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } } /* ******************************************************** */ @@ -1072,92 +1088,92 @@ void ED_gp_project_point_to_plane( */ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) { - bGPDspoint *temp_points; - int totnewpoints, oldtotpoints; - int i2; - - /* loop as many times as levels */ - for (int s = 0; s < subdivide; s++) { - totnewpoints = gps->totpoints - 1; - /* duplicate points in a temp area */ - temp_points = MEM_dupallocN(gps->points); - oldtotpoints = gps->totpoints; - - /* resize the points arrays */ - gps->totpoints += totnewpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* move points from last to first to new place */ - i2 = gps->totpoints - 1; - for (int i = oldtotpoints - 1; i > 0; i--) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *pt_final = &gps->points[i2]; - - copy_v3_v3(&pt_final->x, &pt->x); - pt_final->pressure = pt->pressure; - pt_final->strength = pt->strength; - pt_final->time = pt->time; - pt_final->flag = pt->flag; - pt_final->uv_fac = pt->uv_fac; - pt_final->uv_rot = pt->uv_rot; - - if (gps->dvert != NULL) { - MDeformVert *dvert = &gps->dvert[i]; - MDeformVert *dvert_final = &gps->dvert[i2]; - - dvert_final->totweight = dvert->totweight; - dvert_final->dw = dvert->dw; - } - - i2 -= 2; - } - /* interpolate mid points */ - i2 = 1; - for (int i = 0; i < oldtotpoints - 1; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i2]; - - /* add a half way point */ - interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); - pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); - pt_final->strength = interpf(pt->strength, next->strength, 0.5f); - CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt_final->time = interpf(pt->time, next->time, 0.5f); - pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); - pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); - - if (gps->dvert != NULL) { - MDeformVert *dvert_final = &gps->dvert[i2]; - dvert_final->totweight = 0; - dvert_final->dw = NULL; - } - - i2 += 2; - } - - MEM_SAFE_FREE(temp_points); - - /* move points to smooth stroke */ - /* duplicate points in a temp area with the new subdivide data */ - temp_points = MEM_dupallocN(gps->points); - - /* extreme points are not changed */ - for (int i = 0; i < gps->totpoints - 2; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i + 1]; - - /* move point */ - interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); - } - /* free temp memory */ - MEM_SAFE_FREE(temp_points); - } + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + int i2; + + /* loop as many times as levels */ + for (int s = 0; s < subdivide; s++) { + totnewpoints = gps->totpoints - 1; + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* resize the points arrays */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* move points from last to first to new place */ + i2 = gps->totpoints - 1; + for (int i = oldtotpoints - 1; i > 0; i--) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + } + + i2 -= 2; + } + /* interpolate mid points */ + i2 = 1; + for (int i = 0; i < oldtotpoints - 1; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i2]; + + /* add a half way point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); + pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); + + if (gps->dvert != NULL) { + MDeformVert *dvert_final = &gps->dvert[i2]; + dvert_final->totweight = 0; + dvert_final->dw = NULL; + } + + i2 += 2; + } + + MEM_SAFE_FREE(temp_points); + + /* move points to smooth stroke */ + /* duplicate points in a temp area with the new subdivide data */ + temp_points = MEM_dupallocN(gps->points); + + /* extreme points are not changed */ + for (int i = 0; i < gps->totpoints - 2; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i + 1]; + + /* move point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); + } } /** @@ -1167,184 +1183,189 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) */ void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, RNG *rng) { - bGPDspoint *pt1, *pt2, *pt3; - float v1[3]; - float v2[3]; - if (gps->totpoints < 3) { - return; - } - - /* get two vectors using 3 points */ - pt1 = &gps->points[0]; - pt2 = &gps->points[1]; - pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; - - sub_v3_v3v3(v1, &pt2->x, &pt1->x); - sub_v3_v3v3(v2, &pt3->x, &pt2->x); - normalize_v3(v1); - normalize_v3(v2); - - /* get normal vector to plane created by two vectors */ - float normal[3]; - cross_v3_v3v3(normal, v1, v2); - normalize_v3(normal); - - /* get orthogonal vector to plane to rotate random effect */ - float ortho[3]; - cross_v3_v3v3(ortho, v1, normal); - normalize_v3(ortho); - - /* Read all points and apply shift vector (first and last point not modified) */ - for (int i = 1; i < gps->totpoints - 1; i++) { - bGPDspoint *pt = &gps->points[i]; - /* get vector with shift (apply a division because random is too sensitive */ - const float fac = BLI_rng_get_float(rng) * (brush->gpencil_settings->draw_random_sub / 10.0f); - float svec[3]; - copy_v3_v3(svec, ortho); - if (BLI_rng_get_float(rng) > 0.5f) { - mul_v3_fl(svec, -fac); - } - else { - mul_v3_fl(svec, fac); - } - - /* apply shift */ - add_v3_v3(&pt->x, svec); - } + bGPDspoint *pt1, *pt2, *pt3; + float v1[3]; + float v2[3]; + if (gps->totpoints < 3) { + return; + } + + /* get two vectors using 3 points */ + pt1 = &gps->points[0]; + pt2 = &gps->points[1]; + pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; + + sub_v3_v3v3(v1, &pt2->x, &pt1->x); + sub_v3_v3v3(v2, &pt3->x, &pt2->x); + normalize_v3(v1); + normalize_v3(v2); + + /* get normal vector to plane created by two vectors */ + float normal[3]; + cross_v3_v3v3(normal, v1, v2); + normalize_v3(normal); + + /* get orthogonal vector to plane to rotate random effect */ + float ortho[3]; + cross_v3_v3v3(ortho, v1, normal); + normalize_v3(ortho); + + /* Read all points and apply shift vector (first and last point not modified) */ + for (int i = 1; i < gps->totpoints - 1; i++) { + bGPDspoint *pt = &gps->points[i]; + /* get vector with shift (apply a division because random is too sensitive */ + const float fac = BLI_rng_get_float(rng) * (brush->gpencil_settings->draw_random_sub / 10.0f); + float svec[3]; + copy_v3_v3(svec, ortho); + if (BLI_rng_get_float(rng) > 0.5f) { + mul_v3_fl(svec, -fac); + } + else { + mul_v3_fl(svec, fac); + } + + /* apply shift */ + add_v3_v3(&pt->x, svec); + } } /* ******************************************************** */ /* Layer Parenting - Compute Parent Transforms */ /* calculate difference matrix */ -void ED_gpencil_parent_location( - const Depsgraph *depsgraph, Object *obact, bGPdata *UNUSED(gpd), - bGPDlayer *gpl, float diff_mat[4][4]) +void ED_gpencil_parent_location(const Depsgraph *depsgraph, + Object *obact, + bGPdata *UNUSED(gpd), + bGPDlayer *gpl, + float diff_mat[4][4]) { - Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; - Object *obparent = gpl->parent; - Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : obparent; - - /* if not layer parented, try with object parented */ - if (obparent_eval == NULL) { - if (ob_eval != NULL) { - if (ob_eval->type == OB_GPENCIL) { - copy_m4_m4(diff_mat, ob_eval->obmat); - return; - } - } - /* not gpencil object */ - unit_m4(diff_mat); - return; - } - else { - if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { - mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); - return; - } - else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); - if (pchan) { - float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat); - mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); - } - else { - /* if bone not found use object (armature) */ - mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); - } - return; - } - else { - unit_m4(diff_mat); /* not defined type */ - } - } + Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; + Object *obparent = gpl->parent; + Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : + obparent; + + /* if not layer parented, try with object parented */ + if (obparent_eval == NULL) { + if (ob_eval != NULL) { + if (ob_eval->type == OB_GPENCIL) { + copy_m4_m4(diff_mat, ob_eval->obmat); + return; + } + } + /* not gpencil object */ + unit_m4(diff_mat); + return; + } + else { + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); + return; + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat); + mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); + } + else { + /* if bone not found use object (armature) */ + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); + } + return; + } + else { + unit_m4(diff_mat); /* not defined type */ + } + } } /* reset parent matrix for all layers */ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd) { - bGPDspoint *pt; - int i; - float diff_mat[4][4]; - float cur_mat[4][4]; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->parent != NULL) { - /* calculate new matrix */ - if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { - invert_m4_m4(cur_mat, gpl->parent->obmat); - } - else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(gpl->parent->pose, gpl->parsubstr); - if (pchan) { - float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, gpl->parent->obmat, pchan->pose_mat); - invert_m4_m4(cur_mat, tmp_mat); - } - } - - /* only redo if any change */ - if (!equals_m4m4(gpl->inverse, cur_mat)) { - /* first apply current transformation to all strokes */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - mul_m4_v3(diff_mat, &pt->x); - } - } - } - /* set new parent matrix */ - copy_m4_m4(gpl->inverse, cur_mat); - } - } - } + bGPDspoint *pt; + int i; + float diff_mat[4][4]; + float cur_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent != NULL) { + /* calculate new matrix */ + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + invert_m4_m4(cur_mat, gpl->parent->obmat); + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(gpl->parent->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, gpl->parent->obmat, pchan->pose_mat); + invert_m4_m4(cur_mat, tmp_mat); + } + } + + /* only redo if any change */ + if (!equals_m4m4(gpl->inverse, cur_mat)) { + /* first apply current transformation to all strokes */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + mul_m4_v3(diff_mat, &pt->x); + } + } + } + /* set new parent matrix */ + copy_m4_m4(gpl->inverse, cur_mat); + } + } + } } /* ******************************************************** */ /* GP Object Stuff */ /* Helper function to create new OB_GPENCIL Object */ -Object *ED_gpencil_add_object(bContext *C, Scene *UNUSED(scene), const float loc[3], ushort local_view_bits) +Object *ED_gpencil_add_object(bContext *C, + Scene *UNUSED(scene), + const float loc[3], + ushort local_view_bits) { - float rot[3] = {0.0f}; + float rot[3] = {0.0f}; - Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, local_view_bits); + Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, local_view_bits); - /* create default brushes and colors */ - ED_gpencil_add_defaults(C, ob); + /* create default brushes and colors */ + ED_gpencil_add_defaults(C, ob); - return ob; + return ob; } /* Helper function to create default colors and drawing brushes */ void ED_gpencil_add_defaults(bContext *C, Object *ob) { - Main *bmain = CTX_data_main(C); - ToolSettings *ts = CTX_data_tool_settings(C); - - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - Paint *paint = &ts->gp_paint->paint; - /* if not exist, create a new one */ - if (paint->brush == NULL) { - /* create new brushes */ - BKE_brush_gpencil_presets(C); - } - - /* ensure a color exists and is assigned to object */ - BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts); - - /* ensure multiframe falloff curve */ - if (ts->gp_sculpt.cur_falloff == NULL) { - ts->gp_sculpt.cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; - curvemapping_initialize(gp_falloff_curve); - curvemap_reset( - gp_falloff_curve->cm, - &gp_falloff_curve->clipr, - CURVE_PRESET_GAUSS, - CURVEMAP_SLOPE_POSITIVE); - } + Main *bmain = CTX_data_main(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + Paint *paint = &ts->gp_paint->paint; + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + + /* ensure a color exists and is assigned to object */ + BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts); + + /* ensure multiframe falloff curve */ + if (ts->gp_sculpt.cur_falloff == NULL) { + ts->gp_sculpt.cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; + curvemapping_initialize(gp_falloff_curve); + curvemap_reset(gp_falloff_curve->cm, + &gp_falloff_curve->clipr, + CURVE_PRESET_GAUSS, + CURVEMAP_SLOPE_POSITIVE); + } } /* ******************************************************** */ @@ -1353,213 +1374,209 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) /* assign points to vertex group */ void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) { - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) - return; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - bGPDstroke *gps = NULL; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - if (gps->flag & GP_STROKE_SELECT) { - /* verify the weight array is created */ - BKE_gpencil_dvert_ensure(gps); - - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - MDeformVert *dvert = &gps->dvert[i]; - if (pt->flag & GP_SPOINT_SELECT) { - MDeformWeight *dw = defvert_verify_index(dvert, def_nr); - if (dw) { - dw->weight = weight; - } - } - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + bGPDstroke *gps = NULL; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + /* verify the weight array is created */ + BKE_gpencil_dvert_ensure(gps); + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + if (pt->flag & GP_SPOINT_SELECT) { + MDeformWeight *dw = defvert_verify_index(dvert, def_nr); + if (dw) { + dw->weight = weight; + } + } + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; } /* remove points from vertex group */ void ED_gpencil_vgroup_remove(bContext *C, Object *ob) { - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) - return; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - bGPDstroke *gps = NULL; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (gps->dvert == NULL) { - continue; - } - MDeformVert *dvert = &gps->dvert[i]; - - if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) { - MDeformWeight *dw = defvert_find_index(dvert, def_nr); - if (dw != NULL) { - defvert_remove_group(dvert, dw); - } - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + bGPDstroke *gps = NULL; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (gps->dvert == NULL) { + continue; + } + MDeformVert *dvert = &gps->dvert[i]; + + if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) { + MDeformWeight *dw = defvert_find_index(dvert, def_nr); + if (dw != NULL) { + defvert_remove_group(dvert, dw); + } + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; } /* select points of vertex group */ void ED_gpencil_vgroup_select(bContext *C, Object *ob) { - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) - return; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - bGPDstroke *gps = NULL; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (gps->dvert == NULL) { - continue; - } - MDeformVert *dvert = &gps->dvert[i]; - - if (defvert_find_index(dvert, def_nr) != NULL) { - pt->flag |= GP_SPOINT_SELECT; - gps->flag |= GP_STROKE_SELECT; - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + bGPDstroke *gps = NULL; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (gps->dvert == NULL) { + continue; + } + MDeformVert *dvert = &gps->dvert[i]; + + if (defvert_find_index(dvert, def_nr) != NULL) { + pt->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; } /* unselect points of vertex group */ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) { - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) - return; - - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *init_gpf = gpl->actframe; - bGPDstroke *gps = NULL; - if (is_multiedit) { - init_gpf = gpl->frames.first; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - if (gpf == NULL) - continue; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; - - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (gps->dvert == NULL) { - continue; - } - MDeformVert *dvert = &gps->dvert[i]; - - if (defvert_find_index(dvert, def_nr) != NULL) { - pt->flag &= ~GP_SPOINT_SELECT; - } - } - } - } - - /* if not multiedit, exit loop*/ - if (!is_multiedit) { - break; - } - } - } - CTX_DATA_END; + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = gpl->actframe; + bGPDstroke *gps = NULL; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (gps->dvert == NULL) { + continue; + } + MDeformVert *dvert = &gps->dvert[i]; + + if (defvert_find_index(dvert, def_nr) != NULL) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; } /* ******************************************************** */ @@ -1568,903 +1585,902 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) /* check if cursor is in drawing region */ static bool gp_check_cursor_region(bContext *C, int mval_i[2]) { - ARegion *ar = CTX_wm_region(C); - ScrArea *sa = CTX_wm_area(C); - Object *ob = CTX_data_active_object(C); - - if ((ob == NULL) || - (!ELEM(ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) - { - return false; - } - - /* TODO: add more spacetypes */ - if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { - return false; - } - if ((ar) && (ar->regiontype != RGN_TYPE_WINDOW)) { - return false; - } - else if (ar) { - return BLI_rcti_isect_pt_v(&ar->winrct, mval_i); - } - else { - return false; - } + ARegion *ar = CTX_wm_region(C); + ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); + + if ((ob == NULL) || + (!ELEM(ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) { + return false; + } + + /* TODO: add more spacetypes */ + if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { + return false; + } + if ((ar) && (ar->regiontype != RGN_TYPE_WINDOW)) { + return false; + } + else if (ar) { + return BLI_rcti_isect_pt_v(&ar->winrct, mval_i); + } + else { + return false; + } } /* draw eraser cursor */ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) { - short radius = (short)brush->size; + short radius = (short)brush->size; - GPUVertFormat *format = immVertexFormat(); - const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_line_smooth(true); - GPU_blend(true); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + GPU_line_smooth(true); + GPU_blend(true); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - immUniformColor4ub(255, 100, 100, 20); - imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); - immUnbindProgram(); + immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - glGetFloatv(GL_VIEWPORT, viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniform1f("dash_width", 12.0f); - immUniform1f("dash_factor", 0.5f); + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); - imm_draw_circle_wire_2d( - shdr_pos, x, y, radius, - /* XXX Dashed shader gives bad results with sets of small segments currently, - * temp hack around the issue. :( */ - max_ii(8, radius / 2)); /* was fixed 40 */ + imm_draw_circle_wire_2d( + shdr_pos, + x, + y, + radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, radius / 2)); /* was fixed 40 */ - immUnbindProgram(); + immUnbindProgram(); - GPU_blend(false); - GPU_line_smooth(false); + GPU_blend(false); + GPU_line_smooth(false); } static bool gp_brush_cursor_poll(bContext *C) { - if (WM_toolsystem_active_tool_is_brush(C)) { - return true; - } - return false; + if (WM_toolsystem_active_tool_is_brush(C)) { + return true; + } + return false; } /* Helper callback for drawing the cursor itself */ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata) { - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - ARegion *ar = CTX_wm_region(C); - - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - bGPdata *gpd = ED_gpencil_data_get_active(C); - GP_Sculpt_Data *gp_brush = NULL; - Brush *brush = NULL; - Material *ma = NULL; - MaterialGPencilStyle *gp_style = NULL; - float *last_mouse_position = customdata; - - if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { - gp_brush = &gset->brush[gset->weighttype]; - } - else { - gp_brush = &gset->brush[gset->brushtype]; - } - - /* default radius and color */ - float color[3] = {1.0f, 1.0f, 1.0f}; - float darkcolor[3]; - float radius = 3.0f; - - int mval_i[2] = {x, y}; - /* check if cursor is in drawing region and has valid datablock */ - if ((!gp_check_cursor_region(C, mval_i)) || (gpd == NULL)) { - return; - } - - /* for paint use paint brush size and color */ - if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { - brush = scene->toolsettings->gp_paint->paint.brush; - if ((brush == NULL) || (brush->gpencil_settings == NULL)) { - return; - } - - /* while drawing hide */ - if ((gpd->runtime.sbuffer_size > 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0)) - { - return; - } - - if ((brush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { - return; - } - - /* eraser has special shape and use a different shader program */ - if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { - ED_gpencil_brush_draw_eraser(brush, x, y); - return; - } - - /* get current drawing color */ - ma = BKE_gpencil_object_material_get_from_brush(ob, brush); - - if (ma) { - gp_style = ma->gp_style; - - /* after some testing, display the size of the brush is not practical because - * is too disruptive and the size of cursor does not change with zoom factor. - * The decision was to use a fix size, instead of brush->thickness value. - */ - if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && - (brush->gpencil_tool == GPAINT_TOOL_DRAW)) - { - radius = 2.0f; - copy_v3_v3(color, gp_style->stroke_rgba); - } - else { - radius = 5.0f; - copy_v3_v3(color, brush->add_col); - } - } - } - - /* for sculpt use sculpt brush size */ - if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { - if (gp_brush) { - if ((gp_brush->flag & GP_SCULPT_FLAG_ENABLE_CURSOR) == 0) { - return; - } - - radius = gp_brush->size; - if (gp_brush->flag & (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) { - copy_v3_v3(color, gp_brush->curcolor_sub); - } - else { - copy_v3_v3(color, gp_brush->curcolor_add); - } - } - } - - /* draw icon */ - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_smooth(true); - GPU_blend(true); - - /* Inner Ring: Color from UI panel */ - immUniformColor4f(color[0], color[1], color[2], 0.8f); - if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && - (brush->gpencil_tool == GPAINT_TOOL_DRAW)) - { - imm_draw_circle_fill_2d(pos, x, y, radius, 40); - } - else { - imm_draw_circle_wire_2d(pos, x, y, radius, 40); - } - - /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ - mul_v3_v3fl(darkcolor, color, 0.40f); - immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); - imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); - - GPU_blend(false); - GPU_line_smooth(false); - - /* Draw line for lazy mouse */ - if ((last_mouse_position) && - (brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) - { - GPU_line_smooth(true); - GPU_blend(true); - - copy_v3_v3(color, brush->add_col); - immUniformColor4f(color[0], color[1], color[2], 0.8f); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, x, y); - immVertex2f( - pos, - last_mouse_position[0] + ar->winrct.xmin, - last_mouse_position[1] + ar->winrct.ymin); - immEnd(); - - GPU_blend(false); - GPU_line_smooth(false); - } - - immUnbindProgram(); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ARegion *ar = CTX_wm_region(C); + + GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; + bGPdata *gpd = ED_gpencil_data_get_active(C); + GP_Sculpt_Data *gp_brush = NULL; + Brush *brush = NULL; + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; + float *last_mouse_position = customdata; + + if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { + gp_brush = &gset->brush[gset->weighttype]; + } + else { + gp_brush = &gset->brush[gset->brushtype]; + } + + /* default radius and color */ + float color[3] = {1.0f, 1.0f, 1.0f}; + float darkcolor[3]; + float radius = 3.0f; + + int mval_i[2] = {x, y}; + /* check if cursor is in drawing region and has valid datablock */ + if ((!gp_check_cursor_region(C, mval_i)) || (gpd == NULL)) { + return; + } + + /* for paint use paint brush size and color */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + brush = scene->toolsettings->gp_paint->paint.brush; + if ((brush == NULL) || (brush->gpencil_settings == NULL)) { + return; + } + + /* while drawing hide */ + if ((gpd->runtime.sbuffer_size > 0) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0)) { + return; + } + + if ((brush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { + return; + } + + /* eraser has special shape and use a different shader program */ + if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { + ED_gpencil_brush_draw_eraser(brush, x, y); + return; + } + + /* get current drawing color */ + ma = BKE_gpencil_object_material_get_from_brush(ob, brush); + + if (ma) { + gp_style = ma->gp_style; + + /* after some testing, display the size of the brush is not practical because + * is too disruptive and the size of cursor does not change with zoom factor. + * The decision was to use a fix size, instead of brush->thickness value. + */ + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (brush->gpencil_tool == GPAINT_TOOL_DRAW)) { + radius = 2.0f; + copy_v3_v3(color, gp_style->stroke_rgba); + } + else { + radius = 5.0f; + copy_v3_v3(color, brush->add_col); + } + } + } + + /* for sculpt use sculpt brush size */ + if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { + if (gp_brush) { + if ((gp_brush->flag & GP_SCULPT_FLAG_ENABLE_CURSOR) == 0) { + return; + } + + radius = gp_brush->size; + if (gp_brush->flag & (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) { + copy_v3_v3(color, gp_brush->curcolor_sub); + } + else { + copy_v3_v3(color, gp_brush->curcolor_add); + } + } + } + + /* draw icon */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_line_smooth(true); + GPU_blend(true); + + /* Inner Ring: Color from UI panel */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (brush->gpencil_tool == GPAINT_TOOL_DRAW)) { + imm_draw_circle_fill_2d(pos, x, y, radius, 40); + } + else { + imm_draw_circle_wire_2d(pos, x, y, radius, 40); + } + + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + mul_v3_v3fl(darkcolor, color, 0.40f); + immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); + imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); + + GPU_blend(false); + GPU_line_smooth(false); + + /* Draw line for lazy mouse */ + if ((last_mouse_position) && (brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) { + GPU_line_smooth(true); + GPU_blend(true); + + copy_v3_v3(color, brush->add_col); + immUniformColor4f(color[0], color[1], color[2], 0.8f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, x, y); + immVertex2f( + pos, last_mouse_position[0] + ar->winrct.xmin, last_mouse_position[1] + ar->winrct.ymin); + immEnd(); + + GPU_blend(false); + GPU_line_smooth(false); + } + + immUnbindProgram(); } /* Turn brush cursor in on/off */ void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata) { - Scene *scene = CTX_data_scene(C); - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - float *lastpost = customdata; - - if (gset->paintcursor && !enable) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); - gset->paintcursor = NULL; - } - else if (enable) { - /* in some situations cursor could be duplicated, so it is better disable first if exist */ - if (gset->paintcursor) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); - gset->paintcursor = NULL; - } - /* enable cursor */ - gset->paintcursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - gp_brush_cursor_poll, - gp_brush_cursor_draw, - (lastpost) ? customdata : NULL); - } + Scene *scene = CTX_data_scene(C); + GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; + float *lastpost = customdata; + + if (gset->paintcursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + else if (enable) { + /* in some situations cursor could be duplicated, so it is better disable first if exist */ + if (gset->paintcursor) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + /* enable cursor */ + gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + gp_brush_cursor_poll, + gp_brush_cursor_draw, + (lastpost) ? customdata : NULL); + } } /* verify if is using the right brush */ static void gpencil_verify_brush_type(bContext *C, int newmode) { - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - - switch (newmode) { - case OB_MODE_SCULPT_GPENCIL: - gset->flag &= ~GP_SCULPT_SETT_FLAG_WEIGHT_MODE; - if ((gset->brushtype < 0) || (gset->brushtype >= GP_SCULPT_TYPE_WEIGHT)) { - gset->brushtype = GP_SCULPT_TYPE_PUSH; - } - break; - case OB_MODE_WEIGHT_GPENCIL: - gset->flag |= GP_SCULPT_SETT_FLAG_WEIGHT_MODE; - if ((gset->weighttype < GP_SCULPT_TYPE_WEIGHT) || (gset->weighttype >= GP_SCULPT_TYPE_MAX)) { - gset->weighttype = GP_SCULPT_TYPE_WEIGHT; - } - break; - default: - break; - } + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + + switch (newmode) { + case OB_MODE_SCULPT_GPENCIL: + gset->flag &= ~GP_SCULPT_SETT_FLAG_WEIGHT_MODE; + if ((gset->brushtype < 0) || (gset->brushtype >= GP_SCULPT_TYPE_WEIGHT)) { + gset->brushtype = GP_SCULPT_TYPE_PUSH; + } + break; + case OB_MODE_WEIGHT_GPENCIL: + gset->flag |= GP_SCULPT_SETT_FLAG_WEIGHT_MODE; + if ((gset->weighttype < GP_SCULPT_TYPE_WEIGHT) || (gset->weighttype >= GP_SCULPT_TYPE_MAX)) { + gset->weighttype = GP_SCULPT_TYPE_WEIGHT; + } + break; + default: + break; + } } /* set object modes */ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) { - if (!gpd) { - return; - } - - switch (newmode) { - case OB_MODE_EDIT_GPENCIL: - gpd->flag |= GP_DATA_STROKE_EDITMODE; - gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; - gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; - gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - ED_gpencil_toggle_brush_cursor(C, false, NULL); - break; - case OB_MODE_PAINT_GPENCIL: - gpd->flag &= ~GP_DATA_STROKE_EDITMODE; - gpd->flag |= GP_DATA_STROKE_PAINTMODE; - gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; - gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - ED_gpencil_toggle_brush_cursor(C, true, NULL); - break; - case OB_MODE_SCULPT_GPENCIL: - gpd->flag &= ~GP_DATA_STROKE_EDITMODE; - gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; - gpd->flag |= GP_DATA_STROKE_SCULPTMODE; - gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - gpencil_verify_brush_type(C, OB_MODE_SCULPT_GPENCIL); - ED_gpencil_toggle_brush_cursor(C, true, NULL); - break; - case OB_MODE_WEIGHT_GPENCIL: - gpd->flag &= ~GP_DATA_STROKE_EDITMODE; - gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; - gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; - gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; - gpencil_verify_brush_type(C, OB_MODE_WEIGHT_GPENCIL); - ED_gpencil_toggle_brush_cursor(C, true, NULL); - break; - default: - gpd->flag &= ~GP_DATA_STROKE_EDITMODE; - gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; - gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; - gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - ED_gpencil_toggle_brush_cursor(C, false, NULL); - break; - } + if (!gpd) { + return; + } + + switch (newmode) { + case OB_MODE_EDIT_GPENCIL: + gpd->flag |= GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; + case OB_MODE_PAINT_GPENCIL: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag |= GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_SCULPT_GPENCIL: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag |= GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpencil_verify_brush_type(C, OB_MODE_SCULPT_GPENCIL); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_WEIGHT_GPENCIL: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; + gpencil_verify_brush_type(C, OB_MODE_WEIGHT_GPENCIL); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + default: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; + } } /* helper to convert 2d to 3d for simple drawing buffer */ -static void gpencil_stroke_convertcoords(ARegion *ar, const tGPspoint *point2D, float origin[3], float out[3]) +static void gpencil_stroke_convertcoords(ARegion *ar, + const tGPspoint *point2D, + float origin[3], + float out[3]) { - float mval_f[2] = { (float)point2D->x, (float)point2D->y }; - float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; - - copy_v3_v3(rvec, origin); - - zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); - - if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); - sub_v3_v3v3(out, rvec, dvec); - } - else { - zero_v3(out); - } + float mval_f[2] = {(float)point2D->x, (float)point2D->y}; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; + + copy_v3_v3(rvec, origin); + + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } } /* convert 2d tGPspoint to 3d bGPDspoint */ void ED_gpencil_tpoint_to_point(ARegion *ar, float origin[3], const tGPspoint *tpt, bGPDspoint *pt) { - float p3d[3]; - /* conversion to 3d format */ - gpencil_stroke_convertcoords(ar, tpt, origin, p3d); - copy_v3_v3(&pt->x, p3d); - - pt->pressure = tpt->pressure; - pt->strength = tpt->strength; - pt->uv_fac = tpt->uv_fac; - pt->uv_rot = tpt->uv_rot; + float p3d[3]; + /* conversion to 3d format */ + gpencil_stroke_convertcoords(ar, tpt, origin, p3d); + copy_v3_v3(&pt->x, p3d); + + pt->pressure = tpt->pressure; + pt->strength = tpt->strength; + pt->uv_fac = tpt->uv_fac; + pt->uv_rot = tpt->uv_rot; } /* texture coordinate utilities */ void ED_gpencil_calc_stroke_uv(Object *ob, bGPDstroke *gps) { - if (gps == NULL) { - return; - } - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); - float pixsize; - if (gp_style) { - pixsize = gp_style->texture_pixsize / 1000000.0f; - } - else { - /* use this value by default */ - pixsize = 0.0001f; - } - pixsize = MAX2(pixsize, 0.0000001f); - - bGPDspoint *pt = NULL; - bGPDspoint *ptb = NULL; - int i; - float totlen = 0.0f; - - /* first read all points and calc distance */ - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - /* first point */ - if (i == 0) { - pt->uv_fac = 0.0f; - continue; - } - - ptb = &gps->points[i - 1]; - totlen += len_v3v3(&pt->x, &ptb->x) / pixsize; - pt->uv_fac = totlen; - } - - /* normalize the distance using a factor */ - float factor; - - /* if image, use texture width */ - if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && - (gp_style->sima)) - { - factor = gp_style->sima->gen_x; - } - else if (totlen == 0) { - return; - } - else { - factor = totlen; - } - - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - pt->uv_fac /= factor; - } + if (gps == NULL) { + return; + } + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + float pixsize; + if (gp_style) { + pixsize = gp_style->texture_pixsize / 1000000.0f; + } + else { + /* use this value by default */ + pixsize = 0.0001f; + } + pixsize = MAX2(pixsize, 0.0000001f); + + bGPDspoint *pt = NULL; + bGPDspoint *ptb = NULL; + int i; + float totlen = 0.0f; + + /* first read all points and calc distance */ + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + /* first point */ + if (i == 0) { + pt->uv_fac = 0.0f; + continue; + } + + ptb = &gps->points[i - 1]; + totlen += len_v3v3(&pt->x, &ptb->x) / pixsize; + pt->uv_fac = totlen; + } + + /* normalize the distance using a factor */ + float factor; + + /* if image, use texture width */ + if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && + (gp_style->sima)) { + factor = gp_style->sima->gen_x; + } + else if (totlen == 0) { + return; + } + else { + factor = totlen; + } + + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + pt->uv_fac /= factor; + } } /* recalc uv for any stroke using the material */ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) { - Material *gps_ma = NULL; - /* read all strokes */ - for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; - if (gpd == NULL) { - continue; - } - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl)) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* check if it is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - gps_ma = give_current_material(ob, gps->mat_nr + 1); - /* update */ - if ((gps_ma) && (gps_ma == mat)) { - ED_gpencil_calc_stroke_uv(ob, gps); - } - } - } - } - } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - } + Material *gps_ma = NULL; + /* read all strokes */ + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + if (gpd == NULL) { + continue; + } + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl)) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if it is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + gps_ma = give_current_material(ob, gps->mat_nr + 1); + /* update */ + if ((gps_ma) && (gps_ma == mat)) { + ED_gpencil_calc_stroke_uv(ob, gps); + } + } + } + } + } + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + } } -static bool gpencil_check_collision( - bGPDstroke *gps, bGPDstroke **gps_array, GHash *all_2d, - int totstrokes, float p2d_a1[2], float p2d_a2[2], float r_hit[2]) +static bool gpencil_check_collision(bGPDstroke *gps, + bGPDstroke **gps_array, + GHash *all_2d, + int totstrokes, + float p2d_a1[2], + float p2d_a2[2], + float r_hit[2]) { - bool hit = false; - /* check segment with all segments of all strokes */ - for (int s = 0; s < totstrokes; s++) { - bGPDstroke *gps_iter = gps_array[s]; - if (gps_iter->totpoints < 2) { - continue; - } - /* get stroke 2d version */ - float(*points2d)[2] = BLI_ghash_lookup(all_2d, gps_iter); - - for (int i2 = 0; i2 < gps_iter->totpoints - 1; i2++) { - float p2d_b1[2], p2d_b2[2]; - copy_v2_v2(p2d_b1, points2d[i2]); - copy_v2_v2(p2d_b2, points2d[i2 + 1]); - - /* don't self check */ - if (gps == gps_iter) { - if (equals_v2v2(p2d_a1, p2d_b1) || equals_v2v2(p2d_a1, p2d_b2)) { - continue; - } - if (equals_v2v2(p2d_a2, p2d_b1) || equals_v2v2(p2d_a2, p2d_b2)) { - continue; - } - } - /* check collision */ - int check = isect_seg_seg_v2_point(p2d_a1, p2d_a2, p2d_b1, p2d_b2, r_hit); - if (check > 0) { - hit = true; - break; - } - } - - if (hit) { - break; - } - } - - if (!hit) { - zero_v2(r_hit); - } - - return hit; + bool hit = false; + /* check segment with all segments of all strokes */ + for (int s = 0; s < totstrokes; s++) { + bGPDstroke *gps_iter = gps_array[s]; + if (gps_iter->totpoints < 2) { + continue; + } + /* get stroke 2d version */ + float(*points2d)[2] = BLI_ghash_lookup(all_2d, gps_iter); + + for (int i2 = 0; i2 < gps_iter->totpoints - 1; i2++) { + float p2d_b1[2], p2d_b2[2]; + copy_v2_v2(p2d_b1, points2d[i2]); + copy_v2_v2(p2d_b2, points2d[i2 + 1]); + + /* don't self check */ + if (gps == gps_iter) { + if (equals_v2v2(p2d_a1, p2d_b1) || equals_v2v2(p2d_a1, p2d_b2)) { + continue; + } + if (equals_v2v2(p2d_a2, p2d_b1) || equals_v2v2(p2d_a2, p2d_b2)) { + continue; + } + } + /* check collision */ + int check = isect_seg_seg_v2_point(p2d_a1, p2d_a2, p2d_b1, p2d_b2, r_hit); + if (check > 0) { + hit = true; + break; + } + } + + if (hit) { + break; + } + } + + if (!hit) { + zero_v2(r_hit); + } + + return hit; } -static void gp_copy_points( - bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final, int i, int i2) +static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final, int i, int i2) { - /* don't copy same point */ - if (i == i2) { - return; - } - - copy_v3_v3(&pt_final->x, &pt->x); - pt_final->pressure = pt->pressure; - pt_final->strength = pt->strength; - pt_final->time = pt->time; - pt_final->flag = pt->flag; - pt_final->uv_fac = pt->uv_fac; - pt_final->uv_rot = pt->uv_rot; - - if (gps->dvert != NULL) { - MDeformVert *dvert = &gps->dvert[i]; - MDeformVert *dvert_final = &gps->dvert[i2]; - MEM_SAFE_FREE(dvert_final->dw); - - dvert_final->totweight = dvert->totweight; - if (dvert->dw == NULL) { - dvert_final->dw = NULL; - dvert_final->totweight = 0; - } - else { - dvert_final->dw = MEM_dupallocN(dvert->dw); - } - } - + /* don't copy same point */ + if (i == i2) { + return; + } + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + MEM_SAFE_FREE(dvert_final->dw); + + dvert_final->totweight = dvert->totweight; + if (dvert->dw == NULL) { + dvert_final->dw = NULL; + dvert_final->totweight = 0; + } + else { + dvert_final->dw = MEM_dupallocN(dvert->dw); + } + } } static void gp_insert_point( - bGPDstroke *gps, - bGPDspoint *a_pt, bGPDspoint *b_pt, - float co_a[3], float co_b[3]) + bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, float co_a[3], float co_b[3]) { - bGPDspoint *temp_points; - int totnewpoints, oldtotpoints; - - totnewpoints = gps->totpoints; - if (a_pt) { - totnewpoints++; - } - if (b_pt) { - totnewpoints++; - } - - /* duplicate points in a temp area */ - temp_points = MEM_dupallocN(gps->points); - oldtotpoints = gps->totpoints; - - /* look index of base points because memory is changed when resize points array */ - int a_idx = -1; - int b_idx = -1; - for (int i = 0; i < oldtotpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (pt == a_pt) { - a_idx = i; - } - if (pt == b_pt) { - b_idx = i; - } - } - - /* resize the points arrays */ - gps->totpoints = totnewpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* copy all points */ - int i2 = 0; - for (int i = 0; i < oldtotpoints; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *pt_final = &gps->points[i2]; - gp_copy_points(gps, pt, pt_final, i, i2); - - /* create new point duplicating point and copy location */ - if ((i == a_idx) || (i == b_idx)) { - i2++; - pt_final = &gps->points[i2]; - gp_copy_points(gps, pt, pt_final, i, i2); - copy_v3_v3(&pt_final->x, (i == a_idx) ? co_a : co_b); - - /* unselect */ - pt_final->flag &= ~GP_SPOINT_SELECT; - /* tag to avoid more checking with this point */ - pt_final->flag |= GP_SPOINT_TAG; - } - - i2++; - } - - MEM_SAFE_FREE(temp_points); + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + + totnewpoints = gps->totpoints; + if (a_pt) { + totnewpoints++; + } + if (b_pt) { + totnewpoints++; + } + + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* look index of base points because memory is changed when resize points array */ + int a_idx = -1; + int b_idx = -1; + for (int i = 0; i < oldtotpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (pt == a_pt) { + a_idx = i; + } + if (pt == b_pt) { + b_idx = i; + } + } + + /* resize the points arrays */ + gps->totpoints = totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* copy all points */ + int i2 = 0; + for (int i = 0; i < oldtotpoints; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + gp_copy_points(gps, pt, pt_final, i, i2); + + /* create new point duplicating point and copy location */ + if ((i == a_idx) || (i == b_idx)) { + i2++; + pt_final = &gps->points[i2]; + gp_copy_points(gps, pt, pt_final, i, i2); + copy_v3_v3(&pt_final->x, (i == a_idx) ? co_a : co_b); + + /* unselect */ + pt_final->flag &= ~GP_SPOINT_SELECT; + /* tag to avoid more checking with this point */ + pt_final->flag |= GP_SPOINT_TAG; + } + + i2++; + } + + MEM_SAFE_FREE(temp_points); } static float gp_calc_factor(float p2d_a1[2], float p2d_a2[2], float r_hit2d[2]) { - float dist1 = len_squared_v2v2(p2d_a1, p2d_a2); - float dist2 = len_squared_v2v2(p2d_a1, r_hit2d); - float f = dist1 > 0.0f ? dist2 / dist1 : 0.0f; - - /* apply a correction factor */ - float v1[2]; - interp_v2_v2v2(v1, p2d_a1, p2d_a2, f); - float dist3 = len_squared_v2v2(p2d_a1, v1); - float f1 = dist1 > 0.0f ? dist3 / dist1 : 0.0f; - f = f + (f - f1); - - return f; + float dist1 = len_squared_v2v2(p2d_a1, p2d_a2); + float dist2 = len_squared_v2v2(p2d_a1, r_hit2d); + float f = dist1 > 0.0f ? dist2 / dist1 : 0.0f; + + /* apply a correction factor */ + float v1[2]; + interp_v2_v2v2(v1, p2d_a1, p2d_a2, f); + float dist3 = len_squared_v2v2(p2d_a1, v1); + float f1 = dist1 > 0.0f ? dist3 / dist1 : 0.0f; + f = f + (f - f1); + + return f; } /* extend selection to stroke intersections */ -int ED_gpencil_select_stroke_segment( - bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *pt, - bool select, bool insert, const float scale, - float r_hita[3], float r_hitb[3]) +int ED_gpencil_select_stroke_segment(bGPDlayer *gpl, + bGPDstroke *gps, + bGPDspoint *pt, + bool select, + bool insert, + const float scale, + float r_hita[3], + float r_hitb[3]) { - const float min_factor = 0.0015f; - bGPDspoint *pta1 = NULL; - bGPDspoint *pta2 = NULL; - float f = 0.0f; - int i2 = 0; - - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - return 0; - } - - int memsize = BLI_listbase_count(&gpf->strokes); - bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * memsize, __func__); - - /* save points */ - bGPDspoint *oldpoints = MEM_dupallocN(gps->points); - - /* Save list of strokes to check */ - int totstrokes = 0; - for (bGPDstroke *gps_iter = gpf->strokes.first; gps_iter; gps_iter = gps_iter->next) { - if (gps_iter->totpoints < 2) { - continue; - } - gps_array[totstrokes] = gps_iter; - totstrokes++; - } - - if (totstrokes == 0) { - return 0; - } - - /* look for index of the current point */ - int cur_idx = -1; - for (int i = 0; i < gps->totpoints; i++) { - pta1 = &gps->points[i]; - if (pta1 == pt) { - cur_idx = i; - break; - } - } - if (cur_idx < 0) { - return 0; - } - - /* convert all gps points to 2d and save in a hash to avoid recalculation */ - int direction = 0; - float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); - BKE_gpencil_stroke_2d_flat_ref( - gps->points, gps->totpoints, - gps->points, gps->totpoints, points2d, scale, &direction); - - GHash *all_2d = BLI_ghash_ptr_new(__func__); - - for (int s = 0; s < totstrokes; s++) { - bGPDstroke *gps_iter = gps_array[s]; - float(*points2d_iter)[2] = MEM_mallocN(sizeof(*points2d_iter) * gps_iter->totpoints, __func__); - - /* the extremes of the stroke are scaled to improve collision detection - * for near lines */ - BKE_gpencil_stroke_2d_flat_ref( - gps->points, gps->totpoints, - gps_iter->points, gps_iter->totpoints, points2d_iter, - scale, &direction); - BLI_ghash_insert(all_2d, gps_iter, points2d_iter); - } - - bool hit_a = false; - bool hit_b = false; - float p2d_a1[2] = {0.0f, 0.0f}; - float p2d_a2[2] = {0.0f, 0.0f}; - float r_hit2d[2]; - bGPDspoint *hit_pointa = NULL; - bGPDspoint *hit_pointb = NULL; - - /* analyze points before current */ - if (cur_idx > 0) { - for (int i = cur_idx; i >= 0; i--) { - pta1 = &gps->points[i]; - copy_v2_v2(p2d_a1, points2d[i]); - - i2 = i - 1; - CLAMP_MIN(i2, 0); - pta2 = &gps->points[i2]; - copy_v2_v2(p2d_a2, points2d[i2]); - - hit_a = gpencil_check_collision( - gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d); - - if (select) { - pta1->flag |= GP_SPOINT_SELECT; - } - else { - pta1->flag &= ~GP_SPOINT_SELECT; - } - - if (hit_a) { - f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d); - interp_v3_v3v3(r_hita, &pta1->x, &pta2->x, f); - if (f > min_factor) { - hit_pointa = pta2; /* first point is second (inverted loop) */ - } - else { - pta1->flag &= ~GP_SPOINT_SELECT; - } - break; - } - } - } - - /* analyze points after current */ - for (int i = cur_idx; i < gps->totpoints; i++) { - pta1 = &gps->points[i]; - copy_v2_v2(p2d_a1, points2d[i]); - - i2 = i + 1; - CLAMP_MAX(i2, gps->totpoints - 1); - pta2 = &gps->points[i2]; - copy_v2_v2(p2d_a2, points2d[i2]); - - hit_b = gpencil_check_collision( - gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d); - - if (select) { - pta1->flag |= GP_SPOINT_SELECT; - } - else { - pta1->flag &= ~GP_SPOINT_SELECT; - } - - if (hit_b) { - f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d); - interp_v3_v3v3(r_hitb, &pta1->x, &pta2->x, f); - if (f > min_factor) { - hit_pointb = pta1; - } - else { - pta1->flag &= ~GP_SPOINT_SELECT; - } - break; - } - } - - /* insert new point in the collision points */ - if (insert) { - gp_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb); - } - - /* free memory */ - if (all_2d) { - GHashIterator gh_iter; - GHASH_ITER(gh_iter, all_2d) { - float(*p2d)[2] = BLI_ghashIterator_getValue(&gh_iter); - MEM_SAFE_FREE(p2d); - } - BLI_ghash_free(all_2d, NULL, NULL); - } - - /* if no hit, reset selection flag */ - if ((!hit_a) && (!hit_b)) { - for (int i = 0; i < gps->totpoints; i++) { - pta1 = &gps->points[i]; - pta2 = &oldpoints[i]; - pta1->flag = pta2->flag; - } - } - - MEM_SAFE_FREE(points2d); - MEM_SAFE_FREE(gps_array); - MEM_SAFE_FREE(oldpoints); - - /* return type of hit */ - if ((hit_a) && (hit_b)) { - return 3; - } - else if (hit_a) { - return 1; - } - else if (hit_b) { - return 2; - } - else { - return 0; - } + const float min_factor = 0.0015f; + bGPDspoint *pta1 = NULL; + bGPDspoint *pta2 = NULL; + float f = 0.0f; + int i2 = 0; + + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + return 0; + } + + int memsize = BLI_listbase_count(&gpf->strokes); + bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * memsize, __func__); + + /* save points */ + bGPDspoint *oldpoints = MEM_dupallocN(gps->points); + + /* Save list of strokes to check */ + int totstrokes = 0; + for (bGPDstroke *gps_iter = gpf->strokes.first; gps_iter; gps_iter = gps_iter->next) { + if (gps_iter->totpoints < 2) { + continue; + } + gps_array[totstrokes] = gps_iter; + totstrokes++; + } + + if (totstrokes == 0) { + return 0; + } + + /* look for index of the current point */ + int cur_idx = -1; + for (int i = 0; i < gps->totpoints; i++) { + pta1 = &gps->points[i]; + if (pta1 == pt) { + cur_idx = i; + break; + } + } + if (cur_idx < 0) { + return 0; + } + + /* convert all gps points to 2d and save in a hash to avoid recalculation */ + int direction = 0; + float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, + "GP Stroke temp 2d points"); + BKE_gpencil_stroke_2d_flat_ref( + gps->points, gps->totpoints, gps->points, gps->totpoints, points2d, scale, &direction); + + GHash *all_2d = BLI_ghash_ptr_new(__func__); + + for (int s = 0; s < totstrokes; s++) { + bGPDstroke *gps_iter = gps_array[s]; + float(*points2d_iter)[2] = MEM_mallocN(sizeof(*points2d_iter) * gps_iter->totpoints, __func__); + + /* the extremes of the stroke are scaled to improve collision detection + * for near lines */ + BKE_gpencil_stroke_2d_flat_ref(gps->points, + gps->totpoints, + gps_iter->points, + gps_iter->totpoints, + points2d_iter, + scale, + &direction); + BLI_ghash_insert(all_2d, gps_iter, points2d_iter); + } + + bool hit_a = false; + bool hit_b = false; + float p2d_a1[2] = {0.0f, 0.0f}; + float p2d_a2[2] = {0.0f, 0.0f}; + float r_hit2d[2]; + bGPDspoint *hit_pointa = NULL; + bGPDspoint *hit_pointb = NULL; + + /* analyze points before current */ + if (cur_idx > 0) { + for (int i = cur_idx; i >= 0; i--) { + pta1 = &gps->points[i]; + copy_v2_v2(p2d_a1, points2d[i]); + + i2 = i - 1; + CLAMP_MIN(i2, 0); + pta2 = &gps->points[i2]; + copy_v2_v2(p2d_a2, points2d[i2]); + + hit_a = gpencil_check_collision(gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d); + + if (select) { + pta1->flag |= GP_SPOINT_SELECT; + } + else { + pta1->flag &= ~GP_SPOINT_SELECT; + } + + if (hit_a) { + f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d); + interp_v3_v3v3(r_hita, &pta1->x, &pta2->x, f); + if (f > min_factor) { + hit_pointa = pta2; /* first point is second (inverted loop) */ + } + else { + pta1->flag &= ~GP_SPOINT_SELECT; + } + break; + } + } + } + + /* analyze points after current */ + for (int i = cur_idx; i < gps->totpoints; i++) { + pta1 = &gps->points[i]; + copy_v2_v2(p2d_a1, points2d[i]); + + i2 = i + 1; + CLAMP_MAX(i2, gps->totpoints - 1); + pta2 = &gps->points[i2]; + copy_v2_v2(p2d_a2, points2d[i2]); + + hit_b = gpencil_check_collision(gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d); + + if (select) { + pta1->flag |= GP_SPOINT_SELECT; + } + else { + pta1->flag &= ~GP_SPOINT_SELECT; + } + + if (hit_b) { + f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d); + interp_v3_v3v3(r_hitb, &pta1->x, &pta2->x, f); + if (f > min_factor) { + hit_pointb = pta1; + } + else { + pta1->flag &= ~GP_SPOINT_SELECT; + } + break; + } + } + + /* insert new point in the collision points */ + if (insert) { + gp_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb); + } + + /* free memory */ + if (all_2d) { + GHashIterator gh_iter; + GHASH_ITER (gh_iter, all_2d) { + float(*p2d)[2] = BLI_ghashIterator_getValue(&gh_iter); + MEM_SAFE_FREE(p2d); + } + BLI_ghash_free(all_2d, NULL, NULL); + } + + /* if no hit, reset selection flag */ + if ((!hit_a) && (!hit_b)) { + for (int i = 0; i < gps->totpoints; i++) { + pta1 = &gps->points[i]; + pta2 = &oldpoints[i]; + pta1->flag = pta2->flag; + } + } + + MEM_SAFE_FREE(points2d); + MEM_SAFE_FREE(gps_array); + MEM_SAFE_FREE(oldpoints); + + /* return type of hit */ + if ((hit_a) && (hit_b)) { + return 3; + } + else if (hit_a) { + return 1; + } + else if (hit_b) { + return 2; + } + else { + return 0; + } } void ED_gpencil_select_toggle_all(bContext *C, int action) { - /* for "toggle", test for existing selected strokes */ - if (action == SEL_TOGGLE) { - action = SEL_SELECT; - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - if (gps->flag & GP_STROKE_SELECT) { - action = SEL_DESELECT; - break; // XXX: this only gets out of the inner loop... - } - } - CTX_DATA_END; - } - - /* if deselecting, we need to deselect strokes across all frames - * - Currently, an exception is only given for deselection - * Selecting and toggling should only affect what's visible, - * while deselecting helps clean up unintended/forgotten - * stuff on other frames - */ - if (action == SEL_DESELECT) { - /* deselect strokes across editable layers - * NOTE: we limit ourselves to editable layers, since once a layer is "locked/hidden - * nothing should be able to touch it - */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - bGPDframe *gpf; - - /* deselect all strokes on all frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - bGPDstroke *gps; - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - bGPDspoint *pt; - int i; - - /* only edit strokes that are valid in this view... */ - if (ED_gpencil_stroke_can_use(C, gps)) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } - - gps->flag &= ~GP_STROKE_SELECT; - } - } - } - } - CTX_DATA_END; - } - else { - /* select or deselect all strokes */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - bool selected = false; - - /* Change selection status of all points, then make the stroke match */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - switch (action) { - case SEL_SELECT: - pt->flag |= GP_SPOINT_SELECT; - break; - //case SEL_DESELECT: - // pt->flag &= ~GP_SPOINT_SELECT; - // break; - case SEL_INVERT: - pt->flag ^= GP_SPOINT_SELECT; - break; - } - - if (pt->flag & GP_SPOINT_SELECT) - selected = true; - } - - /* Change status of stroke */ - if (selected) - gps->flag |= GP_STROKE_SELECT; - else - gps->flag &= ~GP_STROKE_SELECT; - } - CTX_DATA_END; - } + /* for "toggle", test for existing selected strokes */ + if (action == SEL_TOGGLE) { + action = SEL_SELECT; + + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if (gps->flag & GP_STROKE_SELECT) { + action = SEL_DESELECT; + break; // XXX: this only gets out of the inner loop... + } + } + CTX_DATA_END; + } + + /* if deselecting, we need to deselect strokes across all frames + * - Currently, an exception is only given for deselection + * Selecting and toggling should only affect what's visible, + * while deselecting helps clean up unintended/forgotten + * stuff on other frames + */ + if (action == SEL_DESELECT) { + /* deselect strokes across editable layers + * NOTE: we limit ourselves to editable layers, since once a layer is "locked/hidden + * nothing should be able to touch it + */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *gpf; + + /* deselect all strokes on all frames */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + bGPDstroke *gps; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + /* only edit strokes that are valid in this view... */ + if (ED_gpencil_stroke_can_use(C, gps)) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + gps->flag &= ~GP_STROKE_SELECT; + } + } + } + } + CTX_DATA_END; + } + else { + /* select or deselect all strokes */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + bGPDspoint *pt; + int i; + bool selected = false; + + /* Change selection status of all points, then make the stroke match */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + switch (action) { + case SEL_SELECT: + pt->flag |= GP_SPOINT_SELECT; + break; + //case SEL_DESELECT: + // pt->flag &= ~GP_SPOINT_SELECT; + // break; + case SEL_INVERT: + pt->flag ^= GP_SPOINT_SELECT; + break; + } + + if (pt->flag & GP_SPOINT_SELECT) + selected = true; + } + + /* Change status of stroke */ + if (selected) + gps->flag |= GP_STROKE_SELECT; + else + gps->flag &= ~GP_STROKE_SELECT; + } + CTX_DATA_END; + } } |