GP: New Curve primitive and other primitive improvements
authorAntonioya <blendergit@gmail.com>
Sat, 15 Dec 2018 16:21:47 +0000 (17:21 +0100)
committerAntonioya <blendergit@gmail.com>
Sat, 15 Dec 2018 16:21:47 +0000 (17:21 +0100)
This commit adds support for new curve tool and adds more functionalities to the existing primitives, including new handles, editing, stroke thickness curve, noise, preview of the real stroke, etc.

Thanks to @charlie for his great contribution to this improvement.

22 files changed:
release/scripts/presets/keyconfig/keymap_data/blender_default.py
release/scripts/startup/bl_ui/properties_grease_pencil_common.py
release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
release/scripts/startup/bl_ui/space_topbar.py
source/blender/blenkernel/intern/colortools.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_280.c
source/blender/blenloader/intern/versioning_defaults.c
source/blender/blenloader/intern/writefile.c
source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
source/blender/draw/engines/gpencil/gpencil_draw_utils.c
source/blender/draw/engines/gpencil/gpencil_engine.c
source/blender/draw/engines/gpencil/gpencil_engine.h
source/blender/editors/gpencil/drawgpencil.c
source/blender/editors/gpencil/gpencil_intern.h
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/gpencil/gpencil_primitive.c
source/blender/makesdna/DNA_color_types.h
source/blender/makesdna/DNA_gpencil_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_sculpt_paint.c

index 72b3e166cc1fad1c5869008b8c9b898feb2c4ca2..8c386af57be527083aa10ce14476c8fafe362b9a 100644 (file)
@@ -5709,6 +5709,15 @@ def km_3d_view_tool_paint_gpencil_arc(params):
         ]},
     )
 
+def km_3d_view_tool_paint_gpencil_curve(params):
+    return (
+        "3D View Tool: Paint Gpencil, Curve",
+        {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+        {"items": [
+            ("gpencil.primitive", {"type": params.tool_tweak, "value": 'ANY'},
+             {"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
+        ]},
+    )  
 
 def km_3d_view_tool_edit_gpencil_select(params):
     return (
@@ -6019,6 +6028,7 @@ def generate_keymaps(params=None):
         km_3d_view_tool_paint_gpencil_box(params),
         km_3d_view_tool_paint_gpencil_circle(params),
         km_3d_view_tool_paint_gpencil_arc(params),
+        km_3d_view_tool_paint_gpencil_curve(params),
         km_3d_view_tool_edit_gpencil_select(params),
         km_3d_view_tool_edit_gpencil_select_box(params),
         km_3d_view_tool_edit_gpencil_select_circle(params),
index 09e81115d6a725120caaa02d640e8ec0992983b1..32c0aa6ab0bd2a1318ecf7fecaa4bd7aca2a35b6 100644 (file)
@@ -645,6 +645,7 @@ class GPENCIL_MT_gpencil_draw_specials(Menu):
         layout.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX'
         layout.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE'
         layout.operator("gpencil.primitive", text="Arc", icon='SPHERECURVE').type = 'ARC'
+        layout.operator("gpencil.primitive", text="Curve", icon='CURVE_BEZCURVE').type = 'CURVE'
 
 
 class GPENCIL_MT_gpencil_draw_delete(Menu):
index 4da5455298f0ff36f9facf5784c72d664dd32380..aa6adee0d195f29d17d6be34bc34fb57a7b45e2a 100644 (file)
@@ -1076,6 +1076,17 @@ class _defs_gpencil_paint:
             widget=None,
             keymap=(),
         )
+               
+
+    @ToolDef.from_fn
+    def curve():
+        return dict(
+            text="Curve",
+            icon="ops.gpencil.primitive_curve",
+            cursor='CROSSHAIR',
+            widget=None,
+            keymap=(),
+        )              
 
 class _defs_gpencil_edit:
     @ToolDef.from_fn
@@ -1583,6 +1594,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
             _defs_gpencil_paint.box,
             _defs_gpencil_paint.circle,
             _defs_gpencil_paint.arc,
+            _defs_gpencil_paint.curve,
         ],
         'EDIT_GPENCIL': [
             *_tools_gpencil_select,
index f73d21dcfb843863259292860c2571ec8a69ea1b..a94ebea4561d3408e8a327de15b2a5eecfaf9e3a 100644 (file)
@@ -301,7 +301,7 @@ class _draw_left_context_mode:
                 return
 
             is_paint = True
-            if (tool.name in {"Line", "Box", "Circle", "Arc"}):
+            if (tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}):
                 is_paint = False
             elif (not tool.has_datablock):
                 return
@@ -375,6 +375,17 @@ class _draw_left_context_mode:
 
                 draw_color_selector()
 
+                if tool.name in {"Arc", "Curve", "Line", "Box", "Circle"}:
+                    settings = context.tool_settings.gpencil_sculpt
+                    row = layout.row(align=True)
+                    row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
+                    sub = row.row(align=True)
+                    sub.active = settings.use_thickness_curve
+                    sub.popover(
+                        panel="TOPBAR_PT_gpencil_primitive",
+                        text="Thickness Profile"
+                    )
+
         @staticmethod
         def SCULPT_GPENCIL(context, layout, tool):
             if (tool is None) or (not tool.has_datablock):
@@ -1039,6 +1050,21 @@ class TOPBAR_PT_active_tool(Panel):
         ToolSelectPanelHelper.draw_active_tool_header(context, layout, show_tool_name=True)
 
 
+# Grease Pencil Object - Primitive curve
+class TOPBAR_PT_gpencil_primitive(Panel):
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'HEADER'
+    bl_label = "Primitives"
+
+    @staticmethod
+    def draw(self, context):
+        settings = context.tool_settings.gpencil_sculpt
+
+        layout = self.layout
+        # Curve
+        layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
+
+
 classes = (
     TOPBAR_HT_upper_bar,
     TOPBAR_HT_lower_bar,
@@ -1059,6 +1085,7 @@ classes = (
     TOPBAR_MT_help,
     TOPBAR_PT_active_tool,
     TOPBAR_PT_gpencil_layers,
+    TOPBAR_PT_gpencil_primitive,
 )
 
 if __name__ == "__main__":  # only for live edit.
index aad59e321875459105ed5c81d2012e8c892122c8..bc34b24c8f2af4f15de9fed0e0b8a44d52ee8453 100644 (file)
@@ -283,6 +283,7 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
                case CURVE_PRESET_ROUND: cuma->totpoint = 4; break;
                case CURVE_PRESET_ROOT: cuma->totpoint = 4; break;
                case CURVE_PRESET_GAUSS: cuma->totpoint = 7; break;
+               case CURVE_PRESET_BELL: cuma->totpoint = 3; break;
        }
 
        cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points");
@@ -371,6 +372,16 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
                        cuma->curve[6].x = 1.0f;
                        cuma->curve[6].y = 0.025f;
                        break;
+               case CURVE_PRESET_BELL:
+                       cuma->curve[0].x = 0;
+                       cuma->curve[0].y = 0.025f;
+
+                       cuma->curve[1].x = 0.50f;
+                       cuma->curve[1].y = 1.0f;
+
+                       cuma->curve[2].x = 1.0f;
+                       cuma->curve[2].y = 0.025f;
+                       break;
        }
 
        /* mirror curve in x direction to have positive slope
index a070bf97317c6485132d476f5c666f20747a30e2..3894d83c169c6c0cbca9cb4d366d748662256669 100644 (file)
@@ -188,6 +188,7 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
        ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo);
        /* duplicate Grease Pencil multiframe fallof */
        ts->gp_sculpt.cur_falloff = curvemapping_copy(ts->gp_sculpt.cur_falloff);
+       ts->gp_sculpt.cur_primitive = curvemapping_copy(ts->gp_sculpt.cur_primitive);
        return ts;
 }
 
@@ -226,6 +227,9 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
        if (toolsettings->gp_sculpt.cur_falloff) {
                curvemapping_free(toolsettings->gp_sculpt.cur_falloff);
        }
+       if (toolsettings->gp_sculpt.cur_primitive) {
+               curvemapping_free(toolsettings->gp_sculpt.cur_primitive);
+       }
 
        MEM_freeN(toolsettings);
 }
@@ -699,6 +703,14 @@ void BKE_scene_init(Scene *sce)
                CURVE_PRESET_GAUSS,
                CURVEMAP_SLOPE_POSITIVE);
 
+       sce->toolsettings->gp_sculpt.cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+       CurveMapping *gp_primitive_curve = sce->toolsettings->gp_sculpt.cur_primitive;
+       curvemapping_initialize(gp_primitive_curve);
+       curvemap_reset(gp_primitive_curve->cm,
+               &gp_primitive_curve->clipr,
+               CURVE_PRESET_BELL,
+               CURVEMAP_SLOPE_POSITIVE);
+
        sce->physics_settings.gravity[0] = 0.0f;
        sce->physics_settings.gravity[1] = 0.0f;
        sce->physics_settings.gravity[2] = -9.81f;
index 6aff6f6506fa4b381ac7f4526ffdb9dd10b64d6f..6dd9c80b5d956691c3ea7bd2daaf8b064ca8a0c5 100644 (file)
@@ -6275,6 +6275,11 @@ static void direct_link_scene(FileData *fd, Scene *sce)
                if (sce->toolsettings->gp_sculpt.cur_falloff) {
                        direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_falloff);
                }
+               /* relink grease pencil primitive curve */
+               sce->toolsettings->gp_sculpt.cur_primitive = newdataadr(fd, sce->toolsettings->gp_sculpt.cur_primitive);
+               if (sce->toolsettings->gp_sculpt.cur_primitive) {
+                       direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_primitive);
+               }
        }
 
        if (sce->ed) {
index 56ca663e3f06f4f8f7152378dd932fe72f8f262b..5a1002702b3ce45e41e50d226ebe819b040b4d8e 100644 (file)
@@ -2516,6 +2516,21 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
                                }
                        }
                }
+
+               /* Grease pencil primitive curve */
+               if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "CurveMapping", "cur_primitive")) {
+                       for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
+                               GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
+                               if ((gset) && (gset->cur_primitive == NULL)) {
+                                       gset->cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+                                       curvemapping_initialize(gset->cur_primitive);
+                                       curvemap_reset(gset->cur_primitive->cm,
+                                               &gset->cur_primitive->clipr,
+                                               CURVE_PRESET_BELL,
+                                               CURVEMAP_SLOPE_POSITIVE);
+                               }
+                       }
+               }
        }
 
        {
index 4dd046d9a7305db4223d34a339f2df5fe14e83cf..f3ae1426fa880ea5f104d636811a48a0004d6148 100644 (file)
@@ -187,7 +187,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
                        }
                }
 
-               /* Be sure curfalloff is initializated */
+               /* Be sure curfalloff and primitive are initializated */
                for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
                        ToolSettings *ts = scene->toolsettings;
                        if (ts->gp_sculpt.cur_falloff == NULL) {
@@ -199,6 +199,15 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
                                               CURVE_PRESET_GAUSS,
                                               CURVEMAP_SLOPE_POSITIVE);
                        }
+                       if (ts->gp_sculpt.cur_primitive == NULL) {
+                               ts->gp_sculpt.cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+                               CurveMapping *gp_primitive_curve = ts->gp_sculpt.cur_primitive;
+                               curvemapping_initialize(gp_primitive_curve);
+                               curvemap_reset(gp_primitive_curve->cm,
+                                       &gp_primitive_curve->clipr,
+                                       CURVE_PRESET_BELL,
+                                       CURVEMAP_SLOPE_POSITIVE);
+                       }
                }
        }
 
index 0854af0f818cbcd00bf576b313f7b07ef9139553..c1d09a836ccaa6f6102b479b7333cac600c5581f 100644 (file)
@@ -2555,6 +2555,10 @@ static void write_scene(WriteData *wd, Scene *sce)
        if (tos->gp_sculpt.cur_falloff) {
                write_curvemapping(wd, tos->gp_sculpt.cur_falloff);
        }
+       /* write grease-pencil primitive curve to file */
+       if (tos->gp_sculpt.cur_primitive) {
+               write_curvemapping(wd, tos->gp_sculpt.cur_primitive);
+       }
 
        write_paint(wd, &tos->imapaint.paint);
 
index c75445f3ae535c50de8655615123a3f80d6bbe07..a098fb74bc8b8e7e9d3645054bd1e22e0f547273 100644 (file)
@@ -252,6 +252,9 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
 
        tGPspoint *points = gpd->runtime.sbuffer;
        int totpoints = gpd->runtime.sbuffer_size;
+       /* if cyclic needs more vertex */
+       int cyclic_add = (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC) ? 1 : 0;
+       int totvertex = totpoints + cyclic_add + 2;
 
        static GPUVertFormat format = { 0 };
        static uint pos_id, color_id, thickness_id, uvdata_id;
@@ -263,11 +266,11 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
        }
 
        GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
-       GPU_vertbuf_data_alloc(vbo, totpoints + 2);
+       GPU_vertbuf_data_alloc(vbo, totvertex);
 
        /* draw stroke curve */
        const tGPspoint *tpt = points;
-       bGPDspoint pt, pt2;
+       bGPDspoint pt, pt2, pt3;
        int idx = 0;
 
        /* get origin to reproject point */
@@ -281,19 +284,22 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
 
                /* first point for adjacency (not drawn) */
                if (i == 0) {
-                       if (totpoints > 1) {
-                               ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2);
+                       if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) {
+                               ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 1], &pt2);
                                gpencil_set_stroke_point(
-                                       vbo, &pt2, idx,
-                                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                                       vbo, &pt2, idx,
+                                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                               idx++;
                        }
                        else {
+                               ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2);
                                gpencil_set_stroke_point(
-                                       vbo, &pt, idx,
-                                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                                       vbo, &pt2, idx,
+                                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                               idx++;
                        }
-                       idx++;
                }
+
                /* set point */
                gpencil_set_stroke_point(
                        vbo, &pt, idx,
@@ -302,16 +308,27 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
        }
 
        /* last adjacency point (not drawn) */
-       if (totpoints > 2) {
-               ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2);
+       if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) {
+               /* draw line to first point to complete the cycle */
+               ED_gpencil_tpoint_to_point(ar, origin, &points[0], &pt2);
                gpencil_set_stroke_point(
-                       vbo, &pt2, idx,
-                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                       vbo, &pt2, idx,
+                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+               idx++;
+               /* now add adjacency point (not drawn) */
+               ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt3);
+               gpencil_set_stroke_point(
+                       vbo, &pt3, idx,
+                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+               idx++;
        }
+       /* last adjacency point (not drawn) */
        else {
+               ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2);
                gpencil_set_stroke_point(
-                       vbo, &pt, idx,
-                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+                       vbo, &pt2, idx,
+                       pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+               idx++;
        }
 
        return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO);
@@ -367,6 +384,42 @@ GPUBatch *DRW_gpencil_get_buffer_point_geom(bGPdata *gpd, short thickness)
        return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
 }
 
+/* create batch geometry data for current buffer control point shader */
+GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd)
+{
+       bGPDcontrolpoint *cps = gpd->runtime.cp_points;
+       int totpoints = gpd->runtime.tot_cp_points;
+
+       static GPUVertFormat format = { 0 };
+       static uint pos_id, color_id, size_id;
+       if (format.attr_len == 0) {
+               pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+               size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+               color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+       }
+
+       GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+       GPU_vertbuf_data_alloc(vbo, totpoints);
+
+       int idx = 0;
+       for (int i = 0; i < gpd->runtime.tot_cp_points; i++) {
+               bGPDcontrolpoint *cp = &cps[i];
+               float color[4];
+               copy_v3_v3(color, cp->color);
+               color[3] = 0.5f;
+               GPU_vertbuf_attr_set(vbo, color_id, idx, color);
+
+               /* scale size */
+               float size = cp->size * 0.8f;
+               GPU_vertbuf_attr_set(vbo, size_id, idx, &size);
+
+               GPU_vertbuf_attr_set(vbo, pos_id, idx, &cp->x);
+               idx++;
+       }
+
+       return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
 /* create batch geometry data for current buffer fill shader */
 GPUBatch *DRW_gpencil_get_buffer_fill_geom(bGPdata *gpd)
 {
index 87772a7201556219b4f6745348fe82c11953f2dc..3b1c51cb8eb440027a4a7562688fb3c517b65e59 100644 (file)
@@ -434,7 +434,7 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
                DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
        }
        else {
-               /* for drawing always on front */
+               /* for drawing always on predefined z-depth */
                DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
        }
 
@@ -527,7 +527,7 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(
                DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&gpd->xray_mode, 1);
        }
        else {
-               /* for drawing always on front */
+               /* for drawing always on on predefined z-depth */
                DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
        }
 
@@ -1161,6 +1161,9 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
 {
        GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
        GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+       const DRWContextState *draw_ctx = DRW_context_state_get();
+       View3D *v3d = draw_ctx->v3d;
+       const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true;
        Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
        bGPdata *gpd_eval = ob->data;
        /* need the original to avoid cow overhead while drawing */
@@ -1184,7 +1187,7 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
        /* 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() && (gpd->runtime.sbuffer_size > 0)) {
+       if (gpd->runtime.sbuffer_size > 0) {
                if ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
                        /* It should also be noted that sbuffer contains temporary point types
                         * i.e. tGPspoints NOT bGPDspoints
@@ -1194,11 +1197,11 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
                        if (gpd->runtime.sbuffer_size > 1) {
                                if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
                                        stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create(
-                                               e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
+                                               e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
                                }
                                else {
                                        stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create(
-                                               e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
+                                               e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
                                }
 
                                /* clean previous version of the batch */
@@ -1211,32 +1214,32 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
                                /* use unit matrix because the buffer is in screen space and does not need conversion */
                                if (gpd->runtime.mode == GP_STYLE_MODE_LINE) {
                                        e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_stroke_geom(
-                                               gpd, lthick);
+                                               gpd, lthick);
                                }
                                else {
                                        e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom(
-                                               gpd, lthick);
+                                               gpd, lthick);
                                }
 
                                if (gp_style->flag & GP_STYLE_STROKE_SHOW) {
                                        DRW_shgroup_call_add(
-                                               stl->g_data->shgrps_drawing_stroke,
-                                               e_data->batch_buffer_stroke,
-                                               stl->storage->unit_matrix);
+                                               stl->g_data->shgrps_drawing_stroke,
+                                               e_data->batch_buffer_stroke,
+                                               stl->storage->unit_matrix);
                                }
 
                                if ((gpd->runtime.sbuffer_size >= 3) &&
-                                   (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
-                                   ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) &&
-                                   ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) &&
-                                   (gp_style->flag & GP_STYLE_FILL_SHOW))
+                                       (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
+                                       ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) &&
+                                       ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) &&
+                                       (gp_style->flag & GP_STYLE_FILL_SHOW))
                                {
                                        /* if not solid, fill is simulated with solid color */
                                        if (gpd->runtime.bfill_style > 0) {
                                                gpd->runtime.sfill[3] = 0.5f;
                                        }
                                        stl->g_data->shgrps_drawing_fill = DRW_shgroup_create(
-                                               e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
+                                               e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
 
                                        /* clean previous version of the batch */
                                        if (stl->storage->buffer_fill) {
@@ -1247,15 +1250,44 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
 
                                        e_data->batch_buffer_fill = DRW_gpencil_get_buffer_fill_geom(gpd);
                                        DRW_shgroup_call_add(
-                                               stl->g_data->shgrps_drawing_fill,
-                                               e_data->batch_buffer_fill,
-                                               stl->storage->unit_matrix);
+                                               stl->g_data->shgrps_drawing_fill,
+                                               e_data->batch_buffer_fill,
+                                               stl->storage->unit_matrix);
                                        stl->storage->buffer_fill = true;
                                }
                                stl->storage->buffer_stroke = true;
                        }
                }
        }
+
+       /* control points */
+       if ((overlay) && (gpd->runtime.tot_cp_points > 0) &&
+               ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
+               ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) &&
+               ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0))
+       {
+
+               DRWShadingGroup *shgrp = DRW_shgroup_create(
+                       e_data->gpencil_edit_point_sh, psl->drawing_pass);
+               const float *viewport_size = DRW_viewport_size_get();
+               DRW_shgroup_uniform_vec2(shgrp, "Viewport", viewport_size, 1);
+
+               /* clean previous version of the batch */
+               if (stl->storage->buffer_ctrlpoint) {
+                       GPU_BATCH_DISCARD_SAFE(e_data->batch_buffer_ctrlpoint);
+                       MEM_SAFE_FREE(e_data->batch_buffer_ctrlpoint);
+                       stl->storage->buffer_ctrlpoint = false;
+               }
+
+               e_data->batch_buffer_ctrlpoint = DRW_gpencil_get_buffer_ctrlpoint_geom(gpd);
+
+               DRW_shgroup_call_add(
+                       shgrp,
+                       e_data->batch_buffer_ctrlpoint,
+                       stl->storage->unit_matrix);
+
+               stl->storage->buffer_ctrlpoint = true;
+       }
 }
 
 /* create all missing batches */
index 66d9bac1fac40c44898ba10368b917d005458ebd..dc62ca34c929f5c3eeb23f8addbfd510bd9934b9 100644 (file)
@@ -289,6 +289,9 @@ static void GPENCIL_engine_free(void)
        GPU_BATCH_DISCARD_SAFE(e_data.batch_buffer_fill);
        MEM_SAFE_FREE(e_data.batch_buffer_fill);
 
+       GPU_BATCH_DISCARD_SAFE(e_data.batch_buffer_ctrlpoint);
+       MEM_SAFE_FREE(e_data.batch_buffer_ctrlpoint);
+
        GPU_BATCH_DISCARD_SAFE(e_data.batch_grid);
        MEM_SAFE_FREE(e_data.batch_grid);
 
index 3c0675c5e8ce840244c95e81b7cd82b0777ea814..6f2b40136ca00427863963138ac108c63a2b9782 100644 (file)
@@ -133,6 +133,7 @@ typedef struct GPENCIL_Storage {
        bool reset_cache;
        bool buffer_stroke;
        bool buffer_fill;
+       bool buffer_ctrlpoint;
        const float *pixsize;
        float render_pixsize;
        int tonemapping;
@@ -299,6 +300,7 @@ typedef struct GPENCIL_e_data {
        /* for buffer only one batch is nedeed because the drawing is only of one stroke */
        GPUBatch *batch_buffer_stroke;
        GPUBatch *batch_buffer_fill;
+       GPUBatch *batch_buffer_ctrlpoint;
 
        /* grid geometry */
        GPUBatch *batch_grid;
@@ -386,6 +388,7 @@ void DRW_gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, struct bGPDstr
 struct GPUBatch *DRW_gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, short thickness);
 struct GPUBatch *DRW_gpencil_get_buffer_fill_geom(struct bGPdata *gpd);
 struct GPUBatch *DRW_gpencil_get_buffer_point_geom(struct bGPdata *gpd, short thickness);
+struct GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(struct bGPdata *gpd);
 struct GPUBatch *DRW_gpencil_get_grid(Object *ob);
 
 /* object cache functions */
index b44c9105e107fba675b7aac2ae0d23ab53771e32..c16ea84ec812375528150827f2660351612032d0 100644 (file)
@@ -1430,63 +1430,6 @@ void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const in
        glDisable(GL_BLEND);
 }
 
-/* draw interpolate strokes (used only while operator is running) */
-void ED_gp_draw_primitives(const bContext *C, tGPDprimitive *tgpi, const int type)
-{
-       tGPDdraw tgpw;
-       ARegion *ar = CTX_wm_region(C);
-       RegionView3D *rv3d = ar->regiondata;
-
-       /* if idle, do not draw */
-       if (tgpi->flag == 0) {
-               return;
-       }
-
-       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);
-       /* calculate parent position */
-       ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpi->gpl, tgpw.diff_mat);
-       if (tgpi->gpf) {
-               tgpw.gps = tgpi->gpf->strokes.first;
-               if (tgpw.gps->totpoints > 0) {
-                       tgpw.gpl = tgpi->gpl;
-                       tgpw.gpf = tgpi->gpf;
-                       tgpw.t_gpf = tgpi->gpf;
-
-                       tgpw.lthick = tgpi->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)
 {
index 2a92185cdd55a2426f9cfd02422d8384c07446bd..e2cbeda273373d771d320195dde15d55974102e6 100644 (file)
@@ -138,6 +138,7 @@ typedef struct tGPDinterpolate {
 
 /* Temporary primitive operation data */
 typedef struct tGPDprimitive {
+       struct Main *bmain;               /* main database pointer */
        struct Depsgraph *depsgraph;
        struct wmWindow *win;             /* window where painting originated */
        struct Scene *scene;              /* current scene from context */
@@ -154,25 +155,34 @@ typedef struct tGPDprimitive {
        struct bGPDlayer *gpl;            /* layer */
        struct bGPDframe *gpf;            /* frame */
        int type;                         /* type of primitive */
-       short cyclic;                       /* cyclic option */
-       short flip;                         /* flip option */
+       int orign_type;                   /* original type of primitive */
+       bool curve;                       /* type of primitive is a curve */
+       short flip;                       /* flip option */
+       tGPspoint *points;                /* array of data-points for stroke */
+       int point_count;                  /* number of edges allocated */
+       int tot_stored_edges;             /* stored number of polygon edges */
        int tot_edges;                    /* number of polygon edges */
-       int top[2];                       /* first box corner */
-       int bottom[2];                    /* last box corner */
-       int origin[2];                    /* initial box corner */
+       float origin[2];                  /* initial box corner */
+       float start[2];                   /* first box corner */
+       float end[2];                     /* last box corner */
+       float midpoint[2];                /* midpoint box corner */
+       float cp1[2];                     /* first control point */
+       float cp2[2];                     /* second control point */
+       int sel_cp;                       /* flag to determine control point is selected */
        int flag;                         /* flag to determine operations in progress */
+       float mval[2];                    /* recorded mouse-position */
+       float mvalo[2];                   /* previous recorded mouse-position */
 
        int lock_axis;                    /* lock to viewport axis */
+       struct RNG *rng;
 
        NumInput num;                     /* numeric input */
-       void *draw_handle_3d;             /* handle for drawing strokes while operator is running 3d stuff */
 } tGPDprimitive;
 
 
 /* Modal Operator Drawing Callbacks ------------------------ */
 
 void ED_gp_draw_interpolation(const struct bContext *C, struct tGPDinterpolate *tgpi, const int type);
-void ED_gp_draw_primitives(const struct bContext *C, struct tGPDprimitive *tgpi, const int type);
 void ED_gp_draw_fill(struct tGPDdraw *tgpw);
 
 /* ***************************************************** */
@@ -369,7 +379,8 @@ enum {
        GP_STROKE_BOX = -1,
        GP_STROKE_LINE = 1,
        GP_STROKE_CIRCLE = 2,
-       GP_STROKE_ARC = 3
+       GP_STROKE_ARC = 3,
+       GP_STROKE_CURVE = 4
 };
 
 
index 68d0f6506d0db9dd2552a05ecd0cba8bb6aae2ac..6d8f337bfc2224fd283a138899f2ddcc226ce024 100644 (file)
@@ -1066,7 +1066,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
                                                }
                                                last_valid = i;
                                        }
-                                       /* invalidate any point other point, to interpolate between
+                                       /* 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)) {
index 029db23499e6a9c47057c372581bf2189f155a96..0e4f95588425e7d1b7e44b3915d4a93d118fc520 100644 (file)
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
 #include "BLI_math.h"
+#include "BLI_rand.h"
 
 #include "BLT_translation.h"
 
+#include "PIL_time.h"
+
 #include "DNA_brush_types.h"
 #include "DNA_gpencil_types.h"
 #include "DNA_meshdata_types.h"
@@ -54,6 +57,7 @@
 #include "DNA_view3d_types.h"
 
 #include "BKE_brush.h"
+#include "BKE_colortools.h"
 #include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_global.h"
@@ -64,6 +68,7 @@
 #include "BKE_report.h"
 
 #include "UI_interface.h"
+#include "UI_resources.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
 
 #define MIN_EDGES 2
 #define MAX_EDGES 128
+#define MAX_CP 128
 
 #define IDLE 0
 #define IN_PROGRESS 1
+#define IN_CURVE_EDIT 2
+#define IN_MOVE 3
+
+#define SELECT_NONE 0
+#define SELECT_START 1
+#define SELECT_CP1 2
+#define SELECT_CP2 3
+#define SELECT_END 4
+
+#define BIG_SIZE_CTL    15
+#define MID_SIZE_CTL    10
+#define SMALL_SIZE_CTL   8
+
+#define MOVE_NONE 0
+#define MOVE_ENDS 1
+#define MOVE_CP 2
 
   /* ************************************************ */
   /* Core/Shared Utilities */
 
+/* clear the session buffers (call this before AND after a paint operation) */
+static void gp_session_validatebuffer(tGPDprimitive *p)
+{
+       bGPdata *gpd = p->gpd;
+
+       /* clear memory of buffer (or allocate it if starting a new session) */
+       if (gpd->runtime.sbuffer) {
+               memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX);
+       }
+       else {
+               gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
+       }
+
+       /* reset indices */
+       gpd->runtime.sbuffer_size = 0;
+
+       /* reset flags */
+       gpd->runtime.sbuffer_sflag = 0;
+       gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE;
+
+       if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE))
+               gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC;
+}
+
+static void gp_init_colors(tGPDprimitive *p)
+{
+       bGPdata *gpd = p->gpd;
+       Brush *brush = p->brush;
+
+       Material *ma = NULL;
+       MaterialGPencilStyle *gp_style = NULL;
+
+       /* use brush material */
+       ma = BKE_gpencil_get_material_from_brush(brush);
+
+       /* if no brush defaults, get material and color info */
+       if ((ma == NULL) || (ma->gp_style == NULL)) {
+               BKE_gpencil_material_ensure(p->bmain, p->ob);
+
+               /* assign always the first material to the brush */
+               p->mat = give_current_material(p->ob, 1);
+               brush->gpencil_settings->material = p->mat;
+       }
+       else {
+               p->mat = ma;
+       }
+
+       /* check if the material is already on object material slots and add it if missing */
+       if (BKE_gpencil_get_material_index(p->ob, p->mat) == 0) {
+               BKE_object_material_slot_add(p->bmain, p->ob);
+               assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_USERPREF);
+       }
+
+       /* assign color information to temp data */
+       gp_style = p->mat->gp_style;
+       if (gp_style) {
+
+               /* set colors */
+               copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
+               copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
+               /* add some alpha to make easy the filling without hide strokes */
+               if (gpd->runtime.sfill[3] > 0.8f) {
+                       gpd->runtime.sfill[3] = 0.8f;
+               }
+
+               gpd->runtime.mode = (short)gp_style->mode;
+               gpd->runtime.bstroke_style = gp_style->stroke_style;
+               gpd->runtime.bfill_style = gp_style->fill_style;
+       }
+}
+
+/* Helper to square a primitive */
+static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, const float y)
+{
+       float w = fabsf(x);
+       float h = fabsf(y);
+       if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
+               if (w > h)
+                       tgpi->end[1] = tgpi->origin[1] + x;
+               else
+                       tgpi->end[0] = tgpi->origin[0] + y;
+       }
+       else {
+               if (w > h)
+                       tgpi->end[1] = tgpi->origin[1] - x;
+               else
+                       tgpi->end[0] = tgpi->origin[0] - y;
+       }
+}
+
+/* Helper to rotate point around origin */
+static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle)
+{
+       float pt[2];
+       float r[2];
+       sub_v2_v2v2(pt, p, origin);
+       rotate_v2_v2fl(r, pt, angle);
+       add_v2_v2v2(v, r, origin);
+}
+
+/* Helper to rotate line around line centre */
+static void gp_primitive_rotate_line(float va[2], float vb[2], const float a[2], const float b[2], const float angle)
+{
+       float midpoint[2];
+       mid_v2_v2v2(midpoint, a, b);
+       gp_rotate_v2_v2v2fl(va, a, midpoint, angle);
+       gp_rotate_v2_v2v2fl(vb, b, midpoint, angle);
+}
+
+/* Helper to update cps */
+static void gp_primitive_update_cps(tGPDprimitive *tgpi)
+{
+       if (!tgpi->curve) {
+               mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+               copy_v2_v2(tgpi->cp1, tgpi->midpoint);
+               copy_v2_v2(tgpi->cp2, tgpi->cp1);
+       }
+       else if (tgpi->type == GP_STROKE_CURVE) {
+               mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+               copy_v2_v2(tgpi->cp1, tgpi->midpoint);
+               copy_v2_v2(tgpi->cp2, tgpi->cp1);
+       }
+       else if (tgpi->type == GP_STROKE_ARC) {
+               if (tgpi->flip) {
+                       gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
+               }
+               else {
+                       gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
+               }
+       }
+}
+
   /* Poll callback for primitive operators */
 static bool gpencil_primitive_add_poll(bContext *C)
 {
@@ -129,6 +283,15 @@ static bool gpencil_primitive_add_poll(bContext *C)
        return 1;
 }
 
+/* Allocate memory to stroke, adds MAX_EDGES on every call */
+static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi) {
+       tgpi->point_count += (tgpi->type == GP_STROKE_BOX) ? (MAX_EDGES * 4 + 1) : (MAX_EDGES + 1);
+       bGPDstroke *gpsf = tgpi->gpf->strokes.first;
+       gpsf->points = MEM_reallocN(gpsf->points, sizeof(bGPDspoint) * tgpi->point_count);
+       if (gpsf->dvert != NULL)
+               gpsf->dvert = MEM_reallocN(gpsf->dvert, sizeof(MDeformVert) * tgpi->point_count);
+       tgpi->points = MEM_reallocN(tgpi->points, sizeof(tGPspoint) * tgpi->point_count);
+}
 
 /* ****************** Primitive Interactive *********************** */
 
@@ -167,13 +330,11 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
 
        /* enable recalculation flag by default */
        gps->flag |= GP_STROKE_RECALC_CACHES;
+       gps->flag &= ~GP_STROKE_SELECT;
        /* the polygon must be closed, so enabled cyclic */
-       if (tgpi->type != GP_STROKE_LINE && tgpi->type != GP_STROKE_ARC) {
+       if (ELEM(tgpi->type,GP_STROKE_BOX ,GP_STROKE_CIRCLE))
                gps->flag |= GP_STROKE_CYCLIC;
-       }
-       else {
-               gps->flag &= ~GP_STROKE_CYCLIC;
-       }
+
        gps->flag |= GP_STROKE_3DSPACE;
 
        gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat) - 1;
@@ -188,19 +349,38 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
 
        /* add to strokes */
        BLI_addtail(&tgpi->gpf->strokes, gps);
-}
 
-/* ----------------------- */
-/* Drawing Callbacks */
+       /* allocate memory for storage points */
+       gpencil_primitive_allocate_memory(tgpi);
+
+       /* Random generator, only init once. */
+       tgpi->rng = BLI_rng_new((uint)0);
+}
 
-/* Drawing callback for modal operator in 3d mode */
-static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg)
+/* add new segment to curve */
+static void gpencil_primitive_add_segment(tGPDprimitive *tgpi)
 {
-       tGPDprimitive *tgpi = (tGPDprimitive *)arg;
-       ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW);
+       if(tgpi->tot_stored_edges > 0)
+               tgpi->tot_stored_edges += (tgpi->tot_edges - 1);
+       else
+               tgpi->tot_stored_edges += tgpi->tot_edges;
+       gpencil_primitive_allocate_memory(tgpi);
 }
 
-/* ----------------------- */
+/* Helper: set control point */
+static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
+{
+       bGPDcontrolpoint *cp_points = tgpi->gpd->runtime.cp_points;
+
+       if (tgpi->gpd->runtime.tot_cp_points < MAX_CP) {
+               CLAMP(size, 5, 20);
+               bGPDcontrolpoint *cp = &cp_points[tgpi->gpd->runtime.tot_cp_points];
+               copy_v2_v2(&cp->x, p);
+               copy_v4_v4(cp->color, color);
+               cp->size = size;
+               tgpi->gpd->runtime.tot_cp_points += 1;
+       }
+}
 
 /* Helper: Draw status message while the user is running the operator */
 static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi)
@@ -209,20 +389,23 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi
        char status_str[UI_MAX_DRAW_STR];
        char msg_str[UI_MAX_DRAW_STR];
 
-       if (tgpi->type == GP_STROKE_BOX) {
-               BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
+       if (tgpi->type == GP_STROKE_LINE) {
+               BLI_strncpy(msg_str, IFACE_("Line: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to align, Alt to center"), UI_MAX_DRAW_STR);
+       }
+       else if (tgpi->type == GP_STROKE_BOX) {
+               BLI_strncpy(msg_str, IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
        }
-       else if (tgpi->type == GP_STROKE_LINE) {
-               BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Alt to center"), UI_MAX_DRAW_STR);
+       else if (tgpi->type == GP_STROKE_CIRCLE) {
+               BLI_strncpy(msg_str, IFACE_("Circle: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
        }
        else if (tgpi->type == GP_STROKE_ARC) {
-               BLI_strncpy(msg_str, IFACE_("Arc: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, F to flip, C to Close"), UI_MAX_DRAW_STR);
+               BLI_strncpy(msg_str, IFACE_("Arc: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, M: Flip"), UI_MAX_DRAW_STR);
        }
-       else {
-               BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
+       else if (tgpi->type == GP_STROKE_CURVE) {
+               BLI_strncpy(msg_str, IFACE_("Curve: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, E: extrude"), UI_MAX_DRAW_STR);
        }
 
-       if (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC) {
+       if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) {
                if (hasNumInput(&tgpi->num)) {
                        char str_offs[NUM_STR_REP_LEN];
 
@@ -233,114 +416,208 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi
                        if (tgpi->flag == IN_PROGRESS) {
                                BLI_snprintf(
                                        status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges,
-                                       tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
+                                       tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]);
                        }
                        else {
                                BLI_snprintf(
                                        status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges,
-                                       tgpi->bottom[0], tgpi->bottom[1]);
+                                       tgpi->end[0], tgpi->end[1]);
                        }
                }
        }
        else {
                if (tgpi->flag == IN_PROGRESS) {
                        BLI_snprintf(
-                               status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str,
-                               tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
+                               status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges,
+                               tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]);
                }
                else {
                        BLI_snprintf(
                                status_str, sizeof(status_str), "%s: (%d, %d)", msg_str,
-                               tgpi->bottom[0], tgpi->bottom[1]);
+                               tgpi->end[0], tgpi->end[1]);
                }
        }
        ED_workspace_status_text(C, status_str);
 }
 
-/* ----------------------- */
-
 /* create a rectangle */
 static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
 {
-       BLI_assert(tgpi->tot_edges == 4);
-
-       points2D[0].x = (float)tgpi->top[0];
-       points2D[0].y = (float)tgpi->top[1];
-
-       points2D[1].x = (float)tgpi->bottom[0];
-       points2D[1].y = (float)tgpi->top[1];
-
-       points2D[2].x = (float)tgpi->bottom[0];
-       points2D[2].y = (float)tgpi->bottom[1];
+       float coords[5][2];
+
+       coords[0][0] = tgpi->start[0];
+       coords[0][1] = tgpi->start[1];
+       coords[1][0] = tgpi->end[0];
+       coords[1][1] = tgpi->start[1];
+       coords[2][0] = tgpi->end[0];
+       coords[2][1] = tgpi->end[1];
+       coords[3][0] = tgpi->start[0];
+       coords[3][1] = tgpi->end[1];
+       coords[4][0] = tgpi->start[0];
+       coords[4][1] = tgpi->start[1];
+
+       const float step = 1.0f / (float)(tgpi->tot_edges);
+       int i = tgpi->tot_stored_edges;
+
+       for (int j = 0; j < 4; j++) {
+               float a = 0.0f;
+               for (int k = 0; k < tgpi->tot_edges; k++) {
+                       tGPspoint *p2d = &points2D[i];
+                       interp_v2_v2v2(&p2d->x, coords[j], coords[j + 1], a);
+                       a += step;
+                       i++;
+               }
+       }
 
-       points2D[3].x = (float)tgpi->top[0];
-       points2D[3].y = (float)tgpi->bottom[1];
+       mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+       float color[4];
+       UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+       if (tgpi->tot_stored_edges) {
+               UI_GetThemeColor4fv(TH_REDALERT, color);
+               gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+       }
+       else
+               gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+       UI_GetThemeColor4fv(TH_REDALERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
 }
 
 /* create a line */
 static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D)
 {
-       BLI_assert(tgpi->tot_edges == 2);
+       if (tgpi->tot_edges == 2) {
+               int i = tgpi->tot_stored_edges;
 
-       points2D[0].x = (float)tgpi->top[0];
-       points2D[0].y = (float)tgpi->top[1];
+               points2D[i].x = tgpi->start[0];
+               points2D[i].y = tgpi->start[1];
 
-       points2D[1].x = (float)tgpi->bottom[0];
-       points2D[1].y = (float)tgpi->bottom[1];
+               points2D[i + 1].x = tgpi->end[0];
+               points2D[i + 1].y = tgpi->end[1];
+       }
+       else {
+               const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+               const float step = 1.0f / (float)(tgpi->tot_edges - 1);
+               float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+               for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
+                       tGPspoint *p2d = &points2D[i];
+                       interp_v2_v2v2(&p2d->x, tgpi->start, tgpi->end, a);
+                       a += step;
+               }
+       }
+       float color[4];
+       UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+       if (tgpi->tot_stored_edges) {
+               UI_GetThemeColor4fv(TH_REDALERT, color);
+               gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+       }
+       else
+               gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
 }
 
 /* create an arc */
 static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
 {
-       const int totpoints = tgpi->tot_edges;
-       const float step = M_PI_2 / (float)(totpoints - 1);
-       float length[2];
-       int start[2];
-       int end[2];
-       float a = 0.0f;
-
-       start[0] = tgpi->top[0];
-       start[1] = tgpi->top[1];
-       end[0] = tgpi->bottom[0];
-       end[1] = tgpi->bottom[1];
-
-       if (tgpi->flip) {
-               SWAP(int, end[0], start[0]);
-               SWAP(int, end[1], start[1]);
+       const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+       const float step = M_PI_2 / (float)(tgpi->tot_edges - 1);
+       float start[2];
+       float end[2];
+       float cp1[2];
+       float corner[2];
+       float midpoint[2];
+       float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+       mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+       copy_v2_v2(start, tgpi->start);
+       copy_v2_v2(end, tgpi->end);
+       copy_v2_v2(cp1, tgpi->cp1);
+       copy_v2_v2(midpoint, tgpi->midpoint);
+
+       corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
+       corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
+       
+       for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
+               tGPspoint *p2d = &points2D[i];
+               p2d->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
+               p2d->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
+               a += step;
        }
+       float color[4];
+       UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+       if (tgpi->tot_stored_edges) {
+               UI_GetThemeColor4fv(TH_REDALERT, color);
+               gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+       }
+       else
+               gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+       UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color);
+       gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+}
 
-       length[0] = end[0] - start[0];
-       length[1] = end[1] - start[1];
-
-       for (int i = 0; i < totpoints; i++) {
+/* create a bezier */
+static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
+{
+       const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+       const float step = 1.0f / (float)(tgpi->tot_edges - 1);
+       float bcp1[2];
+       float bcp2[2];
+       float bcp3[2];
+       float bcp4[2];
+       float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+       copy_v2_v2(bcp1, tgpi->start);
+       copy_v2_v2(bcp2, tgpi->cp1);
+       copy_v2_v2(bcp3, tgpi->cp2);
+       copy_v2_v2(bcp4, tgpi->end);
+
+       for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
                tGPspoint *p2d = &points2D[i];
-               p2d->x = (start[0] + sinf(a) * length[0]);
-               p2d->y = (end[1] - cosf(a) * length[1]);
+               interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a);
                a += step;
        }
+       float color[4];
+       UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+       if (tgpi->tot_stored_edges) {
+               UI_GetThemeColor4fv(TH_REDALERT, color);
+               gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+       }
+       else
+               gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+       UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color);
+       gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+       gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
 }
 
 /* create a circle */
 static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
 {
-       const int totpoints = tgpi->tot_edges;
-       const float step = (2.0f * M_PI) / (float)(totpoints);
+       const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+       const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges);
        float center[2];
        float radius[2];
        float a = 0.0f;
 
-       /* TODO: Use math-lib functions for these? */
-       center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f);
-       center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f);
-       radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f));
-       radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f));
+       center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f);
+       center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f);
+       radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f));
+       radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f));
 
-       for (int i = 0; i < totpoints; i++) {
+       for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
                tGPspoint *p2d = &points2D[i];
                p2d->x = (center[0] + cosf(a) * radius[0]);
                p2d->y = (center[1] + sinf(a) * radius[1]);
                a += step;
        }
+       float color[4];
+       UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+       gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+       gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+       UI_GetThemeColor4fv(TH_REDALERT, color);
+       gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
 }
 
 /* Helper: Update shape of the stroke */
@@ -348,18 +625,26 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
 {
        ToolSettings *ts = tgpi->scene->toolsettings;
        bGPdata *gpd = tgpi->gpd;
+       Brush *brush = tgpi->brush;
        bGPDstroke *gps = tgpi->gpf->strokes.first;
+       GP_Sculpt_Settings *gset = &ts->gp_sculpt;
+       int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
+       char *align_flag = &ts->gpencil_v3d_align;
+       bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
 
-       /* realloc points to new size */
-       /* TODO: only do this if the size has changed? */
-       gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges);
-       if (gps->dvert != NULL) {
-               gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges);
-       }
-       gps->totpoints = tgpi->tot_edges;
+       if (tgpi->type == GP_STROKE_BOX)
+               gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges);
+       else
+               gps->totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+
+       if (tgpi->tot_stored_edges)
+               gps->totpoints--;
+
+       tgpi->gpd->runtime.tot_cp_points = 0;
 
        /* compute screen-space coordinates for points */
-       tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D");
+       tGPspoint *points2D = tgpi->points;
+
        switch (tgpi->type) {
                case GP_STROKE_BOX:
                        gp_primitive_rectangle(tgpi, points2D);
@@ -372,46 +657,254 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
                        break;
                case GP_STROKE_ARC:
                        gp_primitive_arc(tgpi, points2D);
-                       if (tgpi->cyclic)
-                               gps->flag |= GP_STROKE_CYCLIC;
-                       else
-                               gps->flag &= ~GP_STROKE_CYCLIC;
                        break;
+               case GP_STROKE_CURVE:
+                       gp_primitive_bezier(tgpi, points2D);
                default:
                        break;
-       }
+               }
 
        /* convert screen-coordinates to 3D coordinates */
+       gp_session_validatebuffer(tgpi);
+       gp_init_colors(tgpi);
+       if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
+               curvemapping_initialize(ts->gp_sculpt.cur_primitive);
+       }
+       if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
+               curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter);
+       }
+       if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+               curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength);
+       }
+
+       /* get an array of depths, far depths are blended */
+       float *depth_arr = NULL;
+       if (is_depth) {
+               int i;
+               int mval_i[2], mval_prev[2] = { 0 };
+               bool interp_depth = false;
+               bool found_depth = false;
+
+               /* need to restore the original projection settings before packing up */
+               view3d_region_operator_needs_opengl(tgpi->win, tgpi->ar);
+               ED_view3d_autodist_init(tgpi->depsgraph, tgpi->ar, tgpi->v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+
+               depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
+               tGPspoint *ptc = &points2D[0];
+               for (i = 0; i < gps->totpoints; i++, ptc++) {
+                       round_v2i_v2fl(mval_i, &ptc->x);
+                       if ((ED_view3d_autodist_depth(tgpi->ar, mval_i, depth_margin, depth_arr + i) == 0) &&
+                               (i && (ED_view3d_autodist_depth_seg(tgpi->ar, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0)))
+                       {
+                               interp_depth = true;
+                       }
+                       else {
+                               found_depth = true;
+                       }
+                       copy_v2_v2_int(mval_prev, mval_i);
+               }
+
+               if (!found_depth) {
+                       for (i = 0; i < gps->totpoints; i++) {
+                               depth_arr[i] = 0.9999f;
+                       }
+               }
+               else {
+                       /* if all depth are too high disable */
+                       bool valid_depth = false;
+                       for (i = 0; i < gps->totpoints; i++) {
+                               if (depth_arr[i] < 0.9999f) {
+                                       valid_depth = true;
+                                       break;
+                               }
+                       }
+                       if (!valid_depth) {
+                               MEM_SAFE_FREE(depth_arr);
+                               is_depth = false;
+                       }
+                       else {
+                               if ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) ||
+                                       (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST))
+                               {
+                                       int first_valid = 0;
+                                       int last_valid = 0;
+
+                                       /* find first valid contact point */
+                                       for (i = 0; i < gps->totpoints; i++) {
+                                               if (depth_arr[i] != FLT_MAX)
+                                                       break;
+                                       }
+                                       first_valid = i;
+
+                                       /* find last valid contact point */
+                                       if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST) {
+                                               last_valid = first_valid;
+                                       }
+                                       else {
+                                               for (i = gps->totpoints - 1; i >= 0; i--) {
+                                                       if (depth_arr[i] != FLT_MAX)
+                                                               break;
+                                               }
+                                               last_valid = i;
+                                       }
+
+                                       /* invalidate any other point, to interpolate between
+                                        * first and last contact in an imaginary line between them */
+                                       for (i = 0; i < gps->totpoints; i++) {
+                                               if ((i != first_valid) && (i != last_valid)) {
+                                                       depth_arr[i] = FLT_MAX;
+                                               }
+                                       }
+                                       interp_depth = true;
+                               }
+
+                               if (interp_depth) {
+                                       interp_sparse_array(depth_arr, gps->totpoints, FLT_MAX);
+                               }
+                       }
+               }
+       }
+
+       /* load stroke points and sbuffer */
        for (int i = 0; i < gps->totpoints; i++) {
                bGPDspoint *pt = &gps->points[i];
                tGPspoint *p2d = &points2D[i];
 
+               /* Copy points to buffer */
+               tGPspoint *tpt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size);
+
+               /* Store original points */
+               float tmp_xyp[2];
+               copy_v2_v2(tmp_xyp, &p2d->x);
+
+               /* calc pressure */
+               float curve_pressure = 1.0;
+               float pressure = 1.0;
+               float strength = brush->gpencil_settings->draw_strength;
+
+               /* normalize value to evaluate curve */
+               if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
+                       float value = (float)i / (gps->totpoints - 1);
+                       curve_pressure = curvemapping_evaluateF(gset->cur_primitive, 0, value);
+                       pressure = curve_pressure;
+               }
+
+               /* apply jitter to position */
+               if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+                       (brush->gpencil_settings->draw_jitter > 0.0f))
+               {
+                       float jitter;
+
+                       if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
+                               jitter = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, curve_pressure);
+                               jitter *= brush->gpencil_settings->draw_sensitivity;
+                       }
+                       else {
+                               jitter = brush->gpencil_settings->draw_jitter;
+                       }
+
+                       const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */
+                       const float rnd = BLI_rng_get_float(tgpi->rng);
+                       const float fac = rnd * exfactor * jitter;
+                       if (rnd > 0.5f) {
+                               add_v2_fl(&p2d->x, -fac);
+                       }
+                       else {
+                               add_v2_fl(&p2d->x, fac);
+                       }
+               }
+
+               /* apply randomness to pressure */
+               if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+                       (brush->gpencil_settings->draw_random_press > 0.0f))
+               {
+                       float rnd = BLI_rng_get_float(tgpi->rng);
+                       if (rnd > 0.5f) {
+                               pressure -= brush->gpencil_settings->draw_random_press * rnd;
+                       }
+                       else {
+                               pressure += brush->gpencil_settings->draw_random_press * rnd;
+                       }
+               }
+
+               /* color strength */
+               if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+                       float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, curve_pressure);
+                       strength *= curvef * brush->gpencil_settings->draw_sensitivity;
+                       strength *= brush->gpencil_settings->draw_strength;
+               }
+               
+               CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
+
+               /* apply randomness to color strength */
+               if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+                       (brush->gpencil_settings->draw_random_strength > 0.0f))
+               {
+                       const float rnd = BLI_rng_get_float(tgpi->rng);
+                       if (rnd > 0.5f) {
+                               strength -= strength * brush->gpencil_settings->draw_random_strength * rnd;
+                       }
+                       else {
+                               strength += strength * brush->gpencil_settings->draw_random_strength * rnd;
+                       }
+                       CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
+               }
+
+               copy_v2_v2(&tpt->x, &p2d->x);
+
+               CLAMP_MIN(pressure, 0.1f);
+
+               tpt->pressure = pressure;
+               tpt->strength = strength;
+               tpt->time = p2d->time;
+               tpt->uv_fac = 1.0f;
+               tpt->uv_rot = p2d->uv_rot;
+
+               gpd->runtime.sbuffer_size++;
+
+               /* add small offset to keep stroke over the surface */
+               if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) {
+                       depth_arr[i] *= (1.0f - gpd->zdepth_offset);
+               }
+
                /* convert screen-coordinates to 3D coordinates */
-               gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x);
+               gp_stroke_convertcoords_tpoint(
+                       tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl,
+                       p2d, depth_arr ? depth_arr + i : NULL,
+                       &pt->x);
 
-               pt->pressure = 1.0f;
-               pt->strength = tgpi->brush->gpencil_settings->draw_strength;
+               pt->pressure = pressure;
+               pt->strength = strength;
                pt->time = 0.0f;
+               pt->flag = 0;
+               pt->uv_fac = 1.0f;
 
                if (gps->dvert != NULL) {
                        MDeformVert *dvert = &gps->dvert[i];
                        dvert->totweight = 0;
                        dvert->dw = NULL;
                }
+
+               /* Restore original points */
+               copy_v2_v2(&p2d->x, tmp_xyp);
        }
 
-       /* if axis locked, reproject to plane locked */
-       if (tgpi->lock_axis > GP_LOCKAXIS_VIEW) {
-               bGPDspoint *tpt = gps->points;
+       /* store cps and convert coords */
+       if (tgpi->gpd->runtime.tot_cp_points > 0) {
+               bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points;
+               for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) {
+                       bGPDcontrolpoint *cp = &cps[i];
+                       gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, (tGPspoint *)cp, NULL, &cp->x);
+               }
+       }
+
+       /* reproject to plane */
+       if (!is_depth) {
                float origin[3];
                ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl,
                        ts->gpencil_v3d_align, origin);
-
-               for (int i = 0; i < gps->totpoints; i++, tpt++) {
-                       ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin,
-                               ts->gp_sculpt.lock_axis - 1,
-                               tpt);
-               }
+               ED_gp_project_stroke_to_plane(
+                       tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
        }
 
        /* if parented change position relative to parent object */
@@ -423,8 +916,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
        /* force fill recalc */
        gps->flag |= GP_STROKE_RECALC_CACHES;
 
-       /* free temp data */
-       MEM_SAFE_FREE(points2D);
+       MEM_SAFE_FREE(depth_arr);
 
        DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
        DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -443,18 +935,14 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive
        gp_primitive_update_strokes(C, tgpi);
 }
 
-/* ----------------------- */
-
 static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
 {
-       tgpi->origin[0] = event->mval[0];
-       tgpi->origin[1] = event->mval[1];
-
-       tgpi->top[0] = event->mval[0];
-       tgpi->top[1] = event->mval[1];
-
-       tgpi->bottom[0] = event->mval[0];
-       tgpi->bottom[1] = event->mval[1];
+       copy_v2fl_v2i(tgpi->mval, event->mval);
+       copy_v2_v2(tgpi->origin, tgpi->mval);
+       copy_v2_v2(tgpi->start, tgpi->mval);
+       copy_v2_v2(tgpi->end, tgpi->mval);
+       copy_v2_v2(tgpi->cp1, tgpi->mval);
+       copy_v2_v2(tgpi->cp2, tgpi->mval);
 }
 
 /* Exit and free memory */
@@ -465,20 +953,35 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op)
 
        /* don't assume that operator data exists at all */
        if (tgpi) {
-               /* remove drawing handler */
-               if (tgpi->draw_handle_3d) {
-                       ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d);
-               }
-
                /* clear status message area */
                ED_workspace_status_text(C, NULL);
 
+               MEM_SAFE_FREE(tgpi->points);
+               tgpi->gpd->runtime.tot_cp_points = 0;
+               MEM_SAFE_FREE(tgpi->gpd->runtime.cp_points);
                /* finally, free memory used by temp data */
                BKE_gpencil_free_strokes(tgpi->gpf);
-               MEM_freeN(tgpi->gpf);
+               MEM_SAFE_FREE(tgpi->gpf);
+
+               /* free random seed */
+               if (tgpi->rng != NULL) {
+                       BLI_rng_free(tgpi->rng);
+               }
+
                MEM_freeN(tgpi);
        }
-       DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+       /* free stroke buffer */
+       if ((gpd != NULL) && (gpd->runtime.sbuffer)) {
+               MEM_SAFE_FREE(gpd->runtime.sbuffer);
+               gpd->runtime.sbuffer = NULL;
+
+               /* clear flags */
+               gpd->runtime.sbuffer_size = 0;
+               gpd->runtime.sbuffer_sflag = 0;
+       }
+
+       DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
        WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
 
        /* clear pointer */
@@ -499,7 +1002,10 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
        tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data");
        op->customdata = tgpi;
 
+       tgpi->points = MEM_callocN(sizeof(tGPspoint), "gp primitive points2D");
+
        /* set current scene and window info */
+       tgpi->bmain = CTX_data_main(C);
        tgpi->scene = scene;
        tgpi->ob = CTX_data_active_object(C);
        tgpi->sa = CTX_wm_area(C);
@@ -509,11 +1015,20 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
        tgpi->depsgraph = CTX_data_depsgraph(C);
        tgpi->win = CTX_wm_window(C);
 
+       /* save original type */
+       tgpi->orign_type = RNA_enum_get(op->ptr, "type");
+
        /* set current frame number */
        tgpi->cframe = cfra_eval;
 
        /* set GP datablock */
        tgpi->gpd = gpd;
+       /* region where paint was originated */
+       tgpi->gpd->runtime.ar = tgpi->ar;
+
+       /* control points */
+       tgpi->gpd->runtime.cp_points = MEM_callocN(sizeof(bGPDcontrolpoint) * MAX_CP, "gp primitive cpoint");
+       tgpi->gpd->runtime.tot_cp_points = 0;
 
        /* getcolor info */
        tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob);
@@ -521,31 +1036,44 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
        /* set parameters */
        tgpi->type = RNA_enum_get(op->ptr, "type");
 
+       if(ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_CURVE))
+               tgpi->curve = true;
+       else
+               tgpi->curve = false;
+
        /* set default edge count */
-       if (tgpi->type == GP_STROKE_CIRCLE) {
-               RNA_int_set(op->ptr, "edges", 64);
-       }
-       else if (tgpi->type == GP_STROKE_ARC) {
-               RNA_int_set(op->ptr, "edges", 32);
-       }
-       else if (tgpi->type == GP_STROKE_BOX) {
-               RNA_int_set(op->ptr, "edges", 4);
-       }
-       else { /* LINE */
-               RNA_int_set(op->ptr, "edges", 2);
+       switch (tgpi->type) {
+               case GP_STROKE_LINE:
+               {
+                       RNA_int_set(op->ptr, "edges", 8);
+                       break;
+               }
+               case GP_STROKE_BOX:
+               {
+                       RNA_int_set(op->ptr, "edges", 8);
+                       break;
+               }
+               case GP_STROKE_CIRCLE:
+               {
+                       RNA_int_set(op->ptr, "edges", 96);
+                       break;
+               }
+               default:
+               {
+                       RNA_int_set(op->ptr, "edges", 64);
+                       break;
+               }
        }
 
+       tgpi->tot_stored_edges = 0;
        tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
        tgpi->flag = IDLE;
-
        tgpi->lock_axis = ts->gp_sculpt.lock_axis;
 
        /* set temp layer, frame and stroke */
        gp_primitive_set_initdata(C, tgpi);
 }
 
-/* ----------------------- */
-
 /* Invoke handler: Initialize the operator */
 static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
@@ -568,9 +1096,6 @@ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *
         */
        op->flag |= OP_IS_MODAL_CURSOR_REGION;
 
-       /* Enable custom drawing handlers */
-       tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW);
-
        /* set cursor to indicate modal */
        WM_cursor_modal_set(win, BC_CROSSCURSOR);
 
@@ -624,7 +1149,6 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin
                        if (dw) {
                                dw->weight = ts->vgroup_weight;
                        }
-
                }
        }
 
@@ -635,25 +1159,136 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin
        gpencil_primitive_exit(C, op);
 }
 
-/* Helper to square a primitive */
-static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const int x, const int y)
+/* edit event handling */
+static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi)
 {
-       int w = abs(x);
-       int h = abs(y);
-       if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
-               if (w > h)
-                       tgpi->bottom[1] = tgpi->origin[1] + x;
-               else
-                       tgpi->bottom[0] = tgpi->origin[0] + y;
+       /* calculate nearest point then set cursor */
+       int move = MOVE_NONE;
+       float a = len_v2v2(tgpi->mval, tgpi->start);
+       float b = len_v2v2(tgpi->mval, tgpi->end);
+
+       float c = len_v2v2(tgpi->mval, tgpi->cp1);
+       float d = len_v2v2(tgpi->mval, tgpi->cp2);
+
+       if (tgpi->flag == IN_CURVE_EDIT) {
+               if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) {
+                       move = MOVE_ENDS;
+                       WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+               }
+               else if(tgpi->curve) {
+                       move = MOVE_CP;
+                       WM_cursor_modal_set(win, BC_HANDCURSOR);
+               }
+               else {
+                       WM_cursor_modal_set(win, BC_CROSSCURSOR);
+               }
        }
-       else {
-               if (w > h)
-                       tgpi->bottom[1] = tgpi->origin[1] - x;
-               else
-                       tgpi->bottom[0] = tgpi->origin[0] - y;
+       else if (tgpi->flag == IN_PROGRESS) {
+               WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+       }
+
+       switch (event->type) {
+               case MOUSEMOVE:
+               {
+                       if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) {
+                               if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) {
+                                       copy_v2_v2(tgpi->start, tgpi->mval);
+                               }
+                               else if (tgpi->sel_cp == SELECT_END) {
+                                       copy_v2_v2(tgpi->end, tgpi->mval);
+                               }
+                               else if (tgpi->sel_cp == SELECT_CP1 || (tgpi->sel_cp == SELECT_CP2 && tgpi->type != GP_STROKE_CURVE)) {
+                                       float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
+                                       float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
+                                       tgpi->cp1[0] += dx;
+                                       tgpi->cp1[1] += dy;
+                                       if (event->shift)
+                                               copy_v2_v2(tgpi->cp2, tgpi->cp1);
+                               }
+                               else if (tgpi->sel_cp == SELECT_CP2) {
+                                       float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
+                                       float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
+                                       tgpi->cp2[0] += dx;
+                                       tgpi->cp2[1] += dy;
+                                       if (event->shift)
+                                               copy_v2_v2(tgpi->cp1, tgpi->cp2);
+                               }
+                               /* update screen */
+                               gpencil_primitive_update(C, op, tgpi);
+                       }                       
+                       break;
+               }
+               case LEFTMOUSE:
+               {
+                       if ((event->val == KM_PRESS)) {
+                               /* find nearest cp based on stroke end points */
+                               if (move == MOVE_ENDS)
+                                       tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END;
+                               else if (move == MOVE_CP)
+                                       tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2;
+                               else
+                                       tgpi->sel_cp = SELECT_NONE;
+                               break;
+                       }
+                       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
+                               /* set control points and enter edit mode */
+                               tgpi->flag = IN_CURVE_EDIT;
+                               gp_primitive_update_cps(tgpi);
+                               gpencil_primitive_update(C, op, tgpi);
+                       }
+                       else {
+                               tgpi->sel_cp = SELECT_NONE;
+                       }
+                       break;
+               }
+               case MKEY:
+               {
+                       if ((event->val == KM_PRESS) &&
+                               (tgpi->curve) &&
+                               (tgpi->orign_type == GP_STROKE_ARC))
+                       {
+                               tgpi->flip ^= 1;
+                               gp_primitive_update_cps(tgpi);
+                               gpencil_primitive_update(C, op, tgpi);
+                       }
+                       break;
+               }
+               case EKEY:
+               {
+                       if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
+                               tgpi->flag = IN_PROGRESS;
+                               WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+                               gpencil_primitive_add_segment(tgpi);
+                               copy_v2_v2(tgpi->start, tgpi->end);
+                               copy_v2_v2(tgpi->origin, tgpi->start);
+                               gp_primitive_update_cps(tgpi);
+                       }
+                       break;
+               }
        }
 }
 
+/* move */
+static void gpencil_primitive_move(tGPDprimitive *tgpi)
+{
+       float move[2];
+       sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
+       
+       bGPDstroke *gps = tgpi->gpf->strokes.first;
+       tGPspoint *points2D = tgpi->points;
+
+       for (int i = 0; i < gps->totpoints; i++) {
+               tGPspoint *p2d = &points2D[i];
+               add_v2_v2(&p2d->x, move);
+       }
+
+       add_v2_v2(tgpi->start, move);
+       add_v2_v2(tgpi->end, move);
+       add_v2_v2(tgpi->cp1, move);
+       add_v2_v2(tgpi->cp2, move);
+       add_v2_v2(tgpi->origin, move);
+}
+
 /* Modal handler: Events handling during interactive part */
 static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
@@ -661,15 +1296,47 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
        wmWindow *win = CTX_wm_window(C);
        const bool has_numinput = hasNumInput(&tgpi->num);
 
+       copy_v2fl_v2i(tgpi->mval, event->mval);
+
+       if (tgpi->flag == IN_MOVE) {
+
+               switch (event->type) {
+                       case MOUSEMOVE:
+                               gpencil_primitive_move(tgpi);
+                               gpencil_primitive_update(C, op, tgpi);
+                               break;
+                       case ESCKEY:
+                       case RIGHTMOUSE:
+                       case LEFTMOUSE:
+                               tgpi->flag = IN_CURVE_EDIT;
+                               break;
+               }
+               copy_v2_v2(tgpi->mvalo, tgpi->mval);
+               return OPERATOR_RUNNING_MODAL;
+       }
+       else if (tgpi->flag != IDLE) {
+               gpencil_primitive_edit_event_handling(C, op, win, event, tgpi);
+       }
+
        switch (event->type) {
                case LEFTMOUSE:
+               {
                        if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) {
                                /* start drawing primitive */
                                /* TODO: Ignore if not in main region yet */
                                tgpi->flag = IN_PROGRESS;
                                gpencil_primitive_interaction_begin(tgpi, event);
                        }
+                       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) {
+                               tgpi->flag = IN_CURVE_EDIT;
+                       }
                        else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
+                               /* set control points and enter edit mode */
+                               tgpi->flag = IN_CURVE_EDIT;
+                               gp_primitive_update_cps(tgpi);
+                               gpencil_primitive_update(C, op, tgpi);
+                       }
+                       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && (tgpi->type != GP_STROKE_CURVE)) {
                                /* stop drawing primitive */
                                tgpi->flag = IDLE;
                                gpencil_primitive_interaction_end(C, op, win, tgpi);
@@ -682,15 +1349,25 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
                                }
                        }
                        break;
-               case RETKEY:  /* confirm */
+               }
+               case SPACEKEY:  /* confirm */
+               case RETKEY:
                {
                        tgpi->flag = IDLE;
                        gpencil_primitive_interaction_end(C, op, win, tgpi);
                        /* done! */
                        return OPERATOR_FINISHED;
                }
-               case ESCKEY:    /* cancel */
                case RIGHTMOUSE:
+               {
+                       if (tgpi->flag == IN_CURVE_EDIT || (tgpi->flag == IN_PROGRESS && tgpi->tot_stored_edges > 0)) {
+                               tgpi->flag = IDLE;
+                               gpencil_primitive_interaction_end(C, op, win, tgpi);
+                               /* done! */
+                               return OPERATOR_FINISHED;
+                       }
+               }
+               case ESCKEY:
                {
                        /* return to normal cursor and header status */
                        ED_workspace_status_text(C, NULL);
@@ -702,76 +1379,95 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
                        /* canceled! */
                        return OPERATOR_CANCELLED;
                }
-               case CKEY:
+               case PADPLUSKEY:
+               case WHEELUPMOUSE:
                {
-                       if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) {
-                               tgpi->cyclic ^= 1;
+                       if ((event->val != KM_RELEASE)) {
+                               tgpi->tot_edges = tgpi->tot_edges + 1;
+                               CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
+                               RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
 
                                /* update screen */
                                gpencil_primitive_update(C, op, tgpi);
                        }
                        break;
                }
-               case FKEY:
+               case PADMINUS:
+               case WHEELDOWNMOUSE:
                {
-                       if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) {
-                               tgpi->flip ^= 1;
+                       if ((event->val != KM_RELEASE)) {
+                               tgpi->tot_edges = tgpi->tot_edges - 1;
+                               CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
+                               RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
 
                                /* update screen */
                                gpencil_primitive_update(C, op, tgpi);
                        }
                        break;
                }
-               case PADPLUSKEY:
-               case WHEELUPMOUSE:
+               case GKEY: /* grab mode */
                {
-                       if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) {
-                               tgpi->tot_edges = tgpi->tot_edges + 1;
-                               CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
-                               RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
+                       if ((event->val == KM_PRESS)) {
+                               tgpi->flag = IN_MOVE;
+                               WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+                       }
+                       break;
+               }
+               case CKEY: /* curve mode */
+               {
+                       if ((event->val == KM_PRESS) &&
+                               (tgpi->orign_type == GP_STROKE_CURVE))
+                       {
+                               switch (tgpi->type) {
+                                       case GP_STROKE_CURVE:
+                                               tgpi->type = GP_STROKE_ARC;
+                                               break;
+                                       default:
+                                       case GP_STROKE_ARC:
+                                               tgpi->type = GP_STROKE_CURVE;
+                                               break;
+                               }
 
-                               /* update screen */
+                               RNA_enum_set(op->ptr, "type", tgpi->type);
+                               gp_primitive_update_cps(tgpi);
                                gpencil_primitive_update(C, op, tgpi);
                        }
                        break;
                }
-               case PADMINUS:
-               case WHEELDOWNMOUSE:
+               case TABKEY:
                {
-                       if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) {
-                               tgpi->tot_edges = tgpi->tot_edges - 1;
-                               CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
-                               RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
-
-                               /* update screen */
+                       if (tgpi->flag == IN_CURVE_EDIT) {
+                               tgpi->flag = IN_PROGRESS;
+                               WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+                               gp_primitive_update_cps(tgpi);
                                gpencil_primitive_update(C, op, tgpi);
                        }
                        break;
                }
                case MOUSEMOVE: /* calculate new position */
                {
+                       if (tgpi->flag == IN_CURVE_EDIT) {
+                               break;
+                       }
                        /* only handle mousemove if not doing numinput */
                        if (has_numinput == false) {
                                /* update position of mouse */
-                               tgpi->bottom[0] = event->mval[0];
-                               tgpi->bottom[1] = event->mval[1];
-                               tgpi->top[0] = tgpi->origin[0];
-                               tgpi->top[1] = tgpi->origin[1];
+                               copy_v2_v2(tgpi->end, tgpi->mval);
+                               copy_v2_v2(tgpi->start, tgpi->origin);
                                if (tgpi->flag == IDLE) {
-                                       tgpi->origin[0] = event->mval[0];
-                                       tgpi->origin[1] = event->mval[1];
+                                       copy_v2_v2(tgpi->origin, tgpi->mval);
                                }
                                /* Keep square if shift key */
                                if (event->shift) {
-                                       int x = tgpi->bottom[0] - tgpi->origin[0];
-                                       int y = tgpi->bottom[1] - tgpi->origin[1];
-                                       if (tgpi->type == GP_STROKE_LINE) {
-                                               float angle = fabsf(atan2f((float)y, (float)x));
-                                               if (angle < 0.4f || angle > (M_PI - 0.4f)) {
-                                                       tgpi->bottom[1] = tgpi->origin[1];
+                                       float x = tgpi->end[0] - tgpi->origin[0];
+                                       float y = tgpi->end[1] - tgpi->origin[1];
+                                       if (tgpi->type == GP_STROKE_LINE || tgpi->curve) {
+                                               float angle = fabsf(atan2f(y, x));
+                                               if (angle < 0.4f || angle >(M_PI - 0.4f)) {
+                                                       tgpi->end[1] = tgpi->origin[1];
                                                }
                                                else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) {
-                                                       tgpi->bottom[0] = tgpi->origin[0];
+                                                       tgpi->end[0] = tgpi->origin[0];
                                                }
                                                else {
                                                        gpencil_primitive_to_square(tgpi, x, y);
@@ -783,9 +1479,10 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
                                }
                                /* Center primitive if alt key */
                                if (event->alt) {
-                                       tgpi->top[0] = tgpi->origin[0] - (tgpi->bottom[0] - tgpi->origin[0]);
-                                       tgpi->top[1] = tgpi->origin[1] - (tgpi->bottom[1] - tgpi->origin[1]);
+                                       tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
+                                       tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
                                }
+                               gp_primitive_update_cps(tgpi);
                                /* update screen */
                                gpencil_primitive_update(C, op, tgpi);
                        }
@@ -793,7 +1490,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
                }
                default:
                {
-                       if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
+                       if (tgpi->flag != IN_CURVE_EDIT && (event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
                                float value;
 
                                /* Grab data from numeric input, and store this new value (the user see an int) */
@@ -816,6 +1513,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
                }
        }
 
+       copy_v2_v2(tgpi->mvalo, tgpi->mval);
        /* still running... */
        return OPERATOR_RUNNING_MODAL;
 }
@@ -834,6 +1532,7 @@ void GPENCIL_OT_primitive(wmOperatorType *ot)
                {GP_STROKE_LINE, "LINE", 0, "Line", ""},
                {GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""},
                {GP_STROKE_ARC, "ARC", 0, "Arc", ""},
+               {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""},
                {0, NULL, 0, NULL, NULL}
        };
 
@@ -854,11 +1553,11 @@ void GPENCIL_OT_primitive(wmOperatorType *ot)
        /* properties */
        PropertyRNA *prop;
 
-       RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
+       prop = RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
        RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape");
 
        prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
        RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
 }
-
-/* *************************************************************** */
index 01f0b06c178f51e1fddd04e911cd2d6c14193afe..bf205729a72ff81878dc3b30dfca5841f1272187 100644 (file)
@@ -103,6 +103,7 @@ typedef enum eCurveMappingPreset {
        CURVE_PRESET_ROUND  = 5,
        CURVE_PRESET_ROOT   = 6,
        CURVE_PRESET_GAUSS  = 7,
+       CURVE_PRESET_BELL   = 8,
 } eCurveMappingPreset;
 
 /* CurveMapping->tone */
index 43ce3649db35535a4bb35bd6835d6ae5faa93d95..934ef4a4829746adff75f21e70279851d0fbca5d 100644 (file)
@@ -48,6 +48,13 @@ struct MDeformVert;
 /* ***************************************** */
 /* GP Stroke Points */
 
+/* 'Control Point' data for primitives and curves */
+typedef struct bGPDcontrolpoint {
+       float x, y, z;          /* x and y coordinates of control point */
+       float color[4];         /* point color */
+       int size;               /* radius */
+} bGPDcontrolpoint;
+
 /* Grease-Pencil Annotations - 'Stroke Point'
  * -> Coordinates may either be 2d or 3d depending on settings at the time
  * -> Coordinates of point on stroke, in proportions of window size
@@ -345,6 +352,10 @@ typedef struct bGPdata_Runtime {
        short sbuffer_size;                     /* number of elements currently in cache */
        short sbuffer_sflag;            /* flags for stroke that cache represents */
        char pad_[6];
+
+       int tot_cp_points;                 /* number of control-points for stroke */
+       char pad1_[4];
+       bGPDcontrolpoint *cp_points;       /* array of control-points for stroke */
 } bGPdata_Runtime;
 
 /* grid configuration */
index c1853ce17452a3f5025024ea00ae1c8f3395ceb0..b06ab596059b1182c353034162f9d918cf237cc1 100644 (file)
@@ -1035,6 +1035,7 @@ typedef struct GP_Sculpt_Settings {
        int weighttype;               /* eGP_Sculpt_Types (weight paint) */
        char pad[4];
        struct CurveMapping *cur_falloff; /* multiframe edit falloff effect by frame */
+       struct CurveMapping *cur_primitive; /* Curve used for primitve tools */
 } GP_Sculpt_Settings;
 
 /* GP_Sculpt_Settings.flag */
@@ -1053,6 +1054,8 @@ typedef enum eGP_Sculpt_SettingsFlag {
        GP_SCULPT_SETT_FLAG_FRAME_FALLOFF = (1 << 5),
        /* apply brush to uv data */
        GP_SCULPT_SETT_FLAG_APPLY_UV = (1 << 6),
+       /* apply primitve curve */
+       GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE = (1 << 7),
 } eGP_Sculpt_SettingsFlag;
 
 /* Settings for GP Interpolation Operators */
index 8ee542fcc51e258a4534b6e61a21997f48fec29f..1086abd0c6fb5a3b5cc259d2521d61a19638dfbd 100644 (file)
@@ -69,11 +69,11 @@ const EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
        {GP_SCULPT_TYPE_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", "Smooth stroke points"},
        {GP_SCULPT_TYPE_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", "Adjust thickness of strokes"},
        {GP_SCULPT_TYPE_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", "Adjust color strength of strokes" },
+       {GP_SCULPT_TYPE_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", "Introduce jitter/randomness into strokes"},
        {GP_SCULPT_TYPE_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", "Translate the set of points initially within the brush circle" },
        {GP_SCULPT_TYPE_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", "Move points out of the way, as if combing them"},
        {GP_SCULPT_TYPE_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", "Rotate points around the midpoint of the brush"},
        {GP_SCULPT_TYPE_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", "Pull points towards the midpoint of the brush"},
-       {GP_SCULPT_TYPE_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", "Introduce jitter/randomness into strokes"},
        {GP_SCULPT_TYPE_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", "Paste copies of the strokes stored on the clipboard"},
        { 0, NULL, 0, NULL, NULL }
 };
@@ -1250,6 +1250,12 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
        RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
        RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
 
+       prop = RNA_def_property(srna, "use_thickness_curve", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE);
+       RNA_def_property_ui_text(prop, "Use Curve", "Use curve to define primitive stroke thickness");
+       RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+       RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
        /* custom falloff curve */
        prop = RNA_def_property(srna, "multiframe_falloff_curve", PROP_POINTER, PROP_NONE);
        RNA_def_property_pointer_sdna(prop, NULL, "cur_falloff");
@@ -1259,6 +1265,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
        RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
        RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
 
+       /* custom primitive curve */
+       prop = RNA_def_property(srna, "thickness_primitive_curve", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "cur_primitive");
+       RNA_def_property_struct_type(prop, "CurveMapping");
+       RNA_def_property_ui_text(prop, "Curve",
+               "Custom curve to control primitive thickness");
+       RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+       RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
        /* lock axis */
        prop = RNA_def_property(srna, "lock_axis", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "lock_axis");