GPencil: Cleanup - Split BKE_gpencil.h geometry functions into BKE_gpencil_geom.h
authorAntonio Vazquez <blendergit@gmail.com>
Thu, 19 Mar 2020 10:35:17 +0000 (11:35 +0100)
committerAntonio Vazquez <blendergit@gmail.com>
Thu, 19 Mar 2020 10:38:22 +0000 (11:38 +0100)
This split prepare the code for future geometry functions.

37 files changed:
source/blender/blenkernel/BKE_gpencil.h
source/blender/blenkernel/BKE_gpencil_geom.h [new file with mode: 0644]
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/gpencil.c
source/blender/blenkernel/intern/gpencil_geom.c [new file with mode: 0644]
source/blender/blenkernel/intern/gpencil_modifier.c
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/versioning_280.c
source/blender/draw/intern/draw_cache_impl_gpencil.c
source/blender/editors/gpencil/gpencil_add_monkey.c
source/blender/editors/gpencil/gpencil_add_stroke.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/gpencil/gpencil_fill.c
source/blender/editors/gpencil/gpencil_interpolate.c
source/blender/editors/gpencil/gpencil_merge.c
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/gpencil/gpencil_primitive.c
source/blender/editors/gpencil/gpencil_sculpt_paint.c
source/blender/editors/gpencil/gpencil_utils.c
source/blender/editors/gpencil/gpencil_uv.c
source/blender/editors/object/object_add.c
source/blender/editors/object/object_transform.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/transform/transform_generics.c
source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
source/blender/makesrna/intern/rna_gpencil.c
source/blender/makesrna/intern/rna_object_api.c

index 5d23029df9ea37158cd5d527acb26281ddcdd08c..8cd3081389e9a5fd11b971003ade55fe26538f7e 100644 (file)
@@ -28,7 +28,6 @@
 extern "C" {
 #endif
 
-struct BoundBox;
 struct Brush;
 struct CurveMapping;
 struct Depsgraph;
@@ -226,18 +225,8 @@ struct Material *BKE_gpencil_object_material_ensure_from_active_input_brush(stru
                                                                             struct Brush *brush);
 struct Material *BKE_gpencil_object_material_ensure_from_active_input_material(struct Object *ob);
 
-/* object boundbox */
-bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]);
-bool BKE_gpencil_stroke_minmax(const struct bGPDstroke *gps,
-                               const bool use_select,
-                               float r_min[3],
-                               float r_max[3]);
 bool BKE_gpencil_stroke_select_check(const struct bGPDstroke *gps);
 
-struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob);
-void BKE_gpencil_centroid_3d(struct bGPdata *gpd, float r_centroid[3]);
-void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps);
-
 /* vertex groups */
 void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps);
 void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup);
@@ -246,66 +235,10 @@ void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGP
 /* Set active frame by layer. */
 void BKE_gpencil_frame_active_set(struct Depsgraph *depsgraph, struct bGPdata *gpd);
 
-/* stroke geometry utilities */
-void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]);
-void BKE_gpencil_stroke_simplify_adaptive(struct bGPDstroke *gps, float factor);
-void BKE_gpencil_stroke_simplify_fixed(struct bGPDstroke *gps);
-void BKE_gpencil_stroke_subdivide(struct bGPDstroke *gps, int level, int type);
-bool BKE_gpencil_stroke_trim(struct bGPDstroke *gps);
-void BKE_gpencil_stroke_merge_distance(struct bGPDframe *gpf,
-                                       struct bGPDstroke *gps,
-                                       const float threshold,
-                                       const bool use_unselected);
-
-void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
-                                int totpoints,
-                                float (*points2d)[2],
-                                int *r_direction);
-void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
-                                    int ref_totpoints,
-                                    const struct bGPDspoint *points,
-                                    int totpoints,
-                                    float (*points2d)[2],
-                                    const float scale,
-                                    int *r_direction);
-void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps);
-void BKE_gpencil_stroke_geometry_update(struct bGPDstroke *gps);
-void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
-
-void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
-
-bool BKE_gpencil_stroke_sample(struct bGPDstroke *gps, const float dist, const bool select);
-bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf);
-bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
-bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence);
-bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence);
-bool BKE_gpencil_stroke_close(struct bGPDstroke *gps);
-void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag);
-
-bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
-bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
-                                    const int index_from,
-                                    const int index_to);
-bool BKE_gpencil_stroke_split(struct bGPDframe *gpf,
-                              struct bGPDstroke *gps,
-                              const int before_index,
-                              struct bGPDstroke **remaining_gps);
-bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
-
-float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
-
 void BKE_gpencil_frame_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe);
 float BKE_gpencil_multiframe_falloff_calc(
     struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff);
 
-void BKE_gpencil_convert_curve(struct Main *bmain,
-                               struct Scene *scene,
-                               struct Object *ob_gp,
-                               struct Object *ob_cu,
-                               const bool gpencil_lines,
-                               const bool use_collections,
-                               const bool only_stroke);
-
 void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene);
 
 bool BKE_gpencil_from_image(struct SpaceImage *sima,
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
new file mode 100644 (file)
index 0000000..8c52e6d
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ */
+
+#ifndef __BKE_GPENCIL_GEOM_H__
+#define __BKE_GPENCIL_GEOM_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BoundBox;
+struct Depsgraph;
+struct Main;
+struct Object;
+struct Scene;
+struct bGPDframe;
+struct bGPDlayer;
+struct bGPDspoint;
+struct bGPDstroke;
+struct bGPdata;
+
+/* Object boundbox. */
+bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]);
+bool BKE_gpencil_stroke_minmax(const struct bGPDstroke *gps,
+                               const bool use_select,
+                               float r_min[3],
+                               float r_max[3]);
+
+struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob);
+void BKE_gpencil_centroid_3d(struct bGPdata *gpd, float r_centroid[3]);
+void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps);
+
+/* stroke geometry utilities */
+void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]);
+void BKE_gpencil_stroke_simplify_adaptive(struct bGPDstroke *gps, float factor);
+void BKE_gpencil_stroke_simplify_fixed(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_subdivide(struct bGPDstroke *gps, int level, int type);
+bool BKE_gpencil_stroke_trim(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_merge_distance(struct bGPDframe *gpf,
+                                       struct bGPDstroke *gps,
+                                       const float threshold,
+                                       const bool use_unselected);
+
+void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
+                                int totpoints,
+                                float (*points2d)[2],
+                                int *r_direction);
+void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
+                                    int ref_totpoints,
+                                    const struct bGPDspoint *points,
+                                    int totpoints,
+                                    float (*points2d)[2],
+                                    const float scale,
+                                    int *r_direction);
+void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_geometry_update(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
+
+void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
+
+bool BKE_gpencil_stroke_sample(struct bGPDstroke *gps, const float dist, const bool select);
+bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf);
+bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence);
+bool BKE_gpencil_stroke_close(struct bGPDstroke *gps);
+void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag);
+
+bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
+bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
+                                    const int index_from,
+                                    const int index_to);
+bool BKE_gpencil_stroke_split(struct bGPDframe *gpf,
+                              struct bGPDstroke *gps,
+                              const int before_index,
+                              struct bGPDstroke **remaining_gps);
+bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
+
+float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
+
+void BKE_gpencil_convert_curve(struct Main *bmain,
+                               struct Scene *scene,
+                               struct Object *ob_gp,
+                               struct Object *ob_cu,
+                               const bool gpencil_lines,
+                               const bool use_collections,
+                               const bool only_stroke);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*  __BKE_GPENCIL_GEOM_H__ */
index 7fd5470cecc3f77efc802775bafef1451783fed6..37e00b56fd9fd4c09b9e39f5cb97b40fd86095cb 100644 (file)
@@ -119,6 +119,7 @@ set(SRC
   intern/font.c
   intern/freestyle.c
   intern/gpencil.c
+  intern/gpencil_geom.c
   intern/gpencil_modifier.c
   intern/hair.c
   intern/icons.c
@@ -299,6 +300,7 @@ set(SRC
   BKE_freestyle.h
   BKE_global.h
   BKE_gpencil.h
+  BKE_gpencil_geom.h
   BKE_gpencil_modifier.h
   BKE_hair.h
   BKE_icons.h
index 7036da1f8706bce59be184d769032455af860fb5..555b0af5a6390de4c6694f03bd5571e18bb8df16 100644 (file)
 
 #include "BLI_blenlib.h"
 #include "BLI_math_vector.h"
-#include "BLI_polyfill_2d.h"
 #include "BLI_string_utils.h"
-#include "BLI_utildefines.h"
 
 #include "BLT_translation.h"
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 
-#include "DNA_anim_types.h"
 #include "DNA_gpencil_types.h"
 #include "DNA_material_types.h"
 #include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
 #include "DNA_space_types.h"
-#include "DNA_userdef_types.h"
 
 #include "BKE_action.h"
-#include "BKE_animsys.h"
 #include "BKE_collection.h"
 #include "BKE_colortools.h"
-#include "BKE_curve.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_icons.h"
 #include "BKE_idtype.h"
 #include "BKE_image.h"
 #include "BKE_lib_id.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
-#include "BKE_object.h"
 #include "BKE_paint.h"
 
 #include "BLI_math_color.h"
 
-#include "DEG_depsgraph.h"
 #include "DEG_depsgraph_query.h"
 
 static CLG_LogRef LOG = {"bke.gpencil"};
@@ -1412,58 +1403,6 @@ Material *BKE_gpencil_object_material_ensure_active(Object *ob)
 }
 
 /* ************************************************** */
-/* GP Object - Boundbox Support */
-
-/**
- * Get min/max coordinate bounds for single stroke
- * \return Returns whether we found any selected points
- */
-bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
-                               const bool use_select,
-                               float r_min[3],
-                               float r_max[3])
-{
-  const bGPDspoint *pt;
-  int i;
-  bool changed = false;
-
-  if (ELEM(NULL, gps, r_min, r_max)) {
-    return false;
-  }
-
-  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
-    if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {
-      minmax_v3v3_v3(r_min, r_max, &pt->x);
-      changed = true;
-    }
-  }
-  return changed;
-}
-
-/* get min/max bounds of all strokes in GP datablock */
-bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
-{
-  bool changed = false;
-
-  INIT_MINMAX(r_min, r_max);
-
-  if (gpd == NULL) {
-    return changed;
-  }
-
-  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
-    bGPDframe *gpf = gpl->actframe;
-
-    if (gpf != NULL) {
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        changed = BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
-      }
-    }
-  }
-
-  return changed;
-}
-
 bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
 {
   const bGPDspoint *pt;
@@ -1476,101 +1415,6 @@ bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
   return false;
 }
 
-/* compute center of bounding box */
-void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
-{
-  float min[3], max[3], tot[3];
-
-  BKE_gpencil_data_minmax(gpd, min, max);
-
-  add_v3_v3v3(tot, min, max);
-  mul_v3_v3fl(r_centroid, tot, 0.5f);
-}
-
-/* Compute stroke bounding box. */
-void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
-{
-  INIT_MINMAX(gps->boundbox_min, gps->boundbox_max);
-  BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
-}
-
-/* create bounding box values */
-static void boundbox_gpencil(Object *ob)
-{
-  BoundBox *bb;
-  bGPdata *gpd;
-  float min[3], max[3];
-
-  if (ob->runtime.bb == NULL) {
-    ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
-  }
-
-  bb = ob->runtime.bb;
-  gpd = ob->data;
-
-  if (!BKE_gpencil_data_minmax(gpd, min, max)) {
-    min[0] = min[1] = min[2] = -1.0f;
-    max[0] = max[1] = max[2] = 1.0f;
-  }
-
-  BKE_boundbox_init_from_minmax(bb, min, max);
-
-  bb->flag &= ~BOUNDBOX_DIRTY;
-}
-
-/* get bounding box */
-BoundBox *BKE_gpencil_boundbox_get(Object *ob)
-{
-  if (ELEM(NULL, ob, ob->data)) {
-    return NULL;
-  }
-
-  bGPdata *gpd = (bGPdata *)ob->data;
-  if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) {
-    return ob->runtime.bb;
-  }
-
-  boundbox_gpencil(ob);
-
-  return ob->runtime.bb;
-}
-
-/* ************************************************** */
-/* Apply Transforms */
-
-void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4])
-{
-  if (gpd == NULL) {
-    return;
-  }
-
-  const float scalef = mat4_to_scale(mat);
-  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
-    /* FIXME: For now, we just skip parented layers.
-     * Otherwise, we have to update each frame to find
-     * the current parent position/effects.
-     */
-    if (gpl->parent) {
-      continue;
-    }
-
-    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        bGPDspoint *pt;
-        int i;
-
-        for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
-          mul_m4_v3(mat, &pt->x);
-          pt->pressure *= scalef;
-        }
-
-        /* Distortion may mean we need to re-triangulate. */
-        BKE_gpencil_stroke_geometry_update(gps);
-      }
-    }
-  }
-}
-
 /* ************************************************** */
 /* GP Object - Vertex Groups */
 
@@ -1624,918 +1468,143 @@ void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
 
 /* ************************************************** */
 
-static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
-                                          int count,
-                                          ListBase *result,
-                                          int *totweight)
-{
-  LinkData *ld;
-  MDeformVert *dv;
-  MDeformWeight *dw;
-  int i, j;
-  int tw = 0;
-  for (i = 0; i < count; i++) {
-    dv = &dv_list[i];
-
-    /* find def_nr in list, if not exist, then create one */
-    for (j = 0; j < dv->totweight; j++) {
-      bool found = false;
-      dw = &dv->dw[j];
-      for (ld = result->first; ld; ld = ld->next) {
-        if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
-        ld->data = POINTER_FROM_INT(dw->def_nr);
-        BLI_addtail(result, ld);
-        tw++;
-      }
-    }
-  }
-
-  *totweight = tw;
-}
-
-static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
+/**
+ * Get range of selected frames in layer.
+ * Always the active frame is considered as selected, so if no more selected the range
+ * will be equal to the current active frame.
+ * \param gpl: Layer
+ * \param r_initframe: Number of first selected frame
+ * \param r_endframe: Number of last selected frame
+ */
+void BKE_gpencil_frame_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe)
 {
-  int i, j;
-  LinkData *ld;
-  MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
+  *r_initframe = gpl->actframe->framenum;
+  *r_endframe = gpl->actframe->framenum;
 
-  for (i = 0; i < count; i++) {
-    dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
-    dst[i].totweight = totweight;
-    j = 0;
-    /* re-assign deform groups */
-    for (ld = def_nr_list->first; ld; ld = ld->next) {
-      dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
-      j++;
+  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+    if (gpf->flag & GP_FRAME_SELECT) {
+      if (gpf->framenum < *r_initframe) {
+        *r_initframe = gpf->framenum;
+      }
+      if (gpf->framenum > *r_endframe) {
+        *r_endframe = gpf->framenum;
+      }
     }
   }
-
-  return dst;
 }
 
-static void stroke_interpolate_deform_weights(
-    bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
+/**
+ * Get Falloff factor base on frame range
+ * \param gpf: Frame
+ * \param actnum: Number of active frame in layer
+ * \param f_init: Number of first selected frame
+ * \param f_end: Number of last selected frame
+ * \param cur_falloff: Curve with falloff factors
+ */
+float BKE_gpencil_multiframe_falloff_calc(
+    bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff)
 {
-  const MDeformVert *vl = &gps->dvert[index_from];
-  const MDeformVert *vr = &gps->dvert[index_to];
-  int i;
+  float fnum = 0.5f; /* default mid curve */
+  float value;
 
-  for (i = 0; i < vert->totweight; i++) {
-    float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
-    float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
-    vert->dw[i].weight = interpf(wr, wl, ratio);
-  }
-}
-
-static int stroke_march_next_point(const bGPDstroke *gps,
-                                   const int index_next_pt,
-                                   const float *current,
-                                   const float dist,
-                                   float *result,
-                                   float *pressure,
-                                   float *strength,
-                                   float *vert_color,
-                                   float *ratio_result,
-                                   int *index_from,
-                                   int *index_to)
-{
-  float remaining_till_next = 0.0f;
-  float remaining_march = dist;
-  float step_start[3];
-  float point[3];
-  int next_point_index = index_next_pt;
-  bGPDspoint *pt = NULL;
-
-  if (!(next_point_index < gps->totpoints)) {
-    return -1;
+  /* check curve is available */
+  if (cur_falloff == NULL) {
+    return 1.0f;
   }
 
-  copy_v3_v3(step_start, current);
-  pt = &gps->points[next_point_index];
-  copy_v3_v3(point, &pt->x);
-  remaining_till_next = len_v3v3(point, step_start);
-
-  while (remaining_till_next < remaining_march) {
-    remaining_march -= remaining_till_next;
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(point, &pt->x);
-    copy_v3_v3(step_start, point);
-    next_point_index++;
-    if (!(next_point_index < gps->totpoints)) {
-      next_point_index = gps->totpoints - 1;
-      break;
-    }
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(point, &pt->x);
-    remaining_till_next = len_v3v3(point, step_start);
+  /* frames to the right of the active frame */
+  if (gpf->framenum < actnum) {
+    fnum = (float)(gpf->framenum - f_init) / (actnum - f_init);
+    fnum *= 0.5f;
+    value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum);
   }
-  if (remaining_till_next < remaining_march) {
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(result, &pt->x);
-    *pressure = gps->points[next_point_index].pressure;
-    *strength = gps->points[next_point_index].strength;
-    memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float) * 4);
-
-    *index_from = next_point_index - 1;
-    *index_to = next_point_index;
-    *ratio_result = 1.0f;
-
-    return 0;
+  /* frames to the left of the active frame */
+  else if (gpf->framenum > actnum) {
+    fnum = (float)(gpf->framenum - actnum) / (f_end - actnum);
+    fnum *= 0.5f;
+    value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f);
   }
   else {
-    float ratio = remaining_march / remaining_till_next;
-    interp_v3_v3v3(result, step_start, point, ratio);
-    *pressure = interpf(
-        gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
-    *strength = interpf(
-        gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
-    interp_v4_v4v4(vert_color,
-                   gps->points[next_point_index - 1].vert_color,
-                   gps->points[next_point_index].vert_color,
-                   ratio);
-
-    *index_from = next_point_index - 1;
-    *index_to = next_point_index;
-    *ratio_result = ratio;
-
-    return next_point_index;
-  }
-}
-
-static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
-                                             const int index_next_pt,
-                                             const float *current,
-                                             const float dist,
-                                             float *result)
-{
-  float remaining_till_next = 0.0f;
-  float remaining_march = dist;
-  float step_start[3];
-  float point[3];
-  int next_point_index = index_next_pt;
-  bGPDspoint *pt = NULL;
-
-  if (!(next_point_index < gps->totpoints)) {
-    return -1;
+    value = 1.0f;
   }
 
-  copy_v3_v3(step_start, current);
-  pt = &gps->points[next_point_index];
-  copy_v3_v3(point, &pt->x);
-  remaining_till_next = len_v3v3(point, step_start);
-
-  while (remaining_till_next < remaining_march) {
-    remaining_march -= remaining_till_next;
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(point, &pt->x);
-    copy_v3_v3(step_start, point);
-    next_point_index++;
-    if (!(next_point_index < gps->totpoints)) {
-      next_point_index = gps->totpoints - 1;
-      break;
-    }
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(point, &pt->x);
-    remaining_till_next = len_v3v3(point, step_start);
-  }
-  if (remaining_till_next < remaining_march) {
-    pt = &gps->points[next_point_index];
-    copy_v3_v3(result, &pt->x);
-    return 0;
-  }
-  else {
-    float ratio = remaining_march / remaining_till_next;
-    interp_v3_v3v3(result, step_start, point, ratio);
-    return next_point_index;
-  }
+  return value;
 }
 
-static int stroke_march_count(const bGPDstroke *gps, const float dist)
+/* reassign strokes using a material */
+void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
 {
-  int point_count = 0;
-  float point[3];
-  int next_point_index = 1;
-  bGPDspoint *pt = NULL;
-
-  pt = &gps->points[0];
-  copy_v3_v3(point, &pt->x);
-  point_count++;
-
-  while ((next_point_index = stroke_march_next_point_no_interp(
-              gps, next_point_index, point, dist, point)) > -1) {
-    point_count++;
-    if (next_point_index == 0) {
-      break; /* last point finished */
+  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+        /* reassign strokes */
+        if ((gps->mat_nr > index) || (gps->mat_nr > totcol - 1)) {
+          gps->mat_nr--;
+          CLAMP_MIN(gps->mat_nr, 0);
+        }
+      }
     }
   }
-  return point_count;
 }
 
-/**
- * Resample a stroke
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- */
-bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select)
+/* remove strokes using a material */
+bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
 {
-  bGPDspoint *pt = gps->points;
-  bGPDspoint *pt1 = NULL;
-  bGPDspoint *pt2 = NULL;
-  int i;
-  LinkData *ld;
-  ListBase def_nr_list = {0};
-
-  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
-    return false;
-  }
-  /* TODO: Implement feature point preservation. */
-  int count = stroke_march_count(gps, dist);
-
-  bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
-  MDeformVert *new_dv = NULL;
-
-  int result_totweight;
-
-  if (gps->dvert != NULL) {
-    stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
-    new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
-  }
-
-  int next_point_index = 1;
-  i = 0;
-  float pressure, strength, ratio_result;
-  float vert_color[4];
-  int index_from, index_to;
-  float last_coord[3];
-
-  /*  1st point is always at the start */
-  pt1 = &gps->points[0];
-  copy_v3_v3(last_coord, &pt1->x);
-  pt2 = &new_pt[i];
-  copy_v3_v3(&pt2->x, last_coord);
-  new_pt[i].pressure = pt[0].pressure;
-  new_pt[i].strength = pt[0].strength;
-  memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float) * 4);
-  if (select) {
-    new_pt[i].flag |= GP_SPOINT_SELECT;
-  }
-  i++;
-
-  if (new_dv) {
-    stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
-  }
-
-  /*  the rest */
-  while ((next_point_index = stroke_march_next_point(gps,
-                                                     next_point_index,
-                                                     last_coord,
-                                                     dist,
-                                                     last_coord,
-                                                     &pressure,
-                                                     &strength,
-                                                     vert_color,
-                                                     &ratio_result,
-                                                     &index_from,
-                                                     &index_to)) > -1) {
-    pt2 = &new_pt[i];
-    copy_v3_v3(&pt2->x, last_coord);
-    new_pt[i].pressure = pressure;
-    new_pt[i].strength = strength;
-    memcpy(new_pt[i].vert_color, vert_color, sizeof(float) * 4);
-    if (select) {
-      new_pt[i].flag |= GP_SPOINT_SELECT;
-    }
-
-    if (new_dv) {
-      stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
-    }
-
-    i++;
-    if (next_point_index == 0) {
-      break; /* last point finished */
-    }
-  }
-
-  gps->points = new_pt;
-  /* Free original vertex list. */
-  MEM_freeN(pt);
-
-  if (new_dv) {
-    /* Free original weight data. */
-    BKE_gpencil_free_stroke_weights(gps);
-    MEM_freeN(gps->dvert);
-    while ((ld = BLI_pophead(&def_nr_list))) {
-      MEM_freeN(ld);
+  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+        if (gps->mat_nr == index) {
+          return true;
+        }
+      }
     }
-
-    gps->dvert = new_dv;
   }
 
-  gps->totpoints = i;
-
-  /* Calc geometry data. */
-  BKE_gpencil_stroke_geometry_update(gps);
-
-  return true;
+  return false;
 }
 
-/**
- * Backbone stretch similar to Freestyle.
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- * \param tip_length: Ignore tip jittering, set zero to use default value.
- */
-bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+void BKE_gpencil_material_remap(struct bGPdata *gpd,
+                                const unsigned int *remap,
+                                unsigned int remap_len)
 {
-  bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
-  int i;
-  float threshold = (tip_length == 0 ? 0.001f : tip_length);
-
-  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
-    return false;
-  }
-
-  last_pt = &pt[gps->totpoints - 1];
-  second_last = &pt[gps->totpoints - 2];
-  next_pt = &pt[1];
-
-  float len1 = 0.0f;
-  float len2 = 0.0f;
+  const short remap_len_short = (short)remap_len;
 
-  i = 1;
-  while (len1 < threshold && gps->totpoints > i) {
-    next_pt = &pt[i];
-    len1 = len_v3v3(&next_pt->x, &pt->x);
-    i++;
-  }
+#define MAT_NR_REMAP(n) \
+  if (n < remap_len_short) { \
+    BLI_assert(n >= 0 && remap[n] < remap_len_short); \
+    n = remap[n]; \
+  } \
+  ((void)0)
 
-  i = 2;
-  while (len2 < threshold && gps->totpoints >= i) {
-    second_last = &pt[gps->totpoints - i];
-    len2 = len_v3v3(&last_pt->x, &second_last->x);
-    i++;
+  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+        /* reassign strokes */
+        MAT_NR_REMAP(gps->mat_nr);
+      }
+    }
   }
 
-  float extend1 = (len1 + dist) / len1;
-  float extend2 = (len2 + dist) / len2;
-
-  float result1[3], result2[3];
-
-  interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
-  interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
-
-  copy_v3_v3(&pt->x, result1);
-  copy_v3_v3(&last_pt->x, result2);
-
-  return true;
+#undef MAT_NR_REMAP
 }
 
-/**
- * Trim stroke to needed segments
- * \param gps: Target stroke
- * \param index_from: the index of the first point to be used in the trimmed result
- * \param index_to: the index of the last point to be used in the trimmed result
- */
-bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
+/* Load a table with material conversion index for merged materials. */
+bool BKE_gpencil_merge_materials_table_get(Object *ob,
+                                           const float hue_threshold,
+                                           const float sat_threshold,
+                                           const float val_threshold,
+                                           GHash *r_mat_table)
 {
-  bGPDspoint *pt = gps->points, *new_pt;
-  MDeformVert *dv, *new_dv;
-
-  const int new_count = index_to - index_from + 1;
+  bool changed = false;
 
-  if (new_count >= gps->totpoints) {
-    return false;
-  }
+  Material *ma_primary = NULL;
+  Material *ma_secondary = NULL;
+  MaterialGPencilStyle *gp_style_primary = NULL;
+  MaterialGPencilStyle *gp_style_secondary = NULL;
 
-  if (new_count == 1) {
-    BKE_gpencil_free_stroke_weights(gps);
-    MEM_freeN(gps->points);
-    gps->points = NULL;
-    gps->dvert = NULL;
-    gps->totpoints = 0;
-    return false;
-  }
-
-  new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
-
-  for (int i = 0; i < new_count; i++) {
-    memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
-  }
-
-  if (gps->dvert) {
-    new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
-    for (int i = 0; i < new_count; i++) {
-      dv = &gps->dvert[i + index_from];
-      new_dv[i].flag = dv->flag;
-      new_dv[i].totweight = dv->totweight;
-      new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
-                                 "gp_stroke_dverts_dw_trimmed");
-      for (int j = 0; j < dv->totweight; j++) {
-        new_dv[i].dw[j].weight = dv->dw[j].weight;
-        new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
-      }
-    }
-    MEM_freeN(gps->dvert);
-    gps->dvert = new_dv;
-  }
-
-  MEM_freeN(gps->points);
-  gps->points = new_pt;
-  gps->totpoints = new_count;
-
-  return true;
-}
-
-bool BKE_gpencil_stroke_split(bGPDframe *gpf,
-                              bGPDstroke *gps,
-                              const int before_index,
-                              bGPDstroke **remaining_gps)
-{
-  bGPDstroke *new_gps;
-  bGPDspoint *pt = gps->points, *new_pt;
-  MDeformVert *dv, *new_dv;
-
-  if (before_index >= gps->totpoints || before_index == 0) {
-    return false;
-  }
-
-  const int new_count = gps->totpoints - before_index;
-  const int old_count = before_index;
-
-  /* Handle remaining segments first. */
-
-  new_gps = BKE_gpencil_stroke_add_existing_style(
-      gpf, gps, gps->mat_nr, new_count, gps->thickness);
-
-  new_pt = new_gps->points; /* Allocated from above. */
-
-  for (int i = 0; i < new_count; i++) {
-    memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
-  }
-
-  if (gps->dvert) {
-    new_dv = MEM_callocN(sizeof(MDeformVert) * new_count,
-                         "gp_stroke_dverts_remaining(MDeformVert)");
-    for (int i = 0; i < new_count; i++) {
-      dv = &gps->dvert[i + before_index];
-      new_dv[i].flag = dv->flag;
-      new_dv[i].totweight = dv->totweight;
-      new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
-                                 "gp_stroke_dverts_dw_remaining(MDeformWeight)");
-      for (int j = 0; j < dv->totweight; j++) {
-        new_dv[i].dw[j].weight = dv->dw[j].weight;
-        new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
-      }
-    }
-    new_gps->dvert = new_dv;
-  }
-
-  (*remaining_gps) = new_gps;
-
-  /* Trim the original stroke into a shorter one.
-   * Keep the end point. */
-
-  BKE_gpencil_stroke_trim_points(gps, 0, old_count);
-  BKE_gpencil_stroke_geometry_update(gps);
-  return true;
-}
-
-/**
- * Shrink the stroke by length.
- * \param gps: Stroke to shrink
- * \param dist: delta length
- */
-bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
-{
-  bGPDspoint *pt = gps->points, *second_last;
-  int i;
-
-  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
-    return false;
-  }
-
-  second_last = &pt[gps->totpoints - 2];
-
-  float len1, this_len1, cut_len1;
-  float len2, this_len2, cut_len2;
-  int index_start, index_end;
-
-  len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
-
-  i = 1;
-  while (len1 < dist && gps->totpoints > i - 1) {
-    this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
-    len1 += this_len1;
-    cut_len1 = len1 - dist;
-    i++;
-  }
-  index_start = i - 2;
-
-  i = 2;
-  while (len2 < dist && gps->totpoints >= i) {
-    second_last = &pt[gps->totpoints - i];
-    this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
-    len2 += this_len2;
-    cut_len2 = len2 - dist;
-    i++;
-  }
-  index_end = gps->totpoints - i + 2;
-
-  if (len1 < dist || len2 < dist || index_end <= index_start) {
-    index_start = index_end = 0; /* empty stroke */
-  }
-
-  if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
-    index_start = index_end = 0; /* no length left to cut */
-  }
-
-  BKE_gpencil_stroke_trim_points(gps, index_start, index_end);
-
-  if (gps->totpoints == 0) {
-    return false;
-  }
-
-  pt = gps->points;
-
-  float cut1 = cut_len1 / this_len1;
-  float cut2 = cut_len2 / this_len2;
-
-  float result1[3], result2[3];
-
-  interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
-  interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
-
-  copy_v3_v3(&pt[0].x, result1);
-  copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
-
-  return true;
-}
-
-/**
- * Apply smooth to stroke point
- * \param gps: Stroke to smooth
- * \param i: Point index
- * \param inf: Amount of smoothing to apply
- */
-bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
-{
-  bGPDspoint *pt = &gps->points[i];
-  float sco[3] = {0.0f};
-
-  /* Do nothing if not enough points to smooth out */
-  if (gps->totpoints <= 2) {
-    return false;
-  }
-
-  /* Only affect endpoints by a fraction of the normal strength,
-   * to prevent the stroke from shrinking too much
-   */
-  if ((i == 0) || (i == gps->totpoints - 1)) {
-    inf *= 0.1f;
-  }
-
-  /* Compute smoothed coordinate by taking the ones nearby */
-  /* XXX: This is potentially slow,
-   *      and suffers from accumulation error as earlier points are handled before later ones. */
-  {
-    /* XXX: this is hardcoded to look at 2 points on either side of the current one
-     * (i.e. 5 items total). */
-    const int steps = 2;
-    const float average_fac = 1.0f / (float)(steps * 2 + 1);
-    int step;
-
-    /* add the point itself */
-    madd_v3_v3fl(sco, &pt->x, average_fac);
-
-    /* n-steps before/after current point */
-    /* XXX: review how the endpoints are treated by this algorithm. */
-    /* XXX: falloff measures should also introduce some weighting variations,
-     *      so that further-out points get less weight. */
-    for (step = 1; step <= steps; step++) {
-      bGPDspoint *pt1, *pt2;
-      int before = i - step;
-      int after = i + step;
-
-      CLAMP_MIN(before, 0);
-      CLAMP_MAX(after, gps->totpoints - 1);
-
-      pt1 = &gps->points[before];
-      pt2 = &gps->points[after];
-
-      /* add both these points to the average-sum (s += p[i]/n) */
-      madd_v3_v3fl(sco, &pt1->x, average_fac);
-      madd_v3_v3fl(sco, &pt2->x, average_fac);
-    }
-  }
-
-  /* Based on influence factor, blend between original and optimal smoothed coordinate */
-  interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
-
-  return true;
-}
-
-/**
- * Apply smooth for strength to stroke point */
-bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
-{
-  bGPDspoint *ptb = &gps->points[point_index];
-
-  /* Do nothing if not enough points */
-  if ((gps->totpoints <= 2) || (point_index < 1)) {
-    return false;
-  }
-  /* Only affect endpoints by a fraction of the normal influence */
-  float inf = influence;
-  if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
-    inf *= 0.01f;
-  }
-  /* Limit max influence to reduce pop effect. */
-  CLAMP_MAX(inf, 0.98f);
-
-  float total = 0.0f;
-  float max_strength = 0.0f;
-  const int steps = 4;
-  const float average_fac = 1.0f / (float)(steps * 2 + 1);
-  int step;
-
-  /* add the point itself */
-  total += ptb->strength * average_fac;
-  max_strength = ptb->strength;
-
-  /* n-steps before/after current point */
-  for (step = 1; step <= steps; step++) {
-    bGPDspoint *pt1, *pt2;
-    int before = point_index - step;
-    int after = point_index + step;
-
-    CLAMP_MIN(before, 0);
-    CLAMP_MAX(after, gps->totpoints - 1);
-
-    pt1 = &gps->points[before];
-    pt2 = &gps->points[after];
-
-    /* add both these points to the average-sum (s += p[i]/n) */
-    total += pt1->strength * average_fac;
-    total += pt2->strength * average_fac;
-    /* Save max value. */
-    if (max_strength < pt1->strength) {
-      max_strength = pt1->strength;
-    }
-    if (max_strength < pt2->strength) {
-      max_strength = pt2->strength;
-    }
-  }
-
-  /* Based on influence factor, blend between original and optimal smoothed value. */
-  ptb->strength = interpf(ptb->strength, total, inf);
-  /* Clamp to maximum stroke strength to avoid weird results. */
-  CLAMP_MAX(ptb->strength, max_strength);
-
-  return true;
-}
-
-/**
- * Apply smooth for thickness to stroke point (use pressure) */
-bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
-{
-  bGPDspoint *ptb = &gps->points[point_index];
-
-  /* Do nothing if not enough points */
-  if ((gps->totpoints <= 2) || (point_index < 1)) {
-    return false;
-  }
-  /* Only affect endpoints by a fraction of the normal influence */
-  float inf = influence;
-  if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
-    inf *= 0.01f;
-  }
-  /* Limit max influence to reduce pop effect. */
-  CLAMP_MAX(inf, 0.98f);
-
-  float total = 0.0f;
-  float max_pressure = 0.0f;
-  const int steps = 4;
-  const float average_fac = 1.0f / (float)(steps * 2 + 1);
-  int step;
-
-  /* add the point itself */
-  total += ptb->pressure * average_fac;
-  max_pressure = ptb->pressure;
-
-  /* n-steps before/after current point */
-  for (step = 1; step <= steps; step++) {
-    bGPDspoint *pt1, *pt2;
-    int before = point_index - step;
-    int after = point_index + step;
-
-    CLAMP_MIN(before, 0);
-    CLAMP_MAX(after, gps->totpoints - 1);
-
-    pt1 = &gps->points[before];
-    pt2 = &gps->points[after];
-
-    /* add both these points to the average-sum (s += p[i]/n) */
-    total += pt1->pressure * average_fac;
-    total += pt2->pressure * average_fac;
-    /* Save max value. */
-    if (max_pressure < pt1->pressure) {
-      max_pressure = pt1->pressure;
-    }
-    if (max_pressure < pt2->pressure) {
-      max_pressure = pt2->pressure;
-    }
-  }
-
-  /* Based on influence factor, blend between original and optimal smoothed value. */
-  ptb->pressure = interpf(ptb->pressure, total, inf);
-  /* Clamp to maximum stroke thickness to avoid weird results. */
-  CLAMP_MAX(ptb->pressure, max_pressure);
-  return true;
-}
-
-/**
- * Apply smooth for UV rotation to stroke point (use pressure).
- */
-bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
-{
-  bGPDspoint *ptb = &gps->points[point_index];
-
-  /* Do nothing if not enough points */
-  if (gps->totpoints <= 2) {
-    return false;
-  }
-
-  /* Compute theoretical optimal value */
-  bGPDspoint *pta, *ptc;
-  int before = point_index - 1;
-  int after = point_index + 1;
-
-  CLAMP_MIN(before, 0);
-  CLAMP_MAX(after, gps->totpoints - 1);
-
-  pta = &gps->points[before];
-  ptc = &gps->points[after];
-
-  /* the optimal value is the corresponding to the interpolation of the pressure
-   * at the distance of point b
-   */
-  float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
-  /* sometimes the factor can be wrong due stroke geometry, so use middle point */
-  if ((fac < 0.0f) || (fac > 1.0f)) {
-    fac = 0.5f;
-  }
-  float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
-
-  /* Based on influence factor, blend between original and optimal */
-  ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
-  CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
-
-  return true;
-}
-
-/**
- * Get range of selected frames in layer.
- * Always the active frame is considered as selected, so if no more selected the range
- * will be equal to the current active frame.
- * \param gpl: Layer
- * \param r_initframe: Number of first selected frame
- * \param r_endframe: Number of last selected frame
- */
-void BKE_gpencil_frame_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe)
-{
-  *r_initframe = gpl->actframe->framenum;
-  *r_endframe = gpl->actframe->framenum;
-
-  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
-    if (gpf->flag & GP_FRAME_SELECT) {
-      if (gpf->framenum < *r_initframe) {
-        *r_initframe = gpf->framenum;
-      }
-      if (gpf->framenum > *r_endframe) {
-        *r_endframe = gpf->framenum;
-      }
-    }
-  }
-}
-
-/**
- * Get Falloff factor base on frame range
- * \param gpf: Frame
- * \param actnum: Number of active frame in layer
- * \param f_init: Number of first selected frame
- * \param f_end: Number of last selected frame
- * \param cur_falloff: Curve with falloff factors
- */
-float BKE_gpencil_multiframe_falloff_calc(
-    bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff)
-{
-  float fnum = 0.5f; /* default mid curve */
-  float value;
-
-  /* check curve is available */
-  if (cur_falloff == NULL) {
-    return 1.0f;
-  }
-
-  /* frames to the right of the active frame */
-  if (gpf->framenum < actnum) {
-    fnum = (float)(gpf->framenum - f_init) / (actnum - f_init);
-    fnum *= 0.5f;
-    value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum);
-  }
-  /* frames to the left of the active frame */
-  else if (gpf->framenum > actnum) {
-    fnum = (float)(gpf->framenum - actnum) / (f_end - actnum);
-    fnum *= 0.5f;
-    value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f);
-  }
-  else {
-    value = 1.0f;
-  }
-
-  return value;
-}
-
-/* reassign strokes using a material */
-void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
-{
-  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
-    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        /* reassign strokes */
-        if ((gps->mat_nr > index) || (gps->mat_nr > totcol - 1)) {
-          gps->mat_nr--;
-          CLAMP_MIN(gps->mat_nr, 0);
-        }
-      }
-    }
-  }
-}
-
-/* remove strokes using a material */
-bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
-{
-  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
-    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        if (gps->mat_nr == index) {
-          return true;
-        }
-      }
-    }
-  }
-
-  return false;
-}
-
-void BKE_gpencil_material_remap(struct bGPdata *gpd,
-                                const unsigned int *remap,
-                                unsigned int remap_len)
-{
-  const short remap_len_short = (short)remap_len;
-
-#define MAT_NR_REMAP(n) \
-  if (n < remap_len_short) { \
-    BLI_assert(n >= 0 && remap[n] < remap_len_short); \
-    n = remap[n]; \
-  } \
-  ((void)0)
-
-  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
-    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        /* reassign strokes */
-        MAT_NR_REMAP(gps->mat_nr);
-      }
-    }
-  }
-
-#undef MAT_NR_REMAP
-}
-
-/* Load a table with material conversion index for merged materials. */
-bool BKE_gpencil_merge_materials_table_get(Object *ob,
-                                           const float hue_threshold,
-                                           const float sat_threshold,
-                                           const float val_threshold,
-                                           GHash *r_mat_table)
-{
-  bool changed = false;
-
-  Material *ma_primary = NULL;
-  Material *ma_secondary = NULL;
-  MaterialGPencilStyle *gp_style_primary = NULL;
-  MaterialGPencilStyle *gp_style_secondary = NULL;
-
-  short *totcol = BKE_object_material_len_p(ob);
-  if (totcol == 0) {
-    return changed;
+  short *totcol = BKE_object_material_len_p(ob);
+  if (totcol == 0) {
+    return changed;
   }
 
   for (int idx_primary = 0; idx_primary < *totcol; idx_primary++) {
@@ -2650,1053 +1719,6 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
   return -1;
 }
 
-/* Get points of stroke always flat to view not affected by camera view or view position */
-void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
-                                int totpoints,
-                                float (*points2d)[2],
-                                int *r_direction)
-{
-  BLI_assert(totpoints >= 2);
-
-  const bGPDspoint *pt0 = &points[0];
-  const bGPDspoint *pt1 = &points[1];
-  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
-
-  float locx[3];
-  float locy[3];
-  float loc3[3];
-  float normal[3];
-
-  /* local X axis (p0 -> p1) */
-  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
-
-  /* point vector at 3/4 */
-  float v3[3];
-  if (totpoints == 2) {
-    mul_v3_v3fl(v3, &pt3->x, 0.001f);
-  }
-  else {
-    copy_v3_v3(v3, &pt3->x);
-  }
-
-  sub_v3_v3v3(loc3, v3, &pt0->x);
-
-  /* vector orthogonal to polygon plane */
-  cross_v3_v3v3(normal, locx, loc3);
-
-  /* local Y axis (cross to normal/x axis) */
-  cross_v3_v3v3(locy, normal, locx);
-
-  /* Normalize vectors */
-  normalize_v3(locx);
-  normalize_v3(locy);
-
-  /* Get all points in local space */
-  for (int i = 0; i < totpoints; i++) {
-    const bGPDspoint *pt = &points[i];
-    float loc[3];
-
-    /* Get local space using first point as origin */
-    sub_v3_v3v3(loc, &pt->x, &pt0->x);
-
-    points2d[i][0] = dot_v3v3(loc, locx);
-    points2d[i][1] = dot_v3v3(loc, locy);
-  }
-
-  /* Concave (-1), Convex (1), or Autodetect (0)? */
-  *r_direction = (int)locy[2];
-}
-
-/* Get points of stroke always flat to view not affected by camera view or view position
- * using another stroke as reference
- */
-void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
-                                    int ref_totpoints,
-                                    const bGPDspoint *points,
-                                    int totpoints,
-                                    float (*points2d)[2],
-                                    const float scale,
-                                    int *r_direction)
-{
-  BLI_assert(totpoints >= 2);
-
-  const bGPDspoint *pt0 = &ref_points[0];
-  const bGPDspoint *pt1 = &ref_points[1];
-  const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
-
-  float locx[3];
-  float locy[3];
-  float loc3[3];
-  float normal[3];
-
-  /* local X axis (p0 -> p1) */
-  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
-
-  /* point vector at 3/4 */
-  float v3[3];
-  if (totpoints == 2) {
-    mul_v3_v3fl(v3, &pt3->x, 0.001f);
-  }
-  else {
-    copy_v3_v3(v3, &pt3->x);
-  }
-
-  sub_v3_v3v3(loc3, v3, &pt0->x);
-
-  /* vector orthogonal to polygon plane */
-  cross_v3_v3v3(normal, locx, loc3);
-
-  /* local Y axis (cross to normal/x axis) */
-  cross_v3_v3v3(locy, normal, locx);
-
-  /* Normalize vectors */
-  normalize_v3(locx);
-  normalize_v3(locy);
-
-  /* Get all points in local space */
-  for (int i = 0; i < totpoints; i++) {
-    const bGPDspoint *pt = &points[i];
-    float loc[3];
-    float v1[3];
-    float vn[3] = {0.0f, 0.0f, 0.0f};
-
-    /* apply scale to extremes of the stroke to get better collision detection
-     * the scale is divided to get more control in the UI parameter
-     */
-    /* first point */
-    if (i == 0) {
-      const bGPDspoint *pt_next = &points[i + 1];
-      sub_v3_v3v3(vn, &pt->x, &pt_next->x);
-      normalize_v3(vn);
-      mul_v3_fl(vn, scale / 10.0f);
-      add_v3_v3v3(v1, &pt->x, vn);
-    }
-    /* last point */
-    else if (i == totpoints - 1) {
-      const bGPDspoint *pt_prev = &points[i - 1];
-      sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
-      normalize_v3(vn);
-      mul_v3_fl(vn, scale / 10.0f);
-      add_v3_v3v3(v1, &pt->x, vn);
-    }
-    else {
-      copy_v3_v3(v1, &pt->x);
-    }
-
-    /* Get local space using first point as origin (ref stroke) */
-    sub_v3_v3v3(loc, v1, &pt0->x);
-
-    points2d[i][0] = dot_v3v3(loc, locx);
-    points2d[i][1] = dot_v3v3(loc, locy);
-  }
-
-  /* Concave (-1), Convex (1), or Autodetect (0)? */
-  *r_direction = (int)locy[2];
-}
-
-/* calc texture coordinates using flat projected points */
-static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
-                                        bGPDstroke *gps,
-                                        const float minv[2],
-                                        float maxv[2],
-                                        float (*r_uv)[2])
-{
-  const float s = sin(gps->uv_rotation);
-  const float c = cos(gps->uv_rotation);
-
-  /* Calc center for rotation. */
-  float center[2] = {0.5f, 0.5f};
-  float d[2];
-  d[0] = maxv[0] - minv[0];
-  d[1] = maxv[1] - minv[1];
-  for (int i = 0; i < gps->totpoints; i++) {
-    r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
-    r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
-
-    /* Apply translation. */
-    add_v2_v2(r_uv[i], gps->uv_translation);
-
-    /* Apply Rotation. */
-    r_uv[i][0] -= center[0];
-    r_uv[i][1] -= center[1];
-
-    float x = r_uv[i][0] * c - r_uv[i][1] * s;
-    float y = r_uv[i][0] * s + r_uv[i][1] * c;
-
-    r_uv[i][0] = x + center[0];
-    r_uv[i][1] = y + center[1];
-
-    /* Apply scale. */
-    if (gps->uv_scale != 0.0f) {
-      mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale);
-    }
-  }
-}
-
-/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was
- * modified) */
-void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
-{
-  BLI_assert(gps->totpoints >= 3);
-
-  /* allocate memory for temporary areas */
-  gps->tot_triangles = gps->totpoints - 2;
-  uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
-                                        "GP Stroke temp triangulation");
-  float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
-                                    "GP Stroke temp 2d points");
-  float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
-
-  int direction = 0;
-
-  /* convert to 2d and triangulate */
-  BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
-  BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
-
-  /* calc texture coordinates automatically */
-  float minv[2];
-  float maxv[2];
-  /* first needs bounding box data */
-  ARRAY_SET_ITEMS(minv, -1.0f, -1.0f);
-  ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f);
-
-  /* calc uv data */
-  gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv);
-
-  /* Save triangulation data. */
-  if (gps->tot_triangles > 0) {
-    MEM_SAFE_FREE(gps->triangles);
-    gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
-                                 "GP Stroke triangulation");
-
-    for (int i = 0; i < gps->tot_triangles; i++) {
-      memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
-    }
-
-    /* Copy UVs to bGPDspoint. */
-    for (int i = 0; i < gps->totpoints; i++) {
-      copy_v2_v2(gps->points[i].uv_fill, uv[i]);
-    }
-  }
-  else {
-    /* No triangles needed - Free anything allocated previously */
-    if (gps->triangles) {
-      MEM_freeN(gps->triangles);
-    }
-
-    gps->triangles = NULL;
-  }
-
-  /* clear memory */
-  MEM_SAFE_FREE(tmp_triangles);
-  MEM_SAFE_FREE(points2d);
-  MEM_SAFE_FREE(uv);
-}
-
-/* texture coordinate utilities */
-void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
-{
-  if (gps == NULL || gps->totpoints == 0) {
-    return;
-  }
-
-  bGPDspoint *pt = gps->points;
-  float totlen = 0.0f;
-  pt[0].uv_fac = totlen;
-  for (int i = 1; i < gps->totpoints; i++) {
-    totlen += len_v3v3(&pt[i - 1].x, &pt[i].x);
-    pt[i].uv_fac = totlen;
-  }
-}
-
-/* Recalc the internal geometry caches for fill and uvs. */
-void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
-{
-  if (gps == NULL) {
-    return;
-  }
-
-  if (gps->totpoints > 2) {
-    BKE_gpencil_stroke_fill_triangulate(gps);
-  }
-  else {
-    gps->tot_triangles = 0;
-    MEM_SAFE_FREE(gps->triangles);
-  }
-
-  /* calc uv data along the stroke */
-  BKE_gpencil_stroke_uv_update(gps);
-
-  /* Calc stroke bounding box. */
-  BKE_gpencil_stroke_boundingbox_calc(gps);
-}
-
-float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
-{
-  if (!gps->points || gps->totpoints < 2) {
-    return 0.0f;
-  }
-  float *last_pt = &gps->points[0].x;
-  int i;
-  bGPDspoint *pt;
-  float total_length = 0.0f;
-  for (i = 1; i < gps->totpoints; i++) {
-    pt = &gps->points[i];
-    if (use_3d) {
-      total_length += len_v3v3(&pt->x, last_pt);
-    }
-    else {
-      total_length += len_v2v2(&pt->x, last_pt);
-    }
-    last_pt = &pt->x;
-  }
-  return total_length;
-}
-
-/**
- * Trim stroke to the first intersection or loop
- * \param gps: Stroke data
- */
-bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
-{
-  if (gps->totpoints < 4) {
-    return false;
-  }
-  bool intersect = false;
-  int start, end;
-  float point[3];
-  /* loop segments from start until we have an intersection */
-  for (int i = 0; i < gps->totpoints - 2; i++) {
-    start = i;
-    bGPDspoint *a = &gps->points[start];
-    bGPDspoint *b = &gps->points[start + 1];
-    for (int j = start + 2; j < gps->totpoints - 1; j++) {
-      end = j + 1;
-      bGPDspoint *c = &gps->points[j];
-      bGPDspoint *d = &gps->points[end];
-      float pointb[3];
-      /* get intersection */
-      if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
-        if (len_v3(point) > 0.0f) {
-          float closest[3];
-          /* check intersection is on both lines */
-          float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
-          if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
-            continue;
-          }
-          lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
-          if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
-            continue;
-          }
-          else {
-            intersect = true;
-            break;
-          }
-        }
-      }
-    }
-    if (intersect) {
-      break;
-    }
-  }
-
-  /* trim unwanted points */
-  if (intersect) {
-
-    /* save points */
-    bGPDspoint *old_points = MEM_dupallocN(gps->points);
-    MDeformVert *old_dvert = NULL;
-    MDeformVert *dvert_src = NULL;
-
-    if (gps->dvert != NULL) {
-      old_dvert = MEM_dupallocN(gps->dvert);
-    }
-
-    /* resize gps */
-    int newtot = end - start + 1;
-
-    gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
-    if (gps->dvert != NULL) {
-      gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
-    }
-
-    for (int i = 0; i < newtot; i++) {
-      int idx = start + i;
-      bGPDspoint *pt_src = &old_points[idx];
-      bGPDspoint *pt_new = &gps->points[i];
-      memcpy(pt_new, pt_src, sizeof(bGPDspoint));
-      if (gps->dvert != NULL) {
-        dvert_src = &old_dvert[idx];
-        MDeformVert *dvert = &gps->dvert[i];
-        memcpy(dvert, dvert_src, sizeof(MDeformVert));
-        if (dvert_src->dw) {
-          memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
-        }
-      }
-      if (idx == start || idx == end) {
-        copy_v3_v3(&pt_new->x, point);
-      }
-    }
-
-    gps->totpoints = newtot;
-
-    MEM_SAFE_FREE(old_points);
-    MEM_SAFE_FREE(old_dvert);
-  }
-
-  BKE_gpencil_stroke_geometry_update(gps);
-
-  return intersect;
-}
-
-/**
- * Close stroke
- * \param gps: Stroke to close
- */
-bool BKE_gpencil_stroke_close(bGPDstroke *gps)
-{
-  bGPDspoint *pt1 = NULL;
-  bGPDspoint *pt2 = NULL;
-
-  /* Only can close a stroke with 3 points or more. */
-  if (gps->totpoints < 3) {
-    return false;
-  }
-
-  /* Calc average distance between points to get same level of sampling. */
-  float dist_tot = 0.0f;
-  for (int i = 0; i < gps->totpoints - 1; i++) {
-    pt1 = &gps->points[i];
-    pt2 = &gps->points[i + 1];
-    dist_tot += len_v3v3(&pt1->x, &pt2->x);
-  }
-  /* Calc the average distance. */
-  float dist_avg = dist_tot / (gps->totpoints - 1);
-
-  /* Calc distance between last and first point. */
-  pt1 = &gps->points[gps->totpoints - 1];
-  pt2 = &gps->points[0];
-  float dist_close = len_v3v3(&pt1->x, &pt2->x);
-
-  /* if the distance to close is very small, don't need add points and just enable cyclic. */
-  if (dist_close <= dist_avg) {
-    gps->flag |= GP_STROKE_CYCLIC;
-    return true;
-  }
-
-  /* Calc number of points required using the average distance. */
-  int tot_newpoints = MAX2(dist_close / dist_avg, 1);
-
-  /* Resize stroke array. */
-  int old_tot = gps->totpoints;
-  gps->totpoints += tot_newpoints;
-  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
-  if (gps->dvert != NULL) {
-    gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
-  }
-
-  /* Generate new points */
-  pt1 = &gps->points[old_tot - 1];
-  pt2 = &gps->points[0];
-  bGPDspoint *pt = &gps->points[old_tot];
-  for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
-    float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f;
-    /* Clamp last point to be near, but not on top of first point. */
-    if ((tot_newpoints > 1) && (i == tot_newpoints)) {
-      step *= 0.99f;
-    }
-
-    /* Average point. */
-    interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
-    pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
-    pt->strength = interpf(pt2->strength, pt1->strength, step);
-    pt->flag = 0;
-    interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
-
-    /* Set weights. */
-    if (gps->dvert != NULL) {
-      MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
-      MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
-      float weight_1 = dw1 ? dw1->weight : 0.0f;
-
-      MDeformVert *dvert2 = &gps->dvert[0];
-      MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
-      float weight_2 = dw2 ? dw2->weight : 0.0f;
-
-      MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
-      dvert_final->totweight = 0;
-      MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
-      if (dvert_final->dw) {
-        dw->weight = interpf(weight_2, weight_1, step);
-      }
-    }
-  }
-
-  /* Enable cyclic flag. */
-  gps->flag |= GP_STROKE_CYCLIC;
-
-  return true;
-}
-/* Dissolve points in stroke */
-void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
-{
-  bGPDspoint *pt;
-  MDeformVert *dvert = NULL;
-  int i;
-
-  int tot = gps->totpoints; /* number of points in new buffer */
-  /* first pass: count points to remove */
-  /* Count how many points are selected (i.e. how many to remove) */
-  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
-    if (pt->flag & tag) {
-      /* selected point - one of the points to remove */
-      tot--;
-    }
-  }
-
-  /* if no points are left, we simply delete the entire stroke */
-  if (tot <= 0) {
-    /* remove the entire stroke */
-    if (gps->points) {
-      MEM_freeN(gps->points);
-    }
-    if (gps->dvert) {
-      BKE_gpencil_free_stroke_weights(gps);
-      MEM_freeN(gps->dvert);
-    }
-    if (gps->triangles) {
-      MEM_freeN(gps->triangles);
-    }
-    BLI_freelinkN(&gpf->strokes, gps);
-  }
-  else {
-    /* just copy all points to keep into a smaller buffer */
-    bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
-    bGPDspoint *npt = new_points;
-
-    MDeformVert *new_dvert = NULL;
-    MDeformVert *ndvert = NULL;
-
-    if (gps->dvert != NULL) {
-      new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
-      ndvert = new_dvert;
-    }
-
-    (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
-    for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
-      if ((pt->flag & tag) == 0) {
-        *npt = *pt;
-        npt++;
-
-        if (gps->dvert != NULL) {
-          *ndvert = *dvert;
-          ndvert->dw = MEM_dupallocN(dvert->dw);
-          ndvert++;
-        }
-      }
-      if (gps->dvert != NULL) {
-        dvert++;
-      }
-    }
-
-    /* free the old buffer */
-    if (gps->points) {
-      MEM_freeN(gps->points);
-    }
-    if (gps->dvert) {
-      BKE_gpencil_free_stroke_weights(gps);
-      MEM_freeN(gps->dvert);
-    }
-
-    /* save the new buffer */
-    gps->points = new_points;
-    gps->dvert = new_dvert;
-    gps->totpoints = tot;
-
-    /* triangles cache needs to be recalculated */
-    BKE_gpencil_stroke_geometry_update(gps);
-  }
-}
-
-/* Merge by distance ------------------------------------- */
-/* 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 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,
-                                       bGPDstroke *gps,
-                                       const float threshold,
-                                       const bool use_unselected)
-{
-  bGPDspoint *pt = NULL;
-  bGPDspoint *pt_next = NULL;
-  float tagged = false;
-  /* Use square distance to speed up loop */
-  const float th_square = threshold * threshold;
-  /* Need to have something to merge. */
-  if (gps->totpoints < 2) {
-    return;
-  }
-  int i = 0;
-  int step = 1;
-  while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
-    pt = &gps->points[i];
-    if (pt->flag & GP_SPOINT_TAG) {
-      i++;
-      step = 1;
-      continue;
-    }
-    pt_next = &gps->points[i + step];
-    /* Do not recalc tagged points. */
-    if (pt_next->flag & GP_SPOINT_TAG) {
-      step++;
-      continue;
-    }
-    /* Check if contiguous points are selected. */
-    if (!use_unselected) {
-      if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
-        i++;
-        step = 1;
-        continue;
-      }
-    }
-    float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
-    if (len_square <= th_square) {
-      tagged = true;
-      if (i != gps->totpoints - 1) {
-        /* Tag second point for delete. */
-        pt_next->flag |= GP_SPOINT_TAG;
-      }
-      else {
-        pt->flag |= GP_SPOINT_TAG;
-      }
-      /* Jump to next pair of points, keeping first point segment equals.*/
-      step++;
-    }
-    else {
-      /* Analyze next point. */
-      i++;
-      step = 1;
-    }
-  }
-
-  /* Always untag extremes. */
-  pt = &gps->points[0];
-  pt->flag &= ~GP_SPOINT_TAG;
-  pt = &gps->points[gps->totpoints - 1];
-  pt->flag &= ~GP_SPOINT_TAG;
-
-  /* Dissolve tagged points */
-  if (tagged) {
-    BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
-  }
-
-  /* Calc geometry data. */
-  BKE_gpencil_stroke_geometry_update(gps);
-}
-
-/* Helper: Check materials with same color. */
-static int gpencil_check_same_material_color(Object *ob_gp, float color[4], Material **r_mat)
-{
-  Material *ma = NULL;
-  float color_cu[4];
-  linearrgb_to_srgb_v3_v3(color_cu, color);
-  float hsv1[4];
-  rgb_to_hsv_v(color_cu, hsv1);
-  hsv1[3] = color[3];
-
-  for (int i = 1; i <= ob_gp->totcol; i++) {
-    ma = BKE_object_material_get(ob_gp, i);
-    MaterialGPencilStyle *gp_style = ma->gp_style;
-    /* Check color with small tolerance (better in HSV). */
-    float hsv2[4];
-    rgb_to_hsv_v(gp_style->fill_rgba, hsv2);
-    hsv2[3] = gp_style->fill_rgba[3];
-    if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) &&
-        (compare_v4v4(hsv1, hsv2, 0.01f))) {
-      *r_mat = ma;
-      return i - 1;
-    }
-  }
-
-  *r_mat = NULL;
-  return -1;
-}
-
-/* Helper: Add gpencil material using curve material as base. */
-static Material *gpencil_add_from_curve_material(Main *bmain,
-                                                 Object *ob_gp,
-                                                 const float cu_color[4],
-                                                 const bool gpencil_lines,
-                                                 const bool fill,
-                                                 int *r_idx)
-{
-  Material *mat_gp = BKE_gpencil_object_material_new(
-      bmain, ob_gp, (fill) ? "Material" : "Unassigned", r_idx);
-  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
-
-  /* Stroke color. */
-  if (gpencil_lines) {
-    ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
-    gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
-  }
-  else {
-    linearrgb_to_srgb_v4(gp_style->stroke_rgba, cu_color);
-    gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
-  }
-
-  /* Fill color. */
-  linearrgb_to_srgb_v4(gp_style->fill_rgba, cu_color);
-  /* Fill is false if the original curve hasn't material assigned, so enable it. */
-  if (fill) {
-    gp_style->flag |= GP_MATERIAL_FILL_SHOW;
-  }
-
-  /* Check at least one is enabled. */
-  if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
-      ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
-    gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
-  }
-
-  return mat_gp;
-}
-
-/* Helper: Create new stroke section. */
-static void gpencil_add_new_points(bGPDstroke *gps,
-                                   float *coord_array,
-                                   float pressure,
-                                   int init,
-                                   int totpoints,
-                                   const float init_co[3],
-                                   bool last)
-{
-  for (int i = 0; i < totpoints; i++) {
-    bGPDspoint *pt = &gps->points[i + init];
-    copy_v3_v3(&pt->x, &coord_array[3 * i]);
-    /* Be sure the last point is not on top of the first point of the curve or
-     * the close of the stroke will produce glitches. */
-    if ((last) && (i > 0) && (i == totpoints - 1)) {
-      float dist = len_v3v3(init_co, &pt->x);
-      if (dist < 0.1f) {
-        /* Interpolate between previous point and current to back slightly. */
-        bGPDspoint *pt_prev = &gps->points[i + init - 1];
-        interp_v3_v3v3(&pt->x, &pt_prev->x, &pt->x, 0.95f);
-      }
-    }
-
-    pt->pressure = pressure;
-    pt->strength = 1.0f;
-  }
-}
-
-/* Helper: Get the first collection that includes the object. */
-static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob)
-{
-  Collection *mycol = NULL;
-  FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
-    for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
-      if ((mycol == NULL) && (cob->ob == ob)) {
-        mycol = collection;
-      }
-    }
-  }
-  FOREACH_SCENE_COLLECTION_END;
-
-  return mycol;
-}
-
-/* Helper: Convert one spline to grease pencil stroke. */
-static void gpencil_convert_spline(Main *bmain,
-                                   Object *ob_gp,
-                                   Object *ob_cu,
-                                   const bool gpencil_lines,
-                                   const bool only_stroke,
-                                   bGPDframe *gpf,
-                                   Nurb *nu)
-{
-  Curve *cu = (Curve *)ob_cu->data;
-  bool cyclic = true;
-
-  /* Create Stroke. */
-  bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke");
-  gps->thickness = 1.0f;
-  gps->fill_opacity_fac = 1.0f;
-  gps->hardeness = 1.0f;
-  gps->uv_scale = 1.0f;
-
-  ARRAY_SET_ITEMS(gps->aspect_ratio, 1.0f, 1.0f);
-  ARRAY_SET_ITEMS(gps->caps, GP_STROKE_CAP_ROUND, GP_STROKE_CAP_ROUND);
-  gps->inittime = 0.0f;
-
-  gps->flag &= ~GP_STROKE_SELECT;
-  gps->flag |= GP_STROKE_3DSPACE;
-
-  gps->mat_nr = 0;
-  /* Count total points
-   * The total of points must consider that last point of each segment is equal to the first
-   * point of next segment.
-   */
-  int totpoints = 0;
-  int segments = 0;
-  int resolu = nu->resolu + 1;
-  segments = nu->pntsu;
-  if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) {
-    segments--;
-    cyclic = false;
-  }
-  totpoints = (resolu * segments) - (segments - 1);
-
-  /* Materials
-   * Notice: The color of the material is the color of viewport and not the final shader color.
-   */
-  Material *mat_gp = NULL;
-  bool fill = true;
-  /* Check if grease pencil has a material with same color.*/
-  float color[4];
-  if ((cu->mat) && (*cu->mat)) {
-    Material *mat_cu = *cu->mat;
-    copy_v4_v4(color, &mat_cu->r);
-  }
-  else {
-    /* Gray (unassigned from SVG add-on) */
-    zero_v4(color);
-    add_v3_fl(color, 0.6f);
-    color[3] = 1.0f;
-    fill = false;
-  }
-
-  /* Special case: If the color was created by the SVG add-on and the name contains '_stroke' and
-   * there is only one color, the stroke must not be closed, fill to false and use for
-   * stroke the fill color.
-   */
-  bool do_stroke = false;
-  if (ob_cu->totcol == 1) {
-    Material *ma_stroke = BKE_object_material_get(ob_cu, 1);
-    if ((ma_stroke) && (strstr(ma_stroke->id.name, "_stroke") != NULL)) {
-      do_stroke = true;
-    }
-  }
-
-  int r_idx = gpencil_check_same_material_color(ob_gp, color, &mat_gp);
-  if ((ob_cu->totcol > 0) && (r_idx < 0)) {
-    Material *mat_curve = BKE_object_material_get(ob_cu, 1);
-    mat_gp = gpencil_add_from_curve_material(bmain, ob_gp, color, gpencil_lines, fill, &r_idx);
-
-    if ((mat_curve) && (mat_curve->gp_style != NULL)) {
-      MaterialGPencilStyle *gp_style_cur = mat_curve->gp_style;
-      MaterialGPencilStyle *gp_style_gp = mat_gp->gp_style;
-
-      copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba);
-      gp_style_gp->fill_style = gp_style_cur->fill_style;
-      gp_style_gp->mix_factor = gp_style_cur->mix_factor;
-    }
-
-    /* If object has more than 1 material, use second material for stroke color. */
-    if ((!only_stroke) && (ob_cu->totcol > 1) && (BKE_object_material_get(ob_cu, 2))) {
-      mat_curve = BKE_object_material_get(ob_cu, 2);
-      if (mat_curve) {
-        linearrgb_to_srgb_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
-        mat_gp->gp_style->stroke_rgba[3] = mat_curve->a;
-      }
-    }
-    else if ((only_stroke) || (do_stroke)) {
-      /* Also use the first color if the fill is none for stroke color. */
-      if (ob_cu->totcol > 0) {
-        mat_curve = BKE_object_material_get(ob_cu, 1);
-        if (mat_curve) {
-          copy_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
-          mat_gp->gp_style->stroke_rgba[3] = mat_curve->a;
-          /* Set fill and stroke depending of curve type (3D or 2D). */
-          if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) {
-            mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
-            mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
-          }
-          else {
-            mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
-            mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
-          }
-        }
-      }
-    }
-  }
-  CLAMP_MIN(r_idx, 0);
-
-  /* Assign material index to stroke. */
-  gps->mat_nr = r_idx;
-
-  /* Add stroke to frame.*/
-  BLI_addtail(&gpf->strokes, gps);
-
-  float *coord_array = NULL;
-  float init_co[3];
-
-  switch (nu->type) {
-    case CU_POLY: {
-      /* Allocate memory for storage points. */
-      gps->totpoints = nu->pntsu;
-      gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
-      /* Increase thickness for this type. */
-      gps->thickness = 10.0f;
-
-      /* Get all curve points */
-      for (int s = 0; s < gps->totpoints; s++) {
-        BPoint *bp = &nu->bp[s];
-        bGPDspoint *pt = &gps->points[s];
-        copy_v3_v3(&pt->x, bp->vec);
-        pt->pressure = bp->radius;
-        pt->strength = 1.0f;
-      }
-      break;
-    }
-    case CU_BEZIER: {
-      /* Allocate memory for storage points. */
-      gps->totpoints = totpoints;
-      gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
-
-      int init = 0;
-      resolu = nu->resolu + 1;
-      segments = nu->pntsu;
-      if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) {
-        segments--;
-      }
-      /* Get all interpolated curve points of Beziert */
-      for (int s = 0; s < segments; s++) {
-        int inext = (s + 1) % nu->pntsu;
-        BezTriple *prevbezt = &nu->bezt[s];
-        BezTriple *bezt = &nu->bezt[inext];
-        bool last = (bool)(s == segments - 1);
-
-        coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__);
-
-        for (int j = 0; j < 3; j++) {
-          BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
-                                        prevbezt->vec[2][j],
-                                        bezt->vec[0][j],
-                                        bezt->vec[1][j],
-                                        coord_array + j,
-                                        resolu - 1,
-                                        3 * sizeof(float));
-        }
-        /* Save first point coordinates. */
-        if (s == 0) {
-          copy_v3_v3(init_co, &coord_array[0]);
-        }
-        /* Add points to the stroke */
-        gpencil_add_new_points(gps, coord_array, bezt->radius, init, resolu, init_co, last);
-        /* Free memory. */
-        MEM_SAFE_FREE(coord_array);
-
-        /* As the last point of segment is the first point of next segment, back one array
-         * element to avoid duplicated points on the same location.
-         */
-        init += resolu - 1;
-      }
-      break;
-    }
-    case CU_NURBS: {
-      if (nu->pntsv == 1) {
-
-        int nurb_points;
-        if (nu->flagu & CU_NURB_CYCLIC) {
-          resolu++;
-          nurb_points = nu->pntsu * resolu;
-        }
-        else {
-          nurb_points = (nu->pntsu - 1) * resolu;
-        }
-        /* Get all curve points. */
-        coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
-        BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3]));
-
-        /* Allocate memory for storage points. */
-        gps->totpoints = nurb_points - 1;
-        gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
-
-        /* Add points. */
-        gpencil_add_new_points(gps, coord_array, 1.0f, 0, gps->totpoints, init_co, false);
-
-        MEM_SAFE_FREE(coord_array);
-      }
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-  /* Cyclic curve, close stroke. */
-  if ((cyclic) && (!do_stroke)) {
-    BKE_gpencil_stroke_close(gps);
-  }
-
-  /* Recalc fill geometry. */
-  BKE_gpencil_stroke_geometry_update(gps);
-}
-
-/* Convert a curve object to grease pencil stroke.
- *
- * \param bmain: Main thread pointer
- * \param scene: Original scene.
- * \param ob_gp: Grease pencil object to add strokes.
- * \param ob_cu: Curve to convert.
- * \param gpencil_lines: Use lines for strokes.
- * \param use_collections: Create layers using collection names.
- * \param only_stroke: The material must be only stroke without fill.
- */
-void BKE_gpencil_convert_curve(Main *bmain,
-                               Scene *scene,
-                               Object *ob_gp,
-                               Object *ob_cu,
-                               const bool gpencil_lines,
-                               const bool use_collections,
-                               const bool only_stroke)
-{
-  if (ELEM(NULL, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
-    return;
-  }
-
-  Curve *cu = (Curve *)ob_cu->data;
-  bGPdata *gpd = (bGPdata *)ob_gp->data;
-  bGPDlayer *gpl = NULL;
-
-  /* If the curve is empty, cancel. */
-  if (cu->nurb.first == NULL) {
-    return;
-  }
-
-  /* Check if there is an active layer. */
-  if (use_collections) {
-    Collection *collection = gpencil_get_parent_collection(scene, ob_cu);
-    if (collection != NULL) {
-      gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
-      if (gpl == NULL) {
-        gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
-      }
-    }
-  }
-
-  if (gpl == NULL) {
-    gpl = BKE_gpencil_layer_active_get(gpd);
-    if (gpl == NULL) {
-      gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
-    }
-  }
-
-  /* Check if there is an active frame and add if needed. */
-  bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_COPY);
-
-  /* Read all splines of the curve and create a stroke for each. */
-  for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
-    gpencil_convert_spline(bmain, ob_gp, ob_cu, gpencil_lines, only_stroke, gpf, nu);
-  }
-
-  /* Tag for recalculation */
-  DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
-}
-
 /* Create a default palette */
 void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
 {
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
new file mode 100644 (file)
index 0000000..92054c5
--- /dev/null
@@ -0,0 +1,2022 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math_vector.h"
+#include "BLI_polyfill_2d.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_collection.h"
+#include "BKE_curve.h"
+#include "BKE_deform.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+
+#include "DEG_depsgraph_query.h"
+
+static CLG_LogRef LOG = {"bke_geom.gpencil"};
+
+/* GP Object - Boundbox Support */
+/**
+ * Get min/max coordinate bounds for single stroke
+ * \return Returns whether we found any selected points
+ */
+bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
+                               const bool use_select,
+                               float r_min[3],
+                               float r_max[3])
+{
+  const bGPDspoint *pt;
+  int i;
+  bool changed = false;
+
+  if (ELEM(NULL, gps, r_min, r_max)) {
+    return false;
+  }
+
+  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+    if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {
+      minmax_v3v3_v3(r_min, r_max, &pt->x);
+      changed = true;
+    }
+  }
+  return changed;
+}
+
+/* get min/max bounds of all strokes in GP datablock */
+bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
+{
+  bool changed = false;
+
+  INIT_MINMAX(r_min, r_max);
+
+  if (gpd == NULL) {
+    return changed;
+  }
+
+  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+    bGPDframe *gpf = gpl->actframe;
+
+    if (gpf != NULL) {
+      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+        changed = BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
+      }
+    }
+  }
+
+  return changed;
+}
+
+/* compute center of bounding box */
+void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
+{
+  float min[3], max[3], tot[3];
+
+  BKE_gpencil_data_minmax(gpd, min, max);
+
+  add_v3_v3v3(tot, min, max);
+  mul_v3_v3fl(r_centroid, tot, 0.5f);
+}
+
+/* Compute stroke bounding box. */
+void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
+{
+  INIT_MINMAX(gps->boundbox_min, gps->boundbox_max);
+  BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
+}
+
+/* create bounding box values */
+static void boundbox_gpencil(Object *ob)
+{
+  BoundBox *bb;
+  bGPdata *gpd;
+  float min[3], max[3];
+
+  if (ob->runtime.bb == NULL) {
+    ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
+  }
+
+  bb = ob->runtime.bb;
+  gpd = ob->data;
+
+  if (!BKE_gpencil_data_minmax(gpd, min, max)) {
+    min[0] = min[1] = min[2] = -1.0f;
+    max[0] = max[1] = max[2] = 1.0f;
+  }
+
+  BKE_boundbox_init_from_minmax(bb, min, max);
+
+  bb->flag &= ~BOUNDBOX_DIRTY;
+}
+
+/* get bounding box */
+BoundBox *BKE_gpencil_boundbox_get(Object *ob)
+{
+  if (ELEM(NULL, ob, ob->data)) {
+    return NULL;
+  }
+
+  bGPdata *gpd = (bGPdata *)ob->data;
+  if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) {
+    return ob->runtime.bb;
+  }
+
+  boundbox_gpencil(ob);
+
+  return ob->runtime.bb;
+}
+
+/* ************************************************** */
+
+static int stroke_march_next_point(const bGPDstroke *gps,
+                                   const int index_next_pt,
+                                   const float *current,
+                                   const float dist,
+                                   float *result,
+                                   float *pressure,
+                                   float *strength,
+                                   float *vert_color,
+                                   float *ratio_result,
+                                   int *index_from,
+                                   int *index_to)
+{
+  float remaining_till_next = 0.0f;
+  float remaining_march = dist;
+  float step_start[3];
+  float point[3];
+  int next_point_index = index_next_pt;
+  bGPDspoint *pt = NULL;
+
+  if (!(next_point_index < gps->totpoints)) {
+    return -1;
+  }
+
+  copy_v3_v3(step_start, current);
+  pt = &gps->points[next_point_index];
+  copy_v3_v3(point, &pt->x);
+  remaining_till_next = len_v3v3(point, step_start);
+
+  while (remaining_till_next < remaining_march) {
+    remaining_march -= remaining_till_next;
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(point, &pt->x);
+    copy_v3_v3(step_start, point);
+    next_point_index++;
+    if (!(next_point_index < gps->totpoints)) {
+      next_point_index = gps->totpoints - 1;
+      break;
+    }
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(point, &pt->x);
+    remaining_till_next = len_v3v3(point, step_start);
+  }
+  if (remaining_till_next < remaining_march) {
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(result, &pt->x);
+    *pressure = gps->points[next_point_index].pressure;
+    *strength = gps->points[next_point_index].strength;
+    memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float) * 4);
+
+    *index_from = next_point_index - 1;
+    *index_to = next_point_index;
+    *ratio_result = 1.0f;
+
+    return 0;
+  }
+  else {
+    float ratio = remaining_march / remaining_till_next;
+    interp_v3_v3v3(result, step_start, point, ratio);
+    *pressure = interpf(
+        gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
+    *strength = interpf(
+        gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
+    interp_v4_v4v4(vert_color,
+                   gps->points[next_point_index - 1].vert_color,
+                   gps->points[next_point_index].vert_color,
+                   ratio);
+
+    *index_from = next_point_index - 1;
+    *index_to = next_point_index;
+    *ratio_result = ratio;
+
+    return next_point_index;
+  }
+}
+
+static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
+                                             const int index_next_pt,
+                                             const float *current,
+                                             const float dist,
+                                             float *result)
+{
+  float remaining_till_next = 0.0f;
+  float remaining_march = dist;
+  float step_start[3];
+  float point[3];
+  int next_point_index = index_next_pt;
+  bGPDspoint *pt = NULL;
+
+  if (!(next_point_index < gps->totpoints)) {
+    return -1;
+  }
+
+  copy_v3_v3(step_start, current);
+  pt = &gps->points[next_point_index];
+  copy_v3_v3(point, &pt->x);
+  remaining_till_next = len_v3v3(point, step_start);
+
+  while (remaining_till_next < remaining_march) {
+    remaining_march -= remaining_till_next;
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(point, &pt->x);
+    copy_v3_v3(step_start, point);
+    next_point_index++;
+    if (!(next_point_index < gps->totpoints)) {
+      next_point_index = gps->totpoints - 1;
+      break;
+    }
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(point, &pt->x);
+    remaining_till_next = len_v3v3(point, step_start);
+  }
+  if (remaining_till_next < remaining_march) {
+    pt = &gps->points[next_point_index];
+    copy_v3_v3(result, &pt->x);
+    return 0;
+  }
+  else {
+    float ratio = remaining_march / remaining_till_next;
+    interp_v3_v3v3(result, step_start, point, ratio);
+    return next_point_index;
+  }
+}
+
+static int stroke_march_count(const bGPDstroke *gps, const float dist)
+{
+  int point_count = 0;
+  float point[3];
+  int next_point_index = 1;
+  bGPDspoint *pt = NULL;
+
+  pt = &gps->points[0];
+  copy_v3_v3(point, &pt->x);
+  point_count++;
+
+  while ((next_point_index = stroke_march_next_point_no_interp(
+              gps, next_point_index, point, dist, point)) > -1) {
+    point_count++;
+    if (next_point_index == 0) {
+      break; /* last point finished */
+    }
+  }
+  return point_count;
+}
+
+static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
+                                          int count,
+                                          ListBase *result,
+                                          int *totweight)
+{
+  LinkData *ld;
+  MDeformVert *dv;
+  MDeformWeight *dw;
+  int i, j;
+  int tw = 0;
+  for (i = 0; i < count; i++) {
+    dv = &dv_list[i];
+
+    /* find def_nr in list, if not exist, then create one */
+    for (j = 0; j < dv->totweight; j++) {
+      bool found = false;
+      dw = &dv->dw[j];
+      for (ld = result->first; ld; ld = ld->next) {
+        if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
+        ld->data = POINTER_FROM_INT(dw->def_nr);
+        BLI_addtail(result, ld);
+        tw++;
+      }
+    }
+  }
+
+  *totweight = tw;
+}
+
+static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
+{
+  int i, j;
+  LinkData *ld;
+  MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
+
+  for (i = 0; i < count; i++) {
+    dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
+    dst[i].totweight = totweight;
+    j = 0;
+    /* re-assign deform groups */
+    for (ld = def_nr_list->first; ld; ld = ld->next) {
+      dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
+      j++;
+    }
+  }
+
+  return dst;
+}
+
+static void stroke_interpolate_deform_weights(
+    bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
+{
+  const MDeformVert *vl = &gps->dvert[index_from];
+  const MDeformVert *vr = &gps->dvert[index_to];
+  int i;
+
+  for (i = 0; i < vert->totweight; i++) {
+    float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
+    float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
+    vert->dw[i].weight = interpf(wr, wl, ratio);
+  }
+}
+
+/**
+ * Resample a stroke
+ * \param gps: Stroke to sample
+ * \param dist: Distance of one segment
+ */
+bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select)
+{
+  bGPDspoint *pt = gps->points;
+  bGPDspoint *pt1 = NULL;
+  bGPDspoint *pt2 = NULL;
+  int i;
+  LinkData *ld;
+  ListBase def_nr_list = {0};
+
+  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+    return false;
+  }
+  /* TODO: Implement feature point preservation. */
+  int count = stroke_march_count(gps, dist);
+
+  bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
+  MDeformVert *new_dv = NULL;
+
+  int result_totweight;
+
+  if (gps->dvert != NULL) {
+    stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
+    new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
+  }
+
+  int next_point_index = 1;
+  i = 0;
+  float pressure, strength, ratio_result;
+  float vert_color[4];
+  int index_from, index_to;
+  float last_coord[3];
+
+  /*  1st point is always at the start */
+  pt1 = &gps->points[0];
+  copy_v3_v3(last_coord, &pt1->x);
+  pt2 = &new_pt[i];
+  copy_v3_v3(&pt2->x, last_coord);
+  new_pt[i].pressure = pt[0].pressure;
+  new_pt[i].strength = pt[0].strength;
+  memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float) * 4);
+  if (select) {
+    new_pt[i].flag |= GP_SPOINT_SELECT;
+  }
+  i++;
+
+  if (new_dv) {
+    stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
+  }
+
+  /*  the rest */
+  while ((next_point_index = stroke_march_next_point(gps,
+                                                     next_point_index,
+                                                     last_coord,
+                                                     dist,
+                                                     last_coord,
+                                                     &pressure,
+                                                     &strength,
+                                                     vert_color,
+                                                     &ratio_result,
+                                                     &index_from,
+                                                     &index_to)) > -1) {
+    pt2 = &new_pt[i];
+    copy_v3_v3(&pt2->x, last_coord);
+    new_pt[i].pressure = pressure;
+    new_pt[i].strength = strength;
+    memcpy(new_pt[i].vert_color, vert_color, sizeof(float) * 4);
+    if (select) {
+      new_pt[i].flag |= GP_SPOINT_SELECT;
+    }
+
+    if (new_dv) {
+      stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
+    }
+
+    i++;
+    if (next_point_index == 0) {
+      break; /* last point finished */
+    }
+  }
+
+  gps->points = new_pt;
+  /* Free original vertex list. */
+  MEM_freeN(pt);
+
+  if (new_dv) {
+    /* Free original weight data. */
+    BKE_gpencil_free_stroke_weights(gps);
+    MEM_freeN(gps->dvert);
+    while ((ld = BLI_pophead(&def_nr_list))) {
+      MEM_freeN(ld);
+    }
+
+    gps->dvert = new_dv;
+  }
+
+  gps->totpoints = i;
+
+  /* Calc geometry data. */
+  BKE_gpencil_stroke_geometry_update(gps);
+
+  return true;
+}
+
+/**
+ * Backbone stretch similar to Freestyle.
+ * \param gps: Stroke to sample
+ * \param dist: Distance of one segment
+ * \param tip_length: Ignore tip jittering, set zero to use default value.
+ */
+bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+{
+  bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
+  int i;
+  float threshold = (tip_length == 0 ? 0.001f : tip_length);
+
+  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+    return false;
+  }
+
+  last_pt = &pt[gps->totpoints - 1];
+  second_last = &pt[gps->totpoints - 2];
+  next_pt = &pt[1];
+
+  float len1 = 0.0f;
+  float len2 = 0.0f;
+
+  i = 1;
+  while (len1 < threshold && gps->totpoints > i) {
+    next_pt = &pt[i];
+    len1 = len_v3v3(&next_pt->x, &pt->x);
+    i++;
+  }
+
+  i = 2;
+  while (len2 < threshold && gps->totpoints >= i) {
+    second_last = &pt[gps->totpoints - i];
+    len2 = len_v3v3(&last_pt->x, &second_last->x);
+    i++;
+  }
+
+  float extend1 = (len1 + dist) / len1;
+  float extend2 = (len2 + dist) / len2;
+
+  float result1[3], result2[3];
+
+  interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+  interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+
+  copy_v3_v3(&pt->x, result1);
+  copy_v3_v3(&last_pt->x, result2);
+
+  return true;
+}
+
+/**
+ * Trim stroke to needed segments
+ * \param gps: Target stroke
+ * \param index_from: the index of the first point to be used in the trimmed result
+ * \param index_to: the index of the last point to be used in the trimmed result
+ */
+bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
+{
+  bGPDspoint *pt = gps->points, *new_pt;
+  MDeformVert *dv, *new_dv;
+
+  const int new_count = index_to - index_from + 1;
+
+  if (new_count >= gps->totpoints) {
+    return false;
+  }
+
+  if (new_count == 1) {
+    BKE_gpencil_free_stroke_weights(gps);
+    MEM_freeN(gps->points);
+    gps->points = NULL;
+    gps->dvert = NULL;
+    gps->totpoints = 0;
+    return false;
+  }
+
+  new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
+
+  for (int i = 0; i < new_count; i++) {
+    memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
+  }
+
+  if (gps->dvert) {
+    new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
+    for (int i = 0; i < new_count; i++) {
+      dv = &gps->dvert[i + index_from];
+      new_dv[i].flag = dv->flag;
+      new_dv[i].totweight = dv->totweight;
+      new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+                                 "gp_stroke_dverts_dw_trimmed");
+      for (int j = 0; j < dv->totweight; j++) {
+        new_dv[i].dw[j].weight = dv->dw[j].weight;
+        new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
+      }
+    }
+    MEM_freeN(gps->dvert);
+    gps->dvert = new_dv;
+  }
+
+  MEM_freeN(gps->points);
+  gps->points = new_pt;
+  gps->totpoints = new_count;
+
+  return true;
+}
+
+bool BKE_gpencil_stroke_split(bGPDframe *gpf,
+                              bGPDstroke *gps,
+                              const int before_index,
+                              bGPDstroke **remaining_gps)
+{
+  bGPDstroke *new_gps;
+  bGPDspoint *pt = gps->points, *new_pt;
+  MDeformVert *dv, *new_dv;
+
+  if (before_index >= gps->totpoints || before_index == 0) {
+    return false;
+  }
+
+  const int new_count = gps->totpoints - before_index;
+  const int old_count = before_index;
+
+  /* Handle remaining segments first. */
+
+  new_gps = BKE_gpencil_stroke_add_existing_style(
+      gpf, gps, gps->mat_nr, new_count, gps->thickness);
+
+  new_pt = new_gps->points; /* Allocated from above. */
+
+  for (int i = 0; i < new_count; i++) {
+    memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
+  }
+
+  if (gps->dvert) {
+    new_dv = MEM_callocN(sizeof(MDeformVert) * new_count,
+                         "gp_stroke_dverts_remaining(MDeformVert)");
+    for (int i = 0; i < new_count; i++) {
+      dv = &gps->dvert[i + before_index];
+      new_dv[i].flag = dv->flag;
+      new_dv[i].totweight = dv->totweight;
+      new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+                                 "gp_stroke_dverts_dw_remaining(MDeformWeight)");
+      for (int j = 0; j < dv->totweight; j++) {
+        new_dv[i].dw[j].weight = dv->dw[j].weight;
+        new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
+      }
+    }
+    new_gps->dvert = new_dv;
+  }
+
+  (*remaining_gps) = new_gps;
+
+  /* Trim the original stroke into a shorter one.
+   * Keep the end point. */
+
+  BKE_gpencil_stroke_trim_points(gps, 0, old_count);
+  BKE_gpencil_stroke_geometry_update(gps);
+  return true;
+}
+
+/**
+ * Shrink the stroke by length.
+ * \param gps: Stroke to shrink
+ * \param dist: delta length
+ */
+bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
+{
+  bGPDspoint *pt = gps->points, *second_last;
+  int i;
+
+  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+    return false;
+  }
+
+  second_last = &pt[gps->totpoints - 2];
+
+  float len1, this_len1, cut_len1;
+  float len2, this_len2, cut_len2;
+  int index_start, index_end;
+
+  len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
+
+  i = 1;
+  while (len1 < dist && gps->totpoints > i - 1) {
+    this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
+    len1 += this_len1;
+    cut_len1 = len1 - dist;
+    i++;
+  }
+  index_start = i - 2;
+
+  i = 2;
+  while (len2 < dist && gps->totpoints >= i) {
+    second_last = &pt[gps->totpoints - i];
+    this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
+    len2 += this_len2;
+    cut_len2 = len2 - dist;
+    i++;
+  }
+  index_end = gps->totpoints - i + 2;
+
+  if (len1 < dist || len2 < dist || index_end <= index_start) {
+    index_start = index_end = 0; /* empty stroke */
+  }
+
+  if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+    index_start = index_end = 0; /* no length left to cut */
+  }
+
+  BKE_gpencil_stroke_trim_points(gps, index_start, index_end);
+
+  if (gps->totpoints == 0) {
+    return false;
+  }
+
+  pt = gps->points;
+
+  float cut1 = cut_len1 / this_len1;
+  float cut2 = cut_len2 / this_len2;
+
+  float result1[3], result2[3];
+
+  interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
+  interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
+
+  copy_v3_v3(&pt[0].x, result1);
+  copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
+
+  return true;
+}
+
+/**
+ * Apply smooth to stroke point
+ * \param gps: Stroke to smooth
+ * \param i: Point index
+ * \param inf: Amount of smoothing to apply
+ */
+bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
+{
+  bGPDspoint *pt = &gps->points[i];
+  float sco[3] = {0.0f};
+
+  /* Do nothing if not enough points to smooth out */
+  if (gps->totpoints <= 2) {
+    return false;
+  }
+
+  /* Only affect endpoints by a fraction of the normal strength,
+   * to prevent the stroke from shrinking too much
+   */
+  if ((i == 0) || (i == gps->totpoints - 1)) {
+    inf *= 0.1f;
+  }
+
+  /* Compute smoothed coordinate by taking the ones nearby */
+  /* XXX: This is potentially slow,
+   *      and suffers from accumulation error as earlier points are handled before later ones. */
+  {
+    /* XXX: this is hardcoded to look at 2 points on either side of the current one
+     * (i.e. 5 items total). */
+    const int steps = 2;
+    const float average_fac = 1.0f / (float)(steps * 2 + 1);
+    int step;
+
+    /* add the point itself */
+    madd_v3_v3fl(sco, &pt->x, average_fac);
+
+    /* n-steps before/after current point */
+    /* XXX: review how the endpoints are treated by this algorithm. */
+    /* XXX: falloff measures should also introduce some weighting variations,
+     *      so that further-out points get less weight. */
+    for (step = 1; step <= steps; step++) {
+      bGPDspoint *pt1, *pt2;
+      int before = i - step;
+      int after = i + step;
+
+      CLAMP_MIN(before, 0);
+      CLAMP_MAX(after, gps->totpoints - 1);
+
+      pt1 = &gps->points[before];
+      pt2 = &gps->points[after];
+
+      /* add both these points to the average-sum (s += p[i]/n) */
+      madd_v3_v3fl(sco, &pt1->x, average_fac);
+      madd_v3_v3fl(sco, &pt2->x, average_fac);
+    }
+  }
+
+  /* Based on influence factor, blend between original and optimal smoothed coordinate */
+  interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
+
+  return true;
+}
+
+/**
+ * Apply smooth for strength to stroke point */
+bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
+{
+  bGPDspoint *ptb = &gps->points[point_index];
+
+  /* Do nothing if not enough points */
+  if ((gps->totpoints <= 2) || (point_index < 1)) {
+    return false;
+  }
+  /* Only affect endpoints by a fraction of the normal influence */
+  float inf = influence;
+  if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
+    inf *= 0.01f;
+  }
+  /* Limit max influence to reduce pop effect. */
+  CLAMP_MAX(inf, 0.98f);
+
+  float total = 0.0f;
+  float max_strength = 0.0f;
+  const int steps = 4;
+  const float average_fac = 1.0f / (float)(steps * 2 + 1);
+  int step;
+
+  /* add the point itself */
+  total += ptb->strength * average_fac;
+  max_strength = ptb->strength;
+
+  /* n-steps before/after current point */
+  for (step = 1; step <= steps; step++) {
+    bGPDspoint *pt1, *pt2;
+    int before = point_index - step;
+    int after = point_index + step;
+
+    CLAMP_MIN(before, 0);
+    CLAMP_MAX(after, gps->totpoints - 1);
+
+    pt1 = &gps->points[before];
+    pt2 = &gps->points[after];
+
+    /* add both these points to the average-sum (s += p[i]/n) */
+    total += pt1->strength * average_fac;
+    total += pt2->strength * average_fac;
+    /* Save max value. */
+    if (max_strength < pt1->strength) {
+      max_strength = pt1->strength;
+    }
+    if (max_strength < pt2->strength) {
+      max_strength = pt2->strength;
+    }
+  }
+
+  /* Based on influence factor, blend between original and optimal smoothed value. */
+  ptb->strength = interpf(ptb->strength, total, inf);
+  /* Clamp to maximum stroke strength to avoid weird results. */
+  CLAMP_MAX(ptb->strength, max_strength);
+
+  return true;
+}
+
+/**
+ * Apply smooth for thickness to stroke point (use pressure) */
+bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
+{
+  bGPDspoint *ptb = &gps->points[point_index];
+
+  /* Do nothing if not enough points */
+  if ((gps->totpoints <= 2) || (point_index < 1)) {
+    return false;
+  }
+  /* Only affect endpoints by a fraction of the normal influence */
+  float inf = influence;
+  if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
+    inf *= 0.01f;
+  }
+  /* Limit max influence to reduce pop effect. */
+  CLAMP_MAX(inf, 0.98f);
+
+  float total = 0.0f;
+  float max_pressure = 0.0f;
+  const int steps = 4;
+  const float average_fac = 1.0f / (float)(steps * 2 + 1);
+  int step;
+
+  /* add the point itself */
+  total += ptb->pressure * average_fac;
+  max_pressure = ptb->pressure;
+
+  /* n-steps before/after current point */
+  for (step = 1; step <= steps; step++) {
+    bGPDspoint *pt1, *pt2;
+    int before = point_index - step;
+    int after = point_index + step;
+
+    CLAMP_MIN(before, 0);
+    CLAMP_MAX(after, gps->totpoints - 1);
+
+    pt1 = &gps->points[before];
+    pt2 = &gps->points[after];
+
+    /* add both these points to the average-sum (s += p[i]/n) */
+    total += pt1->pressure * average_fac;
+    total += pt2->pressure * average_fac;
+    /* Save max value. */
+    if (max_pressure < pt1->pressure) {
+      max_pressure = pt1->pressure;
+    }
+    if (max_pressure < pt2->pressure) {
+      max_pressure = pt2->pressure;
+    }
+  }
+
+  /* Based on influence factor, blend between original and optimal smoothed value. */
+  ptb->pressure = interpf(ptb->pressure, total, inf);
+  /* Clamp to maximum stroke thickness to avoid weird results. */
+  CLAMP_MAX(ptb->pressure, max_pressure);
+  return true;
+}
+
+/**
+ * Apply smooth for UV rotation to stroke point (use pressure).
+ */
+bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
+{
+  bGPDspoint *ptb = &gps->points[point_index];
+
+  /* Do nothing if not enough points */
+  if (gps->totpoints <= 2) {
+    return false;
+  }
+
+  /* Compute theoretical optimal value */
+  bGPDspoint *pta, *ptc;
+  int before = point_index - 1;
+  int after = point_index + 1;
+
+  CLAMP_MIN(before, 0);
+  CLAMP_MAX(after, gps->totpoints - 1);
+
+  pta = &gps->points[before];
+  ptc = &gps->points[after];
+
+  /* the optimal value is the corresponding to the interpolation of the pressure
+   * at the distance of point b
+   */
+  float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
+  /* sometimes the factor can be wrong due stroke geometry, so use middle point */
+  if ((fac < 0.0f) || (fac > 1.0f)) {
+    fac = 0.5f;
+  }
+  float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
+
+  /* Based on influence factor, blend between original and optimal */
+  ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
+  CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
+
+  return true;
+}
+/* Get points of stroke always flat to view not affected by camera view or view position */
+void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
+                                int totpoints,
+                                float (*points2d)[2],
+                                int *r_direction)
+{
+  BLI_assert(totpoints >= 2);
+
+  const bGPDspoint *pt0 = &points[0];
+  const bGPDspoint *pt1 = &points[1];
+  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
+
+  float locx[3];
+  float locy[3];
+  float loc3[3];
+  float normal[3];
+
+  /* local X axis (p0 -> p1) */
+  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
+
+  /* point vector at 3/4 */
+  float v3[3];
+  if (totpoints == 2) {
+    mul_v3_v3fl(v3, &pt3->x, 0.001f);
+  }
+  else {
+    copy_v3_v3(v3, &pt3->x);
+  }
+
+  sub_v3_v3v3(loc3, v3, &pt0->x);
+
+  /* vector orthogonal to polygon plane */
+  cross_v3_v3v3(normal, locx, loc3);
+
+  /* local Y axis (cross to normal/x axis) */
+  cross_v3_v3v3(locy, normal, locx);
+
+  /* Normalize vectors */
+  normalize_v3(locx);
+  normalize_v3(locy);
+
+  /* Get all points in local space */
+  for (int i = 0; i < totpoints; i++) {
+    const bGPDspoint *pt = &points[i];
+    float loc[3];
+
+    /* Get local space using first point as origin */
+    sub_v3_v3v3(loc, &pt->x, &pt0->x);
+
+    points2d[i][0] = dot_v3v3(loc, locx);
+    points2d[i][1] = dot_v3v3(loc, locy);
+  }
+
+  /* Concave (-1), Convex (1), or Autodetect (0)? */
+  *r_direction = (int)locy[2];
+}
+
+/* Get points of stroke always flat to view not affected by camera view or view position
+ * using another stroke as reference
+ */
+void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
+                                    int ref_totpoints,
+                                    const bGPDspoint *points,
+                                    int totpoints,
+                                    float (*points2d)[2],
+                                    const float scale,
+                                    int *r_direction)
+{
+  BLI_assert(totpoints >= 2);
+
+  const bGPDspoint *pt0 = &ref_points[0];
+  const bGPDspoint *pt1 = &ref_points[1];
+  const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
+
+  float locx[3];
+  float locy[3];
+  float loc3[3];
+  float normal[3];
+
+  /* local X axis (p0 -> p1) */
+  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
+
+  /* point vector at 3/4 */
+  float v3[3];
+  if (totpoints == 2) {
+    mul_v3_v3fl(v3, &pt3->x, 0.001f);
+  }
+  else {
+    copy_v3_v3(v3, &pt3->x);
+  }
+
+  sub_v3_v3v3(loc3, v3, &pt0->x);
+
+  /* vector orthogonal to polygon plane */
+  cross_v3_v3v3(normal, locx, loc3);
+
+  /* local Y axis (cross to normal/x axis) */
+  cross_v3_v3v3(locy, normal, locx);
+
+  /* Normalize vectors */
+  normalize_v3(locx);
+  normalize_v3(locy);
+
+  /* Get all points in local space */
+  for (int i = 0; i < totpoints; i++) {
+    const bGPDspoint *pt = &points[i];
+    float loc[3];
+    float v1[3];
+    float vn[3] = {0.0f, 0.0f, 0.0f};
+
+    /* apply scale to extremes of the stroke to get better collision detection
+     * the scale is divided to get more control in the UI parameter
+     */
+    /* first point */
+    if (i == 0) {
+      const bGPDspoint *pt_next = &points[i + 1];
+      sub_v3_v3v3(vn, &pt->x, &pt_next->x);
+      normalize_v3(vn);
+      mul_v3_fl(vn, scale / 10.0f);
+      add_v3_v3v3(v1, &pt->x, vn);
+    }
+    /* last point */
+    else if (i == totpoints - 1) {
+      const bGPDspoint *pt_prev = &points[i - 1];
+      sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
+      normalize_v3(vn);
+      mul_v3_fl(vn, scale / 10.0f);
+      add_v3_v3v3(v1, &pt->x, vn);
+    }
+    else {
+      copy_v3_v3(v1, &pt->x);
+    }
+
+    /* Get local space using first point as origin (ref stroke) */
+    sub_v3_v3v3(loc, v1, &pt0->x);
+
+    points2d[i][0] = dot_v3v3(loc, locx);
+    points2d[i][1] = dot_v3v3(loc, locy);
+  }
+
+  /* Concave (-1), Convex (1), or Autodetect (0)? */
+  *r_direction = (int)locy[2];
+}
+
+/* calc texture coordinates using flat projected points */
+static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
+                                        bGPDstroke *gps,
+                                        const float minv[2],
+                                        float maxv[2],
+                                        float (*r_uv)[2])
+{
+  const float s = sin(gps->uv_rotation);
+  const float c = cos(gps->uv_rotation);
+
+  /* Calc center for rotation. */
+  float center[2] = {0.5f, 0.5f};
+  float d[2];
+  d[0] = maxv[0] - minv[0];
+  d[1] = maxv[1] - minv[1];
+  for (int i = 0; i < gps->totpoints; i++) {
+    r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
+    r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
+
+    /* Apply translation. */
+    add_v2_v2(r_uv[i], gps->uv_translation);
+
+    /* Apply Rotation. */
+    r_uv[i][0] -= center[0];
+    r_uv[i][1] -= center[1];
+
+    float x = r_uv[i][0] * c - r_uv[i][1] * s;
+    float y = r_uv[i][0] * s + r_uv[i][1] * c;
+
+    r_uv[i][0] = x + center[0];
+    r_uv[i][1] = y + center[1];
+
+    /* Apply scale. */
+    if (gps->uv_scale != 0.0f) {
+      mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale);
+    }
+  }
+}
+
+/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was
+ * modified) */
+void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
+{
+  BLI_assert(gps->totpoints >= 3);
+
+  /* allocate memory for temporary areas */
+  gps->tot_triangles = gps->totpoints - 2;
+  uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
+                                        "GP Stroke temp triangulation");
+  float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
+                                    "GP Stroke temp 2d points");
+  float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
+
+  int direction = 0;
+
+  /* convert to 2d and triangulate */
+  BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
+  BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
+
+  /* calc texture coordinates automatically */
+  float minv[2];
+  float maxv[2];
+  /* first needs bounding box data */
+  ARRAY_SET_ITEMS(minv, -1.0f, -1.0f);
+  ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f);
+
+  /* calc uv data */
+  gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv);
+
+  /* Save triangulation data. */
+  if (gps->tot_triangles > 0) {
+    MEM_SAFE_FREE(gps->triangles);
+    gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
+                                 "GP Stroke triangulation");
+
+    for (int i = 0; i < gps->tot_triangles; i++) {
+      memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
+    }
+
+    /* Copy UVs to bGPDspoint. */
+    for (int i = 0; i < gps->totpoints; i++) {
+      copy_v2_v2(gps->points[i].uv_fill, uv[i]);
+    }
+  }
+  else {
+    /* No triangles needed - Free anything allocated previously */
+    if (gps->triangles) {
+      MEM_freeN(gps->triangles);
+    }
+
+    gps->triangles = NULL;
+  }
+
+  /* clear memory */
+  MEM_SAFE_FREE(tmp_triangles);
+  MEM_SAFE_FREE(points2d);
+  MEM_SAFE_FREE(uv);
+}
+
+/* texture coordinate utilities */
+void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
+{
+  if (gps == NULL || gps->totpoints == 0) {
+    return;
+  }
+
+  bGPDspoint *pt = gps->points;
+  float totlen = 0.0f;
+  pt[0].uv_fac = totlen;
+  for (int i = 1; i < gps->totpoints; i++) {
+    totlen += len_v3v3(&pt[i - 1].x, &pt[i].x);
+    pt[i].uv_fac = totlen;
+  }
+}
+
+/* Recalc the internal geometry caches for fill and uvs. */
+void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
+{
+  if (gps == NULL) {
+    return;
+  }
+
+  if (gps->totpoints > 2) {
+    BKE_gpencil_stroke_fill_triangulate(gps);
+  }
+  else {
+    gps->tot_triangles = 0;
+    MEM_SAFE_FREE(gps->triangles);
+  }
+
+  /* calc uv data along the stroke */
+  BKE_gpencil_stroke_uv_update(gps);
+
+  /* Calc stroke bounding box. */
+  BKE_gpencil_stroke_boundingbox_calc(gps);
+}
+
+float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
+{
+  if (!gps->points || gps->totpoints < 2) {
+    return 0.0f;
+  }
+  float *last_pt = &gps->points[0].x;
+  int i;
+  bGPDspoint *pt;
+  float total_length = 0.0f;
+  for (i = 1; i < gps->totpoints; i++) {
+    pt = &gps->points[i];
+    if (use_3d) {
+      total_length += len_v3v3(&pt->x, last_pt);
+    }
+    else {
+      total_length += len_v2v2(&pt->x, last_pt);
+    }
+    last_pt = &pt->x;
+  }
+  return total_length;
+}
+
+/**
+ * Trim stroke to the first intersection or loop
+ * \param gps: Stroke data
+ */
+bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
+{
+  if (gps->totpoints < 4) {
+    return false;
+  }
+  bool intersect = false;
+  int start, end;
+  float point[3];
+  /* loop segments from start until we have an intersection */
+  for (int i = 0; i < gps->totpoints - 2; i++) {
+    start = i;
+    bGPDspoint *a = &gps->points[start];
+    bGPDspoint *b = &gps->points[start + 1];
+    for (int j = start + 2; j < gps->totpoints - 1; j++) {
+      end = j + 1;
+      bGPDspoint *c = &gps->points[j];
+      bGPDspoint *d = &gps->points[end];
+      float pointb[3];
+      /* get intersection */
+      if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
+        if (len_v3(point) > 0.0f) {
+          float closest[3];
+          /* check intersection is on both lines */
+          float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
+          if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
+            continue;
+          }
+          lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
+          if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
+            continue;
+          }
+          else {
+            intersect = true;
+            break;
+          }
+        }
+      }
+    }
+    if (intersect) {
+      break;
+    }
+  }
+
+  /* trim unwanted points */
+  if (intersect) {
+
+    /* save points */
+    bGPDspoint *old_points = MEM_dupallocN(gps->points);
+    MDeformVert *old_dvert = NULL;
+    MDeformVert *dvert_src = NULL;
+
+    if (gps->dvert != NULL) {
+      old_dvert = MEM_dupallocN(gps->dvert);
+    }
+
+    /* resize gps */
+    int newtot = end - start + 1;
+
+    gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
+    if (gps->dvert != NULL) {
+      gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
+    }
+
+    for (int i = 0; i < newtot; i++) {
+      int idx = start + i;
+      bGPDspoint *pt_src = &old_points[idx];
+      bGPDspoint *pt_new = &gps->points[i];
+      memcpy(pt_new, pt_src, sizeof(bGPDspoint));
+      if (gps->dvert != NULL) {
+        dvert_src = &old_dvert[idx];
+        MDeformVert *dvert = &gps->dvert[i];
+        memcpy(dvert, dvert_src, sizeof(MDeformVert));
+        if (dvert_src->dw) {
+          memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
+        }
+      }
+      if (idx == start || idx == end) {
+        copy_v3_v3(&pt_new->x, point);
+      }
+    }
+
+    gps->totpoints = newtot;
+
+    MEM_SAFE_FREE(old_points);
+    MEM_SAFE_FREE(old_dvert);
+  }
+
+  BKE_gpencil_stroke_geometry_update(gps);
+
+  return intersect;
+}
+
+/**
+ * Close stroke
+ * \param gps: Stroke to close
+ */
+bool BKE_gpencil_stroke_close(bGPDstroke *gps)
+{
+  bGPDspoint *pt1 = NULL;
+  bGPDspoint *pt2 = NULL;
+
+  /* Only can close a stroke with 3 points or more. */
+  if (gps->totpoints < 3) {
+    return false;
+  }
+
+  /* Calc average distance between points to get same level of sampling. */
+  float dist_tot = 0.0f;
+  for (int i = 0; i < gps->totpoints - 1; i++) {
+    pt1 = &gps->points[i];
+    pt2 = &gps->points[i + 1];
+    dist_tot += len_v3v3(&pt1->x, &pt2->x);
+  }
+  /* Calc the average distance. */
+  float dist_avg = dist_tot / (gps->totpoints - 1);
+
+  /* Calc distance between last and first point. */
+  pt1 = &gps->points[gps->totpoints - 1];
+  pt2 = &gps->points[0];
+  float dist_close = len_v3v3(&pt1->x, &pt2->x);
+
+  /* if the distance to close is very small, don't need add points and just enable cyclic. */
+  if (dist_close <= dist_avg) {
+    gps->flag |= GP_STROKE_CYCLIC;
+    return true;
+  }
+
+  /* Calc number of points required using the average distance. */
+  int tot_newpoints = MAX2(dist_close / dist_avg, 1);
+
+  /* Resize stroke array. */
+  int old_tot = gps->totpoints;
+  gps->totpoints += tot_newpoints;
+  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+  if (gps->dvert != NULL) {
+    gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+  }
+
+  /* Generate new points */
+  pt1 = &gps->points[old_tot - 1];
+  pt2 = &gps->points[0];
+  bGPDspoint *pt = &gps->points[old_tot];
+  for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
+    float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f;
+    /* Clamp last point to be near, but not on top of first point. */
+    if ((tot_newpoints > 1) && (i == tot_newpoints)) {
+      step *= 0.99f;
+    }
+
+    /* Average point. */
+    interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
+    pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
+    pt->strength = interpf(pt2->strength, pt1->strength, step);
+    pt->flag = 0;
+    interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
+
+    /* Set weights. */
+    if (gps->dvert != NULL) {
+      MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
+      MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
+      float weight_1 = dw1 ? dw1->weight : 0.0f;
+
+      MDeformVert *dvert2 = &gps->dvert[0];
+      MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
+      float weight_2 = dw2 ? dw2->weight : 0.0f;
+
+      MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
+      dvert_final->totweight = 0;
+      MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
+      if (dvert_final->dw) {
+        dw->weight = interpf(weight_2, weight_1, step);
+      }
+    }
+  }
+
+  /* Enable cyclic flag. */
+  gps->flag |= GP_STROKE_CYCLIC;
+
+  return true;
+}
+/* Dissolve points in stroke */
+void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
+{
+  bGPDspoint *pt;
+  MDeformVert *dvert = NULL;
+  int i;
+
+  int tot = gps->totpoints; /* number of points in new buffer */
+  /* first pass: count points to remove */
+  /* Count how many points are selected (i.e. how many to remove) */
+  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+    if (pt->flag & tag) {
+      /* selected point - one of the points to remove */
+      tot--;
+    }
+  }
+
+  /* if no points are left, we simply delete the entire stroke */
+  if (tot <= 0) {
+    /* remove the entire stroke */
+    if (gps->points) {
+      MEM_freeN(gps->points);
+    }
+    if (gps->dvert) {
+      BKE_gpencil_free_stroke_weights(gps);
+      MEM_freeN(gps->dvert);
+    }
+    if (gps->triangles) {
+      MEM_freeN(gps->triangles);
+    }
+    BLI_freelinkN(&gpf->strokes, gps);
+  }
+  else {
+    /* just copy all points to keep into a smaller buffer */
+    bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
+    bGPDspoint *npt = new_points;
+
+    MDeformVert *new_dvert = NULL;
+    MDeformVert *ndvert = NULL;
+
+    if (gps->dvert != NULL) {
+      new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
+      ndvert = new_dvert;
+    }
+
+    (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+    for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+      if ((pt->flag & tag) == 0) {
+        *npt = *pt;
+        npt++;
+
+        if (gps->dvert != NULL) {
+          *ndvert = *dvert;
+          ndvert->dw = MEM_dupallocN(dvert->dw);
+          ndvert++;
+        }
+      }
+      if (gps->dvert != NULL) {
+        dvert++;
+      }
+    }
+
+    /* free the old buffer */
+    if (gps->points) {
+      MEM_freeN(gps->points);
+    }
+    if (gps->dvert) {
+      BKE_gpencil_free_stroke_weights(gps);
+      MEM_freeN(gps->dvert);
+    }
+
+    /* save the new buffer */
+    gps->points = new_points;
+    gps->dvert = new_dvert;
+    gps->totpoints = tot;
+
+    /* triangles cache needs to be recalculated */
+    BKE_gpencil_stroke_geometry_update(gps);
+  }
+}
+
+/* Merge by distance ------------------------------------- */
+/* 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 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,
+                                       bGPDstroke *gps,
+                                       const float threshold,
+                                       const bool use_unselected)
+{
+  bGPDspoint *pt = NULL;
+  bGPDspoint *pt_next = NULL;
+  float tagged = false;
+  /* Use square distance to speed up loop */
+  const float th_square = threshold * threshold;
+  /* Need to have something to merge. */
+  if (gps->totpoints < 2) {
+    return;
+  }
+  int i = 0;
+  int step = 1;
+  while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
+    pt = &gps->points[i];
+    if (pt->flag & GP_SPOINT_TAG) {
+      i++;
+      step = 1;
+      continue;
+    }
+    pt_next = &gps->points[i + step];
+    /* Do not recalc tagged points. */
+    if (pt_next->flag & GP_SPOINT_TAG) {
+      step++;
+      continue;
+    }
+    /* Check if contiguous points are selected. */
+    if (!use_unselected) {
+      if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
+        i++;
+        step = 1;
+        continue;
+      }
+    }
+    float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
+    if (len_square <= th_square) {
+      tagged = true;
+      if (i != gps->totpoints - 1) {
+        /* Tag second point for delete. */
+        pt_next->flag |= GP_SPOINT_TAG;
+      }
+      else {
+        pt->flag |= GP_SPOINT_TAG;
+      }
+      /* Jump to next pair of points, keeping first point segment equals.*/
+      step++;
+    }
+    else {
+      /* Analyze next point. */
+      i++;
+      step = 1;
+    }
+  }
+
+  /* Always untag extremes. */
+  pt = &gps->points[0];
+  pt->flag &= ~GP_SPOINT_TAG;
+  pt = &gps->points[gps->totpoints - 1];
+  pt->flag &= ~GP_SPOINT_TAG;
+
+  /* Dissolve tagged points */
+  if (tagged) {
+    BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
+  }
+
+  /* Calc geometry data. */
+  BKE_gpencil_stroke_geometry_update(gps);
+}
+
+/* Helper: Check materials with same color. */
+static int gpencil_check_same_material_color(Object *ob_gp, float color[4], Material **r_mat)
+{
+  Material *ma = NULL;
+  float color_cu[4];
+  linearrgb_to_srgb_v3_v3(color_cu, color);
+  float hsv1[4];
+  rgb_to_hsv_v(color_cu, hsv1);
+  hsv1[3] = color[3];
+
+  for (int i = 1; i <= ob_gp->totcol; i++) {
+    ma = BKE_object_material_get(ob_gp, i);
+    MaterialGPencilStyle *gp_style = ma->gp_style;
+    /* Check color with small tolerance (better in HSV). */
+    float hsv2[4];
+    rgb_to_hsv_v(gp_style->fill_rgba, hsv2);
+    hsv2[3] = gp_style->fill_rgba[3];
+    if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) &&
+        (compare_v4v4(hsv1, hsv2, 0.01f))) {
+      *r_mat = ma;
+      return i - 1;
+    }
+  }
+
+  *r_mat = NULL;
+  return -1;
+}
+
+/* Helper: Add gpencil material using curve material as base. */
+static Material *gpencil_add_from_curve_material(Main *bmain,
+                                                 Object *ob_gp,
+                                                 const float cu_color[4],
+                                                 const bool gpencil_lines,
+                                                 const bool fill,
+                                                 int *r_idx)
+{
+  Material *mat_gp = BKE_gpencil_object_material_new(
+      bmain, ob_gp, (fill) ? "Material" : "Unassigned", r_idx);
+  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
+
+  /* Stroke color. */
+  if (gpencil_lines) {
+    ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
+    gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+  }
+  else {
+    linearrgb_to_srgb_v4(gp_style->stroke_rgba, cu_color);
+    gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+  }
+
+  /* Fill color. */
+  linearrgb_to_srgb_v4(gp_style->fill_rgba, cu_color);
+  /* Fill is false if the original curve hasn't material assigned, so enable it. */
+  if (fill) {
+    gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+  }
+
+  /* Check at least one is enabled. */
+  if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
+      ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
+    gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+  }
+
+  return mat_gp;
+}
+
+/* Helper: Create new stroke section. */
+static void gpencil_add_new_points(bGPDstroke *gps,
+                                   float *coord_array,
+                                   float pressure,
+                                   int init,
+                                   int totpoints,
+                                   const float init_co[3],
+                                   bool last)
+{
+  for (int i = 0; i < totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i + init];
+    copy_v3_v3(&pt->x, &coord_array[3 * i]);
+    /* Be sure the last point is not on top of the first point of the curve or
+     * the close of the stroke will produce glitches. */
+    if ((last) && (i > 0) && (i == totpoints - 1)) {
+      float dist = len_v3v3(init_co, &pt->x);
+      if (dist < 0.1f) {
+        /* Interpolate between previous point and current to back slightly. */
+        bGPDspoint *pt_prev = &gps->points[i + init - 1];
+        interp_v3_v3v3(&pt->x, &pt_prev->x, &pt->x, 0.95f);
+      }
+    }
+
+    pt->pressure = pressure;
+    pt->strength = 1.0f;
+  }
+}
+
+/* Helper: Get the first collection that includes the object. */
+static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob)
+{
+  Collection *mycol = NULL;
+  FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
+    for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
+      if ((mycol == NULL) && (cob->ob == ob)) {
+        mycol = collection;
+      }
+    }
+  }
+  FOREACH_SCENE_COLLECTION_END;
+
+  return mycol;
+}
+
+/* Helper: Convert one spline to grease pencil stroke. */
+static void gpencil_convert_spline(Main *bmain,
+                                   Object *ob_gp,
+                                   Object *ob_cu,
+                                   const bool gpencil_lines,
+                                   const bool only_stroke,
+                                   bGPDframe *gpf,
+                                   Nurb *nu)
+{
+  Curve *cu = (Curve *)ob_cu->data;
+  bool cyclic = true;
+
+  /* Create Stroke. */
+  bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke");
+  gps->thickness = 1.0f;
+  gps->fill_opacity_fac = 1.0f;
+  gps->hardeness = 1.0f;
+  gps->uv_scale = 1.0f;
+
+  ARRAY_SET_ITEMS(gps->aspect_ratio, 1.0f, 1.0f);
+  ARRAY_SET_ITEMS(gps->caps, GP_STROKE_CAP_ROUND, GP_STROKE_CAP_ROUND);
+  gps->inittime = 0.0f;
+
+  gps->flag &= ~GP_STROKE_SELECT;
+  gps->flag |= GP_STROKE_3DSPACE;
+
+  gps->mat_nr = 0;
+  /* Count total points
+   * The total of points must consider that last point of each segment is equal to the first
+   * point of next segment.
+   */
+  int totpoints = 0;
+  int segments = 0;
+  int resolu = nu->resolu + 1;
+  segments = nu->pntsu;
+  if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) {
+    segments--;
+    cyclic = false;
+  }
+  totpoints = (resolu * segments) - (segments - 1);
+
+  /* Materials
+   * Notice: The color of the material is the color of viewport and not the final shader color.
+   */
+  Material *mat_gp = NULL;
+  bool fill = true;
+  /* Check if grease pencil has a material with same color.*/
+  float color[4];
+  if ((cu->mat) && (*cu->mat)) {
+    Material *mat_cu = *cu->mat;
+    copy_v4_v4(color, &mat_cu->r);
+  }
+  else {
+    /* Gray (unassigned from SVG add-on) */
+    zero_v4(color);
+    add_v3_fl(color, 0.6f);
+    color[3] = 1.0f;
+    fill = false;
+  }
+
+  /* Special case: If the color was created by the SVG add-on and the name contains '_stroke' and
+   * there is only one color, the stroke must not be closed, fill to false and use for
+   * stroke the fill color.
+   */
+  bool do_stroke = false;
+  if (ob_cu->totcol == 1) {
+    Material *ma_stroke = BKE_object_material_get(ob_cu, 1);
+    if ((ma_stroke) && (strstr(ma_stroke->id.name, "_stroke") != NULL)) {
+      do_stroke = true;
+    }
+  }
+
+  int r_idx = gpencil_check_same_material_color(ob_gp, color, &mat_gp);
+  if ((ob_cu->totcol > 0) && (r_idx < 0)) {
+    Material *mat_curve = BKE_object_material_get(ob_cu, 1);
+    mat_gp = gpencil_add_from_curve_material(bmain, ob_gp, color, gpencil_lines, fill, &r_idx);
+
+    if ((mat_curve) && (mat_curve->gp_style != NULL)) {
+      MaterialGPencilStyle *gp_style_cur = mat_curve->gp_style;
+      MaterialGPencilStyle *gp_style_gp = mat_gp->gp_style;
+
+      copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba);
+      gp_style_gp->fill_style = gp_style_cur->fill_style;
+      gp_style_gp->mix_factor = gp_style_cur->mix_factor;
+    }
+
+    /* If object has more than 1 material, use second material for stroke color. */
+    if ((!only_stroke) && (ob_cu->totcol > 1) && (BKE_object_material_get(ob_cu, 2))) {
+      mat_curve = BKE_object_material_get(ob_cu, 2);
+      if (mat_curve) {
+        linearrgb_to_srgb_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
+        mat_gp->gp_style->stroke_rgba[3] = mat_curve->a;
+      }
+    }
+    else if ((only_stroke) || (do_stroke)) {
+      /* Also use the first color if the fill is none for stroke color. */
+      if (ob_cu->totcol > 0) {
+        mat_curve = BKE_object_material_get(ob_cu, 1);
+        if (mat_curve) {
+          copy_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r);
+          mat_gp->gp_style->stroke_rgba[3] = mat_curve->a;
+          /* Set fill and stroke depending of curve type (3D or 2D). */
+          if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) {
+            mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+            mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
+          }
+          else {
+            mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+            mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+          }
+        }
+      }
+    }
+  }
+  CLAMP_MIN(r_idx, 0);
+
+  /* Assign material index to stroke. */
+  gps->mat_nr = r_idx;
+
+  /* Add stroke to frame.*/
+  BLI_addtail(&gpf->strokes, gps);
+
+  float *coord_array = NULL;
+  float init_co[3];
+
+  switch (nu->type) {
+    case CU_POLY: {
+      /* Allocate memory for storage points. */
+      gps->totpoints = nu->pntsu;
+      gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+      /* Increase thickness for this type. */
+      gps->thickness = 10.0f;
+
+      /* Get all curve points */
+      for (int s = 0; s < gps->totpoints; s++) {
+        BPoint *bp = &nu->bp[s];
+        bGPDspoint *pt = &gps->points[s];
+        copy_v3_v3(&pt->x, bp->vec);
+        pt->pressure = bp->radius;
+        pt->strength = 1.0f;
+      }
+      break;
+    }
+    case CU_BEZIER: {
+      /* Allocate memory for storage points. */
+      gps->totpoints = totpoints;
+      gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+
+      int init = 0;
+      resolu = nu->resolu + 1;
+      segments = nu->pntsu;
+      if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) {
+        segments--;
+      }
+      /* Get all interpolated curve points of Beziert */
+      for (int s = 0; s < segments; s++) {
+        int inext = (s + 1) % nu->pntsu;
+        BezTriple *prevbezt = &nu->bezt[s];
+        BezTriple *bezt = &nu->bezt[inext];
+        bool last = (bool)(s == segments - 1);
+
+        coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__);
+
+        for (int j = 0; j < 3; j++) {
+          BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
+                                        prevbezt->vec[2][j],
+                                        bezt->vec[0][j],
+                                        bezt->vec[1][j],
+                                        coord_array + j,
+                                        resolu - 1,
+                                        3 * sizeof(float));
+        }
+        /* Save first point coordinates. */
+        if (s == 0) {
+          copy_v3_v3(init_co, &coord_array[0]);
+        }
+        /* Add points to the stroke */
+        gpencil_add_new_points(gps, coord_array, bezt->radius, init, resolu, init_co, last);
+        /* Free memory. */
+        MEM_SAFE_FREE(coord_array);
+
+        /* As the last point of segment is the first point of next segment, back one array
+         * element to avoid duplicated points on the same location.
+         */
+        init += resolu - 1;
+      }
+      break;
+    }
+    case CU_NURBS: {
+      if (nu->pntsv == 1) {
+
+        int nurb_points;
+        if (nu->flagu & CU_NURB_CYCLIC) {
+          resolu++;
+          nurb_points = nu->pntsu * resolu;
+        }
+        else {
+          nurb_points = (nu->pntsu - 1) * resolu;
+        }
+        /* Get all curve points. */
+        coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
+        BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3]));
+
+        /* Allocate memory for storage points. */
+        gps->totpoints = nurb_points - 1;
+        gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+
+        /* Add points. */
+        gpencil_add_new_points(gps, coord_array, 1.0f, 0, gps->totpoints, init_co, false);
+
+        MEM_SAFE_FREE(coord_array);
+      }
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+  /* Cyclic curve, close stroke. */
+  if ((cyclic) && (!do_stroke)) {
+    BKE_gpencil_stroke_close(gps);
+  }
+
+  /* Recalc fill geometry. */
+  BKE_gpencil_stroke_geometry_update(gps);
+}
+
+/* Convert a curve object to grease pencil stroke.
+ *
+ * \param bmain: Main thread pointer
+ * \param scene: Original scene.
+ * \param ob_gp: Grease pencil object to add strokes.
+ * \param ob_cu: Curve to convert.
+ * \param gpencil_lines: Use lines for strokes.
+ * \param use_collections: Create layers using collection names.
+ * \param only_stroke: The material must be only stroke without fill.
+ */
+void BKE_gpencil_convert_curve(Main *bmain,
+                               Scene *scene,
+                               Object *ob_gp,
+                               Object *ob_cu,
+                               const bool gpencil_lines,
+                               const bool use_collections,
+                               const bool only_stroke)
+{
+  if (ELEM(NULL, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
+    return;
+  }
+
+  Curve *cu = (Curve *)ob_cu->data;
+  bGPdata *gpd = (bGPdata *)ob_gp->data;
+  bGPDlayer *gpl = NULL;
+
+  /* If the curve is empty, cancel. */
+  if (cu->nurb.first == NULL) {
+    return;
+  }
+
+  /* Check if there is an active layer. */
+  if (use_collections) {
+    Collection *collection = gpencil_get_parent_collection(scene, ob_cu);
+    if (collection != NULL) {
+      gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
+      if (gpl == NULL) {
+        gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
+      }
+    }
+  }
+
+  if (gpl == NULL) {
+    gpl = BKE_gpencil_layer_active_get(gpd);
+    if (gpl == NULL) {
+      gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+    }
+  }
+
+  /* Check if there is an active frame and add if needed. */
+  bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_COPY);
+
+  /* Read all splines of the curve and create a stroke for each. */
+  for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
+    gpencil_convert_spline(bmain, ob_gp, ob_cu, gpencil_lines, only_stroke, gpf, nu);
+  }
+
+  /* Tag for recalculation */
+  DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+}
+
+/* Apply Transforms */
+void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4])
+{
+  if (gpd == NULL) {
+    return;
+  }
+
+  const float scalef = mat4_to_scale(mat);
+  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+    /* FIXME: For now, we just skip parented layers.
+     * Otherwise, we have to update each frame to find
+     * the current parent position/effects.
+     */
+    if (gpl->parent) {
+      continue;
+    }
+
+    LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+        bGPDspoint *pt;
+        int i;
+
+        for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
+          mul_m4_v3(mat, &pt->x);
+          pt->pressure *= scalef;
+        }
+
+        /* Distortion may mean we need to re-triangulate. */
+        BKE_gpencil_stroke_geometry_update(gps);
+      }
+    }
+  }
+}
+/** \} */
index e73070f117b23377959279ec7caa10cfa72ffa9a..0bb6ce84b1b26f745d85a23401f8025740ac68a3 100644 (file)
@@ -40,6 +40,7 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_lattice.h"
 #include "BKE_lib_id.h"
index 7e1b21717b4be2a76897131a9d845b4fdecda345..7a2e9583aa11545263433cace31a4a9a8e95366a 100644 (file)
@@ -83,6 +83,7 @@
 #include "BKE_font.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_hair.h"
 #include "BKE_icons.h"
index b0fbe5c710be0aa6b2bb98d93330c5d5242a2b6e..4d472401521348ff8e05206930d6ec22b09e8b7e 100644 (file)
@@ -77,6 +77,7 @@
 #include "BKE_freestyle.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_idprop.h"
 #include "BKE_key.h"
index afe47160e02cfaeb3ecb36fa9df8549123b252b2..349eb6b00aec244b0b0da82a47a83071397fe24d 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 
 #include "DRW_engine.h"
 #include "DRW_render.h"
index f08188c48cead4c3817e8982ac83bb4a2773ad6a..23ca5241866a10c745ebb6421fb5cad5c9f1547c 100644 (file)
@@ -31,6 +31,7 @@
 #include "BKE_brush.h"
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_lib_id.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index d4e17144ca2dba10496670ab29701bfa47016fca..60a4404beaf6fbf9e337a8c8fc74bf49b22b197c 100644 (file)
@@ -31,6 +31,7 @@
 #include "BKE_brush.h"
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_lib_id.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index bf1497fb2ed4ae568378d5fc7c247aa06da21438..83ecb3ab42f0cbd12ee7fa2ada68d17c822ac663 100644 (file)
@@ -51,6 +51,7 @@
 #include "BKE_context.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_lib_id.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index d487f76a103047a557e568bfd57c24950530815f..f61572fffcafb35848f6ed5ebfc5c6177defdbaf 100644 (file)
@@ -43,6 +43,7 @@
 #include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_image.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index 5577a35c1fdebe2317f5fdcd4c532db990cfc022..fef88007542323fd28193d632ebbabb324a6e5e0 100644 (file)
@@ -50,6 +50,7 @@
 #include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_report.h"
 
 #include "UI_interface.h"
index 9a7ad8d7220cbcb93834b97ca6cc7be06e5edc52..bd9daa834117bc060678d18ad0ea56c86d7aa867 100644 (file)
@@ -35,6 +35,7 @@
 #include "BKE_brush.h"
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
 #include "BKE_report.h"
index bec080e398da61b8f3b5c1d8a0a02964d35cce63..01b0fe80dfce666bb08879eef4077b2cf64b34fb 100644 (file)
@@ -53,6 +53,7 @@
 #include "BKE_deform.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_layer.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index 7470e7789afa5dec5d31a4bd27f995c00c68a3bf..2b30a4150868cea1a67189b0cc82ca86f84769c0 100644 (file)
@@ -54,6 +54,7 @@
 #include "BKE_deform.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
 #include "BKE_paint.h"
index 46867dcb11a624d032b988c0a17391087c684551..59b14a47d1deafead4d16b54628ea7ef24818020 100644 (file)
@@ -53,6 +53,7 @@
 #include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
index da6e51148228c60ea7034fe2c9d6c5a75d3e45f0..276be071e0b82e59d011351a81aa7302743faa98 100644 (file)
@@ -51,6 +51,7 @@
 #include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
 #include "BKE_object.h"
index dac19e3323558ed9e5f78fc50c68ea1703362388..1da32dcc537e1f02f9e8f8b946c608e653ddc6c8 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_unit.h"
 
 #include "RNA_access.h"
index 576e8e0ec25ddbddb91c11cf5f837da7b296c952..ac2958282c16038830afc1a43ca7e21f76a3e072 100644 (file)
@@ -64,7 +64,7 @@
 #include "BKE_displist.h"
 #include "BKE_effect.h"
 #include "BKE_font.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_hair.h"
 #include "BKE_key.h"
 #include "BKE_lattice.h"
index a467667a4c7b243bc41f3b0b9c590f38ffa48218..0eab9c72227cf2ea232091483cd90a170dadd998 100644 (file)
@@ -45,6 +45,7 @@
 #include "BKE_curve.h"
 #include "BKE_editmesh.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_idcode.h"
 #include "BKE_lattice.h"
 #include "BKE_layer.h"
index 92adc7d6756bc62a4c637b4ee28a93bfb33b943a..2aff5482b1c71bdd11ac7a3beba2ad4be6f94835 100644 (file)
@@ -46,7 +46,7 @@
 #include "BKE_camera.h"
 #include "BKE_context.h"
 #include "BKE_font.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_layer.h"
 #include "BKE_lib_id.h"
 #include "BKE_main.h"
index ca0eb40e7b10ce1f7d3619a1922479c723111fba..cb7071e0f49bb589f05bc0f53cfee7d25273dfea 100644 (file)
@@ -69,7 +69,7 @@
 #include "BKE_curve.h"
 #include "BKE_editmesh.h"
 #include "BKE_fcurve.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_lattice.h"
 #include "BKE_layer.h"
 #include "BKE_lib_id.h"
index 09027c1661902dd642d77827e697b1f045bd8d00..59a41901a48147bc1572e2f6def469a7b7d33f6f 100644 (file)
@@ -37,6 +37,7 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_lattice.h"
 #include "BKE_lib_query.h"
index 68bac0ff701555dcc7190d49213e48cdf65c77ba..314879927ffd3be578a2bb89e6d77815d4b0af8f 100644 (file)
@@ -43,6 +43,7 @@
 #include "BKE_collection.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_layer.h"
 #include "BKE_lib_query.h"
index 56779c1e23c755fb92ece4b5e1b3311fb1e73eae..3263b78ba85609d9428cdb7303da67fd971c698d 100644 (file)
@@ -37,6 +37,7 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 
 #include "DEG_depsgraph.h"
index 97e3fca3f4d072feae6ac0c70d1fe4d10fad23d3..76f22fc9a36d155c7ef86a75a6de68516186469e 100644 (file)
@@ -38,7 +38,7 @@
 #include "BKE_action.h"
 #include "BKE_colortools.h"
 #include "BKE_deform.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_layer.h"
 #include "BKE_lib_query.h"
index 2ef0cca97d4ab9acc4d630de0241bc6e9ca9acdc..73b3c332a2eebd1b3337e553c6149f296380af6d 100644 (file)
@@ -33,7 +33,7 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_deform.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_lattice.h"
 #include "BKE_layer.h"
index 4b4d5e9b51130a0da804b7b9a3c794ad73ee3f5a..eab9ffb46304a6e2e719f785b11851731cbbd8da 100644 (file)
@@ -41,6 +41,7 @@
 #include "BKE_context.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_layer.h"
 #include "BKE_lib_query.h"
index f6537a1d1c07dd750bcad7f7d078bcfb578530e3..5ed08e391975f168d84930be304fac0ec55a2146 100644 (file)
@@ -42,6 +42,7 @@
 #include "BKE_colortools.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 #include "BKE_object.h"
 
index 67419add3ed08f285629da0af680a274fee12dc9..a78de314c217d144746a2b344c3ab5b0470af909 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "BKE_colortools.h"
 #include "BKE_deform.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 
 #include "DEG_depsgraph.h"
index f6307253a49bc6f3068ac5b41c0c9f379291dab7..7b914b2a3b0aafaddec205439a12d88c92a05771 100644 (file)
@@ -32,7 +32,7 @@
 #include "DNA_scene_types.h"
 #include "DNA_vec_types.h"
 
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 
 #include "DEG_depsgraph.h"
index 4533aad6fa32ed873308dfccf3fc798e4fb752cc..fb9089ea6cbe908024bd57a56436845722e3b233 100644 (file)
@@ -33,7 +33,7 @@
 
 #include "BKE_colortools.h"
 #include "BKE_deform.h"
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 
 #include "DEG_depsgraph.h"
index 5d221904f1b35808c2cec845aa8df1713c25759d..0fdc3694af236a1a5926a2d79c9b0c5db8585220 100644 (file)
@@ -34,7 +34,7 @@
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_gpencil_modifier.h"
 
 #include "DEG_depsgraph.h"
index f451126a7e07d8db2bcd74b2013194fe526413bd..deacd8e1cfc39f50c3d91eeb757d14e36ee0d433 100644 (file)
@@ -138,6 +138,7 @@ static EnumPropertyItem rna_enum_gpencil_caps_modes_items[] = {
 #  include "BKE_action.h"
 #  include "BKE_animsys.h"
 #  include "BKE_gpencil.h"
+#  include "BKE_gpencil_geom.h"
 #  include "BKE_icons.h"
 
 #  include "DEG_depsgraph.h"
index 1db44f4fdeaaa63621d5a8f50f28773012c2f8cc..5104f4a66a1caab8153e0918c25687b509296de3 100644 (file)
@@ -36,7 +36,7 @@
 #include "DNA_modifier_types.h"
 #include "DNA_object_types.h"
 
-#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_layer.h"
 
 #include "DEG_depsgraph.h"