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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2020-03-19 13:35:17 +0300
committerAntonio Vazquez <blendergit@gmail.com>2020-03-19 13:38:22 +0300
commit6e39445f80bea7abf19c9a82d0dcc3cc3dddc7d2 (patch)
treee39ba060e21c00bf5c8b6c30336ea71d120c4893
parente839a2565190556d397449e07b00c5f03fee6fb5 (diff)
GPencil: Cleanup - Split BKE_gpencil.h geometry functions into BKE_gpencil_geom.h
This split prepare the code for future geometry functions.
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h67
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h113
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/gpencil.c1980
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c2022
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c1
-rw-r--r--source/blender/blenkernel/intern/object.c1
-rw-r--r--source/blender/blenloader/intern/versioning_280.c1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c1
-rw-r--r--source/blender/editors/object/object_add.c2
-rw-r--r--source/blender/editors/object/object_transform.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c1
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c2
37 files changed, 2170 insertions, 2056 deletions
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 5d23029df9e..8cd3081389e 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -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
index 00000000000..8c52e6d458b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -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__ */
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 7fd5470cecc..37e00b56fd9 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -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
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 7036da1f870..555b0af5a63 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -33,43 +33,34 @@
#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,781 +1468,6 @@ 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)
-{
- 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);
- }
-}
-
-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;
-}
-
-/**
- * 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 range of selected frames in layer.
* Always the active frame is considered as selected, so if no more selected the range
@@ -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
index 00000000000..92054c5c79a
--- /dev/null
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -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);
+ }
+ }
+ }
+}
+/** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index e73070f117b..0bb6ce84b1b 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -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"
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 7e1b21717b4..7a2e9583aa1 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -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"
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index b0fbe5c710b..4d472401521 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -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"
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index afe47160e02..349eb6b00ae 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index f08188c48ce..23ca5241866 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index d4e17144ca2..60a4404beaf 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index bf1497fb2ed..83ecb3ab42f 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index d487f76a103..f61572fffca 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 5577a35c1fd..fef88007542 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 9a7ad8d7220..bd9daa83411 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index bec080e398d..01b0fe80dfc 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 7470e7789af..2b30a415086 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 46867dcb11a..59b14a47d1d 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index da6e5114822..276be071e0b 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -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"
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
index dac19e33235..1da32dcc537 100644
--- a/source/blender/editors/gpencil/gpencil_uv.c
+++ b/source/blender/editors/gpencil/gpencil_uv.c
@@ -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"
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 576e8e0ec25..ac2958282c1 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -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"
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index a467667a4c7..0eab9c72227 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -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"
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 92adc7d6756..2aff5482b1c 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -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"
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index ca0eb40e7b1..cb7071e0f49 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index 09027c16619..59a41901a48 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index 68bac0ff701..314879927ff 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 56779c1e23c..3263b78ba85 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index 97e3fca3f4d..76f22fc9a36 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index 2ef0cca97d4..73b3c332a2e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
index 4b4d5e9b511..eab9ffb4630 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
index f6537a1d1c0..5ed08e39197 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index 67419add3ed..a78de314c21 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
index f6307253a49..7b914b2a3b0 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index 4533aad6fa3..fb9089ea6cb 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -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"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
index 5d221904f1b..0fdc3694af2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
@@ -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"
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index f451126a7e0..deacd8e1cfc 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -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"
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 1db44f4fdea..5104f4a66a1 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -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"