Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFalk David <falkdavid@gmx.de>2020-11-13 23:43:00 +0300
committerFalk David <falkdavid@gmx.de>2020-11-13 23:43:00 +0300
commit0be88c7d15d2ad1af284c6283370173647ae74eb (patch)
tree5fff573c512e284547ebe0c921ecffdae2c377c4 /source/blender/blenkernel/intern
parent9d28353b525ecfbcca1501be72e4276dfb2bbc2a (diff)
GPencil: Merge GSoC curve edit mode
Differential Revision: https://developer.blender.org/D8660 This patch is the result of the GSoC 2020 "Editing Grease Pencil Strokes Using Curves" project. It adds a submode to greasepencil edit mode that allows for the transformation of greasepencil strokes using bezier curves. More information about the project can be found here: https://wiki.blender.org/wiki/User:Filedescriptor/GSoC_2020.
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r--source/blender/blenkernel/intern/gpencil.c112
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c874
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c84
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c8
4 files changed, 1042 insertions, 36 deletions
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 4cf8e365cf6..9d3582b2df2 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -155,6 +155,11 @@ static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id
BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points);
BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles);
BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert);
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ BLO_write_struct(writer, bGPDcurve, gpc);
+ BLO_write_struct_array(writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points);
+ }
}
}
}
@@ -222,6 +227,13 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
/* Relink geometry*/
BLO_read_data_address(reader, &gps->triangles);
+ /* relink stroke edit curve. */
+ BLO_read_data_address(reader, &gps->editcurve);
+ if (gps->editcurve != NULL) {
+ /* relink curve point array */
+ BLO_read_data_address(reader, &gps->editcurve->curve_points);
+ }
+
/* relink weight data */
if (gps->dvert) {
BLO_read_data_address(reader, &gps->dvert);
@@ -341,6 +353,20 @@ void BKE_gpencil_free_stroke_weights(bGPDstroke *gps)
}
}
+void BKE_gpencil_free_stroke_editcurve(bGPDstroke *gps)
+{
+ if (gps == NULL) {
+ return;
+ }
+ bGPDcurve *editcurve = gps->editcurve;
+ if (editcurve == NULL) {
+ return;
+ }
+ MEM_freeN(editcurve->curve_points);
+ MEM_freeN(editcurve);
+ gps->editcurve = NULL;
+}
+
/* free stroke, doesn't unlink from any listbase */
void BKE_gpencil_free_stroke(bGPDstroke *gps)
{
@@ -358,6 +384,9 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps)
if (gps->triangles) {
MEM_freeN(gps->triangles);
}
+ if (gps->editcurve != NULL) {
+ BKE_gpencil_free_stroke_editcurve(gps);
+ }
MEM_freeN(gps);
}
@@ -688,6 +717,13 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
+ gpd->curve_edit_resolution = GP_DEFAULT_CURVE_RESOLUTION;
+ gpd->curve_edit_threshold = GP_DEFAULT_CURVE_ERROR;
+ gpd->curve_edit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
+
+ /* use adaptive curve resolution by default */
+ gpd->flag |= GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
+
gpd->zdepth_offset = 0.150f;
/* grid settings */
@@ -776,6 +812,8 @@ bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
gps->mat_nr = mat_idx;
+ gps->editcurve = NULL;
+
return gps;
}
@@ -827,6 +865,16 @@ bGPDstroke *BKE_gpencil_stroke_add_existing_style(
return gps;
}
+bGPDcurve *BKE_gpencil_stroke_editcurve_new(const int tot_curve_points)
+{
+ bGPDcurve *new_gp_curve = (bGPDcurve *)MEM_callocN(sizeof(bGPDcurve), __func__);
+ new_gp_curve->tot_curve_points = tot_curve_points;
+ new_gp_curve->curve_points = (bGPDcurve_point *)MEM_callocN(
+ sizeof(bGPDcurve_point) * tot_curve_points, __func__);
+
+ return new_gp_curve;
+}
+
/* ************************************************** */
/* Data Duplication */
@@ -845,13 +893,28 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d
BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
}
+/* Make a copy of a given gpencil stroke editcurve */
+bGPDcurve *BKE_gpencil_stroke_curve_duplicate(bGPDcurve *gpc_src)
+{
+ bGPDcurve *gpc_dst = MEM_dupallocN(gpc_src);
+
+ if (gpc_src->curve_points != NULL) {
+ gpc_dst->curve_points = MEM_dupallocN(gpc_src->curve_points);
+ }
+
+ return gpc_dst;
+}
+
/**
* Make a copy of a given grease-pencil stroke.
* \param gps_src: Source grease pencil strokes.
* \param dup_points: Duplicate points data.
+ * \param dup_curve: Duplicate curve data.
* \return Pointer to new stroke.
*/
-bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_points)
+bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src,
+ const bool dup_points,
+ const bool dup_curve)
{
bGPDstroke *gps_dst = NULL;
@@ -871,6 +934,10 @@ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_poi
}
}
+ if (dup_curve && gps_src->editcurve != NULL) {
+ gps_dst->editcurve = BKE_gpencil_stroke_curve_duplicate(gps_src->editcurve);
+ }
+
/* return new stroke */
return gps_dst;
}
@@ -898,7 +965,7 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
BLI_listbase_clear(&gpf_dst->strokes);
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* make copy of source stroke */
- gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@@ -923,7 +990,7 @@ void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_ds
BLI_listbase_clear(&gpf_dst->strokes);
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* make copy of source stroke */
- gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
}
@@ -1037,6 +1104,39 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
}
}
+void BKE_gpencil_curve_sync_selection(bGPDstroke *gps)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL) {
+ return;
+ }
+
+ gps->flag &= ~GP_STROKE_SELECT;
+ gpc->flag &= ~GP_CURVE_SELECT;
+
+ bool is_selected = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (BEZT_ISSEL_ANY(bezt)) {
+ gpc_pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ if (gpc_pt->flag & GP_SPOINT_SELECT) {
+ is_selected = true;
+ }
+ }
+
+ if (is_selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ }
+}
+
/* ************************************************** */
/* GP Frame API */
@@ -2304,12 +2404,14 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
/**
* Create grease pencil strokes from image
* \param sima: Image
+ * \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param size: Size
* \param mask: Mask
* \return True if done
*/
-bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size, const bool mask)
+bool BKE_gpencil_from_image(
+ SpaceImage *sima, bGPdata *gpd, bGPDframe *gpf, const float size, const bool mask)
{
Image *image = sima->image;
bool done = false;
@@ -2357,7 +2459,7 @@ bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size,
pt->flag |= GP_SPOINT_SELECT;
}
}
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 6b3f752120a..59c251197eb 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -37,8 +37,10 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
+#include "DNA_meshdata_types.h"
#include "BKE_collection.h"
+#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
@@ -47,8 +49,16 @@
#include "BKE_material.h"
#include "BKE_object.h"
+#include "curve_fit_nd.h"
+
#include "DEG_depsgraph_query.h"
+#define COORD_FITTING_INFLUENCE 20.0f
+
+/* -------------------------------------------------------------------- */
+/** \name Convert to curve object
+ * \{ */
+
/* Helper: Check materials with same color. */
static int gpencil_check_same_material_color(Object *ob_gp,
const float color_stroke[4],
@@ -295,6 +305,7 @@ static void gpencil_convert_spline(Main *bmain,
bGPDframe *gpf,
Nurb *nu)
{
+ bGPdata *gpd = (bGPdata *)ob_gp->data;
bool cyclic = true;
/* Create Stroke. */
@@ -445,11 +456,22 @@ static void gpencil_convert_spline(Main *bmain,
}
if (sample > 0.0f) {
- BKE_gpencil_stroke_sample(gps, sample, false);
+ BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
/* Recalc fill geometry. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+}
+
+static void gpencil_editstroke_deselect_all(bGPDcurve *gpc)
+{
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ gpc->flag &= ~GP_CURVE_SELECT;
}
/**
@@ -536,3 +558,851 @@ void BKE_gpencil_convert_curve(Main *bmain,
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Editcurve kernel functions
+ * \{ */
+
+static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps,
+ const float stroke_radius)
+{
+ BLI_assert(gps->totpoints < 3);
+
+ if (gps->totpoints == 1) {
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(1);
+ bGPDspoint *pt = &gps->points[0];
+ bGPDcurve_point *cpt = &editcurve->curve_points[0];
+ BezTriple *bezt = &cpt->bezt;
+
+ /* Handles are twice as long as the radius of the point. */
+ float offset = (pt->pressure * stroke_radius) * 2.0f;
+
+ float tmp_vec[3];
+ for (int j = 0; j < 3; j++) {
+ copy_v3_v3(tmp_vec, &pt->x);
+ /* Move handles along the x-axis away from the control point */
+ tmp_vec[0] += (float)(j - 1) * offset;
+ copy_v3_v3(bezt->vec[j], tmp_vec);
+ }
+
+ cpt->pressure = pt->pressure;
+ cpt->strength = pt->strength;
+ copy_v4_v4(cpt->vert_color, pt->vert_color);
+
+ /* default handle type */
+ bezt->h1 = HD_FREE;
+ bezt->h2 = HD_FREE;
+
+ cpt->point_index = 0;
+
+ return editcurve;
+ }
+ if (gps->totpoints == 2) {
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(2);
+ bGPDspoint *first_pt = &gps->points[0];
+ bGPDspoint *last_pt = &gps->points[1];
+
+ float length = len_v3v3(&first_pt->x, &last_pt->x);
+ float offset = length / 3;
+ float dir[3];
+ sub_v3_v3v3(dir, &last_pt->x, &first_pt->x);
+
+ for (int i = 0; i < 2; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ bGPDcurve_point *cpt = &editcurve->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+
+ float tmp_vec[3];
+ for (int j = 0; j < 3; j++) {
+ copy_v3_v3(tmp_vec, dir);
+ normalize_v3_length(tmp_vec, (float)(j - 1) * offset);
+ add_v3_v3v3(bezt->vec[j], &pt->x, tmp_vec);
+ }
+
+ cpt->pressure = pt->pressure;
+ cpt->strength = pt->strength;
+ copy_v4_v4(cpt->vert_color, pt->vert_color);
+
+ /* default handle type */
+ bezt->h1 = HD_VECT;
+ bezt->h2 = HD_VECT;
+
+ cpt->point_index = 0;
+ }
+
+ return editcurve;
+ }
+
+ return NULL;
+}
+
+/**
+ * Creates a bGPDcurve by doing a cubic curve fitting on the grease pencil stroke points.
+ */
+bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
+ const float error_threshold,
+ const float corner_angle,
+ const float stroke_radius)
+{
+ if (gps->totpoints < 3) {
+ return gpencil_stroke_editcurve_generate_edgecases(gps, stroke_radius);
+ }
+#define POINT_DIM 9
+
+ float *points = MEM_callocN(sizeof(float) * gps->totpoints * POINT_DIM, __func__);
+ float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+ float tmp_vec[3];
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ int row = i * POINT_DIM;
+
+ /* normalize coordinate to 0..1 */
+ sub_v3_v3v3(tmp_vec, &pt->x, gps->boundbox_min);
+ mul_v3_v3fl(&points[row], tmp_vec, COORD_FITTING_INFLUENCE / diag_length);
+ points[row + 3] = pt->pressure / diag_length;
+
+ /* strength and color are already normalized */
+ points[row + 4] = pt->strength / diag_length;
+ mul_v4_v4fl(&points[row + 5], pt->vert_color, 1.0f / diag_length);
+ }
+
+ uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
+ if (gps->totpoints > 2 && gps->flag & GP_STROKE_CYCLIC) {
+ calc_flag |= CURVE_FIT_CALC_CYCLIC;
+ }
+
+ float *r_cubic_array = NULL;
+ unsigned int r_cubic_array_len = 0;
+ unsigned int *r_cubic_orig_index = NULL;
+ unsigned int *r_corners_index_array = NULL;
+ unsigned int r_corners_index_len = 0;
+ int r = curve_fit_cubic_to_points_refit_fl(points,
+ gps->totpoints,
+ POINT_DIM,
+ error_threshold,
+ calc_flag,
+ NULL,
+ 0,
+ corner_angle,
+ &r_cubic_array,
+ &r_cubic_array_len,
+ &r_cubic_orig_index,
+ &r_corners_index_array,
+ &r_corners_index_len);
+
+ if (r != 0 || r_cubic_array_len < 1) {
+ return NULL;
+ }
+
+ uint curve_point_size = 3 * POINT_DIM;
+
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(r_cubic_array_len);
+
+ for (int i = 0; i < r_cubic_array_len; i++) {
+ bGPDcurve_point *cpt = &editcurve->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ float *curve_point = &r_cubic_array[i * curve_point_size];
+
+ for (int j = 0; j < 3; j++) {
+ float *bez = &curve_point[j * POINT_DIM];
+ madd_v3_v3v3fl(bezt->vec[j], gps->boundbox_min, bez, diag_length / COORD_FITTING_INFLUENCE);
+ }
+
+ float *ctrl_point = &curve_point[1 * POINT_DIM];
+ cpt->pressure = ctrl_point[3] * diag_length;
+ cpt->strength = ctrl_point[4] * diag_length;
+ mul_v4_v4fl(cpt->vert_color, &ctrl_point[5], diag_length);
+
+ /* default handle type */
+ bezt->h1 = HD_ALIGN;
+ bezt->h2 = HD_ALIGN;
+
+ cpt->point_index = r_cubic_orig_index[i];
+ }
+
+ if (r_corners_index_len > 0 && r_corners_index_array != NULL) {
+ int start = 0, end = r_corners_index_len;
+ if ((r_corners_index_len > 1) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
+ start = 1;
+ end = r_corners_index_len - 1;
+ }
+ for (int i = start; i < end; i++) {
+ bGPDcurve_point *cpt = &editcurve->curve_points[r_corners_index_array[i]];
+ BezTriple *bezt = &cpt->bezt;
+ bezt->h1 = HD_FREE;
+ bezt->h2 = HD_FREE;
+ }
+ }
+
+ MEM_freeN(points);
+ if (r_cubic_array) {
+ free(r_cubic_array);
+ }
+ if (r_corners_index_array) {
+ free(r_corners_index_array);
+ }
+ if (r_cubic_orig_index) {
+ free(r_cubic_orig_index);
+ }
+
+#undef POINT_DIM
+ return editcurve;
+}
+
+/**
+ * Updates the editcurve for a stroke. Frees the old curve if one exists and generates a new one.
+ */
+void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps)
+{
+ if (gps == NULL || gps->totpoints < 0) {
+ return;
+ }
+
+ if (gps->editcurve != NULL) {
+ BKE_gpencil_free_stroke_editcurve(gps);
+ }
+
+ float defaultpixsize = 1000.0f / gpd->pixfactor;
+ float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
+
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate(
+ gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle, stroke_radius);
+ if (editcurve == NULL) {
+ return;
+ }
+
+ gps->editcurve = editcurve;
+}
+
+/**
+ * Sync the selection from stroke to editcurve
+ */
+void BKE_gpencil_editcurve_stroke_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
+{
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpc->flag |= GP_CURVE_SELECT;
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ bGPDspoint *pt = &gps->points[gpc_pt->point_index];
+ if (pt->flag & GP_SPOINT_SELECT) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+ }
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ gpencil_editstroke_deselect_all(gpc);
+ }
+}
+
+/**
+ * Sync the selection from editcurve to stroke
+ */
+void BKE_gpencil_stroke_editcurve_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
+{
+ if (gpc->flag & GP_CURVE_SELECT) {
+ gps->flag |= GP_STROKE_SELECT;
+
+ for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ bGPDspoint *pt = &gps->points[gpc_pt->point_index];
+ bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1];
+
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ pt->flag |= GP_SPOINT_SELECT;
+ if (gpc_pt_next->flag & GP_CURVE_POINT_SELECT) {
+ /* select all the points after */
+ for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
+ bGPDspoint *pt_next = &gps->points[j];
+ pt_next->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ }
+ else {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ /* deselect all points after */
+ for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
+ bGPDspoint *pt_next = &gps->points[j];
+ pt_next->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+ }
+
+ bGPDcurve_point *gpc_first = &gpc->curve_points[0];
+ bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ bGPDspoint *last_pt = &gps->points[gpc_last->point_index];
+ if (gpc_last->flag & GP_CURVE_POINT_SELECT) {
+ last_pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ last_pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ if (gpc_first->flag & GP_CURVE_POINT_SELECT && gpc_last->flag & GP_CURVE_POINT_SELECT) {
+ for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
+ bGPDspoint *pt_next = &gps->points[i];
+ pt_next->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ else {
+ for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
+ bGPDspoint *pt_next = &gps->points[i];
+ pt_next->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+ }
+ }
+ else {
+ gps->flag &= ~GP_STROKE_SELECT;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+}
+
+static void gpencil_interpolate_fl_from_to(
+ float from, float to, float *point_offset, int it, int stride)
+{
+ /* smooth interpolation */
+ float *r = point_offset;
+ for (int i = 0; i <= it; i++) {
+ float fac = (float)i / (float)it;
+ fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
+ *r = interpf(to, from, fac);
+ r = POINTER_OFFSET(r, stride);
+ }
+}
+
+static void gpencil_interpolate_v4_from_to(
+ float from[4], float to[4], float *point_offset, int it, int stride)
+{
+ /* smooth interpolation */
+ float *r = point_offset;
+ for (int i = 0; i <= it; i++) {
+ float fac = (float)i / (float)it;
+ fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
+ interp_v4_v4v4(r, from, to, fac);
+ r = POINTER_OFFSET(r, stride);
+ }
+}
+
+static float gpencil_approximate_curve_segment_arclength(bGPDcurve_point *cpt_start,
+ bGPDcurve_point *cpt_end)
+{
+ BezTriple *bezt_start = &cpt_start->bezt;
+ BezTriple *bezt_end = &cpt_end->bezt;
+
+ float chord_len = len_v3v3(bezt_start->vec[1], bezt_end->vec[1]);
+ float net_len = len_v3v3(bezt_start->vec[1], bezt_start->vec[2]);
+ net_len += len_v3v3(bezt_start->vec[2], bezt_end->vec[0]);
+ net_len += len_v3v3(bezt_end->vec[0], bezt_end->vec[1]);
+
+ return (chord_len + net_len) / 2.0f;
+}
+
+static void gpencil_calculate_stroke_points_curve_segment(
+ bGPDcurve_point *cpt, bGPDcurve_point *cpt_next, float *points_offset, int resolu, int stride)
+{
+ /* sample points on all 3 axis between two curve points */
+ for (uint axis = 0; axis < 3; axis++) {
+ BKE_curve_forward_diff_bezier(cpt->bezt.vec[1][axis],
+ cpt->bezt.vec[2][axis],
+ cpt_next->bezt.vec[0][axis],
+ cpt_next->bezt.vec[1][axis],
+ POINTER_OFFSET(points_offset, sizeof(float) * axis),
+ (int)resolu,
+ stride);
+ }
+
+ /* interpolate other attributes */
+ gpencil_interpolate_fl_from_to(cpt->pressure,
+ cpt_next->pressure,
+ POINTER_OFFSET(points_offset, sizeof(float) * 3),
+ resolu,
+ stride);
+ gpencil_interpolate_fl_from_to(cpt->strength,
+ cpt_next->strength,
+ POINTER_OFFSET(points_offset, sizeof(float) * 4),
+ resolu,
+ stride);
+ gpencil_interpolate_v4_from_to(cpt->vert_color,
+ cpt_next->vert_color,
+ POINTER_OFFSET(points_offset, sizeof(float) * 5),
+ resolu,
+ stride);
+}
+
+static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
+ bGPDcurve_point *curve_point_array,
+ int curve_point_array_len,
+ int resolution,
+ bool is_cyclic,
+ int *r_points_len)
+{
+ /* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
+ const uint stride = sizeof(float[9]);
+ const uint cpt_last = curve_point_array_len - 1;
+ const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
+ int *segment_point_lengths = MEM_callocN(sizeof(int) * num_segments, __func__);
+
+ uint points_len = 1;
+ for (int i = 0; i < cpt_last; i++) {
+ bGPDcurve_point *cpt = &curve_point_array[i];
+ bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
+ float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
+ int segment_resolu = (int)floorf(arclen * resolution);
+ CLAMP_MIN(segment_resolu, 1);
+
+ segment_point_lengths[i] = segment_resolu;
+ points_len += segment_resolu;
+ }
+
+ if (is_cyclic) {
+ bGPDcurve_point *cpt = &curve_point_array[cpt_last];
+ bGPDcurve_point *cpt_next = &curve_point_array[0];
+ float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
+ int segment_resolu = (int)floorf(arclen * resolution);
+ CLAMP_MIN(segment_resolu, 1);
+
+ segment_point_lengths[cpt_last] = segment_resolu;
+ points_len += segment_resolu;
+ }
+
+ float(*r_points)[9] = MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__);
+ float *points_offset = &r_points[0][0];
+ int point_index = 0;
+ for (int i = 0; i < cpt_last; i++) {
+ bGPDcurve_point *cpt_curr = &curve_point_array[i];
+ bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
+ int segment_resolu = segment_point_lengths[i];
+ gpencil_calculate_stroke_points_curve_segment(
+ cpt_curr, cpt_next, points_offset, segment_resolu, stride);
+ /* update the index */
+ cpt_curr->point_index = point_index;
+ point_index += segment_resolu;
+ points_offset = POINTER_OFFSET(points_offset, segment_resolu * stride);
+ }
+
+ bGPDcurve_point *cpt_curr = &curve_point_array[cpt_last];
+ cpt_curr->point_index = point_index;
+ if (is_cyclic) {
+ bGPDcurve_point *cpt_next = &curve_point_array[0];
+ int segment_resolu = segment_point_lengths[cpt_last];
+ gpencil_calculate_stroke_points_curve_segment(
+ cpt_curr, cpt_next, points_offset, segment_resolu, stride);
+ }
+
+ MEM_freeN(segment_point_lengths);
+
+ *r_points_len = points_len;
+ return (float(*))r_points;
+}
+
+/**
+ * Helper: calculate the points on a curve with a fixed resolution.
+ */
+static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point *curve_point_array,
+ int curve_point_array_len,
+ int resolution,
+ bool is_cyclic,
+ int *r_points_len)
+{
+ /* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
+ const uint stride = sizeof(float[9]);
+ const uint array_last = curve_point_array_len - 1;
+ const uint resolu_stride = resolution * stride;
+ const uint points_len = BKE_curve_calc_coords_axis_len(
+ curve_point_array_len, resolution, is_cyclic, false);
+
+ float(*r_points)[9] = MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__);
+ float *points_offset = &r_points[0][0];
+ for (unsigned int i = 0; i < array_last; i++) {
+ bGPDcurve_point *cpt_curr = &curve_point_array[i];
+ bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
+
+ gpencil_calculate_stroke_points_curve_segment(
+ cpt_curr, cpt_next, points_offset, resolution, stride);
+ /* update the index */
+ cpt_curr->point_index = i * resolution;
+ points_offset = POINTER_OFFSET(points_offset, resolu_stride);
+ }
+
+ bGPDcurve_point *cpt_curr = &curve_point_array[array_last];
+ cpt_curr->point_index = array_last * resolution;
+ if (is_cyclic) {
+ bGPDcurve_point *cpt_next = &curve_point_array[0];
+ gpencil_calculate_stroke_points_curve_segment(
+ cpt_curr, cpt_next, points_offset, resolution, stride);
+ }
+
+ *r_points_len = points_len;
+ return (float(*))r_points;
+}
+
+/**
+ * Recalculate stroke points with the editcurve of the stroke.
+ */
+void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
+ const uint resolution,
+ const bool adaptive)
+{
+ if (gps == NULL || gps->editcurve == NULL) {
+ return;
+ }
+
+ bGPDcurve *editcurve = gps->editcurve;
+ bGPDcurve_point *curve_point_array = editcurve->curve_points;
+ int curve_point_array_len = editcurve->tot_curve_points;
+ if (curve_point_array_len == 0) {
+ return;
+ }
+ /* Handle case for single curve point. */
+ if (curve_point_array_len == 1) {
+ bGPDcurve_point *cpt = &curve_point_array[0];
+ /* resize stroke point array */
+ gps->totpoints = 1;
+ gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
+ }
+
+ bGPDspoint *pt = &gps->points[0];
+ copy_v3_v3(&pt->x, cpt->bezt.vec[1]);
+
+ pt->pressure = cpt->pressure;
+ pt->strength = cpt->strength;
+
+ copy_v4_v4(pt->vert_color, cpt->vert_color);
+
+ /* deselect */
+ pt->flag &= ~GP_SPOINT_SELECT;
+ gps->flag &= ~GP_STROKE_SELECT;
+
+ return;
+ }
+
+ bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+
+ int points_len = 0;
+ float(*points)[9] = NULL;
+ if (adaptive) {
+ points = (float(*)[9])gpencil_stroke_points_from_editcurve_adaptive_resolu(
+ curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
+ }
+ else {
+ points = (float(*)[9])gpencil_stroke_points_from_editcurve_fixed_resolu(
+ curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
+ }
+
+ if (points == NULL || points_len == 0) {
+ return;
+ }
+
+ /* resize stroke point array */
+ gps->totpoints = points_len;
+ gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
+ }
+
+ /* write new data to stroke point array */
+ for (int i = 0; i < points_len; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ copy_v3_v3(&pt->x, &points[i][0]);
+
+ pt->pressure = points[i][3];
+ pt->strength = points[i][4];
+
+ copy_v4_v4(pt->vert_color, &points[i][5]);
+
+ /* deselect points */
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+
+ /* free temp data */
+ MEM_freeN(points);
+}
+
+/**
+ * Recalculate the handles of the edit curve of a grease pencil stroke
+ */
+void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
+{
+ if (gps == NULL || gps->editcurve == NULL) {
+ return;
+ }
+
+ bool changed = false;
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->tot_curve_points < 2) {
+ return;
+ }
+
+ if (gpc->tot_curve_points == 1) {
+ BKE_nurb_handle_calc(
+ &(gpc->curve_points[0].bezt), NULL, &(gpc->curve_points[0].bezt), false, 0);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ }
+
+ for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ bGPDcurve_point *gpc_pt_prev = &gpc->curve_points[i - 1];
+ bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1];
+ /* update handle if point or neighbour is selected */
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT || gpc_pt_prev->flag & GP_CURVE_POINT_SELECT ||
+ gpc_pt_next->flag & GP_CURVE_POINT_SELECT) {
+ BezTriple *bezt = &gpc_pt->bezt;
+ BezTriple *bezt_prev = &gpc_pt_prev->bezt;
+ BezTriple *bezt_next = &gpc_pt_next->bezt;
+
+ BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, false, 0);
+ changed = true;
+ }
+ }
+
+ bGPDcurve_point *gpc_first = &gpc->curve_points[0];
+ bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ bGPDcurve_point *gpc_first_next = &gpc->curve_points[1];
+ bGPDcurve_point *gpc_last_prev = &gpc->curve_points[gpc->tot_curve_points - 2];
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
+ BezTriple *bezt_first = &gpc_first->bezt;
+ BezTriple *bezt_last = &gpc_last->bezt;
+ BezTriple *bezt_first_next = &gpc_first_next->bezt;
+ BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
+
+ BKE_nurb_handle_calc(bezt_first, bezt_last, bezt_first_next, false, 0);
+ BKE_nurb_handle_calc(bezt_last, bezt_last_prev, bezt_first, false, 0);
+ changed = true;
+ }
+ }
+ else {
+ if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
+ BezTriple *bezt_first = &gpc_first->bezt;
+ BezTriple *bezt_last = &gpc_last->bezt;
+ BezTriple *bezt_first_next = &gpc_first_next->bezt;
+ BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
+
+ BKE_nurb_handle_calc(bezt_first, NULL, bezt_first_next, false, 0);
+ BKE_nurb_handle_calc(bezt_last, bezt_last_prev, NULL, false, 0);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ }
+}
+
+/* Helper: count how many new curve points must be generated. */
+static int gpencil_editcurve_subdivide_count(bGPDcurve *gpc, bool is_cyclic)
+{
+ int count = 0;
+ for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *cpt_next = &gpc->curve_points[i + 1];
+
+ if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
+ count++;
+ }
+ }
+
+ if (is_cyclic) {
+ bGPDcurve_point *cpt = &gpc->curve_points[0];
+ bGPDcurve_point *cpt_next = &gpc->curve_points[gpc->tot_curve_points - 1];
+
+ if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void gpencil_editcurve_subdivide_curve_segment(bGPDcurve_point *cpt_start,
+ bGPDcurve_point *cpt_end,
+ bGPDcurve_point *cpt_new)
+{
+ BezTriple *bezt_start = &cpt_start->bezt;
+ BezTriple *bezt_end = &cpt_end->bezt;
+ BezTriple *bezt_new = &cpt_new->bezt;
+ for (int axis = 0; axis < 3; axis++) {
+ float p0, p1, p2, p3, m0, m1, q0, q1, b;
+ p0 = bezt_start->vec[1][axis];
+ p1 = bezt_start->vec[2][axis];
+ p2 = bezt_end->vec[0][axis];
+ p3 = bezt_end->vec[1][axis];
+
+ m0 = (p0 + p1) / 2;
+ q0 = (p0 + 2 * p1 + p2) / 4;
+ b = (p0 + 3 * p1 + 3 * p2 + p3) / 8;
+ q1 = (p1 + 2 * p2 + p3) / 4;
+ m1 = (p2 + p3) / 2;
+
+ bezt_new->vec[0][axis] = q0;
+ bezt_new->vec[2][axis] = q1;
+ bezt_new->vec[1][axis] = b;
+
+ bezt_start->vec[2][axis] = m0;
+ bezt_end->vec[0][axis] = m1;
+ }
+
+ cpt_new->pressure = interpf(cpt_end->pressure, cpt_start->pressure, 0.5f);
+ cpt_new->strength = interpf(cpt_end->strength, cpt_start->strength, 0.5f);
+ interp_v4_v4v4(cpt_new->vert_color, cpt_start->vert_color, cpt_end->vert_color, 0.5f);
+}
+
+void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 2) {
+ return;
+ }
+ bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+
+ /* repeat for number of cuts */
+ for (int s = 0; s < cuts; s++) {
+ int old_tot_curve_points = gpc->tot_curve_points;
+ int new_num_curve_points = gpencil_editcurve_subdivide_count(gpc, is_cyclic);
+ if (new_num_curve_points == 0) {
+ break;
+ }
+ int new_tot_curve_points = old_tot_curve_points + new_num_curve_points;
+
+ bGPDcurve_point *temp_curve_points = (bGPDcurve_point *)MEM_callocN(
+ sizeof(bGPDcurve_point) * new_tot_curve_points, __func__);
+
+ bool prev_subdivided = false;
+ int j = 0;
+ for (int i = 0; i < old_tot_curve_points - 1; i++, j++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *cpt_next = &gpc->curve_points[i + 1];
+
+ if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
+ bGPDcurve_point *cpt_new = &temp_curve_points[j + 1];
+ gpencil_editcurve_subdivide_curve_segment(cpt, cpt_next, cpt_new);
+
+ memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
+ memcpy(&temp_curve_points[j + 2], cpt_next, sizeof(bGPDcurve_point));
+
+ cpt_new->flag |= GP_CURVE_POINT_SELECT;
+ cpt_new->bezt.h1 = HD_ALIGN;
+ cpt_new->bezt.h2 = HD_ALIGN;
+ BEZT_SEL_ALL(&cpt_new->bezt);
+
+ prev_subdivided = true;
+ j++;
+ }
+ else if (!prev_subdivided) {
+ memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
+ prev_subdivided = false;
+ }
+ else {
+ prev_subdivided = false;
+ }
+ }
+
+ if (is_cyclic) {
+ bGPDcurve_point *cpt = &gpc->curve_points[old_tot_curve_points - 1];
+ bGPDcurve_point *cpt_next = &gpc->curve_points[0];
+
+ if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
+ bGPDcurve_point *cpt_new = &temp_curve_points[j + 1];
+ gpencil_editcurve_subdivide_curve_segment(cpt, cpt_next, cpt_new);
+
+ memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
+ memcpy(&temp_curve_points[0], cpt_next, sizeof(bGPDcurve_point));
+
+ cpt_new->flag |= GP_CURVE_POINT_SELECT;
+ cpt_new->bezt.h1 = HD_ALIGN;
+ cpt_new->bezt.h2 = HD_ALIGN;
+ BEZT_SEL_ALL(&cpt_new->bezt);
+ }
+ else if (!prev_subdivided) {
+ memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
+ }
+ }
+ else {
+ bGPDcurve_point *cpt = &gpc->curve_points[old_tot_curve_points - 1];
+ memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
+ }
+
+ MEM_freeN(gpc->curve_points);
+ gpc->curve_points = temp_curve_points;
+ gpc->tot_curve_points = new_tot_curve_points;
+ }
+}
+
+void BKE_gpencil_strokes_selected_update_editcurve(bGPdata *gpd)
+{
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ /* For all selected strokes, update edit curve. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (!BKE_gpencil_layer_is_editable(gpl)) {
+ continue;
+ }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip deselected stroke */
+ if (!(gps->flag & GP_STROKE_SELECT)) {
+ continue;
+ }
+
+ /* Generate the curve if there is none or the stroke was changed */
+ if (gps->editcurve == NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Continue if curve could not be generated. */
+ if (gps->editcurve == NULL) {
+ continue;
+ }
+ }
+ else if (gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ }
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ }
+ }
+}
+
+void BKE_gpencil_strokes_selected_sync_selection_editcurve(bGPdata *gpd)
+{
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ /* Sync selection for all strokes with editcurve. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (!BKE_gpencil_layer_is_editable(gpl)) {
+ continue;
+ }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc != NULL) {
+ /* Update the selection of every stroke that has an editcurve */
+ BKE_gpencil_stroke_editcurve_sync_selection(gps, gpc);
+ }
+ }
+ }
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index ff7dde27db8..d2cfb36cb15 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -45,8 +45,11 @@
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
+#include "BLT_translation.h"
+
#include "BKE_deform.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -415,10 +418,11 @@ static void stroke_interpolate_deform_weights(
/**
* Resample a stroke
+ * \param gpd: Grease pencil data-block
* \param gps: Stroke to sample
* \param dist: Distance of one segment
*/
-bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select)
+bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
{
bGPDspoint *pt = gps->points;
bGPDspoint *pt1 = NULL;
@@ -515,7 +519,7 @@ bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool sel
gps->totpoints = i;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
@@ -628,13 +632,15 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
/**
* Split stroke.
+ * \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param gps: Grease pencil original stroke
* \param before_index: Position of the point to split
* \param remaining_gps: Secondary stroke after split.
* \return True if the split was done
*/
-bool BKE_gpencil_stroke_split(bGPDframe *gpf,
+bool BKE_gpencil_stroke_split(bGPdata *gpd,
+ bGPDframe *gpf,
bGPDstroke *gps,
const int before_index,
bGPDstroke **remaining_gps)
@@ -684,7 +690,7 @@ bool BKE_gpencil_stroke_split(bGPDframe *gpf,
* Keep the end point. */
BKE_gpencil_stroke_trim_points(gps, 0, old_count);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
@@ -1273,14 +1279,31 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
/**
* Recalc all internal geometry data for the stroke
+ * \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
-void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
+void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
{
if (gps == NULL) {
return;
}
+ if (gps->editcurve != NULL) {
+ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ /* curve geometry was updated: stroke needs recalculation */
+ if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
+ bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, gpd->curve_edit_resolution, is_adaptive);
+ gps->flag &= ~GP_STROKE_NEEDS_CURVE_UPDATE;
+ }
+ }
+ else {
+ /* stroke geometry was updated: editcurve needs recalculation */
+ gps->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
+ }
+ }
+
if (gps->totpoints > 2) {
BKE_gpencil_stroke_fill_triangulate(gps);
}
@@ -1326,7 +1349,7 @@ float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
* Trim stroke to the first intersection or loop.
* \param gps: Stroke data
*/
-bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
+bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 4) {
return false;
@@ -1413,7 +1436,7 @@ bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
MEM_SAFE_FREE(old_dvert);
}
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
return intersect;
}
@@ -1509,11 +1532,12 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
/**
* Dissolve points in stroke.
+ * \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param gps: Grease pencil stroke
* \param tag: Type of tag for point
*/
-void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
+void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
{
bGPDspoint *pt;
MDeformVert *dvert = NULL;
@@ -1589,7 +1613,7 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta
gps->totpoints = tot;
/* triangles cache needs to be recalculated */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
@@ -1635,10 +1659,11 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
*
* Ramer - Douglas - Peucker algorithm
* by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
+ * \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
* \param epsilon: Epsilon value to define precision of the algorithm
*/
-void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
+void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon)
{
bGPDspoint *old_points = MEM_dupallocN(gps->points);
int totpoints = gps->totpoints;
@@ -1735,7 +1760,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
gps->totpoints = j;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
@@ -1744,9 +1769,10 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
/**
* Simplify alternate vertex of stroke except extremes.
+ * \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
-void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
+void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 5) {
return;
@@ -1800,19 +1826,20 @@ void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
gps->totpoints = j;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
}
/**
- * Subdivide grease pencil stroke.
- * \param gps: Grease pencil stroke
+ * Subdivide a stroke
+ * \param gpd: Grease pencil data-block
+ * \param gps: Stroke
* \param level: Level of subdivision
* \param type: Type of subdivision
*/
-void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
+void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
{
bGPDspoint *temp_points;
MDeformVert *temp_dverts = NULL;
@@ -1921,7 +1948,7 @@ void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* Merge by distance ------------------------------------- */
@@ -1930,12 +1957,14 @@ void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
* Reduce a series of points when the distance is below a threshold.
* Special case for first and last points (both are keeped) for other points,
* the merge point always is at first point.
+ * \param gpd: Grease pencil data-block
* \param gpf: Grease Pencil frame
* \param gps: Grease Pencil stroke
* \param threshold: Distance between points
* \param use_unselected: Set to true to analyze all stroke and not only selected points
*/
-void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
+void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
+ bGPDframe *gpf,
bGPDstroke *gps,
const float threshold,
const bool use_unselected)
@@ -2000,11 +2029,11 @@ void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
/* Dissolve tagged points */
if (tagged) {
- BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
+ BKE_gpencil_dissolve_points(gpd, gpf, gps, GP_SPOINT_TAG);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
typedef struct GpEdge {
@@ -2093,6 +2122,7 @@ static int gpencil_walk_edge(GHash *v_table,
}
static void gpencil_generate_edgeloops(Object *ob,
+ bGPdata *gpd,
bGPDframe *gpf_stroke,
int stroke_mat_index,
const float angle,
@@ -2218,7 +2248,7 @@ static void gpencil_generate_edgeloops(Object *ob,
pt->strength = 1.0f;
}
- BKE_gpencil_stroke_geometry_update(gps_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
}
/* Free memory. */
@@ -2397,10 +2427,10 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
}
/* If has only 3 points subdivide. */
if (mp->totloop == 3) {
- BKE_gpencil_stroke_subdivide(gps_fill, 1, GP_SUBDIV_SIMPLE);
+ BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
- BKE_gpencil_stroke_geometry_update(gps_fill);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
}
}
@@ -2417,7 +2447,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
gpencil_generate_edgeloops(
- ob_eval, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
+ ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -2457,7 +2487,7 @@ void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
@@ -2549,7 +2579,7 @@ void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
@@ -2586,7 +2616,7 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index be06638ab64..09f9e9e891c 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -698,7 +698,9 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval);
- const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_eval);
+ const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
+ (ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if ((!do_modifiers) && (!do_parent)) {
return;
@@ -734,8 +736,10 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
- const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
+ const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
+ (ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if (!do_modifiers) {
return;