Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/annotate_paint.c
| Show First 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | |||||
| } eGP_StrokeAdd_Result; | } eGP_StrokeAdd_Result; | ||||
| /* Runtime flags */ | /* Runtime flags */ | ||||
| typedef enum eGPencil_PaintFlags { | typedef enum eGPencil_PaintFlags { | ||||
| GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ | GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ | ||||
| GP_PAINTFLAG_STROKEADDED = (1 << 1), | GP_PAINTFLAG_STROKEADDED = (1 << 1), | ||||
| GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | ||||
| GP_PAINTFLAG_SELECTMASK = (1 << 3), | GP_PAINTFLAG_SELECTMASK = (1 << 3), | ||||
| /* Flags used to indicate if stabilization is being used. */ | |||||
antoniov: "Flags" | |||||
| GP_PAINTFLAG_USE_STABILIZER = (1 << 7), | |||||
| GP_PAINTFLAG_USE_STABILIZER_TEMP = (1 << 8), | |||||
| } eGPencil_PaintFlags; | } eGPencil_PaintFlags; | ||||
| /* Temporary 'Stroke' Operation data | /* Temporary 'Stroke' Operation data | ||||
| * "p" = op->customdata | * "p" = op->customdata | ||||
| */ | */ | ||||
| typedef struct tGPsdata { | typedef struct tGPsdata { | ||||
| Main *bmain; | Main *bmain; | ||||
| /** current scene from context. */ | /** current scene from context. */ | ||||
| Show All 32 Lines | typedef struct tGPsdata { | ||||
| /** mode for painting. */ | /** mode for painting. */ | ||||
| eGPencil_PaintModes paintmode; | eGPencil_PaintModes paintmode; | ||||
| /** flags that can get set during runtime (eGPencil_PaintFlags) */ | /** flags that can get set during runtime (eGPencil_PaintFlags) */ | ||||
| eGPencil_PaintFlags flags; | eGPencil_PaintFlags flags; | ||||
| /** radius of influence for eraser. */ | /** radius of influence for eraser. */ | ||||
| short radius; | short radius; | ||||
| /* Stabilizer. */ | |||||
Done Inline ActionsUse single * if it's a comment for a group. If you use ** the comment is by variable. antoniov: Use single * if it's a comment for a group. If you use ** the comment is by variable. | |||||
| float stabilizer_factor; | |||||
| char stabilizer_radius; | |||||
| void *stabilizer_cursor; | |||||
| /** current mouse-position. */ | /** current mouse-position. */ | ||||
| float mval[2]; | float mval[2]; | ||||
| /** previous recorded mouse-position. */ | /** previous recorded mouse-position. */ | ||||
| float mvalo[2]; | float mvalo[2]; | ||||
| /** current stylus pressure. */ | /** current stylus pressure. */ | ||||
| float pressure; | float pressure; | ||||
| /** previous stylus pressure. */ | /** previous stylus pressure. */ | ||||
| ▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2]) | ||||
| /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | ||||
| if (p->gpd->runtime.sbuffer_used == 0) { | if (p->gpd->runtime.sbuffer_used == 0) { | ||||
| return true; | return true; | ||||
| /* check if mouse moved at least certain distance on both axes (best case) | /* 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 | * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand | ||||
| */ | */ | ||||
| } | } | ||||
| /* If lazy mouse, check minimum distance. */ | |||||
| else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { | |||||
| if ((dx * dx + dy * dy) > (p->stabilizer_radius * p->stabilizer_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; | |||||
| } | |||||
| } | |||||
| else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { | else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { | ||||
| return true; | return true; | ||||
| /* Check if the distance since the last point is significant enough: | /* Check if the distance since the last point is significant enough: | ||||
| * - Prevents points being added too densely | * - Prevents points being added too densely | ||||
| * - Distance here doesn't use sqrt to prevent slowness. | * - Distance here doesn't use sqrt to prevent slowness. | ||||
| * We should still be safe from overflows though. | * We should still be safe from overflows though. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 292 Lines • ▼ Show 20 Lines | else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ | ||||
| /* unused for annotations, but initialise for easier conversions to GP Object */ | /* unused for annotations, but initialise for easier conversions to GP Object */ | ||||
| pt->strength = 1.0f; | pt->strength = 1.0f; | ||||
| /* point time */ | /* point time */ | ||||
| pt->time = (float)(curtime - p->inittime); | pt->time = (float)(curtime - p->inittime); | ||||
| /* increment counters */ | /* increment counters */ | ||||
| gpd->runtime.sbuffer_used++; | gpd->runtime.sbuffer_used++; | ||||
| /* Don't smooth if stabilizer is on. */ | |||||
| if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { | |||||
| /* smooth while drawing previous points with a reduction factor for previous */ | /* smooth while drawing previous points with a reduction factor for previous */ | ||||
| for (int s = 0; s < 3; s++) { | for (int s = 0; s < 3; s++) { | ||||
| gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s); | gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s); | ||||
| } | } | ||||
| } | |||||
| return GP_STROKEADD_NORMAL; | return GP_STROKEADD_NORMAL; | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| /* get pointer to destination point */ | /* get pointer to destination point */ | ||||
| pt = (tGPspoint *)gpd->runtime.sbuffer; | pt = (tGPspoint *)gpd->runtime.sbuffer; | ||||
| /* store settings */ | /* store settings */ | ||||
| ▲ Show 20 Lines • Show All 1,116 Lines • ▼ Show 20 Lines | else if (enable && !p->erasercursor) { | ||||
| p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), | p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), | ||||
| SPACE_TYPE_ANY, | SPACE_TYPE_ANY, | ||||
| RGN_TYPE_ANY, | RGN_TYPE_ANY, | ||||
| NULL, /* XXX */ | NULL, /* XXX */ | ||||
| gpencil_draw_eraser, | gpencil_draw_eraser, | ||||
| p); | p); | ||||
| } | } | ||||
| } | } | ||||
| static void gpencil_draw_stabilizer(bContext *C, int x, int y, void *p_ptr) | |||||
| { | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| tGPsdata *p = (tGPsdata *)p_ptr; | |||||
| bGPdata_Runtime runtime = p->gpd->runtime; | |||||
| const tGPspoint *points = runtime.sbuffer; | |||||
| int totpoints = runtime.sbuffer_used; | |||||
| if (totpoints < 2) { | |||||
| return; | |||||
| } | |||||
| const tGPspoint *pt = &points[totpoints - 1]; | |||||
| 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); | |||||
| GPU_line_width(1.25f); | |||||
| const float color[3] = {1.0f, 0.39f, 0.39f}; | |||||
| /* default radius and color */ | |||||
| float darkcolor[3]; | |||||
| const float radius = 4.0f; | |||||
| /* Inner Ring: Color from UI panel */ | |||||
| immUniformColor4f(color[0], color[1], color[2], 0.8f); | |||||
| 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); | |||||
| /* Rope Simple. */ | |||||
| immUniformColor4f(color[0], color[1], color[2], 0.8f); | |||||
| immBegin(GPU_PRIM_LINES, 2); | |||||
| immVertex2f(pos, pt->x + region->winrct.xmin, pt->y + region->winrct.ymin); | |||||
| immVertex2f(pos, x, y); | |||||
| immEnd(); | |||||
| /* Returns back all GPU settings */ | |||||
| GPU_blend(false); | |||||
| GPU_line_smooth(false); | |||||
| immUnbindProgram(); | |||||
| } | |||||
| /* Turn *stabilizer* brush cursor in 3D view on/off */ | |||||
| static void gpencil_draw_toggle_stabilizer_cursor(bContext *C, tGPsdata *p, short enable) | |||||
| { | |||||
| if (p->stabilizer_cursor && !enable) { | |||||
| /* clear cursor */ | |||||
| WM_paint_cursor_end(CTX_wm_manager(C), p->stabilizer_cursor); | |||||
| p->stabilizer_cursor = NULL; | |||||
| } | |||||
| else if (enable && !p->stabilizer_cursor) { | |||||
| /* enable cursor */ | |||||
| p->stabilizer_cursor = WM_paint_cursor_activate( | |||||
| CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, NULL, gpencil_draw_stabilizer, p); | |||||
| } | |||||
| } | |||||
Done Inline ActionsRemove /* XXX */ antoniov: Remove /* XXX */ | |||||
| /* Check if tablet eraser is being used (when processing events) */ | /* Check if tablet eraser is being used (when processing events) */ | ||||
| static bool gpencil_is_tablet_eraser_active(const wmEvent *event) | static bool gpencil_is_tablet_eraser_active(const wmEvent *event) | ||||
| { | { | ||||
| return (event->tablet.active == EVT_TABLET_ERASER); | return (event->tablet.active == EVT_TABLET_ERASER); | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| static void gpencil_draw_exit(bContext *C, wmOperator *op) | static void gpencil_draw_exit(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| /* restore cursor to indicate end of drawing */ | /* restore cursor to indicate end of drawing */ | ||||
| WM_cursor_modal_restore(CTX_wm_window(C)); | WM_cursor_modal_restore(CTX_wm_window(C)); | ||||
| /* don't assume that operator data exists at all */ | /* don't assume that operator data exists at all */ | ||||
| if (p) { | if (p) { | ||||
| /* check size of buffer before cleanup, to determine if anything happened here */ | /* check size of buffer before cleanup, to determine if anything happened here */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| /* turn off radial brush cursor */ | /* turn off radial brush cursor */ | ||||
| gpencil_draw_toggle_eraser_cursor(C, p, false); | gpencil_draw_toggle_eraser_cursor(C, p, false); | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW) { | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, false); | |||||
| } | |||||
| /* always store the new eraser size to be used again next time | /* always store the new eraser size to be used again next time | ||||
| * NOTE: Do this even when not in eraser mode, as eraser may | * NOTE: Do this even when not in eraser mode, as eraser may | ||||
| * have been toggled at some point. | * have been toggled at some point. | ||||
| */ | */ | ||||
| U.gp_eraser = p->radius; | U.gp_eraser = p->radius; | ||||
| /* clear undo stack */ | /* clear undo stack */ | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | case GP_STATUS_PAINTING: | ||||
| case GP_PAINTMODE_DRAW_POLY: | case GP_PAINTMODE_DRAW_POLY: | ||||
| /* Provide usage tips, since this is modal, and unintuitive without hints */ | /* Provide usage tips, since this is modal, and unintuitive without hints */ | ||||
| ED_workspace_status_text( | ED_workspace_status_text( | ||||
| C, | C, | ||||
| TIP_("Annotation Create Poly: LMB click to place next stroke vertex | " | TIP_("Annotation Create Poly: LMB click to place next stroke vertex | " | ||||
| "ESC/Enter to end (or click outside this area)")); | "ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| default: | default: | ||||
| /* Do nothing - the others are self explanatory, exit quickly once the mouse is released | /* Do nothing - the others are self explanatory, exit quickly once the mouse is | ||||
| * Showing any text would just be annoying as it would flicker. | * released Showing any text would just be annoying as it would flicker. | ||||
| */ | */ | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case GP_STATUS_IDLING: | case GP_STATUS_IDLING: | ||||
| /* print status info */ | /* print status info */ | ||||
| switch (p->paintmode) { | switch (p->paintmode) { | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| /* store used values */ | /* store used values */ | ||||
| p->mvalo[0] = p->mval[0]; | p->mvalo[0] = p->mval[0]; | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| } | } | ||||
| /* Only add current point to buffer if mouse moved | /* Only add current point to buffer if mouse moved | ||||
| * (even though we got an event, it might be just noise). */ | * (even though we got an event, it might be just noise). */ | ||||
| else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | ||||
| /* If lazy mouse, interpolate the last and current mouse positions. */ | |||||
| if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { | |||||
| 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, min_ff(p->stabilizer_factor, .995f)); | |||||
| copy_v2_v2(p->mval, now_mouse); | |||||
| } | |||||
| /* try to add point */ | /* try to add point */ | ||||
| short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | ||||
| /* handle errors while adding point */ | /* handle errors while adding point */ | ||||
| if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { | if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { | ||||
| /* finish off old stroke */ | /* finish off old stroke */ | ||||
| gp_paint_strokeend(p); | gp_paint_strokeend(p); | ||||
| /* And start a new one!!! Else, projection errors! */ | /* And start a new one!!! Else, projection errors! */ | ||||
| Show All 27 Lines | else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| p->ocurtime = p->curtime; | p->ocurtime = p->curtime; | ||||
| } | } | ||||
| } | } | ||||
| /* handle draw event */ | /* handle draw event */ | ||||
| static void annotation_draw_apply_event( | static void annotation_draw_apply_event( | ||||
| wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) | bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| PointerRNA itemptr; | PointerRNA itemptr; | ||||
| float mousef[2]; | float mousef[2]; | ||||
| /* convert from window-space to area-space mouse coordinates | /* convert from window-space to area-space mouse coordinates | ||||
| * add any x,y override position for fake events | * add any x,y override position for fake events | ||||
| */ | */ | ||||
| p->mval[0] = (float)event->mval[0] - x; | p->mval[0] = (float)event->mval[0] - x; | ||||
| p->mval[1] = (float)event->mval[1] - y; | p->mval[1] = (float)event->mval[1] - y; | ||||
| /* Key to toggle stabilization. */ | |||||
| if (event->shift > 0 && p->paintmode == GP_PAINTMODE_DRAW) { | |||||
| /* Using permanent stabilization, shift will deactivate the flag. */ | |||||
| if (p->flags & (GP_PAINTFLAG_USE_STABILIZER)) { | |||||
| if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, false); | |||||
| p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| } | |||||
| } | |||||
| /* Not using any stabilization flag. Activate temporal one. */ | |||||
| else if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { | |||||
| p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, true); | |||||
| } | |||||
| } | |||||
| /* verify key status for straight lines */ | /* verify key status for straight lines */ | ||||
| if ((event->ctrl > 0) || (event->alt > 0)) { | else if ((event->ctrl > 0) || (event->alt > 0)) { | ||||
| if (p->straight[0] == 0) { | if (p->straight[0] == 0) { | ||||
| int dx = abs((int)(p->mval[0] - p->mvalo[0])); | int dx = abs((int)(p->mval[0] - p->mvalo[0])); | ||||
| int dy = abs((int)(p->mval[1] - p->mvalo[1])); | int dy = abs((int)(p->mval[1] - p->mvalo[1])); | ||||
| if ((dx > 0) || (dy > 0)) { | if ((dx > 0) || (dy > 0)) { | ||||
| /* check mouse direction to replace the other coordinate with previous values */ | /* check mouse direction to replace the other coordinate with previous values */ | ||||
| if (dx >= dy) { | if (dx >= dy) { | ||||
| /* horizontal */ | /* horizontal */ | ||||
| p->straight[0] = 1; | p->straight[0] = 1; | ||||
| p->straight[1] = p->mval[1]; /* save y */ | p->straight[1] = p->mval[1]; /* save y */ | ||||
| } | } | ||||
| else { | else { | ||||
| /* vertical */ | /* vertical */ | ||||
| p->straight[0] = 2; | p->straight[0] = 2; | ||||
| p->straight[1] = p->mval[0]; /* save x */ | p->straight[1] = p->mval[0]; /* save x */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| p->straight[0] = 0; | p->straight[0] = 0; | ||||
| /* We were using shift while having permanent stabilization actived, | |||||
| so activate the temp flag back again. */ | |||||
| if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { | |||||
| if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, true); | |||||
| p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| } | |||||
| } | |||||
| /* We are using the temporal stabilizer flag atm, | |||||
| but shift is not pressed as well as the permanent flag is not used, | |||||
| so we don't need the cursor anymore. */ | |||||
| else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { | |||||
| /* Reset temporal stabilizer flag and remove cursor. */ | |||||
| p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, false); | |||||
| } | |||||
| } | } | ||||
| p->curtime = PIL_check_seconds_timer(); | p->curtime = PIL_check_seconds_timer(); | ||||
| /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ | /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ | ||||
| p->pressure = event->tablet.pressure; | p->pressure = event->tablet.pressure; | ||||
| /* Hack for pressure sensitive eraser on D+RMB when using a tablet: | /* Hack for pressure sensitive eraser on D+RMB when using a tablet: | ||||
| ▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | if (p->status == GP_STATUS_CAPTURE) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nothing to erase"); | BKE_report(op->reports, RPT_ERROR, "Nothing to erase"); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| /* TODO: set any additional settings that we can take from the events? | /* TODO: set any additional settings that we can take from the events? | ||||
| * TODO? if tablet is erasing, force eraser to be on? */ | * 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... */ | /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... | ||||
| */ | |||||
| /* if eraser is on, draw radial aid */ | /* if eraser is on, draw radial aid */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| gpencil_draw_toggle_eraser_cursor(C, p, true); | gpencil_draw_toggle_eraser_cursor(C, p, true); | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | else if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | ||||
| if (RNA_enum_get(op->ptr, "arrowstyle_start") != GP_STROKE_ARROWSTYLE_NONE) { | if (RNA_enum_get(op->ptr, "arrowstyle_start") != GP_STROKE_ARROWSTYLE_NONE) { | ||||
Not Done Inline ActionsRemove TODOs antoniov: Remove TODOs | |||||
| p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_START; | p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_START; | ||||
| p->gpd->runtime.arrow_start_style = RNA_enum_get(op->ptr, "arrowstyle_start"); | p->gpd->runtime.arrow_start_style = RNA_enum_get(op->ptr, "arrowstyle_start"); | ||||
| } | } | ||||
| if (RNA_enum_get(op->ptr, "arrowstyle_end") != GP_STROKE_ARROWSTYLE_NONE) { | if (RNA_enum_get(op->ptr, "arrowstyle_end") != GP_STROKE_ARROWSTYLE_NONE) { | ||||
| p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_END; | p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_END; | ||||
| p->gpd->runtime.arrow_end_style = RNA_enum_get(op->ptr, "arrowstyle_end"); | p->gpd->runtime.arrow_end_style = RNA_enum_get(op->ptr, "arrowstyle_end"); | ||||
| } | } | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW) { | |||||
| p->stabilizer_factor = RNA_float_get(op->ptr, "stabilizer_factor"); | |||||
| p->stabilizer_radius = RNA_int_get(op->ptr, "stabilizer_radius"); | |||||
| if (RNA_boolean_get(op->ptr, "use_stabilizer")) { | |||||
| p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, true); | |||||
| } | |||||
| else if (event->shift > 0) { | |||||
| p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; | |||||
| gpencil_draw_toggle_stabilizer_cursor(C, p, true); | |||||
| } | |||||
| } | |||||
| /* set cursor | /* set cursor | ||||
| * NOTE: This may change later (i.e. intentionally via brush toggle, | * NOTE: This may change later (i.e. intentionally via brush toggle, | ||||
| * or unintentionally if the user scrolls outside the area)... | * or unintentionally if the user scrolls outside the area)... | ||||
| */ | */ | ||||
| gpencil_draw_cursor_set(p); | gpencil_draw_cursor_set(p); | ||||
| /* only start drawing immediately if we're allowed to do so... */ | /* only start drawing immediately if we're allowed to do so... */ | ||||
| if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | ||||
| /* hotkey invoked - start drawing */ | /* hotkey invoked - start drawing */ | ||||
| /* printf("\tGP - set first spot\n"); */ | /* printf("\tGP - set first spot\n"); */ | ||||
| p->status = GP_STATUS_PAINTING; | p->status = GP_STATUS_PAINTING; | ||||
| /* handle the initial drawing - i.e. for just doing a simple dot */ | /* handle the initial drawing - i.e. for just doing a simple dot */ | ||||
| annotation_draw_apply_event(op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); | annotation_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); | ||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| else { | else { | ||||
| /* toolbar invoked - don't start drawing yet... */ | /* toolbar invoked - don't start drawing yet... */ | ||||
| /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ | /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ | ||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | static void annotation_add_missing_events(bContext *C, | ||||
| /* get distance in pixels */ | /* get distance in pixels */ | ||||
| float dist = len_v2v2(a, b); | float dist = len_v2v2(a, b); | ||||
| /* for very small distances, add a half way point */ | /* for very small distances, add a half way point */ | ||||
| if (dist <= 2.0f) { | if (dist <= 2.0f) { | ||||
| interp_v2_v2v2(pt, a, b, 0.5f); | interp_v2_v2v2(pt, a, b, 0.5f); | ||||
| sub_v2_v2v2(pt, b, pt); | sub_v2_v2v2(pt, b, pt); | ||||
| /* create fake event */ | /* create fake event */ | ||||
| annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]); | annotation_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); | ||||
| } | } | ||||
| else if (dist >= factor) { | else if (dist >= factor) { | ||||
| int slices = 2 + (int)((dist - 1.0) / factor); | int slices = 2 + (int)((dist - 1.0) / factor); | ||||
| float n = 1.0f / slices; | float n = 1.0f / slices; | ||||
| for (int i = 1; i < slices; i++) { | for (int i = 1; i < slices; i++) { | ||||
| interp_v2_v2v2(pt, a, b, n * i); | interp_v2_v2v2(pt, a, b, n * i); | ||||
| sub_v2_v2v2(pt, b, pt); | sub_v2_v2v2(pt, b, pt); | ||||
| /* create fake event */ | /* create fake event */ | ||||
| annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]); | annotation_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* events handling during interactive drawing part of operator */ | /* events handling during interactive drawing part of operator */ | ||||
| static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| ▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | if (event->val == KM_PRESS && | ||||
| ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { | ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { | ||||
| /* exit() ends the current stroke before cleaning up */ | /* exit() ends the current stroke before cleaning up */ | ||||
| /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | ||||
| p->status = GP_STATUS_DONE; | p->status = GP_STATUS_DONE; | ||||
| estate = OPERATOR_FINISHED; | estate = OPERATOR_FINISHED; | ||||
| } | } | ||||
| /* toggle painting mode upon mouse-button movement | /* toggle painting mode upon mouse-button movement | ||||
| * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) | * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox | ||||
| * only) | |||||
| * - RIGHTMOUSE = polyline (hotkey) / eraser (all) | * - RIGHTMOUSE = polyline (hotkey) / eraser (all) | ||||
| * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) | * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) | ||||
| * also making sure we have a valid event value, to not exit too early | * 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 (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { | ||||
| /* if painting, end stroke */ | /* if painting, end stroke */ | ||||
| if (p->status == GP_STATUS_PAINTING) { | if (p->status == GP_STATUS_PAINTING) { | ||||
| int sketch = 0; | int sketch = 0; | ||||
| ▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | if (p->status == GP_STATUS_PAINTING) { | ||||
| /* handle painting mouse-movements? */ | /* handle painting mouse-movements? */ | ||||
| if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | ||||
| /* handle drawing event */ | /* handle drawing event */ | ||||
| if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { | if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { | ||||
| annotation_add_missing_events(C, op, event, p); | annotation_add_missing_events(C, op, event, p); | ||||
| } | } | ||||
| /* TODO(sergey): Possibly evaluating dependency graph from modal operator? */ | /* TODO(sergey): Possibly evaluating dependency graph from modal operator? */ | ||||
| annotation_draw_apply_event(op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); | annotation_draw_apply_event( | ||||
| C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); | |||||
| /* finish painting operation if anything went wrong just now */ | /* finish painting operation if anything went wrong just now */ | ||||
| if (p->status == GP_STATUS_ERROR) { | if (p->status == GP_STATUS_ERROR) { | ||||
| printf("\t\t\t\tGP - add error done!\n"); | printf("\t\t\t\tGP - add error done!\n"); | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| /* event handled, so just tag as running modal */ | /* event handled, so just tag as running modal */ | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| if (0 == gpencil_area_exists(C, p->area)) { | if (0 == gpencil_area_exists(C, p->area)) { | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| /* update status indicators - cursor, header, etc. */ | /* update status indicators - cursor, header, etc. */ | ||||
| gpencil_draw_status_indicators(C, p); | gpencil_draw_status_indicators(C, p); | ||||
| /* cursor may have changed outside our control - T44084 */ | /* cursor may have changed outside our control - T44084 */ | ||||
| gpencil_draw_cursor_set(p); | gpencil_draw_cursor_set(p); | ||||
| } | } | ||||
Done Inline ActionsDon't use phrases like Stabilizer is a Stabilizer to smooth you need something like `Stabilizer is a antoniov: Don't use phrases like `Stabilizer is a Stabilizer to smooth` you need something like… | |||||
| /* process last operations before exiting */ | /* process last operations before exiting */ | ||||
| switch (estate) { | switch (estate) { | ||||
| case OPERATOR_FINISHED: | case OPERATOR_FINISHED: | ||||
| /* one last flush before we're done */ | /* one last flush before we're done */ | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | void GPENCIL_OT_annotate(wmOperatorType *ot) | ||||
| ot->prop = RNA_def_enum( | ot->prop = RNA_def_enum( | ||||
| ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); | ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); | ||||
| /* properties */ | /* properties */ | ||||
| prop = RNA_def_enum( | prop = RNA_def_enum( | ||||
| ot->srna, "arrowstyle_start", arrow_types, 0, "Start Arrow Style", "Stroke start style"); | ot->srna, "arrowstyle_start", arrow_types, 0, "Start Arrow Style", "Stroke start style"); | ||||
| prop = RNA_def_enum( | prop = RNA_def_enum( | ||||
| ot->srna, "arrowstyle_end", arrow_types, 0, "End Arrow Style", "Stroke end style"); | ot->srna, "arrowstyle_end", arrow_types, 0, "End Arrow Style", "Stroke end style"); | ||||
| prop = RNA_def_boolean(ot->srna, | |||||
| "use_stabilizer", | |||||
| false, | |||||
| "Use Stabilizer", | |||||
| "Helper to draw smooth and clean lines. Press Shift for an invert effect " | |||||
| "(even if this option is not active)"); | |||||
| prop = RNA_def_float(ot->srna, | |||||
| "stabilizer_factor", | |||||
| 0.75f, | |||||
| 0.0f, | |||||
| 1.0f, | |||||
| "Stabilizer Stroke Factor", | |||||
| "Higher values gives a smoother stroke", | |||||
Done Inline Actions"Use" is implied for boolean options. The name and tooltip should describe what happens when the setting is on. So the property should be called "Stabilize Stroke" like @Pablo Vazquez (pablovazquez) suggested. I would also suggest not repeating the name of the property in the tooltip, that's redundant. HooglyBoogly: "Use" is implied for boolean options. The name and tooltip should describe what happens when… | |||||
| 0.0f, | |||||
| 1.0f); | |||||
| prop = RNA_def_int(ot->srna, | |||||
| "stabilizer_radius", | |||||
| 35, | |||||
| 0, | |||||
| 200, | |||||
| "Stabilizer Stroke Radius", | |||||
| "Minimun distance from last point before stroke continues", | |||||
| 1, | |||||
| 100); | |||||
| RNA_def_property_subtype(prop, PROP_PIXEL); | |||||
| prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); | prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); | ||||
| RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | ||||
| /* NOTE: wait for input is enabled by default, | /* NOTE: wait for input is enabled by default, | ||||
| * so that all UI code can work properly without needing users to know about this */ | * so that all UI code can work properly without needing users to know about this */ | ||||
| prop = RNA_def_boolean(ot->srna, | prop = RNA_def_boolean(ot->srna, | ||||
| "wait_for_input", | "wait_for_input", | ||||
| true, | true, | ||||
| "Wait for Input", | "Wait for Input", | ||||
| "Wait for first click instead of painting immediately"); | "Wait for first click instead of painting immediately"); | ||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_SKIP_SAVE); | ||||
| } | } | ||||
"Flags"