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 72b3e16..8c386af 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 09e8111..32c0aa6 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 4da5455..aa6adee 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 f73d21d..a94ebea 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 aad59e3..bc34b24 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 a070bf9..3894d83 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 6aff6f6..6dd9c80 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 56ca663..5a10027 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 4dd046d..f3ae142 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 0854af0..c1d09a8 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 c75445f..a098fb7 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 87772a7..3b1c51c 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 66d9bac..dc62ca3 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 3c0675c..6f2b401 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 b44c910..c16ea84 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 2a92185..e2cbeda 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 68d0f65..6d8f337 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 029db23..0e4f955 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 01f0b06..bf20572 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 43ce364..934ef4a 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 c1853ce..b06ab59 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 8ee542f..1086abd 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");